SkPDF: eliminate skpdfpage class
BUG=skia:3585 Review URL: https://codereview.chromium.org/1007083004
This commit is contained in:
parent
114912d4b9
commit
2f7ebcb424
@ -27,8 +27,6 @@
|
||||
'<(skia_src_path)/pdf/SkPDFFormXObject.h',
|
||||
'<(skia_src_path)/pdf/SkPDFGraphicState.cpp',
|
||||
'<(skia_src_path)/pdf/SkPDFGraphicState.h',
|
||||
'<(skia_src_path)/pdf/SkPDFPage.cpp',
|
||||
'<(skia_src_path)/pdf/SkPDFPage.h',
|
||||
'<(skia_src_path)/pdf/SkPDFResourceDict.cpp',
|
||||
'<(skia_src_path)/pdf/SkPDFResourceDict.h',
|
||||
'<(skia_src_path)/pdf/SkPDFShader.cpp',
|
||||
|
@ -10,7 +10,8 @@
|
||||
#include "SkPDFCatalog.h"
|
||||
#include "SkPDFDevice.h"
|
||||
#include "SkPDFFont.h"
|
||||
#include "SkPDFPage.h"
|
||||
#include "SkPDFResourceDict.h"
|
||||
#include "SkPDFStream.h"
|
||||
#include "SkPDFTypes.h"
|
||||
#include "SkStream.h"
|
||||
|
||||
@ -40,13 +41,14 @@ static void emit_pdf_footer(SkWStream* stream,
|
||||
stream->writeText("\n%%EOF");
|
||||
}
|
||||
|
||||
static void perform_font_subsetting(SkPDFCatalog* catalog,
|
||||
const SkTDArray<SkPDFPage*>& pages) {
|
||||
static void perform_font_subsetting(
|
||||
const SkTDArray<const SkPDFDevice*>& pageDevices,
|
||||
SkPDFCatalog* catalog) {
|
||||
SkASSERT(catalog);
|
||||
|
||||
SkPDFGlyphSetMap usage;
|
||||
for (int i = 0; i < pages.count(); ++i) {
|
||||
usage.merge(pages[i]->getFontGlyphUsage());
|
||||
for (int i = 0; i < pageDevices.count(); ++i) {
|
||||
usage.merge(pageDevices[i]->getFontGlyphUsage());
|
||||
}
|
||||
SkPDFGlyphSetMap::F2BIter iterator(usage);
|
||||
const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
|
||||
@ -60,23 +62,125 @@ static void perform_font_subsetting(SkPDFCatalog* catalog,
|
||||
}
|
||||
}
|
||||
|
||||
static SkPDFDict* create_pdf_page(const SkPDFDevice* pageDevice) {
|
||||
SkAutoTUnref<SkPDFDict> page(SkNEW_ARGS(SkPDFDict, ("Page")));
|
||||
SkAutoTUnref<SkPDFResourceDict> deviceResourceDict(
|
||||
pageDevice->createResourceDict());
|
||||
page->insert("Resources", deviceResourceDict.get());
|
||||
|
||||
SkAutoTUnref<SkPDFArray> mediaBox(pageDevice->copyMediaBox());
|
||||
page->insert("MediaBox", mediaBox.get());
|
||||
|
||||
SkPDFArray* annots = pageDevice->getAnnotations();
|
||||
if (annots && annots->size() > 0) {
|
||||
page->insert("Annots", annots);
|
||||
}
|
||||
|
||||
SkAutoTDelete<SkStreamAsset> content(pageDevice->content());
|
||||
SkAutoTUnref<SkPDFStream> contentStream(
|
||||
SkNEW_ARGS(SkPDFStream, (content.get())));
|
||||
page->insert("Contents", new SkPDFObjRef(contentStream.get()))->unref();
|
||||
return page.detach();
|
||||
}
|
||||
|
||||
static void generate_page_tree(const SkTDArray<SkPDFDict*>& pages,
|
||||
SkTDArray<SkPDFDict*>* pageTree,
|
||||
SkPDFDict** rootNode) {
|
||||
// PDF wants a tree describing all the pages in the document. We arbitrary
|
||||
// choose 8 (kNodeSize) as the number of allowed children. The internal
|
||||
// nodes have type "Pages" with an array of children, a parent pointer, and
|
||||
// the number of leaves below the node as "Count." The leaves are passed
|
||||
// into the method, have type "Page" and need a parent pointer. This method
|
||||
// builds the tree bottom up, skipping internal nodes that would have only
|
||||
// one child.
|
||||
static const int kNodeSize = 8;
|
||||
|
||||
SkAutoTUnref<SkPDFName> kidsName(new SkPDFName("Kids"));
|
||||
SkAutoTUnref<SkPDFName> countName(new SkPDFName("Count"));
|
||||
SkAutoTUnref<SkPDFName> parentName(new SkPDFName("Parent"));
|
||||
|
||||
// curNodes takes a reference to its items, which it passes to pageTree.
|
||||
SkTDArray<SkPDFDict*> curNodes;
|
||||
curNodes.setReserve(pages.count());
|
||||
for (int i = 0; i < pages.count(); i++) {
|
||||
SkSafeRef(pages[i]);
|
||||
curNodes.push(pages[i]);
|
||||
}
|
||||
|
||||
// nextRoundNodes passes its references to nodes on to curNodes.
|
||||
SkTDArray<SkPDFDict*> nextRoundNodes;
|
||||
nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
|
||||
|
||||
int treeCapacity = kNodeSize;
|
||||
do {
|
||||
for (int i = 0; i < curNodes.count(); ) {
|
||||
if (i > 0 && i + 1 == curNodes.count()) {
|
||||
nextRoundNodes.push(curNodes[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
SkPDFDict* newNode = new SkPDFDict("Pages");
|
||||
SkAutoTUnref<SkPDFObjRef> newNodeRef(new SkPDFObjRef(newNode));
|
||||
|
||||
SkAutoTUnref<SkPDFArray> kids(new SkPDFArray);
|
||||
kids->reserve(kNodeSize);
|
||||
|
||||
int count = 0;
|
||||
for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
|
||||
curNodes[i]->insert(parentName.get(), newNodeRef.get());
|
||||
kids->append(new SkPDFObjRef(curNodes[i]))->unref();
|
||||
|
||||
// TODO(vandebo): put the objects in strict access order.
|
||||
// Probably doesn't matter because they are so small.
|
||||
if (curNodes[i] != pages[0]) {
|
||||
pageTree->push(curNodes[i]); // Transfer reference.
|
||||
} else {
|
||||
SkSafeUnref(curNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// treeCapacity is the number of leaf nodes possible for the
|
||||
// current set of subtrees being generated. (i.e. 8, 64, 512, ...).
|
||||
// It is hard to count the number of leaf nodes in the current
|
||||
// subtree. However, by construction, we know that unless it's the
|
||||
// last subtree for the current depth, the leaf count will be
|
||||
// treeCapacity, otherwise it's what ever is left over after
|
||||
// consuming treeCapacity chunks.
|
||||
int pageCount = treeCapacity;
|
||||
if (i == curNodes.count()) {
|
||||
pageCount = ((pages.count() - 1) % treeCapacity) + 1;
|
||||
}
|
||||
newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref();
|
||||
newNode->insert(kidsName.get(), kids.get());
|
||||
nextRoundNodes.push(newNode); // Transfer reference.
|
||||
}
|
||||
|
||||
curNodes = nextRoundNodes;
|
||||
nextRoundNodes.rewind();
|
||||
treeCapacity *= kNodeSize;
|
||||
} while (curNodes.count() > 1);
|
||||
|
||||
pageTree->push(curNodes[0]); // Transfer reference.
|
||||
if (rootNode) {
|
||||
*rootNode = curNodes[0];
|
||||
}
|
||||
}
|
||||
|
||||
static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices,
|
||||
SkWStream* stream) {
|
||||
if (pageDevices.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkTDArray<SkPDFPage*> pages;
|
||||
SkTDArray<SkPDFDict*> pages;
|
||||
SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict));
|
||||
|
||||
for (int i = 0; i < pageDevices.count(); i++) {
|
||||
SkASSERT(pageDevices[i]);
|
||||
SkASSERT(i == 0 ||
|
||||
pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon());
|
||||
// Reference from new passed to pages.
|
||||
SkAutoTUnref<SkPDFPage> page(SkNEW_ARGS(SkPDFPage, (pageDevices[i])));
|
||||
page->finalizePage();
|
||||
page->appendDestinations(dests);
|
||||
SkAutoTUnref<SkPDFDict> page(create_pdf_page(pageDevices[i]));
|
||||
pageDevices[i]->appendDestinations(dests, page.get());
|
||||
pages.push(page.detach());
|
||||
}
|
||||
SkPDFCatalog catalog;
|
||||
@ -85,7 +189,7 @@ static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices,
|
||||
SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog")));
|
||||
|
||||
SkPDFDict* pageTreeRoot;
|
||||
SkPDFPage::GeneratePageTree(pages, &pageTree, &pageTreeRoot);
|
||||
generate_page_tree(pages, &pageTree, &pageTreeRoot);
|
||||
|
||||
docCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref();
|
||||
|
||||
@ -105,7 +209,7 @@ static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices,
|
||||
}
|
||||
|
||||
// Build font subsetting info before proceeding.
|
||||
perform_font_subsetting(&catalog, pages);
|
||||
perform_font_subsetting(pageDevices, &catalog);
|
||||
|
||||
SkTSet<SkPDFObject*> resourceSet;
|
||||
if (resourceSet.add(docCatalog.get())) {
|
||||
|
@ -1,139 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright 2010 The Android Open Source Project
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
|
||||
#include "SkData.h"
|
||||
#include "SkPDFCatalog.h"
|
||||
#include "SkPDFDevice.h"
|
||||
#include "SkPDFPage.h"
|
||||
#include "SkPDFResourceDict.h"
|
||||
|
||||
SkPDFPage::SkPDFPage(const SkPDFDevice* content)
|
||||
: SkPDFDict("Page"),
|
||||
fDevice(content) {
|
||||
SkSafeRef(content);
|
||||
}
|
||||
|
||||
SkPDFPage::~SkPDFPage() {}
|
||||
|
||||
void SkPDFPage::finalizePage() {
|
||||
if (fContentStream.get() == NULL) {
|
||||
SkAutoTUnref<SkPDFResourceDict> deviceResourceDict(
|
||||
fDevice->createResourceDict());
|
||||
this->insert("Resources", deviceResourceDict.get());
|
||||
SkSafeUnref(this->insert("MediaBox", fDevice->copyMediaBox()));
|
||||
SkPDFArray* annots = fDevice->getAnnotations();
|
||||
if (annots && annots->size() > 0) {
|
||||
insert("Annots", annots);
|
||||
}
|
||||
|
||||
SkAutoTDelete<SkStreamAsset> content(fDevice->content());
|
||||
fContentStream.reset(new SkPDFStream(content.get()));
|
||||
insert("Contents", new SkPDFObjRef(fContentStream.get()))->unref();
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void SkPDFPage::GeneratePageTree(const SkTDArray<SkPDFPage*>& pages,
|
||||
SkTDArray<SkPDFDict*>* pageTree,
|
||||
SkPDFDict** rootNode) {
|
||||
// PDF wants a tree describing all the pages in the document. We arbitrary
|
||||
// choose 8 (kNodeSize) as the number of allowed children. The internal
|
||||
// nodes have type "Pages" with an array of children, a parent pointer, and
|
||||
// the number of leaves below the node as "Count." The leaves are passed
|
||||
// into the method, have type "Page" and need a parent pointer. This method
|
||||
// builds the tree bottom up, skipping internal nodes that would have only
|
||||
// one child.
|
||||
static const int kNodeSize = 8;
|
||||
|
||||
SkAutoTUnref<SkPDFName> kidsName(new SkPDFName("Kids"));
|
||||
SkAutoTUnref<SkPDFName> countName(new SkPDFName("Count"));
|
||||
SkAutoTUnref<SkPDFName> parentName(new SkPDFName("Parent"));
|
||||
|
||||
// curNodes takes a reference to its items, which it passes to pageTree.
|
||||
SkTDArray<SkPDFDict*> curNodes;
|
||||
curNodes.setReserve(pages.count());
|
||||
for (int i = 0; i < pages.count(); i++) {
|
||||
SkSafeRef(pages[i]);
|
||||
curNodes.push(pages[i]);
|
||||
}
|
||||
|
||||
// nextRoundNodes passes its references to nodes on to curNodes.
|
||||
SkTDArray<SkPDFDict*> nextRoundNodes;
|
||||
nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
|
||||
|
||||
int treeCapacity = kNodeSize;
|
||||
do {
|
||||
for (int i = 0; i < curNodes.count(); ) {
|
||||
if (i > 0 && i + 1 == curNodes.count()) {
|
||||
nextRoundNodes.push(curNodes[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
SkPDFDict* newNode = new SkPDFDict("Pages");
|
||||
SkAutoTUnref<SkPDFObjRef> newNodeRef(new SkPDFObjRef(newNode));
|
||||
|
||||
SkAutoTUnref<SkPDFArray> kids(new SkPDFArray);
|
||||
kids->reserve(kNodeSize);
|
||||
|
||||
int count = 0;
|
||||
for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
|
||||
curNodes[i]->insert(parentName.get(), newNodeRef.get());
|
||||
kids->append(new SkPDFObjRef(curNodes[i]))->unref();
|
||||
|
||||
// TODO(vandebo): put the objects in strict access order.
|
||||
// Probably doesn't matter because they are so small.
|
||||
if (curNodes[i] != pages[0]) {
|
||||
pageTree->push(curNodes[i]); // Transfer reference.
|
||||
} else {
|
||||
SkSafeUnref(curNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// treeCapacity is the number of leaf nodes possible for the
|
||||
// current set of subtrees being generated. (i.e. 8, 64, 512, ...).
|
||||
// It is hard to count the number of leaf nodes in the current
|
||||
// subtree. However, by construction, we know that unless it's the
|
||||
// last subtree for the current depth, the leaf count will be
|
||||
// treeCapacity, otherwise it's what ever is left over after
|
||||
// consuming treeCapacity chunks.
|
||||
int pageCount = treeCapacity;
|
||||
if (i == curNodes.count()) {
|
||||
pageCount = ((pages.count() - 1) % treeCapacity) + 1;
|
||||
}
|
||||
newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref();
|
||||
newNode->insert(kidsName.get(), kids.get());
|
||||
nextRoundNodes.push(newNode); // Transfer reference.
|
||||
}
|
||||
|
||||
curNodes = nextRoundNodes;
|
||||
nextRoundNodes.rewind();
|
||||
treeCapacity *= kNodeSize;
|
||||
} while (curNodes.count() > 1);
|
||||
|
||||
pageTree->push(curNodes[0]); // Transfer reference.
|
||||
if (rootNode) {
|
||||
*rootNode = curNodes[0];
|
||||
}
|
||||
}
|
||||
|
||||
const SkTDArray<SkPDFFont*>& SkPDFPage::getFontResources() const {
|
||||
return fDevice->getFontResources();
|
||||
}
|
||||
|
||||
const SkPDFGlyphSetMap& SkPDFPage::getFontGlyphUsage() const {
|
||||
return fDevice->getFontGlyphUsage();
|
||||
}
|
||||
|
||||
void SkPDFPage::appendDestinations(SkPDFDict* dict) {
|
||||
fDevice->appendDestinations(dict, this);
|
||||
}
|
||||
|
||||
SkPDFObject* SkPDFPage::getContentStream() const {
|
||||
return fContentStream.get();
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright 2010 The Android Open Source Project
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SkPDFPage_DEFINED
|
||||
#define SkPDFPage_DEFINED
|
||||
|
||||
#include "SkPDFTypes.h"
|
||||
#include "SkPDFStream.h"
|
||||
#include "SkRefCnt.h"
|
||||
#include "SkTDArray.h"
|
||||
|
||||
class SkPDFCatalog;
|
||||
class SkPDFDevice;
|
||||
class SkWStream;
|
||||
|
||||
/** \class SkPDFPage
|
||||
|
||||
A SkPDFPage contains meta information about a page, is used in the page
|
||||
tree and points to the content of the page.
|
||||
*/
|
||||
class SkPDFPage : public SkPDFDict {
|
||||
SK_DECLARE_INST_COUNT(SkPDFPage)
|
||||
public:
|
||||
/** Create a PDF page with the passed PDF device. The device need not
|
||||
* have content on it yet.
|
||||
* @param content The page content.
|
||||
*/
|
||||
explicit SkPDFPage(const SkPDFDevice* content);
|
||||
~SkPDFPage();
|
||||
|
||||
/** Before a page and its contents can be sized and emitted, it must
|
||||
* be finalized. No changes to the PDFDevice will be honored after
|
||||
* finalizePage has been called.
|
||||
*/
|
||||
void finalizePage();
|
||||
|
||||
/** Add destinations for this page to the supplied dictionary.
|
||||
* @param dict Dictionary to add destinations to.
|
||||
*/
|
||||
void appendDestinations(SkPDFDict* dict);
|
||||
|
||||
/** Generate a page tree for the passed vector of pages. New objects are
|
||||
* added to the catalog. The pageTree vector is populated with all of
|
||||
* the 'Pages' dictionaries as well as the 'Page' objects. Page trees
|
||||
* have both parent and children links, creating reference cycles, so
|
||||
* it must be torn down explicitly. The first page is not added to
|
||||
* the pageTree dictionary array so the caller can handle it specially.
|
||||
* @param pages The ordered vector of page objects.
|
||||
* @param pageTree An output vector with all of the internal and leaf
|
||||
* nodes of the pageTree.
|
||||
* @param rootNode An output parameter set to the root node.
|
||||
*/
|
||||
static void GeneratePageTree(const SkTDArray<SkPDFPage*>& pages,
|
||||
SkTDArray<SkPDFDict*>* pageTree,
|
||||
SkPDFDict** rootNode);
|
||||
|
||||
/** Get the fonts used on this page.
|
||||
*/
|
||||
const SkTDArray<SkPDFFont*>& getFontResources() const;
|
||||
|
||||
/** Returns a SkPDFGlyphSetMap which represents glyph usage of every font
|
||||
* that shows on this page.
|
||||
*/
|
||||
const SkPDFGlyphSetMap& getFontGlyphUsage() const;
|
||||
|
||||
SkPDFObject* getContentStream() const;
|
||||
|
||||
private:
|
||||
// Multiple pages may reference the content.
|
||||
SkAutoTUnref<const SkPDFDevice> fDevice;
|
||||
|
||||
// Once the content is finalized, put it into a stream for output.
|
||||
SkAutoTUnref<SkPDFStream> fContentStream;
|
||||
typedef SkPDFDict INHERITED;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user