aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--examples/build.mk4
-rw-r--r--examples/pdf-c-objects.c114
-rw-r--r--examples/qtest/c-objects.test33
-rw-r--r--examples/qtest/c-objects/1-out.pdfbin0 -> 12315 bytes
-rw-r--r--examples/qtest/c-objects/1.pdf1501
-rw-r--r--examples/qtest/c-objects/2-out.pdfbin0 -> 799 bytes
-rw-r--r--examples/qtest/c-objects/2.pdf97
-rw-r--r--include/qpdf/qpdf-c.h224
-rw-r--r--libqpdf/qpdf-c.cc601
-rw-r--r--qpdf/qpdf-ctest.c172
-rw-r--r--qpdf/qpdf.testcov64
-rw-r--r--qpdf/qtest/qpdf.test15
-rw-r--r--qpdf/qtest/qpdf/c-object-handles-out.pdf104
-rw-r--r--qpdf/qtest/qpdf/c-object-handles.out24
15 files changed, 2959 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index 696e43a6..ae5dd5c3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2020-11-28 Jay Berkenbilt <ejb@ql.org>
+
+ * 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 <ejb@ql.org>
* 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 <qpdf/qpdf-c.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+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 00000000..bc162603
--- /dev/null
+++ b/examples/qtest/c-objects/1-out.pdf
Binary files differ
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 00000000..e3f9f34a
--- /dev/null
+++ b/examples/qtest/c-objects/2-out.pdf
Binary files differ
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 [<ed2874a4596cc552b835f7f40431a8f5><ed2874a4596cc552b835f7f40431a8f5>]
+>>
+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 <qpdf/QPDFExc.hh>
#include <qpdf/Pl_Discard.hh>
#include <qpdf/QIntC.hh>
+#include <qpdf/QUtil.hh>
#include <list>
#include <string>
@@ -37,10 +38,18 @@ struct _qpdf_data
char const* password;
bool write_memory;
PointerHolder<Buffer> output_buffer;
+
+ // QPDFObjectHandle support
+ std::map<qpdf_oh, PointerHolder<QPDFObjectHandle>> oh_cache;
+ qpdf_oh next_oh;
+ std::set<std::string> cur_iter_dict_keys;
+ std::set<std::string>::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<QPDFObjectHandle>{
+ *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, "<</A 1 /B 2 /C 3 /D 4>>"));
+ 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
@@ -4120,6 +4120,21 @@ foreach my $i (@c_check_types)
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 ---");
# $n_tests incremented below
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)
+ <feff00710077007700f703c0>
+ /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