diff options
28 files changed, 218 insertions, 97 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index e0a4db06..6c362767 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.16) # the project line. When updating the version, check make_dist for all # the places it has to be updated. project(qpdf - VERSION 11.2.0 + VERSION 11.2.1 LANGUAGES C CXX) # Enable correct rpath handling for MacOSX diff --git a/README-maintainer b/README-maintainer index bb1859e5..1ca7de8a 100644 --- a/README-maintainer +++ b/README-maintainer @@ -4,18 +4,21 @@ ROUTINE DEVELOPMENT Default: -cmake -DMAINTAINER_MODE=1 -DBUILD_STATIC_LIBS=0 \ +cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 \ + -DMAINTAINER_MODE=1 -DBUILD_STATIC_LIBS=0 \ -DCMAKE_BUILD_TYPE=RelWithDebInfo .. Debugging: -cmake -DMAINTAINER_MODE=1 -DBUILD_SHARED_LIBS=0 \ +cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 \ + -DMAINTAINER_MODE=1 -DBUILD_SHARED_LIBS=0 \ -DCMAKE_BUILD_TYPE=Debug .. Profiling: CFLAGS=-pg LDFLAGS=-pg \ - cmake -DMAINTAINER_MODE=1 -DBUILD_SHARED_LIBS=0 \ + cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 \ + -DMAINTAINER_MODE=1 -DBUILD_SHARED_LIBS=0 \ -DCMAKE_BUILD_TYPE=Debug .. Then run `gprof gmon.out`. Note that gmon.out is not cumulative. @@ -26,7 +29,8 @@ CFLAGS="-fsanitize=address -fsanitize=undefined" \ CXXFLAGS="-fsanitize=address -fsanitize=undefined" \ LDFLAGS="-fsanitize=address -fsanitize=undefined" \ CC=clang CXX=clang++ \ - cmake -DMAINTAINER_MODE=1 -DBUILD_SHARED_LIBS=0 \ + cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 \ + -DMAINTAINER_MODE=1 -DBUILD_SHARED_LIBS=0 \ -DCMAKE_BUILD_TYPE=Debug .. Windows: diff --git a/build-scripts/prebuild b/build-scripts/prebuild index e6ee197c..44b59949 100755 --- a/build-scripts/prebuild +++ b/build-scripts/prebuild @@ -8,9 +8,9 @@ if ! ./generate_auto_job --check; then An input or output file of generate_auto_job was modified without rerunning ./generate_auto_job. If you are seeing this in your pull -request, you should pass --enable-maintainer-mode to ./configure if -possible, or if not, run "./generate_auto_job --generate" and include -any changed files in your commit. +request, you should pass -DMAINTAINER_MODE=1 to cmake if possible, or +if not, run "./generate_auto_job --generate" and include any changed +files in your commit. ****************************** diff --git a/include/qpdf/DLL.h b/include/qpdf/DLL.h index 257486b0..ac161fe7 100644 --- a/include/qpdf/DLL.h +++ b/include/qpdf/DLL.h @@ -26,8 +26,8 @@ /* The first version of qpdf to include the version constants is 10.6.0. */ #define QPDF_MAJOR_VERSION 11 #define QPDF_MINOR_VERSION 2 -#define QPDF_PATCH_VERSION 0 -#define QPDF_VERSION "11.2.0" +#define QPDF_PATCH_VERSION 1 +#define QPDF_VERSION "11.2.1" /* * This file defines symbols that control the which functions, diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index eb3bf2e2..5b2db6a8 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -886,7 +886,6 @@ class QPDF qpdf->resolve(og); } }; - friend class Resolver; // StreamCopier class is restricted to QPDFObjectHandle so it can // copy stream data. @@ -904,7 +903,6 @@ class QPDF qpdf->copyStreamData(dest, src); } }; - friend class Resolver; // The ParseGuard class allows QPDFObjectHandle to detect // re-entrant parsing. @@ -928,7 +926,6 @@ class QPDF } QPDF* qpdf; }; - friend class ParseGuard; // Pipe class is restricted to QPDF_Stream class Pipe @@ -957,7 +954,6 @@ class QPDF will_retry); } }; - friend class Pipe; // For testing only -- do not add to DLL static bool test_json_validators(); @@ -1106,7 +1102,6 @@ class QPDF QPDF* qpdf; std::set<QPDFObjGen>::const_iterator iter; }; - friend class ResolveRecorder; class JSONReactor: public JSON::Reactor { @@ -1173,12 +1168,12 @@ class QPDF std::vector<QPDFObjectHandle> object_stack; std::set<QPDFObjGen> reserved; }; - friend class JSONReactor; void parse(char const* password); void inParse(bool); void setTrailer(QPDFObjectHandle obj); void read_xref(qpdf_offset_t offset); + bool resolveXRefTable(); void reconstruct_xref(QPDFExc& e); bool parse_xrefFirst(std::string const& line, int& obj, int& num, int& bytes); @@ -1208,10 +1203,10 @@ class QPDF bool attempt_recovery, qpdf_offset_t offset, std::string const& description, - QPDFObjGen const& exp_og, + QPDFObjGen exp_og, QPDFObjGen& og, bool skip_cache_if_in_xref); - void resolve(QPDFObjGen const& og); + void resolve(QPDFObjGen og); void resolveObjectsInStream(int obj_stream_number); void stopOnError(std::string const& message); QPDFObjectHandle reserveObjectIfNotExists(QPDFObjGen const& og); diff --git a/include/qpdf/QPDFJob.hh b/include/qpdf/QPDFJob.hh index 2d552f64..b12c052a 100644 --- a/include/qpdf/QPDFJob.hh +++ b/include/qpdf/QPDFJob.hh @@ -382,7 +382,6 @@ class QPDFJob } QPDFJob& o; }; - friend class Config; // Return a top-level configuration item. See CONFIGURATION above // for details. If an invalid configuration is created (such as diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index bf0da746..cfd19ea9 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -1504,7 +1504,6 @@ class QPDFObjectHandle return QPDFObjectHandle(obj); } }; - friend class Factory; // Accessor for raw underlying object -- only QPDF is allowed to // call this. @@ -1533,7 +1532,6 @@ class QPDFObjectHandle return oh.asStream(); } }; - friend class ObjAccessor; // Provide access to specific classes for recursive // disconnected(). @@ -1550,7 +1548,6 @@ class QPDFObjectHandle o.disconnect(); } }; - friend class Resetter; // Convenience routine: Throws if the assumption is violated. Your // code will be better if you call one of the isType methods and diff --git a/include/qpdf/QPDFOutlineDocumentHelper.hh b/include/qpdf/QPDFOutlineDocumentHelper.hh index 28328c71..bbbc2a63 100644 --- a/include/qpdf/QPDFOutlineDocumentHelper.hh +++ b/include/qpdf/QPDFOutlineDocumentHelper.hh @@ -76,7 +76,6 @@ class QPDFOutlineDocumentHelper: public QPDFDocumentHelper return dh.checkSeen(og); } }; - friend class Accessor; private: bool checkSeen(QPDFObjGen const& og); diff --git a/include/qpdf/QPDFOutlineObjectHelper.hh b/include/qpdf/QPDFOutlineObjectHelper.hh index 6c91909c..11010a9b 100644 --- a/include/qpdf/QPDFOutlineObjectHelper.hh +++ b/include/qpdf/QPDFOutlineObjectHelper.hh @@ -93,7 +93,6 @@ class QPDFOutlineObjectHelper: public QPDFObjectHelper return QPDFOutlineObjectHelper(oh, dh, depth); } }; - friend class Accessor; private: QPDF_DLL diff --git a/include/qpdf/QPDFWriter.hh b/include/qpdf/QPDFWriter.hh index 040f3eba..a9d60672 100644 --- a/include/qpdf/QPDFWriter.hh +++ b/include/qpdf/QPDFWriter.hh @@ -550,7 +550,6 @@ class QPDFWriter std::shared_ptr<Buffer>* bp; std::string stack_id; }; - friend class PipelinePopper; unsigned int bytesNeeded(long long n); void writeBinary(unsigned long long val, unsigned int bytes); @@ -14,4 +14,4 @@ libqpdf/qpdf/auto_job_json_decl.hh 06caa46eaf71db8a50c046f91866baa8087745a947431 libqpdf/qpdf/auto_job_json_init.hh 8eec1d4acdf3e40cea46155cbf23a60d226ae6e9493ab18265b95dca790ed66d libqpdf/qpdf/auto_job_schema.hh a960022725ac4a59db531a3e2bc3964e40113b47348864e9229eb0f3fecfbdc0 manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580 -manual/cli.rst 861e3c18c5f17886b715f36ed3ad65fb7b1ccfbe17649dbebb8bd0e228f92aa3 +manual/cli.rst 1fc340f4cbe119b45492ad05526515f2eec98ae8ae4282f73ddbbe5e97ee9f87 diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 7446c6da..86846675 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -577,6 +577,8 @@ QPDF::reconstruct_xref(QPDFExc& e) } this->m->reconstructed_xref = true; + // We may find more objects, which may contain dangling references. + this->m->fixed_dangling_refs = false; warn(damagedPDF("", 0, "file is damaged")); warn(e); @@ -1290,65 +1292,36 @@ QPDF::showXRefTable() } } +// Resolve all objects in the xref table. If this triggers a xref table +// reconstruction abort and return false. Otherwise return true. +bool +QPDF::resolveXRefTable() +{ + bool may_change = !this->m->reconstructed_xref; + for (auto& iter: this->m->xref_table) { + if (isUnresolved(iter.first)) { + resolve(iter.first); + if (may_change && this->m->reconstructed_xref) { + return false; + } + } + } + return true; +} + +// Ensure all objects in the pdf file, including those in indirect +// references, appear in the object cache. void QPDF::fixDanglingReferences(bool force) { - if (this->m->fixed_dangling_refs && (!force)) { + if (this->m->fixed_dangling_refs) { return; } - this->m->fixed_dangling_refs = true; - - // Create a set of all known indirect objects including those - // we've previously resolved and those that we have created. - std::set<QPDFObjGen> to_process; - for (auto const& iter: this->m->obj_cache) { - to_process.insert(iter.first); - } - for (auto const& iter: this->m->xref_table) { - to_process.insert(iter.first); - } - - // For each non-scalar item to process, put it in the queue. - std::list<QPDFObjectHandle> queue; - queue.push_back(this->m->trailer); - for (auto const& og: to_process) { - auto obj = getObject(og); - if (obj.isDictionary() || obj.isArray()) { - queue.push_back(obj); - } else if (obj.isStream()) { - queue.push_back(obj.getDict()); - } - } - - // Process the queue by recursively resolving all object - // references. We don't need to do loop detection because we don't - // traverse known indirect objects when processing the queue. - while (!queue.empty()) { - QPDFObjectHandle obj = queue.front(); - queue.pop_front(); - std::list<QPDFObjectHandle> to_check; - if (obj.isDictionary()) { - std::map<std::string, QPDFObjectHandle> members = - obj.getDictAsMap(); - for (auto const& iter: members) { - to_check.push_back(iter.second); - } - } else if (obj.isArray()) { - auto arr = QPDFObjectHandle::ObjAccessor::asArray(obj); - arr->addExplicitElementsToList(to_check); - } - for (auto sub: to_check) { - if (sub.isIndirect()) { - if ((sub.getOwningQPDF() == this) && - isUnresolved(sub.getObjGen())) { - QTC::TC("qpdf", "QPDF detected dangling ref"); - queue.push_back(sub); - } - } else { - queue.push_back(sub); - } - } + if (!resolveXRefTable()) { + QTC::TC("qpdf", "QPDF fix dangling triggered xref reconstruction"); + resolveXRefTable(); } + this->m->fixed_dangling_refs = true; } size_t @@ -1371,7 +1344,7 @@ QPDF::getAllObjects() { // After fixDanglingReferences is called, all objects are in the // object cache. - fixDanglingReferences(true); + fixDanglingReferences(); std::vector<QPDFObjectHandle> result; for (auto const& iter: this->m->obj_cache) { result.push_back(newIndirect(iter.first, iter.second.object)); @@ -1631,7 +1604,7 @@ QPDF::readObjectAtOffset( bool try_recovery, qpdf_offset_t offset, std::string const& description, - QPDFObjGen const& exp_og, + QPDFObjGen exp_og, QPDFObjGen& og, bool skip_cache_if_in_xref) { @@ -1814,7 +1787,7 @@ QPDF::readObjectAtOffset( } void -QPDF::resolve(QPDFObjGen const& og) +QPDF::resolve(QPDFObjGen og) { if (!isUnresolved(og)) { return; @@ -2082,6 +2055,8 @@ QPDF::reserveStream(QPDFObjGen const& og) QPDFObjectHandle QPDF::getObject(QPDFObjGen const& og) { + // This method is called by the parser and therefore must not + // resolve any objects. if (!isCached(og)) { m->obj_cache[og] = ObjCache(QPDF_Unresolved::create(this, og), -1, -1); } diff --git a/libqpdf/QPDFParser.cc b/libqpdf/QPDFParser.cc index 452e741b..eca55a71 100644 --- a/libqpdf/QPDFParser.cc +++ b/libqpdf/QPDFParser.cc @@ -190,6 +190,11 @@ QPDFParser::parse(bool& empty, bool content_stream) olist.at(size - 2).getIntValueAsInt(), olist.back().getIntValueAsInt()); if (ref_og.isIndirect()) { + // This action has the desirable side effect + // of causing dangling references (references + // to indirect objects that don't appear in + // the PDF) in any parsed object to appear in + // the object cache. object = context->getObject(ref_og); indirect_ref = true; } else { diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index b29f75b9..b07aef53 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -2266,7 +2266,7 @@ QPDFWriter::prepareFileForWrite() // Make document extension level information direct as required by // the spec. - this->m->pdf.fixDanglingReferences(true); + this->m->pdf.fixDanglingReferences(); QPDFObjectHandle root = this->m->pdf.getRoot(); for (auto const& key: root.getKeys()) { QPDFObjectHandle oh = root.getKey(key); diff --git a/libtests/qintc.cc b/libtests/qintc.cc index d5738a4b..f6c15f00 100644 --- a/libtests/qintc.cc +++ b/libtests/qintc.cc @@ -4,7 +4,7 @@ #include <stdint.h> #define try_convert(exp_pass, fn, i) \ - try_convert_real(#fn "(" #i ")", exp_pass, fn, i) + try_convert_real(#fn "(" #i ")", exp_pass, fn, i) template <typename From, typename To> static void @@ -27,7 +27,7 @@ try_convert_real( } #define try_range_check(exp_pass, a, b) \ - try_range_check_real(#a " + " #b, exp_pass, a, b) + try_range_check_real(#a " + " #b, exp_pass, a, b) template <typename T> static void @@ -47,7 +47,7 @@ try_range_check_real( } #define try_range_check_subtract(exp_pass, a, b) \ - try_range_check_subtract_real(#a " - " #b, exp_pass, a, b) + try_range_check_subtract_real(#a " - " #b, exp_pass, a, b) template <typename T> static void diff --git a/manual/cli.rst b/manual/cli.rst index b50f2862..49ae90f8 100644 --- a/manual/cli.rst +++ b/manual/cli.rst @@ -855,7 +855,7 @@ Related Options and gzip) unless those streams are compressed in some other way. This analysis is made after qpdf attempts to uncompress streams and is therefore closely related to :qpdf:ref:`--decode-level`. To - suppress this behavior and leave streams streams uncompressed, use + suppress this behavior and leave streams uncompressed, use :samp:`--compress-streams=n`. In QDF mode (see :ref:`qdf` and :qpdf:ref:`--qdf`), the default is to leave streams uncompressed. diff --git a/manual/conf.py b/manual/conf.py index a1fc9298..773321cc 100644 --- a/manual/conf.py +++ b/manual/conf.py @@ -16,7 +16,7 @@ project = 'QPDF' copyright = '2005-2022, Jay Berkenbilt' author = 'Jay Berkenbilt' # make_dist and the CI build lexically find the release version from this file. -release = '11.2.0' +release = '11.2.1' version = release extensions = [ 'sphinx_rtd_theme', diff --git a/performance_check b/performance_check index ac04d168..85ee658b 100755 --- a/performance_check +++ b/performance_check @@ -12,6 +12,8 @@ my $whoami = basename($0); $| = 1; # [ name, [ args ] ] +# If <IN> appears, it is replaced with the input file name. Otherwise, +# the input file name is added to the end of the arguments. my @tests = ( ['no arguments', []], ['generate object streams', ['--object-streams=generate']], @@ -20,6 +22,7 @@ my @tests = ( ['shared resource check', ['--split-pages', '--remove-unreferenced-resources=auto']], ['linearize', ['--linearize']], ['encrypt', ['--encrypt', 'u', 'o', '256', '--']], + ['extract first page', ['--empty', '--pages', '<IN>', '1', '--']], ['json-output', ['--json-output']], ['json-input', ['--json-input']], ); @@ -214,7 +217,7 @@ sub filter_args { my $to_check = $arg; $to_check =~ s/=.*$//; - if (index($help, $to_check) == -1) + if (($to_check =~ m/^-/) && (index($help, $to_check) == -1)) { my $new_arg = $arg_compat{$arg}; if (! defined $new_arg) @@ -287,15 +290,27 @@ sub run_test my ($file, $args) = @_; my $outfile = "out.pdf"; - foreach my $arg (@$args) + my $found_in = 0; + my @cmd = ($executable, @$report_mem); + for (@$args) { + my $arg = $_; if ($arg eq '--json-output') { $outfile = "out.json"; - last; } + elsif ($arg eq '<IN>') + { + $found_in = 1; + $arg = $file; + } + push(@cmd, $arg); + } + if (! $found_in) + { + push(@cmd, $file); } - my @cmd = ($executable, @$args, @$report_mem, $file, "$workdir/$outfile"); + push(@cmd, "$workdir/$outfile"); # Run once and discard to update caches system("sync"); run_cmd(@cmd); diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index d9215dd1..3cef71d2 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -381,7 +381,6 @@ QPDFFormFieldObjectHelper list not found 0 QPDFFormFieldObjectHelper list found 0 QPDFFormFieldObjectHelper list first too low 0 QPDFFormFieldObjectHelper list last too high 0 -QPDF detected dangling ref 0 QPDFJob image optimize no pipeline 0 QPDFJob image optimize no shrink 0 QPDFJob image optimize too small 0 @@ -679,3 +678,4 @@ QPDF_json bad pushedinheritedpageresources 0 QPDFPageObjectHelper copied fallback 0 QPDFPageObjectHelper used fallback without copying 0 QPDF skipping cache for known unchecked object 0 +QPDF fix dangling triggered xref reconstruction 0 diff --git a/qpdf/qtest/dangling-refs.test b/qpdf/qtest/dangling-refs.test index 44d39212..18c2cf6b 100644 --- a/qpdf/qtest/dangling-refs.test +++ b/qpdf/qtest/dangling-refs.test @@ -14,7 +14,7 @@ cleanup(); my $td = new TestDriver('dangling-refs'); -my @dangling = (qw(minimal dangling-refs)); +my @dangling = (qw(minimal dangling-refs dangling-bad-xref)); my $n_tests = 2 * scalar(@dangling); foreach my $f (@dangling) diff --git a/qpdf/qtest/qpdf/dangling-bad-xref-dangling-out.pdf b/qpdf/qtest/qpdf/dangling-bad-xref-dangling-out.pdf Binary files differnew file mode 100644 index 00000000..db2f38d1 --- /dev/null +++ b/qpdf/qtest/qpdf/dangling-bad-xref-dangling-out.pdf diff --git a/qpdf/qtest/qpdf/dangling-bad-xref-dangling.out b/qpdf/qtest/qpdf/dangling-bad-xref-dangling.out new file mode 100644 index 00000000..05221c72 --- /dev/null +++ b/qpdf/qtest/qpdf/dangling-bad-xref-dangling.out @@ -0,0 +1,19 @@ +WARNING: dangling-bad-xref.pdf: file is damaged +WARNING: dangling-bad-xref.pdf (object 7 0, offset 10000): expected n n obj +WARNING: dangling-bad-xref.pdf: Attempting to reconstruct cross-reference table +new object: 13 0 R +all objects +1 0 R +2 0 R +3 0 R +4 0 R +5 0 R +6 0 R +7 0 R +8 0 R +9 0 R +10 0 R +11 0 R +12 0 R +13 0 R +test 53 done diff --git a/qpdf/qtest/qpdf/dangling-bad-xref.pdf b/qpdf/qtest/qpdf/dangling-bad-xref.pdf new file mode 100644 index 00000000..797cf4fa --- /dev/null +++ b/qpdf/qtest/qpdf/dangling-bad-xref.pdf @@ -0,0 +1,110 @@ +%PDF-1.3 +%¿÷¢þ +%QDF-1.0 + +1 0 obj +<< + /Pages 2 0 R + /Type /Catalog + /Dangling 8 0 R + /AlsoDangling [ + 9 0 R + << + /yes 2 0 R + /no 10 0 R + /nope 8 0 R + >> + ] +>> +endobj + +2 0 obj +<< + /Count 1 + /Kids [ + 3 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +3 0 obj +<< + /Contents 4 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 6 0 R + >> + /ProcSet 7 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +4 0 obj +<< + /Length 5 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +5 0 obj +44 +endobj + +6 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +7 0 obj +[ + /PDF + /Text +] +endobj + +11 0 obj +[ + 12 0 R +] +endobj + +xref +0 8 +0000000000 65535 f +0000000025 00000 n +0000000195 00000 n +0000000277 00000 n +0000000492 00000 n +0000000591 00000 n +0000000610 00000 n +0000010000 00000 n +trailer << + /Root 1 0 R + /Size 8 + /ID [<7141a6cf32de469328cf0f51982b5f89><7141a6cf32de469328cf0f51982b5f89>] +>> +startxref +793 +%%EOF diff --git a/qpdf/qtest/qpdf/dangling-refs-dangling-out.pdf b/qpdf/qtest/qpdf/dangling-refs-dangling-out.pdf Binary files differindex 8ca87ebe..75c5e4db 100644 --- a/qpdf/qtest/qpdf/dangling-refs-dangling-out.pdf +++ b/qpdf/qtest/qpdf/dangling-refs-dangling-out.pdf diff --git a/qpdf/qtest/qpdf/dangling-refs-dangling.out b/qpdf/qtest/qpdf/dangling-refs-dangling.out index cf1522c4..a41cd59c 100644 --- a/qpdf/qtest/qpdf/dangling-refs-dangling.out +++ b/qpdf/qtest/qpdf/dangling-refs-dangling.out @@ -1,3 +1,4 @@ +new object: 11 0 R all objects 1 0 R 2 0 R diff --git a/qpdf/qtest/qpdf/minimal-dangling-out.pdf b/qpdf/qtest/qpdf/minimal-dangling-out.pdf Binary files differindex 48dff413..1ac2c6a0 100644 --- a/qpdf/qtest/qpdf/minimal-dangling-out.pdf +++ b/qpdf/qtest/qpdf/minimal-dangling-out.pdf diff --git a/qpdf/qtest/qpdf/minimal-dangling.out b/qpdf/qtest/qpdf/minimal-dangling.out index 6f656850..c3ad7c73 100644 --- a/qpdf/qtest/qpdf/minimal-dangling.out +++ b/qpdf/qtest/qpdf/minimal-dangling.out @@ -1,3 +1,4 @@ +new object: 7 0 R all objects 1 0 R 2 0 R diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index 21fc0150..1f5363c8 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -194,7 +194,7 @@ print_rect(std::ostream& out, QPDFObjectHandle::Rectangle const& r) } #define assert_compare_numbers(expected, expr) \ - compare_numbers(#expr, expected, expr) + compare_numbers(#expr, expected, expr) template <typename T1, typename T2> static void @@ -2062,8 +2062,10 @@ test_53(QPDF& pdf, char const* arg2) { // Test get all objects and dangling ref handling QPDFObjectHandle root = pdf.getRoot(); - root.replaceKey( - "/Q1", pdf.makeIndirectObject(QPDFObjectHandle::newString("potato"))); + auto new_obj = + pdf.makeIndirectObject(QPDFObjectHandle::newString("potato")); + root.replaceKey("/Q1", new_obj); + std::cout << "new object: " << new_obj.unparse() << std::endl; std::cout << "all objects" << std::endl; for (auto& obj: pdf.getAllObjects()) { std::cout << obj.unparse() << std::endl; @@ -2071,6 +2073,7 @@ test_53(QPDF& pdf, char const* arg2) QPDFWriter w(pdf, "a.pdf"); w.setStaticID(true); + w.setPreserveUnreferencedObjects(true); w.write(); } |