Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / generic / glyphs / gcach_ftyp.cxx
blobd6a2116321f85f7fa2d58336ce744d990c68e2bf
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "gcach_ftyp.hxx"
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>
34 #include "tools/poly.hxx"
35 #include "basegfx/matrix/b2dhommatrix.hxx"
36 #include "basegfx/matrix/b2dhommatrixtools.hxx"
37 #include "basegfx/polygon/b2dpolypolygon.hxx"
39 #include "osl/file.hxx"
40 #include "osl/thread.hxx"
42 #include "langboost.hxx"
43 #include "PhysicalFontCollection.hxx"
44 #include "sft.hxx"
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
56 #include "rtl/instance.hxx"
58 typedef const FT_Vector* FT_Vector_CPtr;
60 #include <vector>
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"
69 // the gamma table makes artificial bold look better for CJK glyphs
70 static unsigned char aGammaTable[257];
72 static void InitGammaTable()
74 static const int M_MAX = 255;
75 static const int M_X = 128;
76 static const int M_Y = 208;
78 int x, a;
79 for( x = 0; x < 256; x++)
81 if ( x <= M_X )
82 a = ( x * M_Y + M_X / 2) / M_X;
83 else
84 a = M_Y + ( ( x - M_X ) * ( M_MAX - M_Y ) +
85 ( M_MAX - M_X ) / 2 ) / ( M_MAX - M_X );
87 aGammaTable[x] = (unsigned char)a;
91 static FT_Library aLibFT = 0;
93 // enable linking with old FT versions
94 static int nFTVERSION = 0;
96 typedef std::unordered_map<const char*, std::shared_ptr<FtFontFile>, rtl::CStringHash, rtl::CStringEqual> FontFileList;
98 namespace { struct vclFontFileList : public rtl::Static< FontFileList, vclFontFileList > {}; }
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;
111 // FreetypeManager
113 FtFontFile::FtFontFile( const OString& rNativeFileName )
114 : maNativeFileName( rNativeFileName ),
115 mpFileMap( NULL ),
116 mnFileSize( 0 ),
117 mnRefCount( 0 ),
118 mnLangBoost( 0 )
120 // boost font preference if UI language is mentioned in filename
121 int nPos = maNativeFileName.lastIndexOf( '_' );
122 if( nPos == -1 || maNativeFileName[nPos+1] == '.' )
123 mnLangBoost += 0x1000; // no langinfo => good
124 else
126 static const char* pLangBoost = NULL;
127 static bool bOnce = true;
128 if( bOnce )
130 bOnce = false;
131 pLangBoost = vcl::getLangBoost();
134 if( pLangBoost && !strncasecmp( pLangBoost, &maNativeFileName.getStr()[nPos+1], 3 ) )
135 mnLangBoost += 0x2000; // matching langinfo => better
139 FtFontFile* FtFontFile::FindFontFile( const OString& rNativeFileName )
141 // font file already known? (e.g. for ttc, synthetic, aliased fonts)
142 const char* pFileName = rNativeFileName.getStr();
143 FontFileList &rFontFileList = vclFontFileList::get();
144 FontFileList::const_iterator it = rFontFileList.find( pFileName );
145 if( it != rFontFileList.end() )
146 return it->second.get();
148 // no => create new one
149 FtFontFile* pFontFile = new FtFontFile( rNativeFileName );
150 pFileName = pFontFile->maNativeFileName.getStr();
151 rFontFileList[pFileName].reset(pFontFile);
152 return pFontFile;
155 bool FtFontFile::Map()
157 if( mnRefCount++ <= 0 )
159 const char* pFileName = maNativeFileName.getStr();
160 int nFile = open( pFileName, O_RDONLY );
161 if( nFile < 0 )
162 return false;
164 struct stat aStat;
165 int nRet = fstat( nFile, &aStat );
166 if (nRet < 0)
168 close (nFile);
169 return false;
171 mnFileSize = aStat.st_size;
172 mpFileMap = static_cast<unsigned char*>(
173 mmap( NULL, mnFileSize, PROT_READ, MAP_SHARED, nFile, 0 ));
174 if( mpFileMap == MAP_FAILED )
175 mpFileMap = NULL;
176 close( nFile );
179 return (mpFileMap != NULL);
182 void FtFontFile::Unmap()
184 if( (--mnRefCount > 0) || (mpFileMap == NULL) )
185 return;
187 munmap( mpFileMap, mnFileSize );
188 mpFileMap = NULL;
191 #if ENABLE_GRAPHITE
192 // wrap FtFontInfo's table function
193 const void * graphiteFontTable(const void* appFaceHandle, unsigned int name, size_t *len)
195 const FtFontInfo * pFontInfo = static_cast<const FtFontInfo*>(appFaceHandle);
196 typedef union {
197 char m_c[5];
198 unsigned int m_id;
199 } TableId;
200 TableId tableId;
201 tableId.m_id = name;
202 #ifndef OSL_BIGENDIAN
203 TableId swapped;
204 swapped.m_c[3] = tableId.m_c[0];
205 swapped.m_c[2] = tableId.m_c[1];
206 swapped.m_c[1] = tableId.m_c[2];
207 swapped.m_c[0] = tableId.m_c[3];
208 tableId.m_id = swapped.m_id;
209 #endif
210 tableId.m_c[4] = '\0';
211 sal_uLong nLength = 0;
212 const void * pTable = static_cast<const void*>(pFontInfo->GetTable(tableId.m_c, &nLength));
213 if (len) *len = static_cast<size_t>(nLength);
214 return pTable;
216 #endif
218 FtFontInfo::FtFontInfo( const ImplDevFontAttributes& rDevFontAttributes,
219 const OString& rNativeFileName, int nFaceNum, sal_IntPtr nFontId, int nSynthetic)
221 maFaceFT( NULL ),
222 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 mpGlyph2Char( NULL )
236 // prefer font with low ID
237 maDevFontAttributes.mnQuality += 10000 - nFontId;
238 // prefer font with matching file names
239 maDevFontAttributes.mnQuality += mpFontFile->GetLangBoost();
242 FtFontInfo::~FtFontInfo()
244 if( mpFontCharMap )
245 mpFontCharMap = 0;
246 delete mpChar2Glyph;
247 delete mpGlyph2Char;
248 #if ENABLE_GRAPHITE
249 delete mpGraphiteFace;
250 #endif
253 void FtFontInfo::InitHashes() const
255 // TODO: avoid pointers when empty stl::hash_* objects become cheap
256 mpChar2Glyph = new Int2IntMap();
257 mpGlyph2Char = new Int2IntMap();
260 FT_FaceRec_* FtFontInfo::GetFaceFT()
262 if (!maFaceFT && mpFontFile->Map())
264 FT_Error rc = FT_New_Memory_Face( aLibFT,
265 mpFontFile->GetBuffer(),
266 mpFontFile->GetFileSize(), mnFaceNum, &maFaceFT );
267 if( (rc != FT_Err_Ok) || (maFaceFT->num_glyphs <= 0) )
268 maFaceFT = NULL;
271 ++mnRefCount;
272 return maFaceFT;
275 #if ENABLE_GRAPHITE
276 GraphiteFaceWrapper * FtFontInfo::GetGraphiteFace()
278 if (mbCheckedGraphite)
279 return mpGraphiteFace;
280 // test for graphite here so that it is cached most efficiently
281 if (GetTable("Silf", 0))
283 static const char* pGraphiteCacheStr = getenv( "SAL_GRAPHITE_CACHE_SIZE" );
284 int graphiteSegCacheSize = pGraphiteCacheStr ? (atoi(pGraphiteCacheStr)) : 0;
285 gr_face * pGraphiteFace;
286 if (graphiteSegCacheSize > 500)
287 pGraphiteFace = gr_make_face_with_seg_cache(this, graphiteFontTable, graphiteSegCacheSize, gr_face_cacheCmap);
288 else
289 pGraphiteFace = gr_make_face(this, graphiteFontTable, gr_face_cacheCmap);
290 if (pGraphiteFace)
291 mpGraphiteFace = new GraphiteFaceWrapper(pGraphiteFace);
293 mbCheckedGraphite = true;
294 return mpGraphiteFace;
296 #endif
298 void FtFontInfo::ReleaseFaceFT()
300 if (--mnRefCount <= 0)
302 FT_Done_Face( maFaceFT );
303 maFaceFT = NULL;
304 mpFontFile->Unmap();
308 static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);}
309 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]));}
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' */
316 const unsigned char* FtFontInfo::GetTable( const char* pTag, sal_uLong* pLength ) const
318 const unsigned char* pBuffer = mpFontFile->GetBuffer();
319 int nFileSize = mpFontFile->GetFileSize();
320 if( !pBuffer || nFileSize<1024 )
321 return NULL;
323 // we currently handle TTF, TTC and OTF headers
324 unsigned nFormat = GetUInt( pBuffer );
326 const unsigned char* p = pBuffer + 12;
327 if( nFormat == T_ttcf ) // TTC_MAGIC
328 p += GetUInt( p + 4 * mnFaceNum );
329 else if( nFormat != 0x00010000 && nFormat != T_true && nFormat != T_otto) // TTF_MAGIC and Apple TTF Magic and PS-OpenType font
330 return NULL;
332 // walk table directory until match
333 int nTables = GetUShort( p - 8 );
334 if( nTables >= 64 ) // something fishy?
335 return NULL;
336 for( int i = 0; i < nTables; ++i, p+=16 )
338 if( p[0]==pTag[0] && p[1]==pTag[1] && p[2]==pTag[2] && p[3]==pTag[3] )
340 sal_uLong nLength = GetUInt( p + 12 );
341 if( pLength != NULL )
342 *pLength = nLength;
343 const unsigned char* pTable = pBuffer + GetUInt( p + 8 );
344 if( (pTable + nLength) <= (mpFontFile->GetBuffer() + nFileSize) )
345 return pTable;
349 return NULL;
352 void FtFontInfo::AnnounceFont( PhysicalFontCollection* pFontCollection )
354 ImplFTSFontData* pFD = new ImplFTSFontData( this, maDevFontAttributes );
355 pFontCollection->Add( pFD );
358 FreetypeManager::FreetypeManager()
359 : mnMaxFontId( 0 )
361 /*FT_Error rcFT =*/ FT_Init_FreeType( &aLibFT );
363 FT_Int nMajor = 0, nMinor = 0, nPatch = 0;
364 FT_Library_Version(aLibFT, &nMajor, &nMinor, &nPatch);
365 nFTVERSION = nMajor * 1000 + nMinor * 100 + nPatch;
367 // TODO: remove when the priorities are selected by UI
368 char* pEnv;
369 pEnv = ::getenv( "SAL_EMBEDDED_BITMAP_PRIORITY" );
370 if( pEnv )
371 nDefaultPrioEmbedded = pEnv[0] - '0';
372 pEnv = ::getenv( "SAL_ANTIALIASED_TEXT_PRIORITY" );
373 if( pEnv )
374 nDefaultPrioAntiAlias = pEnv[0] - '0';
375 pEnv = ::getenv( "SAL_AUTOHINTING_PRIORITY" );
376 if( pEnv )
377 nDefaultPrioAutoHint = pEnv[0] - '0';
379 InitGammaTable();
380 vclFontFileList::get();
383 FT_Face ServerFont::GetFtFace() const
385 FT_Activate_Size( maSizeFT );
387 return maFaceFT;
390 FreetypeManager::~FreetypeManager()
392 ClearFontList();
395 void FreetypeManager::AddFontFile( const OString& rNormalizedName,
396 int nFaceNum, sal_IntPtr nFontId, const ImplDevFontAttributes& rDevFontAttr)
398 if( rNormalizedName.isEmpty() )
399 return;
401 if( maFontList.find( nFontId ) != maFontList.end() )
402 return;
404 FtFontInfo* pFontInfo = new FtFontInfo( rDevFontAttr,
405 rNormalizedName, nFaceNum, nFontId, 0);
406 maFontList[ nFontId ] = pFontInfo;
407 if( mnMaxFontId < nFontId )
408 mnMaxFontId = nFontId;
411 void FreetypeManager::AnnounceFonts( PhysicalFontCollection* pToAdd ) const
413 for( FontList::const_iterator it = maFontList.begin(); it != maFontList.end(); ++it )
415 FtFontInfo* pFtFontInfo = it->second;
416 pFtFontInfo->AnnounceFont( pToAdd );
420 void FreetypeManager::ClearFontList( )
422 for( FontList::iterator it = maFontList.begin(); it != maFontList.end(); ++it )
424 FtFontInfo* pFtFontInfo = it->second;
425 delete pFtFontInfo;
427 maFontList.clear();
430 ServerFont* FreetypeManager::CreateFont( const FontSelectPattern& rFSD )
432 FtFontInfo* pFontInfo = NULL;
434 // find a FontInfo matching to the font id
435 sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFSD.mpFontData );
436 FontList::iterator it = maFontList.find( nFontId );
437 if( it != maFontList.end() )
438 pFontInfo = it->second;
440 if( !pFontInfo )
441 return NULL;
443 ServerFont* pNew = new ServerFont( rFSD, pFontInfo );
445 return pNew;
448 ImplFTSFontData::ImplFTSFontData( FtFontInfo* pFI, const ImplDevFontAttributes& rDFA )
449 : PhysicalFontFace( rDFA, IFTSFONT_MAGIC ),
450 mpFtFontInfo( pFI )
452 mbDevice = false;
453 mbOrientation = true;
456 ImplFontEntry* ImplFTSFontData::CreateFontInstance( FontSelectPattern& rFSD ) const
458 ImplServerFontEntry* pEntry = new ImplServerFontEntry( rFSD );
459 return pEntry;
462 // ServerFont
464 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 mpLayoutEngine( NULL )
487 // TODO: move update of mpFontEntry into FontEntry class when
488 // it becomes responsible for the ServerFont instantiation
489 static_cast<ImplServerFontEntry*>(rFSD.mpFontEntry)->SetServerFont( this );
491 maFaceFT = pFI->GetFaceFT();
493 if( rFSD.mnOrientation != 0 )
495 const double dRad = rFSD.mnOrientation * ( F_2PI / 3600.0 );
496 mnCos = static_cast<long>( 0x10000 * cos( dRad ) + 0.5 );
497 mnSin = static_cast<long>( 0x10000 * sin( dRad ) + 0.5 );
500 // set the pixel size of the font instance
501 mnWidth = rFSD.mnWidth;
502 if( !mnWidth )
503 mnWidth = rFSD.mnHeight;
504 mfStretch = (double)mnWidth / rFSD.mnHeight;
505 // sanity check (e.g. #i66394#, #i66244#, #66537#)
506 if( (mnWidth < 0) || (mfStretch > +64.0) || (mfStretch < -64.0) )
507 return;
509 if( !maFaceFT )
510 return;
512 FT_New_Size( maFaceFT, &maSizeFT );
513 FT_Activate_Size( maSizeFT );
514 FT_Error rc = FT_Set_Pixel_Sizes( maFaceFT, mnWidth, rFSD.mnHeight );
515 if( rc != FT_Err_Ok )
516 return;
518 FT_Select_Charmap(maFaceFT, FT_ENCODING_UNICODE);
520 if( mpFontInfo->IsSymbolFont() )
522 FT_Encoding eEncoding = FT_ENCODING_MS_SYMBOL;
523 if (!FT_IS_SFNT(maFaceFT))
524 eEncoding = FT_ENCODING_ADOBE_CUSTOM; // freetype wants this for PS symbol fonts
526 FT_Select_Charmap(maFaceFT, eEncoding);
529 mbFaceOk = true;
531 ApplyGSUB( rFSD );
533 // TODO: query GASP table for load flags
534 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 mnLoadFlags |= FT_LOAD_IGNORE_TRANSFORM;
538 #endif
540 mbArtItalic = (rFSD.GetSlant() != ITALIC_NONE && pFI->GetFontAttributes().GetSlant() == ITALIC_NONE);
541 mbArtBold = (rFSD.GetWeight() > WEIGHT_MEDIUM && pFI->GetFontAttributes().GetWeight() <= WEIGHT_MEDIUM);
542 if( mbArtBold )
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 const TT_OS2* pOs2 = static_cast<const TT_OS2*>(FT_Get_Sfnt_Table( maFaceFT, ft_sfnt_os2 ));
552 if ((pOs2) && (pOs2->ulCodePageRange1 & TT_CODEPAGE_RANGES1_CJKT )
553 && rFSD.mnHeight < 20)
554 mbUseGamma = true;
557 if( ((mnCos != 0) && (mnSin != 0)) || (mnPrioEmbedded <= 0) )
558 mnLoadFlags |= FT_LOAD_NO_BITMAP;
561 void ServerFont::SetFontOptions(std::shared_ptr<ImplFontOptions> xFontOptions)
563 mxFontOptions = xFontOptions;
565 if (!mxFontOptions)
566 return;
568 FontAutoHint eHint = mxFontOptions->GetUseAutoHint();
569 if( eHint == AUTOHINT_DONTKNOW )
570 eHint = mbUseGamma ? AUTOHINT_TRUE : AUTOHINT_FALSE;
572 if( eHint == AUTOHINT_TRUE )
573 mnLoadFlags |= FT_LOAD_FORCE_AUTOHINT;
575 if( (mnSin != 0) && (mnCos != 0) ) // hinting for 0/90/180/270 degrees only
576 mnLoadFlags |= FT_LOAD_NO_HINTING;
577 mnLoadFlags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; //#88334#
579 if (mxFontOptions->DontUseAntiAlias())
580 mnPrioAntiAlias = 0;
581 if (mxFontOptions->DontUseEmbeddedBitmaps())
582 mnPrioEmbedded = 0;
583 if (mxFontOptions->DontUseHinting())
584 mnPrioAutoHint = 0;
586 if( mnPrioAutoHint <= 0 )
587 mnLoadFlags |= FT_LOAD_NO_HINTING;
589 #if defined(FT_LOAD_TARGET_LIGHT) && defined(FT_LOAD_TARGET_NORMAL)
590 if( !(mnLoadFlags & FT_LOAD_NO_HINTING) )
592 mnLoadFlags |= FT_LOAD_TARGET_NORMAL;
593 switch (mxFontOptions->GetHintStyle())
595 case HINT_NONE:
596 mnLoadFlags |= FT_LOAD_NO_HINTING;
597 break;
598 case HINT_SLIGHT:
599 mnLoadFlags |= FT_LOAD_TARGET_LIGHT;
600 break;
601 case HINT_MEDIUM:
602 break;
603 case HINT_FULL:
604 default:
605 break;
608 #endif
610 if( mnPrioEmbedded <= 0 )
611 mnLoadFlags |= FT_LOAD_NO_BITMAP;
614 std::shared_ptr<ImplFontOptions> ServerFont::GetFontOptions() const
616 return mxFontOptions;
619 const OString& ServerFont::GetFontFileName() const
621 return mpFontInfo->GetFontFileName();
625 ServerFont::~ServerFont()
627 delete mpLayoutEngine;
629 if( maSizeFT )
630 FT_Done_Size( maSizeFT );
632 mpFontInfo->ReleaseFaceFT();
634 ReleaseFromGarbageCollect();
638 void ServerFont::FetchFontMetric( ImplFontMetricData& rTo, long& rFactor ) const
640 static_cast<ImplFontAttributes&>(rTo) = mpFontInfo->GetFontAttributes();
642 rTo.mbScalableFont = true;
643 rTo.mbDevice = true;
644 rTo.mbKernableFont = FT_HAS_KERNING( maFaceFT ) != 0;
645 rTo.mnOrientation = GetFontSelData().mnOrientation;
647 //Always consider [star]symbol as symbol fonts
648 if ( IsStarSymbol( rTo.GetFamilyName() ) )
649 rTo.SetSymbolFlag( true );
651 FT_Activate_Size( maSizeFT );
653 rFactor = 0x100;
655 const TT_OS2* pOS2 = static_cast<const TT_OS2*>(FT_Get_Sfnt_Table( maFaceFT, ft_sfnt_os2 ));
656 const double fScale = (double)GetFontSelData().mnHeight / maFaceFT->units_per_EM;
658 rTo.mnAscent = 0;
659 rTo.mnDescent = 0;
660 rTo.mnExtLeading = 0;
661 rTo.mnSlant = 0;
662 rTo.mnWidth = mnWidth;
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 if (nFTVERSION >= 2406)
672 const FT_Size_Metrics& rMetrics = maFaceFT->size->metrics;
673 rTo.mnAscent = (rMetrics.ascender + 32) >> 6;
674 rTo.mnDescent = (-rMetrics.descender + 32) >> 6;
675 rTo.mnExtLeading = ((rMetrics.height + 32) >> 6) - (rTo.mnAscent + rTo.mnDescent);
677 else
679 const TT_HoriHeader* pHHea = static_cast<const TT_HoriHeader*>(FT_Get_Sfnt_Table(maFaceFT, ft_sfnt_hhea));
680 if (pHHea)
682 rTo.mnAscent = pHHea->Ascender * fScale + 0.5;
683 rTo.mnDescent = -pHHea->Descender * fScale + 0.5;
684 rTo.mnExtLeading = pHHea->Line_Gap * fScale + 0.5;
687 if (!(rTo.mnAscent || rTo.mnDescent))
689 if (pOS2 && (pOS2->version != 0xFFFF))
691 if (pOS2->sTypoAscender || pOS2->sTypoDescender)
693 rTo.mnAscent = pOS2->sTypoAscender * fScale + 0.5;
694 rTo.mnDescent = -pOS2->sTypoDescender * fScale + 0.5;
695 rTo.mnExtLeading = pOS2->sTypoLineGap * fScale + 0.5;
697 else
699 rTo.mnAscent = pOS2->usWinAscent * fScale + 0.5;
700 rTo.mnDescent = pOS2->usWinDescent * fScale + 0.5;
701 rTo.mnExtLeading = 0;
706 if (!(rTo.mnAscent || rTo.mnDescent))
708 const FT_Size_Metrics& rMetrics = maFaceFT->size->metrics;
709 rTo.mnAscent = (rMetrics.ascender + 32) >> 6;
710 rTo.mnDescent = (-rMetrics.descender + 32) >> 6;
711 rTo.mnExtLeading = ((rMetrics.height + 32) >> 6) - (rTo.mnAscent + rTo.mnDescent);
715 rTo.mnIntLeading = rTo.mnAscent + rTo.mnDescent - (maFaceFT->units_per_EM * fScale + 0.5);
717 if( pOS2 && (pOS2->version != 0xFFFF) )
719 // map the panose info from the OS2 table to their VCL counterparts
720 switch( pOS2->panose[0] )
722 case 1: rTo.SetFamilyType( FAMILY_ROMAN ); break;
723 case 2: rTo.SetFamilyType( FAMILY_SWISS ); break;
724 case 3: rTo.SetFamilyType( FAMILY_MODERN ); break;
725 case 4: rTo.SetFamilyType( FAMILY_SCRIPT ); break;
726 case 5: rTo.SetFamilyType( FAMILY_DECORATIVE ); break;
727 // TODO: is it reasonable to override the attribute with DONTKNOW?
728 case 0: // fall through
729 default: rTo.meFamilyType = FAMILY_DONTKNOW; break;
732 switch( pOS2->panose[3] )
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 case 8: rTo.SetPitch( PITCH_VARIABLE ); break;
741 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 default: rTo.SetPitch( PITCH_DONTKNOW ); break;
749 // initialize kashida width
750 // TODO: what if there are different versions of this glyph available
751 const int nKashidaGlyphId = GetRawGlyphIndex( 0x0640 );
752 if( nKashidaGlyphId )
754 GlyphData aGlyphData;
755 InitGlyphData( nKashidaGlyphId, aGlyphData );
756 rTo.mnMinKashida = aGlyphData.GetMetric().GetCharWidth();
760 static inline void SplitGlyphFlags( const ServerFont& rFont, sal_GlyphId& rGlyphId, int& nGlyphFlags )
762 nGlyphFlags = rGlyphId & GF_FLAGMASK;
763 rGlyphId &= GF_IDXMASK;
765 if( rGlyphId & GF_ISCHAR )
766 rGlyphId = rFont.GetRawGlyphIndex( rGlyphId );
769 int ServerFont::ApplyGlyphTransform( int nGlyphFlags,
770 FT_Glyph pGlyphFT, bool bForBitmapProcessing ) const
772 int nAngle = GetFontSelData().mnOrientation;
773 // shortcut most common case
774 if( !nAngle && !nGlyphFlags )
775 return nAngle;
777 const FT_Size_Metrics& rMetrics = maFaceFT->size->metrics;
778 FT_Vector aVector;
779 FT_Matrix aMatrix;
781 bool bStretched = false;
783 switch( nGlyphFlags & GF_ROTMASK )
785 default: // straight
786 aVector.x = 0;
787 aVector.y = 0;
788 aMatrix.xx = +mnCos;
789 aMatrix.yy = +mnCos;
790 aMatrix.xy = -mnSin;
791 aMatrix.yx = +mnSin;
792 break;
793 case GF_ROTL: // left
794 nAngle += 900;
795 bStretched = (mfStretch != 1.0);
796 aVector.x = (FT_Pos)(+rMetrics.descender * mfStretch);
797 aVector.y = -rMetrics.ascender;
798 aMatrix.xx = (FT_Pos)(-mnSin / mfStretch);
799 aMatrix.yy = (FT_Pos)(-mnSin * mfStretch);
800 aMatrix.xy = (FT_Pos)(-mnCos * mfStretch);
801 aMatrix.yx = (FT_Pos)(+mnCos / mfStretch);
802 break;
803 case GF_ROTR: // right
804 nAngle -= 900;
805 bStretched = (mfStretch != 1.0);
806 aVector.x = -maFaceFT->glyph->metrics.horiAdvance;
807 aVector.x += (FT_Pos)(rMetrics.descender * mnSin/65536.0);
808 aVector.y = (FT_Pos)(-rMetrics.descender * mfStretch * mnCos/65536.0);
809 aMatrix.xx = (FT_Pos)(+mnSin / mfStretch);
810 aMatrix.yy = (FT_Pos)(+mnSin * mfStretch);
811 aMatrix.xy = (FT_Pos)(+mnCos * mfStretch);
812 aMatrix.yx = (FT_Pos)(-mnCos / mfStretch);
813 break;
816 while( nAngle < 0 )
817 nAngle += 3600;
819 if( pGlyphFT->format != FT_GLYPH_FORMAT_BITMAP )
821 FT_Glyph_Transform( pGlyphFT, NULL, &aVector );
823 // orthogonal transforms are better handled by bitmap operations
824 if( bStretched || (bForBitmapProcessing && (nAngle % 900) != 0) )
826 // apply non-orthogonal or stretch transformations
827 FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
828 nAngle = 0;
831 else
833 // FT<=2005 ignores transforms for bitmaps, so do it manually
834 FT_BitmapGlyph pBmpGlyphFT = reinterpret_cast<FT_BitmapGlyph>(pGlyphFT);
835 pBmpGlyphFT->left += (aVector.x + 32) >> 6;
836 pBmpGlyphFT->top += (aVector.y + 32) >> 6;
839 return nAngle;
842 sal_GlyphId ServerFont::GetRawGlyphIndex(sal_UCS4 aChar, sal_UCS4 aVS) const
844 if( mpFontInfo->IsSymbolFont() )
846 if( !FT_IS_SFNT( maFaceFT ) )
848 if( (aChar & 0xFF00) == 0xF000 )
849 aChar &= 0xFF; // PS font symbol mapping
850 else if( aChar > 0xFF )
851 return 0;
855 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 if (aVS && nFTVERSION >= 2404)
862 nGlyphIndex = FT_Face_GetCharVariantIndex(maFaceFT, aChar, aVS);
863 #endif
865 if (nGlyphIndex == 0)
867 // cache glyph indexes in font info to share between different sizes
868 nGlyphIndex = mpFontInfo->GetGlyphIndex( aChar );
869 if( nGlyphIndex < 0 )
871 nGlyphIndex = FT_Get_Char_Index( maFaceFT, aChar );
872 if( !nGlyphIndex)
874 // check if symbol aliasing helps
875 if( (aChar <= 0x00FF) && mpFontInfo->IsSymbolFont() )
876 nGlyphIndex = FT_Get_Char_Index( maFaceFT, aChar | 0xF000 );
878 mpFontInfo->CacheGlyphIndex( aChar, nGlyphIndex );
882 return sal_GlyphId( nGlyphIndex);
885 sal_GlyphId ServerFont::FixupGlyphIndex( sal_GlyphId aGlyphId, sal_UCS4 aChar ) const
887 int nGlyphFlags = GF_NONE;
889 // do glyph substitution if necessary
890 // CJK vertical writing needs special treatment
891 if( GetFontSelData().mbVertical )
893 // TODO: rethink when GSUB is used for non-vertical case
894 GlyphSubstitution::const_iterator it = maGlyphSubstitution.find( aGlyphId );
895 if( it == maGlyphSubstitution.end() )
897 nGlyphFlags |= GetVerticalFlags( aChar );
899 else
901 // for vertical GSUB also compensate for nOrientation=2700
902 aGlyphId = (*it).second;
903 nGlyphFlags |= GF_GSUB | GF_ROTL;
907 if( aGlyphId != 0 )
908 aGlyphId |= nGlyphFlags;
910 return aGlyphId;
913 sal_GlyphId ServerFont::GetGlyphIndex( sal_UCS4 aChar ) const
915 sal_GlyphId aGlyphId = GetRawGlyphIndex( aChar );
916 aGlyphId = FixupGlyphIndex( aGlyphId, aChar );
917 return aGlyphId;
920 static int lcl_GetCharWidth( FT_FaceRec_* pFaceFT, double fStretch, int nGlyphFlags )
922 int nCharWidth = pFaceFT->glyph->metrics.horiAdvance;
924 if( nGlyphFlags & GF_ROTMASK ) // for bVertical rotated glyphs
926 const FT_Size_Metrics& rMetrics = pFaceFT->size->metrics;
927 nCharWidth = (int)((rMetrics.height + rMetrics.descender) * fStretch);
930 return (nCharWidth + 32) >> 6;
933 void ServerFont::InitGlyphData( sal_GlyphId aGlyphId, GlyphData& rGD ) const
935 FT_Activate_Size( maSizeFT );
937 int nGlyphFlags;
938 SplitGlyphFlags( *this, aGlyphId, nGlyphFlags );
940 int nLoadFlags = mnLoadFlags;
942 // if( mbArtItalic )
943 // nLoadFlags |= FT_LOAD_NO_BITMAP;
945 FT_Error rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags );
947 if( rc != FT_Err_Ok )
949 // we get here e.g. when a PS font lacks the default glyph
950 rGD.SetCharWidth( 0 );
951 rGD.SetDelta( 0, 0 );
952 rGD.SetOffset( 0, 0 );
953 rGD.SetSize( Size( 0, 0 ) );
954 return;
957 const bool bOriginallyZeroWidth = (maFaceFT->glyph->metrics.horiAdvance == 0);
958 if (mbArtBold)
959 FT_GlyphSlot_Embolden(maFaceFT->glyph);
961 const int nCharWidth = bOriginallyZeroWidth ? 0 : lcl_GetCharWidth( maFaceFT, mfStretch, nGlyphFlags );
962 rGD.SetCharWidth( nCharWidth );
964 FT_Glyph pGlyphFT;
965 rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
967 ApplyGlyphTransform( nGlyphFlags, pGlyphFT, false );
968 rGD.SetDelta( (pGlyphFT->advance.x + 0x8000) >> 16, -((pGlyphFT->advance.y + 0x8000) >> 16) );
970 FT_BBox aBbox;
971 FT_Glyph_Get_CBox( pGlyphFT, FT_GLYPH_BBOX_PIXELS, &aBbox );
972 if( aBbox.yMin > aBbox.yMax ) // circumvent freetype bug
974 int t=aBbox.yMin; aBbox.yMin=aBbox.yMax, aBbox.yMax=t;
977 rGD.SetOffset( aBbox.xMin, -aBbox.yMax );
978 rGD.SetSize( Size( (aBbox.xMax-aBbox.xMin+1), (aBbox.yMax-aBbox.yMin) ) );
980 FT_Done_Glyph( pGlyphFT );
983 bool ServerFont::GetAntialiasAdvice() const
985 if( GetFontSelData().mbNonAntialiased || (mnPrioAntiAlias<=0) )
986 return false;
987 bool bAdviseAA = true;
988 // TODO: also use GASP info
989 return bAdviseAA;
992 bool ServerFont::GetGlyphBitmap1( sal_GlyphId aGlyphId, RawBitmap& rRawBitmap ) const
994 FT_Activate_Size( maSizeFT );
996 int nGlyphFlags;
997 SplitGlyphFlags( *this, aGlyphId, nGlyphFlags );
999 FT_Int nLoadFlags = mnLoadFlags;
1000 // #i70930# force mono-hinting for monochrome text
1001 nLoadFlags &= ~0xF0000;
1002 nLoadFlags |= FT_LOAD_TARGET_MONO;
1004 if( mbArtItalic )
1005 nLoadFlags |= FT_LOAD_NO_BITMAP;
1007 // for 0/90/180/270 degree fonts enable hinting even if not advisable
1008 // non-hinted and non-antialiased bitmaps just look too ugly
1009 if( (mnCos==0 || mnSin==0) && (mnPrioAutoHint > 0) )
1010 nLoadFlags &= ~FT_LOAD_NO_HINTING;
1012 if( mnPrioEmbedded <= mnPrioAutoHint )
1013 nLoadFlags |= FT_LOAD_NO_BITMAP;
1015 FT_Error rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags );
1017 if( rc != FT_Err_Ok )
1018 return false;
1020 if (mbArtBold)
1021 FT_GlyphSlot_Embolden(maFaceFT->glyph);
1023 FT_Glyph pGlyphFT;
1024 rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
1025 if( rc != FT_Err_Ok )
1026 return false;
1028 int nAngle = ApplyGlyphTransform( nGlyphFlags, pGlyphFT, true );
1030 if( mbArtItalic )
1032 FT_Matrix aMatrix;
1033 aMatrix.xx = aMatrix.yy = 0x10000L;
1034 aMatrix.xy = 0x6000L, aMatrix.yx = 0;
1035 FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
1038 // Check for zero area bounding boxes as this crashes some versions of FT.
1039 // This also provides a handy short cut as much of the code following
1040 // becomes an expensive nop when a glyph covers no pixels.
1041 FT_BBox cbox;
1042 FT_Glyph_Get_CBox(pGlyphFT, ft_glyph_bbox_unscaled, &cbox);
1044 if( (cbox.xMax - cbox.xMin) == 0 || (cbox.yMax - cbox.yMin == 0) )
1046 nAngle = 0;
1047 memset(&rRawBitmap, 0, sizeof rRawBitmap);
1048 FT_Done_Glyph( pGlyphFT );
1049 return true;
1052 if( pGlyphFT->format != FT_GLYPH_FORMAT_BITMAP )
1054 if( pGlyphFT->format == FT_GLYPH_FORMAT_OUTLINE )
1055 reinterpret_cast<FT_OutlineGlyphRec*>(pGlyphFT)->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
1056 FT_Render_Mode nRenderMode = FT_RENDER_MODE_MONO;
1058 rc = FT_Glyph_To_Bitmap( &pGlyphFT, nRenderMode, NULL, true );
1059 if( rc != FT_Err_Ok )
1061 FT_Done_Glyph( pGlyphFT );
1062 return false;
1066 const FT_BitmapGlyph pBmpGlyphFT = reinterpret_cast<const FT_BitmapGlyph>(pGlyphFT);
1067 // NOTE: autohinting in FT<=2.0.2 miscalculates the offsets below by +-1
1068 rRawBitmap.mnXOffset = +pBmpGlyphFT->left;
1069 rRawBitmap.mnYOffset = -pBmpGlyphFT->top;
1071 const FT_Bitmap& rBitmapFT = pBmpGlyphFT->bitmap;
1072 rRawBitmap.mnHeight = rBitmapFT.rows;
1073 rRawBitmap.mnBitCount = 1;
1074 rRawBitmap.mnWidth = rBitmapFT.width;
1075 rRawBitmap.mnScanlineSize = rBitmapFT.pitch;
1077 const sal_uLong nNeededSize = rRawBitmap.mnScanlineSize * rRawBitmap.mnHeight;
1079 if( rRawBitmap.mnAllocated < nNeededSize )
1081 rRawBitmap.mnAllocated = 2*nNeededSize;
1082 rRawBitmap.mpBits.reset(new unsigned char[ rRawBitmap.mnAllocated ]);
1085 if (!mbArtBold)
1087 memcpy( rRawBitmap.mpBits.get(), rBitmapFT.buffer, nNeededSize );
1089 else
1091 memset( rRawBitmap.mpBits.get(), 0, nNeededSize );
1092 const unsigned char* pSrcLine = rBitmapFT.buffer;
1093 unsigned char* pDstLine = rRawBitmap.mpBits.get();
1094 for( int h = rRawBitmap.mnHeight; --h >= 0; )
1096 memcpy( pDstLine, pSrcLine, rBitmapFT.pitch );
1097 pDstLine += rRawBitmap.mnScanlineSize;
1098 pSrcLine += rBitmapFT.pitch;
1101 unsigned char* p = rRawBitmap.mpBits.get();
1102 for( sal_uLong y=0; y < rRawBitmap.mnHeight; y++ )
1104 unsigned char nLastByte = 0;
1105 for( sal_uLong x=0; x < rRawBitmap.mnScanlineSize; x++ )
1107 unsigned char nTmp = p[x] << 7;
1108 p[x] |= (p[x] >> 1) | nLastByte;
1109 nLastByte = nTmp;
1111 p += rRawBitmap.mnScanlineSize;
1115 FT_Done_Glyph( pGlyphFT );
1117 // special case for 0/90/180/270 degree orientation
1118 switch( nAngle )
1120 case -900:
1121 case +900:
1122 case +1800:
1123 case +2700:
1124 rRawBitmap.Rotate( nAngle );
1125 break;
1128 return true;
1131 bool ServerFont::GetGlyphBitmap8( sal_GlyphId aGlyphId, RawBitmap& rRawBitmap ) const
1133 FT_Activate_Size( maSizeFT );
1135 int nGlyphFlags;
1136 SplitGlyphFlags( *this, aGlyphId, nGlyphFlags );
1138 FT_Int nLoadFlags = mnLoadFlags;
1140 if( mbArtItalic )
1141 nLoadFlags |= FT_LOAD_NO_BITMAP;
1143 if( (nGlyphFlags & GF_UNHINTED) || (mnPrioAutoHint < mnPrioAntiAlias) )
1144 nLoadFlags |= FT_LOAD_NO_HINTING;
1146 if( mnPrioEmbedded <= mnPrioAntiAlias )
1147 nLoadFlags |= FT_LOAD_NO_BITMAP;
1149 FT_Error rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags );
1151 if( rc != FT_Err_Ok )
1152 return false;
1154 if (mbArtBold)
1155 FT_GlyphSlot_Embolden(maFaceFT->glyph);
1157 FT_Glyph pGlyphFT;
1158 rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
1159 if( rc != FT_Err_Ok )
1160 return false;
1162 int nAngle = ApplyGlyphTransform( nGlyphFlags, pGlyphFT, true );
1164 if( mbArtItalic )
1166 FT_Matrix aMatrix;
1167 aMatrix.xx = aMatrix.yy = 0x10000L;
1168 aMatrix.xy = 0x6000L, aMatrix.yx = 0;
1169 FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
1172 if( pGlyphFT->format == FT_GLYPH_FORMAT_OUTLINE )
1173 reinterpret_cast<FT_OutlineGlyph>(pGlyphFT)->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
1175 bool bEmbedded = (pGlyphFT->format == FT_GLYPH_FORMAT_BITMAP);
1176 if( !bEmbedded )
1178 rc = FT_Glyph_To_Bitmap( &pGlyphFT, FT_RENDER_MODE_NORMAL, NULL, true );
1179 if( rc != FT_Err_Ok )
1181 FT_Done_Glyph( pGlyphFT );
1182 return false;
1186 const FT_BitmapGlyph pBmpGlyphFT = reinterpret_cast<const FT_BitmapGlyph>(pGlyphFT);
1187 rRawBitmap.mnXOffset = +pBmpGlyphFT->left;
1188 rRawBitmap.mnYOffset = -pBmpGlyphFT->top;
1190 const FT_Bitmap& rBitmapFT = pBmpGlyphFT->bitmap;
1191 rRawBitmap.mnHeight = rBitmapFT.rows;
1192 rRawBitmap.mnWidth = rBitmapFT.width;
1193 rRawBitmap.mnBitCount = 8;
1194 rRawBitmap.mnScanlineSize = bEmbedded ? rBitmapFT.width : rBitmapFT.pitch;
1195 rRawBitmap.mnScanlineSize = (rRawBitmap.mnScanlineSize + 3) & -4;
1197 const sal_uLong nNeededSize = rRawBitmap.mnScanlineSize * rRawBitmap.mnHeight;
1198 if( rRawBitmap.mnAllocated < nNeededSize )
1200 rRawBitmap.mnAllocated = 2*nNeededSize;
1201 rRawBitmap.mpBits.reset(new unsigned char[ rRawBitmap.mnAllocated ]);
1204 const unsigned char* pSrc = rBitmapFT.buffer;
1205 unsigned char* pDest = rRawBitmap.mpBits.get();
1206 if( !bEmbedded )
1208 unsigned int x;
1209 for( int y = rRawBitmap.mnHeight; --y >= 0 ; )
1211 for( x = 0; x < static_cast<unsigned int>(rBitmapFT.width); ++x )
1212 *(pDest++) = *(pSrc++);
1213 for(; x < rRawBitmap.mnScanlineSize; ++x )
1214 *(pDest++) = 0;
1217 else
1219 unsigned int x;
1220 for( int y = rRawBitmap.mnHeight; --y >= 0 ; )
1222 unsigned char nSrc = 0;
1223 for( x = 0; x < static_cast<unsigned int>(rBitmapFT.width); ++x, nSrc+=nSrc )
1225 if( (x & 7) == 0 )
1226 nSrc = *(pSrc++);
1227 *(pDest++) = (0x7F - nSrc) >> 8;
1229 for(; x < rRawBitmap.mnScanlineSize; ++x )
1230 *(pDest++) = 0;
1234 if( !bEmbedded && mbUseGamma )
1236 unsigned char* p = rRawBitmap.mpBits.get();
1237 for( sal_uLong y=0; y < rRawBitmap.mnHeight; y++ )
1239 for( sal_uLong x=0; x < rRawBitmap.mnWidth; x++ )
1241 p[x] = aGammaTable[ p[x] ];
1243 p += rRawBitmap.mnScanlineSize;
1247 FT_Done_Glyph( pGlyphFT );
1249 // special case for 0/90/180/270 degree orientation
1250 switch( nAngle )
1252 case -900:
1253 case +900:
1254 case +1800:
1255 case +2700:
1256 rRawBitmap.Rotate( nAngle );
1257 break;
1260 return true;
1263 // determine unicode ranges in font
1265 const FontCharMapPtr ServerFont::GetFontCharMap() const
1267 const FontCharMapPtr pFCMap = mpFontInfo->GetFontCharMap();
1268 return pFCMap;
1271 const FontCharMapPtr FtFontInfo::GetFontCharMap()
1273 // check if the charmap is already cached
1274 if( mpFontCharMap )
1275 return mpFontCharMap;
1277 // get the charmap and cache it
1278 CmapResult aCmapResult;
1279 bool bOK = GetFontCodeRanges( aCmapResult );
1280 if( bOK )
1282 FontCharMapPtr pFontCharMap( new FontCharMap ( aCmapResult ) );
1283 mpFontCharMap = pFontCharMap;
1285 else
1287 FontCharMapPtr pFontCharMap( new FontCharMap() );
1288 mpFontCharMap = pFontCharMap;
1290 // mpFontCharMap on either branch now has a refcount of 1
1291 return mpFontCharMap;
1294 // TODO: merge into method GetFontCharMap()
1295 bool FtFontInfo::GetFontCodeRanges( CmapResult& rResult ) const
1297 rResult.mbSymbolic = IsSymbolFont();
1299 // TODO: is the full CmapResult needed on platforms calling this?
1300 if( FT_IS_SFNT( maFaceFT ) )
1302 sal_uLong nLength = 0;
1303 const unsigned char* pCmap = GetTable( "cmap", &nLength );
1304 if( pCmap && (nLength > 0) )
1305 if( ParseCMAP( pCmap, nLength, rResult ) )
1306 return true;
1309 typedef std::vector<sal_uInt32> U32Vector;
1310 U32Vector aCodes;
1312 // FT's coverage is available since FT>=2.1.0 (OOo-baseline>=2.1.4 => ok)
1313 aCodes.reserve( 0x1000 );
1314 FT_UInt nGlyphIndex;
1315 for( sal_uInt32 cCode = FT_Get_First_Char( maFaceFT, &nGlyphIndex );; )
1317 if( !nGlyphIndex )
1318 break;
1319 aCodes.push_back( cCode ); // first code inside range
1320 sal_uInt32 cNext = cCode;
1321 do cNext = FT_Get_Next_Char( maFaceFT, cCode, &nGlyphIndex ); while( cNext == ++cCode );
1322 aCodes.push_back( cCode ); // first code outside range
1323 cCode = cNext;
1326 const int nCount = aCodes.size();
1327 if( !nCount) {
1328 if( !rResult.mbSymbolic )
1329 return false;
1331 // we usually get here for Type1 symbol fonts
1332 aCodes.push_back( 0xF020 );
1333 aCodes.push_back( 0xF100 );
1336 sal_uInt32* pCodes = new sal_uInt32[ nCount ];
1337 for( int i = 0; i < nCount; ++i )
1338 pCodes[i] = aCodes[i];
1339 rResult.mpRangeCodes = pCodes;
1340 rResult.mnRangeCount = nCount / 2;
1341 return true;
1344 bool ServerFont::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
1346 bool bRet = false;
1348 sal_uLong nLength = 0;
1349 // load GSUB table
1350 const FT_Byte* pGSUB = mpFontInfo->GetTable("GSUB", &nLength);
1351 if (pGSUB)
1352 vcl::getTTScripts(rFontCapabilities.maGSUBScriptTags, pGSUB, nLength);
1354 // load OS/2 table
1355 const FT_Byte* pOS2 = mpFontInfo->GetTable("OS/2", &nLength);
1356 if (pOS2)
1358 bRet = vcl::getTTCoverage(
1359 rFontCapabilities.maUnicodeRange,
1360 rFontCapabilities.maCodePageRange,
1361 pOS2, nLength);
1364 return bRet;
1367 // outline stuff
1369 class PolyArgs
1371 public:
1372 PolyArgs( tools::PolyPolygon& rPolyPoly, sal_uInt16 nMaxPoints );
1373 ~PolyArgs();
1375 void AddPoint( long nX, long nY, PolyFlags);
1376 void ClosePolygon();
1378 long GetPosX() const { return maPosition.x;}
1379 long GetPosY() const { return maPosition.y;}
1381 private:
1382 tools::PolyPolygon& mrPolyPoly;
1384 Point* mpPointAry;
1385 sal_uInt8* mpFlagAry;
1387 FT_Vector maPosition;
1388 sal_uInt16 mnMaxPoints;
1389 sal_uInt16 mnPoints;
1390 sal_uInt16 mnPoly;
1391 bool bHasOffline;
1394 PolyArgs::PolyArgs( tools::PolyPolygon& rPolyPoly, sal_uInt16 nMaxPoints )
1395 : mrPolyPoly(rPolyPoly),
1396 mnMaxPoints(nMaxPoints),
1397 mnPoints(0),
1398 mnPoly(0),
1399 bHasOffline(false)
1401 mpPointAry = new Point[ mnMaxPoints ];
1402 mpFlagAry = new sal_uInt8 [ mnMaxPoints ];
1403 maPosition.x = maPosition.y = 0;
1406 PolyArgs::~PolyArgs()
1408 delete[] mpFlagAry;
1409 delete[] mpPointAry;
1412 void PolyArgs::AddPoint( long nX, long nY, PolyFlags aFlag )
1414 DBG_ASSERT( (mnPoints < mnMaxPoints), "FTGlyphOutline: AddPoint overflow!" );
1415 if( mnPoints >= mnMaxPoints )
1416 return;
1418 maPosition.x = nX;
1419 maPosition.y = nY;
1420 mpPointAry[ mnPoints ] = Point( nX, nY );
1421 mpFlagAry[ mnPoints++ ]= aFlag;
1422 bHasOffline |= (aFlag != POLY_NORMAL);
1425 void PolyArgs::ClosePolygon()
1427 if( !mnPoly++ )
1428 return;
1430 // freetype seems to always close the polygon with an ON_CURVE point
1431 // PolyPoly wants to close the polygon itself => remove last point
1432 DBG_ASSERT( (mnPoints >= 2), "FTGlyphOutline: PolyFinishNum failed!" );
1433 --mnPoints;
1434 DBG_ASSERT( (mpPointAry[0]==mpPointAry[mnPoints]), "FTGlyphOutline: PolyFinishEq failed!" );
1435 DBG_ASSERT( (mpFlagAry[0]==POLY_NORMAL), "FTGlyphOutline: PolyFinishFE failed!" );
1436 DBG_ASSERT( (mpFlagAry[mnPoints]==POLY_NORMAL), "FTGlyphOutline: PolyFinishFS failed!" );
1438 Polygon aPoly( mnPoints, mpPointAry, (bHasOffline ? mpFlagAry : NULL) );
1440 // #i35928#
1441 // This may be a invalid polygons, e.g. the last point is a control point.
1442 // So close the polygon (and add the first point again) if the last point
1443 // is a control point or different from first.
1444 // #i48298#
1445 // Now really duplicating the first point, to close or correct the
1446 // polygon. Also no longer duplicating the flags, but enforcing
1447 // POLY_NORMAL for the newly added last point.
1448 const sal_uInt16 nPolySize(aPoly.GetSize());
1449 if(nPolySize)
1451 if((aPoly.HasFlags() && POLY_CONTROL == aPoly.GetFlags(nPolySize - 1))
1452 || (aPoly.GetPoint(nPolySize - 1) != aPoly.GetPoint(0)))
1454 aPoly.SetSize(nPolySize + 1);
1455 aPoly.SetPoint(aPoly.GetPoint(0), nPolySize);
1457 if(aPoly.HasFlags())
1459 aPoly.SetFlags(nPolySize, POLY_NORMAL);
1464 mrPolyPoly.Insert( aPoly );
1465 mnPoints = 0;
1466 bHasOffline = false;
1469 extern "C" {
1471 // TODO: wait till all compilers accept that calling conventions
1472 // for functions are the same independent of implementation constness,
1473 // then uncomment the const-tokens in the function interfaces below
1474 static int FT_move_to( FT_Vector_CPtr p0, void* vpPolyArgs )
1476 PolyArgs& rA = *static_cast<PolyArgs*>(vpPolyArgs);
1478 // move_to implies a new polygon => finish old polygon first
1479 rA.ClosePolygon();
1481 rA.AddPoint( p0->x, p0->y, POLY_NORMAL );
1482 return 0;
1485 static int FT_line_to( FT_Vector_CPtr p1, void* vpPolyArgs )
1487 PolyArgs& rA = *static_cast<PolyArgs*>(vpPolyArgs);
1488 rA.AddPoint( p1->x, p1->y, POLY_NORMAL );
1489 return 0;
1492 static int FT_conic_to( FT_Vector_CPtr p1, FT_Vector_CPtr p2, void* vpPolyArgs )
1494 PolyArgs& rA = *static_cast<PolyArgs*>(vpPolyArgs);
1496 // VCL's Polygon only knows cubic beziers
1497 const long nX1 = (2 * rA.GetPosX() + 4 * p1->x + 3) / 6;
1498 const long nY1 = (2 * rA.GetPosY() + 4 * p1->y + 3) / 6;
1499 rA.AddPoint( nX1, nY1, POLY_CONTROL );
1501 const long nX2 = (2 * p2->x + 4 * p1->x + 3) / 6;
1502 const long nY2 = (2 * p2->y + 4 * p1->y + 3) / 6;
1503 rA.AddPoint( nX2, nY2, POLY_CONTROL );
1505 rA.AddPoint( p2->x, p2->y, POLY_NORMAL );
1506 return 0;
1509 static int FT_cubic_to( FT_Vector_CPtr p1, FT_Vector_CPtr p2, FT_Vector_CPtr p3, void* vpPolyArgs )
1511 PolyArgs& rA = *static_cast<PolyArgs*>(vpPolyArgs);
1512 rA.AddPoint( p1->x, p1->y, POLY_CONTROL );
1513 rA.AddPoint( p2->x, p2->y, POLY_CONTROL );
1514 rA.AddPoint( p3->x, p3->y, POLY_NORMAL );
1515 return 0;
1518 } // extern "C"
1520 bool ServerFont::GetGlyphOutline( sal_GlyphId aGlyphId,
1521 ::basegfx::B2DPolyPolygon& rB2DPolyPoly ) const
1523 if( maSizeFT )
1524 FT_Activate_Size( maSizeFT );
1526 rB2DPolyPoly.clear();
1528 int nGlyphFlags;
1529 SplitGlyphFlags( *this, aGlyphId, nGlyphFlags );
1531 FT_Int nLoadFlags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_TRANSFORM;
1533 #ifdef FT_LOAD_TARGET_LIGHT
1534 // enable "light hinting" if available
1535 nLoadFlags |= FT_LOAD_TARGET_LIGHT;
1536 #endif
1538 FT_Error rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags );
1539 if( rc != FT_Err_Ok )
1540 return false;
1542 if (mbArtBold)
1543 FT_GlyphSlot_Embolden(maFaceFT->glyph);
1545 FT_Glyph pGlyphFT;
1546 rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
1547 if( rc != FT_Err_Ok )
1548 return false;
1550 if( pGlyphFT->format != FT_GLYPH_FORMAT_OUTLINE )
1552 FT_Done_Glyph( pGlyphFT );
1553 return false;
1556 if( mbArtItalic )
1558 FT_Matrix aMatrix;
1559 aMatrix.xx = aMatrix.yy = 0x10000L;
1560 aMatrix.xy = 0x6000L, aMatrix.yx = 0;
1561 FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
1564 FT_Outline& rOutline = reinterpret_cast<FT_OutlineGlyphRec*>(pGlyphFT)->outline;
1565 if( !rOutline.n_points ) // blank glyphs are ok
1567 FT_Done_Glyph( pGlyphFT );
1568 return true;
1571 long nMaxPoints = 1 + rOutline.n_points * 3;
1572 tools::PolyPolygon aToolPolyPolygon;
1573 PolyArgs aPolyArg( aToolPolyPolygon, nMaxPoints );
1575 /*int nAngle =*/ ApplyGlyphTransform( nGlyphFlags, pGlyphFT, false );
1577 FT_Outline_Funcs aFuncs;
1578 aFuncs.move_to = &FT_move_to;
1579 aFuncs.line_to = &FT_line_to;
1580 aFuncs.conic_to = &FT_conic_to;
1581 aFuncs.cubic_to = &FT_cubic_to;
1582 aFuncs.shift = 0;
1583 aFuncs.delta = 0;
1584 rc = FT_Outline_Decompose( &rOutline, &aFuncs, (void*)&aPolyArg );
1585 aPolyArg.ClosePolygon(); // close last polygon
1586 FT_Done_Glyph( pGlyphFT );
1588 // convert to basegfx polypolygon
1589 // TODO: get rid of the intermediate tools polypolygon
1590 rB2DPolyPoly = aToolPolyPolygon.getB2DPolyPolygon();
1591 rB2DPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix( +1.0/(1<<6), -1.0/(1<<6) ));
1593 return true;
1596 bool ServerFont::ApplyGSUB( const FontSelectPattern& rFSD )
1598 #define MKTAG(s) ((((((s[0]<<8)+s[1])<<8)+s[2])<<8)+s[3])
1600 typedef std::vector<sal_uLong> ReqFeatureTagList;
1601 ReqFeatureTagList aReqFeatureTagList;
1602 if( rFSD.mbVertical )
1603 aReqFeatureTagList.push_back( MKTAG("vert") );
1604 // TODO: request more features depending on script and language system
1606 if( aReqFeatureTagList.empty()) // nothing to do
1607 return true;
1609 // load GSUB table into memory
1610 sal_uLong nLength = 0;
1611 const FT_Byte* const pGsubBase = mpFontInfo->GetTable( "GSUB", &nLength );
1612 if( !pGsubBase )
1613 return false;
1615 // parse GSUB header
1616 const FT_Byte* pGsubHeader = pGsubBase;
1617 const sal_uInt16 nOfsScriptList = GetUShort( pGsubHeader+4 );
1618 const sal_uInt16 nOfsFeatureTable = GetUShort( pGsubHeader+6 );
1619 const sal_uInt16 nOfsLookupList = GetUShort( pGsubHeader+8 );
1620 pGsubHeader += 10;
1622 typedef std::vector<sal_uInt16> UshortList;
1623 UshortList aFeatureIndexList;
1625 // parse Script Table
1626 const FT_Byte* pScriptHeader = pGsubBase + nOfsScriptList;
1627 const sal_uInt16 nCntScript = GetUShort( pScriptHeader+0 );
1628 pScriptHeader += 2;
1629 for( sal_uInt16 nScriptIndex = 0; nScriptIndex < nCntScript; ++nScriptIndex )
1631 const sal_uInt16 nOfsScriptTable= GetUShort( pScriptHeader+4 );
1632 pScriptHeader += 6;
1634 const FT_Byte* pScriptTable = pGsubBase + nOfsScriptList + nOfsScriptTable;
1635 const sal_uInt16 nDefaultLangsysOfs = GetUShort( pScriptTable+0 );
1636 const sal_uInt16 nCntLangSystem = GetUShort( pScriptTable+2 );
1637 pScriptTable += 4;
1638 sal_uInt16 nLangsysOffset = 0;
1640 if (nCntLangSystem != 0)
1642 nLangsysOffset = GetUShort( pScriptTable+4 );
1645 if (nDefaultLangsysOfs != 0 && nDefaultLangsysOfs != nLangsysOffset)
1647 const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nDefaultLangsysOfs;
1648 const sal_uInt16 nReqFeatureIdx = GetUShort( pLangSys+2 );
1649 const sal_uInt16 nCntFeature = GetUShort( pLangSys+4 );
1650 pLangSys += 6;
1651 aFeatureIndexList.push_back( nReqFeatureIdx );
1652 for( sal_uInt16 i = 0; i < nCntFeature; ++i )
1654 const sal_uInt16 nFeatureIndex = GetUShort( pLangSys );
1655 pLangSys += 2;
1656 aFeatureIndexList.push_back( nFeatureIndex );
1660 if( nLangsysOffset != 0 )
1662 const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nLangsysOffset;
1663 const sal_uInt16 nReqFeatureIdx = GetUShort( pLangSys+2 );
1664 const sal_uInt16 nCntFeature = GetUShort( pLangSys+4 );
1665 pLangSys += 6;
1666 aFeatureIndexList.push_back( nReqFeatureIdx );
1667 for( sal_uInt16 i = 0; i < nCntFeature; ++i )
1669 const sal_uInt16 nFeatureIndex = GetUShort( pLangSys );
1670 pLangSys += 2;
1671 aFeatureIndexList.push_back( nFeatureIndex );
1676 if( aFeatureIndexList.empty() )
1677 return true;
1679 UshortList aLookupIndexList;
1680 UshortList aLookupOffsetList;
1682 // parse Feature Table
1683 const FT_Byte* pFeatureHeader = pGsubBase + nOfsFeatureTable;
1684 const sal_uInt16 nCntFeature = GetUShort( pFeatureHeader );
1685 pFeatureHeader += 2;
1686 for( sal_uInt16 nFeatureIndex = 0; nFeatureIndex < nCntFeature; ++nFeatureIndex )
1688 const sal_uLong nTag = GetUInt( pFeatureHeader+0 ); // e.g. locl/vert/trad/smpl/liga/fina/...
1689 const sal_uInt16 nOffset= GetUShort( pFeatureHeader+4 );
1690 pFeatureHeader += 6;
1692 // short circuit some feature lookups
1693 if( aFeatureIndexList[0] != nFeatureIndex ) // required feature?
1695 const int nRequested = std::count( aFeatureIndexList.begin(), aFeatureIndexList.end(), nFeatureIndex);
1696 if( !nRequested ) // ignore features that are not requested
1697 continue;
1698 const int nAvailable = std::count( aReqFeatureTagList.begin(), aReqFeatureTagList.end(), nTag);
1699 if( !nAvailable ) // some fonts don't provide features they request!
1700 continue;
1703 const FT_Byte* pFeatureTable = pGsubBase + nOfsFeatureTable + nOffset;
1704 const sal_uInt16 nCntLookups = GetUShort( pFeatureTable+0 );
1705 pFeatureTable += 2;
1706 for( sal_uInt16 i = 0; i < nCntLookups; ++i )
1708 const sal_uInt16 nLookupIndex = GetUShort( pFeatureTable );
1709 pFeatureTable += 2;
1710 aLookupIndexList.push_back( nLookupIndex );
1712 if( nCntLookups == 0 ) //### hack needed by Mincho/Gothic/Mingliu/Simsun/...
1713 aLookupIndexList.push_back( 0 );
1716 // parse Lookup List
1717 const FT_Byte* pLookupHeader = pGsubBase + nOfsLookupList;
1718 const sal_uInt16 nCntLookupTable = GetUShort( pLookupHeader );
1719 pLookupHeader += 2;
1720 for( sal_uInt16 nLookupIdx = 0; nLookupIdx < nCntLookupTable; ++nLookupIdx )
1722 const sal_uInt16 nOffset = GetUShort( pLookupHeader );
1723 pLookupHeader += 2;
1724 if( std::count( aLookupIndexList.begin(), aLookupIndexList.end(), nLookupIdx ) )
1725 aLookupOffsetList.push_back( nOffset );
1728 UshortList::const_iterator lookup_it = aLookupOffsetList.begin();
1729 for(; lookup_it != aLookupOffsetList.end(); ++lookup_it )
1731 const sal_uInt16 nOfsLookupTable = *lookup_it;
1732 const FT_Byte* pLookupTable = pGsubBase + nOfsLookupList + nOfsLookupTable;
1733 const sal_uInt16 eLookupType = GetUShort( pLookupTable+0 );
1734 const sal_uInt16 nCntLookupSubtable = GetUShort( pLookupTable+4 );
1735 pLookupTable += 6;
1737 // TODO: switch( eLookupType )
1738 if( eLookupType != 1 ) // TODO: once we go beyond SingleSubst
1739 continue;
1741 for( sal_uInt16 nSubTableIdx = 0; nSubTableIdx < nCntLookupSubtable; ++nSubTableIdx )
1743 const sal_uInt16 nOfsSubLookupTable = GetUShort( pLookupTable );
1744 pLookupTable += 2;
1745 const FT_Byte* pSubLookup = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable;
1747 const sal_uInt16 nFmtSubstitution = GetUShort( pSubLookup+0 );
1748 const sal_uInt16 nOfsCoverage = GetUShort( pSubLookup+2 );
1749 pSubLookup += 4;
1751 typedef std::pair<sal_uInt16,sal_uInt16> GlyphSubst;
1752 typedef std::vector<GlyphSubst> SubstVector;
1753 SubstVector aSubstVector;
1755 const FT_Byte* pCoverage = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable + nOfsCoverage;
1756 const sal_uInt16 nFmtCoverage = GetUShort( pCoverage+0 );
1757 pCoverage += 2;
1758 switch( nFmtCoverage )
1760 case 1: // Coverage Format 1
1762 const sal_uInt16 nCntGlyph = GetUShort( pCoverage );
1763 pCoverage += 2;
1764 aSubstVector.reserve( nCntGlyph );
1765 for( sal_uInt16 i = 0; i < nCntGlyph; ++i )
1767 const sal_uInt16 nGlyphId = GetUShort( pCoverage );
1768 pCoverage += 2;
1769 aSubstVector.push_back( GlyphSubst( nGlyphId, 0 ) );
1772 break;
1774 case 2: // Coverage Format 2
1776 const sal_uInt16 nCntRange = GetUShort( pCoverage );
1777 pCoverage += 2;
1778 for( int i = nCntRange; --i >= 0; )
1780 const sal_uInt32 nGlyph0 = GetUShort( pCoverage+0 );
1781 const sal_uInt32 nGlyph1 = GetUShort( pCoverage+2 );
1782 const sal_uInt16 nCovIdx = GetUShort( pCoverage+4 );
1783 pCoverage += 6;
1784 for( sal_uInt32 j = nGlyph0; j <= nGlyph1; ++j )
1785 aSubstVector.push_back( GlyphSubst( static_cast<sal_uInt16>(j + nCovIdx), 0 ) );
1788 break;
1791 SubstVector::iterator it( aSubstVector.begin() );
1793 switch( nFmtSubstitution )
1795 case 1: // Single Substitution Format 1
1797 const sal_uInt16 nDeltaGlyphId = GetUShort( pSubLookup );
1798 for(; it != aSubstVector.end(); ++it )
1799 (*it).second = (*it).first + nDeltaGlyphId;
1801 break;
1803 case 2: // Single Substitution Format 2
1805 const sal_uInt16 nCntGlyph = GetUShort( pSubLookup );
1806 pSubLookup += 2;
1807 for( int i = nCntGlyph; (it != aSubstVector.end()) && (--i>=0); ++it )
1809 const sal_uInt16 nGlyphId = GetUShort( pSubLookup );
1810 pSubLookup += 2;
1811 (*it).second = nGlyphId;
1814 break;
1817 DBG_ASSERT( (it == aSubstVector.end()), "lookup<->coverage table mismatch" );
1818 // now apply the glyph substitutions that have been collected in this subtable
1819 for( it = aSubstVector.begin(); it != aSubstVector.end(); ++it )
1820 maGlyphSubstitution[ (*it).first ] = (*it).second;
1824 return true;
1827 const unsigned char* ServerFont::GetTable(const char* pName, sal_uLong* pLength)
1829 return mpFontInfo->GetTable( pName, pLength );
1832 #if ENABLE_GRAPHITE
1833 GraphiteFaceWrapper* ServerFont::GetGraphiteFace() const
1835 return mpFontInfo->GetGraphiteFace();
1837 #endif
1839 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */