modern-text 0.2.4 → 0.2.6
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 +125 -88
- package/dist/index.d.cts +15 -17
- package/dist/index.d.mts +15 -17
- package/dist/index.d.ts +15 -17
- package/dist/index.js +2 -2
- package/dist/index.mjs +126 -89
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BoundingBox,
|
|
1
|
+
import { BoundingBox, Point2D, Path2D, parseSvgToDom, parseSvg, Matrix3 } from 'modern-path2d';
|
|
2
2
|
import { fonts, Woff, Ttf } from 'modern-font';
|
|
3
3
|
export * from 'modern-font';
|
|
4
4
|
|
|
@@ -170,9 +170,10 @@ class Character {
|
|
|
170
170
|
this.index = index;
|
|
171
171
|
this.parent = parent;
|
|
172
172
|
__publicField$4(this, "boundingBox", new BoundingBox());
|
|
173
|
-
__publicField$4(this, "path", new Path2D());
|
|
174
173
|
__publicField$4(this, "textWidth", 0);
|
|
175
174
|
__publicField$4(this, "textHeight", 0);
|
|
175
|
+
// glyph
|
|
176
|
+
__publicField$4(this, "commands", []);
|
|
176
177
|
}
|
|
177
178
|
get computedStyle() {
|
|
178
179
|
return this.parent.computedStyle;
|
|
@@ -190,11 +191,14 @@ class Character {
|
|
|
190
191
|
}
|
|
191
192
|
return void 0;
|
|
192
193
|
}
|
|
193
|
-
|
|
194
|
+
updateGlyph(font = this._font()) {
|
|
195
|
+
if (!font) {
|
|
196
|
+
return this;
|
|
197
|
+
}
|
|
198
|
+
const { unitsPerEm, ascender, descender, os2, post } = font;
|
|
194
199
|
const { content, computedStyle, boundingBox, isVertical } = this;
|
|
195
200
|
const { left, top, height } = boundingBox;
|
|
196
201
|
const { fontSize } = computedStyle;
|
|
197
|
-
const { unitsPerEm, ascender, descender, os2, post } = font;
|
|
198
202
|
const rate = unitsPerEm / fontSize;
|
|
199
203
|
const glyphWidth = font.getAdvanceWidth(content, fontSize);
|
|
200
204
|
const glyphHeight = (ascender + Math.abs(descender)) / rate;
|
|
@@ -215,84 +219,7 @@ class Character {
|
|
|
215
219
|
this.centerPoint = this.glyphBox.getCenterPoint();
|
|
216
220
|
return this;
|
|
217
221
|
}
|
|
218
|
-
|
|
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
|
-
}
|
|
295
|
-
updatePath() {
|
|
222
|
+
updateCommands() {
|
|
296
223
|
const font = this._font();
|
|
297
224
|
if (!font) {
|
|
298
225
|
return this;
|
|
@@ -307,7 +234,7 @@ class Character {
|
|
|
307
234
|
baseline,
|
|
308
235
|
glyphHeight,
|
|
309
236
|
glyphWidth
|
|
310
|
-
} = this.
|
|
237
|
+
} = this.updateGlyph(font);
|
|
311
238
|
const { os2, ascender, descender } = font;
|
|
312
239
|
const usWinAscent = ascender;
|
|
313
240
|
const usWinDescent = descender;
|
|
@@ -365,11 +292,120 @@ class Character {
|
|
|
365
292
|
}
|
|
366
293
|
commands.push(...this._decoration());
|
|
367
294
|
this.commands = commands;
|
|
368
|
-
this.path = new Path2D(commands);
|
|
369
295
|
return this;
|
|
370
296
|
}
|
|
371
297
|
update() {
|
|
372
|
-
this.
|
|
298
|
+
this.updateCommands();
|
|
299
|
+
return this;
|
|
300
|
+
}
|
|
301
|
+
_decoration() {
|
|
302
|
+
const { isVertical, underlinePosition, yStrikeoutPosition } = this;
|
|
303
|
+
const { textDecoration, fontSize } = this.computedStyle;
|
|
304
|
+
const { left, top, width, height } = this.boundingBox;
|
|
305
|
+
const lineWidth = 0.1 * fontSize;
|
|
306
|
+
let start;
|
|
307
|
+
switch (textDecoration) {
|
|
308
|
+
case "underline":
|
|
309
|
+
if (isVertical) {
|
|
310
|
+
start = left;
|
|
311
|
+
} else {
|
|
312
|
+
start = top + underlinePosition;
|
|
313
|
+
}
|
|
314
|
+
break;
|
|
315
|
+
case "line-through":
|
|
316
|
+
if (isVertical) {
|
|
317
|
+
start = left + width / 2;
|
|
318
|
+
} else {
|
|
319
|
+
start = top + yStrikeoutPosition;
|
|
320
|
+
}
|
|
321
|
+
break;
|
|
322
|
+
case "none":
|
|
323
|
+
default:
|
|
324
|
+
return [];
|
|
325
|
+
}
|
|
326
|
+
if (isVertical) {
|
|
327
|
+
return [
|
|
328
|
+
{ type: "M", x: start, y: top },
|
|
329
|
+
{ type: "L", x: start, y: top + height },
|
|
330
|
+
{ type: "L", x: start + lineWidth, y: top + height },
|
|
331
|
+
{ type: "L", x: start + lineWidth, y: top },
|
|
332
|
+
{ type: "Z" }
|
|
333
|
+
];
|
|
334
|
+
} else {
|
|
335
|
+
return [
|
|
336
|
+
{ type: "M", x: left, y: start },
|
|
337
|
+
{ type: "L", x: left + width, y: start },
|
|
338
|
+
{ type: "L", x: left + width, y: start + lineWidth },
|
|
339
|
+
{ type: "L", x: left, y: start + lineWidth },
|
|
340
|
+
{ type: "Z" }
|
|
341
|
+
];
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
_italic(commands, startPoint) {
|
|
345
|
+
const { baseline, glyphWidth } = this;
|
|
346
|
+
const { left, top } = this.boundingBox;
|
|
347
|
+
const _startPoint = startPoint || {
|
|
348
|
+
y: top + baseline,
|
|
349
|
+
x: left + glyphWidth / 2
|
|
350
|
+
};
|
|
351
|
+
return this._transform(commands, (x, y) => {
|
|
352
|
+
const p = getSkewPoint({ x, y }, _startPoint, -0.24, 0);
|
|
353
|
+
return [p.x, p.y];
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
_rotation90(commands, point) {
|
|
357
|
+
return this._transform(commands, (x, y) => {
|
|
358
|
+
const p = getPointPosition({ x, y }, point, 90);
|
|
359
|
+
return [p.x, p.y];
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
_transform(commands, cb) {
|
|
363
|
+
return commands.map((rawCmd) => {
|
|
364
|
+
const cmd = { ...rawCmd };
|
|
365
|
+
switch (cmd.type) {
|
|
366
|
+
case "L":
|
|
367
|
+
case "M":
|
|
368
|
+
[cmd.x, cmd.y] = cb(cmd.x, cmd.y);
|
|
369
|
+
break;
|
|
370
|
+
case "Q":
|
|
371
|
+
[cmd.x1, cmd.y1] = cb(cmd.x1, cmd.y1);
|
|
372
|
+
[cmd.x, cmd.y] = cb(cmd.x, cmd.y);
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
return cmd;
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
forEachCommand(cb) {
|
|
379
|
+
const commands = this.commands;
|
|
380
|
+
const last = { x: 0, y: 0 };
|
|
381
|
+
const first = { x: 0, y: 0 };
|
|
382
|
+
let isFirst = true;
|
|
383
|
+
let doSetFirstPoint = false;
|
|
384
|
+
for (let i = 0, len = commands.length; i < len; i++) {
|
|
385
|
+
if (isFirst) {
|
|
386
|
+
doSetFirstPoint = true;
|
|
387
|
+
isFirst = false;
|
|
388
|
+
}
|
|
389
|
+
let command = commands[i];
|
|
390
|
+
command = cb(command, i, { last, first }) ?? command;
|
|
391
|
+
switch (command.type) {
|
|
392
|
+
case "M":
|
|
393
|
+
case "L":
|
|
394
|
+
case "Q":
|
|
395
|
+
last.x = command.x;
|
|
396
|
+
last.y = command.y;
|
|
397
|
+
if (doSetFirstPoint) {
|
|
398
|
+
first.x = last.x;
|
|
399
|
+
first.y = last.y;
|
|
400
|
+
}
|
|
401
|
+
break;
|
|
402
|
+
case "Z":
|
|
403
|
+
last.x = first.x;
|
|
404
|
+
last.y = first.y;
|
|
405
|
+
isFirst = true;
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
373
409
|
return this;
|
|
374
410
|
}
|
|
375
411
|
getMinMax(min = Point2D.MAX, max = Point2D.MIN) {
|
|
@@ -403,7 +439,7 @@ class Character {
|
|
|
403
439
|
drawTo(ctx, config = {}) {
|
|
404
440
|
drawPaths({
|
|
405
441
|
ctx,
|
|
406
|
-
paths: [this.
|
|
442
|
+
paths: [new Path2D(this.commands)],
|
|
407
443
|
fontSize: this.computedStyle.fontSize,
|
|
408
444
|
color: this.computedStyle.color,
|
|
409
445
|
...config
|
|
@@ -569,7 +605,7 @@ class Highlighter extends Feature {
|
|
|
569
605
|
this.paths = groups.filter((characters2) => characters2.length).map((characters2) => {
|
|
570
606
|
return {
|
|
571
607
|
url: characters2[0].parent.highlight.url,
|
|
572
|
-
box: BoundingBox.from(...characters2.map((c) => c.
|
|
608
|
+
box: BoundingBox.from(...characters2.map((c) => c.boundingBox)),
|
|
573
609
|
baseline: Math.max(...characters2.map((c) => c.baseline))
|
|
574
610
|
};
|
|
575
611
|
}).map((group2) => this._parseGroup(group2, fontSize)).flat();
|
|
@@ -1038,14 +1074,15 @@ class Text {
|
|
|
1038
1074
|
const { paragraphs, boundingBox } = this.measure();
|
|
1039
1075
|
this.paragraphs = paragraphs;
|
|
1040
1076
|
this.boundingBox = boundingBox;
|
|
1041
|
-
|
|
1077
|
+
const characters = this.characters;
|
|
1078
|
+
characters.forEach((c) => c.update());
|
|
1042
1079
|
if (this.deformation) {
|
|
1043
1080
|
this._deformer.deform();
|
|
1044
1081
|
}
|
|
1045
1082
|
this._highlighter.highlight();
|
|
1046
1083
|
const min = Point2D.MAX;
|
|
1047
1084
|
const max = Point2D.MIN;
|
|
1048
|
-
|
|
1085
|
+
characters.forEach((c) => c.getMinMax(min, max));
|
|
1049
1086
|
this.renderBoundingBox = new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
1050
1087
|
return this;
|
|
1051
1088
|
}
|