summaryrefslogtreecommitdiff
path: root/vcl/source/glyphs
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/glyphs')
-rw-r--r--vcl/source/glyphs/gcach_ftyp.cxx2547
-rw-r--r--vcl/source/glyphs/gcach_ftyp.hxx258
-rw-r--r--vcl/source/glyphs/gcach_layout.cxx635
-rw-r--r--vcl/source/glyphs/gcach_rbmp.cxx277
-rw-r--r--vcl/source/glyphs/gcach_vdev.cxx290
-rw-r--r--vcl/source/glyphs/gcach_vdev.hxx60
-rw-r--r--vcl/source/glyphs/glyphcache.cxx592
-rw-r--r--vcl/source/glyphs/graphite_adaptors.cxx339
-rw-r--r--vcl/source/glyphs/graphite_cache.cxx203
-rw-r--r--vcl/source/glyphs/graphite_features.cxx288
-rw-r--r--vcl/source/glyphs/graphite_layout.cxx1562
-rw-r--r--vcl/source/glyphs/graphite_serverfont.cxx88
-rw-r--r--vcl/source/glyphs/graphite_textsrc.cxx172
-rw-r--r--vcl/source/glyphs/graphite_textsrc.hxx126
-rw-r--r--vcl/source/glyphs/makefile.mk80
15 files changed, 7517 insertions, 0 deletions
diff --git a/vcl/source/glyphs/gcach_ftyp.cxx b/vcl/source/glyphs/gcach_ftyp.cxx
new file mode 100644
index 000000000000..2860eea20d96
--- /dev/null
+++ b/vcl/source/glyphs/gcach_ftyp.cxx
@@ -0,0 +1,2547 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 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 "vcl/outfont.hxx"
+#include "vcl/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;
+
+struct EqStr{ bool operator()(const char* a, const char* b) const { return !strcmp(a,b); } };
+typedef ::std::hash_map<const char*,FtFontFile*,::std::hash<const char*>, EqStr> 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 ),
+ 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()
+{
+ 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, 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] )
+ {
+ 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()
+{
+// This crashes on Solaris 10
+// TODO: check which versions have this problem
+//
+// FT_Error rcFT = FT_Done_FreeType( aLibFT );
+}
+
+// -----------------------------------------------------------------------
+
+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
+ 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, int& nGlyphIndex, int& nGlyphFlags )
+{
+ nGlyphFlags = nGlyphIndex & GF_FLAGMASK;
+ nGlyphIndex &= GF_IDXMASK;
+
+ if( nGlyphIndex & GF_ISCHAR )
+ nGlyphIndex = rFont.GetRawGlyphIndex( nGlyphIndex );
+}
+
+// -----------------------------------------------------------------------
+
+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;
+}
+
+// -----------------------------------------------------------------------
+
+int 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 );
+ }
+ mpFontInfo->CacheGlyphIndex( aChar, nGlyphIndex );
+ }
+
+ return nGlyphIndex;
+}
+
+// -----------------------------------------------------------------------
+
+int FreetypeServerFont::FixupGlyphIndex( int nGlyphIndex, 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( nGlyphIndex );
+ if( it == maGlyphSubstitution.end() )
+ {
+ int nTemp = GetVerticalChar( aChar );
+ if( nTemp ) // is substitution possible
+ nTemp = GetRawGlyphIndex( nTemp );
+ if( nTemp ) // substitute manually if sensible
+ nGlyphIndex = nTemp | (GF_GSUB | GF_ROTL);
+ else
+ nGlyphFlags |= GetVerticalFlags( aChar );
+ }
+ else
+ {
+ // for vertical GSUB also compensate for nOrientation=2700
+ nGlyphIndex = (*it).second;
+ nGlyphFlags |= GF_GSUB | GF_ROTL;
+ }
+ }
+
+ if( nGlyphIndex != 0 )
+ nGlyphIndex |= nGlyphFlags;
+
+ return nGlyphIndex;
+}
+
+
+// -----------------------------------------------------------------------
+
+int FreetypeServerFont::GetGlyphIndex( sal_UCS4 aChar ) const
+{
+ int nGlyphIndex = GetRawGlyphIndex( aChar );
+ nGlyphIndex = FixupGlyphIndex( nGlyphIndex, aChar );
+ return nGlyphIndex;
+}
+
+// -----------------------------------------------------------------------
+
+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( int nGlyphIndex, GlyphData& rGD ) const
+{
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ int nGlyphFlags;
+ SplitGlyphFlags( *this, nGlyphIndex, 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, nGlyphIndex, 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, nGlyphIndex, 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( int nGlyphIndex, RawBitmap& rRawBitmap ) const
+{
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ int nGlyphFlags;
+ SplitGlyphFlags( *this, nGlyphIndex, 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, nGlyphIndex, 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, nGlyphIndex, 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, 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 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( ULONG y=0; y < rRawBitmap.mnHeight; y++ )
+ {
+ unsigned char nLastByte = 0;
+ for( 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( int nGlyphIndex, RawBitmap& rRawBitmap ) const
+{
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ int nGlyphFlags;
+ SplitGlyphFlags( *this, nGlyphIndex, 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, nGlyphIndex, 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, nGlyphIndex, 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, 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 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( ULONG y=0; y < rRawBitmap.mnHeight; y++ )
+ {
+ unsigned char nLastByte = 0;
+ for( 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( ULONG y=0; y < rRawBitmap.mnHeight; y++ )
+ {
+ for( 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
+// -----------------------------------------------------------------------
+
+// TODO: replace with GetFontCharMap()
+bool FreetypeServerFont::GetFontCodeRanges( CmapResult& rResult ) const
+{
+ rResult.mbSymbolic = mpFontInfo->IsSymbolFont();
+
+ // TODO: is the full CmapResult needed on platforms calling this?
+ if( FT_IS_SFNT( maFaceFT ) )
+ {
+ ULONG nLength = 0;
+ const unsigned char* pCmap = mpFontInfo->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;
+}
+
+// -----------------------------------------------------------------------
+
+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
+ 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;
+ ULONG nVersion = GetUShort( pBuffer+0 );
+ USHORT nTableCnt = GetUShort( pBuffer+2 );
+
+ // Microsoft/Old TrueType style kern table
+ if ( nVersion == 0 )
+ {
+ pBuffer += 4;
+
+ for( USHORT nTableIdx = 0; nTableIdx < nTableCnt; ++nTableIdx )
+ {
+ // USHORT nSubVersion = GetUShort( pBuffer+0 );
+ // USHORT nSubLength = GetUShort( pBuffer+2 );
+ USHORT 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
+ {
+ USHORT 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;
+ //USHORT nRowWidth = GetUShort( pBuffer+0 );
+ USHORT nOfsLeft = GetUShort( pBuffer+2 );
+ USHORT nOfsRight = GetUShort( pBuffer+4 );
+ USHORT nOfsArray = GetUShort( pBuffer+6 );
+ pBuffer += 8;
+
+ const FT_Byte* pTmp = pSubTable + nOfsLeft;
+ USHORT nFirstLeft = GetUShort( pTmp+0 );
+ USHORT nLastLeft = GetUShort( pTmp+2 ) + nFirstLeft - 1;
+
+ pTmp = pSubTable + nOfsRight;
+ USHORT nFirstRight = GetUShort( pTmp+0 );
+ USHORT nLastRight = GetUShort( pTmp+2 ) + nFirstRight - 1;
+
+ ULONG nPairs = (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( USHORT nTableIdx = 0; nTableIdx < nTableCnt; ++nTableIdx )
+ {
+ /*ULONG nLength =*/ NEXT_U32( pBuffer );
+ USHORT nCoverage = NEXT_U16( pBuffer );
+ /*USHORT 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
+ {
+ USHORT 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;
+ /*USHORT nRowWidth =*/ NEXT_U16( pBuffer );
+ USHORT nOfsLeft = NEXT_U16( pBuffer );
+ USHORT nOfsRight = NEXT_U16( pBuffer );
+ USHORT nOfsArray = NEXT_U16( pBuffer );
+
+ const FT_Byte* pTmp = pSubTable + nOfsLeft;
+ USHORT nFirstLeft = NEXT_U16( pTmp );
+ USHORT nLastLeft = NEXT_U16( pTmp ) + nFirstLeft - 1;
+
+ pTmp = pSubTable + nOfsRight;
+ USHORT nFirstRight = NEXT_U16( pTmp );
+ USHORT nLastRight = NEXT_U16( pTmp ) + nFirstRight - 1;
+
+ ULONG nPairs = (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
+ 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<USHORT,sal_Unicode> Cmap;
+ Cmap aCmap;
+ for( sal_Unicode aChar = 0x0020; aChar < 0xFFFE; ++aChar )
+ {
+ USHORT 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, USHORT 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;
+ BYTE* mpFlagAry;
+
+ FT_Vector maPosition;
+ USHORT mnMaxPoints;
+ USHORT mnPoints;
+ USHORT mnPoly;
+ long mnHeight;
+ bool bHasOffline;
+};
+
+// -----------------------------------------------------------------------
+
+PolyArgs::PolyArgs( PolyPolygon& rPolyPoly, USHORT nMaxPoints )
+: mrPolyPoly(rPolyPoly),
+ mnMaxPoints(nMaxPoints),
+ mnPoints(0),
+ mnPoly(0),
+ mnHeight(0),
+ bHasOffline(false)
+{
+ mpPointAry = new Point[ mnMaxPoints ];
+ mpFlagAry = new BYTE [ 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( int nGlyphIndex,
+ ::basegfx::B2DPolyPolygon& rB2DPolyPoly ) const
+{
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ rB2DPolyPoly.clear();
+
+ int nGlyphFlags;
+ SplitGlyphFlags( *this, nGlyphIndex, 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, nGlyphIndex, 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<ULONG> ReqFeatureTagList;
+ ReqFeatureTagList aReqFeatureTagList;
+ if( rFSD.mbVertical )
+ aReqFeatureTagList.push_back( MKTAG("vert") );
+ ULONG nRequestedScript = 0; //MKTAG("hani");//### TODO: where to get script?
+ 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
+ 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 USHORT nOfsScriptList = GetUShort( pGsubHeader+4 );
+ const USHORT nOfsFeatureTable = GetUShort( pGsubHeader+6 );
+ const USHORT nOfsLookupList = GetUShort( pGsubHeader+8 );
+ pGsubHeader += 10;
+
+ typedef std::vector<USHORT> UshortList;
+ UshortList aFeatureIndexList;
+ UshortList aFeatureOffsetList;
+
+ // parse Script Table
+ const FT_Byte* pScriptHeader = pGsubBase + nOfsScriptList;
+ const USHORT nCntScript = GetUShort( pScriptHeader+0 );
+ pScriptHeader += 2;
+ for( USHORT nScriptIndex = 0; nScriptIndex < nCntScript; ++nScriptIndex )
+ {
+ const ULONG nScriptTag = GetUInt( pScriptHeader+0 ); // e.g. hani/arab/kana/hang
+ const USHORT nOfsScriptTable= GetUShort( pScriptHeader+4 );
+ pScriptHeader += 6; //###
+ if( (nScriptTag != nRequestedScript) && (nRequestedScript != 0) )
+ continue;
+
+ const FT_Byte* pScriptTable = pGsubBase + nOfsScriptList + nOfsScriptTable;
+ const USHORT nDefaultLangsysOfs = GetUShort( pScriptTable+0 );
+ const USHORT nCntLangSystem = GetUShort( pScriptTable+2 );
+ pScriptTable += 4;
+ USHORT nLangsysOffset = 0;
+
+ for( USHORT nLangsysIndex = 0; nLangsysIndex < nCntLangSystem; ++nLangsysIndex )
+ {
+ const ULONG nTag = GetUInt( pScriptTable+0 ); // e.g. KOR/ZHS/ZHT/JAN
+ const USHORT 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 USHORT nReqFeatureIdx = GetUShort( pLangSys+2 );
+ const USHORT nCntFeature = GetUShort( pLangSys+4 );
+ pLangSys += 6;
+ aFeatureIndexList.push_back( nReqFeatureIdx );
+ for( USHORT i = 0; i < nCntFeature; ++i )
+ {
+ const USHORT nFeatureIndex = GetUShort( pLangSys );
+ pLangSys += 2;
+ aFeatureIndexList.push_back( nFeatureIndex );
+ }
+ }
+
+ if( nLangsysOffset != 0 )
+ {
+ const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nLangsysOffset;
+ const USHORT nReqFeatureIdx = GetUShort( pLangSys+2 );
+ const USHORT nCntFeature = GetUShort( pLangSys+4 );
+ pLangSys += 6;
+ aFeatureIndexList.push_back( nReqFeatureIdx );
+ for( USHORT i = 0; i < nCntFeature; ++i )
+ {
+ const USHORT 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 USHORT nCntFeature = GetUShort( pFeatureHeader );
+ pFeatureHeader += 2;
+ for( USHORT nFeatureIndex = 0; nFeatureIndex < nCntFeature; ++nFeatureIndex )
+ {
+ const ULONG nTag = GetUInt( pFeatureHeader+0 ); // e.g. locl/vert/trad/smpl/liga/fina/...
+ const USHORT 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 USHORT nCntLookups = GetUShort( pFeatureTable+0 );
+ pFeatureTable += 2;
+ for( USHORT i = 0; i < nCntLookups; ++i )
+ {
+ const USHORT 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 USHORT nCntLookupTable = GetUShort( pLookupHeader );
+ pLookupHeader += 2;
+ for( USHORT nLookupIdx = 0; nLookupIdx < nCntLookupTable; ++nLookupIdx )
+ {
+ const USHORT 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 USHORT nOfsLookupTable = *lookup_it;
+ const FT_Byte* pLookupTable = pGsubBase + nOfsLookupList + nOfsLookupTable;
+ const USHORT eLookupType = GetUShort( pLookupTable+0 );
+ const USHORT nCntLookupSubtable = GetUShort( pLookupTable+4 );
+ pLookupTable += 6;
+
+ // TODO: switch( eLookupType )
+ if( eLookupType != 1 ) // TODO: once we go beyond SingleSubst
+ continue;
+
+ for( USHORT nSubTableIdx = 0; nSubTableIdx < nCntLookupSubtable; ++nSubTableIdx )
+ {
+ const USHORT nOfsSubLookupTable = GetUShort( pLookupTable );
+ pLookupTable += 2;
+ const FT_Byte* pSubLookup = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable;
+
+ const USHORT nFmtSubstitution = GetUShort( pSubLookup+0 );
+ const USHORT nOfsCoverage = GetUShort( pSubLookup+2 );
+ pSubLookup += 4;
+
+ typedef std::pair<USHORT,USHORT> GlyphSubst;
+ typedef std::vector<GlyphSubst> SubstVector;
+ SubstVector aSubstVector;
+
+ const FT_Byte* pCoverage = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable + nOfsCoverage;
+ const USHORT nFmtCoverage = GetUShort( pCoverage+0 );
+ pCoverage += 2;
+ switch( nFmtCoverage )
+ {
+ case 1: // Coverage Format 1
+ {
+ const USHORT nCntGlyph = GetUShort( pCoverage );
+ pCoverage += 2;
+ aSubstVector.reserve( nCntGlyph );
+ for( USHORT i = 0; i < nCntGlyph; ++i )
+ {
+ const USHORT nGlyphId = GetUShort( pCoverage );
+ pCoverage += 2;
+ aSubstVector.push_back( GlyphSubst( nGlyphId, 0 ) );
+ }
+ }
+ break;
+
+ case 2: // Coverage Format 2
+ {
+ const USHORT nCntRange = GetUShort( pCoverage );
+ pCoverage += 2;
+ for( int i = nCntRange; --i >= 0; )
+ {
+ const UINT32 nGlyph0 = GetUShort( pCoverage+0 );
+ const UINT32 nGlyph1 = GetUShort( pCoverage+2 );
+ const USHORT nCovIdx = GetUShort( pCoverage+4 );
+ pCoverage += 6;
+ for( UINT32 j = nGlyph0; j <= nGlyph1; ++j )
+ aSubstVector.push_back( GlyphSubst( static_cast<USHORT>(j + nCovIdx), 0 ) );
+ }
+ }
+ break;
+ }
+
+ SubstVector::iterator it( aSubstVector.begin() );
+
+ switch( nFmtSubstitution )
+ {
+ case 1: // Single Substitution Format 1
+ {
+ const USHORT nDeltaGlyphId = GetUShort( pSubLookup );
+ pSubLookup += 2;
+ for(; it != aSubstVector.end(); ++it )
+ (*it).second = (*it).first + nDeltaGlyphId;
+ }
+ break;
+
+ case 2: // Single Substitution Format 2
+ {
+ const USHORT nCntGlyph = GetUShort( pSubLookup );
+ pSubLookup += 2;
+ for( int i = nCntGlyph; (it != aSubstVector.end()) && (--i>=0); ++it )
+ {
+ const USHORT 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;
+}
+
+// =======================================================================
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/glyphs/gcach_ftyp.hxx b/vcl/source/glyphs/gcach_ftyp.hxx
new file mode 100644
index 000000000000..95f792fa9647
--- /dev/null
+++ b/vcl/source/glyphs/gcach_ftyp.hxx
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef _SV_GCACHFTYP_HXX
+#define _SV_GCACHFTYP_HXX
+
+#include <vcl/glyphcache.hxx>
+#include <rtl/textcvt.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+class FreetypeServerFont;
+struct FT_GlyphRec_;
+
+// -----------------------------------------------------------------------
+
+// FtFontFile has the responsibility that a font file is only mapped once.
+// (#86621#) the old directly ft-managed solution caused it to be mapped
+// in up to nTTC*nSizes*nOrientation*nSynthetic times
+class FtFontFile
+{
+public:
+ static FtFontFile* FindFontFile( const ::rtl::OString& rNativeFileName );
+
+ bool Map();
+ void Unmap();
+
+ const unsigned char* GetBuffer() const { return mpFileMap; }
+ int GetFileSize() const { return mnFileSize; }
+ const ::rtl::OString* GetFileName() const { return &maNativeFileName; }
+ int GetLangBoost() const { return mnLangBoost; }
+
+private:
+ FtFontFile( const ::rtl::OString& rNativeFileName );
+
+ const ::rtl::OString maNativeFileName;
+ const unsigned char* mpFileMap;
+ int mnFileSize;
+ int mnRefCount;
+ int mnLangBoost;
+};
+
+// -----------------------------------------------------------------------
+
+// FtFontInfo corresponds to an unscaled font face
+class FtFontInfo
+{
+public:
+ FtFontInfo( const ImplDevFontAttributes&,
+ const ::rtl::OString& rNativeFileName,
+ int nFaceNum, sal_IntPtr nFontId, int nSynthetic,
+ const ExtraKernInfo* );
+ ~FtFontInfo();
+
+ const unsigned char* GetTable( const char*, ULONG* pLength=0 ) const;
+
+ FT_FaceRec_* GetFaceFT();
+ void ReleaseFaceFT( FT_FaceRec_* );
+
+ const ::rtl::OString* GetFontFileName() const { return mpFontFile->GetFileName(); }
+ int GetFaceNum() const { return mnFaceNum; }
+ int GetSynthetic() const { return mnSynthetic; }
+ sal_IntPtr GetFontId() const { return mnFontId; }
+ bool IsSymbolFont() const { return maDevFontAttributes.IsSymbolFont(); }
+ const ImplFontAttributes& GetFontAttributes() const { return maDevFontAttributes; }
+
+ void AnnounceFont( ImplDevFontList* );
+
+ int GetGlyphIndex( sal_UCS4 cChar ) const;
+ void CacheGlyphIndex( sal_UCS4 cChar, int nGI ) const;
+
+ bool HasExtraKerning() const;
+ int GetExtraKernPairs( ImplKernPairData** ) const;
+ int GetExtraGlyphKernValue( int nLeftGlyph, int nRightGlyph ) const;
+
+private:
+ FT_FaceRec_* maFaceFT;
+ FtFontFile* mpFontFile;
+ const int mnFaceNum;
+ int mnRefCount;
+ const int mnSynthetic;
+
+ sal_IntPtr mnFontId;
+ ImplDevFontAttributes maDevFontAttributes;
+
+ // cache unicode->glyphid mapping because looking it up is expensive
+ // TODO: change to hash_multimap when a use case requires a m:n mapping
+ typedef ::std::hash_map<int,int> Int2IntMap;
+ mutable Int2IntMap* mpChar2Glyph;
+ mutable Int2IntMap* mpGlyph2Char;
+ void InitHashes() const;
+
+ const ExtraKernInfo* mpExtraKernInfo;
+};
+
+// these two inlines are very important for performance
+
+inline int FtFontInfo::GetGlyphIndex( sal_UCS4 cChar ) const
+{
+ if( !mpChar2Glyph )
+ return -1;
+ Int2IntMap::const_iterator it = mpChar2Glyph->find( cChar );
+ if( it == mpChar2Glyph->end() )
+ return -1;
+ return it->second;
+}
+
+inline void FtFontInfo::CacheGlyphIndex( sal_UCS4 cChar, int nIndex ) const
+{
+ if( !mpChar2Glyph )
+ InitHashes();
+ (*mpChar2Glyph)[ cChar ] = nIndex;
+ (*mpGlyph2Char)[ nIndex ] = cChar;
+}
+
+// -----------------------------------------------------------------------
+
+class FreetypeManager
+{
+public:
+ FreetypeManager();
+ ~FreetypeManager();
+
+ long AddFontDir( const String& rUrlName );
+ void AddFontFile( const rtl::OString& rNormalizedName,
+ int nFaceNum, sal_IntPtr nFontId, const ImplDevFontAttributes&,
+ const ExtraKernInfo* );
+ void AnnounceFonts( ImplDevFontList* ) const;
+ void ClearFontList();
+
+ FreetypeServerFont* CreateFont( const ImplFontSelectData& );
+
+private:
+ typedef ::std::hash_map<sal_IntPtr,FtFontInfo*> FontList;
+ FontList maFontList;
+
+ sal_IntPtr mnMaxFontId;
+ sal_IntPtr mnNextFontId;
+};
+
+// -----------------------------------------------------------------------
+
+class FreetypeServerFont : public ServerFont
+{
+public:
+ FreetypeServerFont( const ImplFontSelectData&, FtFontInfo* );
+ virtual ~FreetypeServerFont();
+
+ virtual const ::rtl::OString* GetFontFileName() const { return mpFontInfo->GetFontFileName(); }
+ virtual int GetFontFaceNum() const { return mpFontInfo->GetFaceNum(); }
+ virtual bool TestFont() const;
+ virtual void* GetFtFace() const;
+ virtual void SetFontOptions( const ImplFontOptions&);
+ virtual int GetLoadFlags() const { return (mnLoadFlags & ~FT_LOAD_IGNORE_TRANSFORM); }
+ virtual bool NeedsArtificialBold() const { return mbArtBold; }
+ virtual bool NeedsArtificialItalic() const { return mbArtItalic; }
+
+ virtual void FetchFontMetric( ImplFontMetricData&, long& rFactor ) const;
+
+ virtual int GetGlyphIndex( sal_UCS4 ) const;
+ int GetRawGlyphIndex( sal_UCS4 ) const;
+ int FixupGlyphIndex( int nGlyphIndex, sal_UCS4 ) const;
+
+ virtual bool GetAntialiasAdvice( void ) const;
+ virtual bool GetGlyphBitmap1( int nGlyphIndex, RawBitmap& ) const;
+ virtual bool GetGlyphBitmap8( int nGlyphIndex, RawBitmap& ) const;
+ virtual bool GetGlyphOutline( int nGlyphIndex, ::basegfx::B2DPolyPolygon& ) const;
+ virtual int GetGlyphKernValue( int nLeftGlyph, int nRightGlyph ) const;
+ virtual ULONG GetKernPairs( ImplKernPairData** ) const;
+
+ const unsigned char* GetTable( const char* pName, ULONG* pLength )
+ { return mpFontInfo->GetTable( pName, pLength ); }
+ int GetEmUnits() const;
+ const FT_Size_Metrics& GetMetricsFT() const { return maSizeFT->metrics; }
+
+protected:
+ friend class GlyphCache;
+
+ int ApplyGlyphTransform( int nGlyphFlags, FT_GlyphRec_*, bool ) const;
+ virtual void InitGlyphData( int nGlyphIndex, GlyphData& ) const;
+ virtual bool GetFontCodeRanges( CmapResult& ) const;
+ bool ApplyGSUB( const ImplFontSelectData& );
+ virtual ServerFontLayoutEngine* GetLayoutEngine();
+
+private:
+ int mnWidth;
+ int mnPrioEmbedded;
+ int mnPrioAntiAlias;
+ int mnPrioAutoHint;
+ FtFontInfo* mpFontInfo;
+ FT_Int mnLoadFlags;
+ double mfStretch;
+ FT_FaceRec_* maFaceFT;
+ FT_SizeRec_* maSizeFT;
+
+ bool mbFaceOk;
+ bool mbArtItalic;
+ bool mbArtBold;
+ bool mbUseGamma;
+
+ typedef ::std::hash_map<int,int> GlyphSubstitution;
+ GlyphSubstitution maGlyphSubstitution;
+ rtl_UnicodeToTextConverter maRecodeConverter;
+
+ ServerFontLayoutEngine* mpLayoutEngine;
+};
+
+// -----------------------------------------------------------------------
+
+class ImplFTSFontData : public ImplFontData
+{
+private:
+ FtFontInfo* mpFtFontInfo;
+ enum { IFTSFONT_MAGIC = 0x1F150A1C };
+
+public:
+ ImplFTSFontData( FtFontInfo*, const ImplDevFontAttributes& );
+
+ FtFontInfo* GetFtFontInfo() const { return mpFtFontInfo; }
+
+ virtual ImplFontEntry* CreateFontInstance( ImplFontSelectData& ) const;
+ virtual ImplFontData* Clone() const { return new ImplFTSFontData( *this ); }
+ virtual sal_IntPtr GetFontId() const { return mpFtFontInfo->GetFontId(); }
+
+ static bool CheckFontData( const ImplFontData& r ) { return r.CheckMagic( IFTSFONT_MAGIC ); }
+};
+
+// -----------------------------------------------------------------------
+
+#endif // _SV_GCACHFTYP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/glyphs/gcach_layout.cxx b/vcl/source/glyphs/gcach_layout.cxx
new file mode 100644
index 000000000000..bed698d9c1aa
--- /dev/null
+++ b/vcl/source/glyphs/gcach_layout.cxx
@@ -0,0 +1,635 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#define ENABLE_ICU_LAYOUT
+#include <gcach_ftyp.hxx>
+#include <vcl/sallayout.hxx>
+#include <vcl/salgdi.hxx>
+
+#include <vcl/svapp.hxx>
+
+#include <sal/alloca.h>
+
+#if OSL_DEBUG_LEVEL > 1
+#include <cstdio>
+#endif
+#include <rtl/instance.hxx>
+
+namespace { struct SimpleLayoutEngine : public rtl::Static< ServerFontLayoutEngine, SimpleLayoutEngine > {}; }
+
+// =======================================================================
+// layout implementation for ServerFont
+// =======================================================================
+
+ServerFontLayout::ServerFontLayout( ServerFont& rFont )
+: mrServerFont( rFont )
+{}
+
+void ServerFontLayout::DrawText( SalGraphics& rSalGraphics ) const
+{
+ rSalGraphics.DrawServerFontLayout( *this );
+}
+
+// -----------------------------------------------------------------------
+
+bool ServerFontLayout::LayoutText( ImplLayoutArgs& rArgs )
+{
+ ServerFontLayoutEngine* pLE = NULL;
+ if( !(rArgs.mnFlags & SAL_LAYOUT_COMPLEX_DISABLED) )
+ pLE = mrServerFont.GetLayoutEngine();
+ if( !pLE )
+ pLE = &SimpleLayoutEngine::get();
+
+ bool bRet = (*pLE)( *this, rArgs );
+ return bRet;
+}
+
+// -----------------------------------------------------------------------
+
+void ServerFontLayout::AdjustLayout( ImplLayoutArgs& rArgs )
+{
+ GenericSalLayout::AdjustLayout( rArgs );
+
+ // apply asian kerning if the glyphs are not already formatted
+ if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN)
+ && !(rArgs.mnFlags & SAL_LAYOUT_VERTICAL) )
+ if( (rArgs.mpDXArray != NULL) || (rArgs.mnLayoutWidth != 0) )
+ ApplyAsianKerning( rArgs.mpStr, rArgs.mnLength );
+
+ // insert kashidas where requested by the formatting array
+ if( (rArgs.mnFlags & SAL_LAYOUT_KASHIDA_JUSTIFICATON) && rArgs.mpDXArray )
+ {
+ int nKashidaIndex = mrServerFont.GetGlyphIndex( 0x0640 );
+ if( nKashidaIndex != 0 )
+ {
+ const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nKashidaIndex );
+ KashidaJustify( nKashidaIndex, rGM.GetCharWidth() );
+ // TODO: kashida-GSUB/GPOS
+ }
+ }
+}
+
+// =======================================================================
+
+bool ServerFontLayoutEngine::operator()( ServerFontLayout& rLayout, ImplLayoutArgs& rArgs )
+{
+ FreetypeServerFont& rFont = static_cast<FreetypeServerFont&>(rLayout.GetServerFont());
+
+ Point aNewPos( 0, 0 );
+ int nOldGlyphId = -1;
+ int nGlyphWidth = 0;
+ GlyphItem aPrevItem;
+ bool bRightToLeft;
+ for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRightToLeft ); )
+ {
+ sal_UCS4 cChar = rArgs.mpStr[ nCharPos ];
+ if( (cChar >= 0xD800) && (cChar <= 0xDFFF) )
+ {
+ if( cChar >= 0xDC00 ) // this part of a surrogate pair was already processed
+ continue;
+ cChar = 0x10000 + ((cChar - 0xD800) << 10)
+ + (rArgs.mpStr[ nCharPos+1 ] - 0xDC00);
+ }
+
+ if( bRightToLeft )
+ cChar = GetMirroredChar( cChar );
+ int nGlyphIndex = rFont.GetGlyphIndex( cChar );
+ // when glyph fallback is needed update LayoutArgs
+ if( !nGlyphIndex ) {
+ rArgs.NeedFallback( nCharPos, bRightToLeft );
+ if( cChar >= 0x10000 ) // handle surrogate pairs
+ rArgs.NeedFallback( nCharPos+1, bRightToLeft );
+ }
+
+ // apply pair kerning to prev glyph if requested
+ if( SAL_LAYOUT_KERNING_PAIRS & rArgs.mnFlags )
+ {
+ int nKernValue = rFont.GetGlyphKernValue( nOldGlyphId, nGlyphIndex );
+ nGlyphWidth += nKernValue;
+ aPrevItem.mnNewWidth = nGlyphWidth;
+ }
+
+ // finish previous glyph
+ if( nOldGlyphId >= 0 )
+ rLayout.AppendGlyph( aPrevItem );
+ aNewPos.X() += nGlyphWidth;
+
+ // prepare GlyphItem for appending it in next round
+ nOldGlyphId = nGlyphIndex;
+ const GlyphMetric& rGM = rFont.GetGlyphMetric( nGlyphIndex );
+ nGlyphWidth = rGM.GetCharWidth();
+ int nGlyphFlags = bRightToLeft ? GlyphItem::IS_RTL_GLYPH : 0;
+ aPrevItem = GlyphItem( nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth );
+ }
+
+ // append last glyph item if any
+ if( nOldGlyphId >= 0 )
+ rLayout.AppendGlyph( aPrevItem );
+
+ return true;
+}
+
+// =======================================================================
+// bridge to ICU LayoutEngine
+// =======================================================================
+
+#ifdef ENABLE_ICU_LAYOUT
+
+#define bool_t signed char
+
+// disable warnings in icu layout headers
+#if defined __SUNPRO_CC
+#pragma disable_warn
+#endif
+
+#include <layout/LayoutEngine.h>
+#include <layout/LEFontInstance.h>
+#include <layout/LEScripts.h>
+
+// enable warnings again
+#if defined __SUNPRO_CC
+#pragma enable_warn
+#endif
+
+#include <unicode/uscript.h>
+#include <unicode/ubidi.h>
+
+using namespace U_ICU_NAMESPACE;
+
+static const LEGlyphID ICU_DELETED_GLYPH = 0xFFFF;
+static const LEGlyphID ICU_MARKED_GLYPH = 0xFFFE;
+
+// -----------------------------------------------------------------------
+
+class IcuFontFromServerFont
+: public LEFontInstance
+{
+private:
+ FreetypeServerFont& mrServerFont;
+
+public:
+ IcuFontFromServerFont( FreetypeServerFont& rFont )
+ : mrServerFont( rFont )
+ {}
+
+ virtual const void* getFontTable(LETag tableTag) const;
+ virtual le_int32 getUnitsPerEM() const;
+ virtual float getXPixelsPerEm() const;
+ virtual float getYPixelsPerEm() const;
+ virtual float getScaleFactorX() const;
+ virtual float getScaleFactorY() const;
+
+ using LEFontInstance::mapCharToGlyph;
+ virtual LEGlyphID mapCharToGlyph( LEUnicode32 ch ) const;
+
+ virtual le_int32 getAscent() const;
+ virtual le_int32 getDescent() const;
+ virtual le_int32 getLeading() const;
+
+ virtual void getGlyphAdvance( LEGlyphID glyph, LEPoint &advance ) const;
+ virtual le_bool getGlyphPoint( LEGlyphID glyph, le_int32 pointNumber, LEPoint& point ) const;
+};
+
+// -----------------------------------------------------------------------
+
+const void* IcuFontFromServerFont::getFontTable( LETag nICUTableTag ) const
+{
+ char pTagName[5];
+ pTagName[0] = (char)(nICUTableTag >> 24);
+ pTagName[1] = (char)(nICUTableTag >> 16);
+ pTagName[2] = (char)(nICUTableTag >> 8);
+ pTagName[3] = (char)(nICUTableTag);
+ pTagName[4] = 0;
+
+ ULONG nLength;
+ const unsigned char* pBuffer = mrServerFont.GetTable( pTagName, &nLength );
+#ifdef VERBOSE_DEBUG
+ fprintf(stderr,"IcuGetTable(\"%s\") => %p\n", pTagName, pBuffer);
+ int mnHeight = mrServerFont.GetFontSelData().mnHeight;
+ const char* pName = mrServerFont.GetFontFileName()->getStr();
+ fprintf(stderr,"font( h=%d, \"%s\" )\n", mnHeight, pName );
+#endif
+ return (const void*)pBuffer;
+}
+
+// -----------------------------------------------------------------------
+
+le_int32 IcuFontFromServerFont::getUnitsPerEM() const
+{
+ return mrServerFont.GetEmUnits();
+}
+
+// -----------------------------------------------------------------------
+
+float IcuFontFromServerFont::getXPixelsPerEm() const
+{
+ const ImplFontSelectData& r = mrServerFont.GetFontSelData();
+ float fX = r.mnWidth ? r.mnWidth : r.mnHeight;
+ return fX;
+}
+
+// -----------------------------------------------------------------------
+
+float IcuFontFromServerFont::getYPixelsPerEm() const
+{
+ float fY = mrServerFont.GetFontSelData().mnHeight;
+ return fY;
+}
+
+// -----------------------------------------------------------------------
+
+float IcuFontFromServerFont::getScaleFactorX() const
+{
+ return 1.0;
+}
+
+// -----------------------------------------------------------------------
+
+float IcuFontFromServerFont::getScaleFactorY() const
+{
+ return 1.0;
+}
+
+// -----------------------------------------------------------------------
+
+LEGlyphID IcuFontFromServerFont::mapCharToGlyph( LEUnicode32 ch ) const
+{
+ LEGlyphID nGlyphIndex = mrServerFont.GetRawGlyphIndex( ch );
+ return nGlyphIndex;
+}
+
+// -----------------------------------------------------------------------
+
+le_int32 IcuFontFromServerFont::getAscent() const
+{
+ const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
+ le_int32 nAscent = (+rMetrics.ascender + 32) >> 6;
+ return nAscent;
+}
+
+// -----------------------------------------------------------------------
+
+le_int32 IcuFontFromServerFont::getDescent() const
+{
+ const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
+ le_int32 nDescent = (-rMetrics.descender + 32) >> 6;
+ return nDescent;
+}
+
+// -----------------------------------------------------------------------
+
+le_int32 IcuFontFromServerFont::getLeading() const
+{
+ const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
+ le_int32 nLeading = ((rMetrics.height - rMetrics.ascender + rMetrics.descender) + 32) >> 6;
+ return nLeading;
+}
+
+// -----------------------------------------------------------------------
+
+void IcuFontFromServerFont::getGlyphAdvance( LEGlyphID nGlyphIndex,
+ LEPoint &advance ) const
+{
+ if( (nGlyphIndex == ICU_MARKED_GLYPH)
+ || (nGlyphIndex == ICU_DELETED_GLYPH) )
+ {
+ // deleted glyph or mark glyph has not advance
+ advance.fX = 0;
+ }
+ else
+ {
+ const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nGlyphIndex );
+ advance.fX = rGM.GetCharWidth();
+ }
+
+ advance.fY = 0;
+}
+
+// -----------------------------------------------------------------------
+
+le_bool IcuFontFromServerFont::getGlyphPoint( LEGlyphID,
+ le_int32
+#if OSL_DEBUG_LEVEL > 1
+pointNumber
+#endif
+ ,
+ LEPoint& ) const
+{
+ //TODO: replace dummy implementation
+#if OSL_DEBUG_LEVEL > 1
+ fprintf(stderr,"getGlyphPoint(%d)\n", pointNumber );
+#endif
+ return false;
+}
+
+// =======================================================================
+
+class IcuLayoutEngine : public ServerFontLayoutEngine
+{
+private:
+ IcuFontFromServerFont maIcuFont;
+
+ le_int32 meScriptCode;
+ LayoutEngine* mpIcuLE;
+
+public:
+ IcuLayoutEngine( FreetypeServerFont& );
+ virtual ~IcuLayoutEngine();
+
+ virtual bool operator()( ServerFontLayout&, ImplLayoutArgs& );
+};
+
+// -----------------------------------------------------------------------
+
+IcuLayoutEngine::IcuLayoutEngine( FreetypeServerFont& rServerFont )
+: maIcuFont( rServerFont ),
+ meScriptCode( USCRIPT_INVALID_CODE ),
+ mpIcuLE( NULL )
+{}
+
+// -----------------------------------------------------------------------
+
+IcuLayoutEngine::~IcuLayoutEngine()
+{
+ if( mpIcuLE )
+ delete mpIcuLE;
+}
+
+// -----------------------------------------------------------------------
+
+static bool lcl_CharIsJoiner(sal_Unicode cChar)
+{
+ return ((cChar == 0x200C) || (cChar == 0x200D));
+}
+
+bool IcuLayoutEngine::operator()( ServerFontLayout& rLayout, ImplLayoutArgs& rArgs )
+{
+ LEUnicode* pIcuChars;
+ if( sizeof(LEUnicode) == sizeof(*rArgs.mpStr) )
+ pIcuChars = (LEUnicode*)rArgs.mpStr;
+ else
+ {
+ // this conversion will only be needed when either
+ // ICU's or OOo's unicodes stop being unsigned shorts
+ // TODO: watch out for surrogates!
+ pIcuChars = (LEUnicode*)alloca( rArgs.mnLength * sizeof(LEUnicode) );
+ for( xub_StrLen ic = 0; ic < rArgs.mnLength; ++ic )
+ pIcuChars[ic] = static_cast<LEUnicode>( rArgs.mpStr[ic] );
+ }
+
+ // allocate temporary arrays, note: round to even
+ int nGlyphCapacity = (3 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos ) | 15) + 1;
+
+ struct IcuPosition{ float fX, fY; };
+ const int nAllocSize = sizeof(LEGlyphID) + sizeof(le_int32) + sizeof(IcuPosition);
+ LEGlyphID* pIcuGlyphs = (LEGlyphID*)alloca( (nGlyphCapacity * nAllocSize) + sizeof(IcuPosition) );
+ le_int32* pCharIndices = (le_int32*)((char*)pIcuGlyphs + nGlyphCapacity * sizeof(LEGlyphID) );
+ IcuPosition* pGlyphPositions = (IcuPosition*)((char*)pCharIndices + nGlyphCapacity * sizeof(le_int32) );
+
+ FreetypeServerFont& rFont = reinterpret_cast<FreetypeServerFont&>(rLayout.GetServerFont());
+
+ UErrorCode rcI18n = U_ZERO_ERROR;
+ LEErrorCode rcIcu = LE_NO_ERROR;
+ Point aNewPos( 0, 0 );
+ for( int nGlyphCount = 0;; )
+ {
+ int nMinRunPos, nEndRunPos;
+ bool bRightToLeft;
+ if( !rArgs.GetNextRun( &nMinRunPos, &nEndRunPos, &bRightToLeft ) )
+ break;
+
+ // find matching script
+ // TODO: split up bidi run into script runs
+ le_int32 eScriptCode = -1;
+ for( int i = nMinRunPos; i < nEndRunPos; ++i )
+ {
+ eScriptCode = uscript_getScript( pIcuChars[i], &rcI18n );
+ if( (eScriptCode > 0) && (eScriptCode != latnScriptCode) )
+ break;
+ }
+ if( eScriptCode < 0 ) // TODO: handle errors better
+ eScriptCode = latnScriptCode;
+
+ // get layout engine matching to this script
+ // no engine change necessary if script is latin
+ if( !mpIcuLE || ((eScriptCode != meScriptCode) && (eScriptCode > USCRIPT_INHERITED)) )
+ {
+ // TODO: cache multiple layout engines when multiple scripts are used
+ delete mpIcuLE;
+ meScriptCode = eScriptCode;
+ le_int32 eLangCode = 0; // TODO: get better value
+ mpIcuLE = LayoutEngine::layoutEngineFactory( &maIcuFont, eScriptCode, eLangCode, rcIcu );
+ if( LE_FAILURE(rcIcu) )
+ {
+ delete mpIcuLE;
+ mpIcuLE = NULL;
+ }
+ }
+
+ // fall back to default layout if needed
+ if( !mpIcuLE )
+ break;
+
+ // run ICU layout engine
+ // TODO: get enough context, remove extra glyps below
+ int nRawRunGlyphCount = mpIcuLE->layoutChars( pIcuChars,
+ nMinRunPos, nEndRunPos - nMinRunPos, rArgs.mnLength,
+ bRightToLeft, aNewPos.X(), aNewPos.Y(), rcIcu );
+ if( LE_FAILURE(rcIcu) )
+ return false;
+
+ // import layout info from icu
+ mpIcuLE->getGlyphs( pIcuGlyphs, rcIcu );
+ mpIcuLE->getCharIndices( pCharIndices, rcIcu );
+ mpIcuLE->getGlyphPositions( &pGlyphPositions->fX, rcIcu );
+ mpIcuLE->reset(); // TODO: get rid of this, PROBLEM: crash at exit when removed
+ if( LE_FAILURE(rcIcu) )
+ return false;
+
+ // layout bidi/script runs and export them to a ServerFontLayout
+ // convert results to GlyphItems
+ int nLastCharPos = -1;
+ int nClusterMinPos = -1;
+ int nClusterMaxPos = -1;
+ bool bClusterStart = true;
+ int nFilteredRunGlyphCount = 0;
+ const IcuPosition* pPos = pGlyphPositions;
+ for( int i = 0; i < nRawRunGlyphCount; ++i, ++pPos )
+ {
+ LEGlyphID nGlyphIndex = pIcuGlyphs[i];
+ // ignore glyphs which were marked or deleted by ICU
+ if( (nGlyphIndex == ICU_MARKED_GLYPH)
+ || (nGlyphIndex == ICU_DELETED_GLYPH) )
+ continue;
+
+ // adjust the relative char pos
+ int nCharPos = pCharIndices[i];
+ if( nCharPos >= 0 ) {
+ nCharPos += nMinRunPos;
+ // ICU seems to return bad pCharIndices
+ // for some combinations of ICU+font+text
+ // => better give up now than crash later
+ if( nCharPos >= nEndRunPos )
+ continue;
+ }
+
+ // if needed request glyph fallback by updating LayoutArgs
+ if( !nGlyphIndex )
+ {
+ if( nCharPos >= 0 )
+ {
+ rArgs.NeedFallback( nCharPos, bRightToLeft );
+ if ( (nCharPos > 0) && lcl_CharIsJoiner(rArgs.mpStr[nCharPos-1]) )
+ rArgs.NeedFallback( nCharPos-1, bRightToLeft );
+ else if ( (nCharPos + 1 < nEndRunPos) && lcl_CharIsJoiner(rArgs.mpStr[nCharPos+1]) )
+ rArgs.NeedFallback( nCharPos+1, bRightToLeft );
+ }
+
+ if( SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags )
+ continue;
+ }
+
+
+ // apply vertical flags, etc.
+ bool bDiacritic = false;
+ if( nCharPos >= 0 )
+ {
+ sal_UCS4 aChar = rArgs.mpStr[ nCharPos ];
+ nGlyphIndex = rFont.FixupGlyphIndex( nGlyphIndex, aChar );
+
+ // #i99367# HACK: try to detect all diacritics
+ if( aChar>=0x0300 && aChar<0x2100 )
+ bDiacritic = IsDiacritic( aChar );
+ }
+
+ // get glyph position and its metrics
+ aNewPos = Point( (int)(pPos->fX+0.5), (int)(pPos->fY+0.5) );
+ const GlyphMetric& rGM = rFont.GetGlyphMetric( nGlyphIndex );
+ int nGlyphWidth = rGM.GetCharWidth();
+ if( nGlyphWidth <= 0 )
+ bDiacritic |= true;
+ // #i99367# force all diacritics to zero width
+ // TODO: we need mnOrigWidth/mnLogicWidth/mnNewWidth
+ else if( bDiacritic )
+ nGlyphWidth = 0;
+
+ // heuristic to detect glyph clusters
+ bool bInCluster = true;
+ if( nLastCharPos == -1 )
+ {
+ nClusterMinPos = nClusterMaxPos = nCharPos;
+ bInCluster = false;
+ }
+ else if( !bRightToLeft )
+ {
+ // left-to-right case
+ if( nClusterMinPos > nCharPos )
+ nClusterMinPos = nCharPos; // extend cluster
+ else if( nCharPos <= nClusterMaxPos )
+ /*NOTHING*/; // inside cluster
+ else if( bDiacritic )
+ nClusterMaxPos = nCharPos; // add diacritic to cluster
+ else {
+ nClusterMinPos = nClusterMaxPos = nCharPos; // new cluster
+ bInCluster = false;
+ }
+ }
+ else
+ {
+ // right-to-left case
+ if( nClusterMaxPos < nCharPos )
+ nClusterMaxPos = nCharPos; // extend cluster
+ else if( nCharPos >= nClusterMinPos )
+ /*NOTHING*/; // inside cluster
+ else if( bDiacritic )
+ {
+ nClusterMinPos = nCharPos; // ICU often has [diacritic* baseglyph*]
+ if( bClusterStart ) {
+ nClusterMaxPos = nCharPos;
+ bInCluster = false;
+ }
+ }
+ else
+ {
+ nClusterMinPos = nClusterMaxPos = nCharPos; // new cluster
+ bInCluster = !bClusterStart;
+ }
+ }
+
+ long nGlyphFlags = 0;
+ if( bInCluster )
+ nGlyphFlags |= GlyphItem::IS_IN_CLUSTER;
+ if( bRightToLeft )
+ nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
+ if( bDiacritic )
+ nGlyphFlags |= GlyphItem::IS_DIACRITIC;
+
+ // add resulting glyph item to layout
+ const GlyphItem aGI( nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth );
+ rLayout.AppendGlyph( aGI );
+ ++nFilteredRunGlyphCount;
+ nLastCharPos = nCharPos;
+ bClusterStart = !aGI.IsDiacritic(); // TODO: only needed in RTL-codepath
+ }
+ aNewPos = Point( (int)(pPos->fX+0.5), (int)(pPos->fY+0.5) );
+ nGlyphCount += nFilteredRunGlyphCount;
+ }
+
+ // sort glyphs in visual order
+ // and then in logical order (e.g. diacritics after cluster start)
+ rLayout.SortGlyphItems();
+
+ // determine need for kashida justification
+ if( (rArgs.mpDXArray || rArgs.mnLayoutWidth)
+ && ((meScriptCode == arabScriptCode) || (meScriptCode == syrcScriptCode)) )
+ rArgs.mnFlags |= SAL_LAYOUT_KASHIDA_JUSTIFICATON;
+
+ return true;
+}
+
+#endif // ENABLE_ICU_LAYOUT
+
+// =======================================================================
+
+ServerFontLayoutEngine* FreetypeServerFont::GetLayoutEngine()
+{
+ // find best layout engine for font, platform, script and language
+#ifdef ENABLE_ICU_LAYOUT
+ if( !mpLayoutEngine && FT_IS_SFNT( maFaceFT ) )
+ mpLayoutEngine = new IcuLayoutEngine( *this );
+#endif // ENABLE_ICU_LAYOUT
+
+ return mpLayoutEngine;
+}
+
+// =======================================================================
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/glyphs/gcach_rbmp.cxx b/vcl/source/glyphs/gcach_rbmp.cxx
new file mode 100644
index 000000000000..f951590b4b45
--- /dev/null
+++ b/vcl/source/glyphs/gcach_rbmp.cxx
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include <vcl/glyphcache.hxx>
+#include <string.h>
+
+//------------------------------------------------------------------------
+
+RawBitmap::RawBitmap()
+: mpBits(0), mnAllocated(0)
+{}
+
+//------------------------------------------------------------------------
+
+RawBitmap::~RawBitmap()
+{
+ delete[] mpBits;
+ mpBits = 0;
+ mnAllocated = 0;
+}
+
+//------------------------------------------------------------------------
+
+// used by 90 and 270 degree rotations on 8 bit deep bitmaps
+static void ImplRotate8_90( unsigned char* p1, const unsigned char* p2,
+ int xmax, int ymax, int dx, int dy, int nPad )
+{
+ for( int y = ymax; --y >= 0; p2 += dy )
+ {
+ for( int x = xmax; --x >= 0; p2 += dx )
+ *(p1++) = *p2;
+ for( int i = nPad; --i >= 0; )
+ *(p1++) = 0;
+ }
+}
+
+//------------------------------------------------------------------------
+
+// used by inplace 180 degree rotation on 8 bit deep bitmaps
+static void ImplRotate8_180( unsigned char* p1, int xmax, int ymax, int nPad )
+{
+ unsigned char* p2 = p1 + ymax * (xmax + nPad);
+ for( int y = ymax/2; --y >= 0; )
+ {
+ p2 -= nPad;
+ for( int x = xmax; --x >= 0; )
+ {
+ unsigned char cTmp = *(--p2);
+ *p2 = *p1;
+ *(p1++) = cTmp;
+ }
+ p1 += nPad;
+ }
+
+ // reverse middle line
+ p2 -= nPad;
+ while( p1 < p2 )
+ {
+ unsigned char cTmp = *(--p2);
+ *p2 = *p1;
+ *(p1++) = cTmp;
+ }
+}
+
+//------------------------------------------------------------------------
+
+// used by 90 or 270 degree rotations on 1 bit deep bitmaps
+static void ImplRotate1_90( unsigned char* p1, const unsigned char* p2,
+ int xmax, int ymax, int dx, int nShift, int nDeltaShift, int nPad )
+{
+ for( int y = ymax; --y >= 0; )
+ {
+ unsigned nTemp = 1;
+ const unsigned char* p20 = p2;
+ for( int x = xmax; --x >= 0; p2 += dx )
+ {
+ // build bitwise and store when byte finished
+ nTemp += nTemp + ((*p2 >> nShift) & 1);
+ if( nTemp >= 0x100U )
+ {
+ *(p1++) = (unsigned char)nTemp;
+ nTemp = 1;
+ }
+ }
+ p2 = p20;
+
+ // store left aligned remainder if needed
+ if( nTemp > 1 )
+ {
+ for(; nTemp < 0x100U; nTemp += nTemp ) ;
+ *(p1++) = (unsigned char)nTemp;
+ }
+ // pad scanline with zeroes
+ for( int i = nPad; --i >= 0;)
+ *(p1++) = 0;
+
+ // increase/decrease shift, but keep bound inside 0 to 7
+ nShift += nDeltaShift;
+ if( nShift != (nShift & 7) )
+ p2 -= nDeltaShift;
+ nShift &= 7;
+ }
+}
+
+//------------------------------------------------------------------------
+
+// used by 180 degrees rotations on 1 bit deep bitmaps
+static void ImplRotate1_180( unsigned char* p1, const unsigned char* p2,
+ int xmax, int ymax, int nPad )
+{
+ --p2;
+ for( int y = ymax; --y >= 0; )
+ {
+ p2 -= nPad;
+
+ unsigned nTemp = 1;
+ unsigned nInp = (0x100 + *p2) >> (-xmax & 7);
+ for( int x = xmax; --x >= 0; )
+ {
+ // build bitwise and store when byte finished
+ nTemp += nTemp + (nInp & 1);
+ if( nTemp >= 0x100 )
+ {
+ *(p1++) = (unsigned char)nTemp;
+ nTemp = 1;
+ }
+ // update input byte if needed (and available)
+ if( (nInp >>= 1) <= 1 && ((y != 0) || (x != 0)) )
+ nInp = 0x100 + *(--p2);
+ }
+
+ // store left aligned remainder if needed
+ if( nTemp > 1 )
+ {
+ for(; nTemp < 0x100; nTemp += nTemp ) ;
+ *(p1++) = (unsigned char)nTemp;
+ }
+ // scanline pad is already clean
+ p1 += nPad;
+ }
+}
+
+//------------------------------------------------------------------------
+
+bool RawBitmap::Rotate( int nAngle )
+{
+ ULONG nNewScanlineSize = 0;
+ ULONG nNewHeight = 0;
+ ULONG nNewWidth = 0;
+
+ // do inplace rotation or prepare double buffered rotation
+ switch( nAngle )
+ {
+ case 0: // nothing to do
+ case 3600:
+ return true;
+ default: // non rectangular angles not allowed
+ return false;
+ case 1800: // rotate by 180 degrees
+ mnXOffset = -(mnXOffset + mnWidth);
+ mnYOffset = -(mnYOffset + mnHeight);
+ if( mnBitCount == 8 )
+ {
+ ImplRotate8_180( mpBits, mnWidth, mnHeight, mnScanlineSize-mnWidth );
+ return true;
+ }
+ nNewWidth = mnWidth;
+ nNewHeight = mnHeight;
+ nNewScanlineSize = mnScanlineSize;
+ break;
+ case +900: // left by 90 degrees
+ case -900:
+ case 2700: // right by 90 degrees
+ nNewWidth = mnHeight;
+ nNewHeight = mnWidth;
+ if( mnBitCount==1 )
+ nNewScanlineSize = (nNewWidth + 7) / 8;
+ else
+ nNewScanlineSize = (nNewWidth + 3) & -4;
+ break;
+ }
+
+ unsigned int nBufSize = nNewHeight * nNewScanlineSize;
+ unsigned char* pBuf = new unsigned char[ nBufSize ];
+ if( !pBuf )
+ return false;
+
+ memset( pBuf, 0, nBufSize );
+ int i;
+
+ // dispatch non-inplace rotations
+ switch( nAngle )
+ {
+ case 1800: // rotate by 180 degrees
+ // we know we only need to deal with 1 bit depth
+ ImplRotate1_180( pBuf, mpBits + mnHeight * mnScanlineSize,
+ mnWidth, mnHeight, mnScanlineSize - (mnWidth + 7) / 8 );
+ break;
+ case +900: // rotate left by 90 degrees
+ i = mnXOffset;
+ mnXOffset = mnYOffset;
+ mnYOffset = -nNewHeight - i;
+ if( mnBitCount == 8 )
+ ImplRotate8_90( pBuf, mpBits + mnWidth - 1,
+ nNewWidth, nNewHeight, +mnScanlineSize, -1-mnHeight*mnScanlineSize,
+ nNewScanlineSize - nNewWidth );
+ else
+ ImplRotate1_90( pBuf, mpBits + (mnWidth - 1) / 8,
+ nNewWidth, nNewHeight, +mnScanlineSize,
+ (-mnWidth & 7), +1, nNewScanlineSize - (nNewWidth + 7) / 8 );
+ break;
+ case 2700: // rotate right by 90 degrees
+ case -900:
+ i = mnXOffset;
+ mnXOffset = -(nNewWidth + mnYOffset);
+ mnYOffset = i;
+ if( mnBitCount == 8 )
+ ImplRotate8_90( pBuf, mpBits + mnScanlineSize * (mnHeight-1),
+ nNewWidth, nNewHeight, -mnScanlineSize, +1+mnHeight*mnScanlineSize,
+ nNewScanlineSize - nNewWidth );
+ else
+ ImplRotate1_90( pBuf, mpBits + mnScanlineSize * (mnHeight-1),
+ nNewWidth, nNewHeight, -mnScanlineSize,
+ +7, -1, nNewScanlineSize - (nNewWidth + 7) / 8 );
+ break;
+ }
+
+ mnWidth = nNewWidth;
+ mnHeight = nNewHeight;
+ mnScanlineSize = nNewScanlineSize;
+
+ if( nBufSize < mnAllocated )
+ {
+ memcpy( mpBits, pBuf, nBufSize );
+ delete[] pBuf;
+ }
+ else
+ {
+ delete[] mpBits;
+ mpBits = pBuf;
+ mnAllocated = nBufSize;
+ }
+
+ return true;
+}
+
+//------------------------------------------------------------------------
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/glyphs/gcach_vdev.cxx b/vcl/source/glyphs/gcach_vdev.cxx
new file mode 100644
index 000000000000..7a090de30128
--- /dev/null
+++ b/vcl/source/glyphs/gcach_vdev.cxx
@@ -0,0 +1,290 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include <gcach_vdev.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/outfont.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/metric.hxx>
+
+// =======================================================================
+// VirtDevServerFont
+// =======================================================================
+
+// -----------------------------------------------------------------------
+
+void VirtDevServerFont::AnnounceFonts( ImplDevFontList* pToAdd )
+{
+ // TODO: get fonts on server but not on client,
+ // problem is that currently there is no serverside virtual device...
+ VirtualDevice vdev( 1 );
+ long nCount = vdev.GetDevFontCount();
+
+ for( int i = 0; i < nCount; ++i )
+ {
+ const FontInfo aFontInfo = vdev.GetDevFont( i );
+
+ ImplFontData& rData = *new ImplFontData;
+ rData.SetSysData( new FontSysData( (void*)SERVERFONT_MAGIC ) );
+
+ rData.maName = aFontInfo.GetName();
+ rData.maStyleName = aFontInfo.GetStyleName();
+ rData.mnWidth = aFontInfo.GetWidth();
+ rData.mnHeight = aFontInfo.GetHeight();
+ rData.meFamily = aFontInfo.GetFamily();
+ rData.meCharSet = aFontInfo.GetCharSet();
+ rData.mePitch = aFontInfo.GetPitch();
+ rData.meWidthType = aFontInfo.GetWidthType();
+ rData.meWeight = aFontInfo.GetWeight();
+ rData.meItalic = aFontInfo.GetItalic();
+ rData.meType = aFontInfo.GetType();
+ rData.meFamily = aFontInfo.GetFamily();
+
+ rData.mbOrientation = true; // TODO: where to get this info?
+ rData.mbDevice = false;
+ rData.mnQuality = 0; // prefer client-side fonts if available
+
+ pToAdd->Add( &rData );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void VirtDevServerFont::ClearFontList()
+{
+ // TODO
+}
+
+// -----------------------------------------------------------------------
+
+VirtDevServerFont* VirtDevServerFont::CreateFont( const ImplFontSelectData& rFSD )
+{
+ VirtDevServerFont* pServerFont = NULL;
+ // TODO: search list of VirtDevServerFonts, return NULL if not found
+ // pServerFont = new VirtDevServerFont( rFSD );
+ return pServerFont;
+}
+
+// -----------------------------------------------------------------------
+
+VirtDevServerFont::VirtDevServerFont( const ImplFontSelectData& rFSD )
+: ServerFont( rFSD)
+{}
+
+// -----------------------------------------------------------------------
+
+void VirtDevServerFont::FetchFontMetric( ImplFontMetricData& rTo, long& rFactor ) const
+{
+ const ImplFontSelectData& aFSD = GetFontSelData();
+
+ Font aFont;
+ aFont.SetName ( aFSD.maName );
+ aFont.SetStyleName ( aFSD.maStyleName );
+ aFont.SetHeight ( aFSD.mnHeight );
+ aFont.SetWidth ( aFSD.mnWidth );
+ aFont.SetOrientation( aFSD.mnOrientation );
+ aFont.SetVertical ( GetFontSelData().mbVertical );
+
+ VirtualDevice vdev( 1 );
+ FontMetric aMetric( vdev.GetFontMetric( aFont ) );
+
+ rFactor = 0x100;
+
+ rTo.mnAscent = aMetric.GetAscent();
+ rTo.mnDescent = aMetric.GetDescent();
+ rTo.mnIntLeading = aMetric.GetIntLeading();
+ rTo.mnExtLeading = aMetric.GetExtLeading();
+ rTo.mnSlant = aMetric.GetSlant();
+ rTo.meType = aMetric.GetType();
+ rTo.mnFirstChar = 0x0020; // TODO: where to get this info?
+ rTo.mnLastChar = 0xFFFE; // TODO: where to get this info?
+
+ rTo.mnWidth = aFSD.mnWidth;
+ rTo.maName = aFSD.maName;
+ rTo.maStyleName = aFSD.maStyleName;
+ rTo.mnOrientation = aFSD.mnOrientation;
+ rTo.meFamily = aFSD.meFamily;
+ rTo.meCharSet = aFSD.meCharSet;
+ rTo.meWeight = aFSD.meWeight;
+ rTo.meItalic = aFSD.meItalic;
+ rTo.mePitch = aFSD.mePitch;
+ rTo.mbDevice = FALSE;
+}
+
+// -----------------------------------------------------------------------
+
+int VirtDevServerFont::GetGlyphIndex( sal_Unicode aChar ) const
+{
+ return aChar;
+}
+
+// -----------------------------------------------------------------------
+
+void VirtDevServerFont::InitGlyphData( int nGlyphIndex, GlyphData& rGD ) const
+{
+ Font aFont;
+ aFont.SetName ( GetFontSelData().maName );
+ aFont.SetStyleName ( GetFontSelData().maStyleName );
+ aFont.SetHeight ( GetFontSelData().mnHeight );
+ aFont.SetWidth ( GetFontSelData().mnWidth );
+ aFont.SetOrientation( GetFontSelData().mnOrientation );
+ aFont.SetVertical ( GetFontSelData().mbVertical );
+
+ VirtualDevice vdev( 1 );
+ vdev.SetFont( aFont );
+
+ // get glyph metrics
+ sal_Int32 nCharWidth = 10;
+// TODO: vdev.GetCharWidth( nGlyphIndex, nGlyphIndex, &nCharWidth );
+ rGD.SetCharWidth( nCharWidth );
+
+ sal_Unicode aChar = nGlyphIndex;
+ String aGlyphStr( &aChar, 1 );
+ Rectangle aRect;
+ if( vdev.GetTextBoundRect( aRect, aGlyphStr, 0, 1 ) )
+ {
+ rGD.SetOffset( aRect.Top(), aRect.Left() );
+ rGD.SetDelta( vdev.GetTextWidth( nGlyphIndex ), 0 );
+ rGD.SetSize( aRect.GetSize() );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+bool VirtDevServerFont::GetAntialiasAdvice( void ) const
+{
+ return false;
+}
+
+// -----------------------------------------------------------------------
+
+bool VirtDevServerFont::GetGlyphBitmap1( int nGlyphIndex, RawBitmap& ) const
+{
+ /*
+ sal_Unicode aChar = nGlyphIndex;
+ String aGlyphStr( &aChar, 1 );
+
+ // draw bitmap
+ vdev.SetOutputSizePixel( aSize, TRUE );
+ vdev.DrawText( Point(0,0)-rGD.GetMetric().GetOffset(), aGlyphStr );
+
+ // create new glyph item
+
+ const Bitmap& rBitmap = vdev.GetBitmap( Point(0,0), aSize );
+ rGD.SetBitmap( new Bitmap( rBitmap ) );
+ return true;
+ */
+ return false;
+}
+
+// -----------------------------------------------------------------------
+
+bool VirtDevServerFont::GetGlyphBitmap8( int nGlyphIndex, RawBitmap& ) const
+{
+ return false;
+}
+
+// -----------------------------------------------------------------------
+
+int VirtDevServerFont::GetGlyphKernValue( int, int ) const
+{
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+ULONG VirtDevServerFont::GetKernPairs( ImplKernPairData** ppImplKernPairs ) const
+{
+ Font aFont;
+ aFont.SetName ( GetFontSelData().maName );
+ aFont.SetStyleName ( GetFontSelData().maStyleName );
+ aFont.SetHeight ( GetFontSelData().mnHeight );
+ aFont.SetWidth ( GetFontSelData().mnWidth );
+ aFont.SetOrientation( GetFontSelData().mnOrientation );
+ aFont.SetVertical ( GetFontSelData().mbVertical );
+
+ VirtualDevice vdev( 1 );
+ vdev.SetFont( aFont );
+
+ ULONG nPairs = vdev.GetKerningPairCount();
+ if( nPairs > 0 )
+ {
+ KerningPair* const pKernPairs = new KerningPair[ nPairs ];
+ vdev.GetKerningPairs( nPairs, pKernPairs );
+
+ *ppImplKernPairs = new ImplKernPairData[ nPairs ];
+ ImplKernPairData* pTo = *ppImplKernPairs;
+ KerningPair* pFrom = pKernPairs;
+ for ( ULONG n = 0; n < nPairs; n++ )
+ {
+ pTo->mnChar1 = pFrom->nChar1;
+ pTo->mnChar2 = pFrom->nChar2;
+ pTo->mnKern = pFrom->nKern;
+ ++pFrom;
+ ++pTo;
+ }
+
+ delete[] pKernPairs;
+ }
+
+ return nPairs;
+}
+
+// -----------------------------------------------------------------------
+
+bool VirtDevServerFont::GetGlyphOutline( int nGlyphIndex, PolyPolygon& rPolyPoly ) const
+{
+ return false;
+ /*
+ Font aFont;
+ aFont.SetName ( GetFontSelData().maName );
+ aFont.SetStyleName ( GetFontSelData().maStyleName );
+ aFont.SetHeight ( GetFontSelData().mnHeight );
+ aFont.SetWidth ( GetFontSelData().mnWidth );
+ aFont.SetOrientation( GetFontSelData().mnOrientation );
+ aFont.SetVertical ( GetFontSelData().mbVertical );
+
+ VirtualDevice vdev( 1 );
+ vdev.SetFont( aFont );
+
+ const bool bOptimize = true;
+
+ sal_Unicode aChar = nGlyphIndex;
+ String aGlyphStr( &aChar, 1 );
+ return vdev.GetTextOutline( rPolyPoly, aGlyphStr, 0, 1, bOptimize );
+ */
+}
+
+// =======================================================================
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/glyphs/gcach_vdev.hxx b/vcl/source/glyphs/gcach_vdev.hxx
new file mode 100644
index 000000000000..48d602677c16
--- /dev/null
+++ b/vcl/source/glyphs/gcach_vdev.hxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include <vcl/glyphcache.hxx>
+
+// -----------------------------------------------------------------------
+
+class VirtDevServerFont : public ServerFont
+{
+public:
+ virtual bool GetAntialiasAdvice( void ) const;
+ virtual bool GetGlyphBitmap1( int nGlyphIndex, RawBitmap& ) const;
+ virtual bool GetGlyphBitmap8( int nGlyphIndex, RawBitmap& ) const;
+ virtual bool GetGlyphOutline( int nGlyphIndex, PolyPolygon& ) const;
+
+protected:
+ friend class GlyphCache;
+ static void AnnounceFonts( ImplDevFontList* );
+ static void ClearFontList();
+
+ static VirtDevServerFont* CreateFont( const ImplFontSelectData& );
+ virtual void FetchFontMetric( ImplFontMetricData&, long& rFactor ) const;
+ virtual ULONG GetKernPairs( ImplKernPairData** ) const;
+ virtual int GetGlyphKernValue( int, int ) const;
+
+ virtual int GetGlyphIndex( sal_Unicode ) const;
+ virtual void InitGlyphData( int nGlyphIndex, GlyphData& ) const;
+
+private:
+ VirtDevServerFont( const ImplFontSelectData& );
+};
+
+// -----------------------------------------------------------------------
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/glyphs/glyphcache.cxx b/vcl/source/glyphs/glyphcache.cxx
new file mode 100644
index 000000000000..159639ccdd05
--- /dev/null
+++ b/vcl/source/glyphs/glyphcache.cxx
@@ -0,0 +1,592 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <vcl/salbtype.hxx>
+#include <gcach_ftyp.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/outfont.hxx>
+
+#ifdef ENABLE_GRAPHITE
+#include <vcl/graphite_features.hxx>
+#endif
+
+#include <rtl/ustring.hxx> // used only for string=>hashvalue
+#include <osl/file.hxx>
+#include <tools/debug.hxx>
+
+// =======================================================================
+// GlyphCache
+// =======================================================================
+
+static GlyphCache* pInstance = NULL;
+
+GlyphCache::GlyphCache( GlyphCachePeer& rPeer )
+: mrPeer( rPeer ),
+ mnMaxSize( 1500000 ),
+ mnBytesUsed(sizeof(GlyphCache)),
+ mnLruIndex(0),
+ mnGlyphCount(0),
+ mpCurrentGCFont(NULL),
+ mpFtManager(NULL)
+{
+ pInstance = this;
+ mpFtManager = new FreetypeManager;
+}
+
+// -----------------------------------------------------------------------
+
+GlyphCache::~GlyphCache()
+{
+ InvalidateAllGlyphs();
+ if( mpFtManager )
+ delete mpFtManager;
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::InvalidateAllGlyphs()
+{
+}
+
+// -----------------------------------------------------------------------
+
+inline
+size_t GlyphCache::IFSD_Hash::operator()( const ImplFontSelectData& rFontSelData ) const
+{
+ // TODO: is it worth to improve this hash function?
+ sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFontSelData.mpFontData );
+#ifdef ENABLE_GRAPHITE
+ if (rFontSelData.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
+ != STRING_NOTFOUND)
+ {
+ rtl::OString aFeatName = rtl::OUStringToOString( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
+ nFontId ^= aFeatName.hashCode();
+ }
+#endif
+ size_t nHash = nFontId << 8;
+ nHash += rFontSelData.mnHeight;
+ nHash += rFontSelData.mnOrientation;
+ nHash += rFontSelData.mbVertical;
+ nHash += rFontSelData.meItalic;
+ nHash += rFontSelData.meWeight;
+#ifdef ENABLE_GRAPHITE
+ nHash += rFontSelData.meLanguage;
+#endif
+ return nHash;
+}
+
+// -----------------------------------------------------------------------
+
+bool GlyphCache::IFSD_Equal::operator()( const ImplFontSelectData& rA, const ImplFontSelectData& rB) const
+{
+ // check font ids
+ sal_IntPtr nFontIdA = reinterpret_cast<sal_IntPtr>( rA.mpFontData );
+ sal_IntPtr nFontIdB = reinterpret_cast<sal_IntPtr>( rB.mpFontData );
+ if( nFontIdA != nFontIdB )
+ return false;
+
+ // compare with the requested metrics
+ if( (rA.mnHeight != rB.mnHeight)
+ || (rA.mnOrientation != rB.mnOrientation)
+ || (rA.mbVertical != rB.mbVertical)
+ || (rA.mbNonAntialiased != rB.mbNonAntialiased) )
+ return false;
+
+ if( (rA.meItalic != rB.meItalic)
+ || (rA.meWeight != rB.meWeight) )
+ return false;
+
+ // NOTE: ignoring meFamily deliberately
+
+ // compare with the requested width, allow default width
+ if( (rA.mnWidth != rB.mnWidth)
+ && ((rA.mnHeight != rB.mnWidth) || (rA.mnWidth != 0)) )
+ return false;
+#ifdef ENABLE_GRAPHITE
+ if (rA.meLanguage != rB.meLanguage)
+ return false;
+ // check for features
+ if ((rA.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
+ != STRING_NOTFOUND ||
+ rB.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
+ != STRING_NOTFOUND) && rA.maTargetName != rB.maTargetName)
+ return false;
+#endif
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+GlyphCache& GlyphCache::GetInstance()
+{
+ return *pInstance;
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::LoadFonts()
+{
+ if( const char* pFontPath = ::getenv( "SAL_FONTPATH_PRIVATE" ) )
+ AddFontPath( String::CreateFromAscii( pFontPath ) );
+ const String& rFontPath = Application::GetFontPath();
+ if( rFontPath.Len() > 0 )
+ AddFontPath( rFontPath );
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::ClearFontPath()
+{
+ if( mpFtManager )
+ mpFtManager->ClearFontList();
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::AddFontPath( const String& rFontPath )
+{
+ if( !mpFtManager )
+ return;
+
+ for( xub_StrLen nBreaker1 = 0, nBreaker2 = 0; nBreaker2 != STRING_LEN; nBreaker1 = nBreaker2 + 1 )
+ {
+ nBreaker2 = rFontPath.Search( ';', nBreaker1 );
+ if( nBreaker2 == STRING_NOTFOUND )
+ nBreaker2 = STRING_LEN;
+
+ ::rtl::OUString aUrlName;
+ osl::FileBase::getFileURLFromSystemPath( rFontPath.Copy( nBreaker1, nBreaker2 ), aUrlName );
+ mpFtManager->AddFontDir( aUrlName );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::AddFontFile( const rtl::OString& rNormalizedName, int nFaceNum,
+ sal_IntPtr nFontId, const ImplDevFontAttributes& rDFA, const ExtraKernInfo* pExtraKern )
+{
+ if( mpFtManager )
+ mpFtManager->AddFontFile( rNormalizedName, nFaceNum, nFontId, rDFA, pExtraKern );
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::AnnounceFonts( ImplDevFontList* pList ) const
+{
+ if( mpFtManager )
+ mpFtManager->AnnounceFonts( pList );
+ // VirtDevServerFont::AnnounceFonts( pList );
+}
+
+// -----------------------------------------------------------------------
+
+ServerFont* GlyphCache::CacheFont( const ImplFontSelectData& rFontSelData )
+{
+ // a serverfont request has pFontData
+ if( rFontSelData.mpFontData == NULL )
+ return NULL;
+ // a serverfont request has a fontid > 0
+ sal_IntPtr nFontId = rFontSelData.mpFontData->GetFontId();
+ if( nFontId <= 0 )
+ return NULL;
+
+ // the FontList's key mpFontData member is reinterpreted as font id
+ ImplFontSelectData aFontSelData = rFontSelData;
+ aFontSelData.mpFontData = reinterpret_cast<ImplFontData*>( nFontId );
+ FontList::iterator it = maFontList.find( aFontSelData );
+ if( it != maFontList.end() )
+ {
+ ServerFont* pFound = it->second;
+ if( pFound )
+ pFound->AddRef();
+ return pFound;
+ }
+
+ // font not cached yet => create new font item
+ ServerFont* pNew = NULL;
+ if( mpFtManager )
+ pNew = mpFtManager->CreateFont( aFontSelData );
+ // TODO: pNew = VirtDevServerFont::CreateFont( aFontSelData );
+
+ if( pNew )
+ {
+ maFontList[ aFontSelData ] = pNew;
+ mnBytesUsed += pNew->GetByteCount();
+
+ // enable garbage collection for new font
+ if( !mpCurrentGCFont )
+ {
+ mpCurrentGCFont = pNew;
+ pNew->mpNextGCFont = pNew;
+ pNew->mpPrevGCFont = pNew;
+ }
+ else
+ {
+ pNew->mpNextGCFont = mpCurrentGCFont;
+ pNew->mpPrevGCFont = mpCurrentGCFont->mpPrevGCFont;
+ pNew->mpPrevGCFont->mpNextGCFont = pNew;
+ mpCurrentGCFont->mpPrevGCFont = pNew;
+ }
+ }
+
+ return pNew;
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::UncacheFont( ServerFont& rServerFont )
+{
+ // the interface for rServerFont must be const because a
+ // user who wants to release it only got const ServerFonts.
+ // The caching algorithm needs a non-const object
+ ServerFont* pFont = const_cast<ServerFont*>( &rServerFont );
+ if( (pFont->Release() <= 0)
+ && (mnMaxSize <= (mnBytesUsed + mrPeer.GetByteCount())) )
+ {
+ mpCurrentGCFont = pFont;
+ GarbageCollect();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+ULONG GlyphCache::CalcByteCount() const
+{
+ ULONG nCacheSize = sizeof(*this);
+ for( FontList::const_iterator it = maFontList.begin(); it != maFontList.end(); ++it )
+ {
+ const ServerFont* pSF = it->second;
+ if( pSF )
+ nCacheSize += pSF->GetByteCount();
+ }
+ // TODO: also account something for hashtable management
+ return nCacheSize;
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::GarbageCollect()
+{
+ // when current GC font has been destroyed get another one
+ if( !mpCurrentGCFont )
+ {
+ FontList::iterator it = maFontList.begin();
+ if( it != maFontList.end() )
+ mpCurrentGCFont = it->second;
+ }
+
+ // unless there is no other font to collect
+ if( !mpCurrentGCFont )
+ return;
+
+ // prepare advance to next font for garbage collection
+ ServerFont* const pServerFont = mpCurrentGCFont;
+ mpCurrentGCFont = pServerFont->mpNextGCFont;
+
+ if( (pServerFont == mpCurrentGCFont) // no other fonts
+ || (pServerFont->GetRefCount() > 0) ) // font still used
+ {
+ // try to garbage collect at least a few bytes
+ pServerFont->GarbageCollect( mnLruIndex - mnGlyphCount/2 );
+ }
+ else // current GC font is unreferenced
+ {
+ DBG_ASSERT( (pServerFont->GetRefCount() == 0),
+ "GlyphCache::GC detected RefCount underflow" );
+
+ // free all pServerFont related data
+ pServerFont->GarbageCollect( mnLruIndex+0x10000000 );
+ if( pServerFont == mpCurrentGCFont )
+ mpCurrentGCFont = NULL;
+ const ImplFontSelectData& rIFSD = pServerFont->GetFontSelData();
+ maFontList.erase( rIFSD );
+ mrPeer.RemovingFont( *pServerFont );
+ mnBytesUsed -= pServerFont->GetByteCount();
+
+ // remove font from list of garbage collected fonts
+ if( pServerFont->mpPrevGCFont )
+ pServerFont->mpPrevGCFont->mpNextGCFont = pServerFont->mpNextGCFont;
+ if( pServerFont->mpNextGCFont )
+ pServerFont->mpNextGCFont->mpPrevGCFont = pServerFont->mpPrevGCFont;
+ if( pServerFont == mpCurrentGCFont )
+ mpCurrentGCFont = NULL;
+
+ delete pServerFont;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+inline void GlyphCache::UsingGlyph( ServerFont&, GlyphData& rGlyphData )
+{
+ rGlyphData.SetLruValue( mnLruIndex++ );
+}
+
+// -----------------------------------------------------------------------
+
+inline void GlyphCache::AddedGlyph( ServerFont& rServerFont, GlyphData& rGlyphData )
+{
+ ++mnGlyphCount;
+ mnBytesUsed += sizeof( rGlyphData );
+ UsingGlyph( rServerFont, rGlyphData );
+ GrowNotify();
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::GrowNotify()
+{
+ if( (mnBytesUsed + mrPeer.GetByteCount()) > mnMaxSize )
+ GarbageCollect();
+}
+
+// -----------------------------------------------------------------------
+
+inline void GlyphCache::RemovingGlyph( ServerFont& rSF, GlyphData& rGD, int nGlyphIndex )
+{
+ mrPeer.RemovingGlyph( rSF, rGD, nGlyphIndex );
+ mnBytesUsed -= sizeof( GlyphData );
+ --mnGlyphCount;
+}
+
+// =======================================================================
+// ServerFont
+// =======================================================================
+
+ServerFont::ServerFont( const ImplFontSelectData& rFSD )
+: maGlyphList( 0),
+ maFontSelData(rFSD),
+ mnExtInfo(0),
+ mnRefCount(1),
+ mnBytesUsed( sizeof(ServerFont) ),
+ mpPrevGCFont( NULL ),
+ mpNextGCFont( NULL ),
+ mnCos( 0x10000),
+ mnSin( 0 ),
+ mnZWJ( 0 ),
+ mnZWNJ( 0 ),
+ mbCollectedZW( false )
+{
+ // TODO: move update of mpFontEntry into FontEntry class when
+ // it becomes reponsible for the ServerFont instantiation
+ ((ImplServerFontEntry*)rFSD.mpFontEntry)->SetServerFont( this );
+
+ if( rFSD.mnOrientation != 0 )
+ {
+ const double dRad = rFSD.mnOrientation * ( F_2PI / 3600.0 );
+ mnCos = static_cast<long>( 0x10000 * cos( dRad ) + 0.5 );
+ mnSin = static_cast<long>( 0x10000 * sin( dRad ) + 0.5 );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+ServerFont::~ServerFont()
+{
+ ReleaseFromGarbageCollect();
+}
+
+// -----------------------------------------------------------------------
+
+void ServerFont::ReleaseFromGarbageCollect()
+{
+ // remove from GC list
+ ServerFont* pPrev = mpPrevGCFont;
+ ServerFont* pNext = mpNextGCFont;
+ if( pPrev ) pPrev->mpNextGCFont = pNext;
+ if( pNext ) pNext->mpPrevGCFont = pPrev;
+ mpPrevGCFont = NULL;
+ mpNextGCFont = NULL;
+}
+
+// -----------------------------------------------------------------------
+
+long ServerFont::Release() const
+{
+ DBG_ASSERT( mnRefCount > 0, "ServerFont: RefCount underflow" );
+ return --mnRefCount;
+}
+
+// -----------------------------------------------------------------------
+
+GlyphData& ServerFont::GetGlyphData( int nGlyphIndex )
+{
+ // usually the GlyphData is cached
+ GlyphList::iterator it = maGlyphList.find( nGlyphIndex );
+ if( it != maGlyphList.end() ) {
+ GlyphData& rGlyphData = it->second;
+ GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData );
+ return rGlyphData;
+ }
+
+ // sometimes not => we need to create and initialize it ourselves
+ GlyphData& rGlyphData = maGlyphList[ nGlyphIndex ];
+ mnBytesUsed += sizeof( GlyphData );
+ InitGlyphData( nGlyphIndex, rGlyphData );
+ GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData );
+ return rGlyphData;
+}
+
+// -----------------------------------------------------------------------
+
+void ServerFont::GarbageCollect( long nMinLruIndex )
+{
+ GlyphList::iterator it_next = maGlyphList.begin();
+ while( it_next != maGlyphList.end() )
+ {
+ GlyphList::iterator it = it_next++;
+ GlyphData& rGD = it->second;
+ if( (nMinLruIndex - rGD.GetLruValue()) > 0 )
+ {
+ OSL_ASSERT( mnBytesUsed >= sizeof(GlyphData) );
+ mnBytesUsed -= sizeof( GlyphData );
+ GlyphCache::GetInstance().RemovingGlyph( *this, rGD, it->first );
+ maGlyphList.erase( it );
+ it_next = maGlyphList.begin();
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+Point ServerFont::TransformPoint( const Point& rPoint ) const
+{
+ if( mnCos == 0x10000 )
+ return rPoint;
+ // TODO: use 32x32=>64bit intermediate
+ const double dCos = mnCos * (1.0 / 0x10000);
+ const double dSin = mnSin * (1.0 / 0x10000);
+ long nX = (long)(rPoint.X() * dCos + rPoint.Y() * dSin);
+ long nY = (long)(rPoint.Y() * dCos - rPoint.X() * dSin);
+ return Point( nX, nY );
+}
+
+bool ServerFont::IsGlyphInvisible( int nGlyphIndex )
+{
+ if (!mbCollectedZW)
+ {
+ mnZWJ = GetGlyphIndex( 0x200D );
+ mnZWNJ = GetGlyphIndex( 0x200C );
+ mbCollectedZW = true;
+ }
+
+ if( !nGlyphIndex ) // don't hide the NotDef glyph
+ return false;
+ if( (nGlyphIndex == mnZWNJ) || (nGlyphIndex == mnZWJ) )
+ return true;
+
+ return false;
+}
+
+// =======================================================================
+
+ImplServerFontEntry::ImplServerFontEntry( ImplFontSelectData& rFSD )
+: ImplFontEntry( rFSD )
+, mpServerFont( NULL )
+, mbGotFontOptions( false )
+, mbValidFontOptions( false )
+{}
+
+// -----------------------------------------------------------------------
+
+ImplServerFontEntry::~ImplServerFontEntry()
+{
+ // TODO: remove the ServerFont here instead of in the GlyphCache
+}
+
+// =======================================================================
+
+ExtraKernInfo::ExtraKernInfo( sal_IntPtr nFontId )
+: mbInitialized( false ),
+ mnFontId( nFontId ),
+ maUnicodeKernPairs( 0 )
+{}
+
+//--------------------------------------------------------------------------
+
+bool ExtraKernInfo::HasKernPairs() const
+{
+ if( !mbInitialized )
+ Initialize();
+ return !maUnicodeKernPairs.empty();
+}
+
+//--------------------------------------------------------------------------
+
+int ExtraKernInfo::GetUnscaledKernPairs( ImplKernPairData** ppKernPairs ) const
+{
+ if( !mbInitialized )
+ Initialize();
+
+ // return early if no kerning available
+ if( maUnicodeKernPairs.empty() )
+ return 0;
+
+ // allocate kern pair table
+ int nKernCount = maUnicodeKernPairs.size();
+ *ppKernPairs = new ImplKernPairData[ nKernCount ];
+
+ // fill in unicode kern pairs with the kern value scaled to the font width
+ ImplKernPairData* pKernData = *ppKernPairs;
+ UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.begin();
+ for(; it != maUnicodeKernPairs.end(); ++it )
+ *(pKernData++) = *it;
+
+ return nKernCount;
+}
+
+//--------------------------------------------------------------------------
+
+int ExtraKernInfo::GetUnscaledKernValue( sal_Unicode cLeft, sal_Unicode cRight ) const
+{
+ if( !mbInitialized )
+ Initialize();
+
+ if( maUnicodeKernPairs.empty() )
+ return 0;
+
+ ImplKernPairData aKernPair = { cLeft, cRight, 0 };
+ UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.find( aKernPair );
+ if( it == maUnicodeKernPairs.end() )
+ return 0;
+
+ int nUnscaledValue = (*it).mnKern;
+ return nUnscaledValue;
+}
+
+// =======================================================================
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/glyphs/graphite_adaptors.cxx b/vcl/source/glyphs/graphite_adaptors.cxx
new file mode 100644
index 000000000000..f5272333bf8d
--- /dev/null
+++ b/vcl/source/glyphs/graphite_adaptors.cxx
@@ -0,0 +1,339 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// Description: Implements the Graphite interfaces with access to the
+// platform's font and graphics systems.
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+// We need this to enable namespace support in libgrengine headers.
+#define GR_NAMESPACE
+
+// Header files
+//
+// Standard Library
+#include <string>
+#include <cassert>
+// Libraries
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <i18npool/mslangid.hxx>
+// Platform
+#ifndef WNT
+#include <saldisp.hxx>
+
+#include <vcl/salgdi.hxx>
+
+#include <freetype/ftsynth.h>
+
+// Module
+#include "gcach_ftyp.hxx"
+
+#include <vcl/graphite_features.hxx>
+#include <vcl/graphite_adaptors.hxx>
+
+// Module private type definitions and forward declarations.
+//
+using gr::GrResult;
+namespace
+{
+ inline float from_hinted(const int x) {
+ return static_cast<float>(x + 32) / 64.0;
+ }
+ typedef std::hash_map<long,bool> SilfMap;
+}
+extern FT_Error (*pFTEmbolden)(FT_GlyphSlot);
+extern FT_Error (*pFTOblique)(FT_GlyphSlot);
+
+// class CharacterRenderProperties implentation.
+//
+FontProperties::FontProperties(const FreetypeServerFont &font) throw()
+{
+ clrFore = gr::kclrBlack;
+ clrBack = gr::kclrTransparent;
+
+ pixHeight = from_hinted(font.GetMetricsFT().height);
+
+ switch (font.GetFontSelData().meWeight)
+ {
+ case WEIGHT_SEMIBOLD: case WEIGHT_BOLD:
+ case WEIGHT_ULTRABOLD: case WEIGHT_BLACK:
+ fBold = true;
+ break;
+ default :
+ fBold = false;
+ }
+
+ switch (font.GetFontSelData().meItalic)
+ {
+ case ITALIC_NORMAL: case ITALIC_OBLIQUE:
+ fItalic = true;
+ break;
+ default :
+ fItalic = false;
+ }
+
+ // Get the font name, but prefix with file name hash in case
+ // there are 2 fonts on the system with the same face name
+ sal_Int32 nHashCode = font.GetFontFileName()->hashCode();
+ ::rtl::OUStringBuffer nHashFaceName;
+ nHashFaceName.append(nHashCode, 16);
+ const sal_Unicode * name = font.GetFontSelData().maName.GetBuffer();
+ nHashFaceName.append(name);
+
+ const size_t name_sz = std::min(sizeof szFaceName/sizeof(wchar_t)-1,
+ static_cast<size_t>(nHashFaceName.getLength()));
+
+ std::copy(nHashFaceName.getStr(), nHashFaceName.getStr() + name_sz, szFaceName);
+ szFaceName[name_sz] = '\0';
+}
+
+// class GraphiteFontAdaptor implementaion.
+//
+GraphiteFontAdaptor::GraphiteFontAdaptor(ServerFont & sfont, const sal_Int32 dpiX, const sal_Int32 dpiY)
+ : mrFont(static_cast<FreetypeServerFont &>(sfont)),
+ maFontProperties(static_cast<FreetypeServerFont &>(sfont)),
+ mnDpiX(dpiX),
+ mnDpiY(dpiY),
+ mfAscent(from_hinted(static_cast<FreetypeServerFont &>(sfont).GetMetricsFT().ascender)),
+ mfDescent(from_hinted(static_cast<FreetypeServerFont &>(sfont).GetMetricsFT().descender)),
+ mfEmUnits(static_cast<FreetypeServerFont &>(sfont).GetMetricsFT().y_ppem),
+ mpFeatures(NULL)
+{
+ const rtl::OString aLang = MsLangId::convertLanguageToIsoByteString( sfont.GetFontSelData().meLanguage );
+ rtl::OString name = rtl::OUStringToOString(
+ sfont.GetFontSelData().maTargetName, RTL_TEXTENCODING_UTF8 );
+#ifdef DEBUG
+ printf("GraphiteFontAdaptor %lx %s italic=%u bold=%u\n", (long)this, name.getStr(),
+ maFontProperties.fItalic, maFontProperties.fBold);
+#endif
+ sal_Int32 nFeat = name.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + 1;
+ if (nFeat > 0)
+ {
+ rtl::OString aFeat = name.copy(nFeat, name.getLength() - nFeat);
+ mpFeatures = new grutils::GrFeatureParser(*this, aFeat.getStr(), aLang.getStr());
+#ifdef DEBUG
+ printf("GraphiteFontAdaptor %s/%s/%s %x language %d features %d errors\n",
+ rtl::OUStringToOString( sfont.GetFontSelData().maName,
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ rtl::OUStringToOString( sfont.GetFontSelData().maTargetName,
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ rtl::OUStringToOString( sfont.GetFontSelData().maSearchName,
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ sfont.GetFontSelData().meLanguage,
+ (int)mpFeatures->getFontFeatures(NULL), mpFeatures->parseErrors());
+#endif
+ }
+ else
+ {
+ mpFeatures = new grutils::GrFeatureParser(*this, aLang.getStr());
+ }
+}
+
+GraphiteFontAdaptor::GraphiteFontAdaptor(const GraphiteFontAdaptor &rhs) throw()
+ : Font(rhs),
+ mrFont (rhs.mrFont), maFontProperties(rhs.maFontProperties),
+ mnDpiX(rhs.mnDpiX), mnDpiY(rhs.mnDpiY),
+ mfAscent(rhs.mfAscent), mfDescent(rhs.mfDescent), mfEmUnits(rhs.mfEmUnits),
+ mpFeatures(NULL)
+{
+ if (rhs.mpFeatures) mpFeatures = new grutils::GrFeatureParser(*(rhs.mpFeatures));
+}
+
+
+GraphiteFontAdaptor::~GraphiteFontAdaptor() throw()
+{
+ maGlyphMetricMap.clear();
+ if (mpFeatures) delete mpFeatures;
+ mpFeatures = NULL;
+}
+
+void GraphiteFontAdaptor::UniqueCacheInfo(ext_std::wstring & face_name_out, bool & bold_out, bool & italic_out)
+{
+ face_name_out = maFontProperties.szFaceName;
+ bold_out = maFontProperties.fBold;
+ italic_out = maFontProperties.fItalic;
+}
+
+bool GraphiteFontAdaptor::IsGraphiteEnabledFont(ServerFont & font) throw()
+{
+ static SilfMap sSilfMap;
+ // NOTE: this assumes that the same FTFace pointer won't be reused,
+ // so FtFontInfo::ReleaseFaceFT must only be called at shutdown.
+ FreetypeServerFont & aFtFont = dynamic_cast<FreetypeServerFont &>(font);
+ FT_Face aFace = reinterpret_cast<FT_FaceRec_*>(aFtFont.GetFtFace());
+ SilfMap::iterator i = sSilfMap.find(reinterpret_cast<long>(aFace));
+ if (i != sSilfMap.end())
+ {
+#ifdef DEBUG
+ if (static_cast<bool>(aFtFont.GetTable("Silf", 0)) != (*i).second)
+ printf("Silf cache font mismatch\n");
+#endif
+ return (*i).second;
+ }
+ bool bHasSilf = aFtFont.GetTable("Silf", 0);
+ sSilfMap[reinterpret_cast<long>(aFace)] = bHasSilf;
+ return bHasSilf;
+}
+
+
+gr::Font * GraphiteFontAdaptor::copyThis() {
+ return new GraphiteFontAdaptor(*this);
+}
+
+
+unsigned int GraphiteFontAdaptor::getDPIx() {
+ return mnDpiX;
+}
+
+
+unsigned int GraphiteFontAdaptor::getDPIy() {
+ return mnDpiY;
+}
+
+
+float GraphiteFontAdaptor::ascent() {
+ return mfAscent;
+}
+
+
+float GraphiteFontAdaptor::descent() {
+ return mfDescent;
+}
+
+
+bool GraphiteFontAdaptor::bold() {
+ return maFontProperties.fBold;
+}
+
+
+bool GraphiteFontAdaptor::italic() {
+ return maFontProperties.fItalic;
+}
+
+
+float GraphiteFontAdaptor::height() {
+ return maFontProperties.pixHeight;
+}
+
+
+void GraphiteFontAdaptor::getFontMetrics(float * ascent_out, float * descent_out, float * em_square_out) {
+ if (ascent_out) *ascent_out = mfAscent;
+ if (descent_out) *descent_out = mfDescent;
+ if (em_square_out) *em_square_out = mfEmUnits;
+}
+
+
+const void * GraphiteFontAdaptor::getTable(gr::fontTableId32 table_id, size_t * buffer_sz)
+{
+ char tag_name[5] = {char(table_id >> 24), char(table_id >> 16), char(table_id >> 8), char(table_id), 0};
+ ULONG temp = *buffer_sz;
+
+ const void * const tbl_buf = static_cast<FreetypeServerFont &>(mrFont).GetTable(tag_name, &temp);
+ *buffer_sz = temp;
+
+ return tbl_buf;
+}
+
+#define fix26_6(x) (x >> 6) + (x & 32 ? (x > 0 ? 1 : 0) : (x < 0 ? -1 : 0))
+
+// Return the glyph's metrics in pixels.
+void GraphiteFontAdaptor::getGlyphMetrics(gr::gid16 nGlyphId, gr::Rect & aBounding, gr::Point & advances)
+{
+ // There used to be problems when orientation was set however, this no
+ // longer seems to be the case and the Glyph Metric cache in
+ // FreetypeServerFont is more efficient since it lasts between calls to VCL
+#if 1
+ const GlyphMetric & metric = mrFont.GetGlyphMetric(nGlyphId);
+
+ aBounding.right = aBounding.left = metric.GetOffset().X();
+ aBounding.bottom = aBounding.top = -metric.GetOffset().Y();
+ aBounding.right += metric.GetSize().Width();
+ aBounding.bottom -= metric.GetSize().Height();
+
+ advances.x = metric.GetDelta().X();
+ advances.y = -metric.GetDelta().Y();
+
+#else
+ // The problem with the code below is that the cache only lasts
+ // as long as the life time of the GraphiteFontAdaptor, which
+ // is created once per call to X11SalGraphics::GetTextLayout
+ GlyphMetricMap::const_iterator gm_itr = maGlyphMetricMap.find(nGlyphId);
+ if (gm_itr != maGlyphMetricMap.end())
+ {
+ // We've cached the results from last time.
+ aBounding = gm_itr->second.first;
+ advances = gm_itr->second.second;
+ }
+ else
+ {
+ // We need to look up the glyph.
+ FT_Int nLoadFlags = mrFont.GetLoadFlags();
+
+ FT_Face aFace = reinterpret_cast<FT_Face>(mrFont.GetFtFace());
+ if (!aFace)
+ {
+ aBounding.top = aBounding.bottom = aBounding.left = aBounding.right = 0;
+ advances.x = advances.y = 0;
+ return;
+ }
+ FT_Error aStatus = -1;
+ aStatus = FT_Load_Glyph(aFace, nGlyphId, nLoadFlags);
+ if( aStatus != FT_Err_Ok || (!aFace->glyph))
+ {
+ aBounding.top = aBounding.bottom = aBounding.left = aBounding.right = 0;
+ advances.x = advances.y = 0;
+ return;
+ }
+ // check whether we need synthetic bold/italic otherwise metric is wrong
+ if (mrFont.NeedsArtificialBold() && pFTEmbolden)
+ (*pFTEmbolden)(aFace->glyph);
+
+ if (mrFont.NeedsArtificialItalic() && pFTOblique)
+ (*pFTOblique)(aFace->glyph);
+
+ const FT_Glyph_Metrics &gm = aFace->glyph->metrics;
+
+ // Fill out the bounding box an advances.
+ aBounding.top = aBounding.bottom = fix26_6(gm.horiBearingY);
+ aBounding.bottom -= fix26_6(gm.height);
+ aBounding.left = aBounding.right = fix26_6(gm.horiBearingX);
+ aBounding.right += fix26_6(gm.width);
+ advances.x = fix26_6(gm.horiAdvance);
+ advances.y = 0;
+
+ // Now add an entry to our metrics map.
+ maGlyphMetricMap[nGlyphId] = std::make_pair(aBounding, advances);
+ }
+#endif
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/glyphs/graphite_cache.cxx b/vcl/source/glyphs/graphite_cache.cxx
new file mode 100644
index 000000000000..51c898b1a951
--- /dev/null
+++ b/vcl/source/glyphs/graphite_cache.cxx
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#ifdef WNT
+#include <tools/svwin.h>
+#include <svsys.h>
+#endif
+
+#include <tools/debug.hxx>
+#include <vcl/sallayout.hxx>
+
+#include <preextstl.h>
+#include <graphite/GrClient.h>
+#include <graphite/Segment.h>
+#include <postextstl.h>
+
+#include <rtl/ustring.hxx>
+#include <vcl/graphite_layout.hxx>
+#include <vcl/graphite_cache.hxx>
+
+#include "graphite_textsrc.hxx"
+
+GrSegRecord::GrSegRecord(rtl::OUString * rope, TextSourceAdaptor * textSrc, gr::Segment * seg, bool bIsRtl)
+ : m_rope(rope), m_text(textSrc), m_seg(seg), m_nextKey(NULL),
+ m_fontScale(0.0f), mbIsRtl(bIsRtl), m_lockCount(0)
+{
+ m_pStr = textSrc->getLayoutArgs().mpStr + seg->startCharacter();
+ m_startChar = seg->startCharacter();
+}
+
+GrSegRecord::~GrSegRecord()
+{
+ clear();
+}
+
+void GrSegRecord::reuse(rtl::OUString * rope, TextSourceAdaptor * textSrc, gr::Segment * seg, bool bIsRtl)
+{
+ clear();
+ mnWidth = 0;
+ m_rope = rope;
+ m_text = textSrc;
+ m_seg = seg;
+ m_nextKey = NULL;
+ m_pStr = textSrc->getLayoutArgs().mpStr + seg->startCharacter();
+ m_startChar = seg->startCharacter();
+ mbIsRtl = bIsRtl;
+}
+
+void GrSegRecord::clearVectors()
+{
+ mvGlyphs.clear();
+ mvCharDxs.clear();
+ mvChar2BaseGlyph.clear();
+ mvGlyph2Char.clear();
+}
+
+void GrSegRecord::clear()
+{
+#ifdef GR_DEBUG_TEXT
+ if (m_lockCount != 0)
+ OutputDebugString("GrSegRecord locked!");
+#endif
+ clearVectors();
+ delete m_rope;
+ delete m_seg;
+ delete m_text;
+ m_rope = NULL;
+ m_seg = NULL;
+ m_text = NULL;
+ m_fontScale = 0.0f;
+ m_lockCount = 0;
+}
+
+GrSegRecord * GraphiteSegmentCache::cacheSegment(TextSourceAdaptor * adapter, gr::Segment * seg, bool bIsRtl)
+{
+ GrSegRecord * record = NULL;
+ // We keep a record of the oldest key and the last key added
+ // when the next key is added, the record for the prevKey's m_nextKey field
+ // is updated to the newest key so that m_oldestKey can be updated to the
+ // next oldest key when the record for m_oldestKey is deleted
+ if (m_segMap.size() > m_nSegCacheSize)
+ {
+ GraphiteSegMap::iterator oldestPair = m_segMap.find(reinterpret_cast<long>(m_oldestKey));
+ // oldest record may no longer exist if a buffer was changed
+ if (oldestPair != m_segMap.end())
+ {
+ record = oldestPair->second;
+ m_segMap.erase(reinterpret_cast<long>(m_oldestKey));
+ GrRMEntry range = m_ropeMap.equal_range((*(record->m_rope)).hashCode());
+ while (range.first != range.second)
+ {
+ if (range.first->second == record)
+ {
+ m_ropeMap.erase(range.first);
+ break;
+ }
+ ++range.first;
+ }
+ m_oldestKey = record->m_nextKey;
+ // record will be reused, so don't delete
+ }
+ }
+
+
+// const int seg_char_limit = min(adapter->maLayoutArgs().mnLength,
+// adapter->maLayoutArgs().mnEndCharPos
+// + GraphiteLayout::EXTRA_CONTEXT_LENGTH);
+// if (seg->stopCharacter() - seg->startCharacter() <= 0)
+// OutputDebugString("Invalid seg indices\n");
+ rtl::OUString * pRope = new rtl::OUString(adapter->getLayoutArgs().mpStr + seg->startCharacter(),
+ seg->stopCharacter() - seg->startCharacter());
+ if (!pRope) return NULL;
+ bool reuse = false;
+ if (record)
+ record->reuse(pRope, adapter, seg, bIsRtl);
+ else
+ record = new GrSegRecord(pRope, adapter, seg, bIsRtl);
+ if (!record)
+ {
+ delete pRope;
+ return NULL;
+ }
+ GraphiteSegMap::iterator iMap =
+ m_segMap.find(reinterpret_cast<long>(record->m_pStr));
+ if (iMap != m_segMap.end())
+ {
+ // the buffer has changed, so the old cached Segment is useless
+ reuse = true;
+ GrSegRecord * found = iMap->second;
+ // Note: we reuse the old next key to avoid breaking our history
+ // chain. This means it will be prematurely deleted, but this is
+ // unlikely to happen very often.
+ record->m_nextKey = found->m_nextKey;
+ // overwrite the old record
+ m_segMap[reinterpret_cast<long>(record->m_pStr)] = record;
+ // erase the old rope key and save the new one
+ GrRMEntry range = m_ropeMap.equal_range((*(found->m_rope)).hashCode());
+ while (range.first != range.second)
+ {
+ if (range.first->second == found)
+ {
+ m_ropeMap.erase(range.first);
+ break;
+ }
+ ++range.first;
+ }
+ GraphiteRopeMap::value_type mapEntry(record->m_rope->hashCode(), record);
+ m_ropeMap.insert(mapEntry);
+ // remove the old record
+ delete found;
+ record->m_lockCount++;
+ return record;
+ }
+ m_segMap[reinterpret_cast<long>(record->m_pStr)] = record;
+ GraphiteRopeMap::value_type mapEntry((*(record->m_rope)).hashCode(), record);
+ m_ropeMap.insert(mapEntry);
+
+ if (m_oldestKey == NULL)
+ {
+ m_oldestKey = record->m_pStr;
+ m_prevKey = record->m_pStr;
+ }
+ else if (reuse == false)
+ {
+ DBG_ASSERT(m_segMap.count(reinterpret_cast<long>(m_prevKey)),
+ "Previous key got lost somehow!");
+ m_segMap.find(reinterpret_cast<long>(m_prevKey))
+ ->second->m_nextKey = record->m_pStr;
+ m_prevKey = record->m_pStr;
+ }
+ record->m_lockCount++;
+ return record;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/glyphs/graphite_features.cxx b/vcl/source/glyphs/graphite_features.cxx
new file mode 100644
index 000000000000..23325f963b37
--- /dev/null
+++ b/vcl/source/glyphs/graphite_features.cxx
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// Description:
+// Parse a string of features specified as & separated pairs.
+// e.g.
+// 1001=1&2002=2&fav1=0
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include <sal/types.h>
+
+#ifdef WNT
+#include <tools/svwin.h>
+#include <svsys.h>
+#endif
+
+#include <vcl/graphite_features.hxx>
+
+using namespace grutils;
+// These mustn't conflict with font name lists which use ; and ,
+const char GrFeatureParser::FEAT_PREFIX = ':';
+const char GrFeatureParser::FEAT_SEPARATOR = '&';
+const char GrFeatureParser::FEAT_ID_VALUE_SEPARATOR = '=';
+
+GrFeatureParser::GrFeatureParser(gr::Font & font, const std::string lang)
+ : mnNumSettings(0), mbErrors(false)
+{
+ maLang.rgch[0] = maLang.rgch[1] = maLang.rgch[2] = maLang.rgch[3] = '\0';
+ setLang(font, lang);
+}
+
+GrFeatureParser::GrFeatureParser(gr::Font & font, const std::string features, const std::string lang)
+ : mnNumSettings(0), mbErrors(false)
+{
+ size_t nEquals = 0;
+ size_t nFeatEnd = 0;
+ size_t pos = 0;
+ maLang.rgch[0] = maLang.rgch[1] = maLang.rgch[2] = maLang.rgch[3] = '\0';
+ setLang(font, lang);
+ while (pos < features.length() && mnNumSettings < MAX_FEATURES)
+ {
+ nEquals = features.find(FEAT_ID_VALUE_SEPARATOR,pos);
+ if (nEquals == std::string::npos)
+ {
+ mbErrors = true;
+ break;
+ }
+ // check for a lang=xxx specification
+ if (features.compare(pos, nEquals - pos, "lang") == 0)
+ {
+ pos = nEquals + 1;
+ nFeatEnd = features.find(FEAT_SEPARATOR, pos);
+ if (nFeatEnd == std::string::npos)
+ {
+ nFeatEnd = features.length();
+ }
+ if (nFeatEnd - pos > 3)
+ mbErrors = true;
+ else
+ {
+ gr::isocode aLang = maLang;
+ for (size_t i = pos; i < nFeatEnd; i++)
+ aLang.rgch[i-pos] = features[i];
+ ext_std::pair<gr::LanguageIterator,gr::LanguageIterator> aSupported
+ = font.getSupportedLanguages();
+ gr::LanguageIterator iL = aSupported.first;
+ while (iL != aSupported.second)
+ {
+ gr::isocode aSupportedLang = *iL;
+ // here we only expect full 3 letter codes
+ if (aLang.rgch[0] == aSupportedLang.rgch[0] &&
+ aLang.rgch[1] == aSupportedLang.rgch[1] &&
+ aLang.rgch[2] == aSupportedLang.rgch[2] &&
+ aLang.rgch[3] == aSupportedLang.rgch[3]) break;
+ ++iL;
+ }
+ if (iL == aSupported.second) mbErrors = true;
+ else maLang = aLang;
+ }
+ }
+ else
+ {
+ if (isCharId(features, pos, nEquals - pos))
+ maSettings[mnNumSettings].id = getCharId(features, pos, nEquals - pos);
+ else maSettings[mnNumSettings].id = getIntValue(features, pos, nEquals - pos);
+ pos = nEquals + 1;
+ nFeatEnd = features.find(FEAT_SEPARATOR, pos);
+ if (nFeatEnd == std::string::npos)
+ {
+ nFeatEnd = features.length();
+ }
+ if (isCharId(features, pos, nFeatEnd - pos))
+ maSettings[mnNumSettings].value = getCharId(features, pos, nFeatEnd - pos);
+ else
+ maSettings[mnNumSettings].value= getIntValue(features, pos, nFeatEnd - pos);
+ if (isValid(font, maSettings[mnNumSettings]))
+ mnNumSettings++;
+ else
+ mbErrors = true;
+ }
+ pos = nFeatEnd + 1;
+ }
+}
+
+void GrFeatureParser::setLang(gr::Font & font, const std::string & lang)
+{
+ gr::isocode aLang = {{0,0,0,0}};
+ if (lang.length() > 2)
+ {
+ for (size_t i = 0; i < lang.length() && i < 3; i++)
+ {
+ if (lang[i] == '-') break;
+ aLang.rgch[i] = lang[i];
+ }
+ ext_std::pair<gr::LanguageIterator,gr::LanguageIterator> aSupported
+ = font.getSupportedLanguages();
+ gr::LanguageIterator iL = aSupported.first;
+ while (iL != aSupported.second)
+ {
+ gr::isocode aSupportedLang = *iL;
+ if (aLang.rgch[0] == aSupportedLang.rgch[0] &&
+ aLang.rgch[1] == aSupportedLang.rgch[1] &&
+ aLang.rgch[2] == aSupportedLang.rgch[2] &&
+ aLang.rgch[3] == aSupportedLang.rgch[3]) break;
+ ++iL;
+ }
+ if (iL != aSupported.second)
+ maLang = aLang;
+#ifdef DEBUG
+ else
+ printf("%s has no features\n", aLang.rgch);
+#endif
+ }
+}
+
+GrFeatureParser::GrFeatureParser(const GrFeatureParser & aCopy)
+ : maLang(aCopy.maLang), mbErrors(aCopy.mbErrors)
+{
+ mnNumSettings = aCopy.getFontFeatures(maSettings);
+}
+
+GrFeatureParser::~GrFeatureParser()
+{
+}
+
+size_t GrFeatureParser::getFontFeatures(gr::FeatureSetting settings[64]) const
+{
+ if (settings)
+ {
+ std::copy(maSettings, maSettings + mnNumSettings, settings);
+ }
+ return mnNumSettings;
+}
+
+bool GrFeatureParser::isValid(gr::Font & font, gr::FeatureSetting & setting)
+{
+ gr::FeatureIterator i = font.featureWithID(setting.id);
+ if (font.getFeatures().second == i)
+ {
+ return false;
+ }
+ ext_std::pair< gr::FeatureSettingIterator, gr::FeatureSettingIterator >
+ validValues = font.getFeatureSettings(i);
+ gr::FeatureSettingIterator j = validValues.first;
+ while (j != validValues.second)
+ {
+ if (*j == setting.value) return true;
+ ++j;
+ }
+ return false;
+}
+
+bool GrFeatureParser::isCharId(const std::string & id, size_t offset, size_t length)
+{
+ if (length > 4) return false;
+ for (size_t i = 0; i < length; i++)
+ {
+ if (i > 0 && id[offset+i] == '\0') continue;
+ if ((id[offset+i]) < 0x20 || (id[offset+i]) < 0)
+ return false;
+ if (i==0 && id[offset+i] < 0x41)
+ return false;
+ }
+ return true;
+}
+
+int GrFeatureParser::getCharId(const std::string & id, size_t offset, size_t length)
+{
+ FeatId charId;
+ charId.num = 0;
+#ifdef WORDS_BIGENDIAN
+ for (size_t i = 0; i < length; i++)
+ {
+ charId.label[i] = id[offset+i];
+ }
+#else
+ for (size_t i = 0; i < length; i++)
+ {
+ charId.label[3-i] = id[offset+i];
+ }
+#endif
+ return charId.num;
+}
+
+int GrFeatureParser::getIntValue(const std::string & id, size_t offset, size_t length)
+{
+ int value = 0;
+ int sign = 1;
+ for (size_t i = 0; i < length; i++)
+ {
+ switch (id[offset + i])
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ value *= 10;
+ if (sign < 0)
+ {
+ value = -(id[offset + i] - '0');
+ sign = 1;
+ }
+ value += (id[offset + i] - '0');
+ break;
+ case '-':
+ if (i == 0)
+ sign = -1;
+ else
+ {
+ mbErrors = true;
+ break;
+ }
+ default:
+ mbErrors = true;
+ break;
+ }
+ }
+ return value;
+}
+
+
+sal_Int32 GrFeatureParser::hashCode() const
+{
+ union IsoHash { sal_Int32 mInt; gr::isocode mCode; };
+ IsoHash isoHash;
+ isoHash.mCode = maLang;
+ sal_Int32 hash = isoHash.mInt;
+ for (size_t i = 0; i < mnNumSettings; i++)
+ {
+ hash = (hash << 16) ^ ((maSettings[i].id << 8) | maSettings[i].value);
+ }
+ return hash;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/glyphs/graphite_layout.cxx b/vcl/source/glyphs/graphite_layout.cxx
new file mode 100644
index 000000000000..2e4812ba5940
--- /dev/null
+++ b/vcl/source/glyphs/graphite_layout.cxx
@@ -0,0 +1,1562 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// Description: An implementation of the SalLayout interface that uses the
+// Graphite engine.
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+// We need this to enable namespace support in libgrengine headers.
+#define GR_NAMESPACE
+
+// Enable lots of debug info
+#ifdef DEBUG
+//#define GRLAYOUT_DEBUG 1
+//#undef NDEBUG
+#endif
+
+// Header files
+//
+// Standard Library
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <limits>
+#include <numeric>
+#include <deque>
+
+// Platform
+#ifdef WNT
+#include <tools/svwin.h>
+#include <svsys.h>
+#endif
+
+#ifdef UNX
+#include <vcl/graphite_adaptors.hxx>
+#endif
+
+#include <vcl/salgdi.hxx>
+
+#include <unicode/uchar.h>
+#include <unicode/ubidi.h>
+#include <unicode/uscript.h>
+
+// Graphite Libraries (must be after vcl headers on windows)
+#include <preextstl.h>
+#include <graphite/GrClient.h>
+#include <graphite/Font.h>
+#include <graphite/ITextSource.h>
+#include <graphite/Segment.h>
+#include <graphite/SegmentPainter.h>
+#include <postextstl.h>
+
+#include <vcl/graphite_layout.hxx>
+#include <vcl/graphite_features.hxx>
+#include "graphite_textsrc.hxx"
+
+
+// Module private type definitions and forward declarations.
+//
+// Module private names.
+//
+
+#ifdef GRLAYOUT_DEBUG
+FILE * grLogFile = NULL;
+FILE * grLog()
+{
+#ifdef WNT
+ std::string logFileName(getenv("TEMP"));
+ logFileName.append("\\graphitelayout.log");
+ if (grLogFile == NULL) grLogFile = fopen(logFileName.c_str(),"w");
+ else fflush(grLogFile);
+ return grLogFile;
+#else
+ return stdout;
+#endif
+}
+#endif
+
+#ifdef GRCACHE
+#include <vcl/graphite_cache.hxx>
+#endif
+
+
+namespace
+{
+ typedef ext_std::pair<gr::GlyphIterator, gr::GlyphIterator> glyph_range_t;
+ typedef ext_std::pair<gr::GlyphSetIterator, gr::GlyphSetIterator> glyph_set_range_t;
+
+ inline long round(const float n) {
+ return long(n + (n < 0 ? -0.5 : 0.5));
+ }
+
+
+ template<typename T>
+ inline bool in_range(const T i, const T b, const T e) {
+ return !(b > i) && i < e;
+ }
+
+
+ template<typename T>
+ inline bool is_subrange(const T sb, const T se, const T b, const T e) {
+ return !(b > sb || se > e);
+ }
+
+
+ template<typename T>
+ inline bool is_subrange(const std::pair<T, T> &s, const T b, const T e) {
+ return is_subrange(s.first, s.second, b, e);
+ }
+
+ int findSameDirLimit(const xub_Unicode* buffer, int charCount, bool rtl)
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UBiDi *ubidi = ubidi_openSized(charCount, 0, &status);
+ int limit = 0;
+ ubidi_setPara(ubidi, reinterpret_cast<const UChar *>(buffer), charCount,
+ (rtl)?UBIDI_DEFAULT_RTL:UBIDI_DEFAULT_LTR, NULL, &status);
+ UBiDiLevel level = 0;
+ ubidi_getLogicalRun(ubidi, 0, &limit, &level);
+ ubidi_close(ubidi);
+ if ((rtl && !(level & 1)) || (!rtl && (level & 1)))
+ {
+ limit = 0;
+ }
+ return limit;
+ }
+
+} // namespace
+
+
+
+// Impementation of the GraphiteLayout::Glyphs container class.
+// This is an extended vector class with methods added to enable
+// o Correctly filling with glyphs.
+// o Querying clustering relationships.
+// o manipulations that affect neighouring glyphs.
+
+const int GraphiteLayout::EXTRA_CONTEXT_LENGTH = 10;
+#ifdef GRCACHE
+GraphiteCacheHandler GraphiteCacheHandler::instance;
+#endif
+
+// The Graphite glyph stream is really a sequence of glyph attachment trees
+// each rooted at a non-attached base glyph. fill_from walks the glyph stream
+// find each non-attached base glyph and calls append to record them as a
+// sequence of clusters.
+void
+GraphiteLayout::Glyphs::fill_from(gr::Segment & rSegment, ImplLayoutArgs &rArgs,
+ bool bRtl, long &rWidth, float fScaling, std::vector<int> & rChar2Base, std::vector<int> & rGlyph2Char, std::vector<int> & rCharDxs)
+{
+ // Create a glyph item for each of the glyph and append it to the base class glyph list.
+ typedef ext_std::pair< gr::GlyphSetIterator, gr::GlyphSetIterator > GrGlyphSet;
+ int nChar = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
+ glyph_range_t iGlyphs = rSegment.glyphs();
+ int nGlyphs = iGlyphs.second - iGlyphs.first;
+ float fSegmentAdvance = rSegment.advanceWidth();
+ float fMinX = fSegmentAdvance;
+ float fMaxX = 0.0f;
+ rGlyph2Char.assign(nGlyphs, -1);
+ long nDxOffset = 0;
+ int nGlyphIndex = (bRtl)? (nGlyphs - 1) : 0;
+ // OOo always expects the glyphs in ltr order
+ int nDelta = (bRtl)? -1 : 1;
+
+ int nLastGlyph = (bRtl)? nGlyphs - 1: 0;
+ int nNextChar = (bRtl)? (rSegment.stopCharacter() - 1) : rSegment.startCharacter();//rArgs.mnMinCharPos;
+ // current glyph number (Graphite glyphs)
+ //int currGlyph = 0;
+ int nFirstCharInCluster = nNextChar;
+ int nFirstGlyphInCluster = nLastGlyph;
+
+ // ltr first char in cluster is lowest, same is true for rtl
+ // ltr first glyph in cluster is lowest, rtl first glyph is highest
+
+ // loop over the glyphs determining which characters are linked to them
+ gr::GlyphIterator gi;
+ for (gi = iGlyphs.first + nGlyphIndex;
+ nGlyphIndex >= 0 && nGlyphIndex < nGlyphs;
+ nGlyphIndex+= nDelta, gi = iGlyphs.first + nGlyphIndex)
+ {
+ gr::GlyphInfo info = (*gi);
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"Glyph %d %f,%f\n", (int)info.logicalIndex(), info.origin(), info.yOffset());
+#endif
+ // the last character associated with this glyph is after
+ // our current cluster buffer position
+ if ((bRtl && ((signed)info.firstChar() <= nNextChar)) ||
+ (!bRtl && ((signed)info.lastChar() >= nNextChar)))
+ {
+ if ((bRtl && nGlyphIndex < nLastGlyph) ||
+ (!bRtl && nGlyphIndex > nLastGlyph))
+ {
+ // this glyph is after the previous one left->right
+ // if insertion is allowed before it then we are in a
+ // new cluster
+ int nAttachedBase = (*(info.attachedClusterBase())).logicalIndex();
+ if (!info.isAttached() ||
+ !in_range(nAttachedBase, nFirstGlyphInCluster, nGlyphIndex))
+ {
+ if (in_range(nFirstCharInCluster, rArgs.mnMinCharPos, rArgs.mnEndCharPos) &&
+ nFirstGlyphInCluster != nGlyphIndex)
+ {
+ std::pair <float,float> aBounds =
+ appendCluster(rSegment, rArgs, bRtl,
+ fSegmentAdvance, nFirstCharInCluster,
+ nNextChar, nFirstGlyphInCluster, nGlyphIndex, fScaling,
+ rChar2Base, rGlyph2Char, rCharDxs, nDxOffset);
+ fMinX = std::min(aBounds.first, fMinX);
+ fMaxX = std::max(aBounds.second, fMaxX);
+ }
+ nFirstCharInCluster = (bRtl)? info.lastChar() : info.firstChar();
+ nFirstGlyphInCluster = nGlyphIndex;
+ }
+ nLastGlyph = (bRtl)? std::min(nGlyphIndex, nAttachedBase) :
+ std::max(nGlyphIndex, nAttachedBase);
+ }
+ // loop over chacters associated with this glyph and characters
+ // between nextChar and the last character associated with this glyph
+ // giving them the current cluster id. This allows for character /glyph
+ // order reversal.
+ // For each character we do a reverse glyph id look up
+ // and store the glyph id with the highest logical index in nLastGlyph
+ while ((bRtl && ((signed)info.firstChar() <= nNextChar)) ||
+ (!bRtl && (signed)info.lastChar() >= nNextChar))
+ {
+ GrGlyphSet charGlyphs = rSegment.charToGlyphs(nNextChar);
+ nNextChar += nDelta;
+ gr::GlyphSetIterator gj = charGlyphs.first;
+ while (gj != charGlyphs.second)
+ {
+ nLastGlyph = (bRtl)? min(nLastGlyph, (signed)(*gj).logicalIndex()) : max(nLastGlyph, (signed)(*gj).logicalIndex());
+ ++gj;
+ }
+ }
+ // Loop over attached glyphs and make sure they are all in the cluster since you
+ // can have glyphs attached with another base glyph in between
+ glyph_set_range_t iAttached = info.attachedClusterGlyphs();
+ for (gr::GlyphSetIterator agi = iAttached.first; agi != iAttached.second; ++agi)
+ {
+ nLastGlyph = (bRtl)? min(nLastGlyph, (signed)(*agi).logicalIndex()) : max(nLastGlyph, (signed)(*agi).logicalIndex());
+ }
+
+ // if this is a rtl attached glyph, then we need to include its
+ // base in the cluster, which will have a lower graphite index
+ if (bRtl)
+ {
+ if ((signed)info.attachedClusterBase()->logicalIndex() < nLastGlyph)
+ {
+ nLastGlyph = info.attachedClusterBase()->logicalIndex();
+ }
+ }
+ }
+
+ // it is possible for the lastChar to be after nextChar and
+ // firstChar to be before the nFirstCharInCluster in rare
+ // circumstances e.g. Myanmar word for cemetery
+ if ((bRtl && ((signed)info.lastChar() > nFirstCharInCluster)) ||
+ (!bRtl && ((signed)info.firstChar() < nFirstCharInCluster)))
+ {
+ nFirstCharInCluster = info.firstChar();
+ }
+ }
+ // process last cluster
+ if (in_range(nFirstCharInCluster, rArgs.mnMinCharPos, rArgs.mnEndCharPos) &&
+ nFirstGlyphInCluster != nGlyphIndex)
+ {
+ std::pair <float,float> aBounds =
+ appendCluster(rSegment, rArgs, bRtl, fSegmentAdvance,
+ nFirstCharInCluster, nNextChar,
+ nFirstGlyphInCluster, nGlyphIndex, fScaling,
+ rChar2Base, rGlyph2Char, rCharDxs, nDxOffset);
+ fMinX = std::min(aBounds.first, fMinX);
+ fMaxX = std::max(aBounds.second, fMaxX);
+ }
+ long nXOffset = round(fMinX * fScaling);
+ rWidth = round(fMaxX * fScaling) - nXOffset + nDxOffset;
+ if (rWidth < 0)
+ {
+ // This can happen when there was no base inside the range
+ rWidth = 0;
+ }
+ // fill up non-base char dx with cluster widths from previous base glyph
+ if (bRtl)
+ {
+ if (rCharDxs[nChar-1] == -1)
+ rCharDxs[nChar-1] = 0;
+ else
+ rCharDxs[nChar-1] -= nXOffset;
+ for (int i = nChar - 2; i >= 0; i--)
+ {
+ if (rCharDxs[i] == -1) rCharDxs[i] = rCharDxs[i+1];
+ else rCharDxs[i] -= nXOffset;
+ }
+ }
+ else
+ {
+ if (rCharDxs[0] == -1)
+ rCharDxs[0] = 0;
+ else
+ rCharDxs[0] -= nXOffset;
+ for (int i = 1; i < nChar; i++)
+ {
+ if (rCharDxs[i] == -1) rCharDxs[i] = rCharDxs[i-1];
+ else rCharDxs[i] -= nXOffset;
+ }
+ }
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"Glyphs xOff%ld dropDx%ld w%ld\n", nXOffset, nDxOffset, rWidth);
+#endif
+ // remove offset due to context if there is one
+ if (nXOffset != 0)
+ {
+ for (size_t i = 0; i < size(); i++)
+ (*this)[i].maLinearPos.X() -= nXOffset;
+ }
+}
+
+std::pair<float,float> GraphiteLayout::Glyphs::appendCluster(gr::Segment& rSeg,
+ ImplLayoutArgs & rArgs, bool bRtl,float fSegmentAdvance,
+ int nFirstCharInCluster, int nNextChar, int nFirstGlyphInCluster,
+ int nNextGlyph, float fScaling, std::vector<int> & rChar2Base,
+ std::vector<int> & rGlyph2Char, std::vector<int> & rCharDxs, long & rDXOffset)
+{
+ glyph_range_t iGlyphs = rSeg.glyphs();
+ int nGlyphs = iGlyphs.second - iGlyphs.first;
+ int nDelta = (bRtl)? -1 : 1;
+ gr::GlyphInfo aFirstGlyph = *(iGlyphs.first + nFirstGlyphInCluster);
+ std::pair <float, float> aBounds;
+ aBounds.first = aFirstGlyph.origin();
+ aBounds.second = aFirstGlyph.origin();
+ // before we add the glyphs to this vector, we record the
+ // glyph's index in the vector (which is not the same as
+ // the Segment's glyph index!)
+ assert(size() < rGlyph2Char.size());
+ rChar2Base[nFirstCharInCluster-rArgs.mnMinCharPos] = size();
+ rGlyph2Char[size()] = nFirstCharInCluster;
+
+ // can we break before this cluster?
+ // Glyphs may have either a positive or negative breakWeight refering to
+ // the position after or before the glyph respectively
+ int nPrevBreakWeight = 0;
+ if (nFirstGlyphInCluster > 0)
+ {
+ nPrevBreakWeight = (iGlyphs.first + (nFirstGlyphInCluster - 1))->breakweight();
+ }
+ int nBreakWeight = aFirstGlyph.breakweight();
+ if (nBreakWeight < 0)
+ {
+ // negative means it applies to the position before the glyph's character
+ nBreakWeight *= -1;
+ if (nPrevBreakWeight > 0 && nPrevBreakWeight < nBreakWeight)
+ {
+ // prevBreakWeight wins
+ nBreakWeight = nPrevBreakWeight;
+ }
+ }
+ else
+ {
+ nBreakWeight = 0;
+ // positive means break after
+ if (nPrevBreakWeight > 0)
+ nBreakWeight = nPrevBreakWeight;
+ }
+ if (nBreakWeight > gr::klbNoBreak/*0*/ &&
+ // nBreakWeight <= gr::klbHyphenBreak) // uses Graphite hyphenation
+ nBreakWeight <= gr::klbLetterBreak) // Needed for issue 111272
+ {
+ if (nBreakWeight < gr::klbHyphenBreak)
+ rChar2Base[nFirstCharInCluster-rArgs.mnMinCharPos] |= WORD_BREAK_BEFORE;
+ else
+ rChar2Base[nFirstCharInCluster-rArgs.mnMinCharPos] |= HYPHEN_BREAK_BEFORE;
+ }
+ // always allow a break before a space even if graphite doesn't
+ if (rArgs.mpStr[nFirstCharInCluster] == 0x20)
+ rChar2Base[nFirstCharInCluster-rArgs.mnMinCharPos] |= WORD_BREAK_BEFORE;
+
+ bool bBaseGlyph = true;
+ for (int j = nFirstGlyphInCluster;
+ j != nNextGlyph; j += nDelta)
+ {
+ long nNextOrigin;
+ float fNextOrigin;
+ gr::GlyphInfo aGlyph = *(iGlyphs.first + j);
+ if (j + nDelta >= nGlyphs || j + nDelta < 0) // at rhs ltr,rtl
+ {
+ fNextOrigin = fSegmentAdvance;
+ nNextOrigin = round(fSegmentAdvance * fScaling + rDXOffset);
+ aBounds.second = std::max(fSegmentAdvance, aBounds.second);
+ }
+ else
+ {
+ gr::GlyphInfo aNextGlyph = *(iGlyphs.first + j + nDelta);
+ fNextOrigin = std::max(aNextGlyph.attachedClusterBase()->origin(), aNextGlyph.origin());
+ aBounds.second = std::max(fNextOrigin, aBounds.second);
+ nNextOrigin = round(fNextOrigin * fScaling + rDXOffset);
+ }
+ aBounds.first = std::min(aGlyph.origin(), aBounds.first);
+ if ((signed)aGlyph.firstChar() < rArgs.mnEndCharPos &&
+ (signed)aGlyph.firstChar() >= rArgs.mnMinCharPos)
+ {
+ rCharDxs[aGlyph.firstChar()-rArgs.mnMinCharPos] = nNextOrigin;
+ }
+ if ((signed)aGlyph.attachedClusterBase()->logicalIndex() == j)
+ {
+ append(rSeg, rArgs, aGlyph, fNextOrigin, fScaling, rChar2Base, rGlyph2Char, rCharDxs, rDXOffset, bBaseGlyph);
+ bBaseGlyph = false;
+ }
+ }
+ // from the point of view of the dx array, the xpos is
+ // the origin of the first glyph of the next cluster ltr
+ // rtl it is the origin of the 1st glyph of the cluster
+ long nXPos = (bRtl)?
+ round(aFirstGlyph.attachedClusterBase()->origin() * fScaling) + rDXOffset :
+ round(aBounds.second * fScaling) + rDXOffset;
+ // force the last char in range to have the width of the cluster
+ if (bRtl)
+ {
+ for (int n = nNextChar + 1; n <= nFirstCharInCluster; n++)
+ {
+ if ((n < rArgs.mnEndCharPos) && (n >= rArgs.mnMinCharPos))
+ rCharDxs[n-rArgs.mnMinCharPos] = nXPos;
+ }
+ }
+ else
+ {
+ for (int n = nNextChar - 1; n >= nFirstCharInCluster; n--)
+ {
+ if (n < rArgs.mnEndCharPos && n >= rArgs.mnMinCharPos)
+ rCharDxs[n-rArgs.mnMinCharPos] = nXPos;
+ }
+ }
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"Cluster g[%d-%d) c[%d-%d)%x x%ld y%f bw%d\n", nFirstGlyphInCluster, nNextGlyph, nFirstCharInCluster, nNextChar, rArgs.mpStr[nFirstCharInCluster], nXPos, aFirstGlyph.yOffset(), nBreakWeight);
+#endif
+ return aBounds;
+}
+
+// append walks an attachment tree, flattening it, and converting it into a
+// sequence of GlyphItem objects which we can later manipulate.
+void
+GraphiteLayout::Glyphs::append(gr::Segment &segment, ImplLayoutArgs &args, gr::GlyphInfo & gi, float nextGlyphOrigin, float scaling, std::vector<int> & rChar2Base, std::vector<int> & rGlyph2Char, std::vector<int> & rCharDxs, long & rDXOffset, bool bIsBase)
+{
+ float nextOrigin = nextGlyphOrigin;
+ int firstChar = std::min(gi.firstChar(), gi.lastChar());
+ assert(size() < rGlyph2Char.size());
+ if (!bIsBase) rGlyph2Char[size()] = firstChar;
+ // is the next glyph attached or in the next cluster?
+ glyph_set_range_t iAttached = gi.attachedClusterGlyphs();
+ if (iAttached.first != iAttached.second)
+ {
+ nextOrigin = iAttached.first->origin();
+ }
+ long glyphId = gi.glyphID();
+ long deltaOffset = 0;
+ int glyphWidth = round(nextOrigin * scaling) - round(gi.origin() * scaling);
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"c%d g%d gWidth%d x%f ", firstChar, (int)gi.logicalIndex(), glyphWidth, nextOrigin);
+#endif
+ if (glyphId == 0)
+ {
+ args.NeedFallback(
+ firstChar,
+ gr::RightToLeftDir(gr::DirCode(gi.directionality())));
+ if( (SAL_LAYOUT_FOR_FALLBACK & args.mnFlags ))
+ {
+ glyphId = GF_DROPPED;
+ deltaOffset -= glyphWidth;
+ glyphWidth = 0;
+ }
+ }
+ else if(args.mnFlags & SAL_LAYOUT_FOR_FALLBACK)
+ {
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"fallback c%d %x in run %d\n", firstChar, args.mpStr[firstChar],
+ args.maRuns.PosIsInAnyRun(firstChar));
+#endif
+ // glyphs that aren't requested for fallback will be taken from base
+ // layout, so mark them as dropped (should this wait until Simplify(false) is called?)
+ if (!args.maRuns.PosIsInAnyRun(firstChar) &&
+ in_range(firstChar, args.mnMinCharPos, args.mnEndCharPos))
+ {
+ glyphId = GF_DROPPED;
+ deltaOffset -= glyphWidth;
+ glyphWidth = 0;
+ }
+ }
+ // append this glyph.
+ long nGlyphFlags = bIsBase ? 0 : GlyphItem::IS_IN_CLUSTER;
+ // directionality seems to be unreliable
+ //nGlyphFlags |= gr::RightToLeftDir(gr::DirCode(gi.attachedClusterBase()->directionality())) ? GlyphItem::IS_RTL_GLYPH : 0;
+ nGlyphFlags |= (gi.directionLevel() & 0x1)? GlyphItem::IS_RTL_GLYPH : 0;
+ GlyphItem aGlyphItem(size(),//gi.logicalIndex(),
+ glyphId,
+ Point(round(gi.origin() * scaling + rDXOffset),
+ round((-gi.yOffset() * scaling) - segment.AscentOffset()* scaling)),
+ nGlyphFlags,
+ glyphWidth);
+ aGlyphItem.mnOrigWidth = round(gi.advanceWidth() * scaling);
+ push_back(aGlyphItem);
+
+ // update the offset if this glyph was dropped
+ rDXOffset += deltaOffset;
+
+ // Recursively apply append all the attached glyphs.
+ for (gr::GlyphSetIterator agi = iAttached.first; agi != iAttached.second; ++agi)
+ {
+ if (agi + 1 == iAttached.second)
+ append(segment, args, *agi, nextGlyphOrigin, scaling, rChar2Base, rGlyph2Char,rCharDxs, rDXOffset, false);
+ else
+ append(segment, args, *agi, (agi + 1)->origin(), scaling, rChar2Base, rGlyph2Char, rCharDxs, rDXOffset, false);
+ }
+}
+
+//
+// An implementation of the SalLayout interface to enable Graphite enabled fonts to be used.
+//
+GraphiteLayout::GraphiteLayout(const gr::Font & font, const grutils::GrFeatureParser * pFeatures) throw()
+ : mpTextSrc(0),
+ mrFont(font),
+ mnWidth(0),
+ mfScaling(1.0),
+ mpFeatures(pFeatures)
+{
+ // Line settings can have subtle affects on space handling
+ // since we don't really know whether it is the end of a line or just a run
+ // in the middle, it is hard to know what to set them to.
+ // If true, it can cause end of line spaces to be hidden e.g. Doulos SIL
+ maLayout.setStartOfLine(false);
+ maLayout.setEndOfLine(false);
+ maLayout.setDumbFallback(true);
+ // trailing ws doesn't seem to always take affect if end of line is true
+ maLayout.setTrailingWs(gr::ktwshAll);
+#ifdef GRLAYOUT_DEBUG
+ gr::ScriptDirCode aDirCode = font.getSupportedScriptDirections();
+ fprintf(grLog(),"GraphiteLayout scripts %x %lx\n", aDirCode, long(this));
+#endif
+}
+
+
+GraphiteLayout::~GraphiteLayout() throw()
+{
+ clear();
+ // the features are owned by the platform layers
+ mpFeatures = NULL;
+}
+
+void GraphiteLayout::clear()
+{
+ // Destroy the segment and text source from any previous invocation of
+ // LayoutText
+ mvGlyphs.clear();
+ mvCharDxs.clear();
+ mvChar2BaseGlyph.clear();
+ mvGlyph2Char.clear();
+
+#ifndef GRCACHE
+ delete mpTextSrc;
+#endif
+
+ // Reset the state to the empty state.
+ mpTextSrc=0;
+ mnWidth = 0;
+ // Don't reset the scaling, because it is set before LayoutText
+}
+
+// This method shouldn't be called on windows, since it needs the dc reset
+bool GraphiteLayout::LayoutText(ImplLayoutArgs & rArgs)
+{
+#ifdef GRCACHE
+ GrSegRecord * pSegRecord = NULL;
+ gr::Segment * pSegment = NULL;
+ // Graphite can in rare cases crash with a zero length
+ if (rArgs.mnMinCharPos < rArgs.mnEndCharPos)
+ {
+ pSegment = CreateSegment(rArgs, &pSegRecord);
+ if (!pSegment)
+ return false;
+ }
+ else
+ {
+ clear();
+ return true;
+ }
+ // layout the glyphs as required by OpenOffice
+ bool success = LayoutGlyphs(rArgs, pSegment, pSegRecord);
+
+ if (pSegRecord) pSegRecord->unlock();
+ else delete pSegment;
+#else
+ gr::Segment * pSegment = NULL;
+ bool success = true;
+ if (rArgs.mnMinCharPos < rArgs.mnEndCharPos)
+ {
+ pSegment = CreateSegment(rArgs);
+ if (!pSegment)
+ return false;
+ success = LayoutGlyphs(rArgs, pSegment);
+ if (pSegment) delete pSegment;
+ }
+ else
+ {
+ clear();
+ }
+#endif
+ return success;
+}
+
+#ifdef GRCACHE
+class GrFontHasher : public gr::Font
+{
+public:
+ GrFontHasher(const gr::Font & aFont) : gr::Font(aFont), mrRealFont(const_cast<gr::Font&>(aFont)) {};
+ ~GrFontHasher(){};
+ virtual bool bold() { return mrRealFont.bold(); };
+ virtual bool italic() { return mrRealFont.italic(); };
+ virtual float ascent() { return mrRealFont.ascent(); };
+ virtual float descent() { return mrRealFont.descent(); };
+ virtual float height() { return mrRealFont.height(); };
+ virtual gr::Font* copyThis() { return mrRealFont.copyThis(); };
+ virtual unsigned int getDPIx() { return mrRealFont.getDPIx(); };
+ virtual unsigned int getDPIy() { return mrRealFont.getDPIy(); };
+ virtual const void* getTable(gr::fontTableId32 nId, size_t* nSize)
+ { return mrRealFont.getTable(nId,nSize); }
+ virtual void getFontMetrics(float*pA, float*pB, float*pC) { mrRealFont.getFontMetrics(pA,pB,pC); };
+
+ sal_Int32 hashCode(const grutils::GrFeatureParser * mpFeatures)
+ {
+ // is this sufficient?
+ ext_std::wstring aFace;
+ bool bBold;
+ bool bItalic;
+ UniqueCacheInfo(aFace, bBold, bItalic);
+ sal_Unicode uName[32]; // max length used in gr::Font
+ // Note: graphite stores font names as UTF-16 even if wchar_t is 32bit
+ // this conversion should be OK.
+ for (size_t i = 0; i < aFace.size() && i < 32; i++)
+ {
+ uName[i] = aFace[i];
+ }
+ size_t iSize = aFace.size();
+ if (0 == iSize) return 0;
+ sal_Int32 hash = rtl_ustr_hashCode_WithLength(uName, iSize);
+ hash ^= static_cast<sal_Int32>(height());
+ hash |= (bBold)? 0x1000000 : 0;
+ hash |= (bItalic)? 0x2000000 : 0;
+ if (mpFeatures)
+ hash ^= mpFeatures->hashCode();
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(), "font hash %x size %f\n", (int)hash, height());
+#endif
+ return hash;
+ };
+protected:
+ virtual void UniqueCacheInfo( ext_std::wstring& stuFace, bool& fBold, bool& fItalic )
+ {
+#ifdef WIN32
+ dynamic_cast<GraphiteWinFont&>(mrRealFont).UniqueCacheInfo(stuFace, fBold, fItalic);
+#else
+#ifdef UNX
+ dynamic_cast<GraphiteFontAdaptor&>(mrRealFont).UniqueCacheInfo(stuFace, fBold, fItalic);
+#else
+#error Unknown base type for gr::Font::UniqueCacheInfo
+#endif
+#endif
+ }
+private:
+ gr::Font & mrRealFont;
+};
+#endif
+
+#ifdef GRCACHE
+gr::Segment * GraphiteLayout::CreateSegment(ImplLayoutArgs& rArgs, GrSegRecord ** pSegRecord)
+#else
+gr::Segment * GraphiteLayout::CreateSegment(ImplLayoutArgs& rArgs)
+#endif
+{
+ assert(rArgs.mnLength >= 0);
+
+ gr::Segment * pSegment = NULL;
+
+ // Set the SalLayouts values to be the inital ones.
+ SalLayout::AdjustLayout(rArgs);
+ // TODO check if this is needed
+ if (mnUnitsPerPixel > 1)
+ mfScaling = 1.0f / mnUnitsPerPixel;
+
+ // Clear out any previous buffers
+ clear();
+ bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
+ try
+ {
+ // Don't set RTL if font doesn't support it otherwise it forces rtl on
+ // everything
+ if (bRtl && (mrFont.getSupportedScriptDirections() & gr::kfsdcHorizRtl))
+ maLayout.setRightToLeft(bRtl);
+
+ // Context is often needed beyond the specified end, however, we don't
+ // want it if there has been a direction change, since it is hard
+ // to tell between reordering within one direction and multi-directional
+ // text. Extra context, can also cause problems with ligatures stradling
+ // a hyphenation point, so disable if CTL is disabled.
+ const int nSegCharLimit = min(rArgs.mnLength, mnEndCharPos + EXTRA_CONTEXT_LENGTH);
+ int limit = rArgs.mnEndCharPos;
+ if ((nSegCharLimit > limit) && !(SAL_LAYOUT_COMPLEX_DISABLED & rArgs.mnFlags))
+ {
+ limit += findSameDirLimit(rArgs.mpStr + rArgs.mnEndCharPos,
+ nSegCharLimit - rArgs.mnEndCharPos, bRtl);
+ }
+
+#ifdef GRCACHE
+ GrFontHasher hasher(mrFont);
+ sal_Int32 aFontHash = hasher.hashCode(mpFeatures);
+ GraphiteSegmentCache * pCache =
+ (GraphiteCacheHandler::instance).getCache(aFontHash);
+ if (pCache)
+ {
+ *pSegRecord = pCache->getSegment(rArgs, bRtl, limit);
+ if (*pSegRecord)
+ {
+ pSegment = (*pSegRecord)->getSegment();
+ mpTextSrc = (*pSegRecord)->getTextSrc();
+ maLayout.setRightToLeft((*pSegRecord)->isRtl());
+ if (rArgs.mpStr != mpTextSrc->getLayoutArgs().mpStr ||
+ rArgs.mnMinCharPos != mpTextSrc->getLayoutArgs().mnMinCharPos ||
+ rArgs.mnEndCharPos != mpTextSrc->getLayoutArgs().mnEndCharPos ||
+ (SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags) )
+ {
+ (*pSegRecord)->clearVectors();
+ }
+ mpTextSrc->switchLayoutArgs(rArgs);
+ if (limit > rArgs.mnMinCharPos && limit == rArgs.mnEndCharPos
+ && pSegment->stopCharacter() != limit)
+ {
+ // check that the last character is not part of a ligature
+ glyph_set_range_t aGlyphSet = pSegment->charToGlyphs(limit - 1);
+ if (aGlyphSet.first == aGlyphSet.second)
+ {
+ // no glyphs associated with this glyph - occurs mid ligature
+ pSegment = NULL;
+ *pSegRecord = NULL;
+ }
+ else
+ {
+ while (aGlyphSet.first != aGlyphSet.second)
+ {
+ int lastChar = static_cast<int>((*aGlyphSet.first).lastChar());
+ if (lastChar >= limit)
+ {
+ pSegment = NULL;
+ *pSegRecord = NULL;
+ break;
+ }
+ aGlyphSet.first++;
+ }
+ }
+ }
+ if (pSegment)
+ return pSegment;
+ }
+ }
+#endif
+
+ // Create a new TextSource object for the engine.
+ mpTextSrc = new TextSourceAdaptor(rArgs, limit);
+ if (mpFeatures) mpTextSrc->setFeatures(mpFeatures);
+
+ pSegment = new gr::RangeSegment((gr::Font *)&mrFont, mpTextSrc, &maLayout, mnMinCharPos, limit);
+ if (pSegment != NULL)
+ {
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"Gr::LayoutText %d-%d, context %d,len%d rtl%d/%d scaling %f\n", rArgs.mnMinCharPos,
+ rArgs.mnEndCharPos, limit, rArgs.mnLength, maLayout.rightToLeft(), pSegment->rightToLeft(), mfScaling);
+#endif
+#ifdef GRCACHE
+ // on a new segment rightToLeft should be correct
+ *pSegRecord = pCache->cacheSegment(mpTextSrc, pSegment, pSegment->rightToLeft());
+#endif
+ }
+ else
+ {
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(), "Gr::LayoutText failed: ");
+ for (int i = mnMinCharPos; i < limit; i++)
+ {
+ fprintf(grLog(), "%04x ", rArgs.mpStr[i]);
+ }
+ fprintf(grLog(), "\n");
+#endif
+ clear();
+ return NULL;
+ }
+ }
+ catch (...)
+ {
+ clear(); // destroy the text source and any partially built segments.
+ return NULL;
+ }
+ return pSegment;
+}
+
+#ifdef GRCACHE
+bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs& rArgs, gr::Segment * pSegment, GrSegRecord * pSegRecord)
+#else
+bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs& rArgs, gr::Segment * pSegment)
+#endif
+{
+#ifdef GRCACHE
+#ifdef GRCACHE_REUSE_VECTORS
+ // if we have an exact match, then we can reuse the glyph vectors from before
+ if (pSegRecord && (pSegRecord->glyphs().size() > 0) &&
+ (pSegRecord->fontScale() == mfScaling) &&
+ !(SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags) )
+ {
+ mnWidth = pSegRecord->width();
+ mvGlyphs = pSegRecord->glyphs();
+ mvCharDxs = pSegRecord->charDxs();
+ mvChar2BaseGlyph = pSegRecord->char2BaseGlyph();
+ mvGlyph2Char = pSegRecord->glyph2Char();
+ return true;
+ }
+#endif
+#endif
+ // Calculate the initial character dxs.
+ mvCharDxs.assign(mnEndCharPos - mnMinCharPos, -1);
+ mvChar2BaseGlyph.assign(mnEndCharPos - mnMinCharPos, -1);
+ mnWidth = 0;
+ if (mvCharDxs.size() > 0)
+ {
+ // Discover all the clusters.
+ try
+ {
+ // Note: we use the layout rightToLeft() because in cached segments
+ // rightToLeft() may no longer be valid if the engine has been run
+ // ltr since the segment was created.
+#ifdef GRCACHE
+ bool bRtl = pSegRecord? pSegRecord->isRtl() : pSegment->rightToLeft();
+#else
+ bool bRtl = pSegment->rightToLeft();
+#endif
+ mvGlyphs.fill_from(*pSegment, rArgs, bRtl,
+ mnWidth, mfScaling, mvChar2BaseGlyph, mvGlyph2Char, mvCharDxs);
+
+ if (bRtl)
+ {
+ // not needed for adjacent differences, but for mouse clicks to char
+ std::transform(mvCharDxs.begin(), mvCharDxs.end(), mvCharDxs.begin(),
+ std::bind1st(std::minus<long>(), mnWidth));
+ // fixup last dx to ensure it always equals the width
+ mvCharDxs[mvCharDxs.size() - 1] = mnWidth;
+ }
+#ifdef GRCACHE
+#ifdef GRCACHE_REUSE_VECTORS
+ if (pSegRecord && rArgs.maReruns.IsEmpty() &&
+ !(SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags))
+ {
+ pSegRecord->setGlyphVectors(mnWidth, mvGlyphs, mvCharDxs,
+ mvChar2BaseGlyph, mvGlyph2Char,
+ mfScaling);
+ }
+#endif
+#endif
+ }
+ catch (std::exception e)
+ {
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"LayoutGlyphs failed %s\n", e.what());
+#endif
+ return false;
+ }
+ catch (...)
+ {
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"LayoutGlyphs failed with exception");
+#endif
+ return false;
+ }
+ }
+ else
+ {
+ mnWidth = 0;
+ }
+ return true;
+}
+
+int GraphiteLayout::GetTextBreak(long maxmnWidth, long char_extra, int factor) const
+{
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"Gr::GetTextBreak c[%d-%d) maxWidth %ld char extra %ld factor %d\n",
+ mnMinCharPos, mnEndCharPos, maxmnWidth, char_extra, factor);
+#endif
+
+ // return quickly if this segment is narrower than the target width
+ if (maxmnWidth > mnWidth * factor + char_extra * (mnEndCharPos - mnMinCharPos - 1))
+ return STRING_LEN;
+
+ long nWidth = mvCharDxs[0] * factor;
+ int nLastBreak = -1;
+ for (size_t i = 1; i < mvCharDxs.size(); i++)
+ {
+ nWidth += char_extra;
+ if (nWidth > maxmnWidth) break;
+ if (mvChar2BaseGlyph[i] != -1)
+ {
+ if (mvChar2BaseGlyph[i] & (WORD_BREAK_BEFORE | HYPHEN_BREAK_BEFORE))
+ nLastBreak = static_cast<int>(i);
+ }
+ nWidth += (mvCharDxs[i] - mvCharDxs[i-1]) * factor;
+ }
+ int nBreak = mnMinCharPos;
+ if (nLastBreak > -1)
+ nBreak += nLastBreak;
+
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(), "Gr::GetTextBreak break after %d\n", nBreak - mnMinCharPos);
+#endif
+
+ if (nBreak > mnEndCharPos) nBreak = STRING_LEN;
+ else if (nBreak < mnMinCharPos) nBreak = mnMinCharPos;
+ return nBreak;
+}
+
+
+long GraphiteLayout::FillDXArray( sal_Int32* pDXArray ) const
+{
+ if (mnEndCharPos == mnMinCharPos)
+ // Then we must be zero width!
+ return 0;
+
+ if (pDXArray)
+ {
+ for (size_t i = 0; i < mvCharDxs.size(); i++)
+ {
+ assert( (mvChar2BaseGlyph[i] == -1) ||
+ ((signed)(mvChar2BaseGlyph[i] & GLYPH_INDEX_MASK) < (signed)mvGlyphs.size()));
+ if (mvChar2BaseGlyph[i] != -1 &&
+ mvGlyphs[mvChar2BaseGlyph[i] & GLYPH_INDEX_MASK].mnGlyphIndex == GF_DROPPED)
+ {
+ // when used in MultiSalLayout::GetTextBreak dropped glyphs
+ // must have zero width
+ pDXArray[i] = 0;
+ }
+ else
+ {
+ pDXArray[i] = mvCharDxs[i];
+ if (i > 0) pDXArray[i] -= mvCharDxs[i-1];
+ }
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"%d,%d,%d ", (int)i, (int)mvCharDxs[i], pDXArray[i]);
+#endif
+ }
+ //std::adjacent_difference(mvCharDxs.begin(), mvCharDxs.end(), pDXArray);
+ //for (size_t i = 0; i < mvCharDxs.size(); i++)
+ // fprintf(grLog(),"%d,%d,%d ", (int)i, (int)mvCharDxs[i], pDXArray[i]);
+ //fprintf(grLog(),"FillDX %ld,%d\n", mnWidth, std::accumulate(pDXArray, pDXArray + mvCharDxs.size(), 0));
+ }
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"FillDXArray %d-%d,%d=%ld\n", mnMinCharPos, mnEndCharPos, (int)mpTextSrc->getLength(), mnWidth);
+#endif
+ return mnWidth;
+}
+
+
+void GraphiteLayout::AdjustLayout(ImplLayoutArgs& rArgs)
+{
+ SalLayout::AdjustLayout(rArgs);
+ if(rArgs.mpDXArray)
+ {
+ std::vector<int> vDeltaWidths(mvGlyphs.size(), 0);
+ ApplyDXArray(rArgs, vDeltaWidths);
+
+ if( (mnLayoutFlags & SAL_LAYOUT_BIDI_RTL) &&
+ !(rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) )
+ {
+ // check if this is a kashida script
+ bool bKashidaScript = false;
+ for (int i = rArgs.mnMinCharPos; i < rArgs.mnEndCharPos; i++)
+ {
+ UErrorCode aStatus = U_ZERO_ERROR;
+ UScriptCode scriptCode = uscript_getScript(rArgs.mpStr[i], &aStatus);
+ if (scriptCode == USCRIPT_ARABIC || scriptCode == USCRIPT_SYRIAC)
+ {
+ bKashidaScript = true;
+ break;
+ }
+ }
+ int nKashidaWidth = 0;
+ int nKashidaIndex = getKashidaGlyph(nKashidaWidth);
+ if( nKashidaIndex != 0 && bKashidaScript)
+ {
+ kashidaJustify( vDeltaWidths, nKashidaIndex, nKashidaWidth );
+ }
+ }
+ }
+ else if (rArgs.mnLayoutWidth > 0)
+ {
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(), "AdjustLayout width %ld=>%ld\n", mnWidth, rArgs.mnLayoutWidth);
+#endif
+ expandOrCondense(rArgs);
+ }
+}
+
+void GraphiteLayout::expandOrCondense(ImplLayoutArgs &rArgs)
+{
+ int nDeltaWidth = rArgs.mnLayoutWidth - mnWidth;
+ if (nDeltaWidth > 0) // expand, just expand between clusters
+ {
+ int nClusterCount = 0;
+ for (size_t j = 0; j < mvGlyphs.size(); j++)
+ {
+ if (mvGlyphs[j].IsClusterStart())
+ {
+ ++nClusterCount;
+ }
+ }
+ if (nClusterCount > 1)
+ {
+ float fExtraPerCluster = static_cast<float>(nDeltaWidth) / static_cast<float>(nClusterCount - 1);
+ int nCluster = 0;
+ int nOffset = 0;
+ for (size_t i = 0; i < mvGlyphs.size(); i++)
+ {
+ if (mvGlyphs[i].IsClusterStart())
+ {
+ nOffset = fExtraPerCluster * nCluster;
+ size_t nCharIndex = mvGlyph2Char[i];
+ mvCharDxs[nCharIndex] += nOffset;
+ // adjust char dxs for rest of characters in cluster
+ while (++nCharIndex < mvGlyph2Char.size())
+ {
+ int nChar2Base = (mvChar2BaseGlyph[nCharIndex] == -1)? -1 : mvChar2BaseGlyph[nCharIndex] & GLYPH_INDEX_MASK;
+ if (nChar2Base == -1 || nChar2Base == static_cast<int>(i))
+ mvCharDxs[nCharIndex] += nOffset;
+ }
+ ++nCluster;
+ }
+ mvGlyphs[i].maLinearPos.X() += nOffset;
+ }
+ }
+ }
+ else // condense - apply a factor to all glyph positions
+ {
+ if (mvGlyphs.size() == 0) return;
+ Glyphs::iterator iLastGlyph = mvGlyphs.begin() + (mvGlyphs.size() - 1);
+ // position last glyph using original width
+ float fXFactor = static_cast<float>(rArgs.mnLayoutWidth - iLastGlyph->mnOrigWidth) / static_cast<float>(iLastGlyph->maLinearPos.X());
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(), "Condense by factor %f\n", fXFactor);
+#endif
+ iLastGlyph->maLinearPos.X() = rArgs.mnLayoutWidth - iLastGlyph->mnOrigWidth;
+ Glyphs::iterator iGlyph = mvGlyphs.begin();
+ while (iGlyph != iLastGlyph)
+ {
+ iGlyph->maLinearPos.X() = static_cast<float>(iGlyph->maLinearPos.X()) * fXFactor;
+ ++iGlyph;
+ }
+ for (size_t i = 0; i < mvCharDxs.size(); i++)
+ {
+ mvCharDxs[i] = fXFactor * static_cast<float>(mvCharDxs[i]);
+ }
+ }
+ mnWidth = rArgs.mnLayoutWidth;
+}
+
+void GraphiteLayout::ApplyDXArray(ImplLayoutArgs &args, std::vector<int> & rDeltaWidth)
+{
+ const size_t nChars = args.mnEndCharPos - args.mnMinCharPos;
+ if (nChars == 0) return;
+
+#ifdef GRLAYOUT_DEBUG
+ for (size_t iDx = 0; iDx < mvCharDxs.size(); iDx++)
+ fprintf(grLog(),"%d,%d,%d ", (int)iDx, (int)mvCharDxs[iDx], args.mpDXArray[iDx]);
+ fprintf(grLog(),"ApplyDx\n");
+#endif
+ bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
+ int nXOffset = 0;
+ if (bRtl)
+ {
+ nXOffset = args.mpDXArray[nChars - 1] - mvCharDxs[nChars - 1];
+ }
+ int nPrevClusterGlyph = (bRtl)? (signed)mvGlyphs.size() : -1;
+ int nPrevClusterLastChar = -1;
+ for (size_t i = 0; i < nChars; i++)
+ {
+ int nChar2Base = (mvChar2BaseGlyph[i] == -1)? -1 : mvChar2BaseGlyph[i] & GLYPH_INDEX_MASK;
+ if ((nChar2Base > -1) && (nChar2Base != nPrevClusterGlyph))
+ {
+ assert((nChar2Base > -1) && (nChar2Base < (signed)mvGlyphs.size()));
+ GlyphItem & gi = mvGlyphs[nChar2Base];
+ if (!gi.IsClusterStart())
+ continue;
+
+ // find last glyph of this cluster
+ size_t j = i + 1;
+ int nLastChar = i;
+ int nLastGlyph = nChar2Base;
+ for (; j < nChars; j++)
+ {
+ int nChar2BaseJ = (mvChar2BaseGlyph[j] == -1)? -1 : mvChar2BaseGlyph[j] & GLYPH_INDEX_MASK;
+ assert((nChar2BaseJ >= -1) && (nChar2BaseJ < (signed)mvGlyphs.size()));
+ if (nChar2BaseJ != -1 && mvGlyphs[nChar2BaseJ].IsClusterStart())
+ {
+ nLastGlyph = nChar2BaseJ + ((bRtl)? 1 : -1);
+ nLastChar = j - 1;
+ break;
+ }
+ }
+ if (nLastGlyph < 0)
+ {
+ nLastGlyph = nChar2Base;
+ }
+ // Its harder to find the last glyph rtl, since the first of
+ // cluster is still on the left so we need to search towards
+ // the previous cluster to the right
+ if (bRtl)
+ {
+ nLastGlyph = nChar2Base;
+ while (nLastGlyph + 1 < (signed)mvGlyphs.size() &&
+ !mvGlyphs[nLastGlyph+1].IsClusterStart())
+ {
+ ++nLastGlyph;
+ }
+ }
+ if (j == nChars)
+ {
+ nLastChar = nChars - 1;
+ if (!bRtl) nLastGlyph = mvGlyphs.size() - 1;
+ }
+ assert((nLastChar > -1) && (nLastChar < (signed)nChars));
+ long nNewClusterWidth = args.mpDXArray[nLastChar];
+ long nOrigClusterWidth = mvCharDxs[nLastChar];
+ long nDGlyphOrigin = 0;
+ if (nPrevClusterLastChar > - 1)
+ {
+ assert(nPrevClusterLastChar < (signed)nChars);
+ nNewClusterWidth -= args.mpDXArray[nPrevClusterLastChar];
+ nOrigClusterWidth -= mvCharDxs[nPrevClusterLastChar];
+ nDGlyphOrigin = args.mpDXArray[nPrevClusterLastChar] - mvCharDxs[nPrevClusterLastChar];
+ }
+ long nDWidth = nNewClusterWidth - nOrigClusterWidth;
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(), "c%lu last glyph %d/%lu\n", i, nLastGlyph, mvGlyphs.size());
+#endif
+ assert((nLastGlyph > -1) && (nLastGlyph < (signed)mvGlyphs.size()));
+ mvGlyphs[nLastGlyph].mnNewWidth += nDWidth;
+ if (gi.mnGlyphIndex != GF_DROPPED)
+ mvGlyphs[nLastGlyph].mnNewWidth += nDWidth;
+ else
+ nDGlyphOrigin += nDWidth;
+ // update glyph positions
+ if (bRtl)
+ {
+ for (int n = nChar2Base; n <= nLastGlyph; n++)
+ {
+ assert((n > - 1) && (n < (signed)mvGlyphs.size()));
+ mvGlyphs[n].maLinearPos.X() += -nDGlyphOrigin + nXOffset;
+ }
+ }
+ else
+ {
+ for (int n = nChar2Base; n <= nLastGlyph; n++)
+ {
+ assert((n > - 1) && (n < (signed)mvGlyphs.size()));
+ mvGlyphs[n].maLinearPos.X() += nDGlyphOrigin + nXOffset;
+ }
+ }
+ rDeltaWidth[nChar2Base] = nDWidth;
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"c%d g%d-%d dW%ld-%ld=%ld dX%ld x%ld\t", (int)i, nChar2Base, nLastGlyph, nNewClusterWidth, nOrigClusterWidth, nDWidth, nDGlyphOrigin, mvGlyphs[nChar2Base].maLinearPos.X());
+#endif
+ nPrevClusterGlyph = nChar2Base;
+ nPrevClusterLastChar = nLastChar;
+ i = nLastChar;
+ }
+ }
+ // Update the dx vector with the new values.
+ std::copy(args.mpDXArray, args.mpDXArray + nChars,
+ mvCharDxs.begin() + (args.mnMinCharPos - mnMinCharPos));
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"ApplyDx %d(%ld)\n", args.mpDXArray[nChars - 1], mnWidth);
+#endif
+ mnWidth = args.mpDXArray[nChars - 1];
+}
+
+void GraphiteLayout::kashidaJustify(std::vector<int>& rDeltaWidths, sal_GlyphId nKashidaIndex, int nKashidaWidth)
+{
+ // skip if the kashida glyph in the font looks suspicious
+ if( nKashidaWidth <= 0 )
+ return;
+
+ // calculate max number of needed kashidas
+ Glyphs::iterator i = mvGlyphs.begin();
+ int nKashidaCount = 0;
+ int nOrigGlyphIndex = -1;
+ int nGlyphIndex = -1;
+ while (i != mvGlyphs.end())
+ {
+ nOrigGlyphIndex++;
+ nGlyphIndex++;
+ // only inject kashidas in RTL contexts
+ if( !(*i).IsRTLGlyph() )
+ {
+ ++i;
+ continue;
+ }
+ // no kashida-injection for blank justified expansion either
+ if( IsSpacingGlyph( (*i).mnGlyphIndex ) )
+ {
+ ++i;
+ continue;
+ }
+ // calculate gap, ignore if too small
+ int nGapWidth = rDeltaWidths[nOrigGlyphIndex];
+ // worst case is one kashida even for mini-gaps
+ if( 3 * nGapWidth < nKashidaWidth )
+ {
+ ++i;
+ continue;
+ }
+ nKashidaCount = 1 + (nGapWidth / nKashidaWidth);
+#ifdef GRLAYOUT_DEBUG
+ printf("inserting %d kashidas at %u\n", nKashidaCount, (*i).mnGlyphIndex);
+#endif
+ GlyphItem glyphItem = *i;
+ Point aPos(0, 0);
+ aPos.X() = (*i).maLinearPos.X();
+ GlyphItem newGi(glyphItem.mnCharPos, nKashidaIndex, aPos,
+ GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth);
+ mvGlyphs.reserve(mvGlyphs.size() + nKashidaCount);
+ i = mvGlyphs.begin() + nGlyphIndex;
+ mvGlyphs.insert(i, nKashidaCount, newGi);
+ i = mvGlyphs.begin() + nGlyphIndex;
+ nGlyphIndex += nKashidaCount;
+ // now fix up the kashida positions
+ for (int j = 0; j < nKashidaCount; j++)
+ {
+ (*(i)).maLinearPos.X() -= nGapWidth;
+ nGapWidth -= nKashidaWidth;
+ ++i;
+ }
+
+ // fixup rightmost kashida for gap remainder
+ if( nGapWidth < 0 )
+ {
+ if( nKashidaCount <= 1 )
+ nGapWidth /= 2; // for small gap move kashida to middle
+ (*(i-1)).mnNewWidth += nGapWidth; // adjust kashida width to gap width
+ (*(i-1)).maLinearPos.X() += nGapWidth;
+ }
+
+ (*i).mnNewWidth = (*i).mnOrigWidth;
+ ++i;
+ }
+
+}
+
+void GraphiteLayout::GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const
+{
+ // For each character except the last discover the caret positions
+ // immediatly before and after that character.
+ // This is used for underlines in the GUI amongst other things.
+ // It may be used from MultiSalLayout, in which case it must take into account
+ // glyphs that have been moved.
+ std::fill(pCaretXArray, pCaretXArray + nArraySize, -1);
+ // the layout method doesn't modify the layout even though it isn't
+ // const in the interface
+ bool bRtl = const_cast<GraphiteLayout*>(this)->maLayout.rightToLeft();
+ int prevBase = -1;
+ long prevClusterWidth = 0;
+ for (int i = 0, nCharSlot = 0; i < nArraySize && nCharSlot < static_cast<int>(mvCharDxs.size()); ++nCharSlot, i+=2)
+ {
+ if (mvChar2BaseGlyph[nCharSlot] != -1)
+ {
+ int nChar2Base = mvChar2BaseGlyph[nCharSlot] & GLYPH_INDEX_MASK;
+ assert((mvChar2BaseGlyph[nCharSlot] > -1) && (nChar2Base < (signed)mvGlyphs.size()));
+ GlyphItem gi = mvGlyphs[nChar2Base];
+ if (gi.mnGlyphIndex == GF_DROPPED)
+ {
+ continue;
+ }
+ int nCluster = nChar2Base;
+ long origClusterWidth = gi.mnNewWidth;
+ long nMin = gi.maLinearPos.X();
+ long nMax = gi.maLinearPos.X() + gi.mnNewWidth;
+ // attached glyphs are always stored after their base rtl or ltr
+ while (++nCluster < static_cast<int>(mvGlyphs.size()) &&
+ !mvGlyphs[nCluster].IsClusterStart())
+ {
+ origClusterWidth += mvGlyphs[nCluster].mnNewWidth;
+ if (mvGlyph2Char[nCluster] == nCharSlot)
+ {
+ nMin = std::min(nMin, mvGlyphs[nCluster].maLinearPos.X());
+ nMax = std::min(nMax, mvGlyphs[nCluster].maLinearPos.X() + mvGlyphs[nCluster].mnNewWidth);
+ }
+ }
+ if (bRtl)
+ {
+ pCaretXArray[i+1] = nMin;
+ pCaretXArray[i] = nMax;
+ }
+ else
+ {
+ pCaretXArray[i] = nMin;
+ pCaretXArray[i+1] = nMax;
+ }
+ prevBase = nChar2Base;
+ prevClusterWidth = origClusterWidth;
+ }
+ else if (prevBase > -1)
+ {
+ // this could probably be improved
+ assert((prevBase > -1) && (prevBase < (signed)mvGlyphs.size()));
+ GlyphItem gi = mvGlyphs[prevBase];
+ int nGlyph = prevBase + 1;
+ // try to find a better match, otherwise default to complete cluster
+ for (; nGlyph < static_cast<int>(mvGlyphs.size()) &&
+ !mvGlyphs[nGlyph].IsClusterStart(); nGlyph++)
+ {
+ if (mvGlyph2Char[nGlyph] == nCharSlot)
+ {
+ gi = mvGlyphs[nGlyph];
+ break;
+ }
+ }
+ // if no match position at end of cluster
+ if (nGlyph == static_cast<int>(mvGlyphs.size()) ||
+ mvGlyphs[nGlyph].IsClusterStart())
+ {
+ if (bRtl)
+ {
+ pCaretXArray[i+1] = gi.maLinearPos.X();
+ pCaretXArray[i] = gi.maLinearPos.X();
+ }
+ else
+ {
+ pCaretXArray[i] = gi.maLinearPos.X() + prevClusterWidth;
+ pCaretXArray[i+1] = gi.maLinearPos.X() + prevClusterWidth;
+ }
+ }
+ else
+ {
+ if (bRtl)
+ {
+ pCaretXArray[i+1] = gi.maLinearPos.X();
+ pCaretXArray[i] = gi.maLinearPos.X() + gi.mnNewWidth;
+ }
+ else
+ {
+ pCaretXArray[i] = gi.maLinearPos.X();
+ pCaretXArray[i+1] = gi.maLinearPos.X() + gi.mnNewWidth;
+ }
+ }
+ }
+ else
+ {
+ pCaretXArray[i] = pCaretXArray[i+1] = 0;
+ }
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"%d,%d-%d\t", nCharSlot, pCaretXArray[i], pCaretXArray[i+1]);
+#endif
+ }
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"\n");
+#endif
+}
+
+
+// GetNextGlyphs returns a contiguous sequence of glyphs that can be
+// rendered together. It should never return a dropped glyph.
+// The glyph_slot returned should be the index of the next visible
+// glyph after the last glyph returned by this call.
+// The char_index array should be filled with the characters corresponding
+// to each glyph returned.
+// glyph_adv array should be a virtual width such that if successive
+// glyphs returned by this method are added one after the other they
+// have the correct spacing.
+// The logic in this method must match that expected in MultiSalLayout which
+// is used when glyph fallback is in operation.
+int GraphiteLayout::GetNextGlyphs( int length, sal_GlyphId * glyph_out,
+ ::Point & aPosOut, int &glyph_slot, sal_Int32 * glyph_adv, int *char_index) const
+{
+ // Sanity check on the slot index.
+ if (glyph_slot >= signed(mvGlyphs.size()))
+ {
+ glyph_slot = mvGlyphs.size();
+ return 0;
+ }
+ assert(glyph_slot >= 0);
+ // Find the first glyph in the substring.
+ for (; glyph_slot < signed(mvGlyphs.size()) &&
+ ((mvGlyphs.begin() + glyph_slot)->mnGlyphIndex == GF_DROPPED);
+ ++glyph_slot) {};
+
+ // Update the length
+ const int nGlyphSlotEnd = std::min(size_t(glyph_slot + length), mvGlyphs.size());
+
+ // We're all out of glyphs here.
+ if (glyph_slot == nGlyphSlotEnd)
+ {
+ return 0;
+ }
+
+ // Find as many glyphs as we can which can be drawn in one go.
+ Glyphs::const_iterator glyph_itr = mvGlyphs.begin() + glyph_slot;
+ const int glyph_slot_begin = glyph_slot;
+ const int initial_y_pos = glyph_itr->maLinearPos.Y();
+
+ // Set the position to the position of the start glyph.
+ ::Point aStartPos = glyph_itr->maLinearPos;
+ //aPosOut = glyph_itr->maLinearPos;
+ aPosOut = GetDrawPosition(aStartPos);
+
+
+ for (;;) // Forever
+ {
+ // last index of the range from glyph_to_chars does not include this glyph
+ if (char_index)
+ {
+ assert((glyph_slot >= -1) && (glyph_slot < (signed)mvGlyph2Char.size()));
+ if (mvGlyph2Char[glyph_slot] == -1)
+ *char_index++ = mvCharDxs.size();
+ else
+ *char_index++ = mvGlyph2Char[glyph_slot];
+ }
+ // Copy out this glyphs data.
+ ++glyph_slot;
+ *glyph_out++ = glyph_itr->mnGlyphIndex;
+
+ // Find the actual advance - this must be correct if called from
+ // MultiSalLayout::AdjustLayout which requests one glyph at a time.
+ const long nGlyphAdvance = (glyph_slot == static_cast<int>(mvGlyphs.size()))?
+ glyph_itr->mnNewWidth :
+ ((glyph_itr+1)->maLinearPos.X() - glyph_itr->maLinearPos.X());
+
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"GetNextGlyphs g%d c%d x%ld,%ld adv%ld, pos %ld,%ld\n", glyph_slot - 1,
+ GLYPH_INDEX_MASK&mvGlyph2Char[glyph_slot-1], glyph_itr->maLinearPos.X(), glyph_itr->maLinearPos.Y(), nGlyphAdvance,
+ aPosOut.X(), aPosOut.Y());
+#endif
+
+ if (glyph_adv) // If we are returning advance store it.
+ *glyph_adv++ = nGlyphAdvance;
+ else // Stop when next advance is unexpected.
+ if (glyph_itr->mnOrigWidth != nGlyphAdvance) break;
+
+ // Have fetched all the glyphs we need to
+ if (glyph_slot == nGlyphSlotEnd)
+ break;
+
+ ++glyph_itr;
+ // Stop when next y position is unexpected.
+ if (initial_y_pos != glyph_itr->maLinearPos.Y())
+ break;
+
+ // Stop if glyph dropped
+ if (glyph_itr->mnGlyphIndex == GF_DROPPED)
+ break;
+ }
+ int numGlyphs = glyph_slot - glyph_slot_begin;
+ // move the next glyph_slot to a glyph that hasn't been dropped
+ while (glyph_slot < static_cast<int>(mvGlyphs.size()) &&
+ (mvGlyphs.begin() + glyph_slot)->mnGlyphIndex == GF_DROPPED)
+ ++glyph_slot;
+ return numGlyphs;
+}
+
+
+void GraphiteLayout::MoveGlyph( int nGlyphIndex, long nNewPos )
+{
+ // TODO it might be better to actualy implement simplify properly, but this
+ // needs to be done carefully so the glyph/char maps are maintained
+ // If a glyph has been dropped then it wasn't returned by GetNextGlyphs, so
+ // the index here may be wrong
+ while ((mvGlyphs[nGlyphIndex].mnGlyphIndex == GF_DROPPED) &&
+ (nGlyphIndex < (signed)mvGlyphs.size()))
+ {
+ nGlyphIndex++;
+ }
+ const long dx = nNewPos - mvGlyphs[nGlyphIndex].maLinearPos.X();
+
+ if (dx == 0) return;
+ // GenericSalLayout only changes maLinearPos, mvCharDxs doesn't change
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"Move %d (%ld,%ld) c%d by %ld\n", nGlyphIndex, mvGlyphs[nGlyphIndex].maLinearPos.X(), nNewPos, mvGlyph2Char[nGlyphIndex], dx);
+#endif
+ for (size_t gi = nGlyphIndex; gi < mvGlyphs.size(); gi++)
+ {
+ mvGlyphs[gi].maLinearPos.X() += dx;
+ }
+ // width does need to be updated for correct fallback
+ mnWidth += dx;
+}
+
+
+void GraphiteLayout::DropGlyph( int nGlyphIndex )
+{
+ if(nGlyphIndex >= signed(mvGlyphs.size()))
+ return;
+
+ GlyphItem & glyph = mvGlyphs[nGlyphIndex];
+ glyph.mnGlyphIndex = GF_DROPPED;
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"Dropped %d\n", nGlyphIndex);
+#endif
+}
+
+void GraphiteLayout::Simplify( bool isBaseLayout )
+{
+ const sal_GlyphId dropMarker = isBaseLayout ? GF_DROPPED : 0;
+
+ Glyphs::iterator gi = mvGlyphs.begin();
+ // TODO check whether we need to adjust positions here
+ // MultiSalLayout seems to move the glyphs itself, so it may not be needed.
+ long deltaX = 0;
+ while (gi != mvGlyphs.end())
+ {
+ if (gi->mnGlyphIndex == dropMarker)
+ {
+ deltaX += gi->mnNewWidth;
+ gi->mnNewWidth = 0;
+ }
+ else
+ {
+ deltaX = 0;
+ }
+ //mvCharDxs[mvGlyph2Char[gi->mnCharPos]] -= deltaX;
+ ++gi;
+ }
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"Simplify base%d dx=%ld newW=%ld\n", isBaseLayout, deltaX, mnWidth - deltaX);
+#endif
+ // discard width from trailing dropped glyphs, but not those in the middle
+ mnWidth -= deltaX;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/glyphs/graphite_serverfont.cxx b/vcl/source/glyphs/graphite_serverfont.cxx
new file mode 100644
index 000000000000..cda2cd434cba
--- /dev/null
+++ b/vcl/source/glyphs/graphite_serverfont.cxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+// We need this to enable namespace support in libgrengine headers.
+#define GR_NAMESPACE
+
+// Header files
+//
+
+// Platform
+#include <vcl/sallayout.hxx>
+// Module
+#include "gcach_ftyp.hxx"
+#include <vcl/graphite_features.hxx>
+#include "graphite_textsrc.hxx"
+#include <vcl/graphite_serverfont.hxx>
+
+#ifndef WNT
+
+//
+// An implementation of the GraphiteLayout interface to enable Graphite enabled fonts to be used.
+//
+
+GraphiteServerFontLayout::GraphiteServerFontLayout(GraphiteFontAdaptor * pFont) throw()
+ : ServerFontLayout(pFont->font()), mpFont(pFont),
+ maImpl(*mpFont, mpFont->features(), pFont)
+{
+ // Nothing needed here
+}
+
+GraphiteServerFontLayout::~GraphiteServerFontLayout() throw()
+{
+ delete mpFont;
+ mpFont = NULL;
+}
+
+const sal_Unicode* GraphiteServerFontLayout::getTextPtr() const
+{
+ return maImpl.textSrc()->getLayoutArgs().mpStr +
+ maImpl.textSrc()->getLayoutArgs().mnMinCharPos;
+}
+
+sal_GlyphId GraphiteLayoutImpl::getKashidaGlyph(int & width)
+{
+ int nKashidaIndex = mpFont->font().GetGlyphIndex( 0x0640 );
+ if( nKashidaIndex != 0 )
+ {
+ const GlyphMetric& rGM = mpFont->font().GetGlyphMetric( nKashidaIndex );
+ width = rGM.GetCharWidth();
+ }
+ else
+ {
+ width = 0;
+ }
+ return nKashidaIndex;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/glyphs/graphite_textsrc.cxx b/vcl/source/glyphs/graphite_textsrc.cxx
new file mode 100644
index 000000000000..e8967701a52c
--- /dev/null
+++ b/vcl/source/glyphs/graphite_textsrc.cxx
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+// We need this to enable namespace support in libgrengine headers.
+#define GR_NAMESPACE
+
+// Header files
+//
+// Standard Library
+#include <string>
+#include <cassert>
+#include "graphite_textsrc.hxx"
+#include <vcl/graphite_features.hxx>
+
+// class TextSourceAdaptor implementation.
+//
+TextSourceAdaptor::~TextSourceAdaptor()
+{
+ delete mpFeatures;
+}
+
+gr::UtfType TextSourceAdaptor::utfEncodingForm() {
+ return gr::kutf16;
+}
+
+
+size_t TextSourceAdaptor::getLength()
+{
+ return maLayoutArgs.mnLength;
+}
+
+
+size_t TextSourceAdaptor::fetch(gr::toffset, size_t, gr::utf32 *)
+{
+ assert(false);
+ return 0;
+}
+
+
+size_t TextSourceAdaptor::fetch(gr::toffset offset, size_t char_count, gr::utf16 * char_buffer)
+{
+ assert(char_buffer);
+
+ size_t copy_count = std::min(size_t(maLayoutArgs.mnLength), char_count);
+ std::copy(maLayoutArgs.mpStr + offset, maLayoutArgs.mpStr + offset + copy_count, char_buffer);
+
+ return copy_count;
+}
+
+
+size_t TextSourceAdaptor::fetch(gr::toffset, size_t, gr::utf8 *)
+{
+ assert(false);
+ return 0;
+}
+
+
+inline void TextSourceAdaptor::getCharProperties(const int nCharIdx, int & min, int & lim, size_t & depth)
+{
+ maLayoutArgs.ResetPos();
+ bool rtl = maLayoutArgs.mnFlags & SAL_LAYOUT_BIDI_RTL;
+ for(depth = ((rtl)? 1:0); maLayoutArgs.maRuns.GetRun(&min, &lim, &rtl); maLayoutArgs.maRuns.NextRun())
+ {
+ if (min > nCharIdx)
+ break;
+ // Only increase the depth when a change of direction occurs.
+ depth += int(rtl ^ bool(depth & 0x1));
+ if (min <= nCharIdx && nCharIdx < lim)
+ break;
+ }
+ // If there is no run for this position increment the depth, but don't
+ // change if this is out of bounds context
+ if (lim > 0 && nCharIdx >= lim && nCharIdx < maLayoutArgs.mnEndCharPos)
+ depth++;
+}
+
+
+bool TextSourceAdaptor::getRightToLeft(gr::toffset nCharIdx)
+{
+ size_t depth;
+ int min, lim = 0;
+ getCharProperties(nCharIdx, min, lim, depth);
+ //printf("getRtl %d,%x=%d\n", nCharIdx, maLayoutArgs.mpStr[nCharIdx], depth & 0x1);
+ return depth & 0x1;
+}
+
+
+unsigned int TextSourceAdaptor::getDirectionDepth(gr::toffset nCharIdx)
+{
+ size_t depth;
+ int min, lim;
+ getCharProperties(nCharIdx, min, lim, depth);
+ //printf("getDirectionDepth %d,%x=%d\n", nCharIdx, maLayoutArgs.mpStr[nCharIdx], depth);
+ return depth;
+}
+
+
+float TextSourceAdaptor::getVerticalOffset(gr::toffset)
+{
+ return 0.0f; //TODO: Implement correctly
+}
+
+gr::isocode TextSourceAdaptor::getLanguage(gr::toffset)
+{
+ if (mpFeatures && mpFeatures->hasLanguage())
+ return mpFeatures->getLanguage();
+ gr::isocode unknown = {{0,0,0,0}};
+ return unknown;
+}
+
+ext_std::pair<gr::toffset, gr::toffset> TextSourceAdaptor::propertyRange(gr::toffset nCharIdx)
+{
+
+ if (nCharIdx < unsigned(maLayoutArgs.mnMinCharPos))
+ return ext_std::make_pair(0, maLayoutArgs.mnMinCharPos);
+
+ if (nCharIdx < mnEnd)
+ return ext_std::make_pair(maLayoutArgs.mnMinCharPos, mnEnd);
+
+ return ext_std::make_pair(mnEnd, maLayoutArgs.mnLength);
+}
+
+size_t TextSourceAdaptor::getFontFeatures(gr::toffset, gr::FeatureSetting * settings)
+{
+ if (mpFeatures) return mpFeatures->getFontFeatures(settings);
+ return 0;
+}
+
+
+bool TextSourceAdaptor::sameSegment(gr::toffset char_idx1, gr::toffset char_idx2)
+{
+ const ext_std::pair<gr::toffset, gr::toffset>
+ range1 = propertyRange(char_idx1),
+ range2 = propertyRange(char_idx2);
+
+ return range1 == range2;
+}
+
+void TextSourceAdaptor::setFeatures(const grutils::GrFeatureParser * pFeatures)
+{
+ mpFeatures = new grutils::GrFeatureParser(*pFeatures);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/glyphs/graphite_textsrc.hxx b/vcl/source/glyphs/graphite_textsrc.hxx
new file mode 100644
index 000000000000..ca905fc0d824
--- /dev/null
+++ b/vcl/source/glyphs/graphite_textsrc.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef _SV_GRAPHITETEXTSRC_HXX
+#define _SV_GRAPHITETEXTSRC_HXX
+// Description: Implements the Graphite interfaces IGrTextSource and
+// IGrGraphics which provide Graphite with access to the
+// app's text storage system and the platform's font and
+// graphics systems.
+
+// We need this to enable namespace support in libgrengine headers.
+#define GR_NAMESPACE
+
+// Standard Library
+#include <stdexcept>
+// Platform
+
+#include <tools/svwin.h>
+
+#include <svsys.h>
+
+#include <vcl/salgdi.hxx>
+
+#include <vcl/sallayout.hxx>
+
+// Module
+#include "vcl/dllapi.h"
+
+// Libraries
+#include <preextstl.h>
+#include <graphite/GrClient.h>
+#include <graphite/Font.h>
+#include <graphite/ITextSource.h>
+#include <postextstl.h>
+
+// Module type definitions and forward declarations.
+//
+namespace grutils
+{
+ class GrFeatureParser;
+}
+// Implements the Adaptor pattern to adapt the LayoutArgs and the ServerFont interfaces to the
+// gr::IGrTextSource interface.
+// @author tse
+//
+class TextSourceAdaptor : public gr::ITextSource
+{
+public:
+ TextSourceAdaptor(ImplLayoutArgs &layout_args, const int nContextLen) throw();
+ ~TextSourceAdaptor();
+ virtual gr::UtfType utfEncodingForm();
+ virtual size_t getLength();
+ virtual size_t fetch(gr::toffset ichMin, size_t cch, gr::utf32 * prgchBuffer);
+ virtual size_t fetch(gr::toffset ichMin, size_t cch, gr::utf16 * prgchwBuffer);
+ virtual size_t fetch(gr::toffset ichMin, size_t cch, gr::utf8 * prgchsBuffer);
+ virtual bool getRightToLeft(gr::toffset ich);
+ virtual unsigned int getDirectionDepth(gr::toffset ich);
+ virtual float getVerticalOffset(gr::toffset ich);
+ virtual gr::isocode getLanguage(gr::toffset ich);
+
+ virtual ext_std::pair<gr::toffset, gr::toffset> propertyRange(gr::toffset ich);
+ virtual size_t getFontFeatures(gr::toffset ich, gr::FeatureSetting * prgfset);
+ virtual bool sameSegment(gr::toffset ich1, gr::toffset ich2);
+ virtual bool featureVariations() { return false; }
+
+ operator ImplLayoutArgs & () throw();
+ void setFeatures(const grutils::GrFeatureParser * pFeatures);
+ const ImplLayoutArgs & getLayoutArgs() const { return maLayoutArgs; }
+ size_t getContextLength() const { return mnEnd; };
+ inline void switchLayoutArgs(ImplLayoutArgs & newArgs);
+private:
+ // Prevent the generation of a default assignment operator.
+ TextSourceAdaptor & operator=(const TextSourceAdaptor &);
+
+ void getCharProperties(const int, int &, int &, size_t &);
+
+ ImplLayoutArgs maLayoutArgs;
+ size_t mnEnd;
+ const grutils::GrFeatureParser * mpFeatures;
+};
+
+inline TextSourceAdaptor::TextSourceAdaptor(ImplLayoutArgs &la, const int nContextLen) throw()
+ : maLayoutArgs(la),
+ mnEnd(std::min(la.mnLength, nContextLen)),
+ mpFeatures(NULL)
+{
+}
+
+inline TextSourceAdaptor::operator ImplLayoutArgs & () throw() {
+ return maLayoutArgs;
+}
+
+inline void TextSourceAdaptor::switchLayoutArgs(ImplLayoutArgs & aNewArgs)
+{
+ mnEnd += aNewArgs.mnMinCharPos - maLayoutArgs.mnMinCharPos;
+ maLayoutArgs = aNewArgs;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/glyphs/makefile.mk b/vcl/source/glyphs/makefile.mk
new file mode 100644
index 000000000000..74811aba6204
--- /dev/null
+++ b/vcl/source/glyphs/makefile.mk
@@ -0,0 +1,80 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+PRJ=..$/..
+
+PRJNAME=vcl
+TARGET=glyphs
+
+ENABLE_EXCEPTIONS=true
+# --- Settings -----------------------------------------------------
+
+.INCLUDE : $(PRJ)$/util$/makefile.pmk
+.INCLUDE : settings.mk
+.INCLUDE : $(PRJ)$/util$/makefile2.pmk
+
+CFLAGS+= $(FREETYPE_CFLAGS)
+
+# --- Files --------------------------------------------------------
+
+.IF "$(USE_BUILTIN_RASTERIZER)" != ""
+# GlyphCache + FreeType support (only on UNX platforms currently)
+SLOFILES=\
+ $(SLO)$/glyphcache.obj \
+ $(SLO)$/gcach_rbmp.obj \
+ $(SLO)$/gcach_layout.obj \
+ $(SLO)$/gcach_ftyp.obj
+
+.IF "$(ENABLE_GRAPHITE)" != ""
+# Graphite support using the glyphcache infrastructure
+CFLAGS+=-DENABLE_GRAPHITE
+SLOFILES+= $(SLO)$/graphite_adaptors.obj \
+ $(SLO)$/graphite_features.obj \
+ $(SLO)$/graphite_cache.obj \
+ $(SLO)$/graphite_textsrc.obj \
+ $(SLO)$/graphite_serverfont.obj \
+ $(SLO)$/graphite_layout.obj
+.ENDIF
+
+.ELSE
+
+.IF "$(ENABLE_GRAPHITE)" == "TRUE"
+# Graphite support on non-UNX platforms
+# make use of stlport headerfiles
+EXT_USE_STLPORT=TRUE
+SLOFILES=\
+ $(SLO)$/graphite_textsrc.obj \
+ $(SLO)$/graphite_cache.obj \
+ $(SLO)$/graphite_features.obj \
+ $(SLO)$/graphite_layout.obj
+.ENDIF
+.ENDIF
+
+# --- Targets ------------------------------------------------------
+
+.INCLUDE : target.mk
+