| OLD | NEW |
| 1 | 1 |
| 2 /* | 2 /* |
| 3 * Copyright 2011 Google Inc. | 3 * Copyright 2011 Google Inc. |
| 4 * | 4 * |
| 5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
| 6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 | 9 |
| 10 #include "SkPDFCatalog.h" | 10 #include "SkPDFCatalog.h" |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 75 } | 75 } |
| 76 | 76 |
| 77 fSubstitutes.safeUnrefAll(); | 77 fSubstitutes.safeUnrefAll(); |
| 78 | 78 |
| 79 fDocCatalog->unref(); | 79 fDocCatalog->unref(); |
| 80 SkSafeUnref(fTrailerDict); | 80 SkSafeUnref(fTrailerDict); |
| 81 SkDELETE(fFirstPageResources); | 81 SkDELETE(fFirstPageResources); |
| 82 SkDELETE(fOtherPageResources); | 82 SkDELETE(fOtherPageResources); |
| 83 } | 83 } |
| 84 | 84 |
| 85 namespace { | |
| 86 class Streamer { | |
| 87 public: | |
| 88 Streamer(SkPDFCatalog* cat, SkWStream* out) | |
| 89 : fCat(cat), fOut(out), fBaseOffset(SkToOffT(out->bytesWritten())) { | |
| 90 } | |
| 91 | |
| 92 void stream(SkPDFObject* object) { | |
| 93 fCat->setFileOffset(object, this->offset()); | |
| 94 SkPDFObject* realObject = fCat->getSubstituteObject(object); | |
| 95 fCat->emitObjectNumber(fOut, realObject); | |
| 96 fOut->writeText(" obj\n"); | |
| 97 realObject->emitObject(fOut, fCat); | |
| 98 fOut->writeText("\nendobj\n"); | |
| 99 } | |
| 100 | |
| 101 off_t offset() { | |
| 102 return SkToOffT(fOut->bytesWritten()) - fBaseOffset; | |
| 103 } | |
| 104 | |
| 105 private: | |
| 106 SkPDFCatalog* const fCat; | |
| 107 SkWStream* const fOut; | |
| 108 const off_t fBaseOffset; | |
| 109 }; | |
| 110 } // namespace | |
| 111 | |
| 112 bool SkPDFDocument::emitPDF(SkWStream* stream) { | 85 bool SkPDFDocument::emitPDF(SkWStream* stream) { |
| 113 if (fPages.isEmpty()) { | 86 if (fPages.isEmpty()) { |
| 114 return false; | 87 return false; |
| 115 } | 88 } |
| 116 for (int i = 0; i < fPages.count(); i++) { | 89 for (int i = 0; i < fPages.count(); i++) { |
| 117 if (fPages[i] == NULL) { | 90 if (fPages[i] == NULL) { |
| 118 return false; | 91 return false; |
| 119 } | 92 } |
| 120 } | 93 } |
| 121 | 94 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 180 SkPDFDict* raw_dests = dests.get(); | 153 SkPDFDict* raw_dests = dests.get(); |
| 181 fFirstPageResources->add(dests.detach()); // Transfer ownership. | 154 fFirstPageResources->add(dests.detach()); // Transfer ownership. |
| 182 fCatalog->addObject(raw_dests, true /* onFirstPage */); | 155 fCatalog->addObject(raw_dests, true /* onFirstPage */); |
| 183 fDocCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (raw_dests)))->
unref(); | 156 fDocCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (raw_dests)))->
unref(); |
| 184 } | 157 } |
| 185 | 158 |
| 186 // Build font subsetting info before proceeding. | 159 // Build font subsetting info before proceeding. |
| 187 perform_font_subsetting(fCatalog.get(), fPages, &fSubstitutes); | 160 perform_font_subsetting(fCatalog.get(), fPages, &fSubstitutes); |
| 188 } | 161 } |
| 189 | 162 |
| 190 Streamer out(fCatalog, stream); | 163 SkTSet<SkPDFObject*> resourceSet; |
| 164 if (resourceSet.add(fDocCatalog)) { |
| 165 fDocCatalog->addResources(&resourceSet, fCatalog); |
| 166 } |
| 167 off_t baseOffset = SkToOffT(stream->bytesWritten()); |
| 191 emitHeader(stream); | 168 emitHeader(stream); |
| 192 | 169 for (int i = 0; i < resourceSet.count(); ++i) { |
| 193 out.stream(fDocCatalog); | 170 SkPDFObject* object = resourceSet[i]; |
| 194 out.stream(fPages[0]); | 171 fCatalog->setFileOffset(object, |
| 195 out.stream(fPages[0]->getContentStream()); | 172 SkToOffT(stream->bytesWritten()) - baseOffset); |
| 196 | 173 SkASSERT(object == fCatalog->getSubstituteObject(object)); |
| 197 for (int i = 0; i < fFirstPageResources->count(); i++) { | 174 fCatalog->emitObjectNumber(stream, object); |
| 198 out.stream((*fFirstPageResources)[i]); | 175 stream->writeText(" obj\n"); |
| 176 object->emitObject(stream, fCatalog); |
| 177 stream->writeText("\nendobj\n"); |
| 199 } | 178 } |
| 200 | 179 fXRefFileOffset = SkToOffT(stream->bytesWritten()) - baseOffset; |
| 201 SkTSet<SkPDFObject*>* firstPageSubstituteResources = | |
| 202 fCatalog->getSubstituteList(true); | |
| 203 for (int i = 0; i < firstPageSubstituteResources->count(); ++i) { | |
| 204 out.stream((*firstPageSubstituteResources)[i]); | |
| 205 } | |
| 206 // TODO(vandebo): Support linearized format | |
| 207 // if (fPages.size() > 1) { | |
| 208 // // TODO(vandebo): Save the file offset for the first page xref table. | |
| 209 // fCatalog->emitXrefTable(stream, true); | |
| 210 // } | |
| 211 | |
| 212 for (int i = 0; i < fPageTree.count(); i++) { | |
| 213 out.stream(fPageTree[i]); | |
| 214 } | |
| 215 | |
| 216 for (int i = 1; i < fPages.count(); i++) { | |
| 217 out.stream(fPages[i]->getContentStream()); | |
| 218 } | |
| 219 | |
| 220 for (int i = 0; i < fOtherPageResources->count(); i++) { | |
| 221 out.stream((*fOtherPageResources)[i]); | |
| 222 } | |
| 223 | |
| 224 SkTSet<SkPDFObject*>* otherSubstituteResources = | |
| 225 fCatalog->getSubstituteList(false); | |
| 226 for (int i = 0; i < otherSubstituteResources->count(); ++i) { | |
| 227 out.stream((*otherSubstituteResources)[i]); | |
| 228 } | |
| 229 | |
| 230 fXRefFileOffset = out.offset(); | |
| 231 int64_t objCount = fCatalog->emitXrefTable(stream, fPages.count() > 1); | 180 int64_t objCount = fCatalog->emitXrefTable(stream, fPages.count() > 1); |
| 232 emitFooter(stream, objCount); | 181 emitFooter(stream, objCount); |
| 233 return true; | 182 return true; |
| 234 } | 183 } |
| 235 | 184 |
| 185 // TODO(halcanary): remove this method, since it is unused. |
| 236 bool SkPDFDocument::setPage(int pageNumber, SkPDFDevice* pdfDevice) { | 186 bool SkPDFDocument::setPage(int pageNumber, SkPDFDevice* pdfDevice) { |
| 237 if (!fPageTree.isEmpty()) { | 187 if (!fPageTree.isEmpty()) { |
| 238 return false; | 188 return false; |
| 239 } | 189 } |
| 240 | 190 |
| 241 pageNumber--; | 191 pageNumber--; |
| 242 SkASSERT(pageNumber >= 0); | 192 SkASSERT(pageNumber >= 0); |
| 243 | 193 |
| 244 if (pageNumber >= fPages.count()) { | 194 if (pageNumber >= fPages.count()) { |
| 245 int oldSize = fPages.count(); | 195 int oldSize = fPages.count(); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 259 if (!fPageTree.isEmpty()) { | 209 if (!fPageTree.isEmpty()) { |
| 260 return false; | 210 return false; |
| 261 } | 211 } |
| 262 | 212 |
| 263 SkPDFPage* page = new SkPDFPage(pdfDevice); | 213 SkPDFPage* page = new SkPDFPage(pdfDevice); |
| 264 fPages.push(page); // Reference from new passed to fPages. | 214 fPages.push(page); // Reference from new passed to fPages. |
| 265 return true; | 215 return true; |
| 266 } | 216 } |
| 267 | 217 |
| 268 // Deprecated. | 218 // Deprecated. |
| 219 // TODO(halcanary): remove |
| 269 void SkPDFDocument::getCountOfFontTypes( | 220 void SkPDFDocument::getCountOfFontTypes( |
| 270 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 2]) const { | 221 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 2]) const { |
| 271 sk_bzero(counts, sizeof(int) * | 222 sk_bzero(counts, sizeof(int) * |
| 272 (SkAdvancedTypefaceMetrics::kOther_Font + 2)); | 223 (SkAdvancedTypefaceMetrics::kOther_Font + 2)); |
| 273 SkTDArray<SkFontID> seenFonts; | 224 SkTDArray<SkFontID> seenFonts; |
| 274 int notEmbeddable = 0; | 225 int notEmbeddable = 0; |
| 275 | 226 |
| 276 for (int pageNumber = 0; pageNumber < fPages.count(); pageNumber++) { | 227 for (int pageNumber = 0; pageNumber < fPages.count(); pageNumber++) { |
| 277 const SkTDArray<SkPDFFont*>& fontResources = | 228 const SkTDArray<SkPDFFont*>& fontResources = |
| 278 fPages[pageNumber]->getFontResources(); | 229 fPages[pageNumber]->getFontResources(); |
| 279 for (int font = 0; font < fontResources.count(); font++) { | 230 for (int font = 0; font < fontResources.count(); font++) { |
| 280 SkFontID fontID = fontResources[font]->typeface()->uniqueID(); | 231 SkFontID fontID = fontResources[font]->typeface()->uniqueID(); |
| 281 if (seenFonts.find(fontID) == -1) { | 232 if (seenFonts.find(fontID) == -1) { |
| 282 counts[fontResources[font]->getType()]++; | 233 counts[fontResources[font]->getType()]++; |
| 283 seenFonts.push(fontID); | 234 seenFonts.push(fontID); |
| 284 if (!fontResources[font]->canEmbed()) { | 235 if (!fontResources[font]->canEmbed()) { |
| 285 notEmbeddable++; | 236 notEmbeddable++; |
| 286 } | 237 } |
| 287 } | 238 } |
| 288 } | 239 } |
| 289 } | 240 } |
| 290 counts[SkAdvancedTypefaceMetrics::kOther_Font + 1] = notEmbeddable; | 241 counts[SkAdvancedTypefaceMetrics::kOther_Font + 1] = notEmbeddable; |
| 291 } | 242 } |
| 292 | 243 |
| 244 // TODO(halcanary): expose notEmbeddableCount in SkDocument |
| 293 void SkPDFDocument::getCountOfFontTypes( | 245 void SkPDFDocument::getCountOfFontTypes( |
| 294 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], | 246 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], |
| 295 int* notSubsettableCount, | 247 int* notSubsettableCount, |
| 296 int* notEmbeddableCount) const { | 248 int* notEmbeddableCount) const { |
| 297 sk_bzero(counts, sizeof(int) * | 249 sk_bzero(counts, sizeof(int) * |
| 298 (SkAdvancedTypefaceMetrics::kOther_Font + 1)); | 250 (SkAdvancedTypefaceMetrics::kOther_Font + 1)); |
| 299 SkTDArray<SkFontID> seenFonts; | 251 SkTDArray<SkFontID> seenFonts; |
| 300 int notSubsettable = 0; | 252 int notSubsettable = 0; |
| 301 int notEmbeddable = 0; | 253 int notEmbeddable = 0; |
| 302 | 254 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 327 } | 279 } |
| 328 | 280 |
| 329 void SkPDFDocument::emitHeader(SkWStream* stream) { | 281 void SkPDFDocument::emitHeader(SkWStream* stream) { |
| 330 stream->writeText("%PDF-1.4\n%"); | 282 stream->writeText("%PDF-1.4\n%"); |
| 331 // The PDF spec recommends including a comment with four bytes, all | 283 // The PDF spec recommends including a comment with four bytes, all |
| 332 // with their high bits set. This is "Skia" with the high bits set. | 284 // with their high bits set. This is "Skia" with the high bits set. |
| 333 stream->write32(0xD3EBE9E1); | 285 stream->write32(0xD3EBE9E1); |
| 334 stream->writeText("\n"); | 286 stream->writeText("\n"); |
| 335 } | 287 } |
| 336 | 288 |
| 289 //TODO(halcanary): remove this function |
| 337 size_t SkPDFDocument::headerSize() { | 290 size_t SkPDFDocument::headerSize() { |
| 338 SkDynamicMemoryWStream buffer; | 291 SkDynamicMemoryWStream buffer; |
| 339 emitHeader(&buffer); | 292 emitHeader(&buffer); |
| 340 return buffer.getOffset(); | 293 return buffer.getOffset(); |
| 341 } | 294 } |
| 342 | 295 |
| 343 void SkPDFDocument::emitFooter(SkWStream* stream, int64_t objCount) { | 296 void SkPDFDocument::emitFooter(SkWStream* stream, int64_t objCount) { |
| 344 if (NULL == fTrailerDict) { | 297 if (NULL == fTrailerDict) { |
| 345 fTrailerDict = SkNEW(SkPDFDict); | 298 fTrailerDict = SkNEW(SkPDFDict); |
| 346 | 299 |
| 347 // TODO(vandebo): Linearized format will take a Prev entry too. | 300 // TODO(vandebo): Linearized format will take a Prev entry too. |
| 348 // TODO(vandebo): PDF/A requires an ID entry. | 301 // TODO(vandebo): PDF/A requires an ID entry. |
| 349 fTrailerDict->insertInt("Size", int(objCount)); | 302 fTrailerDict->insertInt("Size", int(objCount)); |
| 350 fTrailerDict->insert("Root", new SkPDFObjRef(fDocCatalog))->unref(); | 303 fTrailerDict->insert("Root", new SkPDFObjRef(fDocCatalog))->unref(); |
| 351 } | 304 } |
| 352 | 305 |
| 353 stream->writeText("trailer\n"); | 306 stream->writeText("trailer\n"); |
| 354 fTrailerDict->emitObject(stream, fCatalog.get()); | 307 fTrailerDict->emitObject(stream, fCatalog.get()); |
| 355 stream->writeText("\nstartxref\n"); | 308 stream->writeText("\nstartxref\n"); |
| 356 stream->writeBigDecAsText(fXRefFileOffset); | 309 stream->writeBigDecAsText(fXRefFileOffset); |
| 357 stream->writeText("\n%%EOF"); | 310 stream->writeText("\n%%EOF"); |
| 358 } | 311 } |
| OLD | NEW |