@weasel-js/text 1.13.0 → 1.14.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/dist/chunk-FODNMBHI.js +2256 -0
- package/dist/chunk-FODNMBHI.js.map +1 -0
- package/dist/index.d.ts +83 -17
- package/dist/index.js +158 -400
- package/dist/index.js.map +1 -1
- package/dist/{test-seams-B0BDm36M.d.ts → test-seams-DHGfE_6p.d.ts} +180 -22
- package/dist/test-seams.d.ts +1 -1
- package/dist/test-seams.js +1 -1
- package/package.json +6 -6
- package/dist/chunk-DOTR55MR.js +0 -1199
- package/dist/chunk-DOTR55MR.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { BREAK_ALLOWED, BREAK_MANDATORY, DEFAULT_TEXT_FILL, DEFAULT_TEXT_STYLE, LAYOUT_CACHE_STRUCTURAL_LIMIT, LAYOUT_CACHE_VARIANT_LIMIT, NO_BREAK, cachedLayoutRuns, fontString, isHardLineBreak, layoutRuns, lineBreakOpportunities, paragraphDirection, registerBidi, resolveAlign, resolveTextStyle } from './chunk-
|
|
3
|
-
import { resolveScreenLength,
|
|
4
|
-
import { faceMetricsOf, resolveFontVariant,
|
|
5
|
-
import { pathToD, pathFromD, mergeRegion, growRegion, poseFrameOf, transformPath, PATH_M, PATH_L, PATH_Z, pathSignedArea, reversePath } from '@weasel-js/geom';
|
|
1
|
+
import { DEFAULT_TAB_SIZE, shapesWithFeature, resolveTextStyle, cachedLayoutRuns, graphemeCount, wrapLines, hangs, emboldenGlyph, layoutRuns, lineDecorations, runFaces, tabAdvance, widthWithTabs, DECORATION_KINDS, decorationRule, faceOutlineD, hasGlyphQuad } from './chunk-FODNMBHI.js';
|
|
2
|
+
export { BREAK_ALLOWED, BREAK_MANDATORY, DEFAULT_DECORATION_METRICS, DEFAULT_TEXT_FILL, DEFAULT_TEXT_STYLE, LAYOUT_CACHE_STRUCTURAL_LIMIT, LAYOUT_CACHE_VARIANT_LIMIT, NO_BREAK, QUAD_STRIDE, cachedLayoutRuns, cachedPaintRuns, decorationMetrics, dropRuns, emboldenGlyphD, fontString, isHardLineBreak, layoutRuns, lineBreakOpportunities, paintRuns, paragraphDirection, registerBidi, registerShaper, resolveAlign, resolveTextStyle, syntheticBoldOffset } from './chunk-FODNMBHI.js';
|
|
3
|
+
import { resolveScreenLength, paintKey, strokeKey } from '@weasel-js/paint';
|
|
4
|
+
import { faceMetricsOf, resolveFontVariant, loadFontOutlines, glyphGeneration, faceMetricsFor, outlineStatus, cssFamilyName } from '@weasel-js/font';
|
|
6
5
|
import { MIXED, deepEqual } from '@weasel-js/registry';
|
|
7
6
|
export { MIXED } from '@weasel-js/registry';
|
|
7
|
+
import { transformPath, joinPaths, pathFromD, pathSignedArea, reversePath, poseFrameOf, originFor, coordsFrom, PATH_M, PATH_L, PATH_Z } from '@weasel-js/geom';
|
|
8
8
|
|
|
9
9
|
// src/runs.ts
|
|
10
10
|
function toRuns(input) {
|
|
@@ -173,10 +173,18 @@ var TITLE = /* @__PURE__ */ new Map([
|
|
|
173
173
|
[8180, "\u038F\u0345"],
|
|
174
174
|
[8183, "\u03A9\u0342\u0345"]
|
|
175
175
|
]);
|
|
176
|
-
function titlecase(ch) {
|
|
176
|
+
function titlecase(ch, locale) {
|
|
177
177
|
const cp = ch.codePointAt(0);
|
|
178
178
|
if (cp >= 8064 && cp <= 8111) return String.fromCodePoint(cp | 8);
|
|
179
|
-
return TITLE.get(cp) ?? ch.
|
|
179
|
+
return TITLE.get(cp) ?? ch.toLocaleUpperCase(locale);
|
|
180
|
+
}
|
|
181
|
+
function caseLocale(lang) {
|
|
182
|
+
if (!lang) return void 0;
|
|
183
|
+
try {
|
|
184
|
+
return Intl.getCanonicalLocales(lang)[0];
|
|
185
|
+
} catch {
|
|
186
|
+
return void 0;
|
|
187
|
+
}
|
|
180
188
|
}
|
|
181
189
|
var WORD_FALLBACK = /[\p{L}\p{N}][\p{L}\p{N}\p{M}\p{Pc}'’]*/gu;
|
|
182
190
|
function wordStarts(text) {
|
|
@@ -194,13 +202,15 @@ function alignsPerCodePoint(texts, whole, map) {
|
|
|
194
202
|
for (const t of texts) for (const ch of t) n += map(ch).length;
|
|
195
203
|
return n === whole.length;
|
|
196
204
|
}
|
|
197
|
-
|
|
198
|
-
var lower = (ch) => ch.toLowerCase();
|
|
199
|
-
function transformRunTexts(texts, transforms) {
|
|
205
|
+
function transformRunTexts(texts, transforms, langs = []) {
|
|
200
206
|
if (transforms.every((t) => t === "none")) return texts.map((text) => ({ text }));
|
|
207
|
+
const locales = texts.map((_, i) => caseLocale(langs[i]));
|
|
208
|
+
const shared = locales.every((l) => l === locales[0]) ? locales[0] : void 0;
|
|
209
|
+
const upper = (ch, l = shared) => ch.toLocaleUpperCase(l);
|
|
210
|
+
const lower = (ch, l = shared) => ch.toLocaleLowerCase(l);
|
|
201
211
|
const full = texts.join("");
|
|
202
|
-
const wholeUpper = transforms.includes("uppercase") ? full.
|
|
203
|
-
const wholeLower = transforms.includes("lowercase") ? full.
|
|
212
|
+
const wholeUpper = transforms.includes("uppercase") ? full.toLocaleUpperCase(shared) : "";
|
|
213
|
+
const wholeLower = transforms.includes("lowercase") ? full.toLocaleLowerCase(shared) : "";
|
|
204
214
|
const upperAligned = wholeUpper !== "" && alignsPerCodePoint(texts, wholeUpper, upper);
|
|
205
215
|
const lowerAligned = wholeLower !== "" && alignsPerCodePoint(texts, wholeLower, lower);
|
|
206
216
|
const starts = transforms.includes("capitalize") ? wordStarts(full) : null;
|
|
@@ -211,6 +221,8 @@ function transformRunTexts(texts, transforms) {
|
|
|
211
221
|
for (let i = 0; i < texts.length; i++) {
|
|
212
222
|
const source = texts[i];
|
|
213
223
|
const t = transforms[i] ?? "none";
|
|
224
|
+
const locale = locales[i];
|
|
225
|
+
const own = locale === shared;
|
|
214
226
|
let text = "";
|
|
215
227
|
const srcStarts = [];
|
|
216
228
|
const srcEnds = [];
|
|
@@ -221,11 +233,11 @@ function transformRunTexts(texts, transforms) {
|
|
|
221
233
|
const lo = lower(ch);
|
|
222
234
|
let piece = ch;
|
|
223
235
|
if (t === "uppercase") {
|
|
224
|
-
piece = upperAligned ? wholeUpper.slice(upOff, upOff + up.length) :
|
|
236
|
+
piece = upperAligned && own ? wholeUpper.slice(upOff, upOff + up.length) : upper(ch, locale);
|
|
225
237
|
} else if (t === "lowercase") {
|
|
226
|
-
piece = lowerAligned ? wholeLower.slice(lowOff, lowOff + lo.length) :
|
|
227
|
-
} else if (t === "capitalize" && starts.has(base + local) &&
|
|
228
|
-
piece = titlecase(ch);
|
|
238
|
+
piece = lowerAligned && own ? wholeLower.slice(lowOff, lowOff + lo.length) : lower(ch, locale);
|
|
239
|
+
} else if (t === "capitalize" && starts.has(base + local) && upper(ch, locale) !== ch) {
|
|
240
|
+
piece = titlecase(ch, locale);
|
|
229
241
|
}
|
|
230
242
|
upOff += up.length;
|
|
231
243
|
lowOff += lo.length;
|
|
@@ -252,10 +264,11 @@ function smallCapsScale(face) {
|
|
|
252
264
|
function smallCapsScaleFor(family, weight, style) {
|
|
253
265
|
return smallCapsScale(faceMetricsOf(resolveFontVariant(family, weight, style)));
|
|
254
266
|
}
|
|
255
|
-
function isSmallCapsLetter(ch) {
|
|
256
|
-
return ch.
|
|
267
|
+
function isSmallCapsLetter(ch, lang) {
|
|
268
|
+
return ch.toLocaleUpperCase(caseLocale(lang)) !== ch;
|
|
257
269
|
}
|
|
258
|
-
function smallCapsText(text, srcMap) {
|
|
270
|
+
function smallCapsText(text, srcMap, lang) {
|
|
271
|
+
const locale = caseLocale(lang);
|
|
259
272
|
let out = "";
|
|
260
273
|
const small = [];
|
|
261
274
|
const starts = [];
|
|
@@ -264,8 +277,9 @@ function smallCapsText(text, srcMap) {
|
|
|
264
277
|
let anySmall = false;
|
|
265
278
|
let at = 0;
|
|
266
279
|
for (const ch of text) {
|
|
267
|
-
const
|
|
268
|
-
const
|
|
280
|
+
const capital = ch.toLocaleUpperCase(locale);
|
|
281
|
+
const shrink = capital !== ch;
|
|
282
|
+
const piece = shrink ? capital : ch;
|
|
269
283
|
if (piece.length !== ch.length) resized = true;
|
|
270
284
|
anySmall ||= shrink;
|
|
271
285
|
const start = srcMap ? srcMap.starts[at] : at;
|
|
@@ -284,6 +298,7 @@ function smallCapsText(text, srcMap) {
|
|
|
284
298
|
}
|
|
285
299
|
|
|
286
300
|
// src/runs/resolveRuns.ts
|
|
301
|
+
var SMALL_CAPS_FEATURES = Object.freeze(["smcp"]);
|
|
287
302
|
var SCRIPT_METRICS = Object.freeze({
|
|
288
303
|
super: Object.freeze({ size: 0.583, shift: 0.333 }),
|
|
289
304
|
sub: Object.freeze({ size: 0.583, shift: -0.333 })
|
|
@@ -304,6 +319,11 @@ function scriptMetrics(face) {
|
|
|
304
319
|
function scriptMetricsFor(family, weight, style) {
|
|
305
320
|
return scriptMetrics(faceMetricsOf(resolveFontVariant(family, weight, style)));
|
|
306
321
|
}
|
|
322
|
+
function halfLeadingFor(family, weight, style, fontSize, lineHeight) {
|
|
323
|
+
const m = faceMetricsOf(resolveFontVariant(family, weight, style));
|
|
324
|
+
if (m?.ascent === void 0 || m.descent === void 0) return void 0;
|
|
325
|
+
return (lineHeight - m.ascent - m.descent) / 2 * fontSize;
|
|
326
|
+
}
|
|
307
327
|
function numericWeight(w) {
|
|
308
328
|
if (typeof w === "number") return w;
|
|
309
329
|
if (w === "bold" || w === "bolder") return 700;
|
|
@@ -321,15 +341,20 @@ function resolveRunFace(run, style) {
|
|
|
321
341
|
fontStyle: run.italic ? "italic" : style.fontStyle
|
|
322
342
|
};
|
|
323
343
|
}
|
|
344
|
+
var NOTHING = Object.freeze({});
|
|
324
345
|
function resolveRuns(runs, style, viewScale = 1) {
|
|
325
346
|
const out = [];
|
|
326
|
-
|
|
347
|
+
let transformed = false;
|
|
348
|
+
for (const r of runs) if ((r.textTransform ?? style.textTransform) !== "none") transformed = true;
|
|
349
|
+
const shown = transformed ? transformRunTexts(
|
|
327
350
|
runs.map((r) => r.text),
|
|
328
|
-
runs.map((r) => r.textTransform ?? style.textTransform)
|
|
329
|
-
|
|
351
|
+
runs.map((r) => r.textTransform ?? style.textTransform),
|
|
352
|
+
runs.map((r) => r.lang ?? style.lang)
|
|
353
|
+
) : null;
|
|
330
354
|
for (let i = 0; i < runs.length; i++) {
|
|
331
355
|
const run = runs[i];
|
|
332
|
-
let
|
|
356
|
+
let text = shown ? shown[i].text : run.text;
|
|
357
|
+
let srcMap = shown?.[i].srcMap;
|
|
333
358
|
const scriptKey = run.script ?? style.script;
|
|
334
359
|
const { fontFamily, fontWeight, fontStyle } = resolveRunFace(run, style);
|
|
335
360
|
const script = scriptKey ? scriptMetricsFor(fontFamily, fontWeight, fontStyle)[scriptKey] : void 0;
|
|
@@ -338,8 +363,12 @@ function resolveRuns(runs, style, viewScale = 1) {
|
|
|
338
363
|
const relative = run.fontSize === void 0 && scale !== 1;
|
|
339
364
|
const fontSize = run.fontSize !== void 0 ? resolveScreenLength(run.fontSize, viewScale) : style.fontSize * scale;
|
|
340
365
|
let sizeMap;
|
|
341
|
-
|
|
342
|
-
|
|
366
|
+
let fontFeatures;
|
|
367
|
+
const smallCaps = (run.fontVariantCaps ?? style.fontVariantCaps) === "small-caps";
|
|
368
|
+
if (smallCaps && shapesWithFeature(fontFamily, fontWeight, fontStyle, "smcp")) {
|
|
369
|
+
fontFeatures = SMALL_CAPS_FEATURES;
|
|
370
|
+
} else if (smallCaps) {
|
|
371
|
+
const caps = smallCapsText(text, srcMap, run.lang ?? style.lang);
|
|
343
372
|
text = caps.text;
|
|
344
373
|
srcMap = caps.srcMap;
|
|
345
374
|
if (caps.small) {
|
|
@@ -347,93 +376,33 @@ function resolveRuns(runs, style, viewScale = 1) {
|
|
|
347
376
|
sizeMap = caps.small.map((s) => s ? small : fontSize);
|
|
348
377
|
}
|
|
349
378
|
}
|
|
379
|
+
const lang = run.lang ?? style.lang;
|
|
350
380
|
out.push({
|
|
351
381
|
text,
|
|
352
|
-
...srcMap ? { srcMap } :
|
|
382
|
+
...srcMap ? { srcMap } : NOTHING,
|
|
353
383
|
fontFamily,
|
|
354
384
|
fontSize,
|
|
355
|
-
...sizeMap ? { sizeMap } :
|
|
385
|
+
...sizeMap ? { sizeMap } : NOTHING,
|
|
356
386
|
fontWeight,
|
|
357
387
|
fontStyle,
|
|
358
388
|
fill: run.fill !== void 0 ? run.fill : style.fill,
|
|
359
389
|
// Unlike the decorations below, a run's stroke *replaces* the node's
|
|
360
390
|
// rather than adding to it — there is only one outline to paint.
|
|
361
|
-
...(run.stroke ?? style.stroke) !== void 0 ? { stroke: run.stroke ?? style.stroke } :
|
|
391
|
+
...(run.stroke ?? style.stroke) !== void 0 ? { stroke: run.stroke ?? style.stroke } : NOTHING,
|
|
362
392
|
letterSpacing: run.letterSpacing !== void 0 ? resolveScreenLength(run.letterSpacing, viewScale) : style.letterSpacing,
|
|
363
393
|
// `||`, not `??`: run-level decorations are additive over the node style.
|
|
364
394
|
underline: run.underline || style.underline,
|
|
365
395
|
strikethrough: run.strikethrough || style.strikethrough,
|
|
366
396
|
overline: run.overline || style.overline,
|
|
367
397
|
baselineShift: shiftEm * style.fontSize,
|
|
368
|
-
...relative ? { strutSize: style.fontSize } :
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// src/layout/decorationMetrics.ts
|
|
375
|
-
var DECORATION_KINDS = ["underline", "strikethrough", "overline"];
|
|
376
|
-
var DEFAULT_DECORATION_METRICS = Object.freeze({
|
|
377
|
-
underline: Object.freeze({ offset: 0.1, thickness: 0.05 }),
|
|
378
|
-
strikethrough: Object.freeze({ offset: -0.3, thickness: 0.05 }),
|
|
379
|
-
overline: Object.freeze({ offset: -0.9, thickness: 0.05 })
|
|
380
|
-
});
|
|
381
|
-
var resolved = /* @__PURE__ */ new WeakMap();
|
|
382
|
-
function decorationMetrics(face) {
|
|
383
|
-
if (!face) return DEFAULT_DECORATION_METRICS;
|
|
384
|
-
let out = resolved.get(face);
|
|
385
|
-
if (!out) {
|
|
386
|
-
const underline = face.underline ?? DEFAULT_DECORATION_METRICS.underline;
|
|
387
|
-
out = Object.freeze({
|
|
388
|
-
underline,
|
|
389
|
-
strikethrough: face.strikethrough ?? DEFAULT_DECORATION_METRICS.strikethrough,
|
|
390
|
-
overline: Object.freeze({ offset: DEFAULT_DECORATION_METRICS.overline.offset, thickness: underline.thickness })
|
|
398
|
+
...relative ? { strutSize: style.fontSize } : NOTHING,
|
|
399
|
+
...run.rotate && Number.isFinite(run.rotate) ? { rotate: run.rotate } : NOTHING,
|
|
400
|
+
...fontFeatures ? { fontFeatures } : NOTHING,
|
|
401
|
+
...lang ? { lang } : NOTHING
|
|
391
402
|
});
|
|
392
|
-
resolved.set(face, out);
|
|
393
403
|
}
|
|
394
404
|
return out;
|
|
395
405
|
}
|
|
396
|
-
function decorationRule(kind, baselineY, fontSize, face) {
|
|
397
|
-
const m = decorationMetrics(face)[kind];
|
|
398
|
-
const y0 = baselineY + fontSize * m.offset;
|
|
399
|
-
return { y0, y1: y0 + fontSize * m.thickness };
|
|
400
|
-
}
|
|
401
|
-
function lineDecorations(runs, faceOf, paintOf) {
|
|
402
|
-
const rules = [];
|
|
403
|
-
let open = null;
|
|
404
|
-
const close = () => {
|
|
405
|
-
const s = open;
|
|
406
|
-
open = null;
|
|
407
|
-
if (!s || s.x1 <= s.x0) return;
|
|
408
|
-
for (const kind of DECORATION_KINDS) {
|
|
409
|
-
if (!s.style[kind]) continue;
|
|
410
|
-
const { y0, y1 } = decorationRule(kind, s.baselineY, s.style.fontSize, s.face);
|
|
411
|
-
rules.push({ kind, x0: s.x0, y0, x1: s.x1, y1, run: s.run });
|
|
412
|
-
}
|
|
413
|
-
};
|
|
414
|
-
return {
|
|
415
|
-
add(g) {
|
|
416
|
-
const style = runs[g.run];
|
|
417
|
-
const paint = style.underline || style.strikethrough || style.overline ? paintOf(g.run) : null;
|
|
418
|
-
if (paint === null) {
|
|
419
|
-
if (open !== null) close();
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
const face = faceOf(g.run);
|
|
423
|
-
const joins = open !== null && DECORATION_KINDS.every((kind) => open.style[kind] === style[kind]) && open.style.fontSize === style.fontSize && open.face === face && open.baselineY === g.baselineY && open.paint === paint;
|
|
424
|
-
if (joins) {
|
|
425
|
-
open.x1 = g.x + g.step;
|
|
426
|
-
} else {
|
|
427
|
-
close();
|
|
428
|
-
open = { run: g.run, style, face, paint, baselineY: g.baselineY, x0: g.x, x1: g.x + g.step };
|
|
429
|
-
}
|
|
430
|
-
},
|
|
431
|
-
end() {
|
|
432
|
-
close();
|
|
433
|
-
return rules;
|
|
434
|
-
}
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
406
|
|
|
438
407
|
// src/measure/verticalAlign.ts
|
|
439
408
|
function verticalAlignOffset(align, boxHeight, textHeight) {
|
|
@@ -469,260 +438,6 @@ function layoutTextPose(pose, scale = 1) {
|
|
|
469
438
|
y: pose.y + verticalAlignOffset(pose.verticalAlign, pose.height, laid.bounds.height)
|
|
470
439
|
};
|
|
471
440
|
}
|
|
472
|
-
var SYNTHETIC_BOLD_TOLERANCE = 5e-4;
|
|
473
|
-
var CACHE_LIMIT = 4096;
|
|
474
|
-
var cache = /* @__PURE__ */ new Map();
|
|
475
|
-
var dCache = /* @__PURE__ */ new Map();
|
|
476
|
-
function syntheticBoldOffset(resolved2) {
|
|
477
|
-
if (!resolved2.synthetic.bold) return null;
|
|
478
|
-
const { source } = resolved2;
|
|
479
|
-
if (source !== "atlas" && source !== "canvas") return null;
|
|
480
|
-
const { family, weight, style } = resolved2.resolved;
|
|
481
|
-
const growth = syntheticBoldGrowth(source, family, weight, style);
|
|
482
|
-
if (growth === void 0 || !(growth > 0)) return null;
|
|
483
|
-
return { growth, join: source === "atlas" ? "miter" : "round" };
|
|
484
|
-
}
|
|
485
|
-
function emboldenGlyph(em, d, resolved2) {
|
|
486
|
-
const offset = syntheticBoldOffset(resolved2);
|
|
487
|
-
if (offset === null) return em;
|
|
488
|
-
const key = `${offset.growth}|${offset.join}|${d}`;
|
|
489
|
-
let grown = cache.get(key);
|
|
490
|
-
if (grown) return grown;
|
|
491
|
-
grown = mergeRegion(
|
|
492
|
-
growRegion(em, offset.growth, { join: offset.join, tolerance: SYNTHETIC_BOLD_TOLERANCE }),
|
|
493
|
-
{ tolerance: SYNTHETIC_BOLD_TOLERANCE }
|
|
494
|
-
);
|
|
495
|
-
if (cache.size >= CACHE_LIMIT) cache.clear();
|
|
496
|
-
cache.set(key, grown);
|
|
497
|
-
return grown;
|
|
498
|
-
}
|
|
499
|
-
function emboldenGlyphD(d, resolved2) {
|
|
500
|
-
const offset = syntheticBoldOffset(resolved2);
|
|
501
|
-
if (offset === null) return null;
|
|
502
|
-
const tag = `bold:${offset.growth}:${offset.join}`;
|
|
503
|
-
const key = `${tag}|${d}`;
|
|
504
|
-
let grown = dCache.get(key);
|
|
505
|
-
if (grown === void 0) {
|
|
506
|
-
grown = pathToD(emboldenGlyph(pathFromD(d), d, resolved2));
|
|
507
|
-
if (dCache.size >= CACHE_LIMIT) dCache.clear();
|
|
508
|
-
dCache.set(key, grown);
|
|
509
|
-
}
|
|
510
|
-
return { d: grown, tag };
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// src/layout/paintRuns.ts
|
|
514
|
-
function glyphOutlineD(g, run, minSize) {
|
|
515
|
-
const outlineOnly = g.resolved.source === "outline";
|
|
516
|
-
if (!outlineOnly) {
|
|
517
|
-
if (minSize === void 0) return null;
|
|
518
|
-
if (run.fontSize < minSize && !strokeHasInk(run.stroke)) return null;
|
|
519
|
-
}
|
|
520
|
-
const d = faceOutlineD(g);
|
|
521
|
-
if (d === null) return null;
|
|
522
|
-
const { family, weight, style } = g.resolved.resolved;
|
|
523
|
-
const key = `${family}|${weight}|${style}|${g.cp}`;
|
|
524
|
-
if (outlineOnly || !g.resolved.synthetic.bold) return { d, key };
|
|
525
|
-
const bold = emboldenGlyphD(d, g.resolved);
|
|
526
|
-
return bold && { d: bold.d, key: `${key}|${bold.tag}` };
|
|
527
|
-
}
|
|
528
|
-
function faceOutlineD(g) {
|
|
529
|
-
if (g.glyph?.color) return null;
|
|
530
|
-
const { family, weight, style } = g.resolved.resolved;
|
|
531
|
-
if (g.resolved.source !== "outline" && g.glyph === null) return null;
|
|
532
|
-
return glyphOutline(family, weight, style, g.cp);
|
|
533
|
-
}
|
|
534
|
-
var QUAD_STRIDE = 9;
|
|
535
|
-
function hasGlyphQuad(g) {
|
|
536
|
-
const { font, glyph } = g;
|
|
537
|
-
return font !== null && glyph !== null && glyph.width !== 0 && glyph.page >= 0;
|
|
538
|
-
}
|
|
539
|
-
function writeGlyphQuad(g, out, at) {
|
|
540
|
-
const font = g.font, glyph = g.glyph;
|
|
541
|
-
const scale = g.fontSize / g.metrics.size;
|
|
542
|
-
const x0 = g.x + glyph.xoffset * scale;
|
|
543
|
-
const y0 = g.baselineY + (glyph.yoffset - g.metrics.base) * scale;
|
|
544
|
-
out[at] = x0;
|
|
545
|
-
out[at + 1] = y0;
|
|
546
|
-
out[at + 2] = x0 + glyph.width * scale;
|
|
547
|
-
out[at + 3] = y0 + glyph.height * scale;
|
|
548
|
-
out[at + 4] = glyph.x / font.common.scaleW;
|
|
549
|
-
out[at + 5] = glyph.y / font.common.scaleH;
|
|
550
|
-
out[at + 6] = (glyph.x + glyph.width) / font.common.scaleW;
|
|
551
|
-
out[at + 7] = (glyph.y + glyph.height) / font.common.scaleH;
|
|
552
|
-
out[at + 8] = g.baselineY;
|
|
553
|
-
}
|
|
554
|
-
var groupViews = {
|
|
555
|
-
quads: lazy("quads", (group) => {
|
|
556
|
-
const d = group.quadData;
|
|
557
|
-
const out = [];
|
|
558
|
-
for (let at = 0, k = 0; at < d.length; at += QUAD_STRIDE, k++) {
|
|
559
|
-
const q = {
|
|
560
|
-
x0: d[at],
|
|
561
|
-
y0: d[at + 1],
|
|
562
|
-
x1: d[at + 2],
|
|
563
|
-
y1: d[at + 3],
|
|
564
|
-
u0: d[at + 4],
|
|
565
|
-
v0: d[at + 5],
|
|
566
|
-
u1: d[at + 6],
|
|
567
|
-
v1: d[at + 7],
|
|
568
|
-
baselineY: d[at + 8]
|
|
569
|
-
};
|
|
570
|
-
if (group.colorGlyphs) q.colorGlyph = group.colorGlyphs[k];
|
|
571
|
-
out.push(q);
|
|
572
|
-
}
|
|
573
|
-
return out;
|
|
574
|
-
})
|
|
575
|
-
};
|
|
576
|
-
function runFaces(runs) {
|
|
577
|
-
const faces = runs.map((r) => faceMetricsOf(resolveFontVariant(r.fontFamily, r.fontWeight, r.fontStyle)));
|
|
578
|
-
return (run) => faces[run];
|
|
579
|
-
}
|
|
580
|
-
var isDecorated = (r) => r.underline || r.strikethrough || r.overline;
|
|
581
|
-
function groupFor(groups, g, run, source, last) {
|
|
582
|
-
const { family, weight, style } = g.resolved.resolved;
|
|
583
|
-
const { synthetic } = g.resolved;
|
|
584
|
-
const page = source === "outline" ? 0 : g.glyph.page;
|
|
585
|
-
if (last.group !== null && last.run === g.run && last.source === source && last.page === page && last.family === family && last.weight === weight && last.style === style && last.bold === synthetic.bold && last.italic === synthetic.italic) {
|
|
586
|
-
return last.group;
|
|
587
|
-
}
|
|
588
|
-
const stroke = source === "outline" && strokeHasInk(run.stroke) ? run.stroke : void 0;
|
|
589
|
-
const key = `${family}|${weight}|${style}|${synthetic.bold ? 1 : 0}${synthetic.italic ? 1 : 0}|${paintKey(run.fill?.paint)}|${strokeKey(stroke)}|${source}|${page}`;
|
|
590
|
-
let group = groups.get(key);
|
|
591
|
-
if (!group) {
|
|
592
|
-
group = {
|
|
593
|
-
family,
|
|
594
|
-
weight,
|
|
595
|
-
style,
|
|
596
|
-
synthetic: { ...synthetic },
|
|
597
|
-
source,
|
|
598
|
-
page,
|
|
599
|
-
fill: run.fill,
|
|
600
|
-
...stroke ? { stroke } : {},
|
|
601
|
-
quadData: [],
|
|
602
|
-
...source === "color" ? { colorGlyphs: [] } : {},
|
|
603
|
-
glyphs: []
|
|
604
|
-
};
|
|
605
|
-
groups.set(key, group);
|
|
606
|
-
}
|
|
607
|
-
last.group = group;
|
|
608
|
-
last.run = g.run;
|
|
609
|
-
last.family = family;
|
|
610
|
-
last.weight = weight;
|
|
611
|
-
last.style = style;
|
|
612
|
-
last.bold = synthetic.bold;
|
|
613
|
-
last.italic = synthetic.italic;
|
|
614
|
-
last.source = source;
|
|
615
|
-
last.page = page;
|
|
616
|
-
return group;
|
|
617
|
-
}
|
|
618
|
-
function paintRuns(laid, runs, opts = {}) {
|
|
619
|
-
const groups = /* @__PURE__ */ new Map();
|
|
620
|
-
const last = {
|
|
621
|
-
group: null,
|
|
622
|
-
run: -1,
|
|
623
|
-
family: "",
|
|
624
|
-
weight: 0,
|
|
625
|
-
style: "normal",
|
|
626
|
-
bold: false,
|
|
627
|
-
italic: false,
|
|
628
|
-
source: "atlas",
|
|
629
|
-
page: 0
|
|
630
|
-
};
|
|
631
|
-
const decorations = [];
|
|
632
|
-
const faceOf = runs.some(isDecorated) ? runFaces(runs) : null;
|
|
633
|
-
const rulePaint = (run) => fillHasInk(runs[run].fill) ? paintKey(runs[run].fill.paint) : null;
|
|
634
|
-
const cursor = blankGlyph();
|
|
635
|
-
let glyphCount = 0;
|
|
636
|
-
for (const line of laid.lines) glyphCount += LineBox.count(line);
|
|
637
|
-
let sized = null;
|
|
638
|
-
let sizedAt = 0;
|
|
639
|
-
for (const line of laid.lines) {
|
|
640
|
-
const span = LineBox.span(line);
|
|
641
|
-
const count = LineBox.count(line);
|
|
642
|
-
const rules = faceOf && lineDecorations(runs, faceOf, rulePaint);
|
|
643
|
-
for (let slot = 0; slot < count; slot++) {
|
|
644
|
-
const g = span ? span.cols.glyph(span.start, span.cols.visual(span.start, slot), cursor) : line.glyphs[slot];
|
|
645
|
-
rules?.add(g);
|
|
646
|
-
const run = runs[g.run];
|
|
647
|
-
const outline = glyphOutlineD(g, run, opts.outlineMinSize);
|
|
648
|
-
if (!fillHasInk(run.fill) && !(outline !== null && strokeHasInk(run.stroke))) continue;
|
|
649
|
-
if (outline !== null) {
|
|
650
|
-
groupFor(groups, g, run, "outline", last).glyphs.push({
|
|
651
|
-
...outline,
|
|
652
|
-
x: g.x,
|
|
653
|
-
baselineY: g.baselineY,
|
|
654
|
-
scale: g.fontSize
|
|
655
|
-
});
|
|
656
|
-
continue;
|
|
657
|
-
}
|
|
658
|
-
if (!hasGlyphQuad(g)) continue;
|
|
659
|
-
const group = groupFor(groups, g, run, g.glyph.color ? "color" : g.resolved.source, last);
|
|
660
|
-
if (sized === null) {
|
|
661
|
-
sized = group;
|
|
662
|
-
group.quadData = doubles(glyphCount * QUAD_STRIDE);
|
|
663
|
-
}
|
|
664
|
-
const quads = group.quadData;
|
|
665
|
-
if (group === sized) {
|
|
666
|
-
writeGlyphQuad(g, quads, sizedAt);
|
|
667
|
-
sizedAt += QUAD_STRIDE;
|
|
668
|
-
} else {
|
|
669
|
-
writeGlyphQuad(g, quads, quads.length);
|
|
670
|
-
}
|
|
671
|
-
group.colorGlyphs?.push(g.glyph);
|
|
672
|
-
}
|
|
673
|
-
if (!rules) continue;
|
|
674
|
-
for (const rule of rules.end()) {
|
|
675
|
-
const { run, ...rect } = rule;
|
|
676
|
-
decorations.push({ ...rect, fill: runs[run].fill });
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
if (sized !== null) sized.quadData.length = sizedAt;
|
|
680
|
-
const out = [];
|
|
681
|
-
for (const group of groups.values()) out.push(Object.defineProperties(group, groupViews));
|
|
682
|
-
return { groups: out, decorations };
|
|
683
|
-
}
|
|
684
|
-
var PAINT_CACHE_VARIANT_LIMIT = 32;
|
|
685
|
-
var own = /* @__PURE__ */ new WeakMap();
|
|
686
|
-
var shared = /* @__PURE__ */ new WeakMap();
|
|
687
|
-
function outlineBucket(runs, min) {
|
|
688
|
-
if (min === void 0) return -1;
|
|
689
|
-
const sizes = /* @__PURE__ */ new Set();
|
|
690
|
-
for (const r of runs) sizes.add(r.fontSize);
|
|
691
|
-
let n = 0;
|
|
692
|
-
for (const size of sizes) if (size >= min) n++;
|
|
693
|
-
return n;
|
|
694
|
-
}
|
|
695
|
-
function variantKey(runs, opts) {
|
|
696
|
-
let out = `${outlineBucket(runs, opts.outlineMinSize)}`;
|
|
697
|
-
for (const r of runs) {
|
|
698
|
-
out += `|${r.underline ? 1 : 0}${r.strikethrough ? 1 : 0}${r.overline ? 1 : 0}${paintKey(r.fill?.paint)}|${strokeKey(r.stroke)}`;
|
|
699
|
-
}
|
|
700
|
-
return out;
|
|
701
|
-
}
|
|
702
|
-
function cachedPaintRuns(laid, runs, opts = {}) {
|
|
703
|
-
const generation = glyphGeneration();
|
|
704
|
-
const key = variantKey(runs, opts);
|
|
705
|
-
const fresh = (p) => p !== void 0 && p.generation === generation && p.key === key;
|
|
706
|
-
let byLayout = own.get(runs);
|
|
707
|
-
if (byLayout === void 0) {
|
|
708
|
-
byLayout = /* @__PURE__ */ new WeakMap();
|
|
709
|
-
own.set(runs, byLayout);
|
|
710
|
-
}
|
|
711
|
-
const mine = byLayout.get(laid);
|
|
712
|
-
if (fresh(mine)) return mine.painted;
|
|
713
|
-
let byKey = shared.get(laid);
|
|
714
|
-
if (byKey === void 0) {
|
|
715
|
-
byKey = /* @__PURE__ */ new Map();
|
|
716
|
-
shared.set(laid, byKey);
|
|
717
|
-
}
|
|
718
|
-
let painting = byKey.get(key);
|
|
719
|
-
byKey.delete(key);
|
|
720
|
-
if (!fresh(painting)) painting = { generation, key, painted: paintRuns(laid, runs, opts) };
|
|
721
|
-
if (byKey.size >= PAINT_CACHE_VARIANT_LIMIT) byKey.delete(byKey.keys().next().value);
|
|
722
|
-
byKey.set(key, painting);
|
|
723
|
-
byLayout.set(laid, painting);
|
|
724
|
-
return painting.painted;
|
|
725
|
-
}
|
|
726
441
|
|
|
727
442
|
// src/measure/measureText.ts
|
|
728
443
|
function measuredWidth(ctx, text, style) {
|
|
@@ -764,15 +479,15 @@ function measureText(ctx, text, maxWidth, style) {
|
|
|
764
479
|
|
|
765
480
|
// src/measure/measureTextBounds.ts
|
|
766
481
|
function measureTextBounds(text, style, opts) {
|
|
767
|
-
const
|
|
768
|
-
const runs = resolveRuns([{ text }],
|
|
482
|
+
const resolved = resolveTextStyle(style, void 0, opts?.scale);
|
|
483
|
+
const runs = resolveRuns([{ text }], resolved, opts?.scale);
|
|
769
484
|
const { bounds } = cachedLayoutRuns(runs, {
|
|
770
485
|
maxWidth: opts?.maxWidth ?? Infinity,
|
|
771
|
-
lineHeight: opts?.lineHeight ??
|
|
772
|
-
align:
|
|
773
|
-
direction:
|
|
774
|
-
tabSize:
|
|
775
|
-
});
|
|
486
|
+
lineHeight: opts?.lineHeight ?? resolved.lineHeight,
|
|
487
|
+
align: resolved.align,
|
|
488
|
+
direction: resolved.direction,
|
|
489
|
+
tabSize: resolved.tabSize
|
|
490
|
+
}, "read");
|
|
776
491
|
return { width: bounds.width, height: bounds.height };
|
|
777
492
|
}
|
|
778
493
|
|
|
@@ -799,8 +514,7 @@ function caretCells(cells) {
|
|
|
799
514
|
for (const c of cells) {
|
|
800
515
|
const prev = out[out.length - 1];
|
|
801
516
|
if (prev && (!prev.clusterEnd || prev.srcIndex === c.srcIndex && prev.srcEnd === c.srcEnd)) {
|
|
802
|
-
const x0 = Math.min(prev.x, c.x);
|
|
803
|
-
const x1 = Math.max(prev.x + prev.advance, c.x + c.advance);
|
|
517
|
+
const [x0, x1] = c.advance === 0 ? [prev.x, prev.x + prev.advance] : prev.advance === 0 ? [c.x, c.x + c.advance] : [Math.min(prev.x, c.x), Math.max(prev.x + prev.advance, c.x + c.advance)];
|
|
804
518
|
out[out.length - 1] = {
|
|
805
519
|
...prev,
|
|
806
520
|
x: x0,
|
|
@@ -914,7 +628,11 @@ function runMetrics(run, fontSize, face) {
|
|
|
914
628
|
}
|
|
915
629
|
function layoutMarkdown(runs, maxWidth, fontSize, measure, lineHeightFactor = 1.3, faceOf, tabSize = DEFAULT_TAB_SIZE) {
|
|
916
630
|
if (runs.length === 0) return { lines: [], width: 0, height: 0 };
|
|
917
|
-
const shown = transformRunTexts(
|
|
631
|
+
const shown = transformRunTexts(
|
|
632
|
+
runs.map((r) => r.text),
|
|
633
|
+
runs.map((r) => r.textTransform ?? "none"),
|
|
634
|
+
runs.map((r) => r.lang)
|
|
635
|
+
);
|
|
918
636
|
const segs = [];
|
|
919
637
|
runs.forEach((r, i) => {
|
|
920
638
|
const text = shown[i].text;
|
|
@@ -922,7 +640,7 @@ function layoutMarkdown(runs, maxWidth, fontSize, measure, lineHeightFactor = 1.
|
|
|
922
640
|
segs.push(text === r.text ? r : { ...r, text });
|
|
923
641
|
return;
|
|
924
642
|
}
|
|
925
|
-
const caps = smallCapsText(text);
|
|
643
|
+
const caps = smallCapsText(text, void 0, r.lang);
|
|
926
644
|
if (!caps.small) {
|
|
927
645
|
segs.push({ ...r, text: caps.text });
|
|
928
646
|
return;
|
|
@@ -1127,7 +845,9 @@ var STYLE_KEYS = [
|
|
|
1127
845
|
"baselineShift",
|
|
1128
846
|
"fontScale",
|
|
1129
847
|
"textTransform",
|
|
1130
|
-
"fontVariantCaps"
|
|
848
|
+
"fontVariantCaps",
|
|
849
|
+
"rotate",
|
|
850
|
+
"lang"
|
|
1131
851
|
];
|
|
1132
852
|
var FLAG_KEYS = /* @__PURE__ */ new Set([
|
|
1133
853
|
"bold",
|
|
@@ -1308,9 +1028,9 @@ function setFlagOverRange(runs, style, start, end, key, value) {
|
|
|
1308
1028
|
var FLAGS = ["italic", "underline", "strikethrough", "overline"];
|
|
1309
1029
|
function rangeWeight(range, style) {
|
|
1310
1030
|
const node = numericWeight(style?.fontWeight ?? 400);
|
|
1311
|
-
const
|
|
1312
|
-
if (
|
|
1313
|
-
if (
|
|
1031
|
+
const own = range?.fontWeight;
|
|
1032
|
+
if (own === MIXED) return { fontWeight: MIXED, bold: MIXED };
|
|
1033
|
+
if (own !== void 0) return { fontWeight: own, bold: isBoldWeight(own) };
|
|
1314
1034
|
const flag = range?.bold;
|
|
1315
1035
|
if (flag === MIXED) {
|
|
1316
1036
|
if (node === 700) return { fontWeight: 700, bold: true };
|
|
@@ -1328,19 +1048,22 @@ var OVERRIDES = [
|
|
|
1328
1048
|
"baselineShift",
|
|
1329
1049
|
"fontScale",
|
|
1330
1050
|
"textTransform",
|
|
1331
|
-
"fontVariantCaps"
|
|
1051
|
+
"fontVariantCaps",
|
|
1052
|
+
"rotate",
|
|
1053
|
+
"lang"
|
|
1332
1054
|
];
|
|
1333
1055
|
function effectiveRangeStyle(range, style, paint) {
|
|
1334
|
-
const
|
|
1056
|
+
const resolved = resolveTextStyle(style, paint);
|
|
1335
1057
|
const node = style ?? {};
|
|
1336
1058
|
const out = {
|
|
1337
|
-
fontFamily:
|
|
1338
|
-
fontSize:
|
|
1339
|
-
letterSpacing:
|
|
1340
|
-
textTransform:
|
|
1341
|
-
fontVariantCaps:
|
|
1059
|
+
fontFamily: resolved.fontFamily,
|
|
1060
|
+
fontSize: resolved.fontSize,
|
|
1061
|
+
letterSpacing: resolved.letterSpacing,
|
|
1062
|
+
textTransform: resolved.textTransform,
|
|
1063
|
+
fontVariantCaps: resolved.fontVariantCaps
|
|
1342
1064
|
};
|
|
1343
|
-
if (
|
|
1065
|
+
if (resolved.fill !== null) out.fill = resolved.fill;
|
|
1066
|
+
if (resolved.lang !== void 0) out.lang = resolved.lang;
|
|
1344
1067
|
Object.assign(out, rangeWeight(range, style));
|
|
1345
1068
|
for (const key of FLAGS) {
|
|
1346
1069
|
const on = nodeHasFlag(node, key);
|
|
@@ -1356,6 +1079,14 @@ function effectiveRangeStyle(range, style, paint) {
|
|
|
1356
1079
|
|
|
1357
1080
|
// src/syntheticItalic.ts
|
|
1358
1081
|
var SYNTHETIC_ITALIC_RADIANS = 0.2094;
|
|
1082
|
+
|
|
1083
|
+
// src/glyphTurn.ts
|
|
1084
|
+
function glyphTurn(rotate) {
|
|
1085
|
+
if (!rotate) return UPRIGHT;
|
|
1086
|
+
const rad = rotate * Math.PI / 180;
|
|
1087
|
+
return [Math.cos(rad), Math.sin(rad)];
|
|
1088
|
+
}
|
|
1089
|
+
var UPRIGHT = [1, 0];
|
|
1359
1090
|
var TextOutlinesError = class extends Error {
|
|
1360
1091
|
reason;
|
|
1361
1092
|
family;
|
|
@@ -1437,9 +1168,20 @@ function placedGlyphOutline(g, dx, dy, caller, blankFallback = false) {
|
|
|
1437
1168
|
}
|
|
1438
1169
|
return null;
|
|
1439
1170
|
}
|
|
1171
|
+
return glyphOutlineAt(d, g, dx, dy);
|
|
1172
|
+
}
|
|
1173
|
+
function glyphOutlineAt(d, g, dx, dy) {
|
|
1440
1174
|
const s = g.fontSize;
|
|
1441
1175
|
const shear = g.resolved.synthetic.italic ? Math.tan(SYNTHETIC_ITALIC_RADIANS) : 0;
|
|
1442
|
-
const
|
|
1176
|
+
const [cos, sin] = glyphTurn(g.rotate);
|
|
1177
|
+
const m = [
|
|
1178
|
+
cos * s,
|
|
1179
|
+
sin * s,
|
|
1180
|
+
-(cos * shear + sin) * s,
|
|
1181
|
+
(cos - sin * shear) * s,
|
|
1182
|
+
g.x + dx,
|
|
1183
|
+
g.baselineY + dy
|
|
1184
|
+
];
|
|
1443
1185
|
return transformPath(emboldenGlyph(emGlyph(d), d, g.resolved), m);
|
|
1444
1186
|
}
|
|
1445
1187
|
function colorGlyphError(run, caller) {
|
|
@@ -1452,25 +1194,6 @@ function colorGlyphError(run, caller) {
|
|
|
1452
1194
|
caller
|
|
1453
1195
|
);
|
|
1454
1196
|
}
|
|
1455
|
-
function concat(parts) {
|
|
1456
|
-
let nc = 0, nk = 0;
|
|
1457
|
-
for (const p of parts) {
|
|
1458
|
-
nc += p.commands.length;
|
|
1459
|
-
nk += p.coords.length;
|
|
1460
|
-
}
|
|
1461
|
-
const commands = new Uint8Array(nc);
|
|
1462
|
-
const coords = new Float32Array(nk);
|
|
1463
|
-
let ci = 0, ki = 0;
|
|
1464
|
-
for (const p of parts) {
|
|
1465
|
-
commands.set(p.commands, ci);
|
|
1466
|
-
ci += p.commands.length;
|
|
1467
|
-
coords.set(p.coords, ki);
|
|
1468
|
-
ki += p.coords.length;
|
|
1469
|
-
}
|
|
1470
|
-
return { kind: "polygon", commands, coords, fillRule: "nonzero" };
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
|
-
// src/textToPath.ts
|
|
1474
1197
|
function facesOf(runs) {
|
|
1475
1198
|
const out = /* @__PURE__ */ new Map();
|
|
1476
1199
|
const add = (family, weight, style) => {
|
|
@@ -1501,11 +1224,14 @@ function outlineLayoutInput(data, box, opts, caller) {
|
|
|
1501
1224
|
return input;
|
|
1502
1225
|
}
|
|
1503
1226
|
function ruleContour(x0, y0, x1, y1) {
|
|
1227
|
+
const xy = [x0, y0, x1, y0, x1, y1, x0, y1];
|
|
1228
|
+
const origin = originFor(xy);
|
|
1504
1229
|
return {
|
|
1505
1230
|
kind: "polygon",
|
|
1506
1231
|
commands: new Uint8Array([PATH_M, PATH_L, PATH_L, PATH_L, PATH_Z]),
|
|
1507
|
-
coords:
|
|
1508
|
-
fillRule: "nonzero"
|
|
1232
|
+
coords: coordsFrom(origin, xy),
|
|
1233
|
+
fillRule: "nonzero",
|
|
1234
|
+
...origin ? { origin } : {}
|
|
1509
1235
|
};
|
|
1510
1236
|
}
|
|
1511
1237
|
function walkOutlines(data, pose, opts) {
|
|
@@ -1531,14 +1257,14 @@ function walkOutlines(data, pose, opts) {
|
|
|
1531
1257
|
}
|
|
1532
1258
|
const frame = poseFrameOf(pose);
|
|
1533
1259
|
const paths = parts.map((glyphs, i) => {
|
|
1534
|
-
const path =
|
|
1260
|
+
const path = joinPaths([...glyphs, ...rules[i]]);
|
|
1535
1261
|
if (!frame || path.commands.length === 0) return path;
|
|
1536
1262
|
return transformPath(path, frame);
|
|
1537
1263
|
});
|
|
1538
1264
|
return { paints, paths };
|
|
1539
1265
|
}
|
|
1540
1266
|
function textToPath(data, pose, opts = {}) {
|
|
1541
|
-
return
|
|
1267
|
+
return joinPaths(walkOutlines(data, pose, opts).paths);
|
|
1542
1268
|
}
|
|
1543
1269
|
function textToPathsByPaint(data, pose, opts = {}) {
|
|
1544
1270
|
const { paints, paths } = walkOutlines(data, pose, opts);
|
|
@@ -1561,11 +1287,26 @@ async function loadTextOutlines(data, opts = {}) {
|
|
|
1561
1287
|
}
|
|
1562
1288
|
var MIDDLE_FALLBACK = 0.25;
|
|
1563
1289
|
function textClusters(data, opts = {}) {
|
|
1564
|
-
const
|
|
1290
|
+
const width = opts.width ?? Infinity;
|
|
1291
|
+
const scale = opts.scale ?? 1;
|
|
1292
|
+
const generation = glyphGeneration();
|
|
1293
|
+
const hit = remembered.get(data);
|
|
1294
|
+
if (hit && hit.width === width && hit.scale === scale && hit.generation === generation) return hit.clusters;
|
|
1295
|
+
const clusters = clustersOf(data, width, scale);
|
|
1296
|
+
remembered.set(data, { width, scale, generation, clusters });
|
|
1297
|
+
return clusters;
|
|
1298
|
+
}
|
|
1299
|
+
var remembered = /* @__PURE__ */ new WeakMap();
|
|
1300
|
+
function clustersOf(data, width, scale) {
|
|
1301
|
+
const opts = { scale };
|
|
1302
|
+
const box = { x: 0, y: 0, width, height: 0 };
|
|
1565
1303
|
const { runs, opts: layoutOpts } = outlineLayoutInput(data, box, opts, "textClusters");
|
|
1566
1304
|
const line = layoutRuns(runs, layoutOpts).lines[0];
|
|
1567
1305
|
if (!line || line.cells.length === 0) return [];
|
|
1568
1306
|
const { paints, paintOf } = paintsOf(runs);
|
|
1307
|
+
const deco = lineDecorations(runs, runFaces(runs), (run) => String(paintOf[run]));
|
|
1308
|
+
for (const g of line.glyphs) deco.add(g);
|
|
1309
|
+
const lineRules = deco.end();
|
|
1569
1310
|
const glyphOf = new Array(line.cells.length);
|
|
1570
1311
|
for (const g of line.glyphs) glyphOf[g.cell] = g;
|
|
1571
1312
|
const groups = [];
|
|
@@ -1588,19 +1329,36 @@ function textClusters(data, opts = {}) {
|
|
|
1588
1329
|
}
|
|
1589
1330
|
const paths = [];
|
|
1590
1331
|
parts.forEach((p, k) => {
|
|
1591
|
-
if (p.length > 0) paths.push({ ...paints[k], path:
|
|
1332
|
+
if (p.length > 0) paths.push({ ...paints[k], path: joinPaths(p) });
|
|
1592
1333
|
});
|
|
1334
|
+
const rules = [];
|
|
1335
|
+
for (const r of lineRules) {
|
|
1336
|
+
const x0 = Math.max(r.x0, left);
|
|
1337
|
+
const x1 = Math.min(r.x1, right);
|
|
1338
|
+
if (!(x1 > x0)) continue;
|
|
1339
|
+
const { fill, stroke } = paints[paintOf[r.run]];
|
|
1340
|
+
rules.push({
|
|
1341
|
+
kind: r.kind,
|
|
1342
|
+
x0: x0 - left,
|
|
1343
|
+
x1: x1 - left,
|
|
1344
|
+
y0: r.y0 - line.baselineY,
|
|
1345
|
+
y1: r.y1 - line.baselineY,
|
|
1346
|
+
fill,
|
|
1347
|
+
...stroke ? { stroke } : {}
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1593
1350
|
const first = glyphs[0];
|
|
1594
1351
|
const xHeight = faceMetricsOf(first.resolved)?.xHeight;
|
|
1595
1352
|
return {
|
|
1596
1353
|
x: left - line.x0,
|
|
1597
1354
|
advance: right - left,
|
|
1598
1355
|
paths,
|
|
1599
|
-
middle: (xHeight === void 0 ? MIDDLE_FALLBACK : xHeight / 2) * first.fontSize
|
|
1356
|
+
middle: (xHeight === void 0 ? MIDDLE_FALLBACK : xHeight / 2) * first.fontSize,
|
|
1357
|
+
rules
|
|
1600
1358
|
};
|
|
1601
1359
|
});
|
|
1602
1360
|
}
|
|
1603
1361
|
|
|
1604
|
-
export {
|
|
1362
|
+
export { MARKDOWN_RUN_GRAMMAR, SCRIPT_METRICS, SMALL_CAPS_SCALE, SYNTHETIC_ITALIC_RADIANS, TextOutlinesError, applyStyleToRange, caretIndexAt, createMarkdownRenderer, defaultLabelTextRenderer, effectiveRangeStyle, fitTextPose, glyphOutlineAt, glyphTurn, halfLeadingFor, isBoldWeight, isSmallCapsLetter, isTextData, layoutMarkdown, layoutTextPose, loadTextOutlines, markdownToRuns, measureText, measureTextBounds, measuredWidth, nodeHasFlag, numericWeight, patchRangeStyle, pointInTextPose, rangeWeight, renderLabel, resolveRunFace, resolveRuns, runsCarryStyling, runsToMarkdown, runsToPlainText, scriptMetrics, scriptMetricsFor, setFlagOverRange, smallCapsScale, smallCapsScaleFor, smallCapsText, styleAtRange, supersededKey, textClusters, textLineBoxes, textPoseLayoutInput, textToPath, textToPathsByPaint, toRuns, transformRunTexts, unboldPatch, verticalAlignOffset };
|
|
1605
1363
|
//# sourceMappingURL=index.js.map
|
|
1606
1364
|
//# sourceMappingURL=index.js.map
|