1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/gfx/font_render_params.h"
6
7#include "base/files/file_path.h"
8#include "base/files/scoped_temp_dir.h"
9#include "base/logging.h"
10#include "base/macros.h"
11#include "testing/gtest/include/gtest/gtest.h"
12#include "ui/gfx/font.h"
13#include "ui/gfx/linux_font_delegate.h"
14#include "ui/gfx/pango_util.h"
15#include "ui/gfx/test/fontconfig_util_linux.h"
16
17namespace gfx {
18
19namespace {
20
21// Implementation of LinuxFontDelegate that returns a canned FontRenderParams
22// struct. This is used to isolate tests from the system's local configuration.
23class TestFontDelegate : public LinuxFontDelegate {
24 public:
25  TestFontDelegate() {}
26  virtual ~TestFontDelegate() {}
27
28  void set_params(const FontRenderParams& params) { params_ = params; }
29
30  virtual FontRenderParams GetDefaultFontRenderParams() const OVERRIDE {
31    return params_;
32  }
33  virtual scoped_ptr<ScopedPangoFontDescription>
34      GetDefaultPangoFontDescription() const OVERRIDE {
35    NOTIMPLEMENTED();
36    return scoped_ptr<ScopedPangoFontDescription>();
37  }
38  virtual double GetFontDPI() const OVERRIDE {
39    NOTIMPLEMENTED();
40    return 96.0;
41  }
42
43 private:
44  FontRenderParams params_;
45
46  DISALLOW_COPY_AND_ASSIGN(TestFontDelegate);
47};
48
49// Loads the first system font defined by fontconfig_util_linux.h with a base
50// filename of |basename|. Case is ignored.
51bool LoadSystemFont(const std::string& basename) {
52  for (size_t i = 0; i < kNumSystemFontsForFontconfig; ++i) {
53    base::FilePath path(gfx::kSystemFontsForFontconfig[i]);
54    if (strcasecmp(path.BaseName().value().c_str(), basename.c_str()) == 0)
55      return LoadFontIntoFontconfig(path);
56  }
57  LOG(ERROR) << "Unable to find system font named " << basename;
58  return false;
59}
60
61}  // namespace
62
63class FontRenderParamsTest : public testing::Test {
64 public:
65  FontRenderParamsTest() {
66    SetUpFontconfig();
67    CHECK(temp_dir_.CreateUniqueTempDir());
68    original_font_delegate_ = LinuxFontDelegate::instance();
69    LinuxFontDelegate::SetInstance(&test_font_delegate_);
70    ClearFontRenderParamsCacheForTest();
71  }
72
73  virtual ~FontRenderParamsTest() {
74    LinuxFontDelegate::SetInstance(
75        const_cast<LinuxFontDelegate*>(original_font_delegate_));
76    TearDownFontconfig();
77  }
78
79 protected:
80  base::ScopedTempDir temp_dir_;
81  const LinuxFontDelegate* original_font_delegate_;
82  TestFontDelegate test_font_delegate_;
83
84 private:
85  DISALLOW_COPY_AND_ASSIGN(FontRenderParamsTest);
86};
87
88TEST_F(FontRenderParamsTest, Default) {
89  // Fontconfig needs to know about at least one font to return a match.
90  ASSERT_TRUE(LoadSystemFont("arial.ttf"));
91  ASSERT_TRUE(LoadConfigDataIntoFontconfig(temp_dir_.path(),
92      std::string(kFontconfigFileHeader) +
93      kFontconfigMatchHeader +
94      CreateFontconfigEditStanza("antialias", "bool", "true") +
95      CreateFontconfigEditStanza("autohint", "bool", "false") +
96      CreateFontconfigEditStanza("hinting", "bool", "true") +
97      CreateFontconfigEditStanza("hintstyle", "const", "hintfull") +
98      CreateFontconfigEditStanza("rgba", "const", "rgb") +
99      kFontconfigMatchFooter +
100      kFontconfigFileFooter));
101
102  FontRenderParams params = GetFontRenderParams(
103      FontRenderParamsQuery(true), NULL);
104  EXPECT_TRUE(params.antialiasing);
105  EXPECT_FALSE(params.autohinter);
106  EXPECT_TRUE(params.use_bitmaps);
107  EXPECT_EQ(FontRenderParams::HINTING_FULL, params.hinting);
108  EXPECT_FALSE(params.subpixel_positioning);
109  EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_RGB,
110            params.subpixel_rendering);
111}
112
113TEST_F(FontRenderParamsTest, Size) {
114  ASSERT_TRUE(LoadSystemFont("arial.ttf"));
115  ASSERT_TRUE(LoadConfigDataIntoFontconfig(temp_dir_.path(),
116      std::string(kFontconfigFileHeader) +
117      kFontconfigMatchHeader +
118      CreateFontconfigEditStanza("antialias", "bool", "true") +
119      CreateFontconfigEditStanza("hinting", "bool", "true") +
120      CreateFontconfigEditStanza("hintstyle", "const", "hintfull") +
121      CreateFontconfigEditStanza("rgba", "const", "none") +
122      kFontconfigMatchFooter +
123      kFontconfigMatchHeader +
124      CreateFontconfigTestStanza("pixelsize", "less_eq", "double", "10") +
125      CreateFontconfigEditStanza("antialias", "bool", "false") +
126      kFontconfigMatchFooter +
127      kFontconfigMatchHeader +
128      CreateFontconfigTestStanza("size", "more_eq", "double", "20") +
129      CreateFontconfigEditStanza("hintstyle", "const", "hintslight") +
130      CreateFontconfigEditStanza("rgba", "const", "rgb") +
131      kFontconfigMatchFooter +
132      kFontconfigFileFooter));
133
134  // The defaults should be used when the supplied size isn't matched by the
135  // second or third blocks.
136  FontRenderParamsQuery query(false);
137  query.pixel_size = 12;
138  FontRenderParams params = GetFontRenderParams(query, NULL);
139  EXPECT_TRUE(params.antialiasing);
140  EXPECT_EQ(FontRenderParams::HINTING_FULL, params.hinting);
141  EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE,
142            params.subpixel_rendering);
143
144  query.pixel_size = 10;
145  params = GetFontRenderParams(query, NULL);
146  EXPECT_FALSE(params.antialiasing);
147  EXPECT_EQ(FontRenderParams::HINTING_FULL, params.hinting);
148  EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE,
149            params.subpixel_rendering);
150
151  query.pixel_size = 0;
152  query.point_size = 20;
153  params = GetFontRenderParams(query, NULL);
154  EXPECT_TRUE(params.antialiasing);
155  EXPECT_EQ(FontRenderParams::HINTING_SLIGHT, params.hinting);
156  EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_RGB,
157            params.subpixel_rendering);
158}
159
160TEST_F(FontRenderParamsTest, Style) {
161  ASSERT_TRUE(LoadSystemFont("arial.ttf"));
162  // Load a config that disables subpixel rendering for bold text and disables
163  // hinting for italic text.
164  ASSERT_TRUE(LoadConfigDataIntoFontconfig(temp_dir_.path(),
165      std::string(kFontconfigFileHeader) +
166      kFontconfigMatchHeader +
167      CreateFontconfigEditStanza("antialias", "bool", "true") +
168      CreateFontconfigEditStanza("hinting", "bool", "true") +
169      CreateFontconfigEditStanza("hintstyle", "const", "hintslight") +
170      CreateFontconfigEditStanza("rgba", "const", "rgb") +
171      kFontconfigMatchFooter +
172      kFontconfigMatchHeader +
173      CreateFontconfigTestStanza("weight", "eq", "const", "bold") +
174      CreateFontconfigEditStanza("rgba", "const", "none") +
175      kFontconfigMatchFooter +
176      kFontconfigMatchHeader +
177      CreateFontconfigTestStanza("slant", "eq", "const", "italic") +
178      CreateFontconfigEditStanza("hinting", "bool", "false") +
179      kFontconfigMatchFooter +
180      kFontconfigFileFooter));
181
182  FontRenderParamsQuery query(false);
183  query.style = Font::NORMAL;
184  FontRenderParams params = GetFontRenderParams(query, NULL);
185  EXPECT_EQ(FontRenderParams::HINTING_SLIGHT, params.hinting);
186  EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_RGB,
187            params.subpixel_rendering);
188
189  query.style = Font::BOLD;
190  params = GetFontRenderParams(query, NULL);
191  EXPECT_EQ(FontRenderParams::HINTING_SLIGHT, params.hinting);
192  EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE,
193            params.subpixel_rendering);
194
195  query.style = Font::ITALIC;
196  params = GetFontRenderParams(query, NULL);
197  EXPECT_EQ(FontRenderParams::HINTING_NONE, params.hinting);
198  EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_RGB,
199            params.subpixel_rendering);
200
201  query.style = Font::BOLD | Font::ITALIC;
202  params = GetFontRenderParams(query, NULL);
203  EXPECT_EQ(FontRenderParams::HINTING_NONE, params.hinting);
204  EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE,
205            params.subpixel_rendering);
206}
207
208TEST_F(FontRenderParamsTest, Scalable) {
209  ASSERT_TRUE(LoadSystemFont("arial.ttf"));
210  // Load a config that only enables antialiasing for scalable fonts.
211  ASSERT_TRUE(LoadConfigDataIntoFontconfig(temp_dir_.path(),
212      std::string(kFontconfigFileHeader) +
213      kFontconfigMatchHeader +
214      CreateFontconfigEditStanza("antialias", "bool", "false") +
215      kFontconfigMatchFooter +
216      kFontconfigMatchHeader +
217      CreateFontconfigTestStanza("scalable", "eq", "bool", "true") +
218      CreateFontconfigEditStanza("antialias", "bool", "true") +
219      kFontconfigMatchFooter +
220      kFontconfigFileFooter));
221
222  // Check that we specifically ask how scalable fonts should be rendered.
223  FontRenderParams params = GetFontRenderParams(
224      FontRenderParamsQuery(false), NULL);
225  EXPECT_TRUE(params.antialiasing);
226}
227
228TEST_F(FontRenderParamsTest, UseBitmaps) {
229  ASSERT_TRUE(LoadSystemFont("arial.ttf"));
230  // Load a config that enables embedded bitmaps for fonts <= 10 pixels.
231  ASSERT_TRUE(LoadConfigDataIntoFontconfig(temp_dir_.path(),
232      std::string(kFontconfigFileHeader) +
233      kFontconfigMatchHeader +
234      CreateFontconfigEditStanza("embeddedbitmap", "bool", "false") +
235      kFontconfigMatchFooter +
236      kFontconfigMatchHeader +
237      CreateFontconfigTestStanza("pixelsize", "less_eq", "double", "10") +
238      CreateFontconfigEditStanza("embeddedbitmap", "bool", "true") +
239      kFontconfigMatchFooter +
240      kFontconfigFileFooter));
241
242  FontRenderParamsQuery query(false);
243  FontRenderParams params = GetFontRenderParams(query, NULL);
244  EXPECT_FALSE(params.use_bitmaps);
245
246  query.pixel_size = 5;
247  params = GetFontRenderParams(query, NULL);
248  EXPECT_TRUE(params.use_bitmaps);
249}
250
251TEST_F(FontRenderParamsTest, ForceFullHintingWhenAntialiasingIsDisabled) {
252  // Load a config that disables antialiasing and hinting while requesting
253  // subpixel rendering.
254  ASSERT_TRUE(LoadSystemFont("arial.ttf"));
255  ASSERT_TRUE(LoadConfigDataIntoFontconfig(temp_dir_.path(),
256      std::string(kFontconfigFileHeader) +
257      kFontconfigMatchHeader +
258      CreateFontconfigEditStanza("antialias", "bool", "false") +
259      CreateFontconfigEditStanza("hinting", "bool", "false") +
260      CreateFontconfigEditStanza("hintstyle", "const", "hintnone") +
261      CreateFontconfigEditStanza("rgba", "const", "rgb") +
262      kFontconfigMatchFooter +
263      kFontconfigFileFooter));
264
265  // Full hinting should be forced. See the comment in GetFontRenderParams() for
266  // more information.
267  FontRenderParams params = GetFontRenderParams(
268      FontRenderParamsQuery(false), NULL);
269  EXPECT_FALSE(params.antialiasing);
270  EXPECT_EQ(FontRenderParams::HINTING_FULL, params.hinting);
271  EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE,
272            params.subpixel_rendering);
273  EXPECT_FALSE(params.subpixel_positioning);
274}
275
276#if defined(OS_CHROMEOS)
277TEST_F(FontRenderParamsTest, ForceSubpixelPositioning) {
278  {
279    FontRenderParams params =
280        GetFontRenderParams(FontRenderParamsQuery(false), NULL);
281    EXPECT_TRUE(params.antialiasing);
282    EXPECT_FALSE(params.subpixel_positioning);
283    SetFontRenderParamsDeviceScaleFactor(1.0f);
284  }
285  ClearFontRenderParamsCacheForTest();
286  SetFontRenderParamsDeviceScaleFactor(1.25f);
287  // Subpixel positioning should be forced.
288  {
289    FontRenderParams params =
290        GetFontRenderParams(FontRenderParamsQuery(false), NULL);
291    EXPECT_TRUE(params.antialiasing);
292    EXPECT_TRUE(params.subpixel_positioning);
293    SetFontRenderParamsDeviceScaleFactor(1.0f);
294  }
295}
296#endif
297
298TEST_F(FontRenderParamsTest, OnlySetConfiguredValues) {
299  // Configure the LinuxFontDelegate (which queries GtkSettings on desktop
300  // Linux) to request subpixel rendering.
301  FontRenderParams system_params;
302  system_params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_RGB;
303  test_font_delegate_.set_params(system_params);
304
305  // Load a Fontconfig config that enables antialiasing but doesn't say anything
306  // about subpixel rendering.
307  ASSERT_TRUE(LoadSystemFont("arial.ttf"));
308  ASSERT_TRUE(LoadConfigDataIntoFontconfig(temp_dir_.path(),
309      std::string(kFontconfigFileHeader) +
310      kFontconfigMatchHeader +
311      CreateFontconfigEditStanza("antialias", "bool", "true") +
312      kFontconfigMatchFooter +
313      kFontconfigFileFooter));
314
315  // The subpixel rendering setting from the delegate should make it through.
316  FontRenderParams params = GetFontRenderParams(
317      FontRenderParamsQuery(false), NULL);
318  EXPECT_EQ(system_params.subpixel_rendering, params.subpixel_rendering);
319}
320
321TEST_F(FontRenderParamsTest, NoFontconfigMatch) {
322  // Don't load a Fontconfig configuration.
323  FontRenderParams system_params;
324  system_params.antialiasing = true;
325  system_params.hinting = FontRenderParams::HINTING_MEDIUM;
326  system_params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_RGB;
327  test_font_delegate_.set_params(system_params);
328
329  FontRenderParamsQuery query(false);
330  query.families.push_back("Arial");
331  query.families.push_back("Times New Roman");
332  query.pixel_size = 10;
333  std::string suggested_family;
334  FontRenderParams params = GetFontRenderParams(query, &suggested_family);
335
336  // The system params and the first requested family should be returned.
337  EXPECT_EQ(system_params.antialiasing, params.antialiasing);
338  EXPECT_EQ(system_params.hinting, params.hinting);
339  EXPECT_EQ(system_params.subpixel_rendering, params.subpixel_rendering);
340  EXPECT_EQ(query.families[0], suggested_family);
341}
342
343TEST_F(FontRenderParamsTest, MissingFamily) {
344  // With Arial and Verdana installed, request (in order) Helvetica, Arial, and
345  // Verdana and check that Arial is returned.
346  ASSERT_TRUE(LoadSystemFont("arial.ttf"));
347  ASSERT_TRUE(LoadSystemFont("verdana.ttf"));
348  FontRenderParamsQuery query(false);
349  query.families.push_back("Helvetica");
350  query.families.push_back("Arial");
351  query.families.push_back("Verdana");
352  std::string suggested_family;
353  GetFontRenderParams(query, &suggested_family);
354  EXPECT_EQ("Arial", suggested_family);
355}
356
357TEST_F(FontRenderParamsTest, SubstituteFamily) {
358  // Configure Fontconfig to use Verdana for both Helvetica and Arial.
359  ASSERT_TRUE(LoadSystemFont("arial.ttf"));
360  ASSERT_TRUE(LoadSystemFont("verdana.ttf"));
361  ASSERT_TRUE(LoadConfigDataIntoFontconfig(temp_dir_.path(),
362      std::string(kFontconfigFileHeader) +
363      CreateFontconfigAliasStanza("Helvetica", "Verdana") +
364      kFontconfigMatchHeader +
365      CreateFontconfigTestStanza("family", "eq", "string", "Arial") +
366      CreateFontconfigEditStanza("family", "string", "Verdana") +
367      kFontconfigMatchFooter +
368      kFontconfigFileFooter));
369
370  FontRenderParamsQuery query(false);
371  query.families.push_back("Helvetica");
372  std::string suggested_family;
373  GetFontRenderParams(query, &suggested_family);
374  EXPECT_EQ("Verdana", suggested_family);
375
376  query.families.clear();
377  query.families.push_back("Arial");
378  suggested_family.clear();
379  GetFontRenderParams(query, &suggested_family);
380  EXPECT_EQ("Verdana", suggested_family);
381}
382
383}  // namespace gfx
384