aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/pdf/SkPDFFont.cpp
diff options
context:
space:
mode:
authorGravatar halcanary <halcanary@google.com>2016-08-08 09:09:59 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-08-08 09:09:59 -0700
commit8b1d32c8d017c84828ed65af38cd1f1e7075ce08 (patch)
tree782bf807efb6198f98931b119e6431fd82f24a9d /src/pdf/SkPDFFont.cpp
parent4e97607d9a1cef66fac16f347c5ca813ec4f9515 (diff)
SkPDF/SkAdvancedTypefaceMetrics: simplify ATM, PDF takes over
No public API changes. TBR=reed@google.com GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2222523003 Review-Url: https://codereview.chromium.org/2222523003
Diffstat (limited to 'src/pdf/SkPDFFont.cpp')
-rw-r--r--src/pdf/SkPDFFont.cpp268
1 files changed, 238 insertions, 30 deletions
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index db687fba5f..093ad4a90d 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -36,12 +36,230 @@
// non-symbolic, so always call it symbolic. (PDF 1.4 spec, section 5.7.1)
static const int kPdfSymbolic = 4;
+struct AdvanceMetric {
+ enum MetricType {
+ kDefault, // Default advance: fAdvance.count = 1
+ kRange, // Advances for a range: fAdvance.count = fEndID-fStartID
+ kRun // fStartID-fEndID have same advance: fAdvance.count = 1
+ };
+ MetricType fType;
+ uint16_t fStartId;
+ uint16_t fEndId;
+ SkTDArray<int16_t> fAdvance;
+ AdvanceMetric(uint16_t startId) : fStartId(startId) {}
+ AdvanceMetric(AdvanceMetric&&) = default;
+ AdvanceMetric& operator=(AdvanceMetric&& other) = default;
+ AdvanceMetric(const AdvanceMetric&) = delete;
+ AdvanceMetric& operator=(const AdvanceMetric&) = delete;
+};
+
namespace {
///////////////////////////////////////////////////////////////////////////////
// File-Local Functions
///////////////////////////////////////////////////////////////////////////////
+const int16_t kInvalidAdvance = SK_MinS16;
+const int16_t kDontCareAdvance = SK_MinS16 + 1;
+
+static void stripUninterestingTrailingAdvancesFromRange(
+ AdvanceMetric* range) {
+ SkASSERT(range);
+
+ int expectedAdvanceCount = range->fEndId - range->fStartId + 1;
+ if (range->fAdvance.count() < expectedAdvanceCount) {
+ return;
+ }
+
+ for (int i = expectedAdvanceCount - 1; i >= 0; --i) {
+ if (range->fAdvance[i] != kDontCareAdvance &&
+ range->fAdvance[i] != kInvalidAdvance &&
+ range->fAdvance[i] != 0) {
+ range->fEndId = range->fStartId + i;
+ break;
+ }
+ }
+}
+
+static void zeroWildcardsInRange(AdvanceMetric* range) {
+ SkASSERT(range);
+ if (range->fType != AdvanceMetric::kRange) {
+ return;
+ }
+ SkASSERT(range->fAdvance.count() == range->fEndId - range->fStartId + 1);
+
+ // Zero out wildcards.
+ for (int i = 0; i < range->fAdvance.count(); ++i) {
+ if (range->fAdvance[i] == kDontCareAdvance) {
+ range->fAdvance[i] = 0;
+ }
+ }
+}
+
+static void FinishRange(
+ AdvanceMetric* range,
+ int endId,
+ AdvanceMetric::MetricType type) {
+ range->fEndId = endId;
+ range->fType = type;
+ stripUninterestingTrailingAdvancesFromRange(range);
+ int newLength;
+ if (type == AdvanceMetric::kRange) {
+ newLength = range->fEndId - range->fStartId + 1;
+ } else {
+ if (range->fEndId == range->fStartId) {
+ range->fType = AdvanceMetric::kRange;
+ }
+ newLength = 1;
+ }
+ SkASSERT(range->fAdvance.count() >= newLength);
+ range->fAdvance.setCount(newLength);
+ zeroWildcardsInRange(range);
+}
+
+
+/** Retrieve advance data for glyphs. Used by the PDF backend.
+ @param num_glyphs Total number of glyphs in the given font.
+ @param glyphIDs For per-glyph info, specify subset of the
+ font by giving glyph ids. Each integer
+ represents a glyph id. Passing nullptr
+ means all glyphs in the font.
+ @param glyphIDsCount Number of elements in subsetGlyphIds.
+ Ignored if glyphIDs is nullptr.
+*/
+// TODO(halcanary): this function is complex enough to need its logic
+// tested with unit tests. On the other hand, I want to do another
+// round of re-factoring before figuring out how to mock this.
+// TODO(halcanary): this function should be combined with
+// composeAdvanceData() so that we don't need to produce a linked list
+// of intermediate values. Or we could make the intermediate value
+// something other than a linked list.
+static void get_glyph_widths(SkSinglyLinkedList<AdvanceMetric>* glyphWidths,
+ int num_glyphs,
+ const uint32_t* subsetGlyphIDs,
+ uint32_t subsetGlyphIDsLength,
+ SkGlyphCache* glyphCache) {
+ // Assuming that on average, the ASCII representation of an advance plus
+ // a space is 8 characters and the ASCII representation of a glyph id is 3
+ // characters, then the following cut offs for using different range types
+ // apply:
+ // The cost of stopping and starting the range is 7 characers
+ // a. Removing 4 0's or don't care's is a win
+ // The cost of stopping and starting the range plus a run is 22
+ // characters
+ // b. Removing 3 repeating advances is a win
+ // c. Removing 2 repeating advances and 3 don't cares is a win
+ // When not currently in a range the cost of a run over a range is 16
+ // characaters, so:
+ // d. Removing a leading 0/don't cares is a win because it is omitted
+ // e. Removing 2 repeating advances is a win
+
+ AdvanceMetric* prevRange = nullptr;
+ int16_t lastAdvance = kInvalidAdvance;
+ int repeatedAdvances = 0;
+ int wildCardsInRun = 0;
+ int trailingWildCards = 0;
+ uint32_t subsetIndex = 0;
+
+ // Limit the loop count to glyph id ranges provided.
+ int firstIndex = 0;
+ int lastIndex = num_glyphs;
+ if (subsetGlyphIDs) {
+ firstIndex = static_cast<int>(subsetGlyphIDs[0]);
+ lastIndex =
+ static_cast<int>(subsetGlyphIDs[subsetGlyphIDsLength - 1]) + 1;
+ }
+ AdvanceMetric curRange(firstIndex);
+
+ for (int gId = firstIndex; gId <= lastIndex; gId++) {
+ int16_t advance = kInvalidAdvance;
+ if (gId < lastIndex) {
+ // Get glyph id only when subset is nullptr, or the id is in subset.
+ SkASSERT(!subsetGlyphIDs || (subsetIndex < subsetGlyphIDsLength &&
+ static_cast<uint32_t>(gId) <= subsetGlyphIDs[subsetIndex]));
+ if (!subsetGlyphIDs ||
+ (subsetIndex < subsetGlyphIDsLength &&
+ static_cast<uint32_t>(gId) == subsetGlyphIDs[subsetIndex])) {
+ advance = (int16_t)glyphCache->getGlyphIDAdvance(gId).fAdvanceX;
+ ++subsetIndex;
+ } else {
+ advance = kDontCareAdvance;
+ }
+ }
+ if (advance == lastAdvance) {
+ repeatedAdvances++;
+ trailingWildCards = 0;
+ } else if (advance == kDontCareAdvance) {
+ wildCardsInRun++;
+ trailingWildCards++;
+ } else if (curRange.fAdvance.count() ==
+ repeatedAdvances + 1 + wildCardsInRun) { // All in run.
+ if (lastAdvance == 0) {
+ curRange.fStartId = gId; // reset
+ curRange.fAdvance.setCount(0);
+ trailingWildCards = 0;
+ } else if (repeatedAdvances + 1 >= 2 || trailingWildCards >= 4) {
+ FinishRange(&curRange, gId - 1, AdvanceMetric::kRun);
+ prevRange = glyphWidths->emplace_back(std::move(curRange));
+ curRange = AdvanceMetric(gId);
+ trailingWildCards = 0;
+ }
+ repeatedAdvances = 0;
+ wildCardsInRun = trailingWildCards;
+ trailingWildCards = 0;
+ } else {
+ if (lastAdvance == 0 &&
+ repeatedAdvances + 1 + wildCardsInRun >= 4) {
+ FinishRange(&curRange,
+ gId - repeatedAdvances - wildCardsInRun - 2,
+ AdvanceMetric::kRange);
+ prevRange = glyphWidths->emplace_back(std::move(curRange));
+ curRange = AdvanceMetric(gId);
+ trailingWildCards = 0;
+ } else if (trailingWildCards >= 4 && repeatedAdvances + 1 < 2) {
+ FinishRange(&curRange, gId - trailingWildCards - 1,
+ AdvanceMetric::kRange);
+ prevRange = glyphWidths->emplace_back(std::move(curRange));
+ curRange = AdvanceMetric(gId);
+ trailingWildCards = 0;
+ } else if (lastAdvance != 0 &&
+ (repeatedAdvances + 1 >= 3 ||
+ (repeatedAdvances + 1 >= 2 && wildCardsInRun >= 3))) {
+ FinishRange(&curRange,
+ gId - repeatedAdvances - wildCardsInRun - 2,
+ AdvanceMetric::kRange);
+ (void)glyphWidths->emplace_back(std::move(curRange));
+ curRange =
+ AdvanceMetric(gId - repeatedAdvances - wildCardsInRun - 1);
+ curRange.fAdvance.append(1, &lastAdvance);
+ FinishRange(&curRange, gId - 1, AdvanceMetric::kRun);
+ prevRange = glyphWidths->emplace_back(std::move(curRange));
+ curRange = AdvanceMetric(gId);
+ trailingWildCards = 0;
+ }
+ repeatedAdvances = 0;
+ wildCardsInRun = trailingWildCards;
+ trailingWildCards = 0;
+ }
+ curRange.fAdvance.append(1, &advance);
+ if (advance != kDontCareAdvance) {
+ lastAdvance = advance;
+ }
+ }
+ if (curRange.fStartId == lastIndex) {
+ if (!prevRange) {
+ glyphWidths->reset();
+ return; // https://crbug.com/567031
+ }
+ } else {
+ FinishRange(&curRange, lastIndex - 1, AdvanceMetric::kRange);
+ glyphWidths->emplace_back(std::move(curRange));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+
bool parsePFBSection(const uint8_t** src, size_t* len, int sectionType,
size_t* size) {
// PFB sections have a two or six bytes header. 0x80 and a one byte
@@ -302,18 +520,18 @@ static sk_sp<SkPDFArray> makeFontBBox(SkIRect glyphBBox, uint16_t emSize) {
}
sk_sp<SkPDFArray> composeAdvanceData(
- const SkSinglyLinkedList<SkAdvancedTypefaceMetrics::WidthRange>& advanceInfo,
+ const SkSinglyLinkedList<AdvanceMetric>& advanceInfo,
uint16_t emSize,
int16_t* defaultAdvance) {
auto result = sk_make_sp<SkPDFArray>();
- for (const SkAdvancedTypefaceMetrics::WidthRange& range : advanceInfo) {
+ for (const AdvanceMetric& range : advanceInfo) {
switch (range.fType) {
- case SkAdvancedTypefaceMetrics::WidthRange::kDefault: {
+ case AdvanceMetric::kDefault: {
SkASSERT(range.fAdvance.count() == 1);
*defaultAdvance = range.fAdvance[0];
break;
}
- case SkAdvancedTypefaceMetrics::WidthRange::kRange: {
+ case AdvanceMetric::kRange: {
auto advanceArray = sk_make_sp<SkPDFArray>();
for (int j = 0; j < range.fAdvance.count(); j++)
advanceArray->appendScalar(
@@ -322,7 +540,7 @@ sk_sp<SkPDFArray> composeAdvanceData(
result->appendObject(std::move(advanceArray));
break;
}
- case SkAdvancedTypefaceMetrics::WidthRange::kRun: {
+ case AdvanceMetric::kRun: {
SkASSERT(range.fAdvance.count() == 1);
result->appendInt(range.fStartId);
result->appendInt(range.fEndId);
@@ -1042,24 +1260,17 @@ bool SkPDFCIDFont::addFontDescriptor(int16_t defaultWidth,
void set_glyph_widths(SkTypeface* tf,
const SkTDArray<uint32_t>* glyphIDs,
- SkAdvancedTypefaceMetrics* dst) {
+ SkSinglyLinkedList<AdvanceMetric>* dst) {
SkPaint tmpPaint;
tmpPaint.setHinting(SkPaint::kNo_Hinting);
tmpPaint.setTypeface(sk_ref_sp(tf));
tmpPaint.setTextSize((SkScalar)tf->getUnitsPerEm());
SkAutoGlyphCache autoGlyphCache(tmpPaint, nullptr, nullptr);
- SkGlyphCache* glyphCache = autoGlyphCache.get();
- SkAdvancedTypefaceMetrics::GetAdvance advanceFn =
- [glyphCache](int gid, int16_t* advance) {
- *advance = (int16_t)glyphCache->getGlyphIDAdvance(gid).fAdvanceX;
- return true;
- };
if (!glyphIDs || glyphIDs->isEmpty()) {
- dst->setGlyphWidths(tf->countGlyphs(), nullptr, 0, advanceFn);
+ get_glyph_widths(dst, tf->countGlyphs(), nullptr, 0, autoGlyphCache.get());
} else {
- dst->setGlyphWidths(tf->countGlyphs(),
- glyphIDs->begin(),
- glyphIDs->count(), advanceFn);
+ get_glyph_widths(dst, tf->countGlyphs(), glyphIDs->begin(),
+ glyphIDs->count(), autoGlyphCache.get());
}
}
@@ -1103,12 +1314,11 @@ bool SkPDFCIDFont::populate(const SkPDFGlyphSet* subset) {
sysInfo->insertInt("Supplement", 0);
this->insertObject("CIDSystemInfo", std::move(sysInfo));
- SkAdvancedTypefaceMetrics tmpMetrics;
+ SkSinglyLinkedList<AdvanceMetric> tmpMetrics;
set_glyph_widths(this->typeface(), &glyphIDs, &tmpMetrics);
int16_t defaultWidth = 0;
uint16_t emSize = (uint16_t)this->fontInfo()->fEmSize;
- sk_sp<SkPDFArray> widths = composeAdvanceData(
- tmpMetrics.fGlyphWidths, emSize, &defaultWidth);
+ sk_sp<SkPDFArray> widths = composeAdvanceData(tmpMetrics, emSize, &defaultWidth);
if (widths->size()) {
this->insertObject("W", std::move(widths));
}
@@ -1169,25 +1379,22 @@ bool SkPDFType1Font::addFontDescriptor(int16_t defaultWidth) {
}
bool SkPDFType1Font::populate(int16_t glyphID) {
- SkASSERT(fontInfo()->fVerticalMetrics.empty());
- SkASSERT(fontInfo()->fGlyphWidths.empty());
-
adjustGlyphRangeForSingleByteEncoding(glyphID);
int16_t defaultWidth = 0;
- const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry = nullptr;
+ const AdvanceMetric* widthRangeEntry = nullptr;
{
- SkAdvancedTypefaceMetrics tmpMetrics;
+ SkSinglyLinkedList<AdvanceMetric> tmpMetrics;
set_glyph_widths(this->typeface(), nullptr, &tmpMetrics);
- for (const auto& widthEntry : tmpMetrics.fGlyphWidths) {
+ for (const auto& widthEntry : tmpMetrics) {
switch (widthEntry.fType) {
- case SkAdvancedTypefaceMetrics::WidthRange::kDefault:
+ case AdvanceMetric::kDefault:
defaultWidth = widthEntry.fAdvance[0];
break;
- case SkAdvancedTypefaceMetrics::WidthRange::kRun:
+ case AdvanceMetric::kRun:
SkASSERT(false);
break;
- case SkAdvancedTypefaceMetrics::WidthRange::kRange:
+ case AdvanceMetric::kRange:
SkASSERT(widthRangeEntry == nullptr);
widthRangeEntry = &widthEntry;
break;
@@ -1206,8 +1413,9 @@ bool SkPDFType1Font::populate(int16_t glyphID) {
auto encDiffs = sk_make_sp<SkPDFArray>();
encDiffs->reserve(lastGlyphID() - firstGlyphID() + 2);
encDiffs->appendInt(1);
+ SkASSERT(this->fontInfo()->fGlyphNames.count() >= this->lastGlyphID());
for (int gID = firstGlyphID(); gID <= lastGlyphID(); gID++) {
- encDiffs->appendName(fontInfo()->fGlyphNames->get()[gID].c_str());
+ encDiffs->appendName(fontInfo()->fGlyphNames[gID].c_str());
}
auto encoding = sk_make_sp<SkPDFDict>("Encoding");
@@ -1218,7 +1426,7 @@ bool SkPDFType1Font::populate(int16_t glyphID) {
void SkPDFType1Font::addWidthInfoFromRange(
int16_t defaultWidth,
- const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry) {
+ const AdvanceMetric* widthRangeEntry) {
auto widthArray = sk_make_sp<SkPDFArray>();
int firstChar = 0;
if (widthRangeEntry) {