source: trunk/src/gui/text/qfontengine_win.cpp@ 439

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

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

File size: 48.2 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_p.h"
43#include "qtextengine_p.h"
44#include <qglobal.h>
45#include "qt_windows.h"
46#include <private/qapplication_p.h>
47
48#include <qlibrary.h>
49#include <qpaintdevice.h>
50#include <qpainter.h>
51#include <qlibrary.h>
52#include <limits.h>
53
54#include <qendian.h>
55#include <qmath.h>
56#include <qthreadstorage.h>
57
58#include <private/qunicodetables_p.h>
59#include <qbitmap.h>
60
61#include <private/qpainter_p.h>
62#include <private/qpdf_p.h>
63#include "qpaintengine.h"
64#include "qvarlengtharray.h"
65#include <private/qpaintengine_raster_p.h>
66#include <private/qnativeimage_p.h>
67
68#if defined(Q_OS_WINCE)
69#include "qguifunctions_wince.h"
70#endif
71
72//### mingw needed define
73#ifndef TT_PRIM_CSPLINE
74#define TT_PRIM_CSPLINE 3
75#endif
76
77#ifdef MAKE_TAG
78#undef MAKE_TAG
79#endif
80// GetFontData expects the tags in little endian ;(
81#define MAKE_TAG(ch1, ch2, ch3, ch4) (\
82 (((quint32)(ch4)) << 24) | \
83 (((quint32)(ch3)) << 16) | \
84 (((quint32)(ch2)) << 8) | \
85 ((quint32)(ch1)) \
86 )
87
88typedef BOOL (WINAPI *PtrGetCharWidthI)(HDC, UINT, UINT, LPWORD, LPINT);
89
90// common DC for all fonts
91
92QT_BEGIN_NAMESPACE
93
94class QtHDC
95{
96 HDC _hdc;
97public:
98 QtHDC()
99 {
100 HDC displayDC = GetDC(0);
101 _hdc = CreateCompatibleDC(displayDC);
102 ReleaseDC(0, displayDC);
103 }
104 ~QtHDC()
105 {
106 if (_hdc)
107 DeleteDC(_hdc);
108 }
109 HDC hdc() const
110 {
111 return _hdc;
112 }
113};
114
115#ifndef QT_NO_THREAD
116Q_GLOBAL_STATIC(QThreadStorage<QtHDC *>, local_shared_dc)
117HDC shared_dc()
118{
119 QtHDC *&hdc = local_shared_dc()->localData();
120 if (!hdc)
121 hdc = new QtHDC;
122 return hdc->hdc();
123}
124#else
125HDC shared_dc()
126{
127 return 0;
128}
129#endif
130
131static HFONT stock_sysfont = 0;
132
133static PtrGetCharWidthI ptrGetCharWidthI = 0;
134static bool resolvedGetCharWidthI = false;
135
136static void resolveGetCharWidthI()
137{
138 if (resolvedGetCharWidthI)
139 return;
140 resolvedGetCharWidthI = true;
141 ptrGetCharWidthI = (PtrGetCharWidthI)QLibrary::resolve(QLatin1String("gdi32"), "GetCharWidthI");
142}
143
144// Copy a LOGFONTW struct into a LOGFONTA by converting the face name to an 8 bit value.
145// This is needed when calling CreateFontIndirect on non-unicode windowses.
146inline static void wa_copy_logfont(LOGFONTW *lfw, LOGFONTA *lfa)
147{
148 lfa->lfHeight = lfw->lfHeight;
149 lfa->lfWidth = lfw->lfWidth;
150 lfa->lfEscapement = lfw->lfEscapement;
151 lfa->lfOrientation = lfw->lfOrientation;
152 lfa->lfWeight = lfw->lfWeight;
153 lfa->lfItalic = lfw->lfItalic;
154 lfa->lfUnderline = lfw->lfUnderline;
155 lfa->lfCharSet = lfw->lfCharSet;
156 lfa->lfOutPrecision = lfw->lfOutPrecision;
157 lfa->lfClipPrecision = lfw->lfClipPrecision;
158 lfa->lfQuality = lfw->lfQuality;
159 lfa->lfPitchAndFamily = lfw->lfPitchAndFamily;
160
161 QString fam = QString::fromUtf16((const ushort*)lfw->lfFaceName);
162 memcpy(lfa->lfFaceName, fam.toLocal8Bit().constData(), fam.length() + 1);
163}
164
165// defined in qtextengine_win.cpp
166typedef void *SCRIPT_CACHE;
167typedef HRESULT (WINAPI *fScriptFreeCache)(SCRIPT_CACHE *);
168extern fScriptFreeCache ScriptFreeCache;
169
170static inline quint32 getUInt(unsigned char *p)
171{
172 quint32 val;
173 val = *p++ << 24;
174 val |= *p++ << 16;
175 val |= *p++ << 8;
176 val |= *p;
177
178 return val;
179}
180
181static inline quint16 getUShort(unsigned char *p)
182{
183 quint16 val;
184 val = *p++ << 8;
185 val |= *p;
186
187 return val;
188}
189
190static inline HFONT systemFont()
191{
192 if (stock_sysfont == 0)
193 stock_sysfont = (HFONT)GetStockObject(SYSTEM_FONT);
194 return stock_sysfont;
195}
196
197
198// general font engine
199
200QFixed QFontEngineWin::lineThickness() const
201{
202 if(lineWidth > 0)
203 return lineWidth;
204
205 return QFontEngine::lineThickness();
206}
207
208#if defined(Q_OS_WINCE)
209static OUTLINETEXTMETRICW *getOutlineTextMetric(HDC hdc)
210{
211 int size;
212 size = GetOutlineTextMetricsW(hdc, 0, 0);
213 OUTLINETEXTMETRICW *otm = (OUTLINETEXTMETRICW *)malloc(size);
214 GetOutlineTextMetricsW(hdc, size, otm);
215 return otm;
216}
217#else
218static OUTLINETEXTMETRICA *getOutlineTextMetric(HDC hdc)
219{
220 int size;
221 size = GetOutlineTextMetricsA(hdc, 0, 0);
222 OUTLINETEXTMETRICA *otm = (OUTLINETEXTMETRICA *)malloc(size);
223 GetOutlineTextMetricsA(hdc, size, otm);
224 return otm;
225}
226#endif
227
228void QFontEngineWin::getCMap()
229{
230 QT_WA({
231 ttf = (bool)(tm.w.tmPitchAndFamily & TMPF_TRUETYPE);
232 } , {
233 ttf = (bool)(tm.a.tmPitchAndFamily & TMPF_TRUETYPE);
234 });
235 HDC hdc = shared_dc();
236 SelectObject(hdc, hfont);
237 bool symb = false;
238 if (ttf) {
239 cmapTable = getSfntTable(qbswap<quint32>(MAKE_TAG('c', 'm', 'a', 'p')));
240 int size = 0;
241 cmap = QFontEngine::getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()),
242 cmapTable.size(), &symb, &size);
243 }
244 if (!cmap) {
245 ttf = false;
246 symb = false;
247 }
248 symbol = symb;
249 designToDevice = 1;
250 _faceId.index = 0;
251 if(cmap) {
252#if defined(Q_OS_WINCE)
253 OUTLINETEXTMETRICW *otm = getOutlineTextMetric(hdc);
254#else
255 OUTLINETEXTMETRICA *otm = getOutlineTextMetric(hdc);
256#endif
257 designToDevice = QFixed((int)otm->otmEMSquare)/int(otm->otmTextMetrics.tmHeight);
258 unitsPerEm = otm->otmEMSquare;
259 x_height = (int)otm->otmsXHeight;
260 loadKerningPairs(designToDevice);
261 _faceId.filename = (char *)otm + (int)otm->otmpFullName;
262 lineWidth = otm->otmsUnderscoreSize;
263 fsType = otm->otmfsType;
264 free(otm);
265 } else {
266 unitsPerEm = tm.w.tmHeight;
267 }
268}
269
270
271inline unsigned int getChar(const QChar *str, int &i, const int len)
272{
273 unsigned int uc = str[i].unicode();
274 if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
275 uint low = str[i+1].unicode();
276 if (low >= 0xdc00 && low < 0xe000) {
277 uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
278 ++i;
279 }
280 }
281 return uc;
282}
283
284int QFontEngineWin::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs, bool mirrored) const
285{
286 int i = 0;
287 int glyph_pos = 0;
288 if (mirrored) {
289#if defined(Q_OS_WINCE)
290 {
291#else
292 if (symbol) {
293 for (; i < numChars; ++i, ++glyph_pos) {
294 unsigned int uc = getChar(str, i, numChars);
295 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc);
296 if (!glyphs->glyphs[glyph_pos] && uc < 0x100)
297 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
298 }
299 } else if (ttf) {
300 for (; i < numChars; ++i, ++glyph_pos) {
301 unsigned int uc = getChar(str, i, numChars);
302 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, QChar::mirroredChar(uc));
303 }
304 } else {
305#endif
306 ushort first, last;
307 QT_WA({
308 first = tm.w.tmFirstChar;
309 last = tm.w.tmLastChar;
310 }, {
311 first = tm.a.tmFirstChar;
312 last = tm.a.tmLastChar;
313 });
314 for (; i < numChars; ++i, ++glyph_pos) {
315 uint ucs = QChar::mirroredChar(getChar(str, i, numChars));
316 if (
317#ifdef Q_OS_WINCE
318 tm.w.tmFirstChar > 60000 || // see line 375
319#endif
320 ucs >= first && ucs <= last)
321 glyphs->glyphs[glyph_pos] = ucs;
322 else
323 glyphs->glyphs[glyph_pos] = 0;
324 }
325 }
326 } else {
327#if defined(Q_OS_WINCE)
328 {
329#else
330 if (symbol) {
331 for (; i < numChars; ++i, ++glyph_pos) {
332 unsigned int uc = getChar(str, i, numChars);
333 glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc);
334 if(!glyphs->glyphs[glyph_pos] && uc < 0x100)
335 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
336 }
337 } else if (ttf) {
338 for (; i < numChars; ++i, ++glyph_pos) {
339 unsigned int uc = getChar(str, i, numChars);
340 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc);
341 }
342 } else {
343#endif
344 ushort first, last;
345 QT_WA({
346 first = tm.w.tmFirstChar;
347 last = tm.w.tmLastChar;
348 }, {
349 first = tm.a.tmFirstChar;
350 last = tm.a.tmLastChar;
351 });
352 for (; i < numChars; ++i, ++glyph_pos) {
353 uint uc = getChar(str, i, numChars);
354 if (
355#ifdef Q_OS_WINCE
356 tm.w.tmFirstChar > 60000 || // see comment in QFontEngineWin
357#endif
358 uc >= first && uc <= last)
359 glyphs->glyphs[glyph_pos] = uc;
360 else
361 glyphs->glyphs[glyph_pos] = 0;
362 }
363 }
364 }
365 glyphs->numGlyphs = glyph_pos;
366 return glyph_pos;
367}
368
369
370QFontEngineWin::QFontEngineWin(const QString &name, HFONT _hfont, bool stockFont, LOGFONT lf)
371{
372 //qDebug("regular windows font engine created: font='%s', size=%d", name, lf.lfHeight);
373
374 _name = name;
375
376 cmap = 0;
377 hfont = _hfont;
378 logfont = lf;
379 HDC hdc = shared_dc();
380 SelectObject(hdc, hfont);
381 this->stockFont = stockFont;
382 fontDef.pixelSize = -lf.lfHeight;
383
384 lbearing = SHRT_MIN;
385 rbearing = SHRT_MIN;
386 synthesized_flags = -1;
387 lineWidth = -1;
388 x_height = -1;
389
390 BOOL res;
391 QT_WA({
392 res = GetTextMetricsW(hdc, &tm.w);
393 } , {
394 res = GetTextMetricsA(hdc, &tm.a);
395 });
396 fontDef.fixedPitch = !(tm.w.tmPitchAndFamily & TMPF_FIXED_PITCH);
397 if (!res)
398 qErrnoWarning("QFontEngineWin: GetTextMetrics failed");
399
400 cache_cost = tm.w.tmHeight * tm.w.tmAveCharWidth * 2000;
401 getCMap();
402
403 useTextOutA = false;
404#ifndef Q_OS_WINCE
405 // TextOutW doesn't work for symbol fonts on Windows 95!
406 // since we're using glyph indices we don't care for ttfs about this!
407 if (QSysInfo::WindowsVersion == QSysInfo::WV_95 && !ttf &&
408 (_name == QLatin1String("Marlett") || _name == QLatin1String("Symbol") ||
409 _name == QLatin1String("Webdings") || _name == QLatin1String("Wingdings")))
410 useTextOutA = true;
411#endif
412 widthCache = 0;
413 widthCacheSize = 0;
414 designAdvances = 0;
415 designAdvancesSize = 0;
416
417 if (!resolvedGetCharWidthI)
418 resolveGetCharWidthI();
419}
420
421QFontEngineWin::~QFontEngineWin()
422{
423 if (designAdvances)
424 free(designAdvances);
425
426 if (widthCache)
427 free(widthCache);
428
429 // make sure we aren't by accident still selected
430 SelectObject(shared_dc(), systemFont());
431
432 if (!stockFont) {
433 if (!DeleteObject(hfont))
434 qErrnoWarning("QFontEngineWin: failed to delete non-stock font...");
435 }
436}
437
438HGDIOBJ QFontEngineWin::selectDesignFont(QFixed *overhang) const
439{
440 LOGFONT f = logfont;
441 f.lfHeight = unitsPerEm;
442 HFONT designFont;
443 QT_WA({
444 designFont = CreateFontIndirectW(&f);
445 }, {
446 LOGFONTA fa;
447 wa_copy_logfont(&f, &fa);
448 designFont = CreateFontIndirectA(&fa);
449 });
450 HGDIOBJ oldFont = SelectObject(shared_dc(), designFont);
451
452 if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) {
453 BOOL res;
454 QT_WA({
455 TEXTMETRICW tm;
456 res = GetTextMetricsW(shared_dc(), &tm);
457 if (!res)
458 qErrnoWarning("QFontEngineWin: GetTextMetrics failed");
459 *overhang = QFixed((int)tm.tmOverhang) / designToDevice;
460 } , {
461 TEXTMETRICA tm;
462 res = GetTextMetricsA(shared_dc(), &tm);
463 if (!res)
464 qErrnoWarning("QFontEngineWin: GetTextMetrics failed");
465 *overhang = QFixed((int)tm.tmOverhang) / designToDevice;
466 });
467 } else {
468 *overhang = 0;
469 }
470 return oldFont;
471}
472
473bool QFontEngineWin::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
474{
475 if (*nglyphs < len) {
476 *nglyphs = len;
477 return false;
478 }
479
480 *nglyphs = getGlyphIndexes(str, len, glyphs, flags & QTextEngine::RightToLeft);
481
482 if (flags & QTextEngine::GlyphIndicesOnly)
483 return true;
484
485#if defined(Q_OS_WINCE)
486 HDC hdc = shared_dc();
487 if (flags & QTextEngine::DesignMetrics) {
488 HGDIOBJ oldFont = 0;
489 QFixed overhang = 0;
490
491 int glyph_pos = 0;
492 for(register int i = 0; i < len; i++) {
493 bool surrogate = (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00 && i < len-1
494 && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000);
495 unsigned int glyph = glyphs->glyphs[glyph_pos];
496 if(int(glyph) >= designAdvancesSize) {
497 int newSize = (glyph + 256) >> 8 << 8;
498 designAdvances = (QFixed *)realloc(designAdvances, newSize*sizeof(QFixed));
499 for(int i = designAdvancesSize; i < newSize; ++i)
500 designAdvances[i] = -1000000;
501 designAdvancesSize = newSize;
502 }
503 if(designAdvances[glyph] < -999999) {
504 if(!oldFont)
505 oldFont = selectDesignFont(&overhang);
506 SIZE size = {0, 0};
507 GetTextExtentPoint32W(hdc, (wchar_t *)(str+i), surrogate ? 2 : 1, &size);
508 designAdvances[glyph] = QFixed((int)size.cx)/designToDevice;
509 }
510 glyphs->advances_x[glyph_pos] = designAdvances[glyph];
511 glyphs->advances_y[glyph_pos] = 0;
512 if (surrogate)
513 ++i;
514 ++glyph_pos;
515 }
516 if(oldFont)
517 DeleteObject(SelectObject(hdc, oldFont));
518 } else {
519 int glyph_pos = 0;
520 HGDIOBJ oldFont = 0;
521
522 for(register int i = 0; i < len; i++) {
523 bool surrogate = (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00 && i < len-1
524 && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000);
525 unsigned int glyph = glyphs->glyphs[glyph_pos];
526
527 glyphs->advances_y[glyph_pos] = 0;
528
529 if (glyph >= widthCacheSize) {
530 int newSize = (glyph + 256) >> 8 << 8;
531 widthCache = (unsigned char *)realloc(widthCache, newSize*sizeof(QFixed));
532 memset(widthCache + widthCacheSize, 0, newSize - widthCacheSize);
533 widthCacheSize = newSize;
534 }
535 glyphs->advances_x[glyph_pos] = widthCache[glyph];
536 // font-width cache failed
537 if (glyphs->advances_x[glyph_pos] == 0) {
538 SIZE size = {0, 0};
539 if (!oldFont)
540 oldFont = SelectObject(hdc, hfont);
541 GetTextExtentPoint32W(hdc, (wchar_t *)str + i, surrogate ? 2 : 1, &size);
542 glyphs->advances_x[glyph_pos] = size.cx;
543 // if glyph's within cache range, store it for later
544 if (size.cx > 0 && size.cx < 0x100)
545 widthCache[glyph] = size.cx;
546 }
547
548 if (surrogate)
549 ++i;
550 ++glyph_pos;
551 }
552
553 if (oldFont)
554 SelectObject(hdc, oldFont);
555 }
556#else
557 recalcAdvances(glyphs, flags);
558#endif
559 return true;
560}
561
562void QFontEngineWin::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
563{
564 HGDIOBJ oldFont = 0;
565 HDC hdc = shared_dc();
566 if (ttf && (flags & QTextEngine::DesignMetrics)) {
567 QFixed overhang = 0;
568
569 for(int i = 0; i < glyphs->numGlyphs; i++) {
570 unsigned int glyph = glyphs->glyphs[i];
571 if(int(glyph) >= designAdvancesSize) {
572 int newSize = (glyph + 256) >> 8 << 8;
573 designAdvances = (QFixed *)realloc(designAdvances, newSize*sizeof(QFixed));
574 for(int i = designAdvancesSize; i < newSize; ++i)
575 designAdvances[i] = -1000000;
576 designAdvancesSize = newSize;
577 }
578 if(designAdvances[glyph] < -999999) {
579 if(!oldFont)
580 oldFont = selectDesignFont(&overhang);
581
582 if (ptrGetCharWidthI) {
583 int width = 0;
584 ptrGetCharWidthI(hdc, glyph, 1, 0, &width);
585
586 designAdvances[glyph] = QFixed(width) / designToDevice;
587 } else {
588#ifndef Q_OS_WINCE
589 GLYPHMETRICS gm;
590 DWORD res = GDI_ERROR;
591 MAT2 mat;
592 mat.eM11.value = mat.eM22.value = 1;
593 mat.eM11.fract = mat.eM22.fract = 0;
594 mat.eM21.value = mat.eM12.value = 0;
595 mat.eM21.fract = mat.eM12.fract = 0;
596 QT_WA({
597 res = GetGlyphOutlineW(hdc, glyph, GGO_METRICS|GGO_GLYPH_INDEX|GGO_NATIVE, &gm, 0, 0, &mat);
598 } , {
599 res = GetGlyphOutlineA(hdc, glyph, GGO_METRICS|GGO_GLYPH_INDEX|GGO_NATIVE, &gm, 0, 0, &mat);
600 });
601
602 if (res != GDI_ERROR) {
603 designAdvances[glyph] = QFixed(gm.gmCellIncX) / designToDevice;
604 }
605#endif
606 }
607 }
608 glyphs->advances_x[i] = designAdvances[glyph];
609 glyphs->advances_y[i] = 0;
610 }
611 if(oldFont)
612 DeleteObject(SelectObject(hdc, oldFont));
613 } else {
614 int overhang = (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) ? tm.a.tmOverhang : 0;
615
616 for(int i = 0; i < glyphs->numGlyphs; i++) {
617 unsigned int glyph = glyphs->glyphs[i];
618