Commit 7fa5d1773b599df51df21658dff53f5b66fdbba8

Authored by Jay Berkenbilt
1 parent 8d42eb26

Implement top-level qpdf json parsing

Showing 37 changed files with 1053 additions and 8 deletions
@@ -54,6 +54,13 @@ Soon: Break ground on "Document-level work" @@ -54,6 +54,13 @@ Soon: Break ground on "Document-level work"
54 Output JSON v2 54 Output JSON v2
55 ============== 55 ==============
56 56
  57 +Try to never flatten pages tree. Make sure we do something reasonable
  58 +with pages tree repair. The problem is that if pages tree repair is
  59 +done as a side effect of running --json, the qpdf part of the json may
  60 +contain object numbers that aren't there. Maybe we need to indicate
  61 +whether pages tree repair has been done in the json, but this would
  62 +have to be known early in parsing, which is a problem.
  63 +
57 General things to remember: 64 General things to remember:
58 65
59 * Make sure all the information from --check and other informational 66 * Make sure all the information from --check and other informational
include/qpdf/QPDF.hh
@@ -998,6 +998,7 @@ class QPDF @@ -998,6 +998,7 @@ class QPDF
998 class JSONReactor: public JSON::Reactor 998 class JSONReactor: public JSON::Reactor
999 { 999 {
1000 public: 1000 public:
  1001 + JSONReactor(QPDF&, bool must_be_complete);
1001 virtual ~JSONReactor() = default; 1002 virtual ~JSONReactor() = default;
1002 virtual void dictionaryStart() override; 1003 virtual void dictionaryStart() override;
1003 virtual void arrayStart() override; 1004 virtual void arrayStart() override;
@@ -1006,6 +1007,32 @@ class QPDF @@ -1006,6 +1007,32 @@ class QPDF
1006 virtual bool 1007 virtual bool
1007 dictionaryItem(std::string const& key, JSON const& value) override; 1008 dictionaryItem(std::string const& key, JSON const& value) override;
1008 virtual bool arrayItem(JSON const& value) override; 1009 virtual bool arrayItem(JSON const& value) override;
  1010 +
  1011 + private:
  1012 + enum state_e {
  1013 + st_initial,
  1014 + st_top,
  1015 + st_ignore,
  1016 + st_qpdf,
  1017 + st_objects_top,
  1018 + st_trailer_top,
  1019 + st_object_top,
  1020 + st_stream,
  1021 + st_object,
  1022 + };
  1023 +
  1024 + void containerStart();
  1025 + void nestedState(std::string const& key, JSON const& value, state_e);
  1026 +
  1027 + QPDF& pdf;
  1028 + bool must_be_complete;
  1029 + bool saw_qpdf;
  1030 + bool saw_json_version;
  1031 + bool saw_pdf_version;
  1032 + bool saw_trailer;
  1033 + state_e state;
  1034 + state_e next_state;
  1035 + std::vector<state_e> state_stack;
1009 }; 1036 };
1010 friend class JSONReactor; 1037 friend class JSONReactor;
1011 1038
libqpdf/QPDF_json.cc
1 #include <qpdf/QPDF.hh> 1 #include <qpdf/QPDF.hh>
2 2
3 #include <qpdf/FileInputSource.hh> 3 #include <qpdf/FileInputSource.hh>
  4 +#include <qpdf/QTC.hh>
  5 +#include <qpdf/QUtil.hh>
  6 +#include <regex>
  7 +
  8 +namespace
  9 +{
  10 + class JSONExc: public std::runtime_error
  11 + {
  12 + public:
  13 + JSONExc(JSON const& value, std::string const& msg) :
  14 + std::runtime_error(
  15 + "offset " + QUtil::uint_to_string(value.getStart()) + ": " +
  16 + msg)
  17 + {
  18 + }
  19 + };
  20 +} // namespace
  21 +
  22 +static std::regex PDF_VERSION_RE("^\\d+\\.\\d+$");
  23 +static std::regex OBJ_KEY_RE("^obj:(\\d+) (\\d+) R$");
  24 +
  25 +QPDF::JSONReactor::JSONReactor(QPDF& pdf, bool must_be_complete) :
  26 + pdf(pdf),
  27 + must_be_complete(must_be_complete),
  28 + saw_qpdf(false),
  29 + saw_json_version(false),
  30 + saw_pdf_version(false),
  31 + saw_trailer(false),
  32 + state(st_initial),
  33 + next_state(st_top)
  34 +{
  35 + state_stack.push_back(st_initial);
  36 +}
  37 +
  38 +void
  39 +QPDF::JSONReactor::containerStart()
  40 +{
  41 + state_stack.push_back(state);
  42 + state = next_state;
  43 +}
