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