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

Fix path on Windows and Linux where SMuFL expects system fonts to be #720

Open
wants to merge 1 commit into
base: 3.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 46 additions & 32 deletions libmscore/sym.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ QVector<ScoreFont> ScoreFont::_builtinScoreFonts {
ScoreFont("Finale Maestro", "Finale Maestro", ":/fonts/finalemaestro/", "FinaleMaestro.otf"),
ScoreFont("Finale Broadway", "Finale Broadway", ":/fonts/finalebroadway/", "FinaleBroadway.otf"),
};
QVector<ScoreFont> ScoreFont::_userScoreFonts {};
QVector<ScoreFont> ScoreFont::_systemScoreFonts {};
QVector<ScoreFont> ScoreFont::_privateScoreFonts {};
QVector<ScoreFont> ScoreFont::_standardScoreFonts {};
QVector<ScoreFont> ScoreFont::_allScoreFonts {};

std::array<uint, size_t(SymId::lastSym)+1> ScoreFont::_mainSymCodeTable { {0} };
Expand Down Expand Up @@ -6601,45 +6601,59 @@ void Ms::ScoreFont::initScoreFonts()
// Linux: "$XDG_DATA_HOME/SMuFL/Fonts", "$XDG_DATA_DIRS/SMuFL/Fonts"
// as per https://doc.qt.io/qt-5/qstandardpaths.html#standardLocations that is the (start of the) list
// which `GenericDataLocation` gives (without the "/SMuFL/Fonts")
#ifdef Q_OS_WIN
// take only the first two entries of that list on Windows (on Mac it is 2 elements only anyway)
QStringList systemFontsPaths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).mid(0, 2);
#else
QStringList systemFontsPaths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
// take only the first two entries of that list (on Windows, on Mac it is 2 elements only anyway).
// These standard location roughly match up with what the following returns, but some adjustments are needed.
QStringList standardLocations = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).mid(0, 2);
#if defined(Q_OS_WIN)
// On Windows, the second standard location returned by Qt is %ProgramData%, but we want %CommonProgramFiles%
QStringList globalFontsPaths {
standardLocations.first(),
qgetenv("CommonProgramFiles").replace("\\", "/") // "C:\\Program Files\\Common Files"
};
#elif defined(Q_OS_MACOS)
// MacOS is correctly handled by Qt
QStringList globalFontsPaths = standardLocations;
#elif defined(Q_OS_LINUX)
// On Unix systems, we want $XDG_DATA_HOME (user-specific) and $XDG_DATA_DIRS (system-wide)
QStringList globalFontsPaths { qgetenv("XDG_DATA_HOME") };
if (globalFontsPaths.isEmpty()) // XDG_DATA_HOME not set or empty
globalFontsPaths.append(qgetenv("HOME") + "/.local/share");
globalFontsPaths.append(QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS")).split(':'));
#endif
for (QString& systemFontsPath : systemFontsPaths) {
systemFontsPath += "/SMuFL/Fonts";
scanUserFonts(systemFontsPath, true);
}

// The first location is the user-wide location, so we should iterate in reverse order so that
// user-specific fonts can override system-wide fonts
for (auto it = globalFontsPaths.rbegin(); it != globalFontsPaths.rend(); ++it)
scanUserFonts(*it + "/SMuFL/Fonts", false);
}

