1/*
2
3NixOS support 2 fontconfig versions, "support" and "latest".
4
5- "latest" refers to default fontconfig package (pkgs.fontconfig).
6 configuration files are linked to /etc/fonts/VERSION/conf.d/
7- "support" refers to supportPkg (pkgs."fontconfig_${supportVersion}").
8 configuration files are linked to /etc/fonts/conf.d/
9
10This module generates a package containing configuration files and link it in /etc/fonts.
11
12Fontconfig reads files in folder name / file name order, so the number prepended to the configuration file name decide the order of parsing.
13Low number means high priority.
14
15*/
16
17{ config, lib, pkgs, ... }:
18
19with lib;
20
21let cfg = config.fonts.fontconfig;
22
23 fcBool = x: "<bool>" + (if x then "true" else "false") + "</bool>";
24
25 # back-supported fontconfig version and package
26 # version is used for font cache generation
27 supportVersion = "210";
28 supportPkg = pkgs."fontconfig_${supportVersion}";
29
30 # latest fontconfig version and package
31 # version is used for configuration folder name, /etc/fonts/VERSION/
32 # note: format differs from supportVersion and can not be used with makeCacheConf
33 latestVersion = pkgs.fontconfig.configVersion;
34 latestPkg = pkgs.fontconfig;
35
36 # supported version fonts.conf
37 supportFontsConf = pkgs.makeFontsConf { fontconfig = supportPkg; fontDirectories = config.fonts.fonts; };
38
39 # configuration file to read fontconfig cache
40 # version dependent
41 # priority 0
42 cacheConfSupport = makeCacheConf { version = supportVersion; };
43 cacheConfLatest = makeCacheConf {};
44
45 # generate the font cache setting file for a fontconfig version
46 # use latest when no version is passed
47 makeCacheConf = { version ? null }:
48 let
49 fcPackage = if builtins.isNull version
50 then "fontconfig"
51 else "fontconfig_${version}";
52 makeCache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.fonts; };
53 cache = makeCache pkgs."${fcPackage}";
54 cache32 = makeCache pkgs.pkgsi686Linux."${fcPackage}";
55 in
56 pkgs.writeText "fc-00-nixos-cache.conf" ''
57 <?xml version='1.0'?>
58 <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
59 <fontconfig>
60 <!-- Font directories -->
61 ${concatStringsSep "\n" (map (font: "<dir>${font}</dir>") config.fonts.fonts)}
62 <!-- Pre-generated font caches -->
63 <cachedir>${cache}</cachedir>
64 ${optionalString (pkgs.stdenv.isx86_64 && cfg.cache32Bit) ''
65 <cachedir>${cache32}</cachedir>
66 ''}
67 </fontconfig>
68 '';
69
70 # rendering settings configuration file
71 # priority 10
72 renderConf = pkgs.writeText "fc-10-nixos-rendering.conf" ''
73 <?xml version='1.0'?>
74 <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
75 <fontconfig>
76
77 <!-- Default rendering settings -->
78 <match target="font">
79 <edit mode="assign" name="hinting">
80 ${fcBool cfg.hinting.enable}
81 </edit>
82 <edit mode="assign" name="autohint">
83 ${fcBool cfg.hinting.autohint}
84 </edit>
85 <edit mode="assign" name="hintstyle">
86 <const>hint${cfg.hinting.style}</const>
87 </edit>
88 <edit mode="assign" name="antialias">
89 ${fcBool cfg.antialias}
90 </edit>
91 <edit mode="assign" name="rgba">
92 <const>${cfg.subpixel.rgba}</const>
93 </edit>
94 <edit mode="assign" name="lcdfilter">
95 <const>lcd${cfg.subpixel.lcdfilter}</const>
96 </edit>
97 </match>
98
99 ${optionalString (cfg.dpi != 0) ''
100 <match target="pattern">
101 <edit name="dpi" mode="assign">
102 <double>${toString cfg.dpi}</double>
103 </edit>
104 </match>
105 ''}
106
107 </fontconfig>
108 '';
109
110 # local configuration file
111 # priority 51
112 localConf = pkgs.writeText "fc-local.conf" cfg.localConf;
113
114 # default fonts configuration file
115 # priority 52
116 defaultFontsConf =
117 let genDefault = fonts: name:
118 optionalString (fonts != []) ''
119 <alias>
120 <family>${name}</family>
121 <prefer>
122 ${concatStringsSep ""
123 (map (font: ''
124 <family>${font}</family>
125 '') fonts)}
126 </prefer>
127 </alias>
128 '';
129 in
130 pkgs.writeText "fc-52-nixos-default-fonts.conf" ''
131 <?xml version='1.0'?>
132 <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
133 <fontconfig>
134
135 <!-- Default fonts -->
136 ${genDefault cfg.defaultFonts.sansSerif "sans-serif"}
137
138 ${genDefault cfg.defaultFonts.serif "serif"}
139
140 ${genDefault cfg.defaultFonts.monospace "monospace"}
141
142 </fontconfig>
143 '';
144
145 # fontconfig configuration package
146 confPkg = pkgs.runCommand "fontconfig-conf" {} ''
147 support_folder=$out/etc/fonts
148 latest_folder=$out/etc/fonts/${latestVersion}
149
150 mkdir -p $support_folder/conf.d
151 mkdir -p $latest_folder/conf.d
152
153 # fonts.conf
154 ln -s ${supportFontsConf} $support_folder/fonts.conf
155 ln -s ${latestPkg.out}/etc/fonts/fonts.conf \
156 $latest_folder/fonts.conf
157
158 # fontconfig default config files
159 ln -s ${supportPkg.out}/etc/fonts/conf.d/*.conf \
160 $support_folder/conf.d/
161 ln -s ${latestPkg.out}/etc/fonts/conf.d/*.conf \
162 $latest_folder/conf.d/
163
164 # update latest 51-local.conf path to look at the latest local.conf
165 rm $latest_folder/conf.d/51-local.conf
166
167 substitute ${latestPkg.out}/etc/fonts/conf.d/51-local.conf \
168 $latest_folder/conf.d/51-local.conf \
169 --replace local.conf /etc/fonts/${latestVersion}/local.conf
170
171 # 00-nixos-cache.conf
172 ln -s ${cacheConfSupport} \
173 $support_folder/conf.d/00-nixos-cache.conf
174 ln -s ${cacheConfLatest} $latest_folder/conf.d/00-nixos-cache.conf
175
176 # 10-nixos-rendering.conf
177 ln -s ${renderConf} $support_folder/conf.d/10-nixos-rendering.conf
178 ln -s ${renderConf} $latest_folder/conf.d/10-nixos-rendering.conf
179
180 # 50-user.conf
181 ${optionalString (! cfg.includeUserConf) ''
182 rm $support_folder/conf.d/50-user.conf
183 rm $latest_folder/conf.d/50-user.conf
184 ''}
185
186 # local.conf (indirect priority 51)
187 ${optionalString (cfg.localConf != "") ''
188 ln -s ${localConf} $support_folder/local.conf
189 ln -s ${localConf} $latest_folder/local.conf
190 ''}
191
192 # 52-nixos-default-fonts.conf
193 ln -s ${defaultFontsConf} $support_folder/conf.d/52-nixos-default-fonts.conf
194 ln -s ${defaultFontsConf} $latest_folder/conf.d/52-nixos-default-fonts.conf
195 '';
196
197 # Package with configuration files
198 # this merge all the packages in the fonts.fontconfig.confPackages list
199 fontconfigEtc = pkgs.buildEnv {
200 name = "fontconfig-etc";
201 paths = cfg.confPackages;
202 ignoreCollisions = true;
203 };
204in
205{
206
207 options = {
208
209 fonts = {
210
211 fontconfig = {
212 enable = mkOption {
213 type = types.bool;
214 default = true;
215 description = ''
216 If enabled, a Fontconfig configuration file will be built
217 pointing to a set of default fonts. If you don't care about
218 running X11 applications or any other program that uses
219 Fontconfig, you can turn this option off and prevent a
220 dependency on all those fonts.
221 '';
222 };
223
224 confPackages = mkOption {
225 internal = true;
226 type = with types; listOf path;
227 default = [ ];
228 description = ''
229 Fontconfig configuration packages.
230 '';
231 };
232
233 antialias = mkOption {
234 type = types.bool;
235 default = true;
236 description = "Enable font antialiasing.";
237 };
238
239 dpi = mkOption {
240 type = types.int;
241 default = 0;
242 description = ''
243 Force DPI setting. Setting to <literal>0</literal> disables DPI
244 forcing; the DPI detected for the display will be used.
245 '';
246 };
247
248 localConf = mkOption {
249 type = types.lines;
250 default = "";
251 description = ''
252 System-wide customization file contents, has higher priority than
253 <literal>defaultFonts</literal> settings.
254 '';
255 };
256
257 defaultFonts = {
258 monospace = mkOption {
259 type = types.listOf types.str;
260 default = ["DejaVu Sans Mono"];
261 description = ''
262 System-wide default monospace font(s). Multiple fonts may be
263 listed in case multiple languages must be supported.
264 '';
265 };
266
267 sansSerif = mkOption {
268 type = types.listOf types.str;
269 default = ["DejaVu Sans"];
270 description = ''
271 System-wide default sans serif font(s). Multiple fonts may be
272 listed in case multiple languages must be supported.
273 '';
274 };
275
276 serif = mkOption {
277 type = types.listOf types.str;
278 default = ["DejaVu Serif"];
279 description = ''
280 System-wide default serif font(s). Multiple fonts may be listed
281 in case multiple languages must be supported.
282 '';
283 };
284 };
285
286 hinting = {
287 enable = mkOption {
288 type = types.bool;
289 default = true;
290 description = "Enable TrueType hinting.";
291 };
292
293 autohint = mkOption {
294 type = types.bool;
295 default = true;
296 description = ''
297 Enable the autohinter, which provides hinting for otherwise
298 un-hinted fonts. The results are usually lower quality than
299 correctly-hinted fonts.
300 '';
301 };
302
303 style = mkOption {
304 type = types.str // {
305 check = flip elem ["none" "slight" "medium" "full"];
306 };
307 default = "full";
308 description = ''
309 TrueType hinting style, one of <literal>none</literal>,
310 <literal>slight</literal>, <literal>medium</literal>, or
311 <literal>full</literal>.
312 '';
313 };
314 };
315
316 includeUserConf = mkOption {
317 type = types.bool;
318 default = true;
319 description = ''
320 Include the user configuration from
321 <filename>~/.config/fontconfig/fonts.conf</filename> or
322 <filename>~/.config/fontconfig/conf.d</filename>.
323 '';
324 };
325
326 subpixel = {
327
328 rgba = mkOption {
329 default = "rgb";
330 type = types.enum ["rgb" "bgr" "vrgb" "vbgr" "none"];
331 description = ''
332 Subpixel order, one of <literal>none</literal>,
333 <literal>rgb</literal>, <literal>bgr</literal>,
334 <literal>vrgb</literal>, or <literal>vbgr</literal>.
335 '';
336 };
337
338 lcdfilter = mkOption {
339 default = "default";
340 type = types.enum ["none" "default" "light" "legacy"];
341 description = ''
342 FreeType LCD filter, one of <literal>none</literal>,
343 <literal>default</literal>, <literal>light</literal>, or
344 <literal>legacy</literal>.
345 '';
346 };
347
348 };
349
350 cache32Bit = mkOption {
351 default = false;
352 type = types.bool;
353 description = ''
354 Generate system fonts cache for 32-bit applications.
355 '';
356 };
357
358 };
359
360 };
361
362 };
363 config = mkIf cfg.enable {
364 fonts.fontconfig.confPackages = [ confPkg ];
365
366 environment.systemPackages = [ pkgs.fontconfig ];
367 environment.etc.fonts.source = "${fontconfigEtc}/etc/fonts/";
368 };
369
370}