/** * A word with no letter takes the script of the text around it (#725). * * A word made only of digits, punctuation or symbols (`46.`, `1367)`, `».`) * has no script of its own: all its characters are Unicode `Common`. A * face may draw them differently by script (Amiri sets a Latin full stop, * brackets and guillemets under `latn` and Arabic ones otherwise: the stop * is 0.202 em against 0.317), and a shaper handed such a word alone picks * a script by a rule of its own: Chrome 131's canvas the font's default, * Chrome 154's Latin, fontkit (the PDF) the default. The layout measures * and paints word by word, so the same document broke its lines otherwise * from one browser version to the next, and the PDF set glyphs of another * width in the measured places. * * So the engine decides. A word with no letter is given the script of its * context ({@link resolveScriptContext}), and every stage shapes it in that * script by setting it beside a character of the script, the one thing the * browsers agree on: a character with no script takes the script of the * text it stands in. The measurer measures the word with that mark beside * it and takes the mark's width off ({@link scriptMark}); the canvas paints * the two and clips the mark away; the HTML hides it in a box of no size; * the PDF asks its shaper for the script. Measured in headless Chrome 131 * and 154 with Amiri: the widths, the DOM widths and the clipped pixels are * the same on both, for every mark below. * * It costs nothing where it does nothing: a face whose punctuation is the * same under every script ({@link scriptSensitive}, nearly all of them) is * left alone, with no field on its segments. * * Two more cases follow from the same rule. * * A word that has a letter is one script run, and so are the pieces it is * painted in. `10.1006/ijhc.2001.0458` may end a line at each of its * joints, and each piece between two joints is a text of its own to every * renderer; `10.1006/`, `.2001` and `.0458` hold no letter. Painted alone * they took the browser's pick, while their places were measured inside * the word, in its script: in Noto Serif SC, whose digits are 0.47 em by * default and 0.55 em under `latn`, the difference was air after `ijhc`. * A piece with no letter takes the script it has inside its word * ({@link pieceScriptContext}): of the letter before it there, else of the * word's first letter. * * A doubled ellipsis or dash (`……`, `——`) is the Chinese and Japanese mark * whatever it is cited in. In a document in one of those languages, a * word with no letter that holds one (`…….` closing an English sentence * of a colophon) gives the pair the forms of Han text (`'Hani'`), and * what stands with it (the full stop) the script of the sentence * ({@link cutDoubledMarks}). Noto Serif SC draws `…` as three dots on the * baseline, 0.9 em wide, under `latn`, and as three centred dots in a * full em otherwise. */ import { type BidiIsolate } from '../bidi.js'; /** The scripts a word with no letter may be shaped in (ISO 15924). `'Hani'` * (Han) is given to a doubled ellipsis or dash only ({@link * cutDoubledMarks}): a word beside Han text is shaped alone, as it was. */ export type ScriptContext = 'Latn' | 'Grek' | 'Cyrl' | 'Arab' | 'Hebr' | 'Hani'; /** Whether `text` holds no letter (see {@link OWN_SCRIPT_RE}) and something * to shape: a word of digits, punctuation or symbols. */ export declare function isLetterless(text: string): boolean; /** * The script a word with no letter is shaped in: `[start, end)` of the * paragraph `text`, at an even (`odd` false) or odd bidi level. * - Painted left to right (even level): the script of the nearest letter * before it in its isolate, else of the nearest after it. * - Painted right to left (odd level): the script of the nearest * right-to-left letter before it, else after it. * - With no such letter: the script of the document's language * (`fallback`), when the word is painted that script's way. * Undefined when the context is a script the engine does not act on (the * word is then shaped alone, as up to postext 1.27). * * `isolates` are the paragraph's inline isolates (`spanIsolates`): the * word reads the innermost one that holds it, the paragraph otherwise. */ export declare function resolveScriptContext(text: string, start: number, end: number, odd: boolean, isolates: readonly BidiIsolate[], fallback: ScriptContext | undefined): ScriptContext | undefined; /** * The script the pieces of `word` with no letter are shaped in, when it * may have such pieces: a word painted left to right that holds a letter * and a character with no script (`10.1006/ijhc.2001`, `COVID-19`, `37 °C` * glued by its no-break space). It is the script of its first letter: the * one a shaper gives what stands before that letter. Undefined for a word * of letters only, for a word with no letter (it has a script of its own, * {@link resolveScriptContext}) and when the letter is of a script the * engine does not act on. */ export declare function wordPieceScript(word: string): ScriptContext | undefined; /** * The script the piece `[from, to)` of a word is shaped in when it holds * no letter: the script of the nearest letter before it in `text` (the * word, or what is left of it after a cut), else `wordScript`, the script * in force where `text` starts ({@link wordPieceScript} for a whole word, * {@link scriptAt} for the rest of one). Undefined for a piece with a * letter, which is shaped in its own script, and when the letter before * it is of a script the engine does not act on. */ export declare function pieceScriptContext(text: string, from: number, to: number, wordScript: ScriptContext | undefined): ScriptContext | undefined; /** The script in force at `at` of `text`: of the nearest letter before * it, else `wordScript`. */ export declare function scriptAt(text: string, at: number, wordScript: ScriptContext | undefined): ScriptContext | undefined; /** Whether `text` is a doubled ellipsis or dash and nothing else. */ export declare function isDoubledMark(text: string): boolean; /** * A word with no letter cut around the doubled ellipses and dashes it * holds: `…….` gives `……` and `.`, `“——”` gives `“`, `——` and `”`. The * pair is shaped as Han text ({@link isDoubledMark}), the rest in the * script of its context. Undefined when the word has a letter (a shaper * sets it whole, in its script) or holds no such pair, or is one and * nothing else. */ export declare function cutDoubledMarks(word: string): string[] | undefined; /** * Whether a word shaped in `script` takes no tracking, as a word of a * joining script takes none (`measure/joining.ts`): the Arabic script. The * word belongs to the Arabic text it stands in, which is never spaced * apart; and a current browser applies no letter-spacing to a run in a * cursive script, where Chrome 131 did, so tracked the word was one width * on one and another on the other. Measured and painted untracked it is * the same on both. */ export declare function scriptContextUntracked(script: ScriptContext | undefined): boolean; /** * What a word with no letter is set beside to be shaped in `script` * (`lead` before it, `trail` after it, in the text's order), painted left * to right or right to left (`rtl`, the context's direction then). Either * way the mark lands on the word's right, past its advance: the word * starts where it would alone, and a clip at its advance removes the mark. */ export declare function scriptMark(script: ScriptContext, rtl: boolean): { lead: string; trail: string; }; /** `text` beside its {@link scriptMark}, as it is measured and painted. */ export declare function withScriptMark(text: string, script: ScriptContext, rtl: boolean): string; /** Whether the face `font` draws its punctuation or digits otherwise by * script (`scriptSensitive`, asked once a font string): only then is a * word with no letter given a script. */ export declare function faceReadsScript(font: string): boolean; /** The width of a word with no letter shaped in `script`, painted left to * right or right to left (`rtl`): measured beside its {@link scriptMark}, * the mark's width taken off. */ export declare function scriptTextWidth(text: string, font: string, script: ScriptContext, rtl: boolean): number; /** * The width of a right-to-left word (its bidi level is odd) measured as it * is painted, in a context set right to left. Measured in the default * left-to-right one, a bracket or a stop at its edge (`م)`, `(المخطوط`) * falls at the paragraph's level, outside the word's run: Chrome 131 still * gave it the word's script, Chrome 154 shapes that run alone and gives * it Latin forms, so the word was measured narrower than every output * paints it. * * And in any face the two measures differ by more than the forms (#770). * Left to right, the stop and the guillemet of `خالة.»` are a run apart, * shaped alone, and the word is the sum of two runs. Painted, it is one * right-to-left run: its marks mirrored, in the order they are read, * kerned to each other. Markazi Text sets that word 1993.2 thousandths of * the em wide as two runs and 1988.3 as one, in any language. * * Who is measured so: a word of a face that draws punctuation by script * (`faceReadsScript`), and every right-to-left word of a document shaped * in its own language. Markazi Text is such a face in Arabic alone: its * Arabic stop, exclamation mark, guillemets and ellipsis are a `locl` of * the Arabic language system (`arab`/`ARA `), which Persian, Urdu and a * text with no language do not take, so there its words were measured as * two runs. A document shaped in its host's language keeps the widths it * had. */ export declare function rtlTextWidth(text: string, font: string): number; /** Whether a document in the language of `tag` sets the doubled ellipsis * and dash as its own marks ({@link cutDoubledMarks}): Chinese, Japanese. */ export declare function hanMarksOfLocale(tag: unknown): boolean; /** The script of a language tag when it is one the engine acts on * (`ar`, `fa`, `ur` → `'Arab'`; `he` → `'Hebr'`; `el` → `'Grek'`; `ru` → * `'Cyrl'`; `en`, `es`… → `'Latn'`), else undefined. */ export declare function scriptContextOfLocale(tag: unknown): ScriptContext | undefined; /** What the measurer reads (see {@link setMeasureScriptContext}). */ export interface MeasureScriptContext { /** `bodyText.scriptContext`: off, a word with no letter is shaped alone. */ enabled: boolean; /** The script of the document's language, for a word with no letter * anywhere near it. */ script?: ScriptContext; /** The document is in Chinese or Japanese: a doubled ellipsis or dash * is its mark, set in the forms of Han text ({@link cutDoubledMarks}). */ hanMarks?: boolean; } /** Set how words with no letter are measured: the build does, from * `bodyText.scriptContext` and the document's language, and puts back * what it found when it is done. Outside a build: on, Latin. */ export declare function setMeasureScriptContext(state: MeasureScriptContext): void; /** See {@link setMeasureScriptContext}. */ export declare function getMeasureScriptContext(): MeasureScriptContext; /** The part of a measurement cache key the script context decides: empty * for a document in a Latin-script language with the rule on, so those * keys are unchanged. */ export declare function scriptContextKey(): string; //# sourceMappingURL=script.d.ts.map