blob: 79c402a9ad9ffd382464d0dff4da21ee5cb3537c [file] [log] [blame]
/*
* 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/SkData.h"
#include "include/core/SkExecutor.h"
#include "include/core/SkStream.h"
#include "include/private/base/SkTo.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 <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, SkFloatToScalar(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 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<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)));
}
////////////////////////////////////////////////////////////////////////////////
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));
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<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;
}