Commit 1c62c2a3427e92846ddaaae44f864022b2aade4f

Authored by Jay Berkenbilt
1 parent 8e0b1533

C API: expose functions for indirect objects (fixes #588)

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 &quot;C&quot; {
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 &quot;\n&quot;;
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
... ... @@ -606,3 +606,5 @@ qpdf-c called qpdf_oh_new_uninitialized 0
606 606 qpdf-c warn about oh error 1
607 607 qpdf-c registered oh error handler 0
608 608 qpdf-c cleanup warned about unhandled error 0
  609 +qpdf-c called qpdf_get_object_by_id 0
  610 +qpdf-c called qpdf_replace_object 0
... ...
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-&gt;runtest(&quot;check output&quot;,
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-&gt;runtest(&quot;C type mismatch warning&quot;,
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 + /PDF
  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
... ...