1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
2d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org/*
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2010 The Android Open Source Project
4d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org *
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file.
7d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org */
8d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
9ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
1067ec1f8eecfb48bc0a6ba04c0057f103c1c9696fhalcanary#include "SkData.h"
11d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org#include "SkPDFCatalog.h"
12d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org#include "SkPDFDevice.h"
13d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org#include "SkPDFPage.h"
1447401354074549d8591da7fa115241766d3ee3d2commit-bot@chromium.org#include "SkPDFResourceDict.h"
15d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
161feb33068b1313d2647c50b90ae8e0a3d510db2ereed@google.comSkPDFPage::SkPDFPage(SkPDFDevice* content)
17d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    : SkPDFDict("Page"),
18d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org      fDevice(content) {
19d96d17b9c113ac694138224249ff2ce643e961ddvandebo@chromium.org  SkSafeRef(content);
20d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org}
21d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
22d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.orgSkPDFPage::~SkPDFPage() {}
23d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
24d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.orgvoid SkPDFPage::finalizePage(SkPDFCatalog* catalog, bool firstPage,
256addb1930013ebb2f984045141650fd7afcfa90fedisonn@google.com                             const SkTSet<SkPDFObject*>& knownResourceObjects,
266addb1930013ebb2f984045141650fd7afcfa90fedisonn@google.com                             SkTSet<SkPDFObject*>* newResourceObjects) {
2747401354074549d8591da7fa115241766d3ee3d2commit-bot@chromium.org    SkPDFResourceDict* resourceDict = fDevice->getResourceDict();
28d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    if (fContentStream.get() == NULL) {
2947401354074549d8591da7fa115241766d3ee3d2commit-bot@chromium.org        insert("Resources", resourceDict);
302a006c112743e07ce258ca223631fc19233f5ddcreed@google.com        SkSafeUnref(this->insert("MediaBox", fDevice->copyMediaBox()));
31238be8c7e5de5a83517440a3db7f7965b47fb010vandebo@chromium.org        if (!SkToBool(catalog->getDocumentFlags() &
32238be8c7e5de5a83517440a3db7f7965b47fb010vandebo@chromium.org                      SkPDFDocument::kNoLinks_Flags)) {
332a006c112743e07ce258ca223631fc19233f5ddcreed@google.com            SkPDFArray* annots = fDevice->getAnnotations();
342a006c112743e07ce258ca223631fc19233f5ddcreed@google.com            if (annots && annots->size() > 0) {
352a006c112743e07ce258ca223631fc19233f5ddcreed@google.com                insert("Annots", annots);
36238be8c7e5de5a83517440a3db7f7965b47fb010vandebo@chromium.org            }
37238be8c7e5de5a83517440a3db7f7965b47fb010vandebo@chromium.org        }
38d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
3967ec1f8eecfb48bc0a6ba04c0057f103c1c9696fhalcanary        SkAutoTUnref<SkData> content(fDevice->copyContentToData());
40d96d17b9c113ac694138224249ff2ce643e961ddvandebo@chromium.org        fContentStream.reset(new SkPDFStream(content.get()));
41f7c157610ff85f7323f5e213b62478dcc66edbecvandebo@chromium.org        insert("Contents", new SkPDFObjRef(fContentStream.get()))->unref();
42d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    }
43d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    catalog->addObject(fContentStream.get(), firstPage);
4447401354074549d8591da7fa115241766d3ee3d2commit-bot@chromium.org    resourceDict->getReferencedResources(knownResourceObjects,
4547401354074549d8591da7fa115241766d3ee3d2commit-bot@chromium.org                                         newResourceObjects,
4647401354074549d8591da7fa115241766d3ee3d2commit-bot@chromium.org                                         true);
47d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org}
48d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
49d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.orgoff_t SkPDFPage::getPageSize(SkPDFCatalog* catalog, off_t fileOffset) {
50d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    SkASSERT(fContentStream.get() != NULL);
5130580f69493fd8aa86c223736b4f7564ce458760vandebo@chromium.org    catalog->setFileOffset(fContentStream.get(), fileOffset);
52d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    return fContentStream->getOutputSize(catalog, true);
53d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org}
54d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
55d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.orgvoid SkPDFPage::emitPage(SkWStream* stream, SkPDFCatalog* catalog) {
56d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    SkASSERT(fContentStream.get() != NULL);
57d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    fContentStream->emitObject(stream, catalog, true);
58d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org}
59d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
60d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org// static
61f6c3ebdeb135dcdb9af225bd7af77f1fe1f92787reed@google.comvoid SkPDFPage::GeneratePageTree(const SkTDArray<SkPDFPage*>& pages,
62d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org                                 SkPDFCatalog* catalog,
63d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org                                 SkTDArray<SkPDFDict*>* pageTree,
64d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org                                 SkPDFDict** rootNode) {
65bd36ebe29463c4b70f5c91bb2440cdcf45b22b52vandebo@chromium.org    // PDF wants a tree describing all the pages in the document.  We arbitrary
66bd36ebe29463c4b70f5c91bb2440cdcf45b22b52vandebo@chromium.org    // choose 8 (kNodeSize) as the number of allowed children.  The internal
67bd36ebe29463c4b70f5c91bb2440cdcf45b22b52vandebo@chromium.org    // nodes have type "Pages" with an array of children, a parent pointer, and
68bd36ebe29463c4b70f5c91bb2440cdcf45b22b52vandebo@chromium.org    // the number of leaves below the node as "Count."  The leaves are passed
69bd36ebe29463c4b70f5c91bb2440cdcf45b22b52vandebo@chromium.org    // into the method, have type "Page" and need a parent pointer. This method
70bd36ebe29463c4b70f5c91bb2440cdcf45b22b52vandebo@chromium.org    // builds the tree bottom up, skipping internal nodes that would have only
71bd36ebe29463c4b70f5c91bb2440cdcf45b22b52vandebo@chromium.org    // one child.
72d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    static const int kNodeSize = 8;
73d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
74d96d17b9c113ac694138224249ff2ce643e961ddvandebo@chromium.org    SkAutoTUnref<SkPDFName> kidsName(new SkPDFName("Kids"));
75d96d17b9c113ac694138224249ff2ce643e961ddvandebo@chromium.org    SkAutoTUnref<SkPDFName> countName(new SkPDFName("Count"));
76d96d17b9c113ac694138224249ff2ce643e961ddvandebo@chromium.org    SkAutoTUnref<SkPDFName> parentName(new SkPDFName("Parent"));
77d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
78d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    // curNodes takes a reference to its items, which it passes to pageTree.
79d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    SkTDArray<SkPDFDict*> curNodes;
80d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    curNodes.setReserve(pages.count());
81d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    for (int i = 0; i < pages.count(); i++) {
8282065d667f64e232bcde2ad849756a6096fcbe6freed@google.com        SkSafeRef(pages[i]);
83d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org        curNodes.push(pages[i]);
84d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    }
85d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
86d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    // nextRoundNodes passes its references to nodes on to curNodes.
87d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    SkTDArray<SkPDFDict*> nextRoundNodes;
88d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
89d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
90bd36ebe29463c4b70f5c91bb2440cdcf45b22b52vandebo@chromium.org    int treeCapacity = kNodeSize;
91d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    do {
92d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org        for (int i = 0; i < curNodes.count(); ) {
93d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org            if (i > 0 && i + 1 == curNodes.count()) {
94d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org                nextRoundNodes.push(curNodes[i]);
95d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org                break;
96d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org            }
97d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
98d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org            SkPDFDict* newNode = new SkPDFDict("Pages");
99d96d17b9c113ac694138224249ff2ce643e961ddvandebo@chromium.org            SkAutoTUnref<SkPDFObjRef> newNodeRef(new SkPDFObjRef(newNode));
100d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
101d96d17b9c113ac694138224249ff2ce643e961ddvandebo@chromium.org            SkAutoTUnref<SkPDFArray> kids(new SkPDFArray);
102d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org            kids->reserve(kNodeSize);
103d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
104d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org            int count = 0;
105d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org            for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
106d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org                curNodes[i]->insert(parentName.get(), newNodeRef.get());
107f7c157610ff85f7323f5e213b62478dcc66edbecvandebo@chromium.org                kids->append(new SkPDFObjRef(curNodes[i]))->unref();
108d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
109769fa6a013baca6d7404e2bf096a34a7e3635fa5ctguil@chromium.org                // TODO(vandebo): put the objects in strict access order.
110d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org                // Probably doesn't matter because they are so small.
111d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org                if (curNodes[i] != pages[0]) {
112a5c7234e81748f76cbeede40e619351146e5286actguil@chromium.org                    pageTree->push(curNodes[i]);  // Transfer reference.
113d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org                    catalog->addObject(curNodes[i], false);
114d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org                } else {
11582065d667f64e232bcde2ad849756a6096fcbe6freed@google.com                    SkSafeUnref(curNodes[i]);
116fb6a53a406636a81094be8b75180fddcc02ff957vandebo@chromium.org                    catalog->addObject(curNodes[i], true);
117d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org                }
118d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org            }
119d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
1203c974427a95e9f24eae39167cf972506d70359c9vandebo@chromium.org            // treeCapacity is the number of leaf nodes possible for the
1213c974427a95e9f24eae39167cf972506d70359c9vandebo@chromium.org            // current set of subtrees being generated. (i.e. 8, 64, 512, ...).
1223c974427a95e9f24eae39167cf972506d70359c9vandebo@chromium.org            // It is hard to count the number of leaf nodes in the current
1233c974427a95e9f24eae39167cf972506d70359c9vandebo@chromium.org            // subtree. However, by construction, we know that unless it's the
1243c974427a95e9f24eae39167cf972506d70359c9vandebo@chromium.org            // last subtree for the current depth, the leaf count will be
1253c974427a95e9f24eae39167cf972506d70359c9vandebo@chromium.org            // treeCapacity, otherwise it's what ever is left over after
1263c974427a95e9f24eae39167cf972506d70359c9vandebo@chromium.org            // consuming treeCapacity chunks.
127bd36ebe29463c4b70f5c91bb2440cdcf45b22b52vandebo@chromium.org            int pageCount = treeCapacity;
1283c974427a95e9f24eae39167cf972506d70359c9vandebo@chromium.org            if (i == curNodes.count()) {
1293c974427a95e9f24eae39167cf972506d70359c9vandebo@chromium.org                pageCount = ((pages.count() - 1) % treeCapacity) + 1;
130bd36ebe29463c4b70f5c91bb2440cdcf45b22b52vandebo@chromium.org            }
131bd36ebe29463c4b70f5c91bb2440cdcf45b22b52vandebo@chromium.org            newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref();
1323c974427a95e9f24eae39167cf972506d70359c9vandebo@chromium.org            newNode->insert(kidsName.get(), kids.get());
133d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org            nextRoundNodes.push(newNode);  // Transfer reference.
134d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org        }
135d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
136d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org        curNodes = nextRoundNodes;
137d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org        nextRoundNodes.rewind();
138bd36ebe29463c4b70f5c91bb2440cdcf45b22b52vandebo@chromium.org        treeCapacity *= kNodeSize;
139a5c7234e81748f76cbeede40e619351146e5286actguil@chromium.org    } while (curNodes.count() > 1);
140d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org
141a5c7234e81748f76cbeede40e619351146e5286actguil@chromium.org    pageTree->push(curNodes[0]);  // Transfer reference.
142d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org    catalog->addObject(curNodes[0], false);
143769fa6a013baca6d7404e2bf096a34a7e3635fa5ctguil@chromium.org    if (rootNode) {
144d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org        *rootNode = curNodes[0];
145769fa6a013baca6d7404e2bf096a34a7e3635fa5ctguil@chromium.org    }
146d877fdbb6e64692285c3e6532d88b9458f65b3cdvandebo@chromium.org}
147f0ec2666d9a3f0f1662f0d63b5147628c49648aavandebo@chromium.org
148f0ec2666d9a3f0f1662f0d63b5147628c49648aavandebo@chromium.orgconst SkTDArray<SkPDFFont*>& SkPDFPage::getFontResources() const {
149f0ec2666d9a3f0f1662f0d63b5147628c49648aavandebo@chromium.org    return fDevice->getFontResources();
150f0ec2666d9a3f0f1662f0d63b5147628c49648aavandebo@chromium.org}
1519859428e71c6041928e6dd741ae3284017e78e81vandebo@chromium.org
1529859428e71c6041928e6dd741ae3284017e78e81vandebo@chromium.orgconst SkPDFGlyphSetMap& SkPDFPage::getFontGlyphUsage() const {
1539859428e71c6041928e6dd741ae3284017e78e81vandebo@chromium.org    return fDevice->getFontGlyphUsage();
1549859428e71c6041928e6dd741ae3284017e78e81vandebo@chromium.org}
155b58772f86659cfe0e8d9247fcee878dddd8fdad9epoger@google.com
156b58772f86659cfe0e8d9247fcee878dddd8fdad9epoger@google.comvoid SkPDFPage::appendDestinations(SkPDFDict* dict) {
157b58772f86659cfe0e8d9247fcee878dddd8fdad9epoger@google.com    fDevice->appendDestinations(dict, this);
158b58772f86659cfe0e8d9247fcee878dddd8fdad9epoger@google.com}
159