1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <editeng/formatbreakitem.hxx>
21 #include <sal/log.hxx>
22 #include <tools/stream.hxx>
23 #include <doc.hxx>
24 #include <IDocumentStatistics.hxx>
25 #include <IDocumentLayoutAccess.hxx>
26 #include <docstat.hxx>
27 #include <docary.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
50 #include <limits>
51 #include <set>
52
53 using namespace ::com::sun::star;
54
SwLayoutCache()55 SwLayoutCache::SwLayoutCache() : nLockCount( 0 ) {}
56
57 /*
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.
66 */
67
Read(SvStream & rStream)68 void SwLayoutCache::Read( SvStream &rStream )
69 {
70 if( !pImpl )
71 {
72 pImpl.reset( new SwLayCacheImpl );
73 if( !pImpl->Read( rStream ) )
74 {
75 pImpl.reset();
76 }
77 }
78 }
79
Insert(sal_uInt16 nType,sal_uLong nIndex,sal_Int32 nOffset)80 void SwLayCacheImpl::Insert( sal_uInt16 nType, sal_uLong nIndex, sal_Int32 nOffset )
81 {
82 aType.push_back( nType );
83 mIndices.push_back( nIndex );
84 aOffset.push_back( nOffset );
85 }
86
Read(SvStream & rStream)87 bool SwLayCacheImpl::Read( SvStream& rStream )
88 {
89 SwLayCacheIoImpl aIo( rStream, false );
90 if( aIo.GetMajorVersion() > SW_LAYCACHE_IO_VERSION_MAJOR )
91 return false;
92
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 bUseFlyCache = aIo.GetMinorVersion() >= 1;
98
99 aIo.OpenRec( SW_LAYCACHE_IO_REC_PAGES );
100 aIo.OpenFlagRec();
101 aIo.CloseFlagRec();
102 while( aIo.BytesLeft() && !aIo.HasError() )
103 {
104 sal_uInt32 nIndex(0), nOffset(0);
105
106 switch( aIo.Peek() )
107 {
108 case SW_LAYCACHE_IO_REC_PARA:
109 {
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, nIndex, static_cast<sal_Int32>(nOffset) );
119 aIo.CloseRec();
120 break;
121 }
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, nIndex, static_cast<sal_Int32>(nOffset) );
128 aIo.CloseFlagRec();
129 aIo.CloseRec();
130 break;
131 case SW_LAYCACHE_IO_REC_FLY:
132 {
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;
143 }
144 default:
145 aIo.SkipRec();
146 break;
147 }
148 }
149 aIo.CloseRec();
150
151 return !aIo.HasError();
152 }
153
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
162 */
Write(SvStream & rStream,const SwDoc & rDoc)163 void SwLayoutCache::Write( SvStream &rStream, const SwDoc& rDoc )
164 {
165 if( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ) // the layout itself ..
166 {
167 SwLayCacheIoImpl aIo( rStream, true );
168 // We want to save the relative index, so we need the index
169 // of the first content
170 sal_uLong nStartOfContent = rDoc.GetNodes().GetEndOfContent().
171 StartOfSectionNode()->GetIndex();
172 // The first page...
173 SwPageFrame* pPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->Lower()));
174
175 aIo.OpenRec( SW_LAYCACHE_IO_REC_PAGES );
176 aIo.OpenFlagRec( 0, 0 );
177 aIo.CloseFlagRec();
178 while( pPage )
179 {
180 if( pPage->GetPrev() )
181 {
182 SwLayoutFrame* pLay = pPage->FindBodyCont();
183 SwFrame* pTmp = pLay ? pLay->ContainsAny() : nullptr;
184 // We are only interested in paragraph or table frames,
185 // a section frames contains paragraphs/tables.
186 if( pTmp && pTmp->IsSctFrame() )
187 pTmp = static_cast<SwSectionFrame*>(pTmp)->ContainsAny();
188
189 if( pTmp ) // any content
190 {
191 if( pTmp->IsTextFrame() )
192 {
193 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTmp));
194 assert(!pFrame->GetMergedPara());
195 sal_uLong nNdIdx = pFrame->GetTextNodeFirst()->GetIndex();
196 if( nNdIdx > nStartOfContent )
197 {
198 /* Open Paragraph Record */
199 aIo.OpenRec( SW_LAYCACHE_IO_REC_PARA );
200 bool bFollow = static_cast<SwTextFrame*>(pTmp)->IsFollow();
201 aIo.OpenFlagRec( bFollow ? 0x01 : 0x00,
202 bFollow ? 8 : 4 );
203 nNdIdx -= nStartOfContent;
204 aIo.GetStream().WriteUInt32( nNdIdx );
205 if( bFollow )
206 aIo.GetStream().WriteUInt32( sal_Int32(static_cast<SwTextFrame*>(pTmp)->GetOfst()) );
207 aIo.CloseFlagRec();
208 /* Close Paragraph Record */
209 aIo.CloseRec();
210 }
211 }
212 else if( pTmp->IsTabFrame() )
213 {
214 SwTabFrame* pTab = static_cast<SwTabFrame*>(pTmp);
215 sal_uLong nOfst = COMPLETE_STRING;
216 if( pTab->IsFollow() )
217 {
218 // If the table is a follow, we have to look for the
219 // master and to count all rows to get the row number
220 nOfst = 0;
221 if( pTab->IsFollow() )
222 pTab = pTab->FindMaster( true );
223 while( pTab != pTmp )
224 {
225 SwFrame* pSub = pTab->Lower();
226 while( pSub )
227 {
228 ++nOfst;
229 pSub = pSub->GetNext();
230 }
231 pTab = pTab->GetFollow();
232 assert(pTab && "Table follow without master");
233 }
234 }
235 while (true)
236 {
237 sal_uLong nNdIdx =
238 pTab->GetTable()->GetTableNode()->GetIndex();
239 if( nNdIdx > nStartOfContent )
240 {
241 /* Open Table Record */
242 aIo.OpenRec( SW_LAYCACHE_IO_REC_TABLE );
243 aIo.OpenFlagRec( 0, 8 );
244 nNdIdx -= nStartOfContent;
245 aIo.GetStream().WriteUInt32( nNdIdx )
246 .WriteUInt32( nOfst );
247 aIo.CloseFlagRec();
248 /* Close Table Record */
249 aIo.CloseRec();
250 }
251 // If the table has a follow on the next page,
252 // we know already the row number and store this
253 // immediately.
254 if( pTab->GetFollow() )
255 {
256 if( nOfst == sal_uLong(COMPLETE_STRING) )
257 nOfst = 0;
258 do
259 {
260 SwFrame* pSub = pTab->Lower();
261 while( pSub )
262 {
263 ++nOfst;
264 pSub = pSub->GetNext();
265 }
266 pTab = pTab->GetFollow();
267 SwPageFrame *pTabPage = pTab->FindPageFrame();
268 if( pTabPage != pPage )
269 {
270 OSL_ENSURE( pPage->GetPhyPageNum() <
271 pTabPage->GetPhyPageNum(),
272 "Looping Tableframes" );
273 pPage = pTabPage;
274 break;
275 }
276 } while ( pTab->GetFollow() );
277 }
278 else
279 break;
280 }
281 }
282 }
283 }
284 if( pPage->GetSortedObjs() )
285 {
286 SwSortedObjs &rObjs = *pPage->GetSortedObjs();
287 for (SwAnchoredObject* pAnchoredObj : rObjs)
288 {
289 if (SwFlyFrame *pFly = dynamic_cast<SwFlyFrame*>(pAnchoredObj))
290 {
291 if( pFly->getFrameArea().Left() != FAR_AWAY &&
292 !pFly->GetAnchorFrame()->FindFooterOrHeader() )
293 {
294 const SwContact *pC =
295 ::GetUserCall(pAnchoredObj->GetDrawObj());
296 if( pC )
297 {
298 sal_uInt32 nOrdNum = pAnchoredObj->GetDrawObj()->GetOrdNum();
299 sal_uInt16 nPageNum = pPage->GetPhyPageNum();
300 /* Open Fly Record */
301 aIo.OpenRec( SW_LAYCACHE_IO_REC_FLY );
302 aIo.OpenFlagRec( 0, 0 );
303 aIo.CloseFlagRec();
304 const SwRect& rRct = pFly->getFrameArea();
305 sal_Int32 nX = rRct.Left() - pPage->getFrameArea().Left();
306 sal_Int32 nY = rRct.Top() - pPage->getFrameArea().Top();
307 aIo.GetStream().WriteUInt16( nPageNum ).WriteUInt32( nOrdNum )
308 .WriteInt32( nX ).WriteInt32( nY )
309 .WriteInt32( rRct.Width() )
310 .WriteInt32( rRct.Height() );
311 /* Close Fly Record */
312 aIo.CloseRec();
313 }
314 }
315 }
316 }
317 }
318 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
319 }
320 aIo.CloseRec();
321 }
322 }
323
324 #ifdef DBG_UTIL
CompareLayout(const SwDoc & rDoc) const325 bool SwLayoutCache::CompareLayout( const SwDoc& rDoc ) const
326 {
327 if( !pImpl )
328 return true;
329 const SwRootFrame *pRootFrame = rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
330 if( pRootFrame )
331 {
332 size_t nIndex = 0;
333 sal_uLong 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 )
339 {
340 if( nIndex >= pImpl->size() )
341 return false;
342
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 )
348 {
349 if( pTmp->IsTextFrame() )
350 {
351
352 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTmp));
353 assert(!pFrame->GetMergedPara());
354 sal_uLong nNdIdx = pFrame->GetTextNodeFirst()->GetIndex();
355 if( nNdIdx > nStartOfContent )
356 {
357 bool bFollow = static_cast<const SwTextFrame*>(pTmp)->IsFollow();
358 nNdIdx -= nStartOfContent;
359 if( pImpl->GetBreakIndex( nIndex ) != nNdIdx ||
360 SW_LAYCACHE_IO_REC_PARA !=
361 pImpl->GetBreakType( nIndex ) ||
362 (bFollow
363 ? sal_Int32(static_cast<const SwTextFrame*>(pTmp)->GetOfst())
364 : COMPLETE_STRING) != pImpl->GetBreakOfst(nIndex))
365 {
366 return false;
367 }
368 ++nIndex;
369 }
370 }
371 else if( pTmp->IsTabFrame() )
372 {
373 const SwTabFrame* pTab = static_cast<const SwTabFrame*>(pTmp);
374 sal_Int32 nOfst = COMPLETE_STRING;
375 if( pTab->IsFollow() )
376 {
377 nOfst = 0;
378 if( pTab->IsFollow() )
379 pTab = pTab->FindMaster( true );
380 while( pTab != pTmp )
381 {
382 const SwFrame* pSub = pTab->Lower();
383 while( pSub )
384 {
385 ++nOfst;
386 pSub = pSub->GetNext();
387 }
388 pTab = pTab->GetFollow();
389 }
390 }
391 do
392 {
393 sal_uLong nNdIdx =
394 pTab->GetTable()->GetTableNode()->GetIndex();
395 if( nNdIdx > nStartOfContent )
396 {
397 nNdIdx -= nStartOfContent;
398 if( pImpl->GetBreakIndex( nIndex ) != nNdIdx ||
399 SW_LAYCACHE_IO_REC_TABLE !=
400 pImpl->GetBreakType( nIndex ) ||
401 nOfst != pImpl->GetBreakOfst( nIndex ) )
402 {
403 return false;
404 }
405 ++nIndex;
406 }
407 if( pTab->GetFollow() )
408 {
409 if( nOfst == COMPLETE_STRING )
410 nOfst = 0;
411 do
412 {
413 const SwFrame* pSub = pTab->Lower();
414 while( pSub )
415 {
416 ++nOfst;
417 pSub = pSub->GetNext();
418 }
419 pTab = pTab->GetFollow();
420 const SwPageFrame *pTabPage = pTab->FindPageFrame();
421 if( pTabPage != pPage )
422 {
423 pPage = pTabPage;
424 break;
425 }
426 } while ( pTab->GetFollow() );
427 }
428 else
429 break;
430 } while( pTab );
431 }
432 }
433 pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
434 }
435 }
436 return true;
437 }
438 #endif
439
ClearImpl()440 void SwLayoutCache::ClearImpl()
441 {
442 if( !IsLocked() )
443 {
444 pImpl.reset();
445 }
446 }
447
~SwLayoutCache()448 SwLayoutCache::~SwLayoutCache()
449 {
450 OSL_ENSURE( !nLockCount, "Deleting a locked SwLayoutCache!?" );
451 }
452
453 /// helper class to create not nested section frames for nested sections.
SwActualSection(SwActualSection * pUp,SwSectionFrame * pSect,SwSectionNode * pNd)454 SwActualSection::SwActualSection( SwActualSection *pUp,
455 SwSectionFrame *pSect,
456 SwSectionNode *pNd ) :
457 pUpper( pUp ),
458 pSectFrame( pSect ),
459 pSectNode( pNd )
460 {
461 if ( !pSectNode )
462 {
463 const SwNodeIndex *pIndex = pSect->GetFormat()->GetContent().GetContentIdx();
464 pSectNode = pIndex->GetNode().FindSectionNode();
465 }
466 }
467
468 namespace {
469
sanityCheckLayoutCache(SwLayCacheImpl const & rCache,SwNodes const & rNodes,sal_uLong nNodeIndex)470 bool sanityCheckLayoutCache(SwLayCacheImpl const& rCache,
471 SwNodes const& rNodes, sal_uLong nNodeIndex)
472 {
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)
477 {
478 auto const nBreakIndex(rCache.GetBreakIndex(nIndex));
479 if (nBreakIndex < nNodeIndex || nMaxIndex <= nBreakIndex)
480 {
481 SAL_WARN("sw.layout",
482 "invalid node index in layout-cache: " << nBreakIndex);
483 return false;
484 }
485 auto const nBreakType(rCache.GetBreakType(nIndex));
486 switch (nBreakType)
487 {
488 case SW_LAYCACHE_IO_REC_PARA:
489 if (!rNodes[nBreakIndex + nStartOfContent]->IsTextNode())
490 {
491 SAL_WARN("sw.layout",
492 "invalid node of type 'P' in layout-cache");
493 return false;
494 }
495 break;
496 case SW_LAYCACHE_IO_REC_TABLE:
497 if (!rNodes[nBreakIndex + nStartOfContent]->IsTableNode())
498 {
499 SAL_WARN("sw.layout",
500 "invalid node of type 'T' in layout-cache");
501 return false;
502 }
503 break;
504 default:
505 assert(false); // Read shouldn't have inserted that
506 }
507 }
508 return true;
509 }
510
511 } // namespace
512
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.
518 */
SwLayHelper(SwDoc * pD,SwFrame * & rpF,SwFrame * & rpP,SwPageFrame * & rpPg,SwLayoutFrame * & rpL,std::unique_ptr<SwActualSection> & rpA,sal_uLong nNodeIndex,bool bCache)519 SwLayHelper::SwLayHelper( SwDoc *pD, SwFrame* &rpF, SwFrame* &rpP, SwPageFrame* &rpPg,
520 SwLayoutFrame* &rpL, std::unique_ptr<SwActualSection> &rpA,
521 sal_uLong 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 )
533 {
534 mpImpl = mpDoc->GetLayoutCache() ? mpDoc->GetLayoutCache()->LockImpl() : nullptr;
535 if( mpImpl )
536 {
537 SwNodes const& rNodes(mpDoc->GetNodes());
538 if (sanityCheckLayoutCache(*mpImpl, rNodes, nNodeIndex))
539 {
540 mnIndex = 0;
541 mnStartOfContent = rNodes.GetEndOfContent().StartOfSectionNode()->GetIndex();
542 mnMaxParaPerPage = 1000;
543 }
544 else
545 {
546 mpDoc->GetLayoutCache()->UnlockImpl();
547 mpImpl = nullptr;
548 mnIndex = std::numeric_limits<size_t>::max();
549 mnStartOfContent = USHRT_MAX;
550 }
551 }
552 else
553 {
554 mnIndex = std::numeric_limits<size_t>::max();
555 mnStartOfContent = ULONG_MAX;
556 }
557 }
558
~SwLayHelper()559 SwLayHelper::~SwLayHelper()
560 {
561 if( mpImpl )
562 {
563 OSL_ENSURE( mpDoc && mpDoc->GetLayoutCache(), "Missing layoutcache" );
564 mpDoc->GetLayoutCache()->UnlockImpl();
565 }
566 }
567
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.
571 */
CalcPageCount()572 sal_uLong SwLayHelper::CalcPageCount()
573 {
574 sal_uLong nPgCount;
575 SwLayCacheImpl *pCache = mpDoc->GetLayoutCache() ?
576 mpDoc->GetLayoutCache()->LockImpl() : nullptr;
577 if( pCache )
578 {
579 nPgCount = pCache->size() + 1;
580 mpDoc->GetLayoutCache()->UnlockImpl();
581 }
582 else
583 {
584 nPgCount = mpDoc->getIDocumentStatistics().GetDocStat().nPage;
585 if ( nPgCount <= 10 ) // no page insertion for less than 10 pages
586 nPgCount = 0;
587 sal_uLong nNdCount = mpDoc->getIDocumentStatistics().GetDocStat().nPara;
588 if ( nNdCount <= 1 )
589 {
590 //Estimates the number of paragraphs.
591 sal_uLong nTmp = mpDoc->GetNodes().GetEndOfContent().GetIndex() -
592 mpDoc->GetNodes().GetEndOfExtras().GetIndex();
593 //Tables have a little overhead...
594 nTmp -= mpDoc->GetTableFrameFormats()->size() * 25;
595 //Fly frames, too ..
596 nTmp -= (mpDoc->GetNodes().GetEndOfAutotext().GetIndex() -
597 mpDoc->GetNodes().GetEndOfInserts().GetIndex()) / 3 * 5;
598 if ( nTmp > 0 )
599 nNdCount = nTmp;
600 }
601 if ( nNdCount > 100 ) // no estimation below this value
602 {
603 if ( nPgCount > 0 )
604 { // tdf#129529 avoid 0...
605 mnMaxParaPerPage = std::max<sal_uLong>(3, nNdCount / nPgCount);
606 }
607 else
608 {
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;
614 }
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;
622 }
623 }
624 return nPgCount;
625 }
626
627 /**
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
632 *
633 * The break after flag is set, if the actual content
634 * wants a break after.
635 */
CheckInsertPage()636 bool SwLayHelper::CheckInsertPage()
637 {
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();
647
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;
654
655 if ( bBrk || pDesc )
656 {
657 ::boost::optional<sal_uInt16> oPgNum;
658 if ( !pDesc )
659 {
660 pDesc = mrpPage->GetPageDesc()->GetFollow();
661
662 SwFormatPageDesc aFollowDesc( pDesc );
663 oPgNum = aFollowDesc.GetNumOffset();
664 if ( oPgNum )
665 static_cast<SwRootFrame*>(mrpPage->GetUpper())->SetVirtPageNum(true);
666 }
667 else
668 {
669 oPgNum = rDesc.GetNumOffset();
670 if ( oPgNum )
671 static_cast<SwRootFrame*>(mrpPage->GetUpper())->SetVirtPageNum(true);
672 }
673 bool bNextPageRight = !mrpPage->OnRightPage();
674 bool bInsertEmpty = false;
675 assert(mrpPage->GetUpper()->GetLower());
676 if (oPgNum && bNextPageRight != IsRightPageByNumber(
677 *static_cast<SwRootFrame*>(mrpPage->GetUpper()), *oPgNum))
678 {
679 bNextPageRight = !bNextPageRight;
680 bInsertEmpty = true;
681 }
682 // If the page style is changing, we'll have a first page.
683 bool bNextPageFirst = pDesc != mrpPage->GetPageDesc();
684 ::InsertNewPage( const_cast<SwPageDesc&>(*pDesc), mrpPage->GetUpper(),
685 bNextPageRight, bNextPageFirst, bInsertEmpty, false, mrpPage->GetNext());
686 if ( bEnd )
687 {
688 OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
689 do
690 { mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
691 } while ( mrpPage->GetNext() );
692 }
693 else
694 {
695 OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
696 mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
697 if ( mrpPage->IsEmptyPage() )
698 {
699 OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
700 mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
701 }
702 }
703 mrpLay = mrpPage->FindBodyCont();
704 while( mrpLay->Lower() )
705 mrpLay = static_cast<SwLayoutFrame*>(mrpLay->Lower());
706 return true;
707 }
708 return false;
709 }
710
711 /** entry point for the InsertCnt_-function.
712 * The document content index is checked either it is
713 * in the layout cache either it's time to insert a page
714 * cause the maximal estimation of content per page is reached.
715 * A really big table or long paragraph may contains more than
716 * one page, in this case the needed count of pages will inserted.
717 */
CheckInsert(sal_uLong nNodeIndex)718 bool SwLayHelper::CheckInsert( sal_uLong nNodeIndex )
719 {
720 bool bRet = false;
721 bool bLongTab = false;
722 sal_uLong nMaxRowPerPage( 0 );
723 nNodeIndex -= mnStartOfContent;
724 sal_uInt16 nRows( 0 );
725 if( mrpFrame->IsTabFrame() )
726 {
727 //Inside a table counts every row as a paragraph
728 SwFrame *pLow = static_cast<SwTabFrame*>(mrpFrame)->Lower();
729 nRows = 0;
730 do
731 {
732 ++nRows;
733 pLow = pLow->GetNext();
734 } while ( pLow );
735 mnParagraphCnt += nRows;
736 if( !mpImpl && mnParagraphCnt > mnMaxParaPerPage + 10 )
737 {
738 // OD 09.04.2003 #108698# - improve heuristics:
739 // Assume that a table, which has more than three times the quantity
740 // of maximal paragraphs per page rows, consists of rows, which have
741 // the height of a normal paragraph. Thus, allow as much rows per page
742 // as much paragraphs are allowed.
743 if ( nRows > ( 3*mnMaxParaPerPage ) )
744 {
745 nMaxRowPerPage = mnMaxParaPerPage;
746 }
747 else
748 {
749 SwFrame *pTmp = static_cast<SwTabFrame*>(mrpFrame)->Lower();
750 if( pTmp->GetNext() )
751 pTmp = pTmp->GetNext();
752 pTmp = static_cast<SwRowFrame*>(pTmp)->Lower();
753 sal_uInt16 nCnt = 0;
754 do
755 {
756 ++nCnt;
757 pTmp = pTmp->GetNext();
758 } while( pTmp );
759 nMaxRowPerPage = std::max( sal_uLong(2), mnMaxParaPerPage / nCnt );
760 }
761 bLongTab = true;
762 }
763 }
764 else
765 ++mnParagraphCnt;
766 if( mbFirst && mpImpl && mnIndex < mpImpl->size() &&
767 mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex &&
768 ( mpImpl->GetBreakOfst( mnIndex ) < COMPLETE_STRING ||
769 ( ++mnIndex < mpImpl->size() &&
770 mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex ) ) )
771 mbFirst = false;
772 // OD 09.04.2003 #108698# - always split a big tables.
773 if ( !mbFirst ||
774 ( mrpFrame->IsTabFrame() && bLongTab )
775 )
776 {
777 sal_Int32 nRowCount = 0;
778 do
779 {
780 if( mpImpl || bLongTab )
781 {
782 sal_Int32 nOfst = COMPLETE_STRING;
783 sal_uInt16 nType = SW_LAYCACHE_IO_REC_PAGES;
784 if( bLongTab )
785 {
786 mbBreakAfter = true;
787 nOfst = static_cast<sal_Int32>(nRowCount + nMaxRowPerPage);
788 }
789 else
790 {
791 while( mnIndex < mpImpl->size() &&
792 mpImpl->GetBreakIndex(mnIndex) < nNodeIndex)
793 ++mnIndex;
794 if( mnIndex < mpImpl->size() &&
795 mpImpl->GetBreakIndex(mnIndex) == nNodeIndex )
796 {
797 nType = mpImpl->GetBreakType( mnIndex );
798 nOfst = mpImpl->GetBreakOfst( mnIndex++ );
799 mbBreakAfter = true;
800 }
801 }
802
803 if( nOfst < COMPLETE_STRING )
804 {
805 bool bSplit = false;
806 sal_uInt16 nRepeat( 0 );
807 if( !bLongTab && mrpFrame->IsTextFrame() &&
808 SW_LAYCACHE_IO_REC_PARA == nType &&
809 nOfst < static_cast<SwTextFrame*>(mrpFrame)->GetText().getLength())
810 bSplit = true;
811 else if( mrpFrame->IsTabFrame() && nRowCount < nOfst &&
812 ( bLongTab || SW_LAYCACHE_IO_REC_TABLE == nType ) )
813 {
814 nRepeat = static_cast<SwTabFrame*>(mrpFrame)->
815 GetTable()->GetRowsToRepeat();
816 bSplit = nOfst < nRows && nRowCount + nRepeat < nOfst;
817 bLongTab = bLongTab && bSplit;
818 }
819 if( bSplit )
820 {
821 mrpFrame->InsertBehind( mrpLay, mrpPrv );
822
823 {
824 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mrpFrame);
825 aFrm.Pos() = mrpLay->getFrameArea().Pos();
826 aFrm.Pos().AdjustY(1 );
827 }
828
829 mrpPrv = mrpFrame;
830 if( mrpFrame->IsTabFrame() )
831 {
832 SwTabFrame* pTab = static_cast<SwTabFrame*>(mrpFrame);
833 // #i33629#, #i29955#
834 ::RegistFlys( pTab->FindPageFrame(), pTab );
835 SwFrame *pRow = pTab->Lower();
836 SwTabFrame *pFoll = new SwTabFrame( *pTab );
837
838 SwFrame *pPrv;
839 if( nRepeat > 0 )
840 {
841 bDontCreateObjects = true; //frmtool
842
843 // Insert new headlines:
844 sal_uInt16 nRowIdx = 0;
845 SwRowFrame* pHeadline = nullptr;
846 while( nRowIdx < nRepeat )
847 {
848 OSL_ENSURE( pTab->GetTable()->GetTabLines()[ nRowIdx ], "Table without rows?" );
849 pHeadline =
850 new SwRowFrame( *pTab->GetTable()->GetTabLines()[ nRowIdx ], pTab );
851 pHeadline->SetRepeatedHeadline( true );
852 pHeadline->InsertBefore( pFoll, nullptr );
853 pHeadline->RegistFlys();
854
855 ++nRowIdx;
856 }
857
858 bDontCreateObjects = false;
859 pPrv = pHeadline;
860 nRows = nRows + nRepeat;
861 }
862 else
863 pPrv = nullptr;
864 while( pRow && nRowCount < nOfst )
865 {
866 pRow = pRow->GetNext();
867 ++nRowCount;
868 }
869 while ( pRow )
870 {
871 SwFrame* pNxt = pRow->GetNext();
872 pRow->RemoveFromLayout();
873 pRow->InsertBehind( pFoll, pPrv );
874 pPrv = pRow;
875 pRow = pNxt;
876 }
877 mrpFrame = pFoll;
878 }
879 else
880 {
881 SwTextFrame *const pNew = static_cast<SwTextFrame*>(
882 static_cast<SwTextFrame*>(mrpFrame)
883 ->GetTextNodeFirst()->MakeFrame(mrpFrame));
884 pNew->ManipOfst( TextFrameIndex(nOfst) );
885 pNew->SetFollow( static_cast<SwTextFrame*>(mrpFrame)->GetFollow() );
886 static_cast<SwTextFrame*>(mrpFrame)->SetFollow( pNew );
887 mrpFrame = pNew;
888 }
889 }
890 }
891 }
892
893 SwPageFrame* pLastPage = mrpPage;
894 if( CheckInsertPage() )
895 {
896 CheckFlyCache_( pLastPage );
897 if( mrpPrv && mrpPrv->IsTextFrame() && !mrpPrv->isFrameAreaSizeValid() )
898 {
899 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mrpPrv);
900 aFrm.Height( mrpPrv->GetUpper()->getFramePrintArea().Height() );
901 }
902
903 bRet = true;
904 mrpPrv = nullptr;
905 mnParagraphCnt = 0;
906
907 if ( mrpActualSection )
908 {
909 //Did the SectionFrame even have a content? If not, we can
910 //directly put it somewhere else
911 SwSectionFrame *pSct;
912 bool bInit = false;
913 if ( !mrpActualSection->GetSectionFrame()->ContainsContent())
914 {
915 pSct = mrpActualSection->GetSectionFrame();
916 pSct->RemoveFromLayout();
917 }
918 else
919 {
920 pSct = new SwSectionFrame(
921 *mrpActualSection->GetSectionFrame(), false );
922 mrpActualSection->GetSectionFrame()->SimpleFormat();
923 bInit = true;
924 }
925 mrpActualSection->SetSectionFrame( pSct );
926 pSct->InsertBehind( mrpLay, nullptr );
927
928 if( bInit )
929 {
930 pSct->Init();
931 }
932
933 {
934 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pSct);
935 aFrm.Pos() = mrpLay->getFrameArea().Pos();
936 aFrm.Pos().AdjustY(1 ); //because of the notifications
937 }
938
939 mrpLay = pSct;
940 if ( mrpLay->Lower() && mrpLay->Lower()->IsLayoutFrame() )
941 mrpLay = mrpLay->GetNextLayoutLeaf();
942 }
943 }
944 } while( bLongTab || ( mpImpl && mnIndex < mpImpl->size() &&
945 mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex ) );
946 }
947 mbFirst = false;
948 return bRet;
949 }
950
951 struct SdrObjectCompare
952 {
operator ()SdrObjectCompare953 bool operator()( const SdrObject* pF1, const SdrObject* pF2 ) const
954 {
955 return pF1->GetOrdNum() < pF2->GetOrdNum();
956 }
957 };
958
959 struct FlyCacheCompare
960 {
operator ()FlyCacheCompare961 bool operator()( const SwFlyCache* pC1, const SwFlyCache* pC2 ) const
962 {
963 return pC1->nOrdNum < pC2->nOrdNum;
964 }
965 };
966
967 /**
968 * If a new page is inserted, the last page is analysed.
969 * If there are text frames with default position, the fly cache
970 * is checked, if these frames are stored in the cache.
971 */
CheckFlyCache_(SwPageFrame * pPage)972 void SwLayHelper::CheckFlyCache_( SwPageFrame* pPage )
973 {
974 if( !mpImpl || !pPage )
975 return;
976 const size_t nFlyCount = mpImpl->GetFlyCount();
977 // Any text frames at the page, fly cache available?
978 if( pPage->GetSortedObjs() && mnFlyIdx < nFlyCount )
979 {
980 SwSortedObjs &rObjs = *pPage->GetSortedObjs();
981 sal_uInt16 nPgNum = pPage->GetPhyPageNum();
982
983 // NOTE: Here we do not use the absolute ordnums but
984 // relative ordnums for the objects on this page.
985
986 // skip fly frames from pages before the current page
987 while( mnFlyIdx < nFlyCount &&
988 mpImpl->GetFlyCache(mnFlyIdx).nPageNum < nPgNum )
989 ++mnFlyIdx;
990
991 // sort cached objects on this page by ordnum
992 std::set< const SwFlyCache*, FlyCacheCompare > aFlyCacheSet;
993 size_t nIdx = mnFlyIdx;
994
995 SwFlyCache* pFlyC;
996 while( nIdx < nFlyCount &&
997 ( pFlyC = &mpImpl->GetFlyCache( nIdx ) )->nPageNum == nPgNum )
998 {
999 aFlyCacheSet.insert( pFlyC );
1000 ++nIdx;
1001 }
1002
1003 // sort objects on this page by ordnum
1004 std::set< const SdrObject*, SdrObjectCompare > aFlySet;
1005 for (SwAnchoredObject* pAnchoredObj : rObjs)
1006 {
1007 if (SwFlyFrame *pFly = dynamic_cast<SwFlyFrame*>(pAnchoredObj)) // a text frame?
1008 {
1009 if( pFly->GetAnchorFrame() &&
1010 !pFly->GetAnchorFrame()->FindFooterOrHeader() )
1011 {
1012 const SwContact *pC = ::GetUserCall( pAnchoredObj->GetDrawObj() );
1013 if( pC )
1014 {
1015 aFlySet.insert( pAnchoredObj->GetDrawObj() );
1016 }
1017 }
1018 }
1019 }
1020
1021 if ( aFlyCacheSet.size() == aFlySet.size() )
1022 {
1023 std::set< const SdrObject*, SdrObjectCompare >::iterator aFlySetIt =
1024 aFlySet.begin();
1025
1026 for ( const SwFlyCache* pFlyCache : aFlyCacheSet )
1027 {
1028 SwFlyFrame* pFly = const_cast<SwVirtFlyDrawObj*>(static_cast<const SwVirtFlyDrawObj*>(*aFlySetIt))->GetFlyFrame();
1029
1030 if ( pFly->getFrameArea().Left() == FAR_AWAY )
1031 {
1032 // we get the stored information
1033 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFly);
1034 aFrm.Pos().setX( pFlyCache->Left() + pPage->getFrameArea().Left() );
1035 aFrm.Pos().setY( pFlyCache->Top() + pPage->getFrameArea().Top() );
1036
1037 if ( mpImpl->IsUseFlyCache() )
1038 {
1039 aFrm.Width( pFlyCache->Width() );
1040 aFrm.Height( pFlyCache->Height() );
1041 }
1042 }
1043
1044 ++aFlySetIt;
1045 }
1046 }
1047 }
1048 }
1049
SwLayCacheIoImpl(SvStream & rStrm,bool bWrtMd)1050 SwLayCacheIoImpl::SwLayCacheIoImpl( SvStream& rStrm, bool bWrtMd ) :
1051 pStream( &rStrm ),
1052 nFlagRecEnd ( 0 ),
1053 nMajorVersion(SW_LAYCACHE_IO_VERSION_MAJOR),
1054 nMinorVersion(SW_LAYCACHE_IO_VERSION_MINOR),
1055 bWriteMode( bWrtMd ),
1056 bError( false )
1057 {
1058 if( bWriteMode )
1059 pStream->WriteUInt16( nMajorVersion )
1060 .WriteUInt16( nMinorVersion );
1061
1062 else
1063 pStream->ReadUInt16( nMajorVersion )
1064 .ReadUInt16( nMinorVersion );
1065 }
1066
OpenRec(sal_uInt8 cType)1067 void SwLayCacheIoImpl::OpenRec( sal_uInt8 cType )
1068 {
1069 sal_uInt32 nPos = pStream->Tell();
1070 if( bWriteMode )
1071 {
1072 aRecords.emplace_back(cType, nPos );
1073 pStream->WriteUInt32( 0 );
1074 }
1075 else
1076 {
1077 sal_uInt32 nVal(0);
1078 pStream->ReadUInt32( nVal );
1079 sal_uInt8 cRecTyp = static_cast<sal_uInt8>(nVal);
1080 if (!nVal || cRecTyp != cType || !pStream->good())
1081 {
1082 OSL_ENSURE( nVal, "OpenRec: Record-Header is 0" );
1083 OSL_ENSURE( cRecTyp == cType, "OpenRec: Wrong Record Type" );
1084 aRecords.emplace_back(0, pStream->Tell() );
1085 bError = true;
1086 }
1087 else
1088 {
1089 sal_uInt32 nSize = nVal >> 8;
1090 aRecords.emplace_back(cRecTyp, nPos+nSize );
1091 }
1092 }
1093 }
1094
1095 // Close record
CloseRec()1096 void SwLayCacheIoImpl::CloseRec()
1097 {
1098 bool bRes = true;
1099 OSL_ENSURE( !aRecords.empty(), "CloseRec: no levels" );
1100 if( !aRecords.empty() )
1101 {
1102 sal_uInt32 nPos = pStream->Tell();
1103 if( bWriteMode )
1104 {
1105 sal_uInt32 nBgn = aRecords.back().size;
1106 pStream->Seek( nBgn );
1107 sal_uInt32 nSize = nPos - nBgn;
1108 sal_uInt32 nVal = ( nSize << 8 ) | aRecords.back().type;
1109 pStream->WriteUInt32( nVal );
1110 pStream->Seek( nPos );
1111 if( pStream->GetError() != ERRCODE_NONE )
1112 bRes = false;
1113 }
1114 else
1115 {
1116 sal_uInt32 n = aRecords.back().size;
1117 OSL_ENSURE( n >= nPos, "CloseRec: too much data read" );
1118 if( n != nPos )
1119 {
1120 pStream->Seek( n );
1121 if( n < nPos )
1122 bRes = false;
1123 }
1124 if( pStream->GetErrorCode() != ERRCODE_NONE )
1125 bRes = false;
1126 }
1127 aRecords.pop_back();
1128 }
1129
1130 if( !bRes )
1131 bError = true;
1132 }
1133
BytesLeft()1134 sal_uInt32 SwLayCacheIoImpl::BytesLeft()
1135 {
1136 sal_uInt32 n = 0;
1137 if( !bError && !aRecords.empty() )
1138 {
1139 sal_uInt32 nEndPos = aRecords.back().size;
1140 sal_uInt32 nPos = pStream->Tell();
1141 if( nEndPos > nPos )
1142 n = nEndPos - nPos;
1143 }
1144 return n;
1145 }
1146
Peek()1147 sal_uInt8 SwLayCacheIoImpl::Peek()
1148 {
1149 sal_uInt8 c(0);
1150 if( !bError )
1151 {
1152 sal_uInt32 nPos = pStream->Tell();
1153 pStream->ReadUChar( c );
1154 pStream->Seek( nPos );
1155 if( pStream->GetErrorCode() != ERRCODE_NONE )
1156 {
1157 c = 0;
1158 bError = true;
1159 }
1160 }
1161 return c;
1162 }
1163
SkipRec()1164 void SwLayCacheIoImpl::SkipRec()
1165 {
1166 sal_uInt8 c = Peek();
1167 OpenRec( c );
1168 pStream->Seek( aRecords.back().size );
1169 CloseRec();
1170 }
1171
OpenFlagRec()1172 sal_uInt8 SwLayCacheIoImpl::OpenFlagRec()
1173 {
1174 OSL_ENSURE( !bWriteMode, "OpenFlagRec illegal in write mode" );
1175 sal_uInt8 cFlags(0);
1176 pStream->ReadUChar( cFlags );
1177 nFlagRecEnd = pStream->Tell() + ( cFlags & 0x0F );
1178 return (cFlags >> 4);
1179 }
1180
OpenFlagRec(sal_uInt8 nFlags,sal_uInt8 nLen)1181 void SwLayCacheIoImpl::OpenFlagRec( sal_uInt8 nFlags, sal_uInt8 nLen )
1182 {
1183 OSL_ENSURE( bWriteMode, "OpenFlagRec illegal in read mode" );
1184 OSL_ENSURE( (nFlags & 0xF0) == 0, "illegal flags set" );
1185 OSL_ENSURE( nLen < 16, "wrong flag record length" );
1186 sal_uInt8 cFlags = (nFlags << 4) + nLen;
1187 pStream->WriteUChar( cFlags );
1188 nFlagRecEnd = pStream->Tell() + nLen;
1189 }
1190
CloseFlagRec()1191 void SwLayCacheIoImpl::CloseFlagRec()
1192 {
1193 if( bWriteMode )
1194 {
1195 OSL_ENSURE( pStream->Tell() == nFlagRecEnd, "Wrong amount of data written" );
1196 }
1197 else
1198 {
1199 OSL_ENSURE( pStream->Tell() <= nFlagRecEnd, "Too many data read" );
1200 if( pStream->Tell() != nFlagRecEnd )
1201 pStream->Seek( nFlagRecEnd );
1202 }
1203 }
1204
1205 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1206