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" |
11 #include "SkPDFDevice.h" | 11 #include "SkPDFDevice.h" |
12 #include "SkPDFDocument.h" | 12 #include "SkPDFDocument.h" |
13 #include "SkPDFFont.h" | 13 #include "SkPDFFont.h" |
14 #include "SkPDFPage.h" | 14 #include "SkPDFPage.h" |
15 #include "SkPDFTypes.h" | 15 #include "SkPDFTypes.h" |
16 #include "SkStream.h" | 16 #include "SkStream.h" |
17 | 17 |
18 | 18 |
19 static void perform_font_subsetting(SkPDFCatalog* catalog, | 19 static void perform_font_subsetting(SkPDFCatalog* catalog, |
20 const SkTDArray<SkPDFPage*>& pages, | 20 const SkTDArray<SkPDFPage*>& pages) { |
21 SkTDArray<SkPDFObject*>* substitutes) { | |
22 SkASSERT(catalog); | 21 SkASSERT(catalog); |
23 SkASSERT(substitutes); | |
24 | 22 |
25 SkPDFGlyphSetMap usage; | 23 SkPDFGlyphSetMap usage; |
26 for (int i = 0; i < pages.count(); ++i) { | 24 for (int i = 0; i < pages.count(); ++i) { |
27 usage.merge(pages[i]->getFontGlyphUsage()); | 25 usage.merge(pages[i]->getFontGlyphUsage()); |
28 } | 26 } |
29 SkPDFGlyphSetMap::F2BIter iterator(usage); | 27 SkPDFGlyphSetMap::F2BIter iterator(usage); |
30 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next(); | 28 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next(); |
31 while (entry) { | 29 while (entry) { |
32 SkPDFFont* subsetFont = | 30 SkAutoTUnref<SkPDFFont> subsetFont( |
33 entry->fFont->getFontSubset(entry->fGlyphSet); | 31 entry->fFont->getFontSubset(entry->fGlyphSet)); |
34 if (subsetFont) { | 32 if (subsetFont) { |
35 catalog->setSubstitute(entry->fFont, subsetFont); | 33 catalog->setSubstitute(entry->fFont, subsetFont.get()); |
36 substitutes->push(subsetFont); // Transfer ownership to substitutes | |
37 } | 34 } |
38 entry = iterator.next(); | 35 entry = iterator.next(); |
39 } | 36 } |
40 } | 37 } |
41 | 38 |
42 static void emit_pdf_header(SkWStream* stream) { | 39 static void emit_pdf_header(SkWStream* stream) { |
43 stream->writeText("%PDF-1.4\n%"); | 40 stream->writeText("%PDF-1.4\n%"); |
44 // The PDF spec recommends including a comment with four bytes, all | 41 // The PDF spec recommends including a comment with four bytes, all |
45 // with their high bits set. This is "Skia" with the high bits set. | 42 // with their high bits set. This is "Skia" with the high bits set. |
46 stream->write32(0xD3EBE9E1); | 43 stream->write32(0xD3EBE9E1); |
(...skipping 18 matching lines...) Expand all Loading... |
65 stream->writeText("\n%%EOF"); | 62 stream->writeText("\n%%EOF"); |
66 } | 63 } |
67 | 64 |
68 bool SkPDFDocument::EmitPDF(const SkTDArray<const SkPDFDevice*>& pageDevices, | 65 bool SkPDFDocument::EmitPDF(const SkTDArray<const SkPDFDevice*>& pageDevices, |
69 SkWStream* stream) { | 66 SkWStream* stream) { |
70 if (pageDevices.isEmpty()) { | 67 if (pageDevices.isEmpty()) { |
71 return false; | 68 return false; |
72 } | 69 } |
73 | 70 |
74 SkTDArray<SkPDFPage*> pages; | 71 SkTDArray<SkPDFPage*> pages; |
| 72 SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict)); |
| 73 |
75 for (int i = 0; i < pageDevices.count(); i++) { | 74 for (int i = 0; i < pageDevices.count(); i++) { |
76 SkASSERT(pageDevices[i]); | 75 SkASSERT(pageDevices[i]); |
77 SkASSERT(i == 0 || | 76 SkASSERT(i == 0 || |
78 pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon()); | 77 pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon()); |
79 // Reference from new passed to pages. | 78 // Reference from new passed to pages. |
80 pages.push(SkNEW_ARGS(SkPDFPage, (pageDevices[i]))); | 79 SkAutoTUnref<SkPDFPage> page(SkNEW_ARGS(SkPDFPage, (pageDevices[i]))); |
| 80 page->finalizePage(); |
| 81 page->appendDestinations(dests); |
| 82 pages.push(page.detach()); |
81 } | 83 } |
82 SkPDFCatalog catalog; | 84 SkPDFCatalog catalog; |
83 | 85 |
84 SkTDArray<SkPDFDict*> pageTree; | 86 SkTDArray<SkPDFDict*> pageTree; |
85 SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog"))); | 87 SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog"))); |
86 SkTSet<SkPDFObject*> firstPageResources; | |
87 SkTSet<SkPDFObject*> otherPageResources; | |
88 SkTDArray<SkPDFObject*> substitutes; | |
89 catalog.addObject(docCatalog.get(), true); | |
90 | 88 |
91 SkPDFDict* pageTreeRoot; | 89 SkPDFDict* pageTreeRoot; |
92 SkPDFPage::GeneratePageTree(pages, &catalog, &pageTree, &pageTreeRoot); | 90 SkPDFPage::GeneratePageTree(pages, &pageTree, &pageTreeRoot); |
| 91 |
93 docCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref(); | 92 docCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref(); |
94 | 93 |
95 /* TODO(vandebo): output intent | 94 /* TODO(vandebo): output intent |
96 SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent"); | 95 SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent"); |
97 outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref(); | 96 outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref(); |
98 outputIntent->insert("OutputConditionIdentifier", | 97 outputIntent->insert("OutputConditionIdentifier", |
99 new SkPDFString("sRGB"))->unref(); | 98 new SkPDFString("sRGB"))->unref(); |
100 SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray; | 99 SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray; |
101 intentArray->append(outputIntent.get()); | 100 intentArray->append(outputIntent.get()); |
102 docCatalog->insert("OutputIntent", intentArray.get()); | 101 docCatalog->insert("OutputIntent", intentArray.get()); |
103 */ | 102 */ |
104 | 103 |
105 SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict)); | |
106 | |
107 bool firstPage = true; | |
108 /* The references returned in newResources are transfered to | |
109 * firstPageResources or otherPageResources depending on firstPage and | |
110 * knownResources doesn't have a reference but just relies on the other | |
111 * two sets to maintain a reference. | |
112 */ | |
113 SkTSet<SkPDFObject*> knownResources; | |
114 | |
115 // mergeInto returns the number of duplicates. | |
116 // If there are duplicates, there is a bug and we mess ref counting. | |
117 SkDEBUGCODE(int duplicates = ) knownResources.mergeInto(firstPageResources); | |
118 SkASSERT(duplicates == 0); | |
119 | |
120 for (int i = 0; i < pages.count(); i++) { | |
121 if (i == 1) { | |
122 firstPage = false; | |
123 SkDEBUGCODE(duplicates = ) | |
124 knownResources.mergeInto(otherPageResources); | |
125 } | |
126 SkTSet<SkPDFObject*> newResources; | |
127 pages[i]->finalizePage(&catalog, firstPage, knownResources, | |
128 &newResources); | |
129 for (int j = 0; j < newResources.count(); j++) { | |
130 catalog.addObject(newResources[i], firstPage); | |
131 } | |
132 if (firstPage) { | |
133 SkDEBUGCODE(duplicates = ) | |
134 firstPageResources.mergeInto(newResources); | |
135 } else { | |
136 SkDEBUGCODE(duplicates = ) | |
137 otherPageResources.mergeInto(newResources); | |
138 } | |
139 SkASSERT(duplicates == 0); | |
140 | |
141 SkDEBUGCODE(duplicates = ) knownResources.mergeInto(newResources); | |
142 SkASSERT(duplicates == 0); | |
143 | |
144 pages[i]->appendDestinations(dests); | |
145 } | |
146 | |
147 if (dests->size() > 0) { | 104 if (dests->size() > 0) { |
148 SkPDFDict* raw_dests = dests.get(); | 105 docCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (dests.get()))) |
149 firstPageResources.add(dests.detach()); // Transfer ownership. | |
150 catalog.addObject(raw_dests, true /* onFirstPage */); | |
151 docCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (raw_dests))) | |
152 ->unref(); | 106 ->unref(); |
153 } | 107 } |
154 | 108 |
155 // Build font subsetting info before proceeding. | 109 // Build font subsetting info before proceeding. |
156 perform_font_subsetting(&catalog, pages, &substitutes); | 110 perform_font_subsetting(&catalog, pages); |
157 | 111 |
158 SkTSet<SkPDFObject*> resourceSet; | 112 SkTSet<SkPDFObject*> resourceSet; |
159 if (resourceSet.add(docCatalog.get())) { | 113 if (resourceSet.add(docCatalog.get())) { |
160 docCatalog->addResources(&resourceSet, &catalog); | 114 docCatalog->addResources(&resourceSet, &catalog); |
161 } | 115 } |
| 116 for (int i = 0; i < resourceSet.count(); ++i) { |
| 117 SkAssertResult(catalog.addObject(resourceSet[i])); |
| 118 } |
| 119 |
162 size_t baseOffset = SkToOffT(stream->bytesWritten()); | 120 size_t baseOffset = SkToOffT(stream->bytesWritten()); |
163 emit_pdf_header(stream); | 121 emit_pdf_header(stream); |
| 122 SkTDArray<int32_t> offsets; |
164 for (int i = 0; i < resourceSet.count(); ++i) { | 123 for (int i = 0; i < resourceSet.count(); ++i) { |
165 SkPDFObject* object = resourceSet[i]; | 124 SkPDFObject* object = resourceSet[i]; |
166 catalog.setFileOffset(object, | 125 offsets.push(SkToS32(stream->bytesWritten() - baseOffset)); |
167 SkToOffT(stream->bytesWritten() - baseOffset)); | |
168 SkASSERT(object == catalog.getSubstituteObject(object)); | 126 SkASSERT(object == catalog.getSubstituteObject(object)); |
169 stream->writeDecAsText(catalog.getObjectNumber(object)); | 127 SkASSERT(catalog.getObjectNumber(object) == i + 1); |
| 128 stream->writeDecAsText(i + 1); |
170 stream->writeText(" 0 obj\n"); // Generation number is always 0. | 129 stream->writeText(" 0 obj\n"); // Generation number is always 0. |
171 object->emitObject(stream, &catalog); | 130 object->emitObject(stream, &catalog); |
172 stream->writeText("\nendobj\n"); | 131 stream->writeText("\nendobj\n"); |
173 } | 132 } |
174 int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset); | 133 int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset); |
175 int64_t objCount = catalog.emitXrefTable(stream, pages.count() > 1); | |
176 | 134 |
| 135 int32_t objCount = SkToS32(offsets.count() + 1); |
| 136 |
| 137 stream->writeText("xref\n0 "); |
| 138 stream->writeDecAsText(objCount + 1); |
| 139 stream->writeText("\n0000000000 65535 f \n"); |
| 140 for (int i = 0; i < offsets.count(); i++) { |
| 141 SkASSERT(offsets[i] > 0); |
| 142 stream->writeBigDecAsText(offsets[i], 10); |
| 143 stream->writeText(" 00000 n \n"); |
| 144 } |
177 emit_pdf_footer(stream, &catalog, docCatalog.get(), objCount, | 145 emit_pdf_footer(stream, &catalog, docCatalog.get(), objCount, |
178 xRefFileOffset); | 146 xRefFileOffset); |
179 | 147 |
180 // The page tree has both child and parent pointers, so it creates a | 148 // The page tree has both child and parent pointers, so it creates a |
181 // reference cycle. We must clear that cycle to properly reclaim memory. | 149 // reference cycle. We must clear that cycle to properly reclaim memory. |
182 for (int i = 0; i < pageTree.count(); i++) { | 150 for (int i = 0; i < pageTree.count(); i++) { |
183 pageTree[i]->clear(); | 151 pageTree[i]->clear(); |
184 } | 152 } |
185 pageTree.safeUnrefAll(); | 153 pageTree.safeUnrefAll(); |
186 pages.unrefAll(); | 154 pages.unrefAll(); |
187 | |
188 firstPageResources.safeUnrefAll(); | |
189 otherPageResources.safeUnrefAll(); | |
190 | |
191 substitutes.unrefAll(); | |
192 docCatalog.reset(NULL); | |
193 return true; | 155 return true; |
194 } | 156 } |
195 | 157 |
196 // TODO(halcanary): expose notEmbeddableCount in SkDocument | 158 // TODO(halcanary): expose notEmbeddableCount in SkDocument |
197 void SkPDFDocument::GetCountOfFontTypes( | 159 void SkPDFDocument::GetCountOfFontTypes( |
198 const SkTDArray<SkPDFDevice*>& pageDevices, | 160 const SkTDArray<SkPDFDevice*>& pageDevices, |
199 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], | 161 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], |
200 int* notSubsettableCount, | 162 int* notSubsettableCount, |
201 int* notEmbeddableCount) { | 163 int* notEmbeddableCount) { |
202 sk_bzero(counts, sizeof(int) * | 164 sk_bzero(counts, sizeof(int) * |
(...skipping 20 matching lines...) Expand all Loading... |
223 } | 185 } |
224 } | 186 } |
225 if (notSubsettableCount) { | 187 if (notSubsettableCount) { |
226 *notSubsettableCount = notSubsettable; | 188 *notSubsettableCount = notSubsettable; |
227 | 189 |
228 } | 190 } |
229 if (notEmbeddableCount) { | 191 if (notEmbeddableCount) { |
230 *notEmbeddableCount = notEmbeddable; | 192 *notEmbeddableCount = notEmbeddable; |
231 } | 193 } |
232 } | 194 } |
OLD | NEW |