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

#include <memory>
#include <rootfrm.hxx>
#include <pagefrm.hxx>
#include <viewopt.hxx>
#include <frmatr.hxx>
#include <frmtool.hxx>
#include <txtftn.hxx>
#include <fmtftn.hxx>
#include <ndtxt.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/keepitem.hxx>
#include <osl/diagnose.h>
#include <svx/sdtaitm.hxx>

#include <fmtfsize.hxx>
#include <fmtanchr.hxx>
#include <fmtclbl.hxx>

#include <tabfrm.hxx>
#include <ftnfrm.hxx>
#include <txtfrm.hxx>
#include <sectfrm.hxx>
#include <dbg_lay.hxx>

#include <sortedobjs.hxx>
#include <layouter.hxx>
#include <flyfrms.hxx>

#include <DocumentSettingManager.hxx>
#include <IDocumentLayoutAccess.hxx>

// Move methods

/// Return value tells whether the Frame should be moved.
bool SwContentFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool & )
{
    if ( SwFlowFrame::IsMoveBwdJump() || !IsPrevObjMove() )
    {
        // Floating back a frm uses a bit of time unfortunately.
        // The most common case is the following: The Frame wants to float to
        // somewhere where the FixSize is the same that the Frame itself has already.
        // In that case it's pretty easy to check if the Frame has enough space
        // for its VarSize. If this is NOT the case, we already know that
        // we don't need to move.
        // The Frame checks itself whether it has enough space - respecting the fact
        // that it could possibly split itself if needed.
        // If, however, the FixSize differs from the Frame or Flys are involved
        // (either in the old or the new position), checking is pointless,
        // and we have to move the Frame just to see what happens - if there's
        // some space available to do it, that is.

        // The FixSize of the containers of Contents is always the width.

        // If we moved more than one sheet back (for example jumping over empty
        // pages), we have to move either way. Otherwise, if the Frame doesn't fit
        // into the page, empty pages wouldn't be respected anymore.
        sal_uInt8 nMoveAnyway = 0;
        SwPageFrame * const pNewPage = pNewUpper->FindPageFrame();
        SwPageFrame *pOldPage = FindPageFrame();

        if ( SwFlowFrame::IsMoveBwdJump() )
            return true;

        if( IsInFootnote() && IsInSct() )
        {
            SwFootnoteFrame* pFootnote = FindFootnoteFrame();
            SwSectionFrame* pMySect = pFootnote->FindSctFrame();
            if( pMySect && pMySect->IsFootnoteLock() )
            {
                SwSectionFrame *pSect = pNewUpper->FindSctFrame();
                while( pSect && pSect->IsInFootnote() )
                    pSect = pSect->GetUpper()->FindSctFrame();
                OSL_ENSURE( pSect, "Escaping footnote" );
                if( pSect != pMySect )
                    return false;
            }
        }
        SwRectFnSet aRectFnSet(*this);
        SwRectFnSet fnRectX(*pNewUpper);
        if( std::abs( fnRectX.GetWidth(pNewUpper->getFramePrintArea()) -
                 aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) ) > 1 ) {
            // In this case, only a WouldFit_ with test move is possible
            nMoveAnyway = 2;
        }

        // Do *not* move backward, if <nMoveAnyway> equals 3 and no space is left in new upper.
        nMoveAnyway |= BwdMoveNecessary( pOldPage, getFrameArea() );
        {
            const IDocumentSettingAccess& rIDSA = pNewPage->GetFormat()->getIDocumentSettingAccess();
            SwTwips nSpace = 0;
            SwRect aRect( pNewUpper->getFramePrintArea() );
            aRect.Pos() += pNewUpper->getFrameArea().Pos();
            const SwFrame *pPrevFrame = pNewUpper->Lower();
            while ( pPrevFrame )
            {
                SwTwips nNewTop = fnRectX.GetBottom(pPrevFrame->getFrameArea());
                // Consider lower spacing of last frame in a table cell
                {
                    // Check if last frame is inside table and if it includes its lower spacing.
                    if ( !pPrevFrame->GetNext() && pPrevFrame->IsInTab() &&
                         rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) )
                    {
                        const SwFrame* pLastFrame = pPrevFrame;
                        // if last frame is a section, take its last content
                        if ( pPrevFrame->IsSctFrame() )
                        {
                            pLastFrame = static_cast<const SwSectionFrame*>(pPrevFrame)->FindLastContent();
                            if ( pLastFrame &&
                                 pLastFrame->FindTabFrame() != pPrevFrame->FindTabFrame() )
                            {
                                pLastFrame = pLastFrame->FindTabFrame();
                            }
                        }

                        if ( pLastFrame )
                        {
                            SwBorderAttrAccess aAccess( SwFrame::GetCache(), pLastFrame );
                            const SwBorderAttrs& rAttrs = *aAccess.Get();
                            nNewTop -= rAttrs.GetULSpace().GetLower();
                            if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
                            {
                                nNewTop -= rAttrs.CalcLineSpacing();
                            }
                        }
                    }
                }
                fnRectX.SetTop( aRect, nNewTop );

                pPrevFrame = pPrevFrame->GetNext();
            }

            nMoveAnyway |= BwdMoveNecessary( pNewPage, aRect);

            //determine space left in new upper frame
            nSpace = fnRectX.GetHeight(aRect);
            const SwViewShell *pSh = pNewUpper->getRootFrame()->GetCurrShell();
            if ( IsInFootnote() ||
                 (pSh && pSh->GetViewOptions()->getBrowseMode()) ||
                 pNewUpper->IsCellFrame() ||
                 ( pNewUpper->IsInSct() && ( pNewUpper->IsSctFrame() ||
                   ( pNewUpper->IsColBodyFrame() &&
                     !pNewUpper->GetUpper()->GetPrev() &&
                     !pNewUpper->GetUpper()->GetNext() ) ) ) )
                nSpace += pNewUpper->Grow( LONG_MAX, true );

            auto pTextFrame = DynCastTextFrame();
            if (pTextFrame)
            {
                // This is a text frame. Check if it's an anchor for a non-last element in a split
                // fly chain. If so, we can only move back in case not only the text frame itself,
                // but also its fly fits nSpace.
                SwFlyAtContentFrame* pFly = pTextFrame->HasNonLastSplitFlyDrawObj();
                if (pFly && pFly->getFrameArea().Height() > nSpace)
                {
                    return false;
                }
            }

            if ( nMoveAnyway < 3 )
            {
                if (nSpace || IsHiddenNow())
                {
                    // Do not notify footnotes which are stuck to the paragraph:
                    // This would require extremely confusing code, taking into
                    // account the widths
                    // and Flys, that in turn influence the footnotes, ...

                    // WouldFit_ can only be used if the width is the same and
                    // ONLY self-anchored Flys are present.

                    // WouldFit_ can also be used if ONLY Flys anchored
                    // somewhere else are present.
                    // In this case, the width doesn't even matter,
                    // because we're running a TestFormat in the new upper.
                    const sal_uInt8 nBwdMoveNecessaryResult =
                                            BwdMoveNecessary( pNewPage, aRect);
                    const bool bObjsInNewUpper( nBwdMoveNecessaryResult == 2 ||
                                                nBwdMoveNecessaryResult == 3 );

                    return WouldFit_( nSpace, pNewUpper, nMoveAnyway == 2,
                                      bObjsInNewUpper );
                }
                // It's impossible for WouldFit_ to return a usable result if
                // we have a fresh multi-column section - so we really have to
                // float back unless there is no space.
                return pNewUpper->IsInSct() && pNewUpper->IsColBodyFrame() &&
                       !fnRectX.GetWidth(pNewUpper->getFramePrintArea()) &&
                       ( pNewUpper->GetUpper()->GetPrev() ||
                         pNewUpper->GetUpper()->GetNext() );
            }

            // Check for space left in new upper
            return nSpace != 0 || IsHiddenNow();
        }
    }
    return false;
}

// Calc methods

// Two little friendships form a secret society
inline void PrepareLock( SwFlowFrame *pTab )
{
    pTab->LockJoin();
}
inline void PrepareUnlock( SwFlowFrame *pTab )
{
    pTab->UnlockJoin();

}

// hopefully, one day this function simply will return 'false'
static bool lcl_IsCalcUpperAllowed( const SwFrame& rFrame )
{
    return !rFrame.GetUpper()->IsSctFrame() &&
           !rFrame.GetUpper()->IsFooterFrame() &&
           // No format of upper Writer fly frame
           !rFrame.GetUpper()->IsFlyFrame() &&
           !( rFrame.GetUpper()->IsTabFrame() && rFrame.GetUpper()->GetUpper()->IsInTab() ) &&
           !( rFrame.IsTabFrame() && rFrame.GetUpper()->IsInTab() );
}

/** Prepares the Frame for "formatting" (MakeAll()).
 *
 * This method serves to save stack space: To calculate the position of the Frame
 * we have to make sure that the positions of Upper and Prev respectively are
 * valid. This may require a recursive call (a loop would be quite expensive,
 * as it's not required very often).
 *
 * Every call of MakeAll requires around 500 bytes on the stack - you easily
 * see where this leads to. This method requires only a little bit of stack
 * space, so the recursive call should not be a problem here.
 *
 * Another advantage is that one nice day, this method and with it the
 * formatting of predecessors could be avoided. Then it could probably be
 * possible to jump "quickly" to the document's end.
 *
 * @see MakeAll()
 */
