Commit d0e99f195a987c483bbb6c5449cf39bee34e08a1

Authored by Jay Berkenbilt
1 parent c2e16827

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.
ChangeLog
1 2018-02-17 Jay Berkenbilt <ejb@ql.org> 1 2018-02-17 Jay Berkenbilt <ejb@ql.org>
2 2
  3 + * Major enhancements to handling of type errors within the qpdf
  4 + library. This fix is intended to eliminate those annoying cases
  5 + where qpdf would exit with a message like "operation for
  6 + dictionary object attemped on object of wrong type" without
  7 + providing any context. Now qpdf keeps enough context to be able to
  8 + issue a proper warning and to handle such conditions in a sensible
  9 + way. This should greatly increase the number of bad files that
  10 + qpdf can recover, and it should make it much easier to figure out
  11 + what's broken when a file contains errors.
  12 +
3 * Error message fix: replace "file position" with "offset" in 13 * Error message fix: replace "file position" with "offset" in
4 error messages that report lexical or parsing errors. Sometimes 14 error messages that report lexical or parsing errors. Sometimes
5 it's an offset in an object stream or a content stream rather than 15 it's an offset in an object stream or a content stream rather than
1 Soon 1 Soon
2 ==== 2 ====
3 3
4 - * Consider whether there should be a mode in which QPDFObjectHandle  
5 - returns nulls for operations on the wrong type instead of asserting  
6 - the type. The way things are wired up now, this would have to be a  
7 - global flag. Probably it makes sense to make that be the default  
8 - behavior and to add a static method in QPDFObjectHandle and  
9 - command-line flag that enables the stricter behavior globally for  
10 - easier debugging. For cases where we have enough information to do  
11 - so, we could still warn when not in strict mode.  
12 -  
13 * Add method to push inheritable resources to a single page by 4 * Add method to push inheritable resources to a single page by
14 walking up and copying without overwrite. Above logic will also be 5 walking up and copying without overwrite. Above logic will also be
15 sufficient to fix the limitation in 6 sufficient to fix the limitation in
include/qpdf/QPDF.hh
@@ -703,7 +703,6 @@ class QPDF @@ -703,7 +703,6 @@ class QPDF
703 PointerHolder<InputSource> input, int objid, int generation, 703 PointerHolder<InputSource> input, int objid, int generation,
704 qpdf_offset_t stream_offset); 704 qpdf_offset_t stream_offset);
705 QPDFTokenizer::Token readToken(PointerHolder<InputSource>, 705 QPDFTokenizer::Token readToken(PointerHolder<InputSource>,
706 - bool allow_bad = false,  
707 size_t max_len = 0); 706 size_t max_len = 0);
708 707
709 QPDFObjectHandle readObjectAtOffset( 708 QPDFObjectHandle readObjectAtOffset(
include/qpdf/QPDFObject.hh
@@ -23,6 +23,7 @@ @@ -23,6 +23,7 @@
23 #define __QPDFOBJECT_HH__ 23 #define __QPDFOBJECT_HH__
24 24
25 #include <qpdf/DLL.h> 25 #include <qpdf/DLL.h>
  26 +#include <qpdf/PointerHolder.hh>
26 27
27 #include <string> 28 #include <string>
28 29
@@ -32,6 +33,7 @@ class QPDFObjectHandle; @@ -32,6 +33,7 @@ class QPDFObjectHandle;
32 class QPDFObject 33 class QPDFObject
33 { 34 {
34 public: 35 public:
  36 + QPDFObject();
35 37
36 // Objects derived from QPDFObject are accessible through 38 // Objects derived from QPDFObject are accessible through
37 // QPDFObjectHandle. Each object returns a unique type code that 39 // QPDFObjectHandle. Each object returns a unique type code that
@@ -84,8 +86,27 @@ class QPDFObject @@ -84,8 +86,27 @@ class QPDFObject
84 }; 86 };
85 friend class ObjAccessor; 87 friend class ObjAccessor;
86 88
  89 + virtual void setDescription(QPDF*, std::string const&);
  90 + bool getDescription(QPDF*&, std::string&);
  91 + bool hasDescription();
  92 +
87 protected: 93 protected:
88 virtual void releaseResolved() {} 94 virtual void releaseResolved() {}
  95 +
  96 + private:
  97 + QPDFObject(QPDFObject const&);
  98 + QPDFObject& operator=(QPDFObject const&);
  99 + class Members
  100 + {
  101 + friend class QPDFObject;
  102 + public:
  103 + ~Members();
  104 + private:
  105 + Members();
  106 + QPDF* owning_qpdf;
  107 + std::string object_description;
  108 + };
  109 + PointerHolder<Members> m;
89 }; 110 };
90 111
91 #endif // __QPDFOBJECT_HH__ 112 #endif // __QPDFOBJECT_HH__
include/qpdf/QPDFObjectHandle.hh
@@ -398,6 +398,21 @@ class QPDFObjectHandle @@ -398,6 +398,21 @@ class QPDFObjectHandle
398 QPDF_DLL 398 QPDF_DLL
399 static QPDFObjectHandle newReserved(QPDF* qpdf); 399 static QPDFObjectHandle newReserved(QPDF* qpdf);
400 400
  401 + // Provide an owning qpdf and object description. The library does
  402 + // this automatically with objects that are read from from the
  403 + // input PDF and with objects that are created programmatically
  404 + // and inserted into the QPDF by adding them to an array or a
  405 + // dictionary or creating a new indirect object. Most end user
  406 + // code will not need to call this. If an object has an owning
  407 + // qpdf and object description, it enables qpdf to give warnings
  408 + // with proper context in some cases where it would otherwise
  409 + // raise exceptions.
  410 + QPDF_DLL
  411 + void setObjectDescription(QPDF* owning_qpdf,
  412 + std::string const& object_description);
  413 + QPDF_DLL
  414 + bool hasObjectDescription();
  415 +
401 // Accessor methods. If an accessor method that is valid for only 416 // Accessor methods. If an accessor method that is valid for only
402 // a particular object type is called on an object of the wrong 417 // a particular object type is called on an object of the wrong
403 // type, an exception is thrown. 418 // type, an exception is thrown.
@@ -498,7 +513,7 @@ class QPDFObjectHandle @@ -498,7 +513,7 @@ class QPDFObjectHandle
498 513
499 // Replace value of key, adding it if it does not exist 514 // Replace value of key, adding it if it does not exist
500 QPDF_DLL 515 QPDF_DLL
501 - void replaceKey(std::string const& key, QPDFObjectHandle const&); 516 + void replaceKey(std::string const& key, QPDFObjectHandle);
502 // Remove key, doing nothing if key does not exist 517 // Remove key, doing nothing if key does not exist
503 QPDF_DLL 518 QPDF_DLL
504 void removeKey(std::string const& key); 519 void removeKey(std::string const& key);
@@ -769,7 +784,10 @@ class QPDFObjectHandle @@ -769,7 +784,10 @@ class QPDFObjectHandle
769 }; 784 };
770 friend class ReleaseResolver; 785 friend class ReleaseResolver;
771 786
772 - // Convenience routine: Throws if the assumption is violated. 787 + // Convenience routine: Throws if the assumption is violated. Your
  788 + // code will be better if you call one of the isType methods and
  789 + // handle the case of the type being wrong, but these can be
  790 + // convenient if you have already verified the type.
773 QPDF_DLL 791 QPDF_DLL
774 void assertInitialized() const; 792 void assertInitialized() const;
775 793
@@ -832,10 +850,16 @@ class QPDFObjectHandle @@ -832,10 +850,16 @@ class QPDFObjectHandle
832 QPDF* qpdf, int objid, int generation, 850 QPDF* qpdf, int objid, int generation,
833 QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length); 851 QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length);
834 852
835 - void assertType(char const* type_name, bool istype) const; 853 + void typeWarning(char const* expected_type,
  854 + std::string const& warning);
  855 + void objectWarning(std::string const& warning);
  856 + void assertType(char const* type_name, bool istype);
836 void dereference(); 857 void dereference();
837 void makeDirectInternal(std::set<int>& visited); 858 void makeDirectInternal(std::set<int>& visited);
838 void releaseResolved(); 859 void releaseResolved();
  860 + static void setObjectDescriptionFromInput(
  861 + QPDFObjectHandle, QPDF*, std::string const&,
  862 + PointerHolder<InputSource>, qpdf_offset_t);
