summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libqpdf/QPDF_pages.cc39
-rw-r--r--qpdf/qpdf.testcov2
-rw-r--r--qpdf/qtest/qpdf.test22
-rw-r--r--qpdf/qtest/qpdf/page_api_1-out.pdf168
-rw-r--r--qpdf/qtest/qpdf/page_api_1-out2.pdf146
-rw-r--r--qpdf/qtest/qpdf/page_api_1.pdf472
-rw-r--r--qpdf/qtest/qpdf/page_api_2.out1
-rw-r--r--qpdf/qtest/qpdf/page_api_2.pdf137
-rw-r--r--qpdf/test_driver.cc150
9 files changed, 1125 insertions, 12 deletions
diff --git a/libqpdf/QPDF_pages.cc b/libqpdf/QPDF_pages.cc
index 930e8bd1..ed16e2ef 100644
--- a/libqpdf/QPDF_pages.cc
+++ b/libqpdf/QPDF_pages.cc
@@ -126,18 +126,23 @@ QPDF::insertPageobjToPage(QPDFObjectHandle const& obj, int pos,
bool check_duplicate)
{
ObjGen og(obj.getObjectID(), obj.getGeneration());
- bool duplicate =
- (! this->pageobj_to_pages_pos.insert(std::make_pair(og, pos)).second);
- if (duplicate && check_duplicate)
- {
- QTC::TC("qpdf", "QPDF duplicate page reference");
- setLastObjectDescription("page " + QUtil::int_to_string(pos) +
- " (numbered from zero)",
- og.obj, og.gen);
- throw QPDFExc(qpdf_e_pages, this->file->getName(),
- this->last_object_description, 0,
- "duplicate page reference found;"
- " this would cause loss of data");
+ if (check_duplicate)
+ {
+ if (! this->pageobj_to_pages_pos.insert(std::make_pair(og, pos)).second)
+ {
+ QTC::TC("qpdf", "QPDF duplicate page reference");
+ setLastObjectDescription("page " + QUtil::int_to_string(pos) +
+ " (numbered from zero)",
+ og.obj, og.gen);
+ throw QPDFExc(qpdf_e_pages, this->file->getName(),
+ this->last_object_description, 0,
+ "duplicate page reference found;"
+ " this would cause loss of data");
+ }
+ }
+ else
+ {
+ this->pageobj_to_pages_pos[og] = pos;
}
}
@@ -150,6 +155,16 @@ QPDF::insertPage(QPDFObjectHandle newpage, int pos)
flattenPagesTree();
newpage.assertPageObject();
+ if (! newpage.isIndirect())
+ {
+ QTC::TC("qpdf", "QPDF insert non-indirect page");
+ newpage = this->makeIndirectObject(newpage);
+ }
+ else
+ {
+ QTC::TC("qpdf", "QPDF insert indirect page");
+ }
+
QTC::TC("qpdf", "QPDF insert page",
(pos == 0) ? 0 : // insert at beginning
(pos == ((int)this->all_pages.size())) ? 1 : // insert at end
diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov
index 249497a3..c7207bc8 100644
--- a/qpdf/qpdf.testcov
+++ b/qpdf/qpdf.testcov
@@ -207,3 +207,5 @@ QPDF duplicate page reference 0
QPDF remove page 2
QPDF insert page 2
QPDF updateAllPagesCache 0
+QPDF insert non-indirect page 0
+QPDF insert indirect page 0
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index 03e114f7..bc0d5643 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -110,6 +110,28 @@ $td->runtest("new stream",
show_ntests();
# ----------
+$td->notify("--- Page API Tests ---");
+$n_tests += 5;
+
+$td->runtest("basic page API",
+ {$td->COMMAND => "test_driver 15 page_api_1.pdf"},
+ {$td->STRING => "test 15 done\n", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("check output",
+ {$td->FILE => "a.pdf"},
+ {$td->FILE => "page_api_1-out.pdf"});
+$td->runtest("manual page manipulation",
+ {$td->COMMAND => "test_driver 16 page_api_1.pdf"},
+ {$td->STRING => "test 16 done\n", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("check output",
+ {$td->FILE => "a.pdf"},
+ {$td->FILE => "page_api_1-out2.pdf"});
+$td->runtest("duplicate page",
+ {$td->COMMAND => "test_driver 17 page_api_2.pdf"},
+ {$td->FILE => "page_api_2.out", $td->EXIT_STATUS => 2},
+ $td->NORMALIZE_NEWLINES);
+# ----------
$td->notify("--- Miscellaneous Tests ---");
$n_tests += 37;
diff --git a/qpdf/qtest/qpdf/page_api_1-out.pdf b/qpdf/qtest/qpdf/page_api_1-out.pdf
new file mode 100644
index 00000000..b5439712
--- /dev/null
+++ b/qpdf/qtest/qpdf/page_api_1-out.pdf
@@ -0,0 +1,168 @@
+%PDF-1.3
+%¿÷¢þ
+1 0 obj
+<< /Pages 3 0 R /Type /Catalog >>
+endobj
+2 0 obj
+<< /CreationDate (D:20120621111522) /Producer (Apex PDFWriter) >>
+endobj
+3 0 obj
+<< /Count 13 /Kids [ 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 14 0 R 15 0 R 16 0 R ] /Type /Pages >>
+endobj
+4 0 obj
+<< /Contents 17 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 18 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+5 0 obj
+<< /Contents 19 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 18 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+6 0 obj
+<< /Contents 20 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 18 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+7 0 obj
+<< /Contents 21 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 18 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+8 0 obj
+<< /Contents 22 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 18 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+9 0 obj
+<< /Contents 23 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 18 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+10 0 obj
+<< /Contents 24 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 18 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+11 0 obj
+<< /Contents 25 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 18 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+12 0 obj
+<< /Contents 26 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 18 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+13 0 obj
+<< /Contents 27 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 18 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+14 0 obj
+<< /Contents 28 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 18 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+15 0 obj
+<< /Contents 29 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 18 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+16 0 obj
+<< /Contents 30 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 18 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+17 0 obj
+<< /Length 42 >>
+stream
+BT /F1 15 Tf 72 720 Td (New page 0) Tj ET
+endstream
+endobj
+18 0 obj
+<< /BaseFont /Times-Roman /Encoding /WinAnsiEncoding /Subtype /Type1 /Type /Font >>
+endobj
+19 0 obj
+<< /Length 42 >>
+stream
+BT /F1 15 Tf 72 720 Td (New page 1) Tj ET
+endstream
+endobj
+20 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 1) Tj ET
+endstream
+endobj
+21 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 2) Tj ET
+endstream
+endobj
+22 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 3) Tj ET
+endstream
+endobj
+23 0 obj
+<< /Length 42 >>
+stream
+BT /F1 15 Tf 72 720 Td (New page 5) Tj ET
+endstream
+endobj
+24 0 obj
+<< /Length 42 >>
+stream
+BT /F1 15 Tf 72 720 Td (New page 6) Tj ET
+endstream
+endobj
+25 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 4) Tj ET
+endstream
+endobj
+26 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 6) Tj ET
+endstream
+endobj
+27 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 7) Tj ET
+endstream
+endobj
+28 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 8) Tj ET
+endstream
+endobj
+29 0 obj
+<< /Length 43 >>
+stream
+BT /F1 15 Tf 72 720 Td (New page 11) Tj ET
+endstream
+endobj
+30 0 obj
+<< /Length 43 >>
+stream
+BT /F1 15 Tf 72 720 Td (New page 12) Tj ET
+endstream
+endobj
+xref
+0 31
+0000000000 65535 f
+0000000015 00000 n
+0000000064 00000 n
+0000000145 00000 n
+0000000284 00000 n
+0000000438 00000 n
+0000000592 00000 n
+0000000746 00000 n
+0000000900 00000 n
+0000001054 00000 n
+0000001208 00000 n
+0000001363 00000 n
+0000001518 00000 n
+0000001673 00000 n
+0000001828 00000 n
+0000001983 00000 n
+0000002138 00000 n
+0000002293 00000 n
+0000002385 00000 n
+0000002485 00000 n
+0000002577 00000 n
+0000002674 00000 n
+0000002771 00000 n
+0000002868 00000 n
+0000002960 00000 n
+0000003052 00000 n
+0000003149 00000 n
+0000003246 00000 n
+0000003343 00000 n
+0000003440 00000 n
+0000003533 00000 n
+trailer << /Info 2 0 R /Root 1 0 R /Size 31 /ID [<21f7a6fb083dab8e29743918a08bfa31><31415926535897932384626433832795>] >>
+startxref
+3626
+%%EOF
diff --git a/qpdf/qtest/qpdf/page_api_1-out2.pdf b/qpdf/qtest/qpdf/page_api_1-out2.pdf
new file mode 100644
index 00000000..7ec2142e
--- /dev/null
+++ b/qpdf/qtest/qpdf/page_api_1-out2.pdf
@@ -0,0 +1,146 @@
+%PDF-1.3
+%¿÷¢þ
+1 0 obj
+<< /Pages 3 0 R /Type /Catalog >>
+endobj
+2 0 obj
+<< /CreationDate (D:20120621111522) /Producer (Apex PDFWriter) >>
+endobj
+3 0 obj
+<< /Count 11 /Kids [ 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 14 0 R ] /Type /Pages >>
+endobj
+4 0 obj
+<< /Contents 15 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 16 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+5 0 obj
+<< /Contents 17 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 16 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+6 0 obj
+<< /Contents 18 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 16 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+7 0 obj
+<< /Contents 19 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 16 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+8 0 obj
+<< /Contents 20 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 16 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+9 0 obj
+<< /Contents 21 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 16 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+10 0 obj
+<< /Contents 22 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 16 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+11 0 obj
+<< /Contents 23 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 16 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+12 0 obj
+<< /Contents 24 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 16 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+13 0 obj
+<< /Contents 25 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 16 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+14 0 obj
+<< /Contents 26 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 16 0 R >> /ProcSet [ /PDF /Text ] >> /Type /Page >>
+endobj
+15 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 0) Tj ET
+endstream
+endobj
+16 0 obj
+<< /BaseFont /Times-Roman /Encoding /WinAnsiEncoding /Subtype /Type1 /Type /Font >>
+endobj
+17 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 1) Tj ET
+endstream
+endobj
+18 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 2) Tj ET
+endstream
+endobj
+19 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 3) Tj ET
+endstream
+endobj
+20 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 4) Tj ET
+endstream
+endobj
+21 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 5) Tj ET
+endstream
+endobj
+22 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 6) Tj ET
+endstream
+endobj
+23 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 7) Tj ET
+endstream
+endobj
+24 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 8) Tj ET
+endstream
+endobj
+25 0 obj
+<< /Length 47 >>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 9) Tj ET
+endstream
+endobj
+26 0 obj
+<< /Length 43 >>
+stream
+BT /F1 15 Tf 72 720 Td (New page 10) Tj ET
+endstream
+endobj
+xref
+0 27
+0000000000 65535 f
+0000000015 00000 n
+0000000064 00000 n
+0000000145 00000 n
+0000000270 00000 n
+0000000424 00000 n
+0000000578 00000 n
+0000000732 00000 n
+0000000886 00000 n
+0000001040 00000 n
+0000001194 00000 n
+0000001349 00000 n
+0000001504 00000 n
+0000001659 00000 n
+0000001814 00000 n
+0000001969 00000 n
+0000002066 00000 n
+0000002166 00000 n
+0000002263 00000 n
+0000002360 00000 n
+0000002457 00000 n
+0000002554 00000 n
+0000002651 00000 n
+0000002748 00000 n
+0000002845 00000 n
+0000002942 00000 n
+0000003039 00000 n
+trailer << /Info 2 0 R /Root 1 0 R /Size 27 /ID [<21f7a6fb083dab8e29743918a08bfa31><31415926535897932384626433832795>] >>
+startxref
+3132
+%%EOF
diff --git a/qpdf/qtest/qpdf/page_api_1.pdf b/qpdf/qtest/qpdf/page_api_1.pdf
new file mode 100644
index 00000000..8abe106c
--- /dev/null
+++ b/qpdf/qtest/qpdf/page_api_1.pdf
@@ -0,0 +1,472 @@
+%PDF-1.3
+%¿÷¢þ
+%QDF-1.0
+
+1 0 obj
+<<
+ /Pages 3 0 R
+ /Type /Catalog
+>>
+endobj
+
+2 0 obj
+<<
+ /CreationDate (D:20120621111522)
+ /Producer (Apex PDFWriter)
+>>
+endobj
+
+3 0 obj
+<<
+ /Count 10
+ /Kids [
+ 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
+ ]
+ /Type /Pages
+>>
+endobj
+
+%% Page 1
+4 0 obj
+<<
+ /Contents 14 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 16 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 2
+5 0 obj
+<<
+ /Contents 17 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 16 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 3
+6 0 obj
+<<
+ /Contents 19 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 16 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 4
+7 0 obj
+<<
+ /Contents 21 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 16 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 5
+8 0 obj
+<<
+ /Contents 23 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 16 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 6
+9 0 obj
+<<
+ /Contents 25 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 16 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 7
+10 0 obj
+<<
+ /Contents 27 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 16 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 8
+11 0 obj
+<<
+ /Contents 29 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 16 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 9
+12 0 obj
+<<
+ /Contents 31 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 16 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 10
+13 0 obj
+<<
+ /Contents 33 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 16 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Contents for page 1
+14 0 obj
+<<
+ /Length 15 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 0) Tj ET
+endstream
+endobj
+
+15 0 obj
+47
+endobj
+
+16 0 obj
+<<
+ /BaseFont /Times-Roman
+ /Encoding /WinAnsiEncoding
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+%% Contents for page 2
+17 0 obj
+<<
+ /Length 18 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 1) Tj ET
+endstream
+endobj
+
+18 0 obj
+47
+endobj
+
+%% Contents for page 3
+19 0 obj
+<<
+ /Length 20 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 2) Tj ET
+endstream
+endobj
+
+20 0 obj
+47
+endobj
+
+%% Contents for page 4
+21 0 obj
+<<
+ /Length 22 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 3) Tj ET
+endstream
+endobj
+
+22 0 obj
+47
+endobj
+
+%% Contents for page 5
+23 0 obj
+<<
+ /Length 24 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 4) Tj ET
+endstream
+endobj
+
+24 0 obj
+47
+endobj
+
+%% Contents for page 6
+25 0 obj
+<<
+ /Length 26 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 5) Tj ET
+endstream
+endobj
+
+26 0 obj
+47
+endobj
+
+%% Contents for page 7
+27 0 obj
+<<
+ /Length 28 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 6) Tj ET
+endstream
+endobj
+
+28 0 obj
+47
+endobj
+
+%% Contents for page 8
+29 0 obj
+<<
+ /Length 30 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 7) Tj ET
+endstream
+endobj
+
+30 0 obj
+47
+endobj
+
+%% Contents for page 9
+31 0 obj
+<<
+ /Length 32 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 8) Tj ET
+endstream
+endobj
+
+32 0 obj
+47
+endobj
+
+%% Contents for page 10
+33 0 obj
+<<
+ /Length 34 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 9) Tj ET
+endstream
+endobj
+
+34 0 obj
+47
+endobj
+
+xref
+0 35
+0000000000 65535 f
+0000000025 00000 n
+0000000079 00000 n
+0000000165 00000 n
+0000000342 00000 n
+0000000571 00000 n
+0000000800 00000 n
+0000001029 00000 n
+0000001258 00000 n
+0000001487 00000 n
+0000001716 00000 n
+0000001946 00000 n
+0000002176 00000 n
+0000002407 00000 n
+0000002650 00000 n
+0000002754 00000 n
+0000002774 00000 n
+0000002906 00000 n
+0000003010 00000 n
+0000003053 00000 n
+0000003157 00000 n
+0000003200 00000 n
+0000003304 00000 n
+0000003347 00000 n
+0000003451 00000 n
+0000003494 00000 n
+0000003598 00000 n
+0000003641 00000 n
+0000003745 00000 n
+0000003788 00000 n
+0000003892 00000 n
+0000003935 00000 n
+0000004039 00000 n
+0000004083 00000 n
+0000004187 00000 n
+trailer <<
+ /Info 2 0 R
+ /Root 1 0 R
+ /Size 35
+ /ID [<21f7a6fb083dab8e29743918a08bfa31><21f7a6fb083dab8e29743918a08bfa31>]
+>>
+startxref
+4207
+%%EOF
diff --git a/qpdf/qtest/qpdf/page_api_2.out b/qpdf/qtest/qpdf/page_api_2.out
new file mode 100644
index 00000000..9fc019c2
--- /dev/null
+++ b/qpdf/qtest/qpdf/page_api_2.out
@@ -0,0 +1 @@
+page_api_2.pdf (page 1 (numbered from zero): object 4 0): duplicate page reference found; this would cause loss of data
diff --git a/qpdf/qtest/qpdf/page_api_2.pdf b/qpdf/qtest/qpdf/page_api_2.pdf
new file mode 100644
index 00000000..f3d6eaac
--- /dev/null
+++ b/qpdf/qtest/qpdf/page_api_2.pdf
@@ -0,0 +1,137 @@
+%PDF-1.3
+%¿÷¢þ
+%QDF-1.0
+
+1 0 obj
+<<
+ /Pages 3 0 R
+ /Type /Catalog
+>>
+endobj
+
+2 0 obj
+<<
+ /CreationDate (D:20120621124041)
+ /Producer (Apex PDFWriter)
+>>
+endobj
+
+3 0 obj
+<<
+ /Count 3
+ /Kids [
+ 4 0 R
+ 4 0 R
+ 5 0 R
+ ]
+ /Type /Pages
+>>
+endobj
+
+%% Page 2
+4 0 obj
+<<
+ /Contents 6 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 8 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 3
+5 0 obj
+<<
+ /Contents 9 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 3 0 R
+ /Resources <<
+ /Font <<
+ /F1 8 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Contents for page 2
+6 0 obj
+<<
+ /Length 7 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 0) Tj ET
+endstream
+endobj
+
+7 0 obj
+47
+endobj
+
+8 0 obj
+<<
+ /BaseFont /Times-Roman
+ /Encoding /WinAnsiEncoding
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+%% Contents for page 3
+9 0 obj
+<<
+ /Length 10 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 2) Tj ET
+endstream
+endobj
+
+10 0 obj
+47
+endobj
+
+xref
+0 11
+0000000000 65535 f
+0000000025 00000 n
+0000000079 00000 n
+0000000165 00000 n
+0000000267 00000 n
+0000000494 00000 n
+0000000734 00000 n
+0000000836 00000 n
+0000000855 00000 n
+0000000986 00000 n
+0000001089 00000 n
+trailer <<
+ /Info 2 0 R
+ /Root 1 0 R
+ /Size 11
+ /ID [<fb18b786ff7b358705da8a532aba8f6f><f7179eb35159bfd4c00f128abcfd1f02>]
+>>
+startxref
+1109
+%%EOF
diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc
index 2dc41b41..24e2f1ec 100644
--- a/qpdf/test_driver.cc
+++ b/qpdf/test_driver.cc
@@ -14,6 +14,7 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
+#include <assert.h>
#include <map>
static char const* whoami = 0;
@@ -56,8 +57,34 @@ class Provider: public QPDFObjectHandle::StreamDataProvider
bool bad_length;
};
+static void checkPageContents(QPDFObjectHandle page,
+ std::string const& wanted_string)
+{
+ PointerHolder<Buffer> b1 =
+ page.getKey("/Contents").getStreamData();
+ std::string contents = std::string((char *)(b1->getBuffer()));
+ if (contents.find(wanted_string) == std::string::npos)
+ {
+ std::cout << "didn't find " << wanted_string << " in "
+ << contents << std::endl;
+ }
+}
+
+static QPDFObjectHandle createPageContents(QPDF& pdf, std::string const& text)
+{
+ std::string contents = "BT /F1 15 Tf 72 720 Td (" + text + ") Tj ET\n";
+ PointerHolder<Buffer> b = new Buffer(contents.length());
+ unsigned char* bp = b->getBuffer();
+ memcpy(bp, (char*)contents.c_str(), contents.length());
+ return QPDFObjectHandle::newStream(&pdf, b);
+}
+
void runtest(int n, char const* filename)
{
+ // Most tests here are crafted to work on specific files. Look at
+ // the test suite to see how the test is invoked to find the file
+ // that the test is supposed to operate on.
+
QPDF pdf;
PointerHolder<char> file_buf;
FILE* filep = 0;
@@ -629,6 +656,129 @@ void runtest(int n, char const* filename)
fclose(f);
delete b;
}
+ else if (n == 15)
+ {
+ std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
+ // Reference to original page numbers for this test case are
+ // numbered from 0.
+
+ // Remove pages from various places, checking to make sure
+ // that our pages reference is getting updated.
+ assert(pages.size() == 10);
+ pdf.removePage(pages.back()); // original page 9
+ assert(pages.size() == 9);
+ pdf.removePage(*pages.begin()); // original page 0
+ assert(pages.size() == 8);
+ checkPageContents(pages[4], "Original page 5");
+ pdf.removePage(pages[4]); // original page 5
+ assert(pages.size() == 7);
+ checkPageContents(pages[4], "Original page 6");
+ checkPageContents(pages[0], "Original page 1");
+ checkPageContents(pages[6], "Original page 8");
+
+ // Insert pages
+
+ // Create some content streams.
+ std::vector<QPDFObjectHandle> contents;
+ contents.push_back(createPageContents(pdf, "New page 1"));
+ contents.push_back(createPageContents(pdf, "New page 0"));
+ contents.push_back(createPageContents(pdf, "New page 5"));
+ contents.push_back(createPageContents(pdf, "New page 6"));
+ contents.push_back(createPageContents(pdf, "New page 11"));
+ contents.push_back(createPageContents(pdf, "New page 12"));
+
+ // Create some page objects. Start with an existing
+ // dictionary and modify it. Using the results of
+ // getDictAsMap to create a new dictionary effectively creates
+ // a shallow copy.
+ std::map<std::string, QPDFObjectHandle> page_dict_keys =
+ QPDFObjectHandle(pages[0]).getDictAsMap();
+ std::vector<QPDFObjectHandle> new_pages;
+ for (std::vector<QPDFObjectHandle>::iterator iter = contents.begin();
+ iter != contents.end(); ++iter)
+ {
+ // We will retain indirect object references to other
+ // indirect objects other than page content.
+ page_dict_keys["/Contents"] = *iter;
+ QPDFObjectHandle page =
+ QPDFObjectHandle::newDictionary(page_dict_keys);
+ if (iter == contents.begin())
+ {
+ // leave direct
+ new_pages.push_back(page);
+ }
+ else
+ {
+ new_pages.push_back(pdf.makeIndirectObject(page));
+ }
+ }
+
+ // Now insert the pages
+ pdf.addPage(new_pages[0], true);
+ checkPageContents(pages[0], "New page 1");
+ pdf.addPageAt(new_pages[1], true, pages[0]);
+ assert(pages[0].getObjectID() == new_pages[1].getObjectID());
+ pdf.addPageAt(new_pages[2], true, pages[5]);
+ assert(pages[5].getObjectID() == new_pages[2].getObjectID());
+ pdf.addPageAt(new_pages[3], false, pages[5]);
+ assert(pages[6].getObjectID() == new_pages[3].getObjectID());
+ assert(pages.size() == 11);
+ pdf.addPage(new_pages[4], false);
+ assert(pages[11].getObjectID() == new_pages[4].getObjectID());
+ pdf.addPageAt(new_pages[5], false, pages.back());
+ assert(pages.size() == 13);
+ checkPageContents(pages[0], "New page 0");
+ checkPageContents(pages[1], "New page 1");
+ checkPageContents(pages[5], "New page 5");
+ checkPageContents(pages[6], "New page 6");
+ checkPageContents(pages[11], "New page 11");
+ checkPageContents(pages[12], "New page 12");
+
+ QPDFWriter w(pdf, "a.pdf");
+ w.setStaticID(true);
+ w.setStreamDataMode(qpdf_s_preserve);
+ w.write();
+ }
+ else if (n == 16)
+ {
+ // Insert a page manually and then update the cache.
+ std::vector<QPDFObjectHandle> const& all_pages = pdf.getAllPages();
+
+ QPDFObjectHandle contents = createPageContents(pdf, "New page 10");
+ std::map<std::string, QPDFObjectHandle> page_dict_keys =
+ QPDFObjectHandle(all_pages[0]).getDictAsMap();
+ page_dict_keys["/Contents"] = contents;
+ QPDFObjectHandle page =
+ pdf.makeIndirectObject(
+ QPDFObjectHandle::newDictionary(page_dict_keys));
+
+ // Insert the page manually.
+ QPDFObjectHandle root = pdf.getRoot();
+ QPDFObjectHandle pages = root.getKey("/Pages");
+ QPDFObjectHandle kids = pages.getKey("/Kids");
+ page.replaceKey("/Parent", pages);
+ pages.replaceKey(
+ "/Count",
+ QPDFObjectHandle::newInteger(1 + (int)all_pages.size()));
+ kids.appendItem(page);
+ assert(all_pages.size() == 10);
+ pdf.updateAllPagesCache();
+ assert(all_pages.size() == 11);
+ assert(all_pages.back().getObjectID() == page.getObjectID());
+
+ QPDFWriter w(pdf, "a.pdf");
+ w.setStaticID(true);
+ w.setStreamDataMode(qpdf_s_preserve);
+ w.write();
+ }
+ else if (n == 17)
+ {
+ // The input file to this test case is broken to exercise an
+ // error condition.
+ std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
+ pdf.removePage(pages[0]);
+ std::cout << "you can't see this" << std::endl;
+ }
else
{
throw std::runtime_error(std::string("invalid test ") +