Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plymouth: set custom font #339

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

plymouth: set custom font #339

wants to merge 1 commit into from

Conversation

Jeidnx
Copy link

@Jeidnx Jeidnx commented Apr 12, 2024

Sets the user configured font in plymouth. On my setup plymouth only seemed to accept / work with truetype fonts and i didn't find any documentation for plymouth mentioning fonts at all, so the script checks for that first and falls back to converting another font format to ttf with fontforge.

@danth danth requested a review from trueNAHO April 17, 2024 08:26
@danth
Copy link
Owner

danth commented Apr 17, 2024

@trueNAHO Requesting a review since you've been doing a lot of work around bash scripts recently, so you may be interested in this new one.

Comment on lines 45 to 78
mkPlymouthFont =
font:
pkgs.runCommand "${font.package.name}.ttf"
{ FONTCONFIG_FILE = pkgs.makeFontsConf { fontDirectories = [ font.package ]; }; }
''
matchingFonts=$(${pkgs.fontconfig}/bin/fc-match -asf "%{family}|%{file}\n" "${font.name}" | grep "^${font.name}|" | cut -d'|' -f2)

if [[ -z "$matchingFonts" ]]; then
echo "No font named '${font.name}' found."
exit 1
fi

if ttfFont=$(echo "$matchingFonts" | grep -m1 -E "\.ttf$"); then
cp "$ttfFont" "$out"
else
font=$(echo "$matchingFonts" | head -n1)
${pkgs.fontforge}/bin/fontforge -lang=ff -c 'Open($1); Generate($2); Close()' "$font" "$out"
fi
'';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestions

Note: I have not tested the code. Feel free to fix any emerging bugs.

