@shotstack/shotstack-canvas 2.0.9 → 2.0.10

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.
@@ -566,7 +566,7 @@ var CanvasRichTextAssetSchema = import_zod2.richTextAssetSchema.extend({
566
566
  customFonts: import_zod.z.array(customFontSchema).optional()
567
567
  }).strict();
568
568
  var CanvasSvgAssetSchema = import_zod2.svgAssetSchema;
569
- var wordTimingSchema = import_zod2.wordTimingSchema.extend({
569
+ var wordTimingSchema = import_zod.z.object({
570
570
  text: import_zod.z.string().min(1),
571
571
  start: import_zod.z.number().min(0),
572
572
  end: import_zod.z.number().min(0),
@@ -607,27 +607,18 @@ var richCaptionAssetSchema = import_zod.z.object({
607
607
  stroke: canvasStrokeSchema.optional(),
608
608
  shadow: canvasShadowSchema.optional(),
609
609
  background: canvasBackgroundSchema.optional(),
610
+ border: borderSchema.optional(),
610
611
  padding: paddingSchema.optional(),
611
612
  align: canvasAlignmentSchema.optional(),
612
613
  active: richCaptionActiveSchema.optional(),
613
614
  wordAnimation: richCaptionWordAnimationSchema.optional(),
614
- position: import_zod.z.enum(["top", "center", "bottom"]).default("bottom"),
615
- maxWidth: import_zod.z.number().min(0.1).max(1).default(0.9),
616
- maxLines: import_zod.z.number().int().min(1).max(10).default(2),
617
615
  customFonts: import_zod.z.array(customFontSchema).optional()
618
616
  }).superRefine((data, ctx) => {
619
- if (data.src && data.words) {
620
- ctx.addIssue({
621
- code: import_zod.z.ZodIssueCode.custom,
622
- message: "src and words are mutually exclusive",
623
- path: ["src"]
624
- });
625
- }
626
617
  if (!data.src && !data.words) {
627
618
  ctx.addIssue({
628
619
  code: import_zod.z.ZodIssueCode.custom,
629
620
  message: "Either src or words must be provided",
630
- path: ["words"]
621
+ path: ["src"]
631
622
  });
632
623
  }
633
624
  });
@@ -2494,6 +2485,12 @@ async function createNodePainter(opts) {
2494
2485
  if (!fill) {
2495
2486
  fill = makeGradientFromBBox(context, fillOp.fill, localBBox);
2496
2487
  gradientCache.set(cacheKey, fill);
2488
+ if (gradientCache.size > GRADIENT_CACHE_MAX) {
2489
+ const firstEntry = gradientCache.keys().next();
2490
+ if (!firstEntry.done) {
2491
+ gradientCache.delete(firstEntry.value);
2492
+ }
2493
+ }
2497
2494
  }
2498
2495
  context.fillStyle = fill;
2499
2496
  context.beginPath();
@@ -2841,7 +2838,7 @@ async function createNodePainter(opts) {
2841
2838
  toRawRGBA() {
2842
2839
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
2843
2840
  return {
2844
- data: new Uint8ClampedArray(imageData.data),
2841
+ data: imageData.data,
2845
2842
  width: canvas.width,
2846
2843
  height: canvas.height
2847
2844
  };
@@ -4464,7 +4461,7 @@ var CaptionLayoutEngine = class {
4464
4461
  }
4465
4462
  }
4466
4463
  const wordGroups = groupWordsByPause(store, config.pauseThreshold);
4467
- const pixelMaxWidth = config.frameWidth * config.maxWidth;
4464
+ const pixelMaxWidth = config.availableWidth;
4468
4465
  let spaceWidth;
4469
4466
  if (config.measureTextWidth) {
4470
4467
  const fontString = `${config.fontWeight} ${config.fontSize}px "${config.fontFamily}"`;
@@ -4494,31 +4491,45 @@ var CaptionLayoutEngine = class {
4494
4491
  };
4495
4492
  });
4496
4493
  const allWordIndices = lines.flatMap((l) => l.wordIndices);
4494
+ if (allWordIndices.length === 0) {
4495
+ return null;
4496
+ }
4497
4497
  return {
4498
4498
  wordIndices: allWordIndices,
4499
4499
  startTime: store.startTimes[allWordIndices[0]],
4500
4500
  endTime: store.endTimes[allWordIndices[allWordIndices.length - 1]],
4501
4501
  lines
4502
4502
  };
4503
- });
4503
+ }).filter((g) => g !== null);
4504
4504
  });
