pdfviewer: 1) debug code for drawText (show magenta background for text, to show text even when we fail to load/show it), 2) some cleanup: refactor and rename classes and files
Review URL: https://codereview.chromium.org/23020003
git-svn-id: http://skia.googlecode.com/svn/trunk@10716 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/experimental/PdfViewer/pdfparser/native/SkPdfNativeObject.h b/experimental/PdfViewer/pdfparser/native/SkPdfNativeObject.h
new file mode 100644
index 0000000..1fa6272
--- /dev/null
+++ b/experimental/PdfViewer/pdfparser/native/SkPdfNativeObject.h
@@ -0,0 +1,1032 @@
+#ifndef EXPERIMENTAL_PDFVIEWER_PDFPARSER_NATIVE_SKPDFOBJECT_H_
+#define EXPERIMENTAL_PDFVIEWER_PDFPARSER_NATIVE_SKPDFOBJECT_H_
+
+#include <stdint.h>
+#include <string.h>
+#include <string>
+#include "SkTDArray.h"
+#include "SkTDict.h"
+#include "SkRect.h"
+#include "SkMatrix.h"
+#include "SkString.h"
+
+#include "SkPdfNYI.h"
+#include "SkPdfConfig.h"
+#include "SkPdfUtils.h"
+
+#include "SkPdfNativeTokenizer.h"
+
+class SkPdfDictionary;
+class SkPdfStream;
+class SkPdfAllocator;
+
+// TODO(edisonn): macro it and move it to utils
+SkMatrix SkMatrixFromPdfMatrix(double array[6]);
+
+
+#define kFilteredStreamBit 0
+#define kUnfilteredStreamBit 1
+#define kOwnedStreamBit 2
+
+class SkPdfNativeObject {
+ public:
+ enum ObjectType {
+ kInvalid_PdfObjectType,
+
+ kBoolean_PdfObjectType,
+ kInteger_PdfObjectType,
+ kReal_PdfObjectType,
+ kString_PdfObjectType,
+ kHexString_PdfObjectType,
+ kName_PdfObjectType,
+ kKeyword_PdfObjectType,
+ //kStream_PdfObjectType, // attached to a Dictionary
+ kArray_PdfObjectType,
+ kDictionary_PdfObjectType,
+ kNull_PdfObjectType,
+
+ // TODO(edisonn): after the pdf has been loaded completely, resolve all references
+ // try the same thing with delayed loaded ...
+ kReference_PdfObjectType,
+
+ kUndefined_PdfObjectType, // per 1.4 spec, if the same key appear twice in the dictionary, the value is undefined
+ };
+
+ enum DataType {
+ kEmpty_Data,
+ kFont_Data,
+ kBitmap_Data,
+ };
+
+private:
+ struct Reference {
+ unsigned int fId;
+ unsigned int fGen;
+ };
+
+ // TODO(edisonn): add stream start, stream end, where stream is weither the file
+ // or decoded/filtered pdf stream
+
+ // TODO(edisonn): add warning/report per object
+ // TODO(edisonn): add flag fUsed, to be used once the parsing is complete,
+ // so we could show what parts have been proccessed, ignored, or generated errors
+
+ ObjectType fObjectType;
+
+ union {
+ bool fBooleanValue;
+ int64_t fIntegerValue;
+ // TODO(edisonn): double, float? typedefed
+ double fRealValue;
+ NotOwnedString fStr;
+
+ // TODO(edisonn): make sure the foorprint of fArray and fMap is small, otherwise, use pointers, or classes with up to 8 bytes in footprint
+ SkTDArray<SkPdfNativeObject*>* fArray;
+ Reference fRef;
+ };
+ SkTDict<SkPdfNativeObject*>* fMap;
+
+ // TODO(edisonn): rename data with cache
+ void* fData;
+ DataType fDataType;
+
+
+public:
+
+ SkPdfNativeObject() : fObjectType(kInvalid_PdfObjectType), fMap(NULL), fData(NULL), fDataType(kEmpty_Data) {}
+
+
+ inline bool hasData(DataType type) {
+ return type == fDataType;
+ }
+
+ inline void* data(DataType type) {
+ return type == fDataType ? fData : NULL;
+ }
+
+ inline void setData(void* data, DataType type) {
+ releaseData();
+ fDataType = type;
+ fData = data;
+ }
+
+ void releaseData();
+
+// ~SkPdfNativeObject() {
+// //reset(); must be called manually!
+// }
+
+ void reset() {
+ switch (fObjectType) {
+ case kArray_PdfObjectType:
+ delete fArray;
+ break;
+
+ case kDictionary_PdfObjectType:
+ delete fMap;
+ if (isStreamOwned()) {
+ delete[] fStr.fBuffer;
+ fStr.fBuffer = NULL;
+ fStr.fBytes = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+ fObjectType = kInvalid_PdfObjectType;
+ releaseData();
+ }
+
+ ObjectType type() { return fObjectType; }
+
+ const char* c_str() const {
+ switch (fObjectType) {
+ case kString_PdfObjectType:
+ case kHexString_PdfObjectType:
+ case kKeyword_PdfObjectType:
+ case kName_PdfObjectType:
+ return (const char*)fStr.fBuffer;
+
+ default:
+ // TODO(edisonn): report/warning
+ return NULL;
+ }
+ }
+
+ size_t lenstr() const {
+ switch (fObjectType) {
+ case kString_PdfObjectType:
+ case kHexString_PdfObjectType:
+ case kKeyword_PdfObjectType:
+ case kName_PdfObjectType:
+ return fStr.fBytes;
+
+ default:
+ // TODO(edisonn): report/warning
+ return 0;
+ }
+ }
+
+
+ // TODO(edisonn): NYI
+ SkPdfDate& dateValue() const {
+ static SkPdfDate nyi;
+ return nyi;
+ }
+
+ // TODO(edisonn): NYI
+ SkPdfFunction& functionValue() const {
+ static SkPdfFunction nyi;
+ return nyi;
+ }
+
+ // TODO(edisonn): NYI
+ SkPdfFileSpec& fileSpecValue() const {
+ static SkPdfFileSpec nyi;
+ return nyi;
+ }
+
+ // TODO(edisonn): NYI
+ SkPdfTree& treeValue() const {
+ static SkPdfTree nyi;
+ return nyi;
+ }
+
+ static void makeBoolean(bool value, SkPdfNativeObject* obj) {
+ SkASSERT(obj->fObjectType == kInvalid_PdfObjectType);
+
+ obj->fObjectType = kBoolean_PdfObjectType;
+ obj->fBooleanValue = value;
+ }
+
+ static SkPdfNativeObject makeBoolean(bool value) {
+ SkPdfNativeObject obj;
+ obj.fObjectType = kBoolean_PdfObjectType;
+ obj.fBooleanValue = value;
+ return obj;
+ }
+
+ static void makeInteger(int64_t value, SkPdfNativeObject* obj) {
+ SkASSERT(obj->fObjectType == kInvalid_PdfObjectType);
+
+ obj->fObjectType = kInteger_PdfObjectType;
+ obj->fIntegerValue = value;
+ }
+
+ static void makeReal(double value, SkPdfNativeObject* obj) {
+ SkASSERT(obj->fObjectType == kInvalid_PdfObjectType);
+
+ obj->fObjectType = kReal_PdfObjectType;
+ obj->fRealValue = value;
+ }
+
+ static void makeNull(SkPdfNativeObject* obj) {
+ SkASSERT(obj->fObjectType == kInvalid_PdfObjectType);
+
+ obj->fObjectType = kNull_PdfObjectType;
+ }
+
+ static SkPdfNativeObject makeNull() {
+ SkPdfNativeObject obj;
+ obj.fObjectType = kNull_PdfObjectType;
+ return obj;
+ }
+
+ static SkPdfNativeObject kNull;
+
+ static void makeNumeric(const unsigned char* start, const unsigned char* end, SkPdfNativeObject* obj) {
+ SkASSERT(obj->fObjectType == kInvalid_PdfObjectType);
+
+ // TODO(edisonn): NYI properly
+ // if has dot (impl), or exceeds max int, is real, otherwise is int
+ bool isInt = true;
+ for (const unsigned char* current = start; current < end; current++) {
+ if (*current == '.') {
+ isInt = false;
+ break;
+ }
+ // TODO(edisonn): report parse issue with numbers like "24asdasd123"
+ }
+ if (isInt) {
+ makeInteger(atol((const char*)start), obj);
+ } else {
+ makeReal(atof((const char*)start), obj);
+ }
+ }
+
+ static void makeReference(unsigned int id, unsigned int gen, SkPdfNativeObject* obj) {
+ SkASSERT(obj->fObjectType == kInvalid_PdfObjectType);
+
+ obj->fObjectType = kReference_PdfObjectType;
+ obj->fRef.fId = id;
+ obj->fRef.fGen = gen;
+ }
+
+
+ static void makeString(const unsigned char* start, SkPdfNativeObject* obj) {
+ makeStringCore(start, strlen((const char*)start), obj, kString_PdfObjectType);
+ }
+
+ static void makeString(const unsigned char* start, const unsigned char* end, SkPdfNativeObject* obj) {
+ makeStringCore(start, end - start, obj, kString_PdfObjectType);
+ }
+
+ static void makeString(const unsigned char* start, size_t bytes, SkPdfNativeObject* obj) {
+ makeStringCore(start, bytes, obj, kString_PdfObjectType);
+ }
+
+
+ static void makeHexString(const unsigned char* start, SkPdfNativeObject* obj) {
+ makeStringCore(start, strlen((const char*)start), obj, kHexString_PdfObjectType);
+ }
+
+ static void makeHexString(const unsigned char* start, const unsigned char* end, SkPdfNativeObject* obj) {
+ makeStringCore(start, end - start, obj, kHexString_PdfObjectType);
+ }
+
+ static void makeHexString(const unsigned char* start, size_t bytes, SkPdfNativeObject* obj) {
+ makeStringCore(start, bytes, obj, kHexString_PdfObjectType);
+ }
+
+
+ static void makeName(const unsigned char* start, SkPdfNativeObject* obj) {
+ makeStringCore(start, strlen((const char*)start), obj, kName_PdfObjectType);
+ }
+
+ static void makeName(const unsigned char* start, const unsigned char* end, SkPdfNativeObject* obj) {
+ makeStringCore(start, end - start, obj, kName_PdfObjectType);
+ }
+
+ static void makeName(const unsigned char* start, size_t bytes, SkPdfNativeObject* obj) {
+ makeStringCore(start, bytes, obj, kName_PdfObjectType);
+ }
+
+
+ static void makeKeyword(const unsigned char* start, SkPdfNativeObject* obj) {
+ makeStringCore(start, strlen((const char*)start), obj, kKeyword_PdfObjectType);
+ }
+
+ static void makeKeyword(const unsigned char* start, const unsigned char* end, SkPdfNativeObject* obj) {
+ makeStringCore(start, end - start, obj, kKeyword_PdfObjectType);
+ }
+
+ static void makeKeyword(const unsigned char* start, size_t bytes, SkPdfNativeObject* obj) {
+ makeStringCore(start, bytes, obj, kKeyword_PdfObjectType);
+ }
+
+
+
+ // TODO(edisonn): make the functions to return SkPdfArray, move these functions in SkPdfArray
+ static void makeEmptyArray(SkPdfNativeObject* obj) {
+ SkASSERT(obj->fObjectType == kInvalid_PdfObjectType);
+
+ obj->fObjectType = kArray_PdfObjectType;
+ obj->fArray = new SkTDArray<SkPdfNativeObject*>();
+ // return (SkPdfArray*)obj;
+ }
+
+ bool appendInArray(SkPdfNativeObject* obj) {
+ SkASSERT(fObjectType == kArray_PdfObjectType);
+ if (fObjectType != kArray_PdfObjectType) {
+ // TODO(edisonn): report err
+ return false;
+ }
+
+ fArray->push(obj);
+ return true;
+ }
+
+ size_t size() const {
+ SkASSERT(fObjectType == kArray_PdfObjectType);
+
+ return fArray->count();
+ }
+
+ SkPdfNativeObject* objAtAIndex(int i) {
+ SkASSERT(fObjectType == kArray_PdfObjectType);
+
+ return (*fArray)[i];
+ }
+
+ SkPdfNativeObject* removeLastInArray() {
+ SkASSERT(fObjectType == kArray_PdfObjectType);
+
+ SkPdfNativeObject* ret = NULL;
+ fArray->pop(&ret);
+
+ return ret;
+ }
+
+
+ const SkPdfNativeObject* objAtAIndex(int i) const {
+ SkASSERT(fObjectType == kArray_PdfObjectType);
+
+ return (*fArray)[i];
+ }
+
+ SkPdfNativeObject* operator[](int i) {
+ SkASSERT(fObjectType == kArray_PdfObjectType);
+
+ return (*fArray)[i];
+ }
+
+ const SkPdfNativeObject* operator[](int i) const {
+ SkASSERT(fObjectType == kArray_PdfObjectType);
+
+ return (*fArray)[i];
+ }
+
+
+ // TODO(edisonn): make the functions to return SkPdfDictionary, move these functions in SkPdfDictionary
+ static void makeEmptyDictionary(SkPdfNativeObject* obj) {
+ SkASSERT(obj->fObjectType == kInvalid_PdfObjectType);
+
+ obj->fObjectType = kDictionary_PdfObjectType;
+ obj->fMap = new SkTDict<SkPdfNativeObject*>(1);
+ obj->fStr.fBuffer = NULL;
+ obj->fStr.fBytes = 0;
+ }
+
+ // TODO(edisonn): get all the possible names from spec, and compute a hash function
+ // that would create no overlaps in the same dictionary
+ // or build a tree of chars that when followed goes to a unique id/index/hash
+ // TODO(edisonn): generate constants like kDictFoo, kNameDict_name
+ // which will be used in code
+ // add function SkPdfFastNameKey key(const char* key);
+ // TODO(edisonn): setting the same key twike, will make the value undefined!
+ bool set(const SkPdfNativeObject* key, SkPdfNativeObject* value) {
+ SkASSERT(fObjectType == kDictionary_PdfObjectType);
+ SkASSERT(key->fObjectType == kName_PdfObjectType);
+
+ if (key->fObjectType != kName_PdfObjectType || fObjectType != kDictionary_PdfObjectType) {
+ // TODO(edisonn): report err
+ return false;
+ }
+
+ //// we rewrite all delimiters and white spaces with '\0', so we expect the end of name to be '\0'
+ //SkASSERT(key->fStr.fBuffer[key->fStr.fBytes] == '\0');
+
+ return set(key->fStr.fBuffer, key->fStr.fBytes, value);
+ }
+
+ bool set(const char* key, SkPdfNativeObject* value) {
+ return set((const unsigned char*)key, strlen(key), value);
+ }
+
+ bool set(const unsigned char* key, size_t len, SkPdfNativeObject* value) {
+ SkASSERT(fObjectType == kDictionary_PdfObjectType);
+
+ if (fObjectType != kDictionary_PdfObjectType) {
+ // TODO(edisonn): report err
+ return false;
+ }
+
+ return fMap->set((const char*)key, len, value);
+ }
+
+ SkPdfNativeObject* get(const SkPdfNativeObject* key) {
+ SkASSERT(fObjectType == kDictionary_PdfObjectType);
+ SkASSERT(key->fObjectType == kName_PdfObjectType);
+
+ if (key->fObjectType != kName_PdfObjectType || fObjectType != kDictionary_PdfObjectType) {
+ // TODO(edisonn): report err
+ return NULL;
+ }
+
+ //SkASSERT(key->fStr.fBuffer[key->fStr.fBytes] == '\0');
+
+ return get(key->fStr.fBuffer, key->fStr.fBytes);
+ }
+
+ SkPdfNativeObject* get(const char* key) {
+ return get((const unsigned char*)key, strlen(key));
+ }
+
+ SkPdfNativeObject* get(const unsigned char* key, size_t len) {
+ SkASSERT(fObjectType == kDictionary_PdfObjectType);
+ SkASSERT(key);
+ if (fObjectType != kDictionary_PdfObjectType) {
+ // TODO(edisonn): report err
+ return NULL;
+ }
+ SkPdfNativeObject* ret = NULL;
+ fMap->find((const char*)key, len, &ret);
+
+#ifdef PDF_TRACE
+ SkString _key;
+ _key.append((const char*)key, len);
+ printf("\nget(/%s) = %s\n", _key.c_str(), ret ? ret->toString(0, len + 9).c_str() : "_NOT_FOUND");
+#endif
+
+ return ret;
+ }
+
+ const SkPdfNativeObject* get(const SkPdfNativeObject* key) const {
+ SkASSERT(fObjectType == kDictionary_PdfObjectType);
+ SkASSERT(key->fObjectType == kName_PdfObjectType);
+
+ if (key->fObjectType != kName_PdfObjectType || fObjectType != kDictionary_PdfObjectType) {
+ // TODO(edisonn): report err
+ return NULL;
+ }
+
+ //SkASSERT(key->fStr.fBuffer[key->fStr.fBytes] == '\0');
+
+ return get(key->fStr.fBuffer, key->fStr.fBytes);
+ }
+
+ const SkPdfNativeObject* get(const char* key) const {
+ return get((const unsigned char*)key, strlen(key));
+ }
+
+ const SkPdfNativeObject* get(const unsigned char* key, size_t len) const {
+ SkASSERT(fObjectType == kDictionary_PdfObjectType);
+ SkASSERT(key);
+ if (fObjectType != kDictionary_PdfObjectType) {
+ // TODO(edisonn): report err
+ return NULL;
+ }
+ SkPdfNativeObject* ret = NULL;
+ fMap->find((const char*)key, len, &ret);
+
+#ifdef PDF_TRACE
+ SkString _key;
+ _key.append((const char*)key, len);
+ printf("\nget(/%s) = %s\n", _key.c_str(), ret ? ret->toString(0, len + 9).c_str() : "_NOT_FOUND");
+#endif
+
+ return ret;
+ }
+
+ const SkPdfNativeObject* get(const char* key, const char* abr) const {
+ const SkPdfNativeObject* ret = get(key);
+ // TODO(edisonn): / is a valid name, and it might be an abreviation, so "" should not be like NULL
+ // make this distiontion in generator, and remove "" from condition
+ if (ret != NULL || abr == NULL || *abr == '\0') {
+ return ret;
+ }
+ return get(abr);
+ }
+
+ SkPdfNativeObject* get(const char* key, const char* abr) {
+ SkPdfNativeObject* ret = get(key);
+ // TODO(edisonn): / is a valid name, and it might be an abreviation, so "" should not be like NULL
+ // make this distiontion in generator, and remove "" from condition
+ if (ret != NULL || abr == NULL || *abr == '\0') {
+ return ret;
+ }
+ return get(abr);
+ }
+
+ SkPdfDictionary* asDictionary() {
+ SkASSERT(isDictionary());
+ if (!isDictionary()) {
+ return NULL;
+ }
+ return (SkPdfDictionary*) this;
+ }
+
+ const SkPdfDictionary* asDictionary() const {
+ SkASSERT(isDictionary());
+ if (!isDictionary()) {
+ return NULL;
+ }
+ return (SkPdfDictionary*) this;
+ }
+
+
+ bool isReference() const {
+ return fObjectType == kReference_PdfObjectType;
+ }
+
+ bool isBoolean() const {
+ return fObjectType == kBoolean_PdfObjectType;
+ }
+
+ bool isInteger() const {
+ return fObjectType == kInteger_PdfObjectType;
+ }
+private:
+ bool isReal() const {
+ return fObjectType == kReal_PdfObjectType;
+ }
+public:
+ bool isNumber() const {
+ return fObjectType == kInteger_PdfObjectType || fObjectType == kReal_PdfObjectType;
+ }
+
+ bool isKeywordReference() const {
+ return fObjectType == kKeyword_PdfObjectType && fStr.fBytes == 1 && fStr.fBuffer[0] == 'R';
+ }
+
+ bool isKeyword() const {
+ return fObjectType == kKeyword_PdfObjectType;
+ }
+
+ bool isKeyword(const char* keyword) const {
+ if (!isKeyword()) {
+ return false;
+ }
+
+ if (strlen(keyword) != fStr.fBytes) {
+ return false;
+ }
+
+ if (strncmp(keyword, (const char*)fStr.fBuffer, fStr.fBytes) != 0) {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool isName() const {
+ return fObjectType == kName_PdfObjectType;
+ }
+
+ bool isName(const char* name) const {
+ return fObjectType == kName_PdfObjectType && fStr.fBytes == strlen(name) && strncmp((const char*)fStr.fBuffer, name, fStr.fBytes) == 0;
+ }
+
+ bool isArray() const {
+ return fObjectType == kArray_PdfObjectType;
+ }
+
+ bool isDate() const {
+ return fObjectType == kString_PdfObjectType || fObjectType == kHexString_PdfObjectType;
+ }
+
+ bool isDictionary() const {
+ return fObjectType == kDictionary_PdfObjectType;
+ }
+
+ bool isFunction() const {
+ return false; // NYI
+ }
+
+ bool isRectangle() const {
+ return fObjectType == kArray_PdfObjectType && fArray->count() == 4; // NYI + and elems are numbers
+ }
+
+ // TODO(edisonn): has stream .. or is stream ... TBD
+ bool hasStream() const {
+ return isDictionary() && fStr.fBuffer != NULL;
+ }
+
+ // TODO(edisonn): has stream .. or is stream ... TBD
+ const SkPdfStream* getStream() const {
+ return hasStream() ? (const SkPdfStream*)this : NULL;
+ }
+
+ SkPdfStream* getStream() {
+ return hasStream() ? (SkPdfStream*)this : NULL;
+ }
+
+ bool isAnyString() const {
+ return fObjectType == kString_PdfObjectType || fObjectType == kHexString_PdfObjectType;
+ }
+
+ bool isHexString() const {
+ return fObjectType == kHexString_PdfObjectType;
+ }
+
+ bool isMatrix() const {
+ return fObjectType == kArray_PdfObjectType && fArray->count() == 6; // NYI + and elems are numbers
+ }
+
+ inline int64_t intValue() const {
+ SkASSERT(fObjectType == kInteger_PdfObjectType);
+
+ if (fObjectType != kInteger_PdfObjectType) {
+ // TODO(edisonn): log err
+ return 0;
+ }
+ return fIntegerValue;
+ }
+private:
+ inline double realValue() const {
+ SkASSERT(fObjectType == kReal_PdfObjectType);
+
+ if (fObjectType != kReal_PdfObjectType) {
+ // TODO(edisonn): log err
+ return 0;
+ }
+ return fRealValue;
+ }
+public:
+ inline double numberValue() const {
+ SkASSERT(isNumber());
+
+ if (!isNumber()) {
+ // TODO(edisonn): log err
+ return 0;
+ }
+ return fObjectType == kReal_PdfObjectType ? fRealValue : fIntegerValue;
+ }
+
+ inline SkScalar scalarValue() const {
+ SkASSERT(isNumber());
+
+ if (!isNumber()) {
+ // TODO(edisonn): log err
+ return SkIntToScalar(0);
+ }
+ return fObjectType == kReal_PdfObjectType ? SkDoubleToScalar(fRealValue) :
+ SkIntToScalar(fIntegerValue);
+ }
+
+ int referenceId() const {
+ SkASSERT(fObjectType == kReference_PdfObjectType);
+ return fRef.fId;
+ }
+
+ int referenceGeneration() const {
+ SkASSERT(fObjectType == kReference_PdfObjectType);
+ return fRef.fGen;
+ }
+
+ inline const char* nameValue() const {
+ SkASSERT(fObjectType == kName_PdfObjectType);
+
+ if (fObjectType != kName_PdfObjectType) {
+ // TODO(edisonn): log err
+ return "";
+ }
+ return (const char*)fStr.fBuffer;
+ }
+
+ inline const char* stringValue() const {
+ SkASSERT(fObjectType == kString_PdfObjectType || fObjectType == kHexString_PdfObjectType);
+
+ if (fObjectType != kString_PdfObjectType && fObjectType != kHexString_PdfObjectType) {
+ // TODO(edisonn): log err
+ return "";
+ }
+ return (const char*)fStr.fBuffer;
+ }
+
+ inline NotOwnedString strRef() {
+ switch (fObjectType) {
+ case kString_PdfObjectType:
+ case kHexString_PdfObjectType:
+ case kKeyword_PdfObjectType:
+ case kName_PdfObjectType:
+ return fStr;
+
+ default:
+ // TODO(edisonn): report/warning
+ return NotOwnedString();
+ }
+ }
+
+ // TODO(edisonn): nameValue2 and stringValue2 are used to make code generation easy,
+ // but it is not a performat way to do it, since it will create an extra copy
+ // remove these functions and make code generated faster
+ inline std::string nameValue2() const {
+ SkASSERT(fObjectType == kName_PdfObjectType);
+
+ if (fObjectType != kName_PdfObjectType) {
+ // TODO(edisonn): log err
+ return "";
+ }
+ return std::string((const char*)fStr.fBuffer, fStr.fBytes);
+ }
+
+ inline std::string stringValue2() const {
+ SkASSERT(fObjectType == kString_PdfObjectType || fObjectType == kHexString_PdfObjectType);
+
+ if (fObjectType != kString_PdfObjectType && fObjectType != kHexString_PdfObjectType) {
+ // TODO(edisonn): log err
+ return "";
+ }
+ return std::string((const char*)fStr.fBuffer, fStr.fBytes);
+ }
+
+ inline bool boolValue() const {
+ SkASSERT(fObjectType == kBoolean_PdfObjectType);
+
+ if (fObjectType != kBoolean_PdfObjectType) {
+ // TODO(edisonn): log err
+ return false;
+ }
+ return fBooleanValue;
+ }
+
+ SkRect rectangleValue() const {
+ SkASSERT(isRectangle());
+ if (!isRectangle()) {
+ return SkRect::MakeEmpty();
+ }
+
+ double array[4];
+ for (int i = 0; i < 4; i++) {
+ // TODO(edisonn): version where we could resolve references?
+ const SkPdfNativeObject* elem = objAtAIndex(i);
+ if (elem == NULL || !elem->isNumber()) {
+ // TODO(edisonn): report error
+ return SkRect::MakeEmpty();
+ }
+ array[i] = elem->numberValue();
+ }
+
+ return SkRect::MakeLTRB(SkDoubleToScalar(array[0]),
+ SkDoubleToScalar(array[1]),
+ SkDoubleToScalar(array[2]),
+ SkDoubleToScalar(array[3]));
+ }
+
+ SkMatrix matrixValue() const {
+ SkASSERT(isMatrix());
+ if (!isMatrix()) {
+ return SkMatrix::I();
+ }
+
+ double array[6];
+ for (int i = 0; i < 6; i++) {
+ // TODO(edisonn): version where we could resolve references?
+ const SkPdfNativeObject* elem = objAtAIndex(i);
+ if (elem == NULL || !elem->isNumber()) {
+ // TODO(edisonn): report error
+ return SkMatrix::I();
+ }
+ array[i] = elem->numberValue();
+ }
+
+ return SkMatrixFromPdfMatrix(array);
+ }
+
+ bool filterStream();
+
+
+ bool GetFilteredStreamRef(unsigned char const** buffer, size_t* len) {
+ // TODO(edisonn): add params that couls let the last filter in place if it is jpeg or png to fast load images
+ if (!hasStream()) {
+ return false;
+ }
+
+ filterStream();
+
+ if (buffer) {
+ *buffer = fStr.fBuffer;
+ }
+
+ if (len) {
+ *len = fStr.fBytes >> 2; // last 2 bits
+ }
+
+ return true;
+ }
+
+ bool isStreamFiltered() const {
+ return hasStream() && ((fStr.fBytes & 1) == kFilteredStreamBit);
+ }
+
+ bool isStreamOwned() const {
+ return hasStream() && ((fStr.fBytes & 2) == kOwnedStreamBit);
+ }
+
+ bool GetUnfilteredStreamRef(unsigned char const** buffer, size_t* len) const {
+ if (isStreamFiltered()) {
+ return false;
+ }
+
+ if (!hasStream()) {
+ return false;
+ }
+
+ if (buffer) {
+ *buffer = fStr.fBuffer;
+ }
+
+ if (len) {
+ *len = fStr.fBytes >> 2; // remove last 2 bits
+ }
+
+ return true;
+ }
+
+ bool addStream(const unsigned char* buffer, size_t len) {
+ SkASSERT(!hasStream());
+ SkASSERT(isDictionary());
+
+ if (!isDictionary() || hasStream()) {
+ return false;
+ }
+
+ fStr.fBuffer = buffer;
+ fStr.fBytes = (len << 2) + kUnfilteredStreamBit;
+
+ return true;
+ }
+
+ static void appendSpaces(SkString* str, int level) {
+ for (int i = 0 ; i < level; i++) {
+ str->append(" ");
+ }
+ }
+
+ static void append(SkString* str, const char* data, size_t len, const char* prefix = "\\x") {
+ for (unsigned int i = 0 ; i < len; i++) {
+ if (data[i] == kNUL_PdfWhiteSpace) {
+ str->append(prefix);
+ str->append("00");
+ } else if (data[i] == kHT_PdfWhiteSpace) {
+ str->append(prefix);
+ str->append("09");
+ } else if (data[i] == kLF_PdfWhiteSpace) {
+ str->append(prefix);
+ str->append("0A");
+ } else if (data[i] == kFF_PdfWhiteSpace) {
+ str->append(prefix);
+ str->append("0C");
+ } else if (data[i] == kCR_PdfWhiteSpace) {
+ str->append(prefix);
+ str->append("0D");
+ } else {
+ str->append(data + i, 1);
+ }
+ }
+ }
+
+ SkString toString(int firstRowLevel = 0, int level = 0) {
+ SkString str;
+ appendSpaces(&str, firstRowLevel);
+ switch (fObjectType) {
+ case kInvalid_PdfObjectType:
+ str.append("__Invalid");
+ break;
+
+ case kBoolean_PdfObjectType:
+ str.appendf("%s", fBooleanValue ? "true" : "false");
+ break;
+
+ case kInteger_PdfObjectType:
+ str.appendf("%i", (int)fIntegerValue);
+ break;
+
+ case kReal_PdfObjectType:
+ str.appendf("%f", fRealValue);
+ break;
+
+ case kString_PdfObjectType:
+ str.append("\"");
+ append(&str, (const char*)fStr.fBuffer, fStr.fBytes);
+ str.append("\"");
+ break;
+
+ case kHexString_PdfObjectType:
+ str.append("<");
+ for (unsigned int i = 0 ; i < fStr.fBytes; i++) {
+ str.appendf("%02x", (unsigned int)fStr.fBuffer[i]);
+ }
+ str.append(">");
+ break;
+
+ case kName_PdfObjectType:
+ str.append("/");
+ append(&str, (const char*)fStr.fBuffer, fStr.fBytes, "#");
+ break;
+
+ case kKeyword_PdfObjectType:
+ append(&str, (const char*)fStr.fBuffer, fStr.fBytes);
+ break;
+
+ case kArray_PdfObjectType:
+ str.append("[\n");
+ for (unsigned int i = 0; i < size(); i++) {
+ str.append(objAtAIndex(i)->toString(level + 1, level + 1));
+ if (i < size() - 1) {
+ str.append(",");
+ }
+ str.append("\n");
+ }
+ appendSpaces(&str, level);
+ str.append("]");
+ break;
+
+ case kDictionary_PdfObjectType: {
+ SkTDict<SkPdfNativeObject*>::Iter iter(*fMap);
+ SkPdfNativeObject* obj = NULL;
+ const char* key = NULL;
+ str.append("<<\n");
+ while ((key = iter.next(&obj)) != NULL) {
+ appendSpaces(&str, level + 2);
+ str.appendf("/%s %s\n", key, obj->toString(0, level + strlen(key) + 4).c_str());
+ }
+ appendSpaces(&str, level);
+ str.append(">>");
+ if (hasStream()) {
+ const unsigned char* stream = NULL;
+ size_t length = 0;
+ if (GetFilteredStreamRef(&stream, &length)) {
+ str.append("stream\n");
+ append(&str, (const char*)stream, length > 256 ? 256 : length);
+ str.append("\nendstream");
+ } else {
+ str.append("stream STREAM_ERROR endstream");
+ }
+ }
+ }
+ break;
+
+ case kNull_PdfObjectType:
+ str = "NULL";
+ break;
+
+ case kReference_PdfObjectType:
+ str.appendf("%i %i R", fRef.fId, fRef.fGen);
+ break;
+
+ case kUndefined_PdfObjectType:
+ str = "Undefined";
+ break;
+
+ default:
+ str = "Error";
+ break;
+ }
+
+ return str;
+ }
+
+private:
+ static void makeStringCore(const unsigned char* start, SkPdfNativeObject* obj, ObjectType type) {
+ makeStringCore(start, strlen((const char*)start), obj, type);
+ }
+
+ static void makeStringCore(const unsigned char* start, const unsigned char* end, SkPdfNativeObject* obj, ObjectType type) {
+ makeStringCore(start, end - start, obj, type);
+ }
+
+ static void makeStringCore(const unsigned char* start, size_t bytes, SkPdfNativeObject* obj, ObjectType type) {
+ SkASSERT(obj->fObjectType == kInvalid_PdfObjectType);
+
+ obj->fObjectType = type;
+ obj->fStr.fBuffer = start;
+ obj->fStr.fBytes = bytes;
+ }
+
+ bool applyFilter(const char* name);
+ bool applyFlateDecodeFilter();
+ bool applyDCTDecodeFilter();
+};
+
+class SkPdfStream : public SkPdfNativeObject {};
+class SkPdfArray : public SkPdfNativeObject {};
+class SkPdfString : public SkPdfNativeObject {};
+class SkPdfHexString : public SkPdfNativeObject {};
+class SkPdfInteger : public SkPdfNativeObject {};
+class SkPdfReal : public SkPdfNativeObject {};
+class SkPdfNumber : public SkPdfNativeObject {};
+
+class SkPdfName : public SkPdfNativeObject {
+ SkPdfName() : SkPdfNativeObject() {
+ SkPdfNativeObject::makeName((const unsigned char*)"", this);
+ }
+public:
+ SkPdfName(char* name) : SkPdfNativeObject() {
+ this->makeName((const unsigned char*)name, this);
+ }
+};
+
+#endif // EXPERIMENTAL_PDFVIEWER_PDFPARSER_NATIVE_SKPDFOBJECT_H_