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