| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2011 Google Inc. | 2 * Copyright 2011 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "SkDocument.h" | 8 #include "SkDocument.h" |
| 9 #include "SkPDFCanon.h" | 9 #include "SkPDFCanon.h" |
| 10 #include "SkPDFCatalog.h" | |
| 11 #include "SkPDFDevice.h" | 10 #include "SkPDFDevice.h" |
| 12 #include "SkPDFFont.h" | 11 #include "SkPDFFont.h" |
| 13 #include "SkPDFResourceDict.h" | 12 #include "SkPDFResourceDict.h" |
| 14 #include "SkPDFStream.h" | 13 #include "SkPDFStream.h" |
| 15 #include "SkPDFTypes.h" | 14 #include "SkPDFTypes.h" |
| 16 #include "SkStream.h" | 15 #include "SkStream.h" |
| 17 | 16 |
| 18 static void emit_pdf_header(SkWStream* stream) { | 17 static void emit_pdf_header(SkWStream* stream) { |
| 19 stream->writeText("%PDF-1.4\n%"); | 18 stream->writeText("%PDF-1.4\n%"); |
| 20 // The PDF spec recommends including a comment with four bytes, all | 19 // The PDF spec recommends including a comment with four bytes, all |
| 21 // with their high bits set. This is "Skia" with the high bits set. | 20 // with their high bits set. This is "Skia" with the high bits set. |
| 22 stream->write32(0xD3EBE9E1); | 21 stream->write32(0xD3EBE9E1); |
| 23 stream->writeText("\n"); | 22 stream->writeText("\n"); |
| 24 } | 23 } |
| 25 | 24 |
| 26 static void emit_pdf_footer(SkWStream* stream, | 25 static void emit_pdf_footer(SkWStream* stream, |
| 27 SkPDFCatalog* catalog, | 26 const SkPDFObjNumMap& objNumMap, |
| 27 const SkPDFSubstituteMap& substitutes, |
| 28 SkPDFObject* docCatalog, | 28 SkPDFObject* docCatalog, |
| 29 int64_t objCount, | 29 int64_t objCount, |
| 30 int32_t xRefFileOffset) { | 30 int32_t xRefFileOffset) { |
| 31 SkPDFDict trailerDict; | 31 SkPDFDict trailerDict; |
| 32 // TODO(vandebo): Linearized format will take a Prev entry too. | 32 // TODO(vandebo): Linearized format will take a Prev entry too. |
| 33 // TODO(vandebo): PDF/A requires an ID entry. | 33 // TODO(vandebo): PDF/A requires an ID entry. |
| 34 trailerDict.insertInt("Size", int(objCount)); | 34 trailerDict.insertInt("Size", int(objCount)); |
| 35 trailerDict.insert("Root", new SkPDFObjRef(docCatalog))->unref(); | 35 trailerDict.insert("Root", new SkPDFObjRef(docCatalog))->unref(); |
| 36 | 36 |
| 37 stream->writeText("trailer\n"); | 37 stream->writeText("trailer\n"); |
| 38 trailerDict.emitObject(stream, catalog); | 38 trailerDict.emitObject(stream, objNumMap, substitutes); |
| 39 stream->writeText("\nstartxref\n"); | 39 stream->writeText("\nstartxref\n"); |
| 40 stream->writeBigDecAsText(xRefFileOffset); | 40 stream->writeBigDecAsText(xRefFileOffset); |
| 41 stream->writeText("\n%%EOF"); | 41 stream->writeText("\n%%EOF"); |
| 42 } | 42 } |
| 43 | 43 |
| 44 static void perform_font_subsetting( | 44 static void perform_font_subsetting( |
| 45 const SkTDArray<const SkPDFDevice*>& pageDevices, | 45 const SkTDArray<const SkPDFDevice*>& pageDevices, |
| 46 SkPDFCatalog* catalog) { | 46 SkPDFSubstituteMap* substituteMap) { |
| 47 SkASSERT(catalog); | 47 SkASSERT(substituteMap); |
| 48 | 48 |
| 49 SkPDFGlyphSetMap usage; | 49 SkPDFGlyphSetMap usage; |
| 50 for (int i = 0; i < pageDevices.count(); ++i) { | 50 for (int i = 0; i < pageDevices.count(); ++i) { |
| 51 usage.merge(pageDevices[i]->getFontGlyphUsage()); | 51 usage.merge(pageDevices[i]->getFontGlyphUsage()); |
| 52 } | 52 } |
| 53 SkPDFGlyphSetMap::F2BIter iterator(usage); | 53 SkPDFGlyphSetMap::F2BIter iterator(usage); |
| 54 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next(); | 54 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next(); |
| 55 while (entry) { | 55 while (entry) { |
| 56 SkAutoTUnref<SkPDFFont> subsetFont( | 56 SkAutoTUnref<SkPDFFont> subsetFont( |
| 57 entry->fFont->getFontSubset(entry->fGlyphSet)); | 57 entry->fFont->getFontSubset(entry->fGlyphSet)); |
| 58 if (subsetFont) { | 58 if (subsetFont) { |
| 59 catalog->setSubstitute(entry->fFont, subsetFont.get()); | 59 substituteMap->setSubstitute(entry->fFont, subsetFont.get()); |
| 60 } | 60 } |
| 61 entry = iterator.next(); | 61 entry = iterator.next(); |
| 62 } | 62 } |
| 63 } | 63 } |
| 64 | 64 |
| 65 static SkPDFDict* create_pdf_page(const SkPDFDevice* pageDevice) { | 65 static SkPDFDict* create_pdf_page(const SkPDFDevice* pageDevice) { |
| 66 SkAutoTUnref<SkPDFDict> page(SkNEW_ARGS(SkPDFDict, ("Page"))); | 66 SkAutoTUnref<SkPDFDict> page(SkNEW_ARGS(SkPDFDict, ("Page"))); |
| 67 SkAutoTUnref<SkPDFResourceDict> deviceResourceDict( | 67 SkAutoTUnref<SkPDFResourceDict> deviceResourceDict( |
| 68 pageDevice->createResourceDict()); | 68 pageDevice->createResourceDict()); |
| 69 page->insert("Resources", deviceResourceDict.get()); | 69 page->insert("Resources", deviceResourceDict.get()); |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict)); | 176 SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict)); |
| 177 | 177 |
| 178 for (int i = 0; i < pageDevices.count(); i++) { | 178 for (int i = 0; i < pageDevices.count(); i++) { |
| 179 SkASSERT(pageDevices[i]); | 179 SkASSERT(pageDevices[i]); |
| 180 SkASSERT(i == 0 || | 180 SkASSERT(i == 0 || |
| 181 pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon()); | 181 pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon()); |
| 182 SkAutoTUnref<SkPDFDict> page(create_pdf_page(pageDevices[i])); | 182 SkAutoTUnref<SkPDFDict> page(create_pdf_page(pageDevices[i])); |
| 183 pageDevices[i]->appendDestinations(dests, page.get()); | 183 pageDevices[i]->appendDestinations(dests, page.get()); |
| 184 pages.push(page.detach()); | 184 pages.push(page.detach()); |
| 185 } | 185 } |
| 186 SkPDFCatalog catalog; | |
| 187 | 186 |
| 188 SkTDArray<SkPDFDict*> pageTree; | 187 SkTDArray<SkPDFDict*> pageTree; |
| 189 SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog"))); | 188 SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog"))); |
| 190 | 189 |
| 191 SkPDFDict* pageTreeRoot; | 190 SkPDFDict* pageTreeRoot; |
| 192 generate_page_tree(pages, &pageTree, &pageTreeRoot); | 191 generate_page_tree(pages, &pageTree, &pageTreeRoot); |
| 193 | 192 |
| 194 docCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref(); | 193 docCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref(); |
| 195 | 194 |
| 196 /* TODO(vandebo): output intent | 195 /* TODO(vandebo): output intent |
| 197 SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent"); | 196 SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent"); |
| 198 outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref(); | 197 outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref(); |
| 199 outputIntent->insert("OutputConditionIdentifier", | 198 outputIntent->insert("OutputConditionIdentifier", |
| 200 new SkPDFString("sRGB"))->unref(); | 199 new SkPDFString("sRGB"))->unref(); |
| 201 SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray; | 200 SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray; |
| 202 intentArray->append(outputIntent.get()); | 201 intentArray->append(outputIntent.get()); |
| 203 docCatalog->insert("OutputIntent", intentArray.get()); | 202 docCatalog->insert("OutputIntent", intentArray.get()); |
| 204 */ | 203 */ |
| 205 | 204 |
| 206 if (dests->size() > 0) { | 205 if (dests->size() > 0) { |
| 207 docCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (dests.get()))) | 206 docCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (dests.get()))) |
| 208 ->unref(); | 207 ->unref(); |
| 209 } | 208 } |
| 210 | 209 |
| 211 // Build font subsetting info before proceeding. | 210 // Build font subsetting info before proceeding. |
| 212 perform_font_subsetting(pageDevices, &catalog); | 211 SkPDFSubstituteMap substitutes; |
| 212 perform_font_subsetting(pageDevices, &substitutes); |
| 213 | 213 |
| 214 if (catalog.addObject(docCatalog.get())) { | 214 SkPDFObjNumMap objNumMap; |
| 215 docCatalog->addResources(&catalog); | 215 if (objNumMap.addObject(docCatalog.get())) { |
| 216 docCatalog->addResources(&objNumMap, substitutes); |
| 216 } | 217 } |
| 217 size_t baseOffset = SkToOffT(stream->bytesWritten()); | 218 size_t baseOffset = SkToOffT(stream->bytesWritten()); |
| 218 emit_pdf_header(stream); | 219 emit_pdf_header(stream); |
| 219 SkTDArray<int32_t> offsets; | 220 SkTDArray<int32_t> offsets; |
| 220 for (int i = 0; i < catalog.objects().count(); ++i) { | 221 for (int i = 0; i < objNumMap.objects().count(); ++i) { |
| 221 SkPDFObject* object = catalog.objects()[i]; | 222 SkPDFObject* object = objNumMap.objects()[i]; |
| 222 offsets.push(SkToS32(stream->bytesWritten() - baseOffset)); | 223 offsets.push(SkToS32(stream->bytesWritten() - baseOffset)); |
| 223 SkASSERT(object == catalog.getSubstituteObject(object)); | 224 SkASSERT(object == substitutes.getSubstitute(object)); |
| 224 SkASSERT(catalog.getObjectNumber(object) == i + 1); | 225 SkASSERT(objNumMap.getObjectNumber(object) == i + 1); |
| 225 stream->writeDecAsText(i + 1); | 226 stream->writeDecAsText(i + 1); |
| 226 stream->writeText(" 0 obj\n"); // Generation number is always 0. | 227 stream->writeText(" 0 obj\n"); // Generation number is always 0. |
| 227 object->emitObject(stream, &catalog); | 228 object->emitObject(stream, objNumMap, substitutes); |
| 228 stream->writeText("\nendobj\n"); | 229 stream->writeText("\nendobj\n"); |
| 229 } | 230 } |
| 230 int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset); | 231 int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset); |
| 231 | 232 |
| 232 // Include the zeroth object in the count. | 233 // Include the zeroth object in the count. |
| 233 int32_t objCount = SkToS32(offsets.count() + 1); | 234 int32_t objCount = SkToS32(offsets.count() + 1); |
| 234 | 235 |
| 235 stream->writeText("xref\n0 "); | 236 stream->writeText("xref\n0 "); |
| 236 stream->writeDecAsText(objCount); | 237 stream->writeDecAsText(objCount); |
| 237 stream->writeText("\n0000000000 65535 f \n"); | 238 stream->writeText("\n0000000000 65535 f \n"); |
| 238 for (int i = 0; i < offsets.count(); i++) { | 239 for (int i = 0; i < offsets.count(); i++) { |
| 239 SkASSERT(offsets[i] > 0); | 240 SkASSERT(offsets[i] > 0); |
| 240 stream->writeBigDecAsText(offsets[i], 10); | 241 stream->writeBigDecAsText(offsets[i], 10); |
| 241 stream->writeText(" 00000 n \n"); | 242 stream->writeText(" 00000 n \n"); |
| 242 } | 243 } |
| 243 emit_pdf_footer(stream, &catalog, docCatalog.get(), objCount, | 244 emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount, |
| 244 xRefFileOffset); | 245 xRefFileOffset); |
| 245 | 246 |
| 246 // The page tree has both child and parent pointers, so it creates a | 247 // The page tree has both child and parent pointers, so it creates a |
| 247 // reference cycle. We must clear that cycle to properly reclaim memory. | 248 // reference cycle. We must clear that cycle to properly reclaim memory. |
| 248 for (int i = 0; i < pageTree.count(); i++) { | 249 for (int i = 0; i < pageTree.count(); i++) { |
| 249 pageTree[i]->clear(); | 250 pageTree[i]->clear(); |
| 250 } | 251 } |
| 251 pageTree.safeUnrefAll(); | 252 pageTree.safeUnrefAll(); |
| 252 pages.unrefAll(); | 253 pages.unrefAll(); |
| 253 return true; | 254 return true; |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 359 | 360 |
| 360 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { | 361 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { |
| 361 SkFILEWStream* stream = SkNEW_ARGS(SkFILEWStream, (path)); | 362 SkFILEWStream* stream = SkNEW_ARGS(SkFILEWStream, (path)); |
| 362 if (!stream->isValid()) { | 363 if (!stream->isValid()) { |
| 363 SkDELETE(stream); | 364 SkDELETE(stream); |
| 364 return NULL; | 365 return NULL; |
| 365 } | 366 } |
| 366 auto delete_wstream = [](SkWStream* stream, bool) { SkDELETE(stream); }; | 367 auto delete_wstream = [](SkWStream* stream, bool) { SkDELETE(stream); }; |
| 367 return SkNEW_ARGS(SkDocument_PDF, (stream, delete_wstream, dpi)); | 368 return SkNEW_ARGS(SkDocument_PDF, (stream, delete_wstream, dpi)); |
| 368 } | 369 } |
| OLD | NEW |