@meframe/core 0.5.5 → 0.5.7
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/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +1 -0
- package/dist/{medeo-fe/node_modules → node_modules}/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js +14 -14
- package/dist/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +1 -0
- package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
- package/dist/orchestrator/ExportScheduler.js +13 -0
- package/dist/orchestrator/ExportScheduler.js.map +1 -1
- package/dist/stages/compose/VideoComposer.d.ts.map +1 -1
- package/dist/stages/compose/VideoComposer.js +2 -0
- package/dist/stages/compose/VideoComposer.js.map +1 -1
- package/dist/stages/compose/text-renderers/caption-stagger-entrance-renderer.d.ts +2 -0
- package/dist/stages/compose/text-renderers/caption-stagger-entrance-renderer.d.ts.map +1 -1
- package/dist/stages/compose/text-renderers/caption-stagger-entrance-renderer.js +78 -7
- package/dist/stages/compose/text-renderers/caption-stagger-entrance-renderer.js.map +1 -1
- package/dist/stages/compose/text-utils/text-wrapper.d.ts.map +1 -1
- package/dist/stages/compose/text-utils/text-wrapper.js +38 -113
- package/dist/stages/compose/text-utils/text-wrapper.js.map +1 -1
- package/dist/stages/mux/MP4Muxer.js +1 -1
- package/dist/utils/mp4box.js +2 -3
- package/dist/workers/stages/export/{export.worker.C9m51RNP.js → export.worker.p7X_YtxQ.js} +117 -122
- package/dist/workers/stages/export/export.worker.p7X_YtxQ.js.map +1 -0
- package/dist/workers/worker-manifest.json +1 -1
- package/package.json +1 -1
- package/dist/medeo-fe/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +0 -1
- package/dist/medeo-fe/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +0 -1
- package/dist/workers/stages/export/export.worker.C9m51RNP.js.map +0 -1
- /package/dist/{medeo-fe/node_modules → node_modules}/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js +0 -0
|
@@ -16,129 +16,54 @@ function findAllBreakPoints(text) {
|
|
|
16
16
|
breakPoints.push(chars.length);
|
|
17
17
|
return breakPoints;
|
|
18
18
|
}
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return lengths.reduce((sum, len) => sum + Math.abs(len - avgLength), 0);
|
|
26
|
-
}
|
|
27
|
-
function tryBreakPointsForMultipleLines(ctx, text, start, remainingLines, currentLines, maxWidth, fontSize, fontFamily, fontWeight, breakPoints) {
|
|
28
|
-
let bestLines = [];
|
|
29
|
-
let bestBalance = Infinity;
|
|
30
|
-
if (remainingLines === 1) {
|
|
31
|
-
const lastLine = text.slice(start).trim();
|
|
32
|
-
const lastLineWidth = measureTextWidth(ctx, lastLine, fontSize, fontFamily, fontWeight);
|
|
33
|
-
if (lastLineWidth <= maxWidth) {
|
|
34
|
-
const allLines = [...currentLines, lastLine];
|
|
35
|
-
const balance = evaluateBalance(allLines, ctx, fontSize, fontFamily, fontWeight);
|
|
36
|
-
if (balance < bestBalance) {
|
|
37
|
-
bestBalance = balance;
|
|
38
|
-
bestLines = allLines;
|
|
39
|
-
}
|
|
40
|
-
} else {
|
|
41
|
-
const words = lastLine.split(/\s+/);
|
|
42
|
-
let currentLine = "";
|
|
43
|
-
let tempLines = [...currentLines];
|
|
44
|
-
for (const word of words) {
|
|
45
|
-
const testLine = currentLine ? `${currentLine} ${word}` : word;
|
|
46
|
-
const lineWidth = measureTextWidth(ctx, testLine, fontSize, fontFamily, fontWeight);
|
|
47
|
-
if (lineWidth <= maxWidth) {
|
|
48
|
-
currentLine = testLine;
|
|
49
|
-
} else {
|
|
50
|
-
if (currentLine) {
|
|
51
|
-
tempLines.push(currentLine);
|
|
52
|
-
currentLine = word;
|
|
53
|
-
} else {
|
|
54
|
-
tempLines.push(word);
|
|
55
|
-
currentLine = "";
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
if (currentLine) {
|
|
60
|
-
tempLines.push(currentLine);
|
|
61
|
-
}
|
|
62
|
-
const balance = evaluateBalance(tempLines, ctx, fontSize, fontFamily, fontWeight);
|
|
63
|
-
if (balance < bestBalance) {
|
|
64
|
-
bestBalance = balance;
|
|
65
|
-
bestLines = tempLines;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return { bestLines, bestBalance };
|
|
69
|
-
}
|
|
70
|
-
let foundValidBreak = false;
|
|
71
|
-
for (let i = 0; i < breakPoints.length; i++) {
|
|
19
|
+
function greedyWrap(ctx, text, maxWidth, fontSize, fontFamily, fontWeight) {
|
|
20
|
+
const breakPoints = findAllBreakPoints(text);
|
|
21
|
+
const lines = [];
|
|
22
|
+
let current = "";
|
|
23
|
+
let prev = breakPoints[0];
|
|
24
|
+
for (let i = 1; i < breakPoints.length; i++) {
|
|
72
25
|
const bp = breakPoints[i];
|
|
73
|
-
if (bp <=
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
remainingLines - 1,
|
|
83
|
-
[...currentLines, line],
|
|
84
|
-
maxWidth,
|
|
85
|
-
fontSize,
|
|
86
|
-
fontFamily,
|
|
87
|
-
fontWeight,
|
|
88
|
-
breakPoints
|
|
89
|
-
);
|
|
90
|
-
if (result.bestBalance < bestBalance) {
|
|
91
|
-
bestBalance = result.bestBalance;
|
|
92
|
-
bestLines = result.bestLines;
|
|
93
|
-
}
|
|
26
|
+
if (bp <= prev) continue;
|
|
27
|
+
const segment = text.slice(prev, bp);
|
|
28
|
+
const candidate = current + segment;
|
|
29
|
+
const candidateWidth = measureTextWidth(ctx, candidate, fontSize, fontFamily, fontWeight);
|
|
30
|
+
if (candidateWidth <= maxWidth || current === "") {
|
|
31
|
+
current = candidate;
|
|
32
|
+
} else {
|
|
33
|
+
lines.push(current.trim());
|
|
34
|
+
current = segment;
|
|
94
35
|
}
|
|
36
|
+
prev = bp;
|
|
95
37
|
}
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
const words = textPortion.split(/\s+/);
|
|
99
|
-
let currentLine = "";
|
|
100
|
-
let tempLines = [...currentLines];
|
|
101
|
-
for (const word of words) {
|
|
102
|
-
const testLine = currentLine ? `${currentLine} ${word}` : word;
|
|
103
|
-
const lineWidth = measureTextWidth(ctx, testLine, fontSize, fontFamily, fontWeight);
|
|
104
|
-
if (lineWidth <= maxWidth) {
|
|
105
|
-
currentLine = testLine;
|
|
106
|
-
} else {
|
|
107
|
-
if (currentLine) {
|
|
108
|
-
tempLines.push(currentLine);
|
|
109
|
-
currentLine = word;
|
|
110
|
-
} else {
|
|
111
|
-
tempLines.push(word);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
if (currentLine) {
|
|
116
|
-
tempLines.push(currentLine);
|
|
117
|
-
}
|
|
118
|
-
bestLines = tempLines;
|
|
38
|
+
if (current.trim()) {
|
|
39
|
+
lines.push(current.trim());
|
|
119
40
|
}
|
|
120
|
-
|
|
41
|
+
fixTailOrphan(lines, ctx, maxWidth, fontSize, fontFamily, fontWeight);
|
|
42
|
+
return lines.length > 0 ? lines : [text];
|
|
43
|
+
}
|
|
44
|
+
function fixTailOrphan(lines, ctx, maxWidth, fontSize, fontFamily, fontWeight) {
|
|
45
|
+
if (lines.length < 2) return;
|
|
46
|
+
const last = lines[lines.length - 1];
|
|
47
|
+
const lastWords = last.split(/\s+/).filter(Boolean);
|
|
48
|
+
if (lastWords.length > 1) return;
|
|
49
|
+
const prevLine = lines[lines.length - 2];
|
|
50
|
+
const prevWords = prevLine.split(/\s+/).filter(Boolean);
|
|
51
|
+
if (prevWords.length < 2) return;
|
|
52
|
+
const moved = prevWords.pop();
|
|
53
|
+
const newPrev = prevWords.join(" ");
|
|
54
|
+
const newLast = `${moved} ${last}`.trim();
|
|
55
|
+
const newPrevWidth = measureTextWidth(ctx, newPrev, fontSize, fontFamily, fontWeight);
|
|
56
|
+
const newLastWidth = measureTextWidth(ctx, newLast, fontSize, fontFamily, fontWeight);
|
|
57
|
+
if (newPrevWidth > maxWidth || newLastWidth > maxWidth) return;
|
|
58
|
+
lines[lines.length - 2] = newPrev;
|
|
59
|
+
lines[lines.length - 1] = newLast;
|
|
121
60
|
}
|
|
122
61
|
function wrapText(ctx, text, maxWidth, fontSize, fontFamily, fontWeight = 400) {
|
|
123
62
|
const textWidth = measureTextWidth(ctx, text, fontSize, fontFamily, fontWeight);
|
|
124
63
|
if (textWidth <= maxWidth) {
|
|
125
64
|
return [text];
|
|
126
65
|
}
|
|
127
|
-
|
|
128
|
-
const breakPoints = findAllBreakPoints(text);
|
|
129
|
-
const { bestLines } = tryBreakPointsForMultipleLines(
|
|
130
|
-
ctx,
|
|
131
|
-
text,
|
|
132
|
-
0,
|
|
133
|
-
estimatedLines,
|
|
134
|
-
[],
|
|
135
|
-
maxWidth,
|
|
136
|
-
fontSize,
|
|
137
|
-
fontFamily,
|
|
138
|
-
fontWeight,
|
|
139
|
-
breakPoints
|
|
140
|
-
);
|
|
141
|
-
return bestLines.length > 0 ? bestLines : [text];
|
|
66
|
+
return greedyWrap(ctx, text, maxWidth, fontSize, fontFamily, fontWeight);
|
|
142
67
|
}
|
|
143
68
|
function formLinesWithWords(ctx, words, maxWidth, fontSize, needsSpace, fontFamily, fontWeight = 400) {
|
|
144
69
|
const result = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-wrapper.js","sources":["../../../../src/stages/compose/text-utils/text-wrapper.ts"],"sourcesContent":["import { measureTextWidth } from './text-metrics';\n\nfunction findAllBreakPoints(text: string): number[] {\n const breakPoints = [0];\n const chars = Array.from(text);\n\n for (let i = 1; i < chars.length - 1; i++) {\n if (/[、。!?,,!?;:]/.test(chars[i]!)) {\n breakPoints.push(i + 1);\n } else if (\n /[\\u3040-\\u309F\\u30A0-\\u30FF\\u4E00-\\u9FAF]/.test(chars[i]!) &&\n /[\\u3040-\\u309F\\u30A0-\\u30FF\\u4E00-\\u9FAF]/.test(chars[i + 1]!)\n ) {\n breakPoints.push(i + 1);\n } else if (/[\\s\\-–—,.!?;:]/.test(chars[i]!)) {\n breakPoints.push(i + 1);\n } else if (/\\s/.test(chars[i]!) && /[a-zA-Z]/.test(chars[i + 1]!)) {\n breakPoints.push(i + 1);\n }\n }\n\n breakPoints.push(chars.length);\n return breakPoints;\n}\n\nfunction evaluateBalance(\n lines: string[],\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n fontSize: number,\n fontFamily: string,\n fontWeight: string | number\n): number {\n if (lines.length <= 1) return 0;\n const lengths = lines.map((line) =>\n measureTextWidth(ctx, line, fontSize, fontFamily, fontWeight)\n );\n const avgLength = lengths.reduce((a, b) => a + b, 0) / lengths.length;\n return lengths.reduce((sum, len) => sum + Math.abs(len - avgLength), 0);\n}\n\nfunction tryBreakPointsForMultipleLines(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n text: string,\n start: number,\n remainingLines: number,\n currentLines: string[],\n maxWidth: number,\n fontSize: number,\n fontFamily: string,\n fontWeight: string | number,\n breakPoints: number[]\n): {\n bestLines: string[];\n bestBalance: number;\n} {\n let bestLines: string[] = [];\n let bestBalance = Infinity;\n\n if (remainingLines === 1) {\n const lastLine = text.slice(start).trim();\n const lastLineWidth = measureTextWidth(ctx, lastLine, fontSize, fontFamily, fontWeight);\n\n if (lastLineWidth <= maxWidth) {\n const allLines = [...currentLines, lastLine];\n const balance = evaluateBalance(allLines, ctx, fontSize, fontFamily, fontWeight);\n\n if (balance < bestBalance) {\n bestBalance = balance;\n bestLines = allLines;\n }\n } else {\n const words = lastLine.split(/\\s+/);\n let currentLine = '';\n let tempLines = [...currentLines];\n\n for (const word of words) {\n const testLine = currentLine ? `${currentLine} ${word}` : word;\n const lineWidth = measureTextWidth(ctx, testLine, fontSize, fontFamily, fontWeight);\n\n if (lineWidth <= maxWidth) {\n currentLine = testLine;\n } else {\n if (currentLine) {\n tempLines.push(currentLine);\n currentLine = word;\n } else {\n tempLines.push(word);\n currentLine = '';\n }\n }\n }\n\n if (currentLine) {\n tempLines.push(currentLine);\n }\n\n const balance = evaluateBalance(tempLines, ctx, fontSize, fontFamily, fontWeight);\n if (balance < bestBalance) {\n bestBalance = balance;\n bestLines = tempLines;\n }\n }\n return { bestLines, bestBalance };\n }\n\n let foundValidBreak = false;\n\n for (let i = 0; i < breakPoints.length; i++) {\n const bp = breakPoints[i]!;\n if (bp <= start || bp >= text.length) continue;\n\n const line = text.slice(start, bp).trim();\n const lineWidth = measureTextWidth(ctx, line, fontSize, fontFamily, fontWeight);\n\n if (lineWidth <= maxWidth) {\n foundValidBreak = true;\n const result = tryBreakPointsForMultipleLines(\n ctx,\n text,\n bp,\n remainingLines - 1,\n [...currentLines, line],\n maxWidth,\n fontSize,\n fontFamily,\n fontWeight,\n breakPoints\n );\n if (result.bestBalance < bestBalance) {\n bestBalance = result.bestBalance;\n bestLines = result.bestLines;\n }\n }\n }\n\n if (!foundValidBreak) {\n const textPortion = text.slice(start);\n const words = textPortion.split(/\\s+/);\n let currentLine = '';\n let tempLines = [...currentLines];\n\n for (const word of words) {\n const testLine = currentLine ? `${currentLine} ${word}` : word;\n const lineWidth = measureTextWidth(ctx, testLine, fontSize, fontFamily, fontWeight);\n\n if (lineWidth <= maxWidth) {\n currentLine = testLine;\n } else {\n if (currentLine) {\n tempLines.push(currentLine);\n currentLine = word;\n } else {\n tempLines.push(word);\n }\n }\n }\n\n if (currentLine) {\n tempLines.push(currentLine);\n }\n\n bestLines = tempLines;\n }\n\n return { bestLines, bestBalance };\n}\n\nexport function wrapText(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n text: string,\n maxWidth: number,\n fontSize: number,\n fontFamily: string,\n fontWeight: string | number = 400\n): string[] {\n const textWidth = measureTextWidth(ctx, text, fontSize, fontFamily, fontWeight);\n if (textWidth <= maxWidth) {\n return [text];\n }\n\n const estimatedLines = Math.ceil(textWidth / maxWidth);\n const breakPoints = findAllBreakPoints(text);\n\n const { bestLines } = tryBreakPointsForMultipleLines(\n ctx,\n text,\n 0,\n estimatedLines,\n [],\n maxWidth,\n fontSize,\n fontFamily,\n fontWeight,\n breakPoints\n );\n\n return bestLines.length > 0 ? bestLines : [text];\n}\n\nexport function formLinesWithWords(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n words: string[],\n maxWidth: number,\n fontSize: number,\n needsSpace: boolean,\n fontFamily: string,\n fontWeight: string | number = 400\n): string[] {\n const result: string[] = [];\n let accumulatedWidth = 0;\n const spaceWidth = measureTextWidth(ctx, ' ', fontSize, fontFamily, fontWeight);\n let currentLine = '';\n\n for (const word of words) {\n let wordWidth = measureTextWidth(ctx, word, fontSize, fontFamily, fontWeight);\n if (needsSpace) {\n wordWidth += spaceWidth;\n }\n if (wordWidth + accumulatedWidth <= maxWidth) {\n currentLine += word + (needsSpace ? ' ' : '');\n accumulatedWidth += wordWidth;\n } else {\n if (currentLine) {\n result.push(currentLine);\n }\n currentLine = word + (needsSpace ? ' ' : '');\n accumulatedWidth = wordWidth;\n }\n }\n if (currentLine !== '') {\n result.push(currentLine);\n }\n return result;\n}\n\nexport function formEvenLinesWithWords(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n words: string[],\n maxWidth: number,\n fontSize: number,\n needsSpace: boolean,\n fontFamily: string,\n fontWeight: string | number = 400\n): string[] {\n let minWidth = maxWidth / 2;\n for (const word of words) {\n const wordWidth = measureTextWidth(ctx, word, fontSize, fontFamily, fontWeight);\n if (wordWidth > minWidth) {\n minWidth = wordWidth;\n }\n }\n\n const leastLineNum = formLinesWithWords(\n ctx,\n words,\n maxWidth,\n fontSize,\n needsSpace,\n fontFamily,\n fontWeight\n ).length;\n\n let bestDelta = maxWidth;\n let bestWidth = minWidth;\n for (let width = maxWidth; width >= minWidth; width -= 1) {\n const lines = formLinesWithWords(\n ctx,\n words,\n width,\n fontSize,\n needsSpace,\n fontFamily,\n fontWeight\n );\n if (lines.length > leastLineNum) {\n break;\n }\n let minLineWidth = Infinity;\n let maxLineWidth = 0;\n for (const line of lines) {\n const lineWidth = measureTextWidth(ctx, line, fontSize, fontFamily, fontWeight);\n if (lineWidth < minLineWidth) {\n minLineWidth = lineWidth;\n }\n if (lineWidth > maxLineWidth) {\n maxLineWidth = lineWidth;\n }\n }\n const delta = maxLineWidth - minLineWidth;\n if (delta < bestDelta) {\n bestDelta = delta;\n bestWidth = width;\n }\n }\n\n return formLinesWithWords(ctx, words, bestWidth, fontSize, needsSpace, fontFamily, fontWeight);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"text-wrapper.js","sources":["../../../../src/stages/compose/text-utils/text-wrapper.ts"],"sourcesContent":["import { measureTextWidth } from './text-metrics';\n\nfunction findAllBreakPoints(text: string): number[] {\n const breakPoints = [0];\n const chars = Array.from(text);\n\n for (let i = 1; i < chars.length - 1; i++) {\n if (/[、。!?,,!?;:]/.test(chars[i]!)) {\n breakPoints.push(i + 1);\n } else if (\n /[\\u3040-\\u309F\\u30A0-\\u30FF\\u4E00-\\u9FAF]/.test(chars[i]!) &&\n /[\\u3040-\\u309F\\u30A0-\\u30FF\\u4E00-\\u9FAF]/.test(chars[i + 1]!)\n ) {\n breakPoints.push(i + 1);\n } else if (/[\\s\\-–—,.!?;:]/.test(chars[i]!)) {\n breakPoints.push(i + 1);\n } else if (/\\s/.test(chars[i]!) && /[a-zA-Z]/.test(chars[i + 1]!)) {\n breakPoints.push(i + 1);\n }\n }\n\n breakPoints.push(chars.length);\n return breakPoints;\n}\n\n// 线性贪心折行:从左到右把合法片段往当前行塞,塞不下就换行。\n// 复杂度 O(B)(B = 断点数),替代原 O(B^N) 的递归回溯。\n// 视觉缺陷:最后一行可能只剩一个词(\"孤儿\"),由 fixTailOrphan 修复。\nfunction greedyWrap(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n text: string,\n maxWidth: number,\n fontSize: number,\n fontFamily: string,\n fontWeight: string | number\n): string[] {\n const breakPoints = findAllBreakPoints(text);\n const lines: string[] = [];\n let current = '';\n let prev = breakPoints[0]!;\n\n for (let i = 1; i < breakPoints.length; i++) {\n const bp = breakPoints[i]!;\n if (bp <= prev) continue;\n\n const segment = text.slice(prev, bp);\n const candidate = current + segment;\n const candidateWidth = measureTextWidth(ctx, candidate, fontSize, fontFamily, fontWeight);\n\n // 能塞下就并入当前行;current === '' 兜底:单个 segment 已经超宽时也强制保留,\n // 避免死循环。这种超宽 segment(如超长无空格英文单词)由调用方处理或自然溢出。\n if (candidateWidth <= maxWidth || current === '') {\n current = candidate;\n } else {\n lines.push(current.trim());\n current = segment;\n }\n prev = bp;\n }\n\n if (current.trim()) {\n lines.push(current.trim());\n }\n\n fixTailOrphan(lines, ctx, maxWidth, fontSize, fontFamily, fontWeight);\n\n return lines.length > 0 ? lines : [text];\n}\n\n// 尾行孤儿修正:贪心结束后若最后一行只剩 1 个词,\n// 从倒数第二行末尾挪 1 个词到最后一行,仅当移动后两行都不超宽时才接受。\n// 视觉上避免出现 \"断头\" 的最后一行。原地修改 lines。\nfunction fixTailOrphan(\n lines: string[],\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n maxWidth: number,\n fontSize: number,\n fontFamily: string,\n fontWeight: string | number\n): void {\n if (lines.length < 2) return;\n\n const last = lines[lines.length - 1]!;\n const lastWords = last.split(/\\s+/).filter(Boolean);\n if (lastWords.length > 1) return;\n\n const prevLine = lines[lines.length - 2]!;\n const prevWords = prevLine.split(/\\s+/).filter(Boolean);\n if (prevWords.length < 2) return;\n\n const moved = prevWords.pop()!;\n const newPrev = prevWords.join(' ');\n const newLast = `${moved} ${last}`.trim();\n\n // 移动后必须两行都不超宽,否则放弃修正、保留原贪心结果\n const newPrevWidth = measureTextWidth(ctx, newPrev, fontSize, fontFamily, fontWeight);\n const newLastWidth = measureTextWidth(ctx, newLast, fontSize, fontFamily, fontWeight);\n if (newPrevWidth > maxWidth || newLastWidth > maxWidth) return;\n\n lines[lines.length - 2] = newPrev;\n lines[lines.length - 1] = newLast;\n}\n\nexport function wrapText(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n text: string,\n maxWidth: number,\n fontSize: number,\n fontFamily: string,\n fontWeight: string | number = 400\n): string[] {\n // 整段不超宽直接单行返回(最常见的快路径)\n const textWidth = measureTextWidth(ctx, text, fontSize, fontFamily, fontWeight);\n if (textWidth <= maxWidth) {\n return [text];\n }\n\n return greedyWrap(ctx, text, maxWidth, fontSize, fontFamily, fontWeight);\n}\n\nexport function formLinesWithWords(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n words: string[],\n maxWidth: number,\n fontSize: number,\n needsSpace: boolean,\n fontFamily: string,\n fontWeight: string | number = 400\n): string[] {\n const result: string[] = [];\n let accumulatedWidth = 0;\n const spaceWidth = measureTextWidth(ctx, ' ', fontSize, fontFamily, fontWeight);\n let currentLine = '';\n\n for (const word of words) {\n let wordWidth = measureTextWidth(ctx, word, fontSize, fontFamily, fontWeight);\n if (needsSpace) {\n wordWidth += spaceWidth;\n }\n if (wordWidth + accumulatedWidth <= maxWidth) {\n currentLine += word + (needsSpace ? ' ' : '');\n accumulatedWidth += wordWidth;\n } else {\n if (currentLine) {\n result.push(currentLine);\n }\n currentLine = word + (needsSpace ? ' ' : '');\n accumulatedWidth = wordWidth;\n }\n }\n if (currentLine !== '') {\n result.push(currentLine);\n }\n return result;\n}\n\nexport function formEvenLinesWithWords(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n words: string[],\n maxWidth: number,\n fontSize: number,\n needsSpace: boolean,\n fontFamily: string,\n fontWeight: string | number = 400\n): string[] {\n let minWidth = maxWidth / 2;\n for (const word of words) {\n const wordWidth = measureTextWidth(ctx, word, fontSize, fontFamily, fontWeight);\n if (wordWidth > minWidth) {\n minWidth = wordWidth;\n }\n }\n\n const leastLineNum = formLinesWithWords(\n ctx,\n words,\n maxWidth,\n fontSize,\n needsSpace,\n fontFamily,\n fontWeight\n ).length;\n\n let bestDelta = maxWidth;\n let bestWidth = minWidth;\n for (let width = maxWidth; width >= minWidth; width -= 1) {\n const lines = formLinesWithWords(\n ctx,\n words,\n width,\n fontSize,\n needsSpace,\n fontFamily,\n fontWeight\n );\n if (lines.length > leastLineNum) {\n break;\n }\n let minLineWidth = Infinity;\n let maxLineWidth = 0;\n for (const line of lines) {\n const lineWidth = measureTextWidth(ctx, line, fontSize, fontFamily, fontWeight);\n if (lineWidth < minLineWidth) {\n minLineWidth = lineWidth;\n }\n if (lineWidth > maxLineWidth) {\n maxLineWidth = lineWidth;\n }\n }\n const delta = maxLineWidth - minLineWidth;\n if (delta < bestDelta) {\n bestDelta = delta;\n bestWidth = width;\n }\n }\n\n return formLinesWithWords(ctx, words, bestWidth, fontSize, needsSpace, fontFamily, fontWeight);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StreamTarget, ArrayBufferTarget, Muxer } from "../../
|
|
1
|
+
import { StreamTarget, ArrayBufferTarget, Muxer } from "../../node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js";
|
|
2
2
|
import { checkBrowserCompatibility } from "../../utils/platform-utils.js";
|
|
3
3
|
class MP4Muxer {
|
|
4
4
|
muxer;
|
package/dist/utils/mp4box.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as mp4box_all from "../
|
|
1
|
+
import * as mp4box_all from "../node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js";
|
|
2
2
|
const lib = mp4box_all;
|
|
3
3
|
const MP4Box = lib.default && typeof lib.default.createFile === "function" ? lib.default : lib;
|
|
4
4
|
if (typeof MP4Box.createFile !== "function") {
|
|
@@ -11,7 +11,6 @@ if (typeof MP4Box.createFile !== "function") {
|
|
|
11
11
|
);
|
|
12
12
|
}
|
|
13
13
|
export {
|
|
14
|
-
MP4Box
|
|
15
|
-
MP4Box as default
|
|
14
|
+
MP4Box
|
|
16
15
|
};
|
|
17
16
|
//# sourceMappingURL=mp4box.js.map
|
|
@@ -558,129 +558,54 @@ function findAllBreakPoints(text) {
|
|
|
558
558
|
breakPoints.push(chars.length);
|
|
559
559
|
return breakPoints;
|
|
560
560
|
}
|
|
561
|
-
function
|
|
562
|
-
|
|
563
|
-
const
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
return lengths.reduce((sum, len) => sum + Math.abs(len - avgLength), 0);
|
|
568
|
-
}
|
|
569
|
-
function tryBreakPointsForMultipleLines(ctx, text, start, remainingLines, currentLines, maxWidth, fontSize, fontFamily, fontWeight, breakPoints) {
|
|
570
|
-
let bestLines = [];
|
|
571
|
-
let bestBalance = Infinity;
|
|
572
|
-
if (remainingLines === 1) {
|
|
573
|
-
const lastLine = text.slice(start).trim();
|
|
574
|
-
const lastLineWidth = measureTextWidth(ctx, lastLine, fontSize, fontFamily, fontWeight);
|
|
575
|
-
if (lastLineWidth <= maxWidth) {
|
|
576
|
-
const allLines = [...currentLines, lastLine];
|
|
577
|
-
const balance = evaluateBalance(allLines, ctx, fontSize, fontFamily, fontWeight);
|
|
578
|
-
if (balance < bestBalance) {
|
|
579
|
-
bestBalance = balance;
|
|
580
|
-
bestLines = allLines;
|
|
581
|
-
}
|
|
582
|
-
} else {
|
|
583
|
-
const words = lastLine.split(/\s+/);
|
|
584
|
-
let currentLine = "";
|
|
585
|
-
let tempLines = [...currentLines];
|
|
586
|
-
for (const word of words) {
|
|
587
|
-
const testLine = currentLine ? `${currentLine} ${word}` : word;
|
|
588
|
-
const lineWidth = measureTextWidth(ctx, testLine, fontSize, fontFamily, fontWeight);
|
|
589
|
-
if (lineWidth <= maxWidth) {
|
|
590
|
-
currentLine = testLine;
|
|
591
|
-
} else {
|
|
592
|
-
if (currentLine) {
|
|
593
|
-
tempLines.push(currentLine);
|
|
594
|
-
currentLine = word;
|
|
595
|
-
} else {
|
|
596
|
-
tempLines.push(word);
|
|
597
|
-
currentLine = "";
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
if (currentLine) {
|
|
602
|
-
tempLines.push(currentLine);
|
|
603
|
-
}
|
|
604
|
-
const balance = evaluateBalance(tempLines, ctx, fontSize, fontFamily, fontWeight);
|
|
605
|
-
if (balance < bestBalance) {
|
|
606
|
-
bestBalance = balance;
|
|
607
|
-
bestLines = tempLines;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
return { bestLines, bestBalance };
|
|
611
|
-
}
|
|
612
|
-
let foundValidBreak = false;
|
|
613
|
-
for (let i = 0; i < breakPoints.length; i++) {
|
|
561
|
+
function greedyWrap(ctx, text, maxWidth, fontSize, fontFamily, fontWeight) {
|
|
562
|
+
const breakPoints = findAllBreakPoints(text);
|
|
563
|
+
const lines = [];
|
|
564
|
+
let current = "";
|
|
565
|
+
let prev = breakPoints[0];
|
|
566
|
+
for (let i = 1; i < breakPoints.length; i++) {
|
|
614
567
|
const bp = breakPoints[i];
|
|
615
|
-
if (bp <=
|
|
616
|
-
const
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
if (currentLine) {
|
|
650
|
-
tempLines.push(currentLine);
|
|
651
|
-
currentLine = word;
|
|
652
|
-
} else {
|
|
653
|
-
tempLines.push(word);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
if (currentLine) {
|
|
658
|
-
tempLines.push(currentLine);
|
|
659
|
-
}
|
|
660
|
-
bestLines = tempLines;
|
|
661
|
-
}
|
|
662
|
-
return { bestLines, bestBalance };
|
|
568
|
+
if (bp <= prev) continue;
|
|
569
|
+
const segment = text.slice(prev, bp);
|
|
570
|
+
const candidate = current + segment;
|
|
571
|
+
const candidateWidth = measureTextWidth(ctx, candidate, fontSize, fontFamily, fontWeight);
|
|
572
|
+
if (candidateWidth <= maxWidth || current === "") {
|
|
573
|
+
current = candidate;
|
|
574
|
+
} else {
|
|
575
|
+
lines.push(current.trim());
|
|
576
|
+
current = segment;
|
|
577
|
+
}
|
|
578
|
+
prev = bp;
|
|
579
|
+
}
|
|
580
|
+
if (current.trim()) {
|
|
581
|
+
lines.push(current.trim());
|
|
582
|
+
}
|
|
583
|
+
fixTailOrphan(lines, ctx, maxWidth, fontSize, fontFamily, fontWeight);
|
|
584
|
+
return lines.length > 0 ? lines : [text];
|
|
585
|
+
}
|
|
586
|
+
function fixTailOrphan(lines, ctx, maxWidth, fontSize, fontFamily, fontWeight) {
|
|
587
|
+
if (lines.length < 2) return;
|
|
588
|
+
const last = lines[lines.length - 1];
|
|
589
|
+
const lastWords = last.split(/\s+/).filter(Boolean);
|
|
590
|
+
if (lastWords.length > 1) return;
|
|
591
|
+
const prevLine = lines[lines.length - 2];
|
|
592
|
+
const prevWords = prevLine.split(/\s+/).filter(Boolean);
|
|
593
|
+
if (prevWords.length < 2) return;
|
|
594
|
+
const moved = prevWords.pop();
|
|
595
|
+
const newPrev = prevWords.join(" ");
|
|
596
|
+
const newLast = `${moved} ${last}`.trim();
|
|
597
|
+
const newPrevWidth = measureTextWidth(ctx, newPrev, fontSize, fontFamily, fontWeight);
|
|
598
|
+
const newLastWidth = measureTextWidth(ctx, newLast, fontSize, fontFamily, fontWeight);
|
|
599
|
+
if (newPrevWidth > maxWidth || newLastWidth > maxWidth) return;
|
|
600
|
+
lines[lines.length - 2] = newPrev;
|
|
601
|
+
lines[lines.length - 1] = newLast;
|
|
663
602
|
}
|
|
664
603
|
function wrapText(ctx, text, maxWidth, fontSize, fontFamily, fontWeight = 400) {
|
|
665
604
|
const textWidth = measureTextWidth(ctx, text, fontSize, fontFamily, fontWeight);
|
|
666
605
|
if (textWidth <= maxWidth) {
|
|
667
606
|
return [text];
|
|
668
607
|
}
|
|
669
|
-
|
|
670
|
-
const breakPoints = findAllBreakPoints(text);
|
|
671
|
-
const { bestLines } = tryBreakPointsForMultipleLines(
|
|
672
|
-
ctx,
|
|
673
|
-
text,
|
|
674
|
-
0,
|
|
675
|
-
estimatedLines,
|
|
676
|
-
[],
|
|
677
|
-
maxWidth,
|
|
678
|
-
fontSize,
|
|
679
|
-
fontFamily,
|
|
680
|
-
fontWeight,
|
|
681
|
-
breakPoints
|
|
682
|
-
);
|
|
683
|
-
return bestLines.length > 0 ? bestLines : [text];
|
|
608
|
+
return greedyWrap(ctx, text, maxWidth, fontSize, fontFamily, fontWeight);
|
|
684
609
|
}
|
|
685
610
|
function formLinesWithWords(ctx, words, maxWidth, fontSize, needsSpace, fontFamily, fontWeight = 400) {
|
|
686
611
|
const result = [];
|
|
@@ -1048,6 +973,34 @@ function charProgress(relativeFrame, fps, charIndex, staggerMs, durationMs) {
|
|
|
1048
973
|
const raw = (tMs - startMs) / durationMs;
|
|
1049
974
|
return easeOutExpo(Math.min(1, raw));
|
|
1050
975
|
}
|
|
976
|
+
const charSlotsCache = /* @__PURE__ */ new Map();
|
|
977
|
+
const staggerFinalRasterCache = /* @__PURE__ */ new Map();
|
|
978
|
+
function layoutCacheKey(layer, canvasWidth, canvasHeight) {
|
|
979
|
+
const ts = layer.fontConfig?.textStyle;
|
|
980
|
+
return [
|
|
981
|
+
layer.id,
|
|
982
|
+
layer.text,
|
|
983
|
+
layer.letterCase ?? "",
|
|
984
|
+
canvasWidth,
|
|
985
|
+
canvasHeight,
|
|
986
|
+
ts?.fontSize,
|
|
987
|
+
ts?.fontFamily,
|
|
988
|
+
ts?.fontWeight,
|
|
989
|
+
ts?.lineHeight,
|
|
990
|
+
JSON.stringify(layer.fontConfig?.globalPosition ?? null)
|
|
991
|
+
].join("");
|
|
992
|
+
}
|
|
993
|
+
function staggerRasterKey(layer, canvasWidth, canvasHeight, preset) {
|
|
994
|
+
return `${layoutCacheKey(layer, canvasWidth, canvasHeight)}${preset}`;
|
|
995
|
+
}
|
|
996
|
+
function staggerEntranceEndMs(slotCount, preset) {
|
|
997
|
+
if (slotCount <= 0) return 0;
|
|
998
|
+
const lastIndex = slotCount - 1;
|
|
999
|
+
if (preset === "typewriter") {
|
|
1000
|
+
return lastIndex * DEFAULT_STAGGER_MS + DEFAULT_STAGGER_MS;
|
|
1001
|
+
}
|
|
1002
|
+
return lastIndex * DEFAULT_STAGGER_MS + DEFAULT_DURATION_MS;
|
|
1003
|
+
}
|
|
1051
1004
|
function buildCharSlots(ctx, layer, canvasWidth, canvasHeight) {
|
|
1052
1005
|
const fontConfig = layer.fontConfig?.textStyle;
|
|
1053
1006
|
if (!fontConfig) return null;
|
|
@@ -1060,8 +1013,8 @@ function buildCharSlots(ctx, layer, canvasWidth, canvasHeight) {
|
|
|
1060
1013
|
let lines;
|
|
1061
1014
|
if (layer.wordTimings && layer.wordTimings.length > 0) {
|
|
1062
1015
|
const needsSpace = needsSpaceBetweenWords(layer.localeCode || "en-US", text);
|
|
1063
|
-
const words = text.split(
|
|
1064
|
-
lines =
|
|
1016
|
+
const words = needsSpace ? text.split(/\s+/) : Array.from(text);
|
|
1017
|
+
lines = getCachedEvenLinesWithWords(
|
|
1065
1018
|
ctx,
|
|
1066
1019
|
words,
|
|
1067
1020
|
maxWidth,
|
|
@@ -1071,7 +1024,7 @@ function buildCharSlots(ctx, layer, canvasWidth, canvasHeight) {
|
|
|
1071
1024
|
fontWeight
|
|
1072
1025
|
);
|
|
1073
1026
|
} else {
|
|
1074
|
-
lines =
|
|
1027
|
+
lines = getCachedWrapText(ctx, text, maxWidth, fontSize, fontFamily, fontWeight);
|
|
1075
1028
|
}
|
|
1076
1029
|
const totalHeight = lines.length * fontSize * lineHeight;
|
|
1077
1030
|
const startY = calculateYPosition$3(canvasHeight, totalHeight, layer.fontConfig?.globalPosition);
|
|
@@ -1099,9 +1052,19 @@ function buildCharSlots(ctx, layer, canvasWidth, canvasHeight) {
|
|
|
1099
1052
|
ctx.restore();
|
|
1100
1053
|
return { slots, fontSize, lineHeight };
|
|
1101
1054
|
}
|
|
1102
|
-
function
|
|
1055
|
+
function getCachedCharSlots(ctx, layer, canvasWidth, canvasHeight) {
|
|
1056
|
+
const key = layoutCacheKey(layer, canvasWidth, canvasHeight);
|
|
1057
|
+
const cached = charSlotsCache.get(key);
|
|
1058
|
+
if (cached) {
|
|
1059
|
+
return cached;
|
|
1060
|
+
}
|
|
1103
1061
|
const built = buildCharSlots(ctx, layer, canvasWidth, canvasHeight);
|
|
1104
|
-
if (
|
|
1062
|
+
if (built) {
|
|
1063
|
+
charSlotsCache.set(key, built);
|
|
1064
|
+
}
|
|
1065
|
+
return built;
|
|
1066
|
+
}
|
|
1067
|
+
function drawStaggerEntranceFrame(ctx, layer, built, relativeFrame, fps, preset) {
|
|
1105
1068
|
const { slots, fontSize } = built;
|
|
1106
1069
|
const fontConfig = layer.fontConfig.textStyle;
|
|
1107
1070
|
const fontFamily = fontConfig.fontFamily;
|
|
@@ -1193,6 +1156,37 @@ function renderCaptionStaggerEntrance(ctx, layer, canvasWidth, canvasHeight, rel
|
|
|
1193
1156
|
}
|
|
1194
1157
|
ctx.restore();
|
|
1195
1158
|
}
|
|
1159
|
+
function clearCaptionStaggerCache() {
|
|
1160
|
+
charSlotsCache.clear();
|
|
1161
|
+
for (const bitmap of staggerFinalRasterCache.values()) {
|
|
1162
|
+
bitmap.close();
|
|
1163
|
+
}
|
|
1164
|
+
staggerFinalRasterCache.clear();
|
|
1165
|
+
}
|
|
1166
|
+
function renderCaptionStaggerEntrance(ctx, layer, canvasWidth, canvasHeight, relativeFrame, fps, preset) {
|
|
1167
|
+
const built = getCachedCharSlots(ctx, layer, canvasWidth, canvasHeight);
|
|
1168
|
+
if (!built) return;
|
|
1169
|
+
const endMs = staggerEntranceEndMs(built.slots.length, preset);
|
|
1170
|
+
const endFrame = Math.ceil(endMs / 1e3 * fps);
|
|
1171
|
+
if (relativeFrame >= endFrame) {
|
|
1172
|
+
const rasterKey = staggerRasterKey(layer, canvasWidth, canvasHeight, preset);
|
|
1173
|
+
let bitmap = staggerFinalRasterCache.get(rasterKey);
|
|
1174
|
+
if (!bitmap) {
|
|
1175
|
+
const offscreen = new OffscreenCanvas(canvasWidth, canvasHeight);
|
|
1176
|
+
const offCtx = offscreen.getContext("2d");
|
|
1177
|
+
if (!offCtx) {
|
|
1178
|
+
drawStaggerEntranceFrame(ctx, layer, built, endFrame, fps, preset);
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
drawStaggerEntranceFrame(offCtx, layer, built, endFrame, fps, preset);
|
|
1182
|
+
bitmap = offscreen.transferToImageBitmap();
|
|
1183
|
+
staggerFinalRasterCache.set(rasterKey, bitmap);
|
|
1184
|
+
}
|
|
1185
|
+
ctx.drawImage(bitmap, 0, 0);
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
drawStaggerEntranceFrame(ctx, layer, built, relativeFrame, fps, preset);
|
|
1189
|
+
}
|
|
1196
1190
|
function usToFrame$2(us, fps) {
|
|
1197
1191
|
return Math.floor(us / (1e6 / fps));
|
|
1198
1192
|
}
|
|
@@ -2394,6 +2388,7 @@ class VideoComposer {
|
|
|
2394
2388
|
flush: async () => {
|
|
2395
2389
|
this.filterProcessor.clearCache();
|
|
2396
2390
|
clearTextLayoutCache();
|
|
2391
|
+
clearCaptionStaggerCache();
|
|
2397
2392
|
}
|
|
2398
2393
|
},
|
|
2399
2394
|
{
|
|
@@ -3550,4 +3545,4 @@ const export_worker = null;
|
|
|
3550
3545
|
export {
|
|
3551
3546
|
export_worker as default
|
|
3552
3547
|
};
|
|
3553
|
-
//# sourceMappingURL=export.worker.
|
|
3548
|
+
//# sourceMappingURL=export.worker.p7X_YtxQ.js.map
|