void SwFrame::PrepareMake(vcl::RenderContext* pRenderContext)
{
    StackHack aHack;
    if ( GetUpper() )
    {
        SwFrameDeleteGuard aDeleteGuard(this);
        if ( lcl_IsCalcUpperAllowed( *this ) )
            GetUpper()->Calc(pRenderContext);
        OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
        if ( !GetUpper() )
            return;

        const bool bCnt = IsContentFrame();
        const bool bTab = IsTabFrame();
        bool bNoSect = IsInSct();
        bool bOldTabLock = false, bFoll = false;
        SwFlowFrame* pThis = bCnt ? static_cast<SwContentFrame*>(this) : nullptr;

        if ( bTab )
        {
            pThis = static_cast<SwTabFrame*>(this);
            bOldTabLock = static_cast<SwTabFrame*>(this)->IsJoinLocked();
            ::PrepareLock( static_cast<SwTabFrame*>(this) );
            bFoll = pThis->IsFollow();
        }
        else if( IsSctFrame() )
        {
            pThis = static_cast<SwSectionFrame*>(this);
            bFoll = pThis->IsFollow();
            bNoSect = false;
        }
        else if ( bCnt )
        {
            bFoll = pThis->IsFollow();
            if ( bFoll && GetPrev() )
            {
                // Do not follow the chain when we need only one instance
                const SwTextFrame* pMaster = static_cast<SwContentFrame*>(this)->FindMaster();
                if ( pMaster && pMaster->IsLocked() )
                {
                    MakeAll(pRenderContext);
                    return;
                }
            }
        }

        // There is no format of previous frame, if current frame is a table
        // frame and its previous frame wants to keep with it.
        bool bFormatPrev{!bTab};
        if (!bFormatPrev)
        {
            SwFrame const* pPrev{this};
            do
            {
                pPrev = pPrev->GetPrev();
            }
            while (pPrev && pPrev->IsHiddenNow());
            bFormatPrev = pPrev && !pPrev->GetAttrSet()->GetKeep().GetValue();
        }
        if ( bFormatPrev )
        {
            SwFrame *pFrame = GetUpper()->Lower();
            while ( pFrame != this )
            {
                OSL_ENSURE( pFrame, ":-( Layout unstable (this not found)." );
                if ( !pFrame )
                    return; //Oioioioi ...

                if ( !pFrame->isFrameAreaDefinitionValid() )
                {
                    // A small interference that hopefully improves on the stability:
                    // If I'm Follow AND neighbor of a Frame before me, it would delete
                    // me when formatting. This as you can see could easily become a
                    // confusing situation that we want to avoid.
                    if ( bFoll && pFrame->IsFlowFrame() &&
                         SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow( pThis ) )
                        break;

                    bool const isLast(pFrame->GetNext() == this);
                    // note: this seems obvious but does *not* hold, a MakeAll()
                    // could move more than 1 frame backwards!
                    // that's why FindNext() is used below
                    // assert(pFrame->GetUpper() == GetUpper());
                    pFrame->MakeAll(pRenderContext);
                    if( IsSctFrame() && !static_cast<SwSectionFrame*>(this)->GetSection() )
                        break;
                    if (isLast && pFrame->GetUpper() != GetUpper())
                    {
                        assert(GetUpper()->Lower() == this
                            // empty section frames are created all the time...
                            || GetUpper()->Lower()->IsSctFrame()
                            // tab frame/section frame may split multiple times
                            || (   SwFlowFrame::CastFlowFrame(pFrame)
                                && SwFlowFrame::CastFlowFrame(GetUpper()->Lower())
                                && SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow(
                                    SwFlowFrame::CastFlowFrame(GetUpper()->Lower()))
                                && (GetUpper()->Lower()->GetNext() == this
                                    // if it's more than 10 pages long...
                                    || (SwFlowFrame::CastFlowFrame(GetUpper()->Lower())->GetFollow()
                                            == SwFlowFrame::CastFlowFrame(GetUpper()->Lower()->GetNext())
                                        && GetUpper()->Lower()->GetNext()->GetNext() == this)
                                    // pre-existing empty section frames may end up between them...
                                    || GetUpper()->Lower()->GetNext()->IsSctFrame())));
                        break; // tdf#119109 frame was moved backward, prevent
                               // FindNext() returning a frame inside this if
                    }          // this is a table!
                }
                // With ContentFrames, the chain may be broken while walking through
                // it. Therefore we have to figure out the next frame in a bit more
                // complicated way. However, I'll HAVE to get back to myself
                // sometime again.
                pFrame = pFrame->FindNext();

                // If we started out in a SectionFrame, it might have happened that
                // we landed in a Section Follow via the MakeAll calls.
                // FindNext only gives us the SectionFrame, not it's content - we
                // won't find ourselves anymore!
                if( bNoSect && pFrame && pFrame->IsSctFrame() )
                {
                    SwFrame* pCnt = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
                    if( pCnt )
                        pFrame = pCnt;
                }
            }
            OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone II)." );
            if ( !GetUpper() )
                return;

            if ( lcl_IsCalcUpperAllowed( *this ) )
                GetUpper()->Calc(pRenderContext);

            OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone III)." );
        }

        if ( bTab && !bOldTabLock )
            ::PrepareUnlock( static_cast<SwTabFrame*>(this) );
    }
    MakeAll(pRenderContext);
}

void SwFrame::OptPrepareMake()
{
    // #i23129#, #i36347# - no format of upper Writer fly frame
    if ( GetUpper() && !GetUpper()->IsFooterFrame() &&
         !GetUpper()->IsFlyFrame() )
    {
        {
            SwFrameDeleteGuard aDeleteGuard(this);
            GetUpper()->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
        }
        OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
        if ( !GetUpper() )
            return;
    }
    if ( GetPrev() && !GetPrev()->isFrameAreaDefinitionValid() )
    {
        PrepareMake(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
    }
    else
    {
        StackHack aHack;
        MakeAll(IsRootFrame() ? nullptr : getRootFrame()->GetCurrShell()->GetOut());
    }
}

void SwFrame::PrepareCursor()
{
    StackHack aHack;
    if( GetUpper() && !GetUpper()->IsSctFrame() )
    {
        const bool bCnt = IsContentFrame();
        const bool bTab = IsTabFrame();
        bool bNoSect = IsInSct();

        std::optional<FlowFrameJoinLockGuard> tabGuard;
        std::optional<SwFrameDeleteGuard> rowGuard;
        SwFlowFrame* pThis = bCnt ? static_cast<SwContentFrame*>(this) : nullptr;

        if ( bTab )
        {
            tabGuard.emplace(static_cast<SwTabFrame*>(this)); // tdf#125741
            pThis = static_cast<SwTabFrame*>(this);
        }
        else if (IsRowFrame())
        {
            rowGuard.emplace(this); // tdf#125741 keep this alive
        }
        else if( IsSctFrame() )
        {
            pThis = static_cast<SwSectionFrame*>(this);
            bNoSect = false;
        }

        GetUpper()->PrepareCursor();
        GetUpper()->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);

        OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
        if ( !GetUpper() )
            return;

        bool const bFoll = pThis && pThis->IsFollow();

        SwFrame *pFrame = GetUpper()->Lower();
        while ( pFrame != this )
        {
            OSL_ENSURE( pFrame, ":-( Layout unstable (this not found)." );
            if ( !pFrame )
                return; //Oioioioi ...

            if ( !pFrame->isFrameAreaDefinitionValid() )
            {
                // A small interference that hopefully improves on the stability:
                // If I'm Follow AND neighbor of a Frame before me, it would delete
                // me when formatting. This as you can see could easily become a
                // confusing situation that we want to avoid.
                if ( bFoll && pFrame->IsFlowFrame() &&
                     SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow( pThis ) )
                    break;

                bool const isLast(pFrame->GetNext() == this);
                pFrame->MakeAll(getRootFrame()->GetCurrShell()->GetOut());
                if (isLast && pFrame->GetUpper() != GetUpper())
                {
                    assert(GetUpper()->Lower() == this
                        // empty section frames are created all the time...
                        || GetUpper()->Lower()->IsSctFrame()
                        // tab frame/section frame may split multiple times
                        || (   SwFlowFrame::CastFlowFrame(pFrame)
                            && SwFlowFrame::CastFlowFrame(GetUpper()->Lower())
                            && SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow(
                                SwFlowFrame::CastFlowFrame(GetUpper()->Lower()))
                            && (GetUpper()->Lower()->GetNext() == this
                                // if it's more than 10 pages long...
                                || (SwFlowFrame::CastFlowFrame(GetUpper()->Lower())->GetFollow()
                                        == SwFlowFrame::CastFlowFrame(GetUpper()->Lower()->GetNext())
                                    && GetUpper()->Lower()->GetNext()->GetNext() == this)
                                // pre-existing empty section frames may end up between them...
                                || GetUpper()->Lower()->GetNext()->IsSctFrame())));
                    break; // tdf#119109 frame was moved backward, prevent
                           // FindNext() returning a frame inside this if
                }          // this is a table!
            }
            // With ContentFrames, the chain may be broken while walking through
            // it. Therefore we have to figure out the next frame in a bit more
            // complicated way. However, I'll HAVE to get back to myself
            // sometime again.
            pFrame = pFrame->FindNext();
            if( bNoSect && pFrame && pFrame->IsSctFrame() )
            {
                SwFrame* pCnt = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
                if( pCnt )
                    pFrame = pCnt;
            }
        }
        OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone II)." );
        if ( !GetUpper() )
            return;

        GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());

        OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone III)." );
    }
    Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
}

namespace sw {

// Here we return GetPrev(); however we will ignore empty SectionFrames
SwFrame* PrevSkipDead(SwFrame *const pFrame, bool const bSectPrv)
{
    SwFrame* pRet = pFrame->GetPrev();
    if( !pRet && pFrame->GetUpper() && pFrame->GetUpper()->IsSctFrame() &&
        bSectPrv && !pFrame->IsColumnFrame() )
        pRet = pFrame->GetUpper()->GetPrev();
    while( pRet && pRet->IsSctFrame() &&
           !static_cast<SwSectionFrame*>(pRet)->GetSection() )
        pRet = pRet->GetPrev();
    return pRet;
}

} // namespace sw

static SwFrame* lcl_NotHiddenPrev( SwFrame* pFrame )
{
    SwFrame *pRet = pFrame;
    do
    {
        pRet = ::sw::PrevSkipDead(pRet, true);
    } while ( pRet && pRet->IsHiddenNow() );
    return pRet;
}

