blob: 7aae2804936cf812ac9ede33166003947db39a8b [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"
#ifdef WNT
#include <svsys.h>
#undef CreateFont
#endif
#include "gcach_ftyp.hxx"
#include "vcl/svapp.hxx"
#include "outfont.hxx"
#include "impfont.hxx"
#include "tools/poly.hxx"
#include "basegfx/matrix/b2dhommatrix.hxx"
#include "basegfx/matrix/b2dhommatrixtools.hxx"
#include "basegfx/polygon/b2dpolypolygon.hxx"
#include "osl/file.hxx"
#include "osl/thread.hxx"
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_OUTLINE_H
#include FT_TRUETYPE_TABLES_H
#include FT_TRUETYPE_TAGS_H
#include FT_TRUETYPE_IDS_H
#ifndef FT_RENDER_MODE_MONO // happens in the MACOSX build
#define FT_RENDER_MODE_MONO ft_render_mode_mono
#endif
#include "rtl/instance.hxx"
#ifndef FREETYPE_PATCH
// VERSION_MINOR in freetype.h is too coarse
// if patch-level is not available we need to fine-tune the version ourselves
#define FTVERSION 2005
#else
#define FTVERSION (1000*FREETYPE_MAJOR + 100*FREETYPE_MINOR + FREETYPE_PATCH)
#endif
#if FTVERSION >= 2200
typedef const FT_Vector* FT_Vector_CPtr;
#else // FTVERSION < 2200
typedef FT_Vector* FT_Vector_CPtr;
#endif
#include <vector>
// TODO: move file mapping stuff to OSL
#if defined(UNX)
#if !defined(HPUX)
// PORTERS: dlfcn is used for getting symbols from FT versions newer than baseline
#include <dlfcn.h>
#endif
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "vcl/fontmanager.hxx"
#elif defined(WNT)
#include <io.h>
#define strncasecmp strnicmp
#endif
typedef const unsigned char* CPU8;
inline sal_uInt16 NEXT_U16( CPU8& p ) { p+=2; return (p[-2]<<8)|p[-1]; }
inline sal_Int16 NEXT_S16( CPU8& p ) { return (sal_Int16)NEXT_U16(p); }
inline sal_uInt32 NEXT_U32( CPU8& p ) { p+=4; return (p[-4]<<24)|(p[-3]<<16)|(p[-2]<<8)|p[-1]; }
//inline sal_Int32 NEXT_S32( U8*& p ) { return (sal_Int32)NEXT_U32(p); }
// -----------------------------------------------------------------------
// the gamma table makes artificial bold look better for CJK glyphs
static unsigned char aGammaTable[257];
static void InitGammaTable()
{
static const int M_MAX = 255;
static const int M_X = 128;
static const int M_Y = 208;
int x, a;
for( x = 0; x < 256; x++)
{
if ( x <= M_X )
a = ( x * M_Y + M_X / 2) / M_X;
else
a = M_Y + ( ( x - M_X ) * ( M_MAX - M_Y ) +
( M_MAX - M_X ) / 2 ) / ( M_MAX - M_X );
aGammaTable[x] = (unsigned char)a;
}
}
// -----------------------------------------------------------------------
static FT_Library aLibFT = 0;
// #110607# enable linking with old FT versions
static int nFTVERSION = 0;
static FT_Error (*pFTNewSize)(FT_Face,FT_Size*);
static FT_Error (*pFTActivateSize)(FT_Size);
static FT_Error (*pFTDoneSize)(FT_Size);
FT_Error (*pFTEmbolden)(FT_GlyphSlot);
FT_Error (*pFTOblique)(FT_GlyphSlot);
static bool bEnableSizeFT = false;
typedef ::std::hash_map< const char*, FtFontFile*, rtl::CStringHash, rtl::CStringEqual> FontFileList;
namespace { struct vclFontFileList : public rtl::Static< FontFileList, vclFontFileList > {}; }
// -----------------------------------------------------------------------
// TODO: remove when the priorities are selected by UI
// if (AH==0) => disable autohinting
// if (AA==0) => disable antialiasing
// if (EB==0) => disable embedded bitmaps
// if (AA prio <= AH prio) => antialias + autohint
// if (AH<AA) => do not autohint when antialiasing
// if (EB<AH) => do not autohint for monochrome
static int nDefaultPrioEmbedded = 2;
static int nDefaultPrioAutoHint = 1;
static int nDefaultPrioAntiAlias = 1;
// =======================================================================
// FreetypeManager
// =======================================================================
FtFontFile::FtFontFile( const ::rtl::OString& rNativeFileName )
: maNativeFileName( rNativeFileName ),
mpFileMap( NULL ),
mnFileSize( 0 ),
mnRefCount( 0 ),
mnLangBoost( 0 )
{
// boost font preference if UI language is mentioned in filename
int nPos = maNativeFileName.lastIndexOf( '_' );
if( nPos == -1 || maNativeFileName[nPos+1] == '.' )
mnLangBoost += 0x1000; // no langinfo => good
else
{
static const char* pLangBoost = NULL;
static bool bOnce = true;
if( bOnce )
{
bOnce = false;
LanguageType aLang = Application::GetSettings().GetUILanguage();
switch( aLang )
{
case LANGUAGE_JAPANESE:
pLangBoost = "jan";
break;
case LANGUAGE_CHINESE:
case LANGUAGE_CHINESE_SIMPLIFIED:
case LANGUAGE_CHINESE_SINGAPORE:
pLangBoost = "zhs";
break;
case LANGUAGE_CHINESE_TRADITIONAL:
case LANGUAGE_CHINESE_HONGKONG:
case LANGUAGE_CHINESE_MACAU:
pLangBoost = "zht";
break;
case LANGUAGE_KOREAN:
case LANGUAGE_KOREAN_JOHAB:
pLangBoost = "kor";
break;
}
}
if( pLangBoost && !strncasecmp( pLangBoost, &maNativeFileName.getStr()[nPos+1], 3 ) )
mnLangBoost += 0x2000; // matching langinfo => better
}
}
// -----------------------------------------------------------------------
FtFontFile* FtFontFile::FindFontFile( const ::rtl::OString& rNativeFileName )
{
// font file already known? (e.g. for ttc, synthetic, aliased fonts)
const char* pFileName = rNativeFileName.getStr();
FontFileList &rFontFileList = vclFontFileList::get();
FontFileList::const_iterator it = rFontFileList.find( pFileName );
if( it != rFontFileList.end() )
return (*it).second;
// no => create new one
FtFontFile* pFontFile = new FtFontFile( rNativeFileName );
pFileName = pFontFile->maNativeFileName.getStr();
rFontFileList[ pFileName ] = pFontFile;
return pFontFile;
}
// -----------------------------------------------------------------------
bool FtFontFile::Map()
{
if( mnRefCount++ <= 0 )
{
const char* pFileName = maNativeFileName.getStr();
#if defined(UNX)
int nFile = open( pFileName, O_RDONLY );
if( nFile < 0 )
return false;
struct stat aStat;
fstat( nFile, &aStat );
mnFileSize = aStat.st_size;
mpFileMap = (const unsigned char*)
mmap( NULL, mnFileSize, PROT_READ, MAP_SHARED, nFile, 0 );
if( mpFileMap == MAP_FAILED )
mpFileMap = NULL;
close( nFile );
#elif defined(WNT)
void* pFileDesc = ::CreateFile( pFileName, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
if( pFileDesc == INVALID_HANDLE_VALUE)
return false;
mnFileSize = ::GetFileSize( pFileDesc, NULL );
HANDLE aHandle = ::CreateFileMapping( pFileDesc, NULL, PAGE_READONLY, 0, mnFileSize, "TTF" );
mpFileMap = (const unsigned char*)::MapViewOfFile( aHandle, FILE_MAP_READ, 0, 0, mnFileSize );
::CloseHandle( pFileDesc );
#else
FILE* pFile = fopen( pFileName, "rb" );
if( !pFile )
return false;
struct stat aStat;
stat( pFileName, &aStat );
mnFileSize = aStat.st_size;
mpFileMap = new unsigned char[ mnFileSize ];
if( mnFileSize != fread( mpFileMap, 1, mnFileSize, pFile ) )
{
delete[] mpFileMap;
mpFileMap = NULL;
}
fclose( pFile );
#endif
}
return (mpFileMap != NULL);
}
// -----------------------------------------------------------------------
void FtFontFile::Unmap()
{
if( (--mnRefCount > 0) || (mpFileMap == NULL) )
return;
#if defined(UNX)
munmap( (char*)mpFileMap, mnFileSize );
#elif defined(WNT)
UnmapViewOfFile( (LPCVOID)mpFileMap );
#else
delete[] mpFileMap;
#endif
mpFileMap = NULL;
}
// =======================================================================
FtFontInfo::FtFontInfo( const ImplDevFontAttributes& rDevFontAttributes,
const ::rtl::OString& rNativeFileName, int nFaceNum, sal_IntPtr nFontId, int nSynthetic,
const ExtraKernInfo* pExtraKernInfo )
:
maFaceFT( NULL ),
mpFontFile( FtFontFile::FindFontFile( rNativeFileName ) ),
mnFaceNum( nFaceNum ),
mnRefCount( 0 ),
mnSynthetic( nSynthetic ),
mnFontId( nFontId ),
maDevFontAttributes( rDevFontAttributes ),
mpFontCharMap( NULL ),
mpChar2Glyph( NULL ),
mpGlyph2Char( NULL ),
mpExtraKernInfo( pExtraKernInfo )
{
// prefer font with low ID
maDevFontAttributes.mnQuality += 10000 - nFontId;
// prefer font with matching file names
maDevFontAttributes.mnQuality += mpFontFile->GetLangBoost();
// prefer font with more external info
if( pExtraKernInfo )
maDevFontAttributes.mnQuality += 100;
}
// -----------------------------------------------------------------------
FtFontInfo::~FtFontInfo()
{
if( mpFontCharMap )
mpFontCharMap->DeReference();
delete mpExtraKernInfo;
delete mpChar2Glyph;
delete mpGlyph2Char;
}
void FtFontInfo::InitHashes() const
{
// TODO: avoid pointers when empty stl::hash_* objects become cheap
mpChar2Glyph = new Int2IntMap();
mpGlyph2Char = new Int2IntMap();
}
// -----------------------------------------------------------------------
FT_FaceRec_* FtFontInfo::GetFaceFT()
{
// get faceFT once/multiple depending on availability of SizeFT APIs
if( (mnRefCount++ <= 0) || !bEnableSizeFT )
{
if( !mpFontFile->Map() )
return NULL;
FT_Error rc = FT_New_Memory_Face( aLibFT,
(FT_Byte*)mpFontFile->GetBuffer(),
mpFontFile->GetFileSize(), mnFaceNum, &maFaceFT );
if( (rc != FT_Err_Ok) || (maFaceFT->num_glyphs <= 0) )
maFaceFT = NULL;
}
return maFaceFT;
}
// -----------------------------------------------------------------------
void FtFontInfo::ReleaseFaceFT( FT_FaceRec_* pFaceFT )
{
// release last/each depending on SizeFT availability
if( (--mnRefCount <= 0) || !bEnableSizeFT )
{
FT_Done_Face( pFaceFT );
maFaceFT = NULL;
mpFontFile->Unmap();
}
}
// -----------------------------------------------------------------------
bool FtFontInfo::HasExtraKerning() const
{
if( !mpExtraKernInfo )
return false;
// TODO: how to enable the line below without getting #i29881# back?
// on the other hand being to optimistic doesn't cause problems
// return mpExtraKernInfo->HasKernPairs();
return true;
}
// -----------------------------------------------------------------------
int FtFontInfo::GetExtraKernPairs( ImplKernPairData** ppKernPairs ) const
{
if( !mpExtraKernInfo )
return 0;
return mpExtraKernInfo->GetUnscaledKernPairs( ppKernPairs );
}
// -----------------------------------------------------------------------
int FtFontInfo::GetExtraGlyphKernValue( int nLeftGlyph, int nRightGlyph ) const
{
if( !mpExtraKernInfo )
return 0;
if( !mpGlyph2Char )
return 0;
sal_Unicode cLeftChar = (*mpGlyph2Char)[ nLeftGlyph ];
sal_Unicode cRightChar = (*mpGlyph2Char)[ nRightGlyph ];
return mpExtraKernInfo->GetUnscaledKernValue( cLeftChar, cRightChar );
}
// -----------------------------------------------------------------------
static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);}
static unsigned GetUShort( const unsigned char* p ){ return((p[0]<<8)+p[1]);}
//static signed GetSShort( const unsigned char* p ){ return((short)((p[0]<<8)+p[1]));}
// -----------------------------------------------------------------------
const unsigned char* FtFontInfo::GetTable( const char* pTag, sal_uLong* pLength ) const
{
const unsigned char* pBuffer = mpFontFile->GetBuffer();
int nFileSize = mpFontFile->GetFileSize();
if( !pBuffer || nFileSize<1024 )
return NULL;
// we currently only handle TTF and TTC headers
unsigned nFormat = GetUInt( pBuffer );
const unsigned char* p = pBuffer + 12;
if( nFormat == 0x74746366 ) // TTC_MAGIC
p += GetUInt( p + 4 * mnFaceNum );
else if( (nFormat!=0x00010000) && (nFormat!=0x74727565) ) // TTF_MAGIC and Apple TTF Magic
return NULL;
// walk table directory until match
int nTables = GetUShort( p - 8 );
if( nTables >= 64 ) // something fishy?
return NULL;
for( int i = 0; i < nTables; ++i, p+=16 )
{
if( p[0]==pTag[0] && p[1]==pTag[1] && p[2]==pTag[2] && p[3]==pTag[3] )
{
sal_uLong nLength = GetUInt( p + 12 );
if( pLength != NULL )
*pLength = nLength;
const unsigned char* pTable = pBuffer + GetUInt( p + 8 );
if( (pTable + nLength) <= (mpFontFile->GetBuffer() + nFileSize) )
return pTable;
}
}
return NULL;
}
// -----------------------------------------------------------------------
void FtFontInfo::AnnounceFont( ImplDevFontList* pFontList )
{
ImplFTSFontData* pFD = new ImplFTSFontData( this, maDevFontAttributes );
pFontList->Add( pFD );
}
// =======================================================================
FreetypeManager::FreetypeManager()
: mnMaxFontId( 0 ), mnNextFontId( 0x1000 )
{
/*FT_Error rcFT =*/ FT_Init_FreeType( &aLibFT );
#ifdef RTLD_DEFAULT // true if a good dlfcn.h header was included
// Get version of freetype library to enable workarounds.
// Freetype <= 2.0.9 does not have FT_Library_Version().
// Using dl_sym() instead of osl_getSymbol() because latter
// isn't designed to work with oslModule=NULL
void (*pFTLibraryVersion)(FT_Library library,
FT_Int *amajor, FT_Int *aminor, FT_Int *apatch);
pFTLibraryVersion = (void (*)(FT_Library library,
FT_Int *amajor, FT_Int *aminor, FT_Int *apatch))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_Library_Version" );
pFTNewSize = (FT_Error(*)(FT_Face,FT_Size*))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_New_Size" );
pFTActivateSize = (FT_Error(*)(FT_Size))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_Activate_Size" );
pFTDoneSize = (FT_Error(*)(FT_Size))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_Done_Size" );
pFTEmbolden = (FT_Error(*)(FT_GlyphSlot))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_GlyphSlot_Embolden" );
pFTOblique = (FT_Error(*)(FT_GlyphSlot))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_GlyphSlot_Oblique" );
bEnableSizeFT = (pFTNewSize!=NULL) && (pFTActivateSize!=NULL) && (pFTDoneSize!=NULL);
FT_Int nMajor = 0, nMinor = 0, nPatch = 0;
if( pFTLibraryVersion )
pFTLibraryVersion( aLibFT, &nMajor, &nMinor, &nPatch );
nFTVERSION = nMajor * 1000 + nMinor * 100 + nPatch;
// disable embedded bitmaps for Freetype-2.1.3 unless explicitly
// requested by env var below because it crashes StarOffice on RH9
// reason: double free in freetype's embedded bitmap handling
if( nFTVERSION == 2103 )
nDefaultPrioEmbedded = 0;
// disable artificial emboldening with the Freetype API for older versions
if( nFTVERSION < 2110 )
pFTEmbolden = NULL;
#else // RTLD_DEFAULT
// assume systems where dlsym is not possible use supplied library
nFTVERSION = FTVERSION;
#endif
// TODO: remove when the priorities are selected by UI
char* pEnv;
pEnv = ::getenv( "SAL_EMBEDDED_BITMAP_PRIORITY" );
if( pEnv )
nDefaultPrioEmbedded = pEnv[0] - '0';
pEnv = ::getenv( "SAL_ANTIALIASED_TEXT_PRIORITY" );
if( pEnv )
nDefaultPrioAntiAlias = pEnv[0] - '0';
pEnv = ::getenv( "SAL_AUTOHINTING_PRIORITY" );
if( pEnv )
nDefaultPrioAutoHint = pEnv[0] - '0';
InitGammaTable();
}
// -----------------------------------------------------------------------
void* FreetypeServerFont::GetFtFace() const
{
if( maSizeFT )
pFTActivateSize( maSizeFT );
return maFaceFT;
}
// -----------------------------------------------------------------------
FreetypeManager::~FreetypeManager()
{
// an application about to exit can omit garbage collecting the heap
// since it makes things slower and introduces risks if the heap was not perfect
// for debugging, for memory grinding or leak checking the env allows to force GC
const char* pEnv = getenv( "SAL_FORCE_GC_ON_EXIT" );
if( pEnv && (*pEnv != '0') )
{
// cleanup container of fontinfos
for( FontList::const_iterator it = maFontList.begin(); it != maFontList.end(); ++it )
{
FtFontInfo* pInfo = (*it).second;
delete pInfo;
}
maFontList.clear();
#if 0 // FT_Done_FreeType crashes on Solaris 10
// TODO: check which versions have this problem
FT_Error rcFT = FT_Done_FreeType( aLibFT );
#endif
}
}
// -----------------------------------------------------------------------
void FreetypeManager::AddFontFile( const rtl::OString& rNormalizedName,
int nFaceNum, sal_IntPtr nFontId, const ImplDevFontAttributes& rDevFontAttr,
const ExtraKernInfo* pExtraKernInfo )
{
if( !rNormalizedName.getLength() )
return;
if( maFontList.find( nFontId ) != maFontList.end() )
return;
FtFontInfo* pFontInfo = new FtFontInfo( rDevFontAttr,
rNormalizedName, nFaceNum, nFontId, 0, pExtraKernInfo );
maFontList[ nFontId ] = pFontInfo;
if( mnMaxFontId < nFontId )
mnMaxFontId = nFontId;
}
// -----------------------------------------------------------------------
long FreetypeManager::AddFontDir( const String& rUrlName )
{
osl::Directory aDir( rUrlName );
osl::FileBase::RC rcOSL = aDir.open();
if( rcOSL != osl::FileBase::E_None )
return 0;
long nCount = 0;
osl::DirectoryItem aDirItem;
rtl_TextEncoding theEncoding = osl_getThreadTextEncoding();
while( (rcOSL = aDir.getNextItem( aDirItem, 20 )) == osl::FileBase::E_None )
{
osl::FileStatus aFileStatus( FileStatusMask_FileURL );
rcOSL = aDirItem.getFileStatus( aFileStatus );
::rtl::OUString aUSytemPath;
OSL_VERIFY( osl::FileBase::E_None
== osl::FileBase::getSystemPathFromFileURL( aFileStatus.getFileURL(), aUSytemPath ));
::rtl::OString aCFileName = rtl::OUStringToOString( aUSytemPath, theEncoding );
const char* pszFontFileName = aCFileName.getStr();
FT_FaceRec_* aFaceFT = NULL;
for( int nFaceNum = 0, nMaxFaces = 1; nFaceNum < nMaxFaces; ++nFaceNum )
{
FT_Error rcFT = FT_New_Face( aLibFT, pszFontFileName, nFaceNum, &aFaceFT );
if( (rcFT != FT_Err_Ok) || (aFaceFT == NULL) )
break;
if( !FT_IS_SCALABLE( aFaceFT ) ) // ignore non-scalabale fonts
continue;
nMaxFaces = aFaceFT->num_faces;
ImplDevFontAttributes aDFA;
// TODO: prefer unicode names if available
// TODO: prefer locale specific names if available?
if ( aFaceFT->family_name )
aDFA.maName = String::CreateFromAscii( aFaceFT->family_name );
if ( aFaceFT->style_name )
aDFA.maStyleName = String::CreateFromAscii( aFaceFT->style_name );
aDFA.mbSymbolFlag = false;
for( int i = aFaceFT->num_charmaps; --i >= 0; )
{
const FT_CharMap aCM = aFaceFT->charmaps[i];
#if (FTVERSION < 2000)
if( aCM->encoding == FT_ENCODING_NONE )
#else
if( (aCM->platform_id == TT_PLATFORM_MICROSOFT)
&& (aCM->encoding_id == TT_MS_ID_SYMBOL_CS) )
#endif
aDFA.mbSymbolFlag = true;
}
// TODO: extract better font characterization data from font
aDFA.meFamily = FAMILY_DONTKNOW;
aDFA.mePitch = FT_IS_FIXED_WIDTH( aFaceFT ) ? PITCH_FIXED : PITCH_VARIABLE;
aDFA.meWidthType = WIDTH_DONTKNOW;
aDFA.meWeight = FT_STYLE_FLAG_BOLD & aFaceFT->style_flags ? WEIGHT_BOLD : WEIGHT_NORMAL;
aDFA.meItalic = FT_STYLE_FLAG_ITALIC & aFaceFT->style_flags ? ITALIC_NORMAL : ITALIC_NONE;
aDFA.mnQuality = 0;
aDFA.mbOrientation= true;
aDFA.mbDevice = true;
aDFA.mbSubsettable= false;
aDFA.mbEmbeddable = false;
FT_Done_Face( aFaceFT );
AddFontFile( aCFileName, nFaceNum, ++mnNextFontId, aDFA, NULL );
++nCount;
}
}
aDir.close();
return nCount;
}
// -----------------------------------------------------------------------
void FreetypeManager::AnnounceFonts( ImplDevFontList* pToAdd ) const
{
for( FontList::const_iterator it = maFontList.begin(); it != maFontList.end(); ++it )
{
FtFontInfo* pFtFontInfo = it->second;
pFtFontInfo->AnnounceFont( pToAdd );
}
}
// -----------------------------------------------------------------------
void FreetypeManager::ClearFontList( )
{
for( FontList::iterator it = maFontList.begin(); it != maFontList.end(); ++it )
{
FtFontInfo* pFtFontInfo = it->second;
delete pFtFontInfo;
}
maFontList.clear();
}
// -----------------------------------------------------------------------
FreetypeServerFont* FreetypeManager::CreateFont( const ImplFontSelectData& rFSD )
{
FtFontInfo* pFontInfo = NULL;
// find a FontInfo matching to the font id
sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFSD.mpFontData );
FontList::iterator it = maFontList.find( nFontId );
if( it != maFontList.end() )
pFontInfo = it->second;
if( !pFontInfo )
return NULL;
FreetypeServerFont* pNew = new FreetypeServerFont( rFSD, pFontInfo );
return pNew;
}
// =======================================================================
ImplFTSFontData::ImplFTSFontData( FtFontInfo* pFI, const ImplDevFontAttributes& rDFA )
: ImplFontData( rDFA, IFTSFONT_MAGIC ),
mpFtFontInfo( pFI )
{
mbDevice = false;
mbOrientation = true;
}
// -----------------------------------------------------------------------
ImplFontEntry* ImplFTSFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const
{
ImplServerFontEntry* pEntry = new ImplServerFontEntry( rFSD );
return pEntry;
}
// =======================================================================
// FreetypeServerFont
// =======================================================================
FreetypeServerFont::FreetypeServerFont( const ImplFontSelectData& rFSD, FtFontInfo* pFI )
: ServerFont( rFSD ),
mnPrioEmbedded(nDefaultPrioEmbedded),
mnPrioAntiAlias(nDefaultPrioAntiAlias),
mnPrioAutoHint(nDefaultPrioAutoHint),
mpFontInfo( pFI ),
maFaceFT( NULL ),
maSizeFT( NULL ),
mbFaceOk( false ),
maRecodeConverter( NULL ),
mpLayoutEngine( NULL )
{
maFaceFT = pFI->GetFaceFT();
#ifdef HDU_DEBUG
fprintf( stderr, "FTSF::FTSF(\"%s\", h=%d, w=%d, sy=%d) => %d\n",
pFI->GetFontFileName()->getStr(), rFSD.mnHeight, rFSD.mnWidth, pFI->IsSymbolFont(), maFaceFT!=0 );
#endif
if( !maFaceFT )
return;
// set the pixel size of the font instance
mnWidth = rFSD.mnWidth;
if( !mnWidth )
mnWidth = rFSD.mnHeight;
mfStretch = (double)mnWidth / rFSD.mnHeight;
// sanity check (e.g. #i66394#, #i66244#, #66537#)
if( (mnWidth < 0) || (mfStretch > +64.0) || (mfStretch < -64.0) )
return;
// perf: use maSizeFT if available
if( bEnableSizeFT )
{
pFTNewSize( maFaceFT, &maSizeFT );
pFTActivateSize( maSizeFT );
}
FT_Error rc = FT_Set_Pixel_Sizes( maFaceFT, mnWidth, rFSD.mnHeight );
if( rc != FT_Err_Ok )
return;
// prepare for font encodings other than unicode or symbol
FT_Encoding eEncoding = FT_ENCODING_UNICODE;
if( mpFontInfo->IsSymbolFont() )
{
#if (FTVERSION < 2000)
eEncoding = FT_ENCODING_NONE;
#else
if( FT_IS_SFNT( maFaceFT ) )
eEncoding = ft_encoding_symbol;
else
eEncoding = FT_ENCODING_ADOBE_CUSTOM; // freetype wants this for PS symbol fonts
#endif
}
rc = FT_Select_Charmap( maFaceFT, eEncoding );
// no standard encoding applies => we need an encoding converter
if( rc != FT_Err_Ok )
{
rtl_TextEncoding eRecodeFrom = RTL_TEXTENCODING_UNICODE;
for( int i = maFaceFT->num_charmaps; --i >= 0; )
{
const FT_CharMap aCM = maFaceFT->charmaps[i];
if( aCM->platform_id == TT_PLATFORM_MICROSOFT )
{
switch( aCM->encoding_id )
{
case TT_MS_ID_SJIS:
eEncoding = FT_ENCODING_SJIS;
eRecodeFrom = RTL_TEXTENCODING_SHIFT_JIS;
break;
case TT_MS_ID_GB2312:
eEncoding = FT_ENCODING_GB2312;
eRecodeFrom = RTL_TEXTENCODING_GB_2312;
break;
case TT_MS_ID_BIG_5:
eEncoding = FT_ENCODING_BIG5;
eRecodeFrom = RTL_TEXTENCODING_BIG5;
break;
case TT_MS_ID_WANSUNG:
eEncoding = FT_ENCODING_WANSUNG;
eRecodeFrom = RTL_TEXTENCODING_MS_949;
break;
case TT_MS_ID_JOHAB:
eEncoding = FT_ENCODING_JOHAB;
eRecodeFrom = RTL_TEXTENCODING_MS_1361;
break;
}
}
else if( aCM->platform_id == TT_PLATFORM_MACINTOSH )
{
switch( aCM->encoding_id )
{
case TT_MAC_ID_ROMAN:
eEncoding = FT_ENCODING_APPLE_ROMAN;
eRecodeFrom = RTL_TEXTENCODING_UNICODE; // TODO: use better match
break;
// TODO: add other encodings when Mac-only
// non-unicode fonts show up
}
}
else if( aCM->platform_id == TT_PLATFORM_ADOBE )
{
switch( aCM->encoding_id )
{
#ifdef TT_ADOBE_ID_LATIN1
case TT_ADOBE_ID_LATIN1: // better unicode than nothing
eEncoding = FT_ENCODING_ADOBE_LATIN_1;
eRecodeFrom = RTL_TEXTENCODING_ISO_8859_1;
break;
#endif // TT_ADOBE_ID_LATIN1
case TT_ADOBE_ID_STANDARD: // better unicode than nothing
eEncoding = FT_ENCODING_ADOBE_STANDARD;
eRecodeFrom = RTL_TEXTENCODING_UNICODE; // TODO: use better match
break;
}
}
}
if( FT_Err_Ok != FT_Select_Charmap( maFaceFT, eEncoding ) )
return;
if( eRecodeFrom != RTL_TEXTENCODING_UNICODE )
maRecodeConverter = rtl_createUnicodeToTextConverter( eRecodeFrom );
}
mbFaceOk = true;
ApplyGSUB( rFSD );
// TODO: query GASP table for load flags
mnLoadFlags = FT_LOAD_DEFAULT;
#if 1 // #i97326# cairo sometimes uses FT_Set_Transform() on our FT_FACE
// we are not using FT_Set_Transform() yet, so just ignore it for now
mnLoadFlags |= FT_LOAD_IGNORE_TRANSFORM;
#endif
mbArtItalic = (rFSD.meItalic != ITALIC_NONE && pFI->GetFontAttributes().GetSlant() == ITALIC_NONE);
mbArtBold = (rFSD.meWeight > WEIGHT_MEDIUM && pFI->GetFontAttributes().GetWeight() <= WEIGHT_MEDIUM);
mbUseGamma = false;
if( mbArtBold )
{
//static const int TT_CODEPAGE_RANGE_874 = (1L << 16); // Thai
//static const int TT_CODEPAGE_RANGE_932 = (1L << 17); // JIS/Japan
//static const int TT_CODEPAGE_RANGE_936 = (1L << 18); // Chinese: Simplified
//static const int TT_CODEPAGE_RANGE_949 = (1L << 19); // Korean Wansung
//static const int TT_CODEPAGE_RANGE_950 = (1L << 20); // Chinese: Traditional
//static const int TT_CODEPAGE_RANGE_1361 = (1L << 21); // Korean Johab
static const int TT_CODEPAGE_RANGES1_CJKT = 0x3F0000; // all of the above
const TT_OS2* pOs2 = (const TT_OS2*)FT_Get_Sfnt_Table( maFaceFT, ft_sfnt_os2 );
if ((pOs2) && (pOs2->ulCodePageRange1 & TT_CODEPAGE_RANGES1_CJKT )
&& rFSD.mnHeight < 20)
mbUseGamma = true;
}
if( ((mnCos != 0) && (mnSin != 0)) || (mnPrioEmbedded <= 0) )
mnLoadFlags |= FT_LOAD_NO_BITMAP;
}
void FreetypeServerFont::SetFontOptions( const ImplFontOptions& rFontOptions)
{
FontAutoHint eHint = rFontOptions.GetUseAutoHint();
if( eHint == AUTOHINT_DONTKNOW )
eHint = mbUseGamma ? AUTOHINT_TRUE : AUTOHINT_FALSE;
if( eHint == AUTOHINT_TRUE )
mnLoadFlags |= FT_LOAD_FORCE_AUTOHINT;
if( (mnSin != 0) && (mnCos != 0) ) // hinting for 0/90/180/270 degrees only
mnLoadFlags |= FT_LOAD_NO_HINTING;
mnLoadFlags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; //#88334#
if( rFontOptions.DontUseAntiAlias() )
mnPrioAntiAlias = 0;
if( rFontOptions.DontUseEmbeddedBitmaps() )
mnPrioEmbedded = 0;
if( rFontOptions.DontUseHinting() )
mnPrioAutoHint = 0;
#if (FTVERSION >= 2005) || defined(TT_CONFIG_OPTION_BYTECODE_INTERPRETER)
if( mnPrioAutoHint <= 0 )
#endif
mnLoadFlags |= FT_LOAD_NO_HINTING;
#if defined(FT_LOAD_TARGET_LIGHT) && defined(FT_LOAD_TARGET_NORMAL)
if( !(mnLoadFlags & FT_LOAD_NO_HINTING) && (nFTVERSION >= 2103))
{
mnLoadFlags |= FT_LOAD_TARGET_NORMAL;
switch( rFontOptions.GetHintStyle() )
{
case HINT_NONE:
mnLoadFlags |= FT_LOAD_NO_HINTING;
break;
case HINT_SLIGHT:
mnLoadFlags |= FT_LOAD_TARGET_LIGHT;
break;
case HINT_MEDIUM:
break;
case HINT_FULL:
default:
break;
}
}
#endif
if( mnPrioEmbedded <= 0 )
mnLoadFlags |= FT_LOAD_NO_BITMAP;
}
// -----------------------------------------------------------------------
bool FreetypeServerFont::TestFont() const
{
return mbFaceOk;
}
// -----------------------------------------------------------------------
FreetypeServerFont::~FreetypeServerFont()
{
if( mpLayoutEngine )
delete mpLayoutEngine;
if( maRecodeConverter )
rtl_destroyUnicodeToTextConverter( maRecodeConverter );
if( maSizeFT )
pFTDoneSize( maSizeFT );
mpFontInfo->ReleaseFaceFT( maFaceFT );
}
// -----------------------------------------------------------------------
int FreetypeServerFont::GetEmUnits() const
{
return maFaceFT->units_per_EM;
}
// -----------------------------------------------------------------------
void FreetypeServerFont::FetchFontMetric( ImplFontMetricData& rTo, long& rFactor ) const
{
static_cast<ImplFontAttributes&>(rTo) = mpFontInfo->GetFontAttributes();
rTo.mbScalableFont = true;
rTo.mbDevice = true;
rTo.mbKernableFont = (FT_HAS_KERNING( maFaceFT ) != 0) || mpFontInfo->HasExtraKerning();
rTo.mnOrientation = GetFontSelData().mnOrientation;
//Always consider [star]symbol as symbol fonts
if (
(rTo.GetFamilyName().EqualsAscii("OpenSymbol")) ||
(rTo.GetFamilyName().EqualsAscii("StarSymbol"))
)
{
rTo.mbSymbolFlag = true;
}
if( maSizeFT )
pFTActivateSize( maSizeFT );
rFactor = 0x100;
rTo.mnWidth = mnWidth;
const FT_Size_Metrics& rMetrics = maFaceFT->size->metrics;
rTo.mnAscent = (+rMetrics.ascender + 32) >> 6;
#if (FTVERSION < 2000)
rTo.mnDescent = (+rMetrics.descender + 32) >> 6;
#else
rTo.mnDescent = (-rMetrics.descender + 32) >> 6;
#endif
rTo.mnIntLeading = ((rMetrics.height + 32) >> 6) - (rTo.mnAscent + rTo.mnDescent);
rTo.mnSlant = 0;
const TT_OS2* pOS2 = (const TT_OS2*)FT_Get_Sfnt_Table( maFaceFT, ft_sfnt_os2 );
const TT_HoriHeader* pHHEA = (const TT_HoriHeader*)FT_Get_Sfnt_Table( maFaceFT, ft_sfnt_hhea );
if( pOS2 && (pOS2->version != 0xFFFF) )
{
// map the panose info from the OS2 table to their VCL counterparts
switch( pOS2->panose[0] )
{
case 1: rTo.meFamily = FAMILY_ROMAN; break;
case 2: rTo.meFamily = FAMILY_SWISS; break;
case 3: rTo.meFamily = FAMILY_MODERN; break;
case 4: rTo.meFamily = FAMILY_SCRIPT; break;
case 5: rTo.meFamily = FAMILY_DECORATIVE; break;
// TODO: is it reasonable to override the attribute with DONTKNOW?
case 0: // fall through
default: rTo.meFamilyType = FAMILY_DONTKNOW; break;
}
switch( pOS2->panose[3] )
{
case 2: // fall through
case 3: // fall through
case 4: // fall through
case 5: // fall through
case 6: // fall through
case 7: // fall through
case 8: rTo.mePitch = PITCH_VARIABLE; break;
case 9: rTo.mePitch = PITCH_FIXED; break;
// TODO: is it reasonable to override the attribute with DONTKNOW?
case 0: // fall through
case 1: // fall through
default: rTo.mePitch = PITCH_DONTKNOW; break;
}
// #108862# sanity check, some fonts treat descent as signed !!!
int nDescent = pOS2->usWinDescent;
if( nDescent > 5*maFaceFT->units_per_EM )
nDescent = (short)pOS2->usWinDescent; // interpret it as signed!
const double fScale = (double)GetFontSelData().mnHeight / maFaceFT->units_per_EM;
if( pOS2->usWinAscent || pOS2->usWinDescent ) // #i30551#
{
rTo.mnAscent = (long)( +pOS2->usWinAscent * fScale + 0.5 );
rTo.mnDescent = (long)( +nDescent * fScale + 0.5 );
rTo.mnIntLeading = (long)( (+pOS2->usWinAscent + pOS2->usWinDescent - maFaceFT->units_per_EM) * fScale + 0.5 );
}
rTo.mnExtLeading = 0;
if( (pHHEA != NULL) && (pOS2->usWinAscent || pOS2->usWinDescent) )
{
int nExtLeading = pHHEA->Line_Gap;
nExtLeading -= (pOS2->usWinAscent + pOS2->usWinDescent);
nExtLeading += (pHHEA->Ascender - pHHEA->Descender);
if( nExtLeading > 0 )
rTo.mnExtLeading = (long)(nExtLeading * fScale + 0.5);
}
// Check for CJK capabilities of the current font
// #107888# workaround for Asian...
// TODO: remove when ExtLeading fully implemented
sal_Bool bCJKCapable = ((pOS2->ulUnicodeRange2 & 0x2DF00000) != 0);
if ( bCJKCapable && (pOS2->usWinAscent || pOS2->usWinDescent) )
{
rTo.mnIntLeading += rTo.mnExtLeading;
// #109280# The line height for Asian fonts is too small.
// Therefore we add half of the external leading to the
// ascent, the other half is added to the descent.
const long nHalfTmpExtLeading = rTo.mnExtLeading / 2;
const long nOtherHalfTmpExtLeading = rTo.mnExtLeading -
nHalfTmpExtLeading;
// #110641# external leading for Asian fonts.
// The factor 0.3 has been verified during experiments.
const long nCJKExtLeading = (long)(0.30 * (rTo.mnAscent + rTo.mnDescent));
if ( nCJKExtLeading > rTo.mnExtLeading )
rTo.mnExtLeading = nCJKExtLeading - rTo.mnExtLeading;
else
rTo.mnExtLeading = 0;
rTo.mnAscent += nHalfTmpExtLeading;
rTo.mnDescent += nOtherHalfTmpExtLeading;
}
}
// initialize kashida width
// TODO: what if there are different versions of this glyph available
rTo.mnMinKashida = rTo.mnAscent / 4; // a reasonable default
const int nKashidaGlyphId = GetRawGlyphIndex( 0x0640 );
if( nKashidaGlyphId )
{
GlyphData aGlyphData;
InitGlyphData( nKashidaGlyphId, aGlyphData );
rTo.mnMinKashida = aGlyphData.GetMetric().GetCharWidth();
}
}
// -----------------------------------------------------------------------
static inline void SplitGlyphFlags( const FreetypeServerFont& rFont, sal_GlyphId& rGlyphId, int& nGlyphFlags )
{
nGlyphFlags = rGlyphId & GF_FLAGMASK;
rGlyphId &= GF_IDXMASK;
if( rGlyphId & GF_ISCHAR )
rGlyphId = rFont.GetRawGlyphIndex( rGlyphId );
}
// -----------------------------------------------------------------------
int FreetypeServerFont::ApplyGlyphTransform( int nGlyphFlags,
FT_Glyph pGlyphFT, bool bForBitmapProcessing ) const
{
int nAngle = GetFontSelData().mnOrientation;
// shortcut most common case
if( !nAngle && !nGlyphFlags )
return nAngle;
const FT_Size_Metrics& rMetrics = maFaceFT->size->metrics;
FT_Vector aVector;
FT_Matrix aMatrix;
bool bStretched = false;
switch( nGlyphFlags & GF_ROTMASK )
{
default: // straight
aVector.x = 0;
aVector.y = 0;
aMatrix.xx = +mnCos;
aMatrix.yy = +mnCos;
aMatrix.xy = -mnSin;
aMatrix.yx = +mnSin;
break;
case GF_ROTL: // left
nAngle += 900;
bStretched = (mfStretch != 1.0);
aVector.x = (FT_Pos)(+rMetrics.descender * mfStretch);
aVector.y = -rMetrics.ascender;
aMatrix.xx = (FT_Pos)(-mnSin / mfStretch);
aMatrix.yy = (FT_Pos)(-mnSin * mfStretch);
aMatrix.xy = (FT_Pos)(-mnCos * mfStretch);
aMatrix.yx = (FT_Pos)(+mnCos / mfStretch);
break;
case GF_ROTR: // right
nAngle -= 900;
bStretched = (mfStretch != 1.0);
aVector.x = -maFaceFT->glyph->metrics.horiAdvance;
aVector.x += (FT_Pos)(rMetrics.descender * mnSin/65536.0);
aVector.y = (FT_Pos)(-rMetrics.descender * mfStretch * mnCos/65536.0);
aMatrix.xx = (FT_Pos)(+mnSin / mfStretch);
aMatrix.yy = (FT_Pos)(+mnSin * mfStretch);
aMatrix.xy = (FT_Pos)(+mnCos * mfStretch);
aMatrix.yx = (FT_Pos)(-mnCos / mfStretch);
break;
}
while( nAngle < 0 )
nAngle += 3600;
if( pGlyphFT->format != FT_GLYPH_FORMAT_BITMAP )
{
FT_Glyph_Transform( pGlyphFT, NULL, &aVector );
// orthogonal transforms are better handled by bitmap operations
if( bStretched || (bForBitmapProcessing && (nAngle % 900) != 0) )
{
// workaround for compatibility with older FT versions
if( nFTVERSION < 2102 )
{
FT_Fixed t = aMatrix.xy;
aMatrix.xy = aMatrix.yx;
aMatrix.yx = t;
}
// apply non-orthogonal or stretch transformations
FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
nAngle = 0;
}
}
else
{
// FT<=2005 ignores transforms for bitmaps, so do it manually
FT_BitmapGlyph pBmpGlyphFT = reinterpret_cast<FT_BitmapGlyph>(pGlyphFT);
pBmpGlyphFT->left += (aVector.x + 32) >> 6;
pBmpGlyphFT->top += (aVector.y + 32) >> 6;
}
return nAngle;
}
// -----------------------------------------------------------------------
sal_GlyphId FreetypeServerFont::GetRawGlyphIndex( sal_UCS4 aChar ) const
{
if( mpFontInfo->IsSymbolFont() )
{
if( !FT_IS_SFNT( maFaceFT ) )
{
if( (aChar & 0xFF00) == 0xF000 )
aChar &= 0xFF; // PS font symbol mapping
else if( aChar > 0xFF )
return 0;
}
}
// if needed recode from unicode to font encoding
if( maRecodeConverter )
{
sal_Char aTempArray[8];
sal_Size nTempSize;
sal_uInt32 nCvtInfo;
// assume that modern UCS4 fonts have unicode CMAPs
// => no encoding remapping to unicode is needed
if( aChar > 0xFFFF )
return 0;
sal_Unicode aUCS2Char = static_cast<sal_Unicode>(aChar);
rtl_UnicodeToTextContext aContext = rtl_createUnicodeToTextContext( maRecodeConverter );
int nChars = rtl_convertUnicodeToText( maRecodeConverter, aContext,
&aUCS2Char, 1, aTempArray, sizeof(aTempArray),
RTL_UNICODETOTEXT_FLAGS_UNDEFINED_QUESTIONMARK
| RTL_UNICODETOTEXT_FLAGS_INVALID_QUESTIONMARK,
&nCvtInfo, &nTempSize );
rtl_destroyUnicodeToTextContext( maRecodeConverter, aContext );
aChar = 0;
for( int i = 0; i < nChars; ++i )
aChar = aChar*256 + (aTempArray[i] & 0xFF);
}
// cache glyph indexes in font info to share between different sizes
int nGlyphIndex = mpFontInfo->GetGlyphIndex( aChar );
if( nGlyphIndex < 0 )
{
nGlyphIndex = FT_Get_Char_Index( maFaceFT, aChar );
if( !nGlyphIndex)
{
// check if symbol aliasing helps
if( (aChar <= 0x00FF) && mpFontInfo->IsSymbolFont() )
nGlyphIndex = FT_Get_Char_Index( maFaceFT, aChar | 0xF000 );
#if 0 // disabled for now because it introduced ae bad side-effect (#i88376#)
// Finally try the postscript name table
if (!nGlyphIndex)
nGlyphIndex = psp::PrintFontManager::get().FreeTypeCharIndex( maFaceFT, aChar );
#endif
}
mpFontInfo->CacheGlyphIndex( aChar, nGlyphIndex );
}
return sal_GlyphId( nGlyphIndex);
}
// -----------------------------------------------------------------------
sal_GlyphId FreetypeServerFont::FixupGlyphIndex( sal_GlyphId aGlyphId, sal_UCS4 aChar ) const
{
int nGlyphFlags = GF_NONE;
// do glyph substitution if necessary
// CJK vertical writing needs special treatment
if( GetFontSelData().mbVertical )
{
// TODO: rethink when GSUB is used for non-vertical case
GlyphSubstitution::const_iterator it = maGlyphSubstitution.find( aGlyphId );
if( it == maGlyphSubstitution.end() )
{
sal_GlyphId nTemp = GetVerticalChar( aChar );
if( nTemp ) // is substitution possible
nTemp = GetRawGlyphIndex( nTemp );
if( nTemp ) // substitute manually if sensible
aGlyphId = nTemp | (GF_GSUB | GF_ROTL);
else
nGlyphFlags |= GetVerticalFlags( aChar );
}
else
{
// for vertical GSUB also compensate for nOrientation=2700
aGlyphId = (*it).second;
nGlyphFlags |= GF_GSUB | GF_ROTL;
}
}
#if 0
// #95556# autohinting not yet optimized for non-western glyph styles
if( !(mnLoadFlags & (FT_LOAD_NO_HINTING | FT_LOAD_FORCE_AUTOHINT) )
&& ( (aChar >= 0x0600 && aChar < 0x1E00) // south-east asian + arabic
||(aChar >= 0x2900 && aChar < 0xD800) // CJKV
||(aChar >= 0xF800) ) ) // presentation + symbols
{
nGlyphFlags |= GF_UNHINTED;
}
#endif
if( aGlyphId != 0 )
aGlyphId |= nGlyphFlags;
return aGlyphId;
}
// -----------------------------------------------------------------------
sal_GlyphId FreetypeServerFont::GetGlyphIndex( sal_UCS4 aChar ) const
{
sal_GlyphId aGlyphId = GetRawGlyphIndex( aChar );
aGlyphId = FixupGlyphIndex( aGlyphId, aChar );
return aGlyphId;
}
// -----------------------------------------------------------------------
static int lcl_GetCharWidth( FT_FaceRec_* pFaceFT, double fStretch, int nGlyphFlags )
{
int nCharWidth = pFaceFT->glyph->metrics.horiAdvance;
if( nGlyphFlags & GF_ROTMASK ) // for bVertical rotated glyphs
{
const FT_Size_Metrics& rMetrics = pFaceFT->size->metrics;
#if (FTVERSION < 2000)
nCharWidth = (int)((rMetrics.height - rMetrics.descender) * fStretch);
#else
nCharWidth = (int)((rMetrics.height + rMetrics.descender) * fStretch);
#endif
}
return (nCharWidth + 32) >> 6;
}
// -----------------------------------------------------------------------
void FreetypeServerFont::InitGlyphData( sal_GlyphId aGlyphId, GlyphData& rGD ) const
{
if( maSizeFT )
pFTActivateSize( maSizeFT );
int nGlyphFlags;
SplitGlyphFlags( *this, aGlyphId, nGlyphFlags );
int nLoadFlags = mnLoadFlags;
// if( mbArtItalic )
// nLoadFlags |= FT_LOAD_NO_BITMAP;
FT_Error rc = -1;
#if (FTVERSION <= 2008)
// #88364# freetype<=2005 prefers autohinting to embedded bitmaps
// => first we have to try without hinting
if( (nLoadFlags & (FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) == 0 )
{
rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags|FT_LOAD_NO_HINTING );
if( (rc==FT_Err_Ok) && (maFaceFT->glyph->format!=FT_GLYPH_FORMAT_BITMAP) )
rc = -1; // mark as "loading embedded bitmap" was unsuccessful
nLoadFlags |= FT_LOAD_NO_BITMAP;
}
if( rc != FT_Err_Ok )
#endif
rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags );
if( rc != FT_Err_Ok )
{
// we get here e.g. when a PS font lacks the default glyph
rGD.SetCharWidth( 0 );
rGD.SetDelta( 0, 0 );
rGD.SetOffset( 0, 0 );
rGD.SetSize( Size( 0, 0 ) );
return;
}
const bool bOriginallyZeroWidth = (maFaceFT->glyph->metrics.horiAdvance == 0);
if( mbArtBold && pFTEmbolden )
(*pFTEmbolden)( maFaceFT->glyph );
const int nCharWidth = bOriginallyZeroWidth ? 0 : lcl_GetCharWidth( maFaceFT, mfStretch, nGlyphFlags );
rGD.SetCharWidth( nCharWidth );
FT_Glyph pGlyphFT;
rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
ApplyGlyphTransform( nGlyphFlags, pGlyphFT, false );
if( mbArtBold && pFTEmbolden && (nFTVERSION < 2200) ) // #i71094# workaround staircase bug
pGlyphFT->advance.y = 0;
rGD.SetDelta( (pGlyphFT->advance.x + 0x8000) >> 16, -((pGlyphFT->advance.y + 0x8000) >> 16) );
FT_BBox aBbox;
FT_Glyph_Get_CBox( pGlyphFT, FT_GLYPH_BBOX_PIXELS, &aBbox );
if( aBbox.yMin > aBbox.yMax ) // circumvent freetype bug
{
int t=aBbox.yMin; aBbox.yMin=aBbox.yMax, aBbox.yMax=t;
}
rGD.SetOffset( aBbox.xMin, -aBbox.yMax );
rGD.SetSize( Size( (aBbox.xMax-aBbox.xMin+1), (aBbox.yMax-aBbox.yMin) ) );
FT_Done_Glyph( pGlyphFT );
}
// -----------------------------------------------------------------------
bool FreetypeServerFont::GetAntialiasAdvice( void ) const
{
if( GetFontSelData().mbNonAntialiased || (mnPrioAntiAlias<=0) )
return false;
bool bAdviseAA = true;
// TODO: also use GASP info
return bAdviseAA;
}
// -----------------------------------------------------------------------
bool FreetypeServerFont::GetGlyphBitmap1( sal_GlyphId aGlyphId, RawBitmap& rRawBitmap ) const
{
if( maSizeFT )
pFTActivateSize( maSizeFT );
int nGlyphFlags;
SplitGlyphFlags( *this, aGlyphId, nGlyphFlags );
FT_Int nLoadFlags = mnLoadFlags;
// #i70930# force mono-hinting for monochrome text
if( nFTVERSION >= 2110 ) //#i71947# unless it looks worse
{
nLoadFlags &= ~0xF0000;
nLoadFlags |= FT_LOAD_TARGET_MONO;
}
if( mbArtItalic )
nLoadFlags |= FT_LOAD_NO_BITMAP;
#if (FTVERSION >= 2002)
// for 0/90/180/270 degree fonts enable hinting even if not advisable
// non-hinted and non-antialiased bitmaps just look too ugly
if( (mnCos==0 || mnSin==0) && (mnPrioAutoHint > 0) )
nLoadFlags &= ~FT_LOAD_NO_HINTING;
#endif
if( mnPrioEmbedded <= mnPrioAutoHint )
nLoadFlags |= FT_LOAD_NO_BITMAP;
FT_Error rc = -1;
#if (FTVERSION <= 2008)
// #88364# freetype<=2005 prefers autohinting to embedded bitmaps
// => first we have to try without hinting
if( (nLoadFlags & (FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) == 0 )
{
rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags|FT_LOAD_NO_HINTING );
if( (rc==FT_Err_Ok) && (maFaceFT->glyph->format != FT_GLYPH_FORMAT_BITMAP) )
rc = -1; // mark as "loading embedded bitmap" was unsuccessful
nLoadFlags |= FT_LOAD_NO_BITMAP;
}
if( rc != FT_Err_Ok )
#endif
rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags );
if( rc != FT_Err_Ok )
return false;
if( mbArtBold && pFTEmbolden )
(*pFTEmbolden)( maFaceFT->glyph );
FT_Glyph pGlyphFT;
rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
if( rc != FT_Err_Ok )
return false;
int nAngle = ApplyGlyphTransform( nGlyphFlags, pGlyphFT, true );
if( mbArtItalic )
{
FT_Matrix aMatrix;
aMatrix.xx = aMatrix.yy = 0x10000L;
if( nFTVERSION >= 2102 ) // Freetype 2.1.2 API swapped xy with yx
aMatrix.xy = 0x6000L, aMatrix.yx = 0;
else
aMatrix.yx = 0x6000L, aMatrix.xy = 0;
FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
}
// Check for zero area bounding boxes as this crashes some versions of FT.
// This also provides a handy short cut as much of the code following
// becomes an expensive nop when a glyph covers no pixels.
FT_BBox cbox;
FT_Glyph_Get_CBox(pGlyphFT, ft_glyph_bbox_unscaled, &cbox);
if( (cbox.xMax - cbox.xMin) == 0 || (cbox.yMax - cbox.yMin == 0) )
{
nAngle = 0;
memset(&rRawBitmap, 0, sizeof rRawBitmap);
FT_Done_Glyph( pGlyphFT );
return true;
}
if( pGlyphFT->format != FT_GLYPH_FORMAT_BITMAP )
{
if( pGlyphFT->format == FT_GLYPH_FORMAT_OUTLINE )
((FT_OutlineGlyphRec*)pGlyphFT)->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
// #i15743# freetype API 2.1.3 changed the FT_RENDER_MODE_MONO constant
FT_Render_Mode nRenderMode = (FT_Render_Mode)((nFTVERSION<2103) ? 1 : FT_RENDER_MODE_MONO);
rc = FT_Glyph_To_Bitmap( &pGlyphFT, nRenderMode, NULL, sal_True );
if( rc != FT_Err_Ok )
{
FT_Done_Glyph( pGlyphFT );
return false;
}
}
const FT_BitmapGlyph pBmpGlyphFT = reinterpret_cast<const FT_BitmapGlyph>(pGlyphFT);
// NOTE: autohinting in FT<=2.0.2 miscalculates the offsets below by +-1
rRawBitmap.mnXOffset = +pBmpGlyphFT->left;
rRawBitmap.mnYOffset = -pBmpGlyphFT->top;
const FT_Bitmap& rBitmapFT = pBmpGlyphFT->bitmap;
rRawBitmap.mnHeight = rBitmapFT.rows;
rRawBitmap.mnBitCount = 1;
if( mbArtBold && !pFTEmbolden )
{
rRawBitmap.mnWidth = rBitmapFT.width + 1;
int nLineBytes = (rRawBitmap.mnWidth + 7) >> 3;
rRawBitmap.mnScanlineSize = (nLineBytes > rBitmapFT.pitch) ? nLineBytes : rBitmapFT.pitch;
}
else
{
rRawBitmap.mnWidth = rBitmapFT.width;
rRawBitmap.mnScanlineSize = rBitmapFT.pitch;
}
const sal_uLong nNeededSize = rRawBitmap.mnScanlineSize * rRawBitmap.mnHeight;
if( rRawBitmap.mnAllocated < nNeededSize )
{
delete[] rRawBitmap.mpBits;
rRawBitmap.mnAllocated = 2*nNeededSize;
rRawBitmap.mpBits = new unsigned char[ rRawBitmap.mnAllocated ];
}
if( !mbArtBold || pFTEmbolden )
{
memcpy( rRawBitmap.mpBits, rBitmapFT.buffer, nNeededSize );
}
else
{
memset( rRawBitmap.mpBits, 0, nNeededSize );
const unsigned char* pSrcLine = rBitmapFT.buffer;
unsigned char* pDstLine = rRawBitmap.mpBits;
for( int h = rRawBitmap.mnHeight; --h >= 0; )
{
memcpy( pDstLine, pSrcLine, rBitmapFT.pitch );
pDstLine += rRawBitmap.mnScanlineSize;
pSrcLine += rBitmapFT.pitch;
}
unsigned char* p = rRawBitmap.mpBits;
for( sal_uLong y=0; y < rRawBitmap.mnHeight; y++ )
{
unsigned char nLastByte = 0;
for( sal_uLong x=0; x < rRawBitmap.mnScanlineSize; x++ )
{
unsigned char nTmp = p[x] << 7;
p[x] |= (p[x] >> 1) | nLastByte;
nLastByte = nTmp;
}
p += rRawBitmap.mnScanlineSize;
}
}
FT_Done_Glyph( pGlyphFT );
// special case for 0/90/180/270 degree orientation
switch( nAngle )
{
case -900:
case +900:
case +1800:
case +2700:
rRawBitmap.Rotate( nAngle );
break;
}
return true;
}
// -----------------------------------------------------------------------
bool FreetypeServerFont::GetGlyphBitmap8( sal_GlyphId aGlyphId, RawBitmap& rRawBitmap ) const
{
if( maSizeFT )
pFTActivateSize( maSizeFT );
int nGlyphFlags;
SplitGlyphFlags( *this, aGlyphId, nGlyphFlags );
FT_Int nLoadFlags = mnLoadFlags;
if( mbArtItalic )
nLoadFlags |= FT_LOAD_NO_BITMAP;
#if (FTVERSION <= 2004) && !defined(TT_CONFIG_OPTION_BYTECODE_INTERPRETER)
// autohinting in FT<=2.0.4 makes antialiased glyphs look worse
nLoadFlags |= FT_LOAD_NO_HINTING;
#else
if( (nGlyphFlags & GF_UNHINTED) || (mnPrioAutoHint < mnPrioAntiAlias) )
nLoadFlags |= FT_LOAD_NO_HINTING;
#endif
if( mnPrioEmbedded <= mnPrioAntiAlias )
nLoadFlags |= FT_LOAD_NO_BITMAP;
FT_Error rc = -1;
#if (FTVERSION <= 2008)
// #88364# freetype<=2005 prefers autohinting to embedded bitmaps
// => first we have to try without hinting
if( (nLoadFlags & (FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) == 0 )
{
rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags|FT_LOAD_NO_HINTING );
if( (rc==FT_Err_Ok) && (maFaceFT->glyph->format != FT_GLYPH_FORMAT_BITMAP) )
rc = -1; // mark as "loading embedded bitmap" was unsuccessful
nLoadFlags |= FT_LOAD_NO_BITMAP;
}
if( rc != FT_Err_Ok )
#endif
rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags );
if( rc != FT_Err_Ok )
return false;
if( mbArtBold && pFTEmbolden )
(*pFTEmbolden)( maFaceFT->glyph );
FT_Glyph pGlyphFT;
rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
if( rc != FT_Err_Ok )
return false;
int nAngle = ApplyGlyphTransform( nGlyphFlags, pGlyphFT, true );
if( mbArtItalic )
{
FT_Matrix aMatrix;
aMatrix.xx = aMatrix.yy = 0x10000L;
if( nFTVERSION >= 2102 ) // Freetype 2.1.2 API swapped xy with yx
aMatrix.xy = 0x6000L, aMatrix.yx = 0;
else
aMatrix.yx = 0x6000L, aMatrix.xy = 0;
FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
}
if( pGlyphFT->format == FT_GLYPH_FORMAT_OUTLINE )
((FT_OutlineGlyph)pGlyphFT)->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
bool bEmbedded = (pGlyphFT->format == FT_GLYPH_FORMAT_BITMAP);
if( !bEmbedded )
{
rc = FT_Glyph_To_Bitmap( &pGlyphFT, FT_RENDER_MODE_NORMAL, NULL, sal_True );
if( rc != FT_Err_Ok )
{
FT_Done_Glyph( pGlyphFT );
return false;
}
}
const FT_BitmapGlyph pBmpGlyphFT = reinterpret_cast<const FT_BitmapGlyph>(pGlyphFT);
rRawBitmap.mnXOffset = +pBmpGlyphFT->left;
rRawBitmap.mnYOffset = -pBmpGlyphFT->top;
const FT_Bitmap& rBitmapFT = pBmpGlyphFT->bitmap;
rRawBitmap.mnHeight = rBitmapFT.rows;
rRawBitmap.mnWidth = rBitmapFT.width;
rRawBitmap.mnBitCount = 8;
rRawBitmap.mnScanlineSize = bEmbedded ? rBitmapFT.width : rBitmapFT.pitch;
if( mbArtBold && !pFTEmbolden )
{
++rRawBitmap.mnWidth;
++rRawBitmap.mnScanlineSize;
}
rRawBitmap.mnScanlineSize = (rRawBitmap.mnScanlineSize + 3) & -4;
const sal_uLong nNeededSize = rRawBitmap.mnScanlineSize * rRawBitmap.mnHeight;
if( rRawBitmap.mnAllocated < nNeededSize )
{
delete[] rRawBitmap.mpBits;
rRawBitmap.mnAllocated = 2*nNeededSize;
rRawBitmap.mpBits = new unsigned char[ rRawBitmap.mnAllocated ];
}
const unsigned char* pSrc = rBitmapFT.buffer;
unsigned char* pDest = rRawBitmap.mpBits;
if( !bEmbedded )
{
for( int y = rRawBitmap.mnHeight, x; --y >= 0 ; )
{
for( x = 0; x < rBitmapFT.width; ++x )
*(pDest++) = *(pSrc++);
for(; x < int(rRawBitmap.mnScanlineSize); ++x )
*(pDest++) = 0;
}
}
else
{
for( int y = rRawBitmap.mnHeight, x; --y >= 0 ; )
{
unsigned char nSrc = 0;
for( x = 0; x < rBitmapFT.width; ++x, nSrc+=nSrc )
{
if( (x & 7) == 0 )
nSrc = *(pSrc++);
*(pDest++) = (0x7F - nSrc) >> 8;
}
for(; x < int(rRawBitmap.mnScanlineSize); ++x )
*(pDest++) = 0;
}
}
if( mbArtBold && !pFTEmbolden )
{
// overlay with glyph image shifted by one left pixel
unsigned char* p = rRawBitmap.mpBits;
for( sal_uLong y=0; y < rRawBitmap.mnHeight; y++ )
{
unsigned char nLastByte = 0;
for( sal_uLong x=0; x < rRawBitmap.mnWidth; x++ )
{
unsigned char nTmp = p[x];
p[x] |= p[x] | nLastByte;
nLastByte = nTmp;
}
p += rRawBitmap.mnScanlineSize;
}
}
if( !bEmbedded && mbUseGamma )
{
unsigned char* p = rRawBitmap.mpBits;
for( sal_uLong y=0; y < rRawBitmap.mnHeight; y++ )
{
for( sal_uLong x=0; x < rRawBitmap.mnWidth; x++ )
{
p[x] = aGammaTable[ p[x] ];
}
p += rRawBitmap.mnScanlineSize;
}
}
FT_Done_Glyph( pGlyphFT );
// special case for 0/90/180/270 degree orientation
switch( nAngle )
{
case -900:
case +900:
case +1800:
case +2700:
rRawBitmap.Rotate( nAngle );
break;
}
return true;
}
// -----------------------------------------------------------------------
// determine unicode ranges in font
// -----------------------------------------------------------------------
const ImplFontCharMap* FreetypeServerFont::GetImplFontCharMap( void ) const
{
const ImplFontCharMap* pIFCMap = mpFontInfo->GetImplFontCharMap();
return pIFCMap;
}
const ImplFontCharMap* FtFontInfo::GetImplFontCharMap( void )
{
// check if the charmap is already cached
if( mpFontCharMap )
return mpFontCharMap;
// get the charmap and cache it
CmapResult aCmapResult;
bool bOK = GetFontCodeRanges( aCmapResult );
if( bOK )
mpFontCharMap = new ImplFontCharMap( aCmapResult );
else
mpFontCharMap = ImplFontCharMap::GetDefaultMap();
mpFontCharMap->AddReference();
return mpFontCharMap;
}
// TODO: merge into method GetFontCharMap()
bool FtFontInfo::GetFontCodeRanges( CmapResult& rResult ) const
{
rResult.mbSymbolic = IsSymbolFont();
// TODO: is the full CmapResult needed on platforms calling this?
if( FT_IS_SFNT( maFaceFT ) )
{
sal_uLong nLength = 0;
const unsigned char* pCmap = GetTable( "cmap", &nLength );
if( pCmap && (nLength > 0) )
if( ParseCMAP( pCmap, nLength, rResult ) )
return true;
}
typedef std::vector<sal_uInt32> U32Vector;
U32Vector aCodes;
// FT's coverage is available since FT>=2.1.0 (OOo-baseline>=2.1.4 => ok)
aCodes.reserve( 0x1000 );
FT_UInt nGlyphIndex;
for( sal_uInt32 cCode = FT_Get_First_Char( maFaceFT, &nGlyphIndex );; )
{
if( !nGlyphIndex )
break;
aCodes.push_back( cCode ); // first code inside range
sal_uInt32 cNext = cCode;
do cNext = FT_Get_Next_Char( maFaceFT, cCode, &nGlyphIndex ); while( cNext == ++cCode );
aCodes.push_back( cCode ); // first code outside range
cCode = cNext;
}
const int nCount = aCodes.size();
if( !nCount) {
if( !rResult.mbSymbolic )
return false;
// we usually get here for Type1 symbol fonts
aCodes.push_back( 0xF020 );
aCodes.push_back( 0xF100 );
}
sal_uInt32* pCodes = new sal_uInt32[ nCount ];
for( int i = 0; i < nCount; ++i )
pCodes[i] = aCodes[i];
rResult.mpRangeCodes = pCodes;
rResult.mnRangeCount = nCount / 2;
return true;
}
// -----------------------------------------------------------------------
// kerning stuff
// -----------------------------------------------------------------------
int FreetypeServerFont::GetGlyphKernValue( int nGlyphLeft, int nGlyphRight ) const
{
// if no kerning info is available from Freetype
// then we may have to use extra info provided by e.g. psprint
if( !FT_HAS_KERNING( maFaceFT ) || !FT_IS_SFNT( maFaceFT ) )
{
int nKernVal = mpFontInfo->GetExtraGlyphKernValue( nGlyphLeft, nGlyphRight );
if( !nKernVal )
return 0;
// scale the kern value to match the font size
const ImplFontSelectData& rFSD = GetFontSelData();
nKernVal *= rFSD.mnWidth ? rFSD.mnWidth : rFSD.mnHeight;
return (nKernVal + 500) / 1000;
}
// when font faces of different sizes share the same maFaceFT
// then we have to make sure that it uses the correct maSizeFT
if( maSizeFT )
pFTActivateSize( maSizeFT );
// use Freetype's kerning info
FT_Vector aKernVal;
FT_Error rcFT = FT_Get_Kerning( maFaceFT, nGlyphLeft, nGlyphRight,
FT_KERNING_DEFAULT, &aKernVal );
int nResult = (rcFT == FT_Err_Ok) ? (aKernVal.x + 32) >> 6 : 0;
return nResult;
}
// -----------------------------------------------------------------------
sal_uLong FreetypeServerFont::GetKernPairs( ImplKernPairData** ppKernPairs ) const
{
// if no kerning info is available in the font file
*ppKernPairs = NULL;
if( !FT_HAS_KERNING( maFaceFT ) || !FT_IS_SFNT( maFaceFT ) )
{
// then we have may have extra kerning info from e.g. psprint
int nCount = mpFontInfo->GetExtraKernPairs( ppKernPairs );
// scale the kern values to match the font size
const ImplFontSelectData& rFSD = GetFontSelData();
int nFontWidth = rFSD.mnWidth ? rFSD.mnWidth : rFSD.mnHeight;
ImplKernPairData* pKernPair = *ppKernPairs;
for( int i = nCount; --i >= 0; ++pKernPair )
{
long& rVal = pKernPair->mnKern;
rVal = ((rVal * nFontWidth) + 500) / 1000;
}
return nCount;
}
// when font faces of different sizes share the same maFaceFT
// then we have to make sure that it uses the correct maSizeFT
if( maSizeFT )
pFTActivateSize( maSizeFT );
// first figure out which glyph pairs are involved in kerning
sal_uLong nKernLength = 0;
const FT_Byte* const pKern = mpFontInfo->GetTable( "kern", &nKernLength );
if( !pKern )
return 0;
// combine TTF/OTF tables from the font file to build a vector of
// unicode kerning pairs using Freetype's glyph kerning calculation
// for the kerning value
// TODO: is it worth to share the glyph->unicode mapping between
// different instances of the same font face?
typedef std::vector<ImplKernPairData> KernVector;
KernVector aKernGlyphVector;
ImplKernPairData aKernPair;
aKernPair.mnKern = 0; // To prevent "is used uninitialized" warning...
const FT_Byte* pBuffer = pKern;
sal_uLong nVersion = GetUShort( pBuffer+0 );
sal_uInt16 nTableCnt = GetUShort( pBuffer+2 );
// Microsoft/Old TrueType style kern table
if ( nVersion == 0 )
{
pBuffer += 4;
for( sal_uInt16 nTableIdx = 0; nTableIdx < nTableCnt; ++nTableIdx )
{
// sal_uInt16 nSubVersion = GetUShort( pBuffer+0 );
// sal_uInt16 nSubLength = GetUShort( pBuffer+2 );
sal_uInt16 nSubCoverage = GetUShort( pBuffer+4 );
pBuffer += 6;
if( (nSubCoverage&0x03) != 0x01 ) // no interest in minimum info here
continue;
switch( nSubCoverage >> 8 )
{
case 0: // version 0, kerning format 0
{
sal_uInt16 nPairs = GetUShort( pBuffer );
pBuffer += 8; // skip search hints
aKernGlyphVector.reserve( aKernGlyphVector.size() + nPairs );
for( int i = 0; i < nPairs; ++i )
{
aKernPair.mnChar1 = GetUShort( pBuffer+0 );
aKernPair.mnChar2 = GetUShort( pBuffer+2 );
//long nUnscaledKern= GetSShort( pBuffer );
pBuffer += 6;
aKernGlyphVector.push_back( aKernPair );
}
}
break;
case 2: // version 0, kerning format 2
{
const FT_Byte* pSubTable = pBuffer;
//sal_uInt16 nRowWidth = GetUShort( pBuffer+0 );
sal_uInt16 nOfsLeft = GetUShort( pBuffer+2 );
sal_uInt16 nOfsRight = GetUShort( pBuffer+4 );
sal_uInt16 nOfsArray = GetUShort( pBuffer+6 );
pBuffer += 8;
const FT_Byte* pTmp = pSubTable + nOfsLeft;
sal_uInt16 nFirstLeft = GetUShort( pTmp+0 );
sal_uInt16 nLastLeft = GetUShort( pTmp+2 ) + nFirstLeft - 1;
pTmp = pSubTable + nOfsRight;
sal_uInt16 nFirstRight = GetUShort( pTmp+0 );
sal_uInt16 nLastRight = GetUShort( pTmp+2 ) + nFirstRight - 1;
sal_uLong nPairs = (sal_uLong)(nLastLeft - nFirstLeft + 1) * (nLastRight - nFirstRight + 1);
aKernGlyphVector.reserve( aKernGlyphVector.size() + nPairs );
pTmp = pSubTable + nOfsArray;
for( int nLeft = nFirstLeft; nLeft < nLastLeft; ++nLeft )
{
aKernPair.mnChar1 = nLeft;
for( int nRight = 0; nRight < nLastRight; ++nRight )
{
if( GetUShort( pTmp ) != 0 )
{
aKernPair.mnChar2 = nRight;
aKernGlyphVector.push_back( aKernPair );
}
pTmp += 2;
}
}
}
break;
}
}
}
// Apple New style kern table
pBuffer = pKern;
nVersion = NEXT_U32( pBuffer );
nTableCnt = NEXT_U32( pBuffer );
if ( nVersion == 0x00010000 )
{
for( sal_uInt16 nTableIdx = 0; nTableIdx < nTableCnt; ++nTableIdx )
{
/*sal_uLong nLength =*/ NEXT_U32( pBuffer );
sal_uInt16 nCoverage = NEXT_U16( pBuffer );
/*sal_uInt16 nTupleIndex =*/ NEXT_U16( pBuffer );
// Kerning sub-table format, 0 through 3
sal_uInt8 nSubTableFormat = nCoverage & 0x00FF;
switch( nSubTableFormat )
{
case 0: // version 0, kerning format 0
{
sal_uInt16 nPairs = NEXT_U16( pBuffer );
pBuffer += 6; // skip search hints
aKernGlyphVector.reserve( aKernGlyphVector.size() + nPairs );
for( int i = 0; i < nPairs; ++i )
{
aKernPair.mnChar1 = NEXT_U16( pBuffer );
aKernPair.mnChar2 = NEXT_U16( pBuffer );
/*long nUnscaledKern=*/ NEXT_S16( pBuffer );
aKernGlyphVector.push_back( aKernPair );
}
}
break;
case 2: // version 0, kerning format 2
{
const FT_Byte* pSubTable = pBuffer;
/*sal_uInt16 nRowWidth =*/ NEXT_U16( pBuffer );
sal_uInt16 nOfsLeft = NEXT_U16( pBuffer );
sal_uInt16 nOfsRight = NEXT_U16( pBuffer );
sal_uInt16 nOfsArray = NEXT_U16( pBuffer );
const FT_Byte* pTmp = pSubTable + nOfsLeft;
sal_uInt16 nFirstLeft = NEXT_U16( pTmp );
sal_uInt16 nLastLeft = NEXT_U16( pTmp ) + nFirstLeft - 1;
pTmp = pSubTable + nOfsRight;
sal_uInt16 nFirstRight = NEXT_U16( pTmp );
sal_uInt16 nLastRight = NEXT_U16( pTmp ) + nFirstRight - 1;
sal_uLong nPairs = (sal_uLong)(nLastLeft - nFirstLeft + 1) * (nLastRight - nFirstRight + 1);
aKernGlyphVector.reserve( aKernGlyphVector.size() + nPairs );
pTmp = pSubTable + nOfsArray;
for( int nLeft = nFirstLeft; nLeft < nLastLeft; ++nLeft )
{
aKernPair.mnChar1 = nLeft;
for( int nRight = 0; nRight < nLastRight; ++nRight )
{
if( NEXT_S16( pTmp ) != 0 )
{
aKernPair.mnChar2 = nRight;
aKernGlyphVector.push_back( aKernPair );
}
}
}
}
break;
default:
fprintf( stderr, "gcach_ftyp.cxx: Found unsupported Apple-style kern subtable type %d.\n", nSubTableFormat );
break;
}
}
}
// now create VCL's ImplKernPairData[] format for all glyph pairs
sal_uLong nKernCount = aKernGlyphVector.size();
if( nKernCount )
{
// prepare glyphindex to character mapping
// TODO: this is needed to support VCL's existing kerning infrastructure,
// eliminate it up by redesigning kerning infrastructure to work with glyph indizes
typedef std::hash_multimap<sal_uInt16,sal_Unicode> Cmap;
Cmap aCmap;
for( sal_Unicode aChar = 0x0020; aChar < 0xFFFE; ++aChar )
{
sal_uInt16 nGlyphIndex = GetGlyphIndex( aChar );
if( nGlyphIndex )
aCmap.insert( Cmap::value_type( nGlyphIndex, aChar ) );
}
// translate both glyph indizes in kerning pairs to characters
// problem is that these are 1:n mappings...
KernVector aKernCharVector;
aKernCharVector.reserve( nKernCount );
KernVector::iterator it;
for( it = aKernGlyphVector.begin(); it != aKernGlyphVector.end(); ++it )
{
FT_Vector aKernVal;
FT_Error rcFT = FT_Get_Kerning( maFaceFT, it->mnChar1, it->mnChar2,
FT_KERNING_DEFAULT, &aKernVal );
aKernPair.mnKern = aKernVal.x >> 6;
if( (aKernPair.mnKern == 0) || (rcFT != FT_Err_Ok) )
continue;
typedef std::pair<Cmap::iterator,Cmap::iterator> CPair;
const CPair p1 = aCmap.equal_range( it->mnChar1 );
const CPair p2 = aCmap.equal_range( it->mnChar2 );
for( Cmap::const_iterator i1 = p1.first; i1 != p1.second; ++i1 )
{
aKernPair.mnChar1 = (*i1).second;
for( Cmap::const_iterator i2 = p2.first; i2 != p2.second; ++i2 )
{
aKernPair.mnChar2 = (*i2).second;
aKernCharVector.push_back( aKernPair );
}
}
}
// now move the resulting vector into VCL's ImplKernPairData[] format
nKernCount = aKernCharVector.size();
ImplKernPairData* pTo = new ImplKernPairData[ nKernCount ];
*ppKernPairs = pTo;
for( it = aKernCharVector.begin(); it != aKernCharVector.end(); ++it, ++pTo )
{
pTo->mnChar1 = it->mnChar1;
pTo->mnChar2 = it->mnChar2;
pTo->mnKern = it->mnKern;
}
}
return nKernCount;
}
// -----------------------------------------------------------------------
// outline stuff
// -----------------------------------------------------------------------
class PolyArgs
{
public:
PolyArgs( PolyPolygon& rPolyPoly, sal_uInt16 nMaxPoints );
~PolyArgs();
void AddPoint( long nX, long nY, PolyFlags);
void ClosePolygon();
long GetPosX() const { return maPosition.x;}
long GetPosY() const { return maPosition.y;}
private:
PolyPolygon& mrPolyPoly;
Point* mpPointAry;
sal_uInt8* mpFlagAry;
FT_Vector maPosition;
sal_uInt16 mnMaxPoints;
sal_uInt16 mnPoints;
sal_uInt16 mnPoly;
long mnHeight;
bool bHasOffline;
};
// -----------------------------------------------------------------------
PolyArgs::PolyArgs( PolyPolygon& rPolyPoly, sal_uInt16 nMaxPoints )
: mrPolyPoly(rPolyPoly),
mnMaxPoints(nMaxPoints),
mnPoints(0),
mnPoly(0),
bHasOffline(false)
{
mpPointAry = new Point[ mnMaxPoints ];
mpFlagAry = new sal_uInt8 [ mnMaxPoints ];
}
// -----------------------------------------------------------------------
PolyArgs::~PolyArgs()
{
delete[] mpFlagAry;
delete[] mpPointAry;
}
// -----------------------------------------------------------------------
void PolyArgs::AddPoint( long nX, long nY, PolyFlags aFlag )
{
DBG_ASSERT( (mnPoints < mnMaxPoints), "FTGlyphOutline: AddPoint overflow!" );
if( mnPoints >= mnMaxPoints )
return;
maPosition.x = nX;
maPosition.y = nY;
mpPointAry[ mnPoints ] = Point( nX, nY );
mpFlagAry[ mnPoints++ ]= aFlag;
bHasOffline |= (aFlag != POLY_NORMAL);
}
// -----------------------------------------------------------------------
void PolyArgs::ClosePolygon()
{
if( !mnPoly++ )
return;
// freetype seems to always close the polygon with an ON_CURVE point
// PolyPoly wants to close the polygon itself => remove last point
DBG_ASSERT( (mnPoints >= 2), "FTGlyphOutline: PolyFinishNum failed!" );
--mnPoints;
DBG_ASSERT( (mpPointAry[0]==mpPointAry[mnPoints]), "FTGlyphOutline: PolyFinishEq failed!" );
DBG_ASSERT( (mpFlagAry[0]==POLY_NORMAL), "FTGlyphOutline: PolyFinishFE failed!" );
DBG_ASSERT( (mpFlagAry[mnPoints]==POLY_NORMAL), "FTGlyphOutline: PolyFinishFS failed!" );
Polygon aPoly( mnPoints, mpPointAry, (bHasOffline ? mpFlagAry : NULL) );
// #i35928#
// This may be a invalid polygons, e.g. the last point is a control point.
// So close the polygon (and add the first point again) if the last point
// is a control point or different from first.
// #i48298#
// Now really duplicating the first point, to close or correct the
// polygon. Also no longer duplicating the flags, but enforcing
// POLY_NORMAL for the newly added last point.
const sal_uInt16 nPolySize(aPoly.GetSize());
if(nPolySize)
{
if((aPoly.HasFlags() && POLY_CONTROL == aPoly.GetFlags(nPolySize - 1))
|| (aPoly.GetPoint(nPolySize - 1) != aPoly.GetPoint(0)))
{
aPoly.SetSize(nPolySize + 1);
aPoly.SetPoint(aPoly.GetPoint(0), nPolySize);
if(aPoly.HasFlags())
{
aPoly.SetFlags(nPolySize, POLY_NORMAL);
}
}
}
mrPolyPoly.Insert( aPoly );
mnPoints = 0;
bHasOffline = false;
}
// -----------------------------------------------------------------------
extern "C" {
// TODO: wait till all compilers accept that calling conventions
// for functions are the same independent of implementation constness,
// then uncomment the const-tokens in the function interfaces below
static int FT_move_to( FT_Vector_CPtr p0, void* vpPolyArgs )
{
PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
// move_to implies a new polygon => finish old polygon first
rA.ClosePolygon();
rA.AddPoint( p0->x, p0->y, POLY_NORMAL );
return 0;
}
static int FT_line_to( FT_Vector_CPtr p1, void* vpPolyArgs )
{
PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
rA.AddPoint( p1->x, p1->y, POLY_NORMAL );
return 0;
}
static int FT_conic_to( FT_Vector_CPtr p1, FT_Vector_CPtr p2, void* vpPolyArgs )
{
PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
// VCL's Polygon only knows cubic beziers
const long nX1 = (2 * rA.GetPosX() + 4 * p1->x + 3) / 6;
const long nY1 = (2 * rA.GetPosY() + 4 * p1->y + 3) / 6;
rA.AddPoint( nX1, nY1, POLY_CONTROL );
const long nX2 = (2 * p2->x + 4 * p1->x + 3) / 6;
const long nY2 = (2 * p2->y + 4 * p1->y + 3) / 6;
rA.AddPoint( nX2, nY2, POLY_CONTROL );
rA.AddPoint( p2->x, p2->y, POLY_NORMAL );
return 0;
}
static int FT_cubic_to( FT_Vector_CPtr p1, FT_Vector_CPtr p2, FT_Vector_CPtr p3, void* vpPolyArgs )
{
PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
rA.AddPoint( p1->x, p1->y, POLY_CONTROL );
rA.AddPoint( p2->x, p2->y, POLY_CONTROL );
rA.AddPoint( p3->x, p3->y, POLY_NORMAL );
return 0;
}
} // extern "C"
// -----------------------------------------------------------------------
bool FreetypeServerFont::GetGlyphOutline( sal_GlyphId aGlyphId,
::basegfx::B2DPolyPolygon& rB2DPolyPoly ) const
{
if( maSizeFT )
pFTActivateSize( maSizeFT );
rB2DPolyPoly.clear();
int nGlyphFlags;
SplitGlyphFlags( *this, aGlyphId, nGlyphFlags );
FT_Int nLoadFlags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_TRANSFORM;
#ifdef FT_LOAD_TARGET_LIGHT
// enable "light hinting" if available
if( nFTVERSION >= 2103 )
nLoadFlags |= FT_LOAD_TARGET_LIGHT;
#endif
FT_Error rc = FT_Load_Glyph( maFaceFT, aGlyphId, nLoadFlags );
if( rc != FT_Err_Ok )
return false;
if( mbArtBold && pFTEmbolden )
(*pFTEmbolden)( maFaceFT->glyph );
FT_Glyph pGlyphFT;
rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
if( rc != FT_Err_Ok )
return false;
if( pGlyphFT->format != FT_GLYPH_FORMAT_OUTLINE )
return false;
if( mbArtItalic )
{
FT_Matrix aMatrix;
aMatrix.xx = aMatrix.yy = 0x10000L;
if( nFTVERSION >= 2102 ) // Freetype 2.1.2 API swapped xy with yx
aMatrix.xy = 0x6000L, aMatrix.yx = 0;
else
aMatrix.yx = 0x6000L, aMatrix.xy = 0;
FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
}
FT_Outline& rOutline = reinterpret_cast<FT_OutlineGlyphRec*>(pGlyphFT)->outline;
if( !rOutline.n_points ) // blank glyphs are ok
return true;
long nMaxPoints = 1 + rOutline.n_points * 3;
PolyPolygon aToolPolyPolygon;
PolyArgs aPolyArg( aToolPolyPolygon, nMaxPoints );
/*int nAngle =*/ ApplyGlyphTransform( nGlyphFlags, pGlyphFT, false );
FT_Outline_Funcs aFuncs;
aFuncs.move_to = &FT_move_to;
aFuncs.line_to = &FT_line_to;
aFuncs.conic_to = &FT_conic_to;
aFuncs.cubic_to = &FT_cubic_to;
aFuncs.shift = 0;
aFuncs.delta = 0;
rc = FT_Outline_Decompose( &rOutline, &aFuncs, (void*)&aPolyArg );
aPolyArg.ClosePolygon(); // close last polygon
FT_Done_Glyph( pGlyphFT );
// convert to basegfx polypolygon
// TODO: get rid of the intermediate tools polypolygon
rB2DPolyPoly = aToolPolyPolygon.getB2DPolyPolygon();
rB2DPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix( +1.0/(1<<6), -1.0/(1<<6) ));
return true;
}
// -----------------------------------------------------------------------
bool FreetypeServerFont::ApplyGSUB( const ImplFontSelectData& rFSD )
{
#define MKTAG(s) ((((((s[0]<<8)+s[1])<<8)+s[2])<<8)+s[3])
typedef std::vector<sal_uLong> ReqFeatureTagList;
ReqFeatureTagList aReqFeatureTagList;
if( rFSD.mbVertical )
aReqFeatureTagList.push_back( MKTAG("vert") );
sal_uLong nRequestedScript = 0; //MKTAG("hani");//### TODO: where to get script?
sal_uLong nRequestedLangsys = 0; //MKTAG("ZHT"); //### TODO: where to get langsys?
// TODO: request more features depending on script and language system
if( aReqFeatureTagList.size() == 0) // nothing to do
return true;
// load GSUB table into memory
sal_uLong nLength = 0;
const FT_Byte* const pGsubBase = mpFontInfo->GetTable( "GSUB", &nLength );
if( !pGsubBase )
return false;
// parse GSUB header
const FT_Byte* pGsubHeader = pGsubBase;
const sal_uInt16 nOfsScriptList = GetUShort( pGsubHeader+4 );
const sal_uInt16 nOfsFeatureTable = GetUShort( pGsubHeader+6 );
const sal_uInt16 nOfsLookupList = GetUShort( pGsubHeader+8 );
pGsubHeader += 10;
typedef std::vector<sal_uInt16> UshortList;
UshortList aFeatureIndexList;
UshortList aFeatureOffsetList;
// parse Script Table
const FT_Byte* pScriptHeader = pGsubBase + nOfsScriptList;
const sal_uInt16 nCntScript = GetUShort( pScriptHeader+0 );
pScriptHeader += 2;
for( sal_uInt16 nScriptIndex = 0; nScriptIndex < nCntScript; ++nScriptIndex )
{
const sal_uLong nScriptTag = GetUInt( pScriptHeader+0 ); // e.g. hani/arab/kana/hang
const sal_uInt16 nOfsScriptTable= GetUShort( pScriptHeader+4 );
pScriptHeader += 6; //###
if( (nScriptTag != nRequestedScript) && (nRequestedScript != 0) )
continue;
const FT_Byte* pScriptTable = pGsubBase + nOfsScriptList + nOfsScriptTable;
const sal_uInt16 nDefaultLangsysOfs = GetUShort( pScriptTable+0 );
const sal_uInt16 nCntLangSystem = GetUShort( pScriptTable+2 );
pScriptTable += 4;
sal_uInt16 nLangsysOffset = 0;
for( sal_uInt16 nLangsysIndex = 0; nLangsysIndex < nCntLangSystem; ++nLangsysIndex )
{
const sal_uLong nTag = GetUInt( pScriptTable+0 ); // e.g. KOR/ZHS/ZHT/JAN
const sal_uInt16 nOffset= GetUShort( pScriptTable+4 );
pScriptTable += 6;
if( (nTag != nRequestedLangsys) && (nRequestedLangsys != 0) )
continue;
nLangsysOffset = nOffset;
break;
}
if( (nDefaultLangsysOfs != 0) && (nDefaultLangsysOfs != nLangsysOffset) )
{
const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nDefaultLangsysOfs;
const sal_uInt16 nReqFeatureIdx = GetUShort( pLangSys+2 );
const sal_uInt16 nCntFeature = GetUShort( pLangSys+4 );
pLangSys += 6;
aFeatureIndexList.push_back( nReqFeatureIdx );
for( sal_uInt16 i = 0; i < nCntFeature; ++i )
{
const sal_uInt16 nFeatureIndex = GetUShort( pLangSys );
pLangSys += 2;
aFeatureIndexList.push_back( nFeatureIndex );
}
}
if( nLangsysOffset != 0 )
{
const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nLangsysOffset;
const sal_uInt16 nReqFeatureIdx = GetUShort( pLangSys+2 );
const sal_uInt16 nCntFeature = GetUShort( pLangSys+4 );
pLangSys += 6;
aFeatureIndexList.push_back( nReqFeatureIdx );
for( sal_uInt16 i = 0; i < nCntFeature; ++i )
{
const sal_uInt16 nFeatureIndex = GetUShort( pLangSys );
pLangSys += 2;
aFeatureIndexList.push_back( nFeatureIndex );
}
}
}
if( !aFeatureIndexList.size() )
return true;
UshortList aLookupIndexList;
UshortList aLookupOffsetList;
// parse Feature Table
const FT_Byte* pFeatureHeader = pGsubBase + nOfsFeatureTable;
const sal_uInt16 nCntFeature = GetUShort( pFeatureHeader );
pFeatureHeader += 2;
for( sal_uInt16 nFeatureIndex = 0; nFeatureIndex < nCntFeature; ++nFeatureIndex )
{
const sal_uLong nTag = GetUInt( pFeatureHeader+0 ); // e.g. locl/vert/trad/smpl/liga/fina/...
const sal_uInt16 nOffset= GetUShort( pFeatureHeader+4 );
pFeatureHeader += 6;
// short circuit some feature lookups
if( aFeatureIndexList[0] != nFeatureIndex ) // required feature?
{
const int nRequested = std::count( aFeatureIndexList.begin(), aFeatureIndexList.end(), nFeatureIndex);
if( !nRequested ) // ignore features that are not requested
continue;
const int nAvailable = std::count( aReqFeatureTagList.begin(), aReqFeatureTagList.end(), nTag);
if( !nAvailable ) // some fonts don't provide features they request!
continue;
}
const FT_Byte* pFeatureTable = pGsubBase + nOfsFeatureTable + nOffset;
const sal_uInt16 nCntLookups = GetUShort( pFeatureTable+0 );
pFeatureTable += 2;
for( sal_uInt16 i = 0; i < nCntLookups; ++i )
{
const sal_uInt16 nLookupIndex = GetUShort( pFeatureTable );
pFeatureTable += 2;
aLookupIndexList.push_back( nLookupIndex );
}
if( nCntLookups == 0 ) //### hack needed by Mincho/Gothic/Mingliu/Simsun/...
aLookupIndexList.push_back( 0 );
}
// parse Lookup List
const FT_Byte* pLookupHeader = pGsubBase + nOfsLookupList;
const sal_uInt16 nCntLookupTable = GetUShort( pLookupHeader );
pLookupHeader += 2;
for( sal_uInt16 nLookupIdx = 0; nLookupIdx < nCntLookupTable; ++nLookupIdx )
{
const sal_uInt16 nOffset = GetUShort( pLookupHeader );
pLookupHeader += 2;
if( std::count( aLookupIndexList.begin(), aLookupIndexList.end(), nLookupIdx ) )
aLookupOffsetList.push_back( nOffset );
}
UshortList::const_iterator lookup_it = aLookupOffsetList.begin();
for(; lookup_it != aLookupOffsetList.end(); ++lookup_it )
{
const sal_uInt16 nOfsLookupTable = *lookup_it;
const FT_Byte* pLookupTable = pGsubBase + nOfsLookupList + nOfsLookupTable;
const sal_uInt16 eLookupType = GetUShort( pLookupTable+0 );
const sal_uInt16 nCntLookupSubtable = GetUShort( pLookupTable+4 );
pLookupTable += 6;
// TODO: switch( eLookupType )
if( eLookupType != 1 ) // TODO: once we go beyond SingleSubst
continue;
for( sal_uInt16 nSubTableIdx = 0; nSubTableIdx < nCntLookupSubtable; ++nSubTableIdx )
{
const sal_uInt16 nOfsSubLookupTable = GetUShort( pLookupTable );
pLookupTable += 2;
const FT_Byte* pSubLookup = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable;
const sal_uInt16 nFmtSubstitution = GetUShort( pSubLookup+0 );
const sal_uInt16 nOfsCoverage = GetUShort( pSubLookup+2 );
pSubLookup += 4;
typedef std::pair<sal_uInt16,sal_uInt16> GlyphSubst;
typedef std::vector<GlyphSubst> SubstVector;
SubstVector aSubstVector;
const FT_Byte* pCoverage = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable + nOfsCoverage;
const sal_uInt16 nFmtCoverage = GetUShort( pCoverage+0 );
pCoverage += 2;
switch( nFmtCoverage )
{
case 1: // Coverage Format 1
{
const sal_uInt16 nCntGlyph = GetUShort( pCoverage );
pCoverage += 2;
aSubstVector.reserve( nCntGlyph );
for( sal_uInt16 i = 0; i < nCntGlyph; ++i )
{
const sal_uInt16 nGlyphId = GetUShort( pCoverage );
pCoverage += 2;
aSubstVector.push_back( GlyphSubst( nGlyphId, 0 ) );
}
}
break;
case 2: // Coverage Format 2
{
const sal_uInt16 nCntRange = GetUShort( pCoverage );
pCoverage += 2;
for( int i = nCntRange; --i >= 0; )
{
const sal_uInt32 nGlyph0 = GetUShort( pCoverage+0 );
const sal_uInt32 nGlyph1 = GetUShort( pCoverage+2 );
const sal_uInt16 nCovIdx = GetUShort( pCoverage+4 );
pCoverage += 6;
for( sal_uInt32 j = nGlyph0; j <= nGlyph1; ++j )
aSubstVector.push_back( GlyphSubst( static_cast<sal_uInt16>(j + nCovIdx), 0 ) );
}
}
break;
}
SubstVector::iterator it( aSubstVector.begin() );
switch( nFmtSubstitution )
{
case 1: // Single Substitution Format 1
{
const sal_uInt16 nDeltaGlyphId = GetUShort( pSubLookup );
pSubLookup += 2;
for(; it != aSubstVector.end(); ++it )
(*it).second = (*it).first + nDeltaGlyphId;
}
break;
case 2: // Single Substitution Format 2
{
const sal_uInt16 nCntGlyph = GetUShort( pSubLookup );
pSubLookup += 2;
for( int i = nCntGlyph; (it != aSubstVector.end()) && (--i>=0); ++it )
{
const sal_uInt16 nGlyphId = GetUShort( pSubLookup );
pSubLookup += 2;
(*it).second = nGlyphId;
}
}
break;
}
DBG_ASSERT( (it == aSubstVector.end()), "lookup<->coverage table mismatch" );
// now apply the glyph substitutions that have been collected in this subtable
for( it = aSubstVector.begin(); it != aSubstVector.end(); ++it )
maGlyphSubstitution[ (*it).first ] = (*it).second;
}
}
return true;
}
// =======================================================================