4 44
5 void 45 void
6 QPDF::JSONReactor::dictionaryStart() 46 QPDF::JSONReactor::dictionaryStart()
7 { 47 {
8 - // QXXXXQ 48 + containerStart();
  49 + // QXXXQ
9 } 50 }
10 51
11 void 52 void
12 QPDF::JSONReactor::arrayStart() 53 QPDF::JSONReactor::arrayStart()
13 { 54 {
14 - // QXXXXQ 55 + containerStart();
  56 + if (state == st_top) {
  57 + QTC::TC("qpdf", "QPDF_json top-level array");
  58 + throw std::runtime_error("QPDF JSON must be a dictionary");
  59 + }
  60 + // QXXXQ
15 } 61 }
16 62
17 void 63 void
18 QPDF::JSONReactor::containerEnd(JSON const& value) 64 QPDF::JSONReactor::containerEnd(JSON const& value)
19 { 65 {
20 - // QXXXXQ 66 + state = state_stack.back();
  67 + state_stack.pop_back();
  68 + if (state == st_initial) {
  69 + if (!this->saw_qpdf) {
  70 + QTC::TC("qpdf", "QPDF_json missing qpdf");
  71 + throw std::runtime_error("\"qpdf\" object was not seen");
  72 + }
  73 + if (!this->saw_json_version) {
  74 + QTC::TC("qpdf", "QPDF_json missing json version");
  75 + throw std::runtime_error("\"qpdf.jsonversion\" was not seen");
  76 + }
  77 + if (must_be_complete && !this->saw_pdf_version) {
  78 + QTC::TC("qpdf", "QPDF_json missing pdf version");
  79 + throw std::runtime_error("\"qpdf.pdfversion\" was not seen");
  80 + }
  81 + if (must_be_complete && !this->saw_trailer) {
  82 + /// QTC::TC("qpdf", "QPDF_json missing trailer");
  83 + throw std::runtime_error("\"qpdf.objects.trailer\" was not seen");
  84 + }
  85 + }
  86 +
  87 + // QXXXQ
21 } 88 }
22 89
23 void 90 void
24 QPDF::JSONReactor::topLevelScalar() 91 QPDF::JSONReactor::topLevelScalar()
25 { 92 {
26 - // QXXXXQ 93 + QTC::TC("qpdf", "QPDF_json top-level scalar");
  94 + throw std::runtime_error("QPDF JSON must be a dictionary");
  95 +}
  96 +
  97 +void
  98 +QPDF::JSONReactor::nestedState(
  99 + std::string const& key, JSON const& value, state_e next)
  100 +{
  101 + // Use this method when the next state is for processing a nested
  102 + // dictionary.
  103 + if (!value.isDictionary()) {
  104 + throw JSONExc(value, "\"" + key + "\" must be a dictionary");
  105 + }
  106 + this->next_state = next;
27 } 107 }
28 108
29 bool 109 bool
30 QPDF::JSONReactor::dictionaryItem(std::string const& key, JSON const& value) 110 QPDF::JSONReactor::dictionaryItem(std::string const& key, JSON const& value)
31 { 111 {
32 - // QXXXXQ 112 + if (state == st_ignore) {
  113 + // ignore
  114 + } else if (state == st_top) {
  115 + if (key == "qpdf") {
  116 + this->saw_qpdf = true;
  117 + nestedState(key, value, st_qpdf);
  118 + } else {
  119 + // Ignore all other fields for forward compatibility.
  120 + // Don't use nestedState since this can be any type.
  121 + next_state = st_ignore;
  122 + }
  123 + } else if (state == st_qpdf) {
  124 + if (key == "jsonversion") {
  125 + this->saw_json_version = true;
  126 + std::string v;
  127 + if (!(value.getNumber(v) && (v == "2"))) {
  128 + QTC::TC("qpdf", "QPDF_json bad json version");
  129 + throw JSONExc(value, "only JSON version 2 is supported");
  130 + }
  131 + } else if (key == "pdfversion") {
  132 + this->saw_pdf_version = true;
  133 + bool version_okay = false;
  134 + std::string v;
  135 + if (value.getString(v)) {
  136 + std::smatch m;
  137 + if (std::regex_match(v, m, PDF_VERSION_RE)) {
  138 + version_okay = true;
  139 + this->pdf.m->pdf_version = v;
  140 + }
  141 + }
  142 + if (!version_okay) {
  143 + QTC::TC("qpdf", "QPDF_json bad pdf version");
  144 + throw JSONExc(value, "invalid PDF version (must be x.y)");
  145 + }
  146 + } else if (key == "objects") {
  147 + nestedState(key, value, st_objects_top);
  148 + } else {
  149 + // ignore unknown keys for forward compatibility
  150 + }
  151 + } else if (state == st_objects_top) {
  152 + std::smatch m;
  153 + if (key == "trailer") {
  154 + this->saw_trailer = true;
  155 + nestedState(key, value, st_trailer_top);
  156 + // QXXXQ
  157 + } else if (std::regex_match(key, m, OBJ_KEY_RE)) {
  158 + nestedState(key, value, st_object_top);
  159 + // QXXXQ
  160 + } else {
  161 + QTC::TC("qpdf", "QPDF_json bad object key");
  162 + throw JSONExc(
  163 + value, "object key should be \"trailer\" or \"obj:n n R\"");
  164 + }
  165 + } else if (state == st_object_top) {
  166 + if (key == "value") {
  167 + // Don't use nestedState since this can have any type.
  168 + next_state = st_object;
  169 + // QXXXQ
  170 + } else if (key == "stream") {
  171 + nestedState(key, value, st_stream);
  172 + // QXXXQ
  173 + } else {
  174 + // Ignore unknown keys for forward compatibility
  175 + }
  176 + } else if (state == st_trailer_top) {
  177 + if (key == "value") {
  178 + // The trailer must be a dictionary, so we can use nestedState.
  179 + nestedState("trailer.value", value, st_object);
  180 + // QXXXQ
  181 + } else if (key == "stream") {
  182 + QTC::TC("qpdf", "QPDF_json trailer stream");
  183 + throw JSONExc(value, "the trailer may not be a stream");
  184 + } else {
  185 + // Ignore unknown keys for forward compatibility
  186 + }
  187 + } else if (state == st_stream) {
  188 + if (key == "dict") {
  189 + // Since a stream dictionary must be a dictionary, we can
  190 + // use nestedState to transition to st_value.
  191 + nestedState("stream.dict", value, st_object);
  192 + // QXXXQ
  193 + } else if (key == "data") {
  194 + // QXXXQ
  195 + } else if (key == "datafile") {
  196 + // QXXXQ
  197 + } else {
  198 + // Ignore unknown keys for forward compatibility.
  199 + next_state = st_ignore;
  200 + }
  201 + } else if (state == st_object) {
  202 + // QXXXQ
  203 + } else {
  204 + throw std::logic_error(
  205 + "QPDF_json: unknown state " + QUtil::int_to_string(state));
  206 + }
  207 +
  208 + // QXXXQ
33 return true; 209 return true;
34 } 210 }
35 211
36 bool 212 bool
37 QPDF::JSONReactor::arrayItem(JSON const& value) 213 QPDF::JSONReactor::arrayItem(JSON const& value)
38 { 214 {
39 - // QXXXXQ 215 + // QXXXQ
40 return true; 216 return true;
41 } 217 }
42 218
@@ -65,7 +241,12 @@ QPDF::updateFromJSON(std::shared_ptr&lt;InputSource&gt; is) @@ -65,7 +241,12 @@ QPDF::updateFromJSON(std::shared_ptr&lt;InputSource&gt; is)
65 } 241 }
66 242
67 void 243 void
68 -QPDF::importJSON(std::shared_ptr<InputSource>, bool must_be_complete) 244 +QPDF::importJSON(std::shared_ptr<InputSource> is, bool must_be_complete)
69 { 245 {
70 - // QXXXQ 246 + JSONReactor reactor(*this, must_be_complete);
  247 + try {
  248 + JSON::parse(*is, &reactor);
  249 + } catch (std::runtime_error& e) {
  250 + throw std::runtime_error(is->getName() + ": " + e.what());
  251 + }
71 } 252 }
qpdf/qpdf.testcov
@@ -650,3 +650,12 @@ QPDFJob json encrypt duplicate key length 0 @@ -650,3 +650,12 @@ QPDFJob json encrypt duplicate key length 0
650 QPDFJob json encrypt missing password 0 650 QPDFJob json encrypt missing password 0
651 QPDFJob json pages no file 0 651 QPDFJob json pages no file 0
652 qpdf-c called qpdf_empty_pdf 0 652 qpdf-c called qpdf_empty_pdf 0
  653 +QPDF_json missing qpdf 0
  654 +QPDF_json missing json version 0
  655 +QPDF_json missing pdf version 0
  656 +QPDF_json top-level scalar 0
  657 +QPDF_json bad json version 0
  658 +QPDF_json bad pdf version 0
  659 +QPDF_json top-level array 0
  660 +QPDF_json bad object key 0
  661 +QPDF_json trailer stream 0
