source: trunk/src/kernel/qfontengine_pm.cpp@ 63

Last change on this file since 63 was 60, checked in by dmik, 20 years ago

Fixed: All text drawing functions were not able to draw strings longer than 512 chars (GpiCharString* limitation).

  • Property svn:keywords set to Id
File size: 32.9 KB
Line 
1/****************************************************************************
2** $Id: qfontengine_pm.cpp 60 2006-01-30 21:07: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//@@TODO (dmik): need?
46//#include <limits.h>
47//#include <math.h>
48
49#include <private/qunicodetables_p.h>
50#include <qbitmap.h>
51
52#include <qthreadstorage.h>
53
54//@@TODO (dmik): need?
55//#ifndef M_PI
56//#define M_PI 3.14159265358979
57//#endif
58
59// per-thread unique screen ps to perform font operations
60
61//@@TODO (dmik): optimize: use small mem hps'es 1x1 px instead of screen ps?
62class QDisplayPS
63{
64public:
65 QDisplayPS() { hps = WinGetScreenPS( HWND_DESKTOP ); }
66 ~QDisplayPS() { WinReleasePS( hps ); }
67 HPS hps;
68};
69static QThreadStorage<QDisplayPS *> display_ps;
70
71
72//@@TODO (dmik): remove?
73//// defined in qtextengine_win.cpp
74//typedef void *SCRIPT_CACHE;
75//typedef HRESULT (WINAPI *fScriptFreeCache)( SCRIPT_CACHE *);
76//extern fScriptFreeCache ScriptFreeCache;
77
78
79//@@TODO (dmik): remove?
80//static unsigned char *getCMap( HDC hdc, bool & );
81//static Q_UINT16 getGlyphIndex( unsigned char *table, unsigned short unicode );
82
83
84//@@TODO (dmik): remove?
85//HDC shared_dc = 0; // common dc for all fonts
86//static HFONT shared_dc_font = 0; // used by Windows 95/98
87//static HFONT stock_sysfont = 0;
88//
89//static inline HFONT systemFont()
90//{
91// if ( stock_sysfont == 0 )
92// stock_sysfont = (HFONT)GetStockObject(SYSTEM_FONT);
93// return stock_sysfont;
94//}
95
96// general font engine
97
98QFontEngine::~QFontEngine()
99{
100 hps = 0;
101 delete pfm;
102//@@TODO (dmik): remove...
103// QT_WA( {
104// if ( hdc ) { // one DC per font (Win NT)
105// //SelectObject( hdc, systemFont() );
106// if ( !stockFont )
107// DeleteObject( hfont );
108// if ( !paintDevice )
109// ReleaseDC( 0, hdc );
110// hdc = 0;
111// hfont = 0;
112// }
113// } , {
114// if ( hfont ) { // shared DC (Windows 95/98)
115// if ( shared_dc_font == hfont ) { // this is the current font
116// Q_ASSERT( shared_dc != 0 );
117// SelectObject( shared_dc, systemFont() );
118// shared_dc_font = 0;
119// }
120// if ( !stockFont )
121// DeleteObject( hfont );
122// hfont = 0;
123// }
124// } );
125// delete [] cmap;
126//
127// // for Uniscribe
128// if ( ScriptFreeCache )
129// ScriptFreeCache( &script_cache );
130}
131
132int QFontEngine::lineThickness() const
133{
134//@@TODO (dmik): values from FONTMETRICS are not always good (line is too
135// thick or too close to glyphs). The algorithm below is not always perfect
136// either. Which one to leave?
137// // ad hoc algorithm
138// int score = fontDef.weight * fontDef.pixelSize;
139// int lw = score / 700;
140//
141// // looks better with thicker line for small pointsizes
142// if ( lw < 2 && score >= 1050 ) lw = 2;
143// if ( lw == 0 ) lw = 1;
144//
145// return lw;
146 return pfm->lUnderscoreSize;
147}
148
149int QFontEngine::underlinePosition() const
150{
151//@@TODO (dmik): values from FONTMETRICS are not always good (line is too
152// thick or too close to glyphs). The algorithm below is not always perfect
153// either. Which one to leave?
154// int pos = ( ( lineThickness() * 2 ) + 3 ) / 6;
155 int pos = pfm->lUnderscorePosition;
156 // ensure one pixel between baseline and underline
157 return pos > 1 ? pos : 2;
158}
159
160HPS QFontEngine::ps() const
161{
162 // if hps is not zero this means a printer font.
163 // NOTE: such (printer) font engines can be currently created only by
164 // QPainter::updateFont() and they are removed from the font cache by
165 // QFontCache::cleanupPrinterFonts() that is called by QPainter::end(),
166 // so it is safe to store only a hps of the printer paint device instead
167 // the whole QPaintDevice object (these font engines cannot live after
168 // the printer paint device is destroyed -- they are destroyed first,
169 // when the painting has ended on a particular printer device).
170
171 HPS ps = hps;
172 if ( !ps ) {
173 if ( !display_ps.hasLocalData() )
174 display_ps.setLocalData( new QDisplayPS );
175 ps = display_ps.localData()->hps;
176 }
177 selectTo( ps );
178 return ps;
179}
180
181static inline void uint2STR8( uint num, PSTR8 str )
182{
183 uint *p = (uint *) str;
184 *(p++) = (num & 0x0F0F0F0F) + 0x41414141;
185 *p = ((num >> 4) & 0x0F0F0F0F) + 0x41414141;
186}
187
188static inline uint STR82uint( PSTR8 str )
189{
190 uint *p = (uint *) str;
191 uint num = (*(p++)) - 0x41414141;
192 num |= ((*p) - 0x41414141) << 4;
193 return num;
194}
195
196void QFontEngine::selectTo( HPS ps ) const
197{
198 // innotek ft2lib 2.40 release 1 has a strange bug: when we pass the STR8
199 // identifier to GpiCreateLogFont() with the fourth char being zero it
200 // causes a trap. So we use uint2STR8() to convert pointers to a string
201 // containing 8 uppercase letters instead of passing pointers directly.
202
203 STR8 id;
204 FATTRS dummy;
205 uint2STR8( 0, &id );
206 GpiQueryLogicalFont( ps, LCID_QTFont, &id, &dummy, sizeof(FATTRS) );
207 if ( STR82uint( &id ) != (uint) this ) {
208#if 0
209 qDebug( "QFontEngine::selectTo: selecting font '%i:%i.%s' to %08lX",
210 fontDef.pixelSize, fontDef.pointSize/10, fa.szFacename, ps );
211#endif
212 uint2STR8( (uint) this, &id );
213 GpiCreateLogFont( ps, &id, LCID_QTFont, &fa );
214 GpiSetCharSet( ps, LCID_QTFont );
215 if ( fa.fsFontUse & FATTR_FONTUSE_OUTLINE ) {
216 SIZEF sz;
217 sz.cy = MAKEFIXED( fontDef.pixelSize, 0 );
218 sz.cx = sz.cy;
219 GpiSetCharBox( ps, &sz );
220 }
221 }
222#if 0
223 else {
224 qDebug( "QFontEngine::selectTo: font '%i:%i.%s' is already selected to %08lX",
225 fontDef.pixelSize, fontDef.pointSize/10, fa.szFacename, ps );
226 }
227#endif
228}
229
230//@@TODO (dmik): remove?
231//HDC QFontEngine::dc() const
232//{
233// if ( hdc || (qt_winver & Qt::WV_NT_based) ) // either NT_based or Printer
234// return hdc;
235// Q_ASSERT( shared_dc != 0 && hfont != 0 );
236// if ( shared_dc_font != hfont ) {
237// SelectObject( shared_dc, hfont );
238// shared_dc_font = hfont;
239// }
240// return shared_dc;
241//}
242
243//@@TODO (dmik): remove?
244//void QFontEngine::getCMap()
245//{
246// QT_WA( {
247// ttf = (bool)(tm.w.tmPitchAndFamily & TMPF_TRUETYPE);
248// } , {
249// ttf = (bool)(tm.a.tmPitchAndFamily & TMPF_TRUETYPE);
250// } );
251// HDC hdc = dc();
252// SelectObject( hdc, hfont );
253// bool symb = false;
254// cmap = ttf ? ::getCMap( hdc, symb ) : 0;
255// if ( !cmap ) {
256// ttf = false;
257// symb = false;
258// }
259// symbol = symb;
260// script_cache = 0;
261//}
262
263//@@TODO (dmik): remove
264//void QFontEngine::getGlyphIndexes( const QChar *ch, int numChars, glyph_t *glyphs, bool mirrored ) const
265//{
266// if ( mirrored ) {
267// if ( symbol ) {
268// while( numChars-- ) {
269// *glyphs = getGlyphIndex(cmap, ch->unicode() );
270// if(!*glyphs && ch->unicode() < 0x100)
271// *glyphs = getGlyphIndex(cmap, ch->unicode()+0xf000 );
272// glyphs++;
273// ch++;
274// }
275// } else if ( ttf ) {
276// while( numChars-- ) {
277// *glyphs = getGlyphIndex(cmap, ::mirroredChar(*ch).unicode() );
278// glyphs++;
279// ch++;
280// }
281// } else {
282// while( numChars-- ) {
283// *glyphs = ::mirroredChar(*ch).unicode();
284// glyphs++;
285// ch++;
286// }
287// }
288// } else {
289// if ( symbol ) {
290// while( numChars-- ) {
291// *glyphs = getGlyphIndex(cmap, ch->unicode() );
292// if(!*glyphs && ch->unicode() < 0x100)
293// *glyphs = getGlyphIndex(cmap, ch->unicode()+0xf000 );
294// glyphs++;
295// ch++;
296// }
297// } else if ( ttf ) {
298// while( numChars-- ) {
299// *glyphs = getGlyphIndex(cmap, ch->unicode() );
300// glyphs++;
301// ch++;
302// }
303// } else {
304// while( numChars-- ) {
305// *glyphs = ch->unicode();
306// glyphs++;
307// ch++;
308// }
309// }
310// }
311//}
312
313QFontEnginePM::QFontEnginePM( HPS ps, PFATTRS pfa, int pixelSize, int pointSize )
314{
315 hps = ps;
316 fa = *pfa;
317 pfm = new FONTMETRICS;
318
319 fontDef.pixelSize = pixelSize;
320 fontDef.pointSize = pointSize;
321 // the rest of fontDef is initialized by QFontDatabase::findFont()
322
323 GpiQueryFontMetrics( this->ps(), sizeof(FONTMETRICS), pfm );
324
325 // cache cost here should be in bytes. it is converted to
326 // kbytes by QFontCache::increaseCost()
327//@@TODO (dmik): is this formula for cost ok?
328 cache_cost = pfm->lMaxBaselineExt * pfm->lAveCharWidth * 2000;
329
330 widthCache = new unsigned char [widthCacheSize];
331 memset( widthCache, 0, widthCacheSize );
332}
333
334//@@TODO (dmik): remove
335//QFontEngineWin::QFontEngineWin( const char * name, HDC _hdc, HFONT _hfont, bool stockFont, LOGFONT lf )
336//{
337// paintDevice = FALSE;
338// //qDebug("regular windows font engine created: font='%s', size=%d", name, lf.lfHeight);
339//
340// _name = name;
341//
342// hdc = _hdc;
343// hfont = _hfont;
344// logfont = lf;
345// SelectObject( dc(), hfont );
346// this->stockFont = stockFont;
347//
348// lbearing = SHRT_MIN;
349// rbearing = SHRT_MIN;
350//
351// BOOL res;
352// QT_WA( {
353// res = GetTextMetricsW( dc(), &tm.w );
354// } , {
355// res = GetTextMetricsA( dc(), &tm.a );
356// } );
357//#ifndef QT_NO_DEBUG
358// if ( !res )
359// qSystemWarning( "QFontPrivate: GetTextMetrics failed" );
360//#endif
361// cache_cost = tm.w.tmHeight * tm.w.tmAveCharWidth * 2000;
362// getCMap();
363//
364// useTextOutA = FALSE;
365//#ifndef Q_OS_TEMP
366// // TextOutW doesn't work for symbol fonts on Windows 95!
367// // since we're using glyph indices we don't care for ttfs about this!
368// if ( qt_winver == Qt::WV_95 && !ttf &&
369// ( _name == "Marlett" || _name == "Symbol" || _name == "Webdings" || _name == "Wingdings" ) )
370// useTextOutA = TRUE;
371//#endif
372// memset( widthCache, 0, sizeof(widthCache) );
373//}
374
375//@@TODO (dmik): current implementation of this fn uses the local8bit QChar
376// code as the glyph index, i.e. glyphs are just unicode chars converted to
377// 8 bit chars according to the current (system) code page. This will be
378// changed when all font-related code will be rewritten to support true unicode
379// (for example, by using innotek ft2lib, since native OS/2 unicode support
380// relative to text output using GPI is pretty buggy).
381QFontEngine::Error QFontEnginePM::stringToCMap( const QChar *str, int len, glyph_t *glyphs, advance_t *advances, int *nglyphs, bool mirrored ) const
382{
383 if ( *nglyphs < len ) {
384 *nglyphs = len;
385 return OutOfMemory;
386 }
387
388//@@TODO (dmik): mirrored is currently ignored.
389 Q_UNUSED(mirrored);
390
391 // convert chars to glyphs
392 QCString s = QConstString( str, len ).string().local8Bit();
393 for ( int i = 0; i < len; i++ ) {
394 glyphs[i] = (uchar) s[i];
395 }
396
397 if ( advances ) {
398 HPS ps = 0;
399 glyph_t glyph;
400 for( register int i = 0; i < len; i++ ) {
401 glyph = *(glyphs + i);
402 advances[i] = (glyph < widthCacheSize) ? widthCache[glyph] : 0;
403 // font-width cache failed
404 if ( !advances[i] ) {
405 if ( !ps ) ps = this->ps();
406 POINTL ptls [TXTBOX_COUNT];
407 GpiQueryTextBox( ps, 1, &s[i], TXTBOX_COUNT, ptls);
408 advances[i] = ptls[4].x;
409 // if glyph's within cache range, store it for later
410 if ( glyph < widthCacheSize && advances[i] < 0x100 )
411 ((QFontEnginePM *)this)->widthCache[glyph] = advances[i];
412 }
413 }
414 }
415
416 *nglyphs = len;
417 return NoError;
418
419//@@TODO (dmik): remove
420// if ( *nglyphs < len ) {
421// *nglyphs = len;
422// return OutOfMemory;
423// }
424//
425// getGlyphIndexes( str, len, glyphs, mirrored );
426//
427// if ( advances ) {
428// HDC hdc = dc();
429// unsigned int glyph;
430// int overhang = (qt_winver & Qt::WV_DOS_based) ? tm.a.tmOverhang : 0;
431// for( register int i = 0; i < len; i++ ) {
432// glyph = *(glyphs + i);
433// advances[i] = (glyph < widthCacheSize) ? widthCache[glyph] : 0;
434// // font-width cache failed
435// if ( !advances[i] ) {
436// SIZE size;
437// GetTextExtentPoint32W( hdc, (wchar_t *)str, 1, &size );
438// advances[i] = size.cx - overhang;
439// // if glyph's within cache range, store it for later
440// if ( glyph < widthCacheSize && (size.cx - overhang) < 0x100 )
441// ((QFontEngineWin *)this)->widthCache[glyph] = size.cx - overhang;
442// }
443// str++;
444// }
445// }
446//
447// *nglyphs = len;
448// return NoError;
449}
450
451// QRgb has the same RGB format (0xaarrggbb) as OS/2 uses (ignoring the
452// highest alpha byte) so we just mask alpha to get the valid OS/2 color.
453#define COLOR_VALUE(c) ((p->flags & QPainter::RGBColor) ? (c.rgb() & RGB_MASK) : c.pixel())
454
455// defined in qpaintdevice_pm.cpp
456extern const LONG qt_ropCodes_2ROP[];
457
458void QFontEnginePM::draw( QPainter *p, int x, int y, const QTextEngine *engine, const QScriptItem *si, int textFlags )
459{
460 HPS ps = p->handle();
461
462 glyph_t *glyphs = engine->glyphs( si );
463 advance_t *advances = engine->advances( si );
464 qoffset_t *offsets = engine->offsets( si );
465
466 /// @todo (dmik)
467 // glyphs here are just 8 bit chars (see stringToCMap()),
468 // and glyph_t is temporarly defined as unsigned char.
469 // it should be changed later.
470 PSZ cglyphs = (PSZ) glyphs;
471 /*
472 QConstString str( (QChar *)glyphs, si->num_glyphs );
473 QCString cglyphs = str.string().latin1();
474 */
475
476 // GPI version of the opaque rectangle below the text is rather strange,
477 // so we draw it ourselves
478 if ( p->backgroundMode() == Qt::OpaqueMode ) {
479 LONG oldMix = GpiQueryMix( ps );
480 GpiSetMix( ps, FM_LEAVEALONE );
481 // use drawRect to have the rectangle properly transformed
482 /// @todo (dmik)
483 // we don't add 1 to si->ascent + si->descent to exactly match
484 // QFontMetrics::boundingRect(). This stuff needs to be reviewed
485 // and synchronized everywhere (first we should define
486 // is total_height = acsent + descent or greater by one?).
487 // This also relates to the baseline issue below.
488 p->drawRect( x, y - si->ascent, si->width, si->ascent + si->descent );
489 GpiSetMix( ps, oldMix );
490 }
491
492 if ( !(pfm->fsDefn & FM_DEFN_OUTLINE) && p->txop > QPainter::TxTranslate ) {
493 // GPI is not capable (?) to scale/shear/rotate bitmap fonts,
494 // so we do it by hand
495 QRect bbox( 0, 0, si->width, si->ascent + si->descent + 1 );
496 int w = bbox.width(), h = bbox.height();
497 if ( w == 0 || h == 0 )
498 return;
499 QWMatrix tmat = QPixmap::trueMatrix( p->xmat, w, h );
500 QBitmap bm( w, h, TRUE );
501 QPainter paint;
502
503 // draw text into the bitmap
504 paint.begin( &bm );
505 // select the proper font manually
506 paint.clearf( QPainter::DirtyFont );
507 selectTo ( paint.handle() );
508 draw( &paint, 0, si->ascent, engine, si, textFlags );
509 paint.end();
510 // transform bitmap
511 QBitmap wx_bm = bm.xForm( tmat );
512 if ( wx_bm.isNull() )
513 return;
514
515 // compute the position of the bitmap
516 double fx = x, fy = y, nfx, nfy;
517 p->xmat.map( fx,fy, &nfx, &nfy );
518 double tfx = 0., tfy = si->ascent, dx, dy;
519 tmat.map( tfx, tfy, &dx, &dy );
520 x = qRound( nfx - dx );
521 y = qRound( nfy - dy );
522
523 // flip y coordinate of the bitmap
524 if ( p->devh )
525 y = p->devh - (y + wx_bm.height());
526
527 POINTL ptls[] = { { x, y }, { x + wx_bm.width(), y + wx_bm.height() },
528 { 0, 0 } };
529
530 // leave bitmap background transparent when blitting
531 LONG oldBackMix = GpiQueryBackMix( p->handle() );
532 GpiSetBackMix( p->handle(), BM_SRCTRANSPARENT );
533
534 GpiBitBlt( p->handle(), wx_bm.handle(), 3, ptls,
535 qt_ropCodes_2ROP[p->rop], BBO_IGNORE );
536 GpiSetBackMix( p->handle(), oldBackMix );
537
538 return;
539 }
540
541 if ( p->testf(QPainter::DirtyFont) )
542 p->updateFont();
543
544 // leave text background transparent -- we've already drawn it if OpaqueMode
545 LONG oldBackMix = GpiQueryBackMix( ps );
546 GpiSetBackMix( ps, BM_LEAVEALONE );
547
548 /// @todo (dmik)
549 // OS/2 GPI thinks that regular font letters (without any lower elements)
550 // should "overlap" the baseline, while Qt/Win32 thinks they should "sit"
551 // on it. For now, we simply mimic the Qt/Win32 behavior (for compatibilty)
552 // by correcting the y coordinate by one pixel. This is rather stupid.
553 // The proper way is to review all the code that deals with
554 // ascents/descents, bounding boxes/rects etc. (currently, all this
555 /// stuff has various issues regarding to misaligmnent between the opaque
556 // rectangle of the char and its bounding box etc.) and make a correct
557 // desicion afterwards... Btw, Qt/Win32 is rather buggy here too.
558 y--;
559
560 // prepare transformations
561 bool nativexform = FALSE;
562 LONG ySign = 1;
563
564 if ( p->txop == QPainter::TxTranslate ) {
565 p->map( x, y, &x, &y );
566 } else if ( p->txop > QPainter::TxTranslate ) {
567 y = -y;
568 ySign = -1;
569 nativexform = p->setNativeXForm( TRUE /* assumeYNegation */ );
570 if( !nativexform )
571 return;
572 }
573
574 ULONG options = 0;
575
576 // We draw underscores and strikeouts ourselves (since OS/2 font rasterizer
577 // usually does this very ugly, and sometimes it doesn't work at all with
578 // some fonts (i.e. WarpSans)).
579 /*
580 if ( textFlags & Qt::Underline ) options |= CHS_UNDERSCORE;
581 if ( textFlags & Qt::StrikeOut ) options |= CHS_STRIKEOUT;
582 */
583
584 int xo = x;
585 POINTL ptl;
586
587 if ( !(si->analysis.bidiLevel % 2) ) {
588 bool haveOffsets = FALSE;
589 int w = 0;
590 for( int i = 0; i < si->num_glyphs; i++ ) {
591 if ( offsets[i].x || offsets[i].y ) {
592 haveOffsets = TRUE;
593 break;
594 }
595 w += advances[i];
596 }
597
598 if ( haveOffsets ) {
599 for( int i = 0; i < si->num_glyphs; i++ ) {
600 char chr = *glyphs;
601 ptl.x = x + offsets->x;
602 ptl.y = y + ySign * offsets->y;
603 // flip y coordinate
604 if ( !nativexform && p->devh )
605 ptl.y = p->devh - (ptl.y + 1);
606 GpiCharStringPosAt( ps, &ptl, NULL, options, 1, &chr, NULL );
607 x += *advances;
608 glyphs++;
609 offsets++;
610 advances++;
611 }
612 } else {
613 // fast path
614 ptl.x = x;
615 ptl.y = y;
616 // flip y coordinate
617 if ( !nativexform && p->devh )
618 ptl.y = p->devh - (ptl.y + 1);
619 // draw glyphs in 512 char chunks: it's a GpiCharString* limitation
620 GpiMove( ps, &ptl );
621 for ( int i = 0; i < si->num_glyphs; i += 512 ) {
622 GpiCharStringPos( ps, NULL, options | CHS_VECTOR,
623 QMIN( si->num_glyphs - i, 512 ), cglyphs, (PLONG) advances );
624 cglyphs += 512;
625 advances += 512;
626 }
627 x += w;
628 }
629 } else {
630 offsets += si->num_glyphs;
631 advances += si->num_glyphs;
632 glyphs += si->num_glyphs;
633 for( int i = 0; i < si->num_glyphs; i++ ) {
634 glyphs--;
635 offsets--;
636 advances--;
637 char chr = *glyphs;
638 ptl.x = x + offsets->x;
639 ptl.y = y + ySign * offsets->y;
640 // flip y coordinate
641 if ( !nativexform && p->devh )
642 ptl.y = p->devh - (ptl.y + 1);
643 GpiCharStringPosAt( ps, &ptl, NULL, options, 1, &chr, NULL );
644 x += *advances;
645 }
646 }
647
648 if ( textFlags & (Qt::Overline | Qt::Underline | Qt::StrikeOut) ) {
649 AREABUNDLE oldAb, ab;
650 const LONG aa = ABB_COLOR | ABB_SET | ABB_SYMBOL;
651 GpiQueryAttrs( ps, PRIM_AREA, aa, &oldAb );
652 ab.lColor = COLOR_VALUE( p->cpen.color() );
653 ab.usSet = LCID_DEFAULT;
654 ab.usSymbol = PATSYM_SOLID;
655 GpiSetAttrs( ps, PRIM_AREA, aa, 0, &ab );
656
657 int lw = lineThickness() - 1; // inclusive
658 int lp;
659 // flip y coordinate
660 int yp = y;
661 if ( !nativexform && p->devh )
662 yp = p->devh - (yp + 1);
663 if ( textFlags & (Qt::Underline) ) {
664 ptl.x = xo;
665 ptl.y = yp - underlinePosition();
666 GpiMove( ps, &ptl );
667 ptl.x = x - 1; ptl.y -= lw;
668 GpiBox( ps, DRO_FILL, &ptl, 0, 0 );
669 }
670 if ( textFlags & (Qt::Overline) ) {
671 lp = ascent() + 1; // corresponds to QFontMetrics::overlinePos()
672 if ( !lp ) lp = 1;
673 ptl.x = xo;
674 ptl.y = yp + lp;
675 GpiMove( ps, &ptl );
676 ptl.x = x - 1; ptl.y -= lw;
677 GpiBox( ps, DRO_FILL, &ptl, 0, 0 );
678 }
679 if ( textFlags & (Qt::StrikeOut) ) {
680 lp = ascent() / 3; // corresponds to QFontMetrics::strikeOutPos()
681 if ( !lp ) lp = 1;
682 ptl.x = xo;
683 ptl.y = yp + lp;
684 GpiMove( ps, &ptl );
685 ptl.x = x - 1; ptl.y -= lw;
686 GpiBox( ps, DRO_FILL, &ptl, 0, 0 );
687 }
688 GpiSetAttrs( ps, PRIM_AREA, aa, 0, &oldAb );
689 }
690
691 if ( nativexform ) {
692 p->clearNativeXForm();
693 }
694
695 GpiSetBackMix( ps, oldBackMix );
696}
697
698glyph_metrics_t QFontEnginePM::boundingBox( const glyph_t *glyphs,
699 const advance_t *advances, const qoffset_t *offsets, int numGlyphs )
700{
701 Q_UNUSED( glyphs );
702 Q_UNUSED( offsets );
703
704 if ( numGlyphs == 0 )
705 return glyph_metrics_t();
706
707 int w = 0;
708 const advance_t *end = advances + numGlyphs;
709 while( end > advances )
710 w += *(--end);
711
712//@@TODO (dmik): look at usage of this fn, is the return correct?
713 return glyph_metrics_t(
714 0, -pfm->lMaxAscender, w, pfm->lMaxAscender + pfm->lMaxDescender, w, 0
715 );
716}
717
718glyph_metrics_t QFontEnginePM::boundingBox( glyph_t glyph )
719{
720//@@TODO (dmik): glyphs here are just 8 bit chars (see stringToCMap()),
721// it should be changed later.
722 POINTL ptls [TXTBOX_COUNT];
723 GpiQueryTextBox( ps(), 1, (char *)&glyph, TXTBOX_COUNT, ptls );
724 int minx = 0, miny = 0, maxx = 0, maxy = 0;
725 for ( int i = 0; i < 4; i++ ) {
726 minx = QMIN( minx, ptls[i].x );
727 miny = QMIN( miny, ptls[i].y );
728 maxx = QMAX( maxx, ptls[i].x );
729 maxy = QMAX( maxy, ptls[i].y );
730 }
731 return glyph_metrics_t(
732 minx, -maxy, maxx - minx + 1, maxy - miny + 1, ptls[4].x, -ptls[4].y
733 );
734
735//@@TODO (dmik): remove
736//#ifndef Q_OS_TEMP
737// GLYPHMETRICS gm;
738//
739// if( !ttf ) {
740// SIZE s;
741// WCHAR ch = glyph;
742// BOOL res = GetTextExtentPoint32W( dc(), &ch, 1, &s );
743// Q_UNUSED( res );
744// int overhang = (qt_winver & Qt::WV_DOS_based) ? tm.a.tmOverhang : 0;
745// return glyph_metrics_t( 0, -tm.a.tmAscent, s.cx, tm.a.tmHeight, s.cx-overhang, 0 );
746// } else {
747// DWORD res = 0;
748// MAT2 mat;
749// mat.eM11.value = mat.eM22.value = 1;
750// mat.eM11.fract = mat.eM22.fract = 0;
751// mat.eM21.value = mat.eM12.value = 0;
752// mat.eM21.fract = mat.eM12.fract = 0;
753// QT_WA( {
754// res = GetGlyphOutlineW( dc(), glyph, GGO_METRICS|GGO_GLYPH_INDEX, &gm, 0, 0, &mat );
755// } , {
756// res = GetGlyphOutlineA( dc(), glyph, GGO_METRICS|GGO_GLYPH_INDEX, &gm, 0, 0, &mat );
757// } );
758// if ( res != GDI_ERROR )
759// return glyph_metrics_t( gm.gmptGlyphOrigin.x, -gm.gmptGlyphOrigin.y,
760// gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY );
761// }
762//#endif
763// return glyph_metrics_t();
764}
765
766int QFontEnginePM::ascent() const
767{
768 return pfm->lMaxAscender;
769}
770
771int QFontEnginePM::descent() const
772{
773 return pfm->lMaxDescender;
774}
775
776int QFontEnginePM::leading() const
777{
778 return pfm->lExternalLeading;
779}
780
781int QFontEnginePM::maxCharWidth() const
782{
783 return pfm->lMaxCharInc;
784}
785
786//@@TODO (dmik): remove?
787//enum { max_font_count = 256 };
788//static const ushort char_table[] = {
789// 40,
790// 67,
791// 70,
792// 75,
793// 86,
794// 88,
795// 89,
796// 91,
797// 102,
798// 114,
799// 124,
800// 127,
801// 205,
802// 645,
803// 884,
804// 922,
805// 1070,
806// 12386,
807// 0
808//};
809//
810//static const int char_table_entries = sizeof(char_table)/sizeof(ushort);
811
812
813int QFontEnginePM::minLeftBearing() const
814{
815//@@TODO (dmik): later
816 return 0;
817// if ( lbearing == SHRT_MIN )
818// minRightBearing(); // calculates both
819//
820// return lbearing;
821}
822
823int QFontEnginePM::minRightBearing() const
824{
825//@@TODO (dmik): later
826 return 0;
827//#ifdef Q_OS_TEMP
828// return 0;
829//#else
830// if ( rbearing == SHRT_MIN ) {
831// int ml = 0;
832// int mr = 0;
833// if ( ttf ) {
834// HDC hdc = dc();
835// SelectObject( hdc, hfont );
836// ABC *abc = 0;
837// int n = QT_WA_INLINE( tm.w.tmLastChar - tm.w.tmFirstChar, tm.a.tmLastChar - tm.a.tmFirstChar );
838// if ( n <= max_font_count ) {
839// abc = new ABC[n+1];
840// QT_WA( {
841// GetCharABCWidths(hdc, tm.w.tmFirstChar, tm.w.tmLastChar, abc);
842// }, {
843// GetCharABCWidthsA(hdc,tm.a.tmFirstChar,tm.a.tmLastChar,abc);
844// } );
845// } else {
846// abc = new ABC[char_table_entries+1];
847// QT_WA( {
848// for( int i = 0; i < char_table_entries; i++ )
849// GetCharABCWidths(hdc, char_table[i], char_table[i], abc+i);
850// }, {
851// for( int i = 0; i < char_table_entries; i++ ) {
852// QCString w = QString(QChar(char_table[i])).local8Bit();
853// if ( w.length() == 1 ) {
854// uint ch8 = (uchar)w[0];
855// GetCharABCWidthsA(hdc, ch8, ch8, abc+i );
856// }
857// }
858// } );
859// n = char_table_entries;
860// }
861// ml = abc[0].abcA;
862// mr = abc[0].abcC;
863// for ( int i = 1; i < n; i++ ) {
864// if ( abc[i].abcA + abc[i].abcB + abc[i].abcC != 0 ) {
865// ml = QMIN(ml,abc[i].abcA);
866// mr = QMIN(mr,abc[i].abcC);
867// }
868// }
869// delete [] abc;
870// } else {
871// QT_WA( {
872// ABCFLOAT *abc = 0;
873// int n = tm.w.tmLastChar - tm.w.tmFirstChar+1;
874// if ( n <= max_font_count ) {
875// abc = new ABCFLOAT[n];
876// GetCharABCWidthsFloat(hdc, tm.w.tmFirstChar, tm.w.tmLastChar, abc);
877// } else {
878// abc = new ABCFLOAT[char_table_entries];
879// for( int i = 0; i < char_table_entries; i++ )
880// GetCharABCWidthsFloat(hdc, char_table[i], char_table[i], abc+i);
881// n = char_table_entries;
882// }
883// float fml = abc[0].abcfA;
884// float fmr = abc[0].abcfC;
885// for (int i=1; i<n; i++) {
886// if ( abc[i].abcfA + abc[i].abcfB + abc[i].abcfC != 0 ) {
887// fml = QMIN(fml,abc[i].abcfA);
888// fmr = QMIN(fmr,abc[i].abcfC);
889// }
890// }
891// ml = int(fml-0.9999);
892// mr = int(fmr-0.9999);
893// delete [] abc;
894// } , {
895// ml = 0;
896// mr = -tm.a.tmOverhang;
897// } );
898// }
899// ((QFontEngine *)this)->lbearing = ml;
900// ((QFontEngine *)this)->rbearing = mr;
901// }
902//
903// return rbearing;
904//#endif
905}
906
907//@@TODO (dmik): remove
908//const char *QFontEngineWin::name() const
909//{
910// return 0;
911//}
912
913bool QFontEnginePM::canRender( const QChar *string, int len )
914{
915//@@TODO (dmik): later
916 return TRUE;
917
918// if ( symbol ) {
919// while( len-- ) {
920// if ( getGlyphIndex( cmap, string->unicode() ) == 0 ) {
921// if( string->unicode() < 0x100 ) {
922// if(getGlyphIndex( cmap, string->unicode()+0xf000) == 0)
923// return FALSE;
924// } else {
925// return FALSE;
926// }
927// }
928// string++;
929// }
930// } else if ( ttf ) {
931// while( len-- ) {
932// if ( getGlyphIndex( cmap, string->unicode() ) == 0 )
933// return FALSE;
934// string++;
935// }
936// } else {
937// QT_WA( {
938// while( len-- ) {
939// if ( tm.w.tmFirstChar > string[len].unicode() || tm.w.tmLastChar < string[len].unicode() )
940// return FALSE;
941// }
942// }, {
943// while( len-- ) {
944// if ( tm.a.tmFirstChar > string[len].unicode() || tm.a.tmLastChar < string[len].unicode() )
945// return FALSE;
946// }
947// } );
948// }
949// return TRUE;
950}
951
952QFontEngine::Type QFontEnginePM::type() const
953{
954 return QFontEngine::PM;
955}
956
957
958// ----------------------------------------------------------------------------
959// True type support methods
960// ----------------------------------------------------------------------------
961
962
963
964/// @todo (dmik) need this all?
965#if 0
966#define MAKE_TAG(ch1, ch2, ch3, ch4) (\
967 (((DWORD)(ch4)) << 24) | \
968 (((DWORD)(ch3)) << 16) | \
969 (((DWORD)(ch2)) << 8) | \
970 ((DWORD)(ch1)) \
971 )
972
973static inline Q_UINT32 getUInt(unsigned char *p)
974{
975 Q_UINT32 val;
976 val = *p++ << 24;
977 val |= *p++ << 16;
978 val |= *p++ << 8;
979 val |= *p;
980
981 return val;
982}
983
984static inline Q_UINT16 getUShort(unsigned char *p)
985{
986 Q_UINT16 val;
987 val = *p++ << 8;
988 val |= *p;
989
990 return val;
991}
992
993static inline void tag_to_string( char *string, Q_UINT32 tag )
994{
995 string[0] = (tag >> 24)&0xff;
996 string[1] = (tag >> 16)&0xff;
997 string[2] = (tag >> 8)&0xff;
998 string[3] = tag&0xff;
999 string[4] = 0;
1000}
1001
1002static Q_UINT16 getGlyphIndex( unsigned char *table, unsigned short unicode )
1003{
1004 unsigned short format = getUShort( table );
1005 if ( format == 0 ) {
1006 if ( unicode < 256 )
1007 return (int) *(table+6+unicode);
1008 } else if ( format == 2 ) {
1009 qWarning("format 2 encoding table for Unicode, not implemented!");
1010 } else if ( format == 4 ) {
1011 Q_UINT16 segCountX2 = getUShort( table + 6 );
1012 unsigned char *ends = table + 14;
1013 Q_UINT16 endIndex = 0;
1014 int i = 0;
1015 for ( ; i < segCountX2/2 && (endIndex = getUShort( ends + 2*i )) < unicode; i++ );
1016
1017 unsigned char *idx = ends + segCountX2 + 2 + 2*i;
1018 Q_UINT16 startIndex = getUShort( idx );
1019
1020 if ( startIndex > unicode )
1021 return 0;
1022
1023 idx += segCountX2;
1024 Q_INT16 idDelta = (Q_INT16)getUShort( idx );
1025 idx += segCountX2;
1026 Q_UINT16 idRangeoffset_t = (Q_UINT16)getUShort( idx );
1027
1028 Q_UINT16 glyphIndex;
1029 if ( idRangeoffset_t ) {
1030 Q_UINT16 id = getUShort( idRangeoffset_t + 2*(unicode - startIndex) + idx);
1031 if ( id )
1032 glyphIndex = ( idDelta + id ) % 0x10000;
1033 else
1034 glyphIndex = 0;
1035 } else {
1036 glyphIndex = (idDelta + unicode) % 0x10000;
1037 }
1038 return glyphIndex;
1039 }
1040
1041 return 0;
1042}
1043
1044
1045static unsigned char *getCMap( HDC hdc, bool &symbol )
1046{
1047 const DWORD CMAP = MAKE_TAG( 'c', 'm', 'a', 'p' );
1048
1049 unsigned char header[4];
1050
1051 // get the CMAP header and the number of encoding tables
1052 DWORD bytes =
1053#ifndef Q_OS_TEMP
1054 GetFontData( hdc, CMAP, 0, &header, 4 );
1055#else
1056 0;
1057#endif
1058 if ( bytes == GDI_ERROR )
1059 return 0;
1060 unsigned short version = getUShort( header );
1061 if ( version != 0 )
1062 return 0;
1063
1064 unsigned short numTables = getUShort( header+2);
1065 unsigned char *maps = new unsigned char[8*numTables];
1066
1067 // get the encoding table and look for Unicode
1068#ifndef Q_OS_TEMP
1069 bytes = GetFontData( hdc, CMAP, 4, maps, 8*numTables );
1070#endif
1071 if ( bytes == GDI_ERROR )
1072 return 0;
1073
1074 symbol = TRUE;
1075 unsigned int unicode_table = 0;
1076 for ( int n = 0; n < numTables; n++ ) {
1077 Q_UINT32 version = getUInt( maps + 8*n );
1078 // accept both symbol and Unicode encodings. prefer unicode.
1079 if ( version == 0x00030001 || version == 0x00030000 ) {
1080 unicode_table = getUInt( maps + 8*n + 4 );
1081 if ( version == 0x00030001 ) {
1082 symbol = FALSE;
1083 break;
1084 }
1085 }
1086 }
1087
1088 if ( !unicode_table ) {
1089 // qDebug("no unicode table found" );
1090 return 0;
1091 }
1092
1093 delete [] maps;
1094
1095 // get the header of the unicode table
1096#ifndef Q_OS_TEMP
1097 bytes = GetFontData( hdc, CMAP, unicode_table, &header, 4 );
1098#endif
1099 if ( bytes == GDI_ERROR )
1100 return 0;
1101
1102 unsigned short length = getUShort( header+2 );
1103 unsigned char *unicode_data = new unsigned char[length];
1104
1105 // get the cmap table itself
1106#ifndef Q_OS_TEMP
1107 bytes = GetFontData( hdc, CMAP, unicode_table, unicode_data, length );
1108#endif
1109 if ( bytes == GDI_ERROR ) {
1110 delete [] unicode_data;
1111 return 0;
1112 }
1113 return unicode_data;
1114}
1115#endif // if 0
1116
1117
Note: See TracBrowser for help on using the repository browser.