2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-06-05 03:40:53 +00:00

Implement top-level qpdf json parsing

This commit is contained in:
Jay Berkenbilt 2022-05-15 12:34:27 -04:00
parent 8d42eb2632
commit 7fa5d1773b
37 changed files with 1053 additions and 8 deletions

7
TODO
View File

@ -54,6 +54,13 @@ Soon: Break ground on "Document-level work"
Output JSON v2
==============
Try to never flatten pages tree. Make sure we do something reasonable
with pages tree repair. The problem is that if pages tree repair is
done as a side effect of running --json, the qpdf part of the json may
contain object numbers that aren't there. Maybe we need to indicate
whether pages tree repair has been done in the json, but this would
have to be known early in parsing, which is a problem.
General things to remember:
* Make sure all the information from --check and other informational

View File

@ -998,6 +998,7 @@ class QPDF
class JSONReactor: public JSON::Reactor
{
public:
JSONReactor(QPDF&, bool must_be_complete);
virtual ~JSONReactor() = default;
virtual void dictionaryStart() override;
virtual void arrayStart() override;
@ -1006,6 +1007,32 @@ class QPDF
virtual bool
dictionaryItem(std::string const& key, JSON const& value) override;
virtual bool arrayItem(JSON const& value) override;
private:
enum state_e {
st_initial,
st_top,
st_ignore,
st_qpdf,
st_objects_top,
st_trailer_top,
st_object_top,
st_stream,
st_object,
};
void containerStart();
void nestedState(std::string const& key, JSON const& value, state_e);
QPDF& pdf;
bool must_be_complete;
bool saw_qpdf;
bool saw_json_version;
bool saw_pdf_version;
bool saw_trailer;
state_e state;
state_e next_state;
std::vector<state_e> state_stack;
};
friend class JSONReactor;

View File

