/* -*- 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;
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.