I suggest the following changes:

  • Reformat the code
  • Wrap the code at 80 characters
  • Merge the first if-statement
  • Expand short flag names into their longer form
  • Merge the grep and cut commands into one (faster) awk command
  • Use verboseEcho instead of echo for logging
  • Improve logging message structure to simplify grepping logs
  • Replace non-logging echo commands with printf '%s\n'
  • Replace ${pkgs.<PACKAGE>}/bin/<COMMAND> with ${lib.getExe' <PACKAGE> <COMMAND>}
  • Add TODO: comments.
Suggested change
mkPlymouthFont =
font:
pkgs.runCommand "${font.package.name}.ttf"
{ FONTCONFIG_FILE = pkgs.makeFontsConf { fontDirectories = [ font.package ]; }; }
''
matchingFonts=$(${pkgs.fontconfig}/bin/fc-match -asf "%{family}|%{file}\n" "${font.name}" | grep "^${font.name}|" | cut -d'|' -f2)
if [[ -z "$matchingFonts" ]]; then
echo "No font named '${font.name}' found."
exit 1
fi
if ttfFont=$(echo "$matchingFonts" | grep -m1 -E "\.ttf$"); then
cp "$ttfFont" "$out"
else
font=$(echo "$matchingFonts" | head -n1)
${pkgs.fontforge}/bin/fontforge -lang=ff -c 'Open($1); Generate($2); Close()' "$font" "$out"
fi
'';
mkPlymouthFont = font:
pkgs.runCommand "${font.package.name}.ttf"
{FONTCONFIG_FILE = pkgs.makeFontsConf {fontDirectories = [font.package];};}
''
if ! matchingFonts="$(
# TODO: Explain why the '--sort' flag is needed.
${lib.getExe' pkgs.fontconfig "fc-match"} \
--all \
--format '%{family}|%{file}\n' \
'${font.name}' \
--sort |
awk --field-separator '|' '/^${font.name}\|/ { print $2 }'
)"; then
verboseEcho "Font not found: '${font.name}'"
exit 1
fi
if ttfFont="$(
# TODO: Explain why the '--max-count' flag is needed, if the
# previous '--sort' flag is required.
printf '%s\n' "$matchingFonts" |
grep --extended-regexp --max-count 1 "\.ttf$"
)"; then
cp "$ttfFont" "$out"
else
# TODO: Explain why only the first line is needed, if the previous
# '--sort' flag is required.
font=$(printf '%s\n' "$matchingFonts" | head --lines 1)
${lib.getExe' pkgs.fontforge "fontforge"} \
-lang=ff \
-c 'Open($1); Generate($2); Close()' \
"$font" \
"$out"
fi
'';

Unresolved Questions

Resolve the suggested TODO: comments.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

verboseEcho is only defined within home.activation scripts; a normal echo or printf should be used here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

verboseEcho is only defined within home.activation scripts; a normal echo or printf should be used here.

Good catch. In that case, we should print to stderr with:

printf 'Font not found: %s' '${font.name}'" >&2

Here is the updated suggestion:

Suggested change
mkPlymouthFont =
font:
pkgs.runCommand "${font.package.name}.ttf"
{ FONTCONFIG_FILE = pkgs.makeFontsConf { fontDirectories = [ font.package ]; }; }
''
matchingFonts=$(${pkgs.fontconfig}/bin/fc-match -asf "%{family}|%{file}\n" "${font.name}" | grep "^${font.name}|" | cut -d'|' -f2)
if [[ -z "$matchingFonts" ]]; then
echo "No font named '${font.name}' found."
exit 1
fi
if ttfFont=$(echo "$matchingFonts" | grep -m1 -E "\.ttf$"); then
cp "$ttfFont" "$out"
else
font=$(echo "$matchingFonts" | head -n1)
${pkgs.fontforge}/bin/fontforge -lang=ff -c 'Open($1); Generate($2); Close()' "$font" "$out"
fi
'';
mkPlymouthFont = font:
pkgs.runCommand "${font.package.name}.ttf"
{FONTCONFIG_FILE = pkgs.makeFontsConf {fontDirectories = [font.package];};}
''
if ! matchingFonts="$(
# TODO: Explain why the '--sort' flag is needed.
${lib.getExe' pkgs.fontconfig "fc-match"} \
--all \
--format '%{family}|%{file}\n' \
'${font.name}' \
--sort |
awk --field-separator '|' '/^${font.name}\|/ { print $2 }'
)"; then
printf 'Font not found: %s' '${font.name}'" >&2
exit 1
fi
if ttfFont="$(
# TODO: Explain why the '--max-count' flag is needed, if the
# previous '--sort' flag is required.
printf '%s\n' "$matchingFonts" |
grep --extended-regexp --max-count 1 "\.ttf$"
)"; then
cp "$ttfFont" "$out"
else
# TODO: Explain why only the first line is needed, if the previous
# '--sort' flag is required.
font=$(printf '%s\n' "$matchingFonts" | head --lines 1)
${lib.getExe' pkgs.fontforge "fontforge"} \
-lang=ff \
-c 'Open($1); Generate($2); Close()' \
"$font" \
"$out"
fi
'';

Also, should we apply lib.escapeShellArg on variables like font.name, similar to #318 ?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think that would be appropriate.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lib.escapeShellArg seems to only add single quotes around the variable. This breaks the awk program and i haven't found a reason for escaping anything else in the script. To be clear im not against using it, if anyone can get it to work how it should be i would appreciate it.

Copy link
Collaborator

@trueNAHO trueNAHO May 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lib.escapeShellArg seems to only add single quotes around the variable.

Yes, that is literally all it does:
https://github.com/NixOS/nixpkgs/blob/23.11/lib/strings.nix#L435-L443.

This breaks the awk program

Indeed. Yesterday, I actually stumbled across the same issue myself.

and i haven't found a reason for escaping anything else in the script. To be clear im not against using it, if anyone can get it to work how it should be i would appreciate it.

In my case, I ignored the escaping to simplify the implementation. However, I see two ways of solving this problem in the following descending order of weirdness:

  1. Concatenate the AWK code: awk '<BEFORE>'"${lib.escapeShellArg <ARGUMENT>}"'<AFTER>'.
  2. Provide an AWK variable: awk --assign <VARIABLE>=${lib.escapeShellArg <ARGUMENT>} '<CODE>'.

Btw, very nice error propagation in the matchingFonts variable with the awk script :)

modules/plymouth/nixos.nix Show resolved Hide resolved
@Jeidnx
Copy link
Author

Jeidnx commented May 6, 2024

I have since read more about fonts in plymouth and as i understand it only truetype and opentype are supported with opentype being the preferred format. I am not sure if there is a way to have the derivation point to a .otf or .ttf file, or if the extension even needs to be in the name of the derivation for that matter, so i chose to ignore truetype and convert everything that is not already opentype to that format instead. If anyone has a way of allowing both formats input would be appreciated :)

printf 'Font not found: %s' '${font.name}' >&2
exit 1
fi
echo "fonts: '$matchingFonts'" >&2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this not be echoed on every build? Should we remove this debugging statement:

Suggested change
echo "fonts: '$matchingFonts'" >&2

@trueNAHO
Copy link
Collaborator

trueNAHO commented May 8, 2024

I have since read more about fonts in plymouth and as i understand it only truetype and opentype are supported with opentype being the preferred format. I am not sure if there is a way to have the derivation point to a .otf or .ttf file, or if the extension even needs to be in the name of the derivation for that matter, so i chose to ignore truetype and convert everything that is not already opentype to that format instead. If anyone has a way of allowing both formats input would be appreciated :)

I have not done any further research on this, but would it be possible to extract the font types from certain paths? Maybe the nerdfont package is helpfull: https://github.com/NixOS/nixpkgs/blob/nixos-23.11/pkgs/data/fonts/nerdfonts/default.nix.

For reference, there is an open issue regarding fonts: #308.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants