1 /*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "gm/gm.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkFont.h"
12 #include "include/core/SkFontMetrics.h"
13 #include "include/core/SkFontMgr.h"
14 #include "include/core/SkFontStyle.h"
15 #include "include/core/SkFontTypes.h"
16 #include "include/core/SkGraphics.h"
17 #include "include/core/SkPaint.h"
18 #include "include/core/SkPath.h"
19 #include "include/core/SkPathEffect.h"
20 #include "include/core/SkPoint.h"
21 #include "include/core/SkRect.h"
22 #include "include/core/SkRefCnt.h"
23 #include "include/core/SkScalar.h"
24 #include "include/core/SkSize.h"
25 #include "include/core/SkString.h"
26 #include "include/core/SkTypeface.h"
27 #include "include/core/SkTypes.h"
28 #include "include/effects/SkDashPathEffect.h"
29 #include "src/core/SkFontPriv.h"
30 #include "tools/SkMetaData.h"
31 #include "tools/ToolUtils.h"
32 #include "tools/fonts/FontToolUtils.h"
33
34 #include <utility>
35
36 // limit this just so we don't take too long to draw
37 #define MAX_FAMILIES 30
38
drawString(SkCanvas * canvas,const SkString & text,SkScalar x,SkScalar y,const SkFont & font)39 static SkScalar drawString(SkCanvas* canvas, const SkString& text, SkScalar x,
40 SkScalar y, const SkFont& font) {
41 canvas->drawString(text, x, y, font, SkPaint());
42 return x + font.measureText(text.c_str(), text.size(), SkTextEncoding::kUTF8);
43 }
44
drawCharacter(SkCanvas * canvas,uint32_t character,SkScalar x,SkScalar y,const SkFont & origFont,SkFontMgr * fm,const char * fontName,const char * bcp47[],int bcp47Count,const SkFontStyle & fontStyle)45 static SkScalar drawCharacter(SkCanvas* canvas, uint32_t character, SkScalar x,
46 SkScalar y, const SkFont& origFont, SkFontMgr* fm,
47 const char* fontName, const char* bcp47[], int bcp47Count,
48 const SkFontStyle& fontStyle) {
49 SkFont font = origFont;
50 // find typeface containing the requested character and draw it
51 SkString ch;
52 ch.appendUnichar(character);
53 sk_sp<SkTypeface> typeface(fm->matchFamilyStyleCharacter(fontName, fontStyle,
54 bcp47, bcp47Count, character));
55 font.setTypeface(typeface);
56 x = drawString(canvas, ch, x, y, font) + 20;
57
58 if (nullptr == typeface) {
59 return x;
60 }
61
62 // repeat the process, but this time use the family name of the typeface
63 // from the first pass. This emulates the behavior in Blink where it
64 // it expects to get the same glyph when following this pattern.
65 SkString familyName;
66 typeface->getFamilyName(&familyName);
67 font.setTypeface(fm->legacyMakeTypeface(familyName.c_str(), typeface->fontStyle()));
68 return drawString(canvas, ch, x, y, font) + 20;
69 }
70
71 static const char* zh = "zh";
72 static const char* ja = "ja";
73
74 class FontMgrGM : public skiagm::GM {
75 sk_sp<SkFontMgr> fFM;
76
onOnceBeforeDraw()77 void onOnceBeforeDraw() override {
78 SkGraphics::SetFontCacheLimit(16 * 1024 * 1024);
79 fFM = ToolUtils::TestFontMgr();
80 }
81
getName() const82 SkString getName() const override { return SkString("fontmgr_iter"); }
83
getISize()84 SkISize getISize() override { return {1536, 768}; }
85
onDraw(SkCanvas * canvas,SkString * errorMsg)86 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
87 SkScalar y = 20;
88 SkFont font = ToolUtils::DefaultFont();
89 font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
90 font.setSubpixel(true);
91 font.setSize(17);
92
93 SkFontMgr* fm = fFM.get();
94 int count = std::min(fm->countFamilies(), MAX_FAMILIES);
95 if (count == 0) {
96 *errorMsg = "No families in SkFontMgr";
97 return DrawResult::kSkip;
98 }
99
100 for (int i = 0; i < count; ++i) {
101 SkString familyName;
102 fm->getFamilyName(i, &familyName);
103 font.setTypeface(ToolUtils::DefaultTypeface());
104 (void)drawString(canvas, familyName, 20, y, font);
105
106 SkScalar x = 220;
107
108 sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
109 for (int j = 0; j < set->count(); ++j) {
110 SkString sname;
111 SkFontStyle fs;
112 set->getStyle(j, &fs, &sname);
113 sname.appendf(" [%d %d %d]", fs.weight(), fs.width(), fs.slant());
114
115 font.setTypeface(sk_sp<SkTypeface>(set->createTypeface(j)));
116 x = drawString(canvas, sname, x, y, font) + 20;
117
118 // check to see that we get different glyphs in japanese and chinese
119 x = drawCharacter(canvas, 0x5203, x, y, font, fm, familyName.c_str(), &zh, 1, fs);
120 x = drawCharacter(canvas, 0x5203, x, y, font, fm, familyName.c_str(), &ja, 1, fs);
121 // check that emoji characters are found
122 x = drawCharacter(canvas, 0x1f601, x, y, font, fm, familyName.c_str(), nullptr,0, fs);
123 }
124 y += 24;
125 }
126 return DrawResult::kOk;
127 }
128 };
129
130 class FontMgrMatchGM : public skiagm::GM {
131 sk_sp<SkFontMgr> fFM;
132
onOnceBeforeDraw()133 void onOnceBeforeDraw() override {
134 fFM = ToolUtils::TestFontMgr();
135 SkGraphics::SetFontCacheLimit(16 * 1024 * 1024);
136 }
137
getName() const138 SkString getName() const override { return SkString("fontmgr_match"); }
139
getISize()140 SkISize getISize() override { return {640, 1024}; }
141
iterateFamily(SkCanvas * canvas,const SkFont & font,SkFontStyleSet * fset)142 void iterateFamily(SkCanvas* canvas, const SkFont& font, SkFontStyleSet* fset) {
143 SkFont f(font);
144 SkScalar y = 0;
145
146 for (int j = 0; j < fset->count(); ++j) {
147 SkString sname;
148 SkFontStyle fs;
149 fset->getStyle(j, &fs, &sname);
150
151 sname.appendf(" [%d %d]", fs.weight(), fs.width());
152
153 f.setTypeface(sk_sp<SkTypeface>(fset->createTypeface(j)));
154 (void)drawString(canvas, sname, 0, y, f);
155 y += 24;
156 }
157 }
158
exploreFamily(SkCanvas * canvas,const SkFont & font,SkFontStyleSet * fset)159 void exploreFamily(SkCanvas* canvas, const SkFont& font, SkFontStyleSet* fset) {
160 SkFont f(font);
161 SkScalar y = 0;
162
163 for (int weight = 100; weight <= 900; weight += 200) {
164 for (int width = 1; width <= 9; width += 2) {
165 SkFontStyle fs(weight, width, SkFontStyle::kUpright_Slant);
166 sk_sp<SkTypeface> face(fset->matchStyle(fs));
167 if (face) {
168 SkString str;
169 str.printf("request [%d %d]", fs.weight(), fs.width());
170 f.setTypeface(std::move(face));
171 (void)drawString(canvas, str, 0, y, f);
172 y += 24;
173 }
174 }
175 }
176 }
177
onDraw(SkCanvas * canvas,SkString * errorMsg)178 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
179 SkFont font = ToolUtils::DefaultFont();
180 font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
181 font.setSubpixel(true);
182 font.setSize(17);
183
184 const char* gNames[] = {
185 "Helvetica Neue", "Arial", "sans", "Roboto"
186 };
187
188 sk_sp<SkFontStyleSet> fset;
189 for (size_t i = 0; i < std::size(gNames); ++i) {
190 fset = fFM->matchFamily(gNames[i]);
191 if (fset->count() > 0) {
192 break;
193 }
194 }
195 if (!fset || fset->count() == 0) {
196 *errorMsg = "No SkFontStyleSet";
197 return DrawResult::kSkip;
198 }
199
200 canvas->translate(20, 40);
201 this->exploreFamily(canvas, font, fset.get());
202 canvas->translate(150, 0);
203 this->iterateFamily(canvas, font, fset.get());
204 return DrawResult::kOk;
205 }
206 };
207
208 class FontMgrBoundsGM : public skiagm::GM {
209 public:
FontMgrBoundsGM(float scale,float skew)210 FontMgrBoundsGM(float scale, float skew) : fScaleX(scale) , fSkewX(skew) {}
211
212 private:
getName() const213 SkString getName() const override {
214 if (fScaleX != 1 || fSkewX != 0) {
215 return SkStringPrintf("fontmgr_bounds_%g_%g", fScaleX, fSkewX);
216 }
217 return SkString("fontmgr_bounds");
218 }
219
onOnceBeforeDraw()220 void onOnceBeforeDraw() override { fFM = ToolUtils::TestFontMgr(); }
221
onGetControls(SkMetaData * controls)222 bool onGetControls(SkMetaData* controls) override {
223 controls->setBool("Label Bounds", fLabelBounds);
224 return true;
225 }
226
onSetControls(const SkMetaData & controls)227 void onSetControls(const SkMetaData& controls) override {
228 controls.findBool("Label Bounds", &fLabelBounds);
229 }
230
show_bounds(SkCanvas * canvas,const SkFont & font,SkScalar x,SkScalar y,SkColor boundsColor,bool labelBounds)231 static SkRect show_bounds(SkCanvas* canvas, const SkFont& font, SkScalar x, SkScalar y,
232 SkColor boundsColor, bool labelBounds)
233 {
234 SkGlyphID left = 0, right = 0, top = 0, bottom = 0;
235 SkRect min = SkRect::MakeLTRB(SK_ScalarInfinity, SK_ScalarInfinity,
236 SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity);
237 {
238 int numGlyphs = font.getTypeface()->countGlyphs();
239 for (int i = 0; i < numGlyphs; ++i) {
240 SkGlyphID glyphId = i;
241 SkRect cur;
242 font.getBounds(&glyphId, 1, &cur, nullptr);
243 if (cur.fLeft < min.fLeft ) { min.fLeft = cur.fLeft; left = i; }
244 if (cur.fTop < min.fTop ) { min.fTop = cur.fTop ; top = i; }
245 if (min.fRight < cur.fRight ) { min.fRight = cur.fRight; right = i; }
246 if (min.fBottom < cur.fBottom) { min.fBottom = cur.fBottom; bottom = i; }
247 }
248 }
249
250 SkRect fontBounds = SkFontPriv::GetFontBounds(font);
251
252 SkRect drawBounds = min;
253 drawBounds.join(fontBounds);
254
255 SkAutoCanvasRestore acr(canvas, true);
256 canvas->translate(x - drawBounds.left(), y);
257
258 SkPaint boundsPaint;
259 boundsPaint.setAntiAlias(true);
260 boundsPaint.setColor(boundsColor);
261 boundsPaint.setStyle(SkPaint::kStroke_Style);
262 canvas->drawRect(fontBounds, boundsPaint);
263
264 const SkScalar intervals[] = { 10.f, 10.f };
265 boundsPaint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0.f));
266 canvas->drawRect(min, boundsPaint);
267
268 SkFontMetrics fm;
269 font.getMetrics(&fm);
270 SkPaint metricsPaint(boundsPaint);
271 metricsPaint.setStyle(SkPaint::kFill_Style);
272 metricsPaint.setAlphaf(0.25f);
273 if ((fm.fFlags & SkFontMetrics::kUnderlinePositionIsValid_Flag) &&
274 (fm.fFlags & SkFontMetrics::kUnderlineThicknessIsValid_Flag))
275 {
276 SkRect underline{ min.fLeft, fm.fUnderlinePosition,
277 min.fRight, fm.fUnderlinePosition + fm.fUnderlineThickness };
278 canvas->drawRect(underline, metricsPaint);
279 }
280
281 if ((fm.fFlags & SkFontMetrics::kStrikeoutPositionIsValid_Flag) &&
282 (fm.fFlags & SkFontMetrics::kStrikeoutThicknessIsValid_Flag))
283 {
284 SkRect strikeout{ min.fLeft, fm.fStrikeoutPosition - fm.fStrikeoutThickness,
285 min.fRight, fm.fStrikeoutPosition };
286 canvas->drawRect(strikeout, metricsPaint);
287 }
288
289 struct GlyphToDraw {
290 SkGlyphID id;
291 SkPoint location;
292 SkScalar rotation;
293 } glyphsToDraw [] = {
294 {left, {min.left(), min.centerY()}, 270},
295 {right, {min.right(), min.centerY()}, 90},
296 {top, {min.centerX(), min.top() }, 0},
297 {bottom, {min.centerX(), min.bottom() }, 180},
298 };
299
300 SkFont labelFont;
301 labelFont.setEdging(SkFont::Edging::kAntiAlias);
302 labelFont.setTypeface(ToolUtils::DefaultPortableTypeface());
303
304 if (labelBounds) {
305 SkString name;
306 font.getTypeface()->getFamilyName(&name);
307 canvas->drawString(name, min.fLeft, min.fBottom, labelFont, SkPaint());
308 }
309 for (const GlyphToDraw& glyphToDraw : glyphsToDraw) {
310 SkPath path;
311 font.getPath(glyphToDraw.id, &path);
312 SkPaint::Style style = path.isEmpty() ? SkPaint::kFill_Style : SkPaint::kStroke_Style;
313 SkPaint glyphPaint;
314 glyphPaint.setStyle(style);
315 canvas->drawSimpleText(&glyphToDraw.id, sizeof(glyphToDraw.id),
316 SkTextEncoding::kGlyphID, 0, 0, font, glyphPaint);
317
318 if (labelBounds) {
319 SkAutoCanvasRestore acr2(canvas, true);
320 canvas->translate(glyphToDraw.location.fX, glyphToDraw.location.fY);
321 canvas->rotate(glyphToDraw.rotation);
322 SkString glyphStr;
323 glyphStr.appendS32(glyphToDraw.id);
324 canvas->drawString(glyphStr, 0, 0, labelFont, SkPaint());
325 }
326 }
327
328 return drawBounds;
329 }
330
getISize()331 SkISize getISize() override { return {1024, 850}; }
332
onDraw(SkCanvas * canvas,SkString * errorMsg)333 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
334 SkFont font = ToolUtils::DefaultFont();
335 font.setEdging(SkFont::Edging::kAntiAlias);
336 font.setSubpixel(true);
337 font.setSize(100);
338 font.setScaleX(fScaleX);
339 font.setSkewX(fSkewX);
340
341 const SkColor boundsColors[2] = { SK_ColorRED, SK_ColorBLUE };
342
343 SkFontMgr* fm = fFM.get();
344 int count = std::min(fm->countFamilies(), 32);
345 if (count == 0) {
346 *errorMsg = "No families in SkFontMgr under test.";
347 return DrawResult::kSkip;
348 }
349
350 int index = 0;
351 SkScalar x = 0, y = 0;
352
353 canvas->translate(10, 120);
354
355 for (int i = 0; i < count; ++i) {
356 sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
357 for (int j = 0; j < set->count() && j < 3; ++j) {
358 font.setTypeface(sk_sp<SkTypeface>(set->createTypeface(j)));
359 // Fonts with lots of glyphs are interesting, but can take a long time to find
360 // the glyphs which make up the maximum extent.
361 SkTypeface* typeface = font.getTypeface();
362 if (typeface && 0 < typeface->countGlyphs() && typeface->countGlyphs() < 1000) {
363 SkColor color = boundsColors[index & 1];
364 SkRect drawBounds = show_bounds(canvas, font, x, y, color, fLabelBounds);
365 x += drawBounds.width() + 20;
366 index += 1;
367 if (x > 900) {
368 x = 0;
369 y += 160;
370 }
371 if (y >= 700) {
372 return DrawResult::kOk;
373 }
374 }
375 }
376 }
377 return DrawResult::kOk;
378 }
379
380 sk_sp<SkFontMgr> fFM;
381 const SkScalar fScaleX;
382 const SkScalar fSkewX;
383 bool fLabelBounds = false;
384 };
385
386 //////////////////////////////////////////////////////////////////////////////
387
388 DEF_GM(return new FontMgrGM;)
389 DEF_GM(return new FontMgrMatchGM;)
390 DEF_GM(return new FontMgrBoundsGM(1, 0);)
391 DEF_GM(return new FontMgrBoundsGM(0.75f, 0);)
392 DEF_GM(return new FontMgrBoundsGM(1, -0.25f);)
393