Smart jPDFwriter - Java Graphics2D PDF Writer

100% Java Graphics2D based PDF driver for writing very high quality PDF documents.
  • How this PDF driver works
  • Page (Java Printable interface)
  • Document (Java Pageable interface)
  • The PdfWriter class
  • An example: Hello World PDF
  • The Report Writing Engine
  • Fonts
  • Adobe base 14 fonts
  • TrueType font
  • CID (Unicode) font
  • Type3 fonts
  • How to create PDF of smaller file size
  • Creating custom font for using with the PDF driver
  • Font fail over in PDF generation
  • Surprised to see font got substituted in output PDF

  • How this PDF driver works

    Smart jPDFWriter is a pure Java PDF writer works based on JPS (Java Print Service) framework. While generating a PDF document each page is represented by a java.awt.print.Printable object. You can add multiple of such page (java.awt.print.Printable) to a java.awt.print.Pageable to generate a PDF document. Java java.awt.print.Book is an implementation of Pageable interface. Graphics2D PDF driver translates all your rendering to proper PDF output.

    Back to Top
    Page (Java Printable interface)

    A page in Java Print Service (JPS) APIs is represented by the interface java.awt.print.Printable. Printable interface lets you implement the print(Graphics2D, PageFormat pf, int pageIndex) method for a particular page identified by the pageIndex page number.

    Content of your PDF document is rendered calling the Graphics2D APIs received as an argument to the print() method of the Printable (page) interface. Refer Java Graphics2D API to know more about how to render different kinds of objects such as text, image, shapes etc. Argument PageFormat provides page size, orientation and margin information.

    Back to Top
    Document (Java Pageable interface)

    A document (some time we call it as a Book) is your entire PDF document. A document in Java is represented by an interface called java.awt.print.Pageable. java.awt.print.Book is a known implementation of the Pageable interface. A Pageable interface lets you add or remove one or more pages (java.awt.print.Printable) to create a document.

    It is actually lot more simpler than you may be thinking. If you already have some Graphics2D work try creating PDF output from there. See the "Hello World PDF" example next.

    Back to Top
    The PdfWriter class

    When you are done creating a document (java.awt.print.Pageable) adding bunch of pages (java.awt.print.Printable) all that it requires next is to pass the Pageable to the PdfWriter.saveAsPdf() shown next. Moreover, there is a complete example next. There are more examples are in the downloaded archive. Download the archive, extract the ZIP in a local folder of your computer and execute the windows BAT files from bin directory.

    
        PdfWriter.saveAsPdf(OutputStream pdfOutput, java.awt.print.Pageable, PdfProperties pdfProps)
      

    First argument is the OutputStream where the actual PDF output gets created. The important argument to notice here is the second argument which is your document (Pageable). Last argument PdfProperties lets you set the properties of the generating PDF.

    Back to Top
    An example: Hello World PDF

    Here is an example that creates two pages (page-1 writes "Hello" and page-2 writes "World!"), adds them to a Pageable and then calls PdfWriter.saveAsPdf to save as a PDF document.

    //SmartjPDFWriterDemo.java
    import com.activetree.common.conversion.PdfProperties;
    import com.activetree.pdfgen.PdfWriter;
    
    import java.awt.print.PageFormat;
    import java.awt.print.Printable;
    import java.awt.print.PrinterException;
    import java.awt.print.Book;
    import java.awt.*;
    import java.io.FileOutputStream;
    
    public class SmartjPDFWriterDemo {
    
      public static void main(String[] args) throws Throwable {
        new SmartjPDFWriterDemo().createSamplePdf(args);
      }
    
      public void createSamplePdf(String args[]) throws Throwable {
        if (args.length < 1) {
          System.out.println("Usage: arg1=pdfOutputFile");
          //System.exit(1);
          args = new String[] {"hello_world.pdf"};
        }
        String pdfFile = args[0];
        //Set PDF generation properties; e.g. Compress, doc info etc.
        PdfProperties pdfProp = new PdfProperties();
    
        //PDF output will go into this file
        FileOutputStream pdfOutputStream = new FileOutputStream(pdfFile);
    
        //Need a Page size -- use a PageFormat for it.
        PageFormat pf = new PageFormat();  //default is NA_LETTER
    
        //Create the Pageable object such as Book and add Printable objects
        Book pageable = new Book();
    
        //Add some pages
        //each page can have its own page format.
        pageable.append(new SamplePage("Hello"), pf);
        pageable.append(new SamplePage("World!"), pf);
    
        //Produce PDF Doc
        PdfWriter pdfWriter = new PdfWriter();
        pdfWriter.saveAsPdf(pdfOutputStream, pageable, pdfProp);
    
        System.out.println("A PDF is produced at: \"" + pdfFile + "\"");
      }
    
      //Sample Page
      class SamplePage implements Printable {
        Color textColor = Color.decode("#3366cc");
        Font font = new Font("Helvetica", Font.BOLD, 36);
        String sampleString;
    
        public SamplePage(String sampleString) {
          this.sampleString = sampleString;
        }
    
        public int print(Graphics graphics, PageFormat pf, int pageIndex) throws PrinterException {
          Graphics2D g = (Graphics2D) graphics;
          g.setFont(font);
          g.setColor(textColor);
          g.drawString(sampleString, 72, 72);
          //Do anything else you want using the Graphics2D APIs.
          return pageIndex;
        }
      }
    }
          

    Back to Top
    The Report Writing Engine

    SmartJReport - is a pure Java report writing engine makes use of Smart jPDFwriter as the underlying PDF writer and converts report pages to a PDF document. It is lot easier to simply use the SmartJReport without knowing the details of Graphics2D APIs.

    To know more about SmartJReport visit ActiveTree, read the tutorial, download and try the sample programs.

    Back to Top
    Fonts

    Fonts are embedded automatically to the output PDF document. Unlike other APIs you never have to worry about finding out where the actual font file is located. In the above example a Java Font (Font font = new Font("Helvetica", Font.BOLD, 36)) is created and set in the Graphics2D.setFont(font) is all that needed to make use of fonts. Which font you want to use for a text segment is decided by the first argument in the Font constructor. In this example, "Helvetica" is the font name. Rest of the two arguments are the style and size of the font. Each time a text is drawn through the Graphics2D's drawText() and other text drawing APIs, the font that was set previously is applied. You can set a different font before drawing the text element that needs to be drawn with a different font and style.

    Back to Top
    Adobe base 14 fonts

    There is one interesting fact to keep in mind while creating and using fonts. As you can see in the example you can create any Java runtime supported java.awt.Font to render your text. Font file for each of these fonts are added to your output PDF document if applicable. More you use different kinds of fonts more font files are embedded to the output PDF resulting increased output PDF file size. A bigger file size some time inconvenient to share or send to others over net.

    However, there are fonts known as Adobe base 14 fonts, if used, the font file content is not embedded to the output PDF resulting very light weight PDF file size. These base 14 fonts are designed and powerful enough to create nice looking PDF documents in most cases. The reason for not embedding these base 14 fonts is that they are made available to the PDF reader program. Until and unless there is a real reason to use fonts other than base 14 fonts it is recommended to use these base 14 fonts mentioned below.

      //Courier - total 4
      new Font("Courier", Font.PLAIN, 10),
      new Font("Courier", Font.BOLD, 10),
      new Font("Courier", Font.ITALIC, 10),
      new Font("Courier", Font.BOLD + Font.ITALIC, 10),
    
      //Helvetica - total 4
      new Font("Helvetica", Font.PLAIN, 10),
      new Font("Helvetica", Font.BOLD, 10),
      new Font("Helvetica", Font.ITALIC, 10),
      new Font("Helvetica", Font.BOLD + Font.ITALIC, 10),
    
      //Times Roman - total 4
      new Font("Times Roman", Font.PLAIN, 10),
      new Font("Times Roman", Font.BOLD, 10),
      new Font("Times Roman", Font.ITALIC, 10),
      new Font("Times Roman", Font.BOLD + Font.ITALIC, 10),
    
      //symbol - total 1
      new Font("Symbol", Font.PLAIN, 10),
    
      //Dingbats - total 1
      new Font("ZapfDingbats", Font.PLAIN, 10),
      
    Back to Top
    TrueType font

    One thing about font is whether or not it should be embedded to the PDF document. Those fonts that are not adobe base 14 fonts are typically embedded so that the PDF reader while opening the PDF can find the font in the document itself. If the font is not added, PDF reader still may open it if the font is found in the platform. Due to this uncertainty it is always good idea to ensure font availability by embedding all non-base-14 fonts in the document itself.

    Other important thing is about how to embed fonts when fonts really needs to be embedded. If your text do not contain unicode characters it is recommended to embed font as TrueType. Code segment next shows how to embed non-base-14 font as TrueType by specifying proper ENCODING.

      PdfProperties pdfProp = new PdfProperties();
      pdfProp.setAttribute(PdfProperties.ENCODING, PdfProperties.WINANSI);  
      ...
      OutputStream pdfFile;
      Pageable pageable; 
      ...
      pdfWriter.saveAsPdf(pdfFile, pageable, pdfProp);
    
    Back to Top
    CID (Unicode) font

    If you want to add text that has Unicode characters, embedding fonts a CID fonts is the right choice. Following is how you can choose to embed fonts as CID fonts using IDENTITY_H as the character encoding.

      PdfProperties pdfProp = new PdfProperties();
      pdfProp.setAttribute(PdfProperties.ENCODING, PdfProperties.IDENTITY_H); 
      ...
      OutputStream pdfFile;
      Pageable pageable;
      ...
      pdfWriter.saveAsPdf(pdfFile, pageable, pdfProp);
      

    If your text do have Unicode in it, you may use appropriate font that supports Unicode characters. A Java font such as "Monospaced" (example: new Font("Monospaced", Font.PLAIN, 20) ) supports Unicode characters. For an Unicode supported font like this Java font typically represented by number of font files. Such font is called composite font. If the encoding is IDENTITY_H then the characters in your text is encoded as Unicode, fonts added accordingly and automatically.

    Back to Top
    Type3 fonts

    Like the TrueType and CID font embedding above this driver did not directly allow embedding of fonts as Type3. However, if the encoding is IDENTIFY_H then those Unicode characters not supported in the physical font file is embedded as Type3 font ensuring all characters in your text are added to the PDF.

    Back to Top
    How to create PDF of smaller file size

    One of the major reason for varying PDF output file size is whether or not the definition of the font file is added to the PDF itself. PDF when distributed for reading to a destination device the same fonts may not be available to the PDF reader for its proper text rendering. Therefore, it is typical for the application to generate PDF embedding the font to the PDF itself so that the PDF reader find the desired font in the PDF. This is the reason you may notice using certain fonts causing significantly higher PDF file size . By now one thing is clear is that the decision to add font to the PDF depends on what fonts you are using in rendering the texts.

    While rendering text you have an option to always use the Adobe base 14 fonts or some other fonts. We suggest you always use Adobe base 14 fonts which is guarented to be available with the PDF reader. Therefore, your readers will always see the same fonts you have used, the PDF outout size is going to be smallest possible and easy to distribute over internet.

    Back to Top
    Creating custom font for using with the PDF driver

    Using Java font (java.awt.Font) correctly is the key in successful creation of PDF. Other Java API gives you a hard time in figuring the font file to use for PDF creation. This API makes totally free from the complexity of specifying a font in PDF creation. Despite the fact the SmartJPdfWriter add font automaticaly to the PDF it also support custom java.awt.Font font creation from a known font file. Once the custom font is created and registered it is available for you to simply use it in rendering text using Graphics2D API in your application. Following is how you create your custom Java font:

      ///////////////////////////////////////
      ////This is your Java PDF writer Application//////
      ///////////////////////////////////////
      //Example:  the font file location
      String fontFileName = "c:/myFonts/truetype/myTrueType.ttf";
      File fontFile = new File(fontFileName);
      java.awt.Font myTTFont = java.awt.Font.createFont(Font.TRUETYPE_FONT, fontFile);
      //Once created register the font to see
      GraphicsEnvironment.registerFont(myTTfont);
    
      //subsequently use this font in your text rendering
      Graphics2D g2d = ...
      g2d.setFont(myTTfont);
      g2d.drawString(...);
          

    Back to Top
    Font fail over

    Fonts are platform and machine dependent. Application installed and worked in one machine may not work on another machine on same platform or on a different platform. It is therefore needed to ensure not to fail completely. Application should use the failover mechanism provided here. In worst case it is guaranteed not to fail but to use a default standard font can be set using the setAttribute(DEFAULT_FONT, FontData) method.

    Following is how the fail over happens:
    If the used font at runtime can not be found on this system then, 1. this custom map is used to find the failover font to use.
    2. Incase no custom mapping is provided by your application or this custom map fails to provide a failover java font the DEFAULT_FONT is used to avoid failure.

    By default the failover font map is empty until you add entries to the map using this method. However, there is a DEFAULT_FONT is already set which you can change calling the setAttribute(DEFAULT_FONT, FontData) method. Use getAttribute(DEFAULT_FONT) to check the default standard font used.

    Back to Top
    Surprised to see font got substituted in output PDF

    To understand this better you must read the above section about font fail over. You may find that the font worked just fine in one machine using correct font but got substituted when deployed on another machine. This is because of the above font substitution rule.

    Best practices are:

  • To be consistent in font usage and to make it work on all platforms you may consider creating custom font and use it in rendering the text in Graphics2D.
  • To avoid font substitution it is suggested to deploy the application in identical environment so that the same set of fonts be found in all platforms/machines.
  • Yet another alternative is to set a proper Java font for substitution in the PdfPropertes font substitution map. In worst case if the used font is not found on the platform the corresponding substituted font will be used. If the substitution font is also not found on the platform the last option is to use a default font set in the PdfProperties. This is where the change in font comes from and the generated PDF look different.
  • Back to Top


    Copyright © Activetree, Inc. All rights reserved.
    Web: http://www.activetree.com
    Email: sales@activetree.com
    Tel: +1 408-791-8036 Fax: +1 408-716-8450