diff options
author | Norbert Thiebaud <nthiebaud@gmail.com> | 2014-03-20 00:22:26 -0500 |
---|---|---|
committer | Norbert Thiebaud <nthiebaud@gmail.com> | 2014-03-20 16:11:58 -0500 |
commit | 7260bcd68726739b5fe26b4d29a1de47e7b0f5a7 (patch) | |
tree | 360f470874da345bf0723cfd90684942824df739 /vcl/source/font | |
parent | 7154b092a716d4d0041a8a563294511ec9e5dbc6 (diff) |
cosmetic: split PhysicalFontFamily/Collection out of outdev3.cxx
Change-Id: Iae8eb15413c0c069c14edd92c94ecb0113d9d371
Diffstat (limited to 'vcl/source/font')
-rw-r--r-- | vcl/source/font/PhysicalFontCollection.cxx | 1362 | ||||
-rw-r--r-- | vcl/source/font/PhysicalFontFamily.cxx | 298 |
2 files changed, 1660 insertions, 0 deletions
diff --git a/vcl/source/font/PhysicalFontCollection.cxx b/vcl/source/font/PhysicalFontCollection.cxx new file mode 100644 index 000000000000..6dcecc5d2766 --- /dev/null +++ b/vcl/source/font/PhysicalFontCollection.cxx @@ -0,0 +1,1362 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/types.h> + +#include <vector> + +#include <i18nlangtag/mslangid.hxx> + +#include <config_graphite.h> +#if ENABLE_GRAPHITE +#include "graphite_features.hxx" +#endif + +#include "magic.h" +#include "outdev.h" +#include "outfont.hxx" +#include "PhysicalFontFace.hxx" + +#include "PhysicalFontCollection.hxx" + +static OUString lcl_stripCharSetFromName(const OUString& _aName) +{ + // I worry that someone will have a font which *does* have + // e.g. "Greek" legitimately at the end of its name :-( + const char*suffixes[] = { " baltic", + " ce", + " cyr", + " greek", + " tur", + " (arabic)", + " (hebrew)", + " (thai)", + " (vietnamese)" + }; + + OUString aName = _aName; + // These can be crazily piled up, e.g. Times New Roman CYR Greek + bool bFinished = false; + while (!bFinished) + { + bFinished = true; + for (size_t i = 0; i < SAL_N_ELEMENTS(suffixes); ++i) + { + size_t nLen = strlen(suffixes[i]); + if (aName.endsWithIgnoreAsciiCaseAsciiL(suffixes[i], nLen)) + { + bFinished = false; + aName = aName.copy(0, aName.getLength() - nLen); + } + } + } + return aName; +} + +static unsigned lcl_IsCJKFont( const OUString& rFontName ) +{ + // Test, if Fontname includes CJK characters --> In this case we + // mention that it is a CJK font + for(int i = 0; i < rFontName.getLength(); i++) + { + const sal_Unicode ch = rFontName[i]; + // japanese + if ( ((ch >= 0x3040) && (ch <= 0x30FF)) || + ((ch >= 0x3190) && (ch <= 0x319F)) ) + return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_JP; + + // korean + if ( ((ch >= 0xAC00) && (ch <= 0xD7AF)) || + ((ch >= 0x3130) && (ch <= 0x318F)) || + ((ch >= 0x1100) && (ch <= 0x11FF)) ) + return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_KR; + + // chinese + if ( ((ch >= 0x3400) && (ch <= 0x9FFF)) ) + return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_TC|IMPL_FONT_ATTR_CJK_SC; + + // cjk + if ( ((ch >= 0x3000) && (ch <= 0xD7AF)) || + ((ch >= 0xFF00) && (ch <= 0xFFEE)) ) + return IMPL_FONT_ATTR_CJK; + + } + + return 0; +} + + +PhysicalFontCollection::PhysicalFontCollection() + : mbMatchData( false ) + , mbMapNames( false ) + , mpPreMatchHook( NULL ) + , mpFallbackHook( NULL ) + , mpFallbackList( NULL ) + , mnFallbackCount( -1 ) +{} + +PhysicalFontCollection::~PhysicalFontCollection() +{ + Clear(); +} + +void PhysicalFontCollection::SetPreMatchHook( ImplPreMatchFontSubstitution* pHook ) +{ + mpPreMatchHook = pHook; +} + +void PhysicalFontCollection::SetFallbackHook( ImplGlyphFallbackFontSubstitution* pHook ) +{ + mpFallbackHook = pHook; +} + +void PhysicalFontCollection::Clear() +{ + // remove fallback lists + delete[] mpFallbackList; + mpFallbackList = NULL; + mnFallbackCount = -1; + + // clear all entries in the device font list + PhysicalFontFamilies::iterator it = maPhysicalFontFamilies.begin(); + for(; it != maPhysicalFontFamilies.end(); ++it ) + { + PhysicalFontFamily* pEntry = (*it).second; + delete pEntry; + } + + maPhysicalFontFamilies.clear(); + + // match data must be recalculated too + mbMatchData = false; +} + +void PhysicalFontCollection::InitGenericGlyphFallback( void ) const +{ + // normalized family names of fonts suited for glyph fallback + // if a font is available related fonts can be ignored + // TODO: implement dynamic lists + static const char* aGlyphFallbackList[] = { + // empty strings separate the names of unrelated fonts + "eudc", "", + "arialunicodems", "cyberbit", "code2000", "", + "andalesansui", "", + "starsymbol", "opensymbol", "", + "msmincho", "fzmingti", "fzheiti", "ipamincho", "sazanamimincho", "kochimincho", "", + "sunbatang", "sundotum", "baekmukdotum", "gulim", "batang", "dotum", "", + "hgmincholightj", "msunglightsc", "msunglighttc", "hymyeongjolightk", "", + "tahoma", "dejavusans", "timesnewroman", "liberationsans", "", + "shree", "mangal", "", + "raavi", "shruti", "tunga", "", + "latha", "gautami", "kartika", "vrinda", "", + "shayyalmt", "naskmt", "scheherazade", "", + "david", "nachlieli", "lucidagrande", "", + "norasi", "angsanaupc", "", + "khmerossystem", "", + "muktinarrow", "", + "phetsarathot", "", + "padauk", "pinlonmyanmar", "", + "iskoolapota", "lklug", "", + 0 + }; + + bool bHasEudc = false; + int nMaxLevel = 0; + int nBestQuality = 0; + PhysicalFontFamily** pFallbackList = NULL; + + for( const char** ppNames = &aGlyphFallbackList[0];; ++ppNames ) + { + // advance to next sub-list when end-of-sublist marker + if( !**ppNames ) // #i46456# check for empty string, i.e., deref string itself not only ptr to it + { + if( nBestQuality > 0 ) + if( ++nMaxLevel >= MAX_FALLBACK ) + break; + + if( !ppNames[1] ) + break; + + nBestQuality = 0; + continue; + } + + // test if the glyph fallback candidate font is available and scalable + OUString aTokenName( *ppNames, strlen(*ppNames), RTL_TEXTENCODING_UTF8 ); + PhysicalFontFamily* pFallbackFont = FindFontFamily( aTokenName ); + + if( !pFallbackFont ) + continue; + + if( !pFallbackFont->IsScalable() ) + continue; + + // keep the best font of the glyph fallback sub-list + if( nBestQuality < pFallbackFont->GetMinQuality() ) + { + nBestQuality = pFallbackFont->GetMinQuality(); + // store available glyph fallback fonts + if( !pFallbackList ) + pFallbackList = new PhysicalFontFamily*[ MAX_FALLBACK ]; + + pFallbackList[ nMaxLevel ] = pFallbackFont; + if( !bHasEudc && !nMaxLevel ) + bHasEudc = !strncmp( *ppNames, "eudc", 5 ); + } + } + +#ifdef SAL_FONTENUM_STABLE_ON_PLATFORM // #i113472# + // sort the list of fonts for glyph fallback by quality (highest first) + // #i33947# keep the EUDC font at the front of the list + // an insertion sort is good enough for this short list + const int nSortStart = bHasEudc ? 1 : 0; + for( int i = nSortStart+1, j; i < nMaxLevel; ++i ) + { + PhysicalFontFamily* pTestFont = pFallbackList[ i ]; + int nTestQuality = pTestFont->GetMinQuality(); + + for( j = i; --j >= nSortStart; ) + { + if( nTestQuality > pFallbackList[j]->GetMinQuality() ) + pFallbackList[ j+1 ] = pFallbackList[ j ]; + else + break; + } + pFallbackList[ j+1 ] = pTestFont; + } +#endif + + mnFallbackCount = nMaxLevel; + mpFallbackList = pFallbackList; +} + +PhysicalFontFamily* PhysicalFontCollection::GetGlyphFallbackFont( FontSelectPattern& rFontSelData, + OUString& rMissingCodes, + int nFallbackLevel ) const +{ + PhysicalFontFamily* pFallbackData = NULL; + + // find a matching font candidate for platform specific glyph fallback + if( mpFallbackHook ) + { + // check cache for the first matching entry + // to avoid calling the expensive fallback hook (#i83491#) + sal_UCS4 cChar = 0; + bool bCached = true; + sal_Int32 nStrIndex = 0; + while( nStrIndex < rMissingCodes.getLength() ) + { + cChar = rMissingCodes.iterateCodePoints( &nStrIndex ); + bCached = rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &rFontSelData.maSearchName ); + + // ignore entries which don't have a fallback + if( !bCached || !rFontSelData.maSearchName.isEmpty() ) + break; + } + + if( bCached ) + { + // there is a matching fallback in the cache + // so update rMissingCodes with codepoints not yet resolved by this fallback + int nRemainingLength = 0; + sal_UCS4* pRemainingCodes = (sal_UCS4*)alloca( rMissingCodes.getLength() * sizeof(sal_UCS4) ); + OUString aFontName; + + while( nStrIndex < rMissingCodes.getLength() ) + { + cChar = rMissingCodes.iterateCodePoints( &nStrIndex ); + bCached = rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &aFontName ); + if( !bCached || (rFontSelData.maSearchName != aFontName) ) + pRemainingCodes[ nRemainingLength++ ] = cChar; + } + rMissingCodes = OUString( pRemainingCodes, nRemainingLength ); + } + else + { + OUString aOldMissingCodes = rMissingCodes; + + // call the hook to query the best matching glyph fallback font + if( mpFallbackHook->FindFontSubstitute( rFontSelData, rMissingCodes ) ) + // apply outdev3.cxx specific fontname normalization + GetEnglishSearchFontName( rFontSelData.maSearchName ); + else + rFontSelData.maSearchName = ""; + + // See fdo#32665 for an example. FreeSerif that has glyphs in normal + // font, but not in the italic or bold version + bool bSubSetOfFontRequiresPropertyFaking = rFontSelData.mbEmbolden || rFontSelData.maItalicMatrix != ItalicMatrix(); + + // Cache the result even if there was no match, unless its from part of a font for which the properties need + // to be faked. We need to rework this cache to take into account that fontconfig can return different fonts + // for different input sizes, weights, etc. Basically the cache is way to naive + if (!bSubSetOfFontRequiresPropertyFaking) + { + for(;;) + { + if( !rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &rFontSelData.maSearchName ) ) + rFontSelData.mpFontEntry->AddFallbackForUnicode( cChar, rFontSelData.GetWeight(), rFontSelData.maSearchName ); + if( nStrIndex >= aOldMissingCodes.getLength() ) + break; + cChar = aOldMissingCodes.iterateCodePoints( &nStrIndex ); + } + if( !rFontSelData.maSearchName.isEmpty() ) + { + // remove cache entries that were still not resolved + for( nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); ) + { + cChar = rMissingCodes.iterateCodePoints( &nStrIndex ); + rFontSelData.mpFontEntry->IgnoreFallbackForUnicode( cChar, rFontSelData.GetWeight(), rFontSelData.maSearchName ); + } + } + } + } + + // find the matching device font + if( !rFontSelData.maSearchName.isEmpty() ) + pFallbackData = FindFontFamily( rFontSelData.maSearchName ); + } + + // else find a matching font candidate for generic glyph fallback + if( !pFallbackData ) + { + // initialize font candidates for generic glyph fallback if needed + if( mnFallbackCount < 0 ) + InitGenericGlyphFallback(); + + // TODO: adjust nFallbackLevel by number of levels resolved by the fallback hook + if( nFallbackLevel < mnFallbackCount ) + pFallbackData = mpFallbackList[ nFallbackLevel ]; + } + + return pFallbackData; +} + +void PhysicalFontCollection::Add( PhysicalFontFace* pNewData ) +{ + OUString aSearchName = pNewData->GetFamilyName(); + GetEnglishSearchFontName( aSearchName ); + + PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.find( aSearchName ); + PhysicalFontFamily* pFoundData = NULL; + + if( it != maPhysicalFontFamilies.end() ) + pFoundData = (*it).second; + + if( !pFoundData ) + { + pFoundData = new PhysicalFontFamily( aSearchName ); + maPhysicalFontFamilies[ aSearchName ] = pFoundData; + } + + bool bKeepNewData = pFoundData->AddFontFace( pNewData ); + + if( !bKeepNewData ) + delete pNewData; +} + +// find the font from the normalized font family name +PhysicalFontFamily* PhysicalFontCollection::ImplFindBySearchName( const OUString& rSearchName ) const +{ +#ifdef DEBUG + OUString aTempName = rSearchName; + GetEnglishSearchFontName( aTempName ); + DBG_ASSERT( aTempName == rSearchName, "PhysicalFontCollection::ImplFindBySearchName() called with non-normalized name" ); +#endif + + PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.find( rSearchName ); + if( it == maPhysicalFontFamilies.end() ) + return NULL; + + PhysicalFontFamily* pFoundData = (*it).second; + return pFoundData; +} + +PhysicalFontFamily* PhysicalFontCollection::ImplFindByAliasName(const OUString& rSearchName, + const OUString& rShortName) const +{ + // short circuit for impossible font name alias + if (rSearchName.isEmpty()) + return NULL; + + // short circuit if no alias names are available + if (!mbMapNames) + return NULL; + + // use the font's alias names to find the font + // TODO: get rid of linear search + PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.begin(); + while( it != maPhysicalFontFamilies.end() ) + { + PhysicalFontFamily* pData = (*it).second; + if( pData->GetAliasNames().isEmpty() ) + continue; + + // if one alias name matches we found a matching font + OUString aTempName; + sal_Int32 nIndex = 0; + + do + { + aTempName = GetNextFontToken( pData->GetAliasNames(), nIndex ); + // Test, if the Font name match with one of the mapping names + if ( (aTempName == rSearchName) || (aTempName == rShortName) ) + return pData; + } + while ( nIndex != -1 ); + } + + return NULL; +} + +PhysicalFontFamily* PhysicalFontCollection::FindFontFamily( const OUString& rFontName ) const +{ + // normalize the font family name and + OUString aName = rFontName; + GetEnglishSearchFontName( aName ); + + PhysicalFontFamily* pFound = ImplFindBySearchName( aName ); + return pFound; +} + +PhysicalFontFamily* PhysicalFontCollection::ImplFindByTokenNames(const OUString& rTokenStr) const +{ + PhysicalFontFamily* pFoundData = NULL; + + // use normalized font name tokens to find the font + for( sal_Int32 nTokenPos = 0; nTokenPos != -1; ) + { + OUString aSearchName = GetNextFontToken( rTokenStr, nTokenPos ); + if( aSearchName.isEmpty() ) + continue; + + GetEnglishSearchFontName( aSearchName ); + pFoundData = ImplFindBySearchName( aSearchName ); + + if( pFoundData ) + break; + } + + return pFoundData; +} + +PhysicalFontFamily* PhysicalFontCollection::ImplFindBySubstFontAttr( const utl::FontNameAttr& rFontAttr ) const +{ + PhysicalFontFamily* pFoundData = NULL; + + // use the font substitutions suggested by the FontNameAttr to find the font + ::std::vector< OUString >::const_iterator it = rFontAttr.Substitutions.begin(); + for(; it != rFontAttr.Substitutions.end(); ++it ) + { + OUString aSearchName( *it ); + GetEnglishSearchFontName( aSearchName ); + + pFoundData = ImplFindBySearchName( aSearchName ); + if( pFoundData ) + return pFoundData; + } + + // use known attributes from the configuration to find a matching substitute + const sal_uLong nSearchType = rFontAttr.Type; + if( nSearchType != 0 ) + { + const FontWeight eSearchWeight = rFontAttr.Weight; + const FontWidth eSearchWidth = rFontAttr.Width; + const FontItalic eSearchSlant = ITALIC_DONTKNOW; + const OUString aSearchName; + + pFoundData = ImplFindByAttributes( nSearchType, + eSearchWeight, eSearchWidth, eSearchSlant, aSearchName ); + + if( pFoundData ) + return pFoundData; + } + + return NULL; +} + +void PhysicalFontCollection::InitMatchData() const +{ + // short circuit if already done + if( mbMatchData ) + return; + mbMatchData = true; + + // calculate MatchData for all entries + const utl::FontSubstConfiguration& rFontSubst = utl::FontSubstConfiguration::get(); + + PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.begin(); + for(; it != maPhysicalFontFamilies.end(); ++it ) + { + const OUString& rSearchName = (*it).first; + PhysicalFontFamily* pEntry = (*it).second; + + pEntry->InitMatchData( rFontSubst, rSearchName ); + } +} + +PhysicalFontFamily* PhysicalFontCollection::ImplFindByAttributes( sal_uLong nSearchType, + FontWeight eSearchWeight, + FontWidth eSearchWidth, + FontItalic eSearchItalic, + const OUString& rSearchFamilyName ) const +{ + if( (eSearchItalic != ITALIC_NONE) && (eSearchItalic != ITALIC_DONTKNOW) ) + nSearchType |= IMPL_FONT_ATTR_ITALIC; + + // don't bother to match attributes if the attributes aren't worth matching + if( !nSearchType + && ((eSearchWeight == WEIGHT_DONTKNOW) || (eSearchWeight == WEIGHT_NORMAL)) + && ((eSearchWidth == WIDTH_DONTKNOW) || (eSearchWidth == WIDTH_NORMAL)) ) + return NULL; + + InitMatchData(); + PhysicalFontFamily* pFoundData = NULL; + + long nTestMatch; + long nBestMatch = 40000; + sal_uLong nBestType = 0; + + PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.begin(); + for(; it != maPhysicalFontFamilies.end(); ++it ) + { + PhysicalFontFamily* pData = (*it).second; + + // Get all information about the matching font + sal_uLong nMatchType = pData->GetMatchType(); + FontWeight eMatchWeight= pData->GetMatchWeight(); + FontWidth eMatchWidth = pData->GetMatchWidth(); + + // Calculate Match Value + // 1000000000 + // 100000000 + // 10000000 CJK, CTL, None-Latin, Symbol + // 1000000 FamilyName, Script, Fixed, -Special, -Decorative, + // Titling, Capitals, Outline, Shadow + // 100000 Match FamilyName, Serif, SansSerif, Italic, + // Width, Weight + // 10000 Scalable, Standard, Default, + // full, Normal, Knownfont, + // Otherstyle, +Special, +Decorative, + // 1000 Typewriter, Rounded, Gothic, Schollbook + // 100 + nTestMatch = 0; + + // test CJK script attributes + if ( nSearchType & IMPL_FONT_ATTR_CJK ) + { + // Matching language + if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_CJK_ALLLANG) ) + nTestMatch += 10000000*3; + if( nMatchType & IMPL_FONT_ATTR_CJK ) + nTestMatch += 10000000*2; + if( nMatchType & IMPL_FONT_ATTR_FULL ) + nTestMatch += 10000000; + } + else if ( nMatchType & IMPL_FONT_ATTR_CJK ) + { + nTestMatch -= 10000000; + } + + // test CTL script attributes + if( nSearchType & IMPL_FONT_ATTR_CTL ) + { + if( nMatchType & IMPL_FONT_ATTR_CTL ) + nTestMatch += 10000000*2; + if( nMatchType & IMPL_FONT_ATTR_FULL ) + nTestMatch += 10000000; + } + else if ( nMatchType & IMPL_FONT_ATTR_CTL ) + { + nTestMatch -= 10000000; + } + + // test LATIN script attributes + if( nSearchType & IMPL_FONT_ATTR_NONELATIN ) + { + if( nMatchType & IMPL_FONT_ATTR_NONELATIN ) + nTestMatch += 10000000*2; + if( nMatchType & IMPL_FONT_ATTR_FULL ) + nTestMatch += 10000000; + } + + // test SYMBOL attributes + if ( nSearchType & IMPL_FONT_ATTR_SYMBOL ) + { + const OUString& rSearchName = it->first; + // prefer some special known symbol fonts + if ( rSearchName == "starsymbol" ) + { + nTestMatch += 10000000*6+(10000*3); + } + else if ( rSearchName == "opensymbol" ) + { + nTestMatch += 10000000*6; + } + else if ( rSearchName == "starbats" || + rSearchName == "wingdings" || + rSearchName == "monotypesorts" || + rSearchName == "dingbats" || + rSearchName == "zapfdingbats" ) + { + nTestMatch += 10000000*5; + } + else if ( pData->GetTypeFaces() & FONT_FAMILY_SYMBOL ) + { + nTestMatch += 10000000*4; + } + else + { + if( nMatchType & IMPL_FONT_ATTR_SYMBOL ) + nTestMatch += 10000000*2; + if( nMatchType & IMPL_FONT_ATTR_FULL ) + nTestMatch += 10000000; + } + } + else if ( (pData->GetTypeFaces() & (FONT_FAMILY_SYMBOL | FONT_FAMILY_NONESYMBOL)) == FONT_FAMILY_SYMBOL ) + { + nTestMatch -= 10000000; + } + else if ( nMatchType & IMPL_FONT_ATTR_SYMBOL ) + { + nTestMatch -= 10000; + } + + // match stripped family name + if( !rSearchFamilyName.isEmpty() && (rSearchFamilyName == pData->GetMatchFamilyName()) ) + { + nTestMatch += 1000000*3; + } + + // match ALLSCRIPT? attribute + if( nSearchType & IMPL_FONT_ATTR_ALLSCRIPT ) + { + if( nMatchType & IMPL_FONT_ATTR_ALLSCRIPT ) + { + nTestMatch += 1000000*2; + } + if( nSearchType & IMPL_FONT_ATTR_ALLSUBSCRIPT ) + { + if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_ALLSUBSCRIPT) ) + nTestMatch += 1000000*2; + if( 0 != ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_BRUSHSCRIPT) ) + nTestMatch -= 1000000; + } + } + else if( nMatchType & IMPL_FONT_ATTR_ALLSCRIPT ) + { + nTestMatch -= 1000000; + } + + // test MONOSPACE+TYPEWRITER attributes + if( nSearchType & IMPL_FONT_ATTR_FIXED ) + { + if( nMatchType & IMPL_FONT_ATTR_FIXED ) + nTestMatch += 1000000*2; + // a typewriter attribute is even better + if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_TYPEWRITER) ) + nTestMatch += 10000*2; + } + else if( nMatchType & IMPL_FONT_ATTR_FIXED ) + { + nTestMatch -= 1000000; + } + + // test SPECIAL attribute + if( nSearchType & IMPL_FONT_ATTR_SPECIAL ) + { + if( nMatchType & IMPL_FONT_ATTR_SPECIAL ) + { + nTestMatch += 10000; + } + else if( !(nSearchType & IMPL_FONT_ATTR_ALLSERIFSTYLE) ) + { + if( nMatchType & IMPL_FONT_ATTR_SERIF ) + { + nTestMatch += 1000*2; + } + else if( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) + { + nTestMatch += 1000; + } + } + } + else if( (nMatchType & IMPL_FONT_ATTR_SPECIAL) && !(nSearchType & IMPL_FONT_ATTR_SYMBOL) ) + { + nTestMatch -= 1000000; + } + + // test DECORATIVE attribute + if( nSearchType & IMPL_FONT_ATTR_DECORATIVE ) + { + if( nMatchType & IMPL_FONT_ATTR_DECORATIVE ) + { + nTestMatch += 10000; + } + else if( !(nSearchType & IMPL_FONT_ATTR_ALLSERIFSTYLE) ) + { + if( nMatchType & IMPL_FONT_ATTR_SERIF ) + nTestMatch += 1000*2; + else if ( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) + nTestMatch += 1000; + } + } + else if( nMatchType & IMPL_FONT_ATTR_DECORATIVE ) + { + nTestMatch -= 1000000; + } + + // test TITLE+CAPITALS attributes + if( nSearchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) ) + { + if( nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) ) + { + nTestMatch += 1000000*2; + } + if( 0 == ((nSearchType^nMatchType) & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS))) + { + nTestMatch += 1000000; + } + else if( (nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS)) && + (nMatchType & (IMPL_FONT_ATTR_STANDARD | IMPL_FONT_ATTR_DEFAULT)) ) + { + nTestMatch += 1000000; + } + } + else if( nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) ) + { + nTestMatch -= 1000000; + } + + // test OUTLINE+SHADOW attributes + if( nSearchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) ) + { + if( nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) ) + { + nTestMatch += 1000000*2; + } + if( 0 == ((nSearchType ^ nMatchType) & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW)) ) + { + nTestMatch += 1000000; + } + else if( (nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW)) && + (nMatchType & (IMPL_FONT_ATTR_STANDARD | IMPL_FONT_ATTR_DEFAULT)) ) + { + nTestMatch += 1000000; + } + } + else if ( nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) ) + { + nTestMatch -= 1000000; + } + + // test font name substrings + // TODO: calculate name matching score using e.g. Levenstein distance + if( (rSearchFamilyName.getLength() >= 4) && + (pData->GetMatchFamilyName().getLength() >= 4) && + ((rSearchFamilyName.indexOf( pData->GetMatchFamilyName() ) != -1) || + (pData->GetMatchFamilyName().indexOf( rSearchFamilyName ) != -1)) ) + { + nTestMatch += 5000; + } + // test SERIF attribute + if( nSearchType & IMPL_FONT_ATTR_SERIF ) + { + if( nMatchType & IMPL_FONT_ATTR_SERIF ) + nTestMatch += 1000000*2; + else if( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) + nTestMatch -= 1000000; + } + + // test SANSERIF attribute + if( nSearchType & IMPL_FONT_ATTR_SANSSERIF ) + { + if( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) + nTestMatch += 1000000; + else if ( nMatchType & IMPL_FONT_ATTR_SERIF ) + nTestMatch -= 1000000; + } + + // test ITALIC attribute + if( nSearchType & IMPL_FONT_ATTR_ITALIC ) + { + if( pData->GetTypeFaces() & FONT_FAMILY_ITALIC ) + nTestMatch += 1000000*3; + if( nMatchType & IMPL_FONT_ATTR_ITALIC ) + nTestMatch += 1000000; + } + else if( !(nSearchType & IMPL_FONT_ATTR_ALLSCRIPT) && + ((nMatchType & IMPL_FONT_ATTR_ITALIC) || + !(pData->GetTypeFaces() & FONT_FAMILY_NONEITALIC)) ) + { + nTestMatch -= 1000000*2; + } + + // test WIDTH attribute + if( (eSearchWidth != WIDTH_DONTKNOW) && (eSearchWidth != WIDTH_NORMAL) ) + { + if( eSearchWidth < WIDTH_NORMAL ) + { + if( eSearchWidth == eMatchWidth ) + nTestMatch += 1000000*3; + else if( (eMatchWidth < WIDTH_NORMAL) && (eMatchWidth != WIDTH_DONTKNOW) ) + nTestMatch += 1000000; + } + else + { + if( eSearchWidth == eMatchWidth ) + nTestMatch += 1000000*3; + else if( eMatchWidth > WIDTH_NORMAL ) + nTestMatch += 1000000; + } + } + else if( (eMatchWidth != WIDTH_DONTKNOW) && (eMatchWidth != WIDTH_NORMAL) ) + { + nTestMatch -= 1000000; + } + + // test WEIGHT attribute + if( (eSearchWeight != WEIGHT_DONTKNOW) && + (eSearchWeight != WEIGHT_NORMAL) && + (eSearchWeight != WEIGHT_MEDIUM) ) + { + if( eSearchWeight < WEIGHT_NORMAL ) + { + if( pData->GetTypeFaces() & FONT_FAMILY_LIGHT ) + nTestMatch += 1000000; + if( (eMatchWeight < WEIGHT_NORMAL) && (eMatchWeight != WEIGHT_DONTKNOW) ) + nTestMatch += 1000000; + } + else + { + if( pData->GetTypeFaces() & FONT_FAMILY_BOLD ) + nTestMatch += 1000000; + if( eMatchWeight > WEIGHT_BOLD ) + nTestMatch += 1000000; + } + } + else if( ((eMatchWeight != WEIGHT_DONTKNOW) && + (eMatchWeight != WEIGHT_NORMAL) && + (eMatchWeight != WEIGHT_MEDIUM)) || + !(pData->GetTypeFaces() & FONT_FAMILY_NORMAL) ) + { + nTestMatch -= 1000000; + } + + // prefer scalable fonts + if( pData->GetTypeFaces() & FONT_FAMILY_SCALABLE ) + nTestMatch += 10000*4; + else + nTestMatch -= 10000*4; + + // test STANDARD+DEFAULT+FULL+NORMAL attributes + if( nMatchType & IMPL_FONT_ATTR_STANDARD ) + nTestMatch += 10000*2; + if( nMatchType & IMPL_FONT_ATTR_DEFAULT ) + nTestMatch += 10000; + if( nMatchType & IMPL_FONT_ATTR_FULL ) + nTestMatch += 10000; + if( nMatchType & IMPL_FONT_ATTR_NORMAL ) + nTestMatch += 10000; + + // test OTHERSTYLE attribute + if( ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_OTHERSTYLE) != 0 ) + { + nTestMatch -= 10000; + } + + // test ROUNDED attribute + if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_ROUNDED) ) + nTestMatch += 1000; + + // test TYPEWRITER attribute + if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_TYPEWRITER) ) + nTestMatch += 1000; + + // test GOTHIC attribute + if( nSearchType & IMPL_FONT_ATTR_GOTHIC ) + { + if( nMatchType & IMPL_FONT_ATTR_GOTHIC ) + nTestMatch += 1000*3; + if( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) + nTestMatch += 1000*2; + } + + // test SCHOOLBOOK attribute + if( nSearchType & IMPL_FONT_ATTR_SCHOOLBOOK ) + { + if( nMatchType & IMPL_FONT_ATTR_SCHOOLBOOK ) + nTestMatch += 1000*3; + if( nMatchType & IMPL_FONT_ATTR_SERIF ) + nTestMatch += 1000*2; + } + + // compare with best matching font yet + if ( nTestMatch > nBestMatch ) + { + pFoundData = pData; + nBestMatch = nTestMatch; + nBestType = nMatchType; + } + else if( nTestMatch == nBestMatch ) + { + // some fonts are more suitable defaults + if( nMatchType & IMPL_FONT_ATTR_DEFAULT ) + { + pFoundData = pData; + nBestType = nMatchType; + } + else if( (nMatchType & IMPL_FONT_ATTR_STANDARD) && + !(nBestType & IMPL_FONT_ATTR_DEFAULT) ) + { + pFoundData = pData; + nBestType = nMatchType; + } + } + } + + return pFoundData; +} + +PhysicalFontFamily* PhysicalFontCollection::FindDefaultFont() const +{ + // try to find one of the default fonts of the + // UNICODE, SANSSERIF, SERIF or FIXED default font lists + const utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get(); + LanguageTag aLanguageTag( OUString( "en")); + OUString aFontname = rDefaults.getDefaultFont( aLanguageTag, DEFAULTFONT_SANS_UNICODE ); + PhysicalFontFamily* pFoundData = ImplFindByTokenNames( aFontname ); + + if( pFoundData ) + return pFoundData; + + aFontname = rDefaults.getDefaultFont( aLanguageTag, DEFAULTFONT_SANS ); + pFoundData = ImplFindByTokenNames( aFontname ); + if( pFoundData ) + return pFoundData; + + aFontname = rDefaults.getDefaultFont( aLanguageTag, DEFAULTFONT_SERIF ); + pFoundData = ImplFindByTokenNames( aFontname ); + if( pFoundData ) + return pFoundData; + + aFontname = rDefaults.getDefaultFont( aLanguageTag, DEFAULTFONT_FIXED ); + pFoundData = ImplFindByTokenNames( aFontname ); + if( pFoundData ) + return pFoundData; + + // now try to find a reasonable non-symbol font + + InitMatchData(); + + PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.begin(); + for(; it != maPhysicalFontFamilies.end(); ++it ) + { + PhysicalFontFamily* pData = (*it).second; + if( pData->GetMatchType() & IMPL_FONT_ATTR_SYMBOL ) + continue; + + pFoundData = pData; + if( pData->GetMatchType() & (IMPL_FONT_ATTR_DEFAULT|IMPL_FONT_ATTR_STANDARD) ) + break; + } + if( pFoundData ) + return pFoundData; + + // finding any font is better than finding no font at all + it = maPhysicalFontFamilies.begin(); + if( it != maPhysicalFontFamilies.end() ) + pFoundData = (*it).second; + + return pFoundData; +} + +PhysicalFontCollection* PhysicalFontCollection::Clone( bool bScalable, bool bEmbeddable ) const +{ + PhysicalFontCollection* pClonedCollection = new PhysicalFontCollection; + pClonedCollection->mbMapNames = mbMapNames; + pClonedCollection->mpPreMatchHook = mpPreMatchHook; + pClonedCollection->mpFallbackHook = mpFallbackHook; + + // TODO: clone the config-font attributes too? + pClonedCollection->mbMatchData = false; + + PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.begin(); + for(; it != maPhysicalFontFamilies.end(); ++it ) + { + const PhysicalFontFamily* pFontFace = (*it).second; + pFontFace->UpdateCloneFontList( *pClonedCollection, bScalable, bEmbeddable ); + } + + return pClonedCollection; +} + +ImplGetDevFontList* PhysicalFontCollection::GetDevFontList() const +{ + ImplGetDevFontList* pGetDevFontList = new ImplGetDevFontList; + + PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.begin(); + for(; it != maPhysicalFontFamilies.end(); ++it ) + { + const PhysicalFontFamily* pFontFamily = (*it).second; + pFontFamily->UpdateDevFontList( *pGetDevFontList ); + } + + return pGetDevFontList; +} + +ImplGetDevSizeList* PhysicalFontCollection::GetDevSizeList( const OUString& rFontName ) const +{ + ImplGetDevSizeList* pGetDevSizeList = new ImplGetDevSizeList( rFontName ); + + PhysicalFontFamily* pFontFamily = FindFontFamily( rFontName ); + if( pFontFamily != NULL ) + { + std::set<int> rHeights; + pFontFamily->GetFontHeights( rHeights ); + + std::set<int>::const_iterator it = rHeights.begin(); + for(; it != rHeights.begin(); ++it ) + pGetDevSizeList->Add( *it ); + } + + return pGetDevSizeList; +} + +PhysicalFontFamily* PhysicalFontCollection::ImplFindByFont( FontSelectPattern& rFSD ) const +{ + // give up if no fonts are available + if( !Count() ) + return NULL; + + bool bMultiToken = false; + sal_Int32 nTokenPos = 0; + OUString& aSearchName = rFSD.maSearchName; // TODO: get rid of reference + for(;;) + { + rFSD.maTargetName = GetNextFontToken( rFSD.GetFamilyName(), nTokenPos ); + aSearchName = rFSD.maTargetName; + +#if ENABLE_GRAPHITE + // Until features are properly supported, they are appended to the + // font name, so we need to strip them off so the font is found. + sal_Int32 nFeat = aSearchName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX); + OUString aOrigName = rFSD.maTargetName; + OUString aBaseFontName = aSearchName.copy( 0, (nFeat != -1) ? nFeat : aSearchName.getLength() ); + + if (nFeat != -1 && + -1 != aSearchName.indexOf(grutils::GrFeatureParser::FEAT_ID_VALUE_SEPARATOR, nFeat)) + { + aSearchName = aBaseFontName; + rFSD.maTargetName = aBaseFontName; + } + +#endif + + GetEnglishSearchFontName( aSearchName ); + ImplFontSubstitute( aSearchName ); + // #114999# special emboldening for Ricoh fonts + // TODO: smarter check for special cases by using PreMatch infrastructure? + if( (rFSD.GetWeight() > WEIGHT_MEDIUM) && + aSearchName.startsWithIgnoreAsciiCase( "hg" ) ) + { + OUString aBoldName; + if( aSearchName.startsWithIgnoreAsciiCase( "hggothicb" ) ) + aBoldName = "hggothice"; + else if( aSearchName.startsWithIgnoreAsciiCase( "hgpgothicb" ) ) + aBoldName = "hgpgothice"; + else if( aSearchName.startsWithIgnoreAsciiCase( "hgminchol" ) ) + aBoldName = "hgminchob"; + else if( aSearchName.startsWithIgnoreAsciiCase( "hgpminchol" ) ) + aBoldName = "hgpminchob"; + else if( aSearchName.equalsIgnoreAsciiCase( "hgminchob" ) ) + aBoldName = "hgminchoe"; + else if( aSearchName.equalsIgnoreAsciiCase( "hgpminchob" ) ) + aBoldName = "hgpminchoe"; + + if( !aBoldName.isEmpty() && ImplFindBySearchName( aBoldName ) ) + { + // the other font is available => use it + aSearchName = aBoldName; + // prevent synthetic emboldening of bold version + rFSD.SetWeight(WEIGHT_DONTKNOW); + } + } + +#if ENABLE_GRAPHITE + // restore the features to make the font selection data unique + rFSD.maTargetName = aOrigName; +#endif + // check if the current font name token or its substitute is valid + PhysicalFontFamily* pFoundData = ImplFindBySearchName( aSearchName ); + if( pFoundData ) + return pFoundData; + + // some systems provide special customization + // e.g. they suggest "serif" as UI-font, but this name cannot be used directly + // because the system wants to map it to another font first, e.g. "Helvetica" +#if ENABLE_GRAPHITE + // use the target name to search in the prematch hook + rFSD.maTargetName = aBaseFontName; +#endif + + // Related: fdo#49271 RTF files often contain weird-ass + // Win 3.1/Win95 style fontnames which attempt to put the + // charset encoding into the filename + // http://www.webcenter.ru/~kazarn/eng/fonts_ttf.htm + OUString sStrippedName = lcl_stripCharSetFromName(rFSD.maTargetName); + if (sStrippedName != rFSD.maTargetName) + { + rFSD.maTargetName = sStrippedName; + aSearchName = rFSD.maTargetName; + GetEnglishSearchFontName(aSearchName); + pFoundData = ImplFindBySearchName(aSearchName); + if( pFoundData ) + return pFoundData; + } + + if( mpPreMatchHook ) + { + if( mpPreMatchHook->FindFontSubstitute( rFSD ) ) + GetEnglishSearchFontName( aSearchName ); + } +#if ENABLE_GRAPHITE + // the prematch hook uses the target name to search, but we now need + // to restore the features to make the font selection data unique + rFSD.maTargetName = aOrigName; +#endif + pFoundData = ImplFindBySearchName( aSearchName ); + if( pFoundData ) + return pFoundData; + + // break after last font name token was checked unsuccessfully + if( nTokenPos == -1) + break; + bMultiToken = true; + } + + // if the first font was not available find the next available font in + // the semicolon separated list of font names. A font is also considered + // available when there is a matching entry in the Tools->Options->Fonts + // dialog witho neither ALWAYS nor SCREENONLY flags set and the substitution + // font is available + for( nTokenPos = 0; nTokenPos != -1; ) + { + if( bMultiToken ) + { + rFSD.maTargetName = GetNextFontToken( rFSD.GetFamilyName(), nTokenPos ); + aSearchName = rFSD.maTargetName; + GetEnglishSearchFontName( aSearchName ); + } + else + nTokenPos = -1; + if( mpPreMatchHook ) + if( mpPreMatchHook->FindFontSubstitute( rFSD ) ) + GetEnglishSearchFontName( aSearchName ); + ImplFontSubstitute( aSearchName ); + PhysicalFontFamily* pFoundData = ImplFindBySearchName( aSearchName ); + if( pFoundData ) + return pFoundData; + } + + // if no font with a directly matching name is available use the + // first font name token and get its attributes to find a replacement + if ( bMultiToken ) + { + nTokenPos = 0; + rFSD.maTargetName = GetNextFontToken( rFSD.GetFamilyName(), nTokenPos ); + aSearchName = rFSD.maTargetName; + GetEnglishSearchFontName( aSearchName ); + } + + OUString aSearchShortName; + OUString aSearchFamilyName; + FontWeight eSearchWeight = rFSD.GetWeight(); + FontWidth eSearchWidth = rFSD.GetWidthType(); + sal_uLong nSearchType = 0; + utl::FontSubstConfiguration::getMapName( aSearchName, aSearchShortName, aSearchFamilyName, + eSearchWeight, eSearchWidth, nSearchType ); + + // note: the search name was already translated to english (if possible) + // use the font's shortened name if needed + if ( aSearchShortName != aSearchName ) + { + PhysicalFontFamily* pFoundData = ImplFindBySearchName( aSearchShortName ); + if( pFoundData ) + { +#ifdef UNX + /* #96738# don't use mincho as an replacement for "MS Mincho" on X11: Mincho is + a korean bitmap font that is not suitable here. Use the font replacement table, + that automatically leads to the desired "HG Mincho Light J". Same story for + MS Gothic, there are thai and korean "Gothic" fonts, so we even prefer Andale */ + static OUString aMS_Mincho( "msmincho" ); + static OUString aMS_Gothic( "msgothic" ); + if ((aSearchName != aMS_Mincho) && (aSearchName != aMS_Gothic)) + // TODO: add heuristic to only throw out the fake ms* fonts +#endif + { + return pFoundData; + } + } + } + + // use font fallback + const utl::FontNameAttr* pFontAttr = NULL; + if( !aSearchName.isEmpty() ) + { + // get fallback info using FontSubstConfiguration and + // the target name, it's shortened name and family name in that order + const utl::FontSubstConfiguration& rFontSubst = utl::FontSubstConfiguration::get(); + pFontAttr = rFontSubst.getSubstInfo( aSearchName ); + if ( !pFontAttr && (aSearchShortName != aSearchName) ) + pFontAttr = rFontSubst.getSubstInfo( aSearchShortName ); + if ( !pFontAttr && (aSearchFamilyName != aSearchShortName) ) + pFontAttr = rFontSubst.getSubstInfo( aSearchFamilyName ); + + // try the font substitutions suggested by the fallback info + if( pFontAttr ) + { + PhysicalFontFamily* pFoundData = ImplFindBySubstFontAttr( *pFontAttr ); + if( pFoundData ) + return pFoundData; + } + } + + // if a target symbol font is not available use a default symbol font + if( rFSD.IsSymbolFont() ) + { + LanguageTag aDefaultLanguageTag( OUString( "en")); + aSearchName = utl::DefaultFontConfiguration::get().getDefaultFont( aDefaultLanguageTag, DEFAULTFONT_SYMBOL ); + PhysicalFontFamily* pFoundData = ImplFindByTokenNames( aSearchName ); + if( pFoundData ) + return pFoundData; + } + + // now try the other font name tokens + while( nTokenPos != -1 ) + { + rFSD.maTargetName = GetNextFontToken( rFSD.GetFamilyName(), nTokenPos ); + if( rFSD.maTargetName.isEmpty() ) + continue; + + aSearchName = rFSD.maTargetName; + GetEnglishSearchFontName( aSearchName ); + + OUString aTempShortName; + OUString aTempFamilyName; + sal_uLong nTempType = 0; + FontWeight eTempWeight = rFSD.GetWeight(); + FontWidth eTempWidth = WIDTH_DONTKNOW; + utl::FontSubstConfiguration::getMapName( aSearchName, aTempShortName, aTempFamilyName, + eTempWeight, eTempWidth, nTempType ); + + // use a shortend token name if available + if( aTempShortName != aSearchName ) + { + PhysicalFontFamily* pFoundData = ImplFindBySearchName( aTempShortName ); + if( pFoundData ) + return pFoundData; + } + + // use a font name from font fallback list to determine font attributes + // get fallback info using FontSubstConfiguration and + // the target name, it's shortened name and family name in that order + const utl::FontSubstConfiguration& rFontSubst = utl::FontSubstConfiguration::get(); + const utl::FontNameAttr* pTempFontAttr = rFontSubst.getSubstInfo( aSearchName ); + + if ( !pTempFontAttr && (aTempShortName != aSearchName) ) + pTempFontAttr = rFontSubst.getSubstInfo( aTempShortName ); + + if ( !pTempFontAttr && (aTempFamilyName != aTempShortName) ) + pTempFontAttr = rFontSubst.getSubstInfo( aTempFamilyName ); + + // try the font substitutions suggested by the fallback info + if( pTempFontAttr ) + { + PhysicalFontFamily* pFoundData = ImplFindBySubstFontAttr( *pTempFontAttr ); + if( pFoundData ) + return pFoundData; + if( !pFontAttr ) + pFontAttr = pTempFontAttr; + } + } + + // if still needed use the alias names of the installed fonts + if( mbMapNames ) + { + PhysicalFontFamily* pFoundData = ImplFindByAliasName( rFSD.maTargetName, aSearchShortName ); + if( pFoundData ) + return pFoundData; + } + + // if still needed use the font request's attributes to find a good match + if (MsLangId::isSimplifiedChinese(rFSD.meLanguage)) + nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_SC; + else if (MsLangId::isTraditionalChinese(rFSD.meLanguage)) + nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_TC; + else if (MsLangId::isKorean(rFSD.meLanguage)) + nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_KR; + else if (rFSD.meLanguage == LANGUAGE_JAPANESE) + nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_JP; + else + { + nSearchType |= lcl_IsCJKFont( rFSD.GetFamilyName() ); + if( rFSD.IsSymbolFont() ) + nSearchType |= IMPL_FONT_ATTR_SYMBOL; + } + + PhysicalFontFamily::CalcType( nSearchType, eSearchWeight, eSearchWidth, rFSD.GetFamilyType(), pFontAttr ); + PhysicalFontFamily* pFoundData = ImplFindByAttributes( nSearchType, + eSearchWeight, eSearchWidth, rFSD.GetSlant(), aSearchFamilyName ); + + if( pFoundData ) + { + // overwrite font selection attributes using info from the typeface flags + if( (eSearchWeight >= WEIGHT_BOLD) && + (eSearchWeight > rFSD.GetWeight()) && + (pFoundData->GetTypeFaces() & FONT_FAMILY_BOLD) ) + { + rFSD.SetWeight( eSearchWeight ); + } + else if( (eSearchWeight < WEIGHT_NORMAL) && + (eSearchWeight < rFSD.GetWeight()) && + (eSearchWeight != WEIGHT_DONTKNOW) && + (pFoundData->GetTypeFaces() & FONT_FAMILY_LIGHT) ) + { + rFSD.SetWeight( eSearchWeight ); + } + + if( (nSearchType & IMPL_FONT_ATTR_ITALIC) && + ((rFSD.GetSlant() == ITALIC_DONTKNOW) || + (rFSD.GetSlant() == ITALIC_NONE)) && + (pFoundData->GetTypeFaces() & FONT_FAMILY_ITALIC) ) + { + rFSD.SetItalic( ITALIC_NORMAL ); + } + } + else + { + // if still needed fall back to default fonts + pFoundData = FindDefaultFont(); + } + + return pFoundData; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ + diff --git a/vcl/source/font/PhysicalFontFamily.cxx b/vcl/source/font/PhysicalFontFamily.cxx new file mode 100644 index 000000000000..f0f33071b1b3 --- /dev/null +++ b/vcl/source/font/PhysicalFontFamily.cxx @@ -0,0 +1,298 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/types.h> + +#include <rtl/ustring.hxx> + +#include "outdev.h" +#include "PhysicalFontFace.hxx" +#include "PhysicalFontCollection.hxx" + +#include "PhysicalFontFamily.hxx" + +void PhysicalFontFamily::CalcType( sal_uLong& rType, FontWeight& rWeight, FontWidth& rWidth, + FontFamily eFamily, const utl::FontNameAttr* pFontAttr ) +{ + if ( eFamily != FAMILY_DONTKNOW ) + { + if ( eFamily == FAMILY_SWISS ) + rType |= IMPL_FONT_ATTR_SANSSERIF; + else if ( eFamily == FAMILY_ROMAN ) + rType |= IMPL_FONT_ATTR_SERIF; + else if ( eFamily == FAMILY_SCRIPT ) + rType |= IMPL_FONT_ATTR_SCRIPT; + else if ( eFamily == FAMILY_MODERN ) + rType |= IMPL_FONT_ATTR_FIXED; + else if ( eFamily == FAMILY_DECORATIVE ) + rType |= IMPL_FONT_ATTR_DECORATIVE; + } + + if ( pFontAttr ) + { + rType |= pFontAttr->Type; + + if ( ((rWeight == WEIGHT_DONTKNOW) || (rWeight == WEIGHT_NORMAL)) && + (pFontAttr->Weight != WEIGHT_DONTKNOW) ) + rWeight = pFontAttr->Weight; + if ( ((rWidth == WIDTH_DONTKNOW) || (rWidth == WIDTH_NORMAL)) && + (pFontAttr->Width != WIDTH_DONTKNOW) ) + rWidth = pFontAttr->Width; + } +} + +static unsigned lcl_IsCJKFont( const OUString& rFontName ) +{ + // Test, if Fontname includes CJK characters --> In this case we + // mention that it is a CJK font + for(int i = 0; i < rFontName.getLength(); i++) + { + const sal_Unicode ch = rFontName[i]; + // japanese + if ( ((ch >= 0x3040) && (ch <= 0x30FF)) || + ((ch >= 0x3190) && (ch <= 0x319F)) ) + return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_JP; + + // korean + if ( ((ch >= 0xAC00) && (ch <= 0xD7AF)) || + ((ch >= 0x3130) && (ch <= 0x318F)) || + ((ch >= 0x1100) && (ch <= 0x11FF)) ) + return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_KR; + + // chinese + if ( ((ch >= 0x3400) && (ch <= 0x9FFF)) ) + return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_TC|IMPL_FONT_ATTR_CJK_SC; + + // cjk + if ( ((ch >= 0x3000) && (ch <= 0xD7AF)) || + ((ch >= 0xFF00) && (ch <= 0xFFEE)) ) + return IMPL_FONT_ATTR_CJK; + + } + + return 0; +} + + +PhysicalFontFamily::PhysicalFontFamily( const OUString& rSearchName ) +: mpFirst( NULL ), + maSearchName( rSearchName ), + mnTypeFaces( 0 ), + mnMatchType( 0 ), + meMatchWeight( WEIGHT_DONTKNOW ), + meMatchWidth( WIDTH_DONTKNOW ), + meFamily( FAMILY_DONTKNOW ), + mePitch( PITCH_DONTKNOW ), + mnMinQuality( -1 ) +{} + +PhysicalFontFamily::~PhysicalFontFamily() +{ + // release all physical font faces + while( mpFirst ) + { + PhysicalFontFace* pFace = mpFirst; + mpFirst = pFace->GetNextFace(); + delete pFace; + } +} + +bool PhysicalFontFamily::AddFontFace( PhysicalFontFace* pNewData ) +{ + pNewData->mpNext = NULL; + + if( !mpFirst ) + { + maName = pNewData->GetFamilyName(); + maMapNames = pNewData->maMapNames; + meFamily = pNewData->GetFamilyType(); + mePitch = pNewData->GetPitch(); + mnMinQuality = pNewData->mnQuality; + } + else + { + if( meFamily == FAMILY_DONTKNOW ) + meFamily = pNewData->GetFamilyType(); + if( mePitch == PITCH_DONTKNOW ) + mePitch = pNewData->GetPitch(); + if( mnMinQuality > pNewData->mnQuality ) + mnMinQuality = pNewData->mnQuality; + } + + // set attributes for attribute based font matching + if( pNewData->IsScalable() ) + mnTypeFaces |= FONT_FAMILY_SCALABLE; + + if( pNewData->IsSymbolFont() ) + mnTypeFaces |= FONT_FAMILY_SYMBOL; + else + mnTypeFaces |= FONT_FAMILY_NONESYMBOL; + + if( pNewData->GetWeight() != WEIGHT_DONTKNOW ) + { + if( pNewData->GetWeight() >= WEIGHT_SEMIBOLD ) + mnTypeFaces |= FONT_FAMILY_BOLD; + else if( pNewData->GetWeight() <= WEIGHT_SEMILIGHT ) + mnTypeFaces |= FONT_FAMILY_LIGHT; + else + mnTypeFaces |= FONT_FAMILY_NORMAL; + } + + if( pNewData->GetSlant() == ITALIC_NONE ) + mnTypeFaces |= FONT_FAMILY_NONEITALIC; + else if( (pNewData->GetSlant() == ITALIC_NORMAL) + || (pNewData->GetSlant() == ITALIC_OBLIQUE) ) + mnTypeFaces |= FONT_FAMILY_ITALIC; + + if( (meMatchWeight == WEIGHT_DONTKNOW) + || (meMatchWidth == WIDTH_DONTKNOW) + || (mnMatchType == 0) ) + { + // TODO: is it cheaper to calc matching attributes now or on demand? + // calc matching attributes if other entries are already initialized + + // Do lazy, quite expensive, not needed in start-up! + // const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get(); + // InitMatchData( rFontSubst, maSearchName ); + // mbMatchData=true; // Somewhere else??? + } + + // reassign name (sharing saves memory) + if( pNewData->GetFamilyName() == GetFamilyName() ) + pNewData->SetFamilyName( GetFamilyName() ); + + // insert new physical font face into linked list + // TODO: get rid of linear search? + PhysicalFontFace* pData; + PhysicalFontFace** ppHere = &mpFirst; + for(; (pData=*ppHere) != NULL; ppHere=&pData->mpNext ) + { + sal_Int32 eComp = pNewData->CompareWithSize( *pData ); + if( eComp > 0 ) + continue; + if( eComp < 0 ) + break; + + // ignore duplicate if its quality is worse + if( pNewData->mnQuality < pData->mnQuality ) + return false; + + // keep the device font if its quality is good enough + if( (pNewData->mnQuality == pData->mnQuality) + && (pData->mbDevice || !pNewData->mbDevice) ) + return false; + + // replace existing font face with a better one + pNewData->mpNext = pData->mpNext; + *ppHere = pNewData; + delete pData; + return true; + } + + // insert into or append to list of physical font faces + pNewData->mpNext = pData; + *ppHere = pNewData; + return true; +} + +// get font attributes using the normalized font family name +void PhysicalFontFamily::InitMatchData( const utl::FontSubstConfiguration& rFontSubst, + const OUString& rSearchName ) +{ + OUString aShortName; + OUString aMatchFamilyName(maMatchFamilyName); + // get font attributes from the decorated font name + rFontSubst.getMapName( rSearchName, aShortName, aMatchFamilyName, + meMatchWeight, meMatchWidth, mnMatchType ); + maMatchFamilyName = aMatchFamilyName; + const utl::FontNameAttr* pFontAttr = rFontSubst.getSubstInfo( rSearchName ); + // eventually use the stripped name + if( !pFontAttr ) + if( aShortName != rSearchName ) + pFontAttr = rFontSubst.getSubstInfo( aShortName ); + CalcType( mnMatchType, meMatchWeight, meMatchWidth, meFamily, pFontAttr ); + mnMatchType |= lcl_IsCJKFont( maName ); +} + +PhysicalFontFace* PhysicalFontFamily::FindBestFontFace( const FontSelectPattern& rFSD ) const +{ + if( !mpFirst ) + return NULL; + if( !mpFirst->GetNextFace() ) + return mpFirst; + + // FontName+StyleName should map to FamilyName+StyleName + const OUString& rSearchName = rFSD.maTargetName; + OUString aTargetStyleName; + const OUString* pTargetStyleName = NULL; + if( (rSearchName.getLength() > maSearchName.getLength()) + && rSearchName.startsWith( maSearchName ) ) + { + aTargetStyleName = rSearchName.copy(maSearchName.getLength() + 1); + pTargetStyleName = &aTargetStyleName; + } + + // TODO: linear search improve! + PhysicalFontFace* pFontFace = mpFirst; + PhysicalFontFace* pBestFontFace = pFontFace; + FontMatchStatus aFontMatchStatus = {0,0,0, pTargetStyleName}; + for(; pFontFace; pFontFace = pFontFace->GetNextFace() ) + if( pFontFace->IsBetterMatch( rFSD, aFontMatchStatus ) ) + pBestFontFace = pFontFace; + + return pBestFontFace; +} + +// update device font list with unique font faces, with uniqueness +// meaning different font attributes, but not different fonts sizes +void PhysicalFontFamily::UpdateDevFontList( ImplGetDevFontList& rDevFontList ) const +{ + PhysicalFontFace* pPrevFace = NULL; + for( PhysicalFontFace* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() ) + { + if( !pPrevFace || pFace->CompareIgnoreSize( *pPrevFace ) ) + rDevFontList.Add( pFace ); + pPrevFace = pFace; + } +} + +void PhysicalFontFamily::GetFontHeights( std::set<int>& rHeights ) const +{ + // add all available font heights + for( const PhysicalFontFace* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() ) + rHeights.insert( pFace->GetHeight() ); +} + +void PhysicalFontFamily::UpdateCloneFontList( PhysicalFontCollection& rFontCollection, + bool bScalable, bool bEmbeddable ) const +{ + for( PhysicalFontFace* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() ) + { + if( bScalable && !pFace->IsScalable() ) + continue; + if( bEmbeddable && !pFace->IsEmbeddable() && !pFace->IsSubsettable() ) + continue; + + PhysicalFontFace* pClonedFace = pFace->Clone(); + rFontCollection.Add( pClonedFace ); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |