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.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
- _updateFont() {
188
+ _font() {
188
189
  const font = fonts.get(this.computedStyle.fontFamily)?.font;
189
190
  if (font instanceof Woff || font instanceof Ttf) {
190
- this.font = font.sfnt;
191
+ return font.sfnt;
191
192
  }
192
- return this;
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._updateFont().font;
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, width, height } = boundingBox;
242
- const { fontSize, fontStyle, textDecoration } = computedStyle;
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
- setItalic(
337
+ commands = this._italic(
262
338
  commands,
263
339
  isVertical ? {
264
340
  x: point.x,
265
341
  y: top - (glyphHeight - glyphWidth) / 2 + baseline
266
- } : null
342
+ } : void 0
267
343
  );
268
344
  }
269
- set90Rotation(commands, point);
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
- setItalic(
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
- } : null
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
- setItalic(
361
+ commands = this._italic(
286
362
  commands,
287
- isVertical ? { x: x + glyphHeight / 2, y } : null
363
+ isVertical ? { x: x + glyphHeight / 2, y } : void 0
288
364
  );
289
365
  }
290
366
  }
291
367
  }
292
- const lineWidth = 0.1 * fontSize;
293
- if (isVertical) {
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
- return this._measurer.measure(dom);
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.path.getMinMax(min, max));
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
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "modern-text",
3
3
  "type": "module",
4
- "version": "0.2.3",
4
+ "version": "0.2.5",
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",