/* -*- 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 .
*/
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;
// 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 staticconstchar* 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( constchar** 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>);
// 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;
// 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 ];
}
// 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;
// use the font substitutions suggested by the FontNameAttr to find the font for (autoconst& 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;
for (autoconst& 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();
// finding any font is better than finding no font at all auto it = maPhysicalFontFamilies.begin(); if( it != maPhysicalFontFamilies.end() )
pFoundData = (*it).second.get();
for (autoconst& 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" },
};
staticbool FindMetricCompatibleFont(FontSelectPattern& rFontSelData)
{ for (constauto& aSub : aMetricCompatibleMap)
{ if (rFontSelData.maSearchName == GetEnglishSearchFontName(aSub.first))
{
rFontSelData.maSearchName = aSub.second; returntrue;
}
}
returnfalse;
}
PhysicalFontFamily* PhysicalFontCollection::FindFontFamily(FontSelectPattern& rFSD) const
{ // give up if no fonts are available if( !Count() ) return nullptr;
staticbool 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;
}
// 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( !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;
}
// 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;
// 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 );
}
// 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;
// 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 );
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.