source: trunk/src/gui/text/qfontengine_ft.cpp@ 571

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

trunk: Merged in qt 4.6.1 sources.

File size: 63.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qdir.h"
43#include "qmetatype.h"
44#include "qtextstream.h"
45#include "qvariant.h"
46#include "qfontengine_ft_p.h"
47
48#ifndef QT_NO_FREETYPE
49
50#include "qfile.h"
51#include "qabstractfileengine.h"
52#include "qthreadstorage.h"
53#include <qmath.h>
54#include <private/qpdf_p.h>
55#include <private/qharfbuzz_p.h>
56
57#include "qfontengine_ft_p.h"
58#include <ft2build.h>
59#include FT_FREETYPE_H
60#include FT_OUTLINE_H
61#include FT_TRUETYPE_TABLES_H
62#include FT_TYPE1_TABLES_H
63#include FT_GLYPH_H
64
65#if defined(FT_LCD_FILTER_H)
66#include FT_LCD_FILTER_H
67#endif
68
69#if defined(FT_CONFIG_OPTIONS_H)
70#include FT_CONFIG_OPTIONS_H
71#endif
72
73#if defined(FT_LCD_FILTER_H) && defined(FT_CONFIG_OPTION_SUBPIXEL_RENDERING)
74#define QT_USE_FREETYPE_LCDFILTER
75#endif
76
77#ifdef QT_LINUXBASE
78#include FT_ERRORS_H
79#endif
80
81QT_BEGIN_NAMESPACE
82
83/*
84 * Freetype 2.1.7 and earlier used width/height
85 * for matching sizes in the BDF and PCF loaders.
86 * This has been fixed for 2.1.8.
87 */
88#if (FREETYPE_MAJOR*10000+FREETYPE_MINOR*100+FREETYPE_PATCH) >= 20105
89#define X_SIZE(face,i) ((face)->available_sizes[i].x_ppem)
90#define Y_SIZE(face,i) ((face)->available_sizes[i].y_ppem)
91#else
92#define X_SIZE(face,i) ((face)->available_sizes[i].width << 6)
93#define Y_SIZE(face,i) ((face)->available_sizes[i].height << 6)
94#endif
95
96#define FLOOR(x) ((x) & -64)
97#define CEIL(x) (((x)+63) & -64)
98#define TRUNC(x) ((x) >> 6)
99#define ROUND(x) (((x)+32) & -64)
100
101static HB_Error hb_getSFntTable(void *font, HB_Tag tableTag, HB_Byte *buffer, HB_UInt *length)
102{
103#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) > 20103
104 FT_Face face = (FT_Face)font;
105 FT_ULong ftlen = *length;
106 FT_Error error = 0;
107
108 if ( !FT_IS_SFNT(face) )
109 return HB_Err_Invalid_Argument;
110
111 error = FT_Load_Sfnt_Table(face, tableTag, 0, buffer, &ftlen);
112 *length = ftlen;
113 return (HB_Error)error;
114#else
115 return HB_Err_Invalid_Argument;
116#endif
117}
118
119// -------------------------- Freetype support ------------------------------
120
121class QtFreetypeData
122{
123public:
124 QtFreetypeData()
125 : library(0)
126 { }
127
128 FT_Library library;
129 QHash<QFontEngine::FaceId, QFreetypeFace *> faces;
130};
131
132#ifdef QT_NO_THREAD
133Q_GLOBAL_STATIC(QtFreetypeData, theFreetypeData)
134
135QtFreetypeData *qt_getFreetypeData()
136{
137 return theFreetypeData();
138}
139#else
140Q_GLOBAL_STATIC(QThreadStorage<QtFreetypeData *>, theFreetypeData)
141
142QtFreetypeData *qt_getFreetypeData()
143{
144 QtFreetypeData *&freetypeData = theFreetypeData()->localData();
145 if (!freetypeData)
146 freetypeData = new QtFreetypeData;
147 return freetypeData;
148}
149#endif
150
151FT_Library qt_getFreetype()
152{
153 QtFreetypeData *freetypeData = qt_getFreetypeData();
154 if (!freetypeData->library)
155 FT_Init_FreeType(&freetypeData->library);
156 return freetypeData->library;
157}
158
159int QFreetypeFace::fsType() const
160{
161 int fsType = 0;
162 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
163 if (os2)
164 fsType = os2->fsType;
165 return fsType;
166}
167
168HB_Error QFreetypeFace::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
169{
170 int load_flags = (flags & HB_ShaperFlag_UseDesignMetrics) ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT;
171
172 if (HB_Error error = (HB_Error)FT_Load_Glyph(face, glyph, load_flags))
173 return error;
174
175 if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
176 return HB_Err_Invalid_SubTable;
177
178 *nPoints = face->glyph->outline.n_points;
179 if (!(*nPoints))
180 return HB_Err_Ok;
181
182 if (point > *nPoints)
183 return HB_Err_Invalid_SubTable;
184
185 *xpos = face->glyph->outline.points[point].x;
186 *ypos = face->glyph->outline.points[point].y;
187
188 return HB_Err_Ok;
189}
190
191/*
192 * One font file can contain more than one font (bold/italic for example)
193 * find the right one and return it.
194 *
195 * Returns the freetype face or 0 in case of an empty file or any other problems
196 * (like not being able to open the file)
197 */
198QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id)
199{
200 if (face_id.filename.isEmpty())
201 return 0;
202
203 QtFreetypeData *freetypeData = qt_getFreetypeData();
204 if (!freetypeData->library)
205 FT_Init_FreeType(&freetypeData->library);
206
207 QFreetypeFace *freetype = freetypeData->faces.value(face_id, 0);
208 if (freetype) {
209 freetype->ref.ref();
210 } else {
211 QScopedPointer<QFreetypeFace> newFreetype(new QFreetypeFace);
212 FT_Face face;
213 QFile file(QString::fromUtf8(face_id.filename));
214 if (face_id.filename.startsWith(":qmemoryfonts/")) {
215 // from qfontdatabase.cpp
216 extern QByteArray qt_fontdata_from_index(int);
217 QByteArray idx = face_id.filename;
218 idx.remove(0, 14); // remove ':qmemoryfonts/'
219 bool ok = false;
220 newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok));
221 if (!ok)
222 newFreetype->fontData = QByteArray();
223 } else if (!(file.fileEngine()->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)) {
224 if (!file.open(QIODevice::ReadOnly)) {
225 return 0;
226 }
227 newFreetype->fontData = file.readAll();
228 }
229 if (!newFreetype->fontData.isEmpty()) {
230 if (FT_New_Memory_Face(freetypeData->library, (const FT_Byte *)newFreetype->fontData.constData(), newFreetype->fontData.size(), face_id.index, &face)) {
231 return 0;
232 }
233 } else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) {
234 return 0;
235 }
236 newFreetype->face = face;
237
238 newFreetype->hbFace = qHBNewFace(face, hb_getSFntTable);
239 Q_CHECK_PTR(newFreetype->hbFace);
240 newFreetype->ref = 1;
241 newFreetype->xsize = 0;
242 newFreetype->ysize = 0;
243 newFreetype->matrix.xx = 0x10000;
244 newFreetype->matrix.yy = 0x10000;
245 newFreetype->matrix.xy = 0;
246 newFreetype->matrix.yx = 0;
247 newFreetype->unicode_map = 0;
248 newFreetype->symbol_map = 0;
249#ifndef QT_NO_FONTCONFIG
250 newFreetype->charset = 0;
251#endif
252
253 memset(newFreetype->cmapCache, 0, sizeof(newFreetype->cmapCache));
254
255 for (int i = 0; i < newFreetype->face->num_charmaps; ++i) {
256 FT_CharMap cm = newFreetype->face->charmaps[i];
257 switch(cm->encoding) {
258 case FT_ENCODING_UNICODE:
259 newFreetype->unicode_map = cm;
260 break;
261 case FT_ENCODING_APPLE_ROMAN:
262 case FT_ENCODING_ADOBE_LATIN_1:
263 if (!newFreetype->unicode_map || newFreetype->unicode_map->encoding != FT_ENCODING_UNICODE)
264 newFreetype->unicode_map = cm;
265 break;
266 case FT_ENCODING_ADOBE_CUSTOM:
267 case FT_ENCODING_MS_SYMBOL:
268 if (!newFreetype->symbol_map)
269 newFreetype->symbol_map = cm;
270 break;
271 default:
272 break;
273 }
274 }
275
276 if (!FT_IS_SCALABLE(newFreetype->face) && newFreetype->face->num_fixed_sizes == 1)
277 FT_Set_Char_Size (face, X_SIZE(newFreetype->face, 0), Y_SIZE(newFreetype->face, 0), 0, 0);
278# if 0
279 FcChar8 *name;
280 FcPatternGetString(pattern, FC_FAMILY, 0, &name);
281 qDebug("%s: using maps: default: %x unicode: %x, symbol: %x", name,
282 newFreetype->face->charmap ? newFreetype->face->charmap->encoding : 0,
283 newFreetype->unicode_map ? newFreetype->unicode_map->encoding : 0,
284 newFreetype->symbol_map ? newFreetype->symbol_map->encoding : 0);
285
286 for (int i = 0; i < 256; i += 8)
287 qDebug(" %x: %d %d %d %d %d %d %d %d", i,
288 FcCharSetHasChar(newFreetype->charset, i), FcCharSetHasChar(newFreetype->charset, i),
289 FcCharSetHasChar(newFreetype->charset, i), FcCharSetHasChar(newFreetype->charset, i),
290 FcCharSetHasChar(newFreetype->charset, i), FcCharSetHasChar(newFreetype->charset, i),
291 FcCharSetHasChar(newFreetype->charset, i), FcCharSetHasChar(newFreetype->charset, i));
292#endif
293
294 FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map);
295 QT_TRY {
296 freetypeData->faces.insert(face_id, newFreetype.data());
297 } QT_CATCH(...) {
298 newFreetype.take()->release(face_id);
299 // we could return null in principle instead of throwing
300 QT_RETHROW;
301 }
302 freetype = newFreetype.take();
303 }
304 return freetype;
305}
306
307void QFreetypeFace::release(const QFontEngine::FaceId &face_id)
308{
309 QtFreetypeData *freetypeData = qt_getFreetypeData();
310 if (!ref.deref()) {
311 qHBFreeFace(hbFace);
312 FT_Done_Face(face);
313#ifndef QT_NO_FONTCONFIG
314 if (charset)
315 FcCharSetDestroy(charset);
316#endif
317 if(freetypeData->faces.contains(face_id))
318 freetypeData->faces.take(face_id);
319 delete this;
320 }
321 if (freetypeData->faces.isEmpty()) {
322 FT_Done_FreeType(freetypeData->library);
323 freetypeData->library = 0;
324 }
325}
326
327
328void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing)
329{
330 *ysize = qRound(fontDef.pixelSize * 64);
331 *xsize = *ysize * fontDef.stretch / 100;
332 *outline_drawing = false;
333
334 /*
335 * Bitmap only faces must match exactly, so find the closest
336 * one (height dominant search)
337 */
338 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) {
339 int best = 0;
340 for (int i = 1; i < face->num_fixed_sizes; i++) {
341 if (qAbs(*ysize - Y_SIZE(face,i)) <
342 qAbs (*ysize - Y_SIZE(face, best)) ||
343 (qAbs (*ysize - Y_SIZE(face, i)) ==
344 qAbs (*ysize - Y_SIZE(face, best)) &&
345 qAbs (*xsize - X_SIZE(face, i)) <
346 qAbs (*xsize - X_SIZE(face, best)))) {
347 best = i;
348 }
349 }
350 if (FT_Set_Char_Size (face, X_SIZE(face, best), Y_SIZE(face, best), 0, 0) == 0) {
351 *xsize = X_SIZE(face, best);
352 *ysize = Y_SIZE(face, best);
353 } else {
354 int err = 1;
355 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE) && ysize == 0 && face->num_fixed_sizes >= 1) {
356 // work around FT 2.1.10 problem with BDF without PIXEL_SIZE property
357 err = FT_Set_Pixel_Sizes(face, face->available_sizes[0].width, face->available_sizes[0].height);
358 if (err && face->num_fixed_sizes == 1)
359 err = 0; //even more of a workaround...
360 }
361
362 if (err)
363 *xsize = *ysize = 0;
364 }
365 } else {
366 *outline_drawing = (*xsize > (64<<6) || *ysize > (64<<6));
367 }
368}
369
370QFontEngine::Properties QFreetypeFace::properties() const
371{
372 QFontEngine::Properties p;
373 p.postscriptName = FT_Get_Postscript_Name(face);
374 PS_FontInfoRec font_info;
375 if (FT_Get_PS_Font_Info(face, &font_info) == 0)
376 p.copyright = font_info.notice;
377 if (FT_IS_SCALABLE(face)) {