839 static QPDFObjectHandle parseInternal( 863 static QPDFObjectHandle parseInternal(
840 PointerHolder<InputSource> input, 864 PointerHolder<InputSource> input,
841 std::string const& object_description, 865 std::string const& object_description,
@@ -868,7 +892,7 @@ class QPDFObjectHandle @@ -868,7 +892,7 @@ class QPDFObjectHandle
868 892
869 bool initialized; 893 bool initialized;
870 894
871 - QPDF* qpdf; // 0 for direct object 895 + QPDF* qpdf;
872 int objid; // 0 for direct object 896 int objid; // 0 for direct object
873 int generation; 897 int generation;
874 PointerHolder<QPDFObject> obj; 898 PointerHolder<QPDFObject> obj;
ispell-words
@@ -23,9 +23,11 @@ activatePipelineStack @@ -23,9 +23,11 @@ activatePipelineStack
23 ActiveState 23 ActiveState
24 acyclic 24 acyclic
25 adbe 25 adbe
  26 +addContentTokenFilter
26 addPage 27 addPage
27 addPageAt 28 addPageAt
28 addPageContents 29 addPageContents
  30 +addTokenFilter
29 addToTable 31 addToTable
30 adjustAESStreamLength 32 adjustAESStreamLength
31 admon 33 admon
@@ -54,6 +56,7 @@ allowPoundAnywhereInName @@ -54,6 +56,7 @@ allowPoundAnywhereInName
54 allowPrintHighRes 56 allowPrintHighRes
55 allowPrintLowRes 57 allowPrintLowRes
56 antivirus 58 antivirus
  59 +anyBadTokens
57 aobjid 60 aobjid
58 apexcovantage 61 apexcovantage
59 api 62 api
@@ -71,11 +74,13 @@ argv @@ -71,11 +74,13 @@ argv
71 arko 74 arko
72 arko's 75 arko's
73 Arora 76 Arora
  77 +arrayOrStreamToStreamArray
74 ArtBox 78 ArtBox
75 ascii 79 ascii
76 asciiHex 80 asciiHex
77 ASCIIHexDecode 81 ASCIIHexDecode
78 ASCIIHexDecoder 82 ASCIIHexDecoder
  83 +asMap
79 assertArray 84 assertArray
80 assertBool 85 assertBool
81 assertDictionary 86 assertDictionary
@@ -214,12 +219,15 @@ closeObject @@ -214,12 +219,15 @@ closeObject
214 cmath 219 cmath
215 cmd 220 cmd
216 cmyk 221 cmyk
  222 +coalesceContentStreams
  223 +CoalesceProvider
217 codepage 224 codepage
218 codepoint 225 codepoint
219 col 226 col
220 Coldwind 227 Coldwind
221 ColorSpace 228 ColorSpace
222 colorspace 229 colorspace
  230 +ColorToGray
223 com 231 com
224 compareVersions 232 compareVersions
225 compatbility 233 compatbility
@@ -230,6 +238,7 @@ Cond @@ -230,6 +238,7 @@ Cond
230 config 238 config
231 conftest 239 conftest
232 const 240 const
  241 +ContentNormalizer
233 contrib 242 contrib
234 CopiedStreamDataProvider 243 CopiedStreamDataProvider
235 copyEncryptionParameters 244 copyEncryptionParameters
@@ -300,6 +309,7 @@ deflateEnd @@ -300,6 +309,7 @@ deflateEnd
300 deflateInit 309 deflateInit
301 defq 310 defq
302 delphi 311 delphi
  312 +deque
303 dereference 313 dereference
304 dereferenced 314 dereferenced
305 dest 315 dest
@@ -411,6 +421,7 @@ esize @@ -411,6 +421,7 @@ esize
411 exc 421 exc
412 exe 422 exe
413 exp 423 exp
  424 +expectInlineImage
414 ExtensionLevel 425 ExtensionLevel
415 extern 426 extern
416 fb 427 fb
@@ -439,6 +450,7 @@ Filespec @@ -439,6 +450,7 @@ Filespec
439 FILETIME 450 FILETIME
440 filetrailer 451 filetrailer
441 filterCompressedObjects 452 filterCompressedObjects
  453 +filterPageContents
442 findAndSkipNextEOL 454 findAndSkipNextEOL
443 findAttachmentStreams 455 findAttachmentStreams
444 findEndstream 456 findEndstream
@@ -509,6 +521,7 @@ getCompressibleObjects @@ -509,6 +521,7 @@ getCompressibleObjects
509 getCompressibleObjGens 521 getCompressibleObjGens
510 getCount 522 getCount
511 getDataChecksum 523 getDataChecksum
  524 +getDescription
512 getDict 525 getDict
513 getDictAsMap 526 getDictAsMap
514 getEncryptionKey 527 getEncryptionKey
@@ -562,6 +575,7 @@ getOE @@ -562,6 +575,7 @@ getOE
562 getOffset 575 getOffset
563 getOffsetLength 576 getOffsetLength
564 getOperatorValue 577 getOperatorValue
  578 +getOriginalID
565 getOwningQPDF 579 getOwningQPDF
566 getP 580 getP
567 getPaddedUserPassword 581 getPaddedUserPassword
@@ -626,7 +640,10 @@ handleCode @@ -626,7 +640,10 @@ handleCode
626 handleData 640 handleData
627 handleEOF 641 handleEOF
628 handleObject 642 handleObject
  643 +handleToken
  644 +hasDescription
629 hasKey 645 hasKey
  646 +hasObjectDescription
630 hb 647 hb
631 hbp 648 hbp
632 HCRYPTPROV 649 HCRYPTPROV
@@ -668,6 +685,7 @@ ifeq @@ -668,6 +685,7 @@ ifeq
668 iff 685 iff
669 ifndef 686 ifndef
670 ifstream 687 ifstream
  688 +ignorable
671 ijg 689 ijg
672 Im 690 Im
673 ImageC 691 ImageC
@@ -676,6 +694,7 @@ ImageInverter @@ -676,6 +694,7 @@ ImageInverter
676 ImageMask 694 ImageMask
677 ImageProvider 695 ImageProvider
678 inbuf 696 inbuf
  697 +includeIgnorable
679 INDOC 698 INDOC
680 indx 699 indx
681 inf 700 inf
@@ -720,9 +739,12 @@ iostream @@ -720,9 +739,12 @@ iostream
720 irdp 739 irdp
721 isArray 740 isArray
722 isBool 741 isBool
  742 +isDataModified
  743 +isDelimiter
723 isDictionary 744 isDictionary
724 isdigit 745 isdigit
725 isEncrypted 746 isEncrypted
  747 +isIgnorable
726 isIndirect 748 isIndirect
727 isInitialized 749 isInitialized
728 isInlineImage 750 isInlineImage
@@ -731,6 +753,7 @@ isLinearized @@ -731,6 +753,7 @@ isLinearized
731 isName 753 isName
732 isNull 754 isNull
733 isNumber 755 isNumber
  756 +isNumeric
734 iso 757 iso
735 isOperator 758 isOperator
736 isOrHasName 759 isOrHasName
@@ -741,10 +764,12 @@ isReal @@ -741,10 +764,12 @@ isReal
741 isReserved 764 isReserved
742 isScalar 765 isScalar
743 isspace 766 isspace
  767 +isSpace
744 isStream 768 isStream
745 isString 769 isString
746 istream 770 istream
747 istype 771 istype
  772 +isType
748 italicseq 773 italicseq
749 itemizedlist 774 itemizedlist
750 iter 775 iter
@@ -772,6 +797,7 @@ keyset @@ -772,6 +797,7 @@ keyset
772 LARGEFILE 797 LARGEFILE
773 lastnum 798 lastnum
774 lastreleased 799 lastreleased
  800 +lastTokenWasBad
775 latin 801 latin
776 lbuf 802 lbuf
777 lc 803 lc
@@ -839,6 +865,7 @@ malloc @@ -839,6 +865,7 @@ malloc
839 manualFinish 865 manualFinish
840 Mateusz 866 Mateusz
841 maxEnd 867 maxEnd
  868 +maxlen
842 maxval 869 maxval
843 md 870 md
844 mdash 871 mdash
@@ -933,6 +960,7 @@ NTE @@ -933,6 +960,7 @@ NTE
933 ntotal 960 ntotal
934 NUL 961 NUL
935 num 962 num
  963 +numericValue
936 numrange 964 numrange
937 nval 965 nval
938 nwalsh 966 nwalsh
@@ -1007,6 +1035,7 @@ parms @@ -1007,6 +1035,7 @@ parms
1007 parsecontent 1035 parsecontent
1008 parseContentStream 1036 parseContentStream
1009 parseInternal 1037 parseInternal
  1038 +parsePageContents
1010 ParserCallbacks 1039 ParserCallbacks
1011 parseVersion 1040 parseVersion
1012 partLen 1041 partLen
@@ -1033,6 +1062,9 @@ persistAcrossFinish @@ -1033,6 +1062,9 @@ persistAcrossFinish
1033 ph 1062 ph
1034 phe 1063 phe
1035 php 1064 php
  1065 +pipeContentStreams
  1066 +PipelineAccessor
  1067 +pipePageContents
1036 pipeStreamData 1068 pipeStreamData
1037 pipeStringAndFinish 1069 pipeStringAndFinish
1038 Pkey 1070 Pkey
@@ -1072,6 +1104,7 @@ procset @@ -1072,6 +1104,7 @@ procset
1072 ProcSet 1104 ProcSet
1073 procsets 1105 procsets
1074 programlisting 1106 programlisting
  1107 +programmatically
1075 Projet 1108 Projet
1076 prov 1109 prov
1077 provideRandomData 1110 provideRandomData
@@ -1106,6 +1139,7 @@ QPDF&#39;s @@ -1106,6 +1139,7 @@ QPDF&#39;s
1106 QPDFCONSTANTS 1139 QPDFCONSTANTS
1107 QPDFExc 1140 QPDFExc
1108 QPDFFake 1141 QPDFFake
  1142 +QPDFFakeName
1109 QPDFObject 1143 QPDFObject
1110 QPDFObjectHandle 1144 QPDFObjectHandle
1111 QPDFObjectHandle's 1145 QPDFObjectHandle's
@@ -1170,6 +1204,7 @@ ReleaseResolver @@ -1170,6 +1204,7 @@ ReleaseResolver
1170 remotesensing 1204 remotesensing
1171 removeKey 1205 removeKey
1172 removePage 1206 removePage
  1207 +removereplace
1173 repl 1208 repl
1174 replaceDict 1209 replaceDict
1175 replaceFilterData 1210 replaceFilterData
@@ -1191,6 +1226,7 @@ retested @@ -1191,6 +1226,7 @@ retested
1191 reverseResolved 1226 reverseResolved
1192 rf 1227 rf
1193 rfont 1228 rfont
  1229 +rg
1194 rgb 1230 rgb
1195 rhs 1231 rhs
1196 rijndael 1232 rijndael
@@ -1240,7 +1276,9 @@ setCompressStreams @@ -1240,7 +1276,9 @@ setCompressStreams
1240 setContentNormalization 1276 setContentNormalization
1241 setDataKey 1277 setDataKey
1242 setDecodeLevel 1278 setDecodeLevel
  1279 +setDescription
1243 setDeterministicID 1280 setDeterministicID
  1281 +setDictDescription
1244 setEncryptionParameters 1282 setEncryptionParameters
1245 setEncryptionParametersInternal 1283 setEncryptionParametersInternal
1246 setExtraHeaderText 1284 setExtraHeaderText
@@ -1254,11 +1292,14 @@ setjmp @@ -1254,11 +1292,14 @@ setjmp
1254 setLastObjectDescription 1292 setLastObjectDescription
1255 setLastOffset 1293 setLastOffset
1256 setLinearization 1294 setLinearization
  1295 +setLinearizationPass
1257 setLineBuf 1296 setLineBuf
1258 setMinimumPDFVersion 1297 setMinimumPDFVersion
1259 setmode 1298 setmode
1260 setNewlineBeforeEndstream 1299 setNewlineBeforeEndstream
1261 setO 1300 setO
  1301 +setObjectDescription
  1302 +setObjectDescriptionFromInput
1262 setObjectStreamMode 1303 setObjectStreamMode
1263 setObjGen 1304 setObjGen
1264 setOutputFile 1305 setOutputFile
@@ -1268,6 +1309,7 @@ setOutputPipeline @@ -1268,6 +1309,7 @@ setOutputPipeline
1268 setOutputStreams 1309 setOutputStreams
1269 setPasswordIsHexKey 1310 setPasswordIsHexKey
1270 setPCLm 1311 setPCLm
  1312 +setPipeline
1271 setprecision 1313 setprecision
1272 setPreserveEncryption 1314 setPreserveEncryption
1273 setPreserveUnreferencedObjects 1315 setPreserveUnreferencedObjects
@@ -1277,6 +1319,7 @@ setRandomDataProvider @@ -1277,6 +1319,7 @@ setRandomDataProvider
1277 setStaticAesIV 1319 setStaticAesIV
1278 setStaticID 1320 setStaticID
1279 setStreamDataMode 1321 setStreamDataMode
  1322 +setStreamDescription
1280 setSuppressOriginalObjectIDs 1323 setSuppressOriginalObjectIDs
1281 setSuppressWarnings 1324 setSuppressWarnings
1282 setTrailer 1325 setTrailer
@@ -1329,8 +1372,10 @@ StreamDataProvider @@ -1329,8 +1372,10 @@ StreamDataProvider
1329 strerror 1372 strerror
1330 StrF 1373 StrF
1331 stricmp 1374 stricmp
  1375 +StringCounter
1332 StringDecrypter 1376 StringDecrypter
1333 stringprep 1377 stringprep
  1378 +StringReverser
1334 stripesize 1379 stripesize
1335 strlen 1380 strlen
1336 strncmp 1381 strncmp
@@ -1385,10 +1430,13 @@ tobj @@ -1385,10 +1430,13 @@ tobj
1385 tobjid 1430 tobjid
1386 TODO 1431 TODO
1387 toffset 1432 toffset
  1433 +TokenFilter
  1434 +TokenFilters
1388 tokenize 1435 tokenize
1389 tokenized 1436 tokenized
1390 tokenizer 1437 tokenizer
1391 tokenizing 1438 tokenizing
  1439 +tokenTypeName
1392 toolchain 1440 toolchain
1393 Toolchains 1441 Toolchains
1394 toupper 1442 toupper
@@ -1400,6 +1448,7 @@ trimTrailerForWrite @@ -1400,6 +1448,7 @@ trimTrailerForWrite
1400 tt 1448 tt
1401 turbo 1449 turbo
1402 txt 1450 txt
  1451 +typeWarning
1403 uc 1452 uc
1404 udata 1453 udata
1405 UE 1454 UE
@@ -1428,6 +1477,7 @@ unparse @@ -1428,6 +1477,7 @@ unparse
1428 unparseChild 1477 unparseChild
1429 unparseObject 1478 unparseObject
1430 unparseResolved 1479 unparseResolved
  1480 +unparsing
1431 unreadCh 1481 unreadCh
1432 unreferenced 1482 unreferenced
1433 unresolvable 1483 unresolvable
libqpdf/QPDF.cc
@@ -106,6 +106,7 @@ QPDF::Members::~Members() @@ -106,6 +106,7 @@ QPDF::Members::~Members()
106 QPDF::QPDF() : 106 QPDF::QPDF() :
107 m(new Members()) 107 m(new Members())
108 { 108 {
  109 + m->tokenizer.allowEOF();
109 } 110 }
110 111
111 QPDF::~QPDF() 112 QPDF::~QPDF()
@@ -272,10 +273,10 @@ QPDF::findHeader() @@ -272,10 +273,10 @@ QPDF::findHeader()
272 bool 273 bool
273 QPDF::findStartxref() 274 QPDF::findStartxref()
274 { 275 {
275 - QPDFTokenizer::Token t = readToken(this->m->file, true); 276 + QPDFTokenizer::Token t = readToken(this->m->file);
276 if (t == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "startxref")) 277 if (t == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "startxref"))
277 { 278 {
278 - t = readToken(this->m->file, true); 279 + t = readToken(this->m->file);
279 if (t.getType() == QPDFTokenizer::tt_integer) 280 if (t.getType() == QPDFTokenizer::tt_integer)
280 { 281 {
281 // Position in front of offset token 282 // Position in front of offset token
@@ -421,7 +422,7 @@ QPDF::reconstruct_xref(QPDFExc&amp; e) @@ -421,7 +422,7 @@ QPDF::reconstruct_xref(QPDFExc&amp; e)
421 this->m->file->findAndSkipNextEOL(); 422 this->m->file->findAndSkipNextEOL();
422 qpdf_offset_t next_line_start = this->m->file->tell(); 423 qpdf_offset_t next_line_start = this->m->file->tell();
423 this->m->file->seek(line_start, SEEK_SET); 424 this->m->file->seek(line_start, SEEK_SET);
424 - QPDFTokenizer::Token t1 = readToken(this->m->file, true, MAX_LEN); 425 + QPDFTokenizer::Token t1 = readToken(this->m->file, MAX_LEN);
425 qpdf_offset_t token_start = 426 qpdf_offset_t token_start =
426 this->m->file->tell() - t1.getValue().length(); 427 this->m->file->tell() - t1.getValue().length();
427 if (token_start >= next_line_start) 428 if (token_start >= next_line_start)
@@ -440,9 +441,9 @@ QPDF::reconstruct_xref(QPDFExc&amp; e) @@ -440,9 +441,9 @@ QPDF::reconstruct_xref(QPDFExc&amp; e)
440 if (t1.getType() == QPDFTokenizer::tt_integer) 441 if (t1.getType() == QPDFTokenizer::tt_integer)
441 { 442 {
442 QPDFTokenizer::Token t2 = 443 QPDFTokenizer::Token t2 =
443 - readToken(this->m->file, true, MAX_LEN); 444 + readToken(this->m->file, MAX_LEN);
444 QPDFTokenizer::Token t3 = 445 QPDFTokenizer::Token t3 =
445 - readToken(this->m->file, true, MAX_LEN); 446 + readToken(this->m->file, MAX_LEN);
446 if ((t2.getType() == QPDFTokenizer::tt_integer) && 447 if ((t2.getType() == QPDFTokenizer::tt_integer) &&
447 (t3 == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "obj"))) 448 (t3 == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "obj")))
448 { 449 {
@@ -1429,7 +1430,7 @@ bool @@ -1429,7 +1430,7 @@ bool
1429 QPDF::findEndstream() 1430 QPDF::findEndstream()
1430 { 1431 {
1431 // Find endstream or endobj. Position the input at that token. 1432 // Find endstream or endobj. Position the input at that token.
1432 - QPDFTokenizer::Token t = readToken(this->m->file, true, 20); 1433 + QPDFTokenizer::Token t = readToken(this->m->file, 20);
1433 if ((t.getType() == QPDFTokenizer::tt_word) && 1434 if ((t.getType() == QPDFTokenizer::tt_word) &&
1434 ((t.getValue() == "endobj") || 1435 ((t.getValue() == "endobj") ||
1435 (t.getValue() == "endstream"))) 1436 (t.getValue() == "endstream")))
@@ -1522,11 +1523,10 @@ QPDF::recoverStreamLength(PointerHolder&lt;InputSource&gt; input, @@ -1522,11 +1523,10 @@ QPDF::recoverStreamLength(PointerHolder&lt;InputSource&gt; input,
1522 } 1523 }
1523 1524
1524 QPDFTokenizer::Token 1525 QPDFTokenizer::Token
1525 -QPDF::readToken(PointerHolder<InputSource> input,  
1526 - bool allow_bad, size_t max_len) 1526 +QPDF::readToken(PointerHolder<InputSource> input, size_t max_len)
1527 { 1527 {
1528 return this->m->tokenizer.readToken( 1528 return this->m->tokenizer.readToken(
1529 - input, this->m->last_object_description, allow_bad, max_len); 1529 + input, this->m->last_object_description, true, max_len);
1530 } 1530 }
1531 1531
1532 QPDFObjectHandle 1532 QPDFObjectHandle
@@ -1730,16 +1730,10 @@ QPDF::resolve(int objid, int generation) @@ -1730,16 +1730,10 @@ QPDF::resolve(int objid, int generation)
1730 } 1730 }
1731 ResolveRecorder rr(this, og); 1731 ResolveRecorder rr(this, og);
1732 1732
1733 - if (! this->m->obj_cache.count(og)) 1733 + // PDF spec says unknown objects resolve to the null object.
  1734 + if ((! this->m->obj_cache.count(og)) && this->m->xref_table.count(og))
1734 { 1735 {
1735 - if (! this->m->xref_table.count(og))  
1736 - {  
1737 - // PDF spec says unknown objects resolve to the null object.  
1738 - return new QPDF_Null;  
1739 - }  
1740 -  
1741 QPDFXRefEntry const& entry = this->m->xref_table[og]; 1736 QPDFXRefEntry const& entry = this->m->xref_table[og];
1742 - bool success = false;  
1743 try 1737 try
1744 { 1738 {
1745 switch (entry.getType()) 1739 switch (entry.getType())
@@ -1768,7 +1762,6 @@ QPDF::resolve(int objid, int generation) @@ -1768,7 +1762,6 @@ QPDF::resolve(int objid, int generation)
1768 QUtil::int_to_string(generation) + 1762 QUtil::int_to_string(generation) +
1769 " has unexpected xref entry type"); 1763 " has unexpected xref entry type");
1770 } 1764 }
1771 - success = true;  
1772 } 1765 }
1773 catch (QPDFExc& e) 1766 catch (QPDFExc& e)
1774 { 1767 {
@@ -1782,16 +1775,24 @@ QPDF::resolve(int objid, int generation) @@ -1782,16 +1775,24 @@ QPDF::resolve(int objid, int generation)
1782 QUtil::int_to_string(generation) + 1775 QUtil::int_to_string(generation) +
1783 ": error reading object: " + e.what())); 1776 ": error reading object: " + e.what()));
1784 } 1777 }
1785 - if (! success)  
1786 - {  
1787 - QTC::TC("qpdf", "QPDF resolve failure to null");  
1788 - QPDFObjectHandle oh = QPDFObjectHandle::newNull();  
1789 - this->m->obj_cache[og] =  
1790 - ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1);  
1791 - } 1778 + }
  1779 + if (this->m->obj_cache.count(og) == 0)
  1780 + {
  1781 + QTC::TC("qpdf", "QPDF resolve failure to null");
  1782 + QPDFObjectHandle oh = QPDFObjectHandle::newNull();
  1783 + this->m->obj_cache[og] =
  1784 + ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1);
