summaryrefslogtreecommitdiffstats
path: root/sw/source/core/txtnode/fntcache.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/txtnode/fntcache.cxx')
-rw-r--r--sw/source/core/txtnode/fntcache.cxx2712
1 files changed, 2712 insertions, 0 deletions
diff --git a/sw/source/core/txtnode/fntcache.cxx b/sw/source/core/txtnode/fntcache.cxx
new file mode 100644
index 000000000..decea02c4
--- /dev/null
+++ b/sw/source/core/txtnode/fntcache.cxx
@@ -0,0 +1,2712 @@
+/* -*- 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 <memory>
+#include <sal/config.h>
+
+#include <cstdint>
+
+#include <i18nlangtag/mslangid.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/lineinfo.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/lazydelete.hxx>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <breakit.hxx>
+#include <paintfrm.hxx>
+#include <viewsh.hxx>
+#include <viewopt.hxx>
+#include <fntcache.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <swfont.hxx>
+#include <wrong.hxx>
+#include <txtfrm.hxx>
+#include <pagefrm.hxx>
+#include <tgrditem.hxx>
+#include <scriptinfo.hxx>
+#include <editeng/brushitem.hxx>
+#include <swmodule.hxx>
+#include <accessibilityoptions.hxx>
+#include <svtools/accessibilityoptions.hxx>
+#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
+#include <doc.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <vcl/glyphitem.hxx>
+#include <vcl/vcllayout.hxx>
+#include <docsh.hxx>
+#include <strings.hrc>
+#include <fntcap.hxx>
+#include <vcl/outdev/ScopedStates.hxx>
+
+using namespace ::com::sun::star;
+
+// global variables declared in fntcache.hxx
+// FontCache is created in txtinit.cxx TextInit_ and deleted in TextFinit
+SwFntCache *pFntCache = nullptr;
+// last Font set by ChgFntCache
+SwFntObj *pLastFont = nullptr;
+
+static constexpr Color gWaveCol(COL_GRAY);
+
+long SwFntObj::nPixWidth;
+MapMode* SwFntObj::pPixMap = nullptr;
+static vcl::DeleteOnDeinit< VclPtr<OutputDevice> > s_pFntObjPixOut( new VclPtr<OutputDevice> );
+
+namespace
+{
+
+long EvalGridWidthAdd( const SwTextGridItem *const pGrid, const SwDrawTextInfo &rInf )
+{
+ SwDocShell* pDocShell = rInf.GetShell()->GetDoc()->GetDocShell();
+ SfxStyleSheetBasePool* pBasePool = pDocShell->GetStyleSheetPool();
+
+ SfxStyleSheetBase* pStyle = pBasePool->Find(SwResId(STR_POOLCOLL_STANDARD), SfxStyleFamily::Para);
+ SfxItemSet& aTmpSet = pStyle->GetItemSet();
+ const SvxFontHeightItem &aDefaultFontItem = aTmpSet.Get(RES_CHRATR_CJK_FONTSIZE);
+
+ const SwDoc* pDoc = rInf.GetShell()->GetDoc();
+ const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
+ const sal_uInt32 nFontHeight = aDefaultFontItem.GetHeight();
+ const long nGridWidthAdd = nGridWidth > nFontHeight ? nGridWidth - nFontHeight : 0;
+ if( SwFontScript::Latin == rInf.GetFont()->GetActual() )
+ return nGridWidthAdd / 2;
+
+ return nGridWidthAdd;
+}
+
+/**
+ * Pre-calculates glyph items for the rendered subset of rKey's text, assuming
+ * outdev state does not change between the outdev calls.
+ */
+SalLayoutGlyphs* lcl_CreateLayout(const SwTextGlyphsKey& rKey, SalLayoutGlyphs& rTextGlyphs)
+{
+ // Use pre-calculated result.
+ if (rTextGlyphs.IsValid())
+ return &rTextGlyphs;
+
+ if (rKey.m_nIndex >= rKey.m_aText.getLength())
+ // Same as in OutputDevice::GetTextArray().
+ return nullptr;
+
+ // Calculate glyph items.
+ std::unique_ptr<SalLayout> pLayout
+ = rKey.m_pOutputDevice->ImplLayout(rKey.m_aText, rKey.m_nIndex, rKey.m_nLength, Point(0, 0), 0,
+ nullptr, SalLayoutFlags::GlyphItemsOnly);
+ if (!pLayout)
+ return nullptr;
+
+ const SalLayoutGlyphs* pGlyphs = pLayout->GetGlyphs();
+ if (!pGlyphs)
+ return nullptr;
+
+ // Remember the calculation result.
+ rTextGlyphs = *pGlyphs;
+
+ return &rTextGlyphs;
+}
+}
+
+bool operator<(const SwTextGlyphsKey& l, const SwTextGlyphsKey& r)
+{
+ if (l.m_pOutputDevice.get() < r.m_pOutputDevice.get())
+ return true;
+ if (l.m_pOutputDevice.get() > r.m_pOutputDevice.get())
+ return false;
+ if (l.m_nIndex < r.m_nIndex)
+ return true;
+ if (l.m_nIndex > r.m_nIndex)
+ return false;
+ if (l.m_nLength < r.m_nLength)
+ return true;
+ if (l.m_nLength > r.m_nLength)
+ return false;
+
+ // Comparing strings is expensive, so compare them:
+ // - only at the end of this function
+ // - only once
+ // - only the relevant substring (if the index/length is not out of bounds)
+ sal_Int32 nRet = 0;
+ if (l.m_nLength < 0 || l.m_nIndex < 0 || l.m_nIndex + l.m_nLength > l.m_aText.getLength())
+ nRet = l.m_aText.compareTo(r.m_aText);
+ else
+ nRet = memcmp(l.m_aText.getStr() + l.m_nIndex, r.m_aText.getStr() + r.m_nIndex,
+ l.m_nLength * sizeof(sal_Unicode));
+ if (nRet < 0)
+ return true;
+ if (nRet > 0)
+ return false;
+
+ return false;
+};
+
+void SwFntCache::Flush( )
+{
+ if ( pLastFont )
+ {
+ pLastFont->Unlock();
+ pLastFont = nullptr;
+ }
+ SwCache::Flush( );
+}
+
+SwFntObj::SwFntObj(const SwSubFont &rFont, std::uintptr_t nFontCacheId, SwViewShell const *pSh)
+ : SwCacheObj(reinterpret_cast<void *>(nFontCacheId))
+ , m_aFont(rFont)
+ , m_pScrFont(nullptr)
+ , m_pPrtFont(&m_aFont)
+ , m_pPrinter(nullptr)
+ , m_nGuessedLeading(USHRT_MAX)
+ , m_nExtLeading(USHRT_MAX)
+ , m_nScrAscent(0)
+ , m_nPrtAscent(USHRT_MAX)
+ , m_nScrHeight(0)
+ , m_nPrtHeight(USHRT_MAX)
+ , m_nPropWidth(rFont.GetPropWidth())
+{
+ m_nZoom = pSh ? pSh->GetViewOptions()->GetZoom() : USHRT_MAX;
+ m_bSymbol = RTL_TEXTENCODING_SYMBOL == m_aFont.GetCharSet();
+ m_bPaintBlank = ( LINESTYLE_NONE != m_aFont.GetUnderline()
+ || LINESTYLE_NONE != m_aFont.GetOverline()
+ || STRIKEOUT_NONE != m_aFont.GetStrikeout() )
+ && !m_aFont.IsWordLineMode();
+ m_aFont.SetLanguage(rFont.GetLanguage());
+}
+
+SwFntObj::~SwFntObj()
+{
+ if ( m_pScrFont != m_pPrtFont )
+ delete m_pScrFont;
+ if ( m_pPrtFont != &m_aFont )
+ delete m_pPrtFont;
+}
+
+void SwFntObj::CreatePrtFont( const OutputDevice& rPrt )
+{
+ if ( m_nPropWidth == 100 || m_pPrinter == &rPrt )
+ return;
+
+ if( m_pScrFont != m_pPrtFont )
+ delete m_pScrFont;
+ if( m_pPrtFont != &m_aFont )
+ delete m_pPrtFont;
+
+ const vcl::Font aOldFnt( rPrt.GetFont() );
+ const_cast<OutputDevice&>(rPrt).SetFont( m_aFont );
+ const FontMetric aWinMet( rPrt.GetFontMetric() );
+ const_cast<OutputDevice&>(rPrt).SetFont( aOldFnt );
+ auto nWidth = ( aWinMet.GetFontSize().Width() * m_nPropWidth ) / 100;
+
+ if( !nWidth )
+ ++nWidth;
+ m_pPrtFont = new vcl::Font( m_aFont );
+ m_pPrtFont->SetFontSize( Size( nWidth, m_aFont.GetFontSize().Height() ) );
+ m_pScrFont = nullptr;
+
+}
+
+/*
+ * returns whether we have to adjust the output font to resemble
+ * the formatting font
+ *
+ * _Not_ necessary if
+ *
+ * 1. RefDef == OutDev (text formatting, online layout...)
+ * 2. PDF export from online layout
+ * 3. Prospect/PagePreview printing
+ */
+static bool lcl_IsFontAdjustNecessary( const vcl::RenderContext& rOutDev,
+ const vcl::RenderContext& rRefDev )
+{
+ return &rRefDev != &rOutDev &&
+ OUTDEV_WINDOW != rRefDev.GetOutDevType() &&
+ ( OUTDEV_PRINTER != rRefDev.GetOutDevType() ||
+ OUTDEV_PRINTER != rOutDev.GetOutDevType() );
+}
+
+namespace {
+
+struct CalcLinePosData
+{
+ SwDrawTextInfo& rInf;
+ vcl::Font& rFont;
+ TextFrameIndex nCnt;
+ const bool bSwitchH2V;
+ const bool bSwitchH2VLRBT;
+ const bool bSwitchL2R;
+ long nHalfSpace;
+ long* pKernArray;
+ const bool bBidiPor;
+
+ CalcLinePosData( SwDrawTextInfo& _rInf, vcl::Font& _rFont,
+ TextFrameIndex const _nCnt, const bool _bSwitchH2V, const bool _bSwitchH2VLRBT, const bool _bSwitchL2R,
+ long _nHalfSpace, long* _pKernArray, const bool _bBidiPor) :
+ rInf( _rInf ),
+ rFont( _rFont ),
+ nCnt( _nCnt ),
+ bSwitchH2V( _bSwitchH2V ),
+ bSwitchH2VLRBT( _bSwitchH2VLRBT ),
+ bSwitchL2R( _bSwitchL2R ),
+ nHalfSpace( _nHalfSpace ),
+ pKernArray( _pKernArray ),
+ bBidiPor( _bBidiPor )
+ {
+ }
+};
+
+}
+
+// Computes the start and end position of an underline. This function is called
+// from the DrawText-method (for underlining misspelled words or smarttag terms).
+static void lcl_calcLinePos( const CalcLinePosData &rData,
+ Point &rStart, Point &rEnd, TextFrameIndex const nStart, TextFrameIndex const nWrLen)
+{
+ long nBlank = 0;
+ const TextFrameIndex nEnd = nStart + nWrLen;
+ const long nTmpSpaceAdd = rData.rInf.GetSpace() / SPACING_PRECISION_FACTOR;
+
+ if ( nEnd < rData.nCnt
+ && CH_BLANK == rData.rInf.GetText()[sal_Int32(rData.rInf.GetIdx() + nEnd)] )
+ {
+ if (nEnd + TextFrameIndex(1) == rData.nCnt)
+ nBlank -= nTmpSpaceAdd;
+ else
+ nBlank -= rData.nHalfSpace;
+ }
+
+ // determine start, end and length of wave line
+ sal_Int32 nKernStart = nStart ? rData.pKernArray[sal_Int32(nStart) - 1] : 0;
+ sal_Int32 nKernEnd = rData.pKernArray[sal_Int32(nEnd) - 1];
+
+ const sal_uInt16 nDir = rData.bBidiPor ? 1800
+ : UnMapDirection(rData.rFont.GetOrientation(),
+ rData.bSwitchH2V, rData.bSwitchH2VLRBT);
+
+ switch ( nDir )
+ {
+ case 0 :
+ rStart.AdjustX(nKernStart );
+ rEnd.setX( nBlank + rData.rInf.GetPos().X() + nKernEnd );
+ rEnd.setY( rData.rInf.GetPos().Y() );
+ break;
+ case 900 :
+ rStart.AdjustY( -nKernStart );
+ rEnd.setX( rData.rInf.GetPos().X() );
+ rEnd.setY( nBlank + rData.rInf.GetPos().Y() - nKernEnd );
+ break;
+ case 1800 :
+ rStart.AdjustX( -nKernStart );
+ rEnd.setX( rData.rInf.GetPos().X() - nKernEnd - nBlank );
+ rEnd.setY( rData.rInf.GetPos().Y() );
+ break;
+ case 2700 :
+ rStart.AdjustY(nKernStart );
+ rEnd.setX( rData.rInf.GetPos().X() );
+ rEnd.setY( nBlank + rData.rInf.GetPos().Y() + nKernEnd );
+ break;
+ }
+
+ if ( rData.bSwitchL2R )
+ {
+ rData.rInf.GetFrame()->SwitchLTRtoRTL( rStart );
+ rData.rInf.GetFrame()->SwitchLTRtoRTL( rEnd );
+ }
+
+ if ( rData.bSwitchH2V )
+ {
+ rData.rInf.GetFrame()->SwitchHorizontalToVertical( rStart );
+ rData.rInf.GetFrame()->SwitchHorizontalToVertical( rEnd );
+ }
+}
+
+// Returns the Ascent of the Font on the given output device;
+// it may be necessary to create the screen font first.
+sal_uInt16 SwFntObj::GetFontAscent( const SwViewShell *pSh, const OutputDevice& rOut )
+{
+ sal_uInt16 nRet = 0;
+ const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;
+
+ if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) )
+ {
+ CreateScrFont( *pSh, rOut );
+ OSL_ENSURE( USHRT_MAX != m_nScrAscent, "nScrAscent is going berzerk" );
+ nRet = m_nScrAscent;
+ }
+ else
+ {
+ if (m_nPrtAscent == USHRT_MAX) // printer ascent unknown?
+ {
+ CreatePrtFont( rOut );
+ const vcl::Font aOldFnt( rRefDev.GetFont() );
+ const_cast<OutputDevice&>(rRefDev).SetFont( *m_pPrtFont );
+ const FontMetric aOutMet( rRefDev.GetFontMetric() );
+ m_nPrtAscent = static_cast<sal_uInt16>(aOutMet.GetAscent());
+ const_cast<OutputDevice&>(rRefDev).SetFont( aOldFnt );
+ }
+
+ nRet = m_nPrtAscent;
+ }
+
+#if !defined(MACOSX) // #i89844# extleading is below the line for Mac
+ // TODO: move extleading below the line for all platforms too
+ nRet += GetFontLeading( pSh, rRefDev );
+#endif
+
+ OSL_ENSURE( USHRT_MAX != nRet, "GetFontAscent returned USHRT_MAX" );
+ return nRet;
+}
+
+// Returns the height of the Font on the given output device;
+// it may be necessary to create the screen font first.
+sal_uInt16 SwFntObj::GetFontHeight( const SwViewShell* pSh, const OutputDevice& rOut )
+{
+ sal_uInt16 nRet = 0;
+ const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;
+
+ if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) )
+ {
+ CreateScrFont( *pSh, rOut );
+ OSL_ENSURE( USHRT_MAX != m_nScrHeight, "nScrHeight is going berzerk" );
+ nRet = m_nScrHeight + GetFontLeading( pSh, rRefDev );
+ }
+ else
+ {
+ if (m_nPrtHeight == USHRT_MAX) // printer height unknown?
+ {
+ CreatePrtFont( rOut );
+ const vcl::Font aOldFnt( rRefDev.GetFont() );
+ const_cast<OutputDevice&>(rRefDev).SetFont( *m_pPrtFont );
+ m_nPrtHeight = static_cast<sal_uInt16>(rRefDev.GetTextHeight());
+
+#if OSL_DEBUG_LEVEL > 0
+ // Check if vcl did not change the meaning of GetTextHeight
+ const FontMetric aOutMet( rRefDev.GetFontMetric() );
+ long nTmpPrtHeight = static_cast<sal_uInt16>(aOutMet.GetAscent()) + aOutMet.GetDescent();
+ // #i106098#: do not compare with == here due to rounding error
+ OSL_ENSURE( std::abs(nTmpPrtHeight - m_nPrtHeight) < 3,
+ "GetTextHeight != Ascent + Descent" );
+#endif
+
+ const_cast<OutputDevice&>(rRefDev).SetFont( aOldFnt );
+ }
+
+ nRet = m_nPrtHeight + GetFontLeading( pSh, rRefDev );
+ }
+
+ OSL_ENSURE( USHRT_MAX != nRet, "GetFontHeight returned USHRT_MAX" );
+ return nRet;
+}
+
+sal_uInt16 SwFntObj::GetFontLeading( const SwViewShell *pSh, const OutputDevice& rOut )
+{
+ sal_uInt16 nRet = 0;
+
+ if ( pSh )
+ {
+ if ( USHRT_MAX == m_nGuessedLeading || USHRT_MAX == m_nExtLeading )
+ {
+ SolarMutexGuard aGuard;
+
+ const vcl::Font aOldFnt( rOut.GetFont() );
+ const_cast<OutputDevice&>(rOut).SetFont( *m_pPrtFont );
+ const FontMetric aMet( rOut.GetFontMetric() );
+ const_cast<OutputDevice&>(rOut).SetFont( aOldFnt );
+ m_bSymbol = RTL_TEXTENCODING_SYMBOL == aMet.GetCharSet();
+ GuessLeading( *pSh, aMet );
+ m_nExtLeading = static_cast<sal_uInt16>(aMet.GetExternalLeading());
+ /* HACK: FIXME There is something wrong with Writer's bullet rendering, causing lines
+ with bullets to be higher than they should be. I think this is because
+ Writer uses font's external leading incorrect, as the vertical distance
+ added to every line instead of only a distance between multiple lines,
+ which means a single bullet has external leading added even though it
+ shouldn't, but frankly this is just an educated guess rather than understanding
+ Writer's layout (heh).
+ Symbol font in some documents is 'StarSymbol; Arial Unicode MS', and Windows
+ machines often do not have StarSymbol, falling back to Arial Unicode MS, which
+ has unusually high external leading. So just reset external leading for fonts
+ which are used to bullets, as those should not be used on multiple lines anyway,
+ so in correct rendering external leading should be irrelevant anyway.
+ Interestingly enough, bSymbol is false for 'StarSymbol; Arial Unicode MS', so
+ also check explicitly.
+ */
+ if( m_bSymbol || IsStarSymbol( m_pPrtFont->GetFamilyName()))
+ m_nExtLeading = 0;
+ }
+
+ const IDocumentSettingAccess& rIDSA = pSh->getIDocumentSettingAccess();
+ const bool bBrowse = ( pSh->GetWin() &&
+ pSh->GetViewOptions()->getBrowseMode() &&
+ !pSh->GetViewOptions()->IsPrtFormat() );
+
+ if ( !bBrowse && rIDSA.get(DocumentSettingId::ADD_EXT_LEADING) )
+ nRet = m_nExtLeading;
+ else
+ nRet = m_nGuessedLeading;
+ }
+
+ OSL_ENSURE( USHRT_MAX != nRet, "GetFontLeading returned USHRT_MAX" );
+ return nRet;
+}
+
+// pOut is the output device, not the reference device
+void SwFntObj::CreateScrFont( const SwViewShell& rSh, const OutputDevice& rOut )
+{
+ if ( m_pScrFont )
+ return;
+
+ // any changes to the output device are reset at the end of the function
+ OutputDevice* pOut = const_cast<OutputDevice*>(&rOut);
+
+ // Save old font
+ vcl::Font aOldOutFont( pOut->GetFont() );
+
+ m_nScrHeight = USHRT_MAX;
+
+ // Condition for output font / refdev font adjustment
+ OutputDevice* pPrt = &rSh.GetRefDev();
+
+ if( !rSh.GetWin() ||
+ !rSh.GetViewOptions()->getBrowseMode() ||
+ rSh.GetViewOptions()->IsPrtFormat() )
+ {
+ // After CreatePrtFont m_pPrtFont is the font which is actually used
+ // by the reference device
+ CreatePrtFont( *pPrt );
+ m_pPrinter = pPrt;
+
+ // save old reference device font
+ vcl::Font aOldPrtFnt( pPrt->GetFont() );
+
+ // set the font used at the reference device at the reference device
+ // and the output device
+ pPrt->SetFont( *m_pPrtFont );
+ pOut->SetFont( *m_pPrtFont );
+
+ // This should be the default for pScrFont.
+ m_pScrFont = m_pPrtFont;
+
+ FontMetric aMet = pPrt->GetFontMetric( );
+ // Don't lose "faked" properties of the logical font that don't truly
+ // exist in the physical font metrics which vcl which fake up for us
+ aMet.SetWeight(m_pScrFont->GetWeight());
+ aMet.SetItalic(m_pScrFont->GetItalic());
+
+ m_bSymbol = RTL_TEXTENCODING_SYMBOL == aMet.GetCharSet();
+
+ if ( USHRT_MAX == m_nGuessedLeading )
+ GuessLeading( rSh, aMet );
+
+ if ( USHRT_MAX == m_nExtLeading )
+ m_nExtLeading = static_cast<sal_uInt16>(aMet.GetExternalLeading());
+
+ // reset the original reference device font
+ pPrt->SetFont( aOldPrtFnt );
+ }
+ else
+ {
+ m_bSymbol = RTL_TEXTENCODING_SYMBOL == m_aFont.GetCharSet();
+ if ( m_nGuessedLeading == USHRT_MAX )
+ m_nGuessedLeading = 0;
+
+ // no external leading in browse mode
+ if ( m_nExtLeading == USHRT_MAX )
+ m_nExtLeading = 0;
+
+ m_pScrFont = m_pPrtFont;
+ }
+
+ // check zoom factor, e.g. because of PrtOle2 during export
+ {
+ // In case the zoom factor of the output device differs from the
+ // one in the ViewOptions, this Font must not be cached,
+ // hence set zoom factor to an invalid value
+ long nTmp;
+ if( pOut->GetMapMode().GetScaleX().IsValid() &&
+ pOut->GetMapMode().GetScaleY().IsValid() &&
+ pOut->GetMapMode().GetScaleX() == pOut->GetMapMode().GetScaleY() )
+ {
+ nTmp = long(100 * pOut->GetMapMode().GetScaleX());
+ }
+ else
+ nTmp = 0;
+ if( nTmp != m_nZoom )
+ m_nZoom = USHRT_MAX - 1;
+ }
+
+ m_nScrAscent = static_cast<sal_uInt16>(pOut->GetFontMetric().GetAscent());
+ if ( USHRT_MAX == m_nScrHeight )
+ m_nScrHeight = static_cast<sal_uInt16>(pOut->GetTextHeight());
+
+ // reset original output device font
+ pOut->SetFont( aOldOutFont );
+}
+
+void SwFntObj::GuessLeading( const SwViewShell&
+#if defined(_WIN32)
+ rSh
+#endif
+ , const FontMetric& rMet )
+{
+ // If leading >= 5, this seems to be enough leading.
+ // Nothing has to be done.
+ if ( rMet.GetInternalLeading() >= 5 )
+ {
+ m_nGuessedLeading = 0;
+ return;
+ }
+
+#if defined(_WIN32)
+ OutputDevice *pWin = rSh.GetWin() ?
+ rSh.GetWin() :
+ Application::GetDefaultDevice();
+ if ( pWin )
+ {
+ MapMode aTmpMap( MapUnit::MapTwip );
+ MapMode aOldMap = pWin->GetMapMode( );
+ pWin->SetMapMode( aTmpMap );
+ const vcl::Font aOldFnt( pWin->GetFont() );
+ pWin->SetFont( *m_pPrtFont );
+ const FontMetric aWinMet( pWin->GetFontMetric() );
+ const sal_uInt16 nWinHeight = sal_uInt16( aWinMet.GetFontSize().Height() );
+ if( m_pPrtFont->GetFamilyName().indexOf( aWinMet.GetFamilyName() ) != -1 )
+ {
+ // If the Leading on the Window is also 0, then it has to stay
+ // that way (see also StarMath).
+ long nTmpLeading = aWinMet.GetInternalLeading();
+ if( nTmpLeading <= 0 )
+ {
+ pWin->SetFont( rMet );
+ nTmpLeading = pWin->GetFontMetric().GetInternalLeading();
+ if( nTmpLeading < 0 )
+ m_nGuessedLeading = 0;
+ else
+ m_nGuessedLeading = sal_uInt16(nTmpLeading);
+ }
+ else
+ {
+ m_nGuessedLeading = sal_uInt16(nTmpLeading);
+ // Manta-Hack #50153#:
+ // Wer beim Leading luegt, luegt moeglicherweise auch beim
+ // Ascent/Descent, deshalb wird hier ggf. der Font ein wenig
+ // tiefergelegt, ohne dabei seine Hoehe zu aendern.
+ // (above original comment preserved for cultural reasons)
+ // Those who lie about their Leading, may lie about their
+ // Ascent/Descent as well, hence the Font will be lowered a
+ // little without changing its height.
+ long nDiff = std::min( rMet.GetDescent() - aWinMet.GetDescent(),
+ aWinMet.GetAscent() - rMet.GetAscent() - nTmpLeading );
+ if( nDiff > 0 )
+ {
+ OSL_ENSURE( m_nPrtAscent < USHRT_MAX, "GuessLeading: PrtAscent-Fault" );
+ if ( m_nPrtAscent < USHRT_MAX )
+ m_nPrtAscent = m_nPrtAscent + static_cast<sal_uInt16>(( 2 * nDiff ) / 5);
+ }
+ }
+ }
+ else
+ {
+ // If all else fails, take 15% of the height, as empirically
+ // determined by CL
+ m_nGuessedLeading = (nWinHeight * 15) / 100;
+ }
+ pWin->SetFont( aOldFnt );
+ pWin->SetMapMode( aOldMap );
+ }
+ else
+ m_nGuessedLeading = 0;
+#else
+ m_nGuessedLeading = 0;
+#endif
+}
+
+// Set the font at the given output device; for screens it may be
+// necessary to do some adjustment first.
+void SwFntObj::SetDevFont( const SwViewShell *pSh, OutputDevice& rOut )
+{
+ const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;
+
+ if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) )
+ {
+ CreateScrFont( *pSh, rOut );
+ if( !GetScrFont()->IsSameInstance( rOut.GetFont() ) )
+ rOut.SetFont( *m_pScrFont );
+ if( m_pPrinter && ( !m_pPrtFont->IsSameInstance( m_pPrinter->GetFont() ) ) )
+ m_pPrinter->SetFont( *m_pPrtFont );
+ }
+ else
+ {
+ CreatePrtFont( rOut );
+ if( !m_pPrtFont->IsSameInstance( rOut.GetFont() ) )
+ rOut.SetFont( *m_pPrtFont );
+ }
+
+ // Here, we actually do not need the leading values, but by calling
+ // GetFontLeading() we assure that the values are calculated for later use.
+ GetFontLeading( pSh, rRefDev );
+}
+
+#define WRONG_SHOW_MIN 5
+
+/*
+ * Output text:
+ * on screen => DrawTextArray
+ * on printer, !Kerning => DrawText
+ * on printer + Kerning => DrawStretchText
+ */
+static sal_uInt8 lcl_WhichPunctuation( sal_Unicode cChar )
+{
+ if ( ( cChar < 0x3001 || cChar > 0x3002 ) &&
+ ( cChar < 0x3008 || cChar > 0x3011 ) &&
+ ( cChar < 0x3014 || cChar > 0x301F ) &&
+ 0xFF62 != cChar && 0xFF63 != cChar )
+ // no punctuation
+ return SwScriptInfo::NONE;
+ else if ( 0x3001 == cChar || 0x3002 == cChar ||
+ 0x3009 == cChar || 0x300B == cChar ||
+ 0x300D == cChar || 0x300F == cChar ||
+ 0x3011 == cChar || 0x3015 == cChar ||
+ 0x3017 == cChar || 0x3019 == cChar ||
+ 0x301B == cChar || 0x301E == cChar ||
+ 0x301F == cChar || 0xFF63 == cChar )
+ // right punctuation
+ return SwScriptInfo::SPECIAL_RIGHT;
+
+ return SwScriptInfo::SPECIAL_LEFT;
+}
+
+static bool lcl_IsMonoSpaceFont( const vcl::RenderContext& rOut )
+{
+ const long nWidth1 = rOut.GetTextWidth( OUString( u'\x3008' ) );
+ const long nWidth2 = rOut.GetTextWidth( OUString( u'\x307C' ) );
+ return nWidth1 == nWidth2;
+}
+
+static bool lcl_IsFullstopCentered( const vcl::RenderContext& rOut )
+{
+ const FontMetric aMetric( rOut.GetFontMetric() );
+ return aMetric.IsFullstopCentered() ;
+}
+
+/* This helper structure (SwForbidden) contains the already marked parts of the string
+ to avoid double lines (e.g grammar + spell check error) */
+
+typedef std::vector<std::pair<TextFrameIndex, TextFrameIndex>> SwForbidden;
+
+static void lcl_DrawLineForWrongListData(
+ SwForbidden &rForbidden,
+ const SwDrawTextInfo &rInf,
+ sw::WrongListIterator *pWList,
+ const CalcLinePosData &rCalcLinePosData,
+ const Size &rPrtFontSize )
+{
+ if (!pWList) return;
+
+ TextFrameIndex nStart = rInf.GetIdx();
+ TextFrameIndex nWrLen = rInf.GetLen();
+
+ // check if respective data is available in the current text range
+ if (!pWList->Check( nStart, nWrLen ))
+ {
+ return;
+ }
+
+ long nHght = rInf.GetOut().LogicToPixel( rPrtFontSize ).Height();
+
+ // Draw wavy lines for spell and grammar errors only if font is large enough.
+ // Lines for smart tags will always be drawn.
+ if (pWList != rInf.GetSmartTags() && WRONG_SHOW_MIN >= nHght)
+ {
+ return;
+ }
+
+ SwForbidden::iterator pIter = rForbidden.begin();
+ if (rInf.GetOut().GetConnectMetaFile())
+ rInf.GetOut().Push();
+
+ const Color aCol( rInf.GetOut().GetLineColor() );
+
+ // iterate over all ranges stored in the respective SwWrongList
+ do
+ {
+ nStart -= rInf.GetIdx();
+
+ const TextFrameIndex nEnd = nStart + nWrLen;
+ TextFrameIndex nNext = nStart;
+ while( nNext < nEnd )
+ {
+ while( pIter != rForbidden.end() && pIter->second <= nNext )
+ ++pIter;
+
+ const TextFrameIndex nNextStart = nNext;
+ TextFrameIndex nNextEnd = nEnd;
+
+ if( pIter == rForbidden.end() || nNextEnd <= pIter->first )
+ {
+ // No overlapping mark up found
+ rForbidden.insert(pIter, std::make_pair(nNextStart, nNextEnd));
+ pIter = rForbidden.begin();
+ nNext = nEnd;
+ }
+ else
+ {
+ nNext = pIter->second;
+ if( nNextStart < pIter->first )
+ {
+ nNextEnd = pIter->first;
+ pIter->first = nNextStart;
+ }
+ else
+ continue;
+ }
+ // determine line pos
+ Point aStart( rInf.GetPos() );
+ Point aEnd;
+ lcl_calcLinePos( rCalcLinePosData, aStart, aEnd, nNextStart, nNextEnd - nNextStart );
+
+ SwWrongArea const*const wrongArea = pWList->GetWrongElement(nNextStart + rInf.GetIdx());
+ if (wrongArea != nullptr)
+ {
+ if (WRONGAREA_WAVE == wrongArea->mLineType)
+ {
+ vcl::ScopedAntialiasing a(rInf.GetOut(), true);
+ rInf.GetOut().SetLineColor( wrongArea->mColor );
+ rInf.GetOut().DrawWaveLine( aStart, aEnd, 1 );
+ }
+ else if (WRONGAREA_BOLDWAVE == wrongArea->mLineType)
+ {
+ vcl::ScopedAntialiasing a(rInf.GetOut(), true);
+ rInf.GetOut().SetLineColor( wrongArea->mColor );
+ rInf.GetOut().DrawWaveLine( aStart, aEnd, 2 );
+ }
+ else if (WRONGAREA_BOLD == wrongArea->mLineType)
+ {
+ rInf.GetOut().SetLineColor( wrongArea->mColor );
+
+ aStart.AdjustY(30 );
+ aEnd.AdjustY(30 );
+
+ LineInfo aLineInfo( LineStyle::Solid, 26 );
+
+ rInf.GetOut().DrawLine( aStart, aEnd, aLineInfo );
+ }
+ else if (WRONGAREA_DASHED == wrongArea->mLineType)
+ {
+ rInf.GetOut().SetLineColor( wrongArea->mColor );
+
+ aStart.AdjustY(30 );
+ aEnd.AdjustY(30 );
+
+ LineInfo aLineInfo( LineStyle::Dash );
+ aLineInfo.SetDistance( 40 );
+ aLineInfo.SetDashLen( 1 );
+ aLineInfo.SetDashCount(1);
+
+ rInf.GetOut().DrawLine( aStart, aEnd, aLineInfo );
+ }
+ }
+ }
+
+ nStart = nEnd + rInf.GetIdx();
+ nWrLen = rInf.GetIdx() + rInf.GetLen() - nStart;
+ }
+ while (nWrLen && pWList->Check( nStart, nWrLen ));
+
+ rInf.GetOut().SetLineColor( aCol );
+
+ if (rInf.GetOut().GetConnectMetaFile())
+ rInf.GetOut().Pop();
+}
+
+void SwFntObj::DrawText( SwDrawTextInfo &rInf )
+{
+ OSL_ENSURE( rInf.GetShell(), "SwFntObj::DrawText without shell" );
+
+ OutputDevice& rRefDev = rInf.GetShell()->GetRefDev();
+ OutputDevice* pWin = rInf.GetShell()->GetWin();
+
+ // true if pOut is the printer and the printer has been used for formatting
+ const bool bPrt = OUTDEV_PRINTER == rInf.GetOut().GetOutDevType() &&
+ OUTDEV_PRINTER == rRefDev.GetOutDevType();
+ const bool bBrowse = ( pWin &&
+ rInf.GetShell()->GetViewOptions()->getBrowseMode() &&
+ !rInf.GetShell()->GetViewOptions()->IsPrtFormat() &&
+ !rInf.GetBullet() &&
+ ( rInf.GetSpace() || !rInf.GetKern() ) &&
+ !rInf.GetWrong() &&
+ !rInf.GetGrammarCheck() &&
+ !rInf.GetSmartTags() &&
+ !rInf.GetGreyWave() );
+
+ // bDirectPrint indicates that we can enter the branch which calls
+ // the DrawText functions instead of calling the DrawTextArray functions
+ const bool bDirectPrint = bPrt || bBrowse;
+
+ // Condition for output font / refdev font adjustment
+ const bool bUseScrFont =
+ lcl_IsFontAdjustNecessary( rInf.GetOut(), rRefDev );
+
+ vcl::Font* pTmpFont = bUseScrFont ? m_pScrFont : m_pPrtFont;
+
+ // bDirectPrint and bUseScrFont should have these values:
+
+ // Outdev / RefDef | Printer | VirtPrinter | Window
+
+ // Printer | 1 - 0 | 0 - 1 | -
+
+ // VirtPrinter/PDF | 0 - 1 | 0 - 1 | -
+
+ // Window/VirtWindow| 0 - 1 | 0 - 1 | 1 - 0
+
+ // Exception: During painting of a Writer OLE object, we do not have
+ // a window. Therefore bUseSrcFont is always 0 in this case.
+
+#if OSL_DEBUG_LEVEL > 0
+
+ const bool bNoAdjust = bPrt ||
+ ( pWin &&
+ rInf.GetShell()->GetViewOptions()->getBrowseMode() &&
+ !rInf.GetShell()->GetViewOptions()->IsPrtFormat() );
+
+ if ( OUTDEV_PRINTER == rInf.GetOut().GetOutDevType() )
+ {
+ // Printer output
+ if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() )
+ {
+ OSL_ENSURE( bNoAdjust && !bUseScrFont, "Outdev Check failed" );
+ }
+ else if ( rRefDev.IsVirtual() )
+ {
+ OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
+ }
+ else
+ {
+ OSL_FAIL( "Outdev Check failed" );
+ }
+ }
+ else if ( rInf.GetOut().IsVirtual() && ! pWin )
+ {
+ // PDF export
+ if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() )
+ {
+ OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
+ }
+ else if ( rRefDev.IsVirtual() )
+ {
+ OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
+ }
+ else
+ {
+ OSL_FAIL( "Outdev Check failed" );
+ }
+ }
+ else if ( OUTDEV_WINDOW == rInf.GetOut().GetOutDevType() ||
+ ( rInf.GetOut().IsVirtual() && pWin ) )
+ {
+ // Window or virtual window
+ if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() )
+ {
+ OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
+ }
+ else if ( rRefDev.IsVirtual() )
+ {
+ OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
+ }
+ else if ( OUTDEV_WINDOW == rRefDev.GetOutDevType() )
+ {
+ OSL_ENSURE( bNoAdjust && !bUseScrFont, "Outdev Check failed" );
+ }
+ else
+ {
+ OSL_FAIL( "Outdev Check failed" );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "Outdev Check failed" );
+ }
+
+#endif
+
+ // robust: better use the printer font instead of using no font at all
+ OSL_ENSURE( pTmpFont, "No screen or printer font?" );
+ if ( ! pTmpFont )
+ pTmpFont = m_pPrtFont;
+
+ // HACK: LINESTYLE_WAVE must not be abused any more, hence the grey wave
+ // line of the ExtendedAttributeSets will appear in the font color first
+
+ const bool bSwitchH2V = rInf.GetFrame() && rInf.GetFrame()->IsVertical();
+ const bool bSwitchH2VLRBT = rInf.GetFrame() && rInf.GetFrame()->IsVertLRBT();
+ const bool bSwitchL2R = rInf.GetFrame() && rInf.GetFrame()->IsRightToLeft() &&
+ ! rInf.IsIgnoreFrameRTL();
+ const ComplexTextLayoutFlags nMode = rInf.GetOut().GetLayoutMode();
+ const bool bBidiPor = ( bSwitchL2R !=
+ ( ComplexTextLayoutFlags::Default != ( ComplexTextLayoutFlags::BiDiRtl & nMode ) ) );
+
+ // be sure to have the correct layout mode at the printer
+ if ( m_pPrinter )
+ {
+ m_pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() );
+ m_pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() );
+ }
+
+ Point aTextOriginPos( rInf.GetPos() );
+ if( !bPrt )
+ {
+ if( rInf.GetpOut() != *s_pFntObjPixOut.get() || rInf.GetOut().GetMapMode() != *pPixMap )
+ {
+ *pPixMap = rInf.GetOut().GetMapMode();
+ (*s_pFntObjPixOut.get()) = rInf.GetpOut();
+ Size aTmp( 1, 1 );
+ nPixWidth = rInf.GetOut().PixelToLogic( aTmp ).Width();
+ }
+
+ aTextOriginPos.AdjustX(rInf.GetFrame()->IsRightToLeft() ? 0 : nPixWidth );
+ }
+
+ Color aOldColor( pTmpFont->GetColor() );
+ bool bChgColor = rInf.ApplyAutoColor( pTmpFont );
+ if( !pTmpFont->IsSameInstance( rInf.GetOut().GetFont() ) )
+ rInf.GetOut().SetFont( *pTmpFont );
+ if ( bChgColor )
+ pTmpFont->SetColor( aOldColor );
+
+ if (TextFrameIndex(COMPLETE_STRING) == rInf.GetLen())
+ rInf.SetLen( TextFrameIndex(rInf.GetText().getLength()) );
+
+ // ASIAN LINE AND CHARACTER GRID MODE START
+
+ if ( rInf.GetFrame() && rInf.SnapToGrid() && rInf.GetFont() &&
+ SwFontScript::CJK == rInf.GetFont()->GetActual() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
+
+ // ASIAN LINE AND CHARACTER GRID MODE: Do we want to snap asian characters to the grid?
+ if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars())
+ {
+ //for textgrid refactor
+ //const sal_uInt16 nGridWidth = pGrid->GetBaseHeight();
+ const SwDoc* pDoc = rInf.GetShell()->GetDoc();
+ const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
+
+ // kerning array - gives the absolute position of end of each character
+ std::unique_ptr<long[]> pKernArray(new long[sal_Int32(rInf.GetLen())]);
+
+ if ( m_pPrinter )
+ m_pPrinter->GetTextArray( rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ else
+ rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+
+ // Change the average width per character to an appropriate grid width
+ // basically get the ratio of the avg width to the grid unit width, then
+ // multiple this ratio to give the new avg width - which in this case
+ // gives a new grid width unit size
+
+ long nAvgWidthPerChar = pKernArray[sal_Int32(rInf.GetLen()) - 1] / sal_Int32(rInf.GetLen());
+
+ const sal_uLong nRatioAvgWidthCharToGridWidth = nAvgWidthPerChar ?
+ ( nAvgWidthPerChar - 1 ) / nGridWidth + 1:
+ 1;
+
+ nAvgWidthPerChar = nRatioAvgWidthCharToGridWidth * nGridWidth;
+
+ // the absolute end position of the first character is also its width
+ long nCharWidth = pKernArray[ 0 ];
+ sal_uLong nHalfWidth = nAvgWidthPerChar / 2;
+
+ long nNextFix=0;
+
+ // we work out the start position (origin) of the first character,
+ // and we set the next "fix" offset to half the width of the char.
+ // The exceptions are for punctuation characters that are not centered
+ // so in these cases we just add half a regular "average" character width
+ // to the first characters actual width to allow the next character to
+ // be centered automatically
+ // If the character is "special right", then the offset is correct already
+ // so the fix offset is as normal - half the average character width
+
+ sal_Unicode cChar = rInf.GetText()[ sal_Int32(rInf.GetIdx()) ];
+ sal_uInt8 nType = lcl_WhichPunctuation( cChar );
+ switch ( nType )
+ {
+ // centre character
+ case SwScriptInfo::NONE :
+ aTextOriginPos.AdjustX(( nAvgWidthPerChar - nCharWidth ) / 2 );
+ nNextFix = nCharWidth / 2;
+ break;
+ case SwScriptInfo::SPECIAL_RIGHT :
+ nNextFix = nHalfWidth;
+ break;
+ // punctuation
+ default:
+ aTextOriginPos.AdjustX(nAvgWidthPerChar - nCharWidth );
+ nNextFix = nCharWidth - nHalfWidth;
+ }
+
+ // calculate offsets
+ for (sal_Int32 j = 1; j < sal_Int32(rInf.GetLen()); ++j)
+ {
+ long nCurrentCharWidth = pKernArray[ j ] - pKernArray[ j - 1 ];
+ nNextFix += nAvgWidthPerChar;
+
+ // almost the same as getting the offset for the first character:
+ // punctuation characters are not centered, so just add half an
+ // average character width minus the characters actual char width
+ // to get the offset into the centre of the next character
+
+ cChar = rInf.GetText()[ sal_Int32(rInf.GetIdx()) + j ];
+ nType = lcl_WhichPunctuation( cChar );
+ switch ( nType )
+ {
+ case SwScriptInfo::NONE :
+ pKernArray[ j - 1 ] = nNextFix - ( nCurrentCharWidth / 2 );
+ break;
+ case SwScriptInfo::SPECIAL_RIGHT :
+ pKernArray[ j - 1 ] = nNextFix - nHalfWidth;
+ break;
+ default:
+ pKernArray[ j - 1 ] = nNextFix + nHalfWidth - nCurrentCharWidth;
+ }
+ }
+
+ // the layout engine requires the total width of the output
+ pKernArray[sal_Int32(rInf.GetLen()) - 1] = rInf.GetWidth() -
+ aTextOriginPos.X() + rInf.GetPos().X() ;
+
+ if ( bSwitchH2V )
+ rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
+
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
+ pKernArray.get(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+
+ return;
+ }
+ }
+
+ // For text grid refactor
+ // ASIAN LINE AND CHARACTER GRID MODE START: not snap to characters
+
+ if ( rInf.GetFrame() && rInf.SnapToGrid() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
+
+ // ASIAN LINE AND CHARACTER GRID MODE - do not snap to characters
+ if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() )
+ {
+ const long nGridWidthAdd = EvalGridWidthAdd( pGrid, rInf );
+
+ std::unique_ptr<long[]> pKernArray(new long[sal_Int32(rInf.GetLen())]);
+
+ if ( m_pPrinter )
+ m_pPrinter->GetTextArray( rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ else
+ rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ if ( bSwitchH2V )
+ rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
+ if ( rInf.GetSpace() || rInf.GetKanaComp())
+ {
+ long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
+ if ( rInf.GetFont() && rInf.GetLen() )
+ {
+ bool bSpecialJust = false;
+ const SwScriptInfo* pSI = rInf.GetScriptInfo();
+ const SwFontScript nActual = rInf.GetFont()->GetActual();
+ ///Kana Compression
+ if( SwFontScript::CJK == nActual && rInf.GetKanaComp() &&
+ pSI && pSI->CountCompChg() &&
+ lcl_IsMonoSpaceFont( *(rInf.GetpOut()) ) )
+ {
+ pSI->Compress( pKernArray.get(), rInf.GetIdx(), rInf.GetLen(),
+ rInf.GetKanaComp(), static_cast<sal_uInt16>(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered( rInf.GetOut() ) , &aTextOriginPos );
+ bSpecialJust = true;
+ }
+ ///Asian Justification
+ if ( ( SwFontScript::CJK == nActual || SwFontScript::Latin == nActual ) && nSpaceAdd )
+ {
+ LanguageType aLang = rInf.GetFont()->GetLanguage( SwFontScript::CJK );
+ if (!MsLangId::isKorean(aLang))
+ {
+ long nSpaceSum = nSpaceAdd;
+ for (sal_Int32 nI = 0; nI < sal_Int32(rInf.GetLen()); ++nI)
+ {
+ pKernArray[ nI ] += nSpaceSum;
+ nSpaceSum += nSpaceAdd;
+ }
+ bSpecialJust = true;
+ nSpaceAdd = 0;
+ }
+ }
+ long nGridAddSum = nGridWidthAdd;
+ for (sal_Int32 i = 0; i < sal_Int32(rInf.GetLen()); i++, nGridAddSum += nGridWidthAdd )
+ {
+ pKernArray[i] += nGridAddSum;
+ }
+ long nKernSum = rInf.GetKern();
+ if ( bSpecialJust || rInf.GetKern() )
+ {
+ for (sal_Int32 i = 0; i < sal_Int32(rInf.GetLen()); i++, nKernSum += rInf.GetKern())
+ {
+ if (CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx())+i])
+ nKernSum += nSpaceAdd;
+ pKernArray[i] += nKernSum;
+ }
+ ///With through/uderstr. Grouped style requires a blank at the end
+ ///of a text edition special measures:
+ if( m_bPaintBlank && rInf.GetLen() && (CH_BLANK ==
+ rInf.GetText()[sal_Int32(rInf.GetIdx() + rInf.GetLen()) - 1]))
+ {
+ ///If it concerns a singular, underlined space acts,
+ ///we must spend two:
+ if (TextFrameIndex(1) == rInf.GetLen())
+ {
+ pKernArray[0] = rInf.GetWidth() + nSpaceAdd;
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
+ pKernArray.get(), sal_Int32(rInf.GetIdx()), 1);
+ }
+ else
+ {
+ pKernArray[sal_Int32(rInf.GetLen()) - 2] += nSpaceAdd;
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
+ pKernArray.get(), sal_Int32(rInf.GetIdx()),
+ sal_Int32(rInf.GetLen()));
+ }
+ }
+ else
+ {
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
+ pKernArray.get(), sal_Int32(rInf.GetIdx()),
+ sal_Int32(rInf.GetLen()));
+ }
+ }
+ else
+ {
+ sal_Int32 i;
+ long nSpaceSum = 0;
+ for (i = 0; i < sal_Int32(rInf.GetLen()); i++)
+ {
+ if(CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx()) + i])
+ nSpaceSum += nSpaceAdd + nKernSum;
+
+ pKernArray[i] += nSpaceSum;
+ }
+
+ rInf.GetOut().DrawTextArray(aTextOriginPos,
+ rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()),
+ sal_Int32(rInf.GetLen()));
+ }
+ }
+ }
+ else
+ {
+ //long nKernAdd = rInf.GetKern();
+ long nKernAdd = 0;
+ long nGridAddSum = nGridWidthAdd + nKernAdd;
+ for (sal_Int32 i = 0; i < sal_Int32(rInf.GetLen());
+ i++, nGridAddSum += nGridWidthAdd + nKernAdd)
+ {
+ pKernArray[i] += nGridAddSum;
+ }
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
+ pKernArray.get(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ }
+ return;
+ }
+ }
+
+ // DIRECT PAINTING WITHOUT SCREEN ADJUSTMENT
+
+ if ( bDirectPrint )
+ {
+ const Fraction aTmp( 1, 1 );
+ bool bStretch = rInf.GetWidth() && (rInf.GetLen() > TextFrameIndex(1)) && bPrt
+ && ( aTmp != rInf.GetOut().GetMapMode().GetScaleX() );
+
+ if ( bSwitchL2R )
+ rInf.GetFrame()->SwitchLTRtoRTL( aTextOriginPos );
+
+ if ( bSwitchH2V )
+ rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
+
+ // In the good old days we used to have a simple DrawText if the
+ // output device is the printer. Now we need a DrawTextArray if
+ // 1. KanaCompression is enabled
+ // 2. Justified alignment
+ // Simple kerning is handled by DrawStretchText
+ if( rInf.GetSpace() || rInf.GetKanaComp() )
+ {
+ std::unique_ptr<long[]> pKernArray(new long[sal_Int32(rInf.GetLen())]);
+ rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+
+ if( bStretch )
+ {
+ sal_Int32 nZwi = sal_Int32(rInf.GetLen()) - 1;
+ long nDiff = rInf.GetWidth() - pKernArray[ nZwi ]
+ - sal_Int32(rInf.GetLen()) * rInf.GetKern();
+ long nRest = nDiff % nZwi;
+ long nAdd;
+ if( nRest < 0 )
+ {
+ nAdd = -1;
+ nRest += nZwi;
+ }
+ else
+ {
+ nAdd = +1;
+ nRest = nZwi - nRest;
+ }
+ nDiff /= nZwi;
+ long nSum = nDiff;
+ for( sal_Int32 i = 0; i < nZwi; )
+ {
+ pKernArray[ i ] += nSum;
+ if( ++i == nRest )
+ nDiff += nAdd;
+ nSum += nDiff;
+ }
+ }
+
+ // Modify Array for special justifications
+
+ long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
+ bool bSpecialJust = false;
+
+ if ( rInf.GetFont() && rInf.GetLen() )
+ {
+ const SwScriptInfo* pSI = rInf.GetScriptInfo();
+ const SwFontScript nActual = rInf.GetFont()->GetActual();
+
+ // Kana Compression
+ if ( SwFontScript::CJK == nActual && rInf.GetKanaComp() &&
+ pSI && pSI->CountCompChg() &&
+ lcl_IsMonoSpaceFont( rInf.GetOut() ) )
+ {
+ pSI->Compress( pKernArray.get(), rInf.GetIdx(), rInf.GetLen(),
+ rInf.GetKanaComp(),
+ static_cast<sal_uInt16>(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered( rInf.GetOut() ), &aTextOriginPos );
+ bSpecialJust = true;
+ }
+
+ // Asian Justification
+ if ( SwFontScript::CJK == nActual && nSpaceAdd )
+ {
+ LanguageType aLang = rInf.GetFont()->GetLanguage( SwFontScript::CJK );
+
+ if (!MsLangId::isKorean(aLang))
+ {
+ SwScriptInfo::CJKJustify( rInf.GetText(), pKernArray.get(), nullptr,
+ rInf.GetIdx(), rInf.GetLen(), aLang, nSpaceAdd, rInf.IsSpaceStop() );
+
+ bSpecialJust = true;
+ nSpaceAdd = 0;
+ }
+ }
+
+ // Kashida Justification
+ if ( SwFontScript::CTL == nActual && nSpaceAdd )
+ {
+ if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) )
+ {
+ if ( pSI && pSI->CountKashida() &&
+ pSI->KashidaJustify( pKernArray.get(), nullptr, rInf.GetIdx(),
+ rInf.GetLen(), nSpaceAdd ) != -1 )
+ {
+ bSpecialJust = true;
+ nSpaceAdd = 0;
+ }
+ }
+ }
+
+ // Thai Justification
+ if ( SwFontScript::CTL == nActual && nSpaceAdd )
+ {
+ LanguageType aLang = rInf.GetFont()->GetLanguage( SwFontScript::CTL );
+
+ if ( LANGUAGE_THAI == aLang )
+ {
+ // Use rInf.GetSpace() because it has more precision than
+ // nSpaceAdd:
+ SwScriptInfo::ThaiJustify( rInf.GetText(), pKernArray.get(), nullptr,
+ rInf.GetIdx(), rInf.GetLen(),
+ rInf.GetNumberOfBlanks(),
+ rInf.GetSpace() );
+
+ // adding space to blanks is already done
+ bSpecialJust = true;
+ nSpaceAdd = 0;
+ }
+ }
+ }
+
+ long nKernSum = rInf.GetKern();
+
+ if ( bStretch || m_bPaintBlank || rInf.GetKern() || bSpecialJust )
+ {
+ for (sal_Int32 i = 0; i < sal_Int32(rInf.GetLen()); i++,
+ nKernSum += rInf.GetKern() )
+ {
+ if (CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx()) + i])
+ nKernSum += nSpaceAdd;
+ pKernArray[i] += nKernSum;
+ }
+
+ // In case of underlined/strike-through justified text
+ // a blank at the end requires special handling:
+ if( m_bPaintBlank && rInf.GetLen() && ( CH_BLANK ==
+ rInf.GetText()[sal_Int32(rInf.GetIdx() + rInf.GetLen())-1]))
+ {
+ // If it is a single underlined space, output 2 spaces:
+ if (TextFrameIndex(1) == rInf.GetLen())
+ {
+ pKernArray[0] = rInf.GetWidth() + nSpaceAdd;
+
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
+ pKernArray.get(), sal_Int32(rInf.GetIdx()), 1 );
+ }
+ else
+ {
+ pKernArray[ sal_Int32(rInf.GetLen()) - 2 ] += nSpaceAdd;
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
+ pKernArray.get(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ }
+ }
+ else
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
+ pKernArray.get(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ }
+ else
+ {
+ Point aTmpPos( aTextOriginPos );
+ sal_Int32 j = 0;
+ sal_Int32 i;
+ for( i = 0; i < sal_Int32(rInf.GetLen()); i++ )
+ {
+ if (CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx()) + i])
+ {
+ nKernSum += nSpaceAdd;
+ if( j < i )
+ rInf.GetOut().DrawText( aTmpPos, rInf.GetText(),
+ sal_Int32(rInf.GetIdx()) + j, i - j);
+ j = i + 1;
+ SwTwips nAdd = pKernArray[ i ] + nKernSum;
+ if ( ( ComplexTextLayoutFlags::BiDiStrong | ComplexTextLayoutFlags::BiDiRtl ) == nMode )
+ nAdd *= -1;
+ aTmpPos.setX( aTextOriginPos.X() + nAdd );
+ }
+ }
+ if( j < i )
+ rInf.GetOut().DrawText( aTmpPos, rInf.GetText(),
+ sal_Int32(rInf.GetIdx()) + j, i - j);
+ }
+ }
+ else if( bStretch )
+ {
+ long nTmpWidth = rInf.GetWidth();
+ if( rInf.GetKern() && rInf.GetLen() && nTmpWidth > rInf.GetKern() )
+ nTmpWidth -= rInf.GetKern();
+ rInf.GetOut().DrawStretchText( aTextOriginPos, nTmpWidth,
+ rInf.GetText(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ }
+ else if( rInf.GetKern() )
+ {
+ const long nTmpWidth = GetTextSize( rInf ).Width();
+
+ const Color aSaveColor( pTmpFont->GetColor() );
+ const bool bColorChanged = rInf.ApplyAutoColor( pTmpFont );
+
+ if( bColorChanged )
+ {
+ if( !pTmpFont->IsSameInstance( rInf.GetOut().GetFont() ) )
+ rInf.GetOut().SetFont( *pTmpFont );
+ pTmpFont->SetColor( aSaveColor );
+ }
+
+ rInf.GetOut().DrawStretchText( aTextOriginPos, nTmpWidth,
+ rInf.GetText(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ }
+ else
+ rInf.GetOut().DrawText( aTextOriginPos, rInf.GetText(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ }
+
+ // PAINTING WITH FORMATTING DEVICE/SCREEN ADJUSTMENT
+
+ else
+ {
+ const OUString* pStr = &rInf.GetText();
+
+ OUString aStr;
+ OUString aBulletOverlay;
+ bool bBullet = rInf.GetBullet();
+ if( m_bSymbol )
+ bBullet = false;
+ std::unique_ptr<long[]> pKernArray(new long[sal_Int32(rInf.GetLen())]);
+ CreateScrFont( *rInf.GetShell(), rInf.GetOut() );
+ long nScrPos;
+
+ // get screen array
+ std::unique_ptr<long[]> pScrArray(new long[sal_Int32(rInf.GetLen())]);
+ SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) };
+ SalLayoutGlyphs* pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]);
+ rInf.GetOut().GetTextArray( rInf.GetText(), pScrArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs);
+
+ // OLE: no printer available
+ // OSL_ENSURE( pPrinter, "DrawText needs pPrinter" )
+ if ( m_pPrinter )
+ {
+ // pTmpFont has already been set as current font for rInf.GetOut()
+ if ( m_pPrinter.get() != rInf.GetpOut() || pTmpFont != m_pPrtFont )
+ {
+ if( !m_pPrtFont->IsSameInstance( m_pPrinter->GetFont() ) )
+ m_pPrinter->SetFont( *m_pPrtFont );
+ }
+ aGlyphsKey = SwTextGlyphsKey{ m_pPrinter, rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) };
+ pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]);
+ m_pPrinter->GetTextArray(rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs);
+ }
+ else
+ {
+ rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ }
+
+ // Modify Printer and ScreenArrays for special justifications
+
+ long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
+ bool bNoHalfSpace = false;
+
+ if ( rInf.GetFont() && rInf.GetLen() )
+ {
+ const SwFontScript nActual = rInf.GetFont()->GetActual();
+ const SwScriptInfo* pSI = rInf.GetScriptInfo();
+
+ // Kana Compression
+ if ( SwFontScript::CJK == nActual && rInf.GetKanaComp() &&
+ pSI && pSI->CountCompChg() &&
+ lcl_IsMonoSpaceFont( rInf.GetOut() ) )
+ {
+ Point aTmpPos( aTextOriginPos );
+ pSI->Compress( pScrArray.get(), rInf.GetIdx(), rInf.GetLen(),
+ rInf.GetKanaComp(),
+ static_cast<sal_uInt16>(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered( rInf.GetOut() ), &aTmpPos );
+ pSI->Compress( pKernArray.get(), rInf.GetIdx(), rInf.GetLen(),
+ rInf.GetKanaComp(),
+ static_cast<sal_uInt16>(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered( rInf.GetOut() ), &aTextOriginPos );
+ }
+
+ // Asian Justification
+ if ( SwFontScript::CJK == nActual && nSpaceAdd )
+ {
+ LanguageType aLang = rInf.GetFont()->GetLanguage( SwFontScript::CJK );
+
+ if (!MsLangId::isKorean(aLang))
+ {
+ SwScriptInfo::CJKJustify( rInf.GetText(), pKernArray.get(), pScrArray.get(),
+ rInf.GetIdx(), rInf.GetLen(), aLang, nSpaceAdd, rInf.IsSpaceStop() );
+
+ nSpaceAdd = 0;
+ }
+ }
+
+ // Kashida Justification
+ if ( SwFontScript::CTL == nActual && nSpaceAdd )
+ {
+ if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) )
+ {
+ if ( pSI && pSI->CountKashida() &&
+ pSI->KashidaJustify( pKernArray.get(), pScrArray.get(), rInf.GetIdx(),
+ rInf.GetLen(), nSpaceAdd ) != -1 )
+ nSpaceAdd = 0;
+ else
+ bNoHalfSpace = true;
+ }
+ }
+
+ // Thai Justification
+ if ( SwFontScript::CTL == nActual && nSpaceAdd )
+ {
+ LanguageType aLang = rInf.GetFont()->GetLanguage( SwFontScript::CTL );
+
+ if ( LANGUAGE_THAI == aLang )
+ {
+ SwScriptInfo::ThaiJustify( rInf.GetText(), pKernArray.get(),
+ pScrArray.get(), rInf.GetIdx(),
+ rInf.GetLen(),
+ rInf.GetNumberOfBlanks(),
+ rInf.GetSpace() );
+
+ // adding space to blanks is already done
+ nSpaceAdd = 0;
+ }
+ }
+ }
+
+ nScrPos = pScrArray[ 0 ];
+
+ if( bBullet )
+ {
+ // !!! HACK !!!
+ // The Arabic layout engine requires some context of the string
+ // which should be painted.
+ sal_Int32 nCopyStart = sal_Int32(rInf.GetIdx());
+ if ( nCopyStart )
+ --nCopyStart;
+
+ sal_Int32 nCopyLen = sal_Int32(rInf.GetLen());
+ if ( nCopyStart + nCopyLen < rInf.GetText().getLength() )
+ ++nCopyLen;
+
+ aStr = rInf.GetText().copy( nCopyStart, nCopyLen );
+ pStr = &aStr;
+
+ aBulletOverlay = rInf.GetText().copy( nCopyStart, nCopyLen );
+
+ for( sal_Int32 i = 0; i < aBulletOverlay.getLength(); ++i )
+ if( CH_BLANK == aBulletOverlay[ i ] )
+ {
+ /* fdo#72488 Hack: try to see if the space is zero width
+ * and don't bother with inserting a bullet in this case.
+ */
+ if ((i + nCopyStart + 1 >= sal_Int32(rInf.GetLen())) ||
+ pKernArray[i + nCopyStart] != pKernArray[ i + nCopyStart + 1])
+ {
+ aBulletOverlay = aBulletOverlay.replaceAt(i, 1, OUString(CH_BULLET));
+ }
+ else
+ {
+ aBulletOverlay = aBulletOverlay.replaceAt(i, 1, OUString(CH_BLANK));
+ }
+ }
+ else
+ {
+ aBulletOverlay = aBulletOverlay.replaceAt(i, 1, OUString(CH_BLANK));
+ }
+ }
+
+ TextFrameIndex nCnt(rInf.GetText().getLength());
+ if ( nCnt < rInf.GetIdx() )
+ assert(false); // layout bug, not handled below
+ else
+ nCnt = nCnt - rInf.GetIdx();
+ nCnt = std::min(nCnt, rInf.GetLen());
+ long nKernSum = rInf.GetKern();
+ sal_Unicode cChPrev = rInf.GetText()[sal_Int32(rInf.GetIdx())];
+
+ // In case of a single underlined space in justified text,
+ // have to output 2 spaces:
+ if ((nCnt == TextFrameIndex(1)) && rInf.GetSpace() && (cChPrev == CH_BLANK))
+ {
+ pKernArray[0] = rInf.GetWidth() +
+ rInf.GetKern() +
+ ( rInf.GetSpace() / SPACING_PRECISION_FACTOR );
+
+ if ( bSwitchL2R )
+ rInf.GetFrame()->SwitchLTRtoRTL( aTextOriginPos );
+
+ if ( bSwitchH2V )
+ rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
+
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
+ pKernArray.get(), sal_Int32(rInf.GetIdx()), 1 );
+ if( bBullet )
+ rInf.GetOut().DrawTextArray( aTextOriginPos, *pStr, pKernArray.get(),
+ rInf.GetIdx() ? 1 : 0, 1 );
+ }
+ else
+ {
+ sal_Unicode nCh;
+
+ // In case of Pair Kerning the printer influence on the positioning
+ // grows
+ const int nMul = m_pPrtFont->GetKerning() != FontKerning::NONE ? 1 : 3;
+ const int nDiv = nMul+1;
+
+ // nSpaceSum contains the sum of the intermediate space distributed
+ // among Spaces by the Justification.
+ // The Spaces themselves will be positioned in the middle of the
+ // intermediate space, hence the nSpace/2.
+ // In case of word-by-word underlining they have to be positioned
+ // at the beginning of the intermediate space, so that the space
+ // is not underlined.
+ // A Space at the beginning or end of the text must be positioned
+ // before (resp. after) the whole intermediate space, otherwise
+ // the underline/strike-through would have gaps.
+ long nSpaceSum = 0;
+ // in word line mode and for Arabic, we disable the half space trick:
+ const long nHalfSpace = m_pPrtFont->IsWordLineMode() || bNoHalfSpace ? 0 : nSpaceAdd / 2;
+ const long nOtherHalf = nSpaceAdd - nHalfSpace;
+ if ( nSpaceAdd && ( cChPrev == CH_BLANK ) )
+ nSpaceSum = nHalfSpace;
+ for (sal_Int32 i = 1; i < sal_Int32(nCnt); ++i, nKernSum += rInf.GetKern())
+ {
+ nCh = rInf.GetText()[sal_Int32(rInf.GetIdx()) + i];
+
+ OSL_ENSURE( pScrArray, "Where is the screen array?" );
+ long nScr;
+ nScr = pScrArray[ i ] - pScrArray[ i - 1 ];
+
+ // If there is an (ex-)Space before us, position optimally,
+ // i.e., our right margin to the 100% printer position;
+ // if we _are_ an ex-Space, position us left-aligned to the
+ // printer position.
+ if ( nCh == CH_BLANK )
+ {
+ nScrPos = pKernArray[i-1] + nScr;
+
+ if ( cChPrev == CH_BLANK )
+ nSpaceSum += nOtherHalf;
+ if (i + 1 == sal_Int32(nCnt))
+ nSpaceSum += nSpaceAdd;
+ else
+ nSpaceSum += nHalfSpace;
+ }
+ else
+ {
+ if ( cChPrev == CH_BLANK )
+ {
+ nScrPos = pKernArray[i-1] + nScr;
+ // no Pixel is lost:
+ nSpaceSum += nOtherHalf;
+ }
+ else if ( cChPrev == '-' )
+ nScrPos = pKernArray[i-1] + nScr;
+ else
+ {
+ nScrPos += nScr;
+ nScrPos = ( nMul * nScrPos + pKernArray[i] ) / nDiv;
+ }
+ }
+ cChPrev = nCh;
+ pKernArray[i-1] = nScrPos - nScr + nKernSum + nSpaceSum;
+ // In word line mode and for Arabic, we disabled the half space trick. If a portion
+ // ends with a blank, the full nSpaceAdd value has been added to the character in
+ // front of the blank. This leads to painting artifacts, therefore we remove the
+ // nSpaceAdd value again:
+ if ((bNoHalfSpace || m_pPrtFont->IsWordLineMode()) && i+1 == sal_Int32(nCnt) && nCh == CH_BLANK)
+ pKernArray[i-1] = pKernArray[i-1] - nSpaceAdd;
+ }
+
+ // the layout engine requires the total width of the output
+ pKernArray[sal_Int32(rInf.GetLen()) - 1] += nKernSum + nSpaceSum;
+
+ if( rInf.GetGreyWave() )
+ {
+ if( rInf.GetLen() )
+ {
+ long nHght = rInf.GetOut().LogicToPixel(
+ m_pPrtFont->GetFontSize() ).Height();
+ if( WRONG_SHOW_MIN < nHght )
+ {
+ if ( rInf.GetOut().GetConnectMetaFile() )
+ rInf.GetOut().Push();
+
+ Color aCol( rInf.GetOut().GetLineColor() );
+ bool bColSave = aCol != gWaveCol;
+ if ( bColSave )
+ rInf.GetOut().SetLineColor( gWaveCol );
+
+ Point aEnd;
+ long nKernVal = pKernArray[sal_Int32(rInf.GetLen()) - 1];
+
+ const sal_uInt16 nDir = bBidiPor
+ ? 1800
+ : UnMapDirection(GetFont().GetOrientation(),
+ bSwitchH2V, bSwitchH2VLRBT);
+
+ switch ( nDir )
+ {
+ case 0 :
+ aEnd.setX( rInf.GetPos().X() + nKernVal );
+ aEnd.setY( rInf.GetPos().Y() );
+ break;
+ case 900 :
+ aEnd.setX( rInf.GetPos().X() );
+ aEnd.setY( rInf.GetPos().Y() - nKernVal );
+ break;
+ case 1800 :
+ aEnd.setX( rInf.GetPos().X() - nKernVal );
+ aEnd.setY( rInf.GetPos().Y() );
+ break;
+ case 2700 :
+ aEnd.setX( rInf.GetPos().X() );
+ aEnd.setY( rInf.GetPos().Y() + nKernVal );
+ break;
+ }
+
+ Point aCurrPos( rInf.GetPos() );
+
+ if ( bSwitchL2R )
+ {
+ rInf.GetFrame()->SwitchLTRtoRTL( aCurrPos );
+ rInf.GetFrame()->SwitchLTRtoRTL( aEnd );
+ }
+
+ if ( bSwitchH2V )
+ {
+ rInf.GetFrame()->SwitchHorizontalToVertical( aCurrPos );
+ rInf.GetFrame()->SwitchHorizontalToVertical( aEnd );
+ }
+ {
+ vcl::ScopedAntialiasing a(rInf.GetOut(), true);
+ rInf.GetOut().DrawWaveLine( aCurrPos, aEnd );
+ }
+ if ( bColSave )
+ rInf.GetOut().SetLineColor( aCol );
+
+ if ( rInf.GetOut().GetConnectMetaFile() )
+ rInf.GetOut().Pop();
+ }
+ }
+ }
+ else if( !m_bSymbol && rInf.GetLen() )
+ {
+ // anything to do?
+ if (rInf.GetWrong() || rInf.GetGrammarCheck() || rInf.GetSmartTags())
+ {
+ CalcLinePosData aCalcLinePosData(rInf, GetFont(), nCnt, bSwitchH2V,
+ bSwitchH2VLRBT, bSwitchL2R, nHalfSpace,
+ pKernArray.get(), bBidiPor);
+
+ SwForbidden aForbidden;
+ // draw line for smart tag data
+ lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetSmartTags(), aCalcLinePosData, Size() );
+ // draw wave line for spell check errors
+ // draw them BEFORE the grammar check lines to 'override' the latter in case of conflict.
+ // reason: some grammar errors can only be found if spelling errors are fixed,
+ // therefore we don't want the user to miss a spelling error.
+ lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetWrong(), aCalcLinePosData, m_pPrtFont->GetFontSize() );
+ // draw wave line for grammar check errors
+ lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetGrammarCheck(), aCalcLinePosData, m_pPrtFont->GetFontSize() );
+ }
+ }
+
+ sal_Int32 nLen = sal_Int32(rInf.GetLen());
+
+ if( nLen > 0 )
+ {
+
+ if ( bSwitchL2R )
+ rInf.GetFrame()->SwitchLTRtoRTL( aTextOriginPos );
+
+ if ( bSwitchH2V )
+ rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
+
+ // If we paint bullets instead of spaces, we use a copy of
+ // the paragraph string. For the layout engine, the copy
+ // of the string has to be an environment of the range which
+ // is painted
+ sal_Int32 nTmpIdx = bBullet
+ ? (rInf.GetIdx() ? 1 : 0)
+ : sal_Int32(rInf.GetIdx());
+ aGlyphsKey = SwTextGlyphsKey{ &rInf.GetOut(), *pStr, nTmpIdx, nLen };
+ pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]);
+ rInf.GetOut().DrawTextArray( aTextOriginPos, *pStr, pKernArray.get(),
+ nTmpIdx , nLen, SalLayoutFlags::NONE, pGlyphs );
+ if (bBullet)
+ {
+ rInf.GetOut().Push();
+ Color aPreviousColor = pTmpFont->GetColor();
+
+ FontLineStyle aPreviousUnderline = pTmpFont->GetUnderline();
+ FontLineStyle aPreviousOverline = pTmpFont->GetOverline();
+ FontStrikeout aPreviousStrikeout = pTmpFont->GetStrikeout();
+
+ pTmpFont->SetColor( NON_PRINTING_CHARACTER_COLOR );
+ pTmpFont->SetUnderline(LINESTYLE_NONE);
+ pTmpFont->SetOverline(LINESTYLE_NONE);
+ pTmpFont->SetStrikeout(STRIKEOUT_NONE);
+ rInf.GetOut().SetFont( *pTmpFont );
+ long nShift = rInf.GetOut( ).GetFontMetric( ).GetBulletOffset( );
+ if ( nShift )
+ {
+ long nAdd = 0;
+
+ if (aBulletOverlay.getLength() > nTmpIdx &&
+ aBulletOverlay[ nTmpIdx ] == CH_BULLET )
+ {
+ if (bSwitchH2V)
+ aTextOriginPos.AdjustY(nShift ) ;
+ else
+ aTextOriginPos.AdjustX(nShift ) ;
+ nAdd = nShift ;
+ }
+ for( sal_Int32 i = 1 ; i < nLen ; ++i )
+ {
+ if ( aBulletOverlay[ i + nTmpIdx ] == CH_BULLET )
+ pKernArray [ i - 1 ] += nShift ;
+ if ( nAdd )
+ pKernArray [ i - 1 ] -= nAdd;
+ }
+ }
+ rInf.GetOut().DrawTextArray( aTextOriginPos, aBulletOverlay, pKernArray.get(),
+ nTmpIdx , nLen );
+ pTmpFont->SetColor( aPreviousColor );
+
+ pTmpFont->SetUnderline(aPreviousUnderline);
+ pTmpFont->SetOverline(aPreviousOverline);
+ pTmpFont->SetStrikeout(aPreviousStrikeout);
+ rInf.GetOut().Pop();
+ }
+ }
+ }
+ }
+}
+
+Size SwFntObj::GetTextSize( SwDrawTextInfo& rInf )
+{
+ Size aTextSize;
+ const TextFrameIndex nLn = (TextFrameIndex(COMPLETE_STRING) != rInf.GetLen())
+ ? rInf.GetLen()
+ : TextFrameIndex(rInf.GetText().getLength());
+
+ // be sure to have the correct layout mode at the printer
+ if ( m_pPrinter )
+ {
+ m_pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() );
+ m_pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() );
+ }
+
+ if ( rInf.GetFrame() && nLn && rInf.SnapToGrid() && rInf.GetFont() &&
+ SwFontScript::CJK == rInf.GetFont()->GetActual() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
+ if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars() )
+ {
+ const SwDoc* pDoc = rInf.GetShell()->GetDoc();
+ const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
+
+ OutputDevice* pOutDev;
+
+ if ( m_pPrinter )
+ {
+ if( !m_pPrtFont->IsSameInstance( m_pPrinter->GetFont() ) )
+ m_pPrinter->SetFont(*m_pPrtFont);
+ pOutDev = m_pPrinter;
+ }
+ else
+ pOutDev = rInf.GetpOut();
+
+ aTextSize.setWidth( pOutDev->GetTextWidth(rInf.GetText(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(nLn)) );
+
+ OSL_ENSURE( !rInf.GetShell() ||
+ ( USHRT_MAX != GetGuessedLeading() && USHRT_MAX != GetExternalLeading() ),
+ "Leading values should be already calculated" );
+ aTextSize.setHeight( pOutDev->GetTextHeight() +
+ GetFontLeading( rInf.GetShell(), rInf.GetOut() ) );
+
+ long nAvgWidthPerChar = aTextSize.Width() / sal_Int32(nLn);
+
+ const sal_uLong i = nAvgWidthPerChar ?
+ ( nAvgWidthPerChar - 1 ) / nGridWidth + 1:
+ 1;
+
+ aTextSize.setWidth(i * nGridWidth * sal_Int32(nLn));
+ rInf.SetKanaDiff( 0 );
+ return aTextSize;
+ }
+ }
+
+ //for textgrid refactor
+ if ( rInf.GetFrame() && nLn && rInf.SnapToGrid() && rInf.GetFont() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
+ if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() )
+ {
+ const long nGridWidthAdd = EvalGridWidthAdd( pGrid, rInf );
+ OutputDevice* pOutDev;
+ if ( m_pPrinter )
+ {
+ if( !m_pPrtFont->IsSameInstance( m_pPrinter->GetFont() ) )
+ m_pPrinter->SetFont(*m_pPrtFont);
+ pOutDev = m_pPrinter;
+ }
+ else
+ pOutDev = rInf.GetpOut();
+ aTextSize.setWidth(pOutDev->GetTextWidth(rInf.GetText(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(nLn)));
+ aTextSize.setHeight( pOutDev->GetTextHeight() +
+ GetFontLeading( rInf.GetShell(), rInf.GetOut() ) );
+ aTextSize.AdjustWidth(sal_Int32(nLn) * nGridWidthAdd);
+ //if ( rInf.GetKern() && nLn )
+ // aTextSize.Width() += ( nLn ) * long( rInf.GetKern() );
+
+ rInf.SetKanaDiff( 0 );
+ return aTextSize;
+ }
+ }
+
+ const bool bCompress = rInf.GetKanaComp() && nLn &&
+ rInf.GetFont() &&
+ SwFontScript::CJK == rInf.GetFont()->GetActual() &&
+ rInf.GetScriptInfo() &&
+ rInf.GetScriptInfo()->CountCompChg() &&
+ lcl_IsMonoSpaceFont( rInf.GetOut() );
+
+ OSL_ENSURE( !bCompress || ( rInf.GetScriptInfo() && rInf.GetScriptInfo()->
+ CountCompChg()), "Compression without info" );
+
+ // This is the part used e.g., for cursor travelling
+ // See condition for DrawText or DrawTextArray (bDirectPrint)
+ if ( m_pPrinter && m_pPrinter.get() != rInf.GetpOut() )
+ {
+ if( !m_pPrtFont->IsSameInstance( m_pPrinter->GetFont() ) )
+ m_pPrinter->SetFont(*m_pPrtFont);
+ aTextSize.setWidth( m_pPrinter->GetTextWidth( rInf.GetText(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(nLn)));
+ aTextSize.setHeight( m_pPrinter->GetTextHeight() );
+ std::unique_ptr<long[]> pKernArray(new long[sal_Int32(nLn)]);
+ CreateScrFont( *rInf.GetShell(), rInf.GetOut() );
+ if( !GetScrFont()->IsSameInstance( rInf.GetOut().GetFont() ) )
+ rInf.GetOut().SetFont( *m_pScrFont );
+ long nScrPos;
+
+ m_pPrinter->GetTextArray(rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(nLn));
+ if( bCompress )
+ rInf.SetKanaDiff( rInf.GetScriptInfo()->Compress( pKernArray.get(),
+ rInf.GetIdx(), nLn, rInf.GetKanaComp(),
+ static_cast<sal_uInt16>(m_aFont.GetFontSize().Height()) ,lcl_IsFullstopCentered( rInf.GetOut() ) ) );
+ else
+ rInf.SetKanaDiff( 0 );
+
+ if ( rInf.GetKanaDiff() )
+ nScrPos = pKernArray[ sal_Int32(nLn) - 1 ];
+ else
+ {
+ std::unique_ptr<long[]> pScrArray(new long[sal_Int32(rInf.GetLen())]);
+ rInf.GetOut().GetTextArray( rInf.GetText(), pScrArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ nScrPos = pScrArray[ 0 ];
+ TextFrameIndex nCnt(rInf.GetText().getLength());
+ if ( nCnt < rInf.GetIdx() )
+ nCnt = TextFrameIndex(0); // assert???
+ else
+ nCnt = nCnt - rInf.GetIdx();
+ nCnt = std::min(nCnt, nLn);
+ sal_Unicode nChPrev = rInf.GetText()[ sal_Int32(rInf.GetIdx()) ];
+
+ sal_Unicode nCh;
+
+ // In case of Pair Kerning the printer influence on the positioning
+ // grows
+ const int nMul = m_pPrtFont->GetKerning() != FontKerning::NONE ? 1 : 3;
+ const int nDiv = nMul+1;
+ for (sal_Int32 i = 1; i < sal_Int32(nCnt); i++)
+ {
+ nCh = rInf.GetText()[ sal_Int32(rInf.GetIdx()) + i ];
+ long nScr;
+ nScr = pScrArray[ i ] - pScrArray[ i - 1 ];
+ if ( nCh == CH_BLANK )
+ nScrPos = pKernArray[i-1]+nScr;
+ else
+ {
+ if ( nChPrev == CH_BLANK || nChPrev == '-' )
+ nScrPos = pKernArray[i-1]+nScr;
+ else
+ {
+ nScrPos += nScr;
+ nScrPos = ( nMul * nScrPos + pKernArray[i] ) / nDiv;
+ }
+ }
+ nChPrev = nCh;
+ pKernArray[i-1] = nScrPos - nScr;
+ }
+ }
+
+ pKernArray.reset();
+ aTextSize.setWidth( nScrPos );
+ }
+ else
+ {
+ if( !m_pPrtFont->IsSameInstance( rInf.GetOut().GetFont() ) )
+ rInf.GetOut().SetFont( *m_pPrtFont );
+ if( bCompress )
+ {
+ std::unique_ptr<long[]> pKernArray( new long[sal_Int32(nLn)] );
+ rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(nLn));
+ rInf.SetKanaDiff( rInf.GetScriptInfo()->Compress( pKernArray.get(),
+ rInf.GetIdx(), nLn, rInf.GetKanaComp(),
+ static_cast<sal_uInt16>(m_aFont.GetFontSize().Height()) ,lcl_IsFullstopCentered( rInf.GetOut() ) ) );
+ aTextSize.setWidth( pKernArray[sal_Int32(nLn) - 1] );
+ }
+ else
+ {
+ SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(nLn) };
+ SalLayoutGlyphs* pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]);
+ aTextSize.setWidth( rInf.GetOut().GetTextWidth( rInf.GetText(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(nLn),
+ rInf.GetVclCache(), pGlyphs) );
+ rInf.SetKanaDiff( 0 );
+ }
+
+ aTextSize.setHeight( rInf.GetOut().GetTextHeight() );
+ }
+
+ if ( rInf.GetKern() && nLn )
+ aTextSize.AdjustWidth((sal_Int32(nLn) - 1) * rInf.GetKern());
+
+ OSL_ENSURE( !rInf.GetShell() ||
+ ( USHRT_MAX != GetGuessedLeading() && USHRT_MAX != GetExternalLeading() ),
+ "Leading values should be already calculated" );
+ aTextSize.AdjustHeight(GetFontLeading( rInf.GetShell(), rInf.GetOut() ) );
+ return aTextSize;
+}
+
+TextFrameIndex SwFntObj::GetModelPositionForViewPoint(SwDrawTextInfo &rInf)
+{
+ long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
+ const long nSperren = -rInf.GetSperren() / SPACING_PRECISION_FACTOR;
+ long nKern = rInf.GetKern();
+
+ if( 0 != nSperren )
+ nKern -= nSperren;
+
+ std::unique_ptr<long[]> pKernArray(new long[sal_Int32(rInf.GetLen())]);
+
+ // be sure to have the correct layout mode at the printer
+ if ( m_pPrinter )
+ {
+ m_pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() );
+ m_pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() );
+ SwTextGlyphsKey aGlyphsKey{ m_pPrinter, rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) };
+ SalLayoutGlyphs* pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]);
+ m_pPrinter->GetTextArray( rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs);
+ }
+ else
+ rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+
+ const SwScriptInfo* pSI = rInf.GetScriptInfo();
+ if ( rInf.GetFont() && rInf.GetLen() )
+ {
+ const SwFontScript nActual = rInf.GetFont()->GetActual();
+
+ // Kana Compression
+ if ( SwFontScript::CJK == nActual && rInf.GetKanaComp() &&
+ pSI && pSI->CountCompChg() &&
+ lcl_IsMonoSpaceFont( rInf.GetOut() ) )
+ {
+ pSI->Compress( pKernArray.get(), rInf.GetIdx(), rInf.GetLen(),
+ rInf.GetKanaComp(),
+ static_cast<sal_uInt16>(m_aFont.GetFontSize().Height()),
+ lcl_IsFullstopCentered( rInf.GetOut() ) );
+ }
+
+ // Asian Justification
+ if ( SwFontScript::CJK == rInf.GetFont()->GetActual() )
+ {
+ LanguageType aLang = rInf.GetFont()->GetLanguage( SwFontScript::CJK );
+
+ if (!MsLangId::isKorean(aLang))
+ {
+ SwScriptInfo::CJKJustify( rInf.GetText(), pKernArray.get(), nullptr,
+ rInf.GetIdx(), rInf.GetLen(), aLang, nSpaceAdd, rInf.IsSpaceStop() );
+
+ nSpaceAdd = 0;
+ }
+
+ }
+
+ // Kashida Justification
+ if ( SwFontScript::CTL == nActual && rInf.GetSpace() )
+ {
+ if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) )
+ {
+ if ( pSI && pSI->CountKashida() &&
+ pSI->KashidaJustify( pKernArray.get(), nullptr, rInf.GetIdx(), rInf.GetLen(),
+ nSpaceAdd ) != -1 )
+ nSpaceAdd = 0;
+ }
+ }
+
+ // Thai Justification
+ if ( SwFontScript::CTL == nActual && nSpaceAdd )
+ {
+ LanguageType aLang = rInf.GetFont()->GetLanguage( SwFontScript::CTL );
+
+ if ( LANGUAGE_THAI == aLang )
+ {
+ SwScriptInfo::ThaiJustify( rInf.GetText(), pKernArray.get(), nullptr,
+ rInf.GetIdx(), rInf.GetLen(),
+ rInf.GetNumberOfBlanks(),
+ rInf.GetSpace() );
+
+ // adding space to blanks is already done
+ nSpaceAdd = 0;
+ }
+ }
+ }
+
+ long nLeft = 0;
+ long nRight = 0;
+ TextFrameIndex nCnt(0);
+ long nSpaceSum = 0;
+ long nKernSum = 0;
+
+ if ( rInf.GetFrame() && rInf.GetLen() && rInf.SnapToGrid() &&
+ rInf.GetFont() && SwFontScript::CJK == rInf.GetFont()->GetActual() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
+ if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars() )
+ {
+ const SwDoc* pDoc = rInf.GetShell()->GetDoc();
+ const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
+
+ long nAvgWidthPerChar = pKernArray[sal_Int32(rInf.GetLen()) - 1] / sal_Int32(rInf.GetLen());
+
+ sal_uLong i = nAvgWidthPerChar ?
+ ( nAvgWidthPerChar - 1 ) / nGridWidth + 1:
+ 1;
+
+ nAvgWidthPerChar = i * nGridWidth;
+
+// stupid CLANG
+ nCnt = TextFrameIndex(rInf.GetOffset() / nAvgWidthPerChar);
+ if (2 * (rInf.GetOffset() - sal_Int32(nCnt) * nAvgWidthPerChar) > nAvgWidthPerChar)
+ ++nCnt;
+
+ return nCnt;
+ }
+ }
+
+ //for textgrid refactor
+ if ( rInf.GetFrame() && rInf.GetLen() && rInf.SnapToGrid() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
+ if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() )
+ {
+
+ const long nGridWidthAdd = EvalGridWidthAdd( pGrid, rInf );
+
+ for (TextFrameIndex j(0); j < rInf.GetLen(); j++)
+ {
+ long nScr = pKernArray[sal_Int32(j)] + (nSpaceAdd + nGridWidthAdd) * (sal_Int32(j) + 1);
+ if( nScr >= rInf.GetOffset())
+ {
+ nCnt = j;
+ break;
+ }
+ }
+ return nCnt;
+ }
+ }
+
+ sal_Int32 nDone = 0;
+ TextFrameIndex nIdx = rInf.GetIdx();
+ TextFrameIndex nLastIdx = nIdx;
+ const TextFrameIndex nEnd = rInf.GetIdx() + rInf.GetLen();
+
+ // #i105901#
+ // skip character cells for all script types
+ LanguageType aLang = rInf.GetFont()->GetLanguage();
+
+ while ( ( nRight < long( rInf.GetOffset() ) ) && ( nIdx < nEnd ) )
+ {
+ if (nSpaceAdd && CH_BLANK == rInf.GetText()[ sal_Int32(nIdx)])
+ nSpaceSum += nSpaceAdd;
+
+ // go to next character (cell).
+ nLastIdx = nIdx;
+
+ nIdx = TextFrameIndex(g_pBreakIt->GetBreakIter()->nextCharacters(
+ rInf.GetText(), sal_Int32(nIdx),
+ g_pBreakIt->GetLocale( aLang ),
+ i18n::CharacterIteratorMode::SKIPCELL, 1, nDone));
+ if ( nIdx <= nLastIdx )
+ break;
+
+ nLeft = nRight;
+ nRight = pKernArray[sal_Int32(nIdx - rInf.GetIdx()) - 1] + nKernSum + nSpaceSum;
+
+ nKernSum += nKern;
+ }
+
+ // step back if position is before the middle of the character
+ // or if we do not want to go to the next character
+ if ( nIdx > rInf.GetIdx() &&
+ ( rInf.IsPosMatchesBounds() ||
+ ( ( nRight > long( rInf.GetOffset() ) ) &&
+ ( nRight - rInf.GetOffset() > rInf.GetOffset() - nLeft ) ) ) )
+ nCnt = nLastIdx - rInf.GetIdx(); // first half
+ else
+ nCnt = nIdx - rInf.GetIdx(); // second half
+
+ if ( pSI )
+ rInf.SetCursorBidiLevel( pSI->DirType( nLastIdx ) );
+
+ return nCnt;
+}
+
+SwFntAccess::SwFntAccess( const void* & rnFontCacheId,
+ sal_uInt16 &rIndex, const void *pOwn, SwViewShell const *pSh,
+ bool bCheck ) :
+ SwCacheAccess( *pFntCache, rnFontCacheId, rIndex ),
+ m_pShell( pSh )
+{
+ // the used ctor of SwCacheAccess searches for rnFontCacheId+rIndex in the cache
+ if ( m_pObj )
+ {
+ // fast case: known Font (rnFontCacheId), no need to check printer and zoom
+ if ( !bCheck )
+ return;
+
+ // Font is known, but has to be checked
+ }
+ else
+ { // Font not known, must be searched
+ bCheck = false;
+ }
+
+ {
+ OutputDevice* pOut = nullptr;
+ sal_uInt16 nZoom = USHRT_MAX;
+
+ // Get the reference device
+ if ( pSh )
+ {
+ pOut = &pSh->GetRefDev();
+ nZoom = pSh->GetViewOptions()->GetZoom();
+ }
+
+ SwFntObj *pFntObj;
+ if ( bCheck )
+ {
+ pFntObj = Get();
+ if ( ( pFntObj->GetZoom( ) == nZoom ) &&
+ ( pFntObj->m_pPrinter == pOut ) &&
+ pFntObj->GetPropWidth() ==
+ static_cast<SwSubFont const *>(pOwn)->GetPropWidth() )
+ {
+ return; // result of Check: Drucker+Zoom okay.
+ }
+ pFntObj->Unlock(); // forget this object, printer/zoom differs
+ m_pObj = nullptr;
+ }
+
+ // Search by font comparison, quite expensive!
+ // Look for same font and same printer
+ pFntObj = pFntCache->First();
+ while ( pFntObj && !( pFntObj->m_aFont == *static_cast<vcl::Font const *>(pOwn) &&
+ pFntObj->GetZoom() == nZoom &&
+ pFntObj->GetPropWidth() ==
+ static_cast<SwSubFont const *>(pOwn)->GetPropWidth() &&
+ ( !pFntObj->m_pPrinter || pFntObj->m_pPrinter == pOut ) ) )
+ pFntObj = SwFntCache::Next( pFntObj );
+
+ if( pFntObj && pFntObj->m_pPrinter.get() != pOut )
+ {
+ // found one without printer, let's see if there is one with
+ // the same printer as well
+ SwFntObj *pTmpObj = pFntObj;
+ while( pTmpObj && !( pTmpObj->m_aFont == *static_cast<vcl::Font const *>(pOwn) &&
+ pTmpObj->GetZoom()==nZoom && pTmpObj->m_pPrinter==pOut &&
+ pTmpObj->GetPropWidth() ==
+ static_cast<SwSubFont const *>(pOwn)->GetPropWidth() ) )
+ pTmpObj = SwFntCache::Next( pTmpObj );
+ if( pTmpObj )
+ pFntObj = pTmpObj;
+ }
+
+ if ( !pFntObj ) // Font has not been found, create one
+ {
+ // Have to create new Object, hence Owner must be a SwFont, later
+ // the Owner will be the "MagicNumber"
+ SwCacheAccess::m_pOwner = pOwn;
+ pFntObj = Get(); // will create via NewObj() and lock
+ OSL_ENSURE(pFntObj, "No Font, no Fun.");
+ }
+ else // Font has been found, so we lock it.
+ {
+ pFntObj->Lock();
+ if (pFntObj->m_pPrinter.get() != pOut) // if no printer is known by now
+ {
+ OSL_ENSURE( !pFntObj->m_pPrinter, "SwFntAccess: Printer Changed" );
+ pFntObj->CreatePrtFont( *pOut );
+ pFntObj->m_pPrinter = pOut;
+ pFntObj->m_pScrFont = nullptr;
+ pFntObj->m_nGuessedLeading = USHRT_MAX;
+ pFntObj->m_nExtLeading = USHRT_MAX;
+ pFntObj->m_nPrtAscent = USHRT_MAX;
+ pFntObj->m_nPrtHeight = USHRT_MAX;
+ }
+ m_pObj = pFntObj;
+ }
+
+ // no matter if new or found, now the Owner of the Object is a
+ // MagicNumber, and will be given to the SwFont, as well as the Index
+ // for later direct access
+ rnFontCacheId = reinterpret_cast<void*>(reinterpret_cast<sal_IntPtr>(pFntObj->GetOwner()));
+ SwCacheAccess::m_pOwner = pFntObj->GetOwner();
+ rIndex = pFntObj->GetCachePos();
+ }
+}
+
+SwCacheObj *SwFntAccess::NewObj( )
+{
+ // "MagicNumber" used to identify Fonts
+ static std::uintptr_t fontCacheIdCounter = 0;
+ // a new Font, a new "MagicNumber".
+ return new SwFntObj( *static_cast<SwSubFont const *>(m_pOwner), ++fontCacheIdCounter, m_pShell );
+}
+
+TextFrameIndex SwFont::GetTextBreak(SwDrawTextInfo const & rInf, long nTextWidth)
+{
+ ChgFnt( rInf.GetShell(), rInf.GetOut() );
+
+ const bool bCompress = rInf.GetKanaComp() && rInf.GetLen() &&
+ SwFontScript::CJK == GetActual() &&
+ rInf.GetScriptInfo() &&
+ rInf.GetScriptInfo()->CountCompChg() &&
+ lcl_IsMonoSpaceFont( rInf.GetOut() );
+
+ OSL_ENSURE( !bCompress || ( rInf.GetScriptInfo() && rInf.GetScriptInfo()->
+ CountCompChg()), "Compression without info" );
+
+ TextFrameIndex nTextBreak(0);
+ long nKern = 0;
+
+ TextFrameIndex nLn = rInf.GetLen() == TextFrameIndex(COMPLETE_STRING)
+ ? TextFrameIndex(rInf.GetText().getLength()) : rInf.GetLen();
+
+ if ( rInf.GetFrame() && nLn && rInf.SnapToGrid() &&
+ rInf.GetFont() && SwFontScript::CJK == rInf.GetFont()->GetActual() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
+ if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars() )
+ {
+ const SwDoc* pDoc = rInf.GetShell()->GetDoc();
+ const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
+
+ std::unique_ptr<long[]> pKernArray(new long[sal_Int32(rInf.GetLen())]);
+ rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+
+ long nAvgWidthPerChar = pKernArray[sal_Int32(rInf.GetLen()) - 1] / sal_Int32(rInf.GetLen());
+
+ const sal_uLong i = nAvgWidthPerChar ?
+ ( nAvgWidthPerChar - 1 ) / nGridWidth + 1:
+ 1;
+
+ nAvgWidthPerChar = i * nGridWidth;
+ long nCurrPos = nAvgWidthPerChar;
+
+ while( nTextBreak < rInf.GetLen() && nTextWidth >= nCurrPos )
+ {
+ nCurrPos += nAvgWidthPerChar;
+ ++nTextBreak;
+ }
+
+ return nTextBreak + rInf.GetIdx();
+ }
+ }
+
+ //for text grid enhancement
+ if ( rInf.GetFrame() && nLn && rInf.SnapToGrid() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
+ if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() )
+ {
+ const long nGridWidthAdd = EvalGridWidthAdd( pGrid, rInf );
+
+ std::unique_ptr<long[]> pKernArray(new long[sal_Int32(rInf.GetLen())] );
+ rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ long nCurrPos = pKernArray[sal_Int32(nTextBreak)] + nGridWidthAdd;
+ while (++nTextBreak < rInf.GetLen() && nTextWidth >= nCurrPos)
+ {
+ nCurrPos = pKernArray[sal_Int32(nTextBreak)] + nGridWidthAdd * (sal_Int32(nTextBreak) + 1);
+ }
+ return nTextBreak + rInf.GetIdx();
+ }
+ }
+
+ if( m_aSub[m_nActual].IsCapital() && nLn )
+ {
+ nTextBreak = GetCapitalBreak( rInf.GetShell(), rInf.GetpOut(),
+ rInf.GetScriptInfo(), rInf.GetText(), nTextWidth, rInf.GetIdx(),
+ nLn );
+ }
+ else
+ {
+ nKern = CheckKerning();
+
+ const OUString* pTmpText;
+ OUString aTmpText;
+ TextFrameIndex nTmpIdx;
+ TextFrameIndex nTmpLen;
+ bool bTextReplaced = false;
+
+ if ( !m_aSub[m_nActual].IsCaseMap() )
+ {
+ pTmpText = &rInf.GetText();
+ nTmpIdx = rInf.GetIdx();
+ nTmpLen = nLn;
+ }
+ else
+ {
+ const OUString aSnippet(rInf.GetText().copy(sal_Int32(rInf.GetIdx()), sal_Int32(nLn)));
+ aTmpText = m_aSub[m_nActual].CalcCaseMap( aSnippet );
+ const bool bTitle = SvxCaseMap::Capitalize == m_aSub[m_nActual].GetCaseMap();
+
+ // Uaaaaahhhh!!! In title case mode, we would get wrong results
+ if ( bTitle && nLn )
+ {
+ // check if rInf.GetIdx() is begin of word
+ if ( !g_pBreakIt->GetBreakIter()->isBeginWord(
+ rInf.GetText(), sal_Int32(rInf.GetIdx()),
+ g_pBreakIt->GetLocale( m_aSub[m_nActual].GetLanguage() ),
+ i18n::WordType::ANYWORD_IGNOREWHITESPACES ) )
+ {
+ // In this case, the beginning of aTmpText is wrong.
+ OUString aSnippetTmp(aSnippet.copy(0, 1));
+ aSnippetTmp = m_aSub[m_nActual].CalcCaseMap( aSnippetTmp );
+ aTmpText = aTmpText.replaceAt( 0, aSnippetTmp.getLength(), OUString(aSnippet[0]) );
+ }
+ }
+
+ pTmpText = &aTmpText;
+ nTmpIdx = TextFrameIndex(0);
+ nTmpLen = TextFrameIndex(aTmpText.getLength());
+ bTextReplaced = true;
+ }
+
+ if( rInf.GetHyphPos() ) {
+ sal_Int32 nHyphPos = sal_Int32(*rInf.GetHyphPos());
+ nTextBreak = TextFrameIndex(rInf.GetOut().GetTextBreak(
+ *pTmpText, nTextWidth,
+ u'-', nHyphPos,
+ sal_Int32(nTmpIdx), sal_Int32(nTmpLen),
+ nKern, rInf.GetVclCache()));
+ *rInf.GetHyphPos() = TextFrameIndex((nHyphPos == -1) ? COMPLETE_STRING : nHyphPos);
+ }
+ else
+ {
+ SwFntAccess aFntAccess(m_aSub[m_nActual].m_nFontCacheId, m_aSub[m_nActual].m_nFontIndex,
+ &m_aSub[m_nActual], rInf.GetShell());
+ SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), *pTmpText, sal_Int32(nTmpIdx), sal_Int32(nTmpLen) };
+ SalLayoutGlyphs* pGlyphs
+ = lcl_CreateLayout(aGlyphsKey, aFntAccess.Get()->GetTextGlyphs()[aGlyphsKey]);
+ nTextBreak = TextFrameIndex(rInf.GetOut().GetTextBreak(
+ *pTmpText, nTextWidth,
+ sal_Int32(nTmpIdx), sal_Int32(nTmpLen),
+ nKern, rInf.GetVclCache(), pGlyphs));
+ }
+
+ if (bTextReplaced && sal_Int32(nTextBreak) != -1)
+ {
+ if ( nTmpLen != nLn )
+ nTextBreak = sw_CalcCaseMap( *this, rInf.GetText(),
+ rInf.GetIdx(), nLn, nTextBreak );
+ else
+ nTextBreak = nTextBreak + rInf.GetIdx();
+ }
+ }
+
+ TextFrameIndex nTextBreak2 = sal_Int32(nTextBreak) == -1
+ ? TextFrameIndex(COMPLETE_STRING)
+ : nTextBreak;
+
+ if ( ! bCompress )
+ return nTextBreak2;
+
+ nTextBreak2 = nTextBreak2 - rInf.GetIdx();
+
+ if( nTextBreak2 < nLn )
+ {
+ if( !nTextBreak2 && nLn )
+ nLn = TextFrameIndex(1);
+ else if (nLn > nTextBreak2 + nTextBreak2)
+ nLn = nTextBreak2 + nTextBreak2;
+ std::unique_ptr<long[]> pKernArray( new long[sal_Int32(nLn)] );
+ rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(nLn));
+ if( rInf.GetScriptInfo()->Compress( pKernArray.get(), rInf.GetIdx(), nLn,
+ rInf.GetKanaComp(), static_cast<sal_uInt16>(GetHeight( m_nActual )),
+ lcl_IsFullstopCentered( rInf.GetOut() ) ) )
+ {
+ long nKernAdd = nKern;
+ TextFrameIndex const nTmpBreak = nTextBreak2;
+ if( nKern && nTextBreak2 )
+ nKern *= sal_Int32(nTextBreak2) - 1;
+ while (nTextBreak2 < nLn && nTextWidth >= pKernArray[sal_Int32(nTextBreak2)] + nKern)
+ {
+ nKern += nKernAdd;
+ ++nTextBreak2;
+ }
+ if( rInf.GetHyphPos() )
+ *rInf.GetHyphPos() += nTextBreak2 - nTmpBreak; // It's not perfect
+ }
+ }
+ nTextBreak2 = nTextBreak2 + rInf.GetIdx();
+
+ return nTextBreak2;
+}
+
+bool SwDrawTextInfo::ApplyAutoColor( vcl::Font* pFont )
+{
+ const vcl::Font& rFnt = pFont ? *pFont : GetOut().GetFont();
+ Color nNewColor = COL_BLACK;
+ bool bChgFntColor = false;
+ bool bChgLineColor = false;
+
+ if (GetShell() && !GetShell()->GetWin() && GetShell()->GetViewOptions()->IsBlackFont())
+ {
+ if ( COL_BLACK != rFnt.GetColor() )
+ bChgFntColor = true;
+
+ if ( (COL_BLACK != GetOut().GetLineColor()) ||
+ (COL_BLACK != GetOut().GetOverlineColor()) )
+ bChgLineColor = true;
+ }
+ else
+ {
+ // FontColor has to be changed if:
+ // 1. FontColor = AUTO or 2. IsAlwaysAutoColor is set
+ // LineColor has to be changed if:
+ // 1. IsAlwaysAutoColor is set
+
+ bChgLineColor = GetShell() && GetShell()->GetWin() &&
+ GetShell()->GetAccessibilityOptions()->IsAlwaysAutoColor();
+
+ bChgFntColor = COL_AUTO == rFnt.GetColor() || bChgLineColor;
+
+ if ( bChgFntColor )
+ {
+ // check if current background has a user defined setting
+ const Color* pCol = GetFont() ? GetFont()->GetBackColor() : nullptr;
+ Color aColor;
+ if( ! pCol || COL_TRANSPARENT == *pCol )
+ {
+ const SvxBrushItem* pItem;
+ SwRect aOrigBackRect;
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
+
+ /// OD 21.08.2002
+ /// consider, that [GetBackgroundBrush(...)] can set <pCol>
+ /// - see implementation in /core/layout/paintfrm.cxx
+ /// OD 21.08.2002 #99657#
+ /// There is a user defined setting for the background, if there
+ /// is a background brush and its color is *not* "no fill"/"auto fill".
+ if( GetFrame()->GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, false, /*bConsiderTextBox=*/true ) )
+ {
+ if (aFillAttributes && aFillAttributes->isUsed())
+ {
+ // First see if fill attributes provide a color.
+ aColor = Color(aFillAttributes->getAverageColor(aGlobalRetoucheColor.getBColor()));
+ pCol = &aColor;
+ }
+
+ // If not, then fall back to the old brush item.
+ if ( !pCol )
+ {
+ pCol = &pItem->GetColor();
+ }
+
+ /// OD 30.08.2002 #99657#
+ /// determined color <pCol> can be <COL_TRANSPARENT>. Thus, check it.
+ if ( *pCol == COL_TRANSPARENT)
+ pCol = nullptr;
+ }
+ else
+ pCol = nullptr;
+ }
+
+ // no user defined color at paragraph or font background
+ if ( ! pCol )
+ pCol = &aGlobalRetoucheColor;
+
+ if( GetShell() && GetShell()->GetWin() )
+ {
+ // here we determine the preferred window text color for painting
+ const SwViewOption* pViewOption = GetShell()->GetViewOptions();
+ if(pViewOption->IsPagePreview() &&
+ !SW_MOD()->GetAccessibilityOptions().GetIsForPagePreviews())
+ nNewColor = COL_BLACK;
+ else
+ // we take the font color from the appearance page
+ nNewColor = SwViewOption::GetFontColor();
+ }
+
+ // change painting color depending of dark/bright background
+ Color aTmpColor( nNewColor );
+ if ( pCol->IsDark() && aTmpColor.IsDark() )
+ nNewColor = COL_WHITE;
+ else if ( pCol->IsBright() && aTmpColor.IsBright() )
+ nNewColor = COL_BLACK;
+ }
+ }
+
+ if ( bChgFntColor || bChgLineColor )
+ {
+ Color aNewColor( nNewColor );
+
+ if ( bChgFntColor )
+ {
+ if ( pFont && aNewColor != pFont->GetColor() )
+ {
+ // only set the new color at the font passed as argument
+ pFont->SetColor( aNewColor );
+ }
+ else if ( aNewColor != GetOut().GetFont().GetColor() )
+ {
+ // set new font with new color at output device
+ vcl::Font aFont( rFnt );
+ aFont.SetColor( aNewColor );
+ GetOut().SetFont( aFont );
+ }
+ }
+
+ // the underline and overline colors have to be set separately
+ if ( bChgLineColor )
+ {
+ // get current font color or color set at output device
+ aNewColor = pFont ? pFont->GetColor() : GetOut().GetFont().GetColor();
+ if ( aNewColor != GetOut().GetLineColor() )
+ GetOut().SetLineColor( aNewColor );
+ if ( aNewColor != GetOut().GetOverlineColor() )
+ GetOut().SetOverlineColor( aNewColor );
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void SwClearFntCacheTextGlyphs()
+{
+ for (SwFntObj* pFntObj = pFntCache->First(); pFntObj; pFntObj = SwFntCache::Next(pFntObj))
+ pFntObj->GetTextGlyphs().clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */