]> www.fi.muni.cz Git - evince.git/blobdiff - pdf/xpdf/XRef.cc
Scroll at the top of the page when changing page
[evince.git] / pdf / xpdf / XRef.cc
index eca638df76177c789ce73341f268cb1afd119b3e..f5c4159bf692f929607e7297b6a8bd38811c43a8 100644 (file)
 #define defPermFlags 0xfffc
 #endif
 
 #define defPermFlags 0xfffc
 #endif
 
+//------------------------------------------------------------------------
+// ObjectStream
+//------------------------------------------------------------------------
+
+class ObjectStream {
+public:
+
+  // Create an object stream, using object number <objStrNum>,
+  // generation 0.
+  ObjectStream(XRef *xref, int objStrNumA);
+
+  ~ObjectStream();
+
+  // Return the object number of this object stream.
+  int getObjStrNum() { return objStrNum; }
+
+  // Get the <objIdx>th object from this stream, which should be
+  // object number <objNum>, generation 0.
+  Object *getObject(int objIdx, int objNum, Object *obj);
+
+private:
+
+  int objStrNum;               // object number of the object stream
+  int nObjects;                        // number of objects in the stream
+  Object *objs;                        // the objects (length = nObjects)
+  int *objNums;                        // the object numbers (length = nObjects)
+};
+
+ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
+  Stream *str;
+  Parser *parser;
+  int *offsets;
+  Object objStr, obj1, obj2;
+  int first, i;
+
+  objStrNum = objStrNumA;
+  nObjects = 0;
+  objs = NULL;
+  objNums = NULL;
+
+  if (!xref->fetch(objStrNum, 0, &objStr)->isStream()) {
+    goto err1;
+  }
+
+  if (!objStr.streamGetDict()->lookup("N", &obj1)->isInt()) {
+    obj1.free();
+    goto err1;
+  }
+  nObjects = obj1.getInt();
+  obj1.free();
+  if (nObjects <= 0) {
+    goto err1;
+  }
+
+  if (!objStr.streamGetDict()->lookup("First", &obj1)->isInt()) {
+    obj1.free();
+    goto err1;
+  }
+  first = obj1.getInt();
+  obj1.free();
+  if (first < 0) {
+    goto err1;
+  }
+
+  if (nObjects*sizeof(int)/sizeof(int) != nObjects) {
+    error(-1, "Invalid 'nObjects'");
+    goto err1;
+  }
+  objs = new Object[nObjects];
+  objNums = (int *)gmalloc(nObjects * sizeof(int));
+  offsets = (int *)gmalloc(nObjects * sizeof(int));
+
+  // parse the header: object numbers and offsets
+  objStr.streamReset();
+  obj1.initNull();
+  str = new EmbedStream(objStr.getStream(), &obj1, gTrue, first);
+  parser = new Parser(xref, new Lexer(xref, str));
+  for (i = 0; i < nObjects; ++i) {
+    parser->getObj(&obj1);
+    parser->getObj(&obj2);
+    if (!obj1.isInt() || !obj2.isInt()) {
+      obj1.free();
+      obj2.free();
+      delete parser;
+      gfree(offsets);
+      goto err1;
+    }
+    objNums[i] = obj1.getInt();
+    offsets[i] = obj2.getInt();
+    obj1.free();
+    obj2.free();
+    if (objNums[i] < 0 || offsets[i] < 0 ||
+       (i > 0 && offsets[i] < offsets[i-1])) {
+      delete parser;
+      gfree(offsets);
+      goto err1;
+    }
+  }
+  while (str->getChar() != EOF) ;
+  delete parser;
+
+  // skip to the first object - this shouldn't be necessary because
+  // the First key is supposed to be equal to offsets[0], but just in
+  // case...
+  for (i = first; i < offsets[0]; ++i) {
+    objStr.getStream()->getChar();
+  }
+
+  // parse the objects
+  for (i = 0; i < nObjects; ++i) {
+    obj1.initNull();
+    if (i == nObjects - 1) {
+      str = new EmbedStream(objStr.getStream(), &obj1, gFalse, 0);
+    } else {
+      str = new EmbedStream(objStr.getStream(), &obj1, gTrue,
+                           offsets[i+1] - offsets[i]);
+    }
+    parser = new Parser(xref, new Lexer(xref, str));
+    parser->getObj(&objs[i]);
+    while (str->getChar() != EOF) ;
+    delete parser;
+  }
+
+  gfree(offsets);
+
+ err1:
+  objStr.free();
+  return;
+}
+
+ObjectStream::~ObjectStream() {
+  int i;
+
+  if (objs) {
+    for (i = 0; i < nObjects; ++i) {
+      objs[i].free();
+    }
+    delete[] objs;
+  }
+  gfree(objNums);
+}
+
+Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
+  if (objIdx < 0 || objIdx >= nObjects || objNum != objNums[objIdx]) {
+    return obj->initNull();
+  }
+  return objs[objIdx].copy(obj);
+}
+
 //------------------------------------------------------------------------
 // XRef
 //------------------------------------------------------------------------
 
 XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
   Guint pos;
 //------------------------------------------------------------------------
 // XRef
 //------------------------------------------------------------------------
 
 XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
   Guint pos;
