modern-text 0.3.1 → 0.3.2

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
@@ -79,16 +79,17 @@ function drawPath(options) {
79
79
  function setupView(ctx, pixelRatio, boundingBox) {
80
80
  const { left, top, width, height } = boundingBox;
81
81
  const view = ctx.canvas;
82
- view.dataset.viewbox = String(`${left} ${top} ${width} ${height}`);
82
+ view.dataset.viewBox = String(`${left} ${top} ${width} ${height}`);
83
83
  view.dataset.pixelRatio = String(pixelRatio);
84
- const canvasWidth = width + left;
85
- const canvasHeight = height + top;
84
+ const canvasWidth = width + Math.abs(left);
85
+ const canvasHeight = height + Math.abs(top);
86
86
  view.width = Math.max(1, Math.ceil(canvasWidth * pixelRatio));
87
87
  view.height = Math.max(1, Math.ceil(canvasHeight * pixelRatio));
88
88
  view.style.width = `${canvasWidth}px`;
89
89
  view.style.height = `${canvasHeight}px`;
90
- ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
90
+ ctx.clearRect(0, 0, view.width, view.height);
91
91
  ctx.scale(pixelRatio, pixelRatio);
92
+ ctx.translate(-Math.min(0, left), -Math.min(0, top));
92
93
  }
93
94
 
94
95
  function uploadColors(ctx, text) {
@@ -150,7 +151,6 @@ class Character {
150
151
  __publicField$3(this, "lineBox", new modernPath2d.BoundingBox());
151
152
  __publicField$3(this, "inlineBox", new modernPath2d.BoundingBox());
152
153
  __publicField$3(this, "glyphBox");
153
- __publicField$3(this, "center");
154
154
  __publicField$3(this, "underlinePosition", 0);
155
155
  __publicField$3(this, "underlineThickness", 0);
156
156
  __publicField$3(this, "yStrikeoutPosition", 0);
@@ -159,6 +159,9 @@ class Character {
159
159
  __publicField$3(this, "centerDiviation", 0);
160
160
  __publicField$3(this, "path", new modernPath2d.Path2D());
161
161
  }
162
+ get center() {
163
+ return this.glyphBox?.center;
164
+ }
162
165
  get computedStyle() {
163
166
  return this.parent.computedStyle;
164
167
  }
@@ -288,7 +291,6 @@ class Character {
288
291
  };
289
292
  this.path = path;
290
293
  this.glyphBox = this.getGlyphBoundingBox();
291
- this.center = this.glyphBox?.getCenterPoint();
292
294
  return this;
293
295
  }
294
296
  update() {
@@ -747,8 +749,8 @@ function getTransformMatrix(a, b, c, isVertical) {
747
749
  y: c.height / b.width
748
750
  };
749
751
  }
750
- const offset = c.getCenterPoint().add(
751
- a.getCenterPoint().sub(b.getCenterPoint()).scale(scale.x, scale.y)
752
+ const offset = c.center.add(
753
+ a.center.sub(b.center).scale(scale.x, scale.y)
752
754
  ).sub({
753
755
  x: a.width / 2 * scale.x,
754
756
  y: a.height / 2 * scale.y
@@ -1103,8 +1105,10 @@ class Text {
1103
1105
  __publicField(this, "needsUpdate", true);
1104
1106
  __publicField(this, "computedStyle", { ...defaultTextStyles });
1105
1107
  __publicField(this, "paragraphs", []);
1106
- __publicField(this, "boundingBox", new modernPath2d.BoundingBox());
1108
+ __publicField(this, "lineBox", new modernPath2d.BoundingBox());
1107
1109
  __publicField(this, "glyphBox", new modernPath2d.BoundingBox());
1110
+ __publicField(this, "pathBox", new modernPath2d.BoundingBox());
1111
+ __publicField(this, "boundingBox", new modernPath2d.BoundingBox());
1108
1112
  __publicField(this, "parser", new Parser(this));
1109
1113
  __publicField(this, "measurer", new Measurer(this));
1110
1114
  __publicField(this, "plugins", /* @__PURE__ */ new Map());
@@ -1132,22 +1136,33 @@ class Text {
1132
1136
  this.computedStyle = { ...defaultTextStyles, ...this.style };
1133
1137
  const old = {
1134
1138
  paragraphs: this.paragraphs,
1135
- boundingBox: this.boundingBox,
1136
- glyphBox: this.glyphBox
1139
+ lineBox: this.lineBox,
1140
+ glyphBox: this.glyphBox,
1141
+ pathBox: this.pathBox,
1142
+ boundingBox: this.boundingBox
1137
1143
  };
1138
1144
  this.paragraphs = this.parser.parse();
1139
1145
  const result = this.measurer.measure(dom);
1140
1146
  this.paragraphs = result.paragraphs;
1141
- this.boundingBox = result.boundingBox;
1142
- const characters = this.characters;
1143
- characters.forEach((c) => c.update());
1147
+ this.lineBox = result.boundingBox;
1148
+ this.characters.forEach((c) => {
1149
+ c.update();
1150
+ });
1144
1151
  const plugins = [...this.plugins.values()];
1145
1152
  plugins.sort((a, b) => (a.updateOrder ?? 0) - (b.updateOrder ?? 0)).forEach((plugin) => {
1146
1153
  plugin.update?.(this);
1147
1154
  });
1155
+ this.updateGlyphBox().updatePathBox().updateBoundingBox();
1156
+ for (const key in old) {
1157
+ result[key] = this[key];
1158
+ this[key] = old[key];
1159
+ }
1160
+ return result;
1161
+ }
1162
+ updateGlyphBox() {
1148
1163
  const min = modernPath2d.Vector2.MAX;
1149
1164
  const max = modernPath2d.Vector2.MIN;
1150
- characters.forEach((c) => {
1165
+ this.characters.forEach((c) => {
1151
1166
  if (!c.getGlyphMinMax(min, max)) {
1152
1167
  const { inlineBox } = c;
1153
1168
  const a = new modernPath2d.Vector2(inlineBox.left, inlineBox.top);
@@ -1156,37 +1171,47 @@ class Text {
1156
1171
  max.max(a, b);
1157
1172
  }
1158
1173
  });
1159
- this.glyphBox = new modernPath2d.BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
1160
- const dLeft = this.glyphBox.left - result.boundingBox.left;
1161
- const dRight = result.boundingBox.right - this.glyphBox.right;
1162
- const dTop = this.glyphBox.top - result.boundingBox.top;
1163
- const dBottom = result.boundingBox.bottom - this.glyphBox.bottom;
1164
- this.glyphBox = modernPath2d.BoundingBox.from(
1174
+ this.glyphBox = new modernPath2d.BoundingBox(
1175
+ min.x,
1176
+ min.y,
1177
+ max.x - min.x,
1178
+ max.y - min.y
1179
+ );
1180
+ return this;
1181
+ }
1182
+ updatePathBox() {
1183
+ const plugins = [...this.plugins.values()];
1184
+ this.pathBox = modernPath2d.BoundingBox.from(
1165
1185
  this.glyphBox,
1166
1186
  ...plugins.map((plugin) => {
1167
- if (plugin.getBoundingBox) {
1168
- return plugin.getBoundingBox(this);
1169
- }
1170
- return modernPath2d.getPathsBoundingBox(plugin.paths ?? []);
1187
+ return plugin.getBoundingBox ? plugin.getBoundingBox(this) : modernPath2d.getPathsBoundingBox(plugin.paths ?? []);
1171
1188
  }).filter(Boolean)
1172
1189
  );
1173
- result.glyphBox = this.glyphBox;
1174
- result.boundingBox.width = this.glyphBox.width + dLeft + dRight;
1175
- result.boundingBox.height = this.glyphBox.height + dTop + dBottom;
1176
- this.paragraphs = old.paragraphs;
1177
- this.boundingBox = old.boundingBox;
1178
- this.glyphBox = old.glyphBox;
1179
- return result;
1190
+ return this;
1191
+ }
1192
+ updateBoundingBox() {
1193
+ const { lineBox, glyphBox, pathBox } = this;
1194
+ const left = pathBox.left + lineBox.left - glyphBox.left;
1195
+ const top = pathBox.top + lineBox.top - glyphBox.top;
1196
+ const right = pathBox.right + Math.max(0, lineBox.right - glyphBox.right);
1197
+ const bottom = pathBox.bottom + Math.max(0, lineBox.bottom - glyphBox.bottom);
1198
+ this.boundingBox = new modernPath2d.BoundingBox(
1199
+ left,
1200
+ top,
1201
+ right - left,
1202
+ bottom - top
1203
+ );
1204
+ return this;
1180
1205
  }
1181
1206
  requestUpdate() {
1182
1207
  this.needsUpdate = true;
1183
1208
  return this;
1184
1209
  }
1185
1210
  update() {
1186
- const { paragraphs, boundingBox, glyphBox } = this.measure();
1187
- this.paragraphs = paragraphs;
1188
- this.boundingBox = boundingBox;
1189
- this.glyphBox = glyphBox;
1211
+ const result = this.measure();
1212
+ for (const key in result) {
1213
+ this[key] = result[key];
1214
+ }
1190
1215
  return this;
1191
1216
  }
1192
1217
  render(options) {
package/dist/index.d.cts CHANGED
@@ -117,7 +117,6 @@ declare class Character {
117
117
  lineBox: BoundingBox;
118
118
  inlineBox: BoundingBox;
119
119
  glyphBox: BoundingBox | undefined;
120
- center: Vector2 | undefined;
121
120
  underlinePosition: number;
122
121
  underlineThickness: number;
123
122
  yStrikeoutPosition: number;
@@ -125,6 +124,7 @@ declare class Character {
125
124
  baseline: number;
126
125
  centerDiviation: number;
127
126
  path: Path2D;
127
+ get center(): Vector2 | undefined;
128
128
  get computedStyle(): TextStyle;
129
129
  get isVertical(): boolean;
130
130
  get fontSize(): number;
@@ -155,6 +155,18 @@ declare class Paragraph {
155
155
  addFragment(content: string, style?: Partial<TextStyle>): Fragment;
156
156
  }
157
157
 
158
+ type PromiseLike<T> = T | Promise<T>;
159
+ interface Plugin {
160
+ name: string;
161
+ paths?: Path2D[];
162
+ getBoundingBox?: (text: Text) => BoundingBox | undefined;
163
+ updateOrder?: number;
164
+ update?: (text: Text) => PromiseLike<void>;
165
+ renderOrder?: number;
166
+ render?: (ctx: CanvasRenderingContext2D, text: Text) => PromiseLike<void>;
167
+ }
168
+ declare function definePlugin(options: Plugin): Plugin;
169
+
158
170
  interface MeasuredParagraph {
159
171
  paragraphIndex: number;
160
172
  left: number;
@@ -214,18 +226,6 @@ declare class Measurer {
214
226
  measure(dom?: HTMLElement): MeasureDomResult;
215
227
  }
216
228
 
217
- type PromiseLike<T> = T | Promise<T>;
218
- interface Plugin {
219
- name: string;
220
- paths?: Path2D[];
221
- getBoundingBox?: (text: Text) => BoundingBox | undefined;
222
- updateOrder?: number;
223
- update?: (text: Text) => PromiseLike<void>;
224
- renderOrder?: number;
225
- render?: (ctx: CanvasRenderingContext2D, text: Text) => PromiseLike<void>;
226
- }
227
- declare function definePlugin(options: Plugin): Plugin;
228
-
229
229
  declare class Parser {
230
230
  protected _text: Text;
231
231
  constructor(_text: Text);
@@ -242,9 +242,13 @@ interface TextOptions {
242
242
  measureDom?: HTMLElement;
243
243
  effects?: Partial<TextStyle>[];
244
244
  }
245
- type MeasureResult = MeasureDomResult & {
245
+ interface MeasureResult {
246
+ paragraphs: Paragraph[];
247
+ lineBox: BoundingBox;
246
248
  glyphBox: BoundingBox;
247
- };
249
+ pathBox: BoundingBox;
250
+ boundingBox: BoundingBox;
251
+ }
248
252
  declare const defaultTextStyles: TextStyle;
249
253
  declare class Text {
250
254
  content: TextContent;
@@ -254,8 +258,10 @@ declare class Text {
254
258
  needsUpdate: boolean;
255
259
  computedStyle: TextStyle;
256
260
  paragraphs: Paragraph[];
257
- boundingBox: BoundingBox;
261
+ lineBox: BoundingBox;
258
262
  glyphBox: BoundingBox;
263
+ pathBox: BoundingBox;
264
+ boundingBox: BoundingBox;
259
265
  parser: Parser;
260
266
  measurer: Measurer;
261
267
  plugins: Map<string, Plugin>;
@@ -265,6 +271,9 @@ declare class Text {
265
271
  constructor(options?: TextOptions);
266
272
  use(plugin: Plugin): this;
267
273
  measure(dom?: HTMLElement | undefined): MeasureResult;
274
+ updateGlyphBox(): this;
275
+ updatePathBox(): this;
276
+ updateBoundingBox(): this;
268
277
  requestUpdate(): this;
269
278
  update(): this;
270
279
  render(options: TextRenderOptions): this;
package/dist/index.d.mts CHANGED
@@ -117,7 +117,6 @@ declare class Character {
117
117
  lineBox: BoundingBox;
118
118
  inlineBox: BoundingBox;
119
119
  glyphBox: BoundingBox | undefined;
120
- center: Vector2 | undefined;
121
120
  underlinePosition: number;
122
121
  underlineThickness: number;
123
122
  yStrikeoutPosition: number;
@@ -125,6 +124,7 @@ declare class Character {
125
124
  baseline: number;
126
125
  centerDiviation: number;
127
126
  path: Path2D;
127
+ get center(): Vector2 | undefined;
128
128
  get computedStyle(): TextStyle;
129
129
  get isVertical(): boolean;
130
130
  get fontSize(): number;
@@ -155,6 +155,18 @@ declare class Paragraph {
155
155
  addFragment(content: string, style?: Partial<TextStyle>): Fragment;
156
156
  }
157
157
 
158
+ type PromiseLike<T> = T | Promise<T>;
159
+ interface Plugin {
160
+ name: string;
161
+ paths?: Path2D[];
162
+ getBoundingBox?: (text: Text) => BoundingBox | undefined;
163
+ updateOrder?: number;
164
+ update?: (text: Text) => PromiseLike<void>;
165
+ renderOrder?: number;
166
+ render?: (ctx: CanvasRenderingContext2D, text: Text) => PromiseLike<void>;
167
+ }
168
+ declare function definePlugin(options: Plugin): Plugin;
169
+
158
170
  interface MeasuredParagraph {
159
171
  paragraphIndex: number;
160
172
  left: number;
@@ -214,18 +226,6 @@ declare class Measurer {
214
226
  measure(dom?: HTMLElement): MeasureDomResult;
215
227
  }
216
228
 
217
- type PromiseLike<T> = T | Promise<T>;
218
- interface Plugin {
219
- name: string;
220
- paths?: Path2D[];
221
- getBoundingBox?: (text: Text) => BoundingBox | undefined;
222
- updateOrder?: number;
223
- update?: (text: Text) => PromiseLike<void>;
224
- renderOrder?: number;
225
- render?: (ctx: CanvasRenderingContext2D, text: Text) => PromiseLike<void>;
226
- }
227
- declare function definePlugin(options: Plugin): Plugin;
228
-
229
229
  declare class Parser {
230
230
  protected _text: Text;
231
231
  constructor(_text: Text);
@@ -242,9 +242,13 @@ interface TextOptions {
242
242
  measureDom?: HTMLElement;
243
243
  effects?: Partial<TextStyle>[];
244
244
  }
245
- type MeasureResult = MeasureDomResult & {
245
+ interface MeasureResult {
246
+ paragraphs: Paragraph[];
247
+ lineBox: BoundingBox;
246
248
  glyphBox: BoundingBox;
247
- };
249
+ pathBox: BoundingBox;
250
+ boundingBox: BoundingBox;
251
+ }
248
252
  declare const defaultTextStyles: TextStyle;
249
253
  declare class Text {
250
254
  content: TextContent;
@@ -254,8 +258,10 @@ declare class Text {
254
258
  needsUpdate: boolean;
255
259
  computedStyle: TextStyle;
256
260
  paragraphs: Paragraph[];
257
- boundingBox: BoundingBox;
261
+ lineBox: BoundingBox;
258
262
  glyphBox: BoundingBox;
263
+ pathBox: BoundingBox;
264
+ boundingBox: BoundingBox;
259
265
  parser: Parser;
260
266
  measurer: Measurer;
261
267
  plugins: Map<string, Plugin>;
@@ -265,6 +271,9 @@ declare class Text {
265
271
  constructor(options?: TextOptions);
266
272
  use(plugin: Plugin): this;
267
273
  measure(dom?: HTMLElement | undefined): MeasureResult;
274
+ updateGlyphBox(): this;
275
+ updatePathBox(): this;
276
+ updateBoundingBox(): this;
268
277
  requestUpdate(): this;
269
278
  update(): this;
270
279
  render(options: TextRenderOptions): this;
package/dist/index.d.ts CHANGED
@@ -117,7 +117,6 @@ declare class Character {
117
117
  lineBox: BoundingBox;
118
118
  inlineBox: BoundingBox;
119
119
  glyphBox: BoundingBox | undefined;
120
- center: Vector2 | undefined;
121
120
  underlinePosition: number;
122
121
  underlineThickness: number;
123
122
  yStrikeoutPosition: number;
@@ -125,6 +124,7 @@ declare class Character {
125
124
  baseline: number;
126
125
  centerDiviation: number;
127
126
  path: Path2D;
127
+ get center(): Vector2 | undefined;
128
128
  get computedStyle(): TextStyle;
129
129
  get isVertical(): boolean;
130
130
  get fontSize(): number;
@@ -155,6 +155,18 @@ declare class Paragraph {
155
155
  addFragment(content: string, style?: Partial<TextStyle>): Fragment;
156
156
  }
157
157
 
158
+ type PromiseLike<T> = T | Promise<T>;
159
+ interface Plugin {
160
+ name: string;
161
+ paths?: Path2D[];
162
+ getBoundingBox?: (text: Text) => BoundingBox | undefined;
163
+ updateOrder?: number;
164
+ update?: (text: Text) => PromiseLike<void>;
165
+ renderOrder?: number;
166
+ render?: (ctx: CanvasRenderingContext2D, text: Text) => PromiseLike<void>;
167
+ }
168
+ declare function definePlugin(options: Plugin): Plugin;
169
+
158
170
  interface MeasuredParagraph {
159
171
  paragraphIndex: number;
160
172
  left: number;
@@ -214,18 +226,6 @@ declare class Measurer {
214
226
  measure(dom?: HTMLElement): MeasureDomResult;
215
227
  }
216
228
 
217
- type PromiseLike<T> = T | Promise<T>;
218
- interface Plugin {
219
- name: string;
220
- paths?: Path2D[];
221
- getBoundingBox?: (text: Text) => BoundingBox | undefined;
222
- updateOrder?: number;
223
- update?: (text: Text) => PromiseLike<void>;
224
- renderOrder?: number;
225
- render?: (ctx: CanvasRenderingContext2D, text: Text) => PromiseLike<void>;
226
- }
227
- declare function definePlugin(options: Plugin): Plugin;
228
-
229
229
  declare class Parser {
230
230
  protected _text: Text;
231
231
  constructor(_text: Text);
@@ -242,9 +242,13 @@ interface TextOptions {
242
242
  measureDom?: HTMLElement;
243
243
  effects?: Partial<TextStyle>[];
244
244
  }
245
- type MeasureResult = MeasureDomResult & {
245
+ interface MeasureResult {
246
+ paragraphs: Paragraph[];
247
+ lineBox: BoundingBox;
246
248
  glyphBox: BoundingBox;
247
- };
249
+ pathBox: BoundingBox;
250
+ boundingBox: BoundingBox;
251
+ }
248
252
  declare const defaultTextStyles: TextStyle;
249
253
  declare class Text {
250
254
  content: TextContent;
@@ -254,8 +258,10 @@ declare class Text {
254
258
  needsUpdate: boolean;
255
259
  computedStyle: TextStyle;
256
260
  paragraphs: Paragraph[];
257
- boundingBox: BoundingBox;
261
+ lineBox: BoundingBox;
258
262
  glyphBox: BoundingBox;
263
+ pathBox: BoundingBox;
264
+ boundingBox: BoundingBox;
259
265
  parser: Parser;
260
266
  measurer: Measurer;
261
267
  plugins: Map<string, Plugin>;
@@ -265,6 +271,9 @@ declare class Text {
265
271
  constructor(options?: TextOptions);
266
272
  use(plugin: Plugin): this;
267
273
  measure(dom?: HTMLElement | undefined): MeasureResult;
274
+ updateGlyphBox(): this;
275
+ updatePathBox(): this;
276
+ updateBoundingBox(): this;
268
277
  requestUpdate(): this;
269
278
  update(): this;
270
279
  render(options: TextRenderOptions): this;