This commit is contained in:
m-holger 2024-05-03 13:19:15 +01:00 committed by GitHub
commit b0368d6ed0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 367 additions and 124 deletions

View File

@ -55,6 +55,9 @@ class QPDF_DLL_CLASS QPDFObjectHelper
}
protected:
#ifdef QPDF_FUTURE
QPDFObjectHelper() = default;
#endif
QPDFObjectHandle oh;
};

View File

@ -79,16 +79,14 @@ NNTreeIterator::PathElement::PathElement(QPDFObjectHandle const& node, int kid_n
QPDFObjectHandle
NNTreeIterator::getNextKid(PathElement& pe, bool backward)
{
QPDFObjectHandle result;
bool found = false;
while (!found) {
while (true) {
pe.kid_number += backward ? -1 : 1;
auto kids = pe.node.getKey("/Kids");
if ((pe.kid_number >= 0) && (pe.kid_number < kids.getArrayNItems())) {
result = kids.getArrayItem(pe.kid_number);
auto result = kids.getArrayItem(pe.kid_number);
if (result.isDictionary() &&
(result.hasKey("/Kids") || result.hasKey(impl.details.itemsKey()))) {
found = true;
return result;
} else {
QTC::TC("qpdf", "NNTree skip invalid kid");
warn(
@ -97,11 +95,12 @@ NNTreeIterator::getNextKid(PathElement& pe, bool backward)
("skipping over invalid kid at index " + std::to_string(pe.kid_number)));
}
} else {
result = QPDFObjectHandle::newNull();
found = true;
#ifndef QPDF_FUTURE
return QPDFObjectHandle::newNull();
#endif
return {};
}
}
return result;
}
bool

View File

@ -228,9 +228,15 @@ QPDF::~QPDF()
// the xref table anyway just to prevent any possibility of resolve() succeeding.
m->xref_table.clear();
for (auto const& iter: m->obj_cache) {
iter.second.object->disconnect();
if (iter.second.object->getTypeCode() != ::ot_null) {
if (iter.second.object) {
iter.second.object->disconnect();
#ifndef QPDF_FUTURE
if (iter.second.object->getTypeCode() != ::ot_null) {
iter.second.object->destroy();
}
#else
iter.second.object->destroy();
#endif
}
}
}
@ -1572,7 +1578,10 @@ QPDF::readObjectAtOffset(
if (offset == 0) {
QTC::TC("qpdf", "QPDF bogus 0 offset", 0);
warn(damagedPDF(0, "object has offset 0"));
#ifndef QPDF_FUTURE
return QPDFObjectHandle::newNull();
#endif
return {};
}
m->file->seek(offset, SEEK_SET);
@ -1630,7 +1639,10 @@ QPDF::readObjectAtOffset(
("object " + exp_og.unparse(' ') +
" not found in file after regenerating cross reference "
"table")));
#ifndef QPDF_FUTURE
return QPDFObjectHandle::newNull();
#endif
return {};
}
} else {
throw;
@ -1836,7 +1848,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
QPDFObjectHandle
QPDF::newIndirect(QPDFObjGen const& og, std::shared_ptr<QPDFObject> const& obj)
{
obj->setDefaultDescription(this, og);
if (obj) {
obj->setDefaultDescription(this, og);
}
return {obj};
}
@ -1891,9 +1905,11 @@ QPDF::makeIndirectFromQPDFObject(std::shared_ptr<QPDFObject> const& obj)
QPDFObjectHandle
QPDF::makeIndirectObject(QPDFObjectHandle oh)
{
#ifndef QPDF_FUTURE
if (!oh.isInitialized()) {
throw std::logic_error("attempted to make an uninitialized QPDFObjectHandle indirect");
}
#endif
return makeIndirectFromQPDFObject(oh.getObj());
}
@ -1986,11 +2002,19 @@ QPDF::replaceObject(int objid, int generation, QPDFObjectHandle oh)
void
QPDF::replaceObject(QPDFObjGen const& og, QPDFObjectHandle oh)
{
#ifndef QPDF_FUTURE
if (oh.isIndirect() || !oh.isInitialized()) {
#else
if (oh.isIndirect()) {
#endif
QTC::TC("qpdf", "QPDF replaceObject called with indirect object");
throw std::logic_error("QPDF::replaceObject called with indirect object handle");
}
updateCache(og, oh.getObj(), -1, -1);
auto obj = oh.getObj();
if (!obj) {
obj = QPDF_Null::create();
}
updateCache(og, obj, -1, -1);
}
void
@ -2093,7 +2117,10 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
if (!obj_copier.object_map.count(og)) {
warn(damagedPDF("unexpected reference to /Pages object while copying foreign object; "
"replacing with null"));
#ifndef QPDF_FUTURE
return QPDFObjectHandle::newNull();
#endif
return {};
}
return obj_copier.object_map[foreign.getObjGen()];
}
@ -2167,7 +2194,9 @@ QPDF::replaceForeignIndirectObjects(QPDFObjectHandle foreign, ObjCopier& obj_cop
// This case would occur if this is a reference to a Pages object that we didn't
// traverse into.
QTC::TC("qpdf", "QPDF replace foreign indirect with null");
#ifndef QPDF_FUTURE
result = QPDFObjectHandle::newNull();
#endif
} else {
result = mapping->second;
}

View File

@ -55,7 +55,11 @@ QPDFAcroFormDocumentHelper::addFormField(QPDFFormFieldObjectHelper ff)
}
fields.appendItem(ff.getObjectHandle());
QPDFObjGen::set visited;
#ifndef QPDF_FUTURE
traverseField(ff.getObjectHandle(), QPDFObjectHandle::newNull(), 0, visited);
#else
traverseField(ff.getObjectHandle(), {}, 0, visited);
#endif
}
void

