Commit ff09d99466d46d8e42aaf9797844c9007cc76746

Authored by m-holger
1 parent ba1ded84

Refactor `QPDFWriter`: move multiple write-related methods to `QPDFWriter::Membe…

…rs`, update encryption and stream handling logic, and remove obsolete test coverage entries.
include/qpdf/QPDFWriter.hh
... ... @@ -458,32 +458,6 @@ class QPDFWriter
458 458  
459 459 enum trailer_e { t_normal, t_lin_first, t_lin_second };
460 460  
461   - unsigned int bytesNeeded(long long n);
462   - void writeBinary(unsigned long long val, unsigned int bytes);
463   - QPDFWriter& write(std::string_view str);
464   - QPDFWriter& write(size_t count, char c);
465   - QPDFWriter& write(std::integral auto val);
466   - QPDFWriter& write_name(std::string const& str);
467   - QPDFWriter& write_string(std::string const& str, bool force_binary = false);
468   - QPDFWriter& write_encrypted(std::string_view str);
469   -
470   - template <typename... Args>
471   - QPDFWriter& write_qdf(Args&&... args);
472   - template <typename... Args>
473   - QPDFWriter& write_no_qdf(Args&&... args);
474   - void writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj);
475   - void writeObjectStream(QPDFObjectHandle object);
476   - void writeObject(QPDFObjectHandle object, int object_stream_index = -1);
477   - void writeTrailer(
478   - trailer_e which, int size, bool xref_stream, qpdf_offset_t prev, int linearization_pass);
479   - void unparseObject(
480   - QPDFObjectHandle object,
481   - size_t level,
482   - int flags,
483   - // for stream dictionaries
484   - size_t stream_length = 0,
485   - bool compress = false);
486   - void unparseChild(QPDFObjectHandle const& child, size_t level, int flags);
487 461 void interpretR3EncryptionParameters(
488 462 bool allow_accessibility,
489 463 bool allow_extract,
... ... @@ -496,50 +470,7 @@ class QPDFWriter
496 470 void setEncryptionParameters(char const* user_password, char const* owner_password);
497 471 void setEncryptionMinimumVersion();
498 472 void setDataKey(int objid);
499   - int openObject(int objid = 0);
500   - void closeObject(int objid);
501 473 void indicateProgress(bool decrement, bool finished);
502   - void writeStandard();
503   - void writeLinearized();
504   - void writeEncryptionDictionary();
505   - void writeHeader();
506   - void writeHintStream(int hint_id);
507   - qpdf_offset_t writeXRefTable(trailer_e which, int first, int last, int size);
508   - qpdf_offset_t writeXRefTable(
509   - trailer_e which,
510   - int first,
511   - int last,
512   - int size,
513   - // for linearization
514   - qpdf_offset_t prev,
515   - bool suppress_offsets,
516   - int hint_id,
517   - qpdf_offset_t hint_offset,
518   - qpdf_offset_t hint_length,
519   - int linearization_pass);
520   - qpdf_offset_t writeXRefStream(
521   - int objid,
522   - int max_id,
523   - qpdf_offset_t max_offset,
524   - trailer_e which,
525   - int first,
526   - int last,
527   - int size);
528   - qpdf_offset_t writeXRefStream(
529   - int objid,
530   - int max_id,
531   - qpdf_offset_t max_offset,
532   - trailer_e which,
533   - int first,
534   - int last,
535   - int size,
536   - // for linearization
537   - qpdf_offset_t prev,
538   - int hint_id,
539   - qpdf_offset_t hint_offset,
540   - qpdf_offset_t hint_length,
541   - bool skip_compression,
542   - int linearization_pass);
543 474 size_t calculateXrefStreamPadding(qpdf_offset_t xref_bytes);
544 475  
545 476 // When filtering subsections, push additional pipelines to the stack. When ready to switch,
... ...
libqpdf/QPDFWriter.cc
... ... @@ -310,6 +310,75 @@ class QPDFWriter::Members
310 310 bool& compress_stream,
311 311 bool& is_metadata,
312 312 std::string* stream_data);
  313 + unsigned int bytesNeeded(long long n);
  314 + void writeBinary(unsigned long long val, unsigned int bytes);
  315 + Members& write(std::string_view str);
  316 + Members& write(size_t count, char c);
  317 + Members& write(std::integral auto val);
  318 + Members& write_name(std::string const& str);
  319 + Members& write_string(std::string const& str, bool force_binary = false);
  320 + Members& write_encrypted(std::string_view str);
  321 +
  322 + template <typename... Args>
  323 + Members& write_qdf(Args&&... args);
  324 + template <typename... Args>
  325 + Members& write_no_qdf(Args&&... args);
  326 + void writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj);
  327 + void writeObjectStream(QPDFObjectHandle object);
  328 + void writeObject(QPDFObjectHandle object, int object_stream_index = -1);
  329 + void writeTrailer(
  330 + trailer_e which, int size, bool xref_stream, qpdf_offset_t prev, int linearization_pass);
  331 + void unparseObject(
  332 + QPDFObjectHandle object,
  333 + size_t level,
  334 + int flags,
  335 + // for stream dictionaries
  336 + size_t stream_length = 0,
  337 + bool compress = false);
  338 + void unparseChild(QPDFObjectHandle const& child, size_t level, int flags);
  339 + int openObject(int objid = 0);
  340 + void closeObject(int objid);
  341 + void writeStandard();
  342 + void writeLinearized();
  343 + void writeEncryptionDictionary();
  344 + void writeHeader();
  345 + void writeHintStream(int hint_id);
  346 + qpdf_offset_t writeXRefTable(trailer_e which, int first, int last, int size);
  347 + qpdf_offset_t writeXRefTable(
  348 + trailer_e which,
  349 + int first,
  350 + int last,
  351 + int size,
  352 + // for linearization
  353 + qpdf_offset_t prev,
  354 + bool suppress_offsets,
  355 + int hint_id,
  356 + qpdf_offset_t hint_offset,
  357 + qpdf_offset_t hint_length,
  358 + int linearization_pass);
  359 + qpdf_offset_t writeXRefStream(
  360 + int objid,
  361 + int max_id,
  362 + qpdf_offset_t max_offset,
  363 + trailer_e which,
  364 + int first,
  365 + int last,
  366 + int size);
  367 + qpdf_offset_t writeXRefStream(
  368 + int objid,
  369 + int max_id,
  370 + qpdf_offset_t max_offset,
  371 + trailer_e which,
  372 + int first,
  373 + int last,
  374 + int size,
  375 + // for linearization
  376 + qpdf_offset_t prev,
  377 + int hint_id,
  378 + qpdf_offset_t hint_offset,
  379 + qpdf_offset_t hint_length,
  380 + bool skip_compression,
  381 + int linearization_pass);
313 382  
314 383 private:
315 384 QPDFWriter& w;
... ... @@ -1047,7 +1116,7 @@ QPDFWriter::setDataKey(int objid)
1047 1116 }
1048 1117  
1049 1118 unsigned int
1050   -QPDFWriter::bytesNeeded(long long n)
  1119 +QPDFWriter::Members::bytesNeeded(long long n)
