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
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { createBrowserCanvas2DContext } from '../text/create-browser-canvas-2d-context.js';
|
|
2
|
+
import { buildTextMaskBandsFromAlpha } from './build-text-mask-bands-from-alpha.js';
|
|
3
|
+
import { getTextMaskPlacement, renderTextMaskRaster, } from './render-text-mask-raster.js';
|
|
4
|
+
import { measureTextMaskContent, resolveTextMaskShapeSize, } from './resolve-text-mask-shape-size.js';
|
|
5
|
+
import { segmentTextMaskGraphemes } from './segment-text-mask-graphemes.js';
|
|
6
|
+
const MAX_MASK_PIXELS = 4_000_000;
|
|
7
|
+
const compiledShapeCache = new Map();
|
|
8
|
+
function validateTextMaskShape(shape, size) {
|
|
9
|
+
if (shape.text.length === 0) {
|
|
10
|
+
throw new Error('text-mask shape needs a non-empty text value');
|
|
11
|
+
}
|
|
12
|
+
const shapeTextMode = shape.shapeTextMode ?? 'whole-text';
|
|
13
|
+
if (shapeTextMode !== 'whole-text' && shapeTextMode !== 'per-character') {
|
|
14
|
+
throw new Error('text-mask shapeTextMode must be whole-text or per-character');
|
|
15
|
+
}
|
|
16
|
+
const maskScale = shape.maskScale ?? 2;
|
|
17
|
+
if (!Number.isFinite(maskScale) || maskScale <= 0) {
|
|
18
|
+
throw new Error('text-mask maskScale must be a finite positive number');
|
|
19
|
+
}
|
|
20
|
+
const pixelWidth = Math.max(1, Math.ceil(size.width * maskScale));
|
|
21
|
+
const pixelHeight = Math.max(1, Math.ceil(size.height * maskScale));
|
|
22
|
+
if (pixelWidth * pixelHeight > MAX_MASK_PIXELS) {
|
|
23
|
+
throw new Error('text-mask raster request is too large');
|
|
24
|
+
}
|
|
25
|
+
const alphaThreshold = shape.alphaThreshold ?? 16;
|
|
26
|
+
if (!Number.isFinite(alphaThreshold) || alphaThreshold < 0 || alphaThreshold > 255) {
|
|
27
|
+
throw new Error('text-mask alphaThreshold must be between 0 and 255');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function buildCacheKey(shape, size, lineHeight, minSlotWidth) {
|
|
31
|
+
return JSON.stringify({
|
|
32
|
+
kind: shape.kind,
|
|
33
|
+
text: shape.text,
|
|
34
|
+
font: shape.font,
|
|
35
|
+
sizeMode: size.mode,
|
|
36
|
+
width: size.width,
|
|
37
|
+
height: size.height,
|
|
38
|
+
shapeTextMode: shape.shapeTextMode ?? 'whole-text',
|
|
39
|
+
padding: size.padding,
|
|
40
|
+
maskScale: shape.maskScale ?? 2,
|
|
41
|
+
alphaThreshold: shape.alphaThreshold ?? 16,
|
|
42
|
+
lineHeight,
|
|
43
|
+
minSlotWidth,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function isFontReadyForShape(shape) {
|
|
47
|
+
if (typeof document === 'undefined' || document.fonts === undefined) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
return document.fonts.check(shape.font, shape.text);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function freezeCompiledShape(compiledShape) {
|
|
58
|
+
freezeBands(compiledShape.bands);
|
|
59
|
+
freezeRegions(compiledShape.regions ?? []);
|
|
60
|
+
Object.freeze(compiledShape.bounds);
|
|
61
|
+
Object.freeze(compiledShape.source);
|
|
62
|
+
Object.freeze(compiledShape.debugView);
|
|
63
|
+
Object.freeze(compiledShape.bands);
|
|
64
|
+
if (compiledShape.regions !== undefined) {
|
|
65
|
+
Object.freeze(compiledShape.regions);
|
|
66
|
+
}
|
|
67
|
+
return Object.freeze(compiledShape);
|
|
68
|
+
}
|
|
69
|
+
function freezeBands(bands) {
|
|
70
|
+
for (let bandIndex = 0; bandIndex < bands.length; bandIndex++) {
|
|
71
|
+
const band = bands[bandIndex];
|
|
72
|
+
for (let intervalIndex = 0; intervalIndex < band.intervals.length; intervalIndex++) {
|
|
73
|
+
Object.freeze(band.intervals[intervalIndex]);
|
|
74
|
+
}
|
|
75
|
+
Object.freeze(band.intervals);
|
|
76
|
+
Object.freeze(band);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function freezeRegions(regions) {
|
|
80
|
+
for (let index = 0; index < regions.length; index++) {
|
|
81
|
+
const region = regions[index];
|
|
82
|
+
freezeBands(region.bands);
|
|
83
|
+
Object.freeze(region.bounds);
|
|
84
|
+
Object.freeze(region.debugView);
|
|
85
|
+
Object.freeze(region.bands);
|
|
86
|
+
Object.freeze(region);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function buildRegion(shape, size, lineHeight, minSlotWidth, index, grapheme, drawX, baseline) {
|
|
90
|
+
const { imageData } = renderTextMaskRaster(shape, size, grapheme, drawX, baseline);
|
|
91
|
+
return {
|
|
92
|
+
index,
|
|
93
|
+
grapheme,
|
|
94
|
+
bounds: { left: 0, top: 0, right: size.width, bottom: size.height },
|
|
95
|
+
bands: buildTextMaskBandsFromAlpha({
|
|
96
|
+
width: size.width,
|
|
97
|
+
height: size.height,
|
|
98
|
+
maskScale: shape.maskScale ?? 2,
|
|
99
|
+
alphaThreshold: shape.alphaThreshold ?? 16,
|
|
100
|
+
}, imageData.data, lineHeight, minSlotWidth),
|
|
101
|
+
debugView: {
|
|
102
|
+
kind: 'text',
|
|
103
|
+
text: grapheme,
|
|
104
|
+
font: shape.font,
|
|
105
|
+
x: drawX,
|
|
106
|
+
baseline,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function buildRegions(shape, size, context, placement, lineHeight, minSlotWidth) {
|
|
111
|
+
if ((shape.shapeTextMode ?? 'whole-text') !== 'per-character') {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
return segmentTextMaskGraphemes(shape.text, placement.x, value => context.measureText(value).width)
|
|
115
|
+
.filter(placementEntry => !placementEntry.isWhitespace)
|
|
116
|
+
.map((placementEntry, index) => buildRegion(shape, size, lineHeight, minSlotWidth, index, placementEntry.grapheme, placementEntry.drawX, placement.baseline))
|
|
117
|
+
.filter(region => region.bands.some(band => band.intervals.length > 0));
|
|
118
|
+
}
|
|
119
|
+
export function compileTextMaskShapeForLayout(shape, lineHeight, minSlotWidth) {
|
|
120
|
+
const context = createBrowserCanvas2DContext(1, 1);
|
|
121
|
+
const contentMetrics = measureTextMaskContent(context, shape);
|
|
122
|
+
const size = resolveTextMaskShapeSize(shape, contentMetrics);
|
|
123
|
+
validateTextMaskShape(shape, size);
|
|
124
|
+
const fontReadyForCache = isFontReadyForShape(shape);
|
|
125
|
+
const cacheKey = buildCacheKey(shape, size, lineHeight, minSlotWidth);
|
|
126
|
+
const cachedShape = fontReadyForCache ? compiledShapeCache.get(cacheKey) : undefined;
|
|
127
|
+
if (cachedShape !== undefined) {
|
|
128
|
+
return cachedShape;
|
|
129
|
+
}
|
|
130
|
+
const placement = getTextMaskPlacement(size, contentMetrics);
|
|
131
|
+
const { imageData } = renderTextMaskRaster(shape, size, shape.text, placement.x, placement.baseline);
|
|
132
|
+
const compiledShape = {
|
|
133
|
+
kind: shape.kind,
|
|
134
|
+
source: shape,
|
|
135
|
+
bounds: { left: 0, top: 0, right: size.width, bottom: size.height },
|
|
136
|
+
bandHeight: lineHeight,
|
|
137
|
+
minSlotWidth,
|
|
138
|
+
bands: buildTextMaskBandsFromAlpha({
|
|
139
|
+
width: size.width,
|
|
140
|
+
height: size.height,
|
|
141
|
+
maskScale: shape.maskScale ?? 2,
|
|
142
|
+
alphaThreshold: shape.alphaThreshold ?? 16,
|
|
143
|
+
}, imageData.data, lineHeight, minSlotWidth),
|
|
144
|
+
regions: buildRegions(shape, size, context, placement, lineHeight, minSlotWidth),
|
|
145
|
+
debugView: {
|
|
146
|
+
kind: 'text',
|
|
147
|
+
text: shape.text,
|
|
148
|
+
font: shape.font,
|
|
149
|
+
x: placement.x,
|
|
150
|
+
baseline: placement.baseline,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
if (compiledShape.bands.every(band => band.intervals.length === 0)) {
|
|
154
|
+
throw new Error('text-mask shape produced no visible fill area');
|
|
155
|
+
}
|
|
156
|
+
const frozenShape = freezeCompiledShape(compiledShape);
|
|
157
|
+
if (fontReadyForCache) {
|
|
158
|
+
compiledShapeCache.set(cacheKey, frozenShape);
|
|
159
|
+
}
|
|
160
|
+
return frozenShape;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=compile-text-mask-shape-for-layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile-text-mask-shape-for-layout.js","sourceRoot":"","sources":["../../src/shape/compile-text-mask-shape-for-layout.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,4BAA4B,EAAE,MAAM,6CAA6C,CAAA;AAC1F,OAAO,EAAE,2BAA2B,EAAE,MAAM,uCAAuC,CAAA;AACnF,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GAGrB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EACL,sBAAsB,EACtB,wBAAwB,GAEzB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAA;AAE3E,MAAM,eAAe,GAAG,SAAS,CAAA;AACjC,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAA8B,CAAA;AAEhE,SAAS,qBAAqB,CAAC,KAAoB,EAAE,IAA0B;IAC7E,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAA;IACjE,CAAC;IAED,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,IAAI,YAAY,CAAA;IACzD,IAAI,aAAa,KAAK,YAAY,IAAI,aAAa,KAAK,eAAe,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAA;IAChF,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,CAAC,CAAA;IACtC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAA;IACzE,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,CAAA;IACjE,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;IACnE,IAAI,UAAU,GAAG,WAAW,GAAG,eAAe,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;IAC1D,CAAC;IAED,MAAM,cAAc,GAAG,KAAK,CAAC,cAAc,IAAI,EAAE,CAAA;IACjD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,cAAc,GAAG,CAAC,IAAI,cAAc,GAAG,GAAG,EAAE,CAAC;QACnF,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAA;IACvE,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CACpB,KAAoB,EACpB,IAA0B,EAC1B,UAAkB,EAClB,YAAoB;IAEpB,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,QAAQ,EAAE,IAAI,CAAC,IAAI;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,YAAY;QAClD,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,CAAC;QAC/B,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,EAAE;QAC1C,UAAU;QACV,YAAY;KACb,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAoB;IAC/C,IAAI,OAAO,QAAQ,KAAK,WAAW,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACpE,OAAO,IAAI,CAAA;IACb,CAAC;IAED,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,aAAiC;IAC5D,WAAW,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;IAChC,aAAa,CAAC,aAAa,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;IAE1C,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;IACnC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;IACnC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAA;IACtC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;IAClC,IAAI,aAAa,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;IACtC,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;AACrC,CAAC;AAED,SAAS,WAAW,CAAC,KAAkC;IACrD,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC;QAC9D,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAE,CAAA;QAC9B,KAAK,IAAI,aAAa,GAAG,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,CAAC;YACnF,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAE,CAAC,CAAA;QAC/C,CAAC;QAED,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACrB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,OAA8B;IACnD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAE,CAAA;QAC9B,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACzB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAC5B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QAC/B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC3B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACvB,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAClB,KAAoB,EACpB,IAA0B,EAC1B,UAAkB,EAClB,YAAoB,EACpB,KAAa,EACb,QAAgB,EAChB,KAAa,EACb,QAAgB;IAEhB,MAAM,EAAE,SAAS,EAAE,GAAG,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAA;IAElF,OAAO;QACL,KAAK;QACL,QAAQ;QACR,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;QACnE,KAAK,EAAE,2BAA2B,CAChC;YACE,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,CAAC;YAC/B,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,EAAE;SAC3C,EACD,SAAS,CAAC,IAAI,EACd,UAAU,EACV,YAAY,CACb;QACD,SAAS,EAAE;YACT,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,CAAC,EAAE,KAAK;YACR,QAAQ;SACT;KACF,CAAA;AACH,CAAC;AAED,SAAS,YAAY,CACnB,KAAoB,EACpB,IAA0B,EAC1B,OAAkC,EAClC,SAA4B,EAC5B,UAAkB,EAClB,YAAoB;IAEpB,IAAI,CAAC,KAAK,CAAC,aAAa,IAAI,YAAY,CAAC,KAAK,eAAe,EAAE,CAAC;QAC9D,OAAO,EAAE,CAAA;IACX,CAAC;IAED,OAAO,wBAAwB,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC;SAChG,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,cAAc,CAAC,YAAY,CAAC;SACtD,GAAG,CAAC,CAAC,cAAc,EAAE,KAAK,EAAE,EAAE,CAC7B,WAAW,CACT,KAAK,EACL,IAAI,EACJ,UAAU,EACV,YAAY,EACZ,KAAK,EACL,cAAc,CAAC,QAAQ,EACvB,cAAc,CAAC,KAAK,EACpB,SAAS,CAAC,QAAQ,CACnB,CACF;SACA,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAA;AAC3E,CAAC;AAED,MAAM,UAAU,6BAA6B,CAC3C,KAAoB,EACpB,UAAkB,EAClB,YAAoB;IAEpB,MAAM,OAAO,GAAG,4BAA4B,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAClD,MAAM,cAAc,GAAG,sBAAsB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IAC7D,MAAM,IAAI,GAAG,wBAAwB,CAAC,KAAK,EAAE,cAAc,CAAC,CAAA;IAC5D,qBAAqB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IAElC,MAAM,iBAAiB,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAA;IACpD,MAAM,QAAQ,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,CAAC,CAAA;IACrE,MAAM,WAAW,GAAG,iBAAiB,CAAC,CAAC,CAAC,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACpF,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,WAAW,CAAA;IACpB,CAAC;IAED,MAAM,SAAS,GAAG,oBAAoB,CAAC,IAAI,EAAE,cAAc,CAAC,CAAA;IAC5D,MAAM,EAAE,SAAS,EAAE,GAAG,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAA;IACpG,MAAM,aAAa,GAAuB;QACxC,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,KAAK;QACb,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;QACnE,UAAU,EAAE,UAAU;QACtB,YAAY;QACZ,KAAK,EAAE,2BAA2B,CAChC;YACE,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,CAAC;YAC/B,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,EAAE;SAC3C,EACD,SAAS,CAAC,IAAI,EACd,UAAU,EACV,YAAY,CACb;QACD,OAAO,EAAE,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,CAAC;QAChF,SAAS,EAAE;YACT,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,CAAC,EAAE,SAAS,CAAC,CAAC;YACd,QAAQ,EAAE,SAAS,CAAC,QAAQ;SAC7B;KACF,CAAA;IAED,IAAI,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAA;IAClE,CAAC;IAED,MAAM,WAAW,GAAG,mBAAmB,CAAC,aAAa,CAAC,CAAA;IACtD,IAAI,iBAAiB,EAAE,CAAC;QACtB,kBAAkB,CAAC,GAAG,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAA;IAC/C,CAAC;IAED,OAAO,WAAW,CAAA;AACpB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TextMaskShape } from '../types.js';
|
|
2
|
+
import { createBrowserCanvas2DContext } from '../text/create-browser-canvas-2d-context.js';
|
|
3
|
+
import type { ResolvedTextMaskSize, TextMaskContentMetrics } from './resolve-text-mask-shape-size.js';
|
|
4
|
+
export type TextMaskPlacement = {
|
|
5
|
+
x: number;
|
|
6
|
+
baseline: number;
|
|
7
|
+
};
|
|
8
|
+
export type TextMaskCanvas = {
|
|
9
|
+
context: ReturnType<typeof createBrowserCanvas2DContext>;
|
|
10
|
+
imageData: ImageData;
|
|
11
|
+
};
|
|
12
|
+
export declare function getTextMaskPlacement(size: ResolvedTextMaskSize, metrics: TextMaskContentMetrics): TextMaskPlacement;
|
|
13
|
+
export declare function renderTextMaskRaster(shape: TextMaskShape, size: ResolvedTextMaskSize, text?: string, drawX?: number, baseline?: number): TextMaskCanvas;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createBrowserCanvas2DContext } from '../text/create-browser-canvas-2d-context.js';
|
|
2
|
+
export function getTextMaskPlacement(size, metrics) {
|
|
3
|
+
const innerWidth = Math.max(0, size.width - size.padding * 2);
|
|
4
|
+
const innerHeight = Math.max(0, size.height - size.padding * 2);
|
|
5
|
+
const left = size.padding + Math.max(0, (innerWidth - metrics.width) / 2);
|
|
6
|
+
const top = size.padding + Math.max(0, (innerHeight - metrics.height) / 2);
|
|
7
|
+
return {
|
|
8
|
+
x: left + metrics.left,
|
|
9
|
+
baseline: top + metrics.ascent,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function renderTextMaskRaster(shape, size, text = shape.text, drawX, baseline) {
|
|
13
|
+
const maskScale = shape.maskScale ?? 2;
|
|
14
|
+
const pixelWidth = Math.max(1, Math.ceil(size.width * maskScale));
|
|
15
|
+
const pixelHeight = Math.max(1, Math.ceil(size.height * maskScale));
|
|
16
|
+
const context = createBrowserCanvas2DContext(pixelWidth, pixelHeight);
|
|
17
|
+
context.setTransform(1, 0, 0, 1, 0, 0);
|
|
18
|
+
context.clearRect(0, 0, pixelWidth, pixelHeight);
|
|
19
|
+
context.setTransform(maskScale, 0, 0, maskScale, 0, 0);
|
|
20
|
+
context.font = shape.font;
|
|
21
|
+
context.fillStyle = '#000000';
|
|
22
|
+
context.textBaseline = 'alphabetic';
|
|
23
|
+
context.fillText(text, drawX ?? 0, baseline ?? 0);
|
|
24
|
+
return {
|
|
25
|
+
context,
|
|
26
|
+
imageData: context.getImageData(0, 0, pixelWidth, pixelHeight),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=render-text-mask-raster.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-text-mask-raster.js","sourceRoot":"","sources":["../../src/shape/render-text-mask-raster.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,4BAA4B,EAAE,MAAM,6CAA6C,CAAA;AAa1F,MAAM,UAAU,oBAAoB,CAClC,IAA0B,EAC1B,OAA+B;IAE/B,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAA;IAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAA;IAC/D,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;IACzE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;IAE1E,OAAO;QACL,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI;QACtB,QAAQ,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM;KAC/B,CAAA;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,KAAoB,EACpB,IAA0B,EAC1B,IAAI,GAAG,KAAK,CAAC,IAAI,EACjB,KAAc,EACd,QAAiB;IAEjB,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,CAAC,CAAA;IACtC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,CAAA;IACjE,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;IACnE,MAAM,OAAO,GAAG,4BAA4B,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;IAErE,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IACtC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,UAAU,EAAE,WAAW,CAAC,CAAA;IAChD,OAAO,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IACtD,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;IACzB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAA;IAC7B,OAAO,CAAC,YAAY,GAAG,YAAY,CAAA;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE,QAAQ,IAAI,CAAC,CAAC,CAAA;IAEjD,OAAO;QACL,OAAO;QACP,SAAS,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,UAAU,EAAE,WAAW,CAAC;KAC/D,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BrowserCanvas2DContext } from '../text/create-browser-canvas-2d-context.js';
|
|
2
|
+
import type { TextMaskShape, TextMaskShapeSizeMode } from '../types.js';
|
|
3
|
+
export type TextMaskContentMetrics = {
|
|
4
|
+
left: number;
|
|
5
|
+
right: number;
|
|
6
|
+
ascent: number;
|
|
7
|
+
descent: number;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
};
|
|
11
|
+
export type ResolvedTextMaskSize = {
|
|
12
|
+
mode: TextMaskShapeSizeMode;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
padding: number;
|
|
16
|
+
};
|
|
17
|
+
export declare function measureTextMaskContent(context: BrowserCanvas2DContext, shape: TextMaskShape, text?: string): TextMaskContentMetrics;
|
|
18
|
+
export declare function resolveTextMaskShapeSize(shape: TextMaskShape, contentMetrics: TextMaskContentMetrics): ResolvedTextMaskSize;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
function assertFiniteNumber(value, label) {
|
|
2
|
+
if (!Number.isFinite(value)) {
|
|
3
|
+
throw new Error(`${label} must be a finite number`);
|
|
4
|
+
}
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
function assertPositiveFiniteNumber(value, label) {
|
|
8
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
9
|
+
throw new Error(`${label} must be a finite positive number`);
|
|
10
|
+
}
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
function assertNoLegacySizeFields(shape) {
|
|
14
|
+
if ('width' in shape || 'height' in shape || 'padding' in shape) {
|
|
15
|
+
throw new Error('text-mask width, height, and padding moved to shape.size');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function resolveMetric(value, fallback = 0) {
|
|
19
|
+
return value !== undefined && Number.isFinite(value) ? value : fallback;
|
|
20
|
+
}
|
|
21
|
+
export function measureTextMaskContent(context, shape, text = shape.text) {
|
|
22
|
+
context.font = shape.font;
|
|
23
|
+
const metrics = context.measureText(text);
|
|
24
|
+
const left = resolveMetric(metrics.actualBoundingBoxLeft, 0);
|
|
25
|
+
const right = resolveMetric(metrics.actualBoundingBoxRight, metrics.width);
|
|
26
|
+
const ascent = resolveMetric(metrics.actualBoundingBoxAscent, 0);
|
|
27
|
+
const descent = resolveMetric(metrics.actualBoundingBoxDescent, 0);
|
|
28
|
+
return {
|
|
29
|
+
left,
|
|
30
|
+
right,
|
|
31
|
+
ascent,
|
|
32
|
+
descent,
|
|
33
|
+
width: left + right,
|
|
34
|
+
height: ascent + descent,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function resolveTextMaskShapeSize(shape, contentMetrics) {
|
|
38
|
+
assertNoLegacySizeFields(shape);
|
|
39
|
+
if (shape.size !== undefined && (shape.size === null || typeof shape.size !== 'object')) {
|
|
40
|
+
throw new Error('text-mask size must be an object');
|
|
41
|
+
}
|
|
42
|
+
const size = shape.size ?? {};
|
|
43
|
+
const mode = size.mode ?? 'fit-content';
|
|
44
|
+
if (mode !== 'fit-content' && mode !== 'fixed') {
|
|
45
|
+
throw new Error('text-mask size.mode must be fit-content or fixed');
|
|
46
|
+
}
|
|
47
|
+
const padding = size.padding === undefined ? 0 : assertFiniteNumber(size.padding, 'text-mask padding');
|
|
48
|
+
if (padding < 0) {
|
|
49
|
+
throw new Error('text-mask padding must be a finite non-negative number');
|
|
50
|
+
}
|
|
51
|
+
if (mode === 'fixed') {
|
|
52
|
+
const fixedSize = size;
|
|
53
|
+
return {
|
|
54
|
+
mode,
|
|
55
|
+
width: assertPositiveFiniteNumber(fixedSize.width, 'text-mask fixed width'),
|
|
56
|
+
height: assertPositiveFiniteNumber(fixedSize.height, 'text-mask fixed height'),
|
|
57
|
+
padding,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
mode,
|
|
62
|
+
width: Math.max(1, contentMetrics.width + padding * 2),
|
|
63
|
+
height: Math.max(1, contentMetrics.height + padding * 2),
|
|
64
|
+
padding,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=resolve-text-mask-shape-size.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-text-mask-shape-size.js","sourceRoot":"","sources":["../../src/shape/resolve-text-mask-shape-size.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
|
|
2
|
+
function isWhitespaceGrapheme(grapheme) {
|
|
3
|
+
return grapheme.trim().length === 0;
|
|
4
|
+
}
|
|
5
|
+
export function segmentTextMaskGraphemes(text, originX, measureAdvance) {
|
|
6
|
+
const segments = Array.from(graphemeSegmenter.segment(text), segment => segment.segment);
|
|
7
|
+
const placements = [];
|
|
8
|
+
let prefix = '';
|
|
9
|
+
for (let index = 0; index < segments.length; index++) {
|
|
10
|
+
const grapheme = segments[index];
|
|
11
|
+
placements.push({
|
|
12
|
+
grapheme,
|
|
13
|
+
drawX: originX + measureAdvance(prefix),
|
|
14
|
+
isWhitespace: isWhitespaceGrapheme(grapheme),
|
|
15
|
+
});
|
|
16
|
+
prefix += grapheme;
|
|
17
|
+
}
|
|
18
|
+
return placements;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=segment-text-mask-graphemes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segment-text-mask-graphemes.js","sourceRoot":"","sources":["../../src/shape/segment-text-mask-graphemes.ts"],"names":[],"mappings":"AAAA,MAAM,iBAAiB,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAA;AAQpF,SAAS,oBAAoB,CAAC,QAAgB;IAC5C,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,IAAY,EACZ,OAAe,EACf,cAAyC;IAEzC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IACxF,MAAM,UAAU,GAAgC,EAAE,CAAA;IAClD,IAAI,MAAM,GAAG,EAAE,CAAA;IAEf,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAE,CAAA;QACjC,UAAU,CAAC,IAAI,CAAC;YACd,QAAQ;YACR,KAAK,EAAE,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC;YACvC,YAAY,EAAE,oBAAoB,CAAC,QAAQ,CAAC;SAC7C,CAAC,CAAA;QACF,MAAM,IAAI,QAAQ,CAAA;IACpB,CAAC;IAED,OAAO,UAAU,CAAA;AACnB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function createBrowserCanvas2DContext(width = 1, height = 1) {
|
|
2
|
+
const safeWidth = Math.max(1, Math.ceil(width));
|
|
3
|
+
const safeHeight = Math.max(1, Math.ceil(height));
|
|
4
|
+
if (typeof OffscreenCanvas !== 'undefined') {
|
|
5
|
+
const context = new OffscreenCanvas(safeWidth, safeHeight).getContext('2d');
|
|
6
|
+
if (context !== null) {
|
|
7
|
+
return context;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
if (typeof document !== 'undefined') {
|
|
11
|
+
const canvas = document.createElement('canvas');
|
|
12
|
+
canvas.width = safeWidth;
|
|
13
|
+
canvas.height = safeHeight;
|
|
14
|
+
const context = canvas.getContext('2d');
|
|
15
|
+
if (context !== null) {
|
|
16
|
+
return context;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
throw new Error('shape-text needs a browser canvas context or a custom TextMeasurer');
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=create-browser-canvas-2d-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-browser-canvas-2d-context.js","sourceRoot":"","sources":["../../src/text/create-browser-canvas-2d-context.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,4BAA4B,CAC1C,KAAK,GAAG,CAAC,EACT,MAAM,GAAG,CAAC;IAEV,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;IAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAEjD,IAAI,OAAO,eAAe,KAAK,WAAW,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QAC3E,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,OAAO,CAAA;QAChB,CAAC;IACH,CAAC;IAED,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;QAC/C,MAAM,CAAC,KAAK,GAAG,SAAS,CAAA;QACxB,MAAM,CAAC,MAAM,GAAG,UAAU,CAAA;QAC1B,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QACvC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,OAAO,CAAA;QAChB,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAA;AACvF,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createBrowserCanvas2DContext } from './create-browser-canvas-2d-context.js';
|
|
2
|
+
export function createCanvasTextMeasurer() {
|
|
3
|
+
const context = createBrowserCanvas2DContext();
|
|
4
|
+
return {
|
|
5
|
+
measureText(text, font) {
|
|
6
|
+
context.font = font;
|
|
7
|
+
return context.measureText(text).width;
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=create-canvas-text-measurer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-canvas-text-measurer.js","sourceRoot":"","sources":["../../src/text/create-canvas-text-measurer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAA;AAEpF,MAAM,UAAU,wBAAwB;IACtC,MAAM,OAAO,GAAG,4BAA4B,EAAE,CAAA;IAE9C,OAAO;QACL,WAAW,CAAC,IAAI,EAAE,IAAI;YACpB,OAAO,CAAC,IAAI,GAAG,IAAI,CAAA;YACnB,OAAO,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,KAAK,CAAA;QACxC,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { getWordSliceText, getWordSliceWidth } from './layout-text-line-helpers.js';
|
|
2
|
+
function findMaxSliceEnd(pattern, start, maxWidth) {
|
|
3
|
+
let low = start;
|
|
4
|
+
let high = pattern.graphemes.length;
|
|
5
|
+
while (low < high) {
|
|
6
|
+
const mid = Math.ceil((low + high) / 2);
|
|
7
|
+
const width = getWordSliceWidth(pattern, start, mid);
|
|
8
|
+
if (width <= maxWidth) {
|
|
9
|
+
low = mid;
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
high = mid - 1;
|
|
13
|
+
}
|
|
14
|
+
return low;
|
|
15
|
+
}
|
|
16
|
+
export function layoutNextLineFromDenseRepeatedText(pattern, startOffset, maxWidth) {
|
|
17
|
+
if (pattern.graphemes.length === 0) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const availableWidth = Math.max(0, maxWidth);
|
|
21
|
+
const start = { tokenIndex: startOffset, graphemeIndex: 0 };
|
|
22
|
+
const textParts = [];
|
|
23
|
+
let width = 0;
|
|
24
|
+
let consumed = 0;
|
|
25
|
+
const appendSlice = (sliceStart, sliceEnd) => {
|
|
26
|
+
if (sliceEnd <= sliceStart) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
textParts.push(getWordSliceText(pattern, sliceStart, sliceEnd));
|
|
30
|
+
width += getWordSliceWidth(pattern, sliceStart, sliceEnd);
|
|
31
|
+
consumed += sliceEnd - sliceStart;
|
|
32
|
+
};
|
|
33
|
+
const tailEnd = findMaxSliceEnd(pattern, startOffset, availableWidth);
|
|
34
|
+
appendSlice(startOffset, tailEnd);
|
|
35
|
+
if (tailEnd === pattern.graphemes.length) {
|
|
36
|
+
const remainingAfterTail = availableWidth - width;
|
|
37
|
+
if (remainingAfterTail >= pattern.width) {
|
|
38
|
+
const fullCycles = Math.floor(remainingAfterTail / pattern.width);
|
|
39
|
+
if (fullCycles > 0) {
|
|
40
|
+
textParts.push(pattern.text.repeat(fullCycles));
|
|
41
|
+
width += pattern.width * fullCycles;
|
|
42
|
+
consumed += pattern.graphemes.length * fullCycles;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const remainingAfterCycles = availableWidth - width;
|
|
46
|
+
const prefixEnd = findMaxSliceEnd(pattern, 0, remainingAfterCycles);
|
|
47
|
+
appendSlice(0, prefixEnd);
|
|
48
|
+
}
|
|
49
|
+
if (consumed === 0) {
|
|
50
|
+
const forcedEnd = Math.min(startOffset + 1, pattern.graphemes.length);
|
|
51
|
+
appendSlice(startOffset, forcedEnd);
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
text: textParts.join(''),
|
|
55
|
+
width,
|
|
56
|
+
start,
|
|
57
|
+
end: {
|
|
58
|
+
tokenIndex: (startOffset + consumed) % pattern.graphemes.length,
|
|
59
|
+
graphemeIndex: 0,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=layout-next-line-from-dense-repeated-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout-next-line-from-dense-repeated-text.js","sourceRoot":"","sources":["../../src/text/layout-next-line-from-dense-repeated-text.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { fitWordFragment, getWordSliceText, getWordSliceWidth } from './layout-text-line-helpers.js';
|
|
2
|
+
export function layoutNextLineFromPreparedText(prepared, start, maxWidth) {
|
|
3
|
+
if (start.tokenIndex >= prepared.tokens.length)
|
|
4
|
+
return null;
|
|
5
|
+
const lineWords = [];
|
|
6
|
+
let width = 0;
|
|
7
|
+
let tokenIndex = start.tokenIndex;
|
|
8
|
+
let graphemeIndex = start.graphemeIndex;
|
|
9
|
+
while (tokenIndex < prepared.tokens.length) {
|
|
10
|
+
const token = prepared.tokens[tokenIndex];
|
|
11
|
+
if (token.kind === 'newline') {
|
|
12
|
+
return {
|
|
13
|
+
text: lineWords.join(' '),
|
|
14
|
+
width,
|
|
15
|
+
start,
|
|
16
|
+
end: { tokenIndex: tokenIndex + 1, graphemeIndex: 0 },
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const sliceStart = tokenIndex === start.tokenIndex ? graphemeIndex : 0;
|
|
20
|
+
const spacing = lineWords.length === 0 ? 0 : prepared.spaceWidth;
|
|
21
|
+
const remainingWidth = getWordSliceWidth(token, sliceStart, token.graphemes.length);
|
|
22
|
+
if (width + spacing + remainingWidth <= maxWidth) {
|
|
23
|
+
lineWords.push(getWordSliceText(token, sliceStart, token.graphemes.length));
|
|
24
|
+
width += spacing + remainingWidth;
|
|
25
|
+
tokenIndex += 1;
|
|
26
|
+
graphemeIndex = 0;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (lineWords.length > 0) {
|
|
30
|
+
return {
|
|
31
|
+
text: lineWords.join(' '),
|
|
32
|
+
width,
|
|
33
|
+
start,
|
|
34
|
+
end: { tokenIndex, graphemeIndex: sliceStart },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const fittedEnd = fitWordFragment(token, sliceStart, maxWidth);
|
|
38
|
+
const fragmentText = getWordSliceText(token, sliceStart, fittedEnd);
|
|
39
|
+
const fragmentWidth = getWordSliceWidth(token, sliceStart, fittedEnd);
|
|
40
|
+
const nextCursor = fittedEnd >= token.graphemes.length
|
|
41
|
+
? { tokenIndex: tokenIndex + 1, graphemeIndex: 0 }
|
|
42
|
+
: { tokenIndex, graphemeIndex: fittedEnd };
|
|
43
|
+
return {
|
|
44
|
+
text: fragmentText,
|
|
45
|
+
width: fragmentWidth,
|
|
46
|
+
start,
|
|
47
|
+
end: nextCursor,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
text: lineWords.join(' '),
|
|
52
|
+
width,
|
|
53
|
+
start,
|
|
54
|
+
end: { tokenIndex, graphemeIndex: 0 },
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=layout-next-line-from-prepared-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout-next-line-from-prepared-text.js","sourceRoot":"","sources":["../../src/text/layout-next-line-from-prepared-text.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AAEpG,MAAM,UAAU,8BAA8B,CAC5C,QAA4B,EAC5B,KAAmB,EACnB,QAAgB;IAEhB,IAAI,KAAK,CAAC,UAAU,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAE3D,MAAM,SAAS,GAAa,EAAE,CAAA;IAC9B,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,UAAU,GAAG,KAAK,CAAC,UAAU,CAAA;IACjC,IAAI,aAAa,GAAG,KAAK,CAAC,aAAa,CAAA;IAEvC,OAAO,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAE,CAAA;QAE1C,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO;gBACL,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;gBACzB,KAAK;gBACL,KAAK;gBACL,GAAG,EAAE,EAAE,UAAU,EAAE,UAAU,GAAG,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE;aACtD,CAAA;QACH,CAAC;QAED,MAAM,UAAU,GAAG,UAAU,KAAK,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;QACtE,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAA;QAChE,MAAM,cAAc,GAAG,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;QAEnF,IAAI,KAAK,GAAG,OAAO,GAAG,cAAc,IAAI,QAAQ,EAAE,CAAC;YACjD,SAAS,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;YAC3E,KAAK,IAAI,OAAO,GAAG,cAAc,CAAA;YACjC,UAAU,IAAI,CAAC,CAAA;YACf,aAAa,GAAG,CAAC,CAAA;YACjB,SAAQ;QACV,CAAC;QAED,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,OAAO;gBACL,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;gBACzB,KAAK;gBACL,KAAK;gBACL,GAAG,EAAE,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE;aAC/C,CAAA;QACH,CAAC;QAED,MAAM,SAAS,GAAG,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAA;QAC9D,MAAM,YAAY,GAAG,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;QACnE,MAAM,aAAa,GAAG,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;QACrE,MAAM,UAAU,GACd,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM;YACjC,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,GAAG,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE;YAClD,CAAC,CAAC,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,CAAA;QAE9C,OAAO;YACL,IAAI,EAAE,YAAY;YAClB,KAAK,EAAE,aAAa;YACpB,KAAK;YACL,GAAG,EAAE,UAAU;SAChB,CAAA;IACH,CAAC;IAED,OAAO;QACL,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;QACzB,KAAK;QACL,KAAK;QACL,GAAG,EAAE,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC,EAAE;KACtC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { fitWordFragment, getWordSliceText, getWordSliceWidth } from './layout-text-line-helpers.js';
|
|
2
|
+
export function layoutNextLineFromRepeatedText(prepared, start, maxWidth) {
|
|
3
|
+
if (prepared.tokens.length === 0)
|
|
4
|
+
return null;
|
|
5
|
+
const lineWords = [];
|
|
6
|
+
let width = 0;
|
|
7
|
+
let tokenIndex = start.tokenIndex;
|
|
8
|
+
let graphemeIndex = start.graphemeIndex;
|
|
9
|
+
while (true) {
|
|
10
|
+
const token = prepared.tokens[tokenIndex % prepared.tokens.length];
|
|
11
|
+
if (token.kind === 'newline') {
|
|
12
|
+
return {
|
|
13
|
+
text: lineWords.join(' '),
|
|
14
|
+
width,
|
|
15
|
+
start,
|
|
16
|
+
end: { tokenIndex: tokenIndex + 1, graphemeIndex: 0 },
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const sliceStart = tokenIndex === start.tokenIndex ? graphemeIndex : 0;
|
|
20
|
+
const spacing = lineWords.length === 0 ? 0 : prepared.spaceWidth;
|
|
21
|
+
const remainingWidth = getWordSliceWidth(token, sliceStart, token.graphemes.length);
|
|
22
|
+
if (width + spacing + remainingWidth <= maxWidth) {
|
|
23
|
+
lineWords.push(getWordSliceText(token, sliceStart, token.graphemes.length));
|
|
24
|
+
width += spacing + remainingWidth;
|
|
25
|
+
tokenIndex += 1;
|
|
26
|
+
graphemeIndex = 0;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (lineWords.length > 0) {
|
|
30
|
+
return {
|
|
31
|
+
text: lineWords.join(' '),
|
|
32
|
+
width,
|
|
33
|
+
start,
|
|
34
|
+
end: { tokenIndex, graphemeIndex: sliceStart },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const fittedEnd = fitWordFragment(token, sliceStart, maxWidth);
|
|
38
|
+
const fragmentText = getWordSliceText(token, sliceStart, fittedEnd);
|
|
39
|
+
const fragmentWidth = getWordSliceWidth(token, sliceStart, fittedEnd);
|
|
40
|
+
const nextCursor = fittedEnd >= token.graphemes.length
|
|
41
|
+
? { tokenIndex: tokenIndex + 1, graphemeIndex: 0 }
|
|
42
|
+
: { tokenIndex, graphemeIndex: fittedEnd };
|
|
43
|
+
return {
|
|
44
|
+
text: fragmentText,
|
|
45
|
+
width: fragmentWidth,
|
|
46
|
+
start,
|
|
47
|
+
end: nextCursor,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=layout-next-line-from-repeated-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout-next-line-from-repeated-text.js","sourceRoot":"","sources":["../../src/text/layout-next-line-from-repeated-text.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AAEpG,MAAM,UAAU,8BAA8B,CAC5C,QAA4B,EAC5B,KAAmB,EACnB,QAAgB;IAEhB,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAE7C,MAAM,SAAS,GAAa,EAAE,CAAA;IAC9B,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,UAAU,GAAG,KAAK,CAAC,UAAU,CAAA;IACjC,IAAI,aAAa,GAAG,KAAK,CAAC,aAAa,CAAA;IAEvC,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAE,CAAA;QAEnE,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO;gBACL,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;gBACzB,KAAK;gBACL,KAAK;gBACL,GAAG,EAAE,EAAE,UAAU,EAAE,UAAU,GAAG,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE;aACtD,CAAA;QACH,CAAC;QAED,MAAM,UAAU,GAAG,UAAU,KAAK,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;QACtE,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAA;QAChE,MAAM,cAAc,GAAG,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;QAEnF,IAAI,KAAK,GAAG,OAAO,GAAG,cAAc,IAAI,QAAQ,EAAE,CAAC;YACjD,SAAS,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;YAC3E,KAAK,IAAI,OAAO,GAAG,cAAc,CAAA;YACjC,UAAU,IAAI,CAAC,CAAA;YACf,aAAa,GAAG,CAAC,CAAA;YACjB,SAAQ;QACV,CAAC;QAED,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,OAAO;gBACL,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;gBACzB,KAAK;gBACL,KAAK;gBACL,GAAG,EAAE,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE;aAC/C,CAAA;QACH,CAAC;QAED,MAAM,SAAS,GAAG,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAA;QAC9D,MAAM,YAAY,GAAG,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;QACnE,MAAM,aAAa,GAAG,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;QACrE,MAAM,UAAU,GACd,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM;YACjC,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,GAAG,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE;YAClD,CAAC,CAAC,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,CAAA;QAE9C,OAAO;YACL,IAAI,EAAE,YAAY;YAClB,KAAK,EAAE,aAAa;YACpB,KAAK;YACL,GAAG,EAAE,UAAU;SAChB,CAAA;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PreparedLayoutToken } from '../types.js';
|
|
2
|
+
export declare function getWordSliceWidth(token: PreparedLayoutToken, start: number, end: number): number;
|
|
3
|
+
export declare function getWordSliceText(token: PreparedLayoutToken, start: number, end: number): string;
|
|
4
|
+
export declare function fitWordFragment(token: PreparedLayoutToken, start: number, maxWidth: number): number;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function getWordSliceWidth(token, start, end) {
|
|
2
|
+
return token.graphemePrefixWidths[end] - token.graphemePrefixWidths[start];
|
|
3
|
+
}
|
|
4
|
+
export function getWordSliceText(token, start, end) {
|
|
5
|
+
return token.graphemes.slice(start, end).join('');
|
|
6
|
+
}
|
|
7
|
+
export function fitWordFragment(token, start, maxWidth) {
|
|
8
|
+
const availableWidth = Math.max(0, maxWidth);
|
|
9
|
+
for (let end = start + 1; end <= token.graphemes.length; end++) {
|
|
10
|
+
const width = getWordSliceWidth(token, start, end);
|
|
11
|
+
if (width > availableWidth) {
|
|
12
|
+
return end === start + 1 ? end : end - 1;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return token.graphemes.length;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=layout-text-line-helpers.js.map
|