Hello, World

Hello, World

Joel GeraciThe words “Hello” and “World”, in that order, are iconic to anyone with any computer programming experience. It’s become the traditional first program that many people learn. It’s also my message to you.

Hello, World – My name is Joel Geraci and I’m the new PDF Developer Evangelist at Datalogics.

In Outliers, Malcolm Gladwell describes the “10,000-Hour Rule”; it takes a total of around 10,000 hours to become an expert in any field. I joined Adobe in 1993 with the introduction of Acrobat 1.0 and have been working with PDF ever since. With my departure from Adobe, I now bring over 30,000 hours of experience to Datalogics and, hopefully, help with your learning more about our company and the products and tools we offer.

That said, today isn’t my first day here. Over the last couple of months I’ve been busy working with a team of engineers, product managers and technical writers to bring you the Adobe PDF Java Toolkit Version 2.0. The PDF Java Toolkit is core technology from Adobe that powers much of its server-based PDF functionality and is available, exclusively through Datalogics, in an unbundled form from the Adobe products or services so that you can use this power to build your own applications.

If you’re familiar with the PDF Java Toolkit Version 1.0, you’re in for a real surprise. New in version 2.0 is a single .jar file making it easier to integrate, new sample files, additional documentation and some serious improvements to the classes.

If you’re new to the PDF Java Toolkit, you can read all about it here.

The PDF Java Toolkit Version 2.0 is just the beginning though. We plan on creating more samples and more building blocks that demonstrate best practices to make it easier to build your PDF applications… starting now… so… in the spirit of the more traditional meaning of “Hello World”…

Hello, World

In this and future blog posts, I’ll be providing additional samples to demonstrate some of the more interesting or esoteric capabilities of the toolkit. The “HelloWorldBasic” example is the first of these. As background, the Toolkit comes with its own “Hello World” sample that leverages the “RichTextContentGenerator“ class, a lightweight layout engine that accepts XHTML Elements as rich text strings, formats them, and draws them on the page all while handling word wrap and font styling automatically. This method of putting text on a page essentially mimics the process of using Acrobat to add a Text Box comment, applying some styles to the text and then flattening the document. The “RichTextContentGenerator“ is the best way to put a block of text on a page if you don’t already know precisely where each word or line of text is going to be positioned. However, the “RichTextContentGenerator“ doesn’t draw graphics or position images… which can be a problem if you are expecting to do those sorts of things.

If you do know exactly where the text, vector art and images are going to go on your page, you’re better off creating a content stream. A content stream is a PDF stream object whose data consists of a sequence of “instructions” describing the graphical elements to be painted on the page. The “ContentWriter” class in conjunction with the “InstructionFactory” class is used to do this

HelloWorldRunning the “HelloWorldBasic” sample results in a single 8.5 x 11 inch PDF page with the words… big shocker here… “Hello World” in 36 point, Helvetica-Bold an inch from the top and left of the page. After running the sample, you should get something that looks like this.

The sample takes you through the process of:

  1. Creating a new, single page PDF document.
  2. Acquiring the first page so it can be drawn on.
  3. Creating a set of resources for the drawing operators to use. In this case the font named Helvetica-Bold
  4. Creating and writing a content stream with instructions to position and show “Hello World” on the page.
  5. Overwriting the previously empty page content stream with the new content stream.
  6. Writing the PDF file to disk.

The “HelloWorldBasic” sample requires utilities that are in the samples that come with the PDF Java Toolkit Version 2.0 so you’ll want to have installed that first. You can put this sample in the src folder of the samples project.

I’ll have a more advanced version of Hello World that shows how to create outlined text, filled and outlined text and drawing instructions in a couple of weeks so stay tuned. Please feel free to provide feedback and suggestions about this sample and propose some new sample requests.

Download HelloWorldBasic.java or copy the code below.

/*
 *  ****************************************************************************
 *
 *  Copyright 2009-2012 Adobe Systems Incorporated. All Rights Reserved.
 *  Portions Copyright 2012 Datalogics Incorporated.
 *
 *  NOTICE: Datalogics and Adobe permit you to use, modify, and distribute
 *  this file in accordance with the terms of the license agreement
 *  accompanying it. If you have received this file from a source other
 *  than Adobe or Datalogics, then your use, modification, or distribution of it
 *  requires the prior written permission of Adobe or Datalogics.
 *
 * ***************************************************************************
 */

