/* * SPDX-License-Identifier: GPL-3.0-only * MuseScore-CLA-applies * * MuseScore Studio * Music Composition & Notation * * Copyright (C) 2024 MuseScore Limited and others * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "fontsdatabase.h" #include "muse_framework_config.h" #ifdef MUSE_MODULE_DRAW_USE_QTFONTMETRICS #include #endif #include "global/io/file.h" #include "log.h" using namespace muse; using namespace muse::draw; #ifndef MUSE_MODULE_DRAW_USE_QTFONTMETRICS static int s_fontID = -1; #endif FontsDatabase::~FontsDatabase() { for (const auto& [key, info] : m_fonts) { release(info); } } void FontsDatabase::setDefaultFont(Font::Type type, const FontDataKey& key) { m_defaults[type] = key; m_changed.notify(); } void FontsDatabase::insertSubstitution(const String& familyName, const String& substituteName) { FontDataKey familyKey(familyName); FontDataKey substituteKey(substituteName); m_familySubstitutions[familyKey].push_back(substituteKey); m_changed.notify(); } void FontsDatabase::removeSubstitutions(const String& familyName, const std::vector& substituteNames) { auto it = m_familySubstitutions.find(FontDataKey(familyName)); if (it == m_familySubstitutions.end()) { return; } std::vector& substitutes = it->second; size_t removed = 0; for (const String& substituteName : substituteNames) { removed += std::erase(substitutes, FontDataKey(substituteName)); } if (removed == 0) { return; } if (substitutes.empty()) { m_familySubstitutions.erase(it); } m_changed.notify(); } const FontDataKey& FontsDatabase::defaultFont(Font::Type type) const { auto it = m_defaults.find(type); if (it != m_defaults.end()) { return it->second; } it = m_defaults.find(Font::Type::Unknown); IF_ASSERT_FAILED(it != m_defaults.end()) { static FontDataKey null; return null; } return it->second; } int FontsDatabase::addFont(const FontDataKey& key, const io::path_t& path) { return addFont(key, std::make_shared(path)); } int FontsDatabase::addFont(const FontDataKey& key, std::shared_ptr device) { if (!device) { LOGE() << "no device for font: " << key.family().id(); return -1; } const std::string filePath = device->meta("file_path"); ByteArray data; if (filePath.empty()) { if (device->open(io::IODevice::ReadOnly)) { data = device->readAll(); } if (data.empty()) { LOGE() << "failed read font: " << key.family().id(); return -1; } } else if (!io::File::exists(filePath)) { LOGW() << "font file not found: " << filePath; return -1; } #ifdef MUSE_MODULE_DRAW_USE_QTFONTMETRICS const int id = filePath.empty() ? QFontDatabase::addApplicationFontFromData(data.toQByteArray()) : QFontDatabase::addApplicationFont(QString::fromStdString(filePath)); if (id < 0) { LOGW() << "failed register font: " << key.family().id(); return id; } #else const int id = ++s_fontID; #endif auto it = m_fonts.find(key); if (it != m_fonts.end()) { release(it->second); m_fonts.erase(it); } m_fonts.insert({ key, FontInfo { id, key, std::move(device) } }); m_changed.notify(); return id; } void FontsDatabase::removeFont(const FontDataKey& key) { auto it = m_fonts.find(key); if (it == m_fonts.end()) { return; } release(it->second); m_fonts.erase(it); m_changed.notify(); } void FontsDatabase::release(const FontInfo& fi) { #ifdef MUSE_MODULE_DRAW_USE_QTFONTMETRICS if (fi.valid()) { QFontDatabase::removeApplicationFont(fi.id); } #else UNUSED(fi); #endif } FontDataKey FontsDatabase::actualFont(const FontDataKey& requireKey, Font::Type type) const { if (fontInfo(requireKey).valid()) { return requireKey; } FontDataKey def = defaultFont(type); LOGW() << "not found required font: " << requireKey.family().id() << ", will be using default: " << def.family().id(); return def; } std::vector FontsDatabase::substitutionFonts(const FontDataKey& requireKey) const { auto familyIt = m_familySubstitutions.find(FontDataKey(requireKey.family())); if (familyIt != m_familySubstitutions.end()) { return familyIt->second; } static std::vector null; return null; } FontData FontsDatabase::fontData(const FontDataKey& requireKey, Font::Type type) const { FontDataKey key = actualFont(requireKey, type); const FontInfo& info = fontInfo(key); if (!info.valid()) { return FontData(); } if (!info.device->isOpen() && !info.device->open(io::IODevice::ReadOnly)) { LOGE() << "failed open font: " << key.family().id(); return FontData(); } info.device->seek(0); ByteArray data = info.device->readAll(); if (data.empty()) { LOGE() << "failed read font: " << key.family().id(); return FontData(); } return FontData { key, data }; } async::Notification FontsDatabase::changed() const { return m_changed; } const FontsDatabase::FontInfo& FontsDatabase::fontInfo(const FontDataKey& key) const { auto it = m_fonts.find(key); if (it != m_fonts.end()) { return it->second; } static FontInfo null; return null; }