Commit e01ae1968b79841797b2ae59eda00b867604e3f9
1 parent
df493c35
Split page handling APIs into a separate source file
Showing
3 changed files
with
196 additions
and
189 deletions
libqpdf/QPDF.cc
| @@ -4,7 +4,6 @@ | @@ -4,7 +4,6 @@ | ||
| 4 | #include <map> | 4 | #include <map> |
| 5 | #include <string.h> | 5 | #include <string.h> |
| 6 | #include <memory.h> | 6 | #include <memory.h> |
| 7 | -#include <assert.h> | ||
| 8 | 7 | ||
| 9 | #include <qpdf/QTC.hh> | 8 | #include <qpdf/QTC.hh> |
| 10 | #include <qpdf/QUtil.hh> | 9 | #include <qpdf/QUtil.hh> |
| @@ -2165,191 +2164,3 @@ QPDF::decodeStreams() | @@ -2165,191 +2164,3 @@ QPDF::decodeStreams() | ||
| 2165 | } | 2164 | } |
| 2166 | } | 2165 | } |
| 2167 | } | 2166 | } |
| 2168 | - | ||
| 2169 | -std::vector<QPDFObjectHandle> const& | ||
| 2170 | -QPDF::getAllPages() | ||
| 2171 | -{ | ||
| 2172 | - if (this->all_pages.empty()) | ||
| 2173 | - { | ||
| 2174 | - getAllPagesInternal( | ||
| 2175 | - this->trailer.getKey("/Root").getKey("/Pages"), this->all_pages); | ||
| 2176 | - } | ||
| 2177 | - return this->all_pages; | ||
| 2178 | -} | ||
| 2179 | - | ||
| 2180 | -void | ||
| 2181 | -QPDF::getAllPagesInternal(QPDFObjectHandle cur_pages, | ||
| 2182 | - std::vector<QPDFObjectHandle>& result) | ||
| 2183 | -{ | ||
| 2184 | - std::string type = cur_pages.getKey("/Type").getName(); | ||
| 2185 | - if (type == "/Pages") | ||
| 2186 | - { | ||
| 2187 | - QPDFObjectHandle kids = cur_pages.getKey("/Kids"); | ||
| 2188 | - int n = kids.getArrayNItems(); | ||
| 2189 | - for (int i = 0; i < n; ++i) | ||
| 2190 | - { | ||
| 2191 | - getAllPagesInternal(kids.getArrayItem(i), result); | ||
| 2192 | - } | ||
| 2193 | - } | ||
| 2194 | - else if (type == "/Page") | ||
| 2195 | - { | ||
| 2196 | - result.push_back(cur_pages); | ||
| 2197 | - } | ||
| 2198 | - else | ||
| 2199 | - { | ||
| 2200 | - throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), | ||
| 2201 | - this->last_object_description, | ||
| 2202 | - this->file->getLastOffset(), | ||
| 2203 | - ": invalid Type in page tree"); | ||
| 2204 | - } | ||
| 2205 | -} | ||
| 2206 | - | ||
| 2207 | -// FIXXX here down | ||
| 2208 | - | ||
| 2209 | -void | ||
| 2210 | -QPDF::clearPagesCache() | ||
| 2211 | -{ | ||
| 2212 | - this->all_pages.clear(); | ||
| 2213 | - this->pageobj_to_pages_pos.clear(); | ||
| 2214 | -} | ||
| 2215 | - | ||
| 2216 | -void | ||
| 2217 | -QPDF::flattenPagesTree() | ||
| 2218 | -{ | ||
| 2219 | - clearPagesCache(); | ||
| 2220 | - | ||
| 2221 | - // FIXME: more specific method, we don't want to generate the extra stuff. | ||
| 2222 | - // We also need cheap fixup after addPage/removePage. | ||
| 2223 | - | ||
| 2224 | - // no compressed objects to be produced here... | ||
| 2225 | - std::map<int, int> object_stream_data; | ||
| 2226 | - optimize(object_stream_data); // push down inheritance | ||
| 2227 | - | ||
| 2228 | - std::vector<QPDFObjectHandle> kids = this->getAllPages(); | ||
| 2229 | - QPDFObjectHandle pages = this->trailer.getKey("/Root").getKey("/Pages"); | ||
| 2230 | - | ||
| 2231 | - const int len = kids.size(); | ||
| 2232 | - for (int pos = 0; pos < len; ++pos) | ||
| 2233 | - { | ||
| 2234 | - // populate pageobj_to_pages_pos | ||
| 2235 | - ObjGen og(kids[pos].getObjectID(), kids[pos].getGeneration()); | ||
| 2236 | - if (! this->pageobj_to_pages_pos.insert(std::make_pair(og, pos)).second) | ||
| 2237 | - { | ||
| 2238 | - // insert failed: duplicate entry found | ||
| 2239 | - *out_stream << "WARNING: duplicate page reference found, " | ||
| 2240 | - << "but currently not fully supported." << std::endl; | ||
| 2241 | - } | ||
| 2242 | - | ||
| 2243 | - // fix parent links | ||
| 2244 | - kids[pos].replaceKey("/Parent", pages); | ||
| 2245 | - } | ||
| 2246 | - | ||
| 2247 | - pages.replaceKey("/Kids", QPDFObjectHandle::newArray(kids)); | ||
| 2248 | - // /Count has not changed | ||
| 2249 | - assert(pages.getKey("/Count").getIntValue() == len); | ||
| 2250 | -} | ||
| 2251 | - | ||
| 2252 | -int | ||
| 2253 | -QPDF::findPage(int objid, int generation) | ||
| 2254 | -{ | ||
| 2255 | - if (this->pageobj_to_pages_pos.empty()) | ||
| 2256 | - { | ||
| 2257 | - flattenPagesTree(); | ||
| 2258 | - } | ||
| 2259 | - std::map<ObjGen, int>::iterator it = | ||
| 2260 | - this->pageobj_to_pages_pos.find(ObjGen(objid, generation)); | ||
| 2261 | - if (it != this->pageobj_to_pages_pos.end()) | ||
| 2262 | - { | ||
| 2263 | - return (*it).second; | ||
| 2264 | - } | ||
| 2265 | - return -1; // throw? | ||
| 2266 | -} | ||
| 2267 | - | ||
| 2268 | -int | ||
| 2269 | -QPDF::findPage(QPDFObjectHandle const& pageoh) | ||
| 2270 | -{ | ||
| 2271 | - if (!pageoh.isInitialized()) | ||
| 2272 | - { | ||
| 2273 | - return -1; | ||
| 2274 | - // TODO? throw | ||
| 2275 | - } | ||
| 2276 | - return findPage(pageoh.getObjectID(), pageoh.getGeneration()); | ||
| 2277 | -} | ||
| 2278 | - | ||
| 2279 | -void | ||
| 2280 | -QPDF::addPage(QPDFObjectHandle newpage, bool first) | ||
| 2281 | -{ | ||
| 2282 | - if (this->pageobj_to_pages_pos.empty()) | ||
| 2283 | - { | ||
| 2284 | - flattenPagesTree(); | ||
| 2285 | - } | ||
| 2286 | - | ||
| 2287 | - newpage.assertPageObject(); // FIXME: currently private | ||
| 2288 | - | ||
| 2289 | - QPDFObjectHandle pages = this->trailer.getKey("/Root").getKey("/Pages"); | ||
| 2290 | - QPDFObjectHandle kids = pages.getKey("/Kids"); | ||
| 2291 | - | ||
| 2292 | - newpage.replaceKey("/Parent", pages); | ||
| 2293 | - if (first) | ||
| 2294 | - { | ||
| 2295 | - kids.insertItem(0, newpage); | ||
| 2296 | - } | ||
| 2297 | - else | ||
| 2298 | - { | ||
| 2299 | - kids.appendItem(newpage); | ||
| 2300 | - } | ||
| 2301 | - pages.replaceKey("/Count", | ||
| 2302 | - QPDFObjectHandle::newInteger(kids.getArrayNItems())); | ||
| 2303 | - | ||
| 2304 | - // FIXME: this is overkill, but cache is now stale | ||
| 2305 | - clearPagesCache(); | ||
| 2306 | -} | ||
| 2307 | - | ||
| 2308 | -void | ||
| 2309 | -QPDF::addPageAt(QPDFObjectHandle newpage, bool before, | ||
| 2310 | - QPDFObjectHandle const &refpage) | ||
| 2311 | -{ | ||
| 2312 | - int refpos = findPage(refpage); // also ensures flat /Pages | ||
| 2313 | - if (refpos == -1) | ||
| 2314 | - { | ||
| 2315 | - throw "Could not find refpage"; | ||
| 2316 | - } | ||
| 2317 | - | ||
| 2318 | - newpage.assertPageObject(); | ||
| 2319 | - | ||
| 2320 | - QPDFObjectHandle pages = this->trailer.getKey("/Root").getKey("/Pages"); | ||
| 2321 | - QPDFObjectHandle kids = pages.getKey("/Kids"); | ||
| 2322 | - | ||
| 2323 | - if (! before) | ||
| 2324 | - { | ||
| 2325 | - ++refpos; | ||
| 2326 | - } | ||
| 2327 | - | ||
| 2328 | - newpage.replaceKey("/Parent", pages); | ||
| 2329 | - kids.insertItem(refpos, newpage); | ||
| 2330 | - pages.replaceKey("/Count", | ||
| 2331 | - QPDFObjectHandle::newInteger(kids.getArrayNItems())); | ||
| 2332 | - | ||
| 2333 | - // FIXME: this is overkill, but cache is now stale | ||
| 2334 | - clearPagesCache(); | ||
| 2335 | -} | ||
| 2336 | - | ||
| 2337 | -void | ||
| 2338 | -QPDF::removePage(QPDFObjectHandle const& pageoh) | ||
| 2339 | -{ | ||
| 2340 | - int pos = findPage(pageoh); // also ensures flat /Pages | ||
| 2341 | - if (pos == -1) | ||
| 2342 | - { | ||
| 2343 | - throw "Can't remove non-existing page"; | ||
| 2344 | - } | ||
| 2345 | - | ||
| 2346 | - QPDFObjectHandle pages = this->trailer.getKey("/Root").getKey("/Pages"); | ||
| 2347 | - QPDFObjectHandle kids = pages.getKey("/Kids"); | ||
| 2348 | - | ||
| 2349 | - kids.eraseItem(pos); | ||
| 2350 | - pages.replaceKey("/Count", | ||
| 2351 | - QPDFObjectHandle::newInteger(kids.getArrayNItems())); | ||
| 2352 | - | ||
| 2353 | - // FIXME: this is overkill, but cache is now stale | ||
| 2354 | - clearPagesCache(); | ||
| 2355 | -} |
libqpdf/QPDF_pages.cc
0 โ 100644
| 1 | +#include <qpdf/QPDF.hh> | ||
| 2 | + | ||
| 3 | +#include <assert.h> | ||
| 4 | + | ||
| 5 | +#include <qpdf/QTC.hh> | ||
| 6 | +#include <qpdf/QUtil.hh> | ||
| 7 | +#include <qpdf/QPDFExc.hh> | ||
| 8 | + | ||
| 9 | +std::vector<QPDFObjectHandle> const& | ||
| 10 | +QPDF::getAllPages() | ||
| 11 | +{ | ||
| 12 | + if (this->all_pages.empty()) | ||
| 13 | + { | ||
| 14 | + getAllPagesInternal( | ||
| 15 | + this->trailer.getKey("/Root").getKey("/Pages"), this->all_pages); | ||
| 16 | + } | ||
| 17 | + return this->all_pages; | ||
| 18 | +} | ||
| 19 | + | ||
| 20 | +void | ||
| 21 | +QPDF::getAllPagesInternal(QPDFObjectHandle cur_pages, | ||
| 22 | + std::vector<QPDFObjectHandle>& result) | ||
| 23 | +{ | ||
| 24 | + std::string type = cur_pages.getKey("/Type").getName(); | ||
| 25 | + if (type == "/Pages") | ||
| 26 | + { | ||
| 27 | + QPDFObjectHandle kids = cur_pages.getKey("/Kids"); | ||
| 28 | + int n = kids.getArrayNItems(); | ||
| 29 | + for (int i = 0; i < n; ++i) | ||
| 30 | + { | ||
| 31 | + getAllPagesInternal(kids.getArrayItem(i), result); | ||
| 32 | + } | ||
| 33 | + } | ||
| 34 | + else if (type == "/Page") | ||
| 35 | + { | ||
| 36 | + result.push_back(cur_pages); | ||
| 37 | + } | ||
| 38 | + else | ||
| 39 | + { | ||
| 40 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), | ||
| 41 | + this->last_object_description, | ||
| 42 | + this->file->getLastOffset(), | ||
| 43 | + ": invalid Type in page tree"); | ||
| 44 | + } | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +// FIXXX here down | ||
| 48 | + | ||
| 49 | +void | ||
| 50 | +QPDF::clearPagesCache() | ||
| 51 | +{ | ||
| 52 | + this->all_pages.clear(); | ||
| 53 | + this->pageobj_to_pages_pos.clear(); | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +void | ||
| 57 | +QPDF::flattenPagesTree() | ||
| 58 | +{ | ||
| 59 | + clearPagesCache(); | ||
| 60 | + | ||
| 61 | + // FIXME: more specific method, we don't want to generate the extra stuff. | ||
| 62 | + // We also need cheap fixup after addPage/removePage. | ||
| 63 | + | ||
| 64 | + // no compressed objects to be produced here... | ||
| 65 | + std::map<int, int> object_stream_data; | ||
| 66 | + optimize(object_stream_data); // push down inheritance | ||
| 67 | + | ||
| 68 | + std::vector<QPDFObjectHandle> kids = this->getAllPages(); | ||
| 69 | + QPDFObjectHandle pages = this->trailer.getKey("/Root").getKey("/Pages"); | ||
| 70 | + | ||
| 71 | + const int len = kids.size(); | ||
| 72 | + for (int pos = 0; pos < len; ++pos) | ||
| 73 | + { | ||
| 74 | + // populate pageobj_to_pages_pos | ||
| 75 | + ObjGen og(kids[pos].getObjectID(), kids[pos].getGeneration()); | ||
| 76 | + if (! this->pageobj_to_pages_pos.insert(std::make_pair(og, pos)).second) | ||
| 77 | + { | ||
| 78 | + // insert failed: duplicate entry found | ||
| 79 | + *out_stream << "WARNING: duplicate page reference found, " | ||
| 80 | + << "but currently not fully supported." << std::endl; | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + // fix parent links | ||
| 84 | + kids[pos].replaceKey("/Parent", pages); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + pages.replaceKey("/Kids", QPDFObjectHandle::newArray(kids)); | ||
| 88 | + // /Count has not changed | ||
| 89 | + assert(pages.getKey("/Count").getIntValue() == len); | ||
| 90 | +} | ||
| 91 | + | ||
| 92 | +int | ||
| 93 | +QPDF::findPage(int objid, int generation) | ||
| 94 | +{ | ||
| 95 | + if (this->pageobj_to_pages_pos.empty()) | ||
| 96 | + { | ||
| 97 | + flattenPagesTree(); | ||
| 98 | + } | ||
| 99 | + std::map<ObjGen, int>::iterator it = | ||
| 100 | + this->pageobj_to_pages_pos.find(ObjGen(objid, generation)); | ||
| 101 | + if (it != this->pageobj_to_pages_pos.end()) | ||
| 102 | + { | ||
| 103 | + return (*it).second; | ||
| 104 | + } | ||
| 105 | + return -1; // throw? | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +int | ||
| 109 | +QPDF::findPage(QPDFObjectHandle const& pageoh) | ||
| 110 | +{ | ||
| 111 | + if (!pageoh.isInitialized()) | ||
| 112 | + { | ||
| 113 | + return -1; | ||
| 114 | + // TODO? throw | ||
| 115 | + } | ||
| 116 | + return findPage(pageoh.getObjectID(), pageoh.getGeneration()); | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +void | ||
| 120 | +QPDF::addPage(QPDFObjectHandle newpage, bool first) | ||
| 121 | +{ | ||
| 122 | + if (this->pageobj_to_pages_pos.empty()) | ||
| 123 | + { | ||
| 124 | + flattenPagesTree(); | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + newpage.assertPageObject(); // FIXME: currently private | ||
| 128 | + | ||
| 129 | + QPDFObjectHandle pages = this->trailer.getKey("/Root").getKey("/Pages"); | ||
| 130 | + QPDFObjectHandle kids = pages.getKey("/Kids"); | ||
| 131 | + | ||
| 132 | + newpage.replaceKey("/Parent", pages); | ||
| 133 | + if (first) | ||
| 134 | + { | ||
| 135 | + kids.insertItem(0, newpage); | ||
| 136 | + } | ||
| 137 | + else | ||
| 138 | + { | ||
| 139 | + kids.appendItem(newpage); | ||
| 140 | + } | ||
| 141 | + pages.replaceKey("/Count", | ||
| 142 | + QPDFObjectHandle::newInteger(kids.getArrayNItems())); | ||
| 143 | + | ||
| 144 | + // FIXME: this is overkill, but cache is now stale | ||
| 145 | + clearPagesCache(); | ||
| 146 | +} | ||
| 147 | + | ||
| 148 | +void | ||
| 149 | +QPDF::addPageAt(QPDFObjectHandle newpage, bool before, | ||
| 150 | + QPDFObjectHandle const &refpage) | ||
| 151 | +{ | ||
| 152 | + int refpos = findPage(refpage); // also ensures flat /Pages | ||
| 153 | + if (refpos == -1) | ||
| 154 | + { | ||
| 155 | + throw "Could not find refpage"; | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + newpage.assertPageObject(); | ||
| 159 | + | ||
| 160 | + QPDFObjectHandle pages = this->trailer.getKey("/Root").getKey("/Pages"); | ||
| 161 | + QPDFObjectHandle kids = pages.getKey("/Kids"); | ||
| 162 | + | ||
| 163 | + if (! before) | ||
| 164 | + { | ||
| 165 | + ++refpos; | ||
| 166 | + } | ||
| 167 | + | ||
| 168 | + newpage.replaceKey("/Parent", pages); | ||
| 169 | + kids.insertItem(refpos, newpage); | ||
| 170 | + pages.replaceKey("/Count", | ||
| 171 | + QPDFObjectHandle::newInteger(kids.getArrayNItems())); | ||
| 172 | + | ||
| 173 | + // FIXME: this is overkill, but cache is now stale | ||
| 174 | + clearPagesCache(); | ||
| 175 | +} | ||
| 176 | + | ||
| 177 | +void | ||
| 178 | +QPDF::removePage(QPDFObjectHandle const& pageoh) | ||
| 179 | +{ | ||
| 180 | + int pos = findPage(pageoh); // also ensures flat /Pages | ||
| 181 | + if (pos == -1) | ||
| 182 | + { | ||
| 183 | + throw "Can't remove non-existing page"; | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + QPDFObjectHandle pages = this->trailer.getKey("/Root").getKey("/Pages"); | ||
| 187 | + QPDFObjectHandle kids = pages.getKey("/Kids"); | ||
| 188 | + | ||
| 189 | + kids.eraseItem(pos); | ||
| 190 | + pages.replaceKey("/Count", | ||
| 191 | + QPDFObjectHandle::newInteger(kids.getArrayNItems())); | ||
| 192 | + | ||
| 193 | + // FIXME: this is overkill, but cache is now stale | ||
| 194 | + clearPagesCache(); | ||
| 195 | +} |
libqpdf/build.mk
| @@ -43,6 +43,7 @@ SRCS_libqpdf = \ | @@ -43,6 +43,7 @@ SRCS_libqpdf = \ | ||
| 43 | libqpdf/QPDF_encryption.cc \ | 43 | libqpdf/QPDF_encryption.cc \ |
| 44 | libqpdf/QPDF_linearization.cc \ | 44 | libqpdf/QPDF_linearization.cc \ |
| 45 | libqpdf/QPDF_optimization.cc \ | 45 | libqpdf/QPDF_optimization.cc \ |
| 46 | + libqpdf/QPDF_pages.cc \ | ||
| 46 | libqpdf/QTC.cc \ | 47 | libqpdf/QTC.cc \ |
| 47 | libqpdf/QUtil.cc \ | 48 | libqpdf/QUtil.cc \ |
| 48 | libqpdf/RC4.cc \ | 49 | libqpdf/RC4.cc \ |