summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/skia/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp')
-rw-r--r--gfx/skia/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp206
1 files changed, 206 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp b/gfx/skia/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp
new file mode 100644
index 0000000000..85a4688ce8
--- /dev/null
+++ b/gfx/skia/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp
@@ -0,0 +1,206 @@
+/*
+ * 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 "src/pdf/SkPDFMakeCIDGlyphWidthsArray.h"
+
+#include "include/core/SkPaint.h"
+#include "include/private/base/SkTo.h"
+#include "src/core/SkStrike.h"
+#include "src/core/SkStrikeSpec.h"
+#include "src/pdf/SkPDFGlyphUse.h"
+
+#include <algorithm>
+#include <vector>
+
+// 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 {
+
+// scale from em-units to base-1000, returning as a SkScalar
+SkScalar from_font_units(SkScalar scaled, uint16_t emSize) {
+ if (emSize == 1000) {
+ return scaled;
+ } else {
+ return scaled * 1000 / emSize;
+ }
+}
+
+SkScalar scale_from_font_units(int16_t val, uint16_t emSize) {
+ return from_font_units(SkIntToScalar(val), emSize);
+}
+
+// Unfortunately poppler does not appear to respect the default width setting.
+#if defined(SK_PDF_CAN_USE_DW)
+int16_t findMode(SkSpan<const int16_t> advances) {
+ if (advances.empty()) {
+ return 0;
+ }
+
+ int16_t previousAdvance = advances[0];
+ int16_t currentModeAdvance = advances[0];
+ size_t currentCount = 1;
+ size_t currentModeCount = 1;
+
+ for (size_t i = 1; i < advances.size(); ++i) {
+ if (advances[i] == previousAdvance) {
+ ++currentCount;
+ } else {
+ if (currentCount > currentModeCount) {
+ currentModeAdvance = previousAdvance;
+ currentModeCount = currentCount;
+ }
+ previousAdvance = advances[i];
+ currentCount = 1;
+ }
+ }
+
+ return currentCount > currentModeCount ? previousAdvance : currentModeAdvance;
+}
+#endif
+} // namespace
+
+/** 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.
+std::unique_ptr<SkPDFArray> SkPDFMakeCIDGlyphWidthsArray(const SkTypeface& typeface,
+ const SkPDFGlyphUse& subset,
+ SkScalar* defaultAdvance) {
+ // There are two ways of expressing advances
+ //
+ // range: " gfid [adv.ances adv.ances ... adv.ances]"
+ // run: " gfid gfid adv.ances"
+ //
+ // Assuming that on average
+ // the ASCII representation of an advance plus a space is 10 characters
+ // the ASCII representation of a glyph id plus a space is 4 characters
+ // the ASCII representation of unused gid plus a space in a range is 2 characters
+ //
+ // When not in a range or run
+ // a. Skipping don't cares or defaults is a win (trivial)
+ // b. Run wins for 2+ repeats " gid gid adv.ances"
+ // " gid [adv.ances adv.ances]"
+ // rule: 2+ repeats create run as long as possible, else start range
+ //
+ // When in a range
+ // Cost of stopping and starting a range is 8 characters "] gid ["
+ // c. Skipping defaults is always a win " adv.ances"
+ // rule: end range if default seen
+ // d. Skipping 4+ don't cares is a win " 0 0 0 0"
+ // rule: end range if 4+ don't cares
+ // Cost of stop and start range plus run is 28 characters "] gid gid adv.ances gid ["
+ // e. Switching for 2+ repeats and 4+ don't cares wins " 0 0 adv.ances 0 0 adv.ances"
+ // rule: end range for 2+ repeats with 4+ don't cares
+ // f. Switching for 3+ repeats wins " adv.ances adv.ances adv.ances"
+ // rule: end range for 3+ repeats
+
+ int emSize;
+ SkStrikeSpec strikeSpec = SkStrikeSpec::MakePDFVector(typeface, &emSize);
+ SkBulkGlyphMetricsAndPaths paths{strikeSpec};
+
+ auto result = SkPDFMakeArray();
+
+ std::vector<SkGlyphID> glyphIDs;
+ subset.getSetValues([&](unsigned index) {
+ glyphIDs.push_back(SkToU16(index));
+ });
+ auto glyphs = paths.glyphs(SkSpan(glyphIDs));
+
+#if defined(SK_PDF_CAN_USE_DW)
+ std::vector<int16_t> advances;
+ advances.reserve_back(glyphs.size());
+ for (const SkGlyph* glyph : glyphs) {
+ advances.push_back((int16_t)glyph->advanceX());
+ }
+ std::sort(advances.begin(), advances.end());
+ int16_t modeAdvance = findMode(SkSpan(advances));
+ *defaultAdvance = scale_from_font_units(modeAdvance, emSize);
+#else
+ *defaultAdvance = 0;
+#endif
+
+ for (size_t i = 0; i < glyphs.size(); ++i) {
+ int16_t advance = (int16_t)glyphs[i]->advanceX();
+
+#if defined(SK_PDF_CAN_USE_DW)
+ // a. Skipping don't cares or defaults is a win (trivial)
+ if (advance == modeAdvance) {
+ continue;
+ }
+#endif
+
+ // b. 2+ repeats create run as long as possible, else start range
+ {
+ size_t j = i + 1; // j is always one past the last known repeat
+ for (; j < glyphs.size(); ++j) {
+ int16_t next_advance = (int16_t)glyphs[j]->advanceX();
+ if (advance != next_advance) {
+ break;
+ }
+ }
+ if (j - i >= 2) {
+ result->appendInt(glyphs[i]->getGlyphID());
+ result->appendInt(glyphs[j - 1]->getGlyphID());
+ result->appendScalar(scale_from_font_units(advance, emSize));
+ i = j - 1;
+ continue;
+ }
+ }
+
+ {
+ result->appendInt(glyphs[i]->getGlyphID());
+ auto advanceArray = SkPDFMakeArray();
+ advanceArray->appendScalar(scale_from_font_units(advance, emSize));
+ size_t j = i + 1; // j is always one past the last output
+ for (; j < glyphs.size(); ++j) {
+ advance = (int16_t)glyphs[j]->advanceX();
+#if defined(SK_PDF_CAN_USE_DW)
+ // c. end range if default seen
+ if (advance == modeAdvance) {
+ break;
+ }
+#endif
+
+ int dontCares = glyphs[j]->getGlyphID() - glyphs[j - 1]->getGlyphID() - 1;
+ // d. end range if 4+ don't cares
+ if (dontCares >= 4) {
+ break;
+ }
+
+ int16_t next_advance = 0;
+ // e. end range for 2+ repeats with 4+ don't cares
+ if (j + 1 < glyphs.size()) {
+ next_advance = (int16_t)glyphs[j+1]->advanceX();
+ int next_dontCares = glyphs[j+1]->getGlyphID() - glyphs[j]->getGlyphID() - 1;
+ if (advance == next_advance && dontCares + next_dontCares >= 4) {
+ break;
+ }
+ }
+
+ // f. end range for 3+ repeats
+ if (j + 2 < glyphs.size() && advance == next_advance) {
+ next_advance = (int16_t)glyphs[j+2]->advanceX();
+ if (advance == next_advance) {
+ break;
+ }
+ }
+
+ while (dontCares --> 0) {
+ advanceArray->appendScalar(0);
+ }
+ advanceArray->appendScalar(scale_from_font_units(advance, emSize));
+ }
+ result->appendObject(std::move(advanceArray));
+ i = j - 1;
+ }
+ }
+
+ return result;
+}