aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2021-12-10 20:57:11 +0100
committerJay Berkenbilt <ejb@ql.org>2021-12-10 20:57:35 +0100
commit1c62c2a3427e92846ddaaae44f864022b2aade4f (patch)
treefd4d89ad177dcdc170f764637cafcdda5f849bde
parent8e0b15333228ab973571eec498af41d3cbb8ab63 (diff)
downloadqpdf-1c62c2a3427e92846ddaaae44f864022b2aade4f.tar.zst
C API: expose functions for indirect objects (fixes #588)
-rw-r--r--ChangeLog4
-rw-r--r--include/qpdf/qpdf-c.h9
-rw-r--r--libqpdf/qpdf-c.cc25
-rw-r--r--manual/qpdf-manual.xml9
-rw-r--r--qpdf/qpdf-ctest.c52
-rw-r--r--qpdf/qpdf.testcov2
-rw-r--r--qpdf/qtest/qpdf.test14
-rw-r--r--qpdf/qtest/qpdf/c-indirect-objects-out.pdf99
8 files changed, 213 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 9efbdc15..f100817f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
2021-12-10 Jay Berkenbilt <ejb@ql.org>
+ * C API: add qpdf_get_object_by_id, qpdf_make_indirect_object, and
+ qpdf_replace_object, exposing the corresponding methods in QPDF
+ and QPDFObjectHandle. Fixes #588.
+
* Add missing QPDF_DLL to QPDFObjectHandle::addTokenFilter so that
it is actually accessible as part of the public interface as
intended. Fixes #580.
diff --git a/include/qpdf/qpdf-c.h b/include/qpdf/qpdf-c.h
index 90219123..fc393152 100644
--- a/include/qpdf/qpdf-c.h
+++ b/include/qpdf/qpdf-c.h
@@ -618,6 +618,15 @@ extern "C" {
QPDF_DLL
qpdf_oh qpdf_get_root(qpdf_data data);
+ /* Retrieve and replace indirect objects */
+ QPDF_DLL
+ qpdf_oh qpdf_get_object_by_id(qpdf_data qpdf, int objid, int generation);
+ QPDF_DLL
+ qpdf_oh qpdf_make_indirect_object(qpdf_data qpdf, qpdf_oh oh);
+ QPDF_DLL
+ void qpdf_replace_object(
+ qpdf_data qpdf, int objid, int generation, qpdf_oh oh);
+
/* Wrappers around QPDFObjectHandle methods. Be sure to read
* corresponding comments in QPDFObjectHandle.hh to understand
* what each function does and what kinds of objects it applies
diff --git a/libqpdf/qpdf-c.cc b/libqpdf/qpdf-c.cc
index b7e67e85..1183ffd2 100644
--- a/libqpdf/qpdf-c.cc
+++ b/libqpdf/qpdf-c.cc
@@ -972,6 +972,12 @@ qpdf_oh qpdf_get_root(qpdf_data qpdf)
});
}
+qpdf_oh qpdf_get_object_by_id(qpdf_data qpdf, int objid, int generation)
+{
+ QTC::TC("qpdf", "qpdf-c called qpdf_get_object_by_id");
+ return new_object(qpdf, qpdf->qpdf->getObjectByID(objid, generation));
+}
+
template<class RET>
static RET do_with_oh(
qpdf_data qpdf, qpdf_oh oh,
@@ -1008,6 +1014,15 @@ static void do_with_oh_void(
});
}
+void qpdf_replace_object(qpdf_data qpdf, int objid, int generation, qpdf_oh oh)
+{
+ do_with_oh_void(
+ qpdf, oh, [&qpdf, &objid, &generation](QPDFObjectHandle& o) {
+ QTC::TC("qpdf", "qpdf-c called qpdf_replace_object");
+ qpdf->qpdf->replaceObject(objid, generation, o);
+ });
+}
+
QPDF_BOOL qpdf_oh_is_initialized(qpdf_data qpdf, qpdf_oh oh)
{
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)
});
}
+qpdf_oh qpdf_make_indirect_object(qpdf_data qpdf, qpdf_oh oh)
+{
+ return do_with_oh<qpdf_oh>(
+ qpdf, oh,
+ return_uninitialized(qpdf),
+ [&qpdf](QPDFObjectHandle& o) {
+ return new_object(qpdf, qpdf->qpdf->makeIndirectObject(o));
+ });
+}
+
static QPDFObjectHandle
qpdf_oh_item_internal(qpdf_data qpdf, qpdf_oh item)
{
diff --git a/manual/qpdf-manual.xml b/manual/qpdf-manual.xml
index f7d224e5..42b1d25c 100644
--- a/manual/qpdf-manual.xml
+++ b/manual/qpdf-manual.xml
@@ -5263,6 +5263,15 @@ print "\n";
C API. This allows you to clone an object handle.
</para>
</listitem>
+ <listitem>
+ <para>
+ Add <function>qpdf_get_object_by_id</function>,
+ <function>qpdf_make_indirect_object</function>, and
+ <function>qpdf_replace_object</function>, exposing the
+ corresponding methods in <classname>QPDF</classname> and
+ <classname>QPDFObjectHandle</classname>.
+ </para>
+ </listitem>
</itemizedlist>
</listitem>
</itemizedlist>
diff --git a/qpdf/qpdf-ctest.c b/qpdf/qpdf-ctest.c
index 6ee19004..d789aa61 100644
--- a/qpdf/qpdf-ctest.c
+++ b/qpdf/qpdf-ctest.c
@@ -881,6 +881,56 @@ static void test31(char const* infile,
report_errors();
}
+static void test32(char const* infile,
+ char const* password,
+ char const* outfile,
+ char const* outfile2)
+{
+ /* This test case is designed for minimal.pdf. */
+ assert(qpdf_read(qpdf, infile, password) == 0);
+ qpdf_oh page = qpdf_get_object_by_id(qpdf, 3, 0);
+ assert(qpdf_oh_is_dictionary(qpdf, page));
+ assert(qpdf_oh_has_key(qpdf, page, "/MediaBox"));
+ report_errors();
+}
+
+static void test33(char const* infile,
+ char const* password,
+ char const* outfile,
+ char const* outfile2)
+{
+ /* This test case is designed for minimal.pdf. */
+
+ /* Convert a direct object to an indirect object and replace it. */
+ assert(qpdf_read(qpdf, infile, password) == 0);
+ qpdf_oh root = qpdf_get_root(qpdf);
+ qpdf_oh pages = qpdf_oh_get_key(qpdf, root, "/Pages");
+ qpdf_oh kids = qpdf_oh_get_key(qpdf, pages, "/Kids");
+ qpdf_oh page1 = qpdf_oh_get_array_item(qpdf, kids, 0);
+ qpdf_oh mediabox = qpdf_oh_get_key(qpdf, page1, "/MediaBox");
+ assert(! qpdf_oh_is_indirect(qpdf, mediabox));
+ qpdf_oh i_mediabox = qpdf_make_indirect_object(qpdf, mediabox);
+ assert(qpdf_oh_is_indirect(qpdf, i_mediabox));
+ qpdf_oh_replace_key(qpdf, page1, "/MediaBox", i_mediabox);
+
+ /* Replace a different indirect object */
+ qpdf_oh resources = qpdf_oh_get_key(qpdf, page1, "/Resources");
+ qpdf_oh procset = qpdf_oh_get_key(qpdf, resources, "/ProcSet");
+ assert(qpdf_oh_is_indirect(qpdf, procset));
+ qpdf_replace_object(
+ qpdf,
+ qpdf_oh_get_object_id(qpdf, procset),
+ qpdf_oh_get_generation(qpdf, procset),
+ qpdf_oh_parse(qpdf, "[/PDF]"));
+
+ 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;
@@ -952,6 +1002,8 @@ int main(int argc, char* argv[])
(n == 29) ? test29 :
(n == 30) ? test30 :
(n == 31) ? test31 :
+ (n == 32) ? test32 :
+ (n == 33) ? test33 :
0);
if (fn == 0)
diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov
index 67311b01..9b4e3cea 100644
--- a/qpdf/qpdf.testcov
+++ b/qpdf/qpdf.testcov
@@ -606,3 +606,5 @@ qpdf-c called qpdf_oh_new_uninitialized 0
qpdf-c warn about oh error 1
qpdf-c registered oh error handler 0
qpdf-c cleanup warned about unhandled error 0
+qpdf-c called qpdf_get_object_by_id 0
+qpdf-c called qpdf_replace_object 0
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index 384d9dca..c7881e02 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -4812,7 +4812,7 @@ foreach my $i (@c_check_types)
show_ntests();
# ----------
$td->notify("--- C API Object Handle ---");
-$n_tests += 10;
+$n_tests += 13;
$td->runtest("C check object handles",
{$td->COMMAND => "qpdf-ctest 24 minimal.pdf '' a.pdf"},
@@ -4831,6 +4831,14 @@ $td->runtest("check output",
{$td->FILE => 'a.pdf'},
{$td->FILE => 'c-object-handle-creation-out.pdf'});
+$td->runtest("C indirect objects",
+ {$td->COMMAND => "qpdf-ctest 33 minimal.pdf '' a.pdf"},
+ {$td->STRING => "", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("check output",
+ {$td->FILE => 'a.pdf'},
+ {$td->FILE => 'c-indirect-objects-out.pdf'});
+
$td->runtest("C uninitialized objects",
{$td->COMMAND => "qpdf-ctest 26 '' '' ''"},
{$td->STRING => "", $td->EXIT_STATUS => 0},
@@ -4855,6 +4863,10 @@ $td->runtest("C type mismatch warning",
{$td->COMMAND => "qpdf-ctest 31 minimal.pdf '' ''"},
{$td->FILE => "c-type-warning.out", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
+$td->runtest("C get object by ID",
+ {$td->COMMAND => "qpdf-ctest 32 minimal.pdf '' ''"},
+ {$td->STRING => "", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
show_ntests();
# ----------
diff --git a/qpdf/qtest/qpdf/c-indirect-objects-out.pdf b/qpdf/qtest/qpdf/c-indirect-objects-out.pdf
new file mode 100644
index 00000000..fe6dc178
--- /dev/null
+++ b/qpdf/qtest/qpdf/c-indirect-objects-out.pdf
@@ -0,0 +1,99 @@
+%PDF-1.3
+%¿÷¢þ
+%QDF-1.0
+
+1 0 obj
+<<
+ /Pages 2 0 R
+ /Type /Catalog
+>>
+endobj
+
+2 0 obj
+<<
+ /Count 1
+ /Kids [
+ 3 0 R
+ ]
+ /Type /Pages
+>>
+endobj
+
+%% Page 1
+3 0 obj
+<<
+ /Contents 4 0 R
+ /MediaBox 6 0 R
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 7 0 R
+ >>
+ /ProcSet 8 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Contents for page 1
+4 0 obj
+<<
+ /Length 5 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato) Tj
+ET
+endstream
+endobj
+
+5 0 obj
+44
+endobj
+
+6 0 obj
+[
+ 0
+ 0
+ 612
+ 792
+]
+endobj
+
+7 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+8 0 obj
+[
+ /PDF
+]
+endobj
+
+xref
+0 9
+0000000000 65535 f
+0000000025 00000 n
+0000000079 00000 n
+0000000161 00000 n
+0000000348 00000 n
+0000000447 00000 n
+0000000466 00000 n
+0000000506 00000 n
+0000000624 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 9
+ /ID [<31415926535897932384626433832795><31415926535897932384626433832795>]
+>>
+startxref
+651
+%%EOF