diff options
Diffstat (limited to 'libqpdf')
-rw-r--r-- | libqpdf/Pl_Buffer.cc | 6 | ||||
-rw-r--r-- | libqpdf/QPDF.cc | 37 | ||||
-rw-r--r-- | libqpdf/QPDF_Dictionary.cc | 6 | ||||
-rw-r--r-- | libqpdf/QPDF_pages.cc | 19 | ||||
-rw-r--r-- | libqpdf/QUtil.cc | 44 |
5 files changed, 82 insertions, 30 deletions
diff --git a/libqpdf/Pl_Buffer.cc b/libqpdf/Pl_Buffer.cc index b10cf544..766c04b5 100644 --- a/libqpdf/Pl_Buffer.cc +++ b/libqpdf/Pl_Buffer.cc @@ -19,7 +19,7 @@ Pl_Buffer::~Pl_Buffer() // NOLINT (modernize-use-equals-default) void Pl_Buffer::write(unsigned char const* buf, size_t len) { - m->data.append(buf, len); + m->data.insert(m->data.end(), buf, buf + len); m->ready = false; if (getNext(true)) { @@ -43,7 +43,7 @@ Pl_Buffer::getBuffer() throw std::logic_error("Pl_Buffer::getBuffer() called when not ready"); } - auto size = m->data.length(); + auto size = m->data.size(); auto* b = new Buffer(size); if (size > 0) { unsigned char* p = b->getBuffer(); @@ -65,7 +65,7 @@ Pl_Buffer::getMallocBuffer(unsigned char** buf, size_t* len) if (!m->ready) { throw std::logic_error("Pl_Buffer::getMallocBuffer() called when not ready"); } - auto size = m->data.length(); + auto size = m->data.size(); *len = size; if (size > 0) { *buf = reinterpret_cast<unsigned char*>(malloc(size)); diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index c5a7ab5d..e39f9b7a 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -1861,6 +1861,12 @@ QPDF::newReserved() } QPDFObjectHandle +QPDF::newIndirectNull() +{ + return makeIndirectFromQPDFObject(QPDF_Null::create()); +} + +QPDFObjectHandle QPDF::newStream() { return makeIndirectFromQPDFObject( @@ -1948,7 +1954,10 @@ void QPDF::replaceReserved(QPDFObjectHandle reserved, QPDFObjectHandle replacement) { QTC::TC("qpdf", "QPDF replaceReserved"); - reserved.assertReserved(); + auto tc = reserved.getTypeCode(); + if (!(tc == ::ot_reserved || tc == ::ot_null)) { + throw std::logic_error("replaceReserved called with non-reserverd object"); + } replaceObject(reserved.getObjGen(), replacement); } @@ -2012,8 +2021,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) reserveObjects(foreign, obj_copier, true); if (!obj_copier.visiting.empty()) { - throw std::logic_error("obj_copier.visiting is not empty" - " after reserving objects"); + throw std::logic_error("obj_copier.visiting is not empty after reserving objects"); } // Copy any new objects and replace the reservations. @@ -2026,7 +2034,13 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) } obj_copier.to_copy.clear(); - return obj_copier.object_map[foreign.getObjGen()]; + auto& result = obj_copier.object_map[foreign.getObjGen()]; + if (!result.isInitialized()) { + result = QPDFObjectHandle::newNull(); + warn(damagedPDF("Unexpected reference to /Pages object while copying foreign object. " + "Replacing with Null object.")); + } + return result; } void @@ -2042,11 +2056,6 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top) return; } - if ((!top) && foreign.isPageObject()) { - QTC::TC("qpdf", "QPDF not crossing page boundary"); - return; - } - if (foreign.isIndirect()) { QPDFObjGen foreign_og(foreign.getObjGen()); if (obj_copier.object_map.count(foreign_og) > 0) { @@ -2061,8 +2070,14 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top) } QTC::TC("qpdf", "QPDF copy indirect"); if (obj_copier.object_map.count(foreign_og) == 0) { + obj_copier.object_map[foreign_og] = + foreign.isStream() ? newStream() : newIndirectNull(); + if ((!top) && foreign.isPageObject()) { + QTC::TC("qpdf", "QPDF not crossing page boundary"); + obj_copier.visiting.erase(foreign); + return; + } obj_copier.to_copy.push_back(foreign); - obj_copier.object_map[foreign_og] = foreign.isStream() ? newStream() : newReserved(); } } @@ -2094,7 +2109,7 @@ QPDF::replaceForeignIndirectObjects(QPDFObjectHandle foreign, ObjCopier& obj_cop QTC::TC("qpdf", "QPDF replace indirect"); auto mapping = obj_copier.object_map.find(foreign.getObjGen()); if (mapping == obj_copier.object_map.end()) { - // This case would occur if this is a reference to a Page or Pages object that we didn't + // This case would occur if this is a reference to a Pages object that we didn't // traverse into. QTC::TC("qpdf", "QPDF replace foreign indirect with null"); result = QPDFObjectHandle::newNull(); diff --git a/libqpdf/QPDF_Dictionary.cc b/libqpdf/QPDF_Dictionary.cc index 7700b81b..942e6d9e 100644 --- a/libqpdf/QPDF_Dictionary.cc +++ b/libqpdf/QPDF_Dictionary.cc @@ -121,8 +121,10 @@ QPDF_Dictionary::getAsMap() const void QPDF_Dictionary::replaceKey(std::string const& key, QPDFObjectHandle value) { - if (value.isNull()) { - // The PDF spec doesn't distinguish between keys with null values and missing keys. + if (value.isNull() && !value.isIndirect()) { + // The PDF spec doesn't distinguish between keys with null values and missing keys. Allow + // indirect nulls which are equivalent to a dangling reference, which is permitted by the + // spec. removeKey(key); } else { // add or replace value diff --git a/libqpdf/QPDF_pages.cc b/libqpdf/QPDF_pages.cc index e03dabc8..4e3e77c0 100644 --- a/libqpdf/QPDF_pages.cc +++ b/libqpdf/QPDF_pages.cc @@ -68,7 +68,7 @@ QPDF::getAllPages() seen.clear(); if (pages.hasKey("/Kids")) { // Ensure we actually found a /Pages object. - getAllPagesInternal(pages, visited, seen); + getAllPagesInternal(pages, visited, seen, false); } } return m->all_pages; @@ -76,7 +76,7 @@ QPDF::getAllPages() void QPDF::getAllPagesInternal( - QPDFObjectHandle cur_node, QPDFObjGen::set& visited, QPDFObjGen::set& seen) + QPDFObjectHandle cur_node, QPDFObjGen::set& visited, QPDFObjGen::set& seen, bool media_box) { if (!visited.add(cur_node)) { throw QPDFExc( @@ -90,13 +90,26 @@ QPDF::getAllPagesInternal( cur_node.warnIfPossible("/Type key should be /Pages but is not; overriding"); cur_node.replaceKey("/Type", "/Pages"_qpdf); } + if (!media_box) { + media_box = cur_node.getKey("/MediaBox").isRectangle(); + QTC::TC("qpdf", "QPDF inherit mediabox", media_box ? 0 : 1); + } auto kids = cur_node.getKey("/Kids"); int n = kids.getArrayNItems(); for (int i = 0; i < n; ++i) { auto kid = kids.getArrayItem(i); if (kid.hasKey("/Kids")) { - getAllPagesInternal(kid, visited, seen); + getAllPagesInternal(kid, visited, seen, media_box); } else { + if (!media_box && !kid.getKey("/MediaBox").isRectangle()) { + QTC::TC("qpdf", "QPDF missing mediabox"); + kid.warnIfPossible( + "kid " + std::to_string(i) + + " (from 0) MediaBox is undefined; setting to letter / ANSI A"); + kid.replaceKey( + "/MediaBox", + QPDFObjectHandle::newArray(QPDFObjectHandle::Rectangle(0, 0, 612, 792))); + } if (!kid.isIndirect()) { QTC::TC("qpdf", "QPDF handle direct page object"); cur_node.warnIfPossible( diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc index 0fb03579..fcba203f 100644 --- a/libqpdf/QUtil.cc +++ b/libqpdf/QUtil.cc @@ -1178,21 +1178,43 @@ std::string QUtil::read_file_into_string(FILE* f, std::string_view filename) { fseek(f, 0, SEEK_END); - auto size = QIntC::to_size(QUtil::tell(f)); - fseek(f, 0, SEEK_SET); - std::string result(size, '\0'); - if (auto read = fread(result.data(), 1, size, f); read != size) { + auto o_size = QUtil::tell(f); + if (o_size >= 0) { + // Seekable file + auto size = QIntC::to_size(o_size); + fseek(f, 0, SEEK_SET); + std::string result(size, '\0'); + if (auto n_read = fread(result.data(), 1, size, f); n_read != size) { + if (ferror(f)) { + throw std::runtime_error( + std::string("failure reading file ") + std::string(filename) + + " into memory: read " + uint_to_string(n_read) + "; wanted " + + uint_to_string(size)); + } else { + throw std::runtime_error( + std::string("premature eof reading file ") + std::string(filename) + + " into memory: read " + uint_to_string(n_read) + "; wanted " + + uint_to_string(size)); + } + } + return result; + } else { + // Pipe or other non-seekable file + size_t buf_size = 8192; + auto n_read = buf_size; + std::string buffer(buf_size, '\0'); + std::string result; + while (n_read == buf_size) { + n_read = fread(buffer.data(), 1, buf_size, f); + buffer.erase(n_read); + result.append(buffer); + } if (ferror(f)) { throw std::runtime_error( - std::string("failure reading file ") + std::string(filename) + - " into memory: read " + uint_to_string(read) + "; wanted " + uint_to_string(size)); - } else { - throw std::runtime_error( - std::string("premature eof reading file ") + std::string(filename) + - " into memory: read " + uint_to_string(read) + "; wanted " + uint_to_string(size)); + std::string("failure reading file ") + std::string(filename) + " into memory"); } + return result; } - return result; } static bool |