• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SkPDFPage(const SkRefPtr<SkPDFDevice> & content)22 SkPDFPage::SkPDFPage(const SkRefPtr<SkPDFDevice>& content)
23     : SkPDFDict("Page"),
24       fDevice(content) {
25 }
26 
~SkPDFPage()27 SkPDFPage::~SkPDFPage() {}
28 
finalizePage(SkPDFCatalog * catalog,bool firstPage,SkTDArray<SkPDFObject * > * resourceObjects)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 
getPageSize(SkPDFCatalog * catalog,off_t fileOffset)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 
emitPage(SkWStream * stream,SkPDFCatalog * catalog)51 void SkPDFPage::emitPage(SkWStream* stream, SkPDFCatalog* catalog) {
52     SkASSERT(fContentStream.get() != NULL);
53     fContentStream->emitObject(stream, catalog, true);
54 }
55 
56 // static
generatePageTree(const SkTDArray<SkPDFPage * > & pages,SkPDFCatalog * catalog,SkTDArray<SkPDFDict * > * pageTree,SkPDFDict ** rootNode)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 
getFontResources() const140 const SkTDArray<SkPDFFont*>& SkPDFPage::getFontResources() const {
141     return fDevice->getFontResources();
142 }
143