tegaki 0.16.0 → 0.17.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/CHANGELOG.md +10 -0
- package/FONTS-LICENSE.md +6 -0
- package/README.md +1 -0
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +1 -1
- package/dist/{core-BRYlZ8i2.mjs → core-BrU5Htjs.mjs} +96 -14
- package/dist/core-BrU5Htjs.mjs.map +1 -0
- package/dist/fonts/amiri/amiri-7df37680.ttf +0 -0
- package/dist/fonts/amiri/amiri.ttf +0 -0
- package/dist/fonts/amiri/bundle.d.mts +10631 -0
- package/dist/fonts/amiri/bundle.mjs +130866 -0
- package/dist/fonts/amiri/bundle.mjs.map +1 -0
- package/dist/fonts/caveat/bundle.d.mts +1992 -10
- package/dist/fonts/caveat/bundle.mjs +31054 -105
- package/dist/fonts/caveat/bundle.mjs.map +1 -1
- package/dist/fonts/italianno/bundle.d.mts +1548 -10
- package/dist/fonts/italianno/bundle.mjs +32832 -198
- package/dist/fonts/italianno/bundle.mjs.map +1 -1
- package/dist/fonts/klee-one/bundle.d.mts +11442 -0
- package/dist/fonts/klee-one/bundle.mjs +234910 -0
- package/dist/fonts/klee-one/bundle.mjs.map +1 -0
- package/dist/fonts/klee-one/klee-one-d192e144.ttf +0 -0
- package/dist/fonts/klee-one/klee-one.ttf +0 -0
- package/dist/fonts/parisienne/bundle.d.mts +939 -10
- package/dist/fonts/parisienne/bundle.mjs +19540 -192
- package/dist/fonts/parisienne/bundle.mjs.map +1 -1
- package/dist/fonts/suez-one/bundle.d.mts +2181 -0
- package/dist/fonts/suez-one/bundle.mjs +30466 -0
- package/dist/fonts/suez-one/bundle.mjs.map +1 -0
- package/dist/fonts/suez-one/suez-one-ac7f1d1c.ttf +0 -0
- package/dist/fonts/suez-one/suez-one.ttf +0 -0
- package/dist/fonts/tangerine/bundle.d.mts +40 -10
- package/dist/fonts/tangerine/bundle.mjs +52 -119
- package/dist/fonts/tangerine/bundle.mjs.map +1 -1
- package/dist/fonts/tillana/bundle.d.mts +8557 -0
- package/dist/fonts/tillana/bundle.mjs +224308 -0
- package/dist/fonts/tillana/bundle.mjs.map +1 -0
- package/dist/fonts/tillana/tillana-12e48378.ttf +0 -0
- package/dist/fonts/tillana/tillana.ttf +0 -0
- package/dist/{index-Duog5eW6.d.mts → index-JT-cQ_gO.d.mts} +3 -3
- package/dist/{index-Qr39WZaW.d.mts → index-vDGB0qjx.d.mts} +121 -63
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +2 -2
- package/dist/react/index.d.mts +3 -3
- package/dist/react/index.mjs +2 -2
- package/dist/{react-Eq0zlDsb.mjs → react-DBKcqp07.mjs} +5 -4
- package/dist/react-DBKcqp07.mjs.map +1 -0
- package/dist/shaper-harfbuzz/index.d.mts +1 -1
- package/dist/shaper-harfbuzz/index.mjs +35 -1
- package/dist/shaper-harfbuzz/index.mjs.map +1 -1
- package/dist/{shaper-registry-HD6_qkK4.d.mts → shaper-registry-DVS5R37Q.d.mts} +10 -5
- package/dist/solid/index.d.mts +2 -2
- package/dist/solid/index.mjs +4 -2
- package/dist/solid/index.mjs.map +1 -1
- package/dist/wc/index.d.mts +6 -2
- package/dist/wc/index.mjs +12 -2
- package/dist/wc/index.mjs.map +1 -1
- package/fonts/amiri/glyphDataById.json +1 -1
- package/fonts/caveat/bundle.ts +3 -0
- package/fonts/caveat/glyphData.json +1 -1
- package/fonts/caveat/glyphDataById.json +1 -0
- package/fonts/italianno/bundle.ts +3 -0
- package/fonts/italianno/glyphData.json +1 -1
- package/fonts/italianno/glyphDataById.json +1 -0
- package/fonts/klee-one/glyphDataById.json +1 -1
- package/fonts/parisienne/bundle.ts +3 -0
- package/fonts/parisienne/glyphData.json +1 -1
- package/fonts/parisienne/glyphDataById.json +1 -0
- package/fonts/suez-one/glyphDataById.json +1 -1
- package/fonts/tangerine/glyphData.json +1 -1
- package/fonts/tillana/bundle.ts +23 -0
- package/fonts/tillana/glyphData.json +1 -0
- package/fonts/tillana/glyphDataById.json +1 -0
- package/fonts/tillana/tillana-12e48378.ttf +0 -0
- package/fonts/tillana/tillana.ttf +0 -0
- package/package.json +8 -1
- package/src/astro/TegakiRenderer.astro +2 -2
- package/src/core/engine.ts +60 -6
- package/src/core/types.ts +10 -1
- package/src/lib/textLayout.ts +65 -10
- package/src/lib/timeline.test.ts +203 -0
- package/src/lib/timeline.ts +25 -3
- package/src/lib/utils.test.ts +73 -0
- package/src/lib/utils.ts +20 -1
- package/src/react/TegakiRenderer.tsx +2 -0
- package/src/shaper-harfbuzz/index.ts +60 -2
- package/src/solid/TegakiRenderer.tsx +2 -0
- package/src/svelte/TegakiRenderer.svelte +3 -2
- package/src/types.ts +9 -4
- package/src/vue/TegakiRenderer.vue +3 -1
- package/src/wc/TegakiElement.ts +12 -0
- package/dist/core-BRYlZ8i2.mjs.map +0 -1
- package/dist/react-Eq0zlDsb.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# tegaki
|
|
2
2
|
|
|
3
|
+
## 0.17.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2b4b435: Support Devanagari writing system and add Tillana as built-in font. Also fixed a bug with generating n-grams, which affected Arabic fonts.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- ee2db76: Fix GPOS and advance width features for some Arabic fonts like "Aref Ruqaa"
|
|
12
|
+
|
|
3
13
|
## 0.16.0
|
|
4
14
|
|
|
5
15
|
### Minor Changes
|
package/FONTS-LICENSE.md
CHANGED
|
@@ -45,6 +45,12 @@ All fonts are licensed under the [SIL Open Font License, Version 1.1](https://op
|
|
|
45
45
|
- **Copyright**: Copyright 2010-2022 The Amiri Project Authors (https://github.com/aliftype/amiri)
|
|
46
46
|
- **License**: SIL Open Font License, Version 1.1
|
|
47
47
|
|
|
48
|
+
## Tillana
|
|
49
|
+
|
|
50
|
+
- **Designer**: Indian Type Foundry, Shiva Nallaperumal
|
|
51
|
+
- **Copyright**: Copyright (c) 2014, Indian Type Foundry (info@indiantypefoundry.com)
|
|
52
|
+
- **License**: SIL Open Font License, Version 1.1
|
|
53
|
+
|
|
48
54
|
---
|
|
49
55
|
|
|
50
56
|
## SIL Open Font License, Version 1.1
|
package/README.md
CHANGED
|
@@ -73,6 +73,7 @@ Several handwriting fonts are bundled and ready to use:
|
|
|
73
73
|
- **Parisienne** — `tegaki/fonts/parisienne` _(Latin)_
|
|
74
74
|
- **Suez One** — `tegaki/fonts/suez-one` _(Hebrew + Latin)_
|
|
75
75
|
- **Amiri** — `tegaki/fonts/amiri` _(Arabic + Latin)_
|
|
76
|
+
- **Tillana** — `tegaki/fonts/tillana` _(Devanagari + Latin)_
|
|
76
77
|
- **Klee One** — `tegaki/fonts/klee-one` _(Japanese: kana + Kyōiku grade 1–2 kanji + Latin)_
|
|
77
78
|
|
|
78
79
|
For other fonts, use the [interactive generator](https://gkurt.com/tegaki/generator/) to create a custom bundle.
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { S as TimedPoint, _ as TegakiEffectName, a as BBox, b as TegakiMultiEffectName, c as CSSLength, d as LineCap, f as PathCommand, g as TegakiEffectConfigs, h as TegakiBundle, i as ShapedGlyph, l as FontOutput, m as Stroke, o as BUNDLE_VERSION, p as Point, r as BundleShaper, s as COMPATIBLE_BUNDLE_VERSIONS, t as ShaperFactory, u as GlyphData, v as TegakiEffects, x as TegakiSingletonEffectName, y as TegakiGlyphData } from "../shaper-registry-
|
|
2
|
-
import { A as
|
|
1
|
+
import { S as TimedPoint, _ as TegakiEffectName, a as BBox, b as TegakiMultiEffectName, c as CSSLength, d as LineCap, f as PathCommand, g as TegakiEffectConfigs, h as TegakiBundle, i as ShapedGlyph, l as FontOutput, m as Stroke, o as BUNDLE_VERSION, p as Point, r as BundleShaper, s as COMPATIBLE_BUNDLE_VERSIONS, t as ShaperFactory, u as GlyphData, v as TegakiEffects, x as TegakiSingletonEffectName, y as TegakiGlyphData } from "../shaper-registry-DVS5R37Q.mjs";
|
|
2
|
+
import { A as TimelineEntry, C as resolveEffects, D as computeTextLayout, E as computeLayoutBbox, O as Timeline, S as hasRenderHooks, T as TextLayout, _ as RenderStageContext, a as CreateElementFn, b as findEffects, c as TimeControlMode, d as getBundle, f as registerBundle, g as EffectDefinition, h as drawGlyph, i as TegakiEngine, j as computeTimeline, k as TimelineConfig, l as TimeControlProp, m as ensureFontFace, n as buildRootProps, o as TegakiEngineOptions, p as resolveBundle, r as domCreateElement, s as TegakiQuality, t as buildChildren, u as createBundle, v as ResolvedEffect, w as LayoutBBox, x as getEffectDefinition, y as findEffect } from "../index-vDGB0qjx.mjs";
|
|
3
3
|
export { BBox, BUNDLE_VERSION, BundleShaper, COMPATIBLE_BUNDLE_VERSIONS, CSSLength, CreateElementFn, EffectDefinition, FontOutput, GlyphData, LayoutBBox, LineCap, PathCommand, Point, RenderStageContext, ResolvedEffect, ShapedGlyph, ShaperFactory, Stroke, TegakiBundle, TegakiEffectConfigs, TegakiEffectName, TegakiEffects, TegakiEngine, TegakiEngineOptions, TegakiGlyphData, TegakiMultiEffectName, TegakiQuality, TegakiSingletonEffectName, TextLayout, TimeControlMode, TimeControlProp, TimedPoint, Timeline, TimelineConfig, TimelineEntry, buildChildren, buildRootProps, computeLayoutBbox, computeTextLayout, computeTimeline, createBundle, domCreateElement, drawGlyph, ensureFontFace, findEffect, findEffects, getBundle, getEffectDefinition, hasRenderHooks, registerBundle, resolveBundle, resolveEffects };
|
package/dist/core/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as findEffect, a as createBundle, b as hasRenderHooks, c as resolveBundle, d as computeTimeline, f as computeLayoutBbox, h as drawGlyph, i as domCreateElement, l as BUNDLE_VERSION, m as ensureFontFace, n as buildChildren, o as getBundle, p as computeTextLayout, r as buildRootProps, s as registerBundle, t as TegakiEngine, u as COMPATIBLE_BUNDLE_VERSIONS, v as findEffects, x as resolveEffects, y as getEffectDefinition } from "../core-
|
|
1
|
+
import { _ as findEffect, a as createBundle, b as hasRenderHooks, c as resolveBundle, d as computeTimeline, f as computeLayoutBbox, h as drawGlyph, i as domCreateElement, l as BUNDLE_VERSION, m as ensureFontFace, n as buildChildren, o as getBundle, p as computeTextLayout, r as buildRootProps, s as registerBundle, t as TegakiEngine, u as COMPATIBLE_BUNDLE_VERSIONS, v as findEffects, x as resolveEffects, y as getEffectDefinition } from "../core-BrU5Htjs.mjs";
|
|
2
2
|
export { BUNDLE_VERSION, COMPATIBLE_BUNDLE_VERSIONS, TegakiEngine, buildChildren, buildRootProps, computeLayoutBbox, computeTextLayout, computeTimeline, createBundle, domCreateElement, drawGlyph, ensureFontFace, findEffect, findEffects, getBundle, getEffectDefinition, hasRenderHooks, registerBundle, resolveBundle, resolveEffects };
|
|
@@ -264,6 +264,24 @@ function cssFontFamily(bundle) {
|
|
|
264
264
|
if (bundle.fullFamily) return `'${bundle.family}', '${bundle.fullFamily}'`;
|
|
265
265
|
return `'${bundle.family}'`;
|
|
266
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Look up `glyphData` for a grapheme cluster, with a fallback to its leading
|
|
269
|
+
* codepoint. Devanagari clusters like `"हि"` or `"न्दी"` have no entry in the
|
|
270
|
+
* single-codepoint-keyed `glyphData`; without the fallback every shaped glyph
|
|
271
|
+
* inside such a cluster that the variant map skipped (i.e. nominal forms like
|
|
272
|
+
* the bare `ह`) would resolve to `undefined`, get tagged `hasGlyph: false`,
|
|
273
|
+
* and collapse onto the 0.2s `unknownDuration` slot — drawn at end-of-slot
|
|
274
|
+
* via DOM `fillText`. The fallback resolves the cluster's first codepoint
|
|
275
|
+
* (e.g. `ह` from `"हि"`), so the nominal glyph picks up its real stroke data
|
|
276
|
+
* and animates instead of popping in.
|
|
277
|
+
*/
|
|
278
|
+
function lookupGlyphData(font, char) {
|
|
279
|
+
const direct = font.glyphData[char];
|
|
280
|
+
if (direct || char.length <= 1) return direct;
|
|
281
|
+
const cp = char.codePointAt(0);
|
|
282
|
+
if (cp === void 0) return void 0;
|
|
283
|
+
return font.glyphData[String.fromCodePoint(cp)];
|
|
284
|
+
}
|
|
267
285
|
function coerceToString(value) {
|
|
268
286
|
if (value == null || typeof value === "boolean") return "";
|
|
269
287
|
if (typeof value === "string") return value;
|
|
@@ -681,7 +699,12 @@ function measureElement(el, fontSize) {
|
|
|
681
699
|
}
|
|
682
700
|
/**
|
|
683
701
|
* Replace `layout.charOffsets` and `charWidths` with values computed from the
|
|
684
|
-
* shaper's advances, while preserving the DOM's line-break decisions.
|
|
702
|
+
* shaper's advances, while preserving the DOM's line-break decisions. When a
|
|
703
|
+
* `timeline` is provided, each entry's `xOffsetEm` and `yOffsetEm` are also
|
|
704
|
+
* filled in from the shaper's per-glyph pen-walk (mutated in place) and
|
|
705
|
+
* `layout.lineLefts` is populated — together they let the engine draw each
|
|
706
|
+
* glyph at its GPOS-positioned origin (cursive-attachment lift, mark
|
|
707
|
+
* attachment) instead of sharing one position per cluster.
|
|
685
708
|
*
|
|
686
709
|
* The DOM's Range API returns imprecise per-grapheme rects inside a complex-
|
|
687
710
|
* shaped cluster (Arabic joining, Indic conjuncts, ligatures with kern/mark
|
|
@@ -693,7 +716,7 @@ function measureElement(el, fontSize) {
|
|
|
693
716
|
* DOM using a full-line Range — per-grapheme rects inside shaped clusters are
|
|
694
717
|
* not reliable enough to anchor against.
|
|
695
718
|
*/
|
|
696
|
-
function applyShaperPositions(layout, el, text, fontSize, font, shaper) {
|
|
719
|
+
function applyShaperPositions(layout, el, text, fontSize, font, shaper, timeline) {
|
|
697
720
|
const chars = graphemes(text);
|
|
698
721
|
if (!chars.length) return layout;
|
|
699
722
|
const textNode = el.firstChild;
|
|
@@ -715,9 +738,19 @@ function applyShaperPositions(layout, el, text, fontSize, font, shaper) {
|
|
|
715
738
|
utf16ToGrapheme[text.length] = chars.length;
|
|
716
739
|
const charOffsets = layout.charOffsets.slice();
|
|
717
740
|
const charWidths = layout.charWidths.slice();
|
|
741
|
+
const lineLefts = new Array(layout.lines.length).fill(0);
|
|
718
742
|
const emPerUnit = 1 / font.unitsPerEm;
|
|
719
|
-
|
|
720
|
-
|
|
743
|
+
const entryQueue = /* @__PURE__ */ new Map();
|
|
744
|
+
if (timeline) for (let ei = 0; ei < timeline.entries.length; ei++) {
|
|
745
|
+
const e = timeline.entries[ei];
|
|
746
|
+
if (e.glyphId === void 0) continue;
|
|
747
|
+
const key = `${e.graphemeIndex}:${e.glyphId}`;
|
|
748
|
+
const list = entryQueue.get(key);
|
|
749
|
+
if (list) list.push(ei);
|
|
750
|
+
else entryQueue.set(key, [ei]);
|
|
751
|
+
}
|
|
752
|
+
for (let li = 0; li < layout.lines.length; li++) {
|
|
753
|
+
const realIndices = layout.lines[li].filter((idx) => chars[idx] !== "\n");
|
|
721
754
|
if (realIndices.length === 0) continue;
|
|
722
755
|
const lineStartU = graphemeStartU[realIndices[0]];
|
|
723
756
|
const lastReal = realIndices[realIndices.length - 1];
|
|
@@ -729,6 +762,7 @@ function applyShaperPositions(layout, el, text, fontSize, font, shaper) {
|
|
|
729
762
|
let lineLeftPx = Infinity;
|
|
730
763
|
for (const r of lineRects) if (r.left < lineLeftPx) lineLeftPx = r.left;
|
|
731
764
|
const lineLeftEm = (lineLeftPx - elLeft) / scale / fontSize;
|
|
765
|
+
lineLefts[li] = lineLeftEm;
|
|
732
766
|
const lineText = text.slice(lineStartU, lineEndU);
|
|
733
767
|
const lineRTL = RTL_CHAR_RE.test(lineText);
|
|
734
768
|
const shaped = shaper.shape(lineText);
|
|
@@ -737,12 +771,29 @@ function applyShaperPositions(layout, el, text, fontSize, font, shaper) {
|
|
|
737
771
|
const clusterLeft = /* @__PURE__ */ new Map();
|
|
738
772
|
const clusterAdvance = /* @__PURE__ */ new Map();
|
|
739
773
|
let penEm = 0;
|
|
774
|
+
let penYEm = 0;
|
|
740
775
|
for (const g of visualGlyphs) {
|
|
741
776
|
const axEm = g.ax * emPerUnit;
|
|
777
|
+
const ayEm = g.ay * emPerUnit;
|
|
742
778
|
const dxEm = g.dx * emPerUnit;
|
|
743
|
-
|
|
779
|
+
const dyEm = g.dy * emPerUnit;
|
|
780
|
+
const glyphXEm = penEm + dxEm;
|
|
781
|
+
const glyphYEm = penYEm - dyEm;
|
|
782
|
+
if (!clusterLeft.has(g.cl)) clusterLeft.set(g.cl, glyphXEm);
|
|
744
783
|
clusterAdvance.set(g.cl, (clusterAdvance.get(g.cl) ?? 0) + axEm);
|
|
784
|
+
if (timeline) {
|
|
785
|
+
const gIdx = utf16ToGrapheme[lineStartU + g.cl];
|
|
786
|
+
if (gIdx !== void 0 && gIdx >= 0) {
|
|
787
|
+
const ei = entryQueue.get(`${gIdx}:${g.g}`)?.shift();
|
|
788
|
+
if (ei !== void 0) {
|
|
789
|
+
const entry = timeline.entries[ei];
|
|
790
|
+
entry.xOffsetEm = glyphXEm;
|
|
791
|
+
entry.yOffsetEm = glyphYEm;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
745
795
|
penEm += axEm;
|
|
796
|
+
penYEm -= ayEm;
|
|
746
797
|
}
|
|
747
798
|
const assigned = /* @__PURE__ */ new Set();
|
|
748
799
|
for (const [cl, leftEm] of clusterLeft) {
|
|
@@ -767,7 +818,8 @@ function applyShaperPositions(layout, el, text, fontSize, font, shaper) {
|
|
|
767
818
|
return {
|
|
768
819
|
lines: layout.lines,
|
|
769
820
|
charOffsets,
|
|
770
|
-
charWidths
|
|
821
|
+
charWidths,
|
|
822
|
+
lineLefts
|
|
771
823
|
};
|
|
772
824
|
}
|
|
773
825
|
/**
|
|
@@ -981,7 +1033,7 @@ function computeGraphemeTimeline(text, font, config) {
|
|
|
981
1033
|
});
|
|
982
1034
|
continue;
|
|
983
1035
|
}
|
|
984
|
-
const glyph = font
|
|
1036
|
+
const glyph = lookupGlyphData(font, char);
|
|
985
1037
|
if (glyph) {
|
|
986
1038
|
const part = partitionGlyph(glyph, unknownDuration, deferDots);
|
|
987
1039
|
sched.add({
|
|
@@ -1047,7 +1099,7 @@ function computeShapedTimeline(text, font, config, shaper) {
|
|
|
1047
1099
|
const clusterText = text.slice(clusterStart, clusterEnd);
|
|
1048
1100
|
const firstChar = chars[graphemeIdx];
|
|
1049
1101
|
const isWhitespace = /^\s+$/.test(clusterText);
|
|
1050
|
-
const data = font.glyphDataById?.[glyph.g] ?? font
|
|
1102
|
+
const data = font.glyphDataById?.[glyph.g] ?? lookupGlyphData(font, firstChar);
|
|
1051
1103
|
const hasGlyph = !!data;
|
|
1052
1104
|
if (isWhitespace) {
|
|
1053
1105
|
sched.separate("word", {
|
|
@@ -1412,6 +1464,7 @@ var TegakiEngine = class {
|
|
|
1412
1464
|
_quality;
|
|
1413
1465
|
_showOverlay = false;
|
|
1414
1466
|
_onComplete;
|
|
1467
|
+
_onChangeTimeline;
|
|
1415
1468
|
_direction;
|
|
1416
1469
|
_resolvedEffects = resolveEffects(void 0);
|
|
1417
1470
|
_seed;
|
|
@@ -1498,6 +1551,32 @@ var TegakiEngine = class {
|
|
|
1498
1551
|
get duration() {
|
|
1499
1552
|
return this._timeline.totalDuration;
|
|
1500
1553
|
}
|
|
1554
|
+
/**
|
|
1555
|
+
* The engine's current timeline — the same object that drives rendering.
|
|
1556
|
+
* Reflects the resolved shaper once the (async) shaper promise has
|
|
1557
|
+
* settled; use the `onChangeTimeline` option to be notified of recomputations.
|
|
1558
|
+
* Treat the returned object as read-only.
|
|
1559
|
+
*/
|
|
1560
|
+
get timeline() {
|
|
1561
|
+
return this._timeline;
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* Compute a timeline for arbitrary text against this engine's currently-
|
|
1565
|
+
* loaded font, timing config, and resolved shaper. Useful for measuring
|
|
1566
|
+
* the duration of hypothetical text without changing what's rendered
|
|
1567
|
+
* (e.g. layout planning, fade-in scheduling).
|
|
1568
|
+
*
|
|
1569
|
+
* Returns an empty timeline when no font is loaded. The result reflects
|
|
1570
|
+
* shaper state at call time — call after `onChangeTimeline` has fired
|
|
1571
|
+
* once to be sure the shaper has resolved.
|
|
1572
|
+
*/
|
|
1573
|
+
computeTimeline(text) {
|
|
1574
|
+
if (!this._font) return {
|
|
1575
|
+
entries: [],
|
|
1576
|
+
totalDuration: 0
|
|
1577
|
+
};
|
|
1578
|
+
return computeTimeline(text, this._font, this._timing, this._shaper);
|
|
1579
|
+
}
|
|
1501
1580
|
get isPlaying() {
|
|
1502
1581
|
return this._playing;
|
|
1503
1582
|
}
|
|
@@ -1547,7 +1626,7 @@ var TegakiEngine = class {
|
|
|
1547
1626
|
let dirtyRender = false;
|
|
1548
1627
|
let dirtyPlayback = false;
|
|
1549
1628
|
if ("text" in options) {
|
|
1550
|
-
const nextText = (options.text ?? "").replace(/\r\n?/g, "\n");
|
|
1629
|
+
const nextText = (options.text ?? "").replace(/\r\n?/g, "\n").normalize("NFC");
|
|
1551
1630
|
if (nextText !== this._text) {
|
|
1552
1631
|
this._text = nextText;
|
|
1553
1632
|
dirtyTimeline = true;
|
|
@@ -1621,6 +1700,7 @@ var TegakiEngine = class {
|
|
|
1621
1700
|
dirtyRender = true;
|
|
1622
1701
|
}
|
|
1623
1702
|
if ("onComplete" in options) this._onComplete = options.onComplete;
|
|
1703
|
+
if ("onChangeTimeline" in options) this._onChangeTimeline = options.onChangeTimeline;
|
|
1624
1704
|
if (dirtyTimeline) this._recomputeTimeline();
|
|
1625
1705
|
if (dirtyRender || dirtyTimeline || dirtyLayout) this._updateDom();
|
|
1626
1706
|
if (dirtyLayout) this._recomputeLayout();
|
|
@@ -1802,6 +1882,7 @@ var TegakiEngine = class {
|
|
|
1802
1882
|
entries: [],
|
|
1803
1883
|
totalDuration: 0
|
|
1804
1884
|
};
|
|
1885
|
+
this._onChangeTimeline?.(this._timeline);
|
|
1805
1886
|
}
|
|
1806
1887
|
_recomputeLayout() {
|
|
1807
1888
|
if (this._fontReady && this._font?.family && this._fontSize && this._containerWidth && this._text) {
|
|
@@ -1810,7 +1891,7 @@ var TegakiEngine = class {
|
|
|
1810
1891
|
if (key === this._layoutKey) return;
|
|
1811
1892
|
this._layoutKey = key;
|
|
1812
1893
|
let layout = computeTextLayout(this._overlayEl, this._fontSize);
|
|
1813
|
-
if (this._shaper && this._font) layout = applyShaperPositions(layout, this._overlayEl, this._text, this._fontSize, this._font, this._shaper);
|
|
1894
|
+
if (this._shaper && this._font) layout = applyShaperPositions(layout, this._overlayEl, this._text, this._fontSize, this._font, this._shaper, this._timeline);
|
|
1814
1895
|
this._layout = layout;
|
|
1815
1896
|
} else {
|
|
1816
1897
|
this._layoutKey = "";
|
|
@@ -1987,15 +2068,16 @@ var TegakiEngine = class {
|
|
|
1987
2068
|
const lineIdx = graphemeToLine[charIdx] ?? -1;
|
|
1988
2069
|
if (lineIdx < 0) continue;
|
|
1989
2070
|
const y = lineIdx * lineHeight;
|
|
1990
|
-
const
|
|
1991
|
-
const
|
|
2071
|
+
const lineLeftEm = layout.lineLefts?.[lineIdx];
|
|
2072
|
+
const x = entry.xOffsetEm !== void 0 && lineLeftEm !== void 0 ? (lineLeftEm + entry.xOffsetEm) * fontSize : (layout.charOffsets[charIdx] ?? 0) * fontSize;
|
|
2073
|
+
const glyph = (entry.glyphId !== void 0 ? font.glyphDataById?.[entry.glyphId] : void 0) ?? lookupGlyphData(font, entry.char);
|
|
1992
2074
|
if (glyph && entry.hasGlyph) {
|
|
1993
2075
|
let localTime = Math.max(0, Math.min(currentTime - entry.offset, entry.duration));
|
|
1994
2076
|
const glyphEasing = this._timing?.glyphEasing;
|
|
1995
2077
|
if (glyphEasing && entry.duration > 0) localTime = glyphEasing(localTime / entry.duration) * entry.duration;
|
|
1996
2078
|
drawGlyph(ctx, glyph, {
|
|
1997
2079
|
x,
|
|
1998
|
-
y: y + halfLeading,
|
|
2080
|
+
y: y + halfLeading + (entry.yOffsetEm ?? 0) * fontSize,
|
|
1999
2081
|
fontSize,
|
|
2000
2082
|
unitsPerEm: font.unitsPerEm,
|
|
2001
2083
|
ascender: font.ascender,
|
|
@@ -2048,4 +2130,4 @@ var TegakiEngine = class {
|
|
|
2048
2130
|
//#endregion
|
|
2049
2131
|
export { findEffect as _, createBundle as a, hasRenderHooks as b, resolveBundle as c, computeTimeline as d, computeLayoutBbox as f, coerceToString as g, drawGlyph as h, domCreateElement as i, BUNDLE_VERSION as l, ensureFontFace as m, buildChildren as n, getBundle as o, computeTextLayout as p, buildRootProps as r, registerBundle as s, TegakiEngine as t, COMPATIBLE_BUNDLE_VERSIONS as u, findEffects as v, resolveEffects as x, getEffectDefinition as y };
|
|
2050
2132
|
|
|
2051
|
-
//# sourceMappingURL=core-
|
|
2133
|
+
//# sourceMappingURL=core-BrU5Htjs.mjs.map
|