1279 lines
47 KiB
C++
1279 lines
47 KiB
C++
/* -*- 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/config.h>
|
|
|
|
#include <memory>
|
|
|
|
#include <i18nlangtag/languagetag.hxx>
|
|
#include <i18nlangtag/mslangid.hxx>
|
|
#include <comphelper/configuration.hxx>
|
|
#include <unotools/fontdefs.hxx>
|
|
#include <o3tl/sorted_vector.hxx>
|
|
|
|
#include <font/PhysicalFontFaceCollection.hxx>
|
|
#include <font/PhysicalFontCollection.hxx>
|
|
#include <font/fontsubstitution.hxx>
|
|
|
|
static ImplFontAttrs lcl_IsCJKFont( std::u16string_view rFontName )
|
|
{
|
|
// Test, if Fontname includes CJK characters --> In this case we
|
|
// mention that it is a CJK font
|
|
for(size_t i = 0; i < rFontName.size(); i++)
|
|
{
|
|
const sal_Unicode ch = rFontName[i];
|
|
// japanese
|
|
if ( ((ch >= 0x3040) && (ch <= 0x30FF)) ||
|
|
((ch >= 0x3190) && (ch <= 0x319F)) )
|
|
return ImplFontAttrs::CJK|ImplFontAttrs::CJK_JP;
|
|
|
|
// korean
|
|
if ( ((ch >= 0xAC00) && (ch <= 0xD7AF)) ||
|
|
((ch >= 0xA960) && (ch <= 0xA97F)) ||
|
|
((ch >= 0xD7B0) && (ch <= 0xD7FF)) ||
|
|
((ch >= 0x3130) && (ch <= 0x318F)) ||
|
|
((ch >= 0x1100) && (ch <= 0x11FF)) )
|
|
return ImplFontAttrs::CJK|ImplFontAttrs::CJK_KR;
|
|
|
|
// chinese
|
|
if ( (ch >= 0x3400) && (ch <= 0x9FFF) )
|
|
return ImplFontAttrs::CJK|ImplFontAttrs::CJK_TC|ImplFontAttrs::CJK_SC;
|
|
|
|
// cjk
|
|
if ( ((ch >= 0x3000) && (ch <= 0xD7AF)) ||
|
|
((ch >= 0xFF00) && (ch <= 0xFFEE)) )
|
|
return ImplFontAttrs::CJK;
|
|
|
|
}
|
|
|
|
return ImplFontAttrs::None;
|
|
}
|
|
|
|
namespace vcl::font
|
|
{
|
|
|
|
PhysicalFontCollection::PhysicalFontCollection()
|
|
: mbMatchData( false )
|
|
, mpPreMatchHook( nullptr )
|
|
, mpFallbackHook( nullptr )
|
|
, mnFallbackCount( -1 )
|
|
{}
|
|
|
|
PhysicalFontCollection::~PhysicalFontCollection()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void PhysicalFontCollection::SetPreMatchHook(PreMatchFontSubstitution* pHook)
|
|
{
|
|
mpPreMatchHook = pHook;
|
|
}
|
|
|
|
void PhysicalFontCollection::SetFallbackHook(GlyphFallbackFontSubstitution* pHook)
|
|
{
|
|
mpFallbackHook = pHook;
|
|
}
|
|
|
|
void PhysicalFontCollection::Clear()
|
|
{
|
|
// remove fallback lists
|
|
mpFallbackList.reset();
|
|
mnFallbackCount = -1;
|
|
|
|
// clear all entries in the device font list
|
|
maPhysicalFontFamilies.clear();
|
|
|
|
// match data must be recalculated too
|
|
mbMatchData = false;
|
|
}
|
|
|
|
void PhysicalFontCollection::ImplInitGenericGlyphFallback() 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", "",
|
|
nullptr
|
|
};
|
|
|
|
bool bHasEudc = false;
|
|
int nMaxLevel = 0;
|
|
int nBestQuality = 0;
|
|
std::unique_ptr<std::array<PhysicalFontFamily*,MAX_GLYPHFALLBACK>> pFallbackList;
|
|
|
|
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_GLYPHFALLBACK )
|
|
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;
|
|
|
|
// 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.reset(new std::array<PhysicalFontFamily*,MAX_GLYPHFALLBACK>);
|
|
|
|
(*pFallbackList)[ nMaxLevel ] = pFallbackFont;
|
|
if( !bHasEudc && !nMaxLevel )
|
|
bHasEudc = !strncmp( *ppNames, "eudc", 5 );
|
|
}
|
|
}
|
|
|
|
mnFallbackCount = nMaxLevel;
|
|
mpFallbackList = std::move(pFallbackList);
|
|
}
|
|
|
|
PhysicalFontFamily* PhysicalFontCollection::GetGlyphFallbackFont(FontSelectPattern& rFontSelData,
|
|
LogicalFontInstance* pFontInstance,
|
|
OUString& rMissingCodes,
|
|
int nFallbackLevel) const
|
|
{
|
|
PhysicalFontFamily* pFallbackData = nullptr;
|
|
|
|
// 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 = pFontInstance->GetFallbackForUnicode(cChar, rFontSelData.GetWeight(),
|
|
&rFontSelData.maSearchName,
|
|
&rFontSelData.mbEmbolden,
|
|
&rFontSelData.maItalicMatrix);
|
|
|
|
// 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;
|
|
std::unique_ptr<sal_UCS4[]> const pRemainingCodes(new sal_UCS4[rMissingCodes.getLength()]);
|
|
OUString aFontName;
|
|
bool bEmbolden;
|
|
ItalicMatrix aMatrix;
|
|
|
|
while( nStrIndex < rMissingCodes.getLength() )
|
|
{
|
|
cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
|
|
bCached = pFontInstance->GetFallbackForUnicode(cChar, rFontSelData.GetWeight(),
|
|
&aFontName, &bEmbolden, &aMatrix);
|
|
if (!bCached || rFontSelData.maSearchName != aFontName ||
|
|
rFontSelData.mbEmbolden != bEmbolden ||
|
|
rFontSelData.maItalicMatrix != aMatrix)
|
|
{
|
|
pRemainingCodes[ nRemainingLength++ ] = cChar;
|
|
}
|
|
}
|
|
rMissingCodes = OUString( pRemainingCodes.get(), nRemainingLength );
|
|
}
|
|
else
|
|
{
|
|
OUString aOldMissingCodes = rMissingCodes;
|
|
|
|
// call the hook to query the best matching glyph fallback font
|
|
if (mpFallbackHook->FindFontSubstitute(rFontSelData, pFontInstance, rMissingCodes))
|
|
// apply outdev3.cxx specific fontname normalization
|
|
rFontSelData.maSearchName = GetEnglishSearchFontName( rFontSelData.maSearchName );
|
|
else
|
|
rFontSelData.maSearchName.clear();
|
|
|
|
// Cache the result even if there was no match
|
|
// See tdf#32665 and tdf#147283 for an example where FreeSerif that has glyphs that exist
|
|
// in the bold font, but not in the bold+italic version where fontconfig suggest the bold
|
|
// font + applying a matrix to fake the missing italic.
|
|
for(;;)
|
|
{
|
|
if (!pFontInstance->GetFallbackForUnicode(cChar, rFontSelData.GetWeight(),
|
|
&rFontSelData.maSearchName,
|
|
&rFontSelData.mbEmbolden,
|
|
&rFontSelData.maItalicMatrix))
|
|
{
|
|
pFontInstance->AddFallbackForUnicode(cChar, rFontSelData.GetWeight(),
|
|
rFontSelData.maSearchName,
|
|
rFontSelData.mbEmbolden,
|
|
rFontSelData.maItalicMatrix);
|
|
}
|
|
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 );
|
|
pFontInstance->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 )
|
|
ImplInitGenericGlyphFallback();
|
|
|
|
// 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 = GetEnglishSearchFontName( pNewData->GetFamilyName() );
|
|
|
|
PhysicalFontFamily* pFoundData = FindOrCreateFontFamily(aSearchName);
|
|
|
|
pFoundData->AddFontFace( pNewData );
|
|
}
|
|
|
|
// find the font from the normalized font family name
|
|
PhysicalFontFamily* PhysicalFontCollection::ImplFindFontFamilyBySearchName(const OUString& rSearchName) const
|
|
{
|
|
// must be called with a normalized name.
|
|
assert( GetEnglishSearchFontName( rSearchName ) == rSearchName );
|
|
|
|
PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.find( rSearchName );
|
|
if( it == maPhysicalFontFamilies.end() )
|
|
return nullptr;
|
|
|
|
PhysicalFontFamily* pFoundData = (*it).second.get();
|
|
return pFoundData;
|
|
}
|
|
|
|
PhysicalFontFamily* PhysicalFontCollection::FindFontFamily(std::u16string_view rFontName) const
|
|
{
|
|
return ImplFindFontFamilyBySearchName( GetEnglishSearchFontName( rFontName ) );
|
|
}
|
|
|
|
PhysicalFontFamily *PhysicalFontCollection::FindOrCreateFontFamily(OUString const& rFamilyName)
|
|
{
|
|
PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.find( rFamilyName );
|
|
PhysicalFontFamily* pFoundData = nullptr;
|
|
|
|
if( it != maPhysicalFontFamilies.end() )
|
|
pFoundData = (*it).second.get();
|
|
|
|
if( !pFoundData )
|
|
{
|
|
pFoundData = new PhysicalFontFamily(rFamilyName);
|
|
maPhysicalFontFamilies[ rFamilyName ].reset(pFoundData);
|
|
}
|
|
|
|
return pFoundData;
|
|
}
|
|
|
|
PhysicalFontFamily* PhysicalFontCollection::FindFontFamilyByTokenNames(std::u16string_view rTokenStr) const
|
|
{
|
|
PhysicalFontFamily* pFoundData = nullptr;
|
|
|
|
// use normalized font name tokens to find the font
|
|
for( sal_Int32 nTokenPos = 0; nTokenPos != -1; )
|
|
{
|
|
std::u16string_view aFamilyName = GetNextFontToken( rTokenStr, nTokenPos );
|
|
if( aFamilyName.empty() )
|
|
continue;
|
|
|
|
pFoundData = FindFontFamily( aFamilyName );
|
|
|
|
if( pFoundData )
|
|
break;
|
|
}
|
|
|
|
return pFoundData;
|
|
}
|
|
|
|
PhysicalFontFamily* PhysicalFontCollection::ImplFindFontFamilyBySubstFontAttr(utl::FontNameAttr const& rFontAttr) const
|
|
{
|
|
PhysicalFontFamily* pFoundData = nullptr;
|
|
|
|
// use the font substitutions suggested by the FontNameAttr to find the font
|
|
for (auto const& substitution : rFontAttr.Substitutions)
|
|
{
|
|
pFoundData = FindFontFamily(substitution);
|
|
if( pFoundData )
|
|
return pFoundData;
|
|
}
|
|
|
|
// use known attributes from the configuration to find a matching substitute
|
|
const ImplFontAttrs nSearchType = rFontAttr.Type;
|
|
if( nSearchType != ImplFontAttrs::None )
|
|
{
|
|
const FontWeight eSearchWeight = rFontAttr.Weight;
|
|
const FontWidth eSearchWidth = rFontAttr.Width;
|
|
const FontItalic eSearchSlant = ITALIC_DONTKNOW;
|
|
|
|
pFoundData = FindFontFamilyByAttributes( nSearchType,
|
|
eSearchWeight, eSearchWidth, eSearchSlant, u"" );
|
|
|
|
if( pFoundData )
|
|
return pFoundData;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void PhysicalFontCollection::ImplInitMatchData() const
|
|
{
|
|
// short circuit if already done
|
|
if( mbMatchData )
|
|
return;
|
|
mbMatchData = true;
|
|
|
|
if (comphelper::IsFuzzing())
|
|
return;
|
|
|
|
// calculate MatchData for all entries
|
|
const utl::FontSubstConfiguration& rFontSubst = utl::FontSubstConfiguration::get();
|
|
|
|
for (auto const& family : maPhysicalFontFamilies)
|
|
{
|
|
const OUString& rSearchName = family.first;
|
|
PhysicalFontFamily* pEntry = family.second.get();
|
|
|
|
pEntry->InitMatchData( rFontSubst, rSearchName );
|
|
}
|
|
}
|
|
|
|
PhysicalFontFamily* PhysicalFontCollection::FindFontFamilyByAttributes(ImplFontAttrs nSearchType,
|
|
FontWeight eSearchWeight,
|
|
FontWidth eSearchWidth,
|
|
FontItalic eSearchItalic,
|
|
std::u16string_view rSearchFamilyName ) const
|
|
{
|
|
if( (eSearchItalic != ITALIC_NONE) && (eSearchItalic != ITALIC_DONTKNOW) )
|
|
nSearchType |= ImplFontAttrs::Italic;
|
|
|
|
// don't bother to match attributes if the attributes aren't worth matching
|
|
if( nSearchType == ImplFontAttrs::None
|
|
&& ((eSearchWeight == WEIGHT_DONTKNOW) || (eSearchWeight == WEIGHT_NORMAL))
|
|
&& ((eSearchWidth == WIDTH_DONTKNOW) || (eSearchWidth == WIDTH_NORMAL)) )
|
|
return nullptr;
|
|
|
|
ImplInitMatchData();
|
|
PhysicalFontFamily* pFoundData = nullptr;
|
|
|
|
tools::Long nBestMatch = 40000;
|
|
ImplFontAttrs nBestType = ImplFontAttrs::None;
|
|
|
|
for (auto const& family : maPhysicalFontFamilies)
|
|
{
|
|
PhysicalFontFamily* pData = family.second.get();
|
|
|
|
// Get all information about the matching font
|
|
ImplFontAttrs 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
|
|
tools::Long nTestMatch = 0;
|
|
|
|
// test CJK script attributes
|
|
if ( nSearchType & ImplFontAttrs::CJK )
|
|
{
|
|
// if the matching font doesn't support any CJK languages, then
|
|
// it is not appropriate
|
|
if ( !(nMatchType & ImplFontAttrs::CJK_AllLang) )
|
|
{
|
|
nTestMatch -= 10000000;
|
|
}
|
|
else
|
|
{
|
|
// Matching language
|
|
if ( (nSearchType & ImplFontAttrs::CJK_AllLang)
|
|
&& (nMatchType & ImplFontAttrs::CJK_AllLang) )
|
|
nTestMatch += 10000000*3;
|
|
if ( nMatchType & ImplFontAttrs::CJK )
|
|
nTestMatch += 10000000*2;
|
|
if ( nMatchType & ImplFontAttrs::Full )
|
|
nTestMatch += 10000000;
|
|
}
|
|
}
|
|
else if ( nMatchType & ImplFontAttrs::CJK )
|
|
{
|
|
nTestMatch -= 10000000;
|
|
}
|
|
|
|
// test CTL script attributes
|
|
if( nSearchType & ImplFontAttrs::CTL )
|
|
{
|
|
if( nMatchType & ImplFontAttrs::CTL )
|
|
nTestMatch += 10000000*2;
|
|
if( nMatchType & ImplFontAttrs::Full )
|
|
nTestMatch += 10000000;
|
|
}
|
|
else if ( nMatchType & ImplFontAttrs::CTL )
|
|
{
|
|
nTestMatch -= 10000000;
|
|
}
|
|
|
|
// test LATIN script attributes
|
|
if( nSearchType & ImplFontAttrs::NoneLatin )
|
|
{
|
|
if( nMatchType & ImplFontAttrs::NoneLatin )
|
|
nTestMatch += 10000000*2;
|
|
if( nMatchType & ImplFontAttrs::Full )
|
|
nTestMatch += 10000000;
|
|
}
|
|
|
|
// test SYMBOL attributes
|
|
if ( nSearchType & ImplFontAttrs::Symbol )
|
|
{
|
|
const OUString& rSearchName = family.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() & FontTypeFaces::Symbol)
|
|
{
|
|
nTestMatch += 10000000*4;
|
|
}
|
|
else
|
|
{
|
|
if( nMatchType & ImplFontAttrs::Symbol )
|
|
nTestMatch += 10000000*2;
|
|
if( nMatchType & ImplFontAttrs::Full )
|
|
nTestMatch += 10000000;
|
|
}
|
|
}
|
|
else if ((pData->GetTypeFaces() & (FontTypeFaces::Symbol | FontTypeFaces::NoneSymbol)) == FontTypeFaces::Symbol)
|
|
{
|
|
nTestMatch -= 10000000;
|
|
}
|
|
else if ( nMatchType & ImplFontAttrs::Symbol )
|
|
{
|
|
nTestMatch -= 10000;
|
|
}
|
|
|
|
// match stripped family name
|
|
if( !rSearchFamilyName.empty() && (rSearchFamilyName == pData->GetMatchFamilyName()) )
|
|
{
|
|
nTestMatch += 1000000*3;
|
|
}
|
|
|
|
// match ALLSCRIPT? attribute
|
|
if( nSearchType & ImplFontAttrs::AllScript )
|
|
{
|
|
if( nMatchType & ImplFontAttrs::AllScript )
|
|
{
|
|
nTestMatch += 1000000*2;
|
|
}
|
|
if( nSearchType & ImplFontAttrs::AllSubscript )
|
|
{
|
|
if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::AllSubscript) )
|
|
nTestMatch += 1000000*2;
|
|
if( ImplFontAttrs::None != ((nSearchType ^ nMatchType) & ImplFontAttrs::BrushScript) )
|
|
nTestMatch -= 1000000;
|
|
}
|
|
}
|
|
else if( nMatchType & ImplFontAttrs::AllScript )
|
|
{
|
|
nTestMatch -= 1000000;
|
|
}
|
|
|
|
// test MONOSPACE+TYPEWRITER attributes
|
|
if( nSearchType & ImplFontAttrs::Fixed )
|
|
{
|
|
if( nMatchType & ImplFontAttrs::Fixed )
|
|
nTestMatch += 1000000*2;
|
|
// a typewriter attribute is even better
|
|
if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::Typewriter) )
|
|
nTestMatch += 10000*2;
|
|
}
|
|
else if( nMatchType & ImplFontAttrs::Fixed )
|
|
{
|
|
nTestMatch -= 1000000;
|
|
}
|
|
|
|
// test SPECIAL attribute
|
|
if( nSearchType & ImplFontAttrs::Special )
|
|
{
|
|
if( nMatchType & ImplFontAttrs::Special )
|
|
{
|
|
nTestMatch += 10000;
|
|
}
|
|
else if( !(nSearchType & ImplFontAttrs::AllSerifStyle) )
|
|
{
|
|
if( nMatchType & ImplFontAttrs::Serif )
|
|
{
|
|
nTestMatch += 1000*2;
|
|
}
|
|
else if( nMatchType & ImplFontAttrs::SansSerif )
|
|
{
|
|
nTestMatch += 1000;
|
|
}
|
|
}
|
|
}
|
|
else if( (nMatchType & ImplFontAttrs::Special) && !(nSearchType & ImplFontAttrs::Symbol) )
|
|
{
|
|
nTestMatch -= 1000000;
|
|
}
|
|
|
|
// test DECORATIVE attribute
|
|
if( nSearchType & ImplFontAttrs::Decorative )
|
|
{
|
|
if( nMatchType & ImplFontAttrs::Decorative )
|
|
{
|
|
nTestMatch += 10000;
|
|
}
|
|
else if( !(nSearchType & ImplFontAttrs::AllSerifStyle) )
|
|
{
|
|
if( nMatchType & ImplFontAttrs::Serif )
|
|
nTestMatch += 1000*2;
|
|
else if ( nMatchType & ImplFontAttrs::SansSerif )
|
|
nTestMatch += 1000;
|
|
}
|
|
}
|
|
else if( nMatchType & ImplFontAttrs::Decorative )
|
|
{
|
|
nTestMatch -= 1000000;
|
|
}
|
|
|
|
// test TITLE+CAPITALS attributes
|
|
if( nSearchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals) )
|
|
{
|
|
if( nMatchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals) )
|
|
{
|
|
nTestMatch += 1000000*2;
|
|
}
|
|
if( ImplFontAttrs::None == ((nSearchType^nMatchType) & ImplFontAttrs(ImplFontAttrs::Titling | ImplFontAttrs::Capitals)))
|
|
{
|
|
nTestMatch += 1000000;
|
|
}
|
|
else if( (nMatchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals)) &&
|
|
(nMatchType & (ImplFontAttrs::Standard | ImplFontAttrs::Default)) )
|
|
{
|
|
nTestMatch += 1000000;
|
|
}
|
|
}
|
|
else if( nMatchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals) )
|
|
{
|
|
nTestMatch -= 1000000;
|
|
}
|
|
|
|
// test OUTLINE+SHADOW attributes
|
|
if( nSearchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow) )
|
|
{
|
|
if( nMatchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow) )
|
|
{
|
|
nTestMatch += 1000000*2;
|
|
}
|
|
if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs(ImplFontAttrs::Outline | ImplFontAttrs::Shadow)) )
|
|
{
|
|
nTestMatch += 1000000;
|
|
}
|
|
else if( (nMatchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow)) &&
|
|
(nMatchType & (ImplFontAttrs::Standard | ImplFontAttrs::Default)) )
|
|
{
|
|
nTestMatch += 1000000;
|
|
}
|
|
}
|
|
else if ( nMatchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow) )
|
|
{
|
|
nTestMatch -= 1000000;
|
|
}
|
|
|
|
// test font name substrings
|
|
// TODO: calculate name matching score using e.g. Levenstein distance
|
|
if( (rSearchFamilyName.size() >= 4) &&
|
|
(pData->GetMatchFamilyName().getLength() >= 4) &&
|
|
((rSearchFamilyName.find( pData->GetMatchFamilyName() ) != std::u16string_view::npos) ||
|
|
(pData->GetMatchFamilyName().indexOf( rSearchFamilyName ) != -1)) )
|
|
{
|
|
nTestMatch += 5000;
|
|
}
|
|
// test SERIF attribute
|
|
if( nSearchType & ImplFontAttrs::Serif )
|
|
{
|
|
if( nMatchType & ImplFontAttrs::Serif )
|
|
nTestMatch += 1000000*2;
|
|
else if( nMatchType & ImplFontAttrs::SansSerif )
|
|
nTestMatch -= 1000000;
|
|
}
|
|
|
|
// test SANSERIF attribute
|
|
if( nSearchType & ImplFontAttrs::SansSerif )
|
|
{
|
|
if( nMatchType & ImplFontAttrs::SansSerif )
|
|
nTestMatch += 1000000;
|
|
else if ( nMatchType & ImplFontAttrs::Serif )
|
|
nTestMatch -= 1000000;
|
|
}
|
|
|
|
// test ITALIC attribute
|
|
if( nSearchType & ImplFontAttrs::Italic )
|
|
{
|
|
if (pData->GetTypeFaces() & FontTypeFaces::Italic)
|
|
nTestMatch += 1000000*3;
|
|
if( nMatchType & ImplFontAttrs::Italic )
|
|
nTestMatch += 1000000;
|
|
}
|
|
else if (!(nSearchType & ImplFontAttrs::AllScript)
|
|
&& ((nMatchType & ImplFontAttrs::Italic)
|
|
|| !(pData->GetTypeFaces() & FontTypeFaces::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() & FontTypeFaces::Light)
|
|
nTestMatch += 1000000;
|
|
if( (eMatchWeight < WEIGHT_NORMAL) && (eMatchWeight != WEIGHT_DONTKNOW) )
|
|
nTestMatch += 1000000;
|
|
}
|
|
else
|
|
{
|
|
if (pData->GetTypeFaces() & FontTypeFaces::Bold)
|
|
nTestMatch += 1000000;
|
|
if( eMatchWeight > WEIGHT_BOLD )
|
|
nTestMatch += 1000000;
|
|
}
|
|
}
|
|
else if (((eMatchWeight != WEIGHT_DONTKNOW)
|
|
&& (eMatchWeight != WEIGHT_NORMAL)
|
|
&& (eMatchWeight != WEIGHT_MEDIUM))
|
|
|| !(pData->GetTypeFaces() & FontTypeFaces::Normal))
|
|
{
|
|
nTestMatch -= 1000000;
|
|
}
|
|
|
|
// prefer scalable fonts
|
|
if (pData->GetTypeFaces() & FontTypeFaces::Scalable)
|
|
nTestMatch += 10000*4;
|
|
else
|
|
nTestMatch -= 10000*4;
|
|
|
|
// test STANDARD+DEFAULT+FULL+NORMAL attributes
|
|
if( nMatchType & ImplFontAttrs::Standard )
|
|
nTestMatch += 10000*2;
|
|
if( nMatchType & ImplFontAttrs::Default )
|
|
nTestMatch += 10000;
|
|
if( nMatchType & ImplFontAttrs::Full )
|
|
nTestMatch += 10000;
|
|
if( nMatchType & ImplFontAttrs::Normal )
|
|
nTestMatch += 10000;
|
|
|
|
// test OTHERSTYLE attribute
|
|
if( ((nSearchType ^ nMatchType) & ImplFontAttrs::OtherStyle) != ImplFontAttrs::None )
|
|
{
|
|
nTestMatch -= 10000;
|
|
}
|
|
|
|
// test ROUNDED attribute
|
|
if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::Rounded) )
|
|
nTestMatch += 1000;
|
|
|
|
// test TYPEWRITER attribute
|
|
if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::Typewriter) )
|
|
nTestMatch += 1000;
|
|
|
|
// test GOTHIC attribute
|
|
if( nSearchType & ImplFontAttrs::Gothic )
|
|
{
|
|
if( nMatchType & ImplFontAttrs::Gothic )
|
|
nTestMatch += 1000*3;
|
|
if( nMatchType & ImplFontAttrs::SansSerif )
|
|
nTestMatch += 1000*2;
|
|
}
|
|
|
|
// test SCHOOLBOOK attribute
|
|
if( nSearchType & ImplFontAttrs::Schoolbook )
|
|
{
|
|
if( nMatchType & ImplFontAttrs::Schoolbook )
|
|
nTestMatch += 1000*3;
|
|
if( nMatchType & ImplFontAttrs::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 & ImplFontAttrs::Default )
|
|
{
|
|
pFoundData = pData;
|
|
nBestType = nMatchType;
|
|
}
|
|
else if( (nMatchType & ImplFontAttrs::Standard) &&
|
|
!(nBestType & ImplFontAttrs::Default) )
|
|
{
|
|
pFoundData = pData;
|
|
nBestType = nMatchType;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pFoundData;
|
|
}
|
|
|
|
PhysicalFontFamily* PhysicalFontCollection::ImplFindFontFamilyOfDefaultFont() const
|
|
{
|
|
// try to find one of the default fonts of the
|
|
// UNICODE, SANSSERIF, SERIF or FIXED default font lists
|
|
PhysicalFontFamily* pFoundData = nullptr;
|
|
if (!comphelper::IsFuzzing())
|
|
{
|
|
const utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get();
|
|
LanguageTag aLanguageTag(u"en"_ustr);
|
|
OUString aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::SANS_UNICODE );
|
|
pFoundData = FindFontFamilyByTokenNames( aFontname );
|
|
|
|
if( pFoundData )
|
|
return pFoundData;
|
|
|
|
aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::SANS );
|
|
pFoundData = FindFontFamilyByTokenNames( aFontname );
|
|
if( pFoundData )
|
|
return pFoundData;
|
|
|
|
aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::SERIF );
|
|
pFoundData = FindFontFamilyByTokenNames( aFontname );
|
|
if( pFoundData )
|
|
return pFoundData;
|
|
|
|
aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::FIXED );
|
|
pFoundData = FindFontFamilyByTokenNames( aFontname );
|
|
if( pFoundData )
|
|
return pFoundData;
|
|
}
|
|
|
|
// now try to find a reasonable non-symbol font
|
|
|
|
ImplInitMatchData();
|
|
|
|
for (auto const& family : maPhysicalFontFamilies)
|
|
{
|
|
PhysicalFontFamily* pData = family.second.get();
|
|
if( pData->GetMatchType() & ImplFontAttrs::Symbol )
|
|
continue;
|
|
|
|
pFoundData = pData;
|
|
if( pData->GetMatchType() & (ImplFontAttrs::Default|ImplFontAttrs::Standard) )
|
|
break;
|
|
}
|
|
if( pFoundData )
|
|
return pFoundData;
|
|
|
|
// finding any font is better than finding no font at all
|
|
auto it = maPhysicalFontFamilies.begin();
|
|
if( it != maPhysicalFontFamilies.end() )
|
|
pFoundData = (*it).second.get();
|
|
|
|
return pFoundData;
|
|
}
|
|
|
|
std::shared_ptr<PhysicalFontCollection> PhysicalFontCollection::Clone() const
|
|
{
|
|
auto xClonedCollection = std::make_shared<PhysicalFontCollection>();
|
|
xClonedCollection->mpPreMatchHook = mpPreMatchHook;
|
|
xClonedCollection->mpFallbackHook = mpFallbackHook;
|
|
|
|
// TODO: clone the config-font attributes too?
|
|
xClonedCollection->mbMatchData = false;
|
|
|
|
for (auto const& family : maPhysicalFontFamilies)
|
|
{
|
|
const PhysicalFontFamily* pFontFace = family.second.get();
|
|
pFontFace->UpdateCloneFontList(*xClonedCollection);
|
|
}
|
|
|
|
return xClonedCollection;
|
|
}
|
|
|
|
std::unique_ptr<PhysicalFontFaceCollection> PhysicalFontCollection::GetFontFaceCollection() const
|
|
{
|
|
std::unique_ptr<PhysicalFontFaceCollection> pDeviceFontList(new PhysicalFontFaceCollection);
|
|
|
|
for (auto const& family : maPhysicalFontFamilies)
|
|
{
|
|
const PhysicalFontFamily* pFontFamily = family.second.get();
|
|
pFontFamily->UpdateDevFontList( *pDeviceFontList );
|
|
}
|
|
|
|
return pDeviceFontList;
|
|
}
|
|
|
|
// These are the metric-compatible replacement fonts that are bundled with
|
|
// LibreOffice, we prefer them over generic substitutions that might be
|
|
// provided by the system.
|
|
const std::vector<std::pair<OUString, OUString>> aMetricCompatibleMap =
|
|
{
|
|
{ "Times New Roman", "Liberation Serif" },
|
|
{ "Arial", "Liberation Sans" },
|
|
{ "Arial Narrow", "Liberation Sans Narrow" },
|
|
{ "Courier New", "Liberation Mono" },
|
|
{ "Cambria", "Caladea" },
|
|
{ "Calibri", "Carlito" },
|
|
};
|
|
|
|
static bool FindMetricCompatibleFont(FontSelectPattern& rFontSelData)
|
|
{
|
|
for (const auto& aSub : aMetricCompatibleMap)
|
|
{
|
|
if (rFontSelData.maSearchName == GetEnglishSearchFontName(aSub.first))
|
|
{
|
|
rFontSelData.maSearchName = aSub.second;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
PhysicalFontFamily* PhysicalFontCollection::FindFontFamily(FontSelectPattern& rFSD) const
|
|
{
|
|
// give up if no fonts are available
|
|
if( !Count() )
|
|
return nullptr;
|
|
|
|
static bool noFontLookup = getenv("SAL_NO_FONT_LOOKUP") != nullptr;
|
|
if (noFontLookup)
|
|
{
|
|
// Hard code the use of Liberation Sans and skip font search.
|
|
sal_Int32 nIndex = 0;
|
|
rFSD.maTargetName = GetNextFontToken(rFSD.GetFamilyName(), nIndex);
|
|
rFSD.maSearchName = "liberationsans";
|
|
PhysicalFontFamily* pFont = ImplFindFontFamilyBySearchName(rFSD.maSearchName);
|
|
assert(pFont);
|
|
return pFont;
|
|
}
|
|
|
|
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;
|
|
|
|
// 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(FontSelectPattern::FEAT_PREFIX);
|
|
OUString aOrigName = rFSD.maTargetName;
|
|
OUString aBaseFontName = aSearchName.copy( 0, (nFeat != -1) ? nFeat : aSearchName.getLength() );
|
|
|
|
if (nFeat != -1)
|
|
{
|
|
aSearchName = aBaseFontName;
|
|
rFSD.maTargetName = aBaseFontName;
|
|
}
|
|
|
|
aSearchName = 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() && ImplFindFontFamilyBySearchName( aBoldName ) )
|
|
{
|
|
// the other font is available => use it
|
|
aSearchName = aBoldName;
|
|
// prevent synthetic emboldening of bold version
|
|
rFSD.SetWeight(WEIGHT_DONTKNOW);
|
|
}
|
|
}
|
|
|
|
// restore the features to make the font selection data unique
|
|
rFSD.maTargetName = aOrigName;
|
|
|
|
// check if the current font name token or its substitute is valid
|
|
PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySearchName(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"
|
|
|
|
// use the target name to search in the prematch hook
|
|
rFSD.maTargetName = aBaseFontName;
|
|
|
|
// 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 = StripScriptFromName(rFSD.maTargetName);
|
|
if (sStrippedName != rFSD.maTargetName)
|
|
{
|
|
rFSD.maTargetName = sStrippedName;
|
|
aSearchName = GetEnglishSearchFontName(rFSD.maTargetName);
|
|
pFoundData = ImplFindFontFamilyBySearchName(aSearchName);
|
|
if( pFoundData )
|
|
return pFoundData;
|
|
}
|
|
|
|
if (FindMetricCompatibleFont(rFSD) ||
|
|
(mpPreMatchHook && mpPreMatchHook->FindFontSubstitute(rFSD)))
|
|
{
|
|
aSearchName = GetEnglishSearchFontName(aSearchName);
|
|
}
|
|
|
|
// 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;
|
|
|
|
pFoundData = ImplFindFontFamilyBySearchName( 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 with 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 = GetEnglishSearchFontName( rFSD.maTargetName );
|
|
}
|
|
else
|
|
nTokenPos = -1;
|
|
if (FindMetricCompatibleFont(rFSD) ||
|
|
(mpPreMatchHook && mpPreMatchHook->FindFontSubstitute(rFSD)))
|
|
{
|
|
aSearchName = GetEnglishSearchFontName( aSearchName );
|
|
}
|
|
ImplFontSubstitute(aSearchName);
|
|
PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySearchName(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 = GetEnglishSearchFontName( rFSD.maTargetName );
|
|
}
|
|
|
|
OUString aSearchShortName;
|
|
OUString aSearchFamilyName;
|
|
FontWeight eSearchWeight = rFSD.GetWeight();
|
|
FontWidth eSearchWidth = rFSD.GetWidthType();
|
|
ImplFontAttrs nSearchType = ImplFontAttrs::None;
|
|
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 = ImplFindFontFamilyBySearchName(aSearchShortName);
|
|
if( pFoundData )
|
|
{
|
|
#ifdef UNX
|
|
/* #96738# don't use mincho as a 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 */
|
|
if ((aSearchName != "msmincho") && (aSearchName != "msgothic"))
|
|
// TODO: add heuristic to only throw out the fake ms* fonts
|
|
#endif
|
|
{
|
|
return pFoundData;
|
|
}
|
|
}
|
|
}
|
|
|
|
// use font fallback
|
|
const utl::FontNameAttr* pFontAttr = nullptr;
|
|
if (!aSearchName.isEmpty() && !comphelper::IsFuzzing())
|
|
{
|
|
// 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 = ImplFindFontFamilyBySubstFontAttr(*pFontAttr);
|
|
if( pFoundData )
|
|
return pFoundData;
|
|
}
|
|
}
|
|
|
|
// if a target symbol font is not available use a default symbol font
|
|
if( rFSD.IsMicrosoftSymbolEncoded() )
|
|
{
|
|
LanguageTag aDefaultLanguageTag(u"en"_ustr);
|
|
if (comphelper::IsFuzzing())
|
|
aSearchName = "OpenSymbol";
|
|
else
|
|
aSearchName = utl::DefaultFontConfiguration::get().getDefaultFont( aDefaultLanguageTag, DefaultFontType::SYMBOL );
|
|
PhysicalFontFamily* pFoundData = FindFontFamilyByTokenNames(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 = GetEnglishSearchFontName( rFSD.maTargetName );
|
|
|
|
OUString aTempShortName;
|
|
OUString aTempFamilyName;
|
|
ImplFontAttrs nTempType = ImplFontAttrs::None;
|
|
FontWeight eTempWeight = rFSD.GetWeight();
|
|
FontWidth eTempWidth = WIDTH_DONTKNOW;
|
|
utl::FontSubstConfiguration::getMapName( aSearchName, aTempShortName, aTempFamilyName,
|
|
eTempWeight, eTempWidth, nTempType );
|
|
|
|
// use a shortened token name if available
|
|
if( aTempShortName != aSearchName )
|
|
{
|
|
PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySearchName(aTempShortName);
|
|
if( pFoundData )
|
|
return pFoundData;
|
|
}
|
|
|
|
const utl::FontNameAttr* pTempFontAttr = nullptr;
|
|
if (!comphelper::IsFuzzing())
|
|
{
|
|
// 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();
|
|
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 = ImplFindFontFamilyBySubstFontAttr(*pTempFontAttr);
|
|
if( pFoundData )
|
|
return pFoundData;
|
|
if( !pFontAttr )
|
|
pFontAttr = pTempFontAttr;
|
|
}
|
|
}
|
|
|
|
// if still needed use the font request's attributes to find a good match
|
|
if (MsLangId::isSimplifiedChinese(rFSD.meLanguage))
|
|
nSearchType |= ImplFontAttrs::CJK | ImplFontAttrs::CJK_SC;
|
|
else if (MsLangId::isTraditionalChinese(rFSD.meLanguage))
|
|
nSearchType |= ImplFontAttrs::CJK | ImplFontAttrs::CJK_TC;
|
|
else if (MsLangId::isKorean(rFSD.meLanguage))
|
|
nSearchType |= ImplFontAttrs::CJK | ImplFontAttrs::CJK_KR;
|
|
else if (rFSD.meLanguage == LANGUAGE_JAPANESE)
|
|
nSearchType |= ImplFontAttrs::CJK | ImplFontAttrs::CJK_JP;
|
|
else
|
|
{
|
|
nSearchType |= lcl_IsCJKFont( rFSD.GetFamilyName() );
|
|
if( rFSD.IsMicrosoftSymbolEncoded() )
|
|
nSearchType |= ImplFontAttrs::Symbol;
|
|
}
|
|
|
|
PhysicalFontFamily::CalcType(nSearchType, eSearchWeight, eSearchWidth, rFSD.GetFamilyType(), pFontAttr);
|
|
PhysicalFontFamily* pFoundData = FindFontFamilyByAttributes(nSearchType,
|
|
eSearchWeight, eSearchWidth, rFSD.GetItalic(), aSearchFamilyName);
|
|
|
|
if( pFoundData )
|
|
{
|
|
// overwrite font selection attributes using info from the typeface flags
|
|
if ((eSearchWeight >= WEIGHT_BOLD)
|
|
&& (eSearchWeight > rFSD.GetWeight())
|
|
&& (pFoundData->GetTypeFaces() & FontTypeFaces::Bold))
|
|
{
|
|
rFSD.SetWeight( eSearchWeight );
|
|
}
|
|
else if ((eSearchWeight < WEIGHT_NORMAL)
|
|
&& (eSearchWeight < rFSD.GetWeight())
|
|
&& (eSearchWeight != WEIGHT_DONTKNOW)
|
|
&& (pFoundData->GetTypeFaces() & FontTypeFaces::Light))
|
|
{
|
|
rFSD.SetWeight( eSearchWeight );
|
|
}
|
|
|
|
if ((nSearchType & ImplFontAttrs::Italic)
|
|
&& ((rFSD.GetItalic() == ITALIC_DONTKNOW)
|
|
|| (rFSD.GetItalic() == ITALIC_NONE))
|
|
&& (pFoundData->GetTypeFaces() & FontTypeFaces::Italic))
|
|
{
|
|
rFSD.SetItalic( ITALIC_NORMAL );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if still needed fall back to default fonts
|
|
pFoundData = ImplFindFontFamilyOfDefaultFont();
|
|
}
|
|
|
|
return pFoundData;
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|