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 CHANGED
@@ -184,12 +184,12 @@ class Character {
184
184
  get fontSize() {
185
185
  return this.computedStyle.fontSize;
186
186
  }
187
- _updateFont() {
187
+ _font() {
188
188
  const font = modernFont.fonts.get(this.computedStyle.fontFamily)?.font;
189
189
  if (font instanceof modernFont.Woff || font instanceof modernFont.Ttf) {
190
- this.font = font.sfnt;
190
+ return font.sfnt;
191
191
  }
192
- return this;
192
+ return void 0;
193
193
  }
194
194
  _updateGlyph(font) {
195
195
  const { content, computedStyle, boundingBox, isVertical } = this;
@@ -216,8 +216,85 @@ class Character {
216
216
  this.centerPoint = this.glyphBox.getCenterPoint();
217
217
  return this;
218
218
  }
219
+ _decoration() {
220
+ const { isVertical, underlinePosition, yStrikeoutPosition } = this;
221
+ const { textDecoration, fontSize } = this.computedStyle;
222
+ const { left, top, width, height } = this.boundingBox;
223
+ const lineWidth = 0.1 * fontSize;
224
+ let start;
225
+ switch (textDecoration) {
226
+ case "underline":
227
+ if (isVertical) {
228
+ start = left;
229
+ } else {
230
+ start = top + underlinePosition;
231
+ }
232
+ break;
233
+ case "line-through":
234
+ if (isVertical) {
235
+ start = left + width / 2;
236
+ } else {
237
+ start = top + yStrikeoutPosition;
238
+ }
239
+ break;
240
+ case "none":
241
+ default:
242
+ return [];
243
+ }
244
+ if (isVertical) {
245
+ return [
246
+ { type: "M", x: start, y: top },
247
+ { type: "L", x: start, y: top + height },
248
+ { type: "L", x: start + lineWidth, y: top + height },
249
+ { type: "L", x: start + lineWidth, y: top },
250
+ { type: "Z" }
251
+ ];
252
+ } else {
253
+ return [
254
+ { type: "M", x: left, y: start },
255
+ { type: "L", x: left + width, y: start },
256
+ { type: "L", x: left + width, y: start + lineWidth },
257
+ { type: "L", x: left, y: start + lineWidth },
258
+ { type: "Z" }
259
+ ];
260
+ }
261
+ }
262
+ _transform(commands, cb) {
263
+ return commands.map((rawCmd) => {
264
+ const cmd = { ...rawCmd };
265
+ switch (cmd.type) {
266
+ case "L":
267
+ case "M":
268
+ [cmd.x, cmd.y] = cb(cmd.x, cmd.y);
269
+ break;
270
+ case "Q":
271
+ [cmd.x1, cmd.y1] = cb(cmd.x1, cmd.y1);
272
+ [cmd.x, cmd.y] = cb(cmd.x, cmd.y);
273
+ break;
274
+ }
275
+ return cmd;
276
+ });
277
+ }
278
+ _italic(commands, startPoint) {
279
+ const { baseline, glyphWidth } = this;
280
+ const { left, top } = this.boundingBox;
281
+ const _startPoint = startPoint || {
282
+ y: top + baseline,
283
+ x: left + glyphWidth / 2
284
+ };
285
+ return this._transform(commands, (x, y) => {
286
+ const p = getSkewPoint({ x, y }, _startPoint, -0.24, 0);
287
+ return [p.x, p.y];
288
+ });
289
+ }
290
+ _rotation90(commands, point) {
291
+ return this._transform(commands, (x, y) => {
292
+ const p = getPointPosition({ x, y }, point, 90);
293
+ return [p.x, p.y];
294
+ });
295
+ }
219
296
  updatePath() {
220
- const font = this._updateFont().font;
297
+ const font = this._font();
221
298
  if (!font) {
222
299
  return this;
223
300
  }
@@ -230,16 +307,14 @@ class Character {
230
307
  computedStyle,
231
308
  baseline,
232
309
  glyphHeight,
233
- glyphWidth,
234
- yStrikeoutPosition,
235
- underlinePosition
310
+ glyphWidth
236
311
  } = this._updateGlyph(font);
237
312
  const { os2, ascender, descender } = font;
238
313
  const usWinAscent = ascender;
239
314
  const usWinDescent = descender;
240
315
  const typoAscender = os2.sTypoAscender;
241
- const { left, top, width, height } = boundingBox;
242
- const { fontSize, fontStyle, textDecoration } = computedStyle;
316
+ const { left, top } = boundingBox;
317
+ const { fontSize, fontStyle } = computedStyle;
243
318
  let x = left;
244
319
  let y = top + baseline;
245
320
  let glyphIndex;
@@ -258,119 +333,74 @@ class Character {
258
333
  x: x + glyphWidth / 2
259
334
  };
260
335
  if (fontStyle === "italic") {
261
- setItalic(
336
+ commands = this._italic(
262
337
  commands,
263
338
  isVertical ? {
264
339
  x: point.x,
265
340
  y: top - (glyphHeight - glyphWidth) / 2 + baseline
266
- } : null
341
+ } : void 0
267
342
  );
268
343
  }
269
- set90Rotation(commands, point);
344
+ commands = this._rotation90(commands, point);
270
345
  } else {
271
346
  if (glyphIndex !== void 0) {
272
347
  commands = font.glyf.glyphs.get(glyphIndex).getPathCommands(x, y, fontSize);
273
348
  if (fontStyle === "italic") {
274
- setItalic(
349
+ commands = this._italic(
275
350
  commands,
276
351
  isVertical ? {
277
352
  x: x + glyphWidth / 2,
278
353
  y: top + typoAscender / (usWinAscent + Math.abs(usWinDescent)) * glyphHeight
279
- } : null
354
+ } : void 0
280
355
  );
281
356
  }
282
357
  } else {
283
358
  commands = font.getPathCommands(content, x, y, fontSize) ?? [];
284
359
  if (fontStyle === "italic") {
285
- setItalic(
360
+ commands = this._italic(
286
361
  commands,
287
- isVertical ? { x: x + glyphHeight / 2, y } : null
362
+ isVertical ? { x: x + glyphHeight / 2, y } : void 0
288
363
  );
289
364
  }
290
365
  }
291
366
  }
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
- }
367
+ commands.push(...this._decoration());
368
+ this.commands = commands;
326
369
  this.path = new modernPath2d.Path2D(commands);
327
370
  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
371
  }
370
372
  update() {
371
373
  this.updatePath();
372
374
  return this;
373
375
  }
376
+ getMinMax(min = modernPath2d.Point2D.MAX, max = modernPath2d.Point2D.MIN) {
377
+ let last = { x: 0, y: 0 };
378
+ this.commands.forEach((cmd) => {
379
+ switch (cmd.type) {
380
+ case "L":
381
+ case "M":
382
+ min.x = Math.min(min.x, cmd.x);
383
+ min.y = Math.min(min.y, cmd.y);
384
+ max.x = Math.max(max.x, cmd.x);
385
+ max.y = Math.max(max.y, cmd.y);
386
+ last = { x: cmd.x, y: cmd.y };
387
+ break;
388
+ case "Q": {
389
+ const x1 = 0.5 * (last.x + cmd.x1);
390
+ const y1 = 0.5 * (last.y + cmd.y1);
391
+ const x2 = 0.5 * (last.x + cmd.x);
392
+ const y2 = 0.5 * (last.y + cmd.y);
393
+ min.x = Math.min(min.x, last.x, cmd.x, x1, x2);
394
+ min.y = Math.min(min.y, last.y, cmd.y, y1, y2);
395
+ max.x = Math.max(max.x, last.x, cmd.x, x1, x2);
396
+ max.y = Math.max(max.y, last.y, cmd.y, y1, y2);
397
+ last = { x: cmd.x, y: cmd.y };
398
+ break;
399
+ }
400
+ }
401
+ });
402
+ return { min, max };
403
+ }
374
404
  drawTo(ctx, config = {}) {
375
405
  drawPaths({
376
406
  ctx,
@@ -455,6 +485,7 @@ class Feature {
455
485
 
456
486
  class Deformer extends Feature {
457
487
  deform() {
488
+ this._text.deformation?.();
458
489
  }
459
490
  }
460
491
 
@@ -994,8 +1025,11 @@ class Text {
994
1025
  }
995
1026
  measure(dom = this.measureDom) {
996
1027
  this.computedStyle = { ...defaultTextStyles, ...this.style };
1028
+ const old = this.paragraphs;
997
1029
  this.paragraphs = this._parser.parse();
998
- return this._measurer.measure(dom);
1030
+ const result = this._measurer.measure(dom);
1031
+ this.paragraphs = old;
1032
+ return result;
999
1033
  }
1000
1034
  requestUpdate() {
1001
1035
  this.needsUpdate = true;
@@ -1012,7 +1046,7 @@ class Text {
1012
1046
  this._highlighter.highlight();
1013
1047
  const min = modernPath2d.Point2D.MAX;
1014
1048
  const max = modernPath2d.Point2D.MIN;
1015
- this.characters.forEach((c) => c.path.getMinMax(min, max));
1049
+ this.characters.forEach((c) => c.getMinMax(min, max));
1016
1050
  this.renderBoundingBox = new modernPath2d.BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
1017
1051
  return this;
1018
1052
  }
@@ -1071,9 +1105,6 @@ exports.getScalePoint = getScalePoint;
1071
1105
  exports.getSkewPoint = getSkewPoint;
1072
1106
  exports.parseColor = parseColor;
1073
1107
  exports.uploadColor = uploadColor;
1074
- Object.keys(modernPath2d).forEach(function (k) {
1075
- if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = modernPath2d[k];
1076
- });
1077
1108
  Object.keys(modernFont).forEach(function (k) {
1078
1109
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = modernFont[k];
1079
1110
  });
package/dist/index.d.cts CHANGED
@@ -1,6 +1,5 @@
1
- import { BoundingBox, Path2D } from 'modern-path2d';
2
- export * from 'modern-path2d';
3
- import { Sfnt } from 'modern-font';
1
+ import { BoundingBox, Path2D, Point2D } from 'modern-path2d';
2
+ import { GlyphPathCommand, Sfnt } from 'modern-font';
4
3
  export * from 'modern-font';
5
4
 
6
5
  type WritingMode = 'horizontal-tb' | 'vertical-lr' | 'vertical-rl';
@@ -53,10 +52,7 @@ type TextEffect = Partial<TextDrawStyle & {
53
52
  offsetX: number;
54
53
  offsetY: number;
55
54
  }>;
56
- interface TextDeformation {
57
- type: string;
58
- intensities?: number[];
59
- }
55
+ type TextDeformation = () => void;
60
56
  interface FragmentHighlight {
61
57
  url: string;
62
58
  }
@@ -104,7 +100,7 @@ declare class Character {
104
100
  path: Path2D<any>;
105
101
  textWidth: number;
106
102
  textHeight: number;
107
- font: Sfnt;
103
+ commands: GlyphPathCommand[];
108
104
  glyphHeight: number;
109
105
  glyphWidth: number;
110
106
  underlinePosition: number;
@@ -122,10 +118,24 @@ declare class Character {
122
118
  get isVertical(): boolean;
123
119
  get fontSize(): number;
124
120
  constructor(content: string, index: number, parent: Fragment);
125
- protected _updateFont(): this;
121
+ protected _font(): Sfnt | undefined;
126
122
  protected _updateGlyph(font: Sfnt): this;
123
+ protected _decoration(): GlyphPathCommand[];
124
+ protected _transform(commands: GlyphPathCommand[], cb: (x: number, y: number) => number[]): GlyphPathCommand[];
125
+ protected _italic(commands: GlyphPathCommand[], startPoint?: {
126
+ x: number;
127
+ y: number;
128
+ }): GlyphPathCommand[];
129
+ protected _rotation90(commands: GlyphPathCommand[], point: {
130
+ x: number;
131
+ y: number;
132
+ }): GlyphPathCommand[];
127
133
  updatePath(): this;
128
134
  update(): this;
135
+ getMinMax(min?: Point2D, max?: Point2D): {
136
+ min: Point2D;
137
+ max: Point2D;
138
+ };
129
139
  drawTo(ctx: CanvasRenderingContext2D, config?: Partial<TextEffect>): void;
130
140
  }
131
141
 
package/dist/index.d.mts CHANGED
@@ -1,6 +1,5 @@
1
- import { BoundingBox, Path2D } from 'modern-path2d';
2
- export * from 'modern-path2d';
3
- import { Sfnt } from 'modern-font';
1
+ import { BoundingBox, Path2D, Point2D } from 'modern-path2d';
2
+ import { GlyphPathCommand, Sfnt } from 'modern-font';
4
3
  export * from 'modern-font';
5
4
 
6
5
  type WritingMode = 'horizontal-tb' | 'vertical-lr' | 'vertical-rl';
@@ -53,10 +52,7 @@ type TextEffect = Partial<TextDrawStyle & {
53
52
  offsetX: number;
54
53
  offsetY: number;
55
54
  }>;
56
- interface TextDeformation {
57
- type: string;
58
- intensities?: number[];
59
- }
55
+ type TextDeformation = () => void;
60
56
  interface FragmentHighlight {
61
57
  url: string;
62
58
  }
@@ -104,7 +100,7 @@ declare class Character {
104
100
  path: Path2D<any>;
105
101
  textWidth: number;
106
102
  textHeight: number;
107
- font: Sfnt;
103
+ commands: GlyphPathCommand[];
108
104
  glyphHeight: number;
109
105
  glyphWidth: number;
110
106
  underlinePosition: number;
@@ -122,10 +118,24 @@ declare class Character {
122
118
  get isVertical(): boolean;
123
119
  get fontSize(): number;
124
120
  constructor(content: string, index: number, parent: Fragment);
125
- protected _updateFont(): this;
121
+ protected _font(): Sfnt | undefined;
126
122
  protected _updateGlyph(font: Sfnt): this;
123
+ protected _decoration(): GlyphPathCommand[];
124
+ protected _transform(commands: GlyphPathCommand[], cb: (x: number, y: number) => number[]): GlyphPathCommand[];
125
+ protected _italic(commands: GlyphPathCommand[], startPoint?: {
126
+ x: number;
127
+ y: number;
128
+ }): GlyphPathCommand[];
129
+ protected _rotation90(commands: GlyphPathCommand[], point: {
130
+ x: number;
131
+ y: number;
132
+ }): GlyphPathCommand[];
127
133
  updatePath(): this;
128
134
  update(): this;
135
+ getMinMax(min?: Point2D, max?: Point2D): {
136
+ min: Point2D;
137
+ max: Point2D;
138
+ };
129
139
  drawTo(ctx: CanvasRenderingContext2D, config?: Partial<TextEffect>): void;
130
140
  }
131
141
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- import { BoundingBox, Path2D } from 'modern-path2d';
2
- export * from 'modern-path2d';
3
- import { Sfnt } from 'modern-font';
1
+ import { BoundingBox, Path2D, Point2D } from 'modern-path2d';
2
+ import { GlyphPathCommand, Sfnt } from 'modern-font';
4
3
  export * from 'modern-font';
5
4
 
6
5
  type WritingMode = 'horizontal-tb' | 'vertical-lr' | 'vertical-rl';
@@ -53,10 +52,7 @@ type TextEffect = Partial<TextDrawStyle & {
53
52
  offsetX: number;
54
53
  offsetY: number;
55
54
  }>;
56
- interface TextDeformation {
57
- type: string;
58
- intensities?: number[];
59
- }
55
+ type TextDeformation = () => void;
60
56
  interface FragmentHighlight {
61
57
  url: string;
62
58
  }
@@ -104,7 +100,7 @@ declare class Character {
104
100
  path: Path2D<any>;
105
101
  textWidth: number;
106
102
  textHeight: number;
107
- font: Sfnt;
103
+ commands: GlyphPathCommand[];
108
104
  glyphHeight: number;
109
105
  glyphWidth: number;
110
106
  underlinePosition: number;
@@ -122,10 +118,24 @@ declare class Character {
122
118
  get isVertical(): boolean;
123
119
  get fontSize(): number;
124
120
  constructor(content: string, index: number, parent: Fragment);
125
- protected _updateFont(): this;
121
+ protected _font(): Sfnt | undefined;
126
122
  protected _updateGlyph(font: Sfnt): this;
123
+ protected _decoration(): GlyphPathCommand[];
124
+ protected _transform(commands: GlyphPathCommand[], cb: (x: number, y: number) => number[]): GlyphPathCommand[];
125
+ protected _italic(commands: GlyphPathCommand[], startPoint?: {
126
+ x: number;
127
+ y: number;
128
+ }): GlyphPathCommand[];
129
+ protected _rotation90(commands: GlyphPathCommand[], point: {
130
+ x: number;
131
+ y: number;
132
+ }): GlyphPathCommand[];
127
133
  updatePath(): this;
128
134
  update(): this;
135
+ getMinMax(min?: Point2D, max?: Point2D): {
136
+ min: Point2D;
137
+ max: Point2D;
138
+ };
129
139
  drawTo(ctx: CanvasRenderingContext2D, config?: Partial<TextEffect>): void;
130
140
  }
131
141