@shotstack/shotstack-canvas 1.4.6 → 1.5.0
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/chunk-HYGMWVDX.js +19 -0
- package/dist/entry.node.cjs +154 -30
- package/dist/entry.node.d.cts +31 -0
- package/dist/entry.node.d.ts +31 -0
- package/dist/entry.node.js +154 -30
- package/dist/entry.web.d.ts +31 -0
- package/dist/entry.web.js +161 -33
- package/dist/hb-KXF2MJ2J.js +550 -0
- package/dist/hbjs-ZTRARROF.js +499 -0
- package/package.json +62 -62
package/dist/entry.web.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
__publicField
|
|
3
|
+
} from "./chunk-HYGMWVDX.js";
|
|
4
4
|
|
|
5
5
|
// src/schema/asset-schema.ts
|
|
6
6
|
import Joi from "joi";
|
|
@@ -98,11 +98,26 @@ var animationSchema = Joi.object({
|
|
|
98
98
|
otherwise: Joi.forbidden()
|
|
99
99
|
})
|
|
100
100
|
}).unknown(false);
|
|
101
|
+
var borderSchema = Joi.object({
|
|
102
|
+
width: Joi.number().min(0).default(0),
|
|
103
|
+
color: Joi.string().pattern(HEX6).default("#000000"),
|
|
104
|
+
opacity: Joi.number().min(0).max(1).default(1)
|
|
105
|
+
}).unknown(false);
|
|
101
106
|
var backgroundSchema = Joi.object({
|
|
102
107
|
color: Joi.string().pattern(HEX6).optional(),
|
|
103
108
|
opacity: Joi.number().min(0).max(1).default(1),
|
|
104
|
-
borderRadius: Joi.number().min(0).default(0)
|
|
109
|
+
borderRadius: Joi.number().min(0).default(0),
|
|
110
|
+
border: borderSchema.optional()
|
|
105
111
|
}).unknown(false);
|
|
112
|
+
var paddingSchema = Joi.alternatives().try(
|
|
113
|
+
Joi.number().min(0).default(0),
|
|
114
|
+
Joi.object({
|
|
115
|
+
top: Joi.number().min(0).default(0),
|
|
116
|
+
right: Joi.number().min(0).default(0),
|
|
117
|
+
bottom: Joi.number().min(0).default(0),
|
|
118
|
+
left: Joi.number().min(0).default(0)
|
|
119
|
+
}).unknown(false)
|
|
120
|
+
);
|
|
106
121
|
var customFontSchema = Joi.object({
|
|
107
122
|
src: Joi.string().uri().required(),
|
|
108
123
|
family: Joi.string().required(),
|
|
@@ -120,6 +135,7 @@ var RichTextAssetSchema = Joi.object({
|
|
|
120
135
|
stroke: strokeSchema.optional(),
|
|
121
136
|
shadow: shadowSchema.optional(),
|
|
122
137
|
background: backgroundSchema.optional(),
|
|
138
|
+
padding: paddingSchema.optional(),
|
|
123
139
|
align: alignmentSchema.optional(),
|
|
124
140
|
animation: animationSchema.optional(),
|
|
125
141
|
customFonts: Joi.array().items(customFontSchema).optional(),
|
|
@@ -258,6 +274,23 @@ function setupWasmInterceptors(wasmBinary) {
|
|
|
258
274
|
}
|
|
259
275
|
return originalFetch.apply(this, [input, init]);
|
|
260
276
|
};
|
|
277
|
+
const originalInstantiate = WebAssembly.instantiate;
|
|
278
|
+
WebAssembly.instantiate = async function(bufferSourceOrModule, importObject) {
|
|
279
|
+
console.log(
|
|
280
|
+
`\u{1F504} WebAssembly.instantiate called, type: ${bufferSourceOrModule instanceof WebAssembly.Module ? "Module" : "BufferSource"}`
|
|
281
|
+
);
|
|
282
|
+
if (bufferSourceOrModule instanceof WebAssembly.Module) {
|
|
283
|
+
return originalInstantiate.call(WebAssembly, bufferSourceOrModule, importObject);
|
|
284
|
+
}
|
|
285
|
+
console.log(`\u{1F504} Intercepted WebAssembly.instantiate, using pre-loaded WASM binary`);
|
|
286
|
+
const module = await WebAssembly.compile(wasmBinary);
|
|
287
|
+
const instance = await originalInstantiate.call(
|
|
288
|
+
WebAssembly,
|
|
289
|
+
module,
|
|
290
|
+
importObject
|
|
291
|
+
);
|
|
292
|
+
return { module, instance };
|
|
293
|
+
};
|
|
261
294
|
const originalInstantiateStreaming = WebAssembly.instantiateStreaming;
|
|
262
295
|
if (originalInstantiateStreaming) {
|
|
263
296
|
WebAssembly.instantiateStreaming = async function(source, importObject) {
|
|
@@ -295,21 +328,25 @@ async function initHB(wasmBaseURL) {
|
|
|
295
328
|
console.log(`\u2705 WASM binary loaded successfully (${wasmBinary.byteLength} bytes)`);
|
|
296
329
|
if (!isNode()) {
|
|
297
330
|
setupWasmInterceptors(wasmBinary);
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
}
|
|
331
|
+
window.Module = {
|
|
332
|
+
wasmBinary,
|
|
333
|
+
locateFile: (path) => {
|
|
334
|
+
console.log(`\u{1F50D} locateFile called for: ${path}`);
|
|
335
|
+
return path;
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
console.log(`\u{1F30D} Set global Module.wasmBinary (${wasmBinary.byteLength} bytes)`);
|
|
339
|
+
}
|
|
340
|
+
console.log("\u{1F504} Importing harfbuzzjs/hb.js (factory)");
|
|
341
|
+
const hbModule = await import("./hb-KXF2MJ2J.js");
|
|
342
|
+
const hbFactory = hbModule.default || hbModule;
|
|
343
|
+
console.log("\u{1F504} Calling hb factory with wasmBinary");
|
|
344
|
+
const hbInstance = await hbFactory({ wasmBinary });
|
|
345
|
+
console.log("\u{1F504} Importing harfbuzzjs/hbjs.js (wrapper)");
|
|
346
|
+
const hbjsModule = await import("./hbjs-ZTRARROF.js");
|
|
347
|
+
const hbjsWrapper = hbjsModule.default || hbjsModule;
|
|
348
|
+
console.log("\u{1F504} Wrapping hb instance");
|
|
349
|
+
const hb = hbjsWrapper(hbInstance);
|
|
313
350
|
if (!hb || typeof hb.createBuffer !== "function" || typeof hb.createFont !== "function") {
|
|
314
351
|
throw new Error("Failed to initialize HarfBuzz: unexpected export shape from 'harfbuzzjs'.");
|
|
315
352
|
}
|
|
@@ -773,15 +810,27 @@ function decorationGeometry(kind, p) {
|
|
|
773
810
|
}
|
|
774
811
|
|
|
775
812
|
// src/core/drawops.ts
|
|
813
|
+
function normalizePadding(padding) {
|
|
814
|
+
if (padding === void 0 || padding === null) {
|
|
815
|
+
return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
816
|
+
}
|
|
817
|
+
if (typeof padding === "number") {
|
|
818
|
+
return { top: padding, right: padding, bottom: padding, left: padding };
|
|
819
|
+
}
|
|
820
|
+
return padding;
|
|
821
|
+
}
|
|
776
822
|
async function buildDrawOps(p) {
|
|
777
823
|
const ops = [];
|
|
824
|
+
const padding = normalizePadding(p.padding);
|
|
825
|
+
const borderWidth = p.background?.border?.width ?? 0;
|
|
778
826
|
ops.push({
|
|
779
827
|
op: "BeginFrame",
|
|
780
828
|
width: p.canvas.width,
|
|
781
829
|
height: p.canvas.height,
|
|
782
830
|
pixelRatio: p.canvas.pixelRatio,
|
|
783
831
|
clear: true,
|
|
784
|
-
bg:
|
|
832
|
+
bg: void 0
|
|
833
|
+
// Background will be drawn as a separate layer with proper padding/border
|
|
785
834
|
});
|
|
786
835
|
if (p.lines.length === 0) return ops;
|
|
787
836
|
const upem = Math.max(1, await p.getUnitsPerEm());
|
|
@@ -791,33 +840,34 @@ async function buildDrawOps(p) {
|
|
|
791
840
|
let blockY;
|
|
792
841
|
switch (p.align.vertical) {
|
|
793
842
|
case "top":
|
|
794
|
-
blockY = p.font.size;
|
|
843
|
+
blockY = p.font.size + padding.top;
|
|
795
844
|
break;
|
|
796
845
|
case "bottom":
|
|
797
|
-
blockY = p.textRect.height - (numLines - 1) * lineHeightPx;
|
|
846
|
+
blockY = p.textRect.height - (numLines - 1) * lineHeightPx + padding.top;
|
|
798
847
|
break;
|
|
799
848
|
case "middle":
|
|
800
849
|
default:
|
|
801
850
|
const capHeightRatio = 0.35;
|
|
802
851
|
const visualOffset = p.font.size * capHeightRatio;
|
|
803
|
-
blockY = (p.textRect.height - (numLines - 1) * lineHeightPx) / 2 + visualOffset;
|
|
852
|
+
blockY = (p.textRect.height - (numLines - 1) * lineHeightPx) / 2 + visualOffset + padding.top;
|
|
804
853
|
break;
|
|
805
854
|
}
|
|
806
855
|
const fill = p.style.gradient ? gradientSpecFrom(p.style.gradient, 1) : { kind: "solid", color: p.font.color, opacity: p.font.opacity };
|
|
807
856
|
const decoColor = p.style.gradient ? p.style.gradient.stops[p.style.gradient.stops.length - 1]?.color ?? p.font.color : p.font.color;
|
|
857
|
+
const textOps = [];
|
|
808
858
|
let gMinX = Infinity, gMinY = Infinity, gMaxX = -Infinity, gMaxY = -Infinity;
|
|
809
859
|
for (const line of p.lines) {
|
|
810
860
|
let lineX;
|
|
811
861
|
switch (p.align.horizontal) {
|
|
812
862
|
case "left":
|
|
813
|
-
lineX =
|
|
863
|
+
lineX = padding.left;
|
|
814
864
|
break;
|
|
815
865
|
case "right":
|
|
816
|
-
lineX = p.textRect.width - line.width;
|
|
866
|
+
lineX = p.textRect.width - line.width + padding.left;
|
|
817
867
|
break;
|
|
818
868
|
case "center":
|
|
819
869
|
default:
|
|
820
|
-
lineX = (p.textRect.width - line.width) / 2;
|
|
870
|
+
lineX = (p.textRect.width - line.width) / 2 + padding.left;
|
|
821
871
|
break;
|
|
822
872
|
}
|
|
823
873
|
let xCursor = lineX;
|
|
@@ -841,7 +891,7 @@ async function buildDrawOps(p) {
|
|
|
841
891
|
if (x2 > gMaxX) gMaxX = x2;
|
|
842
892
|
if (y2 > gMaxY) gMaxY = y2;
|
|
843
893
|
if (p.shadow && p.shadow.blur > 0) {
|
|
844
|
-
|
|
894
|
+
textOps.push({
|
|
845
895
|
isShadow: true,
|
|
846
896
|
op: "FillPath",
|
|
847
897
|
path,
|
|
@@ -852,7 +902,7 @@ async function buildDrawOps(p) {
|
|
|
852
902
|
});
|
|
853
903
|
}
|
|
854
904
|
if (p.stroke && p.stroke.width > 0) {
|
|
855
|
-
|
|
905
|
+
textOps.push({
|
|
856
906
|
op: "StrokePath",
|
|
857
907
|
path,
|
|
858
908
|
x: glyphX,
|
|
@@ -863,7 +913,7 @@ async function buildDrawOps(p) {
|
|
|
863
913
|
opacity: p.stroke.opacity
|
|
864
914
|
});
|
|
865
915
|
}
|
|
866
|
-
|
|
916
|
+
textOps.push({
|
|
867
917
|
op: "FillPath",
|
|
868
918
|
path,
|
|
869
919
|
x: glyphX,
|
|
@@ -880,7 +930,7 @@ async function buildDrawOps(p) {
|
|
|
880
930
|
lineWidth: line.width,
|
|
881
931
|
xStart: lineX
|
|
882
932
|
});
|
|
883
|
-
|
|
933
|
+
textOps.push({
|
|
884
934
|
op: "DecorationLine",
|
|
885
935
|
from: { x: deco.x1, y: deco.y },
|
|
886
936
|
to: { x: deco.x2, y: deco.y },
|
|
@@ -892,12 +942,46 @@ async function buildDrawOps(p) {
|
|
|
892
942
|
}
|
|
893
943
|
if (gMinX !== Infinity) {
|
|
894
944
|
const gbox = { x: gMinX, y: gMinY, w: Math.max(1, gMaxX - gMinX), h: Math.max(1, gMaxY - gMinY) };
|
|
895
|
-
for (const op of
|
|
945
|
+
for (const op of textOps) {
|
|
896
946
|
if (op.op === "FillPath" && !op.isShadow) {
|
|
897
947
|
op.gradientBBox = gbox;
|
|
898
948
|
}
|
|
899
949
|
}
|
|
900
950
|
}
|
|
951
|
+
if (p.background && (p.background.color || p.background.border)) {
|
|
952
|
+
const bgX = 0;
|
|
953
|
+
const bgY = 0;
|
|
954
|
+
const bgWidth = p.canvas.width;
|
|
955
|
+
const bgHeight = p.canvas.height;
|
|
956
|
+
if (p.background.color) {
|
|
957
|
+
ops.push({
|
|
958
|
+
op: "Rectangle",
|
|
959
|
+
x: bgX,
|
|
960
|
+
y: bgY,
|
|
961
|
+
width: bgWidth,
|
|
962
|
+
height: bgHeight,
|
|
963
|
+
fill: { kind: "solid", color: p.background.color, opacity: p.background.opacity },
|
|
964
|
+
borderRadius: p.background.borderRadius
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
if (p.background.border && p.background.border.width > 0) {
|
|
968
|
+
const halfBorder = p.background.border.width / 2;
|
|
969
|
+
ops.push({
|
|
970
|
+
op: "RectangleStroke",
|
|
971
|
+
x: bgX + halfBorder,
|
|
972
|
+
y: bgY + halfBorder,
|
|
973
|
+
width: bgWidth - p.background.border.width,
|
|
974
|
+
height: bgHeight - p.background.border.width,
|
|
975
|
+
stroke: {
|
|
976
|
+
width: p.background.border.width,
|
|
977
|
+
color: p.background.border.color,
|
|
978
|
+
opacity: p.background.border.opacity
|
|
979
|
+
},
|
|
980
|
+
borderRadius: Math.max(0, p.background.borderRadius - halfBorder)
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
ops.push(...textOps);
|
|
901
985
|
return ops;
|
|
902
986
|
}
|
|
903
987
|
function tokenizePath(d) {
|
|
@@ -1655,6 +1739,48 @@ function createWebPainter(canvas) {
|
|
|
1655
1739
|
ctx.restore();
|
|
1656
1740
|
continue;
|
|
1657
1741
|
}
|
|
1742
|
+
if (op.op === "Rectangle") {
|
|
1743
|
+
ctx.save();
|
|
1744
|
+
const fill = makeGradientFromBBox(ctx, op.fill, {
|
|
1745
|
+
x: op.x,
|
|
1746
|
+
y: op.y,
|
|
1747
|
+
w: op.width,
|
|
1748
|
+
h: op.height
|
|
1749
|
+
});
|
|
1750
|
+
ctx.fillStyle = fill;
|
|
1751
|
+
if (op.borderRadius && op.borderRadius > 0) {
|
|
1752
|
+
drawRoundedRect(ctx, op.x, op.y, op.width, op.height, op.borderRadius);
|
|
1753
|
+
} else {
|
|
1754
|
+
ctx.fillRect(op.x, op.y, op.width, op.height);
|
|
1755
|
+
}
|
|
1756
|
+
ctx.restore();
|
|
1757
|
+
continue;
|
|
1758
|
+
}
|
|
1759
|
+
if (op.op === "RectangleStroke") {
|
|
1760
|
+
ctx.save();
|
|
1761
|
+
const c = parseHex6(op.stroke.color, op.stroke.opacity);
|
|
1762
|
+
ctx.strokeStyle = `rgba(${c.r},${c.g},${c.b},${c.a})`;
|
|
1763
|
+
ctx.lineWidth = op.stroke.width;
|
|
1764
|
+
if (op.borderRadius && op.borderRadius > 0) {
|
|
1765
|
+
const p = new Path2D();
|
|
1766
|
+
const r = op.borderRadius;
|
|
1767
|
+
const x = op.x;
|
|
1768
|
+
const y = op.y;
|
|
1769
|
+
const w = op.width;
|
|
1770
|
+
const h = op.height;
|
|
1771
|
+
p.moveTo(x + r, y);
|
|
1772
|
+
p.arcTo(x + w, y, x + w, y + h, r);
|
|
1773
|
+
p.arcTo(x + w, y + h, x, y + h, r);
|
|
1774
|
+
p.arcTo(x, y + h, x, y, r);
|
|
1775
|
+
p.arcTo(x, y, x + w, y, r);
|
|
1776
|
+
p.closePath();
|
|
1777
|
+
ctx.stroke(p);
|
|
1778
|
+
} else {
|
|
1779
|
+
ctx.strokeRect(op.x, op.y, op.width, op.height);
|
|
1780
|
+
}
|
|
1781
|
+
ctx.restore();
|
|
1782
|
+
continue;
|
|
1783
|
+
}
|
|
1658
1784
|
}
|
|
1659
1785
|
}
|
|
1660
1786
|
};
|
|
@@ -1984,14 +2110,15 @@ async function createTextEngine(opts = {}) {
|
|
|
1984
2110
|
`Failed to layout text: ${err instanceof Error ? err.message : String(err)}`
|
|
1985
2111
|
);
|
|
1986
2112
|
}
|
|
2113
|
+
const padding = asset.padding ? typeof asset.padding === "number" ? { top: asset.padding, right: asset.padding, bottom: asset.padding, left: asset.padding } : asset.padding : { top: 0, right: 0, bottom: 0, left: 0 };
|
|
1987
2114
|
const textRect = {
|
|
1988
2115
|
x: 0,
|
|
1989
2116
|
y: 0,
|
|
1990
2117
|
width: asset.width ?? width,
|
|
1991
2118
|
height: asset.height ?? height
|
|
1992
2119
|
};
|
|
1993
|
-
const canvasW = asset.width ?? width;
|
|
1994
|
-
const canvasH = asset.height ?? height;
|
|
2120
|
+
const canvasW = (asset.width ?? width) + padding.left + padding.right;
|
|
2121
|
+
const canvasH = (asset.height ?? height) + padding.top + padding.bottom;
|
|
1995
2122
|
const canvasPR = asset.pixelRatio ?? pixelRatio;
|
|
1996
2123
|
let ops0;
|
|
1997
2124
|
try {
|
|
@@ -2018,6 +2145,7 @@ async function createTextEngine(opts = {}) {
|
|
|
2018
2145
|
vertical: asset.align?.vertical ?? "middle"
|
|
2019
2146
|
},
|
|
2020
2147
|
background: asset.background,
|
|
2148
|
+
padding: asset.padding,
|
|
2021
2149
|
glyphPathProvider: (gid, fontDesc) => fonts.glyphPath(fontDesc || desc, gid),
|
|
2022
2150
|
getUnitsPerEm: (fontDesc) => fonts.getUnitsPerEm(fontDesc || desc)
|
|
2023
2151
|
});
|