qpdf/qtest/qpdf-json.test 0 โ†’ 100644
  1 +#!/usr/bin/env perl
  2 +require 5.008;
  3 +use warnings;
  4 +use strict;
  5 +
  6 +unshift(@INC, '.');
  7 +require qpdf_test_helpers;
  8 +
  9 +chdir("qpdf") or die "chdir testdir failed: $!\n";
  10 +
  11 +require TestDriver;
  12 +
  13 +cleanup();
  14 +
  15 +my $td = new TestDriver('qpdf-json');
  16 +
  17 +my $n_tests = 0;
  18 +
  19 +my @badfiles = (
  20 + 'no-qpdf-object',
  21 + 'no-json-version',
  22 + 'no-pdf-version',
  23 + 'top-level-scalar',
  24 + 'bad-json-version1',
  25 + 'bad-json-version2',
  26 + 'bad-pdf-version1',
  27 + 'bad-pdf-version2',
  28 + 'top-level-array',
  29 + 'objects-not-dict',
  30 + 'bad-object-key',
  31 + 'object-not-dict',
  32 + 'stream-not-dict',
  33 + 'stream-dict-not-dict',
  34 + 'trailer-not-dict',
  35 + 'trailer-stream',
  36 + );
  37 +
  38 +$n_tests += scalar(@badfiles);
  39 +
  40 +foreach my $f (@badfiles)
  41 +{
  42 + $td->runtest("bad: $f",
  43 + {$td->COMMAND =>
  44 + "qpdf --create-from-json=qjson-$f.json a.pdf"},
  45 + {$td->FILE => "qjson-$f.out", $td->EXIT_STATUS => 2},
  46 + $td->NORMALIZE_NEWLINES);
  47 +}
  48 +
  49 +cleanup();
  50 +$td->report($n_tests);
