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 }