diff options
author | Tobias Hoffmann <thobi@worker> | 2012-06-19 01:53:10 +0200 |
---|---|---|
committer | Jay Berkenbilt <ejb@ql.org> | 2012-06-21 21:01:02 +0200 |
commit | 5d3f93be29800dd0eb876c3062451d32e7798948 (patch) | |
tree | 76b6d3767d20edb4e516254131549d28c6d658c5 | |
parent | 405a549f8c86769d14d6a350621f4642dd30920f (diff) | |
download | qpdf-5d3f93be29800dd0eb876c3062451d32e7798948.tar.zst |
Added first version of pages API.
-rw-r--r-- | include/qpdf/QPDF.hh | 25 | ||||
-rw-r--r-- | libqpdf/QPDF.cc | 145 |
2 files changed, 170 insertions, 0 deletions
diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index 048bad47..8d8f4a3e 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -344,9 +344,24 @@ class QPDF QPDF_DLL std::vector<QPDFObjectHandle> const& getAllPages(); + // QPDF internally caches the /Pages tree. This method will clear + // the cache when e.g. direct modifications have been made. QPDF_DLL void clearPagesCache(); + // Add new page at the beginning or the end of the current pdf + QPDF_DLL + void addPage(QPDFObjectHandle newpage, bool first); + + // Add new page before or after refpage + QPDF_DLL + void addPageAt(QPDFObjectHandle newpage, bool before, + QPDFObjectHandle const& refpage); + + // Remove pageoh from the pdf. + QPDF_DLL + void removePage(QPDFObjectHandle const& pageoh); + // Resolver class is restricted to QPDFObjectHandle so that only // it can resolve indirect references. class Resolver @@ -521,8 +536,17 @@ class QPDF off_t offset, size_t length, QPDFObjectHandle dict, Pipeline* pipeline); + + // methods to support page handling + void getAllPagesInternal(QPDFObjectHandle cur_pages, std::vector<QPDFObjectHandle>& result); + // creates pageobj_to_pages_pos if necessary + // returns position, or -1 if not found + int findPage(int objid, int generation); + int findPage(QPDFObjectHandle const& pageoh); // convenience + + void flattenPagesTree(); // methods to support encryption -- implemented in QPDF_encryption.cc encryption_method_e interpretCF(QPDFObjectHandle); @@ -887,6 +911,7 @@ class QPDF std::map<ObjGen, ObjCache> obj_cache; QPDFObjectHandle trailer; std::vector<QPDFObjectHandle> all_pages; + std::map<ObjGen, int> pageobj_to_pages_pos; std::vector<QPDFExc> warnings; // Linearization data diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 64c454a2..d36aebe2 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -4,6 +4,7 @@ #include <map> #include <string.h> #include <memory.h> +#include <assert.h> #include <qpdf/QTC.hh> #include <qpdf/QUtil.hh> @@ -2203,8 +2204,152 @@ QPDF::getAllPagesInternal(QPDFObjectHandle cur_pages, } } +// FIXXX here down + void QPDF::clearPagesCache() { this->all_pages.clear(); + this->pageobj_to_pages_pos.clear(); +} + +void +QPDF::flattenPagesTree() +{ + clearPagesCache(); + + // FIXME: more specific method, we don't want to generate the extra stuff. + // We also need cheap fixup after addPage/removePage. + + // no compressed objects to be produced here... + std::map<int, int> object_stream_data; + optimize(object_stream_data); // push down inheritance + + std::vector<QPDFObjectHandle> kids = this->getAllPages(); + QPDFObjectHandle pages = this->trailer.getKey("/Root").getKey("/Pages"); + + const int len = kids.size(); + for (int pos = 0; pos < len; ++pos) + { + // populate pageobj_to_pages_pos + ObjGen og(kids[pos].getObjectID(), kids[pos].getGeneration()); + if (! this->pageobj_to_pages_pos.insert(std::make_pair(og, pos)).second) + { + // insert failed: duplicate entry found + *out_stream << "WARNING: duplicate page reference found, " + << "but currently not fully supported." << std::endl; + } + + // fix parent links + kids[pos].replaceKey("/Parent", pages); + } + + pages.replaceKey("/Kids", QPDFObjectHandle::newArray(kids)); + // /Count has not changed + assert(pages.getKey("/Count").getIntValue() == len); +} + +int +QPDF::findPage(int objid, int generation) +{ + if (this->pageobj_to_pages_pos.empty()) + { + flattenPagesTree(); + } + std::map<ObjGen, int>::iterator it = + this->pageobj_to_pages_pos.find(ObjGen(objid, generation)); + if (it != this->pageobj_to_pages_pos.end()) + { + return (*it).second; + } + return -1; // throw? +} + +int +QPDF::findPage(QPDFObjectHandle const& pageoh) +{ + if (!pageoh.isInitialized()) + { + return -1; + // TODO? throw + } + return findPage(pageoh.getObjectID(), pageoh.getGeneration()); +} + +void +QPDF::addPage(QPDFObjectHandle newpage, bool first) +{ + if (this->pageobj_to_pages_pos.empty()) + { + flattenPagesTree(); + } + + newpage.assertPageObject(); // FIXME: currently private + + QPDFObjectHandle pages = this->trailer.getKey("/Root").getKey("/Pages"); + QPDFObjectHandle kids = pages.getKey("/Kids"); + + newpage.replaceKey("/Parent", pages); + if (first) + { + kids.insertItem(0, newpage); + } + else + { + kids.appendItem(newpage); + } + pages.replaceKey("/Count", + QPDFObjectHandle::newInteger(kids.getArrayNItems())); + + // FIXME: this is overkill, but cache is now stale + clearPagesCache(); +} + +void +QPDF::addPageAt(QPDFObjectHandle newpage, bool before, + QPDFObjectHandle const &refpage) +{ + int refpos = findPage(refpage); // also ensures flat /Pages + if (refpos == -1) + { + throw "Could not find refpage"; + } + + newpage.assertPageObject(); + + QPDFObjectHandle pages = this->trailer.getKey("/Root").getKey("/Pages"); + QPDFObjectHandle kids = pages.getKey("/Kids"); + + if (! before) + { + ++refpos; + } + + newpage.replaceKey("/Parent", pages); + kids.insertItem(refpos, newpage); + pages.replaceKey("/Count", + QPDFObjectHandle::newInteger(kids.getArrayNItems())); + + // FIXME: this is overkill, but cache is now stale + clearPagesCache(); +} + +void +QPDF::removePage(QPDFObjectHandle const& pageoh) +{ + int pos = findPage(pageoh); // also ensures flat /Pages + if (pos == -1) + { + throw "Can't remove non-existing page"; + } + + QPDFObjectHandle pages = this->trailer.getKey("/Root").getKey("/Pages"); + QPDFObjectHandle kids = pages.getKey("/Kids"); + + kids.eraseItem(pos); + pages.replaceKey("/Count", + QPDFObjectHandle::newInteger(kids.getArrayNItems())); + + // FIXME: this is overkill, but cache is now stale + clearPagesCache(); } |