[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

What music font packaging/selection experience do we want?

From: Jean Abou Samra
Subject: What music font packaging/selection experience do we want?
Date: Fri, 21 Apr 2023 12:21:56 +0200
User-agent: Evolution 3.46.4 (3.46.4-1.fc37)


With [!1957]( that 
will soon become ready, I think it's about time to discuss how we want the “big 
picture” user experience and font developer experience to be with respect to 
alternative music fonts.

For those who haven't followed the (long) story, this MR basically lets 
LilyPond search for music fonts in the same way as it searches for text fonts. 
It thus makes it possible to make music fonts found with 
`ly:font-config-add-font` or `ly:font-config-add-directory`, in contrast to the 
current approach where you have to drop them into directories that are normally 
supposed to be internal to LilyPond (and therefore re-add them with every new 
LilyPond version). This lays the internal foundation for a better user 
experience of using alternative music fonts.

While LilyPond doesn't support SMuFL right now, it's very desirable to have 
that in the medium term and I think it makes sense to keep it in mind for 
designing this — at least, to avoid making changes that will complicate it.

Now, from the user point of view, the question is how we want to organize the 
UX of using an alternative music font:

1. Download the font: from where? Are some alternative fonts preinstalled with 
our official binaries? This would deserve a thread of its own and I won't 
discuss it here.

2. Install the font: how?

3. Use the font in a .ly file: how?

For (3), the basic way to select a music font is

\paper { = "whatever"

The main issue is that this only switches the glyphs themselves. However, to 
look good, there are also other style adjustments to make in order to match the 
font, like the thickness of stems and staff lines, etc. We want to have a way 
to select a font *and* make those adjustments automatically at the same time.

I can see two approaches.

The first one is to keep doing what has been done so far: define include files. 
For an example, see


In that perspective, the font `.otf` file can be put next to the include file, 
and it can do something like

#(ly:font-config-add-directory (dirname (car (ly:input-file-line-char-column 

which registers the directory of the include file as a font directory.

This is a workable approach. There are at least two reasons why this is 
suboptimal, though.

The first reason is subjective: I would prefer if the way to select alternative 
music fonts was uniform with text fonts, i.e., with `\paper { = 
"..." }`. Also, the include file would have to be `\include`d inside `\score` 
blocks to make the font change local to one score. I'm not very fond of this. 
See [!1929]( for 
some discussion of this practice.

The second reason is related to SMuFL. We want people to be able to take 
advantage of any SMuFL font downloaded from a random place on the Internet, and 
not require that every font come with a LilyPond include file to be usable with 

With SMuFL, there is a JSON file next to the font, which contains, among other 
things, an `engravingDefaults` structure.


So we'll also eventually want to read that. But it's obviously much more 
limited than the full range of things you can customize in LilyPond.

Now to the second approach, which I prefer.

In this approach, `\paper { = "foo" }` remains the syntax to select 
an alternative music font.

If, next to the `.otf` font file, a file called `stylesheet.ily` (or another 
bikeshed color) is found, it is read and defines the style parameters. Because 
we want to be able to apply it both globally and locally to one 
score/bookpart/book, we take it in the form of a `\layout` block. To do that, 
we read `stylesheet.ily` with `ly:parse-string-expression`. That is, in exactly 
the same way as the content of `#{ ... #}` is read. For the `stylesheet.ily` 
writer, this means that the file is written as a single `\layout` block (plus 
perhaps a `\version` statement).

If in the future we support SMuFL, we'll also read the JSON metadata and 
synthetize a layout from it, then use it in the same way. `stylesheet.ily` 
would continue to be supported and could be used to define styles that SMuFL 
doesn't allow.

This leaves with the question of how font files are found and where they are 
installed. We don't really want users to need `#(ly:font-config-add-directory 
"/home/me/...")` in every file with a custom music font.

With the include-based approach, LilyPond has an include path, which can be 
extended with the `-I` option. Frescobaldi has a nice interface for adding 
include directories (in the LilyPond preferences). I was initially hoping to 
reuse that. However, Fontconfig scans directories recursively. We don't want to 
add all the include path to the font path because include directories (this 
also means the current directory) could contain many files and take forever to 

A possibility would be to scan our include directories ourselves (in a shallow 
way) and add `.otf` files individually to Fontconfig.

This isn't that nice for SMuFL fonts, though. Each SMuFL font has an associated 
JSON file, and I believe the two would normally be put into a directory of its 
own. Ergo, you would not be able to add a directory "my-custom-fonts" with one 
subdirectory per font inside. You would need to add each of the subdirectories 

For this reason, I lean towards a `-dfont-path` option, similar to `-I` but for 
font directories, and scanned recursively.

The downside, of course, is that we'll need to wait until a Frescobaldi update 
implements a graphical way to add font directories, like include directories, 
for this become a really seamless experience. (I would be willing to contribute 
that in Frescobaldi.)

Well, that was long again. What are your thoughts? Do you see any problems or 
better alternatives?



Attachment: signature.asc
Description: This is a digitally signed message part

reply via email to

[Prev in Thread] Current Thread [Next in Thread]