Back to index

tetex-bin  3.0
XRef.cc
Go to the documentation of this file.
00001 //========================================================================
00002 //
00003 // XRef.cc
00004 //
00005 // Copyright 1996-2003 Glyph & Cog, LLC
00006 //
00007 //========================================================================
00008 
00009 #include <aconf.h>
00010 
00011 #ifdef USE_GCC_PRAGMAS
00012 #pragma implementation
00013 #endif
00014 
00015 #include <stdlib.h>
00016 #include <stddef.h>
00017 #include <string.h>
00018 #include <ctype.h>
00019 #include "gmem.h"
00020 #include "Object.h"
00021 #include "Stream.h"
00022 #include "Lexer.h"
00023 #include "Parser.h"
00024 #include "Dict.h"
00025 #ifndef NO_DECRYPTION
00026 #include "Decrypt.h"
00027 #endif
00028 #include "Error.h"
00029 #include "ErrorCodes.h"
00030 #include "XRef.h"
00031 
00032 //------------------------------------------------------------------------
00033 
00034 #define xrefSearchSize 1024 // read this many bytes at end of file
00035                             //   to look for 'startxref'
00036 
00037 #ifndef NO_DECRYPTION
00038 //------------------------------------------------------------------------
00039 // Permission bits
00040 //------------------------------------------------------------------------
00041 
00042 #define permPrint    (1<<2)
00043 #define permChange   (1<<3)
00044 #define permCopy     (1<<4)
00045 #define permNotes    (1<<5)
00046 #define defPermFlags 0xfffc
00047 #endif
00048 
00049 //------------------------------------------------------------------------
00050 // ObjectStream
00051 //------------------------------------------------------------------------
00052 
00053 class ObjectStream {
00054 public:
00055 
00056   // Create an object stream, using object number <objStrNum>,
00057   // generation 0.
00058   ObjectStream(XRef *xref, int objStrNumA);
00059 
00060   ~ObjectStream();
00061 
00062   // Return the object number of this object stream.
00063   int getObjStrNum() { return objStrNum; }
00064 
00065   // Get the <objIdx>th object from this stream, which should be
00066   // object number <objNum>, generation 0.
00067   Object *getObject(int objIdx, int objNum, Object *obj);
00068 
00069 private:
00070 
00071   int objStrNum;            // object number of the object stream
00072   int nObjects;                    // number of objects in the stream
00073   Object *objs;                    // the objects (length = nObjects)
00074   int *objNums;                    // the object numbers (length = nObjects)
00075 };
00076 
00077 ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
00078   Stream *str;
00079   Parser *parser;
00080   int *offsets;
00081   Object objStr, obj1, obj2;
00082   int first, i;
00083 
00084   objStrNum = objStrNumA;
00085   nObjects = 0;
00086   objs = NULL;
00087   objNums = NULL;
00088 
00089   if (!xref->fetch(objStrNum, 0, &objStr)->isStream()) {
00090     goto err1;
00091   }
00092 
00093   if (!objStr.streamGetDict()->lookup("N", &obj1)->isInt()) {
00094     obj1.free();
00095     goto err1;
00096   }
00097   nObjects = obj1.getInt();
00098   obj1.free();
00099   if (nObjects <= 0) {
00100     goto err1;
00101   }
00102 
00103   if (!objStr.streamGetDict()->lookup("First", &obj1)->isInt()) {
00104     obj1.free();
00105     goto err1;
00106   }
00107   first = obj1.getInt();
00108   obj1.free();
00109   if (first < 0) {
00110     goto err1;
00111   }
00112 
00113   objs = new Object[nObjects];
00114   objNums = (int *)gmalloc(nObjects * sizeof(int));
00115   offsets = (int *)gmalloc(nObjects * sizeof(int));
00116 
00117   // parse the header: object numbers and offsets
00118   objStr.streamReset();
00119   obj1.initNull();
00120   str = new EmbedStream(objStr.getStream(), &obj1, gTrue, first);
00121   parser = new Parser(xref, new Lexer(xref, str));
00122   for (i = 0; i < nObjects; ++i) {
00123     parser->getObj(&obj1);
00124     parser->getObj(&obj2);
00125     if (!obj1.isInt() || !obj2.isInt()) {
00126       obj1.free();
00127       obj2.free();
00128       delete parser;
00129       gfree(offsets);
00130       goto err1;
00131     }
00132     objNums[i] = obj1.getInt();
00133     offsets[i] = obj2.getInt();
00134     obj1.free();
00135     obj2.free();
00136     if (objNums[i] < 0 || offsets[i] < 0 ||
00137        (i > 0 && offsets[i] < offsets[i-1])) {
00138       delete parser;
00139       gfree(offsets);
00140       goto err1;
00141     }
00142   }
00143   while (str->getChar() != EOF) ;
00144   delete parser;
00145 
00146   // skip to the first object - this shouldn't be necessary because
00147   // the First key is supposed to be equal to offsets[0], but just in
00148   // case...
00149   for (i = first; i < offsets[0]; ++i) {
00150     objStr.getStream()->getChar();
00151   }
00152 
00153   // parse the objects
00154   for (i = 0; i < nObjects; ++i) {
00155     obj1.initNull();
00156     if (i == nObjects - 1) {
00157       str = new EmbedStream(objStr.getStream(), &obj1, gFalse, 0);
00158     } else {
00159       str = new EmbedStream(objStr.getStream(), &obj1, gTrue,
00160                          offsets[i+1] - offsets[i]);
00161     }
00162     parser = new Parser(xref, new Lexer(xref, str));
00163     parser->getObj(&objs[i]);
00164     while (str->getChar() != EOF) ;
00165     delete parser;
00166   }
00167 
00168   gfree(offsets);
00169 
00170  err1:
00171   objStr.free();
00172   return;
00173 }
00174 
00175 ObjectStream::~ObjectStream() {
00176   int i;
00177 
00178   if (objs) {
00179     for (i = 0; i < nObjects; ++i) {
00180       objs[i].free();
00181     }
00182     delete[] objs;
00183   }
00184   gfree(objNums);
00185 }
00186 
00187 Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
00188   if (objIdx < 0 || objIdx >= nObjects || objNum != objNums[objIdx]) {
00189     return obj->initNull();
00190   }
00191   return objs[objIdx].copy(obj);
00192 }
00193 
00194 //------------------------------------------------------------------------
00195 // XRef
00196 //------------------------------------------------------------------------
00197 
00198 XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
00199   Guint pos;
00200   Object obj;
00201 
00202   ok = gTrue;
00203   errCode = errNone;
00204   size = 0;
00205   entries = NULL;
00206   streamEnds = NULL;
00207   streamEndsLen = 0;
00208   objStr = NULL;
00209 
00210   // read the trailer
00211   str = strA;
00212   start = str->getStart();
00213   pos = getStartXref();
00214 
00215   // if there was a problem with the 'startxref' position, try to
00216   // reconstruct the xref table
00217   if (pos == 0) {
00218     if (!(ok = constructXRef())) {
00219       errCode = errDamaged;
00220       return;
00221     }
00222 
00223   // read the xref table
00224   } else {
00225     while (readXRef(&pos)) ;
00226 
00227     // if there was a problem with the xref table,
00228     // try to reconstruct it
00229     if (!ok) {
00230       if (!(ok = constructXRef())) {
00231        errCode = errDamaged;
00232        return;
00233       }
00234     }
00235   }
00236 
00237   // get the root dictionary (catalog) object
00238   trailerDict.dictLookupNF("Root", &obj);
00239   if (obj.isRef()) {
00240     rootNum = obj.getRefNum();
00241     rootGen = obj.getRefGen();
00242     obj.free();
00243   } else {
00244     obj.free();
00245     if (!(ok = constructXRef())) {
00246       errCode = errDamaged;
00247       return;
00248     }
00249   }
00250 
00251   // now set the trailer dictionary's xref pointer so we can fetch
00252   // indirect objects from it
00253   trailerDict.getDict()->setXRef(this);
00254 
00255   // check for encryption
00256 #ifndef NO_DECRYPTION
00257   encrypted = gFalse;
00258 #endif
00259   if (checkEncrypted(ownerPassword, userPassword)) {
00260     ok = gFalse;
00261     errCode = errEncrypted;
00262     return;
00263   }
00264 }
00265 
00266 XRef::~XRef() {
00267   gfree(entries);
00268   trailerDict.free();
00269   if (streamEnds) {
00270     gfree(streamEnds);
00271   }
00272   if (objStr) {
00273     delete objStr;
00274   }
00275 }
00276 
00277 // Read the 'startxref' position.
00278 Guint XRef::getStartXref() {
00279   char buf[xrefSearchSize+1];
00280   char *p;
00281   int c, n, i;
00282 
00283   // read last xrefSearchSize bytes
00284   str->setPos(xrefSearchSize, -1);
00285   for (n = 0; n < xrefSearchSize; ++n) {
00286     if ((c = str->getChar()) == EOF) {
00287       break;
00288     }
00289     buf[n] = c;
00290   }
00291   buf[n] = '\0';
00292 
00293   // find startxref
00294   for (i = n - 9; i >= 0; --i) {
00295     if (!strncmp(&buf[i], "startxref", 9)) {
00296       break;
00297     }
00298   }
00299   if (i < 0) {
00300     return 0;
00301   }
00302   for (p = &buf[i+9]; isspace(*p); ++p) ;
00303   lastXRefPos = strToUnsigned(p);
00304 
00305   return lastXRefPos;
00306 }
00307 
00308 // Read one xref table section.  Also reads the associated trailer
00309 // dictionary, and returns the prev pointer (if any).
00310 GBool XRef::readXRef(Guint *pos) {
00311   Parser *parser;
00312   Object obj;
00313   GBool more;
00314 
00315   // start up a parser, parse one token
00316   obj.initNull();
00317   parser = new Parser(NULL,
00318             new Lexer(NULL,
00319               str->makeSubStream(start + *pos, gFalse, 0, &obj)));
00320   parser->getObj(&obj);
00321 
00322   // parse an old-style xref table
00323   if (obj.isCmd("xref")) {
00324     obj.free();
00325     more = readXRefTable(parser, pos);
00326 
00327   // parse an xref stream
00328   } else if (obj.isInt()) {
00329     obj.free();
00330     if (!parser->getObj(&obj)->isInt()) {
00331       goto err1;
00332     }
00333     obj.free();
00334     if (!parser->getObj(&obj)->isCmd("obj")) {
00335       goto err1;
00336     }
00337     obj.free();
00338     if (!parser->getObj(&obj)->isStream()) {
00339       goto err1;
00340     }
00341     more = readXRefStream(obj.getStream(), pos);
00342     obj.free();
00343 
00344   } else {
00345     goto err1;
00346   }
00347 
00348   delete parser;
00349   return more;
00350 
00351  err1:
00352   obj.free();
00353   delete parser;
00354   ok = gFalse;
00355   return gFalse;
00356 }
00357 
00358 GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
00359   XRefEntry entry;
00360   GBool more;
00361   Object obj, obj2;
00362   Guint pos2;
00363   int first, n, newSize, i;
00364 
00365   while (1) {
00366     parser->getObj(&obj);
00367     if (obj.isCmd("trailer")) {
00368       obj.free();
00369       break;
00370     }
00371     if (!obj.isInt()) {
00372       goto err1;
00373     }
00374     first = obj.getInt();
00375     obj.free();
00376     if (!parser->getObj(&obj)->isInt()) {
00377       goto err1;
00378     }
00379     n = obj.getInt();
00380     obj.free();
00381     if (first < 0 || n < 0 || first + n < 0) {
00382       goto err1;
00383     }
00384     if (first + n > size) {
00385       for (newSize = size ? 2 * size : 1024;
00386           first + n > newSize && newSize > 0;
00387           newSize <<= 1) ;
00388       if (newSize < 0) {
00389        goto err1;
00390       }
00391       entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
00392       for (i = size; i < newSize; ++i) {
00393        entries[i].offset = 0xffffffff;
00394        entries[i].type = xrefEntryFree;
00395       }
00396       size = newSize;
00397     }
00398     for (i = first; i < first + n; ++i) {
00399       if (!parser->getObj(&obj)->isInt()) {
00400        goto err1;
00401       }
00402       entry.offset = (Guint)obj.getInt();
00403       obj.free();
00404       if (!parser->getObj(&obj)->isInt()) {
00405        goto err1;
00406       }
00407       entry.gen = obj.getInt();
00408       obj.free();
00409       parser->getObj(&obj);
00410       if (obj.isCmd("n")) {
00411        entry.type = xrefEntryUncompressed;
00412       } else if (obj.isCmd("f")) {
00413        entry.type = xrefEntryFree;
00414       } else {
00415        goto err1;
00416       }
00417       obj.free();
00418       if (entries[i].offset == 0xffffffff) {
00419        entries[i] = entry;
00420        // PDF files of patents from the IBM Intellectual Property
00421        // Network have a bug: the xref table claims to start at 1
00422        // instead of 0.
00423        if (i == 1 && first == 1 &&
00424            entries[1].offset == 0 && entries[1].gen == 65535 &&
00425            entries[1].type == xrefEntryFree) {
00426          i = first = 0;
00427          entries[0] = entries[1];
00428          entries[1].offset = 0xffffffff;
00429        }
00430       }
00431     }
00432   }
00433 
00434   // read the trailer dictionary
00435   if (!parser->getObj(&obj)->isDict()) {
00436     goto err1;
00437   }
00438 
00439   // get the 'Prev' pointer
00440   obj.getDict()->lookupNF("Prev", &obj2);
00441   if (obj2.isInt()) {
00442     *pos = (Guint)obj2.getInt();
00443     more = gTrue;
00444   } else if (obj2.isRef()) {
00445     // certain buggy PDF generators generate "/Prev NNN 0 R" instead
00446     // of "/Prev NNN"
00447     *pos = (Guint)obj2.getRefNum();
00448     more = gTrue;
00449   } else {
00450     more = gFalse;
00451   }
00452   obj2.free();
00453 
00454   // save the first trailer dictionary
00455   if (trailerDict.isNone()) {
00456     obj.copy(&trailerDict);
00457   }
00458 
00459   // check for an 'XRefStm' key
00460   if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
00461     pos2 = (Guint)obj2.getInt();
00462     readXRef(&pos2);
00463     if (!ok) {
00464       goto err1;
00465     }
00466   }
00467   obj2.free();
00468 
00469   obj.free();
00470   return more;
00471 
00472  err1:
00473   obj.free();
00474   ok = gFalse;
00475   return gFalse;
00476 }
00477 
00478 GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
00479   Dict *dict;
00480   int w[3];
00481   GBool more;
00482   Object obj, obj2, idx;
00483   int newSize, first, n, i;
00484 
00485   dict = xrefStr->getDict();
00486 
00487   if (!dict->lookupNF("Size", &obj)->isInt()) {
00488     goto err1;
00489   }
00490   newSize = obj.getInt();
00491   obj.free();
00492   if (newSize < 0) {
00493     goto err1;
00494   }
00495   if (newSize > size) {
00496     entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
00497     for (i = size; i < newSize; ++i) {
00498       entries[i].offset = 0xffffffff;
00499       entries[i].type = xrefEntryFree;
00500     }
00501     size = newSize;
00502   }
00503 
00504   if (!dict->lookupNF("W", &obj)->isArray() ||
00505       obj.arrayGetLength() < 3) {
00506     goto err1;
00507   }
00508   for (i = 0; i < 3; ++i) {
00509     if (!obj.arrayGet(i, &obj2)->isInt()) {
00510       obj2.free();
00511       goto err1;
00512     }
00513     w[i] = obj2.getInt();
00514     obj2.free();
00515     if (w[i] < 0 || w[i] > 4) {
00516       goto err1;
00517     }
00518   }
00519   obj.free();
00520 
00521   xrefStr->reset();
00522   dict->lookupNF("Index", &idx);
00523   if (idx.isArray()) {
00524     for (i = 0; i+1 < idx.arrayGetLength(); i += 2) {
00525       if (!idx.arrayGet(i, &obj)->isInt()) {
00526        idx.free();
00527        goto err1;
00528       }
00529       first = obj.getInt();
00530       obj.free();
00531       if (!idx.arrayGet(i+1, &obj)->isInt()) {
00532        idx.free();
00533        goto err1;
00534       }
00535       n = obj.getInt();
00536       obj.free();
00537       if (first < 0 || n < 0 ||
00538          !readXRefStreamSection(xrefStr, w, first, n)) {
00539        idx.free();
00540        goto err0;
00541       }
00542     }
00543   } else {
00544     if (!readXRefStreamSection(xrefStr, w, 0, newSize)) {
00545       idx.free();
00546       goto err0;
00547     }
00548   }
00549   idx.free();
00550 
00551   dict->lookupNF("Prev", &obj);
00552   if (obj.isInt()) {
00553     *pos = (Guint)obj.getInt();
00554     more = gTrue;
00555   } else {
00556     more = gFalse;
00557   }
00558   obj.free();
00559   if (trailerDict.isNone()) {
00560     trailerDict.initDict(dict);
00561   }
00562 
00563   return more;
00564 
00565  err1:
00566   obj.free();
00567  err0:
00568   ok = gFalse;
00569   return gFalse;
00570 }
00571 
00572 GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
00573   Guint offset;
00574   int type, gen, c, newSize, i, j;
00575 
00576   if (first + n < 0) {
00577     return gFalse;
00578   }
00579   if (first + n > size) {
00580     for (newSize = size ? 2 * size : 1024;
00581         first + n > newSize && newSize > 0;
00582         newSize <<= 1) ;
00583     if (newSize < 0) {
00584       return gFalse;
00585     }
00586     entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
00587     for (i = size; i < newSize; ++i) {
00588       entries[i].offset = 0xffffffff;
00589       entries[i].type = xrefEntryFree;
00590     }
00591     size = newSize;
00592   }
00593   for (i = first; i < first + n; ++i) {
00594     if (w[0] == 0) {
00595       type = 1;
00596     } else {
00597       for (type = 0, j = 0; j < w[0]; ++j) {
00598        if ((c = xrefStr->getChar()) == EOF) {
00599          return gFalse;
00600        }
00601        type = (type << 8) + c;
00602       }
00603     }
00604     for (offset = 0, j = 0; j < w[1]; ++j) {
00605       if ((c = xrefStr->getChar()) == EOF) {
00606        return gFalse;
00607       }
00608       offset = (offset << 8) + c;
00609     }
00610     for (gen = 0, j = 0; j < w[2]; ++j) {
00611       if ((c = xrefStr->getChar()) == EOF) {
00612        return gFalse;
00613       }
00614       gen = (gen << 8) + c;
00615     }
00616     if (entries[i].offset == 0xffffffff) {
00617       switch (type) {
00618       case 0:
00619        entries[i].offset = offset;
00620        entries[i].gen = gen;
00621        entries[i].type = xrefEntryFree;
00622        break;
00623       case 1:
00624        entries[i].offset = offset;
00625        entries[i].gen = gen;
00626        entries[i].type = xrefEntryUncompressed;
00627        break;
00628       case 2:
00629        entries[i].offset = offset;
00630        entries[i].gen = gen;
00631        entries[i].type = xrefEntryCompressed;
00632        break;
00633       default:
00634        return gFalse;
00635       }
00636     }
00637   }
00638 
00639   return gTrue;
00640 }
00641 
00642 // Attempt to construct an xref table for a damaged file.
00643 GBool XRef::constructXRef() {
00644   Parser *parser;
00645   Object newTrailerDict, obj;
00646   char buf[256];
00647   Guint pos;
00648   int num, gen;
00649   int newSize;
00650   int streamEndsSize;
00651   char *p;
00652   int i;
00653   GBool gotRoot;
00654 
00655   gfree(entries);
00656   size = 0;
00657   entries = NULL;
00658 
00659   error(0, "PDF file is damaged - attempting to reconstruct xref table...");
00660   gotRoot = gFalse;
00661   streamEndsLen = streamEndsSize = 0;
00662 
00663   str->reset();
00664   while (1) {
00665     pos = str->getPos();
00666     if (!str->getLine(buf, 256)) {
00667       break;
00668     }
00669     p = buf;
00670 
00671     // got trailer dictionary
00672     if (!strncmp(p, "trailer", 7)) {
00673       obj.initNull();
00674       parser = new Parser(NULL,
00675                new Lexer(NULL,
00676                  str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
00677       parser->getObj(&newTrailerDict);
00678       if (newTrailerDict.isDict()) {
00679        newTrailerDict.dictLookupNF("Root", &obj);
00680        if (obj.isRef()) {
00681          rootNum = obj.getRefNum();
00682          rootGen = obj.getRefGen();
00683          if (!trailerDict.isNone()) {
00684            trailerDict.free();
00685          }
00686          newTrailerDict.copy(&trailerDict);
00687          gotRoot = gTrue;
00688        }
00689        obj.free();
00690       }
00691       newTrailerDict.free();
00692       delete parser;
00693 
00694     // look for object
00695     } else if (isdigit(*p)) {
00696       num = atoi(p);
00697       if (num > 0) {
00698        do {
00699          ++p;
00700        } while (*p && isdigit(*p));
00701        if (isspace(*p)) {
00702          do {
00703            ++p;
00704          } while (*p && isspace(*p));
00705          if (isdigit(*p)) {
00706            gen = atoi(p);
00707            do {
00708              ++p;
00709            } while (*p && isdigit(*p));
00710            if (isspace(*p)) {
00711              do {
00712               ++p;
00713              } while (*p && isspace(*p));
00714              if (!strncmp(p, "obj", 3)) {
00715               if (num >= size) {
00716                 newSize = (num + 1 + 255) & ~255;
00717                 if (newSize < 0) {
00718                   error(-1, "Bad object number");
00719                   return gFalse;
00720                 }
00721                 entries = (XRefEntry *)
00722                     grealloc(entries, newSize * sizeof(XRefEntry));
00723                 for (i = size; i < newSize; ++i) {
00724                   entries[i].offset = 0xffffffff;
00725                   entries[i].type = xrefEntryFree;
00726                 }
00727                 size = newSize;
00728               }
00729               if (entries[num].type == xrefEntryFree ||
00730                   gen >= entries[num].gen) {
00731                 entries[num].offset = pos - start;
00732                 entries[num].gen = gen;
00733                 entries[num].type = xrefEntryUncompressed;
00734               }
00735              }
00736            }
00737          }
00738        }
00739       }
00740 
00741     } else if (!strncmp(p, "endstream", 9)) {
00742       if (streamEndsLen == streamEndsSize) {
00743        streamEndsSize += 64;
00744        streamEnds = (Guint *)grealloc(streamEnds,
00745                                    streamEndsSize * sizeof(int));
00746       }
00747       streamEnds[streamEndsLen++] = pos;
00748     }
00749   }
00750 
00751   if (gotRoot)
00752     return gTrue;
00753 
00754   error(-1, "Couldn't find trailer dictionary");
00755   return gFalse;
00756 }
00757 
00758 #ifndef NO_DECRYPTION
00759 GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
00760   Object encrypt, filterObj, versionObj, revisionObj, lengthObj;
00761   Object ownerKey, userKey, permissions, fileID, fileID1;
00762   GBool encrypted1;
00763   GBool ret;
00764 
00765   keyLength = 0;
00766   encVersion = encRevision = 0;
00767   ret = gFalse;
00768 
00769   permFlags = defPermFlags;
00770   ownerPasswordOk = gFalse;
00771   trailerDict.dictLookup("Encrypt", &encrypt);
00772   if ((encrypted1 = encrypt.isDict())) {
00773     ret = gTrue;
00774     encrypt.dictLookup("Filter", &filterObj);
00775     if (filterObj.isName("Standard")) {
00776       encrypt.dictLookup("V", &versionObj);
00777       encrypt.dictLookup("R", &revisionObj);
00778       encrypt.dictLookup("Length", &lengthObj);
00779       encrypt.dictLookup("O", &ownerKey);
00780       encrypt.dictLookup("U", &userKey);
00781       encrypt.dictLookup("P", &permissions);
00782       trailerDict.dictLookup("ID", &fileID);
00783       if (versionObj.isInt() &&
00784          revisionObj.isInt() &&
00785          ownerKey.isString() && ownerKey.getString()->getLength() == 32 &&
00786          userKey.isString() && userKey.getString()->getLength() == 32 &&
00787          permissions.isInt() &&
00788          fileID.isArray()) {
00789        encVersion = versionObj.getInt();
00790        encRevision = revisionObj.getInt();
00791        if (lengthObj.isInt()) {
00792          keyLength = lengthObj.getInt() / 8;
00793        } else {
00794          keyLength = 5;
00795        }
00796         if (keyLength > 16) {
00797           keyLength = 16;
00798         }
00799        permFlags = permissions.getInt();
00800        if (encVersion >= 1 && encVersion <= 2 &&
00801            encRevision >= 2 && encRevision <= 3) {
00802          fileID.arrayGet(0, &fileID1);
00803          if (fileID1.isString()) {
00804            if (Decrypt::makeFileKey(encVersion, encRevision, keyLength,
00805                                  ownerKey.getString(), userKey.getString(),
00806                                  permFlags, fileID1.getString(),
00807                                  ownerPassword, userPassword, fileKey,
00808                                  &ownerPasswordOk)) {
00809              if (ownerPassword && !ownerPasswordOk) {
00810               error(-1, "Incorrect owner password");
00811              }
00812              ret = gFalse;
00813            } else {
00814              error(-1, "Incorrect password");
00815            }
00816          } else {
00817            error(-1, "Weird encryption info");
00818          }
00819          fileID1.free();
00820        } else {
00821          error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
00822               encVersion, encRevision);
00823        }
00824       } else {
00825        error(-1, "Weird encryption info");
00826       }
00827       fileID.free();
00828       permissions.free();
00829       userKey.free();
00830       ownerKey.free();
00831       lengthObj.free();
00832       revisionObj.free();
00833       versionObj.free();
00834     } else {
00835       error(-1, "Unknown security handler '%s'",
00836            filterObj.isName() ? filterObj.getName() : "???");
00837     }
00838     filterObj.free();
00839   }
00840   encrypt.free();
00841 
00842   // this flag has to be set *after* we read the O/U/P strings
00843   encrypted = encrypted1;
00844 
00845   return ret;
00846 }
00847 #else
00848 GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
00849   Object obj;
00850   GBool encrypted;
00851 
00852   trailerDict.dictLookup("Encrypt", &obj);
00853   if ((encrypted = !obj.isNull())) {
00854     error(-1, "PDF file is encrypted and this version of the Xpdf tools");
00855     error(-1, "was built without decryption support.");
00856   }
00857   obj.free();
00858   return encrypted;
00859 }
00860 #endif
00861 
00862 GBool XRef::okToPrint(GBool ignoreOwnerPW) {
00863 #ifndef NO_DECRYPTION
00864   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
00865 #else
00866   return gTrue;
00867 #endif
00868 }
00869 
00870 GBool XRef::okToChange(GBool ignoreOwnerPW) {
00871 #ifndef NO_DECRYPTION
00872   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
00873 #else
00874   return gTrue;
00875 #endif
00876 }
00877 
00878 GBool XRef::okToCopy(GBool ignoreOwnerPW) {
00879 #ifndef NO_DECRYPTION
00880   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
00881 #else
00882   return gTrue;
00883 #endif
00884 }
00885 
00886 GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
00887 #ifndef NO_DECRYPTION
00888   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
00889 #else
00890   return gTrue;
00891 #endif
00892 }
00893 
00894 Object *XRef::fetch(int num, int gen, Object *obj) {
00895   XRefEntry *e;
00896   Parser *parser;
00897   Object obj1, obj2, obj3;
00898 
00899   // check for bogus ref - this can happen in corrupted PDF files
00900   if (num < 0 || num >= size) {
00901     goto err;
00902   }
00903 
00904   e = &entries[num];
00905   switch (e->type) {
00906 
00907   case xrefEntryUncompressed:
00908     if (e->gen != gen) {
00909       goto err;
00910     }
00911     obj1.initNull();
00912     parser = new Parser(this,
00913               new Lexer(this,
00914                str->makeSubStream(start + e->offset, gFalse, 0, &obj1)));
00915     parser->getObj(&obj1);
00916     parser->getObj(&obj2);
00917     parser->getObj(&obj3);
00918     if (!obj1.isInt() || obj1.getInt() != num ||
00919        !obj2.isInt() || obj2.getInt() != gen ||
00920        !obj3.isCmd("obj")) {
00921       goto err;
00922     }
00923 #ifndef NO_DECRYPTION
00924     parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
00925                  num, gen);
00926 #else
00927     parser->getObj(obj);
00928 #endif
00929     obj1.free();
00930     obj2.free();
00931     obj3.free();
00932     delete parser;
00933     break;
00934 
00935   case xrefEntryCompressed:
00936     if (gen != 0) {
00937       goto err;
00938     }
00939     if (!objStr || objStr->getObjStrNum() != (int)e->offset) {
00940       if (objStr) {
00941        delete objStr;
00942       }
00943       objStr = new ObjectStream(this, e->offset);
00944     }
00945     objStr->getObject(e->gen, num, obj);
00946     break;
00947 
00948   default:
00949     goto err;
00950   }
00951 
00952   return obj;
00953 
00954  err:
00955   return obj->initNull();
00956 }
00957 
00958 Object *XRef::getDocInfo(Object *obj) {
00959   return trailerDict.dictLookup("Info", obj);
00960 }
00961 
00962 // Added for the pdftex project.
00963 Object *XRef::getDocInfoNF(Object *obj) {
00964   return trailerDict.dictLookupNF("Info", obj);
00965 }
00966 
00967 GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
00968   int a, b, m;
00969 
00970   if (streamEndsLen == 0 ||
00971       streamStart > streamEnds[streamEndsLen - 1]) {
00972     return gFalse;
00973   }
00974 
00975   a = -1;
00976   b = streamEndsLen - 1;
00977   // invariant: streamEnds[a] < streamStart <= streamEnds[b]
00978   while (b - a > 1) {
00979     m = (a + b) / 2;
00980     if (streamStart <= streamEnds[m]) {
00981       b = m;
00982     } else {
00983       a = m;
00984     }
00985   }
00986   *streamEnd = streamEnds[b];
00987   return gTrue;
00988 }
00989 
00990 Guint XRef::strToUnsigned(char *s) {
00991   Guint x;
00992   char *p;
00993   int i;
00994 
00995   x = 0;
00996   for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
00997     x = 10 * x + (*p - '0');
00998   }
00999   return x;
01000 }