@shotstack/shotstack-canvas 1.5.8 → 1.6.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.
@@ -397,6 +397,37 @@ async function initHB(wasmBaseURL) {
397
397
 
398
398
  // src/core/font-registry.ts
399
399
  var fontkit = __toESM(require("fontkit"), 1);
400
+ function normalizeWeight(weight) {
401
+ if (weight === void 0 || weight === null) return "400";
402
+ if (typeof weight === "number") return String(weight);
403
+ const lowerWeight = String(weight).toLowerCase().trim();
404
+ const weightMap = {
405
+ thin: "100",
406
+ hairline: "100",
407
+ extralight: "200",
408
+ ultralight: "200",
409
+ light: "300",
410
+ normal: "400",
411
+ regular: "400",
412
+ medium: "500",
413
+ semibold: "600",
414
+ demibold: "600",
415
+ bold: "700",
416
+ extrabold: "800",
417
+ ultrabold: "800",
418
+ black: "900",
419
+ heavy: "900"
420
+ };
421
+ if (weightMap[lowerWeight]) {
422
+ return weightMap[lowerWeight];
423
+ }
424
+ const numWeight = parseInt(String(weight), 10);
425
+ if (!isNaN(numWeight) && numWeight >= 100 && numWeight <= 900) {
426
+ return String(numWeight);
427
+ }
428
+ console.warn(`Invalid font weight "${weight}", defaulting to 400`);
429
+ return "400";
430
+ }
400
431
  var FontRegistry = class _FontRegistry {
401
432
  hb;
402
433
  faces = /* @__PURE__ */ new Map();
@@ -454,7 +485,8 @@ var FontRegistry = class _FontRegistry {
454
485
  return this.hb;
455
486
  }
456
487
  key(desc) {
457
- return `${desc.family}__${desc.weight ?? "400"}`;
488
+ const normalizedWeight = normalizeWeight(desc.weight);
489
+ return `${desc.family}__${normalizedWeight}`;
458
490
  }
459
491
  async registerFromBytes(bytes, desc) {
460
492
  try {
@@ -466,8 +498,8 @@ var FontRegistry = class _FontRegistry {
466
498
  const font = this.hb.createFont(face);
467
499
  const upem = face.upem || 1e3;
468
500
  font.setScale(upem, upem);
469
- const weight = desc.weight ?? "400";
470
- const weightNum = typeof weight === "string" ? parseInt(weight, 10) : weight;
501
+ const normalizedWeight = normalizeWeight(desc.weight);
502
+ const weightNum = parseInt(normalizedWeight, 10);
471
503
  if (weightNum !== 400 && typeof font.setVariations === "function") {
472
504
  try {
473
505
  font.setVariations(`wght=${weightNum}`);
@@ -501,14 +533,15 @@ var FontRegistry = class _FontRegistry {
501
533
  const loader = _FontRegistry.fallbackLoader;
502
534
  if (!loader) return false;
503
535
  try {
536
+ const normalizedWeight = normalizeWeight(desc.weight);
504
537
  const bytes = await loader({
505
538
  family: desc.family,
506
- weight: desc.weight ?? "400"
539
+ weight: normalizedWeight
507
540
  });
508
541
  if (!bytes) return false;
509
542
  await this.registerFromBytes(bytes, {
510
543
  family: desc.family,
511
- weight: desc.weight ?? "400"
544
+ weight: normalizedWeight
512
545
  });
513
546
  return true;
514
547
  } catch {
@@ -870,19 +903,20 @@ async function buildDrawOps(p) {
870
903
  const scale = p.font.size / upem;
871
904
  const numLines = p.lines.length;
872
905
  const lineHeightPx = p.font.size * p.style.lineHeight;
906
+ const textOffsetY = borderWidth + padding.top;
873
907
  let blockY;
874
908
  switch (p.align.vertical) {
875
909
  case "top":
876
- blockY = p.font.size + padding.top;
910
+ blockY = p.font.size + textOffsetY;
877
911
  break;
878
912
  case "bottom":
879
- blockY = p.textRect.height - (numLines - 1) * lineHeightPx + padding.top;
913
+ blockY = p.textRect.height - (numLines - 1) * lineHeightPx + textOffsetY;
880
914
  break;
881
915
  case "middle":
882
916
  default:
883
917
  const capHeightRatio = 0.35;
884
918
  const visualOffset = p.font.size * capHeightRatio;
885
- blockY = (p.textRect.height - (numLines - 1) * lineHeightPx) / 2 + visualOffset + padding.top;
919
+ blockY = (p.textRect.height - (numLines - 1) * lineHeightPx) / 2 + visualOffset + textOffsetY;
886
920
  break;
887
921
  }
888
922
  const fill = p.style.gradient ? gradientSpecFrom(p.style.gradient, 1) : { kind: "solid", color: p.font.color, opacity: p.font.opacity };
@@ -891,17 +925,18 @@ async function buildDrawOps(p) {
891
925
  const highlighterOps = [];
892
926
  let gMinX = Infinity, gMinY = Infinity, gMaxX = -Infinity, gMaxY = -Infinity;
893
927
  for (const line of p.lines) {
928
+ const textOffsetX = borderWidth + padding.left;
894
929
  let lineX;
895
930
  switch (p.align.horizontal) {
896
931
  case "left":
897
- lineX = padding.left;
932
+ lineX = textOffsetX;
898
933
  break;
899
934
  case "right":
900
- lineX = p.textRect.width - line.width + padding.left;
935
+ lineX = p.textRect.width - line.width + textOffsetX;
901
936
  break;
902
937
  case "center":
903
938
  default:
904
- lineX = (p.textRect.width - line.width) / 2 + padding.left;
939
+ lineX = (p.textRect.width - line.width) / 2 + textOffsetX;
905
940
  break;
906
941
  }
907
942
  let xCursor = lineX;
@@ -2481,8 +2516,9 @@ async function createTextEngine(opts = {}) {
2481
2516
  width: asset.width ?? width,
2482
2517
  height: asset.height ?? height
2483
2518
  };
2484
- const canvasW = (asset.width ?? width) + padding.left + padding.right;
2485
- const canvasH = (asset.height ?? height) + padding.top + padding.bottom;
2519
+ const borderWidth = asset.border?.width ?? 0;
2520
+ const canvasW = (asset.width ?? width) + padding.left + padding.right + borderWidth * 2;
2521
+ const canvasH = (asset.height ?? height) + padding.top + padding.bottom + borderWidth * 2;
2486
2522
  const canvasPR = pixelRatio;
2487
2523
  let ops0;
2488
2524
  try {
@@ -358,6 +358,37 @@ async function initHB(wasmBaseURL) {
358
358
 
359
359
  // src/core/font-registry.ts
360
360
  import * as fontkit from "fontkit";
361
+ function normalizeWeight(weight) {
362
+ if (weight === void 0 || weight === null) return "400";
363
+ if (typeof weight === "number") return String(weight);
364
+ const lowerWeight = String(weight).toLowerCase().trim();
365
+ const weightMap = {
366
+ thin: "100",
367
+ hairline: "100",
368
+ extralight: "200",
369
+ ultralight: "200",
370
+ light: "300",
371
+ normal: "400",
372
+ regular: "400",
373
+ medium: "500",
374
+ semibold: "600",
375
+ demibold: "600",
376
+ bold: "700",
377
+ extrabold: "800",
378
+ ultrabold: "800",
379
+ black: "900",
380
+ heavy: "900"
381
+ };
382
+ if (weightMap[lowerWeight]) {
383
+ return weightMap[lowerWeight];
384
+ }
385
+ const numWeight = parseInt(String(weight), 10);
386
+ if (!isNaN(numWeight) && numWeight >= 100 && numWeight <= 900) {
387
+ return String(numWeight);
388
+ }
389
+ console.warn(`Invalid font weight "${weight}", defaulting to 400`);
390
+ return "400";
391
+ }
361
392
  var FontRegistry = class _FontRegistry {
362
393
  hb;
363
394
  faces = /* @__PURE__ */ new Map();
@@ -415,7 +446,8 @@ var FontRegistry = class _FontRegistry {
415
446
  return this.hb;
416
447
  }
417
448
  key(desc) {
418
- return `${desc.family}__${desc.weight ?? "400"}`;
449
+ const normalizedWeight = normalizeWeight(desc.weight);
450
+ return `${desc.family}__${normalizedWeight}`;
419
451
  }
420
452
  async registerFromBytes(bytes, desc) {
421
453
  try {
@@ -427,8 +459,8 @@ var FontRegistry = class _FontRegistry {
427
459
  const font = this.hb.createFont(face);
428
460
  const upem = face.upem || 1e3;
429
461
  font.setScale(upem, upem);
430
- const weight = desc.weight ?? "400";
431
- const weightNum = typeof weight === "string" ? parseInt(weight, 10) : weight;
462
+ const normalizedWeight = normalizeWeight(desc.weight);
463
+ const weightNum = parseInt(normalizedWeight, 10);
432
464
  if (weightNum !== 400 && typeof font.setVariations === "function") {
433
465
  try {
434
466
  font.setVariations(`wght=${weightNum}`);
@@ -462,14 +494,15 @@ var FontRegistry = class _FontRegistry {
462
494
  const loader = _FontRegistry.fallbackLoader;
463
495
  if (!loader) return false;
464
496
  try {
497
+ const normalizedWeight = normalizeWeight(desc.weight);
465
498
  const bytes = await loader({
466
499
  family: desc.family,
467
- weight: desc.weight ?? "400"
500
+ weight: normalizedWeight
468
501
  });
469
502
  if (!bytes) return false;
470
503
  await this.registerFromBytes(bytes, {
471
504
  family: desc.family,
472
- weight: desc.weight ?? "400"
505
+ weight: normalizedWeight
473
506
  });
474
507
  return true;
475
508
  } catch {
@@ -831,19 +864,20 @@ async function buildDrawOps(p) {
831
864
  const scale = p.font.size / upem;
832
865
  const numLines = p.lines.length;
833
866
  const lineHeightPx = p.font.size * p.style.lineHeight;
867
+ const textOffsetY = borderWidth + padding.top;
834
868
  let blockY;
835
869
  switch (p.align.vertical) {
836
870
  case "top":
837
- blockY = p.font.size + padding.top;
871
+ blockY = p.font.size + textOffsetY;
838
872
  break;
839
873
  case "bottom":
840
- blockY = p.textRect.height - (numLines - 1) * lineHeightPx + padding.top;
874
+ blockY = p.textRect.height - (numLines - 1) * lineHeightPx + textOffsetY;
841
875
  break;
842
876
  case "middle":
843
877
  default:
844
878
  const capHeightRatio = 0.35;
845
879
  const visualOffset = p.font.size * capHeightRatio;
846
- blockY = (p.textRect.height - (numLines - 1) * lineHeightPx) / 2 + visualOffset + padding.top;
880
+ blockY = (p.textRect.height - (numLines - 1) * lineHeightPx) / 2 + visualOffset + textOffsetY;
847
881
  break;
848
882
  }
849
883
  const fill = p.style.gradient ? gradientSpecFrom(p.style.gradient, 1) : { kind: "solid", color: p.font.color, opacity: p.font.opacity };
@@ -852,17 +886,18 @@ async function buildDrawOps(p) {
852
886
  const highlighterOps = [];
853
887
  let gMinX = Infinity, gMinY = Infinity, gMaxX = -Infinity, gMaxY = -Infinity;
854
888
  for (const line of p.lines) {
889
+ const textOffsetX = borderWidth + padding.left;
855
890
  let lineX;
856
891
  switch (p.align.horizontal) {
857
892
  case "left":
858
- lineX = padding.left;
893
+ lineX = textOffsetX;
859
894
  break;
860
895
  case "right":
861
- lineX = p.textRect.width - line.width + padding.left;
896
+ lineX = p.textRect.width - line.width + textOffsetX;
862
897
  break;
863
898
  case "center":
864
899
  default:
865
- lineX = (p.textRect.width - line.width) / 2 + padding.left;
900
+ lineX = (p.textRect.width - line.width) / 2 + textOffsetX;
866
901
  break;
867
902
  }
868
903
  let xCursor = lineX;
@@ -2442,8 +2477,9 @@ async function createTextEngine(opts = {}) {
2442
2477
  width: asset.width ?? width,
2443
2478
  height: asset.height ?? height
2444
2479
  };
2445
- const canvasW = (asset.width ?? width) + padding.left + padding.right;
2446
- const canvasH = (asset.height ?? height) + padding.top + padding.bottom;
2480
+ const borderWidth = asset.border?.width ?? 0;
2481
+ const canvasW = (asset.width ?? width) + padding.left + padding.right + borderWidth * 2;
2482
+ const canvasH = (asset.height ?? height) + padding.top + padding.bottom + borderWidth * 2;
2447
2483
  const canvasPR = pixelRatio;
2448
2484
  let ops0;
2449
2485
  try {
package/dist/entry.web.js CHANGED
@@ -362,6 +362,37 @@ async function initHB(wasmBaseURL) {
362
362
 
363
363
  // src/core/font-registry.ts
364
364
  import * as fontkit from "fontkit";
365
+ function normalizeWeight(weight) {
366
+ if (weight === void 0 || weight === null) return "400";
367
+ if (typeof weight === "number") return String(weight);
368
+ const lowerWeight = String(weight).toLowerCase().trim();
369
+ const weightMap = {
370
+ thin: "100",
371
+ hairline: "100",
372
+ extralight: "200",
373
+ ultralight: "200",
374
+ light: "300",
375
+ normal: "400",
376
+ regular: "400",
377
+ medium: "500",
378
+ semibold: "600",
379
+ demibold: "600",
380
+ bold: "700",
381
+ extrabold: "800",
382
+ ultrabold: "800",
383
+ black: "900",
384
+ heavy: "900"
385
+ };
386
+ if (weightMap[lowerWeight]) {
387
+ return weightMap[lowerWeight];
388
+ }
389
+ const numWeight = parseInt(String(weight), 10);
390
+ if (!isNaN(numWeight) && numWeight >= 100 && numWeight <= 900) {
391
+ return String(numWeight);
392
+ }
393
+ console.warn(`Invalid font weight "${weight}", defaulting to 400`);
394
+ return "400";
395
+ }
365
396
  var _FontRegistry = class _FontRegistry {
366
397
  constructor(wasmBaseURL) {
367
398
  __publicField(this, "hb");
@@ -418,7 +449,8 @@ var _FontRegistry = class _FontRegistry {
418
449
  return this.hb;
419
450
  }
420
451
  key(desc) {
421
- return `${desc.family}__${desc.weight ?? "400"}`;
452
+ const normalizedWeight = normalizeWeight(desc.weight);
453
+ return `${desc.family}__${normalizedWeight}`;
422
454
  }
423
455
  async registerFromBytes(bytes, desc) {
424
456
  try {
@@ -430,8 +462,8 @@ var _FontRegistry = class _FontRegistry {
430
462
  const font = this.hb.createFont(face);
431
463
  const upem = face.upem || 1e3;
432
464
  font.setScale(upem, upem);
433
- const weight = desc.weight ?? "400";
434
- const weightNum = typeof weight === "string" ? parseInt(weight, 10) : weight;
465
+ const normalizedWeight = normalizeWeight(desc.weight);
466
+ const weightNum = parseInt(normalizedWeight, 10);
435
467
  if (weightNum !== 400 && typeof font.setVariations === "function") {
436
468
  try {
437
469
  font.setVariations(`wght=${weightNum}`);
@@ -465,14 +497,15 @@ var _FontRegistry = class _FontRegistry {
465
497
  const loader = _FontRegistry.fallbackLoader;
466
498
  if (!loader) return false;
467
499
  try {
500
+ const normalizedWeight = normalizeWeight(desc.weight);
468
501
  const bytes = await loader({
469
502
  family: desc.family,
470
- weight: desc.weight ?? "400"
503
+ weight: normalizedWeight
471
504
  });
472
505
  if (!bytes) return false;
473
506
  await this.registerFromBytes(bytes, {
474
507
  family: desc.family,
475
- weight: desc.weight ?? "400"
508
+ weight: normalizedWeight
476
509
  });
477
510
  return true;
478
511
  } catch {
@@ -836,19 +869,20 @@ async function buildDrawOps(p) {
836
869
  const scale = p.font.size / upem;
837
870
  const numLines = p.lines.length;
838
871
  const lineHeightPx = p.font.size * p.style.lineHeight;
872
+ const textOffsetY = borderWidth + padding.top;
839
873
  let blockY;
840
874
  switch (p.align.vertical) {
841
875
  case "top":
842
- blockY = p.font.size + padding.top;
876
+ blockY = p.font.size + textOffsetY;
843
877
  break;
844
878
  case "bottom":
845
- blockY = p.textRect.height - (numLines - 1) * lineHeightPx + padding.top;
879
+ blockY = p.textRect.height - (numLines - 1) * lineHeightPx + textOffsetY;
846
880
  break;
847
881
  case "middle":
848
882
  default:
849
883
  const capHeightRatio = 0.35;
850
884
  const visualOffset = p.font.size * capHeightRatio;
851
- blockY = (p.textRect.height - (numLines - 1) * lineHeightPx) / 2 + visualOffset + padding.top;
885
+ blockY = (p.textRect.height - (numLines - 1) * lineHeightPx) / 2 + visualOffset + textOffsetY;
852
886
  break;
853
887
  }
854
888
  const fill = p.style.gradient ? gradientSpecFrom(p.style.gradient, 1) : { kind: "solid", color: p.font.color, opacity: p.font.opacity };
@@ -857,17 +891,18 @@ async function buildDrawOps(p) {
857
891
  const highlighterOps = [];
858
892
  let gMinX = Infinity, gMinY = Infinity, gMaxX = -Infinity, gMaxY = -Infinity;
859
893
  for (const line of p.lines) {
894
+ const textOffsetX = borderWidth + padding.left;
860
895
  let lineX;
861
896
  switch (p.align.horizontal) {
862
897
  case "left":
863
- lineX = padding.left;
898
+ lineX = textOffsetX;
864
899
  break;
865
900
  case "right":
866
- lineX = p.textRect.width - line.width + padding.left;
901
+ lineX = p.textRect.width - line.width + textOffsetX;
867
902
  break;
868
903
  case "center":
869
904
  default:
870
- lineX = (p.textRect.width - line.width) / 2 + padding.left;
905
+ lineX = (p.textRect.width - line.width) / 2 + textOffsetX;
871
906
  break;
872
907
  }
873
908
  let xCursor = lineX;
@@ -2153,8 +2188,9 @@ async function createTextEngine(opts = {}) {
2153
2188
  width: asset.width ?? width,
2154
2189
  height: asset.height ?? height
2155
2190
  };
2156
- const canvasW = (asset.width ?? width) + padding.left + padding.right;
2157
- const canvasH = (asset.height ?? height) + padding.top + padding.bottom;
2191
+ const borderWidth = asset.border?.width ?? 0;
2192
+ const canvasW = (asset.width ?? width) + padding.left + padding.right + borderWidth * 2;
2193
+ const canvasH = (asset.height ?? height) + padding.top + padding.bottom + borderWidth * 2;
2158
2194
  const canvasPR = pixelRatio;
2159
2195
  let ops0;
2160
2196
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shotstack/shotstack-canvas",
3
- "version": "1.5.8",
3
+ "version": "1.6.0",
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",