/* -*- 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 .
*/
// platform specific font substitution hooks for glyph fallback enhancement
namespace {
class WinPreMatchFontSubstititution
: public vcl::font::PreMatchFontSubstitution
{ public: bool FindFontSubstitute(vcl::font::FontSelectPattern&) const override;
};
class WinGlyphFallbackSubstititution
: public vcl::font::GlyphFallbackFontSubstitution
{ public: bool FindFontSubstitute(vcl::font::FontSelectPattern&, LogicalFontInstance* pLogicalFont, OUString& rMissingChars) const override;
};
// does a font face hold the given missing characters? bool HasMissingChars(vcl::font::PhysicalFontFace* pFace, OUString& rMissingChars)
{
FontCharMapRef xFontCharMap = pFace->GetFontCharMap();
// avoid fonts with unknown CMAP subtables for glyph fallback if( !xFontCharMap.is() || xFontCharMap->IsDefaultMap() ) returnfalse;
if (nMatchCount > 0)
rMissingChars = OUString(rRemainingCodes.data(), rRemainingCodes.size());
return nMatchCount > 0;
}
//used by 2-level font fallback
vcl::font::PhysicalFontFamily* findDevFontListByLocale(const vcl::font::PhysicalFontCollection &rFontCollection, const LanguageTag& rLanguageTag )
{ // get the default font for a specified locale const utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get(); const OUString aDefault = rDefaults.getUserInterfaceFont(rLanguageTag); return rFontCollection.FindFontFamilyByTokenNames(aDefault);
}
}
// These are Win 3.1 bitmap fonts using "FON" font format // which is not supported with DirectWrite so let's substitute them // with a font that is supported and always available. // Based on: // https://dxr.mozilla.org/mozilla-esr10/source/gfx/thebes/gfxDWriteFontList.cpp#1057 const std::map<OUString, OUString> aBitmapFontSubs =
{
{ "MS Sans Serif", "Microsoft Sans Serif" },
{ "MS Serif", "Times New Roman" },
{ "Small Fonts", "Arial" },
{ "Courier", "Courier New" },
{ "Roman", "Times New Roman" },
{ "Script", "Mistral" }
};
// TODO: See if Windows have API that we can use here to improve font fallback. bool WinPreMatchFontSubstititution::FindFontSubstitute(vcl::font::FontSelectPattern& rFontSelData) const
{ if (rFontSelData.IsMicrosoftSymbolEncoded() || IsOpenSymbol(rFontSelData.maSearchName)) returnfalse;
for (constauto& aSub : aBitmapFontSubs)
{ if (rFontSelData.maSearchName == GetEnglishSearchFontName(aSub.first))
{
rFontSelData.maSearchName = aSub.second; returntrue;
}
}
returnfalse;
}
// find a fallback font for missing characters // TODO: should stylistic matches be searched and preferred? bool WinGlyphFallbackSubstititution::FindFontSubstitute(vcl::font::FontSelectPattern& rFontSelData, LogicalFontInstance* /*pLogicalFont*/, OUString& rMissingChars) const
{ // guess a locale matching to the missing chars
LanguageType eLang = rFontSelData.meLanguage;
LanguageTag aLanguageTag( eLang);
// fall back to default UI locale if the font language is inconclusive if( eLang == LANGUAGE_DONTKNOW )
aLanguageTag = Application::GetSettings().GetUILanguageTag();
// first level fallback: // try use the locale specific default fonts defined in VCL.xcu const vcl::font::PhysicalFontCollection* pFontCollection = ImplGetSVData()->maGDIData.mxScreenFontList.get();
vcl::font::PhysicalFontFamily* pFontFamily = findDevFontListByLocale(*pFontCollection, aLanguageTag); if( pFontFamily )
{
vcl::font::PhysicalFontFace* pFace = pFontFamily->FindBestFontFace( rFontSelData ); if( HasMissingChars( pFace, rMissingChars ) )
{
rFontSelData.maSearchName = pFontFamily->GetSearchName(); returntrue;
}
}
// last level fallback, check each font type face one by one
std::unique_ptr<vcl::font::PhysicalFontFaceCollection> pTestFontList = pFontCollection->GetFontFaceCollection(); // limit the count of fonts to be checked to prevent hangs staticconstint MAX_GFBFONT_COUNT = 600; int nTestFontCount = pTestFontList->Count(); if( nTestFontCount > MAX_GFBFONT_COUNT )
nTestFontCount = MAX_GFBFONT_COUNT;
bool bFound = false; for( int i = 0; i < nTestFontCount; ++i )
{
vcl::font::PhysicalFontFace* pFace = pTestFontList->Get( i );
bFound = HasMissingChars( pFace, rMissingChars ); if( !bFound ) continue;
rFontSelData.maSearchName = pFace->GetFamilyName(); break;
}
// keep default font if( !mhDefFont )
mhDefFont = hOldFont; else
{ // release no longer referenced font handles for( int i = nFallbackLevel + 1; i < MAX_FALLBACK && mpWinFontEntry[i].is(); ++i )
mpWinFontEntry[i] = nullptr;
}
}
void WinSalGraphics::GetFontMetric( FontMetricDataRef& rxFontMetric, int nFallbackLevel )
{ // temporarily change the HDC to the font in the fallback level
rtl::Reference<WinFontInstance> pFontInstance = mpWinFontEntry[nFallbackLevel]; const HFONT hOldFont = SelectFont(getHDC(), pFontInstance->GetHFONT());
// get the font metric
OUTLINETEXTMETRICW aOutlineMetric; constbool bOK = GetOutlineTextMetricsW(getHDC(), sizeof(aOutlineMetric), &aOutlineMetric); // restore the HDC to the font in the base level
SelectFont( getHDC(), hOldFont ); if( !bOK ) return;
// add the font to the PhysicalFontCollection
EnumFontFamiliesExW(getHDC(), &aLogFont,
SalEnumFontsProcExW, reinterpret_cast<LPARAM>(&aInfo), 0);
SAL_WARN_IF(nExpectedFontCount != pFontCollection->Count(), "vcl.fonts", "temp font was registered but is not in enumeration: " << rFontFileURL);
returntrue;
}
void WinSalGraphics::GetDevFontList( vcl::font::PhysicalFontCollection* pFontCollection )
{ // make sure all LO shared fonts are registered temporarily static std::once_flag init;
std::call_once(init, []()
{ auto registerFontsIn = [](const OUString& dir) { // collect fonts in font path that could not be registered
osl::Directory aFontDir(dir);
osl::FileBase::RC rcOSL = aFontDir.open(); if (rcOSL == osl::FileBase::E_None)
{
osl::DirectoryItem aDirItem;
SalData* pSalData = GetSalData();
assert(pSalData);
while (aFontDir.getNextItem(aDirItem, 10) == osl::FileBase::E_None)
{
osl::FileStatus aFileStatus(osl_FileStatus_Mask_FileURL);
rcOSL = aDirItem.getFileStatus(aFileStatus); if (rcOSL == osl::FileBase::E_None)
lcl_AddFontResource(*pSalData, aFileStatus.getFileURL());
}
}
};
// determine font path // since we are only interested in fonts that could not be // registered before because of missing administration rights // only the font path of the user installation is needed
OUString aPath("$BRAND_BASE_DIR");
rtl_bootstrap_expandMacros(&aPath.pData);
// internal font resources, required for normal operation, like OpenSymbol
registerFontsIn(aPath + "/" LIBO_SHARE_RESOURCE_FOLDER "/common/fonts");
// collect fonts in font path that could not be registered
registerFontsIn(aPath + "/" LIBO_SHARE_FOLDER "/fonts/truetype");
// calculate endpoint of segment
nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
if ( i+1 >= pCurve->cpfx )
{ // endpoint is either last point in segment => advance
++i;
} else
{ // or endpoint is the middle of two control points
nX += IntTimes256FromFixed( pCurve->apfx[ i-1 ].x );
nY += IntTimes256FromFixed( pCurve->apfx[ i-1 ].y );
nX = (nX + 1) / 2;
nY = (nY + 1) / 2; // no need to advance, because the current point // is the control point in next bezier spline
}
// next curve segment
pCurve = reinterpret_cast<TTPOLYCURVE*>(&pCurve->apfx[ i ]);
}
// end point is start point for closed contour // disabled, because Polygon class closes the contour itself // pPoints[nPnt++] = pPoints[0]; // #i35928# // Added again, but add only when not yet closed if(pPoints[nPnt - 1] != pPoints[0])
{ if( bHasOfflinePoints )
pFlags[nPnt] = pFlags[0];
pPoints[nPnt++] = pPoints[0];
}
// convert y-coordinates W32 -> VCL for( int i = 0; i < nPnt; ++i )
pPoints[i].setY(-pPoints[i].Y());
// insert into polypolygon
tools::Polygon aPoly( nPnt, pPoints, (bHasOfflinePoints ? pFlags : nullptr) ); // convert to B2DPolyPolygon // TODO: get rid of the intermediate PolyPolygon
rB2DPolyPoly.append( aPoly.getB2DPolygon() );
}
delete[] pPoints; delete[] pFlags;
delete[] pData;
// rescaling needed for the tools::PolyPolygon conversion if( rB2DPolyPoly.count() )
{ constdouble fFactor(1.0f/256);
rB2DPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix(fFactor, fFactor));
}
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.