Commit 14fe2e6de3ae3b91436ccb4948fca75c29565440

Authored by Jay Berkenbilt
1 parent ce358f10

qpdf_set_info_key, qpdf_get_info_key

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
... ...
... ... @@ -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 &quot;C&quot; {
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 &quot;\n&quot;;
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-&gt;runtest(&quot;new stream&quot;,
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-&gt;runtest(&quot;check output&quot;,
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
  1 +Info key /Author: (null)
  2 +Info key /Producer: (null)
  3 +Info key /Creator: (null)
  4 +Info key /Author: Mr. Potato Head
  5 +Info key /Producer: QPDF libary
  6 +Info key /Creator: (null)
... ...
qpdf/qtest/qpdf/c-info2-in.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/c-info2.out 0 → 100644
  1 +Info key /Author: Someone Else
  2 +Info key /Producer: Something Else
  3 +Info key /Creator: A. Nony Mous
  4 +Info key /Author: Mr. Potato Head
  5 +Info key /Producer: QPDF libary
  6 +Info key /Creator: (null)
... ...