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.cjs CHANGED
@@ -174,6 +174,8 @@ class Character {
174
174
  __publicField$4(this, "path", new modernPath2d.Path2D());
175
175
  __publicField$4(this, "textWidth", 0);
176
176
  __publicField$4(this, "textHeight", 0);
177
+ // glyph
178
+ __publicField$4(this, "commands", []);
177
179
  }
178
180
  get computedStyle() {
179
181
  return this.parent.computedStyle;
@@ -184,12 +186,12 @@ class Character {
184
186
  get fontSize() {
185
187
  return this.computedStyle.fontSize;
186
188
  }
187
- _updateFont() {
189
+ _font() {
188
190
  const font = modernFont.fonts.get(this.computedStyle.fontFamily)?.font;
189
191
  if (font instanceof modernFont.Woff || font instanceof modernFont.Ttf) {
190
- this.font = font.sfnt;
192
+ return font.sfnt;
191
193
  }
192
- return this;
194
+ return void 0;
193
195
  }
194
196
  _updateGlyph(font) {
195
197
  const { content, computedStyle, boundingBox, isVertical } = this;
@@ -216,8 +218,85 @@ class Character {
216
218
  this.centerPoint = this.glyphBox.getCenterPoint();
217
219
  return this;
218
220
  }
221
+ _decoration() {
222
+ const { isVertical, underlinePosition, yStrikeoutPosition } = this;
223
+ const { textDecoration, fontSize } = this.computedStyle;
224
+ const { left, top, width, height } = this.boundingBox;
225
+ const lineWidth = 0.1 * fontSize;
226
+ let start;
227
+ switch (textDecoration) {
228
+ case "underline":
229
+ if (isVertical) {
230
+ start = left;
231
+ } else {
232
+ start = top + underlinePosition;
233
+ }
234
+ break;
235
+ case "line-through":
236
+ if (isVertical) {
237
+ start = left + width / 2;
238
+ } else {
239
+ start = top + yStrikeoutPosition;
240
+ }
241
+ break;
242
+ case "none":
243
+ default:
244
+ return [];
245
+ }
246
+ if (isVertical) {
247
+ return [
248
+ { type: "M", x: start, y: top },
249
+ { type: "L", x: start, y: top + height },
250
+ { type: "L", x: start + lineWidth, y: top + height },
251
+ { type: "L", x: start + lineWidth, y: top },
252
+ { type: "Z" }
253
+ ];
254
+ } else {
255
+ return [
256
+ { type: "M", x: left, y: start },
257
+ { type: "L", x: left + width, y: start },
258
+ { type: "L", x: left + width, y: start + lineWidth },
259
+ { type: "L", x: left, y: start + lineWidth },
260
+ { type: "Z" }
261
+ ];
262
+ }
263
+ }
264
+ _transform(commands, cb) {
265
+ return commands.map((rawCmd) => {
266
+ const cmd = { ...rawCmd };
267
+ switch (cmd.type) {
268
+ case "L":
269
+ case "M":
270
+ [cmd.x, cmd.y] = cb(cmd.x, cmd.y);
271
+ break;
272
+ case "Q":
273
+ [cmd.x1, cmd.y1] = cb(cmd.x1, cmd.y1);
274
+ [cmd.x, cmd.y] = cb(cmd.x, cmd.y);
275
+ break;
276
+ }
277
+ return cmd;
278
+ });
279
+ }
280
+ _italic(commands, startPoint) {
281
+ const { baseline, glyphWidth } = this;
282
+ const { left, top } = this.boundingBox;
283
+ const _startPoint = startPoint || {
284
+ y: top + baseline,
285
+ x: left + glyphWidth / 2
286
+ };
287
+ return this._transform(commands, (x, y) => {
288
+ const p = getSkewPoint({ x, y }, _startPoint, -0.24, 0);
289
+ return [p.x, p.y];
290
+ });
291
+ }
292
+ _rotation90(commands, point) {
293
+ return this._transform(commands, (x, y) => {
294
+ const p = getPointPosition({ x, y }, point, 90);
295
+ return [p.x, p.y];
296
+ });
297
+ }
219
298
  updatePath() {
220
- const font = this._updateFont().font;
299
+ const font = this._font();
221
300
  if (!font) {
222
301
  return this;
223
302
  }
@@ -230,16 +309,14 @@ class Character {
230
309
  computedStyle,
231
310
  baseline,
232
311
  glyphHeight,
233
- glyphWidth,
234
- yStrikeoutPosition,
235
- underlinePosition
312
+ glyphWidth
236
313
  } = this._updateGlyph(font);
237
314
  const { os2, ascender, descender } = font;
238
315
  const usWinAscent = ascender;
239
316
  const usWinDescent = descender;
240
317
  const typoAscender = os2.sTypoAscender;
241
- const { left, top, width, height } = boundingBox;
242
- const { fontSize, fontStyle, textDecoration } = computedStyle;
318
+ const { left, top } = boundingBox;
319
+ const { fontSize, fontStyle } = computedStyle;
243
320
  let x = left;
244
321
  let y = top + baseline;
245
322
  let glyphIndex;
@@ -258,119 +335,74 @@ class Character {
258
335
  x: x + glyphWidth / 2
259
336
  };
260
337
  if (fontStyle === "italic") {
261
- setItalic(
338
+ commands = this._italic(
262
339
  commands,
263
340
  isVertical ? {
264
341
  x: point.x,
265
342
  y: top - (glyphHeight - glyphWidth) / 2 + baseline
266
- } : null
343
+ } : void 0
267
344
  );
268
345
  }
269
- set90Rotation(commands, point);
346
+ commands = this._rotation90(commands, point);
270
347
  } else {
271
348
  if (glyphIndex !== void 0) {
272
349
  commands = font.glyf.glyphs.get(glyphIndex).getPathCommands(x, y, fontSize);
273
350
  if (fontStyle === "italic") {
274
- setItalic(
351
+ commands = this._italic(
275
352
  commands,
276
353
  isVertical ? {
277
354
  x: x + glyphWidth / 2,
278
355
  y: top + typoAscender / (usWinAscent + Math.abs(usWinDescent)) * glyphHeight
279
- } : null
356
+ } : void 0
280
357
  );
281
358
  }
282
359
  } else {
283
360
  commands = font.getPathCommands(content, x, y, fontSize) ?? [];
284
361
  if (fontStyle === "italic") {
285
- setItalic(
362
+ commands = this._italic(
286
363
  commands,
287
- isVertical ? { x: x + glyphHeight / 2, y } : null
364
+ isVertical ? { x: x + glyphHeight / 2, y } : void 0
288
365
  );
289
366
  }
290
367
  }
291
368
  }
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
- }
369
+ commands.push(...this._decoration());
370
+ this.commands = commands;
326
371
  this.path = new modernPath2d.Path2D(commands);
327
372
  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
373
  }
370
374
  update() {
371
375
  this.updatePath();
372
376
  return this;
373
377
  }
378
+ getMinMax(min = modernPath2d.Point2D.MAX, max = modernPath2d.Point2D.MIN) {
379
+ let last = { x: 0, y: 0 };
380
+ this.commands.forEach((cmd) => {
381
+ switch (cmd.type) {
382
+ case "L":
383
+ case "M":
384
+ min.x = Math.min(min.x, cmd.x);
385
+ min.y = Math.min(min.y, cmd.y);
386
+ max.x = Math.max(max.x, cmd.x);
387
+ max.y = Math.max(max.y, cmd.y);
388
+ last = { x: cmd.x, y: cmd.y };
389
+ break;
390
+ case "Q": {
391
+ const x1 = 0.5 * (last.x + cmd.x1);
392
+ const y1 = 0.5 * (last.y + cmd.y1);
393
+ const x2 = 0.5 * (last.x + cmd.x);
394
+ const y2 = 0.5 * (last.y + cmd.y);
395
+ min.x = Math.min(min.x, last.x, cmd.x, x1, x2);
396
+ min.y = Math.min(min.y, last.y, cmd.y, y1, y2);
397
+ max.x = Math.max(max.x, last.x, cmd.x, x1, x2);
398
+ max.y = Math.max(max.y, last.y, cmd.y, y1, y2);
399
+ last = { x: cmd.x, y: cmd.y };
400
+ break;
401
+ }
402
+ }
403
+ });
404
+ return { min, max };
405
+ }
374
406
  drawTo(ctx, config = {}) {
375
407
  drawPaths({
376
408
  ctx,
@@ -455,6 +487,7 @@ class Feature {
455
487
 
456
488
  class Deformer extends Feature {
457
489
  deform() {
490
+ this._text.deformation?.();
458
491
  }
459
492
  }
460
493
 
@@ -994,8 +1027,11 @@ class Text {
994
1027
  }
995
1028
  measure(dom = this.measureDom) {
996
1029
  this.computedStyle = { ...defaultTextStyles, ...this.style };
1030
+ const old = this.paragraphs;
997
1031
  this.paragraphs = this._parser.parse();
998
- return this._measurer.measure(dom);
1032
+ const result = this._measurer.measure(dom);
1033
+ this.paragraphs = old;
1034
+ return result;
999
1035
  }
1000
1036
  requestUpdate() {
1001
1037
  this.needsUpdate = true;
@@ -1012,7 +1048,7 @@ class Text {
1012
1048
  this._highlighter.highlight();
1013
1049
  const min = modernPath2d.Point2D.MAX;
1014
1050
  const max = modernPath2d.Point2D.MIN;
1015
- this.characters.forEach((c) => c.path.getMinMax(min, max));
1051
+ this.characters.forEach((c) => c.getMinMax(min, max));
1016
1052
  this.renderBoundingBox = new modernPath2d.BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
1017
1053
  return this;
1018
1054
  }
@@ -1071,9 +1107,6 @@ exports.getScalePoint = getScalePoint;
1071
1107
  exports.getSkewPoint = getSkewPoint;
1072
1108
  exports.parseColor = parseColor;
1073
1109
  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
1110
  Object.keys(modernFont).forEach(function (k) {
1078
1111
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = modernFont[k];
1079
1112
  });
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