summaryrefslogtreecommitdiff
path: root/vcl/win/source/gdi/salgdi3.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/win/source/gdi/salgdi3.cxx')
-rw-r--r--vcl/win/source/gdi/salgdi3.cxx3245
1 files changed, 3245 insertions, 0 deletions
diff --git a/vcl/win/source/gdi/salgdi3.cxx b/vcl/win/source/gdi/salgdi3.cxx
new file mode 100644
index 000000000000..c8e0210196e6
--- /dev/null
+++ b/vcl/win/source/gdi/salgdi3.cxx
@@ -0,0 +1,3245 @@
+/*************************************************************************
+ *
+ * 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 <string.h>
+#include <malloc.h>
+
+#include <tools/prewin.h>
+#include <windows.h>
+#include <tools/postwin.h>
+#include <vcl/sysdata.hxx>
+#include "tools/svwin.h"
+
+#include "wincomp.hxx"
+#include "saldata.hxx"
+#include "salgdi.h"
+
+#include "vcl/svapp.hxx"
+#include "vcl/outfont.hxx"
+#include "vcl/font.hxx"
+#include "vcl/fontsubset.hxx"
+#include "vcl/sallayout.hxx"
+
+#include "vcl/outdev.h" // for ImplGlyphFallbackFontSubstitution
+#include "unotools/fontcfg.hxx" // for IMPL_FONT_ATTR_SYMBOL
+
+#include "rtl/logfile.hxx"
+#include "rtl/tencinfo.h"
+#include "rtl/textcvt.h"
+#include "rtl/bootstrap.hxx"
+
+#include "i18npool/mslangid.hxx"
+
+#include "osl/module.h"
+#include "osl/file.hxx"
+#include "osl/thread.hxx"
+#include "osl/process.h"
+
+#include "tools/poly.hxx"
+#include "tools/debug.hxx"
+#include "tools/stream.hxx"
+
+#include "basegfx/polygon/b2dpolygon.hxx"
+#include "basegfx/polygon/b2dpolypolygon.hxx"
+#include "basegfx/matrix/b2dhommatrix.hxx"
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+#include "sft.hxx"
+
+#ifdef GCP_KERN_HACK
+#include <algorithm>
+#endif
+
+#ifdef ENABLE_GRAPHITE
+#include <graphite/GrClient.h>
+#include <graphite/WinFont.h>
+#endif
+
+#include <vector>
+#include <set>
+#include <map>
+
+using namespace vcl;
+
+static const int MAXFONTHEIGHT = 2048;
+
+// -----------
+// - Inlines -
+// -----------
+
+inline FIXED FixedFromDouble( double d )
+{
+ const long l = (long) ( d * 65536. );
+ return *(FIXED*) &l;
+}
+
+// -----------------------------------------------------------------------
+
+inline int IntTimes256FromFixed(FIXED f)
+{
+ int nFixedTimes256 = (f.value << 8) + ((f.fract+0x80) >> 8);
+ return nFixedTimes256;
+}
+
+// =======================================================================
+
+// these variables can be static because they store system wide settings
+static bool bImplSalCourierScalable = false;
+static bool bImplSalCourierNew = false;
+
+
+// =======================================================================
+
+// -----------------------------------------------------------------------
+
+// TODO: also support temporary TTC font files
+typedef std::map< String, ImplDevFontAttributes > FontAttrMap;
+
+class ImplFontAttrCache
+{
+private:
+ FontAttrMap aFontAttributes;
+ rtl::OUString aCacheFileName;
+ String aBaseURL;
+ BOOL bModified;
+
+protected:
+ String OptimizeURL( const String& rURL ) const;
+
+ enum{ MAGIC = 0x12349876 }; // change if fontattrcache format changes
+
+public:
+ ImplFontAttrCache( const String& rCacheFileName, const String& rBaseURL );
+ ~ImplFontAttrCache();
+
+ ImplDevFontAttributes GetFontAttr( const String& rFontFileName ) const;
+ void AddFontAttr( const String& rFontFileName, const ImplDevFontAttributes& );
+};
+
+ImplFontAttrCache::ImplFontAttrCache( const String& rFileNameURL, const String& rBaseURL ) : aBaseURL( rBaseURL )
+{
+ bModified = FALSE;
+ aBaseURL.ToLowerAscii(); // Windows only, no problem...
+
+ // open the cache file
+ osl::FileBase::getSystemPathFromFileURL( rFileNameURL, aCacheFileName );
+ SvFileStream aCacheFile( aCacheFileName, STREAM_READ );
+ if( !aCacheFile.IsOpen() )
+ return;
+
+ // check the cache version
+ sal_uInt32 nCacheMagic;
+ aCacheFile >> nCacheMagic;
+ if( nCacheMagic != ImplFontAttrCache::MAGIC )
+ return; // ignore cache and rewrite if no match
+
+ // read the cache entries from the file
+ String aFontFileURL, aFontName;
+ ImplDevFontAttributes aDFA;
+ for(;;)
+ {
+ aCacheFile.ReadByteString( aFontFileURL, RTL_TEXTENCODING_UTF8 );
+ if( !aFontFileURL.Len() )
+ break;
+ aCacheFile.ReadByteString( aDFA.maName, RTL_TEXTENCODING_UTF8 );
+
+ short n;
+ aCacheFile >> n; aDFA.meWeight = static_cast<FontWeight>(n);
+ aCacheFile >> n; aDFA.meItalic = static_cast<FontItalic>(n);
+ aCacheFile >> n; aDFA.mePitch = static_cast<FontPitch>(n);
+ aCacheFile >> n; aDFA.meWidthType = static_cast<FontWidth>(n);
+ aCacheFile >> n; aDFA.meFamily = static_cast<FontFamily>(n);
+ aCacheFile >> n; aDFA.mbSymbolFlag = (n != 0);
+
+ aCacheFile.ReadByteStringLine( aDFA.maStyleName, RTL_TEXTENCODING_UTF8 );
+
+ aFontAttributes[ aFontFileURL ] = aDFA;
+ }
+}
+
+ImplFontAttrCache::~ImplFontAttrCache()
+{
+ if ( bModified )
+ {
+ SvFileStream aCacheFile( aCacheFileName, STREAM_WRITE|STREAM_TRUNC );
+ if ( aCacheFile.IsWritable() )
+ {
+ sal_uInt32 nCacheMagic = ImplFontAttrCache::MAGIC;
+ aCacheFile << nCacheMagic;
+
+ // write the cache entries to the file
+ FontAttrMap::const_iterator aIter = aFontAttributes.begin();
+ while ( aIter != aFontAttributes.end() )
+ {
+ const String rFontFileURL( (*aIter).first );
+ const ImplDevFontAttributes& rDFA( (*aIter).second );
+ aCacheFile.WriteByteString( rFontFileURL, RTL_TEXTENCODING_UTF8 );
+ aCacheFile.WriteByteString( rDFA.maName, RTL_TEXTENCODING_UTF8 );
+
+ aCacheFile << static_cast<short>(rDFA.meWeight);
+ aCacheFile << static_cast<short>(rDFA.meItalic);
+ aCacheFile << static_cast<short>(rDFA.mePitch);
+ aCacheFile << static_cast<short>(rDFA.meWidthType);
+ aCacheFile << static_cast<short>(rDFA.meFamily);
+ aCacheFile << static_cast<short>(rDFA.mbSymbolFlag != false);
+
+ aCacheFile.WriteByteStringLine( rDFA.maStyleName, RTL_TEXTENCODING_UTF8 );
+
+ aIter++;
+ }
+ // EOF Marker
+ String aEmptyStr;
+ aCacheFile.WriteByteString( aEmptyStr, RTL_TEXTENCODING_UTF8 );
+ }
+ }
+}
+
+String ImplFontAttrCache::OptimizeURL( const String& rURL ) const
+{
+ String aOptimizedFontFileURL( rURL );
+ aOptimizedFontFileURL.ToLowerAscii(); // Windows only, no problem...
+ if ( aOptimizedFontFileURL.CompareTo( aBaseURL, aBaseURL.Len() ) == COMPARE_EQUAL )
+ aOptimizedFontFileURL = aOptimizedFontFileURL.Copy( aBaseURL.Len() );
+ return aOptimizedFontFileURL;
+}
+
+ImplDevFontAttributes ImplFontAttrCache::GetFontAttr( const String& rFontFileName ) const
+{
+ ImplDevFontAttributes aDFA;
+ FontAttrMap::const_iterator it = aFontAttributes.find( OptimizeURL( rFontFileName ) );
+ if( it != aFontAttributes.end() )
+ {
+ aDFA = it->second;
+ }
+ return aDFA;
+}
+
+void ImplFontAttrCache::AddFontAttr( const String& rFontFileName, const ImplDevFontAttributes& rDFA )
+{
+ DBG_ASSERT( rFontFileName.Len() && rDFA.maName.Len(), "ImplFontNameCache::AddFontName - invalid data!" );
+ if ( rFontFileName.Len() && rDFA.maName.Len() )
+ {
+ aFontAttributes.insert( FontAttrMap::value_type( OptimizeURL( rFontFileName ), rDFA ) );
+ bModified = TRUE;
+ }
+}
+
+// =======================================================================
+
+// raw font data with a scoped lifetime
+class RawFontData
+{
+public:
+ explicit RawFontData( HDC, DWORD nTableTag=0 );
+ ~RawFontData() { delete[] mpRawBytes; }
+ const unsigned char* get() const { return mpRawBytes; }
+ const unsigned char* steal() { unsigned char* p = mpRawBytes; mpRawBytes = NULL; return p; }
+ const int size() const { return mnByteCount; }
+
+private:
+ unsigned char* mpRawBytes;
+ int mnByteCount;
+};
+
+RawFontData::RawFontData( HDC hDC, DWORD nTableTag )
+: mpRawBytes( NULL )
+, mnByteCount( 0 )
+{
+ // get required size in bytes
+ mnByteCount = ::GetFontData( hDC, nTableTag, 0, NULL, 0 );
+ if( mnByteCount == GDI_ERROR )
+ return;
+ else if( !mnByteCount )
+ return;
+
+ // allocate the array
+ mpRawBytes = new unsigned char[ mnByteCount ];
+
+ // get raw data in chunks small enough for GetFontData()
+ int nRawDataOfs = 0;
+ DWORD nMaxChunkSize = 0x100000;
+ for(;;)
+ {
+ // calculate remaining raw data to get
+ DWORD nFDGet = mnByteCount - nRawDataOfs;
+ if( nFDGet <= 0 )
+ break;
+ // #i56745# limit GetFontData requests
+ if( nFDGet > nMaxChunkSize )
+ nFDGet = nMaxChunkSize;
+ const DWORD nFDGot = ::GetFontData( hDC, nTableTag, nRawDataOfs,
+ (void*)(mpRawBytes + nRawDataOfs), nFDGet );
+ if( !nFDGot )
+ break;
+ else if( nFDGot != GDI_ERROR )
+ nRawDataOfs += nFDGot;
+ else
+ {
+ // was the chunk too big? reduce it
+ nMaxChunkSize /= 2;
+ if( nMaxChunkSize < 0x10000 )
+ break;
+ }
+ }
+
+ // cleanup if the raw data is incomplete
+ if( nRawDataOfs != mnByteCount )
+ {
+ delete[] mpRawBytes;
+ mpRawBytes = NULL;
+ }
+}
+
+// ===========================================================================
+// platform specific font substitution hooks for glyph fallback enhancement
+// TODO: move into i18n module (maybe merge with svx/ucsubset.*
+// or merge with i18nutil/source/utility/unicode_data.h)
+struct Unicode2LangType
+{
+ sal_UCS4 mnMinCode;
+ sal_UCS4 mnMaxCode;
+ LanguageType mnLangID;
+};
+
+// entries marked with default-CJK get replaced with the default-CJK language
+#define LANGUAGE_DEFAULT_CJK 0xFFF0
+
+// map unicode ranges to languages supported by OOo
+// NOTE: due to the binary search used this list must be sorted by mnMinCode
+static Unicode2LangType aLangFromCodeChart[]= {
+ {0x0000, 0x007F, LANGUAGE_ENGLISH}, // Basic Latin
+ {0x0080, 0x024F, LANGUAGE_ENGLISH}, // Latin Extended-A and Latin Extended-B
+ {0x0250, 0x02AF, LANGUAGE_SYSTEM}, // IPA Extensions
+ {0x0370, 0x03FF, LANGUAGE_GREEK}, // Greek
+ {0x0590, 0x05FF, LANGUAGE_HEBREW}, // Hebrew
+ {0x0600, 0x06FF, LANGUAGE_ARABIC_PRIMARY_ONLY}, // Arabic
+ {0x0900, 0x097F, LANGUAGE_HINDI}, // Devanagari
+ {0x0980, 0x09FF, LANGUAGE_BENGALI}, // Bengali
+ {0x0A80, 0x0AFF, LANGUAGE_GUJARATI}, // Gujarati
+ {0x0B00, 0x0B7F, LANGUAGE_ORIYA}, // Oriya
+ {0x0B80, 0x0BFF, LANGUAGE_TAMIL}, // Tamil
+ {0x0C00, 0x0C7F, LANGUAGE_TELUGU}, // Telugu
+ {0x0C80, 0x0CFF, LANGUAGE_KANNADA}, // Kannada
+ {0x0D00, 0x0D7F, LANGUAGE_MALAYALAM}, // Malayalam
+ {0x0D80, 0x0D7F, LANGUAGE_SINHALESE_SRI_LANKA}, // Sinhala
+ {0x0E00, 0x0E7F, LANGUAGE_THAI}, // Thai
+ {0x0E80, 0x0EFF, LANGUAGE_LAO}, // Lao
+ {0x0F00, 0x0FFF, LANGUAGE_TIBETAN}, // Tibetan
+ {0x1000, 0x109F, LANGUAGE_BURMESE}, // Burmese
+ {0x10A0, 0x10FF, LANGUAGE_GEORGIAN}, // Georgian
+ {0x1100, 0x11FF, LANGUAGE_KOREAN}, // Hangul Jamo, Korean-specific
+// {0x1200, 0x139F, LANGUAGE_AMHARIC_ETHIOPIA}, // Ethiopic
+// {0x1200, 0x139F, LANGUAGE_TIGRIGNA_ETHIOPIA}, // Ethiopic
+ {0x13A0, 0x13FF, LANGUAGE_CHEROKEE_UNITED_STATES}, // Cherokee
+// {0x1400, 0x167F, LANGUAGE_CANADIAN_ABORIGINAL}, // Canadian Aboriginial Syllabics
+// {0x1680, 0x169F, LANGUAGE_OGHAM}, // Ogham
+// {0x16A0, 0x16F0, LANGUAGE_RUNIC}, // Runic
+// {0x1700, 0x171F, LANGUAGE_TAGALOG}, // Tagalog
+// {0x1720, 0x173F, LANGUAGE_HANUNOO}, // Hanunoo
+// {0x1740, 0x175F, LANGUAGE_BUHID}, // Buhid
+// {0x1760, 0x177F, LANGUAGE_TAGBANWA}, // Tagbanwa
+ {0x1780, 0x17FF, LANGUAGE_KHMER}, // Khmer
+ {0x18A0, 0x18AF, LANGUAGE_MONGOLIAN}, // Mongolian
+// {0x1900, 0x194F, LANGUAGE_LIMBU}, // Limbu
+// {0x1950, 0x197F, LANGUAGE_TAILE}, // Tai Le
+// {0x1980, 0x19DF, LANGUAGE_TAILUE}, // Tai Lue
+ {0x19E0, 0x19FF, LANGUAGE_KHMER}, // Khmer Symbols
+// {0x1A00, 0x1A1F, LANGUAGE_BUGINESE}, // Buginese/Lontara
+// {0x1B00, 0x1B7F, LANGUAGE_BALINESE}, // Balinese
+// {0x1D00, 0x1DFF, LANGUAGE_NONE}, // Phonetic Symbols
+ {0x1E00, 0x1EFF, LANGUAGE_ENGLISH}, // Latin Extended Additional
+ {0x1F00, 0x1FFF, LANGUAGE_GREEK}, // Greek Extended
+ {0x2C60, 0x2C7F, LANGUAGE_ENGLISH}, // Latin Extended-C
+ {0x2E80, 0x2FFf, LANGUAGE_CHINESE_SIMPLIFIED}, // CJK Radicals Supplement + Kangxi Radical + Ideographic Description Characters
+ {0x3000, 0x303F, LANGUAGE_DEFAULT_CJK}, // CJK Symbols and punctuation
+ {0x3040, 0x30FF, LANGUAGE_JAPANESE}, // Japanese Hiragana + Katakana
+ {0x3100, 0x312F, LANGUAGE_CHINESE_TRADITIONAL}, // Bopomofo
+ {0x3130, 0x318F, LANGUAGE_KOREAN}, // Hangul Compatibility Jamo, Kocrean-specific
+ {0x3190, 0x319F, LANGUAGE_JAPANESE}, // Kanbun
+ {0x31A0, 0x31BF, LANGUAGE_CHINESE_TRADITIONAL}, // Bopomofo Extended
+ {0x31C0, 0x31EF, LANGUAGE_DEFAULT_CJK}, // CJK Ideographs
+ {0x31F0, 0x31FF, LANGUAGE_JAPANESE}, // Japanese Katakana Phonetic Extensions
+ {0x3200, 0x321F, LANGUAGE_KOREAN}, // Parenthesized Hangul
+ {0x3220, 0x325F, LANGUAGE_DEFAULT_CJK}, // Parenthesized Ideographs
+ {0x3260, 0x327F, LANGUAGE_KOREAN}, // Circled Hangul
+ {0x3280, 0x32CF, LANGUAGE_DEFAULT_CJK}, // Circled Ideographs
+ {0x32d0, 0x32FF, LANGUAGE_JAPANESE}, // Japanese Circled Katakana
+ {0x3400, 0x4DBF, LANGUAGE_DEFAULT_CJK}, // CJK Unified Ideographs Extension A
+ {0x4E00, 0x9FCF, LANGUAGE_DEFAULT_CJK}, // Unified CJK Ideographs
+ {0xA720, 0xA7FF, LANGUAGE_ENGLISH}, // Latin Extended-D
+ {0xAC00, 0xD7AF, LANGUAGE_KOREAN}, // Hangul Syllables, Korean-specific
+ {0xF900, 0xFAFF, LANGUAGE_DEFAULT_CJK}, // CJK Compatibility Ideographs
+ {0xFB00, 0xFB4F, LANGUAGE_HEBREW}, // Hebrew Presentation Forms
+ {0xFB50, 0xFDFF, LANGUAGE_ARABIC_PRIMARY_ONLY}, // Arabic Presentation Forms-A
+ {0xFE70, 0xFEFE, LANGUAGE_ARABIC_PRIMARY_ONLY}, // Arabic Presentation Forms-B
+ {0xFF65, 0xFF9F, LANGUAGE_JAPANESE}, // Japanese Halfwidth Katakana variant
+ {0xFFA0, 0xFFDC, LANGUAGE_KOREAN}, // Kocrean halfwidth hangual variant
+ {0x10140, 0x1018F, LANGUAGE_GREEK}, // Ancient Greak numbers
+ {0x1D200, 0x1D24F, LANGUAGE_GREEK}, // Ancient Greek Musical
+ {0x20000, 0x2A6DF, LANGUAGE_DEFAULT_CJK}, // CJK Unified Ideographs Extension B
+ {0x2F800, 0x2FA1F, LANGUAGE_DEFAULT_CJK} // CJK Compatibility Ideographs Supplement
+};
+
+// get language matching to the missing char
+LanguageType MapCharToLanguage( sal_UCS4 uChar )
+{
+ // entries marked with default-CJK get replaced with the prefered CJK language
+ static bool bFirst = true;
+ if( bFirst )
+ {
+ bFirst = false;
+
+ // use method suggested in #i97086# to determnine the systems default language
+ // TODO: move into i18npool or sal/osl/w32/nlsupport.c
+ LanguageType nDefaultLang = 0;
+ HKEY hKey = NULL;
+ LONG lResult = ::RegOpenKeyExA( HKEY_LOCAL_MACHINE,
+ "SYSTEM\\CurrentControlSet\\Control\\Nls\\Language",
+ 0, KEY_QUERY_VALUE, &hKey );
+ char aKeyValBuf[16];
+ DWORD nKeyValSize = sizeof(aKeyValBuf);
+ if( ERROR_SUCCESS == lResult )
+ lResult = RegQueryValueExA( hKey, "Default", NULL, NULL, (LPBYTE)aKeyValBuf, &nKeyValSize );
+ aKeyValBuf[ sizeof(aKeyValBuf)-1 ] = '\0';
+ if( ERROR_SUCCESS == lResult )
+ nDefaultLang = (LanguageType)rtl_str_toInt32( aKeyValBuf, 16 );
+
+ // TODO: use the default-CJK language selected in
+ // Tools->Options->LangSettings->Languages when it becomes available here
+ if( !nDefaultLang )
+ nDefaultLang = Application::GetSettings().GetUILanguage();
+
+ LanguageType nDefaultCJK = LANGUAGE_CHINESE;
+ switch( nDefaultLang )
+ {
+ case LANGUAGE_JAPANESE:
+ case LANGUAGE_KOREAN:
+ case LANGUAGE_KOREAN_JOHAB:
+ case LANGUAGE_CHINESE_SIMPLIFIED:
+ case LANGUAGE_CHINESE_TRADITIONAL:
+ case LANGUAGE_CHINESE_SINGAPORE:
+ case LANGUAGE_CHINESE_HONGKONG:
+ case LANGUAGE_CHINESE_MACAU:
+ nDefaultCJK = nDefaultLang;
+ break;
+ default:
+ nDefaultCJK = LANGUAGE_CHINESE;
+ break;
+ }
+
+ // change the marked entries to prefered language
+ static const int nCount = (sizeof(aLangFromCodeChart) / sizeof(*aLangFromCodeChart));
+ for( int i = 0; i < nCount; ++i )
+ {
+ if( aLangFromCodeChart[ i].mnLangID == LANGUAGE_DEFAULT_CJK )
+ aLangFromCodeChart[ i].mnLangID = nDefaultCJK;
+ }
+ }
+
+ // binary search
+ int nLow = 0;
+ int nHigh = (sizeof(aLangFromCodeChart) / sizeof(*aLangFromCodeChart)) - 1;
+ while( nLow <= nHigh )
+ {
+ int nMiddle = (nHigh + nLow) / 2;
+ if( uChar < aLangFromCodeChart[ nMiddle].mnMinCode )
+ nHigh = nMiddle - 1;
+ else if( uChar > aLangFromCodeChart[ nMiddle].mnMaxCode )
+ nLow = nMiddle + 1;
+ else
+ return aLangFromCodeChart[ nMiddle].mnLangID;
+ }
+
+ return LANGUAGE_DONTKNOW;
+}
+
+class WinGlyphFallbackSubstititution
+: public ImplGlyphFallbackFontSubstitution
+{
+public:
+ explicit WinGlyphFallbackSubstititution( HDC );
+
+ bool FindFontSubstitute( ImplFontSelectData&, rtl::OUString& rMissingChars ) const;
+private:
+ HDC mhDC;
+ bool HasMissingChars( const ImplFontData*, const rtl::OUString& rMissingChars ) const;
+};
+
+inline WinGlyphFallbackSubstititution::WinGlyphFallbackSubstititution( HDC hDC )
+: mhDC( hDC )
+{}
+
+void ImplGetLogFontFromFontSelect( HDC, const ImplFontSelectData*,
+ LOGFONTW&, bool /*bTestVerticalAvail*/ );
+
+// does a font face hold the given missing characters?
+bool WinGlyphFallbackSubstititution::HasMissingChars( const ImplFontData* pFace, const rtl::OUString& rMissingChars ) const
+{
+ const ImplWinFontData* pWinFont = static_cast<const ImplWinFontData*>(pFace);
+ const ImplFontCharMap* pCharMap = pWinFont->GetImplFontCharMap();
+ if( !pCharMap )
+ {
+ // construct a Size structure as the parameter of constructor of class ImplFontSelectData
+ const Size aSize( pFace->GetWidth(), pFace->GetHeight() );
+ // create a ImplFontSelectData object for getting s LOGFONT
+ const ImplFontSelectData aFSD( *pFace, aSize, (float)aSize.Height(), 0, false );
+ // construct log font
+ LOGFONTW aLogFont;
+ ImplGetLogFontFromFontSelect( mhDC, &aFSD, aLogFont, true );
+
+ // create HFONT from log font
+ HFONT hNewFont = ::CreateFontIndirectW( &aLogFont );
+ // select the new font into device
+ HFONT hOldFont = ::SelectFont( mhDC, hNewFont );
+
+ // read CMAP table to update their pCharMap
+ pWinFont->UpdateFromHDC( mhDC );;
+
+ // cleanup temporary font
+ ::SelectFont( mhDC, hOldFont );
+ ::DeleteFont( hNewFont );
+
+ // get the new charmap
+ pCharMap = pWinFont->GetImplFontCharMap();
+ }
+
+ // avoid fonts with unknown CMAP subtables for glyph fallback
+ if( !pCharMap || pCharMap->IsDefaultMap() )
+ return false;
+ pCharMap->AddReference();
+
+ int nMatchCount = 0;
+ // static const int nMaxMatchCount = 1; // TODO: tolerate more missing characters?
+ const sal_Int32 nStrLen = rMissingChars.getLength();
+ for( sal_Int32 nStrIdx = 0; nStrIdx < nStrLen; ++nStrIdx )
+ {
+ const sal_UCS4 uChar = rMissingChars.iterateCodePoints( &nStrIdx );
+ nMatchCount += pCharMap->HasChar( uChar );
+ break; // for now
+ }
+ pCharMap->DeReference();
+
+ const bool bHasMatches = (nMatchCount > 0);
+ return bHasMatches;
+}
+
+// find a fallback font for missing characters
+// TODO: should stylistic matches be searched and prefered?
+bool WinGlyphFallbackSubstititution::FindFontSubstitute( ImplFontSelectData& rFontSelData, rtl::OUString& rMissingChars ) const
+{
+ // guess a locale matching to the missing chars
+ com::sun::star::lang::Locale aLocale;
+
+ sal_Int32 nStrIdx = 0;
+ const sal_Int32 nStrLen = rMissingChars.getLength();
+ while( nStrIdx < nStrLen )
+ {
+ const sal_UCS4 uChar = rMissingChars.iterateCodePoints( &nStrIdx );
+ const LanguageType eLang = MapCharToLanguage( uChar );
+ if( eLang == LANGUAGE_DONTKNOW )
+ continue;
+ MsLangId::convertLanguageToLocale( eLang, aLocale );
+ break;
+ }
+
+ // fall back to default UI locale if the missing characters are inconclusive
+ if( nStrIdx >= nStrLen )
+ aLocale = Application::GetSettings().GetUILocale();
+
+ // first level fallback:
+ // try use the locale specific default fonts defined in VCL.xcu
+ const ImplDevFontList* pDevFontList = ImplGetSVData()->maGDIData.mpScreenFontList;
+ /*const*/ ImplDevFontListData* pDevFont = pDevFontList->ImplFindByLocale( aLocale );
+ if( pDevFont )
+ {
+ const ImplFontData* pFace = pDevFont->FindBestFontFace( rFontSelData );
+ if( HasMissingChars( pFace, rMissingChars ) )
+ {
+ rFontSelData.maSearchName = pDevFont->GetSearchName();
+ return true;
+ }
+ }
+
+ // are the missing characters symbols?
+ pDevFont = pDevFontList->ImplFindByAttributes( IMPL_FONT_ATTR_SYMBOL,
+ rFontSelData.meWeight, rFontSelData.meWidthType,
+ rFontSelData.meFamily, rFontSelData.meItalic, rFontSelData.maSearchName );
+ if( pDevFont )
+ {
+ const ImplFontData* pFace = pDevFont->FindBestFontFace( rFontSelData );
+ if( HasMissingChars( pFace, rMissingChars ) )
+ {
+ rFontSelData.maSearchName = pDevFont->GetSearchName();
+ return true;
+ }
+ }
+
+ // last level fallback, check each font type face one by one
+ const ImplGetDevFontList* pTestFontList = pDevFontList->GetDevFontList();
+ // limit the count of fonts to be checked to prevent hangs
+ static const int MAX_GFBFONT_COUNT = 600;
+ int nTestFontCount = pTestFontList->Count();
+ if( nTestFontCount > MAX_GFBFONT_COUNT )
+ nTestFontCount = MAX_GFBFONT_COUNT;
+
+ for( int i = 0; i < nTestFontCount; ++i )
+ {
+ const ImplFontData* pFace = pTestFontList->Get( i );
+ if( !HasMissingChars( pFace, rMissingChars ) )
+ continue;
+ rFontSelData.maSearchName = pFace->maName;
+ return true;
+ }
+
+ return false;
+}
+
+// =======================================================================
+
+struct ImplEnumInfo
+{
+ HDC mhDC;
+ ImplDevFontList* mpList;
+ String* mpName;
+ LOGFONTA* mpLogFontA;
+ LOGFONTW* mpLogFontW;
+ UINT mnPreferedCharSet;
+ bool mbCourier;
+ bool mbImplSalCourierScalable;
+ bool mbImplSalCourierNew;
+ bool mbPrinter;
+ int mnFontCount;
+};
+
+// =======================================================================
+
+static CharSet ImplCharSetToSal( BYTE nCharSet )
+{
+ rtl_TextEncoding eTextEncoding;
+
+ if ( nCharSet == OEM_CHARSET )
+ {
+ UINT nCP = (USHORT)GetOEMCP();
+ switch ( nCP )
+ {
+ // It is unclear why these two (undefined?) code page numbers are
+ // handled specially here:
+ case 1004: eTextEncoding = RTL_TEXTENCODING_MS_1252; break;
+ case 65400: eTextEncoding = RTL_TEXTENCODING_SYMBOL; break;
+ default:
+ eTextEncoding = rtl_getTextEncodingFromWindowsCodePage(nCP);
+ break;
+ };
+ }
+ else
+ {
+ if( nCharSet )
+ eTextEncoding = rtl_getTextEncodingFromWindowsCharset( nCharSet );
+ else
+ eTextEncoding = RTL_TEXTENCODING_UNICODE;
+ }
+
+ return eTextEncoding;
+}
+
+// -----------------------------------------------------------------------
+
+static FontFamily ImplFamilyToSal( BYTE nFamily )
+{
+ switch ( nFamily & 0xF0 )
+ {
+ case FF_DECORATIVE:
+ return FAMILY_DECORATIVE;
+
+ case FF_MODERN:
+ return FAMILY_MODERN;
+
+ case FF_ROMAN:
+ return FAMILY_ROMAN;
+
+ case FF_SCRIPT:
+ return FAMILY_SCRIPT;
+
+ case FF_SWISS:
+ return FAMILY_SWISS;
+
+ default:
+ break;
+ }
+
+ return FAMILY_DONTKNOW;
+}
+
+// -----------------------------------------------------------------------
+
+static BYTE ImplFamilyToWin( FontFamily eFamily )
+{
+ switch ( eFamily )
+ {
+ case FAMILY_DECORATIVE:
+ return FF_DECORATIVE;
+
+ case FAMILY_MODERN:
+ return FF_MODERN;
+
+ case FAMILY_ROMAN:
+ return FF_ROMAN;
+
+ case FAMILY_SCRIPT:
+ return FF_SCRIPT;
+
+ case FAMILY_SWISS:
+ return FF_SWISS;
+
+ case FAMILY_SYSTEM:
+ return FF_SWISS;
+
+ default:
+ break;
+ }
+
+ return FF_DONTCARE;
+}
+
+// -----------------------------------------------------------------------
+
+static FontWeight ImplWeightToSal( int nWeight )
+{
+ if ( nWeight <= FW_THIN )
+ return WEIGHT_THIN;
+ else if ( nWeight <= FW_ULTRALIGHT )
+ return WEIGHT_ULTRALIGHT;
+ else if ( nWeight <= FW_LIGHT )
+ return WEIGHT_LIGHT;
+ else if ( nWeight < FW_MEDIUM )
+ return WEIGHT_NORMAL;
+ else if ( nWeight == FW_MEDIUM )
+ return WEIGHT_MEDIUM;
+ else if ( nWeight <= FW_SEMIBOLD )
+ return WEIGHT_SEMIBOLD;
+ else if ( nWeight <= FW_BOLD )
+ return WEIGHT_BOLD;
+ else if ( nWeight <= FW_ULTRABOLD )
+ return WEIGHT_ULTRABOLD;
+ else
+ return WEIGHT_BLACK;
+}
+
+// -----------------------------------------------------------------------
+
+static int ImplWeightToWin( FontWeight eWeight )
+{
+ switch ( eWeight )
+ {
+ case WEIGHT_THIN:
+ return FW_THIN;
+
+ case WEIGHT_ULTRALIGHT:
+ return FW_ULTRALIGHT;
+
+ case WEIGHT_LIGHT:
+ return FW_LIGHT;
+
+ case WEIGHT_SEMILIGHT:
+ case WEIGHT_NORMAL:
+ return FW_NORMAL;
+
+ case WEIGHT_MEDIUM:
+ return FW_MEDIUM;
+
+ case WEIGHT_SEMIBOLD:
+ return FW_SEMIBOLD;
+
+ case WEIGHT_BOLD:
+ return FW_BOLD;
+
+ case WEIGHT_ULTRABOLD:
+ return FW_ULTRABOLD;
+
+ case WEIGHT_BLACK:
+ return FW_BLACK;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+inline FontPitch ImplLogPitchToSal( BYTE nPitch )
+{
+ if ( nPitch & FIXED_PITCH )
+ return PITCH_FIXED;
+ else
+ return PITCH_VARIABLE;
+}
+
+// -----------------------------------------------------------------------
+
+inline FontPitch ImplMetricPitchToSal( BYTE nPitch )
+{
+ // Sausaecke bei MS !! siehe NT Hilfe
+ if ( !(nPitch & TMPF_FIXED_PITCH) )
+ return PITCH_FIXED;
+ else
+ return PITCH_VARIABLE;
+}
+
+// -----------------------------------------------------------------------
+
+inline BYTE ImplPitchToWin( FontPitch ePitch )
+{
+ if ( ePitch == PITCH_FIXED )
+ return FIXED_PITCH;
+ else if ( ePitch == PITCH_VARIABLE )
+ return VARIABLE_PITCH;
+ else
+ return DEFAULT_PITCH;
+}
+
+// -----------------------------------------------------------------------
+
+static ImplDevFontAttributes WinFont2DevFontAttributes( const ENUMLOGFONTEXA& rEnumFont,
+ const NEWTEXTMETRICA& rMetric, DWORD nFontType )
+{
+ ImplDevFontAttributes aDFA;
+
+ const LOGFONTA rLogFont = rEnumFont.elfLogFont;
+
+ // get font face attributes
+ aDFA.meFamily = ImplFamilyToSal( rLogFont.lfPitchAndFamily );
+ aDFA.meWidthType = WIDTH_DONTKNOW;
+ aDFA.meWeight = ImplWeightToSal( rLogFont.lfWeight );
+ aDFA.meItalic = (rLogFont.lfItalic) ? ITALIC_NORMAL : ITALIC_NONE;
+ aDFA.mePitch = ImplLogPitchToSal( rLogFont.lfPitchAndFamily );
+ aDFA.mbSymbolFlag = (rLogFont.lfCharSet == SYMBOL_CHARSET);
+
+ // get the font face name
+ aDFA.maName = ImplSalGetUniString( rLogFont.lfFaceName );
+
+ // use the face's style name only if it looks reasonable
+ const char* pStyleName = (const char*)rEnumFont.elfStyle;
+ const char* pEnd = pStyleName + sizeof( rEnumFont.elfStyle );
+ const char* p = pStyleName;
+ for(; *p && (p < pEnd); ++p )
+ if( (0x00 < *p) && (*p < 0x20) )
+ break;
+ if( p < pEnd )
+ aDFA.maStyleName = ImplSalGetUniString( pStyleName );
+
+ // get device specific font attributes
+ aDFA.mbOrientation = (nFontType & RASTER_FONTTYPE) == 0;
+ aDFA.mbDevice = (rMetric.tmPitchAndFamily & TMPF_DEVICE) != 0;
+
+ aDFA.mbEmbeddable = false;
+ aDFA.mbSubsettable = false;
+ if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE))
+ || 0 != (rMetric.tmPitchAndFamily & TMPF_TRUETYPE))
+ aDFA.mbSubsettable = true;
+ else if( 0 != (rMetric.ntmFlags & NTM_TYPE1) ) // TODO: implement subsetting for type1 too
+ aDFA.mbEmbeddable = true;
+
+ // heuristics for font quality
+ // - standard-type1 > opentypeTT > truetype > non-standard-type1 > raster
+ // - subsetting > embedding > none
+ aDFA.mnQuality = 0;
+ if( rMetric.tmPitchAndFamily & TMPF_TRUETYPE )
+ aDFA.mnQuality += 50;
+ if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE)) )
+ aDFA.mnQuality += 10;
+ if( aDFA.mbSubsettable )
+ aDFA.mnQuality += 200;
+ else if( aDFA.mbEmbeddable )
+ aDFA.mnQuality += 100;
+
+ // #i38665# prefer Type1 versions of the standard postscript fonts
+ if( aDFA.mbEmbeddable )
+ {
+ if( aDFA.maName.EqualsAscii( "AvantGarde" )
+ || aDFA.maName.EqualsAscii( "Bookman" )
+ || aDFA.maName.EqualsAscii( "Courier" )
+ || aDFA.maName.EqualsAscii( "Helvetica" )
+ || aDFA.maName.EqualsAscii( "NewCenturySchlbk" )
+ || aDFA.maName.EqualsAscii( "Palatino" )
+ || aDFA.maName.EqualsAscii( "Symbol" )
+ || aDFA.maName.EqualsAscii( "Times" )
+ || aDFA.maName.EqualsAscii( "ZapfChancery" )
+ || aDFA.maName.EqualsAscii( "ZapfDingbats" ) )
+ aDFA.mnQuality += 500;
+ }
+
+ // TODO: add alias names
+ return aDFA;
+}
+
+// -----------------------------------------------------------------------
+
+static ImplDevFontAttributes WinFont2DevFontAttributes( const ENUMLOGFONTEXW& rEnumFont,
+ const NEWTEXTMETRICW& rMetric, DWORD nFontType )
+{
+ ImplDevFontAttributes aDFA;
+
+ const LOGFONTW rLogFont = rEnumFont.elfLogFont;
+
+ // get font face attributes
+ aDFA.meFamily = ImplFamilyToSal( rLogFont.lfPitchAndFamily );
+ aDFA.meWidthType = WIDTH_DONTKNOW;
+ aDFA.meWeight = ImplWeightToSal( rLogFont.lfWeight );
+ aDFA.meItalic = (rLogFont.lfItalic) ? ITALIC_NORMAL : ITALIC_NONE;
+ aDFA.mePitch = ImplLogPitchToSal( rLogFont.lfPitchAndFamily );
+ aDFA.mbSymbolFlag = (rLogFont.lfCharSet == SYMBOL_CHARSET);
+
+ // get the font face name
+ aDFA.maName = reinterpret_cast<const sal_Unicode*>(rLogFont.lfFaceName);
+
+ // use the face's style name only if it looks reasonable
+ const wchar_t* pStyleName = rEnumFont.elfStyle;
+ const wchar_t* pEnd = pStyleName + sizeof(rEnumFont.elfStyle)/sizeof(*rEnumFont.elfStyle);
+ const wchar_t* p = pStyleName;
+ for(; *p && (p < pEnd); ++p )
+ if( *p < 0x0020 )
+ break;
+ if( p < pEnd )
+ aDFA.maStyleName = reinterpret_cast<const sal_Unicode*>(pStyleName);
+
+ // get device specific font attributes
+ aDFA.mbOrientation = (nFontType & RASTER_FONTTYPE) == 0;
+ aDFA.mbDevice = (rMetric.tmPitchAndFamily & TMPF_DEVICE) != 0;
+
+ aDFA.mbEmbeddable = false;
+ aDFA.mbSubsettable = false;
+ if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE))
+ || 0 != (rMetric.tmPitchAndFamily & TMPF_TRUETYPE))
+ aDFA.mbSubsettable = true;
+ else if( 0 != (rMetric.ntmFlags & NTM_TYPE1) ) // TODO: implement subsetting for type1 too
+ aDFA.mbEmbeddable = true;
+
+ // heuristics for font quality
+ // - standard-type1 > opentypeTT > truetype > non-standard-type1 > raster
+ // - subsetting > embedding > none
+ aDFA.mnQuality = 0;
+ if( rMetric.tmPitchAndFamily & TMPF_TRUETYPE )
+ aDFA.mnQuality += 50;
+ if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE)) )
+ aDFA.mnQuality += 10;
+ if( aDFA.mbSubsettable )
+ aDFA.mnQuality += 200;
+ else if( aDFA.mbEmbeddable )
+ aDFA.mnQuality += 100;
+
+ // #i38665# prefer Type1 versions of the standard postscript fonts
+ if( aDFA.mbEmbeddable )
+ {
+ if( aDFA.maName.EqualsAscii( "AvantGarde" )
+ || aDFA.maName.EqualsAscii( "Bookman" )
+ || aDFA.maName.EqualsAscii( "Courier" )
+ || aDFA.maName.EqualsAscii( "Helvetica" )
+ || aDFA.maName.EqualsAscii( "NewCenturySchlbk" )
+ || aDFA.maName.EqualsAscii( "Palatino" )
+ || aDFA.maName.EqualsAscii( "Symbol" )
+ || aDFA.maName.EqualsAscii( "Times" )
+ || aDFA.maName.EqualsAscii( "ZapfChancery" )
+ || aDFA.maName.EqualsAscii( "ZapfDingbats" ) )
+ aDFA.mnQuality += 500;
+ }
+
+ // TODO: add alias names
+ return aDFA;
+}
+
+// -----------------------------------------------------------------------
+
+static ImplWinFontData* ImplLogMetricToDevFontDataA( const ENUMLOGFONTEXA* pLogFont,
+ const NEWTEXTMETRICA* pMetric,
+ DWORD nFontType )
+{
+ int nHeight = 0;
+ if ( nFontType & RASTER_FONTTYPE )
+ nHeight = pMetric->tmHeight - pMetric->tmInternalLeading;
+
+ ImplWinFontData* pData = new ImplWinFontData(
+ WinFont2DevFontAttributes(*pLogFont, *pMetric, nFontType),
+ nHeight,
+ pLogFont->elfLogFont.lfCharSet,
+ pMetric->tmPitchAndFamily );
+
+ return pData;
+}
+
+// -----------------------------------------------------------------------
+
+static ImplWinFontData* ImplLogMetricToDevFontDataW( const ENUMLOGFONTEXW* pLogFont,
+ const NEWTEXTMETRICW* pMetric,
+ DWORD nFontType )
+{
+ int nHeight = 0;
+ if ( nFontType & RASTER_FONTTYPE )
+ nHeight = pMetric->tmHeight - pMetric->tmInternalLeading;
+
+ ImplWinFontData* pData = new ImplWinFontData(
+ WinFont2DevFontAttributes(*pLogFont, *pMetric, nFontType),
+ nHeight,
+ pLogFont->elfLogFont.lfCharSet,
+ pMetric->tmPitchAndFamily );
+
+ return pData;
+}
+
+// -----------------------------------------------------------------------
+
+void ImplSalLogFontToFontA( HDC hDC, const LOGFONTA& rLogFont, Font& rFont )
+{
+ String aFontName( ImplSalGetUniString( rLogFont.lfFaceName ) );
+ if ( aFontName.Len() )
+ {
+ rFont.SetName( aFontName );
+ rFont.SetCharSet( ImplCharSetToSal( rLogFont.lfCharSet ) );
+ rFont.SetFamily( ImplFamilyToSal( rLogFont.lfPitchAndFamily ) );
+ rFont.SetPitch( ImplLogPitchToSal( rLogFont.lfPitchAndFamily ) );
+ rFont.SetWeight( ImplWeightToSal( rLogFont.lfWeight ) );
+
+ long nFontHeight = rLogFont.lfHeight;
+ if ( nFontHeight < 0 )
+ nFontHeight = -nFontHeight;
+ long nDPIY = GetDeviceCaps( hDC, LOGPIXELSY );
+ if( !nDPIY )
+ nDPIY = 600;
+ nFontHeight *= 72;
+ nFontHeight += nDPIY/2;
+ nFontHeight /= nDPIY;
+ rFont.SetSize( Size( 0, nFontHeight ) );
+ rFont.SetOrientation( (short)rLogFont.lfEscapement );
+ if ( rLogFont.lfItalic )
+ rFont.SetItalic( ITALIC_NORMAL );
+ else
+ rFont.SetItalic( ITALIC_NONE );
+ if ( rLogFont.lfUnderline )
+ rFont.SetUnderline( UNDERLINE_SINGLE );
+ else
+ rFont.SetUnderline( UNDERLINE_NONE );
+ if ( rLogFont.lfStrikeOut )
+ rFont.SetStrikeout( STRIKEOUT_SINGLE );
+ else
+ rFont.SetStrikeout( STRIKEOUT_NONE );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void ImplSalLogFontToFontW( HDC hDC, const LOGFONTW& rLogFont, Font& rFont )
+{
+ XubString aFontName( reinterpret_cast<const xub_Unicode*>(rLogFont.lfFaceName) );
+ if ( aFontName.Len() )
+ {
+ rFont.SetName( aFontName );
+ rFont.SetCharSet( ImplCharSetToSal( rLogFont.lfCharSet ) );
+ rFont.SetFamily( ImplFamilyToSal( rLogFont.lfPitchAndFamily ) );
+ rFont.SetPitch( ImplLogPitchToSal( rLogFont.lfPitchAndFamily ) );
+ rFont.SetWeight( ImplWeightToSal( rLogFont.lfWeight ) );
+
+ long nFontHeight = rLogFont.lfHeight;
+ if ( nFontHeight < 0 )
+ nFontHeight = -nFontHeight;
+ long nDPIY = GetDeviceCaps( hDC, LOGPIXELSY );
+ if( !nDPIY )
+ nDPIY = 600;
+ nFontHeight *= 72;
+ nFontHeight += nDPIY/2;
+ nFontHeight /= nDPIY;
+ rFont.SetSize( Size( 0, nFontHeight ) );
+ rFont.SetOrientation( (short)rLogFont.lfEscapement );
+ if ( rLogFont.lfItalic )
+ rFont.SetItalic( ITALIC_NORMAL );
+ else
+ rFont.SetItalic( ITALIC_NONE );
+ if ( rLogFont.lfUnderline )
+ rFont.SetUnderline( UNDERLINE_SINGLE );
+ else
+ rFont.SetUnderline( UNDERLINE_NONE );
+ if ( rLogFont.lfStrikeOut )
+ rFont.SetStrikeout( STRIKEOUT_SINGLE );
+ else
+ rFont.SetStrikeout( STRIKEOUT_NONE );
+ }
+}
+
+// =======================================================================
+
+ImplWinFontData::ImplWinFontData( const ImplDevFontAttributes& rDFS,
+ int nHeight, WIN_BYTE eWinCharSet, WIN_BYTE nPitchAndFamily )
+: ImplFontData( rDFS, 0 ),
+ meWinCharSet( eWinCharSet ),
+ mnPitchAndFamily( nPitchAndFamily ),
+ mpFontCharSets( NULL ),
+ mpUnicodeMap( NULL ),
+ mbGsubRead( false ),
+ mbDisableGlyphApi( false ),
+ mbHasKoreanRange( false ),
+ mbHasCJKSupport( false ),
+#ifdef ENABLE_GRAPHITE
+ mbHasGraphiteSupport( false ),
+#endif
+ mbHasArabicSupport ( false ),
+ mbAliasSymbolsLow( false ),
+ mbAliasSymbolsHigh( false ),
+ mnId( 0 ),
+ mpEncodingVector( NULL )
+{
+ SetBitmapSize( 0, nHeight );
+
+ if( eWinCharSet == SYMBOL_CHARSET )
+ {
+ if( (nPitchAndFamily & TMPF_TRUETYPE) != 0 )
+ {
+ // truetype fonts need their symbols as U+F0xx
+ mbAliasSymbolsHigh = true;
+ }
+ else if( (nPitchAndFamily & (TMPF_VECTOR|TMPF_DEVICE))
+ == (TMPF_VECTOR|TMPF_DEVICE) )
+ {
+ // scalable device fonts (e.g. builtin printer fonts)
+ // need their symbols as U+00xx
+ mbAliasSymbolsLow = true;
+ }
+ else if( (nPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE)) == 0 )
+ {
+ // bitmap fonts need their symbols as U+F0xx
+ mbAliasSymbolsHigh = true;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+ImplWinFontData::~ImplWinFontData()
+{
+ delete[] mpFontCharSets;
+
+ if( mpUnicodeMap )
+ mpUnicodeMap->DeReference();
+ delete mpEncodingVector;
+}
+
+// -----------------------------------------------------------------------
+
+sal_IntPtr ImplWinFontData::GetFontId() const
+{
+ return mnId;
+}
+
+// -----------------------------------------------------------------------
+
+void ImplWinFontData::UpdateFromHDC( HDC hDC ) const
+{
+ // short circuit if already initialized
+ if( mpUnicodeMap != NULL )
+ return;
+
+ ReadCmapTable( hDC );
+ ReadOs2Table( hDC );
+#ifdef ENABLE_GRAPHITE
+ static const char* pDisableGraphiteText = getenv( "SAL_DISABLE_GRAPHITE" );
+ if( !pDisableGraphiteText || (pDisableGraphiteText[0] == '0') )
+ {
+ mbHasGraphiteSupport = gr::WinFont::FontHasGraphiteTables(hDC);
+ }
+#endif
+
+ // even if the font works some fonts have problems with the glyph API
+ // => the heuristic below tries to figure out which fonts have the problem
+ TEXTMETRICA aTextMetric;
+ if( ::GetTextMetricsA( hDC, &aTextMetric ) )
+ if( !(aTextMetric.tmPitchAndFamily & TMPF_TRUETYPE)
+ || (aTextMetric.tmPitchAndFamily & TMPF_DEVICE) )
+ mbDisableGlyphApi = true;
+
+#if 0
+ // #110548# more important than #107885# => TODO: better solution
+ DWORD nFLI = GetFontLanguageInfo( hDC );
+ if( 0 == (nFLI & GCP_GLYPHSHAPE) )
+ mbDisableGlyphApi = true;
+#endif
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplWinFontData::HasGSUBstitutions( HDC hDC ) const
+{
+ if( !mbGsubRead )
+ ReadGsubTable( hDC );
+ return !maGsubTable.empty();
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplWinFontData::IsGSUBstituted( sal_UCS4 cChar ) const
+{
+ return( maGsubTable.find( cChar ) != maGsubTable.end() );
+}
+
+// -----------------------------------------------------------------------
+
+const ImplFontCharMap* ImplWinFontData::GetImplFontCharMap() const
+{
+ if( !mpUnicodeMap )
+ return NULL;
+ return mpUnicodeMap;
+}
+
+// -----------------------------------------------------------------------
+
+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]));}
+static inline DWORD CalcTag( const char p[4]) { return (p[0]+(p[1]<<8)+(p[2]<<16)+(p[3]<<24)); }
+
+void ImplWinFontData::ReadOs2Table( HDC hDC ) const
+{
+ const DWORD Os2Tag = CalcTag( "OS/2" );
+ DWORD nLength = ::GetFontData( hDC, Os2Tag, 0, NULL, 0 );
+ if( (nLength == GDI_ERROR) || !nLength )
+ return;
+ std::vector<unsigned char> aOS2map( nLength );
+ unsigned char* pOS2map = &aOS2map[0];
+ ::GetFontData( hDC, Os2Tag, 0, pOS2map, nLength );
+ sal_uInt32 nVersion = GetUShort( pOS2map );
+ if ( nVersion >= 0x0001 && nLength >= 58 )
+ {
+ // We need at least version 0x0001 (TrueType rev 1.66)
+ // to have access to the needed struct members.
+ sal_uInt32 ulUnicodeRange1 = GetUInt( pOS2map + 42 );
+ sal_uInt32 ulUnicodeRange2 = GetUInt( pOS2map + 46 );
+#if 0
+ sal_uInt32 ulUnicodeRange3 = GetUInt( pOS2map + 50 );
+ sal_uInt32 ulUnicodeRange4 = GetUInt( pOS2map + 54 );
+#endif
+
+ // Check for CJK capabilities of the current font
+ mbHasCJKSupport = (ulUnicodeRange2 & 0x2DF00000);
+ mbHasKoreanRange= (ulUnicodeRange1 & 0x10000000)
+ | (ulUnicodeRange2 & 0x01100000);
+ mbHasArabicSupport = (ulUnicodeRange1 & 0x00002000);
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void ImplWinFontData::ReadGsubTable( HDC hDC ) const
+{
+ mbGsubRead = true;
+
+ // check the existence of a GSUB table
+ const DWORD GsubTag = CalcTag( "GSUB" );
+ DWORD nRC = ::GetFontData( hDC, GsubTag, 0, NULL, 0 );
+ if( (nRC == GDI_ERROR) || !nRC )
+ return;
+
+ // parse the GSUB table through sft
+ // TODO: parse it directly
+
+ // sft needs the full font file data => get it
+ const RawFontData aRawFontData( hDC );
+ if( !aRawFontData.get() )
+ return;
+
+ // open font file
+ sal_uInt32 nFaceNum = 0;
+ if( !*aRawFontData.get() ) // TTC candidate
+ nFaceNum = ~0U; // indicate "TTC font extracts only"
+
+ TrueTypeFont* pTTFont = NULL;
+ ::OpenTTFontBuffer( (void*)aRawFontData.get(), aRawFontData.size(), nFaceNum, &pTTFont );
+ if( !pTTFont )
+ return;
+
+ // add vertically substituted characters to list
+ static const sal_Unicode aGSUBCandidates[] = {
+ 0x0020, 0x0080, // ASCII
+ 0x2000, 0x2600, // misc
+ 0x3000, 0x3100, // CJK punctutation
+ 0x3300, 0x3400, // squared words
+ 0xFF00, 0xFFF0, // halfwidth|fullwidth forms
+ 0 };
+
+ for( const sal_Unicode* pPair = aGSUBCandidates; *pPair; pPair += 2 )
+ for( sal_Unicode cChar = pPair[0]; cChar < pPair[1]; ++cChar )
+ if( ::MapChar( pTTFont, cChar, 0 ) != ::MapChar( pTTFont, cChar, 1 ) )
+ maGsubTable.insert( cChar ); // insert GSUBbed unicodes
+
+ CloseTTFont( pTTFont );
+}
+
+// -----------------------------------------------------------------------
+
+void ImplWinFontData::ReadCmapTable( HDC hDC ) const
+{
+ if( mpUnicodeMap != NULL )
+ return;
+
+ bool bIsSymbolFont = (meWinCharSet == SYMBOL_CHARSET);
+ // get the CMAP table from the font which is selected into the DC
+ const DWORD nCmapTag = CalcTag( "cmap" );
+ const RawFontData aRawFontData( hDC, nCmapTag );
+ // parse the CMAP table if available
+ if( aRawFontData.get() ) {
+ CmapResult aResult;
+ ParseCMAP( aRawFontData.get(), aRawFontData.size(), aResult );
+ mbDisableGlyphApi |= aResult.mbRecoded;
+ aResult.mbSymbolic = bIsSymbolFont;
+ if( aResult.mnRangeCount > 0 )
+ mpUnicodeMap = new ImplFontCharMap( aResult );
+ }
+
+ if( !mpUnicodeMap )
+ mpUnicodeMap = ImplFontCharMap::GetDefaultMap( bIsSymbolFont );
+ mpUnicodeMap->AddReference();
+}
+
+// =======================================================================
+
+void WinSalGraphics::SetTextColor( SalColor nSalColor )
+{
+ COLORREF aCol = PALETTERGB( SALCOLOR_RED( nSalColor ),
+ SALCOLOR_GREEN( nSalColor ),
+ SALCOLOR_BLUE( nSalColor ) );
+
+ if( !mbPrinter &&
+ GetSalData()->mhDitherPal &&
+ ImplIsSysColorEntry( nSalColor ) )
+ {
+ aCol = PALRGB_TO_RGB( aCol );
+ }
+
+ ::SetTextColor( mhDC, aCol );
+}
+
+// -----------------------------------------------------------------------
+
+int CALLBACK SalEnumQueryFontProcExW( const ENUMLOGFONTEXW*,
+ const NEWTEXTMETRICEXW*,
+ DWORD, LPARAM lParam )
+{
+ *((bool*)(void*)lParam) = true;
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+int CALLBACK SalEnumQueryFontProcExA( const ENUMLOGFONTEXA*,
+ const NEWTEXTMETRICEXA*,
+ DWORD, LPARAM lParam )
+{
+ *((bool*)(void*)lParam) = true;
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplIsFontAvailable( HDC hDC, const UniString& rName )
+{
+ bool bAvailable = false;
+
+ if ( aSalShlData.mbWNT )
+ {
+ // Test, if Font available
+ LOGFONTW aLogFont;
+ memset( &aLogFont, 0, sizeof( aLogFont ) );
+ aLogFont.lfCharSet = DEFAULT_CHARSET;
+
+ UINT nNameLen = rName.Len();
+ if ( nNameLen > (sizeof( aLogFont.lfFaceName )/sizeof( wchar_t ))-1 )
+ nNameLen = (sizeof( aLogFont.lfFaceName )/sizeof( wchar_t ))-1;
+ memcpy( aLogFont.lfFaceName, rName.GetBuffer(), nNameLen*sizeof( wchar_t ) );
+ aLogFont.lfFaceName[nNameLen] = 0;
+
+ EnumFontFamiliesExW( hDC, &aLogFont, (FONTENUMPROCW)SalEnumQueryFontProcExW,
+ (LPARAM)(void*)&bAvailable, 0 );
+ }
+ else
+ {
+ ByteString aTemp = ImplSalGetWinAnsiString( rName );
+
+ // Test, if Font available
+ LOGFONTA aLogFont;
+ memset( &aLogFont, 0, sizeof( aLogFont ) );
+ aLogFont.lfCharSet = DEFAULT_CHARSET;
+
+ UINT nNameLen = aTemp.Len();
+ if ( nNameLen > sizeof( aLogFont.lfFaceName )-1 )
+ nNameLen = sizeof( aLogFont.lfFaceName )-1;
+ memcpy( aLogFont.lfFaceName, aTemp.GetBuffer(), nNameLen );
+ aLogFont.lfFaceName[nNameLen] = 0;
+
+ EnumFontFamiliesExA( hDC, &aLogFont, (FONTENUMPROCA)SalEnumQueryFontProcExA,
+ (LPARAM)(void*)&bAvailable, 0 );
+ }
+
+ return bAvailable;
+}
+
+// -----------------------------------------------------------------------
+
+void ImplGetLogFontFromFontSelect( HDC hDC,
+ const ImplFontSelectData* pFont,
+ LOGFONTW& rLogFont,
+ bool /*bTestVerticalAvail*/ )
+{
+ UniString aName;
+ if ( pFont->mpFontData )
+ aName = pFont->mpFontData->maName;
+ else
+ aName = pFont->maName.GetToken( 0 );
+
+ UINT nNameLen = aName.Len();
+ if ( nNameLen > (sizeof( rLogFont.lfFaceName )/sizeof( wchar_t ))-1 )
+ nNameLen = (sizeof( rLogFont.lfFaceName )/sizeof( wchar_t ))-1;
+ memcpy( rLogFont.lfFaceName, aName.GetBuffer(), nNameLen*sizeof( wchar_t ) );
+ rLogFont.lfFaceName[nNameLen] = 0;
+
+ if( !pFont->mpFontData )
+ {
+ rLogFont.lfCharSet = pFont->IsSymbolFont() ? SYMBOL_CHARSET : DEFAULT_CHARSET;
+ rLogFont.lfPitchAndFamily = ImplPitchToWin( pFont->mePitch )
+ | ImplFamilyToWin( pFont->meFamily );
+ }
+ else
+ {
+ const ImplWinFontData* pWinFontData = static_cast<const ImplWinFontData*>( pFont->mpFontData );
+ rLogFont.lfCharSet = pWinFontData->GetCharSet();
+ rLogFont.lfPitchAndFamily = pWinFontData->GetPitchAndFamily();
+ }
+
+ rLogFont.lfWeight = ImplWeightToWin( pFont->meWeight );
+ rLogFont.lfHeight = (LONG)-pFont->mnHeight;
+ rLogFont.lfWidth = (LONG)pFont->mnWidth;
+ rLogFont.lfUnderline = 0;
+ rLogFont.lfStrikeOut = 0;
+ rLogFont.lfItalic = (pFont->meItalic) != ITALIC_NONE;
+ rLogFont.lfEscapement = pFont->mnOrientation;
+ rLogFont.lfOrientation = rLogFont.lfEscapement;
+ rLogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ rLogFont.lfQuality = DEFAULT_QUALITY;
+ rLogFont.lfOutPrecision = OUT_TT_PRECIS;
+ if ( pFont->mnOrientation )
+ rLogFont.lfClipPrecision |= CLIP_LH_ANGLES;
+
+ // disable antialiasing if requested
+ if ( pFont->mbNonAntialiased )
+ rLogFont.lfQuality = NONANTIALIASED_QUALITY;
+
+ // select vertical mode if requested and available
+ if( pFont->mbVertical && nNameLen )
+ {
+ // vertical fonts start with an '@'
+ memmove( &rLogFont.lfFaceName[1], &rLogFont.lfFaceName[0],
+ sizeof(rLogFont.lfFaceName)-sizeof(rLogFont.lfFaceName[0]) );
+ rLogFont.lfFaceName[0] = '@';
+
+ // check availability of vertical mode for this font
+ bool bAvailable = false;
+ EnumFontFamiliesExW( hDC, &rLogFont, (FONTENUMPROCW)SalEnumQueryFontProcExW,
+ (LPARAM)&bAvailable, 0 );
+
+ if( !bAvailable )
+ {
+ // restore non-vertical name if not vertical mode isn't available
+ memcpy( &rLogFont.lfFaceName[0], aName.GetBuffer(), nNameLen*sizeof(wchar_t) );
+ if( nNameLen < LF_FACESIZE )
+ rLogFont.lfFaceName[nNameLen] = '\0';
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+static void ImplGetLogFontFromFontSelect( HDC hDC,
+ const ImplFontSelectData* pFont,
+ LOGFONTA& rLogFont,
+ bool /*bTestVerticalAvail*/ )
+{
+ ByteString aName;
+ if( pFont->mpFontData )
+ aName = ImplSalGetWinAnsiString( pFont->mpFontData->maName );
+ else
+ aName = ImplSalGetWinAnsiString( pFont->maName.GetToken( 0 ) );
+
+ int nNameLen = aName.Len();
+ if( nNameLen > LF_FACESIZE )
+ nNameLen = LF_FACESIZE;
+ memcpy( rLogFont.lfFaceName, aName.GetBuffer(), nNameLen );
+ if( nNameLen < LF_FACESIZE )
+ rLogFont.lfFaceName[nNameLen] = '\0';
+
+ if( !pFont->mpFontData )
+ {
+ rLogFont.lfCharSet = pFont->IsSymbolFont() ? SYMBOL_CHARSET : DEFAULT_CHARSET;
+ rLogFont.lfPitchAndFamily = ImplPitchToWin( pFont->mePitch )
+ | ImplFamilyToWin( pFont->meFamily );
+ }
+ else
+ {
+ const ImplWinFontData* pWinFontData = static_cast<const ImplWinFontData*>( pFont->mpFontData );
+ rLogFont.lfCharSet = pWinFontData->GetCharSet();
+ rLogFont.lfPitchAndFamily = pWinFontData->GetPitchAndFamily();
+ }
+
+ rLogFont.lfWeight = ImplWeightToWin( pFont->meWeight );
+ rLogFont.lfHeight = (LONG)-pFont->mnHeight;
+ rLogFont.lfWidth = (LONG)pFont->mnWidth;
+ rLogFont.lfUnderline = 0;
+ rLogFont.lfStrikeOut = 0;
+ rLogFont.lfItalic = (pFont->meItalic) != ITALIC_NONE;
+ rLogFont.lfEscapement = pFont->mnOrientation;
+ rLogFont.lfOrientation = rLogFont.lfEscapement; // ignored by W98
+ rLogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ rLogFont.lfQuality = DEFAULT_QUALITY;
+ rLogFont.lfOutPrecision = OUT_TT_PRECIS;
+ if( pFont->mnOrientation )
+ rLogFont.lfClipPrecision |= CLIP_LH_ANGLES;
+
+ // disable antialiasing if requested
+ if( pFont->mbNonAntialiased )
+ rLogFont.lfQuality = NONANTIALIASED_QUALITY;
+
+ // select vertical mode if requested and available
+ if( pFont->mbVertical && nNameLen )
+ {
+ // vertical fonts start with an '@'
+ memmove( &rLogFont.lfFaceName[1], &rLogFont.lfFaceName[0],
+ sizeof(rLogFont.lfFaceName)-sizeof(rLogFont.lfFaceName[0]) );
+ rLogFont.lfFaceName[0] = '@';
+
+ // check availability of vertical mode for this font
+ bool bAvailable = false;
+ EnumFontFamiliesExA( hDC, &rLogFont, (FONTENUMPROCA)SalEnumQueryFontProcExA,
+ (LPARAM)&bAvailable, 0 );
+
+ if( !bAvailable )
+ {
+ // restore non-vertical name if vertical mode is not supported
+ memcpy( rLogFont.lfFaceName, aName.GetBuffer(), nNameLen );
+ if( nNameLen < LF_FACESIZE )
+ rLogFont.lfFaceName[nNameLen] = '\0';
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+HFONT WinSalGraphics::ImplDoSetFont( ImplFontSelectData* i_pFont, float& o_rFontScale, HFONT& o_rOldFont )
+{
+ HFONT hNewFont = 0;
+
+ HDC hdcScreen = 0;
+ if( mbVirDev )
+ // only required for virtual devices, see below for details
+ hdcScreen = GetDC(0);
+
+ if( aSalShlData.mbWNT )
+ {
+ LOGFONTW aLogFont;
+ ImplGetLogFontFromFontSelect( mhDC, i_pFont, aLogFont, true );
+
+ // on the display we prefer Courier New when Courier is a
+ // bitmap only font and we need to stretch or rotate it
+ if( mbScreen
+ && (i_pFont->mnWidth != 0
+ || i_pFont->mnOrientation != 0
+ || i_pFont->mpFontData == NULL
+ || (i_pFont->mpFontData->GetHeight() != i_pFont->mnHeight))
+ && !bImplSalCourierScalable
+ && bImplSalCourierNew
+ && (ImplSalWICompareAscii( aLogFont.lfFaceName, "Courier" ) == 0) )
+ lstrcpynW( aLogFont.lfFaceName, L"Courier New", 11 );
+
+ // #i47675# limit font requests to MAXFONTHEIGHT
+ // TODO: share MAXFONTHEIGHT font instance
+ if( (-aLogFont.lfHeight <= MAXFONTHEIGHT)
+ && (+aLogFont.lfWidth <= MAXFONTHEIGHT) )
+ {
+ o_rFontScale = 1.0;
+ }
+ else if( -aLogFont.lfHeight >= +aLogFont.lfWidth )
+ {
+ o_rFontScale = -aLogFont.lfHeight / (float)MAXFONTHEIGHT;
+ aLogFont.lfHeight = -MAXFONTHEIGHT;
+ aLogFont.lfWidth = FRound( aLogFont.lfWidth / o_rFontScale );
+ }
+ else // #i95867# also limit font widths
+ {
+ o_rFontScale = +aLogFont.lfWidth / (float)MAXFONTHEIGHT;
+ aLogFont.lfWidth = +MAXFONTHEIGHT;
+ aLogFont.lfHeight = FRound( aLogFont.lfHeight / o_rFontScale );
+ }
+
+ hNewFont = ::CreateFontIndirectW( &aLogFont );
+ if( hdcScreen )
+ {
+ // select font into screen hdc first to get an antialiased font
+ // see knowledge base article 305290:
+ // "PRB: Fonts Not Drawn Antialiased on Device Context for DirectDraw Surface"
+ SelectFont( hdcScreen, SelectFont( hdcScreen , hNewFont ) );
+ }
+ o_rOldFont = ::SelectFont( mhDC, hNewFont );
+
+ TEXTMETRICW aTextMetricW;
+ if( !::GetTextMetricsW( mhDC, &aTextMetricW ) )
+ {
+ // the selected font doesn't work => try a replacement
+ // TODO: use its font fallback instead
+ lstrcpynW( aLogFont.lfFaceName, L"Courier New", 11 );
+ aLogFont.lfPitchAndFamily = FIXED_PITCH;
+ HFONT hNewFont2 = CreateFontIndirectW( &aLogFont );
+ SelectFont( mhDC, hNewFont2 );
+ DeleteFont( hNewFont );
+ hNewFont = hNewFont2;
+ }
+ }
+ else
+ {
+ if( !mpLogFont )
+ // mpLogFont is needed for getting the kerning pairs
+ // TODO: get them from somewhere else
+ mpLogFont = new LOGFONTA;
+ LOGFONTA& aLogFont = *mpLogFont;
+ ImplGetLogFontFromFontSelect( mhDC, i_pFont, aLogFont, true );
+
+ // on the display we prefer Courier New when Courier is a
+ // bitmap only font and we need to stretch or rotate it
+ if( mbScreen
+ && (i_pFont->mnWidth != 0
+ || i_pFont->mnOrientation != 0
+ || i_pFont->mpFontData == NULL
+ || (i_pFont->mpFontData->GetHeight() != i_pFont->mnHeight))
+ && !bImplSalCourierScalable
+ && bImplSalCourierNew
+ && (stricmp( aLogFont.lfFaceName, "Courier" ) == 0) )
+ strncpy( aLogFont.lfFaceName, "Courier New", 11 );
+
+ // limit font requests to MAXFONTHEIGHT to work around driver problems
+ // TODO: share MAXFONTHEIGHT font instance
+ if( -aLogFont.lfHeight <= MAXFONTHEIGHT )
+ o_rFontScale = 1.0;
+ else
+ {
+ o_rFontScale = -aLogFont.lfHeight / (float)MAXFONTHEIGHT;
+ aLogFont.lfHeight = -MAXFONTHEIGHT;
+ aLogFont.lfWidth = static_cast<LONG>( aLogFont.lfWidth / o_rFontScale );
+ }
+
+ hNewFont = ::CreateFontIndirectA( &aLogFont );
+ if( hdcScreen )
+ {
+ // select font into screen hdc first to get an antialiased font
+ // see knowledge base article 305290:
+ // "PRB: Fonts Not Drawn Antialiased on Device Context for DirectDraw Surface"
+ ::SelectFont( hdcScreen, ::SelectFont( hdcScreen , hNewFont ) );
+ }
+ o_rOldFont = ::SelectFont( mhDC, hNewFont );
+
+ TEXTMETRICA aTextMetricA;
+ // when the font doesn't work try a replacement
+ if ( !::GetTextMetricsA( mhDC, &aTextMetricA ) )
+ {
+ // the selected font doesn't work => try a replacement
+ // TODO: use its font fallback instead
+ LOGFONTA aTempLogFont = aLogFont;
+ strncpy( aTempLogFont.lfFaceName, "Courier New", 11 );
+ aTempLogFont.lfPitchAndFamily = FIXED_PITCH;
+ HFONT hNewFont2 = CreateFontIndirectA( &aTempLogFont );
+ ::SelectFont( mhDC, hNewFont2 );
+ ::DeleteFont( hNewFont );
+ hNewFont = hNewFont2;
+ }
+ }
+
+ if( hdcScreen )
+ ::ReleaseDC( NULL, hdcScreen );
+
+ return hNewFont;
+}
+
+USHORT WinSalGraphics::SetFont( ImplFontSelectData* pFont, int nFallbackLevel )
+{
+ // return early if there is no new font
+ if( !pFont )
+ {
+ // deselect still active font
+ if( mhDefFont )
+ ::SelectFont( mhDC, mhDefFont );
+ // release no longer referenced font handles
+ for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
+ {
+ if( mhFonts[i] )
+ ::DeleteFont( mhFonts[i] );
+ mhFonts[ i ] = 0;
+ }
+ mhDefFont = 0;
+ return 0;
+ }
+
+ DBG_ASSERT( pFont->mpFontData, "WinSalGraphics mpFontData==NULL");
+ mpWinFontEntry[ nFallbackLevel ] = reinterpret_cast<ImplWinFontEntry*>( pFont->mpFontEntry );
+ mpWinFontData[ nFallbackLevel ] = static_cast<const ImplWinFontData*>( pFont->mpFontData );
+
+ HFONT hOldFont = 0;
+ HFONT hNewFont = ImplDoSetFont( pFont, mfFontScale, hOldFont );
+
+ if( !mhDefFont )
+ {
+ // keep default font
+ mhDefFont = hOldFont;
+ }
+ else
+ {
+ // release no longer referenced font handles
+ for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
+ {
+ if( mhFonts[i] )
+ {
+ ::DeleteFont( mhFonts[i] );
+ mhFonts[i] = 0;
+ }
+ }
+ }
+
+ // store new font in correct layer
+ mhFonts[ nFallbackLevel ] = hNewFont;
+ // now the font is live => update font face
+ if( mpWinFontData[ nFallbackLevel ] )
+ mpWinFontData[ nFallbackLevel ]->UpdateFromHDC( mhDC );
+
+ if( !nFallbackLevel )
+ {
+ mbFontKernInit = TRUE;
+ if ( mpFontKernPairs )
+ {
+ delete[] mpFontKernPairs;
+ mpFontKernPairs = NULL;
+ }
+ mnFontKernPairCount = 0;
+ }
+
+ mnFontCharSetCount = 0;
+
+ // some printers have higher internal resolution, so their
+ // text output would be different from what we calculated
+ // => suggest DrawTextArray to workaround this problem
+ if ( mbPrinter )
+ return SAL_SETFONT_USEDRAWTEXTARRAY;
+ else
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int nFallbackLevel )
+{
+ // temporarily change the HDC to the font in the fallback level
+ HFONT hOldFont = SelectFont( mhDC, mhFonts[nFallbackLevel] );
+
+ if ( aSalShlData.mbWNT )
+ {
+ wchar_t aFaceName[LF_FACESIZE+60];
+ if( ::GetTextFaceW( mhDC, sizeof(aFaceName)/sizeof(wchar_t), aFaceName ) )
+ pMetric->maName = reinterpret_cast<const sal_Unicode*>(aFaceName);
+ }
+ else
+ {
+ char aFaceName[LF_FACESIZE+60];
+ if( ::GetTextFaceA( mhDC, sizeof(aFaceName), aFaceName ) )
+ pMetric->maName = ImplSalGetUniString( aFaceName );
+ }
+
+ // get the font metric
+ TEXTMETRICA aWinMetric;
+ const bool bOK = GetTextMetricsA( mhDC, &aWinMetric );
+ // restore the HDC to the font in the base level
+ SelectFont( mhDC, hOldFont );
+ if( !bOK )
+ return;
+
+ // device independent font attributes
+ pMetric->meFamily = ImplFamilyToSal( aWinMetric.tmPitchAndFamily );;
+ pMetric->mbSymbolFlag = (aWinMetric.tmCharSet == SYMBOL_CHARSET);
+ pMetric->meWeight = ImplWeightToSal( aWinMetric.tmWeight );
+ pMetric->mePitch = ImplMetricPitchToSal( aWinMetric.tmPitchAndFamily );
+ pMetric->meItalic = aWinMetric.tmItalic ? ITALIC_NORMAL : ITALIC_NONE;
+ pMetric->mnSlant = 0;
+
+ // device dependend font attributes
+ pMetric->mbDevice = (aWinMetric.tmPitchAndFamily & TMPF_DEVICE) != 0;
+ pMetric->mbScalableFont = (aWinMetric.tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE)) != 0;
+ if( pMetric->mbScalableFont )
+ {
+ // check if there are kern pairs
+ // TODO: does this work with GPOS kerning?
+ DWORD nKernPairs = ::GetKerningPairsA( mhDC, 0, NULL );
+ pMetric->mbKernableFont = (nKernPairs > 0);
+ }
+ else
+ {
+ // bitmap fonts cannot be rotated directly
+ pMetric->mnOrientation = 0;
+ // bitmap fonts have no kerning
+ pMetric->mbKernableFont = false;
+ }
+
+ // transformation dependend font metrics
+ pMetric->mnWidth = static_cast<int>( mfFontScale * aWinMetric.tmAveCharWidth );
+ pMetric->mnIntLeading = static_cast<int>( mfFontScale * aWinMetric.tmInternalLeading );
+ pMetric->mnExtLeading = static_cast<int>( mfFontScale * aWinMetric.tmExternalLeading );
+ pMetric->mnAscent = static_cast<int>( mfFontScale * aWinMetric.tmAscent );
+ pMetric->mnDescent = static_cast<int>( mfFontScale * aWinMetric.tmDescent );
+
+ // #107888# improved metric compatibility for Asian fonts...
+ // TODO: assess workaround below for CWS >= extleading
+ // TODO: evaluate use of aWinMetric.sTypo* members for CJK
+ if( mpWinFontData[nFallbackLevel] && mpWinFontData[nFallbackLevel]->SupportsCJK() )
+ {
+ pMetric->mnIntLeading += pMetric->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 = pMetric->mnExtLeading / 2;
+ const long nOtherHalfTmpExtLeading = pMetric->mnExtLeading - nHalfTmpExtLeading;
+
+ // #110641# external leading for Asian fonts.
+ // The factor 0.3 has been confirmed with experiments.
+ long nCJKExtLeading = static_cast<long>(0.30 * (pMetric->mnAscent + pMetric->mnDescent));
+ nCJKExtLeading -= pMetric->mnExtLeading;
+ pMetric->mnExtLeading = (nCJKExtLeading > 0) ? nCJKExtLeading : 0;
+
+ pMetric->mnAscent += nHalfTmpExtLeading;
+ pMetric->mnDescent += nOtherHalfTmpExtLeading;
+
+ // #109280# HACK korean only: increase descent for wavelines and impr
+ if( !aSalShlData.mbWNT )
+ if( mpWinFontData[nFallbackLevel]->SupportsKorean() )
+ pMetric->mnDescent += pMetric->mnExtLeading;
+ }
+
+ pMetric->mnMinKashida = GetMinKashidaWidth();
+}
+
+// -----------------------------------------------------------------------
+
+int CALLBACK SalEnumCharSetsProcExA( const ENUMLOGFONTEXA* pLogFont,
+ const NEWTEXTMETRICEXA* /*pMetric*/,
+ DWORD /*nFontType*/, LPARAM lParam )
+{
+ WinSalGraphics* pData = (WinSalGraphics*)lParam;
+ // Charset already in the list?
+ for ( BYTE i = 0; i < pData->mnFontCharSetCount; i++ )
+ {
+ if ( pData->mpFontCharSets[i] == pLogFont->elfLogFont.lfCharSet )
+ return 1;
+ }
+ pData->mpFontCharSets[pData->mnFontCharSetCount] = pLogFont->elfLogFont.lfCharSet;
+ pData->mnFontCharSetCount++;
+ return 1;
+}
+
+// -----------------------------------------------------------------------
+
+static void ImplGetAllFontCharSets( WinSalGraphics* pData )
+{
+ if ( !pData->mpFontCharSets )
+ pData->mpFontCharSets = new BYTE[256];
+
+ LOGFONTA aLogFont;
+ memset( &aLogFont, 0, sizeof( aLogFont ) );
+ aLogFont.lfCharSet = DEFAULT_CHARSET;
+ GetTextFaceA( pData->mhDC, sizeof( aLogFont.lfFaceName ), aLogFont.lfFaceName );
+ EnumFontFamiliesExA( pData->mhDC, &aLogFont, (FONTENUMPROCA)SalEnumCharSetsProcExA,
+ (LPARAM)(void*)pData, 0 );
+}
+
+// -----------------------------------------------------------------------
+
+static void ImplAddKerningPairs( WinSalGraphics* pData )
+{
+ ULONG nPairs = ::GetKerningPairsA( pData->mhDC, 0, NULL );
+ if ( !nPairs )
+ return;
+
+ CHARSETINFO aInfo;
+ if ( !TranslateCharsetInfo( (DWORD*)(ULONG)GetTextCharset( pData->mhDC ), &aInfo, TCI_SRCCHARSET ) )
+ return;
+
+ if ( !pData->mpFontKernPairs )
+ pData->mpFontKernPairs = new KERNINGPAIR[nPairs];
+ else
+ {
+ KERNINGPAIR* pOldPairs = pData->mpFontKernPairs;
+ pData->mpFontKernPairs = new KERNINGPAIR[nPairs+pData->mnFontKernPairCount];
+ memcpy( pData->mpFontKernPairs, pOldPairs,
+ pData->mnFontKernPairCount*sizeof( KERNINGPAIR ) );
+ delete[] pOldPairs;
+ }
+
+ UINT nCP = aInfo.ciACP;
+ ULONG nOldPairs = pData->mnFontKernPairCount;
+ KERNINGPAIR* pTempPair = pData->mpFontKernPairs+pData->mnFontKernPairCount;
+ nPairs = ::GetKerningPairsA( pData->mhDC, nPairs, pTempPair );
+ for ( ULONG i = 0; i < nPairs; i++ )
+ {
+ unsigned char aBuf[2];
+ wchar_t nChar;
+ int nLen;
+ BOOL bAdd = TRUE;
+
+ // None-ASCII?, then we must convert the char
+ if ( (pTempPair->wFirst > 125) || (pTempPair->wFirst == 92) )
+ {
+ if ( pTempPair->wFirst < 256 )
+ {
+ aBuf[0] = (unsigned char)pTempPair->wFirst;
+ nLen = 1;
+ }
+ else
+ {
+ aBuf[0] = (unsigned char)(pTempPair->wFirst >> 8);
+ aBuf[1] = (unsigned char)(pTempPair->wFirst & 0xFF);
+ nLen = 2;
+ }
+ if ( MultiByteToWideChar( nCP, MB_PRECOMPOSED | MB_USEGLYPHCHARS,
+ (const char*)aBuf, nLen, &nChar, 1 ) )
+ pTempPair->wFirst = nChar;
+ else
+ bAdd = FALSE;
+ }
+ if ( (pTempPair->wSecond > 125) || (pTempPair->wSecond == 92) )
+ {
+ if ( pTempPair->wSecond < 256 )
+ {
+ aBuf[0] = (unsigned char)pTempPair->wSecond;
+ nLen = 1;
+ }
+ else
+ {
+ aBuf[0] = (unsigned char)(pTempPair->wSecond >> 8);
+ aBuf[1] = (unsigned char)(pTempPair->wSecond & 0xFF);
+ nLen = 2;
+ }
+ if ( MultiByteToWideChar( nCP, MB_PRECOMPOSED | MB_USEGLYPHCHARS,
+ (const char*)aBuf, nLen, &nChar, 1 ) )
+ pTempPair->wSecond = nChar;
+ else
+ bAdd = FALSE;
+ }
+
+ // TODO: get rid of linear search!
+ KERNINGPAIR* pTempPair2 = pData->mpFontKernPairs;
+ for ( ULONG j = 0; j < nOldPairs; j++ )
+ {
+ if ( (pTempPair2->wFirst == pTempPair->wFirst) &&
+ (pTempPair2->wSecond == pTempPair->wSecond) )
+ {
+ bAdd = FALSE;
+ break;
+ }
+ pTempPair2++;
+ }
+
+ if ( bAdd )
+ {
+ KERNINGPAIR* pDestPair = pData->mpFontKernPairs+pData->mnFontKernPairCount;
+ if ( pDestPair != pTempPair )
+ memcpy( pDestPair, pTempPair, sizeof( KERNINGPAIR ) );
+ pData->mnFontKernPairCount++;
+ }
+
+ pTempPair++;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+ULONG WinSalGraphics::GetKernPairs( ULONG nPairs, ImplKernPairData* pKernPairs )
+{
+ DBG_ASSERT( sizeof( KERNINGPAIR ) == sizeof( ImplKernPairData ),
+ "WinSalGraphics::GetKernPairs(): KERNINGPAIR != ImplKernPairData" );
+
+ if ( mbFontKernInit )
+ {
+ if( mpFontKernPairs )
+ {
+ delete[] mpFontKernPairs;
+ mpFontKernPairs = NULL;
+ }
+ mnFontKernPairCount = 0;
+
+ if ( aSalShlData.mbWNT )
+ {
+ KERNINGPAIR* pPairs = NULL;
+ int nCount = ::GetKerningPairsW( mhDC, 0, NULL );
+ if( nCount )
+ {
+#ifdef GCP_KERN_HACK
+ pPairs = new KERNINGPAIR[ nCount+1 ];
+ mpFontKernPairs = pPairs;
+ mnFontKernPairCount = nCount;
+ ::GetKerningPairsW( mhDC, nCount, pPairs );
+#else // GCP_KERN_HACK
+ pPairs = pKernPairs;
+ nCount = (nCount < nPairs) : nCount : nPairs;
+ ::GetKerningPairsW( mhDC, nCount, pPairs );
+ return nCount;
+#endif // GCP_KERN_HACK
+ }
+ }
+ else
+ {
+ if ( !mnFontCharSetCount )
+ ImplGetAllFontCharSets( this );
+
+ if ( mnFontCharSetCount <= 1 )
+ ImplAddKerningPairs( this );
+ else
+ {
+ // Query All Kerning Pairs from all possible CharSets
+ for ( BYTE i = 0; i < mnFontCharSetCount; i++ )
+ {
+ mpLogFont->lfCharSet = mpFontCharSets[i];
+ HFONT hNewFont = CreateFontIndirectA( mpLogFont );
+ HFONT hOldFont = SelectFont( mhDC, hNewFont );
+ ImplAddKerningPairs( this );
+ SelectFont( mhDC, hOldFont );
+ DeleteFont( hNewFont );
+ }
+ }
+ }
+
+ mbFontKernInit = FALSE;
+
+ std::sort( mpFontKernPairs, mpFontKernPairs + mnFontKernPairCount, ImplCmpKernData );
+ }
+
+ if( !pKernPairs )
+ return mnFontKernPairCount;
+ else if( mpFontKernPairs )
+ {
+ if ( nPairs < mnFontKernPairCount )
+ nPairs = mnFontKernPairCount;
+ memcpy( pKernPairs, mpFontKernPairs,
+ nPairs*sizeof( ImplKernPairData ) );
+ return nPairs;
+ }
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+const ImplFontCharMap* WinSalGraphics::GetImplFontCharMap() const
+{
+ if( !mpWinFontData[0] )
+ return ImplFontCharMap::GetDefaultMap();
+ return mpWinFontData[0]->GetImplFontCharMap();
+}
+
+// -----------------------------------------------------------------------
+
+int CALLBACK SalEnumFontsProcExA( const ENUMLOGFONTEXA* pLogFont,
+ const NEWTEXTMETRICEXA* pMetric,
+ DWORD nFontType, LPARAM lParam )
+{
+ ImplEnumInfo* pInfo = (ImplEnumInfo*)(void*)lParam;
+ if ( !pInfo->mpName )
+ {
+ // Ignore vertical fonts
+ if ( pLogFont->elfLogFont.lfFaceName[0] != '@' )
+ {
+ if ( !pInfo->mbImplSalCourierNew )
+ pInfo->mbImplSalCourierNew = stricmp( pLogFont->elfLogFont.lfFaceName, "Courier New" ) == 0;
+ if ( !pInfo->mbImplSalCourierScalable )
+ pInfo->mbCourier = stricmp( pLogFont->elfLogFont.lfFaceName, "Courier" ) == 0;
+ else
+ pInfo->mbCourier = FALSE;
+ String aName( ImplSalGetUniString( pLogFont->elfLogFont.lfFaceName ) );
+ pInfo->mpName = &aName;
+ strncpy( pInfo->mpLogFontA->lfFaceName, pLogFont->elfLogFont.lfFaceName, LF_FACESIZE );
+ pInfo->mpLogFontA->lfCharSet = pLogFont->elfLogFont.lfCharSet;
+ EnumFontFamiliesExA( pInfo->mhDC, pInfo->mpLogFontA, (FONTENUMPROCA)SalEnumFontsProcExA,
+ (LPARAM)(void*)pInfo, 0 );
+ pInfo->mpLogFontA->lfFaceName[0] = '\0';
+ pInfo->mpLogFontA->lfCharSet = DEFAULT_CHARSET;
+ pInfo->mpName = NULL;
+ pInfo->mbCourier = FALSE;
+ }
+ }
+ else
+ {
+ // ignore non-scalable non-device font on printer
+ if( pInfo->mbPrinter )
+ if( (nFontType & RASTER_FONTTYPE) && !(nFontType & DEVICE_FONTTYPE) )
+ return 1;
+
+ ImplWinFontData* pData = ImplLogMetricToDevFontDataA( pLogFont, &(pMetric->ntmTm), nFontType );
+ pData->SetFontId( sal_IntPtr( pInfo->mnFontCount++ ) );
+
+ // prefer the system character set, so that we get as much as
+ // possible important characters. In the other case we could only
+ // display a limited set of characters (#87309#)
+ if ( pInfo->mnPreferedCharSet == pLogFont->elfLogFont.lfCharSet )
+ pData->mnQuality += 100;
+
+ // knowing Courier to be scalable is nice
+ if( pInfo->mbCourier )
+ pInfo->mbImplSalCourierScalable |= pData->IsScalable();
+
+ pInfo->mpList->Add( pData );
+ }
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------
+
+int CALLBACK SalEnumFontsProcExW( const ENUMLOGFONTEXW* pLogFont,
+ const NEWTEXTMETRICEXW* pMetric,
+ DWORD nFontType, LPARAM lParam )
+{
+ ImplEnumInfo* pInfo = (ImplEnumInfo*)(void*)lParam;
+ if ( !pInfo->mpName )
+ {
+ // Ignore vertical fonts
+ if ( pLogFont->elfLogFont.lfFaceName[0] != '@' )
+ {
+ if ( !pInfo->mbImplSalCourierNew )
+ pInfo->mbImplSalCourierNew = ImplSalWICompareAscii( pLogFont->elfLogFont.lfFaceName, "Courier New" ) == 0;
+ if ( !pInfo->mbImplSalCourierScalable )
+ pInfo->mbCourier = ImplSalWICompareAscii( pLogFont->elfLogFont.lfFaceName, "Courier" ) == 0;
+ else
+ pInfo->mbCourier = FALSE;
+ String aName( reinterpret_cast<const sal_Unicode*>(pLogFont->elfLogFont.lfFaceName) );
+ pInfo->mpName = &aName;
+ memcpy( pInfo->mpLogFontW->lfFaceName, pLogFont->elfLogFont.lfFaceName, (aName.Len()+1)*sizeof( wchar_t ) );
+ pInfo->mpLogFontW->lfCharSet = pLogFont->elfLogFont.lfCharSet;
+ EnumFontFamiliesExW( pInfo->mhDC, pInfo->mpLogFontW, (FONTENUMPROCW)SalEnumFontsProcExW,
+ (LPARAM)(void*)pInfo, 0 );
+ pInfo->mpLogFontW->lfFaceName[0] = '\0';
+ pInfo->mpLogFontW->lfCharSet = DEFAULT_CHARSET;
+ pInfo->mpName = NULL;
+ pInfo->mbCourier = FALSE;
+ }
+ }
+ else
+ {
+ // ignore non-scalable non-device font on printer
+ if( pInfo->mbPrinter )
+ if( (nFontType & RASTER_FONTTYPE) && !(nFontType & DEVICE_FONTTYPE) )
+ return 1;
+
+ ImplWinFontData* pData = ImplLogMetricToDevFontDataW( pLogFont, &(pMetric->ntmTm), nFontType );
+ pData->SetFontId( sal_IntPtr( pInfo->mnFontCount++ ) );
+
+ // knowing Courier to be scalable is nice
+ if( pInfo->mbCourier )
+ pInfo->mbImplSalCourierScalable |= pData->IsScalable();
+
+ pInfo->mpList->Add( pData );
+ }
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------
+
+struct TempFontItem
+{
+ ::rtl::OUString maFontFilePath;
+ ::rtl::OString maResourcePath;
+ TempFontItem* mpNextItem;
+};
+
+#ifdef FR_PRIVATE
+static int WINAPI __AddFontResourceExW( LPCWSTR lpszfileName, DWORD fl, PVOID pdv )
+{
+ typedef int (WINAPI *AddFontResourceExW_FUNC)(LPCWSTR, DWORD, PVOID );
+
+ static AddFontResourceExW_FUNC pFunc = NULL;
+ static HMODULE hmGDI = NULL;
+
+ if ( !pFunc && !hmGDI )
+ {
+ hmGDI = GetModuleHandleA( "GDI32" );
+ if ( hmGDI )
+ pFunc = reinterpret_cast<AddFontResourceExW_FUNC>( GetProcAddress( hmGDI, "AddFontResourceExW" ) );
+ }
+
+ if ( pFunc )
+ return pFunc( lpszfileName, fl, pdv );
+ else
+ {
+ SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
+ return 0;
+ }
+}
+#endif
+
+bool ImplAddTempFont( SalData& rSalData, const String& rFontFileURL )
+{
+ int nRet = 0;
+ ::rtl::OUString aUSytemPath;
+ OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSytemPath ) );
+
+#ifdef FR_PRIVATE
+ nRet = __AddFontResourceExW( reinterpret_cast<LPCWSTR>(aUSytemPath.getStr()), FR_PRIVATE, NULL );
+#endif
+
+ if ( !nRet )
+ {
+ static int nCounter = 0;
+ char aFileName[] = "soAA.fot";
+ aFileName[2] = sal::static_int_cast<char>('A' + (15 & (nCounter>>4)));
+ aFileName[3] = sal::static_int_cast<char>('A' + (15 & nCounter));
+ char aResourceName[512];
+ int nMaxLen = sizeof(aResourceName)/sizeof(*aResourceName) - 16;
+ int nLen = ::GetTempPathA( nMaxLen, aResourceName );
+ ::strncpy( aResourceName + nLen, aFileName, sizeof( aResourceName )- nLen );
+ // security: end buffer in any case
+ aResourceName[ (sizeof(aResourceName)/sizeof(*aResourceName))-1 ] = 0;
+ ::DeleteFileA( aResourceName );
+
+ rtl_TextEncoding theEncoding = osl_getThreadTextEncoding();
+ ::rtl::OString aCFileName = rtl::OUStringToOString( aUSytemPath, theEncoding );
+ // TODO: font should be private => need to investigate why it doesn't work then
+ if( !::CreateScalableFontResourceA( 0, aResourceName, aCFileName.getStr(), NULL ) )
+ return false;
+ ++nCounter;
+
+ nRet = ::AddFontResourceA( aResourceName );
+ if( nRet > 0 )
+ {
+ TempFontItem* pNewItem = new TempFontItem;
+ pNewItem->maResourcePath = rtl::OString( aResourceName );
+ pNewItem->maFontFilePath = aUSytemPath.getStr();
+ pNewItem->mpNextItem = rSalData.mpTempFontItem;
+ rSalData.mpTempFontItem = pNewItem;
+ }
+ }
+
+ return (nRet > 0);
+}
+
+// -----------------------------------------------------------------------
+
+void ImplReleaseTempFonts( SalData& rSalData )
+{
+ int nCount = 0;
+ while( TempFontItem* p = rSalData.mpTempFontItem )
+ {
+ ++nCount;
+ if( p->maResourcePath.getLength() )
+ {
+ const char* pResourcePath = p->maResourcePath.getStr();
+ ::RemoveFontResourceA( pResourcePath );
+ ::DeleteFileA( pResourcePath );
+ }
+ else
+ {
+ if( aSalShlData.mbWNT )
+ ::RemoveFontResourceW( reinterpret_cast<LPCWSTR>(p->maFontFilePath.getStr()) );
+ else
+ {
+ // poor man's string conversion because converter is gone
+ int nLen = p->maFontFilePath.getLength();
+ char* pNameA = new char[ nLen + 1 ];
+ for( int i = 0; i < nLen; ++i )
+ pNameA[i] = (char)(p->maFontFilePath.getStr())[i];
+ pNameA[ nLen ] = 0;
+ ::RemoveFontResourceA( pNameA );
+ delete[] pNameA;
+ }
+ }
+
+ rSalData.mpTempFontItem = p->mpNextItem;
+ delete p;
+ }
+
+#ifndef FR_PRIVATE
+ // notify every other application
+ // unless the temp fonts were installed as private fonts
+ if( nCount > 0 )
+ ::PostMessage( HWND_BROADCAST, WM_FONTCHANGE, 0, NULL );
+#endif // FR_PRIVATE
+}
+
+// -----------------------------------------------------------------------
+
+static bool ImplGetFontAttrFromFile( const String& rFontFileURL,
+ ImplDevFontAttributes& rDFA )
+{
+ ::rtl::OUString aUSytemPath;
+ OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSytemPath ) );
+
+ // get FontAttributes from a *fot file
+ // TODO: use GetTTGlobalFontInfo() to access the font directly
+ rDFA.mnQuality = 1000;
+ rDFA.mbDevice = true;
+ rDFA.meFamily = FAMILY_DONTKNOW;
+ rDFA.meWidthType = WIDTH_DONTKNOW;
+ rDFA.meWeight = WEIGHT_DONTKNOW;
+ rDFA.meItalic = ITALIC_DONTKNOW;
+ rDFA.mePitch = PITCH_DONTKNOW;;
+ rDFA.mbSubsettable= true;
+ rDFA.mbEmbeddable = false;
+
+ // Create temporary file name
+ char aFileName[] = "soAAT.fot";
+ char aResourceName[512];
+ int nMaxLen = sizeof(aResourceName)/sizeof(*aResourceName) - 16;
+ int nLen = ::GetTempPathA( nMaxLen, aResourceName );
+ ::strncpy( aResourceName + nLen, aFileName, Max( 0, nMaxLen - nLen ));
+ ::DeleteFileA( aResourceName );
+
+ // Create font resource file (typically with a .fot file name extension).
+ rtl_TextEncoding theEncoding = osl_getThreadTextEncoding();
+ ::rtl::OString aCFileName = rtl::OUStringToOString( aUSytemPath, theEncoding );
+ ::CreateScalableFontResourceA( 0, aResourceName, aCFileName.getStr(), NULL );
+
+ // Open and read the font resource file
+ rtl::OUString aFotFileName = rtl::OStringToOUString( aResourceName, osl_getThreadTextEncoding() );
+ osl::FileBase::getFileURLFromSystemPath( aFotFileName, aFotFileName );
+ osl::File aFotFile( aFotFileName );
+ osl::FileBase::RC aError = aFotFile.open( osl_File_OpenFlag_Read );
+ if( aError != osl::FileBase::E_None )
+ return false;
+
+ sal_uInt64 nBytesRead = 0;
+ char aBuffer[4096];
+ aFotFile.read( aBuffer, sizeof( aBuffer ), nBytesRead );
+ // clean up temporary resource file
+ aFotFile.close();
+ ::DeleteFileA( aResourceName );
+
+ // retrieve font family name from byte offset 0x4F6
+ int i = 0x4F6;
+ int nNameOfs = i;
+ while( (i < nBytesRead) && (aBuffer[i++] != 0) );
+ // skip full name
+ while( (i < nBytesRead) && (aBuffer[i++] != 0) );
+ // retrieve font style name
+ int nStyleOfs = i;
+ while( (i < nBytesRead) && (aBuffer[i++] != 0) );
+ if( i >= nBytesRead )
+ return false;
+
+ // convert byte strings to unicode
+ rDFA.maName = String( aBuffer + nNameOfs, osl_getThreadTextEncoding() );
+ rDFA.maStyleName = String( aBuffer + nStyleOfs, osl_getThreadTextEncoding() );
+
+ // byte offset 0x4C7: OS2_fsSelection
+ const char nFSS = aBuffer[ 0x4C7 ];
+ if( nFSS & 0x01 ) // italic
+ rDFA.meItalic = ITALIC_NORMAL;
+ //if( nFSS & 0x20 ) // bold
+ // rDFA.meWeight = WEIGHT_BOLD;
+ if( nFSS & 0x40 ) // regular
+ {
+ rDFA.meWeight = WEIGHT_NORMAL;
+ rDFA.meItalic = ITALIC_NONE;
+ }
+
+ // byte offsets 0x4D7/0x4D8: wingdi's FW_WEIGHT
+ int nWinWeight = (aBuffer[0x4D7] & 0xFF) + ((aBuffer[0x4D8] & 0xFF) << 8);
+ rDFA.meWeight = ImplWeightToSal( nWinWeight );
+
+ rDFA.mbSymbolFlag = false; // TODO
+ rDFA.mePitch = PITCH_DONTKNOW; // TODO
+
+ // byte offset 0x4DE: pitch&family
+ rDFA.meFamily = ImplFamilyToSal( aBuffer[0x4DE] );
+
+ // byte offsets 0x4C8/0x4C9: emunits
+ // byte offsets 0x4CE/0x4CF: winascent
+ // byte offsets 0x4D0/0x4D1: winascent+windescent-emunits
+ // byte offsets 0x4DF/0x4E0: avgwidth
+ //...
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+bool WinSalGraphics::AddTempDevFont( ImplDevFontList* pFontList,
+ const String& rFontFileURL, const String& rFontName )
+{
+ RTL_LOGFILE_TRACE1( "WinSalGraphics::AddTempDevFont(): %s", rtl::OUStringToOString( rFontFileURL, RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ ImplDevFontAttributes aDFA;
+ aDFA.maName = rFontName;
+ aDFA.mnQuality = 1000;
+ aDFA.mbDevice = true;
+
+ // Search Font Name in Cache
+ if( !rFontName.Len() && mpFontAttrCache )
+ aDFA = mpFontAttrCache->GetFontAttr( rFontFileURL );
+
+ // Retrieve font name from font resource
+ if( !aDFA.maName.Len() )
+ {
+ ImplGetFontAttrFromFile( rFontFileURL, aDFA );
+ if( mpFontAttrCache && aDFA.maName.Len() )
+ mpFontAttrCache->AddFontAttr( rFontFileURL, aDFA );
+ }
+
+ if ( !aDFA.maName.Len() )
+ return false;
+
+ // remember temp font for cleanup later
+ if( !ImplAddTempFont( *GetSalData(), rFontFileURL ) )
+ return false;
+
+ UINT nPreferedCharSet = DEFAULT_CHARSET;
+ if ( !aSalShlData.mbWNT )
+ {
+ // for W98 guess charset preference from active codepage
+ CHARSETINFO aCharSetInfo;
+ DWORD nCP = GetACP();
+ if ( TranslateCharsetInfo( (DWORD*)nCP, &aCharSetInfo, TCI_SRCCODEPAGE ) )
+ nPreferedCharSet = aCharSetInfo.ciCharset;
+ }
+
+ // create matching FontData struct
+ aDFA.mbSymbolFlag = false; // TODO: how to know it without accessing the font?
+ aDFA.meFamily = FAMILY_DONTKNOW;
+ aDFA.meWidthType = WIDTH_DONTKNOW;
+ aDFA.meWeight = WEIGHT_DONTKNOW;
+ aDFA.meItalic = ITALIC_DONTKNOW;
+ aDFA.mePitch = PITCH_DONTKNOW;;
+ aDFA.mbSubsettable= true;
+ aDFA.mbEmbeddable = false;
+
+ /*
+ // TODO: improve ImplDevFontAttributes using the "font resource file"
+ aDFS.maName = // using "FONTRES:" from file
+ if( rFontName != aDFS.maName )
+ aDFS.maMapName = aFontName;
+ */
+
+ ImplWinFontData* pFontData = new ImplWinFontData( aDFA, 0,
+ sal::static_int_cast<WIN_BYTE>(nPreferedCharSet),
+ sal::static_int_cast<WIN_BYTE>(TMPF_VECTOR|TMPF_TRUETYPE) );
+ pFontData->SetFontId( reinterpret_cast<sal_IntPtr>(pFontData) );
+ pFontList->Add( pFontData );
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+void WinSalGraphics::GetDevFontList( ImplDevFontList* pFontList )
+{
+ // make sure all fonts are registered at least temporarily
+ static bool bOnce = true;
+ if( bOnce )
+ {
+ bOnce = false;
+
+ // determine font path
+ // since we are only interested in fonts that could not be
+ // registered before because of missing administration rights
+ // only the font path of the user installation is needed
+ ::rtl::OUString aPath;
+ osl_getExecutableFile( &aPath.pData );
+ ::rtl::OUString aExecutableFile( aPath );
+ aPath = aPath.copy( 0, aPath.lastIndexOf('/') );
+ String aFontDirUrl = aPath.copy( 0, aPath.lastIndexOf('/') );
+ aFontDirUrl += String( RTL_CONSTASCII_USTRINGPARAM("/Basis/share/fonts/truetype") );
+
+ // collect fonts in font path that could not be registered
+ osl::Directory aFontDir( aFontDirUrl );
+ osl::FileBase::RC rcOSL = aFontDir.open();
+ if( rcOSL == osl::FileBase::E_None )
+ {
+ osl::DirectoryItem aDirItem;
+ String aEmptyString;
+
+ ::rtl::OUString aBootStrap;
+ rtl::Bootstrap::get( String( RTL_CONSTASCII_USTRINGPARAM( "BRAND_BASE_DIR" ) ), aBootStrap );
+ aBootStrap += String( RTL_CONSTASCII_USTRINGPARAM( "/program/" SAL_CONFIGFILE( "bootstrap" ) ) );
+ rtl::Bootstrap aBootstrap( aBootStrap );
+ ::rtl::OUString aUserPath;
+ aBootstrap.getFrom( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "UserInstallation" ) ), aUserPath );
+ aUserPath += String( RTL_CONSTASCII_USTRINGPARAM("/user/config/fontnames.dat") );
+ String aBaseURL = aPath.copy( 0, aPath.lastIndexOf('/')+1 );
+ mpFontAttrCache = new ImplFontAttrCache( aUserPath, aBaseURL );
+
+ while( aFontDir.getNextItem( aDirItem, 10 ) == osl::FileBase::E_None )
+ {
+ osl::FileStatus aFileStatus( FileStatusMask_FileURL );
+ rcOSL = aDirItem.getFileStatus( aFileStatus );
+ if ( rcOSL == osl::FileBase::E_None )
+ AddTempDevFont( pFontList, aFileStatus.getFileURL(), aEmptyString );
+ }
+
+ delete mpFontAttrCache; // destructor rewrites the cache file if needed
+ mpFontAttrCache = NULL;
+ }
+ }
+
+ ImplEnumInfo aInfo;
+ aInfo.mhDC = mhDC;
+ aInfo.mpList = pFontList;
+ aInfo.mpName = NULL;
+ aInfo.mpLogFontA = NULL;
+ aInfo.mpLogFontW = NULL;
+ aInfo.mbCourier = false;
+ aInfo.mbPrinter = mbPrinter;
+ aInfo.mnFontCount = 0;
+ if ( !mbPrinter )
+ {
+ aInfo.mbImplSalCourierScalable = false;
+ aInfo.mbImplSalCourierNew = false;
+ }
+ else
+ {
+ aInfo.mbImplSalCourierScalable = true;
+ aInfo.mbImplSalCourierNew = true;
+ }
+
+ aInfo.mnPreferedCharSet = DEFAULT_CHARSET;
+ DWORD nCP = GetACP();
+ CHARSETINFO aCharSetInfo;
+ if ( TranslateCharsetInfo( (DWORD*)nCP, &aCharSetInfo, TCI_SRCCODEPAGE ) )
+ aInfo.mnPreferedCharSet = aCharSetInfo.ciCharset;
+
+ if ( aSalShlData.mbWNT )
+ {
+ LOGFONTW aLogFont;
+ memset( &aLogFont, 0, sizeof( aLogFont ) );
+ aLogFont.lfCharSet = DEFAULT_CHARSET;
+ aInfo.mpLogFontW = &aLogFont;
+ EnumFontFamiliesExW( mhDC, &aLogFont,
+ (FONTENUMPROCW)SalEnumFontsProcExW, (LPARAM)(void*)&aInfo, 0 );
+ }
+ else
+ {
+ LOGFONTA aLogFont;
+ memset( &aLogFont, 0, sizeof( aLogFont ) );
+ aLogFont.lfCharSet = DEFAULT_CHARSET;
+ aInfo.mpLogFontA = &aLogFont;
+ EnumFontFamiliesExA( mhDC, &aLogFont,
+ (FONTENUMPROCA)SalEnumFontsProcExA, (LPARAM)(void*)&aInfo, 0 );
+ }
+
+ // Feststellen, was es fuer Courier-Schriften auf dem Bildschirm gibt,
+ // um in SetFont() evt. Courier auf Courier New zu mappen
+ if ( !mbPrinter )
+ {
+ bImplSalCourierScalable = aInfo.mbImplSalCourierScalable;
+ bImplSalCourierNew = aInfo.mbImplSalCourierNew;
+ }
+
+ // set glyph fallback hook
+ static WinGlyphFallbackSubstititution aSubstFallback( mhDC );
+ pFontList->SetFallbackHook( &aSubstFallback );
+}
+
+// ----------------------------------------------------------------------------
+
+void WinSalGraphics::GetDevFontSubstList( OutputDevice* )
+{}
+
+// -----------------------------------------------------------------------
+
+BOOL WinSalGraphics::GetGlyphBoundRect( long nIndex, Rectangle& rRect )
+{
+ HDC hDC = mhDC;
+
+ // use unity matrix
+ MAT2 aMat;
+ aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
+ aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
+
+ UINT nGGOFlags = GGO_METRICS;
+ if( !(nIndex & GF_ISCHAR) )
+ nGGOFlags |= GGO_GLYPH_INDEX;
+ nIndex &= GF_IDXMASK;
+
+ GLYPHMETRICS aGM;
+ aGM.gmptGlyphOrigin.x = aGM.gmptGlyphOrigin.y = 0;
+ aGM.gmBlackBoxX = aGM.gmBlackBoxY = 0;
+ DWORD nSize = GDI_ERROR;
+ if ( aSalShlData.mbWNT )
+ nSize = ::GetGlyphOutlineW( hDC, nIndex, nGGOFlags, &aGM, 0, NULL, &aMat );
+ else if( (nGGOFlags & GGO_GLYPH_INDEX) || (nIndex <= 255) )
+ nSize = ::GetGlyphOutlineA( hDC, nIndex, nGGOFlags, &aGM, 0, NULL, &aMat );
+
+ if( nSize == GDI_ERROR )
+ return false;
+
+ rRect = Rectangle( Point( +aGM.gmptGlyphOrigin.x, -aGM.gmptGlyphOrigin.y ),
+ Size( aGM.gmBlackBoxX, aGM.gmBlackBoxY ) );
+ rRect.Left() = static_cast<int>( mfFontScale * rRect.Left() );
+ rRect.Right() = static_cast<int>( mfFontScale * rRect.Right() );
+ rRect.Top() = static_cast<int>( mfFontScale * rRect.Top() );
+ rRect.Bottom() = static_cast<int>( mfFontScale * rRect.Bottom() );
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+BOOL WinSalGraphics::GetGlyphOutline( long nIndex,
+ ::basegfx::B2DPolyPolygon& rB2DPolyPoly )
+{
+ rB2DPolyPoly.clear();
+
+ BOOL bRet = FALSE;
+ HDC hDC = mhDC;
+
+ // use unity matrix
+ MAT2 aMat;
+ aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
+ aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
+
+ UINT nGGOFlags = GGO_NATIVE;
+ if( !(nIndex & GF_ISCHAR) )
+ nGGOFlags |= GGO_GLYPH_INDEX;
+ nIndex &= GF_IDXMASK;
+
+ GLYPHMETRICS aGlyphMetrics;
+ DWORD nSize1 = GDI_ERROR;
+ if ( aSalShlData.mbWNT )
+ nSize1 = ::GetGlyphOutlineW( hDC, nIndex, nGGOFlags, &aGlyphMetrics, 0, NULL, &aMat );
+ else if( (nGGOFlags & GGO_GLYPH_INDEX) || (nIndex <= 255) )
+ nSize1 = ::GetGlyphOutlineA( hDC, nIndex, nGGOFlags, &aGlyphMetrics, 0, NULL, &aMat );
+
+ if( !nSize1 ) // blank glyphs are ok
+ bRet = TRUE;
+ else if( nSize1 != GDI_ERROR )
+ {
+ BYTE* pData = new BYTE[ nSize1 ];
+ DWORD nSize2;
+ if ( aSalShlData.mbWNT )
+ nSize2 = ::GetGlyphOutlineW( hDC, nIndex, nGGOFlags,
+ &aGlyphMetrics, nSize1, pData, &aMat );
+ else
+ nSize2 = ::GetGlyphOutlineA( hDC, nIndex, nGGOFlags,
+ &aGlyphMetrics, nSize1, pData, &aMat );
+
+ if( nSize1 == nSize2 )
+ {
+ bRet = TRUE;
+
+ int nPtSize = 512;
+ Point* pPoints = new Point[ nPtSize ];
+ BYTE* pFlags = new BYTE[ nPtSize ];
+
+ TTPOLYGONHEADER* pHeader = (TTPOLYGONHEADER*)pData;
+ while( (BYTE*)pHeader < pData+nSize2 )
+ {
+ // only outline data is interesting
+ if( pHeader->dwType != TT_POLYGON_TYPE )
+ break;
+
+ // get start point; next start points are end points
+ // of previous segment
+ USHORT nPnt = 0;
+
+ long nX = IntTimes256FromFixed( pHeader->pfxStart.x );
+ long nY = IntTimes256FromFixed( pHeader->pfxStart.y );
+ pPoints[ nPnt ] = Point( nX, nY );
+ pFlags[ nPnt++ ] = POLY_NORMAL;
+
+ bool bHasOfflinePoints = false;
+ TTPOLYCURVE* pCurve = (TTPOLYCURVE*)( pHeader + 1 );
+ pHeader = (TTPOLYGONHEADER*)( (BYTE*)pHeader + pHeader->cb );
+ while( (BYTE*)pCurve < (BYTE*)pHeader )
+ {
+ int nNeededSize = nPnt + 16 + 3 * pCurve->cpfx;
+ if( nPtSize < nNeededSize )
+ {
+ Point* pOldPoints = pPoints;
+ BYTE* pOldFlags = pFlags;
+ nPtSize = 2 * nNeededSize;
+ pPoints = new Point[ nPtSize ];
+ pFlags = new BYTE[ nPtSize ];
+ for( USHORT i = 0; i < nPnt; ++i )
+ {
+ pPoints[ i ] = pOldPoints[ i ];
+ pFlags[ i ] = pOldFlags[ i ];
+ }
+ delete[] pOldPoints;
+ delete[] pOldFlags;
+ }
+
+ int i = 0;
+ if( TT_PRIM_LINE == pCurve->wType )
+ {
+ while( i < pCurve->cpfx )
+ {
+ nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
+ nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
+ ++i;
+ pPoints[ nPnt ] = Point( nX, nY );
+ pFlags[ nPnt ] = POLY_NORMAL;
+ ++nPnt;
+ }
+ }
+ else if( TT_PRIM_QSPLINE == pCurve->wType )
+ {
+ bHasOfflinePoints = true;
+ while( i < pCurve->cpfx )
+ {
+ // get control point of quadratic bezier spline
+ nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
+ nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
+ ++i;
+ Point aControlP( nX, nY );
+
+ // calculate first cubic control point
+ // P0 = 1/3 * (PBeg + 2 * PQControl)
+ nX = pPoints[ nPnt-1 ].X() + 2 * aControlP.X();
+ nY = pPoints[ nPnt-1 ].Y() + 2 * aControlP.Y();
+ pPoints[ nPnt+0 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
+ pFlags[ nPnt+0 ] = POLY_CONTROL;
+
+ // calculate endpoint of segment
+ nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
+ nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
+
+ if ( i+1 >= pCurve->cpfx )
+ {
+ // endpoint is either last point in segment => advance
+ ++i;
+ }
+ else
+ {
+ // or endpoint is the middle of two control points
+ nX += IntTimes256FromFixed( pCurve->apfx[ i-1 ].x );
+ nY += IntTimes256FromFixed( pCurve->apfx[ i-1 ].y );
+ nX = (nX + 1) / 2;
+ nY = (nY + 1) / 2;
+ // no need to advance, because the current point
+ // is the control point in next bezier spline
+ }
+
+ pPoints[ nPnt+2 ] = Point( nX, nY );
+ pFlags[ nPnt+2 ] = POLY_NORMAL;
+
+ // calculate second cubic control point
+ // P1 = 1/3 * (PEnd + 2 * PQControl)
+ nX = pPoints[ nPnt+2 ].X() + 2 * aControlP.X();
+ nY = pPoints[ nPnt+2 ].Y() + 2 * aControlP.Y();
+ pPoints[ nPnt+1 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
+ pFlags[ nPnt+1 ] = POLY_CONTROL;
+
+ nPnt += 3;
+ }
+ }
+
+ // next curve segment
+ pCurve = (TTPOLYCURVE*)&pCurve->apfx[ i ];
+ }
+
+ // end point is start point for closed contour
+ // disabled, because Polygon class closes the contour itself
+ // pPoints[nPnt++] = pPoints[0];
+ // #i35928#
+ // Added again, but add only when not yet closed
+ if(pPoints[nPnt - 1] != pPoints[0])
+ {
+ if( bHasOfflinePoints )
+ pFlags[nPnt] = pFlags[0];
+
+ pPoints[nPnt++] = pPoints[0];
+ }
+
+ // convert y-coordinates W32 -> VCL
+ for( int i = 0; i < nPnt; ++i )
+ pPoints[i].Y() = -pPoints[i].Y();
+
+ // insert into polypolygon
+ Polygon aPoly( nPnt, pPoints, (bHasOfflinePoints ? pFlags : NULL) );
+ // convert to B2DPolyPolygon
+ // TODO: get rid of the intermediate PolyPolygon
+ rB2DPolyPoly.append( aPoly.getB2DPolygon() );
+ }
+
+ delete[] pPoints;
+ delete[] pFlags;
+ }
+
+ delete[] pData;
+ }
+
+ // rescaling needed for the PolyPolygon conversion
+ if( rB2DPolyPoly.count() )
+ {
+ const double fFactor(mfFontScale/256);
+ rB2DPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix(fFactor, fFactor));
+ }
+
+ return bRet;
+}
+
+// -----------------------------------------------------------------------
+
+class ScopedFont
+{
+public:
+ explicit ScopedFont(WinSalGraphics & rData);
+
+ ~ScopedFont();
+
+private:
+ WinSalGraphics & m_rData;
+ HFONT m_hOrigFont;
+};
+
+ScopedFont::ScopedFont(WinSalGraphics & rData): m_rData(rData)
+{
+ m_hOrigFont = m_rData.mhFonts[0];
+ m_rData.mhFonts[0] = 0; // avoid deletion of current font
+}
+
+ScopedFont::~ScopedFont()
+{
+ if( m_hOrigFont )
+ {
+ // restore original font, destroy temporary font
+ HFONT hTempFont = m_rData.mhFonts[0];
+ m_rData.mhFonts[0] = m_hOrigFont;
+ SelectObject( m_rData.mhDC, m_hOrigFont );
+ DeleteObject( hTempFont );
+ }
+}
+
+class ScopedTrueTypeFont
+{
+public:
+ inline ScopedTrueTypeFont(): m_pFont(0) {}
+
+ ~ScopedTrueTypeFont();
+
+ int open(void * pBuffer, sal_uInt32 nLen, sal_uInt32 nFaceNum);
+
+ inline TrueTypeFont * get() const { return m_pFont; }
+
+private:
+ TrueTypeFont * m_pFont;
+};
+
+ScopedTrueTypeFont::~ScopedTrueTypeFont()
+{
+ if (m_pFont != 0)
+ CloseTTFont(m_pFont);
+}
+
+int ScopedTrueTypeFont::open(void * pBuffer, sal_uInt32 nLen,
+ sal_uInt32 nFaceNum)
+{
+ OSL_ENSURE(m_pFont == 0, "already open");
+ return OpenTTFontBuffer(pBuffer, nLen, nFaceNum, &m_pFont);
+}
+
+BOOL WinSalGraphics::CreateFontSubset( const rtl::OUString& rToFile,
+ const ImplFontData* pFont, long* pGlyphIDs, sal_uInt8* pEncoding,
+ sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo )
+{
+ // TODO: use more of the central font-subsetting code, move stuff there if needed
+
+ // create matching ImplFontSelectData
+ // we need just enough to get to the font file data
+ // use height=1000 for easier debugging (to match psprint's font units)
+ ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
+
+ // TODO: much better solution: move SetFont and restoration of old font to caller
+ ScopedFont aOldFont(*this);
+ float fScale = 1.0;
+ HFONT hOldFont = 0;
+ ImplDoSetFont( &aIFSD, fScale, hOldFont );
+
+ ImplWinFontData* pWinFontData = (ImplWinFontData*)aIFSD.mpFontData;
+
+#if OSL_DEBUG_LEVEL > 1
+ // get font metrics
+ TEXTMETRICA aWinMetric;
+ if( !::GetTextMetricsA( mhDC, &aWinMetric ) )
+ return FALSE;
+
+ DBG_ASSERT( !(aWinMetric.tmPitchAndFamily & TMPF_DEVICE), "cannot subset device font" );
+ DBG_ASSERT( aWinMetric.tmPitchAndFamily & TMPF_TRUETYPE, "can only subset TT font" );
+#endif
+
+ rtl::OUString aSysPath;
+ if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) )
+ return FALSE;
+ const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding();
+ const ByteString aToFile( aSysPath.getStr(), (xub_StrLen)aSysPath.getLength(), aThreadEncoding );
+
+ // check if the font has a CFF-table
+ const DWORD nCffTag = CalcTag( "CFF " );
+ const RawFontData aRawCffData( mhDC, nCffTag );
+ if( aRawCffData.get() )
+ {
+ pWinFontData->UpdateFromHDC( mhDC );
+ const ImplFontCharMap* pCharMap = pWinFontData->GetImplFontCharMap();
+ pCharMap->AddReference();
+
+ long nRealGlyphIds[ 256 ];
+ for( int i = 0; i < nGlyphCount; ++i )
+ {
+ // TODO: remap notdef glyph if needed
+ // TODO: use GDI's GetGlyphIndices instead? Does it handle GSUB properly?
+ sal_uInt32 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK;
+ if( pGlyphIDs[i] & GF_ISCHAR ) // remaining pseudo-glyphs need to be translated
+ nGlyphIdx = pCharMap->GetGlyphIndex( nGlyphIdx );
+ if( (pGlyphIDs[i] & (GF_ROTMASK|GF_GSUB)) != 0) // TODO: vertical substitution
+ {/*####*/}
+
+ nRealGlyphIds[i] = nGlyphIdx;
+ }
+
+ pCharMap->DeReference(); // TODO: and and use a RAII object
+
+ // provide a font subset from the CFF-table
+ FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" );
+ rInfo.LoadFont( FontSubsetInfo::CFF_FONT, aRawCffData.get(), aRawCffData.size() );
+ bool bRC = rInfo.CreateFontSubset( FontSubsetInfo::TYPE1_PFB, pOutFile, NULL,
+ nRealGlyphIds, pEncoding, nGlyphCount, pGlyphWidths );
+ fclose( pOutFile );
+ return bRC;
+ }
+
+ // get raw font file data
+ const RawFontData xRawFontData( mhDC, NULL );
+ if( !xRawFontData.get() )
+ return FALSE;
+
+ // open font file
+ sal_uInt32 nFaceNum = 0;
+ if( !*xRawFontData.get() ) // TTC candidate
+ nFaceNum = ~0U; // indicate "TTC font extracts only"
+
+ ScopedTrueTypeFont aSftTTF;
+ int nRC = aSftTTF.open( (void*)xRawFontData.get(), xRawFontData.size(), nFaceNum );
+ if( nRC != SF_OK )
+ return FALSE;
+
+ TTGlobalFontInfo aTTInfo;
+ ::GetTTGlobalFontInfo( aSftTTF.get(), &aTTInfo );
+ rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF;
+ rInfo.m_aPSName = ImplSalGetUniString( aTTInfo.psname );
+ rInfo.m_nAscent = +aTTInfo.winAscent;
+ rInfo.m_nDescent = -aTTInfo.winDescent;
+ rInfo.m_aFontBBox = Rectangle( Point( aTTInfo.xMin, aTTInfo.yMin ),
+ Point( aTTInfo.xMax, aTTInfo.yMax ) );
+ rInfo.m_nCapHeight = aTTInfo.yMax; // Well ...
+
+ // subset TTF-glyphs and get their properties
+ // take care that subset fonts require the NotDef glyph in pos 0
+ int nOrigCount = nGlyphCount;
+ USHORT aShortIDs[ 256 ];
+ sal_uInt8 aTempEncs[ 256 ];
+
+ int nNotDef=-1, i;
+ for( i = 0; i < nGlyphCount; ++i )
+ {
+ aTempEncs[i] = pEncoding[i];
+ sal_uInt32 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK;
+ if( pGlyphIDs[i] & GF_ISCHAR )
+ {
+ sal_Unicode cChar = static_cast<sal_Unicode>(nGlyphIdx); // TODO: sal_UCS4
+ const bool bVertical = ((pGlyphIDs[i] & (GF_ROTMASK|GF_GSUB)) != 0);
+ nGlyphIdx = ::MapChar( aSftTTF.get(), cChar, bVertical );
+ if( (nGlyphIdx == 0) && pFont->IsSymbolFont() )
+ {
+ // #i12824# emulate symbol aliasing U+FXXX <-> U+0XXX
+ cChar = (cChar & 0xF000) ? (cChar & 0x00FF) : (cChar | 0xF000);
+ nGlyphIdx = ::MapChar( aSftTTF.get(), cChar, bVertical );
+ }
+ }
+ aShortIDs[i] = static_cast<USHORT>( nGlyphIdx );
+ if( !nGlyphIdx )
+ if( nNotDef < 0 )
+ nNotDef = i; // first NotDef glyph found
+ }
+
+ if( nNotDef != 0 )
+ {
+ // add fake NotDef glyph if needed
+ if( nNotDef < 0 )
+ nNotDef = nGlyphCount++;
+
+ // NotDef glyph must be in pos 0 => swap glyphids
+ aShortIDs[ nNotDef ] = aShortIDs[0];
+ aTempEncs[ nNotDef ] = aTempEncs[0];
+ aShortIDs[0] = 0;
+ aTempEncs[0] = 0;
+ }
+ DBG_ASSERT( nGlyphCount < 257, "too many glyphs for subsetting" );
+
+ // fill pWidth array
+ TTSimpleGlyphMetrics* pMetrics =
+ ::GetTTSimpleGlyphMetrics( aSftTTF.get(), aShortIDs, nGlyphCount, aIFSD.mbVertical );
+ if( !pMetrics )
+ return FALSE;
+ sal_uInt16 nNotDefAdv = pMetrics[0].adv;
+ pMetrics[0].adv = pMetrics[nNotDef].adv;
+ pMetrics[nNotDef].adv = nNotDefAdv;
+ for( i = 0; i < nOrigCount; ++i )
+ pGlyphWidths[i] = pMetrics[i].adv;
+ free( pMetrics );
+
+ // write subset into destination file
+ nRC = ::CreateTTFromTTGlyphs( aSftTTF.get(), aToFile.GetBuffer(), aShortIDs,
+ aTempEncs, nGlyphCount, 0, NULL, 0 );
+ return (nRC == SF_OK);
+}
+
+//--------------------------------------------------------------------------
+
+const void* WinSalGraphics::GetEmbedFontData( const ImplFontData* pFont,
+ const sal_Unicode* pUnicodes, sal_Int32* pCharWidths,
+ FontSubsetInfo& rInfo, long* pDataLen )
+{
+ // create matching ImplFontSelectData
+ // we need just enough to get to the font file data
+ ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
+
+ // TODO: much better solution: move SetFont and restoration of old font to caller
+ ScopedFont aOldFont(*this);
+ SetFont( &aIFSD, 0 );
+
+ // get the raw font file data
+ RawFontData aRawFontData( mhDC );
+ *pDataLen = aRawFontData.size();
+ if( !aRawFontData.get() )
+ return NULL;
+
+ // get important font properties
+ TEXTMETRICA aTm;
+ if( !::GetTextMetricsA( mhDC, &aTm ) )
+ *pDataLen = 0;
+ const bool bPFA = (*aRawFontData.get() < 0x80);
+ rInfo.m_nFontType = bPFA ? FontSubsetInfo::TYPE1_PFA : FontSubsetInfo::TYPE1_PFB;
+ WCHAR aFaceName[64];
+ int nFNLen = ::GetTextFaceW( mhDC, 64, aFaceName );
+ // #i59854# strip eventual null byte
+ while( nFNLen > 0 && aFaceName[nFNLen-1] == 0 )
+ nFNLen--;
+ if( nFNLen == 0 )
+ *pDataLen = 0;
+ rInfo.m_aPSName = String( reinterpret_cast<const sal_Unicode*>(aFaceName), sal::static_int_cast<USHORT>(nFNLen) );
+ rInfo.m_nAscent = +aTm.tmAscent;
+ rInfo.m_nDescent = -aTm.tmDescent;
+ rInfo.m_aFontBBox = Rectangle( Point( -aTm.tmOverhang, -aTm.tmDescent ),
+ Point( aTm.tmMaxCharWidth, aTm.tmAscent+aTm.tmExternalLeading ) );
+ rInfo.m_nCapHeight = aTm.tmAscent; // Well ...
+
+ // get individual character widths
+ for( int i = 0; i < 256; ++i )
+ {
+ int nCharWidth = 0;
+ const sal_Unicode cChar = pUnicodes[i];
+ if( !::GetCharWidth32W( mhDC, cChar, cChar, &nCharWidth ) )
+ *pDataLen = 0;
+ pCharWidths[i] = nCharWidth;
+ }
+
+ if( !*pDataLen )
+ return NULL;
+
+ const unsigned char* pData = aRawFontData.steal();
+ return (void*)pData;
+}
+
+//--------------------------------------------------------------------------
+
+void WinSalGraphics::FreeEmbedFontData( const void* pData, long /*nLen*/ )
+{
+ delete[] reinterpret_cast<char*>(const_cast<void*>(pData));
+}
+
+//--------------------------------------------------------------------------
+
+const Ucs2SIntMap* WinSalGraphics::GetFontEncodingVector( const ImplFontData* pFont, const Ucs2OStrMap** pNonEncoded )
+{
+ // TODO: even for builtin fonts we get here... why?
+ if( !pFont->IsEmbeddable() )
+ return NULL;
+
+ // fill the encoding vector
+ // currently no nonencoded vector
+ if( pNonEncoded )
+ *pNonEncoded = NULL;
+
+ const ImplWinFontData* pWinFontData = static_cast<const ImplWinFontData*>(pFont);
+ const Ucs2SIntMap* pEncoding = pWinFontData->GetEncodingVector();
+ if( pEncoding == NULL )
+ {
+ Ucs2SIntMap* pNewEncoding = new Ucs2SIntMap;
+ #if 0
+ // TODO: get correct encoding vector
+ GLYPHSET aGlyphSet;
+ aGlyphSet.cbThis = sizeof(aGlyphSet);
+ DWORD aW = ::GetFontUnicodeRanges( mhDC, &aGlyphSet);
+ #else
+ for( sal_Unicode i = 32; i < 256; ++i )
+ (*pNewEncoding)[i] = i;
+ #endif
+ pWinFontData->SetEncodingVector( pNewEncoding );
+ pEncoding = pNewEncoding;
+ }
+
+ return pEncoding;
+}
+
+//--------------------------------------------------------------------------
+
+void WinSalGraphics::GetGlyphWidths( const ImplFontData* pFont,
+ bool bVertical,
+ Int32Vector& rWidths,
+ Ucs2UIntMap& rUnicodeEnc )
+{
+ // create matching ImplFontSelectData
+ // we need just enough to get to the font file data
+ ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
+
+ // TODO: much better solution: move SetFont and restoration of old font to caller
+ ScopedFont aOldFont(*this);
+
+ float fScale = 0.0;
+ HFONT hOldFont = 0;
+ ImplDoSetFont( &aIFSD, fScale, hOldFont );
+
+ if( pFont->IsSubsettable() )
+ {
+ // get raw font file data
+ const RawFontData xRawFontData( mhDC );
+ if( !xRawFontData.get() )
+ return;
+
+ // open font file
+ sal_uInt32 nFaceNum = 0;
+ if( !*xRawFontData.get() ) // TTC candidate
+ nFaceNum = ~0U; // indicate "TTC font extracts only"
+
+ ScopedTrueTypeFont aSftTTF;
+ int nRC = aSftTTF.open( (void*)xRawFontData.get(), xRawFontData.size(), nFaceNum );
+ if( nRC != SF_OK )
+ return;
+
+ int nGlyphs = GetTTGlyphCount( aSftTTF.get() );
+ if( nGlyphs > 0 )
+ {
+ rWidths.resize(nGlyphs);
+ std::vector<sal_uInt16> aGlyphIds(nGlyphs);
+ for( int i = 0; i < nGlyphs; i++ )
+ aGlyphIds[i] = sal_uInt16(i);
+ TTSimpleGlyphMetrics* pMetrics = ::GetTTSimpleGlyphMetrics( aSftTTF.get(),
+ &aGlyphIds[0],
+ nGlyphs,
+ bVertical ? 1 : 0 );
+ if( pMetrics )
+ {
+ for( int i = 0; i< nGlyphs; i++ )
+ rWidths[i] = pMetrics[i].adv;
+ free( pMetrics );
+ rUnicodeEnc.clear();
+ }
+ const ImplWinFontData* pWinFont = static_cast<const ImplWinFontData*>(pFont);
+ const ImplFontCharMap* pMap = pWinFont->GetImplFontCharMap();
+ DBG_ASSERT( pMap && pMap->GetCharCount(), "no map" );
+ pMap->AddReference();
+
+ int nCharCount = pMap->GetCharCount();
+ sal_uInt32 nChar = pMap->GetFirstChar();
+ for( int i = 0; i < nCharCount; i++ )
+ {
+ if( nChar < 0x00010000 )
+ {
+ sal_uInt16 nGlyph = ::MapChar( aSftTTF.get(),
+ static_cast<sal_Ucs>(nChar),
+ bVertical ? 1 : 0 );
+ if( nGlyph )
+ rUnicodeEnc[ static_cast<sal_Unicode>(nChar) ] = nGlyph;
+ }
+ nChar = pMap->GetNextChar( nChar );
+ }
+
+ pMap->DeReference(); // TODO: and and use a RAII object
+ }
+ }
+ else if( pFont->IsEmbeddable() )
+ {
+ // get individual character widths
+ rWidths.clear();
+ rUnicodeEnc.clear();
+ rWidths.reserve( 224 );
+ for( sal_Unicode i = 32; i < 256; ++i )
+ {
+ int nCharWidth = 0;
+ if( ::GetCharWidth32W( mhDC, i, i, &nCharWidth ) )
+ {
+ rUnicodeEnc[ i ] = rWidths.size();
+ rWidths.push_back( nCharWidth );
+ }
+ }
+ }
+}
+
+//--------------------------------------------------------------------------
+
+void WinSalGraphics::DrawServerFontLayout( const ServerFontLayout& )
+{}
+
+//--------------------------------------------------------------------------
+
+SystemFontData WinSalGraphics::GetSysFontData( int nFallbacklevel ) const
+{
+ SystemFontData aSysFontData;
+
+ if (nFallbacklevel >= MAX_FALLBACK) nFallbacklevel = MAX_FALLBACK - 1;
+ if (nFallbacklevel < 0 ) nFallbacklevel = 0;
+
+ aSysFontData.nSize = sizeof( SystemFontData );
+ aSysFontData.hFont = mhFonts[nFallbacklevel];
+ aSysFontData.bFakeBold = false;
+ aSysFontData.bFakeItalic = false;
+ aSysFontData.bAntialias = true;
+ aSysFontData.bVerticalCharacterType = false;
+
+ OSL_TRACE("\r\n:WinSalGraphics::GetSysFontData(): FontID: %p, Fallback level: %d",
+ aSysFontData.hFont,
+ nFallbacklevel);
+
+ return aSysFontData;
+}
+
+//--------------------------------------------------------------------------
+