1 #include <qpdf/qpdf-c.h>
2 #include <stdio.h>
3 #include <assert.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <errno.h>
7 #include "../libqpdf/qpdf/qpdf-config.h" // for LL_FMT
8 
9 static char* whoami = 0;
10 static qpdf_data qpdf = 0;
11 
safe_fopen(char const * filename,char const * mode)12 static FILE* safe_fopen(char const* filename, char const* mode)
13 {
14     // This function is basically a "C" port of QUtil::safe_fopen.
15     FILE* f = 0;
16 #ifdef _MSC_VER
17     errno_t err = fopen_s(&f, filename, mode);
18     if (err != 0)
19     {
20         char buf[94];
21         strerror_s(buf, sizeof(buf), errno);
22 	fprintf(stderr, "%s: unable to open %s: %s\n",
23 		whoami, filename, buf);
24 	exit(2);
25     }
26 #else
27     f = fopen(filename, mode);
28     if (f == NULL)
29     {
30 	fprintf(stderr, "%s: unable to open %s: %s\n",
31 		whoami, filename, strerror(errno));
32 	exit(2);
33     }
34 #endif
35     return f;
36 }
37 
print_error(char const * label,qpdf_data q,qpdf_error e)38 static void print_error(char const* label, qpdf_data q, qpdf_error e)
39 {
40 #define POS_FMT "  pos : " LL_FMT "\n"
41     printf("%s: %s\n", label, qpdf_get_error_full_text(q, e));
42     printf("  code: %d\n", qpdf_get_error_code(q, e));
43     printf("  file: %s\n", qpdf_get_error_filename(q, e));
44     printf(POS_FMT, qpdf_get_error_file_position(q, e));
45     printf("  text: %s\n", qpdf_get_error_message_detail(q, e));
46 }
47 
report_errors()48 static void report_errors()
49 {
50     qpdf_error e = 0;
51     while (qpdf_more_warnings(qpdf))
52     {
53 	e = qpdf_next_warning(qpdf);
54         print_error("warning", qpdf, e);
55     }
56     if (qpdf_has_error(qpdf))
57     {
58 	e = qpdf_get_error(qpdf);
59 	assert(qpdf_has_error(qpdf) == QPDF_FALSE);
60         print_error("error", qpdf, e);
61     }
62     else
63     {
64 	e = qpdf_get_error(qpdf);
65 	assert(e == 0);
66 	assert(qpdf_get_error_code(qpdf, e) == qpdf_e_success);
67 	// Call these to ensure that they can be called on a null
68 	// error pointer.
69 	(void)qpdf_get_error_full_text(qpdf, e);
70 	(void)qpdf_get_error_filename(qpdf, e);
71 	(void)qpdf_get_error_file_position(qpdf, e);
72 	(void)qpdf_get_error_message_detail(qpdf, e);
73     }
74 }
75 
handle_oh_error(qpdf_data q,char const * label)76 static void handle_oh_error(qpdf_data q, char const* label)
77 {
78     if (qpdf_has_error(q))
79     {
80         print_error(label, q, qpdf_get_error(q));
81     }
82 }
83 
read_file_into_memory(char const * filename,char ** buf,unsigned long * size)84 static void read_file_into_memory(char const* filename,
85 				  char** buf, unsigned long* size)
86 {
87     char* buf_p = 0;
88     FILE* f = NULL;
89     size_t bytes_read = 0;
90     size_t len = 0;
91 
92     f = safe_fopen(filename, "rb");
93     fseek(f, 0, SEEK_END);
94     *size = (unsigned long) ftell(f);
95     fseek(f, 0, SEEK_SET);
96     *buf = malloc(*size);
97     if (*buf == NULL)
98     {
99 	fprintf(stderr, "%s: unable to allocate %lu bytes\n",
100 		whoami, *size);
101 	exit(2);
102     }
103     buf_p = *buf;
104     bytes_read = 0;
105     len = 0;
106     while ((len = fread(buf_p + bytes_read, 1, *size - bytes_read, f)) > 0)
107     {
108 	bytes_read += len;
109     }
110     if (bytes_read != *size)
111     {
112 	if (ferror(f))
113 	{
114 	    fprintf(stderr, "%s: failure reading file %s into memory:",
115 		    whoami, filename);
116 	}
117 	else
118 	{
119 	    fprintf(stderr, "%s: premature EOF reading file %s:",
120 		    whoami, filename);
121 	}
122 	fprintf(stderr, " read %lu, wanted %lu\n",
123 		(unsigned long) bytes_read, (unsigned long) *size);
124 	exit(2);
125     }
126     fclose(f);
127 }
128 
count_progress(int percent,void * data)129 static void count_progress(int percent, void* data)
130 {
131     ++(*(int*)data);
132 }
133 
test01(char const * infile,char const * password,char const * outfile,char const * xarg)134 static void test01(char const* infile,
135 		   char const* password,
136 		   char const* outfile,
137 		   char const* xarg)
138 {
139     qpdf_read(qpdf, infile, password);
140     printf("version: %s\n", qpdf_get_pdf_version(qpdf));
141     if (qpdf_get_pdf_extension_level(qpdf) > 0)
142     {
143         printf("extension level: %d\n", qpdf_get_pdf_extension_level(qpdf));
144     }
145     printf("linearized: %d\n", qpdf_is_linearized(qpdf));
146     printf("encrypted: %d\n", qpdf_is_encrypted(qpdf));
147     if (qpdf_is_encrypted(qpdf))
148     {
149 	printf("user password: %s\n", qpdf_get_user_password(qpdf));
150 	printf("extract for accessibility: %d\n",
151 	       qpdf_allow_accessibility(qpdf));
152 	printf("extract for any purpose: %d\n",
153 	       qpdf_allow_extract_all(qpdf));
154 	printf("print low resolution: %d\n",
155 	       qpdf_allow_print_low_res(qpdf));
156 	printf("print high resolution: %d\n",
157 	       qpdf_allow_print_high_res(qpdf));
158 	printf("modify document assembly: %d\n",
159 	       qpdf_allow_modify_assembly(qpdf));
160 	printf("modify forms: %d\n",
161 	       qpdf_allow_modify_form(qpdf));
162 	printf("modify annotations: %d\n",
163 	       qpdf_allow_modify_annotation(qpdf));
164 	printf("modify other: %d\n",
165 	       qpdf_allow_modify_other(qpdf));
166 	printf("modify anything: %d\n",
167 	       qpdf_allow_modify_all(qpdf));
168     }
169     report_errors();
170 }
171 
test02(char const * infile,char const * password,char const * outfile,char const * xarg)172 static void test02(char const* infile,
173 		   char const* password,
174 		   char const* outfile,
175 		   char const* xarg)
176 {
177     qpdf_set_suppress_warnings(qpdf, QPDF_TRUE);
178     if (((qpdf_read(qpdf, infile, password) & QPDF_ERRORS) == 0) &&
179 	((qpdf_init_write(qpdf, outfile) & QPDF_ERRORS) == 0))
180     {
181 	qpdf_set_static_ID(qpdf, QPDF_TRUE);
182 	qpdf_write(qpdf);
183     }
184     report_errors();
185 }
186 
test03(char const * infile,char const * password,char const * outfile,char const * xarg)187 static void test03(char const* infile,
188 		   char const* password,
189 		   char const* outfile,
190 		   char const* xarg)
191 {
192     qpdf_read(qpdf, infile, password);
193     qpdf_init_write(qpdf, outfile);
194     qpdf_set_static_ID(qpdf, QPDF_TRUE);
195     qpdf_set_content_normalization(qpdf, QPDF_TRUE);
196     qpdf_write(qpdf);
197     report_errors();
198 }
199 
test04(char const * infile,char const * password,char const * outfile,char const * xarg)200 static void test04(char const* infile,
201 		   char const* password,
202 		   char const* outfile,
203 		   char const* xarg)
204 {
205     qpdf_set_ignore_xref_streams(qpdf, QPDF_TRUE);
206     qpdf_read(qpdf, infile, password);
207     qpdf_init_write(qpdf, outfile);
208     qpdf_set_static_ID(qpdf, QPDF_TRUE);
209     qpdf_write(qpdf);
210     report_errors();
211 }
212 
test05(char const * infile,char const * password,char const * outfile,char const * xarg)213 static void test05(char const* infile,
214 		   char const* password,
215 		   char const* outfile,
216 		   char const* xarg)
217 {
218     int count = 0;
219     qpdf_read(qpdf, infile, password);
220     qpdf_init_write(qpdf, outfile);
221     qpdf_register_progress_reporter(qpdf, count_progress, (void*)&count);
222     qpdf_set_static_ID(qpdf, QPDF_TRUE);
223     qpdf_set_linearization(qpdf, QPDF_TRUE);
224     qpdf_write(qpdf);
225     /* make sure progress reporter was called */
226     assert(count > 0);
227     report_errors();
228 }
229 
test06(char const * infile,char const * password,char const * outfile,char const * xarg)230 static void test06(char const* infile,
231 		   char const* password,
232 		   char const* outfile,
233 		   char const* xarg)
234 {
235     char* buf = NULL;
236     unsigned long size = 0;
237     read_file_into_memory(infile, &buf, &size);
238     qpdf_read_memory(qpdf, infile, buf, size, password);
239     qpdf_init_write(qpdf, outfile);
240     qpdf_set_static_ID(qpdf, QPDF_TRUE);
241     qpdf_set_object_stream_mode(qpdf, qpdf_o_generate);
242     qpdf_write(qpdf);
243     report_errors();
244     free(buf);
245 }
246 
test07(char const * infile,char const * password,char const * outfile,char const * xarg)247 static void test07(char const* infile,
248 		   char const* password,
249 		   char const* outfile,
250 		   char const* xarg)
251 {
252     qpdf_read(qpdf, infile, password);
253     qpdf_init_write(qpdf, outfile);
254     qpdf_set_static_ID(qpdf, QPDF_TRUE);
255     qpdf_set_qdf_mode(qpdf, QPDF_TRUE);
256     qpdf_write(qpdf);
257     report_errors();
258 }
259 
test08(char const * infile,char const * password,char const * outfile,char const * xarg)260 static void test08(char const* infile,
261 		   char const* password,
262 		   char const* outfile,
263 		   char const* xarg)
264 {
265     qpdf_read(qpdf, infile, password);
266     qpdf_init_write(qpdf, outfile);
267     qpdf_set_static_ID(qpdf, QPDF_TRUE);
268     qpdf_set_qdf_mode(qpdf, QPDF_TRUE);
269     qpdf_set_suppress_original_object_IDs(qpdf, QPDF_TRUE);
270     qpdf_write(qpdf);
271     report_errors();
272 }
273 
test09(char const * infile,char const * password,char const * outfile,char const * xarg)274 static void test09(char const* infile,
275 		   char const* password,
276 		   char const* outfile,
277 		   char const* xarg)
278 {
279     qpdf_read(qpdf, infile, password);
280     qpdf_init_write(qpdf, outfile);
281     qpdf_set_static_ID(qpdf, QPDF_TRUE);
282     qpdf_set_stream_data_mode(qpdf, qpdf_s_uncompress);
283     qpdf_write(qpdf);
284     report_errors();
285 }
286 
test10(char const * infile,char const * password,char const * outfile,char const * xarg)287 static void test10(char const* infile,
288 		   char const* password,
289 		   char const* outfile,
290 		   char const* xarg)
291 {
292     qpdf_set_attempt_recovery(qpdf, QPDF_FALSE);
293     qpdf_read(qpdf, infile, password);
294     report_errors();
295 }
296 
test11(char const * infile,char const * password,char const * outfile,char const * xarg)297 static void test11(char const* infile,
298 		   char const* password,
299 		   char const* outfile,
300 		   char const* xarg)
301 {
302     qpdf_read(qpdf, infile, password);
303     qpdf_init_write(qpdf, outfile);
304     qpdf_set_static_ID(qpdf, QPDF_TRUE);
305     qpdf_set_r2_encryption_parameters(
306 	qpdf, "user1", "owner1", QPDF_FALSE, QPDF_TRUE, QPDF_TRUE, QPDF_TRUE);
307     qpdf_write(qpdf);
308     report_errors();
309 }
310 
test12(char const * infile,char const * password,char const * outfile,char const * xarg)311 static void test12(char const* infile,
312 		   char const* password,
313 		   char const* outfile,
314 		   char const* xarg)
315 {
316     qpdf_read(qpdf, infile, password);
317     qpdf_init_write(qpdf, outfile);
318     qpdf_set_static_ID(qpdf, QPDF_TRUE);
319     qpdf_set_r3_encryption_parameters2(
320 	qpdf, "user2", "owner2", QPDF_TRUE, QPDF_TRUE,
321         QPDF_TRUE, QPDF_TRUE, QPDF_TRUE, QPDF_TRUE,
322 	qpdf_r3p_low);
323     qpdf_write(qpdf);
324     report_errors();
325 }
326 
test13(char const * infile,char const * password,char const * outfile,char const * xarg)327 static void test13(char const* infile,
328 		   char const* password,
329 		   char const* outfile,
330 		   char const* xarg)
331 {
332     qpdf_read(qpdf, infile, password);
333     printf("user password: %s\n", qpdf_get_user_password(qpdf));
334     qpdf_init_write(qpdf, outfile);
335     qpdf_set_static_ID(qpdf, QPDF_TRUE);
336     qpdf_set_preserve_encryption(qpdf, QPDF_FALSE);
337     qpdf_write(qpdf);
338     report_errors();
339 }
340 
test14(char const * infile,char const * password,char const * outfile,char const * xarg)341 static void test14(char const* infile,
342 		   char const* password,
343 		   char const* outfile,
344 		   char const* xarg)
345 {
346     qpdf_read(qpdf, infile, password);
347     qpdf_init_write(qpdf, outfile);
348     qpdf_set_static_ID(qpdf, QPDF_TRUE);
349     qpdf_set_minimum_pdf_version_and_extension(qpdf, "1.7", 8);
350     qpdf_write(qpdf);
351     qpdf_init_write(qpdf, xarg);
352     qpdf_set_static_ID(qpdf, QPDF_TRUE);
353     qpdf_force_pdf_version(qpdf, "1.4");
354     qpdf_write(qpdf);
355     report_errors();
356 }
357 
test15(char const * infile,char const * password,char const * outfile,char const * xarg)358 static void test15(char const* infile,
359 		   char const* password,
360 		   char const* outfile,
361 		   char const* xarg)
362 {
363     qpdf_read(qpdf, infile, password);
364     qpdf_init_write(qpdf, outfile);
365     qpdf_set_static_ID(qpdf, QPDF_TRUE);
366     qpdf_set_static_aes_IV(qpdf, QPDF_TRUE);
367     qpdf_set_r4_encryption_parameters2(
368 	qpdf, "user2", "owner2", QPDF_TRUE, QPDF_TRUE,
369         QPDF_TRUE, QPDF_TRUE, QPDF_TRUE, QPDF_TRUE,
370 	qpdf_r3p_low, QPDF_TRUE, QPDF_TRUE);
371     qpdf_write(qpdf);
372     report_errors();
373 }
374 
print_info(char const * key)375 static void print_info(char const* key)
376 {
377     char const* value = qpdf_get_info_key(qpdf, key);
378     printf("Info key %s: %s\n",
379 	   key, (value ? value : "(null)"));
380 }
381 
test16(char const * infile,char const * password,char const * outfile,char const * xarg)382 static void test16(char const* infile,
383 		   char const* password,
384 		   char const* outfile,
385 		   char const* xarg)
386 {
387     unsigned long buflen = 0L;
388     unsigned char const* buf = 0;
389     FILE* f = 0;
390 
391     qpdf_read(qpdf, infile, password);
392     print_info("/Author");
393     print_info("/Producer");
394     print_info("/Creator");
395     qpdf_set_info_key(qpdf, "/Author", "Mr. Potato Head");
396     qpdf_set_info_key(qpdf, "/Producer", "QPDF library");
397     qpdf_set_info_key(qpdf, "/Creator", 0);
398     print_info("/Author");
399     print_info("/Producer");
400     print_info("/Creator");
401     qpdf_init_write_memory(qpdf);
402     qpdf_set_static_ID(qpdf, QPDF_TRUE);
403     qpdf_set_static_aes_IV(qpdf, QPDF_TRUE);
404     qpdf_set_stream_data_mode(qpdf, qpdf_s_uncompress);
405     qpdf_write(qpdf);
406     f = safe_fopen(outfile, "wb");
407     buflen = (unsigned long)(qpdf_get_buffer_length(qpdf));
408     buf = qpdf_get_buffer(qpdf);
409     fwrite(buf, 1, buflen, f);
410     fclose(f);
411     report_errors();
412 }
413 
test17(char const * infile,char const * password,char const * outfile,char const * xarg)414 static void test17(char const* infile,
415 		   char const* password,
416 		   char const* outfile,
417 		   char const* xarg)
418 {
419     qpdf_read(qpdf, infile, password);
420     qpdf_init_write(qpdf, outfile);
421     qpdf_set_static_ID(qpdf, QPDF_TRUE);
422     qpdf_set_static_aes_IV(qpdf, QPDF_TRUE);
423     qpdf_set_r5_encryption_parameters2(
424 	qpdf, "user3", "owner3", QPDF_TRUE, QPDF_TRUE,
425         QPDF_TRUE, QPDF_TRUE, QPDF_TRUE, QPDF_TRUE,
426 	qpdf_r3p_low, QPDF_TRUE);
427     qpdf_write(qpdf);
428     report_errors();
429 }
430 
test18(char const * infile,char const * password,char const * outfile,char const * xarg)431 static void test18(char const* infile,
432 		   char const* password,
433 		   char const* outfile,
434 		   char const* xarg)
435 {
436     qpdf_read(qpdf, infile, password);
437     qpdf_init_write(qpdf, outfile);
438     qpdf_set_static_ID(qpdf, QPDF_TRUE);
439     qpdf_set_static_aes_IV(qpdf, QPDF_TRUE);
440     qpdf_set_r6_encryption_parameters2(
441 	qpdf, "user4", "owner4", QPDF_TRUE, QPDF_TRUE,
442         QPDF_TRUE, QPDF_TRUE, QPDF_TRUE, QPDF_TRUE,
443 	qpdf_r3p_low, QPDF_TRUE);
444     qpdf_write(qpdf);
445     report_errors();
446 }
447 
test19(char const * infile,char const * password,char const * outfile,char const * xarg)448 static void test19(char const* infile,
449 		   char const* password,
450 		   char const* outfile,
451 		   char const* xarg)
452 {
453     qpdf_read(qpdf, infile, password);
454     qpdf_init_write(qpdf, outfile);
455     qpdf_set_deterministic_ID(qpdf, QPDF_TRUE);
456     qpdf_write(qpdf);
457     report_errors();
458 }
459 
test20(char const * infile,char const * password,char const * outfile,char const * xarg)460 static void test20(char const* infile,
461 		   char const* password,
462 		   char const* outfile,
463 		   char const* xarg)
464 {
465     qpdf_read(qpdf, infile, password);
466     qpdf_init_write(qpdf, outfile);
467     qpdf_set_static_ID(qpdf, QPDF_TRUE);
468     qpdf_set_static_aes_IV(qpdf, QPDF_TRUE);
469     qpdf_set_compress_streams(qpdf, QPDF_FALSE);
470     qpdf_set_decode_level(qpdf, qpdf_dl_specialized);
471     qpdf_write(qpdf);
472     report_errors();
473 }
474 
test21(char const * infile,char const * password,char const * outfile,char const * xarg)475 static void test21(char const* infile,
476 		   char const* password,
477 		   char const* outfile,
478 		   char const* xarg)
479 {
480     qpdf_read(qpdf, infile, password);
481     qpdf_init_write(qpdf, outfile);
482     qpdf_set_static_ID(qpdf, QPDF_TRUE);
483     qpdf_set_static_aes_IV(qpdf, QPDF_TRUE);
484     qpdf_set_preserve_unreferenced_objects(qpdf, QPDF_TRUE);
485     qpdf_write(qpdf);
486     report_errors();
487 }
488 
test22(char const * infile,char const * password,char const * outfile,char const * xarg)489 static void test22(char const* infile,
490 		   char const* password,
491 		   char const* outfile,
492 		   char const* xarg)
493 {
494     qpdf_read(qpdf, infile, password);
495     qpdf_init_write(qpdf, outfile);
496     qpdf_set_static_ID(qpdf, QPDF_TRUE);
497     qpdf_set_static_aes_IV(qpdf, QPDF_TRUE);
498     qpdf_set_compress_streams(qpdf, QPDF_FALSE);
499     qpdf_set_newline_before_endstream(qpdf, QPDF_TRUE);
500     qpdf_write(qpdf);
501     report_errors();
502 }
503 
test23(char const * infile,char const * password,char const * outfile,char const * xarg)504 static void test23(char const* infile,
505 		   char const* password,
506 		   char const* outfile,
507 		   char const* xarg)
508 {
509     QPDF_ERROR_CODE status = 0;
510     qpdf_read(qpdf, infile, password);
511     status = qpdf_check_pdf(qpdf);
512     printf("status: %d\n", status);
513     report_errors();
514 }
515 
test24(char const * infile,char const * password,char const * outfile,char const * xarg)516 static void test24(char const* infile,
517 		   char const* password,
518 		   char const* outfile,
519 		   char const* xarg)
520 {
521     /* This test case is designed for minimal.pdf. Pull objects out of
522      * minimal.pdf to make sure all our accessors work as expected.
523      */
524 
525     qpdf_read(qpdf, infile, password);
526     qpdf_oh trailer = qpdf_get_trailer(qpdf);
527     /* The library never returns 0 */
528     assert(trailer == 1);
529 
530     /* Get root two different ways */
531     qpdf_oh root = qpdf_get_root(qpdf);
532     assert(qpdf_oh_get_generation(qpdf, root) == 0);
533     qpdf_oh root_from_trailer = qpdf_oh_get_key(qpdf, trailer, "/Root");
534     assert(qpdf_oh_get_object_id(qpdf, root) ==
535            qpdf_oh_get_object_id(qpdf, root_from_trailer));
536 
537     /* Go to the first page and look at all the keys */
538     qpdf_oh pages = qpdf_oh_get_key(qpdf, root, "/Pages");
539     assert(qpdf_oh_is_dictionary(qpdf, pages));
540     assert(qpdf_oh_get_type_code(qpdf, pages) == ot_dictionary);
541     assert(strcmp(qpdf_oh_get_type_name(qpdf, pages), "dictionary") == 0);
542     assert(qpdf_oh_is_initialized(qpdf, pages));
543     qpdf_oh kids = qpdf_oh_get_key(qpdf, pages, "/Kids");
544     assert(qpdf_oh_is_array(qpdf, kids));
545     assert(qpdf_oh_get_type_code(qpdf, kids) == ot_array);
546     assert(strcmp(qpdf_oh_get_type_name(qpdf, kids), "array") == 0);
547     assert(qpdf_oh_get_array_n_items(qpdf, kids) == 1);
548     qpdf_oh page1 = qpdf_oh_get_array_item(qpdf, kids, 0);
549     qpdf_oh_begin_dict_key_iter(qpdf, page1);
550     while (qpdf_oh_dict_more_keys(qpdf))
551     {
552         printf("page dictionary key: %s\n", qpdf_oh_dict_next_key(qpdf));
553     }
554 
555     /* Inspect the first page */
556     qpdf_oh type = qpdf_oh_get_key(qpdf, page1, "/Type");
557     assert(qpdf_oh_is_name(qpdf, type));
558     assert(qpdf_oh_get_type_code(qpdf, type) == ot_name);
559     assert(strcmp(qpdf_oh_get_type_name(qpdf, type), "name") == 0);
560     assert(strcmp(qpdf_oh_get_name(qpdf, type), "/Page") == 0);
561     qpdf_oh parent = qpdf_oh_get_key(qpdf, page1, "/Parent");
562     assert(qpdf_oh_is_indirect(qpdf, parent));
563     qpdf_oh mediabox = qpdf_oh_get_key(qpdf, page1, "/MediaBox");
564     assert(! qpdf_oh_is_scalar(qpdf, mediabox));
565     assert(qpdf_oh_is_array(qpdf, mediabox));
566     assert(qpdf_oh_get_array_n_items(qpdf, mediabox) == 4);
567     for (int i = 0; i < 4; ++i)
568     {
569         qpdf_oh item = qpdf_oh_get_array_item(qpdf, mediabox, i);
570         printf("item %d: %d %.2f\n",
571                i, qpdf_oh_get_int_value_as_int(qpdf, item),
572                qpdf_oh_get_numeric_value(qpdf, item));
573     }
574 
575     /* Exercise different ways of looking at integers */
576     qpdf_oh i2 = qpdf_oh_get_array_item(qpdf, mediabox, 2);
577     assert(qpdf_oh_get_int_value_as_int(qpdf, i2) == 612);
578     assert(qpdf_oh_get_int_value(qpdf, i2) == 612ll);
579     assert(qpdf_oh_get_uint_value_as_uint(qpdf, i2) == 612u);
580     assert(qpdf_oh_get_uint_value(qpdf, i2) == 612ull);
581     /* Exercise accessors of other object types */
582     assert(! qpdf_oh_is_operator(qpdf, i2));
583     assert(! qpdf_oh_is_inline_image(qpdf, i2));
584     /* Chain calls. */
585     qpdf_oh encoding = qpdf_oh_get_key(
586         qpdf, qpdf_oh_get_key(
587             qpdf, qpdf_oh_get_key(
588                 qpdf, qpdf_oh_get_key(
589                     qpdf, page1, "/Resources"),
590                 "/Font"),
591             "/F1"),
592         "/Encoding");
593     assert(strcmp(qpdf_oh_get_name(qpdf, encoding), "/WinAnsiEncoding") == 0);
594 
595     /* Look at page contents to exercise stream functions */
596     qpdf_oh contents = qpdf_oh_get_key(qpdf, page1, "/Contents");
597     assert(qpdf_oh_is_stream(qpdf, contents));
598     assert(qpdf_oh_get_type_code(qpdf, contents) == ot_stream);
599     assert(strcmp(qpdf_oh_get_type_name(qpdf, contents), "stream") == 0);
600     qpdf_oh contents_dict = qpdf_oh_get_dict(qpdf, contents);
601     assert(! qpdf_oh_is_scalar(qpdf, contents));
602     assert(! qpdf_oh_is_scalar(qpdf, contents_dict));
603     qpdf_oh contents_length = qpdf_oh_get_key(qpdf, contents_dict, "/Length");
604     assert(qpdf_oh_is_integer(qpdf, contents_length));
605     assert(qpdf_oh_get_type_code(qpdf, contents_length) == ot_integer);
606     assert(strcmp(qpdf_oh_get_type_name(qpdf, contents_length), "integer") == 0);
607     assert(qpdf_oh_is_scalar(qpdf, contents_length));
608     assert(qpdf_oh_is_number(qpdf, contents_length));
609     qpdf_oh contents_array = qpdf_oh_wrap_in_array(qpdf, contents);
610     assert(qpdf_oh_get_array_n_items(qpdf, contents_array) == 1);
611     assert(qpdf_oh_get_object_id(
612                qpdf, qpdf_oh_get_array_item(qpdf, contents_array, 0)) ==
613            qpdf_oh_get_object_id(qpdf, contents));
614     /* Wrap in array for a non-trivial case */
615     qpdf_oh wrapped_contents_array =
616         qpdf_oh_wrap_in_array(qpdf, contents_array);
617     assert(qpdf_oh_get_array_n_items(qpdf, wrapped_contents_array) == 1);
618     assert(qpdf_oh_get_object_id(
619                qpdf, qpdf_oh_get_array_item(qpdf, wrapped_contents_array, 0)) ==
620            qpdf_oh_get_object_id(qpdf, contents));
621 
622     /* Exercise functions that work with indirect objects */
623     qpdf_oh resources = qpdf_oh_get_key(qpdf, page1, "/Resources");
624     qpdf_oh procset = qpdf_oh_get_key(qpdf, resources, "/ProcSet");
625     assert(strcmp(qpdf_oh_unparse(qpdf, procset),
626            "5 0 R") == 0);
627     assert(strcmp(qpdf_oh_unparse_resolved(qpdf, procset),
628            "[ /PDF /Text ]") == 0);
629     qpdf_oh_make_direct(qpdf, procset);
630     assert(strcmp(qpdf_oh_unparse(qpdf, procset),
631            "[ /PDF /Text ]") == 0);
632     /* The replaced /ProcSet can be seen to be a direct object in the
633      * expected output PDF.
634      */
635     qpdf_oh_replace_key(qpdf, resources, "/ProcSet", procset);
636 
637     /* Release and access to exercise handling of object handle errors
638      * and to show that write still works after releasing. This test
639      * uses the default oh error handler, so messages get written to
640      * stderr. The warning about using the default error handler only
641      * appears once.
642      */
643     qpdf_oh_release(qpdf, page1);
644     contents = qpdf_oh_get_key(qpdf, page1, "/Contents");
645     assert(qpdf_oh_is_null(qpdf, contents));
646     assert(qpdf_oh_get_type_code(qpdf, contents) == ot_null);
647     assert(strcmp(qpdf_oh_get_type_name(qpdf, contents), "null") == 0);
648     assert(qpdf_oh_is_array(qpdf, mediabox));
649     qpdf_oh_release_all(qpdf);
650     assert(! qpdf_oh_is_null(qpdf, mediabox));
651     assert(! qpdf_oh_is_array(qpdf, mediabox));
652     /* Make sure something is assigned when we exit so we check that
653      * it gets properly freed.
654      */
655     qpdf_get_root(qpdf);
656 
657     qpdf_init_write(qpdf, outfile);
658     qpdf_set_static_ID(qpdf, QPDF_TRUE);
659     qpdf_set_qdf_mode(qpdf, QPDF_TRUE);
660     qpdf_set_suppress_original_object_IDs(qpdf, QPDF_TRUE);
661     qpdf_write(qpdf);
662     report_errors();
663 }
664 
test25(char const * infile,char const * password,char const * outfile,char const * xarg)665 static void test25(char const* infile,
666 		   char const* password,
667 		   char const* outfile,
668 		   char const* xarg)
669 {
670     /* This test case is designed for minimal.pdf. */
671     qpdf_read(qpdf, infile, password);
672     qpdf_oh root = qpdf_get_root(qpdf);
673 
674     /* Parse objects from a string */
675     qpdf_oh parsed = qpdf_oh_parse(
676         qpdf, "[ 1 2.0 (3\xf7) << /Four [/Five] >> null true ]");
677     qpdf_oh p_int = qpdf_oh_get_array_item(qpdf, parsed, 0);
678     qpdf_oh p_real = qpdf_oh_get_array_item(qpdf, parsed, 1);
679     qpdf_oh p_string = qpdf_oh_get_array_item(qpdf, parsed, 2);
680     qpdf_oh p_dict = qpdf_oh_get_array_item(qpdf, parsed, 3);
681     qpdf_oh p_null = qpdf_oh_get_array_item(qpdf, parsed, 4);
682     qpdf_oh p_bool = qpdf_oh_get_array_item(qpdf, parsed, 5);
683     assert(qpdf_oh_is_integer(qpdf, p_int) &&
684            qpdf_oh_get_int_value_as_int(qpdf, p_int) == 1);
685     assert(qpdf_oh_get_type_code(qpdf, p_int) == ot_integer);
686     assert(strcmp(qpdf_oh_get_type_name(qpdf, p_int), "integer") == 0);
687     assert(qpdf_oh_is_real(qpdf, p_real) &&
688            (strcmp(qpdf_oh_get_real_value(qpdf, p_real), "2.0") == 0) &&
689            qpdf_oh_get_numeric_value(qpdf, p_real) == 2.0);
690     assert(qpdf_oh_get_type_code(qpdf, p_real) == ot_real);
691     assert(strcmp(qpdf_oh_get_type_name(qpdf, p_real), "real") == 0);
692     assert(qpdf_oh_is_string(qpdf, p_string) &&
693            (strcmp(qpdf_oh_get_string_value(qpdf, p_string), "3\xf7") == 0) &&
694            (strcmp(qpdf_oh_get_utf8_value(qpdf, p_string), "3\xc3\xb7") == 0) &&
695            (strcmp(qpdf_oh_unparse_binary(qpdf, p_string), "<33f7>") == 0));
696     assert(qpdf_oh_get_type_code(qpdf, p_string) == ot_string);
697     assert(strcmp(qpdf_oh_get_type_name(qpdf, p_string), "string") == 0);
698     assert(qpdf_oh_is_dictionary(qpdf, p_dict));
699     qpdf_oh p_five = qpdf_oh_get_key(qpdf, p_dict, "/Four");
700     assert(qpdf_oh_is_or_has_name(qpdf, p_five, "/Five"));
701     assert(qpdf_oh_is_or_has_name(
702                qpdf, qpdf_oh_get_array_item(qpdf, p_five, 0), "/Five"));
703     assert(qpdf_oh_is_null(qpdf, p_null));
704     assert(qpdf_oh_get_type_code(qpdf, p_null) == ot_null);
705     assert(strcmp(qpdf_oh_get_type_name(qpdf, p_null), "null") == 0);
706     assert(qpdf_oh_is_bool(qpdf, p_bool) &&
707            (qpdf_oh_get_bool_value(qpdf, p_bool) == QPDF_TRUE));
708     assert(qpdf_oh_get_type_code(qpdf, p_bool) == ot_boolean);
709     assert(strcmp(qpdf_oh_get_type_name(qpdf, p_bool), "boolean") == 0);
710     qpdf_oh_erase_item(qpdf, parsed, 4);
711     qpdf_oh_insert_item(
712         qpdf, parsed, 2,
713         qpdf_oh_parse(qpdf, "<</A 1 /B 2 /C 3 /D 4>>"));
714     qpdf_oh new_dict = qpdf_oh_get_array_item(qpdf, parsed, 2);
715     assert(qpdf_oh_has_key(qpdf, new_dict, "/A"));
716     assert(qpdf_oh_has_key(qpdf, new_dict, "/D"));
717     qpdf_oh new_array = qpdf_oh_new_array(qpdf);
718     qpdf_oh_replace_or_remove_key(
719         qpdf, new_dict, "/A", qpdf_oh_new_null(qpdf));
720     qpdf_oh_replace_or_remove_key(
721         qpdf, new_dict, "/B", new_array);
722     qpdf_oh_replace_key(
723         qpdf, new_dict, "/C", qpdf_oh_new_dictionary(qpdf));
724     qpdf_oh_remove_key(qpdf, new_dict, "/D");
725     assert(! qpdf_oh_has_key(qpdf, new_dict, "/A"));
726     assert(! qpdf_oh_has_key(qpdf, new_dict, "/D"));
727     qpdf_oh_append_item(
728         qpdf, new_array, qpdf_oh_new_string(qpdf, "potato"));
729     qpdf_oh_append_item(
730         qpdf, new_array,
731         qpdf_oh_new_unicode_string(qpdf, "qww\xc3\xb7\xcf\x80"));
732     qpdf_oh_append_item(qpdf, new_array, qpdf_oh_new_null(qpdf)); /* 2 */
733     qpdf_oh_append_item(qpdf, new_array, qpdf_oh_new_null(qpdf)); /* 3 */
734     qpdf_oh_set_array_item(
735         qpdf, new_array, 2,
736         qpdf_oh_new_name(qpdf, "/Quack"));
737     qpdf_oh_append_item(
738         qpdf, new_array,
739         qpdf_oh_new_real_from_double(qpdf, 4.123, 2));
740     qpdf_oh_append_item(
741         qpdf, new_array,
742         qpdf_oh_new_real_from_string(qpdf, "5.0"));
743     qpdf_oh_append_item(
744         qpdf, new_array,
745         qpdf_oh_new_integer(qpdf, 6));
746     qpdf_oh_append_item(
747         qpdf, new_array, qpdf_oh_new_bool(qpdf, QPDF_TRUE));
748     qpdf_oh_replace_key(qpdf, root, "/QTest", new_dict);
749 
750     qpdf_init_write(qpdf, outfile);
751     qpdf_set_static_ID(qpdf, QPDF_TRUE);
752     qpdf_set_qdf_mode(qpdf, QPDF_TRUE);
753     qpdf_set_suppress_original_object_IDs(qpdf, QPDF_TRUE);
754     qpdf_write(qpdf);
755     report_errors();
756 }
test26(char const * infile,char const * password,char const * outfile,char const * xarg)757 static void test26(char const* infile,
758 		   char const* password,
759 		   char const* outfile,
760 		   char const* xarg)
761 {
762     /* Make sure we detect uninitialized objects */
763     qpdf_data qpdf2 = qpdf_init();
764     qpdf_oh trailer = qpdf_get_trailer(qpdf2);
765     assert(! qpdf_oh_is_initialized(qpdf2, trailer));
766     assert(qpdf_oh_get_type_code(qpdf, trailer) == ot_uninitialized);
767     qpdf_cleanup(&qpdf2);
768 }
769 
test27(char const * infile,char const * password,char const * outfile,char const * xarg)770 static void test27(char const* infile,
771 		   char const* password,
772 		   char const* outfile,
773 		   char const* xarg)
774 {
775     /* Exercise a string with a null. Since the regular methods return
776      * char*, we can't see past the null character without looking
777      * explicitly at the length.
778      */
779     qpdf_oh p_string_with_null = qpdf_oh_parse(qpdf, "<6f6e650074776f>");
780     assert(qpdf_oh_is_string(qpdf, p_string_with_null));
781     assert(strcmp(qpdf_oh_get_string_value(qpdf, p_string_with_null),
782                   "one") == 0);
783     assert(qpdf_get_last_string_length(qpdf) == 7);
784     /* memcmp adds a character to verify the trailing null */
785     assert(memcmp(qpdf_oh_get_string_value(qpdf, p_string_with_null),
786                   "one\000two", 8) == 0);
787     size_t length = 0;
788     p_string_with_null = qpdf_oh_new_binary_string(qpdf, "potato\000salad", 12);
789     /* memcmp adds a character to verify the trailing null */
790     assert(memcmp(qpdf_oh_get_binary_string_value(
791                       qpdf, p_string_with_null, &length),
792                   "potato\000salad", 13) == 0);
793     assert(qpdf_get_last_string_length(qpdf) == 12);
794     assert(length == 12);
795 }
796 
test28(char const * infile,char const * password,char const * outfile,char const * xarg)797 static void test28(char const* infile,
798 		   char const* password,
799 		   char const* outfile,
800 		   char const* xarg)
801 {
802     /* This test case is designed for minimal.pdf. */
803 
804     /* Look at the media box. The media box is in array. Trivially
805      * wrap it and also clone it and make sure we get different
806      * handles with the same contents.
807      */
808     qpdf_read(qpdf, infile, password);
809     qpdf_oh root = qpdf_get_root(qpdf);
810     qpdf_oh pages = qpdf_oh_get_key(qpdf, root, "/Pages");
811     qpdf_oh kids = qpdf_oh_get_key(qpdf, pages, "/Kids");
812     qpdf_oh page1 = qpdf_oh_get_array_item(qpdf, kids, 0);
813     qpdf_oh mediabox = qpdf_oh_get_key(qpdf, page1, "/MediaBox");
814     qpdf_oh wrapped_mediabox = qpdf_oh_wrap_in_array(qpdf, mediabox);
815     qpdf_oh cloned_mediabox = qpdf_oh_new_object(qpdf, mediabox);
816     assert(wrapped_mediabox != mediabox);
817     assert(cloned_mediabox != mediabox);
818     assert(qpdf_oh_get_array_n_items(qpdf, wrapped_mediabox) == 4);
819     for (int i = 0; i < 4; ++i)
820     {
821         qpdf_oh item = qpdf_oh_get_array_item(qpdf, mediabox, i);
822         qpdf_oh item2 = qpdf_oh_get_array_item(qpdf, wrapped_mediabox, i);
823         qpdf_oh item3 = qpdf_oh_get_array_item(qpdf, cloned_mediabox, i);
824         assert(qpdf_oh_get_int_value_as_int(qpdf, item) ==
825                (i == 0 ? 0 :
826                 i == 1 ? 0 :
827                 i == 2 ? 612 :
828                 i == 3 ? 792 :
829                 -1));
830         assert(qpdf_oh_get_int_value_as_int(qpdf, item) ==
831                qpdf_oh_get_int_value_as_int(qpdf, item2));
832         assert(qpdf_oh_get_int_value_as_int(qpdf, item) ==
833                qpdf_oh_get_int_value_as_int(qpdf, item3));
834         qpdf_oh_release(qpdf, item);
835     }
836 }
837 
test29(char const * infile,char const * password,char const * outfile,char const * xarg)838 static void test29(char const* infile,
839 		   char const* password,
840 		   char const* outfile,
841 		   char const* xarg)
842 {
843     /* Trap exceptions thrown by object accessors. Type mismatches are
844      * errors rather than warnings when they don't have an owning QPDF
845      * object.
846      */
847     qpdf_silence_errors(qpdf);
848 
849     /* get_root fails when we have no trailer */
850     qpdf_oh root = qpdf_get_root(qpdf);
851     handle_oh_error(qpdf, "get root");
852     assert(root != 0);
853     assert(! qpdf_oh_is_initialized(qpdf, root));
854 
855     assert(! qpdf_oh_is_initialized(qpdf, qpdf_oh_parse(qpdf, "[oops")));
856     handle_oh_error(qpdf, "bad parse");
857     report_errors();
858 
859     assert(qpdf_oh_get_int_value_as_int(
860                qpdf, qpdf_oh_new_string(qpdf, "x")) == 0);
861     handle_oh_error(qpdf, "type mismatch (int operation on string)");
862     qpdf_oh int_oh = qpdf_oh_new_integer(qpdf, 12);
863     assert(strlen(qpdf_oh_get_string_value(qpdf, int_oh)) == 0);
864     handle_oh_error(qpdf, "type mismatch (string operation on int)");
865 
866     // This doesn't test every possible error flow, but it tests each
867     // way of handling errors in the library code.
868     assert(qpdf_oh_get_array_n_items(qpdf, int_oh) == 0);
869     handle_oh_error(qpdf, "array type mismatch - n_items");
870     assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_array_item(qpdf, int_oh, 3)));
871     handle_oh_error(qpdf, "array type mismatch - item");
872     qpdf_oh_append_item(qpdf, int_oh, qpdf_oh_new_null(qpdf));
873     handle_oh_error(qpdf, "append to non-array");
874     qpdf_oh array = qpdf_oh_new_array(qpdf);
875     assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_array_item(qpdf, array, 3)));
876     handle_oh_error(qpdf, "array bounds");
877 
878     qpdf_oh_begin_dict_key_iter(qpdf, int_oh);
879     assert(qpdf_oh_dict_more_keys(qpdf) == QPDF_FALSE);
880     handle_oh_error(qpdf, "dictionary iter type mismatch");
881     assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_key(qpdf, int_oh, "potato")));
882     handle_oh_error(qpdf, "dictionary type mismatch");
883     assert(qpdf_oh_has_key(qpdf, int_oh, "potato") == QPDF_FALSE);
884     handle_oh_error(qpdf, "dictionary type mismatch");
885 
886     report_errors();
887 }
888 
test30(char const * infile,char const * password,char const * outfile,char const * xarg)889 static void test30(char const* infile,
890 		   char const* password,
891 		   char const* outfile,
892 		   char const* xarg)
893 {
894     assert(qpdf_read(qpdf, infile, password) & QPDF_ERRORS);
895     /* Fail to handle error */
896 }
897 
test31(char const * infile,char const * password,char const * outfile,char const * xarg)898 static void test31(char const* infile,
899 		   char const* password,
900 		   char const* outfile,
901 		   char const* xarg)
902 {
903     /* Make sure type warnings have a specific error code. This test
904      * case is designed for minimal.pdf.
905      */
906     qpdf_read(qpdf, infile, password);
907     qpdf_oh trailer = qpdf_get_trailer(qpdf);
908     assert(qpdf_oh_get_int_value(qpdf, trailer) == 0LL);
909     assert(! qpdf_has_error(qpdf));
910     assert(qpdf_more_warnings(qpdf));
911     qpdf_error e = qpdf_next_warning(qpdf);
912     assert(qpdf_get_error_code(qpdf, e) == qpdf_e_object);
913     report_errors();
914 }
915 
test32(char const * infile,char const * password,char const * outfile,char const * xarg)916 static void test32(char const* infile,
917 		   char const* password,
918 		   char const* outfile,
919 		   char const* xarg)
920 {
921     /* This test case is designed for minimal.pdf. */
922     assert(qpdf_read(qpdf, infile, password) == 0);
923     qpdf_oh page = qpdf_get_object_by_id(qpdf, 3, 0);
924     assert(qpdf_oh_is_dictionary(qpdf, page));
925     assert(qpdf_oh_has_key(qpdf, page, "/MediaBox"));
926     report_errors();
927 }
928 
test33(char const * infile,char const * password,char const * outfile,char const * xarg)929 static void test33(char const* infile,
930 		   char const* password,
931 		   char const* outfile,
932 		   char const* xarg)
933 {
934     /* This test case is designed for minimal.pdf. */
935 
936     /* Convert a direct object to an indirect object and replace it. */
937     assert(qpdf_read(qpdf, infile, password) == 0);
938     qpdf_oh root = qpdf_get_root(qpdf);
939     qpdf_oh pages = qpdf_oh_get_key(qpdf, root, "/Pages");
940     qpdf_oh kids = qpdf_oh_get_key(qpdf, pages, "/Kids");
941     qpdf_oh page1 = qpdf_oh_get_array_item(qpdf, kids, 0);
942     qpdf_oh mediabox = qpdf_oh_get_key(qpdf, page1, "/MediaBox");
943     assert(! qpdf_oh_is_indirect(qpdf, mediabox));
944     qpdf_oh i_mediabox = qpdf_make_indirect_object(qpdf, mediabox);
945     assert(qpdf_oh_is_indirect(qpdf, i_mediabox));
946     qpdf_oh_replace_key(qpdf, page1, "/MediaBox", i_mediabox);
947 
948     /* Replace a different indirect object */
949     qpdf_oh resources = qpdf_oh_get_key(qpdf, page1, "/Resources");
950     qpdf_oh procset = qpdf_oh_get_key(qpdf, resources, "/ProcSet");
951     assert(qpdf_oh_is_indirect(qpdf, procset));
952     qpdf_replace_object(
953         qpdf,
954         qpdf_oh_get_object_id(qpdf, procset),
955         qpdf_oh_get_generation(qpdf, procset),
956         qpdf_oh_parse(qpdf, "[/PDF]"));
957 
958     qpdf_init_write(qpdf, outfile);
959     qpdf_set_static_ID(qpdf, QPDF_TRUE);
960     qpdf_set_qdf_mode(qpdf, QPDF_TRUE);
961     qpdf_set_suppress_original_object_IDs(qpdf, QPDF_TRUE);
962     qpdf_write(qpdf);
963     report_errors();
964 }
965 
test34(char const * infile,char const * password,char const * outfile,char const * xarg)966 static void test34(char const* infile,
967 		   char const* password,
968 		   char const* outfile,
969 		   char const* xarg)
970 {
971     /* This test expects 11-pages.pdf as file1 and minimal.pdf as xarg. */
972 
973     /* Non-error cases for page API */
974 
975     qpdf_data qpdf2 = qpdf_init();
976     assert(qpdf_read(qpdf, infile, password) == 0);
977     assert(qpdf_read(qpdf2, xarg, "") == 0);
978     assert(qpdf_get_num_pages(qpdf) == 11);
979     assert(qpdf_get_num_pages(qpdf2) == 1);
980 
981     /* At this time, there is no C API for accessing stream data, so
982      * we hard-code object IDs from a known input file.
983      */
984     assert(qpdf_oh_get_object_id(qpdf, qpdf_get_page_n(qpdf, 0)) == 4);
985     assert(qpdf_oh_get_object_id(qpdf, qpdf_get_page_n(qpdf, 10)) == 14);
986     qpdf_oh page3 = qpdf_get_page_n(qpdf, 3);
987     assert(qpdf_find_page_by_oh(qpdf, page3) == 3);
988     assert(qpdf_find_page_by_id(
989                qpdf, qpdf_oh_get_object_id(qpdf, page3), 0) == 3);
990 
991     /* Add other page to the end */
992     qpdf_oh opage0 = qpdf_get_page_n(qpdf2, 0);
993     assert(qpdf_add_page(qpdf, qpdf2, opage0, QPDF_FALSE) == 0);
994     /* Add other page before page 3 */
995     assert(qpdf_add_page_at(qpdf, qpdf2, opage0, QPDF_TRUE, page3) == 0);
996     /* Remove page 3 */
997     assert(qpdf_remove_page(qpdf, page3) == 0);
998     /* At page 3 back at the beginning */
999     assert(qpdf_add_page(qpdf, qpdf, page3, QPDF_TRUE) == 0);
1000 
1001     qpdf_init_write(qpdf, outfile);
1002     qpdf_set_static_ID(qpdf, QPDF_TRUE);
1003     qpdf_set_qdf_mode(qpdf, QPDF_TRUE);
1004     qpdf_set_suppress_original_object_IDs(qpdf, QPDF_TRUE);
1005     qpdf_write(qpdf);
1006     report_errors();
1007     qpdf_cleanup(&qpdf2);
1008 }
1009 
test35(char const * infile,char const * password,char const * outfile,char const * xarg)1010 static void test35(char const* infile,
1011 		   char const* password,
1012 		   char const* outfile,
1013 		   char const* xarg)
1014 {
1015     /* This test uses 11-pages.pdf */
1016 
1017     assert(qpdf_get_num_pages(qpdf) == -1);
1018     assert(qpdf_has_error(qpdf));
1019     qpdf_error e = qpdf_get_error(qpdf);
1020     assert(qpdf_get_error_code(qpdf, e) != QPDF_SUCCESS);
1021     assert(! qpdf_has_error(qpdf));
1022 
1023     assert(qpdf_read(qpdf, infile, password) == 0);
1024 
1025     qpdf_oh range = qpdf_get_page_n(qpdf, 11);
1026     assert(! qpdf_oh_is_initialized(qpdf, range));
1027     assert(qpdf_has_error(qpdf));
1028     e = qpdf_get_error(qpdf);
1029     assert(qpdf_get_error_code(qpdf, e) != QPDF_SUCCESS);
1030     assert(! qpdf_has_error(qpdf));
1031 
1032     assert(qpdf_find_page_by_id(qpdf, 100, 0) == -1);
1033     assert(qpdf_has_error(qpdf));
1034     e = qpdf_get_error(qpdf);
1035     assert(qpdf_get_error_code(qpdf, e) != QPDF_SUCCESS);
1036     assert(! qpdf_has_error(qpdf));
1037 
1038     assert(qpdf_find_page_by_oh(qpdf, qpdf_get_root(qpdf)) == -1);
1039     assert(qpdf_more_warnings(qpdf));
1040     e = qpdf_next_warning(qpdf);
1041     assert(qpdf_get_error_code(qpdf, e) != QPDF_SUCCESS);
1042     assert(qpdf_has_error(qpdf));
1043     e = qpdf_get_error(qpdf);
1044     assert(qpdf_get_error_code(qpdf, e) != QPDF_SUCCESS);
1045     assert(! qpdf_has_error(qpdf));
1046 
1047     assert(qpdf_find_page_by_id(qpdf, 100, 0) == -1);
1048     assert(qpdf_has_error(qpdf));
1049     e = qpdf_get_error(qpdf);
1050     assert(qpdf_get_error_code(qpdf, e) != QPDF_SUCCESS);
1051     assert(! qpdf_has_error(qpdf));
1052 
1053     assert(qpdf_add_page(qpdf, qpdf, 1000, QPDF_FALSE) != 0);
1054 
1055     report_errors();
1056 }
1057 
test36(char const * infile,char const * password,char const * outfile,char const * xarg)1058 static void test36(char const* infile,
1059 		   char const* password,
1060 		   char const* outfile,
1061 		   char const* xarg)
1062 {
1063     /* This test uses inherited-rotate.pdf */
1064 
1065     assert(qpdf_read(qpdf, infile, password) == 0);
1066 
1067     /* Non-trivially push inherited attributes */
1068     qpdf_oh page0 = qpdf_get_object_by_id(qpdf, 3, 0);
1069     assert(qpdf_oh_is_dictionary(qpdf, page0));
1070     qpdf_oh r = qpdf_oh_get_key(qpdf, page0, "/Rotate");
1071     assert(qpdf_oh_get_int_value(qpdf, r) == 90);
1072     qpdf_oh_remove_key(qpdf, page0, "/Rotate");
1073     assert(! qpdf_oh_has_key(qpdf, page0, "/Rotate"));
1074 
1075     assert(qpdf_push_inherited_attributes_to_page(qpdf) == 0);
1076     r = qpdf_oh_get_key(qpdf, page0, "/Rotate");
1077     assert(qpdf_oh_get_int_value(qpdf, r) == 270);
1078 
1079     assert(qpdf_add_page(qpdf, qpdf, page0, QPDF_TRUE) == 0);
1080 }
1081 
test37(char const * infile,char const * password,char const * outfile,char const * xarg)1082 static void test37(char const* infile,
1083 		   char const* password,
1084 		   char const* outfile,
1085 		   char const* xarg)
1086 {
1087     /* This test uses 11-pages.pdf */
1088 
1089     /* Manually manipulate pages tree */
1090     assert(qpdf_read(qpdf, infile, password) == 0);
1091     assert(qpdf_get_num_pages(qpdf) == 11);
1092     qpdf_oh pages = qpdf_get_object_by_id(qpdf, 3, 0);
1093     qpdf_oh kids = qpdf_oh_get_key(qpdf, pages, "/Kids");
1094     assert(qpdf_oh_get_array_n_items(qpdf, kids) == 11);
1095     qpdf_oh_erase_item(qpdf, kids, 0);
1096     assert(qpdf_get_num_pages(qpdf) == 11);
1097     assert(qpdf_update_all_pages_cache(qpdf) == 0);
1098     assert(qpdf_get_num_pages(qpdf) == 10);
1099 }
1100 
test38(char const * infile,char const * password,char const * outfile,char const * xarg)1101 static void test38(char const* infile,
1102 		   char const* password,
1103 		   char const* outfile,
1104 		   char const* xarg)
1105 {
1106     /* This test expects 11-pages.pdf. */
1107 
1108     /* Read stream data */
1109 
1110     assert(qpdf_read(qpdf, infile, password) == 0);
1111     qpdf_oh stream = qpdf_get_object_by_id(qpdf, 17, 0);
1112     qpdf_oh dict = qpdf_oh_get_dict(qpdf, stream);
1113     assert(qpdf_oh_get_int_value_as_int(
1114                qpdf, qpdf_oh_get_key(qpdf, dict, "/Length")) == 53);
1115     /* Get raw data */
1116     unsigned char *buf = 0;
1117     size_t len = 0;
1118     assert(qpdf_oh_get_stream_data(
1119                qpdf, stream, qpdf_dl_none, 0, &buf, &len) == 0);
1120     assert(len == 53);
1121     assert(((int)buf[0] == 'x') && ((int)buf[1] == 0234));
1122     free(buf);
1123 
1124     /* Test whether filterable */
1125     QPDF_BOOL filtered = QPDF_FALSE;
1126     assert(qpdf_oh_get_stream_data(
1127                qpdf, stream, qpdf_dl_all, &filtered, 0, 0) == 0);
1128     assert(filtered == QPDF_TRUE);
1129 
1130     /* Get filtered data */
1131     assert(qpdf_oh_get_stream_data(
1132                qpdf, stream, qpdf_dl_all, 0, &buf, &len) == 0);
1133     assert(len == 47);
1134     assert(memcmp((char const*)buf,
1135                   "BT /F1 15 Tf 72 720 Td (Original page 2) Tj ET\n",
1136                   len) == 0);
1137 
1138     /* Get page data */
1139     qpdf_oh page2 = qpdf_get_page_n(qpdf, 1); /* 0-based index */
1140     unsigned char* buf2 = 0;
1141     assert(qpdf_oh_get_page_content_data(qpdf, page2, &buf2, &len) == 0);
1142     assert(len == 47);
1143     assert(memcmp(buf, buf2, len) == 0);
1144     free(buf);
1145     free(buf2);
1146 
1147     /* errors */
1148     printf("page content on broken page\n");
1149     qpdf_oh_replace_key(qpdf, page2, "/Contents", qpdf_oh_new_integer(qpdf, 3));
1150     buf = 0;
1151     qpdf_oh_get_page_content_data(qpdf, page2, &buf, &len);
1152     assert(buf == 0);
1153     report_errors();
1154     printf("stream data for non stream\n");
1155     qpdf_oh root = qpdf_get_root(qpdf);
1156     assert(qpdf_oh_get_stream_data(qpdf, root, qpdf_dl_all, 0, 0, 0) != 0);
1157     report_errors();
1158 }
1159 
test39(char const * infile,char const * password,char const * outfile,char const * xarg)1160 static void test39(char const* infile,
1161 		   char const* password,
1162 		   char const* outfile,
1163 		   char const* xarg)
1164 {
1165     /* This test expects 11-pages.pdf as file1 and minimal.pdf as xarg. */
1166 
1167     /* Foreign object */
1168 
1169     qpdf_data qpdf2 = qpdf_init();
1170     assert(qpdf_read(qpdf, infile, password) == 0);
1171     assert(qpdf_read(qpdf2, xarg, "") == 0);
1172 
1173     qpdf_oh resources = qpdf_get_object_by_id(qpdf2, 3, 0);
1174     qpdf_oh copy = qpdf_oh_copy_foreign_object(qpdf, qpdf2, resources);
1175     qpdf_oh root = qpdf_get_root(qpdf);
1176     qpdf_oh_replace_key(qpdf, root, "/Copy", copy);
1177 
1178     qpdf_init_write(qpdf, outfile);
1179     qpdf_set_static_ID(qpdf, QPDF_TRUE);
1180     qpdf_set_qdf_mode(qpdf, QPDF_TRUE);
1181     qpdf_set_suppress_original_object_IDs(qpdf, QPDF_TRUE);
1182     qpdf_write(qpdf);
1183     report_errors();
1184     qpdf_cleanup(&qpdf2);
1185 }
1186 
test40(char const * infile,char const * password,char const * outfile,char const * xarg)1187 static void test40(char const* infile,
1188 		   char const* password,
1189 		   char const* outfile,
1190 		   char const* xarg)
1191 {
1192     /* This test expects minimal.pdf. */
1193 
1194     /* New stream */
1195 
1196     assert(qpdf_read(qpdf, infile, password) == 0);
1197     qpdf_oh stream = qpdf_oh_new_stream(qpdf);
1198     qpdf_oh_replace_stream_data(
1199         qpdf, stream,
1200         (unsigned char*)"12345\000abcde", 11, /* embedded null */
1201         qpdf_oh_new_null(qpdf), qpdf_oh_new_null(qpdf));
1202     qpdf_oh root = qpdf_get_root(qpdf);
1203     qpdf_oh_replace_key(qpdf, root, "/Potato", stream);
1204 
1205     qpdf_init_write(qpdf, outfile);
1206     qpdf_set_static_ID(qpdf, QPDF_TRUE);
1207     qpdf_set_qdf_mode(qpdf, QPDF_TRUE);
1208     qpdf_set_suppress_original_object_IDs(qpdf, QPDF_TRUE);
1209     qpdf_write(qpdf);
1210     report_errors();
1211 }
1212 
main(int argc,char * argv[])1213 int main(int argc, char* argv[])
1214 {
1215     char* p = 0;
1216     int n = 0;
1217     char const* infile = 0;
1218     char const* password = 0;
1219     char const* outfile = 0;
1220     char const* xarg = 0;
1221     void (*fn)(char const*, char const*, char const*, char const*) = 0;
1222 
1223     if ((p = strrchr(argv[0], '/')) != NULL)
1224     {
1225 	whoami = p + 1;
1226     }
1227     else if ((p = strrchr(argv[0], '\\')) != NULL)
1228     {
1229 	whoami = p + 1;
1230     }
1231     else
1232     {
1233 	whoami = argv[0];
1234     }
1235     if ((argc == 2) && (strcmp(argv[1], "--version") == 0))
1236     {
1237 	printf("qpdf-ctest version %s\n", qpdf_get_qpdf_version());
1238 	return 0;
1239     }
1240 
1241     if (argc < 5)
1242     {
1243 	fprintf(stderr, "usage: %s n infile password outfile\n", whoami);
1244 	exit(2);
1245     }
1246 
1247     n = atoi(argv[1]);
1248     infile = argv[2];
1249     password = argv[3];
1250     outfile = argv[4];
1251     xarg = (argc > 5 ? argv[5] : 0);
1252 
1253     fn = ((n == 1) ? test01 :
1254 	  (n == 2) ? test02 :
1255 	  (n == 3) ? test03 :
1256 	  (n == 4) ? test04 :
1257 	  (n == 5) ? test05 :
1258 	  (n == 6) ? test06 :
1259 	  (n == 7) ? test07 :
1260 	  (n == 8) ? test08 :
1261 	  (n == 9) ? test09 :
1262 	  (n == 10) ? test10 :
1263 	  (n == 11) ? test11 :
1264 	  (n == 12) ? test12 :
1265 	  (n == 13) ? test13 :
1266 	  (n == 14) ? test14 :
1267 	  (n == 15) ? test15 :
1268 	  (n == 16) ? test16 :
1269 	  (n == 17) ? test17 :
1270 	  (n == 18) ? test18 :
1271 	  (n == 19) ? test19 :
1272 	  (n == 20) ? test20 :
1273 	  (n == 21) ? test21 :
1274 	  (n == 22) ? test22 :
1275 	  (n == 23) ? test23 :
1276 	  (n == 24) ? test24 :
1277 	  (n == 25) ? test25 :
1278 	  (n == 26) ? test26 :
1279 	  (n == 27) ? test27 :
1280 	  (n == 28) ? test28 :
1281 	  (n == 29) ? test29 :
1282 	  (n == 30) ? test30 :
1283 	  (n == 31) ? test31 :
1284 	  (n == 32) ? test32 :
1285 	  (n == 33) ? test33 :
1286 	  (n == 34) ? test34 :
1287 	  (n == 35) ? test35 :
1288 	  (n == 36) ? test36 :
1289 	  (n == 37) ? test37 :
1290 	  (n == 38) ? test38 :
1291 	  (n == 39) ? test39 :
1292 	  (n == 40) ? test40 :
1293 	  0);
1294 
1295     if (fn == 0)
1296     {
1297 	fprintf(stderr, "%s: invalid test number %d\n", whoami, n);
1298 	exit(2);
1299     }
1300 
1301     qpdf = qpdf_init();
1302     fn(infile, password, outfile, xarg);
1303     qpdf_cleanup(&qpdf);
1304     assert(qpdf == 0);
1305     printf("C test %d done\n", n);
1306 
1307     return 0;
1308 }
1309