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 QtGui module of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
10 | ** Commercial Usage
|
---|
11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
13 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
14 | ** a written agreement between you and Nokia.
|
---|
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 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you have questions regarding the use of this file, please contact
|
---|
37 | ** Nokia at qt-info@nokia.com.
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
41 |
|
---|
42 | #include <private/qapplication_p.h>
|
---|
43 | #include "qdir.h"
|
---|
44 | #include "qfont_p.h"
|
---|
45 | #include "qfontengine_s60_p.h"
|
---|
46 | #include "qabstractfileengine.h"
|
---|
47 | #include "qdesktopservices.h"
|
---|
48 | #include "qtemporaryfile.h"
|
---|
49 | #include "qtextcodec.h"
|
---|
50 | #include <private/qpixmap_s60_p.h>
|
---|
51 | #include <private/qt_s60_p.h>
|
---|
52 | #include "qendian.h"
|
---|
53 | #include <private/qcore_symbian_p.h>
|
---|
54 | #ifdef QT_NO_FREETYPE
|
---|
55 | #include <openfont.h>
|
---|
56 | #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
|
---|
57 | #include <graphics/openfontrasterizer.h> // COpenFontRasterizer has moved to a new header file
|
---|
58 | #endif // SYMBIAN_ENABLE_SPLIT_HEADERS
|
---|
59 | #endif // QT_NO_FREETYPE
|
---|
60 |
|
---|
61 | QT_BEGIN_NAMESPACE
|
---|
62 |
|
---|
63 | QStringList qt_symbian_fontFamiliesOnFontServer() // Also used in qfont_s60.cpp
|
---|
64 | {
|
---|
65 | QStringList result;
|
---|
66 | QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
|
---|
67 | const int numTypeFaces = S60->screenDevice()->NumTypefaces();
|
---|
68 | for (int i = 0; i < numTypeFaces; i++) {
|
---|
69 | TTypefaceSupport typefaceSupport;
|
---|
70 | S60->screenDevice()->TypefaceSupport(typefaceSupport, i);
|
---|
71 | const QString familyName((const QChar *)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
|
---|
72 | result.append(familyName);
|
---|
73 | }
|
---|
74 | lock.relock();
|
---|
75 | return result;
|
---|
76 | }
|
---|
77 |
|
---|
78 | QFileInfoList alternativeFilePaths(const QString &path, const QStringList &nameFilters,
|
---|
79 | QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort,
|
---|
80 | bool uniqueFileNames = true)
|
---|
81 | {
|
---|
82 | QFileInfoList result;
|
---|
83 |
|
---|
84 | // Prepare a 'soft to hard' drive list: W:, X: ... A:, Z:
|
---|
85 | QStringList driveStrings;
|
---|
86 | foreach (const QFileInfo &drive, QDir::drives())
|
---|
87 | driveStrings.append(drive.absolutePath());
|
---|
88 | driveStrings.sort();
|
---|
89 | const QString zDriveString(QLatin1String("Z:/"));
|
---|
90 | driveStrings.removeAll(zDriveString);
|
---|
91 | driveStrings.prepend(zDriveString);
|
---|
92 |
|
---|
93 | QStringList uniqueFileNameList;
|
---|
94 | for (int i = driveStrings.count() - 1; i >= 0; --i) {
|
---|
95 | const QDir dirOnDrive(driveStrings.at(i) + path);
|
---|
96 | const QFileInfoList entriesOnDrive = dirOnDrive.entryInfoList(nameFilters, filters, sort);
|
---|
97 | if (uniqueFileNames) {
|
---|
98 | foreach(const QFileInfo &entry, entriesOnDrive) {
|
---|
99 | if (!uniqueFileNameList.contains(entry.fileName())) {
|
---|
100 | uniqueFileNameList.append(entry.fileName());
|
---|
101 | result.append(entry);
|
---|
102 | }
|
---|
103 | }
|
---|
104 | } else {
|
---|
105 | result.append(entriesOnDrive);
|
---|
106 | }
|
---|
107 | }
|
---|
108 | return result;
|
---|
109 | }
|
---|
110 |
|
---|
111 | #ifdef QT_NO_FREETYPE
|
---|
112 | class QSymbianFontDatabaseExtrasImplementation : public QSymbianFontDatabaseExtras
|
---|
113 | {
|
---|
114 | public:
|
---|
115 | QSymbianFontDatabaseExtrasImplementation();
|
---|
116 | ~QSymbianFontDatabaseExtrasImplementation();
|
---|
117 |
|
---|
118 | const QSymbianTypeFaceExtras *extras(const QString &typeface, bool bold, bool italic) const;
|
---|
119 | void removeAppFontData(QFontDatabasePrivate::ApplicationFont *fnt);
|
---|
120 | static inline bool appFontLimitReached();
|
---|
121 | TUid addFontFileToFontStore(const QFileInfo &fontFileInfo);
|
---|
122 | static void clear();
|
---|
123 |
|
---|
124 | static inline QString tempAppFontFolder();
|
---|
125 | static const QString appFontMarkerPrefix;
|
---|
126 | static QString appFontMarker(); // 'qaf<shortUid[+shortPid]>'
|
---|
127 |
|
---|
128 | struct CFontFromFontStoreReleaser {
|
---|
129 | static inline void cleanup(CFont *font)
|
---|
130 | {
|
---|
131 | if (!font)
|
---|
132 | return;
|
---|
133 | const QSymbianFontDatabaseExtrasImplementation *dbExtras =
|
---|
134 | static_cast<const QSymbianFontDatabaseExtrasImplementation*>(privateDb()->symbianExtras);
|
---|
135 | dbExtras->m_store->ReleaseFont(font);
|
---|
136 | }
|
---|
137 | };
|
---|
138 |
|
---|
139 | struct CFontFromScreenDeviceReleaser {
|
---|
140 | static inline void cleanup(CFont *font)
|
---|
141 | {
|
---|
142 | if (!font)
|
---|
143 | return;
|
---|
144 | S60->screenDevice()->ReleaseFont(font);
|
---|
145 | }
|
---|
146 | };
|
---|
147 |
|
---|
148 | // m_heap, m_store, m_rasterizer and m_extras are used if Symbian
|
---|
149 | // does not provide the Font Table API
|
---|
150 | RHeap* m_heap;
|
---|
151 | CFontStore *m_store;
|
---|
152 | COpenFontRasterizer *m_rasterizer;
|
---|
153 | mutable QList<const QSymbianTypeFaceExtras *> m_extras;
|
---|
154 |
|
---|
155 | mutable QHash<QString, const QSymbianTypeFaceExtras *> m_extrasHash;
|
---|
156 | mutable QSet<QString> m_applicationFontFamilies;
|
---|
157 | };
|
---|
158 |
|
---|
159 | const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix =
|
---|
160 | QLatin1String("Q");
|
---|
161 |
|
---|
162 | inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
|
---|
163 | {
|
---|
164 | return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\');
|
---|
165 | }
|
---|
166 |
|
---|
167 | QString QSymbianFontDatabaseExtrasImplementation::appFontMarker()
|
---|
168 | {
|
---|
169 | static QString result;
|
---|
170 | if (result.isEmpty()) {
|
---|
171 | quint16 id = 0;
|
---|
172 | if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
|
---|
173 | // We are allowed to load app fonts even from previous, crashed runs
|
---|
174 | // of this application, since we can access the font tables.
|
---|
175 | const quint32 uid = RProcess().Type().MostDerived().iUid;
|
---|
176 | id = static_cast<quint16>(uid + (uid >> 16));
|
---|
177 | } else {
|
---|
178 | // If no font table Api is available, we must not even load a font
|
---|
179 | // from a previous (crashed) run of this application. Reason: we
|
---|
180 | // won't get the font tables, they are not in the CFontStore.
|
---|
181 | // So, we use the pid, for more uniqueness.
|
---|
182 | id = static_cast<quint16>(RProcess().Id().Id());
|
---|
183 | }
|
---|
184 | result = appFontMarkerPrefix + QString::fromLatin1("%1").arg(id & 0x7fff, 3, 32, QLatin1Char('0'));
|
---|
185 | Q_ASSERT(appFontMarkerPrefix.length() == 1 && result.length() == 4);
|
---|
186 | }
|
---|
187 | return result;
|
---|
188 | }
|
---|
189 |
|
---|
190 | static inline bool qt_symbian_fontNameHasAppFontMarker(const QString &fontName)
|
---|
191 | {
|
---|
192 | const int idLength = 3; // Keep in sync with id length in appFontMarker().
|
---|
193 | const QString &prefix = QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix;
|
---|
194 | if (fontName.length() < prefix.length() + idLength
|
---|
195 | || fontName.mid(fontName.length() - idLength - prefix.length(), prefix.length()) != prefix)
|
---|
196 | return false;
|
---|
197 | // Testing if the the id is base32 data
|
---|
198 | for (int i = fontName.length() - idLength; i < fontName.length(); ++i) {
|
---|
199 | const QChar &c = fontName.at(i);
|
---|
200 | if (!(c >= QLatin1Char('0') && c <= QLatin1Char('9')
|
---|
201 | || c >= QLatin1Char('a') && c <= QLatin1Char('v')))
|
---|
202 | return false;
|
---|
203 | }
|
---|
204 | return true;
|
---|
205 | }
|
---|
206 |
|
---|
207 | // If fontName is an application font of this app, prepend the app font marker
|
---|
208 | QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName)
|
---|
209 | {
|
---|
210 | QFontDatabasePrivate *db = privateDb();
|
---|
211 | Q_ASSERT(db);
|
---|
212 | const QSymbianFontDatabaseExtrasImplementation *dbExtras =
|
---|
213 | static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
|
---|
214 | return dbExtras->m_applicationFontFamilies.contains(fontName) ?
|
---|
215 | fontName + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
|
---|
216 | : fontName;
|
---|
217 | }
|
---|
218 |
|
---|
219 | static inline QString qt_symbian_appFontNameWithoutMarker(const QString &markedFontName)
|
---|
220 | {
|
---|
221 | return markedFontName.left(markedFontName.length()
|
---|
222 | - QSymbianFontDatabaseExtrasImplementation::appFontMarker().length());
|
---|
223 | }
|
---|
224 |
|
---|
225 | QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementation()
|
---|
226 | {
|
---|
227 | if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
|
---|
228 | QStringList filters;
|
---|
229 | filters.append(QLatin1String("*.ttf"));
|
---|
230 | filters.append(QLatin1String("*.ccc"));
|
---|
231 | filters.append(QLatin1String("*.ltt"));
|
---|
232 | const QFileInfoList fontFiles = alternativeFilePaths(QLatin1String("resource\\Fonts"), filters);
|
---|
233 |
|
---|
234 | const TInt heapMinLength = 0x1000;
|
---|
235 | const TInt heapMaxLength = qMax(0x20000 * fontFiles.count(), heapMinLength);
|
---|
236 | m_heap = User::ChunkHeap(NULL, heapMinLength, heapMaxLength);
|
---|
237 | QT_TRAP_THROWING(
|
---|
238 | m_store = CFontStore::NewL(m_heap);
|
---|
239 | m_rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
|
---|
240 | CleanupStack::PushL(m_rasterizer);
|
---|
241 | m_store->InstallRasterizerL(m_rasterizer);
|
---|
242 | CleanupStack::Pop(m_rasterizer););
|
---|
243 |
|
---|
244 | foreach (const QFileInfo &fontFileInfo, fontFiles)
|
---|
245 | addFontFileToFontStore(fontFileInfo);
|
---|
246 | }
|
---|
247 | }
|
---|
248 |
|
---|
249 | void QSymbianFontDatabaseExtrasImplementation::clear()
|
---|
250 | {
|
---|
251 | QFontDatabasePrivate *db = privateDb();
|
---|
252 | if (!db)
|
---|
253 | return;
|
---|
254 | const QSymbianFontDatabaseExtrasImplementation *dbExtras =
|
---|
255 | static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
|
---|
256 | if (!dbExtras)
|
---|
257 | return; // initializeDb() has never been called
|
---|
258 | if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
|
---|
259 | qDeleteAll(dbExtras->m_extrasHash);
|
---|
260 | } else {
|
---|
261 | typedef QList<const QSymbianTypeFaceExtras *>::iterator iterator;
|
---|
262 | for (iterator p = dbExtras->m_extras.begin(); p != dbExtras->m_extras.end(); ++p) {
|
---|
263 | dbExtras->m_store->ReleaseFont((*p)->fontOwner());
|
---|
264 | delete *p;
|
---|
265 | }
|
---|
266 | dbExtras->m_extras.clear();
|
---|
267 | }
|
---|
268 | dbExtras->m_extrasHash.clear();
|
---|
269 | }
|
---|
270 |
|
---|
271 | void qt_cleanup_symbianFontDatabase()
|
---|
272 | {
|
---|
273 | QFontDatabasePrivate *db = privateDb();
|
---|
274 | if (!db)
|
---|
275 | return;
|
---|
276 |
|
---|
277 | QSymbianFontDatabaseExtrasImplementation::clear();
|
---|
278 |
|
---|
279 | if (!db->applicationFonts.isEmpty()) {
|
---|
280 | QFontDatabase::removeAllApplicationFonts();
|
---|
281 | // We remove the left over temporary font files of Qt application.
|
---|
282 | // Active fonts are undeletable since the font server holds a handle
|
---|
283 | // on them, so we do not need to worry to delete other running
|
---|
284 | // applications' fonts.
|
---|
285 | const QDir dir(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder());
|
---|
286 | const QStringList filter(
|
---|
287 | QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix + QLatin1String("*.ttf"));
|
---|
288 | foreach (const QFileInfo &ttfFile, dir.entryInfoList(filter))
|
---|
289 | QFile(ttfFile.absoluteFilePath()).remove();
|
---|
290 | db->applicationFonts.clear();
|
---|
291 | }
|
---|
292 | }
|
---|
293 |
|
---|
294 | QSymbianFontDatabaseExtrasImplementation::~QSymbianFontDatabaseExtrasImplementation()
|
---|
295 | {
|
---|
296 | qt_cleanup_symbianFontDatabase();
|
---|
297 | if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
|
---|
298 | delete m_store;
|
---|
299 | m_heap->Close();
|
---|
300 | }
|
---|
301 | }
|
---|
302 |
|
---|
303 | #ifndef FNTSTORE_H_INLINES_SUPPORT_FMM
|
---|
304 | /*
|
---|
305 | Workaround: fntstore.h has an inlined function 'COpenFont* CBitmapFont::OpenFont()'
|
---|
306 | that returns a private data member. The header will change between SDKs. But Qt has
|
---|
307 | to build on any SDK version and run on other versions of Symbian OS.
|
---|
308 | This function performs the needed pointer arithmetic to get the right COpenFont*
|
---|
309 | */
|
---|
310 | COpenFont* OpenFontFromBitmapFont(const CBitmapFont* aBitmapFont)
|
---|
311 | {
|
---|
312 | const TInt offsetIOpenFont = 92; // '_FOFF(CBitmapFont, iOpenFont)' ..if iOpenFont weren't private
|
---|
313 | const TUint valueIOpenFont = *(TUint*)PtrAdd(aBitmapFont, offsetIOpenFont);
|
---|
314 | return (valueIOpenFont & 1) ?
|
---|
315 | (COpenFont*)PtrAdd(aBitmapFont, valueIOpenFont & ~1) : // New behavior: iOpenFont is offset
|
---|
316 | (COpenFont*)valueIOpenFont; // Old behavior: iOpenFont is pointer
|
---|
317 | }
|
---|
318 | #endif // FNTSTORE_H_INLINES_SUPPORT_FMM
|
---|
319 |
|
---|
320 | const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &aTypeface,
|
---|
321 | bool bold, bool italic) const
|
---|
322 | {
|
---|
323 | const QString typeface = qt_symbian_fontNameWithAppFontMarker(aTypeface);
|
---|
324 | const QString searchKey = typeface + QString::number(int(bold)) + QString::number(int(italic));
|
---|
325 | if (!m_extrasHash.contains(searchKey)) {
|
---|
326 | TFontSpec searchSpec(qt_QString2TPtrC(typeface), 1);
|
---|
327 | if (bold)
|
---|
328 | searchSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold);
|
---|
329 | if (italic)
|
---|
330 | searchSpec.iFontStyle.SetPosture(EPostureItalic);
|
---|
331 |
|
---|
332 | CFont* font = NULL;
|
---|
333 | if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
|
---|
334 | const TInt err = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(font, searchSpec);
|
---|
335 | Q_ASSERT(err == KErrNone && font);
|
---|
336 | QScopedPointer<CFont, CFontFromScreenDeviceReleaser> sFont(font);
|
---|
337 | QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font);
|
---|
338 | sFont.take();
|
---|
339 | m_extrasHash.insert(searchKey, extras);
|
---|
340 | } else {
|
---|
341 | const TInt err = m_store->GetNearestFontToDesignHeightInPixels(font, searchSpec);
|
---|
342 | Q_ASSERT(err == KErrNone && font);
|
---|
343 | const CBitmapFont *bitmapFont = static_cast<CBitmapFont*>(font);
|
---|
344 | COpenFont *openFont =
|
---|
345 | #ifdef FNTSTORE_H_INLINES_SUPPORT_FMM
|
---|
346 | bitmapFont->OpenFont();
|
---|
347 | #else // FNTSTORE_H_INLINES_SUPPORT_FMM
|
---|
348 | OpenFontFromBitmapFont(bitmapFont);
|
---|
349 | #endif // FNTSTORE_H_INLINES_SUPPORT_FMM
|
---|
350 | const TOpenFontFaceAttrib* const attrib = openFont->FaceAttrib();
|
---|
351 | const QString foundKey =
|
---|
352 | QString((const QChar*)attrib->FullName().Ptr(), attrib->FullName().Length());
|
---|
353 | if (!m_extrasHash.contains(foundKey)) {
|
---|
354 | QScopedPointer<CFont, CFontFromFontStoreReleaser> sFont(font);
|
---|
355 | QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font, openFont);
|
---|
356 | sFont.take();
|
---|
357 | m_extras.append(extras);
|
---|
358 | m_extrasHash.insert(searchKey, extras);
|
---|
359 | m_extrasHash.insert(foundKey, extras);
|
---|
360 | } else {
|
---|
361 | m_store->ReleaseFont(font);
|
---|
362 | m_extrasHash.insert(searchKey, m_extrasHash.value(foundKey));
|
---|
363 | }
|
---|
364 | }
|
---|
365 | }
|
---|
366 | return m_extrasHash.value(searchKey);
|
---|
367 | }
|
---|
368 |
|
---|
369 | void QSymbianFontDatabaseExtrasImplementation::removeAppFontData(
|
---|
370 | QFontDatabasePrivate::ApplicationFont *fnt)
|
---|
371 | {
|
---|
372 | clear();
|
---|
373 | if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()
|
---|
374 | && fnt->fontStoreFontFileUid.iUid != 0)
|
---|
375 | m_store->RemoveFile(fnt->fontStoreFontFileUid);
|
---|
376 | if (!fnt->families.isEmpty())
|
---|
377 | m_applicationFontFamilies.remove(fnt->families.first());
|
---|
378 | if (fnt->screenDeviceFontFileId != 0)
|
---|
379 | S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId);
|
---|
380 | QFile::remove(fnt->temporaryFileName);
|
---|
381 | *fnt = QFontDatabasePrivate::ApplicationFont();
|
---|
382 | }
|
---|
383 |
|
---|
384 | bool QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
|
---|
385 | {
|
---|
386 | QFontDatabasePrivate *db = privateDb();
|
---|
387 | if (!db)
|
---|
388 | return false;
|
---|
389 | const int maxAppFonts = 5;
|
---|
390 | int registeredAppFonts = 0;
|
---|
391 | foreach (const QFontDatabasePrivate::ApplicationFont &appFont, db->applicationFonts)
|
---|
392 | if (!appFont.families.isEmpty() && ++registeredAppFonts == maxAppFonts)
|
---|
393 | return true;
|
---|
394 | return false;
|
---|
395 | }
|
---|
396 |
|
---|
397 | TUid QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo)
|
---|
398 | {
|
---|
399 | Q_ASSERT(!QSymbianTypeFaceExtras::symbianFontTableApiAvailable());
|
---|
400 | const QString fontFile = QDir::toNativeSeparators(fontFileInfo.absoluteFilePath());
|
---|
401 | const TPtrC fontFilePtr(qt_QString2TPtrC(fontFile));
|
---|
402 | TUid fontUid = {0};
|
---|
403 | TRAP_IGNORE(fontUid = m_store->AddFileL(fontFilePtr));
|
---|
404 | return fontUid;
|
---|
405 | }
|
---|
406 |
|
---|
407 | #else // QT_NO_FREETYPE
|
---|
408 | class QFontEngineFTS60 : public QFontEngineFT
|
---|
409 | {
|
---|
410 | public:
|
---|
411 | QFontEngineFTS60(const QFontDef &fd);
|
---|
412 | };
|
---|
413 |
|
---|
414 | QFontEngineFTS60::QFontEngineFTS60(const QFontDef &fd)
|
---|
415 | : QFontEngineFT(fd)
|
---|
416 | {
|
---|
417 | default_hint_style = HintFull;
|
---|
418 | }
|
---|
419 | #endif // QT_NO_FREETYPE
|
---|
420 |
|
---|
421 | /*
|
---|
422 | QFontEngineS60::pixelsToPoints, QFontEngineS60::pointsToPixels, QFontEngineMultiS60::QFontEngineMultiS60
|
---|
423 | and QFontEngineMultiS60::QFontEngineMultiS60 should be in qfontengine_s60.cpp. But since also the
|
---|
424 | Freetype based font rendering need them, they are here.
|
---|
425 | */
|
---|
426 | qreal QFontEngineS60::pixelsToPoints(qreal pixels, Qt::Orientation orientation)
|
---|
427 | {
|
---|
428 | CWsScreenDevice* device = S60->screenDevice();
|
---|
429 | return (orientation == Qt::Horizontal?
|
---|
430 | device->HorizontalPixelsToTwips(pixels)
|
---|
431 | :device->VerticalPixelsToTwips(pixels)) / KTwipsPerPoint;
|
---|
432 | }
|
---|
433 |
|
---|
434 | qreal QFontEngineS60::pointsToPixels(qreal points, Qt::Orientation orientation)
|
---|
435 | {
|
---|
436 | CWsScreenDevice* device = S60->screenDevice();
|
---|
437 | const int twips = points * KTwipsPerPoint;
|
---|
438 | return orientation == Qt::Horizontal?
|
---|
439 | device->HorizontalTwipsToPixels(twips)
|
---|
440 | :device->VerticalTwipsToPixels(twips);
|
---|
441 | }
|
---|
442 |
|
---|
443 | QFontEngineMultiS60::QFontEngineMultiS60(QFontEngine *first, int script, const QStringList &fallbackFamilies)
|
---|
444 | : QFontEngineMulti(fallbackFamilies.size() + 1)
|
---|
445 | , m_script(script)
|
---|
446 | , m_fallbackFamilies(fallbackFamilies)
|
---|
447 | {
|
---|
448 | engines[0] = first;
|
---|
449 | first->ref.ref();
|
---|
450 | fontDef = engines[0]->fontDef;
|
---|
451 | }
|
---|
452 |
|
---|
453 | void QFontEngineMultiS60::loadEngine(int at)
|
---|
454 | {
|
---|
455 | Q_ASSERT(at < engines.size());
|
---|
456 | Q_ASSERT(engines.at(at) == 0);
|
---|
457 |
|
---|
458 | QFontDef request = fontDef;
|
---|
459 | request.styleStrategy |= QFont::NoFontMerging;
|
---|
460 | request.family = m_fallbackFamilies.at(at-1);
|
---|
461 | engines[at] = QFontDatabase::findFont(m_script,
|
---|
462 | /*fontprivate*/0,
|
---|
463 | request);
|
---|
464 | Q_ASSERT(engines[at]);
|
---|
465 | }
|
---|
466 |
|
---|
467 | static bool registerScreenDeviceFont(int screenDeviceFontIndex,
|
---|
468 | const QSymbianFontDatabaseExtrasImplementation *dbExtras)
|
---|
469 | {
|
---|
470 | TTypefaceSupport typefaceSupport;
|
---|
471 | S60->screenDevice()->TypefaceSupport(typefaceSupport, screenDeviceFontIndex);
|
---|
472 |
|
---|
473 | QString familyName((const QChar*)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
|
---|
474 | if (qt_symbian_fontNameHasAppFontMarker(familyName)) {
|
---|
475 | const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker();
|
---|
476 | if (familyName.endsWith(marker)) {
|
---|
477 | familyName = qt_symbian_appFontNameWithoutMarker(familyName);
|
---|
478 | dbExtras->m_applicationFontFamilies.insert(familyName);
|
---|
479 | } else {
|
---|
480 | return false; // This was somebody else's application font. Skip it.
|
---|
481 | }
|
---|
482 | }
|
---|
483 |
|
---|
484 | CFont *font; // We have to get a font instance in order to know all the details
|
---|
485 | TFontSpec fontSpec(typefaceSupport.iTypeface.iName, 11);
|
---|
486 | if (S60->screenDevice()->GetNearestFontInPixels(font, fontSpec) != KErrNone)
|
---|
487 | return false;
|
---|
488 | QScopedPointer<CFont, QSymbianFontDatabaseExtrasImplementation::CFontFromScreenDeviceReleaser> sFont(font);
|
---|
489 | if (font->TypeUid() != KCFbsFontUid)
|
---|
490 | return false;
|
---|
491 | TOpenFontFaceAttrib faceAttrib;
|
---|
492 | const CFbsFont *cfbsFont = static_cast<const CFbsFont *>(font);
|
---|
493 | cfbsFont->GetFaceAttrib(faceAttrib);
|
---|
494 |
|
---|
495 | QtFontStyle::Key styleKey;
|
---|
496 | styleKey.style = faceAttrib.IsItalic()?QFont::StyleItalic:QFont::StyleNormal;
|
---|
497 | styleKey.weight = faceAttrib.IsBold()?QFont::Bold:QFont::Normal;
|
---|
498 |
|
---|
499 | QtFontFamily *family = privateDb()->family(familyName, true);
|
---|
500 | family->fixedPitch = faceAttrib.IsMonoWidth();
|
---|
501 | QtFontFoundry *foundry = family->foundry(QString(), true);
|
---|
502 | QtFontStyle *style = foundry->style(styleKey, true);
|
---|
503 | style->smoothScalable = typefaceSupport.iIsScalable;
|
---|
504 | style->pixelSize(0, true);
|
---|
505 |
|
---|
506 | const QSymbianTypeFaceExtras *typeFaceExtras =
|
---|
507 | dbExtras->extras(familyName, faceAttrib.IsBold(), faceAttrib.IsItalic());
|
---|
508 | const QByteArray os2Table = typeFaceExtras->getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
|
---|
509 | const unsigned char* data = reinterpret_cast<const unsigned char*>(os2Table.constData());
|
---|
510 | const unsigned char* ulUnicodeRange = data + 42;
|
---|
511 | quint32 unicodeRange[4] = {
|
---|
512 | qFromBigEndian<quint32>(ulUnicodeRange),
|
---|
513 | qFromBigEndian<quint32>(ulUnicodeRange + 4),
|
---|
514 | qFromBigEndian<quint32>(ulUnicodeRange + 8),
|
---|
515 | qFromBigEndian<quint32>(ulUnicodeRange + 12)
|
---|
516 | };
|
---|
517 | const unsigned char* ulCodePageRange = data + 78;
|
---|
518 | quint32 codePageRange[2] = {
|
---|
519 | qFromBigEndian<quint32>(ulCodePageRange),
|
---|
520 | qFromBigEndian<quint32>(ulCodePageRange + 4)
|
---|
521 | };
|
---|
522 | const QList<QFontDatabase::WritingSystem> writingSystems =
|
---|
523 | determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
|
---|
524 | foreach (const QFontDatabase::WritingSystem system, writingSystems)
|
---|
525 | family->writingSystems[system] = QtFontFamily::Supported;
|
---|
526 | return true;
|
---|
527 | }
|
---|
528 |
|
---|
529 | static void initializeDb()
|
---|
530 | {
|
---|
531 | QFontDatabasePrivate *db = privateDb();
|
---|
532 | if(!db || db->count)
|
---|
533 | return;
|
---|
534 |
|
---|
535 | #ifdef QT_NO_FREETYPE
|
---|
536 | if (!db->symbianExtras)
|
---|
537 | db->symbianExtras = new QSymbianFontDatabaseExtrasImplementation;
|
---|
538 |
|
---|
539 | QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
|
---|
540 |
|
---|
541 | const int numTypeFaces = S60->screenDevice()->NumTypefaces();
|
---|
542 | const QSymbianFontDatabaseExtrasImplementation *dbExtras =
|
---|
543 | static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
|
---|
544 | for (int i = 0; i < numTypeFaces; i++)
|
---|
545 | registerScreenDeviceFont(i, dbExtras);
|
---|
546 |
|
---|
547 | // We have to clear/release all CFonts, here, in case one of the fonts is
|
---|
548 | // an application font of another running Qt app. Otherwise the other Qt app
|
---|
549 | // cannot remove it's application font, anymore -> "Zombie Font".
|
---|
550 | QSymbianFontDatabaseExtrasImplementation::clear();
|
---|
551 |
|
---|
552 | lock.relock();
|
---|
553 |
|
---|
554 | #else // QT_NO_FREETYPE
|
---|
555 | QDir dir(QDesktopServices::storageLocation(QDesktopServices::FontsLocation));
|
---|
556 | dir.setNameFilters(QStringList() << QLatin1String("*.ttf")
|
---|
557 | << QLatin1String("*.ttc") << QLatin1String("*.pfa")
|
---|
558 | << QLatin1String("*.pfb"));
|
---|
559 | for (int i = 0; i < int(dir.count()); ++i) {
|
---|
560 | const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i]));
|
---|
561 | db->addTTFile(file);
|
---|
562 | }
|
---|
563 | #endif // QT_NO_FREETYPE
|
---|
564 | }
|
---|
565 |
|
---|
566 | static inline void load(const QString &family = QString(), int script = -1)
|
---|
567 | {
|
---|
568 | Q_UNUSED(family)
|
---|
569 | Q_UNUSED(script)
|
---|
570 | initializeDb();
|
---|
571 | }
|
---|
572 |
|
---|
573 | struct OffsetTable {
|
---|
574 | quint32 sfntVersion;
|
---|
575 | quint16 numTables, searchRange, entrySelector, rangeShift;
|
---|
576 | };
|
---|
577 |
|
---|
578 | struct TableRecord {
|
---|
579 | quint32 tag, checkSum, offset, length;
|
---|
580 | };
|
---|
581 |
|
---|
582 | struct NameTableHead {
|
---|
583 | quint16 format, count, stringOffset;
|
---|
584 | };
|
---|
585 |
|
---|
586 | struct NameRecord {
|
---|
587 | quint16 platformID, encodingID, languageID, nameID, length, offset;
|
---|
588 | };
|
---|
589 |
|
---|
590 | static quint32 ttfCalcChecksum(const char *data, quint32 bytesCount)
|
---|
591 | {
|
---|
592 | quint32 result = 0;
|
---|
593 | const quint32 *ptr = reinterpret_cast<const quint32*>(data);
|
---|
594 | const quint32 *endPtr =
|
---|
595 | ptr + (bytesCount + sizeof(quint32) - 1) / sizeof(quint32);
|
---|
596 | while (ptr < endPtr) {
|
---|
597 | const quint32 unit32Value = *ptr++;
|
---|
598 | result += qFromBigEndian(unit32Value);
|
---|
599 | }
|
---|
600 | return result;
|
---|
601 | }
|
---|
602 |
|
---|
603 | static inline quint32 toDWordBoundary(quint32 value)
|
---|
604 | {
|
---|
605 | return (value + 3) & ~3;
|
---|
606 | }
|
---|
607 |
|
---|
608 | static inline quint32 dWordPadding(quint32 value)
|
---|
609 | {
|
---|
610 | return (4 - (value & 3)) & 3;
|
---|
611 | }
|
---|
612 |
|
---|
613 | static inline bool ttfMarkNameTable(QByteArray &table, const QString &marker)
|
---|
614 | {
|
---|
615 | const quint32 tableLength = static_cast<quint32>(table.size());
|
---|
616 |
|
---|
617 | if (tableLength > 50000 // hard limit
|
---|
618 | || tableLength < sizeof(NameTableHead)) // corrupt name table
|
---|
619 | return false;
|
---|
620 |
|
---|
621 | const NameTableHead *head = reinterpret_cast<const NameTableHead*>(table.constData());
|
---|
622 | const quint16 count = qFromBigEndian(head->count);
|
---|
623 | const quint16 stringOffset = qFromBigEndian(head->stringOffset);
|
---|
624 | if (count > 200 // hard limit
|
---|
625 | || stringOffset >= tableLength // corrupt name table
|
---|
626 | || sizeof(NameTableHead) + count * sizeof(NameRecord) >= tableLength) // corrupt name table
|
---|
627 | return false;
|
---|
628 |
|
---|
629 | QTextEncoder encoder(QTextCodec::codecForName("UTF-16BE"), QTextCodec::IgnoreHeader);
|
---|
630 | const QByteArray markerUtf16BE = encoder.fromUnicode(marker);
|
---|
631 | const QByteArray markerAscii = marker.toAscii();
|
---|
632 |
|
---|
633 | QByteArray markedTable;
|
---|
634 | markedTable.reserve(tableLength + marker.length() * 20); // Original size plus some extra
|
---|
635 | markedTable.append(table, stringOffset);
|
---|
636 | QByteArray markedStrings;
|
---|
637 | quint32 stringDataCount = stringOffset;
|
---|
638 | for (quint16 i = 0; i < count; ++i) {
|
---|
639 | const quint32 nameRecordOffset = sizeof(NameTableHead) + sizeof(NameRecord) * i;
|
---|
640 | NameRecord *nameRecord =
|
---|
641 | reinterpret_cast<NameRecord*>(markedTable.data() + nameRecordOffset);
|
---|
642 | const quint16 nameID = qFromBigEndian(nameRecord->nameID);
|
---|
643 | const quint16 platformID = qFromBigEndian(nameRecord->platformID);
|
---|
644 | const quint16 encodingID = qFromBigEndian(nameRecord->encodingID);
|
---|
645 | const quint16 offset = qFromBigEndian(nameRecord->offset);
|
---|
646 | const quint16 length = qFromBigEndian(nameRecord->length);
|
---|
647 | stringDataCount += length;
|
---|
648 | if (stringDataCount > 80000 // hard limit. String data may be > name table size. Multiple records can reference the same string.
|
---|
649 | || static_cast<quint32>(stringOffset + offset + length) > tableLength) // String outside bounds
|
---|
650 | return false;
|
---|
651 | const bool needsMarker =
|
---|
652 | nameID == 1 || nameID == 3 || nameID == 4 || nameID == 16 || nameID == 21;
|
---|
653 | const bool isUnicode =
|
---|
654 | platformID == 0 || platformID == 3 && encodingID == 1;
|
---|
655 | const QByteArray originalString =
|
---|
656 | QByteArray::fromRawData(table.constData() + stringOffset + offset, length);
|
---|
657 | QByteArray markedString;
|
---|
658 | if (needsMarker) {
|
---|
659 | const int maxBytesLength = (KMaxTypefaceNameLength - marker.length()) * (isUnicode ? 2 : 1);
|
---|
660 | markedString = originalString.left(maxBytesLength) + (isUnicode ? markerUtf16BE : markerAscii);
|
---|
661 | } else {
|
---|
662 | markedString = originalString;
|
---|
663 | }
|
---|
664 | nameRecord->offset = qToBigEndian(static_cast<quint16>(markedStrings.length()));
|
---|
665 | nameRecord->length = qToBigEndian(static_cast<quint16>(markedString.length()));
|
---|
666 | markedStrings.append(markedString);
|
---|
667 | }
|
---|
668 | markedTable.append(markedStrings);
|
---|
669 | table = markedTable;
|
---|
670 | return true;
|
---|
671 | }
|
---|
672 |
|
---|
673 | const quint32 ttfMaxFileSize = 3500000;
|
---|
674 |
|
---|
675 | static inline bool ttfMarkAppFont(QByteArray &ttf, const QString &marker)
|
---|
676 | {
|
---|
677 | const quint32 ttfChecksumNumber = 0xb1b0afba;
|
---|
678 | const quint32 alignment = 4;
|
---|
679 | const quint32 ttfLength = static_cast<quint32>(ttf.size());
|
---|
680 | if (ttfLength > ttfMaxFileSize // hard limit
|
---|
681 | || ttfLength % alignment != 0 // ttf sizes are always factors of 4
|
---|
682 | || ttfLength <= sizeof(OffsetTable) // ttf too short
|
---|
683 | || ttfCalcChecksum(ttf.constData(), ttf.size()) != ttfChecksumNumber) // ttf checksum is invalid
|
---|
684 | return false;
|
---|
685 |
|
---|
686 | const OffsetTable *offsetTable = reinterpret_cast<const OffsetTable*>(ttf.constData());
|
---|
687 | const quint16 numTables = qFromBigEndian(offsetTable->numTables);
|
---|
688 | const quint32 recordsLength =
|
---|
689 | toDWordBoundary(sizeof(OffsetTable) + numTables * sizeof(TableRecord));
|
---|
690 | if (numTables > 30 // hard limit
|
---|
691 | || recordsLength + numTables * alignment > ttfLength) // Corrupt ttf. Tables would not fit, even if empty.
|
---|
692 | return false;
|
---|
693 |
|
---|
694 | QByteArray markedTtf;
|
---|
695 | markedTtf.reserve(ttfLength + marker.length() * 20); // Original size plus some extra
|
---|
696 | markedTtf.append(ttf.constData(), recordsLength);
|
---|
697 |
|
---|
698 | const quint32 ttfCheckSumAdjustmentOffset = 8; // Offset from the start of 'head'
|
---|
699 | int indexOfHeadTable = -1;
|
---|
700 | quint32 ttfDataSize = recordsLength;
|
---|
701 | typedef QPair<quint32, quint32> Range;
|
---|
702 | QList<Range> memoryRanges;
|
---|
703 | memoryRanges.reserve(numTables);
|
---|
704 | for (int i = 0; i < numTables; ++i) {
|
---|
705 | TableRecord *tableRecord =
|
---|
706 | reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord));
|
---|
707 | const quint32 offset = qFromBigEndian(tableRecord->offset);
|
---|
708 | const quint32 length = qFromBigEndian(tableRecord->length);
|
---|
709 | const quint32 lengthAligned = toDWordBoundary(length);
|
---|
710 | ttfDataSize += lengthAligned;
|
---|
711 | if (offset < recordsLength // must not intersect ttf header/records
|
---|
712 | || offset % alignment != 0 // must be aligned
|
---|
713 | || offset > ttfLength - alignment // table out of bounds
|
---|
714 | || offset + lengthAligned > ttfLength // table out of bounds
|
---|
715 | || ttfDataSize > ttfLength) // tables would not fit into the ttf
|
---|
716 | return false;
|
---|
717 |
|
---|
718 | foreach (const Range &range, memoryRanges)
|
---|
719 | if (offset < range.first + range.second && offset + lengthAligned > range.first)
|
---|
720 | return false; // Overlaps with another table
|
---|
721 | memoryRanges.append(Range(offset, lengthAligned));
|
---|
722 |
|
---|
723 | quint32 checkSum = qFromBigEndian(tableRecord->checkSum);
|
---|
724 | if (tableRecord->tag == qToBigEndian(static_cast<quint32>('head'))) {
|
---|
725 | if (length < ttfCheckSumAdjustmentOffset + sizeof(quint32))
|
---|
726 | return false; // Invalid 'head' table
|
---|
727 | const quint32 *checkSumAdjustmentTag =
|
---|
728 | reinterpret_cast<const quint32*>(ttf.constData() + offset + ttfCheckSumAdjustmentOffset);
|
---|
729 | const quint32 checkSumAdjustment = qFromBigEndian(*checkSumAdjustmentTag);
|
---|
730 | checkSum += checkSumAdjustment;
|
---|
731 | indexOfHeadTable = i; // For the ttf checksum re-calculation, later
|
---|
732 | }
|
---|
733 | if (checkSum != ttfCalcChecksum(ttf.constData() + offset, length))
|
---|
734 | return false; // Table checksum is invalid
|
---|
735 |
|
---|
736 | bool updateTableChecksum = false;
|
---|
737 | QByteArray table;
|
---|
738 | if (tableRecord->tag == qToBigEndian(static_cast<quint32>('name'))) {
|
---|
739 | table = QByteArray(ttf.constData() + offset, length);
|
---|
740 | if (!ttfMarkNameTable(table, marker))
|
---|
741 | return false; // Name table was not markable.
|
---|
742 | updateTableChecksum = true;
|
---|
743 | } else {
|
---|
744 | table = QByteArray::fromRawData(ttf.constData() + offset, length);
|
---|
745 | }
|
---|
746 |
|
---|
747 | tableRecord->offset = qToBigEndian(markedTtf.size());
|
---|
748 | tableRecord->length = qToBigEndian(table.size());
|
---|
749 | markedTtf.append(table);
|
---|
750 | markedTtf.append(QByteArray(dWordPadding(table.size()), 0)); // 0-padding
|
---|
751 | if (updateTableChecksum) {
|
---|
752 | TableRecord *tableRecord = // Need to recalculate, since markedTtf changed
|
---|
753 | reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord));
|
---|
754 | const quint32 offset = qFromBigEndian(tableRecord->offset);
|
---|
755 | const quint32 length = qFromBigEndian(tableRecord->length);
|
---|
756 | tableRecord->checkSum = qToBigEndian(ttfCalcChecksum(markedTtf.constData() + offset, length));
|
---|
757 | }
|
---|
758 | }
|
---|
759 | if (indexOfHeadTable == -1 // 'head' table is mandatory
|
---|
760 | || ttfDataSize != ttfLength) // We do not allow ttf data "holes". Neither does Symbian.
|
---|
761 | return false;
|
---|
762 | TableRecord *headRecord =
|
---|
763 | reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + indexOfHeadTable * sizeof(TableRecord));
|
---|
764 | quint32 *checkSumAdjustmentTag =
|
---|
765 | reinterpret_cast<quint32*>(markedTtf.data() + qFromBigEndian(headRecord->offset) + ttfCheckSumAdjustmentOffset);
|
---|
766 | *checkSumAdjustmentTag = 0;
|
---|
767 | const quint32 ttfChecksum = ttfCalcChecksum(markedTtf.constData(), markedTtf.count());
|
---|
768 | *checkSumAdjustmentTag = qToBigEndian(ttfChecksumNumber - ttfChecksum);
|
---|
769 | ttf = markedTtf;
|
---|
770 | return true;
|
---|
771 | }
|
---|
772 |
|
---|
773 | static inline bool ttfCanSymbianLoadFont(const QByteArray &data, const QString &fileName)
|
---|
774 | {
|
---|
775 | bool result = false;
|
---|
776 | QString ttfFileName;
|
---|
777 | QFile tempFileGuard;
|
---|
778 | QFileInfo info(fileName);
|
---|
779 | if (!data.isEmpty()) {
|
---|
780 | QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
|
---|
781 | + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
|
---|
782 | + QLatin1String("XXXXXX.ttf"));
|
---|
783 | if (!tempfile.open() || tempfile.write(data) == -1)
|
---|
784 | return false;
|
---|
785 | ttfFileName = QDir::toNativeSeparators(QFileInfo(tempfile).canonicalFilePath());
|
---|
786 | tempfile.setAutoRemove(false);
|
---|
787 | tempfile.close();
|
---|
788 | tempFileGuard.setFileName(ttfFileName);
|
---|
789 | if (!tempFileGuard.open(QIODevice::ReadOnly))
|
---|
790 | return false;
|
---|
791 | } else if (info.isFile()) {
|
---|
792 | ttfFileName = QDir::toNativeSeparators(info.canonicalFilePath());
|
---|
793 | } else {
|
---|
794 | return false;
|
---|
795 | }
|
---|
796 |
|
---|
797 | CFontStore *store = 0;
|
---|
798 | RHeap* heap = User::ChunkHeap(NULL, 0x1000, 0x20000);
|
---|
799 | if (heap) {
|
---|
800 | QT_TRAP_THROWING(
|
---|
801 | CleanupClosePushL(*heap);
|
---|
802 | store = CFontStore::NewL(heap);
|
---|
803 | CleanupStack::PushL(store);
|
---|
804 | COpenFontRasterizer *rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
|
---|
805 | CleanupStack::PushL(rasterizer);
|
---|
806 | store->InstallRasterizerL(rasterizer);
|
---|
807 | CleanupStack::Pop(rasterizer);
|
---|
808 | TUid fontUid = {-1};
|
---|
809 | TRAP_IGNORE(fontUid = store->AddFileL(qt_QString2TPtrC(ttfFileName)));
|
---|
810 | if (fontUid.iUid != -1)
|
---|
811 | result = true;
|
---|
812 | CleanupStack::PopAndDestroy(2, heap); // heap, store
|
---|
813 | );
|
---|
814 | }
|
---|
815 |
|
---|
816 | if (tempFileGuard.isOpen())
|
---|
817 | tempFileGuard.remove();
|
---|
818 |
|
---|
819 | return result;
|
---|
820 | }
|
---|
821 |
|
---|
822 | static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
|
---|
823 | {
|
---|
824 | if (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
|
---|
825 | || fnt->data.size() > ttfMaxFileSize // hard limit
|
---|
826 | || fnt->data.isEmpty() && (!fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive) // Only buffer or .ttf
|
---|
827 | || QFileInfo(fnt->fileName).size() > ttfMaxFileSize)) // hard limit
|
---|
828 | return;
|
---|
829 |
|
---|
830 | // Using ttfCanSymbianLoadFont() causes crashes on app destruction (Symbian^3|PR1 and lower).
|
---|
831 | // Therefore, not using it for now, but eventually in a later version.
|
---|
832 | // if (!ttfCanSymbianLoadFont(fnt->data, fnt->fileName))
|
---|
833 | // return;
|
---|
834 |
|
---|
835 | QFontDatabasePrivate *db = privateDb();
|
---|
836 | if (!db)
|
---|
837 | return;
|
---|
838 |
|
---|
839 | if (!db->count)
|
---|
840 | initializeDb();
|
---|
841 |
|
---|
842 | QSymbianFontDatabaseExtrasImplementation *dbExtras =
|
---|
843 | static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
|
---|
844 | if (!dbExtras)
|
---|
845 | return;
|
---|
846 |
|
---|
847 | const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker();
|
---|
848 |
|
---|
849 | // The QTemporaryFile object being used in the following section must be
|
---|
850 | // destructed before letting Symbian load the TTF file. Symbian would not
|
---|
851 | // load it otherwise, because QTemporaryFile will still keep some handle
|
---|
852 | // on it. The scope is used to reduce the life time of the QTemporaryFile.
|
---|
853 | // In order to prevent other processes from modifying the file between the
|
---|
854 | // moment where the QTemporaryFile is destructed and the file is loaded by
|
---|
855 | // Symbian, we have a QFile "tempFileGuard" outside the scope which opens
|
---|
856 | // the file in ReadOnly mode while the QTemporaryFile is still alive.
|
---|
857 | QFile tempFileGuard;
|
---|
858 | {
|
---|
859 | QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
|
---|
860 | + marker + QLatin1String("XXXXXX.ttf"));
|
---|
861 | if (!tempfile.open())
|
---|
862 | return;
|
---|
863 | const QString tempFileName = QFileInfo(tempfile).canonicalFilePath();
|
---|
864 | if (fnt->data.isEmpty()) {
|
---|
865 | QFile sourceFile(fnt->fileName);
|
---|
866 | if (!sourceFile.open(QIODevice::ReadOnly))
|
---|
867 | return;
|
---|
868 | fnt->data = sourceFile.readAll();
|
---|
869 | }
|
---|
870 | if (!ttfMarkAppFont(fnt->data, marker) || tempfile.write(fnt->data) == -1)
|
---|
871 | return;
|
---|
872 | tempfile.setAutoRemove(false);
|
---|
873 | tempfile.close(); // Tempfile still keeps a file handle, forbidding write access
|
---|
874 | fnt->data.clear(); // The TTF data was marked and saved. Not needed in memory, anymore.
|
---|
875 | tempFileGuard.setFileName(tempFileName);
|
---|
876 | if (!tempFileGuard.open(QIODevice::ReadOnly))
|
---|
877 | return;
|
---|
878 | fnt->temporaryFileName = tempFileName;
|
---|
879 | }
|
---|
880 |
|
---|
881 | const QString fullFileName = QDir::toNativeSeparators(fnt->temporaryFileName);
|
---|
882 | QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
|
---|
883 | const QStringList fontsOnServerBefore = qt_symbian_fontFamiliesOnFontServer();
|
---|
884 | const TInt err =
|
---|
885 | S60->screenDevice()->AddFile(qt_QString2TPtrC(fullFileName), fnt->screenDeviceFontFileId);
|
---|
886 | tempFileGuard.close(); // Did its job
|
---|
887 | const QStringList fontsOnServerAfter = qt_symbian_fontFamiliesOnFontServer();
|
---|
888 | if (err == KErrNone && fontsOnServerBefore.count() < fontsOnServerAfter.count()) { // Added to screen device?
|
---|
889 | int fontOnServerIndex = fontsOnServerAfter.count() - 1;
|
---|
890 | for (int i = 0; i < fontsOnServerBefore.count(); i++) {
|
---|
891 | if (fontsOnServerBefore.at(i) != fontsOnServerAfter.at(i)) {
|
---|
892 | fontOnServerIndex = i;
|
---|
893 | break;
|
---|
894 | }
|
---|
895 | }
|
---|
896 |
|
---|
897 | // Must remove all font engines with their CFonts, first.
|
---|
898 | QFontCache::instance()->clear();
|
---|
899 | db->free();
|
---|
900 | QSymbianFontDatabaseExtrasImplementation::clear();
|
---|
901 |
|
---|
902 | if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable())
|
---|
903 | fnt->fontStoreFontFileUid = dbExtras->addFontFileToFontStore(QFileInfo(fullFileName));
|
---|
904 |
|
---|
905 | const QString &appFontName = fontsOnServerAfter.at(fontOnServerIndex);
|
---|
906 | fnt->families.append(qt_symbian_appFontNameWithoutMarker(appFontName));
|
---|
907 | if (!qt_symbian_fontNameHasAppFontMarker(appFontName)
|
---|
908 | || !registerScreenDeviceFont(fontOnServerIndex, dbExtras))
|
---|
909 | dbExtras->removeAppFontData(fnt);
|
---|
910 | } else {
|
---|
911 | if (fnt->screenDeviceFontFileId > 0)
|
---|
912 | S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); // May still have the file open!
|
---|
913 | QFile::remove(fnt->temporaryFileName);
|
---|
914 | *fnt = QFontDatabasePrivate::ApplicationFont();
|
---|
915 | }
|
---|
916 | lock.relock();
|
---|
917 | }
|
---|
918 |
|
---|
919 | bool QFontDatabase::removeApplicationFont(int handle)
|
---|
920 | {
|
---|
921 | QMutexLocker locker(fontDatabaseMutex());
|
---|
922 |
|
---|
923 | QFontDatabasePrivate *db = privateDb();
|
---|
924 | if (!db || handle < 0 || handle >= db->applicationFonts.count())
|
---|
925 | return false;
|
---|
926 | QSymbianFontDatabaseExtrasImplementation *dbExtras =
|
---|
927 | static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
|
---|
928 | if (!dbExtras)
|
---|
929 | return false;
|
---|
930 |
|
---|
931 | QFontDatabasePrivate::ApplicationFont *fnt = &db->applicationFonts[handle];
|
---|
932 | if (fnt->families.isEmpty())
|
---|
933 | return true; // Nothing to remove. Return peacefully.
|
---|
934 |
|
---|
935 | // Must remove all font engines with their CFonts, first
|
---|
936 | QFontCache::instance()->clear();
|
---|
937 | db->free();
|
---|
938 | dbExtras->removeAppFontData(fnt);
|
---|
939 |
|
---|
940 | db->invalidate(); // This will just emit 'fontDatabaseChanged()'
|
---|
941 | return true;
|
---|
942 | }
|
---|
943 |
|
---|
944 | bool QFontDatabase::removeAllApplicationFonts()
|
---|
945 | {
|
---|
946 | QMutexLocker locker(fontDatabaseMutex());
|
---|
947 |
|
---|
948 | const int applicationFontsCount = privateDb()->applicationFonts.count();
|
---|
949 | for (int i = 0; i < applicationFontsCount; ++i)
|
---|
950 | if (!removeApplicationFont(i))
|
---|
951 | return false;
|
---|
952 | return true;
|
---|
953 | }
|
---|
954 |
|
---|
955 | bool QFontDatabase::supportsThreadedFontRendering()
|
---|
956 | {
|
---|
957 | return false;
|
---|
958 | }
|
---|
959 |
|
---|
960 | static
|
---|
961 | QFontDef cleanedFontDef(const QFontDef &req)
|
---|
962 | {
|
---|
963 | QFontDef result = req;
|
---|
964 | if (result.pixelSize <= 0) {
|
---|
965 | result.pixelSize = QFontEngineS60::pointsToPixels(qMax(qreal(1.0), result.pointSize));
|
---|
966 | result.pointSize = 0;
|
---|
967 | }
|
---|
968 | return result;
|
---|
969 | }
|
---|
970 |
|
---|
971 | QFontEngine *QFontDatabase::findFont(int script, const QFontPrivate *d, const QFontDef &req)
|
---|
972 | {
|
---|
973 | const QFontCache::Key key(cleanedFontDef(req), script);
|
---|
974 |
|
---|
975 | if (!privateDb()->count)
|
---|
976 | initializeDb();
|
---|
977 |
|
---|
978 | QFontEngine *fe = QFontCache::instance()->findEngine(key);
|
---|
979 | if (!fe) {
|
---|
980 | // Making sure that fe->fontDef.family will be an existing font.
|
---|
981 | initializeDb();
|
---|
982 | QFontDatabasePrivate *db = privateDb();
|
---|
983 | QtFontDesc desc;
|
---|
984 | QList<int> blacklistedFamilies;
|
---|
985 | match(script, key.def, key.def.family, QString(), -1, &desc, blacklistedFamilies);
|
---|
986 | if (!desc.family) // falling back to application font
|
---|
987 | desc.family = db->family(QApplication::font().defaultFamily());
|
---|
988 | Q_ASSERT(desc.family);
|
---|
989 |
|
---|
990 | // Making sure that desc.family supports the requested script
|
---|
991 | QtFontDesc mappedDesc;
|
---|
992 | bool supportsScript = false;
|
---|
993 | do {
|
---|
994 | match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
|
---|
995 | if (mappedDesc.family == desc.family) {
|
---|
996 | supportsScript = true;
|
---|
997 | break;
|
---|
998 | }
|
---|
999 | blacklistedFamilies.append(mappedDesc.familyIndex);
|
---|
1000 | } while (mappedDesc.family);
|
---|
1001 | if (!supportsScript) {
|
---|
1002 | blacklistedFamilies.clear();
|
---|
1003 | match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
|
---|
1004 | if (mappedDesc.family)
|
---|
1005 | desc = mappedDesc;
|
---|
1006 | }
|
---|
1007 |
|
---|
1008 | const QString fontFamily = desc.family->name;
|
---|
1009 | QFontDef request = req;
|
---|
1010 | request.family = fontFamily;
|
---|
1011 | #ifdef QT_NO_FREETYPE
|
---|
1012 | const QSymbianFontDatabaseExtrasImplementation *dbExtras =
|
---|
1013 | static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
|
---|
1014 | const QSymbianTypeFaceExtras *typeFaceExtras =
|
---|
1015 | dbExtras->extras(fontFamily, request.weight > QFont::Normal, request.style != QFont::StyleNormal);
|
---|
1016 |
|
---|
1017 | // We need a valid pixelSize, e.g. for lineThickness()
|
---|
1018 | if (request.pixelSize < 0)
|
---|
1019 | request.pixelSize = request.pointSize * d->dpi / 72;
|
---|
1020 |
|
---|
1021 | fe = new QFontEngineS60(request, typeFaceExtras);
|
---|
1022 | #else // QT_NO_FREETYPE
|
---|
1023 | Q_UNUSED(d)
|
---|
1024 | QFontEngine::FaceId faceId;
|
---|
1025 | const QtFontFamily * const reqQtFontFamily = db->family(fontFamily);
|
---|
1026 | faceId.filename = reqQtFontFamily->fontFilename;
|
---|
1027 | faceId.index = reqQtFontFamily->fontFileIndex;
|
---|
1028 |
|
---|
1029 | QFontEngineFTS60 *fte = new QFontEngineFTS60(cleanedFontDef(request));
|
---|
1030 | if (fte->init(faceId, true, QFontEngineFT::Format_A8))
|
---|
1031 | fe = fte;
|
---|
1032 | else
|
---|
1033 | delete fte;
|
---|
1034 | #endif // QT_NO_FREETYPE
|
---|
1035 |
|
---|
1036 | Q_ASSERT(fe);
|
---|
1037 | if (script == QUnicodeTables::Common
|
---|
1038 | && !(req.styleStrategy & QFont::NoFontMerging)
|
---|
1039 | && !fe->symbol) {
|
---|
1040 |
|
---|
1041 | QStringList commonFonts;
|
---|
1042 | for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) {
|
---|
1043 | if (scriptForWritingSystem[ws] != script)
|
---|
1044 | continue;
|
---|
1045 | for (int i = 0; i < db->count; ++i) {
|
---|
1046 | if (db->families[i]->writingSystems[ws] & QtFontFamily::Supported)
|
---|
1047 | commonFonts.append(db->families[i]->name);
|
---|
1048 | }
|
---|
1049 | }
|
---|
1050 |
|
---|
1051 | // Hack: Prioritize .ccc fonts
|
---|
1052 | const QString niceEastAsianFont(QLatin1String("Sans MT 936_S60"));
|
---|
1053 | if (commonFonts.removeAll(niceEastAsianFont) > 0)
|
---|
1054 | commonFonts.prepend(niceEastAsianFont);
|
---|
1055 |
|
---|
1056 | fe = new QFontEngineMultiS60(fe, script, commonFonts);
|
---|
1057 | }
|
---|
1058 | }
|
---|
1059 | fe->ref.ref();
|
---|
1060 | QFontCache::instance()->insertEngine(key, fe);
|
---|
1061 | return fe;
|
---|
1062 | }
|
---|
1063 |
|
---|
1064 | void QFontDatabase::load(const QFontPrivate *d, int script)
|
---|
1065 | {
|
---|
1066 | QFontEngine *fe = 0;
|
---|
1067 | QFontDef req = d->request;
|
---|
1068 |
|
---|
1069 | if (!d->engineData) {
|
---|
1070 | const QFontCache::Key key(cleanedFontDef(req), script);
|
---|
1071 | getEngineData(d, key);
|
---|
1072 | }
|
---|
1073 |
|
---|
1074 | // the cached engineData could have already loaded the engine we want
|
---|
1075 | if (d->engineData->engines[script])
|
---|
1076 | fe = d->engineData->engines[script];
|
---|
1077 |
|
---|
1078 | if (!fe) {
|
---|
1079 | if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
|
---|
1080 | fe = new QTestFontEngine(req.pixelSize);
|
---|
1081 | fe->fontDef = req;
|
---|
1082 | } else {
|
---|
1083 | fe = findFont(script, d, req);
|
---|
1084 | }
|
---|
1085 | d->engineData->engines[script] = fe;
|
---|
1086 | }
|
---|
1087 | }
|
---|
1088 |
|
---|
1089 | QT_END_NAMESPACE
|
---|