From 20509770999f705c25670bb14a8bb83aa1cc5b96 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sat, 28 Nov 2020 19:48:07 -0500 Subject: [PATCH] Add QPDFObjectHandle manipulation to C API --- ChangeLog | 8 + examples/build.mk | 4 +- examples/pdf-c-objects.c | 114 ++ examples/qtest/c-objects.test | 33 + examples/qtest/c-objects/1-out.pdf | Bin 0 -> 12315 bytes examples/qtest/c-objects/1.pdf | 1501 ++++++++++++++++++++++ examples/qtest/c-objects/2-out.pdf | Bin 0 -> 799 bytes examples/qtest/c-objects/2.pdf | 97 ++ include/qpdf/qpdf-c.h | 224 ++++ libqpdf/qpdf-c.cc | 601 ++++++++- qpdf/qpdf-ctest.c | 172 +++ qpdf/qpdf.testcov | 64 + qpdf/qtest/qpdf.test | 15 + qpdf/qtest/qpdf/c-object-handles-out.pdf | 104 ++ qpdf/qtest/qpdf/c-object-handles.out | 24 + 15 files changed, 2959 insertions(+), 2 deletions(-) create mode 100644 examples/pdf-c-objects.c create mode 100644 examples/qtest/c-objects.test create mode 100644 examples/qtest/c-objects/1-out.pdf create mode 100644 examples/qtest/c-objects/1.pdf create mode 100644 examples/qtest/c-objects/2-out.pdf create mode 100644 examples/qtest/c-objects/2.pdf create mode 100644 qpdf/qtest/qpdf/c-object-handles-out.pdf create mode 100644 qpdf/qtest/qpdf/c-object-handles.out diff --git a/ChangeLog b/ChangeLog index 696e43a6..ae5dd5c3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2020-11-28 Jay Berkenbilt + + * Add new functions to the C API for manipulating + QPDFObjectHandles. The new functions allow creation and + modification of objects, which brings a lot of additional power to + the C API. See include/qpdf/qpdf-c.h for details and + examples/pdf-c-objects.c for a simple example. + 2020-11-21 Jay Berkenbilt * 10.0.4: release diff --git a/examples/build.mk b/examples/build.mk index 8033d7d2..f66e1ea3 100644 --- a/examples/build.mk +++ b/examples/build.mk @@ -11,7 +11,9 @@ BINS_examples = \ pdf-count-strings \ pdf-set-form-values \ pdf-overlay-page -CBINS_examples = pdf-linearize +CBINS_examples = \ + pdf-c-objects \ + pdf-linearize TARGETS_examples = $(foreach B,$(BINS_examples) $(CBINS_examples),examples/$(OUTPUT_DIR)/$(call binname,$(B))) diff --git a/examples/pdf-c-objects.c b/examples/pdf-c-objects.c new file mode 100644 index 00000000..d800b8af --- /dev/null +++ b/examples/pdf-c-objects.c @@ -0,0 +1,114 @@ +/* + * This is an example program to demonstrate use of object handle + * functions in the C API. + */ + +#include +#include +#include +#include + +static char const* whoami = 0; + +static void usage() +{ + fprintf(stderr, "Usage: %s infile infile-password outfile\n", whoami); + exit(2); +} + +QPDF_BOOL +modify_file(qpdf_data qpdf) +{ + /* This small example performs the following operation on the + * document catalog (a.k.a. root): + * - Remove PageLayout + * - Remove OpenAction + * - If there are outlines, set PageMode to UseOutlines; otherwise, + * remove it. + */ + + qpdf_oh root = qpdf_get_root(qpdf); + qpdf_oh_remove_key(qpdf, root, "/PageLayout"); + qpdf_oh_remove_key(qpdf, root, "/OpenAction"); + /* 0 is never a valid qpdf_oh */ + qpdf_oh pagemode = 0; + if (qpdf_oh_is_dictionary( + qpdf, qpdf_oh_get_key(qpdf, root, "/PageLabels"))) + { + pagemode = qpdf_oh_new_name(qpdf, "/UseOutlines"); + } + else + { + pagemode = qpdf_oh_new_null(qpdf); + } + qpdf_oh_replace_or_remove_key(qpdf, root, "/PageMode", pagemode); + + return QPDF_TRUE; +} + +int main(int argc, char* argv[]) +{ + char* infile = NULL; + char* password = NULL; + char* outfile = NULL; + qpdf_data qpdf = qpdf_init(); + int warnings = 0; + int errors = 0; + char* p = 0; + + if ((p = strrchr(argv[0], '/')) != NULL) + { + whoami = p + 1; + } + else if ((p = strrchr(argv[0], '\\')) != NULL) + { + whoami = p + 1; + } + else + { + whoami = argv[0]; + } + + if (argc != 4) + { + usage(); + } + + infile = argv[1]; + password = argv[2]; + outfile = argv[3]; + + if (((qpdf_read(qpdf, infile, password) & QPDF_ERRORS) == 0) && + modify_file(qpdf) && + ((qpdf_init_write(qpdf, outfile) & QPDF_ERRORS) == 0)) + { + /* Use static ID for testing only. For production, a + * non-static ID is used. See also + * qpdf_set_deterministic_ID. */ + qpdf_set_static_ID(qpdf, QPDF_TRUE); /* for testing only */ + qpdf_write(qpdf); + } + while (qpdf_more_warnings(qpdf)) + { + warnings = 1; + printf("warning: %s\n", + qpdf_get_error_full_text(qpdf, qpdf_next_warning(qpdf))); + } + if (qpdf_has_error(qpdf)) + { + errors = 1; + printf("error: %s\n", + qpdf_get_error_full_text(qpdf, qpdf_get_error(qpdf))); + } + qpdf_cleanup(&qpdf); + if (errors) + { + return 2; + } + else if (warnings) + { + return 3; + } + + return 0; +} diff --git a/examples/qtest/c-objects.test b/examples/qtest/c-objects.test new file mode 100644 index 00000000..7ca22d00 --- /dev/null +++ b/examples/qtest/c-objects.test @@ -0,0 +1,33 @@ +#!/usr/bin/env perl +require 5.008; +BEGIN { $^W = 1; } +use strict; + +chdir("c-objects") or die "chdir testdir failed: $!\n"; + +require TestDriver; + +cleanup(); + +my $td = new TestDriver('c-objects'); + +my $qpdf = $ENV{'QPDF_BIN'} or die; + +foreach my $i (qw(1 2)) +{ + $td->runtest("c-objects ($i)", + {$td->COMMAND => "pdf-c-objects $i.pdf '' a.pdf"}, + {$td->STRING => "", $td->EXIT_STATUS => 0}); + $td->runtest("check output", + {$td->FILE => "a.pdf"}, + {$td->FILE => "$i-out.pdf"}); +} + +cleanup(); + +$td->report(4); + +sub cleanup +{ + unlink "a.pdf"; +} diff --git a/examples/qtest/c-objects/1-out.pdf b/examples/qtest/c-objects/1-out.pdf new file mode 100644 index 0000000000000000000000000000000000000000..bc1626038bc438e5e2ae542601f2f4336bcc0edf GIT binary patch literal 12315 zcmc&*O>f*p80JHW$`W_V=M+(?1yXy)_SiPNb2`jMtdpoP$bo4Dzg&2HJOm~<+T5R8{&Ou#_O?PYf;3m+`}e28Ncr{&%E>4^LV;{ zX}&eb5BbjiAO8OKABQuK^{!lZ4j*Rj`R!q+-OUC}K;trVm(tDbYf}-aeHgP z*5FV0Wr-a)$c`L=uJ~<*xqbK?GLCIgsYg=oA+ZPNr7nCNWnM)Qs!y0-s2lZ#I@&n9 z1_SAJ9|(k)D&e^F!bJVS&qHdop{H6a2*UtdFXM-z;9PGbWA3|yY|M{p9xz|iw0ipn z{QX2aOgp_zxXQ`88%Qa#m{WVAx7{5w$=p`EKNzw=UwJl#-$OgfI7JZ#vy{X59&^vM zH*hkfx;RpwvHDEZC-?LZoZe*yw5T5%FxT0+PRw-(u48Z=gzG3mM-e&-(27xnjv{mv zp`!>LMd&C(M-e&-z{Tr)9mUsCd>zHtQG6ZcGIKSVSa}1BO6-+$FrNWwYs`RhFTMW( zlafWDg!zB~i`mYY)1|bZ0qr`YyV@RhGIn6KpWR45g2p+h!|-iZif}MTU?#%CfDm&G zdO8CrI}HZv8HcDsb+?J?Flr$W@c>Yoef#Y=lew>}FmY2JOP2J%CjOSaYaF zX#8ygV9Zwl?hb(ps98NV?zwEEogVM)s0HmY2_+IG0tqyb^!&KX*`T-GUx(Sib>8X$ zo$43Pd_B8rmGpb-D;bovo@s0<=Fy@M2Tc$KhPy@(AsZBtDVRnOA*&RTnTQUDN9=28mgsQ z>4v8jb(3jk;%c=!Y*M?`vkIBGuu1V&&rK9klj^OW;V7gg)muHfQAkayw|cgvkeXC) z^_)#1HL2d}nV&*xQoYswgF3NKLA@@)RhfCe>T{8x&HL>aDyM3aLr;R=y8~)TDYVo2Gz-w%wdx7(HeJ zTkRowCED4EjI=9fCo-a{e5i`eW7deVc8+bO-Hnv-L(1Oa9IPk^JHgOV(aO+7fy|Hw zAx`C1j)7-1Rl_rCW?UYFNEuJLL1o~r0J__NP8(&uMyEngf@m8mXS43+@EU`C3k<9? z%)oSQb<$xrpRJ>%3Kr*J*w4}}XXl%N^LTmb;VpmF6BqU@Jbmw{ukPGGy6^R8?myad z-(7g?!A0-k>YfLG^`CqE)DwU1b)E-{)m&@LR^>+J+#F8_nX;0r1gV&tBgoD7gAf}r5mm=|-&I>fLZ8S_Z@uMPf;Jio^+jhcK6aQQ! ze#d!ripE9V5%ejwMcy4d4(pn4S=a8{-a2I$$6C~o|UBBwIn5V z7)^|dI*U2JYpvt!&OVY@89mj+AV&XRM1x|;qsUpHnepZ|o6NjZ2%nF|FQ-E@{{<~jwNO6KpZ_57)Gfo86gu<2yB*7Fz6GRds$j+rQl zu*Q6>MoHw%G)!1yK3$_Ea%LJPEDc+!Q4%>b4HK4zeORL;vQERM@WB7bRoCD$2eN9&FE}&BXi!d? z%)A2QT|={VIfrKQPUI{{qd7BcsIJa(ODfqlG@QD8ndHzWu8y22R%r`k;JMNkQG7gx6S?lX(k(1kw)zlC}_E zNLJ!Wl+Q|Aj7J43Z3#Ydl-oim4$>y@sk_n-K$Er*pHwUTB8o4J1DZDp2?t?9YfW;G zTq^~>ZY<}Oen7aA0jf}?Ur6UjMtG#Q(l4epE)(42m-|ItM8*+uS}&20r%5W;252r< z+G;qBB(0Yyro6zBPm&|{@KjGZN6cxx#3CW%hyy%FT)8$xZ@$tdX}!czOll)eJi=e% z(RxX0^ePc_E)yTlxK^+vA+6^`#x!qCI6&#Fj*WXfuJXq{A78GNIN)Aj*VV9}wma~K z1P|9P_j-7eUOhCdv_Hw<9>Ls&dA4@g=K&8BA>k$wCsE=HKMtgjp#HHhq9i=>-#zeL YJG^&E`$KgD;-2*3{{rkkdA{ZR3s{nk8UO$Q literal 0 HcmV?d00001 diff --git a/examples/qtest/c-objects/1.pdf b/examples/qtest/c-objects/1.pdf new file mode 100644 index 00000000..0168720e --- /dev/null +++ b/examples/qtest/c-objects/1.pdf @@ -0,0 +1,1501 @@ +%PDF-1.3 +%¿÷¢þ +%QDF-1.0 + +1 0 obj +<< + /PageLabels << /Nums [ + 0 << /P () >> + 2 << /S /r /St 1 >> + 7 << /P () >> + 9 << /S /r /St 6 >> + 11 << /P () >> + 12 << /S /D /St 2 >> + 15 << /S /D /St 6 >> + 19 << /P () >> + 20 << /S /D /St 12 >> + 22 << /S /D /St 16059 >> + 23 << /S /r /St 50 >> + 29 << /S /r /St 54 >> + ] >> + /Pages 2 0 R + /Type /Catalog + /Outlines 95 0 R +>> +endobj + +2 0 obj +<< + /Count 30 + /Kids [ + 3 0 R + 4 0 R + 5 0 R + 6 0 R + 7 0 R + 8 0 R + 9 0 R + 10 0 R + 11 0 R + 12 0 R + 13 0 R + 14 0 R + 15 0 R + 16 0 R + 17 0 R + 18 0 R + 19 0 R + 20 0 R + 21 0 R + 22 0 R + 23 0 R + 24 0 R + 25 0 R + 26 0 R + 27 0 R + 28 0 R + 29 0 R + 30 0 R + 31 0 R + 32 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +3 0 obj +<< + /Contents 33 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 2 +4 0 obj +<< + /Contents 37 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 3 +5 0 obj +<< + /Contents 39 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 4 +6 0 obj +<< + /Contents 41 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 5 +7 0 obj +<< + /Contents 43 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 6 +8 0 obj +<< + /Contents 45 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 7 +9 0 obj +<< + /Contents 47 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 8 +10 0 obj +<< + /Contents 49 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 9 +11 0 obj +<< + /Contents 51 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 10 +12 0 obj +<< + /Contents 53 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 11 +13 0 obj +<< + /Contents 55 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 12 +14 0 obj +<< + /Contents 57 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 13 +15 0 obj +<< + /Contents 59 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 14 +16 0 obj +<< + /Contents 61 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 15 +17 0 obj +<< + /Contents 63 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 16 +18 0 obj +<< + /Contents 65 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 17 +19 0 obj +<< + /Contents 67 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 18 +20 0 obj +<< + /Contents 69 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 19 +21 0 obj +<< + /Contents 71 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 20 +22 0 obj +<< + /Contents 73 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 21 +23 0 obj +<< + /Contents 75 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 22 +24 0 obj +<< + /Contents 77 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 23 +25 0 obj +<< + /Contents 79 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 24 +26 0 obj +<< + /Contents 81 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 25 +27 0 obj +<< + /Contents 83 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 26 +28 0 obj +<< + /Contents 85 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 27 +29 0 obj +<< + /Contents 87 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 28 +30 0 obj +<< + /Contents 89 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 29 +31 0 obj +<< + /Contents 91 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Page 30 +32 0 obj +<< + /Contents 93 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 35 0 R + >> + /ProcSet 36 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +33 0 obj +<< + /Length 34 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 0) Tj +ET +endstream +endobj + +34 0 obj +46 +endobj + +35 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +36 0 obj +[ + /PDF + /Text +] +endobj + +%% Contents for page 2 +37 0 obj +<< + /Length 38 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 1) Tj +ET +endstream +endobj + +38 0 obj +46 +endobj + +%% Contents for page 3 +39 0 obj +<< + /Length 40 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 2) Tj +ET +endstream +endobj + +40 0 obj +46 +endobj + +%% Contents for page 4 +41 0 obj +<< + /Length 42 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 3) Tj +ET +endstream +endobj + +42 0 obj +46 +endobj + +%% Contents for page 5 +43 0 obj +<< + /Length 44 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 4) Tj +ET +endstream +endobj + +44 0 obj +46 +endobj + +%% Contents for page 6 +45 0 obj +<< + /Length 46 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 5) Tj +ET +endstream +endobj + +46 0 obj +46 +endobj + +%% Contents for page 7 +47 0 obj +<< + /Length 48 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 6) Tj +ET +endstream +endobj + +48 0 obj +46 +endobj + +%% Contents for page 8 +49 0 obj +<< + /Length 50 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 7) Tj +ET +endstream +endobj + +50 0 obj +46 +endobj + +%% Contents for page 9 +51 0 obj +<< + /Length 52 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 8) Tj +ET +endstream +endobj + +52 0 obj +46 +endobj + +%% Contents for page 10 +53 0 obj +<< + /Length 54 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 9) Tj +ET +endstream +endobj + +54 0 obj +46 +endobj + +%% Contents for page 11 +55 0 obj +<< + /Length 56 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 10) Tj +ET +endstream +endobj + +56 0 obj +47 +endobj + +%% Contents for page 12 +57 0 obj +<< + /Length 58 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 11) Tj +ET +endstream +endobj + +58 0 obj +47 +endobj + +%% Contents for page 13 +59 0 obj +<< + /Length 60 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 12) Tj +ET +endstream +endobj + +60 0 obj +47 +endobj + +%% Contents for page 14 +61 0 obj +<< + /Length 62 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 13) Tj +ET +endstream +endobj + +62 0 obj +47 +endobj + +%% Contents for page 15 +63 0 obj +<< + /Length 64 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 14) Tj +ET +endstream +endobj + +64 0 obj +47 +endobj + +%% Contents for page 16 +65 0 obj +<< + /Length 66 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 15) Tj +ET +endstream +endobj + +66 0 obj +47 +endobj + +%% Contents for page 17 +67 0 obj +<< + /Length 68 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 16) Tj +ET +endstream +endobj + +68 0 obj +47 +endobj + +%% Contents for page 18 +69 0 obj +<< + /Length 70 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 17) Tj +ET +endstream +endobj + +70 0 obj +47 +endobj + +%% Contents for page 19 +71 0 obj +<< + /Length 72 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 18) Tj +ET +endstream +endobj + +72 0 obj +47 +endobj + +%% Contents for page 20 +73 0 obj +<< + /Length 74 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 19) Tj +ET +endstream +endobj + +74 0 obj +47 +endobj + +%% Contents for page 21 +75 0 obj +<< + /Length 76 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 20) Tj +ET +endstream +endobj + +76 0 obj +47 +endobj + +%% Contents for page 22 +77 0 obj +<< + /Length 78 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 21) Tj +ET +endstream +endobj + +78 0 obj +47 +endobj + +%% Contents for page 23 +79 0 obj +<< + /Length 80 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 22) Tj +ET +endstream +endobj + +80 0 obj +47 +endobj + +%% Contents for page 24 +81 0 obj +<< + /Length 82 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 23) Tj +ET +endstream +endobj + +82 0 obj +47 +endobj + +%% Contents for page 25 +83 0 obj +<< + /Length 84 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 24) Tj +ET +endstream +endobj + +84 0 obj +47 +endobj + +%% Contents for page 26 +85 0 obj +<< + /Length 86 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 25) Tj +ET +endstream +endobj + +86 0 obj +47 +endobj + +%% Contents for page 27 +87 0 obj +<< + /Length 88 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 26) Tj +ET +endstream +endobj + +88 0 obj +47 +endobj + +%% Contents for page 28 +89 0 obj +<< + /Length 90 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 27) Tj +ET +endstream +endobj + +90 0 obj +47 +endobj + +%% Contents for page 29 +91 0 obj +<< + /Length 92 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 28) Tj +ET +endstream +endobj + +92 0 obj +47 +endobj + +%% Contents for page 30 +93 0 obj +<< + /Length 94 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato 29) Tj +ET +endstream +endobj + +94 0 obj +47 +endobj + +95 0 obj +<< + /Type /Outlines + /First 97 0 R + /Last 96 0 R + /Count 6 +>> +endobj + +96 0 obj +<< + /Type /Outline + /Title (Isis 1 -> 5: /XYZ null null null) + /Parent 95 0 R + /Count 4 + /Prev 97 0 R + /First 98 0 R + /Last 99 0 R + /Dest [ 8 0 R /XYZ null null null ] +>> +endobj + +97 0 obj +<< + /Type /Outline + /Title (Trepak 2 -> 15: /XYZ 66 756 3) + /Parent 95 0 R + /Next 96 0 R + /Dest [ 18 0 R /XYZ 66 756 3 ] +>> +endobj + +98 0 obj +<< + /Type /Outline + /Title (Amanda 1.1 -> 11: /Fit) + /Parent 96 0 R + /Next 99 0 R + /First 100 0 R + /Last 101 0 R + /Count -3 + /Dest [ 14 0 R /Fit ] +>> +endobj + +99 0 obj +<< + /Type /Outline + /Title (Sandy 1.2 -> 13: /FitH 792) + /Parent 96 0 R + /Prev 98 0 R + /First 105 0 R + /Last 106 0 R + /Count 2 + /Dest [ 16 0 R /FitH 792 ] +>> +endobj + +100 0 obj +<< + /Type /Outline + /Title (Isosicle 1.1.1 -> 12: /FitV 100) + /Parent 98 0 R + /Next 101 0 R + /First 102 0 R + /Last 103 0 R + /Count -2 + /Dest [ 15 0 R /FitV 100 ] +>> +endobj + +101 0 obj +<< + /Type /Outline + /Title (Isosicle 1.1.2 -> 12: /XYZ null null null) + /Parent 98 0 R + /Prev 100 0 R + /First 104 0 R + /Last 104 0 R + /Count 1 + /Dest [ 15 0 R /XYZ null null null ] +>> +endobj + +102 0 obj +<< + /Type /Outline + /Title (Isosicle 1.1.1.1 -> 18: /XYZ null null null) + /Parent 100 0 R + /Next 103 0 R + /Dest [ 21 0 R /XYZ null null null ] +>> +endobj + +103 0 obj +<< + /Type /Outline + /Title (Isosicle 1.1.1.2 -> 19: /XYZ null null null) + /Parent 100 0 R + /Prev 102 0 R + /Dest [ 22 0 R /XYZ null null null ] +>> +endobj + +104 0 obj +<< + /Type /Outline + /Title (Isosicle 1.1.2.1 -> 22: /XYZ null null null) + /Parent 101 0 R + /Dest [ 25 0 R /XYZ null null null ] +>> +endobj + +105 0 obj +<< + /Type /Outline + /Title (Trepsichord 1.2.1 -> 1: /FitR 66 714 180 770) + /Parent 99 0 R + /Next 106 0 R + /Dest [ 4 0 R /FitR 66 714 180 770 ] +>> +endobj + +106 0 obj +<< + /Type /Outline + /Title (Trepsicle 1.2.2 -> 0: /XYZ null null null) + /Parent 99 0 R + /Prev 105 0 R + /Dest [ 3 0 R /XYZ null null null ] +>> +endobj + +xref +0 107 +0000000000 65535 f +0000000025 00000 n +0000000409 00000 n +0000000805 00000 n +0000001010 00000 n +0000001215 00000 n +0000001420 00000 n +0000001625 00000 n +0000001830 00000 n +0000002035 00000 n +0000002240 00000 n +0000002446 00000 n +0000002653 00000 n +0000002860 00000 n +0000003067 00000 n +0000003274 00000 n +0000003481 00000 n +0000003688 00000 n +0000003895 00000 n +0000004102 00000 n +0000004309 00000 n +0000004516 00000 n +0000004723 00000 n +0000004930 00000 n +0000005137 00000 n +0000005344 00000 n +0000005551 00000 n +0000005758 00000 n +0000005965 00000 n +0000006172 00000 n +0000006379 00000 n +0000006586 00000 n +0000006793 00000 n +0000007012 00000 n +0000007115 00000 n +0000007135 00000 n +0000007254 00000 n +0000007313 00000 n +0000007416 00000 n +0000007459 00000 n +0000007562 00000 n +0000007605 00000 n +0000007708 00000 n +0000007751 00000 n +0000007854 00000 n +0000007897 00000 n +0000008000 00000 n +0000008043 00000 n +0000008146 00000 n +0000008189 00000 n +0000008292 00000 n +0000008335 00000 n +0000008438 00000 n +0000008482 00000 n +0000008585 00000 n +0000008629 00000 n +0000008733 00000 n +0000008777 00000 n +0000008881 00000 n +0000008925 00000 n +0000009029 00000 n +0000009073 00000 n +0000009177 00000 n +0000009221 00000 n +0000009325 00000 n +0000009369 00000 n +0000009473 00000 n +0000009517 00000 n +0000009621 00000 n +0000009665 00000 n +0000009769 00000 n +0000009813 00000 n +0000009917 00000 n +0000009961 00000 n +0000010065 00000 n +0000010109 00000 n +0000010213 00000 n +0000010257 00000 n +0000010361 00000 n +0000010405 00000 n +0000010509 00000 n +0000010553 00000 n +0000010657 00000 n +0000010701 00000 n +0000010805 00000 n +0000010849 00000 n +0000010953 00000 n +0000010997 00000 n +0000011101 00000 n +0000011145 00000 n +0000011249 00000 n +0000011293 00000 n +0000011397 00000 n +0000011441 00000 n +0000011545 00000 n +0000011565 00000 n +0000011652 00000 n +0000011848 00000 n +0000011994 00000 n +0000012169 00000 n +0000012352 00000 n +0000012543 00000 n +0000012753 00000 n +0000012922 00000 n +0000013091 00000 n +0000013244 00000 n +0000013413 00000 n +trailer << + /Root 1 0 R + /Size 107 +>> +startxref +13578 +%%EOF diff --git a/examples/qtest/c-objects/2-out.pdf b/examples/qtest/c-objects/2-out.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e3f9f34a2a4f90a8339756e976e14bb6a33ca262 GIT binary patch literal 799 zcmZWnOODe(5alDJ&=PkjB(ekS?soelMHb9tEFyj(iNqqxqGPv%k;g`M2LdZDzztY& z1TMffBZR~X*25*(A*!8mj6!>1`*po~Rj;Z)9!7WQT~7L6et-HyDA-UgUy{K9tg&3F z8Wb@$ynq4b8p{d;+p;FAF4t9zb()H!5^tIh3Zh9AhRCSOc&aqG zdt}#)spp?gjB;DfBUP;Q3vhhQndMql_|BzPLlu`vL#lP92$;GF#OnS*xZ6>w(H& zE1ks>tdjyIvtk9-^Q^dE)L92Sk*7G52o0aEm%15{=?wpO4K}$tzfJ!}YL1o+BMed- z4U2Y=n@Xj`29MbL1FjHUKni<|V!7r((P1tccYslbvpv9=i~0u`_y6ZQ0q$TMrz@G| z=)Rfhq%6&HHQPAN-YM|F8VzASPzm!rM>-;K<5&o`^tnjA)Uh2-rJss$z^Ow;z+CLc j54?ag6n2^GaPD*F1tMISJ*c&;bYp{Hl=S;2Pb2adMqt}^ literal 0 HcmV?d00001 diff --git a/examples/qtest/c-objects/2.pdf b/examples/qtest/c-objects/2.pdf new file mode 100644 index 00000000..7b33acb1 --- /dev/null +++ b/examples/qtest/c-objects/2.pdf @@ -0,0 +1,97 @@ +%PDF-1.3 +%¿÷¢þ +%QDF-1.0 + +1 0 obj +<< + /Pages 2 0 R + /Type /Catalog + /PageLayout /v1 + /OpenAction /v2 +>> +endobj + +2 0 obj +<< + /Count 1 + /Kids [ + 3 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +3 0 obj +<< + /Contents 4 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 6 0 R + >> + /ProcSet 7 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 +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +7 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 8 +0000000000 65535 f +0000000025 00000 n +0000000115 00000 n +0000000197 00000 n +0000000412 00000 n +0000000511 00000 n +0000000530 00000 n +0000000648 00000 n +trailer << + /Root 1 0 R + /Size 8 + /ID [] +>> +startxref +683 +%%EOF diff --git a/include/qpdf/qpdf-c.h b/include/qpdf/qpdf-c.h index a6c32fe8..961cfa4a 100644 --- a/include/qpdf/qpdf-c.h +++ b/include/qpdf/qpdf-c.h @@ -471,6 +471,230 @@ extern "C" { QPDF_DLL QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf); + /* Object handling. + * + * These methods take and return a qpdf_oh, which is just an + * unsigned integer. The value 0 is never returned, which makes it + * usable as an uninitialized value. + * + * Each function below, starting with qpdf_oh, corresponds to a + * specific method of QPDFObjectHandler. For example, + * qpdf_oh_is_bool corresponds to QPDFObjectHandle::isBool. If the + * C++ method is overloaded, the C function's name will be + * disambiguated. If the C++ method takes optional argumens, the C + * method will have required arguments in those positions. For + * details about the method, please see comments in + * QPDFObjectHandle.hh. Comments here only explain things that are + * specific to the "C" API. + * + * Only a fraction of the methods of QPDFObjectHandle are + * available here. Most of the basic methods for creating, + * accessing, and modifying most types of objects are present. + * Most of the higher-level functions are not implemented. + * Functions for dealing with content streams as well as objects + * that only exist in content streams (operators and inline + * images) are mostly not provided. + * + * To refer to a specific QPDFObjectHandle, you need a pair + * consisting of a qpdf_data and a qpdf_oh, which is just an index + * into an internal table of objects. All memory allocated by any + * of these methods is returned when qpdf_cleanup is called. + * + * Regarding memory, the same rules apply as the above functions. + * Specifically, if a method returns a char*, the memory is + * managed by the library and, unless otherwise specified, is not + * expected to be valid after the next qpdf call. + * + * The qpdf_data object keeps a cache of objects returned by these + * methods. Once you are finished referencing an object, you can + * optionally release it. Releasing objects is optional since they + * will all get released by qpdf_cleanup, but it can help to + * reduce the memory footprint of the qpdf_data object to release + * them when you're done. Releasing an object does not destroy the + * object. All QPDFObjectHandle objects are deleted when they are + * no longer referenced. Releasing an object simply invalidates + * the qpdf_oh handle to it. For example, if you create an object, + * add it to an existing dictionary or array, and then release it, + * the object is safely part of the dictionary or array. + * Explicitly releasing an object is essentially the same as + * letting a QPDFObjectHandle go out of scope in the C++ API. + */ + + /* For examples of using this API, see examples/pdf-c-objects.c */ + + typedef unsigned int qpdf_oh; + + /* Releasing objects -- see comments above. These methods have no + * equivalent in the C++ API. + */ + QPDF_DLL + void qpdf_oh_release(qpdf_data data, qpdf_oh oh); + QPDF_DLL + void qpdf_oh_release_all(qpdf_data data); + + /* Get trailer and root objects */ + QPDF_DLL + qpdf_oh qpdf_get_trailer(qpdf_data data); + QPDF_DLL + qpdf_oh qpdf_get_root(qpdf_data data); + + /* 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 + * to. + */ + + QPDF_DLL + QPDF_BOOL qpdf_oh_is_bool(qpdf_data data, qpdf_oh oh); + QPDF_DLL + QPDF_BOOL qpdf_oh_is_null(qpdf_data data, qpdf_oh oh); + QPDF_DLL + QPDF_BOOL qpdf_oh_is_integer(qpdf_data data, qpdf_oh oh); + QPDF_DLL + QPDF_BOOL qpdf_oh_is_real(qpdf_data data, qpdf_oh oh); + QPDF_DLL + QPDF_BOOL qpdf_oh_is_name(qpdf_data data, qpdf_oh oh); + QPDF_DLL + QPDF_BOOL qpdf_oh_is_string(qpdf_data data, qpdf_oh oh); + QPDF_DLL + QPDF_BOOL qpdf_oh_is_operator(qpdf_data data, qpdf_oh oh); + QPDF_DLL + QPDF_BOOL qpdf_oh_is_inline_image(qpdf_data data, qpdf_oh oh); + QPDF_DLL + QPDF_BOOL qpdf_oh_is_array(qpdf_data data, qpdf_oh oh); + QPDF_DLL + QPDF_BOOL qpdf_oh_is_dictionary(qpdf_data data, qpdf_oh oh); + QPDF_DLL + QPDF_BOOL qpdf_oh_is_stream(qpdf_data data, qpdf_oh oh); + QPDF_DLL + QPDF_BOOL qpdf_oh_is_indirect(qpdf_data data, qpdf_oh oh); + QPDF_DLL + QPDF_BOOL qpdf_oh_is_scalar(qpdf_data data, qpdf_oh oh); + + QPDF_DLL + qpdf_oh qpdf_oh_wrap_in_array(qpdf_data data, qpdf_oh oh); + + QPDF_DLL + qpdf_oh qpdf_oh_parse(qpdf_data data, char const* object_str); + + QPDF_DLL + QPDF_BOOL qpdf_oh_get_bool_value(qpdf_data data, qpdf_oh oh); + + QPDF_DLL + long long qpdf_oh_get_int_value(qpdf_data data, qpdf_oh oh); + QPDF_DLL + int qpdf_oh_get_int_value_as_int(qpdf_data data, qpdf_oh oh); + QPDF_DLL + unsigned long long qpdf_oh_get_uint_value(qpdf_data data, qpdf_oh oh); + QPDF_DLL + unsigned int qpdf_oh_get_uint_value_as_uint(qpdf_data data, qpdf_oh oh); + + QPDF_DLL + char const* qpdf_oh_get_real_value(qpdf_data data, qpdf_oh oh); + + QPDF_DLL + QPDF_BOOL qpdf_oh_is_number(qpdf_data data, qpdf_oh oh); + QPDF_DLL + double qpdf_oh_get_numeric_value(qpdf_data data, qpdf_oh oh); + + QPDF_DLL + char const* qpdf_oh_get_name(qpdf_data data, qpdf_oh oh); + + QPDF_DLL + char const* qpdf_oh_get_string_value(qpdf_data data, qpdf_oh oh); + QPDF_DLL + char const* qpdf_oh_get_utf8_value(qpdf_data data, qpdf_oh oh); + + QPDF_DLL + int qpdf_oh_get_array_n_items(qpdf_data data, qpdf_oh oh); + QPDF_DLL + qpdf_oh qpdf_oh_get_array_item(qpdf_data data, qpdf_oh oh, int n); + + /* "C"-specific dictionary key iteration */ + + /* Iteration is allowed on only one dictionary at a time. */ + QPDF_DLL + void qpdf_oh_begin_dict_key_iter(qpdf_data data, qpdf_oh dict); + QPDF_DLL + QPDF_BOOL qpdf_oh_dict_more_keys(qpdf_data data); + /* The memory returned by qpdf_oh_dict_next_key is owned by + * qpdf_data. It is good until the next call to + * qpdf_oh_dict_next_key with the same qpdf_data object. Calling + * the method again, even with a different dict, invalidates + * previous return values. + */ + QPDF_DLL + char const* qpdf_oh_dict_next_key(qpdf_data data); + + /* end "C"-specific dictionary key iteration */ + + QPDF_DLL + QPDF_BOOL qpdf_oh_has_key(qpdf_data data, qpdf_oh oh, char const* key); + QPDF_DLL + qpdf_oh qpdf_oh_get_key(qpdf_data data, qpdf_oh oh, char const* key); + + QPDF_DLL + QPDF_BOOL qpdf_oh_is_or_has_name( + qpdf_data data, qpdf_oh oh, char const* key); + + QPDF_DLL + qpdf_oh qpdf_oh_new_null(qpdf_data data); + QPDF_DLL + qpdf_oh qpdf_oh_new_bool(qpdf_data data, QPDF_BOOL value); + QPDF_DLL + qpdf_oh qpdf_oh_new_integer(qpdf_data data, long long value); + QPDF_DLL + qpdf_oh qpdf_oh_new_real_from_string(qpdf_data data, char const* value); + QPDF_DLL + qpdf_oh qpdf_oh_new_real_from_double(qpdf_data data, + double value, int decimal_places); + QPDF_DLL + qpdf_oh qpdf_oh_new_name(qpdf_data data, char const* name); + QPDF_DLL + qpdf_oh qpdf_oh_new_string(qpdf_data data, char const* str); + QPDF_DLL + qpdf_oh qpdf_oh_new_unicode_string(qpdf_data data, char const* utf8_str); + QPDF_DLL + qpdf_oh qpdf_oh_new_array(qpdf_data data); + QPDF_DLL + qpdf_oh qpdf_oh_new_dictionary(qpdf_data data); + + QPDF_DLL + void qpdf_oh_make_direct(qpdf_data data, qpdf_oh oh); + + QPDF_DLL + void qpdf_oh_set_array_item(qpdf_data data, qpdf_oh oh, + int at, qpdf_oh item); + QPDF_DLL + void qpdf_oh_insert_item(qpdf_data data, qpdf_oh oh, int at, qpdf_oh item); + QPDF_DLL + void qpdf_oh_append_item(qpdf_data data, qpdf_oh oh, qpdf_oh item); + QPDF_DLL + void qpdf_oh_erase_item(qpdf_data data, qpdf_oh oh, int at); + + QPDF_DLL + void qpdf_oh_replace_key(qpdf_data data, qpdf_oh oh, + char const* key, qpdf_oh item); + QPDF_DLL + void qpdf_oh_remove_key(qpdf_data data, qpdf_oh oh, char const* key); + QPDF_DLL + void qpdf_oh_replace_or_remove_key(qpdf_data data, qpdf_oh oh, + char const* key, qpdf_oh item); + + QPDF_DLL + qpdf_oh qpdf_oh_get_dict(qpdf_data data, qpdf_oh oh); + + QPDF_DLL + int qpdf_oh_get_object_id(qpdf_data data, qpdf_oh oh); + QPDF_DLL + int qpdf_oh_get_generation(qpdf_data data, qpdf_oh oh); + + QPDF_DLL + char const* qpdf_oh_unparse(qpdf_data data, qpdf_oh oh); + QPDF_DLL + char const* qpdf_oh_unparse_resolved(qpdf_data data, qpdf_oh oh); + QPDF_DLL + char const* qpdf_oh_unparse_binary(qpdf_data data, qpdf_oh oh); #ifdef __cplusplus } #endif diff --git a/libqpdf/qpdf-c.cc b/libqpdf/qpdf-c.cc index 244bf663..d3ada5fd 100644 --- a/libqpdf/qpdf-c.cc +++ b/libqpdf/qpdf-c.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -37,10 +38,18 @@ struct _qpdf_data char const* password; bool write_memory; PointerHolder output_buffer; + + // QPDFObjectHandle support + std::map> oh_cache; + qpdf_oh next_oh; + std::set cur_iter_dict_keys; + std::set::const_iterator dict_iter; + std::string cur_dict_key; }; _qpdf_data::_qpdf_data() : - write_memory(false) + write_memory(false), + next_oh(0) { } @@ -134,6 +143,7 @@ qpdf_data qpdf_init() void qpdf_cleanup(qpdf_data* qpdf) { QTC::TC("qpdf", "qpdf-c called qpdf_cleanup"); + qpdf_oh_release_all(*qpdf); delete *qpdf; *qpdf = 0; } @@ -749,3 +759,592 @@ QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf) QTC::TC("qpdf", "qpdf-c called qpdf_write", (status == 0) ? 0 : 1); return status; } + +static qpdf_oh +new_object(qpdf_data qpdf, QPDFObjectHandle const& qoh) +{ + qpdf_oh oh = ++qpdf->next_oh; // never return 0 + qpdf->oh_cache[oh] = new QPDFObjectHandle(qoh); + return oh; +} + +void qpdf_oh_release(qpdf_data qpdf, qpdf_oh oh) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_release"); + qpdf->oh_cache.erase(oh); +} + +void qpdf_oh_release_all(qpdf_data qpdf) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_release_all"); + qpdf->oh_cache.clear(); +} + +qpdf_oh qpdf_get_trailer(qpdf_data qpdf) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_get_trailer"); + return new_object(qpdf, qpdf->qpdf->getTrailer()); +} + +qpdf_oh qpdf_get_root(qpdf_data qpdf) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_get_root"); + return new_object(qpdf, qpdf->qpdf->getRoot()); +} + +static bool +qpdf_oh_valid_internal(qpdf_data qpdf, qpdf_oh oh) +{ + auto i = qpdf->oh_cache.find(oh); + bool result = ((i != qpdf->oh_cache.end()) && + (i->second).getPointer() && + (i->second)->isInitialized()); + if (! result) + { + QTC::TC("qpdf", "qpdf-c invalid object handle"); + qpdf->warnings.push_back( + QPDFExc( + qpdf_e_damaged_pdf, + qpdf->qpdf->getFilename(), + std::string("C API object handle ") + + QUtil::uint_to_string(oh), + 0, "attempted access to unknown/uninitialized object handle")); + } + return result; +} + +QPDF_BOOL qpdf_oh_is_bool(qpdf_data qpdf, qpdf_oh oh) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_bool"); + return (qpdf_oh_valid_internal(qpdf, oh) && + qpdf->oh_cache[oh]->isBool()); +} + +QPDF_BOOL qpdf_oh_is_null(qpdf_data qpdf, qpdf_oh oh) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_null"); + return (qpdf_oh_valid_internal(qpdf, oh) && + qpdf->oh_cache[oh]->isNull()); +} + +QPDF_BOOL qpdf_oh_is_integer(qpdf_data qpdf, qpdf_oh oh) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_integer"); + return (qpdf_oh_valid_internal(qpdf, oh) && + qpdf->oh_cache[oh]->isInteger()); +} + +QPDF_BOOL qpdf_oh_is_real(qpdf_data qpdf, qpdf_oh oh) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_real"); + return (qpdf_oh_valid_internal(qpdf, oh) && + qpdf->oh_cache[oh]->isReal()); +} + +QPDF_BOOL qpdf_oh_is_name(qpdf_data qpdf, qpdf_oh oh) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_name"); + return (qpdf_oh_valid_internal(qpdf, oh) && + qpdf->oh_cache[oh]->isName()); +} + +QPDF_BOOL qpdf_oh_is_string(qpdf_data qpdf, qpdf_oh oh) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_string"); + return (qpdf_oh_valid_internal(qpdf, oh) && + qpdf->oh_cache[oh]->isString()); +} + +QPDF_BOOL qpdf_oh_is_operator(qpdf_data qpdf, qpdf_oh oh) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_operator"); + return (qpdf_oh_valid_internal(qpdf, oh) && + qpdf->oh_cache[oh]->isOperator()); +} + +QPDF_BOOL qpdf_oh_is_inline_image(qpdf_data qpdf, qpdf_oh oh) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_inline_image"); + return (qpdf_oh_valid_internal(qpdf, oh) && + qpdf->oh_cache[oh]->isInlineImage()); +} + +QPDF_BOOL qpdf_oh_is_array(qpdf_data qpdf, qpdf_oh oh) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_array"); + return (qpdf_oh_valid_internal(qpdf, oh) && + qpdf->oh_cache[oh]->isArray()); +} + +QPDF_BOOL qpdf_oh_is_dictionary(qpdf_data qpdf, qpdf_oh oh) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_dictionary"); + return (qpdf_oh_valid_internal(qpdf, oh) && + qpdf->oh_cache[oh]->isDictionary()); +} + +QPDF_BOOL qpdf_oh_is_stream(qpdf_data qpdf, qpdf_oh oh) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_stream"); + return (qpdf_oh_valid_internal(qpdf, oh) && + qpdf->oh_cache[oh]->isStream()); +} + +QPDF_BOOL qpdf_oh_is_indirect(qpdf_data qpdf, qpdf_oh oh) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_indirect"); + return (qpdf_oh_valid_internal(qpdf, oh) && + qpdf->oh_cache[oh]->isIndirect()); +} + +QPDF_BOOL qpdf_oh_is_scalar(qpdf_data qpdf, qpdf_oh oh) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_scalar"); + return (qpdf_oh_valid_internal(qpdf, oh) && + qpdf->oh_cache[oh]->isScalar()); +} + +qpdf_oh qpdf_oh_wrap_in_array(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return qpdf_oh_new_array(qpdf); + } + auto qoh = qpdf->oh_cache[oh]; + if (qoh->isArray()) + { + QTC::TC("qpdf", "qpdf-c array to wrap_in_array"); + return oh; + } + else + { + QTC::TC("qpdf", "qpdf-c non-array to wrap_in_array"); + return new_object(qpdf, + QPDFObjectHandle::newArray( + std::vector{ + *qpdf->oh_cache[oh]})); + } +} + +qpdf_oh qpdf_oh_parse(qpdf_data qpdf, char const* object_str) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_parse"); + return new_object(qpdf, QPDFObjectHandle::parse(object_str)); +} + +QPDF_BOOL qpdf_oh_get_bool_value(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return QPDF_FALSE; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_bool_value"); + return qpdf->oh_cache[oh]->getBoolValue(); +} + +long long qpdf_oh_get_int_value(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return 0LL; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_int_value"); + return qpdf->oh_cache[oh]->getIntValue(); +} + +int qpdf_oh_get_int_value_as_int(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return 0; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_int_value_as_int"); + return qpdf->oh_cache[oh]->getIntValueAsInt(); +} + +unsigned long long qpdf_oh_get_uint_value(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return 0ULL; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_uint_value"); + return qpdf->oh_cache[oh]->getUIntValue(); +} + +unsigned int qpdf_oh_get_uint_value_as_uint(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return 0U; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_uint_value_as_uint"); + return qpdf->oh_cache[oh]->getUIntValueAsUInt(); +} + +char const* qpdf_oh_get_real_value(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return ""; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_real_value"); + qpdf->tmp_string = qpdf->oh_cache[oh]->getRealValue(); + return qpdf->tmp_string.c_str(); +} + +QPDF_BOOL qpdf_oh_is_number(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return QPDF_FALSE; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_number"); + return qpdf->oh_cache[oh]->isNumber(); +} + +double qpdf_oh_get_numeric_value(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return 0.0; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_numeric_value"); + return qpdf->oh_cache[oh]->getNumericValue(); +} + +char const* qpdf_oh_get_name(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return ""; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_name"); + qpdf->tmp_string = qpdf->oh_cache[oh]->getName(); + return qpdf->tmp_string.c_str(); +} + +char const* qpdf_oh_get_string_value(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return ""; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_string_value"); + qpdf->tmp_string = qpdf->oh_cache[oh]->getStringValue(); + return qpdf->tmp_string.c_str(); +} + +char const* qpdf_oh_get_utf8_value(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return ""; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_utf8_value"); + qpdf->tmp_string = qpdf->oh_cache[oh]->getUTF8Value(); + return qpdf->tmp_string.c_str(); +} + +int qpdf_oh_get_array_n_items(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return 0; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_array_n_items"); + return qpdf->oh_cache[oh]->getArrayNItems(); +} + +qpdf_oh qpdf_oh_get_array_item(qpdf_data qpdf, qpdf_oh oh, int n) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return qpdf_oh_new_null(qpdf); + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_array_item"); + return new_object(qpdf, qpdf->oh_cache[oh]->getArrayItem(n)); +} + +void qpdf_oh_begin_dict_key_iter(qpdf_data qpdf, qpdf_oh oh) +{ + if (qpdf_oh_valid_internal(qpdf, oh) && + qpdf_oh_is_dictionary(qpdf, oh)) + { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_begin_dict_key_iter"); + qpdf->cur_iter_dict_keys = qpdf->oh_cache[oh]->getKeys(); + } + else + { + qpdf->cur_iter_dict_keys = {}; + } + qpdf->dict_iter = qpdf->cur_iter_dict_keys.begin(); +} + +QPDF_BOOL qpdf_oh_dict_more_keys(qpdf_data qpdf) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_dict_more_keys"); + return qpdf->dict_iter != qpdf->cur_iter_dict_keys.end(); +} + +char const* qpdf_oh_dict_next_key(qpdf_data qpdf) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_dict_next_key"); + if (qpdf_oh_dict_more_keys(qpdf)) + { + qpdf->cur_dict_key = *qpdf->dict_iter; + ++qpdf->dict_iter; + return qpdf->cur_dict_key.c_str(); + } + else + { + return nullptr; + } +} + +QPDF_BOOL qpdf_oh_has_key(qpdf_data qpdf, qpdf_oh oh, char const* key) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return QPDF_FALSE; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_has_key"); + return qpdf->oh_cache[oh]->hasKey(key); +} + +qpdf_oh qpdf_oh_get_key(qpdf_data qpdf, qpdf_oh oh, char const* key) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return qpdf_oh_new_null(qpdf); + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_key"); + return new_object(qpdf, qpdf->oh_cache[oh]->getKey(key)); +} + +QPDF_BOOL qpdf_oh_is_or_has_name(qpdf_data qpdf, qpdf_oh oh, char const* key) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return QPDF_FALSE; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_or_has_name"); + return qpdf->oh_cache[oh]->isOrHasName(key); +} + +qpdf_oh qpdf_oh_new_null(qpdf_data qpdf) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_new_null"); + return new_object(qpdf, QPDFObjectHandle::newNull()); +} + +qpdf_oh qpdf_oh_new_bool(qpdf_data qpdf, QPDF_BOOL value) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_new_bool"); + return new_object(qpdf, QPDFObjectHandle::newBool(value)); +} + +qpdf_oh qpdf_oh_new_integer(qpdf_data qpdf, long long value) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_new_integer"); + return new_object(qpdf, QPDFObjectHandle::newInteger(value)); +} + +qpdf_oh qpdf_oh_new_real_from_string(qpdf_data qpdf, char const* value) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_new_real_from_string"); + return new_object(qpdf, QPDFObjectHandle::newReal(value)); +} + +qpdf_oh qpdf_oh_new_real_from_double(qpdf_data qpdf, + double value, int decimal_places) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_new_real_from_double"); + return new_object(qpdf, QPDFObjectHandle::newReal(value, decimal_places)); +} + +qpdf_oh qpdf_oh_new_name(qpdf_data qpdf, char const* name) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_new_name"); + return new_object(qpdf, QPDFObjectHandle::newName(name)); +} + +qpdf_oh qpdf_oh_new_string(qpdf_data qpdf, char const* str) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_new_string"); + return new_object(qpdf, QPDFObjectHandle::newString(str)); +} + +qpdf_oh qpdf_oh_new_unicode_string(qpdf_data qpdf, char const* utf8_str) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_new_unicode_string"); + return new_object(qpdf, QPDFObjectHandle::newUnicodeString(utf8_str)); +} + +qpdf_oh qpdf_oh_new_array(qpdf_data qpdf) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_new_array"); + return new_object(qpdf, QPDFObjectHandle::newArray()); +} + +qpdf_oh qpdf_oh_new_dictionary(qpdf_data qpdf) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_new_dictionary"); + return new_object(qpdf, QPDFObjectHandle::newDictionary()); +} + +void qpdf_oh_make_direct(qpdf_data qpdf, qpdf_oh oh) +{ + if (qpdf_oh_valid_internal(qpdf, oh)) + { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_make_direct"); + qpdf->oh_cache[oh]->makeDirect(); + } +} + +static QPDFObjectHandle +qpdf_oh_item_internal(qpdf_data qpdf, qpdf_oh item) +{ + if (qpdf_oh_valid_internal(qpdf, item)) + { + return *(qpdf->oh_cache[item]); + } + else + { + return QPDFObjectHandle::newNull(); + } +} + +void qpdf_oh_set_array_item(qpdf_data qpdf, qpdf_oh oh, + int at, qpdf_oh item) +{ + if (qpdf_oh_is_array(qpdf, oh)) + { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_set_array_item"); + qpdf->oh_cache[oh]->setArrayItem( + at, qpdf_oh_item_internal(qpdf, item)); + } +} + +void qpdf_oh_insert_item(qpdf_data qpdf, qpdf_oh oh, int at, qpdf_oh item) +{ + if (qpdf_oh_is_array(qpdf, oh)) + { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_insert_item"); + qpdf->oh_cache[oh]->insertItem( + at, qpdf_oh_item_internal(qpdf, item)); + } +} + +void qpdf_oh_append_item(qpdf_data qpdf, qpdf_oh oh, qpdf_oh item) +{ + if (qpdf_oh_is_array(qpdf, oh)) + { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_append_item"); + qpdf->oh_cache[oh]->appendItem( + qpdf_oh_item_internal(qpdf, item)); + } +} + +void qpdf_oh_erase_item(qpdf_data qpdf, qpdf_oh oh, int at) +{ + if (qpdf_oh_is_array(qpdf, oh)) + { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_erase_item"); + qpdf->oh_cache[oh]->eraseItem(at); + } +} + +void qpdf_oh_replace_key(qpdf_data qpdf, qpdf_oh oh, + char const* key, qpdf_oh item) +{ + if (qpdf_oh_is_dictionary(qpdf, oh)) + { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_replace_key"); + qpdf->oh_cache[oh]->replaceKey( + key, qpdf_oh_item_internal(qpdf, item)); + } +} + +void qpdf_oh_remove_key(qpdf_data qpdf, qpdf_oh oh, char const* key) +{ + if (qpdf_oh_is_dictionary(qpdf, oh)) + { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_remove_key"); + qpdf->oh_cache[oh]->removeKey(key); + } +} + +void qpdf_oh_replace_or_remove_key(qpdf_data qpdf, qpdf_oh oh, + char const* key, qpdf_oh item) +{ + if (qpdf_oh_is_dictionary(qpdf, oh)) + { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_replace_or_remove_key"); + qpdf->oh_cache[oh]->replaceOrRemoveKey( + key, qpdf_oh_item_internal(qpdf, item)); + } +} + +qpdf_oh qpdf_oh_get_dict(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return qpdf_oh_new_null(qpdf); + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_dict"); + return new_object(qpdf, qpdf->oh_cache[oh]->getDict()); +} + +int qpdf_oh_get_object_id(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return 0; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_object_id"); + return qpdf->oh_cache[oh]->getObjectID(); +} + +int qpdf_oh_get_generation(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return 0; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_generation"); + return qpdf->oh_cache[oh]->getGeneration(); +} + +char const* qpdf_oh_unparse(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return ""; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse"); + qpdf->tmp_string = qpdf->oh_cache[oh]->unparse(); + return qpdf->tmp_string.c_str(); +} + +char const* qpdf_oh_unparse_resolved(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return ""; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse_resolved"); + qpdf->tmp_string = qpdf->oh_cache[oh]->unparseResolved(); + return qpdf->tmp_string.c_str(); +} + +char const* qpdf_oh_unparse_binary(qpdf_data qpdf, qpdf_oh oh) +{ + if (! qpdf_oh_valid_internal(qpdf, oh)) + { + return ""; + } + QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse_binary"); + qpdf->tmp_string = qpdf->oh_cache[oh]->unparseBinary(); + return qpdf->tmp_string.c_str(); +} diff --git a/qpdf/qpdf-ctest.c b/qpdf/qpdf-ctest.c index fad121af..361a2070 100644 --- a/qpdf/qpdf-ctest.c +++ b/qpdf/qpdf-ctest.c @@ -495,6 +495,177 @@ static void test23(char const* infile, report_errors(); } +static void test24(char const* infile, + char const* password, + char const* outfile, + char const* outfile2) +{ + /* This test case is designed for minimal.pdf. */ + qpdf_read(qpdf, infile, password); + qpdf_oh trailer = qpdf_get_trailer(qpdf); + /* The library never returns 0 */ + assert(trailer == 1); + qpdf_oh root = qpdf_get_root(qpdf); + assert(qpdf_oh_get_generation(qpdf, root) == 0); + qpdf_oh root_from_trailer = qpdf_oh_get_key(qpdf, trailer, "/Root"); + assert(qpdf_oh_get_object_id(qpdf, root) == + qpdf_oh_get_object_id(qpdf, root_from_trailer)); + qpdf_oh pages = qpdf_oh_get_key(qpdf, root, "/Pages"); + assert(qpdf_oh_is_dictionary(qpdf, pages)); + qpdf_oh kids = qpdf_oh_get_key(qpdf, pages, "/Kids"); + assert(qpdf_oh_is_array(qpdf, kids)); + assert(qpdf_oh_get_array_n_items(qpdf, kids) == 1); + qpdf_oh page1 = qpdf_oh_get_array_item(qpdf, kids, 0); + qpdf_oh_begin_dict_key_iter(qpdf, page1); + while (qpdf_oh_dict_more_keys(qpdf)) + { + printf("page dictionary key: %s\n", qpdf_oh_dict_next_key(qpdf)); + } + qpdf_oh type = qpdf_oh_get_key(qpdf, page1, "/Type"); + assert(qpdf_oh_is_name(qpdf, type)); + assert(strcmp(qpdf_oh_get_name(qpdf, type), "/Page") == 0); + qpdf_oh parent = qpdf_oh_get_key(qpdf, page1, "/Parent"); + assert(qpdf_oh_is_indirect(qpdf, parent)); + qpdf_oh mediabox = qpdf_oh_get_key(qpdf, page1, "/MediaBox"); + assert(! qpdf_oh_is_scalar(qpdf, mediabox)); + assert(qpdf_oh_is_array(qpdf, mediabox)); + assert(qpdf_oh_get_array_n_items(qpdf, mediabox) == 4); + assert(qpdf_oh_wrap_in_array(qpdf, mediabox) == mediabox); + for (int i = 0; i < 4; ++i) + { + qpdf_oh item = qpdf_oh_get_array_item(qpdf, mediabox, i); + printf("item %d: %d %.2f\n", + i, qpdf_oh_get_int_value_as_int(qpdf, item), + qpdf_oh_get_numeric_value(qpdf, item)); + qpdf_oh_release(qpdf, item); + } + qpdf_oh i2 = qpdf_oh_get_array_item(qpdf, mediabox, 2); + assert(qpdf_oh_get_int_value_as_int(qpdf, i2) == 612); + assert(qpdf_oh_get_int_value(qpdf, i2) == 612ll); + assert(qpdf_oh_get_uint_value_as_uint(qpdf, i2) == 612u); + assert(qpdf_oh_get_uint_value(qpdf, i2) == 612ull); + assert(! qpdf_oh_is_operator(qpdf, i2)); + assert(! qpdf_oh_is_inline_image(qpdf, i2)); + qpdf_oh encoding = qpdf_oh_get_key( + qpdf, qpdf_oh_get_key( + qpdf, qpdf_oh_get_key( + qpdf, qpdf_oh_get_key( + qpdf, page1, "/Resources"), + "/Font"), + "/F1"), + "/Encoding"); + assert(strcmp(qpdf_oh_get_name(qpdf, encoding), "/WinAnsiEncoding") == 0); + qpdf_oh contents = qpdf_oh_get_key(qpdf, page1, "/Contents"); + assert(qpdf_oh_is_stream(qpdf, contents)); + qpdf_oh contents_dict = qpdf_oh_get_dict(qpdf, contents); + assert(! qpdf_oh_is_scalar(qpdf, contents)); + assert(! qpdf_oh_is_scalar(qpdf, contents_dict)); + qpdf_oh contents_length = qpdf_oh_get_key(qpdf, contents_dict, "/Length"); + assert(qpdf_oh_is_integer(qpdf, contents_length)); + assert(qpdf_oh_is_scalar(qpdf, contents_length)); + assert(qpdf_oh_is_number(qpdf, contents_length)); + qpdf_oh contents_array = qpdf_oh_wrap_in_array(qpdf, contents); + assert(qpdf_oh_get_array_n_items(qpdf, contents_array) == 1); + assert(qpdf_oh_get_object_id( + qpdf, qpdf_oh_get_array_item(qpdf, contents_array, 0)) == + qpdf_oh_get_object_id(qpdf, contents)); + qpdf_oh resources = qpdf_oh_get_key(qpdf, page1, "/Resources"); + qpdf_oh procset = qpdf_oh_get_key(qpdf, resources, "/ProcSet"); + assert(strcmp(qpdf_oh_unparse(qpdf, procset), + "5 0 R") == 0); + assert(strcmp(qpdf_oh_unparse_resolved(qpdf, procset), + "[ /PDF /Text ]") == 0); + qpdf_oh_make_direct(qpdf, procset); + assert(strcmp(qpdf_oh_unparse(qpdf, procset), + "[ /PDF /Text ]") == 0); + qpdf_oh_replace_key(qpdf, resources, "/ProcSet", procset); + + qpdf_oh parsed = qpdf_oh_parse( + qpdf, "[ 1 2.0 (3\xf7) << /Four [/Five] >> null true ]"); + qpdf_oh p_int = qpdf_oh_get_array_item(qpdf, parsed, 0); + qpdf_oh p_real = qpdf_oh_get_array_item(qpdf, parsed, 1); + qpdf_oh p_string = qpdf_oh_get_array_item(qpdf, parsed, 2); + qpdf_oh p_dict = qpdf_oh_get_array_item(qpdf, parsed, 3); + qpdf_oh p_null = qpdf_oh_get_array_item(qpdf, parsed, 4); + qpdf_oh p_bool = qpdf_oh_get_array_item(qpdf, parsed, 5); + assert(qpdf_oh_is_integer(qpdf, p_int) && + qpdf_oh_get_int_value_as_int(qpdf, p_int) == 1); + assert(qpdf_oh_is_real(qpdf, p_real) && + (strcmp(qpdf_oh_get_real_value(qpdf, p_real), "2.0") == 0) && + qpdf_oh_get_numeric_value(qpdf, p_real) == 2.0); + assert(qpdf_oh_is_string(qpdf, p_string) && + (strcmp(qpdf_oh_get_string_value(qpdf, p_string), "3\xf7") == 0) && + (strcmp(qpdf_oh_get_utf8_value(qpdf, p_string), "3\xc3\xb7") == 0) && + (strcmp(qpdf_oh_unparse_binary(qpdf, p_string), "<33f7>") == 0)); + assert(qpdf_oh_is_dictionary(qpdf, p_dict)); + qpdf_oh p_five = qpdf_oh_get_key(qpdf, p_dict, "/Four"); + assert(qpdf_oh_is_or_has_name(qpdf, p_five, "/Five")); + assert(qpdf_oh_is_or_has_name( + qpdf, qpdf_oh_get_array_item(qpdf, p_five, 0), "/Five")); + assert(qpdf_oh_is_null(qpdf, p_null)); + assert(qpdf_oh_is_bool(qpdf, p_bool) && + (qpdf_oh_get_bool_value(qpdf, p_bool) == QPDF_TRUE)); + qpdf_oh_erase_item(qpdf, parsed, 4); + qpdf_oh_insert_item( + qpdf, parsed, 2, + qpdf_oh_parse(qpdf, "<>")); + qpdf_oh new_dict = qpdf_oh_get_array_item(qpdf, parsed, 2); + assert(qpdf_oh_has_key(qpdf, new_dict, "/A")); + assert(qpdf_oh_has_key(qpdf, new_dict, "/D")); + qpdf_oh new_array = qpdf_oh_new_array(qpdf); + qpdf_oh_replace_or_remove_key( + qpdf, new_dict, "/A", qpdf_oh_new_null(qpdf)); + qpdf_oh_replace_or_remove_key( + qpdf, new_dict, "/B", new_array); + qpdf_oh_replace_key( + qpdf, new_dict, "/C", qpdf_oh_new_dictionary(qpdf)); + qpdf_oh_remove_key(qpdf, new_dict, "/D"); + assert(! qpdf_oh_has_key(qpdf, new_dict, "/A")); + assert(! qpdf_oh_has_key(qpdf, new_dict, "/D")); + qpdf_oh_append_item( + qpdf, new_array, qpdf_oh_new_string(qpdf, "potato")); + qpdf_oh_append_item( + qpdf, new_array, + qpdf_oh_new_unicode_string(qpdf, "qww\xc3\xb7\xcf\x80")); + qpdf_oh_append_item(qpdf, new_array, qpdf_oh_new_null(qpdf)); /* 2 */ + qpdf_oh_append_item(qpdf, new_array, qpdf_oh_new_null(qpdf)); /* 3 */ + qpdf_oh_set_array_item( + qpdf, new_array, 2, + qpdf_oh_new_name(qpdf, "/Quack")); + qpdf_oh_append_item( + qpdf, new_array, + qpdf_oh_new_real_from_double(qpdf, 4.0, 2)); + qpdf_oh_append_item( + qpdf, new_array, + qpdf_oh_new_real_from_string(qpdf, "5.0")); + qpdf_oh_append_item( + qpdf, new_array, + qpdf_oh_new_integer(qpdf, 6)); + qpdf_oh_append_item( + qpdf, new_array, qpdf_oh_new_bool(qpdf, QPDF_TRUE)); + qpdf_oh_replace_key(qpdf, root, "/QTest", new_dict); + + /* Release and access to exercise warnings */ + qpdf_oh_release(qpdf, page1); + contents = qpdf_oh_get_key(qpdf, page1, "/Contents"); + assert(qpdf_oh_is_null(qpdf, contents)); + assert(qpdf_oh_is_array(qpdf, mediabox)); + qpdf_oh_release_all(qpdf); + assert(! qpdf_oh_is_null(qpdf, mediabox)); + assert(! qpdf_oh_is_array(qpdf, mediabox)); + /* Make sure something is assigned when we exit so we check that + * it gets properl freed. + */ + qpdf_get_root(qpdf); + + 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; @@ -558,6 +729,7 @@ int main(int argc, char* argv[]) (n == 21) ? test21 : (n == 22) ? test22 : (n == 23) ? test23 : + (n == 24) ? test24 : 0); if (fn == 0) diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index 7a2da359..b109a162 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -457,3 +457,67 @@ QPDF copy foreign with data 1 QPDF copy foreign with foreign_stream 1 QPDFObjectHandle need_newline 1 qpdf pages range omitted with . 0 +qpdf-c invalid object handle 0 +qpdf-c called qpdf_oh_release 0 +qpdf-c called qpdf_oh_release_all 0 +qpdf-c called qpdf_get_trailer 0 +qpdf-c called qpdf_get_root 0 +qpdf-c called qpdf_oh_is_bool 0 +qpdf-c called qpdf_oh_is_null 0 +qpdf-c called qpdf_oh_is_integer 0 +qpdf-c called qpdf_oh_is_real 0 +qpdf-c called qpdf_oh_is_name 0 +qpdf-c called qpdf_oh_is_string 0 +qpdf-c called qpdf_oh_is_operator 0 +qpdf-c called qpdf_oh_is_inline_image 0 +qpdf-c called qpdf_oh_is_array 0 +qpdf-c called qpdf_oh_is_dictionary 0 +qpdf-c called qpdf_oh_is_stream 0 +qpdf-c called qpdf_oh_is_indirect 0 +qpdf-c called qpdf_oh_is_scalar 0 +qpdf-c array to wrap_in_array 0 +qpdf-c non-array to wrap_in_array 0 +qpdf-c called qpdf_oh_parse 0 +qpdf-c called qpdf_oh_get_bool_value 0 +qpdf-c called qpdf_oh_get_int_value 0 +qpdf-c called qpdf_oh_get_int_value_as_int 0 +qpdf-c called qpdf_oh_get_uint_value 0 +qpdf-c called qpdf_oh_get_uint_value_as_uint 0 +qpdf-c called qpdf_oh_get_real_value 0 +qpdf-c called qpdf_oh_is_number 0 +qpdf-c called qpdf_oh_get_numeric_value 0 +qpdf-c called qpdf_oh_get_name 0 +qpdf-c called qpdf_oh_get_string_value 0 +qpdf-c called qpdf_oh_get_utf8_value 0 +qpdf-c called qpdf_oh_get_array_n_items 0 +qpdf-c called qpdf_oh_get_array_item 0 +qpdf-c called qpdf_oh_begin_dict_key_iter 0 +qpdf-c called qpdf_oh_dict_more_keys 0 +qpdf-c called qpdf_oh_dict_next_key 0 +qpdf-c called qpdf_oh_has_key 0 +qpdf-c called qpdf_oh_get_key 0 +qpdf-c called qpdf_oh_is_or_has_name 0 +qpdf-c called qpdf_oh_new_null 0 +qpdf-c called qpdf_oh_new_bool 0 +qpdf-c called qpdf_oh_new_integer 0 +qpdf-c called qpdf_oh_new_real_from_string 0 +qpdf-c called qpdf_oh_new_real_from_double 0 +qpdf-c called qpdf_oh_new_name 0 +qpdf-c called qpdf_oh_new_string 0 +qpdf-c called qpdf_oh_new_unicode_string 0 +qpdf-c called qpdf_oh_new_array 0 +qpdf-c called qpdf_oh_new_dictionary 0 +qpdf-c called qpdf_oh_make_direct 0 +qpdf-c called qpdf_oh_set_array_item 0 +qpdf-c called qpdf_oh_insert_item 0 +qpdf-c called qpdf_oh_append_item 0 +qpdf-c called qpdf_oh_erase_item 0 +qpdf-c called qpdf_oh_replace_key 0 +qpdf-c called qpdf_oh_remove_key 0 +qpdf-c called qpdf_oh_replace_or_remove_key 0 +qpdf-c called qpdf_oh_get_dict 0 +qpdf-c called qpdf_oh_get_object_id 0 +qpdf-c called qpdf_oh_get_generation 0 +qpdf-c called qpdf_oh_unparse 0 +qpdf-c called qpdf_oh_unparse_resolved 0 +qpdf-c called qpdf_oh_unparse_binary 0 diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 58f646f4..a786fa33 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -4118,6 +4118,21 @@ foreach my $i (@c_check_types) $td->NORMALIZE_NEWLINES); } +show_ntests(); +# ---------- +$td->notify("--- C API Object Handle ---"); +$n_tests += scalar(@c_check_types); + +$td->runtest("C check object handles", + {$td->COMMAND => "qpdf-ctest 24 minimal.pdf '' a.pdf"}, + {$td->FILE => "c-object-handles.out", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + +$td->runtest("check output", + {$td->FILE => 'a.pdf'}, + {$td->FILE => 'c-object-handles-out.pdf'}); + show_ntests(); # ---------- $td->notify("--- Content Preservation Tests ---"); diff --git a/qpdf/qtest/qpdf/c-object-handles-out.pdf b/qpdf/qtest/qpdf/c-object-handles-out.pdf new file mode 100644 index 00000000..596618b4 --- /dev/null +++ b/qpdf/qtest/qpdf/c-object-handles-out.pdf @@ -0,0 +1,104 @@ +%PDF-1.3 +%¿÷¢þ +%QDF-1.0 + +1 0 obj +<< + /Pages 2 0 R + /QTest << + /B [ + (potato) + + /Quack + null + 4.00 + 5.0 + 6 + true + ] + /C << + >> + >> + /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 [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 6 0 R + >> + /ProcSet [ + /PDF + /Text + ] + >> + /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 +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +xref +0 7 +0000000000 65535 f +0000000025 00000 n +0000000240 00000 n +0000000322 00000 n +0000000562 00000 n +0000000661 00000 n +0000000680 00000 n +trailer << + /Root 1 0 R + /Size 7 + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] +>> +startxref +798 +%%EOF diff --git a/qpdf/qtest/qpdf/c-object-handles.out b/qpdf/qtest/qpdf/c-object-handles.out new file mode 100644 index 00000000..7fef9dad --- /dev/null +++ b/qpdf/qtest/qpdf/c-object-handles.out @@ -0,0 +1,24 @@ +page dictionary key: /Contents +page dictionary key: /MediaBox +page dictionary key: /Parent +page dictionary key: /Resources +page dictionary key: /Type +item 0: 0 0.00 +item 1: 0 0.00 +item 2: 612 612.00 +item 3: 792 792.00 +warning: minimal.pdf (C API object handle 6): attempted access to unknown/uninitialized object handle + code: 5 + file: minimal.pdf + pos : 0 + text: attempted access to unknown/uninitialized object handle +warning: minimal.pdf (C API object handle 9): attempted access to unknown/uninitialized object handle + code: 5 + file: minimal.pdf + pos : 0 + text: attempted access to unknown/uninitialized object handle +warning: minimal.pdf (C API object handle 9): attempted access to unknown/uninitialized object handle + code: 5 + file: minimal.pdf + pos : 0 + text: attempted access to unknown/uninitialized object handle