import java.awt.Color;
import java.io.File;
import java.io.RandomAccessFile;

import com.adobe.internal.io.ByteWriter;
import com.adobe.internal.io.RandomAccessFileByteWriter;
import com.adobe.pdfjt.core.exceptions.PDFException;
import com.adobe.pdfjt.core.types.ASName;
import com.adobe.pdfjt.core.types.ASRectangle;
import com.adobe.pdfjt.core.types.ASString;
import com.adobe.pdfjt.pdf.content.Content;
import com.adobe.pdfjt.pdf.content.InstructionFactory;
import com.adobe.pdfjt.pdf.contentmodify.ContentWriter;
import com.adobe.pdfjt.pdf.contentmodify.ModifiableContent;
import com.adobe.pdfjt.pdf.document.PDFContents;
import com.adobe.pdfjt.pdf.document.PDFDocument;
import com.adobe.pdfjt.pdf.document.PDFOpenOptions;
import com.adobe.pdfjt.pdf.document.PDFResources;
import com.adobe.pdfjt.pdf.document.PDFSaveFullOptions;
import com.adobe.pdfjt.pdf.graphics.font.PDFFont;
import com.adobe.pdfjt.pdf.graphics.font.PDFFontMap;
import com.adobe.pdfjt.pdf.graphics.font.PDFFontSimple;
import com.adobe.pdfjt.pdf.page.PDFPage;
import com.adobe.pdfjt.pdf.page.PDFPageTree;

/*
 * Demonstrates creating a new document and placing text.
 * Comments in this sample refer to the ISO 32000:2008 PDF Reference located at
 * http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/PDF32000_2008.pdf
 *
 * Output file is written to "output/HelloWorldBasic.pdf"
 */

public class HelloWorldBasic {

