Commit f7e07240c7080e6350aaacf374027de0f8fc3d33
Committed by
GitHub
Merge pull request #1490 from m-holger/writer
Modernize QPDFWriter write methods
Showing
5 changed files
with
224 additions
and
252 deletions
include/qpdf/QPDFWriter.hh
| ... | ... | @@ -24,6 +24,7 @@ |
| 24 | 24 | #include <qpdf/Types.h> |
| 25 | 25 | |
| 26 | 26 | #include <bitset> |
| 27 | +#include <concepts> | |
| 27 | 28 | #include <cstdio> |
| 28 | 29 | #include <functional> |
| 29 | 30 | #include <list> |
| ... | ... | @@ -481,10 +482,16 @@ class QPDFWriter |
| 481 | 482 | |
| 482 | 483 | unsigned int bytesNeeded(long long n); |
| 483 | 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 | + QPDFWriter& write_name(std::string const& str); | |
| 489 | + QPDFWriter& write_string(std::string const& str, bool force_binary = false); | |
| 490 | + | |
| 491 | + template <typename... Args> | |
| 492 | + QPDFWriter& write_qdf(Args&&... args); | |
| 493 | + template <typename... Args> | |
| 494 | + QPDFWriter& write_no_qdf(Args&&... args); | |
| 488 | 495 | void assignCompressedObjectNumbers(QPDFObjGen og); |
| 489 | 496 | void enqueueObject(QPDFObjectHandle object); |
| 490 | 497 | void writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj); | ... | ... |
libqpdf.pc.in
libqpdf/QPDFWriter.cc
| ... | ... | @@ -832,32 +832,59 @@ QPDFWriter::writeBinary(unsigned long long val, unsigned int bytes) |
| 832 | 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 | +QPDFWriter& | |
| 857 | +QPDFWriter::write_name(std::string const& str) | |
| 858 | +{ | |
| 859 | + m->pipeline->write(Name::normalize(str)); | |
| 860 | + return *this; | |
| 861 | +} | |
| 862 | + | |
| 863 | +QPDFWriter& | |
| 864 | +QPDFWriter::write_string(std::string const& str, bool force_binary) | |
| 865 | +{ | |
| 866 | + m->pipeline->write(QPDF_String(str).unparse(force_binary)); | |
| 867 | + return *this; | |
| 868 | +} | |
| 869 | + | |
| 870 | +template <typename... Args> | |
| 871 | +QPDFWriter& | |
| 872 | +QPDFWriter::write_qdf(Args&&... args) | |
| 873 | +{ | |
| 874 | + if (m->qdf_mode) { | |
| 875 | + m->pipeline->write(std::forward<Args>(args)...); | |
| 854 | 876 | } |
| 877 | + return *this; | |
| 855 | 878 | } |
| 856 | 879 | |
| 857 | -void | |
| 858 | -QPDFWriter::writePad(size_t nspaces) | |
| 880 | +template <typename... Args> | |
| 881 | +QPDFWriter& | |
| 882 | +QPDFWriter::write_no_qdf(Args&&... args) | |
| 859 | 883 | { |
| 860 | - writeString(std::string(nspaces, ' ')); | |
| 884 | + if (!m->qdf_mode) { | |
| 885 | + m->pipeline->write(std::forward<Args>(args)...); | |
| 886 | + } | |
| 887 | + return *this; | |
| 861 | 888 | } |
| 862 | 889 | |
| 863 | 890 | Pipeline* |
| ... | ... | @@ -1005,8 +1032,7 @@ QPDFWriter::openObject(int objid) |
| 1005 | 1032 | objid = m->next_objid++; |
| 1006 | 1033 | } |
| 1007 | 1034 | m->new_obj[objid].xref = QPDFXRefEntry(m->pipeline->getCount()); |
| 1008 | - writeString(std::to_string(objid)); | |
| 1009 | - writeString(" 0 obj\n"); | |
| 1035 | + write(objid).write(" 0 obj\n"); | |
| 1010 | 1036 | return objid; |
| 1011 | 1037 | } |
| 1012 | 1038 | |
| ... | ... | @@ -1014,8 +1040,7 @@ void |
| 1014 | 1040 | QPDFWriter::closeObject(int objid) |
| 1015 | 1041 | { |
| 1016 | 1042 | // Write a newline before endobj as it makes the file easier to repair. |
| 1017 | - writeString("\nendobj\n"); | |
| 1018 | - writeStringQDF("\n"); | |
| 1043 | + write("\nendobj\n").write_qdf("\n"); | |
| 1019 | 1044 | auto& new_obj = m->new_obj[objid]; |
| 1020 | 1045 | new_obj.length = m->pipeline->getCount() - new_obj.xref.getOffset(); |
| 1021 | 1046 | } |
| ... | ... | @@ -1114,8 +1139,7 @@ QPDFWriter::unparseChild(QPDFObjectHandle child, int level, int flags) |
| 1114 | 1139 | enqueueObject(child); |
| 1115 | 1140 | } |
| 1116 | 1141 | if (child.isIndirect()) { |
| 1117 | - writeString(std::to_string(m->obj[child].renumber)); | |
| 1118 | - writeString(" 0 R"); | |
| 1142 | + write(m->obj[child].renumber).write(" 0 R"); | |
| 1119 | 1143 | } else { |
| 1120 | 1144 | unparseObject(child, level, flags); |
| 1121 | 1145 | } |
| ... | ... | @@ -1129,78 +1153,63 @@ QPDFWriter::writeTrailer( |
| 1129 | 1153 | if (xref_stream) { |
| 1130 | 1154 | m->cur_data_key.clear(); |
| 1131 | 1155 | } else { |
| 1132 | - writeString("trailer <<"); | |
| 1156 | + write("trailer <<"); | |
| 1133 | 1157 | } |
| 1134 | - writeStringQDF("\n"); | |
| 1158 | + write_qdf("\n"); | |
| 1135 | 1159 | if (which == t_lin_second) { |
| 1136 | - writeString(" /Size "); | |
| 1137 | - writeString(std::to_string(size)); | |
| 1160 | + write(" /Size ").write(size); | |
| 1138 | 1161 | } else { |
| 1139 | 1162 | for (auto const& [key, value]: trailer.as_dictionary()) { |
| 1140 | 1163 | if (value.null()) { |
| 1141 | 1164 | continue; |
| 1142 | 1165 | } |
| 1143 | - writeStringQDF(" "); | |
| 1144 | - writeStringNoQDF(" "); | |
| 1145 | - writeString(Name::normalize(key)); | |
| 1146 | - writeString(" "); | |
| 1166 | + write_qdf(" ").write_no_qdf(" ").write_name(key).write(" "); | |
| 1147 | 1167 | if (key == "/Size") { |
| 1148 | - writeString(std::to_string(size)); | |
| 1168 | + write(size); | |
| 1149 | 1169 | if (which == t_lin_first) { |
| 1150 | - writeString(" /Prev "); | |
| 1170 | + write(" /Prev "); | |
| 1151 | 1171 | qpdf_offset_t pos = m->pipeline->getCount(); |
| 1152 | - writeString(std::to_string(prev)); | |
| 1153 | - writePad(QIntC::to_size(pos - m->pipeline->getCount() + 21)); | |
| 1172 | + write(prev).write(QIntC::to_size(pos - m->pipeline->getCount() + 21), ' '); | |
| 1154 | 1173 | } |
| 1155 | 1174 | } else { |
| 1156 | 1175 | unparseChild(value, 1, 0); |
| 1157 | 1176 | } |
| 1158 | - writeStringQDF("\n"); | |
| 1177 | + write_qdf("\n"); | |
| 1159 | 1178 | } |
| 1160 | 1179 | } |
| 1161 | 1180 | |
| 1162 | 1181 | // Write ID |
| 1163 | - writeStringQDF(" "); | |
| 1164 | - writeString(" /ID ["); | |
| 1182 | + write_qdf(" ").write(" /ID ["); | |
| 1165 | 1183 | if (linearization_pass == 1) { |
| 1166 | 1184 | std::string original_id1 = getOriginalID1(); |
| 1167 | 1185 | if (original_id1.empty()) { |
| 1168 | - writeString("<00000000000000000000000000000000>"); | |
| 1186 | + write("<00000000000000000000000000000000>"); | |
| 1169 | 1187 | } else { |
| 1170 | 1188 | // Write a string of zeroes equal in length to the representation of the original ID. |
| 1171 | 1189 | // While writing the original ID would have the same number of bytes, it would cause a |
| 1172 | 1190 | // change to the deterministic ID generated by older versions of the software that |
| 1173 | 1191 | // hard-coded the length of the ID to 16 bytes. |
| 1174 | - writeString("<"); | |
| 1175 | 1192 | 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(">"); | |
| 1193 | + write("<").write(len, '0').write(">"); | |
| 1180 | 1194 | } |
| 1181 | - writeString("<00000000000000000000000000000000>"); | |
| 1195 | + write("<00000000000000000000000000000000>"); | |
| 1182 | 1196 | } else { |
| 1183 | - if ((linearization_pass == 0) && (m->deterministic_id)) { | |
| 1197 | + if (linearization_pass == 0 && m->deterministic_id) { | |
| 1184 | 1198 | computeDeterministicIDData(); |
| 1185 | 1199 | } |
| 1186 | 1200 | generateID(); |
| 1187 | - writeString(QPDF_String(m->id1).unparse(true)); | |
| 1188 | - writeString(QPDF_String(m->id2).unparse(true)); | |
| 1201 | + write_string(m->id1, true).write_string(m->id2, true); | |
| 1189 | 1202 | } |
| 1190 | - writeString("]"); | |
| 1203 | + write("]"); | |
| 1191 | 1204 | |
| 1192 | 1205 | if (which != t_lin_second) { |
| 1193 | 1206 | // Write reference to encryption dictionary |
| 1194 | 1207 | if (m->encryption) { |
| 1195 | - writeString(" /Encrypt "); | |
| 1196 | - writeString(std::to_string(m->encryption_dict_objid)); | |
| 1197 | - writeString(" 0 R"); | |
| 1208 | + write(" /Encrypt ").write(m->encryption_dict_objid).write(" 0 R"); | |
| 1198 | 1209 | } |
| 1199 | 1210 | } |
| 1200 | 1211 | |
| 1201 | - writeStringQDF("\n"); | |
| 1202 | - writeStringNoQDF(" "); | |
| 1203 | - writeString(">>"); | |
| 1212 | + write_qdf("\n>>").write_no_qdf(" >>"); | |
| 1204 | 1213 | } |
| 1205 | 1214 | |
| 1206 | 1215 | bool |
| ... | ... | @@ -1314,26 +1323,25 @@ QPDFWriter::unparseObject( |
| 1314 | 1323 | if (level < 0) { |
| 1315 | 1324 | throw std::logic_error("invalid level in QPDFWriter::unparseObject"); |
| 1316 | 1325 | } |
| 1317 | - // For non-qdf, "indent" is a single space between tokens. For qdf, indent includes the | |
| 1318 | - // preceding newline. | |
| 1319 | - std::string indent = " "; | |
| 1326 | + // For non-qdf, "indent" and "indent_large" are a single space between tokens. For qdf, they | |
| 1327 | + // include the preceding newline. | |
| 1328 | + std::string indent_large = " "; | |
| 1320 | 1329 | if (m->qdf_mode) { |
| 1321 | - indent.append(static_cast<size_t>(2 * level), ' '); | |
| 1322 | - indent[0] = '\n'; | |
| 1330 | + indent_large.append(static_cast<size_t>(2 * (level + 1)), ' '); | |
| 1331 | + indent_large[0] = '\n'; | |
| 1323 | 1332 | } |
| 1333 | + std::string_view indent{indent_large.data(), m->qdf_mode ? indent_large.size() - 2 : 1}; | |
| 1324 | 1334 | |
| 1325 | 1335 | if (auto const tc = object.getTypeCode(); tc == ::ot_array) { |
| 1326 | 1336 | // Note: PDF spec 1.4 implementation note 121 states that Acrobat requires a space after the |
| 1327 | 1337 | // [ in the /H key of the linearization parameter dictionary. We'll do this unconditionally |
| 1328 | 1338 | // for all arrays because it looks nicer and doesn't make the files that much bigger. |
| 1329 | - writeString("["); | |
| 1339 | + write("["); | |
| 1330 | 1340 | for (auto const& item: object.as_array()) { |
| 1331 | - writeString(indent); | |
| 1332 | - writeStringQDF(" "); | |
| 1341 | + write(indent_large); | |
| 1333 | 1342 | unparseChild(item, level + 1, child_flags); |
| 1334 | 1343 | } |
| 1335 | - writeString(indent); | |
| 1336 | - writeString("]"); | |
| 1344 | + write(indent).write("]"); | |
| 1337 | 1345 | } else if (tc == ::ot_dictionary) { |
| 1338 | 1346 | // Handle special cases for specific dictionaries. |
| 1339 | 1347 | |
| ... | ... | @@ -1474,14 +1482,11 @@ QPDFWriter::unparseObject( |
| 1474 | 1482 | } |
| 1475 | 1483 | } |
| 1476 | 1484 | |
| 1477 | - writeString("<<"); | |
| 1485 | + write("<<"); | |
| 1478 | 1486 | |
| 1479 | 1487 | for (auto const& [key, value]: object.as_dictionary()) { |
| 1480 | 1488 | if (!value.null()) { |
| 1481 | - writeString(indent); | |
| 1482 | - writeStringQDF(" "); | |
| 1483 | - writeString(Name::normalize(key)); | |
| 1484 | - writeString(" "); | |
| 1489 | + write(indent_large).write_name(key).write(" "); | |
| 1485 | 1490 | if (key == "/Contents" && object.isDictionaryOfType("/Sig") && |
| 1486 | 1491 | object.hasKey("/ByteRange")) { |
| 1487 | 1492 | QTC::TC("qpdf", "QPDFWriter no encryption sig contents"); |
| ... | ... | @@ -1493,25 +1498,19 @@ QPDFWriter::unparseObject( |
| 1493 | 1498 | } |
| 1494 | 1499 | |
| 1495 | 1500 | if (flags & f_stream) { |
| 1496 | - writeString(indent); | |
| 1497 | - writeStringQDF(" "); | |
| 1498 | - writeString("/Length "); | |
| 1501 | + write(indent_large).write("/Length "); | |
| 1499 | 1502 | |
| 1500 | 1503 | if (m->direct_stream_lengths) { |
| 1501 | - writeString(std::to_string(stream_length)); | |
| 1504 | + write(stream_length); | |
| 1502 | 1505 | } else { |
| 1503 | - writeString(std::to_string(m->cur_stream_length_id)); | |
| 1504 | - writeString(" 0 R"); | |
| 1506 | + write(m->cur_stream_length_id).write(" 0 R"); | |
| 1505 | 1507 | } |
| 1506 | 1508 | if (compress && (flags & f_filtered)) { |
| 1507 | - writeString(indent); | |
| 1508 | - writeStringQDF(" "); | |
| 1509 | - writeString("/Filter /FlateDecode"); | |
| 1509 | + write(indent_large).write("/Filter /FlateDecode"); | |
| 1510 | 1510 | } |
| 1511 | 1511 | } |
| 1512 | 1512 | |
| 1513 | - writeString(indent); | |
| 1514 | - writeString(">>"); | |
| 1513 | + write(indent).write(">>"); | |
| 1515 | 1514 | } else if (tc == ::ot_stream) { |
| 1516 | 1515 | // Write stream data to a buffer. |
| 1517 | 1516 | if (!m->direct_stream_lengths) { |
| ... | ... | @@ -1535,18 +1534,18 @@ QPDFWriter::unparseObject( |
| 1535 | 1534 | adjustAESStreamLength(m->cur_stream_length); |
| 1536 | 1535 | unparseObject(stream_dict, 0, flags, m->cur_stream_length, compress_stream); |
| 1537 | 1536 | char last_char = stream_data.empty() ? '\0' : stream_data.back(); |
| 1538 | - writeString("\nstream\n"); | |
| 1537 | + write("\nstream\n"); | |
| 1539 | 1538 | { |
| 1540 | 1539 | PipelinePopper pp_enc(this); |
| 1541 | 1540 | pushEncryptionFilter(pp_enc); |
| 1542 | - writeString(stream_data); | |
| 1541 | + write(stream_data); | |
| 1543 | 1542 | } |
| 1544 | 1543 | |
| 1545 | 1544 | if ((m->added_newline = |
| 1546 | 1545 | m->newline_before_endstream || (m->qdf_mode && last_char != '\n'))) { |
| 1547 | - writeString("\nendstream"); | |
| 1546 | + write("\nendstream"); | |
| 1548 | 1547 | } else { |
| 1549 | - writeString("endstream"); | |
| 1548 | + write("endstream"); | |
| 1550 | 1549 | } |
| 1551 | 1550 | } else if (tc == ::ot_string) { |
| 1552 | 1551 | std::string val; |
| ... | ... | @@ -1580,9 +1579,9 @@ QPDFWriter::unparseObject( |
| 1580 | 1579 | } else { |
| 1581 | 1580 | val = object.unparseResolved(); |
| 1582 | 1581 | } |
| 1583 | - writeString(val); | |
| 1582 | + write(val); | |
| 1584 | 1583 | } else { |
| 1585 | - writeString(object.unparseResolved()); | |
| 1584 | + write(object.unparseResolved()); | |
| 1586 | 1585 | } |
| 1587 | 1586 | } |
| 1588 | 1587 | |
| ... | ... | @@ -1596,14 +1595,13 @@ QPDFWriter::writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int fi |
| 1596 | 1595 | if (is_first) { |
| 1597 | 1596 | is_first = false; |
| 1598 | 1597 | } else { |
| 1599 | - writeStringQDF("\n"); | |
| 1600 | - writeStringNoQDF(" "); | |
| 1598 | + write_qdf("\n").write_no_qdf(" "); | |
| 1601 | 1599 | } |
| 1602 | - writeString(id); | |
| 1600 | + write(id); | |
| 1603 | 1601 | util::increment(id, 1); |
| 1604 | - writeString(std::to_string(offset)); | |
| 1602 | + write(offset); | |
| 1605 | 1603 | } |
| 1606 | - writeString("\n"); | |
| 1604 | + write("\n"); | |
| 1607 | 1605 | } |
| 1608 | 1606 | |
| 1609 | 1607 | void |
| ... | ... | @@ -1639,20 +1637,18 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1639 | 1637 | first_obj = new_obj; |
| 1640 | 1638 | } |
| 1641 | 1639 | if (m->qdf_mode) { |
| 1642 | - writeString( | |
| 1643 | - "%% Object stream: object " + std::to_string(new_obj) + ", index " + | |
| 1644 | - std::to_string(count)); | |
| 1640 | + write("%% Object stream: object ").write(new_obj).write(", index ").write(count); | |
| 1645 | 1641 | if (!m->suppress_original_object_ids) { |
| 1646 | - writeString("; original object ID: " + std::to_string(obj.getObj())); | |
| 1642 | + write("; original object ID: ").write(obj.getObj()); | |
| 1647 | 1643 | // For compatibility, only write the generation if non-zero. While object |
| 1648 | 1644 | // streams only allow objects with generation 0, if we are generating object |
| 1649 | 1645 | // streams, the old object could have a non-zero generation. |
| 1650 | 1646 | if (obj.getGen() != 0) { |
| 1651 | 1647 | QTC::TC("qpdf", "QPDFWriter original obj non-zero gen"); |
| 1652 | - writeString(" " + std::to_string(obj.getGen())); | |
| 1648 | + write(" ").write(obj.getGen()); | |
| 1653 | 1649 | } |
| 1654 | 1650 | } |
| 1655 | - writeString("\n"); | |
| 1651 | + write("\n"); | |
| 1656 | 1652 | } |
| 1657 | 1653 | |
| 1658 | 1654 | offsets.push_back(m->pipeline->getCount()); |
| ... | ... | @@ -1698,7 +1694,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1698 | 1694 | activatePipelineStack(pp_ostream, stream_buffer_pass2); |
| 1699 | 1695 | } |
| 1700 | 1696 | writeObjectStreamOffsets(offsets, first_obj); |
| 1701 | - writeString(stream_buffer_pass1); | |
| 1697 | + write(stream_buffer_pass1); | |
| 1702 | 1698 | stream_buffer_pass1.clear(); |
| 1703 | 1699 | stream_buffer_pass1.shrink_to_fit(); |
| 1704 | 1700 | } |
| ... | ... | @@ -1706,46 +1702,37 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1706 | 1702 | // Write the object |
| 1707 | 1703 | openObject(new_stream_id); |
| 1708 | 1704 | setDataKey(new_stream_id); |
| 1709 | - writeString("<<"); | |
| 1710 | - writeStringQDF("\n "); | |
| 1711 | - writeString(" /Type /ObjStm"); | |
| 1712 | - writeStringQDF("\n "); | |
| 1705 | + write("<<").write_qdf("\n ").write(" /Type /ObjStm").write_qdf("\n "); | |
| 1713 | 1706 | size_t length = stream_buffer_pass2.size(); |
| 1714 | 1707 | adjustAESStreamLength(length); |
| 1715 | - writeString(" /Length " + std::to_string(length)); | |
| 1716 | - writeStringQDF("\n "); | |
| 1708 | + write(" /Length ").write(length).write_qdf("\n "); | |
| 1717 | 1709 | if (compressed) { |
| 1718 | - writeString(" /Filter /FlateDecode"); | |
| 1710 | + write(" /Filter /FlateDecode"); | |
| 1719 | 1711 | } |
| 1720 | - writeString(" /N " + std::to_string(offsets.size())); | |
| 1721 | - writeStringQDF("\n "); | |
| 1722 | - writeString(" /First " + std::to_string(first)); | |
| 1712 | + write(" /N ").write(offsets.size()).write_qdf("\n ").write(" /First ").write(first); | |
| 1723 | 1713 | if (!object.isNull()) { |
| 1724 | 1714 | // If the original object has an /Extends key, preserve it. |
| 1725 | 1715 | QPDFObjectHandle dict = object.getDict(); |
| 1726 | 1716 | QPDFObjectHandle extends = dict.getKey("/Extends"); |
| 1727 | 1717 | if (extends.isIndirect()) { |
| 1728 | 1718 | QTC::TC("qpdf", "QPDFWriter copy Extends"); |
| 1729 | - writeStringQDF("\n "); | |
| 1730 | - writeString(" /Extends "); | |
| 1719 | + write_qdf("\n ").write(" /Extends "); | |
| 1731 | 1720 | unparseChild(extends, 1, f_in_ostream); |
| 1732 | 1721 | } |
| 1733 | 1722 | } |
| 1734 | - writeStringQDF("\n"); | |
| 1735 | - writeStringNoQDF(" "); | |
| 1736 | - writeString(">>\nstream\n"); | |
| 1723 | + write_qdf("\n").write_no_qdf(" ").write(">>\nstream\n"); | |
| 1737 | 1724 | if (m->encryption) { |
| 1738 | 1725 | QTC::TC("qpdf", "QPDFWriter encrypt object stream"); |
| 1739 | 1726 | } |
| 1740 | 1727 | { |
| 1741 | 1728 | PipelinePopper pp_enc(this); |
| 1742 | 1729 | pushEncryptionFilter(pp_enc); |
| 1743 | - writeString(stream_buffer_pass2); | |
| 1730 | + write(stream_buffer_pass2); | |
| 1744 | 1731 | } |
| 1745 | 1732 | if (m->newline_before_endstream) { |
| 1746 | - writeString("\n"); | |
| 1733 | + write("\n"); | |
| 1747 | 1734 | } |
| 1748 | - writeString("endstream"); | |
| 1735 | + write("endstream"); | |
| 1749 | 1736 | m->cur_data_key.clear(); |
| 1750 | 1737 | closeObject(new_stream_id); |
| 1751 | 1738 | } |
| ... | ... | @@ -1755,8 +1742,8 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index) |
| 1755 | 1742 | { |
| 1756 | 1743 | QPDFObjGen old_og = object.getObjGen(); |
| 1757 | 1744 | |
| 1758 | - if ((object_stream_index == -1) && (old_og.getGen() == 0) && | |
| 1759 | - (m->object_stream_to_objects.count(old_og.getObj()))) { | |
| 1745 | + if (object_stream_index == -1 && old_og.getGen() == 0 && | |
| 1746 | + m->object_stream_to_objects.contains(old_og.getObj())) { | |
| 1760 | 1747 | writeObjectStream(object); |
| 1761 | 1748 | return; |
| 1762 | 1749 | } |
| ... | ... | @@ -1765,19 +1752,15 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index) |
| 1765 | 1752 | auto new_id = m->obj[old_og].renumber; |
| 1766 | 1753 | if (m->qdf_mode) { |
| 1767 | 1754 | 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"); | |
| 1755 | + write("%% Page ").write(m->page_object_to_seq[old_og]).write("\n"); | |
| 1771 | 1756 | } |
| 1772 | 1757 | 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"); | |
| 1758 | + write("%% Contents for page ").write(m->contents_to_page_seq[old_og]).write("\n"); | |
| 1776 | 1759 | } |
| 1777 | 1760 | } |
| 1778 | 1761 | if (object_stream_index == -1) { |
| 1779 | 1762 | if (m->qdf_mode && (!m->suppress_original_object_ids)) { |
| 1780 | - writeString("%% Original object ID: " + object.getObjGen().unparse(' ') + "\n"); | |
| 1763 | + write("%% Original object ID: ").write(object.getObjGen().unparse(' ')).write("\n"); | |
| 1781 | 1764 | } |
| 1782 | 1765 | openObject(new_id); |
| 1783 | 1766 | setDataKey(new_id); |
| ... | ... | @@ -1786,17 +1769,17 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index) |
| 1786 | 1769 | closeObject(new_id); |
| 1787 | 1770 | } else { |
| 1788 | 1771 | unparseObject(object, 0, f_in_ostream); |
| 1789 | - writeString("\n"); | |
| 1772 | + write("\n"); | |
| 1790 | 1773 | } |
| 1791 | 1774 | |
| 1792 | - if ((!m->direct_stream_lengths) && object.isStream()) { | |
| 1775 | + if (!m->direct_stream_lengths && object.isStream()) { | |
| 1793 | 1776 | if (m->qdf_mode) { |
| 1794 | 1777 | if (m->added_newline) { |
| 1795 | - writeString("%QDF: ignore_newline\n"); | |
| 1778 | + write("%QDF: ignore_newline\n"); | |
| 1796 | 1779 | } |
| 1797 | 1780 | } |
| 1798 | 1781 | openObject(new_id + 1); |
| 1799 | - writeString(std::to_string(m->cur_stream_length)); | |
| 1782 | + write(m->cur_stream_length); | |
| 1800 | 1783 | closeObject(new_id + 1); |
| 1801 | 1784 | } |
| 1802 | 1785 | } |
| ... | ... | @@ -2274,46 +2257,36 @@ QPDFWriter::writeEncryptionDictionary() |
| 2274 | 2257 | auto& enc = *m->encryption; |
| 2275 | 2258 | auto const V = enc.getV(); |
| 2276 | 2259 | |
| 2277 | - writeString("<<"); | |
| 2260 | + write("<<"); | |
| 2278 | 2261 | if (V >= 4) { |
| 2279 | - writeString(" /CF << /StdCF << /AuthEvent /DocOpen /CFM "); | |
| 2280 | - writeString(m->encrypt_use_aes ? ((V < 5) ? "/AESV2" : "/AESV3") : "/V2"); | |
| 2262 | + write(" /CF << /StdCF << /AuthEvent /DocOpen /CFM "); | |
| 2263 | + write(m->encrypt_use_aes ? ((V < 5) ? "/AESV2" : "/AESV3") : "/V2"); | |
| 2281 | 2264 | // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of |
| 2282 | 2265 | // MacOS won't open encrypted files without it. |
| 2283 | - writeString((V < 5) ? " /Length 16 >> >>" : " /Length 32 >> >>"); | |
| 2266 | + write((V < 5) ? " /Length 16 >> >>" : " /Length 32 >> >>"); | |
| 2284 | 2267 | if (!m->encryption->getEncryptMetadata()) { |
| 2285 | - writeString(" /EncryptMetadata false"); | |
| 2268 | + write(" /EncryptMetadata false"); | |
| 2286 | 2269 | } |
| 2287 | 2270 | } |
| 2288 | - writeString(" /Filter /Standard /Length "); | |
| 2289 | - writeString(std::to_string(enc.getLengthBytes() * 8)); | |
| 2290 | - writeString(" /O "); | |
| 2291 | - writeString(QPDF_String(enc.getO()).unparse(true)); | |
| 2271 | + write(" /Filter /Standard /Length ").write(enc.getLengthBytes() * 8); | |
| 2272 | + write(" /O ").write_string(enc.getO(), true); | |
| 2292 | 2273 | if (V >= 4) { |
| 2293 | - writeString(" /OE "); | |
| 2294 | - writeString(QPDF_String(enc.getOE()).unparse(true)); | |
| 2274 | + write(" /OE ").write_string(enc.getOE(), true); | |
| 2295 | 2275 | } |
| 2296 | - writeString(" /P "); | |
| 2297 | - writeString(std::to_string(enc.getP())); | |
| 2276 | + write(" /P ").write(enc.getP()); | |
| 2298 | 2277 | if (V >= 5) { |
| 2299 | - writeString(" /Perms "); | |
| 2300 | - writeString(QPDF_String(enc.getPerms()).unparse(true)); | |
| 2278 | + write(" /Perms ").write_string(enc.getPerms(), true); | |
| 2301 | 2279 | } |
| 2302 | - writeString(" /R "); | |
| 2303 | - writeString(std::to_string(enc.getR())); | |
| 2280 | + write(" /R ").write(enc.getR()); | |
| 2304 | 2281 | |
| 2305 | 2282 | if (V >= 4) { |
| 2306 | - writeString(" /StmF /StdCF /StrF /StdCF"); | |
| 2283 | + write(" /StmF /StdCF /StrF /StdCF"); | |
| 2307 | 2284 | } |
| 2308 | - writeString(" /U "); | |
| 2309 | - writeString(QPDF_String(enc.getU()).unparse(true)); | |
| 2285 | + write(" /U ").write_string(enc.getU(), true); | |
| 2310 | 2286 | if (V >= 4) { |
| 2311 | - writeString(" /UE "); | |
| 2312 | - writeString(QPDF_String(enc.getUE()).unparse(true)); | |
| 2287 | + write(" /UE ").write_string(enc.getUE(), true); | |
| 2313 | 2288 | } |
| 2314 | - writeString(" /V "); | |
| 2315 | - writeString(std::to_string(enc.getV())); | |
| 2316 | - writeString(" >>"); | |
| 2289 | + write(" /V ").write(enc.getV()).write(" >>"); | |
| 2317 | 2290 | closeObject(m->encryption_dict_objid); |
| 2318 | 2291 | } |
| 2319 | 2292 | |
| ... | ... | @@ -2327,17 +2300,16 @@ QPDFWriter::getFinalVersion() |
| 2327 | 2300 | void |
| 2328 | 2301 | QPDFWriter::writeHeader() |
| 2329 | 2302 | { |
| 2330 | - writeString("%PDF-"); | |
| 2331 | - writeString(m->final_pdf_version); | |
| 2303 | + write("%PDF-").write(m->final_pdf_version); | |
| 2332 | 2304 | if (m->pclm) { |
| 2333 | 2305 | // PCLm version |
| 2334 | - writeString("\n%PCLm 1.0\n"); | |
| 2306 | + write("\n%PCLm 1.0\n"); | |
| 2335 | 2307 | } else { |
| 2336 | 2308 | // This string of binary characters would not be valid UTF-8, so it really should be treated |
| 2337 | 2309 | // as binary. |
| 2338 | - writeString("\n%\xbf\xf7\xa2\xfe\n"); | |
| 2310 | + write("\n%\xbf\xf7\xa2\xfe\n"); | |
| 2339 | 2311 | } |
| 2340 | - writeStringQDF("%QDF-1.0\n\n"); | |
| 2312 | + write_qdf("%QDF-1.0\n\n"); | |
| 2341 | 2313 | |
| 2342 | 2314 | // Note: do not write extra header text here. Linearized PDFs must include the entire |
| 2343 | 2315 | // linearization parameter dictionary within the first 1024 characters of the PDF file, so for |
| ... | ... | @@ -2351,7 +2323,7 @@ QPDFWriter::writeHintStream(int hint_id) |
| 2351 | 2323 | std::string hint_buffer; |
| 2352 | 2324 | int S = 0; |
| 2353 | 2325 | int O = 0; |
| 2354 | - bool compressed = (m->compress_streams && !m->qdf_mode); | |
| 2326 | + bool compressed = m->compress_streams && !m->qdf_mode; | |
| 2355 | 2327 | QPDF::Writer::generateHintStream(m->pdf, m->new_obj, m->obj, hint_buffer, S, O, compressed); |
| 2356 | 2328 | |
| 2357 | 2329 | openObject(hint_id); |
| ... | ... | @@ -2359,20 +2331,17 @@ QPDFWriter::writeHintStream(int hint_id) |
| 2359 | 2331 | |
| 2360 | 2332 | size_t hlen = hint_buffer.size(); |
| 2361 | 2333 | |
| 2362 | - writeString("<< "); | |
| 2334 | + write("<< "); | |
| 2363 | 2335 | if (compressed) { |
| 2364 | - writeString("/Filter /FlateDecode "); | |
| 2336 | + write("/Filter /FlateDecode "); | |
| 2365 | 2337 | } |
| 2366 | - writeString("/S "); | |
| 2367 | - writeString(std::to_string(S)); | |
| 2338 | + write("/S ").write(S); | |
| 2368 | 2339 | if (O) { |
| 2369 | - writeString(" /O "); | |
| 2370 | - writeString(std::to_string(O)); | |
| 2340 | + write(" /O ").write(O); | |
| 2371 | 2341 | } |
| 2372 | - writeString(" /Length "); | |
| 2373 | 2342 | adjustAESStreamLength(hlen); |
| 2374 | - writeString(std::to_string(hlen)); | |
| 2375 | - writeString(" >>\nstream\n"); | |
| 2343 | + write(" /Length ").write(hlen); | |
| 2344 | + write(" >>\nstream\n"); | |
| 2376 | 2345 | |
| 2377 | 2346 | if (m->encryption) { |
| 2378 | 2347 | QTC::TC("qpdf", "QPDFWriter encrypted hint stream"); |
| ... | ... | @@ -2381,13 +2350,13 @@ QPDFWriter::writeHintStream(int hint_id) |
| 2381 | 2350 | { |
| 2382 | 2351 | PipelinePopper pp_enc(this); |
| 2383 | 2352 | pushEncryptionFilter(pp_enc); |
| 2384 | - writeString(hint_buffer); | |
| 2353 | + write(hint_buffer); | |
| 2385 | 2354 | } |
| 2386 | 2355 | |
| 2387 | 2356 | if (last_char != '\n') { |
| 2388 | - writeString("\n"); | |
| 2357 | + write("\n"); | |
| 2389 | 2358 | } |
| 2390 | - writeString("endstream"); | |
| 2359 | + write("endstream"); | |
| 2391 | 2360 | closeObject(hint_id); |
| 2392 | 2361 | } |
| 2393 | 2362 | |
| ... | ... | @@ -2412,29 +2381,25 @@ QPDFWriter::writeXRefTable( |
| 2412 | 2381 | qpdf_offset_t hint_length, |
| 2413 | 2382 | int linearization_pass) |
| 2414 | 2383 | { |
| 2415 | - writeString("xref\n"); | |
| 2416 | - writeString(std::to_string(first)); | |
| 2417 | - writeString(" "); | |
| 2418 | - writeString(std::to_string(last - first + 1)); | |
| 2384 | + write("xref\n").write(first).write(" ").write(last - first + 1); | |
| 2419 | 2385 | qpdf_offset_t space_before_zero = m->pipeline->getCount(); |
| 2420 | - writeString("\n"); | |
| 2386 | + write("\n"); | |
| 2387 | + if (first == 0) { | |
| 2388 | + write("0000000000 65535 f \n"); | |
| 2389 | + ++first; | |
| 2390 | + } | |
| 2421 | 2391 | 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 | - } | |
| 2392 | + qpdf_offset_t offset = 0; | |
| 2393 | + if (!suppress_offsets) { | |
| 2394 | + offset = m->new_obj[i].xref.getOffset(); | |
| 2395 | + if ((hint_id != 0) && (i != hint_id) && (offset >= hint_offset)) { | |
| 2396 | + offset += hint_length; | |
| 2431 | 2397 | } |
| 2432 | - writeString(QUtil::int_to_string(offset, 10)); | |
| 2433 | - writeString(" 00000 n \n"); | |
| 2434 | 2398 | } |
| 2399 | + write(QUtil::int_to_string(offset, 10)).write(" 00000 n \n"); | |
| 2435 | 2400 | } |
| 2436 | 2401 | writeTrailer(which, size, false, prev, linearization_pass); |
| 2437 | - writeString("\n"); | |
| 2402 | + write("\n"); | |
| 2438 | 2403 | return space_before_zero; |
| 2439 | 2404 | } |
| 2440 | 2405 | |
| ... | ... | @@ -2532,27 +2497,18 @@ QPDFWriter::writeXRefStream( |
| 2532 | 2497 | } |
| 2533 | 2498 | |
| 2534 | 2499 | openObject(xref_id); |
| 2535 | - writeString("<<"); | |
| 2536 | - writeStringQDF("\n "); | |
| 2537 | - writeString(" /Type /XRef"); | |
| 2538 | - writeStringQDF("\n "); | |
| 2539 | - writeString(" /Length " + std::to_string(xref_data.size())); | |
| 2500 | + write("<<").write_qdf("\n ").write(" /Type /XRef").write_qdf("\n "); | |
| 2501 | + write(" /Length ").write(xref_data.size()); | |
| 2540 | 2502 | if (compressed) { |
| 2541 | - writeStringQDF("\n "); | |
| 2542 | - writeString(" /Filter /FlateDecode"); | |
| 2543 | - writeStringQDF("\n "); | |
| 2544 | - writeString(" /DecodeParms << /Columns " + std::to_string(esize) + " /Predictor 12 >>"); | |
| 2503 | + write_qdf("\n ").write(" /Filter /FlateDecode").write_qdf("\n "); | |
| 2504 | + write(" /DecodeParms << /Columns ").write(esize).write(" /Predictor 12 >>"); | |
| 2545 | 2505 | } |
| 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) + " ]"); | |
| 2506 | + write_qdf("\n ").write(" /W [ 1 ").write(f1_size).write(" ").write(f2_size).write(" ]"); | |
| 2507 | + if (!(first == 0 && last == (size - 1))) { | |
| 2508 | + write(" /Index [ ").write(first).write(" ").write(last - first + 1).write(" ]"); | |
| 2551 | 2509 | } |
| 2552 | 2510 | writeTrailer(which, size, true, prev, linearization_pass); |
| 2553 | - writeString("\nstream\n"); | |
| 2554 | - writeString(xref_data); | |
| 2555 | - writeString("\nendstream"); | |
| 2511 | + write("\nstream\n").write(xref_data).write("\nendstream"); | |
| 2556 | 2512 | closeObject(xref_id); |
| 2557 | 2513 | return space_before_zero; |
| 2558 | 2514 | } |
| ... | ... | @@ -2726,37 +2682,28 @@ QPDFWriter::writeLinearized() |
| 2726 | 2682 | |
| 2727 | 2683 | qpdf_offset_t pos = m->pipeline->getCount(); |
| 2728 | 2684 | openObject(lindict_id); |
| 2729 | - writeString("<<"); | |
| 2685 | + write("<<"); | |
| 2730 | 2686 | if (pass == 2) { |
| 2731 | 2687 | std::vector<QPDFObjectHandle> const& pages = m->pdf.getAllPages(); |
| 2732 | 2688 | int first_page_object = m->obj[pages.at(0)].renumber; |
| 2733 | - int npages = QIntC::to_int(pages.size()); | |
| 2734 | 2689 | |
| 2735 | - writeString(" /Linearized 1 /L "); | |
| 2736 | - writeString(std::to_string(file_size + hint_length)); | |
| 2690 | + write(" /Linearized 1 /L ").write(file_size + hint_length); | |
| 2737 | 2691 | // 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(" >>"); | |
| 2692 | + write(" /H [ ").write(m->new_obj[hint_id].xref.getOffset()).write(" "); | |
| 2693 | + write(hint_length); | |
| 2694 | + write(" ] /O ").write(first_page_object); | |
| 2695 | + write(" /E ").write(part6_end_offset + hint_length); | |
| 2696 | + write(" /N ").write(pages.size()); | |
| 2697 | + write(" /T ").write(space_before_zero + hint_length); | |
| 2698 | + } | |
| 2699 | + write(" >>"); | |
| 2752 | 2700 | closeObject(lindict_id); |
| 2753 | 2701 | static int const pad = 200; |
| 2754 | - writePad(QIntC::to_size(pos - m->pipeline->getCount() + pad)); | |
| 2755 | - writeString("\n"); | |
| 2702 | + write(QIntC::to_size(pos - m->pipeline->getCount() + pad), ' ').write("\n"); | |
| 2756 | 2703 | |
| 2757 | 2704 | // If the user supplied any additional header text, write it here after the linearization |
| 2758 | 2705 | // parameter dictionary. |
| 2759 | - writeString(m->extra_header_text); | |
| 2706 | + write(m->extra_header_text); | |
| 2760 | 2707 | |
| 2761 | 2708 | // Part 3: first page cross reference table and trailer. |
| 2762 | 2709 | |
| ... | ... | @@ -2793,20 +2740,19 @@ QPDFWriter::writeLinearized() |
| 2793 | 2740 | qpdf_offset_t endpos = m->pipeline->getCount(); |
| 2794 | 2741 | if (pass == 1) { |
| 2795 | 2742 | // Pad so we have enough room for the real xref stream. |
| 2796 | - writePad(calculateXrefStreamPadding(endpos - pos)); | |
| 2743 | + write(calculateXrefStreamPadding(endpos - pos), ' '); | |
| 2797 | 2744 | first_xref_end = m->pipeline->getCount(); |
| 2798 | 2745 | } else { |
| 2799 | 2746 | // Pad so that the next object starts at the same place as in pass 1. |
| 2800 | - writePad(QIntC::to_size(first_xref_end - endpos)); | |
| 2747 | + write(QIntC::to_size(first_xref_end - endpos), ' '); | |
| 2801 | 2748 | |
| 2802 | 2749 | if (m->pipeline->getCount() != first_xref_end) { |
| 2803 | 2750 | throw std::logic_error( |
| 2804 | - "insufficient padding for first pass xref stream; " | |
| 2805 | - "first_xref_end=" + | |
| 2751 | + "insufficient padding for first pass xref stream; first_xref_end=" + | |
| 2806 | 2752 | std::to_string(first_xref_end) + "; endpos=" + std::to_string(endpos)); |
| 2807 | 2753 | } |
| 2808 | 2754 | } |
| 2809 | - writeString("\n"); | |
| 2755 | + write("\n"); | |
| 2810 | 2756 | } else { |
| 2811 | 2757 | writeXRefTable( |
| 2812 | 2758 | t_lin_first, |
| ... | ... | @@ -2819,7 +2765,7 @@ QPDFWriter::writeLinearized() |
| 2819 | 2765 | hint_offset, |
| 2820 | 2766 | hint_length, |
| 2821 | 2767 | pass); |
| 2822 | - writeString("startxref\n0\n%%EOF\n"); | |
| 2768 | + write("startxref\n0\n%%EOF\n"); | |
| 2823 | 2769 | } |
| 2824 | 2770 | |
| 2825 | 2771 | // Parts 4 through 9 |
| ... | ... | @@ -2837,7 +2783,7 @@ QPDFWriter::writeLinearized() |
| 2837 | 2783 | m->new_obj[hint_id].xref = QPDFXRefEntry(m->pipeline->getCount()); |
| 2838 | 2784 | } else { |
| 2839 | 2785 | // Part 5: hint stream |
| 2840 | - writeString(hint_buffer); | |
| 2786 | + write(hint_buffer); | |
| 2841 | 2787 | } |
| 2842 | 2788 | } |
| 2843 | 2789 | if (cur_object.getObjectID() == part6_end_marker) { |
| ... | ... | @@ -2871,14 +2817,13 @@ QPDFWriter::writeLinearized() |
| 2871 | 2817 | if (pass == 1) { |
| 2872 | 2818 | // Pad so we have enough room for the real xref stream. See comments for previous |
| 2873 | 2819 | // xref stream on how we calculate the padding. |
| 2874 | - writePad(calculateXrefStreamPadding(endpos - pos)); | |
| 2875 | - writeString("\n"); | |
| 2820 | + write(calculateXrefStreamPadding(endpos - pos), ' ').write("\n"); | |
| 2876 | 2821 | second_xref_end = m->pipeline->getCount(); |
| 2877 | 2822 | } else { |
| 2878 | 2823 | // Make the file size the same. |
| 2879 | - writePad( | |
| 2880 | - QIntC::to_size(second_xref_end + hint_length - 1 - m->pipeline->getCount())); | |
| 2881 | - writeString("\n"); | |
| 2824 | + auto padding = | |
| 2825 | + QIntC::to_size(second_xref_end + hint_length - 1 - m->pipeline->getCount()); | |
| 2826 | + write(padding, ' ').write("\n"); | |
| 2882 | 2827 | |
| 2883 | 2828 | // If this assertion fails, maybe we didn't have enough padding above. |
| 2884 | 2829 | if (m->pipeline->getCount() != second_xref_end + hint_length) { |
| ... | ... | @@ -2890,9 +2835,7 @@ QPDFWriter::writeLinearized() |
| 2890 | 2835 | space_before_zero = writeXRefTable( |
| 2891 | 2836 | t_lin_second, 0, second_half_end, second_trailer_size, 0, false, 0, 0, 0, pass); |
| 2892 | 2837 | } |
| 2893 | - writeString("startxref\n"); | |
| 2894 | - writeString(std::to_string(first_xref_offset)); | |
| 2895 | - writeString("\n%%EOF\n"); | |
| 2838 | + write("startxref\n").write(first_xref_offset).write("\n%%EOF\n"); | |
| 2896 | 2839 | |
| 2897 | 2840 | if (pass == 1) { |
| 2898 | 2841 | if (m->deterministic_id) { |
| ... | ... | @@ -3038,7 +2981,7 @@ QPDFWriter::writeStandard() |
| 3038 | 2981 | // Start writing |
| 3039 | 2982 | |
| 3040 | 2983 | writeHeader(); |
| 3041 | - writeString(m->extra_header_text); | |
| 2984 | + write(m->extra_header_text); | |
| 3042 | 2985 | |
| 3043 | 2986 | if (m->pclm) { |
| 3044 | 2987 | enqueueObjectsPCLm(); |
| ... | ... | @@ -3069,9 +3012,7 @@ QPDFWriter::writeStandard() |
| 3069 | 3012 | writeXRefStream( |
| 3070 | 3013 | xref_id, xref_id, xref_offset, t_normal, 0, m->next_objid - 1, m->next_objid); |
| 3071 | 3014 | } |
| 3072 | - writeString("startxref\n"); | |
| 3073 | - writeString(std::to_string(xref_offset)); | |
| 3074 | - writeString("\n%%EOF\n"); | |
| 3015 | + write("startxref\n").write(xref_offset).write("\n%%EOF\n"); | |
| 3075 | 3016 | |
| 3076 | 3017 | if (m->deterministic_id) { |
| 3077 | 3018 | QTC::TC( | ... | ... |
libqpdf/qpdf/Pipeline_private.hh
| ... | ... | @@ -109,13 +109,36 @@ namespace qpdf::pl |
| 109 | 109 | write(unsigned char const* buf, size_t len) final |
| 110 | 110 | { |
| 111 | 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 | 135 | if (str) { |
| 113 | - str->append(reinterpret_cast<char const*>(buf), len); | |
| 136 | + str->append(len, c); | |
| 114 | 137 | return; |
| 115 | 138 | } |
| 116 | 139 | count += static_cast<qpdf_offset_t>(len); |
| 117 | 140 | if (pass_immediately_to_next) { |
| 118 | - next()->write(buf, len); | |
| 141 | + next()->writeString(std::string(len, c)); | |
| 119 | 142 | } |
| 120 | 143 | } |
| 121 | 144 | } | ... | ... |