@ -1,42 +1,218 @@
#include <qpdf/QPDF.hh>
#include <qpdf/FileInputSource.hh>
#include <qpdf/QTC.hh>
#include <qpdf/QUtil.hh>
#include <regex>
namespace
{
class JSONExc: public std::runtime_error
{
public:
JSONExc(JSON const& value, std::string const& msg) :
std::runtime_error(
"offset " + QUtil::uint_to_string(value.getStart()) + ": " +
msg)
{
}
};
} // namespace
static std::regex PDF_VERSION_RE("^\\d+\\.\\d+$");
static std::regex OBJ_KEY_RE("^obj:(\\d+) (\\d+) R$");
QPDF::JSONReactor::JSONReactor(QPDF& pdf, bool must_be_complete) :
pdf(pdf),
must_be_complete(must_be_complete),
saw_qpdf(false),
saw_json_version(false),
saw_pdf_version(false),
saw_trailer(false),
state(st_initial),
next_state(st_top)
{
state_stack.push_back(st_initial);
}
void
QPDF::JSONReactor::containerStart()
{
state_stack.push_back(state);
state = next_state;
}
void
QPDF::JSONReactor::dictionaryStart()
{
// QXXXXQ
containerStart();
// QXXXQ
}
void
QPDF::JSONReactor::arrayStart()
{
// QXXXXQ
containerStart();
if (state == st_top) {
QTC::TC("qpdf", "QPDF_json top-level array");
throw std::runtime_error("QPDF JSON must be a dictionary");
}
// QXXXQ
}
void
QPDF::JSONReactor::containerEnd(JSON const& value)
{
// QXXXXQ
state = state_stack.back();
state_stack.pop_back();
if (state == st_initial) {
if (!this->saw_qpdf) {
QTC::TC("qpdf", "QPDF_json missing qpdf");
throw std::runtime_error("\"qpdf\" object was not seen");
}
if (!this->saw_json_version) {
QTC::TC("qpdf", "QPDF_json missing json version");
throw std::runtime_error("\"qpdf.jsonversion\" was not seen");
}
if (must_be_complete && !this->saw_pdf_version) {
QTC::TC("qpdf", "QPDF_json missing pdf version");
throw std::runtime_error("\"qpdf.pdfversion\" was not seen");
}
if (must_be_complete && !this->saw_trailer) {
/// QTC::TC("qpdf", "QPDF_json missing trailer");
throw std::runtime_error("\"qpdf.objects.trailer\" was not seen");
}
}
// QXXXQ
}
void
QPDF::JSONReactor::topLevelScalar()
{
// QXXXXQ
QTC::TC("qpdf", "QPDF_json top-level scalar");
throw std::runtime_error("QPDF JSON must be a dictionary");
}
void
QPDF::JSONReactor::nestedState(
std::string const& key, JSON const& value, state_e next)
{
// Use this method when the next state is for processing a nested
// dictionary.
if (!value.isDictionary()) {
throw JSONExc(value, "\"" + key + "\" must be a dictionary");
}
this->next_state = next;
}
bool
QPDF::JSONReactor::dictionaryItem(std::string const& key, JSON const& value)
{
// QXXXXQ
if (state == st_ignore) {
// ignore
} else if (state == st_top) {
if (key == "qpdf") {
this->saw_qpdf = true;
nestedState(key, value, st_qpdf);
} else {
// Ignore all other fields for forward compatibility.
// Don't use nestedState since this can be any type.
next_state = st_ignore;
}
} else if (state == st_qpdf) {
if (key == "jsonversion") {
this->saw_json_version = true;
std::string v;
if (!(value.getNumber(v) && (v == "2"))) {
QTC::TC("qpdf", "QPDF_json bad json version");
throw JSONExc(value, "only JSON version 2 is supported");
}
} else if (key == "pdfversion") {
this->saw_pdf_version = true;
bool version_okay = false;
std::string v;
if (value.getString(v)) {
std::smatch m;
if (std::regex_match(v, m, PDF_VERSION_RE)) {
version_okay = true;
this->pdf.m->pdf_version = v;
}
}
if (!version_okay) {
QTC::TC("qpdf", "QPDF_json bad pdf version");
throw JSONExc(value, "invalid PDF version (must be x.y)");
}
} else if (key == "objects") {
nestedState(key, value, st_objects_top);
} else {
// ignore unknown keys for forward compatibility
}
} else if (state == st_objects_top) {
std::smatch m;
if (key == "trailer") {
this->saw_trailer = true;
nestedState(key, value, st_trailer_top);
// QXXXQ
} else if (std::regex_match(key, m, OBJ_KEY_RE)) {
nestedState(key, value, st_object_top);
// QXXXQ
} else {
QTC::TC("qpdf", "QPDF_json bad object key");
throw JSONExc(
value, "object key should be \"trailer\" or \"obj:n n R\"");
}
} else if (state == st_object_top) {
if (key == "value") {
// Don't use nestedState since this can have any type.
next_state = st_object;
// QXXXQ
} else if (key == "stream") {
nestedState(key, value, st_stream);
// QXXXQ
} else {
// Ignore unknown keys for forward compatibility
}
} else if (state == st_trailer_top) {
if (key == "value") {
// The trailer must be a dictionary, so we can use nestedState.
nestedState("trailer.value", value, st_object);
// QXXXQ
} else if (key == "stream") {
QTC::TC("qpdf", "QPDF_json trailer stream");
throw JSONExc(value, "the trailer may not be a stream");
} else {
// Ignore unknown keys for forward compatibility
}
} else if (state == st_stream) {
if (key == "dict") {
// Since a stream dictionary must be a dictionary, we can
// use nestedState to transition to st_value.
nestedState("stream.dict", value, st_object);
// QXXXQ
} else if (key == "data") {
// QXXXQ
} else if (key == "datafile") {
// QXXXQ
} else {
// Ignore unknown keys for forward compatibility.
next_state = st_ignore;
}
} else if (state == st_object) {
// QXXXQ
} else {
throw std::logic_error(
"QPDF_json: unknown state " + QUtil::int_to_string(state));
}
// QXXXQ
return true;
}
bool
QPDF::JSONReactor::arrayItem(JSON const& value)
{
// QXXXXQ
// QXXXQ
return true;
}
@ -65,7 +241,12 @@ QPDF::updateFromJSON(std::shared_ptr<InputSource> is)
}
void
QPDF::importJSON(std::shared_ptr<InputSource>, bool must_be_complete)
QPDF::importJSON(std::shared_ptr<InputSource> is, bool must_be_complete)
{
// QXXXQ
JSONReactor reactor(*this, must_be_complete);
try {
JSON::parse(*is, &reactor);
} catch (std::runtime_error& e) {
throw std::runtime_error(is->getName() + ": " + e.what());
}
}

View File

