modern-text 0.3.1 → 0.3.3

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,11 @@ 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());
1109
+ __publicField(this, "rawGlyphBox", new modernPath2d.BoundingBox());
1107
1110
  __publicField(this, "glyphBox", new modernPath2d.BoundingBox());
1111
+ __publicField(this, "pathBox", new modernPath2d.BoundingBox());
1112
+ __publicField(this, "boundingBox", new modernPath2d.BoundingBox());
1108
1113
  __publicField(this, "parser", new Parser(this));
1109
1114
  __publicField(this, "measurer", new Measurer(this));
1110
1115
  __publicField(this, "plugins", /* @__PURE__ */ new Map());
@@ -1132,22 +1137,36 @@ class Text {
1132
1137
  this.computedStyle = { ...defaultTextStyles, ...this.style };
1133
1138
  const old = {
1134
1139
  paragraphs: this.paragraphs,
1135
- boundingBox: this.boundingBox,
1136
- glyphBox: this.glyphBox
1140
+ lineBox: this.lineBox,
1141
+ rawGlyphBox: this.rawGlyphBox,
1142
+ glyphBox: this.glyphBox,
1143
+ pathBox: this.pathBox,
1144
+ boundingBox: this.boundingBox
1137
1145
  };
1138
1146
  this.paragraphs = this.parser.parse();
1139
1147
  const result = this.measurer.measure(dom);
1140
1148
  this.paragraphs = result.paragraphs;
1141
- this.boundingBox = result.boundingBox;
1142
- const characters = this.characters;
1143
- characters.forEach((c) => c.update());
1149
+ this.lineBox = result.boundingBox;
1150
+ this.characters.forEach((c) => {
1151
+ c.update();
1152
+ });
1153
+ this.rawGlyphBox = this.getGlyphBox();
1144
1154
  const plugins = [...this.plugins.values()];
1145
1155
  plugins.sort((a, b) => (a.updateOrder ?? 0) - (b.updateOrder ?? 0)).forEach((plugin) => {
1146
1156
  plugin.update?.(this);
1147
1157
  });
1158
+ this.glyphBox = this.getGlyphBox();
1159
+ this.updatePathBox().updateBoundingBox();
1160
+ for (const key in old) {
1161
+ result[key] = this[key];
1162
+ this[key] = old[key];
1163
+ }
1164
+ return result;
1165
+ }
1166
+ getGlyphBox() {
1148
1167
  const min = modernPath2d.Vector2.MAX;
1149
1168
  const max = modernPath2d.Vector2.MIN;
1150
- characters.forEach((c) => {
1169
+ this.characters.forEach((c) => {
1151
1170
  if (!c.getGlyphMinMax(min, max)) {
1152
1171
  const { inlineBox } = c;
1153
1172
  const a = new modernPath2d.Vector2(inlineBox.left, inlineBox.top);
@@ -1156,37 +1175,46 @@ class Text {
1156
1175
  max.max(a, b);
1157
1176
  }
1158
1177
  });
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(
1178
+ return new modernPath2d.BoundingBox(
1179
+ min.x,
1180
+ min.y,
1181
+ max.x - min.x,
1182
+ max.y - min.y
1183
+ );
1184
+ }
1185
+ updatePathBox() {
1186
+ const plugins = [...this.plugins.values()];
1187
+ this.pathBox = modernPath2d.BoundingBox.from(
1165
1188
  this.glyphBox,
1166
1189
  ...plugins.map((plugin) => {
1167
- if (plugin.getBoundingBox) {
1168
- return plugin.getBoundingBox(this);
1169
- }
1170
- return modernPath2d.getPathsBoundingBox(plugin.paths ?? []);
1190
+ return plugin.getBoundingBox ? plugin.getBoundingBox(this) : modernPath2d.getPathsBoundingBox(plugin.paths ?? []);
1171
1191
  }).filter(Boolean)
1172
1192
  );
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;
1193
+ return this;
1194
+ }
1195
+ updateBoundingBox() {
1196
+ const { lineBox, rawGlyphBox, pathBox } = this;
1197
+ const left = pathBox.left + lineBox.left - rawGlyphBox.left;
1198
+ const top = pathBox.top + lineBox.top - rawGlyphBox.top;
1199
+ const right = pathBox.right + Math.max(0, lineBox.right - rawGlyphBox.right);
1200
+ const bottom = pathBox.bottom + Math.max(0, lineBox.bottom - rawGlyphBox.bottom);
1201
+ this.boundingBox = new modernPath2d.BoundingBox(
1202
+ left,
1203
+ top,
1204
+ right - left,
1205
+ bottom - top
1206
+ );
1207
+ return this;
1180
1208
  }
1181
1209
  requestUpdate() {
1182
1210
  this.needsUpdate = true;
1183
1211
  return this;
1184
1212
  }
1185
1213
  update() {
1186
- const { paragraphs, boundingBox, glyphBox } = this.measure();
1187
- this.paragraphs = paragraphs;
1188
- this.boundingBox = boundingBox;
1189
- this.glyphBox = glyphBox;
1214
+ const result = this.measure();
1215
+ for (const key in result) {
1216
+ this[key] = result[key];
1217
+ }
1190
1218
  return this;
1191
1219
  }
1192
1220
  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,14 @@ 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;
248
+ rawGlyphBox: BoundingBox;
246
249
  glyphBox: BoundingBox;
247
- };
250
+ pathBox: BoundingBox;
251
+ boundingBox: BoundingBox;
252
+ }
248
253
  declare const defaultTextStyles: TextStyle;
249
254
  declare class Text {
250
255
  content: TextContent;
@@ -254,8 +259,11 @@ declare class Text {
254
259
  needsUpdate: boolean;
255
260
  computedStyle: TextStyle;
256
261
  paragraphs: Paragraph[];
257
- boundingBox: BoundingBox;
262
+ lineBox: BoundingBox;
263
+ rawGlyphBox: BoundingBox;
258
264
  glyphBox: BoundingBox;
265
+ pathBox: BoundingBox;
266
+ boundingBox: BoundingBox;
259
267
  parser: Parser;
260
268
  measurer: Measurer;
261
269
  plugins: Map<string, Plugin>;
@@ -265,6 +273,9 @@ declare class Text {
265
273
  constructor(options?: TextOptions);
266
274
  use(plugin: Plugin): this;
267
275
  measure(dom?: HTMLElement | undefined): MeasureResult;
276
+ getGlyphBox(): BoundingBox;
277
+ updatePathBox(): this;
278
+ updateBoundingBox(): this;
268
279
  requestUpdate(): this;
269
280
  update(): this;
270
281
  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,14 @@ 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;
248
+ rawGlyphBox: BoundingBox;
246
249
  glyphBox: BoundingBox;
247
- };
250
+ pathBox: BoundingBox;
251
+ boundingBox: BoundingBox;
252
+ }
248
253
  declare const defaultTextStyles: TextStyle;
249
254
  declare class Text {
250
255
  content: TextContent;
@@ -254,8 +259,11 @@ declare class Text {
254
259
  needsUpdate: boolean;
255
260
  computedStyle: TextStyle;
256
261
  paragraphs: Paragraph[];
257
- boundingBox: BoundingBox;
262
+ lineBox: BoundingBox;
263
+ rawGlyphBox: BoundingBox;
258
264
  glyphBox: BoundingBox;
265
+ pathBox: BoundingBox;
266
+ boundingBox: BoundingBox;
259
267
  parser: Parser;
260
268
  measurer: Measurer;
261
269
  plugins: Map<string, Plugin>;
@@ -265,6 +273,9 @@ declare class Text {
265
273
  constructor(options?: TextOptions);
266
274
  use(plugin: Plugin): this;
267
275
  measure(dom?: HTMLElement | undefined): MeasureResult;
276
+ getGlyphBox(): BoundingBox;
277
+ updatePathBox(): this;
278
+ updateBoundingBox(): this;
268
279
  requestUpdate(): this;
269
280
  update(): this;
270
281
  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,14 @@ 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;
248
+ rawGlyphBox: BoundingBox;
246
249
  glyphBox: BoundingBox;
247
- };
250
+ pathBox: BoundingBox;
251
+ boundingBox: BoundingBox;
252
+ }
248
253
  declare const defaultTextStyles: TextStyle;
249
254
  declare class Text {
250
255
  content: TextContent;
@@ -254,8 +259,11 @@ declare class Text {
254
259
  needsUpdate: boolean;
255
260
  computedStyle: TextStyle;
256
261
  paragraphs: Paragraph[];
257
- boundingBox: BoundingBox;
262
+ lineBox: BoundingBox;
263
+ rawGlyphBox: BoundingBox;
258
264
  glyphBox: BoundingBox;
265
+ pathBox: BoundingBox;
266
+ boundingBox: BoundingBox;
259
267
  parser: Parser;
260
268
  measurer: Measurer;
261
269
  plugins: Map<string, Plugin>;
@@ -265,6 +273,9 @@ declare class Text {
265
273
  constructor(options?: TextOptions);
266
274
  use(plugin: Plugin): this;
267
275
  measure(dom?: HTMLElement | undefined): MeasureResult;
276
+ getGlyphBox(): BoundingBox;
277
+ updatePathBox(): this;
278
+ updateBoundingBox(): this;
268
279
  requestUpdate(): this;
269
280
  update(): this;
270
281
  render(options: TextRenderOptions): this;