// return root node. 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 // 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; // curNodes takes a reference to its items, which it passes to pageTree. int totalPageCount = pages->count(); SkTArray<sk_sp<SkPDFDict>> curNodes; curNodes.swap(pages); // nextRoundNodes passes its references to nodes on to curNodes. int treeCapacity = kNodeSize; do { SkTArray<sk_sp<SkPDFDict>> nextRoundNodes; for (int i = 0; i < curNodes.count(); ) { if (i > 0 && i + 1 == curNodes.count()) { SkASSERT(curNodes[i]); nextRoundNodes.emplace_back(std::move(curNodes[i])); break; } auto newNode = sk_make_sp<SkPDFDict>("Pages"); auto kids = sk_make_sp<SkPDFArray>(); kids->reserve(kNodeSize); int count = 0; for (; i < curNodes.count() && count < kNodeSize; i++, count++) { SkASSERT(curNodes[i]); curNodes[i]->insertObjRef("Parent", newNode); kids->appendObjRef(std::move(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 = ((totalPageCount - 1) % treeCapacity) + 1; } newNode->insertInt("Count", pageCount); newNode->insertObject("Kids", std::move(kids)); nextRoundNodes.emplace_back(std::move(newNode)); } SkDEBUGCODE( for (const auto& n : curNodes) { SkASSERT(!n); } ); curNodes.swap(&nextRoundNodes); nextRoundNodes.reset(); treeCapacity *= kNodeSize; } while (curNodes.count() > 1);
void SkPDFType0Font::getFontSubset(SkPDFCanon* canon) { const SkAdvancedTypefaceMetrics* metricsPtr = SkPDFFont::GetMetrics(this->typeface(), canon); SkASSERT(metricsPtr); if (!metricsPtr) { return; } const SkAdvancedTypefaceMetrics& metrics = *metricsPtr; SkASSERT(can_embed(metrics)); SkAdvancedTypefaceMetrics::FontType type = this->getType(); SkTypeface* face = this->typeface(); SkASSERT(face); auto descriptor = sk_make_sp<SkPDFDict>("FontDescriptor"); uint16_t emSize = SkToU16(this->typeface()->getUnitsPerEm()); add_common_font_descriptor_entries(descriptor.get(), metrics, emSize , 0); int ttcIndex; std::unique_ptr<SkStreamAsset> fontAsset(face->openStream(&ttcIndex)); size_t fontSize = fontAsset ? fontAsset->getLength() : 0; if (0 == fontSize) { SkDebugf("Error: (SkTypeface)(%p)::openStream() returned " "empty stream (%p) when identified as kType1CID_Font " "or kTrueType_Font.\n", face, fontAsset.get()); } else { switch (type) { case SkAdvancedTypefaceMetrics::kTrueType_Font: { #ifdef SK_PDF_USE_SFNTLY if (!SkToBool(metrics.fFlags & SkAdvancedTypefaceMetrics::kNotSubsettable_FontFlag)) { sk_sp<SkPDFStream> subsetStream = get_subset_font_stream( std::move(fontAsset), this->glyphUsage(), metrics.fFontName.c_str(), ttcIndex); if (subsetStream) { descriptor->insertObjRef("FontFile2", std::move(subsetStream)); break; } // If subsetting fails, fall back to original font data. fontAsset.reset(face->openStream(&ttcIndex)); SkASSERT(fontAsset); SkASSERT(fontAsset->getLength() == fontSize); if (!fontAsset || fontAsset->getLength() == 0) { break; } } #endif // SK_PDF_USE_SFNTLY auto fontStream = sk_make_sp<SkPDFSharedStream>(std::move(fontAsset)); fontStream->dict()->insertInt("Length1", fontSize); descriptor->insertObjRef("FontFile2", std::move(fontStream)); break; } case SkAdvancedTypefaceMetrics::kType1CID_Font: { auto fontStream = sk_make_sp<SkPDFSharedStream>(std::move(fontAsset)); fontStream->dict()->insertName("Subtype", "CIDFontType0C"); descriptor->insertObjRef("FontFile3", std::move(fontStream)); break; } default: SkASSERT(false); } } auto newCIDFont = sk_make_sp<SkPDFDict>("Font"); newCIDFont->insertObjRef("FontDescriptor", std::move(descriptor)); newCIDFont->insertName("BaseFont", metrics.fPostScriptName); switch (type) { case SkAdvancedTypefaceMetrics::kType1CID_Font: newCIDFont->insertName("Subtype", "CIDFontType0"); break; case SkAdvancedTypefaceMetrics::kTrueType_Font: newCIDFont->insertName("Subtype", "CIDFontType2"); newCIDFont->insertName("CIDToGIDMap", "Identity"); break; default: SkASSERT(false); } auto sysInfo = sk_make_sp<SkPDFDict>(); sysInfo->insertString("Registry", "Adobe"); sysInfo->insertString("Ordering", "Identity"); sysInfo->insertInt("Supplement", 0); newCIDFont->insertObject("CIDSystemInfo", std::move(sysInfo)); int16_t defaultWidth = 0; { int emSize; auto glyphCache = SkPDFFont::MakeVectorCache(face, &emSize); sk_sp<SkPDFArray> widths = SkPDFMakeCIDGlyphWidthsArray( glyphCache.get(), &this->glyphUsage(), SkToS16(emSize), &defaultWidth); if (widths && widths->size() > 0) { newCIDFont->insertObject("W", std::move(widths)); } newCIDFont->insertScalar( "DW", scaleFromFontUnits(defaultWidth, SkToS16(emSize))); } //////////////////////////////////////////////////////////////////////////// this->insertName("Subtype", "Type0"); this->insertName("BaseFont", metrics.fPostScriptName); this->insertName("Encoding", "Identity-H"); auto descendantFonts = sk_make_sp<SkPDFArray>(); descendantFonts->appendObjRef(std::move(newCIDFont)); this->insertObject("DescendantFonts", std::move(descendantFonts)); if (metrics.fGlyphToUnicode.count() > 0) { this->insertObjRef("ToUnicode", SkPDFMakeToUnicodeCmap(metrics.fGlyphToUnicode, &this->glyphUsage(), multiByteGlyphs(), firstGlyphID(), lastGlyphID())); } SkDEBUGCODE(fPopulated = true); return; }