summaryrefslogtreecommitdiffstats
path: root/vcl/source/outdev/font.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /vcl/source/outdev/font.cxx
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/source/outdev/font.cxx')
-rw-r--r--vcl/source/outdev/font.cxx1293
1 files changed, 1293 insertions, 0 deletions
diff --git a/vcl/source/outdev/font.cxx b/vcl/source/outdev/font.cxx
new file mode 100644
index 0000000000..2086db7f63
--- /dev/null
+++ b/vcl/source/outdev/font.cxx
@@ -0,0 +1,1293 @@
+/* -*- 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 <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <i18nlangtag/lang.h>
+#include <unotools/configmgr.hxx>
+
+#include <vcl/event.hxx>
+#include <vcl/fontcharmap.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/print.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/virdev.hxx>
+
+#include <window.h>
+#include <font/EmphasisMark.hxx>
+
+#include <ImplLayoutArgs.hxx>
+#include <drawmode.hxx>
+#include <impfontcache.hxx>
+#include <font/DirectFontSubstitution.hxx>
+#include <font/PhysicalFontFaceCollection.hxx>
+#include <font/PhysicalFontCollection.hxx>
+#include <font/FeatureCollector.hxx>
+#include <impglyphitem.hxx>
+#include <sallayout.hxx>
+#include <salgdi.hxx>
+#include <svdata.hxx>
+
+#include <unicode/uchar.h>
+
+#include <strings.hrc>
+
+void OutputDevice::SetFont( const vcl::Font& rNewFont )
+{
+ vcl::Font aFont = vcl::drawmode::GetFont(rNewFont, GetDrawMode(), GetSettings().GetStyleSettings());
+
+ if ( mpMetaFile )
+ {
+ mpMetaFile->AddAction( new MetaFontAction( aFont ) );
+ // the color and alignment actions don't belong here
+ // TODO: get rid of them without breaking anything...
+ mpMetaFile->AddAction( new MetaTextAlignAction( aFont.GetAlignment() ) );
+ mpMetaFile->AddAction( new MetaTextFillColorAction( aFont.GetFillColor(), !aFont.IsTransparent() ) );
+ }
+
+ if ( maFont.IsSameInstance( aFont ) )
+ return;
+
+ // Optimization MT/HDU: COL_TRANSPARENT means SetFont should ignore the font color,
+ // because SetTextColor() is used for this.
+ // #i28759# maTextColor might have been changed behind our back, commit then, too.
+ if( aFont.GetColor() != COL_TRANSPARENT
+ && (aFont.GetColor() != maFont.GetColor() || aFont.GetColor() != maTextColor ) )
+ {
+ maTextColor = aFont.GetColor();
+ mbInitTextColor = true;
+ if( mpMetaFile )
+ mpMetaFile->AddAction( new MetaTextColorAction( aFont.GetColor() ) );
+ }
+ maFont = aFont;
+ mbNewFont = true;
+
+ if( !mpAlphaVDev )
+ return;
+
+ // #i30463#
+ // Since SetFont might change the text color, apply that only
+ // selectively to alpha vdev (which normally paints opaque text
+ // with COL_BLACK)
+ if( aFont.GetColor() != COL_TRANSPARENT )
+ {
+ mpAlphaVDev->SetTextColor( COL_ALPHA_OPAQUE );
+ aFont.SetColor( COL_TRANSPARENT );
+ }
+
+ mpAlphaVDev->SetFont( aFont );
+}
+
+FontMetric OutputDevice::GetFontMetricFromCollection(int nDevFontIndex) const
+{
+ ImplInitFontList();
+
+ if (nDevFontIndex < GetFontFaceCollectionCount())
+ return FontMetric(*mpFontFaceCollection->Get(nDevFontIndex));
+
+ return FontMetric();
+}
+
+int OutputDevice::GetFontFaceCollectionCount() const
+{
+ if( !mpFontFaceCollection )
+ {
+ if (!mxFontCollection)
+ {
+ return 0;
+ }
+
+ mpFontFaceCollection = mxFontCollection->GetFontFaceCollection();
+
+ if (!mpFontFaceCollection->Count())
+ {
+ mpFontFaceCollection.reset();
+ return 0;
+ }
+ }
+ return mpFontFaceCollection->Count();
+}
+
+bool OutputDevice::IsFontAvailable( std::u16string_view rFontName ) const
+{
+ ImplInitFontList();
+ vcl::font::PhysicalFontFamily* pFound = mxFontCollection->FindFontFamily( rFontName );
+ return (pFound != nullptr);
+}
+
+bool OutputDevice::AddTempDevFont( const OUString& rFileURL, const OUString& rFontName )
+{
+ ImplInitFontList();
+
+ if( !mpGraphics && !AcquireGraphics() )
+ return false;
+ assert(mpGraphics);
+
+ bool bRC = mpGraphics->AddTempDevFont( mxFontCollection.get(), rFileURL, rFontName );
+ if( !bRC )
+ return false;
+
+ if( mpAlphaVDev )
+ mpAlphaVDev->AddTempDevFont( rFileURL, rFontName );
+
+ return true;
+}
+
+bool OutputDevice::GetFontFeatures(std::vector<vcl::font::Feature>& rFontFeatures) const
+{
+ if (!ImplNewFont())
+ return false;
+
+ LogicalFontInstance* pFontInstance = mpFontInstance.get();
+ if (!pFontInstance)
+ return false;
+
+ const LanguageTag& rOfficeLanguage = Application::GetSettings().GetUILanguageTag();
+
+ vcl::font::FeatureCollector aFeatureCollector(pFontInstance->GetFontFace(), rFontFeatures, rOfficeLanguage);
+ aFeatureCollector.collect();
+
+ return true;
+}
+
+FontMetric OutputDevice::GetFontMetric() const
+{
+ FontMetric aMetric;
+ if (!ImplNewFont())
+ return aMetric;
+
+ LogicalFontInstance* pFontInstance = mpFontInstance.get();
+ FontMetricDataRef xFontMetric = pFontInstance->mxFontMetric;
+
+ // prepare metric
+ aMetric = maFont;
+
+ // set aMetric with info from font
+ aMetric.SetFamilyName( maFont.GetFamilyName() );
+ aMetric.SetStyleName( xFontMetric->GetStyleName() );
+ aMetric.SetFontSize( PixelToLogic( Size( xFontMetric->GetWidth(), xFontMetric->GetAscent() + xFontMetric->GetDescent() - xFontMetric->GetInternalLeading() ) ) );
+ aMetric.SetCharSet( xFontMetric->IsMicrosoftSymbolEncoded() ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
+ aMetric.SetFamily( xFontMetric->GetFamilyType() );
+ aMetric.SetPitch( xFontMetric->GetPitch() );
+ aMetric.SetWeight( xFontMetric->GetWeight() );
+ aMetric.SetItalic( xFontMetric->GetItalic() );
+ aMetric.SetAlignment( TextAlign::ALIGN_TOP );
+ aMetric.SetWidthType( xFontMetric->GetWidthType() );
+ if ( pFontInstance->mnOwnOrientation )
+ aMetric.SetOrientation( pFontInstance->mnOwnOrientation );
+ else
+ aMetric.SetOrientation( xFontMetric->GetOrientation() );
+
+ // set remaining metric fields
+ aMetric.SetFullstopCenteredFlag( xFontMetric->IsFullstopCentered() );
+ aMetric.SetBulletOffset( xFontMetric->GetBulletOffset() );
+ aMetric.SetAscent( ImplDevicePixelToLogicHeight( xFontMetric->GetAscent() + mnEmphasisAscent ) );
+ aMetric.SetDescent( ImplDevicePixelToLogicHeight( xFontMetric->GetDescent() + mnEmphasisDescent ) );
+ aMetric.SetInternalLeading( ImplDevicePixelToLogicHeight( xFontMetric->GetInternalLeading() + mnEmphasisAscent ) );
+ // OutputDevice has its own external leading function due to #i60945#
+ aMetric.SetExternalLeading( ImplDevicePixelToLogicHeight( GetFontExtLeading() ) );
+ aMetric.SetLineHeight( ImplDevicePixelToLogicHeight( xFontMetric->GetAscent() + xFontMetric->GetDescent() + mnEmphasisAscent + mnEmphasisDescent ) );
+ aMetric.SetSlant( ImplDevicePixelToLogicHeight( xFontMetric->GetSlant() ) );
+ aMetric.SetHangingBaseline( ImplDevicePixelToLogicHeight( xFontMetric->GetHangingBaseline() ) );
+
+ // get miscellaneous data
+ aMetric.SetQuality( xFontMetric->GetQuality() );
+
+ SAL_INFO("vcl.gdi.fontmetric", "OutputDevice::GetFontMetric:" << aMetric);
+
+ xFontMetric = nullptr;
+
+ return aMetric;
+}
+
+FontMetric OutputDevice::GetFontMetric( const vcl::Font& rFont ) const
+{
+ // select font, query metrics, select original font again
+ vcl::Font aOldFont = GetFont();
+ const_cast<OutputDevice*>(this)->SetFont( rFont );
+ FontMetric aMetric( GetFontMetric() );
+ const_cast<OutputDevice*>(this)->SetFont( aOldFont );
+ return aMetric;
+}
+
+bool OutputDevice::GetFontCharMap( FontCharMapRef& rxFontCharMap ) const
+{
+ if (!InitFont())
+ return false;
+
+ FontCharMapRef xFontCharMap ( mpGraphics->GetFontCharMap() );
+ if (!xFontCharMap.is())
+ {
+ FontCharMapRef xDefaultMap( new FontCharMap() );
+ rxFontCharMap = xDefaultMap;
+ }
+ else
+ rxFontCharMap = xFontCharMap;
+
+ return !rxFontCharMap->IsDefaultMap();
+}
+
+bool OutputDevice::GetFontCapabilities( vcl::FontCapabilities& rFontCapabilities ) const
+{
+ if (!InitFont())
+ return false;
+ return mpGraphics->GetFontCapabilities(rFontCapabilities);
+}
+
+tools::Long OutputDevice::GetFontExtLeading() const
+{
+ return mpFontInstance->mxFontMetric->GetExternalLeading();
+}
+
+void OutputDevice::ImplClearFontData( const bool bNewFontLists )
+{
+ // the currently selected logical font is no longer needed
+ mpFontInstance.clear();
+
+ mbInitFont = true;
+ mbNewFont = true;
+
+ if ( bNewFontLists )
+ {
+ mpFontFaceCollection.reset();
+
+ // release all physically selected fonts on this device
+ if( AcquireGraphics() )
+ mpGraphics->ReleaseFonts();
+ }
+
+ ImplSVData* pSVData = ImplGetSVData();
+
+ if (mxFontCache && mxFontCache != pSVData->maGDIData.mxScreenFontCache)
+ mxFontCache->Invalidate();
+
+ if (bNewFontLists && AcquireGraphics())
+ {
+ if (mxFontCollection && mxFontCollection != pSVData->maGDIData.mxScreenFontList)
+ mxFontCollection->Clear();
+ }
+}
+
+void OutputDevice::RefreshFontData( const bool bNewFontLists )
+{
+ ImplRefreshFontData( bNewFontLists );
+}
+
+void OutputDevice::ImplRefreshFontData( const bool bNewFontLists )
+{
+ if (bNewFontLists && AcquireGraphics())
+ mpGraphics->GetDevFontList( mxFontCollection.get() );
+}
+
+void OutputDevice::ImplUpdateFontData()
+{
+ ImplClearFontData( true/*bNewFontLists*/ );
+ ImplRefreshFontData( true/*bNewFontLists*/ );
+}
+
+void OutputDevice::ImplClearAllFontData(bool bNewFontLists)
+{
+ ImplSVData* pSVData = ImplGetSVData();
+
+ ImplUpdateFontDataForAllFrames( &OutputDevice::ImplClearFontData, bNewFontLists );
+
+ // clear global font lists to have them updated
+ pSVData->maGDIData.mxScreenFontCache->Invalidate();
+ if ( !bNewFontLists )
+ return;
+
+ pSVData->maGDIData.mxScreenFontList->Clear();
+ vcl::Window * pFrame = pSVData->maFrameData.mpFirstFrame;
+ if ( pFrame )
+ {
+ if ( pFrame->GetOutDev()->AcquireGraphics() )
+ {
+ OutputDevice *pDevice = pFrame->GetOutDev();
+ pDevice->mpGraphics->ClearDevFontCache();
+ pDevice->mpGraphics->GetDevFontList(pFrame->mpWindowImpl->mpFrameData->mxFontCollection.get());
+ }
+ }
+}
+
+void OutputDevice::ImplRefreshAllFontData(bool bNewFontLists)
+{
+ ImplUpdateFontDataForAllFrames( &OutputDevice::ImplRefreshFontData, bNewFontLists );
+}
+
+void OutputDevice::ImplUpdateAllFontData(bool bNewFontLists)
+{
+ OutputDevice::ImplClearAllFontData(bNewFontLists);
+ OutputDevice::ImplRefreshAllFontData(bNewFontLists);
+}
+
+void OutputDevice::ImplUpdateFontDataForAllFrames( const FontUpdateHandler_t pHdl, const bool bNewFontLists )
+{
+ ImplSVData* const pSVData = ImplGetSVData();
+
+ // update all windows
+ vcl::Window* pFrame = pSVData->maFrameData.mpFirstFrame;
+ while ( pFrame )
+ {
+ ( pFrame->GetOutDev()->*pHdl )( bNewFontLists );
+
+ vcl::Window* pSysWin = pFrame->mpWindowImpl->mpFrameData->mpFirstOverlap;
+ while ( pSysWin )
+ {
+ ( pSysWin->GetOutDev()->*pHdl )( bNewFontLists );
+ pSysWin = pSysWin->mpWindowImpl->mpNextOverlap;
+ }
+
+ pFrame = pFrame->mpWindowImpl->mpFrameData->mpNextFrame;
+ }
+
+ // update all virtual devices
+ VirtualDevice* pVirDev = pSVData->maGDIData.mpFirstVirDev;
+ while ( pVirDev )
+ {
+ ( pVirDev->*pHdl )( bNewFontLists );
+ pVirDev = pVirDev->mpNext;
+ }
+
+ // update all printers
+ Printer* pPrinter = pSVData->maGDIData.mpFirstPrinter;
+ while ( pPrinter )
+ {
+ ( pPrinter->*pHdl )( bNewFontLists );
+ pPrinter = pPrinter->mpNext;
+ }
+}
+
+void OutputDevice::BeginFontSubstitution()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ pSVData->maGDIData.mbFontSubChanged = false;
+}
+
+void OutputDevice::EndFontSubstitution()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ if ( pSVData->maGDIData.mbFontSubChanged )
+ {
+ ImplUpdateAllFontData( false );
+
+ DataChangedEvent aDCEvt( DataChangedEventType::FONTSUBSTITUTION );
+ Application::ImplCallEventListenersApplicationDataChanged(&aDCEvt);
+ Application::NotifyAllWindows( aDCEvt );
+ pSVData->maGDIData.mbFontSubChanged = false;
+ }
+}
+
+void OutputDevice::AddFontSubstitute( const OUString& rFontName,
+ const OUString& rReplaceFontName,
+ AddFontSubstituteFlags nFlags )
+{
+ vcl::font::DirectFontSubstitution*& rpSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
+ if( !rpSubst )
+ rpSubst = new vcl::font::DirectFontSubstitution;
+ rpSubst->AddFontSubstitute( rFontName, rReplaceFontName, nFlags );
+ ImplGetSVData()->maGDIData.mbFontSubChanged = true;
+}
+
+void OutputDevice::RemoveFontsSubstitute()
+{
+ vcl::font::DirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
+ if( pSubst )
+ pSubst->RemoveFontsSubstitute();
+}
+
+//hidpi TODO: This routine has hard-coded font-sizes that break places such as DialControl
+vcl::Font OutputDevice::GetDefaultFont( DefaultFontType nType, LanguageType eLang,
+ GetDefaultFontFlags nFlags, const OutputDevice* pOutDev )
+{
+ static bool bFuzzing = utl::ConfigManager::IsFuzzing();
+ static bool bAbortOnFontSubstitute = [] {
+ const char* pEnv = getenv("SAL_NON_APPLICATION_FONT_USE");
+ return pEnv && strcmp(pEnv, "abort") == 0;
+ }();
+
+ if (!pOutDev && !bFuzzing) // default is NULL
+ pOutDev = Application::GetDefaultDevice();
+
+ OUString aSearch;
+ if (!bFuzzing)
+ {
+ LanguageTag aLanguageTag(
+ ( eLang == LANGUAGE_NONE || eLang == LANGUAGE_SYSTEM || eLang == LANGUAGE_DONTKNOW ) ?
+ Application::GetSettings().GetUILanguageTag() :
+ LanguageTag( eLang ));
+
+ utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get();
+ OUString aDefault = rDefaults.getDefaultFont( aLanguageTag, nType );
+
+ if( !aDefault.isEmpty() )
+ aSearch = aDefault;
+ else
+ aSearch = rDefaults.getUserInterfaceFont( aLanguageTag ); // use the UI font as a fallback
+
+ // during cppunit tests with SAL_NON_APPLICATION_FONT_USE set we don't have any bundled fonts
+ // that support the default CTL and CJK languages of Hindi and Chinese, so just pick something
+ // (unsuitable) that does exist, if they get used with SAL_NON_APPLICATION_FONT_USE=abort then
+ // glyph fallback will trigger std::abort
+ if (bAbortOnFontSubstitute)
+ {
+ if (eLang == LANGUAGE_HINDI || eLang == LANGUAGE_CHINESE_SIMPLIFIED)
+ aSearch = "DejaVu Sans";
+ }
+ }
+ else
+ aSearch = "Liberation Serif";
+
+ vcl::Font aFont;
+ aFont.SetPitch( PITCH_VARIABLE );
+
+ switch ( nType )
+ {
+ case DefaultFontType::SANS_UNICODE:
+ case DefaultFontType::UI_SANS:
+ aFont.SetFamily( FAMILY_SWISS );
+ break;
+
+ case DefaultFontType::SANS:
+ case DefaultFontType::LATIN_HEADING:
+ case DefaultFontType::LATIN_SPREADSHEET:
+ case DefaultFontType::LATIN_DISPLAY:
+ aFont.SetFamily( FAMILY_SWISS );
+ break;
+
+ case DefaultFontType::SERIF:
+ case DefaultFontType::LATIN_TEXT:
+ case DefaultFontType::LATIN_PRESENTATION:
+ aFont.SetFamily( FAMILY_ROMAN );
+ break;
+
+ case DefaultFontType::FIXED:
+ case DefaultFontType::LATIN_FIXED:
+ case DefaultFontType::UI_FIXED:
+ aFont.SetPitch( PITCH_FIXED );
+ aFont.SetFamily( FAMILY_MODERN );
+ break;
+
+ case DefaultFontType::SYMBOL:
+ aFont.SetCharSet( RTL_TEXTENCODING_SYMBOL );
+ break;
+
+ case DefaultFontType::CJK_TEXT:
+ case DefaultFontType::CJK_PRESENTATION:
+ case DefaultFontType::CJK_SPREADSHEET:
+ case DefaultFontType::CJK_HEADING:
+ case DefaultFontType::CJK_DISPLAY:
+ aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later...
+ break;
+
+ case DefaultFontType::CTL_TEXT:
+ case DefaultFontType::CTL_PRESENTATION:
+ case DefaultFontType::CTL_SPREADSHEET:
+ case DefaultFontType::CTL_HEADING:
+ case DefaultFontType::CTL_DISPLAY:
+ aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later...
+ break;
+ }
+
+ if ( !aSearch.isEmpty() )
+ {
+ aFont.SetFontHeight( 12 ); // corresponds to nDefaultHeight
+ aFont.SetWeight( WEIGHT_NORMAL );
+ aFont.SetLanguage( eLang );
+
+ if ( aFont.GetCharSet() == RTL_TEXTENCODING_DONTKNOW )
+ aFont.SetCharSet( osl_getThreadTextEncoding() );
+
+ // Should we only return available fonts on the given device
+ if ( pOutDev )
+ {
+ pOutDev->ImplInitFontList();
+
+ // Search Font in the FontList
+ OUString aName;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ vcl::font::PhysicalFontFamily* pFontFamily = pOutDev->mxFontCollection->FindFontFamily( GetNextFontToken( aSearch, nIndex ) );
+ if( pFontFamily )
+ {
+ AddTokenFontName( aName, pFontFamily->GetFamilyName() );
+ if( nFlags & GetDefaultFontFlags::OnlyOne )
+ break;
+ }
+ }
+ while ( nIndex != -1 );
+ aFont.SetFamilyName( aName );
+ }
+
+ // No Name, then set all names
+ if ( aFont.GetFamilyName().isEmpty() )
+ {
+ if ( nFlags & GetDefaultFontFlags::OnlyOne )
+ {
+ if( !pOutDev )
+ {
+ SAL_WARN_IF(!utl::ConfigManager::IsFuzzing(), "vcl.gdi", "No default window has been set for the application - we really shouldn't be able to get here");
+ aFont.SetFamilyName( aSearch.getToken( 0, ';' ) );
+ }
+ else
+ {
+ pOutDev->ImplInitFontList();
+
+ aFont.SetFamilyName( aSearch );
+
+ // convert to pixel height
+ Size aSize = pOutDev->ImplLogicToDevicePixel( aFont.GetFontSize() );
+ if ( !aSize.Height() )
+ {
+ // use default pixel height only when logical height is zero
+ if ( aFont.GetFontHeight() )
+ aSize.setHeight( 1 );
+ else
+ aSize.setHeight( (12*pOutDev->mnDPIY)/72 );
+ }
+
+ // use default width only when logical width is zero
+ if( (0 == aSize.Width()) && (0 != aFont.GetFontSize().Width()) )
+ aSize.setWidth( 1 );
+
+ // get the name of the first available font
+ float fExactHeight = static_cast<float>(aSize.Height());
+ rtl::Reference<LogicalFontInstance> pFontInstance = pOutDev->mxFontCache->GetFontInstance( pOutDev->mxFontCollection.get(), aFont, aSize, fExactHeight );
+ if (pFontInstance)
+ {
+ assert(pFontInstance->GetFontFace());
+ aFont.SetFamilyName(pFontInstance->GetFontFace()->GetFamilyName());
+ }
+ }
+ }
+ else
+ aFont.SetFamilyName( aSearch );
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 2
+ const char* s = "SANS_UNKNOWN";
+ switch ( nType )
+ {
+ case DefaultFontType::SANS_UNICODE: s = "SANS_UNICODE"; break;
+ case DefaultFontType::UI_SANS: s = "UI_SANS"; break;
+
+ case DefaultFontType::SANS: s = "SANS"; break;
+ case DefaultFontType::LATIN_HEADING: s = "LATIN_HEADING"; break;
+ case DefaultFontType::LATIN_SPREADSHEET: s = "LATIN_SPREADSHEET"; break;
+ case DefaultFontType::LATIN_DISPLAY: s = "LATIN_DISPLAY"; break;
+
+ case DefaultFontType::SERIF: s = "SERIF"; break;
+ case DefaultFontType::LATIN_TEXT: s = "LATIN_TEXT"; break;
+ case DefaultFontType::LATIN_PRESENTATION: s = "LATIN_PRESENTATION"; break;
+
+ case DefaultFontType::FIXED: s = "FIXED"; break;
+ case DefaultFontType::LATIN_FIXED: s = "LATIN_FIXED"; break;
+ case DefaultFontType::UI_FIXED: s = "UI_FIXED"; break;
+
+ case DefaultFontType::SYMBOL: s = "SYMBOL"; break;
+
+ case DefaultFontType::CJK_TEXT: s = "CJK_TEXT"; break;
+ case DefaultFontType::CJK_PRESENTATION: s = "CJK_PRESENTATION"; break;
+ case DefaultFontType::CJK_SPREADSHEET: s = "CJK_SPREADSHEET"; break;
+ case DefaultFontType::CJK_HEADING: s = "CJK_HEADING"; break;
+ case DefaultFontType::CJK_DISPLAY: s = "CJK_DISPLAY"; break;
+
+ case DefaultFontType::CTL_TEXT: s = "CTL_TEXT"; break;
+ case DefaultFontType::CTL_PRESENTATION: s = "CTL_PRESENTATION"; break;
+ case DefaultFontType::CTL_SPREADSHEET: s = "CTL_SPREADSHEET"; break;
+ case DefaultFontType::CTL_HEADING: s = "CTL_HEADING"; break;
+ case DefaultFontType::CTL_DISPLAY: s = "CTL_DISPLAY"; break;
+ }
+ SAL_INFO("vcl.gdi",
+ "OutputDevice::GetDefaultFont() Type=" << s
+ << " lang=" << eLang
+ << " flags=" << static_cast<int>(nFlags)
+ << " family=\"" << aFont.GetFamilyName() << "\"");
+#endif
+
+ return aFont;
+}
+
+void OutputDevice::ImplInitFontList() const
+{
+ if( mxFontCollection->Count() )
+ return;
+
+ if( !(mpGraphics || AcquireGraphics()) )
+ return;
+ assert(mpGraphics);
+
+ SAL_INFO( "vcl.gdi", "OutputDevice::ImplInitFontList()" );
+ mpGraphics->GetDevFontList(mxFontCollection.get());
+
+ // There is absolutely no way there should be no fonts available on the device
+ if( !mxFontCollection->Count() )
+ {
+ OUString aError( "Application error: no fonts and no vcl resource found on your system" );
+ OUString aResStr(VclResId(SV_ACCESSERROR_NO_FONTS));
+ if (!aResStr.isEmpty())
+ aError = aResStr;
+ Application::Abort(aError);
+ }
+}
+
+bool OutputDevice::InitFont() const
+{
+ DBG_TESTSOLARMUTEX();
+
+ if (!ImplNewFont())
+ return false;
+ if (!mpFontInstance)
+ return false;
+ if (!mpGraphics)
+ {
+ if (!AcquireGraphics())
+ return false;
+ }
+ else if (!mbInitFont)
+ return true;
+
+ assert(mpGraphics);
+ mpGraphics->SetFont(mpFontInstance.get(), 0);
+ mbInitFont = false;
+ return true;
+}
+
+const LogicalFontInstance* OutputDevice::GetFontInstance() const
+{
+ if (!InitFont())
+ return nullptr;
+ return mpFontInstance.get();
+}
+
+bool OutputDevice::ImplNewFont() const
+{
+ DBG_TESTSOLARMUTEX();
+
+ if ( !mbNewFont )
+ return true;
+
+ // we need a graphics
+ if ( !mpGraphics && !AcquireGraphics() )
+ {
+ SAL_WARN("vcl.gdi", "OutputDevice::ImplNewFont(): no Graphics, no Font");
+ return false;
+ }
+ assert(mpGraphics);
+
+ ImplInitFontList();
+
+ // convert to pixel height
+ // TODO: replace integer based aSize completely with subpixel accurate type
+ float fExactHeight = ImplLogicHeightToDeviceSubPixel(maFont.GetFontHeight());
+ Size aSize = ImplLogicToDevicePixel( maFont.GetFontSize() );
+ if ( !aSize.Height() )
+ {
+ // use default pixel height only when logical height is zero
+ if ( maFont.GetFontSize().Height() )
+ aSize.setHeight( 1 );
+ else
+ aSize.setHeight( (12*mnDPIY)/72 );
+ fExactHeight = static_cast<float>(aSize.Height());
+ }
+
+ // select the default width only when logical width is zero
+ if( (0 == aSize.Width()) && (0 != maFont.GetFontSize().Width()) )
+ aSize.setWidth( 1 );
+
+ // decide if antialiasing is appropriate
+ bool bNonAntialiased(GetAntialiasing() & AntialiasingFlags::DisableText);
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ bNonAntialiased |= bool(rStyleSettings.GetDisplayOptions() & DisplayOptions::AADisable);
+ bNonAntialiased |= (int(rStyleSettings.GetAntialiasingMinPixelHeight()) > maFont.GetFontSize().Height());
+ }
+
+ // get font entry
+ rtl::Reference<LogicalFontInstance> pOldFontInstance = mpFontInstance;
+ mpFontInstance = mxFontCache->GetFontInstance(mxFontCollection.get(), maFont, aSize, fExactHeight, bNonAntialiased);
+ const bool bNewFontInstance = pOldFontInstance.get() != mpFontInstance.get();
+ pOldFontInstance.clear();
+
+ LogicalFontInstance* pFontInstance = mpFontInstance.get();
+
+ if (!pFontInstance)
+ {
+ SAL_WARN("vcl.gdi", "OutputDevice::ImplNewFont(): no LogicalFontInstance, no Font");
+ return false;
+ }
+
+ // mark when lower layers need to get involved
+ mbNewFont = false;
+ if( bNewFontInstance )
+ mbInitFont = true;
+
+ // select font when it has not been initialized yet
+ if (!pFontInstance->mbInit && InitFont())
+ {
+ // get metric data from device layers
+ pFontInstance->mbInit = true;
+
+ pFontInstance->mxFontMetric->SetOrientation( mpFontInstance->GetFontSelectPattern().mnOrientation );
+ mpGraphics->GetFontMetric( pFontInstance->mxFontMetric, 0 );
+
+ pFontInstance->mxFontMetric->ImplInitTextLineSize( this );
+ pFontInstance->mxFontMetric->ImplInitAboveTextLineSize( this );
+ pFontInstance->mxFontMetric->ImplInitFlags( this );
+
+ pFontInstance->mnLineHeight = pFontInstance->mxFontMetric->GetAscent() + pFontInstance->mxFontMetric->GetDescent();
+
+ SetFontOrientation( pFontInstance );
+ }
+
+ // calculate EmphasisArea
+ mnEmphasisAscent = 0;
+ mnEmphasisDescent = 0;
+ if ( maFont.GetEmphasisMark() & FontEmphasisMark::Style )
+ {
+ FontEmphasisMark nEmphasisMark = maFont.GetEmphasisMarkStyle();
+ tools::Long nEmphasisHeight = (pFontInstance->mnLineHeight*250)/1000;
+ if ( nEmphasisHeight < 1 )
+ nEmphasisHeight = 1;
+ if ( nEmphasisMark & FontEmphasisMark::PosBelow )
+ mnEmphasisDescent = nEmphasisHeight;
+ else
+ mnEmphasisAscent = nEmphasisHeight;
+ }
+
+ // calculate text offset depending on TextAlignment
+ TextAlign eAlign = maFont.GetAlignment();
+ if ( eAlign == ALIGN_BASELINE )
+ {
+ mnTextOffX = 0;
+ mnTextOffY = 0;
+ }
+ else if ( eAlign == ALIGN_TOP )
+ {
+ mnTextOffX = 0;
+ mnTextOffY = +pFontInstance->mxFontMetric->GetAscent() + mnEmphasisAscent;
+ if ( pFontInstance->mnOrientation )
+ {
+ Point aOriginPt(0, 0);
+ aOriginPt.RotateAround( mnTextOffX, mnTextOffY, pFontInstance->mnOrientation );
+ }
+ }
+ else // eAlign == ALIGN_BOTTOM
+ {
+ mnTextOffX = 0;
+ mnTextOffY = -pFontInstance->mxFontMetric->GetDescent() + mnEmphasisDescent;
+ if ( pFontInstance->mnOrientation )
+ {
+ Point aOriginPt(0, 0);
+ aOriginPt.RotateAround( mnTextOffX, mnTextOffY, pFontInstance->mnOrientation );
+ }
+ }
+
+ mbTextLines = ((maFont.GetUnderline() != LINESTYLE_NONE) && (maFont.GetUnderline() != LINESTYLE_DONTKNOW)) ||
+ ((maFont.GetOverline() != LINESTYLE_NONE) && (maFont.GetOverline() != LINESTYLE_DONTKNOW)) ||
+ ((maFont.GetStrikeout() != STRIKEOUT_NONE) && (maFont.GetStrikeout() != STRIKEOUT_DONTKNOW));
+ mbTextSpecial = maFont.IsShadow() || maFont.IsOutline() ||
+ (maFont.GetRelief() != FontRelief::NONE);
+
+
+ bool bRet = true;
+
+ // #95414# fix for OLE objects which use scale factors very creatively
+ if (mbMap && !aSize.Width())
+ bRet = AttemptOLEFontScaleFix(const_cast<vcl::Font&>(maFont), aSize.Height());
+
+ return bRet;
+}
+
+bool OutputDevice::AttemptOLEFontScaleFix(vcl::Font& rFont, tools::Long nHeight) const
+{
+ const float fDenominator = static_cast<float>(maMapRes.mnMapScNumY) * maMapRes.mnMapScDenomX;
+ if (fDenominator == 0.0)
+ return false;
+ const float fNumerator = static_cast<float>(maMapRes.mnMapScNumX) * maMapRes.mnMapScDenomY;
+ float fStretch = fNumerator / fDenominator;
+ int nOrigWidth = mpFontInstance->mxFontMetric->GetWidth();
+ int nNewWidth = static_cast<int>(nOrigWidth * fStretch + 0.5);
+ bool bRet = true;
+ if (nNewWidth != nOrigWidth && nNewWidth != 0)
+ {
+ Size aOrigSize = rFont.GetFontSize();
+ rFont.SetFontSize(Size(nNewWidth, nHeight));
+ mbMap = false;
+ mbNewFont = true;
+ bRet = ImplNewFont(); // recurse once using stretched width
+ mbMap = true;
+ rFont.SetFontSize(aOrigSize);
+ }
+ return bRet;
+}
+
+void OutputDevice::SetFontOrientation( LogicalFontInstance* const pFontInstance ) const
+{
+ if( pFontInstance->GetFontSelectPattern().mnOrientation && !pFontInstance->mxFontMetric->GetOrientation() )
+ {
+ pFontInstance->mnOwnOrientation = pFontInstance->GetFontSelectPattern().mnOrientation;
+ pFontInstance->mnOrientation = pFontInstance->mnOwnOrientation;
+ }
+ else
+ {
+ pFontInstance->mnOrientation = pFontInstance->mxFontMetric->GetOrientation();
+ }
+}
+
+void OutputDevice::ImplDrawEmphasisMark( tools::Long nBaseX, tools::Long nX, tools::Long nY,
+ const tools::PolyPolygon& rPolyPoly, bool bPolyLine,
+ const tools::Rectangle& rRect1, const tools::Rectangle& rRect2 )
+{
+ if( IsRTLEnabled() )
+ nX = nBaseX - (nX - nBaseX - 1);
+
+ nX -= mnOutOffX;
+ nY -= mnOutOffY;
+
+ if ( rPolyPoly.Count() )
+ {
+ if ( bPolyLine )
+ {
+ tools::Polygon aPoly = rPolyPoly.GetObject( 0 );
+ aPoly.Move( nX, nY );
+ DrawPolyLine( aPoly );
+ }
+ else
+ {
+ tools::PolyPolygon aPolyPoly = rPolyPoly;
+ aPolyPoly.Move( nX, nY );
+ DrawPolyPolygon( aPolyPoly );
+ }
+ }
+
+ if ( !rRect1.IsEmpty() )
+ {
+ tools::Rectangle aRect( Point( nX+rRect1.Left(),
+ nY+rRect1.Top() ), rRect1.GetSize() );
+ DrawRect( aRect );
+ }
+
+ if ( !rRect2.IsEmpty() )
+ {
+ tools::Rectangle aRect( Point( nX+rRect2.Left(),
+ nY+rRect2.Top() ), rRect2.GetSize() );
+
+ DrawRect( aRect );
+ }
+}
+
+void OutputDevice::ImplDrawEmphasisMarks( SalLayout& rSalLayout )
+{
+ Color aOldLineColor = GetLineColor();
+ Color aOldFillColor = GetFillColor();
+ bool bOldMap = mbMap;
+ GDIMetaFile* pOldMetaFile = mpMetaFile;
+ mpMetaFile = nullptr;
+ EnableMapMode( false );
+
+ FontEmphasisMark nEmphasisMark = maFont.GetEmphasisMarkStyle();
+ tools::Long nEmphasisHeight;
+
+ if ( nEmphasisMark & FontEmphasisMark::PosBelow )
+ nEmphasisHeight = mnEmphasisDescent;
+ else
+ nEmphasisHeight = mnEmphasisAscent;
+
+ vcl::font::EmphasisMark aEmphasisMark(nEmphasisMark, nEmphasisHeight, GetDPIY());
+
+ if (aEmphasisMark.IsShapePolyLine())
+ {
+ SetLineColor( GetTextColor() );
+ SetFillColor();
+ }
+ else
+ {
+ SetLineColor();
+ SetFillColor( GetTextColor() );
+ }
+
+ Point aOffset(0,0);
+ Point aOffsetVert(0,0);
+
+ if ( nEmphasisMark & FontEmphasisMark::PosBelow )
+ {
+ aOffset.AdjustY(mpFontInstance->mxFontMetric->GetDescent() + aEmphasisMark.GetYOffset());
+ aOffsetVert = aOffset;
+ }
+ else
+ {
+ aOffset.AdjustY(-(mpFontInstance->mxFontMetric->GetAscent() + aEmphasisMark.GetYOffset()));
+ // Todo: use ideographic em-box or ideographic character face information.
+ aOffsetVert.AdjustY(-(mpFontInstance->mxFontMetric->GetAscent() +
+ mpFontInstance->mxFontMetric->GetDescent() + aEmphasisMark.GetYOffset()));
+ }
+
+ tools::Long nEmphasisWidth2 = aEmphasisMark.GetWidth() / 2;
+ tools::Long nEmphasisHeight2 = nEmphasisHeight / 2;
+ aOffset += Point( nEmphasisWidth2, nEmphasisHeight2 );
+
+ basegfx::B2DPoint aOutPoint;
+ tools::Rectangle aRectangle;
+ const GlyphItem* pGlyph;
+ const LogicalFontInstance* pGlyphFont;
+ int nStart = 0;
+ while (rSalLayout.GetNextGlyph(&pGlyph, aOutPoint, nStart, &pGlyphFont))
+ {
+ if (!pGlyph->GetGlyphBoundRect(pGlyphFont, aRectangle))
+ continue;
+
+ if (!pGlyph->IsSpacing())
+ {
+ Point aAdjPoint;
+ if (pGlyph->IsVertical())
+ {
+ aAdjPoint = aOffsetVert;
+ aAdjPoint.AdjustX((-pGlyph->origWidth() + aEmphasisMark.GetWidth()) / 2);
+ }
+ else
+ {
+ aAdjPoint = aOffset;
+ aAdjPoint.AdjustX(aRectangle.Left() + (aRectangle.GetWidth() - aEmphasisMark.GetWidth()) / 2 );
+ }
+
+ if ( mpFontInstance->mnOrientation )
+ {
+ Point aOriginPt(0, 0);
+ aOriginPt.RotateAround( aAdjPoint, mpFontInstance->mnOrientation );
+ }
+ aOutPoint.adjustX(aAdjPoint.X() - nEmphasisWidth2);
+ aOutPoint.adjustY(aAdjPoint.Y() - nEmphasisHeight2);
+ ImplDrawEmphasisMark( rSalLayout.DrawBase().getX(),
+ aOutPoint.getX(), aOutPoint.getY(),
+ aEmphasisMark.GetShape(), aEmphasisMark.IsShapePolyLine(),
+ aEmphasisMark.GetRect1(), aEmphasisMark.GetRect2() );
+ }
+ }
+
+ SetLineColor( aOldLineColor );
+ SetFillColor( aOldFillColor );
+ EnableMapMode( bOldMap );
+ mpMetaFile = pOldMetaFile;
+}
+
+std::unique_ptr<SalLayout> OutputDevice::getFallbackLayout(
+ LogicalFontInstance* pLogicalFont, int nFallbackLevel,
+ vcl::text::ImplLayoutArgs& rLayoutArgs, const SalLayoutGlyphs* pGlyphs) const
+{
+ // we need a graphics
+ if (!mpGraphics && !AcquireGraphics())
+ return nullptr;
+
+ assert(mpGraphics != nullptr);
+ mpGraphics->SetFont( pLogicalFont, nFallbackLevel );
+
+ rLayoutArgs.ResetPos();
+ std::unique_ptr<GenericSalLayout> pFallback = mpGraphics->GetTextLayout(nFallbackLevel);
+
+ if (!pFallback)
+ return nullptr;
+
+ if (!pFallback->LayoutText(rLayoutArgs, pGlyphs ? pGlyphs->Impl(nFallbackLevel) : nullptr))
+ {
+ // there is no need for a font that couldn't resolve anything
+ return nullptr;
+ }
+
+ return pFallback;
+}
+
+bool OutputDevice::ForceFallbackFont(vcl::Font const& rFallbackFont)
+{
+ vcl::Font aOldFont = GetFont();
+ SetFont(rFallbackFont);
+ if (!InitFont())
+ return false;
+
+ mpForcedFallbackInstance = mpFontInstance;
+ SetFont(aOldFont);
+ if (!InitFont())
+ return false;
+
+ if (mpForcedFallbackInstance)
+ return true;
+
+ return false;
+}
+
+std::unique_ptr<SalLayout> OutputDevice::ImplGlyphFallbackLayout( std::unique_ptr<SalLayout> pSalLayout,
+ vcl::text::ImplLayoutArgs& rLayoutArgs, const SalLayoutGlyphs* pGlyphs ) const
+{
+ // This function relies on a valid mpFontInstance, if it doesn't exist bail out
+ // - we'd have crashed later on anyway. At least here we can catch the error in debug
+ // mode.
+ if ( !mpFontInstance )
+ {
+ SAL_WARN ("vcl.gdi", "No font entry set in OutputDevice");
+ assert(mpFontInstance);
+ return nullptr;
+ }
+
+ // prepare multi level glyph fallback
+ std::unique_ptr<MultiSalLayout> pMultiSalLayout;
+ ImplLayoutRuns aLayoutRuns = rLayoutArgs.maRuns;
+ rLayoutArgs.PrepareFallback(nullptr);
+ rLayoutArgs.mnFlags |= SalLayoutFlags::ForFallback;
+
+ // get list of code units that need glyph fallback
+ bool bRTL;
+ int nMinRunPos, nEndRunPos;
+ OUStringBuffer aMissingCodeBuf(512);
+ while (rLayoutArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL))
+ aMissingCodeBuf.append(rLayoutArgs.mrStr.subView(nMinRunPos, nEndRunPos - nMinRunPos));
+ rLayoutArgs.ResetPos();
+ OUString aMissingCodes = aMissingCodeBuf.makeStringAndClear();
+
+ vcl::font::FontSelectPattern aFontSelData(mpFontInstance->GetFontSelectPattern());
+ SalLayoutGlyphsImpl* pGlyphsImpl = pGlyphs ? pGlyphs->Impl(1) : nullptr;
+
+ bool bHasUsedFallback = false;
+
+ // try if fallback fonts support the missing code units
+ for( int nFallbackLevel = 1; nFallbackLevel < MAX_FALLBACK; ++nFallbackLevel )
+ {
+ rtl::Reference<LogicalFontInstance> pFallbackFont;
+ if (!bHasUsedFallback && mpForcedFallbackInstance)
+ {
+ pFallbackFont = mpForcedFallbackInstance;
+ bHasUsedFallback = true;
+ }
+ else if(pGlyphsImpl != nullptr)
+ {
+ pFallbackFont = pGlyphsImpl->GetFont();
+ }
+
+ // find a font family suited for glyph fallback
+ // GetGlyphFallbackFont() needs a valid FontInstance
+ // if the system-specific glyph fallback is active
+ OUString oldMissingCodes = aMissingCodes;
+ if( !pFallbackFont )
+ pFallbackFont = mxFontCache->GetGlyphFallbackFont( mxFontCollection.get(),
+ aFontSelData, mpFontInstance.get(), nFallbackLevel, aMissingCodes );
+ if( !pFallbackFont )
+ break;
+
+ SAL_INFO("vcl", "Fallback font (level " << nFallbackLevel << "): family: " << pFallbackFont->GetFontFace()->GetFamilyName()
+ << ", style: " << pFallbackFont->GetFontFace()->GetStyleName());
+
+ if( nFallbackLevel < MAX_FALLBACK-1)
+ {
+ // ignore fallback font if it is the same as the original font
+ // TODO: This seems broken. Either the font does not provide any of the missing
+ // codes, in which case the fallback should not select it. Or it does provide
+ // some of the missing codes, and then why weren't they used the first time?
+ // This will just loop repeatedly finding the same font (it used to remove
+ // the found font from mxFontCache, but doesn't do that anymore and I don't
+ // see how doing that would remove the font from consideration for fallback).
+ if( mpFontInstance->GetFontFace() == pFallbackFont->GetFontFace())
+ {
+ if(aMissingCodes != oldMissingCodes)
+ {
+ SAL_INFO("vcl.gdi", "Font fallback to the same font, but has missing codes");
+ // Restore the missing codes if we're not going to use this font.
+ aMissingCodes = oldMissingCodes;
+ }
+ continue;
+ }
+ }
+
+ // create and add glyph fallback layout to multilayout
+ std::unique_ptr<SalLayout> pFallback = getFallbackLayout(pFallbackFont.get(),
+ nFallbackLevel, rLayoutArgs, pGlyphs);
+ if (pFallback)
+ {
+ if( !pMultiSalLayout )
+ pMultiSalLayout.reset( new MultiSalLayout( std::move(pSalLayout) ) );
+ pMultiSalLayout->AddFallback(std::move(pFallback), rLayoutArgs.maRuns);
+ if (nFallbackLevel == MAX_FALLBACK-1)
+ pMultiSalLayout->SetIncomplete(true);
+ }
+
+ if (pGlyphs != nullptr)
+ pGlyphsImpl = pGlyphs->Impl(nFallbackLevel + 1);
+
+ // break when this fallback was sufficient
+ if( !rLayoutArgs.PrepareFallback(pGlyphsImpl) )
+ break;
+ }
+
+ if (pMultiSalLayout) // due to missing glyphs, multilevel layout fallback attempted
+ {
+ // if it works, use that Layout
+ if (pMultiSalLayout->LayoutText(rLayoutArgs, nullptr))
+ pSalLayout = std::move(pMultiSalLayout);
+ else
+ {
+ // if it doesn't, give up and restore ownership of the pSalLayout
+ // back to its original state
+ pSalLayout = pMultiSalLayout->ReleaseBaseLayout();
+ }
+ }
+
+ // restore orig font settings
+ pSalLayout->InitFont();
+ rLayoutArgs.maRuns = aLayoutRuns;
+
+ return pSalLayout;
+}
+
+tools::Long OutputDevice::GetMinKashida() const
+{
+ if (!ImplNewFont())
+ return 0;
+
+ auto nKashidaWidth = mpFontInstance->mxFontMetric->GetMinKashida();
+ if (!mbMap)
+ nKashidaWidth = std::ceil(nKashidaWidth);
+
+ return ImplDevicePixelToLogicWidth(nKashidaWidth);
+}
+
+sal_Int32 OutputDevice::ValidateKashidas ( const OUString& rTxt,
+ sal_Int32 nIdx, sal_Int32 nLen,
+ sal_Int32 nKashCount,
+ const sal_Int32* pKashidaPos,
+ sal_Int32* pKashidaPosDropped ) const
+{
+ // do layout
+ std::unique_ptr<SalLayout> pSalLayout = ImplLayout( rTxt, nIdx, nLen );
+ if( !pSalLayout )
+ return 0;
+
+ auto nEnd = nIdx + nLen - 1;
+ sal_Int32 nDropped = 0;
+ for( int i = 0; i < nKashCount; ++i )
+ {
+ auto nPos = pKashidaPos[i];
+ auto nNextPos = nPos + 1;
+
+ // Skip combining marks to find the next character after this position.
+ while (nNextPos <= nEnd &&
+ u_getIntPropertyValue(rTxt[nNextPos], UCHAR_JOINING_TYPE) == U_JT_TRANSPARENT)
+ nNextPos++;
+
+ // The next position is past end of the layout, it would happen if we
+ // changed the text styling in the middle of a word. Since we don’t do
+ // apply OpenType features across different layouts, this can’t be an
+ // invalid place to insert Kashida.
+ if (nNextPos > nEnd)
+ continue;
+
+ if (!pSalLayout->IsKashidaPosValid(nPos, nNextPos))
+ pKashidaPosDropped[nDropped++] = nPos;
+ }
+ return nDropped;
+}
+
+bool OutputDevice::GetGlyphBoundRects( const Point& rOrigin, const OUString& rStr,
+ int nIndex, int nLen, std::vector< tools::Rectangle >& rVector ) const
+{
+ rVector.clear();
+
+ if( nIndex >= rStr.getLength() )
+ return false;
+
+ if( nLen < 0 || nIndex + nLen >= rStr.getLength() )
+ {
+ nLen = rStr.getLength() - nIndex;
+ }
+
+ tools::Rectangle aRect;
+ for( int i = 0; i < nLen; i++ )
+ {
+ if( !GetTextBoundRect( aRect, rStr, nIndex, nIndex + i, 1 ) )
+ break;
+ aRect.Move( rOrigin.X(), rOrigin.Y() );
+ rVector.push_back( aRect );
+ }
+
+ return (nLen == static_cast<int>(rVector.size()));
+}
+
+sal_Int32 OutputDevice::HasGlyphs( const vcl::Font& rTempFont, std::u16string_view rStr,
+ sal_Int32 nIndex, sal_Int32 nLen ) const
+{
+ if( nIndex >= static_cast<sal_Int32>(rStr.size()) )
+ return nIndex;
+ sal_Int32 nEnd;
+ if( nLen == -1 )
+ nEnd = rStr.size();
+ else
+ nEnd = std::min<sal_Int32>( rStr.size(), nIndex + nLen );
+
+ SAL_WARN_IF( nIndex >= nEnd, "vcl.gdi", "StartPos >= EndPos?" );
+ SAL_WARN_IF( nEnd > static_cast<sal_Int32>(rStr.size()), "vcl.gdi", "String too short" );
+
+ // to get the map temporarily set font
+ const vcl::Font aOrigFont = GetFont();
+ const_cast<OutputDevice&>(*this).SetFont( rTempFont );
+ FontCharMapRef xFontCharMap;
+ bool bRet = GetFontCharMap( xFontCharMap );
+ const_cast<OutputDevice&>(*this).SetFont( aOrigFont );
+
+ // if fontmap is unknown assume it doesn't have the glyphs
+ if( !bRet )
+ return nIndex;
+
+ for( sal_Int32 i = nIndex; nIndex < nEnd; ++i, ++nIndex )
+ if( ! xFontCharMap->HasChar( rStr[i] ) )
+ return nIndex;
+
+ return -1;
+}
+
+void OutputDevice::ReleaseFontCache() { mxFontCache.reset(); }
+
+void OutputDevice::ReleaseFontCollection() { mxFontCollection.reset(); }
+
+void OutputDevice::SetFontCollectionFromSVData()
+{
+ mxFontCollection = ImplGetSVData()->maGDIData.mxScreenFontList->Clone();
+}
+
+void OutputDevice::ResetNewFontCache()
+{
+ mxFontCache = std::make_shared<ImplFontCache>();
+}
+
+void OutputDevice::ImplReleaseFonts()
+{
+ mpGraphics->ReleaseFonts();
+
+ mbNewFont = true;
+ mbInitFont = true;
+
+ mpFontInstance.clear();
+ mpForcedFallbackInstance.clear();
+ mpFontFaceCollection.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */