Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2024 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 : #include "fontsdatabase.h"
23 :
24 : #include "muse_framework_config.h"
25 :
26 : #ifdef MUSE_MODULE_DRAW_USE_QTFONTMETRICS
27 : #include <QFontDatabase>
28 : #include <QFont>
29 : #endif
30 :
31 : #include "global/io/file.h"
32 :
33 : #include "log.h"
34 :
35 : using namespace muse;
36 : using namespace muse::draw;
37 :
38 : #ifndef MUSE_MODULE_DRAW_USE_QTFONTMETRICS
39 : static int s_fontID = -1;
40 : #endif
41 :
42 149 : void FontsDatabase::setDefaultFont(Font::Type type, const FontDataKey& key)
43 : {
44 149 : m_defaults[type] = key;
45 149 : m_changed.notify();
46 149 : }
47 :
48 149 : void FontsDatabase::insertSubstitution(const String& familyName, const String& substituteName)
49 : {
50 149 : FontDataKey familyKey(familyName);
51 149 : FontDataKey substituteKey(substituteName);
52 149 : m_familySubstitutions[familyKey].push_back(substituteKey);
53 :
54 : #ifdef MUSE_MODULE_DRAW_USE_QTFONTMETRICS
55 149 : QFont::insertSubstitution(familyName, substituteName);
56 : #endif
57 :
58 149 : m_changed.notify();
59 149 : }
60 :
61 2 : void FontsDatabase::removeSubstitutions(const String& familyName, const std::vector<String>& substituteNames)
62 : {
63 2 : auto it = m_familySubstitutions.find(FontDataKey(familyName));
64 2 : if (it == m_familySubstitutions.end()) {
65 0 : return;
66 : }
67 :
68 2 : std::vector<FontDataKey>& substitutes = it->second;
69 2 : size_t removed = 0;
70 4 : for (const String& substituteName : substituteNames) {
71 2 : removed += std::erase(substitutes, FontDataKey(substituteName));
72 : }
73 :
74 2 : if (removed == 0) {
75 0 : return;
76 : }
77 :
78 : #ifdef MUSE_MODULE_DRAW_USE_QTFONTMETRICS
79 2 : QFont::removeSubstitutions(familyName);
80 4 : for (const FontDataKey& key : substitutes) {
81 2 : QFont::insertSubstitution(familyName, key.family().id().toQString());
82 : }
83 : #endif
84 :
85 2 : if (substitutes.empty()) {
86 1 : m_familySubstitutions.erase(it);
87 : }
88 :
89 2 : m_changed.notify();
90 : }
91 :
92 0 : const FontDataKey& FontsDatabase::defaultFont(Font::Type type) const
93 : {
94 0 : auto it = m_defaults.find(type);
95 0 : if (it != m_defaults.end()) {
96 0 : return it->second;
97 : }
98 :
99 0 : it = m_defaults.find(Font::Type::Unknown);
100 0 : IF_ASSERT_FAILED(it != m_defaults.end()) {
101 0 : static FontDataKey null;
102 0 : return null;
103 : }
104 0 : return it->second;
105 : }
106 :
107 700 : int FontsDatabase::addFont(const FontDataKey& key, const io::path_t& path)
108 : {
109 700 : return addFont(key, std::make_shared<io::File>(path));
110 : }
111 :
112 700 : int FontsDatabase::addFont(const FontDataKey& key, std::shared_ptr<io::IODevice> device)
113 : {
114 700 : if (!device) {
115 0 : LOGE() << "no device for font: " << key.family().id();
116 0 : return -1;
117 : }
118 :
119 1400 : const std::string filePath = device->meta("file_path");
120 700 : ByteArray data;
121 700 : if (filePath.empty()) {
122 0 : if (device->open(io::IODevice::ReadOnly)) {
123 0 : data = device->readAll();
124 : }
125 0 : if (data.empty()) {
126 0 : LOGE() << "failed read font: " << key.family().id();
127 0 : return -1;
128 : }
129 700 : } else if (!io::File::exists(filePath)) {
130 0 : LOGW() << "font file not found: " << filePath;
131 0 : return -1;
132 : }
133 :
134 : #ifdef MUSE_MODULE_DRAW_USE_QTFONTMETRICS
135 700 : const int id = filePath.empty()
136 700 : ? QFontDatabase::addApplicationFontFromData(data.toQByteArray())
137 700 : : QFontDatabase::addApplicationFont(QString::fromStdString(filePath));
138 700 : if (id < 0) {
139 0 : LOGW() << "failed register font: " << key.family().id();
140 0 : return id;
141 : }
142 : #else
143 : const int id = ++s_fontID;
144 : #endif
145 :
146 700 : auto it = m_fonts.find(key);
147 700 : if (it != m_fonts.end()) {
148 0 : release(it->second);
149 0 : m_fonts.erase(it);
150 : }
151 :
152 700 : m_fonts.insert({ key, FontInfo { id, key, std::move(device) } });
153 700 : m_changed.notify();
154 :
155 700 : return id;
156 700 : }
157 :
158 0 : void FontsDatabase::removeFont(const FontDataKey& key)
159 : {
160 0 : auto it = m_fonts.find(key);
161 0 : if (it == m_fonts.end()) {
162 0 : return;
163 : }
164 :
165 0 : release(it->second);
166 0 : m_fonts.erase(it);
167 0 : m_changed.notify();
168 : }
169 :
170 0 : void FontsDatabase::release(const FontInfo& fi)
171 : {
172 : #ifdef MUSE_MODULE_DRAW_USE_QTFONTMETRICS
173 0 : if (fi.valid()) {
174 0 : QFontDatabase::removeApplicationFont(fi.id);
175 : }
176 : #else
177 : UNUSED(fi);
178 : #endif
179 0 : }
180 :
181 312 : FontDataKey FontsDatabase::actualFont(const FontDataKey& requireKey, Font::Type type) const
182 : {
183 312 : if (fontInfo(requireKey).valid()) {
184 312 : return requireKey;
185 : }
186 :
187 0 : FontDataKey def = defaultFont(type);
188 0 : LOGW() << "not found required font: " << requireKey.family().id() << ", will be using default: " << def.family().id();
189 0 : return def;
190 0 : }
191 :
192 106 : std::vector<FontDataKey> FontsDatabase::substitutionFonts(const FontDataKey& requireKey) const
193 : {
194 106 : auto familyIt = m_familySubstitutions.find(FontDataKey(requireKey.family()));
195 106 : if (familyIt != m_familySubstitutions.end()) {
196 3 : return familyIt->second;
197 : }
198 :
199 103 : static std::vector<FontDataKey> null;
200 103 : return null;
201 : }
202 :
203 208 : FontData FontsDatabase::fontData(const FontDataKey& requireKey, Font::Type type) const
204 : {
205 208 : FontDataKey key = actualFont(requireKey, type);
206 208 : const FontInfo& info = fontInfo(key);
207 208 : if (!info.valid()) {
208 0 : return FontData();
209 : }
210 :
211 208 : if (!info.device->isOpen() && !info.device->open(io::IODevice::ReadOnly)) {
212 0 : LOGE() << "failed open font: " << key.family().id();
213 0 : return FontData();
214 : }
215 :
216 208 : info.device->seek(0);
217 208 : ByteArray data = info.device->readAll();
218 208 : if (data.empty()) {
219 0 : LOGE() << "failed read font: " << key.family().id();
220 0 : return FontData();
221 : }
222 :
223 208 : return FontData { key, data };
224 208 : }
225 :
226 23 : async::Notification FontsDatabase::changed() const
227 : {
228 23 : return m_changed;
229 : }
230 :
231 520 : const FontsDatabase::FontInfo& FontsDatabase::fontInfo(const FontDataKey& key) const
232 : {
233 520 : auto it = m_fonts.find(key);
234 520 : if (it != m_fonts.end()) {
235 520 : return it->second;
236 : }
237 :
238 0 : static FontInfo null;
239 0 : return null;
240 : }
|