@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.
Files changed (45) hide show
  1. package/dist/compile/DocumentCompiler.d.ts +40 -0
  2. package/dist/index.browser.d.ts +28 -0
  3. package/dist/index.browser.js +18 -0
  4. package/dist/index.d.ts +30 -0
  5. package/dist/index.js +11 -147930
  6. package/dist/layout/AutoFitEngine.d.ts +44 -0
  7. package/dist/layout/LineBoxValidator.d.ts +25 -0
  8. package/dist/layout/ParagraphLayoutEngine.d.ts +46 -0
  9. package/dist/layout/PositioningEngine.d.ts +68 -0
  10. package/dist/layout/TextFrameLayoutEngine.d.ts +50 -0
  11. package/{src/layout/estimateWidth.ts → dist/layout/estimateWidth.d.ts} +2 -40
  12. package/dist/measure/FontEngine.d.ts +47 -0
  13. package/dist/measure/FontMetricsProvider.d.ts +49 -0
  14. package/dist/measure/FontNotFoundError.d.ts +6 -0
  15. package/dist/measure/SystemFontRegistry.d.ts +46 -0
  16. package/dist/measure/canvas-polyfill.d.ts +30 -0
  17. package/dist/types/Document.d.ts +593 -0
  18. package/dist/types/FontTypes.d.ts +61 -0
  19. package/dist/types/LayoutTypes.d.ts +128 -0
  20. package/{src/utils/env.ts → dist/utils/env.d.ts} +1 -8
  21. package/{src/utils/font.ts → dist/utils/font.d.ts} +1 -13
  22. package/{src/utils/groupLinesByParagraph.ts → dist/utils/groupLinesByParagraph.d.ts} +7 -37
  23. package/dist/utils/list.d.ts +41 -0
  24. package/dist/utils/textTransform.d.ts +32 -0
  25. package/package.json +13 -13
  26. package/src/compile/DocumentCompiler.ts +0 -144
  27. package/src/index.browser.ts +0 -90
  28. package/src/index.ts +0 -97
  29. package/src/layout/AutoFitEngine.ts +0 -101
  30. package/src/layout/LineBoxValidator.ts +0 -162
  31. package/src/layout/ParagraphLayoutEngine.ts +0 -264
  32. package/src/layout/PositioningEngine.ts +0 -615
  33. package/src/layout/TextFrameLayoutEngine.ts +0 -363
  34. package/src/measure/FontEngine.ts +0 -144
  35. package/src/measure/FontMetricsProvider.ts +0 -224
  36. package/src/measure/FontNotFoundError.ts +0 -16
  37. package/src/measure/SystemFontRegistry.ts +0 -152
  38. package/src/measure/canvas-polyfill.d.ts +0 -6
  39. package/src/measure/canvas-polyfill.ts +0 -243
  40. package/src/measure/fontkit.d.ts +0 -44
  41. package/src/types/Document.ts +0 -666
  42. package/src/types/FontTypes.ts +0 -74
  43. package/src/types/LayoutTypes.ts +0 -158
  44. package/src/utils/list.ts +0 -107
  45. package/src/utils/textTransform.ts +0 -96
