/* * 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 "SkPDFTypes.h" #include "SkData.h" #include "SkDeflate.h" #include "SkExecutor.h" #include "SkMakeUnique.h" #include "SkPDFDocumentPriv.h" #include "SkPDFUnion.h" #include "SkPDFUtils.h" #include "SkStream.h" #include "SkStreamPriv.h" #include "SkTo.h" #include //////////////////////////////////////////////////////////////////////////////// SkPDFUnion::SkPDFUnion(Type t) : fType(t) {} 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, SkString v) : fType(t) { fSkString.init(std::move(v)); } SkPDFUnion::~SkPDFUnion() { switch (fType) { case Type::kNameSkS: case Type::kStringSkS: fSkString.destroy(); return; case Type::kObject: SkASSERT(fObject); delete fObject; return; default: return; } } SkPDFUnion& SkPDFUnion::operator=(SkPDFUnion&& other) { if (this != &other) { this->~SkPDFUnion(); new (this) SkPDFUnion(std::move(other)); } 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: u.fSkString.init(fSkString.get()); return u; case Type::kObject: SkRef(u.fObject); return u; default: return u; } } 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; } #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(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_string(SkWStream* wStream, const char* cin, size_t len) { SkDEBUGCODE(static const size_t kMaxLen = 65535;) SkASSERT(len <= kMaxLen); size_t extraCharacterCount = 0; for (size_t i = 0; i < len; i++) { if (cin[i] > '~' || cin[i] < ' ') { extraCharacterCount += 3; } else if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') { ++extraCharacterCount; } } if (extraCharacterCount <= len) { wStream->writeText("("); for (size_t i = 0; i < len; i++) { if (cin[i] > '~' || cin[i] < ' ') { uint8_t c = static_cast(cin[i]); 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 (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') { wStream->writeText("\\"); } wStream->write(&cin[i], 1); } } wStream->writeText(")"); } else { wStream->writeText("<"); for (size_t i = 0; i < len; i++) { uint8_t c = static_cast(cin[i]); char hexValue[2] = { SkHexadecimalDigits::gUpper[c >> 4], SkHexadecimalDigits::gUpper[c & 0xF] }; wStream->write(hexValue, 2); } wStream->writeText(">"); } } void SkPDFWriteString(SkWStream* wStream, const char* cin, size_t len) { write_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::kString: SkASSERT(fStaticString); write_string(stream, fStaticString, strlen(fStaticString)); return; case Type::kNameSkS: stream->writeText("/"); write_name_escaped(stream, fSkString.get().c_str()); return; case Type::kStringSkS: write_string(stream, fSkString.get().c_str(), fSkString.get().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, (int32_t)value); } SkPDFUnion SkPDFUnion::ColorComponentF(float value) { return SkPDFUnion(Type::kColorComponentF, (SkScalar)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) { SkPDFUnion u(Type::kName); SkASSERT(value); SkASSERT(is_valid_name(value)); u.fStaticString = value; return u; } SkPDFUnion SkPDFUnion::String(const char* value) { SkPDFUnion u(Type::kString); SkASSERT(value); u.fStaticString = value; return u; } SkPDFUnion SkPDFUnion::Name(SkString s) { return SkPDFUnion(Type::kNameSkS, std::move(s)); } SkPDFUnion SkPDFUnion::String(SkString s) { return SkPDFUnion(Type::kStringSkS, std::move(s)); } SkPDFUnion SkPDFUnion::Object(std::unique_ptr objSp) { SkPDFUnion u(Type::kObject); SkASSERT(objSp.get()); u.fObject = objSp.release(); // take ownership into union{} return u; } SkPDFUnion SkPDFUnion::Ref(SkPDFIndirectReference ref) { return SkASSERT(ref.fValue > 0), SkPDFUnion(Type::kRef, (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 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::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::appendString(SkString value) { this->append(SkPDFUnion::String(std::move(value))); } void SkPDFArray::appendString(const char value[]) { this->append(SkPDFUnion::String(value)); } void SkPDFArray::appendObject(std::unique_ptr&& 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& 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&& objSp) { fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Object(std::move(objSp))); } void SkPDFDict::insertObject(SkString key, std::unique_ptr&& 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::insertString(const char key[], const char value[]) { fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::String(value)); } void SkPDFDict::insertString(const char key[], SkString value) { fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::String(std::move(value))); } //////////////////////////////////////////////////////////////////////////////// static void serialize_stream(SkPDFDict* origDict, SkStreamAsset* stream, bool deflate, SkPDFDocument* doc, SkPDFIndirectReference ref) { // Code assumes that the stream starts at the beginning. SkASSERT(stream && stream->hasLength()); std::unique_ptr tmp; SkPDFDict tmpDict; SkPDFDict& dict = origDict ? *origDict : tmpDict; static const size_t kMinimumSavings = strlen("/Filter_/FlateDecode_"); if (deflate && stream->getLength() > kMinimumSavings) { SkDynamicMemoryWStream compressedData; SkDeflateWStream deflateWStream(&compressedData); SkStreamCopy(&deflateWStream, stream); deflateWStream.finalize(); #ifdef SK_PDF_BASE85_BINARY { SkPDFUtils::Base85Encode(compressedData.detachAsStream(), &compressedData); tmp = compressedData.detachAsStream(); stream = tmp.get(); auto filters = SkPDFMakeArray(); filters->appendName("ASCII85Decode"); filters->appendName("FlateDecode"); dict.insertObject("Filter", std::move(filters)); } #else if (stream->getLength() > compressedData.bytesWritten() + kMinimumSavings) { tmp = compressedData.detachAsStream(); stream = tmp.get(); dict.insertName("Filter", "FlateDecode"); } else { SkAssertResult(stream->rewind()); } #endif } dict.insertInt("Length", stream->getLength()); doc->emitStream(dict, [stream](SkWStream* dst) { dst->writeStream(stream, stream->getLength()); }, ref); } SkPDFIndirectReference SkPDFStreamOut(std::unique_ptr dict, std::unique_ptr content, SkPDFDocument* doc, bool deflate) { 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, deflate, doc, ref]() { serialize_stream(dictPtr, contentPtr, deflate, doc, ref); delete dictPtr; delete contentPtr; doc->signalJobComplete(); }); return ref; } serialize_stream(dict.get(), content.get(), deflate, doc, ref); return ref; }