Initial PDF backend commit: directories, build rules, primitives
This change establishes and tests the building blocks of the PDF file format.
For now, PDF code is not compiled by default.
Review URL: http://codereview.appspot.com/1950044
git-svn-id: http://skia.googlecode.com/svn/trunk@600 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
new file mode 100644
index 0000000..83f133e
--- /dev/null
+++ b/src/pdf/SkPDFTypes.cpp
@@ -0,0 +1,236 @@
+/*
+ * 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"
+
+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");
+}
+
+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");
+}
+
+void SkPDFInt::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ if (indirect)
+ return emitIndirectObject(stream, catalog);
+ stream->writeDecAsText(fValue);
+}
+
+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)) {
+}
+
+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] > 0x7F || 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)) {}
+
+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();
+}
+
+SkString SkPDFName::formatName(const SkString& input) {
+ SkASSERT(input.size() <= kMaxLen);
+
+ SkString result("/");
+ for (size_t i = 0; i < input.size(); i++) {
+ if (input[i] > 0x7F || input[i] < '!' || input[i] == '#') {
+ result.append("#");
+ result.appendHex(input[i], 2);
+ } else {
+ result.append(input.c_str() + i, 1);
+ }
+ }
+
+ return result;
+}
+
+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() {
+ for (int i = 0; i < fValue.count(); i++) {
+ SkSafeUnref(fValue[i].key);
+ SkSafeUnref(fValue[i].value);
+ }
+}
+
+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);
+}