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 SkASSERT(curNodes[i]); |
| 120 nextRoundNodes.emplace_back(std::move(curNodes[i])); |
161 break; | 121 break; |
162 } | 122 } |
163 | 123 |
164 auto newNode = sk_make_sp<SkPDFDict>("Pages"); | 124 auto newNode = sk_make_sp<SkPDFDict>("Pages"); |
165 auto kids = sk_make_sp<SkPDFArray>(); | 125 auto kids = sk_make_sp<SkPDFArray>(); |
166 kids->reserve(kNodeSize); | 126 kids->reserve(kNodeSize); |
167 | 127 |
168 int count = 0; | 128 int count = 0; |
169 for (; i < curNodes.count() && count < kNodeSize; i++, count++) { | 129 for (; i < curNodes.count() && count < kNodeSize; i++, count++) { |
| 130 SkASSERT(curNodes[i]); |
170 curNodes[i]->insertObjRef("Parent", newNode); | 131 curNodes[i]->insertObjRef("Parent", newNode); |
171 kids->appendObjRef(sk_ref_sp(curNodes[i])); | 132 kids->appendObjRef(std::move(curNodes[i])); |
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 } | 133 } |
181 | 134 |
182 // treeCapacity is the number of leaf nodes possible for the | 135 // treeCapacity is the number of leaf nodes possible for the |
183 // current set of subtrees being generated. (i.e. 8, 64, 512, ...). | 136 // 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 | 137 // 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 | 138 // subtree. However, by construction, we know that unless it's the |
186 // last subtree for the current depth, the leaf count will be | 139 // last subtree for the current depth, the leaf count will be |
187 // treeCapacity, otherwise it's what ever is left over after | 140 // treeCapacity, otherwise it's what ever is left over after |
188 // consuming treeCapacity chunks. | 141 // consuming treeCapacity chunks. |
189 int pageCount = treeCapacity; | 142 int pageCount = treeCapacity; |
190 if (i == curNodes.count()) { | 143 if (i == curNodes.count()) { |
191 pageCount = ((pages.count() - 1) % treeCapacity) + 1; | 144 pageCount = ((totalPageCount - 1) % treeCapacity) + 1; |
192 } | 145 } |
193 newNode->insertInt("Count", pageCount); | 146 newNode->insertInt("Count", pageCount); |
194 newNode->insertObject("Kids", std::move(kids)); | 147 newNode->insertObject("Kids", std::move(kids)); |
195 nextRoundNodes.push(newNode.release()); // Transfer reference. | 148 nextRoundNodes.emplace_back(std::move(newNode)); |
196 } | 149 } |
| 150 SkDEBUGCODE( for (const auto& n : curNodes) { SkASSERT(!n); } ); |
197 | 151 |
198 curNodes = nextRoundNodes; | 152 curNodes.swap(&nextRoundNodes); |
199 nextRoundNodes.rewind(); | 153 nextRoundNodes.reset(); |
200 treeCapacity *= kNodeSize; | 154 treeCapacity *= kNodeSize; |
201 } while (curNodes.count() > 1); | 155 } while (curNodes.count() > 1); |
202 | 156 return std::move(curNodes[0]); |
203 pageTree->push(curNodes[0]); // Transfer reference. | |
204 return sk_ref_sp(curNodes[0]); | |
205 } | 157 } |
206 | 158 |
207 #if 0 | 159 #if 0 |
208 // TODO(halcanary): expose notEmbeddableCount in SkDocument | 160 // TODO(halcanary): expose notEmbeddableCount in SkDocument |
209 void GetCountOfFontTypes( | 161 void GetCountOfFontTypes( |
210 const SkTDArray<SkPDFDevice*>& pageDevices, | 162 const SkTDArray<SkPDFDevice*>& pageDevices, |
211 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], | 163 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], |
212 int* notSubsettableCount, | 164 int* notSubsettableCount, |
213 int* notEmbeddableCount) { | 165 int* notEmbeddableCount) { |
214 sk_bzero(counts, sizeof(int) * | 166 sk_bzero(counts, sizeof(int) * |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
262 } | 214 } |
263 | 215 |
264 void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) { | 216 void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) { |
265 fObjectSerializer.addObjectRecursively(object); | 217 fObjectSerializer.addObjectRecursively(object); |
266 fObjectSerializer.serializeObjects(this->getStream()); | 218 fObjectSerializer.serializeObjects(this->getStream()); |
267 } | 219 } |
268 | 220 |
269 SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height, | 221 SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height, |
270 const SkRect& trimBox) { | 222 const SkRect& trimBox) { |
271 SkASSERT(!fCanvas.get()); // endPage() was called before this. | 223 SkASSERT(!fCanvas.get()); // endPage() was called before this. |
272 if (fPageDevices.empty()) { | 224 if (fPages.empty()) { |
273 // if this is the first page if the document. | 225 // if this is the first page if the document. |
274 fObjectSerializer.serializeHeader(this->getStream(), fMetadata); | 226 fObjectSerializer.serializeHeader(this->getStream(), fMetadata); |
| 227 fDests = sk_make_sp<SkPDFDict>(); |
275 } | 228 } |
276 SkISize pageSize = SkISize::Make( | 229 SkISize pageSize = SkISize::Make( |
277 SkScalarRoundToInt(width), SkScalarRoundToInt(height)); | 230 SkScalarRoundToInt(width), SkScalarRoundToInt(height)); |
278 sk_sp<SkPDFDevice> device( | 231 fPageDevice.reset( |
279 SkPDFDevice::Create(pageSize, fRasterDpi, this)); | 232 SkPDFDevice::Create(pageSize, fRasterDpi, this)); |
280 fCanvas = sk_make_sp<SkPDFCanvas>(device); | 233 fCanvas = sk_make_sp<SkPDFCanvas>(fPageDevice); |
281 fPageDevices.push_back(std::move(device)); | |
282 fCanvas->clipRect(trimBox); | 234 fCanvas->clipRect(trimBox); |
283 fCanvas->translate(trimBox.x(), trimBox.y()); | 235 fCanvas->translate(trimBox.x(), trimBox.y()); |
284 return fCanvas.get(); | 236 return fCanvas.get(); |
285 } | 237 } |
286 | 238 |
287 void SkPDFDocument::onEndPage() { | 239 void SkPDFDocument::onEndPage() { |
288 SkASSERT(fCanvas.get()); | 240 SkASSERT(fCanvas.get()); |
289 fCanvas->flush(); | 241 fCanvas->flush(); |
290 fCanvas.reset(nullptr); | 242 fCanvas.reset(nullptr); |
| 243 SkASSERT(fPageDevice); |
| 244 fGlyphUsage.merge(fPageDevice->getFontGlyphUsage()); |
| 245 auto page = sk_make_sp<SkPDFDict>("Page"); |
| 246 page->insertObject("Resources", fPageDevice->makeResourceDict()); |
| 247 page->insertObject("MediaBox", fPageDevice->copyMediaBox()); |
| 248 auto annotations = sk_make_sp<SkPDFArray>(); |
| 249 fPageDevice->appendAnnotations(annotations.get()); |
| 250 if (annotations->size() > 0) { |
| 251 page->insertObject("Annots", std::move(annotations)); |
| 252 } |
| 253 auto contentData = fPageDevice->content(); |
| 254 auto contentObject = sk_make_sp<SkPDFStream>(contentData.get()); |
| 255 this->serialize(contentObject); |
| 256 page->insertObjRef("Contents", std::move(contentObject)); |
| 257 fPageDevice->appendDestinations(fDests.get(), page.get()); |
| 258 fPages.emplace_back(std::move(page)); |
| 259 fPageDevice.reset(nullptr); |
291 } | 260 } |
292 | 261 |
293 void SkPDFDocument::onAbort() { | 262 void SkPDFDocument::onAbort() { |
294 fCanvas.reset(nullptr); | 263 fCanvas.reset(nullptr); |
295 fPageDevices.reset(); | 264 fPages.reset(); |
296 fCanon.reset(); | 265 fCanon.reset(); |
297 renew(&fObjectSerializer); | 266 renew(&fObjectSerializer); |
298 } | 267 } |
299 | 268 |
300 void SkPDFDocument::setMetadata(const SkDocument::Attribute info[], | 269 void SkPDFDocument::setMetadata(const SkDocument::Attribute info[], |
301 int infoCount, | 270 int infoCount, |
302 const SkTime::DateTime* creationDate, | 271 const SkTime::DateTime* creationDate, |
303 const SkTime::DateTime* modifiedDate) { | 272 const SkTime::DateTime* modifiedDate) { |
304 fMetadata.fInfo.reset(info, infoCount); | 273 fMetadata.fInfo.reset(info, infoCount); |
305 fMetadata.fCreation.reset(clone(creationDate)); | 274 fMetadata.fCreation.reset(clone(creationDate)); |
306 fMetadata.fModified.reset(clone(modifiedDate)); | 275 fMetadata.fModified.reset(clone(modifiedDate)); |
307 } | 276 } |
308 | 277 |
309 bool SkPDFDocument::onClose(SkWStream* stream) { | 278 bool SkPDFDocument::onClose(SkWStream* stream) { |
310 SkASSERT(!fCanvas.get()); | 279 SkASSERT(!fCanvas.get()); |
311 if (fPageDevices.empty()) { | 280 if (fPages.empty()) { |
312 fPageDevices.reset(); | 281 fPages.reset(); |
313 fCanon.reset(); | 282 fCanon.reset(); |
314 renew(&fObjectSerializer); | 283 renew(&fObjectSerializer); |
315 return false; | 284 return false; |
316 } | 285 } |
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"); | 286 auto docCatalog = sk_make_sp<SkPDFDict>("Catalog"); |
329 | |
330 sk_sp<SkPDFObject> id, xmp; | 287 sk_sp<SkPDFObject> id, xmp; |
331 #ifdef SK_PDF_GENERATE_PDFA | 288 #ifdef SK_PDF_GENERATE_PDFA |
332 SkPDFMetadata::UUID uuid = metadata.uuid(); | 289 SkPDFMetadata::UUID uuid = metadata.uuid(); |
333 // We use the same UUID for Document ID and Instance ID since this | 290 // 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 | 291 // is the first revision of this document (and Skia does not |
335 // support revising existing PDF documents). | 292 // support revising existing PDF documents). |
336 // If we are not in PDF/A mode, don't use a UUID since testing | 293 // If we are not in PDF/A mode, don't use a UUID since testing |
337 // works best with reproducible outputs. | 294 // works best with reproducible outputs. |
338 id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid)); | 295 id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid)); |
339 xmp.reset(metadata.createXMPObject(uuid, uuid)); | 296 xmp.reset(metadata.createXMPObject(uuid, uuid)); |
340 docCatalog->insertObjRef("Metadata", std::move(xmp)); | 297 docCatalog->insertObjRef("Metadata", std::move(xmp)); |
341 | 298 |
342 // sRGB is specified by HTML, CSS, and SVG. | 299 // sRGB is specified by HTML, CSS, and SVG. |
343 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent"); | 300 auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent"); |
344 outputIntent->insertName("S", "GTS_PDFA1"); | 301 outputIntent->insertName("S", "GTS_PDFA1"); |
345 outputIntent->insertString("RegistryName", "http://www.color.org"); | 302 outputIntent->insertString("RegistryName", "http://www.color.org"); |
346 outputIntent->insertString("OutputConditionIdentifier", | 303 outputIntent->insertString("OutputConditionIdentifier", |
347 "sRGB IEC61966-2.1"); | 304 "sRGB IEC61966-2.1"); |
348 auto intentArray = sk_make_sp<SkPDFArray>(); | 305 auto intentArray = sk_make_sp<SkPDFArray>(); |
349 intentArray->appendObject(std::move(outputIntent)); | 306 intentArray->appendObject(std::move(outputIntent)); |
350 // Don't specify OutputIntents if we are not in PDF/A mode since | 307 // Don't specify OutputIntents if we are not in PDF/A mode since |
351 // no one has ever asked for this feature. | 308 // no one has ever asked for this feature. |
352 docCatalog->insertObject("OutputIntents", std::move(intentArray)); | 309 docCatalog->insertObject("OutputIntents", std::move(intentArray)); |
353 #endif | 310 #endif |
354 | 311 |
355 SkTDArray<SkPDFDict*> pageTree; | 312 docCatalog->insertObjRef("Pages", generate_page_tree(&fPages)); |
356 docCatalog->insertObjRef("Pages", generate_page_tree(pages, &pageTree)); | |
357 | 313 |
358 if (dests->size() > 0) { | 314 if (fDests->size() > 0) { |
359 docCatalog->insertObjRef("Dests", std::move(dests)); | 315 docCatalog->insertObjRef("Dests", std::move(fDests)); |
360 } | 316 } |
361 | 317 |
362 // Build font subsetting info before calling addObjectRecursively(). | 318 // Build font subsetting info before calling addObjectRecursively(). |
363 perform_font_subsetting(fPageDevices, &fObjectSerializer.fSubstituteMap); | 319 for (const auto& entry : fGlyphUsage) { |
| 320 sk_sp<SkPDFFont> subsetFont( |
| 321 entry.fFont->getFontSubset(entry.fGlyphSet)); |
| 322 if (subsetFont) { |
| 323 fObjectSerializer.fSubstituteMap.setSubstitute( |
| 324 entry.fFont, subsetFont.get()); |
| 325 } |
| 326 } |
364 | 327 |
365 fObjectSerializer.addObjectRecursively(docCatalog); | 328 fObjectSerializer.addObjectRecursively(docCatalog); |
366 fObjectSerializer.serializeObjects(this->getStream()); | 329 fObjectSerializer.serializeObjects(this->getStream()); |
367 fObjectSerializer.serializeFooter( | 330 fObjectSerializer.serializeFooter( |
368 this->getStream(), docCatalog, std::move(id)); | 331 this->getStream(), docCatalog, std::move(id)); |
369 pageTree.unrefAll(); // TODO(halcanary): make this unnecesary by | 332 |
370 // refactoring generate_page_tree(). | 333 SkASSERT(fPages.count() == 0); |
371 pages.unrefAll(); | |
372 fPageDevices.reset(); | |
373 fCanon.reset(); | 334 fCanon.reset(); |
374 renew(&fObjectSerializer); | 335 renew(&fObjectSerializer); |
375 return true; | 336 return true; |
376 } | 337 } |
377 | 338 |
378 /////////////////////////////////////////////////////////////////////////////// | 339 /////////////////////////////////////////////////////////////////////////////// |
379 | 340 |
380 sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream, | 341 sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream, |
381 void (*proc)(SkWStream*, bool), | 342 void (*proc)(SkWStream*, bool), |
382 SkScalar dpi, | 343 SkScalar dpi, |
(...skipping 11 matching lines...) Expand all Loading... |
394 return SkPDFMakeDocument(stream, nullptr, dpi, jpegEncoder).release(); | 355 return SkPDFMakeDocument(stream, nullptr, dpi, jpegEncoder).release(); |
395 } | 356 } |
396 | 357 |
397 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { | 358 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { |
398 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; }; | 359 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; }; |
399 SkAutoTDelete<SkFILEWStream> stream(new SkFILEWStream(path)); | 360 SkAutoTDelete<SkFILEWStream> stream(new SkFILEWStream(path)); |
400 return stream->isValid() | 361 return stream->isValid() |
401 ? SkPDFMakeDocument(stream.release(), delete_wstream, dpi, nullptr).rele
ase() | 362 ? SkPDFMakeDocument(stream.release(), delete_wstream, dpi, nullptr).rele
ase() |
402 : nullptr; | 363 : nullptr; |
403 } | 364 } |
OLD | NEW |