Commit cc8895078a1d64928e8ee335f1e8c7d6928de1b3

Authored by Jay Berkenbilt
1 parent 573b6eb8

Add QPDFObjectHandle::makeDirect(bool allow_streams)

ChangeLog
  1 +2020-12-22 Jay Berkenbilt <ejb@ql.org>
  2 +
  3 + * Add QPDFObjectHandle::makeDirect(bool allow_streams) -- if
  4 + allow_streams is true, preserve indirect references to streams
  5 + rather than throwing an exception. This allows the object to be
  6 + made as direct as possible while preserving stream references.
  7 +
1 8 2020-12-20 Jay Berkenbilt <ejb@ql.org>
2 9  
3 10 * Add qpdf_register_progress_reporter method to C API,
... ...
... ... @@ -155,6 +155,8 @@ ABI Changes
155 155 This is a list of changes to make next time there is an ABI change.
156 156 Comments appear in the code prefixed by "ABI"
157 157  
  158 +* Merge two versions of QPDFObjectHandle::makeDirect per comment
  159 +
158 160 Page splitting/merging
159 161 ======================
160 162  
... ...
include/qpdf/QPDFObjectHandle.hh
... ... @@ -721,8 +721,20 @@ class QPDFObjectHandle
721 721  
722 722 // Mutator methods. Use with caution.
723 723  
724   - // Recursively copy this object, making it direct. Throws an
725   - // exception if a loop is detected or any sub-object is a stream.
  724 + // Recursively copy this object, making it direct. An exception is
  725 + // thrown if a loop is detected. With allow_streams true, keep
  726 + // indirect object references to streams. Otherwise, throw an
  727 + // exception if any sub-object is a stream. Note that, when
  728 + // allow_streams is true and a stream is found, the resulting
  729 + // object is still associated with the containing qpdf. When
  730 + // allow_streams is false, the object will no longer be connected
  731 + // to the original QPDF object after this call completes
  732 + // successfully.
  733 + QPDF_DLL
  734 + void makeDirect(bool allow_streams);
  735 + // Zero-arg version is equivalent to makeDirect(false).
  736 + // ABI: delete zero-arg version of makeDirect, and make
  737 + // allow_streams default to false.
726 738 QPDF_DLL
727 739 void makeDirect();
728 740  
... ... @@ -1121,7 +1133,7 @@ class QPDFObjectHandle
1121 1133 void assertType(char const* type_name, bool istype);
1122 1134 void dereference();
1123 1135 void copyObject(std::set<QPDFObjGen>& visited, bool cross_indirect,
1124   - bool first_level_only);
  1136 + bool first_level_only, bool stop_at_streams);
1125 1137 void shallowCopyInternal(QPDFObjectHandle& oh, bool first_level_only);
1126 1138 void releaseResolved();
1127 1139 static void setObjectDescriptionFromInput(
... ...
libqpdf/QPDFObjectHandle.cc
... ... @@ -2605,18 +2605,24 @@ QPDFObjectHandle::shallowCopyInternal(QPDFObjectHandle&amp; new_obj,
2605 2605 }
2606 2606  
2607 2607 std::set<QPDFObjGen> visited;
2608   - new_obj.copyObject(visited, false, first_level_only);
  2608 + new_obj.copyObject(visited, false, first_level_only, false);
2609 2609 }
2610 2610  
2611 2611 void
2612 2612 QPDFObjectHandle::copyObject(std::set<QPDFObjGen>& visited,
2613   - bool cross_indirect, bool first_level_only)
  2613 + bool cross_indirect, bool first_level_only,
  2614 + bool stop_at_streams)
2614 2615 {
2615 2616 assertInitialized();
2616 2617  
2617 2618 if (isStream())
2618 2619 {
2619   - QTC::TC("qpdf", "QPDFObjectHandle ERR clone stream");
  2620 + QTC::TC("qpdf", "QPDFObjectHandle copy stream",
  2621 + stop_at_streams ? 0 : 1);
  2622 + if (stop_at_streams)
  2623 + {
  2624 + return;
  2625 + }
2620 2626 throw std::runtime_error(
2621 2627 "attempt to make a stream into a direct object");
2622 2628 }
... ... @@ -2690,7 +2696,8 @@ QPDFObjectHandle::copyObject(std::set&lt;QPDFObjGen&gt;&amp; visited,
2690 2696 (cross_indirect || (! items.back().isIndirect())))
2691 2697 {
2692 2698 items.back().copyObject(
2693   - visited, cross_indirect, first_level_only);
  2699 + visited, cross_indirect,
  2700 + first_level_only, stop_at_streams);
2694 2701 }
2695 2702 }
2696 2703 new_obj = new QPDF_Array(items);
... ... @@ -2708,7 +2715,8 @@ QPDFObjectHandle::copyObject(std::set&lt;QPDFObjGen&gt;&amp; visited,
2708 2715 (cross_indirect || (! items[*iter].isIndirect())))
2709 2716 {
2710 2717 items[*iter].copyObject(
2711   - visited, cross_indirect, first_level_only);
  2718 + visited, cross_indirect,
  2719 + first_level_only, stop_at_streams);
