@shotstack/shotstack-canvas 1.5.4 → 1.5.6

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.
@@ -135,13 +135,12 @@ var animationSchema = import_joi.default.object({
135
135
  var borderSchema = import_joi.default.object({
136
136
  width: import_joi.default.number().min(0).default(0),
137
137
  color: import_joi.default.string().pattern(HEX6).default("#000000"),
138
- opacity: import_joi.default.number().min(0).max(1).default(1)
138
+ opacity: import_joi.default.number().min(0).max(1).default(1),
139
+ radius: import_joi.default.number().min(0).default(0)
139
140
  }).unknown(false);
140
141
  var backgroundSchema = import_joi.default.object({
141
142
  color: import_joi.default.string().pattern(HEX6).optional(),
142
- opacity: import_joi.default.number().min(0).max(1).default(1),
143
- borderRadius: import_joi.default.number().min(0).default(0),
144
- border: borderSchema.optional()
143
+ opacity: import_joi.default.number().min(0).max(1).default(1)
145
144
  }).unknown(false);
146
145
  var paddingSchema = import_joi.default.alternatives().try(
147
146
  import_joi.default.number().min(0).default(0),
@@ -169,6 +168,7 @@ var RichTextAssetSchema = import_joi.default.object({
169
168
  stroke: strokeSchema.optional(),
170
169
  shadow: shadowSchema.optional(),
171
170
  background: backgroundSchema.optional(),
171
+ border: borderSchema.optional(),
172
172
  padding: paddingSchema.optional(),
173
173
  align: alignmentSchema.optional(),
174
174
  animation: animationSchema.optional(),
@@ -980,54 +980,42 @@ async function buildDrawOps(p) {
980
980
  }
981
981
  }
982
982
  }
983
- if (p.background && (p.background.color || p.background.border)) {
983
+ if (p.background || p.border) {
984
984
  const bgX = 0;
985
985
  const bgY = 0;
986
986
  const bgWidth = p.canvas.width;
987
987
  const bgHeight = p.canvas.height;
988
- if (p.background.color) {
988
+ const borderWidth2 = p.border?.width ?? 0;
989
+ const borderRadius = p.border?.radius ?? 0;
990
+ const halfBorder = borderWidth2 / 2;
991
+ const maxRadius = Math.min(bgWidth - borderWidth2, bgHeight - borderWidth2) / 2;
992
+ const outerRadius = Math.min(borderRadius, maxRadius);
993
+ const innerRadius = Math.max(0, outerRadius - halfBorder);
994
+ if (p.background?.color) {
989
995
  ops.push({
990
996
  op: "Rectangle",
991
- x: bgX,
992
- y: bgY,
993
- width: bgWidth,
994
- height: bgHeight,
997
+ x: bgX + borderWidth2,
998
+ y: bgY + borderWidth2,
999
+ width: bgWidth - borderWidth2 * 2,
1000
+ height: bgHeight - borderWidth2 * 2,
995
1001
  fill: { kind: "solid", color: p.background.color, opacity: p.background.opacity },
996
- borderRadius: p.background.borderRadius
1002
+ borderRadius: innerRadius
997
1003
  });
998
1004
  }
999
- if (p.background.border && p.background.border.width > 0) {
1000
- const halfBorder = p.background.border.width / 2;
1001
- const borderRadius = p.background.borderRadius;
1002
- if (borderRadius <= halfBorder) {
1003
- ops.push({
1004
- op: "RectangleStroke",
1005
- x: bgX,
1006
- y: bgY,
1007
- width: bgWidth,
1008
- height: bgHeight,
1009
- stroke: {
1010
- width: p.background.border.width,
1011
- color: p.background.border.color,
1012
- opacity: p.background.border.opacity
1013
- },
1014
- borderRadius
1015
- });
1016
- } else {
1017
- ops.push({
1018
- op: "RectangleStroke",
1019
- x: bgX + halfBorder,
1020
- y: bgY + halfBorder,
1021
- width: bgWidth - p.background.border.width,
1022
- height: bgHeight - p.background.border.width,
1023
- stroke: {
1024
- width: p.background.border.width,
1025
- color: p.background.border.color,
1026
- opacity: p.background.border.opacity
1027
- },
1028
- borderRadius: borderRadius - halfBorder
1029
- });
1030
- }
1005
+ if (p.border && p.border.width > 0) {
1006
+ ops.push({
1007
+ op: "RectangleStroke",
1008
+ x: bgX + halfBorder,
1009
+ y: bgY + halfBorder,
1010
+ width: bgWidth - borderWidth2,
1011
+ height: bgHeight - borderWidth2,
1012
+ stroke: {
1013
+ width: p.border.width,
1014
+ color: p.border.color,
1015
+ opacity: p.border.opacity
1016
+ },
1017
+ borderRadius: outerRadius
1018
+ });
1031
1019
  }
1032
1020
  }
1033
1021
  ops.push(...textOps);
@@ -2083,11 +2071,13 @@ function computePathBounds2(d) {
2083
2071
  };
2084
2072
  }
2085
2073
  function roundRectPath(ctx, x, y, w, h, r) {
2086
- ctx.moveTo(x + r, y);
2087
- ctx.arcTo(x + w, y, x + w, y + h, r);
2088
- ctx.arcTo(x + w, y + h, x, y + h, r);
2089
- ctx.arcTo(x, y + h, x, y, r);
2090
- ctx.arcTo(x, y, x + w, y, r);
2074
+ const maxRadius = Math.min(w, h) / 2;
2075
+ const radius = Math.min(r, maxRadius);
2076
+ ctx.moveTo(x + radius, y);
2077
+ ctx.arcTo(x + w, y, x + w, y + h, radius);
2078
+ ctx.arcTo(x + w, y + h, x, y + h, radius);
2079
+ ctx.arcTo(x, y + h, x, y, radius);
2080
+ ctx.arcTo(x, y, x + w, y, radius);
2091
2081
  ctx.closePath();
2092
2082
  }
2093
2083
  function tokenizePath2(d) {
@@ -2476,6 +2466,7 @@ async function createTextEngine(opts = {}) {
2476
2466
  vertical: asset.align?.vertical ?? "middle"
2477
2467
  },
2478
2468
  background: asset.background,
2469
+ border: asset.border,
2479
2470
  padding: asset.padding,
2480
2471
  glyphPathProvider: (gid, fontDesc) => fonts.glyphPath(fontDesc || desc, gid),
2481
2472
  getUnitsPerEm: (fontDesc) => fonts.getUnitsPerEm(fontDesc || desc)
@@ -2528,7 +2519,7 @@ async function createTextEngine(opts = {}) {
2528
2519
  try {
2529
2520
  const hasBackground = !!asset.background?.color;
2530
2521
  const hasAnimation = !!asset.animation?.preset;
2531
- const hasBorderRadius = (asset.background?.borderRadius ?? 0) > 0;
2522
+ const hasBorderRadius = (asset.border?.radius ?? 0) > 0;
2532
2523
  const needsAlpha = !hasBackground && hasAnimation || hasBorderRadius;
2533
2524
  console.log(
2534
2525
  `\u{1F3A8} Video settings: Animation=${hasAnimation}, Background=${hasBackground}, BorderRadius=${hasBorderRadius}, Alpha=${needsAlpha}`
@@ -63,12 +63,12 @@ type RichTextValidated = Required<{
63
63
  background?: {
64
64
  color?: string;
65
65
  opacity: number;
66
- borderRadius: number;
67
- border?: {
68
- width: number;
69
- color: string;
70
- opacity: number;
71
- };
66
+ };
67
+ border?: {
68
+ width: number;
69
+ color: string;
70
+ opacity: number;
71
+ radius: number;
72
72
  };
73
73
  padding?: number | {
74
74
  top: number;
@@ -63,12 +63,12 @@ type RichTextValidated = Required<{
63
63
  background?: {
64
64
  color?: string;
65
65
  opacity: number;
66
- borderRadius: number;
67
- border?: {
68
- width: number;
69
- color: string;
70
- opacity: number;
71
- };
66
+ };
67
+ border?: {
68
+ width: number;
69
+ color: string;
70
+ opacity: number;
71
+ radius: number;
72
72
  };
73
73
  padding?: number | {
74
74
  top: number;
@@ -97,13 +97,12 @@ var animationSchema = Joi.object({
97
97
  var borderSchema = Joi.object({
98
98
  width: Joi.number().min(0).default(0),
99
99
  color: Joi.string().pattern(HEX6).default("#000000"),
100
- opacity: Joi.number().min(0).max(1).default(1)
100
+ opacity: Joi.number().min(0).max(1).default(1),
101
+ radius: Joi.number().min(0).default(0)
101
102
  }).unknown(false);
102
103
  var backgroundSchema = Joi.object({
103
104
  color: Joi.string().pattern(HEX6).optional(),
104
- opacity: Joi.number().min(0).max(1).default(1),
105
- borderRadius: Joi.number().min(0).default(0),
106
- border: borderSchema.optional()
105
+ opacity: Joi.number().min(0).max(1).default(1)
107
106
  }).unknown(false);
108
107
  var paddingSchema = Joi.alternatives().try(
109
108
  Joi.number().min(0).default(0),
@@ -131,6 +130,7 @@ var RichTextAssetSchema = Joi.object({
131
130
  stroke: strokeSchema.optional(),
132
131
  shadow: shadowSchema.optional(),
133
132
  background: backgroundSchema.optional(),
133
+ border: borderSchema.optional(),
134
134
  padding: paddingSchema.optional(),
135
135
  align: alignmentSchema.optional(),
136
136
  animation: animationSchema.optional(),
@@ -941,54 +941,42 @@ async function buildDrawOps(p) {
941
941
  }
942
942
  }
943
943
  }
944
- if (p.background && (p.background.color || p.background.border)) {
944
+ if (p.background || p.border) {
945
945
  const bgX = 0;
946
946
  const bgY = 0;
947
947
  const bgWidth = p.canvas.width;
948
948
  const bgHeight = p.canvas.height;
949
- if (p.background.color) {
949
+ const borderWidth2 = p.border?.width ?? 0;
950
+ const borderRadius = p.border?.radius ?? 0;
951
+ const halfBorder = borderWidth2 / 2;
952
+ const maxRadius = Math.min(bgWidth - borderWidth2, bgHeight - borderWidth2) / 2;
953
+ const outerRadius = Math.min(borderRadius, maxRadius);
954
+ const innerRadius = Math.max(0, outerRadius - halfBorder);
955
+ if (p.background?.color) {
950
956
  ops.push({
951
957
  op: "Rectangle",
952
- x: bgX,
953
- y: bgY,
954
- width: bgWidth,
955
- height: bgHeight,
958
+ x: bgX + borderWidth2,
959
+ y: bgY + borderWidth2,
960
+ width: bgWidth - borderWidth2 * 2,
961
+ height: bgHeight - borderWidth2 * 2,
956
962
  fill: { kind: "solid", color: p.background.color, opacity: p.background.opacity },
957
- borderRadius: p.background.borderRadius
963
+ borderRadius: innerRadius
958
964
  });
959
965
  }
960
- if (p.background.border && p.background.border.width > 0) {
961
- const halfBorder = p.background.border.width / 2;
962
- const borderRadius = p.background.borderRadius;
963
- if (borderRadius <= halfBorder) {
964
- ops.push({
965
- op: "RectangleStroke",
966
- x: bgX,
967
- y: bgY,
968
- width: bgWidth,
969
- height: bgHeight,
970
- stroke: {
971
- width: p.background.border.width,
972
- color: p.background.border.color,
973
- opacity: p.background.border.opacity
974
- },
975
- borderRadius
976
- });
977
- } else {
978
- ops.push({
979
- op: "RectangleStroke",
980
- x: bgX + halfBorder,
981
- y: bgY + halfBorder,
982
- width: bgWidth - p.background.border.width,
983
- height: bgHeight - p.background.border.width,
984
- stroke: {
985
- width: p.background.border.width,
986
- color: p.background.border.color,
987
- opacity: p.background.border.opacity
988
- },
989
- borderRadius: borderRadius - halfBorder
990
- });
991
- }
966
+ if (p.border && p.border.width > 0) {
967
+ ops.push({
968
+ op: "RectangleStroke",
969
+ x: bgX + halfBorder,
970
+ y: bgY + halfBorder,
971
+ width: bgWidth - borderWidth2,
972
+ height: bgHeight - borderWidth2,
973
+ stroke: {
974
+ width: p.border.width,
975
+ color: p.border.color,
976
+ opacity: p.border.opacity
977
+ },
978
+ borderRadius: outerRadius
979
+ });
992
980
  }
993
981
  }
994
982
  ops.push(...textOps);
@@ -2044,11 +2032,13 @@ function computePathBounds2(d) {
2044
2032
  };
2045
2033
  }
2046
2034
  function roundRectPath(ctx, x, y, w, h, r) {
2047
- ctx.moveTo(x + r, y);
2048
- ctx.arcTo(x + w, y, x + w, y + h, r);
2049
- ctx.arcTo(x + w, y + h, x, y + h, r);
2050
- ctx.arcTo(x, y + h, x, y, r);
2051
- ctx.arcTo(x, y, x + w, y, r);
2035
+ const maxRadius = Math.min(w, h) / 2;
2036
+ const radius = Math.min(r, maxRadius);
2037
+ ctx.moveTo(x + radius, y);
2038
+ ctx.arcTo(x + w, y, x + w, y + h, radius);
2039
+ ctx.arcTo(x + w, y + h, x, y + h, radius);
2040
+ ctx.arcTo(x, y + h, x, y, radius);
2041
+ ctx.arcTo(x, y, x + w, y, radius);
2052
2042
  ctx.closePath();
2053
2043
  }
2054
2044
  function tokenizePath2(d) {
@@ -2437,6 +2427,7 @@ async function createTextEngine(opts = {}) {
2437
2427
  vertical: asset.align?.vertical ?? "middle"
2438
2428
  },
2439
2429
  background: asset.background,
2430
+ border: asset.border,
2440
2431
  padding: asset.padding,
2441
2432
  glyphPathProvider: (gid, fontDesc) => fonts.glyphPath(fontDesc || desc, gid),
2442
2433
  getUnitsPerEm: (fontDesc) => fonts.getUnitsPerEm(fontDesc || desc)
@@ -2489,7 +2480,7 @@ async function createTextEngine(opts = {}) {
2489
2480
  try {
2490
2481
  const hasBackground = !!asset.background?.color;
2491
2482
  const hasAnimation = !!asset.animation?.preset;
2492
- const hasBorderRadius = (asset.background?.borderRadius ?? 0) > 0;
2483
+ const hasBorderRadius = (asset.border?.radius ?? 0) > 0;
2493
2484
  const needsAlpha = !hasBackground && hasAnimation || hasBorderRadius;
2494
2485
  console.log(
2495
2486
  `\u{1F3A8} Video settings: Animation=${hasAnimation}, Background=${hasBackground}, BorderRadius=${hasBorderRadius}, Alpha=${needsAlpha}`
@@ -63,12 +63,12 @@ type RichTextValidated = Required<{
63
63
  background?: {
64
64
  color?: string;
65
65
  opacity: number;
66
- borderRadius: number;
67
- border?: {
68
- width: number;
69
- color: string;
70
- opacity: number;
71
- };
66
+ };
67
+ border?: {
68
+ width: number;
69
+ color: string;
70
+ opacity: number;
71
+ radius: number;
72
72
  };
73
73
  padding?: number | {
74
74
  top: number;
package/dist/entry.web.js CHANGED
@@ -101,13 +101,12 @@ var animationSchema = Joi.object({
101
101
  var borderSchema = Joi.object({
102
102
  width: Joi.number().min(0).default(0),
103
103
  color: Joi.string().pattern(HEX6).default("#000000"),
104
- opacity: Joi.number().min(0).max(1).default(1)
104
+ opacity: Joi.number().min(0).max(1).default(1),
105
+ radius: Joi.number().min(0).default(0)
105
106
  }).unknown(false);
106
107
  var backgroundSchema = Joi.object({
107
108
  color: Joi.string().pattern(HEX6).optional(),
108
- opacity: Joi.number().min(0).max(1).default(1),
109
- borderRadius: Joi.number().min(0).default(0),
110
- border: borderSchema.optional()
109
+ opacity: Joi.number().min(0).max(1).default(1)
111
110
  }).unknown(false);
112
111
  var paddingSchema = Joi.alternatives().try(
113
112
  Joi.number().min(0).default(0),
@@ -135,6 +134,7 @@ var RichTextAssetSchema = Joi.object({
135
134
  stroke: strokeSchema.optional(),
136
135
  shadow: shadowSchema.optional(),
137
136
  background: backgroundSchema.optional(),
137
+ border: borderSchema.optional(),
138
138
  padding: paddingSchema.optional(),
139
139
  align: alignmentSchema.optional(),
140
140
  animation: animationSchema.optional(),
@@ -946,54 +946,42 @@ async function buildDrawOps(p) {
946
946
  }
947
947
  }
948
948
  }
949
- if (p.background && (p.background.color || p.background.border)) {
949
+ if (p.background || p.border) {
950
950
  const bgX = 0;
951
951
  const bgY = 0;
952
952
  const bgWidth = p.canvas.width;
953
953
  const bgHeight = p.canvas.height;
954
- if (p.background.color) {
954
+ const borderWidth2 = p.border?.width ?? 0;
955
+ const borderRadius = p.border?.radius ?? 0;
956
+ const halfBorder = borderWidth2 / 2;
957
+ const maxRadius = Math.min(bgWidth - borderWidth2, bgHeight - borderWidth2) / 2;
958
+ const outerRadius = Math.min(borderRadius, maxRadius);
959
+ const innerRadius = Math.max(0, outerRadius - halfBorder);
960
+ if (p.background?.color) {
955
961
  ops.push({
956
962
  op: "Rectangle",
957
- x: bgX,
958
- y: bgY,
959
- width: bgWidth,
960
- height: bgHeight,
963
+ x: bgX + borderWidth2,
964
+ y: bgY + borderWidth2,
965
+ width: bgWidth - borderWidth2 * 2,
966
+ height: bgHeight - borderWidth2 * 2,
961
967
  fill: { kind: "solid", color: p.background.color, opacity: p.background.opacity },
962
- borderRadius: p.background.borderRadius
968
+ borderRadius: innerRadius
963
969
  });
964
970
  }
965
- if (p.background.border && p.background.border.width > 0) {
966
- const halfBorder = p.background.border.width / 2;
967
- const borderRadius = p.background.borderRadius;
968
- if (borderRadius <= halfBorder) {
969
- ops.push({
970
- op: "RectangleStroke",
971
- x: bgX,
972
- y: bgY,
973
- width: bgWidth,
974
- height: bgHeight,
975
- stroke: {
976
- width: p.background.border.width,
977
- color: p.background.border.color,
978
- opacity: p.background.border.opacity
979
- },
980
- borderRadius
981
- });
982
- } else {
983
- ops.push({
984
- op: "RectangleStroke",
985
- x: bgX + halfBorder,
986
- y: bgY + halfBorder,
987
- width: bgWidth - p.background.border.width,
988
- height: bgHeight - p.background.border.width,
989
- stroke: {
990
- width: p.background.border.width,
991
- color: p.background.border.color,
992
- opacity: p.background.border.opacity
993
- },
994
- borderRadius: borderRadius - halfBorder
995
- });
996
- }
971
+ if (p.border && p.border.width > 0) {
972
+ ops.push({
973
+ op: "RectangleStroke",
974
+ x: bgX + halfBorder,
975
+ y: bgY + halfBorder,
976
+ width: bgWidth - borderWidth2,
977
+ height: bgHeight - borderWidth2,
978
+ stroke: {
979
+ width: p.border.width,
980
+ color: p.border.color,
981
+ opacity: p.border.opacity
982
+ },
983
+ borderRadius: outerRadius
984
+ });
997
985
  }
998
986
  }
999
987
  ops.push(...textOps);
@@ -1778,11 +1766,12 @@ function createWebPainter(canvas) {
1778
1766
  ctx.lineWidth = op.stroke.width;
1779
1767
  if (op.borderRadius && op.borderRadius > 0) {
1780
1768
  const p = new Path2D();
1781
- const r = op.borderRadius;
1782
1769
  const x = op.x;
1783
1770
  const y = op.y;
1784
1771
  const w = op.width;
1785
1772
  const h = op.height;
1773
+ const maxRadius = Math.min(w, h) / 2;
1774
+ const r = Math.min(op.borderRadius, maxRadius);
1786
1775
  p.moveTo(x + r, y);
1787
1776
  p.arcTo(x + w, y, x + w, y + h, r);
1788
1777
  p.arcTo(x + w, y + h, x, y + h, r);
@@ -1801,12 +1790,14 @@ function createWebPainter(canvas) {
1801
1790
  };
1802
1791
  }
1803
1792
  function drawRoundedRect(ctx, x, y, w, h, r) {
1793
+ const maxRadius = Math.min(w, h) / 2;
1794
+ const radius = Math.min(r, maxRadius);
1804
1795
  const p = new Path2D();
1805
- p.moveTo(x + r, y);
1806
- p.arcTo(x + w, y, x + w, y + h, r);
1807
- p.arcTo(x + w, y + h, x, y + h, r);
1808
- p.arcTo(x, y + h, x, y, r);
1809
- p.arcTo(x, y, x + w, y, r);
1796
+ p.moveTo(x + radius, y);
1797
+ p.arcTo(x + w, y, x + w, y + h, radius);
1798
+ p.arcTo(x + w, y + h, x, y + h, radius);
1799
+ p.arcTo(x, y + h, x, y, radius);
1800
+ p.arcTo(x, y, x + w, y, radius);
1810
1801
  p.closePath();
1811
1802
  ctx.save();
1812
1803
  ctx.fill(p);
@@ -2160,6 +2151,7 @@ async function createTextEngine(opts = {}) {
2160
2151
  vertical: asset.align?.vertical ?? "middle"
2161
2152
  },
2162
2153
  background: asset.background,
2154
+ border: asset.border,
2163
2155
  padding: asset.padding,
2164
2156
  glyphPathProvider: (gid, fontDesc) => fonts.glyphPath(fontDesc || desc, gid),
2165
2157
  getUnitsPerEm: (fontDesc) => fonts.getUnitsPerEm(fontDesc || desc)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shotstack/shotstack-canvas",
3
- "version": "1.5.4",
3
+ "version": "1.5.6",
4
4
  "description": "Text layout & animation engine (HarfBuzz) for Node & Web - fully self-contained.",
5
5
  "type": "module",
6
6
  "main": "./dist/entry.node.cjs",