xref: /trunk/main/vcl/os2/source/gdi/salgdi3.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #define INCL_GRE_STRINGS
29 #define INCL_GPI
30 #define INCL_DOS
31 
32 #include <string.h>
33 #include <stdlib.h>
34 #include <math.h>
35 #include <svpm.h>
36 
37 #define _SV_SALGDI3_CXX
38 #include <tools/svwin.h>
39 #include <rtl/tencinfo.h>
40 #ifndef _OSL_FILE_HXX
41 #include <osl/file.hxx>
42 #endif
43 #ifndef _OSL_THREAD_HXX
44 #include <osl/thread.hxx>
45 #endif
46 #ifndef _OSL_PROCESS_HXX
47 #include <osl/process.h>
48 #endif
49 #include <vcl/svapp.hxx>
50 #include <saldata.hxx>
51 #include <salgdi.h>
52 #include <vcl/font.hxx>
53 #include <vcl/sallayout.hxx>
54 #include <tools/poly.hxx>
55 #include <tools/debug.hxx>
56 #include <rtl/textcvt.h>
57 #include <tools/debug.hxx>
58 #include <saldata.hxx>
59 #include <salgdi.h>
60 #ifndef _SV_OUTFONT_HXX
61 #include <vcl/outfont.hxx>
62 #endif
63 #include <sallayout.h>
64 #include <tools/poly.hxx>
65 #include <basegfx/polygon/b2dpolygon.hxx>
66 #include <basegfx/polygon/b2dpolypolygon.hxx>
67 #include <basegfx/matrix/b2dhommatrix.hxx>
68 
69 #ifndef __H_FT2LIB
70 #include <wingdi.h>
71 #include <ft2lib.h>
72 #endif
73 
74 #include "sft.hxx"
75 
76 #ifdef GCP_KERN_HACK
77 #include <algorithm>
78 #endif
79 
80 using namespace vcl;
81 
82 // -----------
83 // - Inlines -
84 // -----------
85 
86 
87 inline W32FIXED FixedFromDouble( double d )
88 {
89     const long l = (long) ( d * 65536. );
90     return *(W32FIXED*) &l;
91 }
92 
93 // -----------------------------------------------------------------------
94 
95 inline int IntTimes256FromFixed(W32FIXED f)
96 {
97     int nFixedTimes256 = (f.value << 8) + ((f.fract+0x80) >> 8);
98     return nFixedTimes256;
99 }
100 
101 // -----------
102 // - Defines -
103 // -----------
104 
105 // this is a special codepage code, used to identify OS/2 symbol font.
106 #define SYMBOL_CHARSET                  65400
107 
108 // =======================================================================
109 
110 UniString ImplSalGetUniString( const sal_Char* pStr, xub_StrLen nLen = STRING_LEN)
111 {
112     return UniString( pStr, nLen, gsl_getSystemTextEncoding(),
113                       RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
114                       RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
115                       RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT );
116 }
117 
118 // =======================================================================
119 
120 static USHORT ImplSalToCharSet( CharSet eCharSet )
121 {
122     // !!! Fuer DBCS-Systeme muss dieser Code auskommentiert werden und 0
123     // !!! zurueckgegeben werden, solange die DBCS-Charsets nicht
124     // !!! durchgereicht werden
125 
126     switch ( eCharSet )
127     {
128         case RTL_TEXTENCODING_IBM_437:
129             return 437;
130 
131         case RTL_TEXTENCODING_IBM_850:
132             return 850;
133 
134         case RTL_TEXTENCODING_IBM_860:
135             return 860;
136 
137         case RTL_TEXTENCODING_IBM_861:
138             return 861;
139 
140         case RTL_TEXTENCODING_IBM_863:
141             return 863;
142 
143         case RTL_TEXTENCODING_IBM_865:
144             return 865;
145         case RTL_TEXTENCODING_MS_1252:
146             return 1004;
147         case RTL_TEXTENCODING_SYMBOL:
148             return 65400;
149     }
150 
151     return 0;
152 }
153 
154 // -----------------------------------------------------------------------
155 
156 static CharSet ImplCharSetToSal( USHORT usCodePage )
157 {
158     switch ( usCodePage )
159     {
160         case 437:
161             return RTL_TEXTENCODING_IBM_437;
162 
163         case 850:
164             return RTL_TEXTENCODING_IBM_850;
165 
166         case 860:
167             return RTL_TEXTENCODING_IBM_860;
168 
169         case 861:
170             return RTL_TEXTENCODING_IBM_861;
171 
172         case 863:
173             return RTL_TEXTENCODING_IBM_863;
174 
175         case 865:
176             return RTL_TEXTENCODING_IBM_865;
177         case 1004:
178             return RTL_TEXTENCODING_MS_1252;
179         case 65400:
180             return RTL_TEXTENCODING_SYMBOL;
181     }
182 
183     return RTL_TEXTENCODING_DONTKNOW;
184 }
185 
186 // -----------------------------------------------------------------------
187 
188 static FontFamily ImplFamilyToSal( BYTE bFamilyType )
189 {
190     switch ( bFamilyType )
191     {
192         case 4:
193             return FAMILY_DECORATIVE;
194         case 3:
195             return FAMILY_SCRIPT;
196     }
197 
198     return FAMILY_DONTKNOW;
199 }
200 
201 // -----------------------------------------------------------------------
202 
203 static FontWeight ImplWeightToSal( USHORT nWeight )
204 {
205     // Falls sich jemand an die alte Doku gehalten hat
206     if ( nWeight > 999 )
207         nWeight /= 1000;
208 
209     switch ( nWeight )
210     {
211         case 1:
212             return WEIGHT_THIN;
213 
214         case 2:
215             return WEIGHT_ULTRALIGHT;
216 
217         case 3:
218             return WEIGHT_LIGHT;
219 
220         case 4:
221             return WEIGHT_SEMILIGHT;
222 
223         case 5:
224             return WEIGHT_NORMAL;
225 
226         case 6:
227             return WEIGHT_SEMIBOLD;
228 
229         case 7:
230             return WEIGHT_BOLD;
231 
232         case 8:
233             return WEIGHT_ULTRABOLD;
234 
235         case 9:
236             return WEIGHT_BLACK;
237     }
238 
239     return WEIGHT_DONTKNOW;
240 }
241 
242 // -----------------------------------------------------------------------
243 
244 static UniString ImpStyleNameToSal( const char* pFamilyName,
245                                    const char* pFaceName,
246                                    USHORT nLen )
247 {
248     if ( !nLen )
249         nLen = strlen(pFamilyName);
250 
251     // strip FamilyName from FaceName
252     if ( strncmp( pFamilyName, pFaceName, nLen ) == 0 )
253     {
254         USHORT nFaceLen = (USHORT)strlen( pFaceName+nLen );
255         // Ist Facename laenger, schneiden wir den FamilyName ab
256         if ( nFaceLen > 1 )
257             return UniString( pFaceName+(nLen+1), gsl_getSystemTextEncoding());
258         else
259             return UniString();
260     }
261     else
262         return UniString( pFaceName, gsl_getSystemTextEncoding());
263 }
264 
265 // -----------------------------------------------------------------------
266 
267 inline FontPitch ImplLogPitchToSal( BYTE fsType )
268 {
269     if ( fsType & FM_TYPE_FIXED )
270         return PITCH_FIXED;
271     else
272         return PITCH_VARIABLE;
273 }
274 
275 // -----------------------------------------------------------------------
276 
277 inline BYTE ImplPitchToWin( FontPitch ePitch )
278 {
279     if ( ePitch == PITCH_FIXED )
280         return FM_TYPE_FIXED;
281     //else if ( ePitch == PITCH_VARIABLE )
282 
283     return 0;
284 }
285 
286 // -----------------------------------------------------------------------
287 
288 static ImplDevFontAttributes Os2Font2DevFontAttributes( const PFONTMETRICS pFontMetric)
289 {
290     ImplDevFontAttributes aDFA;
291 
292     // get font face attributes
293     aDFA.meFamily       = ImplFamilyToSal( pFontMetric->panose.bFamilyType);
294     aDFA.meWidthType    = WIDTH_DONTKNOW;
295     aDFA.meWeight       = ImplWeightToSal( pFontMetric->usWeightClass);
296     aDFA.meItalic       = (pFontMetric->fsSelection & FM_SEL_ITALIC) ? ITALIC_NORMAL : ITALIC_NONE;
297     aDFA.mePitch        = ImplLogPitchToSal( pFontMetric->fsType );
298     aDFA.mbSymbolFlag   = (pFontMetric->usCodePage == SYMBOL_CHARSET);
299 
300     // get the font face name
301     // the maName field stores the font name without the style, so under OS/2
302     // we must use the family name
303     aDFA.maName = UniString( pFontMetric->szFamilyname, gsl_getSystemTextEncoding());
304 
305     aDFA.maStyleName = ImpStyleNameToSal( pFontMetric->szFamilyname,
306                                           pFontMetric->szFacename,
307                                           strlen( pFontMetric->szFamilyname) );
308 
309     // get device specific font attributes
310     aDFA.mbOrientation  = (pFontMetric->fsDefn & FM_DEFN_OUTLINE) != 0;
311     aDFA.mbDevice       = (pFontMetric->fsDefn & FM_DEFN_GENERIC) ? FALSE : TRUE;
312 
313     aDFA.mbEmbeddable   = false;
314     aDFA.mbSubsettable  = false;
315     DWORD fontType = Ft2QueryFontType( 0, pFontMetric->szFamilyname);
316     if( fontType == FT2_FONTTYPE_TRUETYPE && !aDFA.mbDevice)
317         aDFA.mbSubsettable = true;
318     // for now we can only embed Type1 fonts
319     if( fontType == FT2_FONTTYPE_TYPE1 )
320         aDFA.mbEmbeddable = true;
321 
322     // heuristics for font quality
323     // -   standard-type1 > opentypeTT > truetype > non-standard-type1 > raster
324     // -   subsetting > embedding > none
325     aDFA.mnQuality = 0;
326     if( fontType == FT2_FONTTYPE_TRUETYPE )
327         aDFA.mnQuality += 50;
328     if( aDFA.mbSubsettable )
329         aDFA.mnQuality += 200;
330     else if( aDFA.mbEmbeddable )
331         aDFA.mnQuality += 100;
332 
333     // #i38665# prefer Type1 versions of the standard postscript fonts
334     if( aDFA.mbEmbeddable )
335     {
336         if( aDFA.maName.EqualsAscii( "AvantGarde" )
337         ||  aDFA.maName.EqualsAscii( "Bookman" )
338         ||  aDFA.maName.EqualsAscii( "Courier" )
339         ||  aDFA.maName.EqualsAscii( "Helvetica" )
340         ||  aDFA.maName.EqualsAscii( "NewCenturySchlbk" )
341         ||  aDFA.maName.EqualsAscii( "Palatino" )
342         ||  aDFA.maName.EqualsAscii( "Symbol" )
343         ||  aDFA.maName.EqualsAscii( "Times" )
344         ||  aDFA.maName.EqualsAscii( "ZapfChancery" )
345         ||  aDFA.maName.EqualsAscii( "ZapfDingbats" ) )
346             aDFA.mnQuality += 500;
347     }
348 
349     aDFA.meEmbeddedBitmap = EMBEDDEDBITMAP_DONTKNOW;
350     aDFA.meAntiAlias = ANTIALIAS_DONTKNOW;
351 
352     // TODO: add alias names
353 
354     return aDFA;
355 }
356 
357 // =======================================================================
358 
359 // -----------------------------------------------------------------------
360 
361 // =======================================================================
362 
363 ImplOs2FontData::ImplOs2FontData( PFONTMETRICS _pFontMetric,
364     int nHeight, BYTE nPitchAndFamily )
365 :   ImplFontData( Os2Font2DevFontAttributes(_pFontMetric), 0 ),
366     pFontMetric( _pFontMetric ),
367     meOs2CharSet( _pFontMetric->usCodePage),
368     mnPitchAndFamily( nPitchAndFamily ),
369     mpFontCharSets( NULL ),
370     mpUnicodeMap( NULL ),
371     mbDisableGlyphApi( false ),
372     mbHasKoreanRange( false ),
373     mbHasCJKSupport( false ),
374     mbAliasSymbolsLow( false ),
375     mbAliasSymbolsHigh( false ),
376     mnId( 0 )
377 {
378     SetBitmapSize( 0, nHeight );
379 }
380 
381 // -----------------------------------------------------------------------
382 
383 ImplOs2FontData::~ImplOs2FontData()
384 {
385     delete[] mpFontCharSets;
386 
387     if( mpUnicodeMap )
388         mpUnicodeMap->DeReference();
389 }
390 
391 // -----------------------------------------------------------------------
392 
393 sal_IntPtr ImplOs2FontData::GetFontId() const
394 {
395     return mnId;
396 }
397 
398 // -----------------------------------------------------------------------
399 
400 void ImplOs2FontData::UpdateFromHPS( HPS hPS ) const
401 {
402     // short circuit if already initialized
403     if( mpUnicodeMap != NULL )
404         return;
405 
406     ReadCmapTable( hPS );
407     ReadOs2Table( hPS );
408 
409     // even if the font works some fonts have problems with the glyph API
410     // => the heuristic below tries to figure out which fonts have the problem
411     DWORD   fontType = Ft2QueryFontType( 0, pFontMetric->szFacename);
412     if( fontType != FT2_FONTTYPE_TRUETYPE
413         && (pFontMetric->fsDefn & FM_DEFN_GENERIC) == 0)
414         mbDisableGlyphApi = true;
415 }
416 
417 // -----------------------------------------------------------------------
418 
419 #ifdef GNG_VERT_HACK
420 bool ImplOs2FontData::HasGSUBstitutions( HPS hPS ) const
421 {
422     if( !mbGsubRead )
423         ReadGsubTable( hPS );
424     return !maGsubTable.empty();
425 }
426 
427 // -----------------------------------------------------------------------
428 
429 bool ImplOs2FontData::IsGSUBstituted( sal_Ucs cChar ) const
430 {
431     return( maGsubTable.find( cChar ) != maGsubTable.end() );
432 }
433 #endif // GNG_VERT_HACK
434 
435 // -----------------------------------------------------------------------
436 
437 const ImplFontCharMap* ImplOs2FontData::GetImplFontCharMap() const
438 {
439     return mpUnicodeMap;
440 }
441 
442 // -----------------------------------------------------------------------
443 
444 static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);}
445 static unsigned GetUShort( const unsigned char* p ){ return((p[0]<<8)+p[1]);}
446 static signed GetSShort( const unsigned char* p ){ return((short)((p[0]<<8)+p[1]));}
447 static inline DWORD CalcTag( const char p[4]) { return (p[0]+(p[1]<<8)+(p[2]<<16)+(p[3]<<24)); }
448 
449 void ImplOs2FontData::ReadOs2Table( HPS hPS ) const
450 {
451     const DWORD Os2Tag = CalcTag( "OS/2" );
452     DWORD nLength = Ft2GetFontData( hPS, Os2Tag, 0, NULL, 0 );
453     if( (nLength == FT2_ERROR) || !nLength )
454         return;
455     std::vector<unsigned char> aOS2map( nLength );
456     unsigned char* pOS2map = &aOS2map[0];
457     DWORD nRC = Ft2GetFontData( hPS, Os2Tag, 0, pOS2map, nLength );
458     sal_uInt32 nVersion = GetUShort( pOS2map );
459     if ( nVersion >= 0x0001 && nLength >= 58 )
460     {
461         // We need at least version 0x0001 (TrueType rev 1.66)
462         // to have access to the needed struct members.
463         sal_uInt32 ulUnicodeRange1 = GetUInt( pOS2map + 42 );
464         sal_uInt32 ulUnicodeRange2 = GetUInt( pOS2map + 46 );
465         sal_uInt32 ulUnicodeRange3 = GetUInt( pOS2map + 50 );
466         sal_uInt32 ulUnicodeRange4 = GetUInt( pOS2map + 54 );
467 
468         // Check for CJK capabilities of the current font
469         mbHasCJKSupport = (ulUnicodeRange2 & 0x2fff0000)
470                         | (ulUnicodeRange3 & 0x00000001);
471         mbHasKoreanRange= (ulUnicodeRange1 & 0x10000000)
472                         | (ulUnicodeRange2 & 0x01100000);
473     }
474 }
475 
476 
477 // -----------------------------------------------------------------------
478 
479 #ifdef GNG_VERT_HACK
480 void ImplOs2FontData::ReadGsubTable( HPS hPS ) const
481 {
482     mbGsubRead = true;
483 
484     // check the existence of a GSUB table
485     const DWORD GsubTag = CalcTag( "GSUB" );
486     DWORD nRC = Ft2GetFontData( hPS, GsubTag, 0, NULL, 0 );
487     if( (nRC == FT2_ERROR) || !nRC )
488         return;
489 
490     // TODO: directly read the GSUB table instead of going through sft
491 
492     // get raw font file data
493     DWORD nFontSize = Ft2GetFontData( hPS, 0, 0, NULL, 0 );
494     if( nFontSize == FT2_ERROR )
495         return;
496     std::vector<char> aRawFont( nFontSize+1 );
497     aRawFont[ nFontSize ] = 0;
498     DWORD nFontSize2 = Ft2GetFontData( hPS, 0, 0, (void*)&aRawFont[0], nFontSize );
499     if( nFontSize != nFontSize2 )
500         return;
501 
502     // open font file
503     sal_uInt32 nFaceNum = 0;
504     if( !aRawFont[0] )  // TTC candidate
505         nFaceNum = ~0U;  // indicate "TTC font extracts only"
506 
507     TrueTypeFont* pTTFont = NULL;
508     ::OpenTTFontBuffer( &aRawFont[0], nFontSize, nFaceNum, &pTTFont );
509     if( !pTTFont )
510         return;
511 
512     // add vertically substituted characters to list
513     static const sal_Unicode aGSUBCandidates[] = {
514         0x0020, 0x0080, // ASCII
515         0x2000, 0x2600, // misc
516         0x3000, 0x3100, // CJK punctutation
517         0x3300, 0x3400, // squared words
518         0xFF00, 0xFFF0, // halfwidth|fullwidth forms
519     0 };
520 
521     for( const sal_Unicode* pPair = aGSUBCandidates; *pPair; pPair += 2 )
522         for( sal_Unicode cChar = pPair[0]; cChar < pPair[1]; ++cChar )
523             if( ::MapChar( pTTFont, cChar, 0 ) != ::MapChar( pTTFont, cChar, 1 ) )
524                 maGsubTable.insert( cChar ); // insert GSUBbed unicodes
525 
526     CloseTTFont( pTTFont );
527 
528 #if 0
529     TrueTypeFont* pTTFont = NULL;
530     ::OpenTTFont( &aRawFont[0], nFontSize, nFaceNum, &pTTFont );
531     if( !pTTFont )
532         return;
533 
534     // add vertically substituted characters to list
535     static const sal_Unicode aGSUBCandidates[] = {
536         0x0020, 0x0080, // ASCII
537         0x2000, 0x2600, // misc
538         0x3000, 0x3100, // CJK punctutation
539         0x3300, 0x3400, // squared words
540         0xFF00, 0xFFF0, // halfwidth|fullwidth forms
541     0 };
542 
543     for( const sal_Unicode* pPair = aGSUBCandidates; *pPair; pPair += 2 )
544         for( sal_Unicode cChar = pPair[0]; cChar < pPair[1]; ++cChar )
545             if( ::MapChar( pTTFont, cChar, 0 ) != ::MapChar( pTTFont, cChar, 1 ) )
546                 maGsubTable.insert( cChar ); // insert GSUBbed unicodes
547 
548     CloseTTFont( pTTFont );
549 #endif
550 }
551 #endif // GNG_VERT_HACK
552 
553 // -----------------------------------------------------------------------
554 
555 void ImplOs2FontData::ReadCmapTable( HPS hPS ) const
556 {
557     CmapResult aResult;
558     aResult.mnPairCount = 0;
559     aResult.mbSymbolic  = (meOs2CharSet == SYMBOL_CHARSET);
560     aResult.mbRecoded   = true;
561 
562     // get the CMAP table from the font which is selected into the DC
563     const DWORD CmapTag = CalcTag( "cmap" );
564     DWORD nRC = Ft2GetFontData( hPS, CmapTag, 0, NULL, 0 );
565     // read the CMAP table if available
566     if( nRC != FT2_ERROR )
567     {
568         const int nLength = nRC;
569         std::vector<unsigned char> aCmap( nLength );
570         unsigned char* pCmap = &aCmap[0];
571         nRC = Ft2GetFontData( hPS, CmapTag, 0, pCmap, nLength );
572         // parse the CMAP table
573         if( nRC == nLength )
574             ParseCMAP( pCmap, nLength, aResult );
575     } else {
576         // we need to define at least a simple charmap, otherwise this font
577         // will be mapped to default charmap, and OOo doesn't accept the
578         // system font to match the default charmap
579         aResult.mnPairCount = 1;
580         // ImplFontCharMap destructor will free this memory
581         aResult.mpPairCodes = new sal_uInt32[ 2 * aResult.mnPairCount ];
582         aResult.mpPairCodes[0] = 0x0020;
583         aResult.mpPairCodes[1] = 0x00FF;
584         aResult.mpStartGlyphs = NULL;
585     }
586 
587     mbDisableGlyphApi |= aResult.mbRecoded;
588 
589     if( aResult.mnPairCount > 0 )
590         mpUnicodeMap = new ImplFontCharMap( aResult.mnPairCount,
591             aResult.mpPairCodes, aResult.mpStartGlyphs );
592     else
593         mpUnicodeMap = ImplFontCharMap::GetDefaultMap();
594     mpUnicodeMap->AddReference();
595 }
596 
597 // =======================================================================
598 
599 void Os2SalGraphics::SetTextColor( SalColor nSalColor )
600 {
601     CHARBUNDLE cb;
602 
603     cb.lColor = RGBCOLOR( SALCOLOR_RED( nSalColor ),
604                           SALCOLOR_GREEN( nSalColor ),
605                           SALCOLOR_BLUE( nSalColor ) );
606 
607     // set default color attributes
608     Ft2SetAttrs( mhPS,
609                  PRIM_CHAR,
610                  CBB_COLOR,
611                  0,
612                  &cb );
613 }
614 
615 // -----------------------------------------------------------------------
616 
617 USHORT Os2SalGraphics::ImplDoSetFont( ImplFontSelectData* i_pFont, float& o_rFontScale, int nFallbackLevel)
618 {
619 
620 #if OSL_DEBUG_LEVEL>10
621     debug_printf( "Os2SalGraphics::ImplDoSetFont\n");
622 #endif
623 
624     ImplOs2FontData* pFontData = (ImplOs2FontData*)i_pFont->mpFontData;
625     PFONTMETRICS    pFontMetric = NULL;
626     FATTRS          aFAttrs;
627     BOOL            bOutline = FALSE;
628     APIRET          rc;
629 
630     memset( &aFAttrs, 0, sizeof( FATTRS ) );
631     aFAttrs.usRecordLength = sizeof( FATTRS );
632 
633     aFAttrs.lMaxBaselineExt = i_pFont->mnHeight;
634     aFAttrs.lAveCharWidth   = i_pFont->mnWidth;
635 
636     // do we have a pointer to the FONTMETRICS of the selected font? -> use it!
637     if ( pFontData )
638     {
639         pFontMetric = pFontData->GetFontMetrics();
640 
641         bOutline = (pFontMetric->fsDefn & FM_DEFN_OUTLINE) != 0;
642 
643         // use match&registry fields to get correct match
644         aFAttrs.lMatch          = pFontMetric->lMatch;
645         aFAttrs.idRegistry      = pFontMetric->idRegistry;
646         aFAttrs.usCodePage      = pFontMetric->usCodePage;
647 
648         if ( bOutline )
649         {
650             aFAttrs.fsFontUse |= FATTR_FONTUSE_OUTLINE;
651             if ( i_pFont->mnOrientation )
652                 aFAttrs.fsFontUse |= FATTR_FONTUSE_TRANSFORMABLE;
653         }
654         else
655         {
656             aFAttrs.lMaxBaselineExt = pFontMetric->lMaxBaselineExt;
657             aFAttrs.lAveCharWidth   = pFontMetric->lAveCharWidth;
658         }
659 
660     }
661 
662     // use family name for outline fonts
663     if ( mbPrinter ) {
664         // use font face name for printers because otherwise ft2lib will fail
665         // to select the correct font for GPI (ticket#117)
666         strncpy( (char*)(aFAttrs.szFacename), pFontMetric->szFacename, sizeof( aFAttrs.szFacename ) );
667     } else if ( !pFontMetric) {
668         // use OOo name if fontmetrics not available!
669         ByteString aName( i_pFont->maName.GetToken( 0 ), gsl_getSystemTextEncoding());
670         strncpy( (char*)(aFAttrs.szFacename), aName.GetBuffer(), sizeof( aFAttrs.szFacename ) );
671     } else if ( bOutline) {
672         // use fontmetric family name for outline fonts
673         strncpy( (char*)(aFAttrs.szFacename), pFontMetric->szFamilyname, sizeof( aFAttrs.szFacename ) );
674     } else {
675         // use real font face name for bitmaps (WarpSans only)
676         strncpy( (char*)(aFAttrs.szFacename), pFontMetric->szFacename, sizeof( aFAttrs.szFacename ) );
677     }
678 
679     if ( i_pFont->meItalic != ITALIC_NONE )
680         aFAttrs.fsSelection |= FATTR_SEL_ITALIC;
681     if ( i_pFont->meWeight > WEIGHT_MEDIUM )
682         aFAttrs.fsSelection |= FATTR_SEL_BOLD;
683 
684 #if OSL_DEBUG_LEVEL>1
685     if (pFontMetric->szFacename[0] == 'A') {
686         debug_printf( "Os2SalGraphics::SetFont hps %x lMatch '%d'\n", mhPS, pFontMetric->lMatch);
687         debug_printf( "Os2SalGraphics::SetFont hps %x fontmetrics facename '%s'\n", mhPS, pFontMetric->szFacename);
688         debug_printf( "Os2SalGraphics::SetFont hps %x fattrs facename '%s'\n", mhPS, aFAttrs.szFacename);
689     }
690 #endif
691 
692     Ft2DeleteSetId( mhPS, nFallbackLevel + LCID_BASE);
693     if ( (rc=Ft2CreateLogFont( mhPS, NULL, nFallbackLevel + LCID_BASE, &aFAttrs)) == GPI_ERROR ) {
694 #if OSL_DEBUG_LEVEL>1
695         ERRORID nLastError = WinGetLastError( GetSalData()->mhAB );
696         debug_printf( "Os2SalGraphics::SetFont hps %x Ft2CreateLogFont failed err %x\n", mhPS, nLastError );
697 #endif
698         return SAL_SETFONT_REMOVEANDMATCHNEW;
699     }
700 
701     CHARBUNDLE aBundle;
702 
703     ULONG nAttrsDefault = 0;
704     ULONG nAttrs = CBB_SET;
705     aBundle.usSet = nFallbackLevel + LCID_BASE;
706 
707     if ( bOutline )
708     {
709         nAttrs |= CBB_BOX;
710         aBundle.sizfxCell.cy = MAKEFIXED( i_pFont->mnHeight, 0 );
711 
712         if ( !i_pFont->mnWidth )
713         {
714             LONG nXFontRes;
715             LONG nYFontRes;
716             LONG nHeight;
717 
718             // Auf die Aufloesung achten, damit das Ergebnis auch auf
719             // Drucken mit 180*360 DPI stimmt. Ausserdem muss gerundet
720             // werden, da auf meinem OS2 beispielsweise als
721             // Bildschirmaufloesung 3618*3622 PixelPerMeter zurueck-
722             // gegeben wird
723             GetResolution( nXFontRes, nYFontRes );
724             nHeight = i_pFont->mnHeight;
725             nHeight *= nXFontRes;
726             nHeight += nYFontRes/2;
727             nHeight /= nYFontRes;
728             aBundle.sizfxCell.cx = MAKEFIXED( nHeight, 0 );
729         }
730         else
731             aBundle.sizfxCell.cx = MAKEFIXED( i_pFont->mnWidth, 0 );
732     }
733 
734     // set orientation for outlinefonts
735     if ( i_pFont->mnOrientation )
736     {
737         if ( bOutline )
738         {
739             nAttrs |= CBB_ANGLE;
740             double alpha = (double)(i_pFont->mnOrientation);
741             alpha *= 0.0017453292;   // *PI / 1800
742             mnOrientationY = (long) (1000.0 * sin( alpha ));
743             mnOrientationX = (long) (1000.0 * cos( alpha ));
744             aBundle.ptlAngle.x = mnOrientationX;
745             aBundle.ptlAngle.y = mnOrientationY;
746         }
747         else
748         {
749             mnOrientationX = 1;
750             mnOrientationY = 0;
751             nAttrs |= CBB_ANGLE;
752             aBundle.ptlAngle.x = 1;
753             aBundle.ptlAngle.y = 0;
754         }
755     }
756     else
757     {
758         mnOrientationX = 1;
759         mnOrientationY = 0;
760         nAttrs |= CBB_ANGLE;
761         aBundle.ptlAngle.x = 1;
762         aBundle.ptlAngle.y = 0;
763     }
764 
765     rc = Ft2SetAttrs( mhPS, PRIM_CHAR, nAttrs, nAttrsDefault, &aBundle );
766 
767 #if OSL_DEBUG_LEVEL>1
768     FONTMETRICS aOS2Metric = {0};
769     Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric );
770 #endif
771 
772     return 0;
773 }
774 
775 
776 USHORT Os2SalGraphics::SetFont( ImplFontSelectData* pFont, int nFallbackLevel )
777 {
778 
779     // return early if there is no new font
780     if( !pFont )
781     {
782 #if 0
783         // deselect still active font
784         if( mhDefFont )
785             Ft2SetCharSet( mhPS, mhDefFont );
786         // release no longer referenced font handles
787         for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
788         {
789             if( mhFonts[i] )
790                 Ft2DeleteSetId( mhPS, mhFonts[i] );
791             mhFonts[ i ] = 0;
792         }
793 #endif
794         mhDefFont = 0;
795         return 0;
796     }
797 
798 #if OSL_DEBUG_LEVEL>10
799     debug_printf( "Os2SalGraphics::SetFont\n");
800 #endif
801 
802     DBG_ASSERT( pFont->mpFontData, "WinSalGraphics mpFontData==NULL");
803     mpOs2FontEntry[ nFallbackLevel ] = reinterpret_cast<ImplOs2FontEntry*>( pFont->mpFontEntry );
804     mpOs2FontData[ nFallbackLevel ] = static_cast<const ImplOs2FontData*>( pFont->mpFontData );
805 
806     ImplDoSetFont( pFont, mfFontScale, nFallbackLevel);
807 
808     if( !mhDefFont )
809     {
810         // keep default font
811         mhDefFont = nFallbackLevel + LCID_BASE;
812     }
813     else
814     {
815         // release no longer referenced font handles
816         for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
817         {
818             if( mhFonts[i] )
819             {
820 #if 0
821                 Ft2DeleteSetId( mhPS, mhFonts[i] );
822 #endif
823                 mhFonts[i] = 0;
824             }
825         }
826     }
827 
828     // store new font in correct layer
829     mhFonts[ nFallbackLevel ] = nFallbackLevel + LCID_BASE;
830 
831     // now the font is live => update font face
832     if( mpOs2FontData[ nFallbackLevel ] )
833         mpOs2FontData[ nFallbackLevel ]->UpdateFromHPS( mhPS );
834 
835     if( !nFallbackLevel )
836     {
837         mbFontKernInit = TRUE;
838         if ( mpFontKernPairs )
839         {
840             delete[] mpFontKernPairs;
841             mpFontKernPairs = NULL;
842         }
843         mnFontKernPairCount = 0;
844     }
845 
846     // some printers have higher internal resolution, so their
847     // text output would be different from what we calculated
848     // => suggest DrawTextArray to workaround this problem
849     if ( mbPrinter )
850         return SAL_SETFONT_USEDRAWTEXTARRAY;
851     else
852         return 0;
853 }
854 
855 // -----------------------------------------------------------------------
856 
857 void Os2SalGraphics::GetFontMetric( ImplFontMetricData* pMetric )
858 {
859     FONTMETRICS aOS2Metric;
860     Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric );
861 
862 #if OSL_DEBUG_LEVEL>1
863     debug_printf( "Os2SalGraphics::GetFontMetric hps %x\n", mhPS);
864     if (aOS2Metric.szFacename[0] == 'A') {
865         debug_printf( "Os2SalGraphics::GetFontMetric hps %x fontmetrics facename '%s'\n", mhPS, aOS2Metric.szFacename);
866         debug_printf( "Os2SalGraphics::GetFontMetric hps %x fontmetrics lMatch '%d'\n", mhPS, aOS2Metric.lMatch);
867     }
868 #endif
869 
870     pMetric->maName             = UniString( aOS2Metric.szFamilyname, gsl_getSystemTextEncoding());
871     pMetric->maStyleName        = ImpStyleNameToSal( aOS2Metric.szFamilyname,
872                                                      aOS2Metric.szFacename,
873                                                      strlen( aOS2Metric.szFamilyname ) );
874 
875     // device independent font attributes
876     pMetric->meFamily       = ImplFamilyToSal( aOS2Metric.panose.bFamilyType);
877     pMetric->mbSymbolFlag   = (aOS2Metric.usCodePage == SYMBOL_CHARSET);
878     pMetric->meWeight       = ImplWeightToSal( aOS2Metric.usWeightClass );
879     pMetric->mePitch        = ImplLogPitchToSal( aOS2Metric.fsType );
880     pMetric->meItalic       = (aOS2Metric.fsSelection & FM_SEL_ITALIC) ? ITALIC_NORMAL : ITALIC_NONE;
881     pMetric->mnSlant        = 0;
882 
883     // device dependend font attributes
884     pMetric->mbDevice       = (aOS2Metric.fsDefn & FM_DEFN_GENERIC) ? FALSE : TRUE;
885     pMetric->mbScalableFont = (aOS2Metric.fsDefn & FM_DEFN_OUTLINE) ? true : false;
886     if( pMetric->mbScalableFont )
887     {
888         // check if there are kern pairs
889         // TODO: does this work with GPOS kerning?
890         pMetric->mbKernableFont = (aOS2Metric.sKerningPairs > 0);
891     }
892     else
893     {
894         // bitmap fonts cannot be rotated directly
895         pMetric->mnOrientation  = 0;
896         // bitmap fonts have no kerning
897         pMetric->mbKernableFont = false;
898     }
899 
900     // transformation dependend font metrics
901     if ( aOS2Metric.fsDefn & FM_DEFN_OUTLINE )
902     {
903         pMetric->mnWidth       = aOS2Metric.lEmInc;
904     }
905     else
906     {
907         pMetric->mnWidth       = aOS2Metric.lAveCharWidth;
908         pMetric->mnOrientation = 0;
909     }
910     pMetric->mnIntLeading       = aOS2Metric.lInternalLeading;
911     pMetric->mnExtLeading       = aOS2Metric.lExternalLeading;
912     pMetric->mnAscent           = aOS2Metric.lMaxAscender;
913     pMetric->mnDescent          = aOS2Metric.lMaxDescender;
914 
915     // #107888# improved metric compatibility for Asian fonts...
916     // TODO: assess workaround below for CWS >= extleading
917     // TODO: evaluate use of aWinMetric.sTypo* members for CJK
918     if( mpOs2FontData[0] && mpOs2FontData[0]->SupportsCJK() )
919     {
920         pMetric->mnIntLeading += pMetric->mnExtLeading;
921 
922         // #109280# The line height for Asian fonts is too small.
923         // Therefore we add half of the external leading to the
924         // ascent, the other half is added to the descent.
925         const long nHalfTmpExtLeading = pMetric->mnExtLeading / 2;
926         const long nOtherHalfTmpExtLeading = pMetric->mnExtLeading - nHalfTmpExtLeading;
927 
928         // #110641# external leading for Asian fonts.
929         // The factor 0.3 has been confirmed with experiments.
930         long nCJKExtLeading = static_cast<long>(0.30 * (pMetric->mnAscent + pMetric->mnDescent));
931         nCJKExtLeading -= pMetric->mnExtLeading;
932         pMetric->mnExtLeading = (nCJKExtLeading > 0) ? nCJKExtLeading : 0;
933 
934         pMetric->mnAscent   += nHalfTmpExtLeading;
935         pMetric->mnDescent  += nOtherHalfTmpExtLeading;
936 
937         // #109280# HACK korean only: increase descent for wavelines and impr
938         // YD win9x only
939     }
940 
941 }
942 
943 // -----------------------------------------------------------------------
944 
945 ULONG Os2SalGraphics::GetKernPairs( ULONG nPairs, ImplKernPairData* pKernPairs )
946 {
947     DBG_ASSERT( sizeof( KERNINGPAIRS ) == sizeof( ImplKernPairData ),
948                 "Os2SalGraphics::GetKernPairs(): KERNINGPAIRS != ImplKernPairData" );
949 
950     if ( mbFontKernInit )
951     {
952         if( mpFontKernPairs )
953         {
954             delete[] mpFontKernPairs;
955             mpFontKernPairs = NULL;
956         }
957         mnFontKernPairCount = 0;
958 
959         {
960             KERNINGPAIRS* pPairs = NULL;
961             FONTMETRICS aOS2Metric;
962             Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric );
963             int nCount = aOS2Metric.sKerningPairs;
964             if( nCount )
965             {
966 #ifdef GCP_KERN_HACK
967                 pPairs = new KERNINGPAIRS[ nCount+1 ];
968                 mpFontKernPairs = pPairs;
969                 mnFontKernPairCount = nCount;
970                 Ft2QueryKerningPairs( mhPS, nCount, (KERNINGPAIRS*)pPairs );
971 #else // GCP_KERN_HACK
972                 pPairs = (KERNINGPAIRS*)pKernPairs;
973                 nCount = (nCount < nPairs) ? nCount : nPairs;
974                 Ft2QueryKerningPairs( mhPS, nCount, (KERNINGPAIRS*)pPairs );
975                 return nCount;
976 #endif // GCP_KERN_HACK
977             }
978         }
979 
980         mbFontKernInit = FALSE;
981 
982         std::sort( mpFontKernPairs, mpFontKernPairs + mnFontKernPairCount, ImplCmpKernData );
983     }
984 
985     if( !pKernPairs )
986         return mnFontKernPairCount;
987     else if( mpFontKernPairs )
988     {
989         if ( nPairs < mnFontKernPairCount )
990             nPairs = mnFontKernPairCount;
991         memcpy( pKernPairs, mpFontKernPairs,
992                 nPairs*sizeof( ImplKernPairData ) );
993         return nPairs;
994     }
995 
996     return 0;
997 }
998 
999 
1000 // -----------------------------------------------------------------------
1001 
1002 static const ImplFontCharMap* pOs2DefaultImplFontCharMap = NULL;
1003 static const sal_uInt32 pOs2DefaultRangeCodes[] = {0x0020,0x00FF};
1004 
1005 const ImplFontCharMap* Os2SalGraphics::GetImplFontCharMap() const
1006 {
1007     if( !mpOs2FontData[0] )
1008         return ImplFontCharMap::GetDefaultMap();
1009     return mpOs2FontData[0]->GetImplFontCharMap();
1010 }
1011 
1012 // -----------------------------------------------------------------------
1013 
1014 bool Os2SalGraphics::AddTempDevFont( ImplDevFontList* pFontList,
1015     const String& rFontFileURL, const String& rFontName )
1016 {
1017 #if OSL_DEBUG_LEVEL>0
1018     debug_printf("Os2SalGraphics::AddTempDevFont\n");
1019 #endif
1020     return false;
1021 }
1022 
1023 // -----------------------------------------------------------------------
1024 
1025 void Os2SalGraphics::GetDevFontList( ImplDevFontList* pList )
1026 {
1027     PFONTMETRICS    pFontMetrics;
1028     ULONG           nFontMetricCount;
1029     SalData*        pSalData;
1030 
1031 #if OSL_DEBUG_LEVEL>0
1032     debug_printf("Os2SalGraphics::GetDevFontList\n");
1033 #endif
1034 
1035     // install OpenSymbol
1036     HMODULE hMod;
1037     ULONG   ObjNum, Offset, rc;
1038     CHAR    Buff[2*_MAX_PATH];
1039     char    drive[_MAX_DRIVE], dir[_MAX_DIR];
1040     char    fname[_MAX_FNAME], ext[_MAX_EXT];
1041     // get module handle (and name)
1042     rc = DosQueryModFromEIP( &hMod, &ObjNum, sizeof( Buff), Buff,
1043                             &Offset, (ULONG)ImplSalGetUniString);
1044     DosQueryModuleName(hMod, sizeof(Buff), Buff);
1045     // replace module path with font path
1046     char* slash = strrchr( Buff, '\\');
1047     *slash = '\0';
1048     slash = strrchr( Buff, '\\');
1049     *slash = '\0';
1050     strcat( Buff, "\\SHARE\\FONTS\\TRUETYPE\\OPENS___.TTF");
1051     rc = GpiLoadPublicFonts( GetSalData()->mhAB, Buff);
1052 
1053     if ( !mbPrinter )
1054     {
1055         // Bei Bildschirm-Devices cachen wir die Liste global, da
1056         // dies im unabhaengigen Teil auch so gemacht wird und wir
1057         // ansonsten auf geloeschten Systemdaten arbeiten koennten
1058         pSalData = GetSalData();
1059         nFontMetricCount    = pSalData->mnFontMetricCount;
1060         pFontMetrics        = pSalData->mpFontMetrics;
1061         // Bei Bildschirm-Devices holen wir uns die Fontliste jedesmal neu
1062         if ( pFontMetrics )
1063         {
1064             delete pFontMetrics;
1065             pFontMetrics        = NULL;
1066             nFontMetricCount    = 0;
1067         }
1068     }
1069     else
1070     {
1071         nFontMetricCount    = mnFontMetricCount;
1072         pFontMetrics        = mpFontMetrics;
1073     }
1074 
1075     // do we have to create the cached font list first?
1076     if ( !pFontMetrics )
1077     {
1078         // query the number of fonts available
1079         LONG nTemp = 0;
1080         nFontMetricCount = Ft2QueryFonts( mhPS,
1081                                           QF_PUBLIC | QF_PRIVATE,
1082                                           NULL, &nTemp,
1083                                           sizeof( FONTMETRICS ), NULL );
1084 
1085         // procede only if at least one is available!
1086         if ( nFontMetricCount )
1087         {
1088             // allocate memory for font list
1089             pFontMetrics = new FONTMETRICS[nFontMetricCount];
1090 
1091             // query font list
1092             Ft2QueryFonts( mhPS,
1093                            QF_PUBLIC | QF_PRIVATE,
1094                            NULL,
1095                            (PLONG)&nFontMetricCount,
1096                            (LONG) sizeof( FONTMETRICS ),
1097                            pFontMetrics );
1098         }
1099 
1100         if ( !mbPrinter )
1101         {
1102             pSalData->mnFontMetricCount         = nFontMetricCount;
1103             pSalData->mpFontMetrics             = pFontMetrics;
1104         }
1105         else
1106         {
1107             mnFontMetricCount   = nFontMetricCount;
1108             mpFontMetrics       = pFontMetrics;
1109         }
1110     }
1111 
1112     // copy data from the font list
1113     for( ULONG i = 0; i < nFontMetricCount; i++ )
1114     {
1115         PFONTMETRICS pFontMetric = &pFontMetrics[i];
1116 
1117         // skip font starting with '@', this is an alias internally
1118         // used by truetype engine.
1119         if (pFontMetric->szFacename[0] == '@')
1120             continue;
1121 
1122         // skip bitmap fonts (but keep WarpSans)
1123         if ( (pFontMetric->fsDefn & FM_DEFN_OUTLINE) == 0
1124             && strncmp( pFontMetric->szFacename, "WarpSans", 8) )
1125             // Font nicht aufnehmen
1126             continue;
1127 
1128         // replace '-' in facename with ' ' (for ft2lib)
1129         char* dash = pFontMetric->szFacename;
1130         while( (dash=strchr( dash, '-')))
1131             *dash++ = ' ';
1132 
1133         // create new font list element
1134         ImplOs2FontData* pData      = new ImplOs2FontData( pFontMetric, 0, 0 );
1135 
1136         // add font list element to font list
1137         pList->Add( pData );
1138 
1139     }
1140 }
1141 
1142 // ----------------------------------------------------------------------------
1143 
1144 void Os2SalGraphics::GetDevFontSubstList( OutputDevice* pOutDev )
1145 {
1146 }
1147 
1148 // -----------------------------------------------------------------------
1149 
1150 BOOL Os2SalGraphics::GetGlyphBoundRect( long nIndex, Rectangle& rRect )
1151 {
1152     // use unity matrix
1153     MAT2 aMat;
1154     aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
1155     aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
1156 
1157     UINT nGGOFlags = GGO_METRICS;
1158     if( !(nIndex & GF_ISCHAR) )
1159         nGGOFlags |= GGO_GLYPH_INDEX;
1160     nIndex &= GF_IDXMASK;
1161 
1162     GLYPHMETRICS aGM;
1163     DWORD nSize = FT2_ERROR;
1164     nSize = Ft2GetGlyphOutline( mhPS, nIndex, nGGOFlags, &aGM, 0, NULL, &aMat );
1165     if( nSize == FT2_ERROR )
1166         return false;
1167 
1168     rRect = Rectangle( Point( +aGM.gmptGlyphOrigin.x, -aGM.gmptGlyphOrigin.y ),
1169         Size( aGM.gmBlackBoxX, aGM.gmBlackBoxY ) );
1170     rRect.Left()    = static_cast<int>( mfFontScale * rRect.Left() );
1171     rRect.Right()   = static_cast<int>( mfFontScale * rRect.Right() );
1172     rRect.Top()     = static_cast<int>( mfFontScale * rRect.Top() );
1173     rRect.Bottom()  = static_cast<int>( mfFontScale * rRect.Bottom() );
1174     return true;
1175 }
1176 
1177 // -----------------------------------------------------------------------
1178 
1179 BOOL Os2SalGraphics::GetGlyphOutline( long nIndex, ::basegfx::B2DPolyPolygon& rB2DPolyPoly )
1180 {
1181 #if OSL_DEBUG_LEVEL>0
1182     debug_printf("Os2SalGraphics::GetGlyphOutline\n");
1183 #endif
1184     rB2DPolyPoly.clear();
1185 
1186     BOOL bRet = FALSE;
1187 
1188     // use unity matrix
1189     MAT2 aMat;
1190     aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
1191     aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
1192 
1193     UINT nGGOFlags = GGO_NATIVE;
1194     if( !(nIndex & GF_ISCHAR) )
1195         nGGOFlags |= GGO_GLYPH_INDEX;
1196     nIndex &= GF_IDXMASK;
1197 
1198     GLYPHMETRICS aGlyphMetrics;
1199     DWORD nSize1 = FT2_ERROR;
1200     nSize1 = Ft2GetGlyphOutline( mhPS, nIndex, nGGOFlags, &aGlyphMetrics, 0, NULL, &aMat );
1201 
1202     if( !nSize1 )       // blank glyphs are ok
1203         bRet = TRUE;
1204     else if( nSize1 != FT2_ERROR )
1205     {
1206         BYTE*   pData = new BYTE[ nSize1 ];
1207         ULONG   nTotalCount = 0;
1208         DWORD   nSize2;
1209         nSize2 = Ft2GetGlyphOutline( mhPS, nIndex, nGGOFlags,
1210                 &aGlyphMetrics, nSize1, pData, &aMat );
1211 
1212         if( nSize1 == nSize2 )
1213         {
1214             bRet = TRUE;
1215 
1216             int     nPtSize = 512;
1217             Point*  pPoints = new Point[ nPtSize ];
1218             BYTE*   pFlags = new BYTE[ nPtSize ];
1219 
1220             TTPOLYGONHEADER* pHeader = (TTPOLYGONHEADER*)pData;
1221             while( (BYTE*)pHeader < pData+nSize2 )
1222             {
1223                 // only outline data is interesting
1224                 if( pHeader->dwType != TT_POLYGON_TYPE )
1225                     break;
1226 
1227                 // get start point; next start points are end points
1228                 // of previous segment
1229                 int nPnt = 0;
1230 
1231                 long nX = IntTimes256FromFixed( pHeader->pfxStart.x );
1232                 long nY = IntTimes256FromFixed( pHeader->pfxStart.y );
1233                 pPoints[ nPnt ] = Point( nX, nY );
1234                 pFlags[ nPnt++ ] = POLY_NORMAL;
1235 
1236                 bool bHasOfflinePoints = false;
1237                 TTPOLYCURVE* pCurve = (TTPOLYCURVE*)( pHeader + 1 );
1238                 pHeader = (TTPOLYGONHEADER*)( (BYTE*)pHeader + pHeader->cb );
1239                 while( (BYTE*)pCurve < (BYTE*)pHeader )
1240                 {
1241                     int nNeededSize = nPnt + 16 + 3 * pCurve->cpfx;
1242                     if( nPtSize < nNeededSize )
1243                     {
1244                         Point* pOldPoints = pPoints;
1245                         BYTE* pOldFlags = pFlags;
1246                         nPtSize = 2 * nNeededSize;
1247                         pPoints = new Point[ nPtSize ];
1248                         pFlags = new BYTE[ nPtSize ];
1249                         for( int i = 0; i < nPnt; ++i )
1250                         {
1251                             pPoints[ i ] = pOldPoints[ i ];
1252                             pFlags[ i ] = pOldFlags[ i ];
1253                         }
1254                         delete[] pOldPoints;
1255                         delete[] pOldFlags;
1256                     }
1257 
1258                     int i = 0;
1259                     if( TT_PRIM_LINE == pCurve->wType )
1260                     {
1261                         while( i < pCurve->cpfx )
1262                         {
1263                             nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1264                             nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1265                             ++i;
1266                             pPoints[ nPnt ] = Point( nX, nY );
1267                             pFlags[ nPnt ] = POLY_NORMAL;
1268                             ++nPnt;
1269                         }
1270                     }
1271                     else if( TT_PRIM_QSPLINE == pCurve->wType )
1272                     {
1273                         bHasOfflinePoints = true;
1274                         while( i < pCurve->cpfx )
1275                         {
1276                             // get control point of quadratic bezier spline
1277                             nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1278                             nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1279                             ++i;
1280                             Point aControlP( nX, nY );
1281 
1282                             // calculate first cubic control point
1283                             // P0 = 1/3 * (PBeg + 2 * PQControl)
1284                             nX = pPoints[ nPnt-1 ].X() + 2 * aControlP.X();
1285                             nY = pPoints[ nPnt-1 ].Y() + 2 * aControlP.Y();
1286                             pPoints[ nPnt+0 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
1287                             pFlags[ nPnt+0 ] = POLY_CONTROL;
1288 
1289                             // calculate endpoint of segment
1290                             nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1291                             nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1292 
1293                             if ( i+1 >= pCurve->cpfx )
1294                             {
1295                                 // endpoint is either last point in segment => advance
1296                                 ++i;
1297                             }
1298                             else
1299                             {
1300                                 // or endpoint is the middle of two control points
1301                                 nX += IntTimes256FromFixed( pCurve->apfx[ i-1 ].x );
1302                                 nY += IntTimes256FromFixed( pCurve->apfx[ i-1 ].y );
1303                                 nX = (nX + 1) / 2;
1304                                 nY = (nY + 1) / 2;
1305                                 // no need to advance, because the current point
1306                                 // is the control point in next bezier spline
1307                             }
1308 
1309                             pPoints[ nPnt+2 ] = Point( nX, nY );
1310                             pFlags[ nPnt+2 ] = POLY_NORMAL;
1311 
1312                             // calculate second cubic control point
1313                             // P1 = 1/3 * (PEnd + 2 * PQControl)
1314                             nX = pPoints[ nPnt+2 ].X() + 2 * aControlP.X();
1315                             nY = pPoints[ nPnt+2 ].Y() + 2 * aControlP.Y();
1316                             pPoints[ nPnt+1 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
1317                             pFlags[ nPnt+1 ] = POLY_CONTROL;
1318 
1319                             nPnt += 3;
1320                         }
1321                     }
1322 
1323                     // next curve segment
1324                     pCurve = (TTPOLYCURVE*)&pCurve->apfx[ i ];
1325                 }
1326 
1327                 // end point is start point for closed contour
1328                 // disabled, because Polygon class closes the contour itself
1329                 // pPoints[nPnt++] = pPoints[0];
1330                 // #i35928#
1331                 // Added again, but add only when not yet closed
1332                 if(pPoints[nPnt - 1] != pPoints[0])
1333                 {
1334                     if( bHasOfflinePoints )
1335                         pFlags[nPnt] = pFlags[0];
1336 
1337                     pPoints[nPnt++] = pPoints[0];
1338                 }
1339 
1340                 // convert y-coordinates W32 -> VCL
1341                 for( int i = 0; i < nPnt; ++i )
1342                     pPoints[i].Y() = -pPoints[i].Y();
1343 
1344                 // insert into polypolygon
1345                 Polygon aPoly( nPnt, pPoints, (bHasOfflinePoints ? pFlags : NULL) );
1346                 // convert to B2DPolyPolygon
1347                 // TODO: get rid of the intermediate PolyPolygon
1348                 rB2DPolyPoly.append( aPoly.getB2DPolygon() );
1349             }
1350 
1351             delete[] pPoints;
1352             delete[] pFlags;
1353         }
1354 
1355         delete[] pData;
1356     }
1357 
1358     // rescaling needed for the PolyPolygon conversion
1359     if( rB2DPolyPoly.count() )
1360     {
1361         const double fFactor((1.0/256) * mfFontScale);
1362         rB2DPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix(fFactor, fFactor));
1363     }
1364 
1365     return bRet;
1366 }
1367 
1368 // -----------------------------------------------------------------------
1369 
1370 // TODO:  Replace this class with boost::scoped_array
1371 class ScopedCharArray
1372 {
1373 public:
1374     inline explicit ScopedCharArray(char * pArray): m_pArray(pArray) {}
1375 
1376     inline ~ScopedCharArray() { delete[] m_pArray; }
1377 
1378     inline char * get() const { return m_pArray; }
1379 
1380 private:
1381     char * m_pArray;
1382 };
1383 
1384 class ScopedFont
1385 {
1386 public:
1387     explicit ScopedFont(Os2SalGraphics & rData);
1388 
1389     ~ScopedFont();
1390 
1391 private:
1392     Os2SalGraphics & m_rData;
1393     ULONG m_hOrigFont;
1394 };
1395 
1396 ScopedFont::ScopedFont(Os2SalGraphics & rData): m_rData(rData)
1397 {
1398 #if 0
1399     m_hOrigFont = m_rData.mhFonts[0];
1400     m_rData.mhFonts[0] = 0; // avoid deletion of current font
1401 #endif
1402 }
1403 
1404 ScopedFont::~ScopedFont()
1405 {
1406 #if 0
1407     if( m_hOrigFont )
1408     {
1409         // restore original font, destroy temporary font
1410         HFONT hTempFont = m_rData.mhFonts[0];
1411         m_rData.mhFonts[0] = m_hOrigFont;
1412         SelectObject( m_rData.mhDC, m_hOrigFont );
1413         DeleteObject( hTempFont );
1414     }
1415 #endif
1416 }
1417 
1418 class ScopedTrueTypeFont
1419 {
1420 public:
1421     inline ScopedTrueTypeFont(): m_pFont(0) {}
1422 
1423     ~ScopedTrueTypeFont();
1424 
1425     int open(void * pBuffer, sal_uInt32 nLen, sal_uInt32 nFaceNum);
1426 
1427     inline TrueTypeFont * get() const { return m_pFont; }
1428 
1429 private:
1430     TrueTypeFont * m_pFont;
1431 };
1432 
1433 ScopedTrueTypeFont::~ScopedTrueTypeFont()
1434 {
1435     if (m_pFont != 0)
1436         CloseTTFont(m_pFont);
1437 }
1438 
1439 int ScopedTrueTypeFont::open(void * pBuffer, sal_uInt32 nLen,
1440                              sal_uInt32 nFaceNum)
1441 {
1442     OSL_ENSURE(m_pFont == 0, "already open");
1443     return OpenTTFontBuffer(pBuffer, nLen, nFaceNum, &m_pFont);
1444 }
1445 
1446 BOOL Os2SalGraphics::CreateFontSubset( const rtl::OUString& rToFile,
1447     const ImplFontData* pFont, long* pGlyphIDs, sal_uInt8* pEncoding,
1448     sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo )
1449 {
1450     // create matching ImplFontSelectData
1451     // we need just enough to get to the font file data
1452     // use height=1000 for easier debugging (to match psprint's font units)
1453     ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
1454 
1455     // TODO: much better solution: move SetFont and restoration of old font to caller
1456     ScopedFont aOldFont(*this);
1457     SetFont( &aIFSD, 0 );
1458 
1459 #if OSL_DEBUG_LEVEL > 100
1460     // get font metrics
1461     TEXTMETRICA aWinMetric;
1462     if( !::GetTextMetricsA( mhDC, &aWinMetric ) )
1463         return FALSE;
1464 
1465     DBG_ASSERT( !(aWinMetric.tmPitchAndFamily & TMPF_DEVICE), "cannot subset device font" );
1466     DBG_ASSERT( aWinMetric.tmPitchAndFamily & TMPF_TRUETYPE, "can only subset TT font" );
1467 #endif
1468 
1469     // get raw font file data
1470     DWORD nFontSize1 = Ft2GetFontData( mhPS, 0, 0, NULL, 0 );
1471     if( nFontSize1 == FT2_ERROR )
1472         return FALSE;
1473     ScopedCharArray xRawFontData(new char[ nFontSize1 ]);
1474     DWORD nFontSize2 = Ft2GetFontData( mhPS, 0, 0, (void*)xRawFontData.get(), nFontSize1 );
1475     if( nFontSize1 != nFontSize2 )
1476         return FALSE;
1477 
1478     // open font file
1479     sal_uInt32 nFaceNum = 0;
1480     if( !*xRawFontData.get() )  // TTC candidate
1481         nFaceNum = ~0U;  // indicate "TTC font extracts only"
1482 
1483     ScopedTrueTypeFont aSftTTF;
1484     int nRC = aSftTTF.open( xRawFontData.get(), nFontSize1, nFaceNum );
1485     if( nRC != SF_OK )
1486         return FALSE;
1487 
1488     TTGlobalFontInfo aTTInfo;
1489     ::GetTTGlobalFontInfo( aSftTTF.get(), &aTTInfo );
1490     rInfo.m_nFontType   = SAL_FONTSUBSETINFO_TYPE_TRUETYPE;
1491     rInfo.m_aPSName     = ImplSalGetUniString( aTTInfo.psname );
1492     rInfo.m_nAscent     = +aTTInfo.winAscent;
1493     rInfo.m_nDescent    = -aTTInfo.winDescent;
1494     rInfo.m_aFontBBox   = Rectangle( Point( aTTInfo.xMin, aTTInfo.yMin ),
1495                                     Point( aTTInfo.xMax, aTTInfo.yMax ) );
1496     rInfo.m_nCapHeight  = aTTInfo.yMax; // Well ...
1497 
1498     // subset glyphs and get their properties
1499     // take care that subset fonts require the NotDef glyph in pos 0
1500     int nOrigCount = nGlyphCount;
1501     USHORT    aShortIDs[ 256 ];
1502     sal_uInt8 aTempEncs[ 256 ];
1503 
1504     int nNotDef=-1, i;
1505     for( i = 0; i < nGlyphCount; ++i )
1506     {
1507         aTempEncs[i] = pEncoding[i];
1508         sal_uInt32 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK;
1509         if( pGlyphIDs[i] & GF_ISCHAR )
1510         {
1511             bool bVertical = (pGlyphIDs[i] & GF_ROTMASK) != 0;
1512             nGlyphIdx = ::MapChar( aSftTTF.get(), sal::static_int_cast<sal_uInt16>(nGlyphIdx), bVertical );
1513             if( nGlyphIdx == 0 && pFont->IsSymbolFont() )
1514             {
1515                 // #i12824# emulate symbol aliasing U+FXXX <-> U+0XXX
1516                 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK;
1517                 nGlyphIdx = (nGlyphIdx & 0xF000) ? (nGlyphIdx & 0x00FF) : (nGlyphIdx | 0xF000 );
1518                 nGlyphIdx = ::MapChar( aSftTTF.get(), sal::static_int_cast<sal_uInt16>(nGlyphIdx), bVertical );
1519             }
1520         }
1521         aShortIDs[i] = static_cast<USHORT>( nGlyphIdx );
1522         if( !nGlyphIdx )
1523             if( nNotDef < 0 )
1524                 nNotDef = i; // first NotDef glyph found
1525     }
1526 
1527     if( nNotDef != 0 )
1528     {
1529         // add fake NotDef glyph if needed
1530         if( nNotDef < 0 )
1531             nNotDef = nGlyphCount++;
1532 
1533         // NotDef glyph must be in pos 0 => swap glyphids
1534         aShortIDs[ nNotDef ] = aShortIDs[0];
1535         aTempEncs[ nNotDef ] = aTempEncs[0];
1536         aShortIDs[0] = 0;
1537         aTempEncs[0] = 0;
1538     }
1539     DBG_ASSERT( nGlyphCount < 257, "too many glyphs for subsetting" );
1540 
1541     // fill pWidth array
1542     TTSimpleGlyphMetrics* pMetrics =
1543         ::GetTTSimpleGlyphMetrics( aSftTTF.get(), aShortIDs, nGlyphCount, aIFSD.mbVertical );
1544     if( !pMetrics )
1545         return FALSE;
1546     sal_uInt16 nNotDefAdv   = pMetrics[0].adv;
1547     pMetrics[0].adv         = pMetrics[nNotDef].adv;
1548     pMetrics[nNotDef].adv   = nNotDefAdv;
1549     for( i = 0; i < nOrigCount; ++i )
1550         pGlyphWidths[i] = pMetrics[i].adv;
1551     free( pMetrics );
1552 
1553     // write subset into destination file
1554     rtl::OUString aSysPath;
1555     if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) )
1556         return FALSE;
1557     rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding();
1558     ByteString aToFile( rtl::OUStringToOString( aSysPath, aThreadEncoding ) );
1559     nRC = ::CreateTTFromTTGlyphs( aSftTTF.get(), aToFile.GetBuffer(), aShortIDs,
1560             aTempEncs, nGlyphCount, 0, NULL, 0 );
1561     return nRC == SF_OK;
1562 }
1563 
1564 //--------------------------------------------------------------------------
1565 
1566 const void* Os2SalGraphics::GetEmbedFontData( const ImplFontData* pFont,
1567     const sal_Ucs* pUnicodes, sal_Int32* pCharWidths,
1568     FontSubsetInfo& rInfo, long* pDataLen )
1569 {
1570     // create matching ImplFontSelectData
1571     // we need just enough to get to the font file data
1572     ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
1573 
1574     // TODO: much better solution: move SetFont and restoration of old font to caller
1575     ScopedFont aOldFont(*this);
1576     SetFont( &aIFSD, 0 );
1577 
1578     // get the raw font file data
1579     DWORD nFontSize1 = Ft2GetFontData( mhPS, 0, 0, NULL, 0 );
1580     if( nFontSize1 == FT2_ERROR || nFontSize1 <= 0 )
1581     return NULL;
1582     *pDataLen = nFontSize1;
1583     void* pData = reinterpret_cast<void*>(new char[ nFontSize1 ]);
1584     DWORD nFontSize2 = Ft2GetFontData( mhPS, 0, 0, pData, nFontSize1 );
1585     if( nFontSize1 != nFontSize2 )
1586         *pDataLen = 0;
1587 
1588     // get important font properties
1589     FONTMETRICS aOS2Metric;
1590     if (Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric ) == GPI_ERROR)
1591             *pDataLen = 0;
1592     rInfo.m_nFontType   = SAL_FONTSUBSETINFO_TYPE_TYPE1;
1593     rInfo.m_aPSName     = ImplSalGetUniString( aOS2Metric.szFacename );
1594     rInfo.m_nAscent     = +aOS2Metric.lMaxAscender;
1595     rInfo.m_nDescent    = -aOS2Metric.lMaxDescender;
1596     rInfo.m_aFontBBox   = Rectangle( Point( 0, -aOS2Metric.lMaxDescender ),
1597               Point( aOS2Metric.lMaxCharInc, aOS2Metric.lMaxAscender+aOS2Metric.lExternalLeading ) );
1598     rInfo.m_nCapHeight  = aOS2Metric.lMaxAscender; // Well ...
1599 
1600     // get individual character widths
1601     for( int i = 0; i < 256; ++i )
1602     {
1603         LONG nCharWidth = 0;
1604         const sal_Ucs cChar = pUnicodes[i];
1605         if( !Ft2QueryStringWidthW( mhPS, (LPWSTR)&cChar, 1, &nCharWidth ) )
1606             *pDataLen = 0;
1607         pCharWidths[i] = nCharWidth;
1608     }
1609 
1610     if( !*pDataLen )
1611     {
1612         FreeEmbedFontData( pData, nFontSize1 );
1613         pData = NULL;
1614     }
1615 
1616     return pData;
1617 }
1618 
1619 //--------------------------------------------------------------------------
1620 
1621 void Os2SalGraphics::FreeEmbedFontData( const void* pData, long /*nLen*/ )
1622 {
1623     delete[] reinterpret_cast<char*>(const_cast<void*>(pData));
1624 }
1625 
1626 const Ucs2SIntMap* Os2SalGraphics::GetFontEncodingVector( const ImplFontData* pFont, const Ucs2OStrMap** pNonEncoded )
1627 {
1628     // TODO: even for builtin fonts we get here... why?
1629     if( !pFont->IsEmbeddable() )
1630         return NULL;
1631 
1632     // fill the encoding vector
1633     Ucs2SIntMap& rMap = *new Ucs2SIntMap;
1634 #if 0
1635     // TODO: get correct encoding vector
1636     ImplWinFontData* pWinFontData = reinterpret_cast<ImplWinFontData*>(pFont);
1637 
1638     GLYPHSET aGlyphSet;
1639     aGlyphSet.cbThis = sizeof(aGlyphSet);
1640     DWORD aW = ::GetFontUnicodeRanges( mhDC, &aGlyphSet);
1641 #else
1642     for( sal_Unicode i = 32; i < 256; ++i )
1643         rMap[i] = i;
1644     if( pNonEncoded )
1645         *pNonEncoded = NULL;
1646 #endif
1647 
1648     return &rMap;
1649 }
1650 
1651 //--------------------------------------------------------------------------
1652 
1653 void Os2SalGraphics::GetGlyphWidths( const ImplFontData* pFont,
1654                                      bool bVertical,
1655                                      Int32Vector& rWidths,
1656                                      Ucs2UIntMap& rUnicodeEnc )
1657 {
1658     // create matching ImplFontSelectData
1659     // we need just enough to get to the font file data
1660     ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
1661 
1662     // TODO: much better solution: move SetFont and restoration of old font to caller
1663     ScopedFont aOldFont(*this);
1664 
1665     float fScale = 0.0;
1666     ImplDoSetFont( &aIFSD, fScale, 0);
1667 
1668     if( pFont->IsSubsettable() )
1669     {
1670         // get raw font file data
1671         DWORD nFontSize1 = ::Ft2GetFontData( mhPS, 0, 0, NULL, 0 );
1672         if( nFontSize1 == FT2_ERROR )
1673             return;
1674         ScopedCharArray xRawFontData(new char[ nFontSize1 ]);
1675         DWORD nFontSize2 = ::Ft2GetFontData( mhPS, 0, 0, (void*)xRawFontData.get(), nFontSize1 );
1676         if( nFontSize1 != nFontSize2 )
1677             return;
1678 
1679         // open font file
1680         sal_uInt32 nFaceNum = 0;
1681         if( !*xRawFontData.get() )  // TTC candidate
1682             nFaceNum = ~0U;  // indicate "TTC font extracts only"
1683 
1684         ScopedTrueTypeFont aSftTTF;
1685         int nRC = aSftTTF.open( xRawFontData.get(), nFontSize1, nFaceNum );
1686         if( nRC != SF_OK )
1687             return;
1688 
1689         int nGlyphs = GetTTGlyphCount( aSftTTF.get() );
1690         if( nGlyphs > 0 )
1691         {
1692             rWidths.resize(nGlyphs);
1693             std::vector<sal_uInt16> aGlyphIds(nGlyphs);
1694             for( int i = 0; i < nGlyphs; i++ )
1695                 aGlyphIds[i] = sal_uInt16(i);
1696             TTSimpleGlyphMetrics* pMetrics = ::GetTTSimpleGlyphMetrics( aSftTTF.get(),
1697                                                                         &aGlyphIds[0],
1698                                                                         nGlyphs,
1699                                                                         bVertical ? 1 : 0 );
1700             if( pMetrics )
1701             {
1702                 for( int i = 0; i< nGlyphs; i++ )
1703                     rWidths[i] = pMetrics[i].adv;
1704                 free( pMetrics );
1705                 rUnicodeEnc.clear();
1706             }
1707             const ImplOs2FontData* pWinFont = static_cast<const ImplOs2FontData*>(pFont);
1708             const ImplFontCharMap* pMap = pWinFont->GetImplFontCharMap();
1709             DBG_ASSERT( pMap && pMap->GetCharCount(), "no map" );
1710 
1711             int nCharCount = pMap->GetCharCount();
1712             sal_uInt32 nChar = pMap->GetFirstChar();
1713             for( int i = 0; i < nCharCount; i++ )
1714             {
1715                 if( nChar < 0x00010000 )
1716                 {
1717                     sal_uInt16 nGlyph = ::MapChar( aSftTTF.get(),
1718                                                    static_cast<sal_uInt16>(nChar),
1719                                                    bVertical ? 1 : 0 );
1720                     if( nGlyph )
1721                         rUnicodeEnc[ static_cast<sal_Unicode>(nChar) ] = nGlyph;
1722                 }
1723                 nChar = pMap->GetNextChar( nChar );
1724             }
1725         }
1726     }
1727     else if( pFont->IsEmbeddable() )
1728     {
1729         // get individual character widths
1730         rWidths.clear();
1731         rUnicodeEnc.clear();
1732         rWidths.reserve( 224 );
1733         for( sal_Unicode i = 32; i < 256; ++i )
1734         {
1735             int nCharWidth = 0;
1736             if( Ft2QueryStringWidthW( mhPS, (LPWSTR)&i, 1, (LONG*)&nCharWidth ) )
1737             {
1738                 rUnicodeEnc[ i ] = rWidths.size();
1739                 rWidths.push_back( nCharWidth );
1740             }
1741         }
1742     }
1743 }
1744 
1745 //--------------------------------------------------------------------------
1746 
1747 void Os2SalGraphics::DrawServerFontLayout( const ServerFontLayout& )
1748 {}
1749 
1750 //--------------------------------------------------------------------------
1751 
1752 SystemFontData Os2SalGraphics::GetSysFontData( int nFallbacklevel ) const
1753 {
1754     SystemFontData aSysFontData;
1755 
1756     if (nFallbacklevel >= MAX_FALLBACK) nFallbacklevel = MAX_FALLBACK - 1;
1757     if (nFallbacklevel < 0 ) nFallbacklevel = 0;
1758 
1759     aSysFontData.nSize = sizeof( SystemFontData );
1760     aSysFontData.hFont = mhFonts[nFallbacklevel];
1761     aSysFontData.bFakeBold = false;
1762     aSysFontData.bFakeItalic = false;
1763     aSysFontData.bAntialias = true;
1764     aSysFontData.bVerticalCharacterType = false;
1765 
1766     return aSysFontData;
1767 }
1768 
1769 //--------------------------------------------------------------------------
1770