Index: src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp |
diff --git a/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp b/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e1bb0d27859409fcd10c54f38f949f668989c425 |
--- /dev/null |
+++ b/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp |
@@ -0,0 +1,262 @@ |
+/* |
+ * Copyright 2016 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include "SkBitSet.h" |
+#include "SkPDFMakeCIDGlyphWidthsArray.h" |
+#include "SkPaint.h" |
+#include "SkGlyphCache.h" |
+ |
+// TODO(halcanary): Write unit tests for SkPDFMakeCIDGlyphWidthsArray(). |
+ |
+// TODO(halcanary): The logic in this file originated in several |
+// disparate places. I feel sure that someone could simplify this |
+// down to a single easy-to-read function. |
+ |
+namespace { |
+ |
+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; |
+}; |
+const int16_t kInvalidAdvance = SK_MinS16; |
+const int16_t kDontCareAdvance = SK_MinS16 + 1; |
+} // namespace |
+ |
+// scale from em-units to base-1000, returning as a SkScalar |
+static SkScalar from_font_units(SkScalar scaled, uint16_t emSize) { |
+ if (emSize == 1000) { |
+ return scaled; |
+ } else { |
+ return SkScalarMulDiv(scaled, 1000, emSize); |
+ } |
+} |
+ |
+static SkScalar scale_from_font_units(int16_t val, uint16_t emSize) { |
+ return from_font_units(SkIntToScalar(val), emSize); |
+} |
+ |
+static void strip_uninteresting_trailing_advances_from_range( |
+ 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 zero_wildcards_in_range(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 finish_range( |
+ AdvanceMetric* range, |
+ int endId, |
+ AdvanceMetric::MetricType type) { |
+ range->fEndId = endId; |
+ range->fType = type; |
+ strip_uninteresting_trailing_advances_from_range(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); |
+ zero_wildcards_in_range(range); |
+} |
+ |
+static void compose_advance_data(const AdvanceMetric& range, |
+ uint16_t emSize, |
+ int16_t* defaultAdvance, |
+ SkPDFArray* result) { |
+ switch (range.fType) { |
+ case AdvanceMetric::kDefault: { |
+ SkASSERT(range.fAdvance.count() == 1); |
+ *defaultAdvance = range.fAdvance[0]; |
+ break; |
+ } |
+ case AdvanceMetric::kRange: { |
+ auto advanceArray = sk_make_sp<SkPDFArray>(); |
+ for (int j = 0; j < range.fAdvance.count(); j++) |
+ advanceArray->appendScalar( |
+ scale_from_font_units(range.fAdvance[j], emSize)); |
+ result->appendInt(range.fStartId); |
+ result->appendObject(std::move(advanceArray)); |
+ break; |
+ } |
+ case AdvanceMetric::kRun: { |
+ SkASSERT(range.fAdvance.count() == 1); |
+ result->appendInt(range.fStartId); |
+ result->appendInt(range.fEndId); |
+ result->appendScalar( |
+ scale_from_font_units(range.fAdvance[0], emSize)); |
+ break; |
+ } |
+ } |
+} |
+ |
+/** Retrieve advance data for glyphs. Used by the PDF backend. */ |
+// TODO(halcanary): this function is complex enough to need its logic |
+// tested with unit tests. |
+sk_sp<SkPDFArray> SkPDFMakeCIDGlyphWidthsArray(SkGlyphCache* cache, |
+ const SkBitSet* subset, |
+ uint16_t emSize, |
+ int16_t* defaultAdvance) { |
+ // 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 |
+ |
+ auto result = sk_make_sp<SkPDFArray>(); |
+ int num_glyphs = SkToInt(cache->getGlyphCount()); |
+ |
+ bool prevRange = false; |
+ |
+ int16_t lastAdvance = kInvalidAdvance; |
+ int repeatedAdvances = 0; |
+ int wildCardsInRun = 0; |
+ int trailingWildCards = 0; |
+ |
+ // Limit the loop count to glyph id ranges provided. |
+ int lastIndex = num_glyphs; |
+ if (subset) { |
+ while (!subset->isBitSet(lastIndex - 1) && lastIndex > 0) { |
+ --lastIndex; |
+ } |
+ } |
+ AdvanceMetric curRange(0); |
+ |
+ for (int gId = 0; gId <= lastIndex; gId++) { |
+ int16_t advance = kInvalidAdvance; |
+ if (gId < lastIndex) { |
+ if (!subset || 0 == gId || subset->isBitSet(gId)) { |
+ advance = (int16_t)cache->getGlyphIDAdvance(gId).fAdvanceX; |
+ } 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) { |
+ finish_range(&curRange, gId - 1, AdvanceMetric::kRun); |
+ compose_advance_data(curRange, emSize, defaultAdvance, result.get()); |
+ prevRange = true; |
+ curRange = AdvanceMetric(gId); |
+ trailingWildCards = 0; |
+ } |
+ repeatedAdvances = 0; |
+ wildCardsInRun = trailingWildCards; |
+ trailingWildCards = 0; |
+ } else { |
+ if (lastAdvance == 0 && |
+ repeatedAdvances + 1 + wildCardsInRun >= 4) { |
+ finish_range(&curRange, |
+ gId - repeatedAdvances - wildCardsInRun - 2, |
+ AdvanceMetric::kRange); |
+ compose_advance_data(curRange, emSize, defaultAdvance, result.get()); |
+ prevRange = true; |
+ curRange = AdvanceMetric(gId); |
+ trailingWildCards = 0; |
+ } else if (trailingWildCards >= 4 && repeatedAdvances + 1 < 2) { |
+ finish_range(&curRange, gId - trailingWildCards - 1, |
+ AdvanceMetric::kRange); |
+ compose_advance_data(curRange, emSize, defaultAdvance, result.get()); |
+ prevRange = true; |
+ curRange = AdvanceMetric(gId); |
+ trailingWildCards = 0; |
+ } else if (lastAdvance != 0 && |
+ (repeatedAdvances + 1 >= 3 || |
+ (repeatedAdvances + 1 >= 2 && wildCardsInRun >= 3))) { |
+ finish_range(&curRange, |
+ gId - repeatedAdvances - wildCardsInRun - 2, |
+ AdvanceMetric::kRange); |
+ compose_advance_data(curRange, emSize, defaultAdvance, result.get()); |
+ curRange = |
+ AdvanceMetric(gId - repeatedAdvances - wildCardsInRun - 1); |
+ curRange.fAdvance.append(1, &lastAdvance); |
+ finish_range(&curRange, gId - 1, AdvanceMetric::kRun); |
+ compose_advance_data(curRange, emSize, defaultAdvance, result.get()); |
+ prevRange = true; |
+ 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) { |
+ return nullptr; // https://crbug.com/567031 |
+ } |
+ } else { |
+ finish_range(&curRange, lastIndex - 1, AdvanceMetric::kRange); |
+ compose_advance_data(curRange, emSize, defaultAdvance, result.get()); |
+ } |
+ return result; |
+} |