import { A as AnchorLocation, I as InChapterAnchor, a as AnchorRect, b as AnchorRange } from '../anchor-BWfPiv2j.js'; export { R as ReadingAnchor } from '../anchor-BWfPiv2j.js'; import { I as InlineAnnotation, T as TcyAnnotation, R as RubyAnnotation } from '../types-DnPOMbQV.js'; export { g as RubyInputAnnotation } from '../types-DnPOMbQV.js'; import { H as HeadingStyle, j as PageSize, k as ParagraphKind, l as BookImage, R as RenderEntry, m as SpreadResult, n as PageResult, B as BookOptions, o as ComputePageSizeOptions, q as BookParagraph } from '../measures-BildgW3g.js'; export { r as PageLine } from '../measures-BildgW3g.js'; import { M as ManuscriptDialect } from '../manuscript-DeQQoLzm.js'; /** * Options for {@link ChapterLayout.findText}. */ interface FindTextOptions { /** * Treat `query` as a regular expression source string instead of a literal * substring. The pattern is compiled with the `g` flag plus `i` when * {@link FindTextOptions.caseSensitive} is `false`. * @defaultValue false */ regex?: boolean; /** * Match case sensitively. When `false`, both literal and regex matches use * the `i` flag. * @defaultValue false */ caseSensitive?: boolean; /** * Cap on the number of matches returned. Useful for incremental UIs that * only render the first N hits. */ maxResults?: number; } /** * A single match returned by {@link ChapterLayout.findText}. * * Combines the in-chapter codepoint range with the resolved layout location * (spread / page / line / side) so callers can both highlight the match and * navigate to it. */ interface SearchMatch extends AnchorLocation { /** Zero-based paragraph index containing the match. */ paragraph: number; /** Inclusive codepoint offset of the match start (anchor-compatible). */ charStart: number; /** Exclusive codepoint offset of the match end. */ charEnd: number; /** The matched substring. */ match: string; } /** * Serializable snapshot of a {@link ChapterLayout}. * * Captures measurement output (advances, ruby layout) and break decisions so * a layout can be reconstructed without invoking the browser-side measurer. * Designed for SSR / build-time pre-computation: the server runs * `layout.snapshot()`, ships the JSON to the client, and the client calls * `MejiroBook.layoutFromSnapshot(snapshot)` to skip the measurement round-trip. * * **Owns its data:** a snapshot shares no object with the layout it was taken * from, so it can be mutated, transferred or serialized freely without the live * layout observing the change. * * **Authoritative config:** the snapshot bakes in the `fontSize` / `lineSpacing` * / `pageWidth` / `lineWidth` / etc. that were active when it was taken. Calling * `layoutFromSnapshot` then `setOptions` re-measures from scratch — see the * {@link MejiroBook.layoutFromSnapshot} docs. */ interface ChapterLayoutSnapshot { /** Snapshot format version. Bump when the shape changes. */ version: 1; /** Layout configuration at snapshot time. */ config: ChapterLayoutSnapshotConfig; /** Page geometry at snapshot time. */ size: Required; /** Per-paragraph data. */ paragraphs: ParagraphSnapshot[]; /** Image exclusions keyed by spread index. Omitted for snapshots without images. */ images?: SpreadImagesSnapshot[]; } /** * Serializable subset of `LayoutConfig`. * * Every field holds the value that was actually in effect when the snapshot was * taken, with {@link BookOptions} defaults already applied — hence no optional * fields apart from `headingStyles`, which has no default. The font family is * deliberately absent: advances are already baked into the snapshot, so * restoring it needs no font. */ interface ChapterLayoutSnapshotConfig { /** Body font size in pixels the advances were measured at. */ fontSize: number; /** Line spacing multiplier used for column pitch. */ lineSpacing: number; /** Scale applied to heading font sizes with no per-level `headingStyles` entry. */ headingScale: number; /** Kinsoku mode the break points were produced under. */ mode: 'strict' | 'loose'; /** Whether hanging punctuation was enabled when breaking. */ enableHanging: boolean; /** Per-level heading overrides (levels 1–6). Omitted when none were set. */ headingStyles?: Record; } /** Per-paragraph snapshot entry. */ interface ParagraphSnapshot { /** Original paragraph text (JS string). `text` and `chars` are rebuilt from this. */ text: string; /** Per-codepoint advance widths (px). */ advances: number[]; /** * Break points in the `BreakResult` convention: the inclusive codepoint index * of the last character before each break. A paragraph therefore has * `breakPoints.length + 1` lines, and line `i` spans * `[breakPoints[i - 1] + 1, breakPoints[i] + 1)` — the ranges `getLineRanges` * produces from the same array. */ breakPoints: number[]; /** * Inline annotations (kept as the original kind-tagged objects). Copies, not * references into the live layout. */ inlineAnnotations: readonly InlineAnnotation[]; /** Legacy/generic heading marker when no heading level is available. */ isHeading?: boolean; /** Heading level (1–6), if any. */ headingLevel?: number; /** Structural classification of the paragraph. Omitted for `'body'`. */ kind?: ParagraphKind; /** Pre-resolved ruby layout (after width measurement). */ layoutRubyAnnotations?: LayoutRubySnapshot[]; /** * Pre-resolved tate-chu-yoko layout: the spans the line breaker collapses to * one box, with the box width already resolved against the paragraph's font * size. {@link TcyAnnotation} holds only numbers, so it needs no serializable * counterpart the way {@link LayoutRubySnapshot} does for its typed arrays. */ layoutTcyAnnotations?: TcyAnnotation[]; } /** * Serializable form of {@link RubyAnnotation}, with the typed arrays widened to * plain number arrays so the snapshot survives `JSON.stringify`. */ interface LayoutRubySnapshot { /** Start index in the base text's codepoint array (inclusive). */ startIndex: number; /** End index in the base text's codepoint array (exclusive). */ endIndex: number; /** Ruby text codepoints. */ rubyText: number[]; /** Per-codepoint advances for the ruby text. */ rubyAdvances: number[]; /** Ruby distribution rule per JLReq. @defaultValue 'mono' */ type?: 'mono' | 'group' | 'jukugo'; /** * For jukugo ruby: base-text-relative indices where line breaks are * permitted. E.g. 東京都 (indices 0,1,2) with `[1, 2]` allows breaks after * 東 and 京. */ jukugoSplitPoints?: number[]; } /** Serializable image exclusions for one spread. */ interface SpreadImagesSnapshot { /** Zero-based index of the spread the images belong to. */ spreadIndex: number; /** Image rectangles excluded on that spread, in right-page coordinates. */ images: BookImage[]; } /** @internal Cached per-paragraph data for fast re-layout. */ interface CachedParagraph { text: Uint32Array; advances: Float32Array; chars: string[]; inlineAnnotations: readonly InlineAnnotation[]; layoutRubyAnnotations?: RubyAnnotation[]; /** * Tate-chu-yoko spans with their box width already resolved against this * paragraph's font size. Kept alongside the advances so every re-break * (resize, re-measure, image exclusion) reserves the same one em per span * and refuses to split it, exactly as the initial layout did. */ layoutTcyAnnotations?: TcyAnnotation[]; isHeading?: boolean; headingLevel?: number; /** * Structural classification of the source paragraph, kept here so a re-break * (resize, re-measure, image exclusion) can put it back on the render entry. */ kind?: ParagraphKind; } /** @internal Layout configuration snapshot. */ interface LayoutConfig { fontSize: number; lineSpacing: number; headingStyles?: Record; headingScale: number; mode: 'strict' | 'loose'; enableHanging: boolean; } /** * Manages the layout of a single chapter with pagination, heading support, * and image exclusion. Created by {@link MejiroBook.layoutChapter}. * * Supports lazy computation: layout is only computed when data is first requested * via {@link getSpread} or {@link getPage}, and is cached until invalidated by * {@link resize}, {@link setImages}, or {@link clearImages}. */ declare class ChapterLayout { private cached; private entries; /** * @internal Per paragraph: the advances `entries[i].breakPoints` were * computed from. Filled on re-break and lazily for externally supplied * entries; see {@link ChapterLayout.layoutAdvancesOf}. */ private layoutAdvances; private config; private size; private images; private normal; private excl; /** * @internal Cached `SpreadExclusionEngine.compute()` results per spread. * * The spread-local exclusion compute is independent across spreads, so we * can keep results for unchanged spreads when `setImages` only modifies a * single spread. Invalidated wholesale on font / size / page-size changes. */ private spreadExclusionCache; /** @internal Created by MejiroBook — do not construct directly. */ constructor(cached: CachedParagraph[], entries: RenderEntry[], config: LayoutConfig, size: Required); /** Total number of pages in the current layout. */ get totalPages(): number; private exclusionTotalPages; /** Whether any spread has image exclusions set. */ get hasImages(): boolean; /** * @internal Applies a fresh layout config snapshot from {@link MejiroBook.setOptions}. * * Updates the fields in place, recomputes line breaks when `mode` / * `enableHanging` change, and invalidates the rendered caches so the next * `getSpread` / `getPage` call reflects the new options. */ applyConfig(config: LayoutConfig, options?: { rebreak?: boolean; }): void; /** @internal Exposes cached paragraphs so {@link MejiroBook} can re-measure on font change. */ getCachedParagraphs(): CachedParagraph[]; /** * @internal Recomputes line breaks after {@link MejiroBook} has refreshed * each cached paragraph's `advances` / `layoutRubyAnnotations`. Distinct * from {@link applyConfig} so a font change re-breaks once, not twice. */ recomputeAfterMeasurement(): void; /** * Updates page geometry and/or line spacing. * Re-computes line breaks if `lineWidth` changes. * * The update is applied as a unit: dimensions are validated and the new line * breaks are computed before any visible state changes, so a rejected size * leaves the layout on its previous geometry, entries and caches. * * @param size - Partial page size overrides plus optional `lineSpacing`. * @throws RangeError If `lineWidth` / `pageWidth` / `lineSpacing` is not a * positive finite number, or a padding is not a non-negative finite number. */ resize(size: Partial & { lineSpacing?: number; }): void; /** * Sets image exclusions for a spread. Passing an empty array removes images for that spread. * * @param spreadIndex - Zero-based spread index. * @param images - Image rectangles relative to the right page's top-left corner. */ setImages(spreadIndex: number, images: BookImage[]): void; /** Removes all image exclusions. */ clearImages(): void; /** * Sets or clears images for a spread and returns the updated spread result. * Combines {@link setImages} / {@link clearImages} with {@link getSpread}. * * @param spreadIndex - Zero-based spread index. * @param images - Image rectangles, or `undefined` / empty array to clear this spread. * @returns Updated spread result for the given spread. */ syncImages(spreadIndex: number, images?: BookImage[]): SpreadResult; /** * Returns layout data for a two-page spread. * * @param spreadIndex - Zero-based spread index. * @returns Spread result containing right and left page data. */ getSpread(spreadIndex: number): SpreadResult; /** * Returns layout data for a single page. * * @param pageIndex - Zero-based page index. * @returns Page result with paragraph data, flat lines, and column slots. */ getPage(pageIndex: number): PageResult; /** * Locates a reading position in the current layout. * * @param anchor - In-chapter anchor (paragraph + char index). * @returns The spread / page / line containing the anchor, or `null` if the * anchor is out of range, either field is not a non-negative safe integer, * or the chapter is empty. */ locateAnchor(anchor: InChapterAnchor): AnchorLocation | null; /** * Returns the in-chapter anchor for the first character of a spread. * * Useful for converting a spread index back into a stable reading anchor * that survives reflow. * * @param spreadIndex - Zero-based spread index. * @param side - Page side. `'right'` (default) is the first page in * vertical-rl reading order; `'left'` is the second page. * @returns An in-chapter anchor pointing at the first character of the * chosen page, or `null` if the spread / page does not exist. */ anchorAt(spreadIndex: number, side?: 'right' | 'left'): InChapterAnchor | null; /** * Returns the pixel bounding rectangle of the character at the given * in-chapter anchor. * * Coordinates are spread-local relative to the right page's content * top-left (see {@link AnchorRect}). Pass `null` results through — they * indicate the anchor is out of range for the current layout. * * Sizes follow the advances the current line breaks were computed from, so * ruby-widened characters get the extent they actually occupy. * * @param anchor - In-chapter anchor (paragraph + char index). * @returns Character rectangle, or `null` when the anchor cannot be located. */ coordOfAnchor(anchor: InChapterAnchor): AnchorRect | null; /** * Returns whether the anchor addresses a character position that exists. * * Both fields must be non-negative safe integers so that no lookup keyed on * them can be fractional or `NaN`; `charIndex` may equal the paragraph * length, which addresses the position past its last character. */ private isAnchorInRange; /** * Returns the in-chapter anchor at a pixel coordinate within a spread. * * Coordinates are spread-local relative to the right page's content * top-left (see {@link AnchorRect}). Right-page x is `[0, contentWidth]`; * left-page x is `[-contentWidth, 0]`. `y` covers the inline-direction * column height `[0, lineWidth]`. * * @param spreadIdx - Zero-based spread index. * @param x - Spread-local x in pixels. * @param y - Spread-local y in pixels. * @returns Anchor at the coordinate, or `null` if outside any column. */ anchorAtCoord(spreadIdx: number, x: number, y: number): InChapterAnchor | null; /** * Returns per-line rectangles covering the characters in `range`, suitable * for rendering a selection highlight overlay. * * Each returned rectangle covers a contiguous run of characters on a single * line. The range is normalized — `start` and `end` may be passed in either * order. An empty range (`start` equal to `end`) returns an empty array. * * Each page the range crosses is located and built once, so the cost follows * the number of pages spanned rather than the number of characters selected. * * @param range - The character range to highlight. * @returns Spread-local rectangles in document order. */ selectionRects(range: AnchorRange): AnchorRect[]; /** * Returns a serializable snapshot of this layout, suitable for SSR / * build-time pre-computation. Pair with {@link MejiroBook.layoutFromSnapshot} * to skip the measurement round-trip on the client. * * The snapshot bakes in the current config (font / size / line spacing / * page geometry); restoring with a different config requires either * passing the snapshot to a fresh `MejiroBook` whose options match, or * calling `setOptions` afterwards (which re-measures from scratch). */ snapshot(): ChapterLayoutSnapshot; /** * Searches the chapter text for matches of `query`, returning a list of * matches resolved to anchor + layout location. * * Search is paragraph-local — matches do not span paragraph boundaries. * Codepoint offsets (`charStart` / `charEnd`) are compatible with * {@link InChapterAnchor.charIndex} and survive reflow. * * @param query - Literal substring (default), a regex source string (when * {@link FindTextOptions.regex} is `true`), or a `RegExp` — whose `source` * takes the regex path whatever {@link FindTextOptions.regex} says. A * `RegExp` keeps its own `i` / `m` / `s` flags unless * {@link FindTextOptions.caseSensitive} is set, which then wins; `g` and * Unicode mode are always applied and `y` is ignored. * @param options - Search options. * @returns Matches in document order. Empty array when `query` is empty * or no matches are found. * @throws If the pattern is invalid or exceeds the regex safety limits. To * keep matching time bounded, the guard also refuses patterns that can * backtrack catastrophically: a quantified group that itself contains a * quantifier or an alternation, and two quantified terms in the same * concatenation with nothing between them that always consumes input * (`a*a*b`, `a*b?a*c`). A term that always consumes anchors the quantifiers * on either side of it, so `\d+年\d+月` is accepted. */ findText(query: string | RegExp, options?: FindTextOptions): SearchMatch[]; private contentWidth; private resolveScale; private paragraphScale; private linePitch; private measureOpts; private invalidate; private recomputeBreaks; /** * Breaks every paragraph at `lineWidth` without touching visible state, so * callers can validate the result before committing it. */ private breakEntries; private commitBreaks; /** * Returns the advances the paragraph's current `breakPoints` were produced * from: the measured advances with tate-chu-yoko collapsing and ruby width * distribution applied, in the order {@link computeBreaks} applies them. * Anchor geometry reads these so rectangles and hit tests follow the same * metric the line breaker used. */ private layoutAdvancesOf; private ensureNormal; private getNormalSpread; private buildNormalPage; private ensureExclusion; private computeExclusion; private computeEntriesWithLineWidths; private buildSpreadLayoutsAndWidths; private getExclusionSpread; private buildExclusionPage; private locateAnchorInNormal; private locateAnchorInExclusion; private anchorAtInNormal; private anchorAtInExclusion; private coordOfAnchorInNormal; private coordOfAnchorInExclusion; private makeAnchorRect; /** * Splits a normalized range into per-line runs, using the break points the * given entries carry. Runs are returned in document order. */ private selectionRuns; private selectionRectsInNormal; private selectionRectsInExclusion; /** Builds the rectangle covering one line-local run of selected characters. */ private makeRunRect; private anchorAtCoordInNormal; private anchorAtCoordInExclusion; private findSlotAt; private charFromY; } /** Default page padding values in pixels for the reading surface. */ declare const DEFAULT_PAGE_PADDING: { /** Horizontal padding on each side of a page. */ readonly x: 52; /** Top padding of a page. */ readonly y: 56; /** Bottom padding of a page. */ readonly bottom: 40; }; /** * Default page geometry used by {@link MejiroBook.computePageSize}. * * `headerOffset` and `gutterOffset` are space reserved on the container * (header chrome height + spread gutter), measured outside of a page's * own padding. */ declare const DEFAULT_PAGE_GEOMETRY: { /** Page aspect ratio (height / width). */ readonly aspect: 1.45; /** Minimum page width in pixels. */ readonly minWidth: 280; /** Minimum page height in pixels. */ readonly minHeight: 400; /** Maximum page height in pixels. */ readonly maxHeight: 780; /** Header chrome height reserved at the top of the container in pixels. */ readonly headerOffset: 56; /** Horizontal gutter reserved between/around the two pages in pixels. */ readonly gutterOffset: 48; }; /** * Default heading style overrides for levels 1–6. * * @example * ```ts * const book = new MejiroBook({ * fontFamily: 'serif', * fontSize: 16, * headingStyles: DEFAULT_HEADING_STYLES, * }); * ``` */ declare const DEFAULT_HEADING_STYLES: Readonly>; /** * Sensible defaults for {@link BookOptions}. Used by framework components * when no `options` prop is supplied so `` works out of the * box. Override individual fields by spreading: * * ```ts * { ...DEFAULT_BOOK_OPTIONS, fontFamily: '"Noto Serif JP"', fontSize: 18 } * ``` */ declare const DEFAULT_BOOK_OPTIONS: Readonly; /** * Constructor options for {@link MejiroBook} — {@link BookOptions} plus the * browser-integration switches a book owns on the caller's behalf. */ interface MejiroBookOptions extends BookOptions { /** * When true, layout throws if the requested font family measures exactly * like the host's default font, which is how a silent fallback presents * itself. @defaultValue false */ strictFontCheck?: boolean; } /** Manuscript chapter input accepted by {@link MejiroBook.layoutManuscript}. */ interface ManuscriptChapter { /** Optional id used as the key in the returned map. */ id?: string; /** Chapter title — emitted as an `h1` paragraph at the top of the layout. */ title: string; /** Raw manuscript body. Blank lines separate paragraphs. */ body: string; } /** Options for {@link MejiroBook.layoutManuscript}. */ interface LayoutManuscriptOptions { /** * Chapters to lay out. Laid out sequentially, and keyed in the returned map * by `id` — or by `chapter-` when a chapter carries no id, so * duplicate or missing ids collapse entries. */ chapters: readonly ManuscriptChapter[]; /** Manuscript notation dialect. @defaultValue `'mejiro'` */ dialect?: ManuscriptDialect; } /** * High-level API for Japanese vertical text layout. * * Manages font loading, character measurement, and provides a simple * interface for layout, pagination, and image exclusion. * * @example * ```ts * const book = new MejiroBook({ * fontFamily: '"Noto Serif JP"', * fontSize: 16, * lineSpacing: 1.8, * headingStyles: { 1: { scale: 1.6, gapAfterEm: 1.4 } }, * }); * * book.setPageSize({ pageWidth: 400, lineWidth: 600 }); * * const layout = await book.layoutChapter(chapter); * const spread = layout.getSpread(0); * // render spread.right and spread.left * ``` */ declare class MejiroBook { private opts; private size; private browser; private get measurer(); private layouts; private pendingOpts; private optionsGeneration; /** * Records the typographic options and creates the browser-side measurer. * Nothing is measured or loaded yet: fonts are loaded lazily on the first * layout, and page geometry is still unset, so call * {@link MejiroBook.setPageSize} (or {@link MejiroBook.computePageSize}) * before laying out a chapter — otherwise `layoutChapter` throws. * * @param options - Typography plus the optional `strictFontCheck` guard, * which is forwarded to the measurer and cannot be changed afterwards. */ constructor(options: MejiroBookOptions); /** Returns a snapshot of the current options. */ getOptions(): Readonly> & Pick>; /** * Updates book options and propagates the change to every live * {@link ChapterLayout} produced by this book. * * Changes that need no re-measurement (`lineSpacing` / `mode` / * `enableHanging`) are applied synchronously and the returned promise is * already resolved. Font family / size / heading scale changes require * re-measurement: the new values are staged and only become visible to * {@link getOptions} once the font has loaded, so every live layout always * holds advances measured with the font recorded in its own config. * * Overlapping calls converge on the last one — an earlier call whose font is * still loading resolves without overwriting the newer options. * * @throws If the font of a staged change fails to load. The rejection leaves * the previously applied options in place. */ setOptions(options: Partial): Promise; /** * Loads the font for a staged option set, then commits it and re-measures * every live layout in one synchronous step so advances and config can never * be observed out of sync. */ private commitMeasuredOptions; /** Walks tracked layouts, pruning collected ones and yielding the rest. */ private liveLayouts; private applyConfigToLayouts; /** * Re-measures every live layout against the committed options. Runs to * completion synchronously so no other call can interleave between the * advances and the config they belong to. */ private remeasureLayouts; private layoutConfigSnapshot; /** * Sets the page geometry used by subsequent {@link layoutChapter} calls. * Must be called before `layoutChapter`. */ setPageSize(size: PageSize): void; /** * Computes page dimensions from a container element and applies them * via {@link setPageSize}. * * Defaults to a 1.45 aspect ratio, page width minimums of 280×400 px, * a 780 px height ceiling, a 56 px header reservation, and a 48 px * gutter reservation. All of these are overridable via the second * argument; see {@link ComputePageSizeOptions}. * * @param container - DOM element representing the reading surface. * @param options - Page geometry and padding overrides. Defaults to * {@link DEFAULT_PAGE_GEOMETRY} + {@link DEFAULT_PAGE_PADDING}. * @returns Computed page width, page height, and content height. */ computePageSize(container: HTMLElement, options?: ComputePageSizeOptions): { pageWidth: number; pageHeight: number; contentHeight: number; }; /** * Lays out a chapter and returns a {@link ChapterLayout} for pagination and rendering. * * The chapter object is compatible with `EpubChapter` from `@libraz/mejiro/epub`. * Font loading and character measurement are handled automatically. * * The page geometry and options in effect when the call starts are captured * up front, so a concurrent {@link setPageSize} / {@link setOptions} cannot * leave the returned layout holding geometry its break points were not * computed for. * * @param chapter - Chapter with paragraphs to lay out. * @returns A layout object for retrieving pages and managing image exclusions. * @throws If {@link setPageSize} has not been called. */ layoutChapter(chapter: { paragraphs: readonly BookParagraph[]; }): Promise; /** * Lays out one or more manuscript chapters directly, skipping the EPUB ZIP * round-trip used by `MejiroEditor` / `EpubProject.export`. Intended for * live preview in manuscript editors. * * Each chapter body is split into paragraphs on blank lines and run through * {@link parseManuscript} so the renderer sees the same `InlineAnnotation`s * an exported EPUB would carry. * * @returns A map keyed by `chapter.id` (or the array index when missing). */ layoutManuscript(options: LayoutManuscriptOptions): Promise>; /** * Rebuilds a {@link ChapterLayout} from a {@link ChapterLayout.snapshot}. * * Skips the measurement round-trip (font loading + Canvas.measureText for * every codepoint) by reusing the pre-computed `advances` and ruby layout * baked into the snapshot. Intended for SSR / build-time pre-computation: * the server runs `layout.snapshot()`, ships the JSON to the client, and * the client calls this method on mount. * * The returned layout uses the **snapshot's** config and page geometry, * not this book's current options. Calling {@link MejiroBook.setOptions} * after restore propagates new font / size values which will trigger a * full re-measure (the measurer rebuilds advances from the live font). * * @param snapshot - Value previously returned by `layout.snapshot()`. * @returns A {@link ChapterLayout} positioned exactly as it was at snapshot time. */ layoutFromSnapshot(snapshot: ChapterLayoutSnapshot): ChapterLayout; /** Clears the character width measurement cache. */ clearCache(fontKey?: string): void; /** * Returns the current measurement cache size. * Useful for capacity monitoring across long reader sessions. * * @returns Number of font specs cached and the total number of codepoints * measured across all fonts. */ cacheStats(): { fonts: number; codepoints: number; }; } /** Options for {@link estimateReadingTime}. */ interface ReadingTimeOptions { /** * Characters-per-minute reading rate. Defaults to 600 — a commonly cited * average for Japanese text. Override for slow / fast readers or for * languages with different unit sizes. */ cpm?: number; /** * Include heading paragraphs in the character count. A paragraph counts as a * heading when {@link isHeadingParagraph} says so, i.e. it carries a * `headingLevel` or is classified as `kind: 'heading'`. Headings are usually * short and skim-read; excluding them yields a more conservative estimate. * @defaultValue false */ includeHeadings?: boolean; } /** * Minimal chapter shape {@link estimateReadingTime} needs. * * Structural on purpose: an `EpubChapter`, a `ChapterLayout` source chapter or * a bare object literal all satisfy it, so the estimate can be taken before a * chapter has been laid out. */ interface ChapterLike { /** Paragraphs whose characters are counted, headings included or not per options. */ paragraphs: readonly BookParagraph[]; } /** * Estimate the reading time of a chapter in milliseconds. * * Counts codepoints (via the string iterator), so surrogate pairs do not * inflate the total, and applies the configured characters-per-minute rate. * Heading paragraphs ({@link isHeadingParagraph}) are excluded unless * {@link ReadingTimeOptions.includeHeadings} is set. */ declare function estimateReadingTime(chapter: ChapterLike, options?: ReadingTimeOptions): number; /** Format a millisecond duration as compact Japanese or English text. */ declare function formatReadingTime(ms: number, locale?: 'ja' | 'en'): string; export { AnchorLocation, AnchorRange, AnchorRect, BookImage, BookOptions, BookParagraph, ChapterLayout, type ChapterLayoutSnapshot, type ChapterLayoutSnapshotConfig, type ChapterLike, ComputePageSizeOptions, DEFAULT_BOOK_OPTIONS, DEFAULT_HEADING_STYLES, DEFAULT_PAGE_GEOMETRY, DEFAULT_PAGE_PADDING, type FindTextOptions, HeadingStyle, InChapterAnchor, type LayoutManuscriptOptions, type LayoutRubySnapshot, type ManuscriptChapter, MejiroBook, type MejiroBookOptions, PageResult, PageSize, ParagraphKind, type ParagraphSnapshot, type ReadingTimeOptions, type SearchMatch, type SpreadImagesSnapshot, SpreadResult, estimateReadingTime, formatReadingTime };