void SwFrame::MakePos()
{
    if ( isFrameAreaPositionValid() )
        return;

    setFrameAreaPositionValid(true);
    bool bUseUpper = false;
    SwFrame* pPrv = ::sw::PrevSkipDead(this, true);
    if ( pPrv &&
         ( !pPrv->IsContentFrame() ||
           ( static_cast<SwContentFrame*>(pPrv)->GetFollow() != this ) )
       )
    {
        if ( !StackHack::IsLocked() &&
             ( !IsInSct() || IsSctFrame() ) &&
             !pPrv->IsSctFrame() &&
             !pPrv->GetAttrSet()->GetKeep().GetValue()
           )
        {
            // tdf#151866 pPrv may MoveBwd and if this is a newly created
            // section frame then CheckPageDescs() may delete the whole page!
            SwFrameDeleteGuard g(this); // Prevent it.
            pPrv->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);   // This may cause Prev to vanish!
        }
        else if ( pPrv->getFrameArea().Top() == 0 )
        {
            bUseUpper = true;
        }
    }

    pPrv = ::sw::PrevSkipDead(this, false);
    const SwFrameType nMyType = GetType();
    SwRectFnSet aRectFnSet((IsCellFrame() && GetUpper() ? *GetUpper() : *this));
    if ( !bUseUpper && pPrv )
    {
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aFrm.Pos( pPrv->getFrameArea().Pos() );

        if( FRM_NEIGHBOUR & nMyType )
        {
            const bool bR2L = IsRightToLeft();

            if( bR2L )
            {
                aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) - aRectFnSet.GetWidth(aFrm) );
            }
            else
            {
                aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) + aRectFnSet.GetWidth(pPrv->getFrameArea()) );
            }

            // cells may now leave their uppers
            if( aRectFnSet.IsVert() && SwFrameType::Cell & nMyType )
            {
                aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + pPrv->getFrameArea().Width());
            }
        }
        else if( aRectFnSet.IsVert() && FRM_NOTE_VERT & nMyType )
        {
            if ( aRectFnSet.IsVertL2R() )
            {
                aFrm.Pos().setX(aFrm.Pos().getX() + pPrv->getFrameArea().Width());
            }
            else
            {
                aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width());
            }
        }
        else
        {
            aFrm.Pos().setY(aFrm.Pos().getY() + pPrv->getFrameArea().Height());
        }
    }
    else if ( GetUpper() )
    {
        // If parent frame is a footer frame and its <ColLocked()>, then
        // do *not* calculate it.
        // NOTE: Footer frame is <ColLocked()> during its
        //     <FormatSize(..)>, which is called from <Format(..)>, which
        //     is called from <MakeAll()>, which is called from <Calc()>.
        // #i56850#
        // - no format of upper Writer fly frame, which is anchored
        //   at-paragraph or at-character.
        if ( !GetUpper()->IsTabFrame() &&
             !( IsTabFrame() && GetUpper()->IsInTab() ) &&
             !GetUpper()->IsSctFrame() &&
             !dynamic_cast<SwFlyAtContentFrame*>(GetUpper()) &&
             !( GetUpper()->IsFooterFrame() &&
                GetUpper()->IsColLocked() )
           )
        {
            GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());
        }
        pPrv = ::sw::PrevSkipDead(this, false);
        if ( !bUseUpper && pPrv )
        {
            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
            aFrm.Pos( pPrv->getFrameArea().Pos() );

            if( FRM_NEIGHBOUR & nMyType )
            {
                const bool bR2L = IsRightToLeft();

                if( bR2L )
                {
                    aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) - aRectFnSet.GetWidth(aFrm) );
                }
                else
                {
                    aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) + aRectFnSet.GetWidth(pPrv->getFrameArea()) );
                }

                // cells may now leave their uppers
                if( aRectFnSet.IsVert() && SwFrameType::Cell & nMyType )
                {
                    aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + pPrv->getFrameArea().Width());
                }
            }
            else if( aRectFnSet.IsVert() && FRM_NOTE_VERT & nMyType )
            {
                aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width());
            }
            else
            {
                aFrm.Pos().setY(aFrm.Pos().getY() + pPrv->getFrameArea().Height());
            }
        }
        else
        {
            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
            aFrm.Pos( GetUpper()->getFrameArea().Pos() );

            if( GetUpper()->IsFlyFrame() )
            {
                aFrm.Pos() += static_cast<SwFlyFrame*>(GetUpper())->ContentPos();
            }
            else
            {
                aFrm.Pos() += GetUpper()->getFramePrintArea().Pos();
            }

            if( FRM_NEIGHBOUR & nMyType && IsRightToLeft() )
            {
                if( aRectFnSet.IsVert() )
                {
                    aFrm.Pos().setY(aFrm.Pos().getY() + GetUpper()->getFramePrintArea().Height() - aFrm.Height());
                }
                else
                {
                    aFrm.Pos().setX(aFrm.Pos().getX() + GetUpper()->getFramePrintArea().Width() - aFrm.Width());
                }
            }
            else if( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() && FRM_NOTE_VERT & nMyType )
            {
                aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + GetUpper()->getFramePrintArea().Width());
            }
        }
    }
    else
    {
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aFrm.Pos().setX(0);
        aFrm.Pos().setY(0);
    }

    if( IsBodyFrame() && aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() && GetUpper() )
    {
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aFrm.Pos().setX(aFrm.Pos().getX() + GetUpper()->getFramePrintArea().Width() - aFrm.Width());
    }

    setFrameAreaPositionValid(true);
}

// #i28701# - new type <SwSortedObjs>
static void lcl_CheckObjects(SwSortedObjs& rSortedObjs, const SwFrame* pFrame, tools::Long& rBot)
{
    // And then there can be paragraph anchored frames that sit below their paragraph.
    tools::Long nMax = 0;
    for (SwAnchoredObject* pObj : rSortedObjs)
    {
        // #i28701# - consider changed type of <SwSortedObjs>
        // entries.
        tools::Long nTmp = 0;
        if ( auto pFly = pObj->DynCastFlyFrame() )
        {
            if( pFly->getFrameArea().Top() != FAR_AWAY &&
                ( pFrame->IsPageFrame() ? pFly->IsFlyLayFrame() :
                  ( pFly->IsFlyAtContentFrame() &&
                    ( pFrame->IsBodyFrame() ? pFly->GetAnchorFrame()->IsInDocBody() :
                                          pFly->GetAnchorFrame()->IsInFootnote() ) ) ) )
            {
                nTmp = pFly->getFrameArea().Bottom();
            }
        }
        else
            nTmp = pObj->GetObjRect().Bottom();
        nMax = std::max( nTmp, nMax );
    }
    ++nMax; // Lower edge vs. height!
    rBot = std::max( rBot, nMax );
}

size_t SwPageFrame::GetContentHeight(const tools::Long nTop, const tools::Long nBottom) const
{
    OSL_ENSURE(!(FindBodyCont() && FindBodyCont()->Lower() && FindBodyCont()->Lower()->IsColumnFrame()),
               "SwPageFrame::GetContentHeight(): No support for columns.");

    // In pages without columns, the content defines the size.
    tools::Long nBot = getFrameArea().Top() + nTop;
    const SwFrame *pFrame = Lower();
    while (pFrame)
    {
        tools::Long nTmp = 0;
        const SwFrame *pCnt = static_cast<const SwLayoutFrame*>(pFrame)->ContainsAny();
        while (pCnt && (pCnt->GetUpper() == pFrame ||
               static_cast<const SwLayoutFrame*>(pFrame)->IsAnLower(pCnt)))
        {
            nTmp += pCnt->getFrameArea().Height();
            if (pCnt->IsTextFrame() &&
                static_cast<const SwTextFrame*>(pCnt)->IsUndersized())
            {
                // This TextFrame would like to be a bit bigger.
                nTmp += static_cast<const SwTextFrame*>(pCnt)->GetParHeight()
                      - pCnt->getFramePrintArea().Height();
            }
            else if (pCnt->IsSctFrame())
            {
                // Grow if undersized, but don't shrink if oversized.
                const auto delta = static_cast<const SwSectionFrame*>(pCnt)->CalcUndersize();
                if (delta > 0)
                    nTmp += delta;
            }

            pCnt = pCnt->FindNext();
        }
        // Consider invalid body frame properties
        if (pFrame->IsBodyFrame() &&
            (!pFrame->isFrameAreaSizeValid() ||
            !pFrame->isFramePrintAreaValid()) &&
            (pFrame->getFrameArea().Height() < pFrame->getFramePrintArea().Height())
            )
        {
            nTmp = std::min(nTmp, pFrame->getFrameArea().Height());
        }
        else
        {
            // Assert invalid lower property
            OSL_ENSURE(!(pFrame->getFrameArea().Height() < pFrame->getFramePrintArea().Height()),
                "SwPageFrame::GetContentHeight(): Lower with frame height < printing height");
            nTmp += pFrame->getFrameArea().Height() - pFrame->getFramePrintArea().Height();
        }
        if (!pFrame->IsBodyFrame())
            nTmp = std::min(nTmp, pFrame->getFrameArea().Height());
        nBot += nTmp;
        // Here we check whether paragraph anchored objects
        // protrude outside the Body/FootnoteCont.
        if (m_pSortedObjs && !pFrame->IsHeaderFrame() &&
            !pFrame->IsFooterFrame())
            lcl_CheckObjects(*m_pSortedObjs, pFrame, nBot);
        pFrame = pFrame->GetNext();
    }
    nBot += nBottom;
    // And the page anchored ones
    if (m_pSortedObjs)
        lcl_CheckObjects(*m_pSortedObjs, this, nBot);
    nBot -= getFrameArea().Top();

    return nBot;
}

void SwPageFrame::MakeAll(vcl::RenderContext* pRenderContext)
{
    PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )

    const SwRect aOldRect( getFrameArea() );     // Adjust root size
    const SwLayNotify aNotify( this );  // takes care of the notification in the dtor
    std::optional<SwBorderAttrAccess> oAccess;
    const SwBorderAttrs*pAttrs = nullptr;

    while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
    {
        if ( !isFrameAreaPositionValid() )
        {
            setFrameAreaPositionValid(true); // positioning of the pages is taken care of by the root frame
        }

        if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
        {
            if ( IsEmptyPage() )
            {
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                aFrm.Width( 0 );
                aFrm.Height( 0 );

                SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                aPrt.Width( 0 );
                aPrt.Height( 0 );
                aPrt.Left( 0 );
                aPrt.Top( 0 );

                setFrameAreaSizeValid(true);
                setFramePrintAreaValid(true);
            }
            else
            {
                if (!oAccess)
                {
                    oAccess.emplace(SwFrame::GetCache(), this);
                    pAttrs = oAccess->Get();
                }
                assert(pAttrs);

                SwRootFrame* pRootFrame = getRootFrame();
                SwViewShell* pSh = pRootFrame->GetCurrShell();
                if (pSh && pSh->GetViewOptions()->getBrowseMode())
                {
                    // In BrowseView, we use fixed settings
                    const Size aBorder = pRenderContext->PixelToLogic( pSh->GetBrowseBorder() );
                    const tools::Long nTop    = pAttrs->CalcTopLine()   + aBorder.Height();
                    const tools::Long nBottom = pAttrs->CalcBottomLine()+ aBorder.Height();

                    tools::Long nWidth = GetUpper() ? static_cast<SwRootFrame*>(GetUpper())->GetBrowseWidth() : 0;
                    const auto nDefWidth = pSh->GetBrowseWidth();
                    if (nWidth < nDefWidth)
                        nWidth = nDefWidth;
                    nWidth += + 2 * aBorder.Width();

                    constexpr tools::Long constTwips_2cm = o3tl::toTwips(2, o3tl::Length::cm);
                    nWidth = std::max(nWidth, 2L * aBorder.Width() + constTwips_2cm);

                    {
                        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                        aFrm.Width( nWidth );

                        SwLayoutFrame *pBody = FindBodyCont();
                        if ( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
                        {
                            // Columns have a fixed height
                            aFrm.Height( pAttrs->GetSize().Height() );
                        }
                        else
                        {
                            // In pages without columns, the content defines the size.
                            tools::Long nBot = GetContentHeight(nTop, nBottom);

                            // #i35143# - If second page frame
                            // exists, the first page doesn't have to fulfill the
                            // visible area.
                            if ( !GetPrev() && !GetNext() )
                            {
                                nBot = std::max( nBot, pSh->VisArea().Height() );
                            }
                            // #i35143# - Assure, that the page
                            // doesn't exceed the defined browse height.
                            aFrm.Height( std::min( nBot, BROWSE_HEIGHT ) );
                        }
                    }

                    {
                        SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                        aPrt.Left ( pAttrs->CalcLeftLine() + aBorder.Width() );
                        aPrt.Top  ( nTop );
                        aPrt.Width( getFrameArea().Width() - ( aPrt.Left() + pAttrs->CalcRightLine() + aBorder.Width() ) );
                        aPrt.Height( getFrameArea().Height() - (nTop + nBottom) );
                    }

                    setFrameAreaSizeValid(true);
                    setFramePrintAreaValid(true);
                    continue;
                }
                else if (pSh && pSh->GetViewOptions()->IsWhitespaceHidden())
                {
                    tools::Long height = 0;
                    SwLayoutFrame *pBody = FindBodyCont();
                    SwTwips nFullBodyHeight = pAttrs->GetSize().Height() - pAttrs->CalcTop() - pAttrs->CalcBottom();
                    if (pRootFrame->GetLastPage() == this)
                    {
                        // Last page is only reduced by the top/bottom margin, the body frame height
                        // is not reduced.
                        height = nFullBodyHeight;
                    }
                    else if ( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
                    {
                        // Columns have a fixed height
                        height = pAttrs->GetSize().Height();
                    }
                    else
                    {
                        // No need for borders.
                        height = GetContentHeight(0, 0);
                        if (height > nFullBodyHeight)
                        {
                            // Content height would be larger than the show-whitespace body height,
                            // limit it.
                            height = nFullBodyHeight;
                        }
                    }

                    if (height > 0)
                    {
                        ChgSize(Size(getFrameArea().Width(), height));
                        SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                        aPrt.Top(0);
                        aPrt.Height(height);
                        setFrameAreaSizeValid(true);
                        setFramePrintAreaValid(true);
                        continue;
                    }

                    // Fallback to default formatting. Especially relevant
                    // when loading a doc when Hide Whitespace is enabled.
                    // Heights are zero initially.
                }

                // Set FixSize. For pages, this is not done from Upper, but from
                // the attribute.
                //FIXME: This resets the size when (isFrameAreaSizeValid() && !isFramePrintAreaValid()).
                {
                    SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                    aFrm.SSize( pAttrs->GetSize() );
                }
                Format( pRenderContext, pAttrs );
            }
        }
    } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )

    if ( getFrameArea() != aOldRect && GetUpper() )
        static_cast<SwRootFrame*>(GetUpper())->CheckViewLayout( nullptr, nullptr );

    OSL_ENSURE( !GetUpper() || GetUpper()->getFramePrintArea().Width() >= getFrameArea().Width(),
        "Upper (Root) must be wide enough to contain the widest page");
}

void SwLayoutFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
{
    PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )

    // takes care of the notification in the dtor
    const SwLayNotify aNotify( this );
    bool bVert = IsVertical();

    SwRectFnSet fnRect(IsNeighbourFrame() != bVert, IsVertLR(), IsVertLRBT());

    std::optional<SwBorderAttrAccess> oAccess;
    const SwBorderAttrs*pAttrs = nullptr;

    while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
    {
        if ( !isFrameAreaPositionValid() )
        {
            MakePos();
        }

        if (IsHiddenNow())
        {
            MakeValidZeroHeight();
        }

        if ( GetUpper() )
        {
            // NEW TABLES
            if ( IsLeaveUpperAllowed() )
            {
                if ( !isFrameAreaSizeValid() )
                {
                    setFramePrintAreaValid(false);
                }
            }
            else
            {
                if ( !isFrameAreaSizeValid() )
                {
                    // Set FixSize; VarSize is set by Format() after calculating the PrtArea
                    setFramePrintAreaValid(false);

                    SwTwips nPrtWidth = fnRect.GetWidth(GetUpper()->getFramePrintArea());
                    if( bVert && ( IsBodyFrame() || IsFootnoteContFrame() ) )
                    {
                        SwFrame* pNxt = GetPrev();
                        while( pNxt && !pNxt->IsHeaderFrame() )
                            pNxt = pNxt->GetPrev();
                        if( pNxt )
                            nPrtWidth -= pNxt->getFrameArea().Height();
                        pNxt = GetNext();
                        while( pNxt && !pNxt->IsFooterFrame() )
                            pNxt = pNxt->GetNext();
                        if( pNxt )
                            nPrtWidth -= pNxt->getFrameArea().Height();
                    }

                    const tools::Long nDiff = nPrtWidth - fnRect.GetWidth(getFrameArea());
                    SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                    // SwRectFn switched between horizontal and vertical when bVert == IsNeighbourFrame().
                    // We pick fnSubLeft or fnAddRight that is correspondent to SwRectFn->fnAddBottom
                    if( ( IsCellFrame() && IsRightToLeft() ) || ( IsColumnFrame() && bVert && !IsVertLR() ) )
                    {
                        fnRect.SubLeft(aFrm, nDiff);
                    }
                    else
                    {
                        fnRect.AddRight(aFrm, nDiff);
                    }
                }
                else
                {
                    // Don't leave your upper
                    const SwTwips nDeadLine = fnRect.GetPrtBottom(*GetUpper());
                    if (fnRect.OverStep(getFrameArea(), nDeadLine))
                    {
                        setFrameAreaSizeValid(false);
                    }
                }
            }
        }

        if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
        {
            if ( !oAccess )
            {
                oAccess.emplace(SwFrame::GetCache(), this);
                pAttrs  = oAccess->Get();
            }
            Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
        }
    } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
}

bool SwTextNode::IsCollapse() const
{
    if (GetDoc().GetDocumentSettingManager().get( DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA )
        &&  GetText().isEmpty())
    {
        SwNodeOffset nIdx=GetIndex();
        const SwEndNode *pNdBefore=GetNodes()[nIdx-1]->GetEndNode();
        const SwEndNode *pNdAfter=GetNodes()[nIdx+1]->GetEndNode();

        // The paragraph is collapsed only if the NdAfter is the end of a cell
        bool bInTable = FindTableNode( ) != nullptr;

        SwSortedObjs* pObjs = getLayoutFrame( GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() )->GetDrawObjs( );
        const size_t nObjs = ( pObjs != nullptr ) ? pObjs->size( ) : 0;

        return pNdBefore!=nullptr && pNdAfter!=nullptr && nObjs == 0 && bInTable;
    }

    return false;
}

bool SwFrame::IsCollapse() const
{
    if (!IsTextFrame())
        return false;

    const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>(this);
    const SwTextNode *pTextNode = pTextFrame->GetTextNodeForParaProps();
    // TODO this SwTextNode function is pointless and should be merged in here
    return pTextFrame->GetText().isEmpty() && pTextNode && pTextNode->IsCollapse();
}

bool SwFrame::IsCollapseUpper() const
{
    const SwTextFrame* pTextFrame = DynCastTextFrame();
    if (!pTextFrame)
    {
        return false;
    }

    const SwDoc& rDoc = pTextFrame->GetDoc();
    const IDocumentSettingAccess& rIDSA = rDoc.getIDocumentSettingAccess();
    if (!rIDSA.get(DocumentSettingId::TAB_OVER_SPACING) || rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN))
    {
        // Writer or Word Word <= 2010 style: upper margin is never ignored.
        return false;
    }

    if (IsInFly())
    {
        // Not in a page's body.
        return false;
    }

    // Word >= 2013 style: when we're at the top of the page's body, but not on the first page, then
    // ignore the upper margin for paragraphs.
    if (GetPrev() || !GetUpper() || !GetUpper()->IsBodyFrame())
    {
        return false;
    }

    const SwPageFrame* pPageFrame = FindPageFrame();
    if (!pPageFrame || !pPageFrame->GetPrev())
    {
        return false;
    }

    // Avoid the ignore after applying a new page style (but do it after page breaks).
    const SwTextNode* pTextNode = pTextFrame->GetTextNodeForParaProps();
    if (pTextNode)
    {
        if (pTextNode->HasSwAttrSet() && pTextNode->GetSwAttrSet().HasItem(RES_PAGEDESC))
        {
            return false;
        }
    }

    return true;
}

void SwContentFrame::MakePrtArea( const SwBorderAttrs &rAttrs )
{
    if ( isFramePrintAreaValid() )
        return;

    setFramePrintAreaValid(true);
    SwRectFnSet aRectFnSet(*this);
    SwTwips nUpper = 0;
    if (IsTextFrame() && IsHiddenNow())
    {
        if ( static_cast<SwTextFrame*>(this)->HasFollow() )
            static_cast<SwTextFrame*>(this)->JoinFrame();

        if( aRectFnSet.GetHeight(getFramePrintArea()) )
        {
            static_cast<SwTextFrame*>(this)->HideHidden();
        }

        {
            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
            aPrt.Pos().setX(0);
            aPrt.Pos().setY(0);
            aRectFnSet.SetWidth( aPrt, aRectFnSet.GetWidth(getFrameArea()) );
            aRectFnSet.SetHeight( aPrt, 0 );
        }

        nUpper = -( aRectFnSet.GetHeight(getFrameArea()) );
    }
    else
    {
        // Simplification: ContentFrames are always variable in height!

        // At the FixSize, the surrounding Frame enforces the size;
        // the borders are simply subtracted.
        const tools::Long nLeft = rAttrs.CalcLeft( this );
        const tools::Long nRight = rAttrs.CalcRight( this );
        aRectFnSet.SetXMargins( *this, nLeft, nRight );

        SwViewShell *pSh = getRootFrame()->GetCurrShell();
        SwTwips nWidthArea;
        if( pSh && 0!=(nWidthArea=aRectFnSet.GetWidth(pSh->VisArea())) &&
            GetUpper()->IsPageBodyFrame() && // but not for BodyFrames in Columns
            pSh->GetViewOptions()->getBrowseMode() )
        {
            // Do not protrude the edge of the visible area. The page may be
            // wider, because there may be objects with excess width
            // (RootFrame::ImplCalcBrowseWidth())
            tools::Long nMinWidth = 0;

            for (size_t i = 0; GetDrawObjs() && i < GetDrawObjs()->size(); ++i)
            {
                // #i28701# - consider changed type of
                // <SwSortedObjs> entries
                SwAnchoredObject* pObj = (*GetDrawObjs())[i];
                const SwFrameFormat* pFormat = pObj->GetFrameFormat();
                const bool bFly = pObj->DynCastFlyFrame() !=  nullptr;
                if ((bFly && (FAR_AWAY == pObj->GetObjRect().Width()))
                    || pFormat->GetFrameSize().GetWidthPercent())
                {
                    continue;
                }

                if ( RndStdIds::FLY_AS_CHAR == pFormat->GetAnchor().GetAnchorId() )
                {
                    nMinWidth = std::max( nMinWidth,
                                     bFly ? pFormat->GetFrameSize().GetWidth()
                                          : pObj->GetObjRect().Width() );
                }
            }

            const Size aBorder = pSh->GetOut()->PixelToLogic( pSh->GetBrowseBorder() );
            tools::Long nWidth = nWidthArea - 2 * ( IsVertical() ? aBorder.Height() : aBorder.Width() );
            nWidth -= aRectFnSet.GetLeft(getFramePrintArea());
            nWidth -= rAttrs.CalcRightLine();
            nWidth = std::max( nMinWidth, nWidth );

            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
            aRectFnSet.SetWidth( aPrt, std::min( nWidth, aRectFnSet.GetWidth(aPrt) ) );
        }

        if ( aRectFnSet.GetWidth(getFramePrintArea()) <= MINLAY )
        {
            // The PrtArea should already be at least MINLAY wide, matching the
            // minimal values of the UI
            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
            aRectFnSet.SetWidth( aPrt, std::min( tools::Long(MINLAY), aRectFnSet.GetWidth(getFrameArea()) ) );
            SwTwips nTmp = aRectFnSet.GetWidth(getFrameArea()) - aRectFnSet.GetWidth(aPrt);

            if( aRectFnSet.GetLeft(aPrt) > nTmp )
            {
                aRectFnSet.SetLeft( aPrt, nTmp );
            }
        }

        // The following rules apply for VarSize:
        // 1. The first entry of a chain has no top border
        // 2. There is never a bottom border
        // 3. The top border is the maximum of the distance
        //    of Prev downwards and our own distance upwards
        // Those three rules apply when calculating spacings
        // that are given by UL- and LRSpace. There might be a spacing
        // in all directions however; this may be caused by borders
        // and / or shadows.
        // 4. The spacing for TextFrames corresponds to the interline lead,
        //    at a minimum.

        nUpper = CalcUpperSpace( &rAttrs );

        SwTwips nLower = CalcLowerSpace( &rAttrs );
        if (IsCollapse()) {
            nUpper=0;
            nLower=0;
        }

        {
            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
            aRectFnSet.SetPosY( aPrt, !aRectFnSet.IsVert() ? nUpper : nLower);
        }

        nUpper += nLower;
        nUpper -= aRectFnSet.GetHeight(getFrameArea()) - aRectFnSet.GetHeight(getFramePrintArea());
    }
    // If there's a difference between old and new size, call Grow() or
    // Shrink() respectively.
    if ( nUpper )
    {
        if ( nUpper > 0 )
            GrowFrame( nUpper );
        else
            ShrinkFrame( -nUpper );
    }
}

