From: halcanary Date: Sat, 25 Apr 2015 13:45:07 +0000 (-0700) Subject: SkPDF: Refactor SkPDFObject heiararchy. X-Git-Tag: accepted/tizen/5.0/unified/20181102.025319~2653 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=130444fdaf737c5931a934018ab50b0c91a3499e;p=platform%2Fupstream%2FlibSkiaSharp.git SkPDF: Refactor SkPDFObject heiararchy. Flatten and use a tagged union type Δmemory ~= -2.4% ± 0.5% Δtime ~= -1.2% ± 0.2% BUG=skia:3585 Review URL: https://codereview.chromium.org/1069103003 --- diff --git a/src/doc/SkDocument_PDF.cpp b/src/doc/SkDocument_PDF.cpp index e812cd5..1e2148b 100644 --- a/src/doc/SkDocument_PDF.cpp +++ b/src/doc/SkDocument_PDF.cpp @@ -94,10 +94,6 @@ static void generate_page_tree(const SkTDArray& pages, // one child. static const int kNodeSize = 8; - SkAutoTUnref kidsName(new SkPDFName("Kids")); - SkAutoTUnref countName(new SkPDFName("Count")); - SkAutoTUnref parentName(new SkPDFName("Parent")); - // curNodes takes a reference to its items, which it passes to pageTree. SkTDArray curNodes; curNodes.setReserve(pages.count()); @@ -126,7 +122,7 @@ static void generate_page_tree(const SkTDArray& pages, int count = 0; for (; i < curNodes.count() && count < kNodeSize; i++, count++) { - curNodes[i]->insert(parentName.get(), newNodeRef.get()); + curNodes[i]->insert("Parent", newNodeRef.get()); kids->append(new SkPDFObjRef(curNodes[i]))->unref(); // TODO(vandebo): put the objects in strict access order. @@ -149,8 +145,8 @@ static void generate_page_tree(const SkTDArray& pages, if (i == curNodes.count()) { pageCount = ((pages.count() - 1) % treeCapacity) + 1; } - newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref(); - newNode->insert(kidsName.get(), kids.get()); + newNode->insert("Count", new SkPDFInt(pageCount))->unref(); + newNode->insert("Kids", kids.get()); nextRoundNodes.push(newNode); // Transfer reference. } diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index b08bb17..40a4576 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -1299,15 +1299,14 @@ const SkTDArray& SkPDFDevice::getFontResources() const { SkPDFArray* SkPDFDevice::copyMediaBox() const { // should this be a singleton? - SkAutoTUnref zero(SkNEW_ARGS(SkPDFInt, (0))); - SkPDFArray* mediaBox = SkNEW(SkPDFArray); + SkAutoTUnref mediaBox(SkNEW(SkPDFArray)); mediaBox->reserve(4); - mediaBox->append(zero.get()); - mediaBox->append(zero.get()); + mediaBox->appendInt(0); + mediaBox->appendInt(0); mediaBox->appendInt(fPageSize.fWidth); mediaBox->appendInt(fPageSize.fHeight); - return mediaBox; + return mediaBox.detach(); } SkStreamAsset* SkPDFDevice::content() const { @@ -1505,7 +1504,7 @@ void SkPDFDevice::handleLinkToURL(SkData* urlData, const SkRect& r, urlData->size() - 1); SkAutoTUnref action(SkNEW_ARGS(SkPDFDict, ("Action"))); action->insertName("S", "URI"); - action->insert("URI", SkNEW_ARGS(SkPDFString, (url)))->unref(); + action->insertString("URI", url); annotation->insert("A", action.get()); } @@ -1514,7 +1513,7 @@ void SkPDFDevice::handleLinkToNamedDest(SkData* nameData, const SkRect& r, SkAutoTUnref annotation(createLinkAnnotation(r, matrix)); SkString name(static_cast(nameData->data()), nameData->size() - 1); - annotation->insert("Dest", SkNEW_ARGS(SkPDFName, (name)))->unref(); + annotation->insertName("Dest", name); } struct NamedDestination { @@ -1522,9 +1521,7 @@ struct NamedDestination { SkPoint point; NamedDestination(const SkData* nameData, const SkPoint& point) - : nameData(nameData), point(point) { - nameData->ref(); - } + : nameData(SkRef(nameData)), point(point) {} ~NamedDestination() { nameData->unref(); @@ -1547,13 +1544,13 @@ void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) const { NamedDestination* dest = fNamedDestinations[i]; SkAutoTUnref pdfDest(SkNEW(SkPDFArray)); pdfDest->reserve(5); - pdfDest->append(SkNEW_ARGS(SkPDFObjRef, (page)))->unref(); + pdfDest->appendObjRef(SkRef(page)); pdfDest->appendName("XYZ"); pdfDest->appendScalar(dest->point.x()); pdfDest->appendScalar(dest->point.y()); pdfDest->appendInt(0); // Leave zoom unchanged - dict->insert(static_cast(dest->nameData->data()), - pdfDest); + SkString name(static_cast(dest->nameData->data())); + dict->insertObject(name, pdfDest.detach()); } } diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp index 1a396c7..c9de907 100644 --- a/src/pdf/SkPDFFont.cpp +++ b/src/pdf/SkPDFFont.cpp @@ -1390,10 +1390,8 @@ bool SkPDFType3Font::populate(uint16_t glyphID) { SkAutoTDelete glyphStream(new SkMemoryStream()); glyphStream->setData(content.copyToData())->unref(); - SkAutoTUnref glyphDescription( - new SkPDFStream(glyphStream.get())); - charProcs->insert(characterName.c_str(), - new SkPDFObjRef(glyphDescription.get()))->unref(); + charProcs->insertObjRef(characterName, + new SkPDFStream(glyphStream.get())); } insert("FontBBox", makeFontBBox(bbox, 1000))->unref(); diff --git a/src/pdf/SkPDFResourceDict.cpp b/src/pdf/SkPDFResourceDict.cpp index de5c910..1f5bcea 100644 --- a/src/pdf/SkPDFResourceDict.cpp +++ b/src/pdf/SkPDFResourceDict.cpp @@ -46,7 +46,7 @@ static char get_resource_type_prefix( static const char* get_resource_type_name( SkPDFResourceDict::SkPDFResourceType type) { SkASSERT(type >= 0); - SkASSERT(type < SkPDFResourceDict::kResourceTypeCount); + SkASSERT(type < SK_ARRAY_COUNT(resource_type_names)); return resource_type_names[type]; } @@ -67,13 +67,10 @@ static void add_subdict( } SkAutoTUnref resources(SkNEW(SkPDFDict)); for (int i = 0; i < resourceList.count(); i++) { - SkString keyString = SkPDFResourceDict::getResourceName(type, i); - SkAutoTUnref keyName(SkNEW_ARGS(SkPDFName, (keyString))); - SkAutoTUnref ref( - SkNEW_ARGS(SkPDFObjRef, (resourceList[i]))); - resources->insert(keyName, ref); + resources->insertObjRef(SkPDFResourceDict::getResourceName(type, i), + SkRef(resourceList[i])); } - dst->insert(get_resource_type_name(type), resources); + dst->insertObject(get_resource_type_name(type), resources.detach()); } SkPDFDict* SkPDFResourceDict::Create( @@ -90,7 +87,7 @@ SkPDFDict* SkPDFResourceDict::Create( for (size_t i = 0; i < SK_ARRAY_COUNT(kProcs); i++) { procSets->appendName(kProcs[i]); } - dict->insert("ProcSets", procSets); + dict->insertObject("ProcSets", procSets.detach()); if (gStateResources) { add_subdict(*gStateResources, kExtGState_ResourceType, dict); diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp index cd130cf..510b98b 100644 --- a/src/pdf/SkPDFTypes.cpp +++ b/src/pdf/SkPDFTypes.cpp @@ -6,7 +6,6 @@ * found in the LICENSE file. */ - #include "SkPDFTypes.h" #include "SkStream.h" @@ -18,62 +17,271 @@ //////////////////////////////////////////////////////////////////////////////// -SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) { - SkSafeRef(obj); +SkString* pun(char* x) { return reinterpret_cast(x); } +const SkString* pun(const char* x) { + return reinterpret_cast(x); } -SkPDFObjRef::~SkPDFObjRef() {} +SkPDFUnion::SkPDFUnion(Type t) : fType(t) {} -void SkPDFObjRef::emitObject(SkWStream* stream, - const SkPDFObjNumMap& objNumMap, - const SkPDFSubstituteMap& substitutes) { - SkPDFObject* obj = substitutes.getSubstitute(fObj); - stream->writeDecAsText(objNumMap.getObjectNumber(obj)); - stream->writeText(" 0 R"); // Generation number is always 0. +SkPDFUnion::~SkPDFUnion() { + switch (fType) { + case Type::kNameSkS: + case Type::kStringSkS: + pun(fSkString)->~SkString(); + return; + case Type::kObjRef: + case Type::kObject: + SkSafeUnref(fObject); + return; + default: + return; + } } -void SkPDFObjRef::addResources(SkPDFObjNumMap* catalog, - const SkPDFSubstituteMap& substitutes) const { - SkPDFObject* obj = substitutes.getSubstitute(fObj); - SkASSERT(obj); - if (catalog->addObject(obj)) { - obj->addResources(catalog, substitutes); +SkPDFUnion& SkPDFUnion::operator=(SkPDFUnion&& other) { + if (this != &other) { + this->~SkPDFUnion(); + SkNEW_PLACEMENT_ARGS(this, SkPDFUnion, (other.move())); } + return *this; +} + +SkPDFUnion::SkPDFUnion(SkPDFUnion&& other) { + SkASSERT(this != &other); + memcpy(this, &other, sizeof(*this)); + other.fType = Type::kDestroyed; +} + +#if 0 +SkPDFUnion SkPDFUnion::copy() const { + SkPDFUnion u(fType); + memcpy(&u, this, sizeof(u)); + switch (fType) { + case Type::kNameSkS: + case Type::kStringSkS: + SkNEW_PLACEMENT_ARGS(pun(u.fSkString), SkString, + (*pun(fSkString))); + return u.move(); + case Type::kObjRef: + case Type::kObject: + SkRef(u.fObject); + return u.move(); + default: + return u.move(); + } +} +SkPDFUnion& SkPDFUnion::operator=(const SkPDFUnion& other) { + return *this = other.copy(); } +SkPDFUnion::SkPDFUnion(const SkPDFUnion& other) { + *this = other.copy(); +} +#endif -//////////////////////////////////////////////////////////////////////////////// +bool SkPDFUnion::isName() const { + return Type::kName == fType || Type::kNameSkS == fType; +} -SkPDFInt::SkPDFInt(int32_t value) : fValue(value) {} -SkPDFInt::~SkPDFInt() {} +#ifdef SK_DEBUG +// Most names need no escaping. Such names are handled as static +// const strings. +bool is_valid_name(const char* n) { + static const char kControlChars[] = "/%()<>[]{}"; + while (*n) { + if (*n < '!' || *n > '~' || strchr(kControlChars, *n)) { + return false; + } + ++n; + } + return true; +} +#endif // SK_DEBUG -void SkPDFInt::emitObject(SkWStream* stream, - const SkPDFObjNumMap&, - const SkPDFSubstituteMap&) { - stream->writeDecAsText(fValue); +// Given an arbitrary string, convert it to a valid name. +static SkString escape_name(const char* name, size_t len) { + static const char kToEscape[] = "#/%()<>[]{}"; + int count = 0; + const char* const end = &name[len]; + for (const char* n = name; n != end; ++n) { + if (*n < '!' || *n > '~' || strchr(kToEscape, *n)) { + count += 2; + } + ++count; + } + SkString result(count); + char* s = result.writable_str(); + static const char kHex[] = "0123456789ABCDEF"; + for (const char* n = name; n != end; ++n) { + if (*n < '!' || *n > '~' || strchr(kToEscape, *n)) { + *s++ = '#'; + *s++ = kHex[(*n >> 4) & 0xF]; + *s++ = kHex[*n & 0xF]; + } else { + *s++ = *n; + } + } + SkASSERT(&result.writable_str()[count] == s); // don't over-write + return result; // allocated space } -//////////////////////////////////////////////////////////////////////////////// +static SkString escape_name(const SkString& name) { + return escape_name(name.c_str(), name.size()); +} -SkPDFBool::SkPDFBool(bool value) : fValue(value) {} -SkPDFBool::~SkPDFBool() {} +static void write_string(SkWStream* o, const SkString& s) { + o->write(s.c_str(), s.size()); +} -void SkPDFBool::emitObject(SkWStream* stream, - const SkPDFObjNumMap&, - const SkPDFSubstituteMap&) { - stream->writeText(fValue ? "true" : "false"); +static SkString format_string(const SkString& s) { + return SkPDFString::FormatString(s.c_str(), s.size()); } -//////////////////////////////////////////////////////////////////////////////// +static SkString format_string(const char* s) { + return SkPDFString::FormatString(s, strlen(s)); +} -SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {} -SkPDFScalar::~SkPDFScalar() {} +void SkPDFUnion::emitObject(SkWStream* stream, + const SkPDFObjNumMap& objNumMap, + const SkPDFSubstituteMap& substitutes) const { + switch (fType) { + case Type::kInt: + stream->writeDecAsText(fIntValue); + return; + case Type::kBool: + stream->writeText(fBoolValue ? "true" : "false"); + return; + case Type::kScalar: + SkPDFScalar::Append(fScalarValue, stream); + return; + case Type::kName: + stream->writeText("/"); + SkASSERT(is_valid_name(fStaticString)); + stream->writeText(fStaticString); + return; + case Type::kString: + SkASSERT(fStaticString); + write_string(stream, format_string(fStaticString)); + return; + case Type::kNameSkS: + stream->writeText("/"); + write_string(stream, escape_name(*pun(fSkString))); + return; + case Type::kStringSkS: + write_string(stream, format_string(*pun(fSkString))); + return; + case Type::kObjRef: + stream->writeDecAsText(objNumMap.getObjectNumber( + substitutes.getSubstitute(fObject))); + stream->writeText(" 0 R"); // Generation number is always 0. + return; + case Type::kObject: + fObject->emitObject(stream, objNumMap, substitutes); + return; + default: + SkDEBUGFAIL("SkPDFUnion::emitObject with bad type"); + } +} + +void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap, + const SkPDFSubstituteMap& substituteMap) const { + switch (fType) { + case Type::kInt: + case Type::kBool: + case Type::kScalar: + case Type::kName: + case Type::kString: + case Type::kNameSkS: + case Type::kStringSkS: + return; // These have no resources. + case Type::kObjRef: { + SkPDFObject* obj = substituteMap.getSubstitute(fObject); + if (objNumMap->addObject(obj)) { + obj->addResources(objNumMap, substituteMap); + } + return; + } + case Type::kObject: + fObject->addResources(objNumMap, substituteMap); + return; + default: + SkDEBUGFAIL("SkPDFUnion::addResources with bad type"); + } +} + +SkPDFUnion SkPDFUnion::Int(int32_t value) { + SkPDFUnion u(Type::kInt); + u.fIntValue = value; + return u.move(); +} + +SkPDFUnion SkPDFUnion::Bool(bool value) { + SkPDFUnion u(Type::kBool); + u.fBoolValue = value; + return u.move(); +} -void SkPDFScalar::emitObject(SkWStream* stream, - const SkPDFObjNumMap&, - const SkPDFSubstituteMap&) { - SkPDFScalar::Append(fValue, stream); +SkPDFUnion SkPDFUnion::Scalar(SkScalar value) { + SkPDFUnion u(Type::kScalar); + u.fScalarValue = value; + return u.move(); } +SkPDFUnion SkPDFUnion::Name(const char* value) { + SkPDFUnion u(Type::kName); + SkASSERT(value); + SkASSERT(is_valid_name(value)); + u.fStaticString = value; + return u.move(); +} + +SkPDFUnion SkPDFUnion::String(const char* value) { + SkPDFUnion u(Type::kString); + SkASSERT(value); + u.fStaticString = value; + return u.move(); +} + +SkPDFUnion SkPDFUnion::Name(const SkString& s) { + SkPDFUnion u(Type::kNameSkS); + SkNEW_PLACEMENT_ARGS(pun(u.fSkString), SkString, (s)); + return u.move(); +} + +SkPDFUnion SkPDFUnion::String(const SkString& s) { + SkPDFUnion u(Type::kStringSkS); + SkNEW_PLACEMENT_ARGS(pun(u.fSkString), SkString, (s)); + return u.move(); +} + +SkPDFUnion SkPDFUnion::ObjRef(SkPDFObject* ptr) { + SkPDFUnion u(Type::kObjRef); + SkASSERT(ptr); + u.fObject = ptr; + return u.move(); +} + +SkPDFUnion SkPDFUnion::Object(SkPDFObject* ptr) { + SkPDFUnion u(Type::kObject); + SkASSERT(ptr); + u.fObject = ptr; + return u.move(); +} + +//////////////////////////////////////////////////////////////////////////////// + +void SkPDFAtom::emitObject(SkWStream* stream, + const SkPDFObjNumMap& objNumMap, + const SkPDFSubstituteMap& substitutes) { + fValue.emitObject(stream, objNumMap, substitutes); +} +void SkPDFAtom::addResources(SkPDFObjNumMap* map, + const SkPDFSubstituteMap& substitutes) const { + fValue.addResources(map, substitutes); +} + +//////////////////////////////////////////////////////////////////////////////// + // static void SkPDFScalar::Append(SkScalar value, SkWStream* stream) { // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and @@ -125,23 +333,8 @@ void SkPDFScalar::Append(SkScalar value, SkWStream* stream) { //////////////////////////////////////////////////////////////////////////////// -SkPDFString::SkPDFString(const char value[]) - : fValue(FormatString(value, strlen(value))) { -} - -SkPDFString::SkPDFString(const SkString& value) - : fValue(FormatString(value.c_str(), value.size())) { -} - -SkPDFString::~SkPDFString() {} - -void SkPDFString::emitObject(SkWStream* stream, - const SkPDFObjNumMap&, - const SkPDFSubstituteMap&) { - stream->write(fValue.c_str(), fValue.size()); -} - // static + SkString SkPDFString::FormatString(const char* cin, size_t len) { SkASSERT(len <= kMaxLen); @@ -187,55 +380,25 @@ SkString SkPDFString::FormatString(const char* cin, size_t len) { //////////////////////////////////////////////////////////////////////////////// -SkPDFName::SkPDFName(const char name[]) : fValue(FormatName(SkString(name))) {} -SkPDFName::SkPDFName(const SkString& name) : fValue(FormatName(name)) {} -SkPDFName::~SkPDFName() {} - -bool SkPDFName::operator==(const SkPDFName& b) const { - return fValue == b.fValue; -} - -void SkPDFName::emitObject(SkWStream* stream, - const SkPDFObjNumMap&, - const SkPDFSubstituteMap&) { - stream->write(fValue.c_str(), fValue.size()); -} - -// static -SkString SkPDFName::FormatName(const SkString& input) { - SkASSERT(input.size() <= kMaxLen); - // TODO(vandebo) If more escaping is needed, improve the linear scan. - static const char escaped[] = "#/%()<>[]{}"; - - SkString result("/"); - for (size_t i = 0; i < input.size(); i++) { - if (input[i] & 0x80 || input[i] < '!' || strchr(escaped, input[i])) { - result.append("#"); - // Mask with 0xFF to avoid sign extension. i.e. #FFFFFF81 - result.appendHex(input[i] & 0xFF, 2); - } else { - result.append(input.c_str() + i, 1); - } +SkPDFArray::SkPDFArray() {} +SkPDFArray::~SkPDFArray() { + for (SkPDFUnion& value : fValues) { + value.~SkPDFUnion(); } - - return result; + fValues.reset(); } -//////////////////////////////////////////////////////////////////////////////// +int SkPDFArray::size() const { return fValues.count(); } -SkPDFArray::SkPDFArray() {} -SkPDFArray::~SkPDFArray() { - fValue.unrefAll(); -} +void SkPDFArray::reserve(int length) { fValues.setReserve(length); } void SkPDFArray::emitObject(SkWStream* stream, const SkPDFObjNumMap& objNumMap, const SkPDFSubstituteMap& substitutes) { stream->writeText("["); - for (int i = 0; i < fValue.count(); i++) { - SkASSERT(substitutes.getSubstitute(fValue[i]) == fValue[i]); - fValue[i]->emitObject(stream, objNumMap, substitutes); - if (i + 1 < fValue.count()) { + for (int i = 0; i < fValues.count(); i++) { + fValues[i].emitObject(stream, objNumMap, substitutes); + if (i + 1 < fValues.count()) { stream->writeText(" "); } } @@ -244,68 +407,74 @@ void SkPDFArray::emitObject(SkWStream* stream, void SkPDFArray::addResources(SkPDFObjNumMap* catalog, const SkPDFSubstituteMap& substitutes) const { - for (int i = 0; i < fValue.count(); i++) { - SkASSERT(substitutes.getSubstitute(fValue[i]) == fValue[i]); - fValue[i]->addResources(catalog, substitutes); + for (const SkPDFUnion& value : fValues) { + value.addResources(catalog, substitutes); } } -void SkPDFArray::reserve(int length) { - SkASSERT(length <= kMaxLen); - fValue.setReserve(length); +void SkPDFArray::append(SkPDFUnion&& value) { + SkNEW_PLACEMENT_ARGS(fValues.append(), SkPDFUnion, (value.move())); } SkPDFObject* SkPDFArray::append(SkPDFObject* value) { - SkASSERT(fValue.count() < kMaxLen); - value->ref(); - fValue.push(value); + // DEPRECATED + this->append(SkPDFUnion::Object(SkRef(value))); return value; } void SkPDFArray::appendInt(int32_t value) { - SkASSERT(fValue.count() < kMaxLen); - fValue.push(new SkPDFInt(value)); + this->append(SkPDFUnion::Int(value)); +} + +void SkPDFArray::appendBool(bool value) { + this->append(SkPDFUnion::Bool(value)); } void SkPDFArray::appendScalar(SkScalar value) { - SkASSERT(fValue.count() < kMaxLen); - fValue.push(new SkPDFScalar(value)); + this->append(SkPDFUnion::Scalar(value)); } void SkPDFArray::appendName(const char name[]) { - SkASSERT(fValue.count() < kMaxLen); - fValue.push(new SkPDFName(name)); + this->append(SkPDFUnion::Name(SkString(name))); } -/////////////////////////////////////////////////////////////////////////////// +void SkPDFArray::appendName(const SkString& name) { + this->append(SkPDFUnion::Name(name)); +} -SkPDFDict::SkPDFDict() {} +void SkPDFArray::appendString(const SkString& value) { + this->append(SkPDFUnion::String(value)); +} -SkPDFDict::SkPDFDict(const char type[]) { - insertName("Type", type); +void SkPDFArray::appendString(const char value[]) { + this->append(SkPDFUnion::String(value)); } -SkPDFDict::~SkPDFDict() { - clear(); +void SkPDFArray::appendObject(SkPDFObject* value) { + this->append(SkPDFUnion::Object(value)); } -int SkPDFDict::size() const { - return fValue.count(); +void SkPDFArray::appendObjRef(SkPDFObject* value) { + this->append(SkPDFUnion::ObjRef(value)); } +/////////////////////////////////////////////////////////////////////////////// + +SkPDFDict::SkPDFDict() {} + +SkPDFDict::~SkPDFDict() { this->clear(); } + +SkPDFDict::SkPDFDict(const char type[]) { this->insertName("Type", type); } + void SkPDFDict::emitObject(SkWStream* stream, const SkPDFObjNumMap& objNumMap, const SkPDFSubstituteMap& substitutes) { stream->writeText("<<"); - for (int i = 0; i < fValue.count(); i++) { - SkASSERT(fValue[i].key); - SkASSERT(fValue[i].value); - SkASSERT(substitutes.getSubstitute(fValue[i].key) == fValue[i].key); - SkASSERT(substitutes.getSubstitute(fValue[i].value) == fValue[i].value); - fValue[i].key->emitObject(stream, objNumMap, substitutes); + for (int i = 0; i < fRecords.count(); i++) { + fRecords[i].fKey.emitObject(stream, objNumMap, substitutes); stream->writeText(" "); - fValue[i].value->emitObject(stream, objNumMap, substitutes); - if (i + 1 < fValue.count()) { + fRecords[i].fValue.emitObject(stream, objNumMap, substitutes); + if (i + 1 < fRecords.count()) { stream->writeText("\n"); } } @@ -314,72 +483,75 @@ void SkPDFDict::emitObject(SkWStream* stream, void SkPDFDict::addResources(SkPDFObjNumMap* catalog, const SkPDFSubstituteMap& substitutes) const { - for (int i = 0; i < fValue.count(); i++) { - SkASSERT(fValue[i].key); - SkASSERT(fValue[i].value); - fValue[i].key->addResources(catalog, substitutes); - SkASSERT(substitutes.getSubstitute(fValue[i].value) == fValue[i].value); - fValue[i].value->addResources(catalog, substitutes); + for (int i = 0; i < fRecords.count(); i++) { + fRecords[i].fKey.addResources(catalog, substitutes); + fRecords[i].fValue.addResources(catalog, substitutes); } } -SkPDFObject* SkPDFDict::append(SkPDFName* key, SkPDFObject* value) { - SkASSERT(key); - SkASSERT(value); - *(fValue.append()) = Rec(key, value); - return value; +void SkPDFDict::set(SkPDFUnion&& name, SkPDFUnion&& value) { + Record* rec = fRecords.append(); + SkASSERT(name.isName()); + SkNEW_PLACEMENT_ARGS(&rec->fKey, SkPDFUnion, (name.move())); + SkNEW_PLACEMENT_ARGS(&rec->fValue, SkPDFUnion, (value.move())); +} + +int SkPDFDict::size() const { return fRecords.count(); } + +void SkPDFDict::insertObjRef(const char key[], SkPDFObject* value) { + this->set(SkPDFUnion::Name(key), SkPDFUnion::ObjRef(value)); +} +void SkPDFDict::insertObjRef(const SkString& key, SkPDFObject* value) { + this->set(SkPDFUnion::Name(key), SkPDFUnion::ObjRef(value)); } -SkPDFObject* SkPDFDict::insert(SkPDFName* key, SkPDFObject* value) { - return this->append(SkRef(key), SkRef(value)); +void SkPDFDict::insertObject(const char key[], SkPDFObject* value) { + this->set(SkPDFUnion::Name(key), SkPDFUnion::Object(value)); +} +void SkPDFDict::insertObject(const SkString& key, SkPDFObject* value) { + this->set(SkPDFUnion::Name(key), SkPDFUnion::Object(value)); } +// DEPRECATED SkPDFObject* SkPDFDict::insert(const char key[], SkPDFObject* value) { - return this->append(new SkPDFName(key), SkRef(value)); + this->set(SkPDFUnion::Name(key), SkPDFUnion::Object(SkRef(value))); + return value; } void SkPDFDict::insertInt(const char key[], int32_t value) { - (void)this->append(new SkPDFName(key), new SkPDFInt(value)); + this->set(SkPDFUnion::Name(key), SkPDFUnion::Int(value)); +} + +void SkPDFDict::insertInt(const char key[], size_t value) { + this->insertInt(key, SkToS32(value)); } void SkPDFDict::insertScalar(const char key[], SkScalar value) { - (void)this->append(new SkPDFName(key), new SkPDFScalar(value)); + this->set(SkPDFUnion::Name(key), SkPDFUnion::Scalar(value)); } void SkPDFDict::insertName(const char key[], const char name[]) { - (void)this->append(new SkPDFName(key), new SkPDFName(name)); + this->set(SkPDFUnion::Name(key), SkPDFUnion::Name(name)); } -void SkPDFDict::clear() { - for (int i = 0; i < fValue.count(); i++) { - SkASSERT(fValue[i].key); - SkASSERT(fValue[i].value); - fValue[i].key->unref(); - fValue[i].value->unref(); - } - fValue.reset(); -} - -void SkPDFDict::remove(const char key[]) { - SkASSERT(key); - SkPDFName name(key); - for (int i = 0; i < fValue.count(); i++) { - SkASSERT(fValue[i].key); - if (*(fValue[i].key) == name) { - fValue[i].key->unref(); - SkASSERT(fValue[i].value); - fValue[i].value->unref(); - fValue.removeShuffle(i); - return; - } - } +void SkPDFDict::insertName(const char key[], const SkString& name) { + this->set(SkPDFUnion::Name(key), SkPDFUnion::Name(name)); +} + +void SkPDFDict::insertString(const char key[], const char value[]) { + this->set(SkPDFUnion::Name(key), SkPDFUnion::String(value)); +} + +void SkPDFDict::insertString(const char key[], const SkString& value) { + this->set(SkPDFUnion::Name(key), SkPDFUnion::String(value)); } -void SkPDFDict::mergeFrom(const SkPDFDict& other) { - for (int i = 0; i < other.fValue.count(); i++) { - *(fValue.append()) = - Rec(SkRef(other.fValue[i].key), SkRef(other.fValue[i].value)); +void SkPDFDict::clear() { + for (Record& rec : fRecords) { + rec.fKey.~SkPDFUnion(); + rec.fValue.~SkPDFUnion(); } + fRecords.reset(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/pdf/SkPDFTypes.h b/src/pdf/SkPDFTypes.h index 76e1911..f258e38 100644 --- a/src/pdf/SkPDFTypes.h +++ b/src/pdf/SkPDFTypes.h @@ -1,4 +1,3 @@ - /* * Copyright 2010 The Android Open Source Project * @@ -10,7 +9,6 @@ #ifndef SkPDFTypes_DEFINED #define SkPDFTypes_DEFINED -#include "SkPDFTypes.h" #include "SkRefCnt.h" #include "SkScalar.h" #include "SkString.h" @@ -28,10 +26,11 @@ class SkWStream; A PDF Object is the base class for primitive elements in a PDF file. A common subtype is used to ease the use of indirect object references, which are common in the PDF format. + */ class SkPDFObject : public SkRefCnt { public: - SK_DECLARE_INST_COUNT(SkPDFObject) + SK_DECLARE_INST_COUNT(SkPDFObject); /** Subclasses must implement this method to print the object to the * PDF file. @@ -55,170 +54,191 @@ private: typedef SkRefCnt INHERITED; }; -/** \class SkPDFObjRef +//////////////////////////////////////////////////////////////////////////////// - An indirect reference to a PDF object. -*/ -class SkPDFObjRef : public SkPDFObject { +/** + A SkPDFUnion is a non-virtualized implementation of the + non-compound, non-specialized PDF Object types: Name, String, + Number, Boolean. + */ +class SkPDFUnion { public: - SK_DECLARE_INST_COUNT(SkPDFObjRef) + // u.move() is analogous to std::move(u). It returns an rvalue. + SkPDFUnion move() { return static_cast(*this); } + // Move contstructor and assignemnt operator destroy the argument + // and steal their references (if needed). + SkPDFUnion(SkPDFUnion&& other); + SkPDFUnion& operator=(SkPDFUnion&& other); - /** Create a reference to an existing SkPDFObject. - * @param obj The object to reference. - */ - explicit SkPDFObjRef(SkPDFObject* obj); - virtual ~SkPDFObjRef(); + ~SkPDFUnion(); - // The SkPDFObject interface. - virtual void emitObject(SkWStream* stream, - const SkPDFObjNumMap& objNumMap, - const SkPDFSubstituteMap& substitutes) override; - virtual void addResources(SkPDFObjNumMap*, - const SkPDFSubstituteMap&) const override; + /** The following nine functions are the standard way of creating + SkPDFUnion objects. */ -private: - SkAutoTUnref fObj; + static SkPDFUnion Int(int32_t); - typedef SkPDFObject INHERITED; -}; + static SkPDFUnion Bool(bool); -/** \class SkPDFInt + static SkPDFUnion Scalar(SkScalar); - An integer object in a PDF. -*/ -class SkPDFInt : public SkPDFObject { -public: - SK_DECLARE_INST_COUNT(SkPDFInt) + /** These two functions do NOT take ownership of ptr, and do NOT + copy the string. Suitable for passing in static const + strings. For example: + SkPDFUnion n = SkPDFUnion::Name("Length"); + SkPDFUnion u = SkPDFUnion::String("Identity"); */ - /** Create a PDF integer (usually for indirect reference purposes). - * @param value An integer value between 2^31 - 1 and -2^31. - */ - explicit SkPDFInt(int32_t value); - virtual ~SkPDFInt(); + /** SkPDFUnion::Name(const char*) assumes that the passed string + is already a valid name (that is: it has no control or + whitespace characters). This will not copy the name. */ + static SkPDFUnion Name(const char*); - // The SkPDFObject interface. - virtual void emitObject(SkWStream* stream, - const SkPDFObjNumMap& objNumMap, - const SkPDFSubstituteMap& substitutes) override; + /** SkPDFUnion::String will encode the passed string. This will + not copy the name. */ + static SkPDFUnion String(const char*); -private: - int32_t fValue; + /** SkPDFUnion::Name(const SkString&) does not assume that the + passed string is already a valid name and it will escape the + string. */ + static SkPDFUnion Name(const SkString&); - typedef SkPDFObject INHERITED; -}; + /** SkPDFUnion::String will encode the passed string. */ + static SkPDFUnion String(const SkString&); -/** \class SkPDFBool + /** This function DOES take ownership of the object. E.g. + SkAutoTUnref dict(new SkPDFDict); + dict->insert(.....); + SkPDFUnion u = SkPDFUnion::Object(dict.detach()) */ + static SkPDFUnion Object(SkPDFObject*); - An boolean value in a PDF. -*/ -class SkPDFBool : public SkPDFObject { -public: - SK_DECLARE_INST_COUNT(SkPDFBool) + /** This function DOES take ownership of the object. E.g. + SkAutoTUnref image( + SkPDFBitmap::Create(fCanon, bitmap)); + SkPDFUnion u = SkPDFUnion::ObjRef(image.detach()) */ + static SkPDFUnion ObjRef(SkPDFObject*); - /** Create a PDF boolean. - * @param value true or false. - */ - explicit SkPDFBool(bool value); - virtual ~SkPDFBool(); + /** These two non-virtual methods mirror SkPDFObject's + corresponding virtuals. */ + void emitObject(SkWStream*, + const SkPDFObjNumMap&, + const SkPDFSubstituteMap&) const; + void addResources(SkPDFObjNumMap*, const SkPDFSubstituteMap&) const; - // The SkPDFObject interface. - virtual void emitObject(SkWStream* stream, - const SkPDFObjNumMap& objNumMap, - const SkPDFSubstituteMap& substitutes) override; + bool isName() const; private: - bool fValue; + union { + int32_t fIntValue; + bool fBoolValue; + SkScalar fScalarValue; + const char* fStaticString; + char fSkString[sizeof(SkString)]; + SkPDFObject* fObject; + }; + enum class Type : char { + /** It is an error to call emitObject() or addResources() on an + kDestroyed object. */ + kDestroyed = 0, + kInt, + kBool, + kScalar, + kName, + kString, + kNameSkS, + kStringSkS, + kObjRef, + kObject, + }; + Type fType; - typedef SkPDFObject INHERITED; + SkPDFUnion(Type); + // We do not now need copy constructor and copy assignment, so we + // will disable this functionality. + SkPDFUnion& operator=(const SkPDFUnion&) = delete; + SkPDFUnion(const SkPDFUnion&) = delete; }; +SK_COMPILE_ASSERT(sizeof(SkString) == sizeof(void*), SkString_size); -/** \class SkPDFScalar +//////////////////////////////////////////////////////////////////////////////// - A real number object in a PDF. -*/ -class SkPDFScalar : public SkPDFObject { +/** This class is a SkPDFUnion with SkPDFObject virtuals attached. */ +// TODO(halcanary): 99% of the uses of this class should be +// transitioned to using a bare SkPDFUnion inside an array or dict. +class SkPDFAtom : public SkPDFObject { public: - SK_DECLARE_INST_COUNT(SkPDFScalar) + void emitObject(SkWStream* stream, + const SkPDFObjNumMap& objNumMap, + const SkPDFSubstituteMap& substitutes) final; + void addResources(SkPDFObjNumMap*, const SkPDFSubstituteMap&) const final; - /** Create a PDF real number. - * @param value A real value. - */ - explicit SkPDFScalar(SkScalar value); - virtual ~SkPDFScalar(); - - static void Append(SkScalar value, SkWStream* stream); - - // The SkPDFObject interface. - virtual void emitObject(SkWStream* stream, - const SkPDFObjNumMap& objNumMap, - const SkPDFSubstituteMap& substitutes) override; +protected: + SkPDFAtom(SkPDFUnion&& v) : fValue(v.move()) {} private: - SkScalar fValue; - + const SkPDFUnion fValue; typedef SkPDFObject INHERITED; }; -/** \class SkPDFString +/** The following six classes exist only to ease transition to SkPDFUnion. */ +class SkPDFObjRef : public SkPDFAtom { +public: + SK_DECLARE_INST_COUNT(SkPDFObjRef); + explicit SkPDFObjRef(SkPDFObject* obj) + : INHERITED(SkPDFUnion::ObjRef(SkRef(obj))) {} + typedef SkPDFAtom INHERITED; +}; - A string object in a PDF. -*/ -class SkPDFString : public SkPDFObject { +class SkPDFInt : public SkPDFAtom { public: - SK_DECLARE_INST_COUNT(SkPDFString) + SK_DECLARE_INST_COUNT(SkPDFInt); + explicit SkPDFInt(int32_t value) : INHERITED(SkPDFUnion::Int(value)) {} + typedef SkPDFAtom INHERITED; +}; - /** Create a PDF string. Maximum length (in bytes) is 65,535. - * @param value A string value. - */ - explicit SkPDFString(const char value[]); - explicit SkPDFString(const SkString& value); +class SkPDFBool : public SkPDFAtom { +public: + SK_DECLARE_INST_COUNT(SkPDFBool); + explicit SkPDFBool(bool value) : INHERITED(SkPDFUnion::Bool(value)) {} + typedef SkPDFAtom INHERITED; +}; - virtual ~SkPDFString(); +class SkPDFScalar : public SkPDFAtom { +public: + SK_DECLARE_INST_COUNT(SkPDFScalar); + explicit SkPDFScalar(SkScalar value) + : INHERITED(SkPDFUnion::Scalar(value)) {} + static void Append(SkScalar value, SkWStream* stream); + typedef SkPDFAtom INHERITED; +}; - // The SkPDFObject interface. - virtual void emitObject(SkWStream* stream, - const SkPDFObjNumMap& objNumMap, - const SkPDFSubstituteMap& substitutes) override; +class SkPDFString : public SkPDFAtom { +public: + SK_DECLARE_INST_COUNT(SkPDFString); + explicit SkPDFString(const char value[]) + : INHERITED(SkPDFUnion::String(value)) {} + explicit SkPDFString(const SkString& value) + : INHERITED(SkPDFUnion::String(value)) {} static SkString FormatString(const char* input, size_t len); -private: - static const size_t kMaxLen = 65535; - const SkString fValue; + static const size_t kMaxLen = 65535; - typedef SkPDFObject INHERITED; +private: + typedef SkPDFAtom INHERITED; }; -/** \class SkPDFName - - A name object in a PDF. -*/ -class SkPDFName : public SkPDFObject { +class SkPDFName : public SkPDFAtom { public: - SK_DECLARE_INST_COUNT(SkPDFName) - - /** Create a PDF name object. Maximum length is 127 bytes. - * @param value The name. - */ - explicit SkPDFName(const char name[]); - explicit SkPDFName(const SkString& name); - virtual ~SkPDFName(); - - bool operator==(const SkPDFName& b) const; - - // The SkPDFObject interface. - virtual void emitObject(SkWStream* stream, - const SkPDFObjNumMap& objNumMap, - const SkPDFSubstituteMap& substitutes) override; + SK_DECLARE_INST_COUNT(SkPDFName); + /** Create a PDF name object. Maximum length is 127 bytes. */ + explicit SkPDFName(const char name[]) + : INHERITED(SkPDFUnion::Name(SkString(name))) {} + explicit SkPDFName(const SkString& name) + : INHERITED(SkPDFUnion::Name(name)) {} -private: static const size_t kMaxLen = 127; - const SkString fValue; - - static SkString FormatName(const SkString& input); - - typedef SkPDFObject INHERITED; +private: + typedef SkPDFAtom INHERITED; }; /** \class SkPDFArray @@ -229,6 +249,8 @@ class SkPDFArray : public SkPDFObject { public: SK_DECLARE_INST_COUNT(SkPDFArray) + static const int kMaxLen = 8191; + /** Create a PDF array. Maximum length is 8191. */ SkPDFArray(); @@ -243,7 +265,7 @@ public: /** The size of the array. */ - int size() { return fValue.count(); } + int size() const; /** Preallocate space for the given number of entries. * @param length The number of array slots to preallocate. @@ -254,27 +276,26 @@ public: * @param value The value to add to the array. * @return The value argument is returned. */ + // DEPRECATED SkPDFObject* append(SkPDFObject* value); - /** Creates a SkPDFInt object and appends it to the array. - * @param value The value to add to the array. - */ - void appendInt(int32_t value); - - /** Creates a SkPDFScalar object and appends it to the array. + /** Appends a value to the end of the array. * @param value The value to add to the array. */ - void appendScalar(SkScalar value); - - /** Creates a SkPDFName object and appends it to the array. - * @param value The value to add to the array. - */ - void appendName(const char name[]); + void appendInt(int32_t); + void appendBool(bool); + void appendScalar(SkScalar); + void appendName(const char[]); + void appendName(const SkString&); + void appendString(const char[]); + void appendString(const SkString&); + /** appendObject and appendObjRef take ownership of the passed object */ + void appendObject(SkPDFObject*); + void appendObjRef(SkPDFObject*); private: - static const int kMaxLen = 8191; - SkTDArray fValue; - + SkTDArray fValues; + void append(SkPDFUnion&& value); typedef SkPDFObject INHERITED; }; @@ -313,104 +334,53 @@ public: * @param value The value for this dictionary entry. * @return The value argument is returned. */ + // DEPRECATED SkPDFObject* insert(SkPDFName* key, SkPDFObject* value); + // DEPRECATED + SkPDFObject* insert(const char key[], SkPDFObject* value); - /** Add the value to the dictionary with the given key. Refs value. The - * method will create the SkPDFName object. + /** Add the value to the dictionary with the given key. Takes + * ownership of the object. * @param key The text of the key for this dictionary entry. * @param value The value for this dictionary entry. - * @return The value argument is returned. */ - SkPDFObject* insert(const char key[], SkPDFObject* value); + void insertObject(const char key[], SkPDFObject* value); + void insertObject(const SkString& key, SkPDFObject* value); + void insertObjRef(const char key[], SkPDFObject* value); + void insertObjRef(const SkString& key, SkPDFObject* value); - /** Add the int to the dictionary with the given key. + /** Add the value to the dictionary with the given key. * @param key The text of the key for this dictionary entry. - * @param value The int value for this dictionary entry. + * @param value The value for this dictionary entry. */ void insertInt(const char key[], int32_t value); - - /** - * Calls insertInt() but asserts in debug builds that the value can be represented - * by an int32_t. - */ - void insertInt(const char key[], size_t value) { - this->insertInt(key, SkToS32(value)); - } - - /** Add the scalar to the dictionary with the given key. - * @param key The text of the key for this dictionary entry. - * @param value The scalar value for this dictionary entry. - */ + void insertInt(const char key[], size_t value); void insertScalar(const char key[], SkScalar value); - - /** Add the name to the dictionary with the given key. - * @param key The text of the key for this dictionary entry. - * @param name The name for this dictionary entry. - */ - void insertName(const char key[], const char name[]); - - /** Add the name to the dictionary with the given key. - * @param key The text of the key for this dictionary entry. - * @param name The name for this dictionary entry. - */ - void insertName(const char key[], const SkString& name) { - this->insertName(key, name.c_str()); - } + void insertName(const char key[], const char nameValue[]); + void insertName(const char key[], const SkString& nameValue); + void insertString(const char key[], const char value[]); + void insertString(const char key[], const SkString& value); /** Remove all entries from the dictionary. */ void clear(); -protected: - /** Use to remove a single key from the dictionary. - */ - void remove(const char key[]); - - /** Insert references to all of the key-value pairs from the other - * dictionary into this one. - */ - void mergeFrom(const SkPDFDict& other); - private: - struct Rec { - SkPDFName* key; - SkPDFObject* value; - Rec(SkPDFName* k, SkPDFObject* v) : key(k), value(v) {} + struct Record { + SkPDFUnion fKey; + SkPDFUnion fValue; }; - + SkTDArray fRecords; static const int kMaxLen = 4095; - SkTDArray fValue; - - SkPDFObject* append(SkPDFName* key, SkPDFObject* value); + void set(const SkPDFUnion& name, const SkPDFUnion& value); + void set(SkPDFUnion&& name, SkPDFUnion&& value); typedef SkPDFObject INHERITED; }; //////////////////////////////////////////////////////////////////////////////// -/** \class SkPDFSubstituteMap - - The PDF Substitute Map manages substitute objects and owns the - substitutes. -*/ -class SkPDFSubstituteMap : SkNoncopyable { -public: - ~SkPDFSubstituteMap(); - /** Set substitute object for the passed object. - Refs substitute. - */ - void setSubstitute(SkPDFObject* original, SkPDFObject* substitute); - - /** Find and return any substitute object set for the passed object. If - * there is none, return the passed object. - */ - SkPDFObject* getSubstitute(SkPDFObject* object) const; - -private: - SkTHashMap fSubstituteMap; -}; - /** \class SkPDFObjNumMap The PDF Object Number Map manages object numbers. It is used to @@ -436,4 +406,32 @@ private: SkTHashMap fObjectNumbers; }; +//////////////////////////////////////////////////////////////////////////////// + +/** \class SkPDFSubstituteMap + + The PDF Substitute Map manages substitute objects and owns the + substitutes. +*/ +class SkPDFSubstituteMap : SkNoncopyable { +public: + ~SkPDFSubstituteMap(); + /** Set substitute object for the passed object. + Refs substitute. + */ + void setSubstitute(SkPDFObject* original, SkPDFObject* substitute); + + /** Find and return any substitute object set for the passed object. If + * there is none, return the passed object. + */ + SkPDFObject* getSubstitute(SkPDFObject* object) const; + + SkPDFObject* operator()(SkPDFObject* o) const { + return this->getSubstitute(o); + } + +private: + SkTHashMap fSubstituteMap; +}; + #endif diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp index 99bf7ac..7f92e43 100644 --- a/tests/PDFPrimitivesTest.cpp +++ b/tests/PDFPrimitivesTest.cpp @@ -69,8 +69,12 @@ static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj, SkDynamicMemoryWStream buffer; emit_object(obj, &buffer, catalog, substituteMap, false); REPORTER_ASSERT(reporter, directSize == buffer.getOffset()); - REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData, - directSize)); + if (!stream_equals(buffer, 0, expectedData, directSize)) { + SkAutoTDelete asset(buffer.detachAsStream()); + SkString s(asset->getLength()); + asset->read(s.writable_str(), s.size()); + ERRORF(reporter, "!stream_equals() '%s' '%s'", expectedData, s.c_str()); + } if (indirect) { // Indirect output. @@ -272,13 +276,11 @@ DEF_TEST(PDFPrimitives, reporter) { SkAutoTUnref dict(new SkPDFDict); SimpleCheckObjectOutput(reporter, dict.get(), "<<>>"); - SkAutoTUnref n1(new SkPDFName("n1")); - dict->insert(n1.get(), int42.get()); + dict->insert("n1", int42.get()); SimpleCheckObjectOutput(reporter, dict.get(), "<>"); - SkAutoTUnref n2(new SkPDFName("n2")); - SkAutoTUnref n3(new SkPDFName("n3")); - dict->insert(n2.get(), realHalf.get()); - dict->insert(n3.get(), array.get()); + SkString n3("n3"); + dict->insert("n2", realHalf.get()); + dict->insertObject(n3, array.detach()); SimpleCheckObjectOutput(reporter, dict.get(), "<>");