Commit 5cf4090aee4a269186e13d902f91e6af3411f4a6

Authored by Masamichi Hosoda
Committed by Jay Berkenbilt
1 parent 46ac3e21

Add QPDFWriter::getRenumberedObjGen()

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-&gt;runtest(&quot;with object streams&quot;, @@ -4036,6 +4036,65 @@ $td-&gt;runtest(&quot;with object streams&quot;,
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 +}