modern-text 0.2.5 → 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.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { BoundingBox, Path2D, Point2D, parseSvgToDom, parseSvg, Matrix3 } from 'modern-path2d';
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,7 +170,6 @@ 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);
176
175
  // glyph
@@ -192,11 +191,14 @@ class Character {
192
191
  }
193
192
  return void 0;
194
193
  }
195
- _updateGlyph(font) {
194
+ updateGlyph(font = this._font()) {
195
+ if (!font) {
196
+ return this;
197
+ }
198
+ const { unitsPerEm, ascender, descender, os2, post } = font;
196
199
  const { content, computedStyle, boundingBox, isVertical } = this;
197
200
  const { left, top, height } = boundingBox;
198
201
  const { fontSize } = computedStyle;
199
- const { unitsPerEm, ascender, descender, os2, post } = font;
200
202
  const rate = unitsPerEm / fontSize;
201
203
  const glyphWidth = font.getAdvanceWidth(content, fontSize);
202
204
  const glyphHeight = (ascender + Math.abs(descender)) / rate;
@@ -217,84 +219,7 @@ class Character {
217
219
  this.centerPoint = this.glyphBox.getCenterPoint();
218
220
  return this;
219
221
  }
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
- }
297
- updatePath() {
222
+ updateCommands() {
298
223
  const font = this._font();
299
224
  if (!font) {
300
225
  return this;
@@ -309,7 +234,7 @@ class Character {
309
234
  baseline,
310
235
  glyphHeight,
311
236
  glyphWidth
312
- } = this._updateGlyph(font);
237
+ } = this.updateGlyph(font);
313
238
  const { os2, ascender, descender } = font;
314
239
  const usWinAscent = ascender;
315
240
  const usWinDescent = descender;
@@ -367,11 +292,120 @@ class Character {
367
292
  }
368
293
  commands.push(...this._decoration());
369
294
  this.commands = commands;
370
- this.path = new Path2D(commands);
371
295
  return this;
372
296
  }
373
297
  update() {
374
- this.updatePath();
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
+ }
375
409
  return this;
376
410
  }
377
411
  getMinMax(min = Point2D.MAX, max = Point2D.MIN) {
@@ -405,7 +439,7 @@ class Character {
405
439
  drawTo(ctx, config = {}) {
406
440
  drawPaths({
407
441
  ctx,
408
- paths: [this.path],
442
+ paths: [new Path2D(this.commands)],
409
443
  fontSize: this.computedStyle.fontSize,
410
444
  color: this.computedStyle.color,
411
445
  ...config
@@ -571,7 +605,7 @@ class Highlighter extends Feature {
571
605
  this.paths = groups.filter((characters2) => characters2.length).map((characters2) => {
572
606
  return {
573
607
  url: characters2[0].parent.highlight.url,
574
- box: BoundingBox.from(...characters2.map((c) => c.glyphBox)),
608
+ box: BoundingBox.from(...characters2.map((c) => c.boundingBox)),
575
609
  baseline: Math.max(...characters2.map((c) => c.baseline))
576
610
  };
577
611
  }).map((group2) => this._parseGroup(group2, fontSize)).flat();
@@ -1040,14 +1074,15 @@ class Text {
1040
1074
  const { paragraphs, boundingBox } = this.measure();
1041
1075
  this.paragraphs = paragraphs;
1042
1076
  this.boundingBox = boundingBox;
1043
- this.characters.forEach((c) => c.update());
1077
+ const characters = this.characters;
1078
+ characters.forEach((c) => c.update());
1044
1079
  if (this.deformation) {
1045
1080
  this._deformer.deform();
1046
1081
  }
1047
1082
  this._highlighter.highlight();
1048
1083
  const min = Point2D.MAX;
1049
1084
  const max = Point2D.MIN;
1050
- this.characters.forEach((c) => c.getMinMax(min, max));
1085
+ characters.forEach((c) => c.getMinMax(min, max));
1051
1086
  this.renderBoundingBox = new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
1052
1087
  return this;
1053
1088
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "modern-text",
3
3
  "type": "module",
4
- "version": "0.2.5",
4
+ "version": "0.2.6",
5
5
  "packageManager": "pnpm@9.9.0",
6
6
  "description": "Measure and render text in a way that describes the DOM.",
7
7
  "author": "wxm",