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 "SkPDFCanon.h" | 8 #include "SkPDFCanon.h" |
9 #include "SkPDFCanvas.h" | 9 #include "SkPDFCanvas.h" |
10 #include "SkPDFDevice.h" | 10 #include "SkPDFDevice.h" |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
86 wStream->writeBigDecAsText(xRefFileOffset); | 86 wStream->writeBigDecAsText(xRefFileOffset); |
87 wStream->writeText("\n%%EOF"); | 87 wStream->writeText("\n%%EOF"); |
88 } | 88 } |
89 | 89 |
90 int32_t SkPDFObjectSerializer::offset(SkWStream* wStream) { | 90 int32_t SkPDFObjectSerializer::offset(SkWStream* wStream) { |
91 size_t offset = wStream->bytesWritten(); | 91 size_t offset = wStream->bytesWritten(); |
92 SkASSERT(offset > fBaseOffset); | 92 SkASSERT(offset > fBaseOffset); |
93 return SkToS32(offset - fBaseOffset); | 93 return SkToS32(offset - fBaseOffset); |
94 } | 94 } |
95 | 95 |
96 static void perform_font_subsetting( | |
97 const SkTArray<sk_sp<const SkPDFDevice>>& pageDevices, | |
98 SkPDFSubstituteMap* substituteMap) { | |
99 SkASSERT(substituteMap); | |
100 | |
101 SkPDFGlyphSetMap usage; | |
102 for (const sk_sp<const SkPDFDevice>& pageDevice : pageDevices) { | |
103 usage.merge(pageDevice->getFontGlyphUsage()); | |
104 } | |
105 SkPDFGlyphSetMap::F2BIter iterator(usage); | |
106 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next(); | |
107 while (entry) { | |
108 sk_sp<SkPDFFont> subsetFont( | |
109 entry->fFont->getFontSubset(entry->fGlyphSet)); | |
110 if (subsetFont) { | |
111 substituteMap->setSubstitute(entry->fFont, subsetFont.get()); | |
112 } | |
113 entry = iterator.next(); | |
114 } | |
115 } | |
116 | |
117 static sk_sp<SkPDFDict> create_pdf_page(const SkPDFDevice* pageDevice) { | |
118 auto page = sk_make_sp<SkPDFDict>("Page"); | |
119 page->insertObject("Resources", pageDevice->makeResourceDict()); | |
120 page->insertObject("MediaBox", pageDevice->copyMediaBox()); | |
121 auto annotations = sk_make_sp<SkPDFArray>(); | |
122 pageDevice->appendAnnotations(annotations.get()); | |
123 if (annotations->size() > 0) { | |
124 page->insertObject("Annots", std::move(annotations)); | |
125 } | |
126 auto content = pageDevice->content(); | |
127 page->insertObjRef("Contents", sk_make_sp<SkPDFStream>(content.get())); | |
128 return page; | |
129 } | |
130 | 96 |
131 // return root node. | 97 // return root node. |
132 static sk_sp<SkPDFDict> generate_page_tree( | 98 static sk_sp<SkPDFDict> generate_page_tree(SkTArray<sk_sp<SkPDFDict>>* pages) { |
133 SkTDArray<SkPDFDict*>& pages, | |
134 SkTDArray<SkPDFDict*>* pageTree) { | |
135 // PDF wants a tree describing all the pages in the document. We arbitrary | 99 // PDF wants a tree describing all the pages in the document. We arbitrary |
136 // choose 8 (kNodeSize) as the number of allowed children. The internal | 100 // choose 8 (kNodeSize) as the number of allowed children. The internal |
137 // nodes have type "Pages" with an array of children, a parent pointer, and | 101 // nodes have type "Pages" with an array of children, a parent pointer, and |
138 // the number of leaves below the node as "Count." The leaves are passed | 102 // the number of leaves below the node as "Count." The leaves are passed |
139 // into the method, have type "Page" and need a parent pointer. This method | 103 // into the method, have type "Page" and need a parent pointer. This method |
140 // builds the tree bottom up, skipping internal nodes that would have only | 104 // builds the tree bottom up, skipping internal nodes that would have only |
141 // one child. | 105 // one child. |
142 static const int kNodeSize = 8; | 106 static const int kNodeSize = 8; |
143 | 107 |
144 // curNodes takes a reference to its items, which it passes to pageTree. | 108 // curNodes takes a reference to its items, which it passes to pageTree. |
145 SkTDArray<SkPDFDict*> curNodes; | 109 int totalPageCount = pages->count(); |
146 curNodes.setReserve(pages.count()); | 110 SkTArray<sk_sp<SkPDFDict>> curNodes; |
147 for (int i = 0; i < pages.count(); i++) { | 111 curNodes.swap(pages); |
148 SkSafeRef(pages[i]); | |
149 curNodes.push(pages[i]); | |
150 } | |
151 | 112 |
152 // nextRoundNodes passes its references to nodes on to curNodes. | 113 // nextRoundNodes passes its references to nodes on to curNodes. |
153 SkTDArray<SkPDFDict*> nextRoundNodes; | |
154 nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize); | |
155 | |
156 int treeCapacity = kNodeSize; | 114 int treeCapacity = kNodeSize; |
157 do { | 115 do { |
116 SkTArray<sk_sp<SkPDFDict>> nextRoundNodes; | |
158 for (int i = 0; i < curNodes.count(); ) { | 117 for (int i = 0; i < curNodes.count(); ) { |
159 if (i > 0 && i + 1 == curNodes.count()) { | 118 if (i > 0 && i + 1 == curNodes.count()) { |
160 nextRoundNodes.push(curNodes[i]); | 119 nextRoundNodes.emplace_back(std::move(curNodes[i])); |
161 break; | 120 break; |
162 } | 121 } |
163 | 122 |
164 auto newNode = sk_make_sp<SkPDFDict>("Pages"); | 123 auto newNode = sk_make_sp<SkPDFDict>("Pages"); |
165 auto kids = sk_make_sp<SkPDFArray>(); | 124 auto kids = sk_make_sp<SkPDFArray>(); |
166 kids->reserve(kNodeSize); | 125 kids->reserve(kNodeSize); |
167 | 126 |
168 int count = 0; | 127 int count = 0; |
169 for (; i < curNodes.count() && count < kNodeSize; i++, count++) { | 128 for (; i < curNodes.count() && count < kNodeSize; i++, count++) { |
170 curNodes[i]->insertObjRef("Parent", newNode); | 129 curNodes[i]->insertObjRef("Parent", newNode); |
171 kids->appendObjRef(sk_ref_sp(curNodes[i])); | 130 kids->appendObjRef(std::move(curNodes[i])); |
tomhudson
2016/03/22 14:56:17
As we work our way through curNodes, it's transien
hal.canary
2016/03/22 21:17:43
Not really. when we move, we leave a nullpr behi
| |
172 | |
173 // TODO(vandebo): put the objects in strict access order. | |
174 // Probably doesn't matter because they are so small. | |
175 if (curNodes[i] != pages[0]) { | |
176 pageTree->push(curNodes[i]); // Transfer reference. | |
177 } else { | |
178 SkSafeUnref(curNodes[i]); | |
179 } | |
180 } | 131 } |
181 | 132 |
182 // treeCapacity is the number of leaf nodes possible for the | 133 // treeCapacity is the number of leaf nodes possible for the |
183 // current set of subtrees being generated. (i.e. 8, 64, 512, ...). | 134 // current set of subtrees being generated. (i.e. 8, 64, 512, ...). |
184 // It is hard to count the number of leaf nodes in the current | 135 // It is hard to count the number of leaf nodes in the current |
185 // subtree. However, by construction, we know that unless it's the | 136 // subtree. However, by construction, we know that unless it's the |
186 // last subtree for the current depth, the leaf count will be | 137 // last subtree for the current depth, the leaf count will be |
187 // treeCapacity, otherwise it's what ever is left over after | 138 // treeCapacity, otherwise it's what ever is left over after |
188 // consuming treeCapacity chunks. | 139 // consuming treeCapacity chunks. |
189 int pageCount = treeCapacity; | 140 int pageCount = treeCapacity; |
190 if (i == curNodes.count()) { | 141 if (i == curNodes.count()) { |
191 pageCount = ((pages.count() - 1) % treeCapacity) + 1; | 142 pageCount = ((totalPageCount - 1) % treeCapacity) + 1; |
192 } | 143 } |
193 newNode->insertInt("Count", pageCount); | 144 newNode->insertInt("Count", pageCount); |
194 newNode->insertObject("Kids", std::move(kids)); | 145 newNode->insertObject("Kids", std::move(kids)); |
195 nextRoundNodes.push(newNode.release()); // Transfer reference. | 146 nextRoundNodes.emplace_back(std::move(newNode)); |
196 } | 147 } |
197 | 148 curNodes.swap(&nextRoundNodes); |
198 curNodes = nextRoundNodes; | 149 nextRoundNodes.reset(); |
199 nextRoundNodes.rewind(); | |
200 treeCapacity *= kNodeSize; | 150 treeCapacity *= kNodeSize; |
201 } while (curNodes.count() > 1); | 151 } while (curNodes.count() > 1); |
202 | 152 return std::move(curNodes[0]); |
203 pageTree->push(curNodes[0]); // Transfer reference. | |
204 return sk_ref_sp(curNodes[0]); | |
205 } | 153 } |
206 | 154 |
207 #if 0 | 155 #if 0 |
208 // TODO(halcanary): expose notEmbeddableCount in SkDocument | 156 // TODO(halcanary): expose notEmbeddableCount in SkDocument |
209 void GetCountOfFontTypes( | 157 void GetCountOfFontTypes( |
210 const SkTDArray<SkPDFDevice*>& pageDevices, | 158 const SkTDArray<SkPDFDevice*>& pageDevices, |
211 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], | 159 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], |
212 int* notSubsettableCount, | 160 int* notSubsettableCount, |
213 int* notEmbeddableCount) { | 161 int* notEmbeddableCount) { |
214 sk_bzero(counts, sizeof(int) * | 162 sk_bzero(counts, sizeof(int) * |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
262 } | 210 } |
263 | 211 |
264 void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) { | 212 void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) { |
265 fObjectSerializer.addObjectRecursively(object); | 213 fObjectSerializer.addObjectRecursively(object); |
266 fObjectSerializer.serializeObjects(this->getStream()); | 214 fObjectSerializer.serializeObjects(this->getStream()); |
267 } | 215 } |
268 | 216 |
269 SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height, | 217 SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height, |
270 const SkRect& trimBox) { | 218 const SkRect& trimBox) { |
271 SkASSERT(!fCanvas.get()); // endPage() was called before this. | 219 SkASSERT(!fCanvas.get()); // endPage() was called before this. |
272 if (fPageDevices.empty()) { | 220 if (fPages.empty()) { |
273 // if this is the first page if the document. | 221 // if this is the first page if the document. |
274 fObjectSerializer.serializeHeader(this->getStream(), fMetadata); | 222 fObjectSerializer.serializeHeader(this->getStream(), fMetadata); |
223 fDests = sk_make_sp<SkPDFDict>(); | |
275 } | 224 } |
276 SkISize pageSize = SkISize::Make( | 225 SkISize pageSize = SkISize::Make( |
277 SkScalarRoundToInt(width), SkScalarRoundToInt(height)); | 226 SkScalarRoundToInt(width), SkScalarRoundToInt(height)); |
278 sk_sp<SkPDFDevice> device( | 227 fPageDevice.reset( |
279 SkPDFDevice::Create(pageSize, fRasterDpi, this)); | 228 SkPDFDevice::Create(pageSize, fRasterDpi, this)); |
280 fCanvas = sk_make_sp<SkPDFCanvas>(device); | 229 fCanvas = sk_make_sp<SkPDFCanvas>(fPageDevice); |
281 fPageDevices.push_back(std::move(device)); | |
282 fCanvas->clipRect(trimBox); | 230 fCanvas->clipRect(trimBox); |
283 fCanvas->translate(trimBox.x(), trimBox.y()); | 231 fCanvas->translate(trimBox.x(), trimBox.y()); |
284 return fCanvas.get(); | 232 return fCanvas.get(); |
285 } | 233 } |
286 | 234 |
287 void SkPDFDocument::onEndPage() { | 235 void SkPDFDocument::onEndPage() { |
288 SkASSERT(fCanvas.get()); | 236 SkASSERT(fCanvas.get()); |
289 fCanvas->flush(); | 237 fCanvas->flush(); |
290 fCanvas.reset(nullptr); | 238 fCanvas.reset(nullptr); |
239 SkASSERT(fPageDevice); | |
240 fGlyphUseage.merge(fPageDevice->getFontGlyphUsage()); | |
241 auto page = sk_make_sp<SkPDFDict>("Page"); | |
242 page->insertObject("Resources", fPageDevice->makeResourceDict()); | |
243 page->insertObject("MediaBox", fPageDevice->copyMediaBox()); | |
244 auto annotations = sk_make_sp<SkPDFArray>(); | |
245 fPageDevice->appendAnnotations(annotations.get()); | |
246 if (annotations->size() > 0) { | |
247 page->insertObject("Annots", std::move(annotations)); | |
248 } | |
249 auto contentData = fPageDevice->content(); | |
250 auto contentObject = sk_make_sp<SkPDFStream>(contentData.get()); | |
251 this->serialize(contentObject); | |
252 page->insertObjRef("Contents", std::move(contentObject)); | |
253 fPageDevice->appendDestinations(fDests.get(), page.get()); | |
254 fPages.emplace_back(std::move(page)); | |
255 fPageDevice.reset(nullptr); | |
291 } | 256 } |
292 | 257 |
293 void SkPDFDocument::onAbort() { | 258 void SkPDFDocument::onAbort() { |
294 fCanvas.reset(nullptr); | 259 fCanvas.reset(nullptr); |
295 fPageDevices.reset(); | 260 fPages.reset(); |
296 fCanon.reset(); | 261 fCanon.reset(); |
297 renew(&fObjectSerializer); | 262 renew(&fObjectSerializer); |
298 } | 263 } |
299 | 264 |
300 void SkPDFDocument::setMetadata(const SkDocument::Attribute info[], | 265 void SkPDFDocument::setMetadata(const SkDocument::Attribute info[], |
301 int infoCount, | 266 int infoCount, |
302 const SkTime::DateTime* creationDate, | 267 const SkTime::DateTime* creationDate, |
303 const SkTime::DateTime* modifiedDate) { | 268 const SkTime::DateTime* modifiedDate) { |
304 fMetadata.fInfo.reset(info, infoCount); | 269 fMetadata.fInfo.reset(info, infoCount); |
305 fMetadata.fCreation.reset(clone(creationDate)); | 270 fMetadata.fCreation.reset(clone(creationDate)); |
306 fMetadata.fModified.reset(clone(modifiedDate)); | 271 fMetadata.fModified.reset(clone(modifiedDate)); |
307 } | 272 } |
308 | 273 |
309 bool SkPDFDocument::onClose(SkWStream* stream) { | 274 bool SkPDFDocument::onClose(SkWStream* stream) { |
310 SkASSERT(!fCanvas.get()); | 275 SkASSERT(!fCanvas.get()); |
311 if (fPageDevices.empty()) { | 276 if (fPages.empty()) { |
312 fPageDevices.reset(); | 277 fPages.reset(); |
313 fCanon.reset(); | 278 fCanon.reset(); |
314 renew(&fObjectSerializer); | 279 renew(&fObjectSerializer); |
315 return false; | 280 return false; |
316 } | 281 } |
317 SkTDArray<SkPDFDict*> pages; // TODO: SkTArray<sk_sp<SkPDFDict>> | |
318 auto dests = sk_make_sp<SkPDFDict>(); | |
319 | |
320 for (const sk_sp<const SkPDFDevice>& pageDevice : fPageDevices) { | |
321 SkASSERT(pageDevice); | |
322 SkASSERT(fPageDevices[0]->getCanon() == pageDevice->getCanon()); | |
323 sk_sp<SkPDFDict> page(create_pdf_page(pageDevice.get())); | |
324 pageDevice->appendDestinations(dests.get(), page.get()); | |
325 pages.push(page.release()); | |
326 } | |
327 | |
328 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog"); | 282 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog"); |
329 | |
330 sk_sp<SkPDFObject> id, xmp; | 283 sk_sp<SkPDFObject> id, xmp; |
331 #ifdef SK_PDF_GENERATE_PDFA | 284 #ifdef SK_PDF_GENERATE_PDFA |
332 SkPDFMetadata::UUID uuid = metadata.uuid(); | 285 SkPDFMetadata::UUID uuid = metadata.uuid(); |
333 // We use the same UUID for Document ID and Instance ID since this | 286 // We use the same UUID for Document ID and Instance ID since this |
334 // is the first revision of this document (and Skia does not | 287 // is the first revision of this document (and Skia does not |
335 // support revising existing PDF documents). | 288 // support revising existing PDF documents). |
336 // If we are not in PDF/A mode, don't use a UUID since testing | 289 // If we are not in PDF/A mode, don't use a UUID since testing |
337 // works best with reproducible outputs. | 290 // works best with reproducible outputs. |
338 id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid)); | 291 id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid)); |
339 xmp.reset(metadata.createXMPObject(uuid, uuid)); | 292 xmp.reset(metadata.createXMPObject(uuid, uuid)); |
340 docCatalog->insertObjRef("Metadata", std::move(xmp)); | 293 docCatalog->insertObjRef("Metadata", std::move(xmp)); |
341 | 294 |
342 // sRGB is specified by HTML, CSS, and SVG. | 295 // sRGB is specified by HTML, CSS, and SVG. |
343 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent"); | 296 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent"); |
344 outputIntent->insertName("S", "GTS_PDFA1"); | 297 outputIntent->insertName("S", "GTS_PDFA1"); |
345 outputIntent->insertString("RegistryName", "http://www.color.org"); | 298 outputIntent->insertString("RegistryName", "http://www.color.org"); |
346 outputIntent->insertString("OutputConditionIdentifier", | 299 outputIntent->insertString("OutputConditionIdentifier", |
347 "sRGB IEC61966-2.1"); | 300 "sRGB IEC61966-2.1"); |
348 auto intentArray = sk_make_sp<SkPDFArray>(); | 301 auto intentArray = sk_make_sp<SkPDFArray>(); |
349 intentArray->appendObject(std::move(outputIntent)); | 302 intentArray->appendObject(std::move(outputIntent)); |
350 // Don't specify OutputIntents if we are not in PDF/A mode since | 303 // Don't specify OutputIntents if we are not in PDF/A mode since |
351 // no one has ever asked for this feature. | 304 // no one has ever asked for this feature. |
352 docCatalog->insertObject("OutputIntents", std::move(intentArray)); | 305 docCatalog->insertObject("OutputIntents", std::move(intentArray)); |
353 #endif | 306 #endif |
354 | 307 |
355 SkTDArray<SkPDFDict*> pageTree; | 308 docCatalog->insertObjRef("Pages", generate_page_tree(&fPages)); |
356 docCatalog->insertObjRef("Pages", generate_page_tree(pages, &pageTree)); | |
357 | 309 |
358 if (dests->size() > 0) { | 310 if (fDests->size() > 0) { |
359 docCatalog->insertObjRef("Dests", std::move(dests)); | 311 docCatalog->insertObjRef("Dests", std::move(fDests)); |
360 } | 312 } |
361 | 313 |
362 // Build font subsetting info before calling addObjectRecursively(). | 314 // Build font subsetting info before calling addObjectRecursively(). |
363 perform_font_subsetting(fPageDevices, &fObjectSerializer.fSubstituteMap); | 315 for (const auto& entry : fGlyphUseage) { |
316 sk_sp<SkPDFFont> subsetFont( | |
317 entry.fFont->getFontSubset(entry.fGlyphSet)); | |
318 if (subsetFont) { | |
319 fObjectSerializer.fSubstituteMap.setSubstitute( | |
320 entry.fFont, subsetFont.get()); | |
321 } | |
322 } | |
364 | 323 |
365 fObjectSerializer.addObjectRecursively(docCatalog); | 324 fObjectSerializer.addObjectRecursively(docCatalog); |
366 fObjectSerializer.serializeObjects(this->getStream()); | 325 fObjectSerializer.serializeObjects(this->getStream()); |
367 fObjectSerializer.serializeFooter( | 326 fObjectSerializer.serializeFooter( |
368 this->getStream(), docCatalog, std::move(id)); | 327 this->getStream(), docCatalog, std::move(id)); |
369 pageTree.unrefAll(); // TODO(halcanary): make this unnecesary by | 328 |
370 // refactoring generate_page_tree(). | 329 SkASSERT(fPages.count() == 0); |
371 pages.unrefAll(); | |
372 fPageDevices.reset(); | |
373 fCanon.reset(); | 330 fCanon.reset(); |
374 renew(&fObjectSerializer); | 331 renew(&fObjectSerializer); |
375 return true; | 332 return true; |
376 } | 333 } |
377 | 334 |
378 /////////////////////////////////////////////////////////////////////////////// | 335 /////////////////////////////////////////////////////////////////////////////// |
379 | 336 |
380 sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream, | 337 sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream, |
381 void (*proc)(SkWStream*, bool), | 338 void (*proc)(SkWStream*, bool), |
382 SkScalar dpi, | 339 SkScalar dpi, |
(...skipping 11 matching lines...) Expand all Loading... | |
394 return SkPDFMakeDocument(stream, nullptr, dpi, jpegEncoder).release(); | 351 return SkPDFMakeDocument(stream, nullptr, dpi, jpegEncoder).release(); |
395 } | 352 } |
396 | 353 |
397 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { | 354 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { |
398 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; }; | 355 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; }; |
399 SkAutoTDelete<SkFILEWStream> stream(new SkFILEWStream(path)); | 356 SkAutoTDelete<SkFILEWStream> stream(new SkFILEWStream(path)); |
400 return stream->isValid() | 357 return stream->isValid() |
401 ? SkPDFMakeDocument(stream.release(), delete_wstream, dpi, nullptr).rele ase() | 358 ? SkPDFMakeDocument(stream.release(), delete_wstream, dpi, nullptr).rele ase() |
402 : nullptr; | 359 : nullptr; |
403 } | 360 } |
OLD | NEW |