@shotstack/shotstack-canvas 1.1.1 → 1.1.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/entry.node.cjs +43 -9
- package/dist/entry.node.cjs.map +1 -1
- package/dist/entry.node.js +43 -9
- package/dist/entry.node.js.map +1 -1
- package/dist/entry.web.js +81 -13
- package/dist/entry.web.js.map +1 -1
- package/package.json +1 -1
package/dist/entry.web.js
CHANGED
|
@@ -157,7 +157,7 @@ async function initHB(wasmBaseURL) {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
// src/core/font-registry.ts
|
|
160
|
-
var
|
|
160
|
+
var _FontRegistry = class _FontRegistry {
|
|
161
161
|
constructor(wasmBaseURL) {
|
|
162
162
|
__publicField(this, "hb");
|
|
163
163
|
__publicField(this, "faces", /* @__PURE__ */ new Map());
|
|
@@ -167,14 +167,15 @@ var FontRegistry = class {
|
|
|
167
167
|
__publicField(this, "initPromise");
|
|
168
168
|
this.wasmBaseURL = wasmBaseURL;
|
|
169
169
|
}
|
|
170
|
+
static setFallbackLoader(loader) {
|
|
171
|
+
_FontRegistry.fallbackLoader = loader;
|
|
172
|
+
}
|
|
170
173
|
async init() {
|
|
171
174
|
if (this.initPromise) {
|
|
172
175
|
await this.initPromise;
|
|
173
176
|
return;
|
|
174
177
|
}
|
|
175
|
-
if (this.hb)
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
+
if (this.hb) return;
|
|
178
179
|
this.initPromise = this._doInit();
|
|
179
180
|
try {
|
|
180
181
|
await this.initPromise;
|
|
@@ -226,11 +227,35 @@ var FontRegistry = class {
|
|
|
226
227
|
);
|
|
227
228
|
}
|
|
228
229
|
}
|
|
230
|
+
async tryFallbackInstall(desc) {
|
|
231
|
+
const loader = _FontRegistry.fallbackLoader;
|
|
232
|
+
if (!loader) return false;
|
|
233
|
+
try {
|
|
234
|
+
const bytes = await loader({
|
|
235
|
+
family: desc.family,
|
|
236
|
+
weight: desc.weight ?? "400",
|
|
237
|
+
style: desc.style ?? "normal"
|
|
238
|
+
});
|
|
239
|
+
if (!bytes) return false;
|
|
240
|
+
await this.registerFromBytes(bytes, {
|
|
241
|
+
family: desc.family,
|
|
242
|
+
weight: desc.weight ?? "400",
|
|
243
|
+
style: desc.style ?? "normal"
|
|
244
|
+
});
|
|
245
|
+
return true;
|
|
246
|
+
} catch {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
229
250
|
async getFont(desc) {
|
|
230
251
|
try {
|
|
231
252
|
if (!this.hb) await this.init();
|
|
232
253
|
const k = this.key(desc);
|
|
233
|
-
|
|
254
|
+
let f = this.fonts.get(k);
|
|
255
|
+
if (!f) {
|
|
256
|
+
const installed = await this.tryFallbackInstall(desc);
|
|
257
|
+
f = installed ? this.fonts.get(k) : void 0;
|
|
258
|
+
}
|
|
234
259
|
if (!f) throw new Error(`Font not registered for ${k}`);
|
|
235
260
|
return f;
|
|
236
261
|
} catch (err) {
|
|
@@ -246,7 +271,12 @@ var FontRegistry = class {
|
|
|
246
271
|
try {
|
|
247
272
|
if (!this.hb) await this.init();
|
|
248
273
|
const k = this.key(desc);
|
|
249
|
-
|
|
274
|
+
let face = this.faces.get(k);
|
|
275
|
+
if (!face) {
|
|
276
|
+
const installed = await this.tryFallbackInstall(desc);
|
|
277
|
+
face = installed ? this.faces.get(k) : void 0;
|
|
278
|
+
}
|
|
279
|
+
return face;
|
|
250
280
|
} catch (err) {
|
|
251
281
|
throw new Error(
|
|
252
282
|
`Failed to get face for "${desc.family}": ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -315,6 +345,8 @@ var FontRegistry = class {
|
|
|
315
345
|
}
|
|
316
346
|
}
|
|
317
347
|
};
|
|
348
|
+
__publicField(_FontRegistry, "fallbackLoader");
|
|
349
|
+
var FontRegistry = _FontRegistry;
|
|
318
350
|
|
|
319
351
|
// src/core/layout.ts
|
|
320
352
|
var LayoutEngine = class {
|
|
@@ -502,7 +534,7 @@ async function buildDrawOps(p) {
|
|
|
502
534
|
height: p.canvas.height,
|
|
503
535
|
pixelRatio: p.canvas.pixelRatio,
|
|
504
536
|
clear: true,
|
|
505
|
-
bg: p.background ? { color: p.background.color, opacity: p.background.opacity, radius: p.background.borderRadius } : void 0
|
|
537
|
+
bg: p.background && p.background.color ? { color: p.background.color, opacity: p.background.opacity, radius: p.background.borderRadius } : void 0
|
|
506
538
|
});
|
|
507
539
|
if (p.lines.length === 0) return ops;
|
|
508
540
|
const upem = Math.max(1, await p.getUnitsPerEm());
|
|
@@ -1133,9 +1165,13 @@ function createWebPainter(canvas) {
|
|
|
1133
1165
|
canvas.width = Math.floor(w * dpr);
|
|
1134
1166
|
canvas.height = Math.floor(h * dpr);
|
|
1135
1167
|
}
|
|
1168
|
+
if ("style" in canvas) {
|
|
1169
|
+
canvas.style.width = `${w}px`;
|
|
1170
|
+
canvas.style.height = `${h}px`;
|
|
1171
|
+
}
|
|
1136
1172
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
1137
1173
|
if (op.clear) ctx.clearRect(0, 0, w, h);
|
|
1138
|
-
if (op.bg) {
|
|
1174
|
+
if (op.bg && op.bg.color) {
|
|
1139
1175
|
const { color, opacity, radius } = op.bg;
|
|
1140
1176
|
if (color) {
|
|
1141
1177
|
const c = parseHex6(color, opacity);
|
|
@@ -1365,6 +1401,7 @@ var isGlyphFill2 = (op) => {
|
|
|
1365
1401
|
};
|
|
1366
1402
|
|
|
1367
1403
|
// src/env/entry.web.ts
|
|
1404
|
+
var DEFAULT_ROBOTO_URL = "https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4mxP.ttf";
|
|
1368
1405
|
async function createTextEngine(opts = {}) {
|
|
1369
1406
|
const width = opts.width ?? CANVAS_CONFIG.DEFAULTS.width;
|
|
1370
1407
|
const height = opts.height ?? CANVAS_CONFIG.DEFAULTS.height;
|
|
@@ -1373,6 +1410,15 @@ async function createTextEngine(opts = {}) {
|
|
|
1373
1410
|
const fonts = new FontRegistry(wasmBaseURL);
|
|
1374
1411
|
const layout = new LayoutEngine(fonts);
|
|
1375
1412
|
try {
|
|
1413
|
+
FontRegistry.setFallbackLoader(async (desc) => {
|
|
1414
|
+
const family = (desc.family ?? "Roboto").toLowerCase();
|
|
1415
|
+
const weight = `${desc.weight ?? "400"}`;
|
|
1416
|
+
const style = desc.style ?? "normal";
|
|
1417
|
+
if (family === "roboto" && weight === "400" && style === "normal") {
|
|
1418
|
+
return fetchToArrayBuffer(DEFAULT_ROBOTO_URL);
|
|
1419
|
+
}
|
|
1420
|
+
return void 0;
|
|
1421
|
+
});
|
|
1376
1422
|
await fonts.init();
|
|
1377
1423
|
} catch (err) {
|
|
1378
1424
|
throw new Error(
|
|
@@ -1405,11 +1451,30 @@ async function createTextEngine(opts = {}) {
|
|
|
1405
1451
|
color: "#000000",
|
|
1406
1452
|
opacity: 1
|
|
1407
1453
|
};
|
|
1454
|
+
const desc = { family: main.family, weight: `${main.weight}`, style: main.style };
|
|
1455
|
+
const ensureFace = async () => {
|
|
1456
|
+
try {
|
|
1457
|
+
await fonts.getFace(desc);
|
|
1458
|
+
} catch {
|
|
1459
|
+
const wantsDefaultRoboto = (main.family || "Roboto").toLowerCase() === "roboto" && `${main.weight}` === "400" && main.style === "normal";
|
|
1460
|
+
if (wantsDefaultRoboto) {
|
|
1461
|
+
const bytes = await fetchToArrayBuffer(DEFAULT_ROBOTO_URL);
|
|
1462
|
+
await fonts.registerFromBytes(bytes, {
|
|
1463
|
+
family: "Roboto",
|
|
1464
|
+
weight: "400",
|
|
1465
|
+
style: "normal"
|
|
1466
|
+
});
|
|
1467
|
+
} else {
|
|
1468
|
+
throw new Error(
|
|
1469
|
+
`Font not registered for ${desc.family}__${desc.weight}__${desc.style}`
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
};
|
|
1474
|
+
await ensureFace();
|
|
1408
1475
|
return main;
|
|
1409
1476
|
} catch (err) {
|
|
1410
|
-
if (err instanceof Error)
|
|
1411
|
-
throw err;
|
|
1412
|
-
}
|
|
1477
|
+
if (err instanceof Error) throw err;
|
|
1413
1478
|
throw new Error(`Failed to ensure fonts: ${String(err)}`);
|
|
1414
1479
|
}
|
|
1415
1480
|
}
|
|
@@ -1495,10 +1560,13 @@ async function createTextEngine(opts = {}) {
|
|
|
1495
1560
|
width: asset.width ?? width,
|
|
1496
1561
|
height: asset.height ?? height
|
|
1497
1562
|
};
|
|
1563
|
+
const canvasW = asset.width ?? width;
|
|
1564
|
+
const canvasH = asset.height ?? height;
|
|
1565
|
+
const canvasPR = asset.pixelRatio ?? pixelRatio;
|
|
1498
1566
|
let ops0;
|
|
1499
1567
|
try {
|
|
1500
1568
|
ops0 = await buildDrawOps({
|
|
1501
|
-
canvas: { width, height, pixelRatio },
|
|
1569
|
+
canvas: { width: canvasW, height: canvasH, pixelRatio: canvasPR },
|
|
1502
1570
|
textRect,
|
|
1503
1571
|
lines,
|
|
1504
1572
|
font: {
|
|
@@ -1516,7 +1584,7 @@ async function createTextEngine(opts = {}) {
|
|
|
1516
1584
|
},
|
|
1517
1585
|
stroke: asset.stroke,
|
|
1518
1586
|
shadow: asset.shadow,
|
|
1519
|
-
align: asset.align ?? { horizontal: "
|
|
1587
|
+
align: asset.align ?? { horizontal: "center", vertical: "middle" },
|
|
1520
1588
|
background: asset.background,
|
|
1521
1589
|
glyphPathProvider: (gid) => fonts.glyphPath(desc, gid),
|
|
1522
1590
|
getUnitsPerEm: () => fonts.getUnitsPerEm(desc)
|