modern-text 0.2.3 → 0.2.5
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.cjs +129 -96
- package/dist/index.d.cts +19 -9
- package/dist/index.d.mts +19 -9
- package/dist/index.d.ts +19 -9
- package/dist/index.js +2 -2
- package/dist/index.mjs +129 -94
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { BoundingBox, Path2D, Point2D, parseSvgToDom, parseSvg, Matrix3 } from 'modern-path2d';
|
|
2
|
-
export * from 'modern-path2d';
|
|
3
2
|
import { fonts, Woff, Ttf } from 'modern-font';
|
|
4
3
|
export * from 'modern-font';
|
|
5
4
|
|
|
@@ -174,6 +173,8 @@ class Character {
|
|
|
174
173
|
__publicField$4(this, "path", new Path2D());
|
|
175
174
|
__publicField$4(this, "textWidth", 0);
|
|
176
175
|
__publicField$4(this, "textHeight", 0);
|
|
176
|
+
// glyph
|
|
177
|
+
__publicField$4(this, "commands", []);
|
|
177
178
|
}
|
|
178
179
|
get computedStyle() {
|
|
179
180
|
return this.parent.computedStyle;
|
|
@@ -184,12 +185,12 @@ class Character {
|
|
|
184
185
|
get fontSize() {
|
|
185
186
|
return this.computedStyle.fontSize;
|
|
186
187
|
}
|
|
187
|
-
|
|
188
|
+
_font() {
|
|
188
189
|
const font = fonts.get(this.computedStyle.fontFamily)?.font;
|
|
189
190
|
if (font instanceof Woff || font instanceof Ttf) {
|
|
190
|
-
|
|
191
|
+
return font.sfnt;
|
|
191
192
|
}
|
|
192
|
-
return
|
|
193
|
+
return void 0;
|
|
193
194
|
}
|
|
194
195
|
_updateGlyph(font) {
|
|
195
196
|
const { content, computedStyle, boundingBox, isVertical } = this;
|
|
@@ -216,8 +217,85 @@ class Character {
|
|
|
216
217
|
this.centerPoint = this.glyphBox.getCenterPoint();
|
|
217
218
|
return this;
|
|
218
219
|
}
|
|
220
|
+
_decoration() {
|
|
221
|
+
const { isVertical, underlinePosition, yStrikeoutPosition } = this;
|
|
222
|
+
const { textDecoration, fontSize } = this.computedStyle;
|
|
223
|
+
const { left, top, width, height } = this.boundingBox;
|
|
224
|
+
const lineWidth = 0.1 * fontSize;
|
|
225
|
+
let start;
|
|
226
|
+
switch (textDecoration) {
|
|
227
|
+
case "underline":
|
|
228
|
+
if (isVertical) {
|
|
229
|
+
start = left;
|
|
230
|
+
} else {
|
|
231
|
+
start = top + underlinePosition;
|
|
232
|
+
}
|
|
233
|
+
break;
|
|
234
|
+
case "line-through":
|
|
235
|
+
if (isVertical) {
|
|
236
|
+
start = left + width / 2;
|
|
237
|
+
} else {
|
|
238
|
+
start = top + yStrikeoutPosition;
|
|
239
|
+
}
|
|
240
|
+
break;
|
|
241
|
+
case "none":
|
|
242
|
+
default:
|
|
243
|
+
return [];
|
|
244
|
+
}
|
|
245
|
+
if (isVertical) {
|
|
246
|
+
return [
|
|
247
|
+
{ type: "M", x: start, y: top },
|
|
248
|
+
{ type: "L", x: start, y: top + height },
|
|
249
|
+
{ type: "L", x: start + lineWidth, y: top + height },
|
|
250
|
+
{ type: "L", x: start + lineWidth, y: top },
|
|
251
|
+
{ type: "Z" }
|
|
252
|
+
];
|
|
253
|
+
} else {
|
|
254
|
+
return [
|
|
255
|
+
{ type: "M", x: left, y: start },
|
|
256
|
+
{ type: "L", x: left + width, y: start },
|
|
257
|
+
{ type: "L", x: left + width, y: start + lineWidth },
|
|
258
|
+
{ type: "L", x: left, y: start + lineWidth },
|
|
259
|
+
{ type: "Z" }
|
|
260
|
+
];
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
_transform(commands, cb) {
|
|
264
|
+
return commands.map((rawCmd) => {
|
|
265
|
+
const cmd = { ...rawCmd };
|
|
266
|
+
switch (cmd.type) {
|
|
267
|
+
case "L":
|
|
268
|
+
case "M":
|
|
269
|
+
[cmd.x, cmd.y] = cb(cmd.x, cmd.y);
|
|
270
|
+
break;
|
|
271
|
+
case "Q":
|
|
272
|
+
[cmd.x1, cmd.y1] = cb(cmd.x1, cmd.y1);
|
|
273
|
+
[cmd.x, cmd.y] = cb(cmd.x, cmd.y);
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
return cmd;
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
_italic(commands, startPoint) {
|
|
280
|
+
const { baseline, glyphWidth } = this;
|
|
281
|
+
const { left, top } = this.boundingBox;
|
|
282
|
+
const _startPoint = startPoint || {
|
|
283
|
+
y: top + baseline,
|
|
284
|
+
x: left + glyphWidth / 2
|
|
285
|
+
};
|
|
286
|
+
return this._transform(commands, (x, y) => {
|
|
287
|
+
const p = getSkewPoint({ x, y }, _startPoint, -0.24, 0);
|
|
288
|
+
return [p.x, p.y];
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
_rotation90(commands, point) {
|
|
292
|
+
return this._transform(commands, (x, y) => {
|
|
293
|
+
const p = getPointPosition({ x, y }, point, 90);
|
|
294
|
+
return [p.x, p.y];
|
|
295
|
+
});
|
|
296
|
+
}
|
|
219
297
|
updatePath() {
|
|
220
|
-
const font = this.
|
|
298
|
+
const font = this._font();
|
|
221
299
|
if (!font) {
|
|
222
300
|
return this;
|
|
223
301
|
}
|
|
@@ -230,16 +308,14 @@ class Character {
|
|
|
230
308
|
computedStyle,
|
|
231
309
|
baseline,
|
|
232
310
|
glyphHeight,
|
|
233
|
-
glyphWidth
|
|
234
|
-
yStrikeoutPosition,
|
|
235
|
-
underlinePosition
|
|
311
|
+
glyphWidth
|
|
236
312
|
} = this._updateGlyph(font);
|
|
237
313
|
const { os2, ascender, descender } = font;
|
|
238
314
|
const usWinAscent = ascender;
|
|
239
315
|
const usWinDescent = descender;
|
|
240
316
|
const typoAscender = os2.sTypoAscender;
|
|
241
|
-
const { left, top
|
|
242
|
-
const { fontSize, fontStyle
|
|
317
|
+
const { left, top } = boundingBox;
|
|
318
|
+
const { fontSize, fontStyle } = computedStyle;
|
|
243
319
|
let x = left;
|
|
244
320
|
let y = top + baseline;
|
|
245
321
|
let glyphIndex;
|
|
@@ -258,119 +334,74 @@ class Character {
|
|
|
258
334
|
x: x + glyphWidth / 2
|
|
259
335
|
};
|
|
260
336
|
if (fontStyle === "italic") {
|
|
261
|
-
|
|
337
|
+
commands = this._italic(
|
|
262
338
|
commands,
|
|
263
339
|
isVertical ? {
|
|
264
340
|
x: point.x,
|
|
265
341
|
y: top - (glyphHeight - glyphWidth) / 2 + baseline
|
|
266
|
-
} :
|
|
342
|
+
} : void 0
|
|
267
343
|
);
|
|
268
344
|
}
|
|
269
|
-
|
|
345
|
+
commands = this._rotation90(commands, point);
|
|
270
346
|
} else {
|
|
271
347
|
if (glyphIndex !== void 0) {
|
|
272
348
|
commands = font.glyf.glyphs.get(glyphIndex).getPathCommands(x, y, fontSize);
|
|
273
349
|
if (fontStyle === "italic") {
|
|
274
|
-
|
|
350
|
+
commands = this._italic(
|
|
275
351
|
commands,
|
|
276
352
|
isVertical ? {
|
|
277
353
|
x: x + glyphWidth / 2,
|
|
278
354
|
y: top + typoAscender / (usWinAscent + Math.abs(usWinDescent)) * glyphHeight
|
|
279
|
-
} :
|
|
355
|
+
} : void 0
|
|
280
356
|
);
|
|
281
357
|
}
|
|
282
358
|
} else {
|
|
283
359
|
commands = font.getPathCommands(content, x, y, fontSize) ?? [];
|
|
284
360
|
if (fontStyle === "italic") {
|
|
285
|
-
|
|
361
|
+
commands = this._italic(
|
|
286
362
|
commands,
|
|
287
|
-
isVertical ? { x: x + glyphHeight / 2, y } :
|
|
363
|
+
isVertical ? { x: x + glyphHeight / 2, y } : void 0
|
|
288
364
|
);
|
|
289
365
|
}
|
|
290
366
|
}
|
|
291
367
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const create = (start, len) => [
|
|
295
|
-
{ type: "M", x: start, y: top },
|
|
296
|
-
{ type: "L", x: start, y: top + height },
|
|
297
|
-
{ type: "L", x: start + len, y: top + height },
|
|
298
|
-
{ type: "L", x: start + len, y: top },
|
|
299
|
-
{ type: "Z" }
|
|
300
|
-
];
|
|
301
|
-
switch (textDecoration) {
|
|
302
|
-
case "underline":
|
|
303
|
-
commands.push(...create(left, lineWidth));
|
|
304
|
-
break;
|
|
305
|
-
case "line-through":
|
|
306
|
-
commands.push(...create(left + width / 2, lineWidth));
|
|
307
|
-
break;
|
|
308
|
-
}
|
|
309
|
-
} else {
|
|
310
|
-
const create = (start, len) => [
|
|
311
|
-
{ type: "M", x: left, y: start },
|
|
312
|
-
{ type: "L", x: left + width, y: start },
|
|
313
|
-
{ type: "L", x: left + width, y: start + len },
|
|
314
|
-
{ type: "L", x: left, y: start + len },
|
|
315
|
-
{ type: "Z" }
|
|
316
|
-
];
|
|
317
|
-
switch (textDecoration) {
|
|
318
|
-
case "underline":
|
|
319
|
-
commands.push(...create(top + underlinePosition, lineWidth));
|
|
320
|
-
break;
|
|
321
|
-
case "line-through":
|
|
322
|
-
commands.push(...create(top + yStrikeoutPosition, lineWidth));
|
|
323
|
-
break;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
368
|
+
commands.push(...this._decoration());
|
|
369
|
+
this.commands = commands;
|
|
326
370
|
this.path = new Path2D(commands);
|
|
327
371
|
return this;
|
|
328
|
-
function setItalic(commands2, startPoint) {
|
|
329
|
-
const _startPoint = startPoint || {
|
|
330
|
-
y: top + baseline,
|
|
331
|
-
x: left + glyphWidth / 2
|
|
332
|
-
};
|
|
333
|
-
commands2.forEach((command) => {
|
|
334
|
-
["", "1", "2"].forEach((arg) => {
|
|
335
|
-
if (command[`x${arg}`]) {
|
|
336
|
-
const pos = getSkewPoint(
|
|
337
|
-
{
|
|
338
|
-
x: command[`x${arg}`],
|
|
339
|
-
y: command[`y${arg}`]
|
|
340
|
-
},
|
|
341
|
-
_startPoint,
|
|
342
|
-
-0.24,
|
|
343
|
-
0
|
|
344
|
-
);
|
|
345
|
-
command[`x${arg}`] = pos.x;
|
|
346
|
-
command[`y${arg}`] = pos.y;
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
function set90Rotation(commands2, point) {
|
|
352
|
-
commands2.forEach((command) => {
|
|
353
|
-
["", "1", "2"].forEach((arg) => {
|
|
354
|
-
if (command[`x${arg}`]) {
|
|
355
|
-
const pos = getPointPosition(
|
|
356
|
-
{
|
|
357
|
-
x: command[`x${arg}`],
|
|
358
|
-
y: command[`y${arg}`]
|
|
359
|
-
},
|
|
360
|
-
point,
|
|
361
|
-
90
|
|
362
|
-
);
|
|
363
|
-
command[`x${arg}`] = pos.x;
|
|
364
|
-
command[`y${arg}`] = pos.y;
|
|
365
|
-
}
|
|
366
|
-
});
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
372
|
}
|
|
370
373
|
update() {
|
|
371
374
|
this.updatePath();
|
|
372
375
|
return this;
|
|
373
376
|
}
|
|
377
|
+
getMinMax(min = Point2D.MAX, max = Point2D.MIN) {
|
|
378
|
+
let last = { x: 0, y: 0 };
|
|
379
|
+
this.commands.forEach((cmd) => {
|
|
380
|
+
switch (cmd.type) {
|
|
381
|
+
case "L":
|
|
382
|
+
case "M":
|
|
383
|
+
min.x = Math.min(min.x, cmd.x);
|
|
384
|
+
min.y = Math.min(min.y, cmd.y);
|
|
385
|
+
max.x = Math.max(max.x, cmd.x);
|
|
386
|
+
max.y = Math.max(max.y, cmd.y);
|
|
387
|
+
last = { x: cmd.x, y: cmd.y };
|
|
388
|
+
break;
|
|
389
|
+
case "Q": {
|
|
390
|
+
const x1 = 0.5 * (last.x + cmd.x1);
|
|
391
|
+
const y1 = 0.5 * (last.y + cmd.y1);
|
|
392
|
+
const x2 = 0.5 * (last.x + cmd.x);
|
|
393
|
+
const y2 = 0.5 * (last.y + cmd.y);
|
|
394
|
+
min.x = Math.min(min.x, last.x, cmd.x, x1, x2);
|
|
395
|
+
min.y = Math.min(min.y, last.y, cmd.y, y1, y2);
|
|
396
|
+
max.x = Math.max(max.x, last.x, cmd.x, x1, x2);
|
|
397
|
+
max.y = Math.max(max.y, last.y, cmd.y, y1, y2);
|
|
398
|
+
last = { x: cmd.x, y: cmd.y };
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
return { min, max };
|
|
404
|
+
}
|
|
374
405
|
drawTo(ctx, config = {}) {
|
|
375
406
|
drawPaths({
|
|
376
407
|
ctx,
|
|
@@ -455,6 +486,7 @@ class Feature {
|
|
|
455
486
|
|
|
456
487
|
class Deformer extends Feature {
|
|
457
488
|
deform() {
|
|
489
|
+
this._text.deformation?.();
|
|
458
490
|
}
|
|
459
491
|
}
|
|
460
492
|
|
|
@@ -994,8 +1026,11 @@ class Text {
|
|
|
994
1026
|
}
|
|
995
1027
|
measure(dom = this.measureDom) {
|
|
996
1028
|
this.computedStyle = { ...defaultTextStyles, ...this.style };
|
|
1029
|
+
const old = this.paragraphs;
|
|
997
1030
|
this.paragraphs = this._parser.parse();
|
|
998
|
-
|
|
1031
|
+
const result = this._measurer.measure(dom);
|
|
1032
|
+
this.paragraphs = old;
|
|
1033
|
+
return result;
|
|
999
1034
|
}
|
|
1000
1035
|
requestUpdate() {
|
|
1001
1036
|
this.needsUpdate = true;
|
|
@@ -1012,7 +1047,7 @@ class Text {
|
|
|
1012
1047
|
this._highlighter.highlight();
|
|
1013
1048
|
const min = Point2D.MAX;
|
|
1014
1049
|
const max = Point2D.MIN;
|
|
1015
|
-
this.characters.forEach((c) => c.
|
|
1050
|
+
this.characters.forEach((c) => c.getMinMax(min, max));
|
|
1016
1051
|
this.renderBoundingBox = new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
1017
1052
|
return this;
|
|
1018
1053
|
}
|