1792 } 1785 }
1793 1786
1794 - return this->m->obj_cache[og].object; 1787 + PointerHolder<QPDFObject> result(this->m->obj_cache[og].object);
  1788 + if (! result->hasDescription())
  1789 + {
  1790 + result->setDescription(
  1791 + this,
  1792 + "object " + QUtil::int_to_string(objid) + " " +
  1793 + QUtil::int_to_string(generation));
  1794 + }
  1795 + return result;
1795 } 1796 }
1796 1797
1797 void 1798 void
libqpdf/QPDFExc.cc
@@ -32,7 +32,10 @@ QPDFExc::createWhat(std::string const&amp; filename, @@ -32,7 +32,10 @@ QPDFExc::createWhat(std::string const&amp; filename,
32 } 32 }
33 if (! (object.empty() && offset == 0)) 33 if (! (object.empty() && offset == 0))
34 { 34 {
35 - result += " ("; 35 + if (! filename.empty())
  36 + {
  37 + result += " (";
  38 + }
36 if (! object.empty()) 39 if (! object.empty())
37 { 40 {
38 result += object; 41 result += object;
@@ -45,7 +48,10 @@ QPDFExc::createWhat(std::string const&amp; filename, @@ -45,7 +48,10 @@ QPDFExc::createWhat(std::string const&amp; filename,
45 { 48 {
46 result += "offset " + QUtil::int_to_string(offset); 49 result += "offset " + QUtil::int_to_string(offset);
47 } 50 }
48 - result += ")"; 51 + if (! filename.empty())
  52 + {
  53 + result += ")";
  54 + }
49 } 55 }
50 if (! result.empty()) 56 if (! result.empty())
51 { 57 {
libqpdf/QPDFObject.cc
1 #include <qpdf/QPDFObject.hh> 1 #include <qpdf/QPDFObject.hh>
  2 +
  3 +QPDFObject::Members::Members() :
  4 + owning_qpdf(0)
  5 +{
  6 +}
  7 +
  8 +QPDFObject::Members::~Members()
  9 +{
  10 +}
  11 +
  12 +QPDFObject::QPDFObject() :
  13 + m(new Members)
  14 +{
  15 +}
  16 +
  17 +void
  18 +QPDFObject::setDescription(QPDF* qpdf, std::string const& description)
  19 +{
  20 + this->m->owning_qpdf = qpdf;
  21 + this->m->object_description = description;
  22 +}
  23 +
  24 +bool
  25 +QPDFObject::getDescription(QPDF*& qpdf, std::string& description)
  26 +{
  27 + qpdf = this->m->owning_qpdf;
  28 + description = this->m->object_description;
  29 + return this->m->owning_qpdf;
  30 +}
  31 +
  32 +bool
  33 +QPDFObject::hasDescription()
  34 +{
  35 + return this->m->owning_qpdf;
  36 +}
libqpdf/QPDFObjectHandle.cc
@@ -190,6 +190,18 @@ QPDFObjectHandle::releaseResolved() @@ -190,6 +190,18 @@ QPDFObjectHandle::releaseResolved()
190 } 190 }
191 } 191 }
192 192
  193 +void
  194 +QPDFObjectHandle::setObjectDescriptionFromInput(
  195 + QPDFObjectHandle object, QPDF* context,
  196 + std::string const& description, PointerHolder<InputSource> input,
  197 + qpdf_offset_t offset)
  198 +{
  199 + object.setObjectDescription(
  200 + context,
  201 + input->getName() + ", " + description +
  202 + " at offset " + QUtil::int_to_string(offset));
  203 +}
  204 +
193 bool 205 bool
194 QPDFObjectHandle::isInitialized() const 206 QPDFObjectHandle::isInitialized() const
195 { 207 {
@@ -282,7 +294,8 @@ QPDFObjectHandle::getNumericValue() @@ -282,7 +294,8 @@ QPDFObjectHandle::getNumericValue()
282 } 294 }
283 else 295 else
284 { 296 {
285 - throw std::logic_error("getNumericValue called for non-numeric object"); 297 + typeWarning("number", "returning 0");
  298 + QTC::TC("qpdf", "QPDFObjectHandle numeric non-numeric");
286 } 299 }
287 return result; 300 return result;
288 } 301 }
@@ -363,8 +376,16 @@ QPDFObjectHandle::isScalar() @@ -363,8 +376,16 @@ QPDFObjectHandle::isScalar()
363 bool 376 bool
364 QPDFObjectHandle::getBoolValue() 377 QPDFObjectHandle::getBoolValue()
365 { 378 {
366 - assertBool();  
367 - return dynamic_cast<QPDF_Bool*>(m->obj.getPointer())->getVal(); 379 + if (isBool())
  380 + {
  381 + return dynamic_cast<QPDF_Bool*>(m->obj.getPointer())->getVal();
  382 + }
  383 + else
  384 + {
  385 + typeWarning("boolean", "returning false");
  386 + QTC::TC("qpdf", "QPDFObjectHandle boolean returning false");
  387 + return false;
  388 + }
368 } 389 }
369 390
370 // Integer accessors 391 // Integer accessors
@@ -372,8 +393,16 @@ QPDFObjectHandle::getBoolValue() @@ -372,8 +393,16 @@ QPDFObjectHandle::getBoolValue()
372 long long 393 long long
373 QPDFObjectHandle::getIntValue() 394 QPDFObjectHandle::getIntValue()
374 { 395 {
375 - assertInteger();  
376 - return dynamic_cast<QPDF_Integer*>(m->obj.getPointer())->getVal(); 396 + if (isInteger())
  397 + {
  398 + return dynamic_cast<QPDF_Integer*>(m->obj.getPointer())->getVal();
  399 + }
  400 + else
  401 + {
  402 + typeWarning("integer", "returning 0");
  403 + QTC::TC("qpdf", "QPDFObjectHandle integer returning 0");
  404 + return 0;
  405 + }
377 } 406 }
378 407
379 // Real accessors 408 // Real accessors
@@ -381,8 +410,16 @@ QPDFObjectHandle::getIntValue() @@ -381,8 +410,16 @@ QPDFObjectHandle::getIntValue()
381 std::string 410 std::string
382 QPDFObjectHandle::getRealValue() 411 QPDFObjectHandle::getRealValue()
383 { 412 {
384 - assertReal();  
385 - return dynamic_cast<QPDF_Real*>(m->obj.getPointer())->getVal(); 413 + if (isReal())
  414 + {
  415 + return dynamic_cast<QPDF_Real*>(m->obj.getPointer())->getVal();
  416 + }
  417 + else
  418 + {
  419 + typeWarning("real", "returning 0.0");
  420 + QTC::TC("qpdf", "QPDFObjectHandle real returning 0.0");
  421 + return "0.0";
  422 + }
386 } 423 }
387 424
388 // Name accessors 425 // Name accessors
@@ -390,8 +427,16 @@ QPDFObjectHandle::getRealValue() @@ -390,8 +427,16 @@ QPDFObjectHandle::getRealValue()
390 std::string 427 std::string
391 QPDFObjectHandle::getName() 428 QPDFObjectHandle::getName()
392 { 429 {
393 - assertName();  
394 - return dynamic_cast<QPDF_Name*>(m->obj.getPointer())->getName(); 430 + if (isName())
  431 + {
  432 + return dynamic_cast<QPDF_Name*>(m->obj.getPointer())->getName();
  433 + }
  434 + else
  435 + {
  436 + typeWarning("name", "returning dummy name");
  437 + QTC::TC("qpdf", "QPDFObjectHandle name returning dummy name");
  438 + return "/QPDFFakeName";
  439 + }
395 } 440 }
396 441
397 // String accessors 442 // String accessors
@@ -399,15 +444,31 @@ QPDFObjectHandle::getName() @@ -399,15 +444,31 @@ QPDFObjectHandle::getName()
399 std::string 444 std::string
400 QPDFObjectHandle::getStringValue() 445 QPDFObjectHandle::getStringValue()
401 { 446 {
402 - assertString();  
403 - return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getVal(); 447 + if (isString())
  448 + {
  449 + return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getVal();
  450 + }
  451 + else
  452 + {
  453 + typeWarning("string", "returning empty string");
  454 + QTC::TC("qpdf", "QPDFObjectHandle string returning empty string");
  455 + return "";
  456 + }
404 } 457 }
405 458
406 std::string 459 std::string
407 QPDFObjectHandle::getUTF8Value() 460 QPDFObjectHandle::getUTF8Value()
408 { 461 {
409 - assertString();  
410 - return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getUTF8Val(); 462 + if (isString())
  463 + {
  464 + return dynamic_cast<QPDF_String*>(m->obj.getPointer())->getUTF8Val();
  465 + }
  466 + else
  467 + {
  468 + typeWarning("string", "returning empty string");
  469 + QTC::TC("qpdf", "QPDFObjectHandle string returning empty utf8");
  470 + return "";
  471 + }
411 } 472 }
412 473
413 // Operator and Inline Image accessors 474 // Operator and Inline Image accessors
@@ -415,15 +476,31 @@ QPDFObjectHandle::getUTF8Value() @@ -415,15 +476,31 @@ QPDFObjectHandle::getUTF8Value()
415 std::string 476 std::string
416 QPDFObjectHandle::getOperatorValue() 477 QPDFObjectHandle::getOperatorValue()
417 { 478 {
418 - assertOperator();  
419 - return dynamic_cast<QPDF_Operator*>(m->obj.getPointer())->getVal(); 479 + if (isOperator())
  480 + {
  481 + return dynamic_cast<QPDF_Operator*>(m->obj.getPointer())->getVal();
  482 + }
  483 + else
  484 + {
  485 + typeWarning("operator", "returning fake value");
  486 + QTC::TC("qpdf", "QPDFObjectHandle operator returning fake value");
  487 + return "QPDFFAKE";
  488 + }
420 } 489 }
421 490
422 std::string 491 std::string
423 QPDFObjectHandle::getInlineImageValue() 492 QPDFObjectHandle::getInlineImageValue()
424 { 493 {
425 - assertInlineImage();  
426 - return dynamic_cast<QPDF_InlineImage*>(m->obj.getPointer())->getVal(); 494 + if (isInlineImage())
  495 + {
  496 + return dynamic_cast<QPDF_InlineImage*>(m->obj.getPointer())->getVal();
  497 + }
  498 + else
  499 + {
  500 + typeWarning("inlineimage", "returning empty data");
  501 + QTC::TC("qpdf", "QPDFObjectHandle inlineimage returning empty data");
  502 + return "";
  503 + }
427 } 504 }
428 505
429 // Array accessors 506 // Array accessors
@@ -431,22 +508,66 @@ QPDFObjectHandle::getInlineImageValue() @@ -431,22 +508,66 @@ QPDFObjectHandle::getInlineImageValue()
431 int 508 int
432 QPDFObjectHandle::getArrayNItems() 509 QPDFObjectHandle::getArrayNItems()
433 { 510 {
434 - assertArray();  
435 - return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getNItems(); 511 + if (isArray())
  512 + {
  513 + return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getNItems();
  514 + }
  515 + else
  516 + {
  517 + typeWarning("array", "treating as empty");
  518 + QTC::TC("qpdf", "QPDFObjectHandle array treating as empty");
  519 + return 0;
  520 + }
436 } 521 }
437 522
438 QPDFObjectHandle 523 QPDFObjectHandle
439 QPDFObjectHandle::getArrayItem(int n) 524 QPDFObjectHandle::getArrayItem(int n)
440 { 525 {
441 - assertArray();  
442 - return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getItem(n); 526 + QPDFObjectHandle result;
  527 + if (isArray() && (n < getArrayNItems()) && (n >= 0))
  528 + {
  529 + result = dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getItem(n);
  530 + }
  531 + else
  532 + {
  533 + result = newNull();
  534 + if (isArray())
  535 + {
  536 + objectWarning("returning null for out of bounds array access");
  537 + QTC::TC("qpdf", "QPDFObjectHandle array bounds");
  538 + }
  539 + else
  540 + {
  541 + typeWarning("array", "returning null");
  542 + QTC::TC("qpdf", "QPDFObjectHandle array null for non-array");
  543 + }
  544 + QPDF* context = 0;
  545 + std::string description;
  546 + if (this->m->obj->getDescription(context, description))
  547 + {
  548 + result.setObjectDescription(
  549 + context,
  550 + description +
  551 + " -> null returned from invalid array access");
  552 + }
  553 + }
  554 + return result;
443 } 555 }
444 556
445 std::vector<QPDFObjectHandle> 557 std::vector<QPDFObjectHandle>
446 QPDFObjectHandle::getArrayAsVector() 558 QPDFObjectHandle::getArrayAsVector()
447 { 559 {
448 - assertArray();  
449 - return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getAsVector(); 560 + std::vector<QPDFObjectHandle> result;
  561 + if (isArray())
  562 + {
  563 + result = dynamic_cast<QPDF_Array*>(m->obj.getPointer())->getAsVector();
  564 + }
  565 + else
  566 + {
  567 + typeWarning("array", "treating as empty");
  568 + QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector");
  569 + }
  570 + return result;
450 } 571 }
451 572
452 // Array mutators 573 // Array mutators
@@ -454,36 +575,79 @@ QPDFObjectHandle::getArrayAsVector() @@ -454,36 +575,79 @@ QPDFObjectHandle::getArrayAsVector()
454 void 575 void
455 QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item) 576 QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item)
456 { 577 {
457 - assertArray();  
458 - return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setItem(n, item); 578 + if (isArray())
  579 + {
  580 + dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setItem(n, item);
  581 + }
  582 + else
  583 + {
  584 + typeWarning("array", "ignoring attempt to set item");
  585 + QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item");
  586 + }
459 } 587 }
460 588
461 void 589 void
462 QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) 590 QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items)
463 { 591 {
464 - assertArray();  
465 - return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setFromVector(items); 592 + if (isArray())
  593 + {
  594 + dynamic_cast<QPDF_Array*>(m->obj.getPointer())->setFromVector(items);
  595 + }
  596 + else
  597 + {
  598 + typeWarning("array", "ignoring attempt to replace items");
  599 + QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items");
  600 + }
466 } 601 }
467 602
468 void 603 void
469 QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item) 604 QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item)
470 { 605 {
471 - assertArray();  
472 - return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->insertItem(at, item); 606 + if (isArray())
  607 + {
  608 + dynamic_cast<QPDF_Array*>(m->obj.getPointer())->insertItem(at, item);
  609 + }
  610 + else
  611 + {
  612 + typeWarning("array", "ignoring attempt to insert item");
  613 + QTC::TC("qpdf", "QPDFObjectHandle array ignoring insert item");
  614 + }
473 } 615 }
474 616
475 void 617 void
476 QPDFObjectHandle::appendItem(QPDFObjectHandle const& item) 618 QPDFObjectHandle::appendItem(QPDFObjectHandle const& item)
477 { 619 {
478 - assertArray();  
479 - return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->appendItem(item); 620 + if (isArray())
  621 + {
  622 + dynamic_cast<QPDF_Array*>(m->obj.getPointer())->appendItem(item);
  623 + }
  624 + else
  625 + {
  626 + typeWarning("array", "ignoring attempt to append item");
  627 + QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item");
  628 + }
480 } 629 }
481 630
482 void 631 void
483 QPDFObjectHandle::eraseItem(int at) 632 QPDFObjectHandle::eraseItem(int at)
484 { 633 {
485 - assertArray();  
486 - return dynamic_cast<QPDF_Array*>(m->obj.getPointer())->eraseItem(at); 634 + if (isArray() && (at < getArrayNItems()) && (at >= 0))
  635 + {
  636 + dynamic_cast<QPDF_Array*>(m->obj.getPointer())->eraseItem(at);
  637 + }
  638 + else
  639 + {
  640 + if (isArray())
  641 + {
  642 + objectWarning("ignoring attempt to erase out of bounds array item");
  643 + QTC::TC("qpdf", "QPDFObjectHandle erase array bounds");
  644 + }
  645 + else
  646 + {
  647 + typeWarning("array", "ignoring attempt to erase item");
  648 + QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item");
  649 + }
  650 + }
487 } 651 }
488 652
489 // Dictionary accessors 653 // Dictionary accessors
@@ -491,29 +655,79 @@ QPDFObjectHandle::eraseItem(int at) @@ -491,29 +655,79 @@ QPDFObjectHandle::eraseItem(int at)
491 bool 655 bool
492 QPDFObjectHandle::hasKey(std::string const& key) 656 QPDFObjectHandle::hasKey(std::string const& key)
493 { 657 {
494 - assertDictionary();  
495 - return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->hasKey(key); 658 + if (isDictionary())
  659 + {
  660 + return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->hasKey(key);
  661 + }
  662 + else
  663 + {
  664 + typeWarning("dictionary",
  665 + "returning false for a key containment request");
  666 + QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey");
  667 + return false;
  668 + }
496 } 669 }
497 670
498 QPDFObjectHandle 671 QPDFObjectHandle
499 QPDFObjectHandle::getKey(std::string const& key) 672 QPDFObjectHandle::getKey(std::string const& key)
500 { 673 {
501 - assertDictionary();  
502 - return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getKey(key); 674 + QPDFObjectHandle result;
  675 + if (isDictionary())
  676 + {
  677 + result = dynamic_cast<QPDF_Dictionary*>(
  678 + m->obj.getPointer())->getKey(key);
  679 + }
  680 + else
  681 + {
  682 + typeWarning(
  683 + "dictionary", "returning null for attempted key retrieval");
  684 + QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey");
  685 + result = newNull();
  686 + QPDF* qpdf = 0;
  687 + std::string description;
  688 + if (this->m->obj->getDescription(qpdf, description))
  689 + {
  690 + result.setObjectDescription(
  691 + qpdf,
  692 + description +
  693 + " -> null returned from getting key " +
  694 + key + " from non-Dictionary");
  695 + }
  696 + }
  697 + return result;
503 } 698 }
504 699
505 std::set<std::string> 700 std::set<std::string>
506 QPDFObjectHandle::getKeys() 701 QPDFObjectHandle::getKeys()
507 { 702 {
508 - assertDictionary();  
509 - return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getKeys(); 703 + std::set<std::string> result;
  704 + if (isDictionary())
  705 + {
  706 + result = dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getKeys();
  707 + }
  708 + else
  709 + {
  710 + typeWarning("dictionary", "treating as empty");
  711 + QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
  712 + }
  713 + return result;
510 } 714 }
511 715
512 std::map<std::string, QPDFObjectHandle> 716 std::map<std::string, QPDFObjectHandle>
513 QPDFObjectHandle::getDictAsMap() 717 QPDFObjectHandle::getDictAsMap()
514 { 718 {
515 - assertDictionary();  
516 - return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->getAsMap(); 719 + std::map<std::string, QPDFObjectHandle> result;
  720 + if (isDictionary())
  721 + {
  722 + result = dynamic_cast<QPDF_Dictionary*>(
  723 + m->obj.getPointer())->getAsMap();
  724 + }
  725 + else
  726 + {
  727 + typeWarning("dictionary", "treating as empty");
  728 + QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap");
  729 + }
  730 + return result;
517 } 731 }
518 732
519 // Array and Name accessors 733 // Array and Name accessors
@@ -551,27 +765,48 @@ QPDFObjectHandle::getOwningQPDF() @@ -551,27 +765,48 @@ QPDFObjectHandle::getOwningQPDF()
551 765
552 void 766 void
553 QPDFObjectHandle::replaceKey(std::string const& key, 767 QPDFObjectHandle::replaceKey(std::string const& key,
554 - QPDFObjectHandle const& value) 768 + QPDFObjectHandle value)
555 { 769 {
556 - assertDictionary();  
557 - return dynamic_cast<QPDF_Dictionary*>(  
558 - m->obj.getPointer())->replaceKey(key, value); 770 + if (isDictionary())
  771 + {
  772 + dynamic_cast<QPDF_Dictionary*>(
  773 + m->obj.getPointer())->replaceKey(key, value);
  774 + }
  775 + else
  776 + {
  777 + typeWarning("dictionary", "ignoring key replacement request");
  778 + QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");
  779 + }
559 } 780 }
560 781
561 void 782 void
562 QPDFObjectHandle::removeKey(std::string const& key) 783 QPDFObjectHandle::removeKey(std::string const& key)
563 { 784 {
564 - assertDictionary();  
565 - return dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->removeKey(key); 785 + if (isDictionary())
  786 + {
  787 + dynamic_cast<QPDF_Dictionary*>(m->obj.getPointer())->removeKey(key);
  788 + }
  789 + else
  790 + {
  791 + typeWarning("dictionary", "ignoring key removal request");
  792 + QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey");
  793 + }
566 } 794 }
567 795
568 void 796 void
569 QPDFObjectHandle::replaceOrRemoveKey(std::string const& key, 797 QPDFObjectHandle::replaceOrRemoveKey(std::string const& key,
570 QPDFObjectHandle value) 798 QPDFObjectHandle value)
571 { 799 {
572 - assertDictionary();  
573 - return dynamic_cast<QPDF_Dictionary*>(  
574 - m->obj.getPointer())->replaceOrRemoveKey(key, value); 800 + if (isDictionary())
  801 + {
  802 + dynamic_cast<QPDF_Dictionary*>(
  803 + m->obj.getPointer())->replaceOrRemoveKey(key, value);
  804 + }
  805 + else
  806 + {
  807 + typeWarning("dictionary", "ignoring key removal/replacement request");
  808 + QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removereplace");
  809 + }
575 } 810 }
576 811
577 // Stream accessors 812 // Stream accessors
@@ -1173,35 +1408,45 @@ QPDFObjectHandle::parseInternal(PointerHolder&lt;InputSource&gt; input, @@ -1173,35 +1408,45 @@ QPDFObjectHandle::parseInternal(PointerHolder&lt;InputSource&gt; input,
1173 std::vector<parser_state_e> state_stack; 1408 std::vector<parser_state_e> state_stack;
1174 state_stack.push_back(st_top); 1409 state_stack.push_back(st_top);
1175 std::vector<qpdf_offset_t> offset_stack; 1410 std::vector<qpdf_offset_t> offset_stack;
1176 - offset_stack.push_back(input->tell()); 1411 + qpdf_offset_t offset = input->tell();
  1412 + offset_stack.push_back(offset);
1177 bool done = false; 1413 bool done = false;
1178 while (! done) 1414 while (! done)
1179 { 1415 {
1180 std::vector<QPDFObjectHandle>& olist = olist_stack.back(); 1416 std::vector<QPDFObjectHandle>& olist = olist_stack.back();
1181 parser_state_e state = state_stack.back(); 1417 parser_state_e state = state_stack.back();
1182 - qpdf_offset_t offset = offset_stack.back(); 1418 + offset = offset_stack.back();
1183 1419
1184 object = QPDFObjectHandle(); 1420 object = QPDFObjectHandle();
1185 1421
1186 QPDFTokenizer::Token token = 1422 QPDFTokenizer::Token token =
1187 - tokenizer.readToken(input, object_description); 1423 + tokenizer.readToken(input, object_description, true);
1188 1424
1189 switch (token.getType()) 1425 switch (token.getType())
1190 { 1426 {
1191 case QPDFTokenizer::tt_eof: 1427 case QPDFTokenizer::tt_eof:
1192 - if (content_stream) 1428 + if (! content_stream)
1193 { 1429 {
1194 - state = st_eof;  
1195 - }  
1196 - else  
1197 - {  
1198 - // When not in content stream mode, EOF is tt_bad and  
1199 - // throws an exception before we get here.  
1200 - throw std::logic_error(  
1201 - "EOF received while not in content stream mode"); 1430 + QTC::TC("qpdf", "QPDFObjectHandle eof in parseInternal");
  1431 + warn(context,
  1432 + QPDFExc(qpdf_e_damaged_pdf, input->getName(),
  1433 + object_description,
  1434 + input->getLastOffset(),
  1435 + "unexpected EOF"));
1202 } 1436 }
  1437 + state = st_eof;
1203 break; 1438 break;
1204 1439
  1440 + case QPDFTokenizer::tt_bad:
  1441 + QTC::TC("qpdf", "QPDFObjectHandle bad token in parse");
  1442 + warn(context,
  1443 + QPDFExc(qpdf_e_damaged_pdf, input->getName(),
  1444 + object_description,
  1445 + input->getLastOffset(),
  1446 + token.getErrorMessage()));
  1447 + object = newNull();
  1448 + break;
  1449 +
1205 case QPDFTokenizer::tt_brace_open: 1450 case QPDFTokenizer::tt_brace_open:
1206 case QPDFTokenizer::tt_brace_close: 1451 case QPDFTokenizer::tt_brace_close:
1207 QTC::TC("qpdf", "QPDFObjectHandle bad brace"); 1452 QTC::TC("qpdf", "QPDFObjectHandle bad brace");
@@ -1375,11 +1620,19 @@ QPDFObjectHandle::parseInternal(PointerHolder&lt;InputSource&gt; input, @@ -1375,11 +1620,19 @@ QPDFObjectHandle::parseInternal(PointerHolder&lt;InputSource&gt; input,
1375 "parse error while reading object")); 1620 "parse error while reading object"));
1376 } 1621 }
1377 done = true; 1622 done = true;
1378 - // Leave object uninitialized to indicate EOF 1623 + // In content stream mode, leave object uninitialized to
  1624 + // indicate EOF
  1625 + if (! content_stream)
  1626 + {
  1627 + object = newNull();
  1628 + }
1379 break; 1629 break;
1380 1630
1381 case st_dictionary: 1631 case st_dictionary:
1382 case st_array: 1632 case st_array:
  1633 + setObjectDescriptionFromInput(
  1634 + object, context, object_description, input,
  1635 + input->getLastOffset());
1383 olist.push_back(object); 1636 olist.push_back(object);
1384 break; 1637 break;
1385 1638
@@ -1402,6 +1655,8 @@ QPDFObjectHandle::parseInternal(PointerHolder&lt;InputSource&gt; input, @@ -1402,6 +1655,8 @@ QPDFObjectHandle::parseInternal(PointerHolder&lt;InputSource&gt; input,
1402 if (old_state == st_array) 1655 if (old_state == st_array)
1403 { 1656 {
1404 object = newArray(olist); 1657 object = newArray(olist);
  1658 + setObjectDescriptionFromInput(
  1659 + object, context, object_description, input, offset);
1405 } 1660 }
1406 else if (old_state == st_dictionary) 1661 else if (old_state == st_dictionary)
1407 { 1662 {
@@ -1458,6 +1713,8 @@ QPDFObjectHandle::parseInternal(PointerHolder&lt;InputSource&gt; input, @@ -1458,6 +1713,8 @@ QPDFObjectHandle::parseInternal(PointerHolder&lt;InputSource&gt; input,
1458 "dictionary ended prematurely; " 1713 "dictionary ended prematurely; "
1459 "using null as value for last key")); 1714 "using null as value for last key"));
1460 val = newNull(); 1715 val = newNull();
  1716 + setObjectDescriptionFromInput(
  1717 + val, context, object_description, input, offset);
1461 } 1718 }
1462 else 1719 else
1463 { 1720 {
@@ -1466,6 +1723,8 @@ QPDFObjectHandle::parseInternal(PointerHolder&lt;InputSource&gt; input, @@ -1466,6 +1723,8 @@ QPDFObjectHandle::parseInternal(PointerHolder&lt;InputSource&gt; input,
1466 dict[key_obj.getName()] = val; 1723 dict[key_obj.getName()] = val;
1467 } 1724 }
1468 object = newDictionary(dict); 1725 object = newDictionary(dict);
  1726 + setObjectDescriptionFromInput(
  1727 + object, context, object_description, input, offset);
1469 } 1728 }
1470 olist_stack.pop_back(); 1729 olist_stack.pop_back();
1471 offset_stack.pop_back(); 1730 offset_stack.pop_back();
@@ -1480,6 +1739,8 @@ QPDFObjectHandle::parseInternal(PointerHolder&lt;InputSource&gt; input, @@ -1480,6 +1739,8 @@ QPDFObjectHandle::parseInternal(PointerHolder&lt;InputSource&gt; input,
1480 } 1739 }
1481 } 1740 }
1482 1741
  1742 + setObjectDescriptionFromInput(
  1743 + object, context, object_description, input, offset);
