title: How To Remove Whitespace on Merge
body: <p>I have some code that takes 3 different PDF byte arrays and merges them.  This code works great.  The issue (some people) are having is that each PDF is considered to be a full page (if printed) even if there is only say 4 inches of content on it, thus leaving 7 inches of white space vertically.  Then the middle document gets put in and may or may not have vertical white space at the end of it.  Then the footer gets put on its own page as well.</p>
  <p>Here is the code:</p>
  <pre><code>byte[] Bytes = rv.LocalReport.Render(&quot;PDF&quot;, null, out MimeType, out Encoding, out Extension, out StreamIDs, out Warnings);
  List&lt;byte[]&gt; MergeSets = // This is filled prior to this code
  
  // Append any other pages to this primary letter
  if (MergeSets.Count &gt; 0) {
    MemoryStream ms = new MemoryStream();
    Document document = new Document();
    PdfCopy copy = new PdfCopy(document, ms);
    document.Open();
    PdfImportedPage page;
    PdfReader reader = new PdfReader(Bytes); // read the generated primary Letter
    int pages = reader.NumberOfPages;
  
    for (int i = 0; i &lt; pages; ) {
      page = copy.GetImportedPage(reader, ++i);
      copy.AddPage(page);
    } // foreach of the pages in the Cover Letter
  
    // Now append the merge sets
    foreach (byte[] ba in MergeSets) {
      reader = new PdfReader(ba);
      pages = reader.NumberOfPages;
  
      for (int i = 0; i &lt; pages; ) {
        page = copy.GetImportedPage(reader, ++i);
        copy.AddPage(page);
      } // foreach of the pages in the current merge set
    } // foreach of the sets of data
  
    document.Close();
  
    ServerSaved = SaveGeneratedLetter(ms.GetBuffer(), DateTime.Now.Year, hl.LetterName, SaveName);
  } // if there is anything to merge
  </code></pre>
  <p>Is there a way when I am merging each page to clip/remove/erase the vertical white space at the end of each pdf so it appears as one seamless document?</p>
  <blockquote>
  <b>UPDATE:</b>
  Here are some sample .pdf files I am trying to merge.
  </blockquote>
  <p><a href="http://1drv.ms/1B8VIVM" rel="nofollow noreferrer">header</a>, <a href="http://1drv.ms/1B8VDBo" rel="nofollow noreferrer">body</a>, <a href="http://1drv.ms/1B8VPkb" rel="nofollow noreferrer">footer</a></p>
  <blockquote>
  <p><b>UPDATE 2: USING THE ANSWER:</b></p>
  <p>I have converted @mkl's code to C# and here it is.</p>
  <p>The tool class:</p>
  </blockquote>
  <pre><code>public class PdfVeryDenseMergeTool {
  
    private Rectangle PageSize;
    private float TopMargin;
    private float BottomMargin;
    private float Gap;
    private Document Document = null;
    private PdfWriter Writer = null;
    private float YPosition = 0;
  
    public PdfVeryDenseMergeTool(Rectangle size, float top, float bottom, float gap) {
      this.PageSize = size;
      this.TopMargin = top;
      this.BottomMargin = bottom;
      this.Gap = gap;
    } // PdfVeryDenseMergeTool
  
    public void Merge(MemoryStream outputStream, List&lt;PdfReader&gt; inputs) {
      try {
        this.OpenDocument(outputStream);
  
        foreach (PdfReader reader in inputs) {
          this.Merge(reader);
        } // foreach of the PDF files to merge
      } finally {
        this.CloseDocument();
      } // try-catch-finally
    } // Merge
  
    public void OpenDocument(MemoryStream outputStream) {
      this.Document = new Document(PageSize, 36, 36, this.TopMargin, this.BottomMargin);
      this.Writer = PdfWriter.GetInstance(Document, outputStream);
  
      this.Document.Open();
      this.NewPage();
    } // OpenDocument
  
    public void CloseDocument() {
      try {
        this.Document.Close();
      } finally {
        this.Document = null;
        this.Writer = null;
        this.YPosition = 0;
      } // try-finally
    } // CloseDocument
  
    public void NewPage() {
      this.Document.NewPage();
      this.YPosition = PageSize.GetTop(this.TopMargin);
    } // Merge
  
    public void Merge(PdfReader reader) {
      PdfReaderContentParser parser = new PdfReaderContentParser(reader);
  
      for (int pageIndex = 1; pageIndex &lt;= reader.NumberOfPages; pageIndex++) {
        this.Merge(reader, parser, pageIndex);
      } // foreach of the pages of the current PDF
    } // Merge
  
    public void Merge(PdfReader reader, PdfReaderContentParser parser, int pageIndex) {
      PdfImportedPage importedPage = Writer.GetImportedPage(reader, pageIndex);
      PdfContentByte directContent = Writer.DirectContent;
  
      PageVerticalAnalyzer finder = parser.ProcessContent(pageIndex, new PageVerticalAnalyzer());
  
      if (finder.VerticalFlips.Count &lt; 2)
        return;
  
      Rectangle pageSizeToImport = reader.GetPageSize(pageIndex);
  
      int startFlip = finder.VerticalFlips.Count - 1;
      bool first = true;
  
      while (startFlip &gt; 0) {
        if (!first)
          this.NewPage();
  
        float freeSpace = this.YPosition - PageSize.GetBottom(BottomMargin);
        int endFlip = startFlip + 1;
  
        while ((endFlip &gt; 1) &amp;&amp; (finder.VerticalFlips[startFlip] - finder.VerticalFlips[endFlip - 2] &lt; freeSpace))
          endFlip -= 2;
  
        if (endFlip &lt; startFlip) {
          float height = finder.VerticalFlips[startFlip] - finder.VerticalFlips[endFlip];
  
          directContent.SaveState();
          directContent.Rectangle(0, this.YPosition - height, pageSizeToImport.Width, height);
          directContent.Clip();
          directContent.NewPath();
  
          this.Writer.DirectContent.AddTemplate(importedPage, 0, this.YPosition - (finder.VerticalFlips[startFlip] - pageSizeToImport.Bottom));
  
          directContent.RestoreState();
          this.YPosition -= height + this.Gap;
          startFlip = endFlip - 1;
        } else if (!first) {
          throw new ArgumentException(string.Format(&quot;Page {0} content too large&quot;, pageIndex));
        } // if
  
        first = false;
      } // while
    } // Merge
  } // PdfVeryDenseMergeTool
  </code></pre>
  <blockquote>
  <p>The RenderListener class:<br />
  <b>UPDATE 3:</b> FIXED 1 LINE OF CODE AND IT WORKS:  See comment in code</p>
  </blockquote>
  <pre><code>public class PageVerticalAnalyzer : IRenderListener {
  
    public PageVerticalAnalyzer() { }
  
    public List&lt;float&gt; VerticalFlips = new List&lt;float&gt;();
  
    public void AddVerticalUseSection(float from, float to) {
      if (to &lt; from) {
        float temp = to;
        to = from;
        from = temp;
      }
  
      int i = 0;
      int j = 0;
  
      for (i = 0; i &lt; VerticalFlips.Count; i++) {
        float flip = VerticalFlips[i];
        if (flip &lt; from)
          continue;
  
        for (j = i; j &lt; VerticalFlips.Count; j++) {
          flip = VerticalFlips[j];
          if (flip &lt; to)
            continue;
          break;
        }
        break;
      } // foreach of the vertical flips
  
      bool fromOutsideInterval = i % 2 == 0;
      bool toOutsideInterval = j % 2 == 0;
  
      while (j-- &gt; i)
        VerticalFlips.RemoveAt(j); // This was the problem line with just .Remove(j)
      if (toOutsideInterval)
        VerticalFlips.Insert(i, to);
      if (fromOutsideInterval)
        VerticalFlips.Insert(i, from);
    } // AddVerticalUseSection
  
    public void BeginTextBlock() { /* Do nothing */  }
  
    public void EndTextBlock() { /* Do nothing */ }
  
    public void RenderImage(ImageRenderInfo renderInfo) {
      Matrix ctm = renderInfo.GetImageCTM();
      List&lt;float&gt; YCoords = new List&lt;float&gt;(4) { 0, 0, 0, 0 };
  
      for (int x = 0; x &lt; 2; x++) {
        for (int y = 0; y &lt; 2; y++) {
          Vector corner = new Vector(x, y, 1).Cross(ctm);
          YCoords[2 * x + y] = corner[Vector.I2];
        }
      }
  
      YCoords.Sort();
      AddVerticalUseSection(YCoords[0], YCoords[3]);
    } // RenderImage
  
    public void RenderText(TextRenderInfo renderInfo) {
      LineSegment ascentLine = renderInfo.GetAscentLine();
      LineSegment descentLine = renderInfo.GetDescentLine();
      List&lt;float&gt; YCoords = new List&lt;float&gt;(4) {
        ascentLine.GetStartPoint()[Vector.I2],
        ascentLine.GetEndPoint()[Vector.I2],
        descentLine.GetStartPoint()[Vector.I2],
        descentLine.GetEndPoint()[Vector.I2],
      };
  
      YCoords.Sort();
      AddVerticalUseSection(YCoords[0], YCoords[3]);
    } // RenderText
  } // PageVericalAnalyzer
  </code></pre>
  <blockquote>
  <p>Code to gather files and run the tool:</p>
  </blockquote>
  <pre><code>public void TestMergeDocuments() {
    PdfVeryDenseMergeTool tool = new PdfVeryDenseMergeTool(iTextSharp.text.PageSize.A4, 18, 18, 10);
    List&lt;byte[]&gt; Files = new List&lt;byte[]&gt;();
  
    // Code to load each of the 3 files I need into this byte array list
    
    using (MemoryStream ms = new MemoryStream()) {
      List&lt;PdfReader&gt; files = new List&lt;PdfReader&gt;();
  
      foreach (byte[] ba in Files) {
        files.Add(new PdfReader(ba));
      } // foreach of the sets of data
  
      tool.Merge(ms, files);
  
      // Save the file using: ms.GetBuffer()
    } // using the memory stream
  } // TestMergeDocuments
  </code></pre>