Commit 485399c71403f49f4becd625b0df6e17ce59f439

Authored by m-holger
1 parent 7d1ba7da

Refactor `QPDFWriter` to consolidate `write*` methods into parameterized `write`…

… overloads, streamline QDF handling, and simplify string and trailer writes.
include/qpdf/QPDFWriter.hh
@@ -24,6 +24,7 @@ @@ -24,6 +24,7 @@
24 #include <qpdf/Types.h> 24 #include <qpdf/Types.h>
25 25
26 #include <bitset> 26 #include <bitset>
  27 +#include <concepts>
27 #include <cstdio> 28 #include <cstdio>
28 #include <functional> 29 #include <functional>
29 #include <list> 30 #include <list>
@@ -481,10 +482,13 @@ class QPDFWriter @@ -481,10 +482,13 @@ class QPDFWriter
481 482
482 unsigned int bytesNeeded(long long n); 483 unsigned int bytesNeeded(long long n);
483 void writeBinary(unsigned long long val, unsigned int bytes); 484 void writeBinary(unsigned long long val, unsigned int bytes);
484 - void writeString(std::string_view str);  
485 - void writeStringQDF(std::string_view str);  
486 - void writeStringNoQDF(std::string_view str);  
487 - void writePad(size_t nspaces); 485 + QPDFWriter& write(std::string_view str);
  486 + QPDFWriter& write(size_t count, char c);
  487 + QPDFWriter& write(std::integral auto val);
  488 + template <typename... Args>
  489 + QPDFWriter& write_qdf(Args&&... args);
  490 + template <typename... Args>
  491 + QPDFWriter& write_no_qdf(Args&&... args);
488 void assignCompressedObjectNumbers(QPDFObjGen og); 492 void assignCompressedObjectNumbers(QPDFObjGen og);
489 void enqueueObject(QPDFObjectHandle object); 493 void enqueueObject(QPDFObjectHandle object);
490 void writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj); 494 void writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj);
libqpdf/QPDFWriter.cc
@@ -832,32 +832,45 @@ QPDFWriter::writeBinary(unsigned long long val, unsigned int bytes) @@ -832,32 +832,45 @@ QPDFWriter::writeBinary(unsigned long long val, unsigned int bytes)
832 m->pipeline->write(data, bytes); 832 m->pipeline->write(data, bytes);
833 } 833 }
834 834
835 -void  
836 -QPDFWriter::writeString(std::string_view str) 835 +QPDFWriter&
  836 +QPDFWriter::write(std::string_view str)
837 { 837 {
838 - m->pipeline->write(reinterpret_cast<unsigned char const*>(str.data()), str.size()); 838 + m->pipeline->write(str);
  839 + return *this;
839 } 840 }
840 841
841 -void  
842 -QPDFWriter::writeStringQDF(std::string_view str) 842 +QPDFWriter&
  843 +QPDFWriter::write(std::integral auto val)
843 { 844 {
844 - if (m->qdf_mode) {  
845 - m->pipeline->write(reinterpret_cast<unsigned char const*>(str.data()), str.size());  
846 - } 845 + m->pipeline->write(std::to_string(val));
  846 + return *this;
847 } 847 }
848 848
849 -void  
850 -QPDFWriter::writeStringNoQDF(std::string_view str) 849 +QPDFWriter&
  850 +QPDFWriter::write(size_t count, char c)
851 { 851 {
852 - if (!m->qdf_mode) {  
853 - m->pipeline->write(reinterpret_cast<unsigned char const*>(str.data()), str.size()); 852 + m->pipeline->write(count, c);
  853 + return *this;
  854 +}
  855 +
  856 +template <typename... Args>
  857 +QPDFWriter&
  858 +QPDFWriter::write_qdf(Args&&... args)
  859 +{
  860 + if (m->qdf_mode) {
  861 + m->pipeline->write(std::forward<Args>(args)...);
854 } 862 }
  863 + return *this;
855 } 864 }
856 865
857 -void  
858 -QPDFWriter::writePad(size_t nspaces) 866 +template <typename... Args>
  867 +QPDFWriter&
  868 +QPDFWriter::write_no_qdf(Args&&... args)