@@ -1,74 +0,0 @@
1
- /**
2
- * FontTypes.ts — font metric type definitions.
3
- *
4
- * Isomorphic layer: works both in browser (Canvas TextMetrics) and Node.js (fontkit).
5
- *
6
- * Two modes:
7
- * 'browser' — uses hhea.ascender/descender (canvas fallback)
8
- * 'office' — uses OS/2.usWinAscent/usWinDescent (MS Office compatible)
9
- */
10
-
11
- /** Physical font metrics (in pixels for a given fontSize) */
12
- export interface FontMetrics {
13
- /** Rise above baseline */
14
- ascent: number;
15
- /** Descent below baseline (positive number!) */
16
- descent: number;
17
- /** Cap height */
18
- capHeight: number;
19
- /** Original font UPM (for reference) */
20
- unitsPerEm: number;
21
- /**
22
- * Which font table was used for ascent/descent:
23
- * 'hhea' — hhea.ascender/descender (browser mode)
24
- * 'OS/2' — OS/2.usWinAscent/usWinDescent (Office mode)
25
- * 'canvas' — canvas.measureText (browser fallback)
26
- * 'fallback' — empirical formula
27
- */
28
- sourceTable?: 'hhea' | 'OS/2' | 'canvas' | 'fallback';
29
- }
30
-
31
- /** Metrics provider — isomorphic interface */
32
- export interface IFontMetricsProvider {
33
- /**
34
- * Set measurement mode.
35
- * 'browser' — hhea.ascender/descender (default)
36
- * 'office' — OS/2.usWinAscent/usWinDescent
37
- */
38
- setMode(mode: 'browser' | 'office'): void;
39
-
40
- /**
41
- * Get current mode.
42
- */
43
- getMode(): 'browser' | 'office';
44
-
45
- /**
46
- * Register a binary font for use with fontkit.
47
- * In browser — no-op (fonts are registered via CSS @font-face).
48
- *
49
- * @param sourcePath — path to .ttf/.otf file for optional @napi-rs/canvas.registerFont()
50
- */
51
- registerFont(
52
- family: string,
53
- options: { weight?: string; style?: string },
54
- source: string | Buffer,
55
- sourcePath?: string,
56
- ): void;
57
-
58
- /**
59
- * Get metrics for a given family and size.
60
- */
61
- getMetrics(
62
- fontFamily: string,
63
- fontSize: number,
64
- weight?: string,
65
- style?: string,
66
- ): FontMetrics;
67
- }
68
-
69
- /** Glyph-level data for a single glyph (per-character tracking/highlighting) */
70
- export interface GlyphData {
71
- char: string; // character
72
- advance: number; // advance width in px
73
- x: number; // position relative to line start
74
- }
@@ -1,158 +0,0 @@
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
-
10
- import type { TextRun, InlineWidget, TextAlignment } from './Document.js';
11
-
12
- // ── Span (render atom, formerly FragmentBox) ─────────────────────────────
13
-
14
- export interface Span {
15
- /** Offset from Line.x */
16
- x: number;
17
- /** Physical span width */
18
- width: number;
19
- /** Span text (or " " for justify spaces) */
20
- text: string;
21
- /** Index of the source run in the paragraph's `children` array. */
22
- itemIndex: number;
23
- /** Paragraph index in TextFrame.paragraphs[]. Stable key for grouping & diff. */
24
- pIdx: number;
25
- /** Optional paragraph tag (set by user via Paragraph.id). */
26
- tag?: string;
27
-
28
- /** Physical font metrics for this span */
29
- fontMetrics: SpanFontMetrics;
30
-
31
- /**
32
- * A snapshot of the source run's style at layout time.
33
- * Copied from the corresponding `TextRun` in the paragraph's `children` array.
34
- */
35
- style: TextRun;
36
-
37
- /** InlineWidget data (if span is an inline-box) */
38
- inlineWidget?: InlineWidget;
39
-
40
- /** Per-character advance widths (for selection/tracking) */
41
- glyphAdvances?: number[] | Float32Array;
42
-
43
- /**
44
- * Span type:
45
- * - `'text'` — regular text
46
- * - `'space'` — whitespace span
47
- * - `'marker'` — list marker (bullet / number), rendered like text
48
- */
49
- type: 'text' | 'space' | 'marker';
50
-
51
- /**
52
- * Trailing whitespace flag.
53
- * - true: span is at end of line, does not participate in line advance
54
- * and is not stretched during justify (zero width for calculations).
55
- * - undefined/false: regular span.
56
- *
57
- * See CSS Text Module Level 3 §4.1.3 (Tracking and Dropping Spaces)
58
- * and Parley LineItemData::has_trailing_whitespace.
59
- */
60
- trailing?: boolean;
61
-
62
- /**
63
- * Line break mode after this span.
64
- * 'soft' — soft line break (insufficient space)
65
- * 'hard' — forced break (\n, explicit separator)
66
- * undefined — not end of line
67
- */
68
- breakType?: 'soft' | 'hard';
69
- }
70
-
71
- export interface SpanFontMetrics {
72
- ascent: number;
73
- descent: number;
74
- fontSize: number;
75
- /** Vertical offset from baseline (px). Used for sub/superscript positioning.
76
- * Negative = above baseline (superscript). Positive = below baseline (subscript).
77
- * Undefined or 0 = normal baseline position. */
78
- baselineOffset?: number;
79
- }
80
-
81
- // ── Line (single line, formerly LineBox) ─────────────────────────────────
82
-
83
- export interface Line {
84
- /**
85
- * Absolute X of the line box left edge within the container.
86
- * Includes outside list markers when present (marker may sit left of text).
87
- */
88
- x: number;
89
- /** Absolute Y of line top edge */
90
- y: number;
91
- /**
92
- * Line box width covering all spans (text + outside markers).
93
- * Equals max(span.x + span.width) − min(span.x).
94
- */
95
- width: number;
96
-
97
- /** Full line height (max spans × lineHeight) */
98
- height: number;
99
-
100
- /** Baseline offset from y */
101
- baseline: number;
102
- /** Maximum ascent in line */
103
- ascent: number;
104
- /** Maximum descent in line */
105
- descent: number;
106
-
107
- /** Index of first character in the original paragraph text */
108
- startIndex: number;
109
- /** Index of last character + 1 (for convenient length calculation) */
110
- endIndex: number;
111
-
112
- /** Paragraph alignment (optional, for PowerPoint render) */
113
- alignment?: TextAlignment;
114
-
115
- /** Column index (0-based) when frame has multi-column layout. */
116
- columnIndex?: number;
117
-
118
- spans: Span[];
119
- }
120
-
121
- // ── ParagraphLayoutResult (single paragraph) ─────────────────────────────
122
-
123
- export interface ParagraphLayoutResult {
124
- width: number; // paragraph width (maxWidth)
125
- height: number; // full paragraph height including spacing
126
- lines: Line[];
127
- /** Actual content width (text bbox, without voids) */
128
- contentWidth: number;
129
- /** Actual content height (text bbox) */
130
- contentHeight: number;
131
- }
132
-
133
- // ── Text region for YAML snapshots ───────────────────────────────────────
134
-
135
- /** Semantic span for snapshots (without physical metrics) */
136
- export interface SemanticFragment {
137
- text: string;
138
- x: number;
139
- width: number;
140
- style?: 'bold' | 'italic' | 'normal';
141
- }
142
-
143
- /** Semantic line for snapshots */
144
- export interface SemanticLine {
145
- y: number;
146
- width: number;
147
- height: number;
148
- baseline: number;
149
- fragments: SemanticFragment[];
150
- }
151
-
152
- /** Semantic paragraph for YAML snapshots */
153
- export interface SemanticParagraph {
154
- width: number;
155
- height: number;
156
- lines: SemanticLine[];
157
- }
158
-
package/src/utils/list.ts DELETED
@@ -1,107 +0,0 @@
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
-
10
- import type { NumberFormat } from '../types/Document.js';
11
-
12
- /**
13
- * Unicode characters used for CSS `disc`, `circle`, `square` list-style-type
14
- * values at different nesting levels.
15
- *
16
- * @see {@link https://www.w3.org/TR/css-lists-3/#ua-stylesheet | CSS Lists UA defaults}
17
- */
18
- export const BULLET_CHARACTERS: Record<number, string> = {
19
- 0: '\u2022', // • BULLET (disc)
20
- 1: '\u25CB', // ○ WHITE CIRCLE (circle)
21
- 2: '\u25AA', // ▪ BLACK SMALL SQUARE (square)
22
- };
23
-
24
- /** Fallback for deeper levels — same as level 2 */
25
- const FALLBACK_BULLET = '\u25AA';
26
-
27
- /**
28
- * Get the default bullet character for a given nesting level.
29
- *
30
- * CSS UA stylesheet uses:
31
- * - Level 0 (ul): disc (•)
32
- * - Level 1 (ul ul): circle (○)
33
- * - Level 2+ (ul ul ul): square (▪)
34
- */
35
- export function defaultBulletChar(level: number): string {
36
- return BULLET_CHARACTERS[level] ?? FALLBACK_BULLET;
37
- }
38
-
39
- /**
40
- * Format a number according to the given numbering format.
41
- *
42
- * Supports the same formats as CSS `list-style-type`:
43
- * - `decimal`: 1, 2, 3, …
44
- * - `upper-roman`: I, II, III, …
45
- * - `lower-roman`: i, ii, iii, …
46
- * - `upper-alpha`: A, B, C, …
47
- * - `lower-alpha`: a, b, c, …
48
- *
49
- * Follows CSS Counter Styles Level 3 algorithms.
50
- * Roman numerals support the range 1–3999.
51
- *
52
- * @see {@link https://www.w3.org/TR/css-counter-styles-3/ | CSS Counter Styles Level 3}
53
- */
54
- export function formatListNumber(n: number, format: NumberFormat): string {
55
- switch (format) {
56
- case 'decimal':
57
- return String(n);
58
- case 'upper-roman':
59
- return toRoman(n).toUpperCase();
60
- case 'lower-roman':
61
- return toRoman(n).toLowerCase();
62
- case 'upper-alpha':
63
- return toAlpha(n).toUpperCase();
64
- case 'lower-alpha':
65
- return toAlpha(n).toLowerCase();
66
- default:
67
- return String(n);
68
- }
69
- }
70
-
71
- /**
72
- * Convert 1-based integer to lowercase roman numeral.
73
- * Supports 1–3999.
74
- */
75
- function toRoman(n: number): string {
76
- if (n < 1 || n > 3999) return String(n);
77
- const romanMap: [number, string][] = [
78
- [1000, 'm'], [900, 'cm'], [500, 'd'], [400, 'cd'],
79
- [100, 'c'], [90, 'xc'], [50, 'l'], [40, 'xl'],
80
- [10, 'x'], [9, 'ix'], [5, 'v'], [4, 'iv'], [1, 'i'],
81
- ];
82
- let result = '';
83
- for (const [value, symbol] of romanMap) {
84
- while (n >= value) {
85
- result += symbol;
86
- n -= value;
87
- }
88
- }
89
- return result;
90
- }
91
-
92
- /**
93
- * Convert 1-based integer to alpha format (a, b, c, …, z, aa, ab, …).
94
- * Uses the same algorithm as CSS `lower-alpha` (CSS Counter Styles §3.1.2).
95
- */
96
- function toAlpha(n: number): string {
97
- if (n < 1) return String(n);
98
- const base = 26;
99
- const offset = 'a'.charCodeAt(0);
100
- let result = '';
101
- while (n > 0) {
102
- n--;
103
- result = String.fromCharCode(offset + (n % base)) + result;
104
- n = Math.floor(n / base);
105
- }
106
- return result;
107
- }
@@ -1,96 +0,0 @@
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
-
18
- import type { TextTransform } from '../types/Document.js';
19
-
20
- /**
21
- * Apply text-transform to a string.
22
- *
23
- * @param text — input text
24
- * @param transform — transform type ('none' | 'uppercase' | 'lowercase' | 'capitalize')
25
- * @returns transformed text
26
- *
27
- * @example
28
- * ```ts
29
- * transformText("hello world", 'uppercase') // → "HELLO WORLD"
30
- * transformText("HELLO", 'lowercase') // → "hello"
31
- * transformText("don't stop", 'capitalize') // → "Don't Stop"
32
- * ```
33
- */
34
- export function transformText(text: string, transform?: TextTransform): string {
35
- if (!transform || transform === 'none' || !text) {
36
- return text;
37
- }
38
-
39
- switch (transform) {
40
- case 'uppercase':
41
- return text.toUpperCase();
42
-
43
- case 'lowercase':
44
- return text.toLowerCase();
45
-
46
- case 'capitalize': {
47
- // Capitalize first letter of each word.
48
- // Word = sequence of Unicode letters (\p{L}).
49
- // Non-letter characters (including hyphen, but NOT apostrophe) are delimiters.
50
- //
51
- // Strategy: split on non-letter sequences (\P{L}+), but preserve delimiters
52
- // by using a capturing group. Then for each letter-segment, capitalize its first \p{L}.
53
- // Non-letter segments pass through unchanged.
54
- //
55
- // The regex splits on non-letters (excluding apostrophe) but keeps delimiters.
56
- // Apostrophe (') is NOT a word boundary: "don't stop" → ["don't", " ", "stop", ""]
57
- // Hyphen IS: "hello-world" → ["hello", "-", "world", ""]
58
- const segments = text.split(/([^\p{L}']+)/u);
59
-
60
- // Pre-allocate with same length
61
- const result: string[] = new Array(segments.length);
62
-
63
- for (let i = 0; i < segments.length; i++) {
64
- const seg = segments[i];
65
- if (!seg) {
66
- result[i] = seg;
67
- continue;
68
- }
69
-
70
- // If this segment is a non-letter (excluding apostrophe) delimiter — pass through unchanged
71
- if (/^[^\p{L}']+$/u.test(seg)) {
72
- result[i] = seg;
73
- continue;
74
- }
75
-
76
- // This is a letter-segment. Capitalize its first letter.
77
- const firstLetter = seg.match(/\p{L}/u);
78
- if (!firstLetter) {
79
- result[i] = seg;
80
- continue;
81
- }
82
-
83
- const idx = firstLetter.index!;
84
- const before = seg.slice(0, idx);
85
- const letter = seg[idx].toUpperCase();
86
- const after = seg.slice(idx + 1);
87
- result[i] = `${before}${letter}${after}`;
88
- }
89
-
90
- return result.join('');
91
- }
92
-
93
- default:
94
- return text;
95
- }
96
- }