如何将PDF合并到一个文件中,而不使用同一字体的多个副本?

如何将PDF合并到一个文件中,而不使用同一字体的多个副本?,pdf,fonts,itext,Pdf,Fonts,Itext,我创建PDF并将它们连接成单个PDF 我得到的PDF文件比我预期的文件大很多 我意识到我的输出PDF有大量重复的字体,这就是文件大小出人意料地大的原因 我的问题是: 我想创建只嵌入字体信息的PDF,所以让他们使用Windows系统字体 当我将它们合并到一个PDF中时,我会插入PDF所需的实际字体 如果可能的话,请告诉我怎么做。我创建了一个示例来解释不同的选项 我们将使用以下代码片段创建PDF: public void createPdf(String filename, String text,

我创建PDF并将它们连接成单个PDF

我得到的PDF文件比我预期的文件大很多

我意识到我的输出PDF有大量重复的字体,这就是文件大小出人意料地大的原因

我的问题是:

我想创建只嵌入字体信息的PDF,所以让他们使用Windows系统字体

当我将它们合并到一个PDF中时,我会插入PDF所需的实际字体

如果可能的话,请告诉我怎么做。

我创建了一个示例来解释不同的选项

我们将使用以下代码片段创建PDF:

public void createPdf(String filename, String text, boolean embedded, boolean subset) throws DocumentException, IOException {
    // step 1
    Document document = new Document();
    // step 2
    PdfWriter.getInstance(document, new FileOutputStream(filename));
    // step 3
    document.open();
    // step 4
    BaseFont bf = BaseFont.createFont(FONT, BaseFont.WINANSI, embedded);
    bf.setSubset(subset);
    Font font = new Font(bf, 12);
    document.add(new Paragraph(text, font));
    // step 5
    document.close();
}
我们使用此代码创建3个测试文件,1、2、3,我们将执行3次:A、B、C

第一次,我们使用参数
embedded=true
subset=true
,生成文本
“abcdefgh”
(3.71 KB)、文本
“ijklmnopq”
(3.49 KB)和文本
“rstuvwxyz”
(3.55 KB)的文件。字体是嵌入的,文件大小相对较小,因为我们只嵌入字体的一个子集

现在,我们使用以下代码合并这些文件,使用
smart
参数指示是要使用
PdfCopy
还是
PdfSmartCopy

public void mergeFiles(String[] files, String result, boolean smart) throws IOException, DocumentException {
    Document document = new Document();
    PdfCopy copy;
    if (smart)
        copy = new PdfSmartCopy(document, new FileOutputStream(result));
    else
        copy = new PdfCopy(document, new FileOutputStream(result));
    document.open();
    PdfReader[] reader = new PdfReader[3];
    for (int i = 0; i < files.length; i++) {
        reader[i] = new PdfReader(files[i]);
        copy.addDocument(reader[i]);
    }
    document.close();
    for (int i = 0; i < reader.length; i++) {
        reader[i].close();
    }
}
现在,我们有了这个文件(22.03KB),这实际上就是你问题的答案。如您所见,第二个选项比第三个选项好

注意事项:此示例使用Gravitas One字体作为简单字体。一旦将字体用作复合字体(通过选择编码
IDENTITY-H
IDENTITY-V
告诉iText将其用作复合字体),您就无法再选择是否嵌入字体,是否对字体进行子集设置。按照ISO-32000-1中的定义,iText将始终嵌入复合字体,并始终将其子集

这意味着您在需要特殊字体(中文、日文、韩文)时无法使用上述解决方案。在这种情况下,不应嵌入字体,而应使用所谓的CJK字体。他们的CJK字体将使用可由Adobe Reader下载的字体包

private void embedFont(String merged, String fontfile, String result) throws IOException, DocumentException {
    // the font file
    RandomAccessFile raf = new RandomAccessFile(fontfile, "r");
    byte fontbytes[] = new byte[(int)raf.length()];
    raf.readFully(fontbytes);
    raf.close();
    // create a new stream for the font file
    PdfStream stream = new PdfStream(fontbytes);
    stream.flateCompress();
    stream.put(PdfName.LENGTH1, new PdfNumber(fontbytes.length));
    // create a reader object
    PdfReader reader = new PdfReader(merged);
    int n = reader.getXrefSize();
    PdfObject object;
    PdfDictionary font;
    PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(result));
    PdfName fontname = new PdfName(BaseFont.createFont(fontfile, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED).getPostscriptFontName());
    for (int i = 0; i < n; i++) {
        object = reader.getPdfObject(i);
        if (object == null || !object.isDictionary())
            continue;
        font = (PdfDictionary)object;
        if (PdfName.FONTDESCRIPTOR.equals(font.get(PdfName.TYPE))
            && fontname.equals(font.get(PdfName.FONTNAME))) {
            PdfIndirectObject objref = stamper.getWriter().addToBody(stream);
            font.put(PdfName.FONTFILE2, objref.getIndirectReference());
        }
    }
    stamper.close();
    reader.close();
}