This article provides a programmatic example of generating PDF using pure Java code without use of any external libraries. In general, preferred approach is always to use PDF generating libraries so that focus stays on PDF content rather than PDF structure & specification. This tutorial is just an example to showcase how low level PDF generation works.
Minimal PDF by hand
To get some heads up, its good to have a quick read of very easy, simple & well-written “Minimal PDF” article which shows how a basic human readable & hand-written PDF structure looks. Example from this link can be copied into a simple text file & that file can be opened with PDF reader to render as PDF. We will use the example give in this article for our example in this article.
As given in “Minimal PDF“, basic structure of PDF looks like this
1 2 3 4 5 6 7 8 9 |
[PDF version header] [PDF Object 1] [PDF Object 2] . . [PDF Object n] [Cross Reference] [Trailer] [End of file] |
To understand format in more depth you can also read Understanding the Portable Document Format (PDF)
Let’s Design PDF Object in Java
We will start with designing PDF object structure first as its basic building block of PDF structure specification.
This is the generic structure of the pdf object as per PDF specification
1 2 3 4 5 6 7 8 9 10 11 12 |
[object-number] [generation-number] obj /* Identifying info for this object **/ << /* Start of object keys */ /Type /[type-value] /* Type of this object */ /[key] [value] /* Key value pairs for this object */ /[key] /* Key with value as another object */ << [other-object] >> /[key] [object-number] [generation-number] R /* Key with value as reference to another object */ >> /* End of object keys */ [stream] /* Stream of data like text data etc. */ endobj /* End of object */ |
So lets start with creating a class representing pdf object.
1 2 3 4 5 6 7 8 9 10 11 12 |
class PDFObject { private String type; private int objectNumber; private int generation; private List<String> keys = new ArrayList<>(); private String stream; } |
For sake of simplicity while creating objects, lets add few constructors
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public PDFObject(int objectNumber, int generation, String type) { this.type = type; this.objectNumber = objectNumber; this.generation = generation; } public PDFObject(int objectNumber, int generation) { this.objectNumber = objectNumber; this.generation = generation; } public PDFObject(String type) { this.type = type; } |
Now lets add few methods for cases where this pdf object is used inside another pdf object as a reference.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public String getObjectReference() { return objectNumber + " " + generation + " R"; } public void addObjectReferenceKey(String key, PDFObject value) { keys.add("/" + key + " " + value.getObjectReference()); } public void addObjectReferenceArrayKey(String key, PDFObject... values) { String finalVal = "/" + key + " ["; for (PDFObject obj : values) { finalVal = finalVal + obj.getObjectReference(); } finalVal = finalVal + "]"; keys.add(finalVal); } |
Now lets add method to add text stream as per specification
1 2 3 4 5 |
public void addTextStream(String font, int fontSize, int xPos, int yPos, String text) { this.stream = "\nstream \n BT \n /" + font + " " + fontSize + " Tf \n " + xPos + " " + yPos + " Td\n (" + text + ") Tj\nET\nendstream\n"; } |
Now finally lets add methods to build object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public StringBuilder build() { StringBuilder pdfObject = new StringBuilder(); pdfObject.append(objectNumber).append(" ").append(generation).append(" obj\n ").append(buildObject()) .append("\nendobj\n\n"); return pdfObject; } public StringBuilder buildObject() { StringBuilder pdfObject = new StringBuilder(); pdfObject.append("<< "); if (type != null) { pdfObject.append("/Type /").append(type).append("\n"); } for (String key : keys) { pdfObject.append(" ").append(key).append("\n"); } pdfObject.append(" >>"); if (stream != null) { pdfObject.append(stream); } return pdfObject; } |
Let’s design PDF structure in java
The class to represent PDF structure in above description will look like this. For simplicity sake, we have excluded cross reference section as it is not mandatory to have. It is helpful to PDF reader to get to objects faster.
Header & trailer will be built directly so no need for separate variables for the same.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class PDF { private List<PDFObject> pdfObjects = new ArrayList<>(); public PDF(PDFObject root, PDFObject... objects) { pdfObjects.add(root); pdfObjects.addAll(Arrays.asList(objects)); } public String build() { StringBuilder pdf = new StringBuilder(); pdf.append("%PDF-1.1\n\n"); for (PDFObject pdfObject : pdfObjects) { pdf.append(pdfObject.build()); } pdf.append("trailer\n << /Root " + pdfObjects.get(0).getObjectReference() + "\n /Size " + (pdfObjects.size() + 1) + "\n >>\n" + "%%EOF"); return pdf.toString(); } } |
Finally, let’s generate PDF file
Now as given in minimal PDF example, we will create all PDF objects, PDF structure & then write it to a generatedPDF.pdf file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
public static void main(String[] args) throws IOException { /** * Create font object with times roman font & give it name as F1. */ PDFObject fontName = new PDFObject("Font"); fontName.addKey("Subtype", "/Type1"); fontName.addKey("BaseFont", "/Times-Roman"); PDFObject font = new PDFObject("Font", new PDFObject("F1", fontName)); /** * Create text object with above font with size 18 & coordinate position (10,10) * with text "Hello World" */ PDFObject text = new PDFObject(4, 0); text.addKey("Length", Integer.toString("Hello World".getBytes().length)); text.addTextStream("F1", 18, 10, 50, "Hello World"); /** * Create page object with above font & text */ PDFObject page = new PDFObject(3, 0, "Page"); page.addObjectKey("Resources", font); page.addObjectReferenceKey("Contents", text); /** * Create pages object with above page. */ PDFObject pages = new PDFObject(2, 0, "Pages"); pages.addKey("Count", "1"); pages.addKey("MediaBox", "[0 0 300 144]"); pages.addObjectReferenceArrayKey("Kids", page); page.addObjectReferenceKey("Parent", pages); /** * Create root object wrapping pages object. */ PDFObject root = new PDFObject(1, 0, "Catalog"); root.addObjectReferenceKey("Pages", pages); /** * Create PDF with abvoe root & all of the objects */ PDF pdf = new PDF(root, pages, page, text); /** * Write PDF to a file. */ FileWriter fileWriter = new FileWriter("generatedPDF.pdf"); fileWriter.write(pdf.build()); fileWriter.close(); } |
Here is the complete source code MinimalPDF.java .
Output
Text version of generated file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
%PDF-1.1 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj 2 0 obj << /Type /Pages /Count 1 /MediaBox [0 0 300 144] /Kids [3 0 R] >> endobj 3 0 obj << /Type /Page /Resources << /Font << /F1 << /Type /Font /Subtype /Type1 /BaseFont /Times-Roman >> >> >> /Contents 4 0 R /Parent 2 0 R >> endobj 4 0 obj << /Length 11 >> stream BT /F1 18 Tf 10 50 Td (Hello World) Tj ET endstream endobj trailer << /Root 1 0 R /Size 5 >> %%EOF |
PDF opened in PDF reader
Further reading for PDF generation
Generate PDF using Java from scratch without any library (Multiple pages and graphics)
Hi
I am able to see the text version of above example but not PDF version. How to check or see the generated version of PDF.
If I understood your question correctly, here are my thoughts. Make sure the name of file has .pdf extension. You also need some kind of PDF reader like acrobat reader etc. which you can search on google & download. Then open that .pdf file with that PDF reader software. That PDF reader file will parse that file & render PDF version as shown in above article. Let me know if I answered your question. If not, please elaborate question further so that I can provide more details.
Hello friend!
it’s a good tutorial, you could explain how put css or some styles in a pdf ?