<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://mehmandarov.com/tag/pdf/feed.xml" rel="self" type="application/atom+xml"/><link href="https://mehmandarov.com/tag/pdf/" rel="alternate" type="text/html"/><updated>2019-06-21T07:01:00+02:00</updated><id>https://mehmandarov.com/tag/pdf/feed.xml</id><title type="html">Rustam Mehmandarov - tag: pdf</title><subtitle type="text">Posts tagged &quot;pdf&quot; on Rustam Mehmandarov.</subtitle><author><name>Rustam Mehmandarov</name></author><entry><title type="html">Generating PDF Files Using Java</title><link href="https://mehmandarov.com/generating-pdf-files-using-java/" rel="alternate" type="text/html" title="Generating PDF Files Using Java"/><published>2019-06-21T07:01:00+02:00</published><updated>2019-06-21T07:01:00+02:00</updated><id>https://mehmandarov.com/generating-pdf-files-using-java</id><content type="html" xml:base="https://mehmandarov.com/generating-pdf-files-using-java/"><![CDATA[<p><em>A step by step tutorial on how to generate PDF files in Java.</em></p>

<ul>
  <li><a href="#intro">Intro</a></li>
  <li><a href="#choosing-a-library">Choosing a Library</a></li>
  <li><a href="#generating-pdf-files">Generaring PDF Files</a>
    <ul>
      <li><a href="#adding-maven-dependency">Adding Maven Dependency</a></li>
      <li><a href="#getting-started-with-the-code">Getting Started With the Code</a></li>
      <li><a href="#adding-text">Adding Text</a></li>
      <li><a href="#adding-images">Adding Images</a></li>
      <li><a href="#adding-metadata-to-the-document">Adding Metadata to the Document</a></li>
      <li><a href="#closing-saving-and-returning-the-document">Closing, Saving and Returning the Document</a></li>
    </ul>
  </li>
  <li><a href="#what-now">What Now?</a></li>
</ul>

<hr />

<h2 id="intro">Intro</h2>

<p>In my previous post, I have started describing a <a href="/generating-qr-codes-with-secure-hashes-using-java/">system for &#8220;checking-in&#8221; to a location using QR codes in Java</a>. Now, I would like to describe another part of that system that will show you how to get started with generating PDF files using Java.</p>

<p>So, let&#8217;s have a look at how this can be implemented in your solution &#8211; step by step.</p>

<h2 id="choosing-a-library">Choosing a Library</h2>

<p>Generating PDFs is normally something you would like to do using 3rd party libraries, and there are quite a few alternatives available. While choosing a library it might be a good idea to have a closer look at the licensing for that library &#8211; some of them might be very permissive and some might force your code to comply to a specific licensing. Some libraries even come with dual licensing, one under a proprietary model and one supporting an open source model.</p>

<p>One should also consider other aspects like maturity and whether it is a high- or low-level library. The latter will tell you how much code you will actually end up writing to implement your features. In the course of this project, I ended up trying two different libraries &#8211; <a href="https://itextpdf.com/en">iText</a> and, later, <a href="https://pdfbox.apache.org/">Apache PDFBox</a>. Since the point of this code was a tutorial, I decided to stick with PDFBox as it is distributed under more permissive license &#8211;&#160;<a href="https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)">Apache License 2.0</a>, as opposed to dual licensed iText that is under <a href="https://tldrlegal.com/license/gnu-affero-general-public-license-v3-(agpl-3.0)">AGPL</a> and a commercial license.</p>

<h2 id="generating-pdf-files">Generating PDF Files</h2>

<p>As mentioned earlier, this library provides quite extensive functionality for generating PDF files, but it is also quite low-level, so you will have to be prepared to implement a few things you might usually take for granted, e.g. things like calculating coordinates for text that has to be centered on a page and a few other things. However, the library has a great community, so it is quite easy to get help.</p>

<h4 id="adding-maven-dependency">Adding Maven Dependency</h4>
<p>Ok, let&#8217;s get started. First things first, you will need to add the following dependencies to your <code class="language-plaintext highlighter-rouge">pom.xml</code> to use PDFBox (assuming you are using Maven to build your project):</p>

<figure class="highlight"><pre><code class="language-xml" data-lang="xml">    <span class="nt">&lt;dependency&gt;</span>
      <span class="nt">&lt;groupId&gt;</span>org.apache.pdfbox<span class="nt">&lt;/groupId&gt;</span>
      <span class="nt">&lt;artifactId&gt;</span>pdfbox<span class="nt">&lt;/artifactId&gt;</span>
      <span class="nt">&lt;version&gt;</span>2.0.15<span class="nt">&lt;/version&gt;</span>
    <span class="nt">&lt;/dependency&gt;</span></code></pre></figure>

