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