@shotstack/shotstack-canvas 2.0.3 → 2.0.5

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.
@@ -362,6 +362,7 @@ __export(entry_node_exports, {
362
362
  RichCaptionRenderer: () => RichCaptionRenderer,
363
363
  WordTimingStore: () => WordTimingStore,
364
364
  arcToCubicBeziers: () => arcToCubicBeziers,
365
+ breakIntoLines: () => breakIntoLines,
365
366
  calculateAnimationStatesForGroup: () => calculateAnimationStatesForGroup,
366
367
  commandsToPathString: () => commandsToPathString,
367
368
  computeSimplePathBounds: () => computeSimplePathBounds,
@@ -417,7 +418,7 @@ var CANVAS_CONFIG = {
417
418
  width: 800,
418
419
  height: 400,
419
420
  pixelRatio: 2,
420
- fontFamily: "Roboto",
421
+ fontFamily: "Open Sans",
421
422
  fontSize: 48,
422
423
  color: "#ffffff",
423
424
  textAlign: "center"
@@ -572,7 +573,7 @@ var wordTimingSchema = import_zod2.wordTimingSchema.extend({
572
573
  confidence: import_zod.z.number().min(0).max(1).optional()
573
574
  });
574
575
  var richCaptionFontSchema = import_zod.z.object({
575
- family: import_zod.z.string().default("Open Sans"),
576
+ family: import_zod.z.string().default(CANVAS_CONFIG.DEFAULTS.fontFamily),
576
577
  size: import_zod.z.number().int().min(1).max(500).default(24),
577
578
  weight: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).default("400"),
578
579
  color: import_zod.z.string().regex(HEX6).default("#ffffff"),
@@ -581,7 +582,7 @@ var richCaptionFontSchema = import_zod.z.object({
581
582
  });
582
583
  var richCaptionActiveSchema = import_zod2.richCaptionActiveSchema.extend({
583
584
  font: import_zod.z.object({
584
- color: import_zod.z.string().regex(HEX6).default("#ffff00"),
585
+ color: import_zod.z.string().regex(HEX6).default("#ffffff"),
585
586
  background: import_zod.z.string().regex(HEX6).optional(),
586
587
  opacity: import_zod.z.number().min(0).max(1).default(1)
587
588
  }).optional(),
@@ -4328,7 +4329,7 @@ function groupWordsByPause(store, pauseThreshold = 500) {
4328
4329
  }
4329
4330
  return groups;
4330
4331
  }
4331
- function breakIntoLines(wordWidths, maxWidth, maxLines, spaceWidth) {
4332
+ function breakIntoLines(wordWidths, maxWidth, spaceWidth) {
4332
4333
  const lines = [];
4333
4334
  let currentLine = [];
4334
4335
  let currentWidth = 0;
@@ -4341,15 +4342,12 @@ function breakIntoLines(wordWidths, maxWidth, maxLines, spaceWidth) {
4341
4342
  } else {
4342
4343
  if (currentLine.length > 0) {
4343
4344
  lines.push(currentLine);
4344
- if (lines.length >= maxLines) {
4345
- return lines;
4346
- }
4347
4345
  }
4348
4346
  currentLine = [i];
4349
4347
  currentWidth = wordWidth;
4350
4348
  }
4351
4349
  }
4352
- if (currentLine.length > 0 && lines.length < maxLines) {
4350
+ if (currentLine.length > 0) {
4353
4351
  lines.push(currentLine);
4354
4352
  }
4355
4353
  return lines;
@@ -4382,6 +4380,13 @@ function transformText(text, transform) {
4382
4380
  return text;
4383
4381
  }
4384
4382
  }
4383
+ function splitIntoChunks(arr, chunkSize) {
4384
+ const chunks = [];
4385
+ for (let i = 0; i < arr.length; i += chunkSize) {
4386
+ chunks.push(arr.slice(i, i + chunkSize));
4387
+ }
4388
+ return chunks;
4389
+ }
4385
4390
  var CaptionLayoutEngine = class {
4386
4391
  fontRegistry;
4387
4392
  cache;
@@ -4468,31 +4473,34 @@ var CaptionLayoutEngine = class {
4468
4473
  const spaceWord = await this.measureWord(" ", measurementConfig);
4469
4474
  spaceWidth = spaceWord.width + config.wordSpacing;
4470
4475
  }
4471
- const groups = wordGroups.map((indices) => {
4476
+ const groups = wordGroups.flatMap((indices) => {
4472
4477
  const groupWidths = indices.map((i) => store.widths[i]);
4473
- const lineIndices = breakIntoLines(
4478
+ const allLines = breakIntoLines(
4474
4479
  groupWidths,
4475
4480
  pixelMaxWidth,
4476
- config.maxLines,
4477
4481
  spaceWidth
4478
4482
  );
4479
- const lines = lineIndices.map((lineWordIndices, lineIndex) => {
4480
- const actualIndices = lineWordIndices.map((i) => indices[i]);
4481
- const lineWidth = actualIndices.reduce((sum, idx) => sum + store.widths[idx], 0) + (actualIndices.length - 1) * spaceWidth;
4483
+ const lineChunks = splitIntoChunks(allLines, config.maxLines);
4484
+ return lineChunks.map((chunkLines) => {
4485
+ const lines = chunkLines.map((lineWordIndices, lineIndex) => {
4486
+ const actualIndices = lineWordIndices.map((i) => indices[i]);
4487
+ const lineWidth = actualIndices.reduce((sum, idx) => sum + store.widths[idx], 0) + (actualIndices.length - 1) * spaceWidth;
4488
+ return {
4489
+ wordIndices: actualIndices,
4490
+ x: 0,
4491
+ y: lineIndex * config.fontSize * config.lineHeight,
4492
+ width: lineWidth,
4493
+ height: config.fontSize
4494
+ };
4495
+ });
4496
+ const allWordIndices = lines.flatMap((l) => l.wordIndices);
4482
4497
  return {
4483
- wordIndices: actualIndices,
4484
- x: 0,
4485
- y: lineIndex * config.fontSize * config.lineHeight,
4486
- width: lineWidth,
4487
- height: config.fontSize
4498
+ wordIndices: allWordIndices,
4499
+ startTime: store.startTimes[allWordIndices[0]],
4500
+ endTime: store.endTimes[allWordIndices[allWordIndices.length - 1]],
4501
+ lines
4488
4502
  };
4489
4503
  });
4490
- return {
4491
- wordIndices: lines.flatMap((l) => l.wordIndices),
4492
- startTime: store.startTimes[indices[0]],
4493
- endTime: store.endTimes[indices[indices.length - 1]],
4494
- lines
4495
- };
4496
4504
  });
4497
4505
  const calculateGroupY = (group) => {
4498
4506
  const totalHeight = group.lines.length * config.fontSize * config.lineHeight;
@@ -4867,11 +4875,11 @@ function extractFontConfig(asset) {
4867
4875
  const font = asset.font;
4868
4876
  const active = asset.active?.font;
4869
4877
  return {
4870
- family: font?.family ?? "Open Sans",
4878
+ family: font?.family ?? CANVAS_CONFIG.DEFAULTS.fontFamily,
4871
4879
  size: font?.size ?? 24,
4872
4880
  weight: String(font?.weight ?? "400"),
4873
4881
  baseColor: font?.color ?? "#ffffff",
4874
- activeColor: active?.color ?? "#ffff00",
4882
+ activeColor: active?.color ?? "#ffffff",
4875
4883
  baseOpacity: font?.opacity ?? 1,
4876
4884
  activeOpacity: active?.opacity ?? 1,
4877
4885
  letterSpacing: asset.style?.letterSpacing ?? 0
@@ -5773,15 +5781,14 @@ async function createNodeRawEncoder(config, options) {
5773
5781
  }
5774
5782
 
5775
5783
  // src/core/rich-caption-renderer.ts
5776
- var ROBOTO_FONT_URLS = {
5777
- "100": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbGmT.ttf",
5778
- "300": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuaabWmT.ttf",
5779
- "400": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbWmT.ttf",
5780
- "500": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWub2bWmT.ttf",
5781
- "600": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuYaammT.ttf",
5782
- "700": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuYjammT.ttf",
5783
- "800": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuZEammT.ttf",
5784
- "900": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuZtammT.ttf"
5784
+ var OPEN_SANS_FONT_URL = "https://fonts.gstatic.com/s/opensans/v44/mem8YaGs126MiZpBA-U1UpcaXcl0Aw.ttf";
5785
+ var OPEN_SANS_FONT_URLS = {
5786
+ "300": OPEN_SANS_FONT_URL,
5787
+ "400": OPEN_SANS_FONT_URL,
5788
+ "500": OPEN_SANS_FONT_URL,
5789
+ "600": OPEN_SANS_FONT_URL,
5790
+ "700": OPEN_SANS_FONT_URL,
5791
+ "800": OPEN_SANS_FONT_URL
5785
5792
  };
5786
5793
  var RichCaptionRenderer = class {
5787
5794
  width;
@@ -5811,12 +5818,12 @@ var RichCaptionRenderer = class {
5811
5818
  async initialize() {
5812
5819
  this.fontRegistry = await FontRegistry.getSharedInstance(this.wasmBaseURL);
5813
5820
  this.layoutEngine = new CaptionLayoutEngine(this.fontRegistry);
5814
- const weightsToLoad = Object.keys(ROBOTO_FONT_URLS);
5821
+ const weightsToLoad = Object.keys(OPEN_SANS_FONT_URLS);
5815
5822
  const loadPromises = weightsToLoad.map(async (weight) => {
5816
- const existingFace = await this.fontRegistry.getFace({ family: "Roboto", weight });
5823
+ const existingFace = await this.fontRegistry.getFace({ family: "Open Sans", weight });
5817
5824
  if (!existingFace) {
5818
- const bytes = await loadFileOrHttpToArrayBuffer(ROBOTO_FONT_URLS[weight]);
5819
- await this.fontRegistry.registerFromBytes(bytes, { family: "Roboto", weight });
5825
+ const bytes = await loadFileOrHttpToArrayBuffer(OPEN_SANS_FONT_URLS[weight]);
5826
+ await this.fontRegistry.registerFromBytes(bytes, { family: "Open Sans", weight });
5820
5827
  }
5821
5828
  });
5822
5829
  await Promise.all(loadPromises);
@@ -5860,7 +5867,7 @@ var RichCaptionRenderer = class {
5860
5867
  maxLines: asset.maxLines ?? 2,
5861
5868
  position: asset.position ?? "bottom",
5862
5869
  fontSize: font?.size ?? 24,
5863
- fontFamily: font?.family ?? "Roboto",
5870
+ fontFamily: font?.family ?? "Open Sans",
5864
5871
  fontWeight: String(font?.weight ?? "400"),
5865
5872
  letterSpacing: style?.letterSpacing ?? 0,
5866
5873
  wordSpacing: typeof style?.wordSpacing === "number" ? style.wordSpacing : 0,
@@ -5953,7 +5960,15 @@ var RichCaptionRenderer = class {
5953
5960
  const elapsed = performance.now() - totalStart;
5954
5961
  const fps = framesProcessed / (elapsed / 1e3);
5955
5962
  const eta = (schedule.totalFrames - framesProcessed) / fps * 1e3;
5956
- this.logProgress(pct, framesProcessed, schedule.totalFrames, i + 1, schedule.uniqueFrameCount, fps, eta);
5963
+ this.logProgress(
5964
+ pct,
5965
+ framesProcessed,
5966
+ schedule.totalFrames,
5967
+ i + 1,
5968
+ schedule.uniqueFrameCount,
5969
+ fps,
5970
+ eta
5971
+ );
5957
5972
  }
5958
5973
  if (i % 500 === 0 && i > 0) {
5959
5974
  this.checkMemoryUsage();
@@ -6333,7 +6348,7 @@ async function createTextEngine(opts = {}) {
6333
6348
  }
6334
6349
  }
6335
6350
  const main = asset.font ?? {
6336
- family: "Roboto",
6351
+ family: "Open Sans",
6337
6352
  weight: "400",
6338
6353
  size: 48,
6339
6354
  color: "#000000",
@@ -6562,6 +6577,7 @@ async function createTextEngine(opts = {}) {
6562
6577
  RichCaptionRenderer,
6563
6578
  WordTimingStore,
6564
6579
  arcToCubicBeziers,
6580
+ breakIntoLines,
6565
6581
  calculateAnimationStatesForGroup,
6566
6582
  commandsToPathString,
6567
6583
  computeSimplePathBounds,
@@ -670,6 +670,7 @@ declare class WordTimingStore {
670
670
  }
671
671
  declare function findWordAtTime(store: WordTimingStore, timeMs: number): number;
672
672
  declare function groupWordsByPause(store: WordTimingStore, pauseThreshold?: number): number[][];
673
+ declare function breakIntoLines(wordWidths: number[], maxWidth: number, spaceWidth: number): number[][];
673
674
  declare class CaptionLayoutEngine {
674
675
  private fontRegistry;
675
676
  private cache;
@@ -1314,4 +1315,4 @@ declare function createTextEngine(opts?: {
1314
1315
  destroy(): void;
1315
1316
  }>;
1316
1317
 
1317
- export { type AnimationDirection, type AnimationStyle, type ArcCommand, type BackgroundConfig, type BoundingBox, type CanvasRichCaptionAsset, CanvasRichCaptionAssetSchema, type CanvasRichTextAsset, CanvasRichTextAssetSchema, type CanvasSvgAsset, CanvasSvgAssetSchema, type CaptionGroup, type CaptionLayout, type CaptionLayoutConfig, CaptionLayoutEngine, type CaptionLine, type ClosePathCommand, type CubicBezierCommand, type DrawOp, type EngineInit, type FastVideoOptions, type FastVideoResult, type FontConfig, FontRegistry, type FrameSchedule, type Glyph, type GradientSpec, type IVideoEncoder, type LineToCommand, type MoveToCommand, NodeRawEncoder, type NormalizedPathCommand, type ParsedPathCommand, type PathCommandType, type Point2D, type PositionedWord, type QuadraticBezierCommand, type RGBA, type RenderFrame, type RenderStats, type Renderer, type ResvgRenderOptions, type ResvgRenderResult, type RichCaptionGeneratorConfig, RichCaptionRenderer, type RichCaptionRendererOptions, type ShadowConfig, type ShapedLine, type ShapedWord, type ShapedWordGlyph, type ShotstackRichTextAsset, type ShotstackSvgAsset, type StrokeConfig, type StrokeSpec, type ValidAsset, type VideoEncoderCapabilities, type VideoEncoderConfig, type VideoEncoderProgress, type WordAnimationConfig, type WordAnimationState, type WordTiming, WordTimingStore, arcToCubicBeziers, calculateAnimationStatesForGroup, commandsToPathString, computeSimplePathBounds, createDefaultGeneratorConfig, createFrameSchedule, createNodePainter, createNodeRawEncoder, createRichCaptionRenderer, createTextEngine, createVideoEncoder, detectPlatform, detectSubtitleFormat, findWordAtTime, generateRichCaptionDrawOps, generateRichCaptionFrame, generateShapePathData, getDefaultAnimationConfig, getDrawCaptionWordOps, getEncoderCapabilities, getEncoderWarning, groupWordsByPause, isDrawCaptionWordOp, isRTLText, isWebCodecsH264Supported, normalizePath, normalizePathString, parseSubtitleToWords, parseSvgPath, quadraticToCubic, renderSvgAssetToPng, renderSvgToPng, richCaptionAssetSchema, shapeToSvgString };
1318
+ export { type AnimationDirection, type AnimationStyle, type ArcCommand, type BackgroundConfig, type BoundingBox, type CanvasRichCaptionAsset, CanvasRichCaptionAssetSchema, type CanvasRichTextAsset, CanvasRichTextAssetSchema, type CanvasSvgAsset, CanvasSvgAssetSchema, type CaptionGroup, type CaptionLayout, type CaptionLayoutConfig, CaptionLayoutEngine, type CaptionLine, type ClosePathCommand, type CubicBezierCommand, type DrawOp, type EngineInit, type FastVideoOptions, type FastVideoResult, type FontConfig, FontRegistry, type FrameSchedule, type Glyph, type GradientSpec, type IVideoEncoder, type LineToCommand, type MoveToCommand, NodeRawEncoder, type NormalizedPathCommand, type ParsedPathCommand, type PathCommandType, type Point2D, type PositionedWord, type QuadraticBezierCommand, type RGBA, type RenderFrame, type RenderStats, type Renderer, type ResvgRenderOptions, type ResvgRenderResult, type RichCaptionGeneratorConfig, RichCaptionRenderer, type RichCaptionRendererOptions, type ShadowConfig, type ShapedLine, type ShapedWord, type ShapedWordGlyph, type ShotstackRichTextAsset, type ShotstackSvgAsset, type StrokeConfig, type StrokeSpec, type ValidAsset, type VideoEncoderCapabilities, type VideoEncoderConfig, type VideoEncoderProgress, type WordAnimationConfig, type WordAnimationState, type WordTiming, WordTimingStore, arcToCubicBeziers, breakIntoLines, calculateAnimationStatesForGroup, commandsToPathString, computeSimplePathBounds, createDefaultGeneratorConfig, createFrameSchedule, createNodePainter, createNodeRawEncoder, createRichCaptionRenderer, createTextEngine, createVideoEncoder, detectPlatform, detectSubtitleFormat, findWordAtTime, generateRichCaptionDrawOps, generateRichCaptionFrame, generateShapePathData, getDefaultAnimationConfig, getDrawCaptionWordOps, getEncoderCapabilities, getEncoderWarning, groupWordsByPause, isDrawCaptionWordOp, isRTLText, isWebCodecsH264Supported, normalizePath, normalizePathString, parseSubtitleToWords, parseSvgPath, quadraticToCubic, renderSvgAssetToPng, renderSvgToPng, richCaptionAssetSchema, shapeToSvgString };
@@ -670,6 +670,7 @@ declare class WordTimingStore {
670
670
  }
671
671
  declare function findWordAtTime(store: WordTimingStore, timeMs: number): number;
672
672
  declare function groupWordsByPause(store: WordTimingStore, pauseThreshold?: number): number[][];
673
+ declare function breakIntoLines(wordWidths: number[], maxWidth: number, spaceWidth: number): number[][];
673
674
  declare class CaptionLayoutEngine {
674
675
  private fontRegistry;
675
676
  private cache;
@@ -1314,4 +1315,4 @@ declare function createTextEngine(opts?: {
1314
1315
  destroy(): void;
1315
1316
  }>;
1316
1317
 
1317
- export { type AnimationDirection, type AnimationStyle, type ArcCommand, type BackgroundConfig, type BoundingBox, type CanvasRichCaptionAsset, CanvasRichCaptionAssetSchema, type CanvasRichTextAsset, CanvasRichTextAssetSchema, type CanvasSvgAsset, CanvasSvgAssetSchema, type CaptionGroup, type CaptionLayout, type CaptionLayoutConfig, CaptionLayoutEngine, type CaptionLine, type ClosePathCommand, type CubicBezierCommand, type DrawOp, type EngineInit, type FastVideoOptions, type FastVideoResult, type FontConfig, FontRegistry, type FrameSchedule, type Glyph, type GradientSpec, type IVideoEncoder, type LineToCommand, type MoveToCommand, NodeRawEncoder, type NormalizedPathCommand, type ParsedPathCommand, type PathCommandType, type Point2D, type PositionedWord, type QuadraticBezierCommand, type RGBA, type RenderFrame, type RenderStats, type Renderer, type ResvgRenderOptions, type ResvgRenderResult, type RichCaptionGeneratorConfig, RichCaptionRenderer, type RichCaptionRendererOptions, type ShadowConfig, type ShapedLine, type ShapedWord, type ShapedWordGlyph, type ShotstackRichTextAsset, type ShotstackSvgAsset, type StrokeConfig, type StrokeSpec, type ValidAsset, type VideoEncoderCapabilities, type VideoEncoderConfig, type VideoEncoderProgress, type WordAnimationConfig, type WordAnimationState, type WordTiming, WordTimingStore, arcToCubicBeziers, calculateAnimationStatesForGroup, commandsToPathString, computeSimplePathBounds, createDefaultGeneratorConfig, createFrameSchedule, createNodePainter, createNodeRawEncoder, createRichCaptionRenderer, createTextEngine, createVideoEncoder, detectPlatform, detectSubtitleFormat, findWordAtTime, generateRichCaptionDrawOps, generateRichCaptionFrame, generateShapePathData, getDefaultAnimationConfig, getDrawCaptionWordOps, getEncoderCapabilities, getEncoderWarning, groupWordsByPause, isDrawCaptionWordOp, isRTLText, isWebCodecsH264Supported, normalizePath, normalizePathString, parseSubtitleToWords, parseSvgPath, quadraticToCubic, renderSvgAssetToPng, renderSvgToPng, richCaptionAssetSchema, shapeToSvgString };
1318
+ export { type AnimationDirection, type AnimationStyle, type ArcCommand, type BackgroundConfig, type BoundingBox, type CanvasRichCaptionAsset, CanvasRichCaptionAssetSchema, type CanvasRichTextAsset, CanvasRichTextAssetSchema, type CanvasSvgAsset, CanvasSvgAssetSchema, type CaptionGroup, type CaptionLayout, type CaptionLayoutConfig, CaptionLayoutEngine, type CaptionLine, type ClosePathCommand, type CubicBezierCommand, type DrawOp, type EngineInit, type FastVideoOptions, type FastVideoResult, type FontConfig, FontRegistry, type FrameSchedule, type Glyph, type GradientSpec, type IVideoEncoder, type LineToCommand, type MoveToCommand, NodeRawEncoder, type NormalizedPathCommand, type ParsedPathCommand, type PathCommandType, type Point2D, type PositionedWord, type QuadraticBezierCommand, type RGBA, type RenderFrame, type RenderStats, type Renderer, type ResvgRenderOptions, type ResvgRenderResult, type RichCaptionGeneratorConfig, RichCaptionRenderer, type RichCaptionRendererOptions, type ShadowConfig, type ShapedLine, type ShapedWord, type ShapedWordGlyph, type ShotstackRichTextAsset, type ShotstackSvgAsset, type StrokeConfig, type StrokeSpec, type ValidAsset, type VideoEncoderCapabilities, type VideoEncoderConfig, type VideoEncoderProgress, type WordAnimationConfig, type WordAnimationState, type WordTiming, WordTimingStore, arcToCubicBeziers, breakIntoLines, calculateAnimationStatesForGroup, commandsToPathString, computeSimplePathBounds, createDefaultGeneratorConfig, createFrameSchedule, createNodePainter, createNodeRawEncoder, createRichCaptionRenderer, createTextEngine, createVideoEncoder, detectPlatform, detectSubtitleFormat, findWordAtTime, generateRichCaptionDrawOps, generateRichCaptionFrame, generateShapePathData, getDefaultAnimationConfig, getDrawCaptionWordOps, getEncoderCapabilities, getEncoderWarning, groupWordsByPause, isDrawCaptionWordOp, isRTLText, isWebCodecsH264Supported, normalizePath, normalizePathString, parseSubtitleToWords, parseSvgPath, quadraticToCubic, renderSvgAssetToPng, renderSvgToPng, richCaptionAssetSchema, shapeToSvgString };
@@ -29,7 +29,7 @@ var CANVAS_CONFIG = {
29
29
  width: 800,
30
30
  height: 400,
31
31
  pixelRatio: 2,
32
- fontFamily: "Roboto",
32
+ fontFamily: "Open Sans",
33
33
  fontSize: 48,
34
34
  color: "#ffffff",
35
35
  textAlign: "center"
@@ -184,7 +184,7 @@ var wordTimingSchema = baseWordTimingSchema.extend({
184
184
  confidence: z.number().min(0).max(1).optional()
185
185
  });
186
186
  var richCaptionFontSchema = z.object({
187
- family: z.string().default("Open Sans"),
187
+ family: z.string().default(CANVAS_CONFIG.DEFAULTS.fontFamily),
188
188
  size: z.number().int().min(1).max(500).default(24),
189
189
  weight: z.union([z.string(), z.number()]).default("400"),
190
190
  color: z.string().regex(HEX6).default("#ffffff"),
@@ -193,7 +193,7 @@ var richCaptionFontSchema = z.object({
193
193
  });
194
194
  var richCaptionActiveSchema = baseCaptionActiveSchema.extend({
195
195
  font: z.object({
196
- color: z.string().regex(HEX6).default("#ffff00"),
196
+ color: z.string().regex(HEX6).default("#ffffff"),
197
197
  background: z.string().regex(HEX6).optional(),
198
198
  opacity: z.number().min(0).max(1).default(1)
199
199
  }).optional(),
@@ -3939,7 +3939,7 @@ function groupWordsByPause(store, pauseThreshold = 500) {
3939
3939
  }
3940
3940
  return groups;
3941
3941
  }
3942
- function breakIntoLines(wordWidths, maxWidth, maxLines, spaceWidth) {
3942
+ function breakIntoLines(wordWidths, maxWidth, spaceWidth) {
3943
3943
  const lines = [];
3944
3944
  let currentLine = [];
3945
3945
  let currentWidth = 0;
@@ -3952,15 +3952,12 @@ function breakIntoLines(wordWidths, maxWidth, maxLines, spaceWidth) {
3952
3952
  } else {
3953
3953
  if (currentLine.length > 0) {
3954
3954
  lines.push(currentLine);
3955
- if (lines.length >= maxLines) {
3956
- return lines;
3957
- }
3958
3955
  }
3959
3956
  currentLine = [i];
3960
3957
  currentWidth = wordWidth;
3961
3958
  }
3962
3959
  }
3963
- if (currentLine.length > 0 && lines.length < maxLines) {
3960
+ if (currentLine.length > 0) {
3964
3961
  lines.push(currentLine);
3965
3962
  }
3966
3963
  return lines;
@@ -3993,6 +3990,13 @@ function transformText(text, transform) {
3993
3990
  return text;
3994
3991
  }
3995
3992
  }
3993
+ function splitIntoChunks(arr, chunkSize) {
3994
+ const chunks = [];
3995
+ for (let i = 0; i < arr.length; i += chunkSize) {
3996
+ chunks.push(arr.slice(i, i + chunkSize));
3997
+ }
3998
+ return chunks;
3999
+ }
3996
4000
  var CaptionLayoutEngine = class {
3997
4001
  fontRegistry;
3998
4002
  cache;
@@ -4079,31 +4083,34 @@ var CaptionLayoutEngine = class {
4079
4083
  const spaceWord = await this.measureWord(" ", measurementConfig);
4080
4084
  spaceWidth = spaceWord.width + config.wordSpacing;
4081
4085
  }
4082
- const groups = wordGroups.map((indices) => {
4086
+ const groups = wordGroups.flatMap((indices) => {
4083
4087
  const groupWidths = indices.map((i) => store.widths[i]);
4084
- const lineIndices = breakIntoLines(
4088
+ const allLines = breakIntoLines(
4085
4089
  groupWidths,
4086
4090
  pixelMaxWidth,
4087
- config.maxLines,
4088
4091
  spaceWidth
4089
4092
  );
4090
- const lines = lineIndices.map((lineWordIndices, lineIndex) => {
4091
- const actualIndices = lineWordIndices.map((i) => indices[i]);
4092
- const lineWidth = actualIndices.reduce((sum, idx) => sum + store.widths[idx], 0) + (actualIndices.length - 1) * spaceWidth;
4093
+ const lineChunks = splitIntoChunks(allLines, config.maxLines);
4094
+ return lineChunks.map((chunkLines) => {
4095
+ const lines = chunkLines.map((lineWordIndices, lineIndex) => {
4096
+ const actualIndices = lineWordIndices.map((i) => indices[i]);
4097
+ const lineWidth = actualIndices.reduce((sum, idx) => sum + store.widths[idx], 0) + (actualIndices.length - 1) * spaceWidth;
4098
+ return {
4099
+ wordIndices: actualIndices,
4100
+ x: 0,
4101
+ y: lineIndex * config.fontSize * config.lineHeight,
4102
+ width: lineWidth,
4103
+ height: config.fontSize
4104
+ };
4105
+ });
4106
+ const allWordIndices = lines.flatMap((l) => l.wordIndices);
4093
4107
  return {
4094
- wordIndices: actualIndices,
4095
- x: 0,
4096
- y: lineIndex * config.fontSize * config.lineHeight,
4097
- width: lineWidth,
4098
- height: config.fontSize
4108
+ wordIndices: allWordIndices,
4109
+ startTime: store.startTimes[allWordIndices[0]],
4110
+ endTime: store.endTimes[allWordIndices[allWordIndices.length - 1]],
4111
+ lines
4099
4112
  };
4100
4113
  });
4101
- return {
4102
- wordIndices: lines.flatMap((l) => l.wordIndices),
4103
- startTime: store.startTimes[indices[0]],
4104
- endTime: store.endTimes[indices[indices.length - 1]],
4105
- lines
4106
- };
4107
4114
  });
4108
4115
  const calculateGroupY = (group) => {
4109
4116
  const totalHeight = group.lines.length * config.fontSize * config.lineHeight;
@@ -4478,11 +4485,11 @@ function extractFontConfig(asset) {
4478
4485
  const font = asset.font;
4479
4486
  const active = asset.active?.font;
4480
4487
  return {
4481
- family: font?.family ?? "Open Sans",
4488
+ family: font?.family ?? CANVAS_CONFIG.DEFAULTS.fontFamily,
4482
4489
  size: font?.size ?? 24,
4483
4490
  weight: String(font?.weight ?? "400"),
4484
4491
  baseColor: font?.color ?? "#ffffff",
4485
- activeColor: active?.color ?? "#ffff00",
4492
+ activeColor: active?.color ?? "#ffffff",
4486
4493
  baseOpacity: font?.opacity ?? 1,
4487
4494
  activeOpacity: active?.opacity ?? 1,
4488
4495
  letterSpacing: asset.style?.letterSpacing ?? 0
@@ -5384,15 +5391,14 @@ async function createNodeRawEncoder(config, options) {
5384
5391
  }
5385
5392
 
5386
5393
  // src/core/rich-caption-renderer.ts
5387
- var ROBOTO_FONT_URLS = {
5388
- "100": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbGmT.ttf",
5389
- "300": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuaabWmT.ttf",
5390
- "400": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbWmT.ttf",
5391
- "500": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWub2bWmT.ttf",
5392
- "600": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuYaammT.ttf",
5393
- "700": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuYjammT.ttf",
5394
- "800": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuZEammT.ttf",
5395
- "900": "https://fonts.gstatic.com/s/roboto/v50/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuZtammT.ttf"
5394
+ var OPEN_SANS_FONT_URL = "https://fonts.gstatic.com/s/opensans/v44/mem8YaGs126MiZpBA-U1UpcaXcl0Aw.ttf";
5395
+ var OPEN_SANS_FONT_URLS = {
5396
+ "300": OPEN_SANS_FONT_URL,
5397
+ "400": OPEN_SANS_FONT_URL,
5398
+ "500": OPEN_SANS_FONT_URL,
5399
+ "600": OPEN_SANS_FONT_URL,
5400
+ "700": OPEN_SANS_FONT_URL,
5401
+ "800": OPEN_SANS_FONT_URL
5396
5402
  };
5397
5403
  var RichCaptionRenderer = class {
5398
5404
  width;
@@ -5422,12 +5428,12 @@ var RichCaptionRenderer = class {
5422
5428
  async initialize() {
5423
5429
  this.fontRegistry = await FontRegistry.getSharedInstance(this.wasmBaseURL);
5424
5430
  this.layoutEngine = new CaptionLayoutEngine(this.fontRegistry);
5425
- const weightsToLoad = Object.keys(ROBOTO_FONT_URLS);
5431
+ const weightsToLoad = Object.keys(OPEN_SANS_FONT_URLS);
5426
5432
  const loadPromises = weightsToLoad.map(async (weight) => {
5427
- const existingFace = await this.fontRegistry.getFace({ family: "Roboto", weight });
5433
+ const existingFace = await this.fontRegistry.getFace({ family: "Open Sans", weight });
5428
5434
  if (!existingFace) {
5429
- const bytes = await loadFileOrHttpToArrayBuffer(ROBOTO_FONT_URLS[weight]);
5430
- await this.fontRegistry.registerFromBytes(bytes, { family: "Roboto", weight });
5435
+ const bytes = await loadFileOrHttpToArrayBuffer(OPEN_SANS_FONT_URLS[weight]);
5436
+ await this.fontRegistry.registerFromBytes(bytes, { family: "Open Sans", weight });
5431
5437
  }
5432
5438
  });
5433
5439
  await Promise.all(loadPromises);
@@ -5471,7 +5477,7 @@ var RichCaptionRenderer = class {
5471
5477
  maxLines: asset.maxLines ?? 2,
5472
5478
  position: asset.position ?? "bottom",
5473
5479
  fontSize: font?.size ?? 24,
5474
- fontFamily: font?.family ?? "Roboto",
5480
+ fontFamily: font?.family ?? "Open Sans",
5475
5481
  fontWeight: String(font?.weight ?? "400"),
5476
5482
  letterSpacing: style?.letterSpacing ?? 0,
5477
5483
  wordSpacing: typeof style?.wordSpacing === "number" ? style.wordSpacing : 0,
@@ -5564,7 +5570,15 @@ var RichCaptionRenderer = class {
5564
5570
  const elapsed = performance.now() - totalStart;
5565
5571
  const fps = framesProcessed / (elapsed / 1e3);
5566
5572
  const eta = (schedule.totalFrames - framesProcessed) / fps * 1e3;
5567
- this.logProgress(pct, framesProcessed, schedule.totalFrames, i + 1, schedule.uniqueFrameCount, fps, eta);
5573
+ this.logProgress(
5574
+ pct,
5575
+ framesProcessed,
5576
+ schedule.totalFrames,
5577
+ i + 1,
5578
+ schedule.uniqueFrameCount,
5579
+ fps,
5580
+ eta
5581
+ );
5568
5582
  }
5569
5583
  if (i % 500 === 0 && i > 0) {
5570
5584
  this.checkMemoryUsage();
@@ -5944,7 +5958,7 @@ async function createTextEngine(opts = {}) {
5944
5958
  }
5945
5959
  }
5946
5960
  const main = asset.font ?? {
5947
- family: "Roboto",
5961
+ family: "Open Sans",
5948
5962
  weight: "400",
5949
5963
  size: 48,
5950
5964
  color: "#000000",
@@ -6172,6 +6186,7 @@ export {
6172
6186
  RichCaptionRenderer,
6173
6187
  WordTimingStore,
6174
6188
  arcToCubicBeziers,
6189
+ breakIntoLines,
6175
6190
  calculateAnimationStatesForGroup,
6176
6191
  commandsToPathString,
6177
6192
  computeSimplePathBounds,
@@ -670,6 +670,7 @@ declare class WordTimingStore {
670
670
  }
671
671
  declare function findWordAtTime(store: WordTimingStore, timeMs: number): number;
672
672
  declare function groupWordsByPause(store: WordTimingStore, pauseThreshold?: number): number[][];
673
+ declare function breakIntoLines(wordWidths: number[], maxWidth: number, spaceWidth: number): number[][];
673
674
  declare class CaptionLayoutEngine {
674
675
  private fontRegistry;
675
676
  private cache;
@@ -1259,4 +1260,4 @@ declare function createTextEngine(opts?: {
1259
1260
  destroy(): void;
1260
1261
  }>;
1261
1262
 
1262
- export { type AnimationDirection, type AnimationStyle, type ArcCommand, type BackgroundConfig, type BoundingBox, type CanvasRichCaptionAsset, CanvasRichCaptionAssetSchema, type CanvasRichTextAsset, CanvasRichTextAssetSchema, type CanvasSvgAsset, CanvasSvgAssetSchema, type CaptionGroup, type CaptionLayout, type CaptionLayoutConfig, CaptionLayoutEngine, type CaptionLine, type ClosePathCommand, type CubicBezierCommand, type DrawOp, type EngineInit, type FastVideoOptions, type FastVideoResult, type FontConfig, FontRegistry, type FrameSchedule, type Glyph, type GradientSpec, type IVideoEncoder, type LineToCommand, MediaRecorderFallback, type MoveToCommand, type NormalizedPathCommand, type ParsedPathCommand, type PathCommandType, type Point2D, type PositionedWord, type QuadraticBezierCommand, type RGBA, type RenderFrame, type RenderStats, type Renderer, type ResvgRenderOptions, type ResvgRenderResult, type RichCaptionGeneratorConfig, type RichCaptionRendererOptions, type ShadowConfig, type ShapedLine, type ShapedWord, type ShapedWordGlyph, type ShotstackRichTextAsset, type ShotstackSvgAsset, type StrokeConfig, type StrokeSpec, type ValidAsset, type VideoEncoderCapabilities, type VideoEncoderConfig, type VideoEncoderProgress, WebCodecsEncoder, type WordAnimationConfig, type WordAnimationState, type WordTiming, WordTimingStore, arcToCubicBeziers, calculateAnimationStatesForGroup, commandsToPathString, computeSimplePathBounds, createDefaultGeneratorConfig, createFrameSchedule, createMediaRecorderFallback, createTextEngine, createVideoEncoder, createWebCodecsEncoder, createWebPainter, detectPlatform, detectSubtitleFormat, findWordAtTime, generateRichCaptionDrawOps, generateRichCaptionFrame, generateShapePathData, getDefaultAnimationConfig, getDrawCaptionWordOps, getEncoderCapabilities, getEncoderWarning, groupWordsByPause, initResvg, isDrawCaptionWordOp, isMediaRecorderSupported, isRTLText, isWebCodecsH264Supported, normalizePath, normalizePathString, parseSubtitleToWords, parseSvgPath, quadraticToCubic, renderSvgAssetToPng, renderSvgToPng, richCaptionAssetSchema, shapeToSvgString };
1263
+ export { type AnimationDirection, type AnimationStyle, type ArcCommand, type BackgroundConfig, type BoundingBox, type CanvasRichCaptionAsset, CanvasRichCaptionAssetSchema, type CanvasRichTextAsset, CanvasRichTextAssetSchema, type CanvasSvgAsset, CanvasSvgAssetSchema, type CaptionGroup, type CaptionLayout, type CaptionLayoutConfig, CaptionLayoutEngine, type CaptionLine, type ClosePathCommand, type CubicBezierCommand, type DrawOp, type EngineInit, type FastVideoOptions, type FastVideoResult, type FontConfig, FontRegistry, type FrameSchedule, type Glyph, type GradientSpec, type IVideoEncoder, type LineToCommand, MediaRecorderFallback, type MoveToCommand, type NormalizedPathCommand, type ParsedPathCommand, type PathCommandType, type Point2D, type PositionedWord, type QuadraticBezierCommand, type RGBA, type RenderFrame, type RenderStats, type Renderer, type ResvgRenderOptions, type ResvgRenderResult, type RichCaptionGeneratorConfig, type RichCaptionRendererOptions, type ShadowConfig, type ShapedLine, type ShapedWord, type ShapedWordGlyph, type ShotstackRichTextAsset, type ShotstackSvgAsset, type StrokeConfig, type StrokeSpec, type ValidAsset, type VideoEncoderCapabilities, type VideoEncoderConfig, type VideoEncoderProgress, WebCodecsEncoder, type WordAnimationConfig, type WordAnimationState, type WordTiming, WordTimingStore, arcToCubicBeziers, breakIntoLines, calculateAnimationStatesForGroup, commandsToPathString, computeSimplePathBounds, createDefaultGeneratorConfig, createFrameSchedule, createMediaRecorderFallback, createTextEngine, createVideoEncoder, createWebCodecsEncoder, createWebPainter, detectPlatform, detectSubtitleFormat, findWordAtTime, generateRichCaptionDrawOps, generateRichCaptionFrame, generateShapePathData, getDefaultAnimationConfig, getDrawCaptionWordOps, getEncoderCapabilities, getEncoderWarning, groupWordsByPause, initResvg, isDrawCaptionWordOp, isMediaRecorderSupported, isRTLText, isWebCodecsH264Supported, normalizePath, normalizePathString, parseSubtitleToWords, parseSvgPath, quadraticToCubic, renderSvgAssetToPng, renderSvgToPng, richCaptionAssetSchema, shapeToSvgString };