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