@unmade/text-renderer 1.1.0 → 1.2.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/README.md +184 -0
- package/dist/__vite-browser-external-oLchLuMx.js +7 -0
- package/dist/baselines/index.d.ts +59 -0
- package/dist/ce-adapter/index.cjs +1 -1
- package/dist/ce-adapter/index.js +154 -152
- package/dist/constants.d.ts +5 -0
- package/dist/fitting/index.d.ts +86 -0
- package/dist/glyph/svg-glyph-converter.d.ts +42 -0
- package/dist/helpers/bbox.d.ts +34 -0
- package/dist/helpers/point.d.ts +17 -0
- package/dist/helpers/xml.d.ts +1 -0
- package/dist/index.d.ts +17 -249
- package/dist/index.js +2221 -2005
- package/dist/loading/createLoadedFont.d.ts +53 -0
- package/dist/loading/index.d.ts +4 -0
- package/dist/measuring/conversion.d.ts +2 -0
- package/dist/measuring/index.d.ts +28 -0
- package/dist/node/baselines/index.d.ts +59 -0
- package/dist/node/ce-adapter/ce-types.d.ts +171 -0
- package/dist/node/ce-adapter/extractTextPlacements.d.ts +11 -0
- package/dist/node/ce-adapter/index.d.ts +12 -0
- package/dist/node/ce-adapter/mapStateToRendererOptions.d.ts +13 -0
- package/dist/node/ce-adapter/types.d.ts +56 -0
- package/dist/node/constants.d.ts +5 -0
- package/dist/node/fitting/index.d.ts +86 -0
- package/dist/node/glyph/svg-glyph-converter.d.ts +42 -0
- package/dist/node/helpers/bbox.d.ts +34 -0
- package/dist/node/helpers/point.d.ts +17 -0
- package/dist/node/helpers/xml.d.ts +1 -0
- package/dist/node/index.d.ts +17 -249
- package/dist/node/index.js +940 -948
- package/dist/node/loading/createLoadedFont.d.ts +53 -0
- package/dist/node/loading/index.d.ts +4 -0
- package/dist/node/measuring/conversion.d.ts +2 -0
- package/dist/node/measuring/index.d.ts +28 -0
- package/dist/node/renderText.d.ts +5 -0
- package/dist/node/rendering/index.d.ts +42 -0
- package/dist/node/svg/index.d.ts +11 -0
- package/dist/node/types.d.ts +75 -0
- package/dist/renderText.d.ts +5 -0
- package/dist/rendering/index.d.ts +42 -0
- package/dist/rolldown-runtime-DtPi1Y-2.js +13 -0
- package/dist/svg/index.d.ts +11 -0
- package/dist/types.d.ts +75 -0
- package/dist/worker/baselines/index.d.ts +59 -0
- package/dist/worker/ce-adapter/ce-types.d.ts +171 -0
- package/dist/worker/ce-adapter/extractTextPlacements.d.ts +11 -0
- package/dist/worker/ce-adapter/index.d.ts +12 -0
- package/dist/worker/ce-adapter/mapStateToRendererOptions.d.ts +13 -0
- package/dist/worker/ce-adapter/types.d.ts +56 -0
- package/dist/worker/constants.d.ts +5 -0
- package/dist/worker/fitting/index.d.ts +86 -0
- package/dist/worker/glyph/svg-glyph-converter.d.ts +42 -0
- package/dist/worker/helpers/bbox.d.ts +34 -0
- package/dist/worker/helpers/point.d.ts +17 -0
- package/dist/worker/helpers/xml.d.ts +1 -0
- package/dist/worker/index.d.ts +17 -249
- package/dist/worker/index.js +8346 -9728
- package/dist/worker/loading/createLoadedFont.d.ts +53 -0
- package/dist/worker/loading/index.d.ts +4 -0
- package/dist/worker/measuring/conversion.d.ts +2 -0
- package/dist/worker/measuring/index.d.ts +28 -0
- package/dist/worker/renderText.d.ts +5 -0
- package/dist/worker/rendering/index.d.ts +42 -0
- package/dist/worker/svg/index.d.ts +11 -0
- package/dist/worker/types.d.ts +75 -0
- package/package.json +19 -11
- package/dist/__vite-browser-external-l0sNRNKZ.js +0 -1
package/README.md
CHANGED
|
@@ -46,6 +46,107 @@ const { getRenderedText } = await import('@unmade/text-renderer/node');
|
|
|
46
46
|
|
|
47
47
|
**CE Lambda integration**: When wiring text-renderer into the Configuration Engine Lambda, use the `/node` ESM entry (`@unmade/text-renderer/node`) from an ESM Lambda handler, or use the `await import()` pattern above from an existing CJS handler. The `ce-adapter` sub-package (`@unmade/text-renderer/ce-adapter`) retains a CJS build and can be `require()`'d normally.
|
|
48
48
|
|
|
49
|
+
## Choosing a font size
|
|
50
|
+
|
|
51
|
+
`getRenderedText` draws text at a size you give it. These work out what that size should be.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { getFontSizeToFitBox, loadFont } from '@unmade/text-renderer';
|
|
55
|
+
|
|
56
|
+
const font = await loadFont('https://example.com/font.ttf');
|
|
57
|
+
|
|
58
|
+
const { fontSize, fits, boundBy } = getFontSizeToFitBox({
|
|
59
|
+
font,
|
|
60
|
+
text: 'HELLO',
|
|
61
|
+
boxDimensions: {
|
|
62
|
+
width: 500,
|
|
63
|
+
height: 200,
|
|
64
|
+
physicalWidth: 100,
|
|
65
|
+
physicalHeight: 40,
|
|
66
|
+
physicalUnits: 'mm',
|
|
67
|
+
},
|
|
68
|
+
size: { mode: 'fitToBox', minFontSize: 20, maxFontSize: 200 },
|
|
69
|
+
spacing: { outlineWidth: 0, letterSpacing: 0, letterSpacingOutline: 0 },
|
|
70
|
+
baseline: 'flat', // or 'curved', or { type: 'custom', path: '...' }
|
|
71
|
+
verticalAlignment: 'centre', // affects where a curved baseline sits
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
For flat and custom baselines this is a direct calculation rather than a search: glyph
|
|
76
|
+
widths, outline stroke and letter spacing are all `fontSize * constant`, so measuring
|
|
77
|
+
once at a reference size and solving for a scale factor is exact. Curved baselines are
|
|
78
|
+
bisected instead, against both limits at once: neither the arc's length nor the arch's
|
|
79
|
+
position is proportional to font size, so both are evaluated at each candidate size.
|
|
80
|
+
|
|
81
|
+
The height check asks where the text will actually land, not how tall it is. The arch is
|
|
82
|
+
positioned from a standard capital, so that its shape does not change as characters are
|
|
83
|
+
typed — which means a descender hangs below the arch's ends, outside a box the arch itself
|
|
84
|
+
sits comfortably inside. The check reads the arch's placement from the same function that
|
|
85
|
+
generates the baseline path, so a size it accepts is one the renderer can draw.
|
|
86
|
+
|
|
87
|
+
### The three ways to decide a size
|
|
88
|
+
|
|
89
|
+
`size` covers each of them, so none is a special case:
|
|
90
|
+
|
|
91
|
+
| `size` | Meaning |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `{ mode: 'fitToBox', minFontSize, maxFontSize }` | Solve for the largest size that fits the box |
|
|
94
|
+
| `{ mode: 'fontSize', fontSize }` | Pin a size in pixels |
|
|
95
|
+
| `{ mode: 'physicalSize', physicalSize: [60, 'mm'] }` | Pin a physical size — what a fixed size preset is |
|
|
96
|
+
|
|
97
|
+
`minFontSize` and `maxFontSize` belong only to `fitToBox`, because they only mean
|
|
98
|
+
anything while solving. A pinned size overrides them by design.
|
|
99
|
+
|
|
100
|
+
Pinning inverts the problem. Instead of the text being fixed and the size free, the
|
|
101
|
+
size is fixed and the **text length** becomes the free variable — hold 60mm, and fit as
|
|
102
|
+
many characters as will go.
|
|
103
|
+
|
|
104
|
+
### Reading the result
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
interface FitTextToBoxResult {
|
|
108
|
+
fontSize: number;
|
|
109
|
+
fits: boolean; // false when it won't fit even at the smallest allowed size
|
|
110
|
+
boundBy: 'width' | 'height' | 'requestedSize';
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`boundBy` says which limit decided the outcome — when it fits, what capped the size;
|
|
115
|
+
when it doesn't, what couldn't be satisfied. Callers generally need to respond
|
|
116
|
+
differently to each: text too wide for its box and text too tall for its box call for
|
|
117
|
+
different remedies, and in the configuration engine they surface as different messages.
|
|
118
|
+
|
|
119
|
+
`requestedSize` means the box wasn't the limiting factor: either `maxFontSize` was
|
|
120
|
+
reached while solving, or a pinned size was simply granted.
|
|
121
|
+
|
|
122
|
+
### Shortening text that won't fit
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { fitAndTruncateTextToBox } from '@unmade/text-renderer';
|
|
126
|
+
|
|
127
|
+
const result = fitAndTruncateTextToBox({ font, text, boxDimensions, size, spacing });
|
|
128
|
+
|
|
129
|
+
result.text; // possibly shortened
|
|
130
|
+
result.truncated; // whether anything was removed
|
|
131
|
+
result.linesDropped; // whole lines removed
|
|
132
|
+
result.charactersRemoved; // trailing characters removed
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Whole lines are dropped before characters are trimmed. Both counts are reported rather
|
|
136
|
+
than a single mode, because one call can do both — drop a line, then find the remainder
|
|
137
|
+
still needs trimming — and because the caller usually wants to say something different
|
|
138
|
+
about each.
|
|
139
|
+
|
|
140
|
+
Works in every `size` mode. With a pinned size it is the only thing that can give: the
|
|
141
|
+
size stays put and the text shortens.
|
|
142
|
+
|
|
143
|
+
### Rotation
|
|
144
|
+
|
|
145
|
+
Deliberately absent. Pass the box you want text laid out in and apply rotation to the
|
|
146
|
+
rendered output. Rotating the frame rather than reasoning about rotation inside the
|
|
147
|
+
solver keeps it working in one coordinate space, and is also what lets a rotated tall
|
|
148
|
+
placement use its long side for the baseline.
|
|
149
|
+
|
|
49
150
|
## Configuration Engine adapter
|
|
50
151
|
|
|
51
152
|
The `ce-adapter` sub-package bridges CE (Configuration Engine) editor state to renderer options:
|
|
@@ -88,6 +189,89 @@ comparisons/{designUrlHash}/{timestamp}/{placementId}/
|
|
|
88
189
|
renderer-options.json # GetRenderedTextOptions (mapped TR input)
|
|
89
190
|
```
|
|
90
191
|
|
|
192
|
+
## Platform bundle suites
|
|
193
|
+
|
|
194
|
+
The package builds a separate bundle per platform — browser, worker and node —
|
|
195
|
+
each resolving a different variant of `@unmade/platform`:
|
|
196
|
+
|
|
197
|
+
| | `DOMPoint` | `DOMMatrix` | `DOMParser` |
|
|
198
|
+
|---|---|---|---|
|
|
199
|
+
| node | polyfill | `@thednp` polyfill | linkedom |
|
|
200
|
+
| worker | **native** | `@thednp` polyfill | linkedom/worker |
|
|
201
|
+
| browser | native | native | native |
|
|
202
|
+
|
|
203
|
+
`__tests__/platform/` tests those built bundles, each in the host it ships into:
|
|
204
|
+
the node bundle in the test process, the browser bundle on a page, and the worker
|
|
205
|
+
bundle inside a real `DedicatedWorkerGlobalScope`. Nothing else runs them — the
|
|
206
|
+
unit suite tests `src` under node, where neither `DOMPoint` nor `DOMMatrix`
|
|
207
|
+
exists, so it can only ever exercise the polyfills.
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npm run test:platform
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
It builds first, because it loads `dist` and a stale bundle would otherwise give
|
|
214
|
+
a green run against code that is no longer there. It needs a browser:
|
|
215
|
+
`npx playwright install chromium`.
|
|
216
|
+
|
|
217
|
+
### The two suites
|
|
218
|
+
|
|
219
|
+
**`render.test.ts`** draws every scenario in `scenarios.ts` on all three bundles,
|
|
220
|
+
through both public render entry points — `getRenderedText`, which loads its own
|
|
221
|
+
font, and `renderText`, which takes one already loaded. Each render is rasterised
|
|
222
|
+
here, by one resvg call for all three, and checked twice:
|
|
223
|
+
|
|
224
|
+
- **pixel-identical to the node bundle's, with no tolerance.** All three go
|
|
225
|
+
through the same rasteriser in the same process, so nothing legitimate can move
|
|
226
|
+
a pixel. This is the assertion that the differing geometry and DOM
|
|
227
|
+
implementations do not reach the artwork.
|
|
228
|
+
- **within 1% of a committed reference** in `platform/references/`, so a change
|
|
229
|
+
that shifts all three together is caught as well.
|
|
230
|
+
|
|
231
|
+
**`api.test.ts`** covers the rest of the public API, which nothing else would
|
|
232
|
+
call on the browser or worker bundles: constants, baseline generation and
|
|
233
|
+
validation, measurement, font loading and the font cache. Each is called with
|
|
234
|
+
identical inputs on all three bundles and the results compared. It also asserts
|
|
235
|
+
the three export the same names, that every export has a probe, and that each
|
|
236
|
+
bundle ran in the host it is built for — a `typeof DOMPoint` check cannot tell a
|
|
237
|
+
page from a worker, since both have the native globals.
|
|
238
|
+
|
|
239
|
+
### The report
|
|
240
|
+
|
|
241
|
+
Every run writes `__tests__/platform-output/report.html`: one self-contained file
|
|
242
|
+
with each scenario's committed reference beside what each bundle drew, badged
|
|
243
|
+
with both comparisons, plus the diff images and the browser's user agent. It is
|
|
244
|
+
gitignored and collected as a CI artifact from `platform-test` whether the run
|
|
245
|
+
passed or failed, so a by-eye check is always available. The SVG each bundle
|
|
246
|
+
produced is written alongside it, so a difference the report shows as pixels can
|
|
247
|
+
be diffed as text.
|
|
248
|
+
|
|
249
|
+
### Fonts
|
|
250
|
+
|
|
251
|
+
Served from the test process, so every platform fetches byte-identical bytes from
|
|
252
|
+
the same URL and the font is never a variable. Three, chosen for what they
|
|
253
|
+
exercise:
|
|
254
|
+
|
|
255
|
+
- **Bangers** — irregular display face: curves, diagonals, varying stroke widths.
|
|
256
|
+
Caps only.
|
|
257
|
+
- **Lora** — high-contrast serif with true lowercase, so descenders feed the
|
|
258
|
+
baseline and line-height calculation.
|
|
259
|
+
- **giants-color** — glyphs stored as SVG documents, the only path that goes
|
|
260
|
+
through a `DOMParser` and back out through an `XMLSerializer`.
|
|
261
|
+
|
|
262
|
+
The two added faces are OFL, with their licences beside them in
|
|
263
|
+
`__tests__/fixtures/`.
|
|
264
|
+
|
|
265
|
+
### Updating the references
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
UPDATE_FIXTURES=true npm run test:platform
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Rewrites every reference from the node bundle's render, then still checks the
|
|
272
|
+
other two against what it just stored. Review the diffs in the report before
|
|
273
|
+
committing.
|
|
274
|
+
|
|
91
275
|
## Regression tests
|
|
92
276
|
|
|
93
277
|
Regression tests live in `__tests__/regression.test.ts` and use fixture pairs in `__tests__/fixtures/regression/`:
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { BaselineConfig, BaselineType, BoxDimensions, VerticalTextAlignmentValues } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Normalise a baseline option (string shorthand or object) into a BaselineConfig.
|
|
4
|
+
*/
|
|
5
|
+
export declare const normaliseBaselineOption: (baseline: BaselineType | BaselineConfig | undefined) => BaselineConfig;
|
|
6
|
+
/**
|
|
7
|
+
* Validate a custom baseline SVG path string.
|
|
8
|
+
*/
|
|
9
|
+
export declare const validateCustomBaselinePath: (path: string) => void;
|
|
10
|
+
export interface GenerateFlatBaselineOptions {
|
|
11
|
+
boxDimensions: BoxDimensions;
|
|
12
|
+
totalTextHeight: number;
|
|
13
|
+
baseline: number;
|
|
14
|
+
lineHeight: number;
|
|
15
|
+
verticalAlignment?: VerticalTextAlignmentValues;
|
|
16
|
+
lineNumber?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generate a path as a baseline for straight text
|
|
20
|
+
* This can be called multiple times for multiline text by incrementing the lineNumber parameter
|
|
21
|
+
*
|
|
22
|
+
* @returns an SVG path command to draw the baseline
|
|
23
|
+
*/
|
|
24
|
+
export declare const generateFlatBaseline: ({ boxDimensions, totalTextHeight, baseline, lineHeight, verticalAlignment, lineNumber, }: GenerateFlatBaselineOptions) => string;
|
|
25
|
+
export interface GenerateCurvedBaselineOptions {
|
|
26
|
+
boxDimensions: BoxDimensions;
|
|
27
|
+
baseline: number;
|
|
28
|
+
textHeight: number;
|
|
29
|
+
verticalAlignment: VerticalTextAlignmentValues;
|
|
30
|
+
}
|
|
31
|
+
/** Where the arch sits vertically: its peak, and the baseline at its ends. */
|
|
32
|
+
export interface CurvedBaselineGeometry {
|
|
33
|
+
/** y of the peak — the highest point of the baseline. */
|
|
34
|
+
curveTop: number;
|
|
35
|
+
/** y of the two ends — the lowest points of the baseline. */
|
|
36
|
+
curveBottom: number;
|
|
37
|
+
/** Horizontal gap left at each end so the outer glyphs are not clipped. */
|
|
38
|
+
curveXInset: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The arch's placement, on its own so that the path and anything reasoning
|
|
42
|
+
* about where the text will land come from one calculation.
|
|
43
|
+
*
|
|
44
|
+
* Sizing needs this: it has to know where the renderer will actually put the
|
|
45
|
+
* baseline before it can say whether the text riding it clears the box. Working
|
|
46
|
+
* that out separately is what let the fit engine believe a size fitted while
|
|
47
|
+
* the render clipped.
|
|
48
|
+
*/
|
|
49
|
+
export declare const curvedBaselineGeometry: ({ boxDimensions, baseline, textHeight, verticalAlignment, }: GenerateCurvedBaselineOptions) => CurvedBaselineGeometry;
|
|
50
|
+
/**
|
|
51
|
+
* Generate a path command to curve text and align that text to the top of the box.
|
|
52
|
+
* Uses two quadratic bezier curves — one from the bottom left to the peak, and a
|
|
53
|
+
* second mirrored from the peak to the end.
|
|
54
|
+
*
|
|
55
|
+
* Curved text is not currently supported for multiline text.
|
|
56
|
+
*
|
|
57
|
+
* @returns an SVG path command to draw the curve
|
|
58
|
+
*/
|
|
59
|
+
export declare const generateCurvedBaseline: ({ boxDimensions, baseline, textHeight, verticalAlignment, }: GenerateCurvedBaselineOptions) => string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){return e.type===`text`}function t(e){let t=e.getState().garment;return e.settings.garments.find(e=>e.slug===t)}function n(e,t,n){if(!e.embellishment_positions)return;let r=e.embellishment_positions.filter(e=>e.placement===t?!n||!e.variant||e.variant===n||e.variant.toLowerCase()===`common`:!1);if(n&&r.length>1){let e=r.find(e=>e.variant===n);if(e)return e}return r[0]}function r(e,t){let n=e.fonts.find(e=>e.id===t);if(n)return{id:n.id,fontUrl:n.font_url,fontFamily:n.font_family,letterSpacing:n.letter_spacing??0,letterSpacingOutline:n.letter_spacing_outline??0,outlineWidth:n.outline_width??0,fixedColours:n.fixed_colours}}function i(t,i,a,o,s,c){let{library:l}=s,u=[];for(let[d,f]of Object.entries(o)){if(f.type!==`text`)continue;let o=f,{meta:p}=o;if(!p?.text)continue;let{libraryItemID:m}=o,h=l[m];if(!h||!e(h))continue;let g=p.font;if(g===void 0)continue;let _=r(h,g);if(!_)continue;let v=n(i,d,c);if(!v)continue;let y=null,b=null;if(p.colours&&h.palette!=null)try{let e=t.palettes.getUsedColours(h.palette,p.colours);y=e[0]??null,b=e[1]??null}catch(e){console.warn(`[extractTextPlacements] Colour resolution failed for ${d}:`,e)}let x=s.byId?.[d],S={vertical:`top`,horizontal:`left`};if(x){let e=x;e.vertical_alignment&&(S.vertical=e.vertical_alignment),e.horizontal_alignment&&(S.horizontal=e.horizontal_alignment)}u.push({placementId:d,displayName:d,source:a,text:p.text,font:_,isCurved:p.isCurved??!1,isDistributed:p.isDistributed??!1,verticalAlignment:p.verticalAlignment??S.vertical,horizontalAlignment:p.horizontalAlignment??S.horizontal,rotation:p.rotation??0,spacing:p.spacing??null,fillColour:y,outlineColour:b,position:{width:v.width,height:v.height,physicalWidth:v.physical_width_decimal,physicalHeight:v.physical_height_decimal,physicalUnits:v.physical_size_units,baselinePath:v.baseline_path,rotation:v.rotation},userSetPhysicalSize:p.retainedPhysicalSize??null,fontMetrics:(()=>{let e=p.fontMetrics??{size:0,height:0,baseline:0,width:0},t=e.physicalFontSize||(e.physicalHeight!=null&&e.physicalHeight>0&&e.physicalUnits!=null?[e.physicalHeight,e.physicalUnits]:void 0);return{...e,physicalFontSize:t}})(),manufacturingMethod:p.manufacturing_method??``})}return u}function a(e){let n=e.getState(),r=t(e);if(!r)return console.warn(`[extractTextPlacements] No matching garment found`),[];let a=typeof e.getCurrentVariant==`function`?e.getCurrentVariant():void 0,o=[],s=n.vals?.placements,c=e.settings.placements;s&&c&&o.push(...i(e,r,`placements`,s,c,a));let l=n.vals?.embellishments,u=e.settings.embellishments;return l&&u&&o.push(...i(e,r,`embellishments`,l,u,a)),o.length===0&&console.warn(`[extractTextPlacements] No text entries found in either placements or embellishments`),o}function o(e){return e===`center`?`centre`:e}function s(e){let t=o(e.horizontalAlignment);e.isDistributed&&(t=`distributed`);let n;n=e.position.baselinePath?{type:`custom`,path:e.position.baselinePath}:e.isCurved?`curved`:`flat`;let r=e.userSetPhysicalSize?[e.userSetPhysicalSize[0],e.userSetPhysicalSize[1]]:e.fontMetrics.physicalFontSize==null?[e.position.physicalHeight,e.position.physicalUnits]:e.fontMetrics.physicalFontSize,i={outlineWidth:e.outlineColour==null?0:e.font.outlineWidth,letterSpacing:e.spacing?.letter_spacing??0,letterSpacingOutline:e.spacing?.letter_spacing_outline??0},a=e.fillColour?{hex:e.fillColour.hex,reference:e.fillColour.reference??void 0}:void 0,s=e.outlineColour?{hex:e.outlineColour.hex,reference:e.outlineColour.reference??void 0}:void 0,c;if(e.font.fixedColours){let t={};for(let[n,r]of Object.entries(e.font.fixedColours))t[n]={hex:r.hex,reference:r.reference};c=t}return{text:e.text,fontUrl:e.font.fontUrl,physicalSize:r,fontSizeFallback:e.fontMetrics.size,boxDimensions:{width:e.position.width,height:e.position.height,physicalWidth:e.position.physicalWidth,physicalHeight:e.position.physicalHeight,physicalUnits:e.position.physicalUnits},spacing:i,verticalAlignment:o(e.verticalAlignment),horizontalAlignment:t,baseline:n,fillColour:a,strokeColour:s,colourMap:c}}exports.extractTextPlacements=a,exports.mapStateToRendererOptions=s;
|
package/dist/ce-adapter/index.js
CHANGED
|
@@ -1,160 +1,162 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/ce-adapter/extractTextPlacements.ts
|
|
2
|
+
function e(e) {
|
|
3
|
+
return e.type === "text";
|
|
3
4
|
}
|
|
4
|
-
function
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
function t(e) {
|
|
6
|
+
let t = e.getState().garment;
|
|
7
|
+
return e.settings.garments.find((e) => e.slug === t);
|
|
7
8
|
}
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
function n(e, t, n) {
|
|
10
|
+
if (!e.embellishment_positions) return;
|
|
11
|
+
let r = e.embellishment_positions.filter((e) => e.placement === t ? !n || !e.variant || e.variant === n || e.variant.toLowerCase() === "common" : !1);
|
|
12
|
+
if (n && r.length > 1) {
|
|
13
|
+
let e = r.find((e) => e.variant === n);
|
|
14
|
+
if (e) return e;
|
|
15
|
+
}
|
|
16
|
+
return r[0];
|
|
16
17
|
}
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
};
|
|
18
|
+
function r(e, t) {
|
|
19
|
+
let n = e.fonts.find((e) => e.id === t);
|
|
20
|
+
if (n) return {
|
|
21
|
+
id: n.id,
|
|
22
|
+
fontUrl: n.font_url,
|
|
23
|
+
fontFamily: n.font_family,
|
|
24
|
+
letterSpacing: n.letter_spacing ?? 0,
|
|
25
|
+
letterSpacingOutline: n.letter_spacing_outline ?? 0,
|
|
26
|
+
outlineWidth: n.outline_width ?? 0,
|
|
27
|
+
fixedColours: n.fixed_colours
|
|
28
|
+
};
|
|
29
29
|
}
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
30
|
+
function i(t, i, a, o, s, c) {
|
|
31
|
+
let { library: l } = s, u = [];
|
|
32
|
+
for (let [d, f] of Object.entries(o)) {
|
|
33
|
+
if (f.type !== "text") continue;
|
|
34
|
+
let o = f, { meta: p } = o;
|
|
35
|
+
if (!p?.text) continue;
|
|
36
|
+
let { libraryItemID: m } = o, h = l[m];
|
|
37
|
+
if (!h || !e(h)) continue;
|
|
38
|
+
let g = p.font;
|
|
39
|
+
if (g === void 0) continue;
|
|
40
|
+
let _ = r(h, g);
|
|
41
|
+
if (!_) continue;
|
|
42
|
+
let v = n(i, d, c);
|
|
43
|
+
if (!v) continue;
|
|
44
|
+
let y = null, b = null;
|
|
45
|
+
if (p.colours && h.palette != null) try {
|
|
46
|
+
let e = t.palettes.getUsedColours(h.palette, p.colours);
|
|
47
|
+
y = e[0] ?? null, b = e[1] ?? null;
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.warn(`[extractTextPlacements] Colour resolution failed for ${d}:`, e);
|
|
50
|
+
}
|
|
51
|
+
let x = s.byId?.[d], S = {
|
|
52
|
+
vertical: "top",
|
|
53
|
+
horizontal: "left"
|
|
54
|
+
};
|
|
55
|
+
if (x) {
|
|
56
|
+
let e = x;
|
|
57
|
+
e.vertical_alignment && (S.vertical = e.vertical_alignment), e.horizontal_alignment && (S.horizontal = e.horizontal_alignment);
|
|
58
|
+
}
|
|
59
|
+
u.push({
|
|
60
|
+
placementId: d,
|
|
61
|
+
displayName: d,
|
|
62
|
+
source: a,
|
|
63
|
+
text: p.text,
|
|
64
|
+
font: _,
|
|
65
|
+
isCurved: p.isCurved ?? !1,
|
|
66
|
+
isDistributed: p.isDistributed ?? !1,
|
|
67
|
+
verticalAlignment: p.verticalAlignment ?? S.vertical,
|
|
68
|
+
horizontalAlignment: p.horizontalAlignment ?? S.horizontal,
|
|
69
|
+
rotation: p.rotation ?? 0,
|
|
70
|
+
spacing: p.spacing ?? null,
|
|
71
|
+
fillColour: y,
|
|
72
|
+
outlineColour: b,
|
|
73
|
+
position: {
|
|
74
|
+
width: v.width,
|
|
75
|
+
height: v.height,
|
|
76
|
+
physicalWidth: v.physical_width_decimal,
|
|
77
|
+
physicalHeight: v.physical_height_decimal,
|
|
78
|
+
physicalUnits: v.physical_size_units,
|
|
79
|
+
baselinePath: v.baseline_path,
|
|
80
|
+
rotation: v.rotation
|
|
81
|
+
},
|
|
82
|
+
userSetPhysicalSize: p.retainedPhysicalSize ?? null,
|
|
83
|
+
fontMetrics: (() => {
|
|
84
|
+
let e = p.fontMetrics ?? {
|
|
85
|
+
size: 0,
|
|
86
|
+
height: 0,
|
|
87
|
+
baseline: 0,
|
|
88
|
+
width: 0
|
|
89
|
+
}, t = e.physicalFontSize || (e.physicalHeight != null && e.physicalHeight > 0 && e.physicalUnits != null ? [e.physicalHeight, e.physicalUnits] : void 0);
|
|
90
|
+
return {
|
|
91
|
+
...e,
|
|
92
|
+
physicalFontSize: t
|
|
93
|
+
};
|
|
94
|
+
})(),
|
|
95
|
+
manufacturingMethod: p.manufacturing_method ?? ""
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return u;
|
|
95
99
|
}
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return u && r && o.push(...S(t, i, "embellishments", u, r, s)), o.length === 0 && console.warn("[extractTextPlacements] No text entries found in either placements or embellishments"), o;
|
|
100
|
+
function a(e) {
|
|
101
|
+
let n = e.getState(), r = t(e);
|
|
102
|
+
if (!r) return console.warn("[extractTextPlacements] No matching garment found"), [];
|
|
103
|
+
let a = typeof e.getCurrentVariant == "function" ? e.getCurrentVariant() : void 0, o = [], s = n.vals?.placements, c = e.settings.placements;
|
|
104
|
+
s && c && o.push(...i(e, r, "placements", s, c, a));
|
|
105
|
+
let l = n.vals?.embellishments, u = e.settings.embellishments;
|
|
106
|
+
return l && u && o.push(...i(e, r, "embellishments", l, u, a)), o.length === 0 && console.warn("[extractTextPlacements] No text entries found in either placements or embellishments"), o;
|
|
104
107
|
}
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/ce-adapter/mapStateToRendererOptions.ts
|
|
110
|
+
function o(e) {
|
|
111
|
+
return e === "center" ? "centre" : e;
|
|
107
112
|
}
|
|
108
|
-
function
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
};
|
|
113
|
+
function s(e) {
|
|
114
|
+
let t = o(e.horizontalAlignment);
|
|
115
|
+
e.isDistributed && (t = "distributed");
|
|
116
|
+
let n;
|
|
117
|
+
n = e.position.baselinePath ? {
|
|
118
|
+
type: "custom",
|
|
119
|
+
path: e.position.baselinePath
|
|
120
|
+
} : e.isCurved ? "curved" : "flat";
|
|
121
|
+
let r = e.userSetPhysicalSize ? [e.userSetPhysicalSize[0], e.userSetPhysicalSize[1]] : e.fontMetrics.physicalFontSize == null ? [e.position.physicalHeight, e.position.physicalUnits] : e.fontMetrics.physicalFontSize, i = {
|
|
122
|
+
outlineWidth: e.outlineColour == null ? 0 : e.font.outlineWidth,
|
|
123
|
+
letterSpacing: e.spacing?.letter_spacing ?? 0,
|
|
124
|
+
letterSpacingOutline: e.spacing?.letter_spacing_outline ?? 0
|
|
125
|
+
}, a = e.fillColour ? {
|
|
126
|
+
hex: e.fillColour.hex,
|
|
127
|
+
reference: e.fillColour.reference ?? void 0
|
|
128
|
+
} : void 0, s = e.outlineColour ? {
|
|
129
|
+
hex: e.outlineColour.hex,
|
|
130
|
+
reference: e.outlineColour.reference ?? void 0
|
|
131
|
+
} : void 0, c;
|
|
132
|
+
if (e.font.fixedColours) {
|
|
133
|
+
let t = {};
|
|
134
|
+
for (let [n, r] of Object.entries(e.font.fixedColours)) t[n] = {
|
|
135
|
+
hex: r.hex,
|
|
136
|
+
reference: r.reference
|
|
137
|
+
};
|
|
138
|
+
c = t;
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
text: e.text,
|
|
142
|
+
fontUrl: e.font.fontUrl,
|
|
143
|
+
physicalSize: r,
|
|
144
|
+
fontSizeFallback: e.fontMetrics.size,
|
|
145
|
+
boxDimensions: {
|
|
146
|
+
width: e.position.width,
|
|
147
|
+
height: e.position.height,
|
|
148
|
+
physicalWidth: e.position.physicalWidth,
|
|
149
|
+
physicalHeight: e.position.physicalHeight,
|
|
150
|
+
physicalUnits: e.position.physicalUnits
|
|
151
|
+
},
|
|
152
|
+
spacing: i,
|
|
153
|
+
verticalAlignment: o(e.verticalAlignment),
|
|
154
|
+
horizontalAlignment: t,
|
|
155
|
+
baseline: n,
|
|
156
|
+
fillColour: a,
|
|
157
|
+
strokeColour: s,
|
|
158
|
+
colourMap: c
|
|
159
|
+
};
|
|
156
160
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
H as mapStateToRendererOptions
|
|
160
|
-
};
|
|
161
|
+
//#endregion
|
|
162
|
+
export { a as extractTextPlacements, s as mapStateToRendererOptions };
|