title: How to retain page labels when concatenating an existing pdf with a pdf created from scratch?
body: <p>I have a code which is creating a "cover page" and then merging it with an existing pdf. The pdf labels were lost after merging. How can I retain the pdf labels of the existing pdf and then add a page label to the pdf page created from scratch (eg "Cover page")? The example of the book I think is about retrieving and replacing page labels. I don't know how to apply this when concatenating an existing pdf with a pdf created from scratch. I am using itext 5.3.0. Thanks in advance.</p>
  
  <p><strong>EDIT</strong>
  as per comment of mkl</p>
  
  <pre><code>public ByteArrayOutputStream getConcatenatePDF()
  {
      if (bitstream == null)
          return null;
  
      if (item == null)
      {
          item = getItem();
          if (item == null)
              return null;
      }
  
      ByteArrayOutputStream byteout = null;
      InputStream coverStream = null;
  
      try
      {
          // Get Cover Page
          coverStream = getCoverStream();
          if (coverStream == null) 
              return null;
  
          byteout = new ByteArrayOutputStream();
          int pageOffset = 0;
          ArrayList&lt;HashMap&lt;String, Object&gt;&gt; master = new ArrayList&lt;HashMap&lt;String, Object&gt;&gt;();
  
          Document document = null;
          PdfCopy    writer = null;
          PdfReader  reader = null;
  
          byte[] password = (ownerpass != null &amp;&amp; !"".equals(ownerpass)) ? ownerpass.getBytes() : null;
  
          // Get infomation of the original pdf
          reader = new PdfReader(bitstream.retrieve(), password);
  
          boolean isPortfolio = reader.getCatalog().contains(PdfName.COLLECTION);
          char version = reader.getPdfVersion();
          int permissions = reader.getPermissions();
  
          // Get metadata
          HashMap&lt;String, String&gt; info = reader.getInfo();
          String title = (info.get("Title") == null || "".equals(info.get("Title")))
              ? getFieldValue("dc.title") : info.get("Title");
          String author = (info.get("Author") == null || "".equals(info.get("Author")))
              ? getFieldValue("dc.contributor.author") : info.get("Author");
          String subject = (info.get("Subject") == null || "".equals(info.get("Subject")))
              ? "" : info.get("Subject");
          String keywords = (info.get("Keywords") == null || "".equals(info.get("Keywords")))
              ? getFieldValue("dc.subject") : info.get("Keywords");
  
          reader.close();
  
          // Merge cover page and the original pdf
          InputStream[] is = new InputStream[2];
          is[0] = coverStream;
          is[1] = bitstream.retrieve();
  
          for (int i = 0; i &lt; is.length; i++) 
          {
              // we create a reader for a certain document
              reader = new PdfReader(is[i], password);
              reader.consolidateNamedDestinations();
  
              if (i == 0) 
              {
                  // step 1: creation of a document-object
                  document = new Document(reader.getPageSizeWithRotation(1));
  
                  // step 2: we create a writer that listens to the document
                  writer = new PdfCopy(document, byteout);
  
                  // Set metadata from the original pdf 
                  // the position of these lines is important
                  document.addTitle(title);
                  document.addAuthor(author);
                  document.addSubject(subject);
                  document.addKeywords(keywords);
  
                  if (pdfa)
                  {
                      // Set thenecessary information for PDF/A-1B
                      // the position of these lines is important
                      writer.setPdfVersion(PdfWriter.VERSION_1_4);
                      writer.setPDFXConformance(PdfWriter.PDFA1B);
                      writer.createXmpMetadata();
                  }
                  else if (version == '5')
                      writer.setPdfVersion(PdfWriter.VERSION_1_5);
                  else if (version == '6')
                      writer.setPdfVersion(PdfWriter.VERSION_1_6);
                  else if (version == '7')
                      writer.setPdfVersion(PdfWriter.VERSION_1_7);
                  else
                      ;  // no operation
  
                  // Set security parameters
                  if (!pdfa)
                  {
                      if (password != null)
                      {
                          if (security &amp;&amp; permissions != 0) 
                          {
                              writer.setEncryption(null, password, permissions, PdfWriter.STANDARD_ENCRYPTION_128);
                          } 
                          else
                          {
                              writer.setEncryption(null, password, PdfWriter.ALLOW_PRINTING | PdfWriter.ALLOW_COPY | PdfWriter.ALLOW_SCREENREADERS, PdfWriter.STANDARD_ENCRYPTION_128);
                          }
                      }
                  }
  
                  // step 3: we open the document
                  document.open();
  
                  // if this pdf is portfolio, does not add cover page
                  if (isPortfolio)
                  {
                      reader.close();
                      byte[] coverByte = getCoverByte();
                      if (coverByte == null || coverByte.length == 0) 
                          return null;
                      PdfCollection collection = new PdfCollection(PdfCollection.TILE);
                      writer.setCollection(collection);
  
                      PdfFileSpecification fs = PdfFileSpecification.fileEmbedded(writer, null, "cover.pdf", coverByte);
                      fs.addDescription("cover.pdf", false);
                      writer.addFileAttachment(fs);
                      continue;
                  }
              }
              int n = reader.getNumberOfPages();
              // step 4: we add content
              PdfImportedPage page;
              PdfCopy.PageStamp stamp;
              for (int j = 0; j &lt; n; )
              {
                  ++j;
                  page = writer.getImportedPage(reader, j);
                  if (i == 1) {
                      stamp = writer.createPageStamp(page);
                      Rectangle mediabox = reader.getPageSize(j);
                      Rectangle crop = new Rectangle(mediabox);
                      writer.setCropBoxSize(crop);
                      // add overlay text
                      //&lt;-- Code for adding overlay text --&gt;
                      stamp.alterContents();
                  }
                  writer.addPage(page);
              }
  
              PRAcroForm form = reader.getAcroForm();
              if (form != null &amp;&amp; !pdfa)
              {
                  writer.copyAcroForm(reader);
              }
              // we retrieve the total number of pages
              List&lt;HashMap&lt;String, Object&gt;&gt; bookmarks = SimpleBookmark.getBookmark(reader);
              //if (bookmarks != null &amp;&amp; !pdfa) 
              if (bookmarks != null) 
              {
                  if (pageOffset != 0)
                  {
                      SimpleBookmark.shiftPageNumbers(bookmarks, pageOffset, null);
                  }
                  master.addAll(bookmarks);
              }
              pageOffset += n;
          }
          if (!master.isEmpty())
          {
              writer.setOutlines(master);
          }
  
          if (isPortfolio)
          {
              reader = new PdfReader(bitstream.retrieve(), password);
              PdfDictionary catalog = reader.getCatalog();
              PdfDictionary documentnames = catalog.getAsDict(PdfName.NAMES);
              PdfDictionary embeddedfiles = documentnames.getAsDict(PdfName.EMBEDDEDFILES);
              PdfArray filespecs = embeddedfiles.getAsArray(PdfName.NAMES);
              PdfDictionary filespec;
              PdfDictionary refs;
              PRStream stream;
              PdfFileSpecification fs;
              String path;
              // copy embedded files
              for (int i = 0; i &lt; filespecs.size(); ) 
              {
                  filespecs.getAsString(i++);     // remove description
                  filespec = filespecs.getAsDict(i++);
                  refs = filespec.getAsDict(PdfName.EF);
                  for (PdfName key : refs.getKeys()) 
                  {
                      stream = (PRStream) PdfReader.getPdfObject(refs.getAsIndirectObject(key));
                      path = filespec.getAsString(key).toString();
                      fs = PdfFileSpecification.fileEmbedded(writer, null, path, PdfReader.getStreamBytes(stream));
                      fs.addDescription(path, false);
                      writer.addFileAttachment(fs);
                  }
              }
          }
  
          if (pdfa)
          {
              InputStream iccFile = this.getClass().getClassLoader().getResourceAsStream(PROFILE);
              ICC_Profile icc = ICC_Profile.getInstance(iccFile);
              writer.setOutputIntents("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1", icc);
              writer.setViewerPreferences(PdfWriter.PageModeUseOutlines);
          }
          // step 5: we close the document
          document.close();
      } 
      catch (Exception e) 
      {
          log.info(LogManager.getHeader(context, "cover_page: getConcatenatePDF", "bitstream_id="+bitstream.getID()+", error="+e.getMessage()));
          // e.printStackTrace();
          return null;
      }
  
      return byteout;
  }
  </code></pre>
  
  <p><strong>UPDATE</strong></p>
  
  <p>Based on mkl's answer, I modified the code above to look like this:</p>
  
  <pre><code>    public ByteArrayOutputStream getConcatenatePDF()
  {
      if (bitstream == null)
          return null;
  
      if (item == null)
      {
          item = getItem();
          if (item == null)
              return null;
      }
  
      ByteArrayOutputStream byteout = null;
      try
      {
          // Get Cover Page
          InputStream coverStream = getCoverStream();
          if (coverStream == null) 
              return null;
  
          byteout = new ByteArrayOutputStream();
  
          InputStream documentStream = bitstream.retrieve();
  
  
          PdfReader coverPageReader = new PdfReader(coverStream);
          PdfReader reader = new PdfReader(documentStream);
          PdfStamper stamper = new PdfStamper(reader, byteout);
  
          PdfImportedPage page = stamper.getImportedPage(coverPageReader, 1);
          stamper.insertPage(1, coverPageReader.getPageSize(1));
  
          PdfContentByte content = stamper.getUnderContent(1);
  
          int n = reader.getNumberOfPages();
          for (int j = 2; j &lt;= n; j++) {
             //code for overlay text
              ColumnText.showTextAligned(stamper.getOverContent(j), Element.ALIGN_CENTER, overlayText,
                      crop.getLeft(10), crop.getHeight() / 2 + crop.getBottom(), 90);
          }
          content.addTemplate(page, 0, 0);
          stamper.close();
      }
      catch (Exception e) 
      {
          log.info(LogManager.getHeader(context, "cover_page: getConcatenatePDF", "bitstream_id="+bitstream.getID()+", error="+e.getMessage()));
          e.printStackTrace();
          return null;
      }
  
      return byteout;
  }
  </code></pre>
  
  <p>And then I set the page labels to the cover page. I omitted code not relevant to my question.</p>
  
  <pre><code>/**
   * 
   * @return InputStream the resulting output stream
   */
  private InputStream getCoverStream()
  {
      ByteArrayOutputStream byteout = getCover();
      return new ByteArrayInputStream(byteout.toByteArray());
  }
  
  /**
   * 
   * @return InputStream the resulting output stream
   */
  private byte[] getCoverByte()
  {
      ByteArrayOutputStream byteout = getCover();
      return byteout.toByteArray();
  }
  
  /**
   * 
   * @return InputStream the resulting output stream
   */
      private ByteArrayOutputStream getCover()
  
  
  {
      ByteArrayOutputStream byteout;
      Document doc = null;
      try 
      {
          byteout = new ByteArrayOutputStream();   
          doc = new Document(PageSize.LETTER, 24, 24, 20, 40);
          PdfWriter pdfwriter = PdfWriter.getInstance(doc, byteout);
          PdfPageLabels labels = new PdfPageLabels();
          labels.addPageLabel(1, PdfPageLabels.EMPTY, "Cover page", 1);
          pdfwriter.setPageLabels(labels);
  
          pdfwriter.setPageEvent(new HeaderFooter());
          doc.open(); 
          //code omitted (contents of cover page)
          doc.close();
          return byteout; 
      } 
      catch (Exception e)
      {
          log.info(LogManager.getHeader(context, "cover_page", "bitstream_id="+bitstream.getID()+", error="+e.getMessage()));
          return null;
      }
  }
  </code></pre>
  
  <p>The modified code retained the page labels of the existing pdf (<strong>see screenshot 1</strong>) (<code>documentStream</code>), but the resulting merged pdf (<strong>screenshots 2 and 3</strong>) is off by 1 page since a cover page was inserted. As suggested by mkl, I should use page labels to the cover page, but it seems the pdf labels of the imported page was lost. My concern now is how do I set the page labels to the final document state as also suggested by mkl? I suppose I should use PdfWriter but I don't know where to put that in my modified code. Am I correct to assume that after the <code>stamper.close()</code> portion, that is the final state of my document? Thanks again in advance.</p>
  
  <p><strong>Screenshot 1.</strong> <em>Notice the actual page 1 labeled</em> <strong>Front cover</strong>
  <img src="https://i.stack.imgur.com/VWq8J.png" alt="Original"></p>
  
  <p><strong>Screenshot 2.</strong> Merged pdf, after the generated on-the-fly "cover page" was inserted. The page label "Front cover" was now assigned to the cover page even after I've set the pdf label of the inserted page using <code>labels.addPageLabel(1, PdfPageLabels.EMPTY, "Cover page", 1)</code>
  <img src="https://i.stack.imgur.com/CMFjD.png" alt="Inserted Cover page"></p>
  
  <p><strong>Screenshot 3.</strong> Note that the page label <em>3</em> was assigned to page 2.
  <img src="https://i.stack.imgur.com/2iRqg.png" alt="Page 2"></p>
  
  <p><strong>FINAL UPDATE</strong>
  Kudos to <a href="https://stackoverflow.com/users/1729265/mkl">@mkl</a>
  The screenshot below is the result after I applied the latest update of mkl's answer. The pages labels are now assigned correctly to pages. Also, using PdfStamper instead of PdfCopy (as used in my original code) did not break the PDF/A compliance of the existing pdf.</p>
  
  <p><img src="https://i.stack.imgur.com/gc1jk.png" alt="enter image description here"></p>