1483 return object; 1744 return object;
1484 } 1745 }
1485 1746
@@ -1635,6 +1896,26 @@ QPDFObjectHandle::newReserved(QPDF* qpdf) @@ -1635,6 +1896,26 @@ QPDFObjectHandle::newReserved(QPDF* qpdf)
1635 return result; 1896 return result;
1636 } 1897 }
1637 1898
  1899 +void
  1900 +QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf,
  1901 + std::string const& object_description)
  1902 +{
  1903 + if (isInitialized() && this->m->obj.getPointer())
  1904 + {
  1905 + this->m->obj->setDescription(owning_qpdf, object_description);
  1906 + }
  1907 +}
  1908 +
  1909 +bool
  1910 +QPDFObjectHandle::hasObjectDescription()
  1911 +{
  1912 + if (isInitialized() && this->m->obj.getPointer())
  1913 + {
  1914 + return this->m->obj->hasDescription();
  1915 + }
  1916 + return false;
  1917 +}
  1918 +
1638 QPDFObjectHandle 1919 QPDFObjectHandle
1639 QPDFObjectHandle::shallowCopy() 1920 QPDFObjectHandle::shallowCopy()
1640 { 1921 {
@@ -1793,85 +2074,127 @@ QPDFObjectHandle::assertInitialized() const @@ -1793,85 +2074,127 @@ QPDFObjectHandle::assertInitialized() const
1793 } 2074 }
1794 2075
1795 void 2076 void
1796 -QPDFObjectHandle::assertType(char const* type_name, bool istype) const 2077 +QPDFObjectHandle::typeWarning(char const* expected_type,
  2078 + std::string const& warning)
  2079 +{
  2080 + QPDF* context = 0;
  2081 + std::string description;
  2082 + if (this->m->obj->getDescription(context, description))
  2083 + {
  2084 + warn(context,
  2085 + QPDFExc(
  2086 + qpdf_e_damaged_pdf,
  2087 + "", description, 0,
  2088 + std::string("operation for ") + expected_type +
  2089 + " attempted on object of type " +
  2090 + getTypeName() + ": " + warning));
  2091 + }
  2092 + else
  2093 + {
  2094 + assertType(expected_type, false);
  2095 + }
  2096 +}
  2097 +
  2098 +void
  2099 +QPDFObjectHandle::objectWarning(std::string const& warning)
  2100 +{
  2101 + QPDF* context = 0;
  2102 + std::string description;
  2103 + if (this->m->obj->getDescription(context, description))
  2104 + {
  2105 + warn(context,
  2106 + QPDFExc(
  2107 + qpdf_e_damaged_pdf,
  2108 + "", description, 0,
  2109 + warning));
  2110 + }
  2111 + else
  2112 + {
  2113 + throw std::logic_error(warning);
  2114 + }
  2115 +}
  2116 +
  2117 +void
  2118 +QPDFObjectHandle::assertType(char const* type_name, bool istype)
