Commit 14fe2e6de3ae3b91436ccb4948fca75c29565440
1 parent
ce358f10
qpdf_set_info_key, qpdf_get_info_key
Showing
12 changed files
with
216 additions
and
7 deletions
ChangeLog
| 1 | +2011-08-11 Jay Berkenbilt <ejb@ql.org> | |
| 2 | + | |
| 3 | + * include/qpdf/qpdf-c.h ("C"): add new methods qpdf_get_info_key | |
| 4 | + and qpdf_set_info_key for manipulating text fields of the /Info | |
| 5 | + dictionary. | |
| 6 | + | |
| 1 | 7 | 2011-08-10 Jay Berkenbilt <ejb@ql.org> |
| 2 | 8 | |
| 3 | 9 | * libqpdf/QPDFWriter.cc (copyEncryptionParameters): preserve | ... | ... |
TODO
| ... | ... | @@ -3,10 +3,6 @@ |
| 3 | 3 | |
| 4 | 4 | * Provide an example of using replace and swap. Maybe. |
| 5 | 5 | |
| 6 | - * Figure out a way to update the C API with something that can | |
| 7 | - update dictionary keys at least with strings. Make sure it is | |
| 8 | - possible to implement something akin to pdf-mod-info in C. | |
| 9 | - | |
| 10 | 6 | * Add C API for writing to memory if possible |
| 11 | 7 | |
| 12 | 8 | General | ... | ... |
include/qpdf/qpdf-c.h
| ... | ... | @@ -199,18 +199,46 @@ extern "C" { |
| 199 | 199 | /* Read functions below must be called after qpdf_read or |
| 200 | 200 | * qpdf_read_memory. */ |
| 201 | 201 | |
| 202 | - /* Return the version of the PDF file. */ | |
| 202 | + /* | |
| 203 | + * NOTE: Functions that return char* are returning a pointer to an | |
| 204 | + * internal buffer that will be reused for each call to a function | |
| 205 | + * that returns a char*. You must use or copy the value before | |
| 206 | + * calling any other qpdf library functions. | |
| 207 | + */ | |
| 208 | + | |
| 209 | + /* Return the version of the PDF file. See warning above about | |
| 210 | + * functions that return char*. */ | |
| 203 | 211 | QPDF_DLL |
| 204 | 212 | char const* qpdf_get_pdf_version(qpdf_data qpdf); |
| 205 | 213 | |
| 206 | 214 | /* Return the user password. If the file is opened using the |
| 207 | 215 | * owner password, the user password may be retrieved using this |
| 208 | 216 | * function. If the file is opened using the user password, this |
| 209 | - * function will return that user password. | |
| 217 | + * function will return that user password. See warning above | |
| 218 | + * about functions that return char*. | |
| 210 | 219 | */ |
| 211 | 220 | QPDF_DLL |
| 212 | 221 | char const* qpdf_get_user_password(qpdf_data qpdf); |
| 213 | 222 | |
| 223 | + /* Return the string value of a key in the document's Info | |
| 224 | + * dictionary. The key parameter should include the leading | |
| 225 | + * slash, e.g. "/Author". If the key is not present or has a | |
| 226 | + * non-string value, a null pointer is returned. Otherwise, a | |
| 227 | + * pointer to an internal buffer is returned. See warning above | |
| 228 | + * about functions that return char*. | |
| 229 | + */ | |
| 230 | + QPDF_DLL | |
| 231 | + char const* qpdf_get_info_key(qpdf_data qpdf, char const* key); | |
| 232 | + | |
| 233 | + /* Set a value in the info dictionary, possibly replacing an | |
| 234 | + * existing value. The key must include the leading slash | |
| 235 | + * (e.g. "/Author"). Passing a null pointer as a value will | |
| 236 | + * remove the key from the info dictionary. Otherwise, a copy | |
| 237 | + * will be made of the string that is passed in. | |
| 238 | + */ | |
| 239 | + QPDF_DLL | |
| 240 | + void qpdf_set_info_key(qpdf_data qpdf, char const* key, char const* value); | |
| 241 | + | |
| 214 | 242 | /* Indicate whether the input file is linearized. */ |
| 215 | 243 | QPDF_DLL |
| 216 | 244 | QPDF_BOOL qpdf_is_linearized(qpdf_data qpdf); | ... | ... |
libqpdf/qpdf-c.cc
| ... | ... | @@ -8,6 +8,7 @@ |
| 8 | 8 | #include <list> |
| 9 | 9 | #include <string> |
| 10 | 10 | #include <stdexcept> |
| 11 | +#include <cstring> | |
| 11 | 12 | |
| 12 | 13 | struct _qpdf_error |
| 13 | 14 | { |
| ... | ... | @@ -283,6 +284,64 @@ char const* qpdf_get_user_password(qpdf_data qpdf) |
| 283 | 284 | return qpdf->tmp_string.c_str(); |
| 284 | 285 | } |
| 285 | 286 | |
| 287 | +char const* qpdf_get_info_key(qpdf_data qpdf, char const* key) | |
| 288 | +{ | |
| 289 | + char const* result = 0; | |
| 290 | + QPDFObjectHandle trailer = qpdf->qpdf->getTrailer(); | |
| 291 | + if (trailer.hasKey("/Info")) | |
| 292 | + { | |
| 293 | + QPDFObjectHandle info = trailer.getKey("/Info"); | |
| 294 | + if (info.hasKey(key)) | |
| 295 | + { | |
| 296 | + QPDFObjectHandle value = info.getKey(key); | |
| 297 | + if (value.isString()) | |
| 298 | + { | |
| 299 | + qpdf->tmp_string = value.getStringValue(); | |
| 300 | + result = qpdf->tmp_string.c_str(); | |
| 301 | + } | |
| 302 | + } | |
| 303 | + } | |
| 304 | + QTC::TC("qpdf", "qpdf-c get_info_key", (result == 0 ? 0 : 1)); | |
| 305 | + return result; | |
| 306 | +} | |
| 307 | + | |
| 308 | +void qpdf_set_info_key(qpdf_data qpdf, char const* key, char const* value) | |
| 309 | +{ | |
| 310 | + if ((key == 0) || (std::strlen(key) == 0) || (key[0] != '/')) | |
| 311 | + { | |
| 312 | + return; | |
| 313 | + } | |
| 314 | + QPDFObjectHandle value_object; | |
| 315 | + if (value) | |
| 316 | + { | |
| 317 | + QTC::TC("qpdf", "qpdf-c set_info_key to value"); | |
| 318 | + value_object = QPDFObjectHandle::newString(value); | |
| 319 | + } | |
| 320 | + else | |
| 321 | + { | |
| 322 | + QTC::TC("qpdf", "qpdf-c set_info_key to null"); | |
| 323 | + value_object = QPDFObjectHandle::newNull(); | |
| 324 | + } | |
| 325 | + | |
| 326 | + QPDFObjectHandle trailer = qpdf->qpdf->getTrailer(); | |
| 327 | + if (! trailer.hasKey("/Info")) | |
| 328 | + { | |
| 329 | + QTC::TC("qpdf", "qpdf-c add info to trailer"); | |
| 330 | + trailer.replaceKey( | |
| 331 | + "/Info", | |
| 332 | + qpdf->qpdf->makeIndirectObject( | |
| 333 | + QPDFObjectHandle::newDictionary( | |
| 334 | + std::map<std::string, QPDFObjectHandle>()))); | |
| 335 | + } | |
| 336 | + else | |
| 337 | + { | |
| 338 | + QTC::TC("qpdf", "qpdf-c set-info-key use existing info"); | |
| 339 | + } | |
| 340 | + | |
| 341 | + QPDFObjectHandle info = trailer.getKey("/Info"); | |
| 342 | + info.replaceOrRemoveKey(key, value_object); | |
| 343 | +} | |
| 344 | + | |
| 286 | 345 | QPDF_BOOL qpdf_is_linearized(qpdf_data qpdf) |
| 287 | 346 | { |
| 288 | 347 | QTC::TC("qpdf", "qpdf-c called qpdf_is_linearized"); | ... | ... |
manual/qpdf-manual.xml
| ... | ... | @@ -2123,6 +2123,14 @@ print "\n"; |
| 2123 | 2123 | objects as vectors. |
| 2124 | 2124 | </para> |
| 2125 | 2125 | </listitem> |
| 2126 | + <listitem> | |
| 2127 | + <para> | |
| 2128 | + Add functions <function>qpdf_get_info_key</function> and | |
| 2129 | + <function>qpdf_set_info_key</function> to the C API for | |
| 2130 | + manipulating string fields of the document's | |
| 2131 | + <literal>/Info</literal> dictionary. | |
| 2132 | + </para> | |
| 2133 | + </listitem> | |
| 2126 | 2134 | </itemizedlist> |
| 2127 | 2135 | </listitem> |
| 2128 | 2136 | </varlistentry> | ... | ... |
qpdf/qpdf-ctest.c
| ... | ... | @@ -325,6 +325,36 @@ static void test15(char const* infile, |
| 325 | 325 | report_errors(); |
| 326 | 326 | } |
| 327 | 327 | |
| 328 | +static void print_info(char const* key) | |
| 329 | +{ | |
| 330 | + char const* value = qpdf_get_info_key(qpdf, key); | |
| 331 | + printf("Info key %s: %s\n", | |
| 332 | + key, (value ? value : "(null)")); | |
| 333 | +} | |
| 334 | + | |
| 335 | +static void test16(char const* infile, | |
| 336 | + char const* password, | |
| 337 | + char const* outfile, | |
| 338 | + char const* outfile2) | |
| 339 | +{ | |
| 340 | + qpdf_read(qpdf, infile, password); | |
| 341 | + print_info("/Author"); | |
| 342 | + print_info("/Producer"); | |
| 343 | + print_info("/Creator"); | |
| 344 | + qpdf_set_info_key(qpdf, "/Author", "Mr. Potato Head"); | |
| 345 | + qpdf_set_info_key(qpdf, "/Producer", "QPDF libary"); | |
| 346 | + qpdf_set_info_key(qpdf, "/Creator", 0); | |
| 347 | + print_info("/Author"); | |
| 348 | + print_info("/Producer"); | |
| 349 | + print_info("/Creator"); | |
| 350 | + qpdf_init_write(qpdf, outfile); | |
| 351 | + qpdf_set_static_ID(qpdf, QPDF_TRUE); | |
| 352 | + qpdf_set_static_aes_IV(qpdf, QPDF_TRUE); | |
| 353 | + qpdf_set_stream_data_mode(qpdf, qpdf_s_uncompress); | |
| 354 | + qpdf_write(qpdf); | |
| 355 | + report_errors(); | |
| 356 | +} | |
| 357 | + | |
| 328 | 358 | int main(int argc, char* argv[]) |
| 329 | 359 | { |
| 330 | 360 | char* p = 0; |
| ... | ... | @@ -380,6 +410,7 @@ int main(int argc, char* argv[]) |
| 380 | 410 | (n == 13) ? test13 : |
| 381 | 411 | (n == 14) ? test14 : |
| 382 | 412 | (n == 15) ? test15 : |
| 413 | + (n == 16) ? test16 : | |
| 383 | 414 | 0); |
| 384 | 415 | |
| 385 | 416 | if (fn == 0) | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -194,3 +194,8 @@ QPDF stream with CRNL 0 |
| 194 | 194 | QPDF stream with NL only 0 |
| 195 | 195 | QPDF replaceObject called with indirect object 0 |
| 196 | 196 | QPDFWriter copy encrypt metadata 1 |
| 197 | +qpdf-c get_info_key 1 | |
| 198 | +qpdf-c set_info_key to value 0 | |
| 199 | +qpdf-c set_info_key to null 0 | |
| 200 | +qpdf-c set-info-key use existing info 0 | |
| 201 | +qpdf-c add info to trailer 0 | ... | ... |
qpdf/qtest/qpdf.test
| ... | ... | @@ -111,7 +111,7 @@ $td->runtest("new stream", |
| 111 | 111 | show_ntests(); |
| 112 | 112 | # ---------- |
| 113 | 113 | $td->notify("--- Miscellaneous Tests ---"); |
| 114 | -$n_tests += 33; | |
| 114 | +$n_tests += 37; | |
| 115 | 115 | |
| 116 | 116 | $td->runtest("qpdf version", |
| 117 | 117 | {$td->COMMAND => "qpdf --version"}, |
| ... | ... | @@ -285,6 +285,26 @@ $td->runtest("check output", |
| 285 | 285 | {$td->FILE => "a.pdf"}, |
| 286 | 286 | {$td->FILE => "test14-out.pdf"}); |
| 287 | 287 | |
| 288 | +$td->runtest("C API info key functions", | |
| 289 | + {$td->COMMAND => "qpdf-ctest 16 minimal.pdf '' a.pdf"}, | |
| 290 | + {$td->FILE => "c-info1.out", | |
| 291 | + $td->EXIT_STATUS => 0}, | |
| 292 | + $td->NORMALIZE_NEWLINES); | |
| 293 | +$td->runtest("check output", | |
| 294 | + {$td->FILE => "a.pdf"}, | |
| 295 | + {$td->FILE => "c-info-out.pdf"}); | |
| 296 | +unlink "a.pdf" or die; | |
| 297 | + | |
| 298 | +$td->runtest("C API info key functions", | |
| 299 | + {$td->COMMAND => "qpdf-ctest 16 c-info2-in.pdf '' a.pdf"}, | |
| 300 | + {$td->FILE => "c-info2.out", | |
| 301 | + $td->EXIT_STATUS => 0}, | |
| 302 | + $td->NORMALIZE_NEWLINES); | |
| 303 | +$td->runtest("check output", | |
| 304 | + {$td->FILE => "a.pdf"}, | |
| 305 | + {$td->FILE => "c-info-out.pdf"}); | |
| 306 | +unlink "a.pdf" or die; | |
| 307 | + | |
| 288 | 308 | show_ntests(); |
| 289 | 309 | # ---------- |
| 290 | 310 | $td->notify("--- Error Condition Tests ---"); | ... | ... |
qpdf/qtest/qpdf/c-info-out.pdf
0 → 100644
| 1 | +%PDF-1.3 | |
| 2 | +%¿÷¢þ | |
| 3 | +1 0 obj | |
| 4 | +<< /Pages 3 0 R /Type /Catalog >> | |
| 5 | +endobj | |
| 6 | +2 0 obj | |
| 7 | +<< /Author (Mr. Potato Head) /Producer (QPDF libary) >> | |
| 8 | +endobj | |
| 9 | +3 0 obj | |
| 10 | +<< /Count 1 /Kids [ 4 0 R ] /Type /Pages >> | |
| 11 | +endobj | |
| 12 | +4 0 obj | |
| 13 | +<< /Contents 5 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << /Font << /F1 6 0 R >> /ProcSet 7 0 R >> /Type /Page >> | |
| 14 | +endobj | |
| 15 | +5 0 obj | |
| 16 | +<< /Length 44 >> | |
| 17 | +stream | |
| 18 | +BT | |
| 19 | + /F1 24 Tf | |
| 20 | + 72 720 Td | |
| 21 | + (Potato) Tj | |
| 22 | +ET | |
| 23 | +endstream | |
| 24 | +endobj | |
| 25 | +6 0 obj | |
| 26 | +<< /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >> | |
| 27 | +endobj | |
| 28 | +7 0 obj | |
| 29 | +[ /PDF /Text ] | |
| 30 | +endobj | |
| 31 | +xref | |
| 32 | +0 8 | |
| 33 | +0000000000 65535 f | |
| 34 | +0000000015 00000 n | |
| 35 | +0000000064 00000 n | |
| 36 | +0000000135 00000 n | |
| 37 | +0000000194 00000 n | |
| 38 | +0000000337 00000 n | |
| 39 | +0000000430 00000 n | |
| 40 | +0000000537 00000 n | |
| 41 | +trailer << /Info 2 0 R /Root 1 0 R /Size 8 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] >> | |
| 42 | +startxref | |
| 43 | +567 | |
| 44 | +%%EOF | ... | ... |
qpdf/qtest/qpdf/c-info1.out
0 → 100644
qpdf/qtest/qpdf/c-info2-in.pdf
0 → 100644
No preview for this file type