@weasel-js/text 1.13.0-rc.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 +93 -17
- package/dist/index.js +162 -348
- 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-ADRQIIHS.js +0 -1043
- package/dist/chunk-ADRQIIHS.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { DEFAULT_TAB_SIZE, resolveTextStyle, cachedLayoutRuns, graphemeCount, wrapLines, hangs, layoutRuns, tabAdvance, widthWithTabs } from './chunk-
|
|
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,90 +376,34 @@ 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 } :
|
|
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
|
|
369
402
|
});
|
|
370
403
|
}
|
|
371
404
|
return out;
|
|
372
405
|
}
|
|
373
406
|
|
|
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 })
|
|
391
|
-
});
|
|
392
|
-
resolved.set(face, out);
|
|
393
|
-
}
|
|
394
|
-
return out;
|
|
395
|
-
}
|
|
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(glyphs, 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
|
-
for (const g of glyphs) {
|
|
415
|
-
const style = runs[g.run];
|
|
416
|
-
const paint = style.underline || style.strikethrough || style.overline ? paintOf(g.run) : null;
|
|
417
|
-
if (paint === null) {
|
|
418
|
-
close();
|
|
419
|
-
continue;
|
|
420
|
-
}
|
|
421
|
-
const face = faceOf(g.run);
|
|
422
|
-
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;
|
|
423
|
-
if (joins) {
|
|
424
|
-
open.x1 = g.x + g.step;
|
|
425
|
-
} else {
|
|
426
|
-
close();
|
|
427
|
-
open = { run: g.run, style, face, paint, baselineY: g.baselineY, x0: g.x, x1: g.x + g.step };
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
close();
|
|
431
|
-
return rules;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
407
|
// src/measure/verticalAlign.ts
|
|
435
408
|
function verticalAlignOffset(align, boxHeight, textHeight) {
|
|
436
409
|
if (align === void 0 || align === "top" || boxHeight === void 0) return 0;
|
|
@@ -465,210 +438,6 @@ function layoutTextPose(pose, scale = 1) {
|
|
|
465
438
|
y: pose.y + verticalAlignOffset(pose.verticalAlign, pose.height, laid.bounds.height)
|
|
466
439
|
};
|
|
467
440
|
}
|
|
468
|
-
var SYNTHETIC_BOLD_TOLERANCE = 5e-4;
|
|
469
|
-
var CACHE_LIMIT = 4096;
|
|
470
|
-
var cache = /* @__PURE__ */ new Map();
|
|
471
|
-
var dCache = /* @__PURE__ */ new Map();
|
|
472
|
-
function syntheticBoldOffset(resolved2) {
|
|
473
|
-
if (!resolved2.synthetic.bold) return null;
|
|
474
|
-
const { source } = resolved2;
|
|
475
|
-
if (source !== "atlas" && source !== "canvas") return null;
|
|
476
|
-
const { family, weight, style } = resolved2.resolved;
|
|
477
|
-
const growth = syntheticBoldGrowth(source, family, weight, style);
|
|
478
|
-
if (growth === void 0 || !(growth > 0)) return null;
|
|
479
|
-
return { growth, join: source === "atlas" ? "miter" : "round" };
|
|
480
|
-
}
|
|
481
|
-
function emboldenGlyph(em, d, resolved2) {
|
|
482
|
-
const offset = syntheticBoldOffset(resolved2);
|
|
483
|
-
if (offset === null) return em;
|
|
484
|
-
const key = `${offset.growth}|${offset.join}|${d}`;
|
|
485
|
-
let grown = cache.get(key);
|
|
486
|
-
if (grown) return grown;
|
|
487
|
-
grown = mergeRegion(
|
|
488
|
-
growRegion(em, offset.growth, { join: offset.join, tolerance: SYNTHETIC_BOLD_TOLERANCE }),
|
|
489
|
-
{ tolerance: SYNTHETIC_BOLD_TOLERANCE }
|
|
490
|
-
);
|
|
491
|
-
if (cache.size >= CACHE_LIMIT) cache.clear();
|
|
492
|
-
cache.set(key, grown);
|
|
493
|
-
return grown;
|
|
494
|
-
}
|
|
495
|
-
function emboldenGlyphD(d, resolved2) {
|
|
496
|
-
const offset = syntheticBoldOffset(resolved2);
|
|
497
|
-
if (offset === null) return null;
|
|
498
|
-
const tag = `bold:${offset.growth}:${offset.join}`;
|
|
499
|
-
const key = `${tag}|${d}`;
|
|
500
|
-
let grown = dCache.get(key);
|
|
501
|
-
if (grown === void 0) {
|
|
502
|
-
grown = pathToD(emboldenGlyph(pathFromD(d), d, resolved2));
|
|
503
|
-
if (dCache.size >= CACHE_LIMIT) dCache.clear();
|
|
504
|
-
dCache.set(key, grown);
|
|
505
|
-
}
|
|
506
|
-
return { d: grown, tag };
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
// src/layout/paintRuns.ts
|
|
510
|
-
function glyphOutlineD(g, run, minSize) {
|
|
511
|
-
const outlineOnly = g.resolved.source === "outline";
|
|
512
|
-
if (!outlineOnly) {
|
|
513
|
-
if (minSize === void 0) return null;
|
|
514
|
-
if (run.fontSize < minSize && !strokeHasInk(run.stroke)) return null;
|
|
515
|
-
}
|
|
516
|
-
const d = faceOutlineD(g);
|
|
517
|
-
if (d === null) return null;
|
|
518
|
-
const { family, weight, style } = g.resolved.resolved;
|
|
519
|
-
const key = `${family}|${weight}|${style}|${g.cp}`;
|
|
520
|
-
if (outlineOnly || !g.resolved.synthetic.bold) return { d, key };
|
|
521
|
-
const bold = emboldenGlyphD(d, g.resolved);
|
|
522
|
-
return bold && { d: bold.d, key: `${key}|${bold.tag}` };
|
|
523
|
-
}
|
|
524
|
-
function faceOutlineD(g) {
|
|
525
|
-
if (g.glyph?.color) return null;
|
|
526
|
-
const { family, weight, style } = g.resolved.resolved;
|
|
527
|
-
if (g.resolved.source !== "outline" && g.glyph === null) return null;
|
|
528
|
-
return glyphOutline(family, weight, style, g.cp);
|
|
529
|
-
}
|
|
530
|
-
function glyphQuad(g) {
|
|
531
|
-
const { font, glyph } = g;
|
|
532
|
-
if (font === null || glyph === null || glyph.width === 0 || glyph.page < 0) return null;
|
|
533
|
-
const scale = g.fontSize / g.metrics.size;
|
|
534
|
-
const x0 = g.x + glyph.xoffset * scale;
|
|
535
|
-
const y0 = g.baselineY + (glyph.yoffset - g.metrics.base) * scale;
|
|
536
|
-
return {
|
|
537
|
-
x0,
|
|
538
|
-
y0,
|
|
539
|
-
x1: x0 + glyph.width * scale,
|
|
540
|
-
y1: y0 + glyph.height * scale,
|
|
541
|
-
u0: glyph.x / font.common.scaleW,
|
|
542
|
-
v0: glyph.y / font.common.scaleH,
|
|
543
|
-
u1: (glyph.x + glyph.width) / font.common.scaleW,
|
|
544
|
-
v1: (glyph.y + glyph.height) / font.common.scaleH,
|
|
545
|
-
baselineY: g.baselineY
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
function runFaces(runs) {
|
|
549
|
-
const faces = runs.map((r) => faceMetricsOf(resolveFontVariant(r.fontFamily, r.fontWeight, r.fontStyle)));
|
|
550
|
-
return (run) => faces[run];
|
|
551
|
-
}
|
|
552
|
-
function groupFor(groups, g, run, source, last) {
|
|
553
|
-
const { family, weight, style } = g.resolved.resolved;
|
|
554
|
-
const { synthetic } = g.resolved;
|
|
555
|
-
const page = source === "outline" ? 0 : g.glyph.page;
|
|
556
|
-
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) {
|
|
557
|
-
return last.group;
|
|
558
|
-
}
|
|
559
|
-
const stroke = source === "outline" && strokeHasInk(run.stroke) ? run.stroke : void 0;
|
|
560
|
-
const key = `${family}|${weight}|${style}|${synthetic.bold ? 1 : 0}${synthetic.italic ? 1 : 0}|${paintKey(run.fill?.paint)}|${strokeKey(stroke)}|${source}|${page}`;
|
|
561
|
-
let group = groups.get(key);
|
|
562
|
-
if (!group) {
|
|
563
|
-
group = {
|
|
564
|
-
family,
|
|
565
|
-
weight,
|
|
566
|
-
style,
|
|
567
|
-
synthetic: { ...synthetic },
|
|
568
|
-
source,
|
|
569
|
-
page,
|
|
570
|
-
fill: run.fill,
|
|
571
|
-
...stroke ? { stroke } : {},
|
|
572
|
-
quads: [],
|
|
573
|
-
glyphs: []
|
|
574
|
-
};
|
|
575
|
-
groups.set(key, group);
|
|
576
|
-
}
|
|
577
|
-
last.group = group;
|
|
578
|
-
last.run = g.run;
|
|
579
|
-
last.family = family;
|
|
580
|
-
last.weight = weight;
|
|
581
|
-
last.style = style;
|
|
582
|
-
last.bold = synthetic.bold;
|
|
583
|
-
last.italic = synthetic.italic;
|
|
584
|
-
last.source = source;
|
|
585
|
-
last.page = page;
|
|
586
|
-
return group;
|
|
587
|
-
}
|
|
588
|
-
function paintRuns(laid, runs, opts = {}) {
|
|
589
|
-
const groups = /* @__PURE__ */ new Map();
|
|
590
|
-
const last = {
|
|
591
|
-
group: null,
|
|
592
|
-
run: -1,
|
|
593
|
-
family: "",
|
|
594
|
-
weight: 0,
|
|
595
|
-
style: "normal",
|
|
596
|
-
bold: false,
|
|
597
|
-
italic: false,
|
|
598
|
-
source: "atlas",
|
|
599
|
-
page: 0
|
|
600
|
-
};
|
|
601
|
-
const decorations = [];
|
|
602
|
-
const faceOf = runFaces(runs);
|
|
603
|
-
const rulePaint = (run) => fillHasInk(runs[run].fill) ? paintKey(runs[run].fill.paint) : null;
|
|
604
|
-
for (const line of laid.lines) {
|
|
605
|
-
for (const g of line.glyphs) {
|
|
606
|
-
const run = runs[g.run];
|
|
607
|
-
const outline = glyphOutlineD(g, run, opts.outlineMinSize);
|
|
608
|
-
if (!fillHasInk(run.fill) && !(outline !== null && strokeHasInk(run.stroke))) continue;
|
|
609
|
-
if (outline !== null) {
|
|
610
|
-
groupFor(groups, g, run, "outline", last).glyphs.push({
|
|
611
|
-
...outline,
|
|
612
|
-
x: g.x,
|
|
613
|
-
baselineY: g.baselineY,
|
|
614
|
-
scale: g.fontSize
|
|
615
|
-
});
|
|
616
|
-
continue;
|
|
617
|
-
}
|
|
618
|
-
const quad = glyphQuad(g);
|
|
619
|
-
if (!quad) continue;
|
|
620
|
-
if (g.glyph?.color) groupFor(groups, g, run, "color", last).quads.push({ ...quad, colorGlyph: g.glyph });
|
|
621
|
-
else groupFor(groups, g, run, g.resolved.source, last).quads.push(quad);
|
|
622
|
-
}
|
|
623
|
-
for (const rule of lineDecorations(line.glyphs, runs, faceOf, rulePaint)) {
|
|
624
|
-
const { run, ...rect } = rule;
|
|
625
|
-
decorations.push({ ...rect, fill: runs[run].fill });
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
return { groups: [...groups.values()], decorations };
|
|
629
|
-
}
|
|
630
|
-
var PAINT_CACHE_VARIANT_LIMIT = 32;
|
|
631
|
-
var own = /* @__PURE__ */ new WeakMap();
|
|
632
|
-
var shared = /* @__PURE__ */ new WeakMap();
|
|
633
|
-
function outlineBucket(runs, min) {
|
|
634
|
-
if (min === void 0) return -1;
|
|
635
|
-
const sizes = /* @__PURE__ */ new Set();
|
|
636
|
-
for (const r of runs) sizes.add(r.fontSize);
|
|
637
|
-
let n = 0;
|
|
638
|
-
for (const size of sizes) if (size >= min) n++;
|
|
639
|
-
return n;
|
|
640
|
-
}
|
|
641
|
-
function variantKey(runs, opts) {
|
|
642
|
-
let out = `${outlineBucket(runs, opts.outlineMinSize)}`;
|
|
643
|
-
for (const r of runs) {
|
|
644
|
-
out += `|${r.underline ? 1 : 0}${r.strikethrough ? 1 : 0}${r.overline ? 1 : 0}${paintKey(r.fill?.paint)}|${strokeKey(r.stroke)}`;
|
|
645
|
-
}
|
|
646
|
-
return out;
|
|
647
|
-
}
|
|
648
|
-
function cachedPaintRuns(laid, runs, opts = {}) {
|
|
649
|
-
const generation = glyphGeneration();
|
|
650
|
-
const key = variantKey(runs, opts);
|
|
651
|
-
const fresh = (p) => p !== void 0 && p.generation === generation && p.key === key;
|
|
652
|
-
let byLayout = own.get(runs);
|
|
653
|
-
if (byLayout === void 0) {
|
|
654
|
-
byLayout = /* @__PURE__ */ new WeakMap();
|
|
655
|
-
own.set(runs, byLayout);
|
|
656
|
-
}
|
|
657
|
-
const mine = byLayout.get(laid);
|
|
658
|
-
if (fresh(mine)) return mine.painted;
|
|
659
|
-
let byKey = shared.get(laid);
|
|
660
|
-
if (byKey === void 0) {
|
|
661
|
-
byKey = /* @__PURE__ */ new Map();
|
|
662
|
-
shared.set(laid, byKey);
|
|
663
|
-
}
|
|
664
|
-
let painting = byKey.get(key);
|
|
665
|
-
byKey.delete(key);
|
|
666
|
-
if (!fresh(painting)) painting = { generation, key, painted: paintRuns(laid, runs, opts) };
|
|
667
|
-
if (byKey.size >= PAINT_CACHE_VARIANT_LIMIT) byKey.delete(byKey.keys().next().value);
|
|
668
|
-
byKey.set(key, painting);
|
|
669
|
-
byLayout.set(laid, painting);
|
|
670
|
-
return painting.painted;
|
|
671
|
-
}
|
|
672
441
|
|
|
673
442
|
// src/measure/measureText.ts
|
|
674
443
|
function measuredWidth(ctx, text, style) {
|
|
@@ -710,15 +479,15 @@ function measureText(ctx, text, maxWidth, style) {
|
|
|
710
479
|
|
|
711
480
|
// src/measure/measureTextBounds.ts
|
|
712
481
|
function measureTextBounds(text, style, opts) {
|
|
713
|
-
const
|
|
714
|
-
const runs = resolveRuns([{ text }],
|
|
482
|
+
const resolved = resolveTextStyle(style, void 0, opts?.scale);
|
|
483
|
+
const runs = resolveRuns([{ text }], resolved, opts?.scale);
|
|
715
484
|
const { bounds } = cachedLayoutRuns(runs, {
|
|
716
485
|
maxWidth: opts?.maxWidth ?? Infinity,
|
|
717
|
-
lineHeight: opts?.lineHeight ??
|
|
718
|
-
align:
|
|
719
|
-
direction:
|
|
720
|
-
tabSize:
|
|
721
|
-
});
|
|
486
|
+
lineHeight: opts?.lineHeight ?? resolved.lineHeight,
|
|
487
|
+
align: resolved.align,
|
|
488
|
+
direction: resolved.direction,
|
|
489
|
+
tabSize: resolved.tabSize
|
|
490
|
+
}, "read");
|
|
722
491
|
return { width: bounds.width, height: bounds.height };
|
|
723
492
|
}
|
|
724
493
|
|
|
@@ -745,8 +514,7 @@ function caretCells(cells) {
|
|
|
745
514
|
for (const c of cells) {
|
|
746
515
|
const prev = out[out.length - 1];
|
|
747
516
|
if (prev && (!prev.clusterEnd || prev.srcIndex === c.srcIndex && prev.srcEnd === c.srcEnd)) {
|
|
748
|
-
const x0 = Math.min(prev.x, c.x);
|
|
749
|
-
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)];
|
|
750
518
|
out[out.length - 1] = {
|
|
751
519
|
...prev,
|
|
752
520
|
x: x0,
|
|
@@ -860,7 +628,11 @@ function runMetrics(run, fontSize, face) {
|
|
|
860
628
|
}
|
|
861
629
|
function layoutMarkdown(runs, maxWidth, fontSize, measure, lineHeightFactor = 1.3, faceOf, tabSize = DEFAULT_TAB_SIZE) {
|
|
862
630
|
if (runs.length === 0) return { lines: [], width: 0, height: 0 };
|
|
863
|
-
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
|
+
);
|
|
864
636
|
const segs = [];
|
|
865
637
|
runs.forEach((r, i) => {
|
|
866
638
|
const text = shown[i].text;
|
|
@@ -868,7 +640,7 @@ function layoutMarkdown(runs, maxWidth, fontSize, measure, lineHeightFactor = 1.
|
|
|
868
640
|
segs.push(text === r.text ? r : { ...r, text });
|
|
869
641
|
return;
|
|
870
642
|
}
|
|
871
|
-
const caps = smallCapsText(text);
|
|
643
|
+
const caps = smallCapsText(text, void 0, r.lang);
|
|
872
644
|
if (!caps.small) {
|
|
873
645
|
segs.push({ ...r, text: caps.text });
|
|
874
646
|
return;
|
|
@@ -1073,7 +845,9 @@ var STYLE_KEYS = [
|
|
|
1073
845
|
"baselineShift",
|
|
1074
846
|
"fontScale",
|
|
1075
847
|
"textTransform",
|
|
1076
|
-
"fontVariantCaps"
|
|
848
|
+
"fontVariantCaps",
|
|
849
|
+
"rotate",
|
|
850
|
+
"lang"
|
|
1077
851
|
];
|
|
1078
852
|
var FLAG_KEYS = /* @__PURE__ */ new Set([
|
|
1079
853
|
"bold",
|
|
@@ -1254,9 +1028,9 @@ function setFlagOverRange(runs, style, start, end, key, value) {
|
|
|
1254
1028
|
var FLAGS = ["italic", "underline", "strikethrough", "overline"];
|
|
1255
1029
|
function rangeWeight(range, style) {
|
|
1256
1030
|
const node = numericWeight(style?.fontWeight ?? 400);
|
|
1257
|
-
const
|
|
1258
|
-
if (
|
|
1259
|
-
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) };
|
|
1260
1034
|
const flag = range?.bold;
|
|
1261
1035
|
if (flag === MIXED) {
|
|
1262
1036
|
if (node === 700) return { fontWeight: 700, bold: true };
|
|
@@ -1274,19 +1048,22 @@ var OVERRIDES = [
|
|
|
1274
1048
|
"baselineShift",
|
|
1275
1049
|
"fontScale",
|
|
1276
1050
|
"textTransform",
|
|
1277
|
-
"fontVariantCaps"
|
|
1051
|
+
"fontVariantCaps",
|
|
1052
|
+
"rotate",
|
|
1053
|
+
"lang"
|
|
1278
1054
|
];
|
|
1279
1055
|
function effectiveRangeStyle(range, style, paint) {
|
|
1280
|
-
const
|
|
1056
|
+
const resolved = resolveTextStyle(style, paint);
|
|
1281
1057
|
const node = style ?? {};
|
|
1282
1058
|
const out = {
|
|
1283
|
-
fontFamily:
|
|
1284
|
-
fontSize:
|
|
1285
|
-
letterSpacing:
|
|
1286
|
-
textTransform:
|
|
1287
|
-
fontVariantCaps:
|
|
1059
|
+
fontFamily: resolved.fontFamily,
|
|
1060
|
+
fontSize: resolved.fontSize,
|
|
1061
|
+
letterSpacing: resolved.letterSpacing,
|
|
1062
|
+
textTransform: resolved.textTransform,
|
|
1063
|
+
fontVariantCaps: resolved.fontVariantCaps
|
|
1288
1064
|
};
|
|
1289
|
-
if (
|
|
1065
|
+
if (resolved.fill !== null) out.fill = resolved.fill;
|
|
1066
|
+
if (resolved.lang !== void 0) out.lang = resolved.lang;
|
|
1290
1067
|
Object.assign(out, rangeWeight(range, style));
|
|
1291
1068
|
for (const key of FLAGS) {
|
|
1292
1069
|
const on = nodeHasFlag(node, key);
|
|
@@ -1302,6 +1079,14 @@ function effectiveRangeStyle(range, style, paint) {
|
|
|
1302
1079
|
|
|
1303
1080
|
// src/syntheticItalic.ts
|
|
1304
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];
|
|
1305
1090
|
var TextOutlinesError = class extends Error {
|
|
1306
1091
|
reason;
|
|
1307
1092
|
family;
|
|
@@ -1370,7 +1155,7 @@ function emGlyph(d) {
|
|
|
1370
1155
|
function placedGlyphOutline(g, dx, dy, caller, blankFallback = false) {
|
|
1371
1156
|
const d = faceOutlineD(g);
|
|
1372
1157
|
if (d === null) {
|
|
1373
|
-
if (!blankFallback &&
|
|
1158
|
+
if (!blankFallback && hasGlyphQuad(g)) {
|
|
1374
1159
|
const { family, weight, style } = g.resolved.resolved;
|
|
1375
1160
|
throw new TextOutlinesError(
|
|
1376
1161
|
"no-outlines",
|
|
@@ -1383,9 +1168,20 @@ function placedGlyphOutline(g, dx, dy, caller, blankFallback = false) {
|
|
|
1383
1168
|
}
|
|
1384
1169
|
return null;
|
|
1385
1170
|
}
|
|
1171
|
+
return glyphOutlineAt(d, g, dx, dy);
|
|
1172
|
+
}
|
|
1173
|
+
function glyphOutlineAt(d, g, dx, dy) {
|
|
1386
1174
|
const s = g.fontSize;
|
|
1387
1175
|
const shear = g.resolved.synthetic.italic ? Math.tan(SYNTHETIC_ITALIC_RADIANS) : 0;
|
|
1388
|
-
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
|
+
];
|
|
1389
1185
|
return transformPath(emboldenGlyph(emGlyph(d), d, g.resolved), m);
|
|
1390
1186
|
}
|
|
1391
1187
|
function colorGlyphError(run, caller) {
|
|
@@ -1398,25 +1194,6 @@ function colorGlyphError(run, caller) {
|
|
|
1398
1194
|
caller
|
|
1399
1195
|
);
|
|
1400
1196
|
}
|
|
1401
|
-
function concat(parts) {
|
|
1402
|
-
let nc = 0, nk = 0;
|
|
1403
|
-
for (const p of parts) {
|
|
1404
|
-
nc += p.commands.length;
|
|
1405
|
-
nk += p.coords.length;
|
|
1406
|
-
}
|
|
1407
|
-
const commands = new Uint8Array(nc);
|
|
1408
|
-
const coords = new Float32Array(nk);
|
|
1409
|
-
let ci = 0, ki = 0;
|
|
1410
|
-
for (const p of parts) {
|
|
1411
|
-
commands.set(p.commands, ci);
|
|
1412
|
-
ci += p.commands.length;
|
|
1413
|
-
coords.set(p.coords, ki);
|
|
1414
|
-
ki += p.coords.length;
|
|
1415
|
-
}
|
|
1416
|
-
return { kind: "polygon", commands, coords, fillRule: "nonzero" };
|
|
1417
|
-
}
|
|
1418
|
-
|
|
1419
|
-
// src/textToPath.ts
|
|
1420
1197
|
function facesOf(runs) {
|
|
1421
1198
|
const out = /* @__PURE__ */ new Map();
|
|
1422
1199
|
const add = (family, weight, style) => {
|
|
@@ -1447,11 +1224,14 @@ function outlineLayoutInput(data, box, opts, caller) {
|
|
|
1447
1224
|
return input;
|
|
1448
1225
|
}
|
|
1449
1226
|
function ruleContour(x0, y0, x1, y1) {
|
|
1227
|
+
const xy = [x0, y0, x1, y0, x1, y1, x0, y1];
|
|
1228
|
+
const origin = originFor(xy);
|
|
1450
1229
|
return {
|
|
1451
1230
|
kind: "polygon",
|
|
1452
1231
|
commands: new Uint8Array([PATH_M, PATH_L, PATH_L, PATH_L, PATH_Z]),
|
|
1453
|
-
coords:
|
|
1454
|
-
fillRule: "nonzero"
|
|
1232
|
+
coords: coordsFrom(origin, xy),
|
|
1233
|
+
fillRule: "nonzero",
|
|
1234
|
+
...origin ? { origin } : {}
|
|
1455
1235
|
};
|
|
1456
1236
|
}
|
|
1457
1237
|
function walkOutlines(data, pose, opts) {
|
|
@@ -1464,25 +1244,27 @@ function walkOutlines(data, pose, opts) {
|
|
|
1464
1244
|
const rules = paints.map(() => []);
|
|
1465
1245
|
const faceOf = runFaces(runs);
|
|
1466
1246
|
for (const line of laid.lines) {
|
|
1247
|
+
const deco = lineDecorations(runs, faceOf, (run) => String(paintOf[run]));
|
|
1467
1248
|
for (const g of line.glyphs) {
|
|
1249
|
+
deco.add(g);
|
|
1468
1250
|
if (g.glyph?.color) throw colorGlyphError(runs[g.run], "textToPath");
|
|
1469
1251
|
const glyph = placedGlyphOutline(g, dx, dy, "textToPath");
|
|
1470
1252
|
if (glyph) parts[paintOf[g.run]].push(glyph);
|
|
1471
1253
|
}
|
|
1472
|
-
for (const r of
|
|
1254
|
+
for (const r of deco.end()) {
|
|
1473
1255
|
rules[paintOf[r.run]].push(ruleContour(r.x0 + dx, r.y0 + dy, r.x1 + dx, r.y1 + dy));
|
|
1474
1256
|
}
|
|
1475
1257
|
}
|
|
1476
1258
|
const frame = poseFrameOf(pose);
|
|
1477
1259
|
const paths = parts.map((glyphs, i) => {
|
|
1478
|
-
const path =
|
|
1260
|
+
const path = joinPaths([...glyphs, ...rules[i]]);
|
|
1479
1261
|
if (!frame || path.commands.length === 0) return path;
|
|
1480
1262
|
return transformPath(path, frame);
|
|
1481
1263
|
});
|
|
1482
1264
|
return { paints, paths };
|
|
1483
1265
|
}
|
|
1484
1266
|
function textToPath(data, pose, opts = {}) {
|
|
1485
|
-
return
|
|
1267
|
+
return joinPaths(walkOutlines(data, pose, opts).paths);
|
|
1486
1268
|
}
|
|
1487
1269
|
function textToPathsByPaint(data, pose, opts = {}) {
|
|
1488
1270
|
const { paints, paths } = walkOutlines(data, pose, opts);
|
|
@@ -1505,11 +1287,26 @@ async function loadTextOutlines(data, opts = {}) {
|
|
|
1505
1287
|
}
|
|
1506
1288
|
var MIDDLE_FALLBACK = 0.25;
|
|
1507
1289
|
function textClusters(data, opts = {}) {
|
|
1508
|
-
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 };
|
|
1509
1303
|
const { runs, opts: layoutOpts } = outlineLayoutInput(data, box, opts, "textClusters");
|
|
1510
1304
|
const line = layoutRuns(runs, layoutOpts).lines[0];
|
|
1511
1305
|
if (!line || line.cells.length === 0) return [];
|
|
1512
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();
|
|
1513
1310
|
const glyphOf = new Array(line.cells.length);
|
|
1514
1311
|
for (const g of line.glyphs) glyphOf[g.cell] = g;
|
|
1515
1312
|
const groups = [];
|
|
@@ -1532,19 +1329,36 @@ function textClusters(data, opts = {}) {
|
|
|
1532
1329
|
}
|
|
1533
1330
|
const paths = [];
|
|
1534
1331
|
parts.forEach((p, k) => {
|
|
1535
|
-
if (p.length > 0) paths.push({ ...paints[k], path:
|
|
1332
|
+
if (p.length > 0) paths.push({ ...paints[k], path: joinPaths(p) });
|
|
1536
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
|
+
}
|
|
1537
1350
|
const first = glyphs[0];
|
|
1538
1351
|
const xHeight = faceMetricsOf(first.resolved)?.xHeight;
|
|
1539
1352
|
return {
|
|
1540
1353
|
x: left - line.x0,
|
|
1541
1354
|
advance: right - left,
|
|
1542
1355
|
paths,
|
|
1543
|
-
middle: (xHeight === void 0 ? MIDDLE_FALLBACK : xHeight / 2) * first.fontSize
|
|
1356
|
+
middle: (xHeight === void 0 ? MIDDLE_FALLBACK : xHeight / 2) * first.fontSize,
|
|
1357
|
+
rules
|
|
1544
1358
|
};
|
|
1545
1359
|
});
|
|
1546
1360
|
}
|
|
1547
1361
|
|
|
1548
|
-
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 };
|
|
1549
1363
|
//# sourceMappingURL=index.js.map
|
|
1550
1364
|
//# sourceMappingURL=index.js.map
|