1797 { 2119 {
1798 if (! istype) 2120 if (! istype)
1799 { 2121 {
1800 throw std::logic_error(std::string("operation for ") + type_name + 2122 throw std::logic_error(std::string("operation for ") + type_name +
1801 - " object attempted on object of wrong type"); 2123 + " attempted on object of type " +
  2124 + getTypeName());
1802 } 2125 }
1803 } 2126 }
1804 2127
1805 void 2128 void
1806 QPDFObjectHandle::assertNull() 2129 QPDFObjectHandle::assertNull()
1807 { 2130 {
1808 - assertType("Null", isNull()); 2131 + assertType("null", isNull());
1809 } 2132 }
1810 2133
1811 void 2134 void
1812 QPDFObjectHandle::assertBool() 2135 QPDFObjectHandle::assertBool()
1813 { 2136 {
1814 - assertType("Boolean", isBool()); 2137 + assertType("boolean", isBool());
1815 } 2138 }
1816 2139
1817 void 2140 void
1818 QPDFObjectHandle::assertInteger() 2141 QPDFObjectHandle::assertInteger()
1819 { 2142 {
1820 - assertType("Integer", isInteger()); 2143 + assertType("integer", isInteger());
1821 } 2144 }
1822 2145
1823 void 2146 void
1824 QPDFObjectHandle::assertReal() 2147 QPDFObjectHandle::assertReal()
1825 { 2148 {
1826 - assertType("Real", isReal()); 2149 + assertType("real", isReal());
1827 } 2150 }
1828 2151
1829 void 2152 void
1830 QPDFObjectHandle::assertName() 2153 QPDFObjectHandle::assertName()
1831 { 2154 {
1832 - assertType("Name", isName()); 2155 + assertType("name", isName());
1833 } 2156 }
1834 2157
1835 void 2158 void
1836 QPDFObjectHandle::assertString() 2159 QPDFObjectHandle::assertString()
1837 { 2160 {
1838 - assertType("String", isString()); 2161 + assertType("string", isString());
1839 } 2162 }
1840 2163
1841 void 2164 void
1842 QPDFObjectHandle::assertOperator() 2165 QPDFObjectHandle::assertOperator()
1843 { 2166 {
1844 - assertType("Operator", isOperator()); 2167 + assertType("operator", isOperator());
1845 } 2168 }
1846 2169
1847 void 2170 void
1848 QPDFObjectHandle::assertInlineImage() 2171 QPDFObjectHandle::assertInlineImage()
1849 { 2172 {
1850 - assertType("InlineImage", isInlineImage()); 2173 + assertType("inlineimage", isInlineImage());
1851 } 2174 }
1852 2175
1853 void 2176 void
1854 QPDFObjectHandle::assertArray() 2177 QPDFObjectHandle::assertArray()
1855 { 2178 {
1856 - assertType("Array", isArray()); 2179 + assertType("array", isArray());
1857 } 2180 }
1858 2181
1859 void 2182 void
1860 QPDFObjectHandle::assertDictionary() 2183 QPDFObjectHandle::assertDictionary()
1861 { 2184 {
1862 - assertType("Dictionary", isDictionary()); 2185 + assertType("dictionary", isDictionary());
1863 } 2186 }
1864 2187
1865 void 2188 void
1866 QPDFObjectHandle::assertStream() 2189 QPDFObjectHandle::assertStream()
1867 { 2190 {
1868 - assertType("Stream", isStream()); 2191 + assertType("stream", isStream());
1869 } 2192 }
1870 2193
1871 void 2194 void
1872 QPDFObjectHandle::assertReserved() 2195 QPDFObjectHandle::assertReserved()
1873 { 2196 {
1874 - assertType("Reserved", isReserved()); 2197 + assertType("reserved", isReserved());
1875 } 2198 }
1876 2199
1877 void 2200 void
@@ -1887,13 +2210,13 @@ QPDFObjectHandle::assertIndirect() @@ -1887,13 +2210,13 @@ QPDFObjectHandle::assertIndirect()
1887 void 2210 void
1888 QPDFObjectHandle::assertScalar() 2211 QPDFObjectHandle::assertScalar()
1889 { 2212 {
1890 - assertType("Scalar", isScalar()); 2213 + assertType("scalar", isScalar());
1891 } 2214 }
1892 2215
1893 void 2216 void
1894 QPDFObjectHandle::assertNumber() 2217 QPDFObjectHandle::assertNumber()
1895 { 2218 {
1896 - assertType("Number", isNumber()); 2219 + assertType("number", isNumber());
1897 } 2220 }
1898 2221
1899 bool 2222 bool
@@ -1928,7 +2251,8 @@ QPDFObjectHandle::dereference() @@ -1928,7 +2251,8 @@ QPDFObjectHandle::dereference()
1928 this->m->qpdf, this->m->objid, this->m->generation); 2251 this->m->qpdf, this->m->objid, this->m->generation);
1929 if (obj.getPointer() == 0) 2252 if (obj.getPointer() == 0)
1930 { 2253 {
1931 - QTC::TC("qpdf", "QPDFObjectHandle indirect to unknown"); 2254 + // QPDF::resolve never returns an uninitialized object, but
  2255 + // check just in case.
1932 this->m->obj = new QPDF_Null(); 2256 this->m->obj = new QPDF_Null();
1933 } 2257 }
1934 else if (dynamic_cast<QPDF_Reserved*>(obj.getPointer())) 2258 else if (dynamic_cast<QPDF_Reserved*>(obj.getPointer()))
libqpdf/QPDFTokenizer.cc
@@ -640,7 +640,9 @@ QPDFTokenizer::readToken(PointerHolder&lt;InputSource&gt; input, @@ -640,7 +640,9 @@ QPDFTokenizer::readToken(PointerHolder&lt;InputSource&gt; input,
640 presented_eof = true; 640 presented_eof = true;
641 if ((this->m->type == tt_eof) && (! this->m->allow_eof)) 641 if ((this->m->type == tt_eof) && (! this->m->allow_eof))
642 { 642 {
643 - QTC::TC("qpdf", "QPDFTokenizer EOF when not allowed"); 643 + // Nothing in the qpdf library calls readToken
  644 + // without allowEOF anymore, so this case is not
  645 + // exercised.
644 this->m->type = tt_bad; 646 this->m->type = tt_bad;
645 this->m->error_message = "unexpected EOF"; 647 this->m->error_message = "unexpected EOF";
646 offset = input->getLastOffset(); 648 offset = input->getLastOffset();
@@ -677,7 +679,10 @@ QPDFTokenizer::readToken(PointerHolder&lt;InputSource&gt; input, @@ -677,7 +679,10 @@ QPDFTokenizer::readToken(PointerHolder&lt;InputSource&gt; input,
677 input->unreadCh(char_to_unread); 679 input->unreadCh(char_to_unread);
678 } 680 }
679 681
680 - input->setLastOffset(offset); 682 + if (token.getType() != tt_eof)
  683 + {
  684 + input->setLastOffset(offset);
  685 + }
681 686
682 if (token.getType() == tt_bad) 687 if (token.getType() == tt_bad)
683 { 688 {
libqpdf/QPDF_Array.cc
1 #include <qpdf/QPDF_Array.hh> 1 #include <qpdf/QPDF_Array.hh>
  2 +#include <qpdf/QUtil.hh>
2 #include <stdexcept> 3 #include <stdexcept>
3 4
4 QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& items) : 5 QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& items) :
@@ -46,6 +47,12 @@ QPDF_Array::getTypeName() const @@ -46,6 +47,12 @@ QPDF_Array::getTypeName() const
46 return "array"; 47 return "array";
47 } 48 }
48 49
  50 +void
  51 +QPDF_Array::setDescription(QPDF* qpdf, std::string const& description)
  52 +{
  53 + this->QPDFObject::setDescription(qpdf, description);
  54 +}
  55 +
49 int 56 int
50 QPDF_Array::getNItems() const 57 QPDF_Array::getNItems() const
51 { 58 {
libqpdf/QPDF_Dictionary.cc
@@ -51,6 +51,12 @@ QPDF_Dictionary::getTypeName() const @@ -51,6 +51,12 @@ QPDF_Dictionary::getTypeName() const
51 return "dictionary"; 51 return "dictionary";
52 } 52 }
53 53
  54 +void
  55 +QPDF_Dictionary::setDescription(QPDF* qpdf, std::string const& description)
  56 +{
  57 + this->QPDFObject::setDescription(qpdf, description);
  58 +}
  59 +
54 bool 60 bool
55 QPDF_Dictionary::hasKey(std::string const& key) 61 QPDF_Dictionary::hasKey(std::string const& key)
56 { 62 {
@@ -70,7 +76,15 @@ QPDF_Dictionary::getKey(std::string const&amp; key) @@ -70,7 +76,15 @@ QPDF_Dictionary::getKey(std::string const&amp; key)
70 } 76 }
71 else 77 else
72 { 78 {
73 - return QPDFObjectHandle::newNull(); 79 + QPDFObjectHandle null = QPDFObjectHandle::newNull();
  80 + QPDF* qpdf = 0;
  81 + std::string description;
  82 + if (getDescription(qpdf, description))
  83 + {
  84 + null.setObjectDescription(
  85 + qpdf, description + " -> dictionary key " + key);
  86 + }
  87 + return null;
74 } 88 }
75 } 89 }
76 90
@@ -93,13 +107,12 @@ QPDF_Dictionary::getKeys() @@ -93,13 +107,12 @@ QPDF_Dictionary::getKeys()
93 std::map<std::string, QPDFObjectHandle> const& 107 std::map<std::string, QPDFObjectHandle> const&
94 QPDF_Dictionary::getAsMap() const 108 QPDF_Dictionary::getAsMap() const
95 { 109 {
96 -  
97 return this->items; 110 return this->items;
98 } 111 }
99 112
100 void 113 void
101 QPDF_Dictionary::replaceKey(std::string const& key, 114 QPDF_Dictionary::replaceKey(std::string const& key,
102 - QPDFObjectHandle const& value) 115 + QPDFObjectHandle value)
103 { 116 {
104 // add or replace value 117 // add or replace value
105 this->items[key] = value; 118 this->items[key] = value;
libqpdf/QPDF_Stream.cc
@@ -39,6 +39,7 @@ QPDF_Stream::QPDF_Stream(QPDF* qpdf, int objid, int generation, @@ -39,6 +39,7 @@ QPDF_Stream::QPDF_Stream(QPDF* qpdf, int objid, int generation,
39 "stream object instantiated with non-dictionary " 39 "stream object instantiated with non-dictionary "
40 "object for dictionary"); 40 "object for dictionary");
41 } 41 }
  42 + setStreamDescription();
42 } 43 }
43 44
44 QPDF_Stream::~QPDF_Stream() 45 QPDF_Stream::~QPDF_Stream()
@@ -85,6 +86,35 @@ QPDF_Stream::getTypeName() const @@ -85,6 +86,35 @@ QPDF_Stream::getTypeName() const
85 return "stream"; 86 return "stream";
86 } 87 }
87 88
  89 +void
  90 +QPDF_Stream::setDescription(QPDF* qpdf, std::string const& description)
  91 +{
  92 + this->QPDFObject::setDescription(qpdf, description);
  93 + setDictDescription();
  94 +}
  95 +
  96 +void
  97 +QPDF_Stream::setStreamDescription()
  98 +{
  99 + setDescription(
  100 + this->qpdf,
  101 + "stream object " + QUtil::int_to_string(this->objid) + " " +
  102 + QUtil::int_to_string(this->generation));
  103 +}
  104 +
  105 +void
  106 +QPDF_Stream::setDictDescription()
  107 +{
  108 + QPDF* qpdf = 0;
  109 + std::string description;
  110 + if ((! this->stream_dict.hasObjectDescription()) &&
  111 + getDescription(qpdf, description))
  112 + {
  113 + this->stream_dict.setObjectDescription(
  114 + qpdf, description + " -> stream dictionary");
  115 + }
  116 +}
  117 +
