diff -r 000000000000 -r 6474c204b198 gfx/skia/trunk/src/pdf/SkPDFCatalog.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/skia/trunk/src/pdf/SkPDFCatalog.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,215 @@ + +/* + * Copyright 2010 The Android Open Source Project + * + * 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" +#include "SkTypes.h" + +SkPDFCatalog::SkPDFCatalog(SkPDFDocument::Flags flags) + : fFirstPageCount(0), + fNextObjNum(1), + fNextFirstPageObjNum(0), + fDocumentFlags(flags) { +} + +SkPDFCatalog::~SkPDFCatalog() { + fSubstituteResourcesRemaining.safeUnrefAll(); + fSubstituteResourcesFirstPage.safeUnrefAll(); +} + +SkPDFObject* SkPDFCatalog::addObject(SkPDFObject* obj, bool onFirstPage) { + if (findObjectIndex(obj) != -1) { // object already added + return obj; + } + SkASSERT(fNextFirstPageObjNum == 0); + if (onFirstPage) { + fFirstPageCount++; + } + + struct Rec newEntry(obj, onFirstPage); + fCatalog.append(1, &newEntry); + return obj; +} + +size_t SkPDFCatalog::setFileOffset(SkPDFObject* obj, off_t offset) { + int objIndex = assignObjNum(obj) - 1; + SkASSERT(fCatalog[objIndex].fObjNumAssigned); + SkASSERT(fCatalog[objIndex].fFileOffset == 0); + fCatalog[objIndex].fFileOffset = offset; + + return getSubstituteObject(obj)->getOutputSize(this, true); +} + +void SkPDFCatalog::emitObjectNumber(SkWStream* stream, SkPDFObject* obj) { + stream->writeDecAsText(assignObjNum(obj)); + stream->writeText(" 0"); // Generation number is always 0. +} + +size_t SkPDFCatalog::getObjectNumberSize(SkPDFObject* obj) { + SkDynamicMemoryWStream buffer; + emitObjectNumber(&buffer, obj); + return buffer.getOffset(); +} + +int SkPDFCatalog::findObjectIndex(SkPDFObject* obj) const { + for (int i = 0; i < fCatalog.count(); i++) { + if (fCatalog[i].fObject == obj) { + return i; + } + } + // If it's not in the main array, check if it's a substitute object. + for (int i = 0; i < fSubstituteMap.count(); ++i) { + if (fSubstituteMap[i].fSubstitute == obj) { + return findObjectIndex(fSubstituteMap[i].fOriginal); + } + } + return -1; +} + +int SkPDFCatalog::assignObjNum(SkPDFObject* obj) { + int pos = findObjectIndex(obj); + // If this assert fails, it means you probably forgot to add an object + // to the resource list. + SkASSERT(pos >= 0); + uint32_t currentIndex = pos; + if (fCatalog[currentIndex].fObjNumAssigned) { + return currentIndex + 1; + } + + // First assignment. + if (fNextFirstPageObjNum == 0) { + fNextFirstPageObjNum = fCatalog.count() - fFirstPageCount + 1; + } + + uint32_t objNum; + if (fCatalog[currentIndex].fOnFirstPage) { + objNum = fNextFirstPageObjNum; + fNextFirstPageObjNum++; + } else { + objNum = fNextObjNum; + fNextObjNum++; + } + + // When we assign an object an object number, we put it in that array + // offset (minus 1 because object number 0 is reserved). + SkASSERT(!fCatalog[objNum - 1].fObjNumAssigned); + if (objNum - 1 != currentIndex) { + SkTSwap(fCatalog[objNum - 1], fCatalog[currentIndex]); + } + fCatalog[objNum - 1].fObjNumAssigned = true; + return objNum; +} + +int32_t SkPDFCatalog::emitXrefTable(SkWStream* stream, bool firstPage) { + int first = -1; + int last = fCatalog.count() - 1; + // TODO(vandebo): Support linearized format. + // int last = fCatalog.count() - fFirstPageCount - 1; + // if (firstPage) { + // first = fCatalog.count() - fFirstPageCount; + // last = fCatalog.count() - 1; + // } + + stream->writeText("xref\n"); + stream->writeDecAsText(first + 1); + stream->writeText(" "); + stream->writeDecAsText(last - first + 1); + stream->writeText("\n"); + + if (first == -1) { + stream->writeText("0000000000 65535 f \n"); + first++; + } + for (int i = first; i <= last; i++) { + // For 32 bits platforms, the maximum offset has to fit within off_t + // which is a 32 bits signed integer on these platforms. + SkDEBUGCODE(static const off_t kMaxOff = SK_MaxS32;) + SkASSERT(fCatalog[i].fFileOffset > 0); + SkASSERT(fCatalog[i].fFileOffset < kMaxOff); + stream->writeBigDecAsText(fCatalog[i].fFileOffset, 10); + stream->writeText(" 00000 n \n"); + } + + return fCatalog.count() + 1; +} + +void SkPDFCatalog::setSubstitute(SkPDFObject* original, + SkPDFObject* substitute) { +#if defined(SK_DEBUG) + // Sanity check: is the original already in substitute list? + for (int i = 0; i < fSubstituteMap.count(); ++i) { + if (original == fSubstituteMap[i].fSubstitute || + original == fSubstituteMap[i].fOriginal) { + SkASSERT(false); + return; + } + } +#endif + // Check if the original is on first page. + bool onFirstPage = false; + for (int i = 0; i < fCatalog.count(); ++i) { + if (fCatalog[i].fObject == original) { + onFirstPage = fCatalog[i].fOnFirstPage; + break; + } +#if defined(SK_DEBUG) + if (i == fCatalog.count() - 1) { + SkASSERT(false); // original not in catalog + return; + } +#endif + } + + SubstituteMapping newMapping(original, substitute); + fSubstituteMap.append(1, &newMapping); + + // Add resource objects of substitute object to catalog. + SkTSet* targetSet = getSubstituteList(onFirstPage); + SkTSet newResourceObjects; + newMapping.fSubstitute->getResources(*targetSet, &newResourceObjects); + for (int i = 0; i < newResourceObjects.count(); ++i) { + addObject(newResourceObjects[i], onFirstPage); + } + // mergeInto returns the number of duplicates. + // If there are duplicates, there is a bug and we mess ref counting. + SkDEBUGCODE(int duplicates =) targetSet->mergeInto(newResourceObjects); + SkASSERT(duplicates == 0); +} + +SkPDFObject* SkPDFCatalog::getSubstituteObject(SkPDFObject* object) { + for (int i = 0; i < fSubstituteMap.count(); ++i) { + if (object == fSubstituteMap[i].fOriginal) { + return fSubstituteMap[i].fSubstitute; + } + } + return object; +} + +off_t SkPDFCatalog::setSubstituteResourcesOffsets(off_t fileOffset, + bool firstPage) { + SkTSet* targetSet = getSubstituteList(firstPage); + off_t offsetSum = fileOffset; + for (int i = 0; i < targetSet->count(); ++i) { + offsetSum += setFileOffset((*targetSet)[i], offsetSum); + } + return offsetSum - fileOffset; +} + +void SkPDFCatalog::emitSubstituteResources(SkWStream *stream, bool firstPage) { + SkTSet* targetSet = getSubstituteList(firstPage); + for (int i = 0; i < targetSet->count(); ++i) { + (*targetSet)[i]->emit(stream, this, true); + } +} + +SkTSet* SkPDFCatalog::getSubstituteList(bool firstPage) { + return firstPage ? &fSubstituteResourcesFirstPage : + &fSubstituteResourcesRemaining; +}