Warning, file /graphics/glaxnimate/src/core/model/custom_font.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "custom_font.hpp" 0008 0009 #include <unordered_map> 0010 0011 #include <QFontDatabase> 0012 #include <QCryptographicHash> 0013 0014 #include "app/utils/qbytearray_hash.hpp" 0015 0016 0017 glaxnimate::model::FontFileFormat glaxnimate::model::CustomFontDatabase::font_data_format(const QByteArray& data) 0018 { 0019 QByteArray head = data.left(4); 0020 0021 if ( head == "OTTO" ) 0022 return FontFileFormat::OpenType; 0023 if ( head == QByteArray("\0\1\0\0", 4) ) 0024 return FontFileFormat::TrueType; 0025 if ( head == "wOF2" ) 0026 return FontFileFormat::Woff2; 0027 if ( head == "wOFF" ) 0028 return FontFileFormat::Woff; 0029 0030 return FontFileFormat::Unknown; 0031 } 0032 0033 class glaxnimate::model::CustomFontDatabase::CustomFontData 0034 { 0035 public: 0036 CustomFontData() = default; 0037 0038 CustomFontData(const QRawFont& font, int database_index, const QByteArray& data_hash, const QByteArray& data) 0039 : font(font), 0040 database_index(database_index), 0041 data_hash(data_hash), 0042 data(data) 0043 {} 0044 0045 QString family_name() const 0046 { 0047 return font.familyName(); 0048 } 0049 0050 0051 QRawFont font; 0052 int database_index = -1; 0053 QByteArray data_hash; 0054 QByteArray data; 0055 QString source_url; 0056 QString css_url; 0057 std::set<QString> name_aliases; 0058 }; 0059 0060 class glaxnimate::model::CustomFontDatabase::Private 0061 { 0062 public: 0063 std::unordered_map<int, DataPtr> fonts; 0064 // we keep track of hashes to avoid registering the exact same file twice 0065 std::unordered_map<QByteArray, int> hashes; 0066 0067 std::unordered_map<QString, std::vector<int>> name_aliases; 0068 0069 void tag_alias(const DataPtr& data, const QString& name) 0070 { 0071 if ( !name.isEmpty() && name != data->family_name() && data->name_aliases.insert(name).second ) 0072 name_aliases[name].push_back(data->database_index); 0073 } 0074 0075 void uninstall(std::unordered_map<int, DataPtr>::iterator iterator) 0076 { 0077 for ( const auto& name : iterator->second->name_aliases ) 0078 { 0079 auto iter = name_aliases.find(name); 0080 if ( iter != name_aliases.end() ) 0081 { 0082 if ( iter->second.size() <= 1 ) 0083 name_aliases.erase(iter); 0084 else 0085 iter->second.erase(std::find(iter->second.begin(), iter->second.end(), iterator->second->database_index)); 0086 } 0087 } 0088 0089 hashes.erase(iterator->second->data_hash); 0090 QFontDatabase::removeApplicationFont(iterator->first); 0091 fonts.erase(iterator); 0092 } 0093 0094 void remove_reference(int font) 0095 { 0096 auto it = fonts.find(font); 0097 if ( it == fonts.end() ) 0098 return; 0099 0100 if ( it->second.use_count() == 1 ) 0101 uninstall(it); 0102 } 0103 0104 DataPtr install(const QString& name_alias, const QByteArray& data) 0105 { 0106 auto hash = QCryptographicHash::hash(data, QCryptographicHash::Sha1); 0107 auto hashit = hashes.find(hash); 0108 if ( hashit != hashes.end() ) 0109 { 0110 auto item = fonts.at(hashit->second); 0111 tag_alias(item, name_alias); 0112 return item; 0113 } 0114 0115 0116 QRawFont raw(data, 16); 0117 if ( !raw.isValid() ) 0118 return {}; 0119 0120 int index = QFontDatabase::addApplicationFontFromData(data); 0121 if ( index == -1 ) 0122 return {}; 0123 0124 hashes[hash] = index; 0125 0126 auto ptr = std::make_shared<CustomFontData>(raw, index, hash, data); 0127 fonts.emplace(index, ptr); 0128 tag_alias(ptr, name_alias); 0129 return ptr; 0130 } 0131 }; 0132 0133 glaxnimate::model::CustomFontDatabase::CustomFontDatabase() 0134 : d(std::make_unique<Private>()) 0135 { 0136 } 0137 0138 glaxnimate::model::CustomFontDatabase::~CustomFontDatabase() 0139 { 0140 } 0141 0142 glaxnimate::model::CustomFontDatabase & glaxnimate::model::CustomFontDatabase::instance() 0143 { 0144 static CustomFontDatabase instance; 0145 return instance; 0146 } 0147 0148 std::vector<glaxnimate::model::CustomFont> glaxnimate::model::CustomFontDatabase::fonts() const 0149 { 0150 std::vector<CustomFont> fonts; 0151 fonts.reserve(d->fonts.size()); 0152 for ( const auto& font : d->fonts ) 0153 fonts.emplace_back(font.second); 0154 return fonts; 0155 } 0156 0157 glaxnimate::model::CustomFont glaxnimate::model::CustomFontDatabase::add_font(const QString& name_alias, const QByteArray& ttf_data) 0158 { 0159 return d->install(name_alias, ttf_data); 0160 } 0161 0162 glaxnimate::model::CustomFont glaxnimate::model::CustomFontDatabase::get_font(int database_index) 0163 { 0164 auto it = d->fonts.find(database_index); 0165 if ( it == d->fonts.end() ) 0166 return {}; 0167 return it->second; 0168 } 0169 0170 QFont glaxnimate::model::CustomFontDatabase::font(const QString& family, const QString& style_name, qreal size) const 0171 { 0172 auto it = d->name_aliases.find(family); 0173 if ( it == d->name_aliases.end() ) 0174 { 0175 QFont font(family); 0176 font.setPointSizeF(size); 0177 font.setStyleName(style_name); 0178 return font; 0179 } 0180 0181 CustomFontData* match = d->fonts.at(it->second[0]).get(); 0182 for ( int id : it->second ) 0183 { 0184 const auto& font = d->fonts.at(id); 0185 if ( font->font.styleName() == style_name ) 0186 { 0187 match = font.get(); 0188 break; 0189 } 0190 } 0191 0192 QFont font(match->family_name()); 0193 font.setPointSizeF(size); 0194 font.setStyleName(style_name); 0195 return font; 0196 } 0197 0198 std::unordered_map<QString, std::set<QString>> glaxnimate::model::CustomFontDatabase::aliases() const 0199 { 0200 std::unordered_map<QString, std::set<QString>> map; 0201 0202 for ( const auto& p : d->name_aliases ) 0203 { 0204 std::set<QString> names; 0205 for ( const auto& id : p.second ) 0206 names.insert(d->fonts.at(id)->family_name()); 0207 map[p.first] = names; 0208 } 0209 0210 return map; 0211 } 0212 0213 0214 glaxnimate::model::CustomFont::CustomFont(CustomFontDatabase::DataPtr dd) 0215 : d(std::move(dd)) 0216 { 0217 if ( !d ) 0218 d = std::make_shared<CustomFontDatabase::CustomFontData>(); 0219 } 0220 0221 glaxnimate::model::CustomFont::CustomFont() 0222 : CustomFont(std::make_shared<CustomFontDatabase::CustomFontData>()) 0223 { 0224 } 0225 0226 glaxnimate::model::CustomFont::CustomFont(int database_index) 0227 : CustomFont(CustomFontDatabase::instance().get_font(database_index)) 0228 { 0229 } 0230 0231 glaxnimate::model::CustomFont::~CustomFont() 0232 { 0233 if ( d ) 0234 { 0235 int index = d->database_index; 0236 if ( index != -1 ) 0237 { 0238 d = {}; 0239 CustomFontDatabase::instance().d->remove_reference(index); 0240 } 0241 } 0242 } 0243 0244 bool glaxnimate::model::CustomFont::is_valid() const 0245 { 0246 return d->database_index != -1; 0247 } 0248 0249 int glaxnimate::model::CustomFont::database_index() const 0250 { 0251 return d->database_index; 0252 } 0253 0254 QString glaxnimate::model::CustomFont::family() const 0255 { 0256 return d->family_name(); 0257 } 0258 0259 QString glaxnimate::model::CustomFont::style_name() const 0260 { 0261 return d->font.styleName(); 0262 } 0263 0264 QFont glaxnimate::model::CustomFont::font(int size) const 0265 { 0266 QFont font(family(), size); 0267 font.setStyleName(style_name()); 0268 return font; 0269 } 0270 0271 const QRawFont & glaxnimate::model::CustomFont::raw_font() const 0272 { 0273 return d->font; 0274 } 0275 0276 0277 QByteArray glaxnimate::model::CustomFont::data() const 0278 { 0279 return d->data; 0280 } 0281 0282 void glaxnimate::model::CustomFont::set_css_url(const QString& url) 0283 { 0284 d->css_url = url; 0285 } 0286 0287 void glaxnimate::model::CustomFont::set_source_url(const QString& url) 0288 { 0289 d->source_url = url; 0290 } 0291 0292 const QString & glaxnimate::model::CustomFont::css_url() const 0293 { 0294 return d->css_url; 0295 } 0296 0297 const QString & glaxnimate::model::CustomFont::source_url() const 0298 { 0299 return d->source_url; 0300 }