diff options
Diffstat (limited to 'Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc')
-rw-r--r-- | Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc | 365 |
1 files changed, 246 insertions, 119 deletions
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc index d6b43cd3bea..0c364638b39 100644 --- a/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc +++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc @@ -350,7 +350,7 @@ XRef::XRef(BaseStream *strA, GBool repair) { // read the xref table posSet = new XRefPosSet(); - while (readXRef(&pos, posSet)) ; + while (readXRef(&pos, posSet, gFalse)) ; xrefTablePosLen = posSet->getLength(); xrefTablePos = (GFileOffset *)gmallocn(xrefTablePosLen, sizeof(GFileOffset)); @@ -438,8 +438,10 @@ GFileOffset XRef::getStartXref() { } // Read one xref table section. Also reads the associated trailer -// dictionary, and returns the prev pointer (if any). -GBool XRef::readXRef(GFileOffset *pos, XRefPosSet *posSet) { +// dictionary, and returns the prev pointer (if any). The [hybrid] +// flag is true when following the XRefStm link in a hybrid-reference +// file. +GBool XRef::readXRef(GFileOffset *pos, XRefPosSet *posSet, GBool hybrid) { Parser *parser; Object obj; GBool more; @@ -461,47 +463,44 @@ GBool XRef::readXRef(GFileOffset *pos, XRefPosSet *posSet) { for (i = 0; i < n && Lexer::isSpace(buf[i]); ++i) ; // parse an old-style xref table - if (i + 4 < n && + if (!hybrid && + i + 4 < n && buf[i] == 'x' && buf[i+1] == 'r' && buf[i+2] == 'e' && buf[i+3] == 'f' && Lexer::isSpace(buf[i+4])) { more = readXRefTable(pos, i + 5, posSet); // parse an xref stream - } else if (i < n && buf[i] >= '0' && buf[i] <= '9') { + } else { obj.initNull(); parser = new Parser(NULL, new Lexer(NULL, str->makeSubStream(start + *pos, gFalse, 0, &obj)), gTrue); if (!parser->getObj(&obj, gTrue)->isInt()) { - goto err2; + goto err; } obj.free(); if (!parser->getObj(&obj, gTrue)->isInt()) { - goto err2; + goto err; } obj.free(); if (!parser->getObj(&obj, gTrue)->isCmd("obj")) { - goto err2; + goto err; } obj.free(); if (!parser->getObj(&obj)->isStream()) { - goto err2; + goto err; } - more = readXRefStream(obj.getStream(), pos); + more = readXRefStream(obj.getStream(), pos, hybrid); obj.free(); delete parser; - - } else { - goto err1; } return more; - err2: + err: obj.free(); delete parser; - err1: ok = gFalse; return gFalse; } @@ -662,7 +661,7 @@ GBool XRef::readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet) { //~ this can be a 64-bit int (?) if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) { pos2 = (GFileOffset)(Guint)obj2.getInt(); - readXRef(&pos2, posSet); + readXRef(&pos2, posSet, gTrue); if (!ok) { obj2.free(); obj.free(); @@ -679,7 +678,7 @@ GBool XRef::readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet) { return gFalse; } -GBool XRef::readXRefStream(Stream *xrefStr, GFileOffset *pos) { +GBool XRef::readXRefStream(Stream *xrefStr, GFileOffset *pos, GBool hybrid) { Dict *dict; int w[3]; GBool more; @@ -857,125 +856,250 @@ GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) { // Attempt to construct an xref table for a damaged file. GBool XRef::constructXRef() { - Parser *parser; - Object newTrailerDict, obj; - char buf[256]; - GFileOffset pos; - int num, gen; - int newSize; - int streamEndsSize; - char *p; - int i; - GBool gotRoot; - - gfree(entries); - size = 0; - entries = NULL; - - gotRoot = gFalse; - streamEndsLen = streamEndsSize = 0; - + int *streamObjNums = NULL; + int streamObjNumsLen = 0; + int streamObjNumsSize = 0; + int lastObjNum = -1; + rootNum = -1; + int streamEndsSize = 0; + streamEndsLen = 0; + char buf[4096 + 1]; str->reset(); + GFileOffset bufPos = start; + char *p = buf; + char *end = buf; + GBool startOfLine = gTrue; + GBool eof = gFalse; while (1) { - pos = str->getPos(); - if (!str->getLine(buf, 256)) { + if (end - p < 256 && !eof) { + memcpy(buf, p, end - p); + bufPos += p - buf; + p = buf + (end - p); + int n = (int)(buf + 4096 - p); + int m = str->getBlock(p, n); + end = p + m; + *end = '\0'; + p = buf; + eof = m < n; + } + if (p == end && eof) { break; } - p = buf; + if (startOfLine && !strncmp(p, "trailer", 7)) { + constructTrailerDict((GFileOffset)(bufPos + (p + 7 - buf))); + p += 7; + startOfLine = gFalse; + } else if (startOfLine && !strncmp(p, "endstream", 9)) { + if (streamEndsLen == streamEndsSize) { + streamEndsSize += 64; + streamEnds = (GFileOffset *)greallocn(streamEnds, streamEndsSize, + sizeof(GFileOffset)); + } + streamEnds[streamEndsLen++] = (GFileOffset)(bufPos + (p - buf)); + p += 9; + startOfLine = gFalse; + } else if (startOfLine && *p >= '0' && *p <= '9') { + p = constructObjectEntry(p, (GFileOffset)(bufPos + (p - buf)), + &lastObjNum); + startOfLine = gFalse; + } else if (p[0] == '>' && p[1] == '>') { + p += 2; + startOfLine = gFalse; + // skip any PDF whitespace except for '\0' + while (*p == '\t' || *p == '\n' || *p == '\x0c' || + *p == '\r' || *p == ' ') { + startOfLine = *p == '\n' || *p == '\r'; + ++p; + } + if (!strncmp(p, "stream", 6)) { + if (lastObjNum >= 0) { + if (streamObjNumsLen == streamObjNumsSize) { + streamObjNumsSize += 64; + streamObjNums = (int *)greallocn(streamObjNums, streamObjNumsSize, + sizeof(int)); + } + streamObjNums[streamObjNumsLen++] = lastObjNum; + } + p += 6; + startOfLine = gFalse; + } + } else { + startOfLine = *p == '\n' || *p == '\r'; + ++p; + } + } - // skip whitespace - while (*p && Lexer::isSpace(*p & 0xff)) ++p; + // read each stream object, check for xref or object stream + for (int i = 0; i < streamObjNumsLen; ++i) { + Object obj; + fetch(streamObjNums[i], entries[streamObjNums[i]].gen, &obj); + if (obj.isStream()) { + Dict *dict = obj.streamGetDict(); + Object type; + dict->lookup("Type", &type); + if (type.isName("XRef")) { + saveTrailerDict(dict, gTrue); + } else if (type.isName("ObjStm")) { + constructObjectStreamEntries(&obj, streamObjNums[i]); + } + type.free(); + } + obj.free(); + } + + gfree(streamObjNums); - // got trailer dictionary - if (!strncmp(p, "trailer", 7)) { - obj.initNull(); - parser = new Parser(NULL, + if (rootNum < 0) { + error(errSyntaxError, -1, "Couldn't find trailer dictionary"); + return gFalse; + } + return gTrue; +} + +// Attempt to construct a trailer dict at [pos] in the stream. +void XRef::constructTrailerDict(GFileOffset pos) { + Object newTrailerDict, obj; + obj.initNull(); + Parser *parser = + new Parser(NULL, new Lexer(NULL, - str->makeSubStream(pos + 7, gFalse, 0, &obj)), + str->makeSubStream(pos, gFalse, 0, &obj)), gFalse); - parser->getObj(&newTrailerDict); - if (newTrailerDict.isDict()) { - newTrailerDict.dictLookupNF("Root", &obj); - if (obj.isRef()) { - rootNum = obj.getRefNum(); - rootGen = obj.getRefGen(); - if (!trailerDict.isNone()) { - trailerDict.free(); - } - newTrailerDict.copy(&trailerDict); - gotRoot = gTrue; - } - obj.free(); - } - newTrailerDict.free(); - delete parser; + parser->getObj(&newTrailerDict); + if (newTrailerDict.isDict()) { + saveTrailerDict(newTrailerDict.getDict(), gFalse); + } + newTrailerDict.free(); + delete parser; +} - // look for object - } else if (isdigit(*p & 0xff)) { - num = atoi(p); - if (num > 0) { - do { - ++p; - } while (*p && isdigit(*p & 0xff)); - if (isspace(*p & 0xff)) { - do { - ++p; - } while (*p && isspace(*p & 0xff)); - if (isdigit(*p & 0xff)) { - gen = atoi(p); - do { - ++p; - } while (*p && isdigit(*p & 0xff)); - if (isspace(*p & 0xff)) { - do { - ++p; - } while (*p && isspace(*p & 0xff)); - if (!strncmp(p, "obj", 3)) { - if (num >= size) { - newSize = (num + 1 + 255) & ~255; - if (newSize < 0) { - error(errSyntaxError, -1, "Bad object number"); - return gFalse; - } - entries = (XRefEntry *) - greallocn(entries, newSize, sizeof(XRefEntry)); - for (i = size; i < newSize; ++i) { - entries[i].offset = (GFileOffset)-1; - 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; - if (num > last) { - last = num; - } - } - } - } - } - } +// If [dict] "looks like" a trailer dict (i.e., has a Root entry), +// save it as the trailer dict. +void XRef::saveTrailerDict(Dict *dict, GBool isXRefStream) { + Object obj; + dict->lookupNF("Root", &obj); + if (obj.isRef()) { + int newRootNum = obj.getRefNum(); + // the xref stream scanning code runs after all objects are found, + // so we can check for a valid root object number at that point + if (!isXRefStream || newRootNum <= last) { + rootNum = newRootNum; + rootGen = obj.getRefGen(); + if (!trailerDict.isNone()) { + trailerDict.free(); } + trailerDict.initDict(dict); + } + } + obj.free(); +} - } else if (!strncmp(p, "endstream", 9)) { - if (streamEndsLen == streamEndsSize) { - streamEndsSize += 64; - streamEnds = (GFileOffset *)greallocn(streamEnds, streamEndsSize, - sizeof(GFileOffset)); +// Look for an object header ("nnn ggg obj") at [p]. The first +// character at *[p] is a digit. [pos] is the position of *[p]. +char *XRef::constructObjectEntry(char *p, GFileOffset pos, int *objNum) { + // we look for non-end-of-line space characters here, to deal with + // situations like: + // nnn <-- garbage digits on a line + // nnn nnn obj <-- actual object + // and we also ignore '\0' (because it's used to terminate the + // buffer in this damage-scanning code) + int num = 0; + do { + num = (num * 10) + (*p - '0'); + ++p; + } while (*p >= '0' && *p <= '9' && num < 100000000); + if (*p != '\t' && *p != '\x0c' && *p != ' ') { + return p; + } + do { + ++p; + } while (*p == '\t' || *p == '\x0c' || *p == ' '); + if (!(*p >= '0' && *p <= '9')) { + return p; + } + int gen = 0; + do { + gen = (gen * 10) + (*p - '0'); + ++p; + } while (*p >= '0' && *p <= '9' && gen < 100000000); + if (*p != '\t' && *p != '\x0c' && *p != ' ') { + return p; + } + do { + ++p; + } while (*p == '\t' || *p == '\x0c' || *p == ' '); + if (strncmp(p, "obj", 3)) { + return p; + } + + if (constructXRefEntry(num, gen, pos - start, xrefEntryUncompressed)) { + *objNum = num; + } + + return p; +} + +// Read the header from an object stream, and add xref entries for all +// of its objects. +void XRef::constructObjectStreamEntries(Object *objStr, int objStrObjNum) { + Object obj1, obj2; + + // get the object count + if (!objStr->streamGetDict()->lookup("N", &obj1)->isInt()) { + obj1.free(); + return; + } + int nObjects = obj1.getInt(); + obj1.free(); + if (nObjects <= 0 || nObjects > 1000000) { + return; + } + + // parse the header: object numbers and offsets + Parser *parser = new Parser(NULL, + new Lexer(NULL, objStr->getStream()->copy()), + gFalse); + for (int i = 0; i < nObjects; ++i) { + parser->getObj(&obj1, gTrue); + parser->getObj(&obj2, gTrue); + if (obj1.isInt() && obj2.isInt()) { + int num = obj1.getInt(); + if (num >= 0 && num < 1000000) { + constructXRefEntry(num, i, objStrObjNum, xrefEntryCompressed); } - streamEnds[streamEndsLen++] = pos; } + obj2.free(); + obj1.free(); } + delete parser; +} - if (gotRoot) { - return gTrue; +GBool XRef::constructXRefEntry(int num, int gen, GFileOffset pos, + XRefEntryType type) { + if (num >= size) { + int newSize = (num + 1 + 255) & ~255; + if (newSize < 0) { + return gFalse; + } + entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry)); + for (int i = size; i < newSize; ++i) { + entries[i].offset = (GFileOffset)-1; + entries[i].type = xrefEntryFree; + } + size = newSize; } - error(errSyntaxError, -1, "Couldn't find trailer dictionary"); - return gFalse; + if (entries[num].type == xrefEntryFree || + gen >= entries[num].gen) { + entries[num].offset = pos; + entries[num].gen = gen; + entries[num].type = type; + if (num > last) { + last = num; + } + } + + return gTrue; } void XRef::setEncryption(int permFlagsA, GBool ownerPasswordOkA, @@ -1153,6 +1277,9 @@ GBool XRef::getObjectStreamObject(int objStrNum, int objIdx, gLockMutex(&objStrsMutex); #endif if (!(objStr = getObjectStream(objStrNum))) { +#if MULTITHREADED + gUnlockMutex(&objStrsMutex); +#endif return gFalse; } cleanObjectStreamCache(); |