void ScoreFont::scanUserFonts(const QString& path, bool system)
void ScoreFont::scanUserFonts(const QString& path, bool standard)
{
QVector<ScoreFont> userfonts;

QDirIterator iterator(path, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable);
QDirIterator dirs(path, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable);

while (iterator.hasNext()) {
QString fontDir = iterator.next();
QString fontDirPath = iterator.filePath() + "/";
QString fontDirName = iterator.fileName();
while (dirs.hasNext()) {
QString fontDir = dirs.next();
QString fontDirPath = dirs.filePath() + "/";
QString fontDirName = dirs.fileName();

QString fontName;
QString fontFilename;
QDirIterator innerIterator(fontDirPath, { "*.otf", "*.ttf" }, QDir::Files);
QDirIterator files(fontDirPath, { "*.otf", "*.ttf" }, QDir::Files);

while (innerIterator.hasNext()) {
QString potentialFontFile = innerIterator.next();
while (files.hasNext()) {
QString potentialFontFile = files.next();
QFileInfo fileinfo(potentialFontFile);

if (fileinfo.completeBaseName().toLower() == fontDirName.toLower()) {
fontName = fileinfo.completeBaseName();
fontFilename = innerIterator.fileName();
fontFilename = files.fileName();
break;
}
}

bool hasMetadataFile = QFileInfo::exists(fontDirPath + (system ? fontName : "metadata") + ".json");
bool hasMetadataFile = QFileInfo::exists(fontDirPath + (standard ? fontName : "metadata") + ".json");

if (hasMetadataFile && !fontFilename.isEmpty()) {
QByteArray name = fontName.toLocal8Bit();
Expand All @@ -6650,26 +6664,26 @@ void ScoreFont::scanUserFonts(const QString& path, bool system)
}


qDebug() << "Found" << userfonts.count() << (system ? "system" : "user") << "score font" << (userfonts.count() > 1? "s" : "") << " in" << path <<".";
qDebug() << "Found" << userfonts.count() << (standard ? "standard" : "private") << "score" << (userfonts.count() > 1? "fonts" : "font") << "in" << path << ".";

// TODO: Check for fonts that duplicate built-in fonts
if (!system) // reset list when re-reading due to changed Preferences
_userScoreFonts.clear();
if (!standard) // reset list when re-reading due to changed Preferences
_privateScoreFonts.clear();

// Make sure the fonts are loaded, to avoid the situation that MuseScore
// thinks a font exists but in practice it has disappeared.
for (const ScoreFont& f : userfonts) {
ScoreFont font = f;
if (!font.face)
font.load(system);
if (system)
_systemScoreFonts.push_back(font);
font.load(standard);
if (standard)
_standardScoreFonts.push_back(font);
else
_userScoreFonts.push_back(font);
_privateScoreFonts.push_back(font);
}

_allScoreFonts = _builtinScoreFonts;
_allScoreFonts << _userScoreFonts << _systemScoreFonts;
_allScoreFonts << _privateScoreFonts << _standardScoreFonts;

// Include external and internal score fonts into QFontDatabase
for (auto& f : _allScoreFonts) {
Expand Down Expand Up @@ -6740,7 +6754,7 @@ void ScoreFont::computeMetrics(Sym* sym, int code)
// load
//---------------------------------------------------------

void ScoreFont::load(bool system)
void ScoreFont::load(bool standard)
{
QString facePath = _fontPath + _filename;
QFile f(facePath);
Expand Down Expand Up @@ -6769,7 +6783,7 @@ void ScoreFont::load(bool system)
}

QJsonParseError error;
QFile fi(_fontPath + (system ? _name : "metadata") + ".json");
QFile fi(_fontPath + (standard ? _name : "metadata") + ".json");
if (!fi.open(QIODevice::ReadOnly))
qDebug("ScoreFont: open glyph metadata file <%s> failed", qPrintable(fi.fileName()));
QJsonObject metadataJson = QJsonDocument::fromJson(fi.readAll(), &error).object();
Expand Down
8 changes: 4 additions & 4 deletions libmscore/sym.h
Original file line number Diff line number Diff line change
Expand Up @@ -3159,11 +3159,11 @@ class ScoreFont {
mutable QFont* font { 0 };

static QVector<ScoreFont> _builtinScoreFonts;
static QVector<ScoreFont> _userScoreFonts;
static QVector<ScoreFont> _systemScoreFonts;
static QVector<ScoreFont> _privateScoreFonts;
static QVector<ScoreFont> _standardScoreFonts;
static QVector<ScoreFont> _allScoreFonts;
static std::array<uint, size_t(SymId::lastSym)+1> _mainSymCodeTable;
void load(bool system = false);
void load(bool standard = false);
void computeMetrics(Sym* sym, int code);

public:
Expand All @@ -3185,7 +3185,7 @@ class ScoreFont {
QString fontPath() const { return _fontPath; }

static void initScoreFonts();
static void scanUserFonts(const QString& path, bool system = false);
static void scanUserFonts(const QString& path, bool standard = false);
static ScoreFont* fontFactory(QString);
static ScoreFont* fallbackFont();
static const char* fallbackTextFont();
Expand Down