summaryrefslogtreecommitdiffstats
path: root/sw/source/core/layout/laycache.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/layout/laycache.cxx')
-rw-r--r--sw/source/core/layout/laycache.cxx1205
1 files changed, 1205 insertions, 0 deletions
diff --git a/sw/source/core/layout/laycache.cxx b/sw/source/core/layout/laycache.cxx
new file mode 100644
index 000000000..d7c9cc72f
--- /dev/null
+++ b/sw/source/core/layout/laycache.cxx
@@ -0,0 +1,1205 @@
+/* -*- 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 <editeng/formatbreakitem.hxx>
+#include <sal/log.hxx>
+#include <tools/stream.hxx>
+#include <doc.hxx>
+#include <IDocumentStatistics.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docstat.hxx>
+#include <docary.hxx>
+#include <fmtpdsc.hxx>
+#include <laycache.hxx>
+#include "layhelp.hxx"
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <swtable.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <sectfrm.hxx>
+#include <fmtcntnt.hxx>
+#include <pagedesc.hxx>
+#include <frmtool.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <viewopt.hxx>
+#include <flyfrm.hxx>
+#include <sortedobjs.hxx>
+#include <ndindex.hxx>
+#include <node.hxx>
+#include <ndtxt.hxx>
+#include <frameformats.hxx>
+
+#include <limits>
+#include <set>
+
+using namespace ::com::sun::star;
+
+SwLayoutCache::SwLayoutCache() : nLockCount( 0 ) {}
+
+/*
+ * Reading and writing of the layout cache.
+ * The layout cache is not necessary, but it improves
+ * the performance and reduces the text flow during
+ * the formatting.
+ * The layout cache contains the index of the paragraphs/tables
+ * at the top of every page, so it's possible to create
+ * the right count of pages and to distribute the document content
+ * to this pages before the formatting starts.
+ */
+
+void SwLayoutCache::Read( SvStream &rStream )
+{
+ if( !pImpl )
+ {
+ pImpl.reset( new SwLayCacheImpl );
+ if( !pImpl->Read( rStream ) )
+ {
+ pImpl.reset();
+ }
+ }
+}
+
+void SwLayCacheImpl::Insert( sal_uInt16 nType, sal_uLong nIndex, sal_Int32 nOffset )
+{
+ aType.push_back( nType );
+ mIndices.push_back( nIndex );
+ aOffset.push_back( nOffset );
+}
+
+bool SwLayCacheImpl::Read( SvStream& rStream )
+{
+ SwLayCacheIoImpl aIo( rStream, false );
+ if( aIo.GetMajorVersion() > SW_LAYCACHE_IO_VERSION_MAJOR )
+ return false;
+
+ // Due to an evil bug in the layout cache (#102759#), we cannot trust the
+ // sizes of fly frames which have been written using the "old" layout cache.
+ // This flag should indicate that we do not want to trust the width and
+ // height of fly frames
+ bUseFlyCache = aIo.GetMinorVersion() >= 1;
+
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_PAGES );
+ aIo.OpenFlagRec();
+ aIo.CloseFlagRec();
+ while( aIo.BytesLeft() && !aIo.HasError() )
+ {
+ sal_uInt32 nIndex(0), nOffset(0);
+
+ switch( aIo.Peek() )
+ {
+ case SW_LAYCACHE_IO_REC_PARA:
+ {
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_PARA );
+ sal_uInt8 cFlags = aIo.OpenFlagRec();
+ aIo.GetStream().ReadUInt32( nIndex );
+ if( (cFlags & 0x01) != 0 )
+ aIo.GetStream().ReadUInt32( nOffset );
+ else
+ nOffset = COMPLETE_STRING;
+ aIo.CloseFlagRec();
+ Insert( SW_LAYCACHE_IO_REC_PARA, nIndex, static_cast<sal_Int32>(nOffset) );
+ aIo.CloseRec();
+ break;
+ }
+ case SW_LAYCACHE_IO_REC_TABLE:
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_TABLE );
+ aIo.OpenFlagRec();
+ aIo.GetStream().ReadUInt32( nIndex )
+ .ReadUInt32( nOffset );
+ Insert( SW_LAYCACHE_IO_REC_TABLE, nIndex, static_cast<sal_Int32>(nOffset) );
+ aIo.CloseFlagRec();
+ aIo.CloseRec();
+ break;
+ case SW_LAYCACHE_IO_REC_FLY:
+ {
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_FLY );
+ aIo.OpenFlagRec();
+ aIo.CloseFlagRec();
+ sal_Int32 nX(0), nY(0), nW(0), nH(0);
+ sal_uInt16 nPgNum(0);
+ aIo.GetStream().ReadUInt16( nPgNum ).ReadUInt32( nIndex )
+ .ReadInt32( nX ).ReadInt32( nY ).ReadInt32( nW ).ReadInt32( nH );
+ m_FlyCache.emplace_back( nPgNum, nIndex, nX, nY, nW, nH );
+ aIo.CloseRec();
+ break;
+ }
+ default:
+ aIo.SkipRec();
+ break;
+ }
+ }
+ aIo.CloseRec();
+
+ return !aIo.HasError();
+}
+
+/** writes the index (more precise: the difference between
+ * the index and the first index of the document content)
+ * of the first paragraph/table at the top of every page.
+ * If at the top of a page is the rest of a paragraph/table
+ * from the bottom of the previous page, the character/row
+ * number is stored, too.
+ * The position, size and page number of the text frames
+ * are stored, too
+ */
+void SwLayoutCache::Write( SvStream &rStream, const SwDoc& rDoc )
+{
+ if( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ) // the layout itself ..
+ {
+ SwLayCacheIoImpl aIo( rStream, true );
+ // We want to save the relative index, so we need the index
+ // of the first content
+ sal_uLong nStartOfContent = rDoc.GetNodes().GetEndOfContent().
+ StartOfSectionNode()->GetIndex();
+ // The first page...
+ SwPageFrame* pPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->Lower()));
+
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_PAGES );
+ aIo.OpenFlagRec( 0, 0 );
+ aIo.CloseFlagRec();
+ while( pPage )
+ {
+ if( pPage->GetPrev() )
+ {
+ SwLayoutFrame* pLay = pPage->FindBodyCont();
+ SwFrame* pTmp = pLay ? pLay->ContainsAny() : nullptr;
+ // We are only interested in paragraph or table frames,
+ // a section frames contains paragraphs/tables.
+ if( pTmp && pTmp->IsSctFrame() )
+ pTmp = static_cast<SwSectionFrame*>(pTmp)->ContainsAny();
+
+ if( pTmp ) // any content
+ {
+ if( pTmp->IsTextFrame() )
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTmp));
+ assert(!pFrame->GetMergedPara());
+ sal_uLong nNdIdx = pFrame->GetTextNodeFirst()->GetIndex();
+ if( nNdIdx > nStartOfContent )
+ {
+ /* Open Paragraph Record */
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_PARA );
+ bool bFollow = static_cast<SwTextFrame*>(pTmp)->IsFollow();
+ aIo.OpenFlagRec( bFollow ? 0x01 : 0x00,
+ bFollow ? 8 : 4 );
+ nNdIdx -= nStartOfContent;
+ aIo.GetStream().WriteUInt32( nNdIdx );
+ if( bFollow )
+ aIo.GetStream().WriteUInt32( sal_Int32(static_cast<SwTextFrame*>(pTmp)->GetOffset()) );
+ aIo.CloseFlagRec();
+ /* Close Paragraph Record */
+ aIo.CloseRec();
+ }
+ }
+ else if( pTmp->IsTabFrame() )
+ {
+ SwTabFrame* pTab = static_cast<SwTabFrame*>(pTmp);
+ sal_uLong nOfst = COMPLETE_STRING;
+ if( pTab->IsFollow() )
+ {
+ // If the table is a follow, we have to look for the
+ // master and to count all rows to get the row number
+ nOfst = 0;
+ if( pTab->IsFollow() )
+ pTab = pTab->FindMaster( true );
+ while( pTab != pTmp )
+ {
+ SwFrame* pSub = pTab->Lower();
+ while( pSub )
+ {
+ ++nOfst;
+ pSub = pSub->GetNext();
+ }
+ pTab = pTab->GetFollow();
+ assert(pTab && "Table follow without master");
+ }
+ }
+ while (true)
+ {
+ sal_uLong nNdIdx =
+ pTab->GetTable()->GetTableNode()->GetIndex();
+ if( nNdIdx > nStartOfContent )
+ {
+ /* Open Table Record */
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_TABLE );
+ aIo.OpenFlagRec( 0, 8 );
+ nNdIdx -= nStartOfContent;
+ aIo.GetStream().WriteUInt32( nNdIdx )
+ .WriteUInt32( nOfst );
+ aIo.CloseFlagRec();
+ /* Close Table Record */
+ aIo.CloseRec();
+ }
+ // If the table has a follow on the next page,
+ // we know already the row number and store this
+ // immediately.
+ if( pTab->GetFollow() )
+ {
+ if( nOfst == sal_uLong(COMPLETE_STRING) )
+ nOfst = 0;
+ do
+ {
+ SwFrame* pSub = pTab->Lower();
+ while( pSub )
+ {
+ ++nOfst;
+ pSub = pSub->GetNext();
+ }
+ pTab = pTab->GetFollow();
+ SwPageFrame *pTabPage = pTab->FindPageFrame();
+ if( pTabPage != pPage )
+ {
+ OSL_ENSURE( pPage->GetPhyPageNum() <
+ pTabPage->GetPhyPageNum(),
+ "Looping Tableframes" );
+ pPage = pTabPage;
+ break;
+ }
+ } while ( pTab->GetFollow() );
+ }
+ else
+ break;
+ }
+ }
+ }
+ }
+ if( pPage->GetSortedObjs() )
+ {
+ SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if (SwFlyFrame *pFly = dynamic_cast<SwFlyFrame*>(pAnchoredObj))
+ {
+ if( pFly->getFrameArea().Left() != FAR_AWAY &&
+ !pFly->GetAnchorFrame()->FindFooterOrHeader() )
+ {
+ const SwContact *pC =
+ ::GetUserCall(pAnchoredObj->GetDrawObj());
+ if( pC )
+ {
+ sal_uInt32 nOrdNum = pAnchoredObj->GetDrawObj()->GetOrdNum();
+ sal_uInt16 nPageNum = pPage->GetPhyPageNum();
+ /* Open Fly Record */
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_FLY );
+ aIo.OpenFlagRec( 0, 0 );
+ aIo.CloseFlagRec();
+ const SwRect& rRct = pFly->getFrameArea();
+ sal_Int32 nX = rRct.Left() - pPage->getFrameArea().Left();
+ sal_Int32 nY = rRct.Top() - pPage->getFrameArea().Top();
+ aIo.GetStream().WriteUInt16( nPageNum ).WriteUInt32( nOrdNum )
+ .WriteInt32( nX ).WriteInt32( nY )
+ .WriteInt32( rRct.Width() )
+ .WriteInt32( rRct.Height() );
+ /* Close Fly Record */
+ aIo.CloseRec();
+ }
+ }
+ }
+ }
+ }
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ aIo.CloseRec();
+ }
+}
+
+#ifdef DBG_UTIL
+bool SwLayoutCache::CompareLayout( const SwDoc& rDoc ) const
+{
+ if( !pImpl )
+ return true;
+ const SwRootFrame *pRootFrame = rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ if( pRootFrame )
+ {
+ size_t nIndex = 0;
+ sal_uLong nStartOfContent = rDoc.GetNodes().GetEndOfContent().
+ StartOfSectionNode()->GetIndex();
+ const SwPageFrame* pPage = static_cast<const SwPageFrame*>(pRootFrame->Lower());
+ if( pPage )
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ while( pPage )
+ {
+ if( nIndex >= pImpl->size() )
+ return false;
+
+ const SwLayoutFrame* pLay = pPage->FindBodyCont();
+ const SwFrame* pTmp = pLay ? pLay->ContainsAny() : nullptr;
+ if( pTmp && pTmp->IsSctFrame() )
+ pTmp = static_cast<const SwSectionFrame*>(pTmp)->ContainsAny();
+ if( pTmp )
+ {
+ if( pTmp->IsTextFrame() )
+ {
+
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTmp));
+ assert(!pFrame->GetMergedPara());
+ sal_uLong nNdIdx = pFrame->GetTextNodeFirst()->GetIndex();
+ if( nNdIdx > nStartOfContent )
+ {
+ bool bFollow = static_cast<const SwTextFrame*>(pTmp)->IsFollow();
+ nNdIdx -= nStartOfContent;
+ if( pImpl->GetBreakIndex( nIndex ) != nNdIdx ||
+ SW_LAYCACHE_IO_REC_PARA !=
+ pImpl->GetBreakType( nIndex ) ||
+ (bFollow
+ ? sal_Int32(static_cast<const SwTextFrame*>(pTmp)->GetOffset())
+ : COMPLETE_STRING) != pImpl->GetBreakOfst(nIndex))
+ {
+ return false;
+ }
+ ++nIndex;
+ }
+ }
+ else if( pTmp->IsTabFrame() )
+ {
+ const SwTabFrame* pTab = static_cast<const SwTabFrame*>(pTmp);
+ sal_Int32 nOfst = COMPLETE_STRING;
+ if( pTab->IsFollow() )
+ {
+ nOfst = 0;
+ if( pTab->IsFollow() )
+ pTab = pTab->FindMaster( true );
+ while( pTab != pTmp )
+ {
+ const SwFrame* pSub = pTab->Lower();
+ while( pSub )
+ {
+ ++nOfst;
+ pSub = pSub->GetNext();
+ }
+ pTab = pTab->GetFollow();
+ }
+ }
+ do
+ {
+ sal_uLong nNdIdx =
+ pTab->GetTable()->GetTableNode()->GetIndex();
+ if( nNdIdx > nStartOfContent )
+ {
+ nNdIdx -= nStartOfContent;
+ if( pImpl->GetBreakIndex( nIndex ) != nNdIdx ||
+ SW_LAYCACHE_IO_REC_TABLE !=
+ pImpl->GetBreakType( nIndex ) ||
+ nOfst != pImpl->GetBreakOfst( nIndex ) )
+ {
+ return false;
+ }
+ ++nIndex;
+ }
+ if( pTab->GetFollow() )
+ {
+ if( nOfst == COMPLETE_STRING )
+ nOfst = 0;
+ do
+ {
+ const SwFrame* pSub = pTab->Lower();
+ while( pSub )
+ {
+ ++nOfst;
+ pSub = pSub->GetNext();
+ }
+ pTab = pTab->GetFollow();
+ const SwPageFrame *pTabPage = pTab->FindPageFrame();
+ if( pTabPage != pPage )
+ {
+ pPage = pTabPage;
+ break;
+ }
+ } while ( pTab->GetFollow() );
+ }
+ else
+ break;
+ } while( pTab );
+ }
+ }
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+ }
+ return true;
+}
+#endif
+
+void SwLayoutCache::ClearImpl()
+{
+ if( !IsLocked() )
+ {
+ pImpl.reset();
+ }
+}
+
+SwLayoutCache::~SwLayoutCache()
+{
+ OSL_ENSURE( !nLockCount, "Deleting a locked SwLayoutCache!?" );
+}
+
+/// helper class to create not nested section frames for nested sections.
+SwActualSection::SwActualSection( SwActualSection *pUp,
+ SwSectionFrame *pSect,
+ SwSectionNode *pNd ) :
+ pUpper( pUp ),
+ pSectFrame( pSect ),
+ pSectNode( pNd )
+{
+ if ( !pSectNode )
+ {
+ const SwNodeIndex *pIndex = pSect->GetFormat()->GetContent().GetContentIdx();
+ pSectNode = pIndex->GetNode().FindSectionNode();
+ }
+}
+
+namespace {
+
+bool sanityCheckLayoutCache(SwLayCacheImpl const& rCache,
+ SwNodes const& rNodes, sal_uLong nNodeIndex)
+{
+ auto const nStartOfContent(rNodes.GetEndOfContent().StartOfSectionNode()->GetIndex());
+ nNodeIndex -= nStartOfContent;
+ auto const nMaxIndex(rNodes.GetEndOfContent().GetIndex() - nStartOfContent);
+ for (size_t nIndex = 0; nIndex < rCache.size(); ++nIndex)
+ {
+ auto const nBreakIndex(rCache.GetBreakIndex(nIndex));
+ if (nBreakIndex < nNodeIndex || nMaxIndex <= nBreakIndex)
+ {
+ SAL_WARN("sw.layout",
+ "invalid node index in layout-cache: " << nBreakIndex);
+ return false;
+ }
+ auto const nBreakType(rCache.GetBreakType(nIndex));
+ switch (nBreakType)
+ {
+ case SW_LAYCACHE_IO_REC_PARA:
+ if (!rNodes[nBreakIndex + nStartOfContent]->IsTextNode())
+ {
+ SAL_WARN("sw.layout",
+ "invalid node of type 'P' in layout-cache");
+ return false;
+ }
+ break;
+ case SW_LAYCACHE_IO_REC_TABLE:
+ if (!rNodes[nBreakIndex + nStartOfContent]->IsTableNode())
+ {
+ SAL_WARN("sw.layout",
+ "invalid node of type 'T' in layout-cache");
+ return false;
+ }
+ break;
+ default:
+ assert(false); // Read shouldn't have inserted that
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+/** helper class, which utilizes the layout cache information
+ * to distribute the document content to the right pages.
+ * It's used by the InsertCnt_(..)-function.
+ * If there's no layout cache, the distribution to the pages is more
+ * a guess, but a guess with statistical background.
+ */
+SwLayHelper::SwLayHelper( SwDoc *pD, SwFrame* &rpF, SwFrame* &rpP, SwPageFrame* &rpPg,
+ SwLayoutFrame* &rpL, std::unique_ptr<SwActualSection> &rpA,
+ sal_uLong nNodeIndex, bool bCache )
+ : mrpFrame( rpF )
+ , mrpPrv( rpP )
+ , mrpPage( rpPg )
+ , mrpLay( rpL )
+ , mrpActualSection( rpA )
+ , mbBreakAfter(false)
+ , mpDoc(pD)
+ , mnMaxParaPerPage( 25 )
+ , mnParagraphCnt( bCache ? 0 : USHRT_MAX )
+ , mnFlyIdx( 0 )
+ , mbFirst( bCache )
+{
+ mpImpl = mpDoc->GetLayoutCache() ? mpDoc->GetLayoutCache()->LockImpl() : nullptr;
+ if( mpImpl )
+ {
+ SwNodes const& rNodes(mpDoc->GetNodes());
+ if (sanityCheckLayoutCache(*mpImpl, rNodes, nNodeIndex))
+ {
+ mnIndex = 0;
+ mnStartOfContent = rNodes.GetEndOfContent().StartOfSectionNode()->GetIndex();
+ mnMaxParaPerPage = 1000;
+ }
+ else
+ {
+ mpDoc->GetLayoutCache()->UnlockImpl();
+ mpImpl = nullptr;
+ mnIndex = std::numeric_limits<size_t>::max();
+ mnStartOfContent = USHRT_MAX;
+ }
+ }
+ else
+ {
+ mnIndex = std::numeric_limits<size_t>::max();
+ mnStartOfContent = ULONG_MAX;
+ }
+}
+
+SwLayHelper::~SwLayHelper()
+{
+ if( mpImpl )
+ {
+ OSL_ENSURE( mpDoc && mpDoc->GetLayoutCache(), "Missing layoutcache" );
+ mpDoc->GetLayoutCache()->UnlockImpl();
+ }
+}
+
+/** Does NOT really calculate the page count,
+ * it returns the page count value from the layout cache, if available,
+ * otherwise it estimates the page count.
+ */
+sal_uLong SwLayHelper::CalcPageCount()
+{
+ sal_uLong nPgCount;
+ SwLayCacheImpl *pCache = mpDoc->GetLayoutCache() ?
+ mpDoc->GetLayoutCache()->LockImpl() : nullptr;
+ if( pCache )
+ {
+ nPgCount = pCache->size() + 1;
+ mpDoc->GetLayoutCache()->UnlockImpl();
+ }
+ else
+ {
+ nPgCount = mpDoc->getIDocumentStatistics().GetDocStat().nPage;
+ if ( nPgCount <= 10 ) // no page insertion for less than 10 pages
+ nPgCount = 0;
+ sal_uLong nNdCount = mpDoc->getIDocumentStatistics().GetDocStat().nPara;
+ if ( nNdCount <= 1 )
+ {
+ //Estimates the number of paragraphs.
+ sal_uLong nTmp = mpDoc->GetNodes().GetEndOfContent().GetIndex() -
+ mpDoc->GetNodes().GetEndOfExtras().GetIndex();
+ //Tables have a little overhead...
+ nTmp -= mpDoc->GetTableFrameFormats()->size() * 25;
+ //Fly frames, too ..
+ nTmp -= (mpDoc->GetNodes().GetEndOfAutotext().GetIndex() -
+ mpDoc->GetNodes().GetEndOfInserts().GetIndex()) / 3 * 5;
+ if ( nTmp > 0 )
+ nNdCount = nTmp;
+ }
+ if ( nNdCount > 100 ) // no estimation below this value
+ {
+ if ( nPgCount > 0 )
+ { // tdf#129529 avoid 0...
+ mnMaxParaPerPage = std::max<sal_uLong>(3, nNdCount / nPgCount);
+ }
+ else
+ {
+ mnMaxParaPerPage = std::max( sal_uLong(20),
+ sal_uLong(20 + nNdCount / 1000 * 3) );
+ const sal_uLong nMax = 53;
+ mnMaxParaPerPage = std::min( mnMaxParaPerPage, nMax );
+ nPgCount = nNdCount / mnMaxParaPerPage;
+ }
+ if ( nNdCount < 1000 )
+ nPgCount = 0;// no progress bar for small documents
+ SwViewShell *pSh = nullptr;
+ if( mrpLay && mrpLay->getRootFrame() )
+ pSh = mrpLay->getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ mnMaxParaPerPage *= 6;
+ }
+ }
+ return nPgCount;
+}
+
+/**
+ * inserts a page and return true, if
+ * - the break after flag is set
+ * - the actual content wants a break before
+ * - the maximum count of paragraph/rows is reached
+ *
+ * The break after flag is set, if the actual content
+ * wants a break after.
+ */
+bool SwLayHelper::CheckInsertPage()
+{
+ bool bEnd = nullptr == mrpPage->GetNext();
+ const SvxFormatBreakItem& rBrk = mrpFrame->GetBreakItem();
+ const SwFormatPageDesc& rDesc = mrpFrame->GetPageDescItem();
+ // #118195# Do not evaluate page description if frame
+ // is a follow frame!
+ const SwPageDesc* pDesc = mrpFrame->IsFlowFrame() &&
+ SwFlowFrame::CastFlowFrame( mrpFrame )->IsFollow() ?
+ nullptr :
+ rDesc.GetPageDesc();
+
+ bool bBrk = mnParagraphCnt > mnMaxParaPerPage || mbBreakAfter;
+ mbBreakAfter = rBrk.GetBreak() == SvxBreak::PageAfter ||
+ rBrk.GetBreak() == SvxBreak::PageBoth;
+ if ( !bBrk )
+ bBrk = rBrk.GetBreak() == SvxBreak::PageBefore ||
+ rBrk.GetBreak() == SvxBreak::PageBoth;
+
+ if ( bBrk || pDesc )
+ {
+ ::std::optional<sal_uInt16> oPgNum;
+ if ( !pDesc )
+ {
+ pDesc = mrpPage->GetPageDesc()->GetFollow();
+ }
+ else
+ {
+ oPgNum = rDesc.GetNumOffset();
+ if ( oPgNum )
+ static_cast<SwRootFrame*>(mrpPage->GetUpper())->SetVirtPageNum(true);
+ }
+ bool bNextPageRight = !mrpPage->OnRightPage();
+ bool bInsertEmpty = false;
+ assert(mrpPage->GetUpper()->GetLower());
+ if (oPgNum && bNextPageRight != IsRightPageByNumber(
+ *static_cast<SwRootFrame*>(mrpPage->GetUpper()), *oPgNum))
+ {
+ bNextPageRight = !bNextPageRight;
+ bInsertEmpty = true;
+ }
+ // If the page style is changing, we'll have a first page.
+ bool bNextPageFirst = pDesc != mrpPage->GetPageDesc();
+ ::InsertNewPage( const_cast<SwPageDesc&>(*pDesc), mrpPage->GetUpper(),
+ bNextPageRight, bNextPageFirst, bInsertEmpty, false, mrpPage->GetNext());
+ if ( bEnd )
+ {
+ OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
+ do
+ { mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
+ } while ( mrpPage->GetNext() );
+ }
+ else
+ {
+ OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
+ mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
+ if ( mrpPage->IsEmptyPage() )
+ {
+ OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
+ mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
+ }
+ }
+ mrpLay = mrpPage->FindBodyCont();
+ while( mrpLay->Lower() )
+ mrpLay = static_cast<SwLayoutFrame*>(mrpLay->Lower());
+ return true;
+ }
+ return false;
+}
+
+/** entry point for the InsertCnt_-function.
+ * The document content index is checked either it is
+ * in the layout cache either it's time to insert a page
+ * cause the maximal estimation of content per page is reached.
+ * A really big table or long paragraph may contains more than
+ * one page, in this case the needed count of pages will inserted.
+ */
+bool SwLayHelper::CheckInsert( sal_uLong nNodeIndex )
+{
+ bool bRet = false;
+ bool bLongTab = false;
+ sal_uLong nMaxRowPerPage( 0 );
+ nNodeIndex -= mnStartOfContent;
+ sal_uInt16 nRows( 0 );
+ if( mrpFrame->IsTabFrame() )
+ {
+ //Inside a table counts every row as a paragraph
+ SwFrame *pLow = static_cast<SwTabFrame*>(mrpFrame)->Lower();
+ nRows = 0;
+ do
+ {
+ ++nRows;
+ pLow = pLow->GetNext();
+ } while ( pLow );
+ mnParagraphCnt += nRows;
+ if( !mpImpl && mnParagraphCnt > mnMaxParaPerPage + 10 )
+ {
+ // OD 09.04.2003 #108698# - improve heuristics:
+ // Assume that a table, which has more than three times the quantity
+ // of maximal paragraphs per page rows, consists of rows, which have
+ // the height of a normal paragraph. Thus, allow as much rows per page
+ // as much paragraphs are allowed.
+ if ( nRows > ( 3*mnMaxParaPerPage ) )
+ {
+ nMaxRowPerPage = mnMaxParaPerPage;
+ }
+ else
+ {
+ SwFrame *pTmp = static_cast<SwTabFrame*>(mrpFrame)->Lower();
+ if( pTmp->GetNext() )
+ pTmp = pTmp->GetNext();
+ pTmp = static_cast<SwRowFrame*>(pTmp)->Lower();
+ sal_uInt16 nCnt = 0;
+ do
+ {
+ ++nCnt;
+ pTmp = pTmp->GetNext();
+ } while( pTmp );
+ nMaxRowPerPage = std::max( sal_uLong(2), mnMaxParaPerPage / nCnt );
+ }
+ bLongTab = true;
+ }
+ }
+ else
+ ++mnParagraphCnt;
+ if( mbFirst && mpImpl && mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex &&
+ ( mpImpl->GetBreakOfst( mnIndex ) < COMPLETE_STRING ||
+ ( ++mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex ) ) )
+ mbFirst = false;
+ // OD 09.04.2003 #108698# - always split a big tables.
+ if ( !mbFirst ||
+ ( mrpFrame->IsTabFrame() && bLongTab )
+ )
+ {
+ sal_Int32 nRowCount = 0;
+ do
+ {
+ if( mpImpl || bLongTab )
+ {
+ sal_Int32 nOfst = COMPLETE_STRING;
+ sal_uInt16 nType = SW_LAYCACHE_IO_REC_PAGES;
+ if( bLongTab )
+ {
+ mbBreakAfter = true;
+ nOfst = static_cast<sal_Int32>(nRowCount + nMaxRowPerPage);
+ }
+ else
+ {
+ while( mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex(mnIndex) < nNodeIndex)
+ ++mnIndex;
+ if( mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex(mnIndex) == nNodeIndex )
+ {
+ nType = mpImpl->GetBreakType( mnIndex );
+ nOfst = mpImpl->GetBreakOfst( mnIndex++ );
+ mbBreakAfter = true;
+ }
+ }
+
+ if( nOfst < COMPLETE_STRING )
+ {
+ bool bSplit = false;
+ sal_uInt16 nRepeat( 0 );
+ if( !bLongTab && mrpFrame->IsTextFrame() &&
+ SW_LAYCACHE_IO_REC_PARA == nType &&
+ nOfst < static_cast<SwTextFrame*>(mrpFrame)->GetText().getLength())
+ bSplit = true;
+ else if( mrpFrame->IsTabFrame() && nRowCount < nOfst &&
+ ( bLongTab || SW_LAYCACHE_IO_REC_TABLE == nType ) )
+ {
+ nRepeat = static_cast<SwTabFrame*>(mrpFrame)->
+ GetTable()->GetRowsToRepeat();
+ bSplit = nOfst < nRows && nRowCount + nRepeat < nOfst;
+ bLongTab = bLongTab && bSplit;
+ }
+ if( bSplit )
+ {
+ mrpFrame->InsertBehind( mrpLay, mrpPrv );
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mrpFrame);
+ aFrm.Pos() = mrpLay->getFrameArea().Pos();
+ aFrm.Pos().AdjustY(1 );
+ }
+
+ mrpPrv = mrpFrame;
+ if( mrpFrame->IsTabFrame() )
+ {
+ SwTabFrame* pTab = static_cast<SwTabFrame*>(mrpFrame);
+ // #i33629#, #i29955#
+ ::RegistFlys( pTab->FindPageFrame(), pTab );
+ SwFrame *pRow = pTab->Lower();
+ SwTabFrame *pFoll = new SwTabFrame( *pTab );
+
+ SwFrame *pPrv;
+ if( nRepeat > 0 )
+ {
+ bDontCreateObjects = true; //frmtool
+
+ // Insert new headlines:
+ sal_uInt16 nRowIdx = 0;
+ SwRowFrame* pHeadline = nullptr;
+ while( nRowIdx < nRepeat )
+ {
+ OSL_ENSURE( pTab->GetTable()->GetTabLines()[ nRowIdx ], "Table without rows?" );
+ pHeadline =
+ new SwRowFrame( *pTab->GetTable()->GetTabLines()[ nRowIdx ], pTab );
+ pHeadline->SetRepeatedHeadline( true );
+ pHeadline->InsertBefore( pFoll, nullptr );
+ pHeadline->RegistFlys();
+
+ ++nRowIdx;
+ }
+
+ bDontCreateObjects = false;
+ pPrv = pHeadline;
+ nRows = nRows + nRepeat;
+ }
+ else
+ pPrv = nullptr;
+ while( pRow && nRowCount < nOfst )
+ {
+ pRow = pRow->GetNext();
+ ++nRowCount;
+ }
+ while ( pRow )
+ {
+ SwFrame* pNxt = pRow->GetNext();
+ pRow->RemoveFromLayout();
+ pRow->InsertBehind( pFoll, pPrv );
+ pPrv = pRow;
+ pRow = pNxt;
+ }
+ mrpFrame = pFoll;
+ }
+ else
+ {
+ SwTextFrame *const pNew = static_cast<SwTextFrame*>(
+ static_cast<SwTextFrame*>(mrpFrame)
+ ->GetTextNodeFirst()->MakeFrame(mrpFrame));
+ pNew->ManipOfst( TextFrameIndex(nOfst) );
+ pNew->SetFollow( static_cast<SwTextFrame*>(mrpFrame)->GetFollow() );
+ static_cast<SwTextFrame*>(mrpFrame)->SetFollow( pNew );
+ mrpFrame = pNew;
+ }
+ }
+ }
+ }
+
+ SwPageFrame* pLastPage = mrpPage;
+ if( CheckInsertPage() )
+ {
+ CheckFlyCache_( pLastPage );
+ if( mrpPrv && mrpPrv->IsTextFrame() && !mrpPrv->isFrameAreaSizeValid() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mrpPrv);
+ aFrm.Height( mrpPrv->GetUpper()->getFramePrintArea().Height() );
+ }
+
+ bRet = true;
+ mrpPrv = nullptr;
+ mnParagraphCnt = 0;
+
+ if ( mrpActualSection )
+ {
+ //Did the SectionFrame even have a content? If not, we can
+ //directly put it somewhere else
+ SwSectionFrame *pSct;
+ bool bInit = false;
+ if ( !mrpActualSection->GetSectionFrame()->ContainsContent())
+ {
+ pSct = mrpActualSection->GetSectionFrame();
+ pSct->RemoveFromLayout();
+ }
+ else
+ {
+ pSct = new SwSectionFrame(
+ *mrpActualSection->GetSectionFrame(), false );
+ mrpActualSection->GetSectionFrame()->SimpleFormat();
+ bInit = true;
+ }
+ mrpActualSection->SetSectionFrame( pSct );
+ pSct->InsertBehind( mrpLay, nullptr );
+
+ if( bInit )
+ {
+ pSct->Init();
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pSct);
+ aFrm.Pos() = mrpLay->getFrameArea().Pos();
+ aFrm.Pos().AdjustY(1 ); //because of the notifications
+ }
+
+ mrpLay = pSct;
+ if ( mrpLay->Lower() && mrpLay->Lower()->IsLayoutFrame() )
+ mrpLay = mrpLay->GetNextLayoutLeaf();
+ }
+ }
+ } while( bLongTab || ( mpImpl && mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex ) );
+ }
+ mbFirst = false;
+ return bRet;
+}
+
+namespace {
+
+struct SdrObjectCompare
+{
+ bool operator()( const SdrObject* pF1, const SdrObject* pF2 ) const
+ {
+ return pF1->GetOrdNum() < pF2->GetOrdNum();
+ }
+};
+
+struct FlyCacheCompare
+{
+ bool operator()( const SwFlyCache* pC1, const SwFlyCache* pC2 ) const
+ {
+ return pC1->nOrdNum < pC2->nOrdNum;
+ }
+};
+
+}
+
+/**
+ * If a new page is inserted, the last page is analysed.
+ * If there are text frames with default position, the fly cache
+ * is checked, if these frames are stored in the cache.
+ */
+void SwLayHelper::CheckFlyCache_( SwPageFrame* pPage )
+{
+ if( !mpImpl || !pPage )
+ return;
+ const size_t nFlyCount = mpImpl->GetFlyCount();
+ // Any text frames at the page, fly cache available?
+ if( pPage->GetSortedObjs() && mnFlyIdx < nFlyCount )
+ {
+ SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ sal_uInt16 nPgNum = pPage->GetPhyPageNum();
+
+ // NOTE: Here we do not use the absolute ordnums but
+ // relative ordnums for the objects on this page.
+
+ // skip fly frames from pages before the current page
+ while( mnFlyIdx < nFlyCount &&
+ mpImpl->GetFlyCache(mnFlyIdx).nPageNum < nPgNum )
+ ++mnFlyIdx;
+
+ // sort cached objects on this page by ordnum
+ std::set< const SwFlyCache*, FlyCacheCompare > aFlyCacheSet;
+ size_t nIdx = mnFlyIdx;
+
+ SwFlyCache* pFlyC;
+ while( nIdx < nFlyCount &&
+ ( pFlyC = &mpImpl->GetFlyCache( nIdx ) )->nPageNum == nPgNum )
+ {
+ aFlyCacheSet.insert( pFlyC );
+ ++nIdx;
+ }
+
+ // sort objects on this page by ordnum
+ std::set< const SdrObject*, SdrObjectCompare > aFlySet;
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if (SwFlyFrame *pFly = dynamic_cast<SwFlyFrame*>(pAnchoredObj)) // a text frame?
+ {
+ if( pFly->GetAnchorFrame() &&
+ !pFly->GetAnchorFrame()->FindFooterOrHeader() )
+ {
+ const SwContact *pC = ::GetUserCall( pAnchoredObj->GetDrawObj() );
+ if( pC )
+ {
+ aFlySet.insert( pAnchoredObj->GetDrawObj() );
+ }
+ }
+ }
+ }
+
+ if ( aFlyCacheSet.size() == aFlySet.size() )
+ {
+ std::set< const SdrObject*, SdrObjectCompare >::iterator aFlySetIt =
+ aFlySet.begin();
+
+ for ( const SwFlyCache* pFlyCache : aFlyCacheSet )
+ {
+ SwFlyFrame* pFly = const_cast<SwVirtFlyDrawObj*>(static_cast<const SwVirtFlyDrawObj*>(*aFlySetIt))->GetFlyFrame();
+
+ if ( pFly->getFrameArea().Left() == FAR_AWAY )
+ {
+ // we get the stored information
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFly);
+ aFrm.Pos().setX( pFlyCache->Left() + pPage->getFrameArea().Left() );
+ aFrm.Pos().setY( pFlyCache->Top() + pPage->getFrameArea().Top() );
+
+ if ( mpImpl->IsUseFlyCache() )
+ {
+ aFrm.Width( pFlyCache->Width() );
+ aFrm.Height( pFlyCache->Height() );
+ }
+ }
+
+ ++aFlySetIt;
+ }
+ }
+ }
+}
+
+SwLayCacheIoImpl::SwLayCacheIoImpl( SvStream& rStrm, bool bWrtMd ) :
+ pStream( &rStrm ),
+ nFlagRecEnd ( 0 ),
+ nMajorVersion(SW_LAYCACHE_IO_VERSION_MAJOR),
+ nMinorVersion(SW_LAYCACHE_IO_VERSION_MINOR),
+ bWriteMode( bWrtMd ),
+ bError( false )
+{
+ if( bWriteMode )
+ pStream->WriteUInt16( nMajorVersion )
+ .WriteUInt16( nMinorVersion );
+
+ else
+ pStream->ReadUInt16( nMajorVersion )
+ .ReadUInt16( nMinorVersion );
+}
+
+void SwLayCacheIoImpl::OpenRec( sal_uInt8 cType )
+{
+ sal_uInt32 nPos = pStream->Tell();
+ if( bWriteMode )
+ {
+ aRecords.emplace_back(cType, nPos );
+ pStream->WriteUInt32( 0 );
+ }
+ else
+ {
+ sal_uInt32 nVal(0);
+ pStream->ReadUInt32( nVal );
+ sal_uInt8 cRecTyp = static_cast<sal_uInt8>(nVal);
+ if (!nVal || cRecTyp != cType || !pStream->good())
+ {
+ OSL_ENSURE( nVal, "OpenRec: Record-Header is 0" );
+ OSL_ENSURE( cRecTyp == cType, "OpenRec: Wrong Record Type" );
+ aRecords.emplace_back(0, pStream->Tell() );
+ bError = true;
+ }
+ else
+ {
+ sal_uInt32 nSize = nVal >> 8;
+ aRecords.emplace_back(cRecTyp, nPos+nSize );
+ }
+ }
+}
+
+// Close record
+void SwLayCacheIoImpl::CloseRec()
+{
+ bool bRes = true;
+ OSL_ENSURE( !aRecords.empty(), "CloseRec: no levels" );
+ if( !aRecords.empty() )
+ {
+ sal_uInt32 nPos = pStream->Tell();
+ if( bWriteMode )
+ {
+ sal_uInt32 nBgn = aRecords.back().size;
+ pStream->Seek( nBgn );
+ sal_uInt32 nSize = nPos - nBgn;
+ sal_uInt32 nVal = ( nSize << 8 ) | aRecords.back().type;
+ pStream->WriteUInt32( nVal );
+ pStream->Seek( nPos );
+ if( pStream->GetError() != ERRCODE_NONE )
+ bRes = false;
+ }
+ else
+ {
+ sal_uInt32 n = aRecords.back().size;
+ OSL_ENSURE( n >= nPos, "CloseRec: too much data read" );
+ if( n != nPos )
+ {
+ pStream->Seek( n );
+ if( n < nPos )
+ bRes = false;
+ }
+ if( pStream->GetErrorCode() != ERRCODE_NONE )
+ bRes = false;
+ }
+ aRecords.pop_back();
+ }
+
+ if( !bRes )
+ bError = true;
+}
+
+sal_uInt32 SwLayCacheIoImpl::BytesLeft()
+{
+ sal_uInt32 n = 0;
+ if( !bError && !aRecords.empty() )
+ {
+ sal_uInt32 nEndPos = aRecords.back().size;
+ sal_uInt32 nPos = pStream->Tell();
+ if( nEndPos > nPos )
+ n = nEndPos - nPos;
+ }
+ return n;
+}
+
+sal_uInt8 SwLayCacheIoImpl::Peek()
+{
+ sal_uInt8 c(0);
+ if( !bError )
+ {
+ sal_uInt32 nPos = pStream->Tell();
+ pStream->ReadUChar( c );
+ pStream->Seek( nPos );
+ if( pStream->GetErrorCode() != ERRCODE_NONE )
+ {
+ c = 0;
+ bError = true;
+ }
+ }
+ return c;
+}
+
+void SwLayCacheIoImpl::SkipRec()
+{
+ sal_uInt8 c = Peek();
+ OpenRec( c );
+ pStream->Seek( aRecords.back().size );
+ CloseRec();
+}
+
+sal_uInt8 SwLayCacheIoImpl::OpenFlagRec()
+{
+ OSL_ENSURE( !bWriteMode, "OpenFlagRec illegal in write mode" );
+ sal_uInt8 cFlags(0);
+ pStream->ReadUChar( cFlags );
+ nFlagRecEnd = pStream->Tell() + ( cFlags & 0x0F );
+ return (cFlags >> 4);
+}
+
+void SwLayCacheIoImpl::OpenFlagRec( sal_uInt8 nFlags, sal_uInt8 nLen )
+{
+ OSL_ENSURE( bWriteMode, "OpenFlagRec illegal in read mode" );
+ OSL_ENSURE( (nFlags & 0xF0) == 0, "illegal flags set" );
+ OSL_ENSURE( nLen < 16, "wrong flag record length" );
+ sal_uInt8 cFlags = (nFlags << 4) + nLen;
+ pStream->WriteUChar( cFlags );
+ nFlagRecEnd = pStream->Tell() + nLen;
+}
+
+void SwLayCacheIoImpl::CloseFlagRec()
+{
+ if( bWriteMode )
+ {
+ OSL_ENSURE( pStream->Tell() == nFlagRecEnd, "Wrong amount of data written" );
+ }
+ else
+ {
+ OSL_ENSURE( pStream->Tell() <= nFlagRecEnd, "Too many data read" );
+ if( pStream->Tell() != nFlagRecEnd )
+ pStream->Seek( nFlagRecEnd );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */