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 |