Initial import from the monolithic Qt.
[profile/ivi/qtdeclarative.git] / src / declarative / util / qdeclarativefontloader.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "private/qdeclarativefontloader_p.h"
43
44 #include <qdeclarativecontext.h>
45 #include <qdeclarativeengine.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 <private/qdeclarativeengine_p.h>
56 #include <qdeclarativeinfo.h>
57
58 QT_BEGIN_NAMESPACE
59
60 #define FONTLOADER_MAXIMUM_REDIRECT_RECURSION 16
61
62 class QDeclarativeFontObject : public QObject
63 {
64 Q_OBJECT
65
66 public:
67     QDeclarativeFontObject(int _id);
68
69     void download(const QUrl &url, QNetworkAccessManager *manager);
70
71 Q_SIGNALS:
72     void fontDownloaded(const QString&, QDeclarativeFontLoader::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(QDeclarativeFontObject)
85 };
86
87 QDeclarativeFontObject::QDeclarativeFontObject(int _id = -1)
88     : QObject(0), id(_id), reply(0), redirectCount(0) {}
89
90
91 void QDeclarativeFontObject::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 QDeclarativeFontObject::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), QDeclarativeFontLoader::Ready);
120             else
121                 emit fontDownloaded(QString(), QDeclarativeFontLoader::Error);
122         } else {
123             emit fontDownloaded(QString(), QDeclarativeFontLoader::Error);
124         }
125         reply->deleteLater();
126         reply = 0;
127     }
128 }
129
130
131 class QDeclarativeFontLoaderPrivate : public QObjectPrivate
132 {
133     Q_DECLARE_PUBLIC(QDeclarativeFontLoader)
134
135 public:
136     QDeclarativeFontLoaderPrivate() : status(QDeclarativeFontLoader::Null) {}
137
138     QUrl url;
139     QString name;
140     QDeclarativeFontLoader::Status status;
141     static QHash<QUrl, QDeclarativeFontObject*> fonts;
142 };
143
144 QHash<QUrl, QDeclarativeFontObject*> QDeclarativeFontLoaderPrivate::fonts;
145
146 /*!
147     \qmlclass FontLoader QDeclarativeFontLoader
148   \ingroup qml-utility-elements
149     \since 4.7
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 1.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 QDeclarativeFontLoader::QDeclarativeFontLoader(QObject *parent)
173     : QObject(*(new QDeclarativeFontLoaderPrivate), parent)
174 {
175 }
176
177 QDeclarativeFontLoader::~QDeclarativeFontLoader()
178 {
179 }
180
181 /*!
182     \qmlproperty url FontLoader::source
183     The url of the font to load.
184 */
185 QUrl QDeclarativeFontLoader::source() const
186 {
187     Q_D(const QDeclarativeFontLoader);
188     return d->url;
189 }
190
191 void QDeclarativeFontLoader::setSource(const QUrl &url)
192 {
193     Q_D(QDeclarativeFontLoader);
194     if (url == d->url)
195         return;
196     d->url = qmlContext(this)->resolvedUrl(url);
197     emit sourceChanged();
198
199 #ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
200     QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url);
201     if (!localFile.isEmpty()) {
202         if (!d->fonts.contains(d->url)) {
203             int id = QFontDatabase::addApplicationFont(localFile);
204             if (id != -1) {
205                 updateFontInfo(QFontDatabase::applicationFontFamilies(id).at(0), Ready);
206                 QDeclarativeFontObject *fo = new QDeclarativeFontObject(id);
207                 d->fonts[d->url] = fo;
208             } else {
209                 updateFontInfo(QString(), Error);
210             }
211         } else {
212             updateFontInfo(QFontDatabase::applicationFontFamilies(d->fonts[d->url]->id).at(0), Ready);
213         }
214     } else
215 #endif
216     {
217         if (!d->fonts.contains(d->url)) {
218             QDeclarativeFontObject *fo = new QDeclarativeFontObject;
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,QDeclarativeFontLoader::Status)),
224                 this, SLOT(updateFontInfo(QString,QDeclarativeFontLoader::Status)));
225         } else {
226             QDeclarativeFontObject *fo = d->fonts[d->url];
227             if (fo->id == -1) {
228                 d->status = Loading;
229                 emit statusChanged();
230                 QObject::connect(fo, SIGNAL(fontDownloaded(QString,QDeclarativeFontLoader::Status)),
231                     this, SLOT(updateFontInfo(QString,QDeclarativeFontLoader::Status)));
232             }
233             else
234                 updateFontInfo(QFontDatabase::applicationFontFamilies(fo->id).at(0), Ready);
235         }
236     }
237 }
238
239 void QDeclarativeFontLoader::updateFontInfo(const QString& name, QDeclarativeFontLoader::Status status)
240 {
241     Q_D(QDeclarativeFontLoader);
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 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 QDeclarativeFontLoader::name() const
280 {
281     Q_D(const QDeclarativeFontLoader);
282     return d->name;
283 }
284
285 void QDeclarativeFontLoader::setName(const QString &name)
286 {
287     Q_D(QDeclarativeFontLoader);
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 FontLoader::status
298
299     This property holds the status of font loading.  It can be one of:
300     \list
301     \o FontLoader.Null - no font has been set
302     \o FontLoader.Ready - the font has been loaded
303     \o FontLoader.Loading - the font is currently being loaded
304     \o 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     \o Trigger a state change:
312     \qml
313         State { name: 'loaded'; when: loader.status == FontLoader.Ready }
314     \endqml
315
316     \o 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     \o Bind to the status value:
325     \qml
326         Text { text: loader.status == FontLoader.Ready ? 'Loaded' : 'Not loaded' }
327     \endqml
328     \endlist
329 */
330 QDeclarativeFontLoader::Status QDeclarativeFontLoader::status() const
331 {
332     Q_D(const QDeclarativeFontLoader);
333     return d->status;
334 }
335
336 QT_END_NAMESPACE
337
338 #include <qdeclarativefontloader.moc>