能否用iText7 .NET仅嵌入问题字符以优化PDF文件大小?
解决方案:用iText7 .NET实现字体子集化,仅嵌入问题字符
完全可以通过iText7的字体子集化功能实现你的需求——只嵌入PDF中实际用到的特定字符(包括č、ć这类缺失字符),既解决手机端字符显示问题,又能最大程度控制PDF体积。
核心思路
RDLC生成的未嵌入字体PDF体积小,但缺失的字符依赖系统字体;而全字体嵌入会大幅增加体积。子集化的本质是:提取文档中实际使用的字符集,仅嵌入这些字符对应的字体glyphs,而非整个字体文件。
具体实现步骤(C#)
- 准备依赖:安装iText7的NuGet包:
itext7和itext7.fonts - 加载源PDF并收集需嵌入的字符:遍历PDF内容,收集所有出现的字符(至少包含č、ć)
- 创建子集字体:加载对应字体文件,生成仅包含目标字符的子集
- 替换原PDF字体并嵌入子集:将原PDF中未嵌入的字体替换为子集字体,确保字符能正确渲染
示例代码
using iText.Kernel.Font; using iText.Kernel.Pdf; using iText.Layout.Font; using System.Text; // 源PDF路径和输出PDF路径 string sourcePdfPath = "input.pdf"; string outputPdfPath = "output.pdf"; // 收集文档中所有用到的字符(主动添加问题字符,再补充页面所有字符) HashSet<char> requiredChars = new HashSet<char> { 'č', 'ć' }; // 加载源PDF,遍历页面收集所有字符 using (PdfReader reader = new PdfReader(sourcePdfPath)) using (PdfDocument pdfDoc = new PdfDocument(reader)) { for (int pageNum = 1; pageNum <= pdfDoc.GetNumberOfPages(); pageNum++) { PdfPage page = pdfDoc.GetPage(pageNum); string pageText = PdfTextExtractor.GetTextFromPage(page); foreach (char c in pageText) { requiredChars.Add(c); } } } // 加载RDLC使用的字体文件(替换为实际字体路径,比如Arial.ttf) string fontPath = @"C:\Windows\Fonts\arial.ttf"; FontProgram fontProgram = FontProgramFactory.CreateFont(fontPath); // 生成仅包含目标字符的子集字体 fontProgram.Subset(requiredChars.Select(c => (int)c).ToArray()); PdfFont subsetFont = PdfFontFactory.CreateFont(fontProgram, PdfEncodings.IDENTITY_H, true); // 替换原PDF中的未嵌入字体并嵌入子集 using (PdfReader reader = new PdfReader(sourcePdfPath)) using (PdfWriter writer = new PdfWriter(outputPdfPath)) using (PdfDocument pdfDoc = new PdfDocument(reader, writer)) { PdfDictionary resources = pdfDoc.GetCatalog().GetPdfObject().GetAsDictionary(PdfName.Resources); if (resources == null) return; PdfDictionary fontDict = resources.GetAsDictionary(PdfName.Font); foreach (PdfName fontName in fontDict.KeySet()) { PdfDictionary font = fontDict.GetAsDictionary(fontName); // 匹配未嵌入的目标字体(根据实际字体名称调整判断逻辑) if (font.GetAsString(PdfName.BaseFont)?.ToString().Contains("Arial") == true && !font.GetAsBoolean(PdfName.Embedded)) { // 替换为子集字体并标记为已嵌入 font.Put(PdfName.BaseFont, subsetFont.GetPdfObject().GetAsString(PdfName.BaseFont)); font.Put(PdfName.FontDescriptor, subsetFont.GetFontDescriptor().GetPdfObject()); font.Put(PdfName.Embedded, PdfBoolean.TRUE); } } }
关键注意事项
- 字体匹配:确保加载的字体文件和RDLC生成PDF时使用的字体完全一致,否则可能出现字符渲染偏差
- 字符收集:主动添加č、ć后再遍历页面文本收集所有字符,避免遗漏其他潜在问题字符
- 编码设置:使用
IDENTITY_H编码支持Unicode字符,确保特殊字符能正确嵌入
这种方法相比“RDLC全嵌入再压缩”的优势在于:仅嵌入必要字符,体积增加微乎其微,同时从根源解决了手机端因缺少字体导致的字符缺失问题。
内容的提问来源于stack exchange,提问作者Milan
相关产品推荐
相关产品推荐