2712 2720 }
2713 2721 }
2714 2722 new_obj = new QPDF_Dictionary(items);
... ... @@ -2730,8 +2738,14 @@ QPDFObjectHandle::copyObject(std::set&lt;QPDFObjGen&gt;&amp; visited,
2730 2738 void
2731 2739 QPDFObjectHandle::makeDirect()
2732 2740 {
  2741 + makeDirect(false);
  2742 +}
  2743 +
  2744 +void
  2745 +QPDFObjectHandle::makeDirect(bool allow_streams)
  2746 +{
2733 2747 std::set<QPDFObjGen> visited;
2734   - copyObject(visited, true, false);
  2748 + copyObject(visited, true, false, allow_streams);
2735 2749 }
2736 2750  
2737 2751 void
... ...
qpdf/qpdf.testcov
... ... @@ -79,7 +79,7 @@ QPDFObjectHandle clone string 0
79 79 QPDFObjectHandle clone array 0
80 80 QPDFObjectHandle clone dictionary 0
81 81 QPDFObjectHandle makeDirect loop 0
82   -QPDFObjectHandle ERR clone stream 0
  82 +QPDFObjectHandle copy stream 1
83 83 QPDF default for xref stream field 0 0
84 84 QPDF prev key in xref stream dictionary 0
85 85 QPDF prev key in trailer dictionary 0
... ...
qpdf/qtest/qpdf.test
... ... @@ -2963,7 +2963,7 @@ $td-&gt;runtest(&quot;check output&quot;,
2963 2963 show_ntests();
2964 2964 # ----------
2965 2965 $td->notify("--- Mutability Tests ---");
2966   -$n_tests += 4;
  2966 +$n_tests += 5;
2967 2967  
2968 2968 $td->runtest("no normalization",
2969 2969 {$td->COMMAND => "test_driver 4 test4-1.pdf"},
... ... @@ -2975,13 +2975,18 @@ $td-&gt;runtest(&quot;object ordering&quot;,
2975 2975 {$td->FILE => "test4-4.qdf",
2976 2976 $td->EXIT_STATUS => 0});
2977 2977  
2978   -$td->runtest("loop detected",
  2978 +$td->runtest("make direct with allow_streams",
  2979 + {$td->COMMAND => "test_driver 4 test4-5.pdf"},
  2980 + {$td->FILE => "test4-5.qdf",
  2981 + $td->EXIT_STATUS => 0});
  2982 +
  2983 +$td->runtest("stream detected",
2979 2984 {$td->COMMAND => "test_driver 4 test4-2.pdf"},
2980 2985 {$td->FILE => "test4-2.out",
2981 2986 $td->EXIT_STATUS => 2},
2982 2987 $td->NORMALIZE_NEWLINES);
2983 2988  
2984   -$td->runtest("stream detected",
  2989 +$td->runtest("loop detected",
2985 2990 {$td->COMMAND => "test_driver 4 test4-3.pdf"},
2986 2991 {$td->FILE => "test4-3.out",
2987 2992 $td->EXIT_STATUS => 2},
... ...
qpdf/qtest/qpdf/test4-5.pdf 0 โ†’ 100644
  1 +%PDF-1.3
  2 +%ยฟรทยขรพ
  3 +%QDF-1.0
  4 +
  5 +1 0 obj
  6 +<<
  7 + /Pages 3 0 R
  8 + /Type /Catalog
  9 +>>
  10 +endobj
  11 +
  12 +2 0 obj
  13 +<<
  14 + /A 4 0 R
  15 + /B 5 0 R
  16 + /Subject (Subject)
  17 + /Title (Some Title Is Here)
  18 +>>
  19 +endobj
  20 +
  21 +3 0 obj
  22 +<<
  23 + /Count 1
  24 + /Kids [
  25 + 6 0 R
  26 + ]
  27 + /Type /Pages
  28 +>>
  29 +endobj
  30 +
  31 +4 0 obj
  32 +[
  33 + 100
  34 + 2
  35 + 3
  36 +]
  37 +endobj
  38 +
  39 +5 0 obj
  40 +<<
  41 + /A 4 0 R
  42 + /B (B)
  43 +>>
  44 +endobj
  45 +
  46 +%% Page 1
  47 +6 0 obj
  48 +<<
  49 + /Contents 7 0 R
  50 + /MediaBox [
  51 + 0
  52 + 0
  53 + 612
  54 + 792
  55 + ]
  56 + /Parent 3 0 R
  57 + /Resources <<
  58 + /Font <<
  59 + /F1 9 0 R
  60 + >>
  61 + /ProcSet 10 0 R
  62 + >>
  63 + /Type /Page
  64 +>>
  65 +endobj
  66 +
  67 +%% Contents for page 1
  68 +7 0 obj
  69 +<<
  70 + /Length 8 0 R
  71 +>>
  72 +stream
  73 +BT
  74 + /F1 24 Tf
  75 + 72 720 Td
  76 + (Potato) Tj
  77 +ET
  78 +endstream
  79 +endobj
  80 +
  81 +8 0 obj
  82 +44
  83 +endobj
  84 +
  85 +9 0 obj
  86 +<<
  87 + /BaseFont /Helvetica
  88 + /Encoding /WinAnsiEncoding
  89 + /Name /F1
  90 + /Subtype /Type1
  91 + /Type /Font
  92 +>>
  93 +endobj
  94 +
  95 +10 0 obj
  96 +[
  97 + /PDF
  98 + /Text
  99 +]
  100 +endobj
  101 +
  102 +11 0 obj
  103 +<<
  104 + /A 12 0 R
  105 + /C (potato)
  106 +>>
  107 +endobj
  108 +
  109 +12 0 obj
  110 +<< /B 13 0 R >>
  111 +endobj
  112 +
  113 +13 0 obj
  114 +<<
  115 + /Length 14 0 R
  116 +>>
  117 +stream
  118 +salad
  119 +endstream
  120 +endobj
  121 +
  122 +14 0 obj
  123 +6
  124 +endobj
  125 +
  126 +xref
  127 +0 15
  128 +0000000000 65535 f
  129 +0000000025 00000 n
  130 +0000000079 00000 n
  131 +0000000174 00000 n
  132 +0000000246 00000 n
  133 +0000000280 00000 n
  134 +0000000332 00000 n
  135 +0000000548 00000 n
  136 +0000000647 00000 n
  137 +0000000666 00000 n
  138 +0000000784 00000 n
  139 +0000000820 00000 n
  140 +0000000869 00000 n
  141 +0000000902 00000 n
  142 +0000000965 00000 n
  143 +trailer <<
  144 + /QTest 2 0 R
  145 + /QTest2 11 0 R
  146 + /Root 1 0 R
  147 + /Size 15
  148 + /ID [<c61bd35bada064f61e0a56aa9588064e><c893e7330be149468080ad6518819868>]
  149 +>>
  150 +startxref
  151 +984
  152 +%%EOF
... ...
qpdf/qtest/qpdf/test4-5.qdf 0 โ†’ 100644
  1 +%PDF-1.3
  2 +%ยฟรทยขรพ
  3 +%QDF-1.0
  4 +
  5 +%% Original object ID: 1 0
  6 +1 0 obj
  7 +<<
  8 + /Pages 6 0 R
  9 + /Type /Catalog
  10 +>>
  11 +endobj
  12 +
  13 +%% Original object ID: 15 0
  14 +2 0 obj
  15 +<<
  16 + /A [
  17 + 14
  18 + 15
  19 + 9
  20 + ]
  21 + /Author (Mr. Potato Head)
  22 + /B <<
  23 + /A [
  24 + 100
  25 + 2
  26 + 3
  27 + ]
  28 + /B (B)
  29 + >>
  30 + /Title (Some Title Is Here)
  31 +>>
  32 +endobj
  33 +
  34 +%% Original object ID: 2 0
  35 +3 0 obj
  36 +<<
  37 + /A 7 0 R
  38 + /B 8 0 R
  39 + /Subject (Subject)
  40 + /Title (Some Title Is Here)
  41 +>>
  42 +endobj
  43 +
  44 +%% Original object ID: 13 0
  45 +4 0 obj
  46 +<<
  47 + /Length 5 0 R
  48 +>>
  49 +stream
  50 +salad
  51 +endstream
  52 +endobj
  53 +
  54 +5 0 obj
  55 +6
  56 +endobj
  57 +
  58 +%% Original object ID: 3 0
  59 +6 0 obj
  60 +<<
  61 + /Count 1
  62 + /Kids [
  63 + 9 0 R
  64 + ]
  65 + /Type /Pages
  66 +>>
  67 +endobj
  68 +
  69 +%% Original object ID: 4 0
  70 +7 0 obj
  71 +[
  72 + 100
  73 + 2
  74 + 3
  75 +]
  76 +endobj
  77 +
  78 +%% Original object ID: 5 0
  79 +8 0 obj
  80 +<<
  81 + /A 7 0 R
  82 + /B (B)
  83 +>>
  84 +endobj
  85 +
  86 +%% Page 1
  87 +%% Original object ID: 6 0
  88 +9 0 obj
  89 +<<
  90 + /Contents 10 0 R
  91 + /MediaBox [
  92 + 0
  93 + 0
  94 + 612
  95 + 792
  96 + ]
  97 + /Parent 6 0 R
  98 + /Resources <<
  99 + /Font <<
  100 + /F1 12 0 R
  101 + >>
  102 + /ProcSet 13 0 R
  103 + >>
  104 + /Type /Page
  105 +>>
  106 +endobj
  107 +
  108 +%% Contents for page 1
  109 +%% Original object ID: 7 0
  110 +10 0 obj
  111 +<<
  112 + /Length 11 0 R
  113 +>>
  114 +stream
  115 +BT
  116 + /F1 24 Tf
  117 + 72 720 Td
  118 + (Potato) Tj
  119 +ET
  120 +endstream
  121 +endobj
  122 +
  123 +11 0 obj
  124 +44
  125 +endobj
  126 +
  127 +%% Original object ID: 9 0
  128 +12 0 obj
  129 +<<
  130 + /BaseFont /Helvetica
  131 + /Encoding /WinAnsiEncoding
  132 + /Name /F1
  133 + /Subtype /Type1
  134 + /Type /Font
  135 +>>
  136 +endobj
  137 +
  138 +%% Original object ID: 10 0
  139 +13 0 obj
  140 +[
  141 + /PDF
  142 + /Text
  143 +]
  144 +endobj
  145 +
  146 +xref
  147 +0 14
  148 +0000000000 65535 f
  149 +0000000052 00000 n
  150 +0000000134 00000 n
  151 +0000000337 00000 n
  152 +0000000460 00000 n
  153 +0000000521 00000 n
  154 +0000000566 00000 n
  155 +0000000665 00000 n
  156 +0000000726 00000 n
  157 +0000000805 00000 n
  158 +0000001050 00000 n
  159 +0000001151 00000 n
  160 +0000001198 00000 n
  161 +0000001345 00000 n
  162 +trailer <<
  163 + /Info 2 0 R
  164 + /QTest 3 0 R
  165 + /QTest2 <<
  166 + /A <<
  167 + /B 4 0 R
  168 + >>
  169 + /C (potato)
  170 + >>
  171 + /Root 1 0 R
  172 + /Size 14
  173 + /ID [<c61bd35bada064f61e0a56aa9588064e><31415926535897932384626433832795>]
  174 +>>
  175 +startxref
  176 +1381
  177 +%%EOF
... ...
qpdf/test_driver.cc
... ... @@ -485,6 +485,14 @@ void runtest(int n, char const* filename1, char const* arg2)
485 485 A.setArrayFromVector(items);
486 486 }
487 487  
  488 + QPDFObjectHandle qtest2 = trailer.getKey("/QTest2");
  489 + if (! qtest2.isNull())
  490 + {
  491 + // Test allow_streams=true
  492 + qtest2.makeDirect(true);
  493 + trailer.replaceKey("/QTest2", qtest2);
  494 + }
  495 +
488 496 trailer.replaceKey("/Info", pdf.makeIndirectObject(qtest));
489 497 QPDFWriter w(pdf, 0);
490 498 w.setQDFMode(true);
... ...