88 QPDFObjectHandle 118 QPDFObjectHandle
89 QPDF_Stream::getDict() const 119 QPDF_Stream::getDict() const
90 { 120 {
@@ -688,6 +718,7 @@ void @@ -688,6 +718,7 @@ void
688 QPDF_Stream::replaceDict(QPDFObjectHandle new_dict) 718 QPDF_Stream::replaceDict(QPDFObjectHandle new_dict)
689 { 719 {
690 this->stream_dict = new_dict; 720 this->stream_dict = new_dict;
  721 + setDictDescription();
691 QPDFObjectHandle length_obj = new_dict.getKey("/Length"); 722 QPDFObjectHandle length_obj = new_dict.getKey("/Length");
692 if (length_obj.isInteger()) 723 if (length_obj.isInteger())
693 { 724 {
libqpdf/QPDF_linearization.cc
@@ -121,10 +121,10 @@ QPDF::isLinearized() @@ -121,10 +121,10 @@ QPDF::isLinearized()
121 ++p; 121 ++p;
122 } 122 }
123 123
124 - QPDFTokenizer::Token t1 = readToken(this->m->file, true);  
125 - QPDFTokenizer::Token t2 = readToken(this->m->file, true);  
126 - QPDFTokenizer::Token t3 = readToken(this->m->file, true);  
127 - QPDFTokenizer::Token t4 = readToken(this->m->file, true); 124 + QPDFTokenizer::Token t1 = readToken(this->m->file);
  125 + QPDFTokenizer::Token t2 = readToken(this->m->file);
  126 + QPDFTokenizer::Token t3 = readToken(this->m->file);
  127 + QPDFTokenizer::Token t4 = readToken(this->m->file);
128 if ((t1.getType() == QPDFTokenizer::tt_integer) && 128 if ((t1.getType() == QPDFTokenizer::tt_integer) &&
129 (t2.getType() == QPDFTokenizer::tt_integer) && 129 (t2.getType() == QPDFTokenizer::tt_integer) &&
130 (t3 == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "obj")) && 130 (t3 == QPDFTokenizer::Token(QPDFTokenizer::tt_word, "obj")) &&
libqpdf/qpdf/QPDF_Array.hh
@@ -14,6 +14,7 @@ class QPDF_Array: public QPDFObject @@ -14,6 +14,7 @@ class QPDF_Array: public QPDFObject
14 virtual std::string unparse(); 14 virtual std::string unparse();
15 virtual QPDFObject::object_type_e getTypeCode() const; 15 virtual QPDFObject::object_type_e getTypeCode() const;
16 virtual char const* getTypeName() const; 16 virtual char const* getTypeName() const;
  17 + virtual void setDescription(QPDF*, std::string const&);
17 18
18 int getNItems() const; 19 int getNItems() const;
19 QPDFObjectHandle getItem(int n) const; 20 QPDFObjectHandle getItem(int n) const;
libqpdf/qpdf/QPDF_Dictionary.hh
@@ -16,6 +16,7 @@ class QPDF_Dictionary: public QPDFObject @@ -16,6 +16,7 @@ class QPDF_Dictionary: public QPDFObject
16 virtual std::string unparse(); 16 virtual std::string unparse();
17 virtual QPDFObject::object_type_e getTypeCode() const; 17 virtual QPDFObject::object_type_e getTypeCode() const;
18 virtual char const* getTypeName() const; 18 virtual char const* getTypeName() const;
  19 + virtual void setDescription(QPDF*, std::string const&);
19 20
20 // hasKey() and getKeys() treat keys with null values as if they 21 // hasKey() and getKeys() treat keys with null values as if they
21 // aren't there. getKey() returns null for the value of a 22 // aren't there. getKey() returns null for the value of a
@@ -26,7 +27,7 @@ class QPDF_Dictionary: public QPDFObject @@ -26,7 +27,7 @@ class QPDF_Dictionary: public QPDFObject
26 std::map<std::string, QPDFObjectHandle> const& getAsMap() const; 27 std::map<std::string, QPDFObjectHandle> const& getAsMap() const;
27 28
28 // Replace value of key, adding it if it does not exist 29 // Replace value of key, adding it if it does not exist
29 - void replaceKey(std::string const& key, QPDFObjectHandle const&); 30 + void replaceKey(std::string const& key, QPDFObjectHandle);
30 // Remove key, doing nothing if key does not exist 31 // Remove key, doing nothing if key does not exist
31 void removeKey(std::string const& key); 32 void removeKey(std::string const& key);
32 // If object is null, replace key; otherwise, remove key 33 // If object is null, replace key; otherwise, remove key
libqpdf/qpdf/QPDF_Stream.hh
@@ -19,6 +19,7 @@ class QPDF_Stream: public QPDFObject @@ -19,6 +19,7 @@ class QPDF_Stream: public QPDFObject
19 virtual std::string unparse(); 19 virtual std::string unparse();
20 virtual QPDFObject::object_type_e getTypeCode() const; 20 virtual QPDFObject::object_type_e getTypeCode() const;
21 virtual char const* getTypeName() const; 21 virtual char const* getTypeName() const;
  22 + virtual void setDescription(QPDF*, std::string const&);
22 QPDFObjectHandle getDict() const; 23 QPDFObjectHandle getDict() const;
23 bool isDataModified() const; 24 bool isDataModified() const;
24 25
@@ -66,6 +67,8 @@ class QPDF_Stream: public QPDFObject @@ -66,6 +67,8 @@ class QPDF_Stream: public QPDFObject
66 int& colors, int& bits_per_component, 67 int& colors, int& bits_per_component,
67 bool& early_code_change); 68 bool& early_code_change);
68 void warn(QPDFExc const& e); 69 void warn(QPDFExc const& e);
  70 + void setDictDescription();
  71 + void setStreamDescription();
69 72
70 QPDF* qpdf; 73 QPDF* qpdf;
71 int objid; 74 int objid;
qpdf/qpdf.testcov
@@ -105,7 +105,6 @@ QPDF reconstructed xref table 0 @@ -105,7 +105,6 @@ QPDF reconstructed xref table 0
105 QPDF recovered in readObjectAtOffset 0 105 QPDF recovered in readObjectAtOffset 0
106 QPDF recovered stream length 0 106 QPDF recovered stream length 0
107 QPDF found wrong endstream in recovery 0 107 QPDF found wrong endstream in recovery 0
108 -QPDFObjectHandle indirect to unknown 0  
109 QPDF_Stream pipeStreamData with null pipeline 0 108 QPDF_Stream pipeStreamData with null pipeline 0
110 QPDFWriter not recompressing /FlateDecode 0 109 QPDFWriter not recompressing /FlateDecode 0
111 QPDF_encryption xref stream from encrypted file 0 110 QPDF_encryption xref stream from encrypted file 0
@@ -300,10 +299,37 @@ qpdf-c called qpdf_set_compress_streams 0 @@ -300,10 +299,37 @@ qpdf-c called qpdf_set_compress_streams 0
300 qpdf-c called qpdf_set_preserve_unreferenced_objects 0 299 qpdf-c called qpdf_set_preserve_unreferenced_objects 0
301 qpdf-c called qpdf_set_newline_before_endstream 0 300 qpdf-c called qpdf_set_newline_before_endstream 0
302 QPDF_Stream TIFF predictor 0 301 QPDF_Stream TIFF predictor 0
303 -QPDFTokenizer EOF when not allowed 0  
304 QPDFTokenizer inline image at EOF 0 302 QPDFTokenizer inline image at EOF 0
305 Pl_QPDFTokenizer found ID 0 303 Pl_QPDFTokenizer found ID 0
306 QPDFObjectHandle non-stream in stream array 0 304 QPDFObjectHandle non-stream in stream array 0
307 QPDFObjectHandle coalesce called on stream 0 305 QPDFObjectHandle coalesce called on stream 0
308 QPDFObjectHandle coalesce provide stream data 0 306 QPDFObjectHandle coalesce provide stream data 0
309 QPDF_Stream bad token at end during normalize 0 307 QPDF_Stream bad token at end during normalize 0
  308 +QPDFObjectHandle bad token in parse 0
  309 +QPDFObjectHandle eof in parseInternal 0
  310 +QPDFObjectHandle array bounds 0
  311 +QPDFObjectHandle boolean returning false 0
  312 +QPDFObjectHandle integer returning 0 0
  313 +QPDFObjectHandle real returning 0.0 0
  314 +QPDFObjectHandle name returning dummy name 0
  315 +QPDFObjectHandle string returning empty string 0
  316 +QPDFObjectHandle string returning empty utf8 0
  317 +QPDFObjectHandle operator returning fake value 0
  318 +QPDFObjectHandle inlineimage returning empty data 0
  319 +QPDFObjectHandle array treating as empty 0
  320 +QPDFObjectHandle array null for non-array 0
  321 +QPDFObjectHandle array treating as empty vector 0
  322 +QPDFObjectHandle array ignoring set item 0
  323 +QPDFObjectHandle array ignoring replace items 0
  324 +QPDFObjectHandle array ignoring insert item 0
  325 +QPDFObjectHandle array ignoring append item 0
  326 +QPDFObjectHandle array ignoring erase item 0
  327 +QPDFObjectHandle dictionary false for hasKey 0
  328 +QPDFObjectHandle dictionary null for getKey 0
  329 +QPDFObjectHandle dictionary empty set for getKeys 0
  330 +QPDFObjectHandle dictionary empty map for asMap 0
  331 +QPDFObjectHandle dictionary ignoring replaceKey 0
  332 +QPDFObjectHandle dictionary ignoring removeKey 0
  333 +QPDFObjectHandle dictionary ignoring removereplace 0
  334 +QPDFObjectHandle numeric non-numeric 0
  335 +QPDFObjectHandle erase array bounds 0
qpdf/qtest/qpdf.test
@@ -212,7 +212,7 @@ my @bug_tests = ( @@ -212,7 +212,7 @@ my @bug_tests = (
212 ["99", "object 0", 2], 212 ["99", "object 0", 2],
213 ["99b", "object 0", 2], 213 ["99b", "object 0", 2],
214 ["100", "xref reconstruction loop", 2], 214 ["100", "xref reconstruction loop", 2],
215 - ["101", "resolve for exception text", 2], 215 + ["101", "resolve for exception text", 3],
216 ["117", "other infinite loop", 2], 216 ["117", "other infinite loop", 2],
217 ["118", "other infinite loop", 2], 217 ["118", "other infinite loop", 2],
218 ["119", "other infinite loop", 3], 218 ["119", "other infinite loop", 3],
@@ -736,6 +736,33 @@ $td-&gt;runtest(&quot;stream with tiff predictor&quot;, @@ -736,6 +736,33 @@ $td-&gt;runtest(&quot;stream with tiff predictor&quot;,
736 736
737 show_ntests(); 737 show_ntests();
738 # ---------- 738 # ----------
  739 +$td->notify("--- Type checks ---");
  740 +$n_tests += 4;
  741 +# Whenever object-types.pdf is edited, object-types-os.pdf should be
  742 +# regenerated.
  743 +$td->runtest("ensure object-types-os is up-to-date",
  744 + {$td->COMMAND =>
  745 + "qpdf" .
  746 + " --object-streams=generate" .
  747 + " --deterministic-id" .
  748 + " --stream-data=uncompress" .
  749 + " object-types.pdf a.pdf"},
  750 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  751 +$td->runtest("check file",
  752 + {$td->FILE => "a.pdf"},
  753 + {$td->FILE => "object-types-os.pdf"});
  754 +$td->runtest("type checks",
  755 + {$td->COMMAND => "test_driver 42 object-types.pdf"},
  756 + {$td->FILE => "object-types.out",
  757 + $td->EXIT_STATUS => 0},
  758 + $td->NORMALIZE_NEWLINES);
  759 +$td->runtest("type checks with object streams",
  760 + {$td->COMMAND => "test_driver 42 object-types-os.pdf"},
  761 + {$td->FILE => "object-types-os.out",
  762 + $td->EXIT_STATUS => 0},
  763 + $td->NORMALIZE_NEWLINES);
  764 +
  765 +# ----------
739 $td->notify("--- Coalesce contents ---"); 766 $td->notify("--- Coalesce contents ---");
740 $n_tests += 6; 767 $n_tests += 6;
741 768
@@ -1200,7 +1227,7 @@ $n_tests += @badfiles + 3; @@ -1200,7 +1227,7 @@ $n_tests += @badfiles + 3;
1200 # have error conditions that used to be fatal but are now considered 1227 # have error conditions that used to be fatal but are now considered
1201 # non-fatal. 1228 # non-fatal.
1202 my %badtest_overrides = (); 1229 my %badtest_overrides = ();
1203 -for(6, 12..15, 17, 22..28, 30..32, 34, 36) 1230 +for(6, 12..15, 17, 18..32, 34, 36)
1204 { 1231 {
1205 $badtest_overrides{$_} = 0; 1232 $badtest_overrides{$_} = 0;
1206 } 1233 }
@@ -1243,7 +1270,7 @@ $n_tests += @badfiles + 6; @@ -1243,7 +1270,7 @@ $n_tests += @badfiles + 6;
1243 # though in some cases it may. Acrobat Reader would not be able to 1270 # though in some cases it may. Acrobat Reader would not be able to
1244 # recover any of these files any better. 1271 # recover any of these files any better.
1245 my %recover_failures = (); 1272 my %recover_failures = ();
1246 -for (1, 7, 16, 18..21, 29, 35) 1273 +for (1, 7, 16, 35)
1247 { 1274 {
1248 $recover_failures{$_} = 1; 1275 $recover_failures{$_} = 1;
1249 } 1276 }
qpdf/qtest/qpdf/bad16-recover.out
1 WARNING: bad16.pdf (trailer, offset 753): unexpected dictionary close token 1 WARNING: bad16.pdf (trailer, offset 753): unexpected dictionary close token
2 WARNING: bad16.pdf (trailer, offset 756): unexpected dictionary close token 2 WARNING: bad16.pdf (trailer, offset 756): unexpected dictionary close token
3 WARNING: bad16.pdf (trailer, offset 759): unknown token while reading object; treating as string 3 WARNING: bad16.pdf (trailer, offset 759): unknown token while reading object; treating as string
4 -WARNING: bad16.pdf: file is damaged  
5 WARNING: bad16.pdf (trailer, offset 779): unexpected EOF 4 WARNING: bad16.pdf (trailer, offset 779): unexpected EOF
  5 +WARNING: bad16.pdf (trailer, offset 779): parse error while reading object
  6 +WARNING: bad16.pdf: file is damaged
  7 +WARNING: bad16.pdf (offset 712): expected trailer dictionary
6 WARNING: bad16.pdf: Attempting to reconstruct cross-reference table 8 WARNING: bad16.pdf: Attempting to reconstruct cross-reference table
7 WARNING: bad16.pdf (trailer, offset 753): unexpected dictionary close token 9 WARNING: bad16.pdf (trailer, offset 753): unexpected dictionary close token
8 WARNING: bad16.pdf (trailer, offset 756): unexpected dictionary close token 10 WARNING: bad16.pdf (trailer, offset 756): unexpected dictionary close token
9 WARNING: bad16.pdf (trailer, offset 759): unknown token while reading object; treating as string 11 WARNING: bad16.pdf (trailer, offset 759): unknown token while reading object; treating as string
10 -bad16.pdf (trailer, offset 779): unexpected EOF 12 +WARNING: bad16.pdf (trailer, offset 779): unexpected EOF
  13 +WARNING: bad16.pdf (trailer, offset 779): parse error while reading object
  14 +bad16.pdf: unable to find trailer dictionary while recovering damaged file
qpdf/qtest/qpdf/bad16.out
1 WARNING: bad16.pdf (trailer, offset 753): unexpected dictionary close token 1 WARNING: bad16.pdf (trailer, offset 753): unexpected dictionary close token
2 WARNING: bad16.pdf (trailer, offset 756): unexpected dictionary close token 2 WARNING: bad16.pdf (trailer, offset 756): unexpected dictionary close token
3 WARNING: bad16.pdf (trailer, offset 759): unknown token while reading object; treating as string 3 WARNING: bad16.pdf (trailer, offset 759): unknown token while reading object; treating as string
4 -bad16.pdf (trailer, offset 779): unexpected EOF 4 +WARNING: bad16.pdf (trailer, offset 779): unexpected EOF
  5 +WARNING: bad16.pdf (trailer, offset 779): parse error while reading object
  6 +bad16.pdf (offset 712): expected trailer dictionary
qpdf/qtest/qpdf/bad18-recover.out
1 -WARNING: bad18.pdf: file is damaged  
2 WARNING: bad18.pdf (trailer, offset 753): unexpected ) 1 WARNING: bad18.pdf (trailer, offset 753): unexpected )
3 -WARNING: bad18.pdf: Attempting to reconstruct cross-reference table  
4 -bad18.pdf (trailer, offset 753): unexpected ) 2 +/QTest is implicit
  3 +/QTest is direct and has type null (2)
  4 +/QTest is null
  5 +unparse: null
  6 +unparseResolved: null
  7 +test 1 done
qpdf/qtest/qpdf/bad18.out
1 -bad18.pdf (trailer, offset 753): unexpected ) 1 +WARNING: bad18.pdf (trailer, offset 753): unexpected )
  2 +/QTest is implicit
  3 +/QTest is direct and has type null (2)
  4 +/QTest is null
  5 +unparse: null
  6 +unparseResolved: null
  7 +test 0 done
qpdf/qtest/qpdf/bad19-recover.out
1 -WARNING: bad19.pdf: file is damaged  
2 WARNING: bad19.pdf (trailer, offset 753): unexpected > 1 WARNING: bad19.pdf (trailer, offset 753): unexpected >
3 -WARNING: bad19.pdf: Attempting to reconstruct cross-reference table  
4 -bad19.pdf (trailer, offset 753): unexpected > 2 +/QTest is implicit
  3 +/QTest is direct and has type null (2)
  4 +/QTest is null
  5 +unparse: null
  6 +unparseResolved: null
  7 +test 1 done
qpdf/qtest/qpdf/bad19.out
1 -bad19.pdf (trailer, offset 753): unexpected > 1 +WARNING: bad19.pdf (trailer, offset 753): unexpected >
  2 +/QTest is implicit
  3 +/QTest is direct and has type null (2)
  4 +/QTest is null
  5 +unparse: null
  6 +unparseResolved: null
  7 +test 0 done
qpdf/qtest/qpdf/bad20-recover.out
1 -WARNING: bad20.pdf: file is damaged  
2 WARNING: bad20.pdf (trailer, offset 753): invalid character (q) in hexstring 1 WARNING: bad20.pdf (trailer, offset 753): invalid character (q) in hexstring
3 -WARNING: bad20.pdf: Attempting to reconstruct cross-reference table  
4 -bad20.pdf (trailer, offset 753): invalid character (q) in hexstring 2 +WARNING: bad20.pdf (trailer, offset 757): unknown token while reading object; treating as string
  3 +WARNING: bad20.pdf (trailer, offset 758): unexpected >
  4 +WARNING: bad20.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake1
  5 +WARNING: bad20.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake2
  6 +/QTest is implicit
  7 +/QTest is direct and has type null (2)
  8 +/QTest is null
  9 +unparse: null
  10 +unparseResolved: null
  11 +test 1 done
qpdf/qtest/qpdf/bad20.out
1 -bad20.pdf (trailer, offset 753): invalid character (q) in hexstring 1 +WARNING: bad20.pdf (trailer, offset 753): invalid character (q) in hexstring
  2 +WARNING: bad20.pdf (trailer, offset 757): unknown token while reading object; treating as string
  3 +WARNING: bad20.pdf (trailer, offset 758): unexpected >
  4 +WARNING: bad20.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake1
  5 +WARNING: bad20.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake2
  6 +/QTest is implicit
  7 +/QTest is direct and has type null (2)
  8 +/QTest is null
  9 +unparse: null
  10 +unparseResolved: null
  11 +test 0 done
qpdf/qtest/qpdf/bad21-recover.out
1 -WARNING: bad21.pdf: file is damaged  
2 WARNING: bad21.pdf (trailer, offset 742): invalid name token 1 WARNING: bad21.pdf (trailer, offset 742): invalid name token
3 -WARNING: bad21.pdf: Attempting to reconstruct cross-reference table  
4 -bad21.pdf (trailer, offset 742): invalid name token 2 +WARNING: bad21.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake1
  3 +WARNING: bad21.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake2
  4 +/QTest is implicit
  5 +/QTest is direct and has type null (2)
  6 +/QTest is null
  7 +unparse: null
  8 +unparseResolved: null
  9 +test 1 done
qpdf/qtest/qpdf/bad21.out
1 -bad21.pdf (trailer, offset 742): invalid name token 1 +WARNING: bad21.pdf (trailer, offset 742): invalid name token
  2 +WARNING: bad21.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake1
  3 +WARNING: bad21.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake2
  4 +/QTest is implicit
  5 +/QTest is direct and has type null (2)
  6 +/QTest is null
  7 +unparse: null
  8 +unparseResolved: null
  9 +test 0 done
qpdf/qtest/qpdf/bad29-recover.out
1 -WARNING: bad29.pdf: file is damaged  
2 WARNING: bad29.pdf (trailer, offset 742): null character not allowed in name token 1 WARNING: bad29.pdf (trailer, offset 742): null character not allowed in name token
3 -WARNING: bad29.pdf: Attempting to reconstruct cross-reference table  
4 -bad29.pdf (trailer, offset 742): null character not allowed in name token 2 +WARNING: bad29.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake1
  3 +WARNING: bad29.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake2
  4 +/QTest is implicit
  5 +/QTest is direct and has type null (2)
  6 +/QTest is null
  7 +unparse: null
  8 +unparseResolved: null
  9 +test 1 done
qpdf/qtest/qpdf/bad29.out
1 -bad29.pdf (trailer, offset 742): null character not allowed in name token 1 +WARNING: bad29.pdf (trailer, offset 742): null character not allowed in name token
  2 +WARNING: bad29.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake1
  3 +WARNING: bad29.pdf (trailer, offset 715): expected dictionary key but found non-name object; inserting key /QPDFFake2
  4 +/QTest is implicit
  5 +/QTest is direct and has type null (2)
  6 +/QTest is null
  7 +unparse: null
  8 +unparseResolved: null
  9 +test 0 done
qpdf/qtest/qpdf/issue-100.out
@@ -8,6 +8,9 @@ WARNING: issue-100.pdf (object 5 0, offset 294): unknown token while reading obj @@ -8,6 +8,9 @@ WARNING: issue-100.pdf (object 5 0, offset 294): unknown token while reading obj
8 WARNING: issue-100.pdf (object 5 0, offset 297): unknown token while reading object; treating as string 8 WARNING: issue-100.pdf (object 5 0, offset 297): unknown token while reading object; treating as string
9 WARNING: issue-100.pdf (object 5 0, offset 304): unknown token while reading object; treating as string 9 WARNING: issue-100.pdf (object 5 0, offset 304): unknown token while reading object; treating as string
10 WARNING: issue-100.pdf (object 5 0, offset 308): unexpected ) 10 WARNING: issue-100.pdf (object 5 0, offset 308): unexpected )
  11 +WARNING: issue-100.pdf (object 5 0, offset 316): treating unexpected array close token as null
  12 +WARNING: issue-100.pdf (object 5 0, offset 227): expected dictionary key but found non-name object; inserting key /QPDFFake1
  13 +WARNING: issue-100.pdf (object 5 0, offset 321): expected endobj
11 WARNING: issue-100.pdf (object 5 0, offset 418): /Length key in stream dictionary is not an integer 14 WARNING: issue-100.pdf (object 5 0, offset 418): /Length key in stream dictionary is not an integer
12 WARNING: issue-100.pdf (object 5 0, offset 489): attempting to recover stream length 15 WARNING: issue-100.pdf (object 5 0, offset 489): attempting to recover stream length
13 WARNING: issue-100.pdf (object 5 0, offset 489): recovered stream length: 12 16 WARNING: issue-100.pdf (object 5 0, offset 489): recovered stream length: 12
qpdf/qtest/qpdf/issue-101.out
@@ -56,4 +56,72 @@ WARNING: issue-101.pdf (object 11 0, offset 811): unknown token while reading ob @@ -56,4 +56,72 @@ WARNING: issue-101.pdf (object 11 0, offset 811): unknown token while reading ob
56 WARNING: issue-101.pdf (object 11 0, offset 819): unknown token while reading object; treating as string 56 WARNING: issue-101.pdf (object 11 0, offset 819): unknown token while reading object; treating as string
57 WARNING: issue-101.pdf (object 11 0, offset 832): unknown token while reading object; treating as string 57 WARNING: issue-101.pdf (object 11 0, offset 832): unknown token while reading object; treating as string
58 WARNING: issue-101.pdf (object 11 0, offset 856): unexpected > 58 WARNING: issue-101.pdf (object 11 0, offset 856): unexpected >
59 -issue-101.pdf (offset 856): unable to find /Root dictionary 59 +WARNING: issue-101.pdf (object 11 0, offset 857): unknown token while reading object; treating as string
  60 +WARNING: issue-101.pdf (object 11 0, offset 868): unknown token while reading object; treating as string
  61 +WARNING: issue-101.pdf (object 11 0, offset 887): unknown token while reading object; treating as string
  62 +WARNING: issue-101.pdf (object 11 0, offset 897): unexpected )
  63 +WARNING: issue-101.pdf (object 11 0, offset 898): unknown token while reading object; treating as string
  64 +WARNING: issue-101.pdf (object 11 0, offset 909): invalid character (ยค) in hexstring
  65 +WARNING: issue-101.pdf (object 11 0, offset 911): unknown token while reading object; treating as string
  66 +WARNING: issue-101.pdf (object 11 0, offset 929): unknown token while reading object; treating as string
  67 +WARNING: issue-101.pdf (object 11 0, offset 930): invalid character (ยฒ) in hexstring
  68 +WARNING: issue-101.pdf (object 11 0, offset 932): unknown token while reading object; treating as string
  69 +WARNING: issue-101.pdf (object 11 0, offset 944): unknown token while reading object; treating as string
  70 +WARNING: issue-101.pdf (object 11 0, offset 947): unknown token while reading object; treating as string
  71 +WARNING: issue-101.pdf (object 11 0, offset 970): unknown token while reading object; treating as string
  72 +WARNING: issue-101.pdf (object 11 0, offset 1046): unknown token while reading object; treating as string
  73 +WARNING: issue-101.pdf (object 11 0, offset 1067): unknown token while reading object; treating as string
  74 +WARNING: issue-101.pdf (object 11 0, offset 1075): unknown token while reading object; treating as string
  75 +WARNING: issue-101.pdf (object 11 0, offset 1080): unknown token while reading object; treating as string
  76 +WARNING: issue-101.pdf (object 11 0, offset 1084): unknown token while reading object; treating as string
  77 +WARNING: issue-101.pdf (object 11 0, offset 1102): unknown token while reading object; treating as string
  78 +WARNING: issue-101.pdf (object 11 0, offset 1112): unknown token while reading object; treating as string
  79 +WARNING: issue-101.pdf (object 11 0, offset 1124): unknown token while reading object; treating as string
  80 +WARNING: issue-101.pdf (object 11 0, offset 1133): unknown token while reading object; treating as string
  81 +WARNING: issue-101.pdf (object 11 0, offset 1145): unknown token while reading object; treating as string
  82 +WARNING: issue-101.pdf (object 11 0, offset 1148): unknown token while reading object; treating as string
  83 +WARNING: issue-101.pdf (object 11 0, offset 1150): unknown token while reading object; treating as string
  84 +WARNING: issue-101.pdf (object 11 0, offset 1151): unexpected )
  85 +WARNING: issue-101.pdf (object 11 0, offset 1153): unexpected dictionary close token
  86 +WARNING: issue-101.pdf (object 11 0, offset 1156): unknown token while reading object; treating as string
  87 +WARNING: issue-101.pdf (object 11 0, offset 1163): unknown token while reading object; treating as string
  88 +WARNING: issue-101.pdf (object 11 0, offset 1168): unexpected >
  89 +WARNING: issue-101.pdf (object 11 0, offset 1170): invalid character (I) in hexstring
  90 +WARNING: issue-101.pdf (object 11 0, offset 1167): expected dictionary key but found non-name object; inserting key /QPDFFake1
  91 +WARNING: issue-101.pdf (object 11 0, offset 1167): expected dictionary key but found non-name object; inserting key /QPDFFake2
  92 +WARNING: issue-101.pdf (object 11 0, offset 1167): expected dictionary key but found non-name object; inserting key /QPDFFake3
  93 +WARNING: issue-101.pdf (object 11 0, offset 1176): unknown token while reading object; treating as string
  94 +WARNING: issue-101.pdf (object 11 0, offset 1180): unknown token while reading object; treating as string
  95 +WARNING: issue-101.pdf (object 11 0, offset 1184): unknown token while reading object; treating as string
  96 +WARNING: issue-101.pdf (object 11 0, offset 1190): unexpected >
  97 +WARNING: issue-101.pdf (object 11 0, offset 1192): unknown token while reading object; treating as string
  98 +WARNING: issue-101.pdf (object 11 0, offset 1195): unknown token while reading object; treating as string
  99 +WARNING: issue-101.pdf (object 11 0, offset 1205): unknown token while reading object; treating as string
  100 +WARNING: issue-101.pdf (object 11 0, offset 1217): unknown token while reading object; treating as string
  101 +WARNING: issue-101.pdf (object 11 0, offset 1224): unknown token while reading object; treating as string
  102 +WARNING: issue-101.pdf (object 11 0, offset 1236): unknown token while reading object; treating as string
  103 +WARNING: issue-101.pdf (object 11 0, offset 1242): expected dictionary key but found non-name object; inserting key /QPDFFake1
  104 +WARNING: issue-101.pdf (object 11 0, offset 1242): dictionary ended prematurely; using null as value for last key
  105 +WARNING: issue-101.pdf (object 11 0, offset 1275): unknown token while reading object; treating as string
  106 +WARNING: issue-101.pdf (object 11 0, offset 1287): unknown token while reading object; treating as string
  107 +WARNING: issue-101.pdf (object 11 0, offset 1291): unexpected dictionary close token
  108 +WARNING: issue-101.pdf (object 11 0, offset 1294): unknown token while reading object; treating as string
  109 +WARNING: issue-101.pdf (object 11 0, offset 1306): unknown token while reading object; treating as string
  110 +WARNING: issue-101.pdf (object 11 0, offset 1322): unknown token while reading object; treating as string
  111 +WARNING: issue-101.pdf (object 11 0, offset 1325): unknown token while reading object; treating as string
  112 +WARNING: issue-101.pdf (object 11 0, offset 1329): unknown token while reading object; treating as string
  113 +WARNING: issue-101.pdf (object 11 0, offset 1341): treating unexpected array close token as null
  114 +WARNING: issue-101.pdf (object 11 0, offset 1312): expected dictionary key but found non-name object; inserting key /QPDFFake1
  115 +WARNING: issue-101.pdf (object 11 0, offset 1312): expected dictionary key but found non-name object; inserting key /QPDFFake2
  116 +WARNING: issue-101.pdf (object 11 0, offset 1312): expected dictionary key but found non-name object; inserting key /QPDFFake3
  117 +WARNING: issue-101.pdf (object 11 0, offset 1312): expected dictionary key but found non-name object; inserting key /QPDFFake4
  118 +WARNING: issue-101.pdf (object 11 0, offset 1312): dictionary ended prematurely; using null as value for last key
  119 +WARNING: issue-101.pdf (object 11 0, offset 1349): unknown token while reading object; treating as string
  120 +WARNING: issue-101.pdf (object 11 0, offset 1353): unknown token while reading object; treating as string
  121 +WARNING: issue-101.pdf (object 11 0, offset 1357): unknown token while reading object; treating as string
  122 +WARNING: issue-101.pdf (object 11 0, offset 1359): unknown token while reading object; treating as string
  123 +WARNING: issue-101.pdf (object 11 0, offset 1368): unexpected )
  124 +WARNING: issue-101.pdf (object 11 0, offset 1373): expected endobj
  125 +WARNING: issue-101.pdf (object 8 0, offset 4067): invalid character ()) in hexstring
  126 +WARNING: issue-101.pdf (object 8 0, offset 4069): expected endobj
  127 +qpdf: operation succeeded with warnings; resulting file may have some problems
