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

Reply via email to