调整字符宽度后嵌入PDFont

时间:2015-02-24 16:10:11

标签: java pdf fonts pdfbox

我想使用Apache PDFBox创建符合PDF / A标准的PDF文件。为了符合PDF / A,必须嵌入所有使用的字体。我可以使用标准字体或从文件加载一个,但我需要调整几个字形的字符宽度。我可以通过加载字体(或使用标准字体)并在之后修改它来完成此操作,如下所示。

doc = new PDDocument();
PDPage page = new PDPage();
doc.addPage( page );
InputStream fontStream = PDFCreator.class.getResourceAsStream("ArialMT.ttf");
PDFont font = PDTrueTypeFont.loadTTF(doc, fontStream);
List<Float> test = font.getWidths();
test.set(101-32, 2000f);
font.setWidths(test);

但是如何嵌入修改后的字体呢?

1 个答案:

答案 0 :(得分:1)

使用修补的宽度字体字典条目

嵌入原始字体

如果使用您的被操纵字体,它将被嵌入。如果你是像这样继续你的代码:

PDPageContentStream stream = new PDPageContentStream(doc, page);
stream.setFont(font, 12);
stream.beginText();
stream.moveTextPositionByAmount(30, 600);
stream.drawString("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
stream.moveTextPositionByAmount(0, -20);
stream.drawString("abcdefghijklmnopqrstuvwxyz");
stream.moveTextPositionByAmount(0, -20);
stream.drawString("0123456789");
stream.endText();
stream.close();

doc.save("embedFont.pdf");

你使用PDFBox 1.8.8获得这样的PDF:

"embedFont.pdf" screenshot

如您所见,您对'e'宽度的操纵

test.set(101-32, 2000f);

使该字母的空间相当广泛。

如果您查看PDF,您会在字体词典中找到 Widths 数组:

/Widths [278.0 278.0 355.0 556.0 556.0 889.0 667.0 191.0 333.0 333.0
389.0 584.0 278.0 333.0 278.0 278.0 556.0 556.0 556.0 556.0
556.0 556.0 556.0 556.0 556.0 556.0 278.0 278.0 584.0 584.0
584.0 556.0 1015.0 667.0 667.0 722.0 722.0 667.0 611.0 778.0
722.0 278.0 500.0 667.0 556.0 833.0 722.0 778.0 667.0 778.0
722.0 667.0 611.0 722.0 667.0 944.0 667.0 667.0 611.0 278.0
278.0 278.0 469.0 556.0 333.0 556.0 556.0 500.0 556.0 2000.0 
...

你的2000没问题。就PDF而言,您的更改已存储。

不可否认,嵌入式字体程序中“e”的宽度不会改变。如果要更改它,则应预先处理宽度已调整的字体文件:

嵌入修补字体

您可以使用例如谷歌sfntly即时修补字体。在这种情况下,模拟代码可能如下所示:

byte[] fontBytes = null;
try (   InputStream arialMtResource = getClass().getResourceAsStream("ArialMT.ttf");
        ByteArrayOutputStream baos = new ByteArrayOutputStream()   )
{
    patchAdvanceWidth(arialMtResource, baos, 101-29, 2000);
    fontBytes = baos.toByteArray();
}

try (   ByteArrayInputStream fontStream = new ByteArrayInputStream(fontBytes);   )
{
    PDDocument doc = new PDDocument();
    PDPage page = new PDPage();
    doc.addPage(page);
    PDFont font = PDTrueTypeFont.loadTTF(doc, fontStream);
    PDPageContentStream stream = new PDPageContentStream(doc, page);
    stream.setFont(font, 12);
    stream.beginText();
    stream.moveTextPositionByAmount(30, 600);
    stream.drawString("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    stream.moveTextPositionByAmount(0, -20);
    stream.drawString("abcdefghijklmnopqrstuvwxyz");
    stream.moveTextPositionByAmount(0, -20);
    stream.drawString("0123456789");
    stream.endText();
    stream.close();

    doc.save("target/test-outputs/embedPatchedFont.pdf");
}

使用这个辅助方法:

void patchAdvanceWidth(InputStream is, OutputStream os, int entry, int newValue) throws IOException
{
    FontFactory fontFactory = FontFactory.getInstance();
    Builder[] builders = fontFactory.loadFontsForBuilding(is);
    Builder builder = builders[0];

    HorizontalMetricsTable.Builder hmtxBuilder = (HorizontalMetricsTable.Builder) builder.getTableBuilder(Tag.hmtx);
    WritableFontData hmtxData = hmtxBuilder.data();

    int offset = 0 + (entry * 4) + 0;
    hmtxData.writeUShort(offset, newValue);
    hmtxBuilder.setData(hmtxData);

    Font font = builder.build();
    fontFactory.serializeFont(font, os);
}