| 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;
|
| +}
|
|
|