调整字符宽度后嵌入 PDFont
Embedding PDFont after adjusting character widths
我想使用 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);
但是如何嵌入修改后的字体呢?
将原始字体嵌入经过修补的 宽度 字体字典条目
如果您使用您操纵的字体,它将被嵌入。如果你例如像这样继续你的代码:
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:
如你所见,你对宽度的操纵'e'
test.set(101-32, 2000f);
使该字母的 space 相当宽泛。
如果您查看 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'在嵌入字体程序中的宽度没有改变。如果你想改变它,你应该预处理调整宽度的字体文件:
嵌入补丁字体
您可以使用例如Google 的 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);
}
我想使用 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);
但是如何嵌入修改后的字体呢?
将原始字体嵌入经过修补的 宽度 字体字典条目
如果您使用您操纵的字体,它将被嵌入。如果你例如像这样继续你的代码:
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:
如你所见,你对宽度的操纵'e'
test.set(101-32, 2000f);
使该字母的 space 相当宽泛。
如果您查看 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'在嵌入字体程序中的宽度没有改变。如果你想改变它,你应该预处理调整宽度的字体文件:
嵌入补丁字体
您可以使用例如Google 的 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);
}