<h4 id="getting-started-with-the-code">Getting Started With the Code</h4>
<p>For this post, I decided to paste the source code for the whole function doing the PDF generation and separate it with a few sentences, explaining the most interesting parts of the code. You can always piece the code together, or just have a look at the code in <a href="https://github.com/mehmandarov/microprofile-qrcodes/blob/master/src/main/java/com/mehmandarov/qrcreator/document/PDFDocumentSupplier.java">my repo</a>. (<em>Bonus: If you are interested in how much work it was to port the code from iText to PDFBox, this <a href="https://github.com/mehmandarov/microprofile-qrcodes/commit/bf53ec918dbc0a2e64c1c830aa4995ef63cee52e">commit</a> should give you a rough idea.</em>)</p>

<p>We start with defining a document object, a page object and add a page to a document. Afterwards, we create a content stream object that will be added to the page and document objects. This object will be responsible for holding the text and images we will be generating here.</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="c1">// Assume that the following variables are declared and set:</span>
<span class="c1">//   QRCodeSupplier qrCodeSupplier - to generate QR codes, shown in the previous post</span>
<span class="c1">//   String id - a string that will be shown on the top of the PDF file and used in the QR code</span>
<span class="c1">//   String timeZone - a string containing current time zone</span>

<span class="nc">String</span> <span class="n">headerTitle</span> <span class="o">=</span> <span class="n">id</span><span class="o">;</span>
<span class="nc">PDFont</span> <span class="n">headerFont</span> <span class="o">=</span> <span class="nc">PDType1Font</span><span class="o">.</span><span class="na">COURIER_BOLD</span><span class="o">;</span>

<span class="kt">int</span> <span class="n">marginTop</span> <span class="o">=</span> <span class="mi">30</span><span class="o">;</span>
<span class="kt">int</span> <span class="n">fontSize</span> <span class="o">=</span> <span class="mi">30</span><span class="o">;</span>

<span class="nc">PDDocument</span> <span class="n">document</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PDDocument</span><span class="o">();</span>

<span class="nc">PDPage</span> <span class="n">page</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PDPage</span><span class="o">(</span><span class="nc">PDRectangle</span><span class="o">.</span><span class="na">A4</span><span class="o">);</span>
<span class="nc">PDRectangle</span> <span class="n">mediaBox</span> <span class="o">=</span> <span class="n">page</span><span class="o">.</span><span class="na">getMediaBox</span><span class="o">();</span>
<span class="n">document</span><span class="o">.</span><span class="na">addPage</span><span class="o">(</span><span class="n">page</span><span class="o">);</span>

<span class="nc">PDPageContentStream</span> <span class="n">contentStream</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PDPageContentStream</span><span class="o">(</span><span class="n">document</span><span class="o">,</span> 
                                                            <span class="n">page</span><span class="o">,</span> 
                                                            <span class="nc">PDPageContentStream</span><span class="o">.</span><span class="na">AppendMode</span><span class="o">.</span><span class="na">APPEND</span><span class="o">,</span> 
                                                            <span class="kc">true</span><span class="o">);</span></code></pre></figure>

<h4 id="adding-text">Adding Text</h4>
<p>Now we will need to calculate the coordinates for the header text string and make sure it will appear centered independent of the font and size. This is one of the &#8220;low-level&#8221; parts you will have to deal with when using PDFBox.</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="c1">// calculate coordinates to center the header text</span>
<span class="kt">float</span> <span class="n">titleWidth</span> <span class="o">=</span> <span class="n">headerFont</span><span class="o">.</span><span class="na">getStringWidth</span><span class="o">(</span><span class="n">headerTitle</span><span class="o">)</span> <span class="o">/</span> <span class="mi">1000</span> <span class="o">*</span> <span class="n">fontSize</span><span class="o">;</span>
<span class="kt">float</span> <span class="n">titleHeight</span> <span class="o">=</span> <span class="n">headerFont</span><span class="o">.</span><span class="na">getFontDescriptor</span><span class="o">().</span><span class="na">getFontBoundingBox</span><span class="o">().</span><span class="na">getHeight</span><span class="o">()</span> <span class="o">/</span> <span class="mi">1000</span> <span class="o">*</span> <span class="n">fontSize</span><span class="o">;</span>
<span class="kt">float</span> <span class="n">titleStartX</span> <span class="o">=</span> <span class="o">(</span><span class="n">mediaBox</span><span class="o">.</span><span class="na">getWidth</span><span class="o">()</span> <span class="o">-</span> <span class="n">titleWidth</span><span class="o">)</span> <span class="o">/</span> <span class="mi">2</span><span class="o">;</span>
<span class="kt">float</span> <span class="n">titleStartY</span> <span class="o">=</span> <span class="n">mediaBox</span><span class="o">.</span><span class="na">getHeight</span><span class="o">()</span> <span class="o">-</span> <span class="n">marginTop</span> <span class="o">-</span> <span class="n">titleHeight</span><span class="o">;</span></code></pre></figure>

