@shotstack/shotstack-canvas 1.0.7 → 1.0.8

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.
@@ -150,14 +150,33 @@ var FontRegistry = class {
150
150
  fonts = /* @__PURE__ */ new Map();
151
151
  blobs = /* @__PURE__ */ new Map();
152
152
  wasmBaseURL;
153
+ initPromise;
153
154
  constructor(wasmBaseURL) {
154
155
  this.wasmBaseURL = wasmBaseURL;
155
156
  }
156
157
  async init() {
157
- if (!this.hb) this.hb = await initHB(this.wasmBaseURL);
158
+ if (this.initPromise) {
159
+ await this.initPromise;
160
+ return;
161
+ }
162
+ if (this.hb) {
163
+ return;
164
+ }
165
+ this.initPromise = this._doInit();
166
+ await this.initPromise;
167
+ }
168
+ async _doInit() {
169
+ try {
170
+ this.hb = await initHB(this.wasmBaseURL);
171
+ } catch (error) {
172
+ this.initPromise = void 0;
173
+ throw error;
174
+ }
158
175
  }
159
- getHB() {
160
- if (!this.hb) throw new Error("FontRegistry not initialized - call init() first");
176
+ async getHB() {
177
+ if (!this.hb) {
178
+ await this.init();
179
+ }
161
180
  return this.hb;
162
181
  }
163
182
  key(desc) {
@@ -176,23 +195,24 @@ var FontRegistry = class {
176
195
  this.faces.set(k, face);
177
196
  this.fonts.set(k, font);
178
197
  }
179
- getFont(desc) {
198
+ async getFont(desc) {
199
+ if (!this.hb) await this.init();
180
200
  const k = this.key(desc);
181
201
  const f = this.fonts.get(k);
182
202
  if (!f) throw new Error(`Font not registered for ${k}`);
183
203
  return f;
184
204
  }
185
- getFace(desc) {
205
+ async getFace(desc) {
206
+ if (!this.hb) await this.init();
186
207
  const k = this.key(desc);
187
208
  return this.faces.get(k);
188
209
  }
189
- /** NEW: expose units-per-em for scaling glyph paths to px */
190
- getUnitsPerEm(desc) {
191
- const face = this.getFace(desc);
210
+ async getUnitsPerEm(desc) {
211
+ const face = await this.getFace(desc);
192
212
  return face?.upem || 1e3;
193
213
  }
194
- glyphPath(desc, glyphId) {
195
- const font = this.getFont(desc);
214
+ async glyphPath(desc, glyphId) {
215
+ const font = await this.getFont(desc);
196
216
  const path = font.glyphToPath(glyphId);
197
217
  return path && path !== "" ? path : "M 0 0";
198
218
  }
@@ -203,6 +223,8 @@ var FontRegistry = class {
203
223
  this.fonts.clear();
204
224
  this.faces.clear();
205
225
  this.blobs.clear();
226
+ this.hb = void 0;
227
+ this.initPromise = void 0;
206
228
  }
207
229
  };
208
230
 
@@ -223,13 +245,13 @@ var LayoutEngine = class {
223
245
  return t;
224
246
  }
225
247
  }
226
- shapeFull(text, desc) {
227
- const hb = this.fonts.getHB();
248
+ async shapeFull(text, desc) {
249
+ const hb = await this.fonts.getHB();
228
250
  const buffer = hb.createBuffer();
229
251
  buffer.addText(text);
230
252
  buffer.guessSegmentProperties();
231
- const font = this.fonts.getFont(desc);
232
- const face = this.fonts.getFace(desc);
253
+ const font = await this.fonts.getFont(desc);
254
+ const face = await this.fonts.getFace(desc);
233
255
  const upem = face?.upem || 1e3;
234
256
  font.setScale(upem, upem);
235
257
  hb.shape(font, buffer);
@@ -237,17 +259,17 @@ var LayoutEngine = class {
237
259
  buffer.destroy();
238
260
  return result;
239
261
  }
240
- layout(params) {
262
+ async layout(params) {
241
263
  const { textTransform, desc, fontSize, letterSpacing, width } = params;
242
264
  const input = this.transformText(params.text, textTransform);
243
265
  if (!input || input.length === 0) {
244
266
  return [];
245
267
  }
246
- const shaped = this.shapeFull(input, desc);
247
- const face = this.fonts.getFace(desc);
268
+ const shaped = await this.shapeFull(input, desc);
269
+ const face = await this.fonts.getFace(desc);
248
270
  const upem = face?.upem || 1e3;
249
271
  const scale = fontSize / upem;
250
- const glyphs = shaped.map((g, i) => {
272
+ const glyphs = shaped.map((g) => {
251
273
  const charIndex = g.cl;
252
274
  let char;
253
275
  if (charIndex >= 0 && charIndex < input.length) {
@@ -351,7 +373,7 @@ function decorationGeometry(kind, p) {
351
373
  }
352
374
 
353
375
  // src/core/drawops.ts
354
- function buildDrawOps(p) {
376
+ async function buildDrawOps(p) {
355
377
  const ops = [];
356
378
  ops.push({
357
379
  op: "BeginFrame",
@@ -362,7 +384,7 @@ function buildDrawOps(p) {
362
384
  bg: p.background ? { color: p.background.color, opacity: p.background.opacity, radius: p.background.borderRadius } : void 0
363
385
  });
364
386
  if (p.lines.length === 0) return ops;
365
- const upem = Math.max(1, p.getUnitsPerEm?.() ?? 1e3);
387
+ const upem = Math.max(1, await p.getUnitsPerEm());
366
388
  const scale = p.font.size / upem;
367
389
  const blockHeight = p.lines[p.lines.length - 1].y;
368
390
  let blockY;
@@ -398,7 +420,7 @@ function buildDrawOps(p) {
398
420
  let xCursor = lineX;
399
421
  const baselineY = blockY + line.y - p.font.size;
400
422
  for (const glyph of line.glyphs) {
401
- const path = p.glyphPathProvider(glyph.id);
423
+ const path = await p.glyphPathProvider(glyph.id);
402
424
  if (!path || path === "M 0 0") {
403
425
  xCursor += glyph.xAdvance;
404
426
  continue;
@@ -1390,6 +1412,7 @@ async function createTextEngine(opts = {}) {
1390
1412
  const fonts = new FontRegistry(wasmBaseURL);
1391
1413
  const layout = new LayoutEngine(fonts);
1392
1414
  const videoGenerator = new VideoGenerator();
1415
+ await fonts.init();
1393
1416
  async function ensureFonts(asset) {
1394
1417
  if (asset.customFonts) {
1395
1418
  for (const cf of asset.customFonts) {
@@ -1431,7 +1454,7 @@ async function createTextEngine(opts = {}) {
1431
1454
  async renderFrame(asset, tSeconds) {
1432
1455
  const main = await ensureFonts(asset);
1433
1456
  const desc = { family: main.family, weight: `${main.weight}`, style: main.style };
1434
- const lines = layout.layout({
1457
+ const lines = await layout.layout({
1435
1458
  text: asset.text,
1436
1459
  width: asset.width ?? width,
1437
1460
  letterSpacing: asset.style?.letterSpacing ?? 0,
@@ -1444,7 +1467,7 @@ async function createTextEngine(opts = {}) {
1444
1467
  const canvasW = asset.width ?? width;
1445
1468
  const canvasH = asset.height ?? height;
1446
1469
  const canvasPR = asset.pixelRatio ?? pixelRatio;
1447
- const ops0 = buildDrawOps({
1470
+ const ops0 = await buildDrawOps({
1448
1471
  canvas: { width: canvasW, height: canvasH, pixelRatio: canvasPR },
1449
1472
  textRect,
1450
1473
  lines,
@@ -1466,7 +1489,6 @@ async function createTextEngine(opts = {}) {
1466
1489
  align: asset.align ?? { horizontal: "left", vertical: "middle" },
1467
1490
  background: asset.background,
1468
1491
  glyphPathProvider: (gid) => fonts.glyphPath(desc, gid),
1469
- /** NEW: provide UPEM so drawops can compute scale */
1470
1492
  getUnitsPerEm: () => fonts.getUnitsPerEm(desc)
1471
1493
  });
1472
1494
  const ops = applyAnimation(ops0, lines, {
@@ -1502,6 +1524,9 @@ async function createTextEngine(opts = {}) {
1502
1524
  return this.renderFrame(asset, time);
1503
1525
  };
1504
1526
  await videoGenerator.generateVideo(frameGenerator, finalOptions);
1527
+ },
1528
+ destroy() {
1529
+ fonts.destroy();
1505
1530
  }
1506
1531
  };
1507
1532
  }