modern-text 1.12.0 → 2.0.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/README.md +283 -22
- package/dist/deformations/index.d.cts +2 -2
- package/dist/deformations/index.d.mts +2 -2
- package/dist/deformations/index.d.ts +2 -2
- package/dist/index.cjs +3 -2
- package/dist/index.d.cts +67 -8
- package/dist/index.d.mts +67 -8
- package/dist/index.d.ts +67 -8
- package/dist/index.js +4 -3
- package/dist/index.mjs +2 -2
- package/dist/shared/{modern-text.d-RdVkZg.d.cts → modern-text.BD7PBYt7.d.cts} +1 -1
- package/dist/shared/{modern-text.Bo2MzSls.d.ts → modern-text.BxijkspX.d.ts} +1 -1
- package/dist/shared/{modern-text.DR1hbjgA.cjs → modern-text.CBgc-cQ1.cjs} +276 -7
- package/dist/shared/{modern-text.w5MRHZDB.d.mts → modern-text.CYa4lfoG.d.mts} +1 -1
- package/dist/shared/{modern-text.CrFHIJtB.mjs → modern-text.ChzjFjsk.mjs} +275 -7
- package/dist/shared/{modern-text.C8g4NZgY.d.cts → modern-text.D4WopQCu.d.cts} +55 -22
- package/dist/shared/{modern-text.C8g4NZgY.d.mts → modern-text.D4WopQCu.d.mts} +55 -22
- package/dist/shared/{modern-text.C8g4NZgY.d.ts → modern-text.D4WopQCu.d.ts} +55 -22
- package/dist/web-components/index.cjs +2 -2
- package/dist/web-components/index.d.cts +1 -1
- package/dist/web-components/index.d.mts +1 -1
- package/dist/web-components/index.d.ts +1 -1
- package/dist/web-components/index.mjs +2 -2
- package/package.json +6 -6
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isNone, isGradient, normalizeGradient, clearUndef, Reactivable, normalizeText, getDefaultStyle, property } from 'modern-idoc';
|
|
2
2
|
import { svgToDom, svgToPath2DSet, Path2DSet, Transform2D, setCanvasContext, Path2D, BoundingBox, Vector2 } from 'modern-path2d';
|
|
3
|
-
import { a as definePlugin, b as deformationPlugin } from './modern-text.JF1ny7A-.mjs';
|
|
4
3
|
import { fonts } from 'modern-font';
|
|
4
|
+
import { a as definePlugin, b as deformationPlugin } from './modern-text.JF1ny7A-.mjs';
|
|
5
5
|
|
|
6
6
|
function createSvgLoader() {
|
|
7
7
|
const loaded = /* @__PURE__ */ new Map();
|
|
@@ -529,6 +529,18 @@ class Character {
|
|
|
529
529
|
this.fontStyle = fsSelectionMap[os2.fsSelection] ?? macStyleMap[head.macStyle];
|
|
530
530
|
return this;
|
|
531
531
|
}
|
|
532
|
+
/**
|
|
533
|
+
* Populate glyph metrics only (advance width/height, ascender/descender,
|
|
534
|
+
* baseline, …) without building the glyph `path` or touching boxes.
|
|
535
|
+
*
|
|
536
|
+
* The DOM {@link DomMeasurer} never needs this — it reads positions back from the
|
|
537
|
+
* browser. A pure-JS measurer (e.g. `FontMeasurer`) must know advances *before*
|
|
538
|
+
* it can place characters, so it calls this ahead of layout. `update()` later
|
|
539
|
+
* recomputes the same metrics while building the path, so this is idempotent.
|
|
540
|
+
*/
|
|
541
|
+
measureGlyph(fonts) {
|
|
542
|
+
return this.updateGlyph(this._getFontSFNT(fonts));
|
|
543
|
+
}
|
|
532
544
|
update(fonts) {
|
|
533
545
|
const sfnt = this._getFontSFNT(fonts);
|
|
534
546
|
if (!sfnt) {
|
|
@@ -721,7 +733,7 @@ function getSharedContainer() {
|
|
|
721
733
|
sharedContainer = container;
|
|
722
734
|
return container;
|
|
723
735
|
}
|
|
724
|
-
class
|
|
736
|
+
class DomMeasurer {
|
|
725
737
|
static notZeroStyles = /* @__PURE__ */ new Set([
|
|
726
738
|
"width",
|
|
727
739
|
"height"
|
|
@@ -756,7 +768,7 @@ class Measurer {
|
|
|
756
768
|
return cached;
|
|
757
769
|
}
|
|
758
770
|
const domStyle = {};
|
|
759
|
-
const { notZeroStyles, pxStyles } =
|
|
771
|
+
const { notZeroStyles, pxStyles } = DomMeasurer;
|
|
760
772
|
for (const key in style) {
|
|
761
773
|
const value = style[key];
|
|
762
774
|
if (notZeroStyles.has(key) && value === 0) {
|
|
@@ -1065,6 +1077,253 @@ class Measurer {
|
|
|
1065
1077
|
}
|
|
1066
1078
|
}
|
|
1067
1079
|
|
|
1080
|
+
function side(style, name, edge) {
|
|
1081
|
+
return style[`${name}${edge}`] ?? style[name] ?? 0;
|
|
1082
|
+
}
|
|
1083
|
+
class FontMeasurer {
|
|
1084
|
+
measure(paragraphs, rootStyle, _dom, fonts$1) {
|
|
1085
|
+
const _fonts = fonts$1 ?? fonts;
|
|
1086
|
+
for (const paragraph of paragraphs) {
|
|
1087
|
+
for (const fragment of paragraph.fragments) {
|
|
1088
|
+
for (const character of fragment.characters) {
|
|
1089
|
+
character.measureGlyph(_fonts);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
return rootStyle.writingMode.includes("vertical") ? this._measureVertical(paragraphs, rootStyle) : this._measureHorizontal(paragraphs, rootStyle);
|
|
1094
|
+
}
|
|
1095
|
+
_rootPadding(rootStyle) {
|
|
1096
|
+
return {
|
|
1097
|
+
top: side(rootStyle, "padding", "Top"),
|
|
1098
|
+
right: side(rootStyle, "padding", "Right"),
|
|
1099
|
+
bottom: side(rootStyle, "padding", "Bottom"),
|
|
1100
|
+
left: side(rootStyle, "padding", "Left")
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
_measureHorizontal(paragraphs, rootStyle) {
|
|
1104
|
+
const rootPad = this._rootPadding(rootStyle);
|
|
1105
|
+
const hasWidth = typeof rootStyle.width === "number";
|
|
1106
|
+
const availWidth = hasWidth ? rootStyle.width - rootPad.left - rootPad.right : Infinity;
|
|
1107
|
+
let y = rootPad.top;
|
|
1108
|
+
let maxRight = rootPad.left;
|
|
1109
|
+
for (const paragraph of paragraphs) {
|
|
1110
|
+
const pStyle = paragraph.computedStyle;
|
|
1111
|
+
const pBox = paragraph.style;
|
|
1112
|
+
const mTop = side(pBox, "margin", "Top");
|
|
1113
|
+
const mBottom = side(pBox, "margin", "Bottom");
|
|
1114
|
+
const mLeft = side(pBox, "margin", "Left");
|
|
1115
|
+
const mRight = side(pBox, "margin", "Right");
|
|
1116
|
+
const pTop = side(pBox, "padding", "Top");
|
|
1117
|
+
const pBottom = side(pBox, "padding", "Bottom");
|
|
1118
|
+
const pLeft = side(pBox, "padding", "Left");
|
|
1119
|
+
const pRight = side(pBox, "padding", "Right");
|
|
1120
|
+
const liLeft = rootPad.left + mLeft + pLeft;
|
|
1121
|
+
const liAvail = availWidth === Infinity ? Infinity : availWidth - mLeft - mRight - pLeft - pRight;
|
|
1122
|
+
y += mTop + pTop;
|
|
1123
|
+
const paraTop = y;
|
|
1124
|
+
let paraRight = liLeft;
|
|
1125
|
+
const lines = this._breakLines(paragraph, liAvail);
|
|
1126
|
+
const align = pStyle.textAlign;
|
|
1127
|
+
const indent = pStyle.textIndent ?? 0;
|
|
1128
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1129
|
+
const line = lines[i];
|
|
1130
|
+
const lineIndent = i === 0 ? indent : 0;
|
|
1131
|
+
let lineHeight = pStyle.fontSize * pStyle.lineHeight;
|
|
1132
|
+
let contentWidth = 0;
|
|
1133
|
+
for (const c of line) {
|
|
1134
|
+
if (c.fontHeight > lineHeight) {
|
|
1135
|
+
lineHeight = c.fontHeight;
|
|
1136
|
+
}
|
|
1137
|
+
contentWidth += this._advance(c);
|
|
1138
|
+
}
|
|
1139
|
+
let x = liLeft + lineIndent;
|
|
1140
|
+
if (liAvail !== Infinity) {
|
|
1141
|
+
const slack = liAvail - lineIndent - contentWidth;
|
|
1142
|
+
if (align === "center") {
|
|
1143
|
+
x += slack / 2;
|
|
1144
|
+
} else if (align === "end" || align === "right") {
|
|
1145
|
+
x += slack;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
for (const c of line) {
|
|
1149
|
+
const adv = c.advanceWidth;
|
|
1150
|
+
const contentHeight = c.advanceHeight;
|
|
1151
|
+
const fontHeight = c.fontHeight;
|
|
1152
|
+
c.inlineBox.left = x;
|
|
1153
|
+
c.inlineBox.top = y + (lineHeight - contentHeight) / 2;
|
|
1154
|
+
c.inlineBox.width = adv;
|
|
1155
|
+
c.inlineBox.height = contentHeight;
|
|
1156
|
+
c.lineBox.left = x;
|
|
1157
|
+
c.lineBox.top = c.inlineBox.top + (contentHeight - fontHeight) / 2;
|
|
1158
|
+
c.lineBox.width = adv;
|
|
1159
|
+
c.lineBox.height = fontHeight;
|
|
1160
|
+
x += this._advance(c);
|
|
1161
|
+
}
|
|
1162
|
+
if (x > paraRight) {
|
|
1163
|
+
paraRight = x;
|
|
1164
|
+
}
|
|
1165
|
+
y += lineHeight;
|
|
1166
|
+
}
|
|
1167
|
+
if (paraRight > maxRight) {
|
|
1168
|
+
maxRight = paraRight;
|
|
1169
|
+
}
|
|
1170
|
+
for (const fragment of paragraph.fragments) {
|
|
1171
|
+
this._unionInto(fragment.inlineBox, fragment.characters.map((c) => c.inlineBox));
|
|
1172
|
+
}
|
|
1173
|
+
paragraph.lineBox.left = liLeft;
|
|
1174
|
+
paragraph.lineBox.top = paraTop;
|
|
1175
|
+
paragraph.lineBox.width = liAvail === Infinity ? paraRight - liLeft : liAvail;
|
|
1176
|
+
paragraph.lineBox.height = y - paraTop;
|
|
1177
|
+
y += pBottom + mBottom;
|
|
1178
|
+
}
|
|
1179
|
+
const contentBottom = y + rootPad.bottom;
|
|
1180
|
+
const totalWidth = hasWidth ? rootStyle.width : maxRight + rootPad.right;
|
|
1181
|
+
const totalHeight = typeof rootStyle.height === "number" ? rootStyle.height : contentBottom;
|
|
1182
|
+
if (typeof rootStyle.height === "number") {
|
|
1183
|
+
const slack = totalHeight - contentBottom;
|
|
1184
|
+
const va = rootStyle.verticalAlign;
|
|
1185
|
+
const dy = va === "middle" ? slack / 2 : va === "bottom" ? slack : 0;
|
|
1186
|
+
if (dy) {
|
|
1187
|
+
this._shiftAll(paragraphs, dy);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
return {
|
|
1191
|
+
paragraphs,
|
|
1192
|
+
boundingBox: new BoundingBox(0, 0, totalWidth, totalHeight)
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
/**
|
|
1196
|
+
* Vertical writing-mode (`vertical-rl`): columns stack right-to-left, glyphs
|
|
1197
|
+
* flow top→bottom. It is the horizontal layout with the inline and block axes
|
|
1198
|
+
* swapped — the inline (down-column) advance is `advanceWidth` (CJK upright = em;
|
|
1199
|
+
* Latin is rotated, so its advance ≈ its width), the cross-axis content box is
|
|
1200
|
+
* `advanceHeight`, and the column thickness is `fontHeight`. The lineBox is
|
|
1201
|
+
* derived exactly as DomMeasurer.measureParagraphDom's vertical branch, so it
|
|
1202
|
+
* stays accurate even though inlineBox carries the content-box rounding residual.
|
|
1203
|
+
*
|
|
1204
|
+
* v1: `vertical-rl` only; no per-paragraph margin/padding or block alignment.
|
|
1205
|
+
*/
|
|
1206
|
+
_measureVertical(paragraphs, rootStyle) {
|
|
1207
|
+
const rootPad = this._rootPadding(rootStyle);
|
|
1208
|
+
const hasHeight = typeof rootStyle.height === "number";
|
|
1209
|
+
const availHeight = hasHeight ? rootStyle.height - rootPad.top - rootPad.bottom : Infinity;
|
|
1210
|
+
const columns = [];
|
|
1211
|
+
for (const paragraph of paragraphs) {
|
|
1212
|
+
const strut = paragraph.computedStyle.fontSize * paragraph.computedStyle.lineHeight;
|
|
1213
|
+
for (const line of this._breakLines(paragraph, availHeight)) {
|
|
1214
|
+
let thickness = strut;
|
|
1215
|
+
for (const c of line) {
|
|
1216
|
+
if (c.fontHeight > thickness) {
|
|
1217
|
+
thickness = c.fontHeight;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
columns.push({ chars: line, thickness });
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
const totalThickness = columns.reduce((sum, c) => sum + c.thickness, 0);
|
|
1224
|
+
const hasWidth = typeof rootStyle.width === "number";
|
|
1225
|
+
const blockWidth = hasWidth ? Math.max(rootStyle.width - rootPad.left - rootPad.right, totalThickness) : totalThickness;
|
|
1226
|
+
let xRight = rootPad.left + blockWidth;
|
|
1227
|
+
let maxBottom = rootPad.top;
|
|
1228
|
+
for (const column of columns) {
|
|
1229
|
+
xRight -= column.thickness;
|
|
1230
|
+
const colLeft = xRight;
|
|
1231
|
+
let y = rootPad.top;
|
|
1232
|
+
for (const c of column.chars) {
|
|
1233
|
+
const advance = c.advanceWidth;
|
|
1234
|
+
const contentWidth = c.advanceHeight;
|
|
1235
|
+
const fontHeight = c.fontHeight;
|
|
1236
|
+
c.inlineBox.top = y;
|
|
1237
|
+
c.inlineBox.height = advance;
|
|
1238
|
+
c.inlineBox.width = contentWidth;
|
|
1239
|
+
c.inlineBox.left = colLeft + (column.thickness - contentWidth) / 2;
|
|
1240
|
+
c.lineBox.left = c.inlineBox.left + (contentWidth - fontHeight) / 2;
|
|
1241
|
+
c.lineBox.top = y;
|
|
1242
|
+
c.lineBox.width = fontHeight;
|
|
1243
|
+
c.lineBox.height = advance;
|
|
1244
|
+
y += this._advance(c);
|
|
1245
|
+
}
|
|
1246
|
+
if (y > maxBottom) {
|
|
1247
|
+
maxBottom = y;
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
for (const paragraph of paragraphs) {
|
|
1251
|
+
for (const fragment of paragraph.fragments) {
|
|
1252
|
+
this._unionInto(fragment.inlineBox, fragment.characters.map((c) => c.inlineBox));
|
|
1253
|
+
}
|
|
1254
|
+
this._unionInto(
|
|
1255
|
+
paragraph.lineBox,
|
|
1256
|
+
paragraph.fragments.flatMap((f) => f.characters.map((c) => c.inlineBox))
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
const totalWidth = hasWidth ? rootStyle.width : blockWidth + rootPad.left + rootPad.right;
|
|
1260
|
+
const totalHeight = hasHeight ? rootStyle.height : maxBottom + rootPad.bottom;
|
|
1261
|
+
return {
|
|
1262
|
+
paragraphs,
|
|
1263
|
+
boundingBox: new BoundingBox(0, 0, totalWidth, totalHeight)
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
/** Advance step including CSS letter-spacing (px). */
|
|
1267
|
+
_advance(character) {
|
|
1268
|
+
return character.advanceWidth + (character.computedStyle.letterSpacing ?? 0);
|
|
1269
|
+
}
|
|
1270
|
+
/**
|
|
1271
|
+
* Break a paragraph's characters into visual lines.
|
|
1272
|
+
* v1: `word-break: break-all` (break before any character that would overflow)
|
|
1273
|
+
* plus explicit `\n`/`\r` hard breaks. The newline itself occupies no line box.
|
|
1274
|
+
*/
|
|
1275
|
+
_breakLines(paragraph, avail) {
|
|
1276
|
+
const lines = [];
|
|
1277
|
+
let current = [];
|
|
1278
|
+
let width = 0;
|
|
1279
|
+
const flush = () => {
|
|
1280
|
+
lines.push(current);
|
|
1281
|
+
current = [];
|
|
1282
|
+
width = 0;
|
|
1283
|
+
};
|
|
1284
|
+
for (const fragment of paragraph.fragments) {
|
|
1285
|
+
for (const c of fragment.characters) {
|
|
1286
|
+
if (c.content === "\n" || c.content === "\r") {
|
|
1287
|
+
flush();
|
|
1288
|
+
continue;
|
|
1289
|
+
}
|
|
1290
|
+
if (avail !== Infinity && current.length > 0 && width + c.advanceWidth > avail) {
|
|
1291
|
+
flush();
|
|
1292
|
+
}
|
|
1293
|
+
current.push(c);
|
|
1294
|
+
width += this._advance(c);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
flush();
|
|
1298
|
+
return lines;
|
|
1299
|
+
}
|
|
1300
|
+
_unionInto(target, boxes) {
|
|
1301
|
+
const used = boxes.filter((b) => b.width !== 0 || b.height !== 0);
|
|
1302
|
+
if (!used.length) {
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
const u = BoundingBox.from(...used);
|
|
1306
|
+
target.left = u.left;
|
|
1307
|
+
target.top = u.top;
|
|
1308
|
+
target.width = u.width;
|
|
1309
|
+
target.height = u.height;
|
|
1310
|
+
}
|
|
1311
|
+
_shiftAll(paragraphs, dy) {
|
|
1312
|
+
for (const paragraph of paragraphs) {
|
|
1313
|
+
paragraph.lineBox.top += dy;
|
|
1314
|
+
for (const fragment of paragraph.fragments) {
|
|
1315
|
+
fragment.inlineBox.top += dy;
|
|
1316
|
+
for (const character of fragment.characters) {
|
|
1317
|
+
character.inlineBox.top += dy;
|
|
1318
|
+
character.lineBox.top += dy;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
dispose() {
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1068
1327
|
function backgroundPlugin() {
|
|
1069
1328
|
const pathSet = new Path2DSet();
|
|
1070
1329
|
const loader = createSvgLoader();
|
|
@@ -1823,7 +2082,7 @@ class Text extends Reactivable {
|
|
|
1823
2082
|
glyphBox = new BoundingBox();
|
|
1824
2083
|
pathBox = new BoundingBox();
|
|
1825
2084
|
boundingBox = new BoundingBox();
|
|
1826
|
-
measurer = new
|
|
2085
|
+
measurer = new DomMeasurer();
|
|
1827
2086
|
plugins = /* @__PURE__ */ new Map();
|
|
1828
2087
|
pathSets = [];
|
|
1829
2088
|
_paragraphs = [];
|
|
@@ -1881,6 +2140,12 @@ class Text extends Reactivable {
|
|
|
1881
2140
|
outline,
|
|
1882
2141
|
deformation
|
|
1883
2142
|
} = normalizeText(options);
|
|
2143
|
+
if (options.measurer && typeof options.measurer !== "string") {
|
|
2144
|
+
this.measurer = options.measurer;
|
|
2145
|
+
} else {
|
|
2146
|
+
const kind = options.measurer ?? (fonts ? "font" : "dom");
|
|
2147
|
+
this.measurer = kind === "font" ? new FontMeasurer() : new DomMeasurer();
|
|
2148
|
+
}
|
|
1884
2149
|
this.debug = options.debug ?? false;
|
|
1885
2150
|
this.content = content;
|
|
1886
2151
|
this.effects = effects;
|
|
@@ -1955,6 +2220,9 @@ class Text extends Reactivable {
|
|
|
1955
2220
|
}
|
|
1956
2221
|
createDom() {
|
|
1957
2222
|
this._update();
|
|
2223
|
+
if (!this.measurer.createDom) {
|
|
2224
|
+
throw new Error("current measurer does not support createDom()");
|
|
2225
|
+
}
|
|
1958
2226
|
return this.measurer.createDom(this.paragraphs, this.computedStyle);
|
|
1959
2227
|
}
|
|
1960
2228
|
measure(dom = this.measureDom) {
|
|
@@ -1968,7 +2236,7 @@ class Text extends Reactivable {
|
|
|
1968
2236
|
boundingBox: this.boundingBox
|
|
1969
2237
|
};
|
|
1970
2238
|
this._update();
|
|
1971
|
-
const result = this.measurer.measure(this.paragraphs, this.computedStyle, dom);
|
|
2239
|
+
const result = this.measurer.measure(this.paragraphs, this.computedStyle, dom, this.fonts);
|
|
1972
2240
|
this.paragraphs = result.paragraphs;
|
|
1973
2241
|
this.lineBox = result.boundingBox;
|
|
1974
2242
|
const characters = this.characters;
|
|
@@ -2100,7 +2368,7 @@ class Text extends Reactivable {
|
|
|
2100
2368
|
options.onContext?.(ctx);
|
|
2101
2369
|
}
|
|
2102
2370
|
dispose() {
|
|
2103
|
-
this.measurer.dispose();
|
|
2371
|
+
this.measurer.dispose?.();
|
|
2104
2372
|
this._renderer = void 0;
|
|
2105
2373
|
this._rendererCtx = void 0;
|
|
2106
2374
|
}
|
|
@@ -2136,4 +2404,4 @@ __decorateClass([
|
|
|
2136
2404
|
property({ internal: true })
|
|
2137
2405
|
], Text.prototype, "fonts");
|
|
2138
2406
|
|
|
2139
|
-
export { Canvas2DRenderer as C,
|
|
2407
|
+
export { Canvas2DRenderer as C, DomMeasurer as D, FontMeasurer as F, Paragraph as P, Text as T, Character as a, Fragment as b, backgroundPlugin as c, createSvgLoader as d, createSvgParser as e, getHighlightStyle as f, getEffectTransform2D as g, highlightPlugin as h, isEqualObject as i, isEqualValue as j, parseTransformOrigin as k, listStylePlugin as l, parseValueNumber as m, textDefaultStyle as n, outlinePlugin as o, parseColormap as p, renderPlugin as r, textDecorationPlugin as t };
|
|
@@ -57,6 +57,16 @@ declare class Character {
|
|
|
57
57
|
constructor(content: string, index: number, parent: Fragment);
|
|
58
58
|
protected _getFontSFNT(fonts?: Fonts): SFNT | undefined;
|
|
59
59
|
updateGlyph(sfnt?: SFNT | undefined): this;
|
|
60
|
+
/**
|
|
61
|
+
* Populate glyph metrics only (advance width/height, ascender/descender,
|
|
62
|
+
* baseline, …) without building the glyph `path` or touching boxes.
|
|
63
|
+
*
|
|
64
|
+
* The DOM {@link DomMeasurer} never needs this — it reads positions back from the
|
|
65
|
+
* browser. A pure-JS measurer (e.g. `FontMeasurer`) must know advances *before*
|
|
66
|
+
* it can place characters, so it calls this ahead of layout. `update()` later
|
|
67
|
+
* recomputes the same metrics while building the path, so this is idempotent.
|
|
68
|
+
*/
|
|
69
|
+
measureGlyph(fonts?: Fonts): this;
|
|
60
70
|
update(fonts?: Fonts): this;
|
|
61
71
|
protected _italic(path: Path2D, startPoint?: Vector2Like): void;
|
|
62
72
|
getGlyphMinMax(min?: Vector2, max?: Vector2, withStyle?: boolean): {
|
|
@@ -66,24 +76,6 @@ declare class Character {
|
|
|
66
76
|
getGlyphBoundingBox(withStyle?: boolean): BoundingBox | undefined;
|
|
67
77
|
}
|
|
68
78
|
|
|
69
|
-
interface Plugin {
|
|
70
|
-
name: string;
|
|
71
|
-
pathSet?: Path2DSet;
|
|
72
|
-
getBoundingBox?: (text: Text$1) => BoundingBox | undefined;
|
|
73
|
-
update?: (text: Text$1) => void;
|
|
74
|
-
updateOrder?: number;
|
|
75
|
-
render?: (renderer: Canvas2DRenderer) => void;
|
|
76
|
-
renderOrder?: number;
|
|
77
|
-
load?: (text: Text$1) => Promise<void>;
|
|
78
|
-
context?: Record<string, any>;
|
|
79
|
-
}
|
|
80
|
-
interface Options extends TextObject {
|
|
81
|
-
debug?: boolean;
|
|
82
|
-
measureDom?: HTMLElement;
|
|
83
|
-
fonts?: Fonts;
|
|
84
|
-
plugins?: Plugin[];
|
|
85
|
-
}
|
|
86
|
-
|
|
87
79
|
interface MeasuredParagraph {
|
|
88
80
|
paragraphIndex: number;
|
|
89
81
|
left: number;
|
|
@@ -127,7 +119,7 @@ interface RootDomStyles {
|
|
|
127
119
|
section: Record<string, any>;
|
|
128
120
|
ul: Record<string, any>;
|
|
129
121
|
}
|
|
130
|
-
declare class
|
|
122
|
+
declare class DomMeasurer {
|
|
131
123
|
static notZeroStyles: Set<string>;
|
|
132
124
|
static pxStyles: Set<string>;
|
|
133
125
|
protected _styleCache: WeakMap<object, Record<string, any>>;
|
|
@@ -166,6 +158,47 @@ declare class Measurer {
|
|
|
166
158
|
dispose(): void;
|
|
167
159
|
}
|
|
168
160
|
|
|
161
|
+
interface Plugin {
|
|
162
|
+
name: string;
|
|
163
|
+
pathSet?: Path2DSet;
|
|
164
|
+
getBoundingBox?: (text: Text$1) => BoundingBox | undefined;
|
|
165
|
+
update?: (text: Text$1) => void;
|
|
166
|
+
updateOrder?: number;
|
|
167
|
+
render?: (renderer: Canvas2DRenderer) => void;
|
|
168
|
+
renderOrder?: number;
|
|
169
|
+
load?: (text: Text$1) => Promise<void>;
|
|
170
|
+
context?: Record<string, any>;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Pluggable layout backend. Implementations fill the four-level boxes
|
|
174
|
+
* (`character.inlineBox`/`lineBox`, `fragment.inlineBox`, `paragraph.lineBox`)
|
|
175
|
+
* in place and return the overall bounding box.
|
|
176
|
+
*
|
|
177
|
+
* - `DomMeasurer` — DOM-based, uses the browser as ground truth (default).
|
|
178
|
+
* - `FontMeasurer` — pure-JS, DOM-free; needs `fonts` to resolve glyph advances.
|
|
179
|
+
*
|
|
180
|
+
* `fonts` is passed positionally by `Text.measure()`; DOM-based measurers ignore
|
|
181
|
+
* it (a method may safely declare fewer parameters than the interface).
|
|
182
|
+
*/
|
|
183
|
+
interface TextMeasurer {
|
|
184
|
+
measure: (paragraphs: Paragraph[], rootStyle: FullStyle, dom?: HTMLElement, fonts?: Fonts) => MeasureDomResult;
|
|
185
|
+
createDom?: (paragraphs: Paragraph[], rootStyle: FullStyle) => HTMLElement;
|
|
186
|
+
dispose?: () => void;
|
|
187
|
+
}
|
|
188
|
+
/** Built-in layout backends. `'font'` → `FontMeasurer`, `'dom'` → `DomMeasurer`. */
|
|
189
|
+
type MeasurerKind = 'dom' | 'font';
|
|
190
|
+
interface Options extends TextObject {
|
|
191
|
+
debug?: boolean;
|
|
192
|
+
measureDom?: HTMLElement;
|
|
193
|
+
fonts?: Fonts;
|
|
194
|
+
plugins?: Plugin[];
|
|
195
|
+
/**
|
|
196
|
+
* Layout backend: `'font'` (pure-JS) or `'dom'` (browser), or a custom
|
|
197
|
+
* `TextMeasurer`. Defaults to `'font'` when `fonts` are provided, else `'dom'`.
|
|
198
|
+
*/
|
|
199
|
+
measurer?: MeasurerKind | TextMeasurer;
|
|
200
|
+
}
|
|
201
|
+
|
|
169
202
|
interface RenderOptions {
|
|
170
203
|
view: HTMLCanvasElement;
|
|
171
204
|
pixelRatio?: number;
|
|
@@ -221,7 +254,7 @@ declare class Text$1 extends Reactivable {
|
|
|
221
254
|
glyphBox: BoundingBox;
|
|
222
255
|
pathBox: BoundingBox;
|
|
223
256
|
boundingBox: BoundingBox;
|
|
224
|
-
measurer:
|
|
257
|
+
measurer: TextMeasurer;
|
|
225
258
|
plugins: Map<string, Plugin>;
|
|
226
259
|
pathSets: Path2DSet[];
|
|
227
260
|
protected _paragraphs: Paragraph[];
|
|
@@ -304,5 +337,5 @@ declare class Canvas2DRenderer {
|
|
|
304
337
|
drawCharacter: (character: Character, effect?: NormalizedEffect) => void;
|
|
305
338
|
}
|
|
306
339
|
|
|
307
|
-
export { Canvas2DRenderer as C, Fragment as F, Paragraph as P, Text$1 as T, Character as a,
|
|
308
|
-
export type {
|
|
340
|
+
export { Canvas2DRenderer as C, DomMeasurer as D, Fragment as F, Paragraph as P, Text$1 as T, Character as a, textDefaultStyle as t };
|
|
341
|
+
export type { MeasureDomResult as M, Options as O, RenderOptions as R, DrawShapePathsOptions as b, MeasureResult as c, MeasuredCharacter as d, MeasuredCharacterRect as e, MeasuredFragment as f, MeasuredParagraph as g, MeasurerKind as h, Plugin as i, TextEvents as j, TextMeasurer as k };
|
|
@@ -57,6 +57,16 @@ declare class Character {
|
|
|
57
57
|
constructor(content: string, index: number, parent: Fragment);
|
|
58
58
|
protected _getFontSFNT(fonts?: Fonts): SFNT | undefined;
|
|
59
59
|
updateGlyph(sfnt?: SFNT | undefined): this;
|
|
60
|
+
/**
|
|
61
|
+
* Populate glyph metrics only (advance width/height, ascender/descender,
|
|
62
|
+
* baseline, …) without building the glyph `path` or touching boxes.
|
|
63
|
+
*
|
|
64
|
+
* The DOM {@link DomMeasurer} never needs this — it reads positions back from the
|
|
65
|
+
* browser. A pure-JS measurer (e.g. `FontMeasurer`) must know advances *before*
|
|
66
|
+
* it can place characters, so it calls this ahead of layout. `update()` later
|
|
67
|
+
* recomputes the same metrics while building the path, so this is idempotent.
|
|
68
|
+
*/
|
|
69
|
+
measureGlyph(fonts?: Fonts): this;
|
|
60
70
|
update(fonts?: Fonts): this;
|
|
61
71
|
protected _italic(path: Path2D, startPoint?: Vector2Like): void;
|
|
62
72
|
getGlyphMinMax(min?: Vector2, max?: Vector2, withStyle?: boolean): {
|
|
@@ -66,24 +76,6 @@ declare class Character {
|
|
|
66
76
|
getGlyphBoundingBox(withStyle?: boolean): BoundingBox | undefined;
|
|
67
77
|
}
|
|
68
78
|
|
|
69
|
-
interface Plugin {
|
|
70
|
-
name: string;
|
|
71
|
-
pathSet?: Path2DSet;
|
|
72
|
-
getBoundingBox?: (text: Text$1) => BoundingBox | undefined;
|
|
73
|
-
update?: (text: Text$1) => void;
|
|
74
|
-
updateOrder?: number;
|
|
75
|
-
render?: (renderer: Canvas2DRenderer) => void;
|
|
76
|
-
renderOrder?: number;
|
|
77
|
-
load?: (text: Text$1) => Promise<void>;
|
|
78
|
-
context?: Record<string, any>;
|
|
79
|
-
}
|
|
80
|
-
interface Options extends TextObject {
|
|
81
|
-
debug?: boolean;
|
|
82
|
-
measureDom?: HTMLElement;
|
|
83
|
-
fonts?: Fonts;
|
|
84
|
-
plugins?: Plugin[];
|
|
85
|
-
}
|
|
86
|
-
|
|
87
79
|
interface MeasuredParagraph {
|
|
88
80
|
paragraphIndex: number;
|
|
89
81
|
left: number;
|
|
@@ -127,7 +119,7 @@ interface RootDomStyles {
|
|
|
127
119
|
section: Record<string, any>;
|
|
128
120
|
ul: Record<string, any>;
|
|
129
121
|
}
|
|
130
|
-
declare class
|
|
122
|
+
declare class DomMeasurer {
|
|
131
123
|
static notZeroStyles: Set<string>;
|
|
132
124
|
static pxStyles: Set<string>;
|
|
133
125
|
protected _styleCache: WeakMap<object, Record<string, any>>;
|
|
@@ -166,6 +158,47 @@ declare class Measurer {
|
|
|
166
158
|
dispose(): void;
|
|
167
159
|
}
|
|
168
160
|
|
|
161
|
+
interface Plugin {
|
|
162
|
+
name: string;
|
|
163
|
+
pathSet?: Path2DSet;
|
|
164
|
+
getBoundingBox?: (text: Text$1) => BoundingBox | undefined;
|
|
165
|
+
update?: (text: Text$1) => void;
|
|
166
|
+
updateOrder?: number;
|
|
167
|
+
render?: (renderer: Canvas2DRenderer) => void;
|
|
168
|
+
renderOrder?: number;
|
|
169
|
+
load?: (text: Text$1) => Promise<void>;
|
|
170
|
+
context?: Record<string, any>;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Pluggable layout backend. Implementations fill the four-level boxes
|
|
174
|
+
* (`character.inlineBox`/`lineBox`, `fragment.inlineBox`, `paragraph.lineBox`)
|
|
175
|
+
* in place and return the overall bounding box.
|
|
176
|
+
*
|
|
177
|
+
* - `DomMeasurer` — DOM-based, uses the browser as ground truth (default).
|
|
178
|
+
* - `FontMeasurer` — pure-JS, DOM-free; needs `fonts` to resolve glyph advances.
|
|
179
|
+
*
|
|
180
|
+
* `fonts` is passed positionally by `Text.measure()`; DOM-based measurers ignore
|
|
181
|
+
* it (a method may safely declare fewer parameters than the interface).
|
|
182
|
+
*/
|
|
183
|
+
interface TextMeasurer {
|
|
184
|
+
measure: (paragraphs: Paragraph[], rootStyle: FullStyle, dom?: HTMLElement, fonts?: Fonts) => MeasureDomResult;
|
|
185
|
+
createDom?: (paragraphs: Paragraph[], rootStyle: FullStyle) => HTMLElement;
|
|
186
|
+
dispose?: () => void;
|
|
187
|
+
}
|
|
188
|
+
/** Built-in layout backends. `'font'` → `FontMeasurer`, `'dom'` → `DomMeasurer`. */
|
|
189
|
+
type MeasurerKind = 'dom' | 'font';
|
|
190
|
+
interface Options extends TextObject {
|
|
191
|
+
debug?: boolean;
|
|
192
|
+
measureDom?: HTMLElement;
|
|
193
|
+
fonts?: Fonts;
|
|
194
|
+
plugins?: Plugin[];
|
|
195
|
+
/**
|
|
196
|
+
* Layout backend: `'font'` (pure-JS) or `'dom'` (browser), or a custom
|
|
197
|
+
* `TextMeasurer`. Defaults to `'font'` when `fonts` are provided, else `'dom'`.
|
|
198
|
+
*/
|
|
199
|
+
measurer?: MeasurerKind | TextMeasurer;
|
|
200
|
+
}
|
|
201
|
+
|
|
169
202
|
interface RenderOptions {
|
|
170
203
|
view: HTMLCanvasElement;
|
|
171
204
|
pixelRatio?: number;
|
|
@@ -221,7 +254,7 @@ declare class Text$1 extends Reactivable {
|
|
|
221
254
|
glyphBox: BoundingBox;
|
|
222
255
|
pathBox: BoundingBox;
|
|
223
256
|
boundingBox: BoundingBox;
|
|
224
|
-
measurer:
|
|
257
|
+
measurer: TextMeasurer;
|
|
225
258
|
plugins: Map<string, Plugin>;
|
|
226
259
|
pathSets: Path2DSet[];
|
|
227
260
|
protected _paragraphs: Paragraph[];
|
|
@@ -304,5 +337,5 @@ declare class Canvas2DRenderer {
|
|
|
304
337
|
drawCharacter: (character: Character, effect?: NormalizedEffect) => void;
|
|
305
338
|
}
|
|
306
339
|
|
|
307
|
-
export { Canvas2DRenderer as C, Fragment as F, Paragraph as P, Text$1 as T, Character as a,
|
|
308
|
-
export type {
|
|
340
|
+
export { Canvas2DRenderer as C, DomMeasurer as D, Fragment as F, Paragraph as P, Text$1 as T, Character as a, textDefaultStyle as t };
|
|
341
|
+
export type { MeasureDomResult as M, Options as O, RenderOptions as R, DrawShapePathsOptions as b, MeasureResult as c, MeasuredCharacter as d, MeasuredCharacterRect as e, MeasuredFragment as f, MeasuredParagraph as g, MeasurerKind as h, Plugin as i, TextEvents as j, TextMeasurer as k };
|