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.cxx2667
-rw-r--r--vcl/source/glyphs/gcach_ftyp.hxx280
-rw-r--r--vcl/source/glyphs/gcach_layout.cxx669
-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.cxx609
-rw-r--r--vcl/source/glyphs/graphite_features.cxx288
-rw-r--r--vcl/source/glyphs/graphite_layout.cxx1366
-rw-r--r--vcl/source/glyphs/graphite_serverfont.cxx164
-rw-r--r--vcl/source/glyphs/graphite_textsrc.cxx172
-rw-r--r--vcl/source/glyphs/graphite_textsrc.hxx124
-rw-r--r--vcl/source/glyphs/makefile.mk80
13 files changed, 7046 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..82fa925097ee
--- /dev/null
+++ b/vcl/source/glyphs/gcach_ftyp.cxx
@@ -0,0 +1,2667 @@
+/* -*- 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"
+#ifdef ENABLE_GRAPHITE
+#include <graphite2/Font.h>
+#include "vcl/graphite_layout.hxx"
+#endif
+
+#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 "sft.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)
+ // PORTERS: dlfcn is used for getting symbols from FT versions newer than baseline
+ #include <dlfcn.h>
+ #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); } };
+struct HashStr { size_t operator()( const char* s ) const { return rtl_str_hashCode(s); } };
+typedef ::boost::unordered_map<const char*,boost::shared_ptr<FtFontFile>,HashStr, 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.get();
+
+ // no => create new one
+ FtFontFile* pFontFile = new FtFontFile( rNativeFileName );
+ pFileName = pFontFile->maNativeFileName.getStr();
+ rFontFileList[pFileName].reset(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;
+}
+
+#ifdef ENABLE_GRAPHITE
+// wrap FtFontInfo's table function
+const void * graphiteFontTable(const void* appFaceHandle, unsigned int name, size_t *len)
+{
+ const FtFontInfo * pFontInfo = reinterpret_cast<const FtFontInfo*>(appFaceHandle);
+ typedef union {
+ char m_c[5];
+ unsigned int m_id;
+ } TableId;
+ TableId tableId;
+ tableId.m_id = name;
+#ifndef WORDS_BIGENDIAN
+ TableId swapped;
+ swapped.m_c[3] = tableId.m_c[0];
+ swapped.m_c[2] = tableId.m_c[1];
+ swapped.m_c[1] = tableId.m_c[2];
+ swapped.m_c[0] = tableId.m_c[3];
+ tableId.m_id = swapped.m_id;
+#endif
+ tableId.m_c[4] = '\0';
+ sal_uLong nLength = 0;
+ const void * pTable = static_cast<const void*>(pFontInfo->GetTable(tableId.m_c, &nLength));
+ if (len) *len = static_cast<size_t>(nLength);
+ return pTable;
+}
+#endif
+
+// =======================================================================
+
+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 ),
+#ifdef ENABLE_GRAPHITE
+ mbCheckedGraphite(false),
+ mpGraphiteFace(NULL),
+#endif
+ mnFontId( nFontId ),
+ maDevFontAttributes( rDevFontAttributes ),
+ mpFontCharMap( NULL ),
+ mpChar2Glyph( NULL ),
+ mpGlyph2Char( NULL ),
+ mpExtraKernInfo( pExtraKernInfo )
+{
+ // prefer font with low ID
+ maDevFontAttributes.mnQuality += 10000 - nFontId;
+ // prefer font with matching file names
+ maDevFontAttributes.mnQuality += mpFontFile->GetLangBoost();
+ // prefer font with more external info
+ if( pExtraKernInfo )
+ maDevFontAttributes.mnQuality += 100;
+}
+
+// -----------------------------------------------------------------------
+
+FtFontInfo::~FtFontInfo()
+{
+ if( mpFontCharMap )
+ mpFontCharMap->DeReference();
+ delete mpExtraKernInfo;
+ delete mpChar2Glyph;
+ delete mpGlyph2Char;
+#ifdef ENABLE_GRAPHITE
+ if (mpGraphiteFace)
+ delete mpGraphiteFace;
+#endif
+}
+
+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;
+}
+
+#ifdef ENABLE_GRAPHITE
+GraphiteFaceWrapper * FtFontInfo::GetGraphiteFace()
+{
+ if (mbCheckedGraphite)
+ return mpGraphiteFace;
+ // test for graphite here so that it is cached most efficiently
+ if (GetTable("Silf", 0))
+ {
+ int graphiteSegCacheSize = 10000;
+ static const char* pGraphiteCacheStr = getenv( "SAL_GRAPHITE_CACHE_SIZE" );
+ graphiteSegCacheSize = pGraphiteCacheStr ? (atoi(pGraphiteCacheStr)) : 0;
+ gr_face * pGraphiteFace;
+ if (graphiteSegCacheSize > 500)
+ pGraphiteFace = gr_make_face_with_seg_cache(this, graphiteFontTable, graphiteSegCacheSize, gr_face_cacheCmap);
+ else
+ pGraphiteFace = gr_make_face(this, graphiteFontTable, gr_face_cacheCmap);
+ if (pGraphiteFace)
+ mpGraphiteFace = new GraphiteFaceWrapper(pGraphiteFace);
+ }
+ mbCheckedGraphite = true;
+ return mpGraphiteFace;
+}
+#endif
+
+// -----------------------------------------------------------------------
+
+void FtFontInfo::ReleaseFaceFT( FT_FaceRec_* pFaceFT )
+{
+ // release last/each depending on SizeFT availability
+ if( (--mnRefCount <= 0) || !bEnableSizeFT )
+ {
+ FT_Done_Face( pFaceFT );
+ maFaceFT = NULL;
+ mpFontFile->Unmap();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+bool FtFontInfo::HasExtraKerning() const
+{
+ if( !mpExtraKernInfo )
+ return false;
+ // TODO: how to enable the line below without getting #i29881# back?
+ // on the other hand being to optimistic doesn't cause problems
+ // return mpExtraKernInfo->HasKernPairs();
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+int FtFontInfo::GetExtraKernPairs( ImplKernPairData** ppKernPairs ) const
+{
+ if( !mpExtraKernInfo )
+ return 0;
+ return mpExtraKernInfo->GetUnscaledKernPairs( ppKernPairs );
+}
+
+// -----------------------------------------------------------------------
+
+int FtFontInfo::GetExtraGlyphKernValue( int nLeftGlyph, int nRightGlyph ) const
+{
+ if( !mpExtraKernInfo )
+ return 0;
+ if( !mpGlyph2Char )
+ return 0;
+ sal_Unicode cLeftChar = (*mpGlyph2Char)[ nLeftGlyph ];
+ sal_Unicode cRightChar = (*mpGlyph2Char)[ nRightGlyph ];
+ return mpExtraKernInfo->GetUnscaledKernValue( cLeftChar, cRightChar );
+}
+
+// -----------------------------------------------------------------------
+
+static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);}
+static unsigned GetUShort( const unsigned char* p ){ return((p[0]<<8)+p[1]);}
+//static signed GetSShort( const unsigned char* p ){ return((short)((p[0]<<8)+p[1]));}
+
+// -----------------------------------------------------------------------
+
+const unsigned char* FtFontInfo::GetTable( const char* pTag, sal_uLong* pLength ) const
+{
+ const unsigned char* pBuffer = mpFontFile->GetBuffer();
+ int nFileSize = mpFontFile->GetFileSize();
+ if( !pBuffer || nFileSize<1024 )
+ return NULL;
+
+ // we currently only handle TTF and TTC headers
+ unsigned nFormat = GetUInt( pBuffer );
+ const unsigned char* p = pBuffer + 12;
+ if( nFormat == 0x74746366 ) // TTC_MAGIC
+ p += GetUInt( p + 4 * mnFaceNum );
+ else if( (nFormat!=0x00010000) && (nFormat!=0x74727565) ) // TTF_MAGIC and Apple TTF Magic
+ return NULL;
+
+ // walk table directory until match
+ int nTables = GetUShort( p - 8 );
+ if( nTables >= 64 ) // something fishy?
+ return NULL;
+ for( int i = 0; i < nTables; ++i, p+=16 )
+ {
+ if( p[0]==pTag[0] && p[1]==pTag[1] && p[2]==pTag[2] && p[3]==pTag[3] )
+ {
+ sal_uLong nLength = GetUInt( p + 12 );
+ if( pLength != NULL )
+ *pLength = nLength;
+ const unsigned char* pTable = pBuffer + GetUInt( p + 8 );
+ if( (pTable + nLength) <= (mpFontFile->GetBuffer() + nFileSize) )
+ return pTable;
+ }
+ }
+
+ return NULL;
+}
+
+// -----------------------------------------------------------------------
+
+void FtFontInfo::AnnounceFont( ImplDevFontList* pFontList )
+{
+ ImplFTSFontData* pFD = new ImplFTSFontData( this, maDevFontAttributes );
+ pFontList->Add( pFD );
+}
+
+// =======================================================================
+
+FreetypeManager::FreetypeManager()
+: mnMaxFontId( 0 ), mnNextFontId( 0x1000 )
+{
+ /*FT_Error rcFT =*/ FT_Init_FreeType( &aLibFT );
+
+#ifdef RTLD_DEFAULT // true if a good dlfcn.h header was included
+ // Get version of freetype library to enable workarounds.
+ // Freetype <= 2.0.9 does not have FT_Library_Version().
+ // Using dl_sym() instead of osl_getSymbol() because latter
+ // isn't designed to work with oslModule=NULL
+ void (*pFTLibraryVersion)(FT_Library library,
+ FT_Int *amajor, FT_Int *aminor, FT_Int *apatch);
+ pFTLibraryVersion = (void (*)(FT_Library library,
+ FT_Int *amajor, FT_Int *aminor, FT_Int *apatch))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_Library_Version" );
+
+ pFTNewSize = (FT_Error(*)(FT_Face,FT_Size*))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_New_Size" );
+ pFTActivateSize = (FT_Error(*)(FT_Size))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_Activate_Size" );
+ pFTDoneSize = (FT_Error(*)(FT_Size))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_Done_Size" );
+ pFTEmbolden = (FT_Error(*)(FT_GlyphSlot))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_GlyphSlot_Embolden" );
+ pFTOblique = (FT_Error(*)(FT_GlyphSlot))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_GlyphSlot_Oblique" );
+
+ bEnableSizeFT = (pFTNewSize!=NULL) && (pFTActivateSize!=NULL) && (pFTDoneSize!=NULL);
+
+ FT_Int nMajor = 0, nMinor = 0, nPatch = 0;
+ if( pFTLibraryVersion )
+ pFTLibraryVersion( aLibFT, &nMajor, &nMinor, &nPatch );
+ nFTVERSION = nMajor * 1000 + nMinor * 100 + nPatch;
+
+ // disable embedded bitmaps for Freetype-2.1.3 unless explicitly
+ // requested by env var below because it crashes StarOffice on RH9
+ // reason: double free in freetype's embedded bitmap handling
+ if( nFTVERSION == 2103 )
+ nDefaultPrioEmbedded = 0;
+ // disable artificial emboldening with the Freetype API for older versions
+ if( nFTVERSION < 2110 )
+ pFTEmbolden = NULL;
+
+#else // RTLD_DEFAULT
+ // assume systems where dlsym is not possible use supplied library
+ nFTVERSION = FTVERSION;
+#endif
+
+ // TODO: remove when the priorities are selected by UI
+ char* pEnv;
+ pEnv = ::getenv( "SAL_EMBEDDED_BITMAP_PRIORITY" );
+ if( pEnv )
+ nDefaultPrioEmbedded = pEnv[0] - '0';
+ pEnv = ::getenv( "SAL_ANTIALIASED_TEXT_PRIORITY" );
+ if( pEnv )
+ nDefaultPrioAntiAlias = pEnv[0] - '0';
+ pEnv = ::getenv( "SAL_AUTOHINTING_PRIORITY" );
+ if( pEnv )
+ nDefaultPrioAutoHint = pEnv[0] - '0';
+
+ InitGammaTable();
+ vclFontFileList::get();
+}
+
+// -----------------------------------------------------------------------
+
+void* FreetypeServerFont::GetFtFace() const
+{
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ return maFaceFT;
+}
+
+// -----------------------------------------------------------------------
+
+FreetypeManager::~FreetypeManager()
+{
+ ClearFontList();
+// 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();
+
+ 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( boost::shared_ptr<ImplFontOptions> pFontOptions)
+{
+ mpFontOptions = pFontOptions;
+
+ if (!mpFontOptions)
+ return;
+
+ FontAutoHint eHint = mpFontOptions->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( mpFontOptions->DontUseAntiAlias() )
+ mnPrioAntiAlias = 0;
+ if( mpFontOptions->DontUseEmbeddedBitmaps() )
+ mnPrioEmbedded = 0;
+ if( mpFontOptions->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( mpFontOptions->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;
+}
+
+boost::shared_ptr<ImplFontOptions> FreetypeServerFont::GetFontOptions() const
+{
+ return mpFontOptions;
+}
+
+// -----------------------------------------------------------------------
+
+bool FreetypeServerFont::TestFont() const
+{
+ return mbFaceOk;
+}
+
+// -----------------------------------------------------------------------
+
+FreetypeServerFont::~FreetypeServerFont()
+{
+ if( mpLayoutEngine )
+ delete mpLayoutEngine;
+
+ if( maRecodeConverter )
+ rtl_destroyUnicodeToTextConverter( maRecodeConverter );
+
+ if( maSizeFT )
+ pFTDoneSize( maSizeFT );
+
+ mpFontInfo->ReleaseFaceFT( maFaceFT );
+}
+
+ // -----------------------------------------------------------------------
+
+int FreetypeServerFont::GetEmUnits() const
+{
+ return maFaceFT->units_per_EM;
+}
+
+// -----------------------------------------------------------------------
+
+void FreetypeServerFont::FetchFontMetric( ImplFontMetricData& rTo, long& rFactor ) const
+{
+ static_cast<ImplFontAttributes&>(rTo) = mpFontInfo->GetFontAttributes();
+
+ rTo.mbScalableFont = true;
+ rTo.mbDevice = true;
+ rTo.mbKernableFont = (FT_HAS_KERNING( maFaceFT ) != 0) || mpFontInfo->HasExtraKerning();
+ rTo.mnOrientation = GetFontSelData().mnOrientation;
+
+ //Always consider [star]symbol as symbol fonts
+ if (
+ (rTo.GetFamilyName().EqualsAscii("OpenSymbol")) ||
+ (rTo.GetFamilyName().EqualsAscii("StarSymbol"))
+ )
+ {
+ rTo.mbSymbolFlag = true;
+ }
+
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ rFactor = 0x100;
+
+ rTo.mnWidth = mnWidth;
+
+ const FT_Size_Metrics& rMetrics = maFaceFT->size->metrics;
+ rTo.mnAscent = (+rMetrics.ascender + 32) >> 6;
+#if (FTVERSION < 2000)
+ rTo.mnDescent = (+rMetrics.descender + 32) >> 6;
+#else
+ rTo.mnDescent = (-rMetrics.descender + 32) >> 6;
+#endif
+ rTo.mnIntLeading = ((rMetrics.height + 32) >> 6) - (rTo.mnAscent + rTo.mnDescent);
+ rTo.mnSlant = 0;
+
+ const TT_OS2* pOS2 = (const TT_OS2*)FT_Get_Sfnt_Table( maFaceFT, ft_sfnt_os2 );
+ const TT_HoriHeader* pHHEA = (const TT_HoriHeader*)FT_Get_Sfnt_Table( maFaceFT, ft_sfnt_hhea );
+ if( pOS2 && (pOS2->version != 0xFFFF) )
+ {
+ // map the panose info from the OS2 table to their VCL counterparts
+ switch( pOS2->panose[0] )
+ {
+ case 1: rTo.meFamily = FAMILY_ROMAN; break;
+ case 2: rTo.meFamily = FAMILY_SWISS; break;
+ case 3: rTo.meFamily = FAMILY_MODERN; break;
+ case 4: rTo.meFamily = FAMILY_SCRIPT; break;
+ case 5: rTo.meFamily = FAMILY_DECORATIVE; break;
+ // TODO: is it reasonable to override the attribute with DONTKNOW?
+ case 0: // fall through
+ default: rTo.meFamilyType = FAMILY_DONTKNOW; break;
+ }
+
+ switch( pOS2->panose[3] )
+ {
+ case 2: // fall through
+ case 3: // fall through
+ case 4: // fall through
+ case 5: // fall through
+ case 6: // fall through
+ case 7: // fall through
+ case 8: rTo.mePitch = PITCH_VARIABLE; break;
+ case 9: rTo.mePitch = PITCH_FIXED; break;
+ // TODO: is it reasonable to override the attribute with DONTKNOW?
+ case 0: // fall through
+ case 1: // fall through
+ default: rTo.mePitch = PITCH_DONTKNOW; break;
+ }
+
+ // #108862# sanity check, some fonts treat descent as signed !!!
+ int nDescent = pOS2->usWinDescent;
+ if( nDescent > 5*maFaceFT->units_per_EM )
+ nDescent = (short)pOS2->usWinDescent; // interpret it as signed!
+
+ const double fScale = (double)GetFontSelData().mnHeight / maFaceFT->units_per_EM;
+ if( pOS2->usWinAscent || pOS2->usWinDescent ) // #i30551#
+ {
+ rTo.mnAscent = (long)( +pOS2->usWinAscent * fScale + 0.5 );
+ rTo.mnDescent = (long)( +nDescent * fScale + 0.5 );
+ rTo.mnIntLeading = (long)( (+pOS2->usWinAscent + pOS2->usWinDescent - maFaceFT->units_per_EM) * fScale + 0.5 );
+ }
+ rTo.mnExtLeading = 0;
+ if( (pHHEA != NULL) && (pOS2->usWinAscent || pOS2->usWinDescent) )
+ {
+ int nExtLeading = pHHEA->Line_Gap;
+ nExtLeading -= (pOS2->usWinAscent + pOS2->usWinDescent);
+ nExtLeading += (pHHEA->Ascender - pHHEA->Descender);
+ if( nExtLeading > 0 )
+ rTo.mnExtLeading = (long)(nExtLeading * fScale + 0.5);
+ }
+
+ // Check for CJK capabilities of the current font
+ // #107888# workaround for Asian...
+ // TODO: remove when ExtLeading fully implemented
+ sal_Bool bCJKCapable = ((pOS2->ulUnicodeRange2 & 0x2DF00000) != 0);
+
+ if ( bCJKCapable && (pOS2->usWinAscent || pOS2->usWinDescent) )
+ {
+ rTo.mnIntLeading += rTo.mnExtLeading;
+
+ // #109280# The line height for Asian fonts is too small.
+ // Therefore we add half of the external leading to the
+ // ascent, the other half is added to the descent.
+ const long nHalfTmpExtLeading = rTo.mnExtLeading / 2;
+ const long nOtherHalfTmpExtLeading = rTo.mnExtLeading -
+ nHalfTmpExtLeading;
+
+ // #110641# external leading for Asian fonts.
+ // The factor 0.3 has been verified during experiments.
+ const long nCJKExtLeading = (long)(0.30 * (rTo.mnAscent + rTo.mnDescent));
+
+ if ( nCJKExtLeading > rTo.mnExtLeading )
+ rTo.mnExtLeading = nCJKExtLeading - rTo.mnExtLeading;
+ else
+ rTo.mnExtLeading = 0;
+
+ rTo.mnAscent += nHalfTmpExtLeading;
+ rTo.mnDescent += nOtherHalfTmpExtLeading;
+ }
+ }
+
+ // initialize kashida width
+ // TODO: what if there are different versions of this glyph available
+ rTo.mnMinKashida = rTo.mnAscent / 4; // a reasonable default
+ const int nKashidaGlyphId = GetRawGlyphIndex( 0x0640 );
+ if( nKashidaGlyphId )
+ {
+ GlyphData aGlyphData;
+ InitGlyphData( nKashidaGlyphId, aGlyphData );
+ rTo.mnMinKashida = aGlyphData.GetMetric().GetCharWidth();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+static inline void SplitGlyphFlags( const FreetypeServerFont& rFont, 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, sal_True );
+ if( rc != FT_Err_Ok )
+ {
+ FT_Done_Glyph( pGlyphFT );
+ return false;
+ }
+ }
+
+ const FT_BitmapGlyph pBmpGlyphFT = reinterpret_cast<const FT_BitmapGlyph>(pGlyphFT);
+ // NOTE: autohinting in FT<=2.0.2 miscalculates the offsets below by +-1
+ rRawBitmap.mnXOffset = +pBmpGlyphFT->left;
+ rRawBitmap.mnYOffset = -pBmpGlyphFT->top;
+
+ const FT_Bitmap& rBitmapFT = pBmpGlyphFT->bitmap;
+ rRawBitmap.mnHeight = rBitmapFT.rows;
+ rRawBitmap.mnBitCount = 1;
+ if( mbArtBold && !pFTEmbolden )
+ {
+ rRawBitmap.mnWidth = rBitmapFT.width + 1;
+ int nLineBytes = (rRawBitmap.mnWidth + 7) >> 3;
+ rRawBitmap.mnScanlineSize = (nLineBytes > rBitmapFT.pitch) ? nLineBytes : rBitmapFT.pitch;
+ }
+ else
+ {
+ rRawBitmap.mnWidth = rBitmapFT.width;
+ rRawBitmap.mnScanlineSize = rBitmapFT.pitch;
+ }
+
+ const sal_uLong nNeededSize = rRawBitmap.mnScanlineSize * rRawBitmap.mnHeight;
+
+ if( rRawBitmap.mnAllocated < nNeededSize )
+ {
+ delete[] rRawBitmap.mpBits;
+ rRawBitmap.mnAllocated = 2*nNeededSize;
+ rRawBitmap.mpBits = new unsigned char[ rRawBitmap.mnAllocated ];
+ }
+
+ if( !mbArtBold || pFTEmbolden )
+ {
+ memcpy( rRawBitmap.mpBits, rBitmapFT.buffer, nNeededSize );
+ }
+ else
+ {
+ memset( rRawBitmap.mpBits, 0, nNeededSize );
+ const unsigned char* pSrcLine = rBitmapFT.buffer;
+ unsigned char* pDstLine = rRawBitmap.mpBits;
+ for( int h = rRawBitmap.mnHeight; --h >= 0; )
+ {
+ memcpy( pDstLine, pSrcLine, rBitmapFT.pitch );
+ pDstLine += rRawBitmap.mnScanlineSize;
+ pSrcLine += rBitmapFT.pitch;
+ }
+
+ unsigned char* p = rRawBitmap.mpBits;
+ for( sal_uLong y=0; y < rRawBitmap.mnHeight; y++ )
+ {
+ unsigned char nLastByte = 0;
+ for( sal_uLong x=0; x < rRawBitmap.mnScanlineSize; x++ )
+ {
+ unsigned char nTmp = p[x] << 7;
+ p[x] |= (p[x] >> 1) | nLastByte;
+ nLastByte = nTmp;
+ }
+ p += rRawBitmap.mnScanlineSize;
+ }
+ }
+
+ FT_Done_Glyph( pGlyphFT );
+
+ // special case for 0/90/180/270 degree orientation
+ switch( nAngle )
+ {
+ case -900:
+ case +900:
+ case +1800:
+ case +2700:
+ rRawBitmap.Rotate( nAngle );
+ break;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+bool FreetypeServerFont::GetGlyphBitmap8( 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, sal_True );
+ if( rc != FT_Err_Ok )
+ {
+ FT_Done_Glyph( pGlyphFT );
+ return false;
+ }
+ }
+
+ const FT_BitmapGlyph pBmpGlyphFT = reinterpret_cast<const FT_BitmapGlyph>(pGlyphFT);
+ rRawBitmap.mnXOffset = +pBmpGlyphFT->left;
+ rRawBitmap.mnYOffset = -pBmpGlyphFT->top;
+
+ const FT_Bitmap& rBitmapFT = pBmpGlyphFT->bitmap;
+ rRawBitmap.mnHeight = rBitmapFT.rows;
+ rRawBitmap.mnWidth = rBitmapFT.width;
+ rRawBitmap.mnBitCount = 8;
+ rRawBitmap.mnScanlineSize = bEmbedded ? rBitmapFT.width : rBitmapFT.pitch;
+ if( mbArtBold && !pFTEmbolden )
+ {
+ ++rRawBitmap.mnWidth;
+ ++rRawBitmap.mnScanlineSize;
+ }
+ rRawBitmap.mnScanlineSize = (rRawBitmap.mnScanlineSize + 3) & -4;
+
+ const sal_uLong nNeededSize = rRawBitmap.mnScanlineSize * rRawBitmap.mnHeight;
+ if( rRawBitmap.mnAllocated < nNeededSize )
+ {
+ delete[] rRawBitmap.mpBits;
+ rRawBitmap.mnAllocated = 2*nNeededSize;
+ rRawBitmap.mpBits = new unsigned char[ rRawBitmap.mnAllocated ];
+ }
+
+ const unsigned char* pSrc = rBitmapFT.buffer;
+ unsigned char* pDest = rRawBitmap.mpBits;
+ if( !bEmbedded )
+ {
+ for( int y = rRawBitmap.mnHeight, x; --y >= 0 ; )
+ {
+ for( x = 0; x < rBitmapFT.width; ++x )
+ *(pDest++) = *(pSrc++);
+ for(; x < int(rRawBitmap.mnScanlineSize); ++x )
+ *(pDest++) = 0;
+ }
+ }
+ else
+ {
+ for( int y = rRawBitmap.mnHeight, x; --y >= 0 ; )
+ {
+ unsigned char nSrc = 0;
+ for( x = 0; x < rBitmapFT.width; ++x, nSrc+=nSrc )
+ {
+ if( (x & 7) == 0 )
+ nSrc = *(pSrc++);
+ *(pDest++) = (0x7F - nSrc) >> 8;
+ }
+ for(; x < int(rRawBitmap.mnScanlineSize); ++x )
+ *(pDest++) = 0;
+ }
+ }
+
+ if( mbArtBold && !pFTEmbolden )
+ {
+ // overlay with glyph image shifted by one left pixel
+ unsigned char* p = rRawBitmap.mpBits;
+ for( sal_uLong y=0; y < rRawBitmap.mnHeight; y++ )
+ {
+ unsigned char nLastByte = 0;
+ for( sal_uLong x=0; x < rRawBitmap.mnWidth; x++ )
+ {
+ unsigned char nTmp = p[x];
+ p[x] |= p[x] | nLastByte;
+ nLastByte = nTmp;
+ }
+ p += rRawBitmap.mnScanlineSize;
+ }
+ }
+
+ if( !bEmbedded && mbUseGamma )
+ {
+ unsigned char* p = rRawBitmap.mpBits;
+ for( sal_uLong y=0; y < rRawBitmap.mnHeight; y++ )
+ {
+ for( sal_uLong x=0; x < rRawBitmap.mnWidth; x++ )
+ {
+ p[x] = aGammaTable[ p[x] ];
+ }
+ p += rRawBitmap.mnScanlineSize;
+ }
+ }
+
+ FT_Done_Glyph( pGlyphFT );
+
+ // special case for 0/90/180/270 degree orientation
+ switch( nAngle )
+ {
+ case -900:
+ case +900:
+ case +1800:
+ case +2700:
+ rRawBitmap.Rotate( nAngle );
+ break;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+// determine unicode ranges in font
+// -----------------------------------------------------------------------
+
+const ImplFontCharMap* FreetypeServerFont::GetImplFontCharMap( void ) const
+{
+ const ImplFontCharMap* pIFCMap = mpFontInfo->GetImplFontCharMap();
+ return pIFCMap;
+}
+
+const ImplFontCharMap* FtFontInfo::GetImplFontCharMap( void )
+{
+ // check if the charmap is already cached
+ if( mpFontCharMap )
+ return mpFontCharMap;
+
+ // get the charmap and cache it
+ CmapResult aCmapResult;
+ bool bOK = GetFontCodeRanges( aCmapResult );
+ if( bOK )
+ mpFontCharMap = new ImplFontCharMap( aCmapResult );
+ else
+ mpFontCharMap = ImplFontCharMap::GetDefaultMap();
+ mpFontCharMap->AddReference();
+ return mpFontCharMap;
+}
+
+// TODO: merge into method GetFontCharMap()
+bool FtFontInfo::GetFontCodeRanges( CmapResult& rResult ) const
+{
+ rResult.mbSymbolic = IsSymbolFont();
+
+ // TODO: is the full CmapResult needed on platforms calling this?
+ if( FT_IS_SFNT( maFaceFT ) )
+ {
+ sal_uLong nLength = 0;
+ const unsigned char* pCmap = GetTable( "cmap", &nLength );
+ if( pCmap && (nLength > 0) )
+ if( ParseCMAP( pCmap, nLength, rResult ) )
+ return true;
+ }
+
+ typedef std::vector<sal_uInt32> U32Vector;
+ U32Vector aCodes;
+
+ // FT's coverage is available since FT>=2.1.0 (OOo-baseline>=2.1.4 => ok)
+ aCodes.reserve( 0x1000 );
+ FT_UInt nGlyphIndex;
+ for( sal_uInt32 cCode = FT_Get_First_Char( maFaceFT, &nGlyphIndex );; )
+ {
+ if( !nGlyphIndex )
+ break;
+ aCodes.push_back( cCode ); // first code inside range
+ sal_uInt32 cNext = cCode;
+ do cNext = FT_Get_Next_Char( maFaceFT, cCode, &nGlyphIndex ); while( cNext == ++cCode );
+ aCodes.push_back( cCode ); // first code outside range
+ cCode = cNext;
+ }
+
+ const int nCount = aCodes.size();
+ if( !nCount) {
+ if( !rResult.mbSymbolic )
+ return false;
+
+ // we usually get here for Type1 symbol fonts
+ aCodes.push_back( 0xF020 );
+ aCodes.push_back( 0xF100 );
+ }
+
+ sal_uInt32* pCodes = new sal_uInt32[ nCount ];
+ for( int i = 0; i < nCount; ++i )
+ pCodes[i] = aCodes[i];
+ rResult.mpRangeCodes = pCodes;
+ rResult.mnRangeCount = nCount / 2;
+ return true;
+}
+
+bool FreetypeServerFont::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
+{
+ bool bRet = false;
+
+ sal_uLong nLength = 0;
+ // load GSUB table
+ const FT_Byte* pGSUB = mpFontInfo->GetTable("GSUB", &nLength);
+ if (pGSUB)
+ vcl::getTTScripts(rFontCapabilities.maGSUBScriptTags, pGSUB, nLength);
+
+ // load OS/2 table
+ const FT_Byte* pOS2 = mpFontInfo->GetTable("OS/2", &nLength);
+ if (pOS2)
+ {
+ bRet = vcl::getTTCoverage(
+ rFontCapabilities.maUnicodeRange,
+ rFontCapabilities.maCodePageRange,
+ pOS2, nLength);
+ }
+
+ return bRet;
+}
+
+// -----------------------------------------------------------------------
+// kerning stuff
+// -----------------------------------------------------------------------
+
+int FreetypeServerFont::GetGlyphKernValue( int nGlyphLeft, int nGlyphRight ) const
+{
+ // if no kerning info is available from Freetype
+ // then we may have to use extra info provided by e.g. psprint
+ if( !FT_HAS_KERNING( maFaceFT ) || !FT_IS_SFNT( maFaceFT ) )
+ {
+ int nKernVal = mpFontInfo->GetExtraGlyphKernValue( nGlyphLeft, nGlyphRight );
+ if( !nKernVal )
+ return 0;
+ // scale the kern value to match the font size
+ const ImplFontSelectData& rFSD = GetFontSelData();
+ nKernVal *= rFSD.mnWidth ? rFSD.mnWidth : rFSD.mnHeight;
+ return (nKernVal + 500) / 1000;
+ }
+
+ // when font faces of different sizes share the same maFaceFT
+ // then we have to make sure that it uses the correct maSizeFT
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ // use Freetype's kerning info
+ FT_Vector aKernVal;
+ FT_Error rcFT = FT_Get_Kerning( maFaceFT, nGlyphLeft, nGlyphRight,
+ FT_KERNING_DEFAULT, &aKernVal );
+ int nResult = (rcFT == FT_Err_Ok) ? (aKernVal.x + 32) >> 6 : 0;
+ return nResult;
+}
+
+// -----------------------------------------------------------------------
+
+sal_uLong FreetypeServerFont::GetKernPairs( ImplKernPairData** ppKernPairs ) const
+{
+ // if no kerning info is available in the font file
+ *ppKernPairs = NULL;
+ if( !FT_HAS_KERNING( maFaceFT ) || !FT_IS_SFNT( maFaceFT ) )
+ {
+ // then we have may have extra kerning info from e.g. psprint
+ int nCount = mpFontInfo->GetExtraKernPairs( ppKernPairs );
+ // scale the kern values to match the font size
+ const ImplFontSelectData& rFSD = GetFontSelData();
+ int nFontWidth = rFSD.mnWidth ? rFSD.mnWidth : rFSD.mnHeight;
+ ImplKernPairData* pKernPair = *ppKernPairs;
+ for( int i = nCount; --i >= 0; ++pKernPair )
+ {
+ long& rVal = pKernPair->mnKern;
+ rVal = ((rVal * nFontWidth) + 500) / 1000;
+ }
+ return nCount;
+ }
+
+ // when font faces of different sizes share the same maFaceFT
+ // then we have to make sure that it uses the correct maSizeFT
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ // first figure out which glyph pairs are involved in kerning
+ sal_uLong nKernLength = 0;
+ const FT_Byte* const pKern = mpFontInfo->GetTable( "kern", &nKernLength );
+ if( !pKern )
+ return 0;
+
+ // combine TTF/OTF tables from the font file to build a vector of
+ // unicode kerning pairs using Freetype's glyph kerning calculation
+ // for the kerning value
+
+ // TODO: is it worth to share the glyph->unicode mapping between
+ // different instances of the same font face?
+
+ typedef std::vector<ImplKernPairData> KernVector;
+ KernVector aKernGlyphVector;
+ ImplKernPairData aKernPair;
+ aKernPair.mnKern = 0; // To prevent "is used uninitialized" warning...
+
+ const FT_Byte* pBuffer = pKern;
+ sal_uLong nVersion = GetUShort( pBuffer+0 );
+ sal_uInt16 nTableCnt = GetUShort( pBuffer+2 );
+
+ // Microsoft/Old TrueType style kern table
+ if ( nVersion == 0 )
+ {
+ pBuffer += 4;
+
+ for( sal_uInt16 nTableIdx = 0; nTableIdx < nTableCnt; ++nTableIdx )
+ {
+ // sal_uInt16 nSubVersion = GetUShort( pBuffer+0 );
+ // sal_uInt16 nSubLength = GetUShort( pBuffer+2 );
+ sal_uInt16 nSubCoverage = GetUShort( pBuffer+4 );
+ pBuffer += 6;
+ if( (nSubCoverage&0x03) != 0x01 ) // no interest in minimum info here
+ continue;
+ switch( nSubCoverage >> 8 )
+ {
+ case 0: // version 0, kerning format 0
+ {
+ sal_uInt16 nPairs = GetUShort( pBuffer );
+ pBuffer += 8; // skip search hints
+ aKernGlyphVector.reserve( aKernGlyphVector.size() + nPairs );
+ for( int i = 0; i < nPairs; ++i )
+ {
+ aKernPair.mnChar1 = GetUShort( pBuffer+0 );
+ aKernPair.mnChar2 = GetUShort( pBuffer+2 );
+ //long nUnscaledKern= GetSShort( pBuffer );
+ pBuffer += 6;
+ aKernGlyphVector.push_back( aKernPair );
+ }
+ }
+ break;
+
+ case 2: // version 0, kerning format 2
+ {
+ const FT_Byte* pSubTable = pBuffer;
+ //sal_uInt16 nRowWidth = GetUShort( pBuffer+0 );
+ sal_uInt16 nOfsLeft = GetUShort( pBuffer+2 );
+ sal_uInt16 nOfsRight = GetUShort( pBuffer+4 );
+ sal_uInt16 nOfsArray = GetUShort( pBuffer+6 );
+ pBuffer += 8;
+
+ const FT_Byte* pTmp = pSubTable + nOfsLeft;
+ sal_uInt16 nFirstLeft = GetUShort( pTmp+0 );
+ sal_uInt16 nLastLeft = GetUShort( pTmp+2 ) + nFirstLeft - 1;
+
+ pTmp = pSubTable + nOfsRight;
+ sal_uInt16 nFirstRight = GetUShort( pTmp+0 );
+ sal_uInt16 nLastRight = GetUShort( pTmp+2 ) + nFirstRight - 1;
+
+ sal_uLong nPairs = (sal_uLong)(nLastLeft - nFirstLeft + 1) * (nLastRight - nFirstRight + 1);
+ aKernGlyphVector.reserve( aKernGlyphVector.size() + nPairs );
+
+ pTmp = pSubTable + nOfsArray;
+ for( int nLeft = nFirstLeft; nLeft < nLastLeft; ++nLeft )
+ {
+ aKernPair.mnChar1 = nLeft;
+ for( int nRight = 0; nRight < nLastRight; ++nRight )
+ {
+ if( GetUShort( pTmp ) != 0 )
+ {
+ aKernPair.mnChar2 = nRight;
+ aKernGlyphVector.push_back( aKernPair );
+ }
+ pTmp += 2;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // Apple New style kern table
+ pBuffer = pKern;
+ nVersion = NEXT_U32( pBuffer );
+ nTableCnt = NEXT_U32( pBuffer );
+ if ( nVersion == 0x00010000 )
+ {
+ for( sal_uInt16 nTableIdx = 0; nTableIdx < nTableCnt; ++nTableIdx )
+ {
+ /*sal_uLong nLength =*/ NEXT_U32( pBuffer );
+ sal_uInt16 nCoverage = NEXT_U16( pBuffer );
+ /*sal_uInt16 nTupleIndex =*/ NEXT_U16( pBuffer );
+
+ // Kerning sub-table format, 0 through 3
+ sal_uInt8 nSubTableFormat = nCoverage & 0x00FF;
+
+ switch( nSubTableFormat )
+ {
+ case 0: // version 0, kerning format 0
+ {
+ sal_uInt16 nPairs = NEXT_U16( pBuffer );
+ pBuffer += 6; // skip search hints
+ aKernGlyphVector.reserve( aKernGlyphVector.size() + nPairs );
+ for( int i = 0; i < nPairs; ++i )
+ {
+ aKernPair.mnChar1 = NEXT_U16( pBuffer );
+ aKernPair.mnChar2 = NEXT_U16( pBuffer );
+ /*long nUnscaledKern=*/ NEXT_S16( pBuffer );
+ aKernGlyphVector.push_back( aKernPair );
+ }
+ }
+ break;
+
+ case 2: // version 0, kerning format 2
+ {
+ const FT_Byte* pSubTable = pBuffer;
+ /*sal_uInt16 nRowWidth =*/ NEXT_U16( pBuffer );
+ sal_uInt16 nOfsLeft = NEXT_U16( pBuffer );
+ sal_uInt16 nOfsRight = NEXT_U16( pBuffer );
+ sal_uInt16 nOfsArray = NEXT_U16( pBuffer );
+
+ const FT_Byte* pTmp = pSubTable + nOfsLeft;
+ sal_uInt16 nFirstLeft = NEXT_U16( pTmp );
+ sal_uInt16 nLastLeft = NEXT_U16( pTmp ) + nFirstLeft - 1;
+
+ pTmp = pSubTable + nOfsRight;
+ sal_uInt16 nFirstRight = NEXT_U16( pTmp );
+ sal_uInt16 nLastRight = NEXT_U16( pTmp ) + nFirstRight - 1;
+
+ sal_uLong nPairs = (sal_uLong)(nLastLeft - nFirstLeft + 1) * (nLastRight - nFirstRight + 1);
+ aKernGlyphVector.reserve( aKernGlyphVector.size() + nPairs );
+
+ pTmp = pSubTable + nOfsArray;
+ for( int nLeft = nFirstLeft; nLeft < nLastLeft; ++nLeft )
+ {
+ aKernPair.mnChar1 = nLeft;
+ for( int nRight = 0; nRight < nLastRight; ++nRight )
+ {
+ if( NEXT_S16( pTmp ) != 0 )
+ {
+ aKernPair.mnChar2 = nRight;
+ aKernGlyphVector.push_back( aKernPair );
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ fprintf( stderr, "gcach_ftyp.cxx: Found unsupported Apple-style kern subtable type %d.\n", nSubTableFormat );
+ break;
+ }
+ }
+ }
+
+ // now create VCL's ImplKernPairData[] format for all glyph pairs
+ sal_uLong nKernCount = aKernGlyphVector.size();
+ if( nKernCount )
+ {
+ // prepare glyphindex to character mapping
+ // TODO: this is needed to support VCL's existing kerning infrastructure,
+ // eliminate it up by redesigning kerning infrastructure to work with glyph indizes
+ typedef boost::unordered_multimap<sal_uInt16,sal_Unicode> Cmap;
+ Cmap aCmap;
+ for( sal_Unicode aChar = 0x0020; aChar < 0xFFFE; ++aChar )
+ {
+ sal_uInt16 nGlyphIndex = GetGlyphIndex( aChar );
+ if( nGlyphIndex )
+ aCmap.insert( Cmap::value_type( nGlyphIndex, aChar ) );
+ }
+
+ // translate both glyph indizes in kerning pairs to characters
+ // problem is that these are 1:n mappings...
+ KernVector aKernCharVector;
+ aKernCharVector.reserve( nKernCount );
+ KernVector::iterator it;
+ for( it = aKernGlyphVector.begin(); it != aKernGlyphVector.end(); ++it )
+ {
+ FT_Vector aKernVal;
+ FT_Error rcFT = FT_Get_Kerning( maFaceFT, it->mnChar1, it->mnChar2,
+ FT_KERNING_DEFAULT, &aKernVal );
+ aKernPair.mnKern = aKernVal.x >> 6;
+ if( (aKernPair.mnKern == 0) || (rcFT != FT_Err_Ok) )
+ continue;
+
+ typedef std::pair<Cmap::iterator,Cmap::iterator> CPair;
+ const CPair p1 = aCmap.equal_range( it->mnChar1 );
+ const CPair p2 = aCmap.equal_range( it->mnChar2 );
+ for( Cmap::const_iterator i1 = p1.first; i1 != p1.second; ++i1 )
+ {
+ aKernPair.mnChar1 = (*i1).second;
+ for( Cmap::const_iterator i2 = p2.first; i2 != p2.second; ++i2 )
+ {
+ aKernPair.mnChar2 = (*i2).second;
+ aKernCharVector.push_back( aKernPair );
+ }
+ }
+ }
+
+ // now move the resulting vector into VCL's ImplKernPairData[] format
+ nKernCount = aKernCharVector.size();
+ ImplKernPairData* pTo = new ImplKernPairData[ nKernCount ];
+ *ppKernPairs = pTo;
+ for( it = aKernCharVector.begin(); it != aKernCharVector.end(); ++it, ++pTo )
+ {
+ pTo->mnChar1 = it->mnChar1;
+ pTo->mnChar2 = it->mnChar2;
+ pTo->mnKern = it->mnKern;
+ }
+ }
+
+ return nKernCount;
+}
+
+// -----------------------------------------------------------------------
+// outline stuff
+// -----------------------------------------------------------------------
+
+class PolyArgs
+{
+public:
+ PolyArgs( PolyPolygon& rPolyPoly, sal_uInt16 nMaxPoints );
+ ~PolyArgs();
+
+ void AddPoint( long nX, long nY, PolyFlags);
+ void ClosePolygon();
+
+ long GetPosX() const { return maPosition.x;}
+ long GetPosY() const { return maPosition.y;}
+
+private:
+ PolyPolygon& mrPolyPoly;
+
+ Point* mpPointAry;
+ sal_uInt8* mpFlagAry;
+
+ FT_Vector maPosition;
+ sal_uInt16 mnMaxPoints;
+ sal_uInt16 mnPoints;
+ sal_uInt16 mnPoly;
+ long mnHeight;
+ bool bHasOffline;
+};
+
+// -----------------------------------------------------------------------
+
+PolyArgs::PolyArgs( PolyPolygon& rPolyPoly, sal_uInt16 nMaxPoints )
+: mrPolyPoly(rPolyPoly),
+ mnMaxPoints(nMaxPoints),
+ mnPoints(0),
+ mnPoly(0),
+ mnHeight(0),
+ bHasOffline(false)
+{
+ mpPointAry = new Point[ mnMaxPoints ];
+ mpFlagAry = new sal_uInt8 [ mnMaxPoints ];
+}
+
+// -----------------------------------------------------------------------
+
+
+PolyArgs::~PolyArgs()
+{
+ delete[] mpFlagAry;
+ delete[] mpPointAry;
+}
+
+// -----------------------------------------------------------------------
+
+void PolyArgs::AddPoint( long nX, long nY, PolyFlags aFlag )
+{
+ DBG_ASSERT( (mnPoints < mnMaxPoints), "FTGlyphOutline: AddPoint overflow!" );
+ if( mnPoints >= mnMaxPoints )
+ return;
+
+ maPosition.x = nX;
+ maPosition.y = nY;
+ mpPointAry[ mnPoints ] = Point( nX, nY );
+ mpFlagAry[ mnPoints++ ]= aFlag;
+ bHasOffline |= (aFlag != POLY_NORMAL);
+}
+
+// -----------------------------------------------------------------------
+
+void PolyArgs::ClosePolygon()
+{
+ if( !mnPoly++ )
+ return;
+
+ // freetype seems to always close the polygon with an ON_CURVE point
+ // PolyPoly wants to close the polygon itself => remove last point
+ DBG_ASSERT( (mnPoints >= 2), "FTGlyphOutline: PolyFinishNum failed!" );
+ --mnPoints;
+ DBG_ASSERT( (mpPointAry[0]==mpPointAry[mnPoints]), "FTGlyphOutline: PolyFinishEq failed!" );
+ DBG_ASSERT( (mpFlagAry[0]==POLY_NORMAL), "FTGlyphOutline: PolyFinishFE failed!" );
+ DBG_ASSERT( (mpFlagAry[mnPoints]==POLY_NORMAL), "FTGlyphOutline: PolyFinishFS failed!" );
+
+ Polygon aPoly( mnPoints, mpPointAry, (bHasOffline ? mpFlagAry : NULL) );
+
+ // #i35928#
+ // This may be a invalid polygons, e.g. the last point is a control point.
+ // So close the polygon (and add the first point again) if the last point
+ // is a control point or different from first.
+ // #i48298#
+ // Now really duplicating the first point, to close or correct the
+ // polygon. Also no longer duplicating the flags, but enforcing
+ // POLY_NORMAL for the newly added last point.
+ const sal_uInt16 nPolySize(aPoly.GetSize());
+ if(nPolySize)
+ {
+ if((aPoly.HasFlags() && POLY_CONTROL == aPoly.GetFlags(nPolySize - 1))
+ || (aPoly.GetPoint(nPolySize - 1) != aPoly.GetPoint(0)))
+ {
+ aPoly.SetSize(nPolySize + 1);
+ aPoly.SetPoint(aPoly.GetPoint(0), nPolySize);
+
+ if(aPoly.HasFlags())
+ {
+ aPoly.SetFlags(nPolySize, POLY_NORMAL);
+ }
+ }
+ }
+
+ mrPolyPoly.Insert( aPoly );
+ mnPoints = 0;
+ bHasOffline = false;
+}
+
+// -----------------------------------------------------------------------
+
+extern "C" {
+
+// TODO: wait till all compilers accept that calling conventions
+// for functions are the same independent of implementation constness,
+// then uncomment the const-tokens in the function interfaces below
+static int FT_move_to( FT_Vector_CPtr p0, void* vpPolyArgs )
+{
+ PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
+
+ // move_to implies a new polygon => finish old polygon first
+ rA.ClosePolygon();
+
+ rA.AddPoint( p0->x, p0->y, POLY_NORMAL );
+ return 0;
+}
+
+static int FT_line_to( FT_Vector_CPtr p1, void* vpPolyArgs )
+{
+ PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
+ rA.AddPoint( p1->x, p1->y, POLY_NORMAL );
+ return 0;
+}
+
+static int FT_conic_to( FT_Vector_CPtr p1, FT_Vector_CPtr p2, void* vpPolyArgs )
+{
+ PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
+
+ // VCL's Polygon only knows cubic beziers
+ const long nX1 = (2 * rA.GetPosX() + 4 * p1->x + 3) / 6;
+ const long nY1 = (2 * rA.GetPosY() + 4 * p1->y + 3) / 6;
+ rA.AddPoint( nX1, nY1, POLY_CONTROL );
+
+ const long nX2 = (2 * p2->x + 4 * p1->x + 3) / 6;
+ const long nY2 = (2 * p2->y + 4 * p1->y + 3) / 6;
+ rA.AddPoint( nX2, nY2, POLY_CONTROL );
+
+ rA.AddPoint( p2->x, p2->y, POLY_NORMAL );
+ return 0;
+}
+
+static int FT_cubic_to( FT_Vector_CPtr p1, FT_Vector_CPtr p2, FT_Vector_CPtr p3, void* vpPolyArgs )
+{
+ PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
+ rA.AddPoint( p1->x, p1->y, POLY_CONTROL );
+ rA.AddPoint( p2->x, p2->y, POLY_CONTROL );
+ rA.AddPoint( p3->x, p3->y, POLY_NORMAL );
+ return 0;
+}
+
+} // extern "C"
+
+// -----------------------------------------------------------------------
+
+bool FreetypeServerFont::GetGlyphOutline( 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<sal_uLong> ReqFeatureTagList;
+ ReqFeatureTagList aReqFeatureTagList;
+ if( rFSD.mbVertical )
+ aReqFeatureTagList.push_back( MKTAG("vert") );
+ sal_uLong nRequestedScript = 0; //MKTAG("hani");//### TODO: where to get script?
+ sal_uLong nRequestedLangsys = 0; //MKTAG("ZHT"); //### TODO: where to get langsys?
+ // TODO: request more features depending on script and language system
+
+ if( aReqFeatureTagList.size() == 0) // nothing to do
+ return true;
+
+ // load GSUB table into memory
+ sal_uLong nLength = 0;
+ const FT_Byte* const pGsubBase = mpFontInfo->GetTable( "GSUB", &nLength );
+ if( !pGsubBase )
+ return false;
+
+ // parse GSUB header
+ const FT_Byte* pGsubHeader = pGsubBase;
+ const sal_uInt16 nOfsScriptList = GetUShort( pGsubHeader+4 );
+ const sal_uInt16 nOfsFeatureTable = GetUShort( pGsubHeader+6 );
+ const sal_uInt16 nOfsLookupList = GetUShort( pGsubHeader+8 );
+ pGsubHeader += 10;
+
+ typedef std::vector<sal_uInt16> UshortList;
+ UshortList aFeatureIndexList;
+ UshortList aFeatureOffsetList;
+
+ // parse Script Table
+ const FT_Byte* pScriptHeader = pGsubBase + nOfsScriptList;
+ const sal_uInt16 nCntScript = GetUShort( pScriptHeader+0 );
+ pScriptHeader += 2;
+ for( sal_uInt16 nScriptIndex = 0; nScriptIndex < nCntScript; ++nScriptIndex )
+ {
+ const sal_uLong nScriptTag = GetUInt( pScriptHeader+0 ); // e.g. hani/arab/kana/hang
+ const sal_uInt16 nOfsScriptTable= GetUShort( pScriptHeader+4 );
+ pScriptHeader += 6; //###
+ if( (nScriptTag != nRequestedScript) && (nRequestedScript != 0) )
+ continue;
+
+ const FT_Byte* pScriptTable = pGsubBase + nOfsScriptList + nOfsScriptTable;
+ const sal_uInt16 nDefaultLangsysOfs = GetUShort( pScriptTable+0 );
+ const sal_uInt16 nCntLangSystem = GetUShort( pScriptTable+2 );
+ pScriptTable += 4;
+ sal_uInt16 nLangsysOffset = 0;
+
+ for( sal_uInt16 nLangsysIndex = 0; nLangsysIndex < nCntLangSystem; ++nLangsysIndex )
+ {
+ const sal_uLong nTag = GetUInt( pScriptTable+0 ); // e.g. KOR/ZHS/ZHT/JAN
+ const sal_uInt16 nOffset= GetUShort( pScriptTable+4 );
+ pScriptTable += 6;
+ if( (nTag != nRequestedLangsys) && (nRequestedLangsys != 0) )
+ continue;
+ nLangsysOffset = nOffset;
+ break;
+ }
+
+ if( (nDefaultLangsysOfs != 0) && (nDefaultLangsysOfs != nLangsysOffset) )
+ {
+ const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nDefaultLangsysOfs;
+ const sal_uInt16 nReqFeatureIdx = GetUShort( pLangSys+2 );
+ const sal_uInt16 nCntFeature = GetUShort( pLangSys+4 );
+ pLangSys += 6;
+ aFeatureIndexList.push_back( nReqFeatureIdx );
+ for( sal_uInt16 i = 0; i < nCntFeature; ++i )
+ {
+ const sal_uInt16 nFeatureIndex = GetUShort( pLangSys );
+ pLangSys += 2;
+ aFeatureIndexList.push_back( nFeatureIndex );
+ }
+ }
+
+ if( nLangsysOffset != 0 )
+ {
+ const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nLangsysOffset;
+ const sal_uInt16 nReqFeatureIdx = GetUShort( pLangSys+2 );
+ const sal_uInt16 nCntFeature = GetUShort( pLangSys+4 );
+ pLangSys += 6;
+ aFeatureIndexList.push_back( nReqFeatureIdx );
+ for( sal_uInt16 i = 0; i < nCntFeature; ++i )
+ {
+ const sal_uInt16 nFeatureIndex = GetUShort( pLangSys );
+ pLangSys += 2;
+ aFeatureIndexList.push_back( nFeatureIndex );
+ }
+ }
+ }
+
+ if( !aFeatureIndexList.size() )
+ return true;
+
+ UshortList aLookupIndexList;
+ UshortList aLookupOffsetList;
+
+ // parse Feature Table
+ const FT_Byte* pFeatureHeader = pGsubBase + nOfsFeatureTable;
+ const sal_uInt16 nCntFeature = GetUShort( pFeatureHeader );
+ pFeatureHeader += 2;
+ for( sal_uInt16 nFeatureIndex = 0; nFeatureIndex < nCntFeature; ++nFeatureIndex )
+ {
+ const sal_uLong nTag = GetUInt( pFeatureHeader+0 ); // e.g. locl/vert/trad/smpl/liga/fina/...
+ const sal_uInt16 nOffset= GetUShort( pFeatureHeader+4 );
+ pFeatureHeader += 6;
+
+ // short circuit some feature lookups
+ if( aFeatureIndexList[0] != nFeatureIndex ) // required feature?
+ {
+ const int nRequested = std::count( aFeatureIndexList.begin(), aFeatureIndexList.end(), nFeatureIndex);
+ if( !nRequested ) // ignore features that are not requested
+ continue;
+ const int nAvailable = std::count( aReqFeatureTagList.begin(), aReqFeatureTagList.end(), nTag);
+ if( !nAvailable ) // some fonts don't provide features they request!
+ continue;
+ }
+
+ const FT_Byte* pFeatureTable = pGsubBase + nOfsFeatureTable + nOffset;
+ const sal_uInt16 nCntLookups = GetUShort( pFeatureTable+0 );
+ pFeatureTable += 2;
+ for( sal_uInt16 i = 0; i < nCntLookups; ++i )
+ {
+ const sal_uInt16 nLookupIndex = GetUShort( pFeatureTable );
+ pFeatureTable += 2;
+ aLookupIndexList.push_back( nLookupIndex );
+ }
+ if( nCntLookups == 0 ) //### hack needed by Mincho/Gothic/Mingliu/Simsun/...
+ aLookupIndexList.push_back( 0 );
+ }
+
+ // parse Lookup List
+ const FT_Byte* pLookupHeader = pGsubBase + nOfsLookupList;
+ const sal_uInt16 nCntLookupTable = GetUShort( pLookupHeader );
+ pLookupHeader += 2;
+ for( sal_uInt16 nLookupIdx = 0; nLookupIdx < nCntLookupTable; ++nLookupIdx )
+ {
+ const sal_uInt16 nOffset = GetUShort( pLookupHeader );
+ pLookupHeader += 2;
+ if( std::count( aLookupIndexList.begin(), aLookupIndexList.end(), nLookupIdx ) )
+ aLookupOffsetList.push_back( nOffset );
+ }
+
+ UshortList::const_iterator lookup_it = aLookupOffsetList.begin();
+ for(; lookup_it != aLookupOffsetList.end(); ++lookup_it )
+ {
+ const sal_uInt16 nOfsLookupTable = *lookup_it;
+ const FT_Byte* pLookupTable = pGsubBase + nOfsLookupList + nOfsLookupTable;
+ const sal_uInt16 eLookupType = GetUShort( pLookupTable+0 );
+ const sal_uInt16 nCntLookupSubtable = GetUShort( pLookupTable+4 );
+ pLookupTable += 6;
+
+ // TODO: switch( eLookupType )
+ if( eLookupType != 1 ) // TODO: once we go beyond SingleSubst
+ continue;
+
+ for( sal_uInt16 nSubTableIdx = 0; nSubTableIdx < nCntLookupSubtable; ++nSubTableIdx )
+ {
+ const sal_uInt16 nOfsSubLookupTable = GetUShort( pLookupTable );
+ pLookupTable += 2;
+ const FT_Byte* pSubLookup = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable;
+
+ const sal_uInt16 nFmtSubstitution = GetUShort( pSubLookup+0 );
+ const sal_uInt16 nOfsCoverage = GetUShort( pSubLookup+2 );
+ pSubLookup += 4;
+
+ typedef std::pair<sal_uInt16,sal_uInt16> GlyphSubst;
+ typedef std::vector<GlyphSubst> SubstVector;
+ SubstVector aSubstVector;
+
+ const FT_Byte* pCoverage = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable + nOfsCoverage;
+ const sal_uInt16 nFmtCoverage = GetUShort( pCoverage+0 );
+ pCoverage += 2;
+ switch( nFmtCoverage )
+ {
+ case 1: // Coverage Format 1
+ {
+ const sal_uInt16 nCntGlyph = GetUShort( pCoverage );
+ pCoverage += 2;
+ aSubstVector.reserve( nCntGlyph );
+ for( sal_uInt16 i = 0; i < nCntGlyph; ++i )
+ {
+ const sal_uInt16 nGlyphId = GetUShort( pCoverage );
+ pCoverage += 2;
+ aSubstVector.push_back( GlyphSubst( nGlyphId, 0 ) );
+ }
+ }
+ break;
+
+ case 2: // Coverage Format 2
+ {
+ const sal_uInt16 nCntRange = GetUShort( pCoverage );
+ pCoverage += 2;
+ for( int i = nCntRange; --i >= 0; )
+ {
+ const sal_uInt32 nGlyph0 = GetUShort( pCoverage+0 );
+ const sal_uInt32 nGlyph1 = GetUShort( pCoverage+2 );
+ const sal_uInt16 nCovIdx = GetUShort( pCoverage+4 );
+ pCoverage += 6;
+ for( sal_uInt32 j = nGlyph0; j <= nGlyph1; ++j )
+ aSubstVector.push_back( GlyphSubst( static_cast<sal_uInt16>(j + nCovIdx), 0 ) );
+ }
+ }
+ break;
+ }
+
+ SubstVector::iterator it( aSubstVector.begin() );
+
+ switch( nFmtSubstitution )
+ {
+ case 1: // Single Substitution Format 1
+ {
+ const sal_uInt16 nDeltaGlyphId = GetUShort( pSubLookup );
+ pSubLookup += 2;
+ for(; it != aSubstVector.end(); ++it )
+ (*it).second = (*it).first + nDeltaGlyphId;
+ }
+ break;
+
+ case 2: // Single Substitution Format 2
+ {
+ const sal_uInt16 nCntGlyph = GetUShort( pSubLookup );
+ pSubLookup += 2;
+ for( int i = nCntGlyph; (it != aSubstVector.end()) && (--i>=0); ++it )
+ {
+ const sal_uInt16 nGlyphId = GetUShort( pSubLookup );
+ pSubLookup += 2;
+ (*it).second = nGlyphId;
+ }
+ }
+ break;
+ }
+
+ DBG_ASSERT( (it == aSubstVector.end()), "lookup<->coverage table mismatch" );
+ // now apply the glyph substitutions that have been collected in this subtable
+ for( it = aSubstVector.begin(); it != aSubstVector.end(); ++it )
+ maGlyphSubstitution[ (*it).first ] = (*it).second;
+ }
+ }
+
+ return true;
+}
+
+// =======================================================================
+
+/* 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..6b461fe59143
--- /dev/null
+++ b/vcl/source/glyphs/gcach_ftyp.hxx
@@ -0,0 +1,280 @@
+/* -*- 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;
+#ifdef ENABLE_GRAPHITE
+class GraphiteFaceWrapper;
+#endif
+
+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*, sal_uLong* pLength=0 ) const;
+
+ FT_FaceRec_* GetFaceFT();
+#ifdef ENABLE_GRAPHITE
+ GraphiteFaceWrapper* GetGraphiteFace();
+#endif
+ 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 GetFontCodeRanges( CmapResult& ) const;
+ const ImplFontCharMap* GetImplFontCharMap( void );
+
+ 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;
+#ifdef ENABLE_GRAPHITE
+ bool mbCheckedGraphite;
+ GraphiteFaceWrapper * mpGraphiteFace;
+#endif
+ sal_IntPtr mnFontId;
+ ImplDevFontAttributes maDevFontAttributes;
+
+ const ImplFontCharMap* mpFontCharMap;
+
+ // cache unicode->glyphid mapping because looking it up is expensive
+ // TODO: change to boost::unordered_multimap when a use case requires a m:n mapping
+ typedef ::boost::unordered_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 ::boost::unordered_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( boost::shared_ptr<ImplFontOptions> );
+ virtual boost::shared_ptr<ImplFontOptions> GetFontOptions() const;
+ 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 const ImplFontCharMap* GetImplFontCharMap( void ) 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 sal_uLong GetKernPairs( ImplKernPairData** ) const;
+
+ const unsigned char* GetTable( const char* pName, sal_uLong* pLength )
+ { return mpFontInfo->GetTable( pName, pLength ); }
+ int GetEmUnits() const;
+ const FT_Size_Metrics& GetMetricsFT() const { return maSizeFT->metrics; }
+#ifdef ENABLE_GRAPHITE
+ GraphiteFaceWrapper* GetGraphiteFace() const { return mpFontInfo->GetGraphiteFace(); }
+#endif
+
+protected:
+ friend class GlyphCache;
+
+ int ApplyGlyphTransform( int nGlyphFlags, FT_GlyphRec_*, bool ) const;
+ virtual void InitGlyphData( int nGlyphIndex, GlyphData& ) const;
+ virtual bool GetFontCapabilities(vcl::FontCapabilities &) 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;
+
+ boost::shared_ptr<ImplFontOptions> mpFontOptions;
+
+ bool mbFaceOk;
+ bool mbArtItalic;
+ bool mbArtBold;
+ bool mbUseGamma;
+
+ typedef ::boost::unordered_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..63a332869664
--- /dev/null
+++ b/vcl/source/glyphs/gcach_layout.cxx
@@ -0,0 +1,669 @@
+/* -*- 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;
+
+ sal_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));
+}
+
+//See https://bugs.freedesktop.org/show_bug.cgi?id=31016
+#define ARABIC_BANDAID
+
+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();
+ int nNewWidth = nGlyphWidth;
+ if( nGlyphWidth <= 0 )
+ bDiacritic |= true;
+ // #i99367# force all diacritics to zero width
+ // TODO: we need mnOrigWidth/mnLogicWidth/mnNewWidth
+ else if( bDiacritic )
+ nGlyphWidth = nNewWidth = 0;
+ else
+ {
+ // Hack, find next +ve width glyph and calculate current
+ // glyph width by substracting the two posituons
+ const IcuPosition* pNextPos = pPos+1;
+ for ( int j = i + 1; j <= nRawRunGlyphCount; ++j, ++pNextPos )
+ {
+ if ( j == nRawRunGlyphCount )
+ {
+ nNewWidth = pNextPos->fX - pPos->fX;
+ break;
+ }
+
+ LEGlyphID nNextGlyphIndex = pIcuGlyphs[j];
+ if( (nNextGlyphIndex == ICU_MARKED_GLYPH)
+ || (nNextGlyphIndex == ICU_DELETED_GLYPH) )
+ continue;
+
+ const GlyphMetric& rNextGM = rFont.GetGlyphMetric( nNextGlyphIndex );
+ int nNextGlyphWidth = rNextGM.GetCharWidth();
+ if ( nNextGlyphWidth > 0 )
+ {
+ nNewWidth = pNextPos->fX - pPos->fX;
+ break;
+ }
+ }
+ }
+
+ // 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
+ GlyphItem aGI( nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth );
+#ifdef ARABIC_BANDAID
+ aGI.mnNewWidth = nNewWidth;
+#endif
+ 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..161e4c6314e7
--- /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 )
+{
+ sal_uLong nNewScanlineSize = 0;
+ sal_uLong nNewHeight = 0;
+ sal_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..944d9aee16a3
--- /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 = sal_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, sal_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;
+}
+
+// -----------------------------------------------------------------------
+
+sal_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 );
+
+ sal_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 ( sal_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..61cd02b1f9cc
--- /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 sal_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..ab56853dcf65
--- /dev/null
+++ b/vcl/source/glyphs/glyphcache.cxx
@@ -0,0 +1,609 @@
+/* -*- 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();
+ for( FontList::iterator it = maFontList.begin(), end = maFontList.end(); it != end; ++it )
+ {
+ ServerFont* pServerFont = it->second;
+ mrPeer.RemovingFont(*pServerFont);
+ delete pServerFont;
+ }
+ if( mpFtManager )
+ delete mpFtManager;
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::InvalidateAllGlyphs()
+{
+ // an application about to exit can omit garbage collecting the heap
+ // since it makes things slower and introduces risks if the heap was not perfect
+ // for debugging, for memory grinding or leak checking the env allows to force GC
+ const char* pEnv = getenv( "SAL_FORCE_GC_ON_EXIT" );
+ if( pEnv && (*pEnv != '0') )
+ {
+ // uncache of all glyph shapes and metrics
+ for( FontList::iterator it = maFontList.begin(); it != maFontList.end(); ++it )
+ delete const_cast<ServerFont*>( it->second );
+ maFontList.clear();
+ mpCurrentGCFont = NULL;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+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();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+sal_uLong GlyphCache::CalcByteCount() const
+{
+ sal_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 )
+{}
+
+// -----------------------------------------------------------------------
+
+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_features.cxx b/vcl/source/glyphs/graphite_features.cxx
new file mode 100644
index 000000000000..175656e25515
--- /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 <windows.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(const gr_face * pFace, const ::rtl::OString lang)
+ : mnNumSettings(0), mbErrors(false), mpSettings(NULL)
+{
+ maLang.label[0] = maLang.label[1] = maLang.label[2] = maLang.label[3] = '\0';
+ setLang(pFace, lang);
+}
+
+GrFeatureParser::GrFeatureParser(const gr_face * pFace, const ::rtl::OString features, const ::rtl::OString lang)
+ : mnNumSettings(0), mbErrors(false), mpSettings(NULL)
+{
+ sal_Int32 nEquals = 0;
+ sal_Int32 nFeatEnd = 0;
+ sal_Int32 pos = 0;
+ maLang.num = 0u;
+ setLang(pFace, lang);
+ while ((pos < features.getLength()) && (mnNumSettings < MAX_FEATURES))
+ {
+ nEquals = features.indexOf(FEAT_ID_VALUE_SEPARATOR, pos);
+ if (nEquals == -1)
+ {
+ mbErrors = true;
+ break;
+ }
+ // check for a lang=xxx specification
+ const ::rtl::OString aLangPrefix("lang");
+ if (features.match(aLangPrefix, pos ))
+ {
+ pos = nEquals + 1;
+ nFeatEnd = features.indexOf(FEAT_SEPARATOR, pos);
+ if (nFeatEnd == -1)
+ {
+ nFeatEnd = features.getLength();
+ }
+ if (nFeatEnd - pos > 3)
+ mbErrors = true;
+ else
+ {
+ FeatId aLang = maLang;
+ aLang.num = 0;
+ for (sal_Int32 i = pos; i < nFeatEnd; i++)
+ aLang.label[i-pos] = features[i];
+
+ //ext_std::pair<gr::LanguageIterator,gr::LanguageIterator> aSupported
+ // = font.getSupportedLanguages();
+ //gr::LanguageIterator iL = aSupported.first;
+ unsigned short i = 0;
+ for (; i < gr_face_n_languages(pFace); i++)
+ {
+ gr_uint32 nFaceLang = gr_face_lang_by_index(pFace, i);
+ FeatId aSupportedLang;
+ aSupportedLang.num = nFaceLang;
+#ifdef __BIG_ENDIAN__
+ // here we only expect full 3 letter codes
+ if (aLang.label[0] == aSupportedLang.label[0] &&
+ aLang.label[1] == aSupportedLang.label[1] &&
+ aLang.label[2] == aSupportedLang.label[2] &&
+ aLang.label[3] == aSupportedLang.label[3])
+#else
+ if (aLang.label[0] == aSupportedLang.label[3] &&
+ aLang.label[1] == aSupportedLang.label[2] &&
+ aLang.label[2] == aSupportedLang.label[1] &&
+ aLang.label[3] == aSupportedLang.label[0])
+#endif
+ {
+ maLang = aSupportedLang;
+ break;
+ }
+ }
+ if (i == gr_face_n_languages(pFace)) mbErrors = true;
+ else
+ {
+ mnHash = maLang.num;
+ mpSettings = gr_face_featureval_for_lang(pFace, maLang.num);
+ }
+ }
+ }
+ else
+ {
+ sal_uInt32 featId = 0;
+ if (isCharId(features, pos, nEquals - pos))
+ {
+ featId = getCharId(features, pos, nEquals - pos);
+ }
+ else
+ {
+ featId = getIntValue(features, pos, nEquals - pos);
+ }
+ const gr_feature_ref * pFref = gr_face_find_fref(pFace, featId);
+ pos = nEquals + 1;
+ nFeatEnd = features.indexOf(FEAT_SEPARATOR, pos);
+ if (nFeatEnd == -1)
+ {
+ nFeatEnd = features.getLength();
+ }
+ sal_Int16 featValue = 0;
+ featValue = getIntValue(features, pos, nFeatEnd - pos);
+ if (pFref && gr_fref_set_feature_value(pFref, featValue, mpSettings))
+ {
+ mnHash = (mnHash << 16) ^ ((featId << 8) | featValue);
+ mnNumSettings++;
+ }
+ else
+ mbErrors = true;
+ }
+ pos = nFeatEnd + 1;
+ }
+}
+
+void GrFeatureParser::setLang(const gr_face * pFace, const rtl::OString & lang)
+{
+ FeatId aLang;
+ aLang.num = 0;
+ if (lang.getLength() >= 2)
+ {
+ for (sal_Int32 i = 0; i < lang.getLength() && i < 3; i++)
+ {
+ if (lang[i] == '-') break;
+ aLang.label[i] = lang[i];
+ }
+ unsigned short i = 0;
+ for (; i < gr_face_n_languages(pFace); i++)
+ {
+ gr_uint32 nFaceLang = gr_face_lang_by_index(pFace, i);
+ FeatId aSupportedLang;
+ aSupportedLang.num = nFaceLang;
+ // here we only expect full 2 & 3 letter codes
+#ifdef __BIG_ENDIAN__
+ if (aLang.label[0] == aSupportedLang.label[0] &&
+ aLang.label[1] == aSupportedLang.label[1] &&
+ aLang.label[2] == aSupportedLang.label[2] &&
+ aLang.label[3] == aSupportedLang.label[3])
+#else
+ if (aLang.label[0] == aSupportedLang.label[3] &&
+ aLang.label[1] == aSupportedLang.label[2] &&
+ aLang.label[2] == aSupportedLang.label[1] &&
+ aLang.label[3] == aSupportedLang.label[0])
+#endif
+ {
+ maLang = aSupportedLang;
+ break;
+ }
+ }
+ if (i != gr_face_n_languages(pFace))
+ {
+ if (mpSettings)
+ gr_featureval_destroy(mpSettings);
+ mpSettings = gr_face_featureval_for_lang(pFace, maLang.num);
+ mnHash = maLang.num;
+ }
+ }
+ if (!mpSettings)
+ mpSettings = gr_face_featureval_for_lang(pFace, 0);
+}
+
+GrFeatureParser::~GrFeatureParser()
+{
+ if (mpSettings)
+ {
+ gr_featureval_destroy(mpSettings);
+ mpSettings = NULL;
+ }
+}
+
+bool GrFeatureParser::isCharId(const rtl::OString & 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;
+}
+
+gr_uint32 GrFeatureParser::getCharId(const rtl::OString & 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;
+}
+
+short GrFeatureParser::getIntValue(const rtl::OString & id, size_t offset, size_t length)
+{
+ short 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;
+}
+
+/* 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..ab5b59025724
--- /dev/null
+++ b/vcl/source/glyphs/graphite_layout.cxx
@@ -0,0 +1,1366 @@
+/* -*- 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
+#include <cstdio>
+//#define GRLAYOUT_DEBUG 1
+#undef NDEBUG
+#endif
+
+// Header files
+//
+// Standard Library
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <limits>
+#include <numeric>
+#include <deque>
+
+// Platform
+#include <svsys.h>
+
+#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 <graphite2/Segment.h>
+
+#include "vcl/graphite_layout.hxx"
+#include "vcl/graphite_features.hxx"
+
+// Module private type definitions and forward declarations.
+//
+// Module private names.
+//
+
+#ifdef GRLAYOUT_DEBUG
+static FILE * grLogFile = NULL;
+static 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
+ fflush(stdout);
+ return stdout;
+#endif
+}
+#endif
+
+namespace
+{
+ 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;
+ }
+
+ template <typename T>
+ T maximum(T a, T b)
+ {
+ return (a > b)? a : b;
+ }
+ template <typename T>
+ T minimum(T a, T b)
+ {
+ return (a < b)? a : b;
+ }
+
+} // 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;
+
+// find first slot of cluster and first slot of subsequent cluster
+static void findFirstClusterSlot(const gr_slot* base, gr_slot const** first, gr_slot const** after, int * firstChar, int * lastChar, bool bRtl)
+{
+ if (gr_slot_attached_to(base) == NULL)
+ {
+ *first = base;
+ *after = (bRtl)? gr_slot_prev_in_segment(base) :
+ gr_slot_next_in_segment(base);
+ *firstChar = gr_slot_before(base);
+ *lastChar = gr_slot_after(base);
+ }
+ const gr_slot * attachment = gr_slot_first_attachment(base);
+ while (attachment)
+ {
+ if (gr_slot_origin_X(*first) > gr_slot_origin_X(attachment))
+ *first = attachment;
+ const gr_slot* attachmentNext = (bRtl)?
+ gr_slot_prev_in_segment(attachment) : gr_slot_next_in_segment(attachment);
+ if (attachmentNext)
+ {
+ if (*after && (gr_slot_origin_X(*after) < gr_slot_origin_X(attachmentNext)))
+ *after = attachmentNext;
+ }
+ else
+ {
+ *after = NULL;
+ }
+ if (gr_slot_before(attachment) < *firstChar)
+ *firstChar = gr_slot_before(attachment);
+ if (gr_slot_after(attachment) > *lastChar)
+ *lastChar = gr_slot_after(attachment);
+ if (gr_slot_first_attachment(attachment))
+ findFirstClusterSlot(attachment, first, after, firstChar, lastChar, bRtl);
+ attachment = gr_slot_next_sibling_attachment(attachment);
+ }
+}
+
+// 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,
+// finds each non-attached base glyph and calls append to record them as a
+// sequence of clusters.
+void
+GraphiteLayout::fillFrom(gr_segment * pSegment, ImplLayoutArgs &rArgs, float fScaling)
+{
+ bool bRtl = (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL);
+ int nCharRequested = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
+ int nChar = gr_seg_n_cinfo(pSegment);
+ float fMinX = gr_seg_advance_X(pSegment);
+ float fMaxX = 0.0f;
+ long nDxOffset = 0; // from dropped glyphs
+ int nFirstCharInCluster = 0;
+ int nLastCharInCluster = 0;
+ unsigned int nGlyphs = gr_seg_n_slots(pSegment);
+ mvGlyph2Char.assign(nGlyphs, -1);
+ mvGlyphs.reserve(nGlyphs);
+
+ if (bRtl)
+ {
+ const gr_slot* baseSlot = gr_seg_last_slot(pSegment);
+ // find first base
+ while (baseSlot && (gr_slot_attached_to(baseSlot) != NULL))
+ baseSlot = gr_slot_prev_in_segment(baseSlot);
+ int iChar = nChar - 1;
+ int iNextChar = nChar - 1;
+ bool reordered = false;
+ int nBaseGlyphIndex = 0;
+ // now loop over bases
+ while (baseSlot)
+ {
+ bool bCluster = !reordered;
+ const gr_slot * clusterFirst = NULL;
+ const gr_slot * clusterAfter = NULL;
+ int firstChar = -1;
+ int lastChar = -1;
+ findFirstClusterSlot(baseSlot, &clusterFirst, &clusterAfter, &firstChar, &lastChar, bRtl);
+ iNextChar = minimum<int>(firstChar, iNextChar);
+ if (bCluster)
+ {
+ nBaseGlyphIndex = mvGlyphs.size();
+ mvGlyph2Char[nBaseGlyphIndex] = iChar + mnSegCharOffset;
+ nFirstCharInCluster = firstChar;
+ nLastCharInCluster = lastChar;
+ }
+ else
+ {
+ mvGlyph2Char[mvGlyphs.size()] = firstChar + mnSegCharOffset;
+ nFirstCharInCluster = minimum<int>(firstChar, nFirstCharInCluster);
+ nLastCharInCluster = maximum<int>(firstChar, nLastCharInCluster);
+ }
+ float leftBoundary = gr_slot_origin_X(clusterFirst);
+ float rightBoundary = (clusterAfter)?
+ gr_slot_origin_X(clusterAfter) : gr_seg_advance_X(pSegment);
+ if (
+ lastChar < iChar &&
+ (gr_cinfo_after(gr_seg_cinfo(pSegment, iChar)) >
+ static_cast<int>(gr_slot_index(clusterAfter)))
+ )
+ {
+ reordered = true;
+ }
+ else
+ {
+ reordered = false;
+ iChar = iNextChar - 1;
+ }
+ if (mnSegCharOffset + nFirstCharInCluster >= mnMinCharPos &&
+ mnSegCharOffset + nFirstCharInCluster < mnEndCharPos)
+ {
+ fMinX = minimum<float>(fMinX, leftBoundary);
+ fMaxX = maximum<float>(fMaxX, rightBoundary);
+ if (!reordered)
+ {
+ for (int i = nFirstCharInCluster; i <= nLastCharInCluster; i++)
+ {
+ if (mnSegCharOffset + i >= mnEndCharPos)
+ break;
+ // from the point of view of the dx array, the xpos is
+ // the origin of the first glyph of the cluster rtl
+ mvCharDxs[mnSegCharOffset + i - mnMinCharPos] =
+ static_cast<int>(leftBoundary * fScaling) + nDxOffset;
+ mvCharBreaks[mnSegCharOffset + i - mnMinCharPos] = gr_cinfo_break_weight(gr_seg_cinfo(pSegment, i));
+ }
+ mvChar2BaseGlyph[mnSegCharOffset + nFirstCharInCluster - mnMinCharPos] = nBaseGlyphIndex;
+ }
+ append(pSegment, rArgs, baseSlot, rightBoundary, fScaling,
+ nDxOffset, bCluster, mnSegCharOffset + firstChar);
+ }
+ if (mnSegCharOffset + nLastCharInCluster < mnMinCharPos)
+ break;
+ baseSlot = gr_slot_next_sibling_attachment(baseSlot);
+ }
+ }
+ else
+ {
+ const gr_slot* baseSlot = gr_seg_first_slot(pSegment);
+ // find first base
+ while (baseSlot && (gr_slot_attached_to(baseSlot) != NULL))
+ baseSlot = gr_slot_next_in_segment(baseSlot);
+ int iChar = 0; // relative to segment
+ int iNextChar = 0;
+ bool reordered = false;
+ int nBaseGlyphIndex = 0;
+ // now loop over bases
+ while (baseSlot)
+ {
+ bool bCluster = !reordered;
+ const gr_slot * clusterFirst = NULL;
+ const gr_slot * clusterAfter = NULL;
+ int firstChar = -1;
+ int lastChar = -1;
+ findFirstClusterSlot(baseSlot, &clusterFirst, &clusterAfter, &firstChar, &lastChar, bRtl);
+ iNextChar = maximum<int>(lastChar, iNextChar);
+ if (bCluster)
+ {
+ nBaseGlyphIndex = mvGlyphs.size();
+ mvGlyph2Char[nBaseGlyphIndex] = iChar + mnSegCharOffset;
+ nFirstCharInCluster = firstChar;
+ nLastCharInCluster = lastChar;
+ }
+ else
+ {
+ mvGlyph2Char[mvGlyphs.size()] = firstChar + mnSegCharOffset;
+ nFirstCharInCluster = minimum<int>(firstChar, nFirstCharInCluster);
+ nLastCharInCluster = maximum<int>(lastChar, nLastCharInCluster);
+ }
+ if (
+ firstChar > iChar &&
+ (gr_cinfo_before(gr_seg_cinfo(pSegment, iChar)) >
+ static_cast<int>(gr_slot_index(clusterFirst)))
+ )
+ {
+ reordered = true;
+ }
+ else
+ {
+ reordered = false;
+ iChar = iNextChar + 1;
+ }
+ float leftBoundary = gr_slot_origin_X(clusterFirst);
+ float rightBoundary = (clusterAfter)?
+ gr_slot_origin_X(clusterAfter) : gr_seg_advance_X(pSegment);
+ if (mnSegCharOffset + nFirstCharInCluster >= mnMinCharPos &&
+ mnSegCharOffset + nFirstCharInCluster < mnEndCharPos)
+ {
+ fMinX = minimum<float>(fMinX, leftBoundary);
+ fMaxX = maximum<float>(fMaxX, rightBoundary);
+ if (!reordered)
+ {
+ for (int i = nFirstCharInCluster; i <= nLastCharInCluster; i++)
+ {
+ if (mnSegCharOffset + i >= mnEndCharPos)
+ break;
+ // from the point of view of the dx array, the xpos is
+ // the origin of the first glyph of the next cluster ltr
+ mvCharDxs[mnSegCharOffset + i - mnMinCharPos] =
+ static_cast<int>(rightBoundary * fScaling) + nDxOffset;
+ mvCharBreaks[mnSegCharOffset + i - mnMinCharPos] = gr_cinfo_break_weight(gr_seg_cinfo(pSegment, i));
+ }
+ // only set mvChar2BaseGlyph for first character of cluster
+ mvChar2BaseGlyph[mnSegCharOffset + nFirstCharInCluster - mnMinCharPos] = nBaseGlyphIndex;
+ }
+ append(pSegment, rArgs, baseSlot, rightBoundary, fScaling,
+ nDxOffset, true, mnSegCharOffset + firstChar);
+ }
+ if (mnSegCharOffset + nFirstCharInCluster >= mnEndCharPos)
+ break;
+ baseSlot = gr_slot_next_sibling_attachment(baseSlot);
+ }
+ }
+ long nXOffset = round(fMinX * fScaling);
+ mnWidth = round(fMaxX * fScaling) - nXOffset + nDxOffset;
+ if (mnWidth < 0)
+ {
+ // This can happen when there was no base inside the range
+ mnWidth = 0;
+ }
+ // fill up non-base char dx with cluster widths from previous base glyph
+ if (bRtl)
+ {
+ if (mvCharDxs[nCharRequested-1] == -1)
+ mvCharDxs[nCharRequested-1] = 0;
+ else
+ mvCharDxs[nCharRequested-1] -= nXOffset;
+ for (int i = nCharRequested - 2; i >= 0; i--)
+ {
+ if (mvCharDxs[i] == -1) mvCharDxs[i] = mvCharDxs[i+1];
+ else mvCharDxs[i] -= nXOffset;
+ }
+ }
+ else
+ {
+ if (mvCharDxs[0] == -1)
+ mvCharDxs[0] = 0;
+ else
+ mvCharDxs[0] -= nXOffset;
+ for (int i = 1; i < nCharRequested; i++)
+ {
+ if (mvCharDxs[i] == -1) mvCharDxs[i] = mvCharDxs[i-1];
+ else mvCharDxs[i] -= nXOffset;
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"%d,%d ", (int)i, (int)mvCharDxs[i]);
+#endif
+ }
+ }
+ // remove offset due to context if there is one
+ if (nXOffset != 0)
+ {
+ for (size_t i = 0; i < mvGlyphs.size(); i++)
+ mvGlyphs[i].maLinearPos.X() -= nXOffset;
+ }
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(), "fillFrom %d glyphs offset %ld width %d\n", mvGlyphs.size(), nXOffset, mnWidth);
+#endif
+}
+
+// append walks an attachment tree, flattening it, and converting it into a
+// sequence of GlyphItem objects which we can later manipulate.
+void
+GraphiteLayout::append(gr_segment *pSeg, ImplLayoutArgs &rArgs,
+ const gr_slot * gi, float nextGlyphOrigin, float scaling, long & rDXOffset,
+ bool bIsBase, int baseChar)
+{
+ bool bRtl = (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL);
+ float nextOrigin = nextGlyphOrigin;
+ assert(gi);
+ assert(gr_slot_before(gi) <= gr_slot_after(gi));
+ int firstChar = gr_slot_before(gi) + mnSegCharOffset;
+ assert(mvGlyphs.size() < mvGlyph2Char.size());
+ if (!bIsBase) mvGlyph2Char[mvGlyphs.size()] = baseChar;//firstChar;
+ // is the next glyph attached or in the next cluster?
+ //glyph_set_range_t iAttached = gi.attachedClusterGlyphs();
+ const gr_slot * pFirstAttached = gr_slot_first_attachment(gi);
+ if (pFirstAttached)
+ {
+ nextOrigin = gr_slot_origin_X(pFirstAttached);
+ }
+ long glyphId = gr_slot_gid(gi);
+ long deltaOffset = 0;
+ int scaledGlyphPos = round(gr_slot_origin_X(gi) * scaling);
+ int glyphWidth = round(nextOrigin * scaling) - scaledGlyphPos;
+ if (glyphWidth < 0)
+ glyphWidth = 0;
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"c%d g%ld,X%d W%d nX%f ", firstChar, glyphId,
+ (int)(gr_slot_origin_X(gi) * scaling), glyphWidth, nextOrigin * scaling);
+#endif
+ if (glyphId == 0)
+ {
+ rArgs.NeedFallback(firstChar, bRtl);
+ if( (SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags ))
+ {
+ glyphId = GF_DROPPED;
+ deltaOffset -= glyphWidth;
+ glyphWidth = 0;
+ }
+ }
+ else if(rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK)
+ {
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(),"fallback c%d %x in run %d\n", firstChar, rArgs.mpStr[firstChar],
+ rArgs.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 (!rArgs.maRuns.PosIsInAnyRun(firstChar) &&
+ in_range(firstChar, rArgs.mnMinCharPos, rArgs.mnEndCharPos))
+ {
+ glyphId = GF_DROPPED;
+ deltaOffset -= glyphWidth;
+ glyphWidth = 0;
+ }
+ }
+ // append this glyph. Set the cluster flag if this glyph is attached to another
+ long nGlyphFlags = bIsBase ? 0 : GlyphItem::IS_IN_CLUSTER;
+ nGlyphFlags |= (bRtl)? GlyphItem::IS_RTL_GLYPH : 0;
+ GlyphItem aGlyphItem(mvGlyphs.size(),
+ glyphId,
+ Point(scaledGlyphPos + rDXOffset,
+ round((-gr_slot_origin_Y(gi) * scaling))),
+ nGlyphFlags,
+ glyphWidth);
+ if (glyphId != static_cast<long>(GF_DROPPED))
+ aGlyphItem.mnOrigWidth = round(gr_slot_advance_X(gi, mpFace, mpFont) * scaling);
+ mvGlyphs.push_back(aGlyphItem);
+
+ // update the offset if this glyph was dropped
+ rDXOffset += deltaOffset;
+
+ // Recursively append all the attached glyphs.
+ for (const gr_slot * agi = gr_slot_first_attachment(gi); agi != NULL;
+ agi = gr_slot_next_sibling_attachment(agi))
+ {
+ if (gr_slot_next_sibling_attachment(agi) == NULL)
+ append(pSeg, rArgs, agi, nextGlyphOrigin, scaling, rDXOffset,
+ false, baseChar);
+ else
+ append(pSeg, rArgs, agi, gr_slot_origin_X(gr_slot_next_sibling_attachment(agi)),
+ scaling, rDXOffset, false, baseChar);
+ }
+}
+
+//
+// An implementation of the SalLayout interface to enable Graphite enabled fonts to be used.
+//
+GraphiteLayout::GraphiteLayout(const gr_face * face, gr_font * font,
+ const grutils::GrFeatureParser * pFeatures) throw()
+ : mpFace(face),
+ mpFont(font),
+ mnWidth(0),
+ mfScaling(1.0),
+ mpFeatures(pFeatures)
+{
+
+}
+
+GraphiteLayout::~GraphiteLayout() throw()
+{
+ clear();
+ // the features and font are owned by the platform layers
+ mpFeatures = NULL;
+ mpFont = NULL;
+}
+
+void GraphiteLayout::clear()
+{
+ // Destroy the segment and text source from any previous invocation of
+ // LayoutText
+ mvGlyphs.clear();
+ mvCharDxs.clear();
+ mvChar2BaseGlyph.clear();
+ mvGlyph2Char.clear();
+
+ // Reset the state to the empty state.
+ 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)
+{
+ 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)
+ {
+ gr_seg_destroy(pSegment);
+ pSegment = NULL;
+ }
+ }
+ else
+ {
+ clear();
+ }
+ return success;
+}
+
+
+gr_segment * GraphiteLayout::CreateSegment(ImplLayoutArgs& rArgs)
+{
+ 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.
+ mnSegCharOffset = rArgs.mnMinCharPos;
+ int limit = rArgs.mnEndCharPos;
+ if (!(SAL_LAYOUT_COMPLEX_DISABLED & rArgs.mnFlags))
+ {
+ const int nSegCharMin = maximum<int>(0, mnMinCharPos - EXTRA_CONTEXT_LENGTH);
+ const int nSegCharLimit = minimum(rArgs.mnLength, mnEndCharPos + EXTRA_CONTEXT_LENGTH);
+ if (nSegCharMin < mnSegCharOffset)
+ {
+ int sameDirEnd = findSameDirLimit(rArgs.mpStr + nSegCharMin,
+ rArgs.mnEndCharPos - nSegCharMin, bRtl);
+ if (sameDirEnd == rArgs.mnEndCharPos)
+ mnSegCharOffset = nSegCharMin;
+ }
+ if (nSegCharLimit > limit)
+ {
+ limit += findSameDirLimit(rArgs.mpStr + rArgs.mnEndCharPos,
+ nSegCharLimit - rArgs.mnEndCharPos, bRtl);
+ }
+ }
+
+ if (mpFeatures)
+ pSegment = gr_make_seg(mpFont, mpFace, 0, mpFeatures->values(), gr_utf16,
+ rArgs.mpStr + mnSegCharOffset, limit - mnSegCharOffset, bRtl);
+ else
+ pSegment = gr_make_seg(mpFont, mpFace, 0, NULL, gr_utf16,
+ rArgs.mpStr + mnSegCharOffset, limit - mnSegCharOffset, bRtl);
+
+ //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 scaling %f\n", rArgs.mnMinCharPos,
+ rArgs.mnEndCharPos, limit, rArgs.mnLength, bRtl, mfScaling);
+#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;
+}
+
+bool GraphiteLayout::LayoutGlyphs(ImplLayoutArgs& rArgs, gr_segment * pSegment)
+{
+ // Calculate the initial character dxs.
+ mvCharDxs.assign(mnEndCharPos - mnMinCharPos, -1);
+ mvChar2BaseGlyph.assign(mnEndCharPos - mnMinCharPos, -1);
+ mvCharBreaks.assign(mnEndCharPos - mnMinCharPos, 0);
+ mnWidth = 0;
+ if (mvCharDxs.size() > 0)
+ {
+ // Discover all the clusters.
+ try
+ {
+ bool bRtl = mnLayoutFlags & SAL_LAYOUT_BIDI_RTL;
+ fillFrom(pSegment, rArgs, mfScaling);
+
+ 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;
+ }
+ }
+ 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;
+ long wLastBreak = 0;
+ int nLastBreak = -1;
+ int nEmergency = -1;
+ for (size_t i = 1; i < mvCharDxs.size(); i++)
+ {
+ nWidth += char_extra;
+ if (nWidth > maxmnWidth) break;
+ if (mvChar2BaseGlyph[i] != -1)
+ {
+ if (
+ (mvCharBreaks[i] > -25 || (mvCharBreaks[i-1] > 0 && mvCharBreaks[i-1] < 25)) &&
+ (mvCharBreaks[i-1] < 25 || (mvCharBreaks[i] < 0 && mvCharBreaks[i] > -25))
+ )
+ {
+ nLastBreak = static_cast<int>(i);
+ wLastBreak = nWidth;
+ }
+ nEmergency = static_cast<int>(i);
+ }
+ nWidth += (mvCharDxs[i] - mvCharDxs[i-1]) * factor;
+ }
+ int nBreak = mnMinCharPos;
+ if (wLastBreak > 9 * maxmnWidth / 10)
+ nBreak += nLastBreak;
+ else
+ if (nEmergency > -1)
+ nBreak += nEmergency;
+
+#ifdef GRLAYOUT_DEBUG
+ fprintf(grLog(), "Gr::GetTextBreak break after %d, weights(%d, %d)\n", nBreak - mnMinCharPos, mvCharBreaks[nBreak - mnMinCharPos], mvCharBreaks[nBreak - mnMinCharPos - 1]);
+#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]) < (signed)mvGlyphs.size()));
+ if (mvChar2BaseGlyph[i] != -1 &&
+ mvGlyphs[mvChar2BaseGlyph[i]].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=%ld\n", mnMinCharPos, mnEndCharPos, 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
+ {
+ // NOTE: for expansion we can use base glyphs (which have IsClusterStart set)
+ // even though they may have been reordered in which case they will have
+ // been placed in a bigger cluster for other purposes.
+ 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 = static_cast<int>(fExtraPerCluster * nCluster);
+ int nCharIndex = mvGlyph2Char[i];
+ assert(nCharIndex > -1);
+ mvCharDxs[nCharIndex-mnMinCharPos] += nOffset;
+ // adjust char dxs for rest of characters in cluster
+ while (++nCharIndex < static_cast<int>(mvGlyph2Char.size()))
+ {
+ int nChar2Base = mvChar2BaseGlyph[nCharIndex-mnMinCharPos];
+ if (nChar2Base == -1 || nChar2Base == static_cast<int>(i))
+ mvCharDxs[nCharIndex-mnMinCharPos] += nOffset;
+ else
+ break;
+ }
+ ++nCluster;
+ }
+ mvGlyphs[i].maLinearPos.X() += nOffset;
+ }
+ }
+ }
+ else if (nDeltaWidth < 0)// 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 last x%ld\n", fXFactor, iLastGlyph->maLinearPos.X());
+#endif
+ if (fXFactor < 0)
+ return; // probably a bad mnOrigWidth value
+ iLastGlyph->maLinearPos.X() = rArgs.mnLayoutWidth - iLastGlyph->mnOrigWidth;
+ Glyphs::iterator iGlyph = mvGlyphs.begin();
+ while (iGlyph != iLastGlyph)
+ {
+ iGlyph->maLinearPos.X() = static_cast<int>(static_cast<float>(iGlyph->maLinearPos.X()) * fXFactor);
+ ++iGlyph;
+ }
+ for (size_t i = 0; i < mvCharDxs.size(); i++)
+ {
+ mvCharDxs[i] = static_cast<int>(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];
+ 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;
+ int nChar2BaseJ = -1;
+ for (; j < nChars; j++)
+ {
+ nChar2BaseJ = mvChar2BaseGlyph[j];
+ assert((nChar2BaseJ >= -1) && (nChar2BaseJ < (signed)mvGlyphs.size()));
+ if (nChar2BaseJ != -1 )
+ {
+ 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;
+ }
+ int nBaseCount = 0;
+ // count bases within cluster - may be more than 1 with reordering
+ for (int k = nChar2Base; k <= nLastGlyph; k++)
+ {
+ if (mvGlyphs[k].IsClusterStart()) ++nBaseCount;
+ }
+ 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;
+ long nDOriginPerBase = (nBaseCount > 0)? nDWidth / nBaseCount : 0;
+ nBaseCount = -1;
+ // update glyph positions
+ if (bRtl)
+ {
+ for (int n = nChar2Base; n <= nLastGlyph; n++)
+ {
+ if (mvGlyphs[n].IsClusterStart()) ++nBaseCount;
+ assert((n > - 1) && (n < (signed)mvGlyphs.size()));
+ mvGlyphs[n].maLinearPos.X() += -(nDGlyphOrigin + nDOriginPerBase * nBaseCount) + nXOffset;
+ }
+ }
+ else
+ {
+ for (int n = nChar2Base; n <= nLastGlyph; n++)
+ {
+ if (mvGlyphs[n].IsClusterStart()) ++nBaseCount;
+ assert((n > - 1) && (n < (signed)mvGlyphs.size()));
+ mvGlyphs[n].maLinearPos.X() += nDGlyphOrigin + (nDOriginPerBase * nBaseCount) + 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 = (mnLayoutFlags & SAL_LAYOUT_BIDI_RTL);//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];
+ assert((nChar2Base > -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 = minimum(nMin, mvGlyphs[nCluster].maLinearPos.X());
+ nMax = maximum(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 = minimum(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)
+ {
+ if (glyph_slot >= (signed)mvGlyph2Char.size())
+ {
+ *char_index++ = mnMinCharPos + mvCharDxs.size();
+ }
+ else
+ {
+ assert(glyph_slot > -1);
+ if (mvGlyph2Char[glyph_slot] == -1)
+ *char_index++ = mnMinCharPos + 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 gid%d c%d x%ld,%ld adv%ld, pos %ld,%ld\n",
+ glyph_slot - 1, glyph_itr->mnGlyphIndex,
+ 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;
+ }
+ ++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..ec1388d9bb29
--- /dev/null
+++ b/vcl/source/glyphs/graphite_serverfont.cxx
@@ -0,0 +1,164 @@
+/* -*- 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 <i18npool/mslangid.hxx>
+#include <vcl/sallayout.hxx>
+// Module
+#include "gcach_ftyp.hxx"
+#include <vcl/glyphcache.hxx>
+#include <vcl/graphite_features.hxx>
+//#include "graphite_textsrc.hxx"
+#include <vcl/graphite_serverfont.hxx>
+
+#ifndef WNT
+
+float freetypeServerFontAdvance(const void* appFontHandle, gr_uint16 glyphId)
+{
+ FreetypeServerFont * pServerFont =
+ const_cast<FreetypeServerFont*>
+ (reinterpret_cast<const FreetypeServerFont*>(appFontHandle));
+ if (pServerFont)
+ {
+ return static_cast<float>(pServerFont->GetGlyphMetric(glyphId).GetCharWidth());
+ }
+ return .0f;
+}
+
+//
+// An implementation of the GraphiteLayout interface to enable Graphite enabled fonts to be used.
+//
+
+GraphiteServerFontLayout::GraphiteServerFontLayout(ServerFont & rServerFont) throw()
+ : ServerFontLayout(rServerFont),
+ maImpl(dynamic_cast<FreetypeServerFont&>(rServerFont).GetGraphiteFace()->face(),
+ rServerFont),
+ mpFeatures(NULL)
+{
+ FreetypeServerFont& rFTServerFont = dynamic_cast<FreetypeServerFont&>(rServerFont);
+ gr_font * pFont = rFTServerFont.GetGraphiteFace()->font(rServerFont.GetFontSelData().mnHeight);
+ if (!pFont)
+ {
+ pFont = gr_make_font_with_advance_fn(
+ // need to use mnHeight here, mfExactHeight can give wrong values
+ static_cast<float>(rServerFont.GetFontSelData().mnHeight),
+ &rFTServerFont,
+ freetypeServerFontAdvance,
+ rFTServerFont.GetGraphiteFace()->face());
+ rFTServerFont.GetGraphiteFace()->addFont(rServerFont.GetFontSelData().mnHeight, pFont);
+ }
+ maImpl.SetFont(pFont);
+ rtl::OString aLang("");
+ if (rServerFont.GetFontSelData().meLanguage != LANGUAGE_DONTKNOW)
+ {
+ aLang = MsLangId::convertLanguageToIsoByteString(
+ rServerFont.GetFontSelData().meLanguage );
+ }
+ rtl::OString name = rtl::OUStringToOString(
+ rServerFont.GetFontSelData().maTargetName, RTL_TEXTENCODING_UTF8 );
+#ifdef DEBUG
+ printf("GraphiteServerFontLayout %lx %s size %d %f\n", (long unsigned int)this, name.getStr(),
+ rFTServerFont.GetMetricsFT().x_ppem,
+ rServerFont.GetFontSelData().mfExactHeight);
+#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(
+ rFTServerFont.GetGraphiteFace()->face(), aFeat, aLang);
+#ifdef DEBUG
+ if (mpFeatures)
+ printf("GraphiteServerFontLayout %s/%s/%s %x language %d features %d errors\n",
+ rtl::OUStringToOString( rServerFont.GetFontSelData().maName,
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ rtl::OUStringToOString( rServerFont.GetFontSelData().maTargetName,
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ rtl::OUStringToOString( rServerFont.GetFontSelData().maSearchName,
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ rServerFont.GetFontSelData().meLanguage,
+ (int)mpFeatures->numFeatures(), mpFeatures->parseErrors());
+#endif
+ }
+ else
+ {
+ mpFeatures = new grutils::GrFeatureParser(
+ rFTServerFont.GetGraphiteFace()->face(), aLang);
+ }
+ maImpl.SetFeatures(mpFeatures);
+}
+
+GraphiteServerFontLayout::~GraphiteServerFontLayout() throw()
+{
+ delete mpFeatures;
+ mpFeatures = NULL;
+}
+
+bool GraphiteServerFontLayout::IsGraphiteEnabledFont(ServerFont * pServerFont)
+{
+ FreetypeServerFont * pFtServerFont = dynamic_cast<FreetypeServerFont*>(pServerFont);
+ if (pFtServerFont)
+ {
+ if (pFtServerFont->GetGraphiteFace())
+ {
+#ifdef DEBUG
+ printf("IsGraphiteEnabledFont\n");
+#endif
+ return true;
+ }
+ }
+ return false;
+}
+
+sal_GlyphId GraphiteLayoutImpl::getKashidaGlyph(int & width)
+{
+ int nKashidaIndex = mrServerFont.GetGlyphIndex( 0x0640 );
+ if( nKashidaIndex != 0 )
+ {
+ const GlyphMetric& rGM = mrServerFont.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..33508ae34712
--- /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;
+}
+
+std::pair<gr::toffset, gr::toffset> TextSourceAdaptor::propertyRange(gr::toffset nCharIdx)
+{
+
+ if (nCharIdx < unsigned(maLayoutArgs.mnMinCharPos))
+ return std::make_pair(0, maLayoutArgs.mnMinCharPos);
+
+ if (nCharIdx < mnEnd)
+ return std::make_pair(maLayoutArgs.mnMinCharPos, mnEnd);
+
+ return 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 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..b5251cff2922
--- /dev/null
+++ b/vcl/source/glyphs/graphite_textsrc.hxx
@@ -0,0 +1,124 @@
+/* -*- 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
+
+#ifdef WNT
+#include <windows.h>
+#endif
+
+#include <vcl/salgdi.hxx>
+
+#include <vcl/sallayout.hxx>
+
+// Module
+#include "vcl/dllapi.h"
+
+// Libraries
+#include <graphite/GrClient.h>
+#include <graphite/Font.h>
+#include <graphite/ITextSource.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 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..cbc9a38b4f9e
--- /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_features.obj \
+ $(SLO)$/graphite_serverfont.obj \
+ $(SLO)$/graphite_layout.obj
+
+.ENDIF
+
+.ELSE
+
+.IF "$(ENABLE_GRAPHITE)" == "TRUE"
+# Graphite support on non-UNX platforms
+SLOFILES=\
+ $(SLO)$/graphite_features.obj \
+ $(SLO)$/graphite_layout.obj
+
+.IF "$(SYSTEM_GRAPHITE)" != "YES"
+CDEFS+=-DGR2_STATIC
+.ENDIF
+
+.ENDIF
+.ENDIF
+
+# --- Targets ------------------------------------------------------
+
+.INCLUDE : target.mk
+