#define STOP_FLY_FORMAT 10
// - loop prevention
const int cnStopFormat = 15;

inline void ValidateSz( SwFrame *pFrame )
{
    if ( pFrame )
    {
        pFrame->setFrameAreaSizeValid(true);
        pFrame->setFramePrintAreaValid(true);
    }
}

void SwContentFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
{
    OSL_ENSURE( GetUpper(), "no Upper?" );
    OSL_ENSURE( IsTextFrame(), "MakeAll(), NoText" );

    if ( !IsFollow() && StackHack::IsLocked() )
        return;

    if ( IsJoinLocked() )
        return;

    OSL_ENSURE( !static_cast<SwTextFrame*>(this)->IsSwapped(), "Calculation of a swapped frame" );

    StackHack aHack;

    if ( static_cast<SwTextFrame*>(this)->IsLocked() )
    {
        OSL_FAIL( "Format for locked TextFrame." );
        return;
    }

    bool const isHiddenNow(static_cast<SwTextFrame*>(this)->IsHiddenNowImpl());
    if (isHiddenNow)
    {
        while (HasFollow())
        {
            static_cast<SwTextFrame&>(*this).JoinFrame();
        }
        HideAndShowObjects();
    }

    std::optional<SwFrameDeleteGuard> oDeleteGuard(std::in_place, this);
    LockJoin();
    tools::Long nFormatCount = 0;
    // - loop prevention
    int nConsecutiveFormatsWithoutChange = 0;
    PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )

    // takes care of the notification in the dtor
    std::optional<SwContentNotify> oNotify( std::in_place, this );

    // as long as bMakePage is true, a new page can be created (exactly once)
    bool bMakePage = true;
    // bMovedBwd gets set to true when the frame flows backwards
    bool bMovedBwd = false;
    // as long as bMovedFwd is false, the Frame may flow backwards (until
    // it has been moved forward once)
    bool bMovedFwd = false;
    sal_Bool bFormatted = false;        // For the widow/orphan rules, we encourage the
                                            // last ContentFrame of a chain to format. This only
                                            // needs to happen once. Every time the Frame is
                                            // moved, the flag will have to be reset.
    bool bMustFit = false;                  // Once the emergency brake is pulled,
                                            // no other prepares will be triggered
    bool bFitPromise = false;               // If a paragraph didn't fit, but promises
                                            // with WouldFit that it would adjust accordingly,
                                            // this flag is set. If it turns out that it
                                            // didn't keep it's promise, we can act in a
                                            // controlled fashion.
    const bool bFly = IsInFly();
    const bool bTab = IsInTab();
    const bool bFootnote = IsInFootnote();
    const bool bSct = IsInSct();
    Point aOldFramePos;               // This is so we can compare with the last pos
    Point aOldPrtPos;               // and determine whether it makes sense to Prepare

    SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
    const SwBorderAttrs &rAttrs = *aAccess.Get();

    if ( !IsFollow() && rAttrs.JoinedWithPrev( *(this) ) )
    {
        oNotify->SetBordersJoinedWithPrev();
    }

    bool bKeep{!isHiddenNow && IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem())};
    if (bKeep)
    {
        // The SwTextFrameBreak ctor already turns off bKeep for split fly anchors, don't
        // change that decision here.
        bKeep = IgnoringSplitFlyAnchor(bKeep);
    }

    std::unique_ptr<SwSaveFootnoteHeight> pSaveFootnote;
    if ( bFootnote )
    {
        SwFootnoteFrame *pFootnote = FindFootnoteFrame();
        SwSectionFrame* pSct = pFootnote->FindSctFrame();
        if ( !static_cast<SwTextFrame*>(pFootnote->GetRef())->IsLocked() )
        {
            SwFootnoteBossFrame* pBoss = pFootnote->GetRef()->FindFootnoteBossFrame(
                                    pFootnote->GetAttr()->GetFootnote().IsEndNote() );
            if( !pSct || pSct->IsColLocked() || !pSct->Growable() )
                pSaveFootnote.reset( new SwSaveFootnoteHeight( pBoss,
                    static_cast<SwTextFrame*>(pFootnote->GetRef())->GetFootnoteLine( pFootnote->GetAttr() ) ) );
        }
    }

    if ( GetUpper()->IsSctFrame() &&
         HasFollow() && !GetFollow()->IsDeleteForbidden() &&
         &GetFollow()->GetFrame() == GetNext() )
    {
        static_cast<SwTextFrame&>(*this).JoinFrame();
    }

    // #i28701# - move master forward, if it has to move,
    // because of its object positioning.
    if (!IsFollow())
    {
        sal_uInt32 nToPageNum = 0;
        const bool bMoveFwdByObjPos = SwLayouter::FrameMovedFwdByObjPos(
                                                    GetAttrSet()->GetDoc(),
                                                    *static_cast<SwTextFrame*>(this),
                                                    nToPageNum );
        // #i58182#
        // Also move a paragraph forward, which is the first one inside a table cell.
        if ( bMoveFwdByObjPos &&
             FindPageFrame()->GetPhyPageNum() < nToPageNum &&
             ( ::sw::PrevSkipDead(this, true) ||
               GetUpper()->IsCellFrame() ||
               ( GetUpper()->IsSctFrame() &&
                 GetUpper()->GetUpper()->IsCellFrame() ) ) &&
             IsMoveable() )
        {
            bMovedFwd = true;
            MoveFwd( bMakePage, false );
        }
    }
    else if (auto *const prev{::sw::PrevSkipDead(this, true)}; prev && IsMoveable())
    {
        // If a Follow sits next to its Master and doesn't fit, we know it can be moved right now.
        bMovedFwd = true;
        // If follow frame is in table, its master will be the last in the
        // current table cell. Thus, invalidate the printing area of the master.
        if ( IsInTab() )
        {
            prev->InvalidatePrt();
        }
        MoveFwd( bMakePage, false );
    }

    // Check footnote content for forward move.
    // If a content of a footnote is on a prior page/column as its invalid
    // reference, it can be moved forward.
    if ( bFootnote && !isFrameAreaPositionValid() )
    {
        SwFootnoteFrame* pFootnote = FindFootnoteFrame();
        SwContentFrame* pRefCnt = pFootnote ? pFootnote->GetRef() : nullptr;

        if ( pRefCnt && !pRefCnt->isFrameAreaDefinitionValid() )
        {
            SwFootnoteBossFrame* pFootnoteBossOfFootnote = pFootnote->FindFootnoteBossFrame();
            SwFootnoteBossFrame* pFootnoteBossOfRef = pRefCnt->FindFootnoteBossFrame();
            //<loop of movefwd until condition held or no move>
            if ( pFootnoteBossOfFootnote && pFootnoteBossOfRef &&
                 pFootnoteBossOfFootnote != pFootnoteBossOfRef &&
                 pFootnoteBossOfFootnote->IsBefore( pFootnoteBossOfRef ) )
            {
                bMovedFwd = true;
                MoveFwd( bMakePage, false );
            }
        }
    }

    SwRectFnSet aRectFnSet(*this);

    SwFrame const* pMoveBwdPre(nullptr);
    bool isMoveBwdPreValid(false);

    SwRect aOldFrame_StopFormat, aOldFrame_StopFormat2;
    SwRect aOldPrt_StopFormat, aOldPrt_StopFormat2;

    while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
    {
        // - loop prevention
        aOldFrame_StopFormat2 = aOldFrame_StopFormat;
        aOldPrt_StopFormat2 = aOldPrt_StopFormat;
        aOldFrame_StopFormat = getFrameArea();
        aOldPrt_StopFormat = getFramePrintArea();

        bool bMoveable = IsMoveable();
        if (bMoveable)
        {
            SwFrame *pPre = GetIndPrev();
            if ( CheckMoveFwd( bMakePage, bKeep, bMovedBwd ) )
            {
                aRectFnSet.Refresh(*this);
                bMovedFwd = true;
                if ( bMovedBwd )
                {
                    // While flowing back, the Upper was encouraged to
                    // completely re-paint itself. We can skip this now after
                    // flowing back and forth.
                    GetUpper()->ResetCompletePaint();
                    // The predecessor was invalidated, so this is obsolete as well now.
                    assert(pPre);
                    if ((pPre == pMoveBwdPre && isMoveBwdPreValid) && !pPre->IsSctFrame())
                        ::ValidateSz( pPre );
                }
                bMoveable = IsMoveable();
            }
        }

        aOldFramePos = aRectFnSet.GetPos(getFrameArea());
        aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());

        if ( !isFrameAreaPositionValid() )
        {
            MakePos();
        }

        if (isHiddenNow)
        {   // call this after MakePos() otherwise Shrink may not work
            MakeValidZeroHeight();
        }

        //Set FixSize. VarSize is being adjusted by Format().
        if ( !isFrameAreaSizeValid() )
        {
            assert(!isHiddenNow); // hidden frame must not be formatted
            // invalidate printing area flag, if the following conditions are hold:
            // - current frame width is 0.
            // - current printing area width is 0.
            // - frame width is adjusted to a value greater than 0.
            // - printing area flag is true.
            // Thus, it's assured that the printing area is adjusted, if the
            // frame area width changes its width from 0 to something greater
            // than 0.
            // Note: A text frame can be in such a situation, if the format is
            //       triggered by method call <SwCursorShell::SetCursor()> after
            //       loading the document.
            const SwTwips nNewFrameWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());

            if ( isFramePrintAreaValid() &&
                nNewFrameWidth > 0 &&
                aRectFnSet.GetWidth(getFrameArea()) == 0 &&
                aRectFnSet.GetWidth(getFramePrintArea()) == 0 )
            {
                setFramePrintAreaValid(false);
            }

            {
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                aRectFnSet.SetWidth( aFrm, nNewFrameWidth );
            }

            // When a lower of a vertically aligned fly frame changes its size we need to recalculate content pos.
            if( GetUpper() && GetUpper()->IsFlyFrame() &&
                GetUpper()->GetFormat()->GetTextVertAdjust().GetValue() != SDRTEXTVERTADJUST_TOP )
            {
                static_cast<SwFlyFrame*>(GetUpper())->InvalidateContentPos();
                GetUpper()->SetCompletePaint();
            }
        }
        if ( !isFramePrintAreaValid() )
        {
            assert(!isHiddenNow); // hidden frame must not be formatted
            const tools::Long nOldW = aRectFnSet.GetWidth(getFramePrintArea());
            // #i34730# - keep current frame height
            const SwTwips nOldH = aRectFnSet.GetHeight(getFrameArea());
            MakePrtArea( rAttrs );
            if ( nOldW != aRectFnSet.GetWidth(getFramePrintArea()) )
                Prepare( PrepareHint::FixSizeChanged );
            // #i34730# - check, if frame height has changed.
            // If yes, send a PrepareHint::AdjustSizeWithoutFormatting and invalidate the size flag to
            // force a format. The format will check in its method
            // <SwTextFrame::CalcPreps()>, if the already formatted lines still
            // fit and if not, performs necessary actions.
            // #i40150# - no check, if frame is undersized.
            if ( isFrameAreaSizeValid() && !IsUndersized() && nOldH != aRectFnSet.GetHeight(getFrameArea()) )
            {
                // #115759# - no PrepareHint::AdjustSizeWithoutFormatting and size
                // invalidation, if height decreases only by the additional
                // lower space as last content of a table cell and an existing
                // follow containing one line exists.
                const SwTwips nHDiff = nOldH - aRectFnSet.GetHeight(getFrameArea());
                const bool bNoPrepAdjustFrame =
                    nHDiff > 0 && IsInTab() && GetFollow() &&
                    (1 == static_cast<SwTextFrame*>(GetFollow())->GetLineCount(TextFrameIndex(COMPLETE_STRING))
                     || aRectFnSet.GetWidth(static_cast<SwTextFrame*>(GetFollow())->getFrameArea()) < 0) &&
                    GetFollow()->CalcAddLowerSpaceAsLastInTableCell() == nHDiff;
                if ( !bNoPrepAdjustFrame )
                {
                    Prepare( PrepareHint::AdjustSizeWithoutFormatting );
                    setFrameAreaSizeValid(false);
                }
            }
        }

        // To make the widow and orphan rules work, we need to notify the ContentFrame.
        // Criteria:
        // - It needs to be movable (otherwise, splitting doesn't make sense)
        // - It needs to overlap with the lower edge of the PrtArea of the Upper
        if (!bMustFit && !isHiddenNow)
        {
            bool bWidow = true;
            const SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
            if( bMoveable && !bFormatted &&
                ( GetFollow() || aRectFnSet.OverStep( getFrameArea(), nDeadLine ) ) )
            {
                Prepare( PrepareHint::WidowsOrphans, nullptr, false );
                setFrameAreaSizeValid(false);
                bWidow = false;
            }
            if( aRectFnSet.GetPos(getFrameArea()) != aOldFramePos ||
                aRectFnSet.GetPos(getFramePrintArea()) != aOldPrtPos )
            {
                // In this Prepare, an InvalidateSize_() might happen.
                // isFrameAreaSizeValid() becomes false and Format() gets called.
                Prepare( PrepareHint::FramePositionChanged, static_cast<const void*>(&bFormatted), false );
                if ( bWidow && GetFollow() )
                {
                    Prepare( PrepareHint::WidowsOrphans, nullptr, false );
                    setFrameAreaSizeValid(false);
                }
            }
        }
        if ( !isFrameAreaSizeValid() )
        {
            assert(!isHiddenNow); // hidden frame must not be formatted
            setFrameAreaSizeValid(true);
            bFormatted = true;
            ++nFormatCount;
            if( nFormatCount > STOP_FLY_FORMAT )
                SetFlyLock( true );
            // - loop prevention
            // No format any longer, if <cnStopFormat> consecutive formats
            // without change occur.
            if ( nConsecutiveFormatsWithoutChange <= cnStopFormat )
            {
                Format(getRootFrame()->GetCurrShell()->GetOut());
            }
#if OSL_DEBUG_LEVEL > 0
            else
            {
                OSL_FAIL( "debug assertion: <SwContentFrame::MakeAll()> - format of text frame suppressed by fix b6448963" );
            }
#endif
        }

        // If this is the first one in a chain, check if this can flow
        // backwards (if this is movable at all).
        // To prevent oscillations/loops, check that this has not just
        // flowed forwards.
        bool bDummy;
        auto const pTemp(GetIndPrev());
        auto const bTemp(pTemp && pTemp->isFrameAreaSizeValid()
                               && pTemp->isFramePrintAreaValid());
        if ( !::sw::PrevSkipDead(this, true) &&
             !bMovedFwd &&
             ( bMoveable || ( bFly && !bTab ) ) &&
             ( !bFootnote || !GetUpper()->FindFootnoteFrame()->GetPrev() )
             && MoveBwd( bDummy ) )
        {
            aRectFnSet.Refresh(*this);
            pMoveBwdPre = pTemp;
            isMoveBwdPreValid = bTemp;
            bMovedBwd = true;
            if (isHiddenNow)
            {   // MoveBwd invalidated the size! Validate to prevent Format!
                MakeValidZeroHeight();
            }
            else
            {
                bFormatted = false;
            }
            if (bKeep && bMoveable && !isHiddenNow)
            {
                if( CheckMoveFwd( bMakePage, false, bMovedBwd ) )
                {
                    bMovedFwd = true;
                    bMoveable = IsMoveable();
                    aRectFnSet.Refresh(*this);
                }
                Point aOldPos = aRectFnSet.GetPos(getFrameArea());
                MakePos();
                if( aOldPos != aRectFnSet.GetPos(getFrameArea()) )
                {
                    Prepare( PrepareHint::FramePositionChanged, static_cast<const void*>(&bFormatted), false );
                    if ( !isFrameAreaSizeValid() )
                    {
                        {
                            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                            aRectFnSet.SetWidth( aFrm, aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) );
                        }

                        if ( !isFramePrintAreaValid() )
                        {
                            const tools::Long nOldW = aRectFnSet.GetWidth(getFramePrintArea());
                            MakePrtArea( rAttrs );
                            if( nOldW != aRectFnSet.GetWidth(getFramePrintArea()) )
                                Prepare( PrepareHint::FixSizeChanged, nullptr, false );
                        }
                        if( GetFollow() )
                        {
                            Prepare( PrepareHint::WidowsOrphans, nullptr, false );
                        }

                        setFrameAreaSizeValid(true);
                        bFormatted = true;
                        Format(getRootFrame()->GetCurrShell()->GetOut());
                    }
                }
                SwFrame *pNxt = HasFollow() ? nullptr : FindNext();
                while( pNxt && pNxt->IsSctFrame() )
                {   // Leave empty sections out, go into the other ones.
                    if( static_cast<SwSectionFrame*>(pNxt)->GetSection() )
                    {
                        SwFrame* pTmp = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
                        if( pTmp )
                        {
                            pNxt = pTmp;
                            break;
                        }
                    }
                    pNxt = pNxt->FindNext();
                }
                if ( pNxt )
                {
                    pNxt->Calc(getRootFrame()->GetCurrShell()->GetOut());
                    if( isFrameAreaPositionValid() && !GetIndNext() )
                    {
                        SwSectionFrame *pSct = FindSctFrame();
                        if( pSct && !pSct->isFrameAreaSizeValid() )
                        {
                            SwSectionFrame* pNxtSct = pNxt->FindSctFrame();
                            if( pNxtSct && pSct->IsAnFollow( pNxtSct ) )
                            {
                                setFrameAreaPositionValid(false);
                            }
                        }
                        else
                        {
                            setFrameAreaPositionValid(false);
                        }
                    }
                }
            }
        }

        // In footnotes, the TextFrame may validate itself, which can lead to the
        // situation that it's position is wrong despite being "valid".
        if ( isFrameAreaPositionValid() )
        {
            // #i59341#
            // Workaround for inadequate layout algorithm:
            // suppress invalidation and calculation of position, if paragraph
            // has formatted itself at least STOP_FLY_FORMAT times and
            // has anchored objects.
            // Thus, the anchored objects get the possibility to format itself
            // and this probably solve the layout loop.
            if ( bFootnote &&
                 nFormatCount <= STOP_FLY_FORMAT &&
                 !GetDrawObjs() )
            {
                setFrameAreaPositionValid(false);
                MakePos();
                aOldFramePos = aRectFnSet.GetPos(getFrameArea());
                aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
            }
        }

        // - loop prevention
        if ( (aOldFrame_StopFormat == getFrameArea() || aOldFrame_StopFormat2 == getFrameArea() ) &&
             (aOldPrt_StopFormat == getFramePrintArea() || aOldPrt_StopFormat2 == getFramePrintArea()))
        {
            ++nConsecutiveFormatsWithoutChange;
        }
        else
        {
            nConsecutiveFormatsWithoutChange = 0;
        }

        // Yet again an invalid value? Repeat from the start...
        if ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
            continue;

        // Done?
        // Attention: because height == 0, it's better to use Top()+Height() instead of
        // Bottom(). This might happen with undersized TextFrames on the lower edge of a
        // multi-column section
        const tools::Long nPrtBottom = aRectFnSet.GetPrtBottom(*GetUpper());
        tools::Long nBottomDist = aRectFnSet.BottomDist(getFrameArea(), nPrtBottom);

        // Hide whitespace may require not to insert a new page.
        SwPageFrame* pPageFrame = FindPageFrame();
        const bool bHeightValid = pPageFrame->CheckPageHeightValidForHideWhitespace(nBottomDist);
        if (!bHeightValid)
        {
            pPageFrame->InvalidateSize();
            nBottomDist = 0;
        }

        if( nBottomDist >= 0 )
        {
            if (bKeep && bMoveable && !isHiddenNow)
            {
                // We make sure the successor will be formatted the same.
                // This way, we keep control until (almost) everything is stable,
                // allowing us to avoid endless loops caused by ever repeating
                // retries.

                // bMoveFwdInvalid is required for #38407#. This was originally solved
                // in flowfrm.cxx rev 1.38, but broke the above schema and
                // preferred to play towers of hanoi (#43669#).
                SwFrame *pNxt = HasFollow() ? nullptr : FindNext();
                // For sections we prefer the content, because it can change
                // the page if required.
                while( pNxt && pNxt->IsSctFrame() )
                {
                    if( static_cast<SwSectionFrame*>(pNxt)->GetSection() )
                    {
                        pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
                        break;
                    }
                    pNxt = pNxt->FindNext();
                }
                if ( pNxt )
                {
                    const bool bMoveFwdInvalid = nullptr != GetIndNext();
                    const bool bNxtNew =
                        ( 0 == aRectFnSet.GetHeight(pNxt->getFramePrintArea()) ) &&
                        !pNxt->IsHiddenNow();

                    pNxt->Calc(getRootFrame()->GetCurrShell()->GetOut());

                    if ( !bMovedBwd &&
                         ((bMoveFwdInvalid && !GetIndNext()) ||
                          bNxtNew) )
                    {
                        if( bMovedFwd )
                            oNotify->SetInvaKeep();
                        bMovedFwd = false;
                    }
                }
            }
            continue;
        }

        // I don't fit into my parents, so it's time to make changes
        // as constructively as possible.

        //If I'm NOT allowed to leave the parent Frame, I've got a problem.
        // Following Arthur Dent, we do the only thing that you can do with
        // an unsolvable problem: We ignore it with all our power.
        if ( !bMoveable || IsUndersized() )
        {
            break;
        }

        // If there's no way I can make myself fit into my Upper, the situation
        // could still probably be mitigated by splitting up.
        // This situation arises with freshly created Follows that had been moved
        // to the next page but is still too big for it - ie. needs to be split
        // as well.

        // If I'm unable to split (WouldFit()) and can't be fitted, I'm going
        // to tell my TextFrame part that, if possible, we still need to split despite
        // the "don't split" attribute.
        bool bMoveOrFit = false;
        bool bDontMoveMe = !GetIndPrev();
        if( bDontMoveMe && IsInSct() )
        {
            SwFootnoteBossFrame* pBoss = FindFootnoteBossFrame();
            bDontMoveMe = !pBoss->IsInSct() ||
                          ( !pBoss->Lower()->GetNext() && !pBoss->GetPrev() );
        }

        // Finally, we are able to split table rows. Therefore, bDontMoveMe
        // can be set to false:
        if( bDontMoveMe && IsInTab() &&
            nullptr != GetNextCellLeaf() )
            bDontMoveMe = false;

        assert(bMoveable);

        if ( bDontMoveMe && aRectFnSet.GetHeight(getFrameArea()) >
                            aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) )
        {
            if ( !bFitPromise )
            {
                SwTwips nTmp = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) -
                               aRectFnSet.GetTop(getFramePrintArea());
                bool bSplit = !IsFwdMoveAllowed();
                if (nTmp > 0 && WouldFit(nTmp, bSplit, false, false))
                {
                    Prepare( PrepareHint::WidowsOrphans, nullptr, false );
                    setFrameAreaSizeValid(false);
                    bFitPromise = true;
                    continue;
                }
                /*
                 * In earlier days, we never tried to fit TextFrames in
                 * frames and sections using bMoveOrFit by ignoring
                 * its attributes (Widows, Keep).
                 * This should have been done at least for column frames;
                 * as it must be tried anyway with linked frames and sections.
                 * Exception: If we sit in FormatWidthCols, we must not ignore
                 * the attributes.
                 */
                else if ( !bFootnote &&
                      ( !bFly || !FindFlyFrame()->IsColLocked() ) &&
                      ( !bSct || !FindSctFrame()->IsColLocked() ) )
                    bMoveOrFit = true;
            }
