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