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 15 SkPDFCatalog::SkPDFCatalog(SkPDFDocument::Flags flags) 16 : fFirstPageCount(0), 17 fNextObjNum(1), 18 fNextFirstPageObjNum(0), 19 fDocumentFlags(flags) { 20 } 21 22 SkPDFCatalog::~SkPDFCatalog() { 23 fSubstituteResourcesRemaining.safeUnrefAll(); 24 fSubstituteResourcesFirstPage.safeUnrefAll(); 25 } 26 27 SkPDFObject* 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 41 size_t SkPDFCatalog::setFileOffset(SkPDFObject* obj, size_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 50 void SkPDFCatalog::emitObjectNumber(SkWStream* stream, SkPDFObject* obj) { 51 stream->writeDecAsText(assignObjNum(obj)); 52 stream->writeText(" 0"); // Generation number is always 0. 53 } 54 55 size_t SkPDFCatalog::getObjectNumberSize(SkPDFObject* obj) { 56 SkDynamicMemoryWStream buffer; 57 emitObjectNumber(&buffer, obj); 58 return buffer.getOffset(); 59 } 60 61 int 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 76 int 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 110 int32_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 SkASSERT(fCatalog[i].fFileOffset > 0); 132 SkASSERT(fCatalog[i].fFileOffset <= 9999999999LL); 133 stream->writeBigDecAsText(fCatalog[i].fFileOffset, 10); 134 stream->writeText(" 00000 n \n"); 135 } 136 137 return fCatalog.count() + 1; 138 } 139 140 void SkPDFCatalog::setSubstitute(SkPDFObject* original, 141 SkPDFObject* substitute) { 142 #if defined(SK_DEBUG) 143 // Sanity check: is the original already in substitute list? 144 for (int i = 0; i < fSubstituteMap.count(); ++i) { 145 if (original == fSubstituteMap[i].fSubstitute || 146 original == fSubstituteMap[i].fOriginal) { 147 SkASSERT(false); 148 return; 149 } 150 } 151 #endif 152 // Check if the original is on first page. 153 bool onFirstPage = false; 154 for (int i = 0; i < fCatalog.count(); ++i) { 155 if (fCatalog[i].fObject == original) { 156 onFirstPage = fCatalog[i].fOnFirstPage; 157 break; 158 } 159 #if defined(SK_DEBUG) 160 if (i == fCatalog.count() - 1) { 161 SkASSERT(false); // original not in catalog 162 return; 163 } 164 #endif 165 } 166 167 SubstituteMapping newMapping(original, substitute); 168 fSubstituteMap.append(1, &newMapping); 169 170 // Add resource objects of substitute object to catalog. 171 SkTDArray<SkPDFObject*>* targetList = getSubstituteList(onFirstPage); 172 int existingSize = targetList->count(); 173 newMapping.fSubstitute->getResources(targetList); 174 for (int i = existingSize; i < targetList->count(); ++i) { 175 addObject((*targetList)[i], onFirstPage); 176 } 177 } 178 179 SkPDFObject* SkPDFCatalog::getSubstituteObject(SkPDFObject* object) { 180 for (int i = 0; i < fSubstituteMap.count(); ++i) { 181 if (object == fSubstituteMap[i].fOriginal) { 182 return fSubstituteMap[i].fSubstitute; 183 } 184 } 185 return object; 186 } 187 188 off_t SkPDFCatalog::setSubstituteResourcesOffsets(off_t fileOffset, 189 bool firstPage) { 190 SkTDArray<SkPDFObject*>* targetList = getSubstituteList(firstPage); 191 off_t offsetSum = fileOffset; 192 for (int i = 0; i < targetList->count(); ++i) { 193 offsetSum += setFileOffset((*targetList)[i], offsetSum); 194 } 195 return offsetSum - fileOffset; 196 } 197 198 void SkPDFCatalog::emitSubstituteResources(SkWStream *stream, bool firstPage) { 199 SkTDArray<SkPDFObject*>* targetList = getSubstituteList(firstPage); 200 for (int i = 0; i < targetList->count(); ++i) { 201 (*targetList)[i]->emit(stream, this, true); 202 } 203 } 204 205 SkTDArray<SkPDFObject*>* SkPDFCatalog::getSubstituteList(bool firstPage) { 206 return firstPage ? &fSubstituteResourcesFirstPage : 207 &fSubstituteResourcesRemaining; 208 } 209