2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-12-22 10:58:58 +00:00

Add QPDF::Xref_table methods type, offset, stream_number and stream_index

This commit is contained in:
m-holger 2024-08-10 14:04:32 +01:00
parent d64b14912d
commit 5fc257f0f7
3 changed files with 75 additions and 55 deletions

View File

@ -1769,20 +1769,17 @@ QPDF::readObjectAtOffset(
if (try_recovery) { if (try_recovery) {
// Try again after reconstructing xref table // Try again after reconstructing xref table
m->xref_table.reconstruct(e); m->xref_table.reconstruct(e);
if (m->xref_table.count(exp_og) && (m->xref_table[exp_og].getType() == 1)) { if (m->xref_table.type(exp_og) == 1) {
qpdf_offset_t new_offset = m->xref_table[exp_og].getOffset();
QPDFObjectHandle result =
readObjectAtOffset(false, new_offset, description, exp_og, og, false);
QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset"); QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset");
return result; return readObjectAtOffset(
false, m->xref_table.offset(exp_og), description, exp_og, og, false);
} else { } else {
QTC::TC("qpdf", "QPDF object gone after xref reconstruction"); QTC::TC("qpdf", "QPDF object gone after xref reconstruction");
warn(damagedPDF( warn(damagedPDF(
"", "",
0, 0,
("object " + exp_og.unparse(' ') + ("object " + exp_og.unparse(' ') +
" not found in file after regenerating cross reference " " not found in file after regenerating cross reference table")));
"table")));
return QPDFObjectHandle::newNull(); return QPDFObjectHandle::newNull();
} }
} else { } else {
@ -1815,7 +1812,7 @@ QPDF::readObjectAtOffset(
} }
} }
qpdf_offset_t end_after_space = m->file->tell(); qpdf_offset_t end_after_space = m->file->tell();
if (skip_cache_if_in_xref && m->xref_table.count(og)) { if (skip_cache_if_in_xref && m->xref_table.type(og)) {
// Ordinarily, an object gets read here when resolved through xref table or stream. In // Ordinarily, an object gets read here when resolved through xref table or stream. In
// the special case of the xref stream and linearization hint tables, the offset comes // the special case of the xref stream and linearization hint tables, the offset comes
// from another source. For the specific case of xref streams, the xref stream is read // from another source. For the specific case of xref streams, the xref stream is read
@ -1867,33 +1864,32 @@ QPDF::resolve(QPDFObjGen og)
} }
ResolveRecorder rr(this, og); ResolveRecorder rr(this, og);
if (m->xref_table.count(og) != 0) { try {
QPDFXRefEntry const& entry = m->xref_table[og]; switch (m->xref_table.type(og)) {
try { case 0:
switch (entry.getType()) { break;
case 1: case 1:
{ {
qpdf_offset_t offset = entry.getOffset(); // Object stored in cache by readObjectAtOffset
// Object stored in cache by readObjectAtOffset QPDFObjGen a_og;
QPDFObjGen a_og; QPDFObjectHandle oh =
QPDFObjectHandle oh = readObjectAtOffset(true, offset, "", og, a_og, false); readObjectAtOffset(true, m->xref_table.offset(og), "", og, a_og, false);
}
break;
case 2:
resolveObjectsInStream(entry.getObjStreamNumber());
break;
default:
throw damagedPDF(
"", 0, ("object " + og.unparse('/') + " has unexpected xref entry type"));
} }
} catch (QPDFExc& e) { break;
warn(e);
} catch (std::exception& e) { case 2:
warn(damagedPDF( resolveObjectsInStream(m->xref_table.stream_number(og.getObj()));
"", 0, ("object " + og.unparse('/') + ": error reading object: " + e.what()))); break;
default:
throw damagedPDF(
"", 0, ("object " + og.unparse('/') + " has unexpected xref entry type"));
} }
} catch (QPDFExc& e) {
warn(e);
} catch (std::exception& e) {
warn(damagedPDF(
"", 0, ("object " + og.unparse('/') + ": error reading object: " + e.what())));
} }
if (isUnresolved(og)) { if (isUnresolved(og)) {
@ -2107,7 +2103,7 @@ QPDF::getObjectForParser(int id, int gen, bool parse_pdf)
if (auto iter = m->obj_cache.find(og); iter != m->obj_cache.end()) { if (auto iter = m->obj_cache.find(og); iter != m->obj_cache.end()) {
return iter->second.object; return iter->second.object;
} }
if (m->xref_table.count(og) || !m->xref_table.parsed) { if (m->xref_table.type(og) || !m->xref_table.parsed) {
return m->obj_cache.insert({og, QPDF_Unresolved::create(this, og)}).first->second.object; return m->obj_cache.insert({og, QPDF_Unresolved::create(this, og)}).first->second.object;
} }
if (parse_pdf) { if (parse_pdf) {
@ -2123,9 +2119,8 @@ QPDF::getObjectForJSON(int id, int gen)
auto [it, inserted] = m->obj_cache.try_emplace(og); auto [it, inserted] = m->obj_cache.try_emplace(og);
auto& obj = it->second.object; auto& obj = it->second.object;
if (inserted) { if (inserted) {
obj = (m->xref_table.parsed && !m->xref_table.count(og)) obj = (m->xref_table.parsed && !m->xref_table.type(og)) ? QPDF_Null::create(this, og)
? QPDF_Null::create(this, og) : QPDF_Unresolved::create(this, og);
: QPDF_Unresolved::create(this, og);
} }
return obj; return obj;
} }
@ -2135,7 +2130,7 @@ QPDF::getObject(QPDFObjGen const& og)
{ {
if (auto it = m->obj_cache.find(og); it != m->obj_cache.end()) { if (auto it = m->obj_cache.find(og); it != m->obj_cache.end()) {
return {it->second.object}; return {it->second.object};
} else if (m->xref_table.parsed && !m->xref_table.count(og)) { } else if (m->xref_table.parsed && !m->xref_table.type(og)) {
return QPDF_Null::create(); return QPDF_Null::create();
} else { } else {
auto result = m->obj_cache.try_emplace(og, QPDF_Unresolved::create(this, og), -1, -1); auto result = m->obj_cache.try_emplace(og, QPDF_Unresolved::create(this, og), -1, -1);

View File

@ -445,7 +445,7 @@ QPDF::checkLinearizationInternal()
for (size_t i = 0; i < toS(npages); ++i) { for (size_t i = 0; i < toS(npages); ++i) {
QPDFObjectHandle const& page = pages.at(i); QPDFObjectHandle const& page = pages.at(i);
QPDFObjGen og(page.getObjGen()); QPDFObjGen og(page.getObjGen());
if (m->xref_table[og].getType() == 2) { if (m->xref_table.type(og) == 2) {
linearizationWarning( linearizationWarning(
"page dictionary for page " + std::to_string(i) + " is compressed"); "page dictionary for page " + std::to_string(i) + " is compressed");
} }
@ -556,23 +556,18 @@ QPDF::maxEnd(ObjUser const& ou)
qpdf_offset_t qpdf_offset_t
QPDF::getLinearizationOffset(QPDFObjGen const& og) QPDF::getLinearizationOffset(QPDFObjGen const& og)
{ {
QPDFXRefEntry entry = m->xref_table[og]; switch (m->xref_table.type(og)) {
qpdf_offset_t result = 0;
switch (entry.getType()) {
case 1: case 1:
result = entry.getOffset(); return m->xref_table.offset(og);
break;
case 2: case 2:
// For compressed objects, return the offset of the object stream that contains them. // For compressed objects, return the offset of the object stream that contains them.
result = getLinearizationOffset(QPDFObjGen(entry.getObjStreamNumber(), 0)); return getLinearizationOffset(QPDFObjGen(m->xref_table.stream_number(og.getObj()), 0));
break;
default: default:
stopOnError("getLinearizationOffset called for xref entry not of type 1 or 2"); stopOnError("getLinearizationOffset called for xref entry not of type 1 or 2");
break; return 0; // unreachable
} }
return result;
} }
QPDFObjectHandle QPDFObjectHandle
@ -603,7 +598,7 @@ QPDF::lengthNextN(int first_object, int n)
int length = 0; int length = 0;
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
QPDFObjGen og(first_object + i, 0); QPDFObjGen og(first_object + i, 0);
if (m->xref_table.count(og) == 0) { if (m->xref_table.type(og) == 0) {
linearizationWarning( linearizationWarning(
"no xref table entry for " + std::to_string(first_object + i) + " 0"); "no xref table entry for " + std::to_string(first_object + i) + " 0");
} else { } else {
@ -635,7 +630,7 @@ QPDF::checkHPageOffset(
int npages = toI(pages.size()); int npages = toI(pages.size());
qpdf_offset_t table_offset = adjusted_offset(m->page_offset_hints.first_page_offset); qpdf_offset_t table_offset = adjusted_offset(m->page_offset_hints.first_page_offset);
QPDFObjGen first_page_og(pages.at(0).getObjGen()); QPDFObjGen first_page_og(pages.at(0).getObjGen());
if (m->xref_table.count(first_page_og) == 0) { if (m->xref_table.type(first_page_og) == 0) {
stopOnError("supposed first page object is not known"); stopOnError("supposed first page object is not known");
} }
qpdf_offset_t offset = getLinearizationOffset(first_page_og); qpdf_offset_t offset = getLinearizationOffset(first_page_og);
@ -646,7 +641,7 @@ QPDF::checkHPageOffset(
for (int pageno = 0; pageno < npages; ++pageno) { for (int pageno = 0; pageno < npages; ++pageno) {
QPDFObjGen page_og(pages.at(toS(pageno)).getObjGen()); QPDFObjGen page_og(pages.at(toS(pageno)).getObjGen());
int first_object = page_og.getObj(); int first_object = page_og.getObj();
if (m->xref_table.count(page_og) == 0) { if (m->xref_table.type(page_og) == 0) {
stopOnError("unknown object in page offset hint table"); stopOnError("unknown object in page offset hint table");
} }
offset = getLinearizationOffset(page_og); offset = getLinearizationOffset(page_og);
@ -768,7 +763,7 @@ QPDF::checkHSharedObject(std::vector<QPDFObjectHandle> const& pages, std::map<in
cur_object = so.first_shared_obj; cur_object = so.first_shared_obj;
QPDFObjGen og(cur_object, 0); QPDFObjGen og(cur_object, 0);
if (m->xref_table.count(og) == 0) { if (m->xref_table.type(og) == 0) {
stopOnError("unknown object in shared object hint table"); stopOnError("unknown object in shared object hint table");
} }
qpdf_offset_t offset = getLinearizationOffset(og); qpdf_offset_t offset = getLinearizationOffset(og);
@ -819,7 +814,7 @@ QPDF::checkHOutlines()
return; return;
} }
QPDFObjGen og(outlines.getObjGen()); QPDFObjGen og(outlines.getObjGen());
if (m->xref_table.count(og) == 0) { if (m->xref_table.type(og) == 0) {
stopOnError("unknown object in outlines hint table"); stopOnError("unknown object in outlines hint table");
} }
qpdf_offset_t offset = getLinearizationOffset(og); qpdf_offset_t offset = getLinearizationOffset(og);
@ -838,8 +833,7 @@ QPDF::checkHOutlines()
std::to_string(table_length) + "; computed = " + std::to_string(length)); std::to_string(table_length) + "; computed = " + std::to_string(length));
} }
} else { } else {
linearizationWarning("incorrect first object number in outline " linearizationWarning("incorrect first object number in outline hints table.");
"hints table.");
} }
} else { } else {
linearizationWarning("incorrect object count in outline hint table"); linearizationWarning("incorrect object count in outline hint table");

View File

@ -19,6 +19,37 @@ class QPDF::Xref_table: public std::map<QPDFObjGen, QPDFXRefEntry>
void show(); void show();
bool resolve(); bool resolve();
// Returns 0 if og is not in table.
int
type(QPDFObjGen og) const
{
auto it = find(og);
return it == end() ? 0 : it->second.getType();
}
// Returns 0 if og is not in table.
qpdf_offset_t
offset(QPDFObjGen og) const
{
auto it = find(og);
return it == end() ? 0 : it->second.getOffset();
}
// Returns 0 if og is not in table.
int
stream_number(int id) const
{
auto it = find(QPDFObjGen(id, 0));
return it == end() ? 0 : it->second.getObjStreamNumber();
}
int
stream_index(int id) const
{
auto it = find(QPDFObjGen(id, 0));
return it == end() ? 0 : it->second.getObjStreamIndex();
}
QPDFObjectHandle trailer; QPDFObjectHandle trailer;
bool reconstructed{false}; bool reconstructed{false};
// Various tables are indexed by object id, with potential size id + 1 // Various tables are indexed by object id, with potential size id + 1