1051 1120 {
1052 1121 unsigned int bytes = 0;
1053 1122 while (n) {
... ... @@ -1058,7 +1127,7 @@ QPDFWriter::bytesNeeded(long long n)
1058 1127 }
1059 1128  
1060 1129 void
1061   -QPDFWriter::writeBinary(unsigned long long val, unsigned int bytes)
  1130 +QPDFWriter::Members::writeBinary(unsigned long long val, unsigned int bytes)
1062 1131 {
1063 1132 if (bytes > sizeof(unsigned long long)) {
1064 1133 throw std::logic_error("QPDFWriter::writeBinary called with too many bytes");
... ... @@ -1068,60 +1137,60 @@ QPDFWriter::writeBinary(unsigned long long val, unsigned int bytes)
1068 1137 data[bytes - i - 1] = static_cast<unsigned char>(val & 0xff);
1069 1138 val >>= 8;
1070 1139 }
1071   - m->pipeline->write(data, bytes);
  1140 + pipeline->write(data, bytes);
1072 1141 }
1073 1142  
1074   -QPDFWriter&
1075   -QPDFWriter::write(std::string_view str)
  1143 +QPDFWriter::Members&
  1144 +QPDFWriter::Members::write(std::string_view str)
1076 1145 {
1077   - m->pipeline->write(str);
  1146 + pipeline->write(str);
1078 1147 return *this;
1079 1148 }
1080 1149  
1081   -QPDFWriter&
1082   -QPDFWriter::write(std::integral auto val)
  1150 +QPDFWriter::Members&
  1151 +QPDFWriter::Members::write(std::integral auto val)
1083 1152 {
1084   - m->pipeline->write(std::to_string(val));
  1153 + pipeline->write(std::to_string(val));
1085 1154 return *this;
1086 1155 }
1087 1156  
1088   -QPDFWriter&
1089   -QPDFWriter::write(size_t count, char c)
  1157 +QPDFWriter::Members&
  1158 +QPDFWriter::Members::write(size_t count, char c)
1090 1159 {
1091   - m->pipeline->write(count, c);
  1160 + pipeline->write(count, c);
1092 1161 return *this;
1093 1162 }
1094 1163  
1095   -QPDFWriter&
1096   -QPDFWriter::write_name(std::string const& str)
  1164 +QPDFWriter::Members&
  1165 +QPDFWriter::Members::write_name(std::string const& str)
1097 1166 {
1098   - m->pipeline->write(Name::normalize(str));
  1167 + pipeline->write(Name::normalize(str));
1099 1168 return *this;
1100 1169 }
1101 1170  
1102   -QPDFWriter&
1103   -QPDFWriter::write_string(std::string const& str, bool force_binary)
  1171 +QPDFWriter::Members&
  1172 +QPDFWriter::Members::write_string(std::string const& str, bool force_binary)
1104 1173 {
1105   - m->pipeline->write(QPDF_String(str).unparse(force_binary));
  1174 + pipeline->write(QPDF_String(str).unparse(force_binary));
1106 1175 return *this;
1107 1176 }
1108 1177  
1109 1178 template <typename... Args>
1110   -QPDFWriter&
1111   -QPDFWriter::write_qdf(Args&&... args)
  1179 +QPDFWriter::Members&
  1180 +QPDFWriter::Members::write_qdf(Args&&... args)
1112 1181 {
1113   - if (m->qdf_mode) {
1114   - m->pipeline->write(std::forward<Args>(args)...);
  1182 + if (qdf_mode) {
  1183 + pipeline->write(std::forward<Args>(args)...);
1115 1184 }
1116 1185 return *this;
1117 1186 }
1118 1187  
1119 1188 template <typename... Args>
1120   -QPDFWriter&
1121   -QPDFWriter::write_no_qdf(Args&&... args)
  1189 +QPDFWriter::Members&
  1190 +QPDFWriter::Members::write_no_qdf(Args&&... args)
1122 1191 {
1123   - if (!m->qdf_mode) {
1124   - m->pipeline->write(std::forward<Args>(args)...);
  1192 + if (!qdf_mode) {
  1193 + pipeline->write(std::forward<Args>(args)...);
1125 1194 }
1126 1195 return *this;
1127 1196 }
... ... @@ -1136,15 +1205,15 @@ QPDFWriter::adjustAESStreamLength(size_t&amp; length)
1136 1205 }
1137 1206 }
1138 1207  
1139   -QPDFWriter&
1140   -QPDFWriter::write_encrypted(std::string_view str)
  1208 +QPDFWriter::Members&
  1209 +QPDFWriter::Members::write_encrypted(std::string_view str)
1141 1210 {
1142   - if (!(m->encryption && !m->cur_data_key.empty())) {
  1211 + if (!(encryption && !cur_data_key.empty())) {
1143 1212 write(str);
1144   - } else if (m->encrypt_use_aes) {
1145   - write(pl::pipe<Pl_AES_PDF>(str, true, m->cur_data_key));
  1213 + } else if (encrypt_use_aes) {
  1214 + write(pl::pipe<Pl_AES_PDF>(str, true, cur_data_key));
1146 1215 } else {
1147   - write(pl::pipe<Pl_RC4>(str, m->cur_data_key));
  1216 + write(pl::pipe<Pl_RC4>(str, cur_data_key));
1148 1217 }
1149 1218  
1150 1219 return *this;
... ... @@ -1163,23 +1232,23 @@ QPDFWriter::computeDeterministicIDData()
1163 1232 }
1164 1233  
1165 1234 int
1166   -QPDFWriter::openObject(int objid)
  1235 +QPDFWriter::Members::openObject(int objid)
1167 1236 {
1168 1237 if (objid == 0) {
1169   - objid = m->next_objid++;
  1238 + objid = next_objid++;
1170 1239 }
1171   - m->new_obj[objid].xref = QPDFXRefEntry(m->pipeline->getCount());
  1240 + new_obj[objid].xref = QPDFXRefEntry(pipeline->getCount());
1172 1241 write(objid).write(" 0 obj\n");
1173 1242 return objid;
1174 1243 }
1175 1244  
1176 1245 void
1177   -QPDFWriter::closeObject(int objid)
  1246 +QPDFWriter::Members::closeObject(int objid)
1178 1247 {
1179 1248 // Write a newline before endobj as it makes the file easier to repair.
1180 1249 write("\nendobj\n").write_qdf("\n");
1181   - auto& new_obj = m->new_obj[objid];
1182   - new_obj.length = m->pipeline->getCount() - new_obj.xref.getOffset();
  1250 + auto& no = new_obj[objid];
  1251 + no.length = pipeline->getCount() - no.xref.getOffset();
1183 1252 }
1184 1253  
1185 1254 void
... ... @@ -1268,25 +1337,25 @@ QPDFWriter::Members::enqueueObject(QPDFObjectHandle object)
1268 1337 }
1269 1338  
1270 1339 void
1271   -QPDFWriter::unparseChild(QPDFObjectHandle const& child, size_t level, int flags)
  1340 +QPDFWriter::Members::unparseChild(QPDFObjectHandle const& child, size_t level, int flags)
1272 1341 {
1273   - if (!m->linearized) {
1274   - m->enqueueObject(child);
  1342 + if (!linearized) {
  1343 + enqueueObject(child);
1275 1344 }
1276 1345 if (child.isIndirect()) {
1277   - write(m->obj[child].renumber).write(" 0 R");
  1346 + write(obj[child].renumber).write(" 0 R");
1278 1347 } else {
1279 1348 unparseObject(child, level, flags);
1280 1349 }
1281 1350 }
1282 1351  
1283 1352 void
1284   -QPDFWriter::writeTrailer(
  1353 +QPDFWriter::Members::writeTrailer(
1285 1354 trailer_e which, int size, bool xref_stream, qpdf_offset_t prev, int linearization_pass)
1286 1355 {
1287   - QPDFObjectHandle trailer = m->getTrimmedTrailer();
  1356 + QPDFObjectHandle trailer = getTrimmedTrailer();
1288 1357 if (xref_stream) {
1289   - m->cur_data_key.clear();
  1358 + cur_data_key.clear();
1290 1359 } else {
1291 1360 write("trailer <<");
1292 1361 }
... ... @@ -1303,8 +1372,8 @@ QPDFWriter::writeTrailer(
1303 1372 write(size);
1304 1373 if (which == t_lin_first) {
1305 1374 write(" /Prev ");
1306   - qpdf_offset_t pos = m->pipeline->getCount();
1307   - write(prev).write(QIntC::to_size(pos - m->pipeline->getCount() + 21), ' ');
  1375 + qpdf_offset_t pos = pipeline->getCount();
  1376 + write(prev).write(QIntC::to_size(pos - pipeline->getCount() + 21), ' ');
1308 1377 }
1309 1378 } else {
1310 1379 unparseChild(value, 1, 0);
... ... @@ -1316,7 +1385,7 @@ QPDFWriter::writeTrailer(
1316 1385 // Write ID
1317 1386 write_qdf(" ").write(" /ID [");
1318 1387 if (linearization_pass == 1) {
1319   - std::string original_id1 = m->getOriginalID1();
  1388 + std::string original_id1 = getOriginalID1();
1320 1389 if (original_id1.empty()) {
1321 1390 write("<00000000000000000000000000000000>");
1322 1391 } else {
... ... @@ -1329,18 +1398,18 @@ QPDFWriter::writeTrailer(
1329 1398 }
1330 1399 write("<00000000000000000000000000000000>");
1331 1400 } else {
1332   - if (linearization_pass == 0 && m->deterministic_id) {
1333   - computeDeterministicIDData();
  1401 + if (linearization_pass == 0 && deterministic_id) {
  1402 + w.computeDeterministicIDData();
1334 1403 }
1335   - m->generateID(m->encryption.get());
1336   - write_string(m->id1, true).write_string(m->id2, true);
  1404 + generateID(encryption.get());
  1405 + write_string(id1, true).write_string(id2, true);
1337 1406 }
1338 1407 write("]");
1339 1408  
1340 1409 if (which != t_lin_second) {
1341 1410 // Write reference to encryption dictionary
1342   - if (m->encryption) {
1343   - write(" /Encrypt ").write(m->encryption_dict_objid).write(" 0 R");
  1411 + if (encryption) {
  1412 + write(" /Encrypt ").write(encryption_dict_objid).write(" 0 R");
1344 1413 }
1345 1414 }
1346 1415  
... ... @@ -1442,7 +1511,7 @@ QPDFWriter::Members::willFilterStream(
1442 1511 }
1443 1512  
1444 1513 void
1445   -QPDFWriter::unparseObject(
  1514 +QPDFWriter::Members::unparseObject(
1446 1515 QPDFObjectHandle object, size_t level, int flags, size_t stream_length, bool compress)
1447 1516 {
1448 1517 QPDFObjGen old_og = object.getObjGen();
... ... @@ -1450,11 +1519,11 @@ QPDFWriter::unparseObject(
1450 1519 // For non-qdf, "indent" and "indent_large" are a single space between tokens. For qdf, they
1451 1520 // include the preceding newline.
1452 1521 std::string indent_large = " ";
1453   - if (m->qdf_mode) {
  1522 + if (qdf_mode) {
1454 1523 indent_large.append(2 * (level + 1), ' ');
1455 1524 indent_large[0] = '\n';
1456 1525 }
1457   - std::string_view indent{indent_large.data(), m->qdf_mode ? indent_large.size() - 2 : 1};
  1526 + std::string_view indent{indent_large.data(), qdf_mode ? indent_large.size() - 2 : 1};
1458 1527  
1459 1528 if (auto const tc = object.getTypeCode(); tc == ::ot_array) {
1460 1529 // Note: PDF spec 1.4 implementation note 121 states that Acrobat requires a space after the
... ... @@ -1469,7 +1538,7 @@ QPDFWriter::unparseObject(
1469 1538 } else if (tc == ::ot_dictionary) {
1470 1539 // Handle special cases for specific dictionaries.
1471 1540  
1472   - if (old_og == m->root_og) {
  1541 + if (old_og == root_og) {
1473 1542 // Extensions dictionaries.
1474 1543  
1475 1544 // We have one of several cases:
... ... @@ -1490,7 +1559,7 @@ QPDFWriter::unparseObject(
1490 1559  
1491 1560 auto extensions = object.getKey("/Extensions");
1492 1561 const bool has_extensions = extensions.isDictionary();
1493   - const bool need_extensions_adbe = m->final_extension_level > 0;
  1562 + const bool need_extensions_adbe = final_extension_level > 0;
1494 1563  
1495 1564 if (has_extensions || need_extensions_adbe) {
1496 1565 // Make a shallow copy of this object so we can modify it safely without affecting
... ... @@ -1510,7 +1579,7 @@ QPDFWriter::unparseObject(
1510 1579 if (need_extensions_adbe) {
1511 1580 if (!(have_extensions_other || have_extensions_adbe)) {
1512 1581 // We need Extensions and don't have it. Create it here.
1513   - QTC::TC("qpdf", "QPDFWriter create Extensions", m->qdf_mode ? 0 : 1);
  1582 + QTC::TC("qpdf", "QPDFWriter create Extensions", qdf_mode ? 0 : 1);
1514 1583 extensions = object.replaceKeyAndGetNew(
1515 1584 "/Extensions", QPDFObjectHandle::newDictionary());
1516 1585 }
... ... @@ -1527,21 +1596,17 @@ QPDFWriter::unparseObject(
1527 1596 QTC::TC("qpdf", "QPDFWriter preserve Extensions");
1528 1597 QPDFObjectHandle adbe = extensions.getKey("/ADBE");
1529 1598 if (adbe.isDictionary() &&
1530   - adbe.getKey("/BaseVersion").isNameAndEquals("/" + m->final_pdf_version) &&
  1599 + adbe.getKey("/BaseVersion").isNameAndEquals("/" + final_pdf_version) &&
1531 1600 adbe.getKey("/ExtensionLevel").isInteger() &&
1532   - (adbe.getKey("/ExtensionLevel").getIntValue() ==
1533   - m->final_extension_level)) {
1534   - QTC::TC("qpdf", "QPDFWriter preserve ADBE");
  1601 + (adbe.getKey("/ExtensionLevel").getIntValue() == final_extension_level)) {
1535 1602 } else {
1536 1603 if (need_extensions_adbe) {
1537 1604 extensions.replaceKey(
1538 1605 "/ADBE",
1539 1606 QPDFObjectHandle::parse(
1540   - "<< /BaseVersion /" + m->final_pdf_version +
1541   - " /ExtensionLevel " + std::to_string(m->final_extension_level) +
1542   - " >>"));
  1607 + "<< /BaseVersion /" + final_pdf_version + " /ExtensionLevel " +
  1608 + std::to_string(final_extension_level) + " >>"));
1543 1609 } else {
1544   - QTC::TC("qpdf", "QPDFWriter remove ADBE");
1545 1610 extensions.removeKey("/ADBE");
1546 1611 }
1547 1612 }
... ... @@ -1619,10 +1684,10 @@ QPDFWriter::unparseObject(
1619 1684 if (flags & f_stream) {
1620 1685 write(indent_large).write("/Length ");
1621 1686  
1622   - if (m->direct_stream_lengths) {
  1687 + if (direct_stream_lengths) {
1623 1688 write(stream_length);
1624 1689 } else {
1625   - write(m->cur_stream_length_id).write(" 0 R");
  1690 + write(cur_stream_length_id).write(" 0 R");
1626 1691 }
1627 1692 if (compress && (flags & f_filtered)) {
1628 1693 write(indent_large).write("/Filter /FlateDecode");
... ... @@ -1632,38 +1697,38 @@ QPDFWriter::unparseObject(
1632 1697 write(indent).write(">>");
1633 1698 } else if (tc == ::ot_stream) {
1634 1699 // Write stream data to a buffer.
1635   - if (!m->direct_stream_lengths) {
1636   - m->cur_stream_length_id = m->obj[old_og].renumber + 1;
  1700 + if (!direct_stream_lengths) {
  1701 + cur_stream_length_id = obj[old_og].renumber + 1;
1637 1702 }
1638 1703  
1639 1704 flags |= f_stream;
1640 1705 bool compress_stream = false;
1641 1706 bool is_metadata = false;
1642 1707 std::string stream_data;
1643   - if (m->willFilterStream(object, compress_stream, is_metadata, &stream_data)) {
  1708 + if (willFilterStream(object, compress_stream, is_metadata, &stream_data)) {
1644 1709 flags |= f_filtered;
1645 1710 }
1646 1711 QPDFObjectHandle stream_dict = object.getDict();
1647 1712  
1648   - m->cur_stream_length = stream_data.size();
1649   - if (is_metadata && m->encryption && !m->encryption->getEncryptMetadata()) {
  1713 + cur_stream_length = stream_data.size();
  1714 + if (is_metadata && encryption && !encryption->getEncryptMetadata()) {
1650 1715 // Don't encrypt stream data for the metadata stream
1651   - m->cur_data_key.clear();
  1716 + cur_data_key.clear();
1652 1717 }
1653   - adjustAESStreamLength(m->cur_stream_length);
1654   - unparseObject(stream_dict, 0, flags, m->cur_stream_length, compress_stream);
  1718 + w.adjustAESStreamLength(cur_stream_length);
  1719 + unparseObject(stream_dict, 0, flags, cur_stream_length, compress_stream);
1655 1720 char last_char = stream_data.empty() ? '\0' : stream_data.back();
1656 1721 write("\nstream\n").write_encrypted(stream_data);
1657   - m->added_newline = m->newline_before_endstream || (m->qdf_mode && last_char != '\n');
1658   - write(m->added_newline ? "\nendstream" : "endstream");
  1722 + added_newline = newline_before_endstream || (qdf_mode && last_char != '\n');
  1723 + write(added_newline ? "\nendstream" : "endstream");
1659 1724 } else if (tc == ::ot_string) {
1660 1725 std::string val;
1661   - if (m->encryption && !(flags & f_in_ostream) && !(flags & f_no_encryption) &&
1662   - !m->cur_data_key.empty()) {
  1726 + if (encryption && !(flags & f_in_ostream) && !(flags & f_no_encryption) &&
  1727 + !cur_data_key.empty()) {
1663 1728 val = object.getStringValue();
1664   - if (m->encrypt_use_aes) {
  1729 + if (encrypt_use_aes) {
1665 1730 Pl_Buffer bufpl("encrypted string");
1666   - Pl_AES_PDF pl("aes encrypt string", &bufpl, true, m->cur_data_key);
  1731 + Pl_AES_PDF pl("aes encrypt string", &bufpl, true, cur_data_key);
1667 1732 pl.writeString(val);
1668 1733 pl.finish();
1669 1734 val = QPDF_String(bufpl.getString()).unparse(true);
... ... @@ -1672,8 +1737,8 @@ QPDFWriter::unparseObject(
1672 1737 char* tmp = tmp_ph.get();
1673 1738 size_t vlen = val.length();
1674 1739 RC4 rc4(
1675   - QUtil::unsigned_char_pointer(m->cur_data_key),
1676   - QIntC::to_int(m->cur_data_key.length()));
  1740 + QUtil::unsigned_char_pointer(cur_data_key),
  1741 + QIntC::to_int(cur_data_key.length()));
1677 1742 auto data = QUtil::unsigned_char_pointer(tmp);
1678 1743 rc4.process(data, vlen, data);
1679 1744 val = QPDF_String(std::string(tmp, vlen)).unparse();
... ... @@ -1690,7 +1755,7 @@ QPDFWriter::unparseObject(
1690 1755 }
1691 1756  
1692 1757 void
1693   -QPDFWriter::writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj)
  1758 +QPDFWriter::Members::writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj)
1694 1759 {
1695 1760 qpdf_assert_debug(first_obj > 0);
1696 1761 bool is_first = true;
... ... @@ -1709,7 +1774,7 @@ QPDFWriter::writeObjectStreamOffsets(std::vector&lt;qpdf_offset_t&gt;&amp; offsets, int fi
1709 1774 }
1710 1775  
1711 1776 void
1712   -QPDFWriter::writeObjectStream(QPDFObjectHandle object)
  1777 +QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object)
1713 1778 {
1714 1779 // Note: object might be null if this is a place-holder for an object stream that we are
1715 1780 // generating from scratch.
... ... @@ -1717,7 +1782,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1717 1782 QPDFObjGen old_og = object.getObjGen();
1718 1783 qpdf_assert_debug(old_og.getGen() == 0);
1719 1784 int old_id = old_og.getObj();
1720   - int new_stream_id = m->obj[old_og].renumber;
  1785 + int new_stream_id = obj[old_og].renumber;
1721 1786  
1722 1787 std::vector<qpdf_offset_t> offsets;
1723 1788 qpdf_offset_t first = 0;
... ... @@ -1727,39 +1792,38 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1727 1792 std::string stream_buffer_pass1;
1728 1793 std::string stream_buffer_pass2;
1729 1794 int first_obj = -1;
1730   - const bool compressed = m->compress_streams && !m->qdf_mode;
  1795 + const bool compressed = compress_streams && !qdf_mode;
1731 1796 {
1732 1797 // Pass 1
1733   - auto pp_ostream_pass1 = m->pipeline_stack.activate(stream_buffer_pass1);
  1798 + auto pp_ostream_pass1 = pipeline_stack.activate(stream_buffer_pass1);
1734 1799  
1735 1800 int count = -1;
1736   - for (auto const& obj: m->object_stream_to_objects[old_id]) {
  1801 + for (auto const& og: object_stream_to_objects[old_id]) {
1737 1802 ++count;
1738   - int new_obj = m->obj[obj].renumber;
  1803 + int new_o = obj[og].renumber;
1739 1804 if (first_obj == -1) {
1740   - first_obj = new_obj;
  1805 + first_obj = new_o;
1741 1806 }
1742   - if (m->qdf_mode) {
1743   - write("%% Object stream: object ").write(new_obj).write(", index ").write(count);
1744   - if (!m->suppress_original_object_ids) {
1745   - write("; original object ID: ").write(obj.getObj());
  1807 + if (qdf_mode) {
  1808 + write("%% Object stream: object ").write(new_o).write(", index ").write(count);
  1809 + if (!suppress_original_object_ids) {
  1810 + write("; original object ID: ").write(og.getObj());
1746 1811 // For compatibility, only write the generation if non-zero. While object
1747 1812 // streams only allow objects with generation 0, if we are generating object
1748 1813 // streams, the old object could have a non-zero generation.
1749   - if (obj.getGen() != 0) {
1750   - QTC::TC("qpdf", "QPDFWriter original obj non-zero gen");
1751   - write(" ").write(obj.getGen());
  1814 + if (og.getGen() != 0) {
  1815 + write(" ").write(og.getGen());
1752 1816 }
1753 1817 }
1754 1818 write("\n");
1755 1819 }
1756 1820  
1757   - offsets.push_back(m->pipeline->getCount());
  1821 + offsets.push_back(pipeline->getCount());
1758 1822 // To avoid double-counting objects being written in object streams for progress
1759 1823 // reporting, decrement in pass 1.
1760   - indicateProgress(true, false);
  1824 + w.indicateProgress(true, false);
1761 1825  
1762   - QPDFObjectHandle obj_to_write = m->pdf.getObject(obj);
  1826 + QPDFObjectHandle obj_to_write = pdf.getObject(og);
1763 1827 if (obj_to_write.isStream()) {
1764 1828 // This condition occurred in a fuzz input. Ideally we should block it at parse
1765 1829 // time, but it's not clear to me how to construct a case for this.
... ... @@ -1768,7 +1832,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1768 1832 }
1769 1833 writeObject(obj_to_write, count);
1770 1834  
1771   - m->new_obj[new_obj].xref = QPDFXRefEntry(new_stream_id, count);
  1835 + new_obj[new_o].xref = QPDFXRefEntry(new_stream_id, count);
1772 1836 }
1773 1837 }
1774 1838 {
... ... @@ -1780,13 +1844,13 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1780 1844  
1781 1845 // Take one pass at writing pairs of numbers so we can get their size information
1782 1846 {
1783   - auto pp_discard = m->pipeline_stack.activate(true);
  1847 + auto pp_discard = pipeline_stack.activate(true);
1784 1848 writeObjectStreamOffsets(offsets, first_obj);
1785   - first += m->pipeline->getCount();
  1849 + first += pipeline->getCount();
1786 1850 }
1787 1851  
1788 1852 // Set up a stream to write the stream data into a buffer.
1789   - auto pp_ostream = m->pipeline_stack.activate(stream_buffer_pass2);
  1853 + auto pp_ostream = pipeline_stack.activate(stream_buffer_pass2);
1790 1854  
1791 1855 writeObjectStreamOffsets(offsets, first_obj);
1792 1856 write(stream_buffer_pass1);
... ... @@ -1799,10 +1863,10 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1799 1863  
1800 1864 // Write the object
1801 1865 openObject(new_stream_id);
1802   - setDataKey(new_stream_id);
  1866 + w.setDataKey(new_stream_id);
1803 1867 write("<<").write_qdf("\n ").write(" /Type /ObjStm").write_qdf("\n ");
1804 1868 size_t length = stream_buffer_pass2.size();
1805   - adjustAESStreamLength(length);
  1869 + w.adjustAESStreamLength(length);
1806 1870 write(" /Length ").write(length).write_qdf("\n ");
1807 1871 if (compressed) {
1808 1872 write(" /Filter /FlateDecode");
... ... @@ -1818,57 +1882,57 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1818 1882 }
1819 1883 }
1820 1884 write_qdf("\n").write_no_qdf(" ").write(">>\nstream\n").write_encrypted(stream_buffer_pass2);
1821   - if (m->encryption) {
  1885 + if (encryption) {
1822 1886 QTC::TC("qpdf", "QPDFWriter encrypt object stream");
1823 1887 }
1824   - write(m->newline_before_endstream ? "\nendstream" : "endstream");
1825   - m->cur_data_key.clear();
  1888 + write(newline_before_endstream ? "\nendstream" : "endstream");
  1889 + cur_data_key.clear();
1826 1890 closeObject(new_stream_id);
1827 1891 }
1828 1892  
1829 1893 void
1830   -QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index)
  1894 +QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_index)
1831 1895 {
1832 1896 QPDFObjGen old_og = object.getObjGen();
1833 1897  
1834 1898 if (object_stream_index == -1 && old_og.getGen() == 0 &&
1835   - m->object_stream_to_objects.contains(old_og.getObj())) {
  1899 + object_stream_to_objects.contains(old_og.getObj())) {
1836 1900 writeObjectStream(object);
1837 1901 return;
1838 1902 }
1839 1903  
1840   - indicateProgress(false, false);
1841   - auto new_id = m->obj[old_og].renumber;
1842   - if (m->qdf_mode) {
1843   - if (m->page_object_to_seq.contains(old_og)) {
1844   - write("%% Page ").write(m->page_object_to_seq[old_og]).write("\n");
  1904 + w.indicateProgress(false, false);
  1905 + auto new_id = obj[old_og].renumber;
  1906 + if (qdf_mode) {
  1907 + if (page_object_to_seq.contains(old_og)) {
  1908 + write("%% Page ").write(page_object_to_seq[old_og]).write("\n");
1845 1909 }
1846   - if (m->contents_to_page_seq.contains(old_og)) {
1847   - write("%% Contents for page ").write(m->contents_to_page_seq[old_og]).write("\n");
  1910 + if (contents_to_page_seq.contains(old_og)) {
  1911 + write("%% Contents for page ").write(contents_to_page_seq[old_og]).write("\n");
1848 1912 }
1849 1913 }
1850 1914 if (object_stream_index == -1) {
1851   - if (m->qdf_mode && (!m->suppress_original_object_ids)) {
  1915 + if (qdf_mode && !suppress_original_object_ids) {
1852 1916 write("%% Original object ID: ").write(object.getObjGen().unparse(' ')).write("\n");
1853 1917 }
1854 1918 openObject(new_id);
1855   - setDataKey(new_id);
  1919 + w.setDataKey(new_id);
1856 1920 unparseObject(object, 0, 0);
1857   - m->cur_data_key.clear();
  1921 + cur_data_key.clear();
1858 1922 closeObject(new_id);
1859 1923 } else {
1860 1924 unparseObject(object, 0, f_in_ostream);
1861 1925 write("\n");
1862 1926 }
1863 1927  
1864   - if (!m->direct_stream_lengths && object.isStream()) {
1865   - if (m->qdf_mode) {
1866   - if (m->added_newline) {
  1928 + if (!direct_stream_lengths && object.isStream()) {
  1929 + if (qdf_mode) {
  1930 + if (added_newline) {
1867 1931 write("%QDF: ignore_newline\n");
1868 1932 }
1869 1933 }
1870 1934 openObject(new_id + 1);
1871   - write(m->cur_stream_length);
  1935 + write(cur_stream_length);
1872 1936 closeObject(new_id + 1);
1873 1937 }
1874 1938 }
... ... @@ -2289,9 +2353,9 @@ QPDFWriter::write()
2289 2353 m->prepareFileForWrite();
2290 2354  
2291 2355 if (m->linearized) {
2292   - writeLinearized();
  2356 + m->writeLinearized();
2293 2357 } else {
2294   - writeStandard();
  2358 + m->writeStandard();
2295 2359 }
2296 2360  
2297 2361 m->pipeline->finish();
... ... @@ -2335,20 +2399,20 @@ QPDFWriter::Members::enqueuePart(std::vector&lt;QPDFObjectHandle&gt;&amp; part)
2335 2399 }
2336 2400  
2337 2401 void
2338   -QPDFWriter::writeEncryptionDictionary()
  2402 +QPDFWriter::Members::writeEncryptionDictionary()
2339 2403 {
2340   - m->encryption_dict_objid = openObject(m->encryption_dict_objid);
2341   - auto& enc = *m->encryption;
  2404 + encryption_dict_objid = openObject(encryption_dict_objid);
  2405 + auto& enc = *encryption;
2342 2406 auto const V = enc.getV();
2343 2407  
2344 2408 write("<<");
2345 2409 if (V >= 4) {
2346 2410 write(" /CF << /StdCF << /AuthEvent /DocOpen /CFM ");
2347   - write(m->encrypt_use_aes ? ((V < 5) ? "/AESV2" : "/AESV3") : "/V2");
  2411 + write(encrypt_use_aes ? ((V < 5) ? "/AESV2" : "/AESV3") : "/V2");
2348 2412 // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of
2349 2413 // MacOS won't open encrypted files without it.
2350 2414 write((V < 5) ? " /Length 16 >> >>" : " /Length 32 >> >>");
2351   - if (!m->encryption->getEncryptMetadata()) {
  2415 + if (!encryption->getEncryptMetadata()) {
2352 2416 write(" /EncryptMetadata false");
2353 2417 }
2354 2418 }
... ... @@ -2371,7 +2435,7 @@ QPDFWriter::writeEncryptionDictionary()
2371 2435 write(" /UE ").write_string(enc.getUE(), true);
2372 2436 }
2373 2437 write(" /V ").write(enc.getV()).write(" >>");
2374   - closeObject(m->encryption_dict_objid);
  2438 + closeObject(encryption_dict_objid);
2375 2439 }
2376 2440  
2377 2441 std::string
... ... @@ -2382,10 +2446,10 @@ QPDFWriter::getFinalVersion()
2382 2446 }
2383 2447  
2384 2448 void
2385   -QPDFWriter::writeHeader()
  2449 +QPDFWriter::Members::writeHeader()
2386 2450 {
2387   - write("%PDF-").write(m->final_pdf_version);
2388   - if (m->pclm) {
  2451 + write("%PDF-").write(final_pdf_version);
  2452 + if (pclm) {
2389 2453 // PCLm version
2390 2454 write("\n%PCLm 1.0\n");
2391 2455 } else {
... ... @@ -2402,16 +2466,16 @@ QPDFWriter::writeHeader()
2402 2466 }
2403 2467  
2404 2468 void
2405   -QPDFWriter::writeHintStream(int hint_id)
  2469 +QPDFWriter::Members::writeHintStream(int hint_id)
2406 2470 {
2407 2471 std::string hint_buffer;
2408 2472 int S = 0;
2409 2473 int O = 0;
2410   - bool compressed = m->compress_streams && !m->qdf_mode;
2411   - QPDF::Writer::generateHintStream(m->pdf, m->new_obj, m->obj, hint_buffer, S, O, compressed);
  2474 + bool compressed = compress_streams && !qdf_mode;
  2475 + QPDF::Writer::generateHintStream(pdf, new_obj, obj, hint_buffer, S, O, compressed);
2412 2476  
2413 2477 openObject(hint_id);
2414   - setDataKey(hint_id);
  2478 + w.setDataKey(hint_id);
2415 2479  
2416 2480 size_t hlen = hint_buffer.size();
2417 2481  
... ... @@ -2423,11 +2487,11 @@ QPDFWriter::writeHintStream(int hint_id)
2423 2487 if (O) {
2424 2488 write(" /O ").write(O);
2425 2489 }
2426   - adjustAESStreamLength(hlen);
  2490 + w.adjustAESStreamLength(hlen);
2427 2491 write(" /Length ").write(hlen);
2428 2492 write(" >>\nstream\n").write_encrypted(hint_buffer);
2429 2493  
2430   - if (m->encryption) {
  2494 + if (encryption) {
2431 2495 QTC::TC("qpdf", "QPDFWriter encrypted hint stream");
2432 2496 }
2433 2497  
... ... @@ -2436,7 +2500,7 @@ QPDFWriter::writeHintStream(int hint_id)
2436 2500 }
2437 2501  
2438 2502 qpdf_offset_t
2439   -QPDFWriter::writeXRefTable(trailer_e which, int first, int last, int size)
  2503 +QPDFWriter::Members::writeXRefTable(trailer_e which, int first, int last, int size)
2440 2504 {
2441 2505 // There are too many extra arguments to replace overloaded function with defaults in the header
2442 2506 // file...too much risk of leaving something off.
... ... @@ -2444,7 +2508,7 @@ QPDFWriter::writeXRefTable(trailer_e which, int first, int last, int size)
2444 2508 }
2445 2509  
2446 2510 qpdf_offset_t
2447   -QPDFWriter::writeXRefTable(
  2511 +QPDFWriter::Members::writeXRefTable(
2448 2512 trailer_e which,
2449 2513 int first,
2450 2514 int last,
... ... @@ -2457,7 +2521,7 @@ QPDFWriter::writeXRefTable(
2457 2521 int linearization_pass)
2458 2522 {
2459 2523 write("xref\n").write(first).write(" ").write(last - first + 1);
2460   - qpdf_offset_t space_before_zero = m->pipeline->getCount();
  2524 + qpdf_offset_t space_before_zero = pipeline->getCount();
2461 2525 write("\n");
2462 2526 if (first == 0) {
2463 2527 write("0000000000 65535 f \n");
... ... @@ -2466,7 +2530,7 @@ QPDFWriter::writeXRefTable(
2466 2530 for (int i = first; i <= last; ++i) {
2467 2531 qpdf_offset_t offset = 0;
2468 2532 if (!suppress_offsets) {
2469   - offset = m->new_obj[i].xref.getOffset();
  2533 + offset = new_obj[i].xref.getOffset();
2470 2534 if ((hint_id != 0) && (i != hint_id) && (offset >= hint_offset)) {
2471 2535 offset += hint_length;
2472 2536 }
... ... @@ -2479,7 +2543,7 @@ QPDFWriter::writeXRefTable(
2479 2543 }
2480 2544  
2481 2545 qpdf_offset_t
2482   -QPDFWriter::writeXRefStream(
  2546 +QPDFWriter::Members::writeXRefStream(
2483 2547 int objid, int max_id, qpdf_offset_t max_offset, trailer_e which, int first, int last, int size)
2484 2548 {
2485 2549 // There are too many extra arguments to replace overloaded function with defaults in the header
... ... @@ -2489,7 +2553,7 @@ QPDFWriter::writeXRefStream(
2489 2553 }
2490 2554  
2491 2555 qpdf_offset_t
2492   -QPDFWriter::writeXRefStream(
  2556 +QPDFWriter::Members::writeXRefStream(
2493 2557 int xref_id,
2494 2558 int max_id,
2495 2559 qpdf_offset_t max_offset,
... ... @@ -2504,28 +2568,28 @@ QPDFWriter::writeXRefStream(
2504 2568 bool skip_compression,
2505 2569 int linearization_pass)
2506 2570 {
2507   - qpdf_offset_t xref_offset = m->pipeline->getCount();
  2571 + qpdf_offset_t xref_offset = pipeline->getCount();
2508 2572 qpdf_offset_t space_before_zero = xref_offset - 1;
2509 2573  
2510 2574 // field 1 contains offsets and object stream identifiers
2511 2575 unsigned int f1_size = std::max(bytesNeeded(max_offset + hint_length), bytesNeeded(max_id));
2512 2576  
2513 2577 // field 2 contains object stream indices
2514   - unsigned int f2_size = bytesNeeded(QIntC::to_longlong(m->max_ostream_index));
  2578 + unsigned int f2_size = bytesNeeded(QIntC::to_longlong(max_ostream_index));
2515 2579  
2516 2580 unsigned int esize = 1 + f1_size + f2_size;
2517 2581  
2518 2582 // Must store in xref table in advance of writing the actual data rather than waiting for
2519 2583 // openObject to do it.
2520   - m->new_obj[xref_id].xref = QPDFXRefEntry(m->pipeline->getCount());
  2584 + new_obj[xref_id].xref = QPDFXRefEntry(pipeline->getCount());
2521 2585  
2522 2586 std::string xref_data;
2523   - const bool compressed = m->compress_streams && !m->qdf_mode;
  2587 + const bool compressed = compress_streams && !qdf_mode;
2524 2588 {
2525   - auto pp_xref = m->pipeline_stack.activate(xref_data);
  2589 + auto pp_xref = pipeline_stack.activate(xref_data);
2526 2590  
2527 2591 for (int i = first; i <= last; ++i) {
2528   - QPDFXRefEntry& e = m->new_obj[i].xref;
  2592 + QPDFXRefEntry& e = new_obj[i].xref;
2529 2593 switch (e.getType()) {
2530 2594 case 0:
2531 2595 writeBinary(0, 1);
... ... @@ -2597,7 +2661,7 @@ QPDFWriter::calculateXrefStreamPadding(qpdf_offset_t xref_bytes)
2597 2661 }
2598 2662  
2599 2663 void
2600   -QPDFWriter::writeLinearized()
  2664 +QPDFWriter::Members::writeLinearized()
2601 2665 {
2602 2666 // Optimize file and enqueue objects in order
2603 2667  
... ... @@ -2608,7 +2672,7 @@ QPDFWriter::writeLinearized()
2608 2672 if (result == 0) {
2609 2673 bool compress_stream;
2610 2674 bool is_metadata;
2611   - if (m->willFilterStream(stream, compress_stream, is_metadata, nullptr)) {
  2675 + if (willFilterStream(stream, compress_stream, is_metadata, nullptr)) {
2612 2676 result = 2;
2613 2677 } else {
2614 2678 result = 1;
... ... @@ -2617,14 +2681,14 @@ QPDFWriter::writeLinearized()
2617 2681 return result;
2618 2682 };
2619 2683  
2620   - QPDF::Writer::optimize(m->pdf, m->obj, skip_stream_parameters);
  2684 + QPDF::Writer::optimize(pdf, obj, skip_stream_parameters);
2621 2685  
2622 2686 std::vector<QPDFObjectHandle> part4;
2623 2687 std::vector<QPDFObjectHandle> part6;
2624 2688 std::vector<QPDFObjectHandle> part7;
2625 2689 std::vector<QPDFObjectHandle> part8;
2626 2690 std::vector<QPDFObjectHandle> part9;
2627   - QPDF::Writer::getLinearizedParts(m->pdf, m->obj, part4, part6, part7, part8, part9);
  2691 + QPDF::Writer::getLinearizedParts(pdf, obj, part4, part6, part7, part8, part9);
2628 2692  
2629 2693 // Object number sequence:
2630 2694 //
... ... @@ -2646,48 +2710,48 @@ QPDFWriter::writeLinearized()
2646 2710 int second_half_uncompressed = QIntC::to_int(part7.size() + part8.size() + part9.size());
2647 2711 int second_half_first_obj = 1;
2648 2712 int after_second_half = 1 + second_half_uncompressed;
2649   - m->next_objid = after_second_half;
  2713 + next_objid = after_second_half;
2650 2714 int second_half_xref = 0;
2651   - bool need_xref_stream = !m->obj.streams_empty;
  2715 + bool need_xref_stream = !obj.streams_empty;
2652 2716 if (need_xref_stream) {
2653   - second_half_xref = m->next_objid++;
  2717 + second_half_xref = next_objid++;
2654 2718 }
2655 2719 // Assign numbers to all compressed objects in the second half.
2656 2720 std::vector<QPDFObjectHandle>* vecs2[] = {&part7, &part8, &part9};
2657 2721 for (int i = 0; i < 3; ++i) {
2658 2722 for (auto const& oh: *vecs2[i]) {
2659   - m->assignCompressedObjectNumbers(oh.getObjGen());
  2723 + assignCompressedObjectNumbers(oh.getObjGen());
2660 2724 }
2661 2725 }
2662   - int second_half_end = m->next_objid - 1;
2663   - int second_trailer_size = m->next_objid;
  2726 + int second_half_end = next_objid - 1;
  2727 + int second_trailer_size = next_objid;
2664 2728  
2665 2729 // First half objects
2666   - int first_half_start = m->next_objid;
2667   - int lindict_id = m->next_objid++;
  2730 + int first_half_start = next_objid;
  2731 + int lindict_id = next_objid++;
2668 2732 int first_half_xref = 0;
2669 2733 if (need_xref_stream) {
2670   - first_half_xref = m->next_objid++;
  2734 + first_half_xref = next_objid++;
2671 2735 }
2672   - int part4_first_obj = m->next_objid;
2673   - m->next_objid += QIntC::to_int(part4.size());
2674   - int after_part4 = m->next_objid;
2675   - if (m->encryption) {
2676   - m->encryption_dict_objid = m->next_objid++;
  2736 + int part4_first_obj = next_objid;
  2737 + next_objid += QIntC::to_int(part4.size());
  2738 + int after_part4 = next_objid;
  2739 + if (encryption) {
  2740 + encryption_dict_objid = next_objid++;
2677 2741 }
2678   - int hint_id = m->next_objid++;
2679   - int part6_first_obj = m->next_objid;
2680   - m->next_objid += QIntC::to_int(part6.size());
2681   - int after_part6 = m->next_objid;
  2742 + int hint_id = next_objid++;
  2743 + int part6_first_obj = next_objid;
  2744 + next_objid += QIntC::to_int(part6.size());
  2745 + int after_part6 = next_objid;
2682 2746 // Assign numbers to all compressed objects in the first half
2683 2747 std::vector<QPDFObjectHandle>* vecs1[] = {&part4, &part6};
2684 2748 for (int i = 0; i < 2; ++i) {
2685 2749 for (auto const& oh: *vecs1[i]) {
2686   - m->assignCompressedObjectNumbers(oh.getObjGen());
  2750 + assignCompressedObjectNumbers(oh.getObjGen());
2687 2751 }
2688 2752 }
2689   - int first_half_end = m->next_objid - 1;
2690   - int first_trailer_size = m->next_objid;
  2753 + int first_half_end = next_objid - 1;
  2754 + int first_trailer_size = next_objid;
2691 2755  
2692 2756 int part4_end_marker = part4.back().getObjectID();
2693 2757 int part6_end_marker = part6.back().getObjectID();
... ... @@ -2699,23 +2763,23 @@ QPDFWriter::writeLinearized()
2699 2763 qpdf_offset_t first_xref_end = 0;
2700 2764 qpdf_offset_t second_xref_end = 0;
2701 2765  
2702   - m->next_objid = part4_first_obj;
2703   - m->enqueuePart(part4);
2704   - if (m->next_objid != after_part4) {
  2766 + next_objid = part4_first_obj;
  2767 + enqueuePart(part4);
  2768 + if (next_objid != after_part4) {
2705 2769 // This can happen with very botched files as in the fuzzer test. There are likely some
2706 2770 // faulty assumptions in calculateLinearizationData
2707 2771 throw std::runtime_error("error encountered after writing part 4 of linearized data");
2708 2772 }
2709   - m->next_objid = part6_first_obj;
2710   - m->enqueuePart(part6);
2711   - if (m->next_objid != after_part6) {
  2773 + next_objid = part6_first_obj;
  2774 + enqueuePart(part6);
  2775 + if (next_objid != after_part6) {
2712 2776 throw std::runtime_error("error encountered after writing part 6 of linearized data");
2713 2777 }
2714   - m->next_objid = second_half_first_obj;
2715   - m->enqueuePart(part7);
2716   - m->enqueuePart(part8);
2717   - m->enqueuePart(part9);
2718   - if (m->next_objid != after_second_half) {
  2778 + next_objid = second_half_first_obj;
  2779 + enqueuePart(part7);
  2780 + enqueuePart(part8);
  2781 + enqueuePart(part9);
  2782 + if (next_objid != after_second_half) {
2719 2783 throw std::runtime_error("error encountered after writing part 9 of linearized data");
2720 2784 }
2721 2785  
... ... @@ -2725,20 +2789,20 @@ QPDFWriter::writeLinearized()
2725 2789 // Write file in two passes. Part numbers refer to PDF spec 1.4.
2726 2790  
2727 2791 FILE* lin_pass1_file = nullptr;
2728   - auto pp_pass1 = m->pipeline_stack.popper();
2729   - auto pp_md5 = m->pipeline_stack.popper();
  2792 + auto pp_pass1 = pipeline_stack.popper();
  2793 + auto pp_md5 = pipeline_stack.popper();
2730 2794 for (int pass: {1, 2}) {
2731 2795 if (pass == 1) {
2732   - if (!m->lin_pass1_filename.empty()) {
2733   - lin_pass1_file = QUtil::safe_fopen(m->lin_pass1_filename.c_str(), "wb");
2734   - m->pipeline_stack.activate(
  2796 + if (!lin_pass1_filename.empty()) {
  2797 + lin_pass1_file = QUtil::safe_fopen(lin_pass1_filename.c_str(), "wb");
  2798 + pipeline_stack.activate(
2735 2799 pp_pass1,
2736 2800 std::make_unique<Pl_StdioFile>("linearization pass1", lin_pass1_file));
2737 2801 } else {
2738   - m->pipeline_stack.activate(pp_pass1, true);
  2802 + pipeline_stack.activate(pp_pass1, true);
2739 2803 }
2740   - if (m->deterministic_id) {
2741   - m->pipeline_stack.activate_md5(pp_md5);
  2804 + if (deterministic_id) {
  2805 + pipeline_stack.activate_md5(pp_md5);
2742 2806 }
2743 2807 }
2744 2808  
... ... @@ -2752,16 +2816,16 @@ QPDFWriter::writeLinearized()
2752 2816 // linearization parameter dictionary must appear within the first 1024 characters of the
2753 2817 // file.
2754 2818  
2755   - qpdf_offset_t pos = m->pipeline->getCount();
  2819 + qpdf_offset_t pos = pipeline->getCount();
2756 2820 openObject(lindict_id);
2757 2821 write("<<");
2758 2822 if (pass == 2) {
2759   - std::vector<QPDFObjectHandle> const& pages = m->pdf.getAllPages();
2760   - int first_page_object = m->obj[pages.at(0)].renumber;
  2823 + std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
  2824 + int first_page_object = obj[pages.at(0)].renumber;
2761 2825  
2762 2826 write(" /Linearized 1 /L ").write(file_size + hint_length);
2763 2827 // Implementation note 121 states that a space is mandatory after this open bracket.
2764   - write(" /H [ ").write(m->new_obj[hint_id].xref.getOffset()).write(" ");
  2828 + write(" /H [ ").write(new_obj[hint_id].xref.getOffset()).write(" ");
2765 2829 write(hint_length);
2766 2830 write(" ] /O ").write(first_page_object);
2767 2831 write(" /E ").write(part6_end_offset + hint_length);
... ... @@ -2771,18 +2835,18 @@ QPDFWriter::writeLinearized()
2771 2835 write(" >>");
2772 2836 closeObject(lindict_id);
2773 2837 static int const pad = 200;
2774   - write(QIntC::to_size(pos - m->pipeline->getCount() + pad), ' ').write("\n");
  2838 + write(QIntC::to_size(pos - pipeline->getCount() + pad), ' ').write("\n");
2775 2839  
2776 2840 // If the user supplied any additional header text, write it here after the linearization
2777 2841 // parameter dictionary.
2778   - write(m->extra_header_text);
  2842 + write(extra_header_text);
2779 2843  
2780 2844 // Part 3: first page cross reference table and trailer.
2781 2845  
2782   - qpdf_offset_t first_xref_offset = m->pipeline->getCount();
  2846 + qpdf_offset_t first_xref_offset = pipeline->getCount();
2783 2847 qpdf_offset_t hint_offset = 0;
2784 2848 if (pass == 2) {
2785   - hint_offset = m->new_obj[hint_id].xref.getOffset();
  2849 + hint_offset = new_obj[hint_id].xref.getOffset();
2786 2850 }
2787 2851 if (need_xref_stream) {
2788 2852 // Must pad here too.
... ... @@ -2794,7 +2858,7 @@ QPDFWriter::writeLinearized()
2794 2858 // value for this, but it's okay if it's smaller.
2795 2859 first_half_max_obj_offset = 1 << 25;
2796 2860 }
2797   - pos = m->pipeline->getCount();
  2861 + pos = pipeline->getCount();
2798 2862 writeXRefStream(
2799 2863 first_half_xref,
2800 2864 first_half_end,
... ... @@ -2809,16 +2873,16 @@ QPDFWriter::writeLinearized()
2809 2873 hint_length,
2810 2874 (pass == 1),
2811 2875 pass);
2812   - qpdf_offset_t endpos = m->pipeline->getCount();
  2876 + qpdf_offset_t endpos = pipeline->getCount();
2813 2877 if (pass == 1) {
2814 2878 // Pad so we have enough room for the real xref stream.
2815   - write(calculateXrefStreamPadding(endpos - pos), ' ');
2816   - first_xref_end = m->pipeline->getCount();
  2879 + write(w.calculateXrefStreamPadding(endpos - pos), ' ');
  2880 + first_xref_end = pipeline->getCount();
2817 2881 } else {
2818 2882 // Pad so that the next object starts at the same place as in pass 1.
2819 2883 write(QIntC::to_size(first_xref_end - endpos), ' ');
2820 2884  
2821   - if (m->pipeline->getCount() != first_xref_end) {
  2885 + if (pipeline->getCount() != first_xref_end) {
2822 2886 throw std::logic_error(
2823 2887 "insufficient padding for first pass xref stream; first_xref_end=" +
2824 2888 std::to_string(first_xref_end) + "; endpos=" + std::to_string(endpos));
... ... @@ -2842,24 +2906,24 @@ QPDFWriter::writeLinearized()
2842 2906  
2843 2907 // Parts 4 through 9
2844 2908  
2845   - for (auto const& cur_object: m->object_queue) {
  2909 + for (auto const& cur_object: object_queue) {
2846 2910 if (cur_object.getObjectID() == part6_end_marker) {
2847   - first_half_max_obj_offset = m->pipeline->getCount();
  2911 + first_half_max_obj_offset = pipeline->getCount();
2848 2912 }
2849 2913 writeObject(cur_object);
2850 2914 if (cur_object.getObjectID() == part4_end_marker) {
2851   - if (m->encryption) {
  2915 + if (encryption) {
2852 2916 writeEncryptionDictionary();
2853 2917 }
2854 2918 if (pass == 1) {
2855   - m->new_obj[hint_id].xref = QPDFXRefEntry(m->pipeline->getCount());
  2919 + new_obj[hint_id].xref = QPDFXRefEntry(pipeline->getCount());
2856 2920 } else {
2857 2921 // Part 5: hint stream
2858 2922 write(hint_buffer);
2859 2923 }
2860 2924 }
2861 2925 if (cur_object.getObjectID() == part6_end_marker) {
2862   - part6_end_offset = m->pipeline->getCount();
  2926 + part6_end_offset = pipeline->getCount();
2863 2927 }
2864 2928 }
2865 2929  
... ... @@ -2867,9 +2931,9 @@ QPDFWriter::writeLinearized()
2867 2931  
2868 2932 // Part 11: main cross reference table and trailer
2869 2933  
2870   - second_xref_offset = m->pipeline->getCount();
  2934 + second_xref_offset = pipeline->getCount();
2871 2935 if (need_xref_stream) {
2872   - pos = m->pipeline->getCount();
  2936 + pos = pipeline->getCount();
2873 2937 space_before_zero = writeXRefStream(
2874 2938 second_half_xref,
2875 2939 second_half_end,
... ... @@ -2884,21 +2948,21 @@ QPDFWriter::writeLinearized()
2884 2948 0,
2885 2949 (pass == 1),
2886 2950 pass);
2887   - qpdf_offset_t endpos = m->pipeline->getCount();
  2951 + qpdf_offset_t endpos = pipeline->getCount();
2888 2952  
2889 2953 if (pass == 1) {
2890 2954 // Pad so we have enough room for the real xref stream. See comments for previous
2891 2955 // xref stream on how we calculate the padding.
2892   - write(calculateXrefStreamPadding(endpos - pos), ' ').write("\n");
2893   - second_xref_end = m->pipeline->getCount();
  2956 + write(w.calculateXrefStreamPadding(endpos - pos), ' ').write("\n");
  2957 + second_xref_end = pipeline->getCount();
2894 2958 } else {
2895 2959 // Make the file size the same.
2896 2960 auto padding =
2897   - QIntC::to_size(second_xref_end + hint_length - 1 - m->pipeline->getCount());
  2961 + QIntC::to_size(second_xref_end + hint_length - 1 - pipeline->getCount());
2898 2962 write(padding, ' ').write("\n");
2899 2963  
2900 2964 // If this assertion fails, maybe we didn't have enough padding above.
2901   - if (m->pipeline->getCount() != second_xref_end + hint_length) {
  2965 + if (pipeline->getCount() != second_xref_end + hint_length) {
2902 2966 throw std::logic_error(
2903 2967 "count mismatch after xref stream; possible insufficient padding?");
2904 2968 }
... ... @@ -2910,28 +2974,28 @@ QPDFWriter::writeLinearized()
2910 2974 write("startxref\n").write(first_xref_offset).write("\n%%EOF\n");
2911 2975  
2912 2976 if (pass == 1) {
2913   - if (m->deterministic_id) {
  2977 + if (deterministic_id) {
2914 2978 QTC::TC("qpdf", "QPDFWriter linearized deterministic ID", need_xref_stream ? 0 : 1);
2915   - computeDeterministicIDData();
  2979 + w.computeDeterministicIDData();
2916 2980 pp_md5.pop();
2917 2981 }
2918 2982  
2919 2983 // Close first pass pipeline
2920   - file_size = m->pipeline->getCount();
  2984 + file_size = pipeline->getCount();
2921 2985 pp_pass1.pop();
2922 2986  
2923 2987 // Save hint offset since it will be set to zero by calling openObject.
2924   - qpdf_offset_t hint_offset1 = m->new_obj[hint_id].xref.getOffset();
  2988 + qpdf_offset_t hint_offset1 = new_obj[hint_id].xref.getOffset();
2925 2989  
2926 2990 // Write hint stream to a buffer
2927 2991 {
2928   - auto pp_hint = m->pipeline_stack.activate(hint_buffer);
  2992 + auto pp_hint = pipeline_stack.activate(hint_buffer);
2929 2993 writeHintStream(hint_id);
2930 2994 }
2931 2995 hint_length = QIntC::to_offset(hint_buffer.size());
2932 2996  
2933 2997 // Restore hint offset
2934   - m->new_obj[hint_id].xref = QPDFXRefEntry(hint_offset1);
  2998 + new_obj[hint_id].xref = QPDFXRefEntry(hint_offset1);
2935 2999 if (lin_pass1_file) {
2936 3000 // Write some debugging information
2937 3001 fprintf(
... ... @@ -3040,53 +3104,52 @@ QPDFWriter::registerProgressReporter(std::shared_ptr&lt;ProgressReporter&gt; pr)
3040 3104 }
3041 3105  
3042 3106 void
3043   -QPDFWriter::writeStandard()
  3107 +QPDFWriter::Members::writeStandard()
3044 3108 {
3045   - auto pp_md5 = m->pipeline_stack.popper();
3046   - if (m->deterministic_id) {
3047   - m->pipeline_stack.activate_md5(pp_md5);
  3109 + auto pp_md5 = pipeline_stack.popper();
  3110 + if (deterministic_id) {
  3111 + pipeline_stack.activate_md5(pp_md5);
3048 3112 }
3049 3113  
3050 3114 // Start writing
3051 3115  
3052 3116 writeHeader();
3053   - write(m->extra_header_text);
  3117 + write(extra_header_text);
3054 3118  
3055   - if (m->pclm) {
3056   - m->enqueueObjectsPCLm();
  3119 + if (pclm) {
  3120 + enqueueObjectsPCLm();
3057 3121 } else {
3058   - m->enqueueObjectsStandard();
  3122 + enqueueObjectsStandard();
3059 3123 }
3060 3124  
3061 3125 // Now start walking queue, outputting each object.
3062   - while (m->object_queue_front < m->object_queue.size()) {
3063   - QPDFObjectHandle cur_object = m->object_queue.at(m->object_queue_front);
3064   - ++m->object_queue_front;
  3126 + while (object_queue_front < object_queue.size()) {
  3127 + QPDFObjectHandle cur_object = object_queue.at(object_queue_front);
  3128 + ++object_queue_front;
3065 3129 writeObject(cur_object);
3066 3130 }
3067 3131  
3068 3132 // Write out the encryption dictionary, if any
3069   - if (m->encryption) {
  3133 + if (encryption) {
3070 3134 writeEncryptionDictionary();
3071 3135 }
3072 3136  
3073 3137 // Now write out xref. next_objid is now the number of objects.
3074   - qpdf_offset_t xref_offset = m->pipeline->getCount();
3075   - if (m->object_stream_to_objects.empty()) {
  3138 + qpdf_offset_t xref_offset = pipeline->getCount();
  3139 + if (object_stream_to_objects.empty()) {
3076 3140 // Write regular cross-reference table
3077   - writeXRefTable(t_normal, 0, m->next_objid - 1, m->next_objid);
  3141 + writeXRefTable(t_normal, 0, next_objid - 1, next_objid);
3078 3142 } else {
3079 3143 // Write cross-reference stream.
3080   - int xref_id = m->next_objid++;
3081   - writeXRefStream(
3082   - xref_id, xref_id, xref_offset, t_normal, 0, m->next_objid - 1, m->next_objid);
  3144 + int xref_id = next_objid++;
  3145 + writeXRefStream(xref_id, xref_id, xref_offset, t_normal, 0, next_objid - 1, next_objid);
3083 3146 }
3084 3147 write("startxref\n").write(xref_offset).write("\n%%EOF\n");
3085 3148  
3086   - if (m->deterministic_id) {
  3149 + if (deterministic_id) {
3087 3150 QTC::TC(
3088 3151 "qpdf",
3089 3152 "QPDFWriter standard deterministic ID",
3090   - m->object_stream_to_objects.empty() ? 0 : 1);
  3153 + object_stream_to_objects.empty() ? 0 : 1);
3091 3154 }
3092 3155 }
... ...
qpdf/qpdf.testcov
... ... @@ -198,9 +198,7 @@ QPDFWriter make Extensions direct 0
198 198 QPDFWriter make ADBE direct 1
199 199 QPDFWriter preserve Extensions 0
200 200 QPDFWriter create Extensions 1
201   -QPDFWriter remove ADBE 0
202 201 QPDFWriter remove existing Extensions 0
203   -QPDFWriter preserve ADBE 0
204 202 QPDF_encryption skip 0x28 0
205 203 qpdf-c called qpdf_get_pdf_extension_level 0
206 204 qpdf-c called qpdf_set_r5_encryption_parameters 0
... ... @@ -208,7 +206,6 @@ qpdf-c called qpdf_set_r6_encryption_parameters 0
208 206 QPDFObjectHandle EOF in inline image 0
209 207 QPDFObjectHandle inline image token 0
210 208 QPDF not caching overridden objstm object 0
211   -QPDFWriter original obj non-zero gen 0
212 209 QPDF_optimization indirect outlines 0
213 210 QPDF xref space 2
214 211 QPDFJob pages range omitted in middle 0
... ...