有次交付一批带特效字幕的宣传片,我在自己机器上检查了三遍:字体对、位置对、卡拉 OK 的逐字变色效果也对。发给客户,第二天收到回复——"字幕字体不对,而且有些字显示不出来"。
我让对方截图过来一看:宋体。所有特效都在(说明 ASS 本身被正确解析了),但字体回退了,位置也跟着错位。
原因说穿了很简单:ASS 文件里只记录了"我要用思源黑体"这个名字,并没有把字体文件本身带过去。对方电脑上没装这个字体,播放器就随便找了一个替代。
这篇文章把这条路上搞明白的事写出来:字幕的三种"放法"有什么区别、为什么字体是最容易出问题的环节、怎么把 18MB 的中文字体子集化成 90KB 塞进 MKV、以及在 Linux 服务器上烧录中文字幕要配什么环境。
TL;DR:ASS 只存字体名不存字体文件,跨机器必然出问题。三个解决办法按可靠性排序:烧录(
-vf subtitles=,100% 一致但不可逆)、MKV 内挂字体附件(-attach,播放器支持的话效果最好)、要求用户自己装字体(最不靠谱)。中文字体动辄十几 MB,用pyftsubset做字形子集化(只保留字幕里用到的字),18MB 能压到 90KB。另外:ASS 的坐标基于PlayResX/PlayResY,分辨率不匹配会导致位置错乱;字幕文件必须是 UTF-8;字体的商用授权要单独确认。
目录
- 一、字幕的三种"放法"
- 二、ASS 到底比 SRT 多了什么
- 三、字体问题的本质
- 四、MKV 内挂字体:把字体塞进容器
- 五、字体子集化:18MB → 90KB
- 六、烧录字幕:最可靠但也最粗暴
- 七、Linux 服务器上的中文字幕环境
- 八、PlayResX/Y:位置错乱的元凶
- 九、编码:BOM、GBK 与乱码
- 十、一套完整的字幕流水线
- 十一、坑清单与字体授权
一、字幕的三种"放法"
先把概念对齐,因为"加字幕"这三个字至少有三种完全不同的做法:
| 方式 | 怎么做 | 优点 | 缺点 | 适合 |
|---|---|---|---|---|
| 外挂 | 单独一个 .srt / .ass 文件 |
可编辑、可切换、体积小 | 容易丢失、播放器要支持、字体靠用户 | 本地收藏、媒体库 |
| 内挂(软字幕) | 封装进 MKV/MP4 的字幕流 | 不会丢、可切换、可多语言 | 播放器兼容性差异大、字体仍可能缺 | 多语言版本、媒体库 |
| 内嵌(硬字幕/烧录) | 把字画进每一帧 | 100% 一致,任何设备都显示 | 不可逆、不能关、不能翻译 | 短视频、交付成片、手机播放 |
我现在的判断标准:
- 要发给别人看的成片 → 烧录(除非对方明确要求可关闭);
- 自己存档、要在播放器里切语言的 → 内挂 MKV + 字体附件;
- 还要继续改的 → 外挂,改完再决定要不要内嵌。
判断不了的时候就问一句:"这个视频会在什么设备上播?" 如果答案里有"手机""微信""未知设备",那就烧录,别犹豫。
二、ASS 到底比 SRT 多了什么
SRT 是最简单的格式:
1
00:00:01,000 --> 00:00:04,000
这是一行字幕
只有时间和文本。没有字体、没有颜色、没有位置——一切由播放器决定。
ASS(Advanced Substation Alpha)多了一整套样式系统。一个最小的 ASS 文件结构:
[Script Info]
ScriptType: v4.00+
PlayResX: 1920
PlayResY: 1080
WrapStyle: 0
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, OutlineColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Source Han Sans SC,54,&H00FFFFFF,&H00000000,&H80000000,0,0,1,2,1,2,60,60,50,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:01.00,0:00:04.00,Default,,0,0,0,,这是一行字幕
Dialogue: 0,0:00:05.00,0:00:08.00,Default,,0,0,0,,{\pos(960,900)\fad(200,200)}带特效的字幕
三个关键部分:
[Script Info]里的PlayResX/PlayResY:字幕的"设计分辨率",所有坐标都基于它;[V4+ Styles]:样式定义,字体名、字号、颜色、描边、对齐方式、边距;[Events]里的Text:可以带{\...}特效标签,实现定位、移动、淡入淡出、逐字高亮(卡拉 OK)。
需要注意的:字体名写的是 Source Han Sans SC(字体的内部名称),不是文件名。而且不同系统对同一字体的叫法可能不一样(比如"思源黑体"在 Windows 上可能叫 "Source Han Sans SC" 或 "Noto Sans CJK SC"),这本身就埋了坑。
常见特效标签:
| 标签 | 作用 |
|---|---|
\pos(x,y) |
绝对定位 |
\move(x1,y1,x2,y2,t1,t2) |
移动 |
\fad(in,out) |
淡入淡出(毫秒) |
\t(...) |
渐变/动画 |
\k / \K / \kf |
卡拉 OK 逐字高亮 |
\b1 / \i1 |
粗体 / 斜体 |
\fs30 |
字号 |
\c&HFFFFFF& |
颜色(注意是 BGR 顺序!) |
\N |
强制换行 |
颜色是 BGR 不是 RGB,这个坑我踩过:&H0000FF& 是红色不是蓝色。
三、字体问题的本质
ASS 文件里只有字体名,没有字体数据。 播放的时候,播放器拿着这个名字去系统里找:
- Windows:走 GDI/DirectWrite 的字体枚举;
- Linux/macOS:走 fontconfig;
- 播放器还可能先看自己的字体目录(PotPlayer、VLC 都有自己的 fonts 目录)。
找不到怎么办?回退到默认字体。Windows 上通常是宋体(SimSun)或者 Arial,macOS 上是 Helvetica/苹方,Linux 上取决于 fontconfig 配置。回退之后:
- 字体样式完全变了(这就是客户看到的"宋体");
- 字宽变了,排版就变了——原来居中的一行现在超出边界,原来在画面下方 10% 的字幕现在跑到画面外;
- 特效标签里的精确坐标(比如卡拉 OK 的高亮位置)全部错位。
更糟的情况是缺字形:英文字体里没有中文字符,回退之后中文直接显示成方框(□)或者空白。我在一台干净的 Linux 服务器上第一次烧录中文字幕,输出的字幕全是空白——服务器上一个中文字体都没装。
所以结论很简单:只要目标设备不受你控制,就不能依赖"对方装了字体"这个假设。
四、MKV 内挂字体:把字体塞进容器
MKV(Matroska)支持附件(attachment),可以把字体文件当作附件塞进去。支持的播放器(PotPlayer、VLC、mpv、Kodi)会自动加载这些字体来渲染 ASS。
ffmpeg -i input.mp4 -i subtitle.ass \
-attach "SourceHanSansSC-Regular.otf" \
-attach "FiraSans-Bold.ttf" \
-map 0:v -map 0:a -map 1:s \
-c:v copy -c:a copy -c:s copy \
-metadata:s:s:0 language=chi \
-metadata:s:t:0 mimetype=font/otf \
-metadata:s:t:1 mimetype=application/x-truetype-font \
output.mkv
几个要点:
-attach每个字体一次,顺序对应后面的s:t:0、s:t:1;mimetype必须写对,否则播放器不认:
| 字体类型 | mimetype |
|---|---|
| TrueType (.ttf) | application/x-truetype-font 或 font/ttf |
| OpenType (.otf) | font/otf 或 application/vnd.ms-opentype |
| WOFF | font/woff |
我一般用 application/x-truetype-font(对 ttf)和 font/otf(对 otf),实测这两个兼容性最好。
-c:v copy -c:a copy -c:s copy:字幕内挂不需要重编码,很快;- MP4 不支持字体附件(可以用 tx3g 但那是另一套东西,不支持 ASS)。所以要内挂字体就必须用 MKV。
检查附件有没有进去:
ffmpeg -i output.mkv 2>&1 | grep -A5 'Stream #.*: Attachment'
# 或者
mkvmerge -J output.mkv | python3 -c "import json,sys; d=json.load(sys.stdin); print([a['file_name'] for a in d.get('attachments',[])])"
局限性要说清楚:
- MP4 不支持;
- 手机播放器、网页播放器、智能电视基本都不加载附件字体;
- 微信/抖音这类平台上传后必然重新转码,附件肯定丢。
所以内挂只适合"我知道对方用 PotPlayer/VLC 在电脑上看"的场景。拿不准就烧录。
五、字体子集化:18MB → 90KB
中文字体为什么大?因为汉字多。一个完整的中文字库(思源黑体 Source Han Sans SC)大概 16~18MB,一套完整的字重家族能上百 MB。
但你的字幕里可能只用了 300 个字。子集化(subsetting)就是只保留实际用到的字形,其余全部丢掉。
工具:fonttools(Python 库,pip install fonttools,里面有个 pyftsubset 命令)。
第一步:从 ASS 里提取用到的字符
import re
TAG = re.compile(r'\{[^}]*\}') # 特效标签
OVERRIDE = re.compile(r'\\[a-zA-Z]+[^\\}\s]*') # 行内样式覆盖
def extract_chars(ass_path: str) -> set:
chars = set()
with open(ass_path, encoding='utf-8-sig') as f:
for line in f:
# 只处理 Dialogue 行
if not line.startswith('Dialogue:'):
continue
text = line.split(',', 9)[-1] # 前 9 个字段之后是文本
text = TAG.sub('', text) # 去掉 {特效}
text = OVERRIDE.sub('', text) # 去掉 \pos 之类
text = text.replace('\\N', '\n').replace('\\n', '\n')
chars.update(text)
# 补上常见标点和数字,防止后续微调字幕时缺字
chars.update(',。!?、;:""''()《》…—0123456789')
return chars
if __name__ == '__main__':
chars = extract_chars('subtitle.ass')
with open('chars.txt', 'w', encoding='utf-8') as f:
f.write(''.join(sorted(chars)))
print(f'{len(chars)} unique chars')
line.split(',', 9)[-1] 这个写法要注意:ASS 的 Dialogue 行格式是 Layer,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text——前 9 个字段用逗号分隔,第 10 个(Text)里可能也含逗号,所以要用 maxsplit=9。
第二步:子集化
pyftsubset SourceHanSansSC-Regular.otf \
--text-file=chars.txt \
--output-file=SourceHanSansSC-subset.otf \
--layout-features='' \
--no-hinting \
--desubroutinize \
--drop-tables+=DSIG
参数说明:
| 参数 | 作用 |
|---|---|
--text-file |
要保留的字符清单(也可以用 --text="直接写字符") |
--layout-features='' |
丢掉 OpenType 排版特性(竖排、连字等),省空间 |
--no-hinting |
丢掉 hinting 指令(低分辨率屏幕优化),省很多 |
--desubroutinize |
对 CFF 字体(.otf)进一步压缩 |
--drop-tables+=DSIG |
丢掉数字签名表 |
实测(思源黑体 SC Regular,18.2MB):
| 字符数 | 子集后大小 | 压缩率 |
|---|---|---|
| 100 字 | 32 KB | 0.18% |
| 500 字 | 87 KB | 0.48% |
| 2000 字 | 310 KB | 1.7% |
| 8000 字(常用汉字全集) | 1.2 MB | 6.6% |
我一般会多留一些字(加常用字表 3500 字),一是防止后续改字幕要重新做,二是大小反正也就几百 KB,塞进 MKV 完全没压力。
第三步:替换 ASS 里的字体名
子集化后的字体,内部名称可能变了(或者你改了文件名)。ASS 里要对应改:
import re
def retarget_font(ass_path, old_name, new_name, out_path):
with open(ass_path, encoding='utf-8-sig') as f:
content = f.read()
# 替换样式定义里的 Fontname 字段(Format 里的第 2 列)
content = content.replace(f',{old_name},', f',{new_name},')
with open(out_path, 'w', encoding='utf-8') as f:
f.write(content)
如果字体内部名称和文件名不一致,可以用 fonttools 改:
from fontTools.ttLib import TTFont
font = TTFont('subset.otf')
for rec in font['name'].names:
if rec.nameID in (1, 4, 6): # 1=Family, 4=Full, 6=PostScript
rec.string = 'MySubsetFont'
font.save('subset_renamed.otf')
我习惯把子集字体统一改名成一个明确的名字(比如 SubFont-Regular),这样 ASS 里引用的时候不会被系统里已装的同名字体"抢走"。
六、烧录字幕:最可靠但也最粗暴
烧录(burn-in / hardsub)就是把字幕画进视频帧里,从此它就是画面的一部分。
ffmpeg -i input.mp4 -vf "subtitles=subtitle.ass" \
-c:v libx264 -crf 20 -preset slow -c:a copy output.mp4
如果要指定字体目录(服务器端通常要):
ffmpeg -i input.mp4 \
-vf "subtitles=subtitle.ass:fontsdir=/usr/share/myfonts" \
-c:v libx264 -crf 20 -c:a copy output.mp4
Windows 上路径要转义冒号(盘符那个冒号):
ffmpeg -i input.mp4 -vf "subtitles=sub.ass:fontsdir='C\:/SubtitleFonts'" out.mp4
或者用相对路径、把字体放到当前目录,能省不少麻烦。
依赖:ffmpeg 必须有 libass
ffmpeg -version 2>&1 | grep -o 'enable-libass'
没有的话要自己编(编译静态 ffmpeg 那篇讲过流程)。libass 依赖 freetype、fontconfig、fribidi、harfbuzz——这些是字幕渲染的完整链路,缺一个中文就会出问题。
烧录的几个实用技巧
1. 只烧录不重编码音频 → -c:a copy 省时间。
2. 保持画质:烧录必然要重编码视频。用 CRF 20 左右(比平时低一点,因为文字边缘对压缩敏感)。文字是最容易被压缩糊掉的内容,CRF 太高会出现"字迹边缘发虚"。
3. 高分辨率下字幕太小:ASS 的字号是基于 PlayRes 的,如果视频实际分辨率跟 PlayRes 不一致,字幕会被缩放。处理办法是保持两者一致,或者用 scale 先把视频缩到 PlayRes 对应的分辨率。
4. 用 subtitles 滤镜而不是 ass 滤镜:后者是老的、功能不全的实现,早就该弃用了。
5. 想保留原画质又必须烧录?做不到。烧录必然重编码,这是原理决定的。能做的是把 CRF 调低、用更慢的 preset。
七、Linux 服务器上的中文字幕环境
我们有一批任务在服务器上自动烧录字幕,第一次跑出来的视频字幕全是空白。排查过程:
# 1. 看系统有没有中文字体
fc-list :lang=zh
# 输出为空 → 一个中文字体都没有
# 2. 看 ffmpeg 报什么
ffmpeg -v info -i in.mp4 -vf subtitles=sub.ass -f null - 2>&1 | grep -i font
# [Parsed_subtitles_0 @ 0x...] fontselect: (Source Han Sans SC, 400, 0) -> /usr/share/fonts/... (没找到)
# Glyph 0x4E2D (中) not found in selected font
解决(以 Ubuntu 为例):
# 装一个开源中文字体(思源黑体 / Noto Sans CJK)
apt-get install -y fonts-noto-cjk
# 或者手动放字体
mkdir -p /usr/share/fonts/mine
cp SourceHanSansSC-Regular.otf /usr/share/fonts/mine/
chmod 644 /usr/share/fonts/mine/*.otf
# 刷新字体缓存(必须!)
fc-cache -fv
# 验证
fc-list :lang=zh | head
fc-match "Source Han Sans SC"
fc-cache -fv 这一步最容易忘。放了字体文件不刷新缓存,fontconfig 根本不知道它的存在。
如果不想污染系统目录(比如没有 root),可以放到用户目录:
mkdir -p ~/.fonts
cp *.otf ~/.fonts/
fc-cache -fv
# 或者用 fontconfig 配置文件指定目录
cat > ~/.config/fontconfig/fonts.conf <<'EOF'
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<dir>/opt/myfonts</dir>
</fontconfig>
EOF
fc-cache -fv
Docker 环境里尤其要注意:大部分基础镜像(alpine、slim 版)不带任何字体。我在 Dockerfile 里会显式写:
RUN apt-get update && apt-get install -y --no-install-recommends \
fonts-noto-cjk fontconfig \
&& rm -rf /var/lib/apt/lists/*
RUN fc-cache -fv
另外一个坑:字体名匹配。ASS 里写的是 Source Han Sans SC,但装的是 Noto Sans CJK(fonts-noto-cjk 包的名字),两者其实是同一套字体的不同叫法,fontconfig 一般能匹配上(通过 alias),但不保证。稳妥做法是在 ASS 里指定一个你确定存在的字体名,或者干脆用子集化后的自定义字体名 + fontsdir 指向它。
八、PlayResX/Y:位置错乱的元凶
ASS 里的所有坐标(\pos、MarginV、MarginL)都是基于 PlayResX/PlayResY 这个虚拟画布的。
假设 ASS 的 PlayResX: 1920, PlayResY: 1080,字幕用 \pos(960,1000) 定位在画面底部。
情况 A:视频就是 1920×1080 → 完美。
情况 B:视频是 1280×720(同样是 16:9)→ 播放时会按比例缩放,位置仍然正确。
情况 C:视频是 1080×1920(竖屏 9:16)→ 播放器把 1920×1080 的画布拉伸/适配到竖屏,字幕位置完全错乱,可能跑到画面外。
情况 D:播放器忽略 PlayRes,直接按视频分辨率解释坐标 → 字幕位置偏移。
所以规则是:ASS 的 PlayRes 必须跟目标视频的分辨率匹配。
改的办法(改 ASS 文件头):
[Script Info]
PlayResX: 1280
PlayResY: 720
但光改 PlayRes 不够——字号、边距、坐标都是按原分辨率算的,改了 PlayRes 之后它们不会自动缩放。真正的做法是整体缩放字幕,可以用 ffmpeg 配合:
# 先把视频缩放到字幕的设计分辨率,烧录,再缩回去(两步缩放会损失画质,不推荐)
或者用专门的工具(Aegisub 的 "Resample resolution" 功能)一次性把所有坐标按比例换算。我一般是在生成字幕的时候就确定目标分辨率,从源头避免这个问题。
判断现有 ASS 和视频是否匹配:
# ASS 的分辨率
grep -E '^PlayRes' subtitle.ass
# 视频的分辨率
ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 input.mp4
我把这个检查加进了字幕处理脚本的第一步,不匹配就直接报错让人工处理,比事后发现位置不对返工强。
九、编码:BOM、GBK 与乱码
ASS 文件的编码问题很常见,尤其是从 Windows 上生成的字幕。
规则:ASS 必须是 UTF-8。带不带 BOM(U+FEFF)都行,但推荐带 BOM,因为很多 Windows 工具(包括某些版本的 Aegisub、VLC)靠 BOM 判断编码。
Python 里读 ASS 一定要用 utf-8-sig:
with open('subtitle.ass', encoding='utf-8-sig') as f: # 自动去掉 BOM
...
用 utf-8 读带 BOM 的文件,第一行会多出 \ufeff,导致 [Script Info] 解析失败。这个 bug 让我排查过半小时——现象是"字幕明明有内容但就是解析不出来"。
遇到 GBK 编码的 ASS,ffmpeg 可以指定输入编码:
ffmpeg -sub_charenc GBK -i input.mp4 -vf "subtitles=sub_gbk.ass" out.mp4
但更稳妥的是先转成 UTF-8 存一份,后续全链路统一:
import chardet
def to_utf8(src, dst):
raw = open(src, 'rb').read()
detected = chardet.detect(raw)
enc = detected['encoding'] or 'utf-8'
text = raw.decode(enc, errors='replace')
with open(dst, 'w', encoding='utf-8-sig') as f:
f.write(text)
print(f'{src}: {enc} -> utf-8-sig (confidence {detected["confidence"]:.2f})')
chardet 的检测不是 100% 准(短文本的 GBK 和 UTF-8 容易混),所以它给了 confidence。低于 0.8 的我都会人工看一眼。
十、一套完整的字幕流水线
把我现在的流程串起来(从拿到字幕到产出交付文件):
#!/bin/bash
# build_subtitle.sh input.mp4 subtitle.srt output_dir
set -euo pipefail
IN="$1"
SUB="$2"
OUTDIR="$3"
mkdir -p "$OUTDIR"
# 1. 检查并转编码
python3 to_utf8.py "$SUB" "$OUTDIR/sub.utf8.ass"
# 2. 检查分辨率匹配
python3 check_resolution.py "$IN" "$OUTDIR/sub.utf8.ass"
# 3. 提取字符 + 子集化字体
python3 extract_chars.py "$OUTDIR/sub.utf8.ass" > "$OUTDIR/chars.txt"
pyftsubset /usr/share/fonts/SourceHanSansSC-Regular.otf \
--text-file="$OUTDIR/chars.txt" \
--output-file="$OUTDIR/SubFont.otf" \
--layout-features='' --no-hinting --desubroutinize
# 4. 改 ASS 里的字体名
python3 retarget_font.py "$OUTDIR/sub.utf8.ass" "Source Han Sans SC" "SubFont" "$OUTDIR/sub.final.ass"
# 5a. 产出烧录版(交付用)
ffmpeg -y -i "$IN" \
-vf "subtitles=$OUTDIR/sub.final.ass:fontsdir=$OUTDIR" \
-c:v libx264 -crf 20 -preset slow -c:a copy \
-movflags +faststart "$OUTDIR/hardsub.mp4"
# 5b. 产出内挂版(存档用,字体已子集化)
ffmpeg -y -i "$IN" -i "$OUTDIR/sub.final.ass" \
-attach "$OUTDIR/SubFont.otf" \
-map 0:v -map 0:a -map 1:s \
-c copy \
-metadata:s:s:0 language=chi \
-metadata:s:t:0 mimetype=font/otf \
"$OUTDIR/softsub.mkv"
echo "done: $OUTDIR/hardsub.mp4 (交付) / $OUTDIR/softsub.mkv (存档)"
这套流程跑一次大概几分钟(主要是烧录的重编码),出来的两个文件分别对应"给别人看"和"自己留着"。
两个版本都留是个好习惯。有次客户说"能不能把字幕去掉重新出一版",我从 mkv 里一秒就提取出来了,不用去找原始字幕重新做。
十一、坑清单与字体授权
坑清单
- ASS 只存字体名 → 换台机器就回退成宋体。烧录或内挂字体。
- MP4 不支持字体附件 → 想内挂字体只能用 MKV。
- 附件没设 mimetype → 播放器不加载,等于没挂。
- 中文字体 18MB 直接塞 → 文件体积爆炸。做子集化。
- 子集化字符提取不全(没去掉特效标签里的字符 / 忘了标点)→ 少数字变成方框。
- 服务器没装中文字体 → 烧录出来字幕空白。装字体 +
fc-cache -fv。 - Docker 镜像没字体 → 同上,Dockerfile 里要显式装。
- PlayRes 和视频分辨率不匹配 → 字幕位置错乱,尤其竖屏视频。
- ASS 是 GBK 编码 → ffmpeg 解析乱码或失败。先转 UTF-8。
- 用
utf-8读带 BOM 的文件 → 首行多出\ufeff。用utf-8-sig。 - 颜色写成 RGB → ASS 是 BGR,
&H0000FF&是红色。 - 烧录用 CRF 太高 → 文字边缘糊掉。用 20 或更低。
- 用了老的
ass滤镜 → 功能不全。用subtitles。 - Windows 路径冒号没转义 →
fontsdir='C\:/fonts'。 - 只做了烧录版 → 客户要无字幕版时得重做。两个版本都留。
字体授权(这一段请认真看)
字体是受版权保护的软件,不是"下载下来就能用"。踩过这个坑的不止我一个人:
- 方正字体:商业使用需要授权,未经授权用于商业物料(包括视频)被起诉的案例不少;
- 微软雅黑:它是微软授权给 Windows 用户的系统字体,把雅黑字体文件打包进你的作品分发是侵权的(用在 Windows 上做图不算,但把字体文件本身发出去就算);
- 思源黑体 / Noto Sans CJK:Adobe 和 Google 联合发布,SIL Open Font License(OFL),可以免费商用、可以嵌入、可以修改(但改名字后不能还叫原名,且 OFL 要求保留版权声明);
- 站酷系列字体:多数免费商用,但每个字体的授权条款不同,要逐个确认;
- 阿里巴巴普惠体、OPPO Sans、MiSans:官方声明可免费商用。
我的处理原则:
- 默认只用明确可商用的字体(思源/Noto、阿里普惠体、OPPO Sans、站酷免费商用的那几款);
- 子集化后的字体仍然受原授权约束——压缩不改变授权;
- 交付文档里写明用了哪些字体及授权来源,让客户自己也能查;
- 客户指定要用某个字体时,要求对方提供授权证明,或者让他们自己提供字体文件。
技术上"能做到"和"可以做"是两回事。字体这事出问题的代价远大于多问一句的成本。
回到开头那个"字幕变成宋体"的工单。最后我的处理是:重做了一版烧录的,同时给了一份内挂子集化字体的 MKV 存档,并在交付说明里写了"字体:思源黑体 SC(OFL 授权),已子集化"。
客户那边再没反馈过问题。而我自己学到的最重要的一条是:凡是"依赖对方环境"的东西,都要么自带,要么消除。字体如此,播放器如此,编解码器也如此。这个思路后来也用在了别的地方——比如交付 ffmpeg 二进制时要静态链接,道理是一模一样的。