@vyaz/core 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compile/DocumentCompiler.d.ts +40 -0
- package/dist/index.browser.d.ts +28 -0
- package/dist/index.browser.js +18 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +11 -147930
- package/dist/layout/AutoFitEngine.d.ts +44 -0
- package/dist/layout/LineBoxValidator.d.ts +25 -0
- package/dist/layout/ParagraphLayoutEngine.d.ts +46 -0
- package/dist/layout/PositioningEngine.d.ts +68 -0
- package/dist/layout/TextFrameLayoutEngine.d.ts +50 -0
- package/{src/layout/estimateWidth.ts → dist/layout/estimateWidth.d.ts} +2 -40
- package/dist/measure/FontEngine.d.ts +47 -0
- package/dist/measure/FontMetricsProvider.d.ts +49 -0
- package/dist/measure/FontNotFoundError.d.ts +6 -0
- package/dist/measure/SystemFontRegistry.d.ts +46 -0
- package/dist/measure/canvas-polyfill.d.ts +30 -0
- package/dist/types/Document.d.ts +593 -0
- package/dist/types/FontTypes.d.ts +61 -0
- package/dist/types/LayoutTypes.d.ts +128 -0
- package/{src/utils/env.ts → dist/utils/env.d.ts} +1 -8
- package/{src/utils/font.ts → dist/utils/font.d.ts} +1 -13
- package/{src/utils/groupLinesByParagraph.ts → dist/utils/groupLinesByParagraph.d.ts} +7 -37
- package/dist/utils/list.d.ts +41 -0
- package/dist/utils/textTransform.d.ts +32 -0
- package/package.json +13 -13
- package/src/compile/DocumentCompiler.ts +0 -144
- package/src/index.browser.ts +0 -90
- package/src/index.ts +0 -97
- package/src/layout/AutoFitEngine.ts +0 -101
- package/src/layout/LineBoxValidator.ts +0 -162
- package/src/layout/ParagraphLayoutEngine.ts +0 -264
- package/src/layout/PositioningEngine.ts +0 -615
- package/src/layout/TextFrameLayoutEngine.ts +0 -363
- package/src/measure/FontEngine.ts +0 -144
- package/src/measure/FontMetricsProvider.ts +0 -224
- package/src/measure/FontNotFoundError.ts +0 -16
- package/src/measure/SystemFontRegistry.ts +0 -152
- package/src/measure/canvas-polyfill.d.ts +0 -6
- package/src/measure/canvas-polyfill.ts +0 -243
- package/src/measure/fontkit.d.ts +0 -44
- package/src/types/Document.ts +0 -666
- package/src/types/FontTypes.ts +0 -74
- package/src/types/LayoutTypes.ts +0 -158
- package/src/utils/list.ts +0 -107
- package/src/utils/textTransform.ts +0 -96
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AutoFitEngine.ts — proportional font-size scaling (autofit).
|
|
3
|
+
*
|
|
4
|
+
* Algorithm: binary search for scale in [0.0, 1.0].
|
|
5
|
+
* For each candidate scale: temporarily scale fontSize,
|
|
6
|
+
* run full layout, check whether the result fits in
|
|
7
|
+
* maxWidth × maxHeight.
|
|
8
|
+
*
|
|
9
|
+
* Proportions are preserved: every TextRun's fontSize is multiplied
|
|
10
|
+
* by the same scale, the visual hierarchy is unchanged.
|
|
11
|
+
* inlineWidget dimensions are NOT scaled (images keep their size).
|
|
12
|
+
*/
|
|
13
|
+
import type { TextFrame } from '../types/Document.js';
|
|
14
|
+
/** Autofit options */
|
|
15
|
+
export interface AutoFitOptions {
|
|
16
|
+
minScale?: number;
|
|
17
|
+
tolerance?: number;
|
|
18
|
+
maxIterations?: number;
|
|
19
|
+
}
|
|
20
|
+
/** Autofit result */
|
|
21
|
+
export interface AutoFitResult {
|
|
22
|
+
scaleFactor: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Apply a scale factor to all fontSize values in the document.
|
|
26
|
+
* inlineWidget dimensions are NOT scaled.
|
|
27
|
+
* Returns a NEW document (does not mutate the original).
|
|
28
|
+
*/
|
|
29
|
+
export declare function applyScale(doc: TextFrame, scale: number): TextFrame;
|
|
30
|
+
/**
|
|
31
|
+
* Find the optimal scale factor for a document.
|
|
32
|
+
*
|
|
33
|
+
* @param doc — source document
|
|
34
|
+
* @param layoutFn — layout(doc) → { height: number; width: number }
|
|
35
|
+
* @param config — autofit maxWidth/maxHeight
|
|
36
|
+
* @param options — search precision
|
|
37
|
+
*/
|
|
38
|
+
export declare function findScale(doc: TextFrame, layoutFn: (scaledDoc: TextFrame) => {
|
|
39
|
+
height: number;
|
|
40
|
+
width: number;
|
|
41
|
+
}, config: {
|
|
42
|
+
maxWidth: number;
|
|
43
|
+
maxHeight: number;
|
|
44
|
+
}, options?: AutoFitOptions): AutoFitResult;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LineBoxValidator.ts — invariant checks and YAML serialization.
|
|
3
|
+
*
|
|
4
|
+
* Parley-inspired invariant checks:
|
|
5
|
+
* NO_OVERLAP, MONOTONIC_Y, INDEX_CONSIST, WIDTH_FIT, BASELINE_EQ
|
|
6
|
+
*
|
|
7
|
+
* YAML snapshots: semantic data only (no metric noise).
|
|
8
|
+
*/
|
|
9
|
+
import type { Line } from '../types/LayoutTypes.js';
|
|
10
|
+
export interface InvariantError {
|
|
11
|
+
invariant: string;
|
|
12
|
+
message: string;
|
|
13
|
+
details?: any;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Check all 5 invariants for a Line array.
|
|
17
|
+
* Throws on first violation.
|
|
18
|
+
*/
|
|
19
|
+
export declare function assertLineInvariants(lines: Line[], originalText: string, maxWidth: number): void;
|
|
20
|
+
/**
|
|
21
|
+
* Convert Line[] to YAML string for snapshots.
|
|
22
|
+
* Only semantic data: text, x, width, style.
|
|
23
|
+
* No glyphAdvances, fontMetrics (noise), inlineWidget.
|
|
24
|
+
*/
|
|
25
|
+
export declare function linesToYAML(lines: Line[], paragraphWidth: number, paragraphHeight: number): string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ParagraphLayoutEngine.ts — main orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline:
|
|
5
|
+
* Paragraph
|
|
6
|
+
* → compile (DocumentCompiler)
|
|
7
|
+
* → prepareRichInline (pretext)
|
|
8
|
+
* → walkRichInlineLineRanges + materializeRichInlineLineRange (pretext)
|
|
9
|
+
* → positionLines (PositioningEngine)
|
|
10
|
+
* → assertLineInvariants (LineInvariants)
|
|
11
|
+
*
|
|
12
|
+
* Supports autofit via AutoFitEngine.findScale.
|
|
13
|
+
* Caches PreparedRichInline per paragraph key (Parley LayoutContext pattern).
|
|
14
|
+
*/
|
|
15
|
+
import '../measure/canvas-polyfill.js';
|
|
16
|
+
import type { Paragraph, ListStyle } from '../types/Document.js';
|
|
17
|
+
import type { IFontMetricsProvider } from '../types/FontTypes.js';
|
|
18
|
+
import type { ParagraphLayoutResult } from '../types/LayoutTypes.js';
|
|
19
|
+
export declare class ParagraphLayoutEngine {
|
|
20
|
+
private preparedCache;
|
|
21
|
+
/**
|
|
22
|
+
* Layout a single paragraph — basic variant.
|
|
23
|
+
*
|
|
24
|
+
* @param paragraph — input paragraph
|
|
25
|
+
* @param maxWidth — available container width (px)
|
|
26
|
+
* @param fontProvider — optional metrics provider (default: fontMetricsProvider)
|
|
27
|
+
* @returns ParagraphLayoutResult with Line[]
|
|
28
|
+
*/
|
|
29
|
+
layout(paragraph: Paragraph, maxWidth: number, yOffset?: number, fontProvider?: IFontMetricsProvider, listStyle?: ListStyle, listIndex?: number, listMarkerWidth?: number): ParagraphLayoutResult;
|
|
30
|
+
/**
|
|
31
|
+
* Layout with per-glyph advance widths (for SVG glyph mode).
|
|
32
|
+
*
|
|
33
|
+
* glyphAdvances are now filled by layout() automatically, so
|
|
34
|
+
* this method is equivalent to layout(). Kept for API compatibility.
|
|
35
|
+
*/
|
|
36
|
+
layoutGlyph(paragraph: Paragraph, maxWidth: number, yOffset?: number): ParagraphLayoutResult;
|
|
37
|
+
/**
|
|
38
|
+
* Compute per-character advance widths via FontEngine (fontkit).
|
|
39
|
+
* Returns Float32Array for memory efficiency and faster iteration.
|
|
40
|
+
*
|
|
41
|
+
* Throws FontNotFoundError if the font is not registered.
|
|
42
|
+
*/
|
|
43
|
+
private computeGlyphAdvances;
|
|
44
|
+
}
|
|
45
|
+
/** Singleton */
|
|
46
|
+
export declare const paragraphLayoutEngine: ParagraphLayoutEngine;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PositioningEngine.ts — pure X/Y positioning math.
|
|
3
|
+
*
|
|
4
|
+
* Takes pretext output (spans with fragments) + font metrics +
|
|
5
|
+
* paragraph style → returns Line[] with absolute coordinates.
|
|
6
|
+
*
|
|
7
|
+
* X: alignment (left/center/right/justify) + indent
|
|
8
|
+
* Y: baseline + lineHeight + spaceBefore/After
|
|
9
|
+
* Justify: fragmented approach (each space → separate Span)
|
|
10
|
+
*
|
|
11
|
+
* Specs:
|
|
12
|
+
* - CSS Text Module Level 3/4 (browser mode)
|
|
13
|
+
* - ISO/IEC 29500 (Office Open XML / DrawingML, office mode)
|
|
14
|
+
* - Parley alignment.rs (conceptually close, but here justify is simpler:
|
|
15
|
+
* slack is divided equally among stretchable space-spans,
|
|
16
|
+
* without mutating ClusterData.advance)
|
|
17
|
+
*/
|
|
18
|
+
import type { ParagraphStyle, ListStyle } from '../types/Document.js';
|
|
19
|
+
import type { FontMetrics } from '../types/FontTypes.js';
|
|
20
|
+
import type { Line } from '../types/LayoutTypes.js';
|
|
21
|
+
import type { PreparedRichInlineItem } from '../compile/DocumentCompiler.js';
|
|
22
|
+
interface PretextFragment {
|
|
23
|
+
itemIndex: number;
|
|
24
|
+
text: string;
|
|
25
|
+
gapBefore: number;
|
|
26
|
+
occupiedWidth: number;
|
|
27
|
+
start: {
|
|
28
|
+
segmentIndex: number;
|
|
29
|
+
graphemeIndex: number;
|
|
30
|
+
};
|
|
31
|
+
end: {
|
|
32
|
+
segmentIndex: number;
|
|
33
|
+
graphemeIndex: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
interface PretextLine {
|
|
37
|
+
fragments: PretextFragment[];
|
|
38
|
+
width: number;
|
|
39
|
+
end: {
|
|
40
|
+
segmentIndex: number;
|
|
41
|
+
graphemeIndex: number;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Build Line[] from pretext lines with alignment and metrics.
|
|
46
|
+
*
|
|
47
|
+
* @param pretextLines — pretext result (materializeRichInlineLineRange)
|
|
48
|
+
* @param items — original PreparedRichInlineItem[] (for metadata)
|
|
49
|
+
* @param fontMetricsFn — function to get font metrics for a span
|
|
50
|
+
* @param style — paragraph style
|
|
51
|
+
* @param maxWidth — available container width
|
|
52
|
+
* @param startY — initial Y position
|
|
53
|
+
* @param mode — metric mode ('browser' | 'office'), affects line height calculation
|
|
54
|
+
* @param tag — optional tag for debugging
|
|
55
|
+
* @param measureText — function to measure text width accurately via fontkit.
|
|
56
|
+
* The function accepts (text, fontSize, fontFamily, fontWeight, fontStyle)
|
|
57
|
+
* and returns width in px. Throws FontNotFoundError if font not registered.
|
|
58
|
+
* @param listStyle — optional list configuration (bullet / numbered)
|
|
59
|
+
* @param listIndex — current index in the list (for numbered lists). 1-based.
|
|
60
|
+
* @param listMarkerWidth — pre-computed width of the widest marker in the list group.
|
|
61
|
+
* When provided, bulletIndent is expanded to this value if needed.
|
|
62
|
+
* @returns { lines: Line[], contentWidth: number }
|
|
63
|
+
*/
|
|
64
|
+
export declare function positionLines(pretextLines: PretextLine[], items: PreparedRichInlineItem[], fontMetricsFn: (item: PreparedRichInlineItem) => FontMetrics, style: ParagraphStyle, maxWidth: number, startY: number | undefined, mode: "browser" | "office" | undefined, measureText: (text: string, fontSize: number, fontFamily?: string, fontWeight?: string, fontStyle?: string) => number, tag?: string, listStyle?: ListStyle, listIndex?: number, listMarkerWidth?: number): {
|
|
65
|
+
lines: Line[];
|
|
66
|
+
contentWidth: number;
|
|
67
|
+
};
|
|
68
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TextFrameLayoutEngine.ts — Layout a full TextFrame (multi-paragraph).
|
|
3
|
+
*
|
|
4
|
+
* Pipeline:
|
|
5
|
+
* TextFrame → Paragraph[] → paragraphLayoutEngine.layout() each → merge Line[]
|
|
6
|
+
*
|
|
7
|
+
* Handles:
|
|
8
|
+
* - Paragraph stacking with Y offset accumulation
|
|
9
|
+
* - Multi-column layout (CSS multi-column model)
|
|
10
|
+
* - Padding (left reduces available width, left shifts X)
|
|
11
|
+
* - frame.width/height optional → fitHorizontal/fitVertical flags
|
|
12
|
+
* - List grouping: consecutive paragraphs with listStyle form a list group.
|
|
13
|
+
* Numbered list indices are auto-incremented within each group.
|
|
14
|
+
* `listRestart: true` breaks a group and restarts numbering.
|
|
15
|
+
*
|
|
16
|
+
* Multi-column algorithm:
|
|
17
|
+
* 1. Calculate colWidth = (frameWidth - (count-1)*gap - padding) / count
|
|
18
|
+
* 2. Layout each paragraph with maxWidth = colWidth (NOT frame.width)
|
|
19
|
+
* 3. Distribute lines column-by-column (column-fill: auto)
|
|
20
|
+
* 4. If frame.height is set, lines overflow to next column when colHeight exceeded
|
|
21
|
+
* 5. If no frame.height, columns are infinite (all lines stay in column 0)
|
|
22
|
+
*/
|
|
23
|
+
import type { TextFrame } from '../types/Document.js';
|
|
24
|
+
import type { Line } from '../types/LayoutTypes.js';
|
|
25
|
+
/**
|
|
26
|
+
* Result of laying out a full TextFrame.
|
|
27
|
+
*
|
|
28
|
+
* `fitHorizontal` / `fitVertical` tell the renderer which dimension to use:
|
|
29
|
+
* - `'frame'` → use `frameWidth` / `frameHeight`
|
|
30
|
+
* - `'content'` → use `contentWidth` / `contentHeight`
|
|
31
|
+
*/
|
|
32
|
+
export interface TextFrameLayoutResult {
|
|
33
|
+
lines: Line[];
|
|
34
|
+
/** Frame width (set when TextFrame.width was provided). */
|
|
35
|
+
frameWidth?: number;
|
|
36
|
+
/** Frame height (set when TextFrame.height was provided). */
|
|
37
|
+
frameHeight?: number;
|
|
38
|
+
/** Actual content width (may exceed frameWidth when wrap=false). */
|
|
39
|
+
contentWidth: number;
|
|
40
|
+
/** Actual content height (may exceed frameHeight). */
|
|
41
|
+
contentHeight: number;
|
|
42
|
+
/** Whether horizontal dimension should use frame or content size. */
|
|
43
|
+
fitHorizontal: 'frame' | 'content';
|
|
44
|
+
/** Whether vertical dimension should use frame or content size. */
|
|
45
|
+
fitVertical: 'frame' | 'content';
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Layout a full TextFrame by stacking paragraphs with Y offset accumulation.
|
|
49
|
+
*/
|
|
50
|
+
export declare function layoutTextFrame(frame: TextFrame): TextFrameLayoutResult;
|
|
@@ -4,9 +4,6 @@
|
|
|
4
4
|
* Uses exact measurement via FontMetricsProvider.measureText() (fontkit).
|
|
5
5
|
* After measurement — correctToSumInvariant to preserve line-breaking invariant.
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
// ── Invariant correction ─────────────────────────────────────────────
|
|
9
|
-
|
|
10
7
|
/**
|
|
11
8
|
* Correct fragment widths so that their sum equals occupiedWidth.
|
|
12
9
|
*
|
|
@@ -20,25 +17,8 @@
|
|
|
20
17
|
* For fallback estimates (which already sum to occupiedWidth exactly),
|
|
21
18
|
* this is effectively a no-op.
|
|
22
19
|
*/
|
|
23
|
-
export function correctToSumInvariant(
|
|
24
|
-
measured: number[],
|
|
25
|
-
occupiedWidth: number,
|
|
26
|
-
): number[] {
|
|
27
|
-
const sum = measured.reduce((a, b) => a + b, 0);
|
|
28
|
-
if (sum === 0 || Math.abs(occupiedWidth - sum) < 0.001) {
|
|
29
|
-
// Already within rounding tolerance — return as-is
|
|
30
|
-
return measured;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const delta = occupiedWidth - sum;
|
|
34
|
-
// Distribute delta proportionally to each width
|
|
35
|
-
return measured.map(m => m + (m / sum) * delta);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ── Main entry point ─────────────────────────────────────────────────
|
|
39
|
-
|
|
20
|
+
export declare function correctToSumInvariant(measured: number[], occupiedWidth: number): number[];
|
|
40
21
|
export type MeasureFn = (text: string) => number;
|
|
41
|
-
|
|
42
22
|
/**
|
|
43
23
|
* Resolve widths for fragments of a single text group.
|
|
44
24
|
*
|
|
@@ -53,22 +33,4 @@ export type MeasureFn = (text: string) => number;
|
|
|
53
33
|
* @param measureFn — optional callback for exact measurement via font metrics provider
|
|
54
34
|
* @returns widths that sum to occupiedWidth (within floating point tolerance)
|
|
55
35
|
*/
|
|
56
|
-
export function resolveFragmentWidths(
|
|
57
|
-
fragments: string[],
|
|
58
|
-
fullText: string,
|
|
59
|
-
occupiedWidth: number,
|
|
60
|
-
measureFn: MeasureFn,
|
|
61
|
-
): number[] {
|
|
62
|
-
if (fragments.length === 0) return [];
|
|
63
|
-
if (fragments.length === 1) {
|
|
64
|
-
// Single fragment — no distribution needed
|
|
65
|
-
return [occupiedWidth];
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Step 1: Measure each fragment exactly via fontkit
|
|
69
|
-
// measureFn throws FontNotFoundError if font not registered
|
|
70
|
-
const measured = fragments.map(f => measureFn(f));
|
|
71
|
-
|
|
72
|
-
// Step 2: Correct to sum invariant
|
|
73
|
-
return correctToSumInvariant(measured, occupiedWidth);
|
|
74
|
-
}
|
|
36
|
+
export declare function resolveFragmentWidths(fragments: string[], fullText: string, occupiedWidth: number, measureFn: MeasureFn): number[];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FontEngine.ts — unified facade over fontkit.
|
|
3
|
+
*
|
|
4
|
+
* Is the single entry point for all fontkit operations:
|
|
5
|
+
* - create(buffer) → font face
|
|
6
|
+
* - getGlyphAdvance(font, codePoint) → per‑glyph advance
|
|
7
|
+
* - getMetrics(font) → structured metric values
|
|
8
|
+
*
|
|
9
|
+
* fontkit works in both Node.js (native addon) and browser (dist/browser-module.mjs).
|
|
10
|
+
* Bundlers pick the correct entry automatically when `package.json` browser map
|
|
11
|
+
* is removed (or when the import is not blocked by stubs).
|
|
12
|
+
*/
|
|
13
|
+
import type { FontMetrics } from '../types/FontTypes.js';
|
|
14
|
+
/** Opaque font face handle returned by FontEngine.create() */
|
|
15
|
+
export interface FontFace {
|
|
16
|
+
/** fontkit font object (private — not meant for direct access) */
|
|
17
|
+
readonly _raw: any;
|
|
18
|
+
/** Cached values extracted once after creation */
|
|
19
|
+
readonly unitsPerEm: number;
|
|
20
|
+
readonly ascent: number;
|
|
21
|
+
readonly descent: number;
|
|
22
|
+
readonly capHeight: number;
|
|
23
|
+
readonly winAscent: number | null;
|
|
24
|
+
readonly winDescent: number | null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a font face from a binary buffer.
|
|
28
|
+
*
|
|
29
|
+
* @param buffer Font file bytes (ArrayBuffer in browser, Uint8Array/Buffer in Node.js)
|
|
30
|
+
* @returns Opaque FontFace handle
|
|
31
|
+
*/
|
|
32
|
+
export declare function createFontFace(buffer: ArrayBuffer | Uint8Array): Promise<FontFace>;
|
|
33
|
+
/**
|
|
34
|
+
* Get the advance width (in font units) for a single code point.
|
|
35
|
+
*
|
|
36
|
+
* @returns advance width in font units, or `null` if the glyph is missing
|
|
37
|
+
*/
|
|
38
|
+
export declare function getGlyphAdvance(font: FontFace, codePoint: number): number | null;
|
|
39
|
+
/**
|
|
40
|
+
* Compute pixel‑scale metrics for a given font size.
|
|
41
|
+
*/
|
|
42
|
+
export declare function computePixelMetrics(font: FontFace, fontSize: number, mode: 'browser' | 'office'): FontMetrics;
|
|
43
|
+
/**
|
|
44
|
+
* Whether the fontkit module was successfully loaded.
|
|
45
|
+
* Useful for tests to verify the bundler isn't blocking fontkit.
|
|
46
|
+
*/
|
|
47
|
+
export declare function isFontEngineAvailable(): Promise<boolean>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FontMetricsProvider.ts — isomorphic font metrics provider.
|
|
3
|
+
*
|
|
4
|
+
* Strategy (priority):
|
|
5
|
+
* 1. FontEngine (fontkit) — from registered buffer
|
|
6
|
+
* - 'browser' mode: hhea.ascender / hhea.descender
|
|
7
|
+
* - 'office' mode: OS/2.usWinAscent / OS/2.usWinDescent
|
|
8
|
+
* 2. Canvas TextMetrics (browser fallback when fontkit unavailable)
|
|
9
|
+
*
|
|
10
|
+
* Uses FontEngine as the single entry point for all fontkit operations.
|
|
11
|
+
*/
|
|
12
|
+
import type { FontMetrics, IFontMetricsProvider } from '../types/FontTypes.js';
|
|
13
|
+
import type { FontFace } from './FontEngine.js';
|
|
14
|
+
/**
|
|
15
|
+
* Factor used when a glyph is not found in the font.
|
|
16
|
+
* Multiplied by fontSize to estimate the missing glyph width.
|
|
17
|
+
* Used across all measurement code paths (ParagraphLayoutEngine, canvas-polyfill).
|
|
18
|
+
*/
|
|
19
|
+
export declare const MISSING_GLYPH_FACTOR = 0.5;
|
|
20
|
+
export declare class FontMetricsProvider implements IFontMetricsProvider {
|
|
21
|
+
/** Map<string, FontFace> — font engine font face cache */
|
|
22
|
+
private cache;
|
|
23
|
+
private metricsCache;
|
|
24
|
+
private mode;
|
|
25
|
+
setMode(mode: 'browser' | 'office'): void;
|
|
26
|
+
getMode(): 'browser' | 'office';
|
|
27
|
+
/**
|
|
28
|
+
* Register a binary font for use with fontkit.
|
|
29
|
+
*
|
|
30
|
+
* In both Node.js and browser the font is loaded via FontEngine.
|
|
31
|
+
* In the browser the caller must provide font bytes (e.g. fetched via
|
|
32
|
+
* `getFontBuffer()` from `../utils/font.js`).
|
|
33
|
+
*
|
|
34
|
+
* @param source Font file bytes (ArrayBuffer / Uint8Array), or a URL string
|
|
35
|
+
* @param sourcePath Optional filesystem path (used for @napi-rs/canvas in Node.js)
|
|
36
|
+
*/
|
|
37
|
+
registerFont(family: string, options: {
|
|
38
|
+
weight?: string;
|
|
39
|
+
style?: string;
|
|
40
|
+
}, source: string | ArrayBuffer | Uint8Array, sourcePath?: string): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Get font engine FontFace object for per-character calculations.
|
|
43
|
+
* Returns undefined if font is not registered.
|
|
44
|
+
*/
|
|
45
|
+
getFont(family: string, weight?: string, style?: string): FontFace | undefined;
|
|
46
|
+
getMetrics(fontFamily: string, fontSize: number, weight?: string, style?: string): FontMetrics;
|
|
47
|
+
}
|
|
48
|
+
/** Singleton */
|
|
49
|
+
export declare const fontMetricsProvider: FontMetricsProvider;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SystemFontRegistry.ts — singleton that scans system fonts using `get-system-fonts`
|
|
3
|
+
* and registers them in FontMetricsProvider for both fontkit and @napi-rs/canvas.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* import { systemFontRegistry } from './SystemFontRegistry.js';
|
|
7
|
+
* await systemFontRegistry.scan();
|
|
8
|
+
* console.log(systemFontRegistry.getRegisteredFamilies());
|
|
9
|
+
*
|
|
10
|
+
* ⚠️ Node.js built-in module imports are dynamic (lazy) to avoid
|
|
11
|
+
* Vite/Webpack externalization errors in browser builds.
|
|
12
|
+
*/
|
|
13
|
+
interface ScanResult {
|
|
14
|
+
/** Total font files found on the system */
|
|
15
|
+
total: number;
|
|
16
|
+
/** Number of successfully registered font files */
|
|
17
|
+
registered: number;
|
|
18
|
+
}
|
|
19
|
+
export declare class SystemFontRegistry {
|
|
20
|
+
private static _instance;
|
|
21
|
+
/** Map of registered family names → true */
|
|
22
|
+
private registered;
|
|
23
|
+
private constructor();
|
|
24
|
+
static get instance(): SystemFontRegistry;
|
|
25
|
+
/**
|
|
26
|
+
* Scan the system for all fonts and register them in FontMetricsProvider.
|
|
27
|
+
*
|
|
28
|
+
* - Uses `get-system-fonts` to find all .ttf/.otf files
|
|
29
|
+
* - Opens each with fontkit to extract familyName/subfamilyName
|
|
30
|
+
* - Registers in fontMetricsProvider (fontkit buffer + canvas path)
|
|
31
|
+
*
|
|
32
|
+
* @returns stats about what was found and registered
|
|
33
|
+
*/
|
|
34
|
+
scan(): Promise<ScanResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Check if a font family is registered.
|
|
37
|
+
*/
|
|
38
|
+
isRegistered(family: string): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Get list of all registered font families.
|
|
41
|
+
*/
|
|
42
|
+
getRegisteredFamilies(): string[];
|
|
43
|
+
}
|
|
44
|
+
/** Singleton instance */
|
|
45
|
+
export declare const systemFontRegistry: SystemFontRegistry;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas API polyfill for Bun/Node.js via @napi-rs/canvas.
|
|
3
|
+
*
|
|
4
|
+
* Required by @chenglou/pretext in server environments (Bun/Node.js without DOM).
|
|
5
|
+
*
|
|
6
|
+
* Two levels of polyfill:
|
|
7
|
+
* 1. globalThis.document.createElement('canvas') — pretext uses this
|
|
8
|
+
* inside prepare() to create a temporary Canvas and call measureText.
|
|
9
|
+
* 2. OffscreenCanvas — for some libraries and early versions.
|
|
10
|
+
*
|
|
11
|
+
* Exports enableOfficeTextMeasure / disableOfficeTextMeasure —
|
|
12
|
+
* ctx.measureText override for FontEngine-based measurements in Office mode.
|
|
13
|
+
*
|
|
14
|
+
* ⚠️ All Node.js built-in module imports are dynamic (lazy) to avoid
|
|
15
|
+
* Vite/Webpack externalization errors in browser builds.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Register a font with @napi-rs/canvas so ctx.measureText() works in Node.js.
|
|
19
|
+
* No-op in browser or when @napi-rs/canvas is not available.
|
|
20
|
+
*/
|
|
21
|
+
export declare function registerCanvasFont(fontPath: string, family: string): void;
|
|
22
|
+
/**
|
|
23
|
+
* Enable Office measurement: replaces ctx.measureText with FontEngine-based version.
|
|
24
|
+
* @param fontCache — Map<family_weight_style, FontFace> from FontMetricsProvider
|
|
25
|
+
*/
|
|
26
|
+
export declare function enableOfficeTextMeasure(fontCache: Map<string, any>): void;
|
|
27
|
+
/**
|
|
28
|
+
* Restore original ctx.measureText.
|
|
29
|
+
*/
|
|
30
|
+
export declare function disableOfficeTextMeasure(): void;
|