@ -650,3 +650,12 @@ QPDFJob json encrypt duplicate key length 0
QPDFJob json encrypt missing password 0
QPDFJob json pages no file 0
qpdf-c called qpdf_empty_pdf 0
QPDF_json missing qpdf 0
QPDF_json missing json version 0
QPDF_json missing pdf version 0
QPDF_json top-level scalar 0
QPDF_json bad json version 0
QPDF_json bad pdf version 0
QPDF_json top-level array 0
QPDF_json bad object key 0
QPDF_json trailer stream 0

50
qpdf/qtest/qpdf-json.test Normal file
View File

@ -0,0 +1,50 @@
#!/usr/bin/env perl
require 5.008;
use warnings;
use strict;
unshift(@INC, '.');
require qpdf_test_helpers;
chdir("qpdf") or die "chdir testdir failed: $!\n";
require TestDriver;
cleanup();
my $td = new TestDriver('qpdf-json');
my $n_tests = 0;
my @badfiles = (
'no-qpdf-object',
'no-json-version',
'no-pdf-version',
'top-level-scalar',
'bad-json-version1',
'bad-json-version2',
'bad-pdf-version1',
'bad-pdf-version2',
'top-level-array',
'objects-not-dict',
'bad-object-key',
'object-not-dict',
'stream-not-dict',
'stream-dict-not-dict',
'trailer-not-dict',
'trailer-stream',
);
$n_tests += scalar(@badfiles);
foreach my $f (@badfiles)
{
$td->runtest("bad: $f",
{$td->COMMAND =>
"qpdf --create-from-json=qjson-$f.json a.pdf"},
{$td->FILE => "qjson-$f.out", $td->EXIT_STATUS => 2},
$td->NORMALIZE_NEWLINES);
}
cleanup();
$td->report($n_tests);

View File

@ -0,0 +1,73 @@
{
"version": 2,
"parameters": {
"decodelevel": "none"
},
"qpdf": {
"jsonversion": 16059,
"pdfversion": "1.3",
"maxobjectid": 6,
"objects": {
"obj:1 0 R": {
"value": {
"/Pages": "2 0 R",
"/Type": "/Catalog"
}
},
"obj:2 0 R": {
"value": {
"/Count": 1,
"/Kids": [
"3 0 R"
],
"/Type": "/Pages"
}
},
"obj:3 0 R": {
"value": {
"/Contents": "4 0 R",
"/MediaBox": [
0,
0,
612,
792
],
"/Parent": "2 0 R",
"/Resources": {
"/Font": {
"/F1": "6 0 R"
},
"/ProcSet": "5 0 R"
},
"/Type": "/Page"
}
},
"obj:4 0 R": {
"stream": {
"data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
"dict": {}
}
},
"obj:5 0 R": {
"value": [
"/PDF",
"/Text"
]
},
"obj:6 0 R": {
"value": {
"/BaseFont": "/Helvetica",
"/Encoding": "/WinAnsiEncoding",
"/Subtype": "/Type1",
"/Type": "/Font"
}
},
"trailer": {
"value": {
"/Root": "1 0 R",
"/Size": 7
}
}
}
}
}

View File

@ -0,0 +1 @@
qpdf: qjson-bad-json-version1.json: offset 98: only JSON version 2 is supported

View File

@ -0,0 +1,73 @@
{
"version": 2,
"parameters": {
"decodelevel": "none"
},
"qpdf": {
"jsonversion": "potato",
"pdfversion": "1.3",
"maxobjectid": 6,
"objects": {
"obj:1 0 R": {
"value": {
"/Pages": "2 0 R",
"/Type": "/Catalog"
}
},
"obj:2 0 R": {
"value": {
"/Count": 1,
"/Kids": [
"3 0 R"
],
"/Type": "/Pages"
}
},
"obj:3 0 R": {
"value": {
"/Contents": "4 0 R",
"/MediaBox": [
0,
0,
612,
792
],
"/Parent": "2 0 R",
"/Resources": {
"/Font": {
"/F1": "6 0 R"
},
"/ProcSet": "5 0 R"
},
"/Type": "/Page"
}
},
"obj:4 0 R": {
"stream": {
"data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
"dict": {}
}
},
"obj:5 0 R": {
"value": [
"/PDF",
"/Text"
]
},
"obj:6 0 R": {
"value": {
"/BaseFont": "/Helvetica",
"/Encoding": "/WinAnsiEncoding",
"/Subtype": "/Type1",
"/Type": "/Font"
}
},
"trailer": {
"value": {
"/Root": "1 0 R",
"/Size": 7
}
}
}
}
}

