Update to 5.0.0-beta1
[profile/ivi/qtdeclarative.git] / src / quick / util / qquickfontloader.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickfontloader_p.h"
43
44 #include <qqmlcontext.h>
45 #include <qqmlengine.h>
46
47 #include <QStringList>
48 #include <QUrl>
49 #include <QDebug>
50 #include <QNetworkRequest>
51 #include <QNetworkReply>
52 #include <QFontDatabase>
53
54 #include <private/qobject_p.h>
55 #include <qqmlinfo.h>
56 #include <qqmlfile.h>
57
58 QT_BEGIN_NAMESPACE
59
60 #define FONTLOADER_MAXIMUM_REDIRECT_RECURSION 16
61
62 class QQuickFontObject : public QObject
63 {
64 Q_OBJECT
65
66 public:
67     explicit QQuickFontObject(int _id = -1);
68
69     void download(const QUrl &url, QNetworkAccessManager *manager);
70
71 Q_SIGNALS:
72     void fontDownloaded(const QString&, QQuickFontLoader::Status);
73
74 private Q_SLOTS:
75     void replyFinished();
76
77 public:
78     int id;
79
80 private:
81     QNetworkReply *reply;
82     int redirectCount;
83
84     Q_DISABLE_COPY(QQuickFontObject)
85 };
86
87 QQuickFontObject::QQuickFontObject(int _id)
88     : QObject(0), id(_id), reply(0), redirectCount(0) {}
89
90
91 void QQuickFontObject::download(const QUrl &url, QNetworkAccessManager *manager)
92 {
93     QNetworkRequest req(url);
94     req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
95     reply = manager->get(req);
96     QObject::connect(reply, SIGNAL(finished()), this, SLOT(replyFinished()));
97 }
98
99 void QQuickFontObject::replyFinished()
100 {
101     if (reply) {
102         redirectCount++;
103         if (redirectCount < FONTLOADER_MAXIMUM_REDIRECT_RECURSION) {
104             QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
105             if (redirect.isValid()) {
106                 QUrl url = reply->url().resolved(redirect.toUrl());
107                 QNetworkAccessManager *manager = reply->manager();
108                 reply->deleteLater();
109                 reply = 0;
110                 download(url, manager);
111                 return;
112             }
113         }
114         redirectCount = 0;
115
116         if (!reply->error()) {
117             id = QFontDatabase::addApplicationFontFromData(reply->readAll());
118             if (id != -1)
119                 emit fontDownloaded(QFontDatabase::applicationFontFamilies(id).at(0), QQuickFontLoader::Ready);
120             else
121                 emit fontDownloaded(QString(), QQuickFontLoader::Error);
122         } else {
123             qWarning("%s: Unable to load font '%s': %s", Q_FUNC_INFO,
124                      qPrintable(reply->url().toString()), qPrintable(reply->errorString()));
125             emit fontDownloaded(QString(), QQuickFontLoader::Error);
126         }
127         reply->deleteLater();
128         reply = 0;
129     }
130 }
131
132
133 class QQuickFontLoaderPrivate : public QObjectPrivate
134 {
135     Q_DECLARE_PUBLIC(QQuickFontLoader)
136
137 public:
138     QQuickFontLoaderPrivate() : status(QQuickFontLoader::Null) {}
139
140     QUrl url;
141     QString name;
142     QQuickFontLoader::Status status;
143     static QHash<QUrl, QQuickFontObject*> fonts;
144 };
145
146 QHash<QUrl, QQuickFontObject*> QQuickFontLoaderPrivate::fonts;
147
148 /*!
149     \qmltype FontLoader
150     \instantiates QQuickFontLoader
151     \inqmlmodule QtQuick 2
152     \ingroup qtquick-text-utility
153     \brief Allows fonts to be loaded by name or URL
154
155     The FontLoader type is used to load fonts by name or URL.
156
157     The \l status indicates when the font has been loaded, which is useful
158     for fonts loaded from remote sources.
159
160     For example:
161     \qml
162     import QtQuick 2.0
163
164     Column {
165         FontLoader { id: fixedFont; name: "Courier" }
166         FontLoader { id: webFont; source: "http://www.mysite.com/myfont.ttf" }
167
168         Text { text: "Fixed-size font"; font.family: fixedFont.name }
169         Text { text: "Fancy font"; font.family: webFont.name }
170     }
171     \endqml
172
173     \sa {declarative/text/fonts}{Fonts example}
174 */
175 QQuickFontLoader::QQuickFontLoader(QObject *parent)
176     : QObject(*(new QQuickFontLoaderPrivate), parent)
177 {
178 }
179
180 QQuickFontLoader::~QQuickFontLoader()
181 {
182 }
183
184 /*!
185     \qmlproperty url QtQuick2::FontLoader::source
186     The url of the font to load.
187 */
188 QUrl QQuickFontLoader::source() const
189 {
190     Q_D(const QQuickFontLoader);
191     return d->url;
192 }
193
194 void QQuickFontLoader::setSource(const QUrl &url)
195 {
196     Q_D(QQuickFontLoader);
197     if (url == d->url)
198         return;
199     d->url = url;
200     emit sourceChanged();
201
202     QString localFile = QQmlFile::urlToLocalFileOrQrc(d->url);
203     if (!localFile.isEmpty()) {
204         if (!d->fonts.contains(d->url)) {
205             int id = QFontDatabase::addApplicationFont(localFile);
206             if (id != -1) {
207                 updateFontInfo(QFontDatabase::applicationFontFamilies(id).at(0), Ready);
208                 QQuickFontObject *fo = new QQuickFontObject(id);
209                 d->fonts[d->url] = fo;
210             } else {
211                 updateFontInfo(QString(), Error);
212             }
213         } else {
214             updateFontInfo(QFontDatabase::applicationFontFamilies(d->fonts[d->url]->id).at(0), Ready);
215         }
216     } else {
217         if (!d->fonts.contains(d->url)) {
218             QQuickFontObject *fo = new QQuickFontObject;
219             d->fonts[d->url] = fo;
220             fo->download(d->url, qmlEngine(this)->networkAccessManager());
221             d->status = Loading;
222             emit statusChanged();
223             QObject::connect(fo, SIGNAL(fontDownloaded(QString,QQuickFontLoader::Status)),
224                 this, SLOT(updateFontInfo(QString,QQuickFontLoader::Status)));
225         } else {
226             QQuickFontObject *fo = d->fonts[d->url];
227             if (fo->id == -1) {
228                 d->status = Loading;
229                 emit statusChanged();
230                 QObject::connect(fo, SIGNAL(fontDownloaded(QString,QQuickFontLoader::Status)),
231                     this, SLOT(updateFontInfo(QString,QQuickFontLoader::Status)));
232             }
233             else
234                 updateFontInfo(QFontDatabase::applicationFontFamilies(fo->id).at(0), Ready);
235         }
236     }
237 }
238
239 void QQuickFontLoader::updateFontInfo(const QString& name, QQuickFontLoader::Status status)
240 {
241     Q_D(QQuickFontLoader);
242
243     if (name != d->name) {
244         d->name = name;
245         emit nameChanged();
246     }
247     if (status != d->status) {
248         if (status == Error)
249             qmlInfo(this) << "Cannot load font: \"" << d->url.toString() << '"';
250         d->status = status;
251         emit statusChanged();
252     }
253 }
254
255 /*!
256     \qmlproperty string QtQuick2::FontLoader::name
257
258     This property holds the name of the font family.
259     It is set automatically when a font is loaded using the \c url property.
260
261     Use this to set the \c font.family property of a \c Text item.
262
263     Example:
264     \qml
265     Item {
266         width: 200; height: 50
267
268         FontLoader {
269             id: webFont
270             source: "http://www.mysite.com/myfont.ttf"
271         }
272         Text {
273             text: "Fancy font"
274             font.family: webFont.name
275         }
276     }
277     \endqml
278 */
279 QString QQuickFontLoader::name() const
280 {
281     Q_D(const QQuickFontLoader);
282     return d->name;
283 }
284
285 void QQuickFontLoader::setName(const QString &name)
286 {
287     Q_D(QQuickFontLoader);
288     if (d->name == name)
289         return;
290     d->name = name;
291     emit nameChanged();
292     d->status = Ready;
293     emit statusChanged();
294 }
295
296 /*!
297     \qmlproperty enumeration QtQuick2::FontLoader::status
298
299     This property holds the status of font loading.  It can be one of:
300     \list
301     \li FontLoader.Null - no font has been set
302     \li FontLoader.Ready - the font has been loaded
303     \li FontLoader.Loading - the font is currently being loaded
304     \li FontLoader.Error - an error occurred while loading the font
305     \endlist
306
307     Use this status to provide an update or respond to the status change in some way.
308     For example, you could:
309
310     \list
311     \li Trigger a state change:
312     \qml
313         State { name: 'loaded'; when: loader.status == FontLoader.Ready }
314     \endqml
315
316     \li Implement an \c onStatusChanged signal handler:
317     \qml
318         FontLoader {
319             id: loader
320             onStatusChanged: if (loader.status == FontLoader.Ready) console.log('Loaded')
321         }
322     \endqml
323
324     \li Bind to the status value:
325     \qml
326         Text { text: loader.status == FontLoader.Ready ? 'Loaded' : 'Not loaded' }
327     \endqml
328     \endlist
329 */
330 QQuickFontLoader::Status QQuickFontLoader::status() const
331 {
332     Q_D(const QQuickFontLoader);
333     return d->status;
334 }
335
336 QT_END_NAMESPACE
337
338 #include <qquickfontloader.moc>