Commit 1c62c2a3427e92846ddaaae44f864022b2aade4f
1 parent
8e0b1533
C API: expose functions for indirect objects (fixes #588)
Showing
8 changed files
with
213 additions
and
1 deletions
ChangeLog
| 1 | 1 | 2021-12-10 Jay Berkenbilt <ejb@ql.org> |
| 2 | 2 | |
| 3 | + * C API: add qpdf_get_object_by_id, qpdf_make_indirect_object, and | |
| 4 | + qpdf_replace_object, exposing the corresponding methods in QPDF | |
| 5 | + and QPDFObjectHandle. Fixes #588. | |
| 6 | + | |
| 3 | 7 | * Add missing QPDF_DLL to QPDFObjectHandle::addTokenFilter so that |
| 4 | 8 | it is actually accessible as part of the public interface as |
| 5 | 9 | intended. Fixes #580. | ... | ... |
include/qpdf/qpdf-c.h
| ... | ... | @@ -618,6 +618,15 @@ extern "C" { |
| 618 | 618 | QPDF_DLL |
| 619 | 619 | qpdf_oh qpdf_get_root(qpdf_data data); |
| 620 | 620 | |
| 621 | + /* Retrieve and replace indirect objects */ | |
| 622 | + QPDF_DLL | |
| 623 | + qpdf_oh qpdf_get_object_by_id(qpdf_data qpdf, int objid, int generation); | |
| 624 | + QPDF_DLL | |
| 625 | + qpdf_oh qpdf_make_indirect_object(qpdf_data qpdf, qpdf_oh oh); | |
| 626 | + QPDF_DLL | |
| 627 | + void qpdf_replace_object( | |
| 628 | + qpdf_data qpdf, int objid, int generation, qpdf_oh oh); | |
| 629 | + | |
| 621 | 630 | /* Wrappers around QPDFObjectHandle methods. Be sure to read |
| 622 | 631 | * corresponding comments in QPDFObjectHandle.hh to understand |
| 623 | 632 | * what each function does and what kinds of objects it applies | ... | ... |
libqpdf/qpdf-c.cc
| ... | ... | @@ -972,6 +972,12 @@ qpdf_oh qpdf_get_root(qpdf_data qpdf) |
| 972 | 972 | }); |
| 973 | 973 | } |
| 974 | 974 | |
| 975 | +qpdf_oh qpdf_get_object_by_id(qpdf_data qpdf, int objid, int generation) | |
| 976 | +{ | |
| 977 | + QTC::TC("qpdf", "qpdf-c called qpdf_get_object_by_id"); | |
| 978 | + return new_object(qpdf, qpdf->qpdf->getObjectByID(objid, generation)); | |
| 979 | +} | |
| 980 | + | |
| 975 | 981 | template<class RET> |
| 976 | 982 | static RET do_with_oh( |
| 977 | 983 | qpdf_data qpdf, qpdf_oh oh, |
| ... | ... | @@ -1008,6 +1014,15 @@ static void do_with_oh_void( |
| 1008 | 1014 | }); |
| 1009 | 1015 | } |
| 1010 | 1016 | |
| 1017 | +void qpdf_replace_object(qpdf_data qpdf, int objid, int generation, qpdf_oh oh) | |
| 1018 | +{ | |
| 1019 | + do_with_oh_void( | |
| 1020 | + qpdf, oh, [&qpdf, &objid, &generation](QPDFObjectHandle& o) { | |
| 1021 | + QTC::TC("qpdf", "qpdf-c called qpdf_replace_object"); | |
| 1022 | + qpdf->qpdf->replaceObject(objid, generation, o); | |
| 1023 | + }); | |
| 1024 | +} | |
| 1025 | + | |
| 1011 | 1026 | QPDF_BOOL qpdf_oh_is_initialized(qpdf_data qpdf, qpdf_oh oh) |
| 1012 | 1027 | { |
| 1013 | 1028 | QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_initialized"); |
| ... | ... | @@ -1421,6 +1436,16 @@ void qpdf_oh_make_direct(qpdf_data qpdf, qpdf_oh oh) |
| 1421 | 1436 | }); |
| 1422 | 1437 | } |
| 1423 | 1438 | |
| 1439 | +qpdf_oh qpdf_make_indirect_object(qpdf_data qpdf, qpdf_oh oh) | |
| 1440 | +{ | |
| 1441 | + return do_with_oh<qpdf_oh>( | |
| 1442 | + qpdf, oh, | |
| 1443 | + return_uninitialized(qpdf), | |
| 1444 | + [&qpdf](QPDFObjectHandle& o) { | |
| 1445 | + return new_object(qpdf, qpdf->qpdf->makeIndirectObject(o)); | |
| 1446 | + }); | |
| 1447 | +} | |
| 1448 | + | |
| 1424 | 1449 | static QPDFObjectHandle |
| 1425 | 1450 | qpdf_oh_item_internal(qpdf_data qpdf, qpdf_oh item) |
| 1426 | 1451 | { | ... | ... |
manual/qpdf-manual.xml
| ... | ... | @@ -5263,6 +5263,15 @@ print "\n"; |
| 5263 | 5263 | C API. This allows you to clone an object handle. |
| 5264 | 5264 | </para> |
| 5265 | 5265 | </listitem> |
| 5266 | + <listitem> | |
| 5267 | + <para> | |
| 5268 | + Add <function>qpdf_get_object_by_id</function>, | |
| 5269 | + <function>qpdf_make_indirect_object</function>, and | |
| 5270 | + <function>qpdf_replace_object</function>, exposing the | |
| 5271 | + corresponding methods in <classname>QPDF</classname> and | |
| 5272 | + <classname>QPDFObjectHandle</classname>. | |
| 5273 | + </para> | |
| 5274 | + </listitem> | |
| 5266 | 5275 | </itemizedlist> |
| 5267 | 5276 | </listitem> |
| 5268 | 5277 | </itemizedlist> | ... | ... |
qpdf/qpdf-ctest.c
| ... | ... | @@ -881,6 +881,56 @@ static void test31(char const* infile, |
| 881 | 881 | report_errors(); |
| 882 | 882 | } |
| 883 | 883 | |
| 884 | +static void test32(char const* infile, | |
| 885 | + char const* password, | |
| 886 | + char const* outfile, | |
| 887 | + char const* outfile2) | |
| 888 | +{ | |
| 889 | + /* This test case is designed for minimal.pdf. */ | |
| 890 | + assert(qpdf_read(qpdf, infile, password) == 0); | |
| 891 | + qpdf_oh page = qpdf_get_object_by_id(qpdf, 3, 0); | |
| 892 | + assert(qpdf_oh_is_dictionary(qpdf, page)); | |
| 893 | + assert(qpdf_oh_has_key(qpdf, page, "/MediaBox")); | |
| 894 | + report_errors(); | |
| 895 | +} | |
| 896 | + | |
| 897 | +static void test33(char const* infile, | |
| 898 | + char const* password, | |
| 899 | + char const* outfile, | |
| 900 | + char const* outfile2) | |
| 901 | +{ | |
| 902 | + /* This test case is designed for minimal.pdf. */ | |
| 903 | + | |
| 904 | + /* Convert a direct object to an indirect object and replace it. */ | |
| 905 | + assert(qpdf_read(qpdf, infile, password) == 0); | |
| 906 | + qpdf_oh root = qpdf_get_root(qpdf); | |
| 907 | + qpdf_oh pages = qpdf_oh_get_key(qpdf, root, "/Pages"); | |
| 908 | + qpdf_oh kids = qpdf_oh_get_key(qpdf, pages, "/Kids"); | |
| 909 | + qpdf_oh page1 = qpdf_oh_get_array_item(qpdf, kids, 0); | |
| 910 | + qpdf_oh mediabox = qpdf_oh_get_key(qpdf, page1, "/MediaBox"); | |
| 911 | + assert(! qpdf_oh_is_indirect(qpdf, mediabox)); | |
| 912 | + qpdf_oh i_mediabox = qpdf_make_indirect_object(qpdf, mediabox); | |
| 913 | + assert(qpdf_oh_is_indirect(qpdf, i_mediabox)); | |
| 914 | + qpdf_oh_replace_key(qpdf, page1, "/MediaBox", i_mediabox); | |
| 915 | + | |
| 916 | + /* Replace a different indirect object */ | |
| 917 | + qpdf_oh resources = qpdf_oh_get_key(qpdf, page1, "/Resources"); | |
| 918 | + qpdf_oh procset = qpdf_oh_get_key(qpdf, resources, "/ProcSet"); | |
| 919 | + assert(qpdf_oh_is_indirect(qpdf, procset)); | |
| 920 | + qpdf_replace_object( | |
| 921 | + qpdf, | |
| 922 | + qpdf_oh_get_object_id(qpdf, procset), | |
| 923 | + qpdf_oh_get_generation(qpdf, procset), | |
| 924 | + qpdf_oh_parse(qpdf, "[/PDF]")); | |
| 925 | + | |
| 926 | + qpdf_init_write(qpdf, outfile); | |
| 927 | + qpdf_set_static_ID(qpdf, QPDF_TRUE); | |
| 928 | + qpdf_set_qdf_mode(qpdf, QPDF_TRUE); | |
| 929 | + qpdf_set_suppress_original_object_IDs(qpdf, QPDF_TRUE); | |
| 930 | + qpdf_write(qpdf); | |
| 931 | + report_errors(); | |
| 932 | +} | |
| 933 | + | |
| 884 | 934 | int main(int argc, char* argv[]) |
| 885 | 935 | { |
| 886 | 936 | char* p = 0; |
| ... | ... | @@ -952,6 +1002,8 @@ int main(int argc, char* argv[]) |
| 952 | 1002 | (n == 29) ? test29 : |
| 953 | 1003 | (n == 30) ? test30 : |
| 954 | 1004 | (n == 31) ? test31 : |
| 1005 | + (n == 32) ? test32 : | |
| 1006 | + (n == 33) ? test33 : | |
| 955 | 1007 | 0); |
| 956 | 1008 | |
| 957 | 1009 | if (fn == 0) | ... | ... |
qpdf/qpdf.testcov
qpdf/qtest/qpdf.test
| ... | ... | @@ -4812,7 +4812,7 @@ foreach my $i (@c_check_types) |
| 4812 | 4812 | show_ntests(); |
| 4813 | 4813 | # ---------- |
| 4814 | 4814 | $td->notify("--- C API Object Handle ---"); |
| 4815 | -$n_tests += 10; | |
| 4815 | +$n_tests += 13; | |
| 4816 | 4816 | |
| 4817 | 4817 | $td->runtest("C check object handles", |
| 4818 | 4818 | {$td->COMMAND => "qpdf-ctest 24 minimal.pdf '' a.pdf"}, |
| ... | ... | @@ -4831,6 +4831,14 @@ $td->runtest("check output", |
| 4831 | 4831 | {$td->FILE => 'a.pdf'}, |
| 4832 | 4832 | {$td->FILE => 'c-object-handle-creation-out.pdf'}); |
| 4833 | 4833 | |
| 4834 | +$td->runtest("C indirect objects", | |
| 4835 | + {$td->COMMAND => "qpdf-ctest 33 minimal.pdf '' a.pdf"}, | |
| 4836 | + {$td->STRING => "", $td->EXIT_STATUS => 0}, | |
| 4837 | + $td->NORMALIZE_NEWLINES); | |
| 4838 | +$td->runtest("check output", | |
| 4839 | + {$td->FILE => 'a.pdf'}, | |
| 4840 | + {$td->FILE => 'c-indirect-objects-out.pdf'}); | |
| 4841 | + | |
| 4834 | 4842 | $td->runtest("C uninitialized objects", |
| 4835 | 4843 | {$td->COMMAND => "qpdf-ctest 26 '' '' ''"}, |
| 4836 | 4844 | {$td->STRING => "", $td->EXIT_STATUS => 0}, |
| ... | ... | @@ -4855,6 +4863,10 @@ $td->runtest("C type mismatch warning", |
| 4855 | 4863 | {$td->COMMAND => "qpdf-ctest 31 minimal.pdf '' ''"}, |
| 4856 | 4864 | {$td->FILE => "c-type-warning.out", $td->EXIT_STATUS => 0}, |
| 4857 | 4865 | $td->NORMALIZE_NEWLINES); |
| 4866 | +$td->runtest("C get object by ID", | |
| 4867 | + {$td->COMMAND => "qpdf-ctest 32 minimal.pdf '' ''"}, | |
| 4868 | + {$td->STRING => "", $td->EXIT_STATUS => 0}, | |
| 4869 | + $td->NORMALIZE_NEWLINES); | |
| 4858 | 4870 | |
| 4859 | 4871 | show_ntests(); |
| 4860 | 4872 | # ---------- | ... | ... |
qpdf/qtest/qpdf/c-indirect-objects-out.pdf
0 → 100644
| 1 | +%PDF-1.3 | |
| 2 | +%¿÷¢þ | |
| 3 | +%QDF-1.0 | |
| 4 | + | |
| 5 | +1 0 obj | |
| 6 | +<< | |
| 7 | + /Pages 2 0 R | |
| 8 | + /Type /Catalog | |
| 9 | +>> | |
| 10 | +endobj | |
| 11 | + | |
| 12 | +2 0 obj | |
| 13 | +<< | |
| 14 | + /Count 1 | |
| 15 | + /Kids [ | |
| 16 | + 3 0 R | |
| 17 | + ] | |
| 18 | + /Type /Pages | |
| 19 | +>> | |
| 20 | +endobj | |
| 21 | + | |
| 22 | +%% Page 1 | |
| 23 | +3 0 obj | |
| 24 | +<< | |
| 25 | + /Contents 4 0 R | |
| 26 | + /MediaBox 6 0 R | |
| 27 | + /Parent 2 0 R | |
| 28 | + /Resources << | |
| 29 | + /Font << | |
| 30 | + /F1 7 0 R | |
| 31 | + >> | |
| 32 | + /ProcSet 8 0 R | |
| 33 | + >> | |
| 34 | + /Type /Page | |
| 35 | +>> | |
| 36 | +endobj | |
| 37 | + | |
| 38 | +%% Contents for page 1 | |
| 39 | +4 0 obj | |
| 40 | +<< | |
| 41 | + /Length 5 0 R | |
| 42 | +>> | |
| 43 | +stream | |
| 44 | +BT | |
| 45 | + /F1 24 Tf | |
| 46 | + 72 720 Td | |
| 47 | + (Potato) Tj | |
| 48 | +ET | |
| 49 | +endstream | |
| 50 | +endobj | |
| 51 | + | |
| 52 | +5 0 obj | |
| 53 | +44 | |
| 54 | +endobj | |
| 55 | + | |
| 56 | +6 0 obj | |
| 57 | +[ | |
| 58 | + 0 | |
| 59 | + 0 | |
| 60 | + 612 | |
| 61 | + 792 | |
| 62 | +] | |
| 63 | +endobj | |
| 64 | + | |
| 65 | +7 0 obj | |
| 66 | +<< | |
| 67 | + /BaseFont /Helvetica | |
| 68 | + /Encoding /WinAnsiEncoding | |
| 69 | + /Name /F1 | |
| 70 | + /Subtype /Type1 | |
| 71 | + /Type /Font | |
| 72 | +>> | |
| 73 | +endobj | |
| 74 | + | |
| 75 | +8 0 obj | |
| 76 | +[ | |
| 77 | ||
| 78 | +] | |
| 79 | +endobj | |
| 80 | + | |
| 81 | +xref | |
| 82 | +0 9 | |
| 83 | +0000000000 65535 f | |
| 84 | +0000000025 00000 n | |
| 85 | +0000000079 00000 n | |
| 86 | +0000000161 00000 n | |
| 87 | +0000000348 00000 n | |
| 88 | +0000000447 00000 n | |
| 89 | +0000000466 00000 n | |
| 90 | +0000000506 00000 n | |
| 91 | +0000000624 00000 n | |
| 92 | +trailer << | |
| 93 | + /Root 1 0 R | |
| 94 | + /Size 9 | |
| 95 | + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] | |
| 96 | +>> | |
| 97 | +startxref | |
| 98 | +651 | |
| 99 | +%%EOF | ... | ... |