qpdf/qtest/qpdf/issue-146.out
@@ -2,4 +2,6 @@ WARNING: issue-146.pdf: file is damaged @@ -2,4 +2,6 @@ WARNING: issue-146.pdf: file is damaged
2 WARNING: issue-146.pdf: can't find startxref 2 WARNING: issue-146.pdf: can't find startxref
3 WARNING: issue-146.pdf: Attempting to reconstruct cross-reference table 3 WARNING: issue-146.pdf: Attempting to reconstruct cross-reference table
4 WARNING: issue-146.pdf (trailer, offset 20728): unknown token while reading object; treating as string 4 WARNING: issue-146.pdf (trailer, offset 20728): unknown token while reading object; treating as string
5 -issue-146.pdf (trailer, offset 20732): unexpected EOF 5 +WARNING: issue-146.pdf (trailer, offset 20732): unexpected EOF
  6 +WARNING: issue-146.pdf (trailer, offset 20732): parse error while reading object
  7 +issue-146.pdf: unable to find trailer dictionary while recovering damaged file
qpdf/qtest/qpdf/issue-51.out
@@ -6,5 +6,6 @@ WARNING: issue-51.pdf (offset 70): loop detected resolving object 2 0 @@ -6,5 +6,6 @@ WARNING: issue-51.pdf (offset 70): loop detected resolving object 2 0
6 WARNING: issue-51.pdf (object 2 0, offset 26): /Length key in stream dictionary is not an integer 6 WARNING: issue-51.pdf (object 2 0, offset 26): /Length key in stream dictionary is not an integer
7 WARNING: issue-51.pdf (object 2 0, offset 71): attempting to recover stream length 7 WARNING: issue-51.pdf (object 2 0, offset 71): attempting to recover stream length
8 WARNING: issue-51.pdf (object 2 0, offset 71): unable to recover stream data; treating stream as empty 8 WARNING: issue-51.pdf (object 2 0, offset 71): unable to recover stream data; treating stream as empty
9 -WARNING: issue-51.pdf (object 2 0, offset 977): unexpected EOF 9 +WARNING: issue-51.pdf (object 2 0, offset 977): expected endobj
  10 +WARNING: issue-51.pdf (object 2 0, offset 977): EOF after endobj