View File

@ -15,7 +15,9 @@ QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper(QPDFObjectHandle oh) :
}
QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper() :
#ifndef QPDF_FUTURE
QPDFObjectHelper(QPDFObjectHandle::newNull()),
#endif
m(new Members())
{
}
@ -51,15 +53,13 @@ QPDFFormFieldObjectHelper::getFieldFromAcroForm(std::string const& name)
{
QPDFObjectHandle result = QPDFObjectHandle::newNull();
// Fields are supposed to be indirect, so this should work.
QPDF* q = this->oh.getOwningQPDF();
if (!q) {
return result;
if (QPDF* q = this->oh.getOwningQPDF()) {
return q->getRoot().getKey("/AcroForm").getKeyIfDict(name);
}
auto acroform = q->getRoot().getKey("/AcroForm");
if (!acroform.isDictionary()) {
return result;
}
return acroform.getKey(name);
#ifndef QPDF_FUTURE
return QPDFObjectHandle::newNull();
#endif
return {};
}
QPDFObjectHandle
@ -67,9 +67,12 @@ QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name)
{
QPDFObjectHandle node = this->oh;
if (!node.isDictionary()) {
#ifndef QPDF_FUTURE
return QPDFObjectHandle::newNull();
#endif
return {};
}
QPDFObjectHandle result(node.getKey(name));
auto result(node.getKey(name));
if (result.isNull()) {
QPDFObjGen::set seen;
while (seen.add(node) && node.hasKey("/Parent")) {

View File

@ -27,9 +27,11 @@ QPDFObjGen::set::add(QPDFObjectHandle const& oh)
if (auto* ptr = oh.getObjectPtr()) {
return add(ptr->getObjGen());
} else {
#ifndef QPDF_FUTURE
throw std::logic_error(
"attempt to retrieve QPDFObjGen from uninitialized QPDFObjectHandle");
return false;
#endif
return true;
}
}
@ -39,9 +41,11 @@ QPDFObjGen::set::add(QPDFObjectHelper const& helper)
if (auto* ptr = helper.getObjectHandle().getObjectPtr()) {
return add(ptr->getObjGen());
} else {
#ifndef QPDF_FUTURE
throw std::logic_error(
"attempt to retrieve QPDFObjGen from uninitialized QPDFObjectHandle");
return false;
#endif
return true;
}
}
@ -51,8 +55,10 @@ QPDFObjGen::set::erase(QPDFObjectHandle const& oh)
if (auto* ptr = oh.getObjectPtr()) {
erase(ptr->getObjGen());
} else {
#ifndef QPDF_FUTURE
throw std::logic_error(
"attempt to retrieve QPDFObjGen from uninitialized QPDFObjectHandle");
#endif
}
}
@ -62,7 +68,9 @@ QPDFObjGen::set::erase(QPDFObjectHelper const& helper)
if (auto* ptr = helper.getObjectHandle().getObjectPtr()) {
erase(ptr->getObjGen());
} else {
#ifndef QPDF_FUTURE
throw std::logic_error(
"attempt to retrieve QPDFObjGen from uninitialized QPDFObjectHandle");
#endif
}
}

View File