-  int i;
+  Object obj;
 
   ok = gTrue;
   errCode = errNone;
 
   ok = gTrue;
   errCode = errNone;
@@ -60,35 +210,28 @@ XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
   entries = NULL;
   streamEnds = NULL;
   streamEndsLen = 0;
   entries = NULL;
   streamEnds = NULL;
   streamEndsLen = 0;
+  objStr = NULL;
 
   // read the trailer
   str = strA;
   start = str->getStart();
 
   // read the trailer
   str = strA;
   start = str->getStart();
-  pos = readTrailer();
+  pos = getStartXref();
 
 
-  // if there was a problem with the trailer,
-  // try to reconstruct the xref table
+  // if there was a problem with the 'startxref' position, try to
+  // reconstruct the xref table
   if (pos == 0) {
     if (!(ok = constructXRef())) {
       errCode = errDamaged;
       return;
     }
 
   if (pos == 0) {
     if (!(ok = constructXRef())) {
       errCode = errDamaged;
       return;
     }
 
-  // trailer is ok - read the xref table
+  // read the xref table
   } else {
   } else {
-    entries = (XRefEntry *)gmalloc(size * sizeof(XRefEntry));
-    for (i = 0; i < size; ++i) {
-      entries[i].offset = 0xffffffff;
-      entries[i].used = gFalse;
-    }
     while (readXRef(&pos)) ;
 
     // if there was a problem with the xref table,
     // try to reconstruct it
     if (!ok) {
     while (readXRef(&pos)) ;
 
     // if there was a problem with the xref table,
     // try to reconstruct it
     if (!ok) {
-      gfree(entries);
-      size = 0;
-      entries = NULL;
       if (!(ok = constructXRef())) {
        errCode = errDamaged;
        return;
       if (!(ok = constructXRef())) {
        errCode = errDamaged;
        return;
@@ -96,6 +239,20 @@ XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
     }
   }
 
     }
   }
 
+  // get the root dictionary (catalog) object
+  trailerDict.dictLookupNF("Root", &obj);
+  if (obj.isRef()) {
+    rootNum = obj.getRefNum();
+    rootGen = obj.getRefGen();
+    obj.free();
+  } else {
+    obj.free();
+    if (!(ok = constructXRef())) {
+      errCode = errDamaged;
+      return;
+    }
+  }
+
   // now set the trailer dictionary's xref pointer so we can fetch
   // indirect objects from it
   trailerDict.getDict()->setXRef(this);
   // now set the trailer dictionary's xref pointer so we can fetch
   // indirect objects from it
   trailerDict.getDict()->setXRef(this);
@@ -117,19 +274,16 @@ XRef::~XRef() {
   if (streamEnds) {
     gfree(streamEnds);
   }
   if (streamEnds) {
     gfree(streamEnds);
   }
+  if (objStr) {
+    delete objStr;
+  }
 }
 
 }
 
-// Read startxref position, xref table size, and root.  Returns
-// first xref position.
-Guint XRef::readTrailer() {
-  Parser *parser;
-  Object obj;
+// Read the 'startxref' position.
+Guint XRef::getStartXref() {
   char buf[xrefSearchSize+1];
   char buf[xrefSearchSize+1];
-  int n;
-  Guint pos, pos1;
   char *p;
   char *p;
-  int c;
-  int i;
+  int c, n, i;
 
   // read last xrefSearchSize bytes
   str->setPos(xrefSearchSize, -1);
 
   // read last xrefSearchSize bytes
   str->setPos(xrefSearchSize, -1);
@@ -148,175 +302,137 @@ Guint XRef::readTrailer() {
     }
   }
   if (i < 0) {
     }
   }
   if (i < 0) {
-    goto err1;
+    return 0;
   }
   for (p = &buf[i+9]; isspace(*p); ++p) ;
   }
   for (p = &buf[i+9]; isspace(*p); ++p) ;
