source: trunk/src/gui/text/qfontengine_qpf.cpp@ 135

Last change on this file since 135 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 36.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qfontengine_qpf_p.h"
43
44#include "private/qpaintengine_raster_p.h"
45#include <QtCore/qlibraryinfo.h>
46#include <QtCore/qfileinfo.h>
47
48#include <QtCore/qfile.h>
49#include <QtCore/qdir.h>
50#include <QtCore/qbuffer.h>
51#if !defined(QT_NO_FREETYPE)
52#include "private/qfontengine_ft_p.h"
53#endif
54
55// for mmap
56#include <stdlib.h>
57#include <unistd.h>
58#include <sys/types.h>
59#include <sys/stat.h>
60#include <sys/mman.h>
61#include <fcntl.h>
62#include <errno.h>
63
64QT_BEGIN_NAMESPACE
65
66#ifndef QT_NO_QWS_QPF2
67
68QT_BEGIN_INCLUDE_NAMESPACE
69#include "qpfutil.cpp"
70
71#if defined(Q_WS_QWS)
72# include "private/qwscommand_qws_p.h"
73# include "qwsdisplay_qws.h"
74# include "qabstractfontengine_p.h"
75#endif
76#include "qplatformdefs.h"
77QT_END_INCLUDE_NAMESPACE
78
79//#define DEBUG_HEADER
80//#define DEBUG_FONTENGINE
81
82#if defined(DEBUG_HEADER)
83# define DEBUG_VERIFY qDebug
84#else
85# define DEBUG_VERIFY if (0) qDebug
86#endif
87
88#define READ_VERIFY(type, variable) \
89 if (tagPtr + sizeof(type) > endPtr) { \
90 DEBUG_VERIFY() << "read verify failed in line" << __LINE__; \
91 return 0; \
92 } \
93 variable = qFromBigEndian<type>(tagPtr); \
94 DEBUG_VERIFY() << "read value" << variable << "of type " #type; \
95 tagPtr += sizeof(type)
96
97template <typename T>
98T readValue(const uchar *&data)
99{
100 T value = qFromBigEndian<T>(data);
101 data += sizeof(T);
102 return value;
103}
104
105#define VERIFY(condition) \
106 if (!(condition)) { \
107 DEBUG_VERIFY() << "condition " #condition " failed in line" << __LINE__; \
108 return 0; \
109 }
110
111#define VERIFY_TAG(condition) \
112 if (!(condition)) { \
113 DEBUG_VERIFY() << "verifying tag condition " #condition " failed in line" << __LINE__ << "with tag" << tag; \
114 return 0; \
115 }
116
117static inline const uchar *verifyTag(const uchar *tagPtr, const uchar *endPtr)
118{
119 quint16 tag, length;
120 READ_VERIFY(quint16, tag);
121 READ_VERIFY(quint16, length);
122 if (tag == QFontEngineQPF::Tag_EndOfHeader)
123 return endPtr;
124 if (tag < QFontEngineQPF::NumTags) {
125 switch (tagTypes[tag]) {
126 case QFontEngineQPF::BitFieldType:
127 case QFontEngineQPF::StringType:
128 // can't do anything...
129 break;
130 case QFontEngineQPF::UInt32Type:
131 VERIFY_TAG(length == sizeof(quint32));
132 break;
133 case QFontEngineQPF::FixedType:
134 VERIFY_TAG(length == sizeof(quint32));
135 break;
136 case QFontEngineQPF::UInt8Type:
137 VERIFY_TAG(length == sizeof(quint8));
138 break;
139 }
140#if defined(DEBUG_HEADER)
141 if (length == 1)
142 qDebug() << "tag data" << hex << *tagPtr;
143 else if (length == 4)
144 qDebug() << "tag data" << hex << tagPtr[0] << tagPtr[1] << tagPtr[2] << tagPtr[3];
145#endif
146 }
147 return tagPtr + length;
148}
149
150const QFontEngineQPF::Glyph *QFontEngineQPF::findGlyph(glyph_t g) const
151{
152 if (!g || g >= glyphMapEntries)
153 return 0;
154 const quint32 *gmapPtr = reinterpret_cast<const quint32 *>(fontData + glyphMapOffset);
155 quint32 glyphPos = qFromBigEndian<quint32>(gmapPtr[g]);
156 if (glyphPos > glyphDataSize) {
157 if (glyphPos == 0xffffffff)
158 return 0;
159#if defined(DEBUG_FONTENGINE)
160 qDebug() << "glyph" << g << "outside of glyphData, remapping font file";
161#endif
162#if !defined(QT_NO_FREETYPE) && !defined(QT_FONTS_ARE_RESOURCES)
163 const_cast<QFontEngineQPF *>(this)->remapFontData();
164#endif
165 if (glyphPos > glyphDataSize)
166 return 0;
167 }
168 return reinterpret_cast<const Glyph *>(fontData + glyphDataOffset + glyphPos);
169}
170
171bool QFontEngineQPF::verifyHeader(const uchar *data, int size)
172{
173 VERIFY(size >= int(sizeof(Header)));
174 const Header *header = reinterpret_cast<const Header *>(data);
175 if (header->magic[0] != 'Q'
176 || header->magic[1] != 'P'
177 || header->magic[2] != 'F'
178 || header->magic[3] != '2')
179 return false;
180
181 VERIFY(header->majorVersion <= CurrentMajorVersion);
182 const quint16 dataSize = qFromBigEndian<quint16>(header->dataSize);
183 VERIFY(size >= int(sizeof(Header)) + dataSize);
184
185 const uchar *tagPtr = data + sizeof(Header);
186 const uchar *tagEndPtr = tagPtr + dataSize;
187 while (tagPtr < tagEndPtr - 3) {
188 tagPtr = verifyTag(tagPtr, tagEndPtr);
189 VERIFY(tagPtr);
190 }
191
192 VERIFY(tagPtr <= tagEndPtr);
193 return true;
194}
195
196QVariant QFontEngineQPF::extractHeaderField(const uchar *data, HeaderTag requestedTag)
197{
198 const Header *header = reinterpret_cast<const Header *>(data);
199 const uchar *tagPtr = data + sizeof(Header);
200 const uchar *endPtr = tagPtr + qFromBigEndian<quint16>(header->dataSize);
201 while (tagPtr < endPtr - 3) {
202 quint16 tag = readValue<quint16>(tagPtr);
203 quint16 length = readValue<quint16>(tagPtr);
204 if (tag == requestedTag) {
205 switch (tagTypes[requestedTag]) {
206 case StringType:
207 return QVariant(QString::fromUtf8(reinterpret_cast<const char *>(tagPtr), length));
208 case UInt32Type:
209 return QVariant(readValue<quint32>(tagPtr));
210 case UInt8Type:
211 return QVariant(uint(*tagPtr));
212 case FixedType:
213 return QVariant(QFixed::fromFixed(readValue<quint32>(tagPtr)).toReal());
214 case BitFieldType:
215 return QVariant(QByteArray(reinterpret_cast<const char *>(tagPtr), length));
216 }
217 return QVariant();
218 } else if (tag == Tag_EndOfHeader) {
219 break;
220 }
221 tagPtr += length;
222 }
223
224 return QVariant();
225}
226
227#endif // QT_NO_QWS_QPF2
228
229QString qws_fontCacheDir()
230{
231 QString dir;
232#if defined(Q_WS_QWS)
233 extern QString qws_dataDir();
234 dir = qws_dataDir();
235#else
236 dir = QDir::tempPath();
237#endif
238 dir.append(QLatin1String("/fonts/"));
239 QDir qd(dir);
240 if (!qd.exists() && !qd.mkpath(dir))
241 dir = QDir::tempPath();
242 return dir;
243}
244
245#ifndef QT_NO_QWS_QPF2
246
247#ifndef QT_FONTS_ARE_RESOURCES
248QList<QByteArray> QFontEngineQPF::cleanUpAfterClientCrash(const QList<int> &crashedClientIds)
249{
250 QList<QByteArray> removedFonts;
251 QDir dir(qws_fontCacheDir(), QLatin1String("*.qsf"));
252 for (int i = 0; i < int(dir.count()); ++i) {
253 const QByteArray fileName = QFile::encodeName(dir.absoluteFilePath(dir[i]));
254
255 int fd = ::open(fileName.constData(), O_RDONLY);
256 if (fd >= 0) {
257 void *header = ::mmap(0, sizeof(QFontEngineQPF::Header), PROT_READ, MAP_SHARED, fd, 0);
258 if (header && header != MAP_FAILED) {
259 quint32 lockValue = reinterpret_cast<QFontEngineQPF::Header *>(header)->lock;
260
261 if (lockValue && crashedClientIds.contains(lockValue)) {
262 removedFonts.append(fileName);
263 QFile::remove(QFile::decodeName(fileName));
264 }
265
266 ::munmap(header, sizeof(QFontEngineQPF::Header));
267 }
268 ::close(fd);
269 }
270 }
271 if (!removedFonts.isEmpty())
272 qDebug() << "list of corrupted and removed fonts:" << removedFonts;
273 return removedFonts;
274}
275#endif
276
277static inline unsigned int getChar(const QChar *str, int &i, const int len)
278{
279 unsigned int uc = str[i].unicode();
280 if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
281 uint low = str[i+1].unicode();
282 if (low >= 0xdc00 && low < 0xe000) {
283 uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
284 ++i;
285 }
286 }
287 return uc;
288}
289#ifdef QT_FONTS_ARE_RESOURCES
290QFontEngineQPF::QFontEngineQPF(const QFontDef &def, const uchar *bytes, int size)
291 : fd(-1), fontData(bytes), dataSize(size), renderingFontEngine(0)
292#else
293QFontEngineQPF::QFontEngineQPF(const QFontDef &def, int fileDescriptor, QFontEngine *fontEngine)
294 : fd(fileDescriptor), fontData(0), dataSize(0), renderingFontEngine(fontEngine)
295#endif
296{
297 fontDef = def;
298 cache_cost = 100;
299 freetype = 0;
300 externalCMap = 0;
301 cmapOffset = 0;
302 cmapSize = 0;
303 glyphMapOffset = 0;
304 glyphMapEntries = 0;
305 glyphDataOffset = 0;
306 glyphDataSize = 0;
307 kerning_pairs_loaded = false;
308 readOnly = true;
309
310#if defined(DEBUG_FONTENGINE)
311 qDebug() << "QFontEngineQPF::QFontEngineQPF( fd =" << fd << ", renderingFontEngine =" << renderingFontEngine << ")";
312#endif
313
314#ifndef QT_FONTS_ARE_RESOURCES
315 if (fd < 0) {
316 if (!renderingFontEngine)
317 return;
318
319 fileName = fontDef.family.toLower() + QLatin1String("_")
320 + QString::number(fontDef.pixelSize)
321 + QLatin1String("_") + QString::number(fontDef.weight)
322 + (fontDef.style != QFont::StyleNormal ?
323 QLatin1String("_italic") : QLatin1String(""))
324 + QLatin1String(".qsf");
325 fileName.replace(QLatin1Char(' '), QLatin1Char('_'));
326 fileName.prepend(qws_fontCacheDir());
327
328 const QByteArray encodedName = QFile::encodeName(fileName);
329 if (::access(encodedName, F_OK) == 0) {
330#if defined(DEBUG_FONTENGINE)
331 qDebug() << "found existing qpf:" << fileName;
332#endif
333 if (::access(encodedName, W_OK | R_OK) == 0)
334 fd = ::open(encodedName, O_RDWR);
335 else if (::access(encodedName, R_OK) == 0)
336 fd = ::open(encodedName, O_RDONLY);
337 } else {
338#if defined(DEBUG_FONTENGINE)
339 qDebug() << "creating qpf on the fly:" << fileName;
340#endif
341 if (::access(QFile::encodeName(qws_fontCacheDir()), W_OK) == 0) {
342 fd = ::open(encodedName, O_RDWR | O_EXCL | O_CREAT, 0644);
343
344 QBuffer buffer;
345 buffer.open(QIODevice::ReadWrite);
346 QPFGenerator generator(&buffer, renderingFontEngine);
347 generator.generate();
348 buffer.close();
349 const QByteArray &data = buffer.data();
350 ::write(fd, data.constData(), data.size());
351 }
352 }
353 }
354
355 QT_STATBUF st;
356 if (QT_FSTAT(fd, &st)) {
357#if defined(DEBUG_FONTENGINE)
358 qDebug() << "stat failed!";
359#endif
360 return;
361 }
362 dataSize = st.st_size;
363
364
365 fontData = (const uchar *)::mmap(0, st.st_size, PROT_READ | (renderingFontEngine ? PROT_WRITE : 0), MAP_SHARED, fd, 0);
366 if (!fontData || fontData == (const uchar *)MAP_FAILED) {
367#if defined(DEBUG_FONTENGINE)
368 perror("mmap failed");
369#endif
370 fontData = 0;
371 return;
372 }
373#endif //QT_FONTS_ARE_RESOURCES
374
375 if (!verifyHeader(fontData, dataSize)) {
376#if defined(DEBUG_FONTENGINE)
377 qDebug() << "verifyHeader failed!";
378#endif
379 return;
380 }
381
382 const Header *header = reinterpret_cast<const Header *>(fontData);
383
384 readOnly = (header->lock == 0xffffffff);
385
386 const uchar *data = fontData + sizeof(Header) + qFromBigEndian<quint16>(header->dataSize);
387 const uchar *endPtr = fontData + dataSize;
388 while (data <= endPtr - 8) {
389 quint16 blockTag = readValue<quint16>(data);
390 data += 2; // skip padding
391 quint32 blockSize = readValue<quint32>(data);
392
393 if (blockTag == CMapBlock) {
394 cmapOffset = data - fontData;
395 cmapSize = blockSize;
396 } else if (blockTag == GMapBlock) {
397 glyphMapOffset = data - fontData;
398 glyphMapEntries = blockSize / 4;
399 } else if (blockTag == GlyphBlock) {
400 glyphDataOffset = data - fontData;
401 glyphDataSize = blockSize;
402 }
403
404 data += blockSize;
405 }
406
407 face_id.filename = QFile::encodeName(extractHeaderField(fontData, Tag_FileName).toString());
408 face_id.index = extractHeaderField(fontData, Tag_FileIndex).toInt();
409#if !defined(QT_NO_FREETYPE) && !defined(QT_FONTS_ARE_RESOURCES)
410 freetype = QFreetypeFace::getFace(face_id);
411 if (!freetype) {
412 QString newPath =
413#ifndef QT_NO_SETTINGS
414 QLibraryInfo::location(QLibraryInfo::LibrariesPath) +
415#endif
416 QLatin1String("/fonts/") +
417 QFileInfo(QFile::decodeName(face_id.filename)).fileName();
418 face_id.filename = QFile::encodeName(newPath);
419 freetype = QFreetypeFace::getFace(face_id);
420 }
421 if (freetype) {
422 const quint32 qpfTtfRevision = extractHeaderField(fontData, Tag_FontRevision).toUInt();
423 uchar data[4];
424 uint length = 4;
425 bool ok = freetype->getSfntTable(MAKE_TAG('h', 'e', 'a', 'd'), data, &length);
426 if (!ok || length != 4
427 || qFromBigEndian<quint32>(data) != qpfTtfRevision) {
428 freetype->release(face_id);
429 freetype = 0;
430 }
431 }
432 if (!cmapOffset && freetype) {
433 freetypeCMapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p'));
434 externalCMap = reinterpret_cast<const uchar *>(freetypeCMapTable.constData());
435 cmapSize = freetypeCMapTable.size();
436 }
437#endif
438
439 // get the real cmap
440 if (cmapOffset) {
441 int tableSize = cmapSize;
442 const uchar *cmapPtr = getCMap(fontData + cmapOffset, tableSize, &symbol, &cmapSize);
443 if (cmapPtr)
444 cmapOffset = cmapPtr - fontData;
445 else
446 cmapOffset = 0;
447 } else if (externalCMap) {
448 int tableSize = cmapSize;
449 externalCMap = getCMap(externalCMap, tableSize, &symbol, &cmapSize);
450 }
451
452 // verify all the positions in the glyphMap
453 if (glyphMapOffset) {
454 const quint32 *gmapPtr = reinterpret_cast<const quint32 *>(fontData + glyphMapOffset);
455 for (uint i = 0; i < glyphMapEntries; ++i) {
456 quint32 glyphDataPos = qFromBigEndian<quint32>(gmapPtr[i]);
457 if (glyphDataPos == 0xffffffff)
458 continue;
459 if (glyphDataPos >= glyphDataSize) {
460 // error
461 glyphMapOffset = 0;
462 glyphMapEntries = 0;
463 break;
464 }
465 }
466 }
467
468#if defined(DEBUG_FONTENGINE)
469 if (!isValid())
470 qDebug() << "fontData" << fontData << "dataSize" << dataSize
471 << "externalCMap" << externalCMap << "cmapOffset" << cmapOffset
472 << "glyphMapOffset" << glyphMapOffset << "glyphDataOffset" << glyphDataOffset
473 << "fd" << fd << "glyphDataSize" << glyphDataSize;
474#endif
475#if defined(Q_WS_QWS)
476 if (isValid() && renderingFontEngine)
477 qt_fbdpy->sendFontCommand(QWSFontCommand::StartedUsingFont, QFile::encodeName(fileName));
478#endif
479}
480
481QFontEngineQPF::~QFontEngineQPF()
482{
483#if defined(Q_WS_QWS)
484 if (isValid() && renderingFontEngine)
485 qt_fbdpy->sendFontCommand(QWSFontCommand::StoppedUsingFont, QFile::encodeName(fileName));
486#endif
487 delete renderingFontEngine;
488 if (fontData)
489 munmap((void *)fontData, dataSize);
490 if (fd != -1)
491 ::close(fd);
492#if !defined(QT_NO_FREETYPE)
493 if (freetype)
494 freetype->release(face_id);
495#endif
496}
497
498bool QFontEngineQPF::getSfntTableData(uint tag, uchar *buffer, uint *length) const
499{
500#if !defined(QT_NO_FREETYPE)
501 if (freetype)
502 return freetype->getSfntTable(tag, buffer, length);
503#endif
504 Q_UNUSED(tag);
505 Q_UNUSED(buffer);
506 *length = 0;
507 return false;
508}
509
510bool QFontEngineQPF::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
511{
512 if (!externalCMap && !cmapOffset && renderingFontEngine) {
513 if (!renderingFontEngine->stringToCMap(str, len, glyphs, nglyphs, flags))
514 return false;
515#ifndef QT_NO_FREETYPE
516 const_cast<QFontEngineQPF *>(this)->ensureGlyphsLoaded(*glyphs);
517#endif
518 return true;
519 }
520
521 if (*nglyphs < len) {
522 *nglyphs = len;
523 return false;
524 }
525
526#if defined(DEBUG_FONTENGINE)
527 QSet<QChar> seenGlyphs;
528#endif
529
530 const uchar *cmap = externalCMap ? externalCMap : (fontData + cmapOffset);
531
532 bool mirrored = flags & QTextEngine::RightToLeft;
533 int glyph_pos = 0;
534 if (symbol) {
535 for (int i = 0; i < len; ++i) {
536 unsigned int uc = getChar(str, i, len);
537 if (mirrored)
538 uc = QChar::mirroredChar(uc);
539 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc);
540 if(!glyphs->glyphs[glyph_pos] && uc < 0x100)
541 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
542 ++glyph_pos;
543 }
544 } else {
545 for (int i = 0; i < len; ++i) {
546 unsigned int uc = getChar(str, i, len);
547 if (mirrored)
548 uc = QChar::mirroredChar(uc);
549 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc);
550#if 0 && defined(DEBUG_FONTENGINE)
551 QChar c(uc);
552 if (!findGlyph(glyphs[glyph_pos].glyph) && !seenGlyphs.contains(c))
553 qDebug() << "glyph for character" << c << "/" << hex << uc << "is" << dec << glyphs[glyph_pos].glyph;
554
555 seenGlyphs.insert(c);
556#endif
557 ++glyph_pos;
558 }
559 }
560
561 *nglyphs = glyph_pos;
562 glyphs->numGlyphs = glyph_pos;
563 recalcAdvances(glyphs, flags);
564 return true;
565}
566
567void QFontEngineQPF::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const
568{
569#ifndef QT_NO_FREETYPE
570 const_cast<QFontEngineQPF *>(this)->ensureGlyphsLoaded(*glyphs);
571#endif
572 for (int i = 0; i < glyphs->numGlyphs; ++i) {
573 const Glyph *g = findGlyph(glyphs->glyphs[i]);
574 if (!g) {
575 glyphs->glyphs[i] = 0;
576 continue;
577 }
578 glyphs->advances_x[i] = g->advance;
579 glyphs->advances_y[i] = 0;
580 }
581}
582
583QImage QFontEngineQPF::alphaMapForGlyph(glyph_t g)
584{
585 const Glyph *glyph = findGlyph(g);
586 if (!glyph)
587 QImage();
588
589 const uchar *bits = ((const uchar *) glyph) + sizeof(Glyph);
590
591 QImage image(glyph->width, glyph->height, QImage::Format_Indexed8);
592 for (int j=0; j<256; ++j)
593 image.setColor(j, 0xff000000 | j | (j<<8) | (j<<16));
594
595 for (int i=0; i<glyph->height; ++i) {
596 memcpy(image.scanLine(i), bits, glyph->bytesPerLine);
597 bits += glyph->bytesPerLine;
598 }
599 return image;
600}
601
602void QFontEngineQPF::draw(QPaintEngine *p, qreal _x, qreal _y, const QTextItemInt &si)
603{
604 QPaintEngineState *pState = p->state;
605 QRasterPaintEngine *paintEngine = static_cast<QRasterPaintEngine*>(p);
606
607 QTransform matrix = pState->transform();
608 matrix.translate(_x, _y);
609 QFixed x = QFixed::fromReal(matrix.dx());
610 QFixed y = QFixed::fromReal(matrix.dy());
611
612 QVarLengthArray<QFixedPoint> positions;
613 QVarLengthArray<glyph_t> glyphs;
614 getGlyphPositions(si.glyphs, matrix, si.flags, glyphs, positions);
615 if (glyphs.size() == 0)
616 return;
617
618 for(int i = 0; i < glyphs.size(); i++) {
619 const Glyph *glyph = findGlyph(glyphs[i]);
620 if (!glyph)
621 continue;
622
623 const int depth = 8; //###
624
625 paintEngine->alphaPenBlt(reinterpret_cast<const uchar *>(glyph) + sizeof(Glyph), glyph->bytesPerLine, depth,
626 qRound(positions[i].x) + glyph->x,
627 qRound(positions[i].y) + glyph->y,
628 glyph->width, glyph->height);
629 }
630}
631
632void QFontEngineQPF::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
633{
634 if (renderingFontEngine &&
635 (renderingFontEngine->type() != QFontEngine::Proxy
636 || static_cast<QProxyFontEngine *>(renderingFontEngine)->capabilities() & QAbstractFontEngine::CanOutlineGlyphs)) {
637 renderingFontEngine->addOutlineToPath(x, y, glyphs, path, flags);
638 return;
639 }
640 addBitmapFontToPath(x, y, glyphs, path, flags);
641}
642
643glyph_metrics_t QFontEngineQPF::boundingBox(const QGlyphLayout &glyphs)
644{
645#ifndef QT_NO_FREETYPE
646 const_cast<QFontEngineQPF *>(this)->ensureGlyphsLoaded(glyphs);
647#endif
648
649 glyph_metrics_t overall;
650 // initialize with line height, we get the same behaviour on all platforms
651 overall.y = -ascent();
652 overall.height = ascent() + descent() + 1;
653
654 QFixed ymax = 0;
655 QFixed xmax = 0;
656 for (int i = 0; i < glyphs.numGlyphs; i++) {
657 const Glyph *g = findGlyph(glyphs.glyphs[i]);
658 if (!g)
659 continue;
660
661 QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
662 QFixed y = overall.yoff + glyphs.offsets[i].y + g->y;
663 overall.x = qMin(overall.x, x);
664 overall.y = qMin(overall.y, y);
665 xmax = qMax(xmax, x + g->width);
666 ymax = qMax(ymax, y + g->height);
667 overall.xoff += g->advance;
668 }
669 overall.height = qMax(overall.height, ymax - overall.y);
670 overall.width = xmax - overall.x;
671
672 return overall;
673}
674
675glyph_metrics_t QFontEngineQPF::boundingBox(glyph_t glyph)
676{
677#ifndef QT_NO_FREETYPE
678 {
679 QGlyphLayoutArray<1> tmp;
680 tmp.glyphs[0] = glyph;
681 const_cast<QFontEngineQPF *>(this)->ensureGlyphsLoaded(tmp);
682 }
683#endif
684 glyph_metrics_t overall;
685 const Glyph *g = findGlyph(glyph);
686 if (!g)
687 return overall;
688 overall.x = g->x;
689 overall.y = g->y;
690 overall.width = g->width;
691 overall.height = g->height;
692 overall.xoff = g->advance;
693 return overall;
694}
695
696QFixed QFontEngineQPF::ascent() const
697{
698 return QFixed::fromReal(extractHeaderField(fontData, Tag_Ascent).value<qreal>());
699}
700
701QFixed QFontEngineQPF::descent() const
702{
703 return QFixed::fromReal(extractHeaderField(fontData, Tag_Descent).value<qreal>());
704}
705
706QFixed QFontEngineQPF::leading() const
707{
708 return QFixed::fromReal(extractHeaderField(fontData, Tag_Leading).value<qreal>());
709}
710
711qreal QFontEngineQPF::maxCharWidth() const
712{
713 return extractHeaderField(fontData, Tag_MaxCharWidth).value<qreal>();
714}
715
716qreal QFontEngineQPF::minLeftBearing() const
717{
718 return extractHeaderField(fontData, Tag_MinLeftBearing).value<qreal>();
719}
720
721qreal QFontEngineQPF::minRightBearing() const
722{
723 return extractHeaderField(fontData, Tag_MinRightBearing).value<qreal>();
724}
725
726QFixed QFontEngineQPF::underlinePosition() const
727{
728 return QFixed::fromReal(extractHeaderField(fontData, Tag_UnderlinePosition).value<qreal>());
729}
730
731QFixed QFontEngineQPF::lineThickness() const
732{
733 return QFixed::fromReal(extractHeaderField(fontData, Tag_LineThickness).value<qreal>());
734}
735
736QFontEngine::Type QFontEngineQPF::type() const
737{
738 return QFontEngine::QPF2;
739}
740
741bool QFontEngineQPF::canRender(const QChar *string, int len)
742{
743 const uchar *cmap = externalCMap ? externalCMap : (fontData + cmapOffset);
744
745 if (symbol) {
746 for (int i = 0; i < len; ++i) {
747 unsigned int uc = getChar(string, i, len);
748 glyph_t g = getTrueTypeGlyphIndex(cmap, uc);
749 if(!g && uc < 0x100)
750 g = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
751 if (!g)
752 return false;
753 }
754 } else {
755 for (int i = 0; i < len; ++i) {
756 unsigned int uc = getChar(string, i, len);
757 if (!getTrueTypeGlyphIndex(cmap, uc))
758 return false;
759 }
760 }
761 return true;
762}
763
764bool QFontEngineQPF::isValid() const
765{
766 return fontData && dataSize && (cmapOffset || externalCMap || renderingFontEngine)
767 && glyphMapOffset && glyphDataOffset && (fd >= 0 || glyphDataSize > 0);
768}
769
770#if !defined(QT_NO_FREETYPE)
771FT_Face QFontEngineQPF::lockFace() const
772{
773 Q_ASSERT(freetype);
774 freetype->lock();
775 FT_Face face = freetype->face;
776
777 // ### not perfect
778 const int ysize = fontDef.pixelSize << 6;
779 const int xsize = ysize;
780
781 if (freetype->xsize != xsize || freetype->ysize != ysize) {
782 FT_Set_Char_Size(face, xsize, ysize, 0, 0);
783 freetype->xsize = xsize;
784 freetype->ysize = ysize;
785 }
786 FT_Matrix identityMatrix;
787 identityMatrix.xx = 0x10000;
788 identityMatrix.yy = 0x10000;
789 identityMatrix.xy = 0;
790 identityMatrix.yx = 0;
791 if (freetype->matrix.xx != identityMatrix.xx ||
792 freetype->matrix.yy != identityMatrix.yy ||
793 freetype->matrix.xy != identityMatrix.xy ||
794 freetype->matrix.yx != identityMatrix.yx) {
795 freetype->matrix = identityMatrix;
796 FT_Set_Transform(face, &freetype->matrix, 0);
797 }
798 return face;
799}
800
801void QFontEngineQPF::unlockFace() const
802{
803 freetype->unlock();
804}
805
806void QFontEngineQPF::doKerning(QGlyphLayout *g, QTextEngine::ShaperFlags flags) const
807{
808 if (!kerning_pairs_loaded) {
809 kerning_pairs_loaded = true;
810 if (freetype) {
811 lockFace();
812 if (freetype->face->size->metrics.x_ppem != 0) {
813 QFixed scalingFactor(freetype->face->units_per_EM/freetype->face->size->metrics.x_ppem);
814 unlockFace();
815 const_cast<QFontEngineQPF *>(this)->loadKerningPairs(scalingFactor);
816 } else {
817 unlockFace();
818 }
819 }
820 }
821 QFontEngine::doKerning(g, flags);
822}
823
824HB_Error QFontEngineQPF::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
825{
826 if (!freetype)
827 return HB_Err_Not_Covered;
828 lockFace();
829 HB_Error result = freetype->getPointInOutline(glyph, flags, point, xpos, ypos, nPoints);
830 unlockFace();
831 return result;
832}
833
834QFixed QFontEngineQPF::emSquareSize() const
835{
836 if (!freetype)
837 return QFontEngine::emSquareSize();
838 if (FT_IS_SCALABLE(freetype->face))
839 return freetype->face->units_per_EM;
840 else
841 return freetype->face->size->metrics.y_ppem;
842}
843
844void QFontEngineQPF::ensureGlyphsLoaded(const QGlyphLayout &glyphs)
845{
846 if (readOnly)
847 return;
848 bool locked = false;
849 for (int i = 0; i < glyphs.numGlyphs; ++i) {
850 if (!glyphs.glyphs[i])
851 continue;
852 const Glyph *g = findGlyph(glyphs.glyphs[i]);
853 if (g)
854 continue;
855 if (!locked) {
856 if (!lockFile())
857 return;
858 locked = true;
859 g = findGlyph(glyphs.glyphs[i]);
860 if (g)
861 continue;
862 }
863 loadGlyph(glyphs.glyphs[i]);
864 }
865 if (locked) {
866 unlockFile();
867#if defined(DEBUG_FONTENGINE)
868 qDebug() << "Finished rendering glyphs\n";
869#endif
870 }
871}
872
873void QFontEngineQPF::loadGlyph(glyph_t glyph)
874{
875 quint32 glyphPos = ~0;
876
877 if (!renderingFontEngine)
878 return;
879
880 QImage img = renderingFontEngine->alphaMapForGlyph(glyph).convertToFormat(QImage::Format_Indexed8);
881 glyph_metrics_t metrics = renderingFontEngine->boundingBox(glyph);
882 renderingFontEngine->removeGlyphFromCache(glyph);
883
884 off_t oldSize = ::lseek(fd, 0, SEEK_END);
885 if (oldSize == (off_t)-1)
886 return;
887
888 Glyph g;
889 g.width = img.width();
890 g.height = img.height();
891 g.bytesPerLine = img.bytesPerLine();
892 g.x = qRound(metrics.x);
893 g.y = qRound(metrics.y);
894 g.advance = qRound(metrics.xoff);
895
896 ::write(fd, &g, sizeof(g));
897 ::write(fd, img.bits(), img.numBytes());
898
899 glyphPos = oldSize - glyphDataOffset;
900#if 0 && defined(DEBUG_FONTENGINE)
901 qDebug() << "glyphPos for new glyph" << glyph << "is" << glyphPos << "oldSize" << oldSize << "glyphDataOffset" << glyphDataOffset;
902#endif
903
904 quint32 *gmap = (quint32 *)(fontData + glyphMapOffset);
905 gmap[glyph] = qToBigEndian(glyphPos);
906
907 glyphDataSize = glyphPos + sizeof(g) + img.numBytes();
908 quint32 *blockSizePtr = (quint32 *)(fontData + glyphDataOffset - 4);
909 *blockSizePtr = qToBigEndian(glyphDataSize);
910}
911
912bool QFontEngineQPF::lockFile()
913{
914 // #### this does not handle the case when the process holding the
915 // lock hangs for some reason
916 struct flock lock;
917 lock.l_type = F_WRLCK;
918 lock.l_whence = SEEK_SET;
919 lock.l_start = 0;
920 lock.l_len = 0; // lock the whole file
921 while (fcntl(fd, F_SETLKW, &lock) != 0) {
922 if (errno == EINTR)
923 continue;
924 perror("locking qpf");
925 return false;
926 }
927 Header *header = (Header *)fontData;
928 if (header->lock) {
929 lock.l_type = F_UNLCK;
930 if (fcntl(fd, F_SETLK, &lock) != 0)
931 perror("unlocking possibly corrupt qpf");
932 return false;
933 }
934#if defined(Q_WS_QWS)
935 extern int qws_client_id;
936 // qws_client_id == 0 means we're the server. in this case we just
937 // set the id to 1
938 header->lock = qws_client_id ? qws_client_id : 1;
939#else
940 header->lock = 1;
941#endif
942 return true;
943}
944
945void QFontEngineQPF::unlockFile()
946{
947 ((Header *)fontData)->lock = 0;
948
949 struct flock lock;
950 lock.l_type = F_UNLCK;
951 lock.l_whence = SEEK_SET;
952 lock.l_start = 0;
953 lock.l_len = 0; // lock the whole file
954 if (fcntl(fd, F_SETLK, &lock) != 0) {
955 perror("unlocking qpf");
956 }
957
958 remapFontData();
959}
960
961void QFontEngineQPF::remapFontData()
962{
963 off_t newFileSize = ::lseek(fd, 0, SEEK_END);
964 if (newFileSize == (off_t)-1) {
965#ifdef DEBUG_FONTENGINE
966 perror("QFontEngineQPF::remapFontData: lseek failed");
967#endif
968 fontData = 0;
969 return;
970 }
971
972#ifndef QT_NO_MREMAP
973 fontData = static_cast<uchar *>(::mremap(const_cast<uchar *>(fontData), dataSize, newFileSize, MREMAP_MAYMOVE));
974 if (!fontData || fontData == (const uchar *)MAP_FAILED) {
975# if defined(DEBUG_FONTENGINE)