@weasel-js/text 1.10.1 → 1.11.1
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-EFPJZYNI.js → chunk-JOVZEHBJ.js} +26 -27
- package/dist/chunk-JOVZEHBJ.js.map +1 -0
- package/dist/index.d.ts +44 -12
- package/dist/index.js +99 -39
- package/dist/index.js.map +1 -1
- package/dist/{test-seams-CuLNcDew.d.ts → test-seams-BypGSlZj.d.ts} +3 -0
- package/dist/test-seams.d.ts +1 -1
- package/dist/test-seams.js +1 -1
- package/package.json +6 -6
- package/dist/chunk-EFPJZYNI.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { DEFAULT_TAB_SIZE, resolveTextStyle, cachedLayoutRuns, graphemeCount, wrapLines, hangs, tabAdvance, widthWithTabs, layoutRuns } from './chunk-
|
|
2
|
-
export { BREAK_ALLOWED, BREAK_MANDATORY, DEFAULT_TEXT_STYLE, LAYOUT_CACHE_STRUCTURAL_LIMIT, LAYOUT_CACHE_VARIANT_LIMIT, NO_BREAK, cachedLayoutRuns, fontString, isHardLineBreak, layoutRuns, lineBreakOpportunities, paragraphDirection, registerBidi, resolveAlign, resolveTextStyle } from './chunk-
|
|
1
|
+
import { DEFAULT_TAB_SIZE, resolveTextStyle, cachedLayoutRuns, graphemeCount, wrapLines, hangs, tabAdvance, widthWithTabs, layoutRuns } from './chunk-JOVZEHBJ.js';
|
|
2
|
+
export { BREAK_ALLOWED, BREAK_MANDATORY, DEFAULT_TEXT_STYLE, LAYOUT_CACHE_STRUCTURAL_LIMIT, LAYOUT_CACHE_VARIANT_LIMIT, NO_BREAK, cachedLayoutRuns, fontString, isHardLineBreak, layoutRuns, lineBreakOpportunities, paragraphDirection, registerBidi, resolveAlign, resolveTextStyle } from './chunk-JOVZEHBJ.js';
|
|
3
3
|
import { resolveScreenLength, fillHasInk, strokeHasInk, paintKey, strokeKey } from '@weasel-js/paint';
|
|
4
|
-
import { faceMetricsOf, resolveFontVariant, glyphGeneration, loadFontOutlines, faceMetricsFor, glyphOutline, outlineStatus, cssFamilyName } from '@weasel-js/font';
|
|
5
|
-
import {
|
|
4
|
+
import { faceMetricsOf, resolveFontVariant, syntheticBoldGrowth, glyphGeneration, loadFontOutlines, faceMetricsFor, glyphOutline, outlineStatus, cssFamilyName } from '@weasel-js/font';
|
|
5
|
+
import { pathToD, pathFromD, mergeRegion, growRegion, transformPath, poseFrameOf, pathSignedArea, reversePath, PATH_M, PATH_L, PATH_Z } from '@weasel-js/geom';
|
|
6
|
+
import { MIXED, deepEqual } from '@weasel-js/registry';
|
|
6
7
|
export { MIXED } from '@weasel-js/registry';
|
|
7
|
-
import { transformPath, poseFrameOf, pathFromD, pathSignedArea, reversePath, PATH_M, PATH_L, PATH_Z } from '@weasel-js/geom';
|
|
8
8
|
|
|
9
9
|
// src/runs.ts
|
|
10
10
|
function toRuns(input) {
|
|
@@ -413,8 +413,8 @@ function lineDecorations(glyphs, runs, faceOf, paintOf) {
|
|
|
413
413
|
};
|
|
414
414
|
for (const g of glyphs) {
|
|
415
415
|
const style = runs[g.run];
|
|
416
|
-
const paint = paintOf(g.run);
|
|
417
|
-
if (paint === null
|
|
416
|
+
const paint = style.underline || style.strikethrough || style.overline ? paintOf(g.run) : null;
|
|
417
|
+
if (paint === null) {
|
|
418
418
|
close();
|
|
419
419
|
continue;
|
|
420
420
|
}
|
|
@@ -465,14 +465,66 @@ function layoutTextPose(pose, scale = 1) {
|
|
|
465
465
|
y: pose.y + verticalAlignOffset(pose.verticalAlign, pose.height, laid.bounds.height)
|
|
466
466
|
};
|
|
467
467
|
}
|
|
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
|
|
468
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) {
|
|
469
525
|
if (g.glyph?.color) return null;
|
|
470
526
|
const { family, weight, style } = g.resolved.resolved;
|
|
471
|
-
if (g.resolved.source
|
|
472
|
-
if (g.glyph === null) return null;
|
|
473
|
-
if (minSize === void 0) return null;
|
|
474
|
-
if (run.fontSize < minSize && !strokeHasInk(run.stroke)) return null;
|
|
475
|
-
if (g.resolved.synthetic.bold) return null;
|
|
527
|
+
if (g.resolved.source !== "outline" && g.glyph === null) return null;
|
|
476
528
|
return glyphOutline(family, weight, style, g.cp);
|
|
477
529
|
}
|
|
478
530
|
function glyphQuad(g) {
|
|
@@ -497,10 +549,13 @@ function runFaces(runs) {
|
|
|
497
549
|
const faces = runs.map((r) => faceMetricsOf(resolveFontVariant(r.fontFamily, r.fontWeight, r.fontStyle)));
|
|
498
550
|
return (run) => faces[run];
|
|
499
551
|
}
|
|
500
|
-
function groupFor(groups, g, run, source) {
|
|
552
|
+
function groupFor(groups, g, run, source, last) {
|
|
501
553
|
const { family, weight, style } = g.resolved.resolved;
|
|
502
554
|
const { synthetic } = g.resolved;
|
|
503
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
|
+
}
|
|
504
559
|
const stroke = source === "outline" && strokeHasInk(run.stroke) ? run.stroke : void 0;
|
|
505
560
|
const key = `${family}|${weight}|${style}|${synthetic.bold ? 1 : 0}${synthetic.italic ? 1 : 0}|${paintKey(run.fill?.paint)}|${strokeKey(stroke)}|${source}|${page}`;
|
|
506
561
|
let group = groups.get(key);
|
|
@@ -519,23 +574,41 @@ function groupFor(groups, g, run, source) {
|
|
|
519
574
|
};
|
|
520
575
|
groups.set(key, group);
|
|
521
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;
|
|
522
586
|
return group;
|
|
523
587
|
}
|
|
524
588
|
function paintRuns(laid, runs, opts = {}) {
|
|
525
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
|
+
};
|
|
526
601
|
const decorations = [];
|
|
527
602
|
const faceOf = runFaces(runs);
|
|
528
603
|
const rulePaint = (run) => fillHasInk(runs[run].fill) ? paintKey(runs[run].fill.paint) : null;
|
|
529
604
|
for (const line of laid.lines) {
|
|
530
605
|
for (const g of line.glyphs) {
|
|
531
606
|
const run = runs[g.run];
|
|
532
|
-
const
|
|
533
|
-
if (!fillHasInk(run.fill) && !(
|
|
534
|
-
if (
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
d,
|
|
538
|
-
key: `${family}|${weight}|${style}|${g.cp}`,
|
|
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,
|
|
539
612
|
x: g.x,
|
|
540
613
|
baselineY: g.baselineY,
|
|
541
614
|
scale: g.fontSize
|
|
@@ -543,7 +616,9 @@ function paintRuns(laid, runs, opts = {}) {
|
|
|
543
616
|
continue;
|
|
544
617
|
}
|
|
545
618
|
const quad = glyphQuad(g);
|
|
546
|
-
if (quad)
|
|
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);
|
|
547
622
|
}
|
|
548
623
|
for (const rule of lineDecorations(line.glyphs, runs, faceOf, rulePaint)) {
|
|
549
624
|
const { run, ...rect } = rule;
|
|
@@ -1007,18 +1082,6 @@ var FLAG_KEYS = /* @__PURE__ */ new Set([
|
|
|
1007
1082
|
"strikethrough",
|
|
1008
1083
|
"overline"
|
|
1009
1084
|
]);
|
|
1010
|
-
function deepEqual(a, b) {
|
|
1011
|
-
if (Object.is(a, b)) return true;
|
|
1012
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
1013
|
-
return a.length === b.length && a.every((item, i) => deepEqual(item, b[i]));
|
|
1014
|
-
}
|
|
1015
|
-
if (isPlainObject(a) && isPlainObject(b)) {
|
|
1016
|
-
const keys = Object.keys(a);
|
|
1017
|
-
if (keys.length !== Object.keys(b).length) return false;
|
|
1018
|
-
return keys.every((k) => k in b && deepEqual(a[k], b[k]));
|
|
1019
|
-
}
|
|
1020
|
-
return false;
|
|
1021
|
-
}
|
|
1022
1085
|
function valueAt(run, key) {
|
|
1023
1086
|
const value = run[key];
|
|
1024
1087
|
if (FLAG_KEYS.has(key)) return value ?? false;
|
|
@@ -1277,9 +1340,6 @@ function assertOutlinable(run) {
|
|
|
1277
1340
|
}
|
|
1278
1341
|
const r = resolveFontVariant(fontFamily, fontWeight, fontStyle);
|
|
1279
1342
|
const { family, weight, style } = r.resolved;
|
|
1280
|
-
if (r.synthetic.bold) {
|
|
1281
|
-
fail("synthetic-bold", `drawn as a faux bold of ${family} ${weight}/${style}, which has no geometry to embolden. Register outlines for the real bold face.`);
|
|
1282
|
-
}
|
|
1283
1343
|
if (r.source === "outline") return;
|
|
1284
1344
|
const status = outlineStatus(family, weight, style);
|
|
1285
1345
|
if (status === null) {
|
|
@@ -1374,7 +1434,7 @@ function walkOutlines(data, pose, opts) {
|
|
|
1374
1434
|
"the text holds an emoji, which is drawn in its own colors and has no outline."
|
|
1375
1435
|
);
|
|
1376
1436
|
}
|
|
1377
|
-
const d =
|
|
1437
|
+
const d = faceOutlineD(g);
|
|
1378
1438
|
if (d === null) {
|
|
1379
1439
|
if (glyphQuad(g) !== null) {
|
|
1380
1440
|
const { family, weight, style } = g.resolved.resolved;
|
|
@@ -1391,7 +1451,7 @@ function walkOutlines(data, pose, opts) {
|
|
|
1391
1451
|
const s = g.fontSize;
|
|
1392
1452
|
const shear = g.resolved.synthetic.italic ? Math.tan(SYNTHETIC_ITALIC_RADIANS) : 0;
|
|
1393
1453
|
const m = [s, 0, -shear * s, s, g.x + dx, g.baselineY + dy];
|
|
1394
|
-
parts[paintOf[g.run]].push(transformPath(emGlyph(d), m));
|
|
1454
|
+
parts[paintOf[g.run]].push(transformPath(emboldenGlyph(emGlyph(d), d, g.resolved), m));
|
|
1395
1455
|
}
|
|
1396
1456
|
for (const r of lineDecorations(line.glyphs, runs, faceOf, (run) => String(paintOf[run]))) {
|
|
1397
1457
|
rules[paintOf[r.run]].push(ruleContour(r.x0 + dx, r.y0 + dy, r.x1 + dx, r.y1 + dy));
|
|
@@ -1428,6 +1488,6 @@ async function loadTextOutlines(data, opts = {}) {
|
|
|
1428
1488
|
await Promise.all(facesOf(runs).map((f) => loadFontOutlines(f.family, { weight: f.weight, style: f.style })));
|
|
1429
1489
|
}
|
|
1430
1490
|
|
|
1431
|
-
export { DEFAULT_DECORATION_METRICS, MARKDOWN_RUN_GRAMMAR, SCRIPT_METRICS, SMALL_CAPS_SCALE, SYNTHETIC_ITALIC_RADIANS, TextOutlinesError, applyStyleToRange, cachedPaintRuns, caretIndexAt, createMarkdownRenderer, decorationMetrics, defaultLabelTextRenderer, effectiveRangeStyle, fitTextPose, isBoldWeight, isSmallCapsLetter, isTextData, layoutMarkdown, layoutTextPose, loadTextOutlines, markdownToRuns, measureText, measureTextBounds, measuredWidth, nodeHasFlag, numericWeight, paintRuns, patchRangeStyle, pointInTextPose, rangeWeight, renderLabel, resolveRunFace, resolveRuns, runsCarryStyling, runsToMarkdown, runsToPlainText, scriptMetrics, scriptMetricsFor, setFlagOverRange, smallCapsScale, smallCapsScaleFor, smallCapsText, styleAtRange, supersededKey, textLineBoxes, textPoseLayoutInput, textToPath, textToPathsByPaint, toRuns, transformRunTexts, unboldPatch, verticalAlignOffset };
|
|
1491
|
+
export { DEFAULT_DECORATION_METRICS, MARKDOWN_RUN_GRAMMAR, SCRIPT_METRICS, SMALL_CAPS_SCALE, SYNTHETIC_ITALIC_RADIANS, TextOutlinesError, applyStyleToRange, cachedPaintRuns, caretIndexAt, createMarkdownRenderer, decorationMetrics, defaultLabelTextRenderer, effectiveRangeStyle, emboldenGlyphD, fitTextPose, isBoldWeight, isSmallCapsLetter, isTextData, layoutMarkdown, layoutTextPose, loadTextOutlines, markdownToRuns, measureText, measureTextBounds, measuredWidth, nodeHasFlag, numericWeight, paintRuns, patchRangeStyle, pointInTextPose, rangeWeight, renderLabel, resolveRunFace, resolveRuns, runsCarryStyling, runsToMarkdown, runsToPlainText, scriptMetrics, scriptMetricsFor, setFlagOverRange, smallCapsScale, smallCapsScaleFor, smallCapsText, styleAtRange, supersededKey, syntheticBoldOffset, textLineBoxes, textPoseLayoutInput, textToPath, textToPathsByPaint, toRuns, transformRunTexts, unboldPatch, verticalAlignOffset };
|
|
1432
1492
|
//# sourceMappingURL=index.js.map
|
|
1433
1493
|
//# sourceMappingURL=index.js.map
|