summaryrefslogtreecommitdiffstats
path: root/qpdf
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2012-06-24 21:26:28 +0200
committerJay Berkenbilt <ejb@ql.org>2012-06-24 21:56:50 +0200
commit8318d81ada86d4ec8e343c47103932b6bbe45a42 (patch)
treea1ff22dc1584d84829d32c7b1d8698a332877763 /qpdf
parent781c313058e26b6ab6fda060a652a395d27cdb7a (diff)
downloadqpdf-8318d81ada86d4ec8e343c47103932b6bbe45a42.tar.zst
Fix and test support for files >= 4 GB
Diffstat (limited to 'qpdf')
-rw-r--r--qpdf/build.mk2
-rw-r--r--qpdf/qpdf-ctest.c8
-rw-r--r--qpdf/qtest/qpdf.test107
-rw-r--r--qpdf/qtest/qpdf/large_file-check-linearized.out5
-rw-r--r--qpdf/qtest/qpdf/large_file-check-normal.out5
-rw-r--r--qpdf/qtest/qpdf/large_file-check-ostream-linearized.out5
-rw-r--r--qpdf/qtest/qpdf/large_file-check-ostream.out5
-rw-r--r--qpdf/qtest/qpdf/large_file.out200
-rw-r--r--qpdf/qtest/qpdf/large_file_xref_reconstruct.out203
-rw-r--r--qpdf/test_large_file.cc368
10 files changed, 905 insertions, 3 deletions
diff --git a/qpdf/build.mk b/qpdf/build.mk
index dfe169ab..e3d50976 100644
--- a/qpdf/build.mk
+++ b/qpdf/build.mk
@@ -1,4 +1,4 @@
-BINS_qpdf = qpdf test_driver pdf_from_scratch
+BINS_qpdf = qpdf test_driver pdf_from_scratch test_large_file
CBINS_qpdf = qpdf-ctest
TARGETS_qpdf = $(foreach B,$(BINS_qpdf) $(CBINS_qpdf),qpdf/$(OUTPUT_DIR)/$(call binname,$(B)))
diff --git a/qpdf/qpdf-ctest.c b/qpdf/qpdf-ctest.c
index ffb1fff8..0b6c5f1f 100644
--- a/qpdf/qpdf-ctest.c
+++ b/qpdf/qpdf-ctest.c
@@ -17,7 +17,10 @@ static void report_errors()
printf("warning: %s\n", qpdf_get_error_full_text(qpdf, e));
printf(" code: %d\n", qpdf_get_error_code(qpdf, e));
printf(" file: %s\n", qpdf_get_error_filename(qpdf, e));
- printf(" pos : %ld\n", qpdf_get_error_file_position(qpdf, e));
+ /* If your compiler doesn't support %lld, change to %ld and
+ * lose precision in the error message.
+ */
+ printf(" pos : %lld\n", qpdf_get_error_file_position(qpdf, e));
printf(" text: %s\n", qpdf_get_error_message_detail(qpdf, e));
}
if (qpdf_has_error(qpdf))
@@ -27,7 +30,8 @@ static void report_errors()
printf("error: %s\n", qpdf_get_error_full_text(qpdf, e));
printf(" code: %d\n", qpdf_get_error_code(qpdf, e));
printf(" file: %s\n", qpdf_get_error_filename(qpdf, e));
- printf(" pos : %ld\n", qpdf_get_error_file_position(qpdf, e));
+ /* see above comment about %lld */
+ printf(" pos : %lld\n", qpdf_get_error_file_position(qpdf, e));
printf(" text: %s\n", qpdf_get_error_message_detail(qpdf, e));
}
else
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index 94252db4..fa26597d 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -21,6 +21,7 @@ if ((exists $ENV{'SKIP_TEST_COMPARE_IMAGES'}) &&
{
$compare_images = 0;
}
+my $large_file_test_path = $ENV{'LARGE_FILE_TEST_PATH'} || undef;
my $have_acroread = 0;
@@ -1447,8 +1448,114 @@ for (my $n = 1; $n <= 2; ++$n)
}
show_ntests();
+# ----------
+$td->notify("--- Large File Tests ---");
+my $nlarge = 1;
+if (defined $large_file_test_path)
+{
+ $nlarge = 2;
+}
+else
+{
+ $td->notify("--- Skipping tests on actual large files ---");
+}
+$n_tests += $nlarge * 13;
+for (my $large = 0; $large < $nlarge; ++$large)
+{
+ if ($large)
+ {
+ $td->notify("--- Running tests on actual large files ---");
+ }
+ else
+ {
+ $td->notify("--- Running large file tests on small files ---");
+ }
+ my $size = ($large ? "large" : "small");
+ my $file = $large ? "$large_file_test_path/a.pdf" : "a.pdf";
+ $td->runtest("write test file",
+ {$td->COMMAND => "test_large_file write $size $file"},
+ {$td->FILE => "large_file.out", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+ $td->runtest("read test file",
+ {$td->COMMAND => "test_large_file read $size $file"},
+ {$td->FILE => "large_file.out", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+ $td->runtest("check",
+ {$td->COMMAND => "qpdf --suppress-recovery --check $file",
+ $td->FILTER => "grep -v checking"},
+ {$td->FILE => "large_file-check-normal.out",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
+ for my $ostream (0, 1)
+ {
+ for my $linearize (0, 1)
+ {
+ if (($ostream == 0) && ($linearize == 0))
+ {
+ # Original file has no object streams and is not linearized.
+ next;
+ }
+ my $args = "";
+ my $omode = $ostream ? "generate" : "disable";
+ my $lin = $linearize ? "--linearize" : "";
+ my $newfile = "$file-new";
+
+ $td->runtest("transform: ostream=$ostream, linearize=$linearize",
+ {$td->COMMAND =>
+ "qpdf --stream-data=preserve" .
+ " --object-streams=$omode" .
+ " $lin $file $newfile"},
+ {$td->STRING => "", $td->EXIT_STATUS => 0});
+ $td->runtest("read test file",
+ {$td->COMMAND =>
+ "test_large_file read $size $newfile"},
+ {$td->FILE => "large_file.out", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+ my $check_out =
+ ($linearize
+ ? ($ostream
+ ? "large_file-check-ostream-linearized.out"
+ : "large_file-check-linearized.out")
+ : ($ostream
+ ? "large_file-check-ostream.out"
+ : "large_file-check-normal.out"));
+ $td->runtest("check: ostream=$ostream, linearize=$linearize",
+ {$td->COMMAND =>
+ "qpdf --suppress-recovery --check $newfile",
+ $td->FILTER => "grep -v checking"},
+ {$td->FILE => $check_out, $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+ unlink $newfile;
+ }
+ }
+
+ # Clobber xref
+ open(F, "+<$file") or die;
+ seek(F, -50, 2);
+ my $pos = tell F;
+ my $buf;
+ read(F, $buf, 50);
+ die unless $buf =~ m/^(.*startxref\n)\d+/s;
+ $pos += length($1);
+ seek(F, $pos, 0) or die;
+ print F "oops" or die;
+ close(F);
+ my $cmd = +{$td->COMMAND => "test_large_file read $size $file"};
+ if ($large)
+ {
+ $cmd->{$td->FILTER} = "sed -e s,$large_file_test_path/,,";
+ }
+ $td->runtest("reconstruct xref table",
+ $cmd,
+ {$td->FILE => "large_file_xref_reconstruct.out",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+ unlink $file;
+}
# ----------
+
cleanup();
# See comments at beginning about calculation of number of tests. We
diff --git a/qpdf/qtest/qpdf/large_file-check-linearized.out b/qpdf/qtest/qpdf/large_file-check-linearized.out
new file mode 100644
index 00000000..6d3407bd
--- /dev/null
+++ b/qpdf/qtest/qpdf/large_file-check-linearized.out
@@ -0,0 +1,5 @@
+PDF Version: 1.3
+File is not encrypted
+File is linearized
+No syntax or stream encoding errors found; the file may still contain
+errors that qpdf cannot detect
diff --git a/qpdf/qtest/qpdf/large_file-check-normal.out b/qpdf/qtest/qpdf/large_file-check-normal.out
new file mode 100644
index 00000000..c5cc5b5f
--- /dev/null
+++ b/qpdf/qtest/qpdf/large_file-check-normal.out
@@ -0,0 +1,5 @@
+PDF Version: 1.3
+File is not encrypted
+File is not linearized
+No syntax or stream encoding errors found; the file may still contain
+errors that qpdf cannot detect
diff --git a/qpdf/qtest/qpdf/large_file-check-ostream-linearized.out b/qpdf/qtest/qpdf/large_file-check-ostream-linearized.out
new file mode 100644
index 00000000..079bb65d
--- /dev/null
+++ b/qpdf/qtest/qpdf/large_file-check-ostream-linearized.out
@@ -0,0 +1,5 @@
+PDF Version: 1.5
+File is not encrypted
+File is linearized
+No syntax or stream encoding errors found; the file may still contain
+errors that qpdf cannot detect
diff --git a/qpdf/qtest/qpdf/large_file-check-ostream.out b/qpdf/qtest/qpdf/large_file-check-ostream.out
new file mode 100644
index 00000000..8ea0e86f
--- /dev/null
+++ b/qpdf/qtest/qpdf/large_file-check-ostream.out
@@ -0,0 +1,5 @@
+PDF Version: 1.5
+File is not encrypted
+File is not linearized
+No syntax or stream encoding errors found; the file may still contain
+errors that qpdf cannot detect
diff --git a/qpdf/qtest/qpdf/large_file.out b/qpdf/qtest/qpdf/large_file.out
new file mode 100644
index 00000000..b1990d25
--- /dev/null
+++ b/qpdf/qtest/qpdf/large_file.out
@@ -0,0 +1,200 @@
+page 1 of 200
+page 2 of 200
+page 3 of 200
+page 4 of 200
+page 5 of 200
+page 6 of 200
+page 7 of 200
+page 8 of 200
+page 9 of 200
+page 10 of 200
+page 11 of 200
+page 12 of 200
+page 13 of 200
+page 14 of 200
+page 15 of 200
+page 16 of 200
+page 17 of 200
+page 18 of 200
+page 19 of 200
+page 20 of 200
+page 21 of 200
+page 22 of 200
+page 23 of 200
+page 24 of 200
+page 25 of 200
+page 26 of 200
+page 27 of 200
+page 28 of 200
+page 29 of 200
+page 30 of 200
+page 31 of 200
+page 32 of 200
+page 33 of 200
+page 34 of 200
+page 35 of 200
+page 36 of 200
+page 37 of 200
+page 38 of 200
+page 39 of 200
+page 40 of 200
+page 41 of 200
+page 42 of 200
+page 43 of 200
+page 44 of 200
+page 45 of 200
+page 46 of 200
+page 47 of 200
+page 48 of 200
+page 49 of 200
+page 50 of 200
+page 51 of 200
+page 52 of 200
+page 53 of 200
+page 54 of 200
+page 55 of 200
+page 56 of 200
+page 57 of 200
+page 58 of 200
+page 59 of 200
+page 60 of 200
+page 61 of 200
+page 62 of 200
+page 63 of 200
+page 64 of 200
+page 65 of 200
+page 66 of 200
+page 67 of 200
+page 68 of 200
+page 69 of 200
+page 70 of 200
+page 71 of 200
+page 72 of 200
+page 73 of 200
+page 74 of 200
+page 75 of 200
+page 76 of 200
+page 77 of 200
+page 78 of 200
+page 79 of 200
+page 80 of 200
+page 81 of 200
+page 82 of 200
+page 83 of 200
+page 84 of 200
+page 85 of 200
+page 86 of 200
+page 87 of 200
+page 88 of 200
+page 89 of 200
+page 90 of 200
+page 91 of 200
+page 92 of 200
+page 93 of 200
+page 94 of 200
+page 95 of 200
+page 96 of 200
+page 97 of 200
+page 98 of 200
+page 99 of 200
+page 100 of 200
+page 101 of 200
+page 102 of 200
+page 103 of 200
+page 104 of 200
+page 105 of 200
+page 106 of 200
+page 107 of 200
+page 108 of 200
+page 109 of 200
+page 110 of 200
+page 111 of 200
+page 112 of 200
+page 113 of 200
+page 114 of 200
+page 115 of 200
+page 116 of 200
+page 117 of 200
+page 118 of 200
+page 119 of 200
+page 120 of 200
+page 121 of 200
+page 122 of 200
+page 123 of 200
+page 124 of 200
+page 125 of 200
+page 126 of 200
+page 127 of 200
+page 128 of 200
+page 129 of 200
+page 130 of 200
+page 131 of 200
+page 132 of 200
+page 133 of 200
+page 134 of 200
+page 135 of 200
+page 136 of 200
+page 137 of 200
+page 138 of 200
+page 139 of 200
+page 140 of 200
+page 141 of 200
+page 142 of 200
+page 143 of 200
+page 144 of 200
+page 145 of 200
+page 146 of 200
+page 147 of 200
+page 148 of 200
+page 149 of 200
+page 150 of 200
+page 151 of 200
+page 152 of 200
+page 153 of 200
+page 154 of 200
+page 155 of 200
+page 156 of 200
+page 157 of 200
+page 158 of 200
+page 159 of 200
+page 160 of 200
+page 161 of 200
+page 162 of 200
+page 163 of 200
+page 164 of 200
+page 165 of 200
+page 166 of 200
+page 167 of 200
+page 168 of 200
+page 169 of 200
+page 170 of 200
+page 171 of 200
+page 172 of 200
+page 173 of 200
+page 174 of 200
+page 175 of 200
+page 176 of 200
+page 177 of 200
+page 178 of 200
+page 179 of 200
+page 180 of 200
+page 181 of 200
+page 182 of 200
+page 183 of 200
+page 184 of 200
+page 185 of 200
+page 186 of 200
+page 187 of 200
+page 188 of 200
+page 189 of 200
+page 190 of 200
+page 191 of 200
+page 192 of 200
+page 193 of 200
+page 194 of 200
+page 195 of 200
+page 196 of 200
+page 197 of 200
+page 198 of 200
+page 199 of 200
+page 200 of 200
diff --git a/qpdf/qtest/qpdf/large_file_xref_reconstruct.out b/qpdf/qtest/qpdf/large_file_xref_reconstruct.out
new file mode 100644
index 00000000..da5b25b1
--- /dev/null
+++ b/qpdf/qtest/qpdf/large_file_xref_reconstruct.out
@@ -0,0 +1,203 @@
+WARNING: a.pdf: file is damaged
+WARNING: a.pdf: can't find startxref
+WARNING: a.pdf: Attempting to reconstruct cross-reference table
+page 1 of 200
+page 2 of 200
+page 3 of 200
+page 4 of 200
+page 5 of 200
+page 6 of 200
+page 7 of 200
+page 8 of 200
+page 9 of 200
+page 10 of 200
+page 11 of 200
+page 12 of 200
+page 13 of 200
+page 14 of 200
+page 15 of 200
+page 16 of 200
+page 17 of 200
+page 18 of 200
+page 19 of 200
+page 20 of 200
+page 21 of 200
+page 22 of 200
+page 23 of 200
+page 24 of 200
+page 25 of 200
+page 26 of 200
+page 27 of 200
+page 28 of 200
+page 29 of 200
+page 30 of 200
+page 31 of 200
+page 32 of 200
+page 33 of 200
+page 34 of 200
+page 35 of 200
+page 36 of 200
+page 37 of 200
+page 38 of 200
+page 39 of 200
+page 40 of 200
+page 41 of 200
+page 42 of 200
+page 43 of 200
+page 44 of 200
+page 45 of 200
+page 46 of 200
+page 47 of 200
+page 48 of 200
+page 49 of 200
+page 50 of 200
+page 51 of 200
+page 52 of 200
+page 53 of 200
+page 54 of 200
+page 55 of 200
+page 56 of 200
+page 57 of 200
+page 58 of 200
+page 59 of 200
+page 60 of 200
+page 61 of 200
+page 62 of 200
+page 63 of 200
+page 64 of 200
+page 65 of 200
+page 66 of 200
+page 67 of 200
+page 68 of 200
+page 69 of 200
+page 70 of 200
+page 71 of 200
+page 72 of 200
+page 73 of 200
+page 74 of 200
+page 75 of 200
+page 76 of 200
+page 77 of 200
+page 78 of 200
+page 79 of 200
+page 80 of 200
+page 81 of 200
+page 82 of 200
+page 83 of 200
+page 84 of 200
+page 85 of 200
+page 86 of 200
+page 87 of 200
+page 88 of 200
+page 89 of 200
+page 90 of 200
+page 91 of 200
+page 92 of 200
+page 93 of 200
+page 94 of 200
+page 95 of 200
+page 96 of 200
+page 97 of 200
+page 98 of 200
+page 99 of 200
+page 100 of 200
+page 101 of 200
+page 102 of 200
+page 103 of 200
+page 104 of 200
+page 105 of 200
+page 106 of 200
+page 107 of 200
+page 108 of 200
+page 109 of 200
+page 110 of 200
+page 111 of 200
+page 112 of 200
+page 113 of 200
+page 114 of 200
+page 115 of 200
+page 116 of 200
+page 117 of 200
+page 118 of 200
+page 119 of 200
+page 120 of 200
+page 121 of 200
+page 122 of 200
+page 123 of 200
+page 124 of 200
+page 125 of 200
+page 126 of 200
+page 127 of 200
+page 128 of 200
+page 129 of 200
+page 130 of 200
+page 131 of 200
+page 132 of 200
+page 133 of 200
+page 134 of 200
+page 135 of 200
+page 136 of 200
+page 137 of 200
+page 138 of 200
+page 139 of 200
+page 140 of 200
+page 141 of 200
+page 142 of 200
+page 143 of 200
+page 144 of 200
+page 145 of 200
+page 146 of 200
+page 147 of 200
+page 148 of 200
+page 149 of 200
+page 150 of 200
+page 151 of 200
+page 152 of 200
+page 153 of 200
+page 154 of 200
+page 155 of 200
+page 156 of 200
+page 157 of 200
+page 158 of 200
+page 159 of 200
+page 160 of 200
+page 161 of 200
+page 162 of 200
+page 163 of 200
+page 164 of 200
+page 165 of 200
+page 166 of 200
+page 167 of 200
+page 168 of 200
+page 169 of 200
+page 170 of 200
+page 171 of 200
+page 172 of 200
+page 173 of 200
+page 174 of 200
+page 175 of 200
+page 176 of 200
+page 177 of 200
+page 178 of 200
+page 179 of 200
+page 180 of 200
+page 181 of 200
+page 182 of 200
+page 183 of 200
+page 184 of 200
+page 185 of 200
+page 186 of 200
+page 187 of 200
+page 188 of 200
+page 189 of 200
+page 190 of 200
+page 191 of 200
+page 192 of 200
+page 193 of 200
+page 194 of 200
+page 195 of 200
+page 196 of 200
+page 197 of 200
+page 198 of 200
+page 199 of 200
+page 200 of 200
diff --git a/qpdf/test_large_file.cc b/qpdf/test_large_file.cc
new file mode 100644
index 00000000..c1ee4060
--- /dev/null
+++ b/qpdf/test_large_file.cc
@@ -0,0 +1,368 @@
+#include <qpdf/QPDF.hh>
+#include <qpdf/QPDFWriter.hh>
+#include <qpdf/QPDFObjectHandle.hh>
+#include <qpdf/QUtil.hh>
+#include <iostream>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+// Run "test_large_file write small a.pdf" to get a PDF file that you
+// can look at in a reader.
+
+// This program reads and writes specially crafted files for testing
+// large file support. In write mode, write a file of npages pages
+// where each page contains unique text and a unique image. The image
+// is a binary representation of the page number. The image contains
+// horizontal stripes with light stripes representing 1, dark stripes
+// representing 0, and the high bit on top. In read mode, read the
+// file back checking to make sure all the image data and page
+// contents are as expected.
+
+// Running this is small mode produces a small file that is easy to
+// look at in any viewer. Since there is no question about proper
+// functionality for small files, writing and reading the small file
+// allows the qpdf library to test this test program. Writing and
+// reading the large file then allows us to verify large file support
+// with confidence.
+
+static char const* whoami = 0;
+
+// Height should be a multiple of 10
+static int const nstripes = 10;
+static int const stripesize_large = 500;
+static int const stripesize_small = 5;
+static int const npages = 200;
+
+// initialized in main
+int stripesize = 0;
+int width = 0;
+int height = 0;
+static unsigned char* buf = 0;
+
+static inline unsigned char get_pixel_color(int n, int row)
+{
+ return (n & (1 << (nstripes - 1 - row))) ? '\xc0' : '\x40';
+}
+
+class ImageChecker: public Pipeline
+{
+ public:
+ ImageChecker(int n);
+ virtual ~ImageChecker();
+ virtual void write(unsigned char* data, size_t len);
+ virtual void finish();
+
+ private:
+ int n;
+ size_t offset;
+ bool okay;
+};
+
+ImageChecker::ImageChecker(int n) :
+ Pipeline("image checker", 0),
+ n(n),
+ offset(0),
+ okay(true)
+{
+}
+
+ImageChecker::~ImageChecker()
+{
+}
+
+void
+ImageChecker::write(unsigned char* data, size_t len)
+{
+ for (size_t i = 0; i < len; ++i)
+ {
+ int y = (this->offset + i) / width / stripesize;
+ unsigned char color = get_pixel_color(n, y);
+ if (data[i] != color)
+ {
+ okay = false;
+ }
+ }
+ this->offset += len;
+}
+
+void
+ImageChecker::finish()
+{
+ if (! okay)
+ {
+ std::cout << "errors found checking image data for page " << n
+ << std::endl;
+ }
+}
+
+class ImageProvider: public QPDFObjectHandle::StreamDataProvider
+{
+ public:
+ ImageProvider(int n);
+ virtual ~ImageProvider();
+ virtual void provideStreamData(int objid, int generation,
+ Pipeline* pipeline);
+ size_t getLength() const;
+
+ private:
+ int n;
+};
+
+ImageProvider::ImageProvider(int n) :
+ n(n)
+{
+}
+
+ImageProvider::~ImageProvider()
+{
+}
+
+void
+ImageProvider::provideStreamData(int objid, int generation,
+ Pipeline* pipeline)
+{
+ if (buf == 0)
+ {
+ buf = new unsigned char[width * stripesize];
+ }
+ std::cout << "page " << n << " of " << npages << std::endl;
+ for (int y = 0; y < nstripes; ++y)
+ {
+ unsigned char color = get_pixel_color(n, y);
+ memset(buf, (int) color, width * stripesize);
+ pipeline->write(buf, width * stripesize);
+ }
+ pipeline->finish();
+}
+
+size_t
+ImageProvider::getLength() const
+{
+ return width * height;
+}
+
+void usage()
+{
+ std::cerr << "Usage: " << whoami << " {read|write} {large|small} outfile"
+ << std::endl;
+ exit(2);
+}
+
+static void set_parameters(bool large)
+{
+ stripesize = large ? stripesize_large : stripesize_small;
+ height = nstripes * stripesize;
+ width = height;
+}
+
+std::string generate_page_contents(int pageno)
+{
+ std::string contents =
+ "BT /F1 24 Tf 72 720 Td (page " + QUtil::int_to_string(pageno) +
+ ") Tj ET\n"
+ "q 468 0 0 468 72 72 cm /Im1 Do Q\n";
+ return contents;
+}
+
+static QPDFObjectHandle create_page_contents(QPDF& pdf, int pageno)
+{
+ std::string contents = generate_page_contents(pageno);
+ PointerHolder<Buffer> b = new Buffer(contents.length());
+ unsigned char* bp = b->getBuffer();
+ memcpy(bp, (char*)contents.c_str(), contents.length());
+ return QPDFObjectHandle::newStream(&pdf, b);
+}
+
+QPDFObjectHandle newName(std::string const& name)
+{
+ return QPDFObjectHandle::newName(name);
+}
+
+QPDFObjectHandle newInteger(int val)
+{
+ return QPDFObjectHandle::newInteger(val);
+}
+
+static void create_pdf(char const* filename)
+{
+ QPDF pdf;
+
+ pdf.emptyPDF();
+
+ QPDFObjectHandle font = pdf.makeIndirectObject(
+ QPDFObjectHandle::newDictionary());
+ font.replaceKey("/Type", newName("/Font"));
+ font.replaceKey("/Subtype", newName("/Type1"));
+ font.replaceKey("/Name", newName("/F1"));
+ font.replaceKey("/BaseFont", newName("/Helvetica"));
+ font.replaceKey("/Encoding", newName("/WinAnsiEncoding"));
+
+ QPDFObjectHandle procset =
+ pdf.makeIndirectObject(QPDFObjectHandle::newArray());
+ procset.appendItem(newName("/PDF"));
+ procset.appendItem(newName("/Text"));
+ procset.appendItem(newName("/ImageC"));
+
+ QPDFObjectHandle rfont = QPDFObjectHandle::newDictionary();
+ rfont.replaceKey("/F1", font);
+
+ QPDFObjectHandle mediabox = QPDFObjectHandle::newArray();
+ mediabox.appendItem(newInteger(0));
+ mediabox.appendItem(newInteger(0));
+ mediabox.appendItem(newInteger(612));
+ mediabox.appendItem(newInteger(792));
+
+ for (int pageno = 1; pageno <= npages; ++pageno)
+ {
+ QPDFObjectHandle image = QPDFObjectHandle::newStream(&pdf);
+ QPDFObjectHandle image_dict = image.getDict();
+ image_dict.replaceKey("/Type", newName("/XObject"));
+ image_dict.replaceKey("/Subtype", newName("/Image"));
+ image_dict.replaceKey("/ColorSpace", newName("/DeviceGray"));
+ image_dict.replaceKey("/BitsPerComponent", newInteger(8));
+ image_dict.replaceKey("/Width", newInteger(width));
+ image_dict.replaceKey("/Height", newInteger(height));
+ ImageProvider* p = new ImageProvider(pageno);
+ PointerHolder<QPDFObjectHandle::StreamDataProvider> provider(p);
+ image.replaceStreamData(provider,
+ QPDFObjectHandle::newNull(),
+ QPDFObjectHandle::newNull(),
+ p->getLength());
+
+ QPDFObjectHandle xobject = QPDFObjectHandle::newDictionary();
+ xobject.replaceKey("/Im1", image);
+
+ QPDFObjectHandle resources = QPDFObjectHandle::newDictionary();
+ resources.replaceKey("/ProcSet", procset);
+ resources.replaceKey("/Font", rfont);
+ resources.replaceKey("/XObject", xobject);
+
+ QPDFObjectHandle contents = create_page_contents(pdf, pageno);
+
+ QPDFObjectHandle page = pdf.makeIndirectObject(
+ QPDFObjectHandle::newDictionary());
+ page.replaceKey("/Type", newName("/Page"));
+ page.replaceKey("/MediaBox", mediabox);
+ page.replaceKey("/Contents", contents);
+ page.replaceKey("/Resources", resources);
+
+ pdf.addPage(page, false);
+ }
+
+ QPDFWriter w(pdf, filename);
+ w.setStaticID(true); // for testing only
+ w.setStreamDataMode(qpdf_s_preserve);
+ w.setObjectStreamMode(qpdf_o_disable);
+ w.write();
+}
+
+static void check_page_contents(int pageno, QPDFObjectHandle page)
+{
+ PointerHolder<Buffer> buf =
+ page.getKey("/Contents").getStreamData();
+ std::string actual_contents =
+ std::string((char *)(buf->getBuffer()), buf->getSize());
+ std::string expected_contents = generate_page_contents(pageno);
+ if (expected_contents != actual_contents)
+ {
+ std::cout << "page contents wrong for page " << pageno << std::endl
+ << "ACTUAL: " << actual_contents
+ << "EXPECTED: " << expected_contents
+ << "----\n";
+ }
+}
+
+static void check_image(int pageno, QPDFObjectHandle page)
+{
+ QPDFObjectHandle image =
+ page.getKey("/Resources").getKey("/XObject").getKey("/Im1");
+ ImageChecker ic(pageno);
+ image.pipeStreamData(&ic, true, false, false);
+}
+
+static void check_pdf(char const* filename)
+{
+ QPDF pdf;
+ pdf.processFile(filename);
+ std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
+ assert(pages.size() == (size_t)npages);
+ for (int i = 0; i < npages; ++i)
+ {
+ int pageno = i + 1;
+ std::cout << "page " << pageno << " of " << npages << std::endl;
+ check_page_contents(pageno, pages[i]);
+ check_image(pageno, pages[i]);
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ whoami = QUtil::getWhoami(argv[0]);
+ QUtil::setLineBuf(stdout);
+
+ // For libtool's sake....
+ if (strncmp(whoami, "lt-", 3) == 0)
+ {
+ whoami += 3;
+ }
+ if (argc != 4)
+ {
+ usage();
+ }
+ char const* operation = argv[1];
+ char const* size = argv[2];
+ char const* filename = argv[3];
+
+ bool op_write = false;
+ bool size_large = false;
+
+ if (strcmp(operation, "write") == 0)
+ {
+ op_write = true;
+ }
+ else if (strcmp(operation, "read") == 0)
+ {
+ op_write = false;
+ }
+ else
+ {
+ usage();
+ }
+
+ if (strcmp(size, "large") == 0)
+ {
+ size_large = true;
+ }
+ else if (strcmp(size, "small") == 0)
+ {
+ size_large = false;
+ }
+ else
+ {
+ usage();
+ }
+
+ set_parameters(size_large);
+
+ try
+ {
+ if (op_write)
+ {
+ create_pdf(filename);
+ }
+ else
+ {
+ check_pdf(filename);
+ }
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << e.what() << std::endl;
+ exit(2);
+ }
+
+ delete [] buf;
+
+ return 0;
+}