1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
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
101 QT_BEGIN_NAMESPACE
102
103
104 // ------------------------------------------------------------------
105 // Multi XLFD engine
106 // ------------------------------------------------------------------
107
QFontEngineMultiXLFD(const QFontDef & r,const QList<int> & l,int s)108 QFontEngineMultiXLFD::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
~QFontEngineMultiXLFD()115 QFontEngineMultiXLFD::~QFontEngineMultiXLFD()
116 { }
117
loadEngine(int at)118 void 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
135 static QStringList *qt_fontpath = 0;
136
fontPath()137 static 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
fontFile(const QByteArray & _xname,QFreetypeFace ** freetype,int * synth)207 static 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).startsWith(QLatin1Char('/')))
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 }
286 end:
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
300 extern int qt_mib_for_xlfd_encoding(const char *encoding);
301 extern int qt_xlfd_encoding_id(const char *encoding);
302
charStruct(XFontStruct * xfs,uint ch)303 static 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
QFontEngineXLFD(XFontStruct * fs,const QByteArray & name,int mib)323 QFontEngineXLFD::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
~QFontEngineXLFD()340 QFontEngineXLFD::~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
stringToCMap(const QChar * s,int len,QGlyphLayout * glyphs,int * nglyphs,QTextEngine::ShaperFlags flags) const350 bool 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 (s[i].isHighSurrogate() && i < len-1 && s[i+1].isLowSurrogate()) {
362 *str = QChar();
363 ++i;
364 } else {
365 *str = s[i];
366 }
367 ++str;
368 }
369
370 len = str - (QChar *)_s.data();
371 str = (QChar *)_s.data();
372
373 bool mirrored = flags & QTextEngine::RightToLeft;
374 if (_codec) {
375 bool haveNbsp = false;
376 for (int i = 0; i < len; i++)
377 if (str[i].unicode() == 0xa0) {
378 haveNbsp = true;
379 break;
380 }
381
382 QVarLengthArray<unsigned short> ch(len);
383 QChar *chars = (QChar *)ch.data();
384 if (haveNbsp || mirrored) {
385 for (int i = 0; i < len; i++)
386 chars[i] = (str[i].unicode() == 0xa0 ? 0x20 :
387 (mirrored ? QChar::mirroredChar(str[i].unicode()) : str[i].unicode()));
388 } else {
389 for (int i = 0; i < len; i++)
390 chars[i] = str[i].unicode();
391 }
392 QTextCodec::ConverterState state;
393 state.flags = QTextCodec::ConvertInvalidToNull;
394 QByteArray ba = _codec->fromUnicode(chars, len, &state);
395 if (ba.length() == 2*len) {
396 // double byte encoding
397 const uchar *data = (const uchar *)ba.constData();
398 for (int i = 0; i < len; i++) {
399 glyphs->glyphs[i] = ((ushort)data[0] << 8) + data[1];
400 data += 2;
401 }
402 } else {
403 const uchar *data = (const uchar *)ba.constData();
404 for (int i = 0; i < len; i++)
405 glyphs->glyphs[i] = (ushort)data[i];
406 }
407 } else {
408 int i = len;
409 const QChar *c = str + len;
410 if (mirrored) {
411 while (c != str)
412 glyphs->glyphs[--i] = (--c)->unicode() == 0xa0 ? 0x20 : QChar::mirroredChar(c->unicode());
413 } else {
414 while (c != str)
415 glyphs->glyphs[--i] = (--c)->unicode() == 0xa0 ? 0x20 : c->unicode();
416 }
417 }
418 *nglyphs = len;
419 glyphs->numGlyphs = len;
420
421 if (!(flags & QTextEngine::GlyphIndicesOnly))
422 recalcAdvances(glyphs, flags);
423 return true;
424 }
425
recalcAdvances(QGlyphLayout * glyphs,QTextEngine::ShaperFlags) const426 void QFontEngineXLFD::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags /*flags*/) const
427 {
428 int i = glyphs->numGlyphs;
429 XCharStruct *xcs;
430 // inlined for better performance
431 if (!_fs->per_char) {
432 xcs = &_fs->min_bounds;
433 while (i != 0) {
434 --i;
435 const unsigned char r = glyphs->glyphs[i] >> 8;
436 const unsigned char c = glyphs->glyphs[i] & 0xff;
437 if (r >= _fs->min_byte1 &&
438 r <= _fs->max_byte1 &&
439 c >= _fs->min_char_or_byte2 &&
440 c <= _fs->max_char_or_byte2) {
441 glyphs->advances_x[i] = xcs->width;
442 } else {
443 glyphs->glyphs[i] = 0;
444 }
445 }
446 }
447 else if (!_fs->max_byte1) {
448 XCharStruct *base = _fs->per_char - _fs->min_char_or_byte2;
449 while (i != 0) {
450 unsigned int gl = glyphs->glyphs[--i];
451 xcs = (gl >= _fs->min_char_or_byte2 && gl <= _fs->max_char_or_byte2) ?
452 base + gl : 0;
453 if (!xcs || (!xcs->width && !xcs->ascent && !xcs->descent)) {
454 glyphs->glyphs[i] = 0;
455 } else {
456 glyphs->advances_x[i] = xcs->width;
457 }
458 }
459 }
460 else {
461 while (i != 0) {
462 xcs = charStruct(_fs, glyphs->glyphs[--i]);
463 if (!xcs) {
464 glyphs->glyphs[i] = 0;
465 } else {
466 glyphs->advances_x[i] = xcs->width;
467 }
468 }
469 }
470 }
471
boundingBox(const QGlyphLayout & glyphs)472 glyph_metrics_t QFontEngineXLFD::boundingBox(const QGlyphLayout &glyphs)
473 {
474 int i;
475
476 glyph_metrics_t overall;
477 // initialize with line height, we get the same behaviour on all platforms
478 overall.y = -ascent();
479 overall.height = ascent() + descent() + 1;
480 QFixed ymax;
481 QFixed xmax;
482 for (i = 0; i < glyphs.numGlyphs; i++) {
483 XCharStruct *xcs = charStruct(_fs, glyphs.glyphs[i]);
484 if (xcs) {
485 QFixed x = overall.xoff + glyphs.offsets[i].x + xcs->lbearing;
486 QFixed y = overall.yoff + glyphs.offsets[i].y - xcs->ascent;
487 overall.x = qMin(overall.x, x);
488 overall.y = qMin(overall.y, y);
489 // XCharStruct::rbearing is defined as distance from left edge to rightmost pixel
490 xmax = qMax(xmax, overall.xoff + glyphs.offsets[i].x + xcs->rbearing);
491 ymax = qMax(ymax, y + xcs->ascent + xcs->descent);
492 overall.xoff += glyphs.advances_x[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6);
493 } else {
494 QFixed size = _fs->ascent;
495 overall.x = qMin(overall.x, overall.xoff);
496 overall.y = qMin(overall.y, overall.yoff - size);
497 ymax = qMax(ymax, overall.yoff);
498 overall.xoff += size;
499 xmax = qMax(xmax, overall.xoff);
500 }
501 }
502 overall.height = qMax(overall.height, ymax - overall.y);
503 overall.width = xmax - overall.x;
504
505 return overall;
506 }
507
boundingBox(glyph_t glyph)508 glyph_metrics_t QFontEngineXLFD::boundingBox(glyph_t glyph)
509 {
510 glyph_metrics_t gm;
511 XCharStruct *xcs = charStruct(_fs, glyph);
512 if (xcs) {
513 // XCharStruct::rbearing is defined as distance from left edge to rightmost pixel
514 // XCharStruct::width is defined as the advance
515 gm = glyph_metrics_t(xcs->lbearing, -xcs->ascent, xcs->rbearing- xcs->lbearing, xcs->ascent + xcs->descent,
516 xcs->width, 0);
517 } else {
518 QFixed size = ascent();
519 gm = glyph_metrics_t(0, size, size, size, size, 0);
520 }
521 return gm;
522 }
523
ascent() const524 QFixed QFontEngineXLFD::ascent() const
525 {
526 return _fs->ascent;
527 }
528
descent() const529 QFixed QFontEngineXLFD::descent() const
530 {
531 return (_fs->descent-1);
532 }
533
leading() const534 QFixed QFontEngineXLFD::leading() const
535 {
536 QFixed l = QFixed(qMin<int>(_fs->ascent, _fs->max_bounds.ascent)
537 + qMin<int>(_fs->descent, _fs->max_bounds.descent)) * QFixed::fromReal(0.15);
538 return l.ceil();
539 }
540
maxCharWidth() const541 qreal QFontEngineXLFD::maxCharWidth() const
542 {
543 return _fs->max_bounds.width;
544 }
545
546
547 // Loads the font for the specified script
maxIndex(XFontStruct * f)548 static inline int maxIndex(XFontStruct *f) {
549 return (((f->max_byte1 - f->min_byte1) *
550 (f->max_char_or_byte2 - f->min_char_or_byte2 + 1)) +
551 f->max_char_or_byte2 - f->min_char_or_byte2);
552 }
553
minLeftBearing() const554 qreal QFontEngineXLFD::minLeftBearing() const
555 {
556 if (lbearing == SHRT_MIN) {
557 if (_fs->per_char) {
558 XCharStruct *cs = _fs->per_char;
559 int nc = maxIndex(_fs) + 1;
560 int mx = cs->lbearing;
561
562 for (int c = 1; c < nc; c++) {
563 // ignore the bearings for characters whose ink is
564 // completely outside the normal bounding box
565 if ((cs[c].lbearing <= 0 && cs[c].rbearing <= 0) ||
566 (cs[c].lbearing >= cs[c].width && cs[c].rbearing >= cs[c].width))
567 continue;
568
569 int nmx = cs[c].lbearing;
570
571 if (nmx < mx)
572 mx = nmx;
573 }
574
575 ((QFontEngineXLFD *)this)->lbearing = mx;
576 } else
577 ((QFontEngineXLFD *)this)->lbearing = _fs->min_bounds.lbearing;
578 }
579 return lbearing;
580 }
581
minRightBearing() const582 qreal QFontEngineXLFD::minRightBearing() const
583 {
584 if (rbearing == SHRT_MIN) {
585 if (_fs->per_char) {
586 XCharStruct *cs = _fs->per_char;
587 int nc = maxIndex(_fs) + 1;
588 int mx = cs->rbearing;
589
590 for (int c = 1; c < nc; c++) {
591 // ignore the bearings for characters whose ink is
592 // completely outside the normal bounding box
593 if ((cs[c].lbearing <= 0 && cs[c].rbearing <= 0) ||
594 (cs[c].lbearing >= cs[c].width && cs[c].rbearing >= cs[c].width))
595 continue;
596
597 int nmx = cs[c].rbearing;
598
599 if (nmx < mx)
600 mx = nmx;
601 }
602
603 ((QFontEngineXLFD *)this)->rbearing = mx;
604 } else
605 ((QFontEngineXLFD *)this)->rbearing = _fs->min_bounds.rbearing;
606 }
607 return rbearing;
608 }
609
name() const610 const char *QFontEngineXLFD::name() const
611 {
612 return _name;
613 }
614
canRender(const QChar * string,int len)615 bool QFontEngineXLFD::canRender(const QChar *string, int len)
616 {
617 QVarLengthGlyphLayoutArray glyphs(len);
618 int nglyphs = len;
619 if (stringToCMap(string, len, &glyphs, &nglyphs, 0) == false) {
620 glyphs.resize(nglyphs);
621 stringToCMap(string, len, &glyphs, &nglyphs, 0);
622 }
623
624 bool allExist = true;
625 for (int i = 0; i < nglyphs; i++) {
626 if (!glyphs.glyphs[i] || !charStruct(_fs, glyphs.glyphs[i])) {
627 allExist = false;
628 break;
629 }
630 }
631
632 return allExist;
633 }
634
bitmapForGlyphs(const QGlyphLayout & glyphs,const glyph_metrics_t & metrics,QTextItem::RenderFlags flags)635 QBitmap QFontEngineXLFD::bitmapForGlyphs(const QGlyphLayout &glyphs, const glyph_metrics_t &metrics, QTextItem::RenderFlags flags)
636 {
637 int w = metrics.width.toInt();
638 int h = metrics.height.toInt();
639 if (w <= 0 || h <= 0)
640 return QBitmap();
641
642 QPixmapData *data = new QX11PixmapData(QPixmapData::BitmapType);
643 data->resize(w, h);
644 QPixmap bm(data);
645 QPainter p(&bm);
646 p.fillRect(0, 0, w, h, Qt::color0);
647 p.setPen(Qt::color1);
648
649 QTextItemInt item;
650 item.flags = flags;
651 item.ascent = -metrics.y;
652 item.descent = metrics.height - item.ascent;
653 item.width = metrics.width;
654 item.chars = 0;
655 item.num_chars = 0;
656 item.logClusters = 0;
657 item.glyphs = glyphs;
658 item.fontEngine = this;
659 item.f = 0;
660
661 p.drawTextItem(QPointF(-metrics.x.toReal(), item.ascent.toReal()), item);
662 p.end();
663
664 return QBitmap(bm);
665 }
666
addOutlineToPath(qreal x,qreal y,const QGlyphLayout & glyphs,QPainterPath * path,QTextItem::RenderFlags flags)667 void QFontEngineXLFD::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
668 {
669 // cannot use QFontEngine::addBitmapFontToPath(), since we don't
670 // have direct access to the glyph bitmaps, so we have to draw
671 // onto a QBitmap, then convert to QImage, then to path
672 glyph_metrics_t metrics = boundingBox(glyphs);
673
674 QImage image = bitmapForGlyphs(glyphs, metrics, flags).toImage();
675 if (image.isNull())
676 return;
677
678 image = image.convertToFormat(QImage::Format_Mono);
679 const uchar *image_data = image.bits();
680 uint bpl = image.bytesPerLine();
681 // from qfontengine.cpp
682 extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data,
683 int bpl, int w, int h, QPainterPath *path);
684 qt_addBitmapToPath(x, y + metrics.y.toReal(), image_data, bpl, image.width(), image.height(), path);
685 }
686
faceId() const687 QFontEngine::FaceId QFontEngineXLFD::faceId() const
688 {
689 #ifndef QT_NO_FREETYPE
690 if (face_id.index == -1) {
691 face_id = fontFile(_name, &freetype, &synth);
692 if (_codec)
693 face_id.encoding = _codec->mibEnum();
694 if (freetype) {
695 const_cast<QFontEngineXLFD *>(this)->fsType = freetype->fsType();
696 } else {
697 face_id.index = 0;
698 face_id.filename = '-' + QFontEngine::properties().postscriptName;
699 }
700 }
701 #endif
702
703 return face_id;
704 }
705
properties() const706 QFontEngine::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
getUnscaledGlyph(glyph_t glyph,QPainterPath * path,glyph_metrics_t * metrics)718 void 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
getSfntTableData(uint tag,uchar * buffer,uint * length) const766 bool 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
synthesized() const782 int QFontEngineXLFD::synthesized() const
783 {
784 return synth;
785 }
786
alphaMapForGlyph(glyph_t glyph)787 QImage 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
non_locked_face() const826 FT_Face QFontEngineXLFD::non_locked_face() const
827 {
828 return freetype ? freetype->face : 0;
829 }
830
toUnicode(glyph_t g) const831 uint 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
glyphIndexToFreetypeGlyphIndex(glyph_t g) const852 glyph_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
engineForPattern(FcPattern * match,const QFontDef & request,int screen)864 static QFontEngine *engineForPattern(FcPattern *match, const QFontDef &request, int screen)
865 {
866 QFontEngineX11FT *engine = new QFontEngineX11FT(match, request, screen);
867 if (!engine->invalid())
868 return engine;
869
870 delete engine;
871 QFontEngine *fe = new QFontEngineBox(request.pixelSize);
872 fe->fontDef = request;
873 return fe;
874 }
875
QFontEngineMultiFT(QFontEngine * fe,FcPattern * matchedPattern,FcPattern * p,int s,const QFontDef & req)876 QFontEngineMultiFT::QFontEngineMultiFT(QFontEngine *fe, FcPattern *matchedPattern, FcPattern *p, int s, const QFontDef &req)
877 : QFontEngineMulti(2), request(req), pattern(p), fontSet(0), screen(s)
878 {
879 firstEnginePattern = FcPatternDuplicate(matchedPattern);
880 engines[0] = fe;
881 engines.at(0)->ref.ref();
882 fontDef = engines[0]->fontDef;
883 cache_cost = 100;
884 firstFontIndex = 1;
885 }
886
~QFontEngineMultiFT()887 QFontEngineMultiFT::~QFontEngineMultiFT()
888 {
889 extern QMutex *qt_fontdatabase_mutex();
890 QMutexLocker locker(qt_fontdatabase_mutex());
891
892 FcPatternDestroy(pattern);
893 if (firstEnginePattern)
894 FcPatternDestroy(firstEnginePattern);
895 if (fontSet)
896 FcFontSetDestroy(fontSet);
897 }
898
899
loadEngine(int at)900 void QFontEngineMultiFT::loadEngine(int at)
901 {
902 extern QMutex *qt_fontdatabase_mutex();
903 QMutexLocker locker(qt_fontdatabase_mutex());
904
905 extern QFontDef qt_FcPatternToQFontDef(FcPattern *pattern, const QFontDef &);
906 extern FcFontSet *qt_fontSetForPattern(FcPattern *pattern, const QFontDef &request);
907
908 Q_ASSERT(at > 0);
909 if (!fontSet) {
910 fontSet = qt_fontSetForPattern(pattern, request);
911
912 // it may happen that the fontset of fallbacks consists of only one font. In this case we
913 // have to fall back to the box fontengine as we cannot render the glyph.
914 if (fontSet->nfont == 1 && at == 1 && engines.size() == 2) {
915 Q_ASSERT(engines.at(at) == 0);
916 QFontEngine *fe = new QFontEngineBox(request.pixelSize);
917 fe->fontDef = request;
918 engines[at] = fe;
919 return;
920 }
921
922 if (firstEnginePattern) {
923
924 if (!FcPatternEqual(firstEnginePattern, fontSet->fonts[0]))
925 firstFontIndex = 0;
926
927 FcPatternDestroy(firstEnginePattern);
928 firstEnginePattern = 0;
929 }
930
931 engines.resize(fontSet->nfont + 1 - firstFontIndex);
932 }
933 Q_ASSERT(at < engines.size());
934 Q_ASSERT(engines.at(at) == 0);
935
936 FcPattern *match = FcFontRenderPrepare(NULL, pattern, fontSet->fonts[at + firstFontIndex - 1]);
937 QFontDef fontDef = qt_FcPatternToQFontDef(match, this->request);
938
939 // note: we use -1 for the script to make sure that we keep real
940 // FT engines separate from Multi engines in the font cache
941 QFontCache::Key key(fontDef, -1, screen);
942 QFontEngine *fontEngine = QFontCache::instance()->findEngine(key);
943 if (!fontEngine) {
944 fontEngine = engineForPattern(match, request, screen);
945 QFontCache::instance()->insertEngine(key, fontEngine);
946 }
947 FcPatternDestroy(match);
948 fontEngine->ref.ref();
949 engines[at] = fontEngine;
950 }
951
952 // ------------------------------------------------------------------
953 // X11 FT engine
954 // ------------------------------------------------------------------
955
956
957
qt_x11ft_convert_pattern(FcPattern * pattern,QByteArray * file_name,int * index,bool * antialias)958 Q_GUI_EXPORT void qt_x11ft_convert_pattern(FcPattern *pattern, QByteArray *file_name, int *index, bool *antialias)
959 {
960 FcChar8 *fileName;
961 FcPatternGetString(pattern, FC_FILE, 0, &fileName);
962 *file_name = (const char *)fileName;
963 if (!FcPatternGetInteger(pattern, FC_INDEX, 0, index))
964 index = 0;
965 FcBool b;
966 if (FcPatternGetBool(pattern, FC_ANTIALIAS, 0, &b) == FcResultMatch)
967 *antialias = b;
968 }
969
970
QFontEngineX11FT(FcPattern * pattern,const QFontDef & fd,int screen)971 QFontEngineX11FT::QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int screen)
972 : QFontEngineFT(fd)
973 {
974 // FcPatternPrint(pattern);
975
976 bool antialias = X11->fc_antialias;
977 QByteArray file_name;
978 int face_index;
979 qt_x11ft_convert_pattern(pattern, &file_name, &face_index, &antialias);
980 QFontEngine::FaceId face_id;
981 face_id.filename = file_name;
982 face_id.index = face_index;
983
984 canUploadGlyphsToServer = QApplication::testAttribute(Qt::AA_X11InitThreads) || (qApp->thread() == QThread::currentThread());
985
986 subpixelType = Subpixel_None;
987 if (antialias) {
988 int subpixel = X11->display ? X11->screens[screen].subpixel : FC_RGBA_UNKNOWN;
989 if (subpixel == FC_RGBA_UNKNOWN)
990 (void) FcPatternGetInteger(pattern, FC_RGBA, 0, &subpixel);
991 if (!antialias || subpixel == FC_RGBA_UNKNOWN)
992 subpixel = FC_RGBA_NONE;
993
994 switch (subpixel) {
995 case FC_RGBA_NONE: subpixelType = Subpixel_None; break;
996 case FC_RGBA_RGB: subpixelType = Subpixel_RGB; break;
997 case FC_RGBA_BGR: subpixelType = Subpixel_BGR; break;
998 case FC_RGBA_VRGB: subpixelType = Subpixel_VRGB; break;
999 case FC_RGBA_VBGR: subpixelType = Subpixel_VBGR; break;
1000 default: break;
1001 }
1002 }
1003
1004 if (fd.hintingPreference != QFont::PreferDefaultHinting) {
1005 switch (fd.hintingPreference) {
1006 case QFont::PreferNoHinting:
1007 default_hint_style = HintNone;
1008 break;
1009 case QFont::PreferVerticalHinting:
1010 default_hint_style = HintLight;
1011 break;
1012 case QFont::PreferFullHinting:
1013 default:
1014 default_hint_style = HintFull;
1015 break;
1016 }
1017 }
1018 #ifdef FC_HINT_STYLE
1019 else {
1020 int hint_style = 0;
1021 // Try to use Xft.hintstyle from XDefaults first if running in GNOME, to match
1022 // the behavior of cairo
1023 if (X11->fc_hint_style > -1 && X11->desktopEnvironment == DE_GNOME)
1024 hint_style = X11->fc_hint_style;
1025 else if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch
1026 && X11->fc_hint_style > -1)
1027 hint_style = X11->fc_hint_style;
1028
1029 switch (hint_style) {
1030 case FC_HINT_NONE:
1031 default_hint_style = HintNone;
1032 break;
1033 case FC_HINT_SLIGHT:
1034 default_hint_style = HintLight;
1035 break;
1036 case FC_HINT_MEDIUM:
1037 default_hint_style = HintMedium;
1038 break;
1039 default:
1040 default_hint_style = HintFull;
1041 break;
1042 }
1043 }
1044 #endif
1045
1046 #if defined(FC_AUTOHINT) && defined(FT_LOAD_FORCE_AUTOHINT)
1047 {
1048 bool autohint = false;
1049
1050 FcBool b;
1051 if (FcPatternGetBool(pattern, FC_AUTOHINT, 0, &b) == FcResultMatch)
1052 autohint = b;
1053
1054 if (autohint)
1055 default_load_flags |= FT_LOAD_FORCE_AUTOHINT;
1056 }
1057 #endif
1058
1059 #if defined(FC_LCD_FILTER) && defined(FT_LCD_FILTER_H)
1060 {
1061 int filter = FC_LCD_FILTER_NONE;
1062 if (FcPatternGetInteger(pattern, FC_LCD_FILTER, 0, &filter) == FcResultMatch) {
1063 switch (filter) {
1064 case FC_LCD_FILTER_NONE:
1065 lcdFilterType = FT_LCD_FILTER_NONE;
1066 break;
1067 case FC_LCD_FILTER_DEFAULT:
1068 lcdFilterType = FT_LCD_FILTER_DEFAULT;
1069 break;
1070 case FC_LCD_FILTER_LIGHT:
1071 lcdFilterType = FT_LCD_FILTER_LIGHT;
1072 break;
1073 case FC_LCD_FILTER_LEGACY:
1074 lcdFilterType = FT_LCD_FILTER_LEGACY;
1075 break;
1076 default:
1077 // new unknown lcd filter type?!
1078 break;
1079 }
1080 }
1081 }
1082 #endif
1083
1084 #ifdef FC_EMBEDDED_BITMAP
1085 {
1086 FcBool b;
1087 if (FcPatternGetBool(pattern, FC_EMBEDDED_BITMAP, 0, &b) == FcResultMatch)
1088 embeddedbitmap = b;
1089 }
1090 #endif
1091
1092 GlyphFormat defaultFormat = Format_None;
1093
1094 #ifndef QT_NO_XRENDER
1095 if (X11->use_xrender) {
1096 int format = PictStandardA8;
1097 if (!antialias)
1098 format = PictStandardA1;
1099 else if (subpixelType == Subpixel_RGB
1100 || subpixelType == Subpixel_BGR
1101 || subpixelType == Subpixel_VRGB
1102 || subpixelType == Subpixel_VBGR)
1103 format = PictStandardARGB32;
1104 xglyph_format = format;
1105
1106 if (subpixelType != QFontEngineFT::Subpixel_None)
1107 defaultFormat = Format_A32;
1108 else if (antialias)
1109 defaultFormat = Format_A8;
1110 else
1111 defaultFormat = Format_Mono;
1112 }
1113 #endif
1114
1115 if (!init(face_id, antialias, defaultFormat))
1116 return;
1117
1118 if (!freetype->charset) {
1119 FcCharSet *cs;
1120 FcPatternGetCharSet (pattern, FC_CHARSET, 0, &cs);
1121 freetype->charset = FcCharSetCopy(cs);
1122 }
1123 }
1124
~QFontEngineX11FT()1125 QFontEngineX11FT::~QFontEngineX11FT()
1126 {
1127 freeGlyphSets();
1128 }
1129
allocateServerGlyphSet()1130 unsigned long QFontEngineX11FT::allocateServerGlyphSet()
1131 {
1132 #ifndef QT_NO_XRENDER
1133 if (!canUploadGlyphsToServer || !X11->use_xrender)
1134 return 0;
1135 return XRenderCreateGlyphSet(X11->display, XRenderFindStandardFormat(X11->display, xglyph_format));
1136 #else
1137 return 0;
1138 #endif
1139 }
1140
freeServerGlyphSet(unsigned long id)1141 void QFontEngineX11FT::freeServerGlyphSet(unsigned long id)
1142 {
1143 #ifndef QT_NO_XRENDER
1144 if (!id)
1145 return;
1146 XRenderFreeGlyphSet(X11->display, id);
1147 #endif
1148 }
1149
uploadGlyphToServer(QGlyphSet * set,uint glyphid,Glyph * g,GlyphInfo * info,int glyphDataSize) const1150 bool QFontEngineX11FT::uploadGlyphToServer(QGlyphSet *set, uint glyphid, Glyph *g, GlyphInfo *info, int glyphDataSize) const
1151 {
1152 #ifndef QT_NO_XRENDER
1153 if (!canUploadGlyphsToServer)
1154 return false;
1155 if (g->format == Format_Mono) {
1156 /*
1157 * swap bit order around; FreeType is always MSBFirst
1158 */
1159 if (BitmapBitOrder(X11->display) != MSBFirst) {
1160 unsigned char *line = g->data;
1161 int i = glyphDataSize;
1162 while (i--) {
1163 unsigned char c;
1164 c = *line;
1165 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1166 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1167 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1168 *line++ = c;
1169 }
1170 }
1171 }
1172
1173 ::Glyph xglyph = glyphid;
1174 XRenderAddGlyphs (X11->display, set->id, &xglyph, info, 1, (const char *)g->data, glyphDataSize);
1175 delete [] g->data;
1176 g->data = 0;
1177 g->format = Format_None;
1178 g->uploadedToServer = true;
1179 return true;
1180 #else
1181 return false;
1182 #endif
1183 }
1184
cloneWithSize(qreal pixelSize) const1185 QFontEngine *QFontEngineX11FT::cloneWithSize(qreal pixelSize) const
1186 {
1187 QFontDef fontDef;
1188 fontDef.pixelSize = pixelSize;
1189 QFontEngineX11FT *fe = new QFontEngineX11FT(fontDef);
1190 if (!fe->initFromFontEngine(this)) {
1191 delete fe;
1192 return 0;
1193 } else {
1194 #ifndef QT_NO_XRENDER
1195 fe->xglyph_format = xglyph_format;
1196 #endif
1197 return fe;
1198 }
1199 }
1200
1201 #endif // QT_NO_FONTCONFIG
1202
1203 QT_END_NAMESPACE
1204