Initial bundle support
[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             emit fontDownloaded(QString(), QQuickFontLoader::Error);
124         }
125         reply->deleteLater();
126         reply = 0;
127     }
128 }
129
130
131 class QQuickFontLoaderPrivate : public QObjectPrivate
132 {
133     Q_DECLARE_PUBLIC(QQuickFontLoader)
134
135 public:
136     QQuickFontLoaderPrivate() : status(QQuickFontLoader::Null) {}
137
138     QUrl url;
139     QString name;
140     QQuickFontLoader::Status status;
141     static QHash<QUrl, QQuickFontObject*> fonts;
142 };
143
144 QHash<QUrl, QQuickFontObject*> QQuickFontLoaderPrivate::fonts;
145
146 /*!
147     \qmlclass FontLoader QQuickFontLoader
148     \inqmlmodule QtQuick 2
149   \ingroup qml-utility-elements
150     \brief The FontLoader element allows fonts to be loaded by name or URL.
151
152     The FontLoader element is used to load fonts by name or URL. 
153     
154     The \l status indicates when the font has been loaded, which is useful 
155     for fonts loaded from remote sources.
156
157     For example:
158     \qml
159     import QtQuick 2.0
160
161     Column { 
162         FontLoader { id: fixedFont; name: "Courier" }
163         FontLoader { id: webFont; source: "http://www.mysite.com/myfont.ttf" }
164
165         Text { text: "Fixed-size font"; font.family: fixedFont.name }
166         Text { text: "Fancy font"; font.family: webFont.name }
167     }
168     \endqml
169
170     \sa {declarative/text/fonts}{Fonts example}
171 */
172 QQuickFontLoader::QQuickFontLoader(QObject *parent)
173     : QObject(*(new QQuickFontLoaderPrivate), parent)
174 {
175 }
176
177 QQuickFontLoader::~QQuickFontLoader()
178 {
179 }
180
181 /*!
182     \qmlproperty url QtQuick2::FontLoader::source
183     The url of the font to load.
184 */
185 QUrl QQuickFontLoader::source() const
186 {
187     Q_D(const QQuickFontLoader);
188     return d->url;
189 }
190
191 void QQuickFontLoader::setSource(const QUrl &url)
192 {
193     Q_D(QQuickFontLoader);
194     if (url == d->url)
195         return;
196     d->url = url;
197     emit sourceChanged();
198
199     QString localFile = QQmlFile::urlToLocalFileOrQrc(d->url);
200     if (!localFile.isEmpty()) {
201         if (!d->fonts.contains(d->url)) {
202             int id = QFontDatabase::addApplicationFont(localFile);
203             if (id != -1) {
204                 updateFontInfo(QFontDatabase::applicationFontFamilies(id).at(0), Ready);
205                 QQuickFontObject *fo = new QQuickFontObject(id);
206                 d->fonts[d->url] = fo;
207             } else {
208                 updateFontInfo(QString(), Error);
209             }
210         } else {
211             updateFontInfo(QFontDatabase::applicationFontFamilies(d->fonts[d->url]->id).at(0), Ready);
212         }
213     } else {
214         if (!d->fonts.contains(d->url)) {
215             QQuickFontObject *fo = new QQuickFontObject;
216             d->fonts[d->url] = fo;
217             fo->download(d->url, qmlEngine(this)->networkAccessManager());
218             d->status = Loading;
219             emit statusChanged();
220             QObject::connect(fo, SIGNAL(fontDownloaded(QString,QQuickFontLoader::Status)),
221                 this, SLOT(updateFontInfo(QString,QQuickFontLoader::Status)));
222         } else {
223             QQuickFontObject *fo = d->fonts[d->url];
224             if (fo->id == -1) {
225                 d->status = Loading;
226                 emit statusChanged();
227                 QObject::connect(fo, SIGNAL(fontDownloaded(QString,QQuickFontLoader::Status)),
228                     this, SLOT(updateFontInfo(QString,QQuickFontLoader::Status)));
229             }
230             else
231                 updateFontInfo(QFontDatabase::applicationFontFamilies(fo->id).at(0), Ready);
232         }
233     }
234 }
235
236 void QQuickFontLoader::updateFontInfo(const QString& name, QQuickFontLoader::Status status)
237 {
238     Q_D(QQuickFontLoader);
239
240     if (name != d->name) {
241         d->name = name;
242         emit nameChanged();
243     }
244     if (status != d->status) {
245         if (status == Error)
246             qmlInfo(this) << "Cannot load font: \"" << d->url.toString() << "\"";
247         d->status = status;
248         emit statusChanged();
249     }
250 }
251
252 /*!
253     \qmlproperty string QtQuick2::FontLoader::name
254
255     This property holds the name of the font family.
256     It is set automatically when a font is loaded using the \c url property.
257
258     Use this to set the \c font.family property of a \c Text item.
259
260     Example:
261     \qml
262     Item {
263         width: 200; height: 50
264
265         FontLoader {
266             id: webFont
267             source: "http://www.mysite.com/myfont.ttf"
268         }
269         Text {
270             text: "Fancy font"
271             font.family: webFont.name
272         }
273     }
274     \endqml
275 */
276 QString QQuickFontLoader::name() const
277 {
278     Q_D(const QQuickFontLoader);
279     return d->name;
280 }
281
282 void QQuickFontLoader::setName(const QString &name)
283 {
284     Q_D(QQuickFontLoader);
285     if (d->name == name)
286         return;
287     d->name = name;
288     emit nameChanged();
289     d->status = Ready;
290     emit statusChanged();
291 }
292
293 /*!
294     \qmlproperty enumeration QtQuick2::FontLoader::status
295
296     This property holds the status of font loading.  It can be one of:
297     \list
298     \li FontLoader.Null - no font has been set
299     \li FontLoader.Ready - the font has been loaded
300     \li FontLoader.Loading - the font is currently being loaded
301     \li FontLoader.Error - an error occurred while loading the font
302     \endlist
303
304     Use this status to provide an update or respond to the status change in some way.
305     For example, you could:
306
307     \list
308     \li Trigger a state change:
309     \qml
310         State { name: 'loaded'; when: loader.status == FontLoader.Ready }
311     \endqml
312
313     \li Implement an \c onStatusChanged signal handler:
314     \qml
315         FontLoader {
316             id: loader
317             onStatusChanged: if (loader.status == FontLoader.Ready) console.log('Loaded')
318         }
319     \endqml
320
321     \li Bind to the status value:
322     \qml
323         Text { text: loader.status == FontLoader.Ready ? 'Loaded' : 'Not loaded' }
324     \endqml
325     \endlist
326 */
327 QQuickFontLoader::Status QQuickFontLoader::status() const
328 {
329     Q_D(const QQuickFontLoader);
330     return d->status;
331 }
332
333 QT_END_NAMESPACE
334
335 #include <qquickfontloader.moc>