1 2/* 3 * Copyright 2010 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkPDFCatalog.h" 11#include "SkPDFTypes.h" 12#include "SkStream.h" 13#include "SkTypes.h" 14 15SkPDFCatalog::SkPDFCatalog(SkPDFDocument::Flags flags) 16 : fFirstPageCount(0), 17 fNextObjNum(1), 18 fNextFirstPageObjNum(0), 19 fDocumentFlags(flags) { 20} 21 22SkPDFCatalog::~SkPDFCatalog() { 23 fSubstituteResourcesRemaining.safeUnrefAll(); 24 fSubstituteResourcesFirstPage.safeUnrefAll(); 25} 26 27SkPDFObject* SkPDFCatalog::addObject(SkPDFObject* obj, bool onFirstPage) { 28 if (findObjectIndex(obj) != -1) { // object already added 29 return obj; 30 } 31 SkASSERT(fNextFirstPageObjNum == 0); 32 if (onFirstPage) { 33 fFirstPageCount++; 34 } 35 36 struct Rec newEntry(obj, onFirstPage); 37 fCatalog.append(1, &newEntry); 38 return obj; 39} 40 41size_t SkPDFCatalog::setFileOffset(SkPDFObject* obj, off_t offset) { 42 int objIndex = assignObjNum(obj) - 1; 43 SkASSERT(fCatalog[objIndex].fObjNumAssigned); 44 SkASSERT(fCatalog[objIndex].fFileOffset == 0); 45 fCatalog[objIndex].fFileOffset = offset; 46 47 return getSubstituteObject(obj)->getOutputSize(this, true); 48} 49 50void SkPDFCatalog::emitObjectNumber(SkWStream* stream, SkPDFObject* obj) { 51 stream->writeDecAsText(assignObjNum(obj)); 52 stream->writeText(" 0"); // Generation number is always 0. 53} 54 55size_t SkPDFCatalog::getObjectNumberSize(SkPDFObject* obj) { 56 SkDynamicMemoryWStream buffer; 57 emitObjectNumber(&buffer, obj); 58 return buffer.getOffset(); 59} 60 61int SkPDFCatalog::findObjectIndex(SkPDFObject* obj) const { 62 for (int i = 0; i < fCatalog.count(); i++) { 63 if (fCatalog[i].fObject == obj) { 64 return i; 65 } 66 } 67 // If it's not in the main array, check if it's a substitute object. 68 for (int i = 0; i < fSubstituteMap.count(); ++i) { 69 if (fSubstituteMap[i].fSubstitute == obj) { 70 return findObjectIndex(fSubstituteMap[i].fOriginal); 71 } 72 } 73 return -1; 74} 75 76int SkPDFCatalog::assignObjNum(SkPDFObject* obj) { 77 int pos = findObjectIndex(obj); 78 // If this assert fails, it means you probably forgot to add an object 79 // to the resource list. 80 SkASSERT(pos >= 0); 81 uint32_t currentIndex = pos; 82 if (fCatalog[currentIndex].fObjNumAssigned) { 83 return currentIndex + 1; 84 } 85 86 // First assignment. 87 if (fNextFirstPageObjNum == 0) { 88 fNextFirstPageObjNum = fCatalog.count() - fFirstPageCount + 1; 89 } 90 91 uint32_t objNum; 92 if (fCatalog[currentIndex].fOnFirstPage) { 93 objNum = fNextFirstPageObjNum; 94 fNextFirstPageObjNum++; 95 } else { 96 objNum = fNextObjNum; 97 fNextObjNum++; 98 } 99 100 // When we assign an object an object number, we put it in that array 101 // offset (minus 1 because object number 0 is reserved). 102 SkASSERT(!fCatalog[objNum - 1].fObjNumAssigned); 103 if (objNum - 1 != currentIndex) { 104 SkTSwap(fCatalog[objNum - 1], fCatalog[currentIndex]); 105 } 106 fCatalog[objNum - 1].fObjNumAssigned = true; 107 return objNum; 108} 109 110int32_t SkPDFCatalog::emitXrefTable(SkWStream* stream, bool firstPage) { 111 int first = -1; 112 int last = fCatalog.count() - 1; 113 // TODO(vandebo): Support linearized format. 114 // int last = fCatalog.count() - fFirstPageCount - 1; 115 // if (firstPage) { 116 // first = fCatalog.count() - fFirstPageCount; 117 // last = fCatalog.count() - 1; 118 // } 119 120 stream->writeText("xref\n"); 121 stream->writeDecAsText(first + 1); 122 stream->writeText(" "); 123 stream->writeDecAsText(last - first + 1); 124 stream->writeText("\n"); 125 126 if (first == -1) { 127 stream->writeText("0000000000 65535 f \n"); 128 first++; 129 } 130 for (int i = first; i <= last; i++) { 131 // For 32 bits platforms, the maximum offset has to fit within off_t 132 // which is a 32 bits signed integer on these platforms. 133 SkDEBUGCODE(static const off_t kMaxOff = SK_MaxS32;) 134 SkASSERT(fCatalog[i].fFileOffset > 0); 135 SkASSERT(fCatalog[i].fFileOffset < kMaxOff); 136 stream->writeBigDecAsText(fCatalog[i].fFileOffset, 10); 137 stream->writeText(" 00000 n \n"); 138 } 139 140 return fCatalog.count() + 1; 141} 142 143void SkPDFCatalog::setSubstitute(SkPDFObject* original, 144 SkPDFObject* substitute) { 145#if defined(SK_DEBUG) 146 // Sanity check: is the original already in substitute list? 147 for (int i = 0; i < fSubstituteMap.count(); ++i) { 148 if (original == fSubstituteMap[i].fSubstitute || 149 original == fSubstituteMap[i].fOriginal) { 150 SkASSERT(false); 151 return; 152 } 153 } 154#endif 155 // Check if the original is on first page. 156 bool onFirstPage = false; 157 for (int i = 0; i < fCatalog.count(); ++i) { 158 if (fCatalog[i].fObject == original) { 159 onFirstPage = fCatalog[i].fOnFirstPage; 160 break; 161 } 162#if defined(SK_DEBUG) 163 if (i == fCatalog.count() - 1) { 164 SkASSERT(false); // original not in catalog 165 return; 166 } 167#endif 168 } 169 170 SubstituteMapping newMapping(original, substitute); 171 fSubstituteMap.append(1, &newMapping); 172 173 // Add resource objects of substitute object to catalog. 174 SkTSet<SkPDFObject*>* targetSet = getSubstituteList(onFirstPage); 175 SkTSet<SkPDFObject*> newResourceObjects; 176 newMapping.fSubstitute->getResources(*targetSet, &newResourceObjects); 177 for (int i = 0; i < newResourceObjects.count(); ++i) { 178 addObject(newResourceObjects[i], onFirstPage); 179 } 180 // mergeInto returns the number of duplicates. 181 // If there are duplicates, there is a bug and we mess ref counting. 182 SkDEBUGCODE(int duplicates =) targetSet->mergeInto(newResourceObjects); 183 SkASSERT(duplicates == 0); 184} 185 186SkPDFObject* SkPDFCatalog::getSubstituteObject(SkPDFObject* object) { 187 for (int i = 0; i < fSubstituteMap.count(); ++i) { 188 if (object == fSubstituteMap[i].fOriginal) { 189 return fSubstituteMap[i].fSubstitute; 190 } 191 } 192 return object; 193} 194 195off_t SkPDFCatalog::setSubstituteResourcesOffsets(off_t fileOffset, 196 bool firstPage) { 197 SkTSet<SkPDFObject*>* targetSet = getSubstituteList(firstPage); 198 off_t offsetSum = fileOffset; 199 for (int i = 0; i < targetSet->count(); ++i) { 200 offsetSum += setFileOffset((*targetSet)[i], offsetSum); 201 } 202 return offsetSum - fileOffset; 203} 204 205void SkPDFCatalog::emitSubstituteResources(SkWStream *stream, bool firstPage) { 206 SkTSet<SkPDFObject*>* targetSet = getSubstituteList(firstPage); 207 for (int i = 0; i < targetSet->count(); ++i) { 208 (*targetSet)[i]->emit(stream, this, true); 209 } 210} 211 212SkTSet<SkPDFObject*>* SkPDFCatalog::getSubstituteList(bool firstPage) { 213 return firstPage ? &fSubstituteResourcesFirstPage : 214 &fSubstituteResourcesRemaining; 215} 216