-  pos = lastXRefPos = strToUnsigned(p);
+  lastXRefPos = strToUnsigned(p);
 
 
-  // find trailer dict by looking after first xref table
-  // (NB: we can't just use the trailer dict at the end of the file --
-  // this won't work for linearized files.)
-  str->setPos(start + pos);
-  for (i = 0; i < 4; ++i) {
-    buf[i] = str->getChar();
-  }
-  if (strncmp(buf, "xref", 4)) {
-    goto err1;
-  }
-  pos1 = pos + 4;
-  while (1) {
-    str->setPos(start + pos1);
-    for (i = 0; i < 35; ++i) {
-      if ((c = str->getChar()) == EOF) {
-       goto err1;
-      }
-      buf[i] = c;
-    }
-    if (!strncmp(buf, "trailer", 7)) {
-      break;
-    }
-    p = buf;
-    while (isspace(*p)) ++p;
-    while ('0' <= *p && *p <= '9') ++p;
-    while (isspace(*p)) ++p;
-    n = atoi(p);
-    while ('0' <= *p && *p <= '9') ++p;
-    while (isspace(*p)) ++p;
-    if (p == buf) {
-      goto err1;
-    }
-    pos1 += (p - buf) + n * 20;
-  }
-  pos1 += 7;
+  return lastXRefPos;
+}
 
 
-  // read trailer dict
+// Read one xref table section.  Also reads the associated trailer
+// dictionary, and returns the prev pointer (if any).
+GBool XRef::readXRef(Guint *pos) {
+  Parser *parser;
+  Object obj;
+  GBool more;
+
+  // start up a parser, parse one token
   obj.initNull();
   parser = new Parser(NULL,
             new Lexer(NULL,
   obj.initNull();
   parser = new Parser(NULL,
             new Lexer(NULL,
-              str->makeSubStream(start + pos1, gFalse, 0, &obj)));
-  parser->getObj(&trailerDict);
-  if (trailerDict.isDict()) {
-    trailerDict.dictLookupNF("Size", &obj);
-    if (obj.isInt()) {
-      size = obj.getInt();
-    } else {
-      goto err3;
+              str->makeSubStream(start + *pos, gFalse, 0, &obj)));
+  parser->getObj(&obj);
+
+  // parse an old-style xref table
+  if (obj.isCmd("xref")) {
+    obj.free();
+    more = readXRefTable(parser, pos);
+
+  // parse an xref stream
+  } else if (obj.isInt()) {
+    obj.free();
+    if (!parser->getObj(&obj)->isInt()) {
+      goto err1;
     }
     obj.free();
     }
     obj.free();
-    trailerDict.dictLookupNF("Root", &obj);
-    if (obj.isRef()) {
-      rootNum = obj.getRefNum();
-      rootGen = obj.getRefGen();
-    } else {
-      goto err3;
+    if (!parser->getObj(&obj)->isCmd("obj")) {
+      goto err1;
+    }
+    obj.free();
+    if (!parser->getObj(&obj)->isStream()) {
+      goto err1;
     }
     }
+    more = readXRefStream(obj.getStream(), pos);
     obj.free();
     obj.free();
+
   } else {
   } else {
-    goto err2;
+    goto err1;
   }
   }
-  delete parser;
 
 
-  // return first xref position
-  return pos;
+  delete parser;
+  return more;
 
 
- err3:
+ err1:
   obj.free();
   obj.free();
- err2:
-  trailerDict.free();
   delete parser;
   delete parser;
- err1:
-  size = 0;
-  return 0;
+  ok = gFalse;
+  return gFalse;
 }
 
 }
 
