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 "base/test/fontconfig_util_linux.h"
6
7 #include <fontconfig/fontconfig.h>
8
9 #include "base/base_paths.h"
10 #include "base/environment.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/path_service.h"
16 #include "base/strings/string_util.h"
17
18 namespace base {
19
20 namespace {
21
22 const char kFontsConfTemplate[] = R"(<?xml version="1.0"?>
23 <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
24 <fontconfig>
25
26 <!-- Cache location. -->
27 <cachedir>$1</cachedir>
28
29 <!-- GCS-synced fonts. -->
30 <dir>$2</dir>
31
32 <!-- Default properties. -->
33 <match target="font">
34 <edit name="embeddedbitmap" mode="append_last">
35 <bool>false</bool>
36 </edit>
37 </match>
38
39 <!-- TODO(thomasanderson): Figure out why this is necessary. -->
40 <match target="pattern">
41 <test name="family" compare="eq">
42 <string>Tinos</string>
43 </test>
44 <test name="prgname" compare="eq">
45 <string>chromevox_tests</string>
46 </test>
47 <edit name="hintstyle" mode="assign">
48 <const>hintslight</const>
49 </edit>
50 </match>
51
52 <match target="pattern">
53 <test qual="any" name="family">
54 <string>Times</string>
55 </test>
56 <edit name="family" mode="assign">
57 <string>Tinos</string>
58 </edit>
59 </match>
60
61 <match target="pattern">
62 <test qual="any" name="family">
63 <string>sans</string>
64 </test>
65 <edit name="family" mode="assign">
66 <string>DejaVu Sans</string>
67 </edit>
68 </match>
69
70 <match target="pattern">
71 <test qual="any" name="family">
72 <string>sans serif</string>
73 </test>
74 <edit name="family" mode="assign">
75 <string>Arimo</string>
76 </edit>
77 </match>
78
79 <!-- Some layout tests specify Helvetica as a family and we need to make sure
80 that we don't fallback to Tinos for them -->
81 <match target="pattern">
82 <test qual="any" name="family">
83 <string>Helvetica</string>
84 </test>
85 <edit name="family" mode="assign">
86 <string>Arimo</string>
87 </edit>
88 </match>
89
90 <match target="pattern">
91 <test qual="any" name="family">
92 <string>sans-serif</string>
93 </test>
94 <edit name="family" mode="assign">
95 <string>Arimo</string>
96 </edit>
97 </match>
98
99 <match target="pattern">
100 <test qual="any" name="family">
101 <string>serif</string>
102 </test>
103 <edit name="family" mode="assign">
104 <string>Tinos</string>
105 </edit>
106 </match>
107
108 <match target="pattern">
109 <test qual="any" name="family">
110 <string>mono</string>
111 </test>
112 <edit name="family" mode="assign">
113 <string>Cousine</string>
114 </edit>
115 </match>
116
117 <match target="pattern">
118 <test qual="any" name="family">
119 <string>monospace</string>
120 </test>
121 <edit name="family" mode="assign">
122 <string>Cousine</string>
123 </edit>
124 </match>
125
126 <match target="pattern">
127 <test qual="any" name="family">
128 <string>Courier</string>
129 </test>
130 <edit name="family" mode="assign">
131 <string>Cousine</string>
132 </edit>
133 </match>
134
135 <match target="pattern">
136 <test qual="any" name="family">
137 <string>cursive</string>
138 </test>
139 <edit name="family" mode="assign">
140 <string>Comic Sans MS</string>
141 </edit>
142 </match>
143
144 <match target="pattern">
145 <test qual="any" name="family">
146 <string>fantasy</string>
147 </test>
148 <edit name="family" mode="assign">
149 <string>Impact</string>
150 </edit>
151 </match>
152
153 <match target="pattern">
154 <test qual="any" name="family">
155 <string>Monaco</string>
156 </test>
157 <edit name="family" mode="assign">
158 <string>Tinos</string>
159 </edit>
160 </match>
161
162 <match target="pattern">
163 <test qual="any" name="family">
164 <string>Arial</string>
165 </test>
166 <edit name="family" mode="assign">
167 <string>Arimo</string>
168 </edit>
169 </match>
170
171 <match target="pattern">
172 <test qual="any" name="family">
173 <string>Courier New</string>
174 </test>
175 <edit name="family" mode="assign">
176 <string>Cousine</string>
177 </edit>
178 </match>
179
180 <match target="pattern">
181 <test qual="any" name="family">
182 <string>Georgia</string>
183 </test>
184 <edit name="family" mode="assign">
185 <string>Gelasio</string>
186 </edit>
187 </match>
188
189 <match target="pattern">
190 <test qual="any" name="family">
191 <string>Times New Roman</string>
192 </test>
193 <edit name="family" mode="assign">
194 <string>Tinos</string>
195 </edit>
196 </match>
197
198 <match target="pattern">
199 <test qual="any" name="family">
200 <string>Verdana</string>
201 </test>
202 <!-- NOT metrically compatible! -->
203 <edit name="family" mode="assign">
204 <string>Arimo</string>
205 </edit>
206 </match>
207
208 <!-- TODO(thomasanderson): Move these configs to be test-specific. -->
209 <match target="pattern">
210 <test name="family" compare="eq">
211 <string>NonAntiAliasedSans</string>
212 </test>
213 <edit name="family" mode="assign">
214 <string>Arimo</string>
215 </edit>
216 <edit name="antialias" mode="assign">
217 <bool>false</bool>
218 </edit>
219 </match>
220
221 <match target="pattern">
222 <test name="family" compare="eq">
223 <string>SlightHintedGeorgia</string>
224 </test>
225 <edit name="family" mode="assign">
226 <string>Gelasio</string>
227 </edit>
228 <edit name="hintstyle" mode="assign">
229 <const>hintslight</const>
230 </edit>
231 </match>
232
233 <match target="pattern">
234 <test name="family" compare="eq">
235 <string>NonHintedSans</string>
236 </test>
237 <edit name="family" mode="assign">
238 <string>Arimo</string>
239 </edit>
240 <!-- These deliberately contradict each other. The 'hinting' preference
241 should take priority -->
242 <edit name="hintstyle" mode="assign">
243 <const>hintfull</const>
244 </edit>
245 <edit name="hinting" mode="assign">
246 <bool>false</bool>
247 </edit>
248 </match>
249
250 <match target="pattern">
251 <test name="family" compare="eq">
252 <string>AutohintedSerif</string>
253 </test>
254 <edit name="family" mode="assign">
255 <string>Arimo</string>
256 </edit>
257 <edit name="autohint" mode="assign">
258 <bool>true</bool>
259 </edit>
260 <edit name="hintstyle" mode="assign">
261 <const>hintmedium</const>
262 </edit>
263 </match>
264
265 <match target="pattern">
266 <test name="family" compare="eq">
267 <string>HintedSerif</string>
268 </test>
269 <edit name="family" mode="assign">
270 <string>Arimo</string>
271 </edit>
272 <edit name="autohint" mode="assign">
273 <bool>false</bool>
274 </edit>
275 <edit name="hintstyle" mode="assign">
276 <const>hintmedium</const>
277 </edit>
278 </match>
279
280 <match target="pattern">
281 <test name="family" compare="eq">
282 <string>FullAndAutoHintedSerif</string>
283 </test>
284 <edit name="family" mode="assign">
285 <string>Arimo</string>
286 </edit>
287 <edit name="autohint" mode="assign">
288 <bool>true</bool>
289 </edit>
290 <edit name="hintstyle" mode="assign">
291 <const>hintfull</const>
292 </edit>
293 </match>
294
295 <match target="pattern">
296 <test name="family" compare="eq">
297 <string>SubpixelEnabledArial</string>
298 </test>
299 <edit name="family" mode="assign">
300 <string>Arimo</string>
301 </edit>
302 <edit name="rgba" mode="assign">
303 <const>rgb</const>
304 </edit>
305 </match>
306
307 <match target="pattern">
308 <test name="family" compare="eq">
309 <string>SubpixelDisabledArial</string>
310 </test>
311 <edit name="family" mode="assign">
312 <string>Arimo</string>
313 </edit>
314 <edit name="rgba" mode="assign">
315 <const>none</const>
316 </edit>
317 </match>
318
319 <match target="pattern">
320 <!-- FontConfig doesn't currently provide a well-defined way to turn on
321 subpixel positioning. This is just an arbitrary pattern to use after
322 turning subpixel positioning on globally to ensure that we don't have
323 issues with our style getting cached for other tests. -->
324 <test name="family" compare="eq">
325 <string>SubpixelPositioning</string>
326 </test>
327 <edit name="family" mode="assign">
328 <string>Tinos</string>
329 </edit>
330 </match>
331
332 <match target="pattern">
333 <!-- See comments above -->
334 <test name="family" compare="eq">
335 <string>SubpixelPositioningAhem</string>
336 </test>
337 <edit name="family" mode="assign">
338 <string>ahem</string>
339 </edit>
340 </match>
341
342 <match target="pattern">
343 <test name="family" compare="eq">
344 <string>SlightHintedTimesNewRoman</string>
345 </test>
346 <edit name="family" mode="assign">
347 <string>Tinos</string>
348 </edit>
349 <edit name="hintstyle" mode="assign">
350 <const>hintslight</const>
351 </edit>
352 </match>
353
354 <!-- When we encounter a character that the current font doesn't
355 support, gfx::GetFallbackFontForChar() returns the first font
356 that does have a glyph for the character. The list of fonts is
357 sorted by a pattern that includes the current locale, but doesn't
358 include a font family (which means that the fallback font depends
359 on the locale but not on the current font).
360
361 DejaVu Sans is commonly the only font that supports some
362 characters, such as "⇧", and even when other candidates are
363 available, DejaVu Sans is commonly first among them, because of
364 the way Fontconfig is ordinarily configured. For example, the
365 configuration in the Fonconfig source lists DejaVu Sans under the
366 sans-serif generic family, and appends sans-serif to patterns
367 that don't already include a generic family (such as the pattern
368 in gfx::GetFallbackFontForChar()).
369
370 To get the same fallback font in the layout tests, we could
371 duplicate this configuration here, or more directly, simply
372 append DejaVu Sans to all patterns. -->
373 <match target="pattern">
374 <edit name="family" mode="append_last">
375 <string>DejaVu Sans</string>
376 </edit>
377 </match>
378
379 </fontconfig>
380 )";
381
382 } // namespace
383
SetUpFontconfig()384 void SetUpFontconfig() {
385 // TODO(thomasanderson): Use FONTCONFIG_SYSROOT to avoid having to write
386 // a new fonts.conf with updated paths.
387 std::unique_ptr<Environment> env = Environment::Create();
388 if (!env->HasVar("FONTCONFIG_FILE")) {
389 // fonts.conf must be generated on-the-fly since it contains absolute paths
390 // which may be different if
391 // 1. The user moves/renames their build directory (or any parent dirs).
392 // 2. The build directory is mapped on a swarming bot at a location
393 // different from the one the buildbot used.
394 FilePath dir_module;
395 PathService::Get(DIR_MODULE, &dir_module);
396 FilePath font_cache = dir_module.Append("fontconfig_caches");
397 FilePath test_fonts = dir_module.Append("test_fonts");
398 std::string fonts_conf = ReplaceStringPlaceholders(
399 kFontsConfTemplate, {font_cache.value(), test_fonts.value()}, nullptr);
400
401 // Write the data to a different file and then atomically rename it to
402 // fonts.conf. This avoids the file being in a bad state when different
403 // parallel tests call this function at the same time.
404 FilePath fonts_conf_file_temp;
405 if(!CreateTemporaryFileInDir(dir_module, &fonts_conf_file_temp))
406 CHECK(CreateTemporaryFile(&fonts_conf_file_temp));
407 CHECK(
408 WriteFile(fonts_conf_file_temp, fonts_conf.c_str(), fonts_conf.size()));
409 FilePath fonts_conf_file = dir_module.Append("fonts.conf");
410 if (ReplaceFile(fonts_conf_file_temp, fonts_conf_file, nullptr))
411 env->SetVar("FONTCONFIG_FILE", fonts_conf_file.value());
412 else
413 env->SetVar("FONTCONFIG_FILE", fonts_conf_file_temp.value());
414 }
415
416 CHECK(FcInit());
417 }
418
TearDownFontconfig()419 void TearDownFontconfig() {
420 FcFini();
421 }
422
423 } // namespace base
424