开发PDF阅读器:如何利用正确的嵌入字体显示PDF文本?
解决PDF嵌入Type1字体解压后无意义的问题
你遇到的问题其实是对PDF中Type1字体的嵌入方式存在几个常见误解,加上解压代码的小问题导致的,咱们一步步拆解:
1. 不是所有PDF嵌入的Type1字体都是可读的PostScript程序
你期望看到的%!FontType1开头的完整Type1代码,是完整的Type1字体程序,但PDF里的嵌入字体大多是子集化的:
- 子集化字体只会包含文档中实际用到的字形,去掉了未使用的字形定义和其他冗余信息
- 很多子集化的Type1字体会用Compact Font Format (CFF,也叫Type1C) 存储,这是一种二进制格式,解压后是二进制数据,自然不是可读的PostScript文本
- 即使是非子集化的Type1字体,PDF也可能对其进行了特殊包装,不是直接的完整PostScript代码
2. 你的FlateDecode解压代码存在逻辑错误
你直接跳过前两个字节的做法是错误的,因为PDF中的FlateDecode流经常会伴随Predictor参数(比如/Predictor 12,对应PNG的预测器),这时候流的开头包含预测器的相关数据,不能随意跳过。正确的做法是先读取PDF字典中的/Predictor、/Columns等参数,根据这些参数处理解压后的数据,而不是硬编码跳过字节。
给你一个修正后的思路示例:
private static byte[] DecodeFlateDecodeData(byte[] data, int predictor = 1, int columns = 1) { using (var compressedDataStream = new MemoryStream(data)) using (var deflateStream = new DeflateStream(compressedDataStream, CompressionMode.Decompress)) using (var outputStream = new MemoryStream()) { var decompressedBuffer = new byte[1024]; int read; while ((read = deflateStream.Read(decompressedBuffer, 0, decompressedBuffer.Length)) != 0) { outputStream.Write(decompressedBuffer, 0, read); } outputStream.Flush(); var decompressedData = outputStream.ToArray(); // 如果有Predictor,需要处理对应解码逻辑,比如PNG的Predictor 12 if (predictor > 1) { decompressedData = ApplyPredictorCorrection(decompressedData, predictor, columns); } return decompressedData; } } // 示例:处理PNG Predictor(Predictor=12)的逻辑框架 private static byte[] ApplyPredictorCorrection(byte[] data, int predictor, int columns) { var corrected = new List<byte>(); int rowSize = columns + 1; // 每个行开头有一个预测器类型字节 for (int i = 0; i < data.Length; i += rowSize) { byte predictorType = data[i]; var rowData = data.Skip(i + 1).Take(columns).ToArray(); // 这里需要根据PDF规范实现对应预测器的反向计算(比如Paeth预测) // 具体逻辑可参考PDF参考手册第7章的Predictor定义 corrected.AddRange(rowData); } return corrected.ToArray(); }
3. 如何确认字体的实际类型
你可以先查看字体字典的/Subtype字段:
- 如果是
/Type1,可能是完整或子集化的Type1,但子集化版本大概率是CFF包装的二进制格式 - 如果是
/Type1C,那就是标准的CFF二进制字体,解压后需要用CFF解析库处理,而非直接读取文本
只有极少数PDF会嵌入完整的Type1字体,这种情况下解压后才会看到%!FontType1开头的可读内容——毕竟完整字体体积太大,绝大多数场景都会用子集化字体来减小文件大小。
内容的提问来源于stack exchange,提问作者Christoph Bruns
相关产品推荐
相关产品推荐

