Commit c71035c7f306244e716c2bc1132ca7174d212b25
1 parent
a70158a3
Refactor `QPDFWriter`: move methods `prepareFileForWrite`, `enqueueObject`, `enq…
…ueueObjectsStandard`, `enqueueObjectsPCLm`, `enqueuePart`, `assignCompressedObjectNumbers`, and `getTrimmedTrailer` to `QPDFWriter::Members`. Update related logic and remove obsolete test coverage entries.
Showing
3 changed files
with
51 additions
and
57 deletions
include/qpdf/QPDFWriter.hh
| ... | ... | @@ -471,8 +471,6 @@ class QPDFWriter |
| 471 | 471 | QPDFWriter& write_qdf(Args&&... args); |
| 472 | 472 | template <typename... Args> |
| 473 | 473 | QPDFWriter& write_no_qdf(Args&&... args); |
| 474 | - void assignCompressedObjectNumbers(QPDFObjGen og); | |
| 475 | - void enqueueObject(QPDFObjectHandle object); | |
| 476 | 474 | void writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj); |
| 477 | 475 | void writeObjectStream(QPDFObjectHandle object); |
| 478 | 476 | void writeObject(QPDFObjectHandle object, int object_stream_index = -1); |
| ... | ... | @@ -505,14 +503,9 @@ class QPDFWriter |
| 505 | 503 | void setDataKey(int objid); |
| 506 | 504 | int openObject(int objid = 0); |
| 507 | 505 | void closeObject(int objid); |
| 508 | - QPDFObjectHandle getTrimmedTrailer(); | |
| 509 | - void prepareFileForWrite(); | |
| 510 | - void enqueueObjectsStandard(); | |
| 511 | - void enqueueObjectsPCLm(); | |
| 512 | 506 | void indicateProgress(bool decrement, bool finished); |
| 513 | 507 | void writeStandard(); |
| 514 | 508 | void writeLinearized(); |
| 515 | - void enqueuePart(std::vector<QPDFObjectHandle>& part); | |
| 516 | 509 | void writeEncryptionDictionary(); |
| 517 | 510 | void writeHeader(); |
| 518 | 511 | void writeHintStream(int hint_id); | ... | ... |
libqpdf/QPDFWriter.cc
| ... | ... | @@ -287,6 +287,7 @@ class QPDFWriter::Members |
| 287 | 287 | void setMinimumPDFVersion(std::string const& version, int extension_level); |
| 288 | 288 | void copyEncryptionParameters(QPDF&); |
| 289 | 289 | void doWriteSetup(); |
| 290 | + void prepareFileForWrite(); | |
| 290 | 291 | |
| 291 | 292 | void disableIncompatibleEncryption(int major, int minor, int extension_level); |
| 292 | 293 | void parseVersion(std::string const& version, int& major, int& minor) const; |
| ... | ... | @@ -297,6 +298,12 @@ class QPDFWriter::Members |
| 297 | 298 | void preserveObjectStreams(); |
| 298 | 299 | void generateObjectStreams(); |
| 299 | 300 | void initializeSpecialStreams(); |
| 301 | + void enqueueObject(QPDFObjectHandle object); | |
| 302 | + void enqueueObjectsStandard(); | |
| 303 | + void enqueueObjectsPCLm(); | |
| 304 | + void enqueuePart(std::vector<QPDFObjectHandle>& part); | |
| 305 | + void assignCompressedObjectNumbers(QPDFObjGen og); | |
| 306 | + QPDFObjectHandle getTrimmedTrailer(); | |
| 300 | 307 | |
| 301 | 308 | private: |
| 302 | 309 | QPDFWriter& w; |
| ... | ... | @@ -1170,76 +1177,74 @@ QPDFWriter::closeObject(int objid) |
| 1170 | 1177 | } |
| 1171 | 1178 | |
| 1172 | 1179 | void |
| 1173 | -QPDFWriter::assignCompressedObjectNumbers(QPDFObjGen og) | |
| 1180 | +QPDFWriter::Members::assignCompressedObjectNumbers(QPDFObjGen og) | |
| 1174 | 1181 | { |
| 1175 | 1182 | int objid = og.getObj(); |
| 1176 | - if ((og.getGen() != 0) || (!m->object_stream_to_objects.contains(objid))) { | |
| 1183 | + if (og.getGen() != 0 || !object_stream_to_objects.contains(objid)) { | |
| 1177 | 1184 | // This is not an object stream. |
| 1178 | 1185 | return; |
| 1179 | 1186 | } |
| 1180 | 1187 | |
| 1181 | 1188 | // Reserve numbers for the objects that belong to this object stream. |
| 1182 | - for (auto const& iter: m->object_stream_to_objects[objid]) { | |
| 1183 | - m->obj[iter].renumber = m->next_objid++; | |
| 1189 | + for (auto const& iter: object_stream_to_objects[objid]) { | |
| 1190 | + obj[iter].renumber = next_objid++; | |
| 1184 | 1191 | } |
| 1185 | 1192 | } |
| 1186 | 1193 | |
| 1187 | 1194 | void |
| 1188 | -QPDFWriter::enqueueObject(QPDFObjectHandle object) | |
| 1195 | +QPDFWriter::Members::enqueueObject(QPDFObjectHandle object) | |
| 1189 | 1196 | { |
| 1190 | 1197 | if (object.isIndirect()) { |
| 1191 | 1198 | // This owner check can only be done for indirect objects. It is possible for a direct |
| 1192 | 1199 | // object to have an owning QPDF that is from another file if a direct QPDFObjectHandle from |
| 1193 | 1200 | // one file was insert into another file without copying. Doing that is safe even if the |
| 1194 | 1201 | // original QPDF gets destroyed, which just disconnects the QPDFObjectHandle from its owner. |
| 1195 | - if (object.getOwningQPDF() != &(m->pdf)) { | |
| 1196 | - QTC::TC("qpdf", "QPDFWriter foreign object"); | |
| 1202 | + if (object.getOwningQPDF() != &pdf) { | |
| 1197 | 1203 | throw std::logic_error( |
| 1198 | 1204 | "QPDFObjectHandle from different QPDF found while writing. Use " |
| 1199 | 1205 | "QPDF::copyForeignObject to add objects from another file."); |
| 1200 | 1206 | } |
| 1201 | 1207 | |
| 1202 | - if (m->qdf_mode && object.isStreamOfType("/XRef")) { | |
| 1208 | + if (qdf_mode && object.isStreamOfType("/XRef")) { | |
| 1203 | 1209 | // As a special case, do not output any extraneous XRef streams in QDF mode. Doing so |
| 1204 | 1210 | // will confuse fix-qdf, which expects to see only one XRef stream at the end of the |
| 1205 | 1211 | // file. This case can occur when creating a QDF from a file with object streams when |
| 1206 | 1212 | // preserving unreferenced objects since the old cross reference streams are not |
| 1207 | 1213 | // actually referenced by object number. |
| 1208 | - QTC::TC("qpdf", "QPDFWriter ignore XRef in qdf mode"); | |
| 1209 | 1214 | return; |
| 1210 | 1215 | } |
| 1211 | 1216 | |
| 1212 | 1217 | QPDFObjGen og = object.getObjGen(); |
| 1213 | - auto& obj = m->obj[og]; | |
| 1218 | + auto& o = obj[og]; | |
| 1214 | 1219 | |
| 1215 | - if (obj.renumber == 0) { | |
| 1216 | - if (obj.object_stream > 0) { | |
| 1220 | + if (o.renumber == 0) { | |
| 1221 | + if (o.object_stream > 0) { | |
| 1217 | 1222 | // This is in an object stream. Don't process it here. Instead, enqueue the object |
| 1218 | 1223 | // stream. Object streams always have generation 0. |
| 1219 | 1224 | // Detect loops by storing invalid object ID -1, which will get overwritten later. |
| 1220 | - obj.renumber = -1; | |
| 1221 | - enqueueObject(m->pdf.getObject(obj.object_stream, 0)); | |
| 1225 | + o.renumber = -1; | |
| 1226 | + enqueueObject(pdf.getObject(o.object_stream, 0)); | |
| 1222 | 1227 | } else { |
| 1223 | - m->object_queue.push_back(object); | |
| 1224 | - obj.renumber = m->next_objid++; | |
| 1228 | + object_queue.emplace_back(object); | |
| 1229 | + o.renumber = next_objid++; | |
| 1225 | 1230 | |
| 1226 | - if ((og.getGen() == 0) && m->object_stream_to_objects.contains(og.getObj())) { | |
| 1231 | + if (og.getGen() == 0 && object_stream_to_objects.contains(og.getObj())) { | |
| 1227 | 1232 | // For linearized files, uncompressed objects go at end, and we take care of |
| 1228 | 1233 | // assigning numbers to them elsewhere. |
| 1229 | - if (!m->linearized) { | |
| 1234 | + if (!linearized) { | |
| 1230 | 1235 | assignCompressedObjectNumbers(og); |
| 1231 | 1236 | } |
| 1232 | - } else if ((!m->direct_stream_lengths) && object.isStream()) { | |
| 1237 | + } else if (!direct_stream_lengths && object.isStream()) { | |
| 1233 | 1238 | // reserve next object ID for length |
| 1234 | - ++m->next_objid; | |
| 1239 | + ++next_objid; | |
| 1235 | 1240 | } |
| 1236 | 1241 | } |
| 1237 | - } else if (obj.renumber == -1) { | |
| 1242 | + } else if (o.renumber == -1) { | |
| 1238 | 1243 | // This can happen if a specially constructed file indicates that an object stream is |
| 1239 | 1244 | // inside itself. |
| 1240 | 1245 | } |
| 1241 | 1246 | return; |
| 1242 | - } else if (!m->linearized) { | |
| 1247 | + } else if (!linearized) { | |
| 1243 | 1248 | if (object.isArray()) { |
| 1244 | 1249 | for (auto& item: object.as_array()) { |
| 1245 | 1250 | enqueueObject(item); |
| ... | ... | @@ -1260,7 +1265,7 @@ void |
| 1260 | 1265 | QPDFWriter::unparseChild(QPDFObjectHandle const& child, size_t level, int flags) |
| 1261 | 1266 | { |
| 1262 | 1267 | if (!m->linearized) { |
| 1263 | - enqueueObject(child); | |
| 1268 | + m->enqueueObject(child); | |
| 1264 | 1269 | } |
| 1265 | 1270 | if (child.isIndirect()) { |
| 1266 | 1271 | write(m->obj[child].renumber).write(" 0 R"); |
| ... | ... | @@ -1273,7 +1278,7 @@ void |
| 1273 | 1278 | QPDFWriter::writeTrailer( |
| 1274 | 1279 | trailer_e which, int size, bool xref_stream, qpdf_offset_t prev, int linearization_pass) |
| 1275 | 1280 | { |
| 1276 | - QPDFObjectHandle trailer = getTrimmedTrailer(); | |
| 1281 | + QPDFObjectHandle trailer = m->getTrimmedTrailer(); | |
| 1277 | 1282 | if (xref_stream) { |
| 1278 | 1283 | m->cur_data_key.clear(); |
| 1279 | 1284 | } else { |
| ... | ... | @@ -2082,11 +2087,11 @@ QPDFWriter::Members::generateObjectStreams() |
| 2082 | 2087 | } |
| 2083 | 2088 | |
| 2084 | 2089 | QPDFObjectHandle |
| 2085 | -QPDFWriter::getTrimmedTrailer() | |
| 2090 | +QPDFWriter::Members::getTrimmedTrailer() | |
| 2086 | 2091 | { |
| 2087 | 2092 | // Remove keys from the trailer that necessarily have to be replaced when writing the file. |
| 2088 | 2093 | |
| 2089 | - QPDFObjectHandle trailer = m->pdf.getTrailer().unsafeShallowCopy(); | |
| 2094 | + QPDFObjectHandle trailer = pdf.getTrailer().unsafeShallowCopy(); | |
| 2090 | 2095 | |
| 2091 | 2096 | // Remove encryption keys |
| 2092 | 2097 | trailer.removeKey("/ID"); |
| ... | ... | @@ -2109,10 +2114,10 @@ QPDFWriter::getTrimmedTrailer() |
| 2109 | 2114 | |
| 2110 | 2115 | // Make document extension level information direct as required by the spec. |
| 2111 | 2116 | void |
| 2112 | -QPDFWriter::prepareFileForWrite() | |
| 2117 | +QPDFWriter::Members::prepareFileForWrite() | |
| 2113 | 2118 | { |
| 2114 | - m->pdf.fixDanglingReferences(); | |
| 2115 | - auto root = m->pdf.getRoot(); | |
| 2119 | + pdf.fixDanglingReferences(); | |
| 2120 | + auto root = pdf.getRoot(); | |
| 2116 | 2121 | auto oh = root.getKey("/Extensions"); |
| 2117 | 2122 | if (oh.isDictionary()) { |
| 2118 | 2123 | const bool extensions_indirect = oh.isIndirect(); |
| ... | ... | @@ -2280,7 +2285,7 @@ QPDFWriter::write() |
| 2280 | 2285 | // approximation, but it's good enough for progress reporting, which is mostly a guess anyway. |
| 2281 | 2286 | m->events_expected = QIntC::to_int(m->pdf.getObjectCount() * (m->linearized ? 2 : 1)); |
| 2282 | 2287 | |
| 2283 | - prepareFileForWrite(); | |
| 2288 | + m->prepareFileForWrite(); | |
| 2284 | 2289 | |
| 2285 | 2290 | if (m->linearized) { |
| 2286 | 2291 | writeLinearized(); |
| ... | ... | @@ -2321,7 +2326,7 @@ QPDFWriter::getWrittenXRefTable() |
| 2321 | 2326 | } |
| 2322 | 2327 | |
| 2323 | 2328 | void |
| 2324 | -QPDFWriter::enqueuePart(std::vector<QPDFObjectHandle>& part) | |
| 2329 | +QPDFWriter::Members::enqueuePart(std::vector<QPDFObjectHandle>& part) | |
| 2325 | 2330 | { |
| 2326 | 2331 | for (auto const& oh: part) { |
| 2327 | 2332 | enqueueObject(oh); |
| ... | ... | @@ -2650,7 +2655,7 @@ QPDFWriter::writeLinearized() |
| 2650 | 2655 | std::vector<QPDFObjectHandle>* vecs2[] = {&part7, &part8, &part9}; |
| 2651 | 2656 | for (int i = 0; i < 3; ++i) { |
| 2652 | 2657 | for (auto const& oh: *vecs2[i]) { |
| 2653 | - assignCompressedObjectNumbers(oh.getObjGen()); | |
| 2658 | + m->assignCompressedObjectNumbers(oh.getObjGen()); | |
| 2654 | 2659 | } |
| 2655 | 2660 | } |
| 2656 | 2661 | int second_half_end = m->next_objid - 1; |
| ... | ... | @@ -2677,7 +2682,7 @@ QPDFWriter::writeLinearized() |
| 2677 | 2682 | std::vector<QPDFObjectHandle>* vecs1[] = {&part4, &part6}; |
| 2678 | 2683 | for (int i = 0; i < 2; ++i) { |
| 2679 | 2684 | for (auto const& oh: *vecs1[i]) { |
| 2680 | - assignCompressedObjectNumbers(oh.getObjGen()); | |
| 2685 | + m->assignCompressedObjectNumbers(oh.getObjGen()); | |
| 2681 | 2686 | } |
| 2682 | 2687 | } |
| 2683 | 2688 | int first_half_end = m->next_objid - 1; |
| ... | ... | @@ -2694,21 +2699,21 @@ QPDFWriter::writeLinearized() |
| 2694 | 2699 | qpdf_offset_t second_xref_end = 0; |
| 2695 | 2700 | |
| 2696 | 2701 | m->next_objid = part4_first_obj; |
| 2697 | - enqueuePart(part4); | |
| 2702 | + m->enqueuePart(part4); | |
| 2698 | 2703 | if (m->next_objid != after_part4) { |
| 2699 | 2704 | // This can happen with very botched files as in the fuzzer test. There are likely some |
| 2700 | 2705 | // faulty assumptions in calculateLinearizationData |
| 2701 | 2706 | throw std::runtime_error("error encountered after writing part 4 of linearized data"); |
| 2702 | 2707 | } |
| 2703 | 2708 | m->next_objid = part6_first_obj; |
| 2704 | - enqueuePart(part6); | |
| 2709 | + m->enqueuePart(part6); | |
| 2705 | 2710 | if (m->next_objid != after_part6) { |
| 2706 | 2711 | throw std::runtime_error("error encountered after writing part 6 of linearized data"); |
| 2707 | 2712 | } |
| 2708 | 2713 | m->next_objid = second_half_first_obj; |
| 2709 | - enqueuePart(part7); | |
| 2710 | - enqueuePart(part8); | |
| 2711 | - enqueuePart(part9); | |
| 2714 | + m->enqueuePart(part7); | |
| 2715 | + m->enqueuePart(part8); | |
| 2716 | + m->enqueuePart(part9); | |
| 2712 | 2717 | if (m->next_objid != after_second_half) { |
| 2713 | 2718 | throw std::runtime_error("error encountered after writing part 9 of linearized data"); |
| 2714 | 2719 | } |
| ... | ... | @@ -2947,11 +2952,10 @@ QPDFWriter::writeLinearized() |
| 2947 | 2952 | } |
| 2948 | 2953 | |
| 2949 | 2954 | void |
| 2950 | -QPDFWriter::enqueueObjectsStandard() | |
| 2955 | +QPDFWriter::Members::enqueueObjectsStandard() | |
| 2951 | 2956 | { |
| 2952 | - if (m->preserve_unreferenced_objects) { | |
| 2953 | - QTC::TC("qpdf", "QPDFWriter preserve unreferenced standard"); | |
| 2954 | - for (auto const& oh: m->pdf.getAllObjects()) { | |
| 2957 | + if (preserve_unreferenced_objects) { | |
| 2958 | + for (auto const& oh: pdf.getAllObjects()) { | |
| 2955 | 2959 | enqueueObject(oh); |
| 2956 | 2960 | } |
| 2957 | 2961 | } |
| ... | ... | @@ -2970,14 +2974,14 @@ QPDFWriter::enqueueObjectsStandard() |
| 2970 | 2974 | } |
| 2971 | 2975 | |
| 2972 | 2976 | void |
| 2973 | -QPDFWriter::enqueueObjectsPCLm() | |
| 2977 | +QPDFWriter::Members::enqueueObjectsPCLm() | |
| 2974 | 2978 | { |
| 2975 | 2979 | // Image transform stream content for page strip images. Each of this new stream has to come |
| 2976 | 2980 | // after every page image strip written in the pclm file. |
| 2977 | 2981 | std::string image_transform_content = "q /image Do Q\n"; |
| 2978 | 2982 | |
| 2979 | 2983 | // enqueue all pages first |
| 2980 | - std::vector<QPDFObjectHandle> all = m->pdf.getAllPages(); | |
| 2984 | + std::vector<QPDFObjectHandle> all = pdf.getAllPages(); | |
| 2981 | 2985 | for (auto& page: all) { |
| 2982 | 2986 | // enqueue page |
| 2983 | 2987 | enqueueObject(page); |
| ... | ... | @@ -2990,7 +2994,7 @@ QPDFWriter::enqueueObjectsPCLm() |
| 2990 | 2994 | for (auto& image: strips.as_dictionary()) { |
| 2991 | 2995 | if (!image.second.null()) { |
| 2992 | 2996 | enqueueObject(image.second); |
| 2993 | - enqueueObject(QPDFObjectHandle::newStream(&m->pdf, image_transform_content)); | |
| 2997 | + enqueueObject(QPDFObjectHandle::newStream(&pdf, image_transform_content)); | |
| 2994 | 2998 | } |
| 2995 | 2999 | } |
| 2996 | 3000 | } |
| ... | ... | @@ -3048,9 +3052,9 @@ QPDFWriter::writeStandard() |
| 3048 | 3052 | write(m->extra_header_text); |
| 3049 | 3053 | |
| 3050 | 3054 | if (m->pclm) { |
| 3051 | - enqueueObjectsPCLm(); | |
| 3055 | + m->enqueueObjectsPCLm(); | |
| 3052 | 3056 | } else { |
| 3053 | - enqueueObjectsStandard(); | |
| 3057 | + m->enqueueObjectsStandard(); | |
| 3054 | 3058 | } |
| 3055 | 3059 | |
| 3056 | 3060 | // Now start walking queue, outputting each object. | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -188,7 +188,6 @@ QPDF replace dictionary 0 |
| 188 | 188 | QPDF replace stream 0 |
| 189 | 189 | QPDF replace foreign indirect with null 0 |
| 190 | 190 | QPDF insert foreign page 0 |
| 191 | -QPDFWriter foreign object 0 | |
| 192 | 191 | QPDFWriter copy use_aes 1 |
| 193 | 192 | QPDFParser indirect without context 0 |
| 194 | 193 | QPDFObjectHandle trailing data in parse 0 |
| ... | ... | @@ -228,7 +227,6 @@ QPDFParser treat word as string in parseRemainder 0 |
| 228 | 227 | QPDFParser found fake 1 |
| 229 | 228 | QPDFParser no val for last key 0 |
| 230 | 229 | QPDF resolve failure to null 0 |
| 231 | -QPDFWriter preserve unreferenced standard 0 | |
| 232 | 230 | QPDFObjectHandle errors in parsecontent 0 |
| 233 | 231 | QPDFJob same file error 0 |
| 234 | 232 | QPDFJob split-pages %d 0 |
| ... | ... | @@ -365,7 +363,6 @@ QPDF_encryption same password 1 |
| 365 | 363 | QPDFParser duplicate dict key 0 |
| 366 | 364 | QPDFWriter no encryption sig contents 0 |
| 367 | 365 | QPDFPageObjectHelper colorspace lookup 0 |
| 368 | -QPDFWriter ignore XRef in qdf mode 0 | |
| 369 | 366 | QPDFPageObjectHelper filter form xobject 0 |
| 370 | 367 | QPDFJob found resources in non-leaf 0 |
| 371 | 368 | QPDFJob found shared resources in leaf 0 | ... | ... |