| //======================================================================== |
| // |
| // 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 <bradh@frogmouth.net> |
| // Copyright (C) 2006, 2008, 2010-2013, 2017-2022, 2024 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) 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, 2017, 2019 Adrian Johnson <ajohnson@redneon.com> |
| // Copyright (C) 2016 Jakub Alba <jakubalba@gmail.com> |
| // Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de> |
| // Copyright (C) 2018 Marek Kasik <mkasik@redhat.com> |
| // Copyright (C) 2021 Mahmoud Khalil <mahmoudkhalil11@gmail.com> |
| // Copyright (C) 2021 Georgiy Sgibnev <georgiy@sgibnev.com>. Work sponsored by lab50.net. |
| // Copyright (C) 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk> |
| // |
| // 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 <functional> |
| |
| #include "poppler-config.h" |
| #include "poppler_private_export.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; |
| } |
| } |
| }; |
| |
| // How to compress the a added stream |
| enum class StreamCompression |
| { |
| None, /* No compression */ |
| Compress, /* Compresses the stream */ |
| }; |
| |
| class POPPLER_PRIVATE_EXPORT 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 |
| explicit XRef(const Object *trailerDictA); |
| // Constructor. Read xref table from stream. |
| XRef(BaseStream *strA, Goffset pos, Goffset mainXRefEntriesOffsetA = 0, bool *wasReconstructed = nullptr, bool reconstruct = false, const std::function<void()> &xrefReconstructedCallback = {}); |
| |
| // 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); |
| // If endPos is not null, returns file position after parsing the object. This will |
| // be a few bytes after the end of the object due to the parser reading ahead. |
| // Returns -1 if object is in compressed stream. |
| Object fetch(int num, int gen, int recursion = 0, Goffset *endPos = nullptr); |
| |
| // 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); |
| bool add(int num, int gen, Goffset offs, bool used); |
| void add(Ref ref, Goffset offs, bool used); |
| // Adds a stream object using AutoFreeMemStream. |
| // The function takes ownership over dict and buffer. |
| // The buffer should be created using gmalloc(). |
| // For stream compression, if the data is already compressed |
| // don't compress again. If it is not compressed, use compress (Flate / zlib) |
| // Returns ref to a new object. |
| Ref addStreamObject(Dict *dict, char *buffer, const Goffset bufferSize, StreamCompression compression); |
| Ref addStreamObject(Dict *dict, uint8_t *buffer, const Goffset bufferSize, StreamCompression compression); |
| |
| // 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 <entries> array |
| int size; // number of entries |
| int rootNum, rootGen; // catalog dict |
| bool ok; // true if xref table is valid |
| int errCode; // error code (if <ok> 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<Goffset, ObjectStream> 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; |
| std::function<void()> xrefReconstructedCb; |
| |
| RefRecursionChecker refsBeingFetched; |
| |
| int reserve(int newSize); |
| int resize(int newSize); |
| bool readXRef(Goffset *pos, std::vector<Goffset> *followedXRefStm, std::vector<int> *xrefStreamObjsNum); |
| bool readXRefTable(Parser *parser, Goffset *pos, std::vector<Goffset> *followedXRefStm, std::vector<int> *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<int> *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: |
| explicit 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 |