    /* -*- 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 <unotools/linguprops.hxx>
    #include <unotools/lingucfg.hxx>
    #include <hintids.hxx>
    #include <svl/ctloptions.hxx>
    #include <sfx2/printer.hxx>
    #include <sal/log.hxx>
    #include <editeng/hyphenzoneitem.hxx>
    #include <editeng/escapementitem.hxx>
    #include <editeng/hngpnctitem.hxx>
    #include <editeng/scriptspaceitem.hxx>
    #include <editeng/brushitem.hxx>
    #include <editeng/splwrap.hxx>
    #include <editeng/pgrditem.hxx>
    #include <editeng/tstpitem.hxx>
    #include <editeng/shaditem.hxx>

    #include <SwSmartTagMgr.hxx>
    #include <linguistic/lngprops.hxx>
    #include <editeng/unolingu.hxx>
    #include <breakit.hxx>
    #include <editeng/forbiddenruleitem.hxx>
    #include <paintfrm.hxx>
    #include <txatbase.hxx>
    #include <fmtinfmt.hxx>
    #include <swmodule.hxx>
    #include <vcl/svapp.hxx>
    #include <vcl/wrkwin.hxx>
    #include <viewsh.hxx>
    #include <viewopt.hxx>
    #include <frmtool.hxx>
    #include <IDocumentSettingAccess.hxx>
    #include <IDocumentDeviceAccess.hxx>
    #include <IDocumentMarkAccess.hxx>
    #include <paratr.hxx>
    #include <rootfrm.hxx>
    #include "inftxt.hxx"
    #include <blink.hxx>
    #include <noteurl.hxx>
    #include "porftn.hxx"
    #include "porrst.hxx"
    #include "itratr.hxx"
    #include "portab.hxx"
    #include <accessibilityoptions.hxx>
    #include <wrong.hxx>
    #include <doc.hxx>
    #include <pam.hxx>
    #include <SwGrammarMarkUp.hxx>
    #include <numrule.hxx>
    #include <EnhancedPDFExportHelper.hxx>
    #include <docufld.hxx>
    #include <frmfmt.hxx>
    #include <unomid.h>

    using namespace ::com::sun::star;
    using namespace ::com::sun::star::linguistic2;
    using namespace ::com::sun::star::uno;
    using namespace ::com::sun::star::beans;

    #define CHAR_UNDERSCORE u'_'
    #define CHAR_LEFT_ARROW u'\x25C0'
    #define CHAR_RIGHT_ARROW u'\x25B6'
    #define CHAR_TAB u'\x2192'
    #define CHAR_TAB_RTL u'\x2190'
    #define CHAR_LINEBREAK u'\x21B5'
    #define CHAR_LINEBREAK_RTL u'\x21B3'

    #define DRAW_SPECIAL_OPTIONS_CENTER 1
    #define DRAW_SPECIAL_OPTIONS_ROTATE 2

    SwLineInfo::SwLineInfo()
        : pSpace( nullptr ),
          nVertAlign( SvxParaVertAlignItem::Align::Automatic ),
          nDefTabStop( 0 ),
          bListTabStopIncluded( false ),
          nListTabStopPosition( 0 )
    {
    }

    SwLineInfo::~SwLineInfo()
    {
    }

    void SwLineInfo::CtorInitLineInfo( const SwAttrSet& rAttrSet,
                                       const SwTextNode& rTextNode )
    {
        pRuler.reset( new SvxTabStopItem( rAttrSet.GetTabStops() ) );
        if ( rTextNode.GetListTabStopPosition( nListTabStopPosition ) )
        {
            bListTabStopIncluded = true;

            // insert the list tab stop into SvxTabItem instance <pRuler>
            const SvxTabStop aListTabStop( nListTabStopPosition,
                                           SvxTabAdjust::Left );
            pRuler->Insert( aListTabStop );

            // remove default tab stops, which are before the inserted list tab stop
            for ( sal_uInt16 i = 0; i < pRuler->Count(); i++ )
            {
                if ( (*pRuler)[i].GetTabPos() < nListTabStopPosition &&
                     (*pRuler)[i].GetAdjustment() == SvxTabAdjust::Default )
                {
                    pRuler->Remove(i);
                    continue;
                }
            }
        }

        if ( !rTextNode.getIDocumentSettingAccess()->get(DocumentSettingId::TABS_RELATIVE_TO_INDENT) )
        {
            // remove default tab stop at position 0
            for ( sal_uInt16 i = 0; i < pRuler->Count(); i++ )
            {
                if ( (*pRuler)[i].GetTabPos() == 0 &&
                     (*pRuler)[i].GetAdjustment() == SvxTabAdjust::Default )
                {
                    pRuler->Remove(i);
                    break;
                }
            }
        }

        pSpace = &rAttrSet.GetLineSpacing();
        nVertAlign = rAttrSet.GetParaVertAlign().GetValue();
        nDefTabStop = USHRT_MAX;
    }

    void SwTextInfo::CtorInitTextInfo( SwTextFrame *pFrame )
    {
        m_pPara = pFrame->GetPara();
        m_nTextStart = pFrame->GetOfst();
        if (!m_pPara)
        {
            SAL_WARN("sw.core", "+SwTextInfo::CTOR: missing paragraph information");
            pFrame->Format(pFrame->getRootFrame()->GetCurrShell()->GetOut());
            m_pPara = pFrame->GetPara();
        }
    }

    SwTextInfo::SwTextInfo( const SwTextInfo &rInf )
        : m_pPara( const_cast<SwTextInfo&>(rInf).GetParaPortion() )
        , m_nTextStart( rInf.GetTextStart() )
    { }

    #if OSL_DEBUG_LEVEL > 0

    void ChkOutDev( const SwTextSizeInfo &rInf )
    {
        if ( !rInf.GetVsh() )
            return;

        const OutputDevice* pOut = rInf.GetOut();
        const OutputDevice* pRef = rInf.GetRefDev();
        OSL_ENSURE( pOut && pRef, "ChkOutDev: invalid output devices" );
    }
    #endif

    inline TextFrameIndex GetMinLen( const SwTextSizeInfo &rInf )
    {
        const TextFrameIndex nTextLen(rInf.GetText().getLength());
        if (rInf.GetLen() == TextFrameIndex(COMPLETE_STRING))
            return nTextLen;
        const TextFrameIndex nInfLen = rInf.GetIdx() + rInf.GetLen();
        return std::min(nTextLen, nInfLen);
    }

    SwTextSizeInfo::SwTextSizeInfo()
    : m_pKanaComp(nullptr)
    , m_pVsh(nullptr)
    , m_pOut(nullptr)
    , m_pRef(nullptr)
    , m_pFnt(nullptr)
    , m_pUnderFnt(nullptr)
    , m_pFrame(nullptr)
    , m_pOpt(nullptr)
    , m_pText(nullptr)
    , m_nIdx(0)
    , m_nLen(0)
    , m_nKanaIdx(0)
    , m_bOnWin    (false)
    , m_bNotEOL   (false)
    , m_bURLNotify(false)
    , m_bStopUnderflow(false)
    , m_bFootnoteInside(false)
    , m_bOtherThanFootnoteInside(false)
    , m_bMulti(false)
    , m_bFirstMulti(false)
    , m_bRuby(false)
    , m_bHanging(false)
    , m_bScriptSpace(false)
    , m_bForbiddenChars(false)
    , m_bSnapToGrid(false)
    , m_nDirection(0)
    {}

    SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew )
        : SwTextInfo( rNew ),
          m_pKanaComp(rNew.GetpKanaComp()),
          m_pVsh(const_cast<SwTextSizeInfo&>(rNew).GetVsh()),
          m_pOut(const_cast<SwTextSizeInfo&>(rNew).GetOut()),
          m_pRef(const_cast<SwTextSizeInfo&>(rNew).GetRefDev()),
          m_pFnt(const_cast<SwTextSizeInfo&>(rNew).GetFont()),
          m_pUnderFnt(rNew.GetUnderFnt()),
          m_pFrame(rNew.m_pFrame),
          m_pOpt(&rNew.GetOpt()),
          m_pText(&rNew.GetText()),
          m_nIdx(rNew.GetIdx()),
          m_nLen(rNew.GetLen()),
          m_nKanaIdx( rNew.GetKanaIdx() ),
          m_bOnWin( rNew.OnWin() ),
          m_bNotEOL( rNew.NotEOL() ),
          m_bURLNotify( rNew.URLNotify() ),
          m_bStopUnderflow( rNew.StopUnderflow() ),
          m_bFootnoteInside( rNew.IsFootnoteInside() ),
          m_bOtherThanFootnoteInside( rNew.IsOtherThanFootnoteInside() ),
          m_bMulti( rNew.IsMulti() ),
          m_bFirstMulti( rNew.IsFirstMulti() ),
          m_bRuby( rNew.IsRuby() ),
          m_bHanging( rNew.IsHanging() ),
          m_bScriptSpace( rNew.HasScriptSpace() ),
          m_bForbiddenChars( rNew.HasForbiddenChars() ),
          m_bSnapToGrid( rNew.SnapToGrid() ),
          m_nDirection( rNew.GetDirection() )
    {
    #if OSL_DEBUG_LEVEL > 0
        ChkOutDev( *this );
    #endif
    }

    void SwTextSizeInfo::CtorInitTextSizeInfo( OutputDevice* pRenderContext, SwTextFrame *pFrame,
               TextFrameIndex const nNewIdx)
    {
        m_pKanaComp = nullptr;
        m_nKanaIdx = 0;
        m_pFrame = pFrame;
        CtorInitTextInfo( m_pFrame );
        SwDoc const& rDoc(m_pFrame->GetDoc());
        m_pVsh = m_pFrame->getRootFrame()->GetCurrShell();

        // Get the output and reference device
        if ( m_pVsh )
        {
            m_pOut = pRenderContext;
            m_pRef = &m_pVsh->GetRefDev();
            m_bOnWin = m_pVsh->GetWin() || OUTDEV_WINDOW == m_pOut->GetOutDevType() || m_pVsh->isOutputToWindow();
        }
        else
        {
            // Access via StarONE. We do not need a Shell or an active one.
            if (rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE))
            {
                // We can only pick the AppWin here? (there's nothing better to pick?)
                m_pOut = Application::GetDefaultDevice();
            }
            else
                m_pOut = rDoc.getIDocumentDeviceAccess().getPrinter(false);

            m_pRef = m_pOut;
        }

    #if OSL_DEBUG_LEVEL > 0
        ChkOutDev( *this );
    #endif

        // Set default layout mode ( LTR or RTL ).
        if ( m_pFrame->IsRightToLeft() )
        {
            m_pOut->SetLayoutMode( ComplexTextLayoutFlags::BiDiStrong | ComplexTextLayoutFlags::BiDiRtl );
            m_pRef->SetLayoutMode( ComplexTextLayoutFlags::BiDiStrong | ComplexTextLayoutFlags::BiDiRtl );
            m_nDirection = DIR_RIGHT2LEFT;
        }
        else
        {
            m_pOut->SetLayoutMode( ComplexTextLayoutFlags::BiDiStrong );
            m_pRef->SetLayoutMode( ComplexTextLayoutFlags::BiDiStrong );
            m_nDirection = DIR_LEFT2RIGHT;
        }

        // The Options

        m_pOpt = m_pVsh ?
               m_pVsh->GetViewOptions() :
               SW_MOD()->GetViewOption(rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE)); // Options from Module, due to StarONE

        // bURLNotify is set if MakeGraphic prepares it
        // TODO: Unwind
        m_bURLNotify = pNoteURL && !m_bOnWin;

        SetSnapToGrid( m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue() &&
                       m_pFrame->IsInDocBody() );

        m_pFnt = nullptr;
        m_pUnderFnt = nullptr;
        m_pText = &m_pFrame->GetText();

        m_nIdx = nNewIdx;
        m_nLen = TextFrameIndex(COMPLETE_STRING);
        m_bNotEOL = false;
        m_bStopUnderflow = m_bFootnoteInside = m_bOtherThanFootnoteInside = false;
        m_bMulti = m_bFirstMulti = m_bRuby = m_bHanging = m_bScriptSpace =
            m_bForbiddenChars = false;

        SetLen( GetMinLen( *this ) );
    }

    SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew, const OUString* pText,
                  TextFrameIndex const nIndex)
        : SwTextInfo( rNew ),
          m_pKanaComp(rNew.GetpKanaComp()),
          m_pVsh(const_cast<SwTextSizeInfo&>(rNew).GetVsh()),
          m_pOut(const_cast<SwTextSizeInfo&>(rNew).GetOut()),
          m_pRef(const_cast<SwTextSizeInfo&>(rNew).GetRefDev()),
          m_pFnt(const_cast<SwTextSizeInfo&>(rNew).GetFont()),
          m_pUnderFnt(rNew.GetUnderFnt()),
          m_pFrame( rNew.m_pFrame ),
          m_pOpt(&rNew.GetOpt()),
          m_pText(pText),
          m_nIdx(nIndex),
          m_nLen(COMPLETE_STRING),
          m_nKanaIdx( rNew.GetKanaIdx() ),
          m_bOnWin( rNew.OnWin() ),
          m_bNotEOL( rNew.NotEOL() ),
          m_bURLNotify( rNew.URLNotify() ),
          m_bStopUnderflow( rNew.StopUnderflow() ),
          m_bFootnoteInside( rNew.IsFootnoteInside() ),
          m_bOtherThanFootnoteInside( rNew.IsOtherThanFootnoteInside() ),
          m_bMulti( rNew.IsMulti() ),
          m_bFirstMulti( rNew.IsFirstMulti() ),
          m_bRuby( rNew.IsRuby() ),
          m_bHanging( rNew.IsHanging() ),
          m_bScriptSpace( rNew.HasScriptSpace() ),
          m_bForbiddenChars( rNew.HasForbiddenChars() ),
          m_bSnapToGrid( rNew.SnapToGrid() ),
          m_nDirection( rNew.GetDirection() )
    {
    #if OSL_DEBUG_LEVEL > 0
        ChkOutDev( *this );
    #endif
        SetLen( GetMinLen( *this ) );
    }

    SwTextSizeInfo::SwTextSizeInfo(SwTextFrame *const pTextFrame,
                TextFrameIndex const nIndex)
        : m_bOnWin(false)
    {
        CtorInitTextSizeInfo( pTextFrame->getRootFrame()->GetCurrShell()->GetOut(), pTextFrame, nIndex );
    }

    void SwTextSizeInfo::SelectFont()
    {
         // The path needs to go via ChgPhysFnt or the FontMetricCache gets confused.
         // In this case pLastMet has it's old value.
         // Wrong: GetOut()->SetFont( GetFont()->GetFnt() );
        GetFont()->Invalidate();
        GetFont()->ChgPhysFnt( m_pVsh, *GetOut() );
    }

    void SwTextSizeInfo::NoteAnimation() const
    {
        if( OnWin() )
            SwRootFrame::FlushVout();

        OSL_ENSURE( m_pOut == m_pVsh->GetOut(),
                "SwTextSizeInfo::NoteAnimation() changed m_pOut" );
    }

    SwPosSize SwTextSizeInfo::GetTextSize( OutputDevice* pOutDev,
                                         const SwScriptInfo* pSI,
                                         const OUString& rText,
                                         const TextFrameIndex nIndex,
                                         const TextFrameIndex nLength) const
    {
        SwDrawTextInfo aDrawInf( m_pVsh, *pOutDev, pSI, rText, nIndex, nLength );
        aDrawInf.SetFrame( m_pFrame );
        aDrawInf.SetFont( m_pFnt );
        aDrawInf.SetSnapToGrid( SnapToGrid() );
        aDrawInf.SetKanaComp( 0 );
        return SwPosSize(m_pFnt->GetTextSize_( aDrawInf ));
    }

    SwPosSize SwTextSizeInfo::GetTextSize() const
    {
        const SwScriptInfo& rSI =
                         const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();

        // in some cases, compression is not allowed or suppressed for
        // performance reasons
        sal_uInt16 nComp =( SwFontScript::CJK == GetFont()->GetActual() &&
                        rSI.CountCompChg() &&
                        ! IsMulti() ) ?
                        GetKanaComp() :
                                    0 ;

        SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rSI, *m_pText, m_nIdx, m_nLen );
        aDrawInf.SetFrame( m_pFrame );
        aDrawInf.SetFont( m_pFnt );
        aDrawInf.SetSnapToGrid( SnapToGrid() );
        aDrawInf.SetKanaComp( nComp );
        return SwPosSize(m_pFnt->GetTextSize_( aDrawInf ));
    }

    void SwTextSizeInfo::GetTextSize( const SwScriptInfo* pSI, const TextFrameIndex nIndex,
                                    const TextFrameIndex nLength, const sal_uInt16 nComp,
                                    sal_uInt16& nMinSize, sal_uInt16& nMaxSizeDiff,
                                    vcl::TextLayoutCache const*const pCache) const
    {
        SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, pSI, *m_pText, nIndex, nLength,
                0, false, pCache);
        aDrawInf.SetFrame( m_pFrame );
        aDrawInf.SetFont( m_pFnt );
        aDrawInf.SetSnapToGrid( SnapToGrid() );
        aDrawInf.SetKanaComp( nComp );
        SwPosSize aSize( m_pFnt->GetTextSize_( aDrawInf ) );
        nMaxSizeDiff = static_cast<sal_uInt16>(aDrawInf.GetKanaDiff());
        nMinSize = aSize.Width();
    }

    TextFrameIndex SwTextSizeInfo::GetTextBreak( const long nLineWidth,
                                           const TextFrameIndex nMaxLen,
                                           const sal_uInt16 nComp,
                                           vcl::TextLayoutCache const*const pCache) const
    {
        const SwScriptInfo& rScriptInfo =
                         const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();

        OSL_ENSURE( m_pRef == m_pOut, "GetTextBreak is supposed to use the RefDev" );
        SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rScriptInfo,
                                 *m_pText, GetIdx(), nMaxLen,  0, false, pCache );
        aDrawInf.SetFrame( m_pFrame );
        aDrawInf.SetFont( m_pFnt );
        aDrawInf.SetSnapToGrid( SnapToGrid() );
        aDrawInf.SetKanaComp( nComp );
        aDrawInf.SetHyphPos( nullptr );

        return m_pFnt->GetTextBreak( aDrawInf, nLineWidth );
    }

    TextFrameIndex SwTextSizeInfo::GetTextBreak( const long nLineWidth,
                                           const TextFrameIndex nMaxLen,
                                           const sal_uInt16 nComp,
                                           TextFrameIndex& rExtraCharPos,
                                           vcl::TextLayoutCache const*const pCache) const
    {
        const SwScriptInfo& rScriptInfo =
                         const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();

        OSL_ENSURE( m_pRef == m_pOut, "GetTextBreak is supposed to use the RefDev" );
        SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rScriptInfo,
                                 *m_pText, GetIdx(), nMaxLen, 0, false, pCache );
        aDrawInf.SetFrame( m_pFrame );
        aDrawInf.SetFont( m_pFnt );
        aDrawInf.SetSnapToGrid( SnapToGrid() );
        aDrawInf.SetKanaComp( nComp );
        aDrawInf.SetHyphPos( &rExtraCharPos );

        return m_pFnt->GetTextBreak( aDrawInf, nLineWidth );
    }

    bool SwTextSizeInfo::HasHint(TextFrameIndex const nPos) const
    {
        std::pair<SwTextNode const*, sal_Int32> const pos(m_pFrame->MapViewToModel(nPos));
        return pos.first->GetTextAttrForCharAt(pos.second);
    }

    void SwTextPaintInfo::CtorInitTextPaintInfo( OutputDevice* pRenderContext, SwTextFrame *pFrame, const SwRect &rPaint )
    {
        CtorInitTextSizeInfo( pRenderContext, pFrame, TextFrameIndex(0) );
        aTextFly.CtorInitTextFly( pFrame );
        aPaintRect = rPaint;
        nSpaceIdx = 0;
        pSpaceAdd = nullptr;
        m_pWrongList = nullptr;
        m_pGrammarCheckList = nullptr;
        m_pSmartTags = nullptr;
        pBrushItem = nullptr;
    }

    SwTextPaintInfo::SwTextPaintInfo( const SwTextPaintInfo &rInf, const OUString* pText )
        : SwTextSizeInfo( rInf, pText )
        , m_pWrongList( rInf.GetpWrongList() )
        , m_pGrammarCheckList( rInf.GetGrammarCheckList() )
        , m_pSmartTags( rInf.GetSmartTags() )
        , pSpaceAdd( rInf.GetpSpaceAdd() ),
          pBrushItem( rInf.GetBrushItem() ),
          aTextFly( rInf.GetTextFly() ),
          aPos( rInf.GetPos() ),
          aPaintRect( rInf.GetPaintRect() ),
          nSpaceIdx( rInf.GetSpaceIdx() )
    { }

    SwTextPaintInfo::SwTextPaintInfo( const SwTextPaintInfo &rInf )
        : SwTextSizeInfo( rInf )
        , m_pWrongList( rInf.GetpWrongList() )
        , m_pGrammarCheckList( rInf.GetGrammarCheckList() )
        , m_pSmartTags( rInf.GetSmartTags() )
        , pSpaceAdd( rInf.GetpSpaceAdd() ),
          pBrushItem( rInf.GetBrushItem() ),
          aTextFly( rInf.GetTextFly() ),
          aPos( rInf.GetPos() ),
          aPaintRect( rInf.GetPaintRect() ),
          nSpaceIdx( rInf.GetSpaceIdx() )
    { }

    SwTextPaintInfo::SwTextPaintInfo( SwTextFrame *pFrame, const SwRect &rPaint )
    {
        CtorInitTextPaintInfo( pFrame->getRootFrame()->GetCurrShell()->GetOut(), pFrame, rPaint );
    }

    /// Returns if the current background color is dark.
    static bool lcl_IsDarkBackground( const SwTextPaintInfo& rInf )
    {
        const Color* pCol = rInf.GetFont()->GetBackColor();
        if( ! pCol || COL_TRANSPARENT == *pCol )
        {
            const SvxBrushItem* pItem;
            SwRect aOrigBackRect;
            drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;

            // Consider, that [GetBackgroundBrush(...)] can set <pCol>
            // See implementation in /core/layout/paintfrm.cxx
            // There is a background color, if there is a background brush and
            // its color is *not* "no fill"/"auto fill".
            if( rInf.GetTextFrame()->GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, false, /*bConsiderTextBox=*/false ) )
            {
                if ( !pCol )
                    pCol = &pItem->GetColor();

                // Determined color <pCol> can be <COL_TRANSPARENT>. Thus, check it.
                if ( *pCol == COL_TRANSPARENT)
                    pCol = nullptr;
            }
            else
                pCol = nullptr;
        }

        if( !pCol )
            pCol = &aGlobalRetoucheColor;

        return pCol->IsDark();
    }

    void SwTextPaintInfo::DrawText_( const OUString &rText, const SwLinePortion &rPor,
                                    TextFrameIndex const nStart, TextFrameIndex const nLength,
                                    const bool bKern, const bool bWrong,
                                    const bool bSmartTag,
                                    const bool bGrammarCheck )
    {
        if( !nLength )
            return;

        if( GetFont()->IsBlink() && OnWin() && rPor.Width() )
        {
            // check if accessibility options allow blinking portions:
            const SwViewShell* pSh = GetTextFrame()->getRootFrame()->GetCurrShell();
            if ( pSh && ! pSh->GetAccessibilityOptions()->IsStopAnimatedText() &&
                 ! pSh->IsPreview() )
            {
                if( !pBlink )
                    pBlink = new SwBlink();

                Point aPoint( aPos );

                if ( GetTextFrame()->IsRightToLeft() )
                    GetTextFrame()->SwitchLTRtoRTL( aPoint );

                if ( ComplexTextLayoutFlags::BiDiStrong != GetOut()->GetLayoutMode() )
                    aPoint.AdjustX( -(rPor.Width()) );

                if ( GetTextFrame()->IsVertical() )
                    GetTextFrame()->SwitchHorizontalToVertical( aPoint );

                pBlink->Insert( aPoint, &rPor, GetTextFrame(), m_pFnt->GetOrientation() );

                if( !pBlink->IsVisible() )
                    return;
            }
            else
            {
                delete pBlink;
                pBlink = nullptr;
            }
        }

        // The SwScriptInfo is useless if we are inside a field portion
        SwScriptInfo* pSI = nullptr;
        if ( ! rPor.InFieldGrp() )
            pSI = &GetParaPortion()->GetScriptInfo();

        // in some cases, kana compression is not allowed or suppressed for
        // performance reasons
        sal_uInt16 nComp = 0;
        if ( ! IsMulti() )
            nComp = GetKanaComp();

        bool bCfgIsAutoGrammar = false;
        SvtLinguConfig().GetProperty( UPN_IS_GRAMMAR_AUTO ) >>= bCfgIsAutoGrammar;
        const bool bBullet = OnWin() && GetOpt().IsBlank() && IsNoSymbol();
        const bool bTmpWrong = bWrong && OnWin() && GetOpt().IsOnlineSpell();
        const bool bTmpGrammarCheck = bGrammarCheck && OnWin() && bCfgIsAutoGrammar && GetOpt().IsOnlineSpell();
        const bool bTmpSmart = bSmartTag && OnWin() && !GetOpt().IsPagePreview() && SwSmartTagMgr::Get().IsSmartTagsEnabled();

        OSL_ENSURE( GetParaPortion(), "No paragraph!");
        SwDrawTextInfo aDrawInf( m_pFrame->getRootFrame()->GetCurrShell(), *m_pOut, pSI, rText, nStart, nLength,
                                 rPor.Width(), bBullet );

        aDrawInf.SetUnderFnt( m_pUnderFnt );

        const long nSpaceAdd = ( rPor.IsBlankPortion() || rPor.IsDropPortion() ||
                                 rPor.InNumberGrp() ) ? 0 : GetSpaceAdd();
        if ( nSpaceAdd )
        {
            TextFrameIndex nCharCnt(0);
            // #i41860# Thai justified alignment needs some
            // additional information:
            aDrawInf.SetNumberOfBlanks( rPor.InTextGrp() ?
                                        static_cast<const SwTextPortion&>(rPor).GetSpaceCnt( *this, nCharCnt ) :
                                        TextFrameIndex(0) );
        }

        aDrawInf.SetSpace( nSpaceAdd );
        aDrawInf.SetKanaComp( nComp );

        // the font is used to identify the current script via nActual
        aDrawInf.SetFont( m_pFnt );
        // the frame is used to identify the orientation
        aDrawInf.SetFrame( GetTextFrame() );
        // we have to know if the paragraph should snap to grid
        aDrawInf.SetSnapToGrid( SnapToGrid() );
        // for underlining we must know when not to add extra space behind
        // a character in justified mode
        aDrawInf.SetSpaceStop( ! rPor.GetPortion() ||
                                 rPor.GetPortion()->InFixMargGrp() ||
                                 rPor.GetPortion()->IsHolePortion() );

        // Draw text next to the left border
        Point aFontPos(aPos);
        if( m_pFnt->GetLeftBorder() && !static_cast<const SwTextPortion&>(rPor).GetJoinBorderWithPrev() )
        {
            const sal_uInt16 nLeftBorderSpace = m_pFnt->GetLeftBorderSpace();
            if ( GetTextFrame()->IsRightToLeft() )
            {
                aFontPos.AdjustX( -nLeftBorderSpace );
            }
            else
            {
                switch( m_pFnt->GetOrientation(GetTextFrame()->IsVertical()) )
                {
                    case 0 :
                        aFontPos.AdjustX(nLeftBorderSpace );
                        break;
                    case 900 :
                        aFontPos.AdjustY( -nLeftBorderSpace );
                        break;
                    case 1800 :
                        aFontPos.AdjustX( -nLeftBorderSpace );
                        break;
                    case 2700 :
                        aFontPos.AdjustY(nLeftBorderSpace );
                        break;
                }
            }
            if( aFontPos.X() < 0 )
                aFontPos.setX( 0 );
            if( aFontPos.Y() < 0 )
                aFontPos.setY( 0 );
        }

        if( GetTextFly().IsOn() )
        {
            // aPos needs to be the TopLeft, because we cannot calculate the
            // ClipRects otherwise
            const Point aPoint( aFontPos.X(), aFontPos.Y() - rPor.GetAscent() );
            const Size aSize( rPor.Width(), rPor.Height() );
            aDrawInf.SetPos( aPoint );
            aDrawInf.SetSize( aSize );
            aDrawInf.SetAscent( rPor.GetAscent() );
            aDrawInf.SetKern( bKern ? rPor.Width() : 0 );
            aDrawInf.SetWrong( bTmpWrong ? m_pWrongList : nullptr );
            aDrawInf.SetGrammarCheck( bTmpGrammarCheck ? m_pGrammarCheckList : nullptr );
            aDrawInf.SetSmartTags( bTmpSmart ? m_pSmartTags : nullptr );
            GetTextFly().DrawTextOpaque( aDrawInf );
        }
        else
        {
            aDrawInf.SetPos( aFontPos );
            if( bKern )
                m_pFnt->DrawStretchText_( aDrawInf );
            else
            {
                aDrawInf.SetWrong( bTmpWrong ? m_pWrongList : nullptr );
                aDrawInf.SetGrammarCheck( bTmpGrammarCheck ? m_pGrammarCheckList : nullptr );
                aDrawInf.SetSmartTags( bTmpSmart ? m_pSmartTags : nullptr );
                m_pFnt->DrawText_( aDrawInf );
            }
        }
    }

    void SwTextPaintInfo::CalcRect( const SwLinePortion& rPor,
                                   SwRect* pRect, SwRect* pIntersect,
                                   const bool bInsideBox ) const
    {
        Size aSize( rPor.Width(), rPor.Height() );
        if( rPor.IsHangingPortion() )
            aSize.setWidth( static_cast<const SwHangingPortion&>(rPor).GetInnerWidth() );
        if( rPor.InSpaceGrp() && GetSpaceAdd() )
        {
            SwTwips nAdd = rPor.CalcSpacing( GetSpaceAdd(), *this );
            if( rPor.InFieldGrp() && GetSpaceAdd() < 0 && nAdd )
                nAdd += GetSpaceAdd() / SPACING_PRECISION_FACTOR;
            aSize.AdjustWidth(nAdd );
        }

        Point aPoint;

        if( IsRotated() )
        {
            long nTmp = aSize.Width();
            aSize.setWidth( aSize.Height() );
            aSize.setHeight( nTmp );
            if ( 1 == GetDirection() )
            {
                aPoint.setX( X() - rPor.GetAscent() );
                aPoint.setY( Y() - aSize.Height() );
            }
            else
            {
                aPoint.setX( X() - rPor.Height() + rPor.GetAscent() );
                aPoint.setY( Y() );
            }
        }
        else
        {
            aPoint.setX( X() );
            if ( GetTextFrame()->IsVertLR() )
                aPoint.setY( Y() - rPor.Height() + rPor.GetAscent() );
            else
                aPoint.setY( Y() - rPor.GetAscent() );
        }

        // Adjust x coordinate if we are inside a bidi portion
        const bool bFrameDir = GetTextFrame()->IsRightToLeft();
        const bool bCounterDir = ( !bFrameDir && DIR_RIGHT2LEFT == GetDirection() ) ||
                                 (  bFrameDir && DIR_LEFT2RIGHT == GetDirection() );

        if ( bCounterDir )
            aPoint.AdjustX( -(aSize.Width()) );

        SwRect aRect( aPoint, aSize );

        if ( GetTextFrame()->IsRightToLeft() )
            GetTextFrame()->SwitchLTRtoRTL( aRect );

        if ( GetTextFrame()->IsVertical() )
            GetTextFrame()->SwitchHorizontalToVertical( aRect );

        if( bInsideBox && rPor.InTextGrp() )
        {
            const bool bJoinWithPrev =
                static_cast<const SwTextPortion&>(rPor).GetJoinBorderWithPrev();
            const bool bJoinWithNext =
                static_cast<const SwTextPortion&>(rPor).GetJoinBorderWithNext();
            const bool bIsVert = GetTextFrame()->IsVertical();
            aRect.Top(aRect.Top() + GetFont()->CalcShadowSpace(SvxShadowItemSide::TOP, bIsVert, bJoinWithPrev, bJoinWithNext ));
            aRect.Bottom(aRect.Bottom() - GetFont()->CalcShadowSpace(SvxShadowItemSide::BOTTOM, bIsVert, bJoinWithPrev, bJoinWithNext ));
            aRect.Left(aRect.Left() + GetFont()->CalcShadowSpace(SvxShadowItemSide::LEFT, bIsVert, bJoinWithPrev, bJoinWithNext ));
            aRect.Right(aRect.Right() - GetFont()->CalcShadowSpace(SvxShadowItemSide::RIGHT, bIsVert, bJoinWithPrev, bJoinWithNext ));
        }

        if ( pRect )
            *pRect = aRect;

        if( aRect.HasArea() && pIntersect )
        {
            ::SwAlignRect( aRect, GetVsh(), GetOut() );

            if ( GetOut()->IsClipRegion() )
            {
                SwRect aClip( GetOut()->GetClipRegion().GetBoundRect() );
                aRect.Intersection( aClip );
            }

            *pIntersect = aRect;
        }
    }

    /**
     * Draws a special portion
     * E.g.: line break portion, tab portion
     *
     * @param rPor The portion
     * @param rRect The rectangle surrounding the character
     * @param rCol Specify a color for the character
     * @param bCenter Draw the character centered, otherwise left aligned
     * @param bRotate Rotate the character if character rotation is set
     */
    static void lcl_DrawSpecial( const SwTextPaintInfo& rInf, const SwLinePortion& rPor,
                          SwRect& rRect, const Color& rCol, sal_Unicode cChar,
                          sal_uInt8 nOptions )
    {
        bool bCenter = 0 != ( nOptions & DRAW_SPECIAL_OPTIONS_CENTER );
        bool bRotate = 0 != ( nOptions & DRAW_SPECIAL_OPTIONS_ROTATE );

        // rRect is given in absolute coordinates
        if ( rInf.GetTextFrame()->IsRightToLeft() )
            rInf.GetTextFrame()->SwitchRTLtoLTR( rRect );
        if ( rInf.GetTextFrame()->IsVertical() )
            rInf.GetTextFrame()->SwitchVerticalToHorizontal( rRect );

        const SwFont* pOldFnt = rInf.GetFont();

        // Font is generated only once:
        static SwFont* s_pFnt = nullptr;
        if ( ! s_pFnt )
        {
            s_pFnt = new SwFont( *pOldFnt );
            s_pFnt->SetFamily( FAMILY_DONTKNOW, s_pFnt->GetActual() );
            s_pFnt->SetName( numfunc::GetDefBulletFontname(), s_pFnt->GetActual() );
            s_pFnt->SetStyleName( aEmptyOUStr, s_pFnt->GetActual() );
            s_pFnt->SetCharSet( RTL_TEXTENCODING_SYMBOL, s_pFnt->GetActual() );
        }

        // Some of the current values are set at the font:
        if ( ! bRotate )
            s_pFnt->SetVertical( 0, rInf.GetTextFrame()->IsVertical() );
        else
            s_pFnt->SetVertical( pOldFnt->GetOrientation() );

        s_pFnt->SetColor(rCol);

        Size aFontSize( 0, SPECIAL_FONT_HEIGHT );
        s_pFnt->SetSize( aFontSize, s_pFnt->GetActual() );

        const_cast<SwTextPaintInfo&>(rInf).SetFont( s_pFnt );

        // The maximum width depends on the current orientation
        const sal_uInt16 nDir = s_pFnt->GetOrientation( rInf.GetTextFrame()->IsVertical() );
        SwTwips nMaxWidth;
        if (nDir == 900 || nDir == 2700)
            nMaxWidth = rRect.Height();
        else
        {
            assert(nDir == 0); //Unknown direction set at font
            nMaxWidth = rRect.Width();
        }

        // check if char fits into rectangle
        const OUString aTmp( cChar );
        aFontSize = rInf.GetTextSize( aTmp ).SvLSize();
        while ( aFontSize.Width() > nMaxWidth )
        {
            SwTwips nFactor = ( 100 * aFontSize.Width() ) / nMaxWidth;
            const SwTwips nOldWidth = aFontSize.Width();

            // new height for font
            const SwFontScript nAct = s_pFnt->GetActual();
            aFontSize.setHeight( ( 100 * s_pFnt->GetSize( nAct ).Height() ) / nFactor );
            aFontSize.setWidth( ( 100 * s_pFnt->GetSize( nAct).Width() ) / nFactor );

            if ( !aFontSize.Width() && !aFontSize.Height() )
                break;

            s_pFnt->SetSize( aFontSize, nAct );

            aFontSize = rInf.GetTextSize( aTmp ).SvLSize();

            if ( aFontSize.Width() >= nOldWidth )
                break;
        }

        const Point aOldPos( rInf.GetPos() );

        // adjust values so that tab is vertically and horizontally centered
        SwTwips nX = rRect.Left();
        SwTwips nY = rRect.Top();
        switch ( nDir )
        {
        case 0 :
            if ( bCenter )
                nX += ( rRect.Width() - aFontSize.Width() ) / 2;
            nY += ( rRect.Height() - aFontSize.Height() ) / 2 + rInf.GetAscent();
            break;
        case 900 :
            if ( bCenter )
                nX += ( rRect.Width() - aFontSize.Height() ) / 2 + rInf.GetAscent();
            nY += ( rRect.Height() + aFontSize.Width() ) / 2;
            break;
        case 2700 :
            if ( bCenter )
                nX += ( rRect.Width() + aFontSize.Height() ) / 2 - rInf.GetAscent();
            nY += ( rRect.Height() - aFontSize.Width() ) / 2;
            break;
        }

        Point aTmpPos( nX, nY );
        const_cast<SwTextPaintInfo&>(rInf).SetPos( aTmpPos );
        sal_uInt16 nOldWidth = rPor.Width();
        const_cast<SwLinePortion&>(rPor).Width( static_cast<sal_uInt16>(aFontSize.Width()) );
        rInf.DrawText( aTmp, rPor );
        const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
        const_cast<SwTextPaintInfo&>(rInf).SetFont( const_cast<SwFont*>(pOldFnt) );
        const_cast<SwTextPaintInfo&>(rInf).SetPos( aOldPos );
    }

    void SwTextPaintInfo::DrawRect( const SwRect &rRect, bool bRetouche ) const
    {
        if ( OnWin() || !bRetouche )
        {
            if( aTextFly.IsOn() )
                const_cast<SwTextPaintInfo*>(this)->GetTextFly().
                    DrawFlyRect( m_pOut, rRect );
            else
                m_pOut->DrawRect( rRect.SVRect() );
        }
    }

    void SwTextPaintInfo::DrawTab( const SwLinePortion &rPor ) const
    {
        if( OnWin() )
        {
            SwRect aRect;
            CalcRect( rPor, &aRect );

            if ( ! aRect.HasArea() )
                return;

            const sal_Unicode cChar = GetTextFrame()->IsRightToLeft() ? CHAR_TAB_RTL : CHAR_TAB;
            const sal_uInt8 nOptions = DRAW_SPECIAL_OPTIONS_CENTER | DRAW_SPECIAL_OPTIONS_ROTATE;

            lcl_DrawSpecial( *this, rPor, aRect, NON_PRINTING_CHARACTER_COLOR, cChar, nOptions );
        }
    }

    void SwTextPaintInfo::DrawLineBreak( const SwLinePortion &rPor ) const
    {
        if( OnWin() )
        {
            sal_uInt16 nOldWidth = rPor.Width();
            const_cast<SwLinePortion&>(rPor).Width( LINE_BREAK_WIDTH );

            SwRect aRect;
            CalcRect( rPor, &aRect );

            if( aRect.HasArea() )
            {
                const sal_Unicode cChar = GetTextFrame()->IsRightToLeft() ?
                                          CHAR_LINEBREAK_RTL : CHAR_LINEBREAK;
                const sal_uInt8 nOptions = 0;

                lcl_DrawSpecial( *this, rPor, aRect, NON_PRINTING_CHARACTER_COLOR, cChar, nOptions );
            }

            const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
        }
    }

    void SwTextPaintInfo::DrawRedArrow( const SwLinePortion &rPor ) const
    {
        Size aSize( SPECIAL_FONT_HEIGHT, SPECIAL_FONT_HEIGHT );
        SwRect aRect( static_cast<const SwArrowPortion&>(rPor).GetPos(), aSize );
        sal_Unicode cChar;
        if( static_cast<const SwArrowPortion&>(rPor).IsLeft() )
        {
            aRect.Pos().AdjustY(20 - GetAscent() );
            aRect.Pos().AdjustX(20 );
            if( aSize.Height() > rPor.Height() )
                aRect.Height( rPor.Height() );
            cChar = CHAR_LEFT_ARROW;
        }
        else
        {
            if( aSize.Height() > rPor.Height() )
                aRect.Height( rPor.Height() );
            aRect.Pos().AdjustY( -(aRect.Height() + 20) );
            aRect.Pos().AdjustX( -(aRect.Width() + 20) );
            cChar = CHAR_RIGHT_ARROW;
        }

        if ( GetTextFrame()->IsVertical() )
            GetTextFrame()->SwitchHorizontalToVertical( aRect );

        if( aRect.HasArea() )
        {
            const sal_uInt8 nOptions = 0;
            lcl_DrawSpecial( *this, rPor, aRect, COL_LIGHTRED, cChar, nOptions );
        }
    }

    void SwTextPaintInfo::DrawPostIts( bool bScript ) const
    {
        if( !OnWin() || !m_pOpt->IsPostIts() )
            return;

        Size aSize;
        Point aTmp;

        const sal_uInt16 nPostItsWidth = SwViewOption::GetPostItsWidth( GetOut() );
        const sal_uInt16 nFontHeight = m_pFnt->GetHeight( m_pVsh, *GetOut() );
        const sal_uInt16 nFontAscent = m_pFnt->GetAscent( m_pVsh, *GetOut() );

        switch ( m_pFnt->GetOrientation( GetTextFrame()->IsVertical() ) )
        {
        case 0 :
            aSize.setWidth( nPostItsWidth );
            aSize.setHeight( nFontHeight );
            aTmp.setX( aPos.X() );
            aTmp.setY( aPos.Y() - nFontAscent );
            break;
        case 900 :
            aSize.setHeight( nPostItsWidth );
            aSize.setWidth( nFontHeight );
            aTmp.setX( aPos.X() - nFontAscent );
            aTmp.setY( aPos.Y() );
            break;
        case 2700 :
            aSize.setHeight( nPostItsWidth );
            aSize.setWidth( nFontHeight );
            aTmp.setX( aPos.X() - nFontHeight +
                                  nFontAscent );
            aTmp.setY( aPos.Y() );
            break;
        }

        SwRect aTmpRect( aTmp, aSize );

        if ( GetTextFrame()->IsRightToLeft() )
            GetTextFrame()->SwitchLTRtoRTL( aTmpRect );

        if ( GetTextFrame()->IsVertical() )
            GetTextFrame()->SwitchHorizontalToVertical( aTmpRect );

        const tools::Rectangle aRect( aTmpRect.SVRect() );
        SwViewOption::PaintPostIts( const_cast<OutputDevice*>(GetOut()), aRect, bScript );

    }

    void SwTextPaintInfo::DrawCheckBox(const SwFieldFormCheckboxPortion &rPor, bool bChecked) const
    {
        SwRect aIntersect;
        CalcRect( rPor, &aIntersect );
        if ( aIntersect.HasArea() )
        {
            if (OnWin() && SwViewOption::IsFieldShadings() &&
                    !GetOpt().IsPagePreview())
            {
                OutputDevice* pOut = const_cast<OutputDevice*>(GetOut());
                pOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
                pOut->SetFillColor( SwViewOption::GetFieldShadingsColor() );
                pOut->SetLineColor();
                pOut->DrawRect( aIntersect.SVRect() );
                pOut->Pop();
            }
            const int delta=10;
            tools::Rectangle r(aIntersect.Left()+delta, aIntersect.Top()+delta, aIntersect.Right()-delta, aIntersect.Bottom()-delta);
            m_pOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
            m_pOut->SetLineColor( Color(0, 0, 0));
            m_pOut->SetFillColor();
            m_pOut->DrawRect( r );
            if (bChecked)
            {
                m_pOut->DrawLine(r.TopLeft(), r.BottomRight());
                m_pOut->DrawLine(r.TopRight(), r.BottomLeft());
            }
            m_pOut->Pop();
        }
    }

    void SwTextPaintInfo::DrawBackground( const SwLinePortion &rPor ) const
    {
        OSL_ENSURE( OnWin(), "SwTextPaintInfo::DrawBackground: printer pollution ?" );

        SwRect aIntersect;
        CalcRect( rPor, nullptr, &aIntersect, true );

        if ( aIntersect.HasArea() )
        {
            OutputDevice* pOut = const_cast<OutputDevice*>(GetOut());
            pOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );

            // For dark background we do not want to have a filled rectangle
            if ( GetVsh() && GetVsh()->GetWin() && lcl_IsDarkBackground( *this ) )
            {
                pOut->SetLineColor( SwViewOption::GetFontColor() );
            }
            else
            {
                pOut->SetFillColor( SwViewOption::GetFieldShadingsColor() );
                pOut->SetLineColor();
            }

            DrawRect( aIntersect, true );
            pOut->Pop();
        }
    }

    void SwTextPaintInfo::DrawBackBrush( const SwLinePortion &rPor ) const
    {
        {
            SwRect aIntersect;
            CalcRect( rPor, &aIntersect, nullptr, true );
            if(aIntersect.HasArea())
            {
                SwPosition const aPosition(m_pFrame->MapViewToModelPos(GetIdx()));
                const ::sw::mark::IMark* pFieldmark =
                    m_pFrame->GetDoc().getIDocumentMarkAccess()->getFieldmarkFor(aPosition);
                bool bIsStartMark = (TextFrameIndex(1) == GetLen()
                        && CH_TXT_ATR_FIELDSTART == GetText()[sal_Int32(GetIdx())]);
                if(pFieldmark) {
                    SAL_INFO("sw.core", "Found Fieldmark " << pFieldmark->ToString());
                }
                if(bIsStartMark)
                    SAL_INFO("sw.core", "Found StartMark");
                if (OnWin() && (pFieldmark!=nullptr || bIsStartMark) &&
                        SwViewOption::IsFieldShadings() &&
                        !GetOpt().IsPagePreview())
                {
                    OutputDevice* pOutDev = const_cast<OutputDevice*>(GetOut());
                    pOutDev->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
                    pOutDev->SetFillColor( SwViewOption::GetFieldShadingsColor() );
                    pOutDev->SetLineColor( );
                    pOutDev->DrawRect( aIntersect.SVRect() );
                    pOutDev->Pop();
                }
            }
        }

        SwRect aIntersect;
        CalcRect( rPor, nullptr, &aIntersect, true );

        if ( aIntersect.HasArea() )
        {
            OutputDevice* pTmpOut = const_cast<OutputDevice*>(GetOut());

            // #i16816# tagged pdf support
            SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pTmpOut );

            Color aFillColor;

            if( m_pFnt->GetHighlightColor() != COL_TRANSPARENT )
            {
                aFillColor = m_pFnt->GetHighlightColor();
            }
            else
            {
                if( !m_pFnt->GetBackColor() )
                    return;
                aFillColor = *m_pFnt->GetBackColor();
            }

            // tdf#104349 do not highlight portions of space chars before end of line if the compatibility option is enabled
            // for LTR mode only
            if ( !GetTextFrame()->IsRightToLeft() )
            {
                if (GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS))
                {
                    bool           draw = false;
                    bool           full = false;
                    SwLinePortion *pPos = const_cast<SwLinePortion *>(&rPor);
                    TextFrameIndex nIdx = GetIdx();
                    TextFrameIndex nLen;

                    do
                    {
                        nLen = pPos->GetLen();
                        for (TextFrameIndex i = nIdx; i < (nIdx + nLen); ++i)
                        {
                            if (i < TextFrameIndex(GetText().getLength())
                                && GetText()[sal_Int32(i)] == CH_TXTATR_NEWLINE)
                            {
                                if ( i >= (GetIdx() + rPor.GetLen()) )
                                {
                                    goto drawcontinue;
                                }
                            }
                            if (i >= TextFrameIndex(GetText().getLength())
                                || GetText()[sal_Int32(i)] != CH_BLANK)
                            {
                                draw = true;
                                if ( i >= (GetIdx() + rPor.GetLen()) )
                                {
                                    full = true;
                                    goto drawcontinue;
                                }
                            }
                        }
                        nIdx += nLen;
                        pPos = pPos->GetPortion();
                    } while ( pPos );

                drawcontinue:

                    if ( !draw )
                        return;

                    if ( !full )
                    {
                        pPos = const_cast<SwLinePortion *>(&rPor);
                        nIdx = GetIdx();

                        nLen = pPos->GetLen();
                        for (TextFrameIndex i = (nIdx + nLen - TextFrameIndex(1));
                                i >= nIdx; --i)
                        {
                            if (i < TextFrameIndex(GetText().getLength())
                                && GetText()[sal_Int32(i)] == CH_TXTATR_NEWLINE)
                            {
                                continue;
                            }
                            if (i >= TextFrameIndex(GetText().getLength())
                                || GetText()[sal_Int32(i)] != CH_BLANK)
                            {
                                sal_uInt16 nOldWidth = rPor.Width();
                                sal_uInt16 nNewWidth = GetTextSize(m_pOut, nullptr,
                                    GetText(), nIdx, (i + TextFrameIndex(1) - nIdx)).Width();

                                const_cast<SwLinePortion&>(rPor).Width( nNewWidth );
                                CalcRect( rPor, nullptr, &aIntersect, true );
                                const_cast<SwLinePortion&>(rPor).Width( nOldWidth );

                                if ( !aIntersect.HasArea() )
                                {
                                    return;
                                }

                                break;
                            }
                        }
                    }
                }
            }

            pTmpOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );

            pTmpOut->SetFillColor(aFillColor);
            pTmpOut->SetLineColor();

            DrawRect( aIntersect, false );

            pTmpOut->Pop();
        }
    }

    void SwTextPaintInfo::DrawBorder( const SwLinePortion &rPor ) const
    {
        SwRect aDrawArea;
        CalcRect( rPor, &aDrawArea );
        if ( aDrawArea.HasArea() )
        {
            PaintCharacterBorder(
                *m_pFnt, aDrawArea, GetTextFrame()->IsVertical(),
                rPor.GetJoinBorderWithPrev(), rPor.GetJoinBorderWithNext());
        }
    }

    void SwTextPaintInfo::DrawViewOpt( const SwLinePortion &rPor,
                                      const sal_uInt16 nWhich ) const
    {
        if( OnWin() && !IsMulti() )
        {
            bool bDraw = false;
            switch( nWhich )
            {
            case POR_FTN:
            case POR_QUOVADIS:
            case POR_NUMBER:
            case POR_FLD:
            case POR_URL:
            case POR_HIDDEN:
            case POR_TOX:
            case POR_REF:
            case POR_META:
            case POR_CONTROLCHAR:
                if ( !GetOpt().IsPagePreview()
                     && !GetOpt().IsReadonly()
                     && SwViewOption::IsFieldShadings()
                     && ( POR_NUMBER != nWhich
                          || m_pFrame->GetTextNodeForParaProps()->HasMarkedLabel())) // #i27615#
                {
                    bDraw = true;
                }
                break;
            case POR_INPUTFLD:
                // input field shading also in read-only mode
                if ( !GetOpt().IsPagePreview()
                     && SwViewOption::IsFieldShadings() )
                {
                    bDraw = true;
                }
                break;
            case POR_TAB:
                if ( GetOpt().IsTab() )     bDraw = true;
                break;
            case POR_SOFTHYPH:
                if ( GetOpt().IsSoftHyph() )bDraw = true;
                break;
            case POR_BLANK:
                if ( GetOpt().IsHardBlank())bDraw = true;
                break;
            default:
                {
                    OSL_ENSURE( false, "SwTextPaintInfo::DrawViewOpt: don't know how to draw this" );
                    break;
                }
            }
            if ( bDraw )
                DrawBackground( rPor );
        }
    }

    static void lcl_InitHyphValues( PropertyValues &rVals,
                sal_Int16 nMinLeading, sal_Int16 nMinTrailing )
    {
        sal_Int32 nLen = rVals.getLength();

        if (0 == nLen)  // yet to be initialized?
        {
            rVals.realloc( 2 );
            PropertyValue *pVal = rVals.getArray();

            pVal[0].Name    = UPN_HYPH_MIN_LEADING;
            pVal[0].Handle  = UPH_HYPH_MIN_LEADING;
            pVal[0].Value   <<= nMinLeading;

            pVal[1].Name    = UPN_HYPH_MIN_TRAILING;
            pVal[1].Handle  = UPH_HYPH_MIN_TRAILING;
            pVal[1].Value   <<= nMinTrailing;
        }
        else if (2 == nLen) // already initialized once?
        {
            PropertyValue *pVal = rVals.getArray();
            pVal[0].Value <<= nMinLeading;
            pVal[1].Value <<= nMinTrailing;
        }
        else {
            OSL_FAIL( "unexpected size of sequence" );
        }
    }

    const PropertyValues & SwTextFormatInfo::GetHyphValues() const
    {
        OSL_ENSURE( 2 == m_aHyphVals.getLength(),
                "hyphenation values not yet initialized" );
        return m_aHyphVals;
    }

    bool SwTextFormatInfo::InitHyph( const bool bAutoHyphen )
    {
        const SwAttrSet& rAttrSet = GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet();
        SetHanging( rAttrSet.GetHangingPunctuation().GetValue() );
        SetScriptSpace( rAttrSet.GetScriptSpace().GetValue() );
        SetForbiddenChars( rAttrSet.GetForbiddenRule().GetValue() );
        const SvxHyphenZoneItem &rAttr = rAttrSet.GetHyphenZone();
        MaxHyph() = rAttr.GetMaxHyphens();
        const bool bAuto = bAutoHyphen || rAttr.IsHyphen();
        if( bAuto || m_bInterHyph )
        {
            const sal_Int16 nMinimalLeading  = std::max(rAttr.GetMinLead(), sal_uInt8(2));
            const sal_Int16 nMinimalTrailing = rAttr.GetMinTrail();
            lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing);
        }
        return bAuto;
    }

    void SwTextFormatInfo::CtorInitTextFormatInfo( OutputDevice* pRenderContext, SwTextFrame *pNewFrame, const bool bNewInterHyph,
                                    const bool bNewQuick, const bool bTst )
    {
        CtorInitTextPaintInfo( pRenderContext, pNewFrame, SwRect() );

        m_bQuick = bNewQuick;
        m_bInterHyph = bNewInterHyph;

        //! needs to be done in this order
        m_bAutoHyph = InitHyph();

        m_bIgnoreFly = false;
        m_bFakeLineStart = false;
        m_bShift = false;
        m_bDropInit = false;
        m_bTestFormat = bTst;
        m_nLeft = 0;
        m_nRight = 0;
        m_nFirst = 0;
        m_nRealWidth = 0;
        m_nForcedLeftMargin = 0;
        m_pRest = nullptr;
        m_nLineHeight = 0;
        m_nLineNetHeight = 0;
        SetLineStart(TextFrameIndex(0));

        SvtCTLOptions::TextNumerals const nTextNumerals(
                SW_MOD()->GetCTLOptions().GetCTLTextNumerals());
        // cannot cache for NUMERALS_CONTEXT because we need to know the string
        // for the whole paragraph now
        if (nTextNumerals != SvtCTLOptions::NUMERALS_CONTEXT)
        {
            // set digit mode to what will be used later to get same results
            SwDigitModeModifier const m(*m_pRef, LANGUAGE_NONE /*dummy*/);
            assert(m_pRef->GetDigitLanguage() != LANGUAGE_NONE);
            SetCachedVclData(m_pRef->CreateTextLayoutCache(*m_pText));
        }

        Init();
    }

    /**
     * If the Hyphenator returns ERROR or the language is set to NOLANGUAGE
     * we do not hyphenate.
     * Else, we always hyphenate if we do interactive hyphenation.
     * If we do not do interactive hyphenation, we only hyphenate if ParaFormat is
     * set to automatic hyphenation.
     */
    bool SwTextFormatInfo::IsHyphenate() const
    {
        if( !m_bInterHyph && !m_bAutoHyph )
            return false;

        LanguageType eTmp = GetFont()->GetLanguage();
        if( LANGUAGE_DONTKNOW == eTmp || LANGUAGE_NONE == eTmp )
            return false;

        uno::Reference< XHyphenator > xHyph = ::GetHyphenator();
        if (!xHyph.is())
            return false;

        if (m_bInterHyph)
            SvxSpellWrapper::CheckHyphLang( xHyph, eTmp );

        return xHyph->hasLocale( g_pBreakIt->GetLocale(eTmp) );
    }

    const SwFormatDrop *SwTextFormatInfo::GetDropFormat() const
    {
        const SwFormatDrop *pDrop = &GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetDrop();
        if( 1 >= pDrop->GetLines() ||
            ( !pDrop->GetChars() && !pDrop->GetWholeWord() ) )
            pDrop = nullptr;
        return pDrop;
    }

    void SwTextFormatInfo::Init()
    {
        // Not initialized: pRest, nLeft, nRight, nFirst, nRealWidth
        X(0);
        m_bArrowDone = m_bFull = m_bFootnoteDone = m_bErgoDone = m_bNumDone = m_bNoEndHyph =
            m_bNoMidHyph = m_bStop = m_bNewLine = m_bUnderflow = m_bTabOverflow = false;

        // generally we do not allow number portions in follows, except...
        if ( GetTextFrame()->IsFollow() )
        {
            const SwTextFrame* pMaster = GetTextFrame()->FindMaster();
            OSL_ENSURE(pMaster, "pTextFrame without Master");
            const SwLinePortion* pTmpPara = pMaster ? pMaster->GetPara() : nullptr;

            // there is a master for this follow and the master does not have
            // any contents (especially it does not have a number portion)
            m_bNumDone = ! pTmpPara ||
                       ! static_cast<const SwParaPortion*>(pTmpPara)->GetFirstPortion()->IsFlyPortion();
        }

        m_pRoot = nullptr;
        m_pLast = nullptr;
        m_pFly = nullptr;
        m_pLastTab = nullptr;
        m_pUnderflow = nullptr;
        m_cTabDecimal = 0;
        m_nWidth = m_nRealWidth;
        m_nForcedLeftMargin = 0;
        m_nSoftHyphPos = TextFrameIndex(0);
        m_nUnderScorePos = TextFrameIndex(COMPLETE_STRING);
        m_cHookChar = 0;
        SetIdx(TextFrameIndex(0));
        SetLen(TextFrameIndex(GetText().getLength()));
        SetPaintOfst(0);
    }

    SwTextFormatInfo::SwTextFormatInfo(OutputDevice* pRenderContext, SwTextFrame *pFrame, const bool bInterHyphL,
                                       const bool bQuickL, const bool bTst)
    {
        CtorInitTextFormatInfo(pRenderContext, pFrame, bInterHyphL, bQuickL, bTst);
    }

    /**
     * There are a few differences between a copy constructor
     * and the following constructor for multi-line formatting.
     * The root is the first line inside the multi-portion,
     * the line start is the actual position in the text,
     * the line width is the rest width from the surrounding line
     * and the bMulti and bFirstMulti-flag has to be set correctly.
     */
    SwTextFormatInfo::SwTextFormatInfo( const SwTextFormatInfo& rInf,
        SwLineLayout& rLay, SwTwips nActWidth ) : SwTextPaintInfo( rInf ),
        m_bTabOverflow( false )
    {
        m_pRoot = &rLay;
        m_pLast = &rLay;
        m_pFly = nullptr;
        m_pUnderflow = nullptr;
        m_pRest = nullptr;
        m_pLastTab = nullptr;

        m_nSoftHyphPos = TextFrameIndex(0);
        m_nUnderScorePos = TextFrameIndex(COMPLETE_STRING);
        m_nLineStart = rInf.GetIdx();
        m_nLeft = rInf.m_nLeft;
        m_nRight = rInf.m_nRight;
        m_nFirst = rInf.m_nLeft;
        m_nRealWidth = sal_uInt16(nActWidth);
        m_nWidth = m_nRealWidth;
        m_nLineHeight = 0;
        m_nLineNetHeight = 0;
        m_nForcedLeftMargin = 0;

        m_bFull = false;
        m_bFootnoteDone = true;
        m_bErgoDone = true;
        m_bNumDone = true;
        m_bArrowDone = true;
        m_bStop = false;
        m_bNewLine = true;
        m_bShift = false;
        m_bUnderflow = false;
        m_bInterHyph = false;
        m_bAutoHyph = false;
        m_bDropInit = false;
        m_bQuick  = rInf.m_bQuick;
        m_bNoEndHyph = false;
        m_bNoMidHyph = false;
        m_bIgnoreFly = false;
        m_bFakeLineStart = false;

        m_cTabDecimal = 0;
        m_cHookChar = 0;
        m_nMaxHyph = 0;
        m_bTestFormat = rInf.m_bTestFormat;
        SetMulti( true );
        SetFirstMulti( rInf.IsFirstMulti() );
    }

    bool SwTextFormatInfo::CheckFootnotePortion_( SwLineLayout const * pCurr )
    {
        const sal_uInt16 nHeight = pCurr->GetRealHeight();
        for( SwLinePortion *pPor = pCurr->GetPortion(); pPor; pPor = pPor->GetPortion() )
        {
            if( pPor->IsFootnotePortion() && nHeight > static_cast<SwFootnotePortion*>(pPor)->Orig() )
            {
                SetLineHeight( nHeight );
                SetLineNetHeight( pCurr->Height() );
                return true;
            }
        }
        return false;
    }

    TextFrameIndex SwTextFormatInfo::ScanPortionEnd(TextFrameIndex const nStart,
                                                    TextFrameIndex const nEnd)
    {
        m_cHookChar = 0;
        TextFrameIndex i = nStart;

        // Used for decimal tab handling:
        const sal_Unicode cTabDec = GetLastTab() ? GetTabDecimal() : 0;
        const sal_Unicode cThousandSep  = ',' == cTabDec ? '.' : ',';

        // #i45951# German (Switzerland) uses ' as thousand separator
        const sal_Unicode cThousandSep2 = ',' == cTabDec ? '.' : '\'';

        bool bNumFound = false;
        const bool bTabCompat = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT);

        for( ; i < nEnd; ++i )
        {
            const sal_Unicode cPos = GetChar( i );
            switch( cPos )
            {
            case CH_TXTATR_BREAKWORD:
            case CH_TXTATR_INWORD:
                if( !HasHint( i ))
                    break;
                SAL_FALLTHROUGH;

            case CHAR_SOFTHYPHEN:
            case CHAR_HARDHYPHEN:
            case CHAR_HARDBLANK:
            case CH_TAB:
            case CH_BREAK:
            case CHAR_ZWSP :
            case CHAR_ZWNBSP :
                m_cHookChar = cPos;
                return i;

            case CHAR_UNDERSCORE:
                if (TextFrameIndex(COMPLETE_STRING) == m_nUnderScorePos)
                    m_nUnderScorePos = i;
                break;

            default:
                if ( cTabDec )
                {
                    if( cTabDec == cPos )
                    {
                        OSL_ENSURE( cPos, "Unexpected end of string" );
                        if( cPos ) // robust
                        {
                            m_cHookChar = cPos;
                            return i;
                        }
                    }

                    // Compatibility: First non-digit character behind a
                    // a digit character becomes the hook character
                    if ( bTabCompat )
                    {
                        if ( ( 0x2F < cPos && cPos < 0x3A ) ||
                             ( bNumFound && ( cPos == cThousandSep || cPos == cThousandSep2 ) ) )
                        {
                            bNumFound = true;
                        }
                        else
                        {
                            if ( bNumFound )
                            {
                                m_cHookChar = cPos;
                                SetTabDecimal( cPos );
                                return i;
                            }
                        }
                    }
                }
            }
        }

        // Check if character *behind* the portion has
        // to become the hook:
        if (i == nEnd && i < TextFrameIndex(GetText().getLength()) && bNumFound)
        {
            const sal_Unicode cPos = GetChar( i );
            if ( cPos != cTabDec && cPos != cThousandSep && cPos !=cThousandSep2 && ( 0x2F >= cPos || cPos >= 0x3A ) )
            {
                m_cHookChar = GetChar( i );
                SetTabDecimal( m_cHookChar );
            }
        }

        return i;
    }

    bool SwTextFormatInfo::LastKernPortion()
    {
        if( GetLast() )
        {
            if( GetLast()->IsKernPortion() )
                return true;
            if( GetLast()->Width() || ( GetLast()->GetLen() &&
                !GetLast()->IsHolePortion() ) )
                return false;
        }
        SwLinePortion* pPor = GetRoot();
        SwLinePortion *pKern = nullptr;
        while( pPor )
        {
            if( pPor->IsKernPortion() )
                pKern = pPor;
            else if( pPor->Width() || ( pPor->GetLen() && !pPor->IsHolePortion() ) )
                pKern = nullptr;
            pPor = pPor->GetPortion();
        }
        if( pKern )
        {
            SetLast( pKern );
            return true;
        }
        return false;
    }

    SwTwips SwTextFormatInfo::GetLineWidth()
    {
        SwTwips nLineWidth = Width() - X();

        const bool bTabOverMargin = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
            DocumentSettingId::TAB_OVER_MARGIN);
        if (!bTabOverMargin)
            return nLineWidth;

        SwTabPortion* pLastTab = GetLastTab();
        if (!pLastTab)
            return nLineWidth;

        // Consider tab portions over the printing bounds of the text frame.
        if (pLastTab->GetTabPos() <= Width())
            return nLineWidth;

        // If there is one such tab portion, then text is allowed to use the full
        // text frame area (even over the actual tab portion).
        nLineWidth = GetTextFrame()->getFrameArea().Width() - X();

        return nLineWidth;
    }

    SwTextSlot::SwTextSlot(
        const SwTextSizeInfo *pNew,
        const SwLinePortion *pPor,
        bool bTextLen,
        bool bExgLists,
        OUString const & rCh )
        : pOldText(nullptr)
        , m_pOldSmartTagList(nullptr)
        , m_pOldGrammarCheckList(nullptr)
        , nIdx(0)
        , nLen(0)
        , pInf(nullptr)
    {
        if( rCh.isEmpty() )
        {
            bOn = pPor->GetExpText( *pNew, aText );
        }
        else
        {
            aText = rCh;
            bOn = true;
        }

        // The text is replaced ...
        if( bOn )
        {
            pInf = const_cast<SwTextSizeInfo*>(pNew);
            nIdx = pInf->GetIdx();
            nLen = pInf->GetLen();
            pOldText = &(pInf->GetText());
            m_pOldCachedVclData = pInf->GetCachedVclData();
            pInf->SetText( aText );
            pInf->SetIdx(TextFrameIndex(0));
            pInf->SetLen(bTextLen ? TextFrameIndex(pInf->GetText().getLength()) : pPor->GetLen());
            pInf->SetCachedVclData(nullptr);

            // ST2
            if ( bExgLists )
            {
                m_pOldSmartTagList = static_cast<SwTextPaintInfo*>(pInf)->GetSmartTags();
                if (m_pOldSmartTagList)
                {
                    std::pair<SwTextNode const*, sal_Int32> pos(pNew->GetTextFrame()->MapViewToModel(nIdx));
                    SwWrongList const*const pSmartTags(pos.first->GetSmartTags());
                    assert(m_pOldSmartTagList->MergedOrSame(pSmartTags));
                    if (pSmartTags)
                    {
                        const sal_uInt16 nPos = pSmartTags->GetWrongPos(pos.second);
                        const sal_Int32 nListPos = pSmartTags->Pos(nPos);
                        if (nListPos == pos.second && pSmartTags->SubList(nPos) != nullptr)
                        {
                            m_pTempIter.reset(new sw::WrongListIterator(*pSmartTags->SubList(nPos)));
                            static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pTempIter.get());
                        }
                        else if (!m_pTempList && nPos < pSmartTags->Count()
                                    && nListPos < pos.second && !aText.isEmpty())
                        {
                            m_pTempList.reset(new SwWrongList( WRONGLIST_SMARTTAG ));
                            m_pTempList->Insert( OUString(), nullptr, 0, aText.getLength(), 0 );
                            m_pTempIter.reset(new sw::WrongListIterator(*m_pTempList));
                            static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pTempIter.get());
                        }
                        else
                            static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(nullptr);
                    }
                    else
                        static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(nullptr);
                }
                m_pOldGrammarCheckList = static_cast<SwTextPaintInfo*>(pInf)->GetGrammarCheckList();
                if (m_pOldGrammarCheckList)
                {
                    std::pair<SwTextNode const*, sal_Int32> pos(pNew->GetTextFrame()->MapViewToModel(nIdx));
                    SwWrongList const*const pGrammar(pos.first->GetGrammarCheck());
                    assert(m_pOldGrammarCheckList->MergedOrSame(pGrammar));
                    if (pGrammar)
                    {
                        const sal_uInt16 nPos = pGrammar->GetWrongPos(pos.second);
                        const sal_Int32 nListPos = pGrammar->Pos(nPos);
                        if (nListPos == pos.second && pGrammar->SubList(nPos) != nullptr)
                        {
                            m_pTempIter.reset(new sw::WrongListIterator(*pGrammar->SubList(nPos)));
                            static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pTempIter.get());
                        }
                        else if (!m_pTempList && nPos < pGrammar->Count()
                                    && nListPos < pos.second && !aText.isEmpty())
                        {
                            m_pTempList.reset(new SwWrongList( WRONGLIST_GRAMMAR ));
                            m_pTempList->Insert( OUString(), nullptr, 0, aText.getLength(), 0 );
                            m_pTempIter.reset(new sw::WrongListIterator(*m_pTempList));
                            static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pTempIter.get());
                        }
                        else
                            static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(nullptr);
                    }
                    else
                        static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(nullptr);
                }
            }
        }
    }

    SwTextSlot::~SwTextSlot()
    {
        if( bOn )
        {
            pInf->SetCachedVclData(m_pOldCachedVclData);
            pInf->SetText( *pOldText );
            pInf->SetIdx( nIdx );
            pInf->SetLen( nLen );

            // ST2
            // Restore old smart tag list
            if (m_pOldSmartTagList)
                static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pOldSmartTagList);
            if (m_pOldGrammarCheckList)
                static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pOldGrammarCheckList);
        }
    }

    SwFontSave::SwFontSave(const SwTextSizeInfo &rInf, SwFont *pNew,
            SwAttrIter* pItr)
        : pInf(nullptr)
        , pFnt(pNew ? const_cast<SwTextSizeInfo&>(rInf).GetFont() : nullptr)
        , pIter(nullptr)
    {
        if( pFnt )
        {
            pInf = &const_cast<SwTextSizeInfo&>(rInf);
            // In these cases we temporarily switch to the new font:
            // 1. the fonts have a different magic number
            // 2. they have different script types
            // 3. their background colors differ (this is not covered by 1.)
            if( pFnt->DifferentFontCacheId( pNew, pFnt->GetActual() ) ||
                pNew->GetActual() != pFnt->GetActual() ||
                ( ! pNew->GetBackColor() && pFnt->GetBackColor() ) ||
                ( pNew->GetBackColor() && ! pFnt->GetBackColor() ) ||
                ( pNew->GetBackColor() && pFnt->GetBackColor() &&
                  ( *pNew->GetBackColor() != *pFnt->GetBackColor() ) ) )
            {
                pNew->SetTransparent( true );
                pNew->SetAlign( ALIGN_BASELINE );
                pInf->SetFont( pNew );
            }
            else
                pFnt = nullptr;
            pNew->Invalidate();
            pNew->ChgPhysFnt( pInf->GetVsh(), *pInf->GetOut() );
            if( pItr && pItr->GetFnt() == pFnt )
            {
                pIter = pItr;
                pIter->SetFnt( pNew );
            }
        }
    }

    SwFontSave::~SwFontSave()
    {
        if( pFnt )
        {
            // Reset SwFont
            pFnt->Invalidate();
            pInf->SetFont( pFnt );
            if( pIter )
            {
                pIter->SetFnt( pFnt );
                pIter->m_nPosition = COMPLETE_STRING;
            }
        }
    }

    bool SwTextFormatInfo::ChgHyph( const bool bNew )
    {
        const bool bOld = m_bAutoHyph;
        if( m_bAutoHyph != bNew )
        {
            m_bAutoHyph = bNew;
            InitHyph( bNew );
            // Set language in the Hyphenator
            if( m_pFnt )
                m_pFnt->ChgPhysFnt( m_pVsh, *m_pOut );
        }
        return bOld;
    }

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V547 Expression 'cPos' is always true.