859 { 869 {
860 - writeString(std::string(nspaces, ' ')); 870 + if (!m->qdf_mode) {
  871 + m->pipeline->write(std::forward<Args>(args)...);
  872 + }
  873 + return *this;
861 } 874 }
862 875
863 Pipeline* 876 Pipeline*
@@ -1005,8 +1018,7 @@ QPDFWriter::openObject(int objid) @@ -1005,8 +1018,7 @@ QPDFWriter::openObject(int objid)
1005 objid = m->next_objid++; 1018 objid = m->next_objid++;
1006 } 1019 }
1007 m->new_obj[objid].xref = QPDFXRefEntry(m->pipeline->getCount()); 1020 m->new_obj[objid].xref = QPDFXRefEntry(m->pipeline->getCount());
1008 - writeString(std::to_string(objid));  
1009 - writeString(" 0 obj\n"); 1021 + write(objid).write(" 0 obj\n");
1010 return objid; 1022 return objid;
1011 } 1023 }
1012 1024
@@ -1014,8 +1026,7 @@ void @@ -1014,8 +1026,7 @@ void
1014 QPDFWriter::closeObject(int objid) 1026 QPDFWriter::closeObject(int objid)
1015 { 1027 {
1016 // Write a newline before endobj as it makes the file easier to repair. 1028 // Write a newline before endobj as it makes the file easier to repair.
1017 - writeString("\nendobj\n");  
1018 - writeStringQDF("\n"); 1029 + write("\nendobj\n").write_qdf("\n");
1019 auto& new_obj = m->new_obj[objid]; 1030 auto& new_obj = m->new_obj[objid];
1020 new_obj.length = m->pipeline->getCount() - new_obj.xref.getOffset(); 1031 new_obj.length = m->pipeline->getCount() - new_obj.xref.getOffset();
1021 } 1032 }
@@ -1114,8 +1125,7 @@ QPDFWriter::unparseChild(QPDFObjectHandle child, int level, int flags) @@ -1114,8 +1125,7 @@ QPDFWriter::unparseChild(QPDFObjectHandle child, int level, int flags)
1114 enqueueObject(child); 1125 enqueueObject(child);
1115 } 1126 }
1116 if (child.isIndirect()) { 1127 if (child.isIndirect()) {
1117 - writeString(std::to_string(m->obj[child].renumber));  
1118 - writeString(" 0 R"); 1128 + write(m->obj[child].renumber).write(" 0 R");
1119 } else { 1129 } else {
1120 unparseObject(child, level, flags); 1130 unparseObject(child, level, flags);
1121 } 1131 }
@@ -1129,78 +1139,63 @@ QPDFWriter::writeTrailer( @@ -1129,78 +1139,63 @@ QPDFWriter::writeTrailer(
1129 if (xref_stream) { 1139 if (xref_stream) {
1130 m->cur_data_key.clear(); 1140 m->cur_data_key.clear();
1131 } else { 1141 } else {
1132 - writeString("trailer <<"); 1142 + write("trailer <<");
1133 } 1143 }
1134 - writeStringQDF("\n"); 1144 + write_qdf("\n");
1135 if (which == t_lin_second) { 1145 if (which == t_lin_second) {
1136 - writeString(" /Size ");  
1137 - writeString(std::to_string(size)); 1146 + write(" /Size ").write(size);
1138 } else { 1147 } else {
1139 for (auto const& [key, value]: trailer.as_dictionary()) { 1148 for (auto const& [key, value]: trailer.as_dictionary()) {
1140 if (value.null()) { 1149 if (value.null()) {
1141 continue; 1150 continue;
1142 } 1151 }
1143 - writeStringQDF(" ");  
1144 - writeStringNoQDF(" ");  
1145 - writeString(Name::normalize(key));  
1146 - writeString(" "); 1152 + write_qdf(" ").write_no_qdf(" ").write(Name::normalize(key)).write(" ");
1147 if (key == "/Size") { 1153 if (key == "/Size") {
1148 - writeString(std::to_string(size)); 1154 + write(size);
1149 if (which == t_lin_first) { 1155 if (which == t_lin_first) {
1150 - writeString(" /Prev "); 1156 + write(" /Prev ");
1151 qpdf_offset_t pos = m->pipeline->getCount(); 1157 qpdf_offset_t pos = m->pipeline->getCount();
1152 - writeString(std::to_string(prev));  
1153 - writePad(QIntC::to_size(pos - m->pipeline->getCount() + 21)); 1158 + write(prev).write(QIntC::to_size(pos - m->pipeline->getCount() + 21), ' ');
1154 } 1159 }
1155 } else { 1160 } else {
1156 unparseChild(value, 1, 0); 1161 unparseChild(value, 1, 0);
1157 } 1162 }
1158 - writeStringQDF("\n"); 1163 + write_qdf("\n");
1159 } 1164 }
1160 } 1165 }
1161 1166
1162 // Write ID 1167 // Write ID
1163 - writeStringQDF(" ");  
1164 - writeString(" /ID ["); 1168 + write_qdf(" ").write(" /ID [");
1165 if (linearization_pass == 1) { 1169 if (linearization_pass == 1) {
1166 std::string original_id1 = getOriginalID1(); 1170 std::string original_id1 = getOriginalID1();
1167 if (original_id1.empty()) { 1171 if (original_id1.empty()) {
1168 - writeString("<00000000000000000000000000000000>"); 1172 + write("<00000000000000000000000000000000>");
1169 } else { 1173 } else {
1170 // Write a string of zeroes equal in length to the representation of the original ID. 1174 // Write a string of zeroes equal in length to the representation of the original ID.
1171 // While writing the original ID would have the same number of bytes, it would cause a 1175 // While writing the original ID would have the same number of bytes, it would cause a
1172 // change to the deterministic ID generated by older versions of the software that 1176 // change to the deterministic ID generated by older versions of the software that
1173 // hard-coded the length of the ID to 16 bytes. 1177 // hard-coded the length of the ID to 16 bytes.
1174 - writeString("<");  
1175 size_t len = QPDF_String(original_id1).unparse(true).length() - 2; 1178 size_t len = QPDF_String(original_id1).unparse(true).length() - 2;
1176 - for (size_t i = 0; i < len; ++i) {  
1177 - writeString("0");  
1178 - }  
1179 - writeString(">"); 1179 + write("<").write(len, '0').write(">");
1180 } 1180 }
1181 - writeString("<00000000000000000000000000000000>"); 1181 + write("<00000000000000000000000000000000>");
1182 } else { 1182 } else {
1183 - if ((linearization_pass == 0) && (m->deterministic_id)) { 1183 + if (linearization_pass == 0 && m->deterministic_id) {
1184 computeDeterministicIDData(); 1184 computeDeterministicIDData();
1185 } 1185 }
1186 generateID(); 1186 generateID();
1187 - writeString(QPDF_String(m->id1).unparse(true));  
1188 - writeString(QPDF_String(m->id2).unparse(true)); 1187 + write(QPDF_String(m->id1).unparse(true)).write(QPDF_String(m->id2).unparse(true));
1189 } 1188 }
1190 - writeString("]"); 1189 + write("]");
1191 1190
1192 if (which != t_lin_second) { 1191 if (which != t_lin_second) {
1193 // Write reference to encryption dictionary 1192 // Write reference to encryption dictionary
1194 if (m->encryption) { 1193 if (m->encryption) {
1195 - writeString(" /Encrypt ");  
1196 - writeString(std::to_string(m->encryption_dict_objid));  
1197 - writeString(" 0 R"); 1194 + write(" /Encrypt ").write(m->encryption_dict_objid).write(" 0 R");
1198 } 1195 }
1199 } 1196 }
1200 1197
1201 - writeStringQDF("\n");  
1202 - writeStringNoQDF(" ");  
1203 - writeString(">>"); 1198 + write_qdf("\n>>").write_no_qdf(" >>");
1204 } 1199 }
1205 1200
1206 bool 1201 bool
@@ -1326,14 +1321,12 @@ QPDFWriter::unparseObject( @@ -1326,14 +1321,12 @@ QPDFWriter::unparseObject(
1326 // Note: PDF spec 1.4 implementation note 121 states that Acrobat requires a space after the 1321 // Note: PDF spec 1.4 implementation note 121 states that Acrobat requires a space after the
1327 // [ in the /H key of the linearization parameter dictionary. We'll do this unconditionally 1322 // [ in the /H key of the linearization parameter dictionary. We'll do this unconditionally
1328 // for all arrays because it looks nicer and doesn't make the files that much bigger. 1323 // for all arrays because it looks nicer and doesn't make the files that much bigger.
1329 - writeString("["); 1324 + write("[");
1330 for (auto const& item: object.as_array()) { 1325 for (auto const& item: object.as_array()) {
1331 - writeString(indent);  
1332 - writeStringQDF(" "); 1326 + write(indent).write_qdf(" ");
1333 unparseChild(item, level + 1, child_flags); 1327 unparseChild(item, level + 1, child_flags);
1334 } 1328 }
1335 - writeString(indent);  
1336 - writeString("]"); 1329 + write(indent).write("]");
1337 } else if (tc == ::ot_dictionary) { 1330 } else if (tc == ::ot_dictionary) {
1338 // Handle special cases for specific dictionaries. 1331 // Handle special cases for specific dictionaries.
1339 1332
@@ -1474,14 +1467,11 @@ QPDFWriter::unparseObject( @@ -1474,14 +1467,11 @@ QPDFWriter::unparseObject(
1474 } 1467 }
1475 } 1468 }
1476 1469
1477 - writeString("<<"); 1470 + write("<<");
1478 1471
1479 for (auto const& [key, value]: object.as_dictionary()) { 1472 for (auto const& [key, value]: object.as_dictionary()) {
1480 if (!value.null()) { 1473 if (!value.null()) {
1481 - writeString(indent);  
1482 - writeStringQDF(" ");  
1483 - writeString(Name::normalize(key));  
1484 - writeString(" "); 1474 + write(indent).write_qdf(" ").write(Name::normalize(key)).write(" ");
1485 if (key == "/Contents" && object.isDictionaryOfType("/Sig") && 1475 if (key == "/Contents" && object.isDictionaryOfType("/Sig") &&
1486 object.hasKey("/ByteRange")) { 1476 object.hasKey("/ByteRange")) {
1487 QTC::TC("qpdf", "QPDFWriter no encryption sig contents"); 1477 QTC::TC("qpdf", "QPDFWriter no encryption sig contents");
@@ -1493,25 +1483,19 @@ QPDFWriter::unparseObject( @@ -1493,25 +1483,19 @@ QPDFWriter::unparseObject(
1493 } 1483 }
1494 1484
1495 if (flags & f_stream) { 1485 if (flags & f_stream) {
1496 - writeString(indent);  
1497 - writeStringQDF(" ");  
1498 - writeString("/Length "); 1486 + write(indent).write_qdf(" ").write("/Length ");
1499 1487
1500 if (m->direct_stream_lengths) { 1488 if (m->direct_stream_lengths) {
1501 - writeString(std::to_string(stream_length)); 1489 + write(stream_length);
1502 } else { 1490 } else {
1503 - writeString(std::to_string(m->cur_stream_length_id));  
1504 - writeString(" 0 R"); 1491 + write(m->cur_stream_length_id).write(" 0 R");
1505 } 1492 }
1506 if (compress && (flags & f_filtered)) { 1493 if (compress && (flags & f_filtered)) {
1507 - writeString(indent);  
1508 - writeStringQDF(" ");  
1509 - writeString("/Filter /FlateDecode"); 1494 + write(indent).write_qdf(" ").write("/Filter /FlateDecode");
1510 } 1495 }
1511 } 1496 }
1512 1497
1513 - writeString(indent);  
1514 - writeString(">>"); 1498 + write(indent).write(">>");
1515 } else if (tc == ::ot_stream) { 1499 } else if (tc == ::ot_stream) {
1516 // Write stream data to a buffer. 1500 // Write stream data to a buffer.
1517 if (!m->direct_stream_lengths) { 1501 if (!m->direct_stream_lengths) {
@@ -1535,18 +1519,18 @@ QPDFWriter::unparseObject( @@ -1535,18 +1519,18 @@ QPDFWriter::unparseObject(
1535 adjustAESStreamLength(m->cur_stream_length); 1519 adjustAESStreamLength(m->cur_stream_length);
1536 unparseObject(stream_dict, 0, flags, m->cur_stream_length, compress_stream); 1520 unparseObject(stream_dict, 0, flags, m->cur_stream_length, compress_stream);
1537 char last_char = stream_data.empty() ? '\0' : stream_data.back(); 1521 char last_char = stream_data.empty() ? '\0' : stream_data.back();
1538 - writeString("\nstream\n"); 1522 + write("\nstream\n");
1539 { 1523 {
1540 PipelinePopper pp_enc(this); 1524 PipelinePopper pp_enc(this);
1541 pushEncryptionFilter(pp_enc); 1525 pushEncryptionFilter(pp_enc);
1542 - writeString(stream_data); 1526 + write(stream_data);
1543 } 1527 }
1544 1528
1545 if ((m->added_newline = 1529 if ((m->added_newline =
1546 m->newline_before_endstream || (m->qdf_mode && last_char != '\n'))) { 1530 m->newline_before_endstream || (m->qdf_mode && last_char != '\n'))) {
1547 - writeString("\nendstream"); 1531 + write("\nendstream");
1548 } else { 1532 } else {
1549 - writeString("endstream"); 1533 + write("endstream");
1550 } 1534 }
1551 } else if (tc == ::ot_string) { 1535 } else if (tc == ::ot_string) {
1552 std::string val; 1536 std::string val;
@@ -1580,9 +1564,9 @@ QPDFWriter::unparseObject( @@ -1580,9 +1564,9 @@ QPDFWriter::unparseObject(
1580 } else { 1564 } else {
1581 val = object.unparseResolved(); 1565 val = object.unparseResolved();
1582 } 1566 }
1583 - writeString(val); 1567 + write(val);
1584 } else { 1568 } else {
1585 - writeString(object.unparseResolved()); 1569 + write(object.unparseResolved());
1586 } 1570 }
1587 } 1571 }
1588 1572
@@ -1596,14 +1580,13 @@ QPDFWriter::writeObjectStreamOffsets(std::vector&lt;qpdf_offset_t&gt;&amp; offsets, int fi @@ -1596,14 +1580,13 @@ QPDFWriter::writeObjectStreamOffsets(std::vector&lt;qpdf_offset_t&gt;&amp; offsets, int fi
1596 if (is_first) { 1580 if (is_first) {
1597 is_first = false; 1581 is_first = false;
1598 } else { 1582 } else {
1599 - writeStringQDF("\n");  
1600 - writeStringNoQDF(" "); 1583 + write_qdf("\n").write_no_qdf(" ");
1601 } 1584 }
1602 - writeString(id); 1585 + write(id);
1603 util::increment(id, 1); 1586 util::increment(id, 1);
1604 - writeString(std::to_string(offset)); 1587 + write(offset);
1605 } 1588 }
1606 - writeString("\n"); 1589 + write("\n");
1607 } 1590 }
1608 1591
1609 void 1592 void
@@ -1639,20 +1622,18 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1639,20 +1622,18 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1639 first_obj = new_obj; 1622 first_obj = new_obj;
1640 } 1623 }
1641 if (m->qdf_mode) { 1624 if (m->qdf_mode) {
1642 - writeString(  
1643 - "%% Object stream: object " + std::to_string(new_obj) + ", index " +  
1644 - std::to_string(count)); 1625 + write("%% Object stream: object ").write(new_obj).write(", index ").write(count);
1645 if (!m->suppress_original_object_ids) { 1626 if (!m->suppress_original_object_ids) {
1646 - writeString("; original object ID: " + std::to_string(obj.getObj())); 1627 + write("; original object ID: ").write(obj.getObj());
1647 // For compatibility, only write the generation if non-zero. While object 1628 // For compatibility, only write the generation if non-zero. While object
1648 // streams only allow objects with generation 0, if we are generating object 1629 // streams only allow objects with generation 0, if we are generating object
1649 // streams, the old object could have a non-zero generation. 1630 // streams, the old object could have a non-zero generation.
1650 if (obj.getGen() != 0) { 1631 if (obj.getGen() != 0) {
1651 QTC::TC("qpdf", "QPDFWriter original obj non-zero gen"); 1632 QTC::TC("qpdf", "QPDFWriter original obj non-zero gen");
1652 - writeString(" " + std::to_string(obj.getGen())); 1633 + write(" ").write(obj.getGen());
1653 } 1634 }
1654 } 1635 }
1655 - writeString("\n"); 1636 + write("\n");
1656 } 1637 }
1657 1638
1658 offsets.push_back(m->pipeline->getCount()); 1639 offsets.push_back(m->pipeline->getCount());
@@ -1698,7 +1679,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1698,7 +1679,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1698 activatePipelineStack(pp_ostream, stream_buffer_pass2); 1679 activatePipelineStack(pp_ostream, stream_buffer_pass2);
1699 } 1680 }
1700 writeObjectStreamOffsets(offsets, first_obj); 1681 writeObjectStreamOffsets(offsets, first_obj);
1701 - writeString(stream_buffer_pass1); 1682 + write(stream_buffer_pass1);
1702 stream_buffer_pass1.clear(); 1683 stream_buffer_pass1.clear();
1703 stream_buffer_pass1.shrink_to_fit(); 1684 stream_buffer_pass1.shrink_to_fit();
1704 } 1685 }
@@ -1706,46 +1687,37 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1706,46 +1687,37 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1706 // Write the object 1687 // Write the object
1707 openObject(new_stream_id); 1688 openObject(new_stream_id);
1708 setDataKey(new_stream_id); 1689 setDataKey(new_stream_id);
1709 - writeString("<<");  
1710 - writeStringQDF("\n ");  
1711 - writeString(" /Type /ObjStm");  
1712 - writeStringQDF("\n "); 1690 + write("<<").write_qdf("\n ").write(" /Type /ObjStm").write_qdf("\n ");
1713 size_t length = stream_buffer_pass2.size(); 1691 size_t length = stream_buffer_pass2.size();
1714 adjustAESStreamLength(length); 1692 adjustAESStreamLength(length);
1715 - writeString(" /Length " + std::to_string(length));  
1716 - writeStringQDF("\n "); 1693 + write(" /Length ").write(length).write_qdf("\n ");
1717 if (compressed) { 1694 if (compressed) {
1718 - writeString(" /Filter /FlateDecode"); 1695 + write(" /Filter /FlateDecode");
1719 } 1696 }
1720 - writeString(" /N " + std::to_string(offsets.size()));  
1721 - writeStringQDF("\n ");  
1722 - writeString(" /First " + std::to_string(first)); 1697 + write(" /N ").write(offsets.size()).write_qdf("\n ").write(" /First ").write(first);
1723 if (!object.isNull()) { 1698 if (!object.isNull()) {
1724 // If the original object has an /Extends key, preserve it. 1699 // If the original object has an /Extends key, preserve it.
1725 QPDFObjectHandle dict = object.getDict(); 1700 QPDFObjectHandle dict = object.getDict();
1726 QPDFObjectHandle extends = dict.getKey("/Extends"); 1701 QPDFObjectHandle extends = dict.getKey("/Extends");
1727 if (extends.isIndirect()) { 1702 if (extends.isIndirect()) {
1728 QTC::TC("qpdf", "QPDFWriter copy Extends"); 1703 QTC::TC("qpdf", "QPDFWriter copy Extends");
1729 - writeStringQDF("\n ");  
1730 - writeString(" /Extends "); 1704 + write_qdf("\n ").write(" /Extends ");
1731 unparseChild(extends, 1, f_in_ostream); 1705 unparseChild(extends, 1, f_in_ostream);
1732 } 1706 }
1733 } 1707 }
1734 - writeStringQDF("\n");  
1735 - writeStringNoQDF(" ");  
1736 - writeString(">>\nstream\n"); 1708 + write_qdf("\n").write_no_qdf(" ").write(">>\nstream\n");
1737 if (m->encryption) { 1709 if (m->encryption) {
1738 QTC::TC("qpdf", "QPDFWriter encrypt object stream"); 1710 QTC::TC("qpdf", "QPDFWriter encrypt object stream");
1739 } 1711 }
1740 { 1712 {
1741 PipelinePopper pp_enc(this); 1713 PipelinePopper pp_enc(this);
1742 pushEncryptionFilter(pp_enc); 1714 pushEncryptionFilter(pp_enc);
1743 - writeString(stream_buffer_pass2); 1715 + write(stream_buffer_pass2);
1744 } 1716 }
1745 if (m->newline_before_endstream) { 1717 if (m->newline_before_endstream) {
1746 - writeString("\n"); 1718 + write("\n");
1747 } 1719 }
1748 - writeString("endstream"); 1720 + write("endstream");
1749 m->cur_data_key.clear(); 1721 m->cur_data_key.clear();
1750 closeObject(new_stream_id); 1722 closeObject(new_stream_id);
1751 } 1723 }
@@ -1755,8 +1727,8 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index) @@ -1755,8 +1727,8 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index)
1755 { 1727 {
1756 QPDFObjGen old_og = object.getObjGen(); 1728 QPDFObjGen old_og = object.getObjGen();
1757 1729
1758 - if ((object_stream_index == -1) && (old_og.getGen() == 0) &&  
1759 - (m->object_stream_to_objects.count(old_og.getObj()))) { 1730 + if (object_stream_index == -1 && old_og.getGen() == 0 &&
  1731 + m->object_stream_to_objects.contains(old_og.getObj())) {
1760 writeObjectStream(object); 1732 writeObjectStream(object);
1761 return; 1733 return;
1762 } 1734 }
@@ -1765,19 +1737,15 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index) @@ -1765,19 +1737,15 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index)
1765 auto new_id = m->obj[old_og].renumber; 1737 auto new_id = m->obj[old_og].renumber;
1766 if (m->qdf_mode) { 1738 if (m->qdf_mode) {
1767 if (m->page_object_to_seq.contains(old_og)) { 1739 if (m->page_object_to_seq.contains(old_og)) {
1768 - writeString("%% Page ");  
1769 - writeString(std::to_string(m->page_object_to_seq[old_og]));  
1770 - writeString("\n"); 1740 + write("%% Page ").write(m->page_object_to_seq[old_og]).write("\n");
1771 } 1741 }
1772 if (m->contents_to_page_seq.contains(old_og)) { 1742 if (m->contents_to_page_seq.contains(old_og)) {
1773 - writeString("%% Contents for page ");  
1774 - writeString(std::to_string(m->contents_to_page_seq[old_og]));  
1775 - writeString("\n"); 1743 + write("%% Contents for page ").write(m->contents_to_page_seq[old_og]).write("\n");
1776 } 1744 }
1777 } 1745 }
1778 if (object_stream_index == -1) { 1746 if (object_stream_index == -1) {
1779 if (m->qdf_mode && (!m->suppress_original_object_ids)) { 1747 if (m->qdf_mode && (!m->suppress_original_object_ids)) {
1780 - writeString("%% Original object ID: " + object.getObjGen().unparse(' ') + "\n"); 1748 + write("%% Original object ID: ").write(object.getObjGen().unparse(' ')).write("\n");
1781 } 1749 }
1782 openObject(new_id); 1750 openObject(new_id);
1783 setDataKey(new_id); 1751 setDataKey(new_id);
@@ -1786,17 +1754,17 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index) @@ -1786,17 +1754,17 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index)
1786 closeObject(new_id); 1754 closeObject(new_id);
1787 } else { 1755 } else {
1788 unparseObject(object, 0, f_in_ostream); 1756 unparseObject(object, 0, f_in_ostream);
1789 - writeString("\n"); 1757 + write("\n");
1790 } 1758 }
1791 1759
1792 - if ((!m->direct_stream_lengths) && object.isStream()) { 1760 + if (!m->direct_stream_lengths && object.isStream()) {
1793 if (m->qdf_mode) { 1761 if (m->qdf_mode) {
1794 if (m->added_newline) { 1762 if (m->added_newline) {
1795 - writeString("%QDF: ignore_newline\n"); 1763 + write("%QDF: ignore_newline\n");
1796 } 1764 }
1797 } 1765 }
1798 openObject(new_id + 1); 1766 openObject(new_id + 1);
1799 - writeString(std::to_string(m->cur_stream_length)); 1767 + write(m->cur_stream_length);
1800 closeObject(new_id + 1); 1768 closeObject(new_id + 1);
1801 } 1769 }
1802 } 1770 }
@@ -2274,46 +2242,36 @@ QPDFWriter::writeEncryptionDictionary() @@ -2274,46 +2242,36 @@ QPDFWriter::writeEncryptionDictionary()
2274 auto& enc = *m->encryption; 2242 auto& enc = *m->encryption;
2275 auto const V = enc.getV(); 2243 auto const V = enc.getV();
2276 2244
2277 - writeString("<<"); 2245 + write("<<");
2278 if (V >= 4) { 2246 if (V >= 4) {
2279 - writeString(" /CF << /StdCF << /AuthEvent /DocOpen /CFM ");  
2280 - writeString(m->encrypt_use_aes ? ((V < 5) ? "/AESV2" : "/AESV3") : "/V2"); 2247 + write(" /CF << /StdCF << /AuthEvent /DocOpen /CFM ");
  2248 + write(m->encrypt_use_aes ? ((V < 5) ? "/AESV2" : "/AESV3") : "/V2");
2281 // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of 2249 // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of
2282 // MacOS won't open encrypted files without it. 2250 // MacOS won't open encrypted files without it.
2283 - writeString((V < 5) ? " /Length 16 >> >>" : " /Length 32 >> >>"); 2251 + write((V < 5) ? " /Length 16 >> >>" : " /Length 32 >> >>");
2284 if (!m->encryption->getEncryptMetadata()) { 2252 if (!m->encryption->getEncryptMetadata()) {
2285 - writeString(" /EncryptMetadata false"); 2253 + write(" /EncryptMetadata false");
2286 } 2254 }
2287 } 2255 }
2288 - writeString(" /Filter /Standard /Length ");  
2289 - writeString(std::to_string(enc.getLengthBytes() * 8));  
2290 - writeString(" /O ");  
2291 - writeString(QPDF_String(enc.getO()).unparse(true)); 2256 + write(" /Filter /Standard /Length ").write(enc.getLengthBytes() * 8);
  2257 + write(" /O ").write(QPDF_String(enc.getO()).unparse(true));
