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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Admin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,264 @@
1
+ # shape-text
2
+
3
+ Browser-first TypeScript library for shape-paragraph layout and SVG rendering.
4
+
5
+ ## V1 scope
6
+
7
+ - SVG renderer
8
+ - Geometry input
9
+ - Value-derived input from text masks
10
+ - Latin/Vietnamese first
11
+ - Single closed shape, no holes
12
+ - Shape-first API, not a thin wrapper over `pretext`
13
+ - Compile-shape boundary for cache-friendly repeated rendering
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install shape-text
19
+ bun add shape-text
20
+ ```
21
+
22
+ ## Local demo and browser E2E
23
+
24
+ ```bash
25
+ npx playwright install chromium
26
+ npm run demo
27
+ npm run e2e:ui
28
+ ```
29
+
30
+ - `npm run demo` starts the React workbench on `http://127.0.0.1:4173/`
31
+ - `npm run demo:preview` serves the built demo
32
+ - `npm run e2e` and `npm run e2e:ui` run Playwright against the built preview app on port `4174`, which is closer to production than hitting the dev server
33
+ - The npm scripts resolve their own package root so they still work even if Windows launches `cmd.exe` with a broken fallback cwd
34
+
35
+ ## Ship readiness
36
+
37
+ Library packaging is validated for both `npm` and `bun`.
38
+
39
+ ```bash
40
+ npm run ship:check
41
+ ```
42
+
43
+ That flow currently checks:
44
+
45
+ - clean library build output
46
+ - `npm pack` tarball contents
47
+ - `bun pm pack` compatibility
48
+ - smoke install + ESM import via both `npm` and `bun`
49
+
50
+ ## Quick start
51
+
52
+ ```ts
53
+ import {
54
+ createCanvasTextMeasurer,
55
+ layoutTextInShape,
56
+ renderLayoutToSvg,
57
+ } from 'shape-text'
58
+
59
+ const measurer = createCanvasTextMeasurer()
60
+
61
+ const layout = layoutTextInShape({
62
+ text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
63
+ textStyle: {
64
+ family: '"Helvetica Neue", Arial, sans-serif',
65
+ size: 16,
66
+ weight: 700,
67
+ style: 'italic',
68
+ color: '#111827',
69
+ },
70
+ lineHeight: 22,
71
+ shape: {
72
+ kind: 'polygon',
73
+ points: [
74
+ { x: 0, y: 0 },
75
+ { x: 240, y: 0 },
76
+ { x: 240, y: 280 },
77
+ { x: 0, y: 280 },
78
+ ],
79
+ },
80
+ measurer,
81
+ })
82
+
83
+ const svg = renderLayoutToSvg(layout, {
84
+ background: '#fffdf7',
85
+ shapeStyle: {
86
+ backgroundColor: '#dbeafe',
87
+ borderColor: '#94a3b8',
88
+ borderWidth: 2,
89
+ shadow: {
90
+ blur: 6,
91
+ offsetY: 6,
92
+ },
93
+ },
94
+ })
95
+ ```
96
+
97
+ ## Shape sources
98
+
99
+ `shape-text` currently ships two first-class ways to provide the shape paragraph surface:
100
+
101
+ - Geometry input: pass explicit polygon points
102
+ - Value-derived input: pass a `text-mask` shape derived from text and font
103
+
104
+ The low-level API term stays `text-mask`, but the product framing is `value-derived shape`.
105
+
106
+ ## Value-derived example
107
+
108
+ ```ts
109
+ const layout = layoutTextInShape({
110
+ text: 'Shape paragraph can fill a value-derived silhouette too.',
111
+ textStyle: {
112
+ family: 'Arial, sans-serif',
113
+ size: 16,
114
+ weight: 700,
115
+ color: '#0f172a',
116
+ },
117
+ lineHeight: 20,
118
+ autoFill: true,
119
+ shape: {
120
+ kind: 'text-mask',
121
+ text: '23',
122
+ font: '700 420px Arial',
123
+ size: {
124
+ mode: 'fit-content',
125
+ padding: 10,
126
+ },
127
+ },
128
+ measurer,
129
+ })
130
+ ```
131
+
132
+ ## Sequential value-derived regions
133
+
134
+ ```ts
135
+ const layout = layoutTextInShape({
136
+ text: 'ABCDEFGHIJ',
137
+ textStyle: {
138
+ family: 'Arial, sans-serif',
139
+ size: 14,
140
+ weight: 700,
141
+ },
142
+ lineHeight: 18,
143
+ shape: {
144
+ kind: 'text-mask',
145
+ text: 'AB',
146
+ font: '700 160px Arial',
147
+ size: {
148
+ mode: 'fixed',
149
+ width: 260,
150
+ height: 180,
151
+ },
152
+ shapeTextMode: 'per-character',
153
+ },
154
+ measurer,
155
+ })
156
+ ```
157
+
158
+ `shape.size` defaults to `{ mode: 'fit-content', padding: 0 }`. Use `mode: 'fixed'` only when you need to force the glyph mask into an explicit raster box.
159
+
160
+ `shape.shapeTextMode` defaults to `'whole-text'`. Set it to `'per-character'` to compile one ordered region per non-space grapheme and flow layout through those regions in shape-text order.
161
+
162
+ ## Public API
163
+
164
+ - `createCanvasTextMeasurer()`
165
+ - `compileShapeForLayout()`
166
+ - `normalizeTextStyleToFont()`
167
+ - `prepareTextForLayout()`
168
+ - `layoutNextLineFromPreparedText()`
169
+ - `layoutNextLineFromRepeatedText()`
170
+ - `getBandIntervalsFromPolygon()`
171
+ - `layoutTextInCompiledShape()`
172
+ - `layoutTextInShape()`
173
+ - `renderLayoutToSvg()`
174
+
175
+ ## Notes
176
+
177
+ - V1 keeps the text engine simple on purpose. It uses `Intl.Segmenter` for grapheme-safe word breaking, but it does not promise full browser-parity for every writing system.
178
+ - The project takes inspiration from `pretext` for the `prepare -> layout` split and streaming line iteration, but owns its geometry, slot policy, and public API.
179
+ - Geometry and value-derived shapes both compile into reusable line bands before layout.
180
+ - `text-mask` shapes are raster-compiled into reusable line bands. This is the default value-derived path for browser fonts such as `Arial`, and it is designed so callers can precompile `0-9` and `:` for clock-like UIs.
181
+ - `autoFill: true` now means one thing: max-fill stream layout that sweeps every usable interval in reading order.
182
+ - Max fill keeps spaces as normal graphemes instead of stripping them, and it does not fall back to smaller text for leftover pockets.
183
+ - `text-mask` sizing now lives under `shape.size`. The default `fit-content` mode measures the text mask first and grows the raster box to avoid clipping multi-character shapes such as `23`.
184
+ - `shape.shapeTextMode: 'per-character'` keeps the full text-mask debug view, but also compiles ordered per-character regions for sequential fill across multi-character shape text.
185
+ - `textStyle` is the new data-driven API for size, weight, italic/oblique, family, and default text color. Legacy `font` string input still works.
186
+ - `shapeStyle` lives in `renderLayoutToSvg()` because fill, border, and shadow do not affect line breaking or shape compilation.
187
+ - For late-loading web fonts, compile after the font is ready if you want immediate cache reuse. The compiler skips cache writes until `document.fonts.check()` reports the font as ready.
188
+
189
+ ## Local E2E
190
+
191
+ Install the local browser once:
192
+
193
+ ```bash
194
+ npx playwright install chromium
195
+ ```
196
+
197
+ Run the local browser suite:
198
+
199
+ ```bash
200
+ npm run e2e
201
+ ```
202
+
203
+ Run unit coverage for `src/`:
204
+
205
+ ```bash
206
+ npm run test:coverage
207
+ ```
208
+
209
+ Useful dev modes:
210
+
211
+ ```bash
212
+ npm run e2e:ui
213
+ npm run e2e:headed
214
+ npm run e2e:debug
215
+ ```
216
+
217
+ Playwright now targets the React workbench in `demo/`, so browser coverage runs against the same app used for manual exploration.
218
+
219
+ ## Publish notes
220
+
221
+ - Published package surface is limited to `dist/`, `README.md`, and `LICENSE`
222
+ - `npm pack` / `npm publish` trigger a clean library rebuild through `prepack`
223
+ - Build output excludes test files so the tarball stays library-only
224
+ - On Windows terminals that start inside a `\\?\C:\...` cwd, prefer `npm run publish:npm` instead of raw `npm publish`
225
+ - PR validation now runs through `.github/workflows/ci.yml`
226
+ - Tag releases now run through `.github/workflows/release.yml`
227
+ - Preferred publish path is npm Trusted Publisher via GitHub Actions OIDC, with `NPM_TOKEN` as fallback only
228
+ - Maintainer release steps and repository settings live in [docs/deployment-guide.md](./docs/deployment-guide.md)
229
+
230
+ ## Local Demo UI
231
+
232
+ Open the React workbench:
233
+
234
+ ```bash
235
+ npm run demo
236
+ ```
237
+
238
+ Then open:
239
+
240
+ ```text
241
+ http://127.0.0.1:4173/
242
+ ```
243
+
244
+ Build the workbench explicitly:
245
+
246
+ ```bash
247
+ npm run demo:build
248
+ ```
249
+
250
+ Preview the built app:
251
+
252
+ ```bash
253
+ npm run demo:preview
254
+ ```
255
+
256
+ The workbench includes:
257
+
258
+ - geometry vs value-derived shape source switching
259
+ - direct `shape.text` editing for value-derived shapes
260
+ - `shape.size.mode` switching between `fit-content` and `fixed`
261
+ - `shapeTextMode` switching between `whole-text` and sequential `per-character` value-derived regions
262
+ - random character-pattern fill presets
263
+ - a payload JSON editor for the live `layout` + `render` request
264
+ - a scrollable full-output SVG viewport with `Zoom out`, `Zoom in`, `100%`, and `Fit` controls
@@ -0,0 +1,2 @@
1
+ import type { Interval, ShapeTextPoint } from '../types.js';
2
+ export declare function getBandIntervalsFromPolygon(points: ShapeTextPoint[], bandTop: number, bandBottom: number, minSlotWidth?: number): Interval[];
@@ -0,0 +1,44 @@
1
+ import { getXIntersectionsAtY } from './get-x-intersections-at-y.js';
2
+ function pairIntersections(xs) {
3
+ const intervals = [];
4
+ for (let index = 0; index + 1 < xs.length; index += 2) {
5
+ intervals.push({ left: xs[index], right: xs[index + 1] });
6
+ }
7
+ return intervals;
8
+ }
9
+ function intersectIntervalSets(leftSet, rightSet) {
10
+ const intersections = [];
11
+ let leftIndex = 0;
12
+ let rightIndex = 0;
13
+ while (leftIndex < leftSet.length && rightIndex < rightSet.length) {
14
+ const left = leftSet[leftIndex];
15
+ const right = rightSet[rightIndex];
16
+ const overlapLeft = Math.max(left.left, right.left);
17
+ const overlapRight = Math.min(left.right, right.right);
18
+ if (overlapRight > overlapLeft) {
19
+ intersections.push({ left: overlapLeft, right: overlapRight });
20
+ }
21
+ if (left.right < right.right) {
22
+ leftIndex += 1;
23
+ }
24
+ else {
25
+ rightIndex += 1;
26
+ }
27
+ }
28
+ return intersections;
29
+ }
30
+ export function getBandIntervalsFromPolygon(points, bandTop, bandBottom, minSlotWidth = 0) {
31
+ const startRow = Math.floor(bandTop);
32
+ const endRow = Math.max(startRow, Math.ceil(bandBottom) - 1);
33
+ let intervals = null;
34
+ for (let row = startRow; row <= endRow; row++) {
35
+ const rowIntervals = pairIntersections(getXIntersectionsAtY(points, row + 0.5));
36
+ if (rowIntervals.length === 0)
37
+ return [];
38
+ intervals = intervals === null ? rowIntervals : intersectIntervalSets(intervals, rowIntervals);
39
+ if (intervals.length === 0)
40
+ return [];
41
+ }
42
+ return (intervals ?? []).filter(interval => interval.right - interval.left >= minSlotWidth);
43
+ }
44
+ //# sourceMappingURL=get-band-intervals-from-polygon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-band-intervals-from-polygon.js","sourceRoot":"","sources":["../../src/geometry/get-band-intervals-from-polygon.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAA;AAEpE,SAAS,iBAAiB,CAAC,EAAY;IACrC,MAAM,SAAS,GAAe,EAAE,CAAA;IAEhC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACtD,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAE,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAE,EAAE,CAAC,CAAA;IAC7D,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,qBAAqB,CAAC,OAAmB,EAAE,QAAoB;IACtE,MAAM,aAAa,GAAe,EAAE,CAAA;IACpC,IAAI,SAAS,GAAG,CAAC,CAAA;IACjB,IAAI,UAAU,GAAG,CAAC,CAAA;IAElB,OAAO,SAAS,GAAG,OAAO,CAAC,MAAM,IAAI,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;QAClE,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,CAAE,CAAA;QAChC,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAE,CAAA;QACnC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACnD,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;QAEtD,IAAI,YAAY,GAAG,WAAW,EAAE,CAAC;YAC/B,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAA;QAChE,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;YAC7B,SAAS,IAAI,CAAC,CAAA;QAChB,CAAC;aAAM,CAAC;YACN,UAAU,IAAI,CAAC,CAAA;QACjB,CAAC;IACH,CAAC;IAED,OAAO,aAAa,CAAA;AACtB,CAAC;AAED,MAAM,UAAU,2BAA2B,CACzC,MAAwB,EACxB,OAAe,EACf,UAAkB,EAClB,YAAY,GAAG,CAAC;IAEhB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACpC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAA;IAC5D,IAAI,SAAS,GAAsB,IAAI,CAAA;IAEvC,KAAK,IAAI,GAAG,GAAG,QAAQ,EAAE,GAAG,IAAI,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;QAC9C,MAAM,YAAY,GAAG,iBAAiB,CAAC,oBAAoB,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC,CAAA;QAC/E,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAA;QACxC,SAAS,GAAG,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,qBAAqB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAA;QAC9F,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAA;IACvC,CAAC;IAED,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,IAAI,IAAI,YAAY,CAAC,CAAA;AAC7F,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ShapeBounds, ShapeTextPoint } from '../types.js';
2
+ export declare function getPolygonBounds(points: ShapeTextPoint[]): ShapeBounds;
@@ -0,0 +1,18 @@
1
+ export function getPolygonBounds(points) {
2
+ if (points.length < 3) {
3
+ throw new Error('A polygon needs at least 3 points');
4
+ }
5
+ let left = Infinity;
6
+ let top = Infinity;
7
+ let right = -Infinity;
8
+ let bottom = -Infinity;
9
+ for (let index = 0; index < points.length; index++) {
10
+ const point = points[index];
11
+ left = Math.min(left, point.x);
12
+ top = Math.min(top, point.y);
13
+ right = Math.max(right, point.x);
14
+ bottom = Math.max(bottom, point.y);
15
+ }
16
+ return { left, top, right, bottom };
17
+ }
18
+ //# sourceMappingURL=get-polygon-bounds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-polygon-bounds.js","sourceRoot":"","sources":["../../src/geometry/get-polygon-bounds.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,gBAAgB,CAAC,MAAwB;IACvD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;IACtD,CAAC;IAED,IAAI,IAAI,GAAG,QAAQ,CAAA;IACnB,IAAI,GAAG,GAAG,QAAQ,CAAA;IAClB,IAAI,KAAK,GAAG,CAAC,QAAQ,CAAA;IACrB,IAAI,MAAM,GAAG,CAAC,QAAQ,CAAA;IAEtB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACnD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAE,CAAA;QAC5B,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;QAC9B,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;QAC5B,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;QAChC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;IACpC,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;AACrC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ShapeTextPoint } from '../types.js';
2
+ export declare function getXIntersectionsAtY(points: ShapeTextPoint[], y: number): number[];
@@ -0,0 +1,15 @@
1
+ export function getXIntersectionsAtY(points, y) {
2
+ const xs = [];
3
+ let previous = points[points.length - 1];
4
+ if (previous === undefined)
5
+ return xs;
6
+ for (let index = 0; index < points.length; index++) {
7
+ const current = points[index];
8
+ if ((previous.y <= y && y < current.y) || (current.y <= y && y < previous.y)) {
9
+ xs.push(previous.x + ((y - previous.y) * (current.x - previous.x)) / (current.y - previous.y));
10
+ }
11
+ previous = current;
12
+ }
13
+ return xs.sort((left, right) => left - right);
14
+ }
15
+ //# sourceMappingURL=get-x-intersections-at-y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-x-intersections-at-y.js","sourceRoot":"","sources":["../../src/geometry/get-x-intersections-at-y.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,oBAAoB,CAAC,MAAwB,EAAE,CAAS;IACtE,MAAM,EAAE,GAAa,EAAE,CAAA;IACvB,IAAI,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAExC,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,EAAE,CAAA;IAErC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACnD,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAE,CAAA;QAE9B,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7E,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;QAChG,CAAC;QAED,QAAQ,GAAG,OAAO,CAAA;IACpB,CAAC;IAED,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,CAAA;AAC/C,CAAC"}
@@ -0,0 +1,11 @@
1
+ export type { CompiledShapeBand, CompiledShapeBands, CompiledShapeRegion, CompileShapeForLayoutOptions, Interval, LayoutCursor, LayoutLineRange, LayoutTextInCompiledShapeOptions, LayoutTextInShapeOptions, PolygonShape, PreparedLayoutText, PreparedLayoutToken, RenderLayoutToSvgOptions, ResolvedShapeShadow, ResolvedShapeStyle, ResolvedTextStyle, ShapeInput, ShapeBounds, ShapeShadowInput, ShapeStyleInput, ShapeTextLayout, ShapeTextLine, ShapeTextPoint, TextMaskShape, TextMaskShapeFixedSize, TextMaskShapeFitContentSize, TextMaskShapeSize, TextMaskShapeSizeMode, TextMaskShapeTextMode, TextStyleInput, TextMeasurer, } from './types.js';
2
+ export { createCanvasTextMeasurer } from './text/create-canvas-text-measurer.js';
3
+ export { normalizeTextStyleToFont, resolveLayoutTextStyle } from './text/normalize-text-style-to-font.js';
4
+ export { prepareTextForLayout } from './text/prepare-text-for-layout.js';
5
+ export { layoutNextLineFromPreparedText } from './text/layout-next-line-from-prepared-text.js';
6
+ export { layoutNextLineFromRepeatedText } from './text/layout-next-line-from-repeated-text.js';
7
+ export { getBandIntervalsFromPolygon } from './geometry/get-band-intervals-from-polygon.js';
8
+ export { compileShapeForLayout } from './shape/compile-shape-for-layout.js';
9
+ export { layoutTextInCompiledShape } from './layout/layout-text-in-compiled-shape.js';
10
+ export { layoutTextInShape } from './layout/layout-text-in-shape.js';
11
+ export { renderLayoutToSvg } from './render/render-layout-to-svg.js';
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export { createCanvasTextMeasurer } from './text/create-canvas-text-measurer.js';
2
+ export { normalizeTextStyleToFont, resolveLayoutTextStyle } from './text/normalize-text-style-to-font.js';
3
+ export { prepareTextForLayout } from './text/prepare-text-for-layout.js';
4
+ export { layoutNextLineFromPreparedText } from './text/layout-next-line-from-prepared-text.js';
5
+ export { layoutNextLineFromRepeatedText } from './text/layout-next-line-from-repeated-text.js';
6
+ export { getBandIntervalsFromPolygon } from './geometry/get-band-intervals-from-polygon.js';
7
+ export { compileShapeForLayout } from './shape/compile-shape-for-layout.js';
8
+ export { layoutTextInCompiledShape } from './layout/layout-text-in-compiled-shape.js';
9
+ export { layoutTextInShape } from './layout/layout-text-in-shape.js';
10
+ export { renderLayoutToSvg } from './render/render-layout-to-svg.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAkCA,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAA;AAChF,OAAO,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAA;AACzG,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAA;AACxE,OAAO,EAAE,8BAA8B,EAAE,MAAM,+CAA+C,CAAA;AAC9F,OAAO,EAAE,8BAA8B,EAAE,MAAM,+CAA+C,CAAA;AAC9F,OAAO,EAAE,2BAA2B,EAAE,MAAM,+CAA+C,CAAA;AAC3F,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAA;AAC3E,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAA;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAA"}
@@ -0,0 +1,23 @@
1
+ import type { CompiledShapeBands, PreparedLayoutToken, ShapeTextLine } from '../types.js';
2
+ export type DenseFillOccupiedRect = {
3
+ left: number;
4
+ right: number;
5
+ top: number;
6
+ bottom: number;
7
+ };
8
+ type LayoutDenseFillPassOptions = {
9
+ compiledShape: CompiledShapeBands;
10
+ densePattern: PreparedLayoutToken;
11
+ startOffset: number;
12
+ align: 'left' | 'center';
13
+ baselineRatio: number;
14
+ allSlots: boolean;
15
+ font?: string;
16
+ fillPass?: 1 | 2;
17
+ };
18
+ export declare function layoutDenseFillPass(options: LayoutDenseFillPassOptions): {
19
+ lines: ShapeTextLine[];
20
+ endOffset: number;
21
+ occupiedRects: DenseFillOccupiedRect[];
22
+ };
23
+ export {};
@@ -0,0 +1,64 @@
1
+ import { layoutNextLineFromDenseRepeatedText } from '../text/layout-next-line-from-dense-repeated-text.js';
2
+ function pickWidestInterval(intervals) {
3
+ let best = intervals[0];
4
+ for (let index = 1; index < intervals.length; index++) {
5
+ const candidate = intervals[index];
6
+ if (candidate.right - candidate.left > best.right - best.left) {
7
+ best = candidate;
8
+ }
9
+ }
10
+ return best;
11
+ }
12
+ function getOrderedSlots(intervals, allSlots) {
13
+ if (intervals.length === 0) {
14
+ return [];
15
+ }
16
+ if (!allSlots) {
17
+ return [pickWidestInterval(intervals)];
18
+ }
19
+ return [...intervals].sort((left, right) => left.left - right.left);
20
+ }
21
+ export function layoutDenseFillPass(options) {
22
+ const lines = [];
23
+ const occupiedRects = [];
24
+ let offset = options.startOffset;
25
+ for (let bandIndex = 0; bandIndex < options.compiledShape.bands.length; bandIndex++) {
26
+ const band = options.compiledShape.bands[bandIndex];
27
+ const slots = getOrderedSlots(band.intervals, options.allSlots);
28
+ for (let slotIndex = 0; slotIndex < slots.length; slotIndex++) {
29
+ const slot = slots[slotIndex];
30
+ const line = layoutNextLineFromDenseRepeatedText(options.densePattern, offset, slot.right - slot.left);
31
+ if (line === null) {
32
+ continue;
33
+ }
34
+ const x = options.align === 'center'
35
+ ? slot.left + Math.max(0, (slot.right - slot.left - line.width) / 2)
36
+ : slot.left;
37
+ const occupiedRight = Math.min(slot.right, x + Math.max(0, line.width));
38
+ lines.push({
39
+ ...line,
40
+ x,
41
+ top: band.top,
42
+ baseline: band.top + options.compiledShape.bandHeight * options.baselineRatio,
43
+ slot,
44
+ font: options.font,
45
+ fillPass: options.fillPass,
46
+ });
47
+ if (occupiedRight > x) {
48
+ occupiedRects.push({
49
+ left: x,
50
+ right: occupiedRight,
51
+ top: band.top,
52
+ bottom: band.bottom,
53
+ });
54
+ }
55
+ offset = line.end.tokenIndex;
56
+ }
57
+ }
58
+ return {
59
+ lines,
60
+ endOffset: offset,
61
+ occupiedRects,
62
+ };
63
+ }
64
+ //# sourceMappingURL=layout-dense-fill-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout-dense-fill-pass.js","sourceRoot":"","sources":["../../src/layout/layout-dense-fill-pass.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mCAAmC,EAAE,MAAM,sDAAsD,CAAA;AAoB1G,SAAS,kBAAkB,CAAC,SAAqB;IAC/C,IAAI,IAAI,GAAG,SAAS,CAAC,CAAC,CAAE,CAAA;IAExB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACtD,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAE,CAAA;QACnC,IAAI,SAAS,CAAC,KAAK,GAAG,SAAS,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC9D,IAAI,GAAG,SAAS,CAAA;QAClB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,eAAe,CAAC,SAAqB,EAAE,QAAiB;IAC/D,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,CAAA;IACX,CAAC;IAED,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAA;IACxC,CAAC;IAED,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAA;AACrE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAmC;IAKrE,MAAM,KAAK,GAAoB,EAAE,CAAA;IACjC,MAAM,aAAa,GAA4B,EAAE,CAAA;IACjD,IAAI,MAAM,GAAG,OAAO,CAAC,WAAW,CAAA;IAEhC,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC;QACpF,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,CAAE,CAAA;QACpD,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;QAE/D,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC;YAC9D,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAE,CAAA;YAC9B,MAAM,IAAI,GAAG,mCAAmC,CAC9C,OAAO,CAAC,YAAY,EACpB,MAAM,EACN,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,CACvB,CAAA;YAED,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,SAAQ;YACV,CAAC;YAED,MAAM,CAAC,GACL,OAAO,CAAC,KAAK,KAAK,QAAQ;gBACxB,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBACpE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAA;YACf,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;YAEvE,KAAK,CAAC,IAAI,CAAC;gBACT,GAAG,IAAI;gBACP,CAAC;gBACD,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,QAAQ,EAAE,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,aAAa,CAAC,UAAU,GAAG,OAAO,CAAC,aAAa;gBAC7E,IAAI;gBACJ,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;aAC3B,CAAC,CAAA;YAEF,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;gBACtB,aAAa,CAAC,IAAI,CAAC;oBACjB,IAAI,EAAE,CAAC;oBACP,KAAK,EAAE,aAAa;oBACpB,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,MAAM,EAAE,IAAI,CAAC,MAAM;iBACpB,CAAC,CAAA;YACJ,CAAC;YAED,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAA;QAC9B,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK;QACL,SAAS,EAAE,MAAM;QACjB,aAAa;KACd,CAAA;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { CompiledShapeBands, LayoutCursor, PreparedLayoutText, ShapeTextLine } from '../types.js';
2
+ type LayoutFlowLinesInCompiledShapeOptions = {
3
+ compiledShape: CompiledShapeBands;
4
+ prepared: PreparedLayoutText;
5
+ align: 'left' | 'center';
6
+ baselineRatio: number;
7
+ startCursor: LayoutCursor;
8
+ };
9
+ export declare function layoutFlowLinesInCompiledShape(options: LayoutFlowLinesInCompiledShapeOptions): {
10
+ lines: ShapeTextLine[];
11
+ endCursor: LayoutCursor;
12
+ };
13
+ export {};
@@ -0,0 +1,42 @@
1
+ import { layoutNextLineFromPreparedText } from '../text/layout-next-line-from-prepared-text.js';
2
+ function pickWidestInterval(intervals) {
3
+ let best = intervals[0];
4
+ for (let index = 1; index < intervals.length; index++) {
5
+ const candidate = intervals[index];
6
+ if (candidate.right - candidate.left > best.right - best.left) {
7
+ best = candidate;
8
+ }
9
+ }
10
+ return best;
11
+ }
12
+ export function layoutFlowLinesInCompiledShape(options) {
13
+ const lines = [];
14
+ let cursor = options.startCursor;
15
+ for (let index = 0; index < options.compiledShape.bands.length; index++) {
16
+ const band = options.compiledShape.bands[index];
17
+ if (band.intervals.length === 0) {
18
+ continue;
19
+ }
20
+ const slot = pickWidestInterval(band.intervals);
21
+ const line = layoutNextLineFromPreparedText(options.prepared, cursor, slot.right - slot.left);
22
+ if (line === null) {
23
+ break;
24
+ }
25
+ const x = options.align === 'center'
26
+ ? slot.left + Math.max(0, (slot.right - slot.left - line.width) / 2)
27
+ : slot.left;
28
+ lines.push({
29
+ ...line,
30
+ x,
31
+ top: band.top,
32
+ baseline: band.top + options.compiledShape.bandHeight * options.baselineRatio,
33
+ slot,
34
+ });
35
+ cursor = line.end;
36
+ }
37
+ return {
38
+ lines,
39
+ endCursor: cursor,
40
+ };
41
+ }
42
+ //# sourceMappingURL=layout-flow-lines-in-compiled-shape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout-flow-lines-in-compiled-shape.js","sourceRoot":"","sources":["../../src/layout/layout-flow-lines-in-compiled-shape.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,8BAA8B,EAAE,MAAM,gDAAgD,CAAA;AAE/F,SAAS,kBAAkB,CAAC,SAA2D;IACrF,IAAI,IAAI,GAAG,SAAS,CAAC,CAAC,CAAE,CAAA;IAExB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACtD,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAE,CAAA;QACnC,IAAI,SAAS,CAAC,KAAK,GAAG,SAAS,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC9D,IAAI,GAAG,SAAS,CAAA;QAClB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAUD,MAAM,UAAU,8BAA8B,CAC5C,OAA8C;IAK9C,MAAM,KAAK,GAAoB,EAAE,CAAA;IACjC,IAAI,MAAM,GAAiB,OAAO,CAAC,WAAW,CAAA;IAE9C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACxE,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,CAAE,CAAA;QAChD,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChC,SAAQ;QACV,CAAC;QAED,MAAM,IAAI,GAAG,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAC/C,MAAM,IAAI,GAAG,8BAA8B,CACzC,OAAO,CAAC,QAAQ,EAChB,MAAM,EACN,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,CACvB,CAAA;QAED,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,MAAK;QACP,CAAC;QAED,MAAM,CAAC,GACL,OAAO,CAAC,KAAK,KAAK,QAAQ;YACxB,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACpE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAA;QAEf,KAAK,CAAC,IAAI,CAAC;YACT,GAAG,IAAI;YACP,CAAC;YACD,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,QAAQ,EAAE,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,aAAa,CAAC,UAAU,GAAG,OAAO,CAAC,aAAa;YAC7E,IAAI;SACL,CAAC,CAAA;QAEF,MAAM,GAAG,IAAI,CAAC,GAAG,CAAA;IACnB,CAAC;IAED,OAAO;QACL,KAAK;QACL,SAAS,EAAE,MAAM;KAClB,CAAA;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { LayoutTextInCompiledShapeOptions, ShapeTextLayout } from '../types.js';
2
+ export declare function layoutTextInCompiledShape(options: LayoutTextInCompiledShapeOptions): ShapeTextLayout;
@@ -0,0 +1,13 @@
1
+ import { resolveFlowLayout } from './resolve-flow-layout.js';
2
+ import { resolveMaxFillLayout } from './resolve-max-fill-layout.js';
3
+ import { hasSequentialShapeRegions, resolveSequentialShapeRegions, } from './resolve-sequential-shape-regions.js';
4
+ export function layoutTextInCompiledShape(options) {
5
+ if (hasSequentialShapeRegions(options.compiledShape)) {
6
+ return resolveSequentialShapeRegions(options);
7
+ }
8
+ if (options.autoFill === true) {
9
+ return resolveMaxFillLayout(options);
10
+ }
11
+ return resolveFlowLayout(options);
12
+ }
13
+ //# sourceMappingURL=layout-text-in-compiled-shape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout-text-in-compiled-shape.js","sourceRoot":"","sources":["../../src/layout/layout-text-in-compiled-shape.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AACnE,OAAO,EACL,yBAAyB,EACzB,6BAA6B,GAC9B,MAAM,uCAAuC,CAAA;AAE9C,MAAM,UAAU,yBAAyB,CACvC,OAAyC;IAEzC,IAAI,yBAAyB,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QACrD,OAAO,6BAA6B,CAAC,OAAO,CAAC,CAAA;IAC/C,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC9B,OAAO,oBAAoB,CAAC,OAAO,CAAC,CAAA;IACtC,CAAC;IAED,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAA;AACnC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { LayoutTextInShapeOptions, ShapeTextLayout } from '../types.js';
2
+ export declare function layoutTextInShape(options: LayoutTextInShapeOptions): ShapeTextLayout;
@@ -0,0 +1,40 @@
1
+ import { compileShapeForLayout } from '../shape/compile-shape-for-layout.js';
2
+ import { layoutTextInCompiledShape } from './layout-text-in-compiled-shape.js';
3
+ function resolveCompileMinSlotWidth(options) {
4
+ if (options.autoFill !== true) {
5
+ return options.minSlotWidth;
6
+ }
7
+ if (options.minSlotWidth !== undefined) {
8
+ return options.minSlotWidth;
9
+ }
10
+ return Math.max(6, Math.round(options.lineHeight * 0.45));
11
+ }
12
+ export function layoutTextInShape(options) {
13
+ const compiledShape = compileShapeForLayout({
14
+ shape: options.shape,
15
+ lineHeight: options.lineHeight,
16
+ minSlotWidth: resolveCompileMinSlotWidth(options),
17
+ });
18
+ if (options.textStyle !== undefined) {
19
+ return layoutTextInCompiledShape({
20
+ text: options.text,
21
+ font: options.font,
22
+ textStyle: options.textStyle,
23
+ compiledShape,
24
+ measurer: options.measurer,
25
+ align: options.align,
26
+ baselineRatio: options.baselineRatio,
27
+ autoFill: options.autoFill,
28
+ });
29
+ }
30
+ return layoutTextInCompiledShape({
31
+ text: options.text,
32
+ font: options.font,
33
+ compiledShape,
34
+ measurer: options.measurer,
35
+ align: options.align,
36
+ baselineRatio: options.baselineRatio,
37
+ autoFill: options.autoFill,
38
+ });
39
+ }
40
+ //# sourceMappingURL=layout-text-in-shape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout-text-in-shape.js","sourceRoot":"","sources":["../../src/layout/layout-text-in-shape.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAC5E,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAA;AAE9E,SAAS,0BAA0B,CAAC,OAAiC;IACnE,IAAI,OAAO,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC9B,OAAO,OAAO,CAAC,YAAY,CAAA;IAC7B,CAAC;IAED,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACvC,OAAO,OAAO,CAAC,YAAY,CAAA;IAC7B,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAA;AAC3D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,OAAiC;IACjE,MAAM,aAAa,GAAG,qBAAqB,CAAC;QAC1C,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,YAAY,EAAE,0BAA0B,CAAC,OAAO,CAAC;KAClD,CAAC,CAAA;IAEF,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QACpC,OAAO,yBAAyB,CAAC;YAC/B,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,aAAa;YACb,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,yBAAyB,CAAC;QAC/B,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,OAAO,CAAC,IAAK;QACnB,aAAa;QACb,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { LayoutTextInCompiledShapeOptions, ShapeTextLayout } from '../types.js';
2
+ export declare function resolveFlowLayout(options: LayoutTextInCompiledShapeOptions): ShapeTextLayout;