source: trunk/src/gui/text/qfontengine_s60.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 19.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
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 [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qfontengine_s60_p.h"
43#include "qtextengine_p.h"
44#include "qendian.h"
45#include "qglobal.h"
46#include <private/qapplication_p.h>
47#include "qimage.h"
48#include <private/qt_s60_p.h>
49#include <private/qpixmap_s60_p.h>
50
51#include <e32base.h>
52#include <e32std.h>
53#include <eikenv.h>
54#include <gdi.h>
55#if defined(Q_SYMBIAN_HAS_GLYPHOUTLINE_API)
56#include <graphics/gdi/gdiplatapi.h>
57#endif // Q_SYMBIAN_HAS_GLYPHOUTLINE_API
58
59// Replication of TGetFontTableParam & friends.
60// There is unfortunately no compile time flag like SYMBIAN_FONT_TABLE_API
61// that would help us to only replicate these things for Symbian versions
62// that do not yet have the font table Api. Symbian's public SDK does
63// generally not define any usable macros.
64class QSymbianTGetFontTableParam
65{
66public:
67 TUint32 iTag;
68 TAny *iContent;
69 TInt iLength;
70};
71const TUid QSymbianKFontGetFontTable = {0x102872C1};
72const TUid QSymbianKFontReleaseFontTable = {0x2002AC24};
73
74QT_BEGIN_NAMESPACE
75
76QSymbianTypeFaceExtras::QSymbianTypeFaceExtras(CFont* cFont, COpenFont *openFont)
77 : m_cFont(cFont)
78 , m_symbolCMap(false)
79 , m_openFont(openFont)
80{
81 if (!symbianFontTableApiAvailable()) {
82 TAny *trueTypeExtension = NULL;
83 m_openFont->ExtendedInterface(KUidOpenFontTrueTypeExtension, trueTypeExtension);
84 m_trueTypeExtension = static_cast<MOpenFontTrueTypeExtension*>(trueTypeExtension);
85 Q_ASSERT(m_trueTypeExtension);
86 }
87}
88
89QSymbianTypeFaceExtras::~QSymbianTypeFaceExtras()
90{
91 if (symbianFontTableApiAvailable())
92 S60->screenDevice()->ReleaseFont(m_cFont);
93}
94
95QByteArray QSymbianTypeFaceExtras::getSfntTable(uint tag) const
96{
97 if (symbianFontTableApiAvailable()) {
98 QSymbianTGetFontTableParam fontTableParams = { tag, 0, 0 };
99 if (m_cFont->ExtendedFunction(QSymbianKFontGetFontTable, &fontTableParams) == KErrNone) {
100 const char* const fontTableContent =
101 static_cast<const char *>(fontTableParams.iContent);
102 const QByteArray fontTable(fontTableContent, fontTableParams.iLength);
103 m_cFont->ExtendedFunction(QSymbianKFontReleaseFontTable, &fontTableParams);
104 return fontTable;
105 }
106 return QByteArray();
107 } else {
108 Q_ASSERT(m_trueTypeExtension->HasTrueTypeTable(tag));
109 TInt error = KErrNone;
110 TInt tableByteLength = 0;
111 TAny *table = q_check_ptr(m_trueTypeExtension->GetTrueTypeTable(error, tag, &tableByteLength));
112 const QByteArray result(static_cast<const char*>(table), tableByteLength);
113 m_trueTypeExtension->ReleaseTrueTypeTable(table);
114 return result;
115 }
116}
117
118bool QSymbianTypeFaceExtras::getSfntTableData(uint tag, uchar *buffer, uint *length) const
119{
120 bool result = true;
121 if (symbianFontTableApiAvailable()) {
122 QSymbianTGetFontTableParam fontTableParams = { tag, 0, 0 };
123 if (m_cFont->ExtendedFunction(QSymbianKFontGetFontTable, &fontTableParams) == KErrNone) {
124 if (*length > 0 && *length < fontTableParams.iLength) {
125 result = false; // Caller did not allocate enough memory
126 } else {
127 *length = fontTableParams.iLength;
128 if (buffer)
129 memcpy(buffer, fontTableParams.iContent, fontTableParams.iLength);
130 }
131 m_cFont->ExtendedFunction(QSymbianKFontReleaseFontTable, &fontTableParams);
132 } else {
133 result = false;
134 }
135 } else {
136 if (!m_trueTypeExtension->HasTrueTypeTable(tag))
137 return false;
138
139 TInt error = KErrNone;
140 TInt tableByteLength;
141 TAny *table =
142 q_check_ptr(m_trueTypeExtension->GetTrueTypeTable(error, tag, &tableByteLength));
143
144 if (error != KErrNone) {
145 return false;
146 } else if (*length > 0 && *length < tableByteLength) {
147 result = false; // Caller did not allocate enough memory
148 } else {
149 *length = tableByteLength;
150 if (buffer)
151 memcpy(buffer, table, tableByteLength);
152 }
153
154 m_trueTypeExtension->ReleaseTrueTypeTable(table);
155 }
156 return result;
157}
158
159const uchar *QSymbianTypeFaceExtras::cmap() const
160{
161 if (m_cmapTable.isNull()) {
162 const QByteArray cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p'));
163 int size = 0;
164 const uchar *cmap = QFontEngine::getCMap(reinterpret_cast<const uchar *>
165 (cmapTable.constData()), cmapTable.size(), &m_symbolCMap, &size);
166 m_cmapTable = QByteArray(reinterpret_cast<const char *>(cmap), size);
167 }
168 return reinterpret_cast<const uchar *>(m_cmapTable.constData());
169}
170
171bool QSymbianTypeFaceExtras::isSymbolCMap() const
172{
173 return m_symbolCMap;
174}
175
176CFont *QSymbianTypeFaceExtras::fontOwner() const
177{
178 return m_cFont;
179}
180
181QFixed QSymbianTypeFaceExtras::unitsPerEm() const
182{
183 if (m_unitsPerEm.value() != 0)
184 return m_unitsPerEm;
185 const QByteArray head = getSfntTable(MAKE_TAG('h', 'e', 'a', 'd'));
186 const int unitsPerEmOffset = 18;
187 if (head.size() > unitsPerEmOffset + sizeof(quint16)) {
188 const uchar* tableData = reinterpret_cast<const uchar*>(head.constData());
189 const uchar* unitsPerEm = tableData + unitsPerEmOffset;
190 m_unitsPerEm = qFromBigEndian<quint16>(unitsPerEm);
191 } else {
192 // Bitmap font? Corrupt font?
193 // We return -1 and let the QFontEngineS60 return the pixel size.
194 m_unitsPerEm = -1;
195 }
196 return m_unitsPerEm;
197}
198
199bool QSymbianTypeFaceExtras::symbianFontTableApiAvailable()
200{
201 enum FontTableApiAvailability {
202 Unknown,
203 Available,
204 Unavailable
205 };
206 static FontTableApiAvailability availability =
207 QSysInfo::symbianVersion() < QSysInfo::SV_SF_3 ?
208 Unavailable : Unknown;
209 if (availability == Unknown) {
210 // Actually, we should ask CFeatureDiscovery::IsFeatureSupportedL()
211 // with FfFontTable here. But since at the time of writing, the
212 // FfFontTable flag check either gave false positives or false
213 // negatives. Here comes an implicit check via CFont::ExtendedFunction.
214 QSymbianTGetFontTableParam fontTableParams = {
215 MAKE_TAG('O', 'S', '/', '2'), 0, 0 };
216 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
217 CFont *font;
218 const TInt getFontErr = S60->screenDevice()->GetNearestFontInTwips(font, TFontSpec());
219 Q_ASSERT(getFontErr == KErrNone);
220 if (font->ExtendedFunction(QSymbianKFontGetFontTable, &fontTableParams) == KErrNone) {
221 font->ExtendedFunction(QSymbianKFontReleaseFontTable, &fontTableParams);
222 availability = Available;
223 } else {
224 availability = Unavailable;
225 }
226 S60->screenDevice()->ReleaseFont(font);
227 lock.relock();
228 }
229 return availability == Available;
230}
231
232// duplicated from qfontengine_xyz.cpp
233static inline unsigned int getChar(const QChar *str, int &i, const int len)
234{
235 unsigned int uc = str[i].unicode();
236 if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
237 uint low = str[i+1].unicode();
238 if (low >= 0xdc00 && low < 0xe000) {
239 uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
240 ++i;
241 }
242 }
243 return uc;
244}
245
246extern QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName); // qfontdatabase_s60.cpp
247
248CFont *QFontEngineS60::fontWithSize(qreal size) const
249{
250 CFont *result = 0;
251 const QString family = qt_symbian_fontNameWithAppFontMarker(QFontEngine::fontDef.family);
252 TFontSpec fontSpec(qt_QString2TPtrC(family), TInt(size));
253 fontSpec.iFontStyle.SetBitmapType(EAntiAliasedGlyphBitmap);
254 fontSpec.iFontStyle.SetPosture(QFontEngine::fontDef.style == QFont::StyleNormal?EPostureUpright:EPostureItalic);
255 fontSpec.iFontStyle.SetStrokeWeight(QFontEngine::fontDef.weight > QFont::Normal?EStrokeWeightBold:EStrokeWeightNormal);
256 const TInt errorCode = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(result, fontSpec);
257 Q_ASSERT(result && (errorCode == 0));
258 return result;
259}
260
261void QFontEngineS60::setFontScale(qreal scale)
262{
263 if (qFuzzyCompare(scale, qreal(1))) {
264 if (!m_originalFont)
265 m_originalFont = fontWithSize(m_originalFontSizeInPixels);
266 m_activeFont = m_originalFont;
267 } else {
268 const qreal scaledFontSizeInPixels = m_originalFontSizeInPixels * scale;
269 if (!m_scaledFont ||
270 (TInt(scaledFontSizeInPixels) != TInt(m_scaledFontSizeInPixels))) {
271 releaseFont(m_scaledFont);
272 m_scaledFontSizeInPixels = scaledFontSizeInPixels;
273 m_scaledFont = fontWithSize(m_scaledFontSizeInPixels);
274 }
275 m_activeFont = m_scaledFont;
276 }
277}
278
279void QFontEngineS60::releaseFont(CFont *&font)
280{
281 if (font) {
282 S60->screenDevice()->ReleaseFont(font);
283 font = 0;
284 }
285}
286
287QFontEngineS60::QFontEngineS60(const QFontDef &request, const QSymbianTypeFaceExtras *extras)
288 : m_extras(extras)
289 , m_originalFont(0)
290 , m_originalFontSizeInPixels((request.pixelSize >= 0)?
291 request.pixelSize:pointsToPixels(request.pointSize))
292 , m_scaledFont(0)
293 , m_scaledFontSizeInPixels(0)
294 , m_activeFont(0)
295{
296 QFontEngine::fontDef = request;
297 setFontScale(1.0);
298 cache_cost = sizeof(QFontEngineS60);
299}
300
301QFontEngineS60::~QFontEngineS60()
302{
303 releaseFont(m_originalFont);
304 releaseFont(m_scaledFont);
305}
306
307QFixed QFontEngineS60::emSquareSize() const
308{
309 const QFixed unitsPerEm = m_extras->unitsPerEm();
310 return unitsPerEm.toInt() == -1 ?
311 QFixed::fromReal(m_originalFontSizeInPixels) : unitsPerEm;
312}
313
314bool QFontEngineS60::stringToCMap(const QChar *characters, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
315{
316 if (*nglyphs < len) {
317 *nglyphs = len;
318 return false;
319 }
320
321 HB_Glyph *g = glyphs->glyphs;
322 const unsigned char* cmap = m_extras->cmap();
323 const bool isRtl = (flags & QTextEngine::RightToLeft);
324 for (int i = 0; i < len; ++i) {
325 const unsigned int uc = getChar(characters, i, len);
326 *g++ = QFontEngine::getTrueTypeGlyphIndex(cmap,
327 (isRtl && !m_extras->isSymbolCMap()) ? QChar::mirroredChar(uc) : uc);
328 }
329
330 glyphs->numGlyphs = g - glyphs->glyphs;
331 *nglyphs = glyphs->numGlyphs;
332
333 if (flags & QTextEngine::GlyphIndicesOnly)
334 return true;
335
336 recalcAdvances(glyphs, flags);
337 return true;
338}
339
340void QFontEngineS60::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
341{
342 Q_UNUSED(flags);
343 TOpenFontCharMetrics metrics;
344 const TUint8 *glyphBitmapBytes;
345 TSize glyphBitmapSize;
346 for (int i = 0; i < glyphs->numGlyphs; i++) {
347 getCharacterData(glyphs->glyphs[i], metrics, glyphBitmapBytes, glyphBitmapSize);
348 glyphs->advances_x[i] = metrics.HorizAdvance();
349 glyphs->advances_y[i] = 0;
350 }
351}
352
353#ifdef Q_SYMBIAN_HAS_GLYPHOUTLINE_API
354static bool parseGlyphPathData(const char *dataStr, const char *dataEnd, QPainterPath &path,
355 qreal fontPixelSize, const QPointF &offset, bool hinted);
356#endif //Q_SYMBIAN_HAS_GLYPHOUTLINE_API
357
358void QFontEngineS60::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions,
359 int nglyphs, QPainterPath *path,
360 QTextItem::RenderFlags flags)
361{
362#ifdef Q_SYMBIAN_HAS_GLYPHOUTLINE_API
363 Q_UNUSED(flags)
364 RGlyphOutlineIterator iterator;
365 const TInt error = iterator.Open(*m_activeFont, glyphs, nglyphs);
366 if (KErrNone != error)
367 return;
368 const qreal fontSizeInPixels = qreal(m_activeFont->HeightInPixels());
369 int count = 0;
370 do {
371 const TUint8* outlineUint8 = iterator.Outline();
372 const char* const outlineChar = reinterpret_cast<const char*>(outlineUint8);
373 const char* const outlineEnd = outlineChar + iterator.OutlineLength();
374 parseGlyphPathData(outlineChar, outlineEnd, *path, fontSizeInPixels,
375 positions[count++].toPointF(), false);
376 } while(KErrNone == iterator.Next() && count <= nglyphs);
377 iterator.Close();
378#else // Q_SYMBIAN_HAS_GLYPHOUTLINE_API
379 QFontEngine::addGlyphsToPath(glyphs, positions, nglyphs, path, flags);
380#endif //Q_SYMBIAN_HAS_GLYPHOUTLINE_API
381}
382
383QImage QFontEngineS60::alphaMapForGlyph(glyph_t glyph)
384{
385 // Note: On some Symbian versions (apparently <= Symbian^1), this
386 // function will return gray values 0x00, 0x10 ... 0xe0, 0xf0 due
387 // to a bug. The glyphs are nowhere perfectly opaque.
388 // This has been fixed for Symbian^3.
389
390 TOpenFontCharMetrics metrics;
391 const TUint8 *glyphBitmapBytes;
392 TSize glyphBitmapSize;
393 getCharacterData(glyph, metrics, glyphBitmapBytes, glyphBitmapSize);
394 QImage result(glyphBitmapBytes, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight, glyphBitmapSize.iWidth, QImage::Format_Indexed8);
395 result.setColorTable(grayPalette());
396 return result;
397}
398
399glyph_metrics_t QFontEngineS60::boundingBox(const QGlyphLayout &glyphs)
400{
401 if (glyphs.numGlyphs == 0)
402 return glyph_metrics_t();
403
404 QFixed w = 0;
405 for (int i = 0; i < glyphs.numGlyphs; ++i)
406 w += glyphs.effectiveAdvance(i);
407
408 return glyph_metrics_t(0, -ascent(), w - lastRightBearing(glyphs), ascent()+descent()+1, w, 0);
409}
410
411glyph_metrics_t QFontEngineS60::boundingBox_const(glyph_t glyph) const
412{
413 TOpenFontCharMetrics metrics;
414 const TUint8 *glyphBitmapBytes;
415 TSize glyphBitmapSize;
416 getCharacterData(glyph, metrics, glyphBitmapBytes, glyphBitmapSize);
417 const glyph_metrics_t result(
418 metrics.HorizBearingX(),
419 -metrics.HorizBearingY(),
420 metrics.Width(),
421 metrics.Height(),
422 metrics.HorizAdvance(),
423 0
424 );
425 return result;
426}
427
428glyph_metrics_t QFontEngineS60::boundingBox(glyph_t glyph)
429{
430 return boundingBox_const(glyph);
431}
432
433QFixed QFontEngineS60::ascent() const
434{
435 // Workaround for QTBUG-8013
436 // Stroke based fonts may return an incorrect FontMaxAscent of 0.
437 const QFixed ascent = m_originalFont->FontMaxAscent();
438 return (ascent > 0) ? ascent : QFixed::fromReal(m_originalFontSizeInPixels) - descent();
439}
440
441QFixed QFontEngineS60::descent() const
442{
443 return m_originalFont->FontMaxDescent();
444}
445
446QFixed QFontEngineS60::leading() const
447{
448 return 0;
449}
450
451qreal QFontEngineS60::maxCharWidth() const
452{
453 return m_originalFont->MaxCharWidthInPixels();
454}
455
456const char *QFontEngineS60::name() const
457{
458 return "QFontEngineS60";
459}
460
461bool QFontEngineS60::canRender(const QChar *string, int len)
462{
463 const unsigned char *cmap = m_extras->cmap();
464 for (int i = 0; i < len; ++i) {
465 const unsigned int uc = getChar(string, i, len);
466 if (QFontEngine::getTrueTypeGlyphIndex(cmap, uc) == 0)
467 return false;
468 }
469 return true;
470}
471
472QByteArray QFontEngineS60::getSfntTable(uint tag) const
473{
474 return m_extras->getSfntTable(tag);
475}
476
477bool QFontEngineS60::getSfntTableData(uint tag, uchar *buffer, uint *length) const
478{
479 return m_extras->getSfntTableData(tag, buffer, length);
480}
481
482QFontEngine::Type QFontEngineS60::type() const
483{
484 return QFontEngine::S60FontEngine;
485}
486
487void QFontEngineS60::getCharacterData(glyph_t glyph, TOpenFontCharMetrics& metrics, const TUint8*& bitmap, TSize& bitmapSize) const
488{
489 // Setting the most significant bit tells GetCharacterData
490 // that 'code' is a Glyph ID, rather than a UTF-16 value
491 const TUint specialCode = (TUint)glyph | 0x80000000;
492
493 const CFont::TCharacterDataAvailability availability =
494 m_activeFont->GetCharacterData(specialCode, metrics, bitmap, bitmapSize);
495 const glyph_t fallbackGlyph = '?';
496 if (availability != CFont::EAllCharacterData) {
497 const CFont::TCharacterDataAvailability fallbackAvailability =
498 m_activeFont->GetCharacterData(fallbackGlyph, metrics, bitmap, bitmapSize);
499 Q_ASSERT(fallbackAvailability == CFont::EAllCharacterData);
500 }
501}
502
503#ifdef Q_SYMBIAN_HAS_GLYPHOUTLINE_API
504static inline void skipSpacesAndComma(const char* &str, const char* const strEnd)
505{
506 while (str <= strEnd && (*str == ' ' || *str == ','))
507 ++str;
508}
509
510static bool parseGlyphPathData(const char *svgPath, const char *svgPathEnd, QPainterPath &path,
511 qreal fontPixelSize, const QPointF &offset, bool hinted)
512{
513 Q_UNUSED(hinted)
514 QPointF p1, p2, firstSubPathPoint;
515 qreal *elementValues[] =
516 {&p1.rx(), &p1.ry(), &p2.rx(), &p2.ry()};
517 const int unitsPerEm = 2048; // See: http://en.wikipedia.org/wiki/Em_%28typography%29
518 const qreal resizeFactor = fontPixelSize / unitsPerEm;
519
520 while (svgPath < svgPathEnd) {
521 skipSpacesAndComma(svgPath, svgPathEnd);
522 const char pathElem = *svgPath++;
523 skipSpacesAndComma(svgPath, svgPathEnd);
524
525 if (pathElem != 'Z') {
526 char *endStr = 0;
527 int elementValuesCount = 0;
528 for (int i = 0; i < 4; ++i) { // 4 = size of elementValues[]
529 qreal coordinateValue = strtod(svgPath, &endStr);
530 if (svgPath == endStr)
531 break;
532 if (i % 2) // Flip vertically
533 coordinateValue = -coordinateValue;
534 *elementValues[i] = coordinateValue * resizeFactor;
535 elementValuesCount++;
536 svgPath = endStr;
537 skipSpacesAndComma(svgPath, svgPathEnd);
538 }
539 p1 += offset;
540 if (elementValuesCount == 2)
541 p2 = firstSubPathPoint;
542 else
543 p2 += offset;
544 }
545
546 switch (pathElem) {
547 case 'M':
548 firstSubPathPoint = p1;
549 path.moveTo(p1);
550 break;
551 case 'Z':
552 path.closeSubpath();
553 break;
554 case 'L':
555 path.lineTo(p1);
556 break;
557 case 'Q':
558 path.quadTo(p1, p2);
559 break;
560 default:
561 return false;
562 }
563 }
564 return true;
565}
566#endif // Q_SYMBIAN_HAS_GLYPHOUTLINE_API
567
568QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.