SVG 讲解视频脚本生成(compose 一步式)
本 Skill 指导从一条提示词产出"带配音的 SVG 讲解视频":每页 PPT 内容
(每页一个独立 SVG,可直接渲染为单页 PPT)+ 每页配音文字(可直接送
Edge TTS 合成)+ 配音时间表(每页 开始/结束/时长),并落地为 MP4 成片。
完整流程见 §2。
本 Skill 是单 scene.svg + mp4/merge 工作流的 compose 变体(SKILL.md 的
可选替代在此为主流程):不把所有页面拼进一个 SVG、也不手工回填全局时间线,
而是每页一个独立 SVG,由 compose 依据 manifest 一步完成「逐页配音
(audio 已有文件 或 text 现场合成)+ 渲染 + 字幕 + 混流」;每场景窗口
自动 = 配音实测时长 + pad(音频驱动时间轴),声画同步由 compose 保证。
单 scene.svg + mp4/merge 工作流与 compose 二选一,不混用。
核心做法仍是音频先行、声画同步:先逐页合成配音并实测时长(或直接交
text 让 compose 现场合成),再据此(自动)排定每页场景窗口,保证每一页
画面停留时长与配音严格同步。
本技能为自包含规范。§2 给出从提示词到成片的完整流程(准备输出目录 → 解析 →
脚本 → 逐页 SVG → 逐页配音/字幕 → manifest + compose 一步成片 → 校验 → 清理);§3 特性分级
覆盖元素、属性、动画与配音稿的允许 / 不建议 / 禁止三类;§4 视频结构与配音
时间表、§5 每页 PPT 内容与 §6 配音规范给出脚本所需的预算与要素;§7 场景与
工具链映射列出 manifest 字段与命令参数;§9 校验清单用于交付前逐项检查。
0. 工具链程序路径(svgunity-cli,经 npx 调用)
本技能所有命令统一按
npx svgunity-cli <子命令> …
执行。子命令:compose(多页独立 SVG 一步成片,主流程,§2/§7)、
tts --word-boundaries(逐词时间戳,§4.3)、srt(逐页字幕生成,步骤④.5/§4.3)、
render/mp4 --start/--end(区间渲染,§2 ⑤.5 逐页预览)、merge --loudnorm
(响度归一化,仅单 scene.svg 工作流;compose 无此参数,§7 取舍)与
image-check/audio-check/video-check(均支持 --json,§9 校验)。
--version 应输出 svgunity <版本>(如 svgunity 1.0.1)。
前置依赖:需安装 Node.js(npx 随 Node.js 提供)。CLI 为同步、CPU 密集型调用
(渲染用 manifest/参数里的 threads 并行)。
示例命令中的相对路径(--input / --out 等)按当前工作目录解析,正文
示例默认在生成目录 output/<主题>/(§8)下执行;compose manifest 中的
路径相对 manifest 文件所在目录(§7)。
1. 核心原则
- 脚本先行:先定 讲解目标 → 章节结构 → 每页内容与配音 → 再落为逐页 SVG 场景。
- 音频先行:每场景窗口由配音实测时长驱动(
audio场景 = 文件实测时长,
text场景 = compose 现场合成实测时长);字数只做初始预估(中文 4-5 字/秒,
默认 4.5,§6.1)。 - 每页三要素:场景窗口(由配音实测时长推出,compose 自动计算)、PPT 内容
(标题/要点/图示,可渲染为有效静态帧)、配音文字(TTS 可读)。 - 文件组织:每页一个独立 SVG 文件(
scenes/page01.svg … pageNN.svg),
manifest 按页序引用;compose 一次渲染拼接成片,省去手工拼时间轴与视频拼接。
所有页必须解析为相同缩放尺寸(compose 强制,首场景尺寸为准,§7)。 - 声画同步:compose 自动令每场景窗口 = 配音实测时长 +
pad(音频驱动时间轴);
pad=0时背靠背(窗口 = 配音时长),pad>0时窗口末(含末页尾音处)由 compose
内部生成静音;各场景窗口在成片中按序连续覆盖、互不重叠,整片T= 各场景
窗口之和。无需手工回填<set begin>时间。 - 结构先行:每页静态布局(标题、要点、图示、页码)在无动画时即成立;
入场动画只做增强(淡入/位移),不改变最终构图。
2. 完整流程:从提示词到成片
一条提示词到带配音 MP4 的端到端链路:
提示词 → ⓪ 准备输出目录 → ① 解析 → ② 脚本 script.md(含每页 PPT 内容与配音文字)
→ ③ 逐页 SVG scenes/page01.svg … pageNN.svg → ④ 逐页配音(audio 场景,可选)
→ ④.5 逐页字幕 subtitles/pageN.srt(可选)→ ⑤.5 逐页轻量预览(推荐,全片渲染前)
→ ⑤ video.json + compose 一步成片 → ⑥ 校验交付
→ ⑦ 清理交付(最后,视频生成与校验全部结束后)
| 步骤 | 阶段 | 输入 → 输出 | 执行方 |
|---|---|---|---|
| ⓪ | 准备输出目录 | 用户指定输出目录 / 缺省新建空目录 → 就绪的输出目录 | 生成器 |
| ① | 解析提示词 | "SVG 介绍,5 分钟" → 主题 / 总时长 T / 受众 / 语速基准 | 生成器 |
| ② | 生成脚本 | → script.md(预估时间表 + 逐页 PPT 内容 + 逐页配音文字) |
生成器 |
| ③ | 生成页面 | 每页 PPT 内容 → scenes/page01.svg … pageNN.svg(独立 SVG,同尺寸) |
生成器 |
| ④ | 逐页合成配音(audio 场景用) | 每页配音文字 → narration_01.webm …(含实测时长;text 场景跳过;需要字幕的页建议加 --word-boundaries,§4.3) |
npx svgunity-cli tts(CLI) |
| ④.5 | 生成字幕(默认:逐词时间戳) | 各页配音文字 + 逐词时间戳(步骤④ --word-boundaries)→ subtitles/pageN.srt(按各场景窗口计时,§4.3;无逐词时间戳时回退视觉宽度比例) |
npx svgunity-cli srt(CLI) |
| ⑤.5 | 轻量预览(推荐,全片渲染前) | 逐页 scenes/pageN.svg → preview/pageN/(fps=1 抽查代表帧;不入交付物) |
npx svgunity-cli render(CLI) |
| ⑤ | 一步成片 | video.json(每场景 audio/text/duration + 可选 subtitles)→ <标题>.mp4(渲染+配音/合成+字幕+混流一步完成;音频驱动时间轴) |
npx svgunity-cli compose(CLI) |
| ⑥ | 校验交付 | compose --json 实测时长 + video-check/audio-check → 对照 §9 交付 |
生成器 |
| ⑦ | 清理交付(最后) | 全部校验通过后 → 删除 preview/ 与过程日志(tts/srt/render 等),仅保留交付物(§8);必须在视频生成与校验结束后进行 | 生成器 |
各步骤要求:
⓪ 准备输出目录:视频生成会产出 script.md、scenes/、narration/、subtitles/、video.json、<标题>.mp4 等大量文件(§8),开始前必须先确定输出目录——① 用户已指定目录:检查该目录是否为空(无任何文件与子目录);不为空时必须提醒用户(说明该目录已存在文件,继续生成可能混入旧文件/覆盖同名文件,请用户确认继续、清空或改换新目录)后再继续;为空则直接使用。② 用户未指定目录:新建一个空目录(缺省 output/<主题>/,§8;<主题> 待步骤①解析后确定,先以提示词关键词或"未命名"占位,进入步骤①后如有主题名再修正)。目录就绪后方可进入步骤①。
① 解析提示词:提取 主题、总时长 T(秒)、目标受众、风格要求;缺省时长时按 §4.1 推荐分配;缺省受众时按技术科普处理。
② 生成脚本:按 §5 每页 PPT 内容、§6 配音规范生成 script.md(模板见 §10)。每页配音文字按页号编号;先用 §6.1 字数估算初始页面时长,作为步骤 ③/⑤ 的参考(text 场景最终以 compose 现场合成实测为准,audio 场景可先预合成实测回填)。定稿前先校准实测语速:§6.1 的 4.5 字/秒只是兜底初值,正式批量合成前,先用目标音色/rate 合成 1 页代表性配音(含短句与中长句),按实测「字数 ÷ 实测秒数」得到该音色的实际字/秒,再据此反推每页字数预算,使「Σ 配音预估 + Σ pad + 静音页」落在目标 T 内——一次性写足,避免「合成 → 发现不足 → 扩稿 → 重合成」的往返。
③ 生成页面:每页一个独立 SVG 文件(scenes/page01.svg … pageNN.svg),文件名与页号一一对应;页内动画一律按本地时间轴(0 起,至该页场景窗口结束收尾),无跨页全局时间。所有页 viewBox/宽高一致(compose 要求同尺寸,§7)。写盘后立即批量自检:全部页面(连同配音稿/字幕)落盘后,立即批量校验每个关键文件磁盘字节数 > 0 并读回比对(§9"落盘实践提示")——IDE 写入存在回显成功但落盘为 0 字节/旧内容的竞态,须在进入步骤④ 前发现并原子重写。
④ 逐页合成配音(audio 场景用,可选):每页配音独立合成,文件名与页号一一对应;tts 合成完成即输出该页实测时长(duration X.XXs):
npx svgunity-cli tts --input narration/narration_01.txt --voice zh-CN-XiaoxiaoNeural --out narration/narration_01.webm
# 输出(两行):
# written narration/narration_01.webm (97478 bytes, voice=zh-CN-XiaoxiaoNeural, rate=+0%, pitch=+0Hz, volume=+0%)
# duration 12.90s
(需要字幕的页面建议加 --word-boundaries narration/narration_01.boundaries.json 一并导出逐词时间戳——每词 start_ms/end_ms/duration_ms + text,外加实测 duration_seconds;CLI 的 written 行会额外标注 word-boundaries -> <json 路径>,随后同样输出实测 duration X.XXs;§4.3 字幕时间轴默认以此为准,可免去视觉宽度比例估算。)
(整片拼接稿 narration/narration.txt 可选,仅作整片试听,不参与计时。)
④.5 生成字幕(默认:逐词时间戳;可选):用 npx svgunity-cli srt 由各页配音文字与步骤④导出的逐词时间戳生成逐页字幕文件 subtitles/pageN.srt(SRT,UTF-8;规则见 §4.3),供步骤 ⑤ manifest 的 scenes[].subtitles 引用。字幕按该页自己的场景窗口计时(0 起),compose 自动平移到全片(§7):
npx svgunity-cli srt --input narration/narration_01.txt --boundaries narration/narration_01.boundaries.json --out subtitles/page01.srt
# 输出(一行):
# written subtitles/page01.srt (3 entries)
--input 为该页配音文字、--boundaries 为步骤④ --word-boundaries 导出的逐词时间戳 JSON(每词 start_ms/end_ms/duration_ms + text,外加实测 duration_seconds);--out 缺省时输出到 --input 同路径的 .srt,父目录不存在时自动创建;可选 --max-width-em <em> 覆盖单行宽上限(默认 16em ≈ 1080 宽 15 字,碎片合并上限自动取 +2,§4.3)。时间轴默认用逐词时间戳——每条字幕起止直接取句内首词 start_ms / 末词 end_ms(词级聚合,做法见 §4.3"词级时间轴");仅当 JSON 内没有逐词时间戳时,才回退"视觉宽度比例"(用 JSON 内的实测 duration_seconds 分配,末条强制收于 d_N)估算。
总时长控制:实测总时长 T 偏离目标时——
- 超出目标(如 5 分钟 = 300s 而 T>300):text 场景用 manifest 的 rate(如 "rate": 7 ≈ +7%,总时长近似按 1/1.07 缩放)调语速;audio 场景用 tts --rate 重新合成并重测。--rate 为整数百分比、缩放是近似值,须重测微调(实测 --rate 8 约按 0.926 缩放);反复至 T 落入目标范围。
- 不足目标:扩充配音文字(中文约 4.5 字/秒,§6.1;新增内容集中在少数内容页,避免重测全部页)后重新合成(text 场景重跑 compose 即可);反复至 T 落入目标范围。
计时依据:以 tts/ffmpeg 实测时长为准(默认 WebM/Opus 为 VBR,无法按字节估算);compose --json 输出的 video_duration/audio_duration/duration 为实测值,是校验依据(§9)。播放器/资源管理器显示的时长可能偏差,不作依据。
⑤ 一步成片:写 video.json manifest(§7),每页一场景,audio(已有配音)或 text(compose 现场 TTS 合成)二选一,duration 为无配音页的静音窗口;需要字幕的页加 subtitles。然后:
npx svgunity-cli compose video.json --json
compose 一次完成「逐页配音(audio 或 text)+ 渲染 + 字幕 + 混流」:每场景窗口自动 = 配音实测时长 + pad(音频驱动时间轴),全部页按序渲染到临时文件,再把音轨(各页配音 + 场景间静音)混流成 <标题>.mp4;--json 输出实测 video_duration/audio_duration/duration(§9 校验可用),临时文件运行后自动清理。
⑤.5 轻量预览(推荐,全片渲染前):compose 无分页预览,改为逐页低开销抽查,验证该页内容与动画时长后再进步骤 ⑤。做法:npx svgunity-cli render scenes/pageN.svg --out preview/pageN --fps 1 --end <window_N> --scale 0.5(每 1 秒一帧,第 n 帧对应 t=n 秒、0 起),抽查代表帧与动画结束帧,确认内容、动画、文字正常;具备图像视觉能力时一并检查构图;发现问题立即回改对应 SVG,避免全片渲染后才发现错误。预览目录不入交付物(§8)。注意:--duration 会同时覆盖动画采样时长并驱动渲染窗口帧数,因此预览整页窗口可用 --end <window_N> 或 --duration <window_N>(二选一;--end 只限窗口、不改变动画采样基准,更直观)。
- 首帧核验输出尺寸(防画布尺寸错误):低分辨率预览(--scale 0.5)会掩盖画布尺寸错误——若 SVG 缺 width/height,渲染器按默认 800×600 画布输出,0.5 倍预览为 400×300,恰好等于 1280×720 的 0.5 倍,视觉上"正常"。首次预览应先以 --scale 1 渲染 1 帧(如 npx svgunity-cli render scenes/pageN.svg --out preview/pageN --fps 1 --end 1 --scale 1),核对 render 输出行的 exported … (WxH, …) 尺寸等于预期(如 1280×720),再做低分辨率抽查(§5.8)。
- 帧号换算:--fps 1 时第 n 帧 = 时刻 n 秒(frame_00000.png = t=0)。
- 无图像视觉能力时的可执行替代检查:帧文件非空;同一页静态区间(页内动画结束后)的帧字节一致;不同页代表帧互不相同。另可用 npx svgunity-cli image-check <frame> 逐帧读取统计(--json 输出 width/height、avg_rgba、unique_colors、uniform 等字段;人类可读输出为 size、unique_colors、uniform true/false 行)——空白帧(整帧单一颜色)会显示 uniform true 且 unique_colors 1;--pixel x,y 可抽查关键元素落点(如卡片中心、标题首字处,验证元素确实渲染在预期坐标;每次调用仅接受一个坐标,抽查多个落点需多次调用)。
- 浏览器视觉校验(有浏览器工具时的可选补充):把代表帧 PNG(或页面 SVG 的静态区间画面)在浏览器打开截图抽查——可发现几何审计与字节检查不易覆盖的视觉问题(元素重叠、溢出、文字被裁、配色异常)。SVG 含 SMIL 动画时,静态区间画面与 CLI 渲染结果应一致。
- 页内构图缺陷的几何审计(无图像视觉时必须做):字节级帧检查发现不了页内元素重叠或越界,渲染前须对每个场景 SVG(scenes/pageN.svg)做坐标审计——① 每个重复单元(卡片/面板)内所有元素的坐标范围必须落在该单元边界内,文本基线须在容器内,且文本的 text-anchor 语义与 x 取值必须匹配(middle → x=水平中心,start → x=左缩进);同一模板的多个实例,内部各元素相对所属单元原点的偏移必须逐元素一致——禁止出现"文本已平移而图标未平移"的半平移状态;② 多行布局中,同列相邻行单元坐标必须相差固定行距,禁止复用/复制上一行坐标;③ 同页不应交叠的相邻单元(如并列卡片)包围盒不得相交;④ 审计结论随交付说明记录。
⑥ 校验交付:对照 §9 校验清单逐项检查(总时长、配音时间表、PPT 内容、配音稿,以及各 SVG 文件的 XML / SVG·SMIL 规范与禁用元素),通过后交付 <标题>.mp4 与 script.md。全部通过后再执行步骤 ⑦ 清理交付(最后)。
> 步骤 ③-⑥ 的命令与 manifest 字段见 §7;步骤 ⑦ 清理无命令、细则见 §8。
3. 特性分级(速查)
只使用规范定义明确、且主流渲染环境一致实现的特性;任一主流环境不支持或行为
不一致的特性,一律归入"不建议使用"或"禁止使用"。
3.1 明确允许使用
元素:
| 类别 | 元素 |
|---|---|
| 渲染 | svg / g / rect / circle / ellipse / line / polyline / polygon / path / text / tspan / textPath / use / image / switch |
| 定义类(不直接渲染) | defs / clipPath / mask / pattern / filter(17 原语)/ linearGradient / radialGradient / marker / stop / style / title / desc |
| SMIL 动画 | <animate> / <set> / <animateTransform>(translate/scale/rotate/skewX/skewY)/ <animateMotion>(path / mpath / keyPoints / rotate)/ <mpath> |
时序属性:begin(多值分号列表)、end、dur、repeatCount(含小数/indefinite)、repeatDur、fill(remove/freeze)、min/max、restart、calcMode(linear/discrete/spline/paced)、keyTimes、keySplines、keyPoints、additive、accumulate。
触发:syncbase(id.begin / id.end / id.repeat(n) ± 偏移)、事件(rect.click / focusin / mouseover…)、indefinite。
动画类型:
- 数值属性:x/y/cx/cy/r/rx/ry/width/height/font-size/letter-spacing/opacity/stroke-width/stroke-dashoffset…
- 颜色:fill/stroke/stop-color/flood-color/lighting-color…
- 变换:transform(translate/scale/rotate/skewX/skewY)
- 路径 d(命令结构一致时平滑变形)、points(点数一致)、字符串(visibility/display/fill-rule/stroke-dasharray/viewBox)
- 渐变 stop(offset/stop-color/stop-opacity)、滤镜参数(stdDeviation/dx/dy/radius/scale/baseFrequency/flood-*/kernelMatrix/surfaceScale…)
CSS 辅助:@keyframes + animation、@media (prefers-reduced-motion)、transition、var()、calc()、现代颜色(hex/rgb/hsl/oklch/color-mix)。
文本:text/tspan/textPath、text-anchor、dominant-baseline、letter-spacing/word-spacing、textLength+lengthAdjust;一律单行(§3.3)。
视频场景加分项:
| 特性 | 用途 |
|---|---|
pathLength="1" + stroke-dasharray="1" + stroke-dashoffset 1→0 |
折线/弧段/环形图"绘制式生长",免算弧长 |
<clipPath> 内元素动画 |
数字/文本"遮罩揭示"增长效果 |
<animate attributeName="height" / "y"> |
柱状图自底生长 |
页内 <set>/<animate>(本地时间轴,0 起) |
单页内入场/生长/切换动画;本页窗口结束处收尾(fill="freeze") |
页内 <animate attributeName="opacity"> |
页内容淡入(转场) |
compose(manifest 音频驱动时间轴,§7) |
每场景窗口自动 = 配音实测时长 + pad,一步成片;无需手工回填 <set begin> 时间 |
> 注:compose 模式下每页是独立 SVG,动画全部按本地时间轴(0 起)编排,没有单 scene.svg 工作流的跨页全局时间线,因此不需要"页面切换双 set"写法(§4.2);页内若需局部显隐,仍用 <set> + fill="freeze"——仅"由隐转显并保持"时单个 <set> 即可,按区间"先显后隐"时用双 set(§4.2)。
3.2 不建议使用(行为有差异,注意约束)
| 特性 | 约束 |
|---|---|
路径 d 变形 |
起止路径命令数/类型必须一致才有平滑插值;不一致时回退 discrete → 保持命令结构一致 |
points 动画 |
要求起止点数一致 → 保持一致点数 |
viewBox 动画 |
支持但边界微妙 → 尽量避免 |
| 滤镜 | 默认 filter 区域可能裁切 → 显式 x="-50%" y="-50%" width="200%" height="200%" |
| clip-path / mask 的 SMIL 字符串动画 | 渲染器已支持;跨环境一致仍建议静态 clip-path + 内部元素动画代替 |
实心饼图扇区 d 变形 |
弧端点线性插值易走样 → 用环形图 stroke 弧段或静态扇区淡入 |
单个 <set> 带 end 属性做切换(无 dur / 无 fill="freeze") |
此渲染器只在 begin/end 实例时刻生效、不保留中间状态(实测 t=0 后即回退隐藏)→ 页内显隐用双 set + fill="freeze"(§4.2) |
| 配音长句(> 30 字) | TTS 换气吃力 → 按标点拆句(§6.2) |
| 缩写/术语不注读法 | 首次出现给出全称;缩写注明读法("SVG 读作 S-V-G")(§6.2) |
3.3 禁止使用
| 特性 | 原因 |
|---|---|
<par> / <seq> / <excl> 时间容器 + endsync + fillDefault |
主流浏览器不识别 |
<animateColor> |
已被主流浏览器移除 → 用 <animate attributeName="fill\|stroke\|…"> |
wallclock(…) |
主流支持存疑 |
begin="accesskey(s)" |
历史实测无效 |
<script> / JS |
渲染环境不执行脚本 |
<foreignObject> 内部 HTML |
只渲染 SVG 子元素 |
| SVG2 文本换行(inline-size/white-space 多行/text-align/word-break/overflow-wrap) | 主流浏览器均未实现 → 文本一律单行 |
| 3D CSS 变换(rotateX/translate3d/perspective…) | 主流环境行为不一致 |
speed / autoReverse / accelerate / decelerate |
不在标准动画属性表 |
dur="media" |
行为另类 |
| 外部资源(网络图片 / webfont / @import / 跨文档 use) | 单文件内闭环 |
| 文本内容(字符)动画 | 文本节点内容不可被 SMIL/CSS 动画 → 用遮罩揭示/计量条代替 |
配音稿中的尖括号标签(<path>)、符号(& / < / > / → / ↑)、表情符号 |
TTS 无法朗读 → 一律文字化(§6.3) |
| 页面内图标/装饰文本中的 emoji(🧠🔗📈 等) | 依赖系统 emoji 字体,跨机不保证渲染(可能显示为方框)→ 用 SVG 基础图形组合代替 |
4. 视频结构与配音时间表
4.1 页面结构
讲解视频典型结构:封面 → 目录 → N 个内容页 → 总结。
| 角色 | 页数 | 单页配音参考 |
|---|---|---|
| 封面 | 1 | 10-20s |
| 目录 | 1 | 15-25s |
| 内容页 | 4-8 | 35-45s |
| 总结 | 1 | 20-30s |
- 5 分钟(300s)推荐分配:封面 15s + 目录 20s + 内容页 6×40s + 总结 25s = 300s(配音预估参考,实测为准)。
- 每页一个独立 SVG 文件(
scenes/pageN.svg),按页序在 manifest 中引用。 - 每页场景窗口 = 配音实测时长 + 转场余量(compose 自动计算,§7);初始预估用 "字数 ÷ 4.5"(§6.1),四舍五入到 5s 粒度。
4.2 配音时间表(声画同步依据)
compose 的每场景窗口由配音实测时长驱动(音频驱动时间轴),由工具自动计算:
window_N = d_N + pad (d_N 为配音实测时长;duration 场景 d_N = 指定值)
整片 T = Σ window_N
d_N:audio场景取配音文件实测时长(tts直接输出;compose 内部会再次 probe 该文件,数值应一致);text场景取 compose 现场合成音频的实测时长(事先无法精确得知,用字数估算,最终以 compose--json为准);duration场景取 manifest 指定值。pad为转场余量(建议 0.3-0.5s;取 0 表示无余量、不插静音,音频背靠背拼接),manifest 全局统一;compose 在每个场景窗口后(含末页尾音处)内部生成静音,无需预先准备静音文件。- 各场景窗口在成片中按序连续覆盖、互不重叠,整片
T= 各场景窗口之和(帧对齐后画面可能多至 1/fps 秒,音轨会补齐到画面窗口,声画仍严格对齐,§7)。 - 无需为页面切换回填
<set begin>时间(每页独立 SVG,compose 按序渲染);页内动画一律本地时间轴(0 起)。页内按区间"先显后隐"用两个<set>+fill="freeze"(单个<set>带end在此渲染器不保留状态,§3.2);仅"由隐转显并保持"时单个<set>+fill="freeze"即可:
svg
<g id="detail" visibility="hidden">
<set attributeName="visibility" to="visible" begin="1.0s" fill="freeze"/>
<set attributeName="visibility" to="hidden" begin="5.0s" fill="freeze"/>
<!-- 本页内局部内容 -->
</g>
脚本文档顶部仍放配音时间表(步骤④实测后回填,供校验与剪辑对齐;下表示例 pad = 0.5s,第 03 页为 text 场景、以字数预估占位,第 04 页为 duration 静音页):
| 页 | 场景类型 | 配音/时长来源 | 场景窗口(compose 自动 = 时长 + pad) |
|---|---|---|---|
| 01 | audio | narration_01.webm 实测 12.90s | [0.00, 13.40) |
| 02 | audio | narration_02.webm 实测 18.40s | [13.40, 32.30) |
| 03 | text | 预估 40.10s(字数 180 × 4.5) | [32.30, ~72.90) |
| 04 | duration | 3.00s | [72.90, 76.40) |
| … | … | … | … |
| 09 | audio | narration_09.webm 实测 24.20s | [272.60, 297.30) |
规则:
- 表中数值统一保留两位小数;audio 场景的时长须与 tts 实测一致(回填),text 场景以 compose --json 实测为最终依据(§9),duration 场景与 manifest 指定值一致。
- 整片 T = 末场景窗口结束;成片时长用 compose --json 的 duration/video_duration/audio_duration 与 video-check/audio-check 实测核对(§9)。
4.3 字幕文件(逐页 subtitles/pageN.srt)
步骤 ④.5 用 npx svgunity-cli srt 由各页配音文字与逐词时间戳生成逐页 subtitles/pageN.srt(SRT,UTF-8;也接受 ASS),步骤 ⑤ 由 manifest 的 scenes[].subtitles 引用,compose 烧录进视频并自动平移到该场景在全片中的位置(按帧对齐累计起点,§7——字幕按该页自己的窗口计时,从 00:00:00,000 起,无需手工加全片偏移)。
时间轴(默认:逐词时间戳驱动):字幕时间轴默认以步骤④导出的逐词时间戳为准(tts --word-boundaries,见"词级时间轴"小节)——每条字幕起止直接取句内首词 start_ms 与末词 end_ms,不再按视觉宽度比例估算;仅当 JSON 内没有逐词时间戳时,才回退到"视觉宽度比例"分配(见下方条目"时间轴"字段)。
- 条目(默认:按句拆分,单行依次出现):讲解视频字幕默认逐句单行显示,随配音逐条出现/消失;每页内序号 1..N 连续(空配音页跳过、序号仍连续)。分组(只决定"哪些字同一条",与时间轴无关):① 先按
。;!?断句,以视觉宽度而非字符数判断(中文与全角标点计 1em,ASCII 与空格半宽 0.5em;1em ≈ 1 个中文字符宽度 ≈ 字幕字号 px,如 64px 字号下 16em ≈ 1024px(约 1080 宽的 95%));② 句宽 ≤max_width_em(默认 16em ≈ 1080 宽、64px 字号约 15 字;可用--max-width-em覆盖,非 1080 宽画面须显式指定,如 1920 宽建议 25)直接成条;句宽超限再按,拆子句并贪心合并相邻子句(合并后仍 ≤max_width_em);③ 碎片(宽 < 10em)优先并入相邻条消除——并入后 ≤max_width_em即合并;若相邻合并必超宽则允许保留原碎片条并在交付说明中记录。时间轴:默认按"词级时间轴"取各条首/末词时间戳;无逐词时间戳时回退视觉宽度比例(条时长 = 条视觉宽度 ÷ 页总视觉宽度 × d_N,逐条累加且末条结束强制等于d_N,不越窗)。校验:各条文本按序拼接必须与配音稿逐字一致;每条宽度 ≤max_width_em + 2(默认 18em;16em 为目标行宽、18em 为碎片合并容忍上限,64px 字号下 18em ≈ 1152px,可能略超 1080 宽);尽量无 < 10em 碎片条。 - 词级时间轴(默认;
tts --word-boundaries+npx svgunity-cli srt):步骤④合成配音时用--word-boundaries narration/pageN.boundaries.json导出逐词时间戳(每词start_ms/end_ms/duration_ms+text,外加实测duration_seconds),npx svgunity-cli srt生成 SRT 时按词级聚合:
1. 条目→词段映射:把各条目文本与词 token 序列按序等值匹配——双侧对称清洗(条目与词 token 同步去空白与全部标点,仅保留字母数字),从页首词开始按条目顺序贪心消费 token,累计到与条目清洗文本等值为止;token 切分与条目边界不一致(如"矢量"被切成"矢""量"、标点不独立成 token)不影响匹配,累计拼接后仍与条目逐字等值。
2. 起止时间:条start_ms= 首词start_ms,end_ms= 末词end_ms;末条不强制延伸至d_N——语音收尾即消失,尾部静音由场景pad承载;若希望字幕停留到翻页,改用[0, window_N)(场景窗口)。
3. 一致性:同页词时间戳单调递增,相邻条目首尾相接、不重叠不倒退(必要时钳制start ≥ 前一条 end)。
4. 实现:步骤 1-3 全部由npx svgunity-cli srt完成(含"毫秒直接截断"的时间戳换算,见"时间戳"),生成端无需再写脚本,只需按 §2 ④.5 的命令逐页产出subtitles/pageN.srt。 - 条目(可选:每页一条,多行合并显示):仅当要求字幕整页呈现完整文稿时使用——文本为该页配音文字,多句合并为一条,句间用换行分隔;时间窗取
[0, window_N)整页窗口(避免中途消失;整页一条不依赖逐词时间戳);其余规则同默认条目(页内序号连续、逐字一致)。生成端在未收到明确"整页文稿"要求时,一律按默认的按句拆分、单行依次出现生成。 - 时间窗:默认各条落在配音实际发声区间(词级模式:首词
start_ms起、末词end_ms止;回退模式:[0, d_N)),字幕随配音消失;若希望字幕停留到翻页,改用[0, window_N)(场景窗口)。 - 时间戳:
HH:MM:SS,mmm,由毫秒直接截断换算(ms→ 时:分:秒,毫秒,如13310ms →00:00:13,310、8950ms →00:00:08,950),不四舍五入——四舍五入会使整秒边界漂移(+1s 假象);由npx svgunity-cli srt输出,生成端无需手写换算。 - 时序一致性:字幕按各页窗口计时、由 compose 按帧对齐累计起点平移烧录,与配音严格同步(§7);逐词时间戳模式下由词级聚合保证句级对齐(首/末词时间戳与音频实测同源)。
text场景的字幕时机:compose 现场合成时长与预估可能略有出入,要逐字精确同步字幕时——先用tts以相同音色/rate预合成一次(含--word-boundaries)测时长并生成 SRT(此时可直接把文件交给audio,最严格);或 manifest 仍写text、接受预合成与现场合成间的微小时长差(同音色同 rate 下通常 ≤ 数十 ms 量级)。- 中文:manifest 全局给
subtitle_font(如msyh.ttc,相对 manifest 目录),否则依赖系统字体查找、跨机不保证一致。
示例(沿用 §4.2 第 01 页,pad=0.5;默认按句拆分、单行依次出现;下表演示回退(视觉宽度比例)方法,条时长 16.5/12/13em → 5.13/3.73/4.04s;有逐词时间戳时各条起止直接取句内首/末词 start_ms/end_ms,如首条可写 00:00:00,096 --> 00:00:02,769)——该页字幕从 00:00:00,000 起,compose 会把它平移到全片第 0 秒之后(此页为首页):
1
00:00:00,000 --> 00:00:05,130
大家好,欢迎来到本期的 SVG 讲解。
2
00:00:05,130 --> 00:00:08,860
今天我们要介绍的是 SVG。
3
00:00:08,860 --> 00:00:12,900
准备好了吗?让我们开始吧。
5. 每页 PPT 内容规范
每页 PPT 内容需声明以下字段(版式与动画仅使用 §3.1 允许特性;每页渲染为独立 SVG 文件 scenes/pageN.svg):
- 标题:页面主标题(36-44px 粗体),一句点题。
- 要点:≤ 3 条,每条 ≤ 25 字(中文),单行;是页面构图骨架。(目录页例外:可列出全部章节卡片,不受 ≤3 条限制。)
- 图示:一句话描述页面视觉(坐标系网格、对比图、图标组合、动画示意等),可渲染为 SVG 基础图形组合(禁止外部图片)。
- 动画建议(可选,避免全片只做淡入):与要点对应的入场/生长动画——网络/结构图用节点连线"绘制式生长"(
pathLength="1"+stroke-dashoffset1→0)、统计图用柱状自底生长(animate height/y)、数字/文本用遮罩揭示(clipPath内元素动画)、过程流用沿路径运动(animateMotion)、卡片/要点用错峰淡入;仅使用 §3.1 允许特性,且按本地时间轴(0 起)编排,本页窗口结束处收尾。 - 页码:静态文本 "NN / TT"。
- 重复版式单元(多行卡片/并列面板等):第二行及之后的单元必须相对第一行整体偏移固定行距(如行距 200 则第二行所有 y = 第一行对应 y + 200),禁止复用/复制上一行坐标;建议将行距、单元尺寸等布局参数写入 script.md(或 SVG 注释),供 §2 ⑤.5 几何审计核对。单元内部元素建议用
<g transform="translate(单元原点)">+ 局部坐标,或从注释中的布局公式(如图标中心 = 卡片原点 + 固定偏移)推导生成,禁止直接复制粘贴绝对坐标——确需复制时须整块平移(内部所有元素一并平移),防止"文字平移而图标未平移"。 - 文本水平对齐:
text-anchor="middle"(居中)时,x 必须取元素的水平中心(= 容器左缘 + 宽度/2);text-anchor="start"(左对齐,默认)时,x 取左缩进(= 容器左缘 + 内边距)。禁止middle锚点配左缩进坐标、或start锚点配中心坐标的错配——这是"锚点未随坐标约定同步"的典型半平移错误(如居中文字误写成x=容器左缘+50而非容器左缘+宽度/2)。 - 尺寸一致:每页根
<svg>必须显式写width/height属性(建议统一1280×720),且所有页的 viewBox/宽高一致(compose 要求各场景缩放后同尺寸,否则报错,§7)。禁止省略宽高属性——渲染器在无显式宽高时按默认 800×600 画布渲染(独立渲染无包含块,parse_svg_with_viewport默认 viewport 800×600),1280×720 内容会被缩放并出现黑边;且各页同为错误尺寸时 compose 不报错,分辨率错误只能在预览/校验阶段拦截(§2 ⑤.5、§9.2)。
6. 配音文字规范(TTS 友好)
配音文字是核心交付物之一,需逐字可被 TTS 朗读;每页独立成稿,供步骤④逐页合成或步骤⑤ text 现场合成。
6.1 字数预算(初始预估)
- 中文 4-5 字/秒(默认 4.5);英文单词按 ~2.5-3 词/秒折算;中英混排按"中文字数 + 英文单词数"合计。
- 每页配音字数 ≈ 目标页面时长 × 4.5(兜底初值)。此值仅用于步骤②的初始预估;正式定稿前按 §2 步骤② 先合成 1 页校准实测字/秒,再据此反推各页字数预算;实际场景窗口以配音实测时长为准(
audio场景 tts 实测;text场景 compose 现场合成实测)。 - 超过预算即删减,不要压缩语速硬塞。
6.2 断句与结构
- 句子 ≤ 25-30 字,用
。,;断句;TTS 依赖标点换气,长句必须拆分(§3.2)。 - 数字与年份写完整("2024 年"而非"24");百分比写"百分之三十"而非"30%"(或写
30%并注明读法)。 - 英文缩写大写并给读法:"SVG 读作 S-V-G";首次出现给出全称(如"SVG,全称可缩放矢量图形")(§3.2)。
6.3 禁写内容(TTS 无法朗读)
| 禁写 | 替代 |
|---|---|
尖括号标签 <path> |
"path 标签" |
符号 & / < / > / → / ↑ |
"和" / "小于" / "大于" / "到" / 文字化描述 |
| 表情符号 | 不写 |
| 未转义特殊字符 | 一律文字化 |
6.4 口播风格
- 口语化、短句;每段以承上启下句开头("接下来""那么""最后")。
- 技术名词保持规范:SVG、SMIL、CSS、JavaScript、path、fill、stroke 等按原样读,脚本中标注发音提示(可选)。
7. 场景与工具链映射
场景 = 每页一个独立 SVG 文件(scenes/page01.svg … pageNN.svg,按 §3-§5 规范生成),
compose 按 manifest 页序一次渲染成片。manifest 为 JSON 文件,所有路径相对
manifest 所在目录解析;每页一场景,audio(已有配音)或 text(compose
现场 TTS 合成)二选一,duration 为无配音页的静音窗口:
{
"out": "<标题>.mp4", "fps": 30, "pad": 0.4, "threads": 16,
"voice": "zh-CN-XiaoxiaoNeural", "subtitle_font": "msyh.ttc",
"subtitle_font_size": 40, "subtitle_bold": false,
"scenes": [
{ "svg": "scenes/page01.svg", "audio": "narration/narration_01.webm" },
{ "svg": "scenes/page02.svg", "text": "第一页的旁白。", "subtitles": "subtitles/page02.srt" },
{ "svg": "scenes/page03.svg", "duration": 3.0 }
]
}
npx svgunity-cli compose video.json --json——场景窗口自动 = 配音实测时长 + pad
(音频驱动时间轴),渲染 / 配音(audio 或 text)/ 字幕烧录 / 混流一步完成;
--json 报告实测 video_duration/audio_duration/duration(§9 校验可用)。
CLI 的 compose --help 摘要只列出 scenes[{svg, audio?, duration?}],但 manifest
解析为完整 compose 引擎,下表的 text/subtitles/voice/rate/pitch/volume/
tts_format/subtitle_font/subtitle_font_size/subtitle_bold/subtitle_margin_v/
subtitle_alignment/subtitle_outline/subtitle_font_name 字段全部支持。
manifest 字段:
| 字段 | 默认 | 说明 |
|---|---|---|
out |
<标题>.mp4 |
输出 MP4 路径(相对 manifest 目录;可用 --out 覆盖);缺省文件名 = 视频标题 + 扩展名(标题取步骤①解析的主题名,§8 成片命名) |
fps |
30 |
帧率 |
scale |
1.0 |
分辨率倍率;所有场景缩放后必须同尺寸(不一致报错) |
background |
#000000 |
透明区合成底色 |
pad |
0 |
每场景窗口 = 配音时长 + pad;每段音频补齐到帧对齐画面窗口 |
video_codec |
libx264 |
视频编码(不可用时回退 mpeg4) |
crf |
10 |
质量(x264 系为 CRF;取值范围 0-51) |
threads |
逻辑核数 | 并行渲染线程(1-512;内存随线程数线性增长,大分辨率长片按需调低) |
voice |
zh-CN-XiaoxiaoNeural |
text 场景的 Edge TTS 音色 |
rate/pitch/volume |
0 |
text 场景语速(%)/音高(Hz)/音量(%)(语速用于总时长控制,§2) |
tts_format |
webm-24khz-16bit-mono-opus |
text 场景 TTS 输出格式 |
subtitle_font |
系统字体 | 字幕烧录字体(中文建议给,如 msyh.ttc,相对 manifest 目录) |
subtitle_font_size |
按短边 | 字幕字号 px(默认按短边 min(宽,高)/15-8:1080 级约 64px、720 级约 40px)。可显式覆盖,但覆盖字号时须同步调整 --max-width-em(§4.3)——字号与行宽配套(64px 配 16em ≈ 1080 宽约 15 字),只改其一会导致字幕行宽与画面不匹配 |
subtitle_bold |
true |
字幕加粗(true/false;默认加粗) |
subtitle_margin_v |
10 |
字幕底边距 px(ASS MarginV) |
subtitle_alignment |
2 |
字幕对齐 1..9(ASS Alignment;默认 2 = 底部居中) |
subtitle_outline |
1.5 |
字幕描边 px(ASS Outline) |
subtitle_font_name |
跟随 subtitle_font |
字幕字体名(ASS Fontname;缺省取 subtitle_font 文件 stem,避免写死 Arial) |
scenes[].svg |
— | 场景 SVG(相对 manifest) |
scenes[].audio |
— | 已有配音;窗口 = 文件实测时长 + pad(与 text 互斥) |
scenes[].text |
— | 现场合成配音;窗口 = 合成实测时长 + pad(与 audio 互斥) |
scenes[].duration |
SVG 自身动画时长 | 无配音静音页的窗口(audio/text 皆无时生效) |
scenes[].subtitles |
— | 逐页字幕(.srt/.ass,相对 manifest),按该页窗口计时,compose 自动平移到全片 |
字幕字体文件:subtitle_font 为相对 manifest 目录的字体路径,该文件必须真实
存在——目录列表可能滞后于磁盘实际状态,复制后务必 Test-Path 并核对字节数
(> 0)。中文常用微软雅黑 msyh.ttc,Windows 从系统字体目录复制(PowerShell
用正斜杠同样有效):
Copy-Item 'C:/Windows/Fonts/msyh.ttc' '<输出目录>/msyh.ttc'
macOS / Linux 用系统对应中文字体(如 Noto Sans CJK、苹方),并相应修改字段值。
命令执行顺序(与 §2 步骤对应):
| 步骤 | 命令 | 说明 |
|---|---|---|
| ④(可选) | npx svgunity-cli tts --input narration/narration_N.txt --voice zh-CN-XiaoxiaoNeural --out narration/narration_N.webm |
逐页预合成配音(audio 场景用;N = 页号);输出含实测时长 d_N;需要字幕的页追加 --word-boundaries narration/narration_N.boundaries.json(§4.3)。text 场景跳过本步 |
| ④.5(可选) | npx svgunity-cli srt --input narration/narration_N.txt --boundaries narration/narration_N.boundaries.json --out subtitles/pageN.srt [--max-width-em <em>] |
逐页生成字幕(默认逐词时间戳,§4.3;JSON 内无逐词时间戳时回退视觉宽度比例;--max-width-em 覆盖单行宽上限,默认 16em)。text 场景可用同一 tts 命令先预合成一次导出时间戳(§4.3"text 场景的字幕时机") |
| ⑤.5 | npx svgunity-cli render scenes/pageN.svg --out preview/pageN --fps 1 --end <window_N> --scale 0.5 |
轻量预览:逐页抽查代表帧(§2 ⑤.5;渲染窗口可用 --end <window_N> 或 --duration <window_N>,--end 只限窗口、更直观;首次先以 --scale 1 渲染 1 帧核验输出尺寸,再低分辨率抽查);预览目录不入交付物(§8) |
| ⑤ | npx svgunity-cli compose video.json --json |
一步成片:渲染 + 配音(audio/text)+ 字幕烧录 + 混流 → <标题>.mp4;--json 输出实测时长 |
| ⑥ | npx svgunity-cli video-check <标题>.mp4 / npx svgunity-cli audio-check <标题>.mp4(可加 --json) |
校验:视频/音轨时长、非静音(§9) |
| ⑦ | — | 清理交付(§8):删除 preview/ 与过程日志,仅保留交付物;视频生成与校验全部结束后执行 |
compose 行为与约束:
- 时间轴:每场景窗口 = 配音实测时长 +
pad(audio场景由 compose 内部 probe 该文件实测;text场景现场合成后实测;duration场景取指定值,缺省用 SVG 自身动画时长)。每场景帧数 =ceil(window × fps),画面实际长度 = 帧数/fps(可能比窗口多至 1/fps 秒);音轨每段补齐到帧对齐的画面窗口、字幕按帧对齐累计起点平移,故声画严格对齐——--json的video_duration/audio_duration为实测值(可能略大于计划duration,属正常)。 - 尺寸:所有场景缩放后必须相同帧尺寸(writer 以首场景尺寸打开);不同会报错
scene N scaled size … differs from the first scene …。因此各页 viewBox/宽高必须一致,且根<svg>须显式写width/height(§5.8)。 - 混流:画面渲染到临时文件,再混流音轨(视频流
copy不重编码、音轨必然重编码aac);text现场合成音频与合并后的字幕文件均为临时产物,运行后自动清理。 - 响度:compose 无
merge --loudnorm(§0);多页配音响度差异大时,text场景用 manifestvolume统一,audio场景在预合成时用tts --volume统一,或在交付说明中记录差异;仍不满意可改用单scene.svg+merge --loudnorm工作流(SKILL.md)。 - 性能参考:渲染吞吐实测约 40-50 帧/s @1280×720、30fps、16 线程(300s 全片约 3-4 分钟);分辨率/复杂度越高越慢。全片渲染前建议先做 §2 步骤⑤.5 逐页预览。
- 单
scene.svg+mp4/merge工作流与compose二选一,不混用。
8. 输出文件
开始前先按 §2 步骤⓪ 准备输出目录:用户未指定目录时新建一个空目录(缺省
output/<主题>/);用户指定目录时须确认该目录为空——不为空时先提醒用户
(可能混入旧文件/覆盖同名文件,需确认继续、清空或改换新目录),确认后再进入
步骤①。生成目录下产出:
output/<主题>/
├── script.md # 完整脚本(必出):配音时间表 + 逐页 PPT 内容 + 逐页配音文字
├── scenes/ # 场景 SVG(步骤③产出:每页一个独立文件)
│ ├── page01.svg …
│ └── pageNN.svg
├── narration/ # 配音(步骤④产出,可选:仅 audio 场景)
│ ├── narration_01.txt / narration_01.webm / narration_01.boundaries.json …
│ └── narration.txt # 整片拼接稿(可选,试听用)
├── subtitles/ # 逐页字幕(步骤④.5产出,可选)
│ └── page01.srt …
├── video.json # compose manifest(步骤⑤产出)
├── msyh.ttc # 字幕字体(可选,使用 subtitle_font 时随交付保留,§7)
├── preview/ # 轻量预览(步骤⑤.5产出,不入交付物;步骤⑦ 清理时删除)
└── <标题>.mp4 # 成片(步骤⑤ compose 一步产出)
script.md 结构模板见 §10。
命名统一:场景/字幕/配音文件全程采用两位零填充页号(page01.svg、page01.srt、narration_01.txt,与 §7 manifest 示例一致);正文其余处 pageN.svg/pageN.srt 均为占位符,实际按本规则命名。批量命令与 manifest 引用必须与实际文件名逐字一致——page1 与 page01 混用会导致批量命令/引用报 "failed to read … os error 2"(文件找不到)。
成片命名:最终视频文件名为标题 + 扩展名(如 <标题>.mp4,扩展名由 out 字段的路径决定),标题取步骤①解析的视频标题(主题名),与 script.md 中的 # 《<主题>》 一致;文件名不得含 \ / : * ? " < > | 等非法文件名字符(必要时替换为下划线)。manifest 的 out 字段缺省即 <标题>.mp4(§7),校验命令与交付说明均按此文件名引用。
步骤 ⑦ 清理交付(最后,视频生成与校验全部结束后):全部校验通过后,删除
preview/ 与过程日志(如 tts/srt/render 命令的日志文件),仅保留交付物:
script.md、scenes/、narration/、subtitles/、video.json、msyh.ttc(如有)、<标题>.mp4。
清理必须放在所有生成与校验完成之后——中途保留 preview/ 便于逐页返工,日志
便于排查;确认成片合格后再统一清理。
9. 校验清单(生成后逐项检查)
对脚本、字幕与各 SVG 文件逐项检查。SVG 文件只做三项规范符合性检查(XML 规范、
SVG/SMIL 规范、禁用特性),不核查构图、视觉效果或单页画面停留时长与配音的
对应。无图像视觉能力时,生成端须按 §2 ⑤.5 的几何审计自查页内坐标(重叠/越界),
并将审计结论随交付说明记录。
- 总时长:
T与要求一致(如 5 分钟 = 300s);各场景窗口连续覆盖[0,T)、互不重叠、末场景到T。成片用 compose--json与内置检查命令实测核对——duration(计划 Σ窗口)与video_duration/audio_duration(实测)相互吻合,视频时长 ≈T,音轨时长 ≈Σ d_N + N × pad(N 为页数,pad>0时末页含尾音,pad=0时 ≈Σ d_N):
npx svgunity-cli compose video.json --json # 报告 duration / video_duration / audio_duration / scenes / frames npx svgunity-cli video-check <标题>.mp4 # duration(容器时长,缺失时回退解码帧 PTS 跨度)≈ T;同时输出 size/frames/black_frames;size 必须等于预期分辨率(如 1280×720) npx svgunity-cli audio-check <标题>.mp4 # duration(按解码样本数计算)≈ 音轨期望值;peak/rms 确认非静音 (两命令均支持 `--json`:JSON 输出到 stdout、警告到 stderr,适合自动化校验管道) - 多页覆盖与分辨率:每页独立 SVG(
scenes/pageNN.svg)按页序在 manifest 中引用;每页窗口 = 配音实测时长 +pad(compose 自动);所有页缩放后同尺寸(不一致 compose 直接报错)——但"同尺寸"不足以保证尺寸正确(各页同为错误尺寸时 compose 不报错),成片分辨率必须等于 §5.8 的预期值,用video-check的size核对(如 1280×720)。每个文件非空(磁盘字节数 > 0)且能被 CLI 成功加载解析(render/compose不报 "XML parse error: the document does not have a root node" 之类错误)。 - 配音时间表:每页 开始时间 / 结束时间 / 时长(保留两位小数)——
audio场景与tts输出的配音文件实测时长一致(compose 内部 probe 结果也应一致),text场景以 compose--json实测为最终依据,duration场景与 manifest 指定值一致;pad应用正确(pad>0时每场景窗口含尾音、pad=0时背靠背)。 - PPT 内容:每页标题 + ≤3 要点(每条 ≤25 字,目录页除外,§5)+ 图示 + 页码;要点为单行。
- 配音稿:句子 ≤ 30 字且带标点断句;无尖括号、
&、箭头等禁写字符(§3.3);数字/缩写已完整书写(§6.2)。 - SVG 符合 XML 规范:标签闭合、属性引号闭合;同一标签内属性名不重复(重复属性会直接导致渲染失败,典型如
<animateTransform>上误写fill="40 0" fill="freeze"——既重复又污染了from/to值);id唯一;特殊字符已转义。 - SVG 符合 SVG/SMIL 规范:仅使用 §3.1 允许特性(元素、属性、时序与动画语法),属性取值合法;页内动画按本地时间轴(0 起)编排。
- SVG 未使用禁用特性:无 §3.3 禁止项(尤其
<seq>/<par>时间容器、<animateColor>、脚本、多行文本、文本字符动画、外部资源)。 - 字幕(逐页 subtitles/pageN.srt):默认按句拆分、单行依次出现(§4.3)——时间轴默认取逐词时间戳(步骤④
--word-boundaries,§4.3"词级时间轴",由npx svgunity-cli srt生成):各条起止与句内首/末词start_ms/end_ms一致、页内按序单调且不重叠不倒退;无逐词时间戳时才用视觉宽度比例(各条落在该页配音发声区间[0, d_N)内、页内逐条累加、末条强制收于d_N)。两种模式均校验:各条文本按序拼接与配音稿逐字一致;每条宽度 ≤max_width_em + 2(默认 18em)、尽量无 < 10em 碎片(相邻可合并,合并超宽可保留并记录);无 §6.3 禁写字符;页内序号 1..N 连续;毫秒转时间戳直接截断、无整秒漂移。成片抽查若干条确认字幕与配音同步(compose 按帧对齐累计起点平移烧录,字幕窗口 = 该页配音发声区间)。
> 落盘实践提示(与操作系统无关):若 IDE 写入 SVG 后磁盘字节数为 0(回显成功但未落盘),改用命令行按 UTF-8(无 BOM)一次性写入,并以磁盘内容(字节数 > 0)为准验证——Windows 用 PowerShell [System.IO.File]::WriteAllText(path, content, (New-Object System.Text.UTF8Encoding($false)));macOS / Linux 用 printf '%s' "…" > path(或 cat > path <<'EOF' … EOF heredoc),两者均不附加 BOM。同一文件的多次修改建议一次性原子写入(单次写入完成全部改动)——连续或并行编辑可能部分丢失(回显成功但读回旧内容、替换计数异常);每次写盘后以磁盘内容核对所有预期改动均已存在(Windows 用 [System.IO.File]::ReadAllText(path)——.NET 默认按 UTF-8 解码,中文内容读回无乱码;不要用 Get-Content——PowerShell 5.1 对无 BOM 的 UTF-8 文件按系统 ANSI/GBK 解码显示乱码,易被误判为文件损坏;macOS / Linux 用 cat 抽查关键行),而非仅依赖编辑回显。该问题不限于示例目录——output/ 等任意目录都可能出现:回显成功但磁盘仍是旧版,渲染/解析工具读到旧文件会误判为"渲染器 bug"等(先确认文件确实落盘,再查渲染)。关键文件(SVG/SRT/脚本/manifest)一律按上法原子写入,并在同一命令内读回比对(读回内容与内存内容逐字一致)后才进入下一步。对 SVG 等关键文件做批量字符串替换时,先断言替换目标在文件中唯一出现(count==1)再执行,避免非唯一模式误伤其他元素(如 x="165" y="296" 会同时命中同页多个同坐标文本)。批量命令提示:生成端以循环/管道批量调用 CLI(如 1..9 | ForEach-Object { npx svgunity-cli tts … })时,个别环境存在文件视图同步延迟(IDE 写盘后终端进程短暂读不到文件、报 "failed to read … os error 2"),且循环内调用比逐条直连更易触发——批量调用前先 Test-Path 确认输入文件可见;仍失败时改为逐条直连调用(实测有效)。
10. 脚本模板
# 《<主题>》讲解视频脚本
- 总时长:T 秒(约 T/60 分钟)
- SVG 文件:scenes/page01.svg … pageNN.svg(每页一个独立 SVG,同尺寸,两位零填充)
- 成片方式:compose 一步成片(manifest video.json;每场景窗口自动 = 配音实测时长 + pad)
- 转场余量 pad:0.3-0.5s(manifest 全局;取 0 表示不插静音,音频背靠背拼接)
- 字幕文件:subtitles/page01.srt …(逐页,按各页窗口计时;时间轴默认取逐词时间戳,§4.3;可选)
- 目标受众:<一句话>
## 配音时间表(预估/实测回填;compose 音频驱动,实际以实测为准)
| 页 | 场景类型 | 配音/时长来源 | 场景窗口(时长 + pad) |
|---|---|---|---|
| 01 | audio | narration_01.webm 实测 …s | [0.00,…) |
| … | … | … | … |
## 第 01 页 · 封面(场景窗口 [0,a) · 配音 narration_01.webm / text)
**PPT 内容**
- 标题:…
- 要点:…
- 图示:…
- 动画建议:…
**配音文字**
> ……
---