More robust handling of type errors

Give objects descriptions and context so it is possible to issue
warnings instead of fatal errors for attempts to access objects of the
wrong type.
This commit is contained in:
Jay Berkenbilt 2018-02-16 17:25:27 -05:00
parent c2e16827b6
commit d0e99f195a
42 changed files with 1133 additions and 164 deletions

View File

@ -1,5 +1,15 @@
2018-02-17 Jay Berkenbilt <ejb@ql.org> 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 message fix: replace "file position" with "offset" in
error messages that report lexical or parsing errors. Sometimes error messages that report lexical or parsing errors. Sometimes
it's an offset in an object stream or a content stream rather than it's an offset in an object stream or a content stream rather than

9
TODO
View File

@ -1,15 +1,6 @@
Soon 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 * Add method to push inheritable resources to a single page by
walking up and copying without overwrite. Above logic will also be walking up and copying without overwrite. Above logic will also be
sufficient to fix the limitation in sufficient to fix the limitation in

View File

@ -703,7 +703,6 @@ class QPDF
PointerHolder<InputSource> input, int objid, int generation, PointerHolder<InputSource> input, int objid, int generation,
qpdf_offset_t stream_offset); qpdf_offset_t stream_offset);
QPDFTokenizer::Token readToken(PointerHolder<InputSource>, QPDFTokenizer::Token readToken(PointerHolder<InputSource>,
bool allow_bad = false,
size_t max_len = 0); size_t max_len = 0);
QPDFObjectHandle readObjectAtOffset( QPDFObjectHandle readObjectAtOffset(

View File

@ -23,6 +23,7 @@
#define __QPDFOBJECT_HH__ #define __QPDFOBJECT_HH__
#include <qpdf/DLL.h> #include <qpdf/DLL.h>
#include <qpdf/PointerHolder.hh>
#include <string> #include <string>
@ -32,6 +33,7 @@ class QPDFObjectHandle;
class QPDFObject class QPDFObject
{ {
public: public:
QPDFObject();
// Objects derived from QPDFObject are accessible through // Objects derived from QPDFObject are accessible through
// QPDFObjectHandle. Each object returns a unique type code that // QPDFObjectHandle. Each object returns a unique type code that
@ -84,8 +86,27 @@ class QPDFObject
}; };
friend class ObjAccessor; friend class ObjAccessor;
virtual void setDescription(QPDF*, std::string const&);
bool getDescription(QPDF*&, std::string&);
bool hasDescription();
protected: protected:
virtual void releaseResolved() {} 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__ #endif // __QPDFOBJECT_HH__

View File

@ -398,6 +398,21 @@ class QPDFObjectHandle
QPDF_DLL QPDF_DLL
static QPDFObjectHandle newReserved(QPDF* qpdf); 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 // Accessor methods. If an accessor method that is valid for only
// a particular object type is called on an object of the wrong // a particular object type is called on an object of the wrong
// type, an exception is thrown. // type, an exception is thrown.
@ -498,7 +513,7 @@ class QPDFObjectHandle
// Replace value of key, adding it if it does not exist // Replace value of key, adding it if it does not exist
QPDF_DLL 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 // Remove key, doing nothing if key does not exist
QPDF_DLL QPDF_DLL
void removeKey(std::string const& key); void removeKey(std::string const& key);
@ -769,7 +784,10 @@ class QPDFObjectHandle
}; };
friend class ReleaseResolver; 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 QPDF_DLL
void assertInitialized() const; void assertInitialized() const;
@ -832,10 +850,16 @@ class QPDFObjectHandle
QPDF* qpdf, int objid, int generation, QPDF* qpdf, int objid, int generation,
QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length); 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 dereference();
void makeDirectInternal(std::set<int>& visited); void makeDirectInternal(std::set<int>& visited);
void releaseResolved(); void releaseResolved();
static void setObjectDescriptionFromInput(
QPDFObjectHandle, QPDF*, std::string const&,
PointerHolder<InputSource>, qpdf_offset_t);
static QPDFObjectHandle parseInternal( static QPDFObjectHandle parseInternal(
PointerHolder<InputSource> input, PointerHolder<InputSource> input,
std::string const& object_description, std::string const& object_description,
@ -868,7 +892,7 @@ class QPDFObjectHandle
bool initialized; bool initialized;
QPDF* qpdf; // 0 for direct object QPDF* qpdf;
int objid; // 0 for direct object int objid; // 0 for direct object
int generation; int generation;
PointerHolder<QPDFObject> obj; PointerHolder<QPDFObject> obj;

View File

@ -23,9 +23,11 @@ activatePipelineStack
ActiveState ActiveState
acyclic acyclic
adbe adbe
addContentTokenFilter
addPage addPage
addPageAt addPageAt
addPageContents addPageContents
addTokenFilter
addToTable addToTable
adjustAESStreamLength adjustAESStreamLength
admon admon
@ -54,6 +56,7 @@ allowPoundAnywhereInName
allowPrintHighRes allowPrintHighRes
allowPrintLowRes allowPrintLowRes
antivirus antivirus
anyBadTokens
aobjid aobjid
apexcovantage apexcovantage
api api
@ -71,11 +74,13 @@ argv
arko arko
arko's arko's
Arora Arora
arrayOrStreamToStreamArray
ArtBox ArtBox
ascii ascii
asciiHex asciiHex
ASCIIHexDecode ASCIIHexDecode
ASCIIHexDecoder ASCIIHexDecoder
asMap
assertArray assertArray
assertBool assertBool
assertDictionary assertDictionary
@ -214,12 +219,15 @@ closeObject
cmath cmath
cmd cmd
cmyk cmyk
coalesceContentStreams
CoalesceProvider
codepage codepage
codepoint codepoint
col col
Coldwind Coldwind
ColorSpace ColorSpace
colorspace colorspace
ColorToGray
com com
compareVersions compareVersions
compatbility compatbility
@ -230,6 +238,7 @@ Cond
config config
conftest conftest
const const
ContentNormalizer
contrib contrib
CopiedStreamDataProvider CopiedStreamDataProvider
copyEncryptionParameters copyEncryptionParameters
@ -300,6 +309,7 @@ deflateEnd
deflateInit deflateInit
defq defq
delphi delphi
deque
dereference dereference
dereferenced dereferenced
dest dest
@ -411,6 +421,7 @@ esize
exc exc
exe exe
exp exp
expectInlineImage
ExtensionLevel ExtensionLevel
extern extern
fb fb
@ -439,6 +450,7 @@ Filespec
FILETIME FILETIME
filetrailer filetrailer
filterCompressedObjects filterCompressedObjects
filterPageContents
findAndSkipNextEOL findAndSkipNextEOL
findAttachmentStreams findAttachmentStreams
findEndstream findEndstream
@ -509,6 +521,7 @@ getCompressibleObjects
getCompressibleObjGens getCompressibleObjGens
getCount getCount
getDataChecksum getDataChecksum
getDescription
getDict getDict
getDictAsMap getDictAsMap
getEncryptionKey getEncryptionKey
@ -562,6 +575,7 @@ getOE
getOffset getOffset
getOffsetLength getOffsetLength
getOperatorValue getOperatorValue
getOriginalID
getOwningQPDF getOwningQPDF
getP getP
getPaddedUserPassword getPaddedUserPassword
@ -626,7 +640,10 @@ handleCode
handleData handleData
handleEOF handleEOF
handleObject handleObject
handleToken
hasDescription
hasKey hasKey
hasObjectDescription
hb hb
hbp hbp
HCRYPTPROV HCRYPTPROV
@ -668,6 +685,7 @@ ifeq
iff iff
ifndef ifndef
ifstream ifstream
ignorable
ijg ijg
Im Im
ImageC ImageC
@ -676,6 +694,7 @@ ImageInverter
ImageMask ImageMask
ImageProvider ImageProvider
inbuf inbuf
includeIgnorable
INDOC INDOC
indx indx
inf inf
@ -720,9 +739,12 @@ iostream
irdp irdp
isArray isArray
isBool isBool
isDataModified
isDelimiter
isDictionary isDictionary
isdigit isdigit
isEncrypted isEncrypted
isIgnorable
isIndirect isIndirect
isInitialized isInitialized
isInlineImage isInlineImage
@ -731,6 +753,7 @@ isLinearized
isName isName
isNull isNull
isNumber isNumber
isNumeric
iso iso
isOperator isOperator
isOrHasName isOrHasName
@ -741,10 +764,12 @@ isReal
isReserved isReserved
isScalar isScalar
isspace isspace
isSpace
isStream isStream
isString isString
istream istream
istype istype
isType
italicseq italicseq
itemizedlist itemizedlist
iter iter
@ -772,6 +797,7 @@ keyset
LARGEFILE LARGEFILE
lastnum lastnum
lastreleased lastreleased
lastTokenWasBad
latin latin
lbuf lbuf
lc lc
@ -839,6 +865,7 @@ malloc
manualFinish manualFinish
Mateusz Mateusz
maxEnd maxEnd
maxlen
maxval maxval
md md
mdash mdash
@ -933,6 +960,7 @@ NTE
ntotal ntotal
NUL NUL
num num
numericValue
numrange numrange
nval nval
nwalsh nwalsh
@ -1007,6 +1035,7 @@ parms
parsecontent parsecontent
parseContentStream parseContentStream
parseInternal parseInternal
parsePageContents
ParserCallbacks ParserCallbacks
parseVersion parseVersion
partLen partLen
@ -1033,6 +1062,9 @@ persistAcrossFinish
ph ph
phe phe
php php
pipeContentStreams
PipelineAccessor
pipePageContents
pipeStreamData pipeStreamData
pipeStringAndFinish pipeStringAndFinish
Pkey Pkey
@ -1072,6 +1104,7 @@ procset
ProcSet ProcSet
procsets procsets
programlisting programlisting
programmatically
Projet Projet
prov prov
provideRandomData provideRandomData
@ -1106,6 +1139,7 @@ QPDF's
QPDFCONSTANTS QPDFCONSTANTS
QPDFExc QPDFExc
QPDFFake QPDFFake
QPDFFakeName
QPDFObject QPDFObject
QPDFObjectHandle QPDFObjectHandle
QPDFObjectHandle's QPDFObjectHandle's
@ -1170,6 +1204,7 @@ ReleaseResolver
remotesensing remotesensing
removeKey removeKey
removePage removePage
removereplace
repl repl
replaceDict replaceDict
replaceFilterData replaceFilterData
@ -1191,6 +1226,7 @@ retested
reverseResolved reverseResolved
rf rf
rfont rfont
rg
rgb rgb
rhs rhs
rijndael rijndael
@ -1240,7 +1276,9 @@ setCompressStreams
setContentNormalization setContentNormalization
setDataKey setDataKey
setDecodeLevel setDecodeLevel
setDescription
setDeterministicID setDeterministicID
setDictDescription
setEncryptionParameters setEncryptionParameters
setEncryptionParametersInternal setEncryptionParametersInternal
setExtraHeaderText setExtraHeaderText
@ -1254,11 +1292,14 @@ setjmp
setLastObjectDescription setLastObjectDescription
setLastOffset setLastOffset
setLinearization setLinearization
setLinearizationPass
setLineBuf setLineBuf
setMinimumPDFVersion setMinimumPDFVersion
setmode setmode
setNewlineBeforeEndstream setNewlineBeforeEndstream
setO setO
setObjectDescription
setObjectDescriptionFromInput
setObjectStreamMode setObjectStreamMode
setObjGen setObjGen
setOutputFile setOutputFile
@ -1268,6 +1309,7 @@ setOutputPipeline
setOutputStreams setOutputStreams
setPasswordIsHexKey setPasswordIsHexKey
setPCLm setPCLm
setPipeline
setprecision setprecision
setPreserveEncryption setPreserveEncryption
setPreserveUnreferencedObjects setPreserveUnreferencedObjects
@ -1277,6 +1319,7 @@ setRandomDataProvider
setStaticAesIV setStaticAesIV
setStaticID setStaticID
setStreamDataMode setStreamDataMode
setStreamDescription
setSuppressOriginalObjectIDs setSuppressOriginalObjectIDs
setSuppressWarnings setSuppressWarnings
setTrailer setTrailer
@ -1329,8 +1372,10 @@ StreamDataProvider
strerror strerror
StrF StrF
stricmp stricmp
StringCounter
StringDecrypter StringDecrypter
stringprep stringprep
StringReverser
stripesize stripesize
strlen strlen
strncmp strncmp
@ -1385,10 +1430,13 @@ tobj
tobjid tobjid
TODO TODO
toffset toffset
TokenFilter
TokenFilters
tokenize tokenize
tokenized tokenized
tokenizer tokenizer
tokenizing tokenizing
tokenTypeName
toolchain toolchain
Toolchains Toolchains
toupper toupper
@ -1400,6 +1448,7 @@ trimTrailerForWrite
tt tt
turbo turbo
txt txt
typeWarning
uc uc
udata udata
UE UE
@ -1428,6 +1477,7 @@ unparse
unparseChild unparseChild
unparseObject unparseObject
unparseResolved unparseResolved
unparsing
unreadCh unreadCh
unreferenced unreferenced
unresolvable unresolvable

View File

@ -106,6 +106,7 @@ QPDF::Members::~Members()
QPDF::QPDF() : QPDF::QPDF() :
m(new Members()) m(new Members())
{ {
m->tokenizer.allowEOF();
} }
QPDF::~QPDF() QPDF::~QPDF()
@ -272,10 +273,10 @@ QPDF::findHeader()
bool bool
QPDF::findStartxref() 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")) 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) if (t.getType() == QPDFTokenizer::tt_integer)
{ {
// Position in front of offset token // Position in front of offset token
@ -421,7 +422,7 @@ QPDF::reconstruct_xref(QPDFExc& e)
this->m->file->findAndSkipNextEOL(); this->m->file->findAndSkipNextEOL();
qpdf_offset_t next_line_start = this->m->file->tell(); qpdf_offset_t next_line_start = this->m->file->tell();
this->m->file->seek(line_start, SEEK_SET); 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 = qpdf_offset_t token_start =
this->m->file->tell() - t1.getValue().length(); this->m->file->tell() - t1.getValue().length();
if (token_start >= next_line_start) if (token_start >= next_line_start)
@ -440,9 +441,9 @@ QPDF::reconstruct_xref(QPDFExc& e)
if (t1.getType() == QPDFTokenizer::tt_integer) if (t1.getType() == QPDFTokenizer::tt_integer)
{ {
QPDFTokenizer::Token t2 = QPDFTokenizer::Token t2 =
readToken(this->m->file, true, MAX_LEN); readToken(this->m->file, MAX_LEN);
QPDFTokenizer::Token t3 = QPDFTokenizer::Token t3 =
readToken(this->m->file, true, MAX_LEN); readToken(this->m->file, MAX_LEN);
if ((t2.getType() == QPDFTokenizer::tt_integer) && if ((t2.getType() == QPDFTokenizer::tt_integer) &&
(t3 == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "obj"))) (t3 == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "obj")))
{ {
@ -1429,7 +1430,7 @@ bool
QPDF::findEndstream() QPDF::findEndstream()
{ {
// Find endstream or endobj. Position the input at that token. // 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) && if ((t.getType() == QPDFTokenizer::tt_word) &&
((t.getValue() == "endobj") || ((t.getValue() == "endobj") ||
(t.getValue() == "endstream"))) (t.getValue() == "endstream")))
@ -1522,11 +1523,10 @@ QPDF::recoverStreamLength(PointerHolder<InputSource> input,
} }
QPDFTokenizer::Token QPDFTokenizer::Token
QPDF::readToken(PointerHolder<InputSource> input, QPDF::readToken(PointerHolder<InputSource> input, size_t max_len)
bool allow_bad, size_t max_len)
{ {
return this->m->tokenizer.readToken( 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 QPDFObjectHandle
@ -1730,16 +1730,10 @@ QPDF::resolve(int objid, int generation)
} }
ResolveRecorder rr(this, og); 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]; QPDFXRefEntry const& entry = this->m->xref_table[og];
bool success = false;
try try
{ {
switch (entry.getType()) switch (entry.getType())
@ -1768,7 +1762,6 @@ QPDF::resolve(int objid, int generation)
QUtil::int_to_string(generation) + QUtil::int_to_string(generation) +
" has unexpected xref entry type"); " has unexpected xref entry type");
} }
success = true;
} }
catch (QPDFExc& e) catch (QPDFExc& e)
{ {
@ -1782,16 +1775,24 @@ QPDF::resolve(int objid, int generation)
QUtil::int_to_string(generation) + QUtil::int_to_string(generation) +
": error reading object: " + e.what())); ": error reading object: " + e.what()));
} }
if (! success) }
{ if (this->m->obj_cache.count(og) == 0)
QTC::TC("qpdf", "QPDF resolve failure to null"); {
QPDFObjectHandle oh = QPDFObjectHandle::newNull(); QTC::TC("qpdf", "QPDF resolve failure to null");
this->m->obj_cache[og] = QPDFObjectHandle oh = QPDFObjectHandle::newNull();
ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1); 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 void

View File

@ -32,7 +32,10 @@ QPDFExc::createWhat(std::string const& filename,
} }
if (! (object.empty() && offset == 0)) if (! (object.empty() && offset == 0))
{ {
result += " ("; if (! filename.empty())
{
result += " (";
}
if (! object.empty()) if (! object.empty())
{ {
result += object; result += object;
@ -45,7 +48,10 @@ QPDFExc::createWhat(std::string const& filename,
{ {
result += "offset " + QUtil::int_to_string(offset); result += "offset " + QUtil::int_to_string(offset);
} }
result += ")"; if (! filename.empty())
{
result += ")";
}
} }
if (! result.empty()) if (! result.empty())
{ {

View File

@ -1 +1,36 @@
#include <qpdf/QPDFObject.hh> #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;
}

View File

@ -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 bool
QPDFObjectHandle::isInitialized() const QPDFObjectHandle::isInitialized() const
{ {
@ -282,7 +294,8 @@ QPDFObjectHandle::getNumericValue()
} }
else else
{ {
throw std::logic_error("getNumericValue called for non-numeric object"); typeWarning("number", "returning 0");
QTC::TC("qpdf", "QPDFObjectHandle numeric non-numeric");
} }
return result; return result;
} }
@ -363,8 +376,16 @@ QPDFObjectHandle::isScalar()
bool bool
QPDFObjectHandle::getBoolValue() QPDFObjectHandle::getBoolValue()
{ {
assertBool(); if (isBool())
return dynamic_cast<QPDF_Bool*>(m->obj.getPointer())->getVal(); {
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 // Integer accessors
@ -372,8 +393,16 @@ QPDFObjectHandle::getBoolValue()
long long long long
QPDFObjectHandle::getIntValue() QPDFObjectHandle::getIntValue()
{ {
assertInteger(); if (isInteger())
return dynamic_cast<QPDF_Integer*>(m->obj.getPointer())->getVal(); {
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 // Real accessors
@ -381,8 +410,16 @@ QPDFObjectHandle::getIntValue()
std::string std::string
QPDFObjectHandle::getRealValue() QPDFObjectHandle::getRealValue()
{ {
assertReal(); if (isReal())
return dynamic_cast<QPDF_Real*>(m->obj.getPointer())->getVal(); {
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 // Name accessors
@ -390,8 +427,16 @@ QPDFObjectHandle::getRealValue()
std::string std::string
QPDFObjectHandle::getName() QPDFObjectHandle::getName()
{ {
assertName(); if (isName())
return dynamic_cast<QPDF_Name*>(m->obj.getPointer())->getName(); {
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 // String accessors
@ -399,15 +444,31 @@ QPDFObjectHandle::getName()
std::string std::string
QPDFObjectHandle::getStringValue() QPDFObjectHandle::getStringValue()
{ {
assertString(); if (isString())
return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getVal(); {
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 std::string
QPDFObjectHandle::getUTF8Value() QPDFObjectHandle::getUTF8Value()
{ {
assertString(); if (isString())
return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getUTF8Val(); {
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 // Operator and Inline Image accessors
@ -415,15 +476,31 @@ QPDFObjectHandle::getUTF8Value()
std::string std::string
QPDFObjectHandle::getOperatorValue() QPDFObjectHandle::getOperatorValue()
{ {
assertOperator(); if (isOperator())
return dynamic_cast<QPDF_Operator*>(m->obj.getPointer())->getVal(); {
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 std::string
QPDFObjectHandle::getInlineImageValue() QPDFObjectHandle::getInlineImageValue()
{ {
assertInlineImage(); if (isInlineImage())
return dynamic_cast<QPDF_InlineImage*>(m->obj.getPointer())->getVal(); {
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 // Array accessors
@ -431,22 +508,66 @@ QPDFObjectHandle::getInlineImageValue()
int int
QPDFObjectHandle::getArrayNItems() QPDFObjectHandle::getArrayNItems()
{ {
assertArray(); if (isArray())
return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getNItems(); {
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
QPDFObjectHandle::getArrayItem(int n) QPDFObjectHandle::getArrayItem(int n)
{ {
assertArray(); QPDFObjectHandle result;
return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getItem(n); 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> std::vector<QPDFObjectHandle>
QPDFObjectHandle::getArrayAsVector() QPDFObjectHandle::getArrayAsVector()
{ {
assertArray(); std::vector<QPDFObjectHandle> result;
return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getAsVector(); 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 // Array mutators
@ -454,36 +575,79 @@ QPDFObjectHandle::getArrayAsVector()
void void
QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item) QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item)
{ {
assertArray(); if (isArray())
return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setItem(n, item); {
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 void
QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items)
{ {
assertArray(); if (isArray())
return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setFromVector(items); {
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 void
QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item) QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item)
{ {
assertArray(); if (isArray())
return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->insertItem(at, item); {
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 void
QPDFObjectHandle::appendItem(QPDFObjectHandle const& item) QPDFObjectHandle::appendItem(QPDFObjectHandle const& item)
{ {
assertArray(); if (isArray())
return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->appendItem(item); {
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 void
QPDFObjectHandle::eraseItem(int at) QPDFObjectHandle::eraseItem(int at)
{ {
assertArray(); if (isArray() && (at < getArrayNItems()) && (at >= 0))
return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->eraseItem(at); {
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 // Dictionary accessors
@ -491,29 +655,79 @@ QPDFObjectHandle::eraseItem(int at)
bool bool
QPDFObjectHandle::hasKey(std::string const& key) QPDFObjectHandle::hasKey(std::string const& key)
{ {
assertDictionary(); if (isDictionary())
return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->hasKey(key); {
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
QPDFObjectHandle::getKey(std::string const& key) QPDFObjectHandle::getKey(std::string const& key)
{ {
assertDictionary(); QPDFObjectHandle result;
return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getKey(key); 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> std::set<std::string>
QPDFObjectHandle::getKeys() QPDFObjectHandle::getKeys()
{ {
assertDictionary(); std::set<std::string> result;
return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getKeys(); 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> std::map<std::string, QPDFObjectHandle>
QPDFObjectHandle::getDictAsMap() QPDFObjectHandle::getDictAsMap()
{ {
assertDictionary(); std::map<std::string, QPDFObjectHandle> result;
return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getAsMap(); 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 // Array and Name accessors
@ -551,27 +765,48 @@ QPDFObjectHandle::getOwningQPDF()
void void
QPDFObjectHandle::replaceKey(std::string const& key, QPDFObjectHandle::replaceKey(std::string const& key,
QPDFObjectHandle const& value) QPDFObjectHandle value)
{ {
assertDictionary(); if (isDictionary())
return dynamic_cast<QPDF_Dictionary*>( {
m->obj.getPointer())->replaceKey(key, value); 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 void
QPDFObjectHandle::removeKey(std::string const& key) QPDFObjectHandle::removeKey(std::string const& key)
{ {
assertDictionary(); if (isDictionary())
return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->removeKey(key); {
dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->removeKey(key);
}
else
{
typeWarning("dictionary", "ignoring key removal request");
QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey");
}
} }
void void
QPDFObjectHandle::replaceOrRemoveKey(std::string const& key, QPDFObjectHandle::replaceOrRemoveKey(std::string const& key,
QPDFObjectHandle value) QPDFObjectHandle value)
{ {
assertDictionary(); if (isDictionary())
return dynamic_cast<QPDF_Dictionary*>( {
m->obj.getPointer())->replaceOrRemoveKey(key, value); 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 // Stream accessors
@ -1173,35 +1408,45 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
std::vector<parser_state_e> state_stack; std::vector<parser_state_e> state_stack;
state_stack.push_back(st_top); state_stack.push_back(st_top);
std::vector<qpdf_offset_t> offset_stack; 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; bool done = false;
while (! done) while (! done)
{ {
std::vector<QPDFObjectHandle>& olist = olist_stack.back(); std::vector<QPDFObjectHandle>& olist = olist_stack.back();
parser_state_e state = state_stack.back(); parser_state_e state = state_stack.back();
qpdf_offset_t offset = offset_stack.back(); offset = offset_stack.back();
object = QPDFObjectHandle(); object = QPDFObjectHandle();
QPDFTokenizer::Token token = QPDFTokenizer::Token token =
tokenizer.readToken(input, object_description); tokenizer.readToken(input, object_description, true);
switch (token.getType()) switch (token.getType())
{ {
case QPDFTokenizer::tt_eof: case QPDFTokenizer::tt_eof:
if (content_stream) if (! content_stream)
{ {
state = st_eof; QTC::TC("qpdf", "QPDFObjectHandle eof in parseInternal");
} warn(context,
else QPDFExc(qpdf_e_damaged_pdf, input->getName(),
{ object_description,
// When not in content stream mode, EOF is tt_bad and input->getLastOffset(),
// throws an exception before we get here. "unexpected EOF"));
throw std::logic_error(
"EOF received while not in content stream mode");
} }
state = st_eof;
break; 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_open:
case QPDFTokenizer::tt_brace_close: case QPDFTokenizer::tt_brace_close:
QTC::TC("qpdf", "QPDFObjectHandle bad brace"); QTC::TC("qpdf", "QPDFObjectHandle bad brace");
@ -1375,11 +1620,19 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
"parse error while reading object")); "parse error while reading object"));
} }
done = true; done = true;
// Leave object uninitialized to indicate EOF // In content stream mode, leave object uninitialized to
// indicate EOF
if (! content_stream)
{
object = newNull();
}
break; break;
case st_dictionary: case st_dictionary:
case st_array: case st_array:
setObjectDescriptionFromInput(
object, context, object_description, input,
input->getLastOffset());
olist.push_back(object); olist.push_back(object);
break; break;
@ -1402,6 +1655,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
if (old_state == st_array) if (old_state == st_array)
{ {
object = newArray(olist); object = newArray(olist);
setObjectDescriptionFromInput(
object, context, object_description, input, offset);
} }
else if (old_state == st_dictionary) else if (old_state == st_dictionary)
{ {
@ -1458,6 +1713,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
"dictionary ended prematurely; " "dictionary ended prematurely; "
"using null as value for last key")); "using null as value for last key"));
val = newNull(); val = newNull();
setObjectDescriptionFromInput(
val, context, object_description, input, offset);
} }
else else
{ {
@ -1466,6 +1723,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input,
dict[key_obj.getName()] = val; dict[key_obj.getName()] = val;
} }
object = newDictionary(dict); object = newDictionary(dict);
setObjectDescriptionFromInput(
object, context, object_description, input, offset);
} }
olist_stack.pop_back(); olist_stack.pop_back();
offset_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; return object;
} }
@ -1635,6 +1896,26 @@ QPDFObjectHandle::newReserved(QPDF* qpdf)
return result; 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
QPDFObjectHandle::shallowCopy() QPDFObjectHandle::shallowCopy()
{ {
@ -1793,85 +2074,127 @@ QPDFObjectHandle::assertInitialized() const
} }
void 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) if (! istype)
{ {
throw std::logic_error(std::string("operation for ") + type_name + throw std::logic_error(std::string("operation for ") + type_name +
" object attempted on object of wrong type"); " attempted on object of type " +
getTypeName());
} }
} }
void void
QPDFObjectHandle::assertNull() QPDFObjectHandle::assertNull()
{ {
assertType("Null", isNull()); assertType("null", isNull());
} }
void void
QPDFObjectHandle::assertBool() QPDFObjectHandle::assertBool()
{ {
assertType("Boolean", isBool()); assertType("boolean", isBool());
} }
void void
QPDFObjectHandle::assertInteger() QPDFObjectHandle::assertInteger()
{ {
assertType("Integer", isInteger()); assertType("integer", isInteger());
} }
void void
QPDFObjectHandle::assertReal() QPDFObjectHandle::assertReal()
{ {
assertType("Real", isReal()); assertType("real", isReal());
} }
void void
QPDFObjectHandle::assertName() QPDFObjectHandle::assertName()
{ {
assertType("Name", isName()); assertType("name", isName());
} }
void void
QPDFObjectHandle::assertString() QPDFObjectHandle::assertString()
{ {
assertType("String", isString()); assertType("string", isString());
} }
void void
QPDFObjectHandle::assertOperator() QPDFObjectHandle::assertOperator()
{ {
assertType("Operator", isOperator()); assertType("operator", isOperator());
} }
void void
QPDFObjectHandle::assertInlineImage() QPDFObjectHandle::assertInlineImage()
{ {
assertType("InlineImage", isInlineImage()); assertType("inlineimage", isInlineImage());
} }
void void
QPDFObjectHandle::assertArray() QPDFObjectHandle::assertArray()
{ {
assertType("Array", isArray()); assertType("array", isArray());
} }
void void
QPDFObjectHandle::assertDictionary() QPDFObjectHandle::assertDictionary()
{ {
assertType("Dictionary", isDictionary()); assertType("dictionary", isDictionary());
} }
void void
QPDFObjectHandle::assertStream() QPDFObjectHandle::assertStream()
{ {
assertType("Stream", isStream()); assertType("stream", isStream());
} }
void void
QPDFObjectHandle::assertReserved() QPDFObjectHandle::assertReserved()
{ {
assertType("Reserved", isReserved()); assertType("reserved", isReserved());
} }
void void
@ -1887,13 +2210,13 @@ QPDFObjectHandle::assertIndirect()
void void
QPDFObjectHandle::assertScalar() QPDFObjectHandle::assertScalar()
{ {
assertType("Scalar", isScalar()); assertType("scalar", isScalar());
} }
void void
QPDFObjectHandle::assertNumber() QPDFObjectHandle::assertNumber()
{ {
assertType("Number", isNumber()); assertType("number", isNumber());
} }
bool bool
@ -1928,7 +2251,8 @@ QPDFObjectHandle::dereference()
this->m->qpdf, this->m->objid, this->m->generation); this->m->qpdf, this->m->objid, this->m->generation);
if (obj.getPointer() == 0) 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(); this->m->obj = new QPDF_Null();
} }
else if (dynamic_cast<QPDF_Reserved*>(obj.getPointer())) else if (dynamic_cast<QPDF_Reserved*>(obj.getPointer()))

View File

@ -640,7 +640,9 @@ QPDFTokenizer::readToken(PointerHolder<InputSource> input,
presented_eof = true; presented_eof = true;
if ((this->m->type == tt_eof) && (! this->m->allow_eof)) 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->type = tt_bad;
this->m->error_message = "unexpected EOF"; this->m->error_message = "unexpected EOF";
offset = input->getLastOffset(); offset = input->getLastOffset();
@ -677,7 +679,10 @@ QPDFTokenizer::readToken(PointerHolder<InputSource> input,
input->unreadCh(char_to_unread); input->unreadCh(char_to_unread);
} }
input->setLastOffset(offset); if (token.getType() != tt_eof)
{
input->setLastOffset(offset);
}
if (token.getType() == tt_bad) if (token.getType() == tt_bad)
{ {

View File

@ -1,4 +1,5 @@
#include <qpdf/QPDF_Array.hh> #include <qpdf/QPDF_Array.hh>
#include <qpdf/QUtil.hh>
#include <stdexcept> #include <stdexcept>
QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& items) : QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& items) :
@ -46,6 +47,12 @@ QPDF_Array::getTypeName() const
return "array"; return "array";
} }
void
QPDF_Array::setDescription(QPDF* qpdf, std::string const& description)
{
this->QPDFObject::setDescription(qpdf, description);
}
int int
QPDF_Array::getNItems() const QPDF_Array::getNItems() const
{ {

View File

@ -51,6 +51,12 @@ QPDF_Dictionary::getTypeName() const
return "dictionary"; return "dictionary";
} }
void
QPDF_Dictionary::setDescription(QPDF* qpdf, std::string const& description)
{
this->QPDFObject::setDescription(qpdf, description);
}
bool bool
QPDF_Dictionary::hasKey(std::string const& key) QPDF_Dictionary::hasKey(std::string const& key)
{ {
@ -70,7 +76,15 @@ QPDF_Dictionary::getKey(std::string const& key)
} }
else 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& std::map<std::string, QPDFObjectHandle> const&
QPDF_Dictionary::getAsMap() const QPDF_Dictionary::getAsMap() const
{ {
return this->items; return this->items;
} }
void void
QPDF_Dictionary::replaceKey(std::string const& key, QPDF_Dictionary::replaceKey(std::string const& key,
QPDFObjectHandle const& value) QPDFObjectHandle value)
{ {
// add or replace value // add or replace value
this->items[key] = value; this->items[key] = value;

View File

@ -39,6 +39,7 @@ QPDF_Stream::QPDF_Stream(QPDF* qpdf, int objid, int generation,
"stream object instantiated with non-dictionary " "stream object instantiated with non-dictionary "
"object for dictionary"); "object for dictionary");
} }
setStreamDescription();
} }
QPDF_Stream::~QPDF_Stream() QPDF_Stream::~QPDF_Stream()
@ -85,6 +86,35 @@ QPDF_Stream::getTypeName() const
return "stream"; 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 QPDFObjectHandle
QPDF_Stream::getDict() const QPDF_Stream::getDict() const
{ {
@ -688,6 +718,7 @@ void
QPDF_Stream::replaceDict(QPDFObjectHandle new_dict) QPDF_Stream::replaceDict(QPDFObjectHandle new_dict)
{ {
this->stream_dict = new_dict; this->stream_dict = new_dict;
setDictDescription();
QPDFObjectHandle length_obj = new_dict.getKey("/Length"); QPDFObjectHandle length_obj = new_dict.getKey("/Length");
if (length_obj.isInteger()) if (length_obj.isInteger())
{ {

View File

@ -121,10 +121,10 @@ QPDF::isLinearized()
++p; ++p;
} }
QPDFTokenizer::Token t1 = readToken(this->m->file, true); QPDFTokenizer::Token t1 = readToken(this->m->file);
QPDFTokenizer::Token t2 = readToken(this->m->file, true); QPDFTokenizer::Token t2 = readToken(this->m->file);
QPDFTokenizer::Token t3 = readToken(this->m->file, true); QPDFTokenizer::Token t3 = readToken(this->m->file);
QPDFTokenizer::Token t4 = readToken(this->m->file, true); QPDFTokenizer::Token t4 = readToken(this->m->file);
if ((t1.getType() == QPDFTokenizer::tt_integer) && if ((t1.getType() == QPDFTokenizer::tt_integer) &&
(t2.getType() == QPDFTokenizer::tt_integer) && (t2.getType() == QPDFTokenizer::tt_integer) &&
(t3 == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "obj")) && (t3 == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "obj")) &&

View File

@ -14,6 +14,7 @@ class QPDF_Array: public QPDFObject
virtual std::string unparse(); virtual std::string unparse();
virtual QPDFObject::object_type_e getTypeCode() const; virtual QPDFObject::object_type_e getTypeCode() const;
virtual char const* getTypeName() const; virtual char const* getTypeName() const;
virtual void setDescription(QPDF*, std::string const&);
int getNItems() const; int getNItems() const;
QPDFObjectHandle getItem(int n) const; QPDFObjectHandle getItem(int n) const;

View File

@ -16,6 +16,7 @@ class QPDF_Dictionary: public QPDFObject
virtual std::string unparse(); virtual std::string unparse();
virtual QPDFObject::object_type_e getTypeCode() const; virtual QPDFObject::object_type_e getTypeCode() const;
virtual char const* getTypeName() const; virtual char const* getTypeName() const;
virtual void setDescription(QPDF*, std::string const&);
// hasKey() and getKeys() treat keys with null values as if they // hasKey() and getKeys() treat keys with null values as if they
// aren't there. getKey() returns null for the value of a // 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; std::map<std::string, QPDFObjectHandle> const& getAsMap() const;
// Replace value of key, adding it if it does not exist // 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 // Remove key, doing nothing if key does not exist
void removeKey(std::string const& key); void removeKey(std::string const& key);
// If object is null, replace key; otherwise, remove key // If object is null, replace key; otherwise, remove key

View File

@ -19,6 +19,7 @@ class QPDF_Stream: public QPDFObject
virtual std::string unparse(); virtual std::string unparse();
virtual QPDFObject::object_type_e getTypeCode() const; virtual QPDFObject::object_type_e getTypeCode() const;
virtual char const* getTypeName() const; virtual char const* getTypeName() const;
virtual void setDescription(QPDF*, std::string const&);
QPDFObjectHandle getDict() const; QPDFObjectHandle getDict() const;
bool isDataModified() const; bool isDataModified() const;
@ -66,6 +67,8 @@ class QPDF_Stream: public QPDFObject
int& colors, int& bits_per_component, int& colors, int& bits_per_component,
bool& early_code_change); bool& early_code_change);
void warn(QPDFExc const& e); void warn(QPDFExc const& e);
void setDictDescription();
void setStreamDescription();
QPDF* qpdf; QPDF* qpdf;
int objid; int objid;

View File

@ -105,7 +105,6 @@ QPDF reconstructed xref table 0
QPDF recovered in readObjectAtOffset 0 QPDF recovered in readObjectAtOffset 0
QPDF recovered stream length 0 QPDF recovered stream length 0
QPDF found wrong endstream in recovery 0 QPDF found wrong endstream in recovery 0
QPDFObjectHandle indirect to unknown 0
QPDF_Stream pipeStreamData with null pipeline 0 QPDF_Stream pipeStreamData with null pipeline 0
QPDFWriter not recompressing /FlateDecode 0 QPDFWriter not recompressing /FlateDecode 0
QPDF_encryption xref stream from encrypted file 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_preserve_unreferenced_objects 0
qpdf-c called qpdf_set_newline_before_endstream 0 qpdf-c called qpdf_set_newline_before_endstream 0
QPDF_Stream TIFF predictor 0 QPDF_Stream TIFF predictor 0
QPDFTokenizer EOF when not allowed 0
QPDFTokenizer inline image at EOF 0 QPDFTokenizer inline image at EOF 0
Pl_QPDFTokenizer found ID 0 Pl_QPDFTokenizer found ID 0
QPDFObjectHandle non-stream in stream array 0 QPDFObjectHandle non-stream in stream array 0
QPDFObjectHandle coalesce called on stream 0 QPDFObjectHandle coalesce called on stream 0
QPDFObjectHandle coalesce provide stream data 0 QPDFObjectHandle coalesce provide stream data 0
QPDF_Stream bad token at end during normalize 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

View File

@ -212,7 +212,7 @@ my @bug_tests = (
["99", "object 0", 2], ["99", "object 0", 2],
["99b", "object 0", 2], ["99b", "object 0", 2],
["100", "xref reconstruction loop", 2], ["100", "xref reconstruction loop", 2],
["101", "resolve for exception text", 2], ["101", "resolve for exception text", 3],
["117", "other infinite loop", 2], ["117", "other infinite loop", 2],
["118", "other infinite loop", 2], ["118", "other infinite loop", 2],
["119", "other infinite loop", 3], ["119", "other infinite loop", 3],
@ -735,6 +735,33 @@ $td->runtest("stream with tiff predictor",
$td->NORMALIZE_NEWLINES); $td->NORMALIZE_NEWLINES);
show_ntests(); 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 ---"); $td->notify("--- Coalesce contents ---");
$n_tests += 6; $n_tests += 6;
@ -1200,7 +1227,7 @@ $n_tests += @badfiles + 3;
# have error conditions that used to be fatal but are now considered # have error conditions that used to be fatal but are now considered
# non-fatal. # non-fatal.
my %badtest_overrides = (); 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; $badtest_overrides{$_} = 0;
} }
@ -1243,7 +1270,7 @@ $n_tests += @badfiles + 6;
# though in some cases it may. Acrobat Reader would not be able to # though in some cases it may. Acrobat Reader would not be able to
# recover any of these files any better. # recover any of these files any better.
my %recover_failures = (); my %recover_failures = ();
for (1, 7, 16, 18..21, 29, 35) for (1, 7, 16, 35)
{ {
$recover_failures{$_} = 1; $recover_failures{$_} = 1;
} }

View File

@ -1,10 +1,14 @@
WARNING: bad16.pdf (trailer, offset 753): unexpected dictionary close token 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 756): unexpected dictionary close token
WARNING: bad16.pdf (trailer, offset 759): unknown token while reading object; treating as string 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): 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: Attempting to reconstruct cross-reference table
WARNING: bad16.pdf (trailer, offset 753): unexpected dictionary close token 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 756): unexpected dictionary close token
WARNING: bad16.pdf (trailer, offset 759): unknown token while reading object; treating as string 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

View File

@ -1,4 +1,6 @@
WARNING: bad16.pdf (trailer, offset 753): unexpected dictionary close token 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 756): unexpected dictionary close token
WARNING: bad16.pdf (trailer, offset 759): unknown token while reading object; treating as string 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

View File

@ -1,4 +1,7 @@
WARNING: bad18.pdf: file is damaged
WARNING: bad18.pdf (trailer, offset 753): unexpected ) WARNING: bad18.pdf (trailer, offset 753): unexpected )
WARNING: bad18.pdf: Attempting to reconstruct cross-reference table /QTest is implicit
bad18.pdf (trailer, offset 753): unexpected ) /QTest is direct and has type null (2)
/QTest is null
unparse: null
unparseResolved: null
test 1 done

View File

@ -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

View File

@ -1,4 +1,7 @@
WARNING: bad19.pdf: file is damaged
WARNING: bad19.pdf (trailer, offset 753): unexpected > WARNING: bad19.pdf (trailer, offset 753): unexpected >
WARNING: bad19.pdf: Attempting to reconstruct cross-reference table /QTest is implicit
bad19.pdf (trailer, offset 753): unexpected > /QTest is direct and has type null (2)
/QTest is null
unparse: null
unparseResolved: null
test 1 done

View File

@ -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

View File

@ -1,4 +1,11 @@
WARNING: bad20.pdf: file is damaged
WARNING: bad20.pdf (trailer, offset 753): invalid character (q) in hexstring WARNING: bad20.pdf (trailer, offset 753): invalid character (q) in hexstring
WARNING: bad20.pdf: Attempting to reconstruct cross-reference table WARNING: bad20.pdf (trailer, offset 757): unknown token while reading object; treating as string
bad20.pdf (trailer, offset 753): invalid character (q) in hexstring 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

View File

@ -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

View File

@ -1,4 +1,9 @@
WARNING: bad21.pdf: file is damaged
WARNING: bad21.pdf (trailer, offset 742): invalid name token WARNING: bad21.pdf (trailer, offset 742): invalid name token
WARNING: bad21.pdf: Attempting to reconstruct cross-reference table WARNING: bad21.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake1
bad21.pdf (trailer, offset 742): invalid name token 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

View File

@ -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

View File

@ -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 (trailer, offset 742): null character not allowed in name token
WARNING: bad29.pdf: Attempting to reconstruct cross-reference table WARNING: bad29.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake1
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 /QPDFFake2
/QTest is implicit
/QTest is direct and has type null (2)
/QTest is null
unparse: null
unparseResolved: null
test 1 done

View File

@ -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

View File

@ -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 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 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 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 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): attempting to recover stream length
WARNING: issue-100.pdf (object 5 0, offset 489): recovered stream length: 12 WARNING: issue-100.pdf (object 5 0, offset 489): recovered stream length: 12

View File

@ -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 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 832): unknown token while reading object; treating as string
WARNING: issue-101.pdf (object 11 0, offset 856): unexpected > 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

View File

@ -2,4 +2,6 @@ WARNING: issue-146.pdf: file is damaged
WARNING: issue-146.pdf: can't find startxref WARNING: issue-146.pdf: can't find startxref
WARNING: issue-146.pdf: Attempting to reconstruct cross-reference table 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 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

View File

@ -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 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): 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 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 qpdf: operation succeeded with warnings; resulting file may have some problems

View File

@ -2,7 +2,7 @@ checking linearization-bounds-1.pdf
PDF Version: 1.3 PDF Version: 1.3
File is not encrypted File is not encrypted
File is linearized 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): attempting to recover stream length
WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106 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 linearization-bounds-1.pdf (linearization hint table, offset 1183): /S (shared object) offset is out of bounds

View File

@ -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

Binary file not shown.

View File

@ -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

View File

@ -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

View File

@ -1390,6 +1390,66 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true); w.setStaticID(true);
w.write(); 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 else
{ {
throw std::runtime_error(std::string("invalid test ") + throw std::runtime_error(std::string("invalid test ") +