View File

@ -0,0 +1 @@
qpdf: qjson-bad-json-version2.json: offset 98: only JSON version 2 is supported

View File

@ -0,0 +1,75 @@
{
"version": 2,
"parameters": {
"decodelevel": "none"
},
"qpdf": {
"jsonversion": 2,
"pdfversion": "1.3",
"maxobjectid": 6,
"objects": {
"potato": {
},
"obj:1 0 R": {
"value": {
"/Pages": "2 0 R",
"/Type": "/Catalog"
}
},
"obj:2 0 R": {
"value": {
"/Count": 1,
"/Kids": [
"3 0 R"
],
"/Type": "/Pages"
}
},
"obj:3 0 R": {
"value": {
"/Contents": "4 0 R",
"/MediaBox": [
0,
0,
612,
792
],
"/Parent": "2 0 R",
"/Resources": {
"/Font": {
"/F1": "6 0 R"
},
"/ProcSet": "5 0 R"
},
"/Type": "/Page"
}
},
"obj:4 0 R": {
"stream": {
"data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
"dict": {}
}
},
"obj:5 0 R": {
"value": [
"/PDF",
"/Text"
]
},
"obj:6 0 R": {
"value": {
"/BaseFont": "/Helvetica",
"/Encoding": "/WinAnsiEncoding",
"/Subtype": "/Type1",
"/Type": "/Font"
}
},
"trailer": {
"value": {
"/Root": "1 0 R",
"/Size": 7
}
}
}
}
}

View File

@ -0,0 +1 @@
qpdf: qjson-bad-object-key.json: offset 181: object key should be "trailer" or "obj:n n R"

View File

@ -0,0 +1,73 @@
{
"version": 2,
"parameters": {
"decodelevel": "none"
},
"qpdf": {
"jsonversion": 2,
"pdfversion": "potato",
"maxobjectid": 6,
"objects": {
"obj:1 0 R": {
"value": {
"/Pages": "2 0 R",
"/Type": "/Catalog"
}
},
"obj:2 0 R": {
"value": {
"/Count": 1,
"/Kids": [
"3 0 R"
],
"/Type": "/Pages"
}
},
"obj:3 0 R": {
"value": {
"/Contents": "4 0 R",
"/MediaBox": [
0,
0,
612,
792
],
"/Parent": "2 0 R",
"/Resources": {
"/Font": {
"/F1": "6 0 R"
},
"/ProcSet": "5 0 R"
},
"/Type": "/Page"
}
},
"obj:4 0 R": {
"stream": {
"data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
"dict": {}
}
},
"obj:5 0 R": {
"value": [
"/PDF",
"/Text"
]
},
"obj:6 0 R": {
"value": {
"/BaseFont": "/Helvetica",
"/Encoding": "/WinAnsiEncoding",
"/Subtype": "/Type1",
"/Type": "/Font"
}
},
"trailer": {
"value": {
"/Root": "1 0 R",
"/Size": 7
}
}
}
}
}

View File

@ -0,0 +1 @@
qpdf: qjson-bad-pdf-version1.json: offset 119: invalid PDF version (must be x.y)

View File

@ -0,0 +1,73 @@
{
"version": 2,
"parameters": {
"decodelevel": "none"
},
"qpdf": {
"jsonversion": 2,
"pdfversion": [],
"maxobjectid": 6,
"objects": {
"obj:1 0 R": {
"value": {
"/Pages": "2 0 R",
"/Type": "/Catalog"
}
},
"obj:2 0 R": {
"value": {
"/Count": 1,
"/Kids": [
"3 0 R"
],
"/Type": "/Pages"
}
},
"obj:3 0 R": {
"value": {
"/Contents": "4 0 R",
"/MediaBox": [
0,
0,
612,
792
],
"/Parent": "2 0 R",
"/Resources": {
"/Font": {
"/F1": "6 0 R"
},
"/ProcSet": "5 0 R"
},
"/Type": "/Page"
}
},
"obj:4 0 R": {
"stream": {
"data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
"dict": {}
}
},
"obj:5 0 R": {
"value": [
"/PDF",
"/Text"
]
},
"obj:6 0 R": {
"value": {
"/BaseFont": "/Helvetica",
"/Encoding": "/WinAnsiEncoding",
"/Subtype": "/Type1",
"/Type": "/Font"
}
},
"trailer": {
"value": {
"/Root": "1 0 R",
"/Size": 7
}
}
}
}
}

