@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.
@@ -186,14 +186,33 @@ var FontRegistry = class {
186
186
  fonts = /* @__PURE__ */ new Map();
187
187
  blobs = /* @__PURE__ */ new Map();
188
188
  wasmBaseURL;
189
+ initPromise;
189
190
  constructor(wasmBaseURL) {
190
191
  this.wasmBaseURL = wasmBaseURL;
191
192
  }
192
193
  async init() {
193
- if (!this.hb) this.hb = await initHB(this.wasmBaseURL);
194
+ if (this.initPromise) {
195
+ await this.initPromise;
196
+ return;
197
+ }
198
+ if (this.hb) {
199
+ return;
200
+ }
201
+ this.initPromise = this._doInit();
202
+ await this.initPromise;
203
+ }
204
+ async _doInit() {
205
+ try {
206
+ this.hb = await initHB(this.wasmBaseURL);
207
+ } catch (error) {
208
+ this.initPromise = void 0;
209
+ throw error;
210
+ }
194
211
  }
195
- getHB() {
196
- if (!this.hb) throw new Error("FontRegistry not initialized - call init() first");
212
+ async getHB() {
213
+ if (!this.hb) {
214
+ await this.init();
215
+ }
197
216
  return this.hb;
198
217
  }
199
218
  key(desc) {
@@ -212,23 +231,24 @@ var FontRegistry = class {
212
231
  this.faces.set(k, face);
213
232
  this.fonts.set(k, font);
214
233
  }
215
- getFont(desc) {
234
+ async getFont(desc) {
235
+ if (!this.hb) await this.init();
216
236
  const k = this.key(desc);
217
237
  const f = this.fonts.get(k);
218
238
  if (!f) throw new Error(`Font not registered for ${k}`);
219
239
  return f;
220
240
  }
221
- getFace(desc) {
241
+ async getFace(desc) {
242
+ if (!this.hb) await this.init();
222
243
  const k = this.key(desc);
223
244
  return this.faces.get(k);
224
245
  }
225
- /** NEW: expose units-per-em for scaling glyph paths to px */
226
- getUnitsPerEm(desc) {
227
- const face = this.getFace(desc);
246
+ async getUnitsPerEm(desc) {
247
+ const face = await this.getFace(desc);
228
248
  return face?.upem || 1e3;
229
249
  }
230
- glyphPath(desc, glyphId) {
231
- const font = this.getFont(desc);
250
+ async glyphPath(desc, glyphId) {
251
+ const font = await this.getFont(desc);
232
252
  const path = font.glyphToPath(glyphId);
233
253
  return path && path !== "" ? path : "M 0 0";
234
254
  }
@@ -239,6 +259,8 @@ var FontRegistry = class {
239
259
  this.fonts.clear();
240
260
  this.faces.clear();
241
261
  this.blobs.clear();
262
+ this.hb = void 0;
263
+ this.initPromise = void 0;
242
264
  }
243
265
  };
244
266
 
@@ -259,13 +281,13 @@ var LayoutEngine = class {
259
281
  return t;
260
282
  }
261
283
  }
262
- shapeFull(text, desc) {
263
- const hb = this.fonts.getHB();
284
+ async shapeFull(text, desc) {
285
+ const hb = await this.fonts.getHB();
264
286
  const buffer = hb.createBuffer();
265
287
  buffer.addText(text);
266
288
  buffer.guessSegmentProperties();
267
- const font = this.fonts.getFont(desc);
268
- const face = this.fonts.getFace(desc);
289
+ const font = await this.fonts.getFont(desc);
290
+ const face = await this.fonts.getFace(desc);
269
291
  const upem = face?.upem || 1e3;
270
292
  font.setScale(upem, upem);
271
293
  hb.shape(font, buffer);
@@ -273,17 +295,17 @@ var LayoutEngine = class {
273
295
  buffer.destroy();
274
296
  return result;
275
297
  }
276
- layout(params) {
298
+ async layout(params) {
277
299
  const { textTransform, desc, fontSize, letterSpacing, width } = params;
278
300
  const input = this.transformText(params.text, textTransform);
279
301
  if (!input || input.length === 0) {
280
302
  return [];
281
303
  }
282
- const shaped = this.shapeFull(input, desc);
283
- const face = this.fonts.getFace(desc);
304
+ const shaped = await this.shapeFull(input, desc);
305
+ const face = await this.fonts.getFace(desc);
284
306
  const upem = face?.upem || 1e3;
285
307
  const scale = fontSize / upem;
286
- const glyphs = shaped.map((g, i) => {
308
+ const glyphs = shaped.map((g) => {
287
309
  const charIndex = g.cl;
288
310
  let char;
289
311
  if (charIndex >= 0 && charIndex < input.length) {
@@ -387,7 +409,7 @@ function decorationGeometry(kind, p) {
387
409
  }
388
410
 
389
411
  // src/core/drawops.ts
390
- function buildDrawOps(p) {
412
+ async function buildDrawOps(p) {
391
413
  const ops = [];
392
414
  ops.push({
393
415
  op: "BeginFrame",
@@ -398,7 +420,7 @@ function buildDrawOps(p) {
398
420
  bg: p.background ? { color: p.background.color, opacity: p.background.opacity, radius: p.background.borderRadius } : void 0
399
421
  });
400
422
  if (p.lines.length === 0) return ops;
401
- const upem = Math.max(1, p.getUnitsPerEm?.() ?? 1e3);
423
+ const upem = Math.max(1, await p.getUnitsPerEm());
402
424
  const scale = p.font.size / upem;
403
425
  const blockHeight = p.lines[p.lines.length - 1].y;
404
426
  let blockY;
@@ -434,7 +456,7 @@ function buildDrawOps(p) {
434
456
  let xCursor = lineX;
435
457
  const baselineY = blockY + line.y - p.font.size;
436
458
  for (const glyph of line.glyphs) {
437
- const path = p.glyphPathProvider(glyph.id);
459
+ const path = await p.glyphPathProvider(glyph.id);
438
460
  if (!path || path === "M 0 0") {
439
461
  xCursor += glyph.xAdvance;
440
462
  continue;
@@ -1426,6 +1448,7 @@ async function createTextEngine(opts = {}) {
1426
1448
  const fonts = new FontRegistry(wasmBaseURL);
1427
1449
  const layout = new LayoutEngine(fonts);
1428
1450
  const videoGenerator = new VideoGenerator();
1451
+ await fonts.init();
1429
1452
  async function ensureFonts(asset) {
1430
1453
  if (asset.customFonts) {
1431
1454
  for (const cf of asset.customFonts) {
@@ -1467,7 +1490,7 @@ async function createTextEngine(opts = {}) {
1467
1490
  async renderFrame(asset, tSeconds) {
1468
1491
  const main = await ensureFonts(asset);
1469
1492
  const desc = { family: main.family, weight: `${main.weight}`, style: main.style };
1470
- const lines = layout.layout({
1493
+ const lines = await layout.layout({
1471
1494
  text: asset.text,
1472
1495
  width: asset.width ?? width,
1473
1496
  letterSpacing: asset.style?.letterSpacing ?? 0,
@@ -1480,7 +1503,7 @@ async function createTextEngine(opts = {}) {
1480
1503
  const canvasW = asset.width ?? width;
1481
1504
  const canvasH = asset.height ?? height;
1482
1505
  const canvasPR = asset.pixelRatio ?? pixelRatio;
1483
- const ops0 = buildDrawOps({
1506
+ const ops0 = await buildDrawOps({
1484
1507
  canvas: { width: canvasW, height: canvasH, pixelRatio: canvasPR },
1485
1508
  textRect,
1486
1509
  lines,
@@ -1502,7 +1525,6 @@ async function createTextEngine(opts = {}) {
1502
1525
  align: asset.align ?? { horizontal: "left", vertical: "middle" },
1503
1526
  background: asset.background,
1504
1527
  glyphPathProvider: (gid) => fonts.glyphPath(desc, gid),
1505
- /** NEW: provide UPEM so drawops can compute scale */
1506
1528
  getUnitsPerEm: () => fonts.getUnitsPerEm(desc)
1507
1529
  });
1508
1530
  const ops = applyAnimation(ops0, lines, {
@@ -1538,6 +1560,9 @@ async function createTextEngine(opts = {}) {
1538
1560
  return this.renderFrame(asset, time);
1539
1561
  };
1540
1562
  await videoGenerator.generateVideo(frameGenerator, finalOptions);
1563
+ },
1564
+ destroy() {
1565
+ fonts.destroy();
1541
1566
  }
1542
1567
  };
1543
1568
  }