Home | History | Annotate | Download | only in pdf
      1 
      2 /*
      3  * Copyright 2010 The Android Open Source Project
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "SkPDFCatalog.h"
     11 #include "SkPDFDevice.h"
     12 #include "SkPDFPage.h"
     13 #include "SkStream.h"
     14 
     15 SkPDFPage::SkPDFPage(SkPDFDevice* content)
     16     : SkPDFDict("Page"),
     17       fDevice(content) {
     18 }
     19 
     20 SkPDFPage::~SkPDFPage() {}
     21 
     22 void SkPDFPage::finalizePage(SkPDFCatalog* catalog, bool firstPage,
     23                              SkTDArray<SkPDFObject*>* resourceObjects) {
     24     if (fContentStream.get() == NULL) {
     25         insert("Resources", fDevice->getResourceDict());
     26         insert("MediaBox", fDevice->getMediaBox().get());
     27 
     28         SkRefPtr<SkStream> content = fDevice->content();
     29         content->unref();  // SkRefPtr and content() both took a reference.
     30         fContentStream = new SkPDFStream(content.get());
     31         fContentStream->unref();  // SkRefPtr and new both took a reference.
     32         insert("Contents", new SkPDFObjRef(fContentStream.get()))->unref();
     33     }
     34     catalog->addObject(fContentStream.get(), firstPage);
     35     fDevice->getResources(resourceObjects);
     36 }
     37 
     38 off_t SkPDFPage::getPageSize(SkPDFCatalog* catalog, off_t fileOffset) {
     39     SkASSERT(fContentStream.get() != NULL);
     40     catalog->setFileOffset(fContentStream.get(), fileOffset);
     41     return fContentStream->getOutputSize(catalog, true);
     42 }
     43 
     44 void SkPDFPage::emitPage(SkWStream* stream, SkPDFCatalog* catalog) {
     45     SkASSERT(fContentStream.get() != NULL);
     46     fContentStream->emitObject(stream, catalog, true);
     47 }
     48 
     49 // static
     50 void SkPDFPage::GeneratePageTree(const SkTDArray<SkPDFPage*>& pages,
     51                                  SkPDFCatalog* catalog,
     52                                  SkTDArray<SkPDFDict*>* pageTree,
     53                                  SkPDFDict** rootNode) {
     54     // PDF wants a tree describing all the pages in the document.  We arbitrary
     55     // choose 8 (kNodeSize) as the number of allowed children.  The internal
     56     // nodes have type "Pages" with an array of children, a parent pointer, and
     57     // the number of leaves below the node as "Count."  The leaves are passed
     58     // into the method, have type "Page" and need a parent pointer. This method
     59     // builds the tree bottom up, skipping internal nodes that would have only
     60     // one child.
     61     static const int kNodeSize = 8;
     62 
     63     SkRefPtr<SkPDFName> kidsName = new SkPDFName("Kids");
     64     kidsName->unref();  // SkRefPtr and new both took a reference.
     65     SkRefPtr<SkPDFName> countName = new SkPDFName("Count");
     66     countName->unref();  // SkRefPtr and new both took a reference.
     67     SkRefPtr<SkPDFName> parentName = new SkPDFName("Parent");
     68     parentName->unref();  // SkRefPtr and new both took a reference.
     69 
     70     // curNodes takes a reference to its items, which it passes to pageTree.
     71     SkTDArray<SkPDFDict*> curNodes;
     72     curNodes.setReserve(pages.count());
     73     for (int i = 0; i < pages.count(); i++) {
     74         SkSafeRef(pages[i]);
     75         curNodes.push(pages[i]);
     76     }
     77 
     78     // nextRoundNodes passes its references to nodes on to curNodes.
     79     SkTDArray<SkPDFDict*> nextRoundNodes;
     80     nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
     81 
     82     int treeCapacity = kNodeSize;
     83     do {
     84         for (int i = 0; i < curNodes.count(); ) {
     85             if (i > 0 && i + 1 == curNodes.count()) {
     86                 nextRoundNodes.push(curNodes[i]);
     87                 break;
     88             }
     89 
     90             SkPDFDict* newNode = new SkPDFDict("Pages");
     91             SkRefPtr<SkPDFObjRef> newNodeRef = new SkPDFObjRef(newNode);
     92             newNodeRef->unref();  // SkRefPtr and new both took a reference.
     93 
     94             SkRefPtr<SkPDFArray> kids = new SkPDFArray;
     95             kids->unref();  // SkRefPtr and new both took a reference.
     96             kids->reserve(kNodeSize);
     97 
     98             int count = 0;
     99             for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
    100                 curNodes[i]->insert(parentName.get(), newNodeRef.get());
    101                 kids->append(new SkPDFObjRef(curNodes[i]))->unref();
    102 
    103                 // TODO(vandebo): put the objects in strict access order.
    104                 // Probably doesn't matter because they are so small.
    105                 if (curNodes[i] != pages[0]) {
    106                     pageTree->push(curNodes[i]);  // Transfer reference.
    107                     catalog->addObject(curNodes[i], false);
    108                 } else {
    109                     SkSafeUnref(curNodes[i]);
    110                     catalog->addObject(curNodes[i], true);
    111                 }
    112             }
    113 
    114             newNode->insert(kidsName.get(), kids.get());
    115             int pageCount = treeCapacity;
    116             if (count < kNodeSize) {
    117                 pageCount = pages.count() % treeCapacity;
    118             }
    119             newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref();
    120             nextRoundNodes.push(newNode);  // Transfer reference.
    121         }
    122 
    123         curNodes = nextRoundNodes;
    124         nextRoundNodes.rewind();
    125         treeCapacity *= kNodeSize;
    126     } while (curNodes.count() > 1);
    127 
    128     pageTree->push(curNodes[0]);  // Transfer reference.
    129     catalog->addObject(curNodes[0], false);
    130     if (rootNode) {
    131         *rootNode = curNodes[0];
    132     }
    133 }
    134 
    135 const SkTDArray<SkPDFFont*>& SkPDFPage::getFontResources() const {
    136     return fDevice->getFontResources();
    137 }
    138 
    139 const SkPDFGlyphSetMap& SkPDFPage::getFontGlyphUsage() const {
    140     return fDevice->getFontGlyphUsage();
    141 }
    142