/* * 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/base/SkDebug.h" #include "include/private/base/SkTo.h" #include "src/base/SkUTF.h" #include "src/base/SkUtils.h" #include "src/core/SkStreamPriv.h" #include "src/pdf/SkDeflate.h" #include "src/pdf/SkPDFDocumentPriv.h" #include "src/pdf/SkPDFUnion.h" #include "src/pdf/SkPDFUtils.h" #include #include #include //////////////////////////////////////////////////////////////////////////////// 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(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(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(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(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("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(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 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(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::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&& 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::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 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)); SkStreamCopy(&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 dict, std::unique_ptr 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; }