-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Support usage of user-provided SMuFL fonts #25773
base: master
Are you sure you want to change the base?
Conversation
Nice!! However: the SMuFL specification describes how installation should work; might it not be better follow that instead? Users may already have fonts installed in this manner if they are using them for other software. Specifically: the fonts are installed in the usual way (according to the operating system) and then the metadata json files are placed in the correct locations as described on this page. |
Oh, good point - I was not aware that the specification also describes the installation process, thanks for the hint. I'll try to implement that then 👍 |
I've done this in Mu3 in a 3-level way, system-wide, user-wide, both as per that SMuFL standard, and MuseScore only (the 'private' way, as per SMuFL, basically the way you did it. |
Is there any specific reason why the privately installed fonts should use |
The correct canonical form seems to be fontname.json and, yes, it would be good to be consistent. I think metadata.json exists for mysterious legacy reasons (also if you look in the repositories for Bravura - and indeed for Leland which follows it - the filename is fontname_metadata.json, even though the installed filename should be just fontname.json). It might be nice to recognise all those variants unless it'll cause conflicts. (The metadata should be inside a folder which follows the font name, anyway.) |
so fontname.json, fontname_metadata.json and metadata.json, right? In what order, what precendence, should more that one of those exist? |
Probably BTW, I think I will actually do
|
Allegedly there are issues with spaces in the name of fonts and/or their directories under Linux Also what to do if there's a Musical Symbols Font and a Musical Text font, with a separate metadata.json, like e.g. Finale Ash, which consist of Finale Ash Text,json, Finale Ash Text.otf, Finale Ash.json, Finale Ash.otf |
I believe MuseScore's engraving system does not support SMuFL metadata for musical text fonts because they seem to be handled as "normal" fonts, so that would not be supported for now (but the text font would be detected properly, just the metadata file discarded). |
I have only confirmed correct functionality for Windows - would be great if someone could test on MacOS and Linux since the font search will look through different directories depending on the OS. |
@RootCubed Nice work! It would be great if you could add the following to the commit message of the first commit, by way of attribution:
With that out of the way, I'll take a look at the code and leave some comments! |
|
||
muse::io::path_t symbolFontPath; | ||
muse::io::path_t textFontPath; | ||
QDirIterator fontFiles(iterator.filePath(), { "*.otf", "*.ttf" }, QDir::Files); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm afraid it's going to be a bit more complicated:
Currently, you're looking for font files in (subdirs of) the given path
, which works well for fonts from configuration()->userMusicFontPath()
(because the design with that is that the user puts both the otf/ttf and the metadata file side-by-side in a subdirectory there).
But for fonts from systemFontsPaths
, only the metadata file is stored in that path; the otf/ttf is installed in the default system directory for fonts.
Scanning all system fonts directories for fonts, and then looking for corresponding metadata files, seems not very efficient. The other way around works potentially better, i.e. scan for metadata files and then check if there is a corresponding font in the font database (note that in this case, you probably don't have to load the font by reading the otf/ttf, because it is already in the font database).
So you'll probably end up with two scanning functions: one for fonts from configuration()->userMusicFontPath()
, and one for "installed" fonts, in either the global or user-local system font directories.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I just noticed that when trying to install some SMuFL fonts. It seems I actually misread the spec there as I thought that the fonts should be installed in the same place as the metadata file.
But that's actually fine, no more guessing metadata naming conventions 😅
One question about this, though: should we open the metadata file and look at the fontName
field, or just assume from the folder name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm... I'm not sure to be honest. Little bit annoying that the spec is so unclear about how exactly it is supposed to work.
Perhaps for now do what's easiest and then come back to it later if it ever turns out that the other way was better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the last thing I need to get working, and it's proving to be quite an annoyance...
It seems that you can't get the file path to a system font via QFont, which is needed to add a font to IFontsDatabase
... any ideas on how to deal with this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I'm just manually searching through QStandardPaths::standardLocations(QStandardPaths::FontsLocation)
for now. (With an exception for Windows, since it doesn't include %LOCALAPPDATA%/Microsoft/Windows/Fonts
🙃 )
Trying the Ouverture fonts AloisenU and AloisenGrooveU results in a crash (does not start up) Dec 9 10:56:41 L-SYN kernel: [10771.244468] main[18602]: segfault at 18 ip 00005567fb167d8b sp 00007ffceecdb5e0 error 4 in mscore4portabledev[5567fa9af000+2234000] But these fonts not useful anyway. |
I've been trying to fix that with #25050 (and Jojo-Schmitz#672 for Mu3), to fix #25088. Some discussion about that in https://musescore.org/en/node/369769#comment-1261591 ff. |
@cbjeukendrup Thanks for taking the time to look at the PR! I apologize for not asking before starting work on this, in hindsight it would have obviously been the right thing to do. I'll make sure to add the attribution after I apply the code review. (Would you like it squashed all into one commit in the end?) I'll try to get through the review later today. |
@RootCubed Thanks! Squashing all commits is not necessary; but perhaps it's nice to squash those "Fix CI" commits into their respective parent commits. |
Is there a reason to support a musescore-specific location in addition to the standard locations? I am currently working on a utility that emits |
I don't see any support for the userFontsPath specified in the SMuFL spec. I only see support for System and MuseScore-Custom locations. Here is the code I wrote for Finale Lua plugins, that finds the SMuFL font path. (Because Finale was only Mac and Win, it does not have Linux, but adding it would be straightforward.) local calc_smufl_directory = function(for_user)
local is_on_windows = finenv.UI():IsOnWindows()
local do_getenv = function(win_var, mac_var)
if finenv.UI():IsOnWindows() then
return win_var and os.getenv(win_var) or ""
else
return mac_var and os.getenv(mac_var) or ""
end
end
local smufl_directory = for_user and do_getenv("LOCALAPPDATA", "HOME") or do_getenv("COMMONPROGRAMFILES")
if not is_on_windows then
smufl_directory = smufl_directory .. "/Library/Application Support"
end
smufl_directory = smufl_directory .. "/SMuFL/Fonts/"
return smufl_directory
end |
The SMuFL spec does mention System-wide location, User-specific location and Private fonts |
Right. I think we need to support the user-specific-location, and currently I don't see where it is supported. As for the private location, I am questioning whether that should be a user-facing feature in MuseScore. Is there a need for it? |
BTW, on macOS it is extremely important not to rely on I realize Qt no doubt provides a standard path that bypasses these concerns. But I thought I would mention it, since it is a common mistake on the macOS platform, and OP mentioned a lack of knowledge about macOS. (Also, the SMuFL standard erroneously mentions |
It is, here: QStringList systemFontsPaths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).first(2); using the first 2 entries of the list, user and system |
QStringList systemFontsPaths = ... Might I suggest renaming this to |
I still would like to know the use-case for a user-facing private location. For one, looking at the changes, I am not certain the folder itself is actually user facing. It seems as though the intent is to remove the hard-coded list and instead store them there. If so, I get that use case. But if there a reason for the user to put fonts there, I don't understand it. |
TBH, I just did it because the MU3 implementations did it that way. I think the motivation is similar to how VSTs also have the option for custom storage locations - Fonts that install in non-standard-compliant locations, ability to install fonts on a separate drive, etc. |
The more users are encouraged to do that, the less use external utilities like mine will have. But admittedly, if users are storing fonts in the private folder, no other programs will be able to access them. Perhaps that is incentive enough not to put them there. |
About the question whether we should support MuseScore-specific user-specified folders: we've been discussing this internally, and couldn't come up with a convincing use case for it. Perhaps, in order to encourage standardisation, we should not do it. It's a bit difficult to make a decision, because it feels weird to build something that has only questionable use cases, but at the same time it feels weird to discard it while 90% of it has already been built. Perhaps we can archive that part for now, and bring it back if and when we decide we still want it. |
Alone for testing new version of e.g. Leland private fonts would be useful |
I agree. Some fonts are being actively developed. e.g. Sebastian https://github.com/fkretlow/sebastian/releases and this provides a risk free way to test. |
A private location for testing makes a lot of sense. I just am questioning whether it should be configurable thru the UI. |
This comment was marked as resolved.
This comment was marked as resolved.
Latest Linux build: |
|
||
while (fontFiles.hasNext()) { | ||
fontFiles.next(); | ||
metadataPath = fontFiles.filePath(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the last .json file wins?, No order of precendencd, of Fontname.json, fontname_metadate.json, metadata.json?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The spec clearly states that the only standard filename is fontname.json
. Is it really necessary to support non-standard installations? Also, the standard is very clear that the precedence is user-then-system.
(I realize that many on this thread already know this. I'm just explicitly bringing it out.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would argue that there is no reason to not support other, non-standard-compliant metadata file names, especially since this is a reality already (see e.g. Bravura, Leland, many others...)
In any case, there should always only be one .json file in that directory, so I went with this approach.
The precedence should be handled correctly - see scanAllDirectories.
// It's also stored in score file as the musicalTextFont | ||
musicalTextFont->addItem("Leland Text", "Leland Text"); | ||
musicalTextFont->addItem("Bravura Text", "Bravura Text"); | ||
musicalTextFont->addItem("Emmentaler Text", "MScore Text"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this is the odd one out
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This behaviour is kept by using fontFamilyName in line 832 of the same file.
musicalTextFont->addItem("Leland Text", "Leland Text"); | ||
musicalTextFont->addItem("Bravura Text", "Bravura Text"); | ||
musicalTextFont->addItem("Emmentaler Text", "MScore Text"); | ||
musicalTextFont->addItem("Gonville Text", "Gootville Text"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, this one too
Ugh yep, this is where it would have been handy to be able to use QFont for finding fonts... Unfortunately, I'm not sure what the best solution is here. (Ideally, we want to search for family names, not for font file names, but going through all ttf/otf files to look at the family name seems like a rather terrible idea) |
Relevant issues: #24761 and #16189
Based on the work of @cbjeukendrup in https://github.com/cbjeukendrup/MuseScore/tree/custom-music-fonts.
This PR introduces functionality that allows users to provide custom SMuFL fonts in MuseScore.
It adds a new folder selection in Preferences -> Folders -> MusicFonts (Default:
MuseScore4[Development]/MusicFonts
).It searches through this folder for directories with SMuFL files -
<font name>.ttf/.otf
files and a matching metadata json file (<font name>.json
,metadata.json
or<font name>_metadata.json
).It also supports the standard installation directories as specified by the SMuFL docs.
So, if you wanted to add the Sebastian font, you would create a directory in MusicFonts called
Sebastian
and place the filesSebastian.otf
,SebastianText.otf
andmetadata.json
(renamed fromSebastian.json
) inside it.