summaryrefslogtreecommitdiffstats
path: root/sw/source/core/text/txtfly.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /sw/source/core/text/txtfly.cxx
parentInitial commit. (diff)
downloadlibreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz
libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/text/txtfly.cxx')
-rw-r--r--sw/source/core/text/txtfly.cxx1407
1 files changed, 1407 insertions, 0 deletions
diff --git a/sw/source/core/text/txtfly.cxx b/sw/source/core/text/txtfly.cxx
new file mode 100644
index 000000000..52979fde8
--- /dev/null
+++ b/sw/source/core/text/txtfly.cxx
@@ -0,0 +1,1407 @@
+/* -*- 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 "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 <pagedesc.hxx>
+#include <sortedobjs.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentSettingAccess.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() :
+ nPntCnt( 0 )
+{
+}
+
+SwContourCache::~SwContourCache()
+{
+}
+
+void SwContourCache::ClrObject( sal_uInt16 nPos )
+{
+ nPntCnt -= 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->nPntCnt = 0;
+ }
+}
+
+// #i68520#
+SwRect SwContourCache::CalcBoundRect( const SwAnchoredObject* pAnchoredObj,
+ const SwRect &rLine,
+ const SwTextFrame* pFrame,
+ const 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 &&
+ ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) == nullptr ||
+ ( static_cast<const SwFlyFrame*>(pAnchoredObj)->Lower() &&
+ static_cast<const SwFlyFrame*>(pAnchoredObj)->Lower()->IsNoTextFrame() ) ) )
+ {
+ aRet = pAnchoredObj->GetObjRectWithSpaces();
+ if( aRet.IsOver( 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 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 )
+ {
+ nPntCnt -= mvItems.back().mxTextRanger->GetPointCount();
+ mvItems.pop_back();
+ }
+ ::basegfx::B2DPolyPolygon aPolyPolygon;
+ std::unique_ptr<::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( static_cast<const SwVirtFlyDrawObj*>(pObj)->
+ GetFlyFrame()->getFrameArea().SVRect() );
+ aPolyPolygon.clear();
+ aPolyPolygon.append(aPoly.getB2DPolyPolygon());
+ }
+ else
+ {
+ if( dynamic_cast< const E3dObject *>( pObj ) == nullptr )
+ {
+ aPolyPolygon = pObj->TakeXorPoly();
+ }
+
+ ::basegfx::B2DPolyPolygon aContourPoly(pObj->TakeContour());
+ pPolyPolygon.reset(new ::basegfx::B2DPolyPolygon(aContourPoly));
+ }
+ 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.get(), 20,
+ static_cast<sal_uInt16>(rLRSpace.GetLeft()), static_cast<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();
+
+ nPntCnt += mvItems[0].mxTextRanger->GetPointCount();
+ while( nPntCnt > POLY_MAX && mvItems.size() > POLY_MIN )
+ {
+ nPntCnt -= 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);
+ long nTmpTop = aRectFnSet.GetTop(rLine);
+ // fnGetBottom is top + height
+ long nTmpBottom = aRectFnSet.GetBottom(rLine);
+
+ Range aRange( std::min( nTmpTop, nTmpBottom ), std::max( nTmpTop, nTmpBottom ) );
+
+ std::deque<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()
+ : pPage(nullptr)
+ , mpCurrAnchoredObj(nullptr)
+ , m_pCurrFrame(nullptr)
+ , m_pMaster(nullptr)
+ , nMinBottom(0)
+ , nNextTop(0)
+ , m_nCurrFrameNodeIndex(0)
+ , bOn(false)
+ , bTopRule(false)
+ , mbIgnoreCurrentFrame(false)
+ , mbIgnoreContour(false)
+ , mbIgnoreObjsInHeaderFooter(false)
+
+{
+}
+
+SwTextFly::SwTextFly( const SwTextFrame *pFrame )
+{
+ CtorInitTextFly( pFrame );
+}
+
+SwTextFly::SwTextFly( const SwTextFly& rTextFly )
+{
+ pPage = rTextFly.pPage;
+ mpCurrAnchoredObj = rTextFly.mpCurrAnchoredObj;
+ m_pCurrFrame = rTextFly.m_pCurrFrame;
+ m_pMaster = rTextFly.m_pMaster;
+ if( rTextFly.mpAnchoredObjList )
+ {
+ mpAnchoredObjList.reset( new SwAnchoredObjList( *(rTextFly.mpAnchoredObjList) ) );
+ }
+
+ bOn = rTextFly.bOn;
+ bTopRule = rTextFly.bTopRule;
+ nMinBottom = rTextFly.nMinBottom;
+ nNextTop = rTextFly.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;
+ 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();
+ bOn = pPage->GetSortedObjs() != nullptr;
+ bTopRule = true;
+ nMinBottom = 0;
+ nNextTop = 0;
+ m_nCurrFrameNodeIndex = ULONG_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( 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( 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());
+ }
+
+ const SwSortedObjs *pSorted = 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.IsOver( 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( !bTopRule, "DrawTextOpaque: Wrong TopRule" );
+
+ // #i68520#
+ const SwAnchoredObjList::size_type nCount( bOn ? GetAnchoredObjList()->size() : 0 );
+ if (nCount > 0)
+ {
+ const SdrLayerID nHellId = pPage->getRootFrame()->GetCurrShell()->getIDocumentDrawModelAccess().GetHellId();
+ for( SwAnchoredObjList::size_type i = 0; i < nCount; ++i )
+ {
+ // #i68520#
+ const SwAnchoredObject* pTmpAnchoredObj = (*mpAnchoredObjList)[i];
+ if( dynamic_cast<const SwFlyFrame*>(pTmpAnchoredObj) &&
+ mpCurrAnchoredObj != pTmpAnchoredObj )
+ {
+ // #i68520#
+ const SwFlyFrame& rFly = dynamic_cast<const SwFlyFrame&>(*pTmpAnchoredObj);
+ if( aRegion.GetOrigin().IsOver( rFly.getFrameArea() ) )
+ {
+ const SwFrameFormat *pFormat = rFly.GetFormat();
+ const SwFormatSurround &rSur = pFormat->GetSurround();
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ // Only the ones who are opaque and more to the top
+ if( ! rFly.IsBackgroundTransparent() &&
+ css::text::WrapTextMode_THROUGH == rSur.GetSurround() &&
+ ( !rSur.IsAnchorOnly() ||
+ // #i68520#
+ GetMaster() == rFly.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 =
+ rFly.Lower() && rFly.Lower()->IsNoTextFrame()
+ ? static_cast<const SwNoTextFrame*>(rFly.Lower())
+ : nullptr;
+ if ( !pNoText ||
+ (!pNoText->IsTransparent() && !rSur.IsContour()) )
+ {
+ bOpaque = true;
+ aRegion -= rFly.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( !bTopRule, "DrawFlyRect: Wrong TopRule" );
+ // #i68520#
+ const SwAnchoredObjList::size_type nCount( bOn ? GetAnchoredObjList()->size() : 0 );
+ if (nCount > 0)
+ {
+ const SdrLayerID nHellId = 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 = dynamic_cast<const SwFlyFrame*>(pAnchoredObjTmp);
+ 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, 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 ) && bTopRule )
+ {
+ // #i26945#
+ const SwFrameFormat& rFrameFormat = _pAnchoredObj->GetFrameFormat();
+ const SwFormatAnchor& rNewA = rFrameFormat.GetAnchor();
+ if (RndStdIds::FLY_AT_PAGE == rNewA.GetAnchorId())
+ {
+ if ( bInFootnote )
+ return false;
+
+ if ( bInFooterOrHeader )
+ {
+ const SwFormatVertOrient& aVert( rFrameFormat.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( dynamic_cast<const SwFlyFrame*>(mpCurrAnchoredObj), pNew);
+
+ 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 ( !bTopRule )
+ bEvade = true;
+ else
+ {
+ // Within chained Flys we only avoid Lower
+ // #i68520#
+ const SwFormatChain &rChain = mpCurrAnchoredObj->GetFrameFormat().GetChain();
+ if ( !rChain.GetPrev() && !rChain.GetNext() )
+ {
+ // #i26945#
+ const SwFormatAnchor& rNewA = _pAnchoredObj->GetFrameFormat().GetAnchor();
+ // #i68520#
+ const SwFormatAnchor& rCurrA = mpCurrAnchoredObj->GetFrameFormat().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.IsOver( mpCurrAnchoredObj->GetObjRectWithSpaces() ) )
+ bEvade = false;
+ }
+ }
+
+ if ( bEvade )
+ {
+ // #i26945#
+ const SwFormatAnchor& rNewA = _pAnchoredObj->GetFrameFormat().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.
+ sal_uLong nTmpIndex = rNewA.GetContentAnchor()->nNode.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 (ULONG_MAX == m_nCurrFrameNodeIndex)
+ m_nCurrFrameNodeIndex = m_pCurrFrame->GetTextNodeFirst()->GetIndex();
+
+ if (FrameContainsNode(*m_pCurrFrame, nTmpIndex) || nTmpIndex < m_nCurrFrameNodeIndex)
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// #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 = pPage->GetSortedObjs();
+ const size_t nCount = pSorted ? pSorted->size() : 0;
+ // --> #108724# Page header/footer content doesn't have to wrap around
+ // floating screen objects
+ 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 bWrapAllowed = ( pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) ||
+ (!m_pCurrFrame->IsInFootnote() && !bFooterHeader));
+
+ bOn = false;
+
+ if( nCount && bWrapAllowed )
+ {
+ // #i68520#
+ mpAnchoredObjList.reset(new SwAnchoredObjList );
+
+ // #i28701# - consider complete frame area for new
+ // text wrapping
+ SwRect aRect;
+ if ( pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) )
+ {
+ aRect = m_pCurrFrame->getFramePrintArea();
+ aRect += m_pCurrFrame->getFrameArea().Pos();
+ }
+ else
+ {
+ aRect = m_pCurrFrame->getFrameArea();
+ }
+ // Make ourselves a little smaller than we are,
+ // so that 1-Twip-overlappings are ignored (#49532)
+ SwRectFnSet aRectFnSet(m_pCurrFrame);
+ const long nRight = aRectFnSet.GetRight(aRect) - 1;
+ const 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() ) )
+ {
+ 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(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 SwFormatSurround &rFlyFormat = pAnchoredObj->GetFrameFormat().GetSurround();
+ // #i68520#
+ if ( rFlyFormat.IsAnchorOnly() &&
+ pAnchoredObj->GetAnchorFrame() == GetMaster() )
+ {
+ const SwFormatVertOrient &rTmpFormat =
+ pAnchoredObj->GetFrameFormat().GetVertOrient();
+ if( text::VertOrientation::BOTTOM != rTmpFormat.GetVertOrient() )
+ nMinBottom = ( aRectFnSet.IsVert() && nMinBottom ) ?
+ std::min( nMinBottom, aBound.Left() ) :
+ std::max( nMinBottom, aRectFnSet.GetBottom(aBound) );
+ }
+
+ bOn = true;
+ }
+ }
+ if( nMinBottom )
+ {
+ SwTwips nMax = aRectFnSet.GetPrtBottom(*m_pCurrFrame->GetUpper());
+ if( aRectFnSet.YDiff( nMinBottom, nMax ) > 0 )
+ nMinBottom = nMax;
+ }
+ }
+ else
+ {
+ // #i68520#
+ mpAnchoredObjList.reset( new SwAnchoredObjList );
+ }
+
+ // #i68520#
+ return mpAnchoredObjList.get();
+}
+
+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 SwFormatSurround &rFlyFormat = pAnchoredObj->GetFrameFormat().GetSurround();
+ if( rFlyFormat.IsAnchorOnly() )
+ {
+ const SwFormatVertOrient &rTmpFormat =
+ pAnchoredObj->GetFrameFormat().GetVertOrient();
+ if( text::VertOrientation::BOTTOM != rTmpFormat.GetVertOrient() )
+ {
+ const SwRect& aBound( pAnchoredObj->GetObjRectWithSpaces() );
+ if( aBound.Top() < nEndOfFrame )
+ nRet = std::max( nRet, aBound.Bottom() );
+ }
+ }
+ }
+ SwTwips nMax = m_pCurrFrame->GetUpper()->getFrameArea().Top() +
+ m_pCurrFrame->GetUpper()->getFramePrintArea().Bottom();
+ if( nRet > nMax )
+ nRet = nMax;
+ }
+ 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(pPage->getFrameArea()) < aRectFnSet.GetHeight(rRect))
+ {
+ // get the doc model description
+ const SwPageDesc* pPageDesc = 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( 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.IsOver( 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.IsOver( 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().IsOver( 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 long nTmpTop = aRectFnSet.GetTop(aTmp);
+ if( aRectFnSet.YDiff( nTmpTop, aRectFnSet.GetTop(aLine) ) > 0 )
+ {
+ if( aRectFnSet.YDiff( 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.IsOver( 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().IsOver( 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.IsOver( 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 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
+
+// MS Word wraps on sides with even less space (value guessed).
+#define TEXT_MIN_SMALL 300
+
+// 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 long nCurrLeft = aRectFnSet.GetPrtLeft(*m_pCurrFrame);
+ const long nCurrRight = aRectFnSet.GetPrtRight(*m_pCurrFrame);
+ const SwRect& aRect( pAnchoredObj->GetObjRectWithSpaces() );
+ long nFlyLeft = aRectFnSet.GetLeft(aRect);
+ long nFlyRight = aRectFnSet.GetRight(aRect);
+
+ if ( nFlyRight < nCurrLeft || nFlyLeft > nCurrRight )
+ eSurroundForTextWrap = css::text::WrapTextMode_PARALLEL;
+ else
+ {
+ long nLeft = nFlyLeft - nCurrLeft;
+ 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( bOn, "IsAnyFrame: Why?" );
+
+ return ForEach( rLine, nullptr, false );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */