Reputation: 33
I want to create a PDF file conforming to the PDF/A standard by using Apache PDFBox. To conform to PDF/A, all used fonts have to be embedded. I can use either the standard fonts or load one from a file, but I need to adjust the character width of several glyphs. I can do this by loading a font (or using a standard font) and modify it afterwards, as shown below.
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);
But how can I embed the modified font?
Upvotes: 1
Views: 918
Reputation: 96064
If you use your manipulated font, it will be embedded. If you e.g. continue your code like this:
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");
you get a PDF like this using PDFBox 1.8.8:
As you see, your manipulation of the width of 'e'
test.set(101-32, 2000f);
makes the space for that letter fairly broad.
If you look into the PDF, you will find this Widths array in the font dictionary:
/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
...
Your 2000
is there alright. As far as the PDF is concerned, your change has been stored.
Admittedly, though, the width of 'e' in the embedded font program is not changed. If you want to change that, you should pre-process the font file with the widths adjusted:
You can use e.g. Google's sfntly to patch the font on the fly. In that case the analog code could look like this:
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");
}
making use of this helper method:
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);
}
Upvotes: 1