1 /* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkDocument.h" 9 #include "SkPDFCanon.h" 10 #include "SkPDFDevice.h" 11 #include "SkPDFFont.h" 12 #include "SkPDFStream.h" 13 #include "SkPDFTypes.h" 14 #include "SkPDFUtils.h" 15 #include "SkStream.h" 16 #include "SkPDFMetadata.h" 17 18 class SkPDFDict; 19 20 static void emit_pdf_header(SkWStream* stream) { 21 stream->writeText("%PDF-1.4\n%"); 22 // The PDF spec recommends including a comment with four bytes, all 23 // with their high bits set. This is "Skia" with the high bits set. 24 stream->write32(0xD3EBE9E1); 25 stream->writeText("\n"); 26 } 27 28 static void emit_pdf_footer(SkWStream* stream, 29 const SkPDFObjNumMap& objNumMap, 30 const SkPDFSubstituteMap& substitutes, 31 SkPDFObject* docCatalog, 32 int64_t objCount, 33 int32_t xRefFileOffset, 34 SkPDFObject* info /* take ownership */, 35 SkPDFObject* id /* take ownership */) { 36 SkPDFDict trailerDict; 37 // TODO(http://crbug.com/80908): Linearized format will take a 38 // Prev entry too. 39 trailerDict.insertInt("Size", int(objCount)); 40 trailerDict.insertObjRef("Root", SkRef(docCatalog)); 41 SkASSERT(info); 42 trailerDict.insertObjRef("Info", info); 43 if (id) { 44 trailerDict.insertObject("ID", id); 45 } 46 stream->writeText("trailer\n"); 47 trailerDict.emitObject(stream, objNumMap, substitutes); 48 stream->writeText("\nstartxref\n"); 49 stream->writeBigDecAsText(xRefFileOffset); 50 stream->writeText("\n%%EOF"); 51 } 52 53 static void perform_font_subsetting( 54 const SkTDArray<const SkPDFDevice*>& pageDevices, 55 SkPDFSubstituteMap* substituteMap) { 56 SkASSERT(substituteMap); 57 58 SkPDFGlyphSetMap usage; 59 for (int i = 0; i < pageDevices.count(); ++i) { 60 usage.merge(pageDevices[i]->getFontGlyphUsage()); 61 } 62 SkPDFGlyphSetMap::F2BIter iterator(usage); 63 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next(); 64 while (entry) { 65 SkAutoTUnref<SkPDFFont> subsetFont( 66 entry->fFont->getFontSubset(entry->fGlyphSet)); 67 if (subsetFont) { 68 substituteMap->setSubstitute(entry->fFont, subsetFont.get()); 69 } 70 entry = iterator.next(); 71 } 72 } 73 74 static SkPDFObject* create_pdf_page_content(const SkPDFDevice* pageDevice) { 75 SkAutoTDelete<SkStreamAsset> content(pageDevice->content()); 76 return new SkPDFStream(content.get()); 77 } 78 79 static SkPDFDict* create_pdf_page(const SkPDFDevice* pageDevice) { 80 SkAutoTUnref<SkPDFDict> page(new SkPDFDict("Page")); 81 page->insertObject("Resources", pageDevice->createResourceDict()); 82 page->insertObject("MediaBox", pageDevice->copyMediaBox()); 83 SkAutoTUnref<SkPDFArray> annotations(new SkPDFArray); 84 pageDevice->appendAnnotations(annotations); 85 if (annotations->size() > 0) { 86 page->insertObject("Annots", annotations.detach()); 87 } 88 page->insertObjRef("Contents", create_pdf_page_content(pageDevice)); 89 return page.detach(); 90 } 91 92 static void generate_page_tree(const SkTDArray<SkPDFDict*>& pages, 93 SkTDArray<SkPDFDict*>* pageTree, 94 SkPDFDict** rootNode) { 95 // PDF wants a tree describing all the pages in the document. We arbitrary 96 // choose 8 (kNodeSize) as the number of allowed children. The internal 97 // nodes have type "Pages" with an array of children, a parent pointer, and 98 // the number of leaves below the node as "Count." The leaves are passed 99 // into the method, have type "Page" and need a parent pointer. This method 100 // builds the tree bottom up, skipping internal nodes that would have only 101 // one child. 102 static const int kNodeSize = 8; 103 104 // curNodes takes a reference to its items, which it passes to pageTree. 105 SkTDArray<SkPDFDict*> curNodes; 106 curNodes.setReserve(pages.count()); 107 for (int i = 0; i < pages.count(); i++) { 108 SkSafeRef(pages[i]); 109 curNodes.push(pages[i]); 110 } 111 112 // nextRoundNodes passes its references to nodes on to curNodes. 113 SkTDArray<SkPDFDict*> nextRoundNodes; 114 nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize); 115 116 int treeCapacity = kNodeSize; 117 do { 118 for (int i = 0; i < curNodes.count(); ) { 119 if (i > 0 && i + 1 == curNodes.count()) { 120 nextRoundNodes.push(curNodes[i]); 121 break; 122 } 123 124 SkAutoTUnref<SkPDFDict> newNode(new SkPDFDict("Pages")); 125 SkAutoTUnref<SkPDFArray> kids(new SkPDFArray); 126 kids->reserve(kNodeSize); 127 128 int count = 0; 129 for (; i < curNodes.count() && count < kNodeSize; i++, count++) { 130 curNodes[i]->insertObjRef("Parent", SkRef(newNode.get())); 131 kids->appendObjRef(SkRef(curNodes[i])); 132 133 // TODO(vandebo): put the objects in strict access order. 134 // Probably doesn't matter because they are so small. 135 if (curNodes[i] != pages[0]) { 136 pageTree->push(curNodes[i]); // Transfer reference. 137 } else { 138 SkSafeUnref(curNodes[i]); 139 } 140 } 141 142 // treeCapacity is the number of leaf nodes possible for the 143 // current set of subtrees being generated. (i.e. 8, 64, 512, ...). 144 // It is hard to count the number of leaf nodes in the current 145 // subtree. However, by construction, we know that unless it's the 146 // last subtree for the current depth, the leaf count will be 147 // treeCapacity, otherwise it's what ever is left over after 148 // consuming treeCapacity chunks. 149 int pageCount = treeCapacity; 150 if (i == curNodes.count()) { 151 pageCount = ((pages.count() - 1) % treeCapacity) + 1; 152 } 153 newNode->insertInt("Count", pageCount); 154 newNode->insertObject("Kids", kids.detach()); 155 nextRoundNodes.push(newNode.detach()); // Transfer reference. 156 } 157 158 curNodes = nextRoundNodes; 159 nextRoundNodes.rewind(); 160 treeCapacity *= kNodeSize; 161 } while (curNodes.count() > 1); 162 163 pageTree->push(curNodes[0]); // Transfer reference. 164 if (rootNode) { 165 *rootNode = curNodes[0]; 166 } 167 } 168 169 static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices, 170 const SkPDFMetadata& metadata, 171 SkWStream* stream) { 172 if (pageDevices.isEmpty()) { 173 return false; 174 } 175 176 SkTDArray<SkPDFDict*> pages; 177 SkAutoTUnref<SkPDFDict> dests(new SkPDFDict); 178 179 for (int i = 0; i < pageDevices.count(); i++) { 180 SkASSERT(pageDevices[i]); 181 SkASSERT(i == 0 || 182 pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon()); 183 SkAutoTUnref<SkPDFDict> page(create_pdf_page(pageDevices[i])); 184 pageDevices[i]->appendDestinations(dests, page.get()); 185 pages.push(page.detach()); 186 } 187 188 SkAutoTUnref<SkPDFDict> docCatalog(new SkPDFDict("Catalog")); 189 190 SkAutoTUnref<SkPDFObject> infoDict( 191 metadata.createDocumentInformationDict()); 192 193 SkAutoTUnref<SkPDFObject> id, xmp; 194 #ifdef SK_PDF_GENERATE_PDFA 195 SkPDFMetadata::UUID uuid = metadata.uuid(); 196 // We use the same UUID for Document ID and Instance ID since this 197 // is the first revision of this document (and Skia does not 198 // support revising existing PDF documents). 199 // If we are not in PDF/A mode, don't use a UUID since testing 200 // works best with reproducible outputs. 201 id.reset(SkPDFMetadata::CreatePdfId(uuid, uuid)); 202 xmp.reset(metadata.createXMPObject(uuid, uuid)); 203 docCatalog->insertObjRef("Metadata", xmp.detach()); 204 205 // sRGB is specified by HTML, CSS, and SVG. 206 SkAutoTUnref<SkPDFDict> outputIntent(new SkPDFDict("OutputIntent")); 207 outputIntent->insertName("S", "GTS_PDFA1"); 208 outputIntent->insertString("RegistryName", "http://www.color.org"); 209 outputIntent->insertString("OutputConditionIdentifier", 210 "sRGB IEC61966-2.1"); 211 SkAutoTUnref<SkPDFArray> intentArray(new SkPDFArray); 212 intentArray->appendObject(outputIntent.detach()); 213 // Don't specify OutputIntents if we are not in PDF/A mode since 214 // no one has ever asked for this feature. 215 docCatalog->insertObject("OutputIntents", intentArray.detach()); 216 #endif 217 218 SkTDArray<SkPDFDict*> pageTree; 219 SkPDFDict* pageTreeRoot; 220 generate_page_tree(pages, &pageTree, &pageTreeRoot); 221 docCatalog->insertObjRef("Pages", SkRef(pageTreeRoot)); 222 223 if (dests->size() > 0) { 224 docCatalog->insertObjRef("Dests", dests.detach()); 225 } 226 227 // Build font subsetting info before proceeding. 228 SkPDFSubstituteMap substitutes; 229 perform_font_subsetting(pageDevices, &substitutes); 230 231 SkPDFObjNumMap objNumMap; 232 objNumMap.addObjectRecursively(infoDict, substitutes); 233 objNumMap.addObjectRecursively(docCatalog.get(), substitutes); 234 size_t baseOffset = stream->bytesWritten(); 235 emit_pdf_header(stream); 236 SkTDArray<int32_t> offsets; 237 for (int i = 0; i < objNumMap.objects().count(); ++i) { 238 SkPDFObject* object = objNumMap.objects()[i]; 239 size_t offset = stream->bytesWritten(); 240 // This assert checks that size(pdf_header) > 0 and that 241 // the output stream correctly reports bytesWritten(). 242 SkASSERT(offset > baseOffset); 243 offsets.push(SkToS32(offset - baseOffset)); 244 SkASSERT(object == substitutes.getSubstitute(object)); 245 SkASSERT(objNumMap.getObjectNumber(object) == i + 1); 246 stream->writeDecAsText(i + 1); 247 stream->writeText(" 0 obj\n"); // Generation number is always 0. 248 object->emitObject(stream, objNumMap, substitutes); 249 stream->writeText("\nendobj\n"); 250 } 251 int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset); 252 253 // Include the zeroth object in the count. 254 int32_t objCount = SkToS32(offsets.count() + 1); 255 256 stream->writeText("xref\n0 "); 257 stream->writeDecAsText(objCount); 258 stream->writeText("\n0000000000 65535 f \n"); 259 for (int i = 0; i < offsets.count(); i++) { 260 SkASSERT(offsets[i] > 0); 261 stream->writeBigDecAsText(offsets[i], 10); 262 stream->writeText(" 00000 n \n"); 263 } 264 emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount, 265 xRefFileOffset, infoDict.detach(), id.detach()); 266 267 // The page tree has both child and parent pointers, so it creates a 268 // reference cycle. We must clear that cycle to properly reclaim memory. 269 for (int i = 0; i < pageTree.count(); i++) { 270 pageTree[i]->clear(); 271 } 272 pageTree.safeUnrefAll(); 273 pages.unrefAll(); 274 return true; 275 } 276 277 #if 0 278 // TODO(halcanary): expose notEmbeddableCount in SkDocument 279 void GetCountOfFontTypes( 280 const SkTDArray<SkPDFDevice*>& pageDevices, 281 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], 282 int* notSubsettableCount, 283 int* notEmbeddableCount) { 284 sk_bzero(counts, sizeof(int) * 285 (SkAdvancedTypefaceMetrics::kOther_Font + 1)); 286 SkTDArray<SkFontID> seenFonts; 287 int notSubsettable = 0; 288 int notEmbeddable = 0; 289 290 for (int pageNumber = 0; pageNumber < pageDevices.count(); pageNumber++) { 291 const SkTDArray<SkPDFFont*>& fontResources = 292 pageDevices[pageNumber]->getFontResources(); 293 for (int font = 0; font < fontResources.count(); font++) { 294 SkFontID fontID = fontResources[font]->typeface()->uniqueID(); 295 if (seenFonts.find(fontID) == -1) { 296 counts[fontResources[font]->getType()]++; 297 seenFonts.push(fontID); 298 if (!fontResources[font]->canSubset()) { 299 notSubsettable++; 300 } 301 if (!fontResources[font]->canEmbed()) { 302 notEmbeddable++; 303 } 304 } 305 } 306 } 307 if (notSubsettableCount) { 308 *notSubsettableCount = notSubsettable; 309 310 } 311 if (notEmbeddableCount) { 312 *notEmbeddableCount = notEmbeddable; 313 } 314 } 315 #endif 316 317 template <typename T> static T* clone(const T* o) { return o ? new T(*o) : nullptr; } 318 //////////////////////////////////////////////////////////////////////////////// 319 320 namespace { 321 class SkDocument_PDF : public SkDocument { 322 public: 323 SkDocument_PDF(SkWStream* stream, 324 void (*doneProc)(SkWStream*, bool), 325 SkScalar rasterDpi, 326 SkPixelSerializer* jpegEncoder) 327 : SkDocument(stream, doneProc) 328 , fRasterDpi(rasterDpi) { 329 fCanon.fPixelSerializer.reset(SkSafeRef(jpegEncoder)); 330 } 331 332 virtual ~SkDocument_PDF() { 333 // subclasses must call close() in their destructors 334 this->close(); 335 } 336 337 protected: 338 SkCanvas* onBeginPage(SkScalar width, SkScalar height, 339 const SkRect& trimBox) override { 340 SkASSERT(!fCanvas.get()); 341 342 SkISize pageSize = SkISize::Make( 343 SkScalarRoundToInt(width), SkScalarRoundToInt(height)); 344 SkAutoTUnref<SkPDFDevice> device( 345 SkPDFDevice::Create(pageSize, fRasterDpi, &fCanon)); 346 fCanvas.reset(new SkCanvas(device.get())); 347 fPageDevices.push(device.detach()); 348 fCanvas->clipRect(trimBox); 349 fCanvas->translate(trimBox.x(), trimBox.y()); 350 return fCanvas.get(); 351 } 352 353 void onEndPage() override { 354 SkASSERT(fCanvas.get()); 355 fCanvas->flush(); 356 fCanvas.reset(nullptr); 357 } 358 359 bool onClose(SkWStream* stream) override { 360 SkASSERT(!fCanvas.get()); 361 362 bool success = emit_pdf_document(fPageDevices, fMetadata, stream); 363 fPageDevices.unrefAll(); 364 fCanon.reset(); 365 return success; 366 } 367 368 void onAbort() override { 369 fPageDevices.unrefAll(); 370 fCanon.reset(); 371 } 372 373 void setMetadata(const SkDocument::Attribute info[], 374 int infoCount, 375 const SkTime::DateTime* creationDate, 376 const SkTime::DateTime* modifiedDate) override { 377 fMetadata.fInfo.reset(info, infoCount); 378 fMetadata.fCreation.reset(clone(creationDate)); 379 fMetadata.fModified.reset(clone(modifiedDate)); 380 } 381 382 private: 383 SkPDFCanon fCanon; 384 SkTDArray<const SkPDFDevice*> fPageDevices; 385 SkAutoTUnref<SkCanvas> fCanvas; 386 SkScalar fRasterDpi; 387 SkPDFMetadata fMetadata; 388 }; 389 } // namespace 390 /////////////////////////////////////////////////////////////////////////////// 391 392 SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) { 393 return stream ? new SkDocument_PDF(stream, nullptr, dpi, nullptr) : nullptr; 394 } 395 396 SkDocument* SkDocument::CreatePDF(SkWStream* stream, 397 SkScalar dpi, 398 SkPixelSerializer* jpegEncoder) { 399 return stream 400 ? new SkDocument_PDF(stream, nullptr, dpi, jpegEncoder) 401 : nullptr; 402 } 403 404 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) { 405 SkFILEWStream* stream = new SkFILEWStream(path); 406 if (!stream->isValid()) { 407 delete stream; 408 return nullptr; 409 } 410 auto delete_wstream = [](SkWStream* stream, bool) { delete stream; }; 411 return new SkDocument_PDF(stream, delete_wstream, dpi, nullptr); 412 } 413