#if OSL_DEBUG_LEVEL > 0
            else
            {
                OSL_FAIL( "+TextFrame didn't respect WouldFit promise." );
            }
#endif
        }

        // Let's see if I can find some space somewhere...
        // footnotes in the neighbourhood are moved into _MoveFootnoteCntFwd
        SwFrame *pPre = GetIndPrev();
        SwFrame *pOldUp = GetUpper();

/* MA 13. Oct. 98: What is this supposed to be!?
 * AMA 14. Dec 98: If a column section can't find any space for its first ContentFrame, it should be
 *                 moved not only to the next column, but probably even to the next page, creating
 *                 a section-follow there.
 */
        if( IsInSct() && bMovedFwd && bMakePage && pOldUp->IsColBodyFrame() &&
            pOldUp->GetUpper()->GetUpper()->IsSctFrame() &&
            ( pPre || pOldUp->GetUpper()->GetPrev() ) &&
            static_cast<SwSectionFrame*>(pOldUp->GetUpper()->GetUpper())->MoveAllowed(this) )
        {
            bMovedFwd = false;
        }

        const bool bCheckForGrownBody = pOldUp->IsBodyFrame();
        const tools::Long nOldBodyHeight = aRectFnSet.GetHeight(pOldUp->getFrameArea());

        if ( !bMovedFwd && !MoveFwd( bMakePage, false ) )
            bMakePage = false;
        aRectFnSet.Refresh(*this);
        if (!bMovedFwd && bFootnote && GetIndPrev() != pPre)
        {   // SwFlowFrame::CutTree() could have formatted and deleted pPre
            auto const pPrevFootnoteFrame(static_cast<SwFootnoteFrame const*>(
                        FindFootnoteFrame())->GetMaster());
            bool bReset = true;
            if (pPrevFootnoteFrame)
            {   // use GetIndNext() in case there are sections
                for (auto p = pPrevFootnoteFrame->Lower(); p; p = p->GetIndNext())
                {
                    if (p == pPre)
                    {
                        bReset = false;
                        break;
                    }
                }
            }
            if (bReset)
            {
                pPre = nullptr;
            }
        }

        // If MoveFwd moves the paragraph to the next page, a following
        // paragraph, which contains footnotes can cause the old upper
        // frame to grow. In this case we explicitly allow a new check
        // for MoveBwd. Robust: We also check the bMovedBwd flag again.
        // If pOldUp was a footnote frame, it has been deleted inside MoveFwd.
        // Therefore we only check for growing body frames.
        bMovedFwd = !bCheckForGrownBody || bMovedBwd || pOldUp == GetUpper() ||
                    aRectFnSet.GetHeight(pOldUp->getFrameArea()) <= nOldBodyHeight;

        bFormatted = false;
        if ( bMoveOrFit && GetUpper() == pOldUp )
        {
            // FME 2007-08-30 #i81146# new loop control
            if ( nConsecutiveFormatsWithoutChange <= cnStopFormat )
            {
                Prepare( PrepareHint::MustFit, nullptr, false );
                setFrameAreaSizeValid(false);
                bMustFit = true;
                continue;
            }

#if OSL_DEBUG_LEVEL > 0
            OSL_FAIL( "LoopControl in SwContentFrame::MakeAll" );
#endif
        }
        if ( bMovedBwd && GetUpper() )
        {   // Retire invalidations that have become useless.
            GetUpper()->ResetCompletePaint();
            if( pPre && !pPre->IsSctFrame() )
                ::ValidateSz( pPre );
        }

    } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )

    // NEW: Looping Louie (Light). Should not be applied in balanced sections.
    // Should only be applied if there is no better solution!
    LOOPING_LOUIE_LIGHT( bMovedFwd && bMovedBwd && !IsInBalancedSection() &&
                            (

                                ( bFootnote && !FindFootnoteFrame()->GetRef()->IsInSct() ) ||

                                // #i33887#
                                ( IsInSct() && bKeep )

                                // ... add your conditions here ...

                            ),
                         static_cast<SwTextFrame&>(*this) );

    pSaveFootnote.reset();

    UnlockJoin();
    oDeleteGuard.reset();
    if ( bMovedFwd || bMovedBwd )
        oNotify->SetInvaKeep();
    if ( bMovedFwd )
    {
        oNotify->SetInvalidatePrevPrtArea();
    }
    oNotify.reset();
    SetFlyLock( false );
}

