SkPDF: Hold page objects, not SkPDFDevices.
Page Contents: serialize early, at endPage() SkPDFDocument: Rather than holding all SkPDFDevices until onClose(), store fGlyphUseage and array of pages objects. perform_font_subsetting function removed: First half moved to onEndPage. Second half moved to onClose. create_pdf_page function removed: Merged into onEndPage. generate_page_tree: Refactored to use SkTArray<sk_sp<T>> over SkTDArray<T*>. (the former is explicit about ownership, the latter is unclear.) No longer populates a structure of objects to be dumped, unnecessary since dump is always called after serialization. Takes ownership of fPages and returns root of tree-ified version. Less reference churn. SkPDFGlyphSetMap: use new-style iterator. BUG=skia:5087 GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1823683005 Review URL: https://codereview.chromium.org/1823683005
This commit is contained in:
parent
0a291c7b7e
commit
cc77c12293
@ -93,45 +93,9 @@ int32_t SkPDFObjectSerializer::offset(SkWStream* wStream) {
|
||||
return SkToS32(offset - fBaseOffset);
|
||||
}
|
||||
|
||||
static void perform_font_subsetting(
|
||||
const SkTArray<sk_sp<const SkPDFDevice>>& pageDevices,
|
||||
SkPDFSubstituteMap* substituteMap) {
|
||||
SkASSERT(substituteMap);
|
||||
|
||||
SkPDFGlyphSetMap usage;
|
||||
for (const sk_sp<const SkPDFDevice>& pageDevice : pageDevices) {
|
||||
usage.merge(pageDevice->getFontGlyphUsage());
|
||||
}
|
||||
SkPDFGlyphSetMap::F2BIter iterator(usage);
|
||||
const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
|
||||
while (entry) {
|
||||
sk_sp<SkPDFFont> subsetFont(
|
||||
entry->fFont->getFontSubset(entry->fGlyphSet));
|
||||
if (subsetFont) {
|
||||
substituteMap->setSubstitute(entry->fFont, subsetFont.get());
|
||||
}
|
||||
entry = iterator.next();
|
||||
}
|
||||
}
|
||||
|
||||
static sk_sp<SkPDFDict> create_pdf_page(const SkPDFDevice* pageDevice) {
|
||||
auto page = sk_make_sp<SkPDFDict>("Page");
|
||||
page->insertObject("Resources", pageDevice->makeResourceDict());
|
||||
page->insertObject("MediaBox", pageDevice->copyMediaBox());
|
||||
auto annotations = sk_make_sp<SkPDFArray>();
|
||||
pageDevice->appendAnnotations(annotations.get());
|
||||
if (annotations->size() > 0) {
|
||||
page->insertObject("Annots", std::move(annotations));
|
||||
}
|
||||
auto content = pageDevice->content();
|
||||
page->insertObjRef("Contents", sk_make_sp<SkPDFStream>(content.get()));
|
||||
return page;
|
||||
}
|
||||
|
||||
// return root node.
|
||||
static sk_sp<SkPDFDict> generate_page_tree(
|
||||
SkTDArray<SkPDFDict*>& pages,
|
||||
SkTDArray<SkPDFDict*>* pageTree) {
|
||||
static sk_sp<SkPDFDict> generate_page_tree(SkTArray<sk_sp<SkPDFDict>>* pages) {
|
||||
// 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
|
||||
@ -142,22 +106,18 @@ static sk_sp<SkPDFDict> generate_page_tree(
|
||||
static const int kNodeSize = 8;
|
||||
|
||||
// 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]);
|
||||
}
|
||||
int totalPageCount = pages->count();
|
||||
SkTArray<sk_sp<SkPDFDict>> curNodes;
|
||||
curNodes.swap(pages);
|
||||
|
||||
// nextRoundNodes passes its references to nodes on to curNodes.
|
||||
SkTDArray<SkPDFDict*> nextRoundNodes;
|
||||
nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
|
||||
|
||||
int treeCapacity = kNodeSize;
|
||||
do {
|
||||
SkTArray<sk_sp<SkPDFDict>> nextRoundNodes;
|
||||
for (int i = 0; i < curNodes.count(); ) {
|
||||
if (i > 0 && i + 1 == curNodes.count()) {
|
||||
nextRoundNodes.push(curNodes[i]);
|
||||
SkASSERT(curNodes[i]);
|
||||
nextRoundNodes.emplace_back(std::move(curNodes[i]));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -167,16 +127,9 @@ static sk_sp<SkPDFDict> generate_page_tree(
|
||||
|
||||
int count = 0;
|
||||
for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
|
||||
SkASSERT(curNodes[i]);
|
||||
curNodes[i]->insertObjRef("Parent", newNode);
|
||||
kids->appendObjRef(sk_ref_sp(curNodes[i]));
|
||||
|
||||
// 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]);
|
||||
}
|
||||
kids->appendObjRef(std::move(curNodes[i]));
|
||||
}
|
||||
|
||||
// treeCapacity is the number of leaf nodes possible for the
|
||||
@ -188,20 +141,19 @@ static sk_sp<SkPDFDict> generate_page_tree(
|
||||
// consuming treeCapacity chunks.
|
||||
int pageCount = treeCapacity;
|
||||
if (i == curNodes.count()) {
|
||||
pageCount = ((pages.count() - 1) % treeCapacity) + 1;
|
||||
pageCount = ((totalPageCount - 1) % treeCapacity) + 1;
|
||||
}
|
||||
newNode->insertInt("Count", pageCount);
|
||||
newNode->insertObject("Kids", std::move(kids));
|
||||
nextRoundNodes.push(newNode.release()); // Transfer reference.
|
||||
nextRoundNodes.emplace_back(std::move(newNode));
|
||||
}
|
||||
SkDEBUGCODE( for (const auto& n : curNodes) { SkASSERT(!n); } );
|
||||
|
||||
curNodes = nextRoundNodes;
|
||||
nextRoundNodes.rewind();
|
||||
curNodes.swap(&nextRoundNodes);
|
||||
nextRoundNodes.reset();
|
||||
treeCapacity *= kNodeSize;
|
||||
} while (curNodes.count() > 1);
|
||||
|
||||
pageTree->push(curNodes[0]); // Transfer reference.
|
||||
return sk_ref_sp(curNodes[0]);
|
||||
return std::move(curNodes[0]);
|
||||
}
|
||||
|
||||
#if 0
|
||||
@ -269,16 +221,16 @@ void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) {
|
||||
SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height,
|
||||
const SkRect& trimBox) {
|
||||
SkASSERT(!fCanvas.get()); // endPage() was called before this.
|
||||
if (fPageDevices.empty()) {
|
||||
if (fPages.empty()) {
|
||||
// if this is the first page if the document.
|
||||
fObjectSerializer.serializeHeader(this->getStream(), fMetadata);
|
||||
fDests = sk_make_sp<SkPDFDict>();
|
||||
}
|
||||
SkISize pageSize = SkISize::Make(
|
||||
SkScalarRoundToInt(width), SkScalarRoundToInt(height));
|
||||
sk_sp<SkPDFDevice> device(
|
||||
fPageDevice.reset(
|
||||
SkPDFDevice::Create(pageSize, fRasterDpi, this));
|
||||
fCanvas = sk_make_sp<SkPDFCanvas>(device);
|
||||
fPageDevices.push_back(std::move(device));
|
||||
fCanvas = sk_make_sp<SkPDFCanvas>(fPageDevice);
|
||||
fCanvas->clipRect(trimBox);
|
||||
fCanvas->translate(trimBox.x(), trimBox.y());
|
||||
return fCanvas.get();
|
||||
@ -288,11 +240,28 @@ void SkPDFDocument::onEndPage() {
|
||||
SkASSERT(fCanvas.get());
|
||||
fCanvas->flush();
|
||||
fCanvas.reset(nullptr);
|
||||
SkASSERT(fPageDevice);
|
||||
fGlyphUsage.merge(fPageDevice->getFontGlyphUsage());
|
||||
auto page = sk_make_sp<SkPDFDict>("Page");
|
||||
page->insertObject("Resources", fPageDevice->makeResourceDict());
|
||||
page->insertObject("MediaBox", fPageDevice->copyMediaBox());
|
||||
auto annotations = sk_make_sp<SkPDFArray>();
|
||||
fPageDevice->appendAnnotations(annotations.get());
|
||||
if (annotations->size() > 0) {
|
||||
page->insertObject("Annots", std::move(annotations));
|
||||
}
|
||||
auto contentData = fPageDevice->content();
|
||||
auto contentObject = sk_make_sp<SkPDFStream>(contentData.get());
|
||||
this->serialize(contentObject);
|
||||
page->insertObjRef("Contents", std::move(contentObject));
|
||||
fPageDevice->appendDestinations(fDests.get(), page.get());
|
||||
fPages.emplace_back(std::move(page));
|
||||
fPageDevice.reset(nullptr);
|
||||
}
|
||||
|
||||
void SkPDFDocument::onAbort() {
|
||||
fCanvas.reset(nullptr);
|
||||
fPageDevices.reset();
|
||||
fPages.reset();
|
||||
fCanon.reset();
|
||||
renew(&fObjectSerializer);
|
||||
}
|
||||
@ -308,25 +277,13 @@ void SkPDFDocument::setMetadata(const SkDocument::Attribute info[],
|
||||
|
||||
bool SkPDFDocument::onClose(SkWStream* stream) {
|
||||
SkASSERT(!fCanvas.get());
|
||||
if (fPageDevices.empty()) {
|
||||
fPageDevices.reset();
|
||||
if (fPages.empty()) {
|
||||
fPages.reset();
|
||||
fCanon.reset();
|
||||
renew(&fObjectSerializer);
|
||||
return false;
|
||||
}
|
||||
SkTDArray<SkPDFDict*> pages; // TODO: SkTArray<sk_sp<SkPDFDict>>
|
||||
auto dests = sk_make_sp<SkPDFDict>();
|
||||
|
||||
for (const sk_sp<const SkPDFDevice>& pageDevice : fPageDevices) {
|
||||
SkASSERT(pageDevice);
|
||||
SkASSERT(fPageDevices[0]->getCanon() == pageDevice->getCanon());
|
||||
sk_sp<SkPDFDict> page(create_pdf_page(pageDevice.get()));
|
||||
pageDevice->appendDestinations(dests.get(), page.get());
|
||||
pages.push(page.release());
|
||||
}
|
||||
|
||||
auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
|
||||
|
||||
sk_sp<SkPDFObject> id, xmp;
|
||||
#ifdef SK_PDF_GENERATE_PDFA
|
||||
SkPDFMetadata::UUID uuid = metadata.uuid();
|
||||
@ -352,24 +309,28 @@ bool SkPDFDocument::onClose(SkWStream* stream) {
|
||||
docCatalog->insertObject("OutputIntents", std::move(intentArray));
|
||||
#endif
|
||||
|
||||
SkTDArray<SkPDFDict*> pageTree;
|
||||
docCatalog->insertObjRef("Pages", generate_page_tree(pages, &pageTree));
|
||||
docCatalog->insertObjRef("Pages", generate_page_tree(&fPages));
|
||||
|
||||
if (dests->size() > 0) {
|
||||
docCatalog->insertObjRef("Dests", std::move(dests));
|
||||
if (fDests->size() > 0) {
|
||||
docCatalog->insertObjRef("Dests", std::move(fDests));
|
||||
}
|
||||
|
||||
// Build font subsetting info before calling addObjectRecursively().
|
||||
perform_font_subsetting(fPageDevices, &fObjectSerializer.fSubstituteMap);
|
||||
for (const auto& entry : fGlyphUsage) {
|
||||
sk_sp<SkPDFFont> subsetFont(
|
||||
entry.fFont->getFontSubset(entry.fGlyphSet));
|
||||
if (subsetFont) {
|
||||
fObjectSerializer.fSubstituteMap.setSubstitute(
|
||||
entry.fFont, subsetFont.get());
|
||||
}
|
||||
}
|
||||
|
||||
fObjectSerializer.addObjectRecursively(docCatalog);
|
||||
fObjectSerializer.serializeObjects(this->getStream());
|
||||
fObjectSerializer.serializeFooter(
|
||||
this->getStream(), docCatalog, std::move(id));
|
||||
pageTree.unrefAll(); // TODO(halcanary): make this unnecesary by
|
||||
// refactoring generate_page_tree().
|
||||
pages.unrefAll();
|
||||
fPageDevices.reset();
|
||||
|
||||
SkASSERT(fPages.count() == 0);
|
||||
fCanon.reset();
|
||||
renew(&fObjectSerializer);
|
||||
return true;
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include "SkDocument.h"
|
||||
#include "SkPDFMetadata.h"
|
||||
#include "SkPDFTypes.h"
|
||||
#include "SkPDFFont.h"
|
||||
|
||||
class SkPDFDevice;
|
||||
|
||||
@ -72,7 +72,10 @@ public:
|
||||
private:
|
||||
SkPDFObjectSerializer fObjectSerializer;
|
||||
SkPDFCanon fCanon;
|
||||
SkTArray<sk_sp<const SkPDFDevice>> fPageDevices;
|
||||
SkPDFGlyphSetMap fGlyphUsage;
|
||||
SkTArray<sk_sp<SkPDFDict>> fPages;
|
||||
sk_sp<SkPDFDict> fDests;
|
||||
sk_sp<SkPDFDevice> fPageDevice;
|
||||
sk_sp<SkCanvas> fCanvas;
|
||||
SkScalar fRasterDpi;
|
||||
SkPDFMetadata fMetadata;
|
||||
|
@ -664,22 +664,6 @@ SkPDFGlyphSetMap::FontGlyphSetPair::FontGlyphSetPair(SkPDFFont* font,
|
||||
fGlyphSet(glyphSet) {
|
||||
}
|
||||
|
||||
SkPDFGlyphSetMap::F2BIter::F2BIter(const SkPDFGlyphSetMap& map) {
|
||||
reset(map);
|
||||
}
|
||||
|
||||
const SkPDFGlyphSetMap::FontGlyphSetPair* SkPDFGlyphSetMap::F2BIter::next() const {
|
||||
if (fIndex >= fMap->count()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &((*fMap)[fIndex++]);
|
||||
}
|
||||
|
||||
void SkPDFGlyphSetMap::F2BIter::reset(const SkPDFGlyphSetMap& map) {
|
||||
fMap = &(map.fMap);
|
||||
fIndex = 0;
|
||||
}
|
||||
|
||||
SkPDFGlyphSetMap::SkPDFGlyphSetMap() {
|
||||
}
|
||||
|
||||
|
@ -46,16 +46,8 @@ public:
|
||||
SkPDFGlyphSetMap();
|
||||
~SkPDFGlyphSetMap();
|
||||
|
||||
class F2BIter {
|
||||
public:
|
||||
explicit F2BIter(const SkPDFGlyphSetMap& map);
|
||||
const FontGlyphSetPair* next() const;
|
||||
void reset(const SkPDFGlyphSetMap& map);
|
||||
|
||||
private:
|
||||
const SkTDArray<FontGlyphSetPair>* fMap;
|
||||
mutable int fIndex;
|
||||
};
|
||||
const FontGlyphSetPair* begin() const { return fMap.begin(); }
|
||||
const FontGlyphSetPair* end() const { return fMap.end(); }
|
||||
|
||||
void merge(const SkPDFGlyphSetMap& usage);
|
||||
void reset();
|
||||
|
Loading…
Reference in New Issue
Block a user