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.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,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
- _updateGlyph(font) {
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
- _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
- }
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._updateGlyph(font);
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.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
+ }
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.path],
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.glyphBox)),
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
- this.characters.forEach((c) => c.update());
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
- this.characters.forEach((c) => c.getMinMax(min, max));
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
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "modern-text",
3
3
  "type": "module",
4
- "version": "0.2.4",
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",