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

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: 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
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 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
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
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 }
OLDNEW
« src/pdf/SkPDFDocument.h ('K') | « src/pdf/SkPDFDocument.h ('k') | src/pdf/SkPDFFont.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698