Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / layout / laycache.cxx
blobe5b1dc34146dc49f63c6d3428a473beac8839b59
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <editeng/formatbreakitem.hxx>
21 #include <sal/log.hxx>
22 #include <osl/diagnose.h>
23 #include <tools/stream.hxx>
24 #include <doc.hxx>
25 #include <IDocumentStatistics.hxx>
26 #include <IDocumentLayoutAccess.hxx>
27 #include <docstat.hxx>
28 #include <fmtpdsc.hxx>
29 #include <laycache.hxx>
30 #include "layhelp.hxx"
31 #include <pagefrm.hxx>
32 #include <rootfrm.hxx>
33 #include <txtfrm.hxx>
34 #include <swtable.hxx>
35 #include <tabfrm.hxx>
36 #include <rowfrm.hxx>
37 #include <sectfrm.hxx>
38 #include <fmtcntnt.hxx>
39 #include <pagedesc.hxx>
40 #include <frmtool.hxx>
41 #include <dflyobj.hxx>
42 #include <dcontact.hxx>
43 #include <viewopt.hxx>
44 #include <flyfrm.hxx>
45 #include <sortedobjs.hxx>
46 #include <ndindex.hxx>
47 #include <node.hxx>
48 #include <ndtxt.hxx>
49 #include <frameformats.hxx>
51 #include <limits>
53 using namespace ::com::sun::star;
55 SwLayoutCache::SwLayoutCache() : m_nLockCount( 0 ) {}
58 * Reading and writing of the layout cache.
59 * The layout cache is not necessary, but it improves
60 * the performance and reduces the text flow during
61 * the formatting.
62 * The layout cache contains the index of the paragraphs/tables
63 * at the top of every page, so it's possible to create
64 * the right count of pages and to distribute the document content
65 * to this pages before the formatting starts.
68 void SwLayoutCache::Read( SvStream &rStream )
70 if( !m_pImpl )
72 m_pImpl.reset( new SwLayCacheImpl );
73 if( !m_pImpl->Read( rStream ) )
75 m_pImpl.reset();
80 void SwLayCacheImpl::Insert( sal_uInt16 nType, SwNodeOffset nIndex, sal_Int32 nOffset )
82 m_aType.push_back( nType );
83 mIndices.push_back( nIndex );
84 m_aOffset.push_back( nOffset );
87 bool SwLayCacheImpl::Read( SvStream& rStream )
89 SwLayCacheIoImpl aIo( rStream, false );
90 if( aIo.GetMajorVersion() > SW_LAYCACHE_IO_VERSION_MAJOR )
91 return false;
93 // Due to an evil bug in the layout cache (#102759#), we cannot trust the
94 // sizes of fly frames which have been written using the "old" layout cache.
95 // This flag should indicate that we do not want to trust the width and
96 // height of fly frames
97 m_bUseFlyCache = aIo.GetMinorVersion() >= 1;
99 aIo.OpenRec( SW_LAYCACHE_IO_REC_PAGES );
100 aIo.OpenFlagRec();
101 aIo.CloseFlagRec();
102 while( aIo.BytesLeft() && !aIo.HasError() )
104 sal_uInt32 nIndex(0), nOffset(0);
106 switch( aIo.Peek() )
108 case SW_LAYCACHE_IO_REC_PARA:
110 aIo.OpenRec( SW_LAYCACHE_IO_REC_PARA );
111 sal_uInt8 cFlags = aIo.OpenFlagRec();
112 aIo.GetStream().ReadUInt32( nIndex );
113 if( (cFlags & 0x01) != 0 )
114 aIo.GetStream().ReadUInt32( nOffset );
115 else
116 nOffset = COMPLETE_STRING;
117 aIo.CloseFlagRec();
118 Insert( SW_LAYCACHE_IO_REC_PARA, SwNodeOffset(nIndex), static_cast<sal_Int32>(nOffset) );
119 aIo.CloseRec();
120 break;
122 case SW_LAYCACHE_IO_REC_TABLE:
123 aIo.OpenRec( SW_LAYCACHE_IO_REC_TABLE );
124 aIo.OpenFlagRec();
125 aIo.GetStream().ReadUInt32( nIndex )
126 .ReadUInt32( nOffset );
127 Insert( SW_LAYCACHE_IO_REC_TABLE, SwNodeOffset(nIndex), static_cast<sal_Int32>(nOffset) );
128 aIo.CloseFlagRec();
129 aIo.CloseRec();
130 break;
131 case SW_LAYCACHE_IO_REC_FLY:
133 aIo.OpenRec( SW_LAYCACHE_IO_REC_FLY );
134 aIo.OpenFlagRec();
135 aIo.CloseFlagRec();
136 sal_Int32 nX(0), nY(0), nW(0), nH(0);
137 sal_uInt16 nPgNum(0);
138 aIo.GetStream().ReadUInt16( nPgNum ).ReadUInt32( nIndex )
139 .ReadInt32( nX ).ReadInt32( nY ).ReadInt32( nW ).ReadInt32( nH );
140 m_FlyCache.emplace_back( nPgNum, nIndex, nX, nY, nW, nH );
141 aIo.CloseRec();
142 break;
144 default:
145 aIo.SkipRec();
146 break;
149 aIo.CloseRec();
151 return !aIo.HasError();
154 /** writes the index (more precise: the difference between
155 * the index and the first index of the document content)
156 * of the first paragraph/table at the top of every page.
157 * If at the top of a page is the rest of a paragraph/table
158 * from the bottom of the previous page, the character/row
159 * number is stored, too.
160 * The position, size and page number of the text frames
161 * are stored, too
163 void SwLayoutCache::Write( SvStream &rStream, const SwDoc& rDoc )
165 if( !rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ) // the layout itself ..
166 return;
168 SwLayCacheIoImpl aIo( rStream, true );
169 // We want to save the relative index, so we need the index
170 // of the first content
171 SwNodeOffset nStartOfContent = rDoc.GetNodes().GetEndOfContent().
172 StartOfSectionNode()->GetIndex();
173 // The first page...
174 SwPageFrame* pPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->Lower()));
176 aIo.OpenRec( SW_LAYCACHE_IO_REC_PAGES );
177 aIo.OpenFlagRec( 0, 0 );
178 aIo.CloseFlagRec();
179 while( pPage )
181 if( pPage->GetPrev() )
183 SwLayoutFrame* pLay = pPage->FindBodyCont();
184 SwFrame* pTmp = pLay ? pLay->ContainsAny() : nullptr;
185 // We are only interested in paragraph or table frames,
186 // a section frames contains paragraphs/tables.
187 if( pTmp && pTmp->IsSctFrame() )
188 pTmp = static_cast<SwSectionFrame*>(pTmp)->ContainsAny();
190 if( pTmp ) // any content
192 if( pTmp->IsTextFrame() )
194 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTmp));
195 assert(!pFrame->GetMergedPara());
196 SwNodeOffset nNdIdx = pFrame->GetTextNodeFirst()->GetIndex();
197 if( nNdIdx > nStartOfContent )
199 /* Open Paragraph Record */
200 aIo.OpenRec( SW_LAYCACHE_IO_REC_PARA );
201 bool bFollow = static_cast<SwTextFrame*>(pTmp)->IsFollow();
202 aIo.OpenFlagRec( bFollow ? 0x01 : 0x00,
203 bFollow ? 8 : 4 );
204 nNdIdx -= nStartOfContent;
205 aIo.GetStream().WriteUInt32( sal_Int32(nNdIdx) );
206 if( bFollow )
207 aIo.GetStream().WriteUInt32( sal_Int32(static_cast<SwTextFrame*>(pTmp)->GetOffset()) );
208 aIo.CloseFlagRec();
209 /* Close Paragraph Record */
210 aIo.CloseRec();
213 else if( pTmp->IsTabFrame() )
215 SwTabFrame* pTab = static_cast<SwTabFrame*>(pTmp);
216 sal_uLong nOfst = COMPLETE_STRING;
217 if( pTab->IsFollow() )
219 // If the table is a follow, we have to look for the
220 // master and to count all rows to get the row number
221 nOfst = 0;
222 if( pTab->IsFollow() )
223 pTab = pTab->FindMaster( true );
224 while( pTab != pTmp )
226 SwFrame* pSub = pTab->Lower();
227 while( pSub )
229 ++nOfst;
230 pSub = pSub->GetNext();
232 pTab = pTab->GetFollow();
233 assert(pTab && "Table follow without master");
236 while (true)
238 SwNodeOffset nNdIdx =
239 pTab->GetTable()->GetTableNode()->GetIndex();
240 if( nNdIdx > nStartOfContent )
242 /* Open Table Record */
243 aIo.OpenRec( SW_LAYCACHE_IO_REC_TABLE );
244 aIo.OpenFlagRec( 0, 8 );
245 nNdIdx -= nStartOfContent;
246 aIo.GetStream().WriteUInt32( sal_Int32(nNdIdx) )
247 .WriteUInt32( nOfst );
248 aIo.CloseFlagRec();
249 /* Close Table Record */
250 aIo.CloseRec();
252 // If the table has a follow on the next page,
253 // we know already the row number and store this
254 // immediately.
255 if( pTab->GetFollow() )
257 if( nOfst == sal_uLong(COMPLETE_STRING) )
258 nOfst = 0;
261 SwFrame* pSub = pTab->Lower();
262 while( pSub )
264 ++nOfst;
265 pSub = pSub->GetNext();
267 pTab = pTab->GetFollow();
268 SwPageFrame *pTabPage = pTab->FindPageFrame();
269 if( pTabPage != pPage )
271 OSL_ENSURE( pPage->GetPhyPageNum() <
272 pTabPage->GetPhyPageNum(),
273 "Looping Tableframes" );
274 pPage = pTabPage;
275 break;
277 } while ( pTab->GetFollow() );
279 else
280 break;
285 if( pPage->GetSortedObjs() )
287 SwSortedObjs &rObjs = *pPage->GetSortedObjs();
288 for (SwAnchoredObject* pAnchoredObj : rObjs)
290 if (SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame())
292 if( pFly->getFrameArea().Left() != FAR_AWAY &&
293 !pFly->GetAnchorFrame()->FindFooterOrHeader() )
295 const SwContact *pC =
296 ::GetUserCall(pAnchoredObj->GetDrawObj());
297 if( pC )
299 sal_uInt32 nOrdNum = pAnchoredObj->GetDrawObj()->GetOrdNum();
300 sal_uInt16 nPageNum = pPage->GetPhyPageNum();
301 /* Open Fly Record */
302 aIo.OpenRec( SW_LAYCACHE_IO_REC_FLY );
303 aIo.OpenFlagRec( 0, 0 );
304 aIo.CloseFlagRec();
305 const SwRect& rRct = pFly->getFrameArea();
306 sal_Int32 nX = rRct.Left() - pPage->getFrameArea().Left();
307 sal_Int32 nY = rRct.Top() - pPage->getFrameArea().Top();
308 aIo.GetStream().WriteUInt16( nPageNum ).WriteUInt32( nOrdNum )
309 .WriteInt32( nX ).WriteInt32( nY )
310 .WriteInt32( rRct.Width() )
311 .WriteInt32( rRct.Height() );
312 /* Close Fly Record */
313 aIo.CloseRec();
319 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
321 aIo.CloseRec();
324 #ifdef DBG_UTIL
325 bool SwLayoutCache::CompareLayout( const SwDoc& rDoc ) const
327 if( !m_pImpl )
328 return true;
329 const SwRootFrame *pRootFrame = rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
330 if( pRootFrame )
332 size_t nIndex = 0;
333 SwNodeOffset nStartOfContent = rDoc.GetNodes().GetEndOfContent().
334 StartOfSectionNode()->GetIndex();
335 const SwPageFrame* pPage = static_cast<const SwPageFrame*>(pRootFrame->Lower());
336 if( pPage )
337 pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
338 while( pPage )
340 if( nIndex >= m_pImpl->size() )
341 return false;
343 const SwLayoutFrame* pLay = pPage->FindBodyCont();
344 const SwFrame* pTmp = pLay ? pLay->ContainsAny() : nullptr;
345 if( pTmp && pTmp->IsSctFrame() )
346 pTmp = static_cast<const SwSectionFrame*>(pTmp)->ContainsAny();
347 if( pTmp )
349 if( pTmp->IsTextFrame() )
352 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTmp));
353 assert(!pFrame->GetMergedPara());
354 SwNodeOffset nNdIdx = pFrame->GetTextNodeFirst()->GetIndex();
355 if( nNdIdx > nStartOfContent )
357 bool bFollow = static_cast<const SwTextFrame*>(pTmp)->IsFollow();
358 nNdIdx -= nStartOfContent;
359 if( m_pImpl->GetBreakIndex( nIndex ) != nNdIdx ||
360 SW_LAYCACHE_IO_REC_PARA !=
361 m_pImpl->GetBreakType( nIndex ) ||
362 (bFollow
363 ? sal_Int32(static_cast<const SwTextFrame*>(pTmp)->GetOffset())
364 : COMPLETE_STRING) != m_pImpl->GetBreakOfst(nIndex))
366 return false;
368 ++nIndex;
371 else if( pTmp->IsTabFrame() )
373 const SwTabFrame* pTab = static_cast<const SwTabFrame*>(pTmp);
374 sal_Int32 nOfst = COMPLETE_STRING;
375 if( pTab->IsFollow() )
377 nOfst = 0;
378 if( pTab->IsFollow() )
379 pTab = pTab->FindMaster( true );
380 while( pTab != pTmp )
382 const SwFrame* pSub = pTab->Lower();
383 while( pSub )
385 ++nOfst;
386 pSub = pSub->GetNext();
388 pTab = pTab->GetFollow();
393 SwNodeOffset nNdIdx =
394 pTab->GetTable()->GetTableNode()->GetIndex();
395 if( nNdIdx > nStartOfContent )
397 nNdIdx -= nStartOfContent;
398 if( m_pImpl->GetBreakIndex( nIndex ) != nNdIdx ||
399 SW_LAYCACHE_IO_REC_TABLE !=
400 m_pImpl->GetBreakType( nIndex ) ||
401 nOfst != m_pImpl->GetBreakOfst( nIndex ) )
403 return false;
405 ++nIndex;
407 if( pTab->GetFollow() )
409 if( nOfst == COMPLETE_STRING )
410 nOfst = 0;
413 const SwFrame* pSub = pTab->Lower();
414 while( pSub )
416 ++nOfst;
417 pSub = pSub->GetNext();
419 pTab = pTab->GetFollow();
420 const SwPageFrame *pTabPage = pTab->FindPageFrame();
421 if( pTabPage != pPage )
423 pPage = pTabPage;
424 break;
426 } while ( pTab->GetFollow() );
428 else
429 break;
430 } while( pTab );
433 pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
436 return true;
438 #endif
440 void SwLayoutCache::ClearImpl()
442 if( !IsLocked() )
444 m_pImpl.reset();
448 SwLayoutCache::~SwLayoutCache()
450 OSL_ENSURE( !m_nLockCount, "Deleting a locked SwLayoutCache!?" );
453 /// helper class to create not nested section frames for nested sections.
454 SwActualSection::SwActualSection( SwActualSection *pUp,
455 SwSectionFrame *pSect,
456 SwSectionNode *pNd ) :
457 m_pUpper( pUp ),
458 m_pSectFrame( pSect ),
459 m_pSectNode( pNd )
461 if ( !m_pSectNode )
463 const SwNodeIndex *pIndex = pSect->GetFormat()->GetContent().GetContentIdx();
464 m_pSectNode = pIndex->GetNode().FindSectionNode();
468 namespace {
470 bool sanityCheckLayoutCache(SwLayCacheImpl const& rCache,
471 SwNodes const& rNodes, SwNodeOffset nNodeIndex)
473 auto const nStartOfContent(rNodes.GetEndOfContent().StartOfSectionNode()->GetIndex());
474 nNodeIndex -= nStartOfContent;
475 auto const nMaxIndex(rNodes.GetEndOfContent().GetIndex() - nStartOfContent);
476 for (size_t nIndex = 0; nIndex < rCache.size(); ++nIndex)
478 auto const nBreakIndex(rCache.GetBreakIndex(nIndex));
479 if (nBreakIndex < nNodeIndex || nMaxIndex <= nBreakIndex)
481 SAL_WARN("sw.layout",
482 "invalid node index in layout-cache: " << nBreakIndex);
483 return false;
485 auto const nBreakType(rCache.GetBreakType(nIndex));
486 switch (nBreakType)
488 case SW_LAYCACHE_IO_REC_PARA:
489 if (!rNodes[nBreakIndex + nStartOfContent]->IsTextNode())
491 SAL_WARN("sw.layout",
492 "invalid node of type 'P' in layout-cache");
493 return false;
495 break;
496 case SW_LAYCACHE_IO_REC_TABLE:
497 if (!rNodes[nBreakIndex + nStartOfContent]->IsTableNode())
499 SAL_WARN("sw.layout",
500 "invalid node of type 'T' in layout-cache");
501 return false;
503 break;
504 default:
505 assert(false); // Read shouldn't have inserted that
508 return true;
511 } // namespace
513 /** helper class, which utilizes the layout cache information
514 * to distribute the document content to the right pages.
515 * It's used by the InsertCnt_(..)-function.
516 * If there's no layout cache, the distribution to the pages is more
517 * a guess, but a guess with statistical background.
519 SwLayHelper::SwLayHelper( SwDoc *pD, SwFrame* &rpF, SwFrame* &rpP, SwPageFrame* &rpPg,
520 SwLayoutFrame* &rpL, std::unique_ptr<SwActualSection> &rpA,
521 SwNodeOffset nNodeIndex, bool bCache )
522 : mrpFrame( rpF )
523 , mrpPrv( rpP )
524 , mrpPage( rpPg )
525 , mrpLay( rpL )
526 , mrpActualSection( rpA )
527 , mbBreakAfter(false)
528 , mpDoc(pD)
529 , mnMaxParaPerPage( 25 )
530 , mnParagraphCnt( bCache ? 0 : USHRT_MAX )
531 , mnFlyIdx( 0 )
532 , mbFirst( bCache )
534 mpImpl = mpDoc->GetLayoutCache() ? mpDoc->GetLayoutCache()->LockImpl() : nullptr;
535 if( mpImpl )
537 SwNodes const& rNodes(mpDoc->GetNodes());
538 if (sanityCheckLayoutCache(*mpImpl, rNodes, nNodeIndex))
540 mnIndex = 0;
541 mnStartOfContent = rNodes.GetEndOfContent().StartOfSectionNode()->GetIndex();
542 mnMaxParaPerPage = 1000;
544 else
546 mpDoc->GetLayoutCache()->UnlockImpl();
547 mpImpl = nullptr;
548 mnIndex = std::numeric_limits<size_t>::max();
549 mnStartOfContent = SwNodeOffset(USHRT_MAX);
552 else
554 mnIndex = std::numeric_limits<size_t>::max();
555 mnStartOfContent = NODE_OFFSET_MAX;
559 SwLayHelper::~SwLayHelper()
561 if( mpImpl )
563 OSL_ENSURE( mpDoc && mpDoc->GetLayoutCache(), "Missing layoutcache" );
564 mpDoc->GetLayoutCache()->UnlockImpl();
568 /** Does NOT really calculate the page count,
569 * it returns the page count value from the layout cache, if available,
570 * otherwise it estimates the page count.
572 sal_uLong SwLayHelper::CalcPageCount()
574 sal_uLong nPgCount;
575 SwLayCacheImpl *pCache = mpDoc->GetLayoutCache() ?
576 mpDoc->GetLayoutCache()->LockImpl() : nullptr;
577 if( pCache )
579 nPgCount = pCache->size() + 1;
580 mpDoc->GetLayoutCache()->UnlockImpl();
582 else
584 nPgCount = mpDoc->getIDocumentStatistics().GetDocStat().nPage;
585 if ( nPgCount <= 10 ) // no page insertion for less than 10 pages
586 nPgCount = 0;
587 sal_Int32 nNdCount = mpDoc->getIDocumentStatistics().GetDocStat().nPara;
588 if ( nNdCount <= 1 )
590 //Estimates the number of paragraphs.
591 SwNodeOffset nTmp = mpDoc->GetNodes().GetEndOfContent().GetIndex() -
592 mpDoc->GetNodes().GetEndOfExtras().GetIndex();
593 //Tables have a little overhead...
594 nTmp -= SwNodeOffset(mpDoc->GetTableFrameFormats()->size() * 25);
595 //Fly frames, too ..
596 nTmp -= (mpDoc->GetNodes().GetEndOfAutotext().GetIndex() -
597 mpDoc->GetNodes().GetEndOfInserts().GetIndex()) / SwNodeOffset(3 * 5);
598 if ( nTmp > SwNodeOffset(0) )
599 nNdCount = sal_Int32(nTmp);
601 if ( nNdCount > 100 ) // no estimation below this value
603 if ( nPgCount > 0 )
604 { // tdf#129529 avoid 0...
605 mnMaxParaPerPage = std::max<sal_uLong>(3, nNdCount / nPgCount);
607 else
609 mnMaxParaPerPage = std::max( sal_uLong(20),
610 sal_uLong(20 + nNdCount / 1000 * 3) );
611 const sal_uLong nMax = 53;
612 mnMaxParaPerPage = std::min( mnMaxParaPerPage, nMax );
613 nPgCount = nNdCount / mnMaxParaPerPage;
615 if ( nNdCount < 1000 )
616 nPgCount = 0;// no progress bar for small documents
617 SwViewShell *pSh = nullptr;
618 if( mrpLay && mrpLay->getRootFrame() )
619 pSh = mrpLay->getRootFrame()->GetCurrShell();
620 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
621 mnMaxParaPerPage *= 6;
624 return nPgCount;
628 * inserts a page and return true, if
629 * - the break after flag is set
630 * - the actual content wants a break before
631 * - the maximum count of paragraph/rows is reached
633 * The break after flag is set, if the actual content
634 * wants a break after.
636 bool SwLayHelper::CheckInsertPage()
638 bool bEnd = nullptr == mrpPage->GetNext();
639 const SvxFormatBreakItem& rBrk = mrpFrame->GetBreakItem();
640 const SwFormatPageDesc& rDesc = mrpFrame->GetPageDescItem();
641 // #118195# Do not evaluate page description if frame
642 // is a follow frame!
643 const SwPageDesc* pDesc = mrpFrame->IsFlowFrame() &&
644 SwFlowFrame::CastFlowFrame( mrpFrame )->IsFollow() ?
645 nullptr :
646 rDesc.GetPageDesc();
648 bool bBrk = mnParagraphCnt > mnMaxParaPerPage || mbBreakAfter;
649 mbBreakAfter = rBrk.GetBreak() == SvxBreak::PageAfter ||
650 rBrk.GetBreak() == SvxBreak::PageBoth;
651 if ( !bBrk )
652 bBrk = rBrk.GetBreak() == SvxBreak::PageBefore ||
653 rBrk.GetBreak() == SvxBreak::PageBoth;
655 if ( bBrk || pDesc )
657 ::std::optional<sal_uInt16> oPgNum;
658 if ( !pDesc )
660 pDesc = mrpPage->GetPageDesc()->GetFollow();
662 else
664 oPgNum = rDesc.GetNumOffset();
665 if ( oPgNum )
666 static_cast<SwRootFrame*>(mrpPage->GetUpper())->SetVirtPageNum(true);
668 bool bNextPageRight = !mrpPage->OnRightPage();
669 bool bInsertEmpty = false;
670 assert(mrpPage->GetUpper()->GetLower());
671 if (oPgNum && bNextPageRight != IsRightPageByNumber(
672 *static_cast<SwRootFrame*>(mrpPage->GetUpper()), *oPgNum))
674 bNextPageRight = !bNextPageRight;
675 bInsertEmpty = true;
677 // If the page style is changing, we'll have a first page.
678 bool bNextPageFirst = pDesc != mrpPage->GetPageDesc();
679 ::InsertNewPage( const_cast<SwPageDesc&>(*pDesc), mrpPage->GetUpper(),
680 bNextPageRight, bNextPageFirst, bInsertEmpty, false, mrpPage->GetNext());
681 if ( bEnd )
683 OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
685 { mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
686 } while ( mrpPage->GetNext() );
688 else
690 OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
691 mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
692 if ( mrpPage->IsEmptyPage() )
694 OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
695 mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
698 mrpLay = mrpPage->FindBodyCont();
699 while( mrpLay->Lower() )
700 mrpLay = static_cast<SwLayoutFrame*>(mrpLay->Lower());
701 return true;
703 return false;
706 /** entry point for the InsertCnt_-function.
707 * The document content index is checked either it is
708 * in the layout cache either it's time to insert a page
709 * cause the maximal estimation of content per page is reached.
710 * A really big table or long paragraph may contains more than
711 * one page, in this case the needed count of pages will inserted.
713 bool SwLayHelper::CheckInsert( SwNodeOffset nNodeIndex )
715 bool bRet = false;
716 bool bLongTab = false;
717 sal_uLong nMaxRowPerPage( 0 );
718 nNodeIndex -= mnStartOfContent;
719 sal_uInt16 nRows( 0 );
720 if( mrpFrame->IsTabFrame() )
722 //Inside a table counts every row as a paragraph
723 SwFrame *pLow = static_cast<SwTabFrame*>(mrpFrame)->Lower();
724 nRows = 0;
727 ++nRows;
728 pLow = pLow->GetNext();
729 } while ( pLow );
730 mnParagraphCnt += nRows;
731 if( !mpImpl && mnParagraphCnt > mnMaxParaPerPage + 10 )
733 // OD 09.04.2003 #108698# - improve heuristics:
734 // Assume that a table, which has more than three times the quantity
735 // of maximal paragraphs per page rows, consists of rows, which have
736 // the height of a normal paragraph. Thus, allow as much rows per page
737 // as much paragraphs are allowed.
738 if ( nRows > ( 3*mnMaxParaPerPage ) )
740 nMaxRowPerPage = mnMaxParaPerPage;
742 else
744 SwFrame *pTmp = static_cast<SwTabFrame*>(mrpFrame)->Lower();
745 if( pTmp->GetNext() )
746 pTmp = pTmp->GetNext();
747 pTmp = static_cast<SwRowFrame*>(pTmp)->Lower();
748 sal_uInt16 nCnt = 0;
751 ++nCnt;
752 pTmp = pTmp->GetNext();
753 } while( pTmp );
754 nMaxRowPerPage = std::max( sal_uLong(2), mnMaxParaPerPage / nCnt );
756 bLongTab = true;
759 else
760 ++mnParagraphCnt;
761 if( mbFirst && mpImpl && mnIndex < mpImpl->size() &&
762 mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex &&
763 ( mpImpl->GetBreakOfst( mnIndex ) < COMPLETE_STRING ||
764 ( ++mnIndex < mpImpl->size() &&
765 mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex ) ) )
766 mbFirst = false;
767 // OD 09.04.2003 #108698# - always split a big tables.
768 if ( !mbFirst ||
769 ( mrpFrame->IsTabFrame() && bLongTab )
772 sal_Int32 nRowCount = 0;
775 if( mpImpl || bLongTab )
777 sal_Int32 nOfst = COMPLETE_STRING;
778 sal_uInt16 nType = SW_LAYCACHE_IO_REC_PAGES;
779 if( bLongTab )
781 mbBreakAfter = true;
782 nOfst = static_cast<sal_Int32>(nRowCount + nMaxRowPerPage);
784 else
786 while( mnIndex < mpImpl->size() &&
787 mpImpl->GetBreakIndex(mnIndex) < nNodeIndex)
788 ++mnIndex;
789 if( mnIndex < mpImpl->size() &&
790 mpImpl->GetBreakIndex(mnIndex) == nNodeIndex )
792 nType = mpImpl->GetBreakType( mnIndex );
793 nOfst = mpImpl->GetBreakOfst( mnIndex++ );
794 mbBreakAfter = true;
798 if( nOfst < COMPLETE_STRING )
800 bool bSplit = false;
801 sal_uInt16 nRepeat( 0 );
802 if( !bLongTab && mrpFrame->IsTextFrame() &&
803 SW_LAYCACHE_IO_REC_PARA == nType &&
804 nOfst < static_cast<SwTextFrame*>(mrpFrame)->GetText().getLength())
805 bSplit = true;
806 else if( mrpFrame->IsTabFrame() && nRowCount < nOfst &&
807 ( bLongTab || SW_LAYCACHE_IO_REC_TABLE == nType ) )
809 nRepeat = static_cast<SwTabFrame*>(mrpFrame)->
810 GetTable()->GetRowsToRepeat();
811 bSplit = nOfst < nRows && nRowCount + nRepeat < nOfst;
812 bLongTab = bLongTab && bSplit;
814 if( bSplit )
816 mrpFrame->InsertBehind( mrpLay, mrpPrv );
819 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mrpFrame);
820 aFrm.Pos() = mrpLay->getFrameArea().Pos();
821 aFrm.Pos().AdjustY(1 );
824 mrpPrv = mrpFrame;
825 if( mrpFrame->IsTabFrame() )
827 SwTabFrame* pTab = static_cast<SwTabFrame*>(mrpFrame);
828 // #i33629#, #i29955#
829 ::RegistFlys( pTab->FindPageFrame(), pTab );
830 SwFrame *pRow = pTab->Lower();
831 SwTabFrame *pFoll = new SwTabFrame( *pTab );
833 SwFrame *pPrv;
834 if( nRepeat > 0 )
836 sw::FlyCreationSuppressor aSuppressor;
837 // Insert new headlines:
838 sal_uInt16 nRowIdx = 0;
839 SwRowFrame* pHeadline = nullptr;
840 while( nRowIdx < nRepeat )
842 OSL_ENSURE( pTab->GetTable()->GetTabLines()[ nRowIdx ], "Table without rows?" );
843 pHeadline =
844 new SwRowFrame( *pTab->GetTable()->GetTabLines()[ nRowIdx ], pTab );
845 pHeadline->SetRepeatedHeadline( true );
846 pHeadline->InsertBefore( pFoll, nullptr );
847 pHeadline->RegistFlys();
849 ++nRowIdx;
851 pPrv = pHeadline;
852 nRows = nRows + nRepeat;
854 else
855 pPrv = nullptr;
856 while( pRow && nRowCount < nOfst )
858 pRow = pRow->GetNext();
859 ++nRowCount;
861 while ( pRow )
863 SwFrame* pNxt = pRow->GetNext();
864 pRow->RemoveFromLayout();
865 pRow->InsertBehind( pFoll, pPrv );
866 pPrv = pRow;
867 pRow = pNxt;
869 mrpFrame = pFoll;
871 else
873 SwTextFrame *const pNew = static_cast<SwTextFrame*>(
874 static_cast<SwTextFrame*>(mrpFrame)
875 ->GetTextNodeFirst()->MakeFrame(mrpFrame));
876 pNew->ManipOfst( TextFrameIndex(nOfst) );
877 pNew->SetFollow( static_cast<SwTextFrame*>(mrpFrame)->GetFollow() );
878 static_cast<SwTextFrame*>(mrpFrame)->SetFollow( pNew );
879 mrpFrame = pNew;
885 SwPageFrame* pLastPage = mrpPage;
886 if( CheckInsertPage() )
888 CheckFlyCache_( pLastPage );
889 if( mrpPrv && mrpPrv->IsTextFrame() && !mrpPrv->isFrameAreaSizeValid() )
891 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mrpPrv);
892 aFrm.Height( mrpPrv->GetUpper()->getFramePrintArea().Height() );
895 bRet = true;
896 mrpPrv = nullptr;
897 mnParagraphCnt = 0;
899 if ( mrpActualSection )
901 //Did the SectionFrame even have a content? If not, we can
902 //directly put it somewhere else
903 SwSectionFrame *pSct;
904 bool bInit = false;
905 if ( !mrpActualSection->GetSectionFrame()->ContainsContent())
907 pSct = mrpActualSection->GetSectionFrame();
908 pSct->RemoveFromLayout();
910 else
912 pSct = new SwSectionFrame(
913 *mrpActualSection->GetSectionFrame(), false );
914 mrpActualSection->GetSectionFrame()->SimpleFormat();
915 bInit = true;
917 mrpActualSection->SetSectionFrame( pSct );
918 pSct->InsertBehind( mrpLay, nullptr );
920 if( bInit )
922 pSct->Init();
926 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pSct);
927 aFrm.Pos() = mrpLay->getFrameArea().Pos();
928 aFrm.Pos().AdjustY(1 ); //because of the notifications
931 mrpLay = pSct;
932 if ( mrpLay->Lower() && mrpLay->Lower()->IsLayoutFrame() )
933 mrpLay = mrpLay->GetNextLayoutLeaf();
936 } while( bLongTab || ( mpImpl && mnIndex < mpImpl->size() &&
937 mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex ) );
939 mbFirst = false;
940 return bRet;
943 namespace {
945 struct SdrObjectCompare
947 bool operator()( const SdrObject* pF1, const SdrObject* pF2 ) const
949 return pF1->GetOrdNum() < pF2->GetOrdNum();
953 struct FlyCacheCompare
955 bool operator()( const SwFlyCache* pC1, const SwFlyCache* pC2 ) const
957 return pC1->nOrdNum < pC2->nOrdNum;
964 * If a new page is inserted, the last page is analysed.
965 * If there are text frames with default position, the fly cache
966 * is checked, if these frames are stored in the cache.
968 void SwLayHelper::CheckFlyCache_( SwPageFrame* pPage )
970 if( !mpImpl || !pPage )
971 return;
972 const size_t nFlyCount = mpImpl->GetFlyCount();
973 // Any text frames at the page, fly cache available?
974 if( !(pPage->GetSortedObjs() && mnFlyIdx < nFlyCount) )
975 return;
977 SwSortedObjs &rObjs = *pPage->GetSortedObjs();
978 sal_uInt16 nPgNum = pPage->GetPhyPageNum();
980 // NOTE: Here we do not use the absolute ordnums but
981 // relative ordnums for the objects on this page.
983 // skip fly frames from pages before the current page
984 while( mnFlyIdx < nFlyCount &&
985 mpImpl->GetFlyCache(mnFlyIdx).nPageNum < nPgNum )
986 ++mnFlyIdx;
988 // sort cached objects on this page by ordnum
989 o3tl::sorted_vector< const SwFlyCache*, FlyCacheCompare > aFlyCacheSet;
990 size_t nIdx = mnFlyIdx;
992 SwFlyCache* pFlyC;
993 while( nIdx < nFlyCount &&
994 ( pFlyC = &mpImpl->GetFlyCache( nIdx ) )->nPageNum == nPgNum )
996 aFlyCacheSet.insert( pFlyC );
997 ++nIdx;
1000 // sort objects on this page by ordnum
1001 o3tl::sorted_vector< const SdrObject*, SdrObjectCompare > aFlySet;
1002 for (SwAnchoredObject* pAnchoredObj : rObjs)
1004 if (SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame()) // a text frame?
1006 if( pFly->GetAnchorFrame() &&
1007 !pFly->GetAnchorFrame()->FindFooterOrHeader() )
1009 const SwContact *pC = ::GetUserCall( pAnchoredObj->GetDrawObj() );
1010 if( pC )
1012 aFlySet.insert( pAnchoredObj->GetDrawObj() );
1018 if ( aFlyCacheSet.size() != aFlySet.size() )
1019 return;
1021 auto aFlySetIt = aFlySet.begin();
1023 for ( const SwFlyCache* pFlyCache : aFlyCacheSet )
1025 SwFlyFrame* pFly = const_cast<SwVirtFlyDrawObj*>(static_cast<const SwVirtFlyDrawObj*>(*aFlySetIt))->GetFlyFrame();
1027 if ( pFly->getFrameArea().Left() == FAR_AWAY )
1029 // we get the stored information
1030 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFly);
1031 aFrm.Pos().setX( pFlyCache->Left() + pPage->getFrameArea().Left() );
1032 aFrm.Pos().setY( pFlyCache->Top() + pPage->getFrameArea().Top() );
1034 if ( mpImpl->IsUseFlyCache() )
1036 aFrm.Width( pFlyCache->Width() );
1037 aFrm.Height( pFlyCache->Height() );
1041 ++aFlySetIt;
1045 SwLayCacheIoImpl::SwLayCacheIoImpl( SvStream& rStrm, bool bWrtMd ) :
1046 m_pStream( &rStrm ),
1047 m_nFlagRecEnd ( 0 ),
1048 m_nMajorVersion(SW_LAYCACHE_IO_VERSION_MAJOR),
1049 m_nMinorVersion(SW_LAYCACHE_IO_VERSION_MINOR),
1050 m_bWriteMode( bWrtMd ),
1051 m_bError( false )
1053 if( m_bWriteMode )
1054 m_pStream->WriteUInt16( m_nMajorVersion )
1055 .WriteUInt16( m_nMinorVersion );
1057 else
1058 m_pStream->ReadUInt16( m_nMajorVersion )
1059 .ReadUInt16( m_nMinorVersion );
1062 void SwLayCacheIoImpl::OpenRec( sal_uInt8 cType )
1064 sal_uInt32 nPos = m_pStream->Tell();
1065 if( m_bWriteMode )
1067 m_aRecords.emplace_back(cType, nPos );
1068 m_pStream->WriteUInt32( 0 );
1070 else
1072 sal_uInt32 nVal(0);
1073 m_pStream->ReadUInt32( nVal );
1074 sal_uInt8 cRecTyp = static_cast<sal_uInt8>(nVal);
1075 if (!nVal || cRecTyp != cType || !m_pStream->good())
1077 OSL_ENSURE( nVal, "OpenRec: Record-Header is 0" );
1078 OSL_ENSURE( cRecTyp == cType, "OpenRec: Wrong Record Type" );
1079 m_aRecords.emplace_back(0, m_pStream->Tell() );
1080 m_bError = true;
1082 else
1084 sal_uInt32 nSize = nVal >> 8;
1085 m_aRecords.emplace_back(cRecTyp, nPos+nSize );
1090 // Close record
1091 void SwLayCacheIoImpl::CloseRec()
1093 bool bRes = true;
1094 OSL_ENSURE( !m_aRecords.empty(), "CloseRec: no levels" );
1095 if( !m_aRecords.empty() )
1097 sal_uInt32 nPos = m_pStream->Tell();
1098 if( m_bWriteMode )
1100 sal_uInt32 nBgn = m_aRecords.back().size;
1101 m_pStream->Seek( nBgn );
1102 sal_uInt32 nSize = nPos - nBgn;
1103 sal_uInt32 nVal = ( nSize << 8 ) | m_aRecords.back().type;
1104 m_pStream->WriteUInt32( nVal );
1105 m_pStream->Seek( nPos );
1106 if( m_pStream->GetError() != ERRCODE_NONE )
1107 bRes = false;
1109 else
1111 sal_uInt32 n = m_aRecords.back().size;
1112 OSL_ENSURE( n >= nPos, "CloseRec: too much data read" );
1113 if( n != nPos )
1115 m_pStream->Seek( n );
1116 if( n < nPos )
1117 bRes = false;
1119 if( m_pStream->GetErrorCode() != ERRCODE_NONE )
1120 bRes = false;
1122 m_aRecords.pop_back();
1125 if( !bRes )
1126 m_bError = true;
1129 sal_uInt32 SwLayCacheIoImpl::BytesLeft()
1131 sal_uInt32 n = 0;
1132 if( !m_bError && !m_aRecords.empty() )
1134 sal_uInt32 nEndPos = m_aRecords.back().size;
1135 sal_uInt32 nPos = m_pStream->Tell();
1136 if( nEndPos > nPos )
1137 n = nEndPos - nPos;
1139 return n;
1142 sal_uInt8 SwLayCacheIoImpl::Peek()
1144 sal_uInt8 c(0);
1145 if( !m_bError )
1147 sal_uInt32 nPos = m_pStream->Tell();
1148 m_pStream->ReadUChar( c );
1149 m_pStream->Seek( nPos );
1150 if( m_pStream->GetErrorCode() != ERRCODE_NONE )
1152 c = 0;
1153 m_bError = true;
1156 return c;
1159 void SwLayCacheIoImpl::SkipRec()
1161 sal_uInt8 c = Peek();
1162 OpenRec( c );
1163 m_pStream->Seek( m_aRecords.back().size );
1164 CloseRec();
1167 sal_uInt8 SwLayCacheIoImpl::OpenFlagRec()
1169 OSL_ENSURE( !m_bWriteMode, "OpenFlagRec illegal in write mode" );
1170 sal_uInt8 cFlags(0);
1171 m_pStream->ReadUChar( cFlags );
1172 m_nFlagRecEnd = m_pStream->Tell() + ( cFlags & 0x0F );
1173 return (cFlags >> 4);
1176 void SwLayCacheIoImpl::OpenFlagRec( sal_uInt8 nFlags, sal_uInt8 nLen )
1178 OSL_ENSURE( m_bWriteMode, "OpenFlagRec illegal in read mode" );
1179 OSL_ENSURE( (nFlags & 0xF0) == 0, "illegal flags set" );
1180 OSL_ENSURE( nLen < 16, "wrong flag record length" );
1181 sal_uInt8 cFlags = (nFlags << 4) + nLen;
1182 m_pStream->WriteUChar( cFlags );
1183 m_nFlagRecEnd = m_pStream->Tell() + nLen;
1186 void SwLayCacheIoImpl::CloseFlagRec()
1188 if( m_bWriteMode )
1190 OSL_ENSURE( m_pStream->Tell() == m_nFlagRecEnd, "Wrong amount of data written" );
1192 else
1194 OSL_ENSURE( m_pStream->Tell() <= m_nFlagRecEnd, "Too many data read" );
1195 if( m_pStream->Tell() != m_nFlagRecEnd )
1196 m_pStream->Seek( m_nFlagRecEnd );
1200 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */