| /* | |
| * Copyright 2011 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/pdf/SkPDFTypes.h" | |
| #include "include/core/SkExecutor.h" | |
| #include "include/core/SkStream.h" | |
| #include "include/core/SkString.h" | |
| #include "include/docs/SkPDFDocument.h" | |
| #include "include/private/SkDebug.h" | |
| #include "include/private/SkTo.h" | |
| #include "src/core/SkStreamPriv.h" | |
| #include "src/core/SkUTF.h" | |
| #include "src/core/SkUtils.h" | |
| #include "src/pdf/SkDeflate.h" | |
| #include "src/pdf/SkPDFDocumentPriv.h" | |
| #include "src/pdf/SkPDFUnion.h" | |
| #include "src/pdf/SkPDFUtils.h" | |
| #include <cstring> | |
| #include <functional> | |
| #include <new> | |
| //////////////////////////////////////////////////////////////////////////////// | |
| SkPDFUnion::SkPDFUnion(Type t, int32_t v) : fIntValue (v) , fType(t) {} | |
| SkPDFUnion::SkPDFUnion(Type t, bool v) : fBoolValue (v) , fType(t) {} | |
| SkPDFUnion::SkPDFUnion(Type t, SkScalar v) : fScalarValue (v) , fType(t) {} | |
| SkPDFUnion::SkPDFUnion(Type t, const char* v) : fStaticString (v) , fType(t) {} | |
| SkPDFUnion::SkPDFUnion(Type t, SkString v) : fSkString(std::move(v)), fType(t) {} | |
| SkPDFUnion::SkPDFUnion(Type t, PDFObject v) : fObject (std::move(v)), fType(t) {} | |
| SkPDFUnion::~SkPDFUnion() { | |
| switch (fType) { | |
| case Type::kNameSkS: | |
| case Type::kByteStringSkS: | |
| case Type::kTextStringSkS: | |
| fSkString.~SkString(); | |
| return; | |
| case Type::kObject: | |
| fObject.~PDFObject(); | |
| return; | |
| default: | |
| return; | |
| } | |
| } | |
| SkPDFUnion::SkPDFUnion(SkPDFUnion&& that) : fType(that.fType) { | |
| SkASSERT(this != &that); | |
| switch (fType) { | |
| case Type::kDestroyed: | |
| break; | |
| case Type::kInt: | |
| case Type::kColorComponent: | |
| case Type::kRef: | |
| fIntValue = that.fIntValue; | |
| break; | |
| case Type::kBool: | |
| fBoolValue = that.fBoolValue; | |
| break; | |
| case Type::kColorComponentF: | |
| case Type::kScalar: | |
| fScalarValue = that.fScalarValue; | |
| break; | |
| case Type::kName: | |
| case Type::kByteString: | |
| case Type::kTextString: | |
| fStaticString = that.fStaticString; | |
| break; | |
| case Type::kNameSkS: | |
| case Type::kByteStringSkS: | |
| case Type::kTextStringSkS: | |
| new (&fSkString) SkString(std::move(that.fSkString)); | |
| break; | |
| case Type::kObject: | |
| new (&fObject) PDFObject(std::move(that.fObject)); | |
| break; | |
| default: | |
| SkDEBUGFAIL("SkPDFUnion::SkPDFUnion with bad type"); | |
| } | |
| that.fType = Type::kDestroyed; | |
| } | |
| SkPDFUnion& SkPDFUnion::operator=(SkPDFUnion&& that) { | |
| if (this != &that) { | |
| this->~SkPDFUnion(); | |
| new (this) SkPDFUnion(std::move(that)); | |
| } | |
| return *this; | |
| } | |
| bool SkPDFUnion::isName() const { | |
| return Type::kName == fType || Type::kNameSkS == fType; | |
| } | |
| #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 | |
| // Given an arbitrary string, write it as a valid name (not including leading slash). | |
| static void write_name_escaped(SkWStream* o, const char* name) { | |
| static const char kToEscape[] = "#/%()<>[]{}"; | |
| for (const uint8_t* n = reinterpret_cast<const uint8_t*>(name); *n; ++n) { | |
| uint8_t v = *n; | |
| if (v < '!' || v > '~' || strchr(kToEscape, v)) { | |
| char buffer[3] = {'#', | |
| SkHexadecimalDigits::gUpper[v >> 4], | |
| SkHexadecimalDigits::gUpper[v & 0xF]}; | |
| o->write(buffer, sizeof(buffer)); | |
| } else { | |
| o->write(n, 1); | |
| } | |
| } | |
| } | |
| static void write_literal_byte_string(SkWStream* wStream, const char* cin, size_t len) { | |
| wStream->writeText("("); | |
| for (size_t i = 0; i < len; i++) { | |
| uint8_t c = static_cast<uint8_t>(cin[i]); | |
| if (c < ' ' || '~' < c) { | |
| uint8_t octal[4] = { '\\', | |
| (uint8_t)('0' | ( c >> 6 )), | |
| (uint8_t)('0' | ((c >> 3) & 0x07)), | |
| (uint8_t)('0' | ( c & 0x07)) }; | |
| wStream->write(octal, 4); | |
| } else { | |
| if (c == '\\' || c == '(' || c == ')') { | |
| wStream->writeText("\\"); | |
| } | |
| wStream->write(&c, 1); | |
| } | |
| } | |
| wStream->writeText(")"); | |
| } | |
| static void write_hex_byte_string(SkWStream* wStream, const char* cin, size_t len) { | |
| SkDEBUGCODE(static const size_t kMaxLen = 65535;) | |
| SkASSERT(len <= kMaxLen); | |
| wStream->writeText("<"); | |
| for (size_t i = 0; i < len; i++) { | |
| uint8_t c = static_cast<uint8_t>(cin[i]); | |
| char hexValue[2] = { SkHexadecimalDigits::gUpper[c >> 4], | |
| SkHexadecimalDigits::gUpper[c & 0xF] }; | |
| wStream->write(hexValue, 2); | |
| } | |
| wStream->writeText(">"); | |
| } | |
| static void write_optimized_byte_string(SkWStream* wStream, const char* cin, size_t len, | |
| size_t literalExtras) { | |
| const size_t hexLength = 2 + 2*len; | |
| const size_t literalLength = 2 + len + literalExtras; | |
| if (literalLength <= hexLength) { | |
| write_literal_byte_string(wStream, cin, len); | |
| } else { | |
| write_hex_byte_string(wStream, cin, len); | |
| } | |
| } | |
| static void write_byte_string(SkWStream* wStream, const char* cin, size_t len) { | |
| SkDEBUGCODE(static const size_t kMaxLen = 65535;) | |
| SkASSERT(len <= kMaxLen); | |
| size_t literalExtras = 0; | |
| { | |
| for (size_t i = 0; i < len; i++) { | |
| uint8_t c = static_cast<uint8_t>(cin[i]); | |
| if (c < ' ' || '~' < c) { | |
| literalExtras += 3; | |
| } else if (c == '\\' || c == '(' || c == ')') { | |
| ++literalExtras; | |
| } | |
| } | |
| } | |
| write_optimized_byte_string(wStream, cin, len, literalExtras); | |
| } | |
| static void write_text_string(SkWStream* wStream, const char* cin, size_t len) { | |
| SkDEBUGCODE(static const size_t kMaxLen = 65535;) | |
| SkASSERT(len <= kMaxLen); | |
| bool inputIsValidUTF8 = true; | |
| bool inputIsPDFDocEncoding = true; | |
| size_t literalExtras = 0; | |
| { | |
| const char* textPtr = cin; | |
| const char* textEnd = cin + len; | |
| while (textPtr < textEnd) { | |
| SkUnichar unichar = SkUTF::NextUTF8(&textPtr, textEnd); | |
| if (unichar < 0) { | |
| inputIsValidUTF8 = false; | |
| break; | |
| } | |
| // See Table D.2 (PDFDocEncoding Character Set) in the PDF3200_2008 spec. | |
| // Could convert from UTF-8 to PDFDocEncoding and, if successful, use that. | |
| if ((0x15 < unichar && unichar < 0x20) || 0x7E < unichar) { | |
| inputIsPDFDocEncoding = false; | |
| break; | |
| } | |
| if (unichar < ' ' || '~' < unichar) { | |
| literalExtras += 3; | |
| } else if (unichar == '\\' || unichar == '(' || unichar == ')') { | |
| ++literalExtras; | |
| } | |
| } | |
| } | |
| if (!inputIsValidUTF8) { | |
| SkDebugf("Invalid UTF8: %.*s\n", (int)len, cin); | |
| wStream->writeText("<>"); | |
| return; | |
| } | |
| if (inputIsPDFDocEncoding) { | |
| write_optimized_byte_string(wStream, cin, len, literalExtras); | |
| return; | |
| } | |
| wStream->writeText("<FEFF"); | |
| const char* textPtr = cin; | |
| const char* textEnd = cin + len; | |
| while (textPtr < textEnd) { | |
| SkUnichar unichar = SkUTF::NextUTF8(&textPtr, textEnd); | |
| SkPDFUtils::WriteUTF16beHex(wStream, unichar); | |
| } | |
| wStream->writeText(">"); | |
| } | |
| void SkPDFWriteTextString(SkWStream* wStream, const char* cin, size_t len) { | |
| write_text_string(wStream, cin, len); | |
| } | |
| void SkPDFWriteByteString(SkWStream* wStream, const char* cin, size_t len) { | |
| write_byte_string(wStream, cin, len); | |
| } | |
| void SkPDFUnion::emitObject(SkWStream* stream) const { | |
| switch (fType) { | |
| case Type::kInt: | |
| stream->writeDecAsText(fIntValue); | |
| return; | |
| case Type::kColorComponent: | |
| SkPDFUtils::AppendColorComponent(SkToU8(fIntValue), stream); | |
| return; | |
| case Type::kColorComponentF: | |
| SkPDFUtils::AppendColorComponentF(fScalarValue, stream); | |
| return; | |
| case Type::kBool: | |
| stream->writeText(fBoolValue ? "true" : "false"); | |
| return; | |
| case Type::kScalar: | |
| SkPDFUtils::AppendScalar(fScalarValue, stream); | |
| return; | |
| case Type::kName: | |
| stream->writeText("/"); | |
| SkASSERT(is_valid_name(fStaticString)); | |
| stream->writeText(fStaticString); | |
| return; | |
| case Type::kByteString: | |
| SkASSERT(fStaticString); | |
| write_byte_string(stream, fStaticString, strlen(fStaticString)); | |
| return; | |
| case Type::kTextString: | |
| SkASSERT(fStaticString); | |
| write_text_string(stream, fStaticString, strlen(fStaticString)); | |
| return; | |
| case Type::kNameSkS: | |
| stream->writeText("/"); | |
| write_name_escaped(stream, fSkString.c_str()); | |
| return; | |
| case Type::kByteStringSkS: | |
| write_byte_string(stream, fSkString.c_str(), fSkString.size()); | |
| return; | |
| case Type::kTextStringSkS: | |
| write_text_string(stream, fSkString.c_str(), fSkString.size()); | |
| return; | |
| case Type::kObject: | |
| fObject->emitObject(stream); | |
| return; | |
| case Type::kRef: | |
| SkASSERT(fIntValue >= 0); | |
| stream->writeDecAsText(fIntValue); | |
| stream->writeText(" 0 R"); // Generation number is always 0. | |
| return; | |
| default: | |
| SkDEBUGFAIL("SkPDFUnion::emitObject with bad type"); | |
| } | |
| } | |
| SkPDFUnion SkPDFUnion::Int(int32_t value) { | |
| return SkPDFUnion(Type::kInt, value); | |
| } | |
| SkPDFUnion SkPDFUnion::ColorComponent(uint8_t value) { | |
| return SkPDFUnion(Type::kColorComponent, SkTo<int32_t>(value)); | |
| } | |
| SkPDFUnion SkPDFUnion::ColorComponentF(float value) { | |
| return SkPDFUnion(Type::kColorComponentF, value); | |
| } | |
| SkPDFUnion SkPDFUnion::Bool(bool value) { | |
| return SkPDFUnion(Type::kBool, value); | |
| } | |
| SkPDFUnion SkPDFUnion::Scalar(SkScalar value) { | |
| return SkPDFUnion(Type::kScalar, value); | |
| } | |
| SkPDFUnion SkPDFUnion::Name(const char* value) { | |
| SkASSERT(value); | |
| SkASSERT(is_valid_name(value)); | |
| return SkPDFUnion(Type::kName, value); | |
| } | |
| SkPDFUnion SkPDFUnion::ByteString(const char* value) { | |
| SkASSERT(value); | |
| return SkPDFUnion(Type::kByteString, value); | |
| } | |
| SkPDFUnion SkPDFUnion::TextString(const char* value) { | |
| SkASSERT(value); | |
| return SkPDFUnion(Type::kTextString, value); | |
| } | |
| SkPDFUnion SkPDFUnion::Name(SkString s) { | |
| return SkPDFUnion(Type::kNameSkS, std::move(s)); | |
| } | |
| SkPDFUnion SkPDFUnion::ByteString(SkString s) { | |
| return SkPDFUnion(Type::kByteStringSkS, std::move(s)); | |
| } | |
| SkPDFUnion SkPDFUnion::TextString(SkString s) { | |
| return SkPDFUnion(Type::kTextStringSkS, std::move(s)); | |
| } | |
| SkPDFUnion SkPDFUnion::Object(std::unique_ptr<SkPDFObject> objSp) { | |
| SkASSERT(objSp.get()); | |
| return SkPDFUnion(Type::kObject, std::move(objSp)); | |
| } | |
| SkPDFUnion SkPDFUnion::Ref(SkPDFIndirectReference ref) { | |
| SkASSERT(ref.fValue > 0); | |
| return SkPDFUnion(Type::kRef, SkTo<int32_t>(ref.fValue)); | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| #if 0 // Enable if needed. | |
| void SkPDFAtom::emitObject(SkWStream* stream) const { | |
| fValue.emitObject(stream); | |
| } | |
| #endif // 0 | |
| //////////////////////////////////////////////////////////////////////////////// | |
| SkPDFArray::SkPDFArray() {} | |
| SkPDFArray::~SkPDFArray() {} | |
| size_t SkPDFArray::size() const { return fValues.size(); } | |
| void SkPDFArray::reserve(int length) { | |
| fValues.reserve(length); | |
| } | |
| void SkPDFArray::emitObject(SkWStream* stream) const { | |
| stream->writeText("["); | |
| for (size_t i = 0; i < fValues.size(); i++) { | |
| fValues[i].emitObject(stream); | |
| if (i + 1 < fValues.size()) { | |
| stream->writeText(" "); | |
| } | |
| } | |
| stream->writeText("]"); | |
| } | |
| void SkPDFOptionalArray::emitObject(SkWStream* stream) const { | |
| if (this->size() == 1) { | |
| this->values()[0].emitObject(stream); | |
| } else { | |
| this->SkPDFArray::emitObject(stream); | |
| } | |
| } | |
| void SkPDFArray::append(SkPDFUnion&& value) { | |
| fValues.emplace_back(std::move(value)); | |
| } | |
| void SkPDFArray::appendInt(int32_t value) { | |
| this->append(SkPDFUnion::Int(value)); | |
| } | |
| void SkPDFArray::appendColorComponent(uint8_t value) { | |
| this->append(SkPDFUnion::ColorComponent(value)); | |
| } | |
| void SkPDFArray::appendColorComponentF(float value) { | |
| this->append(SkPDFUnion::ColorComponentF(value)); | |
| } | |
| void SkPDFArray::appendBool(bool value) { | |
| this->append(SkPDFUnion::Bool(value)); | |
| } | |
| void SkPDFArray::appendScalar(SkScalar value) { | |
| this->append(SkPDFUnion::Scalar(value)); | |
| } | |
| void SkPDFArray::appendName(const char name[]) { | |
| this->append(SkPDFUnion::Name(SkString(name))); | |
| } | |
| void SkPDFArray::appendName(SkString name) { | |
| this->append(SkPDFUnion::Name(std::move(name))); | |
| } | |
| void SkPDFArray::appendByteString(SkString value) { | |
| this->append(SkPDFUnion::ByteString(std::move(value))); | |
| } | |
| void SkPDFArray::appendTextString(SkString value) { | |
| this->append(SkPDFUnion::TextString(std::move(value))); | |
| } | |
| void SkPDFArray::appendByteString(const char value[]) { | |
| this->append(SkPDFUnion::ByteString(value)); | |
| } | |
| void SkPDFArray::appendTextString(const char value[]) { | |
| this->append(SkPDFUnion::TextString(value)); | |
| } | |
| void SkPDFArray::appendObject(std::unique_ptr<SkPDFObject>&& objSp) { | |
| this->append(SkPDFUnion::Object(std::move(objSp))); | |
| } | |
| void SkPDFArray::appendRef(SkPDFIndirectReference ref) { | |
| this->append(SkPDFUnion::Ref(ref)); | |
| } | |
| /////////////////////////////////////////////////////////////////////////////// | |
| SkPDFDict::~SkPDFDict() {} | |
| SkPDFDict::SkPDFDict(const char type[]) { | |
| if (type) { | |
| this->insertName("Type", type); | |
| } | |
| } | |
| void SkPDFDict::emitObject(SkWStream* stream) const { | |
| stream->writeText("<<"); | |
| for (size_t i = 0; i < fRecords.size(); ++i) { | |
| const std::pair<SkPDFUnion, SkPDFUnion>& record = fRecords[i]; | |
| record.first.emitObject(stream); | |
| stream->writeText(" "); | |
| record.second.emitObject(stream); | |
| if (i + 1 < fRecords.size()) { | |
| stream->writeText("\n"); | |
| } | |
| } | |
| stream->writeText(">>"); | |
| } | |
| size_t SkPDFDict::size() const { return fRecords.size(); } | |
| void SkPDFDict::reserve(int n) { | |
| fRecords.reserve(n); | |
| } | |
| void SkPDFDict::insertRef(const char key[], SkPDFIndirectReference ref) { | |
| fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Ref(ref)); | |
| } | |
| void SkPDFDict::insertRef(SkString key, SkPDFIndirectReference ref) { | |
| fRecords.emplace_back(SkPDFUnion::Name(std::move(key)), SkPDFUnion::Ref(ref)); | |
| } | |
| void SkPDFDict::insertObject(const char key[], std::unique_ptr<SkPDFObject>&& objSp) { | |
| fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Object(std::move(objSp))); | |
| } | |
| void SkPDFDict::insertObject(SkString key, std::unique_ptr<SkPDFObject>&& objSp) { | |
| fRecords.emplace_back(SkPDFUnion::Name(std::move(key)), | |
| SkPDFUnion::Object(std::move(objSp))); | |
| } | |
| void SkPDFDict::insertBool(const char key[], bool value) { | |
| fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Bool(value)); | |
| } | |
| void SkPDFDict::insertInt(const char key[], int32_t value) { | |
| fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Int(value)); | |
| } | |
| void SkPDFDict::insertInt(const char key[], size_t value) { | |
| this->insertInt(key, SkToS32(value)); | |
| } | |
| void SkPDFDict::insertColorComponentF(const char key[], SkScalar value) { | |
| fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::ColorComponentF(value)); | |
| } | |
| void SkPDFDict::insertScalar(const char key[], SkScalar value) { | |
| fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Scalar(value)); | |
| } | |
| void SkPDFDict::insertName(const char key[], const char name[]) { | |
| fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Name(name)); | |
| } | |
| void SkPDFDict::insertName(const char key[], SkString name) { | |
| fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Name(std::move(name))); | |
| } | |
| void SkPDFDict::insertByteString(const char key[], const char value[]) { | |
| fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::ByteString(value)); | |
| } | |
| void SkPDFDict::insertTextString(const char key[], const char value[]) { | |
| fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::TextString(value)); | |
| } | |
| void SkPDFDict::insertByteString(const char key[], SkString value) { | |
| fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::ByteString(std::move(value))); | |
| } | |
| void SkPDFDict::insertTextString(const char key[], SkString value) { | |
| fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::TextString(std::move(value))); | |
| } | |
| void SkPDFDict::insertUnion(const char key[], SkPDFUnion&& value) { | |
| fRecords.emplace_back(SkPDFUnion::Name(key), std::move(value)); | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| static void serialize_stream(SkPDFDict* origDict, | |
| SkStreamAsset* stream, | |
| SkPDFSteamCompressionEnabled compress, | |
| SkPDFDocument* doc, | |
| SkPDFIndirectReference ref) { | |
| // Code assumes that the stream starts at the beginning. | |
| SkASSERT(stream && stream->hasLength()); | |
| std::unique_ptr<SkStreamAsset> tmp; | |
| SkPDFDict tmpDict; | |
| SkPDFDict& dict = origDict ? *origDict : tmpDict; | |
| static const size_t kMinimumSavings = strlen("/Filter_/FlateDecode_"); | |
| if (doc->metadata().fCompressionLevel != SkPDF::Metadata::CompressionLevel::None && | |
| compress == SkPDFSteamCompressionEnabled::Yes && | |
| stream->getLength() > kMinimumSavings) | |
| { | |
| SkDynamicMemoryWStream compressedData; | |
| SkDeflateWStream deflateWStream(&compressedData,SkToInt(doc->metadata().fCompressionLevel)); | |
| SkStreamPriv::Copy(&deflateWStream, stream); | |
| deflateWStream.finalize(); | |
| if (stream->getLength() > compressedData.bytesWritten() + kMinimumSavings) { | |
| tmp = compressedData.detachAsStream(); | |
| stream = tmp.get(); | |
| dict.insertName("Filter", "FlateDecode"); | |
| } else { | |
| SkAssertResult(stream->rewind()); | |
| } | |
| } | |
| dict.insertInt("Length", stream->getLength()); | |
| doc->emitStream(dict, | |
| [stream](SkWStream* dst) { dst->writeStream(stream, stream->getLength()); }, | |
| ref); | |
| } | |
| SkPDFIndirectReference SkPDFStreamOut(std::unique_ptr<SkPDFDict> dict, | |
| std::unique_ptr<SkStreamAsset> content, | |
| SkPDFDocument* doc, | |
| SkPDFSteamCompressionEnabled compress) { | |
| SkPDFIndirectReference ref = doc->reserveRef(); | |
| if (SkExecutor* executor = doc->executor()) { | |
| SkPDFDict* dictPtr = dict.release(); | |
| SkStreamAsset* contentPtr = content.release(); | |
| // Pass ownership of both pointers into a std::function, which should | |
| // only be executed once. | |
| doc->incrementJobCount(); | |
| executor->add([dictPtr, contentPtr, compress, doc, ref]() { | |
| serialize_stream(dictPtr, contentPtr, compress, doc, ref); | |
| delete dictPtr; | |
| delete contentPtr; | |
| doc->signalJobComplete(); | |
| }); | |
| return ref; | |
| } | |
| serialize_stream(dict.get(), content.get(), compress, doc, ref); | |
| return ref; | |
| } |