1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickfontloader_p.h"
5
6#include <qqmlcontext.h>
7#include <qqmlengine.h>
8
9#include <QStringList>
10#include <QUrl>
11#include <QDebug>
12
13#include <QFontDatabase>
14
15#include <private/qobject_p.h>
16#include <qqmlinfo.h>
17#include <qqmlfile.h>
18
19#if QT_CONFIG(qml_network)
20#include <QNetworkRequest>
21#include <QNetworkReply>
22#endif
23
24#include <QtCore/QCoreApplication>
25#include <QtCore/private/qduplicatetracker_p.h>
26
27#include <QtGui/private/qfontdatabase_p.h>
28
29QT_BEGIN_NAMESPACE
30
31class QQuickFontObject : public QObject
32{
33Q_OBJECT
34
35public:
36 explicit QQuickFontObject(int _id = -1);
37
38#if QT_CONFIG(qml_network)
39 void download(const QUrl &url, QNetworkAccessManager *manager);
40
41Q_SIGNALS:
42 void fontDownloaded(int id);
43
44private:
45 QNetworkReply *reply = nullptr;
46
47private Q_SLOTS:
48 void replyFinished();
49#endif // qml_network
50
51public:
52 int id;
53
54 Q_DISABLE_COPY(QQuickFontObject)
55};
56
57QQuickFontObject::QQuickFontObject(int _id)
58 : QObject(nullptr), id(_id)
59{
60}
61
62#if QT_CONFIG(qml_network)
63void QQuickFontObject::download(const QUrl &url, QNetworkAccessManager *manager)
64{
65 QNetworkRequest req(url);
66 req.setAttribute(code: QNetworkRequest::HttpPipeliningAllowedAttribute, value: true);
67 reply = manager->get(request: req);
68 QObject::connect(sender: reply, SIGNAL(finished()), receiver: this, SLOT(replyFinished()));
69}
70
71void QQuickFontObject::replyFinished()
72{
73 if (reply) {
74 if (!reply->error()) {
75 id = QFontDatabase::addApplicationFontFromData(fontData: reply->readAll());
76 emit fontDownloaded(id);
77 } else {
78 qWarning(msg: "%s: Unable to load font '%s': %s", Q_FUNC_INFO,
79 qPrintable(reply->url().toString()), qPrintable(reply->errorString()));
80 emit fontDownloaded(id: -1);
81 }
82 reply->deleteLater();
83 reply = nullptr;
84 }
85}
86#endif // qml_network
87
88class QQuickFontLoaderPrivate : public QObjectPrivate
89{
90 Q_DECLARE_PUBLIC(QQuickFontLoader)
91
92public:
93 QQuickFontLoaderPrivate() {}
94
95 QUrl url;
96 QFont font;
97 QQuickFontLoader::Status status = QQuickFontLoader::Null;
98};
99
100static void q_QFontLoaderFontsStaticReset();
101static void q_QFontLoaderFontsAddReset()
102{
103 qAddPostRoutine(q_QFontLoaderFontsStaticReset);
104}
105class QFontLoaderFonts
106{
107public:
108 QFontLoaderFonts()
109 {
110 qAddPostRoutine(q_QFontLoaderFontsStaticReset);
111 qAddPreRoutine(q_QFontLoaderFontsAddReset);
112 }
113
114 ~QFontLoaderFonts()
115 {
116 qRemovePostRoutine(q_QFontLoaderFontsStaticReset);
117 reset();
118 }
119
120
121 void reset()
122 {
123 QDuplicateTracker<QQuickFontObject *, 256> deleted(map.size());
124 for (QQuickFontObject *fo : std::as_const(t&: map)) {
125 if (!deleted.hasSeen(s: fo))
126 delete fo;
127 }
128 map.clear();
129 }
130
131 QHash<QUrl, QQuickFontObject *> map;
132};
133Q_GLOBAL_STATIC(QFontLoaderFonts, fontLoaderFonts);
134
135static void q_QFontLoaderFontsStaticReset()
136{
137 fontLoaderFonts()->reset();
138}
139
140/*!
141 \qmltype FontLoader
142 \nativetype QQuickFontLoader
143 \inqmlmodule QtQuick
144 \ingroup qtquick-text-utility
145 \brief Allows fonts to be loaded by URL.
146
147 The FontLoader type is used to load fonts by URL.
148
149 The \l status indicates when the font has been loaded, which is useful
150 for fonts loaded from remote sources.
151
152 For example:
153 \qml
154 import QtQuick 2.0
155
156 Column {
157 FontLoader { id: webFont; source: "http://www.mysite.com/myfont.ttf" }
158
159 Text { text: "Fancy font"; font: webFont.font }
160 }
161 \endqml
162
163 \sa {Qt Quick Examples - Text#Fonts}{Qt Quick Examples - Text Fonts}
164*/
165QQuickFontLoader::QQuickFontLoader(QObject *parent)
166 : QObject(*(new QQuickFontLoaderPrivate), parent)
167{
168 connect(sender: this, signal: &QQuickFontLoader::fontChanged, context: this, slot: &QQuickFontLoader::nameChanged);
169}
170
171/*!
172 \qmlproperty url QtQuick::FontLoader::source
173 The URL of the font to load.
174*/
175QUrl QQuickFontLoader::source() const
176{
177 Q_D(const QQuickFontLoader);
178 return d->url;
179}
180
181void QQuickFontLoader::setSource(const QUrl &url)
182{
183 Q_D(QQuickFontLoader);
184 if (url == d->url)
185 return;
186 d->url = url;
187 emit sourceChanged();
188
189 const QQmlContext *context = qmlContext(this);
190 const QUrl &resolvedUrl = context ? context->resolvedUrl(d->url) : d->url;
191 QString localFile = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
192 if (!localFile.isEmpty()) {
193 if (!fontLoaderFonts()->map.contains(key: resolvedUrl)) {
194 int id = QFontDatabase::addApplicationFont(fileName: localFile);
195 updateFontInfo(id);
196 if (id != -1) {
197 QQuickFontObject *fo = new QQuickFontObject(id);
198 fontLoaderFonts()->map[resolvedUrl] = fo;
199 }
200 } else {
201 updateFontInfo(fontLoaderFonts()->map.value(key: resolvedUrl)->id);
202 }
203 } else {
204 if (!fontLoaderFonts()->map.contains(key: resolvedUrl)) {
205 Q_ASSERT(context);
206#if QT_CONFIG(qml_network)
207 QQuickFontObject *fo = new QQuickFontObject;
208 fontLoaderFonts()->map[resolvedUrl] = fo;
209 fo->download(url: resolvedUrl, manager: context->engine()->networkAccessManager());
210 d->status = Loading;
211 emit statusChanged();
212 QObject::connect(sender: fo, SIGNAL(fontDownloaded(int)),
213 receiver: this, SLOT(updateFontInfo(int)));
214#else
215// Silently fail if compiled with no_network
216#endif
217 } else {
218 QQuickFontObject *fo = fontLoaderFonts()->map.value(key: resolvedUrl);
219 if (fo->id == -1) {
220#if QT_CONFIG(qml_network)
221 d->status = Loading;
222 emit statusChanged();
223 QObject::connect(sender: fo, SIGNAL(fontDownloaded(int)),
224 receiver: this, SLOT(updateFontInfo(int)));
225#else
226// Silently fail if compiled with no_network
227#endif
228 }
229 else
230 updateFontInfo(fo->id);
231 }
232 }
233}
234
235void QQuickFontLoader::updateFontInfo(int id)
236{
237 Q_D(QQuickFontLoader);
238
239 QFont font;
240
241 QQuickFontLoader::Status status = Error;
242 if (id >= 0) {
243 QFontDatabasePrivate *p = QFontDatabasePrivate::instance();
244 if (id < p->applicationFonts.size()) {
245 const QFontDatabasePrivate::ApplicationFont &applicationFont = p->applicationFonts.at(i: id);
246
247 if (!applicationFont.properties.isEmpty()) {
248 const QFontDatabasePrivate::ApplicationFont::Properties &properties = applicationFont.properties.at(i: 0);
249 font.setFamily(properties.familyName);
250 font.setStyleName(properties.styleName);
251 font.setWeight(QFont::Weight(properties.weight));
252 font.setStyle(properties.style);
253 font.setStretch(properties.stretch);
254 }
255 }
256
257 status = Ready;
258 }
259
260 if (font != d->font) {
261 d->font = font;
262 emit fontChanged();
263 }
264
265 if (status != d->status) {
266 if (status == Error) {
267 const QQmlContext *context = qmlContext(this);
268 qmlWarning(me: this) << "Cannot load font: \""
269 << (context ? context->resolvedUrl(d->url) : d->url).toString() << '"';
270 }
271 d->status = status;
272 emit statusChanged();
273 }
274}
275
276/*!
277 \qmlproperty font QtQuick::FontLoader::font
278 \since 6.0
279
280 This property holds a default query for the loaded font.
281
282 You can use this to select the font if other properties than just the
283 family name are needed to disambiguate. You can either specify the
284 font using individual properties:
285
286 \qml
287 Item {
288 width: 200; height: 50
289
290 FontLoader {
291 id: webFont
292 source: "http://www.mysite.com/myfont.ttf"
293 }
294 Text {
295 text: "Fancy font"
296 font.family: webFont.font.family
297 font.weight: webFont.font.weight
298 font.styleName: webFont.font.styleName
299 font.pixelSize: 24
300 }
301 }
302 \endqml
303
304 Or you can set the full font query directly:
305
306 \qml
307 Item {
308 width: 200; height: 50
309
310 FontLoader {
311 id: webFont
312 source: "http://www.mysite.com/myfont.ttf"
313 }
314 Text {
315 text: "Fancy font"
316 font: webFont.font
317 }
318 }
319 \endqml
320
321 In this case, the default font query will be used with no modifications
322 (so font size, for instance, will be the system default).
323*/
324QFont QQuickFontLoader::font() const
325{
326 Q_D(const QQuickFontLoader);
327 return d->font;
328}
329
330/*!
331 \qmlproperty string QtQuick::FontLoader::name
332 \readonly
333
334 This property holds the name of the font family.
335 It is set automatically when a font is loaded using the \l source property.
336
337 This is equivalent to the family property of the FontLoader's \l font property.
338
339 Use this to set the \c font.family property of a \c Text item.
340
341 Example:
342 \qml
343 Item {
344 width: 200; height: 50
345
346 FontLoader {
347 id: webFont
348 source: "http://www.mysite.com/myfont.ttf"
349 }
350 Text {
351 text: "Fancy font"
352 font.family: webFont.name
353 }
354 }
355 \endqml
356*/
357QString QQuickFontLoader::name() const
358{
359 Q_D(const QQuickFontLoader);
360 return d->font.resolveMask() == 0 ? QString() : d->font.family();
361}
362
363/*!
364 \qmlproperty enumeration QtQuick::FontLoader::status
365
366 This property holds the status of font loading. It can be one of:
367
368 \value FontLoader.Null no font has been set
369 \value FontLoader.Ready the font has been loaded
370 \value FontLoader.Loading the font is currently being loaded
371 \value FontLoader.Error an error occurred while loading the font
372
373 Use this status to provide an update or respond to the status change in some way.
374 For example, you could:
375
376 \list
377 \li Trigger a state change:
378 \qml
379 State { name: 'loaded'; when: loader.status == FontLoader.Ready }
380 \endqml
381
382 \li Implement an \c onStatusChanged signal handler:
383 \qml
384 FontLoader {
385 id: loader
386 onStatusChanged: if (loader.status == FontLoader.Ready) console.log('Loaded')
387 }
388 \endqml
389
390 \li Bind to the status value:
391 \qml
392 Text { text: loader.status == FontLoader.Ready ? 'Loaded' : 'Not loaded' }
393 \endqml
394 \endlist
395*/
396QQuickFontLoader::Status QQuickFontLoader::status() const
397{
398 Q_D(const QQuickFontLoader);
399 return d->status;
400}
401
402QT_END_NAMESPACE
403
404#include <qquickfontloader.moc>
405
406#include "moc_qquickfontloader_p.cpp"
407

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtdeclarative/src/quick/util/qquickfontloader.cpp