@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/entry.web.js CHANGED
@@ -1,6 +1,6 @@
1
- var __defProp = Object.defineProperty;
2
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
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
- const mod = await import("harfbuzzjs");
300
- let hb;
301
- const candidate = mod.default || mod;
302
- if (typeof candidate === "function") {
303
- hb = await candidate({
304
- wasmBinary
305
- });
306
- } else if (candidate && typeof candidate.then === "function") {
307
- hb = await candidate;
308
- } else if (candidate && typeof candidate.createBuffer === "function") {
309
- hb = candidate;
310
- } else {
311
- throw new Error(`Unexpected harfbuzzjs export type: ${typeof candidate}`);
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: p.background && p.background.color ? { color: p.background.color, opacity: p.background.opacity, radius: p.background.borderRadius } : void 0
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 = 0;
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
- ops.push({
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
- ops.push({
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
- ops.push({
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
- ops.push({
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 ops) {
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
  });