]> Git — Sourcephile - sourcephile-nix.git/blob - nixos/modules/config/fonts/fontconfig.nix
upnp: improve module
[sourcephile-nix.git] / nixos / modules / config / fonts / fontconfig.nix
1 /*
2
3 NixOS 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
10 This module generates a package containing configuration files and link it in /etc/fonts.
11
12 Fontconfig reads files in folder name / file name order, so the number prepended to the configuration file name decide the order of parsing.
13 Low number means high priority.
14
15 */
16
17 { config, pkgs, lib, ... }:
18
19 with lib;
20
21 let
22 cfg = config.fonts.fontconfig;
23
24 fcBool = x: "<bool>" + (boolToString x) + "</bool>";
25
26 # back-supported fontconfig version and package
27 # version is used for font cache generation
28 supportVersion = "210";
29 supportPkg = pkgs."fontconfig_${supportVersion}";
30
31 # latest fontconfig version and package
32 # version is used for configuration folder name, /etc/fonts/VERSION/
33 # note: format differs from supportVersion and can not be used with makeCacheConf
34 latestVersion = pkgs.fontconfig.configVersion;
35 latestPkg = pkgs.fontconfig;
36
37 # supported version fonts.conf
38 supportFontsConf = pkgs.makeFontsConf { fontconfig = supportPkg; fontDirectories = config.fonts.fonts; };
39
40 # configuration file to read fontconfig cache
41 # version dependent
42 # priority 0
43 cacheConfSupport = makeCacheConf { version = supportVersion; };
44 cacheConfLatest = makeCacheConf {};
45
46 # generate the font cache setting file for a fontconfig version
47 # use latest when no version is passed
48 # When cross-compiling, we can’t generate the cache, so we skip the
49 # <cachedir> part. fontconfig still works but is a little slower in
50 # looking things up.
51 makeCacheConf = { version ? null }:
52 let
53 fcPackage = if version == null
54 then "fontconfig"
55 else "fontconfig_${version}";
56 makeCache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.fonts; };
57 cache = makeCache pkgs.${fcPackage};
58 cache32 = makeCache pkgs.pkgsi686Linux.${fcPackage};
59 in
60 pkgs.writeText "fc-00-nixos-cache.conf" ''
61 <?xml version='1.0'?>
62 <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
63 <fontconfig>
64 <!-- Font directories -->
65 ${concatStringsSep "\n" (map (font: "<dir>${font}</dir>") config.fonts.fonts)}
66 ${optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
67 <!-- Pre-generated font caches -->
68 <cachedir>${cache}</cachedir>
69 ${optionalString (pkgs.stdenv.isx86_64 && cfg.cache32Bit) ''
70 <cachedir>${cache32}</cachedir>
71 ''}
72 ''}
73 </fontconfig>
74 '';
75
76 # rendering settings configuration file
77 # priority 10
78 renderConf = pkgs.writeText "fc-10-nixos-rendering.conf" ''
79 <?xml version='1.0'?>
80 <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
81 <fontconfig>
82
83 <!-- Default rendering settings -->
84 <match target="pattern">
85 <edit mode="append" name="hinting">
86 ${fcBool cfg.hinting.enable}
87 </edit>
88 <edit mode="append" name="autohint">
89 ${fcBool cfg.hinting.autohint}
90 </edit>
91 <edit mode="append" name="hintstyle">
92 <const>hintslight</const>
93 </edit>
94 <edit mode="append" name="antialias">
95 ${fcBool cfg.antialias}
96 </edit>
97 <edit mode="append" name="rgba">
98 <const>${cfg.subpixel.rgba}</const>
99 </edit>
100 <edit mode="append" name="lcdfilter">
101 <const>lcd${cfg.subpixel.lcdfilter}</const>
102 </edit>
103 </match>
104
105 ${optionalString (cfg.dpi != 0) ''
106 <match target="pattern">
107 <edit name="dpi" mode="assign">
108 <double>${toString cfg.dpi}</double>
109 </edit>
110 </match>
111 ''}
112
113 </fontconfig>
114 '';
115
116 # local configuration file
117 localConf = pkgs.writeText "fc-local.conf" cfg.localConf;
118
119 # default fonts configuration file
120 # priority 52
121 defaultFontsConf =
122 let genDefault = fonts: name:
123 optionalString (fonts != []) ''
124 <alias binding="same">
125 <family>${name}</family>
126 <prefer>
127 ${concatStringsSep ""
128 (map (font: ''
129 <family>${font}</family>
130 '') fonts)}
131 </prefer>
132 </alias>
133 '';
134 in
135 pkgs.writeText "fc-52-nixos-default-fonts.conf" ''
136 <?xml version='1.0'?>
137 <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
138 <fontconfig>
139
140 <!-- Default fonts -->
141 ${genDefault cfg.defaultFonts.sansSerif "sans-serif"}
142
143 ${genDefault cfg.defaultFonts.serif "serif"}
144
145 ${genDefault cfg.defaultFonts.monospace "monospace"}
146
147 ${genDefault cfg.defaultFonts.emoji "emoji"}
148
149 </fontconfig>
150 '';
151
152 # bitmap font options
153 # priority 53
154 rejectBitmaps = pkgs.writeText "fc-53-no-bitmaps.conf" ''
155 <?xml version="1.0"?>
156 <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
157 <fontconfig>
158
159 ${optionalString (!cfg.allowBitmaps) ''
160 <!-- Reject bitmap fonts -->
161 <selectfont>
162 <rejectfont>
163 <pattern>
164 <patelt name="scalable"><bool>false</bool></patelt>
165 </pattern>
166 </rejectfont>
167 </selectfont>
168 ''}
169
170 <!-- Use embedded bitmaps in fonts like Calibri? -->
171 <match target="font">
172 <edit name="embeddedbitmap" mode="assign">
173 ${fcBool cfg.useEmbeddedBitmaps}
174 </edit>
175 </match>
176
177 </fontconfig>
178 '';
179
180 # reject Type 1 fonts
181 # priority 53
182 rejectType1 = pkgs.writeText "fc-53-nixos-reject-type1.conf" ''
183 <?xml version="1.0"?>
184 <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
185 <fontconfig>
186
187 <!-- Reject Type 1 fonts -->
188 <selectfont>
189 <rejectfont>
190 <pattern>
191 <patelt name="fontformat"><string>Type 1</string></patelt>
192 </pattern>
193 </rejectfont>
194 </selectfont>
195
196 </fontconfig>
197 '';
198
199 # fontconfig configuration package
200 confPkg = pkgs.runCommand "fontconfig-conf" {
201 preferLocalBuild = true;
202 } ''
203 support_folder=$out/etc/fonts/conf.d
204 latest_folder=$out/etc/fonts/${latestVersion}/conf.d
205
206 mkdir -p $support_folder
207 mkdir -p $latest_folder
208
209 # fonts.conf
210 ln -s ${supportFontsConf} $support_folder/../fonts.conf
211 ln -s ${latestPkg.out}/etc/fonts/fonts.conf \
212 $latest_folder/../fonts.conf
213
214 # fontconfig default config files
215 ln -s ${supportPkg.out}/etc/fonts/conf.d/*.conf \
216 $support_folder/
217 ln -s ${latestPkg.out}/etc/fonts/conf.d/*.conf \
218 $latest_folder/
219
220 # update latest 51-local.conf path to look at the latest local.conf
221 rm $latest_folder/51-local.conf
222
223 substitute ${latestPkg.out}/etc/fonts/conf.d/51-local.conf \
224 $latest_folder/51-local.conf \
225 --replace local.conf /etc/fonts/${latestVersion}/local.conf
226
227 # 00-nixos-cache.conf
228 ln -s ${cacheConfSupport} \
229 $support_folder/00-nixos-cache.conf
230 ln -s ${cacheConfLatest} $latest_folder/00-nixos-cache.conf
231
232 # 10-nixos-rendering.conf
233 ln -s ${renderConf} $support_folder/10-nixos-rendering.conf
234 ln -s ${renderConf} $latest_folder/10-nixos-rendering.conf
235
236 # 50-user.conf
237 ${optionalString (!cfg.includeUserConf) ''
238 rm $support_folder/50-user.conf
239 rm $latest_folder/50-user.conf
240 ''}
241
242 # local.conf (indirect priority 51)
243 ${optionalString (cfg.localConf != "") ''
244 ln -s ${localConf} $support_folder/../local.conf
245 ln -s ${localConf} $latest_folder/../local.conf
246 ''}
247
248 # 52-nixos-default-fonts.conf
249 ln -s ${defaultFontsConf} $support_folder/52-nixos-default-fonts.conf
250 ln -s ${defaultFontsConf} $latest_folder/52-nixos-default-fonts.conf
251
252 # 53-no-bitmaps.conf
253 ln -s ${rejectBitmaps} $support_folder/53-no-bitmaps.conf
254 ln -s ${rejectBitmaps} $latest_folder/53-no-bitmaps.conf
255
256 ${optionalString (!cfg.allowType1) ''
257 # 53-nixos-reject-type1.conf
258 ln -s ${rejectType1} $support_folder/53-nixos-reject-type1.conf
259 ln -s ${rejectType1} $latest_folder/53-nixos-reject-type1.conf
260 ''}
261 '';
262
263 # Package with configuration files
264 # this merge all the packages in the fonts.fontconfig.confPackages list
265 fontconfigEtc = pkgs.buildEnv {
266 name = "fontconfig-etc";
267 paths = cfg.confPackages;
268 ignoreCollisions = true;
269 };
270 in
271 {
272 imports = [
273 (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "allowBitmaps" ] [ "fonts" "fontconfig" "allowBitmaps" ])
274 (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "allowType1" ] [ "fonts" "fontconfig" "allowType1" ])
275 (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "useEmbeddedBitmaps" ] [ "fonts" "fontconfig" "useEmbeddedBitmaps" ])
276 (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "forceAutohint" ] [ "fonts" "fontconfig" "forceAutohint" ])
277 (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "renderMonoTTFAsBitmap" ] [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ])
278 (mkRemovedOptionModule [ "fonts" "fontconfig" "hinting" "style" ] "")
279 (mkRemovedOptionModule [ "fonts" "fontconfig" "forceAutohint" ] "")
280 (mkRemovedOptionModule [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ] "")
281 ] ++ lib.forEach [ "enable" "substitutions" "preset" ]
282 (opt: lib.mkRemovedOptionModule [ "fonts" "fontconfig" "ultimate" "${opt}" ] ''
283 The fonts.fontconfig.ultimate module and configuration is obsolete.
284 The repository has since been archived and activity has ceased.
285 https://github.com/bohoomil/fontconfig-ultimate/issues/171.
286 No action should be needed for font configuration, as the fonts.fontconfig
287 module is already used by default.
288 '');
289
290 options = {
291
292 fonts = {
293
294 fontconfig = {
295 enable = mkOption {
296 type = types.bool;
297 default = true;
298 description = ''
299 If enabled, a Fontconfig configuration file will be built
300 pointing to a set of default fonts. If you don't care about
301 running X11 applications or any other program that uses
302 Fontconfig, you can turn this option off and prevent a
303 dependency on all those fonts.
304 '';
305 };
306
307 confPackages = mkOption {
308 internal = true;
309 type = with types; listOf path;
310 default = [ ];
311 description = ''
312 Fontconfig configuration packages.
313 '';
314 };
315
316 antialias = mkOption {
317 type = types.bool;
318 default = true;
319 description = ''
320 Enable font antialiasing. At high resolution (> 200 DPI),
321 antialiasing has no visible effect; users of such displays may want
322 to disable this option.
323 '';
324 };
325
326 dpi = mkOption {
327 type = types.int;
328 default = 0;
329 description = ''
330 Force DPI setting. Setting to <literal>0</literal> disables DPI
331 forcing; the DPI detected for the display will be used.
332 '';
333 };
334
335 localConf = mkOption {
336 type = types.lines;
337 default = "";
338 description = ''
339 System-wide customization file contents, has higher priority than
340 <literal>defaultFonts</literal> settings.
341 '';
342 };
343
344 defaultFonts = {
345 monospace = mkOption {
346 type = types.listOf types.str;
347 default = ["DejaVu Sans Mono"];
348 description = ''
349 System-wide default monospace font(s). Multiple fonts may be
350 listed in case multiple languages must be supported.
351 '';
352 };
353
354 sansSerif = mkOption {
355 type = types.listOf types.str;
356 default = ["DejaVu Sans"];
357 description = ''
358 System-wide default sans serif font(s). Multiple fonts may be
359 listed in case multiple languages must be supported.
360 '';
361 };
362
363 serif = mkOption {
364 type = types.listOf types.str;
365 default = ["DejaVu Serif"];
366 description = ''
367 System-wide default serif font(s). Multiple fonts may be listed
368 in case multiple languages must be supported.
369 '';
370 };
371
372 emoji = mkOption {
373 type = types.listOf types.str;
374 default = ["Noto Color Emoji"];
375 description = ''
376 System-wide default emoji font(s). Multiple fonts may be listed
377 in case a font does not support all emoji.
378
379 Note that fontconfig matches color emoji fonts preferentially,
380 so if you want to use a black and white font while having
381 a color font installed (eg. Noto Color Emoji installed alongside
382 Noto Emoji), fontconfig will still choose the color font even
383 when it is later in the list.
384 '';
385 };
386 };
387
388 hinting = {
389 enable = mkOption {
390 type = types.bool;
391 default = true;
392 description = ''
393 Enable font hinting. Hinting aligns glyphs to pixel boundaries to
394 improve rendering sharpness at low resolution. At high resolution
395 (> 200 dpi) hinting will do nothing (at best); users of such
396 displays may want to disable this option.
397 '';
398 };
399
400 autohint = mkOption {
401 type = types.bool;
402 default = false;
403 description = ''
404 Enable the autohinter in place of the default interpreter.
405 The results are usually lower quality than correctly-hinted
406 fonts, but better than unhinted fonts.
407 '';
408 };
409 };
410
411 includeUserConf = mkOption {
412 type = types.bool;
413 default = true;
414 description = ''
415 Include the user configuration from
416 <filename>~/.config/fontconfig/fonts.conf</filename> or
417 <filename>~/.config/fontconfig/conf.d</filename>.
418 '';
419 };
420
421 subpixel = {
422
423 rgba = mkOption {
424 default = "rgb";
425 type = types.enum ["rgb" "bgr" "vrgb" "vbgr" "none"];
426 description = ''
427 Subpixel order. The overwhelming majority of displays are
428 <literal>rgb</literal> in their normal orientation. Select
429 <literal>vrgb</literal> for mounting such a display 90 degrees
430 clockwise from its normal orientation or <literal>vbgr</literal>
431 for mounting 90 degrees counter-clockwise. Select
432 <literal>bgr</literal> in the unlikely event of mounting 180
433 degrees from the normal orientation. Reverse these directions in
434 the improbable event that the display's native subpixel order is
435 <literal>bgr</literal>.
436 '';
437 };
438
439 lcdfilter = mkOption {
440 default = "default";
441 type = types.enum ["none" "default" "light" "legacy"];
442 description = ''
443 FreeType LCD filter. At high resolution (> 200 DPI), LCD filtering
444 has no visible effect; users of such displays may want to select
445 <literal>none</literal>.
446 '';
447 };
448
449 };
450
451 cache32Bit = mkOption {
452 default = false;
453 type = types.bool;
454 description = ''
455 Generate system fonts cache for 32-bit applications.
456 '';
457 };
458
459 allowBitmaps = mkOption {
460 type = types.bool;
461 default = true;
462 description = ''
463 Allow bitmap fonts. Set to <literal>false</literal> to ban all
464 bitmap fonts.
465 '';
466 };
467
468 allowType1 = mkOption {
469 type = types.bool;
470 default = false;
471 description = ''
472 Allow Type-1 fonts. Default is <literal>false</literal> because of
473 poor rendering.
474 '';
475 };
476
477 useEmbeddedBitmaps = mkOption {
478 type = types.bool;
479 default = false;
480 description = ''Use embedded bitmaps in fonts like Calibri.'';
481 };
482
483 };
484
485 };
486
487 };
488 config = mkMerge [
489 (mkIf cfg.enable {
490 environment.systemPackages = [ pkgs.fontconfig ];
491 environment.etc.fonts.source = "${fontconfigEtc}/etc/fonts/";
492 security.apparmor.includes."abstractions/fonts" = ''
493 # fonts.conf
494 r ${supportFontsConf}
495 r ${latestPkg.out}/etc/fonts/fonts.conf
496
497 # fontconfig default config files
498 r ${supportPkg.out}/etc/fonts/conf.d/*.conf,
499 r ${latestPkg.out}/etc/fonts/conf.d/*.conf,
500
501 substitute ${latestPkg.out}/etc/fonts/conf.d/51-local.conf \
502 $latest_folder/51-local.conf \
503 --replace local.conf /etc/fonts/${latestVersion}/local.conf
504
505 # 00-nixos-cache.conf
506 r ${cacheConfSupport},
507 r ${cacheConfLatest},
508
509 # 10-nixos-rendering.conf
510 r ${renderConf},
511
512 # local.conf (indirect priority 51)
513 ${optionalString (cfg.localConf != "") ''
514 r ${localConf},
515 ''}
516
517 # 52-nixos-default-fonts.conf
518 r ${defaultFontsConf},
519
520 # 53-no-bitmaps.conf
521 r ${rejectBitmaps},
522
523 ${optionalString (!cfg.allowType1) ''
524 # 53-nixos-reject-type1.conf
525 r ${rejectType1},
526 ''}
527 '';
528 })
529 (mkIf (cfg.enable && !cfg.penultimate.enable) {
530 fonts.fontconfig.confPackages = [ confPkg ];
531 })
532 ];
533
534 }