qpdf/qtest/qpdf/qjson-bad-json-version1.json 0 โ†’ 100644
  1 +{
  2 + "version": 2,
  3 + "parameters": {
  4 + "decodelevel": "none"
  5 + },
  6 + "qpdf": {
  7 + "jsonversion": 16059,
  8 + "pdfversion": "1.3",
  9 + "maxobjectid": 6,
  10 + "objects": {
  11 + "obj:1 0 R": {
  12 + "value": {
  13 + "/Pages": "2 0 R",
  14 + "/Type": "/Catalog"
  15 + }
  16 + },
  17 + "obj:2 0 R": {
  18 + "value": {
  19 + "/Count": 1,
  20 + "/Kids": [
  21 + "3 0 R"
  22 + ],
  23 + "/Type": "/Pages"
  24 + }
  25 + },
  26 + "obj:3 0 R": {
  27 + "value": {
  28 + "/Contents": "4 0 R",
  29 + "/MediaBox": [
  30 + 0,
  31 + 0,
  32 + 612,
  33 + 792
  34 + ],
  35 + "/Parent": "2 0 R",
  36 + "/Resources": {
  37 + "/Font": {
  38 + "/F1": "6 0 R"
  39 + },
  40 + "/ProcSet": "5 0 R"
  41 + },
  42 + "/Type": "/Page"
  43 + }
  44 + },
  45 + "obj:4 0 R": {
  46 + "stream": {
  47 + "data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
  48 + "dict": {}
  49 + }
  50 + },
  51 + "obj:5 0 R": {
  52 + "value": [
  53 + "/PDF",
  54 + "/Text"
  55 + ]
  56 + },
  57 + "obj:6 0 R": {
  58 + "value": {
  59 + "/BaseFont": "/Helvetica",
  60 + "/Encoding": "/WinAnsiEncoding",
  61 + "/Subtype": "/Type1",
  62 + "/Type": "/Font"
  63 + }
  64 + },
  65 + "trailer": {
  66 + "value": {
  67 + "/Root": "1 0 R",
  68 + "/Size": 7
  69 + }
  70 + }
  71 + }
  72 + }
  73 +}
qpdf/qtest/qpdf/qjson-bad-json-version1.out 0 โ†’ 100644
  1 +qpdf: qjson-bad-json-version1.json: offset 98: only JSON version 2 is supported
qpdf/qtest/qpdf/qjson-bad-json-version2.json 0 โ†’ 100644
  1 +{
  2 + "version": 2,
  3 + "parameters": {
  4 + "decodelevel": "none"
  5 + },
  6 + "qpdf": {
  7 + "jsonversion": "potato",
  8 + "pdfversion": "1.3",
  9 + "maxobjectid": 6,
  10 + "objects": {
  11 + "obj:1 0 R": {
  12 + "value": {
  13 + "/Pages": "2 0 R",
  14 + "/Type": "/Catalog"
  15 + }
  16 + },
  17 + "obj:2 0 R": {
  18 + "value": {
  19 + "/Count": 1,
  20 + "/Kids": [
  21 + "3 0 R"
  22 + ],
  23 + "/Type": "/Pages"
  24 + }
  25 + },
  26 + "obj:3 0 R": {
  27 + "value": {
  28 + "/Contents": "4 0 R",
  29 + "/MediaBox": [
  30 + 0,
  31 + 0,
  32 + 612,
  33 + 792
  34 + ],
  35 + "/Parent": "2 0 R",
  36 + "/Resources": {
  37 + "/Font": {
  38 + "/F1": "6 0 R"
  39 + },
  40 + "/ProcSet": "5 0 R"
  41 + },
  42 + "/Type": "/Page"
  43 + }
  44 + },
  45 + "obj:4 0 R": {
  46 + "stream": {
  47 + "data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
  48 + "dict": {}
  49 + }
  50 + },
  51 + "obj:5 0 R": {
  52 + "value": [
  53 + "/PDF",
  54 + "/Text"
  55 + ]
  56 + },
  57 + "obj:6 0 R": {
  58 + "value": {
  59 + "/BaseFont": "/Helvetica",
  60 + "/Encoding": "/WinAnsiEncoding",
  61 + "/Subtype": "/Type1",
  62 + "/Type": "/Font"
  63 + }
  64 + },
  65 + "trailer": {
  66 + "value": {
  67 + "/Root": "1 0 R",
  68 + "/Size": 7
  69 + }
  70 + }
  71 + }
  72 + }
  73 +}
qpdf/qtest/qpdf/qjson-bad-json-version2.out 0 โ†’ 100644
  1 +qpdf: qjson-bad-json-version2.json: offset 98: only JSON version 2 is supported