4505
4505
  const calculateGroupY = (group) => {
4506
4506
  const totalHeight = group.lines.length * config.fontSize * config.lineHeight;
4507
- switch (config.position) {
4507
+ switch (config.verticalAlign) {
4508
4508
  case "top":
4509
4509
  return config.fontSize * 1.5;
4510
4510
  case "bottom":
4511
4511
  return config.frameHeight - totalHeight - config.fontSize * 0.5;
4512
- case "center":
4512
+ case "middle":
4513
4513
  default:
4514
4514
  return (config.frameHeight - totalHeight) / 2 + config.fontSize;
4515
4515
  }
4516
4516
  };
4517
+ const calculateLineX = (lineWidth) => {
4518
+ switch (config.horizontalAlign) {
4519
+ case "left":
4520
+ return config.paddingLeft;
4521
+ case "right":
4522
+ return config.frameWidth - lineWidth - config.paddingLeft;
4523
+ case "center":
4524
+ default:
4525
+ return (config.frameWidth - lineWidth) / 2;
4526
+ }
4527
+ };
4517
4528
  for (const group of groups) {
4518
4529
  const baseY = calculateGroupY(group);
4519
4530
  for (let lineIdx = 0; lineIdx < group.lines.length; lineIdx++) {
4520
4531
  const line = group.lines[lineIdx];
4521
- line.x = (config.frameWidth - line.width) / 2;
4532
+ line.x = calculateLineX(line.width);
4522
4533
  line.y = baseY + lineIdx * config.fontSize * config.lineHeight;
4523
4534
  let xCursor = line.x;
4524
4535
  for (const wordIdx of line.wordIndices) {
@@ -4809,6 +4820,7 @@ function calculateNoneState(ctx) {
4809
4820
  };
4810
4821
  }
4811
4822
  function calculateWordAnimationState(wordStart, wordEnd, currentTime, config, activeScale = 1, charCount = 0, fontSize = 48) {
4823
+ const safeSpeed = config.speed > 0 ? config.speed : 1;
4812
4824
  const ctx = {
4813
4825
  wordStart,
4814
4826
  wordEnd,
@@ -4819,25 +4831,25 @@ function calculateWordAnimationState(wordStart, wordEnd, currentTime, config, ac
4819
4831
  let partialState;
4820
4832
  switch (config.style) {
4821
4833
  case "karaoke":
4822
- partialState = calculateKaraokeState(ctx, config.speed);
4834
+ partialState = calculateKaraokeState(ctx, safeSpeed);
4823
4835
  break;
4824
4836
  case "highlight":
4825
4837
  partialState = calculateHighlightState(ctx);
4826
4838
  break;
4827
4839
  case "pop":
4828
- partialState = calculatePopState(ctx, activeScale, config.speed);
4840
+ partialState = calculatePopState(ctx, activeScale, safeSpeed);
4829
4841
  break;
4830
4842
  case "fade":
4831
- partialState = calculateFadeState(ctx, config.speed);
4843
+ partialState = calculateFadeState(ctx, safeSpeed);
4832
4844
  break;
4833
4845
  case "slide":
4834
- partialState = calculateSlideState(ctx, config.direction, config.speed, fontSize);
4846
+ partialState = calculateSlideState(ctx, config.direction, safeSpeed, fontSize);
4835
4847
  break;
4836
4848
  case "bounce":
4837
- partialState = calculateBounceState(ctx, config.speed, fontSize);
4849
+ partialState = calculateBounceState(ctx, safeSpeed, fontSize);
4838
4850
  break;
4839
4851
  case "typewriter":
4840
- partialState = calculateTypewriterState(ctx, charCount, config.speed);
4852
+ partialState = calculateTypewriterState(ctx, charCount, safeSpeed);
4841
4853
  break;
4842
4854
  case "none":
4843
4855
  default:
@@ -4961,6 +4973,22 @@ function extractCaptionBackground(asset) {
4961
4973
  opacity: bg.opacity ?? 1
4962
4974
  };
4963
4975
  }
4976
+ function extractCaptionBackgroundBorderRadius(asset) {
4977
+ const bg = asset.background;
4978
+ return bg?.borderRadius ?? 0;
4979
+ }
4980
+ function extractCaptionBorder(asset) {
4981
+ const border = asset.border;
4982
+ if (!border || !border.width || border.width <= 0) {
4983
+ return void 0;
4984
+ }
4985
+ return {
4986
+ width: border.width,
4987
+ color: border.color ?? "#000000",
4988
+ opacity: border.opacity ?? 1,
4989
+ radius: border.radius ?? 0
4990
+ };
4991
+ }
4964
4992
  function extractAnimationConfig(asset) {
4965
4993
  const wordAnim = asset.wordAnimation;
4966
4994
  if (!wordAnim) {
@@ -5006,6 +5034,28 @@ function createDrawCaptionWordOp(word, animState, asset, fontConfig) {
5006
5034
  background: extractBackgroundConfig(asset, isActive)
5007
5035
  };
5008
5036
  }
5037
+ function calculateGroupBounds(activeGroup, padding) {
5038
+ let minX = Infinity;
5039
+ let maxX = -Infinity;
5040
+ let minY = Infinity;
5041
+ let maxY = -Infinity;
5042
+ for (const line of activeGroup.lines) {
5043
+ const lineX = line.x;
5044
+ const lineRight = line.x + line.width;
5045
+ const lineY = line.y - line.height * 0.8;
5046
+ const lineBottom = line.y + line.height * 0.2;
5047
+ if (lineX < minX) minX = lineX;
5048
+ if (lineRight > maxX) maxX = lineRight;
5049
+ if (lineY < minY) minY = lineY;
5050
+ if (lineBottom > maxY) maxY = lineBottom;
5051
+ }
5052
+ return {
5053
+ bgX: minX - padding.left,
5054
+ bgY: minY - padding.top,
5055
+ bgWidth: maxX - minX + padding.left + padding.right,
5056
+ bgHeight: maxY - minY + padding.top + padding.bottom
5057
+ };
5058
+ }
5009
5059
  function generateRichCaptionDrawOps(asset, layout, frameTimeMs, layoutEngine, _config) {
5010
5060
  if (layout.store.length === 0) {
5011
5061
  return [];
@@ -5025,36 +5075,40 @@ function generateRichCaptionDrawOps(asset, layout, frameTimeMs, layoutEngine, _c
5025
5075
  fontConfig.size
5026
5076
  );
5027
5077
  const ops = [];
5028
- const captionBg = extractCaptionBackground(asset);
5029
- if (captionBg) {
5030
- const activeGroup = layout.groups.find(
5031
- (g) => frameTimeMs >= g.startTime && frameTimeMs <= g.endTime
5032
- );
5033
- if (activeGroup && activeGroup.lines.length > 0) {
5034
- const padding = extractCaptionPadding(asset);
5035
- let minX = Infinity;
5036
- let maxX = -Infinity;
5037
- let minY = Infinity;
5038
- let maxY = -Infinity;
5039
- for (const line of activeGroup.lines) {
5040
- const lineX = line.x;
5041
- const lineRight = line.x + line.width;
5042
- const lineY = line.y - line.height * 0.8;
5043
- const lineBottom = line.y + line.height * 0.2;
5044
- if (lineX < minX) minX = lineX;
5045
- if (lineRight > maxX) maxX = lineRight;
5046
- if (lineY < minY) minY = lineY;
5047
- if (lineBottom > maxY) maxY = lineBottom;
5048
- }
5078
+ const activeGroup = layout.groups.find(
5079
+ (g) => frameTimeMs >= g.startTime && frameTimeMs <= g.endTime
5080
+ );
5081
+ if (activeGroup && activeGroup.lines.length > 0) {
5082
+ const padding = extractCaptionPadding(asset);
5083
+ const { bgX, bgY, bgWidth, bgHeight } = calculateGroupBounds(activeGroup, padding);
5084
+ const captionBg = extractCaptionBackground(asset);
5085
+ if (captionBg) {
5049
5086
  ops.push({
5050
5087
  op: "DrawCaptionBackground",
5051
- x: minX - padding.left,
5052
- y: minY - padding.top,
5053
- width: maxX - minX + padding.left + padding.right,
5054
- height: maxY - minY + padding.top + padding.bottom,
5088
+ x: bgX,
5089
+ y: bgY,
5090
+ width: bgWidth,
5091
+ height: bgHeight,
5055
5092
  color: captionBg.color,
5056
5093
  opacity: captionBg.opacity,
5057
- borderRadius: 8
5094
+ borderRadius: extractCaptionBackgroundBorderRadius(asset)
5095
+ });
5096
+ }
5097
+ const borderConfig = extractCaptionBorder(asset);
5098
+ if (borderConfig) {
5099
+ const halfBorder = borderConfig.width / 2;
5100
+ ops.push({
5101
+ op: "RectangleStroke",
5102
+ x: bgX + halfBorder,
5103
+ y: bgY + halfBorder,
5104
+ width: bgWidth - borderConfig.width,
5105
+ height: bgHeight - borderConfig.width,
5106
+ stroke: {
5107
+ width: borderConfig.width,
5108
+ color: borderConfig.color,
5109
+ opacity: borderConfig.opacity
5110
+ },
5111
+ borderRadius: borderConfig.radius > 0 ? borderConfig.radius : void 0
5058
5112
  });
5059
5113
  }
5060
5114
  }
@@ -5878,18 +5932,28 @@ var RichCaptionRenderer = class {
5878
5932
  const font = asset.font;
5879
5933
  const style = asset.style;
5880
5934
  const measureTextWidth = await createCanvasTextMeasurer();
5935
+ const fontSize = font?.size ?? 24;
5936
+ const lineHeight = style?.lineHeight ?? 1.2;
5937
+ const padding = this.extractPadding(asset);
5938
+ const availableWidth = this.width - padding.left - padding.right;
5939
+ const availableHeight = this.height - padding.top - padding.bottom;
5940
+ const computedMaxLines = Math.max(1, Math.min(10, Math.floor(availableHeight / (fontSize * lineHeight))));
5941
+ const verticalAlign = this.mapVerticalAlign(asset);
5942
+ const horizontalAlign = this.mapHorizontalAlign(asset);
5881
5943
  const layoutConfig = {
5882
5944
  frameWidth: this.width,
5883
5945
  frameHeight: this.height,
5884
- maxWidth: asset.maxWidth ?? 0.9,
5885
- maxLines: asset.maxLines ?? 2,
5886
- position: asset.position ?? "bottom",
5887
- fontSize: font?.size ?? 24,
5946
+ availableWidth,
5947
+ maxLines: computedMaxLines,
5948
+ verticalAlign,
5949
+ horizontalAlign,
5950
+ paddingLeft: padding.left,
5951
+ fontSize,
5888
5952
  fontFamily: font?.family ?? "Roboto",
5889
5953
  fontWeight: String(font?.weight ?? "400"),
5890
5954
  letterSpacing: style?.letterSpacing ?? 0,
5891
5955
  wordSpacing: typeof style?.wordSpacing === "number" ? style.wordSpacing : 0,
5892
- lineHeight: style?.lineHeight ?? 1.2,
5956
+ lineHeight,
5893
5957
  textTransform: style?.textTransform ?? "none",
5894
5958
  pauseThreshold: 500,
5895
5959
  measureTextWidth
@@ -6001,6 +6065,7 @@ var RichCaptionRenderer = class {
6001
6065
  const totalTimeMs = performance.now() - totalStart;
6002
6066
  const realtimeMultiplier = duration / (totalTimeMs / 1e3);
6003
6067
  this.logCompletion(totalTimeMs, realtimeMultiplier);
6068
+ encoder.close();
6004
6069
  return outputPath;
6005
6070
  } catch (error) {
6006
6071
  encoder.close();
@@ -6117,6 +6182,33 @@ var RichCaptionRenderer = class {
6117
6182
  clearCache() {
6118
6183
  this.layoutEngine?.clearCache();
6119
6184
  }
6185
+ extractPadding(asset) {
6186
+ const padding = asset.padding;
6187
+ if (!padding) {
6188
+ return { top: 0, right: 0, bottom: 0, left: 0 };
6189
+ }
6190
+ if (typeof padding === "number") {
6191
+ return { top: padding, right: padding, bottom: padding, left: padding };
6192
+ }
6193
+ return {
6194
+ top: padding.top ?? 0,
6195
+ right: padding.right ?? 0,
6196
+ bottom: padding.bottom ?? 0,
6197
+ left: padding.left ?? 0
6198
+ };
6199
+ }
6200
+ mapVerticalAlign(asset) {
6201
+ const vertical = asset.align?.vertical;
6202
+ if (vertical === "top") return "top";
6203
+ if (vertical === "middle") return "middle";
6204
+ return "bottom";
6205
+ }
6206
+ mapHorizontalAlign(asset) {
6207
+ const horizontal = asset.align?.horizontal;
6208
+ if (horizontal === "left") return "left";
6209
+ if (horizontal === "right") return "right";
6210
+ return "center";
6211
+ }
6120
6212
  extractAnimationStyle() {
6121
6213
  const wordAnim = this.currentAsset?.wordAnimation;
6122
6214
  return wordAnim?.style ?? "highlight";
@@ -263,7 +263,7 @@ declare const richCaptionAssetSchema: z.ZodObject<{
263
263
  start: z.ZodNumber;
264
264
  end: z.ZodNumber;
265
265
  confidence: z.ZodOptional<z.ZodNumber>;
266
- }, z.core.$strict>>>;
266
+ }, z.core.$strip>>>;
267
267
  font: z.ZodOptional<z.ZodObject<{
268
268
  family: z.ZodDefault<z.ZodString>;
269
269
  size: z.ZodDefault<z.ZodNumber>;
@@ -316,6 +316,12 @@ declare const richCaptionAssetSchema: z.ZodObject<{
316
316
  color: z.ZodOptional<z.ZodString>;
317
317
  opacity: z.ZodDefault<z.ZodNumber>;
318
318
  }, z.core.$strict>>;
319
+ border: z.ZodOptional<z.ZodObject<{
320
+ width: z.ZodDefault<z.ZodNumber>;
321
+ color: z.ZodDefault<z.ZodString>;
322
+ opacity: z.ZodDefault<z.ZodNumber>;
323
+ radius: z.ZodDefault<z.ZodNumber>;
324
+ }, z.core.$strip>>;
319
325
  padding: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
320
326
  top: z.ZodDefault<z.ZodNumber>;
321
327
  right: z.ZodDefault<z.ZodNumber>;
@@ -366,13 +372,6 @@ declare const richCaptionAssetSchema: z.ZodObject<{
366
372
  down: "down";
367
373
  }>>;
368
374
  }, z.core.$strict>>;
369
- position: z.ZodDefault<z.ZodEnum<{
370
- center: "center";
371
- top: "top";
372
- bottom: "bottom";
373
- }>>;
374
- maxWidth: z.ZodDefault<z.ZodNumber>;
375
- maxLines: z.ZodDefault<z.ZodNumber>;
376
375
  customFonts: z.ZodOptional<z.ZodArray<z.ZodObject<{
377
376
  src: z.ZodString;
378
377
  family: z.ZodString;
@@ -389,7 +388,7 @@ declare const CanvasRichCaptionAssetSchema: z.ZodObject<{
389
388
  start: z.ZodNumber;
390
389
  end: z.ZodNumber;
391
390
  confidence: z.ZodOptional<z.ZodNumber>;
392
- }, z.core.$strict>>>;
391
+ }, z.core.$strip>>>;
393
392
  font: z.ZodOptional<z.ZodObject<{
394
393
  family: z.ZodDefault<z.ZodString>;
395
394
  size: z.ZodDefault<z.ZodNumber>;
@@ -442,6 +441,12 @@ declare const CanvasRichCaptionAssetSchema: z.ZodObject<{
442
441
  color: z.ZodOptional<z.ZodString>;
443
442
  opacity: z.ZodDefault<z.ZodNumber>;
444
443
  }, z.core.$strict>>;
444
+ border: z.ZodOptional<z.ZodObject<{
445
+ width: z.ZodDefault<z.ZodNumber>;
446
+ color: z.ZodDefault<z.ZodString>;
447
+ opacity: z.ZodDefault<z.ZodNumber>;
448
+ radius: z.ZodDefault<z.ZodNumber>;
449
+ }, z.core.$strip>>;
445
450
  padding: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
446
451
  top: z.ZodDefault<z.ZodNumber>;
447
452
  right: z.ZodDefault<z.ZodNumber>;
@@ -492,13 +497,6 @@ declare const CanvasRichCaptionAssetSchema: z.ZodObject<{
492
497
  down: "down";
493
498
  }>>;
494
499
  }, z.core.$strict>>;
495
- position: z.ZodDefault<z.ZodEnum<{
496
- center: "center";
497
- top: "top";
498
- bottom: "bottom";
499
- }>>;
500
- maxWidth: z.ZodDefault<z.ZodNumber>;
501
- maxLines: z.ZodDefault<z.ZodNumber>;
502
500
  customFonts: z.ZodOptional<z.ZodArray<z.ZodObject<{
503
501
  src: z.ZodString;
504
502
  family: z.ZodString;
@@ -603,9 +601,11 @@ interface WordTiming {
603
601
  interface CaptionLayoutConfig {
604
602
  frameWidth: number;
605
603
  frameHeight: number;
606
- maxWidth: number;
604
+ availableWidth: number;
607
605
  maxLines: number;
608
- position: "top" | "center" | "bottom";
606
+ verticalAlign: "top" | "middle" | "bottom";
607
+ horizontalAlign: "left" | "center" | "right";
608
+ paddingLeft: number;
609
609
  fontSize: number;
610
610
  fontFamily: string;
611
611
  fontWeight: string | number;
@@ -1184,6 +1184,9 @@ declare class RichCaptionRenderer {
1184
1184
  getStats(): RenderStats;
1185
1185
  resetStats(): void;
1186
1186
  clearCache(): void;
1187
+ private extractPadding;
1188
+ private mapVerticalAlign;
1189
+ private mapHorizontalAlign;
1187
1190
  private extractAnimationStyle;
1188
1191
  private extractAnimationSpeed;
1189
1192
  private logProgress;
@@ -263,7 +263,7 @@ declare const richCaptionAssetSchema: z.ZodObject<{
263
263
  start: z.ZodNumber;
264
264
  end: z.ZodNumber;
265
265
  confidence: z.ZodOptional<z.ZodNumber>;
266
- }, z.core.$strict>>>;
266
+ }, z.core.$strip>>>;
267
267
  font: z.ZodOptional<z.ZodObject<{
268
268
  family: z.ZodDefault<z.ZodString>;
269
269
  size: z.ZodDefault<z.ZodNumber>;
@@ -316,6 +316,12 @@ declare const richCaptionAssetSchema: z.ZodObject<{
316
316
  color: z.ZodOptional<z.ZodString>;
317
317
  opacity: z.ZodDefault<z.ZodNumber>;
318
318
  }, z.core.$strict>>;
319
+ border: z.ZodOptional<z.ZodObject<{
320
+ width: z.ZodDefault<z.ZodNumber>;
321
+ color: z.ZodDefault<z.ZodString>;
322
+ opacity: z.ZodDefault<z.ZodNumber>;
323
+ radius: z.ZodDefault<z.ZodNumber>;
324
+ }, z.core.$strip>>;
319
325
  padding: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
320
326
  top: z.ZodDefault<z.ZodNumber>;
321
327
  right: z.ZodDefault<z.ZodNumber>;
@@ -366,13 +372,6 @@ declare const richCaptionAssetSchema: z.ZodObject<{
366
372
  down: "down";
367
373
  }>>;
368
374
  }, z.core.$strict>>;
369
- position: z.ZodDefault<z.ZodEnum<{
370
- center: "center";
371
- top: "top";
372
- bottom: "bottom";
373
- }>>;
374
- maxWidth: z.ZodDefault<z.ZodNumber>;
375
- maxLines: z.ZodDefault<z.ZodNumber>;
376
375
  customFonts: z.ZodOptional<z.ZodArray<z.ZodObject<{
377
376
  src: z.ZodString;
378
377
  family: z.ZodString;
@@ -389,7 +388,7 @@ declare const CanvasRichCaptionAssetSchema: z.ZodObject<{
389
388
  start: z.ZodNumber;
390
389
  end: z.ZodNumber;
391
390
  confidence: z.ZodOptional<z.ZodNumber>;
392
- }, z.core.$strict>>>;
391
+ }, z.core.$strip>>>;
393
392
  font: z.ZodOptional<z.ZodObject<{
394
393
  family: z.ZodDefault<z.ZodString>;
395
394
  size: z.ZodDefault<z.ZodNumber>;
@@ -442,6 +441,12 @@ declare const CanvasRichCaptionAssetSchema: z.ZodObject<{
442
441
  color: z.ZodOptional<z.ZodString>;
443
442
  opacity: z.ZodDefault<z.ZodNumber>;
444
443
  }, z.core.$strict>>;
444
+ border: z.ZodOptional<z.ZodObject<{
445
+ width: z.ZodDefault<z.ZodNumber>;
446
+ color: z.ZodDefault<z.ZodString>;
447
+ opacity: z.ZodDefault<z.ZodNumber>;
448
+ radius: z.ZodDefault<z.ZodNumber>;
449
+ }, z.core.$strip>>;
445
450
  padding: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
446
451
  top: z.ZodDefault<z.ZodNumber>;
447
452
  right: z.ZodDefault<z.ZodNumber>;
@@ -492,13 +497,6 @@ declare const CanvasRichCaptionAssetSchema: z.ZodObject<{
492
497
  down: "down";
493
498
  }>>;
494
499
  }, z.core.$strict>>;
495
- position: z.ZodDefault<z.ZodEnum<{
496
- center: "center";
497
- top: "top";
498
- bottom: "bottom";
499
- }>>;
500
- maxWidth: z.ZodDefault<z.ZodNumber>;
501
- maxLines: z.ZodDefault<z.ZodNumber>;
502
500
  customFonts: z.ZodOptional<z.ZodArray<z.ZodObject<{
503
501
  src: z.ZodString;
504
502
  family: z.ZodString;
@@ -603,9 +601,11 @@ interface WordTiming {
603
601
  interface CaptionLayoutConfig {
604
602
  frameWidth: number;
605
603
  frameHeight: number;
606
- maxWidth: number;
604
+ availableWidth: number;
607
605
  maxLines: number;
608
- position: "top" | "center" | "bottom";
606
+ verticalAlign: "top" | "middle" | "bottom";
607
+ horizontalAlign: "left" | "center" | "right";
608
+ paddingLeft: number;
609
609
  fontSize: number;
610
610
  fontFamily: string;
611
611
  fontWeight: string | number;
@@ -1184,6 +1184,9 @@ declare class RichCaptionRenderer {
1184
1184
  getStats(): RenderStats;
1185
1185
  resetStats(): void;
1186
1186
  clearCache(): void;
1187
+ private extractPadding;
1188
+ private mapVerticalAlign;
1189
+ private mapHorizontalAlign;
1187
1190
  private extractAnimationStyle;
1188
1191
  private extractAnimationSpeed;
1189
1192
  private logProgress;