Ghostscript如何为PDF大纲/书签启用Unicode编码支持
Ghostscript 生成PDF书签时Unicode字符乱码解决方案
Ghostscript 的 pdfmark 语法默认将圆括号()包裹的字符串按单字节PDFDocEncoding编码解析,原生不支持UTF-8编码的文本,因此直接写入带变音符号、非拉丁字符的UTF-8内容必然出现乱码。
正确实现方式
要正常显示包含变音符号在内的所有Unicode字符,需按照PDF规范使用Unicode格式的书签字符串,规则如下:
- 字符串不能用圆括号
()包裹,需使用尖括号<>包裹 - 尖括号内的内容为**带UTF-16BE BOM(固定前缀为
FEFF)**的文本字节对应的大写十六进制序列
最小可用示例
针对测试用的书签文本Lügenbaron,正确的pdfmark配置行如下:
[/Page 1 /Count 0 /View [/XYZ null null null] /Title <FEFF004C00FC00670065006E006200610072006F006E> /OUT pdfmark
批量生成脚本(Python)
不要直接将UTF-16编码的字节对象写入配置文件,需先转成十六进制字符串再拼接,参考代码:
def make_pdfmark(page: int, title: str, count: int = 0) -> str: # 标题文本转UTF-16BE字节 title_bytes = title.encode("utf-16-be") # 拼接BOM前缀,转大写十六进制 title_hex = "FEFF" + title_bytes.hex().upper() return f"[/Page {page} /Count {count} /View [/XYZ null null null] /Title <{title_hex}> /OUT pdfmark\n" if __name__ == "__main__": bookmark_entries = [ (1, 0, "Lügenbaron"), (4, 0, "Vorwort"), (7, 0, "1. Regeln") ] # pdfmark文件必须存为ASCII编码 with open("index.info", "w", encoding="ascii") as f: for p, cnt, t in bookmark_entries: f.write(make_pdfmark(p, t, cnt))
执行Ghostscript命令
-o参数默认隐含-dBATCH -dNOPAUSE参数,无需重复添加,注意pdfmark配置文件必须放在原PDF文件参数之前:
gs -sDEVICE=pdfwrite -o newfile_indexed.pdf index.info -f original_unindexed_file.pdf
之前尝试方案的错误原因
- 直接写入UTF-8编码文本并用
()包裹:解析器按单字节PDFDocEncoding拆分多字节UTF-8字符,直接导致乱码 - 整个pdfmark文件存为带BOM的UTF-16BE编码:Ghostscript的PostScript解析器仅支持ASCII编码的脚本文件,UTF-16编码的文件会触发解析错误直接退出
- 将UTF-16BE字节对象直接格式化写入配置:实际写入的是Python bytes类型的字符串表示(即
b'\x00xxx'格式的文本),并非合法的PostScript字符串,显示必然异常 - 给十六进制字符串额外加
()包裹:尖括号外的括号会被识别为普通文本内容,和书签内容一起显示在阅读器中
内容的提问来源于stack exchange,提问作者Xaver
相关产品推荐
相关产品推荐

