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