<p>Next, we will be adding the text itself and setting font and coordinates for it on the page:</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="c1">// add header text to the document</span>
<span class="c1">// Note: This solution will not support fixed-width paragraphs and text flow</span>
<span class="n">contentStream</span><span class="o">.</span><span class="na">beginText</span><span class="o">();</span>
<span class="n">contentStream</span><span class="o">.</span><span class="na">setFont</span><span class="o">(</span><span class="n">headerFont</span><span class="o">,</span> <span class="n">fontSize</span><span class="o">);</span>
<span class="n">contentStream</span><span class="o">.</span><span class="na">newLineAtOffset</span><span class="o">(</span><span class="n">titleStartX</span><span class="o">,</span> <span class="n">titleStartY</span><span class="o">);</span>
<span class="n">contentStream</span><span class="o">.</span><span class="na">showText</span><span class="o">(</span><span class="n">headerTitle</span><span class="o">);</span>
<span class="n">contentStream</span><span class="o">.</span><span class="na">endText</span><span class="o">();</span></code></pre></figure>

<h4 id="adding-images">Adding Images</h4>
<p>Now, let&#8217;s examine how to add an image to a PDF document. Here, you can use <code class="language-plaintext highlighter-rouge">createFromFile()</code> in case your image is already available, or <code class="language-plaintext highlighter-rouge">createFromImage()</code> is you are generating the image on the fly and/or returning it from another function.</p>

<p>Below, you will also find examples of code to scale and to calculate coordinates for centering the image:</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="c1">// get image as a byte array</span>
<span class="nc">ByteArrayInputStream</span> <span class="n">bais</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ByteArrayInputStream</span><span class="o">(</span><span class="n">qrCodeSupplier</span><span class="o">.</span><span class="na">qrCodeGenerator</span><span class="o">(</span><span class="n">id</span><span class="o">));</span>
<span class="nc">BufferedImage</span> <span class="n">bim</span> <span class="o">=</span> <span class="nc">ImageIO</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">bais</span><span class="o">);</span>

<span class="c1">// convert image to an object that can be added to the PDF document</span>
<span class="nc">PDImageXObject</span> <span class="n">pdImage</span> <span class="o">=</span> <span class="nc">LosslessFactory</span><span class="o">.</span><span class="na">createFromImage</span><span class="o">(</span><span class="n">document</span><span class="o">,</span> <span class="n">bim</span><span class="o">);</span>

<span class="c1">// calculate coordinates to center the image</span>
<span class="kt">float</span> <span class="n">scale</span> <span class="o">=</span> <span class="mi">1</span><span class="n">f</span><span class="o">;</span>
<span class="kt">int</span> <span class="n">imageOffset</span> <span class="o">=</span> <span class="mi">100</span><span class="o">;</span>

<span class="kt">float</span> <span class="n">imageWidth</span> <span class="o">=</span> <span class="n">pdImage</span><span class="o">.</span><span class="na">getWidth</span><span class="o">()</span> <span class="o">*</span> <span class="n">scale</span><span class="o">;</span>
<span class="kt">float</span> <span class="n">imageHeight</span> <span class="o">=</span> <span class="n">pdImage</span><span class="o">.</span><span class="na">getHeight</span><span class="o">()</span> <span class="o">*</span> <span class="n">scale</span><span class="o">;</span>
<span class="kt">float</span> <span class="n">imageStartX</span> <span class="o">=</span> <span class="o">(</span><span class="n">mediaBox</span><span class="o">.</span><span class="na">getWidth</span><span class="o">()</span> <span class="o">-</span> <span class="n">imageWidth</span><span class="o">)</span> <span class="o">/</span> <span class="mi">2</span><span class="o">;</span>
<span class="kt">float</span> <span class="n">imageStartY</span> <span class="o">=</span> <span class="n">titleStartY</span> <span class="o">-</span> <span class="n">imageHeight</span> <span class="o">-</span> <span class="n">imageOffset</span><span class="o">;</span>

