Commit c71035c7f306244e716c2bc1132ca7174d212b25

Authored by m-holger
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.
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
... ...