1 // 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
14 namespace {
utf8_next(const char ** ptr,const char * end)15 SkUnichar 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
22 namespace skia {
23 namespace textlayout {
24
findNext(const char * codepoint,SkFont * font,SkScalar * height)25 bool FontResolver::findNext(const char* codepoint, SkFont* font, SkScalar* height) {
26
27 SkASSERT(fFontIterator != nullptr);
28 TextIndex index = codepoint - fText.begin();
29 while (fFontIterator != fFontSwitches.end() && fFontIterator->fStart <= index) {
30 if (fFontIterator->fStart == index) {
31 *font = fFontIterator->fFont;
32 *height = fFontIterator->fHeight;
33 return true;
34 }
35 ++fFontIterator;
36 }
37 return false;
38 }
39
findAllFontsForStyledBlock(const TextStyle & style,TextRange textRange)40 void FontResolver::findAllFontsForStyledBlock(const TextStyle& style, TextRange textRange) {
41 fCodepoints.reset();
42 fCharacters.reset();
43 fUnresolvedIndexes.reset();
44 fUnresolvedCodepoints.reset();
45
46 // Extract all unicode codepoints
47 const char* end = fText.begin() + textRange.end;
48 const char* current = fText.begin() + textRange.start;
49 while (current != end) {
50 fCharacters.emplace_back(current);
51 fCodepoints.emplace_back(utf8_next(¤t, end));
52 fUnresolvedIndexes.emplace_back(fUnresolvedIndexes.size());
53 }
54 fUnresolved = fCodepoints.size();
55
56 // Walk through all available fonts to resolve the block
57 for (auto& fontFamily : style.getFontFamilies()) {
58 auto typeface = fFontCollection->matchTypeface(fontFamily.c_str(), style.getFontStyle());
59 if (typeface.get() == nullptr) {
60 continue;
61 }
62
63 // Resolve all unresolved characters
64 auto font = makeFont(typeface, style.getFontSize(), style.getHeight());
65 resolveAllCharactersByFont(font);
66 if (fUnresolved == 0) {
67 break;
68 }
69 }
70
71 if (fUnresolved > 0) {
72 auto typeface = fFontCollection->matchDefaultTypeface(style.getFontStyle());
73 if (typeface.get() != nullptr) {
74 // Resolve all unresolved characters
75 auto font = makeFont(typeface, style.getFontSize(), style.getHeight());
76 resolveAllCharactersByFont(font);
77 }
78 }
79
80 addResolvedWhitespacesToMapping();
81
82 if (fUnresolved > 0 && fFontCollection->fontFallbackEnabled()) {
83 while (fUnresolved > 0) {
84 auto unicode = firstUnresolved();
85 auto typeface = fFontCollection->defaultFallback(unicode, style.getFontStyle(), style.getLocale());
86 if (typeface == nullptr) {
87 break;
88 }
89 auto font = makeFont(typeface, style.getFontSize(), style.getHeight());
90 if (!resolveAllCharactersByFont(font)) {
91 // Not a single unicode character was resolved
92 break;
93 }
94 SkString name;
95 typeface->getFamilyName(&name);
96 SkDebugf("Default font fallback resolution: %s\n", name.c_str());
97 }
98 }
99
100 // In case something still unresolved
101 if (fResolvedFonts.count() == 0) {
102 auto result = fFontCollection->defaultFallback(firstUnresolved(), style.getFontStyle(), style.getLocale());
103 if (result == nullptr) {
104 SkDebugf("No fallback!!!\n");
105 return;
106 }
107 makeFont(result,
108 style.getFontSize(),
109 style.getHeight());
110 if (fFirstResolvedFont.fFont.getTypeface() != nullptr) {
111 SkString name;
112 fFirstResolvedFont.fFont.getTypeface()->getFamilyName(&name);
113 SkDebugf("Urgent font resolution: %s\n", name.c_str());
114 } else {
115 SkDebugf("No font!!!\n");
116 }
117 }
118 }
119
resolveAllCharactersByFont(const FontDescr & font)120 size_t FontResolver::resolveAllCharactersByFont(const FontDescr& font) {
121 // Consolidate all unresolved unicodes in one array to make a batch call
122 SkTArray<SkGlyphID> glyphs(fUnresolved);
123 glyphs.push_back_n(fUnresolved, SkGlyphID(0));
124 font.fFont.getTypeface()->unicharsToGlyphs(
125 fUnresolved == fCodepoints.size() ? fCodepoints.data() : fUnresolvedCodepoints.data(),
126 fUnresolved, glyphs.data());
127
128 SkRange<size_t> resolved(0, 0);
129 SkRange<size_t> whitespaces(0, 0);
130 size_t stillUnresolved = 0;
131
132 auto processRuns = [&]() {
133 if (resolved.width() == 0) {
134 return;
135 }
136
137 if (resolved.width() == whitespaces.width()) {
138 // The entire run is just whitespaces;
139 // Remember the font and mark whitespaces back unresolved
140 // to calculate its mapping for the other fonts
141 for (auto w = whitespaces.start; w != whitespaces.end; ++w) {
142 if (fWhitespaces.find(w) == nullptr) {
143 fWhitespaces.set(w, font);
144 }
145 fUnresolvedIndexes[stillUnresolved++] = w;
146 fUnresolvedCodepoints.emplace_back(fCodepoints[w]);
147 }
148 } else {
149 //SkDebugf("Resolved %d @%d\n", font.fFont.getTypeface()->uniqueID(), resolved.start);
150 fFontMapping.set(fCharacters[resolved.start] - fText.begin(), font);
151 }
152 };
153
154 // Try to resolve all the unresolved unicode points
155 for (size_t i = 0; i < glyphs.size(); ++i) {
156 auto glyph = glyphs[i];
157 auto index = fUnresolvedIndexes[i];
158
159 if (glyph == 0) {
160 processRuns();
161
162 resolved = SkRange<size_t>(0, 0);
163 whitespaces = SkRange<size_t>(0, 0);
164
165 fUnresolvedIndexes[stillUnresolved++] = index;
166 fUnresolvedCodepoints.emplace_back(fCodepoints[index]);
167 continue;
168 }
169
170 if (index == resolved.end) {
171 ++resolved.end;
172 } else {
173 processRuns();
174 resolved = SkRange<size_t>(index, index + 1);
175 }
176 if (u_isUWhiteSpace(fCodepoints[index])) {
177 if (index == whitespaces.end) {
178 ++whitespaces.end;
179 } else {
180 whitespaces = SkRange<size_t>(index, index + 1);
181 }
182 } else {
183 whitespaces = SkRange<size_t>(0, 0);
184 }
185 }
186
187 // One last time to take care of the tail run
188 processRuns();
189
190 size_t wasUnresolved = fUnresolved;
191 fUnresolved = stillUnresolved;
192 return fUnresolved < wasUnresolved;
193 }
194
addResolvedWhitespacesToMapping()195 void FontResolver::addResolvedWhitespacesToMapping() {
196 size_t resolvedWhitespaces = 0;
197 for (size_t i = 0; i < fUnresolved; ++i) {
198 auto index = fUnresolvedIndexes[i];
199 auto found = fWhitespaces.find(index);
200 if (found != nullptr) {
201 fFontMapping.set(fCharacters[index] - fText.begin(), *found);
202 ++resolvedWhitespaces;
203 }
204 }
205 fUnresolved -= resolvedWhitespaces;
206 }
207
makeFont(sk_sp<SkTypeface> typeface,SkScalar size,SkScalar height)208 FontDescr FontResolver::makeFont(sk_sp<SkTypeface> typeface, SkScalar size, SkScalar height) {
209 SkFont font(typeface, size);
210 font.setEdging(SkFont::Edging::kAntiAlias);
211 font.setHinting(SkFontHinting::kSlight);
212 font.setSubpixel(true);
213 FontDescr descr(font, height);
214
215 const FontDescr* foundFont = fResolvedFonts.find(descr);
216 if (foundFont == nullptr) {
217 if (fResolvedFonts.count() == 0) {
218 fFirstResolvedFont = descr;
219 }
220 fResolvedFonts.add(descr);
221 }
222 return descr;
223 }
224
firstUnresolved()225 SkUnichar FontResolver::firstUnresolved() {
226 if (fUnresolved == 0) return 0;
227
228 bool firstTry = fUnresolved == fCodepoints.size();
229 auto index = firstTry ? 0 : fUnresolvedIndexes[0];
230 return fCodepoints[index];
231 }
232
findAllFontsForAllStyledBlocks(ParagraphImpl * master)233 void FontResolver::findAllFontsForAllStyledBlocks(ParagraphImpl* master) {
234 fFontCollection = master->fontCollection();
235 fStyles = master->styles();
236 fText = master->text();
237 fTextRange = TextRange(0, fText.size());
238
239 Block combined;
240 for (auto& block : fStyles) {
241 SkASSERT(combined.fRange.empty() ||
242 combined.fRange.end == block.fRange.start);
243
244 if (!combined.fRange.empty() &&
245 block.fStyle.matchOneAttribute(StyleType::kFont, combined.fStyle)) {
246 combined.add(block.fRange);
247 continue;
248 }
249
250 if (!combined.fRange.empty()) {
251 this->findAllFontsForStyledBlock(combined.fStyle, combined.fRange);
252 }
253
254 combined = block;
255 }
256 this->findAllFontsForStyledBlock(combined.fStyle, combined.fRange);
257
258
259 fFontSwitches.reset();
260 FontDescr* prev = nullptr;
261 for (auto& ch : fText) {
262 if (fFontSwitches.count() == fFontMapping.count()) {
263 // Checked all
264 break;
265 }
266 auto found = fFontMapping.find(&ch - fText.begin());
267 if (found == nullptr) {
268 // Insignificant character
269 continue;
270 }
271 if (prev == nullptr) {
272 prev = found;
273 prev->fStart = 0;
274 }
275
276 if (*prev == *found) {
277 // Same font
278 continue;
279 }
280 fFontSwitches.emplace_back(*prev);
281
282 prev = found;
283 prev->fStart = &ch - fText.begin();
284 }
285
286 if (prev == nullptr) {
287 fFirstResolvedFont.fStart = 0;
288 prev = &fFirstResolvedFont;
289 }
290 fFontSwitches.emplace_back(*prev);
291 fFontIterator = fFontSwitches.begin();
292 }
293 } // namespace textlayout
294 } // namespace skia
295