Commit 485399c71403f49f4becd625b0df6e17ce59f439
1 parent
7d1ba7da
Refactor `QPDFWriter` to consolidate `write*` methods into parameterized `write`…
… overloads, streamline QDF handling, and simplify string and trailer writes.
Showing
3 changed files
with
199 additions
and
246 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,13 @@ 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 | + template <typename... Args> | |
| 489 | + QPDFWriter& write_qdf(Args&&... args); | |
| 490 | + template <typename... Args> | |
| 491 | + QPDFWriter& write_no_qdf(Args&&... args); | |
| 488 | 492 | void assignCompressedObjectNumbers(QPDFObjGen og); |
| 489 | 493 | void enqueueObject(QPDFObjectHandle object); |
| 490 | 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 | 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 | 876 | Pipeline* |
| ... | ... | @@ -1005,8 +1018,7 @@ QPDFWriter::openObject(int objid) |
| 1005 | 1018 | objid = m->next_objid++; |
| 1006 | 1019 | } |
| 1007 | 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 | 1022 | return objid; |
| 1011 | 1023 | } |
| 1012 | 1024 | |
| ... | ... | @@ -1014,8 +1026,7 @@ void |
| 1014 | 1026 | QPDFWriter::closeObject(int objid) |
| 1015 | 1027 | { |
| 1016 | 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 | 1030 | auto& new_obj = m->new_obj[objid]; |
| 1020 | 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 | 1125 | enqueueObject(child); |
| 1115 | 1126 | } |
| 1116 | 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 | 1129 | } else { |
| 1120 | 1130 | unparseObject(child, level, flags); |
| 1121 | 1131 | } |
| ... | ... | @@ -1129,78 +1139,63 @@ QPDFWriter::writeTrailer( |
| 1129 | 1139 | if (xref_stream) { |
| 1130 | 1140 | m->cur_data_key.clear(); |
| 1131 | 1141 | } else { |
| 1132 | - writeString("trailer <<"); | |
| 1142 | + write("trailer <<"); | |
| 1133 | 1143 | } |
| 1134 | - writeStringQDF("\n"); | |
| 1144 | + write_qdf("\n"); | |
| 1135 | 1145 | if (which == t_lin_second) { |
| 1136 | - writeString(" /Size "); | |
| 1137 | - writeString(std::to_string(size)); | |
| 1146 | + write(" /Size ").write(size); | |
| 1138 | 1147 | } else { |
| 1139 | 1148 | for (auto const& [key, value]: trailer.as_dictionary()) { |
| 1140 | 1149 | if (value.null()) { |
| 1141 | 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 | 1153 | if (key == "/Size") { |
| 1148 | - writeString(std::to_string(size)); | |
| 1154 | + write(size); | |
| 1149 | 1155 | if (which == t_lin_first) { |
| 1150 | - writeString(" /Prev "); | |
| 1156 | + write(" /Prev "); | |
| 1151 | 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 | 1160 | } else { |
| 1156 | 1161 | unparseChild(value, 1, 0); |
| 1157 | 1162 | } |
| 1158 | - writeStringQDF("\n"); | |
| 1163 | + write_qdf("\n"); | |
| 1159 | 1164 | } |
| 1160 | 1165 | } |
| 1161 | 1166 | |
| 1162 | 1167 | // Write ID |
| 1163 | - writeStringQDF(" "); | |
| 1164 | - writeString(" /ID ["); | |
| 1168 | + write_qdf(" ").write(" /ID ["); | |
| 1165 | 1169 | if (linearization_pass == 1) { |
| 1166 | 1170 | std::string original_id1 = getOriginalID1(); |
| 1167 | 1171 | if (original_id1.empty()) { |
| 1168 | - writeString("<00000000000000000000000000000000>"); | |
| 1172 | + write("<00000000000000000000000000000000>"); | |
| 1169 | 1173 | } else { |
| 1170 | 1174 | // Write a string of zeroes equal in length to the representation of the original ID. |
| 1171 | 1175 | // While writing the original ID would have the same number of bytes, it would cause a |
| 1172 | 1176 | // change to the deterministic ID generated by older versions of the software that |
| 1173 | 1177 | // hard-coded the length of the ID to 16 bytes. |
| 1174 | - writeString("<"); | |
| 1175 | 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 | 1182 | } else { |
| 1183 | - if ((linearization_pass == 0) && (m->deterministic_id)) { | |
| 1183 | + if (linearization_pass == 0 && m->deterministic_id) { | |
| 1184 | 1184 | computeDeterministicIDData(); |
| 1185 | 1185 | } |
| 1186 | 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 | 1191 | if (which != t_lin_second) { |
| 1193 | 1192 | // Write reference to encryption dictionary |
| 1194 | 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 | 1201 | bool |
| ... | ... | @@ -1326,14 +1321,12 @@ QPDFWriter::unparseObject( |
| 1326 | 1321 | // Note: PDF spec 1.4 implementation note 121 states that Acrobat requires a space after the |
| 1327 | 1322 | // [ in the /H key of the linearization parameter dictionary. We'll do this unconditionally |
| 1328 | 1323 | // for all arrays because it looks nicer and doesn't make the files that much bigger. |
| 1329 | - writeString("["); | |
| 1324 | + write("["); | |
| 1330 | 1325 | for (auto const& item: object.as_array()) { |
| 1331 | - writeString(indent); | |
| 1332 | - writeStringQDF(" "); | |
| 1326 | + write(indent).write_qdf(" "); | |
| 1333 | 1327 | unparseChild(item, level + 1, child_flags); |
| 1334 | 1328 | } |
| 1335 | - writeString(indent); | |
| 1336 | - writeString("]"); | |
| 1329 | + write(indent).write("]"); | |
| 1337 | 1330 | } else if (tc == ::ot_dictionary) { |
| 1338 | 1331 | // Handle special cases for specific dictionaries. |
| 1339 | 1332 | |
| ... | ... | @@ -1474,14 +1467,11 @@ QPDFWriter::unparseObject( |
| 1474 | 1467 | } |
| 1475 | 1468 | } |
| 1476 | 1469 | |
| 1477 | - writeString("<<"); | |
| 1470 | + write("<<"); | |
| 1478 | 1471 | |
| 1479 | 1472 | for (auto const& [key, value]: object.as_dictionary()) { |
| 1480 | 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 | 1475 | if (key == "/Contents" && object.isDictionaryOfType("/Sig") && |
| 1486 | 1476 | object.hasKey("/ByteRange")) { |
| 1487 | 1477 | QTC::TC("qpdf", "QPDFWriter no encryption sig contents"); |
| ... | ... | @@ -1493,25 +1483,19 @@ QPDFWriter::unparseObject( |
| 1493 | 1483 | } |
| 1494 | 1484 | |
| 1495 | 1485 | if (flags & f_stream) { |
| 1496 | - writeString(indent); | |
| 1497 | - writeStringQDF(" "); | |
| 1498 | - writeString("/Length "); | |
| 1486 | + write(indent).write_qdf(" ").write("/Length "); | |
| 1499 | 1487 | |
| 1500 | 1488 | if (m->direct_stream_lengths) { |
| 1501 | - writeString(std::to_string(stream_length)); | |
| 1489 | + write(stream_length); | |
| 1502 | 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 | 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 | 1499 | } else if (tc == ::ot_stream) { |
| 1516 | 1500 | // Write stream data to a buffer. |
| 1517 | 1501 | if (!m->direct_stream_lengths) { |
| ... | ... | @@ -1535,18 +1519,18 @@ QPDFWriter::unparseObject( |
| 1535 | 1519 | adjustAESStreamLength(m->cur_stream_length); |
| 1536 | 1520 | unparseObject(stream_dict, 0, flags, m->cur_stream_length, compress_stream); |
| 1537 | 1521 | char last_char = stream_data.empty() ? '\0' : stream_data.back(); |
| 1538 | - writeString("\nstream\n"); | |
| 1522 | + write("\nstream\n"); | |
| 1539 | 1523 | { |
| 1540 | 1524 | PipelinePopper pp_enc(this); |
| 1541 | 1525 | pushEncryptionFilter(pp_enc); |
| 1542 | - writeString(stream_data); | |
| 1526 | + write(stream_data); | |
| 1543 | 1527 | } |
| 1544 | 1528 | |
| 1545 | 1529 | if ((m->added_newline = |
| 1546 | 1530 | m->newline_before_endstream || (m->qdf_mode && last_char != '\n'))) { |
| 1547 | - writeString("\nendstream"); | |
| 1531 | + write("\nendstream"); | |
| 1548 | 1532 | } else { |
| 1549 | - writeString("endstream"); | |
| 1533 | + write("endstream"); | |
| 1550 | 1534 | } |
| 1551 | 1535 | } else if (tc == ::ot_string) { |
| 1552 | 1536 | std::string val; |
| ... | ... | @@ -1580,9 +1564,9 @@ QPDFWriter::unparseObject( |
| 1580 | 1564 | } else { |
| 1581 | 1565 | val = object.unparseResolved(); |
| 1582 | 1566 | } |
| 1583 | - writeString(val); | |
| 1567 | + write(val); | |
| 1584 | 1568 | } else { |
| 1585 | - writeString(object.unparseResolved()); | |
| 1569 | + write(object.unparseResolved()); | |
| 1586 | 1570 | } |
| 1587 | 1571 | } |
| 1588 | 1572 | |
| ... | ... | @@ -1596,14 +1580,13 @@ QPDFWriter::writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int fi |
| 1596 | 1580 | if (is_first) { |
| 1597 | 1581 | is_first = false; |
| 1598 | 1582 | } else { |
| 1599 | - writeStringQDF("\n"); | |
| 1600 | - writeStringNoQDF(" "); | |
| 1583 | + write_qdf("\n").write_no_qdf(" "); | |
| 1601 | 1584 | } |
| 1602 | - writeString(id); | |
| 1585 | + write(id); | |
| 1603 | 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 | 1592 | void |
| ... | ... | @@ -1639,20 +1622,18 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1639 | 1622 | first_obj = new_obj; |
| 1640 | 1623 | } |
| 1641 | 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 | 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 | 1628 | // For compatibility, only write the generation if non-zero. While object |
| 1648 | 1629 | // streams only allow objects with generation 0, if we are generating object |
| 1649 | 1630 | // streams, the old object could have a non-zero generation. |
| 1650 | 1631 | if (obj.getGen() != 0) { |
| 1651 | 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 | 1639 | offsets.push_back(m->pipeline->getCount()); |
| ... | ... | @@ -1698,7 +1679,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1698 | 1679 | activatePipelineStack(pp_ostream, stream_buffer_pass2); |
| 1699 | 1680 | } |
| 1700 | 1681 | writeObjectStreamOffsets(offsets, first_obj); |
| 1701 | - writeString(stream_buffer_pass1); | |
| 1682 | + write(stream_buffer_pass1); | |
| 1702 | 1683 | stream_buffer_pass1.clear(); |
| 1703 | 1684 | stream_buffer_pass1.shrink_to_fit(); |
| 1704 | 1685 | } |
| ... | ... | @@ -1706,46 +1687,37 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) |
| 1706 | 1687 | // Write the object |
| 1707 | 1688 | openObject(new_stream_id); |
| 1708 | 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 | 1691 | size_t length = stream_buffer_pass2.size(); |
| 1714 | 1692 | adjustAESStreamLength(length); |
| 1715 | - writeString(" /Length " + std::to_string(length)); | |
| 1716 | - writeStringQDF("\n "); | |
| 1693 | + write(" /Length ").write(length).write_qdf("\n "); | |
| 1717 | 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 | 1698 | if (!object.isNull()) { |
| 1724 | 1699 | // If the original object has an /Extends key, preserve it. |
| 1725 | 1700 | QPDFObjectHandle dict = object.getDict(); |
| 1726 | 1701 | QPDFObjectHandle extends = dict.getKey("/Extends"); |
| 1727 | 1702 | if (extends.isIndirect()) { |
| 1728 | 1703 | QTC::TC("qpdf", "QPDFWriter copy Extends"); |
| 1729 | - writeStringQDF("\n "); | |
| 1730 | - writeString(" /Extends "); | |
| 1704 | + write_qdf("\n ").write(" /Extends "); | |
| 1731 | 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 | 1709 | if (m->encryption) { |
| 1738 | 1710 | QTC::TC("qpdf", "QPDFWriter encrypt object stream"); |
| 1739 | 1711 | } |
| 1740 | 1712 | { |
| 1741 | 1713 | PipelinePopper pp_enc(this); |
| 1742 | 1714 | pushEncryptionFilter(pp_enc); |
| 1743 | - writeString(stream_buffer_pass2); | |
| 1715 | + write(stream_buffer_pass2); | |
| 1744 | 1716 | } |
| 1745 | 1717 | if (m->newline_before_endstream) { |
| 1746 | - writeString("\n"); | |
| 1718 | + write("\n"); | |
| 1747 | 1719 | } |
| 1748 | - writeString("endstream"); | |
| 1720 | + write("endstream"); | |
| 1749 | 1721 | m->cur_data_key.clear(); |
| 1750 | 1722 | closeObject(new_stream_id); |
| 1751 | 1723 | } |
| ... | ... | @@ -1755,8 +1727,8 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index) |
| 1755 | 1727 | { |
| 1756 | 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 | 1732 | writeObjectStream(object); |
| 1761 | 1733 | return; |
| 1762 | 1734 | } |
| ... | ... | @@ -1765,19 +1737,15 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index) |
| 1765 | 1737 | auto new_id = m->obj[old_og].renumber; |
| 1766 | 1738 | if (m->qdf_mode) { |
| 1767 | 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 | 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 | 1746 | if (object_stream_index == -1) { |
| 1779 | 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 | 1750 | openObject(new_id); |
| 1783 | 1751 | setDataKey(new_id); |
| ... | ... | @@ -1786,17 +1754,17 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index) |
| 1786 | 1754 | closeObject(new_id); |
| 1787 | 1755 | } else { |
| 1788 | 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 | 1761 | if (m->qdf_mode) { |
| 1794 | 1762 | if (m->added_newline) { |
| 1795 | - writeString("%QDF: ignore_newline\n"); | |
| 1763 | + write("%QDF: ignore_newline\n"); | |
| 1796 | 1764 | } |
| 1797 | 1765 | } |
| 1798 | 1766 | openObject(new_id + 1); |
| 1799 | - writeString(std::to_string(m->cur_stream_length)); | |
| 1767 | + write(m->cur_stream_length); | |
| 1800 | 1768 | closeObject(new_id + 1); |
| 1801 | 1769 | } |
| 1802 | 1770 | } |
| ... | ... | @@ -2274,46 +2242,36 @@ QPDFWriter::writeEncryptionDictionary() |
| 2274 | 2242 | auto& enc = *m->encryption; |
| 2275 | 2243 | auto const V = enc.getV(); |
| 2276 | 2244 | |
| 2277 | - writeString("<<"); | |
| 2245 | + write("<<"); | |
| 2278 | 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 | 2249 | // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of |
| 2282 | 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 | 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 | 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 | 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 | 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 | 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 | 2275 | closeObject(m->encryption_dict_objid); |
| 2318 | 2276 | } |
| 2319 | 2277 | |
| ... | ... | @@ -2327,17 +2285,16 @@ QPDFWriter::getFinalVersion() |
| 2327 | 2285 | void |
| 2328 | 2286 | QPDFWriter::writeHeader() |
| 2329 | 2287 | { |
| 2330 | - writeString("%PDF-"); | |
| 2331 | - writeString(m->final_pdf_version); | |
| 2288 | + write("%PDF-").write(m->final_pdf_version); | |
| 2332 | 2289 | if (m->pclm) { |
| 2333 | 2290 | // PCLm version |
| 2334 | - writeString("\n%PCLm 1.0\n"); | |
| 2291 | + write("\n%PCLm 1.0\n"); | |
| 2335 | 2292 | } else { |
| 2336 | 2293 | // This string of binary characters would not be valid UTF-8, so it really should be treated |
| 2337 | 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 | 2299 | // Note: do not write extra header text here. Linearized PDFs must include the entire |
| 2343 | 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 | 2308 | std::string hint_buffer; |
| 2352 | 2309 | int S = 0; |
| 2353 | 2310 | int O = 0; |
| 2354 | - bool compressed = (m->compress_streams && !m->qdf_mode); | |
| 2311 | + bool compressed = m->compress_streams && !m->qdf_mode; | |
| 2355 | 2312 | QPDF::Writer::generateHintStream(m->pdf, m->new_obj, m->obj, hint_buffer, S, O, compressed); |
| 2356 | 2313 | |
| 2357 | 2314 | openObject(hint_id); |
| ... | ... | @@ -2359,20 +2316,17 @@ QPDFWriter::writeHintStream(int hint_id) |
| 2359 | 2316 | |
| 2360 | 2317 | size_t hlen = hint_buffer.size(); |
| 2361 | 2318 | |
| 2362 | - writeString("<< "); | |
| 2319 | + write("<< "); | |
| 2363 | 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 | 2324 | if (O) { |
| 2369 | - writeString(" /O "); | |
| 2370 | - writeString(std::to_string(O)); | |
| 2325 | + write(" /O ").write(O); | |
| 2371 | 2326 | } |
| 2372 | - writeString(" /Length "); | |
| 2373 | 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 | 2331 | if (m->encryption) { |
| 2378 | 2332 | QTC::TC("qpdf", "QPDFWriter encrypted hint stream"); |
| ... | ... | @@ -2381,13 +2335,13 @@ QPDFWriter::writeHintStream(int hint_id) |
| 2381 | 2335 | { |
| 2382 | 2336 | PipelinePopper pp_enc(this); |
| 2383 | 2337 | pushEncryptionFilter(pp_enc); |
| 2384 | - writeString(hint_buffer); | |
| 2338 | + write(hint_buffer); | |
| 2385 | 2339 | } |
| 2386 | 2340 | |
| 2387 | 2341 | if (last_char != '\n') { |
| 2388 | - writeString("\n"); | |
| 2342 | + write("\n"); | |
| 2389 | 2343 | } |
| 2390 | - writeString("endstream"); | |
| 2344 | + write("endstream"); | |
| 2391 | 2345 | closeObject(hint_id); |
| 2392 | 2346 | } |
| 2393 | 2347 | |
| ... | ... | @@ -2412,29 +2366,25 @@ QPDFWriter::writeXRefTable( |
| 2412 | 2366 | qpdf_offset_t hint_length, |
| 2413 | 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 | 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 | 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 | 2386 | writeTrailer(which, size, false, prev, linearization_pass); |
| 2437 | - writeString("\n"); | |
| 2387 | + write("\n"); | |
| 2438 | 2388 | return space_before_zero; |
| 2439 | 2389 | } |
| 2440 | 2390 | |
| ... | ... | @@ -2532,27 +2482,18 @@ QPDFWriter::writeXRefStream( |
| 2532 | 2482 | } |
| 2533 | 2483 | |
| 2534 | 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 | 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 | 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 | 2497 | closeObject(xref_id); |
| 2557 | 2498 | return space_before_zero; |
| 2558 | 2499 | } |
| ... | ... | @@ -2726,37 +2667,28 @@ QPDFWriter::writeLinearized() |
| 2726 | 2667 | |
| 2727 | 2668 | qpdf_offset_t pos = m->pipeline->getCount(); |
| 2728 | 2669 | openObject(lindict_id); |
| 2729 | - writeString("<<"); | |
| 2670 | + write("<<"); | |
| 2730 | 2671 | if (pass == 2) { |
| 2731 | 2672 | std::vector<QPDFObjectHandle> const& pages = m->pdf.getAllPages(); |
| 2732 | 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 | 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 | 2685 | closeObject(lindict_id); |
| 2753 | 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 | 2689 | // If the user supplied any additional header text, write it here after the linearization |
| 2758 | 2690 | // parameter dictionary. |
| 2759 | - writeString(m->extra_header_text); | |
| 2691 | + write(m->extra_header_text); | |
| 2760 | 2692 | |
| 2761 | 2693 | // Part 3: first page cross reference table and trailer. |
| 2762 | 2694 | |
| ... | ... | @@ -2793,20 +2725,19 @@ QPDFWriter::writeLinearized() |
| 2793 | 2725 | qpdf_offset_t endpos = m->pipeline->getCount(); |
| 2794 | 2726 | if (pass == 1) { |
| 2795 | 2727 | // Pad so we have enough room for the real xref stream. |
| 2796 | - writePad(calculateXrefStreamPadding(endpos - pos)); | |
| 2728 | + write(calculateXrefStreamPadding(endpos - pos), ' '); | |
| 2797 | 2729 | first_xref_end = m->pipeline->getCount(); |
| 2798 | 2730 | } else { |
| 2799 | 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 | 2734 | if (m->pipeline->getCount() != first_xref_end) { |
| 2803 | 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 | 2737 | std::to_string(first_xref_end) + "; endpos=" + std::to_string(endpos)); |
| 2807 | 2738 | } |
| 2808 | 2739 | } |
| 2809 | - writeString("\n"); | |
| 2740 | + write("\n"); | |
| 2810 | 2741 | } else { |
| 2811 | 2742 | writeXRefTable( |
| 2812 | 2743 | t_lin_first, |
| ... | ... | @@ -2819,7 +2750,7 @@ QPDFWriter::writeLinearized() |
| 2819 | 2750 | hint_offset, |
| 2820 | 2751 | hint_length, |
| 2821 | 2752 | pass); |
| 2822 | - writeString("startxref\n0\n%%EOF\n"); | |
| 2753 | + write("startxref\n0\n%%EOF\n"); | |
| 2823 | 2754 | } |
| 2824 | 2755 | |
| 2825 | 2756 | // Parts 4 through 9 |
| ... | ... | @@ -2837,7 +2768,7 @@ QPDFWriter::writeLinearized() |
| 2837 | 2768 | m->new_obj[hint_id].xref = QPDFXRefEntry(m->pipeline->getCount()); |
| 2838 | 2769 | } else { |
| 2839 | 2770 | // Part 5: hint stream |
| 2840 | - writeString(hint_buffer); | |
| 2771 | + write(hint_buffer); | |
| 2841 | 2772 | } |
| 2842 | 2773 | } |
| 2843 | 2774 | if (cur_object.getObjectID() == part6_end_marker) { |
| ... | ... | @@ -2871,14 +2802,13 @@ QPDFWriter::writeLinearized() |
| 2871 | 2802 | if (pass == 1) { |
| 2872 | 2803 | // Pad so we have enough room for the real xref stream. See comments for previous |
| 2873 | 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 | 2806 | second_xref_end = m->pipeline->getCount(); |
| 2877 | 2807 | } else { |
| 2878 | 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 | 2813 | // If this assertion fails, maybe we didn't have enough padding above. |
| 2884 | 2814 | if (m->pipeline->getCount() != second_xref_end + hint_length) { |
| ... | ... | @@ -2890,9 +2820,7 @@ QPDFWriter::writeLinearized() |
| 2890 | 2820 | space_before_zero = writeXRefTable( |
| 2891 | 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 | 2825 | if (pass == 1) { |
| 2898 | 2826 | if (m->deterministic_id) { |
| ... | ... | @@ -3038,7 +2966,7 @@ QPDFWriter::writeStandard() |
| 3038 | 2966 | // Start writing |
| 3039 | 2967 | |
| 3040 | 2968 | writeHeader(); |
| 3041 | - writeString(m->extra_header_text); | |
| 2969 | + write(m->extra_header_text); | |
| 3042 | 2970 | |
| 3043 | 2971 | if (m->pclm) { |
| 3044 | 2972 | enqueueObjectsPCLm(); |
| ... | ... | @@ -3069,9 +2997,7 @@ QPDFWriter::writeStandard() |
| 3069 | 2997 | writeXRefStream( |
| 3070 | 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 | 3002 | if (m->deterministic_id) { |
| 3077 | 3003 | 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 | } | ... | ... |