• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&current, 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