void MakeNxt( SwFrame *pFrame, SwFrame *pNxt )
{
    // fix(25455): Validate, otherwise this leads to a recursion.
    // The first try, cancelling with pFrame = 0 if !Valid, leads to a problem, as
    // the Keep may not be considered properly anymore (27417).
    const bool bOldPos = pFrame->isFrameAreaPositionValid();
    const bool bOldSz  = pFrame->isFrameAreaSizeValid();
    const bool bOldPrt = pFrame->isFramePrintAreaValid();
    pFrame->setFrameAreaPositionValid(true);
    pFrame->setFrameAreaSizeValid(true);
    pFrame->setFramePrintAreaValid(true);

    // fix(29272): Don't call MakeAll - there, pFrame might be invalidated again, and
    // we recursively end up in here again.
    if ( pNxt->IsContentFrame() )
    {
        SwContentNotify aNotify( static_cast<SwContentFrame*>(pNxt) );
        SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNxt );
        const SwBorderAttrs &rAttrs = *aAccess.Get();
        if ( !pNxt->isFrameAreaSizeValid() )
        {
            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNxt);

            if( pNxt->IsVertical() )
            {
                aFrm.Height( pNxt->GetUpper()->getFramePrintArea().Height() );
            }
            else
            {
                aFrm.Width( pNxt->GetUpper()->getFramePrintArea().Width() );
            }
        }
        static_cast<SwContentFrame*>(pNxt)->MakePrtArea( rAttrs );
        pNxt->Format( pNxt->getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
    }
    else
    {
        SwLayNotify aNotify( static_cast<SwLayoutFrame*>(pNxt) );
        SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNxt );
        const SwBorderAttrs &rAttrs = *aAccess.Get();
        if ( !pNxt->isFrameAreaSizeValid() )
        {
            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNxt);

            if( pNxt->IsVertical() )
            {
                aFrm.Height( pNxt->GetUpper()->getFramePrintArea().Height() );
            }
            else
            {
                aFrm.Width( pNxt->GetUpper()->getFramePrintArea().Width() );
            }
        }
        pNxt->Format( pNxt->getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
    }

    pFrame->setFrameAreaPositionValid(bOldPos);
    pFrame->setFrameAreaSizeValid(bOldSz);
    pFrame->setFramePrintAreaValid(bOldPrt);
}

