| //======================================================================== |
| // |
| // Hints.cc |
| // |
| // This file is licensed under the GPLv2 or later |
| // |
| // Copyright 2010 Hib Eris <hib@hiberis.nl> |
| // Copyright 2010, 2011 Albert Astals Cid <aacid@kde.org> |
| // Copyright 2010 Pino Toscano <pino@kde.org> |
| // |
| //======================================================================== |
| |
| #include <config.h> |
| |
| #include "Hints.h" |
| |
| #include "Linearization.h" |
| #include "Object.h" |
| #include "Stream.h" |
| #include "XRef.h" |
| #include "Parser.h" |
| #include "Lexer.h" |
| #include "SecurityHandler.h" |
| |
| #include <limits.h> |
| |
| //------------------------------------------------------------------------ |
| // Hints |
| //------------------------------------------------------------------------ |
| |
| Hints::Hints(BaseStream *str, Linearization *linearization, XRef *xref, SecurityHandler *secHdlr) |
| { |
| mainXRefEntriesOffset = linearization->getMainXRefEntriesOffset(); |
| nPages = linearization->getNumPages(); |
| pageFirst = linearization->getPageFirst(); |
| pageEndFirst = linearization->getEndFirst(); |
| pageObjectFirst = linearization->getObjectNumberFirst(); |
| if (pageObjectFirst < 0 || pageObjectFirst >= xref->getNumObjects()) { |
| error(-1, |
| "Invalid reference for first page object (%d) in linearization table ", |
| pageObjectFirst); |
| pageObjectFirst = 0; |
| } |
| pageOffsetFirst = xref->getEntry(pageObjectFirst)->offset; |
| |
| if (nPages >= INT_MAX / (int)sizeof(Guint)) { |
| error(-1, "Invalid number of pages (%d) for hints table", nPages); |
| nPages = 0; |
| } |
| nObjects = (Guint *) gmallocn_checkoverflow(nPages, sizeof(Guint)); |
| pageObjectNum = (int *) gmallocn_checkoverflow(nPages, sizeof(int)); |
| xRefOffset = (Guint *) gmallocn_checkoverflow(nPages, sizeof(Guint)); |
| pageLength = (Guint *) gmallocn_checkoverflow(nPages, sizeof(Guint)); |
| pageOffset = (Guint *) gmallocn_checkoverflow(nPages, sizeof(Guint)); |
| numSharedObject = (Guint *) gmallocn_checkoverflow(nPages, sizeof(Guint)); |
| sharedObjectId = (Guint **) gmallocn_checkoverflow(nPages, sizeof(Guint*)); |
| if (!nObjects || !pageObjectNum || !xRefOffset || !pageLength || !pageOffset || |
| !numSharedObject || !sharedObjectId) { |
| error(-1, "Failed to allocate memory for hints tabel"); |
| nPages = 0; |
| } |
| |
| memset(numSharedObject, 0, nPages * sizeof(Guint)); |
| memset(pageObjectNum, 0, nPages * sizeof(int)); |
| |
| nSharedGroups = 0; |
| groupLength = NULL; |
| groupOffset = NULL; |
| groupHasSignature = NULL; |
| groupNumObjects = NULL; |
| groupXRefOffset = NULL; |
| |
| readTables(str, linearization, xref, secHdlr); |
| } |
| |
| Hints::~Hints() |
| { |
| gfree(nObjects); |
| gfree(pageObjectNum); |
| gfree(xRefOffset); |
| gfree(pageLength); |
| gfree(pageOffset); |
| for (int i=0; i< nPages; i++) { |
| if (numSharedObject[i]) { |
| gfree(sharedObjectId[i]); |
| } |
| } |
| gfree(sharedObjectId); |
| gfree(numSharedObject); |
| |
| gfree(groupLength); |
| gfree(groupOffset); |
| gfree(groupHasSignature); |
| gfree(groupNumObjects); |
| gfree(groupXRefOffset); |
| } |
| |
| void Hints::readTables(BaseStream *str, Linearization *linearization, XRef *xref, SecurityHandler *secHdlr) |
| { |
| hintsOffset = linearization->getHintsOffset(); |
| hintsLength = linearization->getHintsLength(); |
| hintsOffset2 = linearization->getHintsOffset2(); |
| hintsLength2 = linearization->getHintsLength2(); |
| |
| Parser *parser; |
| Object obj; |
| |
| int bufLength = hintsLength + hintsLength2; |
| |
| std::vector<char> buf(bufLength); |
| char *p = &buf[0]; |
| |
| obj.initNull(); |
| Stream *s = str->makeSubStream(hintsOffset, gFalse, hintsLength, &obj); |
| s->reset(); |
| for (Guint i=0; i < hintsLength; i++) { *p++ = s->getChar(); } |
| delete s; |
| |
| if (hintsOffset2 && hintsLength2) { |
| obj.initNull(); |
| s = str->makeSubStream(hintsOffset2, gFalse, hintsLength2, &obj); |
| s->reset(); |
| for (Guint i=0; i < hintsLength2; i++) { *p++ = s->getChar(); } |
| delete s; |
| } |
| |
| obj.initNull(); |
| MemStream *memStream = new MemStream (&buf[0], 0, bufLength, &obj); |
| |
| obj.initNull(); |
| parser = new Parser(xref, new Lexer(xref, memStream), gTrue); |
| |
| int num, gen; |
| if (parser->getObj(&obj)->isInt() && |
| (num = obj.getInt(), obj.free(), parser->getObj(&obj)->isInt()) && |
| (gen = obj.getInt(), obj.free(), parser->getObj(&obj)->isCmd("obj")) && |
| (obj.free(), parser->getObj(&obj, |
| secHdlr ? secHdlr->getFileKey() : (Guchar *)NULL, |
| secHdlr ? secHdlr->getEncAlgorithm() : cryptRC4, |
| secHdlr ? secHdlr->getFileKeyLength() : 0, |
| num, gen)->isStream())) { |
| Stream *hintsStream = obj.getStream(); |
| Dict *hintsDict = obj.streamGetDict(); |
| |
| int sharedStreamOffset = 0; |
| if (hintsDict->lookupInt("S", NULL, &sharedStreamOffset) && |
| sharedStreamOffset > 0) { |
| |
| hintsStream->reset(); |
| readPageOffsetTable(hintsStream); |
| |
| hintsStream->reset(); |
| for (int i=0; i<sharedStreamOffset; i++) hintsStream->getChar(); |
| readSharedObjectsTable(hintsStream); |
| } else { |
| error(-1, "Invalid shared object hint table offset"); |
| } |
| } else { |
| error(-1, "Failed parsing hints table object"); |
| } |
| obj.free(); |
| |
| delete parser; |
| } |
| |
| void Hints::readPageOffsetTable(Stream *str) |
| { |
| if (nPages < 1) { |
| error(-1, "Invalid number of pages reading page offset hints table"); |
| return; |
| } |
| |
| inputBits = 0; // reset on byte boundary. |
| |
| nObjectLeast = readBits(32, str); |
| |
| objectOffsetFirst = readBits(32, str); |
| if (objectOffsetFirst >= hintsOffset) objectOffsetFirst += hintsLength; |
| |
| nBitsDiffObjects = readBits(16, str); |
| |
| pageLengthLeast = readBits(32, str); |
| |
| nBitsDiffPageLength = readBits(16, str); |
| |
| OffsetStreamLeast = readBits(32, str); |
| |
| nBitsOffsetStream = readBits(16, str); |
| |
| lengthStreamLeast = readBits(32, str); |
| |
| nBitsLengthStream = readBits(16, str); |
| |
| nBitsNumShared = readBits(16, str); |
| |
| nBitsShared = readBits(16, str); |
| |
| nBitsNumerator = readBits(16, str); |
| |
| denominator = readBits(16, str); |
| |
| for (int i=0; i<nPages; i++) { |
| nObjects[i] = nObjectLeast + readBits(nBitsDiffObjects, str); |
| } |
| |
| nObjects[0] = 0; |
| xRefOffset[0] = mainXRefEntriesOffset + 20; |
| for (int i=1; i<nPages; i++) { |
| xRefOffset[i] = xRefOffset[i-1] + 20*nObjects[i-1]; |
| } |
| |
| pageObjectNum[0] = 1; |
| for (int i=1; i<nPages; i++) { |
| pageObjectNum[i] = pageObjectNum[i-1] + nObjects[i-1]; |
| } |
| pageObjectNum[0] = pageObjectFirst; |
| |
| inputBits = 0; // reset on byte boundary. Not in specs! |
| for (int i=0; i<nPages; i++) { |
| pageLength[i] = pageLengthLeast + readBits(nBitsDiffPageLength, str); |
| } |
| |
| inputBits = 0; // reset on byte boundary. Not in specs! |
| numSharedObject[0] = readBits(nBitsNumShared, str); |
| numSharedObject[0] = 0; // Do not trust the read value to be 0. |
| sharedObjectId[0] = NULL; |
| for (int i=1; i<nPages; i++) { |
| numSharedObject[i] = readBits(nBitsNumShared, str); |
| if (numSharedObject[i] >= INT_MAX / (int)sizeof(Guint)) { |
| error(-1, "Invalid number of shared objects"); |
| numSharedObject[i] = 0; |
| return; |
| } |
| sharedObjectId[i] = (Guint *) gmallocn_checkoverflow(numSharedObject[i], sizeof(Guint)); |
| if (numSharedObject[i] && !sharedObjectId[i]) { |
| error(-1, "Failed to allocate memory for shared object IDs"); |
| numSharedObject[i] = 0; |
| return; |
| } |
| } |
| |
| inputBits = 0; // reset on byte boundary. Not in specs! |
| for (int i=1; i<nPages; i++) { |
| for (Guint j=0; j < numSharedObject[i]; j++) { |
| sharedObjectId[i][j] = readBits(nBitsShared, str); |
| } |
| } |
| |
| pageOffset[0] = pageOffsetFirst; |
| // find pageOffsets. |
| for (int i=1; i<nPages; i++) { |
| pageOffset[i] = pageOffset[i-1] + pageLength[i-1]; |
| } |
| |
| } |
| |
| void Hints::readSharedObjectsTable(Stream *str) |
| { |
| inputBits = 0; // reset on byte boundary. |
| |
| Guint firstSharedObjectNumber = readBits(32, str); |
| |
| Guint firstSharedObjectOffset = readBits(32, str); |
| firstSharedObjectOffset += hintsLength; |
| |
| Guint nSharedGroupsFirst = readBits(32, str); |
| |
| Guint nSharedGroups = readBits(32, str); |
| |
| Guint nBitsNumObjects = readBits(16, str); |
| |
| Guint groupLengthLeast = readBits(32, str); |
| |
| Guint nBitsDiffGroupLength = readBits(16, str); |
| |
| if ((!nSharedGroups) || (nSharedGroups >= INT_MAX / (int)sizeof(Guint))) { |
| error(-1, "Invalid number of shared object groups"); |
| nSharedGroups = 0; |
| return; |
| } |
| if ((!nSharedGroupsFirst) || (nSharedGroupsFirst > nSharedGroups)) { |
| error(-1, "Invalid number of first page shared object groups"); |
| nSharedGroupsFirst = nSharedGroups; |
| } |
| |
| groupLength = (Guint *) gmallocn_checkoverflow(nSharedGroups, sizeof(Guint)); |
| groupOffset = (Guint *) gmallocn_checkoverflow(nSharedGroups, sizeof(Guint)); |
| groupHasSignature = (Guint *) gmallocn_checkoverflow(nSharedGroups, sizeof(Guint)); |
| groupNumObjects = (Guint *) gmallocn_checkoverflow(nSharedGroups, sizeof(Guint)); |
| groupXRefOffset = (Guint *) gmallocn_checkoverflow(nSharedGroups, sizeof(Guint)); |
| if (!groupLength || !groupOffset || !groupHasSignature || |
| !groupNumObjects || !groupXRefOffset) { |
| error(-1, "Failed to allocate memory for shared object groups"); |
| nSharedGroups = 0; |
| return; |
| } |
| |
| inputBits = 0; // reset on byte boundary. Not in specs! |
| for (Guint i=0; i<nSharedGroups; i++) { |
| groupLength[i] = groupLengthLeast + readBits(nBitsDiffGroupLength, str); |
| } |
| |
| groupOffset[0] = objectOffsetFirst; |
| for (Guint i=1; i<nSharedGroupsFirst; i++) { |
| groupOffset[i] = groupOffset[i-1] + groupLength[i-1]; |
| } |
| if (nSharedGroups > nSharedGroupsFirst ) { |
| groupOffset[nSharedGroupsFirst] = firstSharedObjectOffset; |
| for (Guint i=nSharedGroupsFirst+1; i<nSharedGroups; i++) { |
| groupOffset[i] = groupOffset[i-1] + groupLength[i-1]; |
| } |
| } |
| |
| inputBits = 0; // reset on byte boundary. Not in specs! |
| for (Guint i=0; i<nSharedGroups; i++) { |
| groupHasSignature[i] = readBits(1, str); |
| } |
| |
| inputBits = 0; // reset on byte boundary. Not in specs! |
| for (Guint i=0; i<nSharedGroups; i++) { |
| if (groupHasSignature[i]) { |
| readBits(128, str); |
| } |
| } |
| |
| inputBits = 0; // reset on byte boundary. Not in specs! |
| for (Guint i=0; i<nSharedGroups; i++) { |
| groupNumObjects[i] = |
| nBitsNumObjects ? 1 + readBits(nBitsNumObjects, str) : 1; |
| } |
| |
| for (Guint i=0; i<nSharedGroupsFirst; i++) { |
| groupNumObjects[i] = 0; |
| groupXRefOffset[i] = 0; |
| } |
| if (nSharedGroups > nSharedGroupsFirst ) { |
| groupXRefOffset[nSharedGroupsFirst] = |
| mainXRefEntriesOffset + 20*firstSharedObjectNumber; |
| for (Guint i=nSharedGroupsFirst+1; i<nSharedGroups; i++) { |
| groupXRefOffset[i] = groupXRefOffset[i-1] + 20*groupNumObjects[i-1]; |
| } |
| } |
| } |
| |
| Guint Hints::getPageOffset(int page) |
| { |
| if ((page < 1) || (page > nPages)) return 0; |
| |
| if (page-1 > pageFirst) |
| return pageOffset[page-1]; |
| else if (page-1 < pageFirst) |
| return pageOffset[page]; |
| else |
| return pageOffset[0]; |
| } |
| |
| std::vector<ByteRange>* Hints::getPageRanges(int page) |
| { |
| if ((page < 1) || (page > nPages)) return NULL; |
| |
| int idx; |
| if (page-1 > pageFirst) |
| idx = page-1; |
| else if (page-1 < pageFirst) |
| idx = page; |
| else |
| idx = 0; |
| |
| ByteRange pageRange; |
| std::vector<ByteRange> *v = new std::vector<ByteRange>; |
| |
| pageRange.offset = pageOffset[idx]; |
| pageRange.length = pageLength[idx]; |
| v->push_back(pageRange); |
| |
| pageRange.offset = xRefOffset[idx]; |
| pageRange.length = 20*nObjects[idx]; |
| v->push_back(pageRange); |
| |
| for (Guint j=0; j<numSharedObject[idx]; j++) { |
| Guint k = sharedObjectId[idx][j]; |
| |
| pageRange.offset = groupOffset[k]; |
| pageRange.length = groupLength[k]; |
| v->push_back(pageRange); |
| |
| pageRange.offset = groupXRefOffset[k]; |
| pageRange.length = 20*groupNumObjects[k]; |
| v->push_back(pageRange); |
| } |
| |
| return v; |
| } |
| |
| Guint Hints::readBit(Stream *str) |
| { |
| Guint bit; |
| int c; |
| |
| if (inputBits == 0) { |
| if ((c = str->getChar()) == EOF) { |
| return (Guint) -1; |
| } |
| bitsBuffer = c; |
| inputBits = 8; |
| } |
| bit = (bitsBuffer >> (inputBits - 1)) & 1; |
| --inputBits; |
| return bit; |
| } |
| |
| Guint Hints::readBits(int n, Stream *str) |
| { |
| Guint bit, bits; |
| |
| if (n < 0) return -1; |
| if (n == 0) return 0; |
| |
| if (n == 1) |
| return readBit(str); |
| |
| bit = (readBit(str) << (n-1)); |
| if (bit == (Guint) -1) |
| return -1; |
| |
| bits = readBits(n-1, str); |
| if (bits == (Guint) -1) |
| return -1; |
| |
| return bit | bits; |
| } |
| |
| int Hints::getPageObjectNum(int page) { |
| if ((page < 1) || (page > nPages)) return 0; |
| |
| if (page-1 > pageFirst) |
| return pageObjectNum[page-1]; |
| else if (page-1 < pageFirst) |
| return pageObjectNum[page]; |
| else |
| return pageObjectNum[0]; |
| } |