1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2016 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "src/pdf/SkPDFMakeCIDGlyphWidthsArray.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/core/SkPaint.h"
11cb93a386Sopenharmony_ci#include "include/private/SkTo.h"
12cb93a386Sopenharmony_ci#include "src/core/SkScalerCache.h"
13cb93a386Sopenharmony_ci#include "src/core/SkStrikeSpec.h"
14cb93a386Sopenharmony_ci#include "src/pdf/SkPDFGlyphUse.h"
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_ci#include <algorithm>
17cb93a386Sopenharmony_ci#include <vector>
18cb93a386Sopenharmony_ci
19cb93a386Sopenharmony_ci// TODO(halcanary): Write unit tests for SkPDFMakeCIDGlyphWidthsArray().
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_ci// TODO(halcanary): The logic in this file originated in several
22cb93a386Sopenharmony_ci// disparate places.  I feel sure that someone could simplify this
23cb93a386Sopenharmony_ci// down to a single easy-to-read function.
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_cinamespace {
26cb93a386Sopenharmony_ci
27cb93a386Sopenharmony_ci// scale from em-units to base-1000, returning as a SkScalar
28cb93a386Sopenharmony_ciSkScalar from_font_units(SkScalar scaled, uint16_t emSize) {
29cb93a386Sopenharmony_ci    if (emSize == 1000) {
30cb93a386Sopenharmony_ci        return scaled;
31cb93a386Sopenharmony_ci    } else {
32cb93a386Sopenharmony_ci        return scaled * 1000 / emSize;
33cb93a386Sopenharmony_ci    }
34cb93a386Sopenharmony_ci}
35cb93a386Sopenharmony_ci
36cb93a386Sopenharmony_ciSkScalar scale_from_font_units(int16_t val, uint16_t emSize) {
37cb93a386Sopenharmony_ci    return from_font_units(SkIntToScalar(val), emSize);
38cb93a386Sopenharmony_ci}
39cb93a386Sopenharmony_ci
40cb93a386Sopenharmony_ci// Unfortunately poppler does not appear to respect the default width setting.
41cb93a386Sopenharmony_ci#if defined(SK_PDF_CAN_USE_DW)
42cb93a386Sopenharmony_ciint16_t findMode(SkSpan<const int16_t> advances) {
43cb93a386Sopenharmony_ci    if (advances.empty()) {
44cb93a386Sopenharmony_ci        return 0;
45cb93a386Sopenharmony_ci    }
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_ci    int16_t previousAdvance = advances[0];
48cb93a386Sopenharmony_ci    int16_t currentModeAdvance = advances[0];
49cb93a386Sopenharmony_ci    size_t currentCount = 1;
50cb93a386Sopenharmony_ci    size_t currentModeCount = 1;
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_ci    for (size_t i = 1; i < advances.size(); ++i) {
53cb93a386Sopenharmony_ci        if (advances[i] == previousAdvance) {
54cb93a386Sopenharmony_ci            ++currentCount;
55cb93a386Sopenharmony_ci        } else {
56cb93a386Sopenharmony_ci            if (currentCount > currentModeCount) {
57cb93a386Sopenharmony_ci                currentModeAdvance = previousAdvance;
58cb93a386Sopenharmony_ci                currentModeCount = currentCount;
59cb93a386Sopenharmony_ci            }
60cb93a386Sopenharmony_ci            previousAdvance = advances[i];
61cb93a386Sopenharmony_ci            currentCount = 1;
62cb93a386Sopenharmony_ci        }
63cb93a386Sopenharmony_ci    }
64cb93a386Sopenharmony_ci
65cb93a386Sopenharmony_ci    return currentCount > currentModeCount ? previousAdvance : currentModeAdvance;
66cb93a386Sopenharmony_ci}
67cb93a386Sopenharmony_ci#endif
68cb93a386Sopenharmony_ci} // namespace
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_ci/** Retrieve advance data for glyphs. Used by the PDF backend. */
71cb93a386Sopenharmony_ci// TODO(halcanary): this function is complex enough to need its logic
72cb93a386Sopenharmony_ci// tested with unit tests.
73cb93a386Sopenharmony_cistd::unique_ptr<SkPDFArray> SkPDFMakeCIDGlyphWidthsArray(const SkTypeface& typeface,
74cb93a386Sopenharmony_ci                                                         const SkPDFGlyphUse& subset,
75cb93a386Sopenharmony_ci                                                         SkScalar* defaultAdvance) {
76cb93a386Sopenharmony_ci    // There are two ways of expressing advances
77cb93a386Sopenharmony_ci    //
78cb93a386Sopenharmony_ci    // range: " gfid [adv.ances adv.ances ... adv.ances]"
79cb93a386Sopenharmony_ci    //   run: " gfid gfid adv.ances"
80cb93a386Sopenharmony_ci    //
81cb93a386Sopenharmony_ci    // Assuming that on average
82cb93a386Sopenharmony_ci    // the ASCII representation of an advance plus a space is 10 characters
83cb93a386Sopenharmony_ci    // the ASCII representation of a glyph id plus a space is 4 characters
84cb93a386Sopenharmony_ci    // the ASCII representation of unused gid plus a space in a range is 2 characters
85cb93a386Sopenharmony_ci    //
86cb93a386Sopenharmony_ci    // When not in a range or run
87cb93a386Sopenharmony_ci    //  a. Skipping don't cares or defaults is a win (trivial)
88cb93a386Sopenharmony_ci    //  b. Run wins for 2+ repeats " gid gid adv.ances"
89cb93a386Sopenharmony_ci    //                             " gid [adv.ances adv.ances]"
90cb93a386Sopenharmony_ci    //     rule: 2+ repeats create run as long as possible, else start range
91cb93a386Sopenharmony_ci    //
92cb93a386Sopenharmony_ci    // When in a range
93cb93a386Sopenharmony_ci    // Cost of stopping and starting a range is 8 characters  "] gid ["
94cb93a386Sopenharmony_ci    //  c. Skipping defaults is always a win                  " adv.ances"
95cb93a386Sopenharmony_ci    //     rule: end range if default seen
96cb93a386Sopenharmony_ci    //  d. Skipping 4+ don't cares is a win                   " 0 0 0 0"
97cb93a386Sopenharmony_ci    //     rule: end range if 4+ don't cares
98cb93a386Sopenharmony_ci    // Cost of stop and start range plus run is 28 characters "] gid gid adv.ances gid ["
99cb93a386Sopenharmony_ci    //  e. Switching for 2+ repeats and 4+ don't cares wins   " 0 0 adv.ances 0 0 adv.ances"
100cb93a386Sopenharmony_ci    //     rule: end range for 2+ repeats with 4+ don't cares
101cb93a386Sopenharmony_ci    //  f. Switching for 3+ repeats wins                      " adv.ances adv.ances adv.ances"
102cb93a386Sopenharmony_ci    //     rule: end range for 3+ repeats
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_ci    int emSize;
105cb93a386Sopenharmony_ci    SkStrikeSpec strikeSpec = SkStrikeSpec::MakePDFVector(typeface, &emSize);
106cb93a386Sopenharmony_ci    SkBulkGlyphMetricsAndPaths paths{strikeSpec};
107cb93a386Sopenharmony_ci
108cb93a386Sopenharmony_ci    auto result = SkPDFMakeArray();
109cb93a386Sopenharmony_ci
110cb93a386Sopenharmony_ci    std::vector<SkGlyphID> glyphIDs;
111cb93a386Sopenharmony_ci    subset.getSetValues([&](unsigned index) {
112cb93a386Sopenharmony_ci        glyphIDs.push_back(SkToU16(index));
113cb93a386Sopenharmony_ci    });
114cb93a386Sopenharmony_ci    auto glyphs = paths.glyphs(SkMakeSpan(glyphIDs));
115cb93a386Sopenharmony_ci
116cb93a386Sopenharmony_ci#if defined(SK_PDF_CAN_USE_DW)
117cb93a386Sopenharmony_ci    std::vector<int16_t> advances;
118cb93a386Sopenharmony_ci    advances.reserve_back(glyphs.size());
119cb93a386Sopenharmony_ci    for (const SkGlyph* glyph : glyphs) {
120cb93a386Sopenharmony_ci        advances.push_back((int16_t)glyph->advanceX());
121cb93a386Sopenharmony_ci    }
122cb93a386Sopenharmony_ci    std::sort(advances.begin(), advances.end());
123cb93a386Sopenharmony_ci    int16_t modeAdvance = findMode(SkMakeSpan(advances));
124cb93a386Sopenharmony_ci    *defaultAdvance = scale_from_font_units(modeAdvance, emSize);
125cb93a386Sopenharmony_ci#else
126cb93a386Sopenharmony_ci    *defaultAdvance = 0;
127cb93a386Sopenharmony_ci#endif
128cb93a386Sopenharmony_ci
129cb93a386Sopenharmony_ci    for (size_t i = 0; i < glyphs.size(); ++i) {
130cb93a386Sopenharmony_ci        int16_t advance = (int16_t)glyphs[i]->advanceX();
131cb93a386Sopenharmony_ci
132cb93a386Sopenharmony_ci#if defined(SK_PDF_CAN_USE_DW)
133cb93a386Sopenharmony_ci        // a. Skipping don't cares or defaults is a win (trivial)
134cb93a386Sopenharmony_ci        if (advance == modeAdvance) {
135cb93a386Sopenharmony_ci            continue;
136cb93a386Sopenharmony_ci        }
137cb93a386Sopenharmony_ci#endif
138cb93a386Sopenharmony_ci
139cb93a386Sopenharmony_ci        // b. 2+ repeats create run as long as possible, else start range
140cb93a386Sopenharmony_ci        {
141cb93a386Sopenharmony_ci            size_t j = i + 1; // j is always one past the last known repeat
142cb93a386Sopenharmony_ci            for (; j < glyphs.size(); ++j) {
143cb93a386Sopenharmony_ci                int16_t next_advance = (int16_t)glyphs[j]->advanceX();
144cb93a386Sopenharmony_ci                if (advance != next_advance) {
145cb93a386Sopenharmony_ci                    break;
146cb93a386Sopenharmony_ci                }
147cb93a386Sopenharmony_ci            }
148cb93a386Sopenharmony_ci            if (j - i >= 2) {
149cb93a386Sopenharmony_ci                result->appendInt(glyphs[i]->getGlyphID());
150cb93a386Sopenharmony_ci                result->appendInt(glyphs[j - 1]->getGlyphID());
151cb93a386Sopenharmony_ci                result->appendScalar(scale_from_font_units(advance, emSize));
152cb93a386Sopenharmony_ci                i = j - 1;
153cb93a386Sopenharmony_ci                continue;
154cb93a386Sopenharmony_ci            }
155cb93a386Sopenharmony_ci        }
156cb93a386Sopenharmony_ci
157cb93a386Sopenharmony_ci        {
158cb93a386Sopenharmony_ci            result->appendInt(glyphs[i]->getGlyphID());
159cb93a386Sopenharmony_ci            auto advanceArray = SkPDFMakeArray();
160cb93a386Sopenharmony_ci            advanceArray->appendScalar(scale_from_font_units(advance, emSize));
161cb93a386Sopenharmony_ci            size_t j = i + 1; // j is always one past the last output
162cb93a386Sopenharmony_ci            for (; j < glyphs.size(); ++j) {
163cb93a386Sopenharmony_ci                advance = (int16_t)glyphs[j]->advanceX();
164cb93a386Sopenharmony_ci#if defined(SK_PDF_CAN_USE_DW)
165cb93a386Sopenharmony_ci                // c. end range if default seen
166cb93a386Sopenharmony_ci                if (advance == modeAdvance) {
167cb93a386Sopenharmony_ci                    break;
168cb93a386Sopenharmony_ci                }
169cb93a386Sopenharmony_ci#endif
170cb93a386Sopenharmony_ci
171cb93a386Sopenharmony_ci                int dontCares = glyphs[j]->getGlyphID() - glyphs[j - 1]->getGlyphID() - 1;
172cb93a386Sopenharmony_ci                // d. end range if 4+ don't cares
173cb93a386Sopenharmony_ci                if (dontCares >= 4) {
174cb93a386Sopenharmony_ci                    break;
175cb93a386Sopenharmony_ci                }
176cb93a386Sopenharmony_ci
177cb93a386Sopenharmony_ci                int16_t next_advance = 0;
178cb93a386Sopenharmony_ci                // e. end range for 2+ repeats with 4+ don't cares
179cb93a386Sopenharmony_ci                if (j + 1 < glyphs.size()) {
180cb93a386Sopenharmony_ci                    next_advance = (int16_t)glyphs[j+1]->advanceX();
181cb93a386Sopenharmony_ci                    int next_dontCares = glyphs[j+1]->getGlyphID() - glyphs[j]->getGlyphID() - 1;
182cb93a386Sopenharmony_ci                    if (advance == next_advance && dontCares + next_dontCares >= 4) {
183cb93a386Sopenharmony_ci                        break;
184cb93a386Sopenharmony_ci                    }
185cb93a386Sopenharmony_ci                }
186cb93a386Sopenharmony_ci
187cb93a386Sopenharmony_ci                // f. end range for 3+ repeats
188cb93a386Sopenharmony_ci                if (j + 2 < glyphs.size() && advance == next_advance) {
189cb93a386Sopenharmony_ci                    next_advance = (int16_t)glyphs[j+2]->advanceX();
190cb93a386Sopenharmony_ci                    if (advance == next_advance) {
191cb93a386Sopenharmony_ci                        break;
192cb93a386Sopenharmony_ci                    }
193cb93a386Sopenharmony_ci                }
194cb93a386Sopenharmony_ci
195cb93a386Sopenharmony_ci                while (dontCares --> 0) {
196cb93a386Sopenharmony_ci                    advanceArray->appendScalar(0);
197cb93a386Sopenharmony_ci                }
198cb93a386Sopenharmony_ci                advanceArray->appendScalar(scale_from_font_units(advance, emSize));
199cb93a386Sopenharmony_ci            }
200cb93a386Sopenharmony_ci            result->appendObject(std::move(advanceArray));
201cb93a386Sopenharmony_ci            i = j - 1;
202cb93a386Sopenharmony_ci        }
203cb93a386Sopenharmony_ci    }
204cb93a386Sopenharmony_ci
205cb93a386Sopenharmony_ci    return result;
206cb93a386Sopenharmony_ci}
207