@vyaz/core 0.0.5 → 0.0.7
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 +83 -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 +13 -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 +599 -0
- package/dist/types/FontTypes.d.ts +61 -0
- package/dist/types/LayoutTypes.d.ts +137 -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,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LayoutTypes.ts — output types (Physical Box Model).
|
|
3
|
+
*
|
|
4
|
+
* ParagraphLayoutResult → Line[] → Span[]
|
|
5
|
+
* This is the contract between layout engine and renderers.
|
|
6
|
+
*
|
|
7
|
+
* Based on plan.md §2.5 (Output — Physical Box Model / Layout Tree)
|
|
8
|
+
*/
|
|
9
|
+
import type { TextRun, InlineWidget, TextAlignment } from './Document.js';
|
|
10
|
+
export interface Span {
|
|
11
|
+
/** Offset from Line.x */
|
|
12
|
+
x: number;
|
|
13
|
+
/** Physical span width */
|
|
14
|
+
width: number;
|
|
15
|
+
/** Span text (or " " for justify spaces) */
|
|
16
|
+
text: string;
|
|
17
|
+
/** Index of the source run in the paragraph's `children` array. */
|
|
18
|
+
itemIndex: number;
|
|
19
|
+
/** Paragraph index in TextFrame.paragraphs[]. Stable key for grouping & diff. */
|
|
20
|
+
pIdx: number;
|
|
21
|
+
/** Optional paragraph tag (set by user via Paragraph.id). */
|
|
22
|
+
tag?: string;
|
|
23
|
+
/** Physical font metrics for this span */
|
|
24
|
+
fontMetrics: SpanFontMetrics;
|
|
25
|
+
/**
|
|
26
|
+
* A snapshot of the source run's style at layout time.
|
|
27
|
+
* Copied from the corresponding `TextRun` in the paragraph's `children` array.
|
|
28
|
+
*/
|
|
29
|
+
style: TextRun;
|
|
30
|
+
/** InlineWidget data (if span is an inline-box) */
|
|
31
|
+
inlineWidget?: InlineWidget;
|
|
32
|
+
/** Per-character advance widths (for selection/tracking) */
|
|
33
|
+
glyphAdvances?: number[] | Float32Array;
|
|
34
|
+
/**
|
|
35
|
+
* Span type:
|
|
36
|
+
* - `'text'` — regular text
|
|
37
|
+
* - `'space'` — whitespace span
|
|
38
|
+
* - `'marker'` — list marker (bullet / number), rendered like text
|
|
39
|
+
*/
|
|
40
|
+
type: 'text' | 'space' | 'marker';
|
|
41
|
+
/**
|
|
42
|
+
* Trailing whitespace flag.
|
|
43
|
+
* - true: span is at end of line, does not participate in line advance
|
|
44
|
+
* and is not stretched during justify (zero width for calculations).
|
|
45
|
+
* - undefined/false: regular span.
|
|
46
|
+
*
|
|
47
|
+
* See CSS Text Module Level 3 §4.1.3 (Tracking and Dropping Spaces)
|
|
48
|
+
* and Parley LineItemData::has_trailing_whitespace.
|
|
49
|
+
*/
|
|
50
|
+
trailing?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Line break mode after this span.
|
|
53
|
+
* 'soft' — soft line break (insufficient space)
|
|
54
|
+
* 'hard' — forced break (\n, explicit separator)
|
|
55
|
+
* undefined — not end of line
|
|
56
|
+
*/
|
|
57
|
+
breakType?: 'soft' | 'hard';
|
|
58
|
+
}
|
|
59
|
+
export interface SpanFontMetrics {
|
|
60
|
+
ascent: number;
|
|
61
|
+
descent: number;
|
|
62
|
+
fontSize: number;
|
|
63
|
+
/** Vertical offset from baseline (px). Used for sub/superscript positioning.
|
|
64
|
+
* Negative = above baseline (superscript). Positive = below baseline (subscript).
|
|
65
|
+
* Undefined or 0 = normal baseline position. */
|
|
66
|
+
baselineOffset?: number;
|
|
67
|
+
}
|
|
68
|
+
export interface Line {
|
|
69
|
+
/**
|
|
70
|
+
* Absolute X of the line box left edge within the container.
|
|
71
|
+
* Includes outside list markers when present (marker may sit left of text).
|
|
72
|
+
*/
|
|
73
|
+
x: number;
|
|
74
|
+
/** Absolute Y of line top edge */
|
|
75
|
+
y: number;
|
|
76
|
+
/**
|
|
77
|
+
* Line box width covering all spans (text + outside markers).
|
|
78
|
+
* Equals max(span.x + span.width) − min(span.x).
|
|
79
|
+
*/
|
|
80
|
+
width: number;
|
|
81
|
+
/** Full line height (max spans × lineHeight) */
|
|
82
|
+
height: number;
|
|
83
|
+
/** Baseline offset from y */
|
|
84
|
+
baseline: number;
|
|
85
|
+
/** Maximum ascent in line */
|
|
86
|
+
ascent: number;
|
|
87
|
+
/** Maximum descent in line */
|
|
88
|
+
descent: number;
|
|
89
|
+
/** Index of first character in the original paragraph text */
|
|
90
|
+
startIndex: number;
|
|
91
|
+
/** Index of last character + 1 (for convenient length calculation) */
|
|
92
|
+
endIndex: number;
|
|
93
|
+
/** Paragraph alignment (optional, for PowerPoint render) */
|
|
94
|
+
alignment?: TextAlignment;
|
|
95
|
+
/** Column index (0-based) when frame has multi-column layout. */
|
|
96
|
+
columnIndex?: number;
|
|
97
|
+
/**
|
|
98
|
+
* True when this line was created by a forced hard break (\n),
|
|
99
|
+
* as opposed to a soft wrap from line width exceeding maxWidth.
|
|
100
|
+
*
|
|
101
|
+
* Used by the editor to distinguish user-inserted line breaks
|
|
102
|
+
* from automatic wrapping (affects Home/End, arrow up/down,
|
|
103
|
+
* Backspace merging behaviour).
|
|
104
|
+
*/
|
|
105
|
+
isHardBreak?: boolean;
|
|
106
|
+
spans: Span[];
|
|
107
|
+
}
|
|
108
|
+
export interface ParagraphLayoutResult {
|
|
109
|
+
width: number;
|
|
110
|
+
height: number;
|
|
111
|
+
lines: Line[];
|
|
112
|
+
/** Actual content width (text bbox, without voids) */
|
|
113
|
+
contentWidth: number;
|
|
114
|
+
/** Actual content height (text bbox) */
|
|
115
|
+
contentHeight: number;
|
|
116
|
+
}
|
|
117
|
+
/** Semantic span for snapshots (without physical metrics) */
|
|
118
|
+
export interface SemanticFragment {
|
|
119
|
+
text: string;
|
|
120
|
+
x: number;
|
|
121
|
+
width: number;
|
|
122
|
+
style?: 'bold' | 'italic' | 'normal';
|
|
123
|
+
}
|
|
124
|
+
/** Semantic line for snapshots */
|
|
125
|
+
export interface SemanticLine {
|
|
126
|
+
y: number;
|
|
127
|
+
width: number;
|
|
128
|
+
height: number;
|
|
129
|
+
baseline: number;
|
|
130
|
+
fragments: SemanticFragment[];
|
|
131
|
+
}
|
|
132
|
+
/** Semantic paragraph for YAML snapshots */
|
|
133
|
+
export interface SemanticParagraph {
|
|
134
|
+
width: number;
|
|
135
|
+
height: number;
|
|
136
|
+
lines: SemanticLine[];
|
|
137
|
+
}
|
|
@@ -7,12 +7,5 @@
|
|
|
7
7
|
* triggering bundler static analysis that could externalize the
|
|
8
8
|
* Node.js `process` global for browser targets.
|
|
9
9
|
*/
|
|
10
|
-
|
|
11
|
-
const _process: any =
|
|
12
|
-
typeof globalThis !== 'undefined' ? (globalThis as any).process : undefined;
|
|
13
|
-
|
|
14
10
|
/** `true` when running on Node.js or Bun (has `process.versions.node`) */
|
|
15
|
-
export const isNodeLike: boolean
|
|
16
|
-
_process != null &&
|
|
17
|
-
_process.versions != null &&
|
|
18
|
-
typeof _process.versions.node === 'string';
|
|
11
|
+
export declare const isNodeLike: boolean;
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* Provides cross‑environment helpers for loading font data in the browser
|
|
5
5
|
* (where there is no local filesystem).
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
7
|
/**
|
|
9
8
|
* Download a font file from a URL and return its bytes.
|
|
10
9
|
*
|
|
@@ -14,15 +13,4 @@
|
|
|
14
13
|
* @param fontUrl URL of the font file (.ttf, .otf, .woff, .woff2)
|
|
15
14
|
* @returns Font file bytes as an ArrayBuffer
|
|
16
15
|
*/
|
|
17
|
-
export
|
|
18
|
-
const response = await fetch(fontUrl);
|
|
19
|
-
|
|
20
|
-
if (!response.ok) {
|
|
21
|
-
throw new Error(
|
|
22
|
-
`[vyaz] Failed to download font from "${fontUrl}": ${response.status} ${response.statusText}`,
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const buffer = await response.arrayBuffer();
|
|
27
|
-
return buffer;
|
|
28
|
-
}
|
|
16
|
+
export declare function getFontBuffer(fontUrl: string): Promise<ArrayBuffer>;
|
|
@@ -7,21 +7,18 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @see {@link https://www.w3.org/TR/css-text-3/ | CSS Text Module Level 3}
|
|
9
9
|
*/
|
|
10
|
-
|
|
11
10
|
import type { Line } from '../types/LayoutTypes.js';
|
|
12
|
-
|
|
13
11
|
/**
|
|
14
12
|
* A group of lines belonging to one paragraph.
|
|
15
13
|
*/
|
|
16
14
|
export interface ParagraphGroup {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
/** Lines in this paragraph. */
|
|
16
|
+
lines: Line[];
|
|
17
|
+
/** Paragraph index in TextFrame.paragraphs[]. */
|
|
18
|
+
pIdx: number;
|
|
19
|
+
/** Optional user-assigned tag (from Paragraph.id). */
|
|
20
|
+
tag?: string;
|
|
23
21
|
}
|
|
24
|
-
|
|
25
22
|
/**
|
|
26
23
|
* Group lines by `pIdx` (paragraph index).
|
|
27
24
|
*
|
|
@@ -44,31 +41,4 @@ export interface ParagraphGroup {
|
|
|
44
41
|
* // groups[0].pIdx → 0
|
|
45
42
|
* ```
|
|
46
43
|
*/
|
|
47
|
-
export function groupLinesByParagraph(lines: Line[]): ParagraphGroup[]
|
|
48
|
-
// Collect lines per paragraph using a Map
|
|
49
|
-
const map = new Map<number, { lines: Line[]; tag?: string }>();
|
|
50
|
-
|
|
51
|
-
for (const line of lines) {
|
|
52
|
-
const idx = line.spans[0]?.pIdx ?? -1;
|
|
53
|
-
let entry = map.get(idx);
|
|
54
|
-
if (!entry) {
|
|
55
|
-
entry = { lines: [] };
|
|
56
|
-
map.set(idx, entry);
|
|
57
|
-
}
|
|
58
|
-
entry.lines.push(line);
|
|
59
|
-
if (!entry.tag) {
|
|
60
|
-
entry.tag = line.spans[0]?.tag;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Convert to array sorted by pIdx
|
|
65
|
-
const groups: ParagraphGroup[] = [];
|
|
66
|
-
const sortedKeys = Array.from(map.keys()).sort((a, b) => a - b);
|
|
67
|
-
for (const key of sortedKeys) {
|
|
68
|
-
if (key === -1) continue; // skip invalid
|
|
69
|
-
const entry = map.get(key)!;
|
|
70
|
-
groups.push({ lines: entry.lines, pIdx: key, tag: entry.tag });
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return groups;
|
|
74
|
-
}
|
|
44
|
+
export declare function groupLinesByParagraph(lines: Line[]): ParagraphGroup[];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* list.ts — helper utilities for list marker generation.
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - `formatListNumber()` — format a number according to `NumberFormat`
|
|
6
|
+
* - `defaultBulletChar()` — pick a bullet character based on nesting level
|
|
7
|
+
* - `BULLET_CHARACTERS` — the Unicode characters for disc/circle/square
|
|
8
|
+
*/
|
|
9
|
+
import type { NumberFormat } from '../types/Document.js';
|
|
10
|
+
/**
|
|
11
|
+
* Unicode characters used for CSS `disc`, `circle`, `square` list-style-type
|
|
12
|
+
* values at different nesting levels.
|
|
13
|
+
*
|
|
14
|
+
* @see {@link https://www.w3.org/TR/css-lists-3/#ua-stylesheet | CSS Lists UA defaults}
|
|
15
|
+
*/
|
|
16
|
+
export declare const BULLET_CHARACTERS: Record<number, string>;
|
|
17
|
+
/**
|
|
18
|
+
* Get the default bullet character for a given nesting level.
|
|
19
|
+
*
|
|
20
|
+
* CSS UA stylesheet uses:
|
|
21
|
+
* - Level 0 (ul): disc (•)
|
|
22
|
+
* - Level 1 (ul ul): circle (○)
|
|
23
|
+
* - Level 2+ (ul ul ul): square (▪)
|
|
24
|
+
*/
|
|
25
|
+
export declare function defaultBulletChar(level: number): string;
|
|
26
|
+
/**
|
|
27
|
+
* Format a number according to the given numbering format.
|
|
28
|
+
*
|
|
29
|
+
* Supports the same formats as CSS `list-style-type`:
|
|
30
|
+
* - `decimal`: 1, 2, 3, …
|
|
31
|
+
* - `upper-roman`: I, II, III, …
|
|
32
|
+
* - `lower-roman`: i, ii, iii, …
|
|
33
|
+
* - `upper-alpha`: A, B, C, …
|
|
34
|
+
* - `lower-alpha`: a, b, c, …
|
|
35
|
+
*
|
|
36
|
+
* Follows CSS Counter Styles Level 3 algorithms.
|
|
37
|
+
* Roman numerals support the range 1–3999.
|
|
38
|
+
*
|
|
39
|
+
* @see {@link https://www.w3.org/TR/css-counter-styles-3/ | CSS Counter Styles Level 3}
|
|
40
|
+
*/
|
|
41
|
+
export declare function formatListNumber(n: number, format: NumberFormat): string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* textTransform.ts — pure text transformation functions.
|
|
3
|
+
*
|
|
4
|
+
* Implements CSS `text-transform` for: none, uppercase, lowercase, capitalize.
|
|
5
|
+
*
|
|
6
|
+
* Explicit capitalization contract (edge cases):
|
|
7
|
+
* - Apostrophe (') is NOT a word boundary: "don't stop" → "Don't Stop"
|
|
8
|
+
* - Hyphen (-) IS a word boundary: "hello-world" → "Hello-World"
|
|
9
|
+
* - Leading whitespace is preserved: " hello" → " Hello"
|
|
10
|
+
* - Numbers are not capitalized: "3d model" → "3d Model"
|
|
11
|
+
* - Unicode full-range: first `\p{L}` in each word segment gets .toUpperCase()
|
|
12
|
+
* - Empty string → ""
|
|
13
|
+
* - No letters → unchanged: "123" → "123"
|
|
14
|
+
*
|
|
15
|
+
* @see {@link https://www.w3.org/TR/css-text-3/#text-transform-property | CSS Text: text-transform}
|
|
16
|
+
*/
|
|
17
|
+
import type { TextTransform } from '../types/Document.js';
|
|
18
|
+
/**
|
|
19
|
+
* Apply text-transform to a string.
|
|
20
|
+
*
|
|
21
|
+
* @param text — input text
|
|
22
|
+
* @param transform — transform type ('none' | 'uppercase' | 'lowercase' | 'capitalize')
|
|
23
|
+
* @returns transformed text
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* transformText("hello world", 'uppercase') // → "HELLO WORLD"
|
|
28
|
+
* transformText("HELLO", 'lowercase') // → "hello"
|
|
29
|
+
* transformText("don't stop", 'capitalize') // → "Don't Stop"
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function transformText(text: string, transform?: TextTransform): string;
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vyaz/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"main": "./
|
|
6
|
-
"types": "./
|
|
7
|
-
"files": ["dist"
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"files": ["dist"],
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"access": "public"
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|
|
14
14
|
"bun": "./src/index.ts",
|
|
15
|
-
"node": "./
|
|
16
|
-
"browser": "./
|
|
17
|
-
"types": "./
|
|
18
|
-
"default": "./
|
|
15
|
+
"node": "./dist/index.js",
|
|
16
|
+
"browser": "./dist/index.browser.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"default": "./dist/index.js"
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"browser": {
|
|
@@ -26,14 +26,12 @@
|
|
|
26
26
|
"child_process": false
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
|
-
"build": "bun build ./src/index.ts --outdir ./dist --target bun",
|
|
29
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target bun && bun build ./src/index.browser.ts --outdir ./dist --outfile ./dist/index.browser.js --target browser && tsc --project tsconfig.json --emitDeclarationOnly",
|
|
30
30
|
"test": "bun test"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@chenglou/pretext": "^0.0.8",
|
|
34
|
-
"
|
|
35
|
-
"js-yaml": "^5.0.0",
|
|
36
|
-
"typedoc": "^0.28.19"
|
|
34
|
+
"js-yaml": "^5.0.0"
|
|
37
35
|
},
|
|
38
36
|
"optionalDependencies": {
|
|
39
37
|
"@napi-rs/canvas": "^1.0.2",
|
|
@@ -41,6 +39,8 @@
|
|
|
41
39
|
"get-system-fonts": "^2.0.2"
|
|
42
40
|
},
|
|
43
41
|
"devDependencies": {
|
|
44
|
-
"@types/node": "^26.0.0"
|
|
42
|
+
"@types/node": "^26.0.0",
|
|
43
|
+
"@clean-jsdoc-theme/typedoc": "^5.0.6",
|
|
44
|
+
"typedoc": "^0.28.19"
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DocumentCompiler.ts — compile Paragraph → PreparedRichInlineItem[].
|
|
3
|
-
*
|
|
4
|
-
* Each TextRun becomes a RichInlineItem for pretext.
|
|
5
|
-
* inline-box: text → \uFFFC, dimensions in metadata.inlineWidget.
|
|
6
|
-
* super/sub: fontSize *= 0.65, baselineOffset in metadata.
|
|
7
|
-
*
|
|
8
|
-
* Simple JSON-serialisable format — does not depend on pretext directly.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { Paragraph, TextRun, TextTransform } from '../types/Document.js';
|
|
12
|
-
import { DEFAULT_TEXT_STYLE } from '../types/Document.js';
|
|
13
|
-
import { transformText } from '../utils/textTransform.js';
|
|
14
|
-
|
|
15
|
-
// ── Font Weight normalization (matching react-pdf convention) ───────────
|
|
16
|
-
|
|
17
|
-
export const FONT_WEIGHTS: Record<string, number> = {
|
|
18
|
-
thin: 100,
|
|
19
|
-
hairline: 100,
|
|
20
|
-
ultralight: 200,
|
|
21
|
-
extralight: 200,
|
|
22
|
-
light: 300,
|
|
23
|
-
normal: 400,
|
|
24
|
-
medium: 500,
|
|
25
|
-
semibold: 600,
|
|
26
|
-
demibold: 600,
|
|
27
|
-
bold: 700,
|
|
28
|
-
ultrabold: 800,
|
|
29
|
-
extrabold: 800,
|
|
30
|
-
heavy: 900,
|
|
31
|
-
black: 900,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/** Normalize fontWeight to a numeric value (400 by default). */
|
|
35
|
-
export function normalizeFontWeight(weight: number | string | undefined): number {
|
|
36
|
-
if (weight == null) return FONT_WEIGHTS.normal;
|
|
37
|
-
if (typeof weight === 'number') return weight;
|
|
38
|
-
return FONT_WEIGHTS[weight.toLowerCase()] ?? FONT_WEIGHTS.normal;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Font token for pretext: "${style}_${weight}_${fontSize}_${family}" */
|
|
42
|
-
export function makeFontToken(run: TextRun, effectiveFontSize: number): string {
|
|
43
|
-
const fontStyle = run.fontStyle || DEFAULT_TEXT_STYLE.fontStyle || 'normal';
|
|
44
|
-
const fontWeight = normalizeFontWeight(run.fontWeight ?? DEFAULT_TEXT_STYLE.fontWeight);
|
|
45
|
-
const fontFamily = run.fontFamily || DEFAULT_TEXT_STYLE.fontFamily || 'Arial';
|
|
46
|
-
return `${fontStyle} ${fontWeight} ${effectiveFontSize}px ${fontFamily}`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Compilation context (passed to pretext) */
|
|
50
|
-
export interface PreparedRichInlineItem {
|
|
51
|
-
text: string;
|
|
52
|
-
font: string;
|
|
53
|
-
letterSpacing?: number;
|
|
54
|
-
extraWidth?: number; // padding, border for inline-box
|
|
55
|
-
break?: 'normal' | 'never'; // for atomic chips
|
|
56
|
-
/** Original text before text-transform (if transform was applied). Used for copy-paste / round-trip. */
|
|
57
|
-
originalText?: string;
|
|
58
|
-
metadata: {
|
|
59
|
-
originalRunIndex: number;
|
|
60
|
-
baselineOffset: number;
|
|
61
|
-
effectiveFontSize: number;
|
|
62
|
-
style: TextRun;
|
|
63
|
-
inlineWidget?: TextRun['inlineWidget'];
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const SUPER_SUB_SCALE = 0.65;
|
|
68
|
-
const SUPER_OFFSET_RATIO = -0.4;
|
|
69
|
-
const SUB_OFFSET_RATIO = 0.25;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Compile a paragraph into PreparedRichInlineItem[].
|
|
73
|
-
*/
|
|
74
|
-
export function compileParagraph(paragraph: Paragraph): PreparedRichInlineItem[] {
|
|
75
|
-
const items: PreparedRichInlineItem[] = [];
|
|
76
|
-
|
|
77
|
-
for (let i = 0; i < paragraph.children.length; i++) {
|
|
78
|
-
const run = paragraph.children[i];
|
|
79
|
-
|
|
80
|
-
// Compute effective fontSize and baselineOffset
|
|
81
|
-
const baseFontSize = run.fontSize ?? DEFAULT_TEXT_STYLE.fontSize ?? 12;
|
|
82
|
-
let effectiveFontSize = baseFontSize;
|
|
83
|
-
let baselineOffset = 0;
|
|
84
|
-
|
|
85
|
-
if (run.script === 'super') {
|
|
86
|
-
effectiveFontSize = baseFontSize * SUPER_SUB_SCALE;
|
|
87
|
-
baselineOffset = baseFontSize * SUPER_OFFSET_RATIO;
|
|
88
|
-
} else if (run.script === 'sub') {
|
|
89
|
-
effectiveFontSize = baseFontSize * SUPER_SUB_SCALE;
|
|
90
|
-
baselineOffset = baseFontSize * SUB_OFFSET_RATIO;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Inline-box: text → \uFFFC
|
|
94
|
-
const rawText = run.type === 'inline-box' ? '\uFFFC' : run.text;
|
|
95
|
-
// Apply text-transform (only for text runs, not inline-box)
|
|
96
|
-
const textTransformValue: TextTransform | undefined = run.textTransform;
|
|
97
|
-
const text = transformText(rawText, textTransformValue);
|
|
98
|
-
|
|
99
|
-
// Normalize fontWeight to numeric value
|
|
100
|
-
const resolvedFontWeight = normalizeFontWeight(run.fontWeight ?? DEFAULT_TEXT_STYLE.fontWeight);
|
|
101
|
-
|
|
102
|
-
// Fill missing style fields from DEFAULT_TEXT_STYLE
|
|
103
|
-
const resolvedStyle: TextRun = {
|
|
104
|
-
...DEFAULT_TEXT_STYLE,
|
|
105
|
-
...run,
|
|
106
|
-
fontSize: effectiveFontSize,
|
|
107
|
-
fontWeight: resolvedFontWeight,
|
|
108
|
-
text: run.text,
|
|
109
|
-
type: run.type,
|
|
110
|
-
} as TextRun;
|
|
111
|
-
|
|
112
|
-
const item: PreparedRichInlineItem = {
|
|
113
|
-
text,
|
|
114
|
-
font: makeFontToken(run, effectiveFontSize),
|
|
115
|
-
letterSpacing: run.letterSpacing,
|
|
116
|
-
// Save original text if transform was applied (for copy-paste / round-trip)
|
|
117
|
-
...(text !== rawText ? { originalText: rawText } : {}),
|
|
118
|
-
metadata: {
|
|
119
|
-
originalRunIndex: i,
|
|
120
|
-
baselineOffset,
|
|
121
|
-
effectiveFontSize,
|
|
122
|
-
style: resolvedStyle,
|
|
123
|
-
inlineWidget: run.inlineWidget,
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
// Inline-box: add extraWidth and break: 'never'
|
|
128
|
-
if (run.type === 'inline-box' && run.inlineWidget) {
|
|
129
|
-
item.extraWidth = run.inlineWidget.width;
|
|
130
|
-
item.break = 'never';
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
items.push(item);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return items;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Get the full text of a paragraph (for INDEX_CONSIST checks).
|
|
141
|
-
*/
|
|
142
|
-
export function getParagraphText(paragraph: Paragraph): string {
|
|
143
|
-
return paragraph.children.map(r => r.text).join('');
|
|
144
|
-
}
|
package/src/index.browser.ts
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @vyaz/core — Browser entry point.
|
|
3
|
-
*
|
|
4
|
-
* Re-exports everything from the main index **except** `SystemFontRegistry`
|
|
5
|
-
* and `systemFontRegistry`, which require Node.js built-in modules
|
|
6
|
-
* (`node:fs`, `get-system-fonts`).
|
|
7
|
-
*
|
|
8
|
-
* All server-only code paths are guarded by runtime checks (`isNodeLike`)
|
|
9
|
-
* so tree-shakers can safely eliminate dead branches.
|
|
10
|
-
*
|
|
11
|
-
* ✅ Safe for Vite / webpack / Rollup / esbuild browser builds.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
// ── Input types (Logical level) ─────────────────────────────────────────
|
|
15
|
-
export type {
|
|
16
|
-
TextFrame,
|
|
17
|
-
Paragraph,
|
|
18
|
-
ParagraphStyle,
|
|
19
|
-
TextRun,
|
|
20
|
-
InlineWidget,
|
|
21
|
-
AutofitConfig,
|
|
22
|
-
TextAlignment,
|
|
23
|
-
WritingMode,
|
|
24
|
-
TextOrientation,
|
|
25
|
-
VerticalAlignment,
|
|
26
|
-
ScriptType,
|
|
27
|
-
WhiteSpace,
|
|
28
|
-
MultiColumnConfig,
|
|
29
|
-
DominantBaseline,
|
|
30
|
-
LineFitEdge,
|
|
31
|
-
TextAlignLast,
|
|
32
|
-
WordBreak,
|
|
33
|
-
LineBreak,
|
|
34
|
-
OverflowWrap,
|
|
35
|
-
TextDecorationStyle,
|
|
36
|
-
TextTransform,
|
|
37
|
-
ListType,
|
|
38
|
-
NumberFormat,
|
|
39
|
-
ListStylePosition,
|
|
40
|
-
ListStyle,
|
|
41
|
-
} from './types/Document.js';
|
|
42
|
-
export {
|
|
43
|
-
DEFAULT_PARAGRAPH_STYLE,
|
|
44
|
-
DEFAULT_TEXT_STYLE,
|
|
45
|
-
} from './types/Document.js';
|
|
46
|
-
|
|
47
|
-
// ── Output types (Physical Box Model) ───────────────────────────────────
|
|
48
|
-
export type {
|
|
49
|
-
ParagraphLayoutResult,
|
|
50
|
-
Line,
|
|
51
|
-
Span,
|
|
52
|
-
SpanFontMetrics,
|
|
53
|
-
SemanticParagraph,
|
|
54
|
-
SemanticLine,
|
|
55
|
-
SemanticFragment,
|
|
56
|
-
} from './types/LayoutTypes.js';
|
|
57
|
-
|
|
58
|
-
// ── Font types ──────────────────────────────────────────────────────────
|
|
59
|
-
export type {
|
|
60
|
-
FontMetrics,
|
|
61
|
-
IFontMetricsProvider,
|
|
62
|
-
GlyphData,
|
|
63
|
-
} from './types/FontTypes.js';
|
|
64
|
-
|
|
65
|
-
// ── Layout Engine (browser‑safe: uses Canvas 2D measureText fallback) ──
|
|
66
|
-
export { ParagraphLayoutEngine, paragraphLayoutEngine } from './layout/ParagraphLayoutEngine.js';
|
|
67
|
-
export { positionLines } from './layout/PositioningEngine.js';
|
|
68
|
-
export { assertLineInvariants, linesToYAML } from './layout/LineBoxValidator.js';
|
|
69
|
-
export type { InvariantError } from './layout/LineBoxValidator.js';
|
|
70
|
-
|
|
71
|
-
// ── TextFrame Layout Engine ──────────────────────────────────────────────
|
|
72
|
-
export { layoutTextFrame } from './layout/TextFrameLayoutEngine.js';
|
|
73
|
-
export type { TextFrameLayoutResult } from './layout/TextFrameLayoutEngine.js';
|
|
74
|
-
|
|
75
|
-
// ── Autofit ─────────────────────────────────────────────────────────────
|
|
76
|
-
export { applyScale, findScale } from './layout/AutoFitEngine.js';
|
|
77
|
-
export type { AutoFitOptions, AutoFitResult } from './layout/AutoFitEngine.js';
|
|
78
|
-
|
|
79
|
-
// ── Compiler (browser‑safe: FontMetricsProvider falls back to Canvas) ──
|
|
80
|
-
export { compileParagraph, getParagraphText, makeFontToken } from './compile/DocumentCompiler.js';
|
|
81
|
-
export type { PreparedRichInlineItem } from './compile/DocumentCompiler.js';
|
|
82
|
-
|
|
83
|
-
// ── Font metrics (browser‑safe: canvas-polyfill has runtime guards) ────
|
|
84
|
-
export { FontMetricsProvider, fontMetricsProvider } from './measure/FontMetricsProvider.js';
|
|
85
|
-
|
|
86
|
-
// ── Errors ──────────────────────────────────────────────────────────────
|
|
87
|
-
export { FontNotFoundError } from './measure/FontNotFoundError.js';
|
|
88
|
-
|
|
89
|
-
// ❌ NOT exported in browser entry:
|
|
90
|
-
// - SystemFontRegistry / systemFontRegistry — requires `node:fs` + `get-system-fonts`
|