/* NixOS support 2 fontconfig versions, "support" and "latest". - "latest" refers to default fontconfig package (pkgs.fontconfig). configuration files are linked to /etc/fonts/VERSION/conf.d/ - "support" refers to supportPkg (pkgs."fontconfig_${supportVersion}"). 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) + ""; # back-supported fontconfig version and package # version is used for font cache generation supportVersion = "210"; supportPkg = pkgs."fontconfig_${supportVersion}"; # latest fontconfig version and package # version is used for configuration folder name, /etc/fonts/VERSION/ # note: format differs from supportVersion and can not be used with makeCacheConf latestVersion = pkgs.fontconfig.configVersion; latestPkg = pkgs.fontconfig; # supported version fonts.conf supportFontsConf = pkgs.makeFontsConf { fontconfig = supportPkg; fontDirectories = config.fonts.fonts; }; # configuration file to read fontconfig cache # version dependent # priority 0 cacheConfSupport = makeCacheConf { version = supportVersion; }; cacheConfLatest = makeCacheConf {}; # generate the font cache setting file for a fontconfig version # use latest when no version is passed makeCacheConf = { version ? null }: let fcPackage = if version == null then "fontconfig" else "fontconfig_${version}"; makeCache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.fonts; }; cache = makeCache pkgs.${fcPackage}; cache32 = makeCache pkgs.pkgsi686Linux.${fcPackage}; in pkgs.writeText "fc-00-nixos-cache.conf" '' ${concatStringsSep "\n" (map (font: "${font}") config.fonts.fonts)} ${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; } '' support_folder=$out/etc/fonts/conf.d latest_folder=$out/etc/fonts/${latestVersion}/conf.d mkdir -p $support_folder mkdir -p $latest_folder # fonts.conf ln -s ${supportFontsConf} $support_folder/../fonts.conf ln -s ${latestPkg.out}/etc/fonts/fonts.conf \ $latest_folder/../fonts.conf # fontconfig default config files ln -s ${supportPkg.out}/etc/fonts/conf.d/*.conf \ $support_folder/ ln -s ${latestPkg.out}/etc/fonts/conf.d/*.conf \ $latest_folder/ # update latest 51-local.conf path to look at the latest local.conf rm $latest_folder/51-local.conf substitute ${latestPkg.out}/etc/fonts/conf.d/51-local.conf \ $latest_folder/51-local.conf \ --replace local.conf /etc/fonts/${latestVersion}/local.conf # 00-nixos-cache.conf ln -s ${cacheConfSupport} \ $support_folder/00-nixos-cache.conf ln -s ${cacheConfLatest} $latest_folder/00-nixos-cache.conf # 10-nixos-rendering.conf ln -s ${renderConf} $support_folder/10-nixos-rendering.conf ln -s ${renderConf} $latest_folder/10-nixos-rendering.conf # 50-user.conf ${optionalString (!cfg.includeUserConf) '' rm $support_folder/50-user.conf rm $latest_folder/50-user.conf ''} # local.conf (indirect priority 51) ${optionalString (cfg.localConf != "") '' ln -s ${localConf} $support_folder/../local.conf ln -s ${localConf} $latest_folder/../local.conf ''} # 52-nixos-default-fonts.conf ln -s ${defaultFontsConf} $support_folder/52-nixos-default-fonts.conf ln -s ${defaultFontsConf} $latest_folder/52-nixos-default-fonts.conf # 53-no-bitmaps.conf ln -s ${rejectBitmaps} $support_folder/53-no-bitmaps.conf ln -s ${rejectBitmaps} $latest_folder/53-no-bitmaps.conf ${optionalString (!cfg.allowType1) '' # 53-nixos-reject-type1.conf ln -s ${rejectType1} $support_folder/53-nixos-reject-type1.conf ln -s ${rejectType1} $latest_folder/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 { 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 && !cfg.penultimate.enable) { fonts.fontconfig.confPackages = [ confPkg ]; }) ]; }