/* * 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 "SkPDFCatalog.h" #include "SkPDFTypes.h" #include "SkStream.h" #ifdef SK_BUILD_FOR_WIN #define SNPRINTF _snprintf #else #define SNPRINTF snprintf #endif /////////////////////////////////////////////////////////////////////////////// void SkPDFObject::emit(SkWStream* stream, SkPDFCatalog* catalog, bool indirect) { SkPDFObject* realObject = catalog->getSubstituteObject(this); return realObject->emitObject(stream, catalog, indirect); } size_t SkPDFObject::getOutputSize(SkPDFCatalog* catalog, bool indirect) { SkDynamicMemoryWStream buffer; emit(&buffer, catalog, indirect); return buffer.getOffset(); } void SkPDFObject::getResources(const SkTSet& knownResourceObjects, SkTSet* newResourceObjects) {} void SkPDFObject::emitIndirectObject(SkWStream* stream, SkPDFCatalog* catalog) { catalog->emitObjectNumber(stream, this); stream->writeText(" obj\n"); emit(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 SkPDFObject::AddResourceHelper(SkPDFObject* resource, SkTDArray* list) { list->push(resource); resource->ref(); } void SkPDFObject::GetResourcesHelper( const SkTDArray* resources, const SkTSet& knownResourceObjects, SkTSet* newResourceObjects) { if (resources->count()) { newResourceObjects->setReserve( newResourceObjects->count() + resources->count()); for (int i = 0; i < resources->count(); i++) { if (!knownResourceObjects.contains((*resources)[i]) && !newResourceObjects->contains((*resources)[i])) { newResourceObjects->add((*resources)[i]); (*resources)[i]->ref(); (*resources)[i]->getResources(knownResourceObjects, newResourceObjects); } } } } SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) { SkSafeRef(obj); } SkPDFObjRef::~SkPDFObjRef() {} 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); } SkPDFBool::SkPDFBool(bool value) : fValue(value) {} SkPDFBool::~SkPDFBool() {} void SkPDFBool::emitObject(SkWStream* stream, SkPDFCatalog* catalog, bool indirect) { SkASSERT(!indirect); if (fValue) { stream->writeText("true"); } else { stream->writeText("false"); } } size_t SkPDFBool::getOutputSize(SkPDFCatalog* catalog, bool indirect) { SkASSERT(!indirect); if (fValue) { return strlen("true"); } return strlen("false"); } SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {} SkPDFScalar::~SkPDFScalar() {} void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog, bool indirect) { if (indirect) { return emitIndirectObject(stream, catalog); } Append(fValue, stream); } // static void SkPDFScalar::Append(SkScalar value, SkWStream* stream) { // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31). // When using floats that are outside the whole value range, we can use // integers instead. #if !defined(SK_ALLOW_LARGE_PDF_SCALARS) if (value > 32767 || value < -32767) { stream->writeDecAsText(SkScalarRoundToInt(value)); return; } char buffer[SkStrAppendScalar_MaxSize]; char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value)); stream->write(buffer, end - buffer); return; #endif // !SK_ALLOW_LARGE_PDF_SCALARS #if defined(SK_ALLOW_LARGE_PDF_SCALARS) // Floats have 24bits of significance, so anything outside that range is // no more precise than an int. (Plus PDF doesn't support scientific // notation, so this clamps to SK_Max/MinS32). if (value > (1 << 24) || value < -(1 << 24)) { stream->writeDecAsText(value); return; } // Continue to enforce the PDF limits for small floats. if (value < 1.0f/65536 && value > -1.0f/65536) { stream->writeDecAsText(0); return; } // SkStrAppendFloat might still use scientific notation, so use snprintf // directly.. static const int kFloat_MaxSize = 19; char buffer[kFloat_MaxSize]; int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value); // %f always prints trailing 0s, so strip them. for (; buffer[len - 1] == '0' && len > 0; len--) { buffer[len - 1] = '\0'; } if (buffer[len - 1] == '.') { buffer[len - 1] = '\0'; } stream->writeText(buffer); return; #endif // SK_ALLOW_LARGE_PDF_SCALARS } SkPDFString::SkPDFString(const char value[]) : fValue(FormatString(value, strlen(value))) { } SkPDFString::SkPDFString(const SkString& value) : fValue(FormatString(value.c_str(), value.size())) { } SkPDFString::SkPDFString(const uint16_t* value, size_t len, bool wideChars) : fValue(FormatString(value, len, wideChars)) { } 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(); } // static SkString SkPDFString::FormatString(const char* input, size_t len) { return DoFormatString(input, len, false, false); } SkString SkPDFString::FormatString(const uint16_t* input, size_t len, bool wideChars) { return DoFormatString(input, len, true, wideChars); } // static SkString SkPDFString::DoFormatString(const void* input, size_t len, bool wideInput, bool wideOutput) { SkASSERT(len <= kMaxLen); const uint16_t* win = (const uint16_t*) input; const char* cin = (const char*) input; if (wideOutput) { SkASSERT(wideInput); SkString result; result.append("<"); for (size_t i = 0; i < len; i++) { result.appendHex(win[i], 4); } result.append(">"); return result; } // 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 < len; i++) { SkASSERT(!wideInput || !(win[i] & ~0xFF)); char val = wideInput ? win[i] : cin[i]; if (val > '~' || val < ' ') { sevenBitClean = false; break; } } SkString result; if (sevenBitClean) { result.append("("); for (size_t i = 0; i < len; i++) { SkASSERT(!wideInput || !(win[i] & ~0xFF)); char val = wideInput ? win[i] : cin[i]; if (val == '\\' || val == '(' || val == ')') { result.append("\\"); } result.append(&val, 1); } result.append(")"); } else { result.append("<"); for (size_t i = 0; i < len; i++) { SkASSERT(!wideInput || !(win[i] & ~0xFF)); unsigned char val = wideInput ? win[i] : cin[i]; result.appendHex(val, 2); } result.append(">"); } return result; } SkPDFName::SkPDFName(const char name[]) : fValue(FormatName(SkString(name))) {} SkPDFName::SkPDFName(const SkString& name) : fValue(FormatName(name)) {} SkPDFName::~SkPDFName() {} bool SkPDFName::operator==(const SkPDFName& b) const { return fValue == b.fValue; } 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); // TODO(vandebo) If more escaping is needed, improve the linear scan. static const char escaped[] = "#/%()<>[]{}"; SkString result("/"); for (size_t i = 0; i < input.size(); i++) { if (input[i] & 0x80 || input[i] < '!' || strchr(escaped, input[i])) { result.append("#"); // Mask with 0xFF to avoid sign extension. i.e. #FFFFFF81 result.appendHex(input[i] & 0xFF, 2); } else { result.append(input.c_str() + i, 1); } } return result; } SkPDFArray::SkPDFArray() {} SkPDFArray::~SkPDFArray() { fValue.unrefAll(); } 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]->emit(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); } SkPDFObject* SkPDFArray::setAt(int offset, SkPDFObject* value) { SkASSERT(offset < fValue.count()); value->ref(); fValue[offset]->unref(); fValue[offset] = value; return value; } SkPDFObject* SkPDFArray::append(SkPDFObject* value) { SkASSERT(fValue.count() < kMaxLen); value->ref(); fValue.push(value); return value; } void SkPDFArray::appendInt(int32_t value) { SkASSERT(fValue.count() < kMaxLen); fValue.push(new SkPDFInt(value)); } void SkPDFArray::appendScalar(SkScalar value) { SkASSERT(fValue.count() < kMaxLen); fValue.push(new SkPDFScalar(value)); } void SkPDFArray::appendName(const char name[]) { SkASSERT(fValue.count() < kMaxLen); fValue.push(new SkPDFName(name)); } /////////////////////////////////////////////////////////////////////////////// SkPDFDict::SkPDFDict() {} SkPDFDict::SkPDFDict(const char type[]) { insertName("Type", type); } 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->emit(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; } SkPDFObject* SkPDFDict::insert(SkPDFName* key, SkPDFObject* value) { key->ref(); value->ref(); struct Rec* newEntry = fValue.append(); newEntry->key = key; newEntry->value = value; return value; } SkPDFObject* SkPDFDict::insert(const char key[], SkPDFObject* value) { value->ref(); struct Rec* newEntry = fValue.append(); newEntry->key = new SkPDFName(key); newEntry->value = value; return value; } void SkPDFDict::insertInt(const char key[], int32_t value) { struct Rec* newEntry = fValue.append(); newEntry->key = new SkPDFName(key); newEntry->value = new SkPDFInt(value); } void SkPDFDict::insertScalar(const char key[], SkScalar value) { struct Rec* newEntry = fValue.append(); newEntry->key = new SkPDFName(key); newEntry->value = new SkPDFScalar(value); } void SkPDFDict::insertName(const char key[], const char name[]) { struct Rec* newEntry = fValue.append(); newEntry->key = new SkPDFName(key); newEntry->value = new SkPDFName(name); } void SkPDFDict::clear() { for (int i = 0; i < fValue.count(); i++) { fValue[i].key->unref(); fValue[i].value->unref(); } fValue.reset(); } SkPDFDict::Iter::Iter(const SkPDFDict& dict) : fIter(dict.fValue.begin()), fStop(dict.fValue.end()) { } SkPDFName* SkPDFDict::Iter::next(SkPDFObject** value) { if (fIter != fStop) { const Rec* cur = fIter; fIter++; *value = cur->value; return cur->key; } *value = NULL; return NULL; }