Commit 5cf4090aee4a269186e13d902f91e6af3411f4a6
Committed by
Jay Berkenbilt
1 parent
46ac3e21
Add QPDFWriter::getRenumberedObjGen()
Showing
5 changed files
with
344 additions
and
0 deletions
include/qpdf/QPDFWriter.hh
| @@ -465,6 +465,11 @@ class QPDFWriter | @@ -465,6 +465,11 @@ class QPDFWriter | ||
| 465 | QPDF_DLL | 465 | QPDF_DLL |
| 466 | void write(); | 466 | void write(); |
| 467 | 467 | ||
| 468 | + // Return renumbered ObjGen that was written into the final file. | ||
| 469 | + // This method can be used after calling write(). | ||
| 470 | + QPDF_DLL | ||
| 471 | + QPDFObjGen getRenumberedObjGen(QPDFObjGen); | ||
| 472 | + | ||
| 468 | private: | 473 | private: |
| 469 | // flags used by unparseObject | 474 | // flags used by unparseObject |
| 470 | static int const f_stream = 1 << 0; | 475 | static int const f_stream = 1 << 0; |
libqpdf/QPDFWriter.cc
| @@ -2741,6 +2741,12 @@ QPDFWriter::write() | @@ -2741,6 +2741,12 @@ QPDFWriter::write() | ||
| 2741 | indicateProgress(false, true); | 2741 | indicateProgress(false, true); |
| 2742 | } | 2742 | } |
| 2743 | 2743 | ||
| 2744 | +QPDFObjGen | ||
| 2745 | +QPDFWriter::getRenumberedObjGen(QPDFObjGen og) | ||
| 2746 | +{ | ||
| 2747 | + return QPDFObjGen(this->m->obj_renumber[og], 0); | ||
| 2748 | +} | ||
| 2749 | + | ||
| 2744 | void | 2750 | void |
| 2745 | QPDFWriter::enqueuePart(std::vector<QPDFObjectHandle>& part) | 2751 | QPDFWriter::enqueuePart(std::vector<QPDFObjectHandle>& part) |
| 2746 | { | 2752 | { |
qpdf/build.mk
| @@ -5,6 +5,7 @@ BINS_qpdf = \ | @@ -5,6 +5,7 @@ BINS_qpdf = \ | ||
| 5 | test_large_file \ | 5 | test_large_file \ |
| 6 | test_pdf_doc_encoding \ | 6 | test_pdf_doc_encoding \ |
| 7 | test_pdf_unicode \ | 7 | test_pdf_unicode \ |
| 8 | + test_renumber \ | ||
| 8 | test_tokenizer \ | 9 | test_tokenizer \ |
| 9 | test_unicode_filenames \ | 10 | test_unicode_filenames \ |
| 10 | test_xref | 11 | test_xref |
qpdf/qtest/qpdf.test
| @@ -4036,6 +4036,65 @@ $td->runtest("with object streams", | @@ -4036,6 +4036,65 @@ $td->runtest("with object streams", | ||
| 4036 | 4036 | ||
| 4037 | show_ntests(); | 4037 | show_ntests(); |
| 4038 | # ---------- | 4038 | # ---------- |
| 4039 | +$td->notify("--- Renumber Objects / XRef ---"); | ||
| 4040 | +$n_tests += 8; | ||
| 4041 | + | ||
| 4042 | +$td->runtest("w/o objstm", | ||
| 4043 | + {$td->COMMAND => "test_renumber minimal.pdf"}, | ||
| 4044 | + {$td->REGEXP => "succeeded\n", | ||
| 4045 | + $td->EXIT_STATUS => 0}, | ||
| 4046 | + $td->NORMALIZE_NEWLINES); | ||
| 4047 | + | ||
| 4048 | +$td->runtest("w/ objstm", | ||
| 4049 | + {$td->COMMAND => "test_renumber digitally-signed.pdf"}, | ||
| 4050 | + {$td->REGEXP => "succeeded\n", | ||
| 4051 | + $td->EXIT_STATUS => 0}, | ||
| 4052 | + $td->NORMALIZE_NEWLINES); | ||
| 4053 | + | ||
| 4054 | +$td->runtest("w/o objstm, --object-streams=generate", | ||
| 4055 | + {$td->COMMAND => | ||
| 4056 | + "test_renumber --object-streams=generate minimal.pdf"}, | ||
| 4057 | + {$td->REGEXP => "succeeded\n", | ||
| 4058 | + $td->EXIT_STATUS => 0}, | ||
| 4059 | + $td->NORMALIZE_NEWLINES); | ||
| 4060 | + | ||
| 4061 | +$td->runtest("w/ objstm, --object-streams=generate", | ||
| 4062 | + {$td->COMMAND => | ||
| 4063 | + "test_renumber --object-streams=generate digitally-signed.pdf"}, | ||
| 4064 | + {$td->REGEXP => "succeeded\n", | ||
| 4065 | + $td->EXIT_STATUS => 0}, | ||
| 4066 | + $td->NORMALIZE_NEWLINES); | ||
| 4067 | + | ||
| 4068 | +$td->runtest("w/o objstm, --linearize", | ||
| 4069 | + {$td->COMMAND => | ||
| 4070 | + "test_renumber --linearize minimal.pdf"}, | ||
| 4071 | + {$td->REGEXP => "succeeded\n", | ||
| 4072 | + $td->EXIT_STATUS => 0}, | ||
| 4073 | + $td->NORMALIZE_NEWLINES); | ||
| 4074 | + | ||
| 4075 | +$td->runtest("w/ objstm, --linearize", | ||
| 4076 | + {$td->COMMAND => | ||
| 4077 | + "test_renumber --linearize digitally-signed.pdf"}, | ||
| 4078 | + {$td->REGEXP => "succeeded\n", | ||
| 4079 | + $td->EXIT_STATUS => 0}, | ||
| 4080 | + $td->NORMALIZE_NEWLINES); | ||
| 4081 | + | ||
| 4082 | +$td->runtest("w/o objstm, --preserve-unreferenced", | ||
| 4083 | + {$td->COMMAND => | ||
| 4084 | + "test_renumber --preserve-unreferenced minimal.pdf"}, | ||
| 4085 | + {$td->REGEXP => "succeeded\n", | ||
| 4086 | + $td->EXIT_STATUS => 0}, | ||
| 4087 | + $td->NORMALIZE_NEWLINES); | ||
| 4088 | + | ||
| 4089 | +$td->runtest("w/ objstm, --preserve-unreferenced", | ||
| 4090 | + {$td->COMMAND => | ||
| 4091 | + "test_renumber --preserve-unreferenced digitally-signed.pdf"}, | ||
| 4092 | + {$td->REGEXP => "succeeded\n", | ||
| 4093 | + $td->EXIT_STATUS => 0}, | ||
| 4094 | + $td->NORMALIZE_NEWLINES); | ||
| 4095 | + | ||
| 4096 | +show_ntests(); | ||
| 4097 | +# ---------- | ||
| 4039 | $td->notify("--- Large File Tests ---"); | 4098 | $td->notify("--- Large File Tests ---"); |
| 4040 | my $nlarge = 1; | 4099 | my $nlarge = 1; |
| 4041 | if (defined $large_file_test_path) | 4100 | if (defined $large_file_test_path) |
qpdf/test_renumber.cc
0 โ 100644
| 1 | +#include <qpdf/Buffer.hh> | ||
| 2 | +#include <qpdf/PointerHolder.hh> | ||
| 3 | +#include <qpdf/QPDF.hh> | ||
| 4 | +#include <qpdf/QPDFObject.hh> | ||
| 5 | +#include <qpdf/QPDFObjectHandle.hh> | ||
| 6 | +#include <qpdf/QPDFObjGen.hh> | ||
| 7 | +#include <qpdf/QPDFWriter.hh> | ||
| 8 | + | ||
| 9 | +#include <algorithm> | ||
| 10 | +#include <iostream> | ||
| 11 | +#include <set> | ||
| 12 | +#include <string> | ||
| 13 | +#include <vector> | ||
| 14 | +#include <cstdlib> | ||
| 15 | + | ||
| 16 | +void usage() | ||
| 17 | +{ | ||
| 18 | + std::cerr | ||
| 19 | + << "Usage: test_renumber [OPTION] INPUT.pdf" | ||
| 20 | + << std::endl | ||
| 21 | + << "Option:" | ||
| 22 | + << std::endl | ||
| 23 | + << " --object-streams=preserve|disable|generate" | ||
| 24 | + << std::endl | ||
| 25 | + << " --linearize" | ||
| 26 | + << std::endl | ||
| 27 | + << " --preserve-unreferenced" | ||
| 28 | + << std::endl; | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +bool compare(QPDFObjectHandle a, QPDFObjectHandle b) | ||
| 32 | +{ | ||
| 33 | + static std::set<QPDFObjGen> visited; | ||
| 34 | + if (a.isIndirect()) | ||
| 35 | + { | ||
| 36 | + if (visited.count(a.getObjGen())) | ||
| 37 | + { | ||
| 38 | + return true; | ||
| 39 | + } | ||
| 40 | + visited.insert(a.getObjGen()); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + if (a.getTypeCode() != b.getTypeCode()) | ||
| 44 | + { | ||
| 45 | + std::cerr | ||
| 46 | + << "different type code" | ||
| 47 | + << std::endl; | ||
| 48 | + return false; | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + switch (a.getTypeCode()) | ||
| 52 | + { | ||
| 53 | + case QPDFObject::ot_boolean: | ||
| 54 | + if (a.getBoolValue() != b.getBoolValue()) | ||
| 55 | + { | ||
| 56 | + std::cerr | ||
| 57 | + << "different boolean" | ||
| 58 | + << std::endl; | ||
| 59 | + return false; | ||
| 60 | + } | ||
| 61 | + break; | ||
| 62 | + case QPDFObject::ot_integer: | ||
| 63 | + if (a.getIntValue() != b.getIntValue()) | ||
| 64 | + { | ||
| 65 | + std::cerr | ||
| 66 | + << "different integer" | ||
| 67 | + << std::endl; | ||
| 68 | + return false; | ||
| 69 | + } | ||
| 70 | + break; | ||
| 71 | + case QPDFObject::ot_real: | ||
| 72 | + if (a.getRealValue() != b.getRealValue()) | ||
| 73 | + { | ||
| 74 | + std::cerr | ||
| 75 | + << "different real" | ||
| 76 | + << std::endl; | ||
| 77 | + return false; | ||
| 78 | + } | ||
| 79 | + break; | ||
| 80 | + case QPDFObject::ot_string: | ||
| 81 | + if (a.getStringValue() != b.getStringValue()) | ||
| 82 | + { | ||
| 83 | + std::cerr | ||
| 84 | + << "different string" | ||
| 85 | + << std::endl; | ||
| 86 | + return false; | ||
| 87 | + } | ||
| 88 | + break; | ||
| 89 | + case QPDFObject::ot_name: | ||
| 90 | + if (a.getName() != b.getName()) | ||
| 91 | + { | ||
| 92 | + std::cerr | ||
| 93 | + << "different name" | ||
| 94 | + << std::endl; | ||
| 95 | + return false; | ||
| 96 | + } | ||
| 97 | + break; | ||
| 98 | + case QPDFObject::ot_array: | ||
| 99 | + { | ||
| 100 | + std::vector<QPDFObjectHandle> objs_a = a.getArrayAsVector(); | ||
| 101 | + std::vector<QPDFObjectHandle> objs_b = b.getArrayAsVector(); | ||
| 102 | + size_t items = objs_a.size(); | ||
| 103 | + if (items != objs_b.size()) | ||
| 104 | + { | ||
| 105 | + std::cerr | ||
| 106 | + << "different array size" | ||
| 107 | + << std::endl; | ||
| 108 | + return false; | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + for (size_t i = 0; i < items; ++i) | ||
| 112 | + { | ||
| 113 | + if (!compare(objs_a[i], objs_b[i])) | ||
| 114 | + { | ||
| 115 | + std::cerr | ||
| 116 | + << "different array item" | ||
| 117 | + << std::endl; | ||
| 118 | + return false; | ||
| 119 | + } | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | + break; | ||
| 123 | + case QPDFObject::ot_dictionary: | ||
| 124 | + { | ||
| 125 | + std::set<std::string> keys_a = a.getKeys(); | ||
| 126 | + std::set<std::string> keys_b = b.getKeys(); | ||
| 127 | + if (keys_a != keys_b) | ||
| 128 | + { | ||
| 129 | + std::cerr | ||
| 130 | + << "different dictionary keys" | ||
| 131 | + << std::endl; | ||
| 132 | + return false; | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + for(std::set<std::string>::iterator iter = keys_a.begin(); | ||
| 136 | + iter != keys_a.end(); ++iter) | ||
| 137 | + { | ||
| 138 | + if (!compare(a.getKey(*iter), b.getKey(*iter))) | ||
| 139 | + { | ||
| 140 | + std::cerr | ||
| 141 | + << "different dictionary item" | ||
| 142 | + << std::endl; | ||
| 143 | + return false; | ||
| 144 | + } | ||
| 145 | + } | ||
| 146 | + } | ||
| 147 | + break; | ||
| 148 | + case QPDFObject::ot_null: | ||
| 149 | + break; | ||
| 150 | + case QPDFObject::ot_stream: | ||
| 151 | + std::cout << "stream objects are not compared" << std::endl; | ||
| 152 | + break; | ||
| 153 | + default: | ||
| 154 | + std::cerr << "unknown object type" << std::endl; | ||
| 155 | + std::exit(2); | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + return true; | ||
| 159 | +} | ||
| 160 | + | ||
| 161 | +int main(int argc, char *argv[]) | ||
| 162 | +{ | ||
| 163 | + if (argc < 2) | ||
| 164 | + { | ||
| 165 | + usage(); | ||
| 166 | + std::exit(2); | ||
| 167 | + } | ||
| 168 | + | ||
| 169 | + qpdf_object_stream_e mode = qpdf_o_preserve; | ||
| 170 | + bool blinearize = false; | ||
| 171 | + bool bpreserve_unreferenced = false; | ||
| 172 | + std::string filename_input; | ||
| 173 | + for (int i = 1; i < argc; ++i) | ||
| 174 | + { | ||
| 175 | + if (argv[i][0] == '-') | ||
| 176 | + { | ||
| 177 | + std::string opt = argv[i]; | ||
| 178 | + if (opt == "--object-streams=preserve") | ||
| 179 | + { | ||
| 180 | + mode = qpdf_o_preserve; | ||
| 181 | + } | ||
| 182 | + else if (opt == "--object-streams=disable") | ||
| 183 | + { | ||
| 184 | + mode = qpdf_o_disable; | ||
| 185 | + } | ||
| 186 | + else if (opt == "--object-streams=generate") | ||
| 187 | + { | ||
| 188 | + mode = qpdf_o_generate; | ||
| 189 | + } | ||
| 190 | + else if (opt == "--linearize") | ||
| 191 | + { | ||
| 192 | + blinearize = true; | ||
| 193 | + } | ||
| 194 | + else if (opt == "--preserve-unreferenced") | ||
| 195 | + { | ||
| 196 | + bpreserve_unreferenced = true; | ||
| 197 | + } | ||
| 198 | + else | ||
| 199 | + { | ||
| 200 | + usage(); | ||
| 201 | + std::exit(2); | ||
| 202 | + } | ||
| 203 | + } | ||
| 204 | + else if (argc == i + 1) | ||
| 205 | + { | ||
| 206 | + filename_input = argv[i]; | ||
| 207 | + break; | ||
| 208 | + } | ||
| 209 | + else | ||
| 210 | + { | ||
| 211 | + usage(); | ||
| 212 | + std::exit(2); | ||
| 213 | + } | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + try | ||
| 217 | + { | ||
| 218 | + QPDF qpdf_in; | ||
| 219 | + qpdf_in.processFile(filename_input.c_str ()); | ||
| 220 | + std::vector<QPDFObjectHandle> objs_in = qpdf_in.getAllObjects(); | ||
| 221 | + | ||
| 222 | + QPDFWriter w(qpdf_in); | ||
| 223 | + w.setOutputMemory(); | ||
| 224 | + w.setObjectStreamMode(mode); | ||
| 225 | + w.setLinearization(blinearize); | ||
| 226 | + w.setPreserveUnreferencedObjects(bpreserve_unreferenced); | ||
| 227 | + w.write(); | ||
| 228 | + | ||
| 229 | + PointerHolder<Buffer> buf = w.getBuffer(); | ||
| 230 | + | ||
| 231 | + QPDF qpdf_ren; | ||
| 232 | + qpdf_ren.processMemoryFile("renumbered", | ||
| 233 | + reinterpret_cast<char*>(buf->getBuffer()), | ||
| 234 | + buf->getSize()); | ||
| 235 | + | ||
| 236 | + for (std::vector<QPDFObjectHandle>::iterator iter = objs_in.begin(); | ||
| 237 | + iter != objs_in.end(); ++iter) | ||
| 238 | + { | ||
| 239 | + QPDFObjGen og_in = iter->getObjGen(); | ||
| 240 | + QPDFObjGen og_ren = w.getRenumberedObjGen(og_in); | ||
| 241 | + | ||
| 242 | + std::cout | ||
| 243 | + << "input " | ||
| 244 | + << og_in.getObj() << "/" << og_in.getGen() | ||
| 245 | + << " -> renumbered " | ||
| 246 | + << og_ren.getObj() << "/" << og_ren.getGen() | ||
| 247 | + << std::endl; | ||
| 248 | + | ||
| 249 | + if (og_ren.getObj() == 0) | ||
| 250 | + { | ||
| 251 | + std::cout << "deleted" << std::endl; | ||
| 252 | + continue; | ||
| 253 | + } | ||
| 254 | + | ||
| 255 | + if (!compare(*iter, qpdf_ren.getObjectByObjGen(og_ren))) | ||
| 256 | + { | ||
| 257 | + std::cerr | ||
| 258 | + << "different" | ||
| 259 | + << std::endl; | ||
| 260 | + std::exit(2); | ||
| 261 | + } | ||
| 262 | + } | ||
| 263 | + | ||
| 264 | + std::cout << "succeeded" << std::endl; | ||
| 265 | + } | ||
| 266 | + catch (std::exception& e) | ||
| 267 | + { | ||
| 268 | + std::cerr << e.what() << std::endl; | ||
| 269 | + std::exit(2); | ||
| 270 | + } | ||
| 271 | + | ||
| 272 | + return 0; | ||
| 273 | +} |