LCOV - code coverage report
Current view: top level - vcl/generic/glyphs - gcach_ftyp.cxx (source / functions) Hit Total Coverage
Test: commit 10e77ab3ff6f4314137acd6e2702a6e5c1ce1fae Lines: 597 912 65.5 %
Date: 2014-11-03 Functions: 55 62 88.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include "gcach_ftyp.hxx"
      21             : 
      22             : #include "vcl/svapp.hxx"
      23             : #include <outfont.hxx>
      24             : #include <impfont.hxx>
      25             : #include <config_features.h>
      26             : #include <config_graphite.h>
      27             : #if ENABLE_GRAPHITE
      28             : #  include <graphite_static.hxx>
      29             : #  include <graphite2/Font.h>
      30             : #  include <graphite_layout.hxx>
      31             : #endif
      32             : #include <unotools/fontdefs.hxx>
      33             : 
      34             : #include "tools/poly.hxx"
      35             : #include "basegfx/matrix/b2dhommatrix.hxx"
      36             : #include "basegfx/matrix/b2dhommatrixtools.hxx"
      37             : #include "basegfx/polygon/b2dpolypolygon.hxx"
      38             : 
      39             : #include "osl/file.hxx"
      40             : #include "osl/thread.hxx"
      41             : 
      42             : #include "langboost.hxx"
      43             : #include "PhysicalFontCollection.hxx"
      44             : #include "sft.hxx"
      45             : 
      46             : #include <ft2build.h>
      47             : #include FT_FREETYPE_H
      48             : #include FT_GLYPH_H
      49             : #include FT_OUTLINE_H
      50             : #include FT_SIZES_H
      51             : #include FT_SYNTHESIS_H
      52             : #include FT_TRUETYPE_TABLES_H
      53             : #include FT_TRUETYPE_TAGS_H
      54             : #include FT_TRUETYPE_IDS_H
      55             : 
      56             : #include "rtl/instance.hxx"
      57             : 
      58             : typedef const FT_Vector* FT_Vector_CPtr;
      59             : 
      60             : #include <vector>
      61             : 
      62             : // TODO: move file mapping stuff to OSL
      63             : #include <unistd.h>
      64             : #include <fcntl.h>
      65             : #include <sys/stat.h>
      66             : #include <sys/mman.h>
      67             : #include "fontmanager.hxx"
      68             : 
      69             : // the gamma table makes artificial bold look better for CJK glyphs
      70             : static unsigned char aGammaTable[257];
      71             : 
      72         304 : static void InitGammaTable()
      73             : {
      74             :     static const int M_MAX = 255;
      75             :     static const int M_X   = 128;
      76             :     static const int M_Y   = 208;
      77             : 
      78             :     int x, a;
      79       78128 :     for( x = 0; x < 256; x++)
      80             :     {
      81       77824 :         if ( x <= M_X )
      82       39216 :             a = ( x * M_Y + M_X / 2) / M_X;
      83             :         else
      84       38608 :             a = M_Y + ( ( x - M_X ) * ( M_MAX - M_Y ) +
      85       38608 :                 ( M_MAX - M_X ) / 2 ) / ( M_MAX - M_X );
      86             : 
      87       77824 :         aGammaTable[x] = (unsigned char)a;
      88             :     }
      89         304 : }
      90             : 
      91             : static FT_Library aLibFT = 0;
      92             : 
      93             : // enable linking with old FT versions
      94             : static int nFTVERSION = 0;
      95             : 
      96             : typedef ::boost::unordered_map<const char*, boost::shared_ptr<FtFontFile>, rtl::CStringHash, rtl::CStringEqual> FontFileList;
      97             : 
      98             : namespace { struct vclFontFileList : public rtl::Static< FontFileList, vclFontFileList > {}; }
      99             : 
     100             : // TODO: remove when the priorities are selected by UI
     101             : // if (AH==0) => disable autohinting
     102             : // if (AA==0) => disable antialiasing
     103             : // if (EB==0) => disable embedded bitmaps
     104             : // if (AA prio <= AH prio) => antialias + autohint
     105             : // if (AH<AA) => do not autohint when antialiasing
     106             : // if (EB<AH) => do not autohint for monochrome
     107             : static int nDefaultPrioEmbedded    = 2;
     108             : static int nDefaultPrioAutoHint    = 1;
     109             : static int nDefaultPrioAntiAlias   = 1;
     110             : 
     111             : // FreetypeManager
     112             : 
     113       46208 : FtFontFile::FtFontFile( const OString& rNativeFileName )
     114             : :   maNativeFileName( rNativeFileName ),
     115             :     mpFileMap( NULL ),
     116             :     mnFileSize( 0 ),
     117             :     mnRefCount( 0 ),
     118       46208 :     mnLangBoost( 0 )
     119             : {
     120             :     // boost font preference if UI language is mentioned in filename
     121       46208 :     int nPos = maNativeFileName.lastIndexOf( '_' );
     122       46208 :     if( nPos == -1 || maNativeFileName[nPos+1] == '.' )
     123       41952 :         mnLangBoost += 0x1000;     // no langinfo => good
     124             :     else
     125             :     {
     126             :         static const char* pLangBoost = NULL;
     127             :         static bool bOnce = true;
     128        4256 :         if( bOnce )
     129             :         {
     130         304 :             bOnce = false;
     131         304 :             pLangBoost = vcl::getLangBoost();
     132             :         }
     133             : 
     134        4256 :         if( pLangBoost && !strncasecmp( pLangBoost, &maNativeFileName.getStr()[nPos+1], 3 ) )
     135           0 :            mnLangBoost += 0x2000;     // matching langinfo => better
     136             :     }
     137       46208 : }
     138             : 
     139       46208 : FtFontFile* FtFontFile::FindFontFile( const OString& rNativeFileName )
     140             : {
     141             :     // font file already known? (e.g. for ttc, synthetic, aliased fonts)
     142       46208 :     const char* pFileName = rNativeFileName.getStr();
     143       46208 :     FontFileList &rFontFileList = vclFontFileList::get();
     144       46208 :     FontFileList::const_iterator it = rFontFileList.find( pFileName );
     145       46208 :     if( it != rFontFileList.end() )
     146           0 :         return it->second.get();
     147             : 
     148             :     // no => create new one
     149       46208 :     FtFontFile* pFontFile = new FtFontFile( rNativeFileName );
     150       46208 :     pFileName = pFontFile->maNativeFileName.getStr();
     151       46208 :     rFontFileList[pFileName].reset(pFontFile);
     152       46208 :     return pFontFile;
     153             : }
     154             : 
     155        1403 : bool FtFontFile::Map()
     156             : {
     157        1403 :     if( mnRefCount++ <= 0 )
     158             :     {
     159        1403 :         const char* pFileName = maNativeFileName.getStr();
     160        1403 :         int nFile = open( pFileName, O_RDONLY );
     161        1403 :         if( nFile < 0 )
     162           0 :             return false;
     163             : 
     164             :         struct stat aStat;
     165        1403 :         int nRet = fstat( nFile, &aStat );
     166        1403 :         if (nRet < 0)
     167             :         {
     168           0 :             close (nFile);
     169           0 :             return false;
     170             :         }
     171        1403 :         mnFileSize = aStat.st_size;
     172             :         mpFileMap = (const unsigned char*)
     173        1403 :             mmap( NULL, mnFileSize, PROT_READ, MAP_SHARED, nFile, 0 );
     174        1403 :         if( mpFileMap == MAP_FAILED )
     175           0 :             mpFileMap = NULL;
     176        1403 :         close( nFile );
     177             :     }
     178             : 
     179        1403 :     return (mpFileMap != NULL);
     180             : }
     181             : 
     182        1403 : void FtFontFile::Unmap()
     183             : {
     184        1403 :     if( (--mnRefCount > 0) || (mpFileMap == NULL) )
     185        1403 :         return;
     186             : 
     187        1403 :     munmap( (char*)mpFileMap, mnFileSize );
     188        1403 :     mpFileMap = NULL;
     189             : }
     190             : 
     191             : #if ENABLE_GRAPHITE
     192             : // wrap FtFontInfo's table function
     193           0 : const void * graphiteFontTable(const void* appFaceHandle, unsigned int name, size_t *len)
     194             : {
     195           0 :     const FtFontInfo * pFontInfo = reinterpret_cast<const FtFontInfo*>(appFaceHandle);
     196             :     typedef union {
     197             :         char m_c[5];
     198             :         unsigned int m_id;
     199             :     } TableId;
     200             :     TableId tableId;
     201           0 :     tableId.m_id = name;
     202             : #ifndef OSL_BIGENDIAN
     203             :     TableId swapped;
     204           0 :     swapped.m_c[3] = tableId.m_c[0];
     205           0 :     swapped.m_c[2] = tableId.m_c[1];
     206           0 :     swapped.m_c[1] = tableId.m_c[2];
     207           0 :     swapped.m_c[0] = tableId.m_c[3];
     208           0 :     tableId.m_id = swapped.m_id;
     209             : #endif
     210           0 :     tableId.m_c[4] = '\0';
     211           0 :     sal_uLong nLength = 0;
     212           0 :     const void * pTable = static_cast<const void*>(pFontInfo->GetTable(tableId.m_c, &nLength));
     213           0 :     if (len) *len = static_cast<size_t>(nLength);
     214           0 :     return pTable;
     215             : }
     216             : #endif
     217             : 
     218       46208 : FtFontInfo::FtFontInfo( const ImplDevFontAttributes& rDevFontAttributes,
     219             :     const OString& rNativeFileName, int nFaceNum, sal_IntPtr nFontId, int nSynthetic)
     220             : :
     221             :     maFaceFT( NULL ),
     222       46208 :     mpFontFile( FtFontFile::FindFontFile( rNativeFileName ) ),
     223             :     mnFaceNum( nFaceNum ),
     224             :     mnRefCount( 0 ),
     225             :     mnSynthetic( nSynthetic ),
     226             : #if ENABLE_GRAPHITE
     227             :     mbCheckedGraphite(false),
     228             :     mpGraphiteFace(NULL),
     229             : #endif
     230             :     mnFontId( nFontId ),
     231             :     maDevFontAttributes( rDevFontAttributes ),
     232             :     mpFontCharMap( NULL ),
     233             :     mpChar2Glyph( NULL ),
     234       92416 :     mpGlyph2Char( NULL )
     235             : {
     236             :     // prefer font with low ID
     237       46208 :     maDevFontAttributes.mnQuality += 10000 - nFontId;
     238             :     // prefer font with matching file names
     239       46208 :     maDevFontAttributes.mnQuality += mpFontFile->GetLangBoost();
     240       46208 : }
     241             : 
     242       92416 : FtFontInfo::~FtFontInfo()
     243             : {
     244       46208 :     if( mpFontCharMap )
     245         374 :         mpFontCharMap = 0;
     246       46208 :     delete mpChar2Glyph;
     247       46208 :     delete mpGlyph2Char;
     248             : #if ENABLE_GRAPHITE
     249       46208 :     delete mpGraphiteFace;
     250             : #endif
     251       46208 : }
     252             : 
     253        1399 : void FtFontInfo::InitHashes() const
     254             : {
     255             :     // TODO: avoid pointers when empty stl::hash_* objects become cheap
     256        1399 :     mpChar2Glyph = new Int2IntMap();
     257        1399 :     mpGlyph2Char = new Int2IntMap();
     258        1399 : }
     259             : 
     260        9613 : FT_FaceRec_* FtFontInfo::GetFaceFT()
     261             : {
     262        9613 :     if (!maFaceFT && mpFontFile->Map())
     263             :     {
     264             :         FT_Error rc = FT_New_Memory_Face( aLibFT,
     265        1403 :             (FT_Byte*)mpFontFile->GetBuffer(),
     266        2806 :             mpFontFile->GetFileSize(), mnFaceNum, &maFaceFT );
     267        1403 :         if( (rc != FT_Err_Ok) || (maFaceFT->num_glyphs <= 0) )
     268           0 :             maFaceFT = NULL;
     269             :     }
     270             : 
     271        9613 :    ++mnRefCount;
     272        9613 :    return maFaceFT;
     273             : }
     274             : 
     275             : #if ENABLE_GRAPHITE
     276        6073 : GraphiteFaceWrapper * FtFontInfo::GetGraphiteFace()
     277             : {
     278        6073 :     if (mbCheckedGraphite)
     279        5979 :         return mpGraphiteFace;
     280             :     // test for graphite here so that it is cached most efficiently
     281          94 :     if (GetTable("Silf", 0))
     282             :     {
     283           0 :         static const char* pGraphiteCacheStr = getenv( "SAL_GRAPHITE_CACHE_SIZE" );
     284           0 :         int graphiteSegCacheSize = pGraphiteCacheStr ? (atoi(pGraphiteCacheStr)) : 0;
     285             :         gr_face * pGraphiteFace;
     286           0 :         if (graphiteSegCacheSize > 500)
     287           0 :             pGraphiteFace = gr_make_face_with_seg_cache(this, graphiteFontTable, graphiteSegCacheSize, gr_face_cacheCmap);
     288             :         else
     289           0 :             pGraphiteFace = gr_make_face(this, graphiteFontTable, gr_face_cacheCmap);
     290           0 :         if (pGraphiteFace)
     291           0 :             mpGraphiteFace = new GraphiteFaceWrapper(pGraphiteFace);
     292             :     }
     293          94 :     mbCheckedGraphite = true;
     294          94 :     return mpGraphiteFace;
     295             : }
     296             : #endif
     297             : 
     298        9613 : void FtFontInfo::ReleaseFaceFT()
     299             : {
     300        9613 :     if (--mnRefCount <= 0)
     301             :     {
     302        1403 :         FT_Done_Face( maFaceFT );
     303        1403 :         maFaceFT = NULL;
     304        1403 :         mpFontFile->Unmap();
     305             :     }
     306        9613 : }
     307             : 
     308       85974 : static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);}
     309       35652 : static unsigned GetUShort( const unsigned char* p ){ return((p[0]<<8)+p[1]);}
     310             : //static signed GetSShort( const unsigned char* p ){ return((short)((p[0]<<8)+p[1]));}
     311             : 
     312             : static const sal_uInt32 T_true = 0x74727565;        /* 'true' */
     313             : static const sal_uInt32 T_ttcf = 0x74746366;        /* 'ttcf' */
     314             : static const sal_uInt32 T_otto = 0x4f54544f;        /* 'OTTO' */
     315             : 
     316       33824 : const unsigned char* FtFontInfo::GetTable( const char* pTag, sal_uLong* pLength ) const
     317             : {
     318       33824 :     const unsigned char* pBuffer = mpFontFile->GetBuffer();
     319       33824 :     int nFileSize = mpFontFile->GetFileSize();
     320       33824 :     if( !pBuffer || nFileSize<1024 )
     321           0 :         return NULL;
     322             : 
     323             :     // we currently handle TTF, TTC and OTF headers
     324       33824 :     unsigned nFormat = GetUInt( pBuffer );
     325             : 
     326       33824 :     const unsigned char* p = pBuffer + 12;
     327       33824 :     if( nFormat == T_ttcf )         // TTC_MAGIC
     328           0 :         p += GetUInt( p + 4 * mnFaceNum );
     329       33824 :     else if( nFormat != 0x00010000 && nFormat != T_true && nFormat != T_otto) // TTF_MAGIC and Apple TTF Magic and PS-OpenType font
     330        1196 :         return NULL;
     331             : 
     332             :     // walk table directory until match
     333       32628 :     int nTables = GetUShort( p - 8 );
     334       32628 :     if( nTables >= 64 )  // something fishy?
     335           0 :         return NULL;
     336      222313 :     for( int i = 0; i < nTables; ++i, p+=16 )
     337             :     {
     338      215076 :         if( p[0]==pTag[0] && p[1]==pTag[1] && p[2]==pTag[2] && p[3]==pTag[3] )
     339             :         {
     340       25391 :             sal_uLong nLength = GetUInt( p + 12 );
     341       25391 :             if( pLength != NULL )
     342       25391 :                 *pLength = nLength;
     343       25391 :             const unsigned char* pTable = pBuffer + GetUInt( p + 8 );
     344       25391 :             if( (pTable + nLength) <= (mpFontFile->GetBuffer() + nFileSize) )
     345       25391 :                 return pTable;
     346             :         }
     347             :     }
     348             : 
     349        7237 :     return NULL;
     350             : }
     351             : 
     352       47424 : void FtFontInfo::AnnounceFont( PhysicalFontCollection* pFontCollection )
     353             : {
     354       47424 :     ImplFTSFontData* pFD = new ImplFTSFontData( this, maDevFontAttributes );
     355       47424 :     pFontCollection->Add( pFD );
     356       47424 : }
     357             : 
     358         304 : FreetypeManager::FreetypeManager()
     359         304 : :   mnMaxFontId( 0 )
     360             : {
     361         304 :     /*FT_Error rcFT =*/ FT_Init_FreeType( &aLibFT );
     362             : 
     363         304 :     FT_Int nMajor = 0, nMinor = 0, nPatch = 0;
     364         304 :     FT_Library_Version(aLibFT, &nMajor, &nMinor, &nPatch);
     365         304 :     nFTVERSION = nMajor * 1000 + nMinor * 100 + nPatch;
     366             : 
     367             :     // TODO: remove when the priorities are selected by UI
     368             :     char* pEnv;
     369         304 :     pEnv = ::getenv( "SAL_EMBEDDED_BITMAP_PRIORITY" );
     370         304 :     if( pEnv )
     371           0 :         nDefaultPrioEmbedded  = pEnv[0] - '0';
     372         304 :     pEnv = ::getenv( "SAL_ANTIALIASED_TEXT_PRIORITY" );
     373         304 :     if( pEnv )
     374           0 :         nDefaultPrioAntiAlias = pEnv[0] - '0';
     375         304 :     pEnv = ::getenv( "SAL_AUTOHINTING_PRIORITY" );
     376         304 :     if( pEnv )
     377           0 :         nDefaultPrioAutoHint  = pEnv[0] - '0';
     378             : 
     379         304 :     InitGammaTable();
     380         304 :     vclFontFileList::get();
     381         304 : }
     382             : 
     383     2218137 : FT_Face ServerFont::GetFtFace() const
     384             : {
     385     2218137 :     FT_Activate_Size( maSizeFT );
     386             : 
     387     2218137 :     return maFaceFT;
     388             : }
     389             : 
     390         608 : FreetypeManager::~FreetypeManager()
     391             : {
     392         304 :     ClearFontList();
     393         304 : }
     394             : 
     395       47424 : void FreetypeManager::AddFontFile( const OString& rNormalizedName,
     396             :     int nFaceNum, sal_IntPtr nFontId, const ImplDevFontAttributes& rDevFontAttr)
     397             : {
     398       47424 :     if( rNormalizedName.isEmpty() )
     399           0 :         return;
     400             : 
     401       47424 :     if( maFontList.find( nFontId ) != maFontList.end() )
     402        1216 :         return;
     403             : 
     404             :     FtFontInfo* pFontInfo = new FtFontInfo( rDevFontAttr,
     405       46208 :         rNormalizedName, nFaceNum, nFontId, 0);
     406       46208 :     maFontList[ nFontId ] = pFontInfo;
     407       46208 :     if( mnMaxFontId < nFontId )
     408         608 :         mnMaxFontId = nFontId;
     409             : }
     410             : 
     411         312 : void FreetypeManager::AnnounceFonts( PhysicalFontCollection* pToAdd ) const
     412             : {
     413       47736 :     for( FontList::const_iterator it = maFontList.begin(); it != maFontList.end(); ++it )
     414             :     {
     415       47424 :         FtFontInfo* pFtFontInfo = it->second;
     416       47424 :         pFtFontInfo->AnnounceFont( pToAdd );
     417             :     }
     418         312 : }
     419             : 
     420         304 : void FreetypeManager::ClearFontList( )
     421             : {
     422       46512 :     for( FontList::iterator it = maFontList.begin(); it != maFontList.end(); ++it )
     423             :     {
     424       46208 :         FtFontInfo* pFtFontInfo = it->second;
     425       46208 :         delete pFtFontInfo;
     426             :     }
     427         304 :     maFontList.clear();
     428         304 : }
     429             : 
     430        9613 : ServerFont* FreetypeManager::CreateFont( const FontSelectPattern& rFSD )
     431             : {
     432        9613 :     FtFontInfo* pFontInfo = NULL;
     433             : 
     434             :     // find a FontInfo matching to the font id
     435        9613 :     sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFSD.mpFontData );
     436        9613 :     FontList::iterator it = maFontList.find( nFontId );
     437        9613 :     if( it != maFontList.end() )
     438        9613 :         pFontInfo = it->second;
     439             : 
     440        9613 :     if( !pFontInfo )
     441           0 :         return NULL;
     442             : 
     443        9613 :     ServerFont* pNew = new ServerFont( rFSD, pFontInfo );
     444             : 
     445        9613 :     return pNew;
     446             : }
     447             : 
     448       47424 : ImplFTSFontData::ImplFTSFontData( FtFontInfo* pFI, const ImplDevFontAttributes& rDFA )
     449             : :   PhysicalFontFace( rDFA, IFTSFONT_MAGIC ),
     450       47424 :     mpFtFontInfo( pFI )
     451             : {
     452       47424 :     mbDevice        = false;
     453       47424 :     mbOrientation   = true;
     454       47424 : }
     455             : 
     456       23397 : ImplFontEntry* ImplFTSFontData::CreateFontInstance( FontSelectPattern& rFSD ) const
     457             : {
     458       23397 :     ImplServerFontEntry* pEntry = new ImplServerFontEntry( rFSD );
     459       23397 :     return pEntry;
     460             : }
     461             : 
     462             : // ServerFont
     463             : 
     464        9613 : ServerFont::ServerFont( const FontSelectPattern& rFSD, FtFontInfo* pFI )
     465             : :   maGlyphList( 0),
     466             :     maFontSelData(rFSD),
     467             :     mnRefCount(1),
     468             :     mnBytesUsed( sizeof(ServerFont) ),
     469             :     mpPrevGCFont( NULL ),
     470             :     mpNextGCFont( NULL ),
     471             :     mnCos( 0x10000),
     472             :     mnSin( 0 ),
     473             :     mbCollectedZW( false ),
     474             :     mnPrioEmbedded(nDefaultPrioEmbedded),
     475             :     mnPrioAntiAlias(nDefaultPrioAntiAlias),
     476             :     mnPrioAutoHint(nDefaultPrioAutoHint),
     477             :     mpFontInfo( pFI ),
     478             :     mnLoadFlags( 0 ),
     479             :     maFaceFT( NULL ),
     480             :     maSizeFT( NULL ),
     481             :     mbFaceOk( false ),
     482             :     mbArtItalic( false ),
     483             :     mbArtBold( false ),
     484             :     mbUseGamma( false ),
     485        9613 :     mpLayoutEngine( NULL )
     486             : {
     487             :     // TODO: move update of mpFontEntry into FontEntry class when
     488             :     // it becomes reponsible for the ServerFont instantiation
     489        9613 :     static_cast<ImplServerFontEntry*>(rFSD.mpFontEntry)->SetServerFont( this );
     490             : 
     491        9613 :     maFaceFT = pFI->GetFaceFT();
     492             : 
     493        9613 :     if( rFSD.mnOrientation != 0 )
     494             :     {
     495         306 :         const double dRad = rFSD.mnOrientation * ( F_2PI / 3600.0 );
     496         306 :         mnCos = static_cast<long>( 0x10000 * cos( dRad ) + 0.5 );
     497         306 :         mnSin = static_cast<long>( 0x10000 * sin( dRad ) + 0.5 );
     498             :     }
     499             : 
     500             :     // set the pixel size of the font instance
     501        9613 :     mnWidth = rFSD.mnWidth;
     502        9613 :     if( !mnWidth )
     503        8759 :         mnWidth = rFSD.mnHeight;
     504        9613 :     mfStretch = (double)mnWidth / rFSD.mnHeight;
     505             :     // sanity check (e.g. #i66394#, #i66244#, #66537#)
     506        9613 :     if( (mnWidth < 0) || (mfStretch > +64.0) || (mfStretch < -64.0) )
     507           0 :         return;
     508             : 
     509        9613 :     if( !maFaceFT )
     510           0 :         return;
     511             : 
     512        9613 :     FT_New_Size( maFaceFT, &maSizeFT );
     513        9613 :     FT_Activate_Size( maSizeFT );
     514        9613 :     FT_Error rc = FT_Set_Pixel_Sizes( maFaceFT, mnWidth, rFSD.mnHeight );
     515        9613 :     if( rc != FT_Err_Ok )
     516           0 :         return;
     517             : 
     518        9613 :     FT_Select_Charmap(maFaceFT, FT_ENCODING_UNICODE);
     519             : 
     520        9613 :     if( mpFontInfo->IsSymbolFont() )
     521             :     {
     522          38 :         FT_Encoding eEncoding = FT_ENCODING_MS_SYMBOL;
     523          38 :         if (!FT_IS_SFNT(maFaceFT))
     524          38 :             eEncoding = FT_ENCODING_ADOBE_CUSTOM; // freetype wants this for PS symbol fonts
     525             : 
     526          38 :         FT_Select_Charmap(maFaceFT, eEncoding);
     527             :     }
     528             : 
     529        9613 :     mbFaceOk = true;
     530             : 
     531        9613 :     ApplyGSUB( rFSD );
     532             : 
     533             :     // TODO: query GASP table for load flags
     534        9613 :     mnLoadFlags = FT_LOAD_DEFAULT;
     535             : #if 1 // #i97326# cairo sometimes uses FT_Set_Transform() on our FT_FACE
     536             :     // we are not using FT_Set_Transform() yet, so just ignore it for now
     537        9613 :     mnLoadFlags |= FT_LOAD_IGNORE_TRANSFORM;
     538             : #endif
     539             : 
     540        9613 :     mbArtItalic = (rFSD.GetSlant() != ITALIC_NONE && pFI->GetFontAttributes().GetSlant() == ITALIC_NONE);
     541        9613 :     mbArtBold = (rFSD.GetWeight() > WEIGHT_MEDIUM && pFI->GetFontAttributes().GetWeight() <= WEIGHT_MEDIUM);
     542        9613 :     if( mbArtBold )
     543             :     {
     544             :         //static const int TT_CODEPAGE_RANGE_874  = (1L << 16); // Thai
     545             :         //static const int TT_CODEPAGE_RANGE_932  = (1L << 17); // JIS/Japan
     546             :         //static const int TT_CODEPAGE_RANGE_936  = (1L << 18); // Chinese: Simplified
     547             :         //static const int TT_CODEPAGE_RANGE_949  = (1L << 19); // Korean Wansung
     548             :         //static const int TT_CODEPAGE_RANGE_950  = (1L << 20); // Chinese: Traditional
     549             :         //static const int TT_CODEPAGE_RANGE_1361 = (1L << 21); // Korean Johab
     550             :         static const int TT_CODEPAGE_RANGES1_CJKT = 0x3F0000; // all of the above
     551          18 :         const TT_OS2* pOs2 = (const TT_OS2*)FT_Get_Sfnt_Table( maFaceFT, ft_sfnt_os2 );
     552          18 :         if ((pOs2) && (pOs2->ulCodePageRange1 & TT_CODEPAGE_RANGES1_CJKT )
     553           0 :         && rFSD.mnHeight < 20)
     554           0 :         mbUseGamma = true;
     555             :     }
     556             : 
     557        9613 :     if( ((mnCos != 0) && (mnSin != 0)) || (mnPrioEmbedded <= 0) )
     558         130 :         mnLoadFlags |= FT_LOAD_NO_BITMAP;
     559             : }
     560             : 
     561           0 : void ServerFont::SetFontOptions( boost::shared_ptr<ImplFontOptions> pFontOptions)
     562             : {
     563           0 :     mpFontOptions = pFontOptions;
     564             : 
     565           0 :     if (!mpFontOptions)
     566           0 :         return;
     567             : 
     568           0 :     FontAutoHint eHint = mpFontOptions->GetUseAutoHint();
     569           0 :     if( eHint == AUTOHINT_DONTKNOW )
     570           0 :         eHint = mbUseGamma ? AUTOHINT_TRUE : AUTOHINT_FALSE;
     571             : 
     572           0 :     if( eHint == AUTOHINT_TRUE )
     573           0 :         mnLoadFlags |= FT_LOAD_FORCE_AUTOHINT;
     574             : 
     575           0 :     if( (mnSin != 0) && (mnCos != 0) ) // hinting for 0/90/180/270 degrees only
     576           0 :         mnLoadFlags |= FT_LOAD_NO_HINTING;
     577           0 :     mnLoadFlags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; //#88334#
     578             : 
     579           0 :     if( mpFontOptions->DontUseAntiAlias() )
     580           0 :       mnPrioAntiAlias = 0;
     581           0 :     if( mpFontOptions->DontUseEmbeddedBitmaps() )
     582           0 :       mnPrioEmbedded = 0;
     583           0 :     if( mpFontOptions->DontUseHinting() )
     584           0 :       mnPrioAutoHint = 0;
     585             : 
     586           0 :     if( mnPrioAutoHint <= 0 )
     587           0 :         mnLoadFlags |= FT_LOAD_NO_HINTING;
     588             : 
     589             : #if defined(FT_LOAD_TARGET_LIGHT) && defined(FT_LOAD_TARGET_NORMAL)
     590           0 :     if( !(mnLoadFlags & FT_LOAD_NO_HINTING) )
     591             :     {
     592           0 :        mnLoadFlags |= FT_LOAD_TARGET_NORMAL;
     593           0 :        switch( mpFontOptions->GetHintStyle() )
     594             :        {
     595             :            case HINT_NONE:
     596           0 :                 mnLoadFlags |= FT_LOAD_NO_HINTING;
     597           0 :                 break;
     598             :            case HINT_SLIGHT:
     599           0 :                 mnLoadFlags |= FT_LOAD_TARGET_LIGHT;
     600           0 :                 break;
     601             :            case HINT_MEDIUM:
     602           0 :                 break;
     603             :            case HINT_FULL:
     604             :            default:
     605           0 :                 break;
     606             :        }
     607             :     }
     608             : #endif
     609             : 
     610           0 :     if( mnPrioEmbedded <= 0 )
     611           0 :         mnLoadFlags |= FT_LOAD_NO_BITMAP;
     612             : }
     613             : 
     614           0 : boost::shared_ptr<ImplFontOptions> ServerFont::GetFontOptions() const
     615             : {
     616           0 :     return mpFontOptions;
     617             : }
     618             : 
     619           0 : const OString& ServerFont::GetFontFileName() const
     620             : {
     621           0 :     return mpFontInfo->GetFontFileName();
     622             : }
     623             : 
     624             : 
     625       28839 : ServerFont::~ServerFont()
     626             : {
     627        9613 :     delete mpLayoutEngine;
     628             : 
     629        9613 :     if( maSizeFT )
     630        9613 :         FT_Done_Size( maSizeFT );
     631             : 
     632        9613 :     mpFontInfo->ReleaseFaceFT();
     633             : 
     634        9613 :     ReleaseFromGarbageCollect();
     635       19226 : }
     636             : 
     637             : 
     638       22286 : void ServerFont::FetchFontMetric( ImplFontMetricData& rTo, long& rFactor ) const
     639             : {
     640       22286 :     static_cast<ImplFontAttributes&>(rTo) = mpFontInfo->GetFontAttributes();
     641             : 
     642       22286 :     rTo.mbScalableFont  = true;
     643       22286 :     rTo.mbDevice        = true;
     644       22286 :     rTo.mbKernableFont  = FT_HAS_KERNING( maFaceFT ) != 0;
     645       22286 :     rTo.mnOrientation = GetFontSelData().mnOrientation;
     646             : 
     647             :     //Always consider [star]symbol as symbol fonts
     648       22286 :     if ( IsStarSymbol( rTo.GetFamilyName() ) )
     649        1734 :         rTo.SetSymbolFlag( true );
     650             : 
     651       22286 :     FT_Activate_Size( maSizeFT );
     652             : 
     653       22286 :     rFactor = 0x100;
     654             : 
     655       22286 :     const TT_OS2* pOS2 = (const TT_OS2*)FT_Get_Sfnt_Table( maFaceFT, ft_sfnt_os2 );
     656       22286 :     const double fScale = (double)GetFontSelData().mnHeight / maFaceFT->units_per_EM;
     657             : 
     658       22286 :     rTo.mnAscent = 0;
     659       22286 :     rTo.mnDescent = 0;
     660       22286 :     rTo.mnExtLeading = 0;
     661       22286 :     rTo.mnSlant = 0;
     662       22286 :     rTo.mnWidth = mnWidth;
     663             : 
     664             :     // Calculating ascender and descender:
     665             :     // FreeType >= 2.4.6 does the right thing, so we just use what it gives us,
     666             :     // for earlier versions we emulate its behaviour;
     667             :     // take them from 'hhea' table,
     668             :     // if zero take them from 'OS/2' table,
     669             :     // if zero take them from FreeType's font metrics
     670       22286 :     if (nFTVERSION >= 2406)
     671             :     {
     672       22286 :         const FT_Size_Metrics& rMetrics = maFaceFT->size->metrics;
     673       22286 :         rTo.mnAscent = (rMetrics.ascender + 32) >> 6;
     674       22286 :         rTo.mnDescent = (-rMetrics.descender + 32) >> 6;
     675       22286 :         rTo.mnExtLeading = ((rMetrics.height + 32) >> 6) - (rTo.mnAscent + rTo.mnDescent);
     676             :     }
     677             :     else
     678             :     {
     679           0 :         const TT_HoriHeader* pHHea = (const TT_HoriHeader*)FT_Get_Sfnt_Table(maFaceFT, ft_sfnt_hhea);
     680           0 :         if (pHHea)
     681             :         {
     682           0 :             rTo.mnAscent = pHHea->Ascender * fScale + 0.5;
     683           0 :             rTo.mnDescent = -pHHea->Descender * fScale + 0.5;
     684           0 :             rTo.mnExtLeading = pHHea->Line_Gap * fScale + 0.5;
     685             :         }
     686             : 
     687           0 :         if (!(rTo.mnAscent || rTo.mnDescent))
     688             :         {
     689           0 :             if (pOS2 && (pOS2->version != 0xFFFF))
     690             :             {
     691           0 :                 if (pOS2->sTypoAscender || pOS2->sTypoDescender)
     692             :                 {
     693           0 :                     rTo.mnAscent = pOS2->sTypoAscender * fScale + 0.5;
     694           0 :                     rTo.mnDescent = -pOS2->sTypoDescender * fScale + 0.5;
     695           0 :                     rTo.mnExtLeading = pOS2->sTypoLineGap * fScale + 0.5;
     696             :                 }
     697             :                 else
     698             :                 {
     699           0 :                     rTo.mnAscent = pOS2->usWinAscent * fScale + 0.5;
     700           0 :                     rTo.mnDescent = pOS2->usWinDescent * fScale + 0.5;
     701           0 :                     rTo.mnExtLeading = 0;
     702             :                 }
     703             :             }
     704             :         }
     705             : 
     706           0 :         if (!(rTo.mnAscent || rTo.mnDescent))
     707             :         {
     708           0 :             const FT_Size_Metrics& rMetrics = maFaceFT->size->metrics;
     709           0 :             rTo.mnAscent = (rMetrics.ascender + 32) >> 6;
     710           0 :             rTo.mnDescent = (-rMetrics.descender + 32) >> 6;
     711           0 :             rTo.mnExtLeading = ((rMetrics.height + 32) >> 6) - (rTo.mnAscent + rTo.mnDescent);
     712             :         }
     713             :     }
     714             : 
     715       22286 :     rTo.mnIntLeading = rTo.mnAscent + rTo.mnDescent - (maFaceFT->units_per_EM * fScale + 0.5);
     716             : 
     717       22286 :     if( pOS2 && (pOS2->version != 0xFFFF) )
     718             :     {
     719             :         // map the panose info from the OS2 table to their VCL counterparts
     720       21688 :         switch( pOS2->panose[0] )
     721             :         {
     722           0 :             case 1: rTo.SetFamilyType( FAMILY_ROMAN ); break;
     723       19954 :             case 2: rTo.SetFamilyType( FAMILY_SWISS ); break;
     724           0 :             case 3: rTo.SetFamilyType( FAMILY_MODERN ); break;
     725           0 :             case 4: rTo.SetFamilyType( FAMILY_SCRIPT ); break;
     726        1734 :             case 5: rTo.SetFamilyType( FAMILY_DECORATIVE ); break;
     727             :             // TODO: is it reasonable to override the attribute with DONTKNOW?
     728             :             case 0: // fall through
     729           0 :             default: rTo.meFamilyType = FAMILY_DONTKNOW; break;
     730             :         }
     731             : 
     732       21688 :         switch( pOS2->panose[3] )
     733             :         {
     734             :             case 2: // fall through
     735             :             case 3: // fall through
     736             :             case 4: // fall through
     737             :             case 5: // fall through
     738             :             case 6: // fall through
     739             :             case 7: // fall through
     740       19702 :             case 8: rTo.SetPitch( PITCH_VARIABLE ); break;
     741         248 :             case 9: rTo.SetPitch( PITCH_FIXED ); break;
     742             :             // TODO: is it reasonable to override the attribute with DONTKNOW?
     743             :             case 0: // fall through
     744             :             case 1: // fall through
     745        1738 :             default: rTo.SetPitch( PITCH_DONTKNOW ); break;
     746             :         }
     747             :     }
     748             : 
     749             :     // initialize kashida width
     750             :     // TODO: what if there are different versions of this glyph available
     751       22286 :     const int nKashidaGlyphId = GetRawGlyphIndex( 0x0640 );
     752       22286 :     if( nKashidaGlyphId )
     753             :     {
     754        3204 :         GlyphData aGlyphData;
     755        3204 :         InitGlyphData( nKashidaGlyphId, aGlyphData );
     756        3204 :         rTo.mnMinKashida = aGlyphData.GetMetric().GetCharWidth();
     757             :     }
     758       22286 : }
     759             : 
     760      138981 : static inline void SplitGlyphFlags( const ServerFont& rFont, sal_GlyphId& rGlyphId, int& nGlyphFlags )
     761             : {
     762      138981 :     nGlyphFlags = rGlyphId & GF_FLAGMASK;
     763      138981 :     rGlyphId &= GF_IDXMASK;
     764             : 
     765      138981 :     if( rGlyphId & GF_ISCHAR )
     766           0 :         rGlyphId = rFont.GetRawGlyphIndex( rGlyphId );
     767      138981 : }
     768             : 
     769      138826 : int ServerFont::ApplyGlyphTransform( int nGlyphFlags,
     770             :     FT_Glyph pGlyphFT, bool bForBitmapProcessing ) const
     771             : {
     772      138826 :     int nAngle = GetFontSelData().mnOrientation;
     773             :     // shortcut most common case
     774      138826 :     if( !nAngle && !nGlyphFlags )
     775      135161 :         return nAngle;
     776             : 
     777        3665 :     const FT_Size_Metrics& rMetrics = maFaceFT->size->metrics;
     778             :     FT_Vector aVector;
     779             :     FT_Matrix aMatrix;
     780             : 
     781        3665 :     bool bStretched = false;
     782             : 
     783        3665 :     switch( nGlyphFlags & GF_ROTMASK )
     784             :     {
     785             :     default:    // straight
     786        3665 :         aVector.x = 0;
     787        3665 :         aVector.y = 0;
     788        3665 :         aMatrix.xx = +mnCos;
     789        3665 :         aMatrix.yy = +mnCos;
     790        3665 :         aMatrix.xy = -mnSin;
     791        3665 :         aMatrix.yx = +mnSin;
     792        3665 :         break;
     793             :     case GF_ROTL:    // left
     794           0 :         nAngle += 900;
     795           0 :         bStretched = (mfStretch != 1.0);
     796           0 :         aVector.x  = (FT_Pos)(+rMetrics.descender * mfStretch);
     797           0 :         aVector.y  = -rMetrics.ascender;
     798           0 :         aMatrix.xx = (FT_Pos)(-mnSin / mfStretch);
     799           0 :         aMatrix.yy = (FT_Pos)(-mnSin * mfStretch);
     800           0 :         aMatrix.xy = (FT_Pos)(-mnCos * mfStretch);
     801           0 :         aMatrix.yx = (FT_Pos)(+mnCos / mfStretch);
     802           0 :         break;
     803             :     case GF_ROTR:    // right
     804           0 :         nAngle -= 900;
     805           0 :         bStretched = (mfStretch != 1.0);
     806           0 :         aVector.x = -maFaceFT->glyph->metrics.horiAdvance;
     807           0 :         aVector.x += (FT_Pos)(rMetrics.descender * mnSin/65536.0);
     808           0 :         aVector.y  = (FT_Pos)(-rMetrics.descender * mfStretch * mnCos/65536.0);
     809           0 :         aMatrix.xx = (FT_Pos)(+mnSin / mfStretch);
     810           0 :         aMatrix.yy = (FT_Pos)(+mnSin * mfStretch);
     811           0 :         aMatrix.xy = (FT_Pos)(+mnCos * mfStretch);
     812           0 :         aMatrix.yx = (FT_Pos)(-mnCos / mfStretch);
     813           0 :         break;
     814             :     }
     815             : 
     816        7330 :     while( nAngle < 0 )
     817           0 :         nAngle += 3600;
     818             : 
     819        3665 :     if( pGlyphFT->format != FT_GLYPH_FORMAT_BITMAP )
     820             :     {
     821        3665 :         FT_Glyph_Transform( pGlyphFT, NULL, &aVector );
     822             : 
     823             :         // orthogonal transforms are better handled by bitmap operations
     824        3665 :         if( bStretched || (bForBitmapProcessing && (nAngle % 900) != 0) )
     825             :         {
     826             :             // apply non-orthogonal or stretch transformations
     827         612 :             FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
     828         612 :             nAngle = 0;
     829             :         }
     830             :     }
     831             :     else
     832             :     {
     833             :         // FT<=2005 ignores transforms for bitmaps, so do it manually
     834           0 :         FT_BitmapGlyph pBmpGlyphFT = reinterpret_cast<FT_BitmapGlyph>(pGlyphFT);
     835           0 :         pBmpGlyphFT->left += (aVector.x + 32) >> 6;
     836           0 :         pBmpGlyphFT->top  += (aVector.y + 32) >> 6;
     837             :     }
     838             : 
     839        3665 :     return nAngle;
     840             : }
     841             : 
     842    41593335 : sal_GlyphId ServerFont::GetRawGlyphIndex(sal_UCS4 aChar, sal_UCS4 aVS) const
     843             : {
     844    41593335 :     if( mpFontInfo->IsSymbolFont() )
     845             :     {
     846        1124 :         if( !FT_IS_SFNT( maFaceFT ) )
     847             :         {
     848        1124 :             if( (aChar & 0xFF00) == 0xF000 )
     849         248 :                 aChar &= 0xFF;    // PS font symbol mapping
     850         876 :             else if( aChar > 0xFF )
     851         244 :                 return 0;
     852             :         }
     853             :     }
     854             : 
     855    41593091 :     int nGlyphIndex = 0;
     856             : #if HAVE_FT_FACE_GETCHARVARIANTINDEX
     857             :     // If asked, check first for variant glyph with the given Unicode variation
     858             :     // selector. This is quite uncommon so we don't bother with caching here.
     859             :     // Disabled for buggy FreeType versions:
     860             :     // https://bugzilla.mozilla.org/show_bug.cgi?id=618406#c8
     861    41593091 :     if (aVS && nFTVERSION >= 2404)
     862           0 :         nGlyphIndex = FT_Face_GetCharVariantIndex(maFaceFT, aChar, aVS);
     863             : #endif
     864             : 
     865    41593091 :     if (nGlyphIndex == 0)
     866             :     {
     867             :         // cache glyph indexes in font info to share between different sizes
     868    41593091 :         nGlyphIndex = mpFontInfo->GetGlyphIndex( aChar );
     869    41593091 :         if( nGlyphIndex < 0 )
     870             :         {
     871       37694 :             nGlyphIndex = FT_Get_Char_Index( maFaceFT, aChar );
     872       37694 :             if( !nGlyphIndex)
     873             :             {
     874             :                 // check if symbol aliasing helps
     875        1045 :                 if( (aChar <= 0x00FF) && mpFontInfo->IsSymbolFont() )
     876           0 :                     nGlyphIndex = FT_Get_Char_Index( maFaceFT, aChar | 0xF000 );
     877             :             }
     878       37694 :             mpFontInfo->CacheGlyphIndex( aChar, nGlyphIndex );
     879             :         }
     880             :     }
     881             : 
     882    41593091 :     return sal_GlyphId( nGlyphIndex);
     883             : }
     884             : 
     885    41530408 : sal_GlyphId ServerFont::FixupGlyphIndex( sal_GlyphId aGlyphId, sal_UCS4 aChar ) const
     886             : {
     887    41530408 :     int nGlyphFlags = GF_NONE;
     888             : 
     889             :     // do glyph substitution if necessary
     890             :     // CJK vertical writing needs special treatment
     891    41530408 :     if( GetFontSelData().mbVertical )
     892             :     {
     893             :         // TODO: rethink when GSUB is used for non-vertical case
     894        1142 :         GlyphSubstitution::const_iterator it = maGlyphSubstitution.find( aGlyphId );
     895        1142 :         if( it == maGlyphSubstitution.end() )
     896             :         {
     897        1142 :             sal_GlyphId nTemp = GetVerticalChar( aChar );
     898        1142 :             if( nTemp ) // is substitution possible
     899           0 :                 nTemp = GetRawGlyphIndex( nTemp );
     900        1142 :             if( nTemp ) // substitute manually if sensible
     901           0 :                 aGlyphId = nTemp | (GF_GSUB | GF_ROTL);
     902             :             else
     903        1142 :                 nGlyphFlags |= GetVerticalFlags( aChar );
     904             :         }
     905             :         else
     906             :         {
     907             :             // for vertical GSUB also compensate for nOrientation=2700
     908           0 :             aGlyphId = (*it).second;
     909           0 :             nGlyphFlags |= GF_GSUB | GF_ROTL;
     910             :         }
     911             :     }
     912             : 
     913    41530408 :     if( aGlyphId != 0 )
     914    41528116 :         aGlyphId |= nGlyphFlags;
     915             : 
     916    41530408 :     return aGlyphId;
     917             : }
     918             : 
     919           0 : sal_GlyphId ServerFont::GetGlyphIndex( sal_UCS4 aChar ) const
     920             : {
     921           0 :     sal_GlyphId aGlyphId = GetRawGlyphIndex( aChar );
     922           0 :     aGlyphId = FixupGlyphIndex( aGlyphId, aChar );
     923           0 :     return aGlyphId;
     924             : }
     925             : 
     926      113143 : static int lcl_GetCharWidth( FT_FaceRec_* pFaceFT, double fStretch, int nGlyphFlags )
     927             : {
     928      113143 :     int nCharWidth = pFaceFT->glyph->metrics.horiAdvance;
     929             : 
     930      113143 :     if( nGlyphFlags & GF_ROTMASK )  // for bVertical rotated glyphs
     931             :     {
     932           0 :         const FT_Size_Metrics& rMetrics = pFaceFT->size->metrics;
     933           0 :         nCharWidth = (int)((rMetrics.height + rMetrics.descender) * fStretch);
     934             :     }
     935             : 
     936      113143 :     return (nCharWidth + 32) >> 6;
     937             : }
     938             : 
     939      113177 : void ServerFont::InitGlyphData( sal_GlyphId aGlyphId, GlyphData& rGD ) const
     940             : {
     941      113177 :     FT_Activate_Size( maSizeFT );
     942             : 
     943             :     int nGlyphFlags;
     944      113177 :     SplitGlyphFlags( *this, aGlyphId, nGlyphFlags );
     945             : 
     946      113177 :     int nLoadFlags = mnLoadFlags;
     947             : 
     948             : //  if( mbArtItalic )
     949             : //      nLoadFlags |= FT_LOAD_NO_BITMAP;
     950             : 
     951      113177 :     FT_Error rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags );
     952             : 
     953      113177 :     if( rc != FT_Err_Ok )
     954             :     {
     955             :         // we get here e.g. when a PS font lacks the default glyph
     956           0 :         rGD.SetCharWidth( 0 );
     957           0 :         rGD.SetDelta( 0, 0 );
     958           0 :         rGD.SetOffset( 0, 0 );
     959           0 :         rGD.SetSize( Size( 0, 0 ) );
     960      113177 :         return;
     961             :     }
     962             : 
     963      113177 :     const bool bOriginallyZeroWidth = (maFaceFT->glyph->metrics.horiAdvance == 0);
     964      113177 :     if (mbArtBold)
     965          18 :         FT_GlyphSlot_Embolden(maFaceFT->glyph);
     966             : 
     967      113177 :     const int nCharWidth = bOriginallyZeroWidth ? 0 : lcl_GetCharWidth( maFaceFT, mfStretch, nGlyphFlags );
     968      113177 :     rGD.SetCharWidth( nCharWidth );
     969             : 
     970             :     FT_Glyph pGlyphFT;
     971      113177 :     rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
     972             : 
     973      113177 :     ApplyGlyphTransform( nGlyphFlags, pGlyphFT, false );
     974      113177 :     rGD.SetDelta( (pGlyphFT->advance.x + 0x8000) >> 16, -((pGlyphFT->advance.y + 0x8000) >> 16) );
     975             : 
     976             :     FT_BBox aBbox;
     977      113177 :     FT_Glyph_Get_CBox( pGlyphFT, FT_GLYPH_BBOX_PIXELS, &aBbox );
     978      113177 :     if( aBbox.yMin > aBbox.yMax )   // circumvent freetype bug
     979             :     {
     980           0 :         int t=aBbox.yMin; aBbox.yMin=aBbox.yMax, aBbox.yMax=t;
     981             :     }
     982             : 
     983      113177 :     rGD.SetOffset( aBbox.xMin, -aBbox.yMax );
     984      113177 :     rGD.SetSize( Size( (aBbox.xMax-aBbox.xMin+1), (aBbox.yMax-aBbox.yMin) ) );
     985             : 
     986      113177 :     FT_Done_Glyph( pGlyphFT );
     987             : }
     988             : 
     989           0 : bool ServerFont::GetAntialiasAdvice( void ) const
     990             : {
     991           0 :     if( GetFontSelData().mbNonAntialiased || (mnPrioAntiAlias<=0) )
     992           0 :         return false;
     993           0 :     bool bAdviseAA = true;
     994             :     // TODO: also use GASP info
     995           0 :     return bAdviseAA;
     996             : }
     997             : 
     998           0 : bool ServerFont::GetGlyphBitmap1( sal_GlyphId aGlyphId, RawBitmap& rRawBitmap ) const
     999             : {
    1000           0 :     FT_Activate_Size( maSizeFT );
    1001             : 
    1002             :     int nGlyphFlags;
    1003           0 :     SplitGlyphFlags( *this, aGlyphId, nGlyphFlags );
    1004             : 
    1005           0 :     FT_Int nLoadFlags = mnLoadFlags;
    1006             :     // #i70930# force mono-hinting for monochrome text
    1007           0 :     nLoadFlags &= ~0xF0000;
    1008           0 :     nLoadFlags |= FT_LOAD_TARGET_MONO;
    1009             : 
    1010           0 :     if( mbArtItalic )
    1011           0 :         nLoadFlags |= FT_LOAD_NO_BITMAP;
    1012             : 
    1013             :     // for 0/90/180/270 degree fonts enable hinting even if not advisable
    1014             :     // non-hinted and non-antialiased bitmaps just look too ugly
    1015           0 :     if( (mnCos==0 || mnSin==0) && (mnPrioAutoHint > 0) )
    1016           0 :         nLoadFlags &= ~FT_LOAD_NO_HINTING;
    1017             : 
    1018           0 :     if( mnPrioEmbedded <= mnPrioAutoHint )
    1019           0 :         nLoadFlags |= FT_LOAD_NO_BITMAP;
    1020             : 
    1021           0 :     FT_Error rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags );
    1022             : 
    1023           0 :     if( rc != FT_Err_Ok )
    1024           0 :         return false;
    1025             : 
    1026           0 :     if (mbArtBold)
    1027           0 :         FT_GlyphSlot_Embolden(maFaceFT->glyph);
    1028             : 
    1029             :     FT_Glyph pGlyphFT;
    1030           0 :     rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
    1031           0 :     if( rc != FT_Err_Ok )
    1032           0 :         return false;
    1033             : 
    1034           0 :     int nAngle = ApplyGlyphTransform( nGlyphFlags, pGlyphFT, true );
    1035             : 
    1036           0 :     if( mbArtItalic )
    1037             :     {
    1038             :         FT_Matrix aMatrix;
    1039           0 :         aMatrix.xx = aMatrix.yy = 0x10000L;
    1040           0 :         aMatrix.xy = 0x6000L, aMatrix.yx = 0;
    1041           0 :         FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
    1042             :     }
    1043             : 
    1044             :     // Check for zero area bounding boxes as this crashes some versions of FT.
    1045             :     // This also provides a handy short cut as much of the code following
    1046             :     //  becomes an expensive nop when a glyph covers no pixels.
    1047             :     FT_BBox cbox;
    1048           0 :     FT_Glyph_Get_CBox(pGlyphFT, ft_glyph_bbox_unscaled, &cbox);
    1049             : 
    1050           0 :     if( (cbox.xMax - cbox.xMin) == 0 || (cbox.yMax - cbox.yMin == 0) )
    1051             :     {
    1052           0 :         nAngle = 0;
    1053           0 :         memset(&rRawBitmap, 0, sizeof rRawBitmap);
    1054           0 :         FT_Done_Glyph( pGlyphFT );
    1055           0 :         return true;
    1056             :     }
    1057             : 
    1058           0 :     if( pGlyphFT->format != FT_GLYPH_FORMAT_BITMAP )
    1059             :     {
    1060           0 :         if( pGlyphFT->format == FT_GLYPH_FORMAT_OUTLINE )
    1061           0 :             ((FT_OutlineGlyphRec*)pGlyphFT)->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
    1062           0 :         FT_Render_Mode nRenderMode = FT_RENDER_MODE_MONO;
    1063             : 
    1064           0 :         rc = FT_Glyph_To_Bitmap( &pGlyphFT, nRenderMode, NULL, true );
    1065           0 :         if( rc != FT_Err_Ok )
    1066             :         {
    1067           0 :             FT_Done_Glyph( pGlyphFT );
    1068           0 :             return false;
    1069             :         }
    1070             :     }
    1071             : 
    1072           0 :     const FT_BitmapGlyph pBmpGlyphFT = reinterpret_cast<const FT_BitmapGlyph>(pGlyphFT);
    1073             :     // NOTE: autohinting in FT<=2.0.2 miscalculates the offsets below by +-1
    1074           0 :     rRawBitmap.mnXOffset        = +pBmpGlyphFT->left;
    1075           0 :     rRawBitmap.mnYOffset        = -pBmpGlyphFT->top;
    1076             : 
    1077           0 :     const FT_Bitmap& rBitmapFT  = pBmpGlyphFT->bitmap;
    1078           0 :     rRawBitmap.mnHeight         = rBitmapFT.rows;
    1079           0 :     rRawBitmap.mnBitCount       = 1;
    1080           0 :     rRawBitmap.mnWidth          = rBitmapFT.width;
    1081           0 :     rRawBitmap.mnScanlineSize   = rBitmapFT.pitch;
    1082             : 
    1083           0 :     const sal_uLong nNeededSize = rRawBitmap.mnScanlineSize * rRawBitmap.mnHeight;
    1084             : 
    1085           0 :     if( rRawBitmap.mnAllocated < nNeededSize )
    1086             :     {
    1087           0 :         rRawBitmap.mnAllocated = 2*nNeededSize;
    1088           0 :         rRawBitmap.mpBits.reset(new unsigned char[ rRawBitmap.mnAllocated ]);
    1089             :     }
    1090             : 
    1091           0 :     if (!mbArtBold)
    1092             :     {
    1093           0 :         memcpy( rRawBitmap.mpBits.get(), rBitmapFT.buffer, nNeededSize );
    1094             :     }
    1095             :     else
    1096             :     {
    1097           0 :         memset( rRawBitmap.mpBits.get(), 0, nNeededSize );
    1098           0 :         const unsigned char* pSrcLine = rBitmapFT.buffer;
    1099           0 :         unsigned char* pDstLine = rRawBitmap.mpBits.get();
    1100           0 :         for( int h = rRawBitmap.mnHeight; --h >= 0; )
    1101             :         {
    1102           0 :             memcpy( pDstLine, pSrcLine, rBitmapFT.pitch );
    1103           0 :             pDstLine += rRawBitmap.mnScanlineSize;
    1104           0 :             pSrcLine += rBitmapFT.pitch;
    1105             :         }
    1106             : 
    1107           0 :         unsigned char* p = rRawBitmap.mpBits.get();
    1108           0 :         for( sal_uLong y=0; y < rRawBitmap.mnHeight; y++ )
    1109             :         {
    1110           0 :             unsigned char nLastByte = 0;
    1111           0 :             for( sal_uLong x=0; x < rRawBitmap.mnScanlineSize; x++ )
    1112             :             {
    1113           0 :             unsigned char nTmp = p[x] << 7;
    1114           0 :             p[x] |= (p[x] >> 1) | nLastByte;
    1115           0 :             nLastByte = nTmp;
    1116             :             }
    1117           0 :             p += rRawBitmap.mnScanlineSize;
    1118             :         }
    1119             :     }
    1120             : 
    1121           0 :     FT_Done_Glyph( pGlyphFT );
    1122             : 
    1123             :     // special case for 0/90/180/270 degree orientation
    1124           0 :     switch( nAngle )
    1125             :     {
    1126             :         case  -900:
    1127             :         case  +900:
    1128             :         case +1800:
    1129             :         case +2700:
    1130           0 :             rRawBitmap.Rotate( nAngle );
    1131           0 :             break;
    1132             :     }
    1133             : 
    1134           0 :     return true;
    1135             : }
    1136             : 
    1137       23172 : bool ServerFont::GetGlyphBitmap8( sal_GlyphId aGlyphId, RawBitmap& rRawBitmap ) const
    1138             : {
    1139       23172 :     FT_Activate_Size( maSizeFT );
    1140             : 
    1141             :     int nGlyphFlags;
    1142       23172 :     SplitGlyphFlags( *this, aGlyphId, nGlyphFlags );
    1143             : 
    1144       23172 :     FT_Int nLoadFlags = mnLoadFlags;
    1145             : 
    1146       23172 :     if( mbArtItalic )
    1147          28 :         nLoadFlags |= FT_LOAD_NO_BITMAP;
    1148             : 
    1149       23172 :     if( (nGlyphFlags & GF_UNHINTED) || (mnPrioAutoHint < mnPrioAntiAlias) )
    1150           0 :         nLoadFlags |= FT_LOAD_NO_HINTING;
    1151             : 
    1152       23172 :     if( mnPrioEmbedded <= mnPrioAntiAlias )
    1153           0 :         nLoadFlags |= FT_LOAD_NO_BITMAP;
    1154             : 
    1155       23172 :     FT_Error rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags );
    1156             : 
    1157       23172 :     if( rc != FT_Err_Ok )
    1158           0 :         return false;
    1159             : 
    1160       23172 :     if (mbArtBold)
    1161           4 :         FT_GlyphSlot_Embolden(maFaceFT->glyph);
    1162             : 
    1163             :     FT_Glyph pGlyphFT;
    1164       23172 :     rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
    1165       23172 :     if( rc != FT_Err_Ok )
    1166           0 :         return false;
    1167             : 
    1168       23172 :     int nAngle = ApplyGlyphTransform( nGlyphFlags, pGlyphFT, true );
    1169             : 
    1170       23172 :     if( mbArtItalic )
    1171             :     {
    1172             :         FT_Matrix aMatrix;
    1173          28 :         aMatrix.xx = aMatrix.yy = 0x10000L;
    1174          28 :         aMatrix.xy = 0x6000L, aMatrix.yx = 0;
    1175          28 :         FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
    1176             :     }
    1177             : 
    1178       23172 :     if( pGlyphFT->format == FT_GLYPH_FORMAT_OUTLINE )
    1179       23172 :         ((FT_OutlineGlyph)pGlyphFT)->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
    1180             : 
    1181       23172 :     bool bEmbedded = (pGlyphFT->format == FT_GLYPH_FORMAT_BITMAP);
    1182       23172 :     if( !bEmbedded )
    1183             :     {
    1184       23172 :         rc = FT_Glyph_To_Bitmap( &pGlyphFT, FT_RENDER_MODE_NORMAL, NULL, true );
    1185       23172 :         if( rc != FT_Err_Ok )
    1186             :         {
    1187           0 :             FT_Done_Glyph( pGlyphFT );
    1188           0 :             return false;
    1189             :         }
    1190             :     }
    1191             : 
    1192       23172 :     const FT_BitmapGlyph pBmpGlyphFT = reinterpret_cast<const FT_BitmapGlyph>(pGlyphFT);
    1193       23172 :     rRawBitmap.mnXOffset        = +pBmpGlyphFT->left;
    1194       23172 :     rRawBitmap.mnYOffset        = -pBmpGlyphFT->top;
    1195             : 
    1196       23172 :     const FT_Bitmap& rBitmapFT  = pBmpGlyphFT->bitmap;
    1197       23172 :     rRawBitmap.mnHeight         = rBitmapFT.rows;
    1198       23172 :     rRawBitmap.mnWidth          = rBitmapFT.width;
    1199       23172 :     rRawBitmap.mnBitCount       = 8;
    1200       23172 :     rRawBitmap.mnScanlineSize   = bEmbedded ? rBitmapFT.width : rBitmapFT.pitch;
    1201       23172 :     rRawBitmap.mnScanlineSize = (rRawBitmap.mnScanlineSize + 3) & -4;
    1202             : 
    1203       23172 :     const sal_uLong nNeededSize = rRawBitmap.mnScanlineSize * rRawBitmap.mnHeight;
    1204       23172 :     if( rRawBitmap.mnAllocated < nNeededSize )
    1205             :     {
    1206       22396 :         rRawBitmap.mnAllocated = 2*nNeededSize;
    1207       22396 :         rRawBitmap.mpBits.reset(new unsigned char[ rRawBitmap.mnAllocated ]);
    1208             :     }
    1209             : 
    1210       23172 :     const unsigned char* pSrc = rBitmapFT.buffer;
    1211       23172 :     unsigned char* pDest = rRawBitmap.mpBits.get();
    1212       23172 :     if( !bEmbedded )
    1213             :     {
    1214      282078 :         for( int y = rRawBitmap.mnHeight, x; --y >= 0 ; )
    1215             :         {
    1216     3671673 :             for( x = 0; x < rBitmapFT.width; ++x )
    1217     3435939 :                 *(pDest++) = *(pSrc++);
    1218      609427 :             for(; x < int(rRawBitmap.mnScanlineSize); ++x )
    1219      373693 :                 *(pDest++) = 0;
    1220             :         }
    1221             :     }
    1222             :     else
    1223             :     {
    1224           0 :         for( int y = rRawBitmap.mnHeight, x; --y >= 0 ; )
    1225             :         {
    1226           0 :             unsigned char nSrc = 0;
    1227           0 :             for( x = 0; x < rBitmapFT.width; ++x, nSrc+=nSrc )
    1228             :             {
    1229           0 :                 if( (x & 7) == 0 )
    1230           0 :                     nSrc = *(pSrc++);
    1231           0 :                 *(pDest++) = (0x7F - nSrc) >> 8;
    1232             :             }
    1233           0 :             for(; x < int(rRawBitmap.mnScanlineSize); ++x )
    1234           0 :                 *(pDest++) = 0;
    1235             :         }
    1236             :     }
    1237             : 
    1238       23172 :     if( !bEmbedded && mbUseGamma )
    1239             :     {
    1240           0 :         unsigned char* p = rRawBitmap.mpBits.get();
    1241           0 :         for( sal_uLong y=0; y < rRawBitmap.mnHeight; y++ )
    1242             :         {
    1243           0 :             for( sal_uLong x=0; x < rRawBitmap.mnWidth; x++ )
    1244             :             {
    1245           0 :                 p[x] = aGammaTable[ p[x] ];
    1246             :             }
    1247           0 :             p += rRawBitmap.mnScanlineSize;
    1248             :         }
    1249             :     }
    1250             : 
    1251       23172 :     FT_Done_Glyph( pGlyphFT );
    1252             : 
    1253             :     // special case for 0/90/180/270 degree orientation
    1254       23172 :     switch( nAngle )
    1255             :     {
    1256             :         case  -900:
    1257             :         case  +900:
    1258             :         case +1800:
    1259             :         case +2700:
    1260         744 :             rRawBitmap.Rotate( nAngle );
    1261         744 :             break;
    1262             :     }
    1263             : 
    1264       23172 :     return true;
    1265             : }
    1266             : 
    1267             : // determine unicode ranges in font
    1268             : 
    1269       13910 : const FontCharMapPtr ServerFont::GetFontCharMap() const
    1270             : {
    1271       13910 :     const FontCharMapPtr pFCMap = mpFontInfo->GetFontCharMap();
    1272       13910 :     return pFCMap;
    1273             : }
    1274             : 
    1275       13910 : const FontCharMapPtr FtFontInfo::GetFontCharMap()
    1276             : {
    1277             :     // check if the charmap is already cached
    1278       13910 :     if( mpFontCharMap )
    1279       13536 :         return mpFontCharMap;
    1280             : 
    1281             :     // get the charmap and cache it
    1282         374 :     CmapResult aCmapResult;
    1283         374 :     bool bOK = GetFontCodeRanges( aCmapResult );
    1284         374 :     if( bOK )
    1285             :     {
    1286         374 :         FontCharMapPtr pFontCharMap( new FontCharMap ( aCmapResult ) );
    1287         374 :         mpFontCharMap = pFontCharMap;
    1288             :     }
    1289             :     else
    1290             :     {
    1291           0 :         FontCharMapPtr pFontCharMap( new FontCharMap() );
    1292           0 :         mpFontCharMap = pFontCharMap;
    1293             :     }
    1294             :     // mpFontCharMap on either branch now has a refcount of 1
    1295         374 :     return mpFontCharMap;
    1296             : }
    1297             : 
    1298             : // TODO: merge into method GetFontCharMap()
    1299         374 : bool FtFontInfo::GetFontCodeRanges( CmapResult& rResult ) const
    1300             : {
    1301         374 :     rResult.mbSymbolic = IsSymbolFont();
    1302             : 
    1303             :     // TODO: is the full CmapResult needed on platforms calling this?
    1304         374 :     if( FT_IS_SFNT( maFaceFT ) )
    1305             :     {
    1306         368 :         sal_uLong nLength = 0;
    1307         368 :         const unsigned char* pCmap = GetTable( "cmap", &nLength );
    1308         368 :         if( pCmap && (nLength > 0) )
    1309         368 :             if( ParseCMAP( pCmap, nLength, rResult ) )
    1310         368 :                 return true;
    1311             :     }
    1312             : 
    1313             :     typedef std::vector<sal_uInt32> U32Vector;
    1314           6 :     U32Vector aCodes;
    1315             : 
    1316             :     // FT's coverage is available since FT>=2.1.0 (OOo-baseline>=2.1.4 => ok)
    1317           6 :     aCodes.reserve( 0x1000 );
    1318             :     FT_UInt nGlyphIndex;
    1319           6 :     for( sal_uInt32 cCode = FT_Get_First_Char( maFaceFT, &nGlyphIndex );; )
    1320             :     {
    1321         228 :         if( !nGlyphIndex )
    1322           6 :             break;
    1323         222 :         aCodes.push_back( cCode );  // first code inside range
    1324         222 :         sal_uInt32 cNext = cCode;
    1325        3426 :         do cNext = FT_Get_Next_Char( maFaceFT, cCode, &nGlyphIndex ); while( cNext == ++cCode );
    1326         222 :         aCodes.push_back( cCode );  // first code outside range
    1327         222 :         cCode = cNext;
    1328         222 :     }
    1329             : 
    1330           6 :     const int nCount = aCodes.size();
    1331           6 :     if( !nCount) {
    1332           0 :         if( !rResult.mbSymbolic )
    1333           0 :             return false;
    1334             : 
    1335             :         // we usually get here for Type1 symbol fonts
    1336           0 :         aCodes.push_back( 0xF020 );
    1337           0 :         aCodes.push_back( 0xF100 );
    1338             :     }
    1339             : 
    1340           6 :     sal_uInt32* pCodes = new sal_uInt32[ nCount ];
    1341         450 :     for( int i = 0; i < nCount; ++i )
    1342         444 :         pCodes[i] = aCodes[i];
    1343           6 :     rResult.mpRangeCodes = pCodes;
    1344           6 :     rResult.mnRangeCount = nCount / 2;
    1345           6 :     return true;
    1346             : }
    1347             : 
    1348        1712 : bool ServerFont::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
    1349             : {
    1350        1712 :     bool bRet = false;
    1351             : 
    1352        1712 :     sal_uLong nLength = 0;
    1353             :     // load GSUB table
    1354        1712 :     const FT_Byte* pGSUB = mpFontInfo->GetTable("GSUB", &nLength);
    1355        1712 :     if (pGSUB)
    1356        1700 :         vcl::getTTScripts(rFontCapabilities.maGSUBScriptTags, pGSUB, nLength);
    1357             : 
    1358             :     // load OS/2 table
    1359        1712 :     const FT_Byte* pOS2 = mpFontInfo->GetTable("OS/2", &nLength);
    1360        1712 :     if (pOS2)
    1361             :     {
    1362             :         bRet = vcl::getTTCoverage(
    1363             :             rFontCapabilities.maUnicodeRange,
    1364             :             rFontCapabilities.maCodePageRange,
    1365        1700 :             pOS2, nLength);
    1366             :     }
    1367             : 
    1368        1712 :     return bRet;
    1369             : }
    1370             : 
    1371             : // outline stuff
    1372             : 
    1373             : class PolyArgs
    1374             : {
    1375             : public:
    1376             :                 PolyArgs( tools::PolyPolygon& rPolyPoly, sal_uInt16 nMaxPoints );
    1377             :                 ~PolyArgs();
    1378             : 
    1379             :     void        AddPoint( long nX, long nY, PolyFlags);
    1380             :     void        ClosePolygon();
    1381             : 
    1382       29816 :     long        GetPosX() const { return maPosition.x;}
    1383       29816 :     long        GetPosY() const { return maPosition.y;}
    1384             : 
    1385             : private:
    1386             :     tools::PolyPolygon& mrPolyPoly;
    1387             : 
    1388             :     Point*      mpPointAry;
    1389             :     sal_uInt8*       mpFlagAry;
    1390             : 
    1391             :     FT_Vector   maPosition;
    1392             :     sal_uInt16      mnMaxPoints;
    1393             :     sal_uInt16      mnPoints;
    1394             :     sal_uInt16      mnPoly;
    1395             :     bool        bHasOffline;
    1396             : };
    1397             : 
    1398        2477 : PolyArgs::PolyArgs( tools::PolyPolygon& rPolyPoly, sal_uInt16 nMaxPoints )
    1399             : :   mrPolyPoly(rPolyPoly),
    1400             :     mnMaxPoints(nMaxPoints),
    1401             :     mnPoints(0),
    1402             :     mnPoly(0),
    1403        2477 :     bHasOffline(false)
    1404             : {
    1405        2477 :     mpPointAry  = new Point[ mnMaxPoints ];
    1406        2477 :     mpFlagAry   = new sal_uInt8 [ mnMaxPoints ];
    1407        2477 :     maPosition.x = maPosition.y = 0;
    1408        2477 : }
    1409             : 
    1410        2477 : PolyArgs::~PolyArgs()
    1411             : {
    1412        2477 :     delete[] mpFlagAry;
    1413        2477 :     delete[] mpPointAry;
    1414        2477 : }
    1415             : 
    1416      116247 : void PolyArgs::AddPoint( long nX, long nY, PolyFlags aFlag )
    1417             : {
    1418             :     DBG_ASSERT( (mnPoints < mnMaxPoints), "FTGlyphOutline: AddPoint overflow!" );
    1419      116247 :     if( mnPoints >= mnMaxPoints )
    1420      116247 :         return;
    1421             : 
    1422      116247 :     maPosition.x = nX;
    1423      116247 :     maPosition.y = nY;
    1424      116247 :     mpPointAry[ mnPoints ] = Point( nX, nY );
    1425      116247 :     mpFlagAry[ mnPoints++ ]= aFlag;
    1426      116247 :     bHasOffline |= (aFlag != POLY_NORMAL);
    1427             : }
    1428             : 
    1429        6189 : void PolyArgs::ClosePolygon()
    1430             : {
    1431        6189 :     if( !mnPoly++ )
    1432        8666 :         return;
    1433             : 
    1434             :     // freetype seems to always close the polygon with an ON_CURVE point
    1435             :     // PolyPoly wants to close the polygon itself => remove last point
    1436             :     DBG_ASSERT( (mnPoints >= 2), "FTGlyphOutline: PolyFinishNum failed!" );
    1437        3712 :     --mnPoints;
    1438             :     DBG_ASSERT( (mpPointAry[0]==mpPointAry[mnPoints]), "FTGlyphOutline: PolyFinishEq failed!" );
    1439             :     DBG_ASSERT( (mpFlagAry[0]==POLY_NORMAL), "FTGlyphOutline: PolyFinishFE failed!" );
    1440             :     DBG_ASSERT( (mpFlagAry[mnPoints]==POLY_NORMAL), "FTGlyphOutline: PolyFinishFS failed!" );
    1441             : 
    1442        3712 :     Polygon aPoly( mnPoints, mpPointAry, (bHasOffline ? mpFlagAry : NULL) );
    1443             : 
    1444             :     // #i35928#
    1445             :     // This may be a invalid polygons, e.g. the last point is a control point.
    1446             :     // So close the polygon (and add the first point again) if the last point
    1447             :     // is a control point or different from first.
    1448             :     // #i48298#
    1449             :     // Now really duplicating the first point, to close or correct the
    1450             :     // polygon. Also no longer duplicating the flags, but enforcing
    1451             :     // POLY_NORMAL for the newly added last point.
    1452        3712 :     const sal_uInt16 nPolySize(aPoly.GetSize());
    1453        3712 :     if(nPolySize)
    1454             :     {
    1455       10180 :         if((aPoly.HasFlags() && POLY_CONTROL == aPoly.GetFlags(nPolySize - 1))
    1456        5865 :             || (aPoly.GetPoint(nPolySize - 1) != aPoly.GetPoint(0)))
    1457             :         {
    1458        3626 :             aPoly.SetSize(nPolySize + 1);
    1459        3626 :             aPoly.SetPoint(aPoly.GetPoint(0), nPolySize);
    1460             : 
    1461        3626 :             if(aPoly.HasFlags())
    1462             :             {
    1463        2742 :                 aPoly.SetFlags(nPolySize, POLY_NORMAL);
    1464             :             }
    1465             :         }
    1466             :     }
    1467             : 
    1468        3712 :     mrPolyPoly.Insert( aPoly );
    1469        3712 :     mnPoints = 0;
    1470        3712 :     bHasOffline = false;
    1471             : }
    1472             : 
    1473             : extern "C" {
    1474             : 
    1475             : // TODO: wait till all compilers accept that calling conventions
    1476             : // for functions are the same independent of implementation constness,
    1477             : // then uncomment the const-tokens in the function interfaces below
    1478        3712 : static int FT_move_to( FT_Vector_CPtr p0, void* vpPolyArgs )
    1479             : {
    1480        3712 :     PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
    1481             : 
    1482             :     // move_to implies a new polygon => finish old polygon first
    1483        3712 :     rA.ClosePolygon();
    1484             : 
    1485        3712 :     rA.AddPoint( p0->x, p0->y, POLY_NORMAL );
    1486        3712 :     return 0;
    1487             : }
    1488             : 
    1489       21413 : static int FT_line_to( FT_Vector_CPtr p1, void* vpPolyArgs )
    1490             : {
    1491       21413 :     PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
    1492       21413 :     rA.AddPoint( p1->x, p1->y, POLY_NORMAL );
    1493       21413 :     return 0;
    1494             : }
    1495             : 
    1496       29816 : static int FT_conic_to( FT_Vector_CPtr p1, FT_Vector_CPtr p2, void* vpPolyArgs )
    1497             : {
    1498       29816 :     PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
    1499             : 
    1500             :     // VCL's Polygon only knows cubic beziers
    1501       29816 :     const long nX1 = (2 * rA.GetPosX() + 4 * p1->x + 3) / 6;
    1502       29816 :     const long nY1 = (2 * rA.GetPosY() + 4 * p1->y + 3) / 6;
    1503       29816 :     rA.AddPoint( nX1, nY1, POLY_CONTROL );
    1504             : 
    1505       29816 :     const long nX2 = (2 * p2->x + 4 * p1->x + 3) / 6;
    1506       29816 :     const long nY2 = (2 * p2->y + 4 * p1->y + 3) / 6;
    1507       29816 :     rA.AddPoint( nX2, nY2, POLY_CONTROL );
    1508             : 
    1509       29816 :     rA.AddPoint( p2->x, p2->y, POLY_NORMAL );
    1510       29816 :     return 0;
    1511             : }
    1512             : 
    1513         558 : static int FT_cubic_to( FT_Vector_CPtr p1, FT_Vector_CPtr p2, FT_Vector_CPtr p3, void* vpPolyArgs )
    1514             : {
    1515         558 :     PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
    1516         558 :     rA.AddPoint( p1->x, p1->y, POLY_CONTROL );
    1517         558 :     rA.AddPoint( p2->x, p2->y, POLY_CONTROL );
    1518         558 :     rA.AddPoint( p3->x, p3->y, POLY_NORMAL );
    1519         558 :     return 0;
    1520             : }
    1521             : 
    1522             : } // extern "C"
    1523             : 
    1524        2632 : bool ServerFont::GetGlyphOutline( sal_GlyphId aGlyphId,
    1525             :     ::basegfx::B2DPolyPolygon& rB2DPolyPoly ) const
    1526             : {
    1527        2632 :     if( maSizeFT )
    1528        2632 :         FT_Activate_Size( maSizeFT );
    1529             : 
    1530        2632 :     rB2DPolyPoly.clear();
    1531             : 
    1532             :     int nGlyphFlags;
    1533        2632 :     SplitGlyphFlags( *this, aGlyphId, nGlyphFlags );
    1534             : 
    1535        2632 :     FT_Int nLoadFlags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_TRANSFORM;
    1536             : 
    1537             : #ifdef FT_LOAD_TARGET_LIGHT
    1538             :     // enable "light hinting" if available
    1539        2632 :     nLoadFlags |= FT_LOAD_TARGET_LIGHT;
    1540             : #endif
    1541             : 
    1542        2632 :     FT_Error rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags );
    1543        2632 :     if( rc != FT_Err_Ok )
    1544           0 :         return false;
    1545             : 
    1546        2632 :     if (mbArtBold)
    1547           0 :         FT_GlyphSlot_Embolden(maFaceFT->glyph);
    1548             : 
    1549             :     FT_Glyph pGlyphFT;
    1550        2632 :     rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
    1551        2632 :     if( rc != FT_Err_Ok )
    1552           0 :         return false;
    1553             : 
    1554        2632 :     if( pGlyphFT->format != FT_GLYPH_FORMAT_OUTLINE )
    1555             :     {
    1556           0 :         FT_Done_Glyph( pGlyphFT );
    1557           0 :         return false;
    1558             :     }
    1559             : 
    1560        2632 :     if( mbArtItalic )
    1561             :     {
    1562             :         FT_Matrix aMatrix;
    1563           0 :         aMatrix.xx = aMatrix.yy = 0x10000L;
    1564           0 :         aMatrix.xy = 0x6000L, aMatrix.yx = 0;
    1565           0 :         FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
    1566             :     }
    1567             : 
    1568        2632 :     FT_Outline& rOutline = reinterpret_cast<FT_OutlineGlyphRec*>(pGlyphFT)->outline;
    1569        2632 :     if( !rOutline.n_points )    // blank glyphs are ok
    1570             :     {
    1571         155 :         FT_Done_Glyph( pGlyphFT );
    1572         155 :         return true;
    1573             :     }
    1574             : 
    1575        2477 :     long nMaxPoints = 1 + rOutline.n_points * 3;
    1576        2477 :     tools::PolyPolygon aToolPolyPolygon;
    1577        4954 :     PolyArgs aPolyArg( aToolPolyPolygon, nMaxPoints );
    1578             : 
    1579        2477 :     /*int nAngle =*/ ApplyGlyphTransform( nGlyphFlags, pGlyphFT, false );
    1580             : 
    1581             :     FT_Outline_Funcs aFuncs;
    1582        2477 :     aFuncs.move_to  = &FT_move_to;
    1583        2477 :     aFuncs.line_to  = &FT_line_to;
    1584        2477 :     aFuncs.conic_to = &FT_conic_to;
    1585        2477 :     aFuncs.cubic_to = &FT_cubic_to;
    1586        2477 :     aFuncs.shift    = 0;
    1587        2477 :     aFuncs.delta    = 0;
    1588        2477 :     rc = FT_Outline_Decompose( &rOutline, &aFuncs, (void*)&aPolyArg );
    1589        2477 :     aPolyArg.ClosePolygon();    // close last polygon
    1590        2477 :     FT_Done_Glyph( pGlyphFT );
    1591             : 
    1592             :     // convert to basegfx polypolygon
    1593             :     // TODO: get rid of the intermediate tools polypolygon
    1594        2477 :     rB2DPolyPoly = aToolPolyPolygon.getB2DPolyPolygon();
    1595        2477 :     rB2DPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix( +1.0/(1<<6), -1.0/(1<<6) ));
    1596             : 
    1597        4954 :     return true;
    1598             : }
    1599             : 
    1600        9613 : bool ServerFont::ApplyGSUB( const FontSelectPattern& rFSD )
    1601             : {
    1602             : #define MKTAG(s) ((((((s[0]<<8)+s[1])<<8)+s[2])<<8)+s[3])
    1603             : 
    1604             :     typedef std::vector<sal_uLong> ReqFeatureTagList;
    1605        9613 :     ReqFeatureTagList aReqFeatureTagList;
    1606        9613 :     if( rFSD.mbVertical )
    1607          12 :         aReqFeatureTagList.push_back( MKTAG("vert") );
    1608             :     // TODO: request more features depending on script and language system
    1609             : 
    1610        9613 :     if( aReqFeatureTagList.empty()) // nothing to do
    1611        9601 :         return true;
    1612             : 
    1613             :     // load GSUB table into memory
    1614          12 :     sal_uLong nLength = 0;
    1615          12 :     const FT_Byte* const pGsubBase = mpFontInfo->GetTable( "GSUB", &nLength );
    1616          12 :     if( !pGsubBase )
    1617           0 :         return false;
    1618             : 
    1619             :     // parse GSUB header
    1620          12 :     const FT_Byte* pGsubHeader = pGsubBase;
    1621          12 :     const sal_uInt16 nOfsScriptList     = GetUShort( pGsubHeader+4 );
    1622          12 :     const sal_uInt16 nOfsFeatureTable   = GetUShort( pGsubHeader+6 );
    1623          12 :     const sal_uInt16 nOfsLookupList     = GetUShort( pGsubHeader+8 );
    1624          12 :     pGsubHeader += 10;
    1625             : 
    1626             :     typedef std::vector<sal_uInt16> UshortList;
    1627          24 :     UshortList aFeatureIndexList;
    1628             : 
    1629             :     // parse Script Table
    1630          12 :     const FT_Byte* pScriptHeader = pGsubBase + nOfsScriptList;
    1631          12 :     const sal_uInt16 nCntScript = GetUShort( pScriptHeader+0 );
    1632          12 :     pScriptHeader += 2;
    1633          50 :     for( sal_uInt16 nScriptIndex = 0; nScriptIndex < nCntScript; ++nScriptIndex )
    1634             :     {
    1635          38 :         const sal_uInt16 nOfsScriptTable= GetUShort( pScriptHeader+4 );
    1636          38 :         pScriptHeader += 6;
    1637             : 
    1638          38 :         const FT_Byte* pScriptTable     = pGsubBase + nOfsScriptList + nOfsScriptTable;
    1639          38 :         const sal_uInt16 nDefaultLangsysOfs = GetUShort( pScriptTable+0 );
    1640          38 :         const sal_uInt16 nCntLangSystem     = GetUShort( pScriptTable+2 );
    1641          38 :         pScriptTable += 4;
    1642          38 :         sal_uInt16 nLangsysOffset = 0;
    1643             : 
    1644          38 :         if (nCntLangSystem != 0)
    1645             :         {
    1646          22 :             nLangsysOffset = GetUShort( pScriptTable+4 );
    1647             :         }
    1648             : 
    1649          38 :         if (nDefaultLangsysOfs != 0 && nDefaultLangsysOfs != nLangsysOffset)
    1650             :         {
    1651          36 :             const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nDefaultLangsysOfs;
    1652          36 :             const sal_uInt16 nReqFeatureIdx = GetUShort( pLangSys+2 );
    1653          36 :             const sal_uInt16 nCntFeature    = GetUShort( pLangSys+4 );
    1654          36 :             pLangSys += 6;
    1655          36 :             aFeatureIndexList.push_back( nReqFeatureIdx );
    1656         664 :             for( sal_uInt16 i = 0; i < nCntFeature; ++i )
    1657             :             {
    1658         628 :                 const sal_uInt16 nFeatureIndex = GetUShort( pLangSys );
    1659         628 :                 pLangSys += 2;
    1660         628 :                 aFeatureIndexList.push_back( nFeatureIndex );
    1661             :             }
    1662             :         }
    1663             : 
    1664          38 :         if( nLangsysOffset != 0 )
    1665             :         {
    1666          22 :             const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nLangsysOffset;
    1667          22 :             const sal_uInt16 nReqFeatureIdx = GetUShort( pLangSys+2 );
    1668          22 :             const sal_uInt16 nCntFeature    = GetUShort( pLangSys+4 );
    1669          22 :             pLangSys += 6;
    1670          22 :             aFeatureIndexList.push_back( nReqFeatureIdx );
    1671         314 :             for( sal_uInt16 i = 0; i < nCntFeature; ++i )
    1672             :             {
    1673         292 :                 const sal_uInt16 nFeatureIndex = GetUShort( pLangSys );
    1674         292 :                 pLangSys += 2;
    1675         292 :                 aFeatureIndexList.push_back( nFeatureIndex );
    1676             :             }
    1677             :         }
    1678             :     }
    1679             : 
    1680          12 :     if( aFeatureIndexList.empty() )
    1681           0 :         return true;
    1682             : 
    1683          24 :     UshortList aLookupIndexList;
    1684          24 :     UshortList aLookupOffsetList;
    1685             : 
    1686             :     // parse Feature Table
    1687          12 :     const FT_Byte* pFeatureHeader = pGsubBase + nOfsFeatureTable;
    1688          12 :     const sal_uInt16 nCntFeature = GetUShort( pFeatureHeader );
    1689          12 :     pFeatureHeader += 2;
    1690        1380 :     for( sal_uInt16 nFeatureIndex = 0; nFeatureIndex < nCntFeature; ++nFeatureIndex )
    1691             :     {
    1692        1368 :         const sal_uLong nTag    = GetUInt( pFeatureHeader+0 ); // e.g. locl/vert/trad/smpl/liga/fina/...
    1693        1368 :         const sal_uInt16 nOffset= GetUShort( pFeatureHeader+4 );
    1694        1368 :         pFeatureHeader += 6;
    1695             : 
    1696             :         // short circuit some feature lookups
    1697        1368 :         if( aFeatureIndexList[0] != nFeatureIndex ) // required feature?
    1698             :         {
    1699        1368 :             const int nRequested = std::count( aFeatureIndexList.begin(), aFeatureIndexList.end(), nFeatureIndex);
    1700        1368 :             if( !nRequested )  // ignore features that are not requested
    1701        1818 :                 continue;
    1702         918 :             const int nAvailable = std::count( aReqFeatureTagList.begin(), aReqFeatureTagList.end(), nTag);
    1703         918 :             if( !nAvailable )  // some fonts don't provide features they request!
    1704         918 :                 continue;
    1705             :         }
    1706             : 
    1707           0 :         const FT_Byte* pFeatureTable = pGsubBase + nOfsFeatureTable + nOffset;
    1708           0 :         const sal_uInt16 nCntLookups = GetUShort( pFeatureTable+0 );
    1709           0 :         pFeatureTable += 2;
    1710           0 :         for( sal_uInt16 i = 0; i < nCntLookups; ++i )
    1711             :         {
    1712           0 :             const sal_uInt16 nLookupIndex = GetUShort( pFeatureTable );
    1713           0 :             pFeatureTable += 2;
    1714           0 :             aLookupIndexList.push_back( nLookupIndex );
    1715             :         }
    1716           0 :         if( nCntLookups == 0 ) //### hack needed by Mincho/Gothic/Mingliu/Simsun/...
    1717           0 :             aLookupIndexList.push_back( 0 );
    1718             :     }
    1719             : 
    1720             :     // parse Lookup List
    1721          12 :     const FT_Byte* pLookupHeader = pGsubBase + nOfsLookupList;
    1722          12 :     const sal_uInt16 nCntLookupTable = GetUShort( pLookupHeader );
    1723          12 :     pLookupHeader += 2;
    1724         424 :     for( sal_uInt16 nLookupIdx = 0; nLookupIdx < nCntLookupTable; ++nLookupIdx )
    1725             :     {
    1726         412 :         const sal_uInt16 nOffset = GetUShort( pLookupHeader );
    1727         412 :         pLookupHeader += 2;
    1728         412 :         if( std::count( aLookupIndexList.begin(), aLookupIndexList.end(), nLookupIdx ) )
    1729           0 :             aLookupOffsetList.push_back( nOffset );
    1730             :     }
    1731             : 
    1732          12 :     UshortList::const_iterator lookup_it = aLookupOffsetList.begin();
    1733          12 :     for(; lookup_it != aLookupOffsetList.end(); ++lookup_it )
    1734             :     {
    1735           0 :         const sal_uInt16 nOfsLookupTable = *lookup_it;
    1736           0 :         const FT_Byte* pLookupTable = pGsubBase + nOfsLookupList + nOfsLookupTable;
    1737           0 :         const sal_uInt16 eLookupType        = GetUShort( pLookupTable+0 );
    1738           0 :         const sal_uInt16 nCntLookupSubtable = GetUShort( pLookupTable+4 );
    1739           0 :         pLookupTable += 6;
    1740             : 
    1741             :         // TODO: switch( eLookupType )
    1742           0 :         if( eLookupType != 1 )  // TODO: once we go beyond SingleSubst
    1743           0 :             continue;
    1744             : 
    1745           0 :         for( sal_uInt16 nSubTableIdx = 0; nSubTableIdx < nCntLookupSubtable; ++nSubTableIdx )
    1746             :         {
    1747           0 :             const sal_uInt16 nOfsSubLookupTable = GetUShort( pLookupTable );
    1748           0 :             pLookupTable += 2;
    1749           0 :             const FT_Byte* pSubLookup = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable;
    1750             : 
    1751           0 :             const sal_uInt16 nFmtSubstitution   = GetUShort( pSubLookup+0 );
    1752           0 :             const sal_uInt16 nOfsCoverage       = GetUShort( pSubLookup+2 );
    1753           0 :             pSubLookup += 4;
    1754             : 
    1755             :             typedef std::pair<sal_uInt16,sal_uInt16> GlyphSubst;
    1756             :             typedef std::vector<GlyphSubst> SubstVector;
    1757           0 :             SubstVector aSubstVector;
    1758             : 
    1759           0 :             const FT_Byte* pCoverage    = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable + nOfsCoverage;
    1760           0 :             const sal_uInt16 nFmtCoverage   = GetUShort( pCoverage+0 );
    1761           0 :             pCoverage += 2;
    1762           0 :             switch( nFmtCoverage )
    1763             :             {
    1764             :                 case 1:         // Coverage Format 1
    1765             :                     {
    1766           0 :                         const sal_uInt16 nCntGlyph = GetUShort( pCoverage );
    1767           0 :                         pCoverage += 2;
    1768           0 :                         aSubstVector.reserve( nCntGlyph );
    1769           0 :                         for( sal_uInt16 i = 0; i < nCntGlyph; ++i )
    1770             :                         {
    1771           0 :                             const sal_uInt16 nGlyphId = GetUShort( pCoverage );
    1772           0 :                             pCoverage += 2;
    1773           0 :                             aSubstVector.push_back( GlyphSubst( nGlyphId, 0 ) );
    1774             :                         }
    1775             :                     }
    1776           0 :                     break;
    1777             : 
    1778             :                 case 2:         // Coverage Format 2
    1779             :                     {
    1780           0 :                         const sal_uInt16 nCntRange = GetUShort( pCoverage );
    1781           0 :                         pCoverage += 2;
    1782           0 :                         for( int i = nCntRange; --i >= 0; )
    1783             :                         {
    1784           0 :                             const sal_uInt32 nGlyph0 = GetUShort( pCoverage+0 );
    1785           0 :                             const sal_uInt32 nGlyph1 = GetUShort( pCoverage+2 );
    1786           0 :                             const sal_uInt16 nCovIdx = GetUShort( pCoverage+4 );
    1787           0 :                             pCoverage += 6;
    1788           0 :                             for( sal_uInt32 j = nGlyph0; j <= nGlyph1; ++j )
    1789           0 :                                 aSubstVector.push_back( GlyphSubst( static_cast<sal_uInt16>(j + nCovIdx), 0 ) );
    1790             :                         }
    1791             :                     }
    1792           0 :                     break;
    1793             :             }
    1794             : 
    1795           0 :             SubstVector::iterator it( aSubstVector.begin() );
    1796             : 
    1797           0 :             switch( nFmtSubstitution )
    1798             :             {
    1799             :                 case 1:     // Single Substitution Format 1
    1800             :                     {
    1801           0 :                         const sal_uInt16 nDeltaGlyphId = GetUShort( pSubLookup );
    1802           0 :                         for(; it != aSubstVector.end(); ++it )
    1803           0 :                             (*it).second = (*it).first + nDeltaGlyphId;
    1804             :                     }
    1805           0 :                     break;
    1806             : 
    1807             :                 case 2:     // Single Substitution Format 2
    1808             :                     {
    1809           0 :                         const sal_uInt16 nCntGlyph = GetUShort( pSubLookup );
    1810           0 :                         pSubLookup += 2;
    1811           0 :                         for( int i = nCntGlyph; (it != aSubstVector.end()) && (--i>=0); ++it )
    1812             :                         {
    1813           0 :                             const sal_uInt16 nGlyphId = GetUShort( pSubLookup );
    1814           0 :                             pSubLookup += 2;
    1815           0 :                             (*it).second = nGlyphId;
    1816             :                         }
    1817             :                     }
    1818           0 :                     break;
    1819             :             }
    1820             : 
    1821             :             DBG_ASSERT( (it == aSubstVector.end()), "lookup<->coverage table mismatch" );
    1822             :             // now apply the glyph substitutions that have been collected in this subtable
    1823           0 :             for( it = aSubstVector.begin(); it != aSubstVector.end(); ++it )
    1824           0 :                 maGlyphSubstitution[ (*it).first ] =  (*it).second;
    1825           0 :         }
    1826             :     }
    1827             : 
    1828        9625 :     return true;
    1829             : }
    1830             : 
    1831       29926 : const unsigned char* ServerFont::GetTable(const char* pName, sal_uLong* pLength)
    1832             : {
    1833       29926 :     return mpFontInfo->GetTable( pName, pLength );
    1834             : }
    1835             : 
    1836             : #if ENABLE_GRAPHITE
    1837        6073 : GraphiteFaceWrapper* ServerFont::GetGraphiteFace() const
    1838             : {
    1839        6073 :     return mpFontInfo->GetGraphiteFace();
    1840        1233 : }
    1841             : #endif
    1842             : 
    1843             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10