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.h"
9#include "sk_tool_utils.h"
10#include "SkCanvas.h"
11#include "SkFontMgr.h"
12#include "SkGraphics.h"
13#include "SkTypeface.h"
14
15// limit this just so we don't take too long to draw
16#define MAX_FAMILIES    30
17
18static SkScalar drawString(SkCanvas* canvas, const SkString& text, SkScalar x,
19                           SkScalar y, const SkPaint& paint) {
20    canvas->drawString(text, x, y, paint);
21    return x + paint.measureText(text.c_str(), text.size());
22}
23
24static SkScalar drawCharacter(SkCanvas* canvas, uint32_t character, SkScalar x,
25                              SkScalar y, SkPaint& paint, SkFontMgr* fm,
26                              const char* fontName, const char* bcp47[], int bcp47Count,
27                              const SkFontStyle& fontStyle) {
28    // find typeface containing the requested character and draw it
29    SkString ch;
30    ch.appendUnichar(character);
31    sk_sp<SkTypeface> typeface(fm->matchFamilyStyleCharacter(fontName, fontStyle,
32                                                             bcp47, bcp47Count, character));
33    paint.setTypeface(typeface);
34    x = drawString(canvas, ch, x, y, paint) + 20;
35
36    if (nullptr == typeface) {
37        return x;
38    }
39
40    // repeat the process, but this time use the family name of the typeface
41    // from the first pass.  This emulates the behavior in Blink where it
42    // it expects to get the same glyph when following this pattern.
43    SkString familyName;
44    typeface->getFamilyName(&familyName);
45    paint.setTypeface(fm->legacyMakeTypeface(familyName.c_str(), typeface->fontStyle()));
46    return drawString(canvas, ch, x, y, paint) + 20;
47}
48
49static const char* zh = "zh";
50static const char* ja = "ja";
51
52class FontMgrGM : public skiagm::GM {
53public:
54    FontMgrGM() {
55        SkGraphics::SetFontCacheLimit(16 * 1024 * 1024);
56
57        fName.set("fontmgr_iter");
58        fFM = SkFontMgr::RefDefault();
59        fName.append(sk_tool_utils::platform_font_manager());
60    }
61
62protected:
63    SkString onShortName() override {
64        return fName;
65    }
66
67    SkISize onISize() override {
68        return SkISize::Make(1536, 768);
69    }
70
71    void onDraw(SkCanvas* canvas) override {
72        SkScalar y = 20;
73        SkPaint paint;
74        paint.setAntiAlias(true);
75        paint.setLCDRenderText(true);
76        paint.setSubpixelText(true);
77        paint.setTextSize(17);
78
79        SkFontMgr* fm = fFM.get();
80        int count = SkMin32(fm->countFamilies(), MAX_FAMILIES);
81
82        for (int i = 0; i < count; ++i) {
83            SkString familyName;
84            fm->getFamilyName(i, &familyName);
85            paint.setTypeface(nullptr);
86            (void)drawString(canvas, familyName, 20, y, paint);
87
88            SkScalar x = 220;
89
90            sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
91            for (int j = 0; j < set->count(); ++j) {
92                SkString sname;
93                SkFontStyle fs;
94                set->getStyle(j, &fs, &sname);
95                sname.appendf(" [%d %d %d]", fs.weight(), fs.width(), fs.slant());
96
97                paint.setTypeface(sk_sp<SkTypeface>(set->createTypeface(j)));
98                x = drawString(canvas, sname, x, y, paint) + 20;
99
100                // check to see that we get different glyphs in japanese and chinese
101                x = drawCharacter(canvas, 0x5203, x, y, paint, fm, familyName.c_str(), &zh, 1, fs);
102                x = drawCharacter(canvas, 0x5203, x, y, paint, fm, familyName.c_str(), &ja, 1, fs);
103                // check that emoji characters are found
104                x = drawCharacter(canvas, 0x1f601, x, y, paint, fm, familyName.c_str(), nullptr,0, fs);
105            }
106            y += 24;
107        }
108    }
109
110private:
111    sk_sp<SkFontMgr> fFM;
112    SkString fName;
113    typedef GM INHERITED;
114};
115
116class FontMgrMatchGM : public skiagm::GM {
117    sk_sp<SkFontMgr> fFM;
118
119public:
120    FontMgrMatchGM() : fFM(SkFontMgr::RefDefault()) {
121        SkGraphics::SetFontCacheLimit(16 * 1024 * 1024);
122    }
123
124protected:
125    SkString onShortName() override {
126        SkString name("fontmgr_match");
127        name.append(sk_tool_utils::platform_font_manager());
128        return name;
129    }
130
131    SkISize onISize() override {
132        return SkISize::Make(640, 1024);
133    }
134
135    void iterateFamily(SkCanvas* canvas, const SkPaint& paint,
136                       SkFontStyleSet* fset) {
137        SkPaint p(paint);
138        SkScalar y = 0;
139
140        for (int j = 0; j < fset->count(); ++j) {
141            SkString sname;
142            SkFontStyle fs;
143            fset->getStyle(j, &fs, &sname);
144
145            sname.appendf(" [%d %d]", fs.weight(), fs.width());
146
147            p.setTypeface(sk_sp<SkTypeface>(fset->createTypeface(j)));
148            (void)drawString(canvas, sname, 0, y, p);
149            y += 24;
150        }
151    }
152
153    void exploreFamily(SkCanvas* canvas, const SkPaint& paint,
154                       SkFontStyleSet* fset) {
155        SkPaint p(paint);
156        SkScalar y = 0;
157
158        for (int weight = 100; weight <= 900; weight += 200) {
159            for (int width = 1; width <= 9; width += 2) {
160                SkFontStyle fs(weight, width, SkFontStyle::kUpright_Slant);
161                sk_sp<SkTypeface> face(fset->matchStyle(fs));
162                if (face) {
163                    SkString str;
164                    str.printf("request [%d %d]", fs.weight(), fs.width());
165                    p.setTypeface(std::move(face));
166                    (void)drawString(canvas, str, 0, y, p);
167                    y += 24;
168                }
169            }
170        }
171    }
172
173    void onDraw(SkCanvas* canvas) override {
174        SkPaint paint;
175        paint.setAntiAlias(true);
176        paint.setLCDRenderText(true);
177        paint.setSubpixelText(true);
178        paint.setTextSize(17);
179
180        const char* gNames[] = {
181            "Helvetica Neue", "Arial"
182        };
183
184        sk_sp<SkFontStyleSet> fset;
185        for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); ++i) {
186            fset.reset(fFM->matchFamily(gNames[i]));
187            if (fset->count() > 0) {
188                break;
189            }
190        }
191        if (nullptr == fset.get()) {
192            return;
193        }
194
195        canvas->translate(20, 40);
196        this->exploreFamily(canvas, paint, fset.get());
197        canvas->translate(150, 0);
198        this->iterateFamily(canvas, paint, fset.get());
199    }
200
201private:
202    typedef GM INHERITED;
203};
204
205class FontMgrBoundsGM : public skiagm::GM {
206public:
207    FontMgrBoundsGM(double scale, double skew)
208        : fScaleX(SkDoubleToScalar(scale))
209        , fSkewX(SkDoubleToScalar(skew))
210    {
211        fName.set("fontmgr_bounds");
212        if (scale != 1 || skew != 0) {
213            fName.appendf("_%g_%g", scale, skew);
214        }
215        fName.append(sk_tool_utils::platform_font_manager());
216        fFM = SkFontMgr::RefDefault();
217    }
218
219    static void show_bounds(SkCanvas* canvas, const SkPaint& paint, SkScalar x, SkScalar y,
220                            SkColor boundsColor)
221    {
222        SkPaint glyphPaint(paint);
223        SkRect fontBounds = glyphPaint.getFontBounds();
224        fontBounds.offset(x, y);
225        SkPaint boundsPaint(glyphPaint);
226        boundsPaint.setColor(boundsColor);
227        canvas->drawRect(fontBounds, boundsPaint);
228
229        SkPaint::FontMetrics fm;
230        glyphPaint.getFontMetrics(&fm);
231        SkPaint metricsPaint(boundsPaint);
232        metricsPaint.setStyle(SkPaint::kFill_Style);
233        metricsPaint.setAlpha(0x40);
234        if ((fm.fFlags & SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag) &&
235            (fm.fFlags & SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag))
236        {
237            SkRect underline{ fontBounds.fLeft,  fm.fUnderlinePosition+y,
238                              fontBounds.fRight, fm.fUnderlinePosition+y + fm.fUnderlineThickness };
239            canvas->drawRect(underline, metricsPaint);
240        }
241
242        if ((fm.fFlags & SkPaint::FontMetrics::kStrikeoutPositionIsValid_Flag) &&
243            (fm.fFlags & SkPaint::FontMetrics::kStrikeoutPositionIsValid_Flag))
244        {
245            SkRect strikeout{ fontBounds.fLeft,  fm.fStrikeoutPosition+y - fm.fStrikeoutThickness,
246                              fontBounds.fRight, fm.fStrikeoutPosition+y };
247            canvas->drawRect(strikeout, metricsPaint);
248        }
249
250        SkGlyphID left = 0, right = 0, top = 0, bottom = 0;
251        {
252            int numGlyphs = glyphPaint.getTypeface()->countGlyphs();
253            SkRect min = {0, 0, 0, 0};
254            glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
255            for (int i = 0; i < numGlyphs; ++i) {
256                SkGlyphID glyphId = i;
257                SkRect cur;
258                glyphPaint.measureText(&glyphId, sizeof(glyphId), &cur);
259                if (cur.fLeft   < min.fLeft  ) { min.fLeft   = cur.fLeft;   left   = i; }
260                if (cur.fTop    < min.fTop   ) { min.fTop    = cur.fTop ;   top    = i; }
261                if (min.fRight  < cur.fRight ) { min.fRight  = cur.fRight;  right  = i; }
262                if (min.fBottom < cur.fBottom) { min.fBottom = cur.fBottom; bottom = i; }
263            }
264        }
265        SkGlyphID str[] = { left, right, top, bottom };
266        for (size_t i = 0; i < SK_ARRAY_COUNT(str); ++i) {
267            canvas->drawText(&str[i], sizeof(str[0]), x, y, glyphPaint);
268        }
269    }
270
271protected:
272    SkString onShortName() override {
273        return fName;
274    }
275
276    SkISize onISize() override {
277        return SkISize::Make(1024, 850);
278    }
279
280    void onDraw(SkCanvas* canvas) override {
281        SkPaint paint;
282        paint.setAntiAlias(true);
283        paint.setSubpixelText(true);
284        paint.setTextSize(100);
285        paint.setStyle(SkPaint::kStroke_Style);
286        paint.setTextScaleX(fScaleX);
287        paint.setTextSkewX(fSkewX);
288
289        const SkColor boundsColors[2] = { SK_ColorRED, SK_ColorBLUE };
290
291        SkFontMgr* fm = fFM.get();
292        int count = SkMin32(fm->countFamilies(), 32);
293
294        int index = 0;
295        SkScalar x = 0, y = 0;
296
297        canvas->translate(10, 120);
298
299        for (int i = 0; i < count; ++i) {
300            sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
301            for (int j = 0; j < set->count() && j < 3; ++j) {
302                paint.setTypeface(sk_sp<SkTypeface>(set->createTypeface(j)));
303                // Fonts with lots of glyphs are interesting, but can take a long time to find
304                // the glyphs which make up the maximum extent.
305                if (paint.getTypeface() && paint.getTypeface()->countGlyphs() < 1000) {
306                    SkRect fontBounds = paint.getFontBounds();
307                    x -= fontBounds.fLeft;
308                    show_bounds(canvas, paint, x, y, boundsColors[index & 1]);
309                    x += fontBounds.fRight + 20;
310                    index += 1;
311                    if (x > 900) {
312                        x = 0;
313                        y += 160;
314                    }
315                    if (y >= 700) {
316                        return;
317                    }
318                }
319            }
320        }
321    }
322
323private:
324    sk_sp<SkFontMgr> fFM;
325    SkString fName;
326    SkScalar fScaleX, fSkewX;
327    typedef GM INHERITED;
328};
329
330//////////////////////////////////////////////////////////////////////////////
331
332DEF_GM(return new FontMgrGM;)
333DEF_GM(return new FontMgrMatchGM;)
334DEF_GM(return new FontMgrBoundsGM(1.0, 0);)
335DEF_GM(return new FontMgrBoundsGM(0.75, 0);)
336DEF_GM(return new FontMgrBoundsGM(1.0, -0.25);)
337