source: trunk/src/gui/text/qfontengine_x11.cpp@ 219

Last change on this file since 219 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.3 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 "qbitmap.h"
43
44// #define FONTENGINE_DEBUG
45
46#include <qapplication.h>
47#include <qbytearray.h>
48#include <qdebug.h>
49#include <qtextcodec.h>
50#include <qthread.h>
51
52#include "qfontdatabase.h"
53#include "qpaintdevice.h"
54#include "qpainter.h"
55#include "qvarlengtharray.h"
56#include "qwidget.h"
57#include "qsettings.h"
58#include "qfile.h"
59
60#include <private/qpaintengine_x11_p.h>
61#include "qfont.h"
62#include "qfont_p.h"
63#include "qfontengine_p.h"
64#include <qhash.h>
65
66#include <private/qpainter_p.h>
67#include <private/qunicodetables_p.h>
68
69#include <private/qt_x11_p.h>
70#include <private/qpixmap_x11_p.h>
71#include "qx11info_x11.h"
72#include "qfontengine_x11_p.h"
73
74#include <limits.h>
75
76#include <ft2build.h>
77#if defined(FT_LCD_FILTER_H)
78#include FT_LCD_FILTER_H
79#endif
80
81#if defined(FC_LCD_FILTER)
82
83#ifndef FC_LCD_FILTER_NONE
84#define FC_LCD_FILTER_NONE FC_LCD_NONE
85#endif
86
87#ifndef FC_LCD_FILTER_DEFAULT
88#define FC_LCD_FILTER_DEFAULT FC_LCD_DEFAULT
89#endif
90
91#ifndef FC_LCD_FILTER_LIGHT
92#define FC_LCD_FILTER_LIGHT FC_LCD_LIGHT
93#endif
94
95#ifndef FC_LCD_FILTER_LEGACY
96#define FC_LCD_FILTER_LEGACY FC_LCD_LEGACY
97#endif
98
99#endif
100
101QT_BEGIN_NAMESPACE
102
103
104// ------------------------------------------------------------------
105// Multi XLFD engine
106// ------------------------------------------------------------------
107
108QFontEngineMultiXLFD::QFontEngineMultiXLFD(const QFontDef &r, const QList<int> &l, int s)
109 : QFontEngineMulti(l.size()), encodings(l), screen(s), request(r)
110{
111 loadEngine(0);
112 fontDef = engines[0]->fontDef;
113}
114
115QFontEngineMultiXLFD::~QFontEngineMultiXLFD()
116{ }
117
118void QFontEngineMultiXLFD::loadEngine(int at)
119{
120 Q_ASSERT(at < engines.size());
121 Q_ASSERT(engines.at(at) == 0);
122 const int encoding = encodings.at(at);
123 QFontEngine *fontEngine = QFontDatabase::loadXlfd(0, QUnicodeTables::Common, request, encoding);
124 Q_ASSERT(fontEngine != 0);
125 fontEngine->ref.ref();
126 engines[at] = fontEngine;
127}
128
129// ------------------------------------------------------------------
130// Xlfd font engine
131// ------------------------------------------------------------------
132
133#ifndef QT_NO_FREETYPE
134
135static QStringList *qt_fontpath = 0;
136
137static QStringList fontPath()
138{
139 if (qt_fontpath)
140 return *qt_fontpath;
141
142 // append qsettings fontpath
143 QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
144 settings.beginGroup(QLatin1String("Qt"));
145
146 QStringList fontpath;
147
148 int npaths;
149 char** font_path;
150 font_path = XGetFontPath(X11->display, &npaths);
151 bool xfsconfig_read = false;
152 for (int i=0; i<npaths; i++) {
153 // If we're using xfs, append font paths from /etc/X11/fs/config
154 // can't hurt, and chances are we'll get all fonts that way.
155 if (((font_path[i])[0] != '/') && !xfsconfig_read) {
156 // We're using xfs -> read its config
157 bool finished = false;
158 QFile f(QLatin1String("/etc/X11/fs/config"));
159 if (!f.exists())
160 f.setFileName(QLatin1String("/usr/X11R6/lib/X11/fs/config"));
161 if (!f.exists())
162 f.setFileName(QLatin1String("/usr/X11/lib/X11/fs/config"));
163 if (f.exists()) {
164 f.open(QIODevice::ReadOnly);
165 while (f.error()==QFile::NoError && !finished) {
166 QString fs = QString::fromLocal8Bit(f.readLine(1024));
167 fs=fs.trimmed();
168 if (fs.left(9)==QLatin1String("catalogue") && fs.contains(QLatin1Char('='))) {
169 fs = fs.mid(fs.indexOf(QLatin1Char('=')) + 1).trimmed();
170 bool end = false;
171 while (f.error()==QFile::NoError && !end) {
172 if (fs[int(fs.length())-1] == QLatin1Char(','))
173 fs = fs.left(fs.length()-1);
174 else
175 end = true;
176
177 fs = fs.left(fs.indexOf(QLatin1String(":unscaled")));
178 if (fs[0] != QLatin1Char('#'))
179 fontpath += fs;
180 fs = QLatin1String(f.readLine(1024));
181 fs = fs.trimmed();
182 if (fs.isEmpty())
183 end = true;
184 }
185 finished = true;
186 }
187 }
188 f.close();
189 }
190 xfsconfig_read = true;
191 } else {
192 QString fs = QString::fromLocal8Bit(font_path[i]);
193 fontpath += fs.left(fs.indexOf(QLatin1String(":unscaled")));
194 }
195 }
196 XFreeFontPath(font_path);
197
198 // append qsettings fontpath
199 QStringList fp = settings.value(QLatin1String("fontPath")).toStringList();
200 if (!fp.isEmpty())
201 fontpath += fp;
202
203 qt_fontpath = new QStringList(fontpath);
204 return fontpath;
205}
206
207static QFontEngine::FaceId fontFile(const QByteArray &_xname, QFreetypeFace **freetype, int *synth)
208{
209 *freetype = 0;
210 *synth = 0;
211
212 QByteArray xname = _xname.toLower();
213
214 int pos = 0;
215 int minus = 0;
216 while (minus < 5 && (pos = xname.indexOf('-', pos + 1)))
217 ++minus;
218 QByteArray searchname = xname.left(pos);
219 while (minus < 12 && (pos = xname.indexOf('-', pos + 1)))
220 ++minus;
221 QByteArray encoding = xname.mid(pos + 1);
222 //qDebug("xname='%s', searchname='%s', encoding='%s'", xname.data(), searchname.data(), encoding.data());
223 QStringList fontpath = fontPath();
224 QFontEngine::FaceId face_id;
225 face_id.index = 0;
226
227 QByteArray best_mapping;
228
229 for (QStringList::ConstIterator it = fontpath.constBegin(); it != fontpath.constEnd(); ++it) {
230 if ((*it).left(1) != QLatin1String("/"))
231 continue; // not a path name, a font server
232 QString fontmapname;
233 int num = 0;
234 // search font.dir and font.scale for the right file
235 while (num < 2) {
236 if (num == 0)
237 fontmapname = (*it) + QLatin1String("/fonts.scale");
238 else
239 fontmapname = (*it) + QLatin1String("/fonts.dir");
240 ++num;
241 //qWarning(fontmapname);
242 QFile fontmap(fontmapname);
243 if (!fontmap.open(QIODevice::ReadOnly))
244 continue;
245 while (!fontmap.atEnd()) {
246 QByteArray mapping = fontmap.readLine();
247 QByteArray lmapping = mapping.toLower();
248
249 //qWarning(xfontname);
250 //qWarning(mapping);
251 if (!lmapping.contains(searchname))
252 continue;
253 int index = mapping.indexOf(' ');
254 QByteArray ffn = mapping.mid(0,index);
255 // remove bitmap formats freetype can't handle
256 if (ffn.contains(".spd") || ffn.contains(".phont"))
257 continue;
258 bool best_match = false;
259 if (!best_mapping.isEmpty()) {
260 if (lmapping.contains("-0-0-0-0-")) { // scalable font
261 best_match = true;
262 goto found;
263 }
264 if (lmapping.contains(encoding) && !best_mapping.toLower().contains(encoding))
265 goto found;
266 continue;
267 }
268
269 found:
270 int colon = ffn.lastIndexOf(':');
271 if (colon != -1) {
272 QByteArray s = ffn.left(colon);
273 ffn = ffn.mid(colon + 1);
274 if (s.contains("ds="))
275 *synth |= QFontEngine::SynthesizedBold;
276 if (s.contains("ai="))
277 *synth |= QFontEngine::SynthesizedItalic;
278 }
279 face_id.filename = (*it).toLocal8Bit() + '/' + ffn;
280 best_mapping = mapping;
281 if (best_match)
282 goto end;
283 }
284 }
285 }
286end:
287// qDebug("fontfile for %s is from '%s'\n got %s synth=%d", xname.data(),
288// best_mapping.data(), face_id.filename.data(), *synth);
289 *freetype = QFreetypeFace::getFace(face_id);
290 if (!*freetype) {
291 face_id.index = 0;
292 face_id.filename = QByteArray();
293 }
294 return face_id;
295}
296
297#endif // QT_NO_FREETYPE
298
299// defined in qfontdatabase_x11.cpp
300extern int qt_mib_for_xlfd_encoding(const char *encoding);
301extern int qt_xlfd_encoding_id(const char *encoding);
302
303static inline XCharStruct *charStruct(XFontStruct *xfs, uint ch)
304{
305 XCharStruct *xcs = 0;
306 unsigned char r = ch>>8;
307 unsigned char c = ch&0xff;
308 if (xfs->per_char &&
309 r >= xfs->min_byte1 &&
310 r <= xfs->max_byte1 &&
311 c >= xfs->min_char_or_byte2 &&
312 c <= xfs->max_char_or_byte2) {
313 xcs = xfs->per_char + ((r - xfs->min_byte1) *
314 (xfs->max_char_or_byte2 -
315 xfs->min_char_or_byte2 + 1)) +
316 (c - xfs->min_char_or_byte2);
317 if (xcs->width == 0 && xcs->ascent == 0 && xcs->descent == 0)
318 xcs = 0;
319 }
320 return xcs;
321}
322
323QFontEngineXLFD::QFontEngineXLFD(XFontStruct *fs, const QByteArray &name, int mib)
324 : _fs(fs), _name(name), _codec(0), _cmap(mib)
325{
326 if (_cmap) _codec = QTextCodec::codecForMib(_cmap);
327
328 cache_cost = (((fs->max_byte1 - fs->min_byte1) *
329 (fs->max_char_or_byte2 - fs->min_char_or_byte2 + 1)) +
330 fs->max_char_or_byte2 - fs->min_char_or_byte2);
331 cache_cost = ((fs->max_bounds.ascent + fs->max_bounds.descent) *
332 (fs->max_bounds.width * cache_cost / 8));
333 lbearing = SHRT_MIN;
334 rbearing = SHRT_MIN;
335 face_id.index = -1;
336 freetype = 0;
337 synth = 0;
338}
339
340QFontEngineXLFD::~QFontEngineXLFD()
341{
342 XFreeFont(QX11Info::display(), _fs);
343 _fs = 0;
344#ifndef QT_NO_FREETYPE
345 if (freetype)
346 freetype->release(face_id);
347#endif
348}
349
350bool QFontEngineXLFD::stringToCMap(const QChar *s, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
351{
352 if (*nglyphs < len) {
353 *nglyphs = len;
354 return false;
355 }
356
357 // filter out surrogates, we can't handle them anyway with XLFD fonts
358 QVarLengthArray<ushort> _s(len);
359 QChar *str = (QChar *)_s.data();
360 for (int i = 0; i < len; ++i) {
361 if (i < len - 1
362 && s[i].unicode() >= 0xd800 && s[i].unicode() < 0xdc00
363 && s[i+1].unicode() >= 0xdc00 && s[i].unicode() < 0xe000) {
364 *str = QChar();
365 ++i;
366 } else {
367 *str = s[i];
368 }
369 ++str;
370 }
371
372 len = str - (QChar *)_s.data();
373 str = (QChar *)_s.data();
374
375 bool mirrored = flags & QTextEngine::RightToLeft;
376 if (_codec) {
377 bool haveNbsp = false;
378 for (int i = 0; i < len; i++)
379 if (str[i].unicode() == 0xa0) {
380 haveNbsp = true;
381 break;
382 }
383
384 QVarLengthArray<unsigned short> ch(len);
385 QChar *chars = (QChar *)ch.data();
386 if (haveNbsp || mirrored) {
387 for (int i = 0; i < len; i++)
388 chars[i] = (str[i].unicode() == 0xa0 ? 0x20 :
389 (mirrored ? QChar::mirroredChar(str[i].unicode()) : str[i].unicode()));
390 } else {
391 for (int i = 0; i < len; i++)
392 chars[i] = str[i].unicode();
393 }
394 QTextCodec::ConverterState state;
395 state.flags = QTextCodec::ConvertInvalidToNull;
396 QByteArray ba = _codec->fromUnicode(chars, len, &state);
397 if (ba.length() == 2*len) {
398 // double byte encoding
399 const uchar *data = (const uchar *)ba.constData();
400 for (int i = 0; i < len; i++) {
401 glyphs->glyphs[i] = ((ushort)data[0] << 8) + data[1];
402 data += 2;
403 }
404 } else {
405 const uchar *data = (const uchar *)ba.constData();
406 for (int i = 0; i < len; i++)
407 glyphs->glyphs[i] = (ushort)data[i];
408 }
409 } else {
410 int i = len;
411 const QChar *c = str + len;
412 if (mirrored) {
413 while (c != str)
414 glyphs->glyphs[--i] = (--c)->unicode() == 0xa0 ? 0x20 : QChar::mirroredChar(c->unicode());
415 } else {
416 while (c != str)
417 glyphs->glyphs[--i] = (--c)->unicode() == 0xa0 ? 0x20 : c->unicode();
418 }
419 }
420 *nglyphs = len;
421 glyphs->numGlyphs = len;
422
423 if (!(flags & QTextEngine::GlyphIndicesOnly))
424 recalcAdvances(glyphs, flags);
425 return true;
426}
427
428void QFontEngineXLFD::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags /*flags*/) const
429{
430 int i = glyphs->numGlyphs;
431 XCharStruct *xcs;
432 // inlined for better perfomance
433 if (!_fs->per_char) {
434 xcs = &_fs->min_bounds;
435 while (i != 0) {
436 --i;
437 const unsigned char r = glyphs->glyphs[i] >> 8;
438 const unsigned char c = glyphs->glyphs[i] & 0xff;
439 if (r >= _fs->min_byte1 &&
440 r <= _fs->max_byte1 &&
441 c >= _fs->min_char_or_byte2 &&
442 c <= _fs->max_char_or_byte2) {
443 glyphs->advances_x[i] = xcs->width;
444 } else {
445 glyphs->glyphs[i] = 0;
446 }
447 }
448 }
449 else if (!_fs->max_byte1) {
450 XCharStruct *base = _fs->per_char - _fs->min_char_or_byte2;
451 while (i != 0) {
452 unsigned int gl = glyphs->glyphs[--i];
453 xcs = (gl >= _fs->min_char_or_byte2 && gl <= _fs->max_char_or_byte2) ?
454 base + gl : 0;
455 if (!xcs || (!xcs->width && !xcs->ascent && !xcs->descent)) {
456 glyphs->glyphs[i] = 0;
457 } else {
458 glyphs->advances_x[i] = xcs->width;
459 }
460 }
461 }
462 else {
463 while (i != 0) {
464 xcs = charStruct(_fs, glyphs->glyphs[--i]);
465 if (!xcs) {
466 glyphs->glyphs[i] = 0;
467 } else {
468 glyphs->advances_x[i] = xcs->width;
469 }
470 }
471 }
472}
473
474glyph_metrics_t QFontEngineXLFD::boundingBox(const QGlyphLayout &glyphs)
475{
476 int i;
477
478 glyph_metrics_t overall;
479 // initialize with line height, we get the same behaviour on all platforms
480 overall.y = -ascent();
481 overall.height = ascent() + descent() + 1;
482 QFixed ymax;
483 QFixed xmax;
484 for (i = 0; i < glyphs.numGlyphs; i++) {
485 XCharStruct *xcs = charStruct(_fs, glyphs.glyphs[i]);
486 if (xcs) {
487 QFixed x = overall.xoff + glyphs.offsets[i].x + xcs->lbearing;
488 QFixed y = overall.yoff + glyphs.offsets[i].y - xcs->ascent;
489 overall.x = qMin(overall.x, x);
490 overall.y = qMin(overall.y, y);
491 xmax = qMax(xmax, overall.xoff + glyphs.offsets[i].x + xcs->rbearing);
492 ymax = qMax(ymax, y + xcs->ascent + xcs->descent);
493 overall.xoff += glyphs.advances_x[i];
494 } else {
495 QFixed size = _fs->ascent;
496 overall.x = qMin(overall.x, overall.xoff);
497 overall.y = qMin(overall.y, overall.yoff - size);
498 ymax = qMax(ymax, overall.yoff);
499 overall.xoff += size;
500 xmax = qMax(xmax, overall.xoff);
501 }
502 }
503 overall.height = qMax(overall.height, ymax - overall.y);
504 overall.width = xmax - overall.x;
505
506 return overall;
507}
508
509glyph_metrics_t QFontEngineXLFD::boundingBox(glyph_t glyph)
510{
511 glyph_metrics_t gm;
512 XCharStruct *xcs = charStruct(_fs, glyph);
513 if (xcs) {
514 gm = glyph_metrics_t(xcs->lbearing, -xcs->ascent, xcs->rbearing- xcs->lbearing, xcs->ascent + xcs->descent,
515 xcs->width, 0);
516 } else {
517 QFixed size = ascent();
518 gm = glyph_metrics_t(0, size, size, size, size, 0);
519 }
520 return gm;
521}
522
523QFixed QFontEngineXLFD::ascent() const
524{
525 return _fs->ascent;
526}
527
528QFixed QFontEngineXLFD::descent() const
529{
530 return (_fs->descent-1);
531}
532
533QFixed QFontEngineXLFD::leading() const
534{
535 QFixed l = QFixed(qMin<int>(_fs->ascent, _fs->max_bounds.ascent)
536 + qMin<int>(_fs->descent, _fs->max_bounds.descent)) * QFixed::fromReal(0.15);
537 return l.ceil();
538}
539
540qreal QFontEngineXLFD::maxCharWidth() const
541{
542 return _fs->max_bounds.width;
543}
544
545
546// Loads the font for the specified script
547static inline int maxIndex(XFontStruct *f) {
548 return (((f->max_byte1 - f->min_byte1) *
549 (f->max_char_or_byte2 - f->min_char_or_byte2 + 1)) +
550 f->max_char_or_byte2 - f->min_char_or_byte2);
551}
552
553qreal QFontEngineXLFD::minLeftBearing() const
554{
555 if (lbearing == SHRT_MIN) {
556 if (_fs->per_char) {
557 XCharStruct *cs = _fs->per_char;
558 int nc = maxIndex(_fs) + 1;
559 int mx = cs->lbearing;
560
561 for (int c = 1; c < nc; c++) {
562 // ignore the bearings for characters whose ink is
563 // completely outside the normal bounding box
564 if ((cs[c].lbearing <= 0 && cs[c].rbearing <= 0) ||
565 (cs[c].lbearing >= cs[c].width && cs[c].rbearing >= cs[c].width))
566 continue;
567
568 int nmx = cs[c].lbearing;
569
570 if (nmx < mx)
571 mx = nmx;
572 }
573
574 ((QFontEngineXLFD *)this)->lbearing = mx;
575 } else
576 ((QFontEngineXLFD *)this)->lbearing = _fs->min_bounds.lbearing;
577 }
578 return lbearing;
579}
580
581qreal QFontEngineXLFD::minRightBearing() const
582{
583 if (rbearing == SHRT_MIN) {
584 if (_fs->per_char) {
585 XCharStruct *cs = _fs->per_char;
586 int nc = maxIndex(_fs) + 1;
587 int mx = cs->rbearing;
588
589 for (int c = 1; c < nc; c++) {
590 // ignore the bearings for characters whose ink is
591 // completely outside the normal bounding box
592 if ((cs[c].lbearing <= 0 && cs[c].rbearing <= 0) ||
593 (cs[c].lbearing >= cs[c].width && cs[c].rbearing >= cs[c].width))
594 continue;
595
596 int nmx = cs[c].rbearing;
597
598 if (nmx < mx)
599 mx = nmx;
600 }
601
602 ((QFontEngineXLFD *)this)->rbearing = mx;
603 } else
604 ((QFontEngineXLFD *)this)->rbearing = _fs->min_bounds.rbearing;
605 }
606 return rbearing;
607}
608
609const char *QFontEngineXLFD::name() const
610{
611 return _name;
612}
613
614bool QFontEngineXLFD::canRender(const QChar *string, int len)
615{
616 QVarLengthGlyphLayoutArray glyphs(len);
617 int nglyphs = len;
618 if (stringToCMap(string, len, &glyphs, &nglyphs, 0) == false) {
619 glyphs.resize(nglyphs);
620 stringToCMap(string, len, &glyphs, &nglyphs, 0);
621 }
622
623 bool allExist = true;
624 for (int i = 0; i < nglyphs; i++) {
625 if (!glyphs.glyphs[i] || !charStruct(_fs, glyphs.glyphs[i])) {
626 allExist = false;
627 break;
628 }
629 }
630
631 return allExist;
632}
633
634QBitmap QFontEngineXLFD::bitmapForGlyphs(const QGlyphLayout &glyphs, const glyph_metrics_t &metrics, QTextItem::RenderFlags flags)
635{
636 int w = metrics.width.toInt();
637 int h = metrics.height.toInt();
638 if (w <= 0 || h <= 0)
639 return QBitmap();
640
641 QPixmapData *data = new QX11PixmapData(QPixmapData::BitmapType);
642 data->resize(w, h);
643 QPixmap bm(data);
644 QPainter p(&bm);
645 p.fillRect(0, 0, w, h, Qt::color0);
646 p.setPen(Qt::color1);
647
648 QTextItemInt item;
649 item.flags = flags;
650 item.ascent = -metrics.y;
651 item.descent = metrics.height - item.ascent;
652 item.width = metrics.width;
653 item.chars = 0;
654 item.num_chars = 0;
655 item.logClusters = 0;
656 item.glyphs = glyphs;
657 item.fontEngine = this;
658 item.f = 0;
659
660 p.drawTextItem(QPointF(-metrics.x.toReal(), item.ascent.toReal()), item);
661 p.end();
662
663 return QBitmap(bm);
664}
665
666void QFontEngineXLFD::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
667{
668 // cannot use QFontEngine::addBitmapFontToPath(), since we don't
669 // have direct access to the glyph bitmaps, so we have to draw
670 // onto a QBitmap, then convert to QImage, then to path
671 glyph_metrics_t metrics = boundingBox(glyphs);
672
673 QImage image = bitmapForGlyphs(glyphs, metrics, flags).toImage();
674 if (image.isNull())
675 return;
676
677 image = image.convertToFormat(QImage::Format_Mono);
678 const uchar *image_data = image.bits();
679 uint bpl = image.bytesPerLine();
680 // from qfontengine.cpp
681 extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data,
682 int bpl, int w, int h, QPainterPath *path);
683 qt_addBitmapToPath(x, y + metrics.y.toReal(), image_data, bpl, image.width(), image.height(), path);
684}
685
686QFontEngine::FaceId QFontEngineXLFD::faceId() const
687{
688#ifndef QT_NO_FREETYPE
689 if (face_id.index == -1) {
690 face_id = fontFile(_name, &freetype, &synth);
691 if (_codec)
692 face_id.encoding = _codec->mibEnum();
693 if (freetype) {
694 const_cast<QFontEngineXLFD *>(this)->fsType = freetype->fsType();
695 } else {
696 QFontEngine::Properties properties = QFontEngine::properties();
697 face_id.index = 0;
698 face_id.filename = "-" + properties.postscriptName;
699 }
700 }
701#endif
702
703 return face_id;
704}
705
706QFontEngine::Properties QFontEngineXLFD::properties() const
707{
708 if (face_id.index == -1)
709 (void)faceId();
710
711#ifndef QT_NO_FREETYPE
712 if (freetype)
713 return freetype->properties();
714#endif
715 return QFontEngine::properties();
716}
717
718void QFontEngineXLFD::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
719{
720 if (face_id.index == -1)
721 (void)faceId();
722#ifndef QT_NO_FREETYPE
723 if (!freetype)
724#endif
725 {
726 QFontEngine::getUnscaledGlyph(glyph, path, metrics);
727 return;
728 }
729
730#ifndef QT_NO_FREETYPE
731 freetype->lock();
732
733 FT_Face face = freetype->face;
734 FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0);
735 freetype->xsize = face->units_per_EM << 6;
736 freetype->ysize = face->units_per_EM << 6;
737 FT_Set_Transform(face, 0, 0);
738 glyph = glyphIndexToFreetypeGlyphIndex(glyph);
739 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
740
741 int left = face->glyph->metrics.horiBearingX;
742 int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width;
743 int top = face->glyph->metrics.horiBearingY;
744 int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
745
746 QFixedPoint p;
747 p.x = 0;
748 p.y = 0;
749 metrics->width = QFixed::fromFixed(right-left);
750 metrics->height = QFixed::fromFixed(top-bottom);
751 metrics->x = QFixed::fromFixed(left);
752 metrics->y = QFixed::fromFixed(-top);
753 metrics->xoff = QFixed::fromFixed(face->glyph->advance.x);
754
755 if (!FT_IS_SCALABLE(freetype->face))
756 QFreetypeFace::addBitmapToPath(face->glyph, p, path);
757 else
758 QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6);
759
760 FT_Set_Transform(face, &freetype->matrix, 0);
761 freetype->unlock();
762#endif // QT_NO_FREETYPE
763}
764
765
766bool QFontEngineXLFD::getSfntTableData(uint tag, uchar *buffer, uint *length) const
767{
768#ifndef QT_NO_FREETYPE
769 if (face_id.index == -1)
770 (void)faceId();
771 if (!freetype)
772 return false;
773 return freetype->getSfntTable(tag, buffer, length);
774#else
775 Q_UNUSED(tag);
776 Q_UNUSED(buffer);
777 Q_UNUSED(length);
778 return false;
779#endif
780}
781
782int QFontEngineXLFD::synthesized() const
783{
784 return synth;
785}
786
787QImage QFontEngineXLFD::alphaMapForGlyph(glyph_t glyph)
788{
789 glyph_metrics_t metrics = boundingBox(glyph);
790
791/*
792 printf("a) w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f\n",
793 metrics.width.toReal(),
794 metrics.height.toReal(),
795 metrics.xoff.toReal(),
796 metrics.yoff.toReal(),
797 metrics.x.toReal(),
798 metrics.y.toReal());
799*/
800
801 QGlyphLayoutArray<1> glyphs;
802 glyphs.glyphs[0] = glyph;
803
804 QImage image = bitmapForGlyphs(glyphs, metrics).toImage();
805//image.save(QString::fromLatin1("x11cache-%1.png").arg((int)glyph));
806
807 image = image.convertToFormat(QImage::Format_Indexed8);
808 QVector<QRgb> colors(256);
809 for (int i = 0; i < 256; ++i)
810 colors[i] = qRgba(0, 0, 0, i);
811 image.setColorTable(colors);
812
813 int width = image.width();
814 int height = image.height();
815 for (int y = 0; y < height; ++y) {
816 uchar *bits = image.scanLine(y);
817 for (int x = 0; x < width; ++x)
818 bits[x] = ~(bits[x]-1);
819 }
820
821 return image;
822}
823
824#ifndef QT_NO_FREETYPE
825
826FT_Face QFontEngineXLFD::non_locked_face() const
827{
828 return freetype ? freetype->face : 0;
829}
830
831uint QFontEngineXLFD::toUnicode(glyph_t g) const
832{
833 if (_codec) {
834 QTextCodec::ConverterState state;
835 state.flags = QTextCodec::ConvertInvalidToNull;
836 uchar data[2];
837 int l = 1;
838 if (g > 255) {
839 data[0] = (g >> 8);
840 data[1] = (g & 255);
841 l = 2;
842 } else {
843 data[0] = g;
844 }
845 QString s = _codec->toUnicode((char *)data, l, &state);
846 Q_ASSERT(s.length() == 1);
847 g = s.at(0).unicode();
848 }
849 return g;
850}
851
852glyph_t QFontEngineXLFD::glyphIndexToFreetypeGlyphIndex(glyph_t g) const
853{
854 return FT_Get_Char_Index(freetype->face, toUnicode(g));
855}
856#endif
857
858#ifndef QT_NO_FONTCONFIG
859
860// ------------------------------------------------------------------
861// Multi FT engine
862// ------------------------------------------------------------------
863
864static QFontEngine *engineForPattern(FcPattern *pattern, const QFontDef &request,
865 int screen)
866{
867 FcResult res;
868 FcPattern *match = FcFontMatch(0, pattern, &res);
869 QFontEngineX11FT *engine = new QFontEngineX11FT(match, request, screen);
870 if (!engine->invalid())
871 return engine;
872
873 delete engine;
874 QFontEngine *fe = new QFontEngineBox(request.pixelSize);
875 fe->fontDef = request;
876 return fe;
877}
878
879QFontEngineMultiFT::QFontEngineMultiFT(QFontEngine *fe, FcPattern *matchedPattern, FcPattern *p, int s, const QFontDef &req)
880 : QFontEngineMulti(2), request(req), pattern(p), firstEnginePattern(matchedPattern), fontSet(0), screen(s)
881{
882
883 engines[0] = fe;
884 engines.at(0)->ref.ref();
885 fontDef = engines[0]->fontDef;
886 cache_cost = 100;
887 firstFontIndex = 1;
888}
889
890QFontEngineMultiFT::~QFontEngineMultiFT()
891{
892 extern QMutex *qt_fontdatabase_mutex();
893 QMutexLocker locker(qt_fontdatabase_mutex());
894
895 FcPatternDestroy(pattern);
896 if (firstEnginePattern)
897 FcPatternDestroy(firstEnginePattern);
898 if (fontSet)
899 FcFontSetDestroy(fontSet);
900}
901
902
903void QFontEngineMultiFT::loadEngine(int at)
904{
905 extern QMutex *qt_fontdatabase_mutex();
906 QMutexLocker locker(qt_fontdatabase_mutex());
907
908 extern void qt_addPatternProps(FcPattern *pattern, int screen, int script,
909 const QFontDef &request);
910 extern QFontDef qt_FcPatternToQFontDef(FcPattern *pattern, const QFontDef &);
911 extern FcFontSet *qt_fontSetForPattern(FcPattern *pattern, const QFontDef &request);
912
913 Q_ASSERT(at > 0);
914 if (!fontSet) {
915 fontSet = qt_fontSetForPattern(pattern, request);
916
917 // it may happen that the fontset of fallbacks consists of only one font. In this case we
918 // have to fall back to the box fontengine as we cannot render the glyph.
919 if (fontSet->nfont == 1 && at == 1 && engines.size() == 2) {
920 Q_ASSERT(engines.at(at) == 0);
921 QFontEngine *fe = new QFontEngineBox(request.pixelSize);
922 fe->fontDef = request;
923 engines[at] = fe;
924 return;
925 }
926
927 if (firstEnginePattern) {
928
929 if (!FcPatternEqual(firstEnginePattern, fontSet->fonts[0]))
930 firstFontIndex = 0;
931
932 FcPatternDestroy(firstEnginePattern);
933 firstEnginePattern = 0;
934 }
935
936 engines.resize(fontSet->nfont + 1 - firstFontIndex);
937 }
938 Q_ASSERT(at < engines.size());
939 Q_ASSERT(engines.at(at) == 0);
940
941 FcPattern *pattern = FcPatternDuplicate(fontSet->fonts[at + firstFontIndex - 1]);
942 qt_addPatternProps(pattern, screen, QUnicodeTables::Common, request);
943
944 QFontDef fontDef = qt_FcPatternToQFontDef(pattern, this->request);
945
946 // note: we use -1 for the script to make sure that we keep real
947 // FT engines separate from Multi engines in the font cache
948 QFontCache::Key key(fontDef, -1, screen);
949 QFontEngine *fontEngine = QFontCache::instance()->findEngine(key);
950 if (!fontEngine) {
951 FcConfigSubstitute(0, pattern, FcMatchPattern);
952 FcDefaultSubstitute(pattern);
953 fontEngine = engineForPattern(pattern, request, screen);
954 QFontCache::instance()->insertEngine(key, fontEngine);
955 }
956 FcPatternDestroy(pattern);
957 fontEngine->ref.ref();
958 engines[at] = fontEngine;
959}
960
961// ------------------------------------------------------------------
962// X11 FT engine
963// ------------------------------------------------------------------
964
965
966
967Q_GUI_EXPORT void qt_x11ft_convert_pattern(FcPattern *pattern, QByteArray *file_name, int *index, bool *antialias)
968{
969 FcChar8 *fileName;
970 FcPatternGetString(pattern, FC_FILE, 0, &fileName);
971 *file_name = (const char *)fileName;
972 if (!FcPatternGetInteger(pattern, FC_INDEX, 0, index))
973 index = 0;
974 FcBool b;
975 if (FcPatternGetBool(pattern, FC_ANTIALIAS, 0, &b) == FcResultMatch)
976 *antialias = b;
977}
978
979
980QFontEngineX11FT::QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int screen)
981 : QFontEngineFT(fd)
982{
983// FcPatternPrint(pattern);
984
985 bool antialias = X11->fc_antialias;
986 QByteArray file_name;
987 int face_index;
988 qt_x11ft_convert_pattern(pattern, &file_name, &face_index, &antialias);
989 QFontEngine::FaceId face_id;
990 face_id.filename = file_name;
991 face_id.index = face_index;
992
993 canUploadGlyphsToServer = qApp->thread() == QThread::currentThread();
994
995 subpixelType = Subpixel_None;
996 if (antialias) {
997 int subpixel = X11->display ? X11->screens[screen].subpixel : FC_RGBA_UNKNOWN;
998 if (subpixel == FC_RGBA_UNKNOWN)
999 (void) FcPatternGetInteger(pattern, FC_RGBA, 0, &subpixel);
1000 if (!antialias || subpixel == FC_RGBA_UNKNOWN)
1001 subpixel = FC_RGBA_NONE;
1002
1003 switch (subpixel) {
1004 case FC_RGBA_NONE: subpixelType = Subpixel_None; break;
1005 case FC_RGBA_RGB: subpixelType = Subpixel_RGB; break;
1006 case FC_RGBA_BGR: subpixelType = Subpixel_BGR; break;
1007 case FC_RGBA_VRGB: subpixelType = Subpixel_VRGB; break;
1008 case FC_RGBA_VBGR: subpixelType = Subpixel_VBGR; break;
1009 default: break;
1010 }
1011 }
1012
1013#ifdef FC_HINT_STYLE
1014 {
1015 int hint_style = 0;
1016 if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch)
1017 hint_style = X11->fc_hint_style;
1018
1019 switch (hint_style) {
1020 case FC_HINT_NONE:
1021 default_hint_style = HintNone;
1022 break;
1023 case FC_HINT_SLIGHT:
1024 default_hint_style = HintLight;
1025 break;
1026 case FC_HINT_MEDIUM:
1027 default_hint_style = HintMedium;
1028 break;
1029 default:
1030 default_hint_style = HintFull;
1031 break;
1032 }
1033 }
1034#endif
1035
1036#if defined(FC_AUTOHINT) && defined(FT_LOAD_FORCE_AUTOHINT)
1037 {
1038 bool autohint = false;
1039
1040 FcBool b;
1041 if (FcPatternGetBool(pattern, FC_AUTOHINT, 0, &b) == FcResultMatch)
1042 autohint = b;
1043
1044 if (autohint)
1045 default_load_flags |= FT_LOAD_FORCE_AUTOHINT;
1046 }
1047#endif
1048
1049#if defined(FC_LCD_FILTER) && defined(FT_LCD_FILTER_H)
1050 {
1051 int filter = FC_LCD_FILTER_NONE;
1052 if (FcPatternGetInteger(pattern, FC_LCD_FILTER, 0, &filter) == FcResultMatch) {
1053 switch (filter) {
1054 case FC_LCD_FILTER_NONE:
1055 lcdFilterType = FT_LCD_FILTER_NONE;
1056 break;
1057 case FC_LCD_FILTER_DEFAULT:
1058 lcdFilterType = FT_LCD_FILTER_DEFAULT;
1059 break;
1060 case FC_LCD_FILTER_LIGHT:
1061 lcdFilterType = FT_LCD_FILTER_LIGHT;
1062 break;
1063 case FC_LCD_FILTER_LEGACY:
1064 lcdFilterType = FT_LCD_FILTER_LEGACY;
1065 break;
1066 default:
1067 // new unknown lcd filter type?!
1068 break;
1069 }
1070 }
1071 }
1072#endif
1073
1074#ifdef FC_EMBEDDED_BITMAP
1075 {
1076 FcBool b;
1077 if (FcPatternGetBool(pattern, FC_EMBEDDED_BITMAP, 0, &b) == FcResultMatch)
1078 embeddedbitmap = b;
1079 }
1080#endif
1081
1082 GlyphFormat defaultFormat = Format_None;
1083
1084#ifndef QT_NO_XRENDER
1085 if (X11->use_xrender) {
1086 int format = PictStandardA8;
1087 if (!antialias)
1088 format = PictStandardA1;
1089 else if (subpixelType == Subpixel_RGB
1090 || subpixelType == Subpixel_BGR
1091 || subpixelType == Subpixel_VRGB
1092 || subpixelType == Subpixel_VBGR)
1093 format = PictStandardARGB32;
1094 xglyph_format = format;
1095
1096 if (subpixelType != QFontEngineFT::Subpixel_None)
1097 defaultFormat = Format_A32;
1098 else if (antialias)
1099 defaultFormat = Format_A8;
1100 else
1101 defaultFormat = Format_Mono;
1102 }
1103#endif
1104
1105 if (!init(face_id, antialias, defaultFormat)) {
1106 FcPatternDestroy(pattern);
1107 return;
1108 }
1109
1110 if (!freetype->charset) {
1111 FcCharSet *cs;
1112 FcPatternGetCharSet (pattern, FC_CHARSET, 0, &cs);
1113 freetype->charset = FcCharSetCopy(cs);
1114 }
1115 FcPatternDestroy(pattern);
1116}
1117
1118QFontEngineX11FT::~QFontEngineX11FT()
1119{
1120 freeGlyphSets();
1121}
1122
1123unsigned long QFontEngineX11FT::allocateServerGlyphSet()
1124{
1125#ifndef QT_NO_XRENDER
1126 if (!canUploadGlyphsToServer || !X11->use_xrender)
1127 return 0;
1128 return XRenderCreateGlyphSet(X11->display, XRenderFindStandardFormat(X11->display, xglyph_format));
1129#else
1130 return 0;
1131#endif
1132}
1133
1134void QFontEngineX11FT::freeServerGlyphSet(unsigned long id)
1135{
1136#ifndef QT_NO_XRENDER
1137 if (!id)
1138 return;
1139 XRenderFreeGlyphSet(X11->display, id);
1140#endif
1141}
1142
1143bool QFontEngineX11FT::uploadGlyphToServer(QGlyphSet *set, uint glyphid, Glyph *g, GlyphInfo *info, int glyphDataSize) const
1144{
1145#ifndef QT_NO_XRENDER
1146 if (!canUploadGlyphsToServer)
1147 return false;
1148 if (g->format == Format_Mono) {
1149 /*
1150 * swap bit order around; FreeType is always MSBFirst
1151 */
1152 if (BitmapBitOrder(X11->display) != MSBFirst) {
1153 unsigned char *line = g->data;
1154 int i = glyphDataSize;
1155 while (i--) {
1156 unsigned char c;
1157 c = *line;
1158 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1159 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1160 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1161 *line++ = c;
1162 }
1163 }
1164 }
1165
1166 ::Glyph xglyph = glyphid;
1167 XRenderAddGlyphs (X11->display, set->id, &xglyph, info, 1, (const char *)g->data, glyphDataSize);
1168 delete [] g->data;
1169 g->data = 0;
1170 g->format = Format_None;
1171 g->uploadedToServer = true;
1172 return true;
1173#else
1174 return false;
1175#endif
1176}
1177
1178#endif // QT_NO_FONTCONFIG
1179
1180QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.