//======================================================================== // // XRef.h // // 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 Brad Hards // Copyright (C) 2006, 2008, 2010-2013, 2017-2020 Albert Astals Cid // Copyright (C) 2007-2008 Julien Rebetez // Copyright (C) 2007 Carlos Garcia Campos // Copyright (C) 2010 Ilya Gorenbein // Copyright (C) 2010 Hib Eris // Copyright (C) 2012, 2013, 2016 Thomas Freitag // Copyright (C) 2012, 2013 Fabio D'Urso // Copyright (C) 2013, 2017 Adrian Johnson // Copyright (C) 2016 Jakub Alba // Copyright (C) 2018 Adam Reichold // Copyright (C) 2018 Marek Kasik // // 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 // //======================================================================== #ifndef XREF_H #define XREF_H #include "poppler-config.h" #include "Object.h" #include "Stream.h" #include "PopplerCache.h" class Dict; class Stream; class Parser; class ObjectStream; //------------------------------------------------------------------------ // XRef //------------------------------------------------------------------------ enum XRefEntryType { xrefEntryFree, xrefEntryUncompressed, xrefEntryCompressed, xrefEntryNone }; struct XRefEntry { Goffset offset; int gen; XRefEntryType type; int flags; Object obj; // if this entry was updated, obj will contains the updated object enum Flag { // Regular flags Updated, // Entry was modified Parsing, // Entry is currently being parsed // Special flags -- available only after xref->scanSpecialFlags() is run Unencrypted, // Entry is stored in unencrypted form (meaningless in unencrypted documents) DontRewrite // Entry must not be written back in case of full rewrite }; inline bool getFlag(Flag flag) const { const int mask = (1 << (int)flag); return (flags & mask) != 0; } inline void setFlag(Flag flag, bool value) { const int mask = (1 << (int)flag); if (value) { flags |= mask; } else { flags &= ~mask; } } }; class XRef { public: // Constructor, create an empty XRef, used for PDF writing XRef(); // Constructor, create an empty XRef but with info dict, used for PDF writing XRef(const Object *trailerDictA); // Constructor. Read xref table from stream. XRef(BaseStream *strA, Goffset pos, Goffset mainXRefEntriesOffsetA = 0, bool *wasReconstructed = nullptr, bool reconstruct = false); // Destructor. ~XRef(); XRef(const XRef &) = delete; XRef &operator=(const XRef &) = delete; // Copy xref but with new base stream! XRef *copy() const; // Is xref table valid? bool isOk() const { return ok; } // Is the last XRef section a stream or a table? bool isXRefStream() const { return xRefStream; } // Get the error code (if isOk() returns false). int getErrorCode() const { return errCode; } // Set the encryption parameters. void setEncryption(int permFlagsA, bool ownerPasswordOkA, const unsigned char *fileKeyA, int keyLengthA, int encVersionA, int encRevisionA, CryptAlgorithm encAlgorithmA); // Mark Encrypt entry as Unencrypted void markUnencrypted(); void getEncryptionParameters(unsigned char **fileKeyA, CryptAlgorithm *encAlgorithmA, int *keyLengthA); // Is the file encrypted? bool isEncrypted() const { return encrypted; } // Is the given Ref encrypted? bool isRefEncrypted(Ref r); // Check various permissions. bool okToPrint(bool ignoreOwnerPW = false) const; bool okToPrintHighRes(bool ignoreOwnerPW = false) const; bool okToChange(bool ignoreOwnerPW = false) const; bool okToCopy(bool ignoreOwnerPW = false) const; bool okToAddNotes(bool ignoreOwnerPW = false) const; bool okToFillForm(bool ignoreOwnerPW = false) const; bool okToAccessibility(bool ignoreOwnerPW = false) const; bool okToAssemble(bool ignoreOwnerPW = false) const; int getPermFlags() const { return permFlags; } // Get catalog object. Object getCatalog(); // Fetch an indirect reference. Object fetch(const Ref ref, int recursion = 0); Object fetch(int num, int gen, int recursion = 0); // Return the document's Info dictionary (if any). Object getDocInfo(); Object getDocInfoNF(); // Create and return the document's Info dictionary if needed. // Otherwise return the existing one. // Returns in the given parameter the Ref the Info is in Object createDocInfoIfNeeded(Ref *ref); // Remove the document's Info dictionary and update the trailer dictionary. void removeDocInfo(); // Return the number of objects in the xref table. int getNumObjects() const { return size; } // Return the catalog object reference. int getRootNum() const { return rootNum; } int getRootGen() const { return rootGen; } Ref getRoot() const { return { rootNum, rootGen }; } // Get end position for a stream in a damaged file. // Returns false if unknown or file is not damaged. bool getStreamEnd(Goffset streamStart, Goffset *streamEnd); // Retuns the entry that belongs to the offset int getNumEntry(Goffset offset); // Scans the document and sets special flags in all xref entries. One of those // flags is Unencrypted, which affects how the object is fetched. Therefore, // this function must be called before fetching unencrypted objects (e.g. // Encrypt dictionary, XRef streams). Note that the code that initializes // decryption doesn't need to call this function, because it runs before // decryption is enabled, and therefore the Unencrypted flag is ignored. void scanSpecialFlags(); // Direct access. XRefEntry *getEntry(int i, bool complainIfMissing = true); Object *getTrailerDict() { return &trailerDict; } // Was the XRef modified? bool isModified() const { return modified; } // Set the modification flag for XRef to true. void setModified() { modified = true; } // Write access void setModifiedObject(const Object *o, Ref r); Ref addIndirectObject(const Object *o); void removeIndirectObject(Ref r); void add(int num, int gen, Goffset offs, bool used); void add(Ref ref, Goffset offs, bool used); // Output XRef table to stream void writeTableToFile(OutStream *outStr, bool writeAllEntries); // Output XRef stream contents to GooString and fill trailerDict fields accordingly void writeStreamToBuffer(GooString *stmBuf, Dict *xrefDict, XRef *xref); // to be thread safe during write where changes are not allowed void lock(); void unlock(); private: BaseStream *str; // input stream Goffset start; // offset in file (to allow for garbage // at beginning of file) XRefEntry *entries; // xref entries int capacity; // size of array int size; // number of entries int rootNum, rootGen; // catalog dict bool ok; // true if xref table is valid int errCode; // error code (if is false) bool xrefReconstructed; // marker, true if xref was already reconstructed Object trailerDict; // trailer dictionary bool modified; Goffset *streamEnds; // 'endstream' positions - only used in // damaged files int streamEndsLen; // number of valid entries in streamEnds PopplerCache objStrs; // cached object streams bool encrypted; // true if file is encrypted int encRevision; int encVersion; // encryption algorithm CryptAlgorithm encAlgorithm; // encryption algorithm int keyLength; // length of key, in bytes int permFlags; // permission bits unsigned char fileKey[32]; // file decryption key bool ownerPasswordOk; // true if owner password is correct Goffset prevXRefOffset; // position of prev XRef section (= next to read) Goffset mainXRefEntriesOffset; // offset of entries in main XRef table bool xRefStream; // true if last XRef section is a stream Goffset mainXRefOffset; // position of the main XRef table/stream bool scannedSpecialFlags; // true if scanSpecialFlags has been called bool strOwner; // true if str is owned by the instance mutable std::recursive_mutex mutex; int reserve(int newSize); int resize(int newSize); bool readXRef(Goffset *pos, std::vector *followedXRefStm, std::vector *xrefStreamObjsNum); bool readXRefTable(Parser *parser, Goffset *pos, std::vector *followedXRefStm, std::vector *xrefStreamObjsNum); bool readXRefStreamSection(Stream *xrefStr, const int *w, int first, int n); bool readXRefStream(Stream *xrefStr, Goffset *pos); bool constructXRef(bool *wasReconstructed, bool needCatalogDict = false); bool parseEntry(Goffset offset, XRefEntry *entry); void readXRefUntil(int untilEntryNum, std::vector *xrefStreamObjsNum = nullptr); void markUnencrypted(Object *obj); class XRefWriter { public: XRefWriter() = default; virtual void startSection(int first, int count) = 0; virtual void writeEntry(Goffset offset, int gen, XRefEntryType type) = 0; virtual ~XRefWriter(); XRefWriter(const XRefWriter &) = delete; XRefWriter &operator=(const XRefWriter &other) = delete; }; // XRefWriter subclass that writes a XRef table class XRefTableWriter : public XRefWriter { public: XRefTableWriter(OutStream *outStrA); void startSection(int first, int count) override; void writeEntry(Goffset offset, int gen, XRefEntryType type) override; private: OutStream *outStr; }; // XRefWriter subclass that writes a XRef stream class XRefStreamWriter : public XRefWriter { public: XRefStreamWriter(Array *index, GooString *stmBuf, int offsetSize); void startSection(int first, int count) override; void writeEntry(Goffset offset, int gen, XRefEntryType type) override; private: Array *index; GooString *stmBuf; int offsetSize; }; // Dummy XRefWriter subclass that only checks if all offsets fit in 4 bytes class XRefPreScanWriter : public XRefWriter { public: XRefPreScanWriter(); void startSection(int first, int count) override; void writeEntry(Goffset offset, int gen, XRefEntryType type) override; bool hasOffsetsBeyond4GB; }; void writeXRef(XRefWriter *writer, bool writeAllEntries); }; #endif