package org.alfresco.extension.pdftoolkit.service;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.imageio.ImageIO;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.extension.pdftoolkit.constants.PDFToolkitConstants;
import org.alfresco.extension.pdftoolkit.model.PDFToolkitModel;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.template.FreeMarkerProcessor;
import org.alfresco.repo.template.TemplateNode;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.util.PDFMergerUtility;
import org.apache.pdfbox.util.Splitter;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfDictionary;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfNumber;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfWriter;
public class PDFToolkitServiceImpl extends PDFToolkitConstants implements PDFToolkitService
{
private ServiceRegistry serviceRegistry;
private NodeService ns;
private ContentService cs;
private FileFolderService ffs;
private DictionaryService ds;
private PersonService ps;
private AuthenticationService as;
private FreeMarkerProcessor freemarkerProcessor = new FreeMarkerProcessor();
private static Log logger = LogFactory.getLog(PDFToolkitServiceImpl.class);
// do we need to apply the encryption aspect when we encrypt?
private boolean useEncryptionAspect = true;
// do we need to apply the signature aspect when we sign?
private boolean useSignatureAspect = true;
// when we create a new document, do we actually create a new one, or copy the source?
private boolean createNew = false;
@Override
public NodeRef appendPDF(NodeRef targetNodeRef, Map<String, Serializable> params)
{
PDDocument pdf = null;
PDDocument pdfTarget = null;
InputStream is = null;
InputStream tis = null;
File tempDir = null;
ContentWriter writer = null;
NodeRef destinationNode = null;
try
{
NodeRef toAppend = (NodeRef)params.get(PARAM_TARGET_NODE);
Boolean inplace = Boolean.valueOf(String.valueOf(params.get(PARAM_INPLACE)));
ContentReader append = getReader(toAppend);
is = append.getContentInputStream();
ContentReader targetReader = getReader(targetNodeRef);
tis = targetReader.getContentInputStream();
String fileName = getFilename(params, targetNodeRef);
// stream the document in
pdf = PDDocument.load(is);
pdfTarget = PDDocument.load(tis);
// Append the PDFs
PDFMergerUtility merger = new PDFMergerUtility();
merger.appendDocument(pdfTarget, pdf);
merger.setDestinationFileName(fileName);
merger.mergeDocuments();
// build a temp dir name based on the ID of the noderef we are
// importing
File alfTempDir = TempFileProvider.getTempDir();
tempDir = new File(alfTempDir.getPath() + File.separatorChar + targetNodeRef.getId());
tempDir.mkdir();
pdfTarget.save(tempDir + "" + File.separatorChar + fileName);
for (File file : tempDir.listFiles())
{
try
{
if (file.isFile())
{
// Get a writer and prep it for putting it back into the repo
destinationNode = createDestinationNode(fileName,
(NodeRef)params.get(PARAM_DESTINATION_FOLDER), targetNodeRef, inplace);
writer = cs.getWriter(destinationNode, ContentModel.PROP_CONTENT, true);
writer.setEncoding(targetReader.getEncoding()); // original
// encoding
writer.setMimetype(FILE_MIMETYPE);
// Put it in the repo
writer.putContent(file);
// Clean up
file.delete();
}
}
catch (FileExistsException e)
{
throw new AlfrescoRuntimeException("Failed to process file.", e);
}
}
}
catch (COSVisitorException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
finally
{
if (pdf != null)
{
try
{
pdf.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
if (pdfTarget != null)
{
try
{
pdfTarget.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
if (is != null)
{
try
{
is.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
if (tempDir != null)
{
tempDir.delete();
}
}
return destinationNode;
}
@Override
public NodeRef encryptPDF(NodeRef targetNodeRef, Map<String, Serializable> params)
{
PdfStamper stamp = null;
File tempDir = null;
ContentWriter writer = null;
ContentReader targetReader = null;
NodeRef destinationNode = null;
try
{
// get the parameters
String userPassword = (String)params.get(PARAM_USER_PASSWORD);
String ownerPassword = (String)params.get(PARAM_OWNER_PASSWORD);
Boolean inplace = Boolean.valueOf(String.valueOf(params.get(PARAM_INPLACE)));
int permissions = buildPermissionMask(params);
int encryptionType = Integer.parseInt((String)params.get(PARAM_ENCRYPTION_LEVEL));
// if metadata is excluded, alter encryption type
if ((Boolean)params.get(PARAM_EXCLUDE_METADATA))
{
encryptionType = encryptionType | PdfWriter.DO_NOT_ENCRYPT_METADATA;
}
// get temp file
File alfTempDir = TempFileProvider.getTempDir();
tempDir = new File(alfTempDir.getPath() + File.separatorChar + targetNodeRef.getId());
tempDir.mkdir();
File file = new File(tempDir, ffs.getFileInfo(targetNodeRef).getName());
// get the PDF input stream and create a reader for iText
targetReader = getReader(targetNodeRef);
PdfReader reader = new PdfReader(targetReader.getContentInputStream());
stamp = new PdfStamper(reader, new FileOutputStream(file));
// encrypt PDF
stamp.setEncryption(userPassword.getBytes(Charset.forName("UTF-8")), ownerPassword.getBytes(Charset.forName("UTF-8")), permissions, encryptionType);
stamp.close();
String fileName = getFilename(params, targetNodeRef);
// write out to destination
destinationNode = createDestinationNode(fileName,
(NodeRef)params.get(PARAM_DESTINATION_FOLDER), targetNodeRef, inplace);
writer = cs.getWriter(destinationNode, ContentModel.PROP_CONTENT, true);
writer.setEncoding(targetReader.getEncoding());
writer.setMimetype(FILE_MIMETYPE);
writer.putContent(file);
file.delete();
//if useAspect is true, store some additional info about the signature in the props
if(useEncryptionAspect)
{
ns.addAspect(destinationNode, PDFToolkitModel.ASPECT_ENCRYPTED, new HashMap<QName, Serializable>());
ns.setProperty(destinationNode, PDFToolkitModel.PROP_ENCRYPTIONDATE, new java.util.Date());
ns.setProperty(destinationNode, PDFToolkitModel.PROP_ENCRYPTEDBY, AuthenticationUtil.getRunAsUser());
}
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (DocumentException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
finally
{
if (tempDir != null)
{
try
{
tempDir.delete();
}
catch (Exception ex)
{
throw new AlfrescoRuntimeException(ex.getMessage(), ex);
}
}
if (stamp != null)
{
try
{
stamp.close();
}
catch (Exception ex)
{
throw new AlfrescoRuntimeException(ex.getMessage(), ex);
}
}
}
return destinationNode;
}
@Override
public NodeRef decryptPDF(NodeRef targetNodeRef, Map<String, Serializable> params)
{
PdfStamper stamp = null;
File tempDir = null;
ContentWriter writer = null;
ContentReader targetReader = null;
NodeRef destinationNode = null;
try
{
// get the parameters
String ownerPassword = (String)params.get(PARAM_OWNER_PASSWORD);
Boolean inplace = Boolean.valueOf(String.valueOf(params.get(PARAM_INPLACE)));
// get temp file
File alfTempDir = TempFileProvider.getTempDir();
tempDir = new File(alfTempDir.getPath() + File.separatorChar + targetNodeRef.getId());
tempDir.mkdir();
File file = new File(tempDir, ffs.getFileInfo(targetNodeRef).getName());
// get the PDF input stream and create a reader for iText
targetReader = getReader(targetNodeRef);
PdfReader reader = new PdfReader(targetReader.getContentInputStream(), ownerPassword.getBytes());
stamp = new PdfStamper(reader, new FileOutputStream(file));
stamp.close();
String fileName = getFilename(params, targetNodeRef);
// write out to destination
destinationNode = createDestinationNode(fileName,
(NodeRef)params.get(PARAM_DESTINATION_FOLDER), targetNodeRef, inplace);
writer = cs.getWriter(destinationNode, ContentModel.PROP_CONTENT, true);
writer.setEncoding(targetReader.getEncoding());
writer.setMimetype(FILE_MIMETYPE);
writer.putContent(file);
file.delete();
//if useAspect is true, store some additional info about the signature in the props
if(useEncryptionAspect)
{
ns.removeAspect(destinationNode, PDFToolkitModel.ASPECT_ENCRYPTED);
}
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (DocumentException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
finally
{
if (tempDir != null)
{
try
{
tempDir.delete();
}
catch (Exception ex)
{
throw new AlfrescoRuntimeException(ex.getMessage(), ex);
}
}
if (stamp != null)
{
try
{
stamp.close();
}
catch (Exception ex)
{
throw new AlfrescoRuntimeException(ex.getMessage(), ex);
}
}
}
return destinationNode;
}
@Override
public NodeRef signPDF(NodeRef targetNodeRef, Map<String, Serializable> params)
{
NodeRef privateKey = (NodeRef)params.get(PARAM_PRIVATE_KEY);
String location = (String)params.get(PARAM_LOCATION);
String position = (String)params.get(PARAM_POSITION);
String reason = (String)params.get(PARAM_REASON);
String visibility = (String)params.get(PARAM_VISIBILITY);
String keyPassword = (String)params.get(PARAM_KEY_PASSWORD);
String keyType = (String)params.get(PARAM_KEY_TYPE);
int height = getInteger(params.get(PARAM_HEIGHT));
int width = getInteger(params.get(PARAM_WIDTH));
int pageNumber = getInteger(params.get(PARAM_PAGE));
// By default, append the signature as a new PDF revision to avoid
// invalidating any signatures that might already exist on the doc
boolean appendToExisting = true;
if (params.get(PARAM_NEW_REVISION) != null) {
appendToExisting = Boolean.valueOf(String.valueOf(params.get(PARAM_NEW_REVISION)));
}
// New keystore parameters
String alias = (String)params.get(PARAM_ALIAS);
String storePassword = (String)params.get(PARAM_STORE_PASSWORD);
int locationX = getInteger(params.get(PARAM_LOCATION_X));
int locationY = getInteger(params.get(PARAM_LOCATION_Y));
Boolean inplace = Boolean.valueOf(String.valueOf(params.get(PARAM_INPLACE)));
File tempDir = null;
ContentWriter writer = null;
KeyStore ks = null;
NodeRef destinationNode = null;
try
{
// get a keystore instance by
if (keyType == null || keyType.equalsIgnoreCase(KEY_TYPE_DEFAULT))
{
ks = KeyStore.getInstance(KeyStore.getDefaultType());
}
else if (keyType.equalsIgnoreCase(KEY_TYPE_PKCS12))
{
ks = KeyStore.getInstance("pkcs12");
}
else
{
throw new AlfrescoRuntimeException("Unknown key type " + keyType + " specified");
}
// open the reader to the key and load it
ContentReader keyReader = getReader(privateKey);
ks.load(keyReader.getContentInputStream(), storePassword.toCharArray());
// set alias
// String alias = (String) ks.aliases().nextElement();
PrivateKey key = (PrivateKey)ks.getKey(alias, keyPassword.toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
// open original pdf
ContentReader pdfReader = getReader(targetNodeRef);
PdfReader reader = new PdfReader(pdfReader.getContentInputStream());
// If the page number is 0 because it couldn't be parsed or for
// some other reason, set it to the first page, which is 1.
// If the page number is negative, assume the intent is to "wrap".
// For example, -1 would always be the last page.
int numPages = reader.getNumberOfPages();
if (pageNumber < 1 && pageNumber == 0) {
pageNumber = 1; // use the first page
} else {
// page number is negative
pageNumber = numPages + 1 + pageNumber;
if (pageNumber <= 0) pageNumber = 1;
}
// if the page number specified is more than the num of pages,
// use the last page
if (pageNumber > numPages) {
pageNumber = numPages;
}
// create temp dir to store file
File alfTempDir = TempFileProvider.getTempDir();
tempDir = new File(alfTempDir.getPath() + File.separatorChar + targetNodeRef.getId());
tempDir.mkdir();
File file = new File(tempDir, ffs.getFileInfo(targetNodeRef).getName());
FileOutputStream fout = new FileOutputStream(file);
// When adding a second signature, append must be called on PdfStamper.createSignature
// to avoid invalidating previous signatures
PdfStamper stamp = null;
if (appendToExisting) {
stamp = PdfStamper.createSignature(reader, fout, '\0', tempDir, true);
} else {
stamp = PdfStamper.createSignature(reader, fout, '\0');
}
PdfSignatureAppearance sap = stamp.getSignatureAppearance();
sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
// set reason for signature and location of signer
sap.setReason(reason);
sap.setLocation(location);
if (visibility.equalsIgnoreCase(VISIBILITY_VISIBLE))
{
//create the signature rectangle using either the provided position or
//the exact coordinates, if provided
if(position != null && !position.trim().equalsIgnoreCase("")
&& !position.trim().equalsIgnoreCase(POSITION_MANUAL))
{
Rectangle pageRect = reader.getPageSizeWithRotation(pageNumber);
sap.setVisibleSignature(positionSignature(position, pageRect, width, height), pageNumber, null);
}
else
{
sap.setVisibleSignature(new Rectangle(locationX, locationY, locationX + width, locationY - height), pageNumber, null);
}
}
stamp.close();
String fileName = getFilename(params, targetNodeRef);
destinationNode = createDestinationNode(fileName,
(NodeRef)params.get(PARAM_DESTINATION_FOLDER), targetNodeRef, inplace);
writer = cs.getWriter(destinationNode, ContentModel.PROP_CONTENT, true);
writer.setEncoding(pdfReader.getEncoding());
writer.setMimetype(FILE_MIMETYPE);
writer.putContent(file);
file.delete();
//if useAspect is true, store some additional info about the signature in the props
if(useSignatureAspect)
{
ns.addAspect(destinationNode, PDFToolkitModel.ASPECT_SIGNED, new HashMap<QName, Serializable>());
ns.setProperty(destinationNode, PDFToolkitModel.PROP_REASON, reason);
ns.setProperty(destinationNode, PDFToolkitModel.PROP_LOCATION, location);
ns.setProperty(destinationNode, PDFToolkitModel.PROP_SIGNATUREDATE, new java.util.Date());
ns.setProperty(destinationNode, PDFToolkitModel.PROP_SIGNEDBY, AuthenticationUtil.getRunAsUser());
}
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (KeyStoreException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (ContentIOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (NoSuchAlgorithmException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (CertificateException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (UnrecoverableKeyException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (DocumentException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
finally
{
if (tempDir != null)
{
try
{
tempDir.delete();
}
catch (Exception ex)
{
throw new AlfrescoRuntimeException(ex.getMessage(), ex);
}
}
}
return destinationNode;
}
@Override
public NodeRef watermarkPDF(NodeRef targetNodeRef, Map<String, Serializable> params)
{
NodeRef destinationNode = null;
try
{
ContentReader targetReader = getReader(targetNodeRef);
if (params.get(PARAM_WATERMARK_TYPE) != null
&& params.get(PARAM_WATERMARK_TYPE).equals(TYPE_IMAGE))
{
NodeRef watermarkNodeRef = (NodeRef)params.get(PARAM_WATERMARK_IMAGE);
ContentReader watermarkContentReader = getReader(watermarkNodeRef);
destinationNode = this.imageAction(params, targetNodeRef, watermarkNodeRef, targetReader, watermarkContentReader);
}
else if (params.get(PARAM_WATERMARK_TYPE) != null
&& params.get(PARAM_WATERMARK_TYPE).equals(TYPE_TEXT))
{
destinationNode = this.textAction(params, targetNodeRef, targetReader);
}
}
catch (AlfrescoRuntimeException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
return destinationNode;
}
@Override
public NodeRef splitPDF(NodeRef targetNodeRef, Map<String, Serializable> params)
{
PDDocument pdf = null;
InputStream is = null;
File tempDir = null;
ContentWriter writer = null;
NodeRef destinationFolder;
try
{
destinationFolder = (NodeRef)params.get(PARAM_DESTINATION_FOLDER);
ContentReader targetReader = getReader(targetNodeRef);
// Get the split frequency
int splitFrequency = 0;
String splitFrequencyString = params.get(PARAM_SPLIT_FREQUENCY).toString();
if (!splitFrequencyString.equals(""))
{
splitFrequency = Integer.valueOf(splitFrequencyString);
}
// Get contentReader inputStream
is = targetReader.getContentInputStream();
// stream the document in
pdf = PDDocument.load(is);
// split the PDF and put the pages in a list
Splitter splitter = new Splitter();
// if the default split is not every page, then set it to the right
// frequency
if (splitFrequency > 0)
{
splitter.setSplitAtPage(splitFrequency);
}
// Split the pages
List<PDDocument> pdfs = splitter.split(pdf);
// Lets get reading to walk the list
Iterator<PDDocument> it = pdfs.iterator();
// Start page split numbering at
int page = 1;
int endPage = 0;
// build a temp dir name based on the ID of the noderef we are
// importing
File alfTempDir = TempFileProvider.getTempDir();
tempDir = new File(alfTempDir.getPath() + File.separatorChar + targetNodeRef.getId());
tempDir.mkdir();
while (it.hasNext())
{
// Pulling together the right string split pages
String pagePlus = "";
String pg = "_pg";
// Get the split document and save it into the temp dir with new
// name
PDDocument splitpdf = (PDDocument)it.next();
int pagesInPDF = splitpdf.getNumberOfPages();
if (splitFrequency > 0)
{
endPage = endPage + pagesInPDF;
pagePlus = "-" + endPage;
pg = "_pgs";
}
// put together the name and save the PDF
String fileNameSansExt = getFilenameSansExt(targetNodeRef, FILE_EXTENSION);
splitpdf.save(tempDir + "" + File.separatorChar + fileNameSansExt + pg + page + pagePlus + FILE_EXTENSION);
// increment page count
if (splitFrequency > 0)
{
page = (page++) + pagesInPDF;
}
else
{
page++;
}
try
{
splitpdf.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
for (File file : tempDir.listFiles())
{
try
{
if (file.isFile())
{
// Get a writer and prep it for putting it back into the
// repo
NodeRef destinationNode = createDestinationNode(file.getName(),
destinationFolder, targetNodeRef, false);
writer = cs.getWriter(destinationNode, ContentModel.PROP_CONTENT, true);
writer.setEncoding(targetReader.getEncoding()); // original
// encoding
writer.setMimetype(FILE_MIMETYPE);
// Put it in the repo
writer.putContent(file);
// Clean up
file.delete();
}
}
catch (FileExistsException e)
{
throw new AlfrescoRuntimeException("Failed to process file.", e);
}
}
}
catch (COSVisitorException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
finally
{
if (pdf != null)
{
try
{
pdf.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
if (is != null)
{
try
{
is.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
if (tempDir != null)
{
tempDir.delete();
}
}
return destinationFolder;
}
@Override
public NodeRef splitPDFAtPage(NodeRef targetNodeRef, Map<String, Serializable> params)
{
PDDocument pdf = null;
InputStream is = null;
File tempDir = null;
ContentWriter writer = null;
NodeRef destinationFolder;
try
{
destinationFolder = (NodeRef)params.get(PARAM_DESTINATION_FOLDER);
ContentReader targetReader = getReader(targetNodeRef);
// Get the split frequency
int splitPageNumber = 0;
String splitPage = params.get(PARAM_PAGE).toString();
if (!splitPage.equals(""))
{
try
{
splitPageNumber = Integer.valueOf(splitPage);
}
catch (NumberFormatException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
// Get contentReader inputStream
is = targetReader.getContentInputStream();
// stream the document in
pdf = PDDocument.load(is);
// split the PDF and put the pages in a list
Splitter splitter = new Splitter();
// Need to adjust the input value to get the split at the right page
splitter.setSplitAtPage(splitPageNumber - 1);
// Split the pages
List<PDDocument> pdfs = splitter.split(pdf);
// Start page split numbering at
int page = 1;
// build a temp dir, name based on the ID of the noderef we are
// importing
File alfTempDir = TempFileProvider.getTempDir();
tempDir = new File(alfTempDir.getPath() + File.separatorChar + targetNodeRef.getId());
tempDir.mkdir();
// FLAG: This is ugly.....get the first PDF.
PDDocument firstPDF = (PDDocument)pdfs.remove(0);
int pagesInFirstPDF = firstPDF.getNumberOfPages();
String lastPage = "";
String pg = "_pg";
if (pagesInFirstPDF > 1)
{
pg = "_pgs";
lastPage = "-" + pagesInFirstPDF;
}
String fileNameSansExt = getFilenameSansExt(targetNodeRef, FILE_EXTENSION);
firstPDF.save(tempDir + "" + File.separatorChar + fileNameSansExt + pg + page + lastPage + FILE_EXTENSION);
try
{
firstPDF.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
// FLAG: Like I said: "_UGLY_" ..... and it gets worse
PDDocument secondPDF = null;
Iterator<PDDocument> its = pdfs.iterator();
int pagesInSecondPDF = 0;
while (its.hasNext())
{
if (secondPDF != null)
{
// Get the split document and save it into the temp dir with
// new name
PDDocument splitpdf = (PDDocument)its.next();
int pagesInThisPDF = splitpdf.getNumberOfPages();
pagesInSecondPDF = pagesInSecondPDF + pagesInThisPDF;
PDFMergerUtility merger = new PDFMergerUtility();
merger.appendDocument(secondPDF, splitpdf);
merger.mergeDocuments();
try
{
splitpdf.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
else
{
secondPDF = (PDDocument)its.next();
pagesInSecondPDF = secondPDF.getNumberOfPages();
}
}
if (pagesInSecondPDF > 1)
{
pg = "_pgs";
lastPage = "-" + (pagesInSecondPDF + pagesInFirstPDF);
}
else
{
pg = "_pg";
lastPage = "";
}
// This is where we should save the appended PDF
// put together the name and save the PDF
secondPDF.save(tempDir + "" + File.separatorChar + fileNameSansExt + pg + splitPageNumber + lastPage + FILE_EXTENSION);
for (File file : tempDir.listFiles())
{
try
{
if (file.isFile())
{
// Get a writer and prep it for putting it back into the
// repo
NodeRef destinationNode = createDestinationNode(file.getName(),
destinationFolder, targetNodeRef, false);
writer = cs.getWriter(destinationNode, ContentModel.PROP_CONTENT, true);
writer.setEncoding(targetReader.getEncoding()); // original
// encoding
writer.setMimetype(FILE_MIMETYPE);
// Put it in the repo
writer.putContent(file);
// Clean up
file.delete();
}
}
catch (FileExistsException e)
{
throw new AlfrescoRuntimeException("Failed to process file.", e);
}
}
}
catch (COSVisitorException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
finally
{
if (pdf != null)
{
try
{
pdf.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
if (is != null)
{
try
{
is.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
if (tempDir != null)
{
tempDir.delete();
}
}
return destinationFolder;
}
@Override
public NodeRef insertPDF(NodeRef targetNodeRef, Map<String, Serializable> params)
{
PDDocument pdf = null;
PDDocument insertContentPDF = null;
InputStream is = null;
InputStream cis = null;
File tempDir = null;
ContentWriter writer = null;
NodeRef destinationNode = null;
try
{
ContentReader targetReader = getReader(targetNodeRef);
ContentReader insertReader = getReader((NodeRef)params.get(PARAM_INSERT_CONTENT));
int insertAt = Integer.valueOf((String)params.get(PARAM_PAGE)).intValue();
Boolean inplace = Boolean.valueOf(String.valueOf(params.get(PARAM_INPLACE)));
// Get contentReader inputStream
is = targetReader.getContentInputStream();
// Get insertContentReader inputStream
cis = insertReader.getContentInputStream();
// stream the target document in
pdf = PDDocument.load(is);
// stream the insert content document in
insertContentPDF = PDDocument.load(cis);
// split the PDF and put the pages in a list
Splitter splitter = new Splitter();
// Split the pages
List<PDDocument> pdfs = splitter.split(pdf);
// Build the output PDF
PDFMergerUtility merger = new PDFMergerUtility();
PDDocument newDocument = new PDDocument();
for (int i = 0; i < pdfs.size(); i++) {
if (i == insertAt -1) {
merger.appendDocument(newDocument, insertContentPDF);
}
merger.appendDocument(newDocument, (PDDocument)pdfs.get(i));
}
merger.setDestinationFileName(params.get(PARAM_DESTINATION_NAME).toString());
merger.mergeDocuments();
// build a temp dir, name based on the ID of the noderef we are
// importing
File alfTempDir = TempFileProvider.getTempDir();
tempDir = new File(alfTempDir.getPath() + File.separatorChar + targetNodeRef.getId());
tempDir.mkdir();
String fileName = params.get(PARAM_DESTINATION_NAME).toString();
PDDocument completePDF = newDocument;
completePDF.save(tempDir + "" + File.separatorChar + fileName + FILE_EXTENSION);
try
{
completePDF.close();
newDocument.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
for (File file : tempDir.listFiles())
{
try
{
if (file.isFile())
{
// Get a writer and prep it for putting it back into the
// repo
destinationNode = createDestinationNode(file.getName(),
(NodeRef)params.get(PARAM_DESTINATION_FOLDER), targetNodeRef, inplace);
writer = cs.getWriter(destinationNode, ContentModel.PROP_CONTENT, true);
writer.setEncoding(targetReader.getEncoding()); // original
// encoding
writer.setMimetype(FILE_MIMETYPE);
// Put it in the repo
writer.putContent(file);
// Clean up
file.delete();
}
}
catch (FileExistsException e)
{
throw new AlfrescoRuntimeException("Failed to process file.", e);
}
}
}
// TODO add better handling
catch (COSVisitorException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
finally
{
if (pdf != null)
{
try
{
pdf.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
if (is != null)
{
try
{
is.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
if (tempDir != null)
{
tempDir.delete();
}
}
return destinationNode;
}
@Override
public NodeRef deletePagesFromPDF(NodeRef targetNodeRef, Map<String, Serializable> params)
{
String pages = String.valueOf(params.get(PARAM_PAGE));
return subsetPDFDocument(targetNodeRef, params, pages, true);
}
@Override
public NodeRef extractPagesFromPDF(NodeRef targetNodeRef, Map<String, Serializable> params)
{
String pages = String.valueOf(params.get(PARAM_PAGE));
return subsetPDFDocument(targetNodeRef, params, pages, false);
}
@Override
public NodeRef rotatePDF(NodeRef targetNodeRef, Map<String, Serializable> params)
{
InputStream is = null;
File tempDir = null;
ContentWriter writer = null;
PdfReader pdfReader = null;
NodeRef destinationNode = null;
try
{
ContentReader targetReader = getReader(targetNodeRef);
is = targetReader.getContentInputStream();
File alfTempDir = TempFileProvider.getTempDir();
tempDir = new File(alfTempDir.getPath() + File.separatorChar + targetNodeRef.getId());
tempDir.mkdir();
Boolean inplace = Boolean.valueOf(String.valueOf(params.get(PARAM_INPLACE)));
Integer degrees = Integer.valueOf(String.valueOf(params.get(PARAM_DEGREES)));
String pages = String.valueOf(params.get(PARAM_PAGE));
if(degrees % 90 != 0)
{
throw new AlfrescoRuntimeException("Rotation degres must be a multiple of 90 (90, 180, 270, etc)");
}
String fileName = getFilename(params, targetNodeRef);
File file = new File(tempDir, ffs.getFileInfo(targetNodeRef).getName());
pdfReader = new PdfReader(is);
PdfStamper stamp = new PdfStamper(pdfReader, new FileOutputStream(file));
int rotation = 0;
PdfDictionary pageDictionary;
int numPages = pdfReader.getNumberOfPages();
for (int pageNum = 1; pageNum <= numPages; pageNum++)
{
// only apply stamp to requested pages
if (checkPage(pages, pageNum, numPages))
{
rotation = pdfReader.getPageRotation(pageNum);
pageDictionary = pdfReader.getPageN(pageNum);
pageDictionary.put(PdfName.ROTATE, new PdfNumber(rotation + degrees));
}
}
stamp.close();
pdfReader.close();
destinationNode = createDestinationNode(fileName,
(NodeRef)params.get(PARAM_DESTINATION_FOLDER), targetNodeRef, inplace);
writer = cs.getWriter(destinationNode, ContentModel.PROP_CONTENT, true);
writer.setEncoding(targetReader.getEncoding());
writer.setMimetype(FILE_MIMETYPE);
// Put it in the repository
writer.putContent(file);
// Clean up
file.delete();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (DocumentException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (Exception e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
finally
{
if (pdfReader != null)
{
pdfReader.close();
}
if (is != null)
{
try
{
is.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
if (tempDir != null)
{
tempDir.delete();
}
}
return destinationNode;
}
private NodeRef subsetPDFDocument(NodeRef targetNodeRef, Map<String, Serializable> params, String pages, boolean delete)
{
InputStream is = null;
File tempDir = null;
ContentWriter writer = null;
PdfReader pdfReader = null;
NodeRef destinationNode = null;
try
{
ContentReader targetReader = getReader(targetNodeRef);
is = targetReader.getContentInputStream();
File alfTempDir = TempFileProvider.getTempDir();
tempDir = new File(alfTempDir.getPath() + File.separatorChar + targetNodeRef.getId());
tempDir.mkdir();
Boolean inplace = Boolean.valueOf(String.valueOf(params.get(PARAM_INPLACE)));
String fileName = getFilename(params, targetNodeRef);
File file = new File(tempDir, ffs.getFileInfo(targetNodeRef).getName());
pdfReader = new PdfReader(is);
Document doc = new Document(pdfReader.getPageSizeWithRotation(1));
PdfCopy copy = new PdfCopy(doc, new FileOutputStream(file));
doc.open();
List<Integer> pagelist = parsePageList(pages);
for (int pageNum = 1; pageNum <= pdfReader.getNumberOfPages(); pageNum++)
{
if (pagelist.contains(pageNum) && !delete)
{
copy.addPage(copy.getImportedPage(pdfReader, pageNum));
}
else if (!pagelist.contains(pageNum) && delete)
{
copy.addPage(copy.getImportedPage(pdfReader, pageNum));
}
}
doc.close();
destinationNode = createDestinationNode(fileName,
(NodeRef)params.get(PARAM_DESTINATION_FOLDER), targetNodeRef, inplace);
writer = cs.getWriter(destinationNode, ContentModel.PROP_CONTENT, true);
writer.setEncoding(targetReader.getEncoding());
writer.setMimetype(FILE_MIMETYPE);
// Put it in the repository
writer.putContent(file);
// Clean up
file.delete();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (DocumentException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (Exception e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
finally
{
if (pdfReader != null)
{
pdfReader.close();
}
if (is != null)
{
try
{
is.close();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
if (tempDir != null)
{
tempDir.delete();
}
}
return destinationNode;
}
private ContentReader getReader(NodeRef nodeRef)
{
// first, make sure the node exists
if (ns.exists(nodeRef) == false)
{
// node doesn't exist - can't do anything
throw new AlfrescoRuntimeException("NodeRef: " + nodeRef + " does not exist");
}
// Next check that the node is a sub-type of content
QName typeQName = ns.getType(nodeRef);
if (ds.isSubClass(typeQName, ContentModel.TYPE_CONTENT) == false)
{
// it is not content, so can't transform
throw new AlfrescoRuntimeException("The selected node is not a content node");
}
// Get the content reader. If it is null, can't do anything here
ContentReader contentReader = cs.getReader(nodeRef, ContentModel.PROP_CONTENT);
if(contentReader == null)
{
throw new AlfrescoRuntimeException("The content reader for NodeRef: " + nodeRef + "is null");
}
return contentReader;
}
/**
* @param ruleAction
* @param filename
* @return
*/
private NodeRef createDestinationNode(String filename, NodeRef destinationParent, NodeRef target, boolean inplace)
{
NodeRef destinationNode;
// if inplace mode is turned on, the destination for the modified content
// is the original node
if(inplace)
{
return target;
}
if(createNew)
{
//create a file in the right location
FileInfo fileInfo = ffs.create(destinationParent, filename, ContentModel.TYPE_CONTENT);
destinationNode = fileInfo.getNodeRef();
}
else
{
try
{
FileInfo fileInfo = ffs.copy(target, destinationParent, filename);
destinationNode = fileInfo.getNodeRef();
}
catch(FileNotFoundException fnf)
{
throw new AlfrescoRuntimeException(fnf.getMessage(), fnf);
}
}
return destinationNode;
}
private int getInteger(Serializable val)
{
if(val == null)
{
return 0;
}
try
{
return Integer.parseInt(val.toString());
}
catch(NumberFormatException nfe)
{
return 0;
}
}
private File getTempFile(NodeRef nodeRef)
{
File alfTempDir = TempFileProvider.getTempDir();
File toolkitTempDir = new File(alfTempDir.getPath() + File.separatorChar + nodeRef.getId());
toolkitTempDir.mkdir();
File file = new File(toolkitTempDir, ffs.getFileInfo(nodeRef).getName());
return file;
}
private String getFilename(Map<String, Serializable> params, NodeRef targetNodeRef)
{
Serializable providedName = params.get(PARAM_DESTINATION_NAME);
String fileName = null;
if(providedName != null)
{
fileName = String.valueOf(providedName);
if(!fileName.endsWith(FILE_EXTENSION))
{
fileName = fileName + FILE_EXTENSION;
}
}
else
{
fileName = String.valueOf(ns.getProperty(targetNodeRef, ContentModel.PROP_NAME));
}
return fileName;
}
/**
* Parses the list of pages or page ranges to delete and returns a list of page numbers
*
* @param list
* @return
*/
private List<Integer> parsePageList(String list)
{
List<Integer> pages = new ArrayList<Integer>();
String[] tokens = list.split(",");
for(String token : tokens)
{
//parse each, if one is not an int, log it but keep going
try
{
pages.add(Integer.parseInt(token));
}
catch(NumberFormatException nfe)
{
logger.warn("Page list contains non-numeric values");
}
}
return pages;
}
/**
* Build the permissions mask for iText
*
* @param options
* @return
*/
private int buildPermissionMask(Map<String, Serializable> options)
{
int permissions = 0;
if ((Boolean)options.get(PARAM_ALLOW_PRINT))
{
permissions = permissions | PdfWriter.ALLOW_PRINTING;
}
if ((Boolean)options.get(PARAM_ALLOW_COPY))
{
permissions = permissions | PdfWriter.ALLOW_COPY;
}
if ((Boolean)options.get(PARAM_ALLOW_CONTENT_MODIFICATION))
{
permissions = permissions | PdfWriter.ALLOW_MODIFY_CONTENTS;
}
if ((Boolean)options.get(PARAM_ALLOW_ANNOTATION_MODIFICATION))
{
permissions = permissions | PdfWriter.ALLOW_MODIFY_ANNOTATIONS;
}
if ((Boolean)options.get(PARAM_ALLOW_SCREEN_READER))
{
permissions = permissions | PdfWriter.ALLOW_SCREENREADERS;
}
if ((Boolean)options.get(PARAM_ALLOW_DEGRADED_PRINT))
{
permissions = permissions | PdfWriter.ALLOW_DEGRADED_PRINTING;
}
if ((Boolean)options.get(PARAM_ALLOW_ASSEMBLY))
{
permissions = permissions | PdfWriter.ALLOW_ASSEMBLY;
}
if ((Boolean)options.get(PARAM_ALLOW_FORM_FILL))
{
permissions = permissions | PdfWriter.ALLOW_FILL_IN;
}
return permissions;
}
/**
* Create a rectangle for the visible signature using the selected position and signature size
*
* @param position
* @param width
* @param height
* @return
*/
private Rectangle positionSignature(String position, Rectangle pageRect, int width, int height)
{
float pageHeight = pageRect.getHeight();
float pageWidth = pageRect.getWidth();
Rectangle r = null;
if (position.equals(POSITION_BOTTOMLEFT))
{
r = new Rectangle(0, height, width, 0);
}
else if (position.equals(POSITION_BOTTOMRIGHT))
{
r = new Rectangle(pageWidth - width, height, pageWidth, 0);
}
else if (position.equals(POSITION_TOPLEFT))
{
r = new Rectangle(0, pageHeight, width, pageHeight - height);
}
else if (position.equals(POSITION_TOPRIGHT))
{
r = new Rectangle(pageWidth - width, pageHeight, pageWidth, pageHeight - height);
}
else if (position.equals(POSITION_CENTER))
{
r = new Rectangle((pageWidth / 2) - (width / 2), (pageHeight / 2) - (height / 2),
(pageWidth / 2) + (width / 2), (pageHeight / 2) + (height / 2));
}
return r;
}
/**
* @param fileName
* @param extension
* @return
*/
private String removeExtension(String fileName, String extension)
{
// Does the file have the extension?
if (fileName != null && fileName.contains(extension))
{
// Where does the extension start?
int extensionStartsAt = fileName.indexOf(extension);
// Get the Filename sans the extension
return fileName.substring(0, extensionStartsAt);
}
return fileName;
}
private String getFilename(NodeRef targetNodeRef)
{
FileInfo fileInfo = ffs.getFileInfo(targetNodeRef);
String filename = fileInfo.getName();
return filename;
}
private String getFilenameSansExt(NodeRef targetNodeRef, String extension)
{
String filenameSansExt;
filenameSansExt = removeExtension(getFilename(targetNodeRef), extension);
return filenameSansExt;
}
/**
* Applies an image watermark
*
* @param reader
* @param writer
* @param options
* @throws Exception
*/
private NodeRef imageAction(Map<String, Serializable> options, NodeRef targetNodeRef, NodeRef watermarkNodeRef,
ContentReader actionedUponContentReader, ContentReader watermarkContentReader)
{
PdfStamper stamp = null;
File tempDir = null;
ContentWriter writer = null;
NodeRef destinationNode = null;
try
{
File file = getTempFile(targetNodeRef);
// get the PDF input stream and create a reader for iText
PdfReader reader = new PdfReader(actionedUponContentReader.getContentInputStream());
stamp = new PdfStamper(reader, new FileOutputStream(file));
PdfContentByte pcb;
// get a com.itextpdf.text.Image object via java.imageio.ImageIO
Image img = Image.getInstance(ImageIO.read(watermarkContentReader.getContentInputStream()), null);
// get the PDF pages and position
String pages = (String)options.get(PARAM_PAGE);
String position = (String)options.get(PARAM_POSITION);
String depth = (String)options.get(PARAM_WATERMARK_DEPTH);
Boolean inplace = Boolean.valueOf(String.valueOf(options.get(PARAM_INPLACE)));
// get the manual positioning options (if provided)
int locationX = getInteger(options.get(PARAM_LOCATION_X));
int locationY = getInteger(options.get(PARAM_LOCATION_Y));
// image requires absolute positioning or an exception will be
// thrown
// set image position according to parameter. Use
// PdfReader.getPageSizeWithRotation
// to get the canvas size for alignment.
img.setAbsolutePosition(100f, 100f);
// stamp each page
int numpages = reader.getNumberOfPages();
for (int i = 1; i <= numpages; i++)
{
Rectangle r = reader.getPageSizeWithRotation(i);
// set stamp position
if (position.equals(POSITION_BOTTOMLEFT))
{
img.setAbsolutePosition(0, 0);
}
else if (position.equals(POSITION_BOTTOMRIGHT))
{
img.setAbsolutePosition(r.getWidth() - img.getWidth(), 0);
}
else if (position.equals(POSITION_TOPLEFT))
{
img.setAbsolutePosition(0, r.getHeight() - img.getHeight());
}
else if (position.equals(POSITION_TOPRIGHT))
{
img.setAbsolutePosition(r.getWidth() - img.getWidth(), r.getHeight() - img.getHeight());
}
else if (position.equals(POSITION_CENTER))
{
img.setAbsolutePosition(getCenterX(r, img), getCenterY(r, img));
}
else if (position.equals(POSITION_MANUAL))
{
img.setAbsolutePosition(locationX, locationY);
}
// if this is an under-text stamp, use getUnderContent.
// if this is an over-text stamp, usse getOverContent.
if (depth.equals(DEPTH_OVER))
{
pcb = stamp.getOverContent(i);
}
else
{
pcb = stamp.getUnderContent(i);
}
// only apply stamp to requested pages
if (checkPage(pages, i, numpages))
{
pcb.addImage(img);
}
}
stamp.close();
String fileName = getFilename(options, targetNodeRef);
// Get a writer and prep it for putting it back into the repo
//can't use BasePDFActionExecuter.getWriter here need the nodeRef of the destination
destinationNode = createDestinationNode(fileName,
(NodeRef)options.get(PARAM_DESTINATION_FOLDER), targetNodeRef, inplace);
writer = cs.getWriter(destinationNode, ContentModel.PROP_CONTENT, true);
writer.setEncoding(actionedUponContentReader.getEncoding());
writer.setMimetype(FILE_MIMETYPE);
// Put it in the repo
writer.putContent(file);
// delete the temp file
file.delete();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (DocumentException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
finally
{
if (tempDir != null)
{
try
{
tempDir.delete();
}
catch (Exception ex)
{
throw new AlfrescoRuntimeException(ex.getMessage(), ex);
}
}
if (stamp != null)
{
try
{
stamp.close();
}
catch (Exception ex)
{
throw new AlfrescoRuntimeException(ex.getMessage(), ex);
}
}
}
return destinationNode;
}
/**
* Applies a text watermark (current date, user name, etc, depending on
* options)
*
* @param reader
* @param writer
* @param options
*/
private NodeRef textAction(Map<String, Serializable> options, NodeRef targetNodeRef, ContentReader actionedUponContentReader)
{
PdfStamper stamp = null;
File tempDir = null;
ContentWriter writer = null;
String watermarkText;
StringTokenizer st;
Vector<String> tokens = new Vector<String>();
NodeRef destinationNode = null;
try
{
File file = getTempFile(targetNodeRef);
// get the PDF input stream and create a reader for iText
PdfReader reader = new PdfReader(actionedUponContentReader.getContentInputStream());
stamp = new PdfStamper(reader, new FileOutputStream(file));
PdfContentByte pcb;
// get the PDF pages and position
String pages = (String)options.get(PARAM_PAGE);
String position = (String)options.get(PARAM_POSITION);
String depth = (String)options.get(PARAM_WATERMARK_DEPTH);
int locationX = getInteger(options.get(PARAM_LOCATION_X));
int locationY = getInteger(options.get(PARAM_LOCATION_Y));
Boolean inplace = Boolean.valueOf(String.valueOf(options.get(PARAM_INPLACE)));
// create the base font for the text stamp
BaseFont bf = BaseFont.createFont((String)options.get(PARAM_WATERMARK_FONT), BaseFont.CP1250, BaseFont.EMBEDDED);
// get watermark text and process template with model
String templateText = (String)options.get(PARAM_WATERMARK_TEXT);
Map<String, Object> model = buildWatermarkTemplateModel(targetNodeRef);
StringWriter watermarkWriter = new StringWriter();
freemarkerProcessor.processString(templateText, model, watermarkWriter);
watermarkText = watermarkWriter.getBuffer().toString();
// tokenize watermark text to support multiple lines and copy tokens
// to vector for re-use
st = new StringTokenizer(watermarkText, "\r\n", false);
while (st.hasMoreTokens())
{
tokens.add(st.nextToken());
}
// stamp each page
int numpages = reader.getNumberOfPages();
for (int i = 1; i <= numpages; i++)
{
Rectangle r = reader.getPageSizeWithRotation(i);
// if this is an under-text stamp, use getUnderContent.
// if this is an over-text stamp, use getOverContent.
if (depth.equals(DEPTH_OVER))
{
pcb = stamp.getOverContent(i);
}
else
{
pcb = stamp.getUnderContent(i);
}
// set the font and size
float size = Float.parseFloat((String)options.get(PARAM_WATERMARK_SIZE));
pcb.setFontAndSize(bf, size);
// only apply stamp to requested pages
if (checkPage(pages, i, numpages))
{
writeAlignedText(pcb, r, tokens, size, position, locationX, locationY);
}
}
stamp.close();
String fileName = getFilename(options, targetNodeRef);
// Get a writer and prep it for putting it back into the repo
//can't use BasePDFActionExecuter.getWriter here need the nodeRef of the destination
destinationNode = createDestinationNode(fileName,
(NodeRef)options.get(PARAM_DESTINATION_FOLDER), targetNodeRef, inplace);
writer = cs.getWriter(destinationNode, ContentModel.PROP_CONTENT, true);
writer.setEncoding(actionedUponContentReader.getEncoding());
writer.setMimetype(FILE_MIMETYPE);
// Put it in the repo
writer.putContent(file);
// delete the temp file
file.delete();
}
catch (IOException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
catch (DocumentException e)
{
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
finally
{
if (tempDir != null)
{
try
{
tempDir.delete();
}
catch (Exception ex)
{
throw new AlfrescoRuntimeException(ex.getMessage(), ex);
}
}
if (stamp != null)
{
try
{
stamp.close();
}
catch (Exception ex)
{
throw new AlfrescoRuntimeException(ex.getMessage(), ex);
}
}
}
return destinationNode;
}
/**
* Writes text watermark to one of the 5 preconfigured locations
*
* @param pcb
* @param r
* @param tokens
* @param size
* @param position
*/
private void writeAlignedText(PdfContentByte pcb, Rectangle r, Vector<String> tokens, float size,
String position, int locationX, int locationY)
{
// get the dimensions of our 'rectangle' for text
float height = size * tokens.size();
float width = 0;
float centerX = 0, startY = 0;
for (int i = 0; i < tokens.size(); i++)
{
if (pcb.getEffectiveStringWidth(tokens.get(i), false) > width)
{
width = pcb.getEffectiveStringWidth(tokens.get(i), false);
}
}
// now that we have the width and height, we can calculate the center
// position for
// the rectangle that will contain our text.
if (position.equals(POSITION_BOTTOMLEFT))
{
centerX = width / 2 + PAD;
startY = 0 + PAD + height;
}
else if (position.equals(POSITION_BOTTOMRIGHT))
{
centerX = r.getWidth() - (width / 2) - PAD;
startY = 0 + PAD + height;
}
else if (position.equals(POSITION_TOPLEFT))
{
centerX = width / 2 + PAD;
startY = r.getHeight() - (PAD * 2);
}
else if (position.equals(POSITION_TOPRIGHT))
{
centerX = r.getWidth() - (width / 2) - PAD;
startY = r.getHeight() - (PAD * 2);
}
else if (position.equals(POSITION_CENTER))
{
centerX = r.getWidth() / 2;
startY = (r.getHeight() / 2) + (height / 2);
}
else if (position.equals(POSITION_MANUAL))
{
centerX = r.getWidth() / 2 - locationX;
startY = locationY;
}
// apply text to PDF
pcb.beginText();
for (int t = 0; t < tokens.size(); t++)
{
pcb.showTextAligned(PdfContentByte.ALIGN_CENTER, tokens.get(t), centerX, startY - (size * t), 0);
}
pcb.endText();
}
/**
* Builds a freemarker model which supports a subset of the default model.
*
* @param ref
* @return
*/
private Map<String, Object> buildWatermarkTemplateModel(NodeRef ref)
{
Map<String, Object> model = new HashMap<String, Object>();
NodeRef person = ps.getPerson(as.getCurrentUserName());
model.put("person", new TemplateNode(person, serviceRegistry, null));
NodeRef homespace = (NodeRef)ns.getProperty(person, ContentModel.PROP_HOMEFOLDER);
model.put("userhome", new TemplateNode(homespace, serviceRegistry, null));
model.put("document", new TemplateNode(ref, serviceRegistry, null));
NodeRef parent = ns.getPrimaryParent(ref).getParentRef();
model.put("space", new TemplateNode(parent, serviceRegistry, null));
model.put("date", new Date());
//also add all of the node properties to the model
model.put("properties", ns.getProperties(ref));
return model;
}
/**
* Determines whether or not a watermark should be applied to a given page
*
* @param pages
* @param current
* @param numpages
* @return
*/
private boolean checkPage(String pages, int current, int numpages)
{
boolean markPage = false;
if (pages.equals(PAGE_EVEN))
{
if (current % 2 == 0)
{
markPage = true;
}
}
else if (pages.equals(PAGE_ODD))
{
if (current % 2 != 0)
{
markPage = true;
}
}
else if (pages.equals(PAGE_FIRST))
{
if (current == 1)
{
markPage = true;
}
}
else if (pages.equals(PAGE_LAST))
{
if (current == numpages)
{
markPage = true;
}
}
else if (pages.equals(PAGE_ALL))
{
markPage = true;
}
else
{
// if we get here, a scheme wasn't selected, so we can treat this like a page list
List<Integer> pageList = parsePageList(pages);
if(pageList.contains(current))
{
markPage = true;
}
}
return markPage;
}
/**
* Gets the X value for centering the watermark image
*
* @param r
* @param img
* @return
*/
private float getCenterX(Rectangle r, Image img)
{
float x = 0;
float pdfwidth = r.getWidth();
float imgwidth = img.getWidth();
x = (pdfwidth - imgwidth) / 2;
return x;
}
/**
* Gets the Y value for centering the watermark image
*
* @param r
* @param img
* @return
*/
private float getCenterY(Rectangle r, Image img)
{
float y = 0;
float pdfheight = r.getHeight();
float imgheight = img.getHeight();
y = (pdfheight - imgheight) / 2;
return y;
}
/**
* @param serviceRegistry
*/
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
ns = serviceRegistry.getNodeService();
cs = serviceRegistry.getContentService();
ffs = serviceRegistry.getFileFolderService();
ds = serviceRegistry.getDictionaryService();
ps = serviceRegistry.getPersonService();
as = serviceRegistry.getAuthenticationService();
}
/**
* Sets whether a PDF action creates a new empty node or copies the source node, preserving
* the content type, applied aspects and properties
*
* @param createNew
*/
public void setCreateNew(boolean createNew)
{
this.createNew = createNew;
}
public void setUseSignatureAspect(boolean useSignatureAspect)
{
this.useSignatureAspect = useSignatureAspect;
}
public void setUseEncryptionAspect(boolean useEncryptionAspect)
{
this.useEncryptionAspect = useEncryptionAspect;
}
}