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