/* -*- 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 .
*/
void SwTextFrame::ValidateFrame()
{
vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut(); // Validate surroundings to avoid oscillation
SwSwapIfSwapped swap( this );
if ( !IsInFly() && !IsInTab() )
{ // Only validate 'this' when inside a fly, the rest should actually only be // needed for footnotes, which do not exist in flys.
SwSectionFrame* pSct = FindSctFrame(); if( pSct )
{ if( !pSct->IsColLocked() )
pSct->ColLock(); else
pSct = nullptr;
}
// We at least have to save the MustFit flag!
assert(HasPara() && "ResetPreps(), missing ParaPortion, SwCache bug?");
SwParaPortion *pPara = GetPara(); constbool bMustFit = pPara->IsPrepMustFit();
ResetPreps();
pPara->SetPrepMustFit( bMustFit );
}
// After a RemoveFootnote the BodyFrame and all Frames contained within it, need to be // recalculated, so that the DeadLine is right. // First we search outwards, on the way back we calculate everything. staticvoid ValidateBodyFrame_( SwFrame *pFrame )
{
vcl::RenderContext* pRenderContext = pFrame ? pFrame->getRootFrame()->GetCurrShell()->GetOut() : nullptr; if( !pFrame || pFrame->IsCellFrame() ) return;
// tdf#122892 check flag: // 1. WidowsAndOrphans::FindWidows() determines follow is a widow // 2. SwTextFrame::PrepWidows() calls SetPrepWidows() on master; // if it can spare lines, master truncates one line // 3. SwTextFrame::CalcPreps() on master (below); // unless IsPrepMustFit(), if master hasn't shrunk via 2., it will SetWidow() // 4. loop must exit then, because the follow didn't grow so nothing will ever change while (!IsWidow())
{ if( !FormatLevel::LastLevel() )
{ // If the follow is contained within a column section or column // frame, we need to calculate that first. This is because the // FormatWidthCols() does not work if it is called from MakeAll // of the _locked_ follow.
SwSectionFrame* pSct = pMyFollow->FindSctFrame(); if( pSct && !pSct->IsAnLower( this ) )
{ if( pSct->GetFollow() )
pSct->SimpleFormat(); elseif( ( pSct->IsVertical() && !pSct->getFrameArea().Width() ) ||
( ! pSct->IsVertical() && !pSct->getFrameArea().Height() ) ) break;
} // i#11760 - Intrinsic format of follow is controlled. if ( FollowFormatAllowed() )
{ // i#11760 - No nested format of follows, if // text frame is contained in a column frame. // Thus, forbid intrinsic format of follow.
{ bool bIsFollowInColumn = false;
SwFrame* pFollowUpper = pMyFollow->GetUpper(); while ( pFollowUpper )
{ if ( pFollowUpper->IsColumnFrame() )
{
bIsFollowInColumn = true; break;
} if ( pFollowUpper->IsPageFrame() ||
pFollowUpper->IsFlyFrame() )
{ break;
}
pFollowUpper = pFollowUpper->GetUpper();
} if ( bIsFollowInColumn )
{
pMyFollow->ForbidFollowFormat();
}
}
pMyFollow->Calc(pRenderContext); // The Follow can tell from its getFrameArea().Height() that something went wrong
OSL_ENSURE( !pMyFollow->GetPrev(), "SwTextFrame::CalcFollow: cheesy follow" ); if( pMyFollow->GetPrev() )
{
pMyFollow->Prepare();
pMyFollow->Calc(pRenderContext);
OSL_ENSURE( !pMyFollow->GetPrev(), "SwTextFrame::CalcFollow: very cheesy follow" );
}
// i#11760 - Reset control flag for follow format.
pMyFollow->AllowFollowFormat();
}
// Make sure that the Follow gets painted
pMyFollow->SetCompletePaint();
}
pPara = GetPara(); // As long as the Follow requests lines due to Orphans, it is // passed these and is formatted again if possible if( pPara && pPara->IsPrepWidows() )
CalcPreps(); else break;
}
void SwTextFrame::MakePos()
{
Point aOldPos = getFrameArea().Pos();
SwFrame::MakePos();
// Recalc split flys if our position changed. if (aOldPos != getFrameArea().Pos())
{ // Find the master frame. const SwTextFrame* pMaster = this; while (pMaster->IsFollow())
{
pMaster = pMaster->FindMaster();
} // Find which flys are effectively anchored to this frame. for (constauto& pFly : pMaster->GetSplitFlyDrawObjs())
{
SwTextFrame* pFlyAnchor = pFly->FindAnchorCharFrame(); if (pFlyAnchor != this)
{ continue;
} // Possibly this fly was positioned relative to us, invalidate its position now that our // position is changed.
SwPageFrame* pPageFrame = pFly->FindPageFrame(); bool bFlyNeedsPositioning = false; bool bFlyPageMismatch = false; if (pPageFrame)
{ // Was the position just adjusted to be inside the page frame?
bFlyNeedsPositioning = pFly->getFrameArea().Pos() == pPageFrame->getFrameArea().Pos(); // Is the fly on a page different than the anchor frame?
bFlyPageMismatch = pPageFrame != FindPageFrame();
} if (bFlyNeedsPositioning || bFlyPageMismatch)
{ // Not really positioned, unlock the position once to allow a recalc.
pFly->UnlockPosition();
}
pFly->InvalidatePos();
}
}
// Inform LOK clients about change in position of redlines (if any) if(!comphelper::LibreOfficeKit::isActive()) return;
// AdjustFrame is called with a swapped frame during // formatting but the frame is not swapped during FormatEmpty
SwSwapIfSwapped swap( this );
SwRectFnSet aRectFnSet(this);
// The Frame's size variable is incremented by Grow or decremented by Shrink. // If the size cannot change, nothing should happen! if( nChgHght >= 0)
{
SwTwips nChgHeight = nChgHght; if( nChgHght && !bHasToFit )
{ if( IsInFootnote() && !IsInSct() )
{
SwTwips nReal = Grow( nChgHght, true ); if( nReal < nChgHght )
{
SwTwips nBot = aRectFnSet.YInc( aRectFnSet.GetBottom(getFrameArea()),
nChgHght - nReal );
SwFrame* pCont = FindFootnoteFrame()->GetUpper();
if ( IsInFly() )
{ // If one of the Upper is a Fly, it's very likely that this fly changes its // position by the Grow. Therefore, my position has to be corrected also or // the check further down is not meaningful. // The predecessors need to be calculated, so that the position can be // calculated correctly. if ( GetPrev() )
{
SwFrame *pPre = GetUpper()->Lower(); do
{ pPre->Calc(pRenderContext);
pPre = pPre->GetNext();
} while ( pPre && pPre != this );
} const Point aOldPos( getFrameArea().Pos() );
MakePos(); if ( aOldPos != getFrameArea().Pos() )
{
InvalidateObjs(false);
}
}
nChgHeight = 0;
} // A Grow() is always accepted by the Layout, even if the // FixSize of the surrounding layout frame should not allow it. // We text for this case and correct the values. // The Frame must NOT be shrunk further than its size permits // even in the case of an emergency.
SwTwips nRstHeight; if ( IsVertical() )
{
OSL_ENSURE( ! IsSwapped(),"Swapped frame while calculating nRstHeight" );
// We can get a bit of space in table cells, because there could be some // left through a vertical alignment to the top. // Assure that first lower in upper is the current one or is valid. if (IsInTab())
{
SwFrame* pLower = GetUpper()->Lower(); if ( pLower == this || (pLower && pLower->isFrameAreaDefinitionValid()) )
{
tools::Long nAdd = aRectFnSet.YDiff( aRectFnSet.GetTop(pLower->getFrameArea()),
aRectFnSet.GetPrtTop(*GetUpper()) );
OSL_ENSURE( nAdd >= 0, "Ey" );
nRstHeight += nAdd;
}
}
// nRstHeight < 0 means that the TextFrame is located completely outside of its Upper. // This can happen, if it's located within a FlyAtContentFrame, which changed sides by a // Grow(). In such a case, it's wrong to execute the following Grow(). // In the case of a bug, we end up with an infinite loop.
SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
SwTwips nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
if( nRstHeight < nFrameHeight )
{ // It can be that I have the right size, but the Upper is too small and can get me some room if( ( nRstHeight >= 0 || ( IsInFootnote() && IsInSct() ) ) && !bHasToFit )
nRstHeight += GetUpper()->Grow( nFrameHeight - nRstHeight ); // In column sections we do not want to get too big or else more areas are created by // GetNextSctLeaf. Instead, we shrink and remember bUndersized, so that FormatWidthCols // can calculate the right column size. if ( nRstHeight < nFrameHeight )
{ if( bHasToFit || !IsMoveable() ||
( IsInSct() && !FindSctFrame()->MoveAllowed(this) ) )
{
SetUndersized( true );
Shrink( std::min( ( nFrameHeight - nRstHeight), nPrtHeight ) );
} else
SetUndersized( false );
}
} elseif( nChgHeight )
{ if( nRstHeight - nFrameHeight < nChgHeight )
nChgHeight = nRstHeight - nFrameHeight; if( nChgHeight )
Grow( nChgHeight );
}
} else
Shrink( -nChgHght );
}
// get current tab stop information stored in the Frame const SvxTabStop *pTS = aLine.GetLineInfo().GetTabStop( CurrentPos, nRight );
if( !pTS )
{ return {};
}
// copy tab stop information into a Sequence, which only contains one element.
css::style::TabStop ts;
ts.Position = pTS->GetTabPos();
ts.DecimalChar = pTS->GetDecimal();
ts.FillChar = pTS->GetFill(); switch( pTS->GetAdjustment() )
{ case SvxTabAdjust::Left : ts.Alignment = css::style::TabAlign_LEFT; break; case SvxTabAdjust::Center : ts.Alignment = css::style::TabAlign_CENTER; break; case SvxTabAdjust::Right : ts.Alignment = css::style::TabAlign_RIGHT; break; case SvxTabAdjust::Decimal: ts.Alignment = css::style::TabAlign_DECIMAL; break; case SvxTabAdjust::Default: ts.Alignment = css::style::TabAlign_DEFAULT; break; default: break; // prevent warning
}
return { ts };
}
// AdjustFollow expects the following situation: // The SwTextIter points to the lower end of the Master, the Offset is set in the Follow. // nOffset holds the Offset in the text string, from which the Master closes // and the Follow starts. // If it's 0, the FollowFrame is deleted. void SwTextFrame::AdjustFollow_( SwTextFormatter &rLine, const TextFrameIndex nOffset, const TextFrameIndex nEnd, constbool bDontJoin)
{
SwFrameSwapper aSwapper( this, false );
// We got the rest of the text mass: Delete all Follows // DummyPortions() are a special case. if( HasFollow() && !bDontJoin && nOffset == nEnd )
{ while( GetFollow() )
{ if( GetFollow()->IsLocked() )
{ // this can happen when follow calls pMaster->GetFormatted()
SAL_INFO("sw.core", "+SwTextFrame::JoinFrame: Follow is locked." ); return;
} if (GetFollow()->IsDeleteForbidden()) return;
if (HasNonLastSplitFlyDrawObj())
{ // If a fly frame is anchored to us that has a follow, then don't join the anchor. // First those fly frames have to be joined. return;
}
JoinFrame();
}
return;
}
// Dancing on the volcano: We'll just format the last line quickly // for the QuoVadis stuff. // The Offset can move of course: const TextFrameIndex nNewOfst = (IsInFootnote() && (!GetIndNext() || HasFollow()))
? rLine.FormatQuoVadis(nOffset) : nOffset;
if( !bDontJoin )
{ // We steal text mass from our Follows // It can happen that we have to join some of them while( GetFollow() && GetFollow()->GetFollow() &&
nNewOfst >= GetFollow()->GetFollow()->GetOffset() )
{ if (bHasNonLastSplitFlyDrawObj)
{ // A non-last split fly is anchored to us, don't move content from the last frame to // this one and don't join. return;
}
JoinFrame();
}
}
if (IsEmptyMasterWithSplitFly())
{ // A split fly is anchored to us, don't move content from the follow frame to this one. return;
}
// The Offset moved if( GetFollow() )
{ if (!bDontJoin && bHasNonLastSplitFlyDrawObj)
{ // A non-last split fly is anchored to us, our follow is the last one in the text frame // chain. No move of text from that follow to this text frame. return;
}
if (bDontJoin)
GetFollow()->ManipOfst(TextFrameIndex(0));
if ( CalcFollow( nNewOfst ) ) // CalcFollow only at the end, we do a SetOffset there
rLine.SetOnceMore( true );
}
}
void SwTextFrame::SplitFrame(TextFrameIndex const nTextPos)
{
SwSwapIfSwapped swap( this );
// The Paste sends a Modify() to me // I lock myself, so that my data does not disappear
TextFrameLockGuard aLock( this );
SwTextFrame *const pNew = static_cast<SwTextFrame *>(GetTextNodeFirst()->MakeFrame(this));
// No SetOffset or CalcFollow, because an AdjustFollow follows immediately anyways
pNew->ManipOfst( nTextPos );
}
void SwTextFrame::SetOffset_(TextFrameIndex const nNewOfst)
{ // We do not need to invalidate our Follow. // We are a Follow, get formatted right away and call // SetOffset() from there
mnOffset = nNewOfst;
SwParaPortion *pPara = GetPara(); if( pPara )
{
SwCharRange &rReformat = pPara->GetReformat();
rReformat.Start() = TextFrameIndex(0);
rReformat.Len() = TextFrameIndex(GetText().getLength());
pPara->SetDelta(sal_Int32(rReformat.Len()));
}
InvalidateSize();
}
bool bRet = false; if( bPrep && !pPara->GetReformat().Len() )
{ // PrepareHint::Widows means that the orphans rule got activated in the Follow. // In unfortunate cases we could also have a PrepAdjust! if( bPrepWidows )
{ if( !GetFollow() )
{
OSL_ENSURE( GetFollow(), "+SwTextFrame::CalcPreps: no credits" ); returnfalse;
}
// We need to prepare for two cases: // We were able to hand over a few lines to the Follow // -> we need to shrink // or we need to go on the next page // -> we let our Frame become too big
SwTwips nChgHeight = GetParHeight(); if( nChgHeight >= aRectFnSet.GetHeight(getFramePrintArea()) )
{ if( bPrepMustFit )
{
GetFollow()->SetJustWidow( true );
GetFollow()->Prepare();
} elseif ( aRectFnSet.IsVert() )
{ // Replicate the same overflow behavior that is used for horizontal portions.
SwTwips const nTmp = sw::WIDOW_MAGIC - (getFrameArea().Left() + 10000);
SwTwips nDiff = nTmp - getFrameArea().Width();
SetWidow( true );
} else
{ // nTmp should be very large, but not so large as to cause overflow later (e.g., // GetFrameOfModify in sw/source/core/layout/frmtool.cxx calculates nCurrentDist // from, among others, the square of aDiff.getY(), which can be close to nTmp); // the previously used value TWIPS_MAX/2 (i.e., (LONG_MAX - 1)/2) depended on // the range of 'long', while the value (SAL_MAX_INT32 - 1)/2 (which matches the // old value on platforms where 'long' is 'sal_Int32') is empirically shown to // be large enough in practice even on platforms where 'long' is 'sal_Int64':
SwTwips const nTmp = sw::WIDOW_MAGIC - (getFrameArea().Top()+10000);
SwTwips nDiff = nTmp - getFrameArea().Height();
SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
SwTextFormatter aLine( this, &aInf );
WidowsAndOrphans aFrameBreak( this ); // Whatever the attributes say: we split the paragraph in // MustFit case if necessary if( bPrepMustFit )
{
aFrameBreak.SetKeep( false );
aFrameBreak.ClrOrphLines();
} // Before calling FormatAdjust, we need to make sure // that the lines protruding at the bottom get indeed // truncated bool bBreak = aFrameBreak.IsBreakNowWidAndOrp( aLine );
bRet = true; while( !bBreak && aLine.Next() )
{
bBreak = aFrameBreak.IsBreakNowWidAndOrp( aLine );
} if( bBreak )
{ // We run into troubles: when TruncLines is called, the // conditions in IsInside change immediately such that // IsBreakNow can return different results. // For this reason, we tell rFrameBreak that the // end is reached at the location of rLine. // Let's see if it works ...
aLine.TruncLines();
aFrameBreak.SetRstHeight( aLine );
FormatAdjust( aLine, aFrameBreak, TextFrameIndex(aInf.GetText().getLength()), aInf.IsStop() );
} else
{ if( !GetFollow() )
{
FormatAdjust( aLine, aFrameBreak,
TextFrameIndex(aInf.GetText().getLength()), aInf.IsStop() );
} elseif ( !aFrameBreak.IsKeepAlways() )
{ // We delete a line before the Master, because the Follow // could hand over a line const SwCharRange aFollowRg(GetFollow()->GetOffset(), TextFrameIndex(1));
pPara->GetReformat() += aFollowRg; // We should continue!
bRet = false;
}
}
}
// A final check, if FormatAdjust() didn't help we need to // truncate if( bPrepMustFit )
{ const SwTwips nMust = aRectFnSet.GetPrtBottom(*GetUpper()); const SwTwips nIs = aRectFnSet.GetBottom(getFrameArea());
namespace
{ /// Determines if pFrame has at least one anchored object which is positioned against the page frame /// and uses all space available for body text. bool HasFullPageFly(const SwTextFrame* pFrame)
{ const SwFrame* pBodyFrame = pFrame->FindBodyFrame(); if (!pBodyFrame)
{ // Inside a fly frame, not interesting. returnfalse;
}
for (SwAnchoredObject* pDrawObj : *pDrawObjs)
{
SwFrameFormat* pFrameFormat = pDrawObj->GetFrameFormat(); if (pFrameFormat->GetHoriOrient().GetRelationOrient() != text::RelOrientation::PAGE_FRAME)
{ continue;
}
if (pFrameFormat->GetVertOrient().GetRelationOrient() != text::RelOrientation::PAGE_FRAME)
{ continue;
}
if (pFrameFormat->GetSurround().GetValue() != text::WrapTextMode::WrapTextMode_NONE)
{ // Not a case where the request is to wrap the content around the object, ignore. continue;
}
if (pDrawObj->GetObjRectWithSpaces().Contains(rBodyFrameArea))
{ // Wrap is requested, but the object uses all available space: this is a full page // object. returntrue;
}
}
// The StopFlag is set by footnotes which want to go onto the next page // Call base class method <SwTextFrameBreak::IsBreakNow(..)> // instead of method <WidowsAndOrphans::IsBreakNow(..)> to get a break, // even if due to widow rule no enough lines exists. bool createNew = ( !GetFollow() &&
nEnd < nStrLen &&
( rLine.IsStop() ||
( bHasToFit
? ( rLine.GetLineNr() > 1 &&
!rFrameBreak.IsInside( rLine ) )
: rFrameBreak.IsBreakNow( rLine ) ) ) );
SwTextFormatInfo& rInf = rLine.GetInfo(); bool bEmptyWithSplitFly = false; if (!createNew && !nStrLen && !rInf.GetTextFly().IsOn() && IsEmptyWithSplitFly())
{ // Empty paragraph, so IsBreakNow() is not called, but we should split the fly portion and // the paragraph marker.
createNew = true;
bEmptyWithSplitFly = true;
}
const SwFrame *pBodyFrame = FindBodyFrame();
// i#84870 // no split of text frame, which only contains an as-character anchored object bool bLoneAsCharAnchoredObj =
pBodyFrame &&
!IsFollow() && nStrLen == TextFrameIndex(1) &&
GetDrawObjs() && GetDrawObjs()->size() == 1 &&
(*GetDrawObjs())[0]->GetFrameFormat()->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
if (bLoneAsCharAnchoredObj)
{ // Still try split text frame if we have columns. if (FindColFrame())
bLoneAsCharAnchoredObj = false; // tdf#160526: only no split if there is no preceding frames on same page elseif (!isFirstVisibleFrameInPageBody(this))
bLoneAsCharAnchoredObj = false; else
createNew = false;
} elseif (createNew)
{ if (IsFollow())
{ // tdf#160549: do not split the frame at the very beginning again, if its master was empty auto precede = static_cast<SwTextFrame*>(GetPrecede());
assert(precede); auto precedeText = precede->DynCastTextFrame();
assert(precedeText); if (isReallyEmptyMaster(precedeText))
createNew = false;
} elseif (!bEmptyWithSplitFly)
{ // Do not split immediately in the beginning of page (unless there is an at-para or // at-char or at-page fly, which pushes the rest down); tdf#136040: still try split text // frame if we have columns. if (pBodyFrame && !FindColFrame() && isFirstVisibleFrameInPageBody(this)
&& !hasFly(this) && !hasAtPageFly(pBodyFrame))
createNew = false;
}
}
if (createNew && nEnd == TextFrameIndex(0) && !bEmptyWithSplitFly && HasFullPageFly(this))
{ // We intended to split at start, due to an anchored object which would use all space on the // current page. It makes no sense to split & move all text of the frame forward: the // current page would be empty and we would move back later anyway.
createNew = false;
}
// If the current values have been calculated, show that they // are valid now
pPara->GetReformat() = SwCharRange(); bool bDelta = pPara->GetDelta() != 0;
pPara->SetDelta(0);
// FindBreak truncates the last line if( !rFrameBreak.FindBreak( this, rLine, bHasToFit ) )
{ // If we're done formatting, we set nEnd to the end. // AdjustFollow might execute JoinFrame() because of this. // Else, nEnd is the end of the last line in the Master.
TextFrameIndex nOld = nEnd; // Make sure content from the last floating table anchor is not shifted to previous anchors. if (!HasNonLastSplitFlyDrawObj())
{
nEnd = rLine.GetEnd();
} if( GetFollow() )
{ if (dontJoin && nOld < nEnd)
RemoveFootnote( nOld, nEnd - nOld );
ChangeOffset( GetFollow(), nEnd ); if( !bDelta )
GetFollow()->ManipOfst( nEnd );
}
} else
{ // If we pass over lines, we must not call Join in Follows, instead we even // need to create a Follow. // We also need to do this if the whole mass of text remains in the Master, // because a hard line break could necessitate another line (without text mass)!
TextFrameIndex const nOld(nEnd);
nEnd = rLine.GetEnd(); if( GetFollow() )
{ // Another case for not joining the follow: // Text frame has no content, but a numbering. Then, do *not* join. // Example of this case: When an empty, but numbered paragraph // at the end of page is completely displaced by a fly frame. // Thus, the text frame introduced a follow by a // <SwTextFrame::SplitFrame(..)> - see below. The follow then shows // the numbering and must stay. if ( GetFollow()->GetOffset() != nEnd ||
GetFollow()->IsFieldFollow() ||
(nStrLen == TextFrameIndex(0) && GetTextNodeForParaProps()->GetNumRule()))
{
dontJoin = true;
} elseif (FindTabFrame() && nEnd > TextFrameIndex(0) &&
rLine.GetInfo().GetChar(nEnd - TextFrameIndex(1)) == CH_BREAK)
{ // We are in a table, the paragraph has a follow and the text // ends with a hard line break. Don't join the follow just // because the follow would have no content, we may still need it // for the paragraph mark.
dontJoin = true;
} // move footnotes if the follow is kept - if RemoveFootnote() is // called in next format iteration, it will be with the *new* // offset so no effect! if (dontJoin && nOld < nEnd)
{
RemoveFootnote(nOld, nEnd - nOld);
}
ChangeOffset( GetFollow(), nEnd );
SwFlyAtContentFrame* pNonLastSplitFlyDrawObj = HasNonLastSplitFlyDrawObj(); if (pNonLastSplitFlyDrawObj && !pNonLastSplitFlyDrawObj->IsWrapOnAllPages())
{ // Make sure content from the last floating table anchor is not shifted to previous // anchors, unless we're in the special "wrap on all pages" mode.
nEnd = TextFrameIndex(0);
}
if (!pTextNode->HasVisibleNumberingOrBullet())
{
bHasVisibleNumRule = false;
}
// Only split frame, if the frame contains // content or contains no content, but has a numbering. // i#84870 - No split, if text frame only contains one // as-character anchored object. if (!bLoneAsCharAnchoredObj
&& (bHasVisibleNumRule
|| (nStrLen > TextFrameIndex(0)
&& (nEnd != rLine.GetStart() || rInf.GetRest())))
)
{
SplitFrame( nEnd );
dontJoin = true;
}
} // If the remaining height changed e.g by RemoveFootnote() we need to // fill up in order to avoid oscillation. if( bDummy && pBodyFrame &&
nBodyHeight < ( IsVertical() ?
pBodyFrame->getFrameArea().Width() :
pBodyFrame->getFrameArea().Height() ) )
rLine.MakeDummyLine();
}
// In AdjustFrame() we set ourselves via Grow/Shrink // In AdjustFollow() we set our FollowFrame
//#i84870# - no shrink of text frame, if it only contains one as-character anchored object. if (nChg < 0 && !bDelta && bLoneAsCharAnchoredObj)
{
nChg = 0;
}
// Vertical Formatting: // The (rotated) repaint rectangle's x coordinate refers to the frame. // If the frame grows (or shirks) the repaint rectangle cannot simply // be rotated back after formatting, because we use the upper left point // of the frame for rotation. This point changes when growing/shrinking.
// we only declare a line as unchanged, if its main values have not // changed and it is not the last line (!paragraph end symbol!)
bUnChg = nOldHeight == pNew->Height() &&
nOldAscent == pNew->GetAscent() &&
nWidthDiff <= SLOPPY_TWIPS &&
pOldCur->GetNext();
}
// Finally we enlarge the repaint rectangle if we found an underscore // or another glyph extending beyond the line height within the line. auto nBaseAscent = pNew->GetAscent(); auto nMaxExtraAscent
= std::max({ SwTwips{ 0 }, rLine.GetInfo().GetExtraAscent() - nBaseAscent,
rLine.GetCurr()->GetExtraAscent() });
rRepaint.Top(rRepaint.Top() - nMaxExtraAscent); const_cast<SwLineLayout*>(rLine.GetCurr())->SetExtraAscent(nMaxExtraAscent);
// Due to performance reasons we set rReformat to COMPLETE_STRING in Init() // In this case we adjust rReformat if( rReformat.Len() > nStrLen )
rReformat.Len() = nStrLen;
// When inserting or removing a Space, words can be moved out of the edited // line and into the preceding line, hence the preceding line must be // formatted as well. // Optimization: If rReformat starts after the first word of the line, // this line cannot possibly influence the previous one. // ...Turns out that unfortunately it can: Text size changes + FlyFrames; // the feedback can affect multiple lines (Frames!)!
// i#46560 // FME: Yes, consider this case: "(word )" has to go to the next line // because ")" is a forbidden character at the beginning of a line although // "(word" would still fit on the previous line. Adding text right in front // of ")" would not trigger a reformatting of the previous line. Adding 1 // to the result of FindBrk() does not solve the problem in all cases, // nevertheless it should be sufficient. bool bPrev = rLine.GetPrev() &&
(FindBrk(rString, rLine.GetStart(), rReformat.Start() + TextFrameIndex(1)) // i#46560
+ TextFrameIndex(1)
>= rReformat.Start() ||
rLine.GetCurr()->IsRest() ); if( bPrev )
{ while( rLine.Prev() ) if( rLine.GetCurr()->GetLen() && !rLine.GetCurr()->IsRest() )
{ if( !rLine.GetStart() )
rLine.Top(); // So that NumDone doesn't get confused break;
}
TextFrameIndex nNew = rLine.GetStart() + rLine.GetLength(); if( nNew )
{
--nNew; if (CH_BREAK == rString[sal_Int32(nNew)])
{
++nNew;
rLine.Next();
bPrev = false;
}
}
rReformat.Len() += rReformat.Start() - nNew;
rReformat.Start() = nNew;
}
// rLine is now set to the first line which needs formatting. // The bFirst flag makes sure that Next() is not called. // The whole thing looks weird, but we need to make sure that // rLine stops at the last non-fitting line when calling IsBreakNow. bool bFirst = true; bool bFormat = true;
// The CharToLine() can also get us into the danger zone. // In that case we need to walk back until rLine is set // to the non-fitting line. Or else the mass of text is lost, // because the Ofst was set wrongly in the Follow.
// Play it safe
aFrameBreak.IsBreakNowWidAndOrp(rLine);
}
/* Meaning if the following flags are set:
Watch(End/Mid)Hyph: we need to format if we have a break at the line end/Fly, as long as MaxHyph is reached
Jump(End/Mid)Flag: the next line which has no break (line end/Fly), needs to be formatted, because we could wrap now. This might have been forbidden earlier by MaxHyph
Watch(End/Mid)Hyph: if the last formatted line got a cutoff point, but didn't have one before
Jump(End/Mid)Hyph: if a cutoff point disappears
*/ bool bJumpEndHyph = false; bool bWatchEndHyph = false; bool bJumpMidHyph = false; bool bWatchMidHyph = false;
/* Ad cancel criterion: * In order to recognize, whether a line does not fit onto the page * anymore, we need to format it. This overflow is removed again in * e.g. AdjustFollow. * Another complication: if we are the Master, we need to traverse * the lines, because it could happen that one line can overflow * from the Follow to the Master.
*/ do
{ if( bFirst )
bFirst = false; else
{ if ( bMaxHyph )
{ if ( rLine.GetCurr()->IsEndHyph() )
rLine.CntEndHyph()++; else
rLine.CntEndHyph() = 0; if ( rLine.GetCurr()->IsMidHyph() )
rLine.CntMidHyph()++; else
rLine.CntMidHyph() = 0;
} if( !rLine.Next() )
{ if( !bFormat )
{
SwLinePortion* pRest =
rLine.MakeRestPortion( rLine.GetCurr(), rLine.GetEnd() ); if( pRest )
rInf.SetRest( pRest );
}
rLine.Insert( new SwLineLayout() );
rLine.Next();
bFormat = true;
}
} if ( !bFormat && bMaxHyph &&
(bWatchEndHyph || bJumpEndHyph || bWatchMidHyph || bJumpMidHyph) )
{ if ( rLine.GetCurr()->IsEndHyph() )
{ if ( bWatchEndHyph )
bFormat = ( rLine.CntEndHyph() == rInf.MaxHyph() );
} else
{
bFormat = bJumpEndHyph;
bWatchEndHyph = false;
bJumpEndHyph = false;
} if ( rLine.GetCurr()->IsMidHyph() )
{ if ( bWatchMidHyph && !bFormat )
bFormat = ( rLine.CntEndHyph() == rInf.MaxHyph() );
} else
{
bFormat |= bJumpMidHyph;
bWatchMidHyph = false;
bJumpMidHyph = false;
}
} if( bFormat )
{ constbool bOldEndHyph = rLine.GetCurr()->IsEndHyph(); constbool bOldMidHyph = rLine.GetCurr()->IsMidHyph();
bFormat = FormatLine( rLine, bPrev ); // There can only be one bPrev ... (???)
bPrev = false; if ( bMaxHyph )
{ if ( rLine.GetCurr()->IsEndHyph() != bOldEndHyph )
{
bWatchEndHyph = !bOldEndHyph;
bJumpEndHyph = bOldEndHyph;
} if ( rLine.GetCurr()->IsMidHyph() != bOldMidHyph )
{
bWatchMidHyph = !bOldMidHyph;
bJumpMidHyph = bOldMidHyph;
}
}
}
if( !rLine.IsStop() )
{ // If we're finished formatting the text and we still // have other line objects left, these are superfluous // now because the text has gotten shorter. bool bTruncLines = false; if( rLine.GetStart() + rLine.GetLength() >= nStrLen &&
rLine.GetCurr()->GetNext() )
{
bTruncLines = true;
} elseif (GetMergedPara() && rLine.GetCurr()->GetNext())
{ // We can also have superfluous lines with redlining in case the current line is shorter // than the text length, but the total length of lines is still more than expected. // Truncate in this case as well.
TextFrameIndex nLen(0); for (const SwLineLayout* pLine = pPara; pLine; pLine = pLine->GetNext())
{
nLen += pLine->GetLen();
}
bTruncLines = nLen > nStrLen;
}
if (bTruncLines)
{
rLine.TruncLines();
rLine.SetTruncLines( true );
}
}
if( rInf.IsTest() ) return;
// FormatAdjust does not pay off at OnceMore if( bAdjust || !rLine.GetDropFormat() || !rLine.CalcOnceMore() )
{
FormatAdjust( rLine, aFrameBreak, nStrLen, rInf.IsStop() );
} if( rRepaint.HasArea() )
SetRepaint();
rLine.SetTruncLines( false ); if( nOldBottom ) // We check whether paragraphs that need scrolling can // be shrunk, so that they don't need scrolling anymore
{
rLine.Bottom();
SwTwips nNewBottom = rLine.Y(); if( nNewBottom < nOldBottom )
SetOffset_(TextFrameIndex(0));
}
}
void SwTextFrame::FormatOnceMore( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
{
OSL_ENSURE( ! IsVertical() || IsSwapped(), "A frame is not swapped in SwTextFrame::FormatOnceMore" );
if ( bIsEmpty )
{ // Empty lines do not get tortured for very long: // pPara is cleared, which is the same as: // *pPara = SwParaPortion; constbool bMustFit = pPara->IsPrepMustFit();
pPara->Truncate();
pPara->FormatReset();
public:
SwTextFrameFormatScopeGuard(OutputDevice* pOut, SwTextFrame* pFrame)
: m_pOut(pOut)
{ auto pVsh = pFrame->getRootFrame()->GetCurrShell(); if (pVsh)
{
m_pRef = &pVsh->GetRefDev();
}
if (m_pOut)
{
m_pOut->Push(vcl::PushFlags::ALL);
}
if (m_pRef)
{
m_pRef->Push(vcl::PushFlags::ALL);
}
}
~SwTextFrameFormatScopeGuard()
{ if (m_pRef)
{
m_pRef->Pop();
}
if (m_pOut)
{
m_pOut->Pop();
}
}
};
}
// We calculate the text frame's size and send a notification. // Shrink() or Grow() to adjust the frame's size to the changed required space. void SwTextFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs * )
{ // tdf#92091: SwTextFrame::Format is re-entrant, but may change VCL global state. // The previous state is saved here and restored after returning.
SwTextFrameFormatScopeGuard stSg{ pRenderContext, this };
SwRectFnSet aRectFnSet(this);
CalcAdditionalFirstLineOffset();
// The range autopilot or the BASIC interface pass us TextFrames with // a width <= 0 from time to time if( aRectFnSet.GetWidth(getFramePrintArea()) <= 0 )
{ // If MustFit is set, we shrink to the Upper's bottom edge if needed.
SwTextLineAccess aAccess( this );
if (HasNonLastSplitFlyDrawObj())
{ // Non-last part of split fly anchor: consider this empty.
nStrLen = TextFrameIndex(0);
}
if ( nStrLen || !FormatEmpty() )
{
SetEmpty( false ); // In order to not get confused by nested Formats
FormatLevel aLevel; if( 12 == FormatLevel::GetLevel() ) return;
// We could be possibly not allowed to alter the format information if( IsLocked() ) return;
// Attention: Format() could be triggered by GetFormatted() if( IsHiddenNow() )
{
tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea()); if( nPrtHeight )
{
HideHidden();
Shrink( nPrtHeight );
} else
{ // Assure that objects anchored // at paragraph resp. at/as character inside paragraph // are hidden.
HideAndShowObjects();
}
ChgThisLines(); return;
}
// We do not want to be interrupted during formatting
TextFrameLockGuard aLock(this);
// this is to ensure that the similar code in SwTextFrame::Format_ // finds the master formatted in case it's needed if (IsFollow() && IsFieldFollow())
{
SwTextFrame *pMaster = FindMaster();
assert(pMaster); if (!pMaster->HasPara())
{
pMaster->GetFormatted();
} if (!pMaster->HasPara())
{ // master could be locked because it's being formatted upstack
SAL_WARN("sw", "SwTextFrame::Format: failed to format master!");
} else
{
SwTextSizeInfo aInf( pMaster );
SwTextIter aMasterLine( pMaster, &aInf );
aMasterLine.Bottom();
SetOffset(aMasterLine.GetEnd());
}
}
if( CalcPreps() )
; // nothing // We return if already formatted, but if the TextFrame was just created // and does not have any format information elseif( !bNew && !aAccess.GetPara()->GetReformat().Len() )
{ if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
{
aAccess.GetPara()->SetPrepAdjust();
aAccess.GetPara()->SetPrep();
CalcPreps();
}
SetWidow( false );
} elseif( bSetOffset && IsFollow() )
{
SwTextFrame *pMaster = FindMaster();
OSL_ENSURE( pMaster, "SwTextFrame::Format: homeless follow" ); if( pMaster )
pMaster->Prepare( PrepareHint::FollowFollows );
SwTwips nMaxY = aRectFnSet.GetPrtBottom(*GetUpper());
if( aRectFnSet.OverStep( getFrameArea(), nMaxY ) )
{
aRectFnSet.SetLimit( *this, nMaxY );
} elseif( aRectFnSet.BottomDist( getFrameArea(), nMaxY ) < 0 )
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aRectFnSet.AddBottom( aFrm, -aRectFnSet.GetHeight(aFrm) );
}
} else
{ // bSetOffset here means that we have the "red arrow situation" if ( bSetOffset )
SetOffset_(TextFrameIndex(0));
constbool bOrphan = IsWidow(); const SwFootnoteBossFrame* pFootnoteBoss = HasFootnote() ? FindFootnoteBossFrame() : nullptr;
SwTwips nFootnoteHeight = 0; if( pFootnoteBoss )
{ const SwFootnoteContFrame* pCont = pFootnoteBoss->FindFootnoteCont();
nFootnoteHeight = pCont ? aRectFnSet.GetHeight(pCont->getFrameArea()) : 0;
} do
{
::std::vector<SwAnchoredObject *> intersectingObjs;
::std::vector<SwFrame const*> nexts; for (SwFrame const* pNext = GetNext(); pNext; pNext = pNext->GetNext())
{
nexts.push_back(pNext);
}
FormatImpl(pRenderContext, aAccess.GetPara(), intersectingObjs); if( pFootnoteBoss && nFootnoteHeight )
{ const SwFootnoteContFrame* pCont = pFootnoteBoss->FindFootnoteCont();
SwTwips nNewHeight = pCont ? aRectFnSet.GetHeight(pCont->getFrameArea()) : 0; // If we lost some footnotes, we may have more space // for our main text, so we have to format again ... if( nNewHeight < nFootnoteHeight )
{
nFootnoteHeight = nNewHeight; continue;
}
} if (!intersectingObjs.empty())
{ // assumption is that FormatImpl() only moves frames // in the next-chain to next page
SwPageFrame *const pPage(FindPageFrame());
SwTextFrame * pLastMovedAnchor(nullptr); auto lastIter(nexts.end()); for (SwAnchoredObject *const pObj : intersectingObjs)
{
SwFrame *const pAnchor(pObj->AnchorFrame());
SwPageFrame *const pAnchorPage(pAnchor->FindPageFrame()); if (pAnchorPage != pPage)
{ autoconst iter(::std::find(nexts.begin(), nexts.end(), pAnchor)); if (iter != nexts.end())
{
assert(pAnchor->IsTextFrame()); // (can't check SwOszControl::IsInProgress()?) // called in loop in FormatAnchorFrameAndItsPrevs() if (static_cast<SwTextFrame const*>(pAnchor)->IsJoinLocked() // called in loop in SwFrame::PrepareMake()
|| pAnchor->IsDeleteForbidden())
{ // when called via FormatAnchorFrameAndItsPrevs(): // don't do anything, caller will handle it
pLastMovedAnchor = nullptr; break;
}
assert(pPage->GetPhyPageNum() < pAnchorPage->GetPhyPageNum()); // how could it move backward?
if (!pLastMovedAnchor || iter < lastIter)
{
pLastMovedAnchor = static_cast<SwTextFrame *>(pAnchor);
lastIter = iter;
}
}
}
}
SwPageFrame const*const pPrevPage(static_cast<SwPageFrame const*>(pPage->GetPrev())); if (pLastMovedAnchor)
{ for (SwAnchoredObject *const pObj : intersectingObjs)
{ if (pObj->AnchorFrame() == pLastMovedAnchor)
{
SwPageFrame *const pAnchorPage(pLastMovedAnchor->FindPageFrame());
SAL_INFO("sw.layout", "SwTextFrame::Format: move anchored " << pObj << " from " << pPage->GetPhyPageNum() << " to " << pAnchorPage->GetPhyPageNum());
pObj->RegisterAtPage(*pAnchorPage); // tdf#143239 if the position remains valid, it may not be // positioned again so would remain on the wrong page!
pObj->InvalidateObjPos();
::Notify_Background(pObj->GetDrawObj(), pPage,
pObj->GetObjRect(), PrepareHint::FlyFrameLeave, false);
pObj->SetForceNotifyNewBackground(true);
}
} if (GetFollow() // this frame was split
&& (!pPrevPage // prev page is still valid
|| (!pPrevPage->IsInvalid()
&& (!pPrevPage->GetSortedObjs() || !pPrevPage->IsInvalidFly()))))
{ // this seems a bit risky...
SwLayouter::InsertMovedFwdFrame(GetTextNodeFirst()->GetDoc(),
*pLastMovedAnchor, FindPageFrame()->GetPhyPageNum() + 1);
} continue; // try again without the fly
}
} break;
} while ( pFootnoteBoss ); if( bOrphan )
{
ValidateFrame();
SetWidow( false );
}
} if( IsEmptyMaster() )
{
SwFrame* pPre = GetPrev(); if( pPre && // i#10826 It's the first, it cannot keep!
pPre->GetIndPrev() &&
pPre->GetAttrSet()->GetKeep().GetValue() )
{
pPre->InvalidatePos();
}
if (IsEmptyMasterWithSplitFly())
{ // A fly is anchored to us, reduce size, so we definitely still fit the current // page.
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aRectFnSet.SetHeight(aFrm, 0);
// the PrepMustFit should not survive a Format operation
SwParaPortion *pPara = GetPara(); if ( pPara )
pPara->SetPrepMustFit( false );
CalcBaseOfstForFly();
CalcHeightOfLastLine(); // i#11860 - Adjust spacing implementation for // object positioning - Compatibility to MS Word // tdf#117982 -- Fix cell spacing hides content // Check if the cell's content has greater size than the row height if (IsInTab() && GetUpper() && ((GetUpper()->getFramePrintArea().Height() < getFramePrintArea().Height())
|| (getFramePrintArea().Height() <= 0)))
{
SAL_INFO("sw.core", "Warn: Cell content has greater size than cell height!"); //get font size...
SwTwips aTmpHeight = getFrameArea().Height(); //...and push it into the text frame
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this); //if only bottom margin what we have: if (GetTopMargin() == 0) //set the frame to its original location
aPrt.SetTopAndHeight(0, aTmpHeight);
}
}
// bForceQuickFormat is set if GetFormatted() has been called during the // painting process. Actually I cannot imagine a situation which requires // a full formatting of the paragraph during painting, on the other hand // a full formatting can cause the invalidation of other layout frames, // e.g., if there are footnotes in this paragraph, and invalid layout // frames will not calculated during the painting. So I actually want to // avoid a formatting during painting, but since I'm a coward, I'll only // force the quick formatting in the situation of issue i29062. bool SwTextFrame::FormatQuick( bool bForceQuickFormat )
{
OSL_ENSURE( ! IsVertical() || ! IsSwapped(), "SwTextFrame::FormatQuick with swapped frame" );
int nLoopProtection = 0; do
{
TextFrameIndex nNewStart = aLine.FormatLine(nStart); if (nNewStart == nStart)
++nLoopProtection; else
nLoopProtection = 0;
nStart = nNewStart; constbool bWillEndlessInsert = nLoopProtection > 250;
SAL_WARN_IF(bWillEndlessInsert, "sw", "loop detection triggered"); if ((!bWillEndlessInsert) // Check for special case: line is invisible, // like in too thin table cell: tdf#66141
&& (aInf.IsNewLine() || (!aInf.IsStop() && nStart < nEnd)))
aLine.Insert( new SwLineLayout() );
} while( aLine.Next() );
// Last exit: the heights need to match
Point aTopLeft( getFrameArea().Pos() );
aTopLeft += getFramePrintArea().Pos(); const SwTwips nNewHeight = aLine.Y() + aLine.GetLineHeight(); const SwTwips nOldHeight = aTopLeft.Y() + getFramePrintArea().Height();
if( !bForceQuickFormat && nNewHeight != nOldHeight && !IsUndersized() )
{ // Attention: This situation can occur due to FormatLevel==12. Don't panic!
TextFrameIndex const nStrt = GetOffset();
InvalidateRange_( SwCharRange( nStrt, nEnd - nStrt) ); returnfalse;
}
if (m_pFollow && nStart != static_cast<SwTextFrame*>(m_pFollow)->GetOffset()) returnfalse; // can be caused by e.g. Orphans
// We made it!
// Set repaint
pPara->GetRepaint().Pos( aTopLeft );
pPara->GetRepaint().SSize( getFramePrintArea().SSize() );
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.