2292 if (V >= 4) { 2258 if (V >= 4) {
2293 - writeString(" /OE ");  
2294 - writeString(QPDF_String(enc.getOE()).unparse(true)); 2259 + write(" /OE ").write(QPDF_String(enc.getOE()).unparse(true));
2295 } 2260 }
2296 - writeString(" /P ");  
2297 - writeString(std::to_string(enc.getP())); 2261 + write(" /P ").write(enc.getP());
2298 if (V >= 5) { 2262 if (V >= 5) {
2299 - writeString(" /Perms ");  
2300 - writeString(QPDF_String(enc.getPerms()).unparse(true)); 2263 + write(" /Perms ").write(QPDF_String(enc.getPerms()).unparse(true));
2301 } 2264 }
2302 - writeString(" /R ");  
2303 - writeString(std::to_string(enc.getR())); 2265 + write(" /R ").write(enc.getR());
2304 2266
2305 if (V >= 4) { 2267 if (V >= 4) {
2306 - writeString(" /StmF /StdCF /StrF /StdCF"); 2268 + write(" /StmF /StdCF /StrF /StdCF");
2307 } 2269 }
2308 - writeString(" /U ");  
2309 - writeString(QPDF_String(enc.getU()).unparse(true)); 2270 + write(" /U ").write(QPDF_String(enc.getU()).unparse(true));
2310 if (V >= 4) { 2271 if (V >= 4) {
2311 - writeString(" /UE ");  
2312 - writeString(QPDF_String(enc.getUE()).unparse(true)); 2272 + write(" /UE ").write(QPDF_String(enc.getUE()).unparse(true));
2313 } 2273 }
2314 - writeString(" /V ");  
2315 - writeString(std::to_string(enc.getV()));  
2316 - writeString(" >>"); 2274 + write(" /V ").write(enc.getV()).write(" >>");
2317 closeObject(m->encryption_dict_objid); 2275 closeObject(m->encryption_dict_objid);
2318 } 2276 }
2319 2277
@@ -2327,17 +2285,16 @@ QPDFWriter::getFinalVersion() @@ -2327,17 +2285,16 @@ QPDFWriter::getFinalVersion()
2327 void 2285 void
2328 QPDFWriter::writeHeader() 2286 QPDFWriter::writeHeader()
2329 { 2287 {
2330 - writeString("%PDF-");  
2331 - writeString(m->final_pdf_version); 2288 + write("%PDF-").write(m->final_pdf_version);
2332 if (m->pclm) { 2289 if (m->pclm) {
2333 // PCLm version 2290 // PCLm version
2334 - writeString("\n%PCLm 1.0\n"); 2291 + write("\n%PCLm 1.0\n");
2335 } else { 2292 } else {
2336 // This string of binary characters would not be valid UTF-8, so it really should be treated 2293 // This string of binary characters would not be valid UTF-8, so it really should be treated
2337 // as binary. 2294 // as binary.
2338 - writeString("\n%\xbf\xf7\xa2\xfe\n"); 2295 + write("\n%\xbf\xf7\xa2\xfe\n");
2339 } 2296 }
2340 - writeStringQDF("%QDF-1.0\n\n"); 2297 + write_qdf("%QDF-1.0\n\n");
2341 2298
2342 // Note: do not write extra header text here. Linearized PDFs must include the entire 2299 // Note: do not write extra header text here. Linearized PDFs must include the entire
2343 // linearization parameter dictionary within the first 1024 characters of the PDF file, so for 2300 // linearization parameter dictionary within the first 1024 characters of the PDF file, so for
@@ -2351,7 +2308,7 @@ QPDFWriter::writeHintStream(int hint_id) @@ -2351,7 +2308,7 @@ QPDFWriter::writeHintStream(int hint_id)
2351 std::string hint_buffer; 2308 std::string hint_buffer;
2352 int S = 0; 2309 int S = 0;
2353 int O = 0; 2310 int O = 0;
2354 - bool compressed = (m->compress_streams && !m->qdf_mode); 2311 + bool compressed = m->compress_streams && !m->qdf_mode;
2355 QPDF::Writer::generateHintStream(m->pdf, m->new_obj, m->obj, hint_buffer, S, O, compressed); 2312 QPDF::Writer::generateHintStream(m->pdf, m->new_obj, m->obj, hint_buffer, S, O, compressed);
2356 2313
2357 openObject(hint_id); 2314 openObject(hint_id);
@@ -2359,20 +2316,17 @@ QPDFWriter::writeHintStream(int hint_id) @@ -2359,20 +2316,17 @@ QPDFWriter::writeHintStream(int hint_id)
2359 2316
2360 size_t hlen = hint_buffer.size(); 2317 size_t hlen = hint_buffer.size();
2361 2318
2362 - writeString("<< "); 2319 + write("<< ");
2363 if (compressed) { 2320 if (compressed) {
2364 - writeString("/Filter /FlateDecode "); 2321 + write("/Filter /FlateDecode ");
2365 } 2322 }
2366 - writeString("/S ");  
2367 - writeString(std::to_string(S)); 2323 + write("/S ").write(S);
2368 if (O) { 2324 if (O) {
2369 - writeString(" /O ");  
2370 - writeString(std::to_string(O)); 2325 + write(" /O ").write(O);
2371 } 2326 }
2372 - writeString(" /Length ");  
2373 adjustAESStreamLength(hlen); 2327 adjustAESStreamLength(hlen);
2374 - writeString(std::to_string(hlen));  
2375 - writeString(" >>\nstream\n"); 2328 + write(" /Length ").write(hlen);
  2329 + write(" >>\nstream\n");
2376 2330
2377 if (m->encryption) { 2331 if (m->encryption) {
2378 QTC::TC("qpdf", "QPDFWriter encrypted hint stream"); 2332 QTC::TC("qpdf", "QPDFWriter encrypted hint stream");
@@ -2381,13 +2335,13 @@ QPDFWriter::writeHintStream(int hint_id) @@ -2381,13 +2335,13 @@ QPDFWriter::writeHintStream(int hint_id)
2381 { 2335 {
2382 PipelinePopper pp_enc(this); 2336 PipelinePopper pp_enc(this);
2383 pushEncryptionFilter(pp_enc); 2337 pushEncryptionFilter(pp_enc);
2384 - writeString(hint_buffer); 2338 + write(hint_buffer);
2385 } 2339 }
2386 2340
2387 if (last_char != '\n') { 2341 if (last_char != '\n') {
2388 - writeString("\n"); 2342 + write("\n");
2389 } 2343 }
2390 - writeString("endstream"); 2344 + write("endstream");
2391 closeObject(hint_id); 2345 closeObject(hint_id);
2392 } 2346 }
2393 2347
@@ -2412,29 +2366,25 @@ QPDFWriter::writeXRefTable( @@ -2412,29 +2366,25 @@ QPDFWriter::writeXRefTable(
2412 qpdf_offset_t hint_length, 2366 qpdf_offset_t hint_length,
2413 int linearization_pass) 2367 int linearization_pass)
2414 { 2368 {
2415 - writeString("xref\n");  
2416 - writeString(std::to_string(first));  
2417 - writeString(" ");  
2418 - writeString(std::to_string(last - first + 1)); 2369 + write("xref\n").write(first).write(" ").write(last - first + 1);
2419 qpdf_offset_t space_before_zero = m->pipeline->getCount(); 2370 qpdf_offset_t space_before_zero = m->pipeline->getCount();
2420 - writeString("\n"); 2371 + write("\n");
  2372 + if (first == 0) {
  2373 + write("0000000000 65535 f \n");
  2374 + ++first;
  2375 + }
2421 for (int i = first; i <= last; ++i) { 2376 for (int i = first; i <= last; ++i) {
2422 - if (i == 0) {  
2423 - writeString("0000000000 65535 f \n");  
2424 - } else {  
2425 - qpdf_offset_t offset = 0;  
2426 - if (!suppress_offsets) {  
2427 - offset = m->new_obj[i].xref.getOffset();  
2428 - if ((hint_id != 0) && (i != hint_id) && (offset >= hint_offset)) {  
2429 - offset += hint_length;  
2430 - } 2377 + qpdf_offset_t offset = 0;
  2378 + if (!suppress_offsets) {
  2379 + offset = m->new_obj[i].xref.getOffset();
  2380 + if ((hint_id != 0) && (i != hint_id) && (offset >= hint_offset)) {
  2381 + offset += hint_length;
2431 } 2382 }
2432 - writeString(QUtil::int_to_string(offset, 10));  
2433 - writeString(" 00000 n \n");  
2434 } 2383 }
  2384 + write(QUtil::int_to_string(offset, 10)).write(" 00000 n \n");
2435 } 2385 }
2436 writeTrailer(which, size, false, prev, linearization_pass); 2386 writeTrailer(which, size, false, prev, linearization_pass);
2437 - writeString("\n"); 2387 + write("\n");
2438 return space_before_zero; 2388 return space_before_zero;
2439 } 2389 }
2440 2390
@@ -2532,27 +2482,18 @@ QPDFWriter::writeXRefStream( @@ -2532,27 +2482,18 @@ QPDFWriter::writeXRefStream(
2532 } 2482 }
2533 2483
2534 openObject(xref_id); 2484 openObject(xref_id);
2535 - writeString("<<");  
2536 - writeStringQDF("\n ");  
2537 - writeString(" /Type /XRef");  
2538 - writeStringQDF("\n ");  
2539 - writeString(" /Length " + std::to_string(xref_data.size())); 2485 + write("<<").write_qdf("\n ").write(" /Type /XRef").write_qdf("\n ");
  2486 + write(" /Length ").write(xref_data.size());
2540 if (compressed) { 2487 if (compressed) {
2541 - writeStringQDF("\n ");  
2542 - writeString(" /Filter /FlateDecode");  
2543 - writeStringQDF("\n ");  
2544 - writeString(" /DecodeParms << /Columns " + std::to_string(esize) + " /Predictor 12 >>"); 2488 + write_qdf("\n ").write(" /Filter /FlateDecode").write_qdf("\n ");
  2489 + write(" /DecodeParms << /Columns ").write(esize).write(" /Predictor 12 >>");
2545 } 2490 }
2546 - writeStringQDF("\n ");  
2547 - writeString(" /W [ 1 " + std::to_string(f1_size) + " " + std::to_string(f2_size) + " ]");  
2548 - if (!((first == 0) && (last == size - 1))) {  
2549 - writeString(  
2550 - " /Index [ " + std::to_string(first) + " " + std::to_string(last - first + 1) + " ]"); 2491 + write_qdf("\n ").write(" /W [ 1 ").write(f1_size).write(" ").write(f2_size).write(" ]");
  2492 + if (!(first == 0 && last == (size - 1))) {
  2493 + write(" /Index [ ").write(first).write(" ").write(last - first + 1).write(" ]");
2551 } 2494 }
2552 writeTrailer(which, size, true, prev, linearization_pass); 2495 writeTrailer(which, size, true, prev, linearization_pass);
2553 - writeString("\nstream\n");  
2554 - writeString(xref_data);  
2555 - writeString("\nendstream"); 2496 + write("\nstream\n").write(xref_data).write("\nendstream");
2556 closeObject(xref_id); 2497 closeObject(xref_id);
2557 return space_before_zero; 2498 return space_before_zero;
2558 } 2499 }
@@ -2726,37 +2667,28 @@ QPDFWriter::writeLinearized() @@ -2726,37 +2667,28 @@ QPDFWriter::writeLinearized()
2726 2667
2727 qpdf_offset_t pos = m->pipeline->getCount(); 2668 qpdf_offset_t pos = m->pipeline->getCount();
2728 openObject(lindict_id); 2669 openObject(lindict_id);
2729 - writeString("<<"); 2670 + write("<<");
2730 if (pass == 2) { 2671 if (pass == 2) {
2731 std::vector<QPDFObjectHandle> const& pages = m->pdf.getAllPages(); 2672 std::vector<QPDFObjectHandle> const& pages = m->pdf.getAllPages();
2732 int first_page_object = m->obj[pages.at(0)].renumber; 2673 int first_page_object = m->obj[pages.at(0)].renumber;
2733 - int npages = QIntC::to_int(pages.size());  
2734 2674
2735 - writeString(" /Linearized 1 /L ");  
2736 - writeString(std::to_string(file_size + hint_length)); 2675 + write(" /Linearized 1 /L ").write(file_size + hint_length);
2737 // Implementation note 121 states that a space is mandatory after this open bracket. 2676 // Implementation note 121 states that a space is mandatory after this open bracket.
2738 - writeString(" /H [ ");  
2739 - writeString(std::to_string(m->new_obj[hint_id].xref.getOffset()));  
2740 - writeString(" ");  
2741 - writeString(std::to_string(hint_length));  
2742 - writeString(" ] /O ");  
2743 - writeString(std::to_string(first_page_object));  
2744 - writeString(" /E ");  
2745 - writeString(std::to_string(part6_end_offset + hint_length));  
2746 - writeString(" /N ");  
2747 - writeString(std::to_string(npages));  
2748 - writeString(" /T ");  
2749 - writeString(std::to_string(space_before_zero + hint_length));  
2750 - }  
2751 - writeString(" >>"); 2677 + write(" /H [ ").write(m->new_obj[hint_id].xref.getOffset()).write(" ");
  2678 + write(hint_length);
  2679 + write(" ] /O ").write(first_page_object);
  2680 + write(" /E ").write(part6_end_offset + hint_length);
  2681 + write(" /N ").write(pages.size());
  2682 + write(" /T ").write(space_before_zero + hint_length);
  2683 + }
  2684 + write(" >>");
