title: Adding a layer to PDF containing layers with PdfStamper flattens existing layers
body: <p>I create PDF files for print containing a non-printable background layer of the stationery, so it will look the same printed and on screen. </p>
  
  <p><a href="https://i.stack.imgur.com/SOyPB.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/SOyPB.png" alt="enter image description here"></a></p>
  
  <p>Example to be found <a href="https://drive.google.com/file/d/0B9YJlOxlbTanMFBLOFMxUUVCSUU/view?usp=sharing" rel="nofollow noreferrer">here</a></p>
  
  <p>Now once the prepared PDF is approved for print, I want to add a barcode containing archive / approval data - again as a layer not printed. </p>
  
  <p>But once I do, all existing layers are gone and only the layer added remains, causing the not-printable background layer to suddenly be printed.</p>
  
  <p>See <a href="https://drive.google.com/file/d/0B9YJlOxlbTanS0pFdk80SGhXUVk/view?usp=sharing" rel="nofollow noreferrer">here</a> for my (failed) result</p>
  
  <p>Here's my addBarcode method:</p>
  
  <pre><code>public static byte[] addBarcode_OriginalIdea(byte[] src, String barcodeText, String layerName, boolean printable) {
      try (ByteArrayOutputStream bo = new ByteArrayOutputStream()) {
          PdfReader reader = new PdfReader(src);                      
          PdfStamper stamper = new PdfStamper(reader, bo);
          //Add an additional layer to Page 1
          PdfContentByte over = stamper.getOverContent(1);
          Barcode128 barcode = new Barcode128();
          barcode.setCode(barcodeText);
          barcode.setBarHeight(10f);                     
          Image img = barcode.createImageWithBarcode(over, Color.decode("#0c1754"), Color.decode("#0c1754"));
          img.setRotationDegrees(-90f);            
          img.setAbsolutePosition(reader.getPageSize(1).getRight()-img.getHeight()-5,  5);
          PdfLayer barcodeLayer = new PdfLayer(layerName, stamper.getWriter());
          barcodeLayer.setOnPanel(true);
          barcodeLayer.setPrint("Print", printable);
          over.beginLayer(barcodeLayer);
          over.addImage(img);
          over.endLayer();
          //End add layer
          stamper.close();
          bo.close();
          return bo.toByteArray();
      } catch (Exception e) {
          e.printStackTrace();
          return new byte[]{};
      }
  }
  </code></pre>
  
  <p>Confused. So what did I try next?</p>
  
  <ol>
  <li><p>Remove the code to add the layer - keep the stamper part
  Result: Layers are still there.</p></li>
  <li><p>Print the layers before and after adding the new layer:
  Result: I can see the new layer added, but PdfStamper throws an Exception during close.</p></li>
  </ol>
  
  <p>Code for that: </p>
  
  <pre><code>public static byte[] addBarcode_showLayersBeforeAfter(byte[] src, String barcodeText, String layerName, boolean printable) {
      try (ByteArrayOutputStream bo = new ByteArrayOutputStream()) {
          PdfReader reader = new PdfReader(src);                      
          PdfStamper stamper = new PdfStamper(reader, bo);
          //List all layers contained in document before
          Map map = stamper.getPdfLayers();
          for(Object me: map.keySet()) {
              System.out.println("Layer before: " + me);
          }                       
  
          //Add an additional layer to Page 1
          PdfContentByte over = stamper.getOverContent(1);
          Barcode128 barcode = new Barcode128();
          barcode.setCode(barcodeText);
          barcode.setBarHeight(10f);                     
          Image img = barcode.createImageWithBarcode(over, Color.decode("#0c1754"), Color.decode("#0c1754"));
          img.setRotationDegrees(-90f);            
          img.setAbsolutePosition(reader.getPageSize(1).getRight()-img.getHeight()-5,  5);
          PdfLayer barcodeLayer = new PdfLayer(layerName, stamper.getWriter());
          barcodeLayer.setOnPanel(true);
          barcodeLayer.setPrint("Print", printable);
          over.beginLayer(barcodeLayer);
          over.addImage(img);
          over.endLayer();            
          //End add layer
  
          //List all layers contained in document before
          Map map2 = stamper.getPdfLayers();
          for(Object me: map2.keySet()) {
              System.out.println("Layer after: " + me);
          }
          reader.close();
          stamper.close(); // &lt;-- this now throws an Exception!
          bo.close();
          return bo.toByteArray();
      } catch (Exception e) {
          e.printStackTrace();
          return new byte[]{};
      }
  }
  </code></pre>
  
  <p>This produces this output:</p>
  
  <pre><code>Layer before: Footer-2
  Layer before: Template-Page-0
  Layer before: Template-Page-2
  Layer before: Template-Page-1
  Layer before: Footer-0
  Layer before: Footer-1
  Layer after: Footer-2
  Layer after: Template-Page-0
  Layer after: StackLayer
  Layer after: Template-Page-2
  Layer after: Template-Page-1
  Layer after: Footer-0
  Layer after: Footer-1
  java.lang.NullPointerException
      at com.lowagie.text.pdf.PdfWriter.fillOCProperties(Unknown Source)
      at com.lowagie.text.pdf.PdfStamperImp.close(Unknown Source)
      at com.lowagie.text.pdf.PdfStamper.close(Unknown Source)
      at BarcodeLayer.addBarcode_showLayersBeforeAfter(BarcodeLayer.java:119)
      at BarcodeLayer.main(BarcodeLayer.java:44)  
  </code></pre>
  
  <p>I'd really, really hate to have to recreate the whole PDF + barcode at this point just to get the layers straight, does anyone have an idea how to add another non-printable layer in PdfStamper (or some other way) while keeping all existing layers?</p>
  
  <p>Thank you so much!</p>
  
  <p><strong>EDIT</strong></p>
  
  <p>With Bruno's advice to move to itext Version 5 it becomes even more strange. Now I can see the layer being added to the PDF. Now the background stays unprinted (as wanted) but the added barcode layer is <strong>always</strong> printed. I used this code to analyse the layers after my adding the barcode:</p>
  
  <pre><code>        Map map2 = stamper.getPdfLayers();
          for(Object me: map2.keySet()) {
              System.out.println("Layer after: " + me );
              PdfDictionary dict2 = ((PdfLayer)map2.get(me)).getAsDict(PdfName.USAGE);
              if(dict2 != null) {
                  PdfDictionary dict = dict2.getAsDict(PdfName.PRINT);
                  System.out.println("Subtype" + dict.getAsName(PdfName.SUBTYPE));
                  System.out.println("Printable: " + dict.getAsName(PdfName.PRINTSTATE));
              }
          }
  </code></pre>
  
  <p>Resulting in this for the added layer:</p>
  
  <pre><code>Printable: /OFF
  Layer after: StackLayer
  Subtype/Print
  </code></pre>
  
  <p>But still - on hitting print preview the layer is still there. Now I used the just created pdf and listed the layers - and the Print-Attribute is gone:</p>
  
  <pre><code>Layer before: StackLayer
  Layer before: Template-Page-2
  </code></pre>
  
  <p>So whatever happens seems to happen in closing the PdfStamper - like not properly updating PdfLayers or such. I think I'll have to look into itext source next :-(</p>