View File

@ -0,0 +1 @@
qpdf: qjson-bad-pdf-version2.json: offset 119: invalid PDF version (must be x.y)

View File

@ -0,0 +1,72 @@
{
"version": 2,
"parameters": {
"decodelevel": "none"
},
"qpdf": {
"pdfversion": "1.3",
"maxobjectid": 6,
"objects": {
"obj:1 0 R": {
"value": {
"/Pages": "2 0 R",
"/Type": "/Catalog"
}
},
"obj:2 0 R": {
"value": {
"/Count": 1,
"/Kids": [
"3 0 R"
],
"/Type": "/Pages"
}
},
"obj:3 0 R": {
"value": {
"/Contents": "4 0 R",
"/MediaBox": [
0,
0,
612,
792
],
"/Parent": "2 0 R",
"/Resources": {
"/Font": {
"/F1": "6 0 R"
},
"/ProcSet": "5 0 R"
},
"/Type": "/Page"
}
},
"obj:4 0 R": {
"stream": {
"data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
"dict": {}
}
},
"obj:5 0 R": {
"value": [
"/PDF",
"/Text"
]
},
"obj:6 0 R": {
"value": {
"/BaseFont": "/Helvetica",
"/Encoding": "/WinAnsiEncoding",
"/Subtype": "/Type1",
"/Type": "/Font"
}
},
"trailer": {
"value": {
"/Root": "1 0 R",
"/Size": 7
}
}
}
}
}

View File

@ -0,0 +1 @@
qpdf: qjson-no-json-version.json: "qpdf.jsonversion" was not seen

View File

@ -0,0 +1,72 @@
{
"version": 2,
"parameters": {
"decodelevel": "none"
},
"qpdf": {
"jsonversion": 2,
"maxobjectid": 6,
"objects": {
"obj:1 0 R": {
"value": {
"/Pages": "2 0 R",
"/Type": "/Catalog"
}
},
"obj:2 0 R": {
"value": {
"/Count": 1,
"/Kids": [
"3 0 R"
],
"/Type": "/Pages"
}
},
"obj:3 0 R": {
"value": {
"/Contents": "4 0 R",
"/MediaBox": [
0,
0,
612,
792
],
"/Parent": "2 0 R",
"/Resources": {
"/Font": {
"/F1": "6 0 R"
},
"/ProcSet": "5 0 R"
},
"/Type": "/Page"
}
},
"obj:4 0 R": {
"stream": {
"data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
"dict": {}
}
},
"obj:5 0 R": {
"value": [
"/PDF",
"/Text"
]
},
"obj:6 0 R": {
"value": {
"/BaseFont": "/Helvetica",
"/Encoding": "/WinAnsiEncoding",
"/Subtype": "/Type1",
"/Type": "/Font"
}
},
"trailer": {
"value": {
"/Root": "1 0 R",
"/Size": 7
}
}
}
}
}

View File

@ -0,0 +1 @@
qpdf: qjson-no-pdf-version.json: "qpdf.pdfversion" was not seen

View File

@ -0,0 +1,3 @@
{
"potato": "salad"
}

View File

@ -0,0 +1 @@
qpdf: qjson-no-qpdf-object.json: "qpdf" object was not seen

View File

@ -0,0 +1,68 @@
{
"version": 2,
"parameters": {
"decodelevel": "none"
},
"qpdf": {
"jsonversion": 2,
"pdfversion": "1.3",
"maxobjectid": 6,
"objects": {
"obj:1 0 R": "potato",
"obj:2 0 R": {
"value": {
"/Count": 1,
"/Kids": [
"3 0 R"
],
"/Type": "/Pages"
}
},
"obj:3 0 R": {
"value": {
"/Contents": "4 0 R",
"/MediaBox": [
0,
0,
612,
792
],
"/Parent": "2 0 R",
"/Resources": {
"/Font": {
"/F1": "6 0 R"
},
"/ProcSet": "5 0 R"
},
"/Type": "/Page"
}
},
"obj:4 0 R": {
"stream": {
"data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
"dict": {}
}
},
"obj:5 0 R": {
"value": [
"/PDF",
"/Text"
]
},
"obj:6 0 R": {
"value": {
"/BaseFont": "/Helvetica",
"/Encoding": "/WinAnsiEncoding",
"/Subtype": "/Type1",
"/Type": "/Font"
}
},
"trailer": {
"value": {
"/Root": "1 0 R",
"/Size": 7
}
}
}
}
}

