/* -*- 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 <vcl/outdev.hxx>
#include <pagefrm.hxx>
#include <rootfrm.hxx>
#include <pam.hxx>
#include <swfont.hxx>
#include <swregion.hxx>
#include <dflyobj.hxx>
#include <drawfont.hxx>
#include <flyfrm.hxx>
#include <flyfrms.hxx>
#include <fmtornt.hxx>
#include <frmatr.hxx>
#include <frmtool.hxx>
#include <ndtxt.hxx>
#include <txtfly.hxx>
#include "inftxt.hxx"
#include "porrst.hxx"
#include "txtpaint.hxx"
#include <notxtfrm.hxx>
#include <fmtcnct.hxx>
#include <svx/obj3d.hxx>
#include <editeng/txtrange.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/ulspitem.hxx>
#include <fmtsrnd.hxx>
#include <fmtanchr.hxx>
#include <frmfmt.hxx>
#include <fmtfollowtextflow.hxx>
#include <pagedesc.hxx>
#include <sortedobjs.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <IDocumentSettingAccess.hxx>
#include <formatlinebreak.hxx>
#include <svx/svdoedge.hxx>
#ifdef DBG_UTIL
#include <viewsh.hxx>
#include <doc.hxx>
#endif
using namespace ::com::sun::star;
namespace
{
// #i68520#
struct AnchoredObjOrder
{
bool mbR2L;
SwRectFn mfnRect;
AnchoredObjOrder( const bool bR2L,
SwRectFn fnRect )
: mbR2L( bR2L ),
mfnRect( fnRect )
{}
bool operator()( const SwAnchoredObject* pListedAnchoredObj,
const SwAnchoredObject* pNewAnchoredObj )
{
const SwRect& aBoundRectOfListedObj( pListedAnchoredObj->GetObjRectWithSpaces() );
const SwRect& aBoundRectOfNewObj( pNewAnchoredObj->GetObjRectWithSpaces() );
if ( ( mbR2L &&
( (aBoundRectOfListedObj.*mfnRect->fnGetRight)() ==
(aBoundRectOfNewObj.*mfnRect->fnGetRight)() ) ) ||
( !mbR2L &&
( (aBoundRectOfListedObj.*mfnRect->fnGetLeft)() ==
(aBoundRectOfNewObj.*mfnRect->fnGetLeft)() ) ) )
{
SwTwips nTopDiff =
(*mfnRect->fnYDiff)( (aBoundRectOfNewObj.*mfnRect->fnGetTop)(),
(aBoundRectOfListedObj.*mfnRect->fnGetTop)() );
if ( nTopDiff == 0 &&
( ( mbR2L &&
( (aBoundRectOfNewObj.*mfnRect->fnGetLeft)() >
(aBoundRectOfListedObj.*mfnRect->fnGetLeft)() ) ) ||
( !mbR2L &&
( (aBoundRectOfNewObj.*mfnRect->fnGetRight)() <
(aBoundRectOfListedObj.*mfnRect->fnGetRight)() ) ) ) )
{
return true;
}
else if ( nTopDiff > 0 )
{
return true;
}
}
else if ( ( mbR2L &&
( (aBoundRectOfListedObj.*mfnRect->fnGetRight)() >
(aBoundRectOfNewObj.*mfnRect->fnGetRight)() ) ) ||
( !mbR2L &&
( (aBoundRectOfListedObj.*mfnRect->fnGetLeft)() <
(aBoundRectOfNewObj.*mfnRect->fnGetLeft)() ) ) )
{
return true;
}
return false;
}
};
}
SwContourCache::SwContourCache() :
mnPointCount( 0 )
{
}
SwContourCache::~SwContourCache()
{
}
void SwContourCache::ClrObject( sal_uInt16 nPos )
{
mnPointCount -= mvItems[ nPos ].mxTextRanger->GetPointCount();
mvItems.erase(mvItems.begin() + nPos);
}
void ClrContourCache( const SdrObject *pObj )
{
if( pContourCache && pObj )
for( sal_uInt16 i = 0; i < pContourCache->GetCount(); ++i )
if( pObj == pContourCache->GetObject( i ) )
{
pContourCache->ClrObject( i );
break;
}
}
void ClrContourCache()
{
if( pContourCache )
{
pContourCache->mvItems.clear();
pContourCache->mnPointCount = 0;
}
}
// #i68520#
SwRect SwContourCache::CalcBoundRect( const SwAnchoredObject* pAnchoredObj,
const SwRect &rLine,
const SwTextFrame* pFrame,
const tools::Long nXPos,
const bool bRight )
{
SwRect aRet;
const SwFrameFormat* pFormat = pAnchoredObj->GetFrameFormat();
bool bHandleContour(pFormat->GetSurround().IsContour());
if(!bHandleContour)
{
// RotateFlyFrame3: Object has no set contour, but for rotated
// FlyFrames we can create a 'default' contour to make text
// flow around the free, non-covered
const SwFlyFreeFrame* pSwFlyFreeFrame(dynamic_cast< const SwFlyFreeFrame* >(pAnchoredObj));
if(nullptr != pSwFlyFreeFrame && pSwFlyFreeFrame->supportsAutoContour())
{
bHandleContour = true;
}
}
if( bHandleContour &&
( pAnchoredObj->DynCastFlyFrame() == nullptr ||
( static_cast<const SwFlyFrame*>(pAnchoredObj)->Lower() &&
static_cast<const SwFlyFrame*>(pAnchoredObj)->Lower()->IsNoTextFrame() ) ) )
{
aRet = pAnchoredObj->GetObjRectWithSpaces();
if( aRet.Overlaps( rLine ) )
{
if( !pContourCache )
pContourCache = new SwContourCache;
aRet = pContourCache->ContourRect(
pFormat, pAnchoredObj->GetDrawObj(), pFrame, rLine, nXPos, bRight );
}
else
aRet.Width( 0 );
}
else
{
aRet = pAnchoredObj->GetObjRectWithSpaces();
}
return aRet;
}
SwRect SwContourCache::ContourRect( const SwFormat* pFormat,
const SdrObject* pObj, const SwTextFrame* pFrame, const SwRect &rLine,
const tools::Long nXPos, const bool bRight )
{
SwRect aRet;
sal_uInt16 nPos = 0; // Search in the Cache
while( nPos < GetCount() && pObj != mvItems[ nPos ].mpSdrObj )
++nPos;
if( GetCount() == nPos ) // Not found
{
if( GetCount() == POLY_CNT )
{
mnPointCount -= mvItems.back().mxTextRanger->GetPointCount();
mvItems.pop_back();
}
::basegfx::B2DPolyPolygon aPolyPolygon;
std::optional<::basegfx::B2DPolyPolygon> pPolyPolygon;
if ( auto pVirtFlyDrawObj = dynamic_cast< const SwVirtFlyDrawObj *>( pObj ) )
{
// GetContour() causes the graphic to be loaded, which may cause
// the graphic to change its size, call ClrObject()
tools::PolyPolygon aPoly;
if( !pVirtFlyDrawObj->GetFlyFrame()->GetContour( aPoly ) )
aPoly = tools::PolyPolygon( pVirtFlyDrawObj->
GetFlyFrame()->getFrameArea().SVRect() );
aPolyPolygon.clear();
aPolyPolygon.append(aPoly.getB2DPolyPolygon());
}
else
{
if( DynCastE3dObject( pObj ) == nullptr )
{
aPolyPolygon = pObj->TakeXorPoly();
}
pPolyPolygon = pObj->TakeContour();
}
const SvxLRSpaceItem &rLRSpace = pFormat->GetLRSpace();
const SvxULSpaceItem &rULSpace = pFormat->GetULSpace();
CacheItem item {
pObj, // due to #37347 the Object must be entered only after GetContour()
std::make_unique<TextRanger>( aPolyPolygon, pPolyPolygon ? &*pPolyPolygon : nullptr, 20,
o3tl::narrowing<sal_uInt16>(rLRSpace.GetLeft()), o3tl::narrowing<sal_uInt16>(rLRSpace.GetRight()),
pFormat->GetSurround().IsOutside(), false, pFrame->IsVertical() )
};
mvItems.insert(mvItems.begin(), std::move(item));
mvItems[0].mxTextRanger->SetUpper( rULSpace.GetUpper() );
mvItems[0].mxTextRanger->SetLower( rULSpace.GetLower() );
pPolyPolygon.reset();
mnPointCount += mvItems[0].mxTextRanger->GetPointCount();
while( mnPointCount > POLY_MAX && mvItems.size() > POLY_MIN )
{
mnPointCount -= mvItems.back().mxTextRanger->GetPointCount();
mvItems.pop_back();
}
}
else if( nPos )
{
CacheItem item = std::move(mvItems[nPos]);
mvItems.erase(mvItems.begin() + nPos);
mvItems.insert(mvItems.begin(), std::move(item));
}
SwRectFnSet aRectFnSet(pFrame);
tools::Long nTmpTop = aRectFnSet.GetTop(rLine);
// fnGetBottom is top + height
tools::Long nTmpBottom = aRectFnSet.GetBottom(rLine);
Range aRange( std::min( nTmpTop, nTmpBottom ), std::max( nTmpTop, nTmpBottom ) );
std::deque<tools::Long>* pTmp = mvItems[0].mxTextRanger->GetTextRanges( aRange );
const size_t nCount = pTmp->size();
if( 0 != nCount )
{
size_t nIdx = 0;
while( nIdx < nCount && (*pTmp)[ nIdx ] < nXPos )
++nIdx;
bool bOdd = nIdx % 2;
bool bSet = true;
if( bOdd )
--nIdx; // within interval
else if( ! bRight && ( nIdx >= nCount || (*pTmp)[ nIdx ] != nXPos ) )
{
if( nIdx )
nIdx -= 2; // an interval to the left
else
bSet = false; // before the first interval
}
if( bSet && nIdx < nCount )
{
aRectFnSet.SetTopAndHeight( aRet, aRectFnSet.GetTop(rLine),
aRectFnSet.GetHeight(rLine) );
aRectFnSet.SetLeft( aRet, (*pTmp)[ nIdx ] );
aRectFnSet.SetRight( aRet, (*pTmp)[ nIdx + 1 ] + 1 );
}
}
return aRet;
}
SwTextFly::SwTextFly()
: m_pPage(nullptr)
, mpCurrAnchoredObj(nullptr)
, m_pCurrFrame(nullptr)
, m_pMaster(nullptr)
, m_nMinBottom(0)
, m_nNextTop(0)
, m_nCurrFrameNodeIndex(0)
, m_bOn(false)
, m_bTopRule(false)
, mbIgnoreCurrentFrame(false)
, mbIgnoreContour(false)
, mbIgnoreObjsInHeaderFooter(false)
{
}
SwTextFly::SwTextFly( const SwTextFrame *pFrame )
{
CtorInitTextFly( pFrame );
}
SwTextFly::SwTextFly( const SwTextFly& rTextFly )
{
m_pPage = rTextFly.m_pPage;
mpCurrAnchoredObj = rTextFly.mpCurrAnchoredObj;
m_pCurrFrame = rTextFly.m_pCurrFrame;
m_pMaster = rTextFly.m_pMaster;
if( rTextFly.mpAnchoredObjList )
{
mpAnchoredObjList.reset( new SwAnchoredObjList( *(rTextFly.mpAnchoredObjList) ) );
}
m_bOn = rTextFly.m_bOn;
m_bTopRule = rTextFly.m_bTopRule;
m_nMinBottom = rTextFly.m_nMinBottom;
m_nNextTop = rTextFly.m_nNextTop;
m_nCurrFrameNodeIndex = rTextFly.m_nCurrFrameNodeIndex;
mbIgnoreCurrentFrame = rTextFly.mbIgnoreCurrentFrame;
mbIgnoreContour = rTextFly.mbIgnoreContour;
mbIgnoreObjsInHeaderFooter = rTextFly.mbIgnoreObjsInHeaderFooter;
}
SwTextFly::~SwTextFly()
{
}
void SwTextFly::CtorInitTextFly( const SwTextFrame *pFrame )
{
mbIgnoreCurrentFrame = false;
mbIgnoreContour = false;
mbIgnoreObjsInHeaderFooter = false;
m_pPage = pFrame->FindPageFrame();
const SwFlyFrame* pTmp = pFrame->FindFlyFrame();
// #i68520#
mpCurrAnchoredObj = pTmp;
m_pCurrFrame = pFrame;
m_pMaster = m_pCurrFrame->IsFollow() ? nullptr : m_pCurrFrame;
// If we're not overlapped by a frame or if a FlyCollection does not exist
// at all, we switch off forever.
// It could be, however, that a line is added while formatting, that
// extends into a frame.
// That's why we do not optimize for: bOn = pSortedFlys && IsAnyFrame();
m_bOn = m_pPage->GetSortedObjs() != nullptr;
m_bTopRule = true;
m_nMinBottom = 0;
m_nNextTop = 0;
m_nCurrFrameNodeIndex = NODE_OFFSET_MAX;
}
SwRect SwTextFly::GetFrame_( const SwRect &rRect ) const
{
SwRect aRet;
if( ForEach( rRect, &aRet, true ) )
{
SwRectFnSet aRectFnSet(m_pCurrFrame);
aRectFnSet.SetTop( aRet, aRectFnSet.GetTop(rRect) );
// Do not always adapt the bottom
const SwTwips nRetBottom = aRectFnSet.GetBottom(aRet);
const SwTwips nRectBottom = aRectFnSet.GetBottom(rRect);
if ( aRectFnSet.YDiff( nRetBottom, nRectBottom ) > 0 ||
aRectFnSet.GetHeight(aRet) < 0 )
aRectFnSet.SetBottom( aRet, nRectBottom );
}
return aRet;
}
bool SwTextFly::IsAnyFrame() const
{
SwSwapIfSwapped swap(const_cast<SwTextFrame *>(m_pCurrFrame));
OSL_ENSURE( m_bOn, "IsAnyFrame: Why?" );
SwRect aRect(m_pCurrFrame->getFrameArea().Pos() + m_pCurrFrame->getFramePrintArea().Pos(),
m_pCurrFrame->getFramePrintArea().SSize());
return ForEach( aRect, nullptr, false );
}
bool SwTextFly::IsAnyObj( const SwRect &rRect ) const
{
OSL_ENSURE( m_bOn, "SwTextFly::IsAnyObj: Who's knocking?" );
SwRect aRect( rRect );
if ( aRect.IsEmpty() )
{
aRect = SwRect(m_pCurrFrame->getFrameArea().Pos() + m_pCurrFrame->getFramePrintArea().Pos(),
m_pCurrFrame->getFramePrintArea().SSize());
SwTwips nLower = m_pCurrFrame->GetLowerMarginForFlyIntersect();
if (nLower > 0)
{
aRect.AddBottom(nLower);
}
}
const SwSortedObjs *pSorted = m_pPage->GetSortedObjs();
if( pSorted ) // bOn actually makes sure that we have objects on the side,
// but who knows who deleted something in the meantime?
{
for ( size_t i = 0; i < pSorted->size(); ++i )
{
const SwAnchoredObject* pObj = (*pSorted)[i];
const SwRect aBound( pObj->GetObjRectWithSpaces() );
// Optimization
if( pObj->GetObjRect().Left() > aRect.Right() )
continue;
// #i68520#
if( mpCurrAnchoredObj != pObj && aBound.Overlaps( aRect ) )
return true;
}
}
return false;
}
const SwTextFrame* SwTextFly::GetMaster_()
{
m_pMaster = m_pCurrFrame;
while (m_pMaster && m_pMaster->IsFollow())
m_pMaster = m_pMaster->FindMaster();
return m_pMaster;
}
void SwTextFly::DrawTextOpaque( SwDrawTextInfo &rInf )
{
SwSaveClip aClipSave( rInf.GetpOut() );
SwRect aRect( rInf.GetPos(), rInf.GetSize() );
if( rInf.GetSpace() )
{
TextFrameIndex const nTmpLen = TextFrameIndex(COMPLETE_STRING) == rInf.GetLen()
? TextFrameIndex(rInf.GetText().getLength())
: rInf.GetLen();
if( rInf.GetSpace() > 0 )
{
sal_Int32 nSpaceCnt = 0;
const TextFrameIndex nEndPos = rInf.GetIdx() + nTmpLen;
for (TextFrameIndex nPos = rInf.GetIdx(); nPos < nEndPos; ++nPos)
{
if (CH_BLANK == rInf.GetText()[sal_Int32(nPos)])
++nSpaceCnt;
}
if( nSpaceCnt )
aRect.Width( aRect.Width() + nSpaceCnt * rInf.GetSpace() );
}
else
aRect.Width( aRect.Width() - sal_Int32(nTmpLen) * rInf.GetSpace() );
}
if( aClipSave.IsOn() && rInf.GetOut().IsClipRegion() )
{
SwRect aClipRect( rInf.GetOut().GetClipRegion().GetBoundRect() );
aRect.Intersection( aClipRect );
}
SwRegionRects aRegion( aRect );
bool bOpaque = false;
// #i68520#
const sal_uInt32 nCurrOrd = mpCurrAnchoredObj
? mpCurrAnchoredObj->GetDrawObj()->GetOrdNum()
: SAL_MAX_UINT32;
OSL_ENSURE( !m_bTopRule, "DrawTextOpaque: Wrong TopRule" );
// #i68520#
const SwAnchoredObjList::size_type nCount( m_bOn ? GetAnchoredObjList().size() : 0 );
if (nCount > 0)
{
const SdrLayerID nHellId = m_pPage->getRootFrame()->GetCurrShell()->getIDocumentDrawModelAccess().GetHellId();
for( SwAnchoredObjList::size_type i = 0; i < nCount; ++i )
{
// #i68520#
const SwAnchoredObject* pTmpAnchoredObj = (*mpAnchoredObjList)[i];
const SwFlyFrame* pFly = pTmpAnchoredObj->DynCastFlyFrame();
if( pFly && mpCurrAnchoredObj != pTmpAnchoredObj )
{
// #i68520#
if( aRegion.GetOrigin().Overlaps( pFly->getFrameArea() ) )
{
const SwFrameFormat *pFormat = pFly->GetFormat();
const SwFormatSurround &rSur = pFormat->GetSurround();
const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
// Only the ones who are opaque and more to the top
if( ! pFly->IsBackgroundTransparent() &&
css::text::WrapTextMode_THROUGH == rSur.GetSurround() &&
( !rSur.IsAnchorOnly() ||
// #i68520#
GetMaster() == pFly->GetAnchorFrame() ||
((RndStdIds::FLY_AT_PARA != rAnchor.GetAnchorId()) &&
(RndStdIds::FLY_AT_CHAR != rAnchor.GetAnchorId())
)
) &&
// #i68520#
pTmpAnchoredObj->GetDrawObj()->GetLayer() != nHellId &&
nCurrOrd < pTmpAnchoredObj->GetDrawObj()->GetOrdNum()
)
{
// Except for the content is transparent
const SwNoTextFrame *pNoText =
pFly->Lower() && pFly->Lower()->IsNoTextFrame()
? static_cast<const SwNoTextFrame*>(pFly->Lower())
: nullptr;
if ( !pNoText ||
(!pNoText->IsTransparent() && !rSur.IsContour()) )
{
bOpaque = true;
aRegion -= pFly->getFrameArea();
}
}
}
}
}
}
Point aPos( rInf.GetPos().X(), rInf.GetPos().Y() + rInf.GetAscent() );
const Point aOldPos(rInf.GetPos());
rInf.SetPos( aPos );
if( !bOpaque )
{
if( rInf.GetKern() )
rInf.GetFont()->DrawStretchText_( rInf );
else
rInf.GetFont()->DrawText_( rInf );
rInf.SetPos(aOldPos);
return;
}
else if( !aRegion.empty() )
{
// What a huge effort ...
SwSaveClip aClipVout( rInf.GetpOut() );
for( size_t i = 0; i < aRegion.size(); ++i )
{
SwRect &rRect = aRegion[i];
if( rRect != aRegion.GetOrigin() )
aClipVout.ChgClip( rRect );
if( rInf.GetKern() )
rInf.GetFont()->DrawStretchText_( rInf );
else
rInf.GetFont()->DrawText_( rInf );
}
}
rInf.SetPos(aOldPos);
}
void SwTextFly::DrawFlyRect( OutputDevice* pOut, const SwRect &rRect )
{
SwRegionRects aRegion( rRect );
OSL_ENSURE( !m_bTopRule, "DrawFlyRect: Wrong TopRule" );
// #i68520#
const SwAnchoredObjList::size_type nCount( m_bOn ? GetAnchoredObjList().size() : 0 );
if (nCount > 0)
{
const SdrLayerID nHellId = m_pPage->getRootFrame()->GetCurrShell()->getIDocumentDrawModelAccess().GetHellId();
for( SwAnchoredObjList::size_type i = 0; i < nCount; ++i )
{
// #i68520#
const SwAnchoredObject* pAnchoredObjTmp = (*mpAnchoredObjList)[i];
if (mpCurrAnchoredObj == pAnchoredObjTmp)
continue;
// #i68520#
const SwFlyFrame* pFly = pAnchoredObjTmp->DynCastFlyFrame();
if (pFly)
{
// #i68520#
const SwFormatSurround& rSur = pAnchoredObjTmp->GetFrameFormat()->GetSurround();
// OD 24.01.2003 #106593# - correct clipping of fly frame area.
// Consider that fly frame background/shadow can be transparent
// and <SwAlignRect(..)> fly frame area
// #i47804# - consider transparent graphics
// and OLE objects.
bool bClipFlyArea =
( ( css::text::WrapTextMode_THROUGH == rSur.GetSurround() )
// #i68520#
? (pAnchoredObjTmp->GetDrawObj()->GetLayer() != nHellId)
: !rSur.IsContour() ) &&
!pFly->IsBackgroundTransparent() &&
( !pFly->Lower() ||
!pFly->Lower()->IsNoTextFrame() ||
!static_cast<const SwNoTextFrame*>(pFly->Lower())->IsTransparent() );
if ( bClipFlyArea )
{
// #i68520#
SwRect aFly( pAnchoredObjTmp->GetObjRect() );
// OD 24.01.2003 #106593#
::SwAlignRect( aFly, m_pPage->getRootFrame()->GetCurrShell(), pOut );
if( !aFly.IsEmpty() )
aRegion -= aFly;
}
}
}
}
for( size_t i = 0; i < aRegion.size(); ++i )
{
pOut->DrawRect( aRegion[i].SVRect() );
}
}
/**
* #i26945# - change first parameter
* Now it's the <SwAnchoredObject> instance of the floating screen object
*/
bool SwTextFly::GetTop( const SwAnchoredObject* _pAnchoredObj,
const bool bInFootnote,
const bool bInFooterOrHeader )
{
// #i68520#
// <mpCurrAnchoredObj> is set, if <m_pCurrFrame> is inside a fly frame
if( _pAnchoredObj != mpCurrAnchoredObj )
{
// #i26945#
const SdrObject* pNew = _pAnchoredObj->GetDrawObj();
// #102344# Ignore connectors which have one or more connections
if (const SdrEdgeObj* pEdgeObj = dynamic_cast<const SdrEdgeObj*>(pNew))
{
if (pEdgeObj->GetConnectedNode(true) || pEdgeObj->GetConnectedNode(false))
{
return false;
}
}
if( ( bInFootnote || bInFooterOrHeader ) && m_bTopRule )
{
// #i26945#
const SwFrameFormat* pFrameFormat = _pAnchoredObj->GetFrameFormat();
const SwFormatAnchor& rNewA = pFrameFormat->GetAnchor();
if (RndStdIds::FLY_AT_PAGE == rNewA.GetAnchorId())
{
if ( bInFootnote )
return false;
if ( bInFooterOrHeader )
{
const SwFormatVertOrient& aVert(pFrameFormat->GetVertOrient());
bool bVertPrt = aVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA ||
aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA;
if( bVertPrt )
return false;
}
}
}
// #i68520#
// bEvade: consider pNew, if we are not inside a fly
// consider pNew, if pNew is lower of <mpCurrAnchoredObj>
bool bEvade = !mpCurrAnchoredObj ||
Is_Lower_Of( mpCurrAnchoredObj->DynCastFlyFrame(), pNew);
auto pFly = _pAnchoredObj->DynCastFlyFrame();
if (pFly && pFly->IsFlySplitAllowed())
{
// Check if _pAnchoredObj is a split fly inside an other split fly. Always collect such
// flys, otherwise the inner anchor text will overlap with the inner fly.
SwFrame* pFlyAnchor = const_cast<SwAnchoredObject*>(_pAnchoredObj)
->GetAnchorFrameContainingAnchPos();
if (pFlyAnchor && pFlyAnchor->IsInFly())
{
auto pOuterFly = pFlyAnchor->FindFlyFrame();
if (pOuterFly && pOuterFly->IsFlySplitAllowed())
{
return true;
}
}
}
if ( !bEvade )
{
// We are currently inside a fly frame and pNew is not
// inside this fly frame. We can do some more checks if
// we have to consider pNew.
// If bTopRule is not set, we ignore the frame types.
// We directly check the z-order
if ( !m_bTopRule )
bEvade = true;
else
{
// Within chained Flys we only avoid Lower
// #i68520#
const SwFrameFormat* pCurObjFormat = mpCurrAnchoredObj->GetFrameFormat();
const SwFormatChain& rChain = pCurObjFormat->GetChain();
if ( !rChain.GetPrev() && !rChain.GetNext() )
{
// #i26945#
const SwFormatAnchor& rNewA = _pAnchoredObj->GetFrameFormat()->GetAnchor();
// #i68520#
const SwFormatAnchor& rCurrA = pCurObjFormat->GetAnchor();
// If <mpCurrAnchoredObj> is anchored as character, its content
// does not wrap around pNew
if (RndStdIds::FLY_AS_CHAR == rCurrA.GetAnchorId())
return false;
// If pNew is anchored to page and <mpCurrAnchoredObj is not anchored
// to page, the content of <mpCurrAnchoredObj> does not wrap around pNew
// If both pNew and <mpCurrAnchoredObj> are anchored to page, we can do
// some more checks
if (RndStdIds::FLY_AT_PAGE == rNewA.GetAnchorId())
{
if (RndStdIds::FLY_AT_PAGE == rCurrA.GetAnchorId())
{
bEvade = true;
}
else
return false;
}
else if (RndStdIds::FLY_AT_PAGE == rCurrA.GetAnchorId())
return false; // Page anchored ones only avoid page anchored ones
else if (RndStdIds::FLY_AT_FLY == rNewA.GetAnchorId())
bEvade = true; // Non-page anchored ones avoid frame anchored ones
else if( RndStdIds::FLY_AT_FLY == rCurrA.GetAnchorId() )
return false; // Frame anchored ones do not avoid paragraph anchored ones
// #i57062#
// In order to avoid loop situation, it's decided to adjust
// the wrapping behaviour of content of at-paragraph/at-character
// anchored objects to one in the page header/footer and
// the document body --> content of at-paragraph/at-character
// anchored objects doesn't wrap around each other.
else
return false;
}
}
// But: we never avoid a subordinate one and additionally we only avoid when overlapping.
// #i68520#
bEvade &= ( mpCurrAnchoredObj->GetDrawObj()->GetOrdNum() < pNew->GetOrdNum() );
if( bEvade )
{
// #i68520#
const SwRect& aTmp( _pAnchoredObj->GetObjRectWithSpaces() );
if ( !aTmp.Overlaps( mpCurrAnchoredObj->GetObjRectWithSpaces() ) )
bEvade = false;
}
}
if ( bEvade )
{
// #i26945#
if (const SwFrameFormat* pAnchoredObjFormat = _pAnchoredObj->GetFrameFormat())
{
const SwFormatAnchor& rNewA = pAnchoredObjFormat->GetAnchor();
OSL_ENSURE(RndStdIds::FLY_AS_CHAR != rNewA.GetAnchorId(),
"Don't call GetTop with a FlyInContentFrame");
if (RndStdIds::FLY_AT_PAGE == rNewA.GetAnchorId())
return true; // We always avoid page anchored ones
// If Flys anchored at paragraph are caught in a FlyCnt, then
// their influence ends at the borders of the FlyCnt!
// If we are currently formatting the text of the FlyCnt, then
// it has to get out of the way of the Frame anchored at paragraph!
// m_pCurrFrame is the anchor of pNew?
// #i26945#
const SwFrame* pTmp = _pAnchoredObj->GetAnchorFrame();
if (pTmp == m_pCurrFrame)
return true;
if (pTmp->IsTextFrame() && (pTmp->IsInFly() || pTmp->IsInFootnote()))
{
// #i26945#
Point aPos = _pAnchoredObj->GetObjRect().Pos();
pTmp = GetVirtualUpper(pTmp, aPos);
}
// #i26945#
// If <pTmp> is a text frame inside a table, take the upper
// of the anchor frame, which contains the anchor position.
else if (pTmp->IsTextFrame() && pTmp->IsInTab())
{
pTmp = const_cast<SwAnchoredObject*>(_pAnchoredObj)
->GetAnchorFrameContainingAnchPos()
->GetUpper();
}
// #i28701# - consider all objects in same context,
// if wrapping style is considered on object positioning.
// Thus, text will wrap around negative positioned objects.
// #i3317# - remove condition on checking,
// if wrappings style is considered on object positioning.
// Thus, text is wrapping around negative positioned objects.
// #i35640# - no consideration of negative
// positioned objects, if wrapping style isn't considered on
// object position and former text wrapping is applied.
// This condition is typically for documents imported from the
// OpenOffice.org file format.
const IDocumentSettingAccess* pIDSA
= &m_pCurrFrame->GetDoc().getIDocumentSettingAccess();
if ((pIDSA->get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION)
|| !pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING))
&& ::FindContext(pTmp, SwFrameType::None)
== ::FindContext(m_pCurrFrame, SwFrameType::None))
{
return true;
}
const SwFrame* pHeader = nullptr;
if (m_pCurrFrame->GetNext() != pTmp
&& (IsFrameInSameContext(pTmp, m_pCurrFrame) ||
// #i13832#, #i24135# wrap around objects in page header
(!pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING)
&& nullptr != (pHeader = pTmp->FindFooterOrHeader())
&& m_pCurrFrame->IsInDocBody())))
{
if (pHeader || RndStdIds::FLY_AT_FLY == rNewA.GetAnchorId())
return true;
// Compare indices:
// The Index of the other is retrieved from the anchor attr.
SwNodeOffset nTmpIndex = rNewA.GetAnchorNode()->GetIndex();
// Now check whether the current paragraph is before the anchor
// of the displaced object in the text, then we don't have to
// get out of its way.
// If possible determine Index via SwFormatAnchor because
// otherwise it's quite expensive.
if (NODE_OFFSET_MAX == m_nCurrFrameNodeIndex)
m_nCurrFrameNodeIndex = m_pCurrFrame->GetTextNodeFirst()->GetIndex();
if (FrameContainsNode(*m_pCurrFrame, nTmpIndex)
|| nTmpIndex < m_nCurrFrameNodeIndex)
return true;
}
}
}
}
return false;
}
SwRect SwTextFly::GetFrameArea() const
{
// i#28701 - consider complete frame area for new text wrapping
SwRect aRect;
if (m_pCurrFrame->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING))
{
aRect = m_pCurrFrame->getFramePrintArea();
aRect += m_pCurrFrame->getFrameArea().Pos();
}
else
{
aRect = m_pCurrFrame->getFrameArea();
}
return aRect;
}
// #i68520#
SwAnchoredObjList& SwTextFly::InitAnchoredObjList()
{
OSL_ENSURE( m_pCurrFrame, "InitFlyList: No Frame, no FlyList" );
// #i68520#
OSL_ENSURE( !mpAnchoredObjList, "InitFlyList: FlyList already initialized" );
SwSwapIfSwapped swap(const_cast<SwTextFrame *>(m_pCurrFrame));
const SwSortedObjs *pSorted = m_pPage->GetSortedObjs();
const size_t nCount = pSorted ? pSorted->size() : 0;
// --> #108724# Page header/footer content doesn't have to wrap around
// floating screen objects
// which was added simply to be compatible with MS Office.
// MSO still allows text to wrap around in-table-flies in headers/footers/footnotes
const bool bFooterHeader = nullptr != m_pCurrFrame->FindFooterOrHeader();
const IDocumentSettingAccess* pIDSA = &m_pCurrFrame->GetDoc().getIDocumentSettingAccess();
// #i40155# - check, if frame is marked not to wrap
const bool bAllowCompatWrap = m_pCurrFrame->IsInTab() && (bFooterHeader || m_pCurrFrame->IsInFootnote());
const bool bWrapAllowed = ( pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) ||
bAllowCompatWrap ||
(!m_pCurrFrame->IsInFootnote() && !bFooterHeader));
m_bOn = false;
// #i68520#
mpAnchoredObjList.reset(new SwAnchoredObjList);
if( nCount && bWrapAllowed )
{
SwRect const aRect(GetFrameArea());
// Make ourselves a little smaller than we are,
// so that 1-Twip-overlappings are ignored (#49532)
SwRectFnSet aRectFnSet(m_pCurrFrame);
const tools::Long nRight = aRectFnSet.GetRight(aRect) - 1;
const tools::Long nLeft = aRectFnSet.GetLeft(aRect) + 1;
const bool bR2L = m_pCurrFrame->IsRightToLeft();
const IDocumentDrawModelAccess& rIDDMA = m_pCurrFrame->GetDoc().getIDocumentDrawModelAccess();
for( size_t i = 0; i < nCount; ++i )
{
// #i68520#
// do not consider hidden objects
// check, if object has to be considered for text wrap
// #118809# - If requested, do not consider
// objects in page header|footer for text frames not in page
// header|footer. This is requested for the calculation of
// the base offset for objects <SwTextFrame::CalcBaseOfstForFly()>
// #i20505# Do not consider oversized objects
SwAnchoredObject* pAnchoredObj = (*pSorted)[ i ];
assert(pAnchoredObj);
if ( !pAnchoredObj ||
!rIDDMA.IsVisibleLayerId( pAnchoredObj->GetDrawObj()->GetLayer() ) ||
!pAnchoredObj->ConsiderForTextWrap() ||
( mbIgnoreObjsInHeaderFooter && !bFooterHeader &&
pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() ) ||
( bAllowCompatWrap && !pAnchoredObj->GetFrameFormat()->GetFollowTextFlow().GetValue() )
)
{
continue;
}
const SwRect aBound( pAnchoredObj->GetObjRectWithSpaces() );
if ( nRight < aRectFnSet.GetLeft(aBound) ||
aRectFnSet.YDiff( aRectFnSet.GetTop(aRect),
aRectFnSet.GetBottom(aBound) ) > 0 ||
nLeft > aRectFnSet.GetRight(aBound) ||
aRectFnSet.GetHeight(aBound) >
2 * aRectFnSet.GetHeight(m_pPage->getFrameArea()) )
{
continue;
}
// #i26945# - pass <pAnchoredObj> to method
// <GetTop(..)> instead of only the <SdrObject> instance of the
// anchored object
if (GetTop(pAnchoredObj, m_pCurrFrame->IsInFootnote(), bFooterHeader))
{
// OD 11.03.2003 #107862# - adjust insert position:
// overlapping objects should be sorted from left to right and
// inside left to right sorting from top to bottom.
// If objects on the same position are found, they are sorted
// on its width.
// #i68520#
{
SwAnchoredObjList::iterator aInsPosIter =
std::lower_bound( mpAnchoredObjList->begin(),
mpAnchoredObjList->end(),
pAnchoredObj,
AnchoredObjOrder( bR2L, aRectFnSet.FnRect() ) );
mpAnchoredObjList->insert( aInsPosIter, pAnchoredObj );
}
const SwFrameFormat* pObjFormat = pAnchoredObj->GetFrameFormat();
const SwFormatSurround& rFlyFormat = pObjFormat->GetSurround();
// #i68520#
if ( rFlyFormat.IsAnchorOnly() &&
pAnchoredObj->GetAnchorFrame() == GetMaster() )
{
const SwFormatVertOrient &rTmpFormat = pObjFormat->GetVertOrient();
if( text::VertOrientation::BOTTOM != rTmpFormat.GetVertOrient() )
m_nMinBottom = ( aRectFnSet.IsVert() && m_nMinBottom ) ?
std::min( m_nMinBottom, aBound.Left() ) :
std::max( m_nMinBottom, aRectFnSet.GetBottom(aBound) );
}
m_bOn = true;
}
}
if( m_nMinBottom )
{
SwTwips nMax = aRectFnSet.GetPrtBottom(*m_pCurrFrame->GetUpper());
if( aRectFnSet.YDiff( m_nMinBottom, nMax ) > 0 )
m_nMinBottom = nMax;
}
}
// #i68520#
return *mpAnchoredObjList;
}
SwTwips SwTextFly::CalcMinBottom() const
{
SwTwips nRet = 0;
const SwContentFrame *pLclMaster = GetMaster();
OSL_ENSURE(pLclMaster, "SwTextFly without master");
const SwSortedObjs *pDrawObj = pLclMaster ? pLclMaster->GetDrawObjs() : nullptr;
const size_t nCount = pDrawObj ? pDrawObj->size() : 0;
if( nCount )
{
SwTwips nEndOfFrame = m_pCurrFrame->getFrameArea().Bottom();
for( size_t i = 0; i < nCount; ++i )
{
SwAnchoredObject* pAnchoredObj = (*pDrawObj)[ i ];
const SwFrameFormat* pObjFormat = pAnchoredObj->GetFrameFormat();
const SwFormatSurround& rFlyFormat = pObjFormat->GetSurround();
if( rFlyFormat.IsAnchorOnly() )
{
const SwFormatVertOrient &rTmpFormat = pObjFormat->GetVertOrient();
if( text::VertOrientation::BOTTOM != rTmpFormat.GetVertOrient() )
{
const SwRect& aBound( pAnchoredObj->GetObjRectWithSpaces() );
if( aBound.Top() < nEndOfFrame )
nRet = std::max( nRet, SwTwips(aBound.Bottom()) );
}
}
}
SwTwips nMax = m_pCurrFrame->GetUpper()->getFrameArea().Top() +
m_pCurrFrame->GetUpper()->getFramePrintArea().Bottom();
if( nRet > nMax )
nRet = nMax;
}
return nRet;
}
SwTwips SwTextFly::GetMaxBottom(const SwBreakPortion& rPortion, const SwTextFormatInfo& rInfo) const
{
// Note that m_pCurrFrame is already swapped at this stage, so it's correct to bypass
// SwRectFnSet here.
SwTwips nRet = 0;
size_t nCount(m_bOn ? GetAnchoredObjList().size() : 0);
// Get the horizontal position of the break portion in absolute twips. The frame area is in
// absolute twips, the frame's print area is relative to the frame area. Finally the portion's
// position is relative to the frame's print area.
SwTwips nX = rInfo.X();
nX += m_pCurrFrame->getFrameArea().Left();
nX += m_pCurrFrame->getFramePrintArea().Left();
for (size_t i = 0; i < nCount; ++i)
{
const SwAnchoredObject* pAnchoredObj = (*mpAnchoredObjList)[i];
if (pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader())
{
// Anchored in the header or footer, ignore it for clearing break purposes.
continue;
}
const SwFormatSurround& rSurround = pAnchoredObj->GetFrameFormat()->GetSurround();
if (rSurround.GetValue() == text::WrapTextMode_THROUGH)
{
// Wrap through has no influence on clearing breaks.
continue;
}
SwRect aRect(pAnchoredObj->GetObjRectWithSpaces());
if (m_pCurrFrame->IsVertical())
{
m_pCurrFrame->SwitchVerticalToHorizontal(aRect);
}
if (rPortion.GetClear() == SwLineBreakClear::LEFT)
{
if (nX < aRect.Left())
{
// Want to jump down to the first line that's unblocked on the left. This object is
// on the right of the break, ignore it.
continue;
}
}
if (rPortion.GetClear() == SwLineBreakClear::RIGHT)
{
if (nX > aRect.Right())
{
// Want to jump down to the first line that's unblocked on the right. This object is
// on the left of the break, ignore it.
continue;
}
}
SwTwips nBottom = aRect.Top() + aRect.Height();
if (nBottom > nRet)
{
nRet = nBottom;
}
}
return nRet;
}
bool SwTextFly::ForEach( const SwRect &rRect, SwRect* pRect, bool bAvoid ) const
{
SwSwapIfSwapped swap(const_cast<SwTextFrame *>(m_pCurrFrame));
// Optimization
SwRectFnSet aRectFnSet(m_pCurrFrame);
// tdf#127235 stop if the area is larger than the page
if( aRectFnSet.GetHeight(m_pPage->getFrameArea()) < aRectFnSet.GetHeight(rRect))
{
// get the doc model description
const SwPageDesc* pPageDesc = m_pPage->GetPageDesc();
// if there is no next page style or it is the same as the current
// => stop trying to place the frame (it would end in an infinite loop)
if( pPageDesc &&
( !pPageDesc->GetFollow() || pPageDesc->GetFollow() == pPageDesc) )
{
return false;
}
}
bool bRet = false;
// #i68520#
const SwAnchoredObjList::size_type nCount( m_bOn ? GetAnchoredObjList().size() : 0 );
if (nCount > 0)
{
for( SwAnchoredObjList::size_type i = 0; i < nCount; ++i )
{
// #i68520#
const SwAnchoredObject* pAnchoredObj = (*mpAnchoredObjList)[i];
SwRect aRect( pAnchoredObj->GetObjRectWithSpaces() );
if( aRectFnSet.GetLeft(aRect) > aRectFnSet.GetRight(rRect) )
break;
// #i68520#
if ( mpCurrAnchoredObj != pAnchoredObj && aRect.Overlaps( rRect ) )
{
// #i68520#
const SwFormat* pFormat(pAnchoredObj->GetFrameFormat());
const SwFormatSurround &rSur = pFormat->GetSurround();
if( bAvoid )
{
// If the text flows below, it has no influence on
// formatting. In LineIter::DrawText() it is "just"
// necessary to cleverly set the ClippingRegions
const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
if( ( css::text::WrapTextMode_THROUGH == rSur.GetSurround() &&
( !rSur.IsAnchorOnly() ||
// #i68520#
GetMaster() == pAnchoredObj->GetAnchorFrame() ||
((RndStdIds::FLY_AT_PARA != rAnchor.GetAnchorId()) &&
(RndStdIds::FLY_AT_CHAR != rAnchor.GetAnchorId())) ) )
|| aRect.Top() == FAR_AWAY )
continue;
}
// #i58642#
// Compare <GetMaster()> instead of <m_pCurrFrame> with the
// anchor frame of the anchored object, because a follow frame
// has to ignore the anchored objects of its master frame.
// Note: Anchored objects are always registered at the master
// frame, exception are as-character anchored objects,
// but these aren't handled here.
// #i68520#
if ( mbIgnoreCurrentFrame &&
GetMaster() == pAnchoredObj->GetAnchorFrame() )
continue;
if( pRect )
{
// #i68520#
SwRect aFly = AnchoredObjToRect( pAnchoredObj, rRect );
if( aFly.IsEmpty() || !aFly.Overlaps( rRect ) )
continue;
if( !bRet || (
(!m_pCurrFrame->IsRightToLeft() &&
( aRectFnSet.GetLeft(aFly) <
aRectFnSet.GetLeft(*pRect) ) ) ||
(m_pCurrFrame->IsRightToLeft() &&
( aRectFnSet.GetRight(aFly) >
aRectFnSet.GetRight(*pRect) ) ) ) )
*pRect = aFly;
if( rSur.IsContour() )
{
bRet = true;
continue;
}
}
bRet = true;
break;
}
}
}
return bRet;
}
// #i68520#
SwAnchoredObjList::size_type SwTextFly::GetPos( const SwAnchoredObject* pAnchoredObj ) const
{
SwAnchoredObjList::size_type nCount = GetAnchoredObjList().size();
SwAnchoredObjList::size_type nRet = 0;
while ( nRet < nCount && pAnchoredObj != (*mpAnchoredObjList)[ nRet ] )
++nRet;
return nRet;
}
// #i68520#
void SwTextFly::CalcRightMargin( SwRect &rFly,
SwAnchoredObjList::size_type nFlyPos,
const SwRect &rLine ) const
{
// Usually the right margin is the right margin of the Printarea
OSL_ENSURE( !m_pCurrFrame->IsVertical() || !m_pCurrFrame->IsSwapped(),
"SwTextFly::CalcRightMargin with swapped frame" );
SwRectFnSet aRectFnSet(m_pCurrFrame);
// #118796# - correct determination of right of printing area
SwTwips nRight = aRectFnSet.GetPrtRight(*m_pCurrFrame);
SwTwips nFlyRight = aRectFnSet.GetRight(rFly);
SwRect aLine( rLine );
aRectFnSet.SetRight( aLine, nRight );
aRectFnSet.SetLeft( aLine, aRectFnSet.GetLeft(rFly) );
// It is possible that there is another object that is _above_ us
// and protrudes into the same line.
// Flys with run-through are invisible for those below, i.e., they
// are ignored for computing the margins of other Flys.
// 3301: pNext->getFrameArea().Overlaps( rLine ) is necessary
// #i68520#
css::text::WrapTextMode eSurroundForTextWrap;
bool bStop = false;
// #i68520#
SwAnchoredObjList::size_type nPos = 0;
// #i68520#
while( nPos < mpAnchoredObjList->size() && !bStop )
{
if( nPos == nFlyPos )
{
++nPos;
continue;
}
// #i68520#
const SwAnchoredObject* pNext = (*mpAnchoredObjList)[ nPos++ ];
if ( pNext == mpCurrAnchoredObj )
continue;
eSurroundForTextWrap = GetSurroundForTextWrap( pNext );
if( css::text::WrapTextMode_THROUGH == eSurroundForTextWrap )
continue;
const SwRect aTmp( SwContourCache::CalcBoundRect
( pNext, aLine, m_pCurrFrame, nFlyRight, true ) );
SwTwips nTmpRight = aRectFnSet.GetRight(aTmp);
// optimization:
// Record in nNextTop at which Y-position frame related changes are
// likely. This is so that, despite only looking at frames in the
// current line height, for frames without wrap the line height is
// incremented so that with a single line the lower border of the frame
// (or possibly the upper border of another frame) is reached.
// Especially in HTML documents there are often (dummy) paragraphs in
// 2 pt font, and they used to only evade big frames after huge numbers
// of empty lines.
const tools::Long nTmpTop = aRectFnSet.GetTop(aTmp);
if( aRectFnSet.YDiff( nTmpTop, aRectFnSet.GetTop(aLine) ) > 0 )
{
if( aRectFnSet.YDiff( m_nNextTop, nTmpTop ) > 0 )
SetNextTop( nTmpTop ); // upper border of next frame
}
else if (!aRectFnSet.GetWidth(aTmp)) // typical for Objects with contour wrap
{ // For Objects with contour wrap that start before the current
// line, and end below it, but do not actually overlap it, the
// optimization has to be disabled, because the circumstances
// can change in the next line.
if( ! aRectFnSet.GetHeight(aTmp) ||
aRectFnSet.YDiff( aRectFnSet.GetBottom(aTmp),
aRectFnSet.GetTop(aLine) ) > 0 )
SetNextTop( 0 );
}
if( aTmp.Overlaps( aLine ) && nTmpRight > nFlyRight )
{
nFlyRight = nTmpRight;
if( css::text::WrapTextMode_RIGHT == eSurroundForTextWrap ||
css::text::WrapTextMode_PARALLEL == eSurroundForTextWrap )
{
// overrule the FlyFrame
if( nRight > nFlyRight )
nRight = nFlyRight;
bStop = true;
}
}
}
aRectFnSet.SetRight( rFly, nRight );
}
// #i68520#
void SwTextFly::CalcLeftMargin( SwRect &rFly,
SwAnchoredObjList::size_type nFlyPos,
const SwRect &rLine ) const
{
OSL_ENSURE( !m_pCurrFrame->IsVertical() || !m_pCurrFrame->IsSwapped(),
"SwTextFly::CalcLeftMargin with swapped frame" );
SwRectFnSet aRectFnSet(m_pCurrFrame);
// #118796# - correct determination of left of printing area
SwTwips nLeft = aRectFnSet.GetPrtLeft(*m_pCurrFrame);
const SwTwips nFlyLeft = aRectFnSet.GetLeft(rFly);
if( nLeft > nFlyLeft )
nLeft = rFly.Left();
SwRect aLine( rLine );
aRectFnSet.SetLeft( aLine, nLeft );
// It is possible that there is another object that is _above_ us
// and protrudes into the same line.
// Flys with run-through are invisible for those below, i.e., they
// are ignored for computing the margins of other Flys.
// 3301: pNext->getFrameArea().Overlaps( rLine ) is necessary
// #i68520#
SwAnchoredObjList::size_type nMyPos = nFlyPos;
while( ++nFlyPos < mpAnchoredObjList->size() )
{
// #i68520#
const SwAnchoredObject* pNext = (*mpAnchoredObjList)[ nFlyPos ];
const SwRect& aTmp( pNext->GetObjRectWithSpaces() );
if( aRectFnSet.GetLeft(aTmp) >= nFlyLeft )
break;
}
while( nFlyPos )
{
if( --nFlyPos == nMyPos )
continue;
// #i68520#
const SwAnchoredObject* pNext = (*mpAnchoredObjList)[ nFlyPos ];
if( pNext == mpCurrAnchoredObj )
continue;
css::text::WrapTextMode eSurroundForTextWrap = GetSurroundForTextWrap( pNext );
if( css::text::WrapTextMode_THROUGH == eSurroundForTextWrap )
continue;
const SwRect aTmp( SwContourCache::CalcBoundRect
(pNext, aLine, m_pCurrFrame, nFlyLeft, false) );
if( aRectFnSet.GetLeft(aTmp) < nFlyLeft && aTmp.Overlaps( aLine ) )
{
// #118796# - no '+1', because <..fnGetRight>
// returns the correct value.
SwTwips nTmpRight = aRectFnSet.GetRight(aTmp);
if ( nLeft <= nTmpRight )
nLeft = nTmpRight;
break;
}
}
aRectFnSet.SetLeft( rFly, nLeft );
}
// #i68520#
SwRect SwTextFly::AnchoredObjToRect( const SwAnchoredObject* pAnchoredObj,
const SwRect &rLine ) const
{
SwRectFnSet aRectFnSet(m_pCurrFrame);
const tools::Long nXPos = m_pCurrFrame->IsRightToLeft() ?
rLine.Right() :
aRectFnSet.GetLeft(rLine);
SwRect aFly = mbIgnoreContour ?
pAnchoredObj->GetObjRectWithSpaces() :
SwContourCache::CalcBoundRect(pAnchoredObj, rLine, m_pCurrFrame,
nXPos, !m_pCurrFrame->IsRightToLeft());
if( !aFly.Width() )
return aFly;
// so the line may grow up to the lower edge of the frame
SetNextTop( aRectFnSet.GetBottom(aFly) );
SwAnchoredObjList::size_type nFlyPos = GetPos( pAnchoredObj );
// LEFT and RIGHT, we grow the rectangle.
// We have some problems, when several frames are to be seen.
// At the moment, only the easier case is assumed:
// + LEFT means that the text must flow on the left of the frame,
// that is the frame expands to the right edge of the print area
// or to the next frame.
// + RIGHT is the opposite.
// Otherwise the set distance between text and frame is always
// added up.
switch( GetSurroundForTextWrap( pAnchoredObj ) )
{
case css::text::WrapTextMode_LEFT :
{
CalcRightMargin( aFly, nFlyPos, rLine );
break;
}
case css::text::WrapTextMode_RIGHT :
{
CalcLeftMargin( aFly, nFlyPos, rLine );
break;
}
case css::text::WrapTextMode_NONE :
{
CalcRightMargin( aFly, nFlyPos, rLine );
CalcLeftMargin( aFly, nFlyPos, rLine );
break;
}
default:
break;
}
return aFly;
}
// #i68520#
// Wrap only on sides with at least 2cm space for the text
#define TEXT_MIN 1134
// Wrap on both sides up to a frame width of 1.5cm
#define FRAME_MAX 850
css::text::WrapTextMode SwTextFly::GetSurroundForTextWrap( const SwAnchoredObject* pAnchoredObj ) const
{
const SwFrameFormat* pFormat = pAnchoredObj->GetFrameFormat();
const SwFormatSurround &rFlyFormat = pFormat->GetSurround();
css::text::WrapTextMode eSurroundForTextWrap = rFlyFormat.GetSurround();
if( rFlyFormat.IsAnchorOnly() && pAnchoredObj->GetAnchorFrame() != GetMaster() )
{
const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
if ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
(RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()))
{
return css::text::WrapTextMode_NONE;
}
}
// in cause of run-through and nowrap ignore smartly
if( css::text::WrapTextMode_THROUGH == eSurroundForTextWrap ||
css::text::WrapTextMode_NONE == eSurroundForTextWrap )
return eSurroundForTextWrap;
// left is left and right is right
if (m_pCurrFrame->IsRightToLeft())
{
if ( css::text::WrapTextMode_LEFT == eSurroundForTextWrap )
eSurroundForTextWrap = css::text::WrapTextMode_RIGHT;
else if ( css::text::WrapTextMode_RIGHT == eSurroundForTextWrap )
eSurroundForTextWrap = css::text::WrapTextMode_LEFT;
}
// "ideal page wrap":
if ( css::text::WrapTextMode_DYNAMIC == eSurroundForTextWrap )
{
SwRectFnSet aRectFnSet(m_pCurrFrame);
const tools::Long nCurrLeft = aRectFnSet.GetPrtLeft(*m_pCurrFrame);
const tools::Long nCurrRight = aRectFnSet.GetPrtRight(*m_pCurrFrame);
const SwRect& aRect( pAnchoredObj->GetObjRectWithSpaces() );
tools::Long nFlyLeft = aRectFnSet.GetLeft(aRect);
tools::Long nFlyRight = aRectFnSet.GetRight(aRect);
if ( nFlyRight < nCurrLeft || nFlyLeft > nCurrRight )
eSurroundForTextWrap = css::text::WrapTextMode_PARALLEL;
else
{
tools::Long nLeft = nFlyLeft - nCurrLeft;
tools::Long nRight = nCurrRight - nFlyRight;
if( nFlyRight - nFlyLeft > FRAME_MAX )
{
if( nLeft < nRight )
nLeft = 0;
else
nRight = 0;
}
const int textMin = GetMaster()->GetDoc()
.getIDocumentSettingAccess().get(DocumentSettingId::SURROUND_TEXT_WRAP_SMALL )
? TEXT_MIN_SMALL : TEXT_MIN;
// In case there is no space on either side, then css::text::WrapTextMode_PARALLEL
// gives the same result when doing the initial layout or a layout
// update after editing, so prefer that over css::text::WrapTextMode_NONE.
if (nLeft == 0 && nRight == 0)
return css::text::WrapTextMode_PARALLEL;
if( nLeft < textMin )
nLeft = 0;
if( nRight < textMin )
nRight = 0;
if( nLeft )
eSurroundForTextWrap = nRight ? css::text::WrapTextMode_PARALLEL : css::text::WrapTextMode_LEFT;
else
eSurroundForTextWrap = nRight ? css::text::WrapTextMode_RIGHT: css::text::WrapTextMode_NONE;
}
}
return eSurroundForTextWrap;
}
bool SwTextFly::IsAnyFrame( const SwRect &rLine ) const
{
SwSwapIfSwapped swap(const_cast<SwTextFrame *>(m_pCurrFrame));
OSL_ENSURE( m_bOn, "IsAnyFrame: Why?" );
return ForEach( rLine, nullptr, false );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'Intersection' is required to be utilized.