SkPDF: Subset Type3 (fallback) font

Motivation: significant file-size reduction.

Also: SkPDFFont::subsetFont() returns a sk_sp<SkPDFObject>
rather than a SkPDFFont*.

SkPDFType3Font constructor no longer populates font info;
relies on subsetting.

SkPDFFont::Create is easier to read

Also: SkPDFType3Font are scaled by emSize rather than 1000.

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2231483002

Review-Url: https://codereview.chromium.org/2231483002
This commit is contained in:
halcanary 2016-08-11 12:25:09 -07:00 committed by Commit bot
parent 86e7626f08
commit 88b138da99
4 changed files with 197 additions and 92 deletions

View File

@ -483,8 +483,8 @@ bool SkPDFDocument::onClose(SkWStream* stream) {
// Build font subsetting info before calling addObjectRecursively(). // Build font subsetting info before calling addObjectRecursively().
for (const auto& entry : fGlyphUsage) { for (const auto& entry : fGlyphUsage) {
sk_sp<SkPDFFont> subsetFont( sk_sp<SkPDFObject> subsetFont =
entry.fFont->getFontSubset(&entry.fGlyphSet)); entry.fFont->getFontSubset(&entry.fGlyphSet);
if (subsetFont) { if (subsetFont) {
fObjectSerializer.fSubstituteMap.setSubstitute( fObjectSerializer.fSubstituteMap.setSubstitute(
entry.fFont, subsetFont.get()); entry.fFont, subsetFont.get());

View File

@ -60,7 +60,7 @@ public:
SkTypeface* typeface); SkTypeface* typeface);
virtual ~SkPDFType0Font(); virtual ~SkPDFType0Font();
bool multiByteGlyphs() const override { return true; } bool multiByteGlyphs() const override { return true; }
SkPDFFont* getFontSubset(const SkPDFGlyphSet* usage) override; sk_sp<SkPDFObject> getFontSubset(const SkPDFGlyphSet* usage) override;
#ifdef SK_DEBUG #ifdef SK_DEBUG
void emitObject(SkWStream*, void emitObject(SkWStream*,
const SkPDFObjNumMap&, const SkPDFObjNumMap&,
@ -108,11 +108,14 @@ public:
SkPDFType3Font(const SkAdvancedTypefaceMetrics* info, SkPDFType3Font(const SkAdvancedTypefaceMetrics* info,
SkTypeface* typeface, SkTypeface* typeface,
uint16_t glyphID); uint16_t glyphID);
virtual ~SkPDFType3Font(); virtual ~SkPDFType3Font() {}
void emitObject(SkWStream*,
const SkPDFObjNumMap&,
const SkPDFSubstituteMap&) const override {
SkDEBUGFAIL("should call getFontSubset!");
}
sk_sp<SkPDFObject> getFontSubset(const SkPDFGlyphSet* usage) override;
bool multiByteGlyphs() const override { return false; } bool multiByteGlyphs() const override { return false; }
private:
bool populate(uint16_t glyphID);
}; };
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -335,7 +338,7 @@ SkScalar scaleFromFontUnits(int16_t val, uint16_t emSize) {
void setGlyphWidthAndBoundingBox(SkScalar width, SkIRect box, void setGlyphWidthAndBoundingBox(SkScalar width, SkIRect box,
SkWStream* content) { SkDynamicMemoryWStream* content) {
// Specify width and bounding box for the glyph. // Specify width and bounding box for the glyph.
SkPDFUtils::AppendScalar(width, content); SkPDFUtils::AppendScalar(width, content);
content->writeText(" 0 "); content->writeText(" 0 ");
@ -559,7 +562,7 @@ SkPDFFont* SkPDFFont::GetFontResource(SkPDFCanon* canon,
return font; return font;
} }
SkPDFFont* SkPDFFont::getFontSubset(const SkPDFGlyphSet*) { sk_sp<SkPDFObject> SkPDFFont::getFontSubset(const SkPDFGlyphSet*) {
return nullptr; // Default: no support. return nullptr; // Default: no support.
} }
@ -571,12 +574,13 @@ SkPDFFont::SkPDFFont(const SkAdvancedTypefaceMetrics* info,
, fFirstGlyphID(1) , fFirstGlyphID(1)
, fLastGlyphID(info ? info->fLastGlyphID : 0) , fLastGlyphID(info ? info->fLastGlyphID : 0)
, fFontInfo(SkSafeRef(info)) , fFontInfo(SkSafeRef(info))
, fDescriptor(SkSafeRef(relatedFontDescriptor)) { , fDescriptor(SkSafeRef(relatedFontDescriptor))
if (info == nullptr || , fFontType((!info || info->fFlags & SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag)
info->fFlags & SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag) { ? SkAdvancedTypefaceMetrics::kOther_Font
fFontType = SkAdvancedTypefaceMetrics::kOther_Font; : info->fType) {
} else { if (0 == fLastGlyphID) {
fFontType = info->fType; SkAutoResolveDefaultTypeface face(typeface);
fLastGlyphID = SkToU16(face->countGlyphs() - 1);
} }
} }
@ -588,23 +592,28 @@ SkPDFFont* SkPDFFont::Create(SkPDFCanon* canon,
SkPDFDict* relatedFontDescriptor) { SkPDFDict* relatedFontDescriptor) {
SkAdvancedTypefaceMetrics::FontType type = SkAdvancedTypefaceMetrics::FontType type =
info ? info->fType : SkAdvancedTypefaceMetrics::kOther_Font; info ? info->fType : SkAdvancedTypefaceMetrics::kOther_Font;
SkAdvancedTypefaceMetrics::FontFlags flags =
if (info && (info->fFlags & SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag)) { info ? info->fFlags : SkAdvancedTypefaceMetrics::kEmpty_FontFlag;
if (SkToBool(flags & SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag)) {
return new SkPDFType3Font(info, typeface, glyphID); return new SkPDFType3Font(info, typeface, glyphID);
} }
if (type == SkAdvancedTypefaceMetrics::kType1CID_Font || switch (type) {
type == SkAdvancedTypefaceMetrics::kTrueType_Font) { case SkAdvancedTypefaceMetrics::kType1CID_Font:
SkASSERT(relatedFontDescriptor == nullptr); case SkAdvancedTypefaceMetrics::kTrueType_Font:
return new SkPDFType0Font(info, typeface); SkASSERT(relatedFontDescriptor == nullptr);
SkASSERT(info != nullptr);
return new SkPDFType0Font(info, typeface);
case SkAdvancedTypefaceMetrics::kType1_Font:
SkASSERT(info != nullptr);
return new SkPDFType1Font(info, typeface, glyphID, relatedFontDescriptor);
case SkAdvancedTypefaceMetrics::kCFF_Font:
SkASSERT(info != nullptr);
// fallthrough
case SkAdvancedTypefaceMetrics::kOther_Font:
return new SkPDFType3Font(info, typeface, glyphID);
} }
if (type == SkAdvancedTypefaceMetrics::kType1_Font) { SkDEBUGFAIL("invalid SkAdvancedTypefaceMetrics::FontType");
return new SkPDFType1Font(info, typeface, glyphID, relatedFontDescriptor); return nullptr;
}
SkASSERT(type == SkAdvancedTypefaceMetrics::kCFF_Font ||
type == SkAdvancedTypefaceMetrics::kOther_Font);
return new SkPDFType3Font(info, typeface, glyphID);
} }
const SkAdvancedTypefaceMetrics* SkPDFFont::fontInfo() { const SkAdvancedTypefaceMetrics* SkPDFFont::fontInfo() {
@ -703,11 +712,11 @@ SkPDFType0Font::SkPDFType0Font(const SkAdvancedTypefaceMetrics* info, SkTypeface
SkPDFType0Font::~SkPDFType0Font() {} SkPDFType0Font::~SkPDFType0Font() {}
SkPDFFont* SkPDFType0Font::getFontSubset(const SkPDFGlyphSet* subset) { sk_sp<SkPDFObject> SkPDFType0Font::getFontSubset(const SkPDFGlyphSet* subset) {
if (!canSubset()) { if (!canSubset()) {
return nullptr; return nullptr;
} }
SkPDFType0Font* newSubset = new SkPDFType0Font(fontInfo(), typeface()); auto newSubset = sk_make_sp<SkPDFType0Font>(fontInfo(), typeface());
newSubset->populate(subset); newSubset->populate(subset);
return newSubset; return newSubset;
} }
@ -878,7 +887,8 @@ void set_glyph_widths(SkTypeface* tf,
tmpPaint.setHinting(SkPaint::kNo_Hinting); tmpPaint.setHinting(SkPaint::kNo_Hinting);
tmpPaint.setTypeface(sk_ref_sp(tf)); tmpPaint.setTypeface(sk_ref_sp(tf));
tmpPaint.setTextSize((SkScalar)tf->getUnitsPerEm()); tmpPaint.setTextSize((SkScalar)tf->getUnitsPerEm());
SkAutoGlyphCache autoGlyphCache(tmpPaint, nullptr, nullptr); const SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
SkAutoGlyphCache autoGlyphCache(tmpPaint, &props, nullptr);
if (!glyphIDs || glyphIDs->isEmpty()) { if (!glyphIDs || glyphIDs->isEmpty()) {
get_glyph_widths(dst, tf->countGlyphs(), nullptr, 0, autoGlyphCache.get()); get_glyph_widths(dst, tf->countGlyphs(), nullptr, 0, autoGlyphCache.get());
} else { } else {
@ -1017,7 +1027,8 @@ bool SkPDFType1Font::populate(int16_t glyphID) {
tmpPaint.setHinting(SkPaint::kNo_Hinting); tmpPaint.setHinting(SkPaint::kNo_Hinting);
tmpPaint.setTypeface(sk_ref_sp(this->typeface())); tmpPaint.setTypeface(sk_ref_sp(this->typeface()));
tmpPaint.setTextSize((SkScalar)this->typeface()->getUnitsPerEm()); tmpPaint.setTextSize((SkScalar)this->typeface()->getUnitsPerEm());
SkAutoGlyphCache glyphCache(tmpPaint, nullptr, nullptr); const SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
SkAutoGlyphCache glyphCache(tmpPaint, &props, nullptr);
auto widths = sk_make_sp<SkPDFArray>(); auto widths = sk_make_sp<SkPDFArray>();
SkScalar advance = glyphCache->getGlyphIDAdvance(0).fAdvanceX; SkScalar advance = glyphCache->getGlyphIDAdvance(0).fAdvanceX;
const uint16_t emSize = this->fontInfo()->fEmSize; const uint16_t emSize = this->fontInfo()->fEmSize;
@ -1054,85 +1065,177 @@ bool SkPDFType1Font::populate(int16_t glyphID) {
// class SkPDFType3Font // class SkPDFType3Font
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
SkPDFType3Font::SkPDFType3Font(const SkAdvancedTypefaceMetrics* info, namespace {
SkTypeface* typeface, // returns [0, first, first+1, ... last-1, last]
uint16_t glyphID) struct SingleByteGlyphIdIterator {
: SkPDFFont(info, typeface, nullptr) { SingleByteGlyphIdIterator(SkGlyphID first, SkGlyphID last)
this->populate(glyphID); : fFirst(first), fLast(last) {
SkASSERT(fFirst > 0);
SkASSERT(fLast >= first);
}
struct Iter {
void operator++() {
fCurrent = (0 == fCurrent) ? fFirst : fCurrent + 1;
}
// This is an input_iterator
SkGlyphID operator*() const { return (SkGlyphID)fCurrent; }
bool operator!=(const Iter& rhs) const {
return fCurrent != rhs.fCurrent;
}
Iter(SkGlyphID f, int c) : fFirst(f), fCurrent(c) {}
private:
const SkGlyphID fFirst;
int fCurrent; // must be int to make fLast+1 to fit
};
Iter begin() const { return Iter(fFirst, 0); }
Iter end() const { return Iter(fFirst, (int)fLast + 1); }
private:
const SkGlyphID fFirst;
const SkGlyphID fLast;
};
} }
SkPDFType3Font::~SkPDFType3Font() {} static void add_type3_font_info(SkPDFDict* font,
SkTypeface* typeface,
bool SkPDFType3Font::populate(uint16_t glyphID) { SkScalar emSize,
const SkPDFGlyphSet* subset,
SkGlyphID firstGlyphID,
SkGlyphID lastGlyphID) {
SkASSERT(lastGlyphID >= firstGlyphID);
SkPaint paint; SkPaint paint;
paint.setTypeface(sk_ref_sp(this->typeface())); paint.setHinting(SkPaint::kNo_Hinting);
paint.setTextSize(1000); paint.setTypeface(sk_ref_sp(typeface));
paint.setTextSize(emSize);
const SkSurfaceProps props(0, kUnknown_SkPixelGeometry); const SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
SkAutoGlyphCache autoCache(paint, &props, nullptr); SkAutoGlyphCache cache(paint, &props, nullptr);
SkGlyphCache* cache = autoCache.getCache();
// If fLastGlyphID isn't set (because there is not fFontInfo), look it up.
if (lastGlyphID() == 0) {
setLastGlyphID(cache->getGlyphCount() - 1);
}
adjustGlyphRangeForSingleByteEncoding(glyphID); font->insertName("Subtype", "Type3");
// Flip about the x-axis and scale by 1/emSize.
insertName("Subtype", "Type3");
// Flip about the x-axis and scale by 1/1000.
SkMatrix fontMatrix; SkMatrix fontMatrix;
fontMatrix.setScale(SkScalarInvert(1000), -SkScalarInvert(1000)); fontMatrix.setScale(SkScalarInvert(emSize), -SkScalarInvert(emSize));
this->insertObject("FontMatrix", SkPDFUtils::MatrixToArray(fontMatrix)); font->insertObject("FontMatrix", SkPDFUtils::MatrixToArray(fontMatrix));
auto charProcs = sk_make_sp<SkPDFDict>(); auto charProcs = sk_make_sp<SkPDFDict>();
auto encoding = sk_make_sp<SkPDFDict>("Encoding"); auto encoding = sk_make_sp<SkPDFDict>("Encoding");
auto encDiffs = sk_make_sp<SkPDFArray>(); auto encDiffs = sk_make_sp<SkPDFArray>();
encDiffs->reserve(lastGlyphID() - firstGlyphID() + 2); // length(firstGlyphID .. lastGlyphID) == lastGlyphID - firstGlyphID + 1
encDiffs->appendInt(1); // plus 1 for glyph 0;
SkASSERT(firstGlyphID > 0);
SkASSERT(lastGlyphID >= firstGlyphID);
int glyphCount = lastGlyphID - firstGlyphID + 2;
// one other entry for the index of first glyph.
encDiffs->reserve(glyphCount + 1);
encDiffs->appendInt(0); // index of first glyph
auto widthArray = sk_make_sp<SkPDFArray>(); auto widthArray = sk_make_sp<SkPDFArray>();
widthArray->reserve(glyphCount);
SkIRect bbox = SkIRect::MakeEmpty(); SkIRect bbox = SkIRect::MakeEmpty();
for (int gID = firstGlyphID(); gID <= lastGlyphID(); gID++) {
sk_sp<SkPDFStream> emptyStream;
for (SkGlyphID gID : SingleByteGlyphIdIterator(firstGlyphID, lastGlyphID)) {
bool skipGlyph = subset && gID != 0 && !subset->has(gID);
SkString characterName; SkString characterName;
characterName.printf("gid%d", gID); SkScalar advance = 0.0f;
encDiffs->appendName(characterName.c_str()); SkIRect glyphBBox;
if (skipGlyph) {
const SkGlyph& glyph = cache->getGlyphIDMetrics(gID); characterName.set("g0");
widthArray->appendScalar(SkFloatToScalar(glyph.fAdvanceX)); } else {
SkIRect glyphBBox = SkIRect::MakeXYWH(glyph.fLeft, glyph.fTop, characterName.printf("g%X", gID);
glyph.fWidth, glyph.fHeight); const SkGlyph& glyph = cache->getGlyphIDMetrics(gID);
bbox.join(glyphBBox); advance = SkFloatToScalar(glyph.fAdvanceX);
glyphBBox = SkIRect::MakeXYWH(glyph.fLeft, glyph.fTop,
SkDynamicMemoryWStream content; glyph.fWidth, glyph.fHeight);
setGlyphWidthAndBoundingBox(SkFloatToScalar(glyph.fAdvanceX), glyphBBox, bbox.join(glyphBBox);
&content); const SkPath* path = cache->findPath(glyph);
const SkPath* path = cache->findPath(glyph); if (path && !path->isEmpty()) {
if (path) { SkDynamicMemoryWStream content;
SkPDFUtils::EmitPath(*path, paint.getStyle(), &content); setGlyphWidthAndBoundingBox(SkFloatToScalar(glyph.fAdvanceX), glyphBBox,
SkPDFUtils::PaintPath(paint.getStyle(), path->getFillType(), &content);
&content); SkPDFUtils::EmitPath(*path, SkPaint::kFill_Style, &content);
SkPDFUtils::PaintPath(SkPaint::kFill_Style, path->getFillType(),
&content);
charProcs->insertObjRef(
characterName, sk_make_sp<SkPDFStream>(
std::unique_ptr<SkStreamAsset>(content.detachAsStream())));
} else {
if (!emptyStream) {
emptyStream = sk_make_sp<SkPDFStream>(
std::unique_ptr<SkStreamAsset>(
new SkMemoryStream((size_t)0)));
}
charProcs->insertObjRef(characterName, emptyStream);
}
} }
charProcs->insertObjRef( encDiffs->appendName(characterName.c_str());
characterName, sk_make_sp<SkPDFStream>( widthArray->appendScalar(advance);
std::unique_ptr<SkStreamAsset>(content.detachAsStream())));
} }
encoding->insertObject("Differences", std::move(encDiffs)); encoding->insertObject("Differences", std::move(encDiffs));
font->insertInt("FirstChar", 0);
this->insertObject("CharProcs", std::move(charProcs)); font->insertInt("LastChar", lastGlyphID - firstGlyphID + 1);
this->insertObject("Encoding", std::move(encoding)); /* FontBBox: "A rectangle expressed in the glyph coordinate
system, specifying the font bounding box. This is the smallest
this->insertObject("FontBBox", makeFontBBox(bbox, 1000)); rectangle enclosing the shape that would result if all of the
this->insertInt("FirstChar", 1); glyphs of the font were placed with their origins coincident and
this->insertInt("LastChar", lastGlyphID() - firstGlyphID() + 1); then filled." */
this->insertObject("Widths", std::move(widthArray)); auto fontBBox = sk_make_sp<SkPDFArray>();
this->insertName("CIDToGIDMap", "Identity"); fontBBox->reserve(4);
fontBBox->appendInt(bbox.left());
this->populateToUnicodeTable(nullptr); fontBBox->appendInt(bbox.bottom());
return true; fontBBox->appendInt(bbox.right());
fontBBox->appendInt(bbox.top());
font->insertObject("FontBBox", std::move(fontBBox));
font->insertName("CIDToGIDMap", "Identity");
sk_sp<const SkAdvancedTypefaceMetrics> metrics;
if (subset) {
SkTDArray<uint32_t> subsetList;
for (SkGlyphID gID : SingleByteGlyphIdIterator(firstGlyphID, lastGlyphID)) {
if (gID == 0 || subset->has(gID)) { // Always include glyph 0.
subsetList.push(0);
}
}
subset->exportTo(&subsetList);
metrics = SkPDFFont::GetFontMetricsWithToUnicode(typeface, subsetList.begin(),
subsetList.count());
} else {
metrics = SkPDFFont::GetFontMetricsWithToUnicode(typeface, nullptr, 0);
}
font->insertObjRef("ToUnicode",
SkPDFMakeToUnicodeCmap(metrics->fGlyphToUnicode,
subset,
false,
firstGlyphID,
lastGlyphID));
font->insertObject("Widths", std::move(widthArray));
font->insertObject("Encoding", std::move(encoding));
font->insertObject("CharProcs", std::move(charProcs));
} }
SkPDFType3Font::SkPDFType3Font(const SkAdvancedTypefaceMetrics* info,
SkTypeface* typeface,
uint16_t glyphID)
: SkPDFFont(info, typeface, nullptr) {
// If fLastGlyphID isn't set (because there is not fFontInfo), look it up.
this->setLastGlyphID(SkToU16(typeface->countGlyphs() - 1));
this->adjustGlyphRangeForSingleByteEncoding(glyphID);
}
sk_sp<SkPDFObject> SkPDFType3Font::getFontSubset(const SkPDFGlyphSet* usage) {
// All fonts are subset before serialization.
// TODO(halcanary): all fonts should follow this pattern.
auto font = sk_make_sp<SkPDFDict>("Font");
const SkAdvancedTypefaceMetrics* info = this->fontInfo();
uint16_t emSize = info && info->fEmSize > 0 ? info->fEmSize : 1000;
add_type3_font_info(font.get(), this->typeface(), (SkScalar)emSize, usage,
this->firstGlyphID(), this->lastGlyphID());
return font;
}
////////////////////////////////////////////////////////////////////////////////
SkPDFFont::Match SkPDFFont::IsMatch(SkPDFFont* existingFont, SkPDFFont::Match SkPDFFont::IsMatch(SkPDFFont* existingFont,
uint32_t existingFontID, uint32_t existingFontID,
uint16_t existingGlyphID, uint16_t existingGlyphID,

View File

@ -132,7 +132,7 @@ public:
* @return nullptr if font does not support subsetting, a new instance * @return nullptr if font does not support subsetting, a new instance
* of SkPDFFont otherwise. * of SkPDFFont otherwise.
*/ */
virtual SkPDFFont* getFontSubset(const SkPDFGlyphSet* usage); virtual sk_sp<SkPDFObject> getFontSubset(const SkPDFGlyphSet* usage);
enum Match { enum Match {
kExact_Match, kExact_Match,

View File

@ -573,7 +573,9 @@ void SkPDFStream::setData(std::unique_ptr<SkStreamAsset> stream) {
SkASSERT(stream->hasLength()); SkASSERT(stream->hasLength());
SkDynamicMemoryWStream compressedData; SkDynamicMemoryWStream compressedData;
SkDeflateWStream deflateWStream(&compressedData); SkDeflateWStream deflateWStream(&compressedData);
SkStreamCopy(&deflateWStream, stream.get()); if (stream->getLength() > 0) {
SkStreamCopy(&deflateWStream, stream.get());
}
deflateWStream.finalize(); deflateWStream.finalize();
size_t compressedLength = compressedData.bytesWritten(); size_t compressedLength = compressedData.bytesWritten();
size_t originalLength = stream->getLength(); size_t originalLength = stream->getLength();