aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog10
-rw-r--r--TODO9
-rw-r--r--include/qpdf/QPDF.hh1
-rw-r--r--include/qpdf/QPDFObject.hh21
-rw-r--r--include/qpdf/QPDFObjectHandle.hh32
-rw-r--r--ispell-words50
-rw-r--r--libqpdf/QPDF.cc53
-rw-r--r--libqpdf/QPDFExc.cc10
-rw-r--r--libqpdf/QPDFObject.cc35
-rw-r--r--libqpdf/QPDFObjectHandle.cc484
-rw-r--r--libqpdf/QPDFTokenizer.cc9
-rw-r--r--libqpdf/QPDF_Array.cc7
-rw-r--r--libqpdf/QPDF_Dictionary.cc19
-rw-r--r--libqpdf/QPDF_Stream.cc31
-rw-r--r--libqpdf/QPDF_linearization.cc8
-rw-r--r--libqpdf/qpdf/QPDF_Array.hh1
-rw-r--r--libqpdf/qpdf/QPDF_Dictionary.hh3
-rw-r--r--libqpdf/qpdf/QPDF_Stream.hh3
-rw-r--r--qpdf/qpdf.testcov30
-rw-r--r--qpdf/qtest/qpdf.test33
-rw-r--r--qpdf/qtest/qpdf/bad16-recover.out8
-rw-r--r--qpdf/qtest/qpdf/bad16.out4
-rw-r--r--qpdf/qtest/qpdf/bad18-recover.out9
-rw-r--r--qpdf/qtest/qpdf/bad18.out8
-rw-r--r--qpdf/qtest/qpdf/bad19-recover.out9
-rw-r--r--qpdf/qtest/qpdf/bad19.out8
-rw-r--r--qpdf/qtest/qpdf/bad20-recover.out13
-rw-r--r--qpdf/qtest/qpdf/bad20.out12
-rw-r--r--qpdf/qtest/qpdf/bad21-recover.out11
-rw-r--r--qpdf/qtest/qpdf/bad21.out10
-rw-r--r--qpdf/qtest/qpdf/bad29-recover.out11
-rw-r--r--qpdf/qtest/qpdf/bad29.out10
-rw-r--r--qpdf/qtest/qpdf/issue-100.out3
-rw-r--r--qpdf/qtest/qpdf/issue-101.out70
-rw-r--r--qpdf/qtest/qpdf/issue-146.out4
-rw-r--r--qpdf/qtest/qpdf/issue-51.out3
-rw-r--r--qpdf/qtest/qpdf/linearization-bounds-1.out2
-rw-r--r--qpdf/qtest/qpdf/object-types-os.out41
-rw-r--r--qpdf/qtest/qpdf/object-types-os.pdfbin0 -> 865 bytes
-rw-r--r--qpdf/qtest/qpdf/object-types.out41
-rw-r--r--qpdf/qtest/qpdf/object-types.pdf111
-rw-r--r--qpdf/test_driver.cc60
42 files changed, 1133 insertions, 164 deletions
diff --git a/ChangeLog b/ChangeLog
index 0d6ab5ac..1d551461 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
2018-02-17 Jay Berkenbilt <ejb@ql.org>
+ * Major enhancements to handling of type errors within the qpdf
+ library. This fix is intended to eliminate those annoying cases
+ where qpdf would exit with a message like "operation for
+ dictionary object attemped on object of wrong type" without
+ providing any context. Now qpdf keeps enough context to be able to
+ issue a proper warning and to handle such conditions in a sensible
+ way. This should greatly increase the number of bad files that
+ qpdf can recover, and it should make it much easier to figure out
+ what's broken when a file contains errors.
+
* Error message fix: replace "file position" with "offset" in
error messages that report lexical or parsing errors. Sometimes
it's an offset in an object stream or a content stream rather than
diff --git a/TODO b/TODO
index 402a715f..6a27acb0 100644
--- a/TODO
+++ b/TODO
@@ -1,15 +1,6 @@
Soon
====
- * Consider whether there should be a mode in which QPDFObjectHandle
- returns nulls for operations on the wrong type instead of asserting
- the type. The way things are wired up now, this would have to be a
- global flag. Probably it makes sense to make that be the default
- behavior and to add a static method in QPDFObjectHandle and
- command-line flag that enables the stricter behavior globally for
- easier debugging. For cases where we have enough information to do
- so, we could still warn when not in strict mode.
-
* Add method to push inheritable resources to a single page by
walking up and copying without overwrite. Above logic will also be
sufficient to fix the limitation in
diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh
index 70bfac3e..7da150f1 100644
--- a/include/qpdf/QPDF.hh
+++ b/include/qpdf/QPDF.hh
@@ -703,7 +703,6 @@ class QPDF
PointerHolder<InputSource> input, int objid, int generation,
qpdf_offset_t stream_offset);
QPDFTokenizer::Token readToken(PointerHolder<InputSource>,
- bool allow_bad = false,
size_t max_len = 0);
QPDFObjectHandle readObjectAtOffset(
diff --git a/include/qpdf/QPDFObject.hh b/include/qpdf/QPDFObject.hh
index 8d479b3c..da54c027 100644
--- a/include/qpdf/QPDFObject.hh
+++ b/include/qpdf/QPDFObject.hh
@@ -23,6 +23,7 @@
#define __QPDFOBJECT_HH__
#include <qpdf/DLL.h>
+#include <qpdf/PointerHolder.hh>
#include <string>
@@ -32,6 +33,7 @@ class QPDFObjectHandle;
class QPDFObject
{
public:
+ QPDFObject();
// Objects derived from QPDFObject are accessible through
// QPDFObjectHandle. Each object returns a unique type code that
@@ -84,8 +86,27 @@ class QPDFObject
};
friend class ObjAccessor;
+ virtual void setDescription(QPDF*, std::string const&);
+ bool getDescription(QPDF*&, std::string&);
+ bool hasDescription();
+
protected:
virtual void releaseResolved() {}
+
+ private:
+ QPDFObject(QPDFObject const&);
+ QPDFObject& operator=(QPDFObject const&);
+ class Members
+ {
+ friend class QPDFObject;
+ public:
+ ~Members();
+ private:
+ Members();
+ QPDF* owning_qpdf;
+ std::string object_description;
+ };
+ PointerHolder<Members> m;
};
#endif // __QPDFOBJECT_HH__
diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh
index d12fe87d..53b219ce 100644
--- a/include/qpdf/QPDFObjectHandle.hh
+++ b/include/qpdf/QPDFObjectHandle.hh
@@ -398,6 +398,21 @@ class QPDFObjectHandle
QPDF_DLL
static QPDFObjectHandle newReserved(QPDF* qpdf);
+ // Provide an owning qpdf and object description. The library does
+ // this automatically with objects that are read from from the
+ // input PDF and with objects that are created programmatically
+ // and inserted into the QPDF by adding them to an array or a
+ // dictionary or creating a new indirect object. Most end user
+ // code will not need to call this. If an object has an owning
+ // qpdf and object description, it enables qpdf to give warnings
+ // with proper context in some cases where it would otherwise
+ // raise exceptions.
+ QPDF_DLL
+ void setObjectDescription(QPDF* owning_qpdf,
+ std::string const& object_description);
+ QPDF_DLL
+ bool hasObjectDescription();
+
// Accessor methods. If an accessor method that is valid for only
// a particular object type is called on an object of the wrong
// type, an exception is thrown.
@@ -498,7 +513,7 @@ class QPDFObjectHandle
// Replace value of key, adding it if it does not exist
QPDF_DLL
- void replaceKey(std::string const& key, QPDFObjectHandle const&);
+ void replaceKey(std::string const& key, QPDFObjectHandle);
// Remove key, doing nothing if key does not exist
QPDF_DLL
void removeKey(std::string const& key);
@@ -769,7 +784,10 @@ class QPDFObjectHandle
};
friend class ReleaseResolver;
- // Convenience routine: Throws if the assumption is violated.
+ // Convenience routine: Throws if the assumption is violated. Your
+ // code will be better if you call one of the isType methods and
+ // handle the case of the type being wrong, but these can be
+ // convenient if you have already verified the type.
QPDF_DLL
void assertInitialized() const;
@@ -832,10 +850,16 @@ class QPDFObjectHandle
QPDF* qpdf, int objid, int generation,
QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length);
- void assertType(char const* type_name, bool istype) const;
+ void typeWarning(char const* expected_type,
+ std::string const& warning);
+ void objectWarning(std::string const& warning);
+ void assertType(char const* type_name, bool istype);
void dereference();
void makeDirectInternal(std::set<int>& visited);
void releaseResolved();
+ static void setObjectDescriptionFromInput(
+ QPDFObjectHandle, QPDF*, std::string const&,
+ PointerHolder<InputSource>, qpdf_offset_t);
static QPDFObjectHandle parseInternal(
PointerHolder<InputSource> input,
std::string const& object_description,
@@ -868,7 +892,7 @@ class QPDFObjectHandle
bool initialized;
- QPDF* qpdf; // 0 for direct object
+ QPDF* qpdf;
int objid; // 0 for direct object
int generation;
PointerHolder<QPDFObject> obj;
diff --git a/ispell-words b/ispell-words
index 82461656..1db7bdd9 100644
--- a/ispell-words
+++ b/ispell-words
@@ -23,9 +23,11 @@ activatePipelineStack
ActiveState
acyclic
adbe
+addContentTokenFilter
addPage
addPageAt
addPageContents
+addTokenFilter
addToTable
adjustAESStreamLength
admon
@@ -54,6 +56,7 @@ allowPoundAnywhereInName
allowPrintHighRes
allowPrintLowRes
antivirus
+anyBadTokens
aobjid
apexcovantage
api
@@ -71,11 +74,13 @@ argv
arko
arko's
Arora
+arrayOrStreamToStreamArray
ArtBox
ascii
asciiHex
ASCIIHexDecode
ASCIIHexDecoder
+asMap
assertArray
assertBool
assertDictionary
@@ -214,12 +219,15 @@ closeObject
cmath
cmd
cmyk
+coalesceContentStreams
+CoalesceProvider
codepage
codepoint
col
Coldwind
ColorSpace
colorspace
+ColorToGray
com
compareVersions
compatbility
@@ -230,6 +238,7 @@ Cond
config
conftest
const
+ContentNormalizer
contrib
CopiedStreamDataProvider
copyEncryptionParameters
@@ -300,6 +309,7 @@ deflateEnd
deflateInit
defq
delphi
+deque
dereference
dereferenced
dest
@@ -411,6 +421,7 @@ esize
exc
exe
exp
+expectInlineImage
ExtensionLevel
extern
fb
@@ -439,6 +450,7 @@ Filespec
FILETIME
filetrailer
filterCompressedObjects
+filterPageContents
findAndSkipNextEOL
findAttachmentStreams
findEndstream
@@ -509,6 +521,7 @@ getCompressibleObjects
getCompressibleObjGens
getCount
getDataChecksum
+getDescription
getDict
getDictAsMap
getEncryptionKey
@@ -562,6 +575,7 @@ getOE
getOffset
getOffsetLength
getOperatorValue
+getOriginalID
getOwningQPDF
getP
getPaddedUserPassword
@@ -626,7 +640,10 @@ handleCode
handleData
handleEOF
handleObject
+handleToken
+hasDescription
hasKey
+hasObjectDescription
hb
hbp
HCRYPTPROV
@@ -668,6 +685,7 @@ ifeq
iff
ifndef
ifstream
+ignorable
ijg
Im
ImageC
@@ -676,6 +694,7 @@ ImageInverter
ImageMask
ImageProvider
inbuf
+includeIgnorable
INDOC
indx
inf
@@ -720,9 +739,12 @@ iostream
irdp
isArray
isBool
+isDataModified
+isDelimiter
isDictionary
isdigit
isEncrypted
+isIgnorable
isIndirect
isInitialized
isInlineImage
@@ -731,6 +753,7 @@ isLinearized
isName
isNull
isNumber
+isNumeric
iso
isOperator
isOrHasName
@@ -741,10 +764,12 @@ isReal
isReserved
isScalar
isspace
+isSpace
isStream
isString
istream
istype
+isType
italicseq
itemizedlist
iter
@@ -772,6 +797,7 @@ keyset
LARGEFILE
lastnum
lastreleased
+lastTokenWasBad
latin
lbuf
lc
@@ -839,6 +865,7 @@ malloc
manualFinish
Mateusz
maxEnd
+maxlen
maxval
md
mdash
@@ -933,6 +960,7 @@ NTE
ntotal
NUL
num
+numericValue
numrange
nval
nwalsh
@@ -1007,6 +1035,7 @@ parms
parsecontent
parseContentStream
parseInternal
+parsePageContents
ParserCallbacks
parseVersion
partLen
@@ -1033,6 +1062,9 @@ persistAcrossFinish
ph
phe
php
+pipeContentStreams
+PipelineAccessor
+pipePageContents
pipeStreamData
pipeStringAndFinish
Pkey
@@ -1072,6 +1104,7 @@ procset
ProcSet
procsets
programlisting
+programmatically
Projet
prov
provideRandomData
@@ -1106,6 +1139,7 @@ QPDF's
QPDFCONSTANTS
QPDFExc
QPDFFake
+QPDFFakeName
QPDFObject
QPDFObjectHandle
QPDFObjectHandle's
@@ -1170,6 +1204,7 @@ ReleaseResolver
remotesensing
removeKey
removePage
+removereplace
repl
replaceDict
replaceFilterData
@@ -1191,6 +1226,7 @@ retested
reverseResolved
rf
rfont
+rg
rgb
rhs
rijndael
@@ -1240,7 +1276,9 @@ setCompressStreams
setContentNormalization
setDataKey
setDecodeLevel
+setDescription
setDeterministicID
+setDictDescription
setEncryptionParameters
setEncryptionParametersInternal
setExtraHeaderText
@@ -1254,11 +1292,14 @@ setjmp
setLastObjectDescription
setLastOffset
setLinearization
+setLinearizationPass
setLineBuf
setMinimumPDFVersion
setmode
setNewlineBeforeEndstream
setO
+setObjectDescription
+setObjectDescriptionFromInput
setObjectStreamMode
setObjGen
setOutputFile
@@ -1268,6 +1309,7 @@ setOutputPipeline
setOutputStreams
setPasswordIsHexKey
setPCLm
+setPipeline
setprecision
setPreserveEncryption
setPreserveUnreferencedObjects
@@ -1277,6 +1319,7 @@ setRandomDataProvider
setStaticAesIV
setStaticID
setStreamDataMode
+setStreamDescription
setSuppressOriginalObjectIDs
setSuppressWarnings
setTrailer
@@ -1329,8 +1372,10 @@ StreamDataProvider
strerror
StrF
stricmp
+StringCounter
StringDecrypter
stringprep
+StringReverser
stripesize
strlen
strncmp
@@ -1385,10 +1430,13 @@ tobj
tobjid
TODO
toffset
+TokenFilter
+TokenFilters
tokenize
tokenized
tokenizer
tokenizing
+tokenTypeName
toolchain
Toolchains
toupper
@@ -1400,6 +1448,7 @@ trimTrailerForWrite
tt
turbo
txt
+typeWarning
uc
udata
UE
@@ -1428,6 +1477,7 @@ unparse
unparseChild
unparseObject
unparseResolved
+unparsing
unreadCh
unreferenced
unresolvable
diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc
index 31c8d8e2..31f13118 100644
--- a/libqpdf/QPDF.cc
+++ b/libqpdf/QPDF.cc
@@ -106,6 +106,7 @@ QPDF::Members::~Members()
QPDF::QPDF() :
m(new Members())
{
+ m->tokenizer.allowEOF();
}
QPDF::~QPDF()
@@ -272,10 +273,10 @@ QPDF::findHeader()
bool
QPDF::findStartxref()
{
- QPDFTokenizer::Token t = readToken(this->m->file, true);
+ QPDFTokenizer::Token t = readToken(this->m->file);
if (t == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "startxref"))
{
- t = readToken(this->m->file, true);
+ t = readToken(this->m->file);
if (t.getType() == QPDFTokenizer::tt_integer)
{
// Position in front of offset token
@@ -421,7 +422,7 @@ QPDF::reconstruct_xref(QPDFExc& e)
this->m->file->findAndSkipNextEOL();
qpdf_offset_t next_line_start = this->m->file->tell();
this->m->file->seek(line_start, SEEK_SET);
- QPDFTokenizer::Token t1 = readToken(this->m->file, true, MAX_LEN);
+ QPDFTokenizer::Token t1 = readToken(this->m->file, MAX_LEN);
qpdf_offset_t token_start =
this->m->file->tell() - t1.getValue().length();
if (token_start >= next_line_start)
@@ -440,9 +441,9 @@ QPDF::reconstruct_xref(QPDFExc& e)
if (t1.getType() == QPDFTokenizer::tt_integer)
{
QPDFTokenizer::Token t2 =
- readToken(this->m->file, true, MAX_LEN);
+ readToken(this->m->file, MAX_LEN);
QPDFTokenizer::Token t3 =
- readToken(this->m->file, true, MAX_LEN);
+ readToken(this->m->file, MAX_LEN);
if ((t2.getType() == QPDFTokenizer::tt_integer) &&
(t3 == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "obj")))
{
@@ -1429,7 +1430,7 @@ bool
QPDF::findEndstream()
{
// Find endstream or endobj. Position the input at that token.
- QPDFTokenizer::Token t = readToken(this->m->file, true, 20);
+ QPDFTokenizer::Token t = readToken(this->m->file, 20);
if ((t.getType() == QPDFTokenizer::tt_word) &&
((t.getValue() == "endobj") ||
(t.getValue() == "endstream")))
@@ -1522,11 +1523,10 @@ QPDF::recoverStreamLength(PointerHolder<InputSource> input,
}
QPDFTokenizer::Token
-QPDF::readToken(PointerHolder<InputSource> input,
- bool allow_bad, size_t max_len)
+QPDF::readToken(PointerHolder<InputSource> input, size_t max_len)
{
return this->m->tokenizer.readToken(
- input, this->m->last_object_description, allow_bad, max_len);
+ input, this->m->last_object_description, true, max_len);
}
QPDFObjectHandle
@@ -1730,16 +1730,10 @@ QPDF::resolve(int objid, int generation)
}
ResolveRecorder rr(this, og);
- if (! this->m->obj_cache.count(og))
+ // PDF spec says unknown objects resolve to the null object.
+ if ((! this->m->obj_cache.count(og)) && this->m->xref_table.count(og))
{
- if (! this->m->xref_table.count(og))
- {
- // PDF spec says unknown objects resolve to the null object.
- return new QPDF_Null;
- }
-
QPDFXRefEntry const& entry = this->m->xref_table[og];
- bool success = false;
try
{
switch (entry.getType())
@@ -1768,7 +1762,6 @@ QPDF::resolve(int objid, int generation)
QUtil::int_to_string(generation) +
" has unexpected xref entry type");
}
- success = true;
}
catch (QPDFExc& e)
{
@@ -1782,16 +1775,24 @@ QPDF::resolve(int objid, int generation)
QUtil::int_to_string(generation) +
": error reading object: " + e.what()));
}
- if (! success)
- {
- QTC::TC("qpdf", "QPDF resolve failure to null");
- QPDFObjectHandle oh = QPDFObjectHandle::newNull();
- this->m->obj_cache[og] =
- ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1);
- }
+ }
+ if (this->m->obj_cache.count(og) == 0)
+ {
+ QTC::TC("qpdf", "QPDF resolve failure to null");
+ QPDFObjectHandle oh = QPDFObjectHandle::newNull();
+ this->m->obj_cache[og] =
+ ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1);
}
- return this->m->obj_cache[og].object;
+ PointerHolder<QPDFObject> result(this->m->obj_cache[og].object);
+ if (! result->hasDescription())
+ {
+ result->setDescription(
+ this,
+ "object " + QUtil::int_to_string(objid) + " " +
+ QUtil::int_to_string(generation));
+ }
+ return result;
}
void
diff --git a/libqpdf/QPDFExc.cc b/libqpdf/QPDFExc.cc
index 728d4ce8..b816e913 100644
--- a/libqpdf/QPDFExc.cc
+++ b/libqpdf/QPDFExc.cc
@@ -32,7 +32,10 @@ QPDFExc::createWhat(std::string const& filename,
}
if (! (object.empty() && offset == 0))
{
- result += " (";
+ if (! filename.empty())
+ {
+ result += " (";
+ }
if (! object.empty())
{
result += object;
@@ -45,7 +48,10 @@ QPDFExc::createWhat(std::string const& filename,
{
result += "offset " + QUtil::int_to_string(offset);
}
- result += ")";
+ if (! filename.empty())
+ {
+ result += ")";
+ }
}
if (! result.empty())
{
diff --git a/libqpdf/QPDFObject.cc b/libqpdf/QPDFObject.cc
index 8df2b480..cffb8a56 100644
--- a/libqpdf/QPDFObject.cc
+++ b/libqpdf/QPDFObject.cc
@@ -1 +1,36 @@
#include <qpdf/QPDFObject.hh>
+
+QPDFObject::Members::Members() :
+ owning_qpdf(0)
+{
+}
+
+QPDFObject::Members::~Members()
+{
+}
+
+QPDFObject::QPDFObject() :
+ m(new Members)
+{
+}
+
+void
+QPDFObject::setDescription(QPDF* qpdf, std::string const& description)
+{
+ this->m->owning_qpdf = qpdf;
+ this->m->object_description = description;
+}
+
+bool
+QPDFObject::getDescription(QPDF*& qpdf, std::string& description)
+{
+ qpdf = this->m->owning_qpdf;
+ description = this->m->object_description;
+ return this->m->owning_qpdf;
+}
+
+bool
+QPDFObject::hasDescription()
+{
+ return this->m->owning_qpdf;
+}
diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc
index d48461bf..2e9cc996 100644
--- a/libqpdf/QPDFObjectHandle.cc
+++ b/libqpdf/QPDFObjectHandle.cc
@@ -190,6 +190,18 @@ QPDFObjectHandle::releaseResolved()
}
}
+void
+QPDFObjectHandle::setObjectDescriptionFromInput(
+ QPDFObjectHandle object, QPDF* context,
+ std::string const& description, PointerHolder<InputSource> input,
+ qpdf_offset_t offset)
+{
+ object.setObjectDescription(
+ context,
+ input->getName() + ", " + description +
+ " at offset " + QUtil::int_to_string(offset));
+}
+
bool
QPDFObjectHandle::isInitialized() const
{
@@ -282,7 +294,8 @@ QPDFObjectHandle::getNumericValue()
}
else
{
- throw std::logic_error("getNumericValue called for non-numeric object");
+ typeWarning("number", "returning 0");
+ QTC::TC("qpdf", "QPDFObjectHandle numeric non-numeric");
}
return result;
}
@@ -363,8 +376,16 @@ QPDFObjectHandle::isScalar()
bool
QPDFObjectHandle::getBoolValue()
{
- assertBool();
- return dynamic_cast<QPDF_Bool*>(m->obj.getPointer())->getVal();
+ if (isBool())
+ {
+ return dynamic_cast<QPDF_Bool*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("boolean", "returning false");
+ QTC::TC("qpdf", "QPDFObjectHandle boolean returning false");
+ return false;
+ }
}
// Integer accessors
@@ -372,8 +393,16 @@ QPDFObjectHandle::getBoolValue()
long long
QPDFObjectHandle::getIntValue()
{
- assertInteger();
- return dynamic_cast<QPDF_Integer*>(m->obj.getPointer())->getVal();
+ if (isInteger())
+ {
+ return dynamic_cast<QPDF_Integer*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("integer", "returning 0");
+ QTC::TC("qpdf", "QPDFObjectHandle integer returning 0");
+ return 0;
+ }
}
// Real accessors
@@ -381,8 +410,16 @@ QPDFObjectHandle::getIntValue()
std::string
QPDFObjectHandle::getRealValue()
{
- assertReal();
- return dynamic_cast<QPDF_Real*>(m->obj.getPointer())->getVal();
+ if (isReal())
+ {
+ return dynamic_cast<QPDF_Real*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("real", "returning 0.0");
+ QTC::TC("qpdf", "QPDFObjectHandle real returning 0.0");
+ return "0.0";
+ }
}
// Name accessors
@@ -390,8 +427,16 @@ QPDFObjectHandle::getRealValue()
std::string
QPDFObjectHandle::getName()
{
- assertName();
- return dynamic_cast<QPDF_Name*>(m->obj.getPointer())->getName();
+ if (isName())
+ {
+ return dynamic_cast<QPDF_Name*>(m->obj.getPointer())->getName();
+ }
+ else
+ {
+ typeWarning("name", "returning dummy name");
+ QTC::TC("qpdf", "QPDFObjectHandle name returning dummy name");
+ return "/QPDFFakeName";
+ }
}
// String accessors
@@ -399,15 +444,31 @@ QPDFObjectHandle::getName()
std::string
QPDFObjectHandle::getStringValue()
{
- assertString();
- return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getVal();
+ if (isString())
+ {
+ return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("string", "returning empty string");
+ QTC::TC("qpdf", "QPDFObjectHandle string returning empty string");
+ return "";
+ }
}
std::string
QPDFObjectHandle::getUTF8Value()
{
- assertString();
- return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getUTF8Val();
+ if (isString())
+ {
+ return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getUTF8Val();
+ }
+ else
+ {
+ typeWarning("string", "returning empty string");
+ QTC::TC("qpdf", "QPDFObjectHandle string returning empty utf8");
+ return "";
+ }
}
// Operator and Inline Image accessors
@@ -415,15 +476,31 @@ QPDFObjectHandle::getUTF8Value()
std::string
QPDFObjectHandle::getOperatorValue()
{
- assertOperator();
- return dynamic_cast<QPDF_Operator*>(m->obj.getPointer())->getVal();
+ if (isOperator())
+ {
+ return dynamic_cast<QPDF_Operator*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("operator", "returning fake value");
+ QTC::TC("qpdf", "QPDFObjectHandle operator returning fake value");
+ return "QPDFFAKE";
+ }
}
std::string
QPDFObjectHandle::getInlineImageValue()
{
- assertInlineImage();
- return dynamic_cast<QPDF_InlineImage*>(m->obj.getPointer())->getVal();
+ if (isInlineImage())
+ {
+ return dynamic_cast<QPDF_InlineImage*>(m->obj.getPointer())->getVal();
+ }
+ else
+ {
+ typeWarning("inlineimage", "returning empty data");
+ QTC::TC("qpdf", "QPDFObjectHandle inlineimage returning empty data");
+ return "";
+ }
}
// Array accessors
@@ -431,22 +508,66 @@ QPDFObjectHandle::getInlineImageValue()
int
QPDFObjectHandle::getArrayNItems()
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getNItems();
+ if (isArray())
+ {
+ return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getNItems();
+ }
+ else
+ {
+ typeWarning("array", "treating as empty");
+ QTC::TC("qpdf", "QPDFObjectHandle array treating as empty");
+ return 0;
+ }
}
QPDFObjectHandle
QPDFObjectHandle::getArrayItem(int n)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getItem(n);
+ QPDFObjectHandle result;
+ if (isArray() && (n < getArrayNItems()) && (n >= 0))
+ {
+ result = dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getItem(n);
+ }
+ else
+ {
+ result = newNull();
+ if (isArray())
+ {
+ objectWarning("returning null for out of bounds array access");
+ QTC::TC("qpdf", "QPDFObjectHandle array bounds");
+ }
+ else
+ {
+ typeWarning("array", "returning null");
+ QTC::TC("qpdf", "QPDFObjectHandle array null for non-array");
+ }
+ QPDF* context = 0;
+ std::string description;
+ if (this->m->obj->getDescription(context, description))
+ {
+ result.setObjectDescription(
+ context,
+ description +
+ " -> null returned from invalid array access");
+ }
+ }
+ return result;
}
std::vector<QPDFObjectHandle>
QPDFObjectHandle::getArrayAsVector()
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getAsVector();
+ std::vector<QPDFObjectHandle> result;
+ if (isArray())
+ {
+ result = dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getAsVector();
+ }
+ else
+ {
+ typeWarning("array", "treating as empty");
+ QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector");
+ }
+ return result;
}
// Array mutators
@@ -454,36 +575,79 @@ QPDFObjectHandle::getArrayAsVector()
void
QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setItem(n, item);
+ if (isArray())
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setItem(n, item);
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to set item");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item");
+ }
}
void
QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setFromVector(items);
+ if (isArray())
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setFromVector(items);
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to replace items");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items");
+ }
}
void
QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->insertItem(at, item);
+ if (isArray())
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->insertItem(at, item);
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to insert item");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring insert item");
+ }
}
void
QPDFObjectHandle::appendItem(QPDFObjectHandle const& item)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->appendItem(item);
+ if (isArray())
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->appendItem(item);
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to append item");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item");
+ }
}
void
QPDFObjectHandle::eraseItem(int at)
{
- assertArray();
- return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->eraseItem(at);
+ if (isArray() && (at < getArrayNItems()) && (at >= 0))
+ {
+ dynamic_cast<QPDF_Array*>(m->obj.getPointer())->eraseItem(at);
+ }
+ else
+ {
+ if (isArray())
+ {
+ objectWarning("ignoring attempt to erase out of bounds array item");
+ QTC::TC("qpdf", "QPDFObjectHandle erase array bounds");
+ }
+ else
+ {
+ typeWarning("array", "ignoring attempt to erase item");
+ QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item");
+ }
+ }
}
// Dictionary accessors
@@ -491,29 +655,79 @@ QPDFObjectHandle::eraseItem(int at)
bool
QPDFObjectHandle::hasKey(std::string const& key)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->hasKey(key);
+ if (isDictionary())
+ {
+ return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->hasKey(key);
+ }
+ else
+ {
+ typeWarning("dictionary",
+ "returning false for a key containment request");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey");
+ return false;
+ }
}
QPDFObjectHandle
QPDFObjectHandle::getKey(std::string const& key)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getKey(key);
+ QPDFObjectHandle result;
+ if (isDictionary())
+ {
+ result = dynamic_cast<QPDF_Dictionary*>(
+ m->obj.getPointer())->getKey(key);
+ }
+ else
+ {
+ typeWarning(
+ "dictionary", "returning null for attempted key retrieval");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey");
+ result = newNull();
+ QPDF* qpdf = 0;
+ std::string description;
+ if (this->m->obj->getDescription(qpdf, description))
+ {
+ result.setObjectDescription(
+ qpdf,
+ description +
+ " -> null returned from getting key " +
+ key + " from non-Dictionary");
+ }
+ }
+ return result;
}
std::set<std::string>
QPDFObjectHandle::getKeys()
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getKeys();
+ std::set<std::string> result;
+ if (isDictionary())
+ {
+ result = dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getKeys();
+ }
+ else
+ {
+ typeWarning("dictionary", "treating as empty");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
+ }
+ return result;
}
std::map<std::string, QPDFObjectHandle>
QPDFObjectHandle::getDictAsMap()
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getAsMap();
+ std::map<std::string, QPDFObjectHandle> result;
+ if (isDictionary())
+ {
+ result = dynamic_cast<QPDF_Dictionary*>(
+ m->obj.getPointer())->getAsMap();
+ }
+ else
+ {
+ typeWarning("dictionary", "treating as empty");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap");
+ }
+ return result;
}
// Array and Name accessors
@@ -551,27 +765,48 @@ QPDFObjectHandle::getOwningQPDF()
void
QPDFObjectHandle::replaceKey(std::string const& key,
- QPDFObjectHandle const& value)
+ QPDFObjectHandle value)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(
- m->obj.getPointer())->replaceKey(key, value);
+ if (isDictionary())
+ {
+ dynamic_cast<QPDF_Dictionary*>(
+ m->obj.getPointer())->replaceKey(key, value);
+ }
+ else
+ {
+ typeWarning("dictionary", "ignoring key replacement request");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");
+ }
}
void
QPDFObjectHandle::removeKey(std::string const& key)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->removeKey(key);
+ if (isDictionary())
+ {
+ dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->removeKey(key);
+ }
+ else
+ {
+ typeWarning("dictionary", "ignoring key removal request");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey");
+ }
}
void
QPDFObjectHandle::replaceOrRemoveKey(std::string const& key,
QPDFObjectHandle value)
{
- assertDictionary();
- return dynamic_cast<QPDF_Dictionary*>(
- m->obj.getPointer())->replaceOrRemoveKey(key, value);
+ if (isDictionary())
+ {
+ dynamic_cast<QPDF_Dictionary*>(
+ m->obj.getPointer())->replaceOrRemoveKey(key, value);
+ }
+ else
+ {
+ typeWarning("dictionary", "ignoring key removal/replacement request");
+ QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removereplace");
+ }
}
// Stream accessors
@@ -1173,35 +1408,45 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
std::vector<parser_state_e> state_stack;
state_stack.push_back(st_top);
std::vector<qpdf_offset_t> offset_stack;
- offset_stack.push_back(input->tell());
+ qpdf_offset_t offset = input->tell();
+ offset_stack.push_back(offset);
bool done = false;
while (! done)
{
std::vector<QPDFObjectHandle>& olist = olist_stack.back();
parser_state_e state = state_stack.back();
- qpdf_offset_t offset = offset_stack.back();
+ offset = offset_stack.back();
object = QPDFObjectHandle();
QPDFTokenizer::Token token =
- tokenizer.readToken(input, object_description);
+ tokenizer.readToken(input, object_description, true);
switch (token.getType())
{
case QPDFTokenizer::tt_eof:
- if (content_stream)
+ if (! content_stream)
{
- state = st_eof;
- }
- else
- {
- // When not in content stream mode, EOF is tt_bad and
- // throws an exception before we get here.
- throw std::logic_error(
- "EOF received while not in content stream mode");
+ QTC::TC("qpdf", "QPDFObjectHandle eof in parseInternal");
+ warn(context,
+ QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ object_description,
+ input->getLastOffset(),
+ "unexpected EOF"));
}
+ state = st_eof;
break;
+ case QPDFTokenizer::tt_bad:
+ QTC::TC("qpdf", "QPDFObjectHandle bad token in parse");
+ warn(context,
+ QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+ object_description,
+ input->getLastOffset(),
+ token.getErrorMessage()));
+ object = newNull();
+ break;
+
case QPDFTokenizer::tt_brace_open:
case QPDFTokenizer::tt_brace_close:
QTC::TC("qpdf", "QPDFObjectHandle bad brace");
@@ -1375,11 +1620,19 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
"parse error while reading object"));
}
done = true;
- // Leave object uninitialized to indicate EOF
+ // In content stream mode, leave object uninitialized to
+ // indicate EOF
+ if (! content_stream)
+ {
+ object = newNull();
+ }
break;
case st_dictionary:
case st_array:
+ setObjectDescriptionFromInput(
+ object, context, object_description, input,
+ input->getLastOffset());
olist.push_back(object);
break;
@@ -1402,6 +1655,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
if (old_state == st_array)
{
object = newArray(olist);
+ setObjectDescriptionFromInput(
+ object, context, object_description, input, offset);
}
else if (old_state == st_dictionary)
{
@@ -1458,6 +1713,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
"dictionary ended prematurely; "
"using null as value for last key"));
val = newNull();
+ setObjectDescriptionFromInput(
+ val, context, object_description, input, offset);
}
else
{
@@ -1466,6 +1723,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
dict[key_obj.getName()] = val;
}
object = newDictionary(dict);
+ setObjectDescriptionFromInput(
+ object, context, object_description, input, offset);
}
olist_stack.pop_back();
offset_stack.pop_back();
@@ -1480,6 +1739,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
}
}
+ setObjectDescriptionFromInput(
+ object, context, object_description, input, offset);
return object;
}
@@ -1635,6 +1896,26 @@ QPDFObjectHandle::newReserved(QPDF* qpdf)
return result;
}
+void
+QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf,
+ std::string const& object_description)
+{
+ if (isInitialized() && this->m->obj.getPointer())
+ {
+ this->m->obj->setDescription(owning_qpdf, object_description);
+ }
+}
+
+bool
+QPDFObjectHandle::hasObjectDescription()
+{
+ if (isInitialized() && this->m->obj.getPointer())
+ {
+ return this->m->obj->hasDescription();
+ }
+ return false;
+}
+
QPDFObjectHandle
QPDFObjectHandle::shallowCopy()
{
@@ -1793,85 +2074,127 @@ QPDFObjectHandle::assertInitialized() const
}
void
-QPDFObjectHandle::assertType(char const* type_name, bool istype) const
+QPDFObjectHandle::typeWarning(char const* expected_type,
+ std::string const& warning)
+{
+ QPDF* context = 0;
+ std::string description;
+ if (this->m->obj->getDescription(context, description))
+ {
+ warn(context,
+ QPDFExc(
+ qpdf_e_damaged_pdf,
+ "", description, 0,
+ std::string("operation for ") + expected_type +
+ " attempted on object of type " +
+ getTypeName() + ": " + warning));
+ }
+ else
+ {
+ assertType(expected_type, false);
+ }
+}
+
+void
+QPDFObjectHandle::objectWarning(std::string const& warning)
+{
+ QPDF* context = 0;
+ std::string description;
+ if (this->m->obj->getDescription(context, description))
+ {
+ warn(context,
+ QPDFExc(
+ qpdf_e_damaged_pdf,
+ "", description, 0,
+ warning));
+ }
+ else
+ {
+ throw std::logic_error(warning);
+ }
+}
+
+void
+QPDFObjectHandle::assertType(char const* type_name, bool istype)
{
if (! istype)
{
throw std::logic_error(std::string("operation for ") + type_name +
- " object attempted on object of wrong type");
+ " attempted on object of type " +
+ getTypeName());
}
}
void
QPDFObjectHandle::assertNull()
{
- assertType("Null", isNull());
+ assertType("null", isNull());
}
void
QPDFObjectHandle::assertBool()
{
- assertType("Boolean", isBool());
+ assertType("boolean", isBool());
}
void
QPDFObjectHandle::assertInteger()
{
- assertType("Integer", isInteger());
+ assertType("integer", isInteger());
}
void
QPDFObjectHandle::assertReal()
{
- assertType("Real", isReal());
+ assertType("real", isReal());
}
void
QPDFObjectHandle::assertName()
{
- assertType("Name", isName());
+ assertType("name", isName());
}
void
QPDFObjectHandle::assertString()
{
- assertType("String", isString());
+ assertType("string", isString());
}
void
QPDFObjectHandle::assertOperator()
{
- assertType("Operator", isOperator());
+ assertType("operator", isOperator());
}
void
QPDFObjectHandle::assertInlineImage()
{
- assertType("InlineImage", isInlineImage());
+ assertType("inlineimage", isInlineImage());
}
void
QPDFObjectHandle::assertArray()
{
- assertType("Array", isArray());
+ assertType("array", isArray());
}
void
QPDFObjectHandle::assertDictionary()
{
- assertType("Dictionary", isDictionary());
+ assertType("dictionary", isDictionary());
}
void
QPDFObjectHandle::assertStream()
{
- assertType("Stream", isStream());
+ assertType("stream", isStream());
}
void
QPDFObjectHandle::assertReserved()
{
- assertType("Reserved", isReserved());
+ assertType("reserved", isReserved());
}
void
@@ -1887,13 +2210,13 @@ QPDFObjectHandle::assertIndirect()
void
QPDFObjectHandle::assertScalar()
{
- assertType("Scalar", isScalar());
+ assertType("scalar", isScalar());
}
void
QPDFObjectHandle::assertNumber()
{
- assertType("Number", isNumber());
+ assertType("number", isNumber());
}
bool
@@ -1928,7 +2251,8 @@ QPDFObjectHandle::dereference()
this->m->qpdf, this->m->objid, this->m->generation);
if (obj.getPointer() == 0)
{
- QTC::TC("qpdf", "QPDFObjectHandle indirect to unknown");
+ // QPDF::resolve never returns an uninitialized object, but
+ // check just in case.
this->m->obj = new QPDF_Null();
}
else if (dynamic_cast<QPDF_Reserved*>(obj.getPointer()))
diff --git a/libqpdf/QPDFTokenizer.cc b/libqpdf/QPDFTokenizer.cc
index c3a017d0..95551e7c 100644
--- a/libqpdf/QPDFTokenizer.cc
+++ b/libqpdf/QPDFTokenizer.cc
@@ -640,7 +640,9 @@ QPDFTokenizer::readToken(PointerHolder<InputSource> input,
presented_eof = true;
if ((this->m->type == tt_eof) && (! this->m->allow_eof))
{
- QTC::TC("qpdf", "QPDFTokenizer EOF when not allowed");
+ // Nothing in the qpdf library calls readToken
+ // without allowEOF anymore, so this case is not
+ // exercised.
this->m->type = tt_bad;
this->m->error_message = "unexpected EOF";
offset = input->getLastOffset();
@@ -677,7 +679,10 @@ QPDFTokenizer::readToken(PointerHolder<InputSource> input,
input->unreadCh(char_to_unread);
}
- input->setLastOffset(offset);
+ if (token.getType() != tt_eof)
+ {
+ input->setLastOffset(offset);
+ }
if (token.getType() == tt_bad)
{
diff --git a/libqpdf/QPDF_Array.cc b/libqpdf/QPDF_Array.cc
index c526174f..1a4ba61d 100644
--- a/libqpdf/QPDF_Array.cc
+++ b/libqpdf/QPDF_Array.cc
@@ -1,4 +1,5 @@
#include <qpdf/QPDF_Array.hh>
+#include <qpdf/QUtil.hh>
#include <stdexcept>
QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& items) :
@@ -46,6 +47,12 @@ QPDF_Array::getTypeName() const
return "array";
}
+void
+QPDF_Array::setDescription(QPDF* qpdf, std::string const& description)
+{
+ this->QPDFObject::setDescription(qpdf, description);
+}
+
int
QPDF_Array::getNItems() const
{
diff --git a/libqpdf/QPDF_Dictionary.cc b/libqpdf/QPDF_Dictionary.cc
index 0af2f4bf..df640354 100644
--- a/libqpdf/QPDF_Dictionary.cc
+++ b/libqpdf/QPDF_Dictionary.cc
@@ -51,6 +51,12 @@ QPDF_Dictionary::getTypeName() const
return "dictionary";
}
+void
+QPDF_Dictionary::setDescription(QPDF* qpdf, std::string const& description)
+{
+ this->QPDFObject::setDescription(qpdf, description);
+}
+
bool
QPDF_Dictionary::hasKey(std::string const& key)
{
@@ -70,7 +76,15 @@ QPDF_Dictionary::getKey(std::string const& key)
}
else
{
- return QPDFObjectHandle::newNull();
+ QPDFObjectHandle null = QPDFObjectHandle::newNull();
+ QPDF* qpdf = 0;
+ std::string description;
+ if (getDescription(qpdf, description))
+ {
+ null.setObjectDescription(
+ qpdf, description + " -> dictionary key " + key);
+ }
+ return null;
}
}
@@ -93,13 +107,12 @@ QPDF_Dictionary::getKeys()
std::map<std::string, QPDFObjectHandle> const&
QPDF_Dictionary::getAsMap() const
{
-
return this->items;
}
void
QPDF_Dictionary::replaceKey(std::string const& key,
- QPDFObjectHandle const& value)
+ QPDFObjectHandle value)
{
// add or replace value
this->items[key] = value;
diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc
index 7b84d10c..384652e2 100644
--- a/libqpdf/QPDF_Stream.cc
+++ b/libqpdf/QPDF_Stream.cc
@@ -39,6 +39,7 @@ QPDF_Stream::QPDF_Stream(QPDF* qpdf, int objid, int generation,
"stream object instantiated with non-dictionary "
"object for dictionary");
}
+ setStreamDescription();
}
QPDF_Stream::~QPDF_Stream()
@@ -85,6 +86,35 @@ QPDF_Stream::getTypeName() const
return "stream";
}
+void
+QPDF_Stream::setDescription(QPDF* qpdf, std::string const& description)
+{
+ this->QPDFObject::setDescription(qpdf, description);
+ setDictDescription();
+}
+
+void
+QPDF_Stream::setStreamDescription()
+{
+ setDescription(
+ this->qpdf,
+ "stream object " + QUtil::int_to_string(this->objid) + " " +
+ QUtil::int_to_string(this->generation));
+}
+
+void
+QPDF_Stream::setDictDescription()
+{
+ QPDF* qpdf = 0;
+ std::string description;
+ if ((! this->stream_dict.hasObjectDescription()) &&
+ getDescription(qpdf, description))
+ {
+ this->stream_dict.setObjectDescription(
+ qpdf, description + " -> stream dictionary");
+ }
+}
+
QPDFObjectHandle
QPDF_Stream::getDict() const
{
@@ -688,6 +718,7 @@ void
QPDF_Stream::replaceDict(QPDFObjectHandle new_dict)
{
this->stream_dict = new_dict;
+ setDictDescription();
QPDFObjectHandle length_obj = new_dict.getKey("/Length");
if (length_obj.isInteger())
{
diff --git a/libqpdf/QPDF_linearization.cc b/libqpdf/QPDF_linearization.cc
index 3d04ab90..ecf81bee 100644
--- a/libqpdf/QPDF_linearization.cc
+++ b/libqpdf/QPDF_linearization.cc
@@ -121,10 +121,10 @@ QPDF::isLinearized()
++p;
}
- QPDFTokenizer::Token t1 = readToken(this->m->file, true);
- QPDFTokenizer::Token t2 = readToken(this->m->file, true);
- QPDFTokenizer::Token t3 = readToken(this->m->file, true);
- QPDFTokenizer::Token t4 = readToken(this->m->file, true);
+ QPDFTokenizer::Token t1 = readToken(this->m->file);
+ QPDFTokenizer::Token t2 = readToken(this->m->file);
+ QPDFTokenizer::Token t3 = readToken(this->m->file);
+ QPDFTokenizer::Token t4 = readToken(this->m->file);
if ((t1.getType() == QPDFTokenizer::tt_integer) &&
(t2.getType() == QPDFTokenizer::tt_integer) &&
(t3 == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "obj")) &&
diff --git a/libqpdf/qpdf/QPDF_Array.hh b/libqpdf/qpdf/QPDF_Array.hh
index e81f8664..8a23da35 100644
--- a/libqpdf/qpdf/QPDF_Array.hh
+++ b/libqpdf/qpdf/QPDF_Array.hh
@@ -14,6 +14,7 @@ class QPDF_Array: public QPDFObject
virtual std::string unparse();
virtual QPDFObject::object_type_e getTypeCode() const;
virtual char const* getTypeName() const;
+ virtual void setDescription(QPDF*, std::string const&);
int getNItems() const;
QPDFObjectHandle getItem(int n) const;
diff --git a/libqpdf/qpdf/QPDF_Dictionary.hh b/libqpdf/qpdf/QPDF_Dictionary.hh
index 5b5630cf..cea63835 100644
--- a/libqpdf/qpdf/QPDF_Dictionary.hh
+++ b/libqpdf/qpdf/QPDF_Dictionary.hh
@@ -16,6 +16,7 @@ class QPDF_Dictionary: public QPDFObject
virtual std::string unparse();
virtual QPDFObject::object_type_e getTypeCode() const;
virtual char const* getTypeName() const;
+ virtual void setDescription(QPDF*, std::string const&);
// hasKey() and getKeys() treat keys with null values as if they
// aren't there. getKey() returns null for the value of a
@@ -26,7 +27,7 @@ class QPDF_Dictionary: public QPDFObject
std::map<std::string, QPDFObjectHandle> const& getAsMap() const;
// Replace value of key, adding it if it does not exist
- void replaceKey(std::string const& key, QPDFObjectHandle const&);
+ void replaceKey(std::string const& key, QPDFObjectHandle);
// Remove key, doing nothing if key does not exist
void removeKey(std::string const& key);
// If object is null, replace key; otherwise, remove key
diff --git a/libqpdf/qpdf/QPDF_Stream.hh b/libqpdf/qpdf/QPDF_Stream.hh
index 86b796cf..98b8c11f 100644
--- a/libqpdf/qpdf/QPDF_Stream.hh
+++ b/libqpdf/qpdf/QPDF_Stream.hh
@@ -19,6 +19,7 @@ class QPDF_Stream: public QPDFObject
virtual std::string unparse();
virtual QPDFObject::object_type_e getTypeCode() const;
virtual char const* getTypeName() const;
+ virtual void setDescription(QPDF*, std::string const&);
QPDFObjectHandle getDict() const;
bool isDataModified() const;
@@ -66,6 +67,8 @@ class QPDF_Stream: public QPDFObject
int& colors, int& bits_per_component,
bool& early_code_change);
void warn(QPDFExc const& e);
+ void setDictDescription();
+ void setStreamDescription();
QPDF* qpdf;
int objid;
diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov
index 2c51867f..67cbfe7f 100644
--- a/qpdf/qpdf.testcov
+++ b/qpdf/qpdf.testcov
@@ -105,7 +105,6 @@ QPDF reconstructed xref table 0
QPDF recovered in readObjectAtOffset 0
QPDF recovered stream length 0
QPDF found wrong endstream in recovery 0
-QPDFObjectHandle indirect to unknown 0
QPDF_Stream pipeStreamData with null pipeline 0
QPDFWriter not recompressing /FlateDecode 0
QPDF_encryption xref stream from encrypted file 0
@@ -300,10 +299,37 @@ qpdf-c called qpdf_set_compress_streams 0
qpdf-c called qpdf_set_preserve_unreferenced_objects 0
qpdf-c called qpdf_set_newline_before_endstream 0
QPDF_Stream TIFF predictor 0
-QPDFTokenizer EOF when not allowed 0
QPDFTokenizer inline image at EOF 0
Pl_QPDFTokenizer found ID 0
QPDFObjectHandle non-stream in stream array 0
QPDFObjectHandle coalesce called on stream 0
QPDFObjectHandle coalesce provide stream data 0
QPDF_Stream bad token at end during normalize 0
+QPDFObjectHandle bad token in parse 0
+QPDFObjectHandle eof in parseInternal 0
+QPDFObjectHandle array bounds 0
+QPDFObjectHandle boolean returning false 0
+QPDFObjectHandle integer returning 0 0
+QPDFObjectHandle real returning 0.0 0
+QPDFObjectHandle name returning dummy name 0
+QPDFObjectHandle string returning empty string 0
+QPDFObjectHandle string returning empty utf8 0
+QPDFObjectHandle operator returning fake value 0
+QPDFObjectHandle inlineimage returning empty data 0
+QPDFObjectHandle array treating as empty 0
+QPDFObjectHandle array null for non-array 0
+QPDFObjectHandle array treating as empty vector 0
+QPDFObjectHandle array ignoring set item 0
+QPDFObjectHandle array ignoring replace items 0
+QPDFObjectHandle array ignoring insert item 0
+QPDFObjectHandle array ignoring append item 0
+QPDFObjectHandle array ignoring erase item 0
+QPDFObjectHandle dictionary false for hasKey 0
+QPDFObjectHandle dictionary null for getKey 0
+QPDFObjectHandle dictionary empty set for getKeys 0
+QPDFObjectHandle dictionary empty map for asMap 0
+QPDFObjectHandle dictionary ignoring replaceKey 0
+QPDFObjectHandle dictionary ignoring removeKey 0
+QPDFObjectHandle dictionary ignoring removereplace 0
+QPDFObjectHandle numeric non-numeric 0
+QPDFObjectHandle erase array bounds 0
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index 45c750fd..b5939703 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -212,7 +212,7 @@ my @bug_tests = (
["99", "object 0", 2],
["99b", "object 0", 2],
["100", "xref reconstruction loop", 2],
- ["101", "resolve for exception text", 2],
+ ["101", "resolve for exception text", 3],
["117", "other infinite loop", 2],
["118", "other infinite loop", 2],
["119", "other infinite loop", 3],
@@ -736,6 +736,33 @@ $td->runtest("stream with tiff predictor",
show_ntests();
# ----------
+$td->notify("--- Type checks ---");
+$n_tests += 4;
+# Whenever object-types.pdf is edited, object-types-os.pdf should be
+# regenerated.
+$td->runtest("ensure object-types-os is up-to-date",
+ {$td->COMMAND =>
+ "qpdf" .
+ " --object-streams=generate" .
+ " --deterministic-id" .
+ " --stream-data=uncompress" .
+ " object-types.pdf a.pdf"},
+ {$td->STRING => "", $td->EXIT_STATUS => 0});
+$td->runtest("check file",
+ {$td->FILE => "a.pdf"},
+ {$td->FILE => "object-types-os.pdf"});
+$td->runtest("type checks",
+ {$td->COMMAND => "test_driver 42 object-types.pdf"},
+ {$td->FILE => "object-types.out",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("type checks with object streams",
+ {$td->COMMAND => "test_driver 42 object-types-os.pdf"},
+ {$td->FILE => "object-types-os.out",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
+# ----------
$td->notify("--- Coalesce contents ---");
$n_tests += 6;
@@ -1200,7 +1227,7 @@ $n_tests += @badfiles + 3;
# have error conditions that used to be fatal but are now considered
# non-fatal.
my %badtest_overrides = ();
-for(6, 12..15, 17, 22..28, 30..32, 34, 36)
+for(6, 12..15, 17, 18..32, 34, 36)
{
$badtest_overrides{$_} = 0;
}
@@ -1243,7 +1270,7 @@ $n_tests += @badfiles + 6;
# though in some cases it may. Acrobat Reader would not be able to
# recover any of these files any better.
my %recover_failures = ();
-for (1, 7, 16, 18..21, 29, 35)
+for (1, 7, 16, 35)
{
$recover_failures{$_} = 1;
}
diff --git a/qpdf/qtest/qpdf/bad16-recover.out b/qpdf/qtest/qpdf/bad16-recover.out
index 5ed231d8..adddb4f7 100644
--- a/qpdf/qtest/qpdf/bad16-recover.out
+++ b/qpdf/qtest/qpdf/bad16-recover.out
@@ -1,10 +1,14 @@
WARNING: bad16.pdf (trailer, offset 753): unexpected dictionary close token
WARNING: bad16.pdf (trailer, offset 756): unexpected dictionary close token
WARNING: bad16.pdf (trailer, offset 759): unknown token while reading object; treating as string
-WARNING: bad16.pdf: file is damaged
WARNING: bad16.pdf (trailer, offset 779): unexpected EOF
+WARNING: bad16.pdf (trailer, offset 779): parse error while reading object
+WARNING: bad16.pdf: file is damaged
+WARNING: bad16.pdf (offset 712): expected trailer dictionary
WARNING: bad16.pdf: Attempting to reconstruct cross-reference table
WARNING: bad16.pdf (trailer, offset 753): unexpected dictionary close token
WARNING: bad16.pdf (trailer, offset 756): unexpected dictionary close token
WARNING: bad16.pdf (trailer, offset 759): unknown token while reading object; treating as string
-bad16.pdf (trailer, offset 779): unexpected EOF
+WARNING: bad16.pdf (trailer, offset 779): unexpected EOF
+WARNING: bad16.pdf (trailer, offset 779): parse error while reading object
+bad16.pdf: unable to find trailer dictionary while recovering damaged file
diff --git a/qpdf/qtest/qpdf/bad16.out b/qpdf/qtest/qpdf/bad16.out
index 1018bd7b..bcc37f35 100644
--- a/qpdf/qtest/qpdf/bad16.out
+++ b/qpdf/qtest/qpdf/bad16.out
@@ -1,4 +1,6 @@
WARNING: bad16.pdf (trailer, offset 753): unexpected dictionary close token
WARNING: bad16.pdf (trailer, offset 756): unexpected dictionary close token
WARNING: bad16.pdf (trailer, offset 759): unknown token while reading object; treating as string
-bad16.pdf (trailer, offset 779): unexpected EOF
+WARNING: bad16.pdf (trailer, offset 779): unexpected EOF
+WARNING: bad16.pdf (trailer, offset 779): parse error while reading object
+bad16.pdf (offset 712): expected trailer dictionary
diff --git a/qpdf/qtest/qpdf/bad18-recover.out b/qpdf/qtest/qpdf/bad18-recover.out
index c14bc1f3..7814c8ac 100644
--- a/qpdf/qtest/qpdf/bad18-recover.out
+++ b/qpdf/qtest/qpdf/bad18-recover.out
@@ -1,4 +1,7 @@
-WARNING: bad18.pdf: file is damaged
WARNING: bad18.pdf (trailer, offset 753): unexpected )
-WARNING: bad18.pdf: Attempting to reconstruct cross-reference table
-bad18.pdf (trailer, offset 753): unexpected )
+/QTest is implicit
+/QTest is direct and has type null (2)
+/QTest is null
+unparse: null
+unparseResolved: null
+test 1 done
diff --git a/qpdf/qtest/qpdf/bad18.out b/qpdf/qtest/qpdf/bad18.out
index b6bce222..53d64cb1 100644
--- a/qpdf/qtest/qpdf/bad18.out
+++ b/qpdf/qtest/qpdf/bad18.out
@@ -1 +1,7 @@
-bad18.pdf (trailer, offset 753): unexpected )
+WARNING: bad18.pdf (trailer, offset 753): unexpected )
+/QTest is implicit
+/QTest is direct and has type null (2)
+/QTest is null
+unparse: null
+unparseResolved: null
+test 0 done
diff --git a/qpdf/qtest/qpdf/bad19-recover.out b/qpdf/qtest/qpdf/bad19-recover.out
index ced8f51a..a828e0ed 100644
--- a/qpdf/qtest/qpdf/bad19-recover.out
+++ b/qpdf/qtest/qpdf/bad19-recover.out
@@ -1,4 +1,7 @@
-WARNING: bad19.pdf: file is damaged
WARNING: bad19.pdf (trailer, offset 753): unexpected >
-WARNING: bad19.pdf: Attempting to reconstruct cross-reference table
-bad19.pdf (trailer, offset 753): unexpected >
+/QTest is implicit
+/QTest is direct and has type null (2)
+/QTest is null
+unparse: null
+unparseResolved: null
+test 1 done
diff --git a/qpdf/qtest/qpdf/bad19.out b/qpdf/qtest/qpdf/bad19.out
index 36eda04f..eafe3d88 100644
--- a/qpdf/qtest/qpdf/bad19.out
+++ b/qpdf/qtest/qpdf/bad19.out
@@ -1 +1,7 @@
-bad19.pdf (trailer, offset 753): unexpected >
+WARNING: bad19.pdf (trailer, offset 753): unexpected >
+/QTest is implicit
+/QTest is direct and has type null (2)
+/QTest is null
+unparse: null
+unparseResolved: null
+test 0 done
diff --git a/qpdf/qtest/qpdf/bad20-recover.out b/qpdf/qtest/qpdf/bad20-recover.out
index 8411d5a9..a9507671 100644
--- a/qpdf/qtest/qpdf/bad20-recover.out
+++ b/qpdf/qtest/qpdf/bad20-recover.out
@@ -1,4 +1,11 @@
-WARNING: bad20.pdf: file is damaged
WARNING: bad20.pdf (trailer, offset 753): invalid character (q) in hexstring
-WARNING: bad20.pdf: Attempting to reconstruct cross-reference table
-bad20.pdf (trailer, offset 753): invalid character (q) in hexstring
+WARNING: bad20.pdf (trailer, offset 757): unknown token while reading object; treating as string
+WARNING: bad20.pdf (trailer, offset 758): unexpected >
+WARNING: bad20.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake1
+WARNING: bad20.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake2
+/QTest is implicit
+/QTest is direct and has type null (2)
+/QTest is null
+unparse: null
+unparseResolved: null
+test 1 done
diff --git a/qpdf/qtest/qpdf/bad20.out b/qpdf/qtest/qpdf/bad20.out
index 8cdad1f1..7253932b 100644
--- a/qpdf/qtest/qpdf/bad20.out
+++ b/qpdf/qtest/qpdf/bad20.out
@@ -1 +1,11 @@
-bad20.pdf (trailer, offset 753): invalid character (q) in hexstring
+WARNING: bad20.pdf (trailer, offset 753): invalid character (q) in hexstring
+WARNING: bad20.pdf (trailer, offset 757): unknown token while reading object; treating as string
+WARNING: bad20.pdf (trailer, offset 758): unexpected >
+WARNING: bad20.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake1
+WARNING: bad20.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake2
+/QTest is implicit
+/QTest is direct and has type null (2)
+/QTest is null
+unparse: null
+unparseResolved: null
+test 0 done
diff --git a/qpdf/qtest/qpdf/bad21-recover.out b/qpdf/qtest/qpdf/bad21-recover.out
index ff483eff..cbf55baf 100644
--- a/qpdf/qtest/qpdf/bad21-recover.out
+++ b/qpdf/qtest/qpdf/bad21-recover.out
@@ -1,4 +1,9 @@
-WARNING: bad21.pdf: file is damaged
WARNING: bad21.pdf (trailer, offset 742): invalid name token
-WARNING: bad21.pdf: Attempting to reconstruct cross-reference table
-bad21.pdf (trailer, offset 742): invalid name token
+WARNING: bad21.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake1
+WARNING: bad21.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake2
+/QTest is implicit
+/QTest is direct and has type null (2)
+/QTest is null
+unparse: null
+unparseResolved: null
+test 1 done
diff --git a/qpdf/qtest/qpdf/bad21.out b/qpdf/qtest/qpdf/bad21.out
index b1a57fef..eab6f636 100644
--- a/qpdf/qtest/qpdf/bad21.out
+++ b/qpdf/qtest/qpdf/bad21.out
@@ -1 +1,9 @@
-bad21.pdf (trailer, offset 742): invalid name token
+WARNING: bad21.pdf (trailer, offset 742): invalid name token
+WARNING: bad21.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake1
+WARNING: bad21.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake2
+/QTest is implicit
+/QTest is direct and has type null (2)
+/QTest is null
+unparse: null
+unparseResolved: null
+test 0 done
diff --git a/qpdf/qtest/qpdf/bad29-recover.out b/qpdf/qtest/qpdf/bad29-recover.out
index bc6c9b09..2f78b306 100644
--- a/qpdf/qtest/qpdf/bad29-recover.out
+++ b/qpdf/qtest/qpdf/bad29-recover.out
@@ -1,4 +1,9 @@
-WARNING: bad29.pdf: file is damaged
WARNING: bad29.pdf (trailer, offset 742): null character not allowed in name token
-WARNING: bad29.pdf: Attempting to reconstruct cross-reference table
-bad29.pdf (trailer, offset 742): null character not allowed in name token
+WARNING: bad29.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake1
+WARNING: bad29.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake2
+/QTest is implicit
+/QTest is direct and has type null (2)
+/QTest is null
+unparse: null
+unparseResolved: null
+test 1 done
diff --git a/qpdf/qtest/qpdf/bad29.out b/qpdf/qtest/qpdf/bad29.out
index e9ded462..f1e8aa38 100644
--- a/qpdf/qtest/qpdf/bad29.out
+++ b/qpdf/qtest/qpdf/bad29.out
@@ -1 +1,9 @@
-bad29.pdf (trailer, offset 742): null character not allowed in name token
+WARNING: bad29.pdf (trailer, offset 742): null character not allowed in name token
+WARNING: bad29.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake1
+WARNING: bad29.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake2
+/QTest is implicit
+/QTest is direct and has type null (2)
+/QTest is null
+unparse: null
+unparseResolved: null
+test 0 done
diff --git a/qpdf/qtest/qpdf/issue-100.out b/qpdf/qtest/qpdf/issue-100.out
index e5007541..da286551 100644
--- a/qpdf/qtest/qpdf/issue-100.out
+++ b/qpdf/qtest/qpdf/issue-100.out
@@ -8,6 +8,9 @@ WARNING: issue-100.pdf (object 5 0, offset 294): unknown token while reading obj
WARNING: issue-100.pdf (object 5 0, offset 297): unknown token while reading object; treating as string
WARNING: issue-100.pdf (object 5 0, offset 304): unknown token while reading object; treating as string
WARNING: issue-100.pdf (object 5 0, offset 308): unexpected )
+WARNING: issue-100.pdf (object 5 0, offset 316): treating unexpected array close token as null
+WARNING: issue-100.pdf (object 5 0, offset 227): expected dictionary key but found non-name object; inserting key /QPDFFake1
+WARNING: issue-100.pdf (object 5 0, offset 321): expected endobj
WARNING: issue-100.pdf (object 5 0, offset 418): /Length key in stream dictionary is not an integer
WARNING: issue-100.pdf (object 5 0, offset 489): attempting to recover stream length
WARNING: issue-100.pdf (object 5 0, offset 489): recovered stream length: 12
diff --git a/qpdf/qtest/qpdf/issue-101.out b/qpdf/qtest/qpdf/issue-101.out
index 29ccbfb7..f1e4d03a 100644
--- a/qpdf/qtest/qpdf/issue-101.out
+++ b/qpdf/qtest/qpdf/issue-101.out
@@ -56,4 +56,72 @@ WARNING: issue-101.pdf (object 11 0, offset 811): unknown token while reading ob
WARNING: issue-101.pdf (object 11 0, offset 819): unknown token while reading object; treating as string
WARNING: issue-101.pdf (object 11 0, offset 832): unknown token while reading object; treating as string
WARNING: issue-101.pdf (object 11 0, offset 856): unexpected >
-issue-101.pdf (offset 856): unable to find /Root dictionary
+WARNING: issue-101.pdf (object 11 0, offset 857): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 868): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 887): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 897): unexpected )
+WARNING: issue-101.pdf (object 11 0, offset 898): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 909): invalid character (¤) in hexstring
+WARNING: issue-101.pdf (object 11 0, offset 911): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 929): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 930): invalid character (²) in hexstring
+WARNING: issue-101.pdf (object 11 0, offset 932): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 944): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 947): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 970): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1046): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1067): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1075): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1080): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1084): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1102): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1112): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1124): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1133): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1145): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1148): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1150): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1151): unexpected )
+WARNING: issue-101.pdf (object 11 0, offset 1153): unexpected dictionary close token
+WARNING: issue-101.pdf (object 11 0, offset 1156): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1163): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1168): unexpected >
+WARNING: issue-101.pdf (object 11 0, offset 1170): invalid character (I) in hexstring
+WARNING: issue-101.pdf (object 11 0, offset 1167): expected dictionary key but found non-name object; inserting key /QPDFFake1
+WARNING: issue-101.pdf (object 11 0, offset 1167): expected dictionary key but found non-name object; inserting key /QPDFFake2
+WARNING: issue-101.pdf (object 11 0, offset 1167): expected dictionary key but found non-name object; inserting key /QPDFFake3
+WARNING: issue-101.pdf (object 11 0, offset 1176): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1180): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1184): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1190): unexpected >
+WARNING: issue-101.pdf (object 11 0, offset 1192): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1195): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1205): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1217): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1224): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1236): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1242): expected dictionary key but found non-name object; inserting key /QPDFFake1
+WARNING: issue-101.pdf (object 11 0, offset 1242): dictionary ended prematurely; using null as value for last key
+WARNING: issue-101.pdf (object 11 0, offset 1275): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1287): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1291): unexpected dictionary close token
+WARNING: issue-101.pdf (object 11 0, offset 1294): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1306): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1322): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1325): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1329): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1341): treating unexpected array close token as null
+WARNING: issue-101.pdf (object 11 0, offset 1312): expected dictionary key but found non-name object; inserting key /QPDFFake1
+WARNING: issue-101.pdf (object 11 0, offset 1312): expected dictionary key but found non-name object; inserting key /QPDFFake2
+WARNING: issue-101.pdf (object 11 0, offset 1312): expected dictionary key but found non-name object; inserting key /QPDFFake3
+WARNING: issue-101.pdf (object 11 0, offset 1312): expected dictionary key but found non-name object; inserting key /QPDFFake4
+WARNING: issue-101.pdf (object 11 0, offset 1312): dictionary ended prematurely; using null as value for last key
+WARNING: issue-101.pdf (object 11 0, offset 1349): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1353): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1357): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1359): unknown token while reading object; treating as string
+WARNING: issue-101.pdf (object 11 0, offset 1368): unexpected )
+WARNING: issue-101.pdf (object 11 0, offset 1373): expected endobj
+WARNING: issue-101.pdf (object 8 0, offset 4067): invalid character ()) in hexstring
+WARNING: issue-101.pdf (object 8 0, offset 4069): expected endobj
+qpdf: operation succeeded with warnings; resulting file may have some problems
diff --git a/qpdf/qtest/qpdf/issue-146.out b/qpdf/qtest/qpdf/issue-146.out
index fc92ab65..79bb8118 100644
--- a/qpdf/qtest/qpdf/issue-146.out
+++ b/qpdf/qtest/qpdf/issue-146.out
@@ -2,4 +2,6 @@ WARNING: issue-146.pdf: file is damaged
WARNING: issue-146.pdf: can't find startxref
WARNING: issue-146.pdf: Attempting to reconstruct cross-reference table
WARNING: issue-146.pdf (trailer, offset 20728): unknown token while reading object; treating as string
-issue-146.pdf (trailer, offset 20732): unexpected EOF
+WARNING: issue-146.pdf (trailer, offset 20732): unexpected EOF
+WARNING: issue-146.pdf (trailer, offset 20732): parse error while reading object
+issue-146.pdf: unable to find trailer dictionary while recovering damaged file
diff --git a/qpdf/qtest/qpdf/issue-51.out b/qpdf/qtest/qpdf/issue-51.out
index 7873886b..692c0984 100644
--- a/qpdf/qtest/qpdf/issue-51.out
+++ b/qpdf/qtest/qpdf/issue-51.out
@@ -6,5 +6,6 @@ WARNING: issue-51.pdf (offset 70): loop detected resolving object 2 0
WARNING: issue-51.pdf (object 2 0, offset 26): /Length key in stream dictionary is not an integer
WARNING: issue-51.pdf (object 2 0, offset 71): attempting to recover stream length
WARNING: issue-51.pdf (object 2 0, offset 71): unable to recover stream data; treating stream as empty
-WARNING: issue-51.pdf (object 2 0, offset 977): unexpected EOF
+WARNING: issue-51.pdf (object 2 0, offset 977): expected endobj
+WARNING: issue-51.pdf (object 2 0, offset 977): EOF after endobj
qpdf: operation succeeded with warnings; resulting file may have some problems
diff --git a/qpdf/qtest/qpdf/linearization-bounds-1.out b/qpdf/qtest/qpdf/linearization-bounds-1.out
index 066dc883..d92c51c5 100644
--- a/qpdf/qtest/qpdf/linearization-bounds-1.out
+++ b/qpdf/qtest/qpdf/linearization-bounds-1.out
@@ -2,7 +2,7 @@ checking linearization-bounds-1.pdf
PDF Version: 1.3
File is not encrypted
File is linearized
-WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 12302): unexpected EOF
+WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 12302): expected endstream
WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length
WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106
linearization-bounds-1.pdf (linearization hint table, offset 1183): /S (shared object) offset is out of bounds
diff --git a/qpdf/qtest/qpdf/object-types-os.out b/qpdf/qtest/qpdf/object-types-os.out
new file mode 100644
index 00000000..26fcf369
--- /dev/null
+++ b/qpdf/qtest/qpdf/object-types-os.out
@@ -0,0 +1,41 @@
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 347: operation for string attempted on object of type dictionary: returning empty string
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: returning null for out of bounds array access
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: returning null for out of bounds array access
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: returning null
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to append item
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: ignoring attempt to erase out of bounds array item
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: ignoring attempt to erase out of bounds array item
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to erase item
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to insert item
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to replace items
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to set item
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: treating as empty
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: treating as empty
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for boolean attempted on object of type integer: returning false
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for dictionary attempted on object of type integer: treating as empty
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for dictionary attempted on object of type integer: treating as empty
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for dictionary attempted on object of type integer: returning false for a key containment request
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for dictionary attempted on object of type integer: ignoring key removal request
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for dictionary attempted on object of type integer: ignoring key removal/replacement request
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for dictionary attempted on object of type integer: ignoring key removal/replacement request
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for dictionary attempted on object of type integer: ignoring key replacement request
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for dictionary attempted on object of type integer: returning null for attempted key retrieval
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for inlineimage attempted on object of type integer: returning empty data
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 362: operation for integer attempted on object of type dictionary: returning 0
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for name attempted on object of type integer: returning dummy name
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for operator attempted on object of type integer: returning fake value
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 362: operation for real attempted on object of type dictionary: returning 0.0
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for string attempted on object of type integer: returning empty string
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for string attempted on object of type integer: returning empty string
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 362: operation for number attempted on object of type dictionary: returning 0
+One error
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 385: operation for string attempted on object of type name: returning empty string
+One error
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 362 -> dictionary key /Quack: operation for string attempted on object of type null: returning empty string
+Two errors
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: returning null for out of bounds array access
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384 -> null returned from invalid array access: operation for string attempted on object of type null: returning empty string
+One error
+WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 400: operation for string attempted on object of type name: returning empty string
+WARNING: object-types-os.pdf, object 8 0 at offset 538 -> dictionary key /Potato: operation for name attempted on object of type null: returning dummy name
+test 42 done
diff --git a/qpdf/qtest/qpdf/object-types-os.pdf b/qpdf/qtest/qpdf/object-types-os.pdf
new file mode 100644
index 00000000..652ed35b
--- /dev/null
+++ b/qpdf/qtest/qpdf/object-types-os.pdf
Binary files differ
diff --git a/qpdf/qtest/qpdf/object-types.out b/qpdf/qtest/qpdf/object-types.out
new file mode 100644
index 00000000..b707a957
--- /dev/null
+++ b/qpdf/qtest/qpdf/object-types.out
@@ -0,0 +1,41 @@
+WARNING: object-types.pdf, object 8 0 at offset 657: operation for string attempted on object of type dictionary: returning empty string
+WARNING: object-types.pdf, object 8 0 at offset 717: returning null for out of bounds array access
+WARNING: object-types.pdf, object 8 0 at offset 717: returning null for out of bounds array access
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: returning null
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to append item
+WARNING: object-types.pdf, object 8 0 at offset 717: ignoring attempt to erase out of bounds array item
+WARNING: object-types.pdf, object 8 0 at offset 717: ignoring attempt to erase out of bounds array item
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to erase item
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to insert item
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to replace items
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to set item
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: treating as empty
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: treating as empty
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for boolean attempted on object of type integer: returning false
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for dictionary attempted on object of type integer: treating as empty
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for dictionary attempted on object of type integer: treating as empty
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for dictionary attempted on object of type integer: returning false for a key containment request
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for dictionary attempted on object of type integer: ignoring key removal request
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for dictionary attempted on object of type integer: ignoring key removal/replacement request
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for dictionary attempted on object of type integer: ignoring key removal/replacement request
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for dictionary attempted on object of type integer: ignoring key replacement request
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for dictionary attempted on object of type integer: returning null for attempted key retrieval
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for inlineimage attempted on object of type integer: returning empty data
+WARNING: object-types.pdf, object 8 0 at offset 687: operation for integer attempted on object of type dictionary: returning 0
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for name attempted on object of type integer: returning dummy name
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for operator attempted on object of type integer: returning fake value
+WARNING: object-types.pdf, object 8 0 at offset 687: operation for real attempted on object of type dictionary: returning 0.0
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for string attempted on object of type integer: returning empty string
+WARNING: object-types.pdf, object 8 0 at offset 669: operation for string attempted on object of type integer: returning empty string
+WARNING: object-types.pdf, object 8 0 at offset 687: operation for number attempted on object of type dictionary: returning 0
+One error
+WARNING: object-types.pdf, object 8 0 at offset 724: operation for string attempted on object of type name: returning empty string
+One error
+WARNING: object-types.pdf, object 8 0 at offset 687 -> dictionary key /Quack: operation for string attempted on object of type null: returning empty string
+Two errors
+WARNING: object-types.pdf, object 8 0 at offset 717: returning null for out of bounds array access
+WARNING: object-types.pdf, object 8 0 at offset 717 -> null returned from invalid array access: operation for string attempted on object of type null: returning empty string
+One error
+WARNING: object-types.pdf, object 8 0 at offset 745: operation for string attempted on object of type name: returning empty string
+WARNING: object-types.pdf, object 4 0 at offset 386 -> dictionary key /Potato: operation for name attempted on object of type null: returning dummy name
+test 42 done
diff --git a/qpdf/qtest/qpdf/object-types.pdf b/qpdf/qtest/qpdf/object-types.pdf
new file mode 100644
index 00000000..a283d129
--- /dev/null
+++ b/qpdf/qtest/qpdf/object-types.pdf
@@ -0,0 +1,111 @@
+%PDF-1.3
+%¿÷¢þ
+%QDF-1.0
+
+1 0 obj
+<<
+ /Pages 2 0 R
+ /Type /Catalog
+>>
+endobj
+
+2 0 obj
+<<
+ /Count 1
+ /Kids [
+ 3 0 R
+ ]
+ /Type /Pages
+>>
+endobj
+
+%% Page 1
+3 0 obj
+<<
+ /Contents 4 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 6 0 R
+ >>
+ /ProcSet 7 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Contents for page 1
+4 0 obj
+<<
+ /Length 5 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato) Tj
+ET
+endstream
+endobj
+
+5 0 obj
+44
+endobj
+
+6 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+7 0 obj
+[
+ /PDF
+ /Text
+]
+endobj
+
+8 0 obj
+<<
+ /Integer 5
+ /Dictionary <<
+ /Key1 /Value1
+ /Key2 [
+ /Item0
+ << /K [ /V ] >>
+ /Item2
+ ]
+ >>
+>>
+endobj
+
+xref
+0 9
+0000000000 65535 f
+0000000025 00000 n
+0000000079 00000 n
+0000000161 00000 n
+0000000376 00000 n
+0000000475 00000 n
+0000000494 00000 n
+0000000612 00000 n
+0000000647 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 9
+ /ID [<5ecb4bcc69402d31e10c2e63ec8500ee><5ecb4bcc69402d31e10c2e63ec8500ee>]
+ /QTest 8 0 R
+>>
+startxref
+788
+%%EOF
diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc
index c03e0250..3ec20dd8 100644
--- a/qpdf/test_driver.cc
+++ b/qpdf/test_driver.cc
@@ -1390,6 +1390,66 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true);
w.write();
}
+ else if (n == 42)
+ {
+ // Access objects as wrong type. This test case is crafted to
+ // work with object-types.pdf.
+ QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest");
+ QPDFObjectHandle array = qtest.getKey("/Dictionary").getKey("/Key2");
+ QPDFObjectHandle dictionary = qtest.getKey("/Dictionary");
+ QPDFObjectHandle integer = qtest.getKey("/Integer");
+ QPDFObjectHandle null = QPDFObjectHandle::newNull();
+ assert(array.isArray());
+ assert(dictionary.isDictionary());
+ assert("" == qtest.getStringValue());
+ array.getArrayItem(-1).assertNull();
+ array.getArrayItem(16059).assertNull();
+ integer.getArrayItem(0).assertNull();
+ integer.appendItem(null);
+ array.eraseItem(-1);
+ array.eraseItem(16059);
+ integer.eraseItem(0);
+ integer.insertItem(0, null);
+ integer.setArrayFromVector(std::vector<QPDFObjectHandle>());
+ integer.setArrayItem(0, null);
+ assert(0 == integer.getArrayNItems());
+ assert(integer.getArrayAsVector().empty());
+ assert(false == integer.getBoolValue());
+ assert(integer.getDictAsMap().empty());
+ assert(integer.getKeys().empty());
+ assert(false == integer.hasKey("/Potato"));
+ integer.removeKey("/Potato");
+ integer.replaceOrRemoveKey("/Potato", null);
+ integer.replaceOrRemoveKey("/Potato", QPDFObjectHandle::newInteger(1));
+ integer.replaceKey("/Potato", QPDFObjectHandle::newInteger(1));
+ qtest.getKey("/Integer").getKey("/Potato");
+ assert(integer.getInlineImageValue().empty());
+ assert(0 == dictionary.getIntValue());
+ assert("/QPDFFakeName" == integer.getName());
+ assert("QPDFFAKE" == integer.getOperatorValue());
+ assert("0.0" == dictionary.getRealValue());
+ assert(integer.getStringValue().empty());
+ assert(integer.getUTF8Value().empty());
+ assert(0.0 == dictionary.getNumericValue());
+ // Make sure error messages are okay for nested values
+ std::cerr << "One error\n";
+ assert(array.getArrayItem(0).getStringValue().empty());
+ std::cerr << "One error\n";
+ assert(dictionary.getKey("/Quack").getStringValue().empty());
+ assert(array.getArrayItem(1).isDictionary());
+ assert(array.getArrayItem(1).getKey("/K").isArray());
+ assert(array.getArrayItem(1).getKey("/K").getArrayItem(0).isName());
+ assert("/V" ==
+ array.getArrayItem(1).getKey("/K").getArrayItem(0).getName());
+ std::cerr << "Two errors\n";
+ assert(array.getArrayItem(16059).getStringValue().empty());
+ std::cerr << "One error\n";
+ array.getArrayItem(1).getKey("/K").getArrayItem(0).getStringValue();
+ // Stream dictionary
+ QPDFObjectHandle page = pdf.getAllPages()[0];
+ assert("/QPDFFakeName" ==
+ page.getKey("/Contents").getDict().getKey("/Potato").getName());
+ }
else
{
throw std::runtime_error(std::string("invalid test ") +