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