aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMasamichi Hosoda <trueroad@trueroad.jp>2019-10-01 16:14:59 +0200
committerJay Berkenbilt <ejb@ql.org>2019-10-22 22:16:16 +0200
commit5cf4090aee4a269186e13d902f91e6af3411f4a6 (patch)
treebee68bc5933fb59a3ac3af3395f2c132365e2f5a
parent46ac3e21b362a8d5c7fd45c9c0384a52fc86fb9e (diff)
downloadqpdf-5cf4090aee4a269186e13d902f91e6af3411f4a6.tar.zst
Add QPDFWriter::getRenumberedObjGen()
-rw-r--r--include/qpdf/QPDFWriter.hh5
-rw-r--r--libqpdf/QPDFWriter.cc6
-rw-r--r--qpdf/build.mk1
-rw-r--r--qpdf/qtest/qpdf.test59
-rw-r--r--qpdf/test_renumber.cc273
5 files changed, 344 insertions, 0 deletions
diff --git a/include/qpdf/QPDFWriter.hh b/include/qpdf/QPDFWriter.hh
index edea3c15..eebc84c3 100644
--- a/include/qpdf/QPDFWriter.hh
+++ b/include/qpdf/QPDFWriter.hh
@@ -465,6 +465,11 @@ class QPDFWriter
QPDF_DLL
void write();
+ // Return renumbered ObjGen that was written into the final file.
+ // This method can be used after calling write().
+ QPDF_DLL
+ QPDFObjGen getRenumberedObjGen(QPDFObjGen);
+
private:
// flags used by unparseObject
static int const f_stream = 1 << 0;
diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc
index 4397c3ad..247a1ac5 100644
--- a/libqpdf/QPDFWriter.cc
+++ b/libqpdf/QPDFWriter.cc
@@ -2741,6 +2741,12 @@ QPDFWriter::write()
indicateProgress(false, true);
}
+QPDFObjGen
+QPDFWriter::getRenumberedObjGen(QPDFObjGen og)
+{
+ return QPDFObjGen(this->m->obj_renumber[og], 0);
+}
+
void
QPDFWriter::enqueuePart(std::vector<QPDFObjectHandle>& part)
{
diff --git a/qpdf/build.mk b/qpdf/build.mk
index 204dbd3c..5b828473 100644
--- a/qpdf/build.mk
+++ b/qpdf/build.mk
@@ -5,6 +5,7 @@ BINS_qpdf = \
test_large_file \
test_pdf_doc_encoding \
test_pdf_unicode \
+ test_renumber \
test_tokenizer \
test_unicode_filenames \
test_xref
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index c4ddce9e..8dd7a4bf 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -4036,6 +4036,65 @@ $td->runtest("with object streams",
show_ntests();
# ----------
+$td->notify("--- Renumber Objects / XRef ---");
+$n_tests += 8;
+
+$td->runtest("w/o objstm",
+ {$td->COMMAND => "test_renumber minimal.pdf"},
+ {$td->REGEXP => "succeeded\n",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
+$td->runtest("w/ objstm",
+ {$td->COMMAND => "test_renumber digitally-signed.pdf"},
+ {$td->REGEXP => "succeeded\n",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
+$td->runtest("w/o objstm, --object-streams=generate",
+ {$td->COMMAND =>
+ "test_renumber --object-streams=generate minimal.pdf"},
+ {$td->REGEXP => "succeeded\n",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
+$td->runtest("w/ objstm, --object-streams=generate",
+ {$td->COMMAND =>
+ "test_renumber --object-streams=generate digitally-signed.pdf"},
+ {$td->REGEXP => "succeeded\n",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
+$td->runtest("w/o objstm, --linearize",
+ {$td->COMMAND =>
+ "test_renumber --linearize minimal.pdf"},
+ {$td->REGEXP => "succeeded\n",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
+$td->runtest("w/ objstm, --linearize",
+ {$td->COMMAND =>
+ "test_renumber --linearize digitally-signed.pdf"},
+ {$td->REGEXP => "succeeded\n",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
+$td->runtest("w/o objstm, --preserve-unreferenced",
+ {$td->COMMAND =>
+ "test_renumber --preserve-unreferenced minimal.pdf"},
+ {$td->REGEXP => "succeeded\n",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
+$td->runtest("w/ objstm, --preserve-unreferenced",
+ {$td->COMMAND =>
+ "test_renumber --preserve-unreferenced digitally-signed.pdf"},
+ {$td->REGEXP => "succeeded\n",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
+show_ntests();
+# ----------
$td->notify("--- Large File Tests ---");
my $nlarge = 1;
if (defined $large_file_test_path)
diff --git a/qpdf/test_renumber.cc b/qpdf/test_renumber.cc
new file mode 100644
index 00000000..1c1954cb
--- /dev/null
+++ b/qpdf/test_renumber.cc
@@ -0,0 +1,273 @@
+#include <qpdf/Buffer.hh>
+#include <qpdf/PointerHolder.hh>
+#include <qpdf/QPDF.hh>
+#include <qpdf/QPDFObject.hh>
+#include <qpdf/QPDFObjectHandle.hh>
+#include <qpdf/QPDFObjGen.hh>
+#include <qpdf/QPDFWriter.hh>
+
+#include <algorithm>
+#include <iostream>
+#include <set>
+#include <string>
+#include <vector>
+#include <cstdlib>
+
+void usage()
+{
+ std::cerr
+ << "Usage: test_renumber [OPTION] INPUT.pdf"
+ << std::endl
+ << "Option:"
+ << std::endl
+ << " --object-streams=preserve|disable|generate"
+ << std::endl
+ << " --linearize"
+ << std::endl
+ << " --preserve-unreferenced"
+ << std::endl;
+}
+
+bool compare(QPDFObjectHandle a, QPDFObjectHandle b)
+{
+ static std::set<QPDFObjGen> visited;
+ if (a.isIndirect())
+ {
+ if (visited.count(a.getObjGen()))
+ {
+ return true;
+ }
+ visited.insert(a.getObjGen());
+ }
+
+ if (a.getTypeCode() != b.getTypeCode())
+ {
+ std::cerr
+ << "different type code"
+ << std::endl;
+ return false;
+ }
+
+ switch (a.getTypeCode())
+ {
+ case QPDFObject::ot_boolean:
+ if (a.getBoolValue() != b.getBoolValue())
+ {
+ std::cerr
+ << "different boolean"
+ << std::endl;
+ return false;
+ }
+ break;
+ case QPDFObject::ot_integer:
+ if (a.getIntValue() != b.getIntValue())
+ {
+ std::cerr
+ << "different integer"
+ << std::endl;
+ return false;
+ }
+ break;
+ case QPDFObject::ot_real:
+ if (a.getRealValue() != b.getRealValue())
+ {
+ std::cerr
+ << "different real"
+ << std::endl;
+ return false;
+ }
+ break;
+ case QPDFObject::ot_string:
+ if (a.getStringValue() != b.getStringValue())
+ {
+ std::cerr
+ << "different string"
+ << std::endl;
+ return false;
+ }
+ break;
+ case QPDFObject::ot_name:
+ if (a.getName() != b.getName())
+ {
+ std::cerr
+ << "different name"
+ << std::endl;
+ return false;
+ }
+ break;
+ case QPDFObject::ot_array:
+ {
+ std::vector<QPDFObjectHandle> objs_a = a.getArrayAsVector();
+ std::vector<QPDFObjectHandle> objs_b = b.getArrayAsVector();
+ size_t items = objs_a.size();
+ if (items != objs_b.size())
+ {
+ std::cerr
+ << "different array size"
+ << std::endl;
+ return false;
+ }
+
+ for (size_t i = 0; i < items; ++i)
+ {
+ if (!compare(objs_a[i], objs_b[i]))
+ {
+ std::cerr
+ << "different array item"
+ << std::endl;
+ return false;
+ }
+ }
+ }
+ break;
+ case QPDFObject::ot_dictionary:
+ {
+ std::set<std::string> keys_a = a.getKeys();
+ std::set<std::string> keys_b = b.getKeys();
+ if (keys_a != keys_b)
+ {
+ std::cerr
+ << "different dictionary keys"
+ << std::endl;
+ return false;
+ }
+
+ for(std::set<std::string>::iterator iter = keys_a.begin();
+ iter != keys_a.end(); ++iter)
+ {
+ if (!compare(a.getKey(*iter), b.getKey(*iter)))
+ {
+ std::cerr
+ << "different dictionary item"
+ << std::endl;
+ return false;
+ }
+ }
+ }
+ break;
+ case QPDFObject::ot_null:
+ break;
+ case QPDFObject::ot_stream:
+ std::cout << "stream objects are not compared" << std::endl;
+ break;
+ default:
+ std::cerr << "unknown object type" << std::endl;
+ std::exit(2);
+ }
+
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc < 2)
+ {
+ usage();
+ std::exit(2);
+ }
+
+ qpdf_object_stream_e mode = qpdf_o_preserve;
+ bool blinearize = false;
+ bool bpreserve_unreferenced = false;
+ std::string filename_input;
+ for (int i = 1; i < argc; ++i)
+ {
+ if (argv[i][0] == '-')
+ {
+ std::string opt = argv[i];
+ if (opt == "--object-streams=preserve")
+ {
+ mode = qpdf_o_preserve;
+ }
+ else if (opt == "--object-streams=disable")
+ {
+ mode = qpdf_o_disable;
+ }
+ else if (opt == "--object-streams=generate")
+ {
+ mode = qpdf_o_generate;
+ }
+ else if (opt == "--linearize")
+ {
+ blinearize = true;
+ }
+ else if (opt == "--preserve-unreferenced")
+ {
+ bpreserve_unreferenced = true;
+ }
+ else
+ {
+ usage();
+ std::exit(2);
+ }
+ }
+ else if (argc == i + 1)
+ {
+ filename_input = argv[i];
+ break;
+ }
+ else
+ {
+ usage();
+ std::exit(2);
+ }
+ }
+
+ try
+ {
+ QPDF qpdf_in;
+ qpdf_in.processFile(filename_input.c_str ());
+ std::vector<QPDFObjectHandle> objs_in = qpdf_in.getAllObjects();
+
+ QPDFWriter w(qpdf_in);
+ w.setOutputMemory();
+ w.setObjectStreamMode(mode);
+ w.setLinearization(blinearize);
+ w.setPreserveUnreferencedObjects(bpreserve_unreferenced);
+ w.write();
+
+ PointerHolder<Buffer> buf = w.getBuffer();
+
+ QPDF qpdf_ren;
+ qpdf_ren.processMemoryFile("renumbered",
+ reinterpret_cast<char*>(buf->getBuffer()),
+ buf->getSize());
+
+ for (std::vector<QPDFObjectHandle>::iterator iter = objs_in.begin();
+ iter != objs_in.end(); ++iter)
+ {
+ QPDFObjGen og_in = iter->getObjGen();
+ QPDFObjGen og_ren = w.getRenumberedObjGen(og_in);
+
+ std::cout
+ << "input "
+ << og_in.getObj() << "/" << og_in.getGen()
+ << " -> renumbered "
+ << og_ren.getObj() << "/" << og_ren.getGen()
+ << std::endl;
+
+ if (og_ren.getObj() == 0)
+ {
+ std::cout << "deleted" << std::endl;
+ continue;
+ }
+
+ if (!compare(*iter, qpdf_ren.getObjectByObjGen(og_ren)))
+ {
+ std::cerr
+ << "different"
+ << std::endl;
+ std::exit(2);
+ }
+ }
+
+ std::cout << "succeeded" << std::endl;
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << e.what() << std::endl;
+ std::exit(2);
+ }
+
+ return 0;
+}