/* Configuration files are linked to /etc/fonts/conf.d/ This module generates a package containing configuration files and link it in /etc/fonts. Fontconfig reads files in folder name / file name order, so the number prepended to the configuration file name decide the order of parsing. Low number means high priority. */ { config, pkgs, lib, ... }: with lib; let cfg = config.fonts.fontconfig; fcBool = x: "" + (boolToString x) + ""; pkg = pkgs.fontconfig; # configuration file to read fontconfig cache # priority 0 cacheConf = makeCacheConf {}; # generate the font cache setting file # When cross-compiling, we can’t generate the cache, so we skip the # part. fontconfig still works but is a little slower in # looking things up. makeCacheConf = { }: let makeCache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.fonts; }; cache = makeCache pkgs.fontconfig; cache32 = makeCache pkgs.pkgsi686Linux.fontconfig; in pkgs.writeText "fc-00-nixos-cache.conf" '' ${concatStringsSep "\n" (map (font: "${font}") config.fonts.fonts)} ${optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) '' ${cache} ${optionalString (pkgs.stdenv.isx86_64 && cfg.cache32Bit) '' ${cache32} ''} ''} ''; # rendering settings configuration file # priority 10 renderConf = pkgs.writeText "fc-10-nixos-rendering.conf" '' ${fcBool cfg.hinting.enable} ${fcBool cfg.hinting.autohint} hintslight ${fcBool cfg.antialias} ${cfg.subpixel.rgba} lcd${cfg.subpixel.lcdfilter} ${optionalString (cfg.dpi != 0) '' ${toString cfg.dpi} ''} ''; # local configuration file localConf = pkgs.writeText "fc-local.conf" cfg.localConf; # default fonts configuration file # priority 52 defaultFontsConf = let genDefault = fonts: name: optionalString (fonts != []) '' ${name} ${concatStringsSep "" (map (font: '' ${font} '') fonts)} ''; in pkgs.writeText "fc-52-nixos-default-fonts.conf" '' ${genDefault cfg.defaultFonts.sansSerif "sans-serif"} ${genDefault cfg.defaultFonts.serif "serif"} ${genDefault cfg.defaultFonts.monospace "monospace"} ${genDefault cfg.defaultFonts.emoji "emoji"} ''; # bitmap font options # priority 53 rejectBitmaps = pkgs.writeText "fc-53-no-bitmaps.conf" '' ${optionalString (!cfg.allowBitmaps) '' false ''} ${fcBool cfg.useEmbeddedBitmaps} ''; # reject Type 1 fonts # priority 53 rejectType1 = pkgs.writeText "fc-53-nixos-reject-type1.conf" '' Type 1 ''; # fontconfig configuration package confPkg = pkgs.runCommand "fontconfig-conf" { preferLocalBuild = true; } '' dst=$out/etc/fonts/conf.d mkdir -p $dst # fonts.conf ln -s ${pkg.out}/etc/fonts/fonts.conf \ $dst/../fonts.conf # TODO: remove this legacy symlink once people stop using packages built before #95358 was merged mkdir -p $out/etc/fonts/2.11 ln -s /etc/fonts/fonts.conf \ $out/etc/fonts/2.11/fonts.conf # fontconfig default config files ln -s ${pkg.out}/etc/fonts/conf.d/*.conf \ $dst/ # 00-nixos-cache.conf ln -s ${cacheConf} $dst/00-nixos-cache.conf # 10-nixos-rendering.conf ln -s ${renderConf} $dst/10-nixos-rendering.conf # 50-user.conf ${optionalString (!cfg.includeUserConf) '' rm $dst/50-user.conf ''} # local.conf (indirect priority 51) ${optionalString (cfg.localConf != "") '' ln -s ${localConf} $dst/../local.conf ''} # 52-nixos-default-fonts.conf ln -s ${defaultFontsConf} $dst/52-nixos-default-fonts.conf # 53-no-bitmaps.conf ln -s ${rejectBitmaps} $dst/53-no-bitmaps.conf ${optionalString (!cfg.allowType1) '' # 53-nixos-reject-type1.conf ln -s ${rejectType1} $dst/53-nixos-reject-type1.conf ''} ''; # Package with configuration files # this merge all the packages in the fonts.fontconfig.confPackages list fontconfigEtc = pkgs.buildEnv { name = "fontconfig-etc"; paths = cfg.confPackages; ignoreCollisions = true; }; in { imports = [ (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "allowBitmaps" ] [ "fonts" "fontconfig" "allowBitmaps" ]) (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "allowType1" ] [ "fonts" "fontconfig" "allowType1" ]) (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "useEmbeddedBitmaps" ] [ "fonts" "fontconfig" "useEmbeddedBitmaps" ]) (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "forceAutohint" ] [ "fonts" "fontconfig" "forceAutohint" ]) (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "renderMonoTTFAsBitmap" ] [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ]) (mkRemovedOptionModule [ "fonts" "fontconfig" "hinting" "style" ] "") (mkRemovedOptionModule [ "fonts" "fontconfig" "forceAutohint" ] "") (mkRemovedOptionModule [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ] "") ] ++ lib.forEach [ "enable" "substitutions" "preset" ] (opt: lib.mkRemovedOptionModule [ "fonts" "fontconfig" "ultimate" "${opt}" ] '' The fonts.fontconfig.ultimate module and configuration is obsolete. The repository has since been archived and activity has ceased. https://github.com/bohoomil/fontconfig-ultimate/issues/171. No action should be needed for font configuration, as the fonts.fontconfig module is already used by default. ''); options = { fonts = { fontconfig = { enable = mkOption { type = types.bool; default = true; description = '' If enabled, a Fontconfig configuration file will be built pointing to a set of default fonts. If you don't care about running X11 applications or any other program that uses Fontconfig, you can turn this option off and prevent a dependency on all those fonts. ''; }; confPackages = mkOption { internal = true; type = with types; listOf path; default = [ ]; description = '' Fontconfig configuration packages. ''; }; antialias = mkOption { type = types.bool; default = true; description = '' Enable font antialiasing. At high resolution (> 200 DPI), antialiasing has no visible effect; users of such displays may want to disable this option. ''; }; dpi = mkOption { type = types.int; default = 0; description = '' Force DPI setting. Setting to 0 disables DPI forcing; the DPI detected for the display will be used. ''; }; localConf = mkOption { type = types.lines; default = ""; description = '' System-wide customization file contents, has higher priority than defaultFonts settings. ''; }; defaultFonts = { monospace = mkOption { type = types.listOf types.str; default = ["DejaVu Sans Mono"]; description = '' System-wide default monospace font(s). Multiple fonts may be listed in case multiple languages must be supported. ''; }; sansSerif = mkOption { type = types.listOf types.str; default = ["DejaVu Sans"]; description = '' System-wide default sans serif font(s). Multiple fonts may be listed in case multiple languages must be supported. ''; }; serif = mkOption { type = types.listOf types.str; default = ["DejaVu Serif"]; description = '' System-wide default serif font(s). Multiple fonts may be listed in case multiple languages must be supported. ''; }; emoji = mkOption { type = types.listOf types.str; default = ["Noto Color Emoji"]; description = '' System-wide default emoji font(s). Multiple fonts may be listed in case a font does not support all emoji. Note that fontconfig matches color emoji fonts preferentially, so if you want to use a black and white font while having a color font installed (eg. Noto Color Emoji installed alongside Noto Emoji), fontconfig will still choose the color font even when it is later in the list. ''; }; }; hinting = { enable = mkOption { type = types.bool; default = true; description = '' Enable font hinting. Hinting aligns glyphs to pixel boundaries to improve rendering sharpness at low resolution. At high resolution (> 200 dpi) hinting will do nothing (at best); users of such displays may want to disable this option. ''; }; autohint = mkOption { type = types.bool; default = false; description = '' Enable the autohinter in place of the default interpreter. The results are usually lower quality than correctly-hinted fonts, but better than unhinted fonts. ''; }; }; includeUserConf = mkOption { type = types.bool; default = true; description = '' Include the user configuration from ~/.config/fontconfig/fonts.conf or ~/.config/fontconfig/conf.d. ''; }; subpixel = { rgba = mkOption { default = "rgb"; type = types.enum ["rgb" "bgr" "vrgb" "vbgr" "none"]; description = '' Subpixel order. The overwhelming majority of displays are rgb in their normal orientation. Select vrgb for mounting such a display 90 degrees clockwise from its normal orientation or vbgr for mounting 90 degrees counter-clockwise. Select bgr in the unlikely event of mounting 180 degrees from the normal orientation. Reverse these directions in the improbable event that the display's native subpixel order is bgr. ''; }; lcdfilter = mkOption { default = "default"; type = types.enum ["none" "default" "light" "legacy"]; description = '' FreeType LCD filter. At high resolution (> 200 DPI), LCD filtering has no visible effect; users of such displays may want to select none. ''; }; }; cache32Bit = mkOption { default = false; type = types.bool; description = '' Generate system fonts cache for 32-bit applications. ''; }; allowBitmaps = mkOption { type = types.bool; default = true; description = '' Allow bitmap fonts. Set to false to ban all bitmap fonts. ''; }; allowType1 = mkOption { type = types.bool; default = false; description = '' Allow Type-1 fonts. Default is false because of poor rendering. ''; }; useEmbeddedBitmaps = mkOption { type = types.bool; default = false; description = ''Use embedded bitmaps in fonts like Calibri.''; }; }; }; }; config = mkMerge [ (mkIf cfg.enable { environment.systemPackages = [ pkgs.fontconfig ]; environment.etc.fonts.source = "${fontconfigEtc}/etc/fonts/"; }) (mkIf cfg.enable { fonts.fontconfig.confPackages = [ confPkg ]; }) ]; }