/// This routine checks whether there are no other FootnoteBosses
/// between the pFrame's FootnoteBoss and the pNxt's FootnoteBoss.
static bool lcl_IsNextFootnoteBoss( const SwFrame *pFrame, const SwFrame* pNxt )
{
    assert(pFrame && pNxt && "lcl_IsNextFootnoteBoss: No Frames?");
    pFrame = pFrame->FindFootnoteBossFrame();
    pNxt = pNxt->FindFootnoteBossFrame();
    // If pFrame is a last column, we use the page instead.
    while( pFrame && pFrame->IsColumnFrame() && !pFrame->GetNext() )
        pFrame = pFrame->GetUpper()->FindFootnoteBossFrame();
    // If pNxt is a first column, we use the page instead.
    while( pNxt && pNxt->IsColumnFrame() && !pNxt->GetPrev() )
        pNxt = pNxt->GetUpper()->FindFootnoteBossFrame();
    // So... now pFrame and pNxt are either two adjacent pages or columns.
    return pFrame && pNxt && pFrame->GetNext() == pNxt;
}

bool SwContentFrame::WouldFit_( SwTwips nSpace,
                            SwLayoutFrame *pNewUpper,
                            bool bTstMove,
                            const bool bObjsInNewUpper )
{
    // To have the footnote select its place carefully, it needs
    // to be moved in any case if there is at least one page/column
    // between the footnote and the new Upper.
    SwFootnoteFrame* pFootnoteFrame = nullptr;
    if ( IsInFootnote() )
    {
        if( !lcl_IsNextFootnoteBoss( pNewUpper, this ) )
            return true;
        pFootnoteFrame = FindFootnoteFrame();
    }

    bool bRet;
    bool bSplit = !pNewUpper->Lower();
    SwContentFrame *pFrame = this;
    const SwFrame *pTmpPrev = pNewUpper->Lower();
    if( pTmpPrev && pTmpPrev->IsFootnoteFrame() )
        pTmpPrev = static_cast<const SwFootnoteFrame*>(pTmpPrev)->Lower();
    {
        SwFrame const* pTmpNonHidden{pTmpPrev && pTmpPrev->IsHiddenNow() ? nullptr : pTmpPrev};
        while (pTmpPrev && pTmpPrev->GetNext())
        {
            pTmpPrev = pTmpPrev->GetNext();
            if (!pTmpPrev->IsHiddenNow())
            {
                pTmpNonHidden = pTmpPrev;
            }
        }
        pTmpPrev = pTmpNonHidden;
    }

    // tdf#156727 if the previous one has keep-with-next, ignore it on this one!
    bool const isIgnoreKeep(pTmpPrev && pTmpPrev->IsFlowFrame()
            && SwFlowFrame::CastFlowFrame(pTmpPrev)->IsKeep(
                pTmpPrev->GetAttrSet()->GetKeep(), pTmpPrev->GetBreakItem()));

    do
    {
        if (pFrame->IsHiddenNow())
        {   // shortcut
            assert(pFrame == this);
            bRet = true;
            pFrame = nullptr;
            break;
        }

        // #i46181#
        SwTwips nSecondCheck = 0;
        SwTwips nOldSpace = nSpace;
        bool bOldSplit = bSplit;

        if ( bTstMove || IsInFly() || ( IsInSct() &&
             ( pFrame->GetUpper()->IsColBodyFrame() || ( pFootnoteFrame &&
               pFootnoteFrame->GetUpper()->GetUpper()->IsColumnFrame() ) ) ) )
        {
            // This is going to get a bit insidious now. If you're faint of heart,
            // you'd better look away here. If a Fly contains columns, then the Contents
            // are movable, except ones in the last column (see SwFrame::IsMoveable()).
            // Of course they're allowed to float back. WouldFit() only returns a usable
            // value if the Frame is movable. To fool WouldFit() into believing there's
            // a movable Frame, I'm just going to hang it somewhere else for the time.
            // The same procedure applies for column sections to make SwSectionFrame::Growable()
            // return the proper value.
            // Within footnotes, we may even need to put the SwFootnoteFrame somewhere else, if
            // there's no SwFootnoteFrame there.
            SwFrame* pTmpFrame = pFrame->IsInFootnote() && !pNewUpper->FindFootnoteFrame() ?
                             static_cast<SwFrame*>(pFrame->FindFootnoteFrame()) : pFrame;
            SwLayoutFrame *pUp = pTmpFrame->GetUpper();
            SwFrame *pOldNext = pTmpFrame->GetNext();
            pTmpFrame->RemoveFromLayout();
            pTmpFrame->InsertBefore( pNewUpper, nullptr );
            // tdf#107126 for a section in a footnote, we have only inserted
            // the SwTextFrame but no SwSectionFrame - reset mbInfSct flag
            // to avoid crashing (but perhaps we should create a temp
            // SwSectionFrame here because WidowsAndOrphans checks for that?)
            pTmpFrame->InvalidateInfFlags();
            if ( pFrame->IsTextFrame() &&
                 ( bTstMove ||
                   static_cast<SwTextFrame*>(pFrame)->HasFollow() ||
                   ( !static_cast<SwTextFrame*>(pFrame)->HasPara() &&
                     !static_cast<SwTextFrame*>(pFrame)->IsEmpty()
                   )
                 )
               )
            {
                bTstMove = true;
                bRet = static_cast<SwTextFrame*>(pFrame)->TestFormat( pTmpPrev, nSpace, bSplit );
            }
            else
                bRet = pFrame->WouldFit(nSpace, bSplit, false, true);

            pTmpFrame->RemoveFromLayout();
            pTmpFrame->InsertBefore( pUp, pOldNext );
            pTmpFrame->InvalidateInfFlags(); // restore flags
        }
        else
        {
            bRet = pFrame->WouldFit(nSpace, bSplit, false, true);
            nSecondCheck = !bSplit ? 1 : 0;
        }

        SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
        const SwBorderAttrs &rAttrs = *aAccess.Get();

        // Sad but true: We need to consider the spacing in our calculation.
        // This already happened in TestFormat.
        if ( bRet && !bTstMove )
        {
            SwTwips nUpper;

            if ( pTmpPrev )
            {
                nUpper = CalcUpperSpace( nullptr, pTmpPrev );

                // in balanced columned section frames we do not want the
                // common border
                bool bCommonBorder = true;
                if ( pFrame->IsInSct() && pFrame->GetUpper()->IsColBodyFrame() )
                {
                    const SwSectionFrame* pSct = pFrame->FindSctFrame();
                    bCommonBorder = pSct->GetFormat()->GetBalancedColumns().GetValue();
                }

                // #i46181#
                nSecondCheck = ( 1 == nSecondCheck &&
                                 pFrame == this &&
                                 IsTextFrame() &&
                                 bCommonBorder &&
                                 !static_cast<const SwTextFrame*>(this)->IsEmpty() ) ?
                                 nUpper :
                                 0;

                nUpper += bCommonBorder ?
                          rAttrs.GetBottomLine( *pFrame ) :
                          rAttrs.CalcBottomLine();

            }
            else
            {
                // #i46181#
                nSecondCheck = 0;

                if( pFrame->IsVertical() )
                    nUpper = pFrame->getFrameArea().Width() - pFrame->getFramePrintArea().Width();
                else
                    nUpper = pFrame->getFrameArea().Height() - pFrame->getFramePrintArea().Height();
            }

            nSpace -= nUpper;

            if ( nSpace < 0 )
            {
                bRet = false;

                // #i46181#
                if ( nSecondCheck > 0 )
                {
                    // The following code is intended to solve a (rare) problem
                    // causing some frames not to move backward:
                    // SwTextFrame::WouldFit() claims that the whole paragraph
                    // fits into the given space and subtracts the height of
                    // all lines from nSpace. nSpace - nUpper is not a valid
                    // indicator if the frame should be allowed to move backward.
                    // We do a second check with the original remaining space
                    // reduced by the required upper space:
                    nOldSpace -= nSecondCheck;
                    const bool bSecondRet = nOldSpace >= 0 && pFrame->WouldFit(nOldSpace, bOldSplit, false, true);
                    if ( bSecondRet && bOldSplit && nOldSpace >= 0 )
                    {
                        bRet = true;
                        bSplit = true;
                    }
                }
            }
        }

        // Also consider lower spacing in table cells
        IDocumentSettingAccess const& rIDSA(pNewUpper->GetFormat()->getIDocumentSettingAccess());
        if ( bRet && IsInTab() &&
            rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS))
        {
            nSpace -= rAttrs.GetULSpace().GetLower();

            if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
            {
                nSpace -= rAttrs.CalcLineSpacing();
            }
            if ( nSpace < 0 )
            {
                bRet = false;
            }
        }

        bool bKeep = false;
        if (bRet && !bSplit && !isIgnoreKeep
            && pFrame->IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem()))
        {
            // The SwTextFrameBreak ctor already turns off bKeep for split fly anchors, don't
            // change that decision here.
            bKeep = pFrame->IgnoringSplitFlyAnchor(true);
        }

        if (bKeep)
        {
            if( bTstMove )
            {
                while( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->HasFollow() )
                {
                    pFrame = static_cast<SwTextFrame*>(pFrame)->GetFollow();
                }
                // If last follow frame of <this> text frame isn't valid,
                // a formatting of the next content frame doesn't makes sense.
                // Thus, return true.
                if ( IsAnFollow( pFrame ) && !pFrame->isFrameAreaDefinitionValid() )
                {
                    OSL_FAIL( "Only a warning for task 108824:/n<SwContentFrame::WouldFit_(..) - follow not valid!" );
                    return true;
                }
            }
            SwFrame *const pNxt{pFrame->FindNextIgnoreHidden()};
            if (nullptr != pNxt && pNxt->IsContentFrame() &&
                ( !pFootnoteFrame || ( pNxt->IsInFootnote() &&
                  pNxt->FindFootnoteFrame()->GetAttr() == pFootnoteFrame->GetAttr() ) ) )
            {
                // TestFormat(?) does not like paragraph- or character anchored objects.

                // current solution for the test formatting doesn't work, if
                // objects are present in the remaining area of the new upper
                if ( bTstMove &&
                     ( pNxt->GetDrawObjs() || bObjsInNewUpper ) )
                {
                    return true;
                }

                if ( !pNxt->isFrameAreaDefinitionValid() )
                {
                    MakeNxt( pFrame, pNxt );
                }

                // Little trick: if the next has a predecessor, then the paragraph
                // spacing has been calculated already, and we don't need to re-calculate
                // it in an expensive way.
                if( lcl_NotHiddenPrev( pNxt ) )
                    pTmpPrev = nullptr;
                else
                {
                    pTmpPrev = pFrame;
                }
                pFrame = static_cast<SwContentFrame*>(pNxt);
            }
            else
                pFrame = nullptr;
        }
        else
            pFrame = nullptr;

    } while ( bRet && pFrame );

    return bRet;
}

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