aboutsummaryrefslogtreecommitdiffstats
path: root/qpdf
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2021-12-17 19:28:11 +0100
committerJay Berkenbilt <ejb@ql.org>2021-12-17 19:28:11 +0100
commitfeafcc4e88798626c80840797822efbf55722716 (patch)
tree1eabc4f818084d56a25a9571d87823f8004c7b9f /qpdf
parenta0dbb71a64e6adb26f8f02881e8d0ea8476ee875 (diff)
downloadqpdf-feafcc4e88798626c80840797822efbf55722716.tar.zst
C API: add several stream functions (fixes #596)
Diffstat (limited to 'qpdf')
-rw-r--r--qpdf/qpdf-ctest.c115
-rw-r--r--qpdf/qpdf.testcov6
-rw-r--r--qpdf/qtest/qpdf.test28
-rw-r--r--qpdf/qtest/qpdf/c-foreign.pdf573
-rw-r--r--qpdf/qtest/qpdf/c-get-stream.out13
-rw-r--r--qpdf/qtest/qpdf/c-new-stream.pdfbin0 -> 1116 bytes
6 files changed, 735 insertions, 0 deletions
diff --git a/qpdf/qpdf-ctest.c b/qpdf/qpdf-ctest.c
index 90ebdcd4..cf03f0e0 100644
--- a/qpdf/qpdf-ctest.c
+++ b/qpdf/qpdf-ctest.c
@@ -1063,6 +1063,118 @@ static void test37(char const* infile,
assert(qpdf_get_num_pages(qpdf) == 10);
}
+static void test38(char const* infile,
+ char const* password,
+ char const* outfile,
+ char const* xarg)
+{
+ /* This test expects 11-pages.pdf. */
+
+ /* Read stream data */
+
+ assert(qpdf_read(qpdf, infile, password) == 0);
+ qpdf_oh stream = qpdf_get_object_by_id(qpdf, 17, 0);
+ qpdf_oh dict = qpdf_oh_get_dict(qpdf, stream);
+ assert(qpdf_oh_get_int_value_as_int(
+ qpdf, qpdf_oh_get_key(qpdf, dict, "/Length")) == 53);
+ /* Get raw data */
+ unsigned char *buf = 0;
+ size_t len = 0;
+ assert(qpdf_oh_get_stream_data(
+ qpdf, stream, qpdf_dl_none, 0, &buf, &len) == 0);
+ assert(len == 53);
+ assert(((int)buf[0] == 'x') && ((int)buf[1] == 0234));
+ free(buf);
+
+ /* Test whether filterable */
+ QPDF_BOOL filtered = QPDF_FALSE;
+ assert(qpdf_oh_get_stream_data(
+ qpdf, stream, qpdf_dl_all, &filtered, 0, 0) == 0);
+ assert(filtered == QPDF_TRUE);
+
+ /* Get filtered data */
+ assert(qpdf_oh_get_stream_data(
+ qpdf, stream, qpdf_dl_all, 0, &buf, &len) == 0);
+ assert(len == 47);
+ assert(memcmp((char const*)buf,
+ "BT /F1 15 Tf 72 720 Td (Original page 2) Tj ET\n",
+ len) == 0);
+
+ /* Get page data */
+ qpdf_oh page2 = qpdf_get_page_n(qpdf, 1); /* 0-based index */
+ unsigned char* buf2 = 0;
+ assert(qpdf_oh_get_page_content_data(qpdf, page2, &buf2, &len) == 0);
+ assert(len == 47);
+ assert(memcmp(buf, buf2, len) == 0);
+ free(buf);
+ free(buf2);
+
+ /* errors */
+ printf("page content on broken page\n");
+ qpdf_oh_replace_key(qpdf, page2, "/Contents", qpdf_oh_new_integer(qpdf, 3));
+ buf = 0;
+ qpdf_oh_get_page_content_data(qpdf, page2, &buf, &len);
+ assert(buf == 0);
+ report_errors();
+ printf("stream data for non stream\n");
+ qpdf_oh root = qpdf_get_root(qpdf);
+ assert(qpdf_oh_get_stream_data(qpdf, root, qpdf_dl_all, 0, 0, 0) != 0);
+ report_errors();
+}
+
+static void test39(char const* infile,
+ char const* password,
+ char const* outfile,
+ char const* xarg)
+{
+ /* This test expects 11-pages.pdf as file1 and minimal.pdf as xarg. */
+
+ /* Foreign object */
+
+ qpdf_data qpdf2 = qpdf_init();
+ assert(qpdf_read(qpdf, infile, password) == 0);
+ assert(qpdf_read(qpdf2, xarg, "") == 0);
+
+ qpdf_oh resources = qpdf_get_object_by_id(qpdf2, 3, 0);
+ qpdf_oh copy = qpdf_oh_copy_foreign_object(qpdf, qpdf2, resources);
+ qpdf_oh root = qpdf_get_root(qpdf);
+ qpdf_oh_replace_key(qpdf, root, "/Copy", copy);
+
+ qpdf_init_write(qpdf, outfile);
+ qpdf_set_static_ID(qpdf, QPDF_TRUE);
+ qpdf_set_qdf_mode(qpdf, QPDF_TRUE);
+ qpdf_set_suppress_original_object_IDs(qpdf, QPDF_TRUE);
+ qpdf_write(qpdf);
+ report_errors();
+ qpdf_cleanup(&qpdf2);
+}
+
+static void test40(char const* infile,
+ char const* password,
+ char const* outfile,
+ char const* xarg)
+{
+ /* This test expects minimal.pdf. */
+
+ /* New stream */
+
+ assert(qpdf_read(qpdf, infile, password) == 0);
+ qpdf_oh stream = qpdf_oh_new_stream(qpdf);
+ qpdf_oh_replace_stream_data(
+ qpdf, stream,
+ (unsigned char*)"12345\000abcde", 11, /* embedded null */
+ qpdf_oh_new_null(qpdf), qpdf_oh_new_null(qpdf));
+ qpdf_oh root = qpdf_get_root(qpdf);
+ qpdf_oh_replace_key(qpdf, root, "/Potato", stream);
+
+ qpdf_init_write(qpdf, outfile);
+ qpdf_set_static_ID(qpdf, QPDF_TRUE);
+ qpdf_set_qdf_mode(qpdf, QPDF_TRUE);
+ qpdf_set_suppress_original_object_IDs(qpdf, QPDF_TRUE);
+ qpdf_write(qpdf);
+ report_errors();
+}
+
int main(int argc, char* argv[])
{
char* p = 0;
@@ -1140,6 +1252,9 @@ int main(int argc, char* argv[])
(n == 35) ? test35 :
(n == 36) ? test36 :
(n == 37) ? test37 :
+ (n == 38) ? test38 :
+ (n == 39) ? test39 :
+ (n == 40) ? test40 :
0);
if (fn == 0)
diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov
index 315c1200..ebe0d2b3 100644
--- a/qpdf/qpdf.testcov
+++ b/qpdf/qpdf.testcov
@@ -617,3 +617,9 @@ qpdf-c called qpdf_push_inherited_attributes_to_page 0
qpdf-c called qpdf_add_page 0
qpdf-c called qpdf_add_page_at 0
qpdf-c called qpdf_remove_page 0
+qpdf-c called qpdf_oh_new_stream 0
+qpdf-c called qpdf_oh_copy_foreign_object 0
+qpdf-c stream data filtered set 1
+qpdf-c stream data buf set 1
+qpdf-c called qpdf_oh_get_page_content_data 0
+qpdf-c called qpdf_oh_replace_stream_data 0
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index 3825cfaa..a5d69a4b 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -4904,6 +4904,34 @@ $td->runtest("C pages cache",
show_ntests();
# ----------
+$td->notify("--- C API Stream Functions ---");
+$n_tests += 5;
+
+$td->runtest("C read streams",
+ {$td->COMMAND =>
+ "qpdf-ctest 38 11-pages.pdf '' ''"},
+ {$td->FILE => "c-get-stream.out", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
+$td->runtest("C foreign object",
+ {$td->COMMAND =>
+ "qpdf-ctest 39 11-pages.pdf '' a.pdf minimal.pdf"},
+ {$td->STRING => "C test 39 done\n", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("check output",
+ {$td->FILE => 'a.pdf'},
+ {$td->FILE => 'c-foreign.pdf'});
+
+$td->runtest("C new stream",
+ {$td->COMMAND =>
+ "qpdf-ctest 40 minimal.pdf '' a.pdf"},
+ {$td->STRING => "C test 40 done\n", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("check output",
+ {$td->FILE => 'a.pdf'},
+ {$td->FILE => 'c-new-stream.pdf'});
+show_ntests();
+# ----------
$td->notify("--- Content Preservation Tests ---");
# $n_tests incremented below
diff --git a/qpdf/qtest/qpdf/c-foreign.pdf b/qpdf/qtest/qpdf/c-foreign.pdf
new file mode 100644
index 00000000..662d9238
--- /dev/null
+++ b/qpdf/qtest/qpdf/c-foreign.pdf
@@ -0,0 +1,573 @@
+%PDF-1.4
+%¿÷¢þ
+%QDF-1.0
+
+1 0 obj
+<<
+ /Copy 3 0 R
+ /Pages 4 0 R
+ /Type /Catalog
+>>
+endobj
+
+2 0 obj
+<<
+ /CreationDate (D:20120721200217)
+ /Producer (Apex PDFWriter)
+>>
+endobj
+
+3 0 obj
+<<
+ /Contents 5 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Resources <<
+ /Font <<
+ /F1 7 0 R
+ >>
+ /ProcSet 8 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+4 0 obj
+<<
+ /Count 11
+ /Kids [
+ 9 0 R
+ 10 0 R
+ 11 0 R
+ 12 0 R
+ 13 0 R
+ 14 0 R
+ 15 0 R
+ 16 0 R
+ 17 0 R
+ 18 0 R
+ 19 0 R
+ ]
+ /Type /Pages
+>>
+endobj
+
+5 0 obj
+<<
+ /Length 6 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato) Tj
+ET
+endstream
+endobj
+
+6 0 obj
+44
+endobj
+
+7 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+8 0 obj
+[
+ /PDF
+ /Text
+]
+endobj
+
+%% Page 1
+9 0 obj
+<<
+ /Contents 20 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 4 0 R
+ /Resources <<
+ /Font <<
+ /F1 22 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 2
+10 0 obj
+<<
+ /Contents 23 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 4 0 R
+ /Resources <<
+ /Font <<
+ /F1 22 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 3
+11 0 obj
+<<
+ /Contents 25 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 4 0 R
+ /Resources <<
+ /Font <<
+ /F1 22 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 4
+12 0 obj
+<<
+ /Contents 27 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 4 0 R
+ /Resources <<
+ /Font <<
+ /F1 22 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 5
+13 0 obj
+<<
+ /Contents 29 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 4 0 R
+ /Resources <<
+ /Font <<
+ /F1 22 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 6
+14 0 obj
+<<
+ /Contents 31 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 4 0 R
+ /Resources <<
+ /Font <<
+ /F1 22 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 7
+15 0 obj
+<<
+ /Contents 33 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 4 0 R
+ /Resources <<
+ /Font <<
+ /F1 22 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 8
+16 0 obj
+<<
+ /Contents 35 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 4 0 R
+ /Resources <<
+ /Font <<
+ /F1 22 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 9
+17 0 obj
+<<
+ /Contents 37 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 4 0 R
+ /Resources <<
+ /Font <<
+ /F1 22 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 10
+18 0 obj
+<<
+ /Contents 39 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 4 0 R
+ /Resources <<
+ /Font <<
+ /F1 22 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Page 11
+19 0 obj
+<<
+ /Contents 41 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 4 0 R
+ /Resources <<
+ /Font <<
+ /F1 22 0 R
+ >>
+ /ProcSet [
+ /PDF
+ /Text
+ ]
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Contents for page 1
+20 0 obj
+<<
+ /Length 21 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 1) Tj ET
+endstream
+endobj
+
+21 0 obj
+47
+endobj
+
+22 0 obj
+<<
+ /BaseFont /Times-Roman
+ /Encoding /WinAnsiEncoding
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+%% Contents for page 2
+23 0 obj
+<<
+ /Length 24 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 2) Tj ET
+endstream
+endobj
+
+24 0 obj
+47
+endobj
+
+%% Contents for page 3
+25 0 obj
+<<
+ /Length 26 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 3) Tj ET
+endstream
+endobj
+
+26 0 obj
+47
+endobj
+
+%% Contents for page 4
+27 0 obj
+<<
+ /Length 28 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 4) Tj ET
+endstream
+endobj
+
+28 0 obj
+47
+endobj
+
+%% Contents for page 5
+29 0 obj
+<<
+ /Length 30 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 5) Tj ET
+endstream
+endobj
+
+30 0 obj
+47
+endobj
+
+%% Contents for page 6
+31 0 obj
+<<
+ /Length 32 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 6) Tj ET
+endstream
+endobj
+
+32 0 obj
+47
+endobj
+
+%% Contents for page 7
+33 0 obj
+<<
+ /Length 34 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 7) Tj ET
+endstream
+endobj
+
+34 0 obj
+47
+endobj
+
+%% Contents for page 8
+35 0 obj
+<<
+ /Length 36 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 8) Tj ET
+endstream
+endobj
+
+36 0 obj
+47
+endobj
+
+%% Contents for page 9
+37 0 obj
+<<
+ /Length 38 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 9) Tj ET
+endstream
+endobj
+
+38 0 obj
+47
+endobj
+
+%% Contents for page 10
+39 0 obj
+<<
+ /Length 40 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 10) Tj ET
+endstream
+endobj
+
+40 0 obj
+48
+endobj
+
+%% Contents for page 11
+41 0 obj
+<<
+ /Length 42 0 R
+>>
+stream
+BT /F1 15 Tf 72 720 Td (Original page 11) Tj ET
+endstream
+endobj
+
+42 0 obj
+48
+endobj
+
+xref
+0 43
+0000000000 65535 f
+0000000025 00000 n
+0000000093 00000 n
+0000000179 00000 n
+0000000355 00000 n
+0000000538 00000 n
+0000000637 00000 n
+0000000656 00000 n
+0000000774 00000 n
+0000000819 00000 n
+0000001048 00000 n
+0000001278 00000 n
+0000001508 00000 n
+0000001738 00000 n
+0000001968 00000 n
+0000002198 00000 n
+0000002428 00000 n
+0000002658 00000 n
+0000002889 00000 n
+0000003120 00000 n
+0000003363 00000 n
+0000003467 00000 n
+0000003487 00000 n
+0000003619 00000 n
+0000003723 00000 n
+0000003766 00000 n
+0000003870 00000 n
+0000003913 00000 n
+0000004017 00000 n
+0000004060 00000 n
+0000004164 00000 n
+0000004207 00000 n
+0000004311 00000 n
+0000004354 00000 n
+0000004458 00000 n
+0000004501 00000 n
+0000004605 00000 n
+0000004648 00000 n
+0000004752 00000 n
+0000004796 00000 n
+0000004901 00000 n
+0000004945 00000 n
+0000005050 00000 n
+trailer <<
+ /Info 2 0 R
+ /Root 1 0 R
+ /Size 43
+ /ID [<e032a88c7a987db6ca3abee555506ccc><31415926535897932384626433832795>]
+>>
+startxref
+5070
+%%EOF
diff --git a/qpdf/qtest/qpdf/c-get-stream.out b/qpdf/qtest/qpdf/c-get-stream.out
new file mode 100644
index 00000000..e78bd4eb
--- /dev/null
+++ b/qpdf/qtest/qpdf/c-get-stream.out
@@ -0,0 +1,13 @@
+page content on broken page
+error: page object 5 0: object is supposed to be a stream or an array of streams but is neither
+ code: 5
+ file:
+ pos : 0
+ text: object is supposed to be a stream or an array of streams but is neither
+stream data for non stream
+error: operation for stream attempted on object of type dictionary
+ code: 2
+ file:
+ pos : 0
+ text: operation for stream attempted on object of type dictionary
+C test 38 done
diff --git a/qpdf/qtest/qpdf/c-new-stream.pdf b/qpdf/qtest/qpdf/c-new-stream.pdf
new file mode 100644
index 00000000..95ff378f
--- /dev/null
+++ b/qpdf/qtest/qpdf/c-new-stream.pdf
Binary files differ