blob: a01fcbaf5d4f9f8d676d1b0745478f946310e346 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "SkPDFCatalog.h"
#include "SkPDFTypes.h"
#include "SkStream.h"
SkPDFObject::SkPDFObject() {}
SkPDFObject::~SkPDFObject() {}
size_t SkPDFObject::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
SkDynamicMemoryWStream buffer;
emitObject(&buffer, catalog, indirect);
return buffer.getOffset();
}
void SkPDFObject::emitIndirectObject(SkWStream* stream, SkPDFCatalog* catalog) {
catalog->emitObjectNumber(stream, this);
stream->writeText(" obj\n");
emitObject(stream, catalog, false);
stream->writeText("\nendobj\n");
}
SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {}
SkPDFObjRef::~SkPDFObjRef() {}
size_t SkPDFObject::getIndirectOutputSize(SkPDFCatalog* catalog) {
return catalog->getObjectNumberSize(this) + strlen(" obj\n") +
this->getOutputSize(catalog, false) + strlen("\nendobj\n");
}
void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
bool indirect) {
SkASSERT(!indirect);
catalog->emitObjectNumber(stream, fObj.get());
stream->writeText(" R");
}
size_t SkPDFObjRef::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
SkASSERT(!indirect);
return catalog->getObjectNumberSize(fObj.get()) + strlen(" R");
}
SkPDFInt::SkPDFInt(int32_t value) : fValue(value) {}
SkPDFInt::~SkPDFInt() {}
void SkPDFInt::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
bool indirect) {
if (indirect)
return emitIndirectObject(stream, catalog);
stream->writeDecAsText(fValue);
}
SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {}
SkPDFScalar::~SkPDFScalar() {}
void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
bool indirect) {
if (indirect)
return emitIndirectObject(stream, catalog);
stream->writeScalarAsText(fValue);
}
SkPDFString::SkPDFString(const char value[])
: fValue(formatString(SkString(value))) {
}
SkPDFString::SkPDFString(const SkString& value)
: fValue(formatString(value)) {
}
SkPDFString::~SkPDFString() {}
void SkPDFString::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
bool indirect) {
if (indirect)
return emitIndirectObject(stream, catalog);
stream->write(fValue.c_str(), fValue.size());
}
size_t SkPDFString::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
if (indirect)
return getIndirectOutputSize(catalog);
return fValue.size();
}
SkString SkPDFString::formatString(const SkString& input) {
SkASSERT(input.size() <= kMaxLen);
// 7-bit clean is a heuristic to decide what string format to use;
// a 7-bit clean string should require little escaping.
bool sevenBitClean = true;
for (size_t i = 0; i < input.size(); i++) {
if (input[i] & 0x80 || input[i] < ' ') {
sevenBitClean = false;
break;
}
}
SkString result;
if (sevenBitClean) {
result.append("(");
for (size_t i = 0; i < input.size(); i++) {
if (input[i] == '\\' || input[i] == '(' || input[i] == ')')
result.append("\\");
result.append(input.c_str() + i, 1);
}
result.append(")");
} else {
result.append("<");
for (size_t i = 0; i < input.size(); i++)
result.appendHex(input[i], 2);
result.append(">");
}
return result;
}
SkPDFName::SkPDFName(const char name[]) : fValue(formatName(SkString(name))) {}
SkPDFName::SkPDFName(const SkString& name) : fValue(formatName(name)) {}
SkPDFName::~SkPDFName() {}
void SkPDFName::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
bool indirect) {
SkASSERT(!indirect);
stream->write(fValue.c_str(), fValue.size());
}
size_t SkPDFName::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
SkASSERT(!indirect);
return fValue.size();
}
// static
SkString SkPDFName::formatName(const SkString& input) {
SkASSERT(input.size() <= kMaxLen);
SkString result("/");
for (size_t i = 0; i < input.size(); i++) {
if (input[i] & 0x80 || input[i] < '!' || input[i] == '#') {
result.append("#");
result.appendHex(input[i], 2);
} else {
result.append(input.c_str() + i, 1);
}
}
return result;
}
SkPDFArray::SkPDFArray() {}
SkPDFArray::~SkPDFArray() {
fValue.safeUnrefAll();
}
void SkPDFArray::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
bool indirect) {
if (indirect)
return emitIndirectObject(stream, catalog);
stream->writeText("[");
for (int i = 0; i < fValue.count(); i++) {
fValue[i]->emitObject(stream, catalog, false);
if (i + 1 < fValue.count())
stream->writeText(" ");
}
stream->writeText("]");
}
size_t SkPDFArray::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
if (indirect)
return getIndirectOutputSize(catalog);
size_t result = strlen("[]");
if (fValue.count())
result += fValue.count() - 1;
for (int i = 0; i < fValue.count(); i++)
result += fValue[i]->getOutputSize(catalog, false);
return result;
}
void SkPDFArray::reserve(int length) {
SkASSERT(length <= kMaxLen);
fValue.setReserve(length);
}
void SkPDFArray::setAt(int offset, SkPDFObject* value) {
SkASSERT(offset < fValue.count());
SkSafeUnref(fValue[offset]);
fValue[offset] = value;
SkSafeRef(fValue[offset]);
}
void SkPDFArray::append(SkPDFObject* value) {
SkASSERT(fValue.count() < kMaxLen);
SkSafeRef(value);
fValue.push(value);
}
SkPDFDict::SkPDFDict() {}
SkPDFDict::SkPDFDict(const char type[]) {
SkRefPtr<SkPDFName> typeName = new SkPDFName(type);
typeName->unref(); // SkRefPtr and new both took a reference.
insert("Type", typeName.get());
}
SkPDFDict::~SkPDFDict() {
clear();
}
void SkPDFDict::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
bool indirect) {
if (indirect)
return emitIndirectObject(stream, catalog);
stream->writeText("<<");
for (int i = 0; i < fValue.count(); i++) {
fValue[i].key->emitObject(stream, catalog, false);
stream->writeText(" ");
fValue[i].value->emitObject(stream, catalog, false);
stream->writeText("\n");
}
stream->writeText(">>");
}
size_t SkPDFDict::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
if (indirect)
return getIndirectOutputSize(catalog);
size_t result = strlen("<<>>") + (fValue.count() * 2);
for (int i = 0; i < fValue.count(); i++) {
result += fValue[i].key->getOutputSize(catalog, false);
result += fValue[i].value->getOutputSize(catalog, false);
}
return result;
}
void SkPDFDict::insert(SkPDFName* key, SkPDFObject* value) {
struct Rec* newEntry = fValue.append();
newEntry->key = key;
SkSafeRef(newEntry->key);
newEntry->value = value;
SkSafeRef(newEntry->value);
}
void SkPDFDict::insert(const char key[], SkPDFObject* value) {
SkRefPtr<SkPDFName> keyName = new SkPDFName(key);
keyName->unref(); // SkRefPtr and new both took a reference.
insert(keyName.get(), value);
}
void SkPDFDict::clear() {
for (int i = 0; i < fValue.count(); i++) {
fValue[i].key->safeUnref();
fValue[i].value->safeUnref();
}
fValue.reset();
}