From 04c203ae060458ae39253263c7dd1c603b931bf0 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Thu, 27 Dec 2012 11:39:01 -0500 Subject: [PATCH] Eliminate flattenScalarReferences --- ChangeLog | 18 +++ TODO | 32 ----- include/qpdf/QPDF.hh | 16 --- include/qpdf/QPDFWriter.hh | 1 + ispell-words | 2 - libqpdf/QPDF.cc | 39 ----- libqpdf/QPDFWriter.cc | 133 +++++++++++++++--- libqpdf/QPDF_optimization.cc | 101 +------------ qpdf/qpdf.cc | 15 +- qpdf/qpdf.testcov | 4 +- qpdf/qtest/qpdf/good13.qdf | 46 +++--- qpdf/qtest/qpdf/good5.qdf | 50 ++++--- qpdf/qtest/qpdf/good8.qdf | 50 ++++--- qpdf/qtest/qpdf/lin-special.disable.exp | Bin 3049 -> 3092 bytes qpdf/qtest/qpdf/lin-special.generate.exp | Bin 2843 -> 2849 bytes qpdf/qtest/qpdf/lin-special.preserve.exp | Bin 3049 -> 3092 bytes qpdf/qtest/qpdf/test4-1.qdf | 58 +++++--- .../qpdf/unreferenced-indirect-scalar.out | Bin 1402 -> 1405 bytes 18 files changed, 260 insertions(+), 305 deletions(-) diff --git a/ChangeLog b/ChangeLog index a06ffdcf..7a42adae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2012-12-27 Jay Berkenbilt + + * Removed public method QPDF::flattenScalarReferences. Instead, + just flatten the scalar references we actually need to flatten. + Flattening scalar references was a wrong decision years ago and + has occasionally caused other problems, among which were that it + caused qpdf to visit otherwise unreferenced and possibly erroneous + objects in the file when it didn't have to. + + * Removed public method QPDF::decodeStreams which was previously + used by qpdf --check but is no longer used. The decodeStreams + method could generate false positives since it would attempt to + access all objects in the file including those that were not + referenced. + + * Removed public method QPDF::trimTrailerForWrite, which was only + intended for use by QPDFWriter and which is no longer used. + 2012-12-25 Jay Berkenbilt * Allow PDF header to appear anywhere in the first 1024 bytes of diff --git a/TODO b/TODO index c8e0ceca..c744b103 100644 --- a/TODO +++ b/TODO @@ -41,38 +41,6 @@ General outlines, page labels, thumbnails, zones. There are probably others. - * See whether it's possible to remove the call to - flattenScalarReferences. I can't easily figure out why I do it, - but removing it causes strange test failures in linearization. I - would have to study the optimization and linearization code to - figure out why I added this to begin with and what in the code - assumes it's the case. For enqueueObject and unparseChild in - QPDFWriter, simply removing the checks for indirect scalars seems - sufficient. Looking back at the branch in the apex epub - repository, before flattening scalar references, there was special - case code in QPDFWriter to avoid writing out indirect nulls. It's - still not obvious to me why I did it though. - - To pursue this, remove the call to flattenScalarReferences in - QPDFWriter.cc and disable the logic_error exceptions for indirect - scalars. Just search for flattenScalarReferences in QPDFWriter.cc - since the logic errors have comments that mention - flattenScalarReferences. Then run the test suite. Several files - that explicitly test flattening of scalar references fail, but the - indirect scalars are properly preserved and written. But then - there are some linearized files that have a bunch of unreferenced - objects that contain scalars. Need to figure out what these are - and why they're there. Maybe they're objects that used to be - stream lengths. Probably we just need to make sure don't traverse - through a stream's /Length stream when enqueueing stream - dictionaries. This could potentially happen with any object that - QPDFWriter replaces when writing out files. Such objects would be - orphaned in the newly written file. This could be fixed, but it - may not be worth fixing. - - If flattenScalarReferences is removed, a new method will be needed - for checking PDF files. - * See if we can avoid preserving unreferenced objects in object streams even when preserving the object streams. diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index 64858625..b2e73929 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -352,24 +352,8 @@ class QPDF void optimize(std::map const& object_stream_data, bool allow_changes = true); - // Replace all references to indirect objects that are "scalars" - // (i.e., things that don't have children: not arrays, streams, or - // dictionaries) with direct objects. - QPDF_DLL - void flattenScalarReferences(); - - // Decode all streams, discarding the output. Used to check - // correctness of stream encoding. - QPDF_DLL - void decodeStreams(); - // For QPDFWriter: - // Remove /ID, /Encrypt, and /Prev keys from the trailer - // dictionary since these are regenerated during write. - QPDF_DLL - void trimTrailerForWrite(); - // Get lists of all objects in order according to the part of a // linearized file that they belong to. QPDF_DLL diff --git a/include/qpdf/QPDFWriter.hh b/include/qpdf/QPDFWriter.hh index 725b2be8..611c6aef 100644 --- a/include/qpdf/QPDFWriter.hh +++ b/include/qpdf/QPDFWriter.hh @@ -299,6 +299,7 @@ class QPDFWriter void setDataKey(int objid); int openObject(int objid = 0); void closeObject(int objid); + void prepareFileForWrite(); void writeStandard(); void writeLinearized(); void enqueuePart(std::vector& part); diff --git a/ispell-words b/ispell-words index 51f70a1d..43d38810 100644 --- a/ispell-words +++ b/ispell-words @@ -206,7 +206,6 @@ debian declspec DecodeParms decodeRow -decodeStreams decrypt decrypted decrypter @@ -335,7 +334,6 @@ fl flate FlateDecode flattenPagesTree -flattenScalarReferences fn fname fo diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 777fb837..fac79796 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -1860,28 +1860,6 @@ QPDF::swapObjects(int objid1, int generation1, int objid2, int generation2) this->obj_cache[og2] = t; } -void -QPDF::trimTrailerForWrite() -{ - // Note that removing the encryption dictionary does not interfere - // with reading encrypted files. QPDF loads all the information - // it needs from the encryption dictionary at the beginning and - // never looks at it again. - this->trailer.removeKey("/ID"); - this->trailer.removeKey("/Encrypt"); - this->trailer.removeKey("/Prev"); - - // Remove all trailer keys that potentially come from a - // cross-reference stream - this->trailer.removeKey("/Index"); - this->trailer.removeKey("/W"); - this->trailer.removeKey("/Length"); - this->trailer.removeKey("/Filter"); - this->trailer.removeKey("/DecodeParms"); - this->trailer.removeKey("/Type"); - this->trailer.removeKey("/XRefStm"); -} - std::string QPDF::getFilename() const { @@ -2067,20 +2045,3 @@ QPDF::pipeStreamData(int objid, int generation, } pipeline->finish(); } - -void -QPDF::decodeStreams() -{ - for (std::map::iterator iter = - this->xref_table.begin(); - iter != this->xref_table.end(); ++iter) - { - ObjGen const& og = (*iter).first; - QPDFObjectHandle obj = getObjectByID(og.obj, og.gen); - if (obj.isStream()) - { - Pl_Discard pl; - obj.pipeStreamData(&pl, true, false, false); - } - } -} diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index c1e4e1dd..05b5a9a9 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -834,16 +834,6 @@ QPDFWriter::enqueueObject(QPDFObjectHandle object) { // This is a place-holder object for an object stream } - else if (object.isScalar()) - { - // flattenScalarReferences is supposed to have removed all - // indirect scalars. - throw std::logic_error( - "INTERNAL ERROR: QPDFWriter::enqueueObject: indirect scalar: " + - std::string(this->filename) + " " + - QUtil::int_to_string(object.getObjectID()) + " " + - QUtil::int_to_string(object.getGeneration())); - } int objid = object.getObjectID(); if (obj_renumber.count(objid) == 0) @@ -916,15 +906,6 @@ QPDFWriter::unparseChild(QPDFObjectHandle child, int level, int flags) } if (child.isIndirect()) { - if (child.isScalar()) - { - // flattenScalarReferences is supposed to have removed all - // indirect scalars. - throw std::logic_error( - "INTERNAL ERROR: QPDFWriter::unparseChild: indirect scalar: " + - QUtil::int_to_string(child.getObjectID()) + " " + - QUtil::int_to_string(child.getGeneration())); - } int old_id = child.getObjectID(); int new_id = obj_renumber[old_id]; writeString(QUtil::int_to_string(new_id)); @@ -1647,6 +1628,117 @@ QPDFWriter::generateObjectStreams() } } +void +QPDFWriter::prepareFileForWrite() +{ + // Remove keys from the trailer that necessarily have to be + // replaced when writing the file. + + QPDFObjectHandle trailer = pdf.getTrailer(); + + // Note that removing the encryption dictionary does not interfere + // with reading encrypted files. QPDF loads all the information + // it needs from the encryption dictionary at the beginning and + // never looks at it again. + trailer.removeKey("/ID"); + trailer.removeKey("/Encrypt"); + trailer.removeKey("/Prev"); + + // Remove all trailer keys that potentially come from a + // cross-reference stream + trailer.removeKey("/Index"); + trailer.removeKey("/W"); + trailer.removeKey("/Length"); + trailer.removeKey("/Filter"); + trailer.removeKey("/DecodeParms"); + trailer.removeKey("/Type"); + trailer.removeKey("/XRefStm"); + + // Do a traversal of the entire PDF file structure replacing all + // indirect objects that QPDFWriter wants to be direct. This + // includes stream lengths, stream filtering parameters, and + // document extension level information. Also replace all + // indirect null references with direct nulls. This way, the only + // indirect nulls queued for output will be object stream place + // holders. + + std::list queue; + queue.push_back(pdf.getTrailer()); + std::set visited; + + while (! queue.empty()) + { + QPDFObjectHandle node = queue.front(); + queue.pop_front(); + if (node.isIndirect()) + { + if (visited.count(node.getObjectID()) > 0) + { + continue; + } + visited.insert(node.getObjectID()); + } + + if (node.isArray()) + { + int nitems = node.getArrayNItems(); + for (int i = 0; i < nitems; ++i) + { + QPDFObjectHandle oh = node.getArrayItem(i); + if (oh.isIndirect() && oh.isNull()) + { + QTC::TC("qpdf", "QPDFWriter flatten array null"); + oh.makeDirect(); + node.setArrayItem(i, oh); + } + else if (! oh.isScalar()) + { + queue.push_back(oh); + } + } + } + else if (node.isDictionary() || node.isStream()) + { + bool is_stream = false; + QPDFObjectHandle dict = node; + if (node.isStream()) + { + is_stream = true; + dict = node.getDict(); + } + + std::set keys = dict.getKeys(); + for (std::set::iterator iter = keys.begin(); + iter != keys.end(); ++iter) + { + std::string const& key = *iter; + QPDFObjectHandle oh = dict.getKey(key); + bool add_to_queue = true; + if (oh.isIndirect()) + { + if (is_stream) + { + if ((key == "/Length") || + (key == "/Filter") || + (key == "/DecodeParms")) + { + QTC::TC("qpdf", "QPDF make stream key direct"); + add_to_queue = false; + oh.makeDirect(); + dict.replaceKey(key, oh); + } + } + } + + if (add_to_queue) + { + queue.push_back(oh); + } + } + } + } +} + void QPDFWriter::write() { @@ -1785,8 +1877,7 @@ QPDFWriter::write() generateID(); - pdf.trimTrailerForWrite(); - pdf.flattenScalarReferences(); + prepareFileForWrite(); if (this->linearized) { diff --git a/libqpdf/QPDF_optimization.cc b/libqpdf/QPDF_optimization.cc index 67f147f3..f832a883 100644 --- a/libqpdf/QPDF_optimization.cc +++ b/libqpdf/QPDF_optimization.cc @@ -58,103 +58,6 @@ QPDF::ObjUser::operator<(ObjUser const& rhs) const return false; } -void -QPDF::flattenScalarReferences() -{ - // Do a traversal of the entire PDF file structure replacing all - // indirect objects that are not arrays, streams, or dictionaries - // with direct objects. - - std::list queue; - queue.push_back(this->trailer); - std::set visited; - - // Add every object in the xref table to the queue. This ensures - // that we flatten scalar references in unreferenced objects. - // This becomes important if we are preserving object streams in a - // file that has unreferenced objects in its object streams. (See - // QPDF bug 2974522 at SourceForge.) - for (std::map::iterator iter = - this->xref_table.begin(); - iter != this->xref_table.end(); ++iter) - { - ObjGen const& og = (*iter).first; - queue.push_back(getObjectByID(og.obj, og.gen)); - } - - while (! queue.empty()) - { - QPDFObjectHandle node = queue.front(); - queue.pop_front(); - if (node.isIndirect()) - { - ObjGen og(node.getObjectID(), node.getGeneration()); - if (visited.count(og) > 0) - { - continue; - } - visited.insert(og); - } - - if (node.isArray()) - { - int nitems = node.getArrayNItems(); - for (int i = 0; i < nitems; ++i) - { - QPDFObjectHandle oh = node.getArrayItem(i); - if (oh.isScalar()) - { - if (oh.isIndirect()) - { - QTC::TC("qpdf", "QPDF opt flatten array scalar"); - oh.makeDirect(); - node.setArrayItem(i, oh); - } - } - else - { - queue.push_back(oh); - } - } - } - else if (node.isDictionary() || node.isStream()) - { - QPDFObjectHandle dict = node; - if (node.isStream()) - { - dict = node.getDict(); - } - std::set keys = dict.getKeys(); - for (std::set::iterator iter = keys.begin(); - iter != keys.end(); ++iter) - { - std::string const& key = *iter; - QPDFObjectHandle oh = dict.getKey(key); - if (oh.isNull()) - { - // QPDF_Dictionary.getKeys() never returns null - // keys. - throw std::logic_error( - "INTERNAL ERROR: dictionary with null key found"); - } - else if (oh.isScalar()) - { - if (oh.isIndirect()) - { - QTC::TC("qpdf", "QPDF opt flatten dict scalar"); - oh.makeDirect(); - dict.replaceKey(key, oh); - } - } - else - { - queue.push_back(oh); - } - } - } - } -} - void QPDF::optimize(std::map const& object_stream_data, bool allow_changes) @@ -304,9 +207,7 @@ QPDF::pushInheritedAttributesToPageInternal( } else { - // Don't defeat flattenScalarReferences which - // would have already been called by this - // time. + // It's okay to copy scalars. QTC::TC("qpdf", "QPDF opt inherited scalar"); } } diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc index 2835a966..ddb2ad69 100644 --- a/qpdf/qpdf.cc +++ b/qpdf/qpdf.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -1381,12 +1382,14 @@ int main(int argc, char* argv[]) else { std::cout << "File is not linearized\n"; - // calling flattenScalarReferences causes full - // traversal of file, so any structural errors - // would be exposed. - pdf.flattenScalarReferences(); - // Also explicitly decode all streams. - pdf.decodeStreams(); + // Write the file no nowhere, uncompressing + // streams. This causes full file traversal + // and decoding of all streams we can decode. + QPDFWriter w(pdf); + Pl_Discard discard; + w.setOutputPipeline(&discard); + w.setStreamDataMode(qpdf_s_uncompress); + w.write(); okay = true; } } diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index 937d2b0c..bad9bbf7 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -29,8 +29,7 @@ QPDF lin outlines in part 1 QPDF lin nshared_total > nshared_first_page 1 QPDF lin part 8 empty 1 QPDF lin check shared past first page 0 -QPDF opt flatten array scalar 0 -QPDF opt flatten dict scalar 0 +QPDFWriter flatten array null 0 main QTest implicit 0 main QTest indirect 1 main QTest null 0 @@ -244,3 +243,4 @@ QPDFWriter extra header text no newline 0 QPDFWriter extra header text add newline 0 QPDF bogus 0 offset 0 QPDF global offset 0 +QPDF make stream key direct 0 diff --git a/qpdf/qtest/qpdf/good13.qdf b/qpdf/qtest/qpdf/good13.qdf index f8993a83..36bd923a 100644 --- a/qpdf/qtest/qpdf/good13.qdf +++ b/qpdf/qtest/qpdf/good13.qdf @@ -18,7 +18,7 @@ endobj <01020300040560> (AB) ] - /indirect (hello) + /indirect 4 0 R /nesting << /a [ 1 @@ -58,17 +58,22 @@ endobj << /Count 1 /Kids [ - 4 0 R + 5 0 R ] /Type /Pages >> endobj +%% Original object ID: 8 0 +4 0 obj +(hello) +endobj + %% Page 1 %% Original object ID: 3 0 -4 0 obj +5 0 obj << - /Contents 5 0 R + /Contents 6 0 R /MediaBox [ 0 0 @@ -78,9 +83,9 @@ endobj /Parent 3 0 R /Resources << /Font << - /F1 7 0 R + /F1 8 0 R >> - /ProcSet 8 0 R + /ProcSet 9 0 R >> /Type /Page >> @@ -88,9 +93,9 @@ endobj %% Contents for page 1 %% Original object ID: 4 0 -5 0 obj +6 0 obj << - /Length 6 0 R + /Length 7 0 R >> stream BT @@ -101,12 +106,12 @@ ET endstream endobj -6 0 obj +7 0 obj 44 endobj %% Original object ID: 6 0 -7 0 obj +8 0 obj << /BaseFont /Helvetica /Encoding /WinAnsiEncoding @@ -117,7 +122,7 @@ endobj endobj %% Original object ID: 5 0 -8 0 obj +9 0 obj [ /PDF /Text @@ -125,22 +130,23 @@ endobj endobj xref -0 9 +0 10 0000000000 65535 f 0000000052 00000 n 0000000133 00000 n -0000000578 00000 n -0000000687 00000 n -0000000929 00000 n -0000001028 00000 n -0000001074 00000 n -0000001219 00000 n +0000000576 00000 n +0000000675 00000 n +0000000736 00000 n +0000000978 00000 n +0000001077 00000 n +0000001123 00000 n +0000001268 00000 n trailer << /QTest 2 0 R /Root 1 0 R - /Size 9 + /Size 10 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] >> startxref -1254 +1303 %%EOF diff --git a/qpdf/qtest/qpdf/good5.qdf b/qpdf/qtest/qpdf/good5.qdf index e830781c..b868de85 100644 --- a/qpdf/qtest/qpdf/good5.qdf +++ b/qpdf/qtest/qpdf/good5.qdf @@ -5,17 +5,22 @@ %% Original object ID: 1 0 1 0 obj << - /Pages 2 0 R + /Pages 3 0 R /Type /Catalog >> endobj -%% Original object ID: 2 0 +%% Original object ID: 7 0 2 0 obj +true +endobj + +%% Original object ID: 2 0 +3 0 obj << /Count 1 /Kids [ - 3 0 R + 4 0 R ] /Type /Pages >> @@ -23,21 +28,21 @@ endobj %% Page 1 %% Original object ID: 3 0 -3 0 obj +4 0 obj << - /Contents 4 0 R + /Contents 5 0 R /MediaBox [ 0 0 612 792 ] - /Parent 2 0 R + /Parent 3 0 R /Resources << /Font << - /F1 6 0 R + /F1 7 0 R >> - /ProcSet 7 0 R + /ProcSet 8 0 R >> /Type /Page >> @@ -45,9 +50,9 @@ endobj %% Contents for page 1 %% Original object ID: 4 0 -4 0 obj +5 0 obj << - /Length 5 0 R + /Length 6 0 R >> stream BT @@ -58,12 +63,12 @@ ET endstream endobj -5 0 obj +6 0 obj 44 endobj %% Original object ID: 6 0 -6 0 obj +7 0 obj << /BaseFont /Helvetica /Encoding /WinAnsiEncoding @@ -74,7 +79,7 @@ endobj endobj %% Original object ID: 5 0 -7 0 obj +8 0 obj [ /PDF /Text @@ -82,21 +87,22 @@ endobj endobj xref -0 8 +0 9 0000000000 65535 f 0000000052 00000 n 0000000133 00000 n -0000000242 00000 n -0000000484 00000 n -0000000583 00000 n -0000000629 00000 n -0000000774 00000 n +0000000181 00000 n +0000000290 00000 n +0000000532 00000 n +0000000631 00000 n +0000000677 00000 n +0000000822 00000 n trailer << - /QTest true + /QTest 2 0 R /Root 1 0 R - /Size 8 + /Size 9 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] >> startxref -809 +857 %%EOF diff --git a/qpdf/qtest/qpdf/good8.qdf b/qpdf/qtest/qpdf/good8.qdf index a8f5297e..0eac9f7f 100644 --- a/qpdf/qtest/qpdf/good8.qdf +++ b/qpdf/qtest/qpdf/good8.qdf @@ -5,17 +5,22 @@ %% Original object ID: 1 0 1 0 obj << - /Pages 2 0 R + /Pages 3 0 R /Type /Catalog >> endobj -%% Original object ID: 2 0 +%% Original object ID: 7 0 2 0 obj +3.14159 +endobj + +%% Original object ID: 2 0 +3 0 obj << /Count 1 /Kids [ - 3 0 R + 4 0 R ] /Type /Pages >> @@ -23,21 +28,21 @@ endobj %% Page 1 %% Original object ID: 3 0 -3 0 obj +4 0 obj << - /Contents 4 0 R + /Contents 5 0 R /MediaBox [ 0 0 612 792 ] - /Parent 2 0 R + /Parent 3 0 R /Resources << /Font << - /F1 6 0 R + /F1 7 0 R >> - /ProcSet 7 0 R + /ProcSet 8 0 R >> /Type /Page >> @@ -45,9 +50,9 @@ endobj %% Contents for page 1 %% Original object ID: 4 0 -4 0 obj +5 0 obj << - /Length 5 0 R + /Length 6 0 R >> stream BT @@ -58,12 +63,12 @@ ET endstream endobj -5 0 obj +6 0 obj 44 endobj %% Original object ID: 6 0 -6 0 obj +7 0 obj << /BaseFont /Helvetica /Encoding /WinAnsiEncoding @@ -74,7 +79,7 @@ endobj endobj %% Original object ID: 5 0 -7 0 obj +8 0 obj [ /PDF /Text @@ -82,21 +87,22 @@ endobj endobj xref -0 8 +0 9 0000000000 65535 f 0000000052 00000 n 0000000133 00000 n -0000000242 00000 n -0000000484 00000 n -0000000583 00000 n -0000000629 00000 n -0000000774 00000 n +0000000184 00000 n +0000000293 00000 n +0000000535 00000 n +0000000634 00000 n +0000000680 00000 n +0000000825 00000 n trailer << - /QTest 3.14159 + /QTest 2 0 R /Root 1 0 R - /Size 8 + /Size 9 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] >> startxref -809 +860 %%EOF diff --git a/qpdf/qtest/qpdf/lin-special.disable.exp b/qpdf/qtest/qpdf/lin-special.disable.exp index 0c34030013bcad821a2729df697dae299adee660..14c2eaefd9a99eac17b2b2db9f183fb0dda308b2 100644 GIT binary patch delta 351 zcmaDUK1D)MHNeG9*HF)xOLhPEMgO=A%_j=V>R1{n=zAzcE0`IXDHxiXD#R-2`zsh3 zDCoN?7#bOxDd_ttm@4RpC>WU;8&8aCVKkq3|D>daf`LMif_`vjRVq+IK|i1hwX=w6=zAzcE0`IXDHxiXD#R-2`zsg% z1zi;kjSS5c^!*e}74$D%^%}dEo%HlFInk>jtr~ncM zaif7sUEF{UO|2+Vh=nUMnY@U_h0$d5bCx@djK-Ufuzd$ollO4!06DCTbGfLIf}I^q nbwyEX8kd2Bq1k3nE=NY;lA^@SoYW!(8yl!k4b3KRzP6=;zK24zf|-$pf}x3-Lac(mzk;Eug1)PQp}B#%g1(;ukQ1U{WM*JA zG3pMpnSt?SOU6oGHU>uKY#=_(z{u7;c|YS*MvKWynbb6oGqwrThOz6i$A>d&NNXJA zbO}{pbvVOyP38On)f-Az4{@Y0G3+eklA8RP=@*lc@#Oc+Dt@L4MurL&#tKG83WjFp z3PwPdp^<`-iGqd z!)|r+wC44z8K&?~dBCzLXabW$8f*5X^anyY6SWdpj2IVLbJkD(!t{&D&|>ljW))8Z z1tUWRa|;C{BLzb1T0BV9mLI3~& diff --git a/qpdf/qtest/qpdf/lin-special.preserve.exp b/qpdf/qtest/qpdf/lin-special.preserve.exp index 0c34030013bcad821a2729df697dae299adee660..14c2eaefd9a99eac17b2b2db9f183fb0dda308b2 100644 GIT binary patch delta 351 zcmaDUK1D)MHNeG9*HF)xOLhPEMgO=A%_j=V>R1{n=zAzcE0`IXDHxiXD#R-2`zsh3 zDCoN?7#bOxDd_ttm@4RpC>WU;8&8aCVKkq3|D>daf`LMif_`vjRVq+IK|i1hwX=w6=zAzcE0`IXDHxiXD#R-2`zsg% z1zi;kjSS5c^!*e}74$D%^%}dEo%HlFInk>jtr~ncM zaif7sUEF{UO|2+Vh=nUMnY@U_h0$d5bCx@djK-Ufuzd$ollO4!06DCTbGfLIf}I^q nbwyEX8kd2Bq1k3nE=NY;lA^@SoYW!(8yl!k4b3KR> endobj @@ -49,7 +49,7 @@ endobj << /Count 1 /Kids [ - 7 0 R + 9 0 R ] /Type /Pages >> @@ -72,11 +72,21 @@ endobj >> endobj +%% Original object ID: 10 0 +7 0 obj +(Subject) +endobj + +%% Original object ID: 9 0 +8 0 obj +(Some Title Is Here) +endobj + %% Page 1 %% Original object ID: 3 0 -7 0 obj +9 0 obj << - /Contents 8 0 R + /Contents 10 0 R /MediaBox [ 0 0 @@ -86,9 +96,9 @@ endobj /Parent 4 0 R /Resources << /Font << - /F1 10 0 R + /F1 12 0 R >> - /ProcSet 11 0 R + /ProcSet 13 0 R >> /Type /Page >> @@ -96,9 +106,9 @@ endobj %% Contents for page 1 %% Original object ID: 4 0 -8 0 obj +10 0 obj << - /Length 9 0 R + /Length 11 0 R >> stream BT @@ -109,12 +119,12 @@ ET endstream endobj -9 0 obj +11 0 obj 44 endobj %% Original object ID: 6 0 -10 0 obj +12 0 obj << /BaseFont /Helvetica /Encoding /WinAnsiEncoding @@ -125,7 +135,7 @@ endobj endobj %% Original object ID: 7 0 -11 0 obj +13 0 obj [ /PDF /Text @@ -133,26 +143,28 @@ endobj endobj xref -0 12 +0 14 0000000000 65535 f 0000000052 00000 n 0000000134 00000 n 0000000353 00000 n -0000000475 00000 n -0000000575 00000 n -0000000635 00000 n -0000000714 00000 n -0000000958 00000 n -0000001057 00000 n -0000001103 00000 n -0000001249 00000 n +0000000456 00000 n +0000000556 00000 n +0000000616 00000 n +0000000686 00000 n +0000000739 00000 n +0000000813 00000 n +0000001058 00000 n +0000001159 00000 n +0000001206 00000 n +0000001352 00000 n trailer << /Info 2 0 R /QTest 3 0 R /Root 1 0 R - /Size 12 + /Size 14 /ID [<31415926535897932384626433832795>] >> startxref -1285 +1388 %%EOF diff --git a/qpdf/qtest/qpdf/unreferenced-indirect-scalar.out b/qpdf/qtest/qpdf/unreferenced-indirect-scalar.out index c1bf4a9d1563afc623e61e6911b148d8e8563438..af070310588d52a42788f30c7b3e0ef209bf6330 100644 GIT binary patch delta 56 zcmeyx^_Odc1Ea-6M`uR!$&JiX8|U9+Vzp2(Pzc&w&wP@RlbwN)B^!wACkL`BaGDx& Lsj9mAyKw;k<<|~% delta 59 zcmey%^^0qQ1Ec9gM`uQp$?ut@HqO7t#G)U?6$|2Qp2xhFk&}giktG9&YbJ-VDsY-u MaH*=g`nz!f07a4zasU7T