Home | History | Annotate | Download | only in doc
      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