@ -240,13 +240,21 @@ QPDFObjectHandle::disconnect()
qpdf_object_type_e
QPDFObjectHandle::getTypeCode()
{
#ifndef QPDF_FUTURE
return dereference() ? this->obj->getTypeCode() : ::ot_uninitialized;
#else
return dereference() ? this->obj->getTypeCode() : ::ot_null;
#endif
}
char const*
QPDFObjectHandle::getTypeName()
{
#ifndef QPDF_FUTURE
return dereference() ? this->obj->getTypeName() : "uninitialized";
#else
return dereference() ? this->obj->getTypeName() : "null";
#endif
}
QPDF_Array*
@ -346,13 +354,21 @@ QPDFObjectHandle::isDirectNull() const
{
// Don't call dereference() -- this is a const method, and we know
// objid == 0, so there's nothing to resolve.
return (isInitialized() && (getObjectID() == 0) && (obj->getTypeCode() == ::ot_null));
#ifdef QPDF_FUTURE
return !obj || (getObjectID() == 0 && obj->getTypeCode() == ::ot_null);
#else
return isInitialized() && getObjectID() == 0 && obj->getTypeCode() == ::ot_null;
#endif
}
bool
QPDFObjectHandle::isNull()
{
return dereference() && (obj->getTypeCode() == ::ot_null);
#ifdef QPDF_FUTURE
return !obj || (dereference() && obj->getTypeCode() == ::ot_null);
#else
return dereference() && obj->getTypeCode() == ::ot_null;
#endif
}
bool
@ -771,8 +787,9 @@ QPDFObjectHandle
QPDFObjectHandle::getArrayItem(int n)
{
if (auto array = asArray()) {
if (auto result = array->at(n); result.obj != nullptr) {
return result;
auto result = array->at(n);
if (result.first) {
return result.second;
} else {
objectWarning("returning null for out of bounds array access");
QTC::TC("qpdf", "QPDFObjectHandle array bounds");
@ -790,7 +807,7 @@ QPDFObjectHandle::isRectangle()
{
if (auto array = asArray()) {
for (int i = 0; i < 4; ++i) {
if (auto item = array->at(i); !(item.obj && item.isNumber())) {
if (auto item = array->at(i).second; !item.isNumber()) {
return false;
}
}
@ -804,7 +821,7 @@ QPDFObjectHandle::isMatrix()
{
if (auto array = asArray()) {
for (int i = 0; i < 6; ++i) {
if (auto item = array->at(i); !(item.obj && item.isNumber())) {
if (auto item = array->at(i).second; !item.isNumber()) {
return false;
}
}
@ -822,7 +839,7 @@ QPDFObjectHandle::getArrayAsRectangle()
}
double items[4];
for (int i = 0; i < 4; ++i) {
if (!array->at(i).getValueAsNumber(items[i])) {
if (auto item = array->at(i).second; !item.getValueAsNumber(items[i])) {
return {};
}
}
@ -844,7 +861,7 @@ QPDFObjectHandle::getArrayAsMatrix()
}
double items[6];
for (int i = 0; i < 6; ++i) {
if (!array->at(i).getValueAsNumber(items[i])) {
if (auto item = array->at(i).second; !item.getValueAsNumber(items[i])) {
return {};
}
}
@ -949,7 +966,7 @@ QPDFObjectHandle
QPDFObjectHandle::eraseItemAndGetOld(int at)
{
auto array = asArray();
auto result = (array && at < array->size() && at >= 0) ? array->at(at) : newNull();
auto result = (array && at < array->size() && at >= 0) ? array->at(at).second : newNull();
eraseItem(at);
return result;
}
@ -991,21 +1008,22 @@ QPDFObjectHandle::getKey(std::string const& key)
QPDFObjectHandle
QPDFObjectHandle::getKeyIfDict(std::string const& key)
{
#ifndef QPDF_FUTURE
return isNull() ? newNull() : getKey(key);
#endif
return isNull() ? QPDFObjectHandle() : getKey(key);
}
std::set<std::string>
QPDFObjectHandle::getKeys()
{
std::set<std::string> result;
auto dict = asDictionary();
if (dict) {
result = dict->getKeys();
if (auto dict = asDictionary()) {
return dict->getKeys();
} else {
typeWarning("dictionary", "treating as empty");
QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
}
return result;
return {};
}
std::map<std::string, QPDFObjectHandle>
@ -1014,12 +1032,12 @@ QPDFObjectHandle::getDictAsMap()
std::map<std::string, QPDFObjectHandle> result;
auto dict = asDictionary();
if (dict) {
result = dict->getAsMap();
return dict->getAsMap();
} else {
typeWarning("dictionary", "treating as empty");
QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap");
}
return result;
return {};
}
// Array and Name accessors
@ -1239,13 +1257,15 @@ QPDFObjectHandle::removeKey(std::string const& key)
QPDFObjectHandle
QPDFObjectHandle::removeKeyAndGetOld(std::string const& key)
{
auto result = QPDFObjectHandle::newNull();
auto dict = asDictionary();
if (dict) {
result = dict->getKey(key);
if (auto dict = asDictionary()) {
auto result = dict->getKey(key);
removeKey(key);
return result;
}
removeKey(key);
return result;
#ifndef QPDF_FUTURE
return QPDFObjectHandle::newNull();
#endif
return {};
}
void
@ -1443,7 +1463,7 @@ QPDFObjectHandle::arrayOrStreamToStreamArray(
if (auto array = asArray()) {
int n_items = array->size();
for (int i = 0; i < n_items; ++i) {
QPDFObjectHandle item = array->at(i);
QPDFObjectHandle item = array->at(i).second;
if (item.isStream()) {
result.push_back(item);
} else {
@ -1587,7 +1607,11 @@ std::string
QPDFObjectHandle::unparseResolved()
{
if (!dereference()) {
#ifndef QPDF_FUTURE
throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
#else
return "null";
#endif
}
return obj->unparse();
}
@ -1616,7 +1640,11 @@ QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect)
if ((!dereference_indirect) && isIndirect()) {
return JSON::makeString(unparse());
} else if (!dereference()) {
#ifndef QPDF_FUTURE
throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
#else
return JSON::makeNull();
#endif
} else {
Pl_Buffer p{"json"};
JSON::Writer jw{&p, 0};
@ -1632,7 +1660,11 @@ QPDFObjectHandle::writeJSON(int json_version, JSON::Writer& p, bool dereference_
if (!dereference_indirect && isIndirect()) {
p << "\"" << getObjGen().unparse(' ') << " R\"";
} else if (!dereference()) {
#ifndef QPDF_FUTURE
throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
#else
p << "null";
#endif
} else {
obj->writeJSON(json_version, p);
}
@ -2073,7 +2105,11 @@ QPDFObjectHandle
QPDFObjectHandle::shallowCopy()
{
if (!dereference()) {
#ifndef QPDF_FUTURE
throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle");
#else
return {};
#endif
}
return {obj->copy()};
}
@ -2082,7 +2118,11 @@ QPDFObjectHandle
QPDFObjectHandle::unsafeShallowCopy()
{
if (!dereference()) {
#ifndef QPDF_FUTURE
throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle");
#else
return {};
#endif
}
return {obj->copy(true)};
}
@ -2091,6 +2131,9 @@ void
QPDFObjectHandle::makeDirect(QPDFObjGen::set& visited, bool stop_at_streams)
{
assertInitialized();
if (!obj) {
return;
}
auto cur_og = getObjGen();
if (!visited.add(cur_og)) {
@ -2105,7 +2148,7 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set& visited, bool stop_at_streams)
auto array = asArray();
int n = array->size();
for (int i = 0; i < n; ++i) {
items.push_back(array->at(i));
items.push_back(array->at(i).second);
items.back().makeDirect(visited, stop_at_streams);
}
this->obj = QPDF_Array::create(items);
@ -2160,9 +2203,11 @@ QPDFObjectHandle::makeDirect(bool allow_streams)
void
QPDFObjectHandle::assertInitialized() const
{
#ifndef QPDF_FUTURE
if (!isInitialized()) {
throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle");
}
#endif
}
void
@ -2172,10 +2217,16 @@ QPDFObjectHandle::typeWarning(char const* expected_type, std::string const& warn
std::string description;
// Type checks above guarantee that the object has been dereferenced. Nevertheless, dereference
// throws exceptions in the test suite
#ifdef QPDF_FUTURE
if (dereference()) {
obj->getDescription(context, description);
}
#else
if (!dereference()) {
throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
}
this->obj->getDescription(context, description);
obj->getDescription(context, description);
#endif
// Null context handled by warn
warn(
context,

View File

@ -90,8 +90,10 @@ QPDFOutlineDocumentHelper::resolveNamedDest(QPDFObjectHandle name)
}
}
}
#ifndef QPDF_FUTURE
if (!result.isInitialized()) {
result = QPDFObjectHandle::newNull();
}
#endif
return result;
}

View File

@ -58,9 +58,11 @@ QPDFOutlineObjectHelper::getDest()
QTC::TC("qpdf", "QPDFOutlineObjectHelper action dest");
dest = A.getKey("/D");
}
#ifndef QPDF_FUTURE
if (!dest.isInitialized()) {
dest = QPDFObjectHandle::newNull();
}
#endif
if (dest.isName() || dest.isString()) {
QTC::TC("qpdf", "QPDFOutlineObjectHelper named dest");
@ -77,7 +79,10 @@ QPDFOutlineObjectHelper::getDestPage()
if ((dest.isArray()) && (dest.getArrayNItems() > 0)) {
return dest.getArrayItem(0);
}
#ifndef QPDF_FUTURE
return QPDFObjectHandle::newNull();
#endif
return {};
}
int

View File

@ -394,7 +394,11 @@ QPDFParser::add(std::shared_ptr<QPDFObject>&& obj)
void
QPDFParser::addNull()
{
#ifndef QPDF_FUTURE
const static ObjectPtr null_obj = QPDF_Null::create();
#else
const static ObjectPtr null_obj;
#endif
if (frame->state != st_dictionary_value) {
// If state is st_dictionary_key then there is a missing key. Push onto olist for
@ -448,7 +452,7 @@ QPDFParser::fixMissingKeys()
{
std::set<std::string> names;
for (auto& obj: frame->olist) {
if (obj->getTypeCode() == ::ot_name) {
if (obj && obj->getTypeCode() == ::ot_name) {
names.insert(obj->getStringValue());
}
}

View File

@ -5,8 +5,11 @@
#include <qpdf/QPDFObject_private.hh>
#include <qpdf/QTC.hh>
#ifndef QPDF_FUTURE
static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull();
#else
static const QPDFObjectHandle null_oh;
#endif
inline void
QPDF_Array::checkOwnership(QPDFObjectHandle const& item) const
{
@ -21,7 +24,9 @@ QPDF_Array::checkOwnership(QPDFObjectHandle const& item) const
}
}
} else {
#ifndef QPDF_FUTURE
throw std::logic_error("Attempting to add an uninitialized object to a QPDF_Array.");
#endif
}
}
@ -48,7 +53,7 @@ QPDF_Array::QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& v, bool sparse
if (sparse) {
sp = std::make_unique<Sparse>();
for (auto&& item: v) {
if (item->getTypeCode() != ::ot_null || item->getObjGen().isIndirect()) {
if (item && (item->getTypeCode() != ::ot_null || item->getObjGen().isIndirect())) {
sp->elements[sp->size] = std::move(item);
}
++sp->size;
@ -84,7 +89,7 @@ QPDF_Array::copy(bool shallow)
for (auto const& element: sp->elements) {
auto const& obj = element.second;
result->sp->elements[element.first] =
obj->getObjGen().isIndirect() ? obj : obj->copy();
!obj || obj->getObjGen().isIndirect() ? obj : obj->copy();
}
return do_create(result);
} else {
@ -106,13 +111,13 @@ QPDF_Array::disconnect()
if (sp) {
for (auto& item: sp->elements) {
auto& obj = item.second;
if (!obj->getObjGen().isIndirect()) {
if (obj && !obj->getObjGen().isIndirect()) {
obj->disconnect();
}
}
} else {
for (auto& obj: elements) {
if (!obj->getObjGen().isIndirect()) {
if (obj && !obj->getObjGen().isIndirect()) {
obj->disconnect();
}
}
@ -130,9 +135,13 @@ QPDF_Array::unparse()
for (int j = next; j < key; ++j) {
result += "null ";
}
item.second->resolve();
auto og = item.second->getObjGen();
result += og.isIndirect() ? og.unparse(' ') + " R " : item.second->unparse() + " ";
if (item.second) {
item.second->resolve();
auto og = item.second->getObjGen();
result += og.isIndirect() ? og.unparse(' ') + " R " : item.second->unparse() + " ";
} else {
result += "null ";
}
next = ++key;
}
for (int j = next; j < sp->size; ++j) {
@ -140,9 +149,13 @@ QPDF_Array::unparse()
}
} else {
for (auto const& item: elements) {
item->resolve();
auto og = item->getObjGen();
result += og.isIndirect() ? og.unparse(' ') + " R " : item->unparse() + " ";
if (item) {
item->resolve();
auto og = item->getObjGen();
result += og.isIndirect() ? og.unparse(' ') + " R " : item->unparse() + " ";
} else {
result += "null ";
}
}
}
result += "]";
@ -161,11 +174,15 @@ QPDF_Array::writeJSON(int json_version, JSON::Writer& p)
p.writeNext() << "null";
}
p.writeNext();
auto og = item.second->getObjGen();
if (og.isIndirect()) {
p << "\"" << og.unparse(' ') << " R\"";
if (item.second) {
auto og = item.second->getObjGen();
if (og.isIndirect()) {
p << "\"" << og.unparse(' ') << " R\"";
} else {
item.second->writeJSON(json_version, p);
}
} else {
item.second->writeJSON(json_version, p);
p << "null";
}
next = ++key;
}
@ -175,27 +192,31 @@ QPDF_Array::writeJSON(int json_version, JSON::Writer& p)
} else {
for (auto const& item: elements) {
p.writeNext();
auto og = item->getObjGen();
if (og.isIndirect()) {
p << "\"" << og.unparse(' ') << " R\"";
if (item) {
auto og = item->getObjGen();
if (og.isIndirect()) {
p << "\"" << og.unparse(' ') << " R\"";
} else {
item->writeJSON(json_version, p);
}
} else {
item->writeJSON(json_version, p);
p << "null";
}
}
}
p.writeEnd(']');
}
QPDFObjectHandle
std::pair<bool, QPDFObjectHandle>
QPDF_Array::at(int n) const noexcept
{
if (n < 0 || n >= size()) {
return {};
return {false, {}};
} else if (sp) {
auto const& iter = sp->elements.find(n);
return iter == sp->elements.end() ? null_oh : (*iter).second;
return {true, iter == sp->elements.end() ? null_oh : (*iter).second};
} else {
return elements[size_t(n)];
return {true, elements[size_t(n)]};
}
}

View File

@ -35,7 +35,10 @@ QPDF_Null::create(
std::shared_ptr<QPDFObject>
QPDF_Null::copy(bool shallow)
{
#ifndef QPDF_FUTURE
return create();
#endif
return nullptr;
}
std::string

View File

@ -30,7 +30,7 @@ class QPDF_Array: public QPDFValue
{
return sp ? sp->size : int(elements.size());
}
QPDFObjectHandle at(int n) const noexcept;
std::pair<bool, QPDFObjectHandle> at(int n) const noexcept;
bool setAt(int n, QPDFObjectHandle const& oh);
std::vector<QPDFObjectHandle> getAsVector() const;
void setFromVector(std::vector<QPDFObjectHandle> const& items);

View File

@ -21,69 +21,69 @@ main()
a.push_back(QPDFObjectHandle::parse("null"));
a.push_back(QPDFObjectHandle::parse("/Quack"));
assert(a.size() == 5);
assert(a.at(0).isInteger() && (a.at(0).getIntValue() == 1));
assert(a.at(1).isString() && (a.at(1).getStringValue() == "potato"));
assert(a.at(2).isNull());
assert(a.at(3).isNull());
assert(a.at(4).isName() && (a.at(4).getName() == "/Quack"));
assert(a.at(0).second.isInteger() && (a.at(0).second.getIntValue() == 1));
assert(a.at(1).second.isString() && (a.at(1).second.getStringValue() == "potato"));
assert(a.at(2).second.isNull());
assert(a.at(3).second.isNull());
assert(a.at(4).second.isName() && (a.at(4).second.getName() == "/Quack"));
a.insert(4, QPDFObjectHandle::parse("/BeforeQuack"));
assert(a.size() == 6);
assert(a.at(0).isInteger() && (a.at(0).getIntValue() == 1));
assert(a.at(4).isName() && (a.at(4).getName() == "/BeforeQuack"));
assert(a.at(5).isName() && (a.at(5).getName() == "/Quack"));
assert(a.at(0).second.isInteger() && (a.at(0).second.getIntValue() == 1));
assert(a.at(4).second.isName() && (a.at(4).second.getName() == "/BeforeQuack"));
assert(a.at(5).second.isName() && (a.at(5).second.getName() == "/Quack"));
a.insert(2, QPDFObjectHandle::parse("/Third"));
assert(a.size() == 7);
assert(a.at(1).isString() && (a.at(1).getStringValue() == "potato"));
assert(a.at(2).isName() && (a.at(2).getName() == "/Third"));
assert(a.at(3).isNull());
assert(a.at(6).isName() && (a.at(6).getName() == "/Quack"));
assert(a.at(1).second.isString() && (a.at(1).second.getStringValue() == "potato"));
assert(a.at(2).second.isName() && (a.at(2).second.getName() == "/Third"));
assert(a.at(3).second.isNull());
assert(a.at(6).second.isName() && (a.at(6).second.getName() == "/Quack"));
a.insert(0, QPDFObjectHandle::parse("/First"));
assert(a.size() == 8);
assert(a.at(0).isName() && (a.at(0).getName() == "/First"));
assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1));
assert(a.at(7).isName() && (a.at(7).getName() == "/Quack"));
assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
assert(a.at(7).second.isName() && (a.at(7).second.getName() == "/Quack"));
a.erase(6);
assert(a.size() == 7);
assert(a.at(0).isName() && (a.at(0).getName() == "/First"));
assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1));
assert(a.at(5).isNull());
assert(a.at(6).isName() && (a.at(6).getName() == "/Quack"));
assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
assert(a.at(5).second.isNull());
assert(a.at(6).second.isName() && (a.at(6).second.getName() == "/Quack"));
a.erase(6);
assert(a.size() == 6);
assert(a.at(0).isName() && (a.at(0).getName() == "/First"));
assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1));
assert(a.at(3).isName() && (a.at(3).getName() == "/Third"));
assert(a.at(4).isNull());
assert(a.at(5).isNull());
assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third"));
assert(a.at(4).second.isNull());
assert(a.at(5).second.isNull());
a.setAt(4, QPDFObjectHandle::parse("12"));
assert(a.at(4).isInteger() && (a.at(4).getIntValue() == 12));
assert(a.at(4).second.isInteger() && (a.at(4).second.getIntValue() == 12));
a.setAt(4, QPDFObjectHandle::newNull());
assert(a.at(4).isNull());
assert(a.at(4).second.isNull());
a.erase(a.size() - 1);
assert(a.size() == 5);
assert(a.at(0).isName() && (a.at(0).getName() == "/First"));
assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1));
assert(a.at(3).isName() && (a.at(3).getName() == "/Third"));
assert(a.at(4).isNull());
assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third"));
assert(a.at(4).second.isNull());
a.erase(a.size() - 1);
assert(a.size() == 4);
assert(a.at(0).isName() && (a.at(0).getName() == "/First"));
assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1));
assert(a.at(3).isName() && (a.at(3).getName() == "/Third"));
assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third"));
a.erase(a.size() - 1);
assert(a.size() == 3);
assert(a.at(0).isName() && (a.at(0).getName() == "/First"));
assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1));
assert(a.at(2).isString() && (a.at(2).getStringValue() == "potato"));
assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
assert(a.at(2).second.isString() && (a.at(2).second.getStringValue() == "potato"));
QPDF pdf;
pdf.emptyPDF();
@ -92,21 +92,24 @@ main()
QPDF_Array& b = *obj->as<QPDF_Array>();
b.setAt(5, pdf.getObject(5, 0));
b.setAt(7, "[0 1 2 3]"_qpdf);
assert(b.at(3).isNull());
assert(b.at(8).isNull());
assert(b.at(5).isIndirect());
assert(b.at(3).second.isNull());
assert(b.at(8).second.isNull());
assert(b.at(5).second.isIndirect());
assert(b.unparse() == "[ null null null null null 5 0 R null [ 0 1 2 3 ] null null ]");
auto c = b.copy(true);
auto d = b.copy(false);
b.at(7).setArrayItem(2, "42"_qpdf);
b.at(7).second.setArrayItem(2, "42"_qpdf);
assert(c->unparse() == "[ null null null null null 5 0 R null [ 0 1 42 3 ] null null ]");
assert(d->unparse() == "[ null null null null null 5 0 R null [ 0 1 2 3 ] null null ]");
#ifndef QPDF_FUTURE
try {
b.setAt(3, {});
std::cout << "inserted uninitialized object\n";
} catch (std::logic_error&) {
}
#endif
QPDF pdf2;
pdf2.emptyPDF();
try {

View File

@ -12,6 +12,8 @@ require TestDriver;
cleanup();
my $future = check_future();
my $td = new TestDriver('c-api-object-handle');
my $n_tests = 13;
@ -54,10 +56,20 @@ $td->runtest("C wrap and clone objects",
{$td->COMMAND => "qpdf-ctest 28 minimal.pdf '' ''"},
{$td->STRING => "C test 28 done\n", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
$td->runtest("C object handle errors",
{$td->COMMAND => "qpdf-ctest 29 minimal.pdf '' ''"},
{$td->FILE => "c-oh-errors.out", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
if ($future)
{
$td->runtest("C object handle errors",
{$td->COMMAND => "qpdf-ctest 29 minimal.pdf '' ''"},
{$td->FILE => "c-oh-errors-future.out", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
}
else
{
$td->runtest("C object handle errors",
{$td->COMMAND => "qpdf-ctest 29 minimal.pdf '' ''"},
{$td->FILE => "c-oh-errors.out", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
}
$td->runtest("C unhandled error warning",
{$td->COMMAND => "qpdf-ctest 30 bad1.pdf '' ''"},
{$td->FILE => "c-unhandled-error.out", $td->EXIT_STATUS => 0},

View File

@ -12,15 +12,28 @@ require TestDriver;
cleanup();
my $future = check_future();
my $td = new TestDriver('invalid-objects');
my $n_tests = 4;
$td->runtest("closed input source",
{$td->COMMAND => "test_driver 73 minimal.pdf"},
{$td->FILE => "test73.out",
$td->EXIT_STATUS => 2},
$td->NORMALIZE_NEWLINES);
if ($future)
{
$td->runtest("closed input source",
{$td->COMMAND => "test_driver 73 minimal.pdf"},
{$td->FILE => "test73_future.out",
$td->EXIT_STATUS => 2},
$td->NORMALIZE_NEWLINES);
}
else
{
$td->runtest("closed input source",
{$td->COMMAND => "test_driver 73 minimal.pdf"},
{$td->FILE => "test73.out",
$td->EXIT_STATUS => 2},
$td->NORMALIZE_NEWLINES);
}
$td->runtest("empty object",
{$td->COMMAND => "qpdf -show-object=7,0 empty-object.pdf"},

View File

@ -0,0 +1,56 @@
get root: operation for dictionary attempted on object of type null: returning null for attempted key retrieval
code: 7
file:
pos: 0
text: operation for dictionary attempted on object of type null: returning null for attempted key retrieval
bad parse: parsed object (offset 1): unknown token while reading object; treating as string
code: 5
file: parsed object
pos: 1
text: unknown token while reading object; treating as string
type mismatch (int operation on string): operation for integer attempted on object of type string: returning 0
code: 7
file:
pos: 0
text: operation for integer attempted on object of type string: returning 0
type mismatch (string operation on int): operation for string attempted on object of type integer: returning empty string
code: 7
file:
pos: 0
text: operation for string attempted on object of type integer: returning empty string
array type mismatch - n_items: operation for array attempted on object of type integer: treating as empty
code: 7
file:
pos: 0
text: operation for array attempted on object of type integer: treating as empty
array type mismatch - item: operation for array attempted on object of type integer: returning null
code: 7
file:
pos: 0
text: operation for array attempted on object of type integer: returning null
append to non-array: operation for array attempted on object of type integer: ignoring attempt to append item
code: 7
file:
pos: 0
text: operation for array attempted on object of type integer: ignoring attempt to append item
array bounds: returning null for out of bounds array access
code: 7
file:
pos: 0
text: returning null for out of bounds array access
dictionary iter type mismatch: operation for dictionary attempted on object of type integer: treating as empty
code: 7
file:
pos: 0
text: operation for dictionary attempted on object of type integer: treating as empty
dictionary type mismatch: operation for dictionary attempted on object of type integer: returning null for attempted key retrieval
code: 7
file:
pos: 0
text: operation for dictionary attempted on object of type integer: returning null for attempted key retrieval
dictionary type mismatch: operation for dictionary attempted on object of type integer: returning false for a key containment request
code: 7
file:
pos: 0
text: operation for dictionary attempted on object of type integer: returning false for a key containment request
C test 29 done

View File

@ -0,0 +1,3 @@
getRoot: operation for dictionary attempted on object of type null: returning null for attempted key retrieval
WARNING: closed input source: object 1/0: error reading object: QPDF operation attempted on a QPDF object with no input source. QPDF operations are invalid before processFile (or another process method) or after closeInputSource
closed input source: unable to find /Root dictionary

View File

@ -158,4 +158,15 @@ sub cleanup
system("rm -rf *split-out* ???-kfo.pdf *.tmpout \@file.pdf auto-*");
}
sub check_future
{
my $future = 0;
chomp($_ = `qpdf --version`);
if (m/future/)
{
$future = 1;
}
$future;
}
1;

View File

@ -214,8 +214,10 @@ test_0_1(QPDF& pdf, char const* arg2)
}
QTC::TC("qpdf", "main QTest indirect", qtest.isIndirect() ? 1 : 0);
std::cout << "/QTest is " << (qtest.isIndirect() ? "in" : "") << "direct and has type "
<< qtest.getTypeName() << " (" << qtest.getTypeCode() << ")" << std::endl;
auto tn = qtest.getTypeName();
auto tc = qtest.getTypeCode();
std::cout << "/QTest is " << (qtest.isIndirect() ? "in" : "") << "direct and has type " << tn
<< " (" << tc << ")" << std::endl;
if (qtest.isNull()) {
QTC::TC("qpdf", "main QTest null");
@ -1545,7 +1547,13 @@ test_42(QPDF& pdf, char const* arg2)
assert(!uninitialized.isInitialized());
assert(!uninitialized.isInteger());
assert(!uninitialized.isDictionary());
#ifdef QPDF_FUTURE
assert(uninitialized.isNull());
assert(uninitialized.isScalar());
#else
assert(!uninitialized.isNull());
assert(!uninitialized.isScalar());
#endif
}
static void
@ -3477,8 +3485,13 @@ runtest(int n, char const* filename1, char const* arg2)
assert(password == "1234567890123456789012(45678");
QPDFObjectHandle uninitialized;
#ifndef QPDF_FUTURE
assert(uninitialized.getTypeCode() == ::ot_uninitialized);
assert(strcmp(uninitialized.getTypeName(), "uninitialized") == 0);
#else
assert(uninitialized.getTypeCode() == ::ot_null);
assert(strcmp(uninitialized.getTypeName(), "null") == 0);
#endif
// ABI: until QPDF 12, spot check deprecated constants
assert(QPDFObject::ot_dictionary == ::ot_dictionary);