I have written an application that exports hundreds of PDF documents from a
content repository and then merges those documents into a single PDF
document. Any PDFs that are either encrypted or corrupted are supplied to
the requester as attachements to the merged PDF (ideally these shouldn't
exist but they do get into the system).
The domain object graph corresponding to the folders and documents in the
content repository is recursively processed and the Document domain object
has a reference to the file path where the document was exported. Once all
of the files have been merged and bookmarks created, the PdfDocument is
closed, but I am receiving the following RuntimeException
11/04/12 15:28:31 Caused by: java.lang.RuntimeException: The page 135658 was
requested but the document has only 135657 pages.
11/04/12 15:28:31 at
com.itextpdf.text.pdf.PdfWriter.close(PdfWriter.java:1165)
11/04/12 15:28:31 at com.itextpdf.text.pdf.PdfCopy.close(PdfCopy.java:488)
11/04/12 15:28:31 at
com.itextpdf.text.pdf.PdfDocument.close(PdfDocument.java:780)
11/04/12 15:28:31 at com.itextpdf.text.Document.close(Document.java:409)
11/04/12 15:28:31 at
ebackend.service.pdf.ITextPDFService.append(ITextPDFService.java:249)
11/04/12 15:28:31 at
ebackend.ejb.EbackendMessageBean.processRedactRequest(EbackendMessageBean.java:432)
This exception only seems to occur when there is a large number of documents
being merged. I have debugged over and over, but have been unable to
determine why this error occurrs. The code is below, the methods of interest
are append() and mergePDFs() and the merging begins with append().
I would appreciate it if someone could take a quick look at the code to see
if I am doing something incorrectly. If the code looks correct, what
conditions could yield this result? What conditions could cause PdfWriter's
pageReferences list to be larger than the currentPageNumber - 1 ?
Thanks.
public class ITextPDFService {
private static final Logger logger =
Logger.getLogger(ITextPDFService.class);
/**
* Loads the CoverPage pdf file and completes the embedded
AcroForm. After
* completing the AcroForm the file is wrapped in a domain object
* so that it can be merged. The page is not added to in any way.
*
* @param info The CoverLetterInfo contains the summary data needed
to create
* the title/cover page.
*
* @param exportFolder The folder where all of the exported
documents are located.
*
* @return Document that wraps the physical Cover Page file, null if
the
* cover page could not be created.
*
* @throws IOException
* @throws DocumentException
*/
private Document getCoverPage(CoverLetterInfo info, String
exportFolder) {
if(!exportFolder.endsWith("\\")){
exportFolder+= "\\";
}
PdfStamper stamper = null;
Document d = null;
String coverLetterPath = exportFolder + "coverletter.pdf";
try {
InputStream is =
getClass().getClassLoader().getResourceAsStream("coverletter.pdf");
PdfReader reader = new PdfReader(is);
stamper = new PdfStamper(reader, new
FileOutputStream(coverLetterPath));
AcroFields form = stamper.getAcroFields();
form.setField("title", info.getLabelTitle());
form.setField("user", info.getLabelUser());
form.setField("folder", info.getLabelFolder());
form.setField("company", info.getLabelCompany());
form.setField("product", info.getLabelProduct());
form.setField("summary", info.getLabelSummary());
form.setField("dateRequested", info.getLabelDateRequested());
form.setField("datePrinted", info.getLabelDatePrinted());
form.setField("note", info.getLabelNote());
stamper.setFormFlattening(true);
d = new Document();
d.setDownloadedPath(coverLetterPath);
} catch(Exception e) {
logger.error("Error creating the cover page for " +
FilenameUtils.getName(exportFolder) ,e);
e.printStackTrace();
} finally {
try {stamper.close();}catch(Exception e){ logger.error("Unable
to close PdfStamper",e); }
}
return d;
}
/**
*
* @param Folder
* @param exportFolder
* @param destFilePath
* @param info
* @param doPageStamps
* @return
* @throws Exception
*/
public List<String> append(Folder Folder, String exportFolder, String
destFilePath, CoverLetterInfo info, boolean doPageStamps) {
List<String> errorDescriptions = new
ArrayList<String>();
// get the total number of pages for all documents in the export
folder
int totalPageNum = countPages(exportFolder);
info.setTotalPageNumber(String.valueOf(totalPageNum));
logger.info("Total Page Count: " + (totalPageNum + 1)); //
including the cover page
Document document = null;
PdfCopy copy = null;
try {
document = new Document();
copy = new PdfCopy(document, new
FileOutputStream(destFilePath));
document.open();
// create and add the cover page
try {
Document coverLetter = getCoverPage(info, exportFolder);
if(coverLetter != null)
{
PdfReader reader = new
PdfReader(coverLetter.getDownloadedPath());
PdfImportedPage page = copy.getImportedPage(reader,1);
copy.addPage(page);
}
}
catch(Exception e)
{
logger.error(e);
errorDescriptions.add(COVER_LETTER_ERROR);
}
// merge pdfs
logger.info("Begin merge of " + Folder.getName() + " folder
contents.");
long startTime = new Date().getTime();
errorDescriptions.addAll(
mergePDFs(copy,
copy.getRootOutline(),
Folder,
doPageStamps,
totalPageNum)
);
long endTime = new Date().getTime();
logger.info("Merge of " + Folder + " folder contents
completed. Time: " + ((endTime - startTime) / 1000) + " seconds");
} catch(Exception e){
logger.error("Unrecoverable error occurred creating base
Document for folder " + Folder, e);
throw new EbackendServiceException(e);
} finally {
if(document != null)
document.close();
}
return errorDescriptions;
}
/**
*
* @param copy
* @param outline
* @param Folder
* @param doPageStamps
* @param totalPages
* @return
*/
public List<String> mergePDFs(PdfCopy copy, PdfOutline outline,
Folder Folder, boolean doPageStamps, int totalPages) {
List<String> errorDescriptions = new ArrayList<String>();
PdfImportedPage page = null;
Font font = new Font(Font.FontFamily.HELVETICA,12.0f,Font.BOLD);
List children = Folder.getChildren();
for(Object o : children)
{
if(o instanceof Folder)
{
Folder f = (Folder) o;
if(f.getChildren().size() > 0 && f.hasDocumentDescendents()){
PdfOutline subOutline = new PdfOutline(outline,
new
PdfDestination(PdfDestination.FITH,copy.getVerticalPosition(true)),
f.getName(),
true);
subOutline.setStyle(Font.BOLD);
logger.debug("Merging contents of folder: " + f.getName());
errorDescriptions.addAll(
mergePDF(copy,subOutline,f,doPageStamps, totalPages) );
}
}
else if(o instanceof Document)
{
float startVerticalPosition = copy.getVerticalPosition(true);
int startPage = copy.getCurrentPageNumber();
String filePath = ( (Document)o).getDownloadedPath();
logger.debug("Merging document: " + filePath);
PdfReader reader = null;
PdfCopy.PageStamp stamp = null;
try
{
reader = new PdfReader(new
RandomAccessFileOrArray(filePath),null);
int pageCount = reader.getNumberOfPages();
for(int i = 1; i <= pageCount; i++){
page = copy.getImportedPage(reader,i);
if(doPageStamps){
stamp = copy.createPageStamp(page);
ColumnText.showTextAligned(
stamp.getOverContent(),
Element.ALIGN_LEFT,
new Phrase(String.format("Page %d of %d",
copy.getCurrentPageNumber() - 1, totalPages ),font),
30.0f, 20.0f, 0.0f);
stamp.alterContents();
}
copy.addPage(page);
}
}
catch(Exception e) // could be encrypted or corrupted, the
latter results in NPE within iText
{
e.printStackTrace();
String fileBaseName = FilenameUtils.getName(filePath);
logger.error("Error encountered with document " +
fileBaseName + " creating error page ",e);
// create an error page
InputStream is =
getErrorPageInputStream(fileBaseName,e.getMessage());
PdfReader reader2 = null;
try
{
reader2 = new PdfReader(is);
page = copy.getImportedPage(reader2,1);
if(doPageStamps)
{
stamp = copy.createPageStamp(page);
ColumnText.showTextAligned(
stamp.getOverContent(),
Element.ALIGN_LEFT,
new Phrase(String.format("Page %d of %d",
copy.getCurrentPageNumber() - 1, totalPages ),font),
30.0f, 20.0f, 0.0f);
stamp.alterContents();
}
copy.addPage(page);
copy.addFileAttachment(
((Document)o).getName(),null,filePath,fileBaseName );
errorDescriptions.add(String.format(MERGING_ERROR,((Document)o).getId(),e.getMessage()));
}
catch(Exception e2)
{
e2.printStackTrace();
logger.error("Issue creating error page for document " +
fileBaseName,e2);
}
finally
{
try {is.close(); } catch(Exception
e3){logger.error("Unable to close error page InputStream",e3);}
}
}
finally
{
/* based on iText source, I think this should result in an
IndexOutOfBoundsException if the page counts do not match,
but it does not */
int endingPage = copy.getCurrentPageNumber();
try {
copy.getPageReference(--endingPage);
}catch(Exception re){
logger.error("Page counts are off after adding " +
((Document)o).getName() + ": " + ((Document)o).getId());
}
// Create a bookmark for the imported document
if(endingPage > startPage)
{
new PdfOutline(outline,
new
PdfDestination(PdfDestination.FITH,startVerticalPosition),
((Document)o).getName(),
true);
}
}
}
}
return errorDescriptions;
}
}
------------------------------------------------------------------------------
Forrester Wave Report - Recovery time is now measured in hours and minutes
not days. Key insights are discussed in the 2010 Forrester Wave Report as
part of an in-depth evaluation of disaster recovery service providers.
Forrester found the best-in-class provider in terms of services and vision.
Read this report now! http://p.sf.net/sfu/ibm-webcastpromo
_______________________________________________
iText-questions mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/itext-questions
iText(R) is a registered trademark of 1T3XT BVBA.
Many questions posted to this list can (and will) be answered with a reference
to the iText book: http://www.itextpdf.com/book/
Please check the keywords list before you ask for examples:
http://itextpdf.com/themes/keywords.php