-// Read an xref table and the prev pointer from the trailer.
-GBool XRef::readXRef(Guint *pos) {
-  Parser *parser;
-  Object obj, obj2;
-  char s[20];
+GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
+  XRefEntry entry;
   GBool more;
   GBool more;
-  int first, newSize, n, i, j;
-  int c;
-
-  // seek to xref in stream
-  str->setPos(start + *pos);
-
-  // make sure it's an xref table
-  while ((c = str->getChar()) != EOF && isspace(c)) ;
-  s[0] = (char)c;
-  s[1] = (char)str->getChar();
-  s[2] = (char)str->getChar();
-  s[3] = (char)str->getChar();
-  if (!(s[0] == 'x' && s[1] == 'r' && s[2] == 'e' && s[3] == 'f')) {
-    goto err2;
-  }
+  Object obj, obj2;
+  Guint pos2;
+  int first, n, newSize, i;
 
 
-  // read xref
   while (1) {
   while (1) {
-    while ((c = str->lookChar()) != EOF && isspace(c)) {
-      str->getChar();
-    }
-    if (c == 't') {
+    parser->getObj(&obj);
+    if (obj.isCmd("trailer")) {
+      obj.free();
       break;
     }
       break;
     }
-    for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
-      s[i] = (char)c;
-    }
-    if (i == 0) {
-      goto err2;
-    }
-    s[i] = '\0';
-    first = atoi(s);
-    while ((c = str->lookChar()) != EOF && isspace(c)) {
-      str->getChar();
-    }
-    for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
-      s[i] = (char)c;
+    if (!obj.isInt()) {
+      goto err1;
     }
     }
-    if (i == 0) {
-      goto err2;
+    first = obj.getInt();
+    obj.free();
+    if (!parser->getObj(&obj)->isInt()) {
+      goto err1;
     }
     }
-    s[i] = '\0';
-    n = atoi(s);
-    while ((c = str->lookChar()) != EOF && isspace(c)) {
-      str->getChar();
+    n = obj.getInt();
+    obj.free();
+    if (first < 0 || n < 0 || first + n < 0) {
+      goto err1;
     }
     }
-    // check for buggy PDF files with an incorrect (too small) xref
-    // table size
     if (first + n > size) {
     if (first + n > size) {
-      newSize = first + n;
+      for (newSize = size ? 2 * size : 1024;
+          first + n > newSize && newSize > 0;
+          newSize <<= 1) ;
+      if (newSize < 0) {
+       goto err1;
+      }
+      if (newSize*sizeof(XRefEntry)/sizeof(XRefEntry) != newSize) {
+        error(-1, "Invalid 'obj' parameters'");
+        goto err1;
+      }
       entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
       for (i = size; i < newSize; ++i) {
        entries[i].offset = 0xffffffff;
       entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
       for (i = size; i < newSize; ++i) {
        entries[i].offset = 0xffffffff;
-       entries[i].used = gFalse;
+       entries[i].type = xrefEntryFree;
       }
       size = newSize;
     }
     for (i = first; i < first + n; ++i) {
       }
       size = newSize;
     }
     for (i = first; i < first + n; ++i) {
-      for (j = 0; j < 20; ++j) {
-       if ((c = str->getChar()) == EOF) {
-         goto err2;
-       }
-       s[j] = (char)c;
+      if (!parser->getObj(&obj)->isInt()) {
+       goto err1;
+      }
+      entry.offset = (Guint)obj.getInt();
+      obj.free();
+      if (!parser->getObj(&obj)->isInt()) {
+       goto err1;
+      }
+      entry.gen = obj.getInt();
+      obj.free();
+      parser->getObj(&obj);
+      if (obj.isCmd("n")) {
+       entry.type = xrefEntryUncompressed;
+      } else if (obj.isCmd("f")) {
+       entry.type = xrefEntryFree;
+      } else {
+       goto err1;
       }
       }
+      obj.free();
       if (entries[i].offset == 0xffffffff) {
       if (entries[i].offset == 0xffffffff) {
-       s[10] = '\0';
-       entries[i].offset = strToUnsigned(s);
-       s[16] = '\0';
-       entries[i].gen = atoi(&s[11]);
-       if (s[17] == 'n') {
-         entries[i].used = gTrue;
-       } else if (s[17] == 'f') {
-         entries[i].used = gFalse;
-       } else {
-         goto err2;
-       }
+       entries[i] = entry;
        // PDF files of patents from the IBM Intellectual Property
        // Network have a bug: the xref table claims to start at 1
        // instead of 0.
        if (i == 1 && first == 1 &&
            entries[1].offset == 0 && entries[1].gen == 65535 &&
        // PDF files of patents from the IBM Intellectual Property
        // Network have a bug: the xref table claims to start at 1
        // instead of 0.
        if (i == 1 && first == 1 &&
            entries[1].offset == 0 && entries[1].gen == 65535 &&
-           !entries[1].used) {
+           entries[1].type == xrefEntryFree) {
          i = first = 0;
          entries[0] = entries[1];
          entries[1].offset = 0xffffffff;
          i = first = 0;
          entries[0] = entries[1];
          entries[1].offset = 0xffffffff;
@@ -325,20 +441,12 @@ GBool XRef::readXRef(Guint *pos) {
     }
   }
 
     }
   }
 
-  // read prev pointer from trailer dictionary
-  obj.initNull();
-  parser = new Parser(NULL,
-            new Lexer(NULL,
-              str->makeSubStream(str->getPos(), gFalse, 0, &obj)));
-  parser->getObj(&obj);
-  if (!obj.isCmd("trailer")) {
-    goto err1;
-  }
-  obj.free();
-  parser->getObj(&obj);
-  if (!obj.isDict()) {
+  // read the trailer dictionary
+  if (!parser->getObj(&obj)->isDict()) {
     goto err1;
   }
     goto err1;
   }
+
+  // get the 'Prev' pointer
   obj.getDict()->lookupNF("Prev", &obj2);
   if (obj2.isInt()) {
     *pos = (Guint)obj2.getInt();
   obj.getDict()->lookupNF("Prev", &obj2);
   if (obj2.isInt()) {
     *pos = (Guint)obj2.getInt();
@@ -351,23 +459,208 @@ GBool XRef::readXRef(Guint *pos) {
   } else {
     more = gFalse;
   }
   } else {
     more = gFalse;
   }
-  obj.free();
   obj2.free();
 
   obj2.free();
 
-  delete parser;
+  // save the first trailer dictionary
+  if (trailerDict.isNone()) {
+    obj.copy(&trailerDict);
+  }
+
+  // check for an 'XRefStm' key
+  if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
+    pos2 = (Guint)obj2.getInt();
+    readXRef(&pos2);
+    if (!ok) {
+      goto err1;
+    }
+  }
+  obj2.free();
+
+  obj.free();
+  return more;
+
+ err1:
+  obj.free();
+  ok = gFalse;
+  return gFalse;
+}
+
+GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
+  Dict *dict;
+  int w[3];
+  GBool more;
+  Object obj, obj2, idx;
+  int newSize, first, n, i;
+
+  dict = xrefStr->getDict();
+
+  if (!dict->lookupNF("Size", &obj)->isInt()) {
+    goto err1;
+  }
+  newSize = obj.getInt();
+  obj.free();
+  if (newSize < 0) {
+    goto err1;
+  }
+  if (newSize > size) {
+    if (newSize * sizeof(XRefEntry)/sizeof(XRefEntry) != newSize) {
+      error(-1, "Invalid 'size' parameter.");
+      return gFalse;
+    }
+    entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
+    for (i = size; i < newSize; ++i) {
+      entries[i].offset = 0xffffffff;
+      entries[i].type = xrefEntryFree;
+    }
+    size = newSize;
+  }
+
+  if (!dict->lookupNF("W", &obj)->isArray() ||
+      obj.arrayGetLength() < 3) {
+    goto err1;
+  }
+  for (i = 0; i < 3; ++i) {
+    if (!obj.arrayGet(i, &obj2)->isInt()) {
+      obj2.free();
+      goto err1;
+    }
+    w[i] = obj2.getInt();
+    obj2.free();
+    if (w[i] < 0 || w[i] > 4) {
+      goto err1;
+    }
+  }
+  obj.free();
+
+  xrefStr->reset();
+  dict->lookupNF("Index", &idx);
+  if (idx.isArray()) {
+    for (i = 0; i+1 < idx.arrayGetLength(); i += 2) {
+      if (!idx.arrayGet(i, &obj)->isInt()) {
+       idx.free();
+       goto err1;
+      }
+      first = obj.getInt();
+      obj.free();
+      if (!idx.arrayGet(i+1, &obj)->isInt()) {
+       idx.free();
+       goto err1;
+      }
+      n = obj.getInt();
+      obj.free();
+      if (first < 0 || n < 0 ||
+         !readXRefStreamSection(xrefStr, w, first, n)) {
+       idx.free();
+       goto err0;
+      }
+    }
+  } else {
+    if (!readXRefStreamSection(xrefStr, w, 0, newSize)) {
+      idx.free();
+      goto err0;
+    }
+  }
+  idx.free();
+
+  dict->lookupNF("Prev", &obj);
+  if (obj.isInt()) {
+    *pos = (Guint)obj.getInt();
+    more = gTrue;
+  } else {
+    more = gFalse;
+  }
+  obj.free();
+  if (trailerDict.isNone()) {
+    trailerDict.initDict(dict);
+  }
+
   return more;
 
  err1:
   obj.free();
   return more;
 
  err1:
   obj.free();
- err2:
+ err0:
   ok = gFalse;
   return gFalse;
 }
 
   ok = gFalse;
   return gFalse;
 }
 
+GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
+  Guint offset;
+  int type, gen, c, newSize, i, j;
+
+  if (first + n < 0) {
+    return gFalse;
+  }
+  if (first + n > size) {
+    for (newSize = size ? 2 * size : 1024;
+        first + n > newSize && newSize > 0;
+        newSize <<= 1) ;
+    if (newSize < 0) {
+      return gFalse;
+    }
+    if (newSize*sizeof(XRefEntry)/sizeof(XRefEntry) != newSize) {
+      error(-1, "Invalid 'size' inside xref table.");
+      return gFalse;
+    }
+    entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
+    for (i = size; i < newSize; ++i) {
+      entries[i].offset = 0xffffffff;
+      entries[i].type = xrefEntryFree;
+    }
+    size = newSize;
+  }
+  for (i = first; i < first + n; ++i) {
+    if (w[0] == 0) {
+      type = 1;
+    } else {
+      for (type = 0, j = 0; j < w[0]; ++j) {
+       if ((c = xrefStr->getChar()) == EOF) {
+         return gFalse;
+       }
+       type = (type << 8) + c;
+      }
+    }
+    for (offset = 0, j = 0; j < w[1]; ++j) {
+      if ((c = xrefStr->getChar()) == EOF) {
+       return gFalse;
+      }
+      offset = (offset << 8) + c;
+    }
+    for (gen = 0, j = 0; j < w[2]; ++j) {
+      if ((c = xrefStr->getChar()) == EOF) {
+       return gFalse;
+      }
+      gen = (gen << 8) + c;
+    }
+    if (entries[i].offset == 0xffffffff) {
+      switch (type) {
+      case 0:
+       entries[i].offset = offset;
+       entries[i].gen = gen;
+       entries[i].type = xrefEntryFree;
+       break;
+      case 1:
+       entries[i].offset = offset;
+       entries[i].gen = gen;
+       entries[i].type = xrefEntryUncompressed;
+       break;
+      case 2:
+       entries[i].offset = offset;
+       entries[i].gen = gen;
+       entries[i].type = xrefEntryCompressed;
+       break;
+      default:
+       return gFalse;
+      }
+    }
+  }
+
+  return gTrue;
+}
+
 // Attempt to construct an xref table for a damaged file.
 GBool XRef::constructXRef() {
   Parser *parser;
 // Attempt to construct an xref table for a damaged file.
 GBool XRef::constructXRef() {
   Parser *parser;
-  Object obj;
+  Object newTrailerDict, obj;
   char buf[256];
   Guint pos;
   int num, gen;
   char buf[256];
   Guint pos;
   int num, gen;
@@ -377,6 +670,10 @@ GBool XRef::constructXRef() {
   int i;
   GBool gotRoot;
 
   int i;
   GBool gotRoot;
 
+  gfree(entries);
+  size = 0;
+  entries = NULL;
+
   error(0, "PDF file is damaged - attempting to reconstruct xref table...");
   gotRoot = gFalse;
   streamEndsLen = streamEndsSize = 0;
   error(0, "PDF file is damaged - attempting to reconstruct xref table...");
   gotRoot = gFalse;
   streamEndsLen = streamEndsSize = 0;
@@ -391,62 +688,72 @@ GBool XRef::constructXRef() {
 
     // got trailer dictionary
     if (!strncmp(p, "trailer", 7)) {
 
     // got trailer dictionary
     if (!strncmp(p, "trailer", 7)) {
-      gotRoot = gFalse;
       obj.initNull();
       parser = new Parser(NULL,
                 new Lexer(NULL,
                   str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
       obj.initNull();
       parser = new Parser(NULL,
                 new Lexer(NULL,
                   str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
-      if (!trailerDict.isNone()) {
-       trailerDict.free();
-      }
-      parser->getObj(&trailerDict);
-      if (trailerDict.isDict()) {
-       trailerDict.dictLookupNF("Root", &obj);
+      parser->getObj(&newTrailerDict);
+      if (newTrailerDict.isDict()) {
+       newTrailerDict.dictLookupNF("Root", &obj);
        if (obj.isRef()) {
          rootNum = obj.getRefNum();
          rootGen = obj.getRefGen();
        if (obj.isRef()) {
          rootNum = obj.getRefNum();
          rootGen = obj.getRefGen();
+         if (!trailerDict.isNone()) {
+           trailerDict.free();
+         }
+         newTrailerDict.copy(&trailerDict);
          gotRoot = gTrue;
        }
        obj.free();
          gotRoot = gTrue;
        }
        obj.free();
-      } else {
-       trailerDict.free();
       }
       }
+      newTrailerDict.free();
       delete parser;
 
     // look for object
     } else if (isdigit(*p)) {
       num = atoi(p);
       delete parser;
 
     // look for object
     } else if (isdigit(*p)) {
       num = atoi(p);
-      do {
-       ++p;
-      } while (*p && isdigit(*p));
-      if (isspace(*p)) {
+      if (num > 0) {
        do {
          ++p;
        do {
          ++p;
-       } while (*p && isspace(*p));
-       if (isdigit(*p)) {
-         gen = atoi(p);
+       } while (*p && isdigit(*p));
+       if (isspace(*p)) {
          do {
            ++p;
          do {
            ++p;
-         } while (*p && isdigit(*p));
-         if (isspace(*p)) {
+         } while (*p && isspace(*p));
+         if (isdigit(*p)) {
+           gen = atoi(p);
            do {
              ++p;
            do {
              ++p;
-           } while (*p && isspace(*p));
-           if (!strncmp(p, "obj", 3)) {
-             if (num >= size) {
-               newSize = (num + 1 + 255) & ~255;
-               entries = (XRefEntry *)
-                           grealloc(entries, newSize * sizeof(XRefEntry));
-               for (i = size; i < newSize; ++i) {
-                 entries[i].offset = 0xffffffff;
-                 entries[i].used = gFalse;
+           } while (*p && isdigit(*p));
+           if (isspace(*p)) {
+             do {
+               ++p;
+             } while (*p && isspace(*p));
+             if (!strncmp(p, "obj", 3)) {
+               if (num >= size) {
+                 newSize = (num + 1 + 255) & ~255;
+                 if (newSize < 0) {
+                   error(-1, "Bad object number");
+                   return gFalse;
+                 }
+                  if (newSize*sizeof(XRefEntry)/sizeof(XRefEntry) != newSize) {
+                    error(-1, "Invalid 'obj' parameters.");
+                    return gFalse;
+                  }
+                 entries = (XRefEntry *)
+                     grealloc(entries, newSize * sizeof(XRefEntry));
+                 for (i = size; i < newSize; ++i) {
+                   entries[i].offset = 0xffffffff;
+                   entries[i].type = xrefEntryFree;
+                 }
+                 size = newSize;
+               }
+               if (entries[num].type == xrefEntryFree ||
+                   gen >= entries[num].gen) {
+                 entries[num].offset = pos - start;
+                 entries[num].gen = gen;
+                 entries[num].type = xrefEntryUncompressed;
                }
                }
-               size = newSize;
-             }
-             if (!entries[num].used || gen >= entries[num].gen) {
-               entries[num].offset = pos - start;
-               entries[num].gen = gen;
-               entries[num].used = gTrue;
              }
            }
          }
              }
            }
          }
@@ -456,6 +763,10 @@ GBool XRef::constructXRef() {
     } else if (!strncmp(p, "endstream", 9)) {
       if (streamEndsLen == streamEndsSize) {
        streamEndsSize += 64;
     } else if (!strncmp(p, "endstream", 9)) {
       if (streamEndsLen == streamEndsSize) {
        streamEndsSize += 64;
+        if (streamEndsSize*sizeof(int)/sizeof(int) != streamEndsSize) {
+          error(-1, "Invalid 'endstream' parameter.");
+          return gFalse;
+        }
        streamEnds = (Guint *)grealloc(streamEnds,
                                       streamEndsSize * sizeof(int));
       }
        streamEnds = (Guint *)grealloc(streamEnds,
                                       streamEndsSize * sizeof(int));
       }
@@ -508,6 +819,9 @@ GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
        } else {
          keyLength = 5;
        }
        } else {
          keyLength = 5;
        }
+       if (keyLength > 16) {
+         keyLength = 16;
+       }
        permFlags = permissions.getInt();
        if (encVersion >= 1 && encVersion <= 2 &&
            encRevision >= 2 && encRevision <= 3) {
        permFlags = permissions.getInt();
        if (encVersion >= 1 && encVersion <= 2 &&
            encRevision >= 2 && encRevision <= 3) {
@@ -610,12 +924,16 @@ Object *XRef::fetch(int num, int gen, Object *obj) {
 
   // check for bogus ref - this can happen in corrupted PDF files
   if (num < 0 || num >= size) {
 
   // check for bogus ref - this can happen in corrupted PDF files
   if (num < 0 || num >= size) {
-    obj->initNull();
-    return obj;
+    goto err;
   }
 
   e = &entries[num];
   }
 
   e = &entries[num];
-  if (e->gen == gen && e->offset != 0xffffffff) {
+  switch (e->type) {
+
+  case xrefEntryUncompressed:
+    if (e->gen != gen) {
+      goto err;
+    }
     obj1.initNull();
     parser = new Parser(this,
               new Lexer(this,
     obj1.initNull();
     parser = new Parser(this,
               new Lexer(this,
@@ -623,26 +941,44 @@ Object *XRef::fetch(int num, int gen, Object *obj) {
     parser->getObj(&obj1);
     parser->getObj(&obj2);
     parser->getObj(&obj3);
     parser->getObj(&obj1);
     parser->getObj(&obj2);
     parser->getObj(&obj3);
-    if (obj1.isInt() && obj1.getInt() == num &&
-       obj2.isInt() && obj2.getInt() == gen &&
-       obj3.isCmd("obj")) {
+    if (!obj1.isInt() || obj1.getInt() != num ||
+       !obj2.isInt() || obj2.getInt() != gen ||
+       !obj3.isCmd("obj")) {
+      goto err;
+    }
 #ifndef NO_DECRYPTION
 #ifndef NO_DECRYPTION
-      parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
-                    num, gen);
+    parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
+                  num, gen);
 #else
 #else
-      parser->getObj(obj);
+    parser->getObj(obj);
 #endif
 #endif
-    } else {
-      obj->initNull();
-    }
     obj1.free();
     obj2.free();
     obj3.free();
     delete parser;
     obj1.free();
     obj2.free();
     obj3.free();
     delete parser;
-  } else {
-    obj->initNull();
+    break;
+
+  case xrefEntryCompressed:
+    if (gen != 0) {
+      goto err;
+    }
+    if (!objStr || objStr->getObjStrNum() != (int)e->offset) {
+      if (objStr) {
+       delete objStr;
+      }
+      objStr = new ObjectStream(this, e->offset);
+    }
+    objStr->getObject(e->gen, num, obj);
+    break;
+
+  default:
+    goto err;
   }
   }
+
   return obj;
   return obj;
+
+ err:
+  return obj->initNull();
 }
 
 Object *XRef::getDocInfo(Object *obj) {
 }
 
 Object *XRef::getDocInfo(Object *obj) {