import { type TabLeaderPlacement, type TabStopPx } from './tabs.js'; import type { TabStopAlign } from '../types.js'; import type { VDTAnnotationRun, VDTChip, VDTLine, VDTLineSegment, VDTSegmentMarks, VDTSwatch } from '../vdt.js'; import type { MathRender } from '../math/types.js'; import type { InlineSpan } from '../parse/index.js'; import { type MeasuredBlock, type MeasureBlockOptions } from './types.js'; import type { CjkRegion } from '../types.js'; import { type ScriptContext } from './script.js'; export interface RichBreakPoint { charIndex: number; widthBefore: number; /** The line ends on the character before `charIndex` as it is — a hard * hyphen the word already carries — and no hyphen is added. */ bare?: boolean; /** A soft hyphen typed in the source: the author's own break, taken like * a dictionary syllable (a hyphen is added) but never held back by the * hyphenation zone of ragged text or its two-line cap. */ author?: boolean; /** A break inside a run with no spaces that is no hyphenation point: * between two ideographs, after a dash or a slash between two words, or * at a zero-width space the author typed. The line * ends as it is, nothing is added, and no hyphenation penalty applies. */ free?: boolean; /** Taken only by the greedy breaker (ragged lines, and justified ones * without optimal line breaking), as pretext's breaker takes it on plain * paragraphs; Knuth–Plass does not break here on either path. */ greedyOnly?: boolean; /** A `free` break next to a CJK character: the line it ends is not * `hyphenated`, so the column-end hyphen rules leave it alone. */ cjk?: boolean; /** A syllable break inside a Catalan `l·l`: the hyphen takes the place of * the middle dot (`il-` | `lusió`), whose width, here, the line ending * at this break gives back. In a document shaped in its own language * it is what the text up to the dot is narrower than the text with it * (`col` against `col·`): the head is then as wide as `col` and its * hyphen, the two texts painted (#770). */ replacesWidth?: number; /** Knuth–Plass, in a document shaped in its own language (#770): what * the rest of the word, set from this break at a line's start as a text * of its own, is wider than the pieces after the break measured inside * the word (negative: narrower). See {@link RichToken.ownPieces}. */ restShift?: number; } export interface RichToken { text: string; bold: boolean; italic: boolean; /** Superscript / subscript token: measured and painted with `scriptFont` * (the span font at the script size) and painted `baselineShift` px * below the baseline (negative: above). */ script?: 'sup' | 'sub'; scriptFont?: string; baselineShift?: number; /** The Arabic words of a slanted style set upright (`InlineSpan.upright`): * the face they are measured and painted in, the span's with its slant * taken off. */ faceFont?: string; /** An inline footnote marker at a size of its own * (`footnotes.markerSize`): the font it is measured and painted with, * on the baseline. */ markerFont?: string; /** One of a subscript and a superscript set over each other (see * {@link stackedScriptPairs}): the `first` has width 0, the `second` * the pair's advance. Neither is ever broken or hyphenated. */ stacked?: 'first' | 'second'; /** Small capitals: `text` keeps its own case (hyphenation reads it) and * `width` is the synthesised small caps' (see {@link smallCapsWidth}); * the line's segments are split into capital runs once it is broken. */ smallCaps?: boolean; kind: 'text' | 'space'; width: number; breakPoints?: RichBreakPoint[]; hyphenWidth?: number; /** The break points are bare (a URL / DOI broken after a slash or before * a dot): the line ends without a hyphen and nothing is appended. */ bareBreaks?: boolean; /** When present, this token is an atomic math formula. `text` is a single * `\uFFFC` placeholder and `width` is the math render's widthPx. */ mathRender?: MathRender; /** When present, this token is an inline `:ref{\u2026}` to a resource. `text` * already carries the resolved label; the token is atomic (never wraps * apart) and the id flows onto the segment so renderers can colour/link it. */ refResourceId?: string; /** The reference names an anchor (#262): `refResourceId` is its id. */ refAnchor?: true; /** The book page index the anchor landed on, when known. */ refPageIndex?: number; /** A later piece of a reference whose label breaks at its spaces (#702, * {@link refLabelTokens}): a word after the first, or a space between * two of them. Its segment is flagged `refContinues`. */ refContinues?: true; /** A footnote marker (`[^id]`): atomic like a reference; the id flows * onto the segment. */ footnoteId?: string; /** A footnote marker set in the line gap (`footnotes.markerPosition: * 'side'`): what is painted there; the token is 0 wide (see * `VDTLineSegment.sideMarker`). */ sideRuns?: VDTAnnotationRun[]; /** A footnote marker set flush with the right side of a vertical line * (`footnotes.markerPosition: 'right'`): how far its baseline moves * across the line (negative: right), px, for its em box at * `markerFont` to end where the line's does. */ markerShift?: number; /** True when this token belongs to a caption's numbered label. Flows to the * segment so renderers can apply the label colour. */ captionLabel?: boolean; /** {@link InlineSpan.labelTab}: a space whose width {@link setLabelTabs} * sets. */ labelTab?: 'lead' | 'gap'; /** When present, this token is an inline colour swatch (`:swatch{…}`): an * atomic square of `width` px, filled with `color` (hex) when resolved, * with the shape its attributes give it (#1104). */ swatch?: VDTSwatch; /** When present, this token is an inline chip (`:chip[…]`): an atomic box * of `width` px (margins included) with its own text runs. */ chip?: VDTChip; /** The chip style's gap (px), read when the word spaces around the chip * are sized (see {@link applyChipGaps}). */ chipGap?: number; /** A space inside inline code (`InlineSpan.codeSpace`, #845): the line * may break at it, but justification neither stretches nor shrinks it. */ fixedGlue?: true; /** Vertical text: a `:tcy[…]` run, one upright cell of one em (see * `VDTLineSegment.tcy`). */ tcy?: true; /** Vertical text: a `:upright[…]` or `:sideways[…]` run (see * `VDTLineSegment.orientation`). */ orientation?: 'upright' | 'sideways'; /** The token ends on a dash a line may end after, and the next token * touches it: a run in another style, as in `riddles—*and*` (see * {@link markDashJoins}). The line may break between the two. */ dashJoin?: boolean; /** Chinese marks on the token's text (#193), for the segment. */ cjkMarks?: VDTSegmentMarks; /** Characters the layout added (a book title's 《》). */ inserted?: boolean; /** One word of a joining script set in more than one style * (`كتا**ب**`), joined from the runs its spans cut it into (see * {@link joinStyledRuns}): each run's text and style, in order. The * token is measured whole, in the style of its longest run (`bold`, * `italic`), and becomes one segment painted as one shaped word * (`VDTLineSegment.runs`). */ runs?: { text: string; bold?: boolean; italic?: boolean; }[]; /** How far kashidas may widen this word, px: its box's stretch in * Knuth–Plass (`measure/kashida.ts`). Absent on any other token. */ kashida?: number; /** A tab (#622, `InlineSpan.tab`): a `space` token whose width the line * breaker sets where its line has got to (`measure/tabs.ts`); `stop` is * the one-off stop of `:tab{at=…}`. Never skipped at a line's start nor * trimmed at its end. */ tab?: { stop?: TabStopPx; }; /** A tab placed at its stop: the stop (see `VDTLineSegment.tab`) and its * leader (see {@link setTabbedLine}). */ tabPlaced?: { align: TabStopAlign; at: number; }; tabLeader?: TabLeaderPlacement; /** A word with no letter, in a face that draws punctuation by script * (#725, `measure/script.ts`): the script it was measured in and is * painted in (`VDTLineSegment.scriptContext`); `scriptRtl` when it was * measured as a right-to-left word (its bidi level is odd). `scriptRtl` * alone: a right-to-left word with letters in such a face, measured in * a context set right to left, as it is painted (`rtlTextWidth`). * Absent on every other token. */ scriptContext?: ScriptContext; scriptRtl?: true; /** The language of the run this word is of, when the run names its own * and the document is shaped in its own (`:ltr[…]{lang=en}`, #764): what * is measured of the word later (a cut, a piece) is measured in it. */ lang?: string; /** A word with a letter that may be painted in pieces with none * (`10.1006/ijhc.2001`): the script in force where its text starts, * which such a piece takes when no letter stands before it in the text * (`pieceScriptContext`). */ wordScript?: ScriptContext; /** In a document shaped in its own language (#770): the two halves of * this word, broken at a line end, are each measured as the text * painted. The rest that opens the next line is measured from the break * on (`lar`, not `instal·lar` less `instal·`), and its later pieces as * that text grows by them ({@link ownRest}). The value is the tracking * each of its characters was measured with, px. Absent in a document * that keeps the host's language, and on a word that holds CJK * characters (measured unit by unit, so its rest is already its own). */ ownPieces?: number; } /** Whether a resolved swatch colour can fill the square: a six- or * eight-digit hex (`#rrggbb`, `#rrggbbaa`) or an `rgb()` / `rgba()` * colour. Anything else leaves the swatch an empty outline. */ export declare function isSwatchFill(color: string): boolean; /** Side of an inline colour swatch: three quarters of the font size (the * height of a capital), read from the `px` size of the CSS font string. */ export declare function swatchSidePx(font: string): number; export declare function pickSpanFont(bold: boolean, italic: boolean, normalFont: string, boldFont: string, italicFont: string, boldItalicFont: string): string; /** Superscripts and subscripts are set at this fraction of the text size * (the compositor's default). */ export declare const SCRIPT_SIZE_RATIO = 0.583; /** A superscript rises this fraction of the text size: a third of an em, * as browsers raise `vertical-align: super`. */ export declare const SUPERSCRIPT_SHIFT_RATIO = 0.333; /** A subscript drops this fraction of the text size (EF-80): 0.15 em, the * drop TeX gives a subscript alone (σ16 of its text fonts, MathJax's * `$T_0$`), which keeps a figure inside the descender line. It used to * drop as far as a superscript rises, and hung below the descenders. */ export declare const SUBSCRIPT_SHIFT_RATIO = 0.15; /** A subscript stacked under a superscript (`T~0~^2^`) drops this fraction * of the text size instead: 0.25 em, TeX's drop for a subscript with a * superscript over it (σ17, 0.247 em), which clears room between the two. */ export declare const STACKED_SUBSCRIPT_SHIFT_RATIO = 0.25; /** `font` at `scale` times its size. */ export declare function scaledFont(font: string, scale: number): string; /** The font a script token is measured and painted with (`font` at the * script size) and its baseline shift in px (negative: up). `stacked`: a * subscript with a superscript set over it, which drops further. */ export declare function scriptMetrics(font: string, script: 'sup' | 'sub', stacked?: boolean): { font: string; baselineShift: number; }; /** * The subscripts and superscripts to stack (EF-80): a subscript and a * superscript that touch, with nothing between them (`T~0~^2^`, or * `T^2^~0~`), are set one over the other, as TeX sets `T_0^2`, not one after * the other. `scripts[i]` is the script of the i-th run of a line or word * (undefined for anything else); returns the index of the first run of each * pair. A run pairs once: after `x~a~^b^~c~` the `c` follows the pair. * * Stacked runs are painted at the same pen position: the first advances * nothing (its width becomes 0 and it is flagged `stacked`), the second * carries the pair's advance, the wider of the two — so every renderer that * paints runs one after another by their widths sets them stacked, and the * widths still add up to the line's. */ export declare function stackedScriptPairs(scripts: readonly ('sup' | 'sub' | undefined)[]): number[]; /** Small capitals are synthesised, the same way on every backend: a * lowercase letter is set as its capital at this fraction of the text * size, while capitals, digits and punctuation keep the full size — the * ratio browsers use for a face without real small caps. */ export declare const SMALL_CAPS_SIZE_RATIO = 0.7; /** `font` at the small-caps size. */ export declare function smallCapsFont(font: string): string; /** A text cut into runs of full-size characters and of lowercase letters * (`small`, already set as capitals). */ export declare function smallCapsRuns(text: string): { text: string; small: boolean; }[]; /** Advance of `text` set in small capitals from `font` (per-character * tracking not included). */ export declare function smallCapsWidth(text: string, font: string): number; /** Advance of `text` in `font`, set in small capitals when `smallCaps`. * In vertical text (`measuringVertically`) a character that stands in a * cell of its own advances its cell (`verticalTextWidth`), as the * renderers paint it, and so does a short number set in one cell * (`cjk.uprightDigits`) and a Japanese pair of !? marks * (`mayHoldVerticalCell`). */ export declare function textWidth(text: string, font: string, smallCaps: boolean | undefined): number; /** Segments that still carry the small-caps flag of their token. */ export type PendingSegment = VDTLineSegment & { smallCaps?: boolean; }; /** * Split every small-caps segment of the broken lines into runs painted as * they were measured: capitals at the segment's font, lowercase letters as * capitals at the small-caps size (a `fontString` of their own). The last * run absorbs any rounding so the segment keeps its measured width, and the * line text follows the painted case (the same length, so source maps * hold). */ export declare function expandSmallCaps(lines: VDTLine[], normalFont: string, boldFont: string, italicFont: string, boldItalicFont: string, letterSpacingPx: number): void; /** The text band of a chip box around the baseline, as fractions of the * chip's font size: from the ascenders down past the descenders; the * vertical padding and border grow it. */ export declare const CHIP_ASCENT_RATIO = 0.8; export declare const CHIP_DESCENT_RATIO = 0.25; /** * One atomic token for an inline chip: its words measured run by run (the * span font — plus the chip style's bold / italic — at the chip's family * and size), boxed by the horizontal padding and border. The box height is * the chip's text band plus the vertical padding and border; it never * enters the line height. A chip span without a resolved box sets as bare * text. */ export declare function chipToken(span: InlineSpan, normalFont: string, boldFont: string, italicFont: string, boldItalicFont: string, letterSpacingPx: number): RichToken; /** * Tokenize inline spans into word/space tokens with bold/italic flags and measured widths. */ /** A word that is a URL or a bare DOI: never hyphenated by the dictionary, * but breakable at its own joints so a long link cannot force an * unfillable line. */ export declare const URL_LIKE_RE: RegExp; /** Break opportunities inside a URL, following the usual editorial rule: * after a slash (never inside the `//` of the scheme) or before a dot, * hyphen or query/fragment punctuation, so the punctuation opens the next * line and no hyphen is added. Never within the first few characters. */ export declare function urlBreakIndices(text: string): number[]; /** * A word wider than the whole line (a table cell, a narrow column): divide * it at the last no-break space that leaves a head that fits ("300 000 | * 000", nothing added), else at the last syllable that fits — the * dictionary's, a hyphen added — or, when none does, at the last character * that fits, so no word ever runs past its measure. A cut next to a hyphen * the word carries falls after it and adds none (`hard`): never "fisica-" | * "-universitaria" nor "fisica--". Null when not even one character fits, * or, with `syllablesOnly`, when no no-break space or syllable does. * * A word of a joining script (Arabic…) is never cut between its letters, * and no hyphen marks a cut in it: only at a no-break space, or where it * touches a run of another script (`ABCكتاب`), with nothing added. Null * when no such place fits: the word then runs past the measure, and the * line says so (`VDTLine.wordOverflow`). * * Before a syllable, it is divided after a slash or at a zero-width space * the author typed, nothing added (#1164). * * `divided` says how a word was cut (#1150): at a syllable (`'syllable'`) * or between two characters (`'character'`); absent for a cut at a * no-break space, after a slash, at a zero-width space, at the edge of a run of another script, or next to an * ideograph, which divide no word. */ export declare function emergencySplit(token: RichToken, font: string, letterSpacingPx: number, lineMaxWidth: number, syllablesOnly?: boolean): { head: RichToken; tail: RichToken; hard?: boolean; divided?: 'syllable' | 'character'; } | null; /** The script fields of a span's tokens (the font at the script size, and * the shift), none for text that is no superscript or subscript. */ export declare function spanScriptFields(span: InlineSpan, normalFont: string, boldFont: string, italicFont: string, boldItalicFont: string): Pick; /** Whether a span sets something other than its text: a `:ref` or a * note marker (its label), a chip, a swatch or a formula. The orientation * marks of vertical text leave such a span as it is. */ export declare function setsObject(span: InlineSpan): boolean; /** * Vertical text: `spans` with each short number that runs with Latin text * (`latinReader`: `printed in 49 and 32 copies`) set sideways, as * `:sideways[…]` sets it, where the words it runs with lie past a space or * in another span (#222). A line keeps each word of a vertical paragraph in * a segment of its own, and a segment alone would read such a number * without its neighbours and stand it upright (`uprightDigitRuns`): the * number is measured and painted sideways as the paragraph reads it, * also one whose marks touch it (`(49)`, `49,`): the words those lead to * may lie in another segment. A piece of the paragraph never reads a * number as Latin that the paragraph stands upright (`latinReader`), so * the numbers left alone read alike in every segment. Nothing changes * when `digits` is 0 or no span holds a digit. */ export declare function sidewaysNumberSpans(spans: InlineSpan[], digits: number, region: CjkRegion): InlineSpan[]; /** * The one token of a span that is set as a whole, or undefined for text: * - an inline `:ref` or a footnote marker: the resolved label (already in * `text`) as one box that never wraps apart. Kind 'text' keeps the * generic layout and renderers treating it as a word; `refResourceId` / * `footnoteId` flow onto the segment for link colouring and PDF links; * - a chip: a box with its own runs, never broken or hyphenated inside; * - a swatch: one square the size of a capital; * - a formula: one box with the render's `widthPx`. Kind 'text' (not * 'space'), so trimming a line's trailing spaces never drops it. */ export declare function atomicSpanToken(span: InlineSpan, normalFont: string, boldFont: string, italicFont: string, boldItalicFont: string, letterSpacingPx: number): RichToken | undefined; /** * The tokens of a reference whose label is words (`ref.breakable`, #702): * one per piece of the label (`refLabelPieces`: a word, with the number that * keeps to it, or a run of breaking spaces), or undefined for a label that * is one token ({@link atomicSpanToken}). A word is built as the whole * label is — the reference's font, no dictionary points, never divided — * and a space is an ordinary space of the line: a legal break, stretched * and shrunk with the others. Every piece carries the reference, so the * link has no holes; every piece after the first is flagged * `refContinues`, so the reference stays one position in the source. A * no-break space inside the label glues, as anywhere. */ export declare function refLabelTokens(span: InlineSpan, normalFont: string, boldFont: string, italicFont: string, boldItalicFont: string, letterSpacingPx: number): RichToken[] | undefined; /** The segment a token (or the part of one on a line) sets: its kind, text * (soft hyphens removed), width and every flag the renderers read. Small * capitals stay flagged until {@link expandSmallCaps} splits them. */ export declare function tokenSegment(t: RichToken): PendingSegment; /** * Widen the spaces of a bibliography label (#290) so the entry's text * starts `columnPx` from the line's start: the gap after the label takes * what the label leaves of the column (its own width at least), or, with * a space before the label, that space takes it and the label ends against * the gap. `items` are a paragraph's tokens or units in order; `setWidth` * writes a new width. */ export declare function setLabelTabs(items: readonly T[], tabOf: (item: T) => 'lead' | 'gap' | undefined, columnPx: number, setWidth: (item: T, width: number) => void): void; /** * Measure a rich text block with mixed font weights. * Uses canvas measureText for per-token measurement and greedy line-breaking. * A text of nothing but whitespace, no-break spaces included, sets no line. */ export declare function measureRichBlock(spans: InlineSpan[], normalFont: string, boldFont: string, italicFont: string, boldItalicFont: string, maxWidthPx: number, lineHeightPx: number, options?: MeasureBlockOptions): MeasuredBlock; /** * {@link measureRichBlock} for a paragraph of a table cell, or a piece of a * caption or a note: a text of nothing but no-break spaces sets a line of * them, as CommonMark reads a line holding U+00A0 (EF-154). Ordinary * whitespace, and a lone zero-width U+FEFF, still set none (see * {@link isBlankText}). The body keeps {@link measureRichBlock}'s rule, as * its parser drops such a paragraph too. */ export declare function measureRichSnippet(spans: InlineSpan[], normalFont: string, boldFont: string, italicFont: string, boldItalicFont: string, maxWidthPx: number, lineHeightPx: number, options?: MeasureBlockOptions): MeasuredBlock; /** `spans` with their tabs (#622) set as word spaces: vertical text, and a * paragraph the CJK composer sets for its ruby, warichu or kanbun marks. */ export declare function tabsAsSpaces(spans: readonly InlineSpan[]): InlineSpan[]; //# sourceMappingURL=rich.d.ts.map