blob: 0b63defa8f50350c9995ac60e2647267f4f1a649 [file] [log] [blame]
Julia Lavrova6e6333f2019-06-17 10:34:10 -04001// Copyright 2019 Google LLC.
2
3#include <unicode/brkiter.h>
4#include <unicode/ubidi.h>
5#include "include/core/SkBlurTypes.h"
6#include "include/core/SkCanvas.h"
7#include "include/core/SkFontMgr.h"
8#include "include/core/SkPictureRecorder.h"
9#include "modules/skparagraph/src/FontResolver.h"
10#include "modules/skparagraph/src/ParagraphImpl.h"
11#include "src/core/SkSpan.h"
12#include "src/utils/SkUTF.h"
13
14namespace {
15SkUnichar utf8_next(const char** ptr, const char* end) {
16 SkUnichar val = SkUTF::NextUTF8(ptr, end);
17 return val < 0 ? 0xFFFD : val;
18}
19} // namespace
20
21// TODO: FontResolver and FontIterator have common functionality
22namespace skia {
23namespace textlayout {
24
25FontResolver::FontResolver(sk_sp<FontCollection> fontCollection)
26 : fFontCollection(fontCollection) {}
27
28bool FontResolver::findFirst(const char* codepoint, SkFont* font, SkScalar* height) {
29 auto found = fFontMapping.find(codepoint);
30 if (found == nullptr) {
31 // Resolve the first character with the first found font
32 found = fFontMapping.set(codepoint, fFirstResolvedFont);
33 }
34 if (found == nullptr) {
35 return false;
36 }
37 *font = found->first;
38 *height = found->second;
39 return true;
40}
41
42bool FontResolver::findNext(const char* codepoint, SkFont* font, SkScalar* height) {
43 auto found = fFontMapping.find(codepoint);
44 if (found == nullptr) {
45 return false;
46 }
47 *font = found->first;
48 *height = found->second;
49 return true;
50}
51
52void FontResolver::findAllFontsForStyledBlock(const TextStyle& style, SkSpan<const char> text) {
53 fCodepoints.reset();
54 fCharacters.reset();
55 fUnresolvedIndexes.reset();
56 fUnresolvedCodepoints.reset();
57
58 // Extract all unicode codepoints
59 const char* current = text.begin();
60 while (current != text.end()) {
61 fCharacters.emplace_back(current);
62 fCodepoints.emplace_back(utf8_next(&current, text.end()));
63 fUnresolvedIndexes.emplace_back(fUnresolvedIndexes.size());
64 }
65 fUnresolved = fCodepoints.size();
66
67 // Walk through all available fonts to resolve the block
68 for (auto& fontFamily : style.getFontFamilies()) {
69 auto typeface = fFontCollection->matchTypeface(fontFamily.c_str(), style.getFontStyle());
70 if (typeface.get() == nullptr) {
71 continue;
72 }
73
74 // Resolve all unresolved characters
75 auto font = makeFont(typeface, style.getFontSize(), style.getHeight());
76 resolveAllCharactersByFont(font);
77 if (fUnresolved == 0) {
78 break;
79 }
80 }
81
82 if (fUnresolved > 0) {
83 auto typeface = fFontCollection->matchDefaultTypeface(style.getFontStyle());
84 if (typeface.get() != nullptr) {
85 // Resolve all unresolved characters
86 auto font = makeFont(typeface, style.getFontSize(), style.getHeight());
87 resolveAllCharactersByFont(font);
88 }
89 }
90
91 addResolvedWhitespacesToMapping();
92
93 if (fUnresolved > 0 && fFontCollection->fontFallbackEnabled()) {
94 while (fUnresolved > 0) {
95 auto unicode = firstUnresolved();
96 auto typeface = fFontCollection->defaultFallback(unicode, style.getFontStyle());
97 if (typeface == nullptr) {
98 break;
99 }
100 auto font = makeFont(typeface, style.getFontSize(), style.getHeight());
101 if (!resolveAllCharactersByFont(font)) {
102 // Not a single unicode character was resolved
103 break;
104 }
105 SkString name;
106 typeface->getFamilyName(&name);
107 SkDebugf("Default font fallback resolution: %s\n", name.c_str());
108 }
109 }
110
111 // In case something still unresolved
112 if (fResolvedFonts.count() == 0) {
113 makeFont(fFontCollection->defaultFallback(firstUnresolved(), style.getFontStyle()),
114 style.getFontSize(), style.getHeight());
115 if (fFirstResolvedFont.first.getTypeface() != nullptr) {
116 SkString name;
117 fFirstResolvedFont.first.getTypeface()->getFamilyName(&name);
118 SkDebugf("Urgent font resolution: %s\n", name.c_str());
119 } else {
120 SkDebugf("No font!!!\n");
121 }
122 }
123}
124
125size_t FontResolver::resolveAllCharactersByFont(std::pair<SkFont, SkScalar> font) {
126 // Consolidate all unresolved unicodes in one array to make a batch call
127 SkTArray<SkGlyphID> glyphs(fUnresolved);
128 glyphs.push_back_n(fUnresolved, SkGlyphID(0));
129 font.first.getTypeface()->unicharsToGlyphs(
130 fUnresolved == fCodepoints.size() ? fCodepoints.data() : fUnresolvedCodepoints.data(),
131 fUnresolved, glyphs.data());
132
133 SkRange<size_t> resolved(0, 0);
134 SkRange<size_t> whitespaces(0, 0);
135 size_t stillUnresolved = 0;
136
137 auto processRuns = [&]() {
138 if (resolved.width() == 0) {
139 return;
140 }
141
142 if (resolved.width() == whitespaces.width()) {
143 // The entire run is just whitespaces;
144 // Remember the font and mark whitespaces back unresolved
145 // to calculate its mapping for the other fonts
146 for (auto w = whitespaces.start; w != whitespaces.end; ++w) {
147 if (fWhitespaces.find(w) == nullptr) {
148 fWhitespaces.set(w, font);
149 }
150 fUnresolvedIndexes[stillUnresolved++] = w;
151 fUnresolvedCodepoints.emplace_back(fCodepoints[w]);
152 }
153 } else {
154 fFontMapping.set(fCharacters[resolved.start], font);
155 }
156 };
157
158 // Try to resolve all the unresolved unicode points
159 for (size_t i = 0; i < glyphs.size(); ++i) {
160 auto glyph = glyphs[i];
161 auto index = fUnresolvedIndexes[i];
162
163 if (glyph == 0) {
164 processRuns();
165
166 resolved = SkRange<size_t>(0, 0);
167 whitespaces = SkRange<size_t>(0, 0);
168
169 fUnresolvedIndexes[stillUnresolved++] = index;
170 fUnresolvedCodepoints.emplace_back(fCodepoints[index]);
171 continue;
172 }
173
174 if (index == resolved.end) {
175 ++resolved.end;
176 } else {
177 processRuns();
178 resolved = SkRange<size_t>(index, index + 1);
179 }
180 if (u_isUWhiteSpace(fCodepoints[index])) {
181 if (index == whitespaces.end) {
182 ++whitespaces.end;
183 } else {
184 whitespaces = SkRange<size_t>(index, index + 1);
185 }
186 } else {
187 whitespaces = SkRange<size_t>(0, 0);
188 }
189 }
190
191 // One last time to take care of the tail run
192 processRuns();
193
194 size_t wasUnresolved = fUnresolved;
195 fUnresolved = stillUnresolved;
196 return fUnresolved < wasUnresolved;
197}
198
199void FontResolver::addResolvedWhitespacesToMapping() {
200 size_t resolvedWhitespaces = 0;
201 for (size_t i = 0; i < fUnresolved; ++i) {
202 auto index = fUnresolvedIndexes[i];
203 auto found = fWhitespaces.find(index);
204 if (found != nullptr) {
205 fFontMapping.set(fCharacters[index], *found);
206 ++resolvedWhitespaces;
207 }
208 }
209 fUnresolved -= resolvedWhitespaces;
210}
211
212std::pair<SkFont, SkScalar> FontResolver::makeFont(sk_sp<SkTypeface> typeface,
213 SkScalar size,
214 SkScalar height) {
215 SkFont font(typeface, size);
216 font.setEdging(SkFont::Edging::kAntiAlias);
217 font.setHinting(SkFontHinting::kSlight);
218 font.setSubpixel(true);
219 auto pair = std::make_pair(font, height);
220
221 auto foundFont = fResolvedFonts.find(pair);
222 if (foundFont == nullptr) {
223 if (fResolvedFonts.count() == 0) {
224 fFirstResolvedFont = pair;
225 }
226 fResolvedFonts.add(pair);
227 }
228
229 return pair;
230}
231
232SkUnichar FontResolver::firstUnresolved() {
233 if (fUnresolved == 0) return 0;
234
235 bool firstTry = fUnresolved == fCodepoints.size();
236 auto index = firstTry ? 0 : fUnresolvedIndexes[0];
237 return fCodepoints[index];
238}
239} // namespace textlayout
240} // namespace skia