blob: 2b2921d3fd6b6df144de4464c6dc689ed3b89c4f [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 "SkPDFTypes.h"
#include "SkPDFUtils.h"
#include "SkStream.h"
#ifdef SK_BUILD_FOR_WIN
#define SNPRINTF _snprintf
#else
#define SNPRINTF snprintf
#endif
////////////////////////////////////////////////////////////////////////////////
SkString* pun(char* x) { return reinterpret_cast<SkString*>(x); }
const SkString* pun(const char* x) {
return reinterpret_cast<const SkString*>(x);
}
SkPDFUnion::SkPDFUnion(Type t) : fType(t) {}
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;
}
}
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;
}
#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, 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());
}
static void write_string(SkWStream* o, const SkString& s) {
o->write(s.c_str(), s.size());
}
static SkString format_string(const SkString& s) {
return SkPDFUtils::FormatString(s.c_str(), s.size());
}
static SkString format_string(const char* s) {
return SkPDFUtils::FormatString(s, strlen(s));
}
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:
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, 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();
}
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();
}
////////////////////////////////////////////////////////////////////////////////
#if 0 // Enable if needed.
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);
}
#endif // 0
////////////////////////////////////////////////////////////////////////////////
SkPDFArray::SkPDFArray() {}
SkPDFArray::~SkPDFArray() {
for (SkPDFUnion& value : fValues) {
value.~SkPDFUnion();
}
fValues.reset();
}
int SkPDFArray::size() const { return fValues.count(); }
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 < fValues.count(); i++) {
fValues[i].emitObject(stream, objNumMap, substitutes);
if (i + 1 < fValues.count()) {
stream->writeText(" ");
}
}
stream->writeText("]");
}
void SkPDFArray::addResources(SkPDFObjNumMap* catalog,
const SkPDFSubstituteMap& substitutes) const {
for (const SkPDFUnion& value : fValues) {
value.addResources(catalog, substitutes);
}
}
void SkPDFArray::append(SkPDFUnion&& value) {
SkNEW_PLACEMENT_ARGS(fValues.append(), SkPDFUnion, (value.move()));
}
void SkPDFArray::appendInt(int32_t value) {
this->append(SkPDFUnion::Int(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(const SkString& name) {
this->append(SkPDFUnion::Name(name));
}
void SkPDFArray::appendString(const SkString& value) {
this->append(SkPDFUnion::String(value));
}
void SkPDFArray::appendString(const char value[]) {
this->append(SkPDFUnion::String(value));
}
void SkPDFArray::appendObject(SkPDFObject* value) {
this->append(SkPDFUnion::Object(value));
}
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 < fRecords.count(); i++) {
fRecords[i].fKey.emitObject(stream, objNumMap, substitutes);
stream->writeText(" ");
fRecords[i].fValue.emitObject(stream, objNumMap, substitutes);
if (i + 1 < fRecords.count()) {
stream->writeText("\n");
}
}
stream->writeText(">>");
}
void SkPDFDict::addResources(SkPDFObjNumMap* catalog,
const SkPDFSubstituteMap& substitutes) const {
for (int i = 0; i < fRecords.count(); i++) {
fRecords[i].fKey.addResources(catalog, substitutes);
fRecords[i].fValue.addResources(catalog, substitutes);
}
}
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));
}
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));
}
void SkPDFDict::insertBool(const char key[], bool value) {
this->set(SkPDFUnion::Name(key), SkPDFUnion::Bool(value));
}
void SkPDFDict::insertInt(const char key[], int32_t 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) {
this->set(SkPDFUnion::Name(key), SkPDFUnion::Scalar(value));
}
void SkPDFDict::insertName(const char key[], const char name[]) {
this->set(SkPDFUnion::Name(key), SkPDFUnion::Name(name));
}
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::clear() {
for (Record& rec : fRecords) {
rec.fKey.~SkPDFUnion();
rec.fValue.~SkPDFUnion();
}
fRecords.reset();
}
////////////////////////////////////////////////////////////////////////////////
SkPDFSubstituteMap::~SkPDFSubstituteMap() {
fSubstituteMap.foreach(
[](SkPDFObject*, SkPDFObject** v) { (*v)->unref(); });
}
void SkPDFSubstituteMap::setSubstitute(SkPDFObject* original,
SkPDFObject* substitute) {
SkASSERT(original != substitute);
SkASSERT(!fSubstituteMap.find(original));
fSubstituteMap.set(original, SkRef(substitute));
}
SkPDFObject* SkPDFSubstituteMap::getSubstitute(SkPDFObject* object) const {
SkPDFObject** found = fSubstituteMap.find(object);
return found ? *found : object;
}
////////////////////////////////////////////////////////////////////////////////
bool SkPDFObjNumMap::addObject(SkPDFObject* obj) {
if (fObjectNumbers.find(obj)) {
return false;
}
fObjectNumbers.set(obj, fObjectNumbers.count() + 1);
fObjects.push(obj);
return true;
}
int32_t SkPDFObjNumMap::getObjectNumber(SkPDFObject* obj) const {
int32_t* objectNumberFound = fObjectNumbers.find(obj);
SkASSERT(objectNumberFound);
return *objectNumberFound;
}