Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: src/pdf/SkPDFDocument.cpp

Issue 1823683005: SkPDF: Hold page objects, not SkPDFDevices. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: 2016-03-22 (Tuesday) 17:16:46 EDT Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/pdf/SkPDFDocument.h ('k') | src/pdf/SkPDFFont.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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 }
OLDNEW
« no previous file with comments | « src/pdf/SkPDFDocument.h ('k') | src/pdf/SkPDFFont.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698