@weasel-js/text 1.12.0 → 1.13.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/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { DEFAULT_TAB_SIZE, resolveTextStyle, cachedLayoutRuns, graphemeCount, wrapLines, hangs, layoutRuns, tabAdvance, widthWithTabs } from './chunk-ADRQIIHS.js';
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-ADRQIIHS.js';
1
+ import { lazy, DEFAULT_TAB_SIZE, resolveTextStyle, cachedLayoutRuns, blankGlyph, LineBox, doubles, graphemeCount, wrapLines, hangs, layoutRuns, tabAdvance, widthWithTabs } from './chunk-DOTR55MR.js';
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-DOTR55MR.js';
3
3
  import { resolveScreenLength, fillHasInk, strokeHasInk, paintKey, strokeKey } from '@weasel-js/paint';
4
4
  import { faceMetricsOf, resolveFontVariant, syntheticBoldGrowth, glyphGeneration, loadFontOutlines, faceMetricsFor, glyphOutline, outlineStatus, cssFamilyName } from '@weasel-js/font';
5
5
  import { pathToD, pathFromD, mergeRegion, growRegion, poseFrameOf, transformPath, PATH_M, PATH_L, PATH_Z, pathSignedArea, reversePath } from '@weasel-js/geom';
@@ -398,7 +398,7 @@ function decorationRule(kind, baselineY, fontSize, face) {
398
398
  const y0 = baselineY + fontSize * m.offset;
399
399
  return { y0, y1: y0 + fontSize * m.thickness };
400
400
  }
401
- function lineDecorations(glyphs, runs, faceOf, paintOf) {
401
+ function lineDecorations(runs, faceOf, paintOf) {
402
402
  const rules = [];
403
403
  let open = null;
404
404
  const close = () => {
@@ -411,24 +411,28 @@ function lineDecorations(glyphs, runs, faceOf, paintOf) {
411
411
  rules.push({ kind, x0: s.x0, y0, x1: s.x1, y1, run: s.run });
412
412
  }
413
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 {
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() {
426
432
  close();
427
- open = { run: g.run, style, face, paint, baselineY: g.baselineY, x0: g.x, x1: g.x + g.step };
433
+ return rules;
428
434
  }
429
- }
430
- close();
431
- return rules;
435
+ };
432
436
  }
433
437
 
434
438
  // src/measure/verticalAlign.ts
@@ -527,28 +531,53 @@ function faceOutlineD(g) {
527
531
  if (g.resolved.source !== "outline" && g.glyph === null) return null;
528
532
  return glyphOutline(family, weight, style, g.cp);
529
533
  }
530
- function glyphQuad(g) {
534
+ var QUAD_STRIDE = 9;
535
+ function hasGlyphQuad(g) {
531
536
  const { font, glyph } = g;
532
- if (font === null || glyph === null || glyph.width === 0 || glyph.page < 0) return null;
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;
533
541
  const scale = g.fontSize / g.metrics.size;
534
542
  const x0 = g.x + glyph.xoffset * scale;
535
543
  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
- }
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
+ };
548
576
  function runFaces(runs) {
549
577
  const faces = runs.map((r) => faceMetricsOf(resolveFontVariant(r.fontFamily, r.fontWeight, r.fontStyle)));
550
578
  return (run) => faces[run];
551
579
  }
580
+ var isDecorated = (r) => r.underline || r.strikethrough || r.overline;
552
581
  function groupFor(groups, g, run, source, last) {
553
582
  const { family, weight, style } = g.resolved.resolved;
554
583
  const { synthetic } = g.resolved;
@@ -569,7 +598,8 @@ function groupFor(groups, g, run, source, last) {
569
598
  page,
570
599
  fill: run.fill,
571
600
  ...stroke ? { stroke } : {},
572
- quads: [],
601
+ quadData: [],
602
+ ...source === "color" ? { colorGlyphs: [] } : {},
573
603
  glyphs: []
574
604
  };
575
605
  groups.set(key, group);
@@ -599,10 +629,20 @@ function paintRuns(laid, runs, opts = {}) {
599
629
  page: 0
600
630
  };
601
631
  const decorations = [];
602
- const faceOf = runFaces(runs);
632
+ const faceOf = runs.some(isDecorated) ? runFaces(runs) : null;
603
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;
604
639
  for (const line of laid.lines) {
605
- for (const g of line.glyphs) {
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);
606
646
  const run = runs[g.run];
607
647
  const outline = glyphOutlineD(g, run, opts.outlineMinSize);
608
648
  if (!fillHasInk(run.fill) && !(outline !== null && strokeHasInk(run.stroke))) continue;
@@ -615,17 +655,31 @@ function paintRuns(laid, runs, opts = {}) {
615
655
  });
616
656
  continue;
617
657
  }
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);
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);
622
672
  }