10 qpdf: operation succeeded with warnings; resulting file may have some problems 11 qpdf: operation succeeded with warnings; resulting file may have some problems
qpdf/qtest/qpdf/linearization-bounds-1.out
@@ -2,7 +2,7 @@ checking linearization-bounds-1.pdf @@ -2,7 +2,7 @@ checking linearization-bounds-1.pdf
2 PDF Version: 1.3 2 PDF Version: 1.3
3 File is not encrypted 3 File is not encrypted
4 File is linearized 4 File is linearized
5 -WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 12302): unexpected EOF 5 +WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 12302): expected endstream
6 WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length 6 WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length
7 WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106 7 WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106
8 linearization-bounds-1.pdf (linearization hint table, offset 1183): /S (shared object) offset is out of bounds 8 linearization-bounds-1.pdf (linearization hint table, offset 1183): /S (shared object) offset is out of bounds
qpdf/qtest/qpdf/object-types-os.out 0 โ†’ 100644
  1 +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
  2 +WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: returning null for out of bounds array access
  3 +WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: returning null for out of bounds array access
  4 +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
  5 +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
  6 +WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: ignoring attempt to erase out of bounds array item
  7 +WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: ignoring attempt to erase out of bounds array item
  8 +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
  9 +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
  10 +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
  11 +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
  12 +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
  13 +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
  14 +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
  15 +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
  16 +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
  17 +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
  18 +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
  19 +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
  20 +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
  21 +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
  22 +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
  23 +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
  24 +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
  25 +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
  26 +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
  27 +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
  28 +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
  29 +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
  30 +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
  31 +One error
  32 +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
  33 +One error
  34 +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
  35 +Two errors
  36 +WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: returning null for out of bounds array access
  37 +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
  38 +One error
  39 +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
  40 +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
  41 +test 42 done
qpdf/qtest/qpdf/object-types-os.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/object-types.out 0 โ†’ 100644
  1 +WARNING: object-types.pdf, object 8 0 at offset 657: operation for string attempted on object of type dictionary: returning empty string
  2 +WARNING: object-types.pdf, object 8 0 at offset 717: returning null for out of bounds array access
  3 +WARNING: object-types.pdf, object 8 0 at offset 717: returning null for out of bounds array access
  4 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: returning null
  5 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to append item
  6 +WARNING: object-types.pdf, object 8 0 at offset 717: ignoring attempt to erase out of bounds array item
  7 +WARNING: object-types.pdf, object 8 0 at offset 717: ignoring attempt to erase out of bounds array item
  8 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to erase item
  9 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to insert item
  10 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to replace items
  11 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to set item
  12 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: treating as empty
  13 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: treating as empty
  14 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for boolean attempted on object of type integer: returning false
  15 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for dictionary attempted on object of type integer: treating as empty
  16 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for dictionary attempted on object of type integer: treating as empty
  17 +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
  18 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for dictionary attempted on object of type integer: ignoring key removal request
  19 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for dictionary attempted on object of type integer: ignoring key removal/replacement request
  20 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for dictionary attempted on object of type integer: ignoring key removal/replacement request
  21 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for dictionary attempted on object of type integer: ignoring key replacement request
  22 +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
  23 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for inlineimage attempted on object of type integer: returning empty data
  24 +WARNING: object-types.pdf, object 8 0 at offset 687: operation for integer attempted on object of type dictionary: returning 0
  25 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for name attempted on object of type integer: returning dummy name
  26 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for operator attempted on object of type integer: returning fake value
  27 +WARNING: object-types.pdf, object 8 0 at offset 687: operation for real attempted on object of type dictionary: returning 0.0
  28 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for string attempted on object of type integer: returning empty string
  29 +WARNING: object-types.pdf, object 8 0 at offset 669: operation for string attempted on object of type integer: returning empty string
  30 +WARNING: object-types.pdf, object 8 0 at offset 687: operation for number attempted on object of type dictionary: returning 0
  31 +One error
  32 +WARNING: object-types.pdf, object 8 0 at offset 724: operation for string attempted on object of type name: returning empty string
  33 +One error
  34 +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
  35 +Two errors
  36 +WARNING: object-types.pdf, object 8 0 at offset 717: returning null for out of bounds array access
  37 +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
  38 +One error
  39 +WARNING: object-types.pdf, object 8 0 at offset 745: operation for string attempted on object of type name: returning empty string
  40 +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
  41 +test 42 done
qpdf/qtest/qpdf/object-types.pdf 0 โ†’ 100644
  1 +%PDF-1.3
  2 +%ยฟรทยขรพ
  3 +%QDF-1.0
  4 +
  5 +1 0 obj
  6 +<<
  7 + /Pages 2 0 R
  8 + /Type /Catalog
  9 +>>
  10 +endobj
  11 +
  12 +2 0 obj
  13 +<<
  14 + /Count 1
  15 + /Kids [
  16 + 3 0 R
  17 + ]
  18 + /Type /Pages
  19 +>>
  20 +endobj
  21 +
  22 +%% Page 1
  23 +3 0 obj
  24 +<<
  25 + /Contents 4 0 R
  26 + /MediaBox [
  27 + 0
  28 + 0
  29 + 612
  30 + 792
  31 + ]
  32 + /Parent 2 0 R
  33 + /Resources <<
  34 + /Font <<
  35 + /F1 6 0 R
  36 + >>
  37 + /ProcSet 7 0 R
  38 + >>
  39 + /Type /Page
  40 +>>
  41 +endobj
  42 +
  43 +%% Contents for page 1
  44 +4 0 obj
  45 +<<
  46 + /Length 5 0 R
  47 +>>
  48 +stream
  49 +BT
  50 + /F1 24 Tf
  51 + 72 720 Td
  52 + (Potato) Tj
  53 +ET
  54 +endstream
  55 +endobj
  56 +
  57 +5 0 obj
  58 +44
  59 +endobj
  60 +
  61 +6 0 obj
  62 +<<
  63 + /BaseFont /Helvetica
  64 + /Encoding /WinAnsiEncoding
  65 + /Name /F1
  66 + /Subtype /Type1
  67 + /Type /Font
  68 +>>
  69 +endobj
  70 +
  71 +7 0 obj
  72 +[
  73 + /PDF
  74 + /Text
  75 +]
  76 +endobj
  77 +
  78 +8 0 obj
  79 +<<
  80 + /Integer 5
  81 + /Dictionary <<
  82 + /Key1 /Value1
  83 + /Key2 [
  84 + /Item0
  85 + << /K [ /V ] >>
  86 + /Item2
  87 + ]
  88 + >>
  89 +>>
  90 +endobj
  91 +
  92 +xref
  93 +0 9
  94 +0000000000 65535 f
  95 +0000000025 00000 n
  96 +0000000079 00000 n
  97 +0000000161 00000 n
  98 +0000000376 00000 n
  99 +0000000475 00000 n
  100 +0000000494 00000 n
  101 +0000000612 00000 n
  102 +0000000647 00000 n
  103 +trailer <<
  104 + /Root 1 0 R
  105 + /Size 9
  106 + /ID [<5ecb4bcc69402d31e10c2e63ec8500ee><5ecb4bcc69402d31e10c2e63ec8500ee>]
  107 + /QTest 8 0 R
  108 +>>
  109 +startxref
  110 +788
  111 +%%EOF
qpdf/test_driver.cc
@@ -1390,6 +1390,66 @@ void runtest(int n, char const* filename1, char const* arg2) @@ -1390,6 +1390,66 @@ void runtest(int n, char const* filename1, char const* arg2)
1390 w.setStaticID(true); 1390 w.setStaticID(true);
1391 w.write(); 1391 w.write();
1392 } 1392 }
  1393 + else if (n == 42)
  1394 + {
  1395 + // Access objects as wrong type. This test case is crafted to
  1396 + // work with object-types.pdf.
  1397 + QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest");
  1398 + QPDFObjectHandle array = qtest.getKey("/Dictionary").getKey("/Key2");
  1399 + QPDFObjectHandle dictionary = qtest.getKey("/Dictionary");
  1400 + QPDFObjectHandle integer = qtest.getKey("/Integer");
  1401 + QPDFObjectHandle null = QPDFObjectHandle::newNull();
  1402 + assert(array.isArray());
  1403 + assert(dictionary.isDictionary());
  1404 + assert("" == qtest.getStringValue());
  1405 + array.getArrayItem(-1).assertNull();
  1406 + array.getArrayItem(16059).assertNull();
  1407 + integer.getArrayItem(0).assertNull();
  1408 + integer.appendItem(null);
  1409 + array.eraseItem(-1);
  1410 + array.eraseItem(16059);
  1411 + integer.eraseItem(0);
  1412 + integer.insertItem(0, null);
  1413 + integer.setArrayFromVector(std::vector<QPDFObjectHandle>());
  1414 + integer.setArrayItem(0, null);
  1415 + assert(0 == integer.getArrayNItems());
  1416 + assert(integer.getArrayAsVector().empty());
  1417 + assert(false == integer.getBoolValue());
  1418 + assert(integer.getDictAsMap().empty());
  1419 + assert(integer.getKeys().empty());
  1420 + assert(false == integer.hasKey("/Potato"));
  1421 + integer.removeKey("/Potato");
  1422 + integer.replaceOrRemoveKey("/Potato", null);
  1423 + integer.replaceOrRemoveKey("/Potato", QPDFObjectHandle::newInteger(1));
  1424 + integer.replaceKey("/Potato", QPDFObjectHandle::newInteger(1));
  1425 + qtest.getKey("/Integer").getKey("/Potato");
  1426 + assert(integer.getInlineImageValue().empty());
  1427 + assert(0 == dictionary.getIntValue());
  1428 + assert("/QPDFFakeName" == integer.getName());
  1429 + assert("QPDFFAKE" == integer.getOperatorValue());
  1430 + assert("0.0" == dictionary.getRealValue());
  1431 + assert(integer.getStringValue().empty());
  1432 + assert(integer.getUTF8Value().empty());
  1433 + assert(0.0 == dictionary.getNumericValue());
  1434 + // Make sure error messages are okay for nested values
  1435 + std::cerr << "One error\n";
  1436 + assert(array.getArrayItem(0).getStringValue().empty());
  1437 + std::cerr << "One error\n";
  1438 + assert(dictionary.getKey("/Quack").getStringValue().empty());
  1439 + assert(array.getArrayItem(1).isDictionary());
  1440 + assert(array.getArrayItem(1).getKey("/K").isArray());
  1441 + assert(array.getArrayItem(1).getKey("/K").getArrayItem(0).isName());
  1442 + assert("/V" ==
  1443 + array.getArrayItem(1).getKey("/K").getArrayItem(0).getName());
  1444 + std::cerr << "Two errors\n";
  1445 + assert(array.getArrayItem(16059).getStringValue().empty());
  1446 + std::cerr << "One error\n";
  1447 + array.getArrayItem(1).getKey("/K").getArrayItem(0).getStringValue();
  1448 + // Stream dictionary
  1449 + QPDFObjectHandle page = pdf.getAllPages()[0];
  1450 + assert("/QPDFFakeName" ==
  1451 + page.getKey("/Contents").getDict().getKey("/Potato").getName());
  1452 + }
1393 else 1453 else
1394 { 1454 {
1395 throw std::runtime_error(std::string("invalid test ") + 1455 throw std::runtime_error(std::string("invalid test ") +