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

This commit is contained in:
Jay Berkenbilt 2021-12-10 14:57:11 -05:00
parent 8e0b153332
commit 1c62c2a342
8 changed files with 213 additions and 1 deletions

View File

@ -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.

View File

@ -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

View File

@ -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)
{

View File

@ -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>

View File

@ -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)

View File

@ -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

View File

@ -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();
# ----------

View File

@ -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