summaryrefslogtreecommitdiff
path: root/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc')
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc365
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();