| /* | |
| * 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/SkScalar.h" | |
| #include "include/core/SkSpan.h" | |
| #include "include/core/SkTypes.h" | |
| #include "include/private/SkTo.h" | |
| #include "src/core/SkGlyph.h" | |
| #include "src/core/SkStrikeSpec.h" | |
| #include "src/pdf/SkPDFFont.h" | |
| #include "src/pdf/SkPDFGlyphUse.h" | |
| #include "src/pdf/SkPDFTypes.h" | |
| #include <algorithm> | |
| #include <cstddef> | |
| #include <utility> | |
| #include <vector> | |
| namespace { | |
| // Scale from em-units to 1000-units. | |
| SkScalar from_font_units(SkScalar scaled, uint16_t emSize) { | |
| if (emSize == 1000) { | |
| return scaled; | |
| } else { | |
| return scaled * 1000 / emSize; | |
| } | |
| } | |
| SkScalar find_mode_or_0(SkSpan<const SkScalar> advances) { | |
| if (advances.empty()) { | |
| return 0; | |
| } | |
| SkScalar currentAdvance = advances[0]; | |
| SkScalar currentModeAdvance = advances[0]; | |
| size_t currentCount = 1; | |
| size_t currentModeCount = 1; | |
| for (size_t i = 1; i < advances.size(); ++i) { | |
| if (advances[i] == currentAdvance) { | |
| ++currentCount; | |
| } else { | |
| if (currentCount > currentModeCount) { | |
| currentModeAdvance = currentAdvance; | |
| currentModeCount = currentCount; | |
| } | |
| currentAdvance = advances[i]; | |
| currentCount = 1; | |
| } | |
| } | |
| return currentCount > currentModeCount ? currentAdvance : currentModeAdvance; | |
| } | |
| } // namespace | |
| std::unique_ptr<SkPDFArray> SkPDFMakeCIDGlyphWidthsArray(const SkPDFStrikeSpec& pdfStrikeSpec, | |
| const SkPDFGlyphUse& subset, | |
| int32_t* 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 = pdfStrikeSpec.fUnitsPerEM; | |
| SkBulkGlyphMetricsAndPaths paths{pdfStrikeSpec.fStrikeSpec}; | |
| auto result = SkPDFMakeArray(); | |
| std::vector<SkGlyphID> glyphIDs; | |
| subset.getSetValues([&](unsigned index) { | |
| glyphIDs.push_back(SkTo<SkGlyphID>(index)); | |
| }); | |
| auto glyphs = paths.glyphs(SkSpan(glyphIDs)); | |
| // C++20 = make_unique_for_overwrite<SkScalar[]>(glyphs.size()); | |
| auto advances = std::unique_ptr<SkScalar[]>(new SkScalar[glyphs.size()]); | |
| // Find the pdf integer mode (most common pdf integer advance). | |
| // Unfortunately, poppler enforces DW (default width) must be an integer, | |
| // so only consider integer pdf advances when finding the mode. | |
| size_t numIntAdvances = 0; | |
| for (const SkGlyph* glyph : glyphs) { | |
| SkScalar currentAdvance = from_font_units(glyph->advanceX(), emSize); | |
| if ((int32_t)currentAdvance == currentAdvance) { | |
| advances[numIntAdvances++] = currentAdvance; | |
| } | |
| } | |
| std::sort(advances.get(), advances.get() + numIntAdvances); | |
| int32_t modeAdvance = (int32_t)find_mode_or_0(SkSpan(advances.get(), numIntAdvances)); | |
| *defaultAdvance = modeAdvance; | |
| // Pre-convert to pdf advances. | |
| for (size_t i = 0; i < glyphs.size(); ++i) { | |
| advances[i] = from_font_units(glyphs[i]->advanceX(), emSize); | |
| } | |
| for (size_t i = 0; i < glyphs.size(); ++i) { | |
| SkScalar advance = advances[i]; | |
| // a. Skipping don't cares or defaults is a win (trivial) | |
| if (advance == modeAdvance) { | |
| continue; | |
| } | |
| // 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) { | |
| SkScalar next_advance = advances[j]; | |
| if (advance != next_advance) { | |
| break; | |
| } | |
| } | |
| if (j - i >= 2) { | |
| result->appendInt(glyphs[i]->getGlyphID()); | |
| result->appendInt(glyphs[j - 1]->getGlyphID()); | |
| result->appendScalar(advance); | |
| i = j - 1; | |
| continue; | |
| } | |
| } | |
| { | |
| result->appendInt(glyphs[i]->getGlyphID()); | |
| auto advanceArray = SkPDFMakeArray(); | |
| advanceArray->appendScalar(advance); | |
| size_t j = i + 1; // j is always one past the last output | |
| for (; j < glyphs.size(); ++j) { | |
| advance = advances[j]; | |
| // c. end range if default seen | |
| if (advance == modeAdvance) { | |
| break; | |
| } | |
| int dontCares = glyphs[j]->getGlyphID() - glyphs[j - 1]->getGlyphID() - 1; | |
| // d. end range if 4+ don't cares | |
| if (dontCares >= 4) { | |
| break; | |
| } | |
| SkScalar next_advance = 0; | |
| // e. end range for 2+ repeats with 4+ don't cares | |
| if (j + 1 < glyphs.size()) { | |
| next_advance = advances[j+1]; | |
| 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 = advances[j+2]; | |
| if (advance == next_advance) { | |
| break; | |
| } | |
| } | |
| while (dontCares --> 0) { | |
| advanceArray->appendScalar(0); | |
| } | |
| advanceArray->appendScalar(advance); | |
| } | |
| result->appendObject(std::move(advanceArray)); | |
| i = j - 1; | |
| } | |
| } | |
| return result; | |
| } |