View File

@ -0,0 +1 @@
qpdf: qjson-object-not-dict.json: offset 184: "obj:1 0 R" must be a dictionary

View File

@ -0,0 +1,7 @@
{
"qpdf": {
"jsonversion": 2,
"pdfversion": "1.7",
"objects": false
}
}

View File

@ -0,0 +1 @@
qpdf: qjson-objects-not-dict.json: offset 77: "objects" must be a dictionary

View File

@ -0,0 +1,13 @@
{
"qpdf": {
"jsonversion": 2,
"pdfversion": "1.7",
"objects": {
"obj:1 0 R": {
"stream": {
"dict": "quack"
}
}
}
}
}

View File

@ -0,0 +1 @@
qpdf: qjson-stream-dict-not-dict.json: offset 137: "stream.dict" must be a dictionary

View File

@ -0,0 +1,11 @@
{
"qpdf": {
"jsonversion": 2,
"pdfversion": "1.7",
"objects": {
"obj:1 0 R": {
"stream": 3
}
}
}
}

View File

@ -0,0 +1 @@
qpdf: qjson-stream-not-dict.json: offset 118: "stream" must be a dictionary

View File

@ -0,0 +1 @@
["potato"]

View File

@ -0,0 +1 @@
qpdf: qjson-top-level-array.json: QPDF JSON must be a dictionary

View File

@ -0,0 +1 @@
"potato"

View File

@ -0,0 +1 @@
qpdf: qjson-top-level-scalar.json: QPDF JSON must be a dictionary

View File

@ -0,0 +1,70 @@
{
"version": 2,
"parameters": {
"decodelevel": "none"
},
"qpdf": {
"jsonversion": 2,
"pdfversion": "1.3",
"maxobjectid": 6,
"objects": {
"obj:1 0 R": {
"value": {
"/Pages": "2 0 R",
"/Type": "/Catalog"
}
},
"obj:2 0 R": {
"value": {
"/Count": 1,
"/Kids": [
"3 0 R"
],
"/Type": "/Pages"
}
},
"obj:3 0 R": {
"value": {
"/Contents": "4 0 R",
"/MediaBox": [
0,
0,
612,
792
],
"/Parent": "2 0 R",
"/Resources": {
"/Font": {
"/F1": "6 0 R"
},
"/ProcSet": "5 0 R"
},
"/Type": "/Page"
}
},
"obj:4 0 R": {
"stream": {
"data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
"dict": {}
}
},
"obj:5 0 R": {
"value": [
"/PDF",
"/Text"
]
},
"obj:6 0 R": {
"value": {
"/BaseFont": "/Helvetica",
"/Encoding": "/WinAnsiEncoding",
"/Subtype": "/Type1",
"/Type": "/Font"
}
},
"trailer": {
"value": false,
}
}
}
}

View File

@ -0,0 +1 @@
qpdf: qjson-trailer-not-dict.json: offset 1326: "trailer.value" must be a dictionary

View File

@ -0,0 +1,70 @@
{
"version": 2,
"parameters": {
"decodelevel": "none"
},
"qpdf": {
"jsonversion": 2,
"pdfversion": "1.3",
"maxobjectid": 6,
"objects": {
"obj:1 0 R": {
"value": {
"/Pages": "2 0 R",
"/Type": "/Catalog"
}
},
"obj:2 0 R": {
"value": {
"/Count": 1,
"/Kids": [
"3 0 R"
],
"/Type": "/Pages"
}
},
"obj:3 0 R": {
"value": {
"/Contents": "4 0 R",
"/MediaBox": [
0,
0,
612,
792
],
"/Parent": "2 0 R",
"/Resources": {
"/Font": {
"/F1": "6 0 R"
},
"/ProcSet": "5 0 R"
},
"/Type": "/Page"
}
},
"obj:4 0 R": {
"stream": {
"data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
"dict": {}
}
},
"obj:5 0 R": {
"value": [
"/PDF",
"/Text"
]
},
"obj:6 0 R": {
"value": {
"/BaseFont": "/Helvetica",
"/Encoding": "/WinAnsiEncoding",
"/Subtype": "/Type1",
"/Type": "/Font"
}
},
"trailer": {
"stream": {},
}
}
}
}

View File

@ -0,0 +1 @@
qpdf: qjson-trailer-stream.json: offset 1327: the trailer may not be a stream