2752 closeObject(lindict_id); 2685 closeObject(lindict_id);
2753 static int const pad = 200; 2686 static int const pad = 200;
2754 - writePad(QIntC::to_size(pos - m->pipeline->getCount() + pad));  
2755 - writeString("\n"); 2687 + write(QIntC::to_size(pos - m->pipeline->getCount() + pad), ' ').write("\n");
2756 2688
2757 // If the user supplied any additional header text, write it here after the linearization 2689 // If the user supplied any additional header text, write it here after the linearization
2758 // parameter dictionary. 2690 // parameter dictionary.
2759 - writeString(m->extra_header_text); 2691 + write(m->extra_header_text);
2760 2692
2761 // Part 3: first page cross reference table and trailer. 2693 // Part 3: first page cross reference table and trailer.
2762 2694
@@ -2793,20 +2725,19 @@ QPDFWriter::writeLinearized() @@ -2793,20 +2725,19 @@ QPDFWriter::writeLinearized()
2793 qpdf_offset_t endpos = m->pipeline->getCount(); 2725 qpdf_offset_t endpos = m->pipeline->getCount();
2794 if (pass == 1) { 2726 if (pass == 1) {
2795 // Pad so we have enough room for the real xref stream. 2727 // Pad so we have enough room for the real xref stream.
2796 - writePad(calculateXrefStreamPadding(endpos - pos)); 2728 + write(calculateXrefStreamPadding(endpos - pos), ' ');
2797 first_xref_end = m->pipeline->getCount(); 2729 first_xref_end = m->pipeline->getCount();
2798 } else { 2730 } else {
2799 // Pad so that the next object starts at the same place as in pass 1. 2731 // Pad so that the next object starts at the same place as in pass 1.
2800 - writePad(QIntC::to_size(first_xref_end - endpos)); 2732 + write(QIntC::to_size(first_xref_end - endpos), ' ');
2801 2733
2802 if (m->pipeline->getCount() != first_xref_end) { 2734 if (m->pipeline->getCount() != first_xref_end) {
2803 throw std::logic_error( 2735 throw std::logic_error(
2804 - "insufficient padding for first pass xref stream; "  
2805 - "first_xref_end=" + 2736 + "insufficient padding for first pass xref stream; first_xref_end=" +
2806 std::to_string(first_xref_end) + "; endpos=" + std::to_string(endpos)); 2737 std::to_string(first_xref_end) + "; endpos=" + std::to_string(endpos));
2807 } 2738 }
2808 } 2739 }
2809 - writeString("\n"); 2740 + write("\n");
2810 } else { 2741 } else {
2811 writeXRefTable( 2742 writeXRefTable(
2812 t_lin_first, 2743 t_lin_first,
@@ -2819,7 +2750,7 @@ QPDFWriter::writeLinearized() @@ -2819,7 +2750,7 @@ QPDFWriter::writeLinearized()
2819 hint_offset, 2750 hint_offset,
2820 hint_length, 2751 hint_length,
2821 pass); 2752 pass);
2822 - writeString("startxref\n0\n%%EOF\n"); 2753 + write("startxref\n0\n%%EOF\n");
2823 } 2754 }
2824 2755
2825 // Parts 4 through 9 2756 // Parts 4 through 9
@@ -2837,7 +2768,7 @@ QPDFWriter::writeLinearized() @@ -2837,7 +2768,7 @@ QPDFWriter::writeLinearized()
2837 m->new_obj[hint_id].xref = QPDFXRefEntry(m->pipeline->getCount()); 2768 m->new_obj[hint_id].xref = QPDFXRefEntry(m->pipeline->getCount());
2838 } else { 2769 } else {
2839 // Part 5: hint stream 2770 // Part 5: hint stream
2840 - writeString(hint_buffer); 2771 + write(hint_buffer);
2841 } 2772 }
2842 } 2773 }
2843 if (cur_object.getObjectID() == part6_end_marker) { 2774 if (cur_object.getObjectID() == part6_end_marker) {
@@ -2871,14 +2802,13 @@ QPDFWriter::writeLinearized() @@ -2871,14 +2802,13 @@ QPDFWriter::writeLinearized()
2871 if (pass == 1) { 2802 if (pass == 1) {
2872 // Pad so we have enough room for the real xref stream. See comments for previous 2803 // Pad so we have enough room for the real xref stream. See comments for previous
2873 // xref stream on how we calculate the padding. 2804 // xref stream on how we calculate the padding.
2874 - writePad(calculateXrefStreamPadding(endpos - pos));  
2875 - writeString("\n"); 2805 + write(calculateXrefStreamPadding(endpos - pos), ' ').write("\n");
2876 second_xref_end = m->pipeline->getCount(); 2806 second_xref_end = m->pipeline->getCount();
2877 } else { 2807 } else {
2878 // Make the file size the same. 2808 // Make the file size the same.
2879 - writePad(  
2880 - QIntC::to_size(second_xref_end + hint_length - 1 - m->pipeline->getCount()));  
2881 - writeString("\n"); 2809 + auto padding =
  2810 + QIntC::to_size(second_xref_end + hint_length - 1 - m->pipeline->getCount());
  2811 + write(padding, ' ').write("\n");
2882 2812
2883 // If this assertion fails, maybe we didn't have enough padding above. 2813 // If this assertion fails, maybe we didn't have enough padding above.
2884 if (m->pipeline->getCount() != second_xref_end + hint_length) { 2814 if (m->pipeline->getCount() != second_xref_end + hint_length) {
@@ -2890,9 +2820,7 @@ QPDFWriter::writeLinearized() @@ -2890,9 +2820,7 @@ QPDFWriter::writeLinearized()
2890 space_before_zero = writeXRefTable( 2820 space_before_zero = writeXRefTable(
2891 t_lin_second, 0, second_half_end, second_trailer_size, 0, false, 0, 0, 0, pass); 2821 t_lin_second, 0, second_half_end, second_trailer_size, 0, false, 0, 0, 0, pass);
2892 } 2822 }
2893 - writeString("startxref\n");  
2894 - writeString(std::to_string(first_xref_offset));  
2895 - writeString("\n%%EOF\n"); 2823 + write("startxref\n").write(first_xref_offset).write("\n%%EOF\n");
2896 2824
2897 if (pass == 1) { 2825 if (pass == 1) {
2898 if (m->deterministic_id) { 2826 if (m->deterministic_id) {
@@ -3038,7 +2966,7 @@ QPDFWriter::writeStandard() @@ -3038,7 +2966,7 @@ QPDFWriter::writeStandard()
3038 // Start writing 2966 // Start writing
3039 2967
3040 writeHeader(); 2968 writeHeader();
3041 - writeString(m->extra_header_text); 2969 + write(m->extra_header_text);
3042 2970
3043 if (m->pclm) { 2971 if (m->pclm) {
3044 enqueueObjectsPCLm(); 2972 enqueueObjectsPCLm();
@@ -3069,9 +2997,7 @@ QPDFWriter::writeStandard() @@ -3069,9 +2997,7 @@ QPDFWriter::writeStandard()
3069 writeXRefStream( 2997 writeXRefStream(
3070 xref_id, xref_id, xref_offset, t_normal, 0, m->next_objid - 1, m->next_objid); 2998 xref_id, xref_id, xref_offset, t_normal, 0, m->next_objid - 1, m->next_objid);
3071 } 2999 }
3072 - writeString("startxref\n");  
3073 - writeString(std::to_string(xref_offset));  
3074 - writeString("\n%%EOF\n"); 3000 + write("startxref\n").write(xref_offset).write("\n%%EOF\n");
3075 3001
3076 if (m->deterministic_id) { 3002 if (m->deterministic_id) {
3077 QTC::TC( 3003 QTC::TC(
libqpdf/qpdf/Pipeline_private.hh
@@ -109,13 +109,36 @@ namespace qpdf::pl @@ -109,13 +109,36 @@ namespace qpdf::pl
109 write(unsigned char const* buf, size_t len) final 109 write(unsigned char const* buf, size_t len) final
110 { 110 {
111 if (len) { 111 if (len) {
  112 + Count::write(std::string_view(reinterpret_cast<char const*>(buf), len));
  113 + }
  114 + }
  115 +
  116 + void
  117 + write(std::string_view sv)
  118 + {
  119 + if (sv.size()) {
  120 + if (str) {
  121 + str->append(sv);
  122 + return;
  123 + }
  124 + count += static_cast<qpdf_offset_t>(sv.size());
  125 + if (pass_immediately_to_next) {
  126 + next()->write(reinterpret_cast<char const*>(sv.data()), sv.size());
  127 + }
  128 + }
  129 + }
  130 +
  131 + void
  132 + write(size_t len, char c)
  133 + {
  134 + if (len) {
112 if (str) { 135 if (str) {
113 - str->append(reinterpret_cast<char const*>(buf), len); 136 + str->append(len, c);
114 return; 137 return;
115 } 138 }
116 count += static_cast<qpdf_offset_t>(len); 139 count += static_cast<qpdf_offset_t>(len);
117 if (pass_immediately_to_next) { 140 if (pass_immediately_to_next) {
118 - next()->write(buf, len); 141 + next()->writeString(std::string(len, c));
119 } 142 }
120 } 143 }
121 } 144 }