1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
34 #include <swtable.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>
45 #include <sortedobjs.hxx>
46 #include <ndindex.hxx>
49 #include <frameformats.hxx>
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
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
)
72 m_pImpl
.reset( new SwLayCacheImpl
);
73 if( !m_pImpl
->Read( rStream
) )
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
)
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
);
102 while( aIo
.BytesLeft() && !aIo
.HasError() )
104 sal_uInt32
nIndex(0), nOffset(0);
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
);
116 nOffset
= COMPLETE_STRING
;
118 Insert( SW_LAYCACHE_IO_REC_PARA
, SwNodeOffset(nIndex
), static_cast<sal_Int32
>(nOffset
) );
122 case SW_LAYCACHE_IO_REC_TABLE
:
123 aIo
.OpenRec( SW_LAYCACHE_IO_REC_TABLE
);
125 aIo
.GetStream().ReadUInt32( nIndex
)
126 .ReadUInt32( nOffset
);
127 Insert( SW_LAYCACHE_IO_REC_TABLE
, SwNodeOffset(nIndex
), static_cast<sal_Int32
>(nOffset
) );
131 case SW_LAYCACHE_IO_REC_FLY
:
133 aIo
.OpenRec( SW_LAYCACHE_IO_REC_FLY
);
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
);
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
163 void SwLayoutCache::Write( SvStream
&rStream
, const SwDoc
& rDoc
)
165 if( !rDoc
.getIDocumentLayoutAccess().GetCurrentLayout() ) // the layout itself ..
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();
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 );
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,
204 nNdIdx
-= nStartOfContent
;
205 aIo
.GetStream().WriteUInt32( sal_Int32(nNdIdx
) );
207 aIo
.GetStream().WriteUInt32( sal_Int32(static_cast<SwTextFrame
*>(pTmp
)->GetOffset()) );
209 /* Close Paragraph Record */
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
222 if( pTab
->IsFollow() )
223 pTab
= pTab
->FindMaster( true );
224 while( pTab
!= pTmp
)
226 SwFrame
* pSub
= pTab
->Lower();
230 pSub
= pSub
->GetNext();
232 pTab
= pTab
->GetFollow();
233 assert(pTab
&& "Table follow without master");
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
);
249 /* Close Table Record */
252 // If the table has a follow on the next page,
253 // we know already the row number and store this
255 if( pTab
->GetFollow() )
257 if( nOfst
== sal_uLong(COMPLETE_STRING
) )
261 SwFrame
* pSub
= pTab
->Lower();
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" );
277 } while ( pTab
->GetFollow() );
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());
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 );
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 */
319 pPage
= static_cast<SwPageFrame
*>(pPage
->GetNext());
325 bool SwLayoutCache::CompareLayout( const SwDoc
& rDoc
) const
329 const SwRootFrame
*pRootFrame
= rDoc
.getIDocumentLayoutAccess().GetCurrentLayout();
333 SwNodeOffset nStartOfContent
= rDoc
.GetNodes().GetEndOfContent().
334 StartOfSectionNode()->GetIndex();
335 const SwPageFrame
* pPage
= static_cast<const SwPageFrame
*>(pRootFrame
->Lower());
337 pPage
= static_cast<const SwPageFrame
*>(pPage
->GetNext());
340 if( nIndex
>= m_pImpl
->size() )
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();
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
) ||
363 ? sal_Int32(static_cast<const SwTextFrame
*>(pTmp
)->GetOffset())
364 : COMPLETE_STRING
) != m_pImpl
->GetBreakOfst(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() )
378 if( pTab
->IsFollow() )
379 pTab
= pTab
->FindMaster( true );
380 while( pTab
!= pTmp
)
382 const SwFrame
* pSub
= pTab
->Lower();
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
) )
407 if( pTab
->GetFollow() )
409 if( nOfst
== COMPLETE_STRING
)
413 const SwFrame
* pSub
= pTab
->Lower();
417 pSub
= pSub
->GetNext();
419 pTab
= pTab
->GetFollow();
420 const SwPageFrame
*pTabPage
= pTab
->FindPageFrame();
421 if( pTabPage
!= pPage
)
426 } while ( pTab
->GetFollow() );
433 pPage
= static_cast<const SwPageFrame
*>(pPage
->GetNext());
440 void SwLayoutCache::ClearImpl()
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
) :
458 m_pSectFrame( pSect
),
463 const SwNodeIndex
*pIndex
= pSect
->GetFormat()->GetContent().GetContentIdx();
464 m_pSectNode
= pIndex
->GetNode().FindSectionNode();
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
);
485 auto const nBreakType(rCache
.GetBreakType(nIndex
));
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");
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");
505 assert(false); // Read shouldn't have inserted that
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
)
526 , mrpActualSection( rpA
)
527 , mbBreakAfter(false)
529 , mnMaxParaPerPage( 25 )
530 , mnParagraphCnt( bCache
? 0 : USHRT_MAX
)
534 mpImpl
= mpDoc
->GetLayoutCache() ? mpDoc
->GetLayoutCache()->LockImpl() : nullptr;
537 SwNodes
const& rNodes(mpDoc
->GetNodes());
538 if (sanityCheckLayoutCache(*mpImpl
, rNodes
, nNodeIndex
))
541 mnStartOfContent
= rNodes
.GetEndOfContent().StartOfSectionNode()->GetIndex();
542 mnMaxParaPerPage
= 1000;
546 mpDoc
->GetLayoutCache()->UnlockImpl();
548 mnIndex
= std::numeric_limits
<size_t>::max();
549 mnStartOfContent
= SwNodeOffset(USHRT_MAX
);
554 mnIndex
= std::numeric_limits
<size_t>::max();
555 mnStartOfContent
= NODE_OFFSET_MAX
;
559 SwLayHelper::~SwLayHelper()
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()
575 SwLayCacheImpl
*pCache
= mpDoc
->GetLayoutCache() ?
576 mpDoc
->GetLayoutCache()->LockImpl() : nullptr;
579 nPgCount
= pCache
->size() + 1;
580 mpDoc
->GetLayoutCache()->UnlockImpl();
584 nPgCount
= mpDoc
->getIDocumentStatistics().GetDocStat().nPage
;
585 if ( nPgCount
<= 10 ) // no page insertion for less than 10 pages
587 sal_Int32 nNdCount
= mpDoc
->getIDocumentStatistics().GetDocStat().nPara
;
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);
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
604 { // tdf#129529 avoid 0...
605 mnMaxParaPerPage
= std::max
<sal_uLong
>(3, nNdCount
/ nPgCount
);
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;
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() ?
648 bool bBrk
= mnParagraphCnt
> mnMaxParaPerPage
|| mbBreakAfter
;
649 mbBreakAfter
= rBrk
.GetBreak() == SvxBreak::PageAfter
||
650 rBrk
.GetBreak() == SvxBreak::PageBoth
;
652 bBrk
= rBrk
.GetBreak() == SvxBreak::PageBefore
||
653 rBrk
.GetBreak() == SvxBreak::PageBoth
;
657 ::std::optional
<sal_uInt16
> oPgNum
;
660 pDesc
= mrpPage
->GetPageDesc()->GetFollow();
664 oPgNum
= rDesc
.GetNumOffset();
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
;
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());
683 OSL_ENSURE( mrpPage
->GetNext(), "No new page?" );
685 { mrpPage
= static_cast<SwPageFrame
*>(mrpPage
->GetNext());
686 } while ( mrpPage
->GetNext() );
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());
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
)
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();
728 pLow
= pLow
->GetNext();
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
;
744 SwFrame
*pTmp
= static_cast<SwTabFrame
*>(mrpFrame
)->Lower();
745 if( pTmp
->GetNext() )
746 pTmp
= pTmp
->GetNext();
747 pTmp
= static_cast<SwRowFrame
*>(pTmp
)->Lower();
752 pTmp
= pTmp
->GetNext();
754 nMaxRowPerPage
= std::max( sal_uLong(2), mnMaxParaPerPage
/ nCnt
);
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
) ) )
767 // OD 09.04.2003 #108698# - always split a big tables.
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
;
782 nOfst
= static_cast<sal_Int32
>(nRowCount
+ nMaxRowPerPage
);
786 while( mnIndex
< mpImpl
->size() &&
787 mpImpl
->GetBreakIndex(mnIndex
) < nNodeIndex
)
789 if( mnIndex
< mpImpl
->size() &&
790 mpImpl
->GetBreakIndex(mnIndex
) == nNodeIndex
)
792 nType
= mpImpl
->GetBreakType( mnIndex
);
793 nOfst
= mpImpl
->GetBreakOfst( mnIndex
++ );
798 if( nOfst
< COMPLETE_STRING
)
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())
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
;
816 mrpFrame
->InsertBehind( mrpLay
, mrpPrv
);
819 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*mrpFrame
);
820 aFrm
.Pos() = mrpLay
->getFrameArea().Pos();
821 aFrm
.Pos().AdjustY(1 );
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
);
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?" );
844 new SwRowFrame( *pTab
->GetTable()->GetTabLines()[ nRowIdx
], pTab
);
845 pHeadline
->SetRepeatedHeadline( true );
846 pHeadline
->InsertBefore( pFoll
, nullptr );
847 pHeadline
->RegistFlys();
852 nRows
= nRows
+ nRepeat
;
856 while( pRow
&& nRowCount
< nOfst
)
858 pRow
= pRow
->GetNext();
863 SwFrame
* pNxt
= pRow
->GetNext();
864 pRow
->RemoveFromLayout();
865 pRow
->InsertBehind( pFoll
, pPrv
);
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
);
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() );
899 if ( mrpActualSection
)
901 //Did the SectionFrame even have a content? If not, we can
902 //directly put it somewhere else
903 SwSectionFrame
*pSct
;
905 if ( !mrpActualSection
->GetSectionFrame()->ContainsContent())
907 pSct
= mrpActualSection
->GetSectionFrame();
908 pSct
->RemoveFromLayout();
912 pSct
= new SwSectionFrame(
913 *mrpActualSection
->GetSectionFrame(), false );
914 mrpActualSection
->GetSectionFrame()->SimpleFormat();
917 mrpActualSection
->SetSectionFrame( pSct
);
918 pSct
->InsertBehind( mrpLay
, nullptr );
926 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*pSct
);
927 aFrm
.Pos() = mrpLay
->getFrameArea().Pos();
928 aFrm
.Pos().AdjustY(1 ); //because of the notifications
932 if ( mrpLay
->Lower() && mrpLay
->Lower()->IsLayoutFrame() )
933 mrpLay
= mrpLay
->GetNextLayoutLeaf();
936 } while( bLongTab
|| ( mpImpl
&& mnIndex
< mpImpl
->size() &&
937 mpImpl
->GetBreakIndex( mnIndex
) == nNodeIndex
) );
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
)
972 const size_t nFlyCount
= mpImpl
->GetFlyCount();
973 // Any text frames at the page, fly cache available?
974 if( !(pPage
->GetSortedObjs() && mnFlyIdx
< nFlyCount
) )
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
)
988 // sort cached objects on this page by ordnum
989 o3tl::sorted_vector
< const SwFlyCache
*, FlyCacheCompare
> aFlyCacheSet
;
990 size_t nIdx
= mnFlyIdx
;
993 while( nIdx
< nFlyCount
&&
994 ( pFlyC
= &mpImpl
->GetFlyCache( nIdx
) )->nPageNum
== nPgNum
)
996 aFlyCacheSet
.insert( pFlyC
);
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() );
1012 aFlySet
.insert( pAnchoredObj
->GetDrawObj() );
1018 if ( aFlyCacheSet
.size() != aFlySet
.size() )
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() );
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
),
1054 m_pStream
->WriteUInt16( m_nMajorVersion
)
1055 .WriteUInt16( m_nMinorVersion
);
1058 m_pStream
->ReadUInt16( m_nMajorVersion
)
1059 .ReadUInt16( m_nMinorVersion
);
1062 void SwLayCacheIoImpl::OpenRec( sal_uInt8 cType
)
1064 sal_uInt32 nPos
= m_pStream
->Tell();
1067 m_aRecords
.emplace_back(cType
, nPos
);
1068 m_pStream
->WriteUInt32( 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() );
1084 sal_uInt32 nSize
= nVal
>> 8;
1085 m_aRecords
.emplace_back(cRecTyp
, nPos
+nSize
);
1091 void SwLayCacheIoImpl::CloseRec()
1094 OSL_ENSURE( !m_aRecords
.empty(), "CloseRec: no levels" );
1095 if( !m_aRecords
.empty() )
1097 sal_uInt32 nPos
= m_pStream
->Tell();
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
)
1111 sal_uInt32 n
= m_aRecords
.back().size
;
1112 OSL_ENSURE( n
>= nPos
, "CloseRec: too much data read" );
1115 m_pStream
->Seek( n
);
1119 if( m_pStream
->GetErrorCode() != ERRCODE_NONE
)
1122 m_aRecords
.pop_back();
1129 sal_uInt32
SwLayCacheIoImpl::BytesLeft()
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
)
1142 sal_uInt8
SwLayCacheIoImpl::Peek()
1147 sal_uInt32 nPos
= m_pStream
->Tell();
1148 m_pStream
->ReadUChar( c
);
1149 m_pStream
->Seek( nPos
);
1150 if( m_pStream
->GetErrorCode() != ERRCODE_NONE
)
1159 void SwLayCacheIoImpl::SkipRec()
1161 sal_uInt8 c
= Peek();
1163 m_pStream
->Seek( m_aRecords
.back().size
);
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()
1190 OSL_ENSURE( m_pStream
->Tell() == m_nFlagRecEnd
, "Wrong amount of data written" );
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: */