Commit cc8895078a1d64928e8ee335f1e8c7d6928de1b3
1 parent
573b6eb8
Add QPDFObjectHandle::makeDirect(bool allow_streams)
Showing
9 changed files
with
390 additions
and
13 deletions
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, | ... | ... |
TODO
| ... | ... | @@ -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& 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<QPDFObjGen>& 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<QPDFObjGen>& 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<QPDFObjGen>& 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->runtest("check output", |
| 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->runtest("object ordering", |
| 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 | ||
| 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 | ||
| 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); | ... | ... |