qpdf/qtest/qpdf/qjson-bad-object-key.json 0 โ†’ 100644
  1 +{
  2 + "version": 2,
  3 + "parameters": {
  4 + "decodelevel": "none"
  5 + },
  6 + "qpdf": {
  7 + "jsonversion": 2,
  8 + "pdfversion": "1.3",
  9 + "maxobjectid": 6,
  10 + "objects": {
  11 + "potato": {
  12 + },
  13 + "obj:1 0 R": {
  14 + "value": {
  15 + "/Pages": "2 0 R",
  16 + "/Type": "/Catalog"
  17 + }
  18 + },
  19 + "obj:2 0 R": {
  20 + "value": {
  21 + "/Count": 1,
  22 + "/Kids": [
  23 + "3 0 R"
  24 + ],
  25 + "/Type": "/Pages"
  26 + }
  27 + },
  28 + "obj:3 0 R": {
  29 + "value": {
  30 + "/Contents": "4 0 R",
  31 + "/MediaBox": [
  32 + 0,
  33 + 0,
  34 + 612,
  35 + 792
  36 + ],
  37 + "/Parent": "2 0 R",
  38 + "/Resources": {
  39 + "/Font": {
  40 + "/F1": "6 0 R"
  41 + },
  42 + "/ProcSet": "5 0 R"
  43 + },
  44 + "/Type": "/Page"
  45 + }
  46 + },
  47 + "obj:4 0 R": {
  48 + "stream": {
  49 + "data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
  50 + "dict": {}
  51 + }
  52 + },
  53 + "obj:5 0 R": {
  54 + "value": [
  55 + "/PDF",
  56 + "/Text"
  57 + ]
  58 + },
  59 + "obj:6 0 R": {
  60 + "value": {
  61 + "/BaseFont": "/Helvetica",
  62 + "/Encoding": "/WinAnsiEncoding",
  63 + "/Subtype": "/Type1",
  64 + "/Type": "/Font"
  65 + }
  66 + },
  67 + "trailer": {
  68 + "value": {
  69 + "/Root": "1 0 R",
  70 + "/Size": 7
  71 + }
  72 + }
  73 + }
  74 + }
  75 +}
qpdf/qtest/qpdf/qjson-bad-object-key.out 0 โ†’ 100644
  1 +qpdf: qjson-bad-object-key.json: offset 181: object key should be "trailer" or "obj:n n R"
qpdf/qtest/qpdf/qjson-bad-pdf-version1.json 0 โ†’ 100644
  1 +{
  2 + "version": 2,
  3 + "parameters": {
  4 + "decodelevel": "none"
  5 + },
  6 + "qpdf": {
  7 + "jsonversion": 2,
  8 + "pdfversion": "potato",
  9 + "maxobjectid": 6,
  10 + "objects": {
  11 + "obj:1 0 R": {
  12 + "value": {
  13 + "/Pages": "2 0 R",
  14 + "/Type": "/Catalog"
  15 + }
  16 + },
  17 + "obj:2 0 R": {
  18 + "value": {
  19 + "/Count": 1,
  20 + "/Kids": [
  21 + "3 0 R"
  22 + ],
  23 + "/Type": "/Pages"
  24 + }
  25 + },
  26 + "obj:3 0 R": {
  27 + "value": {
  28 + "/Contents": "4 0 R",
  29 + "/MediaBox": [
  30 + 0,
  31 + 0,
  32 + 612,
  33 + 792
  34 + ],
  35 + "/Parent": "2 0 R",
  36 + "/Resources": {
  37 + "/Font": {
  38 + "/F1": "6 0 R"
  39 + },
  40 + "/ProcSet": "5 0 R"
  41 + },
  42 + "/Type": "/Page"
  43 + }
  44 + },
  45 + "obj:4 0 R": {
  46 + "stream": {
  47 + "data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
  48 + "dict": {}
  49 + }
  50 + },
  51 + "obj:5 0 R": {
  52 + "value": [
  53 + "/PDF",
  54 + "/Text"
  55 + ]
  56 + },
  57 + "obj:6 0 R": {
  58 + "value": {
  59 + "/BaseFont": "/Helvetica",
  60 + "/Encoding": "/WinAnsiEncoding",
  61 + "/Subtype": "/Type1",
  62 + "/Type": "/Font"
  63 + }
  64 + },
  65 + "trailer": {
  66 + "value": {
  67 + "/Root": "1 0 R",
  68 + "/Size": 7
  69 + }
  70 + }
  71 + }
  72 + }
  73 +}
qpdf/qtest/qpdf/qjson-bad-pdf-version1.out 0 โ†’ 100644
  1 +qpdf: qjson-bad-pdf-version1.json: offset 119: invalid PDF version (must be x.y)
