Skip to content

Instantly share code, notes, and snippets.

@JoelGeraci-Datalogics
Created August 5, 2015 20:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoelGeraci-Datalogics/586a5252f996cdac19a9 to your computer and use it in GitHub Desktop.
Save JoelGeraci-Datalogics/586a5252f996cdac19a9 to your computer and use it in GitHub Desktop.
This sample shows how to add content and annotations to a layer on a PDF page. It also shows how to attach JavaScript to the PDF file at the document level.
/*
* Copyright Datalogics, Inc. 2015
*/
package pdfjt.cookbook.document;
import com.adobe.internal.io.ByteReader;
import com.adobe.internal.io.ByteWriter;
import com.adobe.internal.io.InputStreamByteReader;
import com.adobe.pdfjt.core.types.ASString;
import com.adobe.pdfjt.pdf.document.PDFDocument;
import com.adobe.pdfjt.pdf.document.PDFNameDictionary;
import com.adobe.pdfjt.pdf.document.PDFOpenOptions;
import com.adobe.pdfjt.pdf.document.PDFSaveFullOptions;
import com.adobe.pdfjt.pdf.document.PDFText;
import com.adobe.pdfjt.pdf.graphics.optionalcontent.PDFOCConfig;
import com.adobe.pdfjt.pdf.graphics.optionalcontent.PDFOCGroup;
import com.adobe.pdfjt.pdf.graphics.optionalcontent.PDFOCGroupArray;
import com.adobe.pdfjt.pdf.graphics.optionalcontent.PDFOCObject;
import com.adobe.pdfjt.pdf.graphics.optionalcontent.PDFOCOrderList;
import com.adobe.pdfjt.pdf.graphics.optionalcontent.PDFOCProperties;
import com.adobe.pdfjt.pdf.graphics.xobject.PDFXObjectForm;
import com.adobe.pdfjt.pdf.interactive.action.PDFActionJavaScript;
import com.adobe.pdfjt.pdf.interactive.action.PDFActionURI;
import com.adobe.pdfjt.pdf.interactive.annotation.PDFAnnotationLink;
import com.adobe.pdfjt.pdf.interactive.annotation.PDFBorder;
import com.adobe.pdfjt.pdf.interactive.forms.PDFNamedJavaScripts;
import com.adobe.pdfjt.pdf.page.PDFPage;
import com.adobe.pdfjt.pdf.page.PDFPageMode;
import com.adobe.pdfjt.services.imageconversion.ImageManager;
import com.adobe.pdfjt.services.xobjhandler.XObjectApplyOptions;
import com.adobe.pdfjt.services.xobjhandler.XObjectUseOptions;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.net.URL;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import pdfjt.util.SampleFileServices;
/**
* This sample shows how to add content and annotations to a layer on a PDF
* page. It also shows how to attach JavaScript to the PDF file at the document
* level.
*/
public class AddAnnotationsToLayers {
private static final String inputPDF = "http://dev.datalogics.com/cookbook/document/BlankInput.pdf";
private static final String getReader = "http://dev.datalogics.com/cookbook/images/Get_Adobe_Acrobat_Reader_DC_web_button_158x39.fw.png";
private static final String outputDir = "cookbook/Document/output/";
static public void main(String[] args) throws Exception {
try {
/*
* First read in the two input files. The first one is the document
* that we are going to add the link to. The second is an image file
* we are going to use as the link graphic.
*/
InputStream fis = new URL(inputPDF).openStream();
ByteReader byteReader = new InputStreamByteReader(fis);
PDFDocument pdfDocument = PDFDocument.newInstance(byteReader, PDFOpenOptions.newInstance());
// Read in the image file as a BufferedImage.
ImageReader reader = null;
Iterator<ImageReader> imageReaders = ImageIO.getImageReadersByFormatName("PNG");
if (imageReaders.hasNext()) {
reader = imageReaders.next();
} else {
System.out.println("A PNG Reader isn't available");
System.exit(0);
}
reader.setInput(ImageIO.createImageInputStream(new URL(getReader).openStream()));
BufferedImage bufferedImage = reader.read(0);
/*
* Use the ImageManager class to convert the BufferedImage into a
* PDF XObject that can be added to the page and associated with a
* layer.
*/
PDFXObjectForm linkImage = ImageManager.getXObjPDFImage(pdfDocument, bufferedImage, new XObjectUseOptions());
/*
* Optional Content Groups (OCGs) are referred to as "Layers" in the
* Adobe Reader and Acrobat UI. We begin setting up the layer by
* creating a new named Optional Content Group and adding it to the
* Optional Content Properties object.
*/
PDFOCGroup pdfOCGetReader = PDFOCGroup.newInstance(pdfDocument, "Get Reader");
PDFOCProperties pdfOCProperties = PDFOCProperties.newInstance(pdfDocument);
pdfOCProperties.addOCG(pdfOCGetReader);
/*
* Then we need to set the default state of the Optional Content
* Group that we just created. To set the default state to be
* visible, we need to add it to the "OnList" in the default
* configuration dictionary. There is also an "OFFList" which we
* don't use in this sample.
*/
PDFOCConfig pdfOCConfig = PDFOCConfig.newInstance(pdfDocument);
PDFOCGroupArray pdfOCGroupArrayOn = PDFOCGroupArray.newInstance(pdfDocument);
pdfOCGroupArrayOn.add(pdfOCGetReader);
pdfOCConfig.setONList(pdfOCGroupArrayOn);
/*
* We also need to set the order that the layers will appear when
* visible. This controls the rendering order.
*/
PDFOCOrderList pdfOCOrderList = PDFOCOrderList.newInstance(pdfDocument);
pdfOCOrderList.addOCG(pdfOCGetReader);
pdfOCConfig.setOrderList(pdfOCOrderList);
pdfOCProperties.setDefaultOCConfigDict(pdfOCConfig);
/*
* Finally, we add all that to the PDF Catalog. We now have a layer
* that is configured to display properly in the Adobe Reader or
* Acrobat UI...
*/
pdfDocument.requireCatalog().setOCProperties(pdfOCProperties);
/*
* ... which means we can now safely associate the linkImage XObject
* with the layer.
*/
PDFOCObject.setOC(linkImage, pdfOCGetReader);
/*
* Then we add the image to the page about an inch from the upper
* left corner of the page and below the header.
*/
XObjectApplyOptions applyOptions = new XObjectApplyOptions();
PDFPage pageOne = pdfDocument.requirePages().getPage(0);
double llx = 72;
double lly = pageOne.getCropBox().height() - linkImage.getBBox().height() - 72 - 36;
applyOptions.setPosition(llx, lly);
applyOptions.applyXObjectForm(pageOne, linkImage, new XObjectUseOptions());
/*
* Now we add a link annotation that sits on top of the image
* exactly.
*/
PDFAnnotationLink pdfAnnotationLink = PDFAnnotationLink.newInstance(pdfDocument);
// Set the visual properties
PDFBorder pdfBorder = PDFBorder.newInstance(pdfDocument);
pdfBorder.setWidth(0);
pdfAnnotationLink.setBorder(pdfBorder);
// Set the link action
PDFActionURI pdfActionURI = PDFActionURI.newInstance(pdfDocument);
pdfActionURI.setURI(new ASString("http://www.adobe.com/go/reader"));
pdfAnnotationLink.setAction(pdfActionURI);
// Associate the link with the layer
PDFOCObject.setOC(pdfAnnotationLink, pdfOCGetReader);
// Position the link
double urx = 72 + linkImage.getBBox().width();
double ury = pageOne.getCropBox().height() - 72 - 36;
pdfAnnotationLink.setRect(llx, lly, urx, ury);
// Finally, add the link to the page
pageOne.addAnnotation(pdfAnnotationLink);
/*
* Now set the document up to show the Layers panel when opened in
* Adobe Reader or Acrobat.
*/
pdfDocument.requireCatalog().setPageMode(PDFPageMode.WithLayers);
/*
* At this point we've accomplished the goal of this sample. You now
* have two objects in the PDF file associated with a new layer; the
* image XObject and a link annotation. They can now be toggled on
* and off through the Adobe Reader and Acrobat UI.
*
* In this section we're going to add some JavaScript to the
* document that will run when it is first opened if the viewer is
* capable of running JavaScript. If the JavaScript detects a plugin
* that is unique to Adobe viewers, the layer state is set to false
* (hidden). If the JavaScript doesn't find the plugin or can't run,
* the layer remains visible.
*
* The script will hide both the graphic and the link by hiding the
* layer if the user is viewing the file in an Adobe tool but both
* will remain visible if not, prompting them to download it by
* clicking on the link.
*
* We begin by creating a JavaScript Action from a string of
* JavaScript code.
*/
String javascriptCode =
"function getLayerByName(name) \n"
+ "{ \n"
+ " var ocgArray = this.getOCGs(); \n"
+ " layer = null; \n"
+ " for (var i=0; i < ocgArray.length; i++) { \n"
+ " if (ocgArray[i].name == name) { \n"
+ " layer = ocgArray[i];\n"
+ " break;\n"
+ " } \n"
+ " } \n"
+ " return layer; \n"
+ "}\n"
+ "plugIns = app.plugIns;\n"
+ "for (var i=0; i < plugIns.length; i++) {\n"
+ " if (plugIns[i].name == 'ADBE_Search') {\n"
+ " getLayerByName('Get Reader').state = false;\n"
+ " break;\n"
+ " }\n"
+ "}";
PDFActionJavaScript adobeViewerTest = PDFActionJavaScript.newInstance(pdfDocument,
PDFText.newInstance(pdfDocument, javascriptCode));
/*
* Get the Names dictionary from the PDF file if it exists or create
* a new one if it doesn't. This is where the document level
* JavaScripts are stored. Using "procureNameDictionary()" and
* procureNamedJavaScripts() then appending to them rather than
* creating new Objects and using "set" prevents what may be in the
* file already from being overwritten.
*/
PDFNameDictionary pdfNameDictionary = pdfDocument.requireCatalog().procureNameDictionary();
PDFNamedJavaScripts pdfNamedJavaScripts = pdfNameDictionary.procureNamedJavaScripts();
pdfNamedJavaScripts.addEntry(new ASString("!Datalogics::AdobeViewerTest"), adobeViewerTest);
pdfNameDictionary.setNamedJavaScripts(pdfNamedJavaScripts);
// Save the file.
SampleFileServices.createDir(outputDir);
ByteWriter outputFile = SampleFileServices.getRAFByteWriter(outputDir + "LayeredDocument.pdf");
pdfDocument.save(outputFile, PDFSaveFullOptions.newInstance());
} finally {
//
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment