/**
   * read the cross reference table from a PDF file. When this method is called, the file pointer
   * must point to the start of the word "xref" in the file. Reads the xref table and the trailer
   * dictionary. If dictionary has a /Prev entry, move file pointer and read new trailer
   *
   * @param password
   */
  private void readTrailer(PDFPassword password)
      throws IOException, PDFAuthenticationFailureException,
          EncryptionUnsupportedByProductException, EncryptionUnsupportedByPlatformException {
    // the table of xrefs
    this.objIdx = new PDFXref[50];

    int pos = this.buf.position();

    PDFDecrypter newDefaultDecrypter = null;

    // read a bunch of nested trailer tables
    while (true) {
      // make sure we are looking at an xref table
      if (!nextItemIs("xref")) {
        this.buf.position(pos);
        readTrailer15(password);
        return;
        //                throw new PDFParseException("Expected 'xref' at start of table");
      }

      // read a bunch of linked tabled
      while (true) {
        // read until the word "trailer"
        PDFObject obj = readObject(-1, -1, IdentityDecrypter.getInstance());
        if (obj.getType() == PDFObject.KEYWORD && obj.getStringValue().equals("trailer")) {
          break;
        }

        // read the starting position of the reference
        if (obj.getType() != PDFObject.NUMBER) {
          throw new PDFParseException("Expected number for first xref entry");
        }
        int refstart = obj.getIntValue();

        // read the size of the reference table
        obj = readObject(-1, -1, IdentityDecrypter.getInstance());
        if (obj.getType() != PDFObject.NUMBER) {
          throw new PDFParseException("Expected number for length of xref table");
        }
        int reflen = obj.getIntValue();

        // skip a line
        readLine();

        if (refstart == 1) { // Check and try to fix incorrect Object Number Start
          int startPos = this.buf.position();
          try {
            byte[] refline = new byte[20];
            this.buf.get(refline);
            if (refline[17] == 'f') { // free
              PDFXref objIndex = new PDFXref(refline);
              if (objIndex.getID() == 0
                  && objIndex.getGeneration() == 65535) { // The highest generation number possible
                refstart--;
              }
            }
          } catch (Exception e) { // in case of error ignore
          }
          this.buf.position(startPos);
        }

        // extend the objIdx table, if necessary
        if (refstart + reflen >= this.objIdx.length) {
          PDFXref nobjIdx[] = new PDFXref[refstart + reflen];
          System.arraycopy(this.objIdx, 0, nobjIdx, 0, this.objIdx.length);
          this.objIdx = nobjIdx;
        }

        // read reference lines
        for (int refID = refstart; refID < refstart + reflen; refID++) {
          // each reference line is 20 bytes long
          byte[] refline = new byte[20];
          this.buf.get(refline);

          // ignore this line if the object ID is already defined
          if (this.objIdx[refID] != null) {
            continue;
          }

          // see if it's an active object
          if (refline[17] == 'n') {
            this.objIdx[refID] = new PDFXref(refline);
          } else {
            this.objIdx[refID] = new PDFXref(null);
          }
        }
      }

      // at this point, the "trailer" word (not EOL) has been read.
      PDFObject trailerdict = readObject(-1, -1, IdentityDecrypter.getInstance());
      if (trailerdict.getType() != PDFObject.DICTIONARY) {
        throw new IOException("Expected dictionary after \"trailer\"");
      }

      // read the root object location
      if (this.root == null) {
        this.root = trailerdict.getDictRef("Root");
        if (this.root != null) {
          this.root.setObjectId(PDFObject.OBJ_NUM_TRAILER, PDFObject.OBJ_NUM_TRAILER);
        }
      }

      // read the encryption information
      if (this.encrypt == null) {
        this.encrypt = trailerdict.getDictRef("Encrypt");
        if (this.encrypt != null) {
          this.encrypt.setObjectId(PDFObject.OBJ_NUM_TRAILER, PDFObject.OBJ_NUM_TRAILER);
        }

        if (this.encrypt != null && !PDFDecrypterFactory.isFilterExist(this.encrypt)) {
          this.encrypt = null; // the filter is not located at this trailer, we will try later again
        } else {
          newDefaultDecrypter =
              PDFDecrypterFactory.createDecryptor(
                  this.encrypt, trailerdict.getDictRef("ID"), password);
        }
      }

      if (this.info == null) {
        this.info = trailerdict.getDictRef("Info");
        if (this.info != null) {
          if (!this.info.isIndirect()) {
            throw new PDFParseException("Info in trailer must be an indirect reference");
          }
          this.info.setObjectId(PDFObject.OBJ_NUM_TRAILER, PDFObject.OBJ_NUM_TRAILER);
        }
      }

      // support for hybrid-PDFs containing an additional compressed-xref-stream
      PDFObject xrefstmPos = trailerdict.getDictRef("XRefStm");
      if (xrefstmPos != null) {
        int pos14 = this.buf.position();
        this.buf.position(xrefstmPos.getIntValue());
        readTrailer15(password);
        this.buf.position(pos14);
      }

      // read the location of the previous xref table
      PDFObject prevloc = trailerdict.getDictRef("Prev");
      if (prevloc != null) {
        this.buf.position(prevloc.getIntValue());
      } else {
        break;
      }
      // see if we have an optional Version entry

      if (this.root.getDictRef("Version") != null) {
        processVersion(this.root.getDictRef("Version").getStringValue());
      }
    }

    // make sure we found a root
    if (this.root == null) {
      throw new PDFParseException("No /Root key found in trailer dictionary");
    }

    if (this.encrypt != null && newDefaultDecrypter != null) {
      PDFObject permissions = this.encrypt.getDictRef("P");
      if (permissions != null && !newDefaultDecrypter.isOwnerAuthorised()) {
        int perms = permissions != null ? permissions.getIntValue() : 0;
        if (permissions != null) {
          this.printable = (perms & 4) != 0;
          this.saveable = (perms & 16) != 0;
        }
      }
      // Install the new default decrypter only after the trailer has
      // been read, as nothing we're reading passing through is encrypted
      this.defaultDecrypter = newDefaultDecrypter;
    }

    // dereference the root object
    this.root.dereference();
  }
Beispiel #2
0
 public boolean resolves(PDFXref ref) {
   // it's expected that the object number is relevant
   return type != Type.FREE && generation == ref.getGeneration();
 }
  /**
   * Used internally to track down PDFObject references. You should never need to call this.
   *
   * <p>Since this is the only public method for tracking down PDF objects, it is synchronized. This
   * means that the PDFFile can only hunt down one object at a time, preventing the file's location
   * from getting messed around.
   *
   * <p>This call stores the current buffer position before any changes are made and restores it
   * afterwards, so callers need not know that the position has changed.
   */
  public synchronized PDFObject dereference(PDFXref ref, PDFDecrypter decrypter)
      throws IOException {
    int id = ref.getID();

    // make sure the id is valid and has been read
    if (id >= this.objIdx.length || this.objIdx[id] == null) {
      return PDFObject.nullObj;
    }

    // check to see if this is already dereferenced
    PDFObject obj = this.objIdx[id].getObject();
    if (obj != null) {
      return obj;
    }

    // store the current position in the buffer
    int startPos = this.buf.position();

    boolean compressed = this.objIdx[id].getCompressed();
    if (!compressed) {
      int loc = this.objIdx[id].getFilePos();
      if (loc < 0) {
        return PDFObject.nullObj;
      }

      // move to where this object is
      this.buf.position(loc);

      // read the object and cache the reference
      obj = readObject(ref.getID(), ref.getGeneration(), decrypter);
    } else { // compressed
      int compId = this.objIdx[id].getID();
      int idx = this.objIdx[id].getIndex();
      if (idx < 0) return PDFObject.nullObj;
      PDFXref compRef = new PDFXref(compId, 0);
      PDFObject compObj = dereference(compRef, decrypter);
      int first = compObj.getDictionary().get("First").getIntValue();
      int length = compObj.getDictionary().get("Length").getIntValue();
      int n = compObj.getDictionary().get("N").getIntValue();
      if (idx >= n) return PDFObject.nullObj;
      ByteBuffer strm = compObj.getStreamBuffer();

      ByteBuffer oldBuf = this.buf;
      this.buf = strm;
      // skip other nums
      for (int i = 0; i < idx; i++) {
        PDFObject skip1num = readObject(-1, -1, true, IdentityDecrypter.getInstance());
        PDFObject skip2num = readObject(-1, -1, true, IdentityDecrypter.getInstance());
      }
      PDFObject objNumPO = readObject(-1, -1, true, IdentityDecrypter.getInstance());
      PDFObject offsetPO = readObject(-1, -1, true, IdentityDecrypter.getInstance());
      int objNum = objNumPO.getIntValue();
      int offset = offsetPO.getIntValue();
      if (objNum != id) return PDFObject.nullObj;

      this.buf.position(first + offset);
      obj = readObject(objNum, 0, IdentityDecrypter.getInstance());
      this.buf = oldBuf;
    }

    if (obj == null) {
      obj = PDFObject.nullObj;
    }

    this.objIdx[id].setObject(obj);

    // reset to the previous position
    this.buf.position(startPos);

    return obj;
  }