@shotstack/shotstack-canvas 1.0.7 → 1.0.9
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/entry.node.cjs +69 -31
- package/dist/entry.node.cjs.map +1 -1
- package/dist/entry.node.d.cts +1 -0
- package/dist/entry.node.d.ts +1 -0
- package/dist/entry.node.js +68 -31
- package/dist/entry.node.js.map +1 -1
- package/dist/entry.web.d.ts +1 -0
- package/dist/entry.web.js +68 -31
- package/dist/entry.web.js.map +1 -1
- package/package.json +1 -1
package/dist/entry.web.js
CHANGED
|
@@ -133,14 +133,26 @@ var hbSingleton = null;
|
|
|
133
133
|
async function initHB(wasmBaseURL) {
|
|
134
134
|
if (hbSingleton) return hbSingleton;
|
|
135
135
|
const harfbuzzjs = await import("harfbuzzjs");
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
} else {
|
|
142
|
-
hbSingleton = hbPromise;
|
|
136
|
+
const factory = harfbuzzjs.default;
|
|
137
|
+
let hbUrlFromImport;
|
|
138
|
+
try {
|
|
139
|
+
hbUrlFromImport = (await import("harfbuzzjs/hb.wasm?url")).default;
|
|
140
|
+
} catch {
|
|
143
141
|
}
|
|
142
|
+
const base = (() => {
|
|
143
|
+
if (wasmBaseURL) return wasmBaseURL.replace(/\/$/, "");
|
|
144
|
+
if (hbUrlFromImport) return hbUrlFromImport.replace(/hb\.wasm(?:\?.*)?$/, "");
|
|
145
|
+
return new URL(".", import.meta.url).toString().replace(/\/$/, "");
|
|
146
|
+
})();
|
|
147
|
+
const locateFile = (path, scriptDir) => {
|
|
148
|
+
if (path.endsWith(".wasm")) {
|
|
149
|
+
return `${base}/${path}`.replace(/([^:])\/{2,}/g, "$1/");
|
|
150
|
+
}
|
|
151
|
+
return scriptDir ? scriptDir + path : path;
|
|
152
|
+
};
|
|
153
|
+
const initArg = { locateFile };
|
|
154
|
+
const maybePromise = typeof factory === "function" ? factory(initArg) : factory;
|
|
155
|
+
hbSingleton = maybePromise && typeof maybePromise.then === "function" ? await maybePromise : maybePromise;
|
|
144
156
|
if (!hbSingleton || typeof hbSingleton.createBuffer !== "function") {
|
|
145
157
|
throw new Error("Failed to initialize HarfBuzz: invalid API");
|
|
146
158
|
}
|
|
@@ -155,13 +167,32 @@ var FontRegistry = class {
|
|
|
155
167
|
__publicField(this, "fonts", /* @__PURE__ */ new Map());
|
|
156
168
|
__publicField(this, "blobs", /* @__PURE__ */ new Map());
|
|
157
169
|
__publicField(this, "wasmBaseURL");
|
|
170
|
+
__publicField(this, "initPromise");
|
|
158
171
|
this.wasmBaseURL = wasmBaseURL;
|
|
159
172
|
}
|
|
160
173
|
async init() {
|
|
161
|
-
if (
|
|
174
|
+
if (this.initPromise) {
|
|
175
|
+
await this.initPromise;
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (this.hb) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
this.initPromise = this._doInit();
|
|
182
|
+
await this.initPromise;
|
|
162
183
|
}
|
|
163
|
-
|
|
164
|
-
|
|
184
|
+
async _doInit() {
|
|
185
|
+
try {
|
|
186
|
+
this.hb = await initHB(this.wasmBaseURL);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
this.initPromise = void 0;
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
async getHB() {
|
|
193
|
+
if (!this.hb) {
|
|
194
|
+
await this.init();
|
|
195
|
+
}
|
|
165
196
|
return this.hb;
|
|
166
197
|
}
|
|
167
198
|
key(desc) {
|
|
@@ -180,23 +211,24 @@ var FontRegistry = class {
|
|
|
180
211
|
this.faces.set(k, face);
|
|
181
212
|
this.fonts.set(k, font);
|
|
182
213
|
}
|
|
183
|
-
getFont(desc) {
|
|
214
|
+
async getFont(desc) {
|
|
215
|
+
if (!this.hb) await this.init();
|
|
184
216
|
const k = this.key(desc);
|
|
185
217
|
const f = this.fonts.get(k);
|
|
186
218
|
if (!f) throw new Error(`Font not registered for ${k}`);
|
|
187
219
|
return f;
|
|
188
220
|
}
|
|
189
|
-
getFace(desc) {
|
|
221
|
+
async getFace(desc) {
|
|
222
|
+
if (!this.hb) await this.init();
|
|
190
223
|
const k = this.key(desc);
|
|
191
224
|
return this.faces.get(k);
|
|
192
225
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const face = this.getFace(desc);
|
|
226
|
+
async getUnitsPerEm(desc) {
|
|
227
|
+
const face = await this.getFace(desc);
|
|
196
228
|
return face?.upem || 1e3;
|
|
197
229
|
}
|
|
198
|
-
glyphPath(desc, glyphId) {
|
|
199
|
-
const font = this.getFont(desc);
|
|
230
|
+
async glyphPath(desc, glyphId) {
|
|
231
|
+
const font = await this.getFont(desc);
|
|
200
232
|
const path = font.glyphToPath(glyphId);
|
|
201
233
|
return path && path !== "" ? path : "M 0 0";
|
|
202
234
|
}
|
|
@@ -207,6 +239,8 @@ var FontRegistry = class {
|
|
|
207
239
|
this.fonts.clear();
|
|
208
240
|
this.faces.clear();
|
|
209
241
|
this.blobs.clear();
|
|
242
|
+
this.hb = void 0;
|
|
243
|
+
this.initPromise = void 0;
|
|
210
244
|
}
|
|
211
245
|
};
|
|
212
246
|
|
|
@@ -227,13 +261,13 @@ var LayoutEngine = class {
|
|
|
227
261
|
return t;
|
|
228
262
|
}
|
|
229
263
|
}
|
|
230
|
-
shapeFull(text, desc) {
|
|
231
|
-
const hb = this.fonts.getHB();
|
|
264
|
+
async shapeFull(text, desc) {
|
|
265
|
+
const hb = await this.fonts.getHB();
|
|
232
266
|
const buffer = hb.createBuffer();
|
|
233
267
|
buffer.addText(text);
|
|
234
268
|
buffer.guessSegmentProperties();
|
|
235
|
-
const font = this.fonts.getFont(desc);
|
|
236
|
-
const face = this.fonts.getFace(desc);
|
|
269
|
+
const font = await this.fonts.getFont(desc);
|
|
270
|
+
const face = await this.fonts.getFace(desc);
|
|
237
271
|
const upem = face?.upem || 1e3;
|
|
238
272
|
font.setScale(upem, upem);
|
|
239
273
|
hb.shape(font, buffer);
|
|
@@ -241,17 +275,17 @@ var LayoutEngine = class {
|
|
|
241
275
|
buffer.destroy();
|
|
242
276
|
return result;
|
|
243
277
|
}
|
|
244
|
-
layout(params) {
|
|
278
|
+
async layout(params) {
|
|
245
279
|
const { textTransform, desc, fontSize, letterSpacing, width } = params;
|
|
246
280
|
const input = this.transformText(params.text, textTransform);
|
|
247
281
|
if (!input || input.length === 0) {
|
|
248
282
|
return [];
|
|
249
283
|
}
|
|
250
|
-
const shaped = this.shapeFull(input, desc);
|
|
251
|
-
const face = this.fonts.getFace(desc);
|
|
284
|
+
const shaped = await this.shapeFull(input, desc);
|
|
285
|
+
const face = await this.fonts.getFace(desc);
|
|
252
286
|
const upem = face?.upem || 1e3;
|
|
253
287
|
const scale = fontSize / upem;
|
|
254
|
-
const glyphs = shaped.map((g
|
|
288
|
+
const glyphs = shaped.map((g) => {
|
|
255
289
|
const charIndex = g.cl;
|
|
256
290
|
let char;
|
|
257
291
|
if (charIndex >= 0 && charIndex < input.length) {
|
|
@@ -355,7 +389,7 @@ function decorationGeometry(kind, p) {
|
|
|
355
389
|
}
|
|
356
390
|
|
|
357
391
|
// src/core/drawops.ts
|
|
358
|
-
function buildDrawOps(p) {
|
|
392
|
+
async function buildDrawOps(p) {
|
|
359
393
|
const ops = [];
|
|
360
394
|
ops.push({
|
|
361
395
|
op: "BeginFrame",
|
|
@@ -366,7 +400,7 @@ function buildDrawOps(p) {
|
|
|
366
400
|
bg: p.background ? { color: p.background.color, opacity: p.background.opacity, radius: p.background.borderRadius } : void 0
|
|
367
401
|
});
|
|
368
402
|
if (p.lines.length === 0) return ops;
|
|
369
|
-
const upem = Math.max(1, p.getUnitsPerEm
|
|
403
|
+
const upem = Math.max(1, await p.getUnitsPerEm());
|
|
370
404
|
const scale = p.font.size / upem;
|
|
371
405
|
const blockHeight = p.lines[p.lines.length - 1].y;
|
|
372
406
|
let blockY;
|
|
@@ -402,7 +436,7 @@ function buildDrawOps(p) {
|
|
|
402
436
|
let xCursor = lineX;
|
|
403
437
|
const baselineY = blockY + line.y - p.font.size;
|
|
404
438
|
for (const glyph of line.glyphs) {
|
|
405
|
-
const path = p.glyphPathProvider(glyph.id);
|
|
439
|
+
const path = await p.glyphPathProvider(glyph.id);
|
|
406
440
|
if (!path || path === "M 0 0") {
|
|
407
441
|
xCursor += glyph.xAdvance;
|
|
408
442
|
continue;
|
|
@@ -1180,6 +1214,7 @@ async function createTextEngine(opts = {}) {
|
|
|
1180
1214
|
const wasmBaseURL = opts.wasmBaseURL;
|
|
1181
1215
|
const fonts = new FontRegistry(wasmBaseURL);
|
|
1182
1216
|
const layout = new LayoutEngine(fonts);
|
|
1217
|
+
await fonts.init();
|
|
1183
1218
|
async function ensureFonts(asset) {
|
|
1184
1219
|
if (asset.customFonts) {
|
|
1185
1220
|
for (const cf of asset.customFonts) {
|
|
@@ -1226,7 +1261,7 @@ async function createTextEngine(opts = {}) {
|
|
|
1226
1261
|
async renderFrame(asset, tSeconds) {
|
|
1227
1262
|
const main = await ensureFonts(asset);
|
|
1228
1263
|
const desc = { family: main.family, weight: `${main.weight}`, style: main.style };
|
|
1229
|
-
const lines = layout.layout({
|
|
1264
|
+
const lines = await layout.layout({
|
|
1230
1265
|
text: asset.text,
|
|
1231
1266
|
width: asset.width ?? width,
|
|
1232
1267
|
letterSpacing: asset.style?.letterSpacing ?? 0,
|
|
@@ -1236,7 +1271,7 @@ async function createTextEngine(opts = {}) {
|
|
|
1236
1271
|
textTransform: asset.style?.textTransform ?? "none"
|
|
1237
1272
|
});
|
|
1238
1273
|
const textRect = { x: 0, y: 0, width: asset.width ?? width, height: asset.height ?? height };
|
|
1239
|
-
const ops0 = buildDrawOps({
|
|
1274
|
+
const ops0 = await buildDrawOps({
|
|
1240
1275
|
canvas: { width, height, pixelRatio },
|
|
1241
1276
|
textRect,
|
|
1242
1277
|
lines,
|
|
@@ -1258,7 +1293,6 @@ async function createTextEngine(opts = {}) {
|
|
|
1258
1293
|
align: asset.align ?? { horizontal: "left", vertical: "middle" },
|
|
1259
1294
|
background: asset.background,
|
|
1260
1295
|
glyphPathProvider: (gid) => fonts.glyphPath(desc, gid),
|
|
1261
|
-
/** NEW: provide UPEM so drawops can compute scale */
|
|
1262
1296
|
getUnitsPerEm: () => fonts.getUnitsPerEm(desc)
|
|
1263
1297
|
});
|
|
1264
1298
|
const ops = applyAnimation(ops0, lines, {
|
|
@@ -1276,6 +1310,9 @@ async function createTextEngine(opts = {}) {
|
|
|
1276
1310
|
},
|
|
1277
1311
|
createRenderer(canvas) {
|
|
1278
1312
|
return createWebPainter(canvas);
|
|
1313
|
+
},
|
|
1314
|
+
destroy() {
|
|
1315
|
+
fonts.destroy();
|
|
1279
1316
|
}
|
|
1280
1317
|
};
|
|
1281
1318
|
}
|