Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sw/source/core/layout/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 94 kB image not shown  

Quelle  calcmove.cxx

  Sprache: C
 

/* -*- 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);
}

// Here we return GetPrev(); however we will ignore empty SectionFrames
static SwFrame* lcl_Prev( SwFrame* pFrame, bool bSectPrv = true )
{
    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;
}

static SwFrame* lcl_NotHiddenPrev( SwFrame* pFrame )
{
    SwFrame *pRet = pFrame;
    do
    {
        pRet = lcl_Prev( pRet );
    } while ( pRet && pRet->IsHiddenNow() );
    return pRet;
}

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

    setFrameAreaPositionValid(true);
    bool bUseUpper = false;
    SwFrame* pPrv = lcl_Prev( this );
    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 = lcl_Prev( thisfalse );
    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 = lcl_Prev( thisfalse );
        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 nBottomconst
{
    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 &&
             ( lcl_Prev( this ) ||
               GetUpper()->IsCellFrame() ||
               ( GetUpper()->IsSctFrame() &&
                 GetUpper()->GetUpper()->IsCellFrame() ) ) &&
             IsMoveable() )
        {
            bMovedFwd = true;
            MoveFwd( bMakePage, false );
        }
    }
    else if (auto* prev = lcl_Prev(this); 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 ( !lcl_Prev( this ) &&
             !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.
                    ifstatic_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() )
                {
                    ifstatic_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, falsefalse))
                {
                    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, falsetrue);

            pTmpFrame->RemoveFromLayout();
            pTmpFrame->InsertBefore( pUp, pOldNext );
            pTmpFrame->InvalidateInfFlags(); // restore flags
        }
        else
        {
            bRet = pFrame->WouldFit(nSpace, bSplit, falsetrue);
            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, falsetrue);
                    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: */

Messung V0.5 in Prozent
C=84 H=94 G=88

¤ Dauer der Verarbeitung: 0.51 Sekunden  (vorverarbeitet am  2026-05-05) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.