/*
Configuration files are linked to /etc/fonts/${pkgs.fontconfig.configVersion}/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/${pkg.configVersion}/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
ln -s /etc/fonts/${pkg.configVersion}/fonts.conf \
$out/etc/fonts/fonts.conf
# fontconfig default config files
ln -s ${pkg.out}/etc/fonts/conf.d/*.conf \
$dst/
# update 51-local.conf path to look at local.conf
rm $dst/51-local.conf
substitute ${pkg.out}/etc/fonts/conf.d/51-local.conf \
$dst/51-local.conf \
--replace local.conf /etc/fonts/${pkg.configVersion}/local.conf
# 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
# Since latest fontconfig looks for default files inside the package,
# we had to move this one elsewhere to be able to exclude it here.
${optionalString cfg.includeUserConf ''
ln -s ${pkg.out}/etc/fonts/conf.d.bak/50-user.conf $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 ];
})
];
}