<span class="c1">// add image into the document</span>
<span class="n">contentStream</span><span class="o">.</span><span class="na">drawImage</span><span class="o">(</span><span class="n">pdImage</span><span class="o">,</span> <span class="n">imageStartX</span><span class="o">,</span> <span class="n">imageStartY</span><span class="o">,</span> 
                        <span class="n">pdImage</span><span class="o">.</span><span class="na">getWidth</span><span class="o">()</span> <span class="o">*</span> <span class="n">scale</span><span class="o">,</span> <span class="n">pdImage</span><span class="o">.</span><span class="na">getHeight</span><span class="o">()</span> <span class="o">*</span> <span class="n">scale</span><span class="o">);</span>

<span class="c1">// closing the stream</span>
<span class="n">contentStream</span><span class="o">.</span><span class="na">close</span><span class="o">();</span></code></pre></figure>

<h4 id="adding-metadata-to-the-document">Adding Metadata to the Document</h4>
<p>Here, you can see a few examples of how you can add metadata to your document. This is done with a help of <a href="https://pdfbox.apache.org/docs/2.0.13/javadocs/org/apache/pdfbox/pdmodel/PDDocumentInformation.html">a few methods available thought the API</a>:</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="c1">// add metadata</span>
<span class="n">document</span><span class="o">.</span><span class="na">getDocumentInformation</span><span class="o">().</span><span class="na">setTitle</span><span class="o">(</span><span class="s">"Generated QR code for "</span> <span class="o">+</span> <span class="n">id</span> <span class="o">+</span> <span class="s">"."</span><span class="o">);</span>
<span class="n">document</span><span class="o">.</span><span class="na">getDocumentInformation</span><span class="o">().</span><span class="na">setSubject</span><span class="o">(</span><span class="s">"with a secure string"</span><span class="o">);</span>
<span class="n">document</span><span class="o">.</span><span class="na">getDocumentInformation</span><span class="o">().</span><span class="na">setAuthor</span><span class="o">(</span><span class="s">"rm"</span><span class="o">);</span>
<span class="n">document</span><span class="o">.</span><span class="na">getDocumentInformation</span><span class="o">().</span><span class="na">setCreator</span><span class="o">(</span><span class="s">"rm"</span><span class="o">);</span>
<span class="n">document</span><span class="o">.</span><span class="na">getDocumentInformation</span><span class="o">().</span><span class="na">setCreationDate</span><span class="o">(</span><span class="n">date</span><span class="o">);</span></code></pre></figure>

<h4 id="closing-saving-and-returning-the-document">Closing, Saving and Returning the Document</h4>
<p>After your document is built and you have added all the contents, remember to save and close your document. Now you can either save the document to the file with <a href="https://pdfbox.apache.org/1.8/cookbook/documentcreation.html"><code class="language-plaintext highlighter-rouge">document.save()</code></a>, or returning the document as a byte array to another function with <code class="language-plaintext highlighter-rouge">byteArrayOutputStream.toByteArray()</code>:</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="c1">// save and close document</span>
<span class="nc">ByteArrayOutputStream</span> <span class="n">byteArrayOutputStream</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ByteArrayOutputStream</span><span class="o">();</span>
<span class="n">document</span><span class="o">.</span><span class="na">save</span><span class="o">(</span><span class="n">byteArrayOutputStream</span><span class="o">);</span>
<span class="n">document</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>

<span class="c1">// return document as byte[]</span>
<span class="k">return</span> <span class="n">byteArrayOutputStream</span><span class="o">.</span><span class="na">toByteArray</span><span class="o">();</span></code></pre></figure>

<h2 id="what-now">What Now?</h2>

<p>In the last two posts, we have seen how to generate QR codes with a hashed string and PDF files with Java. In the next post, I will be showing how to put it all together into a <a href="https://tldrlegal.com/license/gnu-affero-general-public-license-v3-(agpl-3.0)">MicroProfile</a> microservice.</p>

<hr />]]></content><author><name>Rustam Mehmandarov</name></author><summary type="html">A step-by-step tutorial on generating PDF files in Java with text, images, and metadata.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mehmandarov.com/assets/images/posts-images/pages.jpg"/><category term="blog"/><category term="java"/><category term="pdf"/><category term="english"/></entry></feed>
