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.
- package/LICENSE +21 -0
- package/README.md +264 -0
- package/dist/geometry/get-band-intervals-from-polygon.d.ts +2 -0
- package/dist/geometry/get-band-intervals-from-polygon.js +44 -0
- package/dist/geometry/get-band-intervals-from-polygon.js.map +1 -0
- package/dist/geometry/get-polygon-bounds.d.ts +2 -0
- package/dist/geometry/get-polygon-bounds.js +18 -0
- package/dist/geometry/get-polygon-bounds.js.map +1 -0
- package/dist/geometry/get-x-intersections-at-y.d.ts +2 -0
- package/dist/geometry/get-x-intersections-at-y.js +15 -0
- package/dist/geometry/get-x-intersections-at-y.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/layout-dense-fill-pass.d.ts +23 -0
- package/dist/layout/layout-dense-fill-pass.js +64 -0
- package/dist/layout/layout-dense-fill-pass.js.map +1 -0
- package/dist/layout/layout-flow-lines-in-compiled-shape.d.ts +13 -0
- package/dist/layout/layout-flow-lines-in-compiled-shape.js +42 -0
- package/dist/layout/layout-flow-lines-in-compiled-shape.js.map +1 -0
- package/dist/layout/layout-text-in-compiled-shape.d.ts +2 -0
- package/dist/layout/layout-text-in-compiled-shape.js +13 -0
- package/dist/layout/layout-text-in-compiled-shape.js.map +1 -0
- package/dist/layout/layout-text-in-shape.d.ts +2 -0
- package/dist/layout/layout-text-in-shape.js +40 -0
- package/dist/layout/layout-text-in-shape.js.map +1 -0
- package/dist/layout/resolve-flow-layout.d.ts +2 -0
- package/dist/layout/resolve-flow-layout.js +31 -0
- package/dist/layout/resolve-flow-layout.js.map +1 -0
- package/dist/layout/resolve-max-fill-helpers.d.ts +3 -0
- package/dist/layout/resolve-max-fill-helpers.js +18 -0
- package/dist/layout/resolve-max-fill-helpers.js.map +1 -0
- package/dist/layout/resolve-max-fill-layout.d.ts +2 -0
- package/dist/layout/resolve-max-fill-layout.js +34 -0
- package/dist/layout/resolve-max-fill-layout.js.map +1 -0
- package/dist/layout/resolve-sequential-shape-regions.d.ts +3 -0
- package/dist/layout/resolve-sequential-shape-regions.js +95 -0
- package/dist/layout/resolve-sequential-shape-regions.js.map +1 -0
- package/dist/render/escape-xml-text.d.ts +1 -0
- package/dist/render/escape-xml-text.js +9 -0
- package/dist/render/escape-xml-text.js.map +1 -0
- package/dist/render/normalize-shape-decoration.d.ts +2 -0
- package/dist/render/normalize-shape-decoration.js +41 -0
- package/dist/render/normalize-shape-decoration.js.map +1 -0
- package/dist/render/render-layout-to-svg.d.ts +2 -0
- package/dist/render/render-layout-to-svg.js +88 -0
- package/dist/render/render-layout-to-svg.js.map +1 -0
- package/dist/render/render-svg-shadow-filter.d.ts +10 -0
- package/dist/render/render-svg-shadow-filter.js +16 -0
- package/dist/render/render-svg-shadow-filter.js.map +1 -0
- package/dist/shape/build-text-mask-bands-from-alpha.d.ts +8 -0
- package/dist/shape/build-text-mask-bands-from-alpha.js +73 -0
- package/dist/shape/build-text-mask-bands-from-alpha.js.map +1 -0
- package/dist/shape/compile-polygon-shape-for-layout.d.ts +2 -0
- package/dist/shape/compile-polygon-shape-for-layout.js +26 -0
- package/dist/shape/compile-polygon-shape-for-layout.js.map +1 -0
- package/dist/shape/compile-shape-for-layout.d.ts +2 -0
- package/dist/shape/compile-shape-for-layout.js +18 -0
- package/dist/shape/compile-shape-for-layout.js.map +1 -0
- package/dist/shape/compile-text-mask-shape-for-layout.d.ts +2 -0
- package/dist/shape/compile-text-mask-shape-for-layout.js +162 -0
- package/dist/shape/compile-text-mask-shape-for-layout.js.map +1 -0
- package/dist/shape/render-text-mask-raster.d.ts +13 -0
- package/dist/shape/render-text-mask-raster.js +29 -0
- package/dist/shape/render-text-mask-raster.js.map +1 -0
- package/dist/shape/resolve-text-mask-shape-size.d.ts +18 -0
- package/dist/shape/resolve-text-mask-shape-size.js +67 -0
- package/dist/shape/resolve-text-mask-shape-size.js.map +1 -0
- package/dist/shape/segment-text-mask-graphemes.d.ts +6 -0
- package/dist/shape/segment-text-mask-graphemes.js +20 -0
- package/dist/shape/segment-text-mask-graphemes.js.map +1 -0
- package/dist/text/create-browser-canvas-2d-context.d.ts +2 -0
- package/dist/text/create-browser-canvas-2d-context.js +21 -0
- package/dist/text/create-browser-canvas-2d-context.js.map +1 -0
- package/dist/text/create-canvas-text-measurer.d.ts +2 -0
- package/dist/text/create-canvas-text-measurer.js +11 -0
- package/dist/text/create-canvas-text-measurer.js.map +1 -0
- package/dist/text/layout-next-line-from-dense-repeated-text.d.ts +2 -0
- package/dist/text/layout-next-line-from-dense-repeated-text.js +63 -0
- package/dist/text/layout-next-line-from-dense-repeated-text.js.map +1 -0
- package/dist/text/layout-next-line-from-prepared-text.d.ts +2 -0
- package/dist/text/layout-next-line-from-prepared-text.js +57 -0
- package/dist/text/layout-next-line-from-prepared-text.js.map +1 -0
- package/dist/text/layout-next-line-from-repeated-text.d.ts +2 -0
- package/dist/text/layout-next-line-from-repeated-text.js +51 -0
- package/dist/text/layout-next-line-from-repeated-text.js.map +1 -0
- package/dist/text/layout-text-line-helpers.d.ts +4 -0
- package/dist/text/layout-text-line-helpers.js +17 -0
- package/dist/text/layout-text-line-helpers.js.map +1 -0
- package/dist/text/normalize-text-style-to-font.d.ts +6 -0
- package/dist/text/normalize-text-style-to-font.js +31 -0
- package/dist/text/normalize-text-style-to-font.js.map +1 -0
- package/dist/text/prepare-dense-repeat-fill-pattern.d.ts +2 -0
- package/dist/text/prepare-dense-repeat-fill-pattern.js +13 -0
- package/dist/text/prepare-dense-repeat-fill-pattern.js.map +1 -0
- package/dist/text/prepare-stream-repeat-fill-pattern.d.ts +2 -0
- package/dist/text/prepare-stream-repeat-fill-pattern.js +16 -0
- package/dist/text/prepare-stream-repeat-fill-pattern.js.map +1 -0
- package/dist/text/prepare-text-for-layout.d.ts +2 -0
- package/dist/text/prepare-text-for-layout.js +22 -0
- package/dist/text/prepare-text-for-layout.js.map +1 -0
- package/dist/text/segment-text-for-layout.d.ts +3 -0
- package/dist/text/segment-text-for-layout.js +43 -0
- package/dist/text/segment-text-for-layout.js.map +1 -0
- package/dist/types.d.ts +200 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- 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,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,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,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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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,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,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"}
|