    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
     * This file is part of the LibreOffice project.
     *
     * This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     *
     * This file incorporates work covered by the following license notice:
     *
     *   Licensed to the Apache Software Foundation (ASF) under one or more
     *   contributor license agreements. See the NOTICE file distributed
     *   with this work for additional information regarding copyright
     *   ownership. The ASF licenses this file to you under the Apache
     *   License, Version 2.0 (the "License"); you may not use this file
     *   except in compliance with the License. You may obtain a copy of
     *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
     */

    #include <sal/config.h>
    #include <sal/log.hxx>

    #include <bodyfrm.hxx>
    #include <hintids.hxx>
    #include <editeng/keepitem.hxx>
    #include <editeng/hyphenzoneitem.hxx>
    #include <pagefrm.hxx>
    #include <ndtxt.hxx>
    #include <dcontact.hxx>
    #include <dflyobj.hxx>
    #include <flyfrm.hxx>
    #include <ftnfrm.hxx>
    #include <txtftn.hxx>
    #include <fmtftn.hxx>
    #include <paratr.hxx>
    #include <viewopt.hxx>
    #include <viewsh.hxx>
    #include <frmatr.hxx>
    #include <pam.hxx>
    #include <flyfrms.hxx>
    #include <fmtanchr.hxx>
    #include "itrform2.hxx"
    #include "widorp.hxx"
    #include "txtcache.hxx"
    #include "porrst.hxx"
    #include <blink.hxx>
    #include "porfld.hxx"
    #include <sectfrm.hxx>
    #include "pormulti.hxx"
    #include <rootfrm.hxx>
    #include <frmfmt.hxx>
    #include <sortedobjs.hxx>
    #include "portab.hxx"
    #include <editeng/lrspitem.hxx>
    #include <editeng/tstpitem.hxx>
    #include <redline.hxx>
    #include <comphelper/lok.hxx>

    // Tolerance in formatting and text output
    #define SLOPPY_TWIPS    5

    class FormatLevel
    {
        static sal_uInt16 nLevel;
    public:
        FormatLevel()  { ++nLevel; }
        ~FormatLevel() { --nLevel; }
        static sal_uInt16 GetLevel() { return nLevel; }
        static bool LastLevel() { return 10 < nLevel; }
    };
    sal_uInt16 FormatLevel::nLevel = 0;

    void ValidateText( SwFrame *pFrame )     // Friend of frame
    {
        if ( ( ! pFrame->IsVertical() &&
                 pFrame->getFrameArea().Width() == pFrame->GetUpper()->getFramePrintArea().Width() ) ||
             (   pFrame->IsVertical() &&
                 pFrame->getFrameArea().Height() == pFrame->GetUpper()->getFramePrintArea().Height() ) )
        {
            pFrame->setFrameAreaSizeValid(true);
        }
    }

    void SwTextFrame::ValidateFrame()
    {
        vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
        // Validate surroundings to avoid oscillation
        SwSwapIfSwapped swap( this );

        if ( !IsInFly() && !IsInTab() )
        {   // Only validate 'this' when inside a fly, the rest should actually only be
            // needed for footnotes, which do not exist in flys.
            SwSectionFrame* pSct = FindSctFrame();
            if( pSct )
            {
                if( !pSct->IsColLocked() )
                    pSct->ColLock();
                else
                    pSct = nullptr;
            }

            SwFrame *pUp = GetUpper();
            pUp->Calc(pRenderContext);
            if( pSct )
                pSct->ColUnlock();
        }
        ValidateText( this );

        // We at least have to save the MustFit flag!
        OSL_ENSURE( HasPara(), "ResetPreps(), missing ParaPortion." );
        SwParaPortion *pPara = GetPara();
        const bool bMustFit = pPara->IsPrepMustFit();
        ResetPreps();
        pPara->SetPrepMustFit( bMustFit );
    }

    // After a RemoveFootnote the BodyFrame and all Frames contained within it, need to be
    // recalculated, so that the DeadLine is right.
    // First we search outwards, on the way back we calculate everything.
    void ValidateBodyFrame_( SwFrame *pFrame )
    {
        vcl::RenderContext* pRenderContext = pFrame ? pFrame->getRootFrame()->GetCurrShell()->GetOut() : nullptr;
        if( pFrame && !pFrame->IsCellFrame() )
        {
            if( !pFrame->IsBodyFrame() && pFrame->GetUpper() )
                ValidateBodyFrame_( pFrame->GetUpper() );
            if( !pFrame->IsSctFrame() )
                pFrame->Calc(pRenderContext);
            else
            {
                const bool bOld = static_cast<SwSectionFrame*>(pFrame)->IsContentLocked();
                static_cast<SwSectionFrame*>(pFrame)->SetContentLock( true );
                pFrame->Calc(pRenderContext);
                if( !bOld )
                    static_cast<SwSectionFrame*>(pFrame)->SetContentLock( false );
            }
        }
    }

    void SwTextFrame::ValidateBodyFrame()
    {
        SwSwapIfSwapped swap( this );

         // See comment in ValidateFrame()
        if ( !IsInFly() && !IsInTab() &&
             !( IsInSct() && FindSctFrame()->Lower()->IsColumnFrame() ) )
            ValidateBodyFrame_( GetUpper() );
    }

    bool SwTextFrame::GetDropRect_( SwRect &rRect ) const
    {
        SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));

        OSL_ENSURE( HasPara(), "SwTextFrame::GetDropRect_: try again next year." );
        SwTextSizeInfo aInf( const_cast<SwTextFrame*>(this) );
        SwTextMargin aLine( const_cast<SwTextFrame*>(this), &aInf );
        if( aLine.GetDropLines() )
        {
            rRect.Top( aLine.Y() );
            rRect.Left( aLine.GetLineStart() );
            rRect.Height( aLine.GetDropHeight() );
            rRect.Width( aLine.GetDropLeft() );

            if ( IsRightToLeft() )
                SwitchLTRtoRTL( rRect );

            if ( IsVertical() )
                SwitchHorizontalToVertical( rRect );
            return true;
        }

        return false;
    }

    const SwBodyFrame *SwTextFrame::FindBodyFrame() const
    {
        if ( IsInDocBody() )
        {
            const SwFrame *pFrame = GetUpper();
            while( pFrame && !pFrame->IsBodyFrame() )
                pFrame = pFrame->GetUpper();
            return static_cast<const SwBodyFrame*>(pFrame);
        }
        return nullptr;
    }

    bool SwTextFrame::CalcFollow(TextFrameIndex const nTextOfst)
    {
        vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
        SwSwapIfSwapped swap( this );

        OSL_ENSURE( HasFollow(), "CalcFollow: missing Follow." );

        SwTextFrame* pMyFollow = GetFollow();

        SwParaPortion *pPara = GetPara();
        const bool bFollowField = pPara && pPara->IsFollowField();

        if( !pMyFollow->GetOfst() || pMyFollow->GetOfst() != nTextOfst ||
            bFollowField || pMyFollow->IsFieldFollow() ||
            ( pMyFollow->IsVertical() && !pMyFollow->getFramePrintArea().Width() ) ||
            ( ! pMyFollow->IsVertical() && !pMyFollow->getFramePrintArea().Height() ) )
        {
    #if OSL_DEBUG_LEVEL > 0
            const SwFrame *pOldUp = GetUpper();
    #endif

            SwRectFnSet aRectFnSet(this);
            SwTwips nOldBottom = aRectFnSet.GetBottom(GetUpper()->getFrameArea());
            SwTwips nMyPos = aRectFnSet.GetTop(getFrameArea());

            const SwPageFrame *pPage = nullptr;
            bool bOldInvaContent = true;
            if ( !IsInFly() && GetNext() )
            {
                pPage = FindPageFrame();
                // Minimize = that is set back if needed - for invalidation see below
                bOldInvaContent  = pPage->IsInvalidContent();
            }

            pMyFollow->SetOfst_( nTextOfst );
            pMyFollow->SetFieldFollow( bFollowField );
            if( HasFootnote() || pMyFollow->HasFootnote() )
            {
                ValidateFrame();
                ValidateBodyFrame();
                if( pPara )
                {
                    pPara->GetReformat() = SwCharRange();
                    pPara->GetDelta() = 0;
                }
            }

            // The footnote area must not get larger
            SwSaveFootnoteHeight aSave( FindFootnoteBossFrame( true ), LONG_MAX );

            pMyFollow->CalcFootnoteFlag();
            if ( !pMyFollow->GetNext() && !pMyFollow->HasFootnote() )
                nOldBottom =  aRectFnSet.IsVert() ? 0 : LONG_MAX;

            while( true )
            {
                if( !FormatLevel::LastLevel() )
                {
                    // If the follow is contained within a column section or column
                    // frame, we need to calculate that first. This is because the
                    // FormatWidthCols() does not work if it is called from MakeAll
                    // of the _locked_ follow.
                    SwSectionFrame* pSct = pMyFollow->FindSctFrame();
                    if( pSct && !pSct->IsAnLower( this ) )
                    {
                        if( pSct->GetFollow() )
                            pSct->SimpleFormat();
                        else if( ( pSct->IsVertical() && !pSct->getFrameArea().Width() ) ||
                                 ( ! pSct->IsVertical() && !pSct->getFrameArea().Height() ) )
                            break;
                    }
                    // i#11760 - Intrinsic format of follow is controlled.
                    if ( FollowFormatAllowed() )
                    {
                        // i#11760 - No nested format of follows, if
                        // text frame is contained in a column frame.
                        // Thus, forbid intrinsic format of follow.
                        {
                            bool bIsFollowInColumn = false;
                            SwFrame* pFollowUpper = pMyFollow->GetUpper();
                            while ( pFollowUpper )
                            {
                                if ( pFollowUpper->IsColumnFrame() )
                                {
                                    bIsFollowInColumn = true;
                                    break;
                                }
                                if ( pFollowUpper->IsPageFrame() ||
                                     pFollowUpper->IsFlyFrame() )
                                {
                                    break;
                                }
                                pFollowUpper = pFollowUpper->GetUpper();
                            }
                            if ( bIsFollowInColumn )
                            {
                                pMyFollow->ForbidFollowFormat();
                            }
                        }

                        pMyFollow->Calc(pRenderContext);
                        // The Follow can tell from its getFrameArea().Height() that something went wrong
                        OSL_ENSURE( !pMyFollow->GetPrev(), "SwTextFrame::CalcFollow: cheesy follow" );
                        if( pMyFollow->GetPrev() )
                        {
                            pMyFollow->Prepare();
                            pMyFollow->Calc(pRenderContext);
                            OSL_ENSURE( !pMyFollow->GetPrev(), "SwTextFrame::CalcFollow: very cheesy follow" );
                        }

                        // i#11760 - Reset control flag for follow format.
                        pMyFollow->AllowFollowFormat();
                    }

                    // Make sure that the Follow gets painted
                    pMyFollow->SetCompletePaint();
                }

                pPara = GetPara();
                // As long as the Follow is requested due to orphan lines, it is passed these
                // and is reformatted if possible
                if( pPara && pPara->IsPrepWidows() )
                    CalcPreps();
                else
                    break;
            }

            if( HasFootnote() || pMyFollow->HasFootnote() )
            {
                ValidateBodyFrame();
                ValidateFrame();
                if( pPara )
                {
                    pPara->GetReformat() = SwCharRange();
                    pPara->GetDelta() = 0;
                }
            }

            if ( pPage )
            {
                if ( !bOldInvaContent )
                    pPage->ValidateContent();
            }

    #if OSL_DEBUG_LEVEL > 0
            OSL_ENSURE( pOldUp == GetUpper(), "SwTextFrame::CalcFollow: heavy follow" );
    #endif

            const long nRemaining =
                     - aRectFnSet.BottomDist( GetUpper()->getFrameArea(), nOldBottom );
            if (  nRemaining > 0 && !GetUpper()->IsSctFrame() &&
                  nRemaining != ( aRectFnSet.IsVert() ?
                                  nMyPos - getFrameArea().Right() :
                                  getFrameArea().Top() - nMyPos ) )
            {
                return true;
            }
        }

        return false;
    }

    void SwTextFrame::MakePos()
    {
        SwFrame::MakePos();
        // Inform LOK clients about change in position of redlines (if any)
        if(comphelper::LibreOfficeKit::isActive())
        {
            SwTextNode const* pTextNode = GetTextNodeFirst();
            const SwRedlineTable& rTable = pTextNode->getIDocumentRedlineAccess().GetRedlineTable();
            for (SwRedlineTable::size_type nRedlnPos = 0; nRedlnPos < rTable.size(); ++nRedlnPos)
            {
                SwRangeRedline* pRedln = rTable[nRedlnPos];
                if (pTextNode->GetIndex() == pRedln->GetPoint()->nNode.GetNode().GetIndex())
                {
                    pRedln->MaybeNotifyRedlinePositionModification(getFrameArea().Top());
                    if (GetMergedPara()
                        && pRedln->GetType() == nsRedlineType_t::REDLINE_DELETE
                        && pRedln->GetPoint()->nNode != pRedln->GetMark()->nNode)
                    {
                        pTextNode = pRedln->End()->nNode.GetNode().GetTextNode();
                    }
                }
            }
        }
    }

    void SwTextFrame::AdjustFrame( const SwTwips nChgHght, bool bHasToFit )
    {
        vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
        if( IsUndersized() )
        {
            if( GetOfst() && !IsFollow() ) // A scrolled paragraph (undersized)
                return;
            SetUndersized( nChgHght == 0 || bHasToFit );
        }

        // AdjustFrame is called with a swapped frame during
        // formatting but the frame is not swapped during FormatEmpty
        SwSwapIfSwapped swap( this );
        SwRectFnSet aRectFnSet(this);

        // The Frame's size variable is incremented by Grow or decremented by Shrink.
        // If the size cannot change, nothing should happen!
        if( nChgHght >= 0)
        {
            SwTwips nChgHeight = nChgHght;
            if( nChgHght && !bHasToFit )
            {
                if( IsInFootnote() && !IsInSct() )
                {
                    SwTwips nReal = Grow( nChgHght, true );
                    if( nReal < nChgHght )
                    {
                        SwTwips nBot = aRectFnSet.YInc( aRectFnSet.GetBottom(getFrameArea()),
                                                          nChgHght - nReal );
                        SwFrame* pCont = FindFootnoteFrame()->GetUpper();

                        if( aRectFnSet.BottomDist( pCont->getFrameArea(), nBot ) > 0 )
                        {
                            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                            aRectFnSet.AddBottom( aFrm, nChgHght );

                            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);

                            if( aRectFnSet.IsVert() )
                            {
                                aPrt.SSize().AdjustWidth(nChgHght );
                            }
                            else
                            {
                                aPrt.SSize().AdjustHeight(nChgHght );
                            }

                            return;
                        }
                    }
                }

                Grow( nChgHght );

                if ( IsInFly() )
                {
                    // If one of the Upper is a Fly, it's very likely that this fly changes its
                    // position by the Grow. Therefore, my position has to be corrected also or
                    // the check further down is not meaningful.
                    // The predecessors need to be calculated, so that the position can be
                    // calculated correctly.
                    if ( GetPrev() )
                    {
                        SwFrame *pPre = GetUpper()->Lower();
                        do
                        {   pPre->Calc(pRenderContext);
                            pPre = pPre->GetNext();
                        } while ( pPre && pPre != this );
                    }
                    const Point aOldPos( getFrameArea().Pos() );
                    MakePos();
                    if ( aOldPos != getFrameArea().Pos() )
                    {
                        // i#28701 - No format is performed for the floating screen objects.
                        InvalidateObjs();
                    }
                }
                nChgHeight = 0;
            }
            // A Grow() is always accepted by the Layout, even if the
            // FixSize of the surrounding layout frame should not allow it.
            // We text for this case and correct the values.
            // The Frame must NOT be shrunk further than its size permits
            // even in the case of an emergency.
            SwTwips nRstHeight;
            if ( IsVertical() )
            {
                OSL_ENSURE( ! IsSwapped(),"Swapped frame while calculating nRstHeight" );

                if ( IsVertLR() )
                        nRstHeight = GetUpper()->getFrameArea().Left()
                                   + GetUpper()->getFramePrintArea().Left()
                                   + GetUpper()->getFramePrintArea().Width()
                                   - getFrameArea().Left();
                else
                    nRstHeight = getFrameArea().Left() + getFrameArea().Width() -
                                ( GetUpper()->getFrameArea().Left() + GetUpper()->getFramePrintArea().Left() );
             }
            else
                nRstHeight = GetUpper()->getFrameArea().Top()
                           + GetUpper()->getFramePrintArea().Top()
                           + GetUpper()->getFramePrintArea().Height()
                           - getFrameArea().Top();

            // We can get a bit of space in table cells, because there could be some
            // left through a vertical alignment to the top.
            // Assure that first lower in upper is the current one or is valid.
            if ( IsInTab() &&
                 ( GetUpper()->Lower() == this ||
                   GetUpper()->Lower()->isFrameAreaDefinitionValid() ) )
            {
                long nAdd = aRectFnSet.YDiff( aRectFnSet.GetTop(GetUpper()->Lower()->getFrameArea()),
                                                aRectFnSet.GetPrtTop(*GetUpper()) );
                OSL_ENSURE( nAdd >= 0, "Ey" );
                nRstHeight += nAdd;
            }

            // nRstHeight < 0 means that the TextFrame is located completely outside of its Upper.
            // This can happen, if it's located within a FlyAtContentFrame, which changed sides by a
            // Grow(). In such a case, it's wrong to execute the following Grow().
            // In the case of a bug, we end up with an infinite loop.
            SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
            SwTwips nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());

            if( nRstHeight < nFrameHeight )
            {
                // It can be that I have the right size, but the Upper is too small and can get me some room
                if( ( nRstHeight >= 0 || ( IsInFootnote() && IsInSct() ) ) && !bHasToFit )
                    nRstHeight += GetUpper()->Grow( nFrameHeight - nRstHeight );
                // In column sections we do not want to get too big or else more areas are created by
                // GetNextSctLeaf. Instead, we shrink and remember bUndersized, so that FormatWidthCols
                // can calculate the right column size.
                if ( nRstHeight < nFrameHeight )
                {
                    if( bHasToFit || !IsMoveable() ||
                        ( IsInSct() && !FindSctFrame()->MoveAllowed(this) ) )
                    {
                        SetUndersized( true );
                        Shrink( std::min( ( nFrameHeight - nRstHeight), nPrtHeight ) );
                    }
                    else
                        SetUndersized( false );
                }
            }
            else if( nChgHeight )
            {
                if( nRstHeight - nFrameHeight < nChgHeight )
                    nChgHeight = nRstHeight - nFrameHeight;
                if( nChgHeight )
                    Grow( nChgHeight );
            }
        }
        else
            Shrink( -nChgHght );
    }

    css::uno::Sequence< css::style::TabStop > SwTextFrame::GetTabStopInfo( SwTwips CurrentPos )
    {
        css::uno::Sequence< css::style::TabStop > tabs(1);
        css::style::TabStop ts;

        SwTextFormatInfo     aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
        SwTextFormatter      aLine( this, &aInf );
        SwTextCursor         TextCursor( this, &aInf );
        const Point aCharPos( TextCursor.GetTopLeft() );

        SwTwips nRight = aLine.Right();
        CurrentPos -= aCharPos.X();

        // get current tab stop information stored in the Frame
        const SvxTabStop *pTS = aLine.GetLineInfo().GetTabStop( CurrentPos, nRight );

        if( !pTS )
        {
            return css::uno::Sequence< css::style::TabStop >();
        }

        // copy tab stop information into a Sequence, which only contains one element.
        ts.Position = pTS->GetTabPos();
        ts.DecimalChar = pTS->GetDecimal();
        ts.FillChar = pTS->GetFill();
        switch( pTS->GetAdjustment() )
        {
        case SvxTabAdjust::Left   : ts.Alignment = css::style::TabAlign_LEFT; break;
        case SvxTabAdjust::Center : ts.Alignment = css::style::TabAlign_CENTER; break;
        case SvxTabAdjust::Right  : ts.Alignment = css::style::TabAlign_RIGHT; break;
        case SvxTabAdjust::Decimal: ts.Alignment = css::style::TabAlign_DECIMAL; break;
        case SvxTabAdjust::Default: ts.Alignment = css::style::TabAlign_DEFAULT; break;
        default: break; // prevent warning
        }

        tabs[0] = ts;
        return tabs;
    }

    // AdjustFollow expects the following situation:
    // The SwTextIter points to the lower end of the Master, the Offset is set in the Follow.
    // nOffset holds the Offset in the text string, from which the Master closes
    // and the Follow starts.
    // If it's 0, the FollowFrame is deleted.
    void SwTextFrame::AdjustFollow_( SwTextFormatter &rLine,
                     const TextFrameIndex nOffset, const TextFrameIndex nEnd,
                                 const sal_uInt8 nMode )
    {
        SwFrameSwapper aSwapper( this, false );

        // We got the rest of the text mass: Delete all Follows
        // DummyPortions() are a special case.
        // Special cases are controlled by parameter <nMode>.
        if( HasFollow() && !(nMode & 1) && nOffset == nEnd )
        {
            while( GetFollow() )
            {
                if( GetFollow()->IsLocked() )
                {
                    OSL_FAIL( "+SwTextFrame::JoinFrame: Follow is locked." );
                    return;
                }
                if (GetFollow()->IsDeleteForbidden())
                    return;
                JoinFrame();
            }

            return;
        }

        // Dancing on the volcano: We'll just format the last line quickly
        // for the QuoVadis stuff.
        // The Offset can move of course:
        const TextFrameIndex nNewOfst = (IsInFootnote() && (!GetIndNext() || HasFollow()))
            ? rLine.FormatQuoVadis(nOffset) : nOffset;

        if( !(nMode & 1) )
        {
            // We steal text mass from our Follows
            // It can happen that we have to join some of them
            while( GetFollow() && GetFollow()->GetFollow() &&
                   nNewOfst >= GetFollow()->GetFollow()->GetOfst() )
            {
                JoinFrame();
            }
        }

        // The Offset moved
        if( GetFollow() )
        {
            if ( nMode )
                GetFollow()->ManipOfst(TextFrameIndex(0));

            if ( CalcFollow( nNewOfst ) )   // CalcFollow only at the end, we do a SetOfst there
                rLine.SetOnceMore( true );
        }
    }

    SwContentFrame *SwTextFrame::JoinFrame()
    {
        OSL_ENSURE( GetFollow(), "+SwTextFrame::JoinFrame: no follow" );
        SwTextFrame  *pFoll = GetFollow();

        SwTextFrame *pNxt = pFoll->GetFollow();

        // All footnotes of the to-be-destroyed Follow are relocated to us
        TextFrameIndex nStart = pFoll->GetOfst();
        if ( pFoll->HasFootnote() )
        {
            SwFootnoteBossFrame *pFootnoteBoss = nullptr;
            SwFootnoteBossFrame *pEndBoss = nullptr;
            SwTextNode const* pNode(nullptr);
            sw::MergedAttrIter iter(*pFoll);
            for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
            {
                if (RES_TXTATR_FTN == pHt->Which()
                    && nStart <= pFoll->MapModelToView(pNode, pHt->GetStart()))
                {
                    if (pHt->GetFootnote().IsEndNote())
                    {
                        if (!pEndBoss)
                            pEndBoss = pFoll->FindFootnoteBossFrame();
                        SwFootnoteBossFrame::ChangeFootnoteRef( pFoll, static_cast<const SwTextFootnote*>(pHt), this );
                    }
                    else
                    {
                        if (!pFootnoteBoss)
                            pFootnoteBoss = pFoll->FindFootnoteBossFrame( true );
                        SwFootnoteBossFrame::ChangeFootnoteRef( pFoll, static_cast<const SwTextFootnote*>(pHt), this );
                    }
                    SetFootnote( true );
                }
            }
        }

    #ifdef DBG_UTIL
        else if ( pFoll->isFramePrintAreaValid() ||
                  pFoll->isFrameAreaSizeValid() )
        {
            pFoll->CalcFootnoteFlag();
            OSL_ENSURE( !pFoll->HasFootnote(), "Missing FootnoteFlag." );
        }
    #endif

        pFoll->MoveFlyInCnt( this, nStart, TextFrameIndex(COMPLETE_STRING) );
        pFoll->SetFootnote( false );
        // i#27138
        // Notify accessibility paragraphs objects about changed CONTENT_FLOWS_FROM/_TO relation.
        // Relation CONTENT_FLOWS_FROM for current next paragraph will change
        // and relation CONTENT_FLOWS_TO for current previous paragraph, which
        // is <this>, will change.
        {
            SwViewShell* pViewShell( pFoll->getRootFrame()->GetCurrShell() );
            if ( pViewShell && pViewShell->GetLayout() &&
                 pViewShell->GetLayout()->IsAnyShellAccessible() )
            {
                pViewShell->InvalidateAccessibleParaFlowRelation(
                                dynamic_cast<SwTextFrame*>(pFoll->FindNextCnt( true )),
                                this );
            }
        }
        pFoll->Cut();
        SetFollow(pNxt);
        SwFrame::DestroyFrame(pFoll);
        return pNxt;
    }

    void SwTextFrame::SplitFrame(TextFrameIndex const nTextPos)
    {
        SwSwapIfSwapped swap( this );

        // The Paste sends a Modify() to me
        // I lock myself, so that my data does not disappear
        TextFrameLockGuard aLock( this );
        SwTextFrame *const pNew = static_cast<SwTextFrame *>(GetTextNodeFirst()->MakeFrame(this));

        pNew->SetFollow( GetFollow() );
        SetFollow( pNew );

        pNew->Paste( GetUpper(), GetNext() );
        // i#27138
        // notify accessibility paragraphs objects about changed CONTENT_FLOWS_FROM/_TO relation.
        // Relation CONTENT_FLOWS_FROM for current next paragraph will change
        // and relation CONTENT_FLOWS_TO for current previous paragraph, which
        // is <this>, will change.
        {
            SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
            if ( pViewShell && pViewShell->GetLayout() &&
                 pViewShell->GetLayout()->IsAnyShellAccessible() )
            {
                pViewShell->InvalidateAccessibleParaFlowRelation(
                                dynamic_cast<SwTextFrame*>(pNew->FindNextCnt( true )),
                                this );
            }
        }

        // If footnotes end up in pNew bz our actions, we need
        // to re-register them
        if ( HasFootnote() )
        {
            SwFootnoteBossFrame *pFootnoteBoss = nullptr;
            SwFootnoteBossFrame *pEndBoss = nullptr;
            SwTextNode const* pNode(nullptr);
            sw::MergedAttrIter iter(*this);
            for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
            {
                if (RES_TXTATR_FTN == pHt->Which()
                    && nTextPos <= MapModelToView(pNode, pHt->GetStart()))
                {
                    if (pHt->GetFootnote().IsEndNote())
                    {
                        if (!pEndBoss)
                            pEndBoss = FindFootnoteBossFrame();
                        SwFootnoteBossFrame::ChangeFootnoteRef( this, static_cast<const SwTextFootnote*>(pHt), pNew );
                    }
                    else
                    {
                        if (!pFootnoteBoss)
                            pFootnoteBoss = FindFootnoteBossFrame( true );
                        SwFootnoteBossFrame::ChangeFootnoteRef( this, static_cast<const SwTextFootnote*>(pHt), pNew );
                    }
                    pNew->SetFootnote( true );
                }
            }
        }

    #ifdef DBG_UTIL
        else
        {
            CalcFootnoteFlag( nTextPos - TextFrameIndex(1) );
            OSL_ENSURE( !HasFootnote(), "Missing FootnoteFlag." );
        }
    #endif

        MoveFlyInCnt( pNew, nTextPos, TextFrameIndex(COMPLETE_STRING) );

        // No SetOfst or CalcFollow, because an AdjustFollow follows immediately anyways

        pNew->ManipOfst( nTextPos );
    }

    void SwTextFrame::SetOfst_(TextFrameIndex const nNewOfst)
    {
        // We do not need to invalidate out Follow.
        // We are a Follow, get formatted right away and call
        // SetOfst() from there
        mnOffset = nNewOfst;
        SwParaPortion *pPara = GetPara();
        if( pPara )
        {
            SwCharRange &rReformat = pPara->GetReformat();
            rReformat.Start() = TextFrameIndex(0);
            rReformat.Len() = TextFrameIndex(GetText().getLength());
            pPara->GetDelta() = sal_Int32(rReformat.Len());
        }
        InvalidateSize();
    }

    bool SwTextFrame::CalcPreps()
    {
        OSL_ENSURE( ! IsVertical() || ! IsSwapped(), "SwTextFrame::CalcPreps with swapped frame" );
        SwRectFnSet aRectFnSet(this);

        SwParaPortion *pPara = GetPara();
        if ( !pPara )
            return false;
        const bool bPrep = pPara->IsPrep();
        const bool bPrepWidows = pPara->IsPrepWidows();
        const bool bPrepAdjust = pPara->IsPrepAdjust();
        const bool bPrepMustFit = pPara->IsPrepMustFit();
        ResetPreps();

        bool bRet = false;
        if( bPrep && !pPara->GetReformat().Len() )
        {
            // PREP_WIDOWS means that the orphans rule got activated in the Follow.
            // In unfortunate cases we could also have a PrepAdjust!
            if( bPrepWidows )
            {
                if( !GetFollow() )
                {
                    OSL_ENSURE( GetFollow(), "+SwTextFrame::CalcPreps: no credits" );
                    return false;
                }

                // We need to prepare for two cases:
                // We were able to hand over a few lines to the Follow
                // -> we need to shrink
                // or we need to go on the next page
                // -> we let our Frame become too big

                SwTwips nChgHeight = GetParHeight();
                if( nChgHeight >= aRectFnSet.GetHeight(getFramePrintArea()) )
                {
                    if( bPrepMustFit )
                    {
                        GetFollow()->SetJustWidow( true );
                        GetFollow()->Prepare();
                    }
                    else if ( aRectFnSet.IsVert() )
                    {
                        {
                            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                            aFrm.Width( aFrm.Width() + aFrm.Left() );
                            aFrm.Left( 0 );
                        }

                        {
                            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                            aPrt.Width( aPrt.Width() + getFrameArea().Left() );
                        }

                        SetWidow( true );
                    }
                    else
                    {
                        SwTwips nTmp  = TWIPS_MAX/2 - (getFrameArea().Top()+10000);
                        SwTwips nDiff = nTmp - getFrameArea().Height();

                        {
                            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                            aFrm.Height( nTmp );
                        }

                        {
                            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                            aPrt.Height( aPrt.Height() + nDiff );
                        }

                        SetWidow( true );
                    }
                }
                else
                {
                    OSL_ENSURE( nChgHeight < aRectFnSet.GetHeight(getFramePrintArea()),
                            "+SwTextFrame::CalcPrep: want to shrink" );

                    nChgHeight = aRectFnSet.GetHeight(getFramePrintArea()) - nChgHeight;

                    GetFollow()->SetJustWidow( true );
                    GetFollow()->Prepare();
                    Shrink( nChgHeight );
                    SwRect &rRepaint = pPara->GetRepaint();

                    if ( aRectFnSet.IsVert() )
                    {
                        SwRect aRepaint( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
                        SwitchVerticalToHorizontal( aRepaint );
                        rRepaint.Chg( aRepaint.Pos(), aRepaint.SSize() );
                    }
                    else
                        rRepaint.Chg( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );

                    if( 0 >= rRepaint.Width() )
                        rRepaint.Width(1);
                }
                bRet = true;
            }
            else if ( bPrepAdjust )
            {
                if ( HasFootnote() )
                {
                    if( !CalcPrepFootnoteAdjust() )
                    {
                        if( bPrepMustFit )
                        {
                            SwTextLineAccess aAccess( this );
                            aAccess.GetPara()->SetPrepMustFit(true);
                        }
                        return false;
                    }
                }

                {
                    SwSwapIfNotSwapped swap( this );

                    SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
                    SwTextFormatter aLine( this, &aInf );

                    WidowsAndOrphans aFrameBreak( this );
                    // Whatever the attributes say: we split the paragraph in
                    // MustFit in any case
                    if( bPrepMustFit )
                    {
                        aFrameBreak.SetKeep( false );
                        aFrameBreak.ClrOrphLines();
                    }
                    // Before calling FormatAdjust, we need to make sure
                    // that the lines protruding at the bottom get indeed
                    // truncated
                    bool bBreak = aFrameBreak.IsBreakNowWidAndOrp( aLine );
                    bRet = true;
                    while( !bBreak && aLine.Next() )
                    {
                        bBreak = aFrameBreak.IsBreakNowWidAndOrp( aLine );
                    }
                    if( bBreak )
                    {
                        // We run into troubles: when TruncLines get called, the
                        // conditions in IsInside change immediately such that
                        // IsBreakNow can return different results.
                        // For this reason, we make it clear to rFrameBreak, that the
                        // end is reached at the location of rLine.
                        // Let's see if it works ...
                        aLine.TruncLines();
                        aFrameBreak.SetRstHeight( aLine );
                        FormatAdjust( aLine, aFrameBreak, TextFrameIndex(aInf.GetText().getLength()), aInf.IsStop() );
                    }
                    else
                    {
                        if( !GetFollow() )
                        {
                            FormatAdjust( aLine, aFrameBreak,
                                TextFrameIndex(aInf.GetText().getLength()), aInf.IsStop() );
                        }
                        else if ( !aFrameBreak.IsKeepAlways() )
                        {
                            // We delete a line before the Master, because the Follow
                            // could hand over a line
                            const SwCharRange aFollowRg(GetFollow()->GetOfst(), TextFrameIndex(1));
                            pPara->GetReformat() += aFollowRg;
                            // We should continue!
                        bRet = false;
                        }
                    }
                }

                // A final check, if FormatAdjust() didn't help we need to
                // truncate
                if( bPrepMustFit )
                {
                    const SwTwips nMust = aRectFnSet.GetPrtBottom(*GetUpper());
                    const SwTwips nIs   = aRectFnSet.GetBottom(getFrameArea());

                    if( aRectFnSet.IsVert() && nIs < nMust )
                    {
                        Shrink( nMust - nIs );

                        if( getFramePrintArea().Width() < 0 )
                        {
                            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                            aPrt.Width( 0 );
                        }

                        SetUndersized( true );
                    }
                    else if ( ! aRectFnSet.IsVert() && nIs > nMust )
                    {
                        Shrink( nIs - nMust );

                        if( getFramePrintArea().Height() < 0 )
                        {
                            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                            aPrt.Height( 0 );
                        }

                        SetUndersized( true );
                    }
                }
            }
        }
        pPara->SetPrepMustFit( bPrepMustFit );
        return bRet;
    }

    // We rewire the footnotes and the character bound objects
    #define CHG_OFFSET( pFrame, nNew )\
        {\
            if( pFrame->GetOfst() < nNew )\
                pFrame->MoveFlyInCnt( this, TextFrameIndex(0), nNew );\
            else if( pFrame->GetOfst() > nNew )\
                MoveFlyInCnt( pFrame, nNew, TextFrameIndex(COMPLETE_STRING) );\
        }

    void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
                                 WidowsAndOrphans &rFrameBreak,
                                 TextFrameIndex const nStrLen,
                                 const bool bDummy )
    {
        SwSwapIfNotSwapped swap( this );

        SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();

        TextFrameIndex nEnd = rLine.GetStart();

        const bool bHasToFit = pPara->IsPrepMustFit();

        // The StopFlag is set by footnotes which want to go onto the next page
        // Call base class method <SwTextFrameBreak::IsBreakNow(..)>
        // instead of method <WidowsAndOrphans::IsBreakNow(..)> to get a break,
        // even if due to widow rule no enough lines exists.
        sal_uInt8 nNew = ( !GetFollow() &&
                           nEnd < nStrLen &&
                           ( rLine.IsStop() ||
                             ( bHasToFit
                               ? ( rLine.GetLineNr() > 1 &&
                                   !rFrameBreak.IsInside( rLine ) )
                               : rFrameBreak.IsBreakNow( rLine ) ) ) )
                         ? 1 : 0;
        // i#84870
        // no split of text frame, which only contains a as-character anchored object
        bool bOnlyContainsAsCharAnchoredObj =
                !IsFollow() && nStrLen == TextFrameIndex(1) &&
                GetDrawObjs() && GetDrawObjs()->size() == 1 &&
                (*GetDrawObjs())[0]->GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;

        // Still try split text frame if we have columns.
        if (FindColFrame())
            bOnlyContainsAsCharAnchoredObj = false;

        if ( nNew && bOnlyContainsAsCharAnchoredObj )
        {
            nNew = 0;
        }

        if ( nNew )
        {
            SplitFrame( nEnd );
        }

        const SwFrame *pBodyFrame = FindBodyFrame();

        const long nBodyHeight = pBodyFrame ? ( IsVertical() ?
                                              pBodyFrame->getFrameArea().Width() :
                                              pBodyFrame->getFrameArea().Height() ) : 0;

        // If the current values have been calculated, show that they
        // are valid now
        pPara->GetReformat() = SwCharRange();
        bool bDelta = pPara->GetDelta() != 0;
        pPara->GetDelta() = 0;

        if( rLine.IsStop() )
        {
            rLine.TruncLines( true );
            nNew = 1;
        }

        // FindBreak truncates the last line
        if( !rFrameBreak.FindBreak( this, rLine, bHasToFit ) )
        {
            // If we're done formatting, we set nEnd to the end.
            // AdjustFollow might execute JoinFrame() because of this.
            // Else, nEnd is the end of the last line in the Master.
            TextFrameIndex nOld = nEnd;
            nEnd = rLine.GetEnd();
            if( GetFollow() )
            {
                if( nNew && nOld < nEnd )
                    RemoveFootnote( nOld, nEnd - nOld );
                CHG_OFFSET( GetFollow(), nEnd )
                if( !bDelta )
                    GetFollow()->ManipOfst( nEnd );
            }
        }
        else
        {   // If we pass over lines, we must not call Join in Follows, instead we even
            // need to create a Follow.
            // We also need to do this if the whole mass of text remains in the Master,
            // because a hard line break could necessitate another line (without text mass)!
            nEnd = rLine.GetEnd();
            if( GetFollow() )
            {
                // Another case for not joining the follow:
                // Text frame has no content, but a numbering. Then, do *not* join.
                // Example of this case: When an empty, but numbered paragraph
                // at the end of page is completely displaced by a fly frame.
                // Thus, the text frame introduced a follow by a
                // <SwTextFrame::SplitFrame(..)> - see below. The follow then shows
                // the numbering and must stay.
                if ( GetFollow()->GetOfst() != nEnd ||
                     GetFollow()->IsFieldFollow() ||
                     (nStrLen == TextFrameIndex(0) && GetTextNodeForParaProps()->GetNumRule()))
                {
                    nNew |= 3;
                }
                else if (FindTabFrame() && nEnd > TextFrameIndex(0) &&
                    rLine.GetInfo().GetChar(nEnd - TextFrameIndex(1)) == CH_BREAK)
                {
                    // We are in a table, the paragraph has a follow and the text
                    // ends with a hard line break. Don't join the follow just
                    // because the follow would have no content, we may still need it
                    // for the paragraph mark.
                    nNew |= 1;
                }
                CHG_OFFSET( GetFollow(), nEnd )
                GetFollow()->ManipOfst( nEnd );
            }
            else
            {
                // Only split frame, if the frame contains
                // content or contains no content, but has a numbering.
                // i#84870 - No split, if text frame only contains one
                // as-character anchored object.
                if ( !bOnlyContainsAsCharAnchoredObj &&
                     (nStrLen > TextFrameIndex(0) ||
                       (nStrLen == TextFrameIndex(0) && GetTextNodeForParaProps()->GetNumRule()))
                   )
                {
                    SplitFrame( nEnd );
                    nNew |= 3;
                }
            }
            // If the remaining height changed e.g by RemoveFootnote() we need to
            // fill up in order to avoid oscillation.
            if( bDummy && pBodyFrame &&
               nBodyHeight < ( IsVertical() ?
                               pBodyFrame->getFrameArea().Width() :
                               pBodyFrame->getFrameArea().Height() ) )
                rLine.MakeDummyLine();
        }

        // In AdjustFrame() we set ourselves via Grow/Shrink
        // In AdjustFollow() we set our FollowFrame

        const SwTwips nDocPrtTop = getFrameArea().Top() + getFramePrintArea().Top();
        const SwTwips nOldHeight = getFramePrintArea().SSize().Height();
        SwTwips nChg = rLine.CalcBottomLine() - nDocPrtTop - nOldHeight;

        //#i84870# - no shrink of text frame, if it only contains one as-character anchored object.
        if ( nChg < 0 && !bDelta && bOnlyContainsAsCharAnchoredObj )
        {
            nChg = 0;
        }

        // Vertical Formatting:
        // The (rotated) repaint rectangle's x coordinate referes to the frame.
        // If the frame grows (or shirks) the repaint rectangle cannot simply
        // be rotated back after formatting, because we use the upper left point
        // of the frame for rotation. This point changes when growing/shrinking.

        if ( IsVertical() && !IsVertLR() && nChg )
        {
            SwRect &rRepaint = pPara->GetRepaint();
            rRepaint.Left( rRepaint.Left() - nChg );
            rRepaint.Width( rRepaint.Width() - nChg );
        }

        AdjustFrame( nChg, bHasToFit );

        if( HasFollow() || IsInFootnote() )
            AdjustFollow_( rLine, nEnd, nStrLen, nNew );

        pPara->SetPrepMustFit( false );
    }

    // bPrev is set whether Reformat.Start() was called because of Prev().
    // Else, wo don't know whether we can limit the repaint or not.
    bool SwTextFrame::FormatLine( SwTextFormatter &rLine, const bool bPrev )
    {
        OSL_ENSURE( ! IsVertical() || IsSwapped(),
                "SwTextFrame::FormatLine( rLine, bPrev) with unswapped frame" );
        SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
        const SwLineLayout *pOldCur = rLine.GetCurr();
        const TextFrameIndex nOldLen = pOldCur->GetLen();
        const sal_uInt16 nOldAscent = pOldCur->GetAscent();
        const sal_uInt16 nOldHeight = pOldCur->Height();
        const SwTwips nOldWidth = pOldCur->Width() + pOldCur->GetHangingMargin();
        const bool bOldHyph = pOldCur->IsEndHyph();
        SwTwips nOldTop = 0;
        SwTwips nOldBottom = 0;
        if( rLine.GetCurr()->IsClipping() )
            rLine.CalcUnclipped( nOldTop, nOldBottom );

        TextFrameIndex const nNewStart = rLine.FormatLine( rLine.GetStart() );

        OSL_ENSURE( getFrameArea().Pos().Y() + getFramePrintArea().Pos().Y() == rLine.GetFirstPos(),
                "SwTextFrame::FormatLine: frame leaves orbit." );
        OSL_ENSURE( rLine.GetCurr()->Height(),
                "SwTextFrame::FormatLine: line height is zero" );

        // The current line break object
        const SwLineLayout *pNew = rLine.GetCurr();

        bool bUnChg = nOldLen == pNew->GetLen() &&
                      bOldHyph == pNew->IsEndHyph();
        if ( bUnChg && !bPrev )
        {
            const long nWidthDiff = nOldWidth > pNew->Width()
                                    ? nOldWidth - pNew->Width()
                                    : pNew->Width() - nOldWidth;

            // we only declare a line as unchanged, if its main values have not
            // changed and it is not the last line (!paragraph end symbol!)
            bUnChg = nOldHeight == pNew->Height() &&
                     nOldAscent == pNew->GetAscent() &&
                     nWidthDiff <= SLOPPY_TWIPS &&
                     pOldCur->GetNext();
        }

        // Calculate rRepaint
        const SwTwips nBottom = rLine.Y() + rLine.GetLineHeight();
        SwRepaint &rRepaint = pPara->GetRepaint();
        if( bUnChg && rRepaint.Top() == rLine.Y()
                   && (bPrev || nNewStart <= pPara->GetReformat().Start())
                   && (nNewStart < TextFrameIndex(GetText().getLength())))
        {
            rRepaint.Top( nBottom );
            rRepaint.Height( 0 );
        }
        else
        {
            if( nOldTop )
            {
                if( nOldTop < rRepaint.Top() )
                    rRepaint.Top( nOldTop );
                if( !rLine.IsUnclipped() || nOldBottom > rRepaint.Bottom() )
                {
                    rRepaint.Bottom( nOldBottom - 1 );
                    rLine.SetUnclipped( true );
                }
            }
            if( rLine.GetCurr()->IsClipping() && rLine.IsFlyInCntBase() )
            {
                SwTwips nTmpTop, nTmpBottom;
                rLine.CalcUnclipped( nTmpTop, nTmpBottom );
                if( nTmpTop < rRepaint.Top() )
                    rRepaint.Top( nTmpTop );
                if( !rLine.IsUnclipped() || nTmpBottom > rRepaint.Bottom() )
                {
                    rRepaint.Bottom( nTmpBottom - 1 );
                    rLine.SetUnclipped( true );
                }
            }
            else
            {
                if( !rLine.IsUnclipped() || nBottom > rRepaint.Bottom() )
                {
                    rRepaint.Bottom( nBottom - 1 );
                    rLine.SetUnclipped( false );
                }
            }
            SwTwips nRght = std::max( nOldWidth, pNew->Width() +
                                 pNew->GetHangingMargin() );
            SwViewShell *pSh = getRootFrame()->GetCurrShell();
            const SwViewOption *pOpt = pSh ? pSh->GetViewOptions() : nullptr;
            if( pOpt && (pOpt->IsParagraph() || pOpt->IsLineBreak()) )
                nRght += ( std::max( nOldAscent, pNew->GetAscent() ) );
            else
                nRght += ( std::max( nOldAscent, pNew->GetAscent() ) / 4);
            nRght += rLine.GetLeftMargin();
            if( rRepaint.GetOfst() || rRepaint.GetRightOfst() < nRght )
                rRepaint.SetRightOfst( nRght );

            // Finally we enlarge the repaint rectangle if we found an underscore
            // within our line. 40 Twips should be enough
            const bool bHasUnderscore =
                    ( rLine.GetInfo().GetUnderScorePos() < nNewStart );
            if ( bHasUnderscore || rLine.GetCurr()->HasUnderscore() )
                rRepaint.Bottom( rRepaint.Bottom() + 40 );

            const_cast<SwLineLayout*>(rLine.GetCurr())->SetUnderscore( bHasUnderscore );
        }

        // Calculating the good ol' nDelta
        pPara->GetDelta() -= sal_Int32(pNew->GetLen()) - sal_Int32(nOldLen);

        // Stop!
        if( rLine.IsStop() )
            return false;

        // Absolutely another line
        if( rLine.IsNewLine() )
            return true;

        // Until the String's end?
        if (nNewStart >= TextFrameIndex(GetText().getLength()))
            return false;

        if( rLine.GetInfo().IsShift() )
            return true;

        // Reached the Reformat's end?
        const TextFrameIndex nEnd = pPara->GetReformat().Start() +
                            pPara->GetReformat().Len();

        if( nNewStart <= nEnd )
            return true;

        return 0 != pPara->GetDelta();
    }

    void SwTextFrame::Format_( SwTextFormatter &rLine, SwTextFormatInfo &rInf,
                            const bool bAdjust )
    {
        OSL_ENSURE( ! IsVertical() || IsSwapped(),"SwTextFrame::Format_ with unswapped frame" );

        SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
        rLine.SetUnclipped( false );

        const OUString & rString = GetText();
        const TextFrameIndex nStrLen(rString.getLength());

        SwCharRange &rReformat = pPara->GetReformat();
        SwRepaint   &rRepaint = pPara->GetRepaint();
        SwRepaint *pFreeze = nullptr;

        // Due to performance reasons we set rReformat to COMPLETE_STRING in Init()
        // In this case we adjust rReformat
        if( rReformat.Len() > nStrLen )
            rReformat.Len() = nStrLen;

        if( rReformat.Start() + rReformat.Len() > nStrLen )
            rReformat.Len() = nStrLen - rReformat.Start();

        SwTwips nOldBottom;
        if( GetOfst() && !IsFollow() )
        {
            rLine.Bottom();
            nOldBottom = rLine.Y();
            rLine.Top();
        }
        else
            nOldBottom = 0;
        rLine.CharToLine( rReformat.Start() );

        // Words can be swapped-out when inserting a space into the
        // line that comes before the edited one. That's why we also
        // need to format that.
        // Optimization: If rReformat starts after the first word of the line
        // this line cannot possibly influence the previous one.
        // Unfortunately it can: Text size changes + FlyFrames.
        // The backlash can affect multiple lines (Frame!)!

        // i#46560
        // FME: Yes, consider this case: (word) has to go to the next line
        // because) is a forbidden character at the beginning of a line although
        // (word would still fit on the previous line. Adding text right in front
        // of) would not trigger a reformatting of the previous line. Adding 1
        // to the result of FindBrk() does not solve the problem in all cases,
        // nevertheless it should be sufficient.
        bool bPrev = rLine.GetPrev() &&
                         (FindBrk(rString, rLine.GetStart(), rReformat.Start() + TextFrameIndex(1))
                           // i#46560
                           + TextFrameIndex(1)
                           >= rReformat.Start() ||
                           rLine.GetCurr()->IsRest() );
        if( bPrev )
        {
            while( rLine.Prev() )
                if( rLine.GetCurr()->GetLen() && !rLine.GetCurr()->IsRest() )
                {
                    if( !rLine.GetStart() )
                        rLine.Top(); // So that NumDone doesn't get confused
                    break;
                }
            TextFrameIndex nNew = rLine.GetStart() + rLine.GetLength();
            if( nNew )
            {
                --nNew;
                if (CH_BREAK == rString[sal_Int32(nNew)])
                {
                    ++nNew;
                    rLine.Next();
                    bPrev = false;
                }
            }
            rReformat.Len()  += rReformat.Start() - nNew;
            rReformat.Start() = nNew;
        }

        rRepaint.SetOfst( 0 );
        rRepaint.SetRightOfst( 0 );
        rRepaint.Chg( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
        if( pPara->IsMargin() )
            rRepaint.Width( rRepaint.Width() + pPara->GetHangingMargin() );
        rRepaint.Top( rLine.Y() );
        if( 0 >= rRepaint.Width() )
            rRepaint.Width(1);
        WidowsAndOrphans aFrameBreak( this, rInf.IsTest() ? 1 : 0 );

        // rLine is now set to the first line which needs formatting.
        // The bFirst flag makes sure that Next() is not called.
        // The whole thing looks weird, but we need to make sure that
        // rLine stops at the last non-fitting line when calling IsBreakNow.
        bool bFirst  = true;
        bool bFormat = true;

        // The CharToLine() can also get us into the danger zone.
        // In that case we need to walk back until rLine is set
        // to the non-fitting line. Or else the mass of text is lost,
        // because the Ofst was set wrongly in the Follow.

        bool bBreak = ( !pPara->IsPrepMustFit() || rLine.GetLineNr() > 1 )
                        && aFrameBreak.IsBreakNowWidAndOrp( rLine );
        if( bBreak )
        {
            bool bPrevDone = nullptr != rLine.Prev();
            while( bPrevDone && aFrameBreak.IsBreakNowWidAndOrp(rLine) )
                bPrevDone = nullptr != rLine.Prev();
            if( bPrevDone )
            {
                aFrameBreak.SetKeep( false );
                rLine.Next();
            }
            rLine.TruncLines();

            // Play it safe
            aFrameBreak.IsBreakNowWidAndOrp(rLine);
        }

     /* Meaning if the following flags are set:

        Watch(End/Mid)Hyph: we need to format if we have a break at
        the line end/Fly, as long as MaxHyph is reached

        Jump(End/Mid)Flag: the next line which has no break (line end/Fly),
        needs to be formatted, because we could wrap now. This might have been
        forbidden earlier by MaxHyph

        Watch(End/Mid)Hyph: if the last formatted line got a cutoff point, but
        didn't have one before

        Jump(End/Mid)Hyph: if a cutoff point disappears
     */
        bool bJumpEndHyph  = false;
        bool bWatchEndHyph = false;
        bool bJumpMidHyph  = false;
        bool bWatchMidHyph = false;

        const SwAttrSet& rAttrSet = GetTextNodeForParaProps()->GetSwAttrSet();
        bool bMaxHyph = ( 0 !=
            ( rInf.MaxHyph() = rAttrSet.GetHyphenZone().GetMaxHyphens() ) );
        if ( bMaxHyph )
            rLine.InitCntHyph();

        if( IsFollow() && IsFieldFollow() && rLine.GetStart() == GetOfst() )
        {
            SwTextFrame *pMaster = FindMaster();
            OSL_ENSURE( pMaster, "SwTextFrame::Format: homeless follow" );
            const SwLineLayout* pLine=nullptr;
            if (pMaster)
            {
                if (!pMaster->HasPara())
                {   // master could be locked because it's being formatted upstack
                    SAL_WARN("sw", "SwTextFrame::Format_: master not formatted!");
                }
                else
                {
                    SwTextSizeInfo aInf( pMaster );
                    SwTextIter aMasterLine( pMaster, &aInf );
                    aMasterLine.Bottom();
                    pLine = aMasterLine.GetCurr();
                    assert(aMasterLine.GetEnd() == GetOfst());
                }
            }
            SwLinePortion* pRest = pLine ?
                rLine.MakeRestPortion(pLine, GetOfst()) : nullptr;
            if( pRest )
                rInf.SetRest( pRest );
            else
                SetFieldFollow( false );
        }

        /* Ad cancel criterion:
         * In order to recognize, whether a line does not fit onto the page
         * anymore, we need to format it. This overflow is removed again in
         * e.g. AdjustFollow.
         * Another complication: if we are the Master, we need to traverse
         * the lines, because it could happen that one line can overflow
         * from the Follow to the Master.
         */
        do
        {
            if( bFirst )
                bFirst = false;
            else
            {
                if ( bMaxHyph )
                {
                    if ( rLine.GetCurr()->IsEndHyph() )
                        rLine.CntEndHyph()++;
                    else
                        rLine.CntEndHyph() = 0;
                    if ( rLine.GetCurr()->IsMidHyph() )
                        rLine.CntMidHyph()++;
                    else
                        rLine.CntMidHyph() = 0;
                }
                if( !rLine.Next() )
                {
                    if( !bFormat )
                    {
                        SwLinePortion* pRest =
                            rLine.MakeRestPortion( rLine.GetCurr(), rLine.GetEnd() );
                        if( pRest )
                            rInf.SetRest( pRest );
                    }
                    rLine.Insert( new SwLineLayout() );
                    rLine.Next();
                    bFormat = true;
                }
            }
            if ( !bFormat && bMaxHyph &&
                  (bWatchEndHyph || bJumpEndHyph || bWatchMidHyph || bJumpMidHyph) )
            {
                if ( rLine.GetCurr()->IsEndHyph() )
                {
                    if ( bWatchEndHyph )
                        bFormat = ( rLine.CntEndHyph() == rInf.MaxHyph() );
                }
                else
                {
                    bFormat = bJumpEndHyph;
                    bWatchEndHyph = false;
                    bJumpEndHyph = false;
                }
                if ( rLine.GetCurr()->IsMidHyph() )
                {
                    if ( bWatchMidHyph && !bFormat )
                        bFormat = ( rLine.CntEndHyph() == rInf.MaxHyph() );
                }
                else
                {
                    bFormat |= bJumpMidHyph;
                    bWatchMidHyph = false;
                    bJumpMidHyph = false;
                }
            }
            if( bFormat )
            {
                const bool bOldEndHyph = rLine.GetCurr()->IsEndHyph();
                const bool bOldMidHyph = rLine.GetCurr()->IsMidHyph();
                bFormat = FormatLine( rLine, bPrev );
                // There can only be one bPrev ... (???)
                bPrev = false;
                if ( bMaxHyph )
                {
                    if ( rLine.GetCurr()->IsEndHyph() != bOldEndHyph )
                    {
                        bWatchEndHyph = !bOldEndHyph;
                        bJumpEndHyph = bOldEndHyph;
                    }
                    if ( rLine.GetCurr()->IsMidHyph() != bOldMidHyph )
                    {
                        bWatchMidHyph = !bOldMidHyph;
                        bJumpMidHyph = bOldMidHyph;
                    }
                }
            }

            if( !rInf.IsNewLine() )
            {
                if( !bFormat )
                     bFormat = nullptr != rInf.GetRest();
                if( rInf.IsStop() || rInf.GetIdx() >= nStrLen )
                    break;
                if( !bFormat && ( !bMaxHyph || ( !bWatchEndHyph &&
                        !bJumpEndHyph && !bWatchMidHyph && !bJumpMidHyph ) ) )
                {
                    if( GetFollow() )
                    {
                        while( rLine.Next() )
                            ; //Nothing
                        pFreeze = new SwRepaint( rRepaint ); // to minimize painting
                    }
                    else
                        break;
                }
            }
            bBreak = aFrameBreak.IsBreakNowWidAndOrp(rLine);
        }while( !bBreak );

        if( pFreeze )
        {
            rRepaint = *pFreeze;
            delete pFreeze;
        }

        if( !rLine.IsStop() )
        {
            // If we're finished formatting the text and we still
            // have other line objects left, these are superfluous
            // now because the text has gotten shorter.
            if( rLine.GetStart() + rLine.GetLength() >= nStrLen &&
                rLine.GetCurr()->GetNext() )
            {
                rLine.TruncLines();
                rLine.SetTruncLines( true );
            }
        }

        if( !rInf.IsTest() )
        {
            // FormatAdjust does not pay off at OnceMore
            if( bAdjust || !rLine.GetDropFormat() || !rLine.CalcOnceMore() )
            {
                FormatAdjust( rLine, aFrameBreak, nStrLen, rInf.IsStop() );
            }
            if( rRepaint.HasArea() )
                SetRepaint();
            rLine.SetTruncLines( false );
            if( nOldBottom ) // We check whether paragraphs that need scrolling can
                             // be shrunk, so that they don't need scrolling anymore
            {
                rLine.Bottom();
                SwTwips nNewBottom = rLine.Y();
                if( nNewBottom < nOldBottom )
                    SetOfst_(TextFrameIndex(0));
            }
        }
    }

    void SwTextFrame::FormatOnceMore( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
    {
        OSL_ENSURE( ! IsVertical() || IsSwapped(),
                "A frame is not swapped in SwTextFrame::FormatOnceMore" );

        SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
        if( !pPara )
            return;

        // If necessary the pPara
        sal_uInt16 nOld  = static_cast<const SwTextMargin&>(rLine).GetDropHeight();
        bool bShrink = false;
        bool bGrow   = false;
        bool bGoOn   = rLine.IsOnceMore();
        sal_uInt8 nGo    = 0;
        while( bGoOn )
        {
            ++nGo;
            rInf.Init();
            rLine.Top();
            if( !rLine.GetDropFormat() )
                rLine.SetOnceMore( false );
            SwCharRange aRange(TextFrameIndex(0), TextFrameIndex(rInf.GetText().getLength()));
            pPara->GetReformat() = aRange;
            Format_( rLine, rInf );

            bGoOn = rLine.IsOnceMore();
            if( bGoOn )
            {
                const sal_uInt16 nNew = static_cast<const SwTextMargin&>(rLine).GetDropHeight();
                if( nOld == nNew )
                    bGoOn = false;
                else
                {
                    if( nOld > nNew )
                        bShrink = true;
                    else
                        bGrow = true;

                    if( bShrink == bGrow || 5 < nGo )
                        bGoOn = false;

                    nOld = nNew;
                }

                // If something went wrong, we need to reformat again
                if( !bGoOn )
                {
                    rInf.CtorInitTextFormatInfo( getRootFrame()->GetCurrShell()->GetOut(), this );
                    rLine.CtorInitTextFormatter( this, &rInf );
                    rLine.SetDropLines( 1 );
                    rLine.CalcDropHeight( 1 );
                    SwCharRange aTmpRange(TextFrameIndex(0), TextFrameIndex(rInf.GetText().getLength()));
                    pPara->GetReformat() = aTmpRange;
                    Format_( rLine, rInf, true );
                    // We paint everything ...
                    SetCompletePaint();
                }
            }
        }
    }

    void SwTextFrame::Format_( vcl::RenderContext* pRenderContext, SwParaPortion *pPara )
    {
        const bool bIsEmpty = GetText().isEmpty();

        if ( bIsEmpty )
        {
            // Empty lines do not get tortured for very long:
            // pPara is cleared, which is the same as:
            // *pPara = SwParaPortion;
            const bool bMustFit = pPara->IsPrepMustFit();
            pPara->Truncate();
            pPara->FormatReset();
            if( pBlink && pPara->IsBlinking() )
                pBlink->Delete( pPara );

            // delete pSpaceAdd and pKanaComp
            pPara->FinishSpaceAdd();
            pPara->FinishKanaComp();
            pPara->ResetFlags();
            pPara->SetPrepMustFit( bMustFit );
        }

        OSL_ENSURE( ! IsSwapped(), "A frame is swapped before Format_" );

        if ( IsVertical() )
            SwapWidthAndHeight();

        SwTextFormatInfo aInf( pRenderContext, this );
        SwTextFormatter  aLine( this, &aInf );

        HideAndShowObjects();

        Format_( aLine, aInf );

        if( aLine.IsOnceMore() )
            FormatOnceMore( aLine, aInf );

        if ( IsVertical() )
            SwapWidthAndHeight();

        OSL_ENSURE( ! IsSwapped(), "A frame is swapped after Format_" );

        if( 1 < aLine.GetDropLines() )
        {
            if( SvxAdjust::Left != aLine.GetAdjust() &&
                SvxAdjust::Block != aLine.GetAdjust() )
            {
                aLine.CalcDropAdjust();
                aLine.SetPaintDrop( true );
            }

            if( aLine.IsPaintDrop() )
            {
                aLine.CalcDropRepaint();
                aLine.SetPaintDrop( false );
            }
        }
    }

    // We calculate the text frame's size and send a notification.
    // Shrink() or Grow() to adjust the frame's size to the changed required space.
    void SwTextFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs * )
    {
        SwRectFnSet aRectFnSet(this);

        CalcAdditionalFirstLineOffset();

        // The range autopilot or the BASIC interface pass us TextFrames with
        // a width <= 0 from time to time
        if( aRectFnSet.GetWidth(getFramePrintArea()) <= 0 )
        {
            // If MustFit is set, we shrink to the Upper's bottom edge if needed.
            // Else we just take a standard size of 12 Pt. (240 twip).
            SwTextLineAccess aAccess( this );
            long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());

            if( aAccess.GetPara()->IsPrepMustFit() )
            {
                const SwTwips nLimit = aRectFnSet.GetPrtBottom(*GetUpper());
                const SwTwips nDiff = - aRectFnSet.BottomDist( getFrameArea(), nLimit );
                if( nDiff > 0 )
                    Shrink( nDiff );
            }
            else if( 240 < nFrameHeight )
            {
                Shrink( nFrameHeight - 240 );
            }
            else if( 240 > nFrameHeight )
            {
                Grow( 240 - nFrameHeight );
            }

            nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
            const long nTop = aRectFnSet.GetTopMargin(*this);

            if( nTop > nFrameHeight )
            {
                aRectFnSet.SetYMargins( *this, nFrameHeight, 0 );
            }
            else if( aRectFnSet.GetHeight(getFramePrintArea()) < 0 )
            {
                SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                aRectFnSet.SetHeight( aPrt, 0 );
            }

            return;
        }

        const TextFrameIndex nStrLen(GetText().getLength());
        if ( nStrLen || !FormatEmpty() )
        {

            SetEmpty( false );
            // In order to not get confused by nested Formats
            FormatLevel aLevel;
            if( 12 == FormatLevel::GetLevel() )
                return;

            // We could be possibly not allowed to alter the format information
            if( IsLocked() )
                return;

            // Attention: Format() could be triggered by GetFormatted()
            if( IsHiddenNow() )
            {
                long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
                if( nPrtHeight )
                {
                    HideHidden();
                    Shrink( nPrtHeight );
                }
                else
                {
                    // Assure that objects anchored
                    // at paragraph resp. at/as character inside paragraph
                    // are hidden.
                    HideAndShowObjects();
                }
                ChgThisLines();
                return;
            }

            // We do not want to be interrupted during formatting
            TextFrameLockGuard aLock(this);

            // this is to ensure that the similar code in SwTextFrame::Format_
            // finds the master formatted in case it's needed
            if (IsFollow() && IsFieldFollow())
            {
                SwTextFrame *pMaster = FindMaster();
                assert(pMaster);
                if (!pMaster->HasPara())
                {
                    pMaster->GetFormatted();
                }
                if (!pMaster->HasPara())
                {   // master could be locked because it's being formatted upstack
                    SAL_WARN("sw", "SwTextFrame::Format: failed to format master!");
                }
                else
                {
                    SwTextSizeInfo aInf( pMaster );
                    SwTextIter aMasterLine( pMaster, &aInf );
                    aMasterLine.Bottom();
                    SetOfst(aMasterLine.GetEnd());
                }
            }

            SwTextLineAccess aAccess( this );
            const bool bNew = !aAccess.IsAvailable();
            const bool bSetOfst =
                (GetOfst() && GetOfst() > TextFrameIndex(GetText().getLength()));

            if( CalcPreps() )
                ; // nothing
            // We return if already formatted, but if the TextFrame was just created
            // and does not have any format information
            else if( !bNew && !aAccess.GetPara()->GetReformat().Len() )
            {
                if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
                {
                    aAccess.GetPara()->SetPrepAdjust();
                    aAccess.GetPara()->SetPrep();
                    CalcPreps();
                }
                SetWidow( false );
            }
            else if( bSetOfst && IsFollow() )
            {
                SwTextFrame *pMaster = FindMaster();
                OSL_ENSURE( pMaster, "SwTextFrame::Format: homeless follow" );
                if( pMaster )
                    pMaster->Prepare( PREP_FOLLOW_FOLLOWS );
                SwTwips nMaxY = aRectFnSet.GetPrtBottom(*GetUpper());

                if( aRectFnSet.OverStep( getFrameArea(), nMaxY  ) )
                {
                    aRectFnSet.SetLimit( *this, nMaxY );
                }
                else if( aRectFnSet.BottomDist( getFrameArea(), nMaxY  ) < 0 )
                {
                    SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                    aRectFnSet.AddBottom( aFrm, -aRectFnSet.GetHeight(aFrm) );
                }
            }
            else
            {
                // bSetOfst here means that we have the "red arrow situation"
                if ( bSetOfst )
                    SetOfst_(TextFrameIndex(0));

                const bool bOrphan = IsWidow();
                const SwFootnoteBossFrame* pFootnoteBoss = HasFootnote() ? FindFootnoteBossFrame() : nullptr;
                SwTwips nFootnoteHeight = 0;
                if( pFootnoteBoss )
                {
                    const SwFootnoteContFrame* pCont = pFootnoteBoss->FindFootnoteCont();
                    nFootnoteHeight = pCont ? aRectFnSet.GetHeight(pCont->getFrameArea()) : 0;
                }
                do
                {
                    Format_( pRenderContext, aAccess.GetPara() );
                    if( pFootnoteBoss && nFootnoteHeight )
                    {
                        const SwFootnoteContFrame* pCont = pFootnoteBoss->FindFootnoteCont();
                        SwTwips nNewHeight = pCont ? aRectFnSet.GetHeight(pCont->getFrameArea()) : 0;
                        // If we lost some footnotes, we may have more space
                        // for our main text, so we have to format again ...
                        if( nNewHeight < nFootnoteHeight )
                            nFootnoteHeight = nNewHeight;
                        else
                            break;
                    }
                    else
                        break;
                } while ( pFootnoteBoss );
                if( bOrphan )
                {
                    ValidateFrame();
                    SetWidow( false );
                }
            }
            if( IsEmptyMaster() )
            {
                SwFrame* pPre = GetPrev();
                if( pPre &&
                    // i#10826 It's the first, it cannot keep!
                    pPre->GetIndPrev() &&
                    pPre->GetAttrSet()->GetKeep().GetValue() )
                {
                    pPre->InvalidatePos();
                }
            }
        }

        ChgThisLines();

        // the PrepMustFit should not survive a Format operation
        SwParaPortion *pPara = GetPara();
        if ( pPara )
               pPara->SetPrepMustFit( false );

        CalcBaseOfstForFly();
        CalcHeightOfLastLine(); // i#11860 - Adjust spacing implementation for
                                 // object positioning - Compatibility to MS Word
    }

    // bForceQuickFormat is set if GetFormatted() has been called during the
    // painting process. Actually I cannot imagine a situation which requires
    // a full formatting of the paragraph during painting, on the other hand
    // a full formatting can cause the invalidation of other layout frames,
    // e.g., if there are footnotes in this paragraph, and invalid layout
    // frames will not calculated during the painting. So I actually want to
    // avoid a formatting during painting, but since I'm a coward, I'll only
    // force the quick formatting in the situation of issue i29062.
    bool SwTextFrame::FormatQuick( bool bForceQuickFormat )
    {
        OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
                "SwTextFrame::FormatQuick with swapped frame" );

        if( IsEmpty() && FormatEmpty() )
            return true;

        // We're very picky:
        if( HasPara() || IsWidow() || IsLocked()
            || !isFrameAreaSizeValid() ||
            ( ( IsVertical() ? getFramePrintArea().Width() : getFramePrintArea().Height() ) && IsHiddenNow() ) )
            return false;

        SwTextLineAccess aAccess( this );
        SwParaPortion *pPara = aAccess.GetPara();
        if( !pPara )
            return false;

        SwFrameSwapper aSwapper( this, true );

        TextFrameLockGuard aLock(this);
        SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this, false, true );
        if( 0 != aInf.MaxHyph() )   // Respect MaxHyphen!
            return false;

        SwTextFormatter  aLine( this, &aInf );

        // DropCaps are too complicated ...
        if( aLine.GetDropFormat() )
            return false;

        TextFrameIndex nStart = GetOfst();
        const TextFrameIndex nEnd = GetFollow()
                      ? GetFollow()->GetOfst()
                      : TextFrameIndex(aInf.GetText().getLength());

        int nLoopProtection = 0;
        do
        {
            TextFrameIndex nNewStart = aLine.FormatLine(nStart);
            if (nNewStart == nStart)
                ++nLoopProtection;
            else
                nLoopProtection = 0;
            nStart = nNewStart;
            const bool bWillEndlessInsert = nLoopProtection > 250;
            SAL_WARN_IF(bWillEndlessInsert, "sw", "loop detection triggered");
            if ((!bWillEndlessInsert) // Check for special case: line is invisible,
                                      // like in too thin table cell: tdf#66141
             && (aInf.IsNewLine() || (!aInf.IsStop() && nStart < nEnd)))
                aLine.Insert( new SwLineLayout() );
        } while( aLine.Next() );

        // Last exit: the heights need to match
        Point aTopLeft( getFrameArea().Pos() );
        aTopLeft += getFramePrintArea().Pos();
        const SwTwips nNewHeight = aLine.Y() + aLine.GetLineHeight();
        const SwTwips nOldHeight = aTopLeft.Y() + getFramePrintArea().Height();

        if( !bForceQuickFormat && nNewHeight != nOldHeight && !IsUndersized() )
        {
            // Attention: This situation can occur due to FormatLevel==12. Don't panic!
            TextFrameIndex const nStrt = GetOfst();
            InvalidateRange_( SwCharRange( nStrt, nEnd - nStrt) );
            return false;
        }

        if (m_pFollow && nStart != static_cast<SwTextFrame*>(m_pFollow)->GetOfst())
            return false; // can be caused by e.g. Orphans

        // We made it!

        // Set repaint
        pPara->GetRepaint().Pos( aTopLeft );
        pPara->GetRepaint().SSize( getFramePrintArea().SSize() );

        // Delete reformat
        pPara->GetReformat() = SwCharRange();
        pPara->GetDelta() = 0;

        return true;
    }

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

↑ V654 The condition 'pFootnoteBoss' of loop is always true.