PDFbox is a fairly robust library for manipulating and extracting information from PDF documents. This is a short and simple Java class that utilizes PDFbox to stamp/watermark a PDF from the command line, be it a bash script or exec call. There isn’t much in the way of existing user documentation for this type of action using PDFbox, so I’m leaving it here in case it’s useful.
/*
* A Simple PDF Stamper using PDFBox
*
* July, 2013
* @author jack
*/
package simplestamper;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import org.apache.pdfbox.Overlay;
import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.exceptions.CryptographyException;
import org.apache.pdfbox.exceptions.InvalidPasswordException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
/**
*
* @author jack
*/
public class SimpleStamper {
private static PDDocument pdf;
private static int numPages;
private static Properties config = new Properties();
private static String configFile;
private static String outputFN = null;
private static String inputFN = null;
private static String stampString = null;
private static String fontFamily = null;
private static Float fontSize = null;
/**
* The main class. It's what plants crave.
*
* @param args the command line arguments
* args[0] -> The config file
* args[1] -> The PDF document to be stamped
* args[2] -> The string to stamp (optional, falls back to ss.text in the config file)
* args[3] -> The desired name and path of the stamped PDF (optional, also defined in config file as ss.outputFN)
*/
public static void main(String[] args) throws IOException, CryptographyException, COSVisitorException {
// if we don't have any parameters...
if (args.length == 0) {
usage();
} else {
// the config file path
configFile = args[0];
// load the config
loadConfig(configFile, args);
}
// stamp
stampPdf();
}
/**
* Loads the configuration data.
*
* @param configFile
*/
public static void loadConfig(String configFile, String[] args) {
// the input stream
InputStream is;
try {
is = new FileInputStream(configFile);
try {
// try to load the config file
config.load(is);
is.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// the input PDF filename/path
if (args.length > 1) {
inputFN = args[1];
}else{
inputFN = config.getProperty("ss.inputFN");
}
// the string to be stamped
if(args.length > 2){
stampString = args[2];
}else{
stampString = config.getProperty("ss.text");
}
// the output PDF filename/path
if(args.length > 3){
outputFN = args[3];
}else{
outputFN = config.getProperty("ss.outputFN");
}
// make sure we have a font from the prop file
try{
fontFamily = config.getProperty("ss.fontFamily");
} catch (NullPointerException e){
System.err.println("You must specify a font in the properties file.");
}
// make sure we have a font size from the prop file
try{
fontSize = Float.parseFloat(config.getProperty("ss.fontSize"));
} catch (NullPointerException e){
System.err.println("You must specify a font size in the properties file.");
}
}
/**
* Coordinate the stamping procedure.
*
*/
public static void stampPdf() throws IOException, CryptographyException, COSVisitorException {
// load the PDF to be stamped
pdf = PDDocument.load(inputFN);
if (pdf.isEncrypted()) {
try {
// try to open the encrypted PDF
pdf.decrypt("");
} catch (InvalidPasswordException e) {
// This error message dictates that the document is encrypted and we have no password
System.err.println("The document is encrypted or otherwise has prohibitive security settings..");
System.exit(1);
}
}
// create the overlay page with the text to be stamped
PDDocument overlayDoc = createOverlay(stampString);
// update the number of pages we have in the incoming pdf
numPages = pdf.getPageCount();
// do the overlay
doOverlay(pdf, overlayDoc);
// close
pdf.close();
overlayDoc.close();
}
/**
* Creates the overlay PDF.
*
* @param text
* @return PDDocument
* @throws IOException
* @throws COSVisitorException
*/
public static PDDocument createOverlay(String text) throws IOException, COSVisitorException {
// Create a document and add a page to it
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
// the x/y coords
Float xVal = Float.parseFloat(config.getProperty("ss.xVal"));
Float yVal = Float.parseFloat(config.getProperty("ss.yVal"));
// Create a new font object selecting one of the PDF base fonts
PDFont font = PDType1Font.getStandardFont(fontFamily);
// Start a new content stream which will "hold" the to be created content
PDPageContentStream contentStream = new PDPageContentStream(document, page);
// Create the text and position it
contentStream.beginText();
contentStream.setFont(font, fontSize);
contentStream.moveTextPositionByAmount(xVal, yVal);
contentStream.drawString(text);
contentStream.endText();
// Make sure that the content stream is closed:
contentStream.close();
//return the string doc
return document;
}
/**
* Performs the overlay of the two documents.
*
* @param basePDF
* @param overlayDoc
* @throws IOException
* @throws COSVisitorException
*/
private static void doOverlay(PDDocument basePDF, PDDocument overlayDoc) throws IOException, COSVisitorException {
PDDocumentCatalog docCatalog = basePDF.getDocumentCatalog();
// get the pages of the pdf
List pages = docCatalog.getAllPages();
Iterator pageIter = pages.iterator();
while (pageIter.hasNext()) {
PDPage page = (PDPage) pageIter.next();
}
Overlay overlay = new Overlay();
PDDocument output = overlay.overlay(overlayDoc, basePDF);
//overlay.overlay(overlayDoc, basePDF);
// save the new doc to the file name specified in the config
output.save(outputFN);
// close, close
overlayDoc.close();
basePDF.close();
}
/**
* Attempts to provide basic usage information.
*/
private static void usage() {
System.err.println("usage: java -jar simplestamper \"PdfToStamp.pdf\" \"The text to stamp, in parenthesis.\" \"Output.pdf\"");
}
}