/**
  * The document catalog can also have a /Version parameter which overrides the version specified
  * in the header if, and only if it is greater.
  *
  * @param parsedTrailer the parsed catalog in the trailer
  */
 private void readVersionInTrailer(COSDictionary parsedTrailer) {
   COSObject root = (COSObject) parsedTrailer.getItem(COSName.ROOT);
   if (root != null) {
     COSName version = (COSName) root.getItem(COSName.VERSION);
     if (version != null) {
       float trailerVersion = Float.valueOf(version.getName());
       if (trailerVersion > document.getVersion()) {
         document.setVersion(trailerVersion);
       }
     }
   }
 }
  /**
   * This will parse the next object from the stream and add it to the local state.
   *
   * @return Returns true if the processed object had an endOfFile marker
   * @throws IOException If an IO error occurs.
   */
  private boolean parseObject() throws IOException {
    long currentObjByteOffset = pdfSource.getOffset();
    boolean isEndOfFile = false;
    skipSpaces();
    // peek at the next character to determine the type of object we are parsing
    char peekedChar = (char) pdfSource.peek();

    // ignore endobj and endstream sections.
    while (peekedChar == 'e') {
      // there are times when there are multiple endobj, so lets
      // just read them and move on.
      readString();
      skipSpaces();
      currentObjByteOffset = pdfSource.getOffset();
      peekedChar = (char) pdfSource.peek();
    }
    if (pdfSource.isEOF()) {
      // "Skipping because of EOF" );
      // end of file we will return a false and call it a day.
    }
    // xref table. Note: The contents of the Xref table are currently ignored
    else if (peekedChar == 'x') {
      parseXrefTable(currentObjByteOffset);
    }
    // Note: startxref can occur in either a trailer section or by itself
    else if (peekedChar == 't' || peekedChar == 's') {
      if (peekedChar == 't') {
        parseTrailer();
        peekedChar = (char) pdfSource.peek();
      }
      if (peekedChar == 's') {
        parseStartXref();
        // readString() calls skipSpaces() will skip comments... that's
        // bad for us b/c the %%EOF flag is a comment
        while (isWhitespace(pdfSource.peek()) && !pdfSource.isEOF()) {
          pdfSource.read(); // read (get rid of) all the whitespace
        }
        String eof = "";
        if (!pdfSource.isEOF()) {
          eof = readLine(); // if there's more data to read, get the EOF flag
        }

        // verify that EOF exists (see PDFBOX-979 for documentation on special cases)
        if (!"%%EOF".equals(eof)) {
          if (eof.startsWith("%%EOF")) {
            // content after marker -> unread with first space byte for read newline
            pdfSource.unread(SPACE_BYTE); // we read a whole line; add space as newline replacement
            pdfSource.unread(eof.substring(5).getBytes("ISO-8859-1"));
          } else {
            // PDF does not conform to spec, we should warn someone
            Log.w("expected='%%EOF' actual='" + eof + "'", PDFBox.LOG_TAG);
            // if we're not at the end of a file, just put it back and move on
            if (!pdfSource.isEOF()) {
              pdfSource.unread(
                  SPACE_BYTE); // we read a whole line; add space as newline replacement
              pdfSource.unread(eof.getBytes("ISO-8859-1"));
            }
          }
        }
        isEndOfFile = true;
      }
    }
    // we are going to parse an normal object
    else {
      long number = -1;
      int genNum = -1;
      String objectKey = null;
      boolean missingObjectNumber = false;
      try {
        char peeked = (char) pdfSource.peek();
        if (peeked == '<') {
          missingObjectNumber = true;
        } else {
          number = readObjectNumber();
        }
      } catch (IOException e) {
        // ok for some reason "GNU Ghostscript 5.10" puts two endobj
        // statements after an object, of course this is nonsense
        // but because we want to support as many PDFs as possible
        // we will simply try again
        number = readObjectNumber();
      }
      if (!missingObjectNumber) {
        skipSpaces();
        genNum = readGenerationNumber();

        objectKey = readString(3);
        // System.out.println( "parseObject() num=" + number +
        // " genNumber=" + genNum + " key='" + objectKey + "'" );
        if (!objectKey.equals("obj")) {
          if (!isContinueOnError(null) || !objectKey.equals("o")) {
            throw new IOException("expected='obj' actual='" + objectKey + "' " + pdfSource);
          }
          // assume that "o" was meant to be "obj" (this is a workaround for
          // PDFBOX-773 attached PDF Andersens_Fairy_Tales.pdf).
        }
      } else {
        number = -1;
        genNum = -1;
      }

      skipSpaces();
      COSBase pb = parseDirObject();
      String endObjectKey = readString();

      if (endObjectKey.equals("stream")) {
        pdfSource.unread(endObjectKey.getBytes("ISO-8859-1"));
        pdfSource.unread(' ');
        if (pb instanceof COSDictionary) {
          pb = parseCOSStream((COSDictionary) pb, getDocument().getScratchFile());

          // test for XRef type
          final COSStream strmObj = (COSStream) pb;
          final COSName objectType = (COSName) strmObj.getItem(COSName.TYPE);
          if (objectType != null && objectType.equals(COSName.XREF)) {
            // XRef stream
            parseXrefStream(strmObj, currentObjByteOffset);
          }
        } else {
          // this is not legal
          // the combination of a dict and the stream/endstream forms a complete stream object
          throw new IOException("stream not preceded by dictionary");
        }
        skipSpaces();
        endObjectKey = readLine();
      }

      COSObjectKey key = new COSObjectKey(number, genNum);
      COSObject pdfObject = document.getObjectFromPool(key);
      if (pdfObject.getObject() == null) {
        pdfObject.setObject(pb);
      }
      /*
       * If the object we returned already has a baseobject, then we have a conflict
       * which we will resolve using information after we parse the xref table.
       */
      else {
        addObjectToConflicts(currentObjByteOffset, key, pb);
      }

      if (!endObjectKey.equals("endobj")) {
        if (endObjectKey.startsWith("endobj")) {
          /*
           * Some PDF files don't contain a new line after endobj so we
           * need to make sure that the next object number is getting read separately
           * and not part of the endobj keyword. Ex. Some files would have "endobj28"
           * instead of "endobj"
           */
          pdfSource.unread(
              SPACE_BYTE); // add a space first in place of the newline consumed by readline()
          pdfSource.unread(endObjectKey.substring(6).getBytes("ISO-8859-1"));
        } else if (endObjectKey.trim().endsWith("endobj")) {
          /*
           * Some PDF files contain junk (like ">> ", in the case of a PDF
           * I found which was created by Exstream Dialogue Version 5.0.039)
           * in which case we ignore the data before endobj and just move on
           */
          Log.w("expected='endobj' actual='" + endObjectKey + "' ", PDFBox.LOG_TAG);
        } else if (!pdfSource.isEOF()) {
          // It is possible that the endobj is missing, there
          // are several PDFs out there that do that so. Unread
          // and assume that endobj was missing
          pdfSource.unread(
              SPACE_BYTE); // add a space first in place of the newline consumed by readline()
          pdfSource.unread(endObjectKey.getBytes("ISO-8859-1"));
        }
      }
      skipSpaces();
    }
    return isEndOfFile;
  }