qpdf/qtest/qpdf/qjson-bad-pdf-version2.json 0 โ†’ 100644
  1 +{
  2 + "version": 2,
  3 + "parameters": {
  4 + "decodelevel": "none"
  5 + },
  6 + "qpdf": {
  7 + "jsonversion": 2,
  8 + "pdfversion": [],
  9 + "maxobjectid": 6,
  10 + "objects": {
  11 + "obj:1 0 R": {
  12 + "value": {
  13 + "/Pages": "2 0 R",
  14 + "/Type": "/Catalog"
  15 + }
  16 + },
  17 + "obj:2 0 R": {
  18 + "value": {
  19 + "/Count": 1,
  20 + "/Kids": [
  21 + "3 0 R"
  22 + ],
  23 + "/Type": "/Pages"
  24 + }
  25 + },
  26 + "obj:3 0 R": {
  27 + "value": {
  28 + "/Contents": "4 0 R",
  29 + "/MediaBox": [
  30 + 0,
  31 + 0,
  32 + 612,
  33 + 792
  34 + ],
  35 + "/Parent": "2 0 R",
  36 + "/Resources": {
  37 + "/Font": {
  38 + "/F1": "6 0 R"
  39 + },
  40 + "/ProcSet": "5 0 R"
  41 + },
  42 + "/Type": "/Page"
  43 + }
  44 + },
  45 + "obj:4 0 R": {
  46 + "stream": {
  47 + "data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
  48 + "dict": {}
  49 + }
  50 + },
  51 + "obj:5 0 R": {
  52 + "value": [
  53 + "/PDF",
  54 + "/Text"
  55 + ]
  56 + },
  57 + "obj:6 0 R": {
  58 + "value": {
  59 + "/BaseFont": "/Helvetica",
  60 + "/Encoding": "/WinAnsiEncoding",
  61 + "/Subtype": "/Type1",
  62 + "/Type": "/Font"
  63 + }
  64 + },
  65 + "trailer": {
  66 + "value": {
  67 + "/Root": "1 0 R",
  68 + "/Size": 7
  69 + }
  70 + }
  71 + }
  72 + }
  73 +}
qpdf/qtest/qpdf/qjson-bad-pdf-version2.out 0 โ†’ 100644
  1 +qpdf: qjson-bad-pdf-version2.json: offset 119: invalid PDF version (must be x.y)
qpdf/qtest/qpdf/qjson-no-json-version.json 0 โ†’ 100644
  1 +{
  2 + "version": 2,
  3 + "parameters": {
  4 + "decodelevel": "none"
  5 + },
  6 + "qpdf": {
  7 + "pdfversion": "1.3",
  8 + "maxobjectid": 6,
  9 + "objects": {
  10 + "obj:1 0 R": {
  11 + "value": {
  12 + "/Pages": "2 0 R",
  13 + "/Type": "/Catalog"
  14 + }
  15 + },
  16 + "obj:2 0 R": {
  17 + "value": {
  18 + "/Count": 1,
  19 + "/Kids": [
  20 + "3 0 R"
  21 + ],
  22 + "/Type": "/Pages"
  23 + }
  24 + },
  25 + "obj:3 0 R": {
  26 + "value": {
  27 + "/Contents": "4 0 R",
  28 + "/MediaBox": [
  29 + 0,
  30 + 0,
  31 + 612,
  32 + 792
  33 + ],
  34 + "/Parent": "2 0 R",
  35 + "/Resources": {
  36 + "/Font": {
  37 + "/F1": "6 0 R"
  38 + },
  39 + "/ProcSet": "5 0 R"
  40 + },
  41 + "/Type": "/Page"
  42 + }
  43 + },
  44 + "obj:4 0 R": {
  45 + "stream": {
  46 + "data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
  47 + "dict": {}
  48 + }
  49 + },
  50 + "obj:5 0 R": {
  51 + "value": [
  52 + "/PDF",
  53 + "/Text"
  54 + ]
  55 + },
  56 + "obj:6 0 R": {
  57 + "value": {
  58 + "/BaseFont": "/Helvetica",
  59 + "/Encoding": "/WinAnsiEncoding",
  60 + "/Subtype": "/Type1",
  61 + "/Type": "/Font"
  62 + }
  63 + },
  64 + "trailer": {
  65 + "value": {
  66 + "/Root": "1 0 R",
  67 + "/Size": 7
  68 + }
  69 + }
  70 + }
  71 + }
  72 +}
qpdf/qtest/qpdf/qjson-no-json-version.out 0 โ†’ 100644
  1 +qpdf: qjson-no-json-version.json: "qpdf.jsonversion" was not seen
qpdf/qtest/qpdf/qjson-no-pdf-version.json 0 โ†’ 100644
  1 +{
  2 + "version": 2,
  3 + "parameters": {
  4 + "decodelevel": "none"
  5 + },
  6 + "qpdf": {
  7 + "jsonversion": 2,
  8 + "maxobjectid": 6,
  9 + "objects": {
  10 + "obj:1 0 R": {
  11 + "value": {
  12 + "/Pages": "2 0 R",
  13 + "/Type": "/Catalog"
  14 + }
  15 + },
  16 + "obj:2 0 R": {
  17 + "value": {
  18 + "/Count": 1,
  19 + "/Kids": [
  20 + "3 0 R"
  21 + ],
  22 + "/Type": "/Pages"
  23 + }
  24 + },
  25 + "obj:3 0 R": {
  26 + "value": {
  27 + "/Contents": "4 0 R",
  28 + "/MediaBox": [
  29 + 0,
  30 + 0,
  31 + 612,
  32 + 792
  33 + ],
  34 + "/Parent": "2 0 R",
  35 + "/Resources": {
  36 + "/Font": {
  37 + "/F1": "6 0 R"
  38 + },
  39 + "/ProcSet": "5 0 R"
  40 + },
  41 + "/Type": "/Page"
  42 + }
  43 + },
  44 + "obj:4 0 R": {
  45 + "stream": {
  46 + "data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
  47 + "dict": {}
  48 + }
  49 + },
  50 + "obj:5 0 R": {
  51 + "value": [
  52 + "/PDF",
  53 + "/Text"
  54 + ]
  55 + },
  56 + "obj:6 0 R": {
  57 + "value": {
  58 + "/BaseFont": "/Helvetica",
  59 + "/Encoding": "/WinAnsiEncoding",
  60 + "/Subtype": "/Type1",
  61 + "/Type": "/Font"
  62 + }
  63 + },
  64 + "trailer": {
  65 + "value": {
  66 + "/Root": "1 0 R",
  67 + "/Size": 7
  68 + }
  69 + }
  70 + }
  71 + }
  72 +}
