Commit c3cee5f1549df6ba3a7829fcbf6a59739636bb88

Authored by Jay Berkenbilt
1 parent fddbcab0

Exercise out of scope original pdf for copyForeignObject

libqpdf/QPDF.cc
@@ -2380,7 +2380,7 @@ QPDF::replaceForeignIndirectObjects( @@ -2380,7 +2380,7 @@ QPDF::replaceForeignIndirectObjects(
2380 stream->getStreamDataProvider(); 2380 stream->getStreamDataProvider();
2381 if (stream_buffer.getPointer()) 2381 if (stream_buffer.getPointer())
2382 { 2382 {
2383 -// QTC::TC("qpdf", "QPDF copy foreign stream with buffer"); 2383 + QTC::TC("qpdf", "QPDF copy foreign stream with buffer");
2384 result.replaceStreamData(stream_buffer, 2384 result.replaceStreamData(stream_buffer,
2385 dict.getKey("/Filter"), 2385 dict.getKey("/Filter"),
2386 dict.getKey("/DecodeParms")); 2386 dict.getKey("/DecodeParms"));
@@ -2388,7 +2388,7 @@ QPDF::replaceForeignIndirectObjects( @@ -2388,7 +2388,7 @@ QPDF::replaceForeignIndirectObjects(
2388 else if (stream_provider.getPointer()) 2388 else if (stream_provider.getPointer())
2389 { 2389 {
2390 // In this case, the remote stream's QPDF must stay in scope. 2390 // In this case, the remote stream's QPDF must stay in scope.
2391 -// QTC::TC("qpdf", "QPDF copy foreign stream with provider"); 2391 + QTC::TC("qpdf", "QPDF copy foreign stream with provider");
2392 this->m->copied_stream_data_provider->registerForeignStream( 2392 this->m->copied_stream_data_provider->registerForeignStream(
2393 local_og, foreign); 2393 local_og, foreign);
2394 result.replaceStreamData(this->m->copied_streams, 2394 result.replaceStreamData(this->m->copied_streams,
qpdf/qpdf.testcov
@@ -408,3 +408,5 @@ qpdf image optimize too small 0 @@ -408,3 +408,5 @@ qpdf image optimize too small 0
408 QPDFFormFieldObjectHelper WinAnsi 0 408 QPDFFormFieldObjectHelper WinAnsi 0
409 QPDF_encryption attachment stream 0 409 QPDF_encryption attachment stream 0
410 QPDF pipe foreign encrypted stream 0 410 QPDF pipe foreign encrypted stream 0
  411 +QPDF copy foreign stream with provider 0
  412 +QPDF copy foreign stream with buffer 0
qpdf/qtest/qpdf.test
@@ -1921,7 +1921,7 @@ foreach my $d ([25, 1], [26, 2], [27, 3]) @@ -1921,7 +1921,7 @@ foreach my $d ([25, 1], [26, 2], [27, 3])
1921 my ($testn, $outn) = @$d; 1921 my ($testn, $outn) = @$d;
1922 $td->runtest("copy objects $outn", 1922 $td->runtest("copy objects $outn",
1923 {$td->COMMAND => "test_driver $testn" . 1923 {$td->COMMAND => "test_driver $testn" .
1924 - " copy-foreign-objects-in.pdf minimal.pdf"}, 1924 + " minimal.pdf copy-foreign-objects-in.pdf"},
1925 {$td->STRING => "test $testn done\n", $td->EXIT_STATUS => 0}, 1925 {$td->STRING => "test $testn done\n", $td->EXIT_STATUS => 0},
1926 $td->NORMALIZE_NEWLINES); 1926 $td->NORMALIZE_NEWLINES);
1927 $td->runtest("check output", 1927 $td->runtest("check output",
qpdf/qtest/qpdf/copy-foreign-objects-out3.pdf
1 %PDF-1.3 1 %PDF-1.3
2 %¿÷¢þ 2 %¿÷¢þ
3 1 0 obj 3 1 0 obj
4 -<< /Pages 3 0 R /Type /Catalog >> 4 +<< /Pages 5 0 R /Type /Catalog >>
5 endobj 5 endobj
6 2 0 obj 6 2 0 obj
7 -<< /O1 4 0 R /O2 5 0 R /O3 6 0 R /This-is-QTest true >> 7 +<< /O1 6 0 R /O2 7 0 R /O3 8 0 R /This-is-QTest true >>
8 endobj 8 endobj
9 3 0 obj 9 3 0 obj
10 -<< /Count 3 /Kids [ 7 0 R 8 0 R 6 0 R ] /Type /Pages >> 10 +<< /Length 20 >>
  11 +stream
  12 +new data for stream
  13 +endstream
11 endobj 14 endobj
12 4 0 obj 15 4 0 obj
13 -[ /This-is-O1 /potato << /O2 [ 3.14159 << /O2 5 0 R >> 2.17828 ] >> /salad /O2 5 0 R /Stream1 9 0 R ] 16 +<< /Length 7 >>
  17 +stream
  18 +potato
  19 +endstream
14 endobj 20 endobj
15 5 0 obj 21 5 0 obj
16 -<< /K1 [ 2.236 /O1 4 0 R 1.732 ] /O1 4 0 R /This-is-O2 true >> 22 +<< /Count 3 /Kids [ 9 0 R 10 0 R 8 0 R ] /Type /Pages >>
17 endobj 23 endobj
18 6 0 obj 24 6 0 obj
19 -<< /Contents 10 0 R /MediaBox [ 0 0 612 792 ] /OtherPage 8 0 R /Parent 3 0 R /Resources << /Font << /F1 11 0 R >> /ProcSet [ /PDF /Text ] >> /Rotate 180 /This-is-O3 true /Type /Page >> 25 +[ /This-is-O1 /potato << /O2 [ 3.14159 << /O2 7 0 R >> 2.17828 ] >> /salad /O2 7 0 R /Stream1 11 0 R ]
20 endobj 26 endobj
21 7 0 obj 27 7 0 obj
22 -<< /Contents 12 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 13 0 R >> /ProcSet 14 0 R >> /Type /Page >> 28 +<< /K1 [ 2.236 /O1 6 0 R 1.732 ] /O1 6 0 R /This-is-O2 true >>
23 endobj 29 endobj
24 8 0 obj 30 8 0 obj
25 -<< /Contents 15 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 11 0 R >> /ProcSet [ /PDF /Text ] >> /Rotate 180 /This-is-O3-other-page true /Type /Page >> 31 +<< /Contents 12 0 R /MediaBox [ 0 0 612 792 ] /OtherPage 10 0 R /Parent 5 0 R /Resources << /Font << /F1 13 0 R >> /ProcSet [ /PDF /Text ] >> /Rotate 180 /This-is-O3 true /Type /Page >>
26 endobj 32 endobj
27 9 0 obj 33 9 0 obj
28 -<< /Stream2 16 0 R /This-is-Stream1 true /Length 18 >> 34 +<< /Contents 14 0 R /MediaBox [ 0 0 612 792 ] /Parent 5 0 R /Resources << /Font << /F1 15 0 R >> /ProcSet 16 0 R >> /Type /Page >>
  35 +endobj
  36 +10 0 obj
  37 +<< /Contents 17 0 R /MediaBox [ 0 0 612 792 ] /Parent 5 0 R /Resources << /Font << /F1 13 0 R >> /ProcSet [ /PDF /Text ] >> /Rotate 180 /This-is-O3-other-page true /Type /Page >>
  38 +endobj
  39 +11 0 obj
  40 +<< /Stream2 18 0 R /This-is-Stream1 true /Length 18 >>
29 stream 41 stream
30 This is stream 1. 42 This is stream 1.
31 endstream 43 endstream
32 endobj 44 endobj
33 -10 0 obj 45 +12 0 obj
34 << /Length 47 >> 46 << /Length 47 >>
35 stream 47 stream
36 BT /F1 15 Tf 72 720 Td (Original page 2) Tj ET 48 BT /F1 15 Tf 72 720 Td (Original page 2) Tj ET
37 endstream 49 endstream
38 endobj 50 endobj
39 -11 0 obj 51 +13 0 obj
40 << /BaseFont /Times-Roman /Encoding /WinAnsiEncoding /Subtype /Type1 /Type /Font >> 52 << /BaseFont /Times-Roman /Encoding /WinAnsiEncoding /Subtype /Type1 /Type /Font >>
41 endobj 53 endobj
42 -12 0 obj 54 +14 0 obj
43 << /Length 44 >> 55 << /Length 44 >>
44 stream 56 stream
45 BT 57 BT
@@ -49,44 +61,46 @@ BT @@ -49,44 +61,46 @@ BT
49 ET 61 ET
50 endstream 62 endstream
51 endobj 63 endobj
52 -13 0 obj 64 +15 0 obj
53 << /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >> 65 << /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >>
54 endobj 66 endobj
55 -14 0 obj 67 +16 0 obj
56 [ /PDF /Text ] 68 [ /PDF /Text ]
57 endobj 69 endobj
58 -15 0 obj 70 +17 0 obj
59 << /Length 47 >> 71 << /Length 47 >>
60 stream 72 stream
61 BT /F1 15 Tf 72 720 Td (Original page 3) Tj ET 73 BT /F1 15 Tf 72 720 Td (Original page 3) Tj ET
62 endstream 74 endstream
63 endobj 75 endobj
64 -16 0 obj  
65 -<< /Stream1 9 0 R /This-is-Stream2 true /Length 18 >> 76 +18 0 obj
  77 +<< /Stream1 11 0 R /This-is-Stream2 true /Length 18 >>
66 stream 78 stream
67 This is stream 2. 79 This is stream 2.
68 endstream 80 endstream
69 endobj 81 endobj
70 xref 82 xref
71 -0 17 83 +0 19
72 0000000000 65535 f 84 0000000000 65535 f
73 0000000015 00000 n 85 0000000015 00000 n
74 0000000064 00000 n 86 0000000064 00000 n
75 0000000135 00000 n 87 0000000135 00000 n
76 -0000000206 00000 n  
77 -0000000323 00000 n  
78 -0000000401 00000 n  
79 -0000000601 00000 n  
80 -0000000747 00000 n  
81 -0000000941 00000 n  
82 -0000001046 00000 n  
83 -0000001143 00000 n  
84 -0000001243 00000 n  
85 -0000001337 00000 n  
86 -0000001445 00000 n  
87 -0000001476 00000 n  
88 -0000001573 00000 n  
89 -trailer << /QTest 2 0 R /Root 1 0 R /Size 17 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] >> 88 +0000000204 00000 n
  89 +0000000259 00000 n
  90 +0000000331 00000 n
  91 +0000000449 00000 n
  92 +0000000527 00000 n
  93 +0000000728 00000 n
  94 +0000000874 00000 n
  95 +0000001069 00000 n
  96 +0000001175 00000 n
  97 +0000001272 00000 n
  98 +0000001372 00000 n
  99 +0000001466 00000 n
  100 +0000001574 00000 n
  101 +0000001605 00000 n
  102 +0000001702 00000 n
  103 +trailer << /QTest 2 0 R /QTest2 [ 3 0 R 4 0 R ] /Root 1 0 R /Size 19 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] >>
90 startxref 104 startxref
91 -1678 105 +1808
92 %%EOF 106 %%EOF
qpdf/test_driver.cc
@@ -1085,13 +1085,16 @@ void runtest(int n, char const* filename1, char const* arg2) @@ -1085,13 +1085,16 @@ void runtest(int n, char const* filename1, char const* arg2)
1085 // and O2 and their streams but not O3 or any other pages. 1085 // and O2 and their streams but not O3 or any other pages.
1086 1086
1087 assert(arg2 != 0); 1087 assert(arg2 != 0);
1088 - QPDF newpdf;  
1089 - newpdf.processFile(arg2);  
1090 - QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest");  
1091 - newpdf.getTrailer().replaceKey(  
1092 - "/QTest", newpdf.copyForeignObject(qtest)); 1088 + {
  1089 + // Make sure original PDF is out of scope when we write.
  1090 + QPDF oldpdf;
  1091 + oldpdf.processFile(arg2);
  1092 + QPDFObjectHandle qtest = oldpdf.getTrailer().getKey("/QTest");
  1093 + pdf.getTrailer().replaceKey(
  1094 + "/QTest", pdf.copyForeignObject(qtest));
  1095 + }
1093 1096
1094 - QPDFWriter w(newpdf, "a.pdf"); 1097 + QPDFWriter w(pdf, "a.pdf");
1095 w.setStaticID(true); 1098 w.setStaticID(true);
1096 w.setStreamDataMode(qpdf_s_preserve); 1099 w.setStreamDataMode(qpdf_s_preserve);
1097 w.write(); 1100 w.write();
@@ -1104,16 +1107,19 @@ void runtest(int n, char const* filename1, char const* arg2) @@ -1104,16 +1107,19 @@ void runtest(int n, char const* filename1, char const* arg2)
1104 // that O3 points to. Also, inherited object will have been 1107 // that O3 points to. Also, inherited object will have been
1105 // pushed down and will be preserved. 1108 // pushed down and will be preserved.
1106 1109
1107 - assert(arg2 != 0);  
1108 - QPDF newpdf;  
1109 - newpdf.processFile(arg2);  
1110 - QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest");  
1111 - QPDFObjectHandle O3 = qtest.getKey("/O3");  
1112 - QPDFPageDocumentHelper(newpdf).addPage(O3, false);  
1113 - newpdf.getTrailer().replaceKey(  
1114 - "/QTest", newpdf.copyForeignObject(qtest)); 1110 + {
  1111 + // Make sure original PDF is out of scope when we write.
  1112 + assert(arg2 != 0);
  1113 + QPDF oldpdf;
  1114 + oldpdf.processFile(arg2);
  1115 + QPDFObjectHandle qtest = oldpdf.getTrailer().getKey("/QTest");
  1116 + QPDFObjectHandle O3 = qtest.getKey("/O3");
  1117 + QPDFPageDocumentHelper(pdf).addPage(O3, false);
  1118 + pdf.getTrailer().replaceKey(
  1119 + "/QTest", pdf.copyForeignObject(qtest));
  1120 + }
1115 1121
1116 - QPDFWriter w(newpdf, "a.pdf"); 1122 + QPDFWriter w(pdf, "a.pdf");
1117 w.setStaticID(true); 1123 w.setStaticID(true);
1118 w.setStreamDataMode(qpdf_s_preserve); 1124 w.setStreamDataMode(qpdf_s_preserve);
1119 w.write(); 1125 w.write();
@@ -1122,20 +1128,48 @@ void runtest(int n, char const* filename1, char const* arg2) @@ -1122,20 +1128,48 @@ void runtest(int n, char const* filename1, char const* arg2)
1122 { 1128 {
1123 // Copy O3 and the page O3 refers to before copying qtest. 1129 // Copy O3 and the page O3 refers to before copying qtest.
1124 // Should get qtest plus only the O3 page and the page that O3 1130 // Should get qtest plus only the O3 page and the page that O3
1125 - // points to. Inherited objects should be preserved. 1131 + // points to. Inherited objects should be preserved. This test
  1132 + // also exercises copying from a stream that has a buffer and
  1133 + // a provider, including copying a provider multiple times.
1126 1134
1127 - assert(arg2 != 0);  
1128 - QPDF newpdf;  
1129 - newpdf.processFile(arg2);  
1130 - QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest");  
1131 - QPDFObjectHandle O3 = qtest.getKey("/O3");  
1132 - QPDFPageDocumentHelper dh(newpdf);  
1133 - dh.addPage(O3.getKey("/OtherPage"), false);  
1134 - dh.addPage(O3, false);  
1135 - newpdf.getTrailer().replaceKey(  
1136 - "/QTest", newpdf.copyForeignObject(qtest));  
1137 -  
1138 - QPDFWriter w(newpdf, "a.pdf"); 1135 + Pl_Buffer p1("buffer");
  1136 + p1.write(QUtil::unsigned_char_pointer("new data for stream\n"),
  1137 + 20); // no null!
  1138 + p1.finish();
  1139 + PointerHolder<Buffer> b = p1.getBuffer();
  1140 + Provider* provider = new Provider(b);
  1141 + PointerHolder<QPDFObjectHandle::StreamDataProvider> p = provider;
  1142 + QPDF empty1;
  1143 + empty1.emptyPDF();
  1144 + QPDFObjectHandle s1 = QPDFObjectHandle::newStream(&empty1);
  1145 + s1.replaceStreamData(
  1146 + p, QPDFObjectHandle::newNull(), QPDFObjectHandle::newNull());
  1147 + QPDF empty2;
  1148 + empty2.emptyPDF();
  1149 + s1 = empty2.copyForeignObject(s1);
  1150 + {
  1151 + // Make sure original PDF is out of scope when we write.
  1152 + assert(arg2 != 0);
  1153 + QPDF oldpdf;
  1154 + oldpdf.processFile(arg2);
  1155 + QPDFObjectHandle qtest = oldpdf.getTrailer().getKey("/QTest");
  1156 + QPDFObjectHandle O3 = qtest.getKey("/O3");
  1157 + QPDFPageDocumentHelper dh(pdf);
  1158 + dh.addPage(O3.getKey("/OtherPage"), false);
  1159 + dh.addPage(O3, false);
  1160 + QPDFObjectHandle s2 = QPDFObjectHandle::newStream(
  1161 + &oldpdf, "potato\n");
  1162 + pdf.getTrailer().replaceKey(
  1163 + "/QTest", pdf.copyForeignObject(qtest));
  1164 + pdf.getTrailer().replaceKey(
  1165 + "/QTest2", QPDFObjectHandle::newArray());
  1166 + pdf.getTrailer().getKey("/QTest2").appendItem(
  1167 + pdf.copyForeignObject(s1));
  1168 + pdf.getTrailer().getKey("/QTest2").appendItem(
  1169 + pdf.copyForeignObject(s2));
  1170 + }
  1171 +
  1172 + QPDFWriter w(pdf, "a.pdf");
1139 w.setStaticID(true); 1173 w.setStaticID(true);
1140 w.setStreamDataMode(qpdf_s_preserve); 1174 w.setStreamDataMode(qpdf_s_preserve);
1141 w.write(); 1175 w.write();