623
- for (const rule of lineDecorations(line.glyphs, runs, faceOf, rulePaint)) {
673
+ if (!rules) continue;
674
+ for (const rule of rules.end()) {
624
675
  const { run, ...rect } = rule;
625
676
  decorations.push({ ...rect, fill: runs[run].fill });
626
677
  }
627
678
  }
628
- return { groups: [...groups.values()], decorations };
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 };
629
683
  }
630
684
  var PAINT_CACHE_VARIANT_LIMIT = 32;
631
685
  var own = /* @__PURE__ */ new WeakMap();
@@ -1370,7 +1424,7 @@ function emGlyph(d) {
1370
1424
  function placedGlyphOutline(g, dx, dy, caller, blankFallback = false) {
1371
1425
  const d = faceOutlineD(g);
1372
1426
  if (d === null) {
1373
- if (!blankFallback && glyphQuad(g) !== null) {
1427
+ if (!blankFallback && hasGlyphQuad(g)) {
1374
1428
  const { family, weight, style } = g.resolved.resolved;
1375
1429
  throw new TextOutlinesError(
1376
1430
  "no-outlines",
@@ -1464,12 +1518,14 @@ function walkOutlines(data, pose, opts) {
1464
1518
  const rules = paints.map(() => []);
1465
1519
  const faceOf = runFaces(runs);
1466
1520
  for (const line of laid.lines) {
1521
+ const deco = lineDecorations(runs, faceOf, (run) => String(paintOf[run]));
1467
1522
  for (const g of line.glyphs) {
1523
+ deco.add(g);
1468
1524
  if (g.glyph?.color) throw colorGlyphError(runs[g.run], "textToPath");
1469
1525
  const glyph = placedGlyphOutline(g, dx, dy, "textToPath");
1470
1526
  if (glyph) parts[paintOf[g.run]].push(glyph);
1471
1527
  }
1472
- for (const r of lineDecorations(line.glyphs, runs, faceOf, (run) => String(paintOf[run]))) {
1528
+ for (const r of deco.end()) {
1473
1529
  rules[paintOf[r.run]].push(ruleContour(r.x0 + dx, r.y0 + dy, r.x1 + dx, r.y1 + dy));
1474
1530
  }
1475
1531
  }
@@ -1545,6 +1601,6 @@ function textClusters(data, opts = {}) {
1545
1601
  });
1546
1602
  }
1547
1603
 
1548
- 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, textClusters, textLineBoxes, textPoseLayoutInput, textToPath, textToPathsByPaint, toRuns, transformRunTexts, unboldPatch, verticalAlignOffset };
1604
+ export { DEFAULT_DECORATION_METRICS, MARKDOWN_RUN_GRAMMAR, QUAD_STRIDE, 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, textClusters, textLineBoxes, textPoseLayoutInput, textToPath, textToPathsByPaint, toRuns, transformRunTexts, unboldPatch, verticalAlignOffset };
1549
1605
  //# sourceMappingURL=index.js.map
1550
1606
  //# sourceMappingURL=index.js.map