shape-text 0.1.0

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 (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +264 -0
  3. package/dist/geometry/get-band-intervals-from-polygon.d.ts +2 -0
  4. package/dist/geometry/get-band-intervals-from-polygon.js +44 -0
  5. package/dist/geometry/get-band-intervals-from-polygon.js.map +1 -0
  6. package/dist/geometry/get-polygon-bounds.d.ts +2 -0
  7. package/dist/geometry/get-polygon-bounds.js +18 -0
  8. package/dist/geometry/get-polygon-bounds.js.map +1 -0
  9. package/dist/geometry/get-x-intersections-at-y.d.ts +2 -0
  10. package/dist/geometry/get-x-intersections-at-y.js +15 -0
  11. package/dist/geometry/get-x-intersections-at-y.js.map +1 -0
  12. package/dist/index.d.ts +11 -0
  13. package/dist/index.js +11 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/layout/layout-dense-fill-pass.d.ts +23 -0
  16. package/dist/layout/layout-dense-fill-pass.js +64 -0
  17. package/dist/layout/layout-dense-fill-pass.js.map +1 -0
  18. package/dist/layout/layout-flow-lines-in-compiled-shape.d.ts +13 -0
  19. package/dist/layout/layout-flow-lines-in-compiled-shape.js +42 -0
  20. package/dist/layout/layout-flow-lines-in-compiled-shape.js.map +1 -0
  21. package/dist/layout/layout-text-in-compiled-shape.d.ts +2 -0
  22. package/dist/layout/layout-text-in-compiled-shape.js +13 -0
  23. package/dist/layout/layout-text-in-compiled-shape.js.map +1 -0
  24. package/dist/layout/layout-text-in-shape.d.ts +2 -0
  25. package/dist/layout/layout-text-in-shape.js +40 -0
  26. package/dist/layout/layout-text-in-shape.js.map +1 -0
  27. package/dist/layout/resolve-flow-layout.d.ts +2 -0
  28. package/dist/layout/resolve-flow-layout.js +31 -0
  29. package/dist/layout/resolve-flow-layout.js.map +1 -0
  30. package/dist/layout/resolve-max-fill-helpers.d.ts +3 -0
  31. package/dist/layout/resolve-max-fill-helpers.js +18 -0
  32. package/dist/layout/resolve-max-fill-helpers.js.map +1 -0
  33. package/dist/layout/resolve-max-fill-layout.d.ts +2 -0
  34. package/dist/layout/resolve-max-fill-layout.js +34 -0
  35. package/dist/layout/resolve-max-fill-layout.js.map +1 -0
  36. package/dist/layout/resolve-sequential-shape-regions.d.ts +3 -0
  37. package/dist/layout/resolve-sequential-shape-regions.js +95 -0
  38. package/dist/layout/resolve-sequential-shape-regions.js.map +1 -0
  39. package/dist/render/escape-xml-text.d.ts +1 -0
  40. package/dist/render/escape-xml-text.js +9 -0
  41. package/dist/render/escape-xml-text.js.map +1 -0
  42. package/dist/render/normalize-shape-decoration.d.ts +2 -0
  43. package/dist/render/normalize-shape-decoration.js +41 -0
  44. package/dist/render/normalize-shape-decoration.js.map +1 -0
  45. package/dist/render/render-layout-to-svg.d.ts +2 -0
  46. package/dist/render/render-layout-to-svg.js +88 -0
  47. package/dist/render/render-layout-to-svg.js.map +1 -0
  48. package/dist/render/render-svg-shadow-filter.d.ts +10 -0
  49. package/dist/render/render-svg-shadow-filter.js +16 -0
  50. package/dist/render/render-svg-shadow-filter.js.map +1 -0
  51. package/dist/shape/build-text-mask-bands-from-alpha.d.ts +8 -0
  52. package/dist/shape/build-text-mask-bands-from-alpha.js +73 -0
  53. package/dist/shape/build-text-mask-bands-from-alpha.js.map +1 -0
  54. package/dist/shape/compile-polygon-shape-for-layout.d.ts +2 -0
  55. package/dist/shape/compile-polygon-shape-for-layout.js +26 -0
  56. package/dist/shape/compile-polygon-shape-for-layout.js.map +1 -0
  57. package/dist/shape/compile-shape-for-layout.d.ts +2 -0
  58. package/dist/shape/compile-shape-for-layout.js +18 -0
  59. package/dist/shape/compile-shape-for-layout.js.map +1 -0
  60. package/dist/shape/compile-text-mask-shape-for-layout.d.ts +2 -0
  61. package/dist/shape/compile-text-mask-shape-for-layout.js +162 -0
  62. package/dist/shape/compile-text-mask-shape-for-layout.js.map +1 -0
  63. package/dist/shape/render-text-mask-raster.d.ts +13 -0
  64. package/dist/shape/render-text-mask-raster.js +29 -0
  65. package/dist/shape/render-text-mask-raster.js.map +1 -0
  66. package/dist/shape/resolve-text-mask-shape-size.d.ts +18 -0
  67. package/dist/shape/resolve-text-mask-shape-size.js +67 -0
  68. package/dist/shape/resolve-text-mask-shape-size.js.map +1 -0
  69. package/dist/shape/segment-text-mask-graphemes.d.ts +6 -0
  70. package/dist/shape/segment-text-mask-graphemes.js +20 -0
  71. package/dist/shape/segment-text-mask-graphemes.js.map +1 -0
  72. package/dist/text/create-browser-canvas-2d-context.d.ts +2 -0
  73. package/dist/text/create-browser-canvas-2d-context.js +21 -0
  74. package/dist/text/create-browser-canvas-2d-context.js.map +1 -0
  75. package/dist/text/create-canvas-text-measurer.d.ts +2 -0
  76. package/dist/text/create-canvas-text-measurer.js +11 -0
  77. package/dist/text/create-canvas-text-measurer.js.map +1 -0
  78. package/dist/text/layout-next-line-from-dense-repeated-text.d.ts +2 -0
  79. package/dist/text/layout-next-line-from-dense-repeated-text.js +63 -0
  80. package/dist/text/layout-next-line-from-dense-repeated-text.js.map +1 -0
  81. package/dist/text/layout-next-line-from-prepared-text.d.ts +2 -0
  82. package/dist/text/layout-next-line-from-prepared-text.js +57 -0
  83. package/dist/text/layout-next-line-from-prepared-text.js.map +1 -0
  84. package/dist/text/layout-next-line-from-repeated-text.d.ts +2 -0
  85. package/dist/text/layout-next-line-from-repeated-text.js +51 -0
  86. package/dist/text/layout-next-line-from-repeated-text.js.map +1 -0
  87. package/dist/text/layout-text-line-helpers.d.ts +4 -0
  88. package/dist/text/layout-text-line-helpers.js +17 -0
  89. package/dist/text/layout-text-line-helpers.js.map +1 -0
  90. package/dist/text/normalize-text-style-to-font.d.ts +6 -0
  91. package/dist/text/normalize-text-style-to-font.js +31 -0
  92. package/dist/text/normalize-text-style-to-font.js.map +1 -0
  93. package/dist/text/prepare-dense-repeat-fill-pattern.d.ts +2 -0
  94. package/dist/text/prepare-dense-repeat-fill-pattern.js +13 -0
  95. package/dist/text/prepare-dense-repeat-fill-pattern.js.map +1 -0
  96. package/dist/text/prepare-stream-repeat-fill-pattern.d.ts +2 -0
  97. package/dist/text/prepare-stream-repeat-fill-pattern.js +16 -0
  98. package/dist/text/prepare-stream-repeat-fill-pattern.js.map +1 -0
  99. package/dist/text/prepare-text-for-layout.d.ts +2 -0
  100. package/dist/text/prepare-text-for-layout.js +22 -0
  101. package/dist/text/prepare-text-for-layout.js.map +1 -0
  102. package/dist/text/segment-text-for-layout.d.ts +3 -0
  103. package/dist/text/segment-text-for-layout.js +43 -0
  104. package/dist/text/segment-text-for-layout.js.map +1 -0
  105. package/dist/types.d.ts +200 -0
  106. package/dist/types.js +2 -0
  107. package/dist/types.js.map +1 -0
  108. package/package.json +79 -0
@@ -0,0 +1,162 @@
1
+ import { createBrowserCanvas2DContext } from '../text/create-browser-canvas-2d-context.js';
2
+ import { buildTextMaskBandsFromAlpha } from './build-text-mask-bands-from-alpha.js';
3
+ import { getTextMaskPlacement, renderTextMaskRaster, } from './render-text-mask-raster.js';
4
+ import { measureTextMaskContent, resolveTextMaskShapeSize, } from './resolve-text-mask-shape-size.js';
5
+ import { segmentTextMaskGraphemes } from './segment-text-mask-graphemes.js';
6
+ const MAX_MASK_PIXELS = 4_000_000;
7
+ const compiledShapeCache = new Map();
8
+ function validateTextMaskShape(shape, size) {
9
+ if (shape.text.length === 0) {
10
+ throw new Error('text-mask shape needs a non-empty text value');
11
+ }
12
+ const shapeTextMode = shape.shapeTextMode ?? 'whole-text';
13
+ if (shapeTextMode !== 'whole-text' && shapeTextMode !== 'per-character') {
14
+ throw new Error('text-mask shapeTextMode must be whole-text or per-character');
15
+ }
16
+ const maskScale = shape.maskScale ?? 2;
17
+ if (!Number.isFinite(maskScale) || maskScale <= 0) {
18
+ throw new Error('text-mask maskScale must be a finite positive number');
19
+ }
20
+ const pixelWidth = Math.max(1, Math.ceil(size.width * maskScale));
21
+ const pixelHeight = Math.max(1, Math.ceil(size.height * maskScale));
22
+ if (pixelWidth * pixelHeight > MAX_MASK_PIXELS) {
23
+ throw new Error('text-mask raster request is too large');
24
+ }
25
+ const alphaThreshold = shape.alphaThreshold ?? 16;
26
+ if (!Number.isFinite(alphaThreshold) || alphaThreshold < 0 || alphaThreshold > 255) {
27
+ throw new Error('text-mask alphaThreshold must be between 0 and 255');
28
+ }
29
+ }
30
+ function buildCacheKey(shape, size, lineHeight, minSlotWidth) {
31
+ return JSON.stringify({
32
+ kind: shape.kind,
33
+ text: shape.text,
34
+ font: shape.font,
35
+ sizeMode: size.mode,
36
+ width: size.width,
37
+ height: size.height,
38
+ shapeTextMode: shape.shapeTextMode ?? 'whole-text',
39
+ padding: size.padding,
40
+ maskScale: shape.maskScale ?? 2,
41
+ alphaThreshold: shape.alphaThreshold ?? 16,
42
+ lineHeight,
43
+ minSlotWidth,
44
+ });
45
+ }
46
+ function isFontReadyForShape(shape) {
47
+ if (typeof document === 'undefined' || document.fonts === undefined) {
48
+ return true;
49
+ }
50
+ try {
51
+ return document.fonts.check(shape.font, shape.text);
52
+ }
53
+ catch {
54
+ return true;
55
+ }
56
+ }
57
+ function freezeCompiledShape(compiledShape) {
58
+ freezeBands(compiledShape.bands);
59
+ freezeRegions(compiledShape.regions ?? []);
60
+ Object.freeze(compiledShape.bounds);
61
+ Object.freeze(compiledShape.source);
62
+ Object.freeze(compiledShape.debugView);
63
+ Object.freeze(compiledShape.bands);
64
+ if (compiledShape.regions !== undefined) {
65
+ Object.freeze(compiledShape.regions);
66
+ }
67
+ return Object.freeze(compiledShape);
68
+ }
69
+ function freezeBands(bands) {
70
+ for (let bandIndex = 0; bandIndex < bands.length; bandIndex++) {
71
+ const band = bands[bandIndex];
72
+ for (let intervalIndex = 0; intervalIndex < band.intervals.length; intervalIndex++) {
73
+ Object.freeze(band.intervals[intervalIndex]);
74
+ }
75
+ Object.freeze(band.intervals);
76
+ Object.freeze(band);
77
+ }
78
+ }
79
+ function freezeRegions(regions) {
80
+ for (let index = 0; index < regions.length; index++) {
81
+ const region = regions[index];
82
+ freezeBands(region.bands);
83
+ Object.freeze(region.bounds);
84
+ Object.freeze(region.debugView);
85
+ Object.freeze(region.bands);
86
+ Object.freeze(region);
87
+ }
88
+ }
89
+ function buildRegion(shape, size, lineHeight, minSlotWidth, index, grapheme, drawX, baseline) {
90
+ const { imageData } = renderTextMaskRaster(shape, size, grapheme, drawX, baseline);
91
+ return {
92
+ index,
93
+ grapheme,
94
+ bounds: { left: 0, top: 0, right: size.width, bottom: size.height },
95
+ bands: buildTextMaskBandsFromAlpha({
96
+ width: size.width,
97
+ height: size.height,
98
+ maskScale: shape.maskScale ?? 2,
99
+ alphaThreshold: shape.alphaThreshold ?? 16,
100
+ }, imageData.data, lineHeight, minSlotWidth),
101
+ debugView: {
102
+ kind: 'text',
103
+ text: grapheme,
104
+ font: shape.font,
105
+ x: drawX,
106
+ baseline,
107
+ },
108
+ };
109
+ }
110
+ function buildRegions(shape, size, context, placement, lineHeight, minSlotWidth) {
111
+ if ((shape.shapeTextMode ?? 'whole-text') !== 'per-character') {
112
+ return [];
113
+ }
114
+ return segmentTextMaskGraphemes(shape.text, placement.x, value => context.measureText(value).width)
115
+ .filter(placementEntry => !placementEntry.isWhitespace)
116
+ .map((placementEntry, index) => buildRegion(shape, size, lineHeight, minSlotWidth, index, placementEntry.grapheme, placementEntry.drawX, placement.baseline))
117
+ .filter(region => region.bands.some(band => band.intervals.length > 0));
118
+ }
119
+ export function compileTextMaskShapeForLayout(shape, lineHeight, minSlotWidth) {
120
+ const context = createBrowserCanvas2DContext(1, 1);
121
+ const contentMetrics = measureTextMaskContent(context, shape);
122
+ const size = resolveTextMaskShapeSize(shape, contentMetrics);
123
+ validateTextMaskShape(shape, size);
124
+ const fontReadyForCache = isFontReadyForShape(shape);
125
+ const cacheKey = buildCacheKey(shape, size, lineHeight, minSlotWidth);
126
+ const cachedShape = fontReadyForCache ? compiledShapeCache.get(cacheKey) : undefined;
127
+ if (cachedShape !== undefined) {
128
+ return cachedShape;
129
+ }
130
+ const placement = getTextMaskPlacement(size, contentMetrics);
131
+ const { imageData } = renderTextMaskRaster(shape, size, shape.text, placement.x, placement.baseline);
132
+ const compiledShape = {
133
+ kind: shape.kind,
134
+ source: shape,
135
+ bounds: { left: 0, top: 0, right: size.width, bottom: size.height },
136
+ bandHeight: lineHeight,
137
+ minSlotWidth,
138
+ bands: buildTextMaskBandsFromAlpha({
139
+ width: size.width,
140
+ height: size.height,
141
+ maskScale: shape.maskScale ?? 2,
142
+ alphaThreshold: shape.alphaThreshold ?? 16,
143
+ }, imageData.data, lineHeight, minSlotWidth),
144
+ regions: buildRegions(shape, size, context, placement, lineHeight, minSlotWidth),
145
+ debugView: {
146
+ kind: 'text',
147
+ text: shape.text,
148
+ font: shape.font,
149
+ x: placement.x,
150
+ baseline: placement.baseline,
151
+ },
152
+ };
153
+ if (compiledShape.bands.every(band => band.intervals.length === 0)) {
154
+ throw new Error('text-mask shape produced no visible fill area');
155
+ }
156
+ const frozenShape = freezeCompiledShape(compiledShape);
157
+ if (fontReadyForCache) {
158
+ compiledShapeCache.set(cacheKey, frozenShape);
159
+ }
160
+ return frozenShape;
161
+ }
162
+ //# sourceMappingURL=compile-text-mask-shape-for-layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile-text-mask-shape-for-layout.js","sourceRoot":"","sources":["../../src/shape/compile-text-mask-shape-for-layout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { TextMaskShape } from '../types.js';
2
+ import { createBrowserCanvas2DContext } from '../text/create-browser-canvas-2d-context.js';
3
+ import type { ResolvedTextMaskSize, TextMaskContentMetrics } from './resolve-text-mask-shape-size.js';
4
+ export type TextMaskPlacement = {
5
+ x: number;
6
+ baseline: number;
7
+ };
8
+ export type TextMaskCanvas = {
9
+ context: ReturnType<typeof createBrowserCanvas2DContext>;
10
+ imageData: ImageData;
11
+ };
12
+ export declare function getTextMaskPlacement(size: ResolvedTextMaskSize, metrics: TextMaskContentMetrics): TextMaskPlacement;
13
+ export declare function renderTextMaskRaster(shape: TextMaskShape, size: ResolvedTextMaskSize, text?: string, drawX?: number, baseline?: number): TextMaskCanvas;
@@ -0,0 +1,29 @@
1
+ import { createBrowserCanvas2DContext } from '../text/create-browser-canvas-2d-context.js';
2
+ export function getTextMaskPlacement(size, metrics) {
3
+ const innerWidth = Math.max(0, size.width - size.padding * 2);
4
+ const innerHeight = Math.max(0, size.height - size.padding * 2);
5
+ const left = size.padding + Math.max(0, (innerWidth - metrics.width) / 2);
6
+ const top = size.padding + Math.max(0, (innerHeight - metrics.height) / 2);
7
+ return {
8
+ x: left + metrics.left,
9
+ baseline: top + metrics.ascent,
10
+ };
11
+ }
12
+ export function renderTextMaskRaster(shape, size, text = shape.text, drawX, baseline) {
13
+ const maskScale = shape.maskScale ?? 2;
14
+ const pixelWidth = Math.max(1, Math.ceil(size.width * maskScale));
15
+ const pixelHeight = Math.max(1, Math.ceil(size.height * maskScale));
16
+ const context = createBrowserCanvas2DContext(pixelWidth, pixelHeight);
17
+ context.setTransform(1, 0, 0, 1, 0, 0);
18
+ context.clearRect(0, 0, pixelWidth, pixelHeight);
19
+ context.setTransform(maskScale, 0, 0, maskScale, 0, 0);
20
+ context.font = shape.font;
21
+ context.fillStyle = '#000000';
22
+ context.textBaseline = 'alphabetic';
23
+ context.fillText(text, drawX ?? 0, baseline ?? 0);
24
+ return {
25
+ context,
26
+ imageData: context.getImageData(0, 0, pixelWidth, pixelHeight),
27
+ };
28
+ }
29
+ //# sourceMappingURL=render-text-mask-raster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-text-mask-raster.js","sourceRoot":"","sources":["../../src/shape/render-text-mask-raster.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,4BAA4B,EAAE,MAAM,6CAA6C,CAAA;AAa1F,MAAM,UAAU,oBAAoB,CAClC,IAA0B,EAC1B,OAA+B;IAE/B,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAA;IAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAA;IAC/D,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;IACzE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;IAE1E,OAAO;QACL,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI;QACtB,QAAQ,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM;KAC/B,CAAA;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,KAAoB,EACpB,IAA0B,EAC1B,IAAI,GAAG,KAAK,CAAC,IAAI,EACjB,KAAc,EACd,QAAiB;IAEjB,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,CAAC,CAAA;IACtC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,CAAA;IACjE,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;IACnE,MAAM,OAAO,GAAG,4BAA4B,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;IAErE,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IACtC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,UAAU,EAAE,WAAW,CAAC,CAAA;IAChD,OAAO,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IACtD,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;IACzB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAA;IAC7B,OAAO,CAAC,YAAY,GAAG,YAAY,CAAA;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE,QAAQ,IAAI,CAAC,CAAC,CAAA;IAEjD,OAAO;QACL,OAAO;QACP,SAAS,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,UAAU,EAAE,WAAW,CAAC;KAC/D,CAAA;AACH,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { BrowserCanvas2DContext } from '../text/create-browser-canvas-2d-context.js';
2
+ import type { TextMaskShape, TextMaskShapeSizeMode } from '../types.js';
3
+ export type TextMaskContentMetrics = {
4
+ left: number;
5
+ right: number;
6
+ ascent: number;
7
+ descent: number;
8
+ width: number;
9
+ height: number;
10
+ };
11
+ export type ResolvedTextMaskSize = {
12
+ mode: TextMaskShapeSizeMode;
13
+ width: number;
14
+ height: number;
15
+ padding: number;
16
+ };
17
+ export declare function measureTextMaskContent(context: BrowserCanvas2DContext, shape: TextMaskShape, text?: string): TextMaskContentMetrics;
18
+ export declare function resolveTextMaskShapeSize(shape: TextMaskShape, contentMetrics: TextMaskContentMetrics): ResolvedTextMaskSize;
@@ -0,0 +1,67 @@
1
+ function assertFiniteNumber(value, label) {
2
+ if (!Number.isFinite(value)) {
3
+ throw new Error(`${label} must be a finite number`);
4
+ }
5
+ return value;
6
+ }
7
+ function assertPositiveFiniteNumber(value, label) {
8
+ if (!Number.isFinite(value) || value <= 0) {
9
+ throw new Error(`${label} must be a finite positive number`);
10
+ }
11
+ return value;
12
+ }
13
+ function assertNoLegacySizeFields(shape) {
14
+ if ('width' in shape || 'height' in shape || 'padding' in shape) {
15
+ throw new Error('text-mask width, height, and padding moved to shape.size');
16
+ }
17
+ }
18
+ function resolveMetric(value, fallback = 0) {
19
+ return value !== undefined && Number.isFinite(value) ? value : fallback;
20
+ }
21
+ export function measureTextMaskContent(context, shape, text = shape.text) {
22
+ context.font = shape.font;
23
+ const metrics = context.measureText(text);
24
+ const left = resolveMetric(metrics.actualBoundingBoxLeft, 0);
25
+ const right = resolveMetric(metrics.actualBoundingBoxRight, metrics.width);
26
+ const ascent = resolveMetric(metrics.actualBoundingBoxAscent, 0);
27
+ const descent = resolveMetric(metrics.actualBoundingBoxDescent, 0);
28
+ return {
29
+ left,
30
+ right,
31
+ ascent,
32
+ descent,
33
+ width: left + right,
34
+ height: ascent + descent,
35
+ };
36
+ }
37
+ export function resolveTextMaskShapeSize(shape, contentMetrics) {
38
+ assertNoLegacySizeFields(shape);
39
+ if (shape.size !== undefined && (shape.size === null || typeof shape.size !== 'object')) {
40
+ throw new Error('text-mask size must be an object');
41
+ }
42
+ const size = shape.size ?? {};
43
+ const mode = size.mode ?? 'fit-content';
44
+ if (mode !== 'fit-content' && mode !== 'fixed') {
45
+ throw new Error('text-mask size.mode must be fit-content or fixed');
46
+ }
47
+ const padding = size.padding === undefined ? 0 : assertFiniteNumber(size.padding, 'text-mask padding');
48
+ if (padding < 0) {
49
+ throw new Error('text-mask padding must be a finite non-negative number');
50
+ }
51
+ if (mode === 'fixed') {
52
+ const fixedSize = size;
53
+ return {
54
+ mode,
55
+ width: assertPositiveFiniteNumber(fixedSize.width, 'text-mask fixed width'),
56
+ height: assertPositiveFiniteNumber(fixedSize.height, 'text-mask fixed height'),
57
+ padding,
58
+ };
59
+ }
60
+ return {
61
+ mode,
62
+ width: Math.max(1, contentMetrics.width + padding * 2),
63
+ height: Math.max(1, contentMetrics.height + padding * 2),
64
+ padding,
65
+ };
66
+ }
67
+ //# sourceMappingURL=resolve-text-mask-shape-size.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-text-mask-shape-size.js","sourceRoot":"","sources":["../../src/shape/resolve-text-mask-shape-size.ts"],"names":[],"mappings":"AAyBA,SAAS,kBAAkB,CAAC,KAAa,EAAE,KAAa;IACtD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,0BAA0B,CAAC,CAAA;IACrD,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,0BAA0B,CAAC,KAAa,EAAE,KAAa;IAC9D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,mCAAmC,CAAC,CAAA;IAC9D,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,wBAAwB,CAAC,KAAwC;IACxE,IAAI,OAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,IAAI,SAAS,IAAI,KAAK,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;IAC7E,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,KAAyB,EAAE,QAAQ,GAAG,CAAC;IAC5D,OAAO,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAA;AACzE,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,OAA+B,EAC/B,KAAoB,EACpB,IAAI,GAAG,KAAK,CAAC,IAAI;IAEjB,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;IACzB,MAAM,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;IACzC,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC,CAAA;IAC5D,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,sBAAsB,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;IAC1E,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAAA;IAChE,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAAC,CAAA;IAElE,OAAO;QACL,IAAI;QACJ,KAAK;QACL,MAAM;QACN,OAAO;QACP,KAAK,EAAE,IAAI,GAAG,KAAK;QACnB,MAAM,EAAE,MAAM,GAAG,OAAO;KACzB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,KAAoB,EACpB,cAAsC;IAEtC,wBAAwB,CAAC,KAA0C,CAAC,CAAA;IAEpE,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,EAAE,CAAC;QACxF,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;IACrD,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE,CAAA;IAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,aAAa,CAAA;IACvC,IAAI,IAAI,KAAK,aAAa,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAA;IACrE,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAA;IACtG,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAA;IAC3E,CAAC;IAED,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,MAAM,SAAS,GAAG,IAAyC,CAAA;QAE3D,OAAO;YACL,IAAI;YACJ,KAAK,EAAE,0BAA0B,CAAC,SAAS,CAAC,KAAK,EAAE,uBAAuB,CAAC;YAC3E,MAAM,EAAE,0BAA0B,CAAC,SAAS,CAAC,MAAM,EAAE,wBAAwB,CAAC;YAC9E,OAAO;SACR,CAAA;IACH,CAAC;IAED,OAAO;QACL,IAAI;QACJ,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC,KAAK,GAAG,OAAO,GAAG,CAAC,CAAC;QACtD,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC;QACxD,OAAO;KACR,CAAA;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ export type TextMaskGraphemePlacement = {
2
+ grapheme: string;
3
+ drawX: number;
4
+ isWhitespace: boolean;
5
+ };
6
+ export declare function segmentTextMaskGraphemes(text: string, originX: number, measureAdvance: (value: string) => number): TextMaskGraphemePlacement[];
@@ -0,0 +1,20 @@
1
+ const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
2
+ function isWhitespaceGrapheme(grapheme) {
3
+ return grapheme.trim().length === 0;
4
+ }
5
+ export function segmentTextMaskGraphemes(text, originX, measureAdvance) {
6
+ const segments = Array.from(graphemeSegmenter.segment(text), segment => segment.segment);
7
+ const placements = [];
8
+ let prefix = '';
9
+ for (let index = 0; index < segments.length; index++) {
10
+ const grapheme = segments[index];
11
+ placements.push({
12
+ grapheme,
13
+ drawX: originX + measureAdvance(prefix),
14
+ isWhitespace: isWhitespaceGrapheme(grapheme),
15
+ });
16
+ prefix += grapheme;
17
+ }
18
+ return placements;
19
+ }
20
+ //# sourceMappingURL=segment-text-mask-graphemes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"segment-text-mask-graphemes.js","sourceRoot":"","sources":["../../src/shape/segment-text-mask-graphemes.ts"],"names":[],"mappings":"AAAA,MAAM,iBAAiB,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAA;AAQpF,SAAS,oBAAoB,CAAC,QAAgB;IAC5C,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,IAAY,EACZ,OAAe,EACf,cAAyC;IAEzC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IACxF,MAAM,UAAU,GAAgC,EAAE,CAAA;IAClD,IAAI,MAAM,GAAG,EAAE,CAAA;IAEf,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAE,CAAA;QACjC,UAAU,CAAC,IAAI,CAAC;YACd,QAAQ;YACR,KAAK,EAAE,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC;YACvC,YAAY,EAAE,oBAAoB,CAAC,QAAQ,CAAC;SAC7C,CAAC,CAAA;QACF,MAAM,IAAI,QAAQ,CAAA;IACpB,CAAC;IAED,OAAO,UAAU,CAAA;AACnB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export type BrowserCanvas2DContext = CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;
2
+ export declare function createBrowserCanvas2DContext(width?: number, height?: number): BrowserCanvas2DContext;
@@ -0,0 +1,21 @@
1
+ export function createBrowserCanvas2DContext(width = 1, height = 1) {
2
+ const safeWidth = Math.max(1, Math.ceil(width));
3
+ const safeHeight = Math.max(1, Math.ceil(height));
4
+ if (typeof OffscreenCanvas !== 'undefined') {
5
+ const context = new OffscreenCanvas(safeWidth, safeHeight).getContext('2d');
6
+ if (context !== null) {
7
+ return context;
8
+ }
9
+ }
10
+ if (typeof document !== 'undefined') {
11
+ const canvas = document.createElement('canvas');
12
+ canvas.width = safeWidth;
13
+ canvas.height = safeHeight;
14
+ const context = canvas.getContext('2d');
15
+ if (context !== null) {
16
+ return context;
17
+ }
18
+ }
19
+ throw new Error('shape-text needs a browser canvas context or a custom TextMeasurer');
20
+ }
21
+ //# sourceMappingURL=create-browser-canvas-2d-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-browser-canvas-2d-context.js","sourceRoot":"","sources":["../../src/text/create-browser-canvas-2d-context.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,4BAA4B,CAC1C,KAAK,GAAG,CAAC,EACT,MAAM,GAAG,CAAC;IAEV,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;IAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAEjD,IAAI,OAAO,eAAe,KAAK,WAAW,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QAC3E,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,OAAO,CAAA;QAChB,CAAC;IACH,CAAC;IAED,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;QAC/C,MAAM,CAAC,KAAK,GAAG,SAAS,CAAA;QACxB,MAAM,CAAC,MAAM,GAAG,UAAU,CAAA;QAC1B,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QACvC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,OAAO,CAAA;QAChB,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAA;AACvF,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { TextMeasurer } from '../types.js';
2
+ export declare function createCanvasTextMeasurer(): TextMeasurer;
@@ -0,0 +1,11 @@
1
+ import { createBrowserCanvas2DContext } from './create-browser-canvas-2d-context.js';
2
+ export function createCanvasTextMeasurer() {
3
+ const context = createBrowserCanvas2DContext();
4
+ return {
5
+ measureText(text, font) {
6
+ context.font = font;
7
+ return context.measureText(text).width;
8
+ },
9
+ };
10
+ }
11
+ //# sourceMappingURL=create-canvas-text-measurer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-canvas-text-measurer.js","sourceRoot":"","sources":["../../src/text/create-canvas-text-measurer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAA;AAEpF,MAAM,UAAU,wBAAwB;IACtC,MAAM,OAAO,GAAG,4BAA4B,EAAE,CAAA;IAE9C,OAAO;QACL,WAAW,CAAC,IAAI,EAAE,IAAI;YACpB,OAAO,CAAC,IAAI,GAAG,IAAI,CAAA;YACnB,OAAO,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,KAAK,CAAA;QACxC,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { LayoutLineRange, PreparedLayoutToken } from '../types.js';
2
+ export declare function layoutNextLineFromDenseRepeatedText(pattern: PreparedLayoutToken, startOffset: number, maxWidth: number): LayoutLineRange | null;
@@ -0,0 +1,63 @@
1
+ import { getWordSliceText, getWordSliceWidth } from './layout-text-line-helpers.js';
2
+ function findMaxSliceEnd(pattern, start, maxWidth) {
3
+ let low = start;
4
+ let high = pattern.graphemes.length;
5
+ while (low < high) {
6
+ const mid = Math.ceil((low + high) / 2);
7
+ const width = getWordSliceWidth(pattern, start, mid);
8
+ if (width <= maxWidth) {
9
+ low = mid;
10
+ continue;
11
+ }
12
+ high = mid - 1;
13
+ }
14
+ return low;
15
+ }
16
+ export function layoutNextLineFromDenseRepeatedText(pattern, startOffset, maxWidth) {
17
+ if (pattern.graphemes.length === 0) {
18
+ return null;
19
+ }
20
+ const availableWidth = Math.max(0, maxWidth);
21
+ const start = { tokenIndex: startOffset, graphemeIndex: 0 };
22
+ const textParts = [];
23
+ let width = 0;
24
+ let consumed = 0;
25
+ const appendSlice = (sliceStart, sliceEnd) => {
26
+ if (sliceEnd <= sliceStart) {
27
+ return;
28
+ }
29
+ textParts.push(getWordSliceText(pattern, sliceStart, sliceEnd));
30
+ width += getWordSliceWidth(pattern, sliceStart, sliceEnd);
31
+ consumed += sliceEnd - sliceStart;
32
+ };
33
+ const tailEnd = findMaxSliceEnd(pattern, startOffset, availableWidth);
34
+ appendSlice(startOffset, tailEnd);
35
+ if (tailEnd === pattern.graphemes.length) {
36
+ const remainingAfterTail = availableWidth - width;
37
+ if (remainingAfterTail >= pattern.width) {
38
+ const fullCycles = Math.floor(remainingAfterTail / pattern.width);
39
+ if (fullCycles > 0) {
40
+ textParts.push(pattern.text.repeat(fullCycles));
41
+ width += pattern.width * fullCycles;
42
+ consumed += pattern.graphemes.length * fullCycles;
43
+ }
44
+ }
45
+ const remainingAfterCycles = availableWidth - width;
46
+ const prefixEnd = findMaxSliceEnd(pattern, 0, remainingAfterCycles);
47
+ appendSlice(0, prefixEnd);
48
+ }
49
+ if (consumed === 0) {
50
+ const forcedEnd = Math.min(startOffset + 1, pattern.graphemes.length);
51
+ appendSlice(startOffset, forcedEnd);
52
+ }
53
+ return {
54
+ text: textParts.join(''),
55
+ width,
56
+ start,
57
+ end: {
58
+ tokenIndex: (startOffset + consumed) % pattern.graphemes.length,
59
+ graphemeIndex: 0,
60
+ },
61
+ };
62
+ }
63
+ //# sourceMappingURL=layout-next-line-from-dense-repeated-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout-next-line-from-dense-repeated-text.js","sourceRoot":"","sources":["../../src/text/layout-next-line-from-dense-repeated-text.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { LayoutCursor, LayoutLineRange, PreparedLayoutText } from '../types.js';
2
+ export declare function layoutNextLineFromPreparedText(prepared: PreparedLayoutText, start: LayoutCursor, maxWidth: number): LayoutLineRange | null;
@@ -0,0 +1,57 @@
1
+ import { fitWordFragment, getWordSliceText, getWordSliceWidth } from './layout-text-line-helpers.js';
2
+ export function layoutNextLineFromPreparedText(prepared, start, maxWidth) {
3
+ if (start.tokenIndex >= prepared.tokens.length)
4
+ return null;
5
+ const lineWords = [];
6
+ let width = 0;
7
+ let tokenIndex = start.tokenIndex;
8
+ let graphemeIndex = start.graphemeIndex;
9
+ while (tokenIndex < prepared.tokens.length) {
10
+ const token = prepared.tokens[tokenIndex];
11
+ if (token.kind === 'newline') {
12
+ return {
13
+ text: lineWords.join(' '),
14
+ width,
15
+ start,
16
+ end: { tokenIndex: tokenIndex + 1, graphemeIndex: 0 },
17
+ };
18
+ }
19
+ const sliceStart = tokenIndex === start.tokenIndex ? graphemeIndex : 0;
20
+ const spacing = lineWords.length === 0 ? 0 : prepared.spaceWidth;
21
+ const remainingWidth = getWordSliceWidth(token, sliceStart, token.graphemes.length);
22
+ if (width + spacing + remainingWidth <= maxWidth) {
23
+ lineWords.push(getWordSliceText(token, sliceStart, token.graphemes.length));
24
+ width += spacing + remainingWidth;
25
+ tokenIndex += 1;
26
+ graphemeIndex = 0;
27
+ continue;
28
+ }
29
+ if (lineWords.length > 0) {
30
+ return {
31
+ text: lineWords.join(' '),
32
+ width,
33
+ start,
34
+ end: { tokenIndex, graphemeIndex: sliceStart },
35
+ };
36
+ }
37
+ const fittedEnd = fitWordFragment(token, sliceStart, maxWidth);
38
+ const fragmentText = getWordSliceText(token, sliceStart, fittedEnd);
39
+ const fragmentWidth = getWordSliceWidth(token, sliceStart, fittedEnd);
40
+ const nextCursor = fittedEnd >= token.graphemes.length
41
+ ? { tokenIndex: tokenIndex + 1, graphemeIndex: 0 }
42
+ : { tokenIndex, graphemeIndex: fittedEnd };
43
+ return {
44
+ text: fragmentText,
45
+ width: fragmentWidth,
46
+ start,
47
+ end: nextCursor,
48
+ };
49
+ }
50
+ return {
51
+ text: lineWords.join(' '),
52
+ width,
53
+ start,
54
+ end: { tokenIndex, graphemeIndex: 0 },
55
+ };
56
+ }
57
+ //# sourceMappingURL=layout-next-line-from-prepared-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout-next-line-from-prepared-text.js","sourceRoot":"","sources":["../../src/text/layout-next-line-from-prepared-text.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { LayoutCursor, LayoutLineRange, PreparedLayoutText } from '../types.js';
2
+ export declare function layoutNextLineFromRepeatedText(prepared: PreparedLayoutText, start: LayoutCursor, maxWidth: number): LayoutLineRange | null;
@@ -0,0 +1,51 @@
1
+ import { fitWordFragment, getWordSliceText, getWordSliceWidth } from './layout-text-line-helpers.js';
2
+ export function layoutNextLineFromRepeatedText(prepared, start, maxWidth) {
3
+ if (prepared.tokens.length === 0)
4
+ return null;
5
+ const lineWords = [];
6
+ let width = 0;
7
+ let tokenIndex = start.tokenIndex;
8
+ let graphemeIndex = start.graphemeIndex;
9
+ while (true) {
10
+ const token = prepared.tokens[tokenIndex % prepared.tokens.length];
11
+ if (token.kind === 'newline') {
12
+ return {
13
+ text: lineWords.join(' '),
14
+ width,
15
+ start,
16
+ end: { tokenIndex: tokenIndex + 1, graphemeIndex: 0 },
17
+ };
18
+ }
19
+ const sliceStart = tokenIndex === start.tokenIndex ? graphemeIndex : 0;
20
+ const spacing = lineWords.length === 0 ? 0 : prepared.spaceWidth;
21
+ const remainingWidth = getWordSliceWidth(token, sliceStart, token.graphemes.length);
22
+ if (width + spacing + remainingWidth <= maxWidth) {
23
+ lineWords.push(getWordSliceText(token, sliceStart, token.graphemes.length));
24
+ width += spacing + remainingWidth;
25
+ tokenIndex += 1;
26
+ graphemeIndex = 0;
27
+ continue;
28
+ }
29
+ if (lineWords.length > 0) {
30
+ return {
31
+ text: lineWords.join(' '),
32
+ width,
33
+ start,
34
+ end: { tokenIndex, graphemeIndex: sliceStart },
35
+ };
36
+ }
37
+ const fittedEnd = fitWordFragment(token, sliceStart, maxWidth);
38
+ const fragmentText = getWordSliceText(token, sliceStart, fittedEnd);
39
+ const fragmentWidth = getWordSliceWidth(token, sliceStart, fittedEnd);
40
+ const nextCursor = fittedEnd >= token.graphemes.length
41
+ ? { tokenIndex: tokenIndex + 1, graphemeIndex: 0 }
42
+ : { tokenIndex, graphemeIndex: fittedEnd };
43
+ return {
44
+ text: fragmentText,
45
+ width: fragmentWidth,
46
+ start,
47
+ end: nextCursor,
48
+ };
49
+ }
50
+ }
51
+ //# sourceMappingURL=layout-next-line-from-repeated-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout-next-line-from-repeated-text.js","sourceRoot":"","sources":["../../src/text/layout-next-line-from-repeated-text.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AAEpG,MAAM,UAAU,8BAA8B,CAC5C,QAA4B,EAC5B,KAAmB,EACnB,QAAgB;IAEhB,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAE7C,MAAM,SAAS,GAAa,EAAE,CAAA;IAC9B,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,UAAU,GAAG,KAAK,CAAC,UAAU,CAAA;IACjC,IAAI,aAAa,GAAG,KAAK,CAAC,aAAa,CAAA;IAEvC,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAE,CAAA;QAEnE,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO;gBACL,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;gBACzB,KAAK;gBACL,KAAK;gBACL,GAAG,EAAE,EAAE,UAAU,EAAE,UAAU,GAAG,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE;aACtD,CAAA;QACH,CAAC;QAED,MAAM,UAAU,GAAG,UAAU,KAAK,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;QACtE,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAA;QAChE,MAAM,cAAc,GAAG,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;QAEnF,IAAI,KAAK,GAAG,OAAO,GAAG,cAAc,IAAI,QAAQ,EAAE,CAAC;YACjD,SAAS,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;YAC3E,KAAK,IAAI,OAAO,GAAG,cAAc,CAAA;YACjC,UAAU,IAAI,CAAC,CAAA;YACf,aAAa,GAAG,CAAC,CAAA;YACjB,SAAQ;QACV,CAAC;QAED,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,OAAO;gBACL,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;gBACzB,KAAK;gBACL,KAAK;gBACL,GAAG,EAAE,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE;aAC/C,CAAA;QACH,CAAC;QAED,MAAM,SAAS,GAAG,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAA;QAC9D,MAAM,YAAY,GAAG,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;QACnE,MAAM,aAAa,GAAG,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;QACrE,MAAM,UAAU,GACd,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM;YACjC,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,GAAG,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE;YAClD,CAAC,CAAC,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,CAAA;QAE9C,OAAO;YACL,IAAI,EAAE,YAAY;YAClB,KAAK,EAAE,aAAa;YACpB,KAAK;YACL,GAAG,EAAE,UAAU;SAChB,CAAA;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { PreparedLayoutToken } from '../types.js';
2
+ export declare function getWordSliceWidth(token: PreparedLayoutToken, start: number, end: number): number;
3
+ export declare function getWordSliceText(token: PreparedLayoutToken, start: number, end: number): string;
4
+ export declare function fitWordFragment(token: PreparedLayoutToken, start: number, maxWidth: number): number;
@@ -0,0 +1,17 @@
1
+ export function getWordSliceWidth(token, start, end) {
2
+ return token.graphemePrefixWidths[end] - token.graphemePrefixWidths[start];
3
+ }
4
+ export function getWordSliceText(token, start, end) {
5
+ return token.graphemes.slice(start, end).join('');
6
+ }
7
+ export function fitWordFragment(token, start, maxWidth) {
8
+ const availableWidth = Math.max(0, maxWidth);
9
+ for (let end = start + 1; end <= token.graphemes.length; end++) {
10
+ const width = getWordSliceWidth(token, start, end);
11
+ if (width > availableWidth) {
12
+ return end === start + 1 ? end : end - 1;
13
+ }
14
+ }
15
+ return token.graphemes.length;
16
+ }
17
+ //# sourceMappingURL=layout-text-line-helpers.js.map