source: trunk/src/kernel/qfontengine_pm.cpp

Last change on this file was 170, checked in by dmik, 18 years ago

Kernel: Fixed DBCS input/output support (thanks again to Ko Myung-Hun).

  • Property svn:keywords set to Id
File size: 27.7 KB
Line 
1/****************************************************************************
2** $Id: qfontengine_pm.cpp 170 2007-04-10 21:12:37Z dmik $
3**
4** ???
5**
6** Copyright (C) 2003 Trolltech AS. All rights reserved.
7** Copyright (C) 2004 Norman ASA. Initial OS/2 Port.
8** Copyright (C) 2005 netlabs.org. Further OS/2 Development.
9**
10** This file is part of the kernel module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qfontengine_p.h"
39#include <qglobal.h>
40#include "qt_os2.h"
41#include "qapplication_p.h"
42
43#include <qpaintdevice.h>
44#include <qpainter.h>
45
46#include <private/qunicodetables_p.h>
47#include <qbitmap.h>
48
49#include <qthreadstorage.h>
50
51/// @todo need?
52//#ifndef M_PI
53//#define M_PI 3.14159265358979
54//#endif
55
56// per-thread unique screen ps to perform font operations
57
58/// @todo (r=dmik) optimize: use small mem hps'es 1x1 px instead of screen ps?
59class QDisplayPS
60{
61public:
62 QDisplayPS() { hps = WinGetScreenPS( HWND_DESKTOP ); }
63 ~QDisplayPS() { WinReleasePS( hps ); }
64 HPS hps;
65};
66static QThreadStorage<QDisplayPS *> display_ps;
67
68
69// general font engine
70
71QFontEngine::~QFontEngine()
72{
73 hps = 0;
74 delete pfm;
75}
76
77int QFontEngine::lineThickness() const
78{
79/// @todo (r=dmik) values from FONTMETRICS are not always good (line is too
80// thick or too close to glyphs). The algorithm below is not always perfect
81// either. Which one to leave?
82// // ad hoc algorithm
83// int score = fontDef.weight * fontDef.pixelSize;
84// int lw = score / 700;
85//
86// // looks better with thicker line for small pointsizes
87// if ( lw < 2 && score >= 1050 ) lw = 2;
88// if ( lw == 0 ) lw = 1;
89//
90// return lw;
91 return pfm->lUnderscoreSize;
92}
93
94int QFontEngine::underlinePosition() const
95{
96/// @todo (r=dmik) values from FONTMETRICS are not always good (line is too
97// thick or too close to glyphs). The algorithm below is not always perfect
98// either. Which one to leave?
99// int pos = ( ( lineThickness() * 2 ) + 3 ) / 6;
100 int pos = pfm->lUnderscorePosition;
101 // ensure one pixel between baseline and underline
102 return pos > 1 ? pos : 2;
103}
104
105HPS QFontEngine::ps() const
106{
107 // if hps is not zero this means a printer font.
108 // NOTE: such (printer) font engines can be currently created only by
109 // QPainter::updateFont() and they are removed from the font cache by
110 // QFontCache::cleanupPrinterFonts() that is called by QPainter::end(),
111 // so it is safe to store only a hps of the printer paint device instead
112 // the whole QPaintDevice object (these font engines cannot live after
113 // the printer paint device is destroyed -- they are destroyed first,
114 // when the painting has ended on a particular printer device).
115
116 HPS ps = hps;
117 if ( !ps ) {
118 if ( !display_ps.hasLocalData() )
119 display_ps.setLocalData( new QDisplayPS );
120 ps = display_ps.localData()->hps;
121 }
122 selectTo( ps );
123 return ps;
124}
125
126static inline void uint2STR8( uint num, PSTR8 str )
127{
128 uint *p = (uint *) str;
129 *(p++) = (num & 0x0F0F0F0F) + 0x41414141;
130 *p = ((num >> 4) & 0x0F0F0F0F) + 0x41414141;
131}
132
133static inline uint STR82uint( PSTR8 str )
134{
135 uint *p = (uint *) str;
136 uint num = (*(p++)) - 0x41414141;
137 num |= ((*p) - 0x41414141) << 4;
138 return num;
139}
140
141void QFontEngine::selectTo( HPS ps ) const
142{
143 // innotek ft2lib 2.40 release 1 has a strange bug: when we pass the STR8
144 // identifier to GpiCreateLogFont() with the fourth char being zero it
145 // causes a trap. So we use uint2STR8() to convert pointers to a string
146 // containing 8 uppercase letters instead of passing pointers directly.
147
148 STR8 id;
149 FATTRS dummy;
150 uint2STR8( 0, &id );
151 GpiQueryLogicalFont( ps, LCID_QTFont, &id, &dummy, sizeof(FATTRS) );
152 if ( STR82uint( &id ) != (uint) this ) {
153#if 0
154 qDebug( "QFontEngine::selectTo: selecting font '%i:%i.%s' to %08lX",
155 fontDef.pixelSize, fontDef.pointSize/10, fa.szFacename, ps );
156#endif
157 uint2STR8( (uint) this, &id );
158 GpiCreateLogFont( ps, &id, LCID_QTFont, &fa );
159 GpiSetCharSet( ps, LCID_QTFont );
160 if ( fa.fsFontUse & FATTR_FONTUSE_OUTLINE ) {
161 SIZEF sz;
162 sz.cy = MAKEFIXED( fontDef.pixelSize, 0 );
163 sz.cx = sz.cy;
164 GpiSetCharBox( ps, &sz );
165 }
166 }
167#if 0
168 else {
169 qDebug( "QFontEngine::selectTo: font '%i:%i.%s' is already selected to %08lX",
170 fontDef.pixelSize, fontDef.pointSize/10, fa.szFacename, ps );
171 }
172#endif
173}
174
175QFontEnginePM::QFontEnginePM( HPS ps, PFATTRS pfa, int pixelSize, int pointSize )
176{
177 hps = ps;
178 fa = *pfa;
179 pfm = new FONTMETRICS;
180
181 fontDef.pixelSize = pixelSize;
182 fontDef.pointSize = pointSize;
183 // the rest of fontDef is initialized by QFontDatabase::findFont()
184
185 GpiQueryFontMetrics( this->ps(), sizeof(FONTMETRICS), pfm );
186
187 // cache cost here should be in bytes. it is converted to
188 // kbytes by QFontCache::increaseCost()
189 /// @todo is this formula for cost ok?
190 cache_cost = pfm->lMaxBaselineExt * pfm->lAveCharWidth * 2000;
191
192 widthCache = new unsigned char [widthCacheSize];
193 memset( widthCache, 0, widthCacheSize );
194}
195
196#define isDBCSGlyph(g) ( (g) & 0xFF00 )
197
198#define queryGlyphSize(g) ( isDBCSGlyph( g ) ? 2 : 1 )
199
200/// @todo (r=dmik) current implementation of this fn uses the local8bit QChar
201// code as the glyph index, i.e. glyphs are just unicode chars converted to
202// 8 bit chars according to the current (system) code page. This will be
203// changed when all font-related code is rewritten to support true unicode
204// (for example, by using innotek ft2lib, since native OS/2 unicode support
205// relative to text output using GPI is pretty buggy).
206QFontEngine::Error QFontEnginePM::stringToCMap( const QChar *str, int len,
207 glyph_t *glyphs, advance_t *advances,
208 int *nglyphs, bool mirrored ) const
209{
210 /// @todo mirrored is currently ignored.
211 Q_UNUSED( mirrored );
212
213 const char *uconvFirstByteTable = qt_os2UconvFirstByteTable();
214
215 // convert multibyte chars to "wide char" glyphs
216 QCString s = QConstString( str, len ).string().local8Bit();
217 // sanity check (uint value must fit into positive int)
218 Q_ASSERT( (int) s.size() >= 1 );
219 if ( (int) s.size() < 1 ) {
220 *nglyphs = 0;
221 return OutOfMemory;
222 }
223 // we use QCString::size() instead of length() because the str array may
224 // contain embedded zero chars that must be also processed
225 int slen = (int) s.size() - 1; // exclude terminating zero
226 if ( slen == 0 ) {
227 // null string or no valid conversion, nothing to do
228 *nglyphs = 0;
229 return NoError;
230 }
231 int givenLen = *nglyphs;
232 int realLen = 0;
233 for( int i = 0; i < slen; i++ ) {
234 bool isDBCSLeadByte = uconvFirstByteTable[ (uchar) s[ i ] ] == 2;
235 if ( realLen < givenLen ) {
236 // enough space, store the glyph
237 glyphs[ realLen ] = s[ i ] & 0xFF;
238 if( isDBCSLeadByte )
239 glyphs[ realLen ] |= ( s[ ++i ] << 8 ) & 0xFF00;
240 } else {
241 // not enough space, keep calulating the length
242 if( isDBCSLeadByte )
243 ++i;
244 }
245 realLen++;
246 }
247
248 if ( givenLen < realLen ) {
249 *nglyphs = realLen;
250 return OutOfMemory;
251 }
252
253 if ( advances ) {
254 HPS ps = 0;
255 glyph_t glyph;
256 for( register int i = 0; i < realLen; i++ ) {
257 glyph = *(glyphs + i);
258 advances[i] = (glyph < widthCacheSize) ? widthCache[glyph] : 0;
259 // font-width cache failed
260 if ( !advances[i] ) {
261 if ( !ps ) ps = this->ps();
262 POINTL ptls [TXTBOX_COUNT];
263 GpiQueryTextBox( ps, queryGlyphSize( glyph ),
264 (PCH) &glyph, TXTBOX_COUNT, ptls );
265 advances[i] = ptls[4].x;
266 // if glyph's within cache range, store it for later
267 if ( glyph < widthCacheSize && advances[i] < 0x100 )
268 ((QFontEnginePM *)this)->widthCache[glyph] = advances[i];
269 }
270 }
271 }
272
273 *nglyphs = realLen;
274 return NoError;
275}
276
277// QRgb has the same RGB format (0xaarrggbb) as OS/2 uses (ignoring the
278// highest alpha byte) so we just mask alpha to get the valid OS/2 color.
279#define COLOR_VALUE(c) ((p->flags & QPainter::RGBColor) ? (c.rgb() & RGB_MASK) : c.pixel())
280
281// defined in qpaintdevice_pm.cpp
282extern const LONG qt_ropCodes_2ROP[];
283
284void QFontEnginePM::draw( QPainter *p, int x, int y, const QTextEngine
285 *engine, const QScriptItem *si, int textFlags )
286{
287 HPS ps = p->handle();
288
289 glyph_t *glyphs = engine->glyphs( si );
290 advance_t *advances = engine->advances( si );
291 qoffset_t *offsets = engine->offsets( si );
292
293 // GPI version of the opaque rectangle below the text is rather strange,
294 // so we draw it ourselves
295 if ( p->backgroundMode() == Qt::OpaqueMode ) {
296 LONG oldMix = GpiQueryMix( ps );
297 GpiSetMix( ps, FM_LEAVEALONE );
298 // use drawRect to have the rectangle properly transformed
299 /// @todo (r=dmik)
300 // we don't add 1 to si->ascent + si->descent to exactly match
301 // QFontMetrics::boundingRect(). This stuff needs to be reviewed
302 // and synchronized everywhere (first we should define
303 // is total_height = acsent + descent or greater by one?).
304 // This also relates to the baseline issue below.
305 p->drawRect( x, y - si->ascent, si->width, si->ascent + si->descent );
306 GpiSetMix( ps, oldMix );
307 }
308
309 if ( !(pfm->fsDefn & FM_DEFN_OUTLINE) && p->txop > QPainter::TxTranslate ) {
310 // GPI is not capable (?) to scale/shear/rotate bitmap fonts,
311 // so we do it by hand
312 QRect bbox( 0, 0, si->width, si->ascent + si->descent + 1 );
313 int w = bbox.width(), h = bbox.height();
314 if ( w == 0 || h == 0 )
315 return;
316 QWMatrix tmat = QPixmap::trueMatrix( p->xmat, w, h );
317 QBitmap bm( w, h, TRUE );
318 QPainter paint;
319
320 // draw text into the bitmap
321 paint.begin( &bm );
322 // select the proper font manually
323 paint.clearf( QPainter::DirtyFont );
324 selectTo ( paint.handle() );
325 draw( &paint, 0, si->ascent, engine, si, textFlags );
326 paint.end();
327 // transform bitmap
328 QBitmap wx_bm = bm.xForm( tmat );
329 if ( wx_bm.isNull() )
330 return;
331
332 // compute the position of the bitmap
333 double fx = x, fy = y, nfx, nfy;
334 p->xmat.map( fx,fy, &nfx, &nfy );
335 double tfx = 0., tfy = si->ascent, dx, dy;
336 tmat.map( tfx, tfy, &dx, &dy );
337 x = qRound( nfx - dx );
338 y = qRound( nfy - dy );
339
340 // flip y coordinate of the bitmap
341 if ( p->devh )
342 y = p->devh - (y + wx_bm.height());
343
344 POINTL ptls[] = { { x, y }, { x + wx_bm.width(), y + wx_bm.height() },
345 { 0, 0 } };
346
347 // leave bitmap background transparent when blitting
348 LONG oldBackMix = GpiQueryBackMix( p->handle() );
349 GpiSetBackMix( p->handle(), BM_SRCTRANSPARENT );
350
351 GpiBitBlt( p->handle(), wx_bm.handle(), 3, ptls,
352 qt_ropCodes_2ROP[p->rop], BBO_IGNORE );
353 GpiSetBackMix( p->handle(), oldBackMix );
354
355 return;
356 }
357
358 if ( p->testf(QPainter::DirtyFont) )
359 p->updateFont();
360
361 // leave text background transparent -- we've already drawn it if OpaqueMode
362 LONG oldBackMix = GpiQueryBackMix( ps );
363 GpiSetBackMix( ps, BM_LEAVEALONE );
364
365 /// @todo (dmik)
366 // OS/2 GPI thinks that regular font letters (without any lower elements)
367 // should "overlap" the baseline, while Qt/Win32 thinks they should "sit"
368 // on it. For now, we simply mimic the Qt/Win32 behavior (for compatibilty)
369 // by correcting the y coordinate by one pixel. This is rather stupid.
370 // The proper way is to review all the code that deals with
371 // ascents/descents, bounding boxes/rects etc. (currently, all this
372 /// stuff has various issues regarding to misaligmnent between the opaque
373 // rectangle of the char and its bounding box etc.) and make a correct
374 // desicion afterwards... Btw, Qt/Win32 is rather buggy here too.
375 y--;
376
377 // prepare transformations
378 bool nativexform = FALSE;
379 LONG ySign = 1;
380
381 if ( p->txop == QPainter::TxTranslate ) {
382 p->map( x, y, &x, &y );
383 } else if ( p->txop > QPainter::TxTranslate ) {
384 y = -y;
385 ySign = -1;
386 nativexform = p->setNativeXForm( TRUE /* assumeYNegation */ );
387 if( !nativexform )
388 return;
389 }
390
391 ULONG options = 0;
392
393 // We draw underscores and strikeouts ourselves (since OS/2 font rasterizer
394 // usually does this very ugly, and sometimes it doesn't work at all with
395 // some fonts (i.e. WarpSans)).
396 /*
397 if ( textFlags & Qt::Underline ) options |= CHS_UNDERSCORE;
398 if ( textFlags & Qt::StrikeOut ) options |= CHS_STRIKEOUT;
399 */
400
401 int xo = x;
402 POINTL ptl;
403
404 if ( !(si->analysis.bidiLevel % 2) ) {
405 bool haveOffsets = FALSE;
406 int w = 0;
407 for( int i = 0; i < si->num_glyphs; i++ ) {
408 if ( offsets[i].x || offsets[i].y ) {
409 haveOffsets = TRUE;
410 break;
411 }
412 w += advances[i];
413 }
414
415 if ( haveOffsets ) {
416 for( int i = 0; i < si->num_glyphs; i++ ) {
417 ptl.x = x + offsets->x;
418 ptl.y = y + ySign * offsets->y;
419 // flip y coordinate
420 if ( !nativexform && p->devh )
421 ptl.y = p->devh - (ptl.y + 1);
422 GpiCharStringPosAt( ps, &ptl, NULL, options,
423 queryGlyphSize( *glyphs ), (PCH) glyphs, NULL );
424 x += *advances;
425 glyphs++;
426 offsets++;
427 advances++;
428 }
429 } else {
430 // fast path
431 ptl.x = x;
432 ptl.y = y;
433 // flip y coordinate
434 if ( !nativexform && p->devh )
435 ptl.y = p->devh - (ptl.y + 1);
436 GpiMove( ps, &ptl );
437 // draw glyphs in 512 char chunks: it's a GpiCharString* limitation
438 advance_t adv[ 512 ];
439 char str[ 512 ];
440 int len = 0;
441 // convert "wide char" glyphs to a multi byte string
442 glyph_t *pg = glyphs;
443 advance_t *pa = advances;
444 for( int i = 0; i < si->num_glyphs; i++, pg++, pa++ ) {
445 str[ len ] = *pg & 0xFF;
446 adv[ len++ ] = *pa;
447
448 if( isDBCSGlyph( *pg ) ) {
449 str[ len ] = ( *pg & 0xFF00 ) >> 8;
450 adv[ len++ ] = 0;
451 }
452
453 if( len > 510 ) {
454 GpiCharStringPos( ps, NULL, options | CHS_VECTOR, len, str, (PLONG) adv );
455 len = 0;
456 }
457 }
458
459 if( len > 0 )
460 GpiCharStringPos( ps, NULL, options | CHS_VECTOR, len, str, (PLONG) adv );
461
462 x += w;
463 }
464 } else {
465 offsets += si->num_glyphs;
466 advances += si->num_glyphs;
467 glyphs += si->num_glyphs;
468 for( int i = 0; i < si->num_glyphs; i++ ) {
469 glyphs--;
470 offsets--;
471 advances--;
472 ptl.x = x + offsets->x;
473 ptl.y = y + ySign * offsets->y;
474 // flip y coordinate
475 if ( !nativexform && p->devh )
476 ptl.y = p->devh - (ptl.y + 1);
477 GpiCharStringPosAt( ps, &ptl, NULL, options,
478 queryGlyphSize( *glyphs ), (PCH) glyphs, NULL );
479 x += *advances;
480 }
481 }
482
483 if ( textFlags & (Qt::Overline | Qt::Underline | Qt::StrikeOut) ) {
484 AREABUNDLE oldAb, ab;
485 const LONG aa = ABB_COLOR | ABB_SET | ABB_SYMBOL;
486 GpiQueryAttrs( ps, PRIM_AREA, aa, &oldAb );
487 ab.lColor = COLOR_VALUE( p->cpen.color() );
488 ab.usSet = LCID_DEFAULT;
489 ab.usSymbol = PATSYM_SOLID;
490 GpiSetAttrs( ps, PRIM_AREA, aa, 0, &ab );
491
492 int lw = lineThickness() - 1; // inclusive
493 int lp;
494 // flip y coordinate
495 int yp = y;
496 if ( !nativexform && p->devh )
497 yp = p->devh - (yp + 1);
498 if ( textFlags & (Qt::Underline) ) {
499 ptl.x = xo;
500 ptl.y = yp - underlinePosition();
501 GpiMove( ps, &ptl );
502 ptl.x = x - 1; ptl.y -= lw;
503 GpiBox( ps, DRO_FILL, &ptl, 0, 0 );
504 }
505 if ( textFlags & (Qt::Overline) ) {
506 lp = ascent() + 1; // corresponds to QFontMetrics::overlinePos()
507 if ( !lp ) lp = 1;
508 ptl.x = xo;
509 ptl.y = yp + lp;
510 GpiMove( ps, &ptl );
511 ptl.x = x - 1; ptl.y -= lw;
512 GpiBox( ps, DRO_FILL, &ptl, 0, 0 );
513 }
514 if ( textFlags & (Qt::StrikeOut) ) {
515 lp = ascent() / 3; // corresponds to QFontMetrics::strikeOutPos()
516 if ( !lp ) lp = 1;
517 ptl.x = xo;
518 ptl.y = yp + lp;
519 GpiMove( ps, &ptl );
520 ptl.x = x - 1; ptl.y -= lw;
521 GpiBox( ps, DRO_FILL, &ptl, 0, 0 );
522 }
523 GpiSetAttrs( ps, PRIM_AREA, aa, 0, &oldAb );
524 }
525
526 if ( nativexform ) {
527 p->clearNativeXForm();
528 }
529
530 GpiSetBackMix( ps, oldBackMix );
531}
532
533glyph_metrics_t QFontEnginePM::boundingBox( const glyph_t *glyphs,
534 const advance_t *advances, const qoffset_t *offsets, int numGlyphs )
535{
536 Q_UNUSED( glyphs );
537 Q_UNUSED( offsets );
538
539 if ( numGlyphs == 0 )
540 return glyph_metrics_t();
541
542 int w = 0;
543 const advance_t *end = advances + numGlyphs;
544 while( end > advances )
545 w += *(--end);
546
547 /// @todo (r=dmik) look at usage of this fn, is the return correct?
548 return glyph_metrics_t(
549 0, -pfm->lMaxAscender, w, pfm->lMaxAscender + pfm->lMaxDescender, w, 0
550 );
551}
552
553glyph_metrics_t QFontEnginePM::boundingBox( glyph_t glyph )
554{
555 POINTL ptls [TXTBOX_COUNT];
556 GpiQueryTextBox( ps(), queryGlyphSize( glyph ),
557 (PCH) &glyph, TXTBOX_COUNT, ptls );
558 int minx = 0, miny = 0, maxx = 0, maxy = 0;
559 for ( int i = 0; i < 4; i++ ) {
560 minx = QMIN( minx, ptls[i].x );
561 miny = QMIN( miny, ptls[i].y );
562 maxx = QMAX( maxx, ptls[i].x );
563 maxy = QMAX( maxy, ptls[i].y );
564 }
565 return glyph_metrics_t(
566 minx, -maxy, maxx - minx + 1, maxy - miny + 1, ptls[4].x, -ptls[4].y
567 );
568}
569
570int QFontEnginePM::ascent() const
571{
572 return pfm->lMaxAscender;
573}
574
575int QFontEnginePM::descent() const
576{
577 return pfm->lMaxDescender;
578}
579
580int QFontEnginePM::leading() const
581{
582 return pfm->lExternalLeading;
583}
584
585int QFontEnginePM::maxCharWidth() const
586{
587 return pfm->lMaxCharInc;
588}
589
590int QFontEnginePM::minLeftBearing() const
591{
592 /// @todo (r=dmik) later
593 return 0;
594// if ( lbearing == SHRT_MIN )
595// minRightBearing(); // calculates both
596//
597// return lbearing;
598}
599
600int QFontEnginePM::minRightBearing() const
601{
602 /// @todo (r=dmik) later
603 return 0;
604//#ifdef Q_OS_TEMP
605// return 0;
606//#else
607// if ( rbearing == SHRT_MIN ) {
608// int ml = 0;
609// int mr = 0;
610// if ( ttf ) {
611// HDC hdc = dc();
612// SelectObject( hdc, hfont );
613// ABC *abc = 0;
614// int n = QT_WA_INLINE( tm.w.tmLastChar - tm.w.tmFirstChar, tm.a.tmLastChar - tm.a.tmFirstChar );
615// if ( n <= max_font_count ) {
616// abc = new ABC[n+1];
617// QT_WA( {
618// GetCharABCWidths(hdc, tm.w.tmFirstChar, tm.w.tmLastChar, abc);
619// }, {
620// GetCharABCWidthsA(hdc,tm.a.tmFirstChar,tm.a.tmLastChar,abc);
621// } );
622// } else {
623// abc = new ABC[char_table_entries+1];
624// QT_WA( {
625// for( int i = 0; i < char_table_entries; i++ )
626// GetCharABCWidths(hdc, char_table[i], char_table[i], abc+i);
627// }, {
628// for( int i = 0; i < char_table_entries; i++ ) {
629// QCString w = QString(QChar(char_table[i])).local8Bit();
630// if ( w.length() == 1 ) {
631// uint ch8 = (uchar)w[0];
632// GetCharABCWidthsA(hdc, ch8, ch8, abc+i );
633// }
634// }
635// } );
636// n = char_table_entries;
637// }
638// ml = abc[0].abcA;
639// mr = abc[0].abcC;
640// for ( int i = 1; i < n; i++ ) {
641// if ( abc[i].abcA + abc[i].abcB + abc[i].abcC != 0 ) {
642// ml = QMIN(ml,abc[i].abcA);
643// mr = QMIN(mr,abc[i].abcC);
644// }
645// }
646// delete [] abc;
647// } else {
648// QT_WA( {
649// ABCFLOAT *abc = 0;
650// int n = tm.w.tmLastChar - tm.w.tmFirstChar+1;
651// if ( n <= max_font_count ) {
652// abc = new ABCFLOAT[n];
653// GetCharABCWidthsFloat(hdc, tm.w.tmFirstChar, tm.w.tmLastChar, abc);
654// } else {
655// abc = new ABCFLOAT[char_table_entries];
656// for( int i = 0; i < char_table_entries; i++ )
657// GetCharABCWidthsFloat(hdc, char_table[i], char_table[i], abc+i);
658// n = char_table_entries;
659// }
660// float fml = abc[0].abcfA;
661// float fmr = abc[0].abcfC;
662// for (int i=1; i<n; i++) {
663// if ( abc[i].abcfA + abc[i].abcfB + abc[i].abcfC != 0 ) {
664// fml = QMIN(fml,abc[i].abcfA);
665// fmr = QMIN(fmr,abc[i].abcfC);
666// }
667// }
668// ml = int(fml-0.9999);
669// mr = int(fmr-0.9999);
670// delete [] abc;
671// } , {
672// ml = 0;
673// mr = -tm.a.tmOverhang;
674// } );
675// }
676// ((QFontEngine *)this)->lbearing = ml;
677// ((QFontEngine *)this)->rbearing = mr;
678// }
679//
680// return rbearing;
681//#endif
682}
683
684bool QFontEnginePM::canRender( const QChar *string, int len )
685{
686 /// @todo (r=dmik) later
687 return TRUE;
688
689// if ( symbol ) {
690// while( len-- ) {
691// if ( getGlyphIndex( cmap, string->unicode() ) == 0 ) {
692// if( string->unicode() < 0x100 ) {
693// if(getGlyphIndex( cmap, string->unicode()+0xf000) == 0)
694// return FALSE;
695// } else {
696// return FALSE;
697// }
698// }
699// string++;
700// }
701// } else if ( ttf ) {
702// while( len-- ) {
703// if ( getGlyphIndex( cmap, string->unicode() ) == 0 )
704// return FALSE;
705// string++;
706// }
707// } else {
708// QT_WA( {
709// while( len-- ) {
710// if ( tm.w.tmFirstChar > string[len].unicode() || tm.w.tmLastChar < string[len].unicode() )
711// return FALSE;
712// }
713// }, {
714// while( len-- ) {
715// if ( tm.a.tmFirstChar > string[len].unicode() || tm.a.tmLastChar < string[len].unicode() )
716// return FALSE;
717// }
718// } );
719// }
720// return TRUE;
721}
722
723QFontEngine::Type QFontEnginePM::type() const
724{
725 return QFontEngine::PM;
726}
727
728
729// ----------------------------------------------------------------------------
730// True type support methods
731// ----------------------------------------------------------------------------
732
733
734
735/// @todo (r=dmik) all this may be necessary when ft2lib is used
736#if 0
737#define MAKE_TAG(ch1, ch2, ch3, ch4) (\
738 (((DWORD)(ch4)) << 24) | \
739 (((DWORD)(ch3)) << 16) | \
740 (((DWORD)(ch2)) << 8) | \
741 ((DWORD)(ch1)) \
742 )
743
744static inline Q_UINT32 getUInt(unsigned char *p)
745{
746 Q_UINT32 val;
747 val = *p++ << 24;
748 val |= *p++ << 16;
749 val |= *p++ << 8;
750 val |= *p;
751
752 return val;
753}
754
755static inline Q_UINT16 getUShort(unsigned char *p)
756{
757 Q_UINT16 val;
758 val = *p++ << 8;
759 val |= *p;
760
761 return val;
762}
763
764static inline void tag_to_string( char *string, Q_UINT32 tag )
765{
766 string[0] = (tag >> 24)&0xff;
767 string[1] = (tag >> 16)&0xff;
768 string[2] = (tag >> 8)&0xff;
769 string[3] = tag&0xff;
770 string[4] = 0;
771}
772
773static Q_UINT16 getGlyphIndex( unsigned char *table, unsigned short unicode )
774{
775 unsigned short format = getUShort( table );
776 if ( format == 0 ) {
777 if ( unicode < 256 )
778 return (int) *(table+6+unicode);
779 } else if ( format == 2 ) {
780 qWarning("format 2 encoding table for Unicode, not implemented!");
781 } else if ( format == 4 ) {
782 Q_UINT16 segCountX2 = getUShort( table + 6 );
783 unsigned char *ends = table + 14;
784 Q_UINT16 endIndex = 0;
785 int i = 0;
786 for ( ; i < segCountX2/2 && (endIndex = getUShort( ends + 2*i )) < unicode; i++ );
787
788 unsigned char *idx = ends + segCountX2 + 2 + 2*i;
789 Q_UINT16 startIndex = getUShort( idx );
790
791 if ( startIndex > unicode )
792 return 0;
793
794 idx += segCountX2;
795 Q_INT16 idDelta = (Q_INT16)getUShort( idx );
796 idx += segCountX2;
797 Q_UINT16 idRangeoffset_t = (Q_UINT16)getUShort( idx );
798
799 Q_UINT16 glyphIndex;
800 if ( idRangeoffset_t ) {
801 Q_UINT16 id = getUShort( idRangeoffset_t + 2*(unicode - startIndex) + idx);
802 if ( id )
803 glyphIndex = ( idDelta + id ) % 0x10000;
804 else
805 glyphIndex = 0;
806 } else {
807 glyphIndex = (idDelta + unicode) % 0x10000;
808 }
809 return glyphIndex;
810 }
811
812 return 0;
813}
814
815
816static unsigned char *getCMap( HDC hdc, bool &symbol )
817{
818 const DWORD CMAP = MAKE_TAG( 'c', 'm', 'a', 'p' );
819
820 unsigned char header[4];
821
822 // get the CMAP header and the number of encoding tables
823 DWORD bytes =
824#ifndef Q_OS_TEMP
825 GetFontData( hdc, CMAP, 0, &header, 4 );
826#else
827 0;
828#endif
829 if ( bytes == GDI_ERROR )
830 return 0;
831 unsigned short version = getUShort( header );
832 if ( version != 0 )
833 return 0;
834
835 unsigned short numTables = getUShort( header+2);
836 unsigned char *maps = new unsigned char[8*numTables];
837
838 // get the encoding table and look for Unicode
839#ifndef Q_OS_TEMP
840 bytes = GetFontData( hdc, CMAP, 4, maps, 8*numTables );
841#endif
842 if ( bytes == GDI_ERROR )
843 return 0;
844
845 symbol = TRUE;
846 unsigned int unicode_table = 0;
847 for ( int n = 0; n < numTables; n++ ) {
848 Q_UINT32 version = getUInt( maps + 8*n );
849 // accept both symbol and Unicode encodings. prefer unicode.
850 if ( version == 0x00030001 || version == 0x00030000 ) {
851 unicode_table = getUInt( maps + 8*n + 4 );
852 if ( version == 0x00030001 ) {
853 symbol = FALSE;
854 break;
855 }
856 }
857 }
858
859 if ( !unicode_table ) {
860 // qDebug("no unicode table found" );
861 return 0;
862 }
863
864 delete [] maps;
865
866 // get the header of the unicode table
867#ifndef Q_OS_TEMP
868 bytes = GetFontData( hdc, CMAP, unicode_table, &header, 4 );
869#endif
870 if ( bytes == GDI_ERROR )
871 return 0;
872
873 unsigned short length = getUShort( header+2 );
874 unsigned char *unicode_data = new unsigned char[length];
875
876 // get the cmap table itself
877#ifndef Q_OS_TEMP
878 bytes = GetFontData( hdc, CMAP, unicode_table, unicode_data, length );
879#endif
880 if ( bytes == GDI_ERROR ) {
881 delete [] unicode_data;
882 return 0;
883 }
884 return unicode_data;
885}
886#endif // if 0
887
888
Note: See TracBrowser for help on using the repository browser.