@shotstack/shotstack-canvas 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entry.node.cjs +96 -95
- package/dist/entry.node.d.cts +8 -2
- package/dist/entry.node.d.ts +8 -2
- package/dist/entry.node.js +96 -95
- package/dist/entry.web.d.ts +8 -2
- package/dist/entry.web.js +95 -94
- package/package.json +7 -2
package/dist/entry.node.cjs
CHANGED
|
@@ -2842,10 +2842,11 @@ function calculateTypewriterState(ctx, charCount, speed) {
|
|
|
2842
2842
|
isActive: isWordActive(ctx)
|
|
2843
2843
|
};
|
|
2844
2844
|
}
|
|
2845
|
-
function calculateNoneState(
|
|
2845
|
+
function calculateNoneState(_ctx) {
|
|
2846
2846
|
return {
|
|
2847
2847
|
opacity: 1,
|
|
2848
|
-
isActive:
|
|
2848
|
+
isActive: false,
|
|
2849
|
+
fillProgress: 0
|
|
2849
2850
|
};
|
|
2850
2851
|
}
|
|
2851
2852
|
function calculateWordAnimationState(wordStart, wordEnd, currentTime, config, activeScale = 1, charCount = 0, fontSize = 48, isRTL = false) {
|
|
@@ -2888,7 +2889,7 @@ function calculateWordAnimationState(wordStart, wordEnd, currentTime, config, ac
|
|
|
2888
2889
|
break;
|
|
2889
2890
|
}
|
|
2890
2891
|
const mergedState = { ...baseState, ...partialState };
|
|
2891
|
-
if (mergedState.isActive && activeScale !== 1 && config.style !== "pop") {
|
|
2892
|
+
if (mergedState.isActive && activeScale !== 1 && config.style !== "pop" && config.style !== "none") {
|
|
2892
2893
|
mergedState.scale = activeScale;
|
|
2893
2894
|
}
|
|
2894
2895
|
return mergedState;
|
|
@@ -2950,7 +2951,10 @@ function extractStrokeConfig(asset, isActive) {
|
|
|
2950
2951
|
if (!baseStroke && !activeStroke) {
|
|
2951
2952
|
return void 0;
|
|
2952
2953
|
}
|
|
2953
|
-
if (isActive
|
|
2954
|
+
if (isActive) {
|
|
2955
|
+
if (!activeStroke) {
|
|
2956
|
+
return void 0;
|
|
2957
|
+
}
|
|
2954
2958
|
return {
|
|
2955
2959
|
width: activeStroke.width ?? baseStroke?.width ?? 0,
|
|
2956
2960
|
color: activeStroke.color ?? baseStroke?.color ?? "#000000",
|
|
@@ -2966,7 +2970,10 @@ function extractStrokeConfig(asset, isActive) {
|
|
|
2966
2970
|
}
|
|
2967
2971
|
return void 0;
|
|
2968
2972
|
}
|
|
2969
|
-
function extractShadowConfig(asset) {
|
|
2973
|
+
function extractShadowConfig(asset, isActive) {
|
|
2974
|
+
if (isActive) {
|
|
2975
|
+
return void 0;
|
|
2976
|
+
}
|
|
2970
2977
|
const shadow = asset.shadow;
|
|
2971
2978
|
if (!shadow) {
|
|
2972
2979
|
return void 0;
|
|
@@ -3073,27 +3080,10 @@ function createDrawCaptionWordOp(word, animState, asset, fontConfig) {
|
|
|
3073
3080
|
visibleCharacters: animState.visibleCharacters,
|
|
3074
3081
|
letterSpacing: fontConfig.letterSpacing > 0 ? fontConfig.letterSpacing : void 0,
|
|
3075
3082
|
stroke: extractStrokeConfig(asset, isActive),
|
|
3076
|
-
shadow: extractShadowConfig(asset),
|
|
3083
|
+
shadow: extractShadowConfig(asset, isActive),
|
|
3077
3084
|
background: extractBackgroundConfig(asset, isActive, fontConfig.size)
|
|
3078
3085
|
};
|
|
3079
3086
|
}
|
|
3080
|
-
function calculateGroupBounds(activeGroup, padding, frameWidth, activeScale, fontSize) {
|
|
3081
|
-
let minY = Infinity;
|
|
3082
|
-
let maxY = -Infinity;
|
|
3083
|
-
for (const line of activeGroup.lines) {
|
|
3084
|
-
const lineY = line.y - line.height * ASCENT_RATIO;
|
|
3085
|
-
const lineBottom = line.y + line.height * DESCENT_RATIO;
|
|
3086
|
-
if (lineY < minY) minY = lineY;
|
|
3087
|
-
if (lineBottom > maxY) maxY = lineBottom;
|
|
3088
|
-
}
|
|
3089
|
-
const scaleExpansion = activeScale > 1 ? (activeScale - 1) * fontSize : 0;
|
|
3090
|
-
return {
|
|
3091
|
-
bgX: 0,
|
|
3092
|
-
bgY: minY - padding.top - scaleExpansion,
|
|
3093
|
-
bgWidth: frameWidth,
|
|
3094
|
-
bgHeight: maxY - minY + padding.top + padding.bottom + scaleExpansion * 2
|
|
3095
|
-
};
|
|
3096
|
-
}
|
|
3097
3087
|
function generateRichCaptionDrawOps(asset, layout, frameTimeMs, layoutEngine, config) {
|
|
3098
3088
|
if (layout.store.length === 0) {
|
|
3099
3089
|
return [];
|
|
@@ -3113,48 +3103,35 @@ function generateRichCaptionDrawOps(asset, layout, frameTimeMs, layoutEngine, co
|
|
|
3113
3103
|
fontConfig.size
|
|
3114
3104
|
);
|
|
3115
3105
|
const ops = [];
|
|
3116
|
-
const
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
);
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
op: "RectangleStroke",
|
|
3146
|
-
x: bgX + halfBorder,
|
|
3147
|
-
y: bgY + halfBorder,
|
|
3148
|
-
width: bgWidth - borderConfig.width,
|
|
3149
|
-
height: bgHeight - borderConfig.width,
|
|
3150
|
-
stroke: {
|
|
3151
|
-
width: borderConfig.width,
|
|
3152
|
-
color: borderConfig.color,
|
|
3153
|
-
opacity: borderConfig.opacity
|
|
3154
|
-
},
|
|
3155
|
-
borderRadius: borderConfig.radius > 0 ? borderConfig.radius : void 0
|
|
3156
|
-
});
|
|
3157
|
-
}
|
|
3106
|
+
const captionBg = extractCaptionBackground(asset);
|
|
3107
|
+
if (captionBg) {
|
|
3108
|
+
ops.push({
|
|
3109
|
+
op: "DrawCaptionBackground",
|
|
3110
|
+
x: 0,
|
|
3111
|
+
y: 0,
|
|
3112
|
+
width: config.frameWidth,
|
|
3113
|
+
height: config.frameHeight,
|
|
3114
|
+
color: captionBg.color,
|
|
3115
|
+
opacity: captionBg.opacity,
|
|
3116
|
+
borderRadius: captionBg.borderRadius
|
|
3117
|
+
});
|
|
3118
|
+
}
|
|
3119
|
+
const borderConfig = extractCaptionBorder(asset);
|
|
3120
|
+
if (borderConfig) {
|
|
3121
|
+
const halfBorder = borderConfig.width / 2;
|
|
3122
|
+
ops.push({
|
|
3123
|
+
op: "RectangleStroke",
|
|
3124
|
+
x: halfBorder,
|
|
3125
|
+
y: halfBorder,
|
|
3126
|
+
width: config.frameWidth - borderConfig.width,
|
|
3127
|
+
height: config.frameHeight - borderConfig.width,
|
|
3128
|
+
stroke: {
|
|
3129
|
+
width: borderConfig.width,
|
|
3130
|
+
color: borderConfig.color,
|
|
3131
|
+
opacity: borderConfig.opacity
|
|
3132
|
+
},
|
|
3133
|
+
borderRadius: borderConfig.radius > 0 ? borderConfig.radius : void 0
|
|
3134
|
+
});
|
|
3158
3135
|
}
|
|
3159
3136
|
const nonActiveOps = [];
|
|
3160
3137
|
const activeOps = [];
|
|
@@ -3501,40 +3478,61 @@ async function createNodePainter(opts) {
|
|
|
3501
3478
|
i++;
|
|
3502
3479
|
}
|
|
3503
3480
|
renderToBoth((context) => {
|
|
3504
|
-
|
|
3505
|
-
|
|
3481
|
+
let bgIdx = 0;
|
|
3482
|
+
while (bgIdx < captionWordOps.length) {
|
|
3483
|
+
const wordOp = captionWordOps[bgIdx];
|
|
3484
|
+
if (!wordOp.background) {
|
|
3485
|
+
bgIdx++;
|
|
3486
|
+
continue;
|
|
3487
|
+
}
|
|
3506
3488
|
const wordDisplayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
3507
|
-
if (wordDisplayText.length === 0)
|
|
3489
|
+
if (wordDisplayText.length === 0) {
|
|
3490
|
+
bgIdx++;
|
|
3491
|
+
continue;
|
|
3492
|
+
}
|
|
3493
|
+
const mergeGroup = [wordOp];
|
|
3494
|
+
let nextIdx = bgIdx + 1;
|
|
3495
|
+
while (nextIdx < captionWordOps.length) {
|
|
3496
|
+
const nextWord = captionWordOps[nextIdx];
|
|
3497
|
+
if (!nextWord.background) break;
|
|
3498
|
+
const nextDisplay = getVisibleText(nextWord.text, nextWord.visibleCharacters, nextWord.isRTL);
|
|
3499
|
+
if (nextDisplay.length === 0) break;
|
|
3500
|
+
if (Math.round(nextWord.y) !== Math.round(wordOp.y)) break;
|
|
3501
|
+
if (nextWord.background.color !== wordOp.background.color) break;
|
|
3502
|
+
mergeGroup.push(nextWord);
|
|
3503
|
+
nextIdx++;
|
|
3504
|
+
}
|
|
3505
|
+
const firstWord = mergeGroup[0];
|
|
3506
|
+
const lastWord = mergeGroup[mergeGroup.length - 1];
|
|
3507
|
+
const firstBg = firstWord.background;
|
|
3508
|
+
const mergedLeft = firstWord.x + firstWord.transform.translateX;
|
|
3509
|
+
const mergedRight = lastWord.x + lastWord.transform.translateX + lastWord.width;
|
|
3510
|
+
const mergedWidth = mergedRight - mergedLeft;
|
|
3508
3511
|
context.save();
|
|
3509
|
-
const bgTx = Math.round(
|
|
3510
|
-
const bgTy = Math.round(
|
|
3512
|
+
const bgTx = Math.round(mergedLeft);
|
|
3513
|
+
const bgTy = Math.round(firstWord.y + firstWord.transform.translateY);
|
|
3511
3514
|
context.translate(bgTx, bgTy);
|
|
3512
|
-
if (
|
|
3513
|
-
const halfWidth =
|
|
3515
|
+
if (firstWord.transform.scale !== 1) {
|
|
3516
|
+
const halfWidth = mergedWidth / 2;
|
|
3514
3517
|
context.translate(halfWidth, 0);
|
|
3515
|
-
context.scale(
|
|
3518
|
+
context.scale(firstWord.transform.scale, firstWord.transform.scale);
|
|
3516
3519
|
context.translate(-halfWidth, 0);
|
|
3517
3520
|
}
|
|
3518
|
-
context.globalAlpha =
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
if (wordOp.letterSpacing) {
|
|
3522
|
-
context.letterSpacing = `${wordOp.letterSpacing}px`;
|
|
3523
|
-
}
|
|
3524
|
-
const bgTextWidth = wordOp.width;
|
|
3525
|
-
const bgAscent = wordOp.fontSize * ASCENT_RATIO;
|
|
3526
|
-
const bgDescent = wordOp.fontSize * DESCENT_RATIO;
|
|
3521
|
+
context.globalAlpha = firstWord.transform.opacity;
|
|
3522
|
+
const bgAscent = firstWord.fontSize * ASCENT_RATIO;
|
|
3523
|
+
const bgDescent = firstWord.fontSize * DESCENT_RATIO;
|
|
3527
3524
|
const bgTextHeight = bgAscent + bgDescent;
|
|
3528
|
-
const bgX = -
|
|
3529
|
-
const bgY = -bgAscent -
|
|
3530
|
-
const bgW =
|
|
3531
|
-
const bgH = bgTextHeight +
|
|
3532
|
-
const bgC = parseHex6(
|
|
3525
|
+
const bgX = -firstBg.padding;
|
|
3526
|
+
const bgY = -bgAscent - firstBg.padding;
|
|
3527
|
+
const bgW = mergedWidth + firstBg.padding * 2;
|
|
3528
|
+
const bgH = bgTextHeight + firstBg.padding * 2;
|
|
3529
|
+
const bgC = parseHex6(firstBg.color, firstBg.opacity);
|
|
3533
3530
|
context.fillStyle = `rgba(${bgC.r},${bgC.g},${bgC.b},${bgC.a})`;
|
|
3534
3531
|
context.beginPath();
|
|
3535
|
-
roundRectPath(context, bgX, bgY, bgW, bgH,
|
|
3532
|
+
roundRectPath(context, bgX, bgY, bgW, bgH, firstBg.borderRadius);
|
|
3536
3533
|
context.fill();
|
|
3537
3534
|
context.restore();
|
|
3535
|
+
bgIdx = nextIdx;
|
|
3538
3536
|
}
|
|
3539
3537
|
for (const wordOp of captionWordOps) {
|
|
3540
3538
|
const displayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
@@ -5317,16 +5315,19 @@ var CaptionLayoutEngine = class {
|
|
|
5317
5315
|
};
|
|
5318
5316
|
}).filter((g) => g !== null);
|
|
5319
5317
|
});
|
|
5318
|
+
const contentHeight = config.frameHeight - config.padding.top - config.padding.bottom;
|
|
5319
|
+
const contentWidth = config.frameWidth - config.padding.left - config.padding.right;
|
|
5320
|
+
const ASCENT_RATIO2 = 0.8;
|
|
5320
5321
|
const calculateGroupY = (group) => {
|
|
5321
5322
|
const totalHeight = group.lines.length * config.fontSize * config.lineHeight;
|
|
5322
5323
|
switch (config.verticalAlign) {
|
|
5323
5324
|
case "top":
|
|
5324
|
-
return config.fontSize *
|
|
5325
|
+
return config.padding.top + config.fontSize * ASCENT_RATIO2;
|
|
5325
5326
|
case "bottom":
|
|
5326
|
-
return config.frameHeight -
|
|
5327
|
+
return config.frameHeight - config.padding.bottom - totalHeight + config.fontSize * ASCENT_RATIO2;
|
|
5327
5328
|
case "middle":
|
|
5328
5329
|
default:
|
|
5329
|
-
return
|
|
5330
|
+
return config.padding.top + (contentHeight - totalHeight) / 2 + config.fontSize * ASCENT_RATIO2;
|
|
5330
5331
|
}
|
|
5331
5332
|
};
|
|
5332
5333
|
const allWordTexts = store.words.slice(0, store.length);
|
|
@@ -5334,12 +5335,12 @@ var CaptionLayoutEngine = class {
|
|
|
5334
5335
|
const calculateLineX = (lineWidth) => {
|
|
5335
5336
|
switch (config.horizontalAlign) {
|
|
5336
5337
|
case "left":
|
|
5337
|
-
return config.
|
|
5338
|
+
return config.padding.left;
|
|
5338
5339
|
case "right":
|
|
5339
|
-
return config.frameWidth - lineWidth - config.
|
|
5340
|
+
return config.frameWidth - lineWidth - config.padding.right;
|
|
5340
5341
|
case "center":
|
|
5341
5342
|
default:
|
|
5342
|
-
return
|
|
5343
|
+
return config.padding.left + (contentWidth - lineWidth) / 2;
|
|
5343
5344
|
}
|
|
5344
5345
|
};
|
|
5345
5346
|
for (const group of groups) {
|
|
@@ -6212,7 +6213,7 @@ var RichCaptionRenderer = class {
|
|
|
6212
6213
|
maxLines: computedMaxLines,
|
|
6213
6214
|
verticalAlign,
|
|
6214
6215
|
horizontalAlign,
|
|
6215
|
-
|
|
6216
|
+
padding,
|
|
6216
6217
|
fontSize,
|
|
6217
6218
|
fontFamily: font?.family ?? "Roboto",
|
|
6218
6219
|
fontWeight: String(font?.weight ?? "400"),
|
package/dist/entry.node.d.cts
CHANGED
|
@@ -609,6 +609,12 @@ interface WordTiming {
|
|
|
609
609
|
end: number;
|
|
610
610
|
confidence?: number;
|
|
611
611
|
}
|
|
612
|
+
interface CaptionPadding {
|
|
613
|
+
top: number;
|
|
614
|
+
right: number;
|
|
615
|
+
bottom: number;
|
|
616
|
+
left: number;
|
|
617
|
+
}
|
|
612
618
|
interface CaptionLayoutConfig {
|
|
613
619
|
frameWidth: number;
|
|
614
620
|
frameHeight: number;
|
|
@@ -616,7 +622,7 @@ interface CaptionLayoutConfig {
|
|
|
616
622
|
maxLines: number;
|
|
617
623
|
verticalAlign: "top" | "middle" | "bottom";
|
|
618
624
|
horizontalAlign: "left" | "center" | "right";
|
|
619
|
-
|
|
625
|
+
padding: CaptionPadding;
|
|
620
626
|
fontSize: number;
|
|
621
627
|
fontFamily: string;
|
|
622
628
|
fontWeight: string | number;
|
|
@@ -1342,4 +1348,4 @@ declare function createTextEngine(opts?: {
|
|
|
1342
1348
|
destroy(): void;
|
|
1343
1349
|
}>;
|
|
1344
1350
|
|
|
1345
|
-
export { ASCENT_RATIO, 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, DESCENT_RATIO, 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 ParagraphDirection, 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, WORD_BG_BORDER_RADIUS, WORD_BG_OPACITY, WORD_BG_PADDING_RATIO, type WordAnimationConfig, type WordAnimationState, type WordTiming, WordTimingStore, arcToCubicBeziers, breakIntoLines, calculateAnimationStatesForGroup, commandsToPathString, computeSimplePathBounds, containsRTLCharacters, createDefaultGeneratorConfig, createFrameSchedule, createNodePainter, createNodeRawEncoder, createRichCaptionRenderer, createTextEngine, createVideoEncoder, detectParagraphDirection, detectParagraphDirectionFromWords, detectPlatform, detectSubtitleFormat, extractCaptionPadding, findWordAtTime, generateRichCaptionDrawOps, generateRichCaptionFrame, generateShapePathData, getDefaultAnimationConfig, getDrawCaptionWordOps, getEncoderCapabilities, getEncoderWarning, getVisibleText, groupWordsByPause, isDrawCaptionWordOp, isRTLText, isWebCodecsH264Supported, mirrorAnimationDirection, normalizePath, normalizePathString, parseSubtitleToWords, parseSvgPath, quadraticToCubic, renderSvgAssetToPng, renderSvgToPng, reorderWordsForLine, richCaptionAssetSchema, shapeToSvgString };
|
|
1351
|
+
export { ASCENT_RATIO, 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 CaptionPadding, type ClosePathCommand, type CubicBezierCommand, DESCENT_RATIO, 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 ParagraphDirection, 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, WORD_BG_BORDER_RADIUS, WORD_BG_OPACITY, WORD_BG_PADDING_RATIO, type WordAnimationConfig, type WordAnimationState, type WordTiming, WordTimingStore, arcToCubicBeziers, breakIntoLines, calculateAnimationStatesForGroup, commandsToPathString, computeSimplePathBounds, containsRTLCharacters, createDefaultGeneratorConfig, createFrameSchedule, createNodePainter, createNodeRawEncoder, createRichCaptionRenderer, createTextEngine, createVideoEncoder, detectParagraphDirection, detectParagraphDirectionFromWords, detectPlatform, detectSubtitleFormat, extractCaptionPadding, findWordAtTime, generateRichCaptionDrawOps, generateRichCaptionFrame, generateShapePathData, getDefaultAnimationConfig, getDrawCaptionWordOps, getEncoderCapabilities, getEncoderWarning, getVisibleText, groupWordsByPause, isDrawCaptionWordOp, isRTLText, isWebCodecsH264Supported, mirrorAnimationDirection, normalizePath, normalizePathString, parseSubtitleToWords, parseSvgPath, quadraticToCubic, renderSvgAssetToPng, renderSvgToPng, reorderWordsForLine, richCaptionAssetSchema, shapeToSvgString };
|
package/dist/entry.node.d.ts
CHANGED
|
@@ -609,6 +609,12 @@ interface WordTiming {
|
|
|
609
609
|
end: number;
|
|
610
610
|
confidence?: number;
|
|
611
611
|
}
|
|
612
|
+
interface CaptionPadding {
|
|
613
|
+
top: number;
|
|
614
|
+
right: number;
|
|
615
|
+
bottom: number;
|
|
616
|
+
left: number;
|
|
617
|
+
}
|
|
612
618
|
interface CaptionLayoutConfig {
|
|
613
619
|
frameWidth: number;
|
|
614
620
|
frameHeight: number;
|
|
@@ -616,7 +622,7 @@ interface CaptionLayoutConfig {
|
|
|
616
622
|
maxLines: number;
|
|
617
623
|
verticalAlign: "top" | "middle" | "bottom";
|
|
618
624
|
horizontalAlign: "left" | "center" | "right";
|
|
619
|
-
|
|
625
|
+
padding: CaptionPadding;
|
|
620
626
|
fontSize: number;
|
|
621
627
|
fontFamily: string;
|
|
622
628
|
fontWeight: string | number;
|
|
@@ -1342,4 +1348,4 @@ declare function createTextEngine(opts?: {
|
|
|
1342
1348
|
destroy(): void;
|
|
1343
1349
|
}>;
|
|
1344
1350
|
|
|
1345
|
-
export { ASCENT_RATIO, 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, DESCENT_RATIO, 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 ParagraphDirection, 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, WORD_BG_BORDER_RADIUS, WORD_BG_OPACITY, WORD_BG_PADDING_RATIO, type WordAnimationConfig, type WordAnimationState, type WordTiming, WordTimingStore, arcToCubicBeziers, breakIntoLines, calculateAnimationStatesForGroup, commandsToPathString, computeSimplePathBounds, containsRTLCharacters, createDefaultGeneratorConfig, createFrameSchedule, createNodePainter, createNodeRawEncoder, createRichCaptionRenderer, createTextEngine, createVideoEncoder, detectParagraphDirection, detectParagraphDirectionFromWords, detectPlatform, detectSubtitleFormat, extractCaptionPadding, findWordAtTime, generateRichCaptionDrawOps, generateRichCaptionFrame, generateShapePathData, getDefaultAnimationConfig, getDrawCaptionWordOps, getEncoderCapabilities, getEncoderWarning, getVisibleText, groupWordsByPause, isDrawCaptionWordOp, isRTLText, isWebCodecsH264Supported, mirrorAnimationDirection, normalizePath, normalizePathString, parseSubtitleToWords, parseSvgPath, quadraticToCubic, renderSvgAssetToPng, renderSvgToPng, reorderWordsForLine, richCaptionAssetSchema, shapeToSvgString };
|
|
1351
|
+
export { ASCENT_RATIO, 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 CaptionPadding, type ClosePathCommand, type CubicBezierCommand, DESCENT_RATIO, 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 ParagraphDirection, 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, WORD_BG_BORDER_RADIUS, WORD_BG_OPACITY, WORD_BG_PADDING_RATIO, type WordAnimationConfig, type WordAnimationState, type WordTiming, WordTimingStore, arcToCubicBeziers, breakIntoLines, calculateAnimationStatesForGroup, commandsToPathString, computeSimplePathBounds, containsRTLCharacters, createDefaultGeneratorConfig, createFrameSchedule, createNodePainter, createNodeRawEncoder, createRichCaptionRenderer, createTextEngine, createVideoEncoder, detectParagraphDirection, detectParagraphDirectionFromWords, detectPlatform, detectSubtitleFormat, extractCaptionPadding, findWordAtTime, generateRichCaptionDrawOps, generateRichCaptionFrame, generateShapePathData, getDefaultAnimationConfig, getDrawCaptionWordOps, getEncoderCapabilities, getEncoderWarning, getVisibleText, groupWordsByPause, isDrawCaptionWordOp, isRTLText, isWebCodecsH264Supported, mirrorAnimationDirection, normalizePath, normalizePathString, parseSubtitleToWords, parseSvgPath, quadraticToCubic, renderSvgAssetToPng, renderSvgToPng, reorderWordsForLine, richCaptionAssetSchema, shapeToSvgString };
|
package/dist/entry.node.js
CHANGED
|
@@ -2439,10 +2439,11 @@ function calculateTypewriterState(ctx, charCount, speed) {
|
|
|
2439
2439
|
isActive: isWordActive(ctx)
|
|
2440
2440
|
};
|
|
2441
2441
|
}
|
|
2442
|
-
function calculateNoneState(
|
|
2442
|
+
function calculateNoneState(_ctx) {
|
|
2443
2443
|
return {
|
|
2444
2444
|
opacity: 1,
|
|
2445
|
-
isActive:
|
|
2445
|
+
isActive: false,
|
|
2446
|
+
fillProgress: 0
|
|
2446
2447
|
};
|
|
2447
2448
|
}
|
|
2448
2449
|
function calculateWordAnimationState(wordStart, wordEnd, currentTime, config, activeScale = 1, charCount = 0, fontSize = 48, isRTL = false) {
|
|
@@ -2485,7 +2486,7 @@ function calculateWordAnimationState(wordStart, wordEnd, currentTime, config, ac
|
|
|
2485
2486
|
break;
|
|
2486
2487
|
}
|
|
2487
2488
|
const mergedState = { ...baseState, ...partialState };
|
|
2488
|
-
if (mergedState.isActive && activeScale !== 1 && config.style !== "pop") {
|
|
2489
|
+
if (mergedState.isActive && activeScale !== 1 && config.style !== "pop" && config.style !== "none") {
|
|
2489
2490
|
mergedState.scale = activeScale;
|
|
2490
2491
|
}
|
|
2491
2492
|
return mergedState;
|
|
@@ -2547,7 +2548,10 @@ function extractStrokeConfig(asset, isActive) {
|
|
|
2547
2548
|
if (!baseStroke && !activeStroke) {
|
|
2548
2549
|
return void 0;
|
|
2549
2550
|
}
|
|
2550
|
-
if (isActive
|
|
2551
|
+
if (isActive) {
|
|
2552
|
+
if (!activeStroke) {
|
|
2553
|
+
return void 0;
|
|
2554
|
+
}
|
|
2551
2555
|
return {
|
|
2552
2556
|
width: activeStroke.width ?? baseStroke?.width ?? 0,
|
|
2553
2557
|
color: activeStroke.color ?? baseStroke?.color ?? "#000000",
|
|
@@ -2563,7 +2567,10 @@ function extractStrokeConfig(asset, isActive) {
|
|
|
2563
2567
|
}
|
|
2564
2568
|
return void 0;
|
|
2565
2569
|
}
|
|
2566
|
-
function extractShadowConfig(asset) {
|
|
2570
|
+
function extractShadowConfig(asset, isActive) {
|
|
2571
|
+
if (isActive) {
|
|
2572
|
+
return void 0;
|
|
2573
|
+
}
|
|
2567
2574
|
const shadow = asset.shadow;
|
|
2568
2575
|
if (!shadow) {
|
|
2569
2576
|
return void 0;
|
|
@@ -2670,27 +2677,10 @@ function createDrawCaptionWordOp(word, animState, asset, fontConfig) {
|
|
|
2670
2677
|
visibleCharacters: animState.visibleCharacters,
|
|
2671
2678
|
letterSpacing: fontConfig.letterSpacing > 0 ? fontConfig.letterSpacing : void 0,
|
|
2672
2679
|
stroke: extractStrokeConfig(asset, isActive),
|
|
2673
|
-
shadow: extractShadowConfig(asset),
|
|
2680
|
+
shadow: extractShadowConfig(asset, isActive),
|
|
2674
2681
|
background: extractBackgroundConfig(asset, isActive, fontConfig.size)
|
|
2675
2682
|
};
|
|
2676
2683
|
}
|
|
2677
|
-
function calculateGroupBounds(activeGroup, padding, frameWidth, activeScale, fontSize) {
|
|
2678
|
-
let minY = Infinity;
|
|
2679
|
-
let maxY = -Infinity;
|
|
2680
|
-
for (const line of activeGroup.lines) {
|
|
2681
|
-
const lineY = line.y - line.height * ASCENT_RATIO;
|
|
2682
|
-
const lineBottom = line.y + line.height * DESCENT_RATIO;
|
|
2683
|
-
if (lineY < minY) minY = lineY;
|
|
2684
|
-
if (lineBottom > maxY) maxY = lineBottom;
|
|
2685
|
-
}
|
|
2686
|
-
const scaleExpansion = activeScale > 1 ? (activeScale - 1) * fontSize : 0;
|
|
2687
|
-
return {
|
|
2688
|
-
bgX: 0,
|
|
2689
|
-
bgY: minY - padding.top - scaleExpansion,
|
|
2690
|
-
bgWidth: frameWidth,
|
|
2691
|
-
bgHeight: maxY - minY + padding.top + padding.bottom + scaleExpansion * 2
|
|
2692
|
-
};
|
|
2693
|
-
}
|
|
2694
2684
|
function generateRichCaptionDrawOps(asset, layout, frameTimeMs, layoutEngine, config) {
|
|
2695
2685
|
if (layout.store.length === 0) {
|
|
2696
2686
|
return [];
|
|
@@ -2710,48 +2700,35 @@ function generateRichCaptionDrawOps(asset, layout, frameTimeMs, layoutEngine, co
|
|
|
2710
2700
|
fontConfig.size
|
|
2711
2701
|
);
|
|
2712
2702
|
const ops = [];
|
|
2713
|
-
const
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
);
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
op: "RectangleStroke",
|
|
2743
|
-
x: bgX + halfBorder,
|
|
2744
|
-
y: bgY + halfBorder,
|
|
2745
|
-
width: bgWidth - borderConfig.width,
|
|
2746
|
-
height: bgHeight - borderConfig.width,
|
|
2747
|
-
stroke: {
|
|
2748
|
-
width: borderConfig.width,
|
|
2749
|
-
color: borderConfig.color,
|
|
2750
|
-
opacity: borderConfig.opacity
|
|
2751
|
-
},
|
|
2752
|
-
borderRadius: borderConfig.radius > 0 ? borderConfig.radius : void 0
|
|
2753
|
-
});
|
|
2754
|
-
}
|
|
2703
|
+
const captionBg = extractCaptionBackground(asset);
|
|
2704
|
+
if (captionBg) {
|
|
2705
|
+
ops.push({
|
|
2706
|
+
op: "DrawCaptionBackground",
|
|
2707
|
+
x: 0,
|
|
2708
|
+
y: 0,
|
|
2709
|
+
width: config.frameWidth,
|
|
2710
|
+
height: config.frameHeight,
|
|
2711
|
+
color: captionBg.color,
|
|
2712
|
+
opacity: captionBg.opacity,
|
|
2713
|
+
borderRadius: captionBg.borderRadius
|
|
2714
|
+
});
|
|
2715
|
+
}
|
|
2716
|
+
const borderConfig = extractCaptionBorder(asset);
|
|
2717
|
+
if (borderConfig) {
|
|
2718
|
+
const halfBorder = borderConfig.width / 2;
|
|
2719
|
+
ops.push({
|
|
2720
|
+
op: "RectangleStroke",
|
|
2721
|
+
x: halfBorder,
|
|
2722
|
+
y: halfBorder,
|
|
2723
|
+
width: config.frameWidth - borderConfig.width,
|
|
2724
|
+
height: config.frameHeight - borderConfig.width,
|
|
2725
|
+
stroke: {
|
|
2726
|
+
width: borderConfig.width,
|
|
2727
|
+
color: borderConfig.color,
|
|
2728
|
+
opacity: borderConfig.opacity
|
|
2729
|
+
},
|
|
2730
|
+
borderRadius: borderConfig.radius > 0 ? borderConfig.radius : void 0
|
|
2731
|
+
});
|
|
2755
2732
|
}
|
|
2756
2733
|
const nonActiveOps = [];
|
|
2757
2734
|
const activeOps = [];
|
|
@@ -3098,40 +3075,61 @@ async function createNodePainter(opts) {
|
|
|
3098
3075
|
i++;
|
|
3099
3076
|
}
|
|
3100
3077
|
renderToBoth((context) => {
|
|
3101
|
-
|
|
3102
|
-
|
|
3078
|
+
let bgIdx = 0;
|
|
3079
|
+
while (bgIdx < captionWordOps.length) {
|
|
3080
|
+
const wordOp = captionWordOps[bgIdx];
|
|
3081
|
+
if (!wordOp.background) {
|
|
3082
|
+
bgIdx++;
|
|
3083
|
+
continue;
|
|
3084
|
+
}
|
|
3103
3085
|
const wordDisplayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
3104
|
-
if (wordDisplayText.length === 0)
|
|
3086
|
+
if (wordDisplayText.length === 0) {
|
|
3087
|
+
bgIdx++;
|
|
3088
|
+
continue;
|
|
3089
|
+
}
|
|
3090
|
+
const mergeGroup = [wordOp];
|
|
3091
|
+
let nextIdx = bgIdx + 1;
|
|
3092
|
+
while (nextIdx < captionWordOps.length) {
|
|
3093
|
+
const nextWord = captionWordOps[nextIdx];
|
|
3094
|
+
if (!nextWord.background) break;
|
|
3095
|
+
const nextDisplay = getVisibleText(nextWord.text, nextWord.visibleCharacters, nextWord.isRTL);
|
|
3096
|
+
if (nextDisplay.length === 0) break;
|
|
3097
|
+
if (Math.round(nextWord.y) !== Math.round(wordOp.y)) break;
|
|
3098
|
+
if (nextWord.background.color !== wordOp.background.color) break;
|
|
3099
|
+
mergeGroup.push(nextWord);
|
|
3100
|
+
nextIdx++;
|
|
3101
|
+
}
|
|
3102
|
+
const firstWord = mergeGroup[0];
|
|
3103
|
+
const lastWord = mergeGroup[mergeGroup.length - 1];
|
|
3104
|
+
const firstBg = firstWord.background;
|
|
3105
|
+
const mergedLeft = firstWord.x + firstWord.transform.translateX;
|
|
3106
|
+
const mergedRight = lastWord.x + lastWord.transform.translateX + lastWord.width;
|
|
3107
|
+
const mergedWidth = mergedRight - mergedLeft;
|
|
3105
3108
|
context.save();
|
|
3106
|
-
const bgTx = Math.round(
|
|
3107
|
-
const bgTy = Math.round(
|
|
3109
|
+
const bgTx = Math.round(mergedLeft);
|
|
3110
|
+
const bgTy = Math.round(firstWord.y + firstWord.transform.translateY);
|
|
3108
3111
|
context.translate(bgTx, bgTy);
|
|
3109
|
-
if (
|
|
3110
|
-
const halfWidth =
|
|
3112
|
+
if (firstWord.transform.scale !== 1) {
|
|
3113
|
+
const halfWidth = mergedWidth / 2;
|
|
3111
3114
|
context.translate(halfWidth, 0);
|
|
3112
|
-
context.scale(
|
|
3115
|
+
context.scale(firstWord.transform.scale, firstWord.transform.scale);
|
|
3113
3116
|
context.translate(-halfWidth, 0);
|
|
3114
3117
|
}
|
|
3115
|
-
context.globalAlpha =
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
if (wordOp.letterSpacing) {
|
|
3119
|
-
context.letterSpacing = `${wordOp.letterSpacing}px`;
|
|
3120
|
-
}
|
|
3121
|
-
const bgTextWidth = wordOp.width;
|
|
3122
|
-
const bgAscent = wordOp.fontSize * ASCENT_RATIO;
|
|
3123
|
-
const bgDescent = wordOp.fontSize * DESCENT_RATIO;
|
|
3118
|
+
context.globalAlpha = firstWord.transform.opacity;
|
|
3119
|
+
const bgAscent = firstWord.fontSize * ASCENT_RATIO;
|
|
3120
|
+
const bgDescent = firstWord.fontSize * DESCENT_RATIO;
|
|
3124
3121
|
const bgTextHeight = bgAscent + bgDescent;
|
|
3125
|
-
const bgX = -
|
|
3126
|
-
const bgY = -bgAscent -
|
|
3127
|
-
const bgW =
|
|
3128
|
-
const bgH = bgTextHeight +
|
|
3129
|
-
const bgC = parseHex6(
|
|
3122
|
+
const bgX = -firstBg.padding;
|
|
3123
|
+
const bgY = -bgAscent - firstBg.padding;
|
|
3124
|
+
const bgW = mergedWidth + firstBg.padding * 2;
|
|
3125
|
+
const bgH = bgTextHeight + firstBg.padding * 2;
|
|
3126
|
+
const bgC = parseHex6(firstBg.color, firstBg.opacity);
|
|
3130
3127
|
context.fillStyle = `rgba(${bgC.r},${bgC.g},${bgC.b},${bgC.a})`;
|
|
3131
3128
|
context.beginPath();
|
|
3132
|
-
roundRectPath(context, bgX, bgY, bgW, bgH,
|
|
3129
|
+
roundRectPath(context, bgX, bgY, bgW, bgH, firstBg.borderRadius);
|
|
3133
3130
|
context.fill();
|
|
3134
3131
|
context.restore();
|
|
3132
|
+
bgIdx = nextIdx;
|
|
3135
3133
|
}
|
|
3136
3134
|
for (const wordOp of captionWordOps) {
|
|
3137
3135
|
const displayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
@@ -4914,16 +4912,19 @@ var CaptionLayoutEngine = class {
|
|
|
4914
4912
|
};
|
|
4915
4913
|
}).filter((g) => g !== null);
|
|
4916
4914
|
});
|
|
4915
|
+
const contentHeight = config.frameHeight - config.padding.top - config.padding.bottom;
|
|
4916
|
+
const contentWidth = config.frameWidth - config.padding.left - config.padding.right;
|
|
4917
|
+
const ASCENT_RATIO2 = 0.8;
|
|
4917
4918
|
const calculateGroupY = (group) => {
|
|
4918
4919
|
const totalHeight = group.lines.length * config.fontSize * config.lineHeight;
|
|
4919
4920
|
switch (config.verticalAlign) {
|
|
4920
4921
|
case "top":
|
|
4921
|
-
return config.fontSize *
|
|
4922
|
+
return config.padding.top + config.fontSize * ASCENT_RATIO2;
|
|
4922
4923
|
case "bottom":
|
|
4923
|
-
return config.frameHeight -
|
|
4924
|
+
return config.frameHeight - config.padding.bottom - totalHeight + config.fontSize * ASCENT_RATIO2;
|
|
4924
4925
|
case "middle":
|
|
4925
4926
|
default:
|
|
4926
|
-
return
|
|
4927
|
+
return config.padding.top + (contentHeight - totalHeight) / 2 + config.fontSize * ASCENT_RATIO2;
|
|
4927
4928
|
}
|
|
4928
4929
|
};
|
|
4929
4930
|
const allWordTexts = store.words.slice(0, store.length);
|
|
@@ -4931,12 +4932,12 @@ var CaptionLayoutEngine = class {
|
|
|
4931
4932
|
const calculateLineX = (lineWidth) => {
|
|
4932
4933
|
switch (config.horizontalAlign) {
|
|
4933
4934
|
case "left":
|
|
4934
|
-
return config.
|
|
4935
|
+
return config.padding.left;
|
|
4935
4936
|
case "right":
|
|
4936
|
-
return config.frameWidth - lineWidth - config.
|
|
4937
|
+
return config.frameWidth - lineWidth - config.padding.right;
|
|
4937
4938
|
case "center":
|
|
4938
4939
|
default:
|
|
4939
|
-
return
|
|
4940
|
+
return config.padding.left + (contentWidth - lineWidth) / 2;
|
|
4940
4941
|
}
|
|
4941
4942
|
};
|
|
4942
4943
|
for (const group of groups) {
|
|
@@ -5809,7 +5810,7 @@ var RichCaptionRenderer = class {
|
|
|
5809
5810
|
maxLines: computedMaxLines,
|
|
5810
5811
|
verticalAlign,
|
|
5811
5812
|
horizontalAlign,
|
|
5812
|
-
|
|
5813
|
+
padding,
|
|
5813
5814
|
fontSize,
|
|
5814
5815
|
fontFamily: font?.family ?? "Roboto",
|
|
5815
5816
|
fontWeight: String(font?.weight ?? "400"),
|
package/dist/entry.web.d.ts
CHANGED
|
@@ -609,6 +609,12 @@ interface WordTiming {
|
|
|
609
609
|
end: number;
|
|
610
610
|
confidence?: number;
|
|
611
611
|
}
|
|
612
|
+
interface CaptionPadding {
|
|
613
|
+
top: number;
|
|
614
|
+
right: number;
|
|
615
|
+
bottom: number;
|
|
616
|
+
left: number;
|
|
617
|
+
}
|
|
612
618
|
interface CaptionLayoutConfig {
|
|
613
619
|
frameWidth: number;
|
|
614
620
|
frameHeight: number;
|
|
@@ -616,7 +622,7 @@ interface CaptionLayoutConfig {
|
|
|
616
622
|
maxLines: number;
|
|
617
623
|
verticalAlign: "top" | "middle" | "bottom";
|
|
618
624
|
horizontalAlign: "left" | "center" | "right";
|
|
619
|
-
|
|
625
|
+
padding: CaptionPadding;
|
|
620
626
|
fontSize: number;
|
|
621
627
|
fontFamily: string;
|
|
622
628
|
fontWeight: string | number;
|
|
@@ -1285,4 +1291,4 @@ declare function createTextEngine(opts?: {
|
|
|
1285
1291
|
destroy(): void;
|
|
1286
1292
|
}>;
|
|
1287
1293
|
|
|
1288
|
-
export { ASCENT_RATIO, 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, DESCENT_RATIO, 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 ParagraphDirection, 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, WORD_BG_BORDER_RADIUS, WORD_BG_OPACITY, WORD_BG_PADDING_RATIO, WebCodecsEncoder, type WordAnimationConfig, type WordAnimationState, type WordTiming, WordTimingStore, arcToCubicBeziers, breakIntoLines, calculateAnimationStatesForGroup, commandsToPathString, computeSimplePathBounds, containsRTLCharacters, createDefaultGeneratorConfig, createFrameSchedule, createMediaRecorderFallback, createTextEngine, createVideoEncoder, createWebCodecsEncoder, createWebPainter, detectParagraphDirection, detectParagraphDirectionFromWords, detectPlatform, detectSubtitleFormat, extractCaptionPadding, findWordAtTime, generateRichCaptionDrawOps, generateRichCaptionFrame, generateShapePathData, getDefaultAnimationConfig, getDrawCaptionWordOps, getEncoderCapabilities, getEncoderWarning, getVisibleText, groupWordsByPause, initResvg, isDrawCaptionWordOp, isMediaRecorderSupported, isRTLText, isWebCodecsH264Supported, mirrorAnimationDirection, normalizePath, normalizePathString, parseSubtitleToWords, parseSvgPath, quadraticToCubic, renderSvgAssetToPng, renderSvgToPng, reorderWordsForLine, richCaptionAssetSchema, shapeToSvgString };
|
|
1294
|
+
export { ASCENT_RATIO, 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 CaptionPadding, type ClosePathCommand, type CubicBezierCommand, DESCENT_RATIO, 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 ParagraphDirection, 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, WORD_BG_BORDER_RADIUS, WORD_BG_OPACITY, WORD_BG_PADDING_RATIO, WebCodecsEncoder, type WordAnimationConfig, type WordAnimationState, type WordTiming, WordTimingStore, arcToCubicBeziers, breakIntoLines, calculateAnimationStatesForGroup, commandsToPathString, computeSimplePathBounds, containsRTLCharacters, createDefaultGeneratorConfig, createFrameSchedule, createMediaRecorderFallback, createTextEngine, createVideoEncoder, createWebCodecsEncoder, createWebPainter, detectParagraphDirection, detectParagraphDirectionFromWords, detectPlatform, detectSubtitleFormat, extractCaptionPadding, findWordAtTime, generateRichCaptionDrawOps, generateRichCaptionFrame, generateShapePathData, getDefaultAnimationConfig, getDrawCaptionWordOps, getEncoderCapabilities, getEncoderWarning, getVisibleText, groupWordsByPause, initResvg, isDrawCaptionWordOp, isMediaRecorderSupported, isRTLText, isWebCodecsH264Supported, mirrorAnimationDirection, normalizePath, normalizePathString, parseSubtitleToWords, parseSvgPath, quadraticToCubic, renderSvgAssetToPng, renderSvgToPng, reorderWordsForLine, richCaptionAssetSchema, shapeToSvgString };
|
package/dist/entry.web.js
CHANGED
|
@@ -34474,10 +34474,11 @@ function calculateTypewriterState(ctx, charCount, speed) {
|
|
|
34474
34474
|
isActive: isWordActive(ctx)
|
|
34475
34475
|
};
|
|
34476
34476
|
}
|
|
34477
|
-
function calculateNoneState(
|
|
34477
|
+
function calculateNoneState(_ctx) {
|
|
34478
34478
|
return {
|
|
34479
34479
|
opacity: 1,
|
|
34480
|
-
isActive:
|
|
34480
|
+
isActive: false,
|
|
34481
|
+
fillProgress: 0
|
|
34481
34482
|
};
|
|
34482
34483
|
}
|
|
34483
34484
|
function calculateWordAnimationState(wordStart, wordEnd, currentTime, config2, activeScale = 1, charCount = 0, fontSize = 48, isRTL = false) {
|
|
@@ -34520,7 +34521,7 @@ function calculateWordAnimationState(wordStart, wordEnd, currentTime, config2, a
|
|
|
34520
34521
|
break;
|
|
34521
34522
|
}
|
|
34522
34523
|
const mergedState = { ...baseState, ...partialState };
|
|
34523
|
-
if (mergedState.isActive && activeScale !== 1 && config2.style !== "pop") {
|
|
34524
|
+
if (mergedState.isActive && activeScale !== 1 && config2.style !== "pop" && config2.style !== "none") {
|
|
34524
34525
|
mergedState.scale = activeScale;
|
|
34525
34526
|
}
|
|
34526
34527
|
return mergedState;
|
|
@@ -34582,7 +34583,10 @@ function extractStrokeConfig(asset, isActive) {
|
|
|
34582
34583
|
if (!baseStroke && !activeStroke) {
|
|
34583
34584
|
return void 0;
|
|
34584
34585
|
}
|
|
34585
|
-
if (isActive
|
|
34586
|
+
if (isActive) {
|
|
34587
|
+
if (!activeStroke) {
|
|
34588
|
+
return void 0;
|
|
34589
|
+
}
|
|
34586
34590
|
return {
|
|
34587
34591
|
width: activeStroke.width ?? baseStroke?.width ?? 0,
|
|
34588
34592
|
color: activeStroke.color ?? baseStroke?.color ?? "#000000",
|
|
@@ -34598,7 +34602,10 @@ function extractStrokeConfig(asset, isActive) {
|
|
|
34598
34602
|
}
|
|
34599
34603
|
return void 0;
|
|
34600
34604
|
}
|
|
34601
|
-
function extractShadowConfig(asset) {
|
|
34605
|
+
function extractShadowConfig(asset, isActive) {
|
|
34606
|
+
if (isActive) {
|
|
34607
|
+
return void 0;
|
|
34608
|
+
}
|
|
34602
34609
|
const shadow = asset.shadow;
|
|
34603
34610
|
if (!shadow) {
|
|
34604
34611
|
return void 0;
|
|
@@ -34705,27 +34712,10 @@ function createDrawCaptionWordOp(word, animState, asset, fontConfig) {
|
|
|
34705
34712
|
visibleCharacters: animState.visibleCharacters,
|
|
34706
34713
|
letterSpacing: fontConfig.letterSpacing > 0 ? fontConfig.letterSpacing : void 0,
|
|
34707
34714
|
stroke: extractStrokeConfig(asset, isActive),
|
|
34708
|
-
shadow: extractShadowConfig(asset),
|
|
34715
|
+
shadow: extractShadowConfig(asset, isActive),
|
|
34709
34716
|
background: extractBackgroundConfig(asset, isActive, fontConfig.size)
|
|
34710
34717
|
};
|
|
34711
34718
|
}
|
|
34712
|
-
function calculateGroupBounds(activeGroup, padding, frameWidth, activeScale, fontSize) {
|
|
34713
|
-
let minY = Infinity;
|
|
34714
|
-
let maxY = -Infinity;
|
|
34715
|
-
for (const line of activeGroup.lines) {
|
|
34716
|
-
const lineY = line.y - line.height * ASCENT_RATIO;
|
|
34717
|
-
const lineBottom = line.y + line.height * DESCENT_RATIO;
|
|
34718
|
-
if (lineY < minY) minY = lineY;
|
|
34719
|
-
if (lineBottom > maxY) maxY = lineBottom;
|
|
34720
|
-
}
|
|
34721
|
-
const scaleExpansion = activeScale > 1 ? (activeScale - 1) * fontSize : 0;
|
|
34722
|
-
return {
|
|
34723
|
-
bgX: 0,
|
|
34724
|
-
bgY: minY - padding.top - scaleExpansion,
|
|
34725
|
-
bgWidth: frameWidth,
|
|
34726
|
-
bgHeight: maxY - minY + padding.top + padding.bottom + scaleExpansion * 2
|
|
34727
|
-
};
|
|
34728
|
-
}
|
|
34729
34719
|
function generateRichCaptionDrawOps(asset, layout, frameTimeMs, layoutEngine, config2) {
|
|
34730
34720
|
if (layout.store.length === 0) {
|
|
34731
34721
|
return [];
|
|
@@ -34745,48 +34735,35 @@ function generateRichCaptionDrawOps(asset, layout, frameTimeMs, layoutEngine, co
|
|
|
34745
34735
|
fontConfig.size
|
|
34746
34736
|
);
|
|
34747
34737
|
const ops = [];
|
|
34748
|
-
const
|
|
34749
|
-
|
|
34750
|
-
|
|
34751
|
-
|
|
34752
|
-
|
|
34753
|
-
|
|
34754
|
-
|
|
34755
|
-
|
|
34756
|
-
|
|
34757
|
-
|
|
34758
|
-
|
|
34759
|
-
);
|
|
34760
|
-
|
|
34761
|
-
|
|
34762
|
-
|
|
34763
|
-
|
|
34764
|
-
|
|
34765
|
-
|
|
34766
|
-
|
|
34767
|
-
|
|
34768
|
-
|
|
34769
|
-
|
|
34770
|
-
|
|
34771
|
-
|
|
34772
|
-
|
|
34773
|
-
|
|
34774
|
-
|
|
34775
|
-
|
|
34776
|
-
|
|
34777
|
-
op: "RectangleStroke",
|
|
34778
|
-
x: bgX + halfBorder,
|
|
34779
|
-
y: bgY + halfBorder,
|
|
34780
|
-
width: bgWidth - borderConfig.width,
|
|
34781
|
-
height: bgHeight - borderConfig.width,
|
|
34782
|
-
stroke: {
|
|
34783
|
-
width: borderConfig.width,
|
|
34784
|
-
color: borderConfig.color,
|
|
34785
|
-
opacity: borderConfig.opacity
|
|
34786
|
-
},
|
|
34787
|
-
borderRadius: borderConfig.radius > 0 ? borderConfig.radius : void 0
|
|
34788
|
-
});
|
|
34789
|
-
}
|
|
34738
|
+
const captionBg = extractCaptionBackground(asset);
|
|
34739
|
+
if (captionBg) {
|
|
34740
|
+
ops.push({
|
|
34741
|
+
op: "DrawCaptionBackground",
|
|
34742
|
+
x: 0,
|
|
34743
|
+
y: 0,
|
|
34744
|
+
width: config2.frameWidth,
|
|
34745
|
+
height: config2.frameHeight,
|
|
34746
|
+
color: captionBg.color,
|
|
34747
|
+
opacity: captionBg.opacity,
|
|
34748
|
+
borderRadius: captionBg.borderRadius
|
|
34749
|
+
});
|
|
34750
|
+
}
|
|
34751
|
+
const borderConfig = extractCaptionBorder(asset);
|
|
34752
|
+
if (borderConfig) {
|
|
34753
|
+
const halfBorder = borderConfig.width / 2;
|
|
34754
|
+
ops.push({
|
|
34755
|
+
op: "RectangleStroke",
|
|
34756
|
+
x: halfBorder,
|
|
34757
|
+
y: halfBorder,
|
|
34758
|
+
width: config2.frameWidth - borderConfig.width,
|
|
34759
|
+
height: config2.frameHeight - borderConfig.width,
|
|
34760
|
+
stroke: {
|
|
34761
|
+
width: borderConfig.width,
|
|
34762
|
+
color: borderConfig.color,
|
|
34763
|
+
opacity: borderConfig.opacity
|
|
34764
|
+
},
|
|
34765
|
+
borderRadius: borderConfig.radius > 0 ? borderConfig.radius : void 0
|
|
34766
|
+
});
|
|
34790
34767
|
}
|
|
34791
34768
|
const nonActiveOps = [];
|
|
34792
34769
|
const activeOps = [];
|
|
@@ -35066,38 +35043,59 @@ function createWebPainter(canvas) {
|
|
|
35066
35043
|
captionWordOps.push(nextOp);
|
|
35067
35044
|
i++;
|
|
35068
35045
|
}
|
|
35069
|
-
|
|
35070
|
-
|
|
35046
|
+
let bgIdx = 0;
|
|
35047
|
+
while (bgIdx < captionWordOps.length) {
|
|
35048
|
+
const wordOp = captionWordOps[bgIdx];
|
|
35049
|
+
if (!wordOp.background) {
|
|
35050
|
+
bgIdx++;
|
|
35051
|
+
continue;
|
|
35052
|
+
}
|
|
35071
35053
|
const wordDisplayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
35072
|
-
if (wordDisplayText.length === 0)
|
|
35054
|
+
if (wordDisplayText.length === 0) {
|
|
35055
|
+
bgIdx++;
|
|
35056
|
+
continue;
|
|
35057
|
+
}
|
|
35058
|
+
const mergeGroup = [wordOp];
|
|
35059
|
+
let nextIdx = bgIdx + 1;
|
|
35060
|
+
while (nextIdx < captionWordOps.length) {
|
|
35061
|
+
const nextWord = captionWordOps[nextIdx];
|
|
35062
|
+
if (!nextWord.background) break;
|
|
35063
|
+
const nextDisplay = getVisibleText(nextWord.text, nextWord.visibleCharacters, nextWord.isRTL);
|
|
35064
|
+
if (nextDisplay.length === 0) break;
|
|
35065
|
+
if (Math.round(nextWord.y) !== Math.round(wordOp.y)) break;
|
|
35066
|
+
if (nextWord.background.color !== wordOp.background.color) break;
|
|
35067
|
+
mergeGroup.push(nextWord);
|
|
35068
|
+
nextIdx++;
|
|
35069
|
+
}
|
|
35070
|
+
const firstWord = mergeGroup[0];
|
|
35071
|
+
const lastWord = mergeGroup[mergeGroup.length - 1];
|
|
35072
|
+
const firstBg = firstWord.background;
|
|
35073
|
+
const mergedLeft = firstWord.x + firstWord.transform.translateX;
|
|
35074
|
+
const mergedRight = lastWord.x + lastWord.transform.translateX + lastWord.width;
|
|
35075
|
+
const mergedWidth = mergedRight - mergedLeft;
|
|
35073
35076
|
ctx.save();
|
|
35074
|
-
const bgTx = Math.round(
|
|
35075
|
-
const bgTy = Math.round(
|
|
35077
|
+
const bgTx = Math.round(mergedLeft);
|
|
35078
|
+
const bgTy = Math.round(firstWord.y + firstWord.transform.translateY);
|
|
35076
35079
|
ctx.translate(bgTx, bgTy);
|
|
35077
|
-
if (
|
|
35078
|
-
const halfWidth =
|
|
35080
|
+
if (firstWord.transform.scale !== 1) {
|
|
35081
|
+
const halfWidth = mergedWidth / 2;
|
|
35079
35082
|
ctx.translate(halfWidth, 0);
|
|
35080
|
-
ctx.scale(
|
|
35083
|
+
ctx.scale(firstWord.transform.scale, firstWord.transform.scale);
|
|
35081
35084
|
ctx.translate(-halfWidth, 0);
|
|
35082
35085
|
}
|
|
35083
|
-
ctx.globalAlpha =
|
|
35084
|
-
|
|
35085
|
-
|
|
35086
|
-
if (wordOp.letterSpacing) {
|
|
35087
|
-
ctx.letterSpacing = `${wordOp.letterSpacing}px`;
|
|
35088
|
-
}
|
|
35089
|
-
const bgTextWidth = wordOp.width;
|
|
35090
|
-
const bgAscent = wordOp.fontSize * ASCENT_RATIO;
|
|
35091
|
-
const bgDescent = wordOp.fontSize * DESCENT_RATIO;
|
|
35086
|
+
ctx.globalAlpha = firstWord.transform.opacity;
|
|
35087
|
+
const bgAscent = firstWord.fontSize * ASCENT_RATIO;
|
|
35088
|
+
const bgDescent = firstWord.fontSize * DESCENT_RATIO;
|
|
35092
35089
|
const bgTextHeight = bgAscent + bgDescent;
|
|
35093
|
-
const bgX = -
|
|
35094
|
-
const bgY = -bgAscent -
|
|
35095
|
-
const bgW =
|
|
35096
|
-
const bgH = bgTextHeight +
|
|
35097
|
-
const bgC = parseHex6(
|
|
35090
|
+
const bgX = -firstBg.padding;
|
|
35091
|
+
const bgY = -bgAscent - firstBg.padding;
|
|
35092
|
+
const bgW = mergedWidth + firstBg.padding * 2;
|
|
35093
|
+
const bgH = bgTextHeight + firstBg.padding * 2;
|
|
35094
|
+
const bgC = parseHex6(firstBg.color, firstBg.opacity);
|
|
35098
35095
|
ctx.fillStyle = `rgba(${bgC.r},${bgC.g},${bgC.b},${bgC.a})`;
|
|
35099
|
-
drawRoundedRect(ctx, bgX, bgY, bgW, bgH,
|
|
35096
|
+
drawRoundedRect(ctx, bgX, bgY, bgW, bgH, firstBg.borderRadius);
|
|
35100
35097
|
ctx.restore();
|
|
35098
|
+
bgIdx = nextIdx;
|
|
35101
35099
|
}
|
|
35102
35100
|
for (const wordOp of captionWordOps) {
|
|
35103
35101
|
const displayText = getVisibleText(wordOp.text, wordOp.visibleCharacters, wordOp.isRTL);
|
|
@@ -37220,16 +37218,19 @@ var CaptionLayoutEngine = class {
|
|
|
37220
37218
|
};
|
|
37221
37219
|
}).filter((g) => g !== null);
|
|
37222
37220
|
});
|
|
37221
|
+
const contentHeight = config2.frameHeight - config2.padding.top - config2.padding.bottom;
|
|
37222
|
+
const contentWidth = config2.frameWidth - config2.padding.left - config2.padding.right;
|
|
37223
|
+
const ASCENT_RATIO2 = 0.8;
|
|
37223
37224
|
const calculateGroupY = (group) => {
|
|
37224
37225
|
const totalHeight = group.lines.length * config2.fontSize * config2.lineHeight;
|
|
37225
37226
|
switch (config2.verticalAlign) {
|
|
37226
37227
|
case "top":
|
|
37227
|
-
return config2.fontSize *
|
|
37228
|
+
return config2.padding.top + config2.fontSize * ASCENT_RATIO2;
|
|
37228
37229
|
case "bottom":
|
|
37229
|
-
return config2.frameHeight -
|
|
37230
|
+
return config2.frameHeight - config2.padding.bottom - totalHeight + config2.fontSize * ASCENT_RATIO2;
|
|
37230
37231
|
case "middle":
|
|
37231
37232
|
default:
|
|
37232
|
-
return
|
|
37233
|
+
return config2.padding.top + (contentHeight - totalHeight) / 2 + config2.fontSize * ASCENT_RATIO2;
|
|
37233
37234
|
}
|
|
37234
37235
|
};
|
|
37235
37236
|
const allWordTexts = store.words.slice(0, store.length);
|
|
@@ -37237,12 +37238,12 @@ var CaptionLayoutEngine = class {
|
|
|
37237
37238
|
const calculateLineX = (lineWidth) => {
|
|
37238
37239
|
switch (config2.horizontalAlign) {
|
|
37239
37240
|
case "left":
|
|
37240
|
-
return config2.
|
|
37241
|
+
return config2.padding.left;
|
|
37241
37242
|
case "right":
|
|
37242
|
-
return config2.frameWidth - lineWidth - config2.
|
|
37243
|
+
return config2.frameWidth - lineWidth - config2.padding.right;
|
|
37243
37244
|
case "center":
|
|
37244
37245
|
default:
|
|
37245
|
-
return
|
|
37246
|
+
return config2.padding.left + (contentWidth - lineWidth) / 2;
|
|
37246
37247
|
}
|
|
37247
37248
|
};
|
|
37248
37249
|
for (const group of groups) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shotstack/shotstack-canvas",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
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",
|
|
@@ -32,8 +32,13 @@
|
|
|
32
32
|
"example:video": "node examples/node-video.mjs",
|
|
33
33
|
"example:web": "vite dev examples/web-example",
|
|
34
34
|
"test:caption-web": "vite dev examples/caption-tests",
|
|
35
|
+
"test:padding-node": "node examples/captionpaddingtests/node-test.mjs",
|
|
36
|
+
"test:padding-web": "vite dev examples/captionpaddingtests",
|
|
37
|
+
"test:fixes-node": "node examples/caption-fix-tests/node-test.mjs",
|
|
38
|
+
"test:fixes-web": "vite dev examples/caption-fix-tests",
|
|
35
39
|
"prepublishOnly": "node scripts/publish-guard.cjs && pnpm build",
|
|
36
|
-
"test": "node --test tests/build-verify.mjs"
|
|
40
|
+
"test": "node --test tests/build-verify.mjs",
|
|
41
|
+
"test:logic": "node --test tests/caption-logic.mjs"
|
|
37
42
|
},
|
|
38
43
|
"publishConfig": {
|
|
39
44
|
"access": "public",
|