summaryrefslogtreecommitdiff
path: root/Build/source/libs/poppler/poppler-src/poppler/XRef.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/poppler/poppler-src/poppler/XRef.cc')
-rw-r--r--Build/source/libs/poppler/poppler-src/poppler/XRef.cc1747
1 files changed, 0 insertions, 1747 deletions
diff --git a/Build/source/libs/poppler/poppler-src/poppler/XRef.cc b/Build/source/libs/poppler/poppler-src/poppler/XRef.cc
deleted file mode 100644
index 4352146ec45..00000000000
--- a/Build/source/libs/poppler/poppler-src/poppler/XRef.cc
+++ /dev/null
@@ -1,1747 +0,0 @@
-//========================================================================
-//
-// XRef.cc
-//
-// Copyright 1996-2003 Glyph & Cog, LLC
-//
-//========================================================================
-
-//========================================================================
-//
-// Modified under the Poppler project - http://poppler.freedesktop.org
-//
-// All changes made under the Poppler project to this file are licensed
-// under GPL version 2 or later
-//
-// Copyright (C) 2005 Dan Sheridan <dan.sheridan@postman.org.uk>
-// Copyright (C) 2005 Brad Hards <bradh@frogmouth.net>
-// Copyright (C) 2006, 2008, 2010, 2012-2014, 2016-2018 Albert Astals Cid <aacid@kde.org>
-// Copyright (C) 2007-2008 Julien Rebetez <julienr@svn.gnome.org>
-// Copyright (C) 2007 Carlos Garcia Campos <carlosgc@gnome.org>
-// Copyright (C) 2009, 2010 Ilya Gorenbein <igorenbein@finjan.com>
-// Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
-// Copyright (C) 2012, 2013, 2016 Thomas Freitag <Thomas.Freitag@kabelmail.de>
-// Copyright (C) 2012, 2013 Fabio D'Urso <fabiodurso@hotmail.it>
-// Copyright (C) 2013, 2014, 2017 Adrian Johnson <ajohnson@redneon.com>
-// Copyright (C) 2013 Pino Toscano <pino@kde.org>
-// Copyright (C) 2016 Jakub Alba <jakubalba@gmail.com>
-//
-// To see a description of the changes please see the Changelog file that
-// came with your tarball or type make ChangeLog if you are building from git
-//
-//========================================================================
-
-#include <config.h>
-#include "poppler-config.h"
-
-#ifdef USE_GCC_PRAGMAS
-#pragma implementation
-#endif
-
-#include <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include <math.h>
-#include <ctype.h>
-#include <limits.h>
-#include <float.h>
-#include "goo/gfile.h"
-#include "goo/gmem.h"
-#include "Object.h"
-#include "Stream.h"
-#include "Lexer.h"
-#include "Parser.h"
-#include "Dict.h"
-#include "Error.h"
-#include "ErrorCodes.h"
-#include "XRef.h"
-#include "PopplerCache.h"
-
-//------------------------------------------------------------------------
-// Permission bits
-// Note that the PDF spec uses 1 base (eg bit 3 is 1<<2)
-//------------------------------------------------------------------------
-
-#define permPrint (1<<2) // bit 3
-#define permChange (1<<3) // bit 4
-#define permCopy (1<<4) // bit 5
-#define permNotes (1<<5) // bit 6
-#define permFillForm (1<<8) // bit 9
-#define permAccessibility (1<<9) // bit 10
-#define permAssemble (1<<10) // bit 11
-#define permHighResPrint (1<<11) // bit 12
-#define defPermFlags 0xfffc
-
-#ifdef MULTITHREADED
-# define xrefLocker() MutexLocker locker(&mutex)
-# define xrefCondLocker(X) MutexLocker locker(&mutex, (X))
-#else
-# define xrefLocker()
-# define xrefCondLocker(X)
-#endif
-
-//------------------------------------------------------------------------
-// ObjectStream
-//------------------------------------------------------------------------
-
-class ObjectStream {
-public:
-
- // Create an object stream, using object number <objStrNum>,
- // generation 0.
- ObjectStream(XRef *xref, int objStrNumA, int recursion = 0);
-
- GBool isOk() { return ok; }
-
- ~ObjectStream();
-
- ObjectStream(const ObjectStream &) = delete;
- ObjectStream& operator=(const ObjectStream &) = delete;
-
- // 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);
-
-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)
- GBool ok;
-};
-
-class ObjectStreamKey : public PopplerCacheKey
-{
- public:
- ObjectStreamKey(int num) : objStrNum(num)
- {
- }
-
- bool operator==(const PopplerCacheKey &key) const override
- {
- const ObjectStreamKey *k = static_cast<const ObjectStreamKey*>(&key);
- return objStrNum == k->objStrNum;
- }
-
- const int objStrNum;
-};
-
-class ObjectStreamItem : public PopplerCacheItem
-{
- public:
- ObjectStreamItem(ObjectStream *objStr) : objStream(objStr)
- {
- }
-
- ~ObjectStreamItem()
- {
- delete objStream;
- }
-
- ObjectStream *objStream;
-};
-
-ObjectStream::ObjectStream(XRef *xref, int objStrNumA, int recursion) {
- Stream *str;
- Parser *parser;
- Goffset *offsets;
- Object objStr, obj1;
- Goffset first;
- int i;
-
- objStrNum = objStrNumA;
- nObjects = 0;
- objs = nullptr;
- objNums = nullptr;
- ok = gFalse;
-
- objStr = xref->fetch(objStrNum, 0, recursion);
- if (!objStr.isStream()) {
- return;
- }
-
- obj1 = objStr.streamGetDict()->lookup("N", recursion);
- if (!obj1.isInt()) {
- return;
- }
- nObjects = obj1.getInt();
- if (nObjects <= 0) {
- return;
- }
-
- obj1 = objStr.streamGetDict()->lookup("First", recursion);
- if (!obj1.isInt() && !obj1.isInt64()) {
- return;
- }
- if (obj1.isInt())
- first = obj1.getInt();
- else
- first = obj1.getInt64();
- if (first < 0) {
- return;
- }
-
- // this is an arbitrary limit to avoid integer overflow problems
- // in the 'new Object[nObjects]' call (Acrobat apparently limits
- // object streams to 100-200 objects)
- if (nObjects > 1000000) {
- error(errSyntaxError, -1, "Too many objects in an object stream");
- return;
- }
- objs = new Object[nObjects];
- objNums = (int *)gmallocn(nObjects, sizeof(int));
- offsets = (Goffset *)gmallocn(nObjects, sizeof(Goffset));
-
- // parse the header: object numbers and offsets
- objStr.streamReset();
- str = new EmbedStream(objStr.getStream(), Object(objNull), gTrue, first);
- parser = new Parser(xref, new Lexer(xref, str), gFalse);
- for (i = 0; i < nObjects; ++i) {
- obj1 = parser->getObj();
- Object obj2 = parser->getObj();
- if (!obj1.isInt() || !(obj2.isInt() || obj2.isInt64())) {
- delete parser;
- gfree(offsets);
- return;
- }
- objNums[i] = obj1.getInt();
- if (obj2.isInt())
- offsets[i] = obj2.getInt();
- else
- offsets[i] = obj2.getInt64();
- if (objNums[i] < 0 || offsets[i] < 0 ||
- (i > 0 && offsets[i] < offsets[i-1])) {
- delete parser;
- gfree(offsets);
- return;
- }
- }
- 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 (Goffset pos = first; pos < offsets[0]; ++pos) {
- objStr.getStream()->getChar();
- }
-
- // parse the objects
- for (i = 0; i < nObjects; ++i) {
- if (i == nObjects - 1) {
- str = new EmbedStream(objStr.getStream(), Object(objNull), gFalse, 0);
- } else {
- str = new EmbedStream(objStr.getStream(), Object(objNull), gTrue,
- offsets[i+1] - offsets[i]);
- }
- parser = new Parser(xref, new Lexer(xref, str), gFalse);
- objs[i] = parser->getObj();
- while (str->getChar() != EOF) ;
- delete parser;
- }
-
- gfree(offsets);
- ok = gTrue;
-}
-
-ObjectStream::~ObjectStream() {
- delete[] objs;
- gfree(objNums);
-}
-
-Object ObjectStream::getObject(int objIdx, int objNum) {
- if (objIdx < 0 || objIdx >= nObjects || objNum != objNums[objIdx]) {
- return Object(objNull);
- }
- return objs[objIdx].copy();
-}
-
-//------------------------------------------------------------------------
-// XRef
-//------------------------------------------------------------------------
-
-void XRef::init() {
-#ifdef MULTITHREADED
- gInitMutex(&mutex);
-#endif
- ok = gTrue;
- errCode = errNone;
- entries = nullptr;
- capacity = 0;
- size = 0;
- modified = gFalse;
- streamEnds = nullptr;
- streamEndsLen = 0;
- objStrs = new PopplerCache(5);
- mainXRefEntriesOffset = 0;
- xRefStream = gFalse;
- scannedSpecialFlags = gFalse;
- encrypted = gFalse;
- permFlags = defPermFlags;
- ownerPasswordOk = gFalse;
- rootNum = -1;
- strOwner = gFalse;
- xrefReconstructed = gFalse;
- encAlgorithm = cryptNone;
-}
-
-XRef::XRef() {
- init();
-}
-
-XRef::XRef(const Object *trailerDictA) {
- init();
-
- if (trailerDictA->isDict())
- trailerDict = trailerDictA->copy();
-}
-
-XRef::XRef(BaseStream *strA, Goffset pos, Goffset mainXRefEntriesOffsetA, GBool *wasReconstructed, GBool reconstruct) {
- Object obj;
-
- init();
- mainXRefEntriesOffset = mainXRefEntriesOffsetA;
-
- // read the trailer
- str = strA;
- start = str->getStart();
- prevXRefOffset = mainXRefOffset = pos;
-
- if (reconstruct && !(ok = constructXRef(wasReconstructed)))
- {
- errCode = errDamaged;
- return;
- }
- else
- {
- // if there was a problem with the 'startxref' position, try to
- // reconstruct the xref table
- if (prevXRefOffset == 0) {
- if (!(ok = constructXRef(wasReconstructed))) {
- errCode = errDamaged;
- return;
- }
-
- // read the xref table
- } else {
- std::vector<Goffset> followedXRefStm;
- readXRef(&prevXRefOffset, &followedXRefStm, nullptr);
-
- // if there was a problem with the xref table,
- // try to reconstruct it
- if (!ok) {
- if (!(ok = constructXRef(wasReconstructed))) {
- errCode = errDamaged;
- return;
- }
- }
- }
-
- // set size to (at least) the size specified in trailer dict
- obj = trailerDict.dictLookupNF("Size");
- if (!obj.isInt()) {
- error(errSyntaxWarning, -1, "No valid XRef size in trailer");
- } else {
- if (obj.getInt() > size) {
- if (resize(obj.getInt()) != obj.getInt()) {
- if (!(ok = constructXRef(wasReconstructed))) {
- errCode = errDamaged;
- return;
- }
- }
- }
- }
-
- // get the root dictionary (catalog) object
- obj = trailerDict.dictLookupNF("Root");
- if (obj.isRef()) {
- rootNum = obj.getRefNum();
- rootGen = obj.getRefGen();
- } else {
- if (!(ok = constructXRef(wasReconstructed))) {
- errCode = errDamaged;
- return;
- }
- }
- }
- // now set the trailer dictionary's xref pointer so we can fetch
- // indirect objects from it
- trailerDict.getDict()->setXRef(this);
-}
-
-XRef::~XRef() {
- for(int i=0; i<size; i++) {
- entries[i].obj.free ();
- }
- gfree(entries);
-
- if (streamEnds) {
- gfree(streamEnds);
- }
- if (objStrs) {
- delete objStrs;
- }
- if (strOwner) {
- delete str;
- }
-#ifdef MULTITHREADED
- gDestroyMutex(&mutex);
-#endif
-}
-
-XRef *XRef::copy() const {
- XRef *xref = new XRef();
- xref->str = str->copy();
- xref->strOwner = gTrue;
- xref->encrypted = encrypted;
- xref->permFlags = permFlags;
- xref->ownerPasswordOk = ownerPasswordOk;
- xref->rootGen = rootGen;
- xref->rootNum = rootNum;
-
- xref->start = start;
- xref->prevXRefOffset = prevXRefOffset;
- xref->mainXRefEntriesOffset = mainXRefEntriesOffset;
- xref->xRefStream = xRefStream;
- xref->trailerDict = trailerDict.copy();
- xref->encAlgorithm = encAlgorithm;
- xref->encRevision = encRevision;
- xref->encVersion = encVersion;
- xref->permFlags = permFlags;
- xref->keyLength = keyLength;
- xref->permFlags = permFlags;
- for (int i = 0; i < 32; i++) {
- xref->fileKey[i] = fileKey[i];
- }
-
- if (xref->reserve(size) == 0) {
- error(errSyntaxError, -1, "unable to allocate {0:d} entries", size);
- delete xref;
- return nullptr;
- }
- xref->size = size;
- for (int i = 0; i < size; ++i) {
- xref->entries[i].offset = entries[i].offset;
- xref->entries[i].type = entries[i].type;
- xref->entries[i].obj.initNullAfterMalloc();
- xref->entries[i].flags = entries[i].flags;
- xref->entries[i].gen = entries[i].gen;
- }
- xref->streamEndsLen = streamEndsLen;
- if (streamEndsLen != 0) {
- xref->streamEnds = (Goffset *)gmalloc(streamEndsLen * sizeof(Goffset));
- for (int i = 0; i < streamEndsLen; i++) {
- xref->streamEnds[i] = streamEnds[i];
- }
- }
- return xref;
-}
-
-int XRef::reserve(int newSize)
-{
- if (newSize > capacity) {
-
- int realNewSize;
- for (realNewSize = capacity ? 2 * capacity : 1024;
- newSize > realNewSize && realNewSize > 0;
- realNewSize <<= 1) ;
- if ((realNewSize < 0) ||
- (realNewSize >= INT_MAX / (int)sizeof(XRefEntry))) {
- return 0;
- }
-
- void *p = greallocn_checkoverflow(entries, realNewSize, sizeof(XRefEntry));
- if (p == nullptr) {
- return 0;
- }
-
- entries = (XRefEntry *) p;
- capacity = realNewSize;
-
- }
-
- return capacity;
-}
-
-int XRef::resize(int newSize)
-{
- if (newSize > size) {
-
- if (reserve(newSize) < newSize) return size;
-
- for (int i = size; i < newSize; ++i) {
- entries[i].offset = -1;
- entries[i].type = xrefEntryNone;
- entries[i].obj.initNullAfterMalloc ();
- entries[i].flags = 0;
- entries[i].gen = 0;
- }
- } else {
- for (int i = newSize; i < size; i++) {
- entries[i].obj.free ();
- }
- }
-
- size = newSize;
-
- return size;
-}
-
-/* Read one xref table section. Also reads the associated trailer
- * dictionary, and returns the prev pointer (if any).
- * Arguments:
- * pos Points to a Goffset containing the offset of the XRef
- * section to be read. If a prev pointer is found, *pos is
- * updated with its value
- * followedXRefStm Used in case of nested readXRef calls to spot circular
- * references in XRefStm pointers
- * xrefStreamObjsNum If not NULL, every time a XRef stream is encountered,
- * its object number is appended
- * Return value:
- * gTrue if a prev pointer is found, otherwise gFalse
- */
-GBool XRef::readXRef(Goffset *pos, std::vector<Goffset> *followedXRefStm, std::vector<int> *xrefStreamObjsNum) {
- Parser *parser;
- Object obj;
- GBool more;
-
- if (unlikely(start > (LLONG_MAX - *pos))) {
- ok = gFalse;
- return gFalse;
- }
-
- // start up a parser, parse one token
- parser = new Parser(nullptr,
- new Lexer(nullptr,
- str->makeSubStream(start + *pos, gFalse, 0, Object(objNull))),
- gTrue);
- obj = parser->getObj(gTrue);
-
- // parse an old-style xref table
- if (obj.isCmd("xref")) {
- more = readXRefTable(parser, pos, followedXRefStm, xrefStreamObjsNum);
-
- // parse an xref stream
- } else if (obj.isInt()) {
- const int objNum = obj.getInt();
- if (obj = parser->getObj(gTrue), !obj.isInt()) {
- goto err1;
- }
- if (obj = parser->getObj(gTrue), !obj.isCmd("obj")) {
- goto err1;
- }
- if (obj = parser->getObj(), !obj.isStream()) {
- goto err1;
- }
- if (trailerDict.isNone()) {
- xRefStream = gTrue;
- }
- if (xrefStreamObjsNum) {
- xrefStreamObjsNum->push_back(objNum);
- }
- more = readXRefStream(obj.getStream(), pos);
-
- } else {
- goto err1;
- }
-
- delete parser;
- return more;
-
- err1:
- delete parser;
- ok = gFalse;
- return gFalse;
-}
-
-GBool XRef::readXRefTable(Parser *parser, Goffset *pos, std::vector<Goffset> *followedXRefStm, std::vector<int> *xrefStreamObjsNum) {
- XRefEntry entry;
- GBool more;
- Object obj, obj2;
- Goffset pos2;
- int first, n;
-
- while (1) {
- obj = parser->getObj(gTrue);
- if (obj.isCmd("trailer")) {
- break;
- }
- if (!obj.isInt()) {
- goto err0;
- }
- first = obj.getInt();
- obj = parser->getObj(gTrue);
- if (!obj.isInt()) {
- goto err0;
- }
- n = obj.getInt();
- if (first < 0 || n < 0 || first > INT_MAX - n) {
- goto err0;
- }
- if (first + n > size) {
- if (resize(first + n) != first + n) {
- error(errSyntaxError, -1, "Invalid 'obj' parameters'");
- goto err0;
- }
- }
- for (int i = first; i < first + n; ++i) {
- obj = parser->getObj(gTrue);
- if (obj.isInt()) {
- entry.offset = obj.getInt();
- } else if (obj.isInt64()) {
- entry.offset = obj.getInt64();
- } else {
- goto err0;
- }
- obj = parser->getObj(gTrue);
- if (!obj.isInt()) {
- goto err0;
- }
- entry.gen = obj.getInt();
- entry.flags = 0;
- obj = parser->getObj(gTrue);
- if (obj.isCmd("n")) {
- entry.type = xrefEntryUncompressed;
- } else if (obj.isCmd("f")) {
- entry.type = xrefEntryFree;
- } else {
- goto err0;
- }
- if (entries[i].offset == -1) {
- entries[i].offset = entry.offset;
- entries[i].gen = entry.gen;
- entries[i].type = entry.type;
- entries[i].flags = entry.flags;
- entries[i].obj.setToNull();
-
- // 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].type == xrefEntryFree) {
- i = first = 0;
- entries[0].offset = 0;
- entries[0].gen = 65535;
- entries[0].type = xrefEntryFree;
- entries[0].flags = entries[1].flags;
- entries[0].obj = std::move(entries[1].obj);
-
- entries[1].offset = -1;
- entries[1].obj.setToNull();
- }
- }
- }
- }
-
- // read the trailer dictionary
- obj = parser->getObj();
- if (!obj.isDict()) {
- goto err0;
- }
-
- // get the 'Prev' pointer
- obj2 = obj.getDict()->lookupNF("Prev");
- if (obj2.isInt() || obj2.isInt64()) {
- if (obj2.isInt())
- pos2 = obj2.getInt();
- else
- pos2 = obj2.getInt64();
- if (pos2 != *pos) {
- *pos = pos2;
- more = gTrue;
- } else {
- error(errSyntaxWarning, -1, "Infinite loop in xref table");
- more = gFalse;
- }
- } else if (obj2.isRef()) {
- // certain buggy PDF generators generate "/Prev NNN 0 R" instead
- // of "/Prev NNN"
- pos2 = (Guint)obj2.getRefNum();
- if (pos2 != *pos) {
- *pos = pos2;
- more = gTrue;
- } else {
- error(errSyntaxWarning, -1, "Infinite loop in xref table");
- more = gFalse;
- }
- } else {
- more = gFalse;
- }
-
- // save the first trailer dictionary
- if (trailerDict.isNone()) {
- trailerDict = obj.copy();
- }
-
- // check for an 'XRefStm' key
- obj2 = obj.getDict()->lookup("XRefStm");
- if (obj2.isInt() || obj2.isInt64()) {
- if (obj2.isInt())
- pos2 = obj2.getInt();
- else
- pos2 = obj2.getInt64();
- for (size_t i = 0; ok == gTrue && i < followedXRefStm->size(); ++i) {
- if (followedXRefStm->at(i) == pos2) {
- ok = gFalse;
- }
- }
- if (ok) {
- followedXRefStm->push_back(pos2);
- readXRef(&pos2, followedXRefStm, xrefStreamObjsNum);
- }
- if (!ok) {
- goto err0;
- }
- }
-
- return more;
-
- err0:
- ok = gFalse;
- return gFalse;
-}
-
-GBool XRef::readXRefStream(Stream *xrefStr, Goffset *pos) {
- int w[3];
- GBool more;
- Object obj;
-
- ok = gFalse;
-
- Dict *dict = xrefStr->getDict();
- obj = dict->lookupNF("Size");
- if (!obj.isInt()) {
- return gFalse;
- }
- int newSize = obj.getInt();
- if (newSize < 0) {
- return gFalse;
- }
- if (newSize > size) {
- if (resize(newSize) != newSize) {
- error(errSyntaxError, -1, "Invalid 'size' parameter");
- return gFalse;
- }
- }
-
- obj = dict->lookupNF("W");
- if (!obj.isArray() || obj.arrayGetLength() < 3) {
- return gFalse;
- }
- for (int i = 0; i < 3; ++i) {
- Object obj2 = obj.arrayGet(i);
- if (!obj2.isInt()) {
- return gFalse;
- }
- w[i] = obj2.getInt();
- if (w[i] < 0) {
- return gFalse;
- }
- }
- if (w[0] > (int)sizeof(int) || w[1] > (int)sizeof(long long) || w[2] > (int)sizeof(long long)) {
- return gFalse;
- }
-
- xrefStr->reset();
- Object idx = dict->lookupNF("Index");
- if (idx.isArray()) {
- for (int i = 0; i+1 < idx.arrayGetLength(); i += 2) {
- obj = idx.arrayGet(i);
- if (!obj.isInt()) {
- return gFalse;
- }
- int first = obj.getInt();
- obj = idx.arrayGet(i+1);
- if (!obj.isInt()) {
- return gFalse;
- }
- int n = obj.getInt();
- if (first < 0 || n < 0 ||
- !readXRefStreamSection(xrefStr, w, first, n)) {
- return gFalse;
- }
- }
- } else {
- if (!readXRefStreamSection(xrefStr, w, 0, newSize)) {
- return gFalse;
- }
- }
-
- obj = dict->lookupNF("Prev");
- if (obj.isInt()) {
- *pos = obj.getInt();
- more = gTrue;
- } else if (obj.isInt64()) {
- *pos = obj.getInt64();
- more = gTrue;
- } else {
- more = gFalse;
- }
- if (trailerDict.isNone()) {
- trailerDict = xrefStr->getDictObject()->copy();
- }
-
- ok = gTrue;
- return more;
-}
-
-GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
- unsigned long long offset, gen;
- int type, c, i, j;
-
- if (first > INT_MAX - n) {
- return gFalse;
- }
- if (first + n < 0) {
- return gFalse;
- }
- if (first + n > size) {
- if (resize(first + n) != size) {
- error(errSyntaxError, -1, "Invalid 'size' inside xref table");
- return gFalse;
- }
- if (first + n > size) {
- error(errSyntaxError, -1, "Invalid 'first' or 'n' inside xref table");
- return gFalse;
- }
- }
- 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;
- }
- if (offset > (unsigned long long)GoffsetMax()) {
- error(errSyntaxError, -1, "Offset inside xref table too large for fseek");
- return gFalse;
- }
- for (gen = 0, j = 0; j < w[2]; ++j) {
- if ((c = xrefStr->getChar()) == EOF) {
- return gFalse;
- }
- gen = (gen << 8) + c;
- }
- if (gen > INT_MAX) {
- error(errSyntaxError, -1, "Gen inside xref table too large (bigger than INT_MAX)");
- return gFalse;
- }
- if (entries[i].offset == -1) {
- 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(GBool *wasReconstructed, GBool needCatalogDict) {
- Parser *parser;
- Object obj;
- char buf[256];
- Goffset pos;
- int num, gen;
- int streamEndsSize;
- char *p;
- GBool gotRoot;
- char* token = nullptr;
- bool oneCycle = true;
- int offset = 0;
-
- resize(0); // free entries properly
- gfree(entries);
- capacity = 0;
- size = 0;
- entries = nullptr;
-
- gotRoot = gFalse;
- streamEndsLen = streamEndsSize = 0;
-
- if (wasReconstructed)
- {
- *wasReconstructed = true;
- }
-
- str->reset();
- while (1) {
- pos = str->getPos();
- if (!str->getLine(buf, 256)) {
- break;
- }
- p = buf;
-
- // skip whitespace
- while (*p && Lexer::isSpace(*p & 0xff)) ++p;
-
- oneCycle = true;
- offset = 0;
-
- while( ( token = strstr( p, "endobj" ) ) || oneCycle ) {
- oneCycle = false;
-
- if( token ) {
- oneCycle = true;
- token[0] = '\0';
- offset = token - p;
- }
-
- // got trailer dictionary
- if (!strncmp(p, "trailer", 7)) {
- parser = new Parser(nullptr,
- new Lexer(nullptr,
- str->makeSubStream(pos + 7, gFalse, 0, Object(objNull))),
- gFalse);
- Object newTrailerDict = parser->getObj();
- if (newTrailerDict.isDict()) {
- obj = newTrailerDict.dictLookupNF("Root");
- if (obj.isRef() && (!gotRoot || !needCatalogDict) && rootNum != obj.getRefNum()) {
- rootNum = obj.getRefNum();
- rootGen = obj.getRefGen();
- trailerDict = newTrailerDict.copy();
- gotRoot = gTrue;
- }
- }
- delete parser;
-
- // look for object
- } else if (isdigit(*p & 0xff)) {
- num = atoi(p);
- if (num > 0) {
- do {
- ++p;
- } while (*p && isdigit(*p & 0xff));
- if ((*p & 0xff) == 0 || isspace(*p & 0xff)) {
- if ((*p & 0xff) == 0) {
- //new line, continue with next line!
- str->getLine(buf, 256);
- p = buf;
- } else {
- ++p;
- }
- while (*p && isspace(*p & 0xff)) ++p;
- if (isdigit(*p & 0xff)) {
- gen = atoi(p);
- do {
- ++p;
- } while (*p && isdigit(*p & 0xff));
- if ((*p & 0xff) == 0 || isspace(*p & 0xff)) {
- if ((*p & 0xff) == 0) {
- //new line, continue with next line!
- str->getLine(buf, 256);
- p = buf;
- } else {
- ++p;
- }
- while (*p && isspace(*p & 0xff)) ++p;
- if (!strncmp(p, "obj", 3)) {
- if (num >= size) {
- if (unlikely(num >= INT_MAX - 1 - 255)) {
- error(errSyntaxError, -1, "Bad object number");
- return gFalse;
- }
- const int newSize = (num + 1 + 255) & ~255;
- if (newSize < 0) {
- error(errSyntaxError, -1, "Bad object number");
- return gFalse;
- }
- if (resize(newSize) != newSize) {
- error(errSyntaxError, -1, "Invalid 'obj' parameters");
- return gFalse;
- }
- }
- if (entries[num].type == xrefEntryFree ||
- gen >= entries[num].gen) {
- entries[num].offset = pos - start;
- entries[num].gen = gen;
- entries[num].type = xrefEntryUncompressed;
- }
- }
- }
- }
- }
- }
-
- } else {
- char *endstream = strstr( p, "endstream" );
- if (endstream) {
- int endstreamPos = endstream - p;
- if ((endstreamPos == 0 || Lexer::isSpace(p[endstreamPos-1] & 0xff)) // endstream is either at beginning or preceeded by space
- && (endstreamPos + 9 >= 256 || Lexer::isSpace(p[endstreamPos+9] & 0xff))) // endstream is either at end or followed by space
- {
- if (streamEndsLen == streamEndsSize) {
- streamEndsSize += 64;
- if (streamEndsSize >= INT_MAX / (int)sizeof(int)) {
- error(errSyntaxError, -1, "Invalid 'endstream' parameter.");
- return gFalse;
- }
- streamEnds = (Goffset *)greallocn(streamEnds,
- streamEndsSize, sizeof(Goffset));
- }
- streamEnds[streamEndsLen++] = pos + endstreamPos;
- }
- }
- }
- if( token ) {
- p = token + 6;// strlen( "endobj" ) = 6
- pos += offset + 6;// strlen( "endobj" ) = 6
- while (*p && Lexer::isSpace(*p & 0xff)) {
- ++p;
- ++pos;
- }
- }
- }
- }
-
- if (gotRoot)
- return gTrue;
-
- error(errSyntaxError, -1, "Couldn't find trailer dictionary");
- return gFalse;
-}
-
-void XRef::setEncryption(int permFlagsA, GBool ownerPasswordOkA,
- const Guchar *fileKeyA, int keyLengthA,
- int encVersionA, int encRevisionA,
- CryptAlgorithm encAlgorithmA) {
- int i;
-
- encrypted = gTrue;
- permFlags = permFlagsA;
- ownerPasswordOk = ownerPasswordOkA;
- if (keyLengthA <= 32) {
- keyLength = keyLengthA;
- } else {
- keyLength = 32;
- }
- for (i = 0; i < keyLength; ++i) {
- fileKey[i] = fileKeyA[i];
- }
- encVersion = encVersionA;
- encRevision = encRevisionA;
- encAlgorithm = encAlgorithmA;
-}
-
-void XRef::getEncryptionParameters(Guchar **fileKeyA, CryptAlgorithm *encAlgorithmA,
- int *keyLengthA) {
- if (encrypted) {
- *fileKeyA = fileKey;
- *encAlgorithmA = encAlgorithm;
- *keyLengthA = keyLength;
- } else {
- // null encryption parameters
- *fileKeyA = nullptr;
- *encAlgorithmA = cryptRC4;
- *keyLengthA = 0;
- }
-}
-
-GBool XRef::okToPrint(GBool ignoreOwnerPW) const {
- return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
-}
-
-// we can print at high res if we are only doing security handler revision
-// 2 (and we are allowed to print at all), or with security handler rev
-// 3 and we are allowed to print, and bit 12 is set.
-GBool XRef::okToPrintHighRes(GBool ignoreOwnerPW) const {
- if (encrypted) {
- if (2 == encRevision) {
- return (okToPrint(ignoreOwnerPW));
- } else if (encRevision >= 3) {
- return (okToPrint(ignoreOwnerPW) && (permFlags & permHighResPrint));
- } else {
- // something weird - unknown security handler version
- return gFalse;
- }
- } else {
- return gTrue;
- }
-}
-
-GBool XRef::okToChange(GBool ignoreOwnerPW) const {
- return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
-}
-
-GBool XRef::okToCopy(GBool ignoreOwnerPW) const {
- return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
-}
-
-GBool XRef::okToAddNotes(GBool ignoreOwnerPW) const {
- return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
-}
-
-GBool XRef::okToFillForm(GBool ignoreOwnerPW) const {
- return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permFillForm);
-}
-
-GBool XRef::okToAccessibility(GBool ignoreOwnerPW) const {
- return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permAccessibility);
-}
-
-GBool XRef::okToAssemble(GBool ignoreOwnerPW) const {
- return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permAssemble);
-}
-
-Object XRef::getCatalog() {
- Object catalog = fetch(rootNum, rootGen);
- if (catalog.isDict()) {
- return catalog;
- }
- GBool wasReconstructed = false;
- if (constructXRef(&wasReconstructed, gTrue)) {
- catalog = fetch(rootNum, rootGen);
- }
- return catalog;
-}
-
-Object XRef::fetch(int num, int gen, int recursion) {
- XRefEntry *e;
- Parser *parser;
- Object obj1, obj2, obj3;
-
- xrefLocker();
- // check for bogus ref - this can happen in corrupted PDF files
- if (num < 0 || num >= size) {
- goto err;
- }
-
- e = getEntry(num);
- if(!e->obj.isNull ()) { //check for updated object
- return e->obj.copy();
- }
-
- switch (e->type) {
-
- case xrefEntryUncompressed:
- {
- if (e->gen != gen || e->offset < 0) {
- goto err;
- }
- parser = new Parser(this,
- new Lexer(this,
- str->makeSubStream(start + e->offset, gFalse, 0, Object(objNull))),
- gTrue);
- obj1 = parser->getObj(recursion);
- obj2 = parser->getObj(recursion);
- obj3 = parser->getObj(recursion);
- if (!obj1.isInt() || obj1.getInt() != num ||
- !obj2.isInt() || obj2.getInt() != gen ||
- !obj3.isCmd("obj")) {
- // some buggy pdf have obj1234 for ints that represent 1234
- // try to recover here
- if (obj1.isInt() && obj1.getInt() == num &&
- obj2.isInt() && obj2.getInt() == gen &&
- obj3.isCmd()) {
- char *cmd = obj3.getCmd();
- if (strlen(cmd) > 3 &&
- cmd[0] == 'o' &&
- cmd[1] == 'b' &&
- cmd[2] == 'j') {
- char *end_ptr;
- long longNumber = strtol(cmd + 3, &end_ptr, 0);
- if (longNumber <= INT_MAX && longNumber >= INT_MIN && *end_ptr == '\0') {
- int number = longNumber;
- error(errSyntaxWarning, -1, "Cmd was not obj but {0:s}, assuming the creator meant obj {1:d}", cmd, number);
- delete parser;
- return Object(number);
- }
- }
- }
- delete parser;
- goto err;
- }
- Object obj = parser->getObj(gFalse, (encrypted && !e->getFlag(XRefEntry::Unencrypted)) ? fileKey : nullptr,
- encAlgorithm, keyLength, num, gen, recursion);
- delete parser;
- return obj;
- }
-
- case xrefEntryCompressed:
- {
-#if 0 // Adobe apparently ignores the generation number on compressed objects
- if (gen != 0) {
- goto err;
- }
-#endif
- if (e->offset >= (Guint)size ||
- entries[e->offset].type != xrefEntryUncompressed) {
- error(errSyntaxError, -1, "Invalid object stream");
- goto err;
- }
-
- ObjectStream *objStr = nullptr;
- ObjectStreamKey key(e->offset);
- PopplerCacheItem *item = objStrs->lookup(key);
- if (item) {
- ObjectStreamItem *it = static_cast<ObjectStreamItem *>(item);
- objStr = it->objStream;
- }
-
- if (!objStr) {
- objStr = new ObjectStream(this, e->offset, recursion + 1);
- if (!objStr->isOk()) {
- delete objStr;
- objStr = nullptr;
- goto err;
- } else {
- // XRef could be reconstructed in constructor of ObjectStream:
- e = getEntry(num);
- ObjectStreamKey *newkey = new ObjectStreamKey(e->offset);
- ObjectStreamItem *newitem = new ObjectStreamItem(objStr);
- objStrs->put(newkey, newitem);
- }
- }
- return objStr->getObject(e->gen, num);
- }
-
- default:
- goto err;
- }
-
- err:
- if (!xRefStream && !xrefReconstructed) {
- error(errInternal, -1, "xref num {0:d} not found but needed, try to reconstruct\n", num);
- rootNum = -1;
- constructXRef(&xrefReconstructed);
- return fetch(num, gen, ++recursion);
- }
- return Object(objNull);
-}
-
-void XRef::lock() {
-#ifdef MULTITHREADED
- gLockMutex(&mutex);
-#endif
-}
-
-void XRef::unlock() {
-#ifdef MULTITHREADED
- gUnlockMutex(&mutex);
-#endif
-}
-
-Object XRef::getDocInfo() {
- return trailerDict.dictLookup("Info");
-}
-
-// Added for the pdftex project.
-Object XRef::getDocInfoNF() {
- return trailerDict.dictLookupNF("Info");
-}
-
-Object XRef::createDocInfoIfNoneExists() {
- Object obj = getDocInfo();
-
- if (obj.isDict()) {
- return obj;
- } else if (!obj.isNull()) {
- // DocInfo exists, but isn't a dictionary (doesn't comply with the PDF reference)
- removeDocInfo();
- }
-
- obj = Object(new Dict(this));
- const Ref ref = addIndirectObject(&obj);
- trailerDict.dictSet("Info", Object(ref.num, ref.gen));
-
- return obj;
-}
-
-void XRef::removeDocInfo() {
- Object infoObjRef = getDocInfoNF();
- if (infoObjRef.isNull()) {
- return;
- }
-
- trailerDict.dictRemove("Info");
-
- removeIndirectObject(infoObjRef.getRef());
-}
-
-GBool XRef::getStreamEnd(Goffset streamStart, Goffset *streamEnd) {
- int a, b, m;
-
- if (streamEndsLen == 0 ||
- streamStart > streamEnds[streamEndsLen - 1]) {
- return gFalse;
- }
-
- a = -1;
- b = streamEndsLen - 1;
- // invariant: streamEnds[a] < streamStart <= streamEnds[b]
- while (b - a > 1) {
- m = (a + b) / 2;
- if (streamStart <= streamEnds[m]) {
- b = m;
- } else {
- a = m;
- }
- }
- *streamEnd = streamEnds[b];
- return gTrue;
-}
-
-int XRef::getNumEntry(Goffset offset)
-{
- if (size > 0)
- {
- int res = 0;
- Goffset resOffset = getEntry(0)->offset;
- XRefEntry *e;
- for (int i = 1; i < size; ++i)
- {
- e = getEntry(i, gFalse);
- if (e->type != xrefEntryFree && e->offset < offset && e->offset >= resOffset)
- {
- res = i;
- resOffset = e->offset;
- }
- }
- return res;
- }
- else return -1;
-}
-
-void XRef::add(int num, int gen, Goffset offs, GBool used) {
- xrefLocker();
- if (num >= size) {
- if (num >= capacity) {
- entries = (XRefEntry *)greallocn(entries, num + 1, sizeof(XRefEntry));
- capacity = num + 1;
- }
- for (int i = size; i < num + 1; ++i) {
- entries[i].offset = -1;
- entries[i].type = xrefEntryFree;
- entries[i].obj.initNullAfterMalloc();
- entries[i].flags = 0;
- entries[i].gen = 0;
- }
- size = num + 1;
- }
- XRefEntry *e = getEntry(num);
- e->gen = gen;
- e->obj.setToNull();
- e->flags = 0;
- if (used) {
- e->type = xrefEntryUncompressed;
- e->offset = offs;
- } else {
- e->type = xrefEntryFree;
- e->offset = 0;
- }
-}
-
-void XRef::setModifiedObject (const Object* o, Ref r) {
- xrefLocker();
- if (r.num < 0 || r.num >= size) {
- error(errInternal, -1,"XRef::setModifiedObject on unknown ref: {0:d}, {1:d}\n", r.num, r.gen);
- return;
- }
- XRefEntry *e = getEntry(r.num);
- e->obj = o->copy();
- e->setFlag(XRefEntry::Updated, gTrue);
- setModified();
-}
-
-Ref XRef::addIndirectObject (const Object *o) {
- int entryIndexToUse = -1;
- for (int i = 1; entryIndexToUse == -1 && i < size; ++i) {
- XRefEntry *e = getEntry(i, false /* complainIfMissing */);
- if (e->type == xrefEntryFree && e->gen != 65535) {
- entryIndexToUse = i;
- }
- }
-
- XRefEntry *e;
- if (entryIndexToUse == -1) {
- entryIndexToUse = size;
- add(entryIndexToUse, 0, 0, gFalse);
- e = getEntry(entryIndexToUse);
- } else {
- //reuse a free entry
- e = getEntry(entryIndexToUse);
- //we don't touch gen number, because it should have been
- //incremented when the object was deleted
- }
- e->type = xrefEntryUncompressed;
- e->obj = o->copy();
- e->setFlag(XRefEntry::Updated, gTrue);
- setModified();
-
- Ref r;
- r.num = entryIndexToUse;
- r.gen = e->gen;
- return r;
-}
-
-void XRef::removeIndirectObject(Ref r) {
- xrefLocker();
- if (r.num < 0 || r.num >= size) {
- error(errInternal, -1,"XRef::removeIndirectObject on unknown ref: {0:d}, {1:d}\n", r.num, r.gen);
- return;
- }
- XRefEntry *e = getEntry(r.num);
- if (e->type == xrefEntryFree) {
- return;
- }
- e->obj.free();
- e->type = xrefEntryFree;
- e->gen++;
- e->setFlag(XRefEntry::Updated, gTrue);
- setModified();
-}
-
-void XRef::writeXRef(XRef::XRefWriter *writer, GBool writeAllEntries) {
- //create free entries linked-list
- if (getEntry(0)->gen != 65535) {
- error(errInternal, -1, "XRef::writeXRef, entry 0 of the XRef is invalid (gen != 65535)\n");
- }
- int lastFreeEntry = 0;
- for (int i=0; i<size; i++) {
- if (getEntry(i)->type == xrefEntryFree) {
- getEntry(lastFreeEntry)->offset = i;
- lastFreeEntry = i;
- }
- }
- getEntry(lastFreeEntry)->offset = 0;
-
- if (writeAllEntries) {
- writer->startSection(0, size);
- for (int i=0; i<size; i++) {
- XRefEntry *e = getEntry(i);
- if(e->gen > 65535) e->gen = 65535; //cap generation number to 65535 (required by PDFReference)
- writer->writeEntry(e->offset, e->gen, e->type);
- }
- } else {
- int i = 0;
- while (i < size) {
- int j;
- for(j=i; j<size; j++) { //look for consecutive entries
- if ((getEntry(j)->type == xrefEntryFree) && (getEntry(j)->gen == 0))
- break;
- }
- if (j-i != 0)
- {
- writer->startSection(i, j-i);
- for (int k=i; k<j; k++) {
- XRefEntry *e = getEntry(k);
- if(e->gen > 65535) e->gen = 65535; //cap generation number to 65535 (required by PDFReference)
- writer->writeEntry(e->offset, e->gen, e->type);
- }
- i = j;
- }
- else ++i;
- }
- }
-}
-
-XRef::XRefTableWriter::XRefTableWriter(OutStream* outStrA) {
- outStr = outStrA;
-}
-
-void XRef::XRefTableWriter::startSection(int first, int count) {
- outStr->printf("%i %i\r\n", first, count);
-}
-
-void XRef::XRefTableWriter::writeEntry(Goffset offset, int gen, XRefEntryType type) {
- outStr->printf("%010lli %05i %c\r\n", (long long)offset, gen, (type==xrefEntryFree)?'f':'n');
-}
-
-void XRef::writeTableToFile(OutStream* outStr, GBool writeAllEntries) {
- XRefTableWriter writer(outStr);
- outStr->printf("xref\r\n");
- writeXRef(&writer, writeAllEntries);
-}
-
-XRef::XRefStreamWriter::XRefStreamWriter(Array *indexA, GooString *stmBufA, int offsetSizeA) {
- index = indexA;
- stmBuf = stmBufA;
- offsetSize = offsetSizeA;
-}
-
-void XRef::XRefStreamWriter::startSection(int first, int count) {
- index->add( Object(first) );
- index->add( Object(count) );
-}
-
-void XRef::XRefStreamWriter::writeEntry(Goffset offset, int gen, XRefEntryType type) {
- const int entryTotalSize = 1 + offsetSize + 2; /* type + offset + gen */
- char data[16];
- data[0] = (type==xrefEntryFree) ? 0 : 1;
- for (int i = offsetSize; i > 0; i--) {
- data[i] = offset & 0xff;
- offset >>= 8;
- }
- data[offsetSize + 1] = (gen >> 8) & 0xff;
- data[offsetSize + 2] = gen & 0xff;
- stmBuf->append(data, entryTotalSize);
-}
-
-XRef::XRefPreScanWriter::XRefPreScanWriter() {
- hasOffsetsBeyond4GB = gFalse;
-}
-
-void XRef::XRefPreScanWriter::startSection(int first, int count) {
-}
-
-void XRef::XRefPreScanWriter::writeEntry(Goffset offset, int gen, XRefEntryType type) {
- if (offset >= 0x100000000ll)
- hasOffsetsBeyond4GB = gTrue;
-}
-
-void XRef::writeStreamToBuffer(GooString *stmBuf, Dict *xrefDict, XRef *xref) {
- Array *index = new Array(xref);
- stmBuf->clear();
-
- // First pass: determine whether all offsets fit in 4 bytes or not
- XRefPreScanWriter prescan;
- writeXRef(&prescan, gFalse);
- const int offsetSize = prescan.hasOffsetsBeyond4GB ? sizeof(Goffset) : 4;
-
- // Second pass: actually write the xref stream
- XRefStreamWriter writer(index, stmBuf, offsetSize);
- writeXRef(&writer, gFalse);
-
- xrefDict->set("Type", Object(objName, "XRef"));
- xrefDict->set("Index", Object(index));
- Array *wArray = new Array(xref);
- wArray->add( Object(1) );
- wArray->add( Object(offsetSize) );
- wArray->add( Object(2) );
- xrefDict->set("W", Object(wArray));
-}
-
-GBool XRef::parseEntry(Goffset offset, XRefEntry *entry)
-{
- GBool r;
-
- if (unlikely(entry == nullptr))
- return gFalse;
-
- Parser parser(nullptr, new Lexer(nullptr,
- str->makeSubStream(offset, gFalse, 20, Object(objNull))), gTrue);
-
- Object obj1, obj2, obj3;
- if (((obj1 = parser.getObj(), obj1.isInt()) || obj1.isInt64()) &&
- (obj2 = parser.getObj(), obj2.isInt()) &&
- (obj3 = parser.getObj(), obj3.isCmd("n") || obj3.isCmd("f"))) {
- if (obj1.isInt64())
- entry->offset = obj1.getInt64();
- else
- entry->offset = obj1.getInt();
- entry->gen = obj2.getInt();
- entry->type = obj3.isCmd("n") ? xrefEntryUncompressed : xrefEntryFree;
- entry->obj.setToNull();
- entry->flags = 0;
- r = gTrue;
- } else {
- r = gFalse;
- }
-
- return r;
-}
-
-/* Traverse all XRef tables and, if untilEntryNum != -1, stop as soon as
- * untilEntryNum is found, or try to reconstruct the xref table if it's not
- * present in any xref.
- * If xrefStreamObjsNum is not NULL, it is filled with the list of the object
- * numbers of the XRef streams that have been traversed */
-void XRef::readXRefUntil(int untilEntryNum, std::vector<int> *xrefStreamObjsNum)
-{
- std::vector<Goffset> followedPrev;
- while (prevXRefOffset && (untilEntryNum == -1 || (untilEntryNum < size && entries[untilEntryNum].type == xrefEntryNone))) {
- bool followed = false;
- for (size_t j = 0; j < followedPrev.size(); j++) {
- if (followedPrev.at(j) == prevXRefOffset) {
- followed = true;
- break;
- }
- }
- if (followed) {
- error(errSyntaxError, -1, "Circular XRef");
- if (!(ok = constructXRef(nullptr))) {
- errCode = errDamaged;
- }
- break;
- }
-
- followedPrev.push_back (prevXRefOffset);
-
- std::vector<Goffset> followedXRefStm;
- if (!readXRef(&prevXRefOffset, &followedXRefStm, xrefStreamObjsNum)) {
- prevXRefOffset = 0;
- }
-
- // if there was a problem with the xref table, or we haven't found the entry
- // we were looking for, try to reconstruct the xref
- if (!ok || (!prevXRefOffset && untilEntryNum != -1 && entries[untilEntryNum].type == xrefEntryNone)) {
- GBool wasReconstructed = false;
- if (!(ok = constructXRef(&wasReconstructed))) {
- errCode = errDamaged;
- break;
- }
- break;
- }
- }
-}
-
-XRefEntry *XRef::getEntry(int i, GBool complainIfMissing)
-{
- if (i >= size || entries[i].type == xrefEntryNone) {
-
- if ((!xRefStream) && mainXRefEntriesOffset) {
- if (!parseEntry(mainXRefEntriesOffset + 20*i, &entries[i])) {
- error(errSyntaxError, -1, "Failed to parse XRef entry [{0:d}].", i);
- }
- } else {
- // Read XRef tables until the entry we're looking for is found
- readXRefUntil(i);
-
- // We might have reconstructed the xref
- // Check again i is in bounds
- if (unlikely(i >= size)) {
- static XRefEntry dummy;
- dummy.offset = 0;
- dummy.gen = -1;
- dummy.type = xrefEntryNone;
- dummy.flags = 0;
- return &dummy;
- }
-
- if (entries[i].type == xrefEntryNone) {
- if (complainIfMissing) {
- error(errSyntaxError, -1, "Invalid XRef entry");
- }
- entries[i].type = xrefEntryFree;
- }
- }
- }
-
- return &entries[i];
-}
-
-// Recursively sets the Unencrypted flag in all referenced xref entries
-void XRef::markUnencrypted(Object *obj) {
- Object obj1;
-
- switch (obj->getType()) {
- case objArray:
- {
- Array *array = obj->getArray();
- for (int i = 0; i < array->getLength(); i++) {
- obj1 = array->getNF(i);
- markUnencrypted(&obj1);
- }
- break;
- }
- case objStream:
- case objDict:
- {
- Dict *dict;
- if (obj->getType() == objStream) {
- Stream *stream = obj->getStream();
- dict = stream->getDict();
- } else {
- dict = obj->getDict();
- }
- for (int i = 0; i < dict->getLength(); i++) {
- obj1 = dict->getValNF(i);
- markUnencrypted(&obj1);
- }
- break;
- }
- case objRef:
- {
- Ref ref = obj->getRef();
- XRefEntry *e = getEntry(ref.num);
- if (e->getFlag(XRefEntry::Unencrypted))
- return; // We've already been here: prevent infinite recursion
- e->setFlag(XRefEntry::Unencrypted, gTrue);
- obj1 = fetch(ref.num, ref.gen);
- markUnencrypted(&obj1);
- break;
- }
- default:
- break;
- }
-}
-
-void XRef::scanSpecialFlags() {
- if (scannedSpecialFlags) {
- return;
- }
- scannedSpecialFlags = gTrue;
-
- // "Rewind" the XRef linked list, so that readXRefUntil re-reads all XRef
- // tables/streams, even those that had already been parsed
- prevXRefOffset = mainXRefOffset;
-
- std::vector<int> xrefStreamObjNums;
- if (!streamEndsLen) { // don't do it for already reconstructed xref
- readXRefUntil(-1 /* read all xref sections */, &xrefStreamObjNums);
- }
-
- // Mark object streams as DontRewrite, because we write each object
- // individually in full rewrite mode.
- for (int i = 0; i < size; ++i) {
- if (entries[i].type == xrefEntryCompressed) {
- const int objStmNum = entries[i].offset;
- if (unlikely(objStmNum < 0 || objStmNum >= size)) {
- error(errSyntaxError, -1, "Compressed object offset out of xref bounds");
- } else {
- getEntry(objStmNum)->setFlag(XRefEntry::DontRewrite, gTrue);
- }
- }
- }
-
- // Mark XRef streams objects as Unencrypted and DontRewrite
- for (size_t i = 0; i < xrefStreamObjNums.size(); ++i) {
- const int objNum = xrefStreamObjNums.at(i);
- getEntry(objNum)->setFlag(XRefEntry::Unencrypted, gTrue);
- getEntry(objNum)->setFlag(XRefEntry::DontRewrite, gTrue);
- }
-
- // Mark objects referred from the Encrypt dict as Unencrypted
- markUnencrypted();
-}
-
-void XRef::markUnencrypted() {
- // Mark objects referred from the Encrypt dict as Unencrypted
- Object obj = trailerDict.dictLookupNF("Encrypt");
- if (obj.isRef()) {
- XRefEntry *e = getEntry(obj.getRefNum());
- e->setFlag(XRefEntry::Unencrypted, gTrue);
- }
-}
-