title: Error Merging Large PDF Files with PDFBox - Missing end of file marker '%%EOF'
body: <p>I have a successfully implemented a pdf merge solution using PDFBox using <code>InputStreams</code>.  However, when I try to merge a document that is of a very large size I receive the following error:</p>
  
  <pre><code>Caused by: java.io.IOException: Missing root object specification in trailer.
  at org.apache.pdfbox.pdfparser.COSParser.parseTrailerValuesDynamically(COSParser.java:2832) ~[pdfbox-2.0.11.jar:2.0.11]
  at org.apache.pdfbox.pdfparser.PDFParser.initialParse(PDFParser.java:173) ~[pdfbox-2.0.11.jar:2.0.11]
  at org.apache.pdfbox.pdfparser.PDFParser.parse(PDFParser.java:220) ~[pdfbox-2.0.11.jar:2.0.11]
  at org.apache.pdfbox.pdmodel.PDDocument.load(PDDocument.java:1144) ~[pdfbox-2.0.11.jar:2.0.11]
  at org.apache.pdfbox.pdmodel.PDDocument.load(PDDocument.java:1060) ~[pdfbox-2.0.11.jar:2.0.11]
  at org.apache.pdfbox.multipdf.PDFMergerUtility.legacyMergeDocuments(PDFMergerUtility.java:379) ~[pdfbox-2.0.11.jar:2.0.11]
  at org.apache.pdfbox.multipdf.PDFMergerUtility.mergeDocuments(PDFMergerUtility.java:280) ~[pdfbox-2.0.11.jar:2.0.11]
  </code></pre>
  
  <p>Of more importance (I think) are these statements that occur just before the error:</p>
  
  <pre><code>FINE (pdfparser.COSParser) [] - Missing end of file marker '%%EOF'
  FINE (pdfparser.COSParser) [] - Set missing offset 388 for object 2 0 R
  </code></pre>
  
  <p>It seems to me that it can't find the <code>'%%EOF'</code> marker in very large files.  Now I know that it is indeed there because I can look at the source (unfortunately I can't provide the file itself).</p>
  
  <p>Doing some searching online I found that there is a <code>setEOFLookupRange()</code> method on the <code>COSParser</code> class.  I'm wondering if perhaps the lookup range is too small and that is why it can't find the <code>'%%EOF'</code> marker.  The problem is...I'm not using the <code>COSParser</code> object at all in my code; I'm only using the <code>PDFMergerUtility</code> class. The <code>PDFMergerUtility</code> seems to be using the <code>COSParser</code> under the hood.</p>
  
  <p>So my questions are </p>
  
  <ol>
  <li>Is my hypothesis about the <code>EOFLookupRange</code> correct?</li>
  <li>If so, how can I set that range only having the <code>PDFMergerUtility</code> in my code and not the <code>COSParser</code> object?</li>
  </ol>
  
  <p>Many thanks for your time!</p>
  
  <h3>UPDATED with code below</h3>
  
  <pre><code> private boolean getCoolDocuments(final String slateId, final String filePathAndName)
              throws IOException {
  
          boolean status = false;
          InputStream pdfStream = null;
          HttpURLConnection connection = null;
          final PDFMergerUtility merger = new PDFMergerUtility();
          final ByteArrayOutputStream mergedPdfOutputStream = new ByteArrayOutputStream();
  
          try {
  
              final List&lt;SlateDocument&gt; parsedSlateDocuments = this.getSpecificDocumentsFromSlate(slateId);
  
              if (!parsedSlateDocuments.isEmpty()) {
  
                  // iterate through each document, adding each pdf stream to the merger utility
                  int numberOfDocuments = 0;
                  for (final SlateDocument slateDocument : parsedSlateDocuments) {
  
                      final String url = this.getBaseURL() + "/slate/" + slateId + "/documents/"
                              + slateDocument.getDocumentId();
  
                       /* code for RequestResponseUtil.initializeRequest(...) below */
                      connection = RequestResponseUtil.initializeRequest(url, "GET", this.getAuthenticationHeader(),
                              true, MediaType.APPLICATION_PDF_VALUE);
  
                      if (RequestResponseUtil.isSuccessful(connection.getResponseCode())) {
                          pdfStream = connection.getInputStream();
  
                      }
                      else {
                          /* do various things */
                      }
  
                      merger.addSource(pdfStream);
                      numberOfDocuments++;
                  }
  
                  merger.setDestinationStream(mergedPdfOutputStream);
  
                  // merge the all the pdf streams together
                 merger.mergeDocuments(MemoryUsageSetting.setupTempFileOnly());
  
                 status = true;
              }
              else {
                  LOG.severe("An error occurred while parsing the slated documents; no documents remain after parsing!");
              }
          }
          finally {
              RequestResponseUtil.close(pdfStream);
  
              this.disconnect(connection);
          }
  
          return status;
      }
  
     public static HttpURLConnection initializeRequest(final String url, final String method,
              final String httpAuthHeader, final boolean multiPartFormData, final String reponseType) {
  
      HttpURLConnection conn = null;
  
      try {
          conn = (HttpURLConnection) new URL(url).openConnection();
          conn.setRequestMethod(method);
          conn.setRequestProperty("X-Slater-Authentication", httpAuthHeader);
          conn.setRequestProperty("Accept", reponseType);
          if (multiPartFormData) {
              conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=BOUNDARY");
              conn.setDoOutput(true);
          }
          else {
              conn.setRequestProperty("Content-Type", "application/xml");
          }
      }
      catch (final MalformedURLException e) {
          throw new CustomException(e);
      }
      catch (final IOException e) {
          throw new CustomException(e);
      }
      return conn;
  
  }
  </code></pre>