Embedding Fonts In Java PDF-Creating Agents
Yesterday I talked about using Web Fonts to overcome the limitations of being restricted to the limited set of fonts available to web developers. Today I'll talk about over-coming the even more limited set of fonts available when creating PDF documents.
When you create a PDF with the likes of iText you're normally limited to the choice of Courier, Helvetica and Times New Roman. Helvetica is probably the default choice, but it's borrrrrring.
Let's use Source Sans Pro instead! Here's an example of a PDF using Source Sans Pro.
So, so, so much nicer than Helvetica.
If you want to try it out, you can create a Domino-driven PDF with the above font embedded in it by using this demo form.
How To Embed Fonts
How do you go about embedded the font for use with a Domino Java agent? Well, first thing you do is download the font. It doesn't have to be Source Sans Pro, but assuming you want it then download the "fonts only" ZIP from Sourceforge and extract to your PC.
Now, open the Java Agent you use for creating PDFs and click the Import dropdown button and choose to import a Resource. Browse to the folder of fonts and select the ones you want to use in your PDF.
When you're done the Agent will list the font files in the "Res" section, as below:
With the font files stored in the Agent you can load them at runtime and create a BaseFont based on them.
Here's the code that load the fonts as two BaseFonts (one for regular fonts and one for bold). The code then uses the BaseFonts to create some useable Fonts.
//Regular base font BaseFont bf = BaseFont.createFont("SourceSansPro-Regular.otf", BaseFont.WINANSI, BaseFont.EMBEDDED, false, getResourceAsByteArray("SourceSansPro-Regular.otf"), null); //Bold base font BaseFont bfb = BaseFont.createFont("SourceSansPro-Bold.otf", BaseFont.WINANSI, BaseFont.EMBEDDED, false, getResourceAsByteArray("SourceSansPro-Semibold.otf"), null); //A collection of fonts (based upon our base fonts) for re-use throughout the code! Font defaultFont = new Font(bf, 12); Font headerFont = new Font(bfb, 18); Font tinyFont = new Font(bf, 9); Font tinyFontBoldAndWhite = new Font(bfb, 9, Font.NORMAL, Color.WHITE);
It's those last four Font objects that we'll use in our code to specify the font for any PDF objects we add to our iText PDF Document.
Loading Resources
Notice in the above code that there was a call to a method call getResourceAsByteArray(). This simply gets a stream on the font file we stored in the Agent and converts it to a byte[] array that the BaseFont class can use.
Here's what that method looks like.
public static byte[] getResourceAsByteArray(String resourceName) throws Exception{ InputStream is = this.getClass().getResourceAsStream("/" + resourceName); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int nRead; byte[] data = new byte[16384]; while ((nRead = is.read(data, 0, data.length)) != -1) { buffer.write(data, 0, nRead); } buffer.flush(); return buffer.toByteArray(); }
The key to the above code is the getClass().getResourceAsStream() call which is what actually loads the file from the Resources section of the Agent.
Using The Fonts
Now that we've defined our Font objects we can use them in the code when added iText objects such as Paragraph to the document.
Paragraph myParagraph = new Paragraph( doc.getItemValueString("Title") , headerFont); myParagraph.setAlignment(Element.ALIGN_CENTER); document.add( myParagraph ); Anchor anchor = new Anchor( new Chunk( "This is a link back to the document which created this PDF!", new Font(bf, 10, Font.UNDERLINE, new Color(0, 0, 255) ) ) );
It's as simple as that. And the results are worth it.
Trade-Off
As with using Web Fonts there's a file size trade-off to consider. Embedding fonts in a PDF can easily add 50KB to its size. And that's per font-style. So, if you want Regular, Bold and Italic, it's three times that size.
You could of course just embed the Regular font and force it to act like bold or italic, but, as with the web, you don't get the same results.
Very timely. I've been working on a tool that uses the user name to generate a watermark with transparency and a pitch angle. The standard font doesn't scale well when you try to make it 100 pt tall, so it gets very blocky. The trick I had to use was to start off with the image for the watermark 300 pt tall and then resize the image a couple of times. This automatically smooths the edges, much to my delight, as the resizing function uses some interpolation rules. Simply using a BaseFont set up with a large font face would be a less memory intensive upgrade to this process.
Thank you, once again, sir Jake. You have been knighted by know, surely?
Reply
From the British Embassy website:
The Most Excellent Order of the British Empire (1917) is awarded mainly to civilians and service personnel for public service or other distinctions and has a military and a civil division. Ranks in the Order are Knight or Dame Grand Cross (GBE), Knight or Dame Commander (KBE or DBE), Commander (CBE), Officer (OBE) and Member (MBE).
If I'm not mistaken, private individuals can be nominated for Knighthood to this order, and your decade of contributions to the advancement of the art of making due with otherwise horrible tools is certainly a service to the crown and humanity.
Reply
Show the rest of this thread
Did u ever try PDFBox? With its Apache license it is less of an development headache
Reply
It would take a better reason than licencing to move me off iText. It just works. And well.
I knows there's a lot of confusion over iText licencing since version 5. But I only ever use version 2.x which doesn't fall fowl. I hope. See, there you go, confusion. Maybe PDFBox would be better ;-)
Reply
Show the rest of this thread
Would this still work if the font files lived in /jvm/lib/ext to avoid the overhead of detaching from the agent each run?
Reply
Not using the getResource() method but if you can put them on the server's disk (anywhere) then you can create a BaseFont from a File object.
As I understand it though. The Agent's content is stored on disk anyway (is it?) and it's not got to load it from inside the NSF each time. Or does it?
Reply
if Jake became a 'Sir' then that would make Karen
a 'Lady' I'm lost for words.~(;-(
Reply
Nice..Does it put any more pressure on the JVM?
Reply
Undoubtedly.
Reply
Show the rest of this thread