/** Ruby annotation type per JLReq. */ type RubyType = 'mono' | 'group' | 'jukugo'; /** * A ruby annotation over a contiguous span of base text. * Indices refer to positions in the base text's codepoint array. */ interface RubyAnnotation { /** Start index in base text (inclusive). */ startIndex: number; /** End index in base text (exclusive). */ endIndex: number; /** Ruby text as Unicode codepoints. */ rubyText: Uint32Array; /** Advance widths of each ruby character in px. */ rubyAdvances: Float32Array; /** @defaultValue 'mono' */ type?: RubyType; /** * For jukugo ruby: base-text-relative indices where line breaks are permitted. * E.g., 東京都 (indices 0,1,2) with splitPoints [1,2] allows breaks after 東 and 京. */ jukugoSplitPoints?: number[]; } /** * Result of ruby preprocessing: effective advances and cluster IDs * that encode ruby constraints for the line breaking algorithm. */ interface RubyPreprocessResult { /** Adjusted advance widths accounting for the width ruby text reserves. */ effectiveAdvances: Float32Array; /** Cluster IDs encoding ruby grouping constraints. */ clusterIds: Uint32Array; } /** * Returns true if the codepoint is hiragana (U+3040–U+309F) or katakana (U+30A0–U+30FF). */ declare function isKana(cp: number): boolean; /** * Preprocesses ruby annotations into effective advances and cluster IDs. * * When ruby text is wider than its base text, the excess is distributed * proportionally across the base characters, so an annotated span reserves the * larger of its base width and its ruby width. Ruby is never charged to a * neighbouring character: the render layer draws each annotation inside its own * span, so the sum of the effective advances on a line is an upper bound for * the inline extent that rendering produces and ruby is never clipped. * * Clustering prevents line breaks within ruby groups: * - `group`: all base characters share one cluster ID (no internal breaks). * - `jukugo`: sub-groups between split points share cluster IDs. * - `mono`: single base character, no clustering needed. * * A `jukugo` annotation that fully covers other annotations is an aggregate: * it only contributes split points, while the covered annotations own the * ruby text and therefore the width. Annotations must not otherwise overlap. * * @param text - Base text codepoints. * @param advances - Original advance widths. * @param annotations - Ruby annotations sorted by startIndex. * @param existingClusterIds - Optional pre-existing cluster IDs to merge with. * @returns Effective advances and merged cluster IDs. */ declare function preprocessRuby(text: Uint32Array, advances: Float32Array, annotations: RubyAnnotation[], existingClusterIds?: Uint32Array): RubyPreprocessResult; /** * Configuration options for the MejiroBrowser instance. */ interface MejiroBrowserOptions { /** Fixed font family. When set, all layouts use this font family. */ fixedFontFamily?: FontFamily; /** Fixed font size in pixels. When set, all layouts use this font size. */ fixedFontSize?: number; /** * When true, `layout()` throws if the requested family measures exactly like * the host's default font, which is how a silent fallback presents itself. * When false (the default), layout proceeds with whatever the host resolved. */ strictFontCheck?: boolean; } /** Ruby variant of {@link InlineAnnotation}. */ interface InlineRubyAnnotation { /** * Discriminant of the {@link InlineAnnotation} union. `'ruby'` and `'tcy'` are * the two variants the line breaker consumes; the rest are render-only. */ kind: 'ruby'; /** Start index in the base text string (character index, not byte). */ startIndex: number; /** End index in the base text string (exclusive). */ endIndex: number; /** Ruby text string. */ rubyText: string; /** @defaultValue 'mono' */ type?: 'mono' | 'group' | 'jukugo'; /** For jukugo ruby: base-text-relative indices where line breaks are permitted. */ jukugoSplitPoints?: number[]; } /** Emphasis-dot (傍点) annotation. */ interface InlineEmphasisAnnotation { /** Discriminant of the {@link InlineAnnotation} union. */ kind: 'emphasis'; /** Start index in the base text string (character index, not byte). */ startIndex: number; /** End index in the base text string (exclusive). */ endIndex: number; /** Dot glyph style. @defaultValue 'sesame' */ style?: 'sesame' | 'dot' | 'circle'; } /** * Tate-chu-yoko (縦中横) annotation — display the span horizontally inside a * vertical column. * * Reaches the line breaker: the span is given a fresh cluster ID of its own, so * it cannot be split across a column boundary, and its effective advances sum to * exactly one em — the width `text-combine-upright: all` draws — distributed * over the span's characters in proportion to their measured advances. * * Preprocessing runs before ruby, so a ruby span covering a combined box * distributes its excess over the collapsed width rather than over the measured * widths the box has already replaced. Unlike ruby, a malformed span (empty, * reversed, out of range, non-integral, non-finite advance, or overlapping an * already-applied span) is skipped instead of throwing, because these spans come * from arbitrary EPUB markup and one broken span must not fail a whole chapter. */ interface InlineTcyAnnotation { /** Discriminant of the {@link InlineAnnotation} union. */ kind: 'tcy'; /** Start index in the base text string (character index, not byte). */ startIndex: number; /** End index in the base text string (exclusive). */ endIndex: number; } /** Italic emphasis (``). */ interface InlineEmAnnotation { /** Discriminant of the {@link InlineAnnotation} union. */ kind: 'em'; /** Start index in the base text string (character index, not byte). */ startIndex: number; /** End index in the base text string (exclusive). */ endIndex: number; } /** Strong emphasis (``). */ interface InlineStrongAnnotation { /** Discriminant of the {@link InlineAnnotation} union. */ kind: 'strong'; /** Start index in the base text string (character index, not byte). */ startIndex: number; /** End index in the base text string (exclusive). */ endIndex: number; } /** Hyperlink annotation. */ interface InlineLinkAnnotation { /** Discriminant of the {@link InlineAnnotation} union. */ kind: 'link'; /** Start index in the base text string (character index, not byte). */ startIndex: number; /** End index in the base text string (exclusive). */ endIndex: number; /** * Destination URL, stored as authored. Renderers sanitize it on the way out: * a scheme other than `http`, `https` or `mailto` degrades to plain text * instead of producing a link. */ href: string; /** Advisory text for the link's `title` attribute. */ title?: string; } /** Footnote reference annotation. */ interface InlineFootnoteAnnotation { /** Discriminant of the {@link InlineAnnotation} union. */ kind: 'footnote'; /** Start index in the base text string (character index, not byte). */ startIndex: number; /** End index in the base text string (exclusive). */ endIndex: number; /** Identifier of the corresponding footnote entry. */ noteId: string; } /** * Inline annotation that applies to a contiguous span of base text. * * Replaces the v0.4-only `RubyInputAnnotation` with a discriminated union so * the same model can carry ruby, emphasis dots, tate-chu-yoko, simple emphasis, * hyperlinks, and footnote references through the layout / render pipeline. * * The `ruby` and `tcy` variants reach the line breaker — both are resolved to * effective advances and cluster IDs before breaking (tate-chu-yoko first, so * ruby distributes over the already-collapsed width). Every other variant is * render-only: it contributes no cluster ID and no advance correction, so the * breaker may split such a span across a column boundary. */ type InlineAnnotation = InlineRubyAnnotation | InlineEmphasisAnnotation | InlineTcyAnnotation | InlineEmAnnotation | InlineStrongAnnotation | InlineLinkAnnotation | InlineFootnoteAnnotation; /** * @deprecated Renamed to {@link InlineRubyAnnotation}; use {@link InlineAnnotation} * for new code. Removal of this alias is deferred to a future major release; no * removal version is scheduled. */ type RubyInputAnnotation = InlineRubyAnnotation; /** * A paragraph to lay out, with text and optional inline annotations. */ interface ParagraphInput { /** Text string to lay out. */ text: string; /** Inline annotations (ruby, emphasis, tcy, em/strong, link, footnote). */ inlineAnnotations?: readonly InlineAnnotation[]; /** Font family override for this paragraph (e.g. for headings with a different typeface). */ fontFamily?: FontFamily; /** Font size override in pixels for this paragraph (e.g. for headings). */ fontSize?: number; /** * Token boundary indices for morphological-aware line breaking. * @see {@link LayoutInput.tokenBoundaries} */ tokenBoundaries?: Uint32Array | readonly number[]; } /** * Options for laying out an entire chapter (multiple paragraphs). */ interface ChapterLayoutOptions { /** Paragraphs to lay out. */ paragraphs: readonly ParagraphInput[]; /** CSS font family for body text. Falls back to MejiroBrowser's fixedFontFamily. */ fontFamily?: FontFamily; /** Font size in pixels for body text. Falls back to MejiroBrowser's fixedFontSize. */ fontSize?: number; /** Available line width in pixels (use `verticalLineWidth()` for vertical text). */ lineWidth: number; /** Kinsoku mode. @defaultValue 'strict' */ mode?: 'strict' | 'loose'; /** Whether to enable hanging punctuation. @defaultValue true */ enableHanging?: boolean; } /** * Layout result for a single paragraph within a chapter. */ interface ParagraphLayoutResult { /** Break result from the line breaking algorithm. */ breakResult: BreakResult; /** Character array of the paragraph text, indexed by NFC Unicode codepoint. */ chars: string[]; } /** * Result of laying out an entire chapter. */ interface ChapterLayoutResult { /** Per-paragraph layout results. */ paragraphs: ParagraphLayoutResult[]; } /** * Options for a single layout operation. */ interface LayoutOptions { /** Text string to lay out. */ text: string; /** CSS font family. Overrides fixedFontFamily. */ fontFamily?: FontFamily; /** Font size in pixels. Overrides fixedFontSize. */ fontSize?: number; /** Available line width in pixels. */ lineWidth: number; /** Kinsoku mode. @defaultValue 'strict' */ mode?: 'strict' | 'loose'; /** Whether to enable hanging punctuation. @defaultValue true */ enableHanging?: boolean; /** Inline annotations (ruby, emphasis, tcy, em/strong, link, footnote). */ inlineAnnotations?: readonly InlineAnnotation[]; /** * Token boundary indices for morphological-aware line breaking. * @see {@link LayoutInput.tokenBoundaries} */ tokenBoundaries?: Uint32Array | readonly number[]; } /** * Font family specifier. Either a CSS-ready string (e.g. `'"Noto Serif JP", serif'`) * or an array of family names (e.g. `['Noto Serif JP', 'serif']`). Arrays are * escaped + joined per CSS rules by {@link normalizeFontFamily}. */ type FontFamily = string | readonly string[]; /** * Normalizes a {@link FontFamily} value to a CSS font-family string. * * Strings pass through unchanged. Arrays have each entry quoted only when * required by CSS (names containing spaces or non-identifier characters), * then joined with `, `. */ declare function normalizeFontFamily(family: FontFamily): string; /** * Composes a CSS font specification from font family and size. * * @param fontFamily - CSS font family (string or array). * @param fontSize - Font size in pixels. * @returns CSS font specification string (e.g. `'16px "Noto Serif JP", serif'`). */ declare function toFontSpec(fontFamily: FontFamily, fontSize: number): string; /** * A tate-chu-yoko (縦中横) span: characters drawn side by side inside a single * upright box of a vertical column. * * Indices refer to positions in the base text's codepoint array. The span is * indivisible and occupies {@link TcyAnnotation.advance} in the inline * direction regardless of how wide its characters measure on their own, * because `text-combine-upright` collapses them into one box. */ interface TcyAnnotation { /** Start index in base text (inclusive). */ startIndex: number; /** End index in base text (exclusive). */ endIndex: number; /** * Inline extent the combined box occupies in px — one em of the font the * span is drawn with, which is what `text-combine-upright: all` produces. */ advance: number; } /** * Result of tate-chu-yoko preprocessing: effective advances and cluster IDs * that encode the combined boxes for the line breaking algorithm. */ interface TcyPreprocessResult { /** Advance widths with every combined span collapsed to its box width. */ effectiveAdvances: Float32Array; /** Cluster IDs marking each combined span as indivisible. */ clusterIds: Uint32Array; } /** * Collects the tate-chu-yoko spans of an inline annotation list. * * @param annotations - Inline annotations of one paragraph, or `undefined`. * @param em - Font size in px of the text the spans sit in; one em is the * inline extent a combined box occupies. * @returns The tcy spans, or `undefined` when the paragraph has none — which * lets callers keep the "no annotations, no preprocessing" fast path. */ declare function buildTcyAnnotations(annotations: readonly InlineAnnotation[] | undefined, em: number): TcyAnnotation[] | undefined; /** * Preprocesses tate-chu-yoko spans into effective advances and cluster IDs. * * A combined span reserves exactly its box width rather than the sum of its * characters' advances, and shares one cluster ID so the line breaker cannot * split it across a column boundary. The box width is spread over the span's * characters in proportion to their measured advances, so anchor rectangles * and hit tests stay monotonic inside the span. * * Unlike ruby, malformed input is skipped rather than rejected: these spans * come from arbitrary EPUB markup, and a broken one must not stop a chapter * from being laid out. Ignored are spans that are empty, reversed, out of * range, non-integral, carry a non-finite advance, or overlap a span that was * already applied (earlier start wins, then the longer one). * * @param text - Base text codepoints. * @param advances - Measured advance widths. * @param annotations - Tate-chu-yoko spans in any order. * @param existingClusterIds - Optional pre-existing cluster IDs to merge with. * @returns Effective advances and merged cluster IDs. */ declare function preprocessTcy(text: Uint32Array, advances: Float32Array, annotations: readonly TcyAnnotation[], existingClusterIds?: Uint32Array): TcyPreprocessResult; /** * Input parameters for the line breaking algorithm. */ interface LayoutInput { /** Text as an array of Unicode codepoints. */ text: Uint32Array; /** Advance width of each character in pixels. */ advances: Float32Array; /** * Available line width in pixels. * Used as the uniform width for all lines, unless `lineWidths` is provided. */ lineWidth: number; /** * Per-line widths in pixels, overriding `lineWidth` for individual lines. * When provided, the i-th line uses `lineWidths[i]` as its width. * Lines beyond the array length fall back to `lineWidth`. */ lineWidths?: Float32Array; /** Kinsoku (line break prohibition) mode. @defaultValue 'strict' */ mode?: KinsokuMode; /** Whether to enable hanging punctuation. @defaultValue true */ enableHanging?: boolean; /** Cluster IDs — characters sharing the same ID cannot be split across lines. */ clusterIds?: Uint32Array; /** Ruby annotations for furigana support. */ rubyAnnotations?: RubyAnnotation[]; /** * Tate-chu-yoko spans. Each one is collapsed to a single indivisible box of * its own width before breaking, so a combined run is never split across a * column boundary and reserves one em instead of the sum of its characters. */ tcyAnnotations?: readonly TcyAnnotation[]; /** * Sorted array of codepoint indices representing token boundaries. * Each value is the index of the last codepoint in a token. * The algorithm prefers breaking at these positions over mid-token positions. * Use {@link tokenLengthsToBoundaries} to convert morphological analyzer output. */ tokenBoundaries?: Uint32Array | readonly number[]; /** Custom kinsoku rules. When provided, overrides the default rules. */ kinsokuRules?: KinsokuRules; } /** * Result of the line breaking computation. */ interface BreakResult { /** Array of break point indices (index of the last character before each break). */ breakPoints: Uint32Array; /** Hanging adjustment amount in pixels for each line. 0 if no hanging occurs. */ hangingAdjustments?: Float32Array; /** * Per-character effective advances after tate-chu-yoko collapsing and ruby * width distribution. Present when either kind of annotation was provided. */ effectiveAdvances?: Float32Array; /** Actual line width used for each line. Present when per-line `lineWidths` was provided. */ lineWidths?: Float32Array; } /** * Kinsoku processing mode. * - `'strict'`: Full prohibition including small kana and long vowel mark. * - `'loose'`: Allows small kana and long vowel mark at line start. */ type KinsokuMode = 'strict' | 'loose'; /** * Custom kinsoku (line break prohibition) rules. * * Use {@link buildKinsokuRules} to create an instance from raw codepoint arrays. */ interface KinsokuRules { /** Codepoints prohibited at the start of a line. */ lineStartProhibited: number[]; /** Codepoints prohibited at the end of a line. */ lineEndProhibited: number[]; /** Adjacent codepoint pairs that must not be split across lines. */ unbreakablePairs: Array; /** Pre-computed lookup set for lineStartProhibited. */ lineStartProhibitedSet: Set; /** Pre-computed lookup set for lineEndProhibited. */ lineEndProhibitedSet: Set; /** Pre-computed lookup set for unbreakablePairs. */ unbreakablePairSet: Set; } export { type BreakResult as B, type ChapterLayoutOptions as C, type FontFamily as F, type InlineAnnotation as I, type KinsokuRules as K, type LayoutInput as L, type MejiroBrowserOptions as M, type ParagraphInput as P, type RubyAnnotation as R, type TcyAnnotation as T, type KinsokuMode as a, type RubyPreprocessResult as b, type RubyType as c, type TcyPreprocessResult as d, buildTcyAnnotations as e, preprocessTcy as f, type RubyInputAnnotation as g, type LayoutOptions as h, isKana as i, type ChapterLayoutResult as j, type InlineEmAnnotation as k, type InlineEmphasisAnnotation as l, type InlineFootnoteAnnotation as m, type InlineLinkAnnotation as n, type InlineRubyAnnotation as o, preprocessRuby as p, type InlineStrongAnnotation as q, type InlineTcyAnnotation as r, type ParagraphLayoutResult as s, normalizeFontFamily as t, toFontSpec as u };