mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-23 03:18:59 +00:00
Add private methods QPDF::damagedPDF
This commit is contained in:
parent
b948366280
commit
5ccab4be03
@ -1194,6 +1194,22 @@ class QPDF
|
|||||||
std::shared_ptr<QPDFObject> const& object,
|
std::shared_ptr<QPDFObject> const& object,
|
||||||
qpdf_offset_t end_before_space,
|
qpdf_offset_t end_before_space,
|
||||||
qpdf_offset_t end_after_space);
|
qpdf_offset_t end_after_space);
|
||||||
|
static QPDFExc damagedPDF(
|
||||||
|
std::shared_ptr<InputSource> const& input,
|
||||||
|
std::string const& object,
|
||||||
|
qpdf_offset_t offset,
|
||||||
|
std::string const& message);
|
||||||
|
QPDFExc damagedPDF(
|
||||||
|
std::shared_ptr<InputSource> const& input,
|
||||||
|
qpdf_offset_t offset,
|
||||||
|
std::string const& message);
|
||||||
|
QPDFExc damagedPDF(
|
||||||
|
std::string const& object,
|
||||||
|
qpdf_offset_t offset,
|
||||||
|
std::string const& message);
|
||||||
|
QPDFExc damagedPDF(std::string const& object, std::string const& message);
|
||||||
|
QPDFExc damagedPDF(qpdf_offset_t offset, std::string const& message);
|
||||||
|
QPDFExc damagedPDF(std::string const& message);
|
||||||
|
|
||||||
// Calls finish() on the pipeline when done but does not delete it
|
// Calls finish() on the pipeline when done but does not delete it
|
||||||
bool pipeStreamData(
|
bool pipeStreamData(
|
||||||
|
460
libqpdf/QPDF.cc
460
libqpdf/QPDF.cc
@ -481,7 +481,7 @@ QPDF::parse(char const* password)
|
|||||||
PatternFinder hf(*this, &QPDF::findHeader);
|
PatternFinder hf(*this, &QPDF::findHeader);
|
||||||
if (!this->m->file->findFirst("%PDF-", 0, 1024, hf)) {
|
if (!this->m->file->findFirst("%PDF-", 0, 1024, hf)) {
|
||||||
QTC::TC("qpdf", "QPDF not a pdf file");
|
QTC::TC("qpdf", "QPDF not a pdf file");
|
||||||
warn(qpdf_e_damaged_pdf, "", 0, "can't find PDF header");
|
warn(damagedPDF("", 0, "can't find PDF header"));
|
||||||
// QPDFWriter writes files that usually require at least
|
// QPDFWriter writes files that usually require at least
|
||||||
// version 1.2 for /FlateDecode
|
// version 1.2 for /FlateDecode
|
||||||
this->m->pdf_version = "1.2";
|
this->m->pdf_version = "1.2";
|
||||||
@ -503,24 +503,15 @@ QPDF::parse(char const* password)
|
|||||||
try {
|
try {
|
||||||
if (xref_offset == 0) {
|
if (xref_offset == 0) {
|
||||||
QTC::TC("qpdf", "QPDF can't find startxref");
|
QTC::TC("qpdf", "QPDF can't find startxref");
|
||||||
throw QPDFExc(
|
throw damagedPDF("", 0, "can't find startxref");
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"",
|
|
||||||
0,
|
|
||||||
"can't find startxref");
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
read_xref(xref_offset);
|
read_xref(xref_offset);
|
||||||
} catch (QPDFExc&) {
|
} catch (QPDFExc&) {
|
||||||
throw;
|
throw;
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
"", 0, std::string("error reading xref: ") + e.what());
|
||||||
this->m->file->getName(),
|
|
||||||
"",
|
|
||||||
0,
|
|
||||||
std::string("error reading xref: ") + e.what());
|
|
||||||
}
|
}
|
||||||
} catch (QPDFExc& e) {
|
} catch (QPDFExc& e) {
|
||||||
if (this->m->attempt_recovery) {
|
if (this->m->attempt_recovery) {
|
||||||
@ -589,13 +580,9 @@ QPDF::reconstruct_xref(QPDFExc& e)
|
|||||||
|
|
||||||
this->m->reconstructed_xref = true;
|
this->m->reconstructed_xref = true;
|
||||||
|
|
||||||
warn(qpdf_e_damaged_pdf, "", 0, "file is damaged");
|
warn(damagedPDF("", 0, "file is damaged"));
|
||||||
warn(e);
|
warn(e);
|
||||||
warn(
|
warn(damagedPDF("", 0, "Attempting to reconstruct cross-reference table"));
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
"",
|
|
||||||
0,
|
|
||||||
"Attempting to reconstruct cross-reference table");
|
|
||||||
|
|
||||||
// Delete all references to type 1 (uncompressed) objects
|
// Delete all references to type 1 (uncompressed) objects
|
||||||
std::set<QPDFObjGen> to_delete;
|
std::set<QPDFObjGen> to_delete;
|
||||||
@ -655,13 +642,10 @@ QPDF::reconstruct_xref(QPDFExc& e)
|
|||||||
// with bad startxref pointers even when they have object
|
// with bad startxref pointers even when they have object
|
||||||
// streams.
|
// streams.
|
||||||
|
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"",
|
"",
|
||||||
0,
|
0,
|
||||||
"unable to find trailer "
|
"unable to find trailer dictionary while recovering damaged file");
|
||||||
"dictionary while recovering damaged file");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We could iterate through the objects looking for streams and
|
// We could iterate through the objects looking for streams and
|
||||||
@ -716,11 +700,8 @@ QPDF::read_xref(qpdf_offset_t xref_offset)
|
|||||||
if ((strncmp(buf, "xref", 4) == 0) && QUtil::is_space(buf[4])) {
|
if ((strncmp(buf, "xref", 4) == 0) && QUtil::is_space(buf[4])) {
|
||||||
if (skipped_space) {
|
if (skipped_space) {
|
||||||
QTC::TC("qpdf", "QPDF xref skipped space");
|
QTC::TC("qpdf", "QPDF xref skipped space");
|
||||||
warn(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
"", 0, "extraneous whitespace seen before xref"));
|
||||||
"",
|
|
||||||
0,
|
|
||||||
"extraneous whitespace seen before xref");
|
|
||||||
}
|
}
|
||||||
QTC::TC(
|
QTC::TC(
|
||||||
"qpdf",
|
"qpdf",
|
||||||
@ -741,22 +722,12 @@ QPDF::read_xref(qpdf_offset_t xref_offset)
|
|||||||
}
|
}
|
||||||
if (visited.count(xref_offset) != 0) {
|
if (visited.count(xref_offset) != 0) {
|
||||||
QTC::TC("qpdf", "QPDF xref loop");
|
QTC::TC("qpdf", "QPDF xref loop");
|
||||||
throw QPDFExc(
|
throw damagedPDF("", 0, "loop detected following xref tables");
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"",
|
|
||||||
0,
|
|
||||||
"loop detected following xref tables");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->m->trailer.isInitialized()) {
|
if (!this->m->trailer.isInitialized()) {
|
||||||
throw QPDFExc(
|
throw damagedPDF("", 0, "unable to find trailer while reading xref");
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"",
|
|
||||||
0,
|
|
||||||
"unable to find trailer while reading xref");
|
|
||||||
}
|
}
|
||||||
int size = this->m->trailer.getKey("/Size").getIntValueAsInt();
|
int size = this->m->trailer.getKey("/Size").getIntValueAsInt();
|
||||||
int max_obj = 0;
|
int max_obj = 0;
|
||||||
@ -768,14 +739,12 @@ QPDF::read_xref(qpdf_offset_t xref_offset)
|
|||||||
}
|
}
|
||||||
if ((size < 1) || (size - 1 != max_obj)) {
|
if ((size < 1) || (size - 1 != max_obj)) {
|
||||||
QTC::TC("qpdf", "QPDF xref size mismatch");
|
QTC::TC("qpdf", "QPDF xref size mismatch");
|
||||||
warn(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
"",
|
"",
|
||||||
0,
|
0,
|
||||||
(std::string("reported number of objects (") +
|
("reported number of objects (" + std::to_string(size) +
|
||||||
std::to_string(size) +
|
|
||||||
") is not one plus the highest object number (" +
|
") is not one plus the highest object number (" +
|
||||||
std::to_string(max_obj) + ")"));
|
std::to_string(max_obj) + ")")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We no longer need the deleted_objects table, so go ahead and
|
// We no longer need the deleted_objects table, so go ahead and
|
||||||
@ -899,11 +868,7 @@ QPDF::parse_xrefEntry(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (invalid) {
|
if (invalid) {
|
||||||
warn(
|
warn(damagedPDF("xref table", "accepting invalid xref table entry"));
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
"xref table",
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"accepting invalid xref table entry");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f1 = QUtil::string_to_ll(f1_str.c_str());
|
f1 = QUtil::string_to_ll(f1_str.c_str());
|
||||||
@ -929,12 +894,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
|
|||||||
int bytes = 0;
|
int bytes = 0;
|
||||||
if (!parse_xrefFirst(line, obj, num, bytes)) {
|
if (!parse_xrefFirst(line, obj, num, bytes)) {
|
||||||
QTC::TC("qpdf", "QPDF invalid xref");
|
QTC::TC("qpdf", "QPDF invalid xref");
|
||||||
throw QPDFExc(
|
throw damagedPDF("xref table", "xref syntax invalid");
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"xref table",
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"xref syntax invalid");
|
|
||||||
}
|
}
|
||||||
this->m->file->seek(this->m->file->getLastOffset() + bytes, SEEK_SET);
|
this->m->file->seek(this->m->file->getLastOffset() + bytes, SEEK_SET);
|
||||||
for (qpdf_offset_t i = obj; i - num < obj; ++i) {
|
for (qpdf_offset_t i = obj; i - num < obj; ++i) {
|
||||||
@ -949,11 +909,8 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
|
|||||||
char type = '\0';
|
char type = '\0';
|
||||||
if (!parse_xrefEntry(xref_entry, f1, f2, type)) {
|
if (!parse_xrefEntry(xref_entry, f1, f2, type)) {
|
||||||
QTC::TC("qpdf", "QPDF invalid xref entry");
|
QTC::TC("qpdf", "QPDF invalid xref entry");
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"xref table",
|
"xref table",
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"invalid xref entry (obj=" + std::to_string(i) + ")");
|
"invalid xref entry (obj=" + std::to_string(i) + ")");
|
||||||
}
|
}
|
||||||
if (type == 'f') {
|
if (type == 'f') {
|
||||||
@ -978,12 +935,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
|
|||||||
readObject(this->m->file, "trailer", QPDFObjGen(), false);
|
readObject(this->m->file, "trailer", QPDFObjGen(), false);
|
||||||
if (!cur_trailer.isDictionary()) {
|
if (!cur_trailer.isDictionary()) {
|
||||||
QTC::TC("qpdf", "QPDF missing trailer");
|
QTC::TC("qpdf", "QPDF missing trailer");
|
||||||
throw QPDFExc(
|
throw damagedPDF("", "expected trailer dictionary");
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"",
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"expected trailer dictionary");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->m->trailer.isInitialized()) {
|
if (!this->m->trailer.isInitialized()) {
|
||||||
@ -991,22 +943,12 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
|
|||||||
|
|
||||||
if (!this->m->trailer.hasKey("/Size")) {
|
if (!this->m->trailer.hasKey("/Size")) {
|
||||||
QTC::TC("qpdf", "QPDF trailer lacks size");
|
QTC::TC("qpdf", "QPDF trailer lacks size");
|
||||||
throw QPDFExc(
|
throw damagedPDF("trailer", "trailer dictionary lacks /Size key");
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"trailer",
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"trailer dictionary lacks /Size key");
|
|
||||||
}
|
}
|
||||||
if (!this->m->trailer.getKey("/Size").isInteger()) {
|
if (!this->m->trailer.getKey("/Size").isInteger()) {
|
||||||
QTC::TC("qpdf", "QPDF trailer size not integer");
|
QTC::TC("qpdf", "QPDF trailer size not integer");
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
"trailer", "/Size key in trailer dictionary is not an integer");
|
||||||
this->m->file->getName(),
|
|
||||||
"trailer",
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"/Size key in trailer dictionary is not "
|
|
||||||
"an integer");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1021,12 +963,8 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
|
|||||||
(void)read_xrefStream(
|
(void)read_xrefStream(
|
||||||
cur_trailer.getKey("/XRefStm").getIntValue());
|
cur_trailer.getKey("/XRefStm").getIntValue());
|
||||||
} else {
|
} else {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
"xref stream", xref_offset, "invalid /XRefStm");
|
||||||
this->m->file->getName(),
|
|
||||||
"xref stream",
|
|
||||||
xref_offset,
|
|
||||||
"invalid /XRefStm");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1039,13 +977,8 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
|
|||||||
if (cur_trailer.hasKey("/Prev")) {
|
if (cur_trailer.hasKey("/Prev")) {
|
||||||
if (!cur_trailer.getKey("/Prev").isInteger()) {
|
if (!cur_trailer.getKey("/Prev").isInteger()) {
|
||||||
QTC::TC("qpdf", "QPDF trailer prev not integer");
|
QTC::TC("qpdf", "QPDF trailer prev not integer");
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
"trailer", "/Prev key in trailer dictionary is not an integer");
|
||||||
this->m->file->getName(),
|
|
||||||
"trailer",
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"/Prev key in trailer dictionary is not "
|
|
||||||
"an integer");
|
|
||||||
}
|
}
|
||||||
QTC::TC("qpdf", "QPDF prev key in trailer dictionary");
|
QTC::TC("qpdf", "QPDF prev key in trailer dictionary");
|
||||||
xref_offset = cur_trailer.getKey("/Prev").getIntValue();
|
xref_offset = cur_trailer.getKey("/Prev").getIntValue();
|
||||||
@ -1078,12 +1011,7 @@ QPDF::read_xrefStream(qpdf_offset_t xref_offset)
|
|||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
QTC::TC("qpdf", "QPDF can't find xref");
|
QTC::TC("qpdf", "QPDF can't find xref");
|
||||||
throw QPDFExc(
|
throw damagedPDF("", xref_offset, "xref not found");
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"",
|
|
||||||
xref_offset,
|
|
||||||
"xref not found");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return xref_offset;
|
return xref_offset;
|
||||||
@ -1101,13 +1029,10 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj)
|
|||||||
W_obj.getArrayItem(2).isInteger() &&
|
W_obj.getArrayItem(2).isInteger() &&
|
||||||
dict.getKey("/Size").isInteger() &&
|
dict.getKey("/Size").isInteger() &&
|
||||||
(Index_obj.isArray() || Index_obj.isNull()))) {
|
(Index_obj.isArray() || Index_obj.isNull()))) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"xref stream",
|
"xref stream",
|
||||||
xref_offset,
|
xref_offset,
|
||||||
"Cross-reference stream does not have"
|
"Cross-reference stream does not have proper /W and /Index keys");
|
||||||
" proper /W and /Index keys");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int W[3];
|
int W[3];
|
||||||
@ -1116,24 +1041,18 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj)
|
|||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
W[i] = W_obj.getArrayItem(i).getIntValueAsInt();
|
W[i] = W_obj.getArrayItem(i).getIntValueAsInt();
|
||||||
if (W[i] > max_bytes) {
|
if (W[i] > max_bytes) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"xref stream",
|
"xref stream",
|
||||||
xref_offset,
|
xref_offset,
|
||||||
"Cross-reference stream's /W contains"
|
"Cross-reference stream's /W contains impossibly large values");
|
||||||
" impossibly large values");
|
|
||||||
}
|
}
|
||||||
entry_size += toS(W[i]);
|
entry_size += toS(W[i]);
|
||||||
}
|
}
|
||||||
if (entry_size == 0) {
|
if (entry_size == 0) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"xref stream",
|
"xref stream",
|
||||||
xref_offset,
|
xref_offset,
|
||||||
"Cross-reference stream's /W indicates"
|
"Cross-reference stream's /W indicates entry size of 0");
|
||||||
" entry size of 0");
|
|
||||||
}
|
}
|
||||||
unsigned long long max_num_entries =
|
unsigned long long max_num_entries =
|
||||||
static_cast<unsigned long long>(-1) / entry_size;
|
static_cast<unsigned long long>(-1) / entry_size;
|
||||||
@ -1142,21 +1061,17 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj)
|
|||||||
if (Index_obj.isArray()) {
|
if (Index_obj.isArray()) {
|
||||||
int n_index = Index_obj.getArrayNItems();
|
int n_index = Index_obj.getArrayNItems();
|
||||||
if ((n_index % 2) || (n_index < 2)) {
|
if ((n_index % 2) || (n_index < 2)) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"xref stream",
|
"xref stream",
|
||||||
xref_offset,
|
xref_offset,
|
||||||
"Cross-reference stream's /Index has an"
|
"Cross-reference stream's /Index has an invalid number of "
|
||||||
" invalid number of values");
|
"values");
|
||||||
}
|
}
|
||||||
for (int i = 0; i < n_index; ++i) {
|
for (int i = 0; i < n_index; ++i) {
|
||||||
if (Index_obj.getArrayItem(i).isInteger()) {
|
if (Index_obj.getArrayItem(i).isInteger()) {
|
||||||
indx.push_back(Index_obj.getArrayItem(i).getIntValue());
|
indx.push_back(Index_obj.getArrayItem(i).getIntValue());
|
||||||
} else {
|
} else {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"xref stream",
|
"xref stream",
|
||||||
xref_offset,
|
xref_offset,
|
||||||
("Cross-reference stream's /Index's item " +
|
("Cross-reference stream's /Index's item " +
|
||||||
@ -1174,13 +1089,10 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj)
|
|||||||
size_t num_entries = 0;
|
size_t num_entries = 0;
|
||||||
for (size_t i = 1; i < indx.size(); i += 2) {
|
for (size_t i = 1; i < indx.size(); i += 2) {
|
||||||
if (indx.at(i) > QIntC::to_longlong(max_num_entries - num_entries)) {
|
if (indx.at(i) > QIntC::to_longlong(max_num_entries - num_entries)) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"xref stream",
|
"xref stream",
|
||||||
xref_offset,
|
xref_offset,
|
||||||
("Cross-reference stream claims to contain"
|
("Cross-reference stream claims to contain too many entries: " +
|
||||||
" too many entries: " +
|
|
||||||
std::to_string(indx.at(i)) + " " +
|
std::to_string(indx.at(i)) + " " +
|
||||||
std::to_string(max_num_entries) + " " +
|
std::to_string(max_num_entries) + " " +
|
||||||
std::to_string(num_entries)));
|
std::to_string(num_entries)));
|
||||||
@ -1196,13 +1108,10 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj)
|
|||||||
size_t actual_size = bp->getSize();
|
size_t actual_size = bp->getSize();
|
||||||
|
|
||||||
if (expected_size != actual_size) {
|
if (expected_size != actual_size) {
|
||||||
QPDFExc x(
|
QPDFExc x = damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"xref stream",
|
"xref stream",
|
||||||
xref_offset,
|
xref_offset,
|
||||||
("Cross-reference stream data has the wrong size;"
|
("Cross-reference stream data has the wrong size; expected = " +
|
||||||
" expected = " +
|
|
||||||
std::to_string(expected_size) +
|
std::to_string(expected_size) +
|
||||||
"; actual = " + std::to_string(actual_size)));
|
"; actual = " + std::to_string(actual_size)));
|
||||||
if (expected_size > actual_size) {
|
if (expected_size > actual_size) {
|
||||||
@ -1286,13 +1195,9 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj)
|
|||||||
|
|
||||||
if (dict.hasKey("/Prev")) {
|
if (dict.hasKey("/Prev")) {
|
||||||
if (!dict.getKey("/Prev").isInteger()) {
|
if (!dict.getKey("/Prev").isInteger()) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"xref stream",
|
"xref stream",
|
||||||
this->m->file->getLastOffset(),
|
"/Prev key in xref stream dictionary is not an integer");
|
||||||
"/Prev key in xref stream dictionary is not "
|
|
||||||
"an integer");
|
|
||||||
}
|
}
|
||||||
QTC::TC("qpdf", "QPDF prev key in xref stream dictionary");
|
QTC::TC("qpdf", "QPDF prev key in xref stream dictionary");
|
||||||
xref_offset = dict.getKey("/Prev").getIntValue();
|
xref_offset = dict.getKey("/Prev").getIntValue();
|
||||||
@ -1351,11 +1256,8 @@ QPDF::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2, bool overwrite)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"xref stream",
|
"xref stream",
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"unknown xref stream entry type " + std::to_string(f0));
|
"unknown xref stream entry type " + std::to_string(f0));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1522,12 +1424,8 @@ QPDF::readObject(
|
|||||||
// Nothing in the PDF spec appears to allow empty objects, but
|
// Nothing in the PDF spec appears to allow empty objects, but
|
||||||
// they have been encountered in actual PDF files and Adobe
|
// they have been encountered in actual PDF files and Adobe
|
||||||
// Reader appears to ignore them.
|
// Reader appears to ignore them.
|
||||||
warn(QPDFExc(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
input, input->getLastOffset(), "empty object treated as null"));
|
||||||
input->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
input->getLastOffset(),
|
|
||||||
"empty object treated as null"));
|
|
||||||
} else if (object.isDictionary() && (!in_object_stream)) {
|
} else if (object.isDictionary() && (!in_object_stream)) {
|
||||||
// check for stream
|
// check for stream
|
||||||
qpdf_offset_t cur_offset = input->tell();
|
qpdf_offset_t cur_offset = input->tell();
|
||||||
@ -1569,34 +1467,27 @@ QPDF::readObject(
|
|||||||
// of not having seen a newline.
|
// of not having seen a newline.
|
||||||
QTC::TC("qpdf", "QPDF stream with CR only");
|
QTC::TC("qpdf", "QPDF stream with CR only");
|
||||||
input->unreadCh(ch);
|
input->unreadCh(ch);
|
||||||
warn(QPDFExc(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
input,
|
||||||
input->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
input->tell(),
|
input->tell(),
|
||||||
"stream keyword followed"
|
"stream keyword followed by carriage return "
|
||||||
" by carriage return only"));
|
"only"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (QUtil::is_space(ch)) {
|
} else if (QUtil::is_space(ch)) {
|
||||||
warn(QPDFExc(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
input,
|
||||||
input->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
input->tell(),
|
input->tell(),
|
||||||
"stream keyword followed by"
|
"stream keyword followed by extraneous whitespace"));
|
||||||
" extraneous whitespace"));
|
|
||||||
done = false;
|
done = false;
|
||||||
} else {
|
} else {
|
||||||
QTC::TC("qpdf", "QPDF stream without newline");
|
QTC::TC("qpdf", "QPDF stream without newline");
|
||||||
input->unreadCh(ch);
|
input->unreadCh(ch);
|
||||||
warn(QPDFExc(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
input,
|
||||||
input->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
input->tell(),
|
input->tell(),
|
||||||
"stream keyword not followed"
|
"stream keyword not followed by proper line "
|
||||||
" by proper line terminator"));
|
"terminator"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1612,21 +1503,15 @@ QPDF::readObject(
|
|||||||
|
|
||||||
if (dict.count("/Length") == 0) {
|
if (dict.count("/Length") == 0) {
|
||||||
QTC::TC("qpdf", "QPDF stream without length");
|
QTC::TC("qpdf", "QPDF stream without length");
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
input, offset, "stream dictionary lacks /Length key");
|
||||||
input->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
offset,
|
|
||||||
"stream dictionary lacks /Length key");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QPDFObjectHandle length_obj = dict["/Length"];
|
QPDFObjectHandle length_obj = dict["/Length"];
|
||||||
if (!length_obj.isInteger()) {
|
if (!length_obj.isInteger()) {
|
||||||
QTC::TC("qpdf", "QPDF stream length not integer");
|
QTC::TC("qpdf", "QPDF stream length not integer");
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
input,
|
||||||
input->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
offset,
|
offset,
|
||||||
"/Length key in stream dictionary is not "
|
"/Length key in stream dictionary is not "
|
||||||
"an integer");
|
"an integer");
|
||||||
@ -1640,12 +1525,8 @@ QPDF::readObject(
|
|||||||
QPDFTokenizer::Token(
|
QPDFTokenizer::Token(
|
||||||
QPDFTokenizer::tt_word, "endstream"))) {
|
QPDFTokenizer::tt_word, "endstream"))) {
|
||||||
QTC::TC("qpdf", "QPDF missing endstream");
|
QTC::TC("qpdf", "QPDF missing endstream");
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
input, input->getLastOffset(), "expected endstream");
|
||||||
input->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
input->getLastOffset(),
|
|
||||||
"expected endstream");
|
|
||||||
}
|
}
|
||||||
} catch (QPDFExc& e) {
|
} catch (QPDFExc& e) {
|
||||||
if (this->m->attempt_recovery) {
|
if (this->m->attempt_recovery) {
|
||||||
@ -1689,12 +1570,8 @@ QPDF::recoverStreamLength(
|
|||||||
{
|
{
|
||||||
// Try to reconstruct stream length by looking for
|
// Try to reconstruct stream length by looking for
|
||||||
// endstream or endobj
|
// endstream or endobj
|
||||||
warn(QPDFExc(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
input, stream_offset, "attempting to recover stream length"));
|
||||||
input->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
stream_offset,
|
|
||||||
"attempting to recover stream length"));
|
|
||||||
|
|
||||||
PatternFinder ef(*this, &QPDF::findEndstream);
|
PatternFinder ef(*this, &QPDF::findEndstream);
|
||||||
size_t length = 0;
|
size_t length = 0;
|
||||||
@ -1734,18 +1611,13 @@ QPDF::recoverStreamLength(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
warn(QPDFExc(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
input,
|
||||||
input->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
stream_offset,
|
stream_offset,
|
||||||
"unable to recover stream data;"
|
"unable to recover stream data; treating stream as empty"));
|
||||||
" treating stream as empty"));
|
|
||||||
} else {
|
} else {
|
||||||
warn(QPDFExc(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
input,
|
||||||
input->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
stream_offset,
|
stream_offset,
|
||||||
"recovered stream length: " + std::to_string(length)));
|
"recovered stream length: " + std::to_string(length)));
|
||||||
}
|
}
|
||||||
@ -1795,11 +1667,7 @@ QPDF::readObjectAtOffset(
|
|||||||
// ignore these.
|
// ignore these.
|
||||||
if (offset == 0) {
|
if (offset == 0) {
|
||||||
QTC::TC("qpdf", "QPDF bogus 0 offset", 0);
|
QTC::TC("qpdf", "QPDF bogus 0 offset", 0);
|
||||||
warn(
|
warn(damagedPDF(0, "object has offset 0"));
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->last_object_description,
|
|
||||||
0,
|
|
||||||
"object has offset 0");
|
|
||||||
return QPDFObjectHandle::newNull();
|
return QPDFObjectHandle::newNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1820,33 +1688,19 @@ QPDF::readObjectAtOffset(
|
|||||||
try {
|
try {
|
||||||
if (!(objidok && genok && objok)) {
|
if (!(objidok && genok && objok)) {
|
||||||
QTC::TC("qpdf", "QPDF expected n n obj");
|
QTC::TC("qpdf", "QPDF expected n n obj");
|
||||||
throw QPDFExc(
|
throw damagedPDF(offset, "expected n n obj");
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
offset,
|
|
||||||
"expected n n obj");
|
|
||||||
}
|
}
|
||||||
int objid = QUtil::string_to_int(tobjid.getValue().c_str());
|
int objid = QUtil::string_to_int(tobjid.getValue().c_str());
|
||||||
int generation = QUtil::string_to_int(tgen.getValue().c_str());
|
int generation = QUtil::string_to_int(tgen.getValue().c_str());
|
||||||
og = QPDFObjGen(objid, generation);
|
og = QPDFObjGen(objid, generation);
|
||||||
if (objid == 0) {
|
if (objid == 0) {
|
||||||
QTC::TC("qpdf", "QPDF object id 0");
|
QTC::TC("qpdf", "QPDF object id 0");
|
||||||
throw QPDFExc(
|
throw damagedPDF(offset, "object with ID 0");
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
offset,
|
|
||||||
"object with ID 0");
|
|
||||||
}
|
}
|
||||||
if (check_og && (exp_og != og)) {
|
if (check_og && (exp_og != og)) {
|
||||||
QTC::TC("qpdf", "QPDF err wrong objid/generation");
|
QTC::TC("qpdf", "QPDF err wrong objid/generation");
|
||||||
QPDFExc e(
|
QPDFExc e =
|
||||||
qpdf_e_damaged_pdf,
|
damagedPDF(offset, "expected " + exp_og.unparse(' ') + " obj");
|
||||||
this->m->file->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
offset,
|
|
||||||
(std::string("expected ") + exp_og.unparse(' ') + " obj"));
|
|
||||||
if (try_recovery) {
|
if (try_recovery) {
|
||||||
// Will be retried below
|
// Will be retried below
|
||||||
throw e;
|
throw e;
|
||||||
@ -1870,14 +1724,12 @@ QPDF::readObjectAtOffset(
|
|||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
QTC::TC("qpdf", "QPDF object gone after xref reconstruction");
|
QTC::TC("qpdf", "QPDF object gone after xref reconstruction");
|
||||||
warn(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
"",
|
"",
|
||||||
0,
|
0,
|
||||||
std::string(
|
("object " + exp_og.unparse(' ') +
|
||||||
"object " + exp_og.unparse(' ') +
|
" not found in file after regenerating cross reference "
|
||||||
" not found in file after regenerating"
|
"table")));
|
||||||
" cross reference table"));
|
|
||||||
return QPDFObjectHandle::newNull();
|
return QPDFObjectHandle::newNull();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1890,11 +1742,7 @@ QPDF::readObjectAtOffset(
|
|||||||
if (!(readToken(this->m->file) ==
|
if (!(readToken(this->m->file) ==
|
||||||
QPDFTokenizer::Token(QPDFTokenizer::tt_word, "endobj"))) {
|
QPDFTokenizer::Token(QPDFTokenizer::tt_word, "endobj"))) {
|
||||||
QTC::TC("qpdf", "QPDF err expected endobj");
|
QTC::TC("qpdf", "QPDF err expected endobj");
|
||||||
warn(
|
warn(damagedPDF("expected endobj"));
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->last_object_description,
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"expected endobj");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUnresolved(og)) {
|
if (isUnresolved(og)) {
|
||||||
@ -1919,12 +1767,7 @@ QPDF::readObjectAtOffset(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw QPDFExc(
|
throw damagedPDF(m->file->tell(), "EOF after endobj");
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
this->m->file->tell(),
|
|
||||||
"EOF after endobj");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qpdf_offset_t end_after_space = this->m->file->tell();
|
qpdf_offset_t end_after_space = this->m->file->tell();
|
||||||
@ -1950,11 +1793,8 @@ QPDF::resolve(QPDFObjGen const& og)
|
|||||||
// indirectly in some key that has to be resolved during
|
// indirectly in some key that has to be resolved during
|
||||||
// object parsing, such as stream length.
|
// object parsing, such as stream length.
|
||||||
QTC::TC("qpdf", "QPDF recursion loop in resolve");
|
QTC::TC("qpdf", "QPDF recursion loop in resolve");
|
||||||
warn(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
"", "loop detected resolving object " + og.unparse(' ')));
|
||||||
"",
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
("loop detected resolving object " + og.unparse(' ')));
|
|
||||||
updateCache(og, QPDF_Null::create(), -1, -1);
|
updateCache(og, QPDF_Null::create(), -1, -1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1979,9 +1819,7 @@ QPDF::resolve(QPDFObjGen const& og)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"",
|
"",
|
||||||
0,
|
0,
|
||||||
("object " + og.unparse('/') +
|
("object " + og.unparse('/') +
|
||||||
@ -1990,12 +1828,11 @@ QPDF::resolve(QPDFObjGen const& og)
|
|||||||
} catch (QPDFExc& e) {
|
} catch (QPDFExc& e) {
|
||||||
warn(e);
|
warn(e);
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
warn(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
"",
|
"",
|
||||||
0,
|
0,
|
||||||
("object " + og.unparse('/') +
|
("object " + og.unparse('/') +
|
||||||
": error reading object: " + e.what()));
|
": error reading object: " + e.what())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2021,13 +1858,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
|
|||||||
// Force resolution of object stream
|
// Force resolution of object stream
|
||||||
QPDFObjectHandle obj_stream = getObjectByID(obj_stream_number, 0);
|
QPDFObjectHandle obj_stream = getObjectByID(obj_stream_number, 0);
|
||||||
if (!obj_stream.isStream()) {
|
if (!obj_stream.isStream()) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
"supposed object stream " + std::to_string(obj_stream_number) +
|
||||||
this->m->file->getName(),
|
" is not a stream");
|
||||||
this->m->last_object_description,
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
("supposed object stream " + std::to_string(obj_stream_number) +
|
|
||||||
" is not a stream"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For linearization data in the object, use the data from the
|
// For linearization data in the object, use the data from the
|
||||||
@ -2041,20 +1874,13 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
|
|||||||
QPDFObjectHandle dict = obj_stream.getDict();
|
QPDFObjectHandle dict = obj_stream.getDict();
|
||||||
if (!dict.isDictionaryOfType("/ObjStm")) {
|
if (!dict.isDictionaryOfType("/ObjStm")) {
|
||||||
QTC::TC("qpdf", "QPDF ERR object stream with wrong type");
|
QTC::TC("qpdf", "QPDF ERR object stream with wrong type");
|
||||||
warn(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
"supposed object stream " + std::to_string(obj_stream_number) +
|
||||||
this->m->last_object_description,
|
" has wrong type"));
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
("supposed object stream " + std::to_string(obj_stream_number) +
|
|
||||||
" has wrong type"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(dict.getKey("/N").isInteger() && dict.getKey("/First").isInteger())) {
|
if (!(dict.getKey("/N").isInteger() && dict.getKey("/First").isInteger())) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
("object stream " + std::to_string(obj_stream_number) +
|
("object stream " + std::to_string(obj_stream_number) +
|
||||||
" has incorrect keys"));
|
" has incorrect keys"));
|
||||||
}
|
}
|
||||||
@ -2077,10 +1903,8 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
|
|||||||
QPDFTokenizer::Token toffset = readToken(input);
|
QPDFTokenizer::Token toffset = readToken(input);
|
||||||
if (!((tnum.getType() == QPDFTokenizer::tt_integer) &&
|
if (!((tnum.getType() == QPDFTokenizer::tt_integer) &&
|
||||||
(toffset.getType() == QPDFTokenizer::tt_integer))) {
|
(toffset.getType() == QPDFTokenizer::tt_integer))) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
input,
|
||||||
input->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
input->getLastOffset(),
|
input->getLastOffset(),
|
||||||
"expected integer in object stream header");
|
"expected integer in object stream header");
|
||||||
}
|
}
|
||||||
@ -2618,12 +2442,7 @@ QPDF::getRoot()
|
|||||||
{
|
{
|
||||||
QPDFObjectHandle root = this->m->trailer.getKey("/Root");
|
QPDFObjectHandle root = this->m->trailer.getKey("/Root");
|
||||||
if (!root.isDictionary()) {
|
if (!root.isDictionary()) {
|
||||||
throw QPDFExc(
|
throw damagedPDF("", 0, "unable to find /Root dictionary");
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"",
|
|
||||||
0,
|
|
||||||
"unable to find /Root dictionary");
|
|
||||||
}
|
}
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
@ -2751,9 +2570,8 @@ QPDF::pipeStreamData(
|
|||||||
size_t to_read = (sizeof(buf) < length ? sizeof(buf) : length);
|
size_t to_read = (sizeof(buf) < length ? sizeof(buf) : length);
|
||||||
size_t len = file->read(buf, to_read);
|
size_t len = file->read(buf, to_read);
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
file,
|
||||||
file->getName(),
|
|
||||||
"",
|
"",
|
||||||
file->getLastOffset(),
|
file->getLastOffset(),
|
||||||
"unexpected EOF reading stream data");
|
"unexpected EOF reading stream data");
|
||||||
@ -2772,9 +2590,8 @@ QPDF::pipeStreamData(
|
|||||||
QTC::TC("qpdf", "QPDF decoding error warning");
|
QTC::TC("qpdf", "QPDF decoding error warning");
|
||||||
qpdf_for_warning.warn(
|
qpdf_for_warning.warn(
|
||||||
// line-break
|
// line-break
|
||||||
QPDFExc(
|
damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
file,
|
||||||
file->getName(),
|
|
||||||
"",
|
"",
|
||||||
file->getLastOffset(),
|
file->getLastOffset(),
|
||||||
("error decoding stream data for object " +
|
("error decoding stream data for object " +
|
||||||
@ -2782,9 +2599,8 @@ QPDF::pipeStreamData(
|
|||||||
if (will_retry) {
|
if (will_retry) {
|
||||||
qpdf_for_warning.warn(
|
qpdf_for_warning.warn(
|
||||||
// line-break
|
// line-break
|
||||||
QPDFExc(
|
damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
file,
|
||||||
file->getName(),
|
|
||||||
"",
|
"",
|
||||||
file->getLastOffset(),
|
file->getLastOffset(),
|
||||||
"stream will be re-processed without"
|
"stream will be re-processed without"
|
||||||
@ -2848,18 +2664,72 @@ QPDF::pipeForeignStreamData(
|
|||||||
will_retry);
|
will_retry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Throw a generic exception when we lack context for something
|
||||||
|
// more specific. New code should not use this. This method exists
|
||||||
|
// to improve somewhat from calling assert in very old code.
|
||||||
void
|
void
|
||||||
QPDF::stopOnError(std::string const& message)
|
QPDF::stopOnError(std::string const& message)
|
||||||
{
|
{
|
||||||
// Throw a generic exception when we lack context for something
|
throw damagedPDF("", message);
|
||||||
// more specific. New code should not use this. This method exists
|
}
|
||||||
// to improve somewhat from calling assert in very old code.
|
|
||||||
throw QPDFExc(
|
// Return an exception of type qpdf_e_damaged_pdf.
|
||||||
qpdf_e_damaged_pdf,
|
QPDFExc
|
||||||
this->m->file->getName(),
|
QPDF::damagedPDF(
|
||||||
"",
|
std::shared_ptr<InputSource> const& input,
|
||||||
this->m->file->getLastOffset(),
|
std::string const& object,
|
||||||
message);
|
qpdf_offset_t offset,
|
||||||
|
std::string const& message)
|
||||||
|
{
|
||||||
|
return QPDFExc(
|
||||||
|
qpdf_e_damaged_pdf, input->getName(), object, offset, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an exception of type qpdf_e_damaged_pdf. The object is taken from
|
||||||
|
// m->last_object_description.
|
||||||
|
QPDFExc
|
||||||
|
QPDF::damagedPDF(
|
||||||
|
std::shared_ptr<InputSource> const& input,
|
||||||
|
qpdf_offset_t offset,
|
||||||
|
std::string const& message)
|
||||||
|
{
|
||||||
|
return damagedPDF(input, m->last_object_description, offset, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an exception of type qpdf_e_damaged_pdf. The filename is taken from
|
||||||
|
// m->file.
|
||||||
|
QPDFExc
|
||||||
|
QPDF::damagedPDF(
|
||||||
|
std::string const& object, qpdf_offset_t offset, std::string const& message)
|
||||||
|
{
|
||||||
|
return QPDFExc(
|
||||||
|
qpdf_e_damaged_pdf, m->file->getName(), object, offset, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an exception of type qpdf_e_damaged_pdf. The filename is taken from
|
||||||
|
// m->file and the offset from .m->file->getLastOffset().
|
||||||
|
QPDFExc
|
||||||
|
QPDF::damagedPDF(std::string const& object, std::string const& message)
|
||||||
|
{
|
||||||
|
return damagedPDF(object, m->file->getLastOffset(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an exception of type qpdf_e_damaged_pdf. The filename is taken from
|
||||||
|
// m->file and the object from .m->last_object_description.
|
||||||
|
QPDFExc
|
||||||
|
QPDF::damagedPDF(qpdf_offset_t offset, std::string const& message)
|
||||||
|
{
|
||||||
|
return damagedPDF(m->last_object_description, offset, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an exception of type qpdf_e_damaged_pdf. The filename is taken from
|
||||||
|
// m->file, the object from m->last_object_description and the offset from
|
||||||
|
// m->file->getLastOffset().
|
||||||
|
QPDFExc
|
||||||
|
QPDF::damagedPDF(std::string const& message)
|
||||||
|
{
|
||||||
|
return damagedPDF(
|
||||||
|
m->last_object_description, m->file->getLastOffset(), message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -804,21 +804,12 @@ QPDF::initializeEncryption()
|
|||||||
// Treating a missing ID as the empty string enables qpdf to
|
// Treating a missing ID as the empty string enables qpdf to
|
||||||
// decrypt some invalid encrypted files with no /ID that
|
// decrypt some invalid encrypted files with no /ID that
|
||||||
// poppler can read but Adobe Reader can't.
|
// poppler can read but Adobe Reader can't.
|
||||||
warn(
|
warn(damagedPDF("trailer", "invalid /ID in trailer dictionary"));
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
"trailer",
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"invalid /ID in trailer dictionary");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QPDFObjectHandle encryption_dict = this->m->trailer.getKey("/Encrypt");
|
QPDFObjectHandle encryption_dict = this->m->trailer.getKey("/Encrypt");
|
||||||
if (!encryption_dict.isDictionary()) {
|
if (!encryption_dict.isDictionary()) {
|
||||||
throw QPDFExc(
|
throw damagedPDF("/Encrypt in trailer dictionary is not a dictionary");
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"/Encrypt in trailer dictionary is not a dictionary");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(encryption_dict.getKey("/Filter").isName() &&
|
if (!(encryption_dict.getKey("/Filter").isName() &&
|
||||||
@ -835,8 +826,7 @@ QPDF::initializeEncryption()
|
|||||||
qpdf_e_unsupported,
|
qpdf_e_unsupported,
|
||||||
"encryption dictionary",
|
"encryption dictionary",
|
||||||
this->m->file->getLastOffset(),
|
this->m->file->getLastOffset(),
|
||||||
"file uses encryption SubFilters,"
|
"file uses encryption SubFilters, which qpdf does not support");
|
||||||
" which qpdf does not support");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(encryption_dict.getKey("/V").isInteger() &&
|
if (!(encryption_dict.getKey("/V").isInteger() &&
|
||||||
@ -844,13 +834,10 @@ QPDF::initializeEncryption()
|
|||||||
encryption_dict.getKey("/O").isString() &&
|
encryption_dict.getKey("/O").isString() &&
|
||||||
encryption_dict.getKey("/U").isString() &&
|
encryption_dict.getKey("/U").isString() &&
|
||||||
encryption_dict.getKey("/P").isInteger())) {
|
encryption_dict.getKey("/P").isInteger())) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"encryption dictionary",
|
"encryption dictionary",
|
||||||
this->m->file->getLastOffset(),
|
"some encryption dictionary parameters are missing or the wrong "
|
||||||
"some encryption dictionary parameters are missing "
|
"type");
|
||||||
"or the wrong type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int V = encryption_dict.getKey("/V").getIntValueAsInt();
|
int V = encryption_dict.getKey("/V").getIntValueAsInt();
|
||||||
@ -886,25 +873,18 @@ QPDF::initializeEncryption()
|
|||||||
pad_short_parameter(O, key_bytes);
|
pad_short_parameter(O, key_bytes);
|
||||||
pad_short_parameter(U, key_bytes);
|
pad_short_parameter(U, key_bytes);
|
||||||
if (!((O.length() == key_bytes) && (U.length() == key_bytes))) {
|
if (!((O.length() == key_bytes) && (U.length() == key_bytes))) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"encryption dictionary",
|
"encryption dictionary",
|
||||||
this->m->file->getLastOffset(),
|
"incorrect length for /O and/or /U in encryption dictionary");
|
||||||
"incorrect length for /O and/or /U in "
|
|
||||||
"encryption dictionary");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!(encryption_dict.getKey("/OE").isString() &&
|
if (!(encryption_dict.getKey("/OE").isString() &&
|
||||||
encryption_dict.getKey("/UE").isString() &&
|
encryption_dict.getKey("/UE").isString() &&
|
||||||
encryption_dict.getKey("/Perms").isString())) {
|
encryption_dict.getKey("/Perms").isString())) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"encryption dictionary",
|
"encryption dictionary",
|
||||||
this->m->file->getLastOffset(),
|
"some V=5 encryption dictionary parameters are missing or the "
|
||||||
"some V=5 encryption dictionary parameters are "
|
"wrong type");
|
||||||
"missing or the wrong type");
|
|
||||||
}
|
}
|
||||||
OE = encryption_dict.getKey("/OE").getStringValue();
|
OE = encryption_dict.getKey("/OE").getStringValue();
|
||||||
UE = encryption_dict.getKey("/UE").getStringValue();
|
UE = encryption_dict.getKey("/UE").getStringValue();
|
||||||
@ -1062,12 +1042,10 @@ QPDF::initializeEncryption()
|
|||||||
this->m->encp->encryption_key = recover_encryption_key_with_password(
|
this->m->encp->encryption_key = recover_encryption_key_with_password(
|
||||||
this->m->encp->provided_password, data, perms_valid);
|
this->m->encp->provided_password, data, perms_valid);
|
||||||
if (!perms_valid) {
|
if (!perms_valid) {
|
||||||
warn(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
"encryption dictionary",
|
"encryption dictionary",
|
||||||
this->m->file->getLastOffset(),
|
"/Perms field in encryption dictionary doesn't match expected "
|
||||||
"/Perms field in encryption dictionary"
|
"value"));
|
||||||
" doesn't match expected value");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1121,13 +1099,9 @@ QPDF::decryptString(std::string& str, QPDFObjGen const& og)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
warn(
|
warn(damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
"unknown encryption filter for strings (check /StrF in "
|
||||||
this->m->last_object_description,
|
"/Encrypt dictionary); strings may be decrypted improperly"));
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"unknown encryption filter for strings"
|
|
||||||
" (check /StrF in /Encrypt dictionary);"
|
|
||||||
" strings may be decrypted improperly");
|
|
||||||
// To avoid repeated warnings, reset cf_string. Assume
|
// To avoid repeated warnings, reset cf_string. Assume
|
||||||
// we'd want to use AES if V == 4.
|
// we'd want to use AES if V == 4.
|
||||||
this->m->encp->cf_string = e_aes;
|
this->m->encp->cf_string = e_aes;
|
||||||
@ -1166,13 +1140,9 @@ QPDF::decryptString(std::string& str, QPDFObjGen const& og)
|
|||||||
} catch (QPDFExc&) {
|
} catch (QPDFExc&) {
|
||||||
throw;
|
throw;
|
||||||
} catch (std::runtime_error& e) {
|
} catch (std::runtime_error& e) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
this->m->last_object_description,
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"error decrypting string for object " + og.unparse() + ": " +
|
"error decrypting string for object " + og.unparse() + ": " +
|
||||||
e.what());
|
e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1265,11 +1235,8 @@ QPDF::decryptStream(
|
|||||||
file->getName(),
|
file->getName(),
|
||||||
"",
|
"",
|
||||||
file->getLastOffset(),
|
file->getLastOffset(),
|
||||||
"unknown encryption filter for streams"
|
"unknown encryption filter for streams (check " +
|
||||||
" (check " +
|
method_source + "); streams may be decrypted improperly"));
|
||||||
method_source +
|
|
||||||
");"
|
|
||||||
" streams may be decrypted improperly"));
|
|
||||||
// To avoid repeated warnings, reset cf_stream. Assume
|
// To avoid repeated warnings, reset cf_stream. Assume
|
||||||
// we'd want to use AES if V == 4.
|
// we'd want to use AES if V == 4.
|
||||||
encp->cf_stream = e_aes;
|
encp->cf_stream = e_aes;
|
||||||
|
@ -186,24 +186,16 @@ QPDF::readLinearizationData()
|
|||||||
|
|
||||||
if (!(H.isArray() && O.isInteger() && E.isInteger() && N.isInteger() &&
|
if (!(H.isArray() && O.isInteger() && E.isInteger() && N.isInteger() &&
|
||||||
T.isInteger() && (P.isInteger() || P.isNull()))) {
|
T.isInteger() && (P.isInteger() || P.isNull()))) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"linearization dictionary",
|
"linearization dictionary",
|
||||||
this->m->file->getLastOffset(),
|
"some keys in linearization dictionary are of the wrong type");
|
||||||
"some keys in linearization dictionary are of "
|
|
||||||
"the wrong type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hint table array: offset length [ offset length ]
|
// Hint table array: offset length [ offset length ]
|
||||||
size_t n_H_items = toS(H.getArrayNItems());
|
size_t n_H_items = toS(H.getArrayNItems());
|
||||||
if (!((n_H_items == 2) || (n_H_items == 4))) {
|
if (!((n_H_items == 2) || (n_H_items == 4))) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
"linearization dictionary", "H has the wrong number of items");
|
||||||
this->m->file->getName(),
|
|
||||||
"linearization dictionary",
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"H has the wrong number of items");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> H_items;
|
std::vector<int> H_items;
|
||||||
@ -212,11 +204,8 @@ QPDF::readLinearizationData()
|
|||||||
if (oh.isInteger()) {
|
if (oh.isInteger()) {
|
||||||
H_items.push_back(oh.getIntValueAsInt());
|
H_items.push_back(oh.getIntValueAsInt());
|
||||||
} else {
|
} else {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"linearization dictionary",
|
"linearization dictionary",
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"some H items are of the wrong type");
|
"some H items are of the wrong type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,12 +238,8 @@ QPDF::readLinearizationData()
|
|||||||
// initialized from N, to pre-allocate memory, so make sure it's
|
// initialized from N, to pre-allocate memory, so make sure it's
|
||||||
// accurate and bail right now if it's not.
|
// accurate and bail right now if it's not.
|
||||||
if (N.getIntValue() != static_cast<long long>(getAllPages().size())) {
|
if (N.getIntValue() != static_cast<long long>(getAllPages().size())) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
"linearization hint table", "/N does not match number of pages");
|
||||||
this->m->file->getName(),
|
|
||||||
"linearization hint table",
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"/N does not match number of pages");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// file_size initialized by isLinearized()
|
// file_size initialized by isLinearized()
|
||||||
@ -297,11 +282,8 @@ QPDF::readLinearizationData()
|
|||||||
|
|
||||||
int HSi = HS.getIntValueAsInt();
|
int HSi = HS.getIntValueAsInt();
|
||||||
if ((HSi < 0) || (toS(HSi) >= h_size)) {
|
if ((HSi < 0) || (toS(HSi) >= h_size)) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"linearization hint table",
|
"linearization hint table",
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"/S (shared object) offset is out of bounds");
|
"/S (shared object) offset is out of bounds");
|
||||||
}
|
}
|
||||||
readHSharedObject(BitStream(h_buf + HSi, h_size - toS(HSi)));
|
readHSharedObject(BitStream(h_buf + HSi, h_size - toS(HSi)));
|
||||||
@ -309,11 +291,8 @@ QPDF::readLinearizationData()
|
|||||||
if (HO.isInteger()) {
|
if (HO.isInteger()) {
|
||||||
int HOi = HO.getIntValueAsInt();
|
int HOi = HO.getIntValueAsInt();
|
||||||
if ((HOi < 0) || (toS(HOi) >= h_size)) {
|
if ((HOi < 0) || (toS(HOi) >= h_size)) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
|
||||||
this->m->file->getName(),
|
|
||||||
"linearization hint table",
|
"linearization hint table",
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"/O (outline) offset is out of bounds");
|
"/O (outline) offset is out of bounds");
|
||||||
}
|
}
|
||||||
readHGeneric(
|
readHGeneric(
|
||||||
@ -331,12 +310,8 @@ QPDF::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length)
|
|||||||
qpdf_offset_t min_end_offset = oc.end_before_space;
|
qpdf_offset_t min_end_offset = oc.end_before_space;
|
||||||
qpdf_offset_t max_end_offset = oc.end_after_space;
|
qpdf_offset_t max_end_offset = oc.end_after_space;
|
||||||
if (!H.isStream()) {
|
if (!H.isStream()) {
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
"linearization dictionary", "hint table is not a stream");
|
||||||
this->m->file->getName(),
|
|
||||||
"linearization dictionary",
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"hint table is not a stream");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QPDFObjectHandle Hdict = H.getDict();
|
QPDFObjectHandle Hdict = H.getDict();
|
||||||
@ -362,12 +337,8 @@ QPDF::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length)
|
|||||||
*this->m->log->getError()
|
*this->m->log->getError()
|
||||||
<< "expected = " << computed_end << "; actual = " << min_end_offset
|
<< "expected = " << computed_end << "; actual = " << min_end_offset
|
||||||
<< ".." << max_end_offset << "\n";
|
<< ".." << max_end_offset << "\n";
|
||||||
throw QPDFExc(
|
throw damagedPDF(
|
||||||
qpdf_e_damaged_pdf,
|
"linearization dictionary", "hint table length mismatch");
|
||||||
this->m->file->getName(),
|
|
||||||
"linearization dictionary",
|
|
||||||
this->m->file->getLastOffset(),
|
|
||||||
"hint table length mismatch");
|
|
||||||
}
|
}
|
||||||
H.pipeStreamData(&pl, 0, qpdf_dl_specialized);
|
H.pipeStreamData(&pl, 0, qpdf_dl_specialized);
|
||||||
return Hdict;
|
return Hdict;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
char ch = '\xf7';
|
char ch = '\xf7';
|
||||||
if (ch < 0) {
|
if (ch < 0) {
|
||||||
|
Loading…
Reference in New Issue
Block a user