	private static final double inches = 72;
	private static final double pageHeight = 11*inches;
	private static final double pageWidth = 8.5*inches;
	static final String filePath = "output/HelloWorldBasic.pdf";

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		try{
			/*
			 * Create an ASRectangle object to initialize an empty PDF. This one
			 * will create a page that is 8.5 x 11 inches based on the constants
			 * above. Then use that rectangle to generate a new document with a
			 * single blank page.
			 */
			double[] rectangle = {0, 0, pageWidth, pageHeight};
			ASRectangle rect = new ASRectangle(rectangle);
			PDFDocument pdfDocument = PDFDocument.newInstance(rect, PDFOpenOptions.newInstance());
			
			/*
			 * The pages of a document are accessed through a structure known as
			 * the page tree, which defines the ordering of pages in the
			 * document.
			 * 
			 * Get the pageTree, then the first page. Pages numbers
			 * are zero based.
			 * 
			 * See section 7.7.3 of the PDF specification, "Page Tree"
			 */
			
			PDFPageTree pdfPageTree = pdfDocument.requirePages();
			PDFPage pdfPage = pdfPageTree.getPage(0);
			
			/*
			 * A content stream (PDFContents) is a PDF stream object whose data consists of a
			 * sequence of "instructions" describing the graphical elements to be
			 * painted on a page. 
			 * 
			 * This will be used by the "ContentWriter" below.
			 * 
			 * See section 7.7.3 of the PDF specification, "Content Streams"
			 */
			
			PDFContents pdfContents = PDFContents.newInstance(pdfPage.getPDFDocument());
			
			/*
			 * A content stream’s named resources (PDFResources) are defined by
			 * a resource dictionary, which enumerates the named resources
			 * needed by the operators in the content stream and the names by
			 * which they can be referred to.
			 * 
			 * EXAMPLE: If a text operator appearing within the content stream
			 * needs a certain font, the content stream’s resource dictionary
			 * can associate the name "F42" with the corresponding font
			 * dictionary. The text operator can then use this name to refer to
			 * the font.
			 * 
			 * This will be used by the "ContentWriter" below.
			 * 
			 * See section 7.8.3 of the PDF specification,
			 * "Resource Dictionaries"
			 * 
			 * For the simplicity I'll use one of the standard 14 fonts. These
			 * fonts, or their font metrics and suitable substitution fonts, are
			 * available to all conforming PDF readers. Any of the standard 14
			 * fonts can be used without the need for font embedding.
			 * 
			 * The standard 14 fonts are as follows:
			 * 
			 * Helvetica
			 * Helvetica-Oblique
			 * Helvetica-Bold
			 * Helvetica-BoldOblique
			 * Courier
			 * Courier-Oblique
			 * Courier-Bold
			 * Courier-BoldOblique
			 * Times-Roman
			 * Times-Italic
			 * Times-Bold
			 * Times-BoldItalic Symbol
			 * ZapfDingbats
			 */
			
			PDFResources pdfResources = PDFResources.newInstance(pdfPage.getPDFDocument());	       
			PDFFont helveticaBold = PDFFontSimple.newInstance(pdfPage.getPDFDocument(), ASName.k_Helvetica_Bold, ASName.k_Type1);
			PDFFontMap pdfFontMap = PDFFontMap.newInstance(pdfPage.getPDFDocument());
			pdfFontMap.set(ASName.create("Helvetica-Bold"), helveticaBold);	
			pdfResources.setFontMap(pdfFontMap);
			pdfResources.setProcSetList(new ASName[] {ASName.k_PDF,ASName.k_Text});
			pdfPage.setResources(pdfResources);
				
			/*
			 * InstructionFactory is used to create new page content "Instructions".
			 * ContentWriter adds Instructions to PDF content.
			 */
				
			ContentWriter contentWriter = ContentWriter.newInstance(ModifiableContent.newInstance(pdfContents,pdfResources));
			contentWriter.write(InstructionFactory.newBeginText()); //A new text block
			contentWriter.write(InstructionFactory.newColorFill(getColorComponents(Color.black))); // Define the color of the text. "getColorComponents" allows us to specify the color by name
			contentWriter.write(InstructionFactory.newTextFont(ASName.create("Helvetica-Bold"), 36)); // Set the font and size in points.
			contentWriter.write(InstructionFactory.newTextPosition(72, pageHeight-72-36)); //One inch from the top minus the height of the text.
			contentWriter.write(InstructionFactory.newShowText(new ASString("Hello World"))); // Here it is! The text to show.		
			contentWriter.write(InstructionFactory.newEndText()); // End the text block
			
			/*
			 * "Content" represents both a content stream and the resources reference by
			 * the content stream.
			 */
			
			Content content = contentWriter.close();
			
			/*
			 * "setContents" overwrites the page content stream with the stream passed here.
			 */
			
			pdfPage.setContents(content.getContents());
					
			/*
			 * ByteWriter does not overwrite existing files, so we want to make
			 * sure this file doesn't already exist beforehand by deleting anything
			 * that is currently at our output path. Be careful when doing this.
			 */
			File file = new File(filePath); 
			file.mkdirs();
			if (file.exists()) {
				file.delete();
			}
			/*
			 * Save everything we've done to the original PDF file
			 */
			RandomAccessFile outputPDFFile = new RandomAccessFile(filePath, "rw");
			ByteWriter outputWriter = new RandomAccessFileByteWriter(outputPDFFile);	
			pdfDocument.saveAndClose(outputWriter, PDFSaveFullOptions.newInstance());
			System.out.println("Done!");

			try { 
				if (pdfDocument != null) {
					pdfDocument.close();
				}
			} catch (PDFException e) {
				e.printStackTrace();
			}

			if (outputWriter != null) {
				outputWriter.close();
			}
		} catch (Exception e){
			e.printStackTrace();
		}
	}
	
	private static double[] getColorComponents(Color color) {
		float[] colorComponents = color.getColorComponents(null);
		double[] colorComponentsDouble = new double[colorComponents.length];
		for (int i = 0; i < colorComponents.length; i++) {
			colorComponentsDouble[i] = colorComponents[i];
		}
		return colorComponentsDouble;
	}	
}

3 thoughts on “Hello, World

Leave a Reply

Your email address will not be published. Required fields are marked *