qpdf/qtest/qpdf/qjson-no-pdf-version.out 0 โ†’ 100644
  1 +qpdf: qjson-no-pdf-version.json: "qpdf.pdfversion" was not seen
qpdf/qtest/qpdf/qjson-no-qpdf-object.json 0 โ†’ 100644
  1 +{
  2 + "potato": "salad"
  3 +}
qpdf/qtest/qpdf/qjson-no-qpdf-object.out 0 โ†’ 100644
  1 +qpdf: qjson-no-qpdf-object.json: "qpdf" object was not seen
qpdf/qtest/qpdf/qjson-object-not-dict.json 0 โ†’ 100644
  1 +{
  2 + "version": 2,
  3 + "parameters": {
  4 + "decodelevel": "none"
  5 + },
  6 + "qpdf": {
  7 + "jsonversion": 2,
  8 + "pdfversion": "1.3",
  9 + "maxobjectid": 6,
  10 + "objects": {
  11 + "obj:1 0 R": "potato",
  12 + "obj:2 0 R": {
  13 + "value": {
  14 + "/Count": 1,
  15 + "/Kids": [
  16 + "3 0 R"
  17 + ],
  18 + "/Type": "/Pages"
  19 + }
  20 + },
  21 + "obj:3 0 R": {
  22 + "value": {
  23 + "/Contents": "4 0 R",
  24 + "/MediaBox": [
  25 + 0,
  26 + 0,
  27 + 612,
  28 + 792
  29 + ],
  30 + "/Parent": "2 0 R",
  31 + "/Resources": {
  32 + "/Font": {
  33 + "/F1": "6 0 R"
  34 + },
  35 + "/ProcSet": "5 0 R"
  36 + },
  37 + "/Type": "/Page"
  38 + }
  39 + },
  40 + "obj:4 0 R": {
  41 + "stream": {
  42 + "data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
  43 + "dict": {}
  44 + }
  45 + },
  46 + "obj:5 0 R": {
  47 + "value": [
  48 + "/PDF",
  49 + "/Text"
  50 + ]
  51 + },
  52 + "obj:6 0 R": {
  53 + "value": {
  54 + "/BaseFont": "/Helvetica",
  55 + "/Encoding": "/WinAnsiEncoding",
  56 + "/Subtype": "/Type1",
  57 + "/Type": "/Font"
  58 + }
  59 + },
  60 + "trailer": {
  61 + "value": {
  62 + "/Root": "1 0 R",
  63 + "/Size": 7
  64 + }
  65 + }
  66 + }
  67 + }
  68 +}
qpdf/qtest/qpdf/qjson-object-not-dict.out 0 โ†’ 100644
  1 +qpdf: qjson-object-not-dict.json: offset 184: "obj:1 0 R" must be a dictionary
qpdf/qtest/qpdf/qjson-objects-not-dict.json 0 โ†’ 100644
  1 +{
  2 + "qpdf": {
  3 + "jsonversion": 2,
  4 + "pdfversion": "1.7",
  5 + "objects": false
  6 + }
  7 +}
qpdf/qtest/qpdf/qjson-objects-not-dict.out 0 โ†’ 100644
  1 +qpdf: qjson-objects-not-dict.json: offset 77: "objects" must be a dictionary
qpdf/qtest/qpdf/qjson-stream-dict-not-dict.json 0 โ†’ 100644
  1 +{
  2 + "qpdf": {
  3 + "jsonversion": 2,
  4 + "pdfversion": "1.7",
  5 + "objects": {
  6 + "obj:1 0 R": {
  7 + "stream": {
  8 + "dict": "quack"
  9 + }
  10 + }
  11 + }
  12 + }
  13 +}
qpdf/qtest/qpdf/qjson-stream-dict-not-dict.out 0 โ†’ 100644
  1 +qpdf: qjson-stream-dict-not-dict.json: offset 137: "stream.dict" must be a dictionary
qpdf/qtest/qpdf/qjson-stream-not-dict.json 0 โ†’ 100644
  1 +{
  2 + "qpdf": {
  3 + "jsonversion": 2,
  4 + "pdfversion": "1.7",
  5 + "objects": {
  6 + "obj:1 0 R": {
  7 + "stream": 3
  8 + }
  9 + }
  10 + }
  11 +}
qpdf/qtest/qpdf/qjson-stream-not-dict.out 0 โ†’ 100644
  1 +qpdf: qjson-stream-not-dict.json: offset 118: "stream" must be a dictionary
qpdf/qtest/qpdf/qjson-top-level-array.json 0 โ†’ 100644
  1 +["potato"]
qpdf/qtest/qpdf/qjson-top-level-array.out 0 โ†’ 100644
  1 +qpdf: qjson-top-level-array.json: QPDF JSON must be a dictionary
qpdf/qtest/qpdf/qjson-top-level-scalar.json 0 โ†’ 100644
  1 +"potato"
qpdf/qtest/qpdf/qjson-top-level-scalar.out 0 โ†’ 100644
  1 +qpdf: qjson-top-level-scalar.json: QPDF JSON must be a dictionary
qpdf/qtest/qpdf/qjson-trailer-not-dict.json 0 โ†’ 100644
  1 +{
  2 + "version": 2,
  3 + "parameters": {
  4 + "decodelevel": "none"
  5 + },
  6 + "qpdf": {
  7 + "jsonversion": 2,
  8 + "pdfversion": "1.3",
  9 + "maxobjectid": 6,
  10 + "objects": {
  11 + "obj:1 0 R": {
  12 + "value": {
  13 + "/Pages": "2 0 R",
  14 + "/Type": "/Catalog"
  15 + }
  16 + },
  17 + "obj:2 0 R": {
  18 + "value": {
  19 + "/Count": 1,
  20 + "/Kids": [
  21 + "3 0 R"
  22 + ],
  23 + "/Type": "/Pages"
  24 + }
  25 + },
  26 + "obj:3 0 R": {
  27 + "value": {
  28 + "/Contents": "4 0 R",
  29 + "/MediaBox": [
  30 + 0,
  31 + 0,
  32 + 612,
  33 + 792
  34 + ],
  35 + "/Parent": "2 0 R",
  36 + "/Resources": {
  37 + "/Font": {
  38 + "/F1": "6 0 R"
  39 + },
  40 + "/ProcSet": "5 0 R"
  41 + },
  42 + "/Type": "/Page"
  43 + }
  44 + },
  45 + "obj:4 0 R": {
  46 + "stream": {
  47 + "data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
  48 + "dict": {}
  49 + }
  50 + },
  51 + "obj:5 0 R": {
  52 + "value": [
  53 + "/PDF",
  54 + "/Text"
  55 + ]
  56 + },
  57 + "obj:6 0 R": {
  58 + "value": {
  59 + "/BaseFont": "/Helvetica",
  60 + "/Encoding": "/WinAnsiEncoding",
  61 + "/Subtype": "/Type1",
  62 + "/Type": "/Font"
  63 + }
  64 + },
  65 + "trailer": {
  66 + "value": false,
  67 + }
  68 + }
  69 + }
  70 +}
qpdf/qtest/qpdf/qjson-trailer-not-dict.out 0 โ†’ 100644
  1 +qpdf: qjson-trailer-not-dict.json: offset 1326: "trailer.value" must be a dictionary
qpdf/qtest/qpdf/qjson-trailer-stream.json 0 โ†’ 100644
  1 +{
  2 + "version": 2,
  3 + "parameters": {
  4 + "decodelevel": "none"
  5 + },
  6 + "qpdf": {
  7 + "jsonversion": 2,
  8 + "pdfversion": "1.3",
  9 + "maxobjectid": 6,
  10 + "objects": {
  11 + "obj:1 0 R": {
  12 + "value": {
  13 + "/Pages": "2 0 R",
  14 + "/Type": "/Catalog"
  15 + }
  16 + },
  17 + "obj:2 0 R": {
  18 + "value": {
  19 + "/Count": 1,
  20 + "/Kids": [
  21 + "3 0 R"
  22 + ],
  23 + "/Type": "/Pages"
  24 + }
  25 + },
  26 + "obj:3 0 R": {
  27 + "value": {
  28 + "/Contents": "4 0 R",
  29 + "/MediaBox": [
  30 + 0,
  31 + 0,
  32 + 612,
  33 + 792
  34 + ],
  35 + "/Parent": "2 0 R",
  36 + "/Resources": {
  37 + "/Font": {
  38 + "/F1": "6 0 R"
  39 + },
  40 + "/ProcSet": "5 0 R"
  41 + },
  42 + "/Type": "/Page"
  43 + }
  44 + },
  45 + "obj:4 0 R": {
  46 + "stream": {
  47 + "data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
  48 + "dict": {}
  49 + }
  50 + },
  51 + "obj:5 0 R": {
  52 + "value": [
  53 + "/PDF",
  54 + "/Text"
  55 + ]
  56 + },
  57 + "obj:6 0 R": {
  58 + "value": {
  59 + "/BaseFont": "/Helvetica",
  60 + "/Encoding": "/WinAnsiEncoding",
  61 + "/Subtype": "/Type1",
  62 + "/Type": "/Font"
  63 + }
  64 + },
  65 + "trailer": {
  66 + "stream": {},
  67 + }
  68 + }
  69 + }
  70 +}
qpdf/qtest/qpdf/qjson-trailer-stream.out 0 โ†’ 100644
  1 +qpdf: qjson-trailer-stream.json: offset 1327: the trailer may not be a stream