1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: laycache.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sw.hxx"
35 #include <hintids.hxx>
36 #include <svx/brkitem.hxx>
37 #include <tools/stream.hxx>
39 #include <docstat.hxx>
41 #include <fmtpdsc.hxx>
42 #include <laycache.hxx>
43 #include <layhelp.hxx>
44 #include <pagefrm.hxx>
45 #include <rootfrm.hxx>
48 #include <swtable.hxx>
52 #include <bodyfrm.hxx>
53 #include <ndindex.hxx>
54 #include <sectfrm.hxx>
56 #include <fmtcntnt.hxx>
57 #include <pagedesc.hxx>
58 #include <frmtool.hxx>
59 #include <dflyobj.hxx>
60 #include <dcontact.hxx>
62 // OD 2004-05-24 #i28701#
63 #include <sortedobjs.hxx>
64 // --> OD 2006-03-22 #b6375613#
69 #include <com/sun/star/document/XDocumentInfoSupplier.hpp>
70 #include <com/sun/star/beans/XPropertySet.hpp>
72 using namespace ::com::sun::star
;
77 SV_IMPL_PTRARR( SwPageFlyCache
, SwFlyCachePtr
)
79 /*-----------------28.5.2001 10:06------------------
80 * Reading and writing of the layout cache.
81 * The layout cache is not necessary, but it improves
82 * the performance and reduces the text flow during
84 * The layout cache contains the index of the paragraphs/tables
85 * at the top of every page, so it's possible to create
86 * the right count of pages and to distribute the document content
87 * to this pages before the formatting starts.
88 *--------------------------------------------------*/
90 void SwLayoutCache::Read( SvStream
&rStream
)
94 pImpl
= new SwLayCacheImpl
;
95 if( !pImpl
->Read( rStream
) )
103 //-----------------------------------------------------------------------------
105 void SwLayCacheImpl::Insert( USHORT nType
, ULONG nIndex
, xub_StrLen nOffset
)
107 aType
.Insert( nType
, aType
.Count() );
108 SvULongs::Insert( nIndex
, SvULongs::Count() );
109 aOffset
.Insert( nOffset
, aOffset
.Count() );
112 BOOL
SwLayCacheImpl::Read( SvStream
& rStream
)
114 SwLayCacheIoImpl
aIo( rStream
, FALSE
);
115 if( aIo
.GetMajorVersion() > SW_LAYCACHE_IO_VERSION_MAJOR
)
118 // Due to an evil bug in the layout cache (#102759#), we cannot trust the
119 // sizes of fly frames which have been written using the "old" layout cache.
120 // This flag should indicate that we do not want to trust the width and
121 // height of fly frames
122 bUseFlyCache
= aIo
.GetMinorVersion() >= 1;
125 UINT32 nIndex
, nOffset
;
127 aIo
.OpenRec( SW_LAYCACHE_IO_REC_PAGES
);
130 while( aIo
.BytesLeft() && !aIo
.HasError() )
134 case SW_LAYCACHE_IO_REC_PARA
:
135 aIo
.OpenRec( SW_LAYCACHE_IO_REC_PARA
);
136 cFlags
= aIo
.OpenFlagRec();
137 aIo
.GetStream() >> nIndex
;
138 if( (cFlags
& 0x01) != 0 )
139 aIo
.GetStream() >> nOffset
;
141 nOffset
= STRING_LEN
;
143 Insert( SW_LAYCACHE_IO_REC_PARA
, nIndex
, (xub_StrLen
)nOffset
);
144 aIo
.CloseRec( SW_LAYCACHE_IO_REC_PARA
);
146 case SW_LAYCACHE_IO_REC_TABLE
:
147 aIo
.OpenRec( SW_LAYCACHE_IO_REC_TABLE
);
149 aIo
.GetStream() >> nIndex
151 Insert( SW_LAYCACHE_IO_REC_TABLE
, nIndex
, (xub_StrLen
)nOffset
);
153 aIo
.CloseRec( SW_LAYCACHE_IO_REC_TABLE
);
155 case SW_LAYCACHE_IO_REC_FLY
:
157 aIo
.OpenRec( SW_LAYCACHE_IO_REC_FLY
);
162 aIo
.GetStream() >> nPgNum
>> nIndex
163 >> nX
>> nY
>> nW
>> nH
;
164 SwFlyCache
* pFly
= new SwFlyCache( nPgNum
, nIndex
, nX
, nY
, nW
, nH
);
165 aFlyCache
.Insert( pFly
, aFlyCache
.Count() );
166 aIo
.CloseRec( SW_LAYCACHE_IO_REC_FLY
);
174 aIo
.CloseRec( SW_LAYCACHE_IO_REC_PAGES
);
176 return !aIo
.HasError();
179 /*-----------------28.5.2001 10:19------------------
180 * SwLayoutCache::Write(..)
181 * writes the index (more precise: the difference between
182 * the index and the first index of the document content)
183 * of the first paragraph/table at the top of every page.
184 * If at the top of a page is the rest of a paragraph/table
185 * from the bottom of the previous page, the character/row
186 * number is stored, too.
187 * The position, size and page number of the text frames
189 * --------------------------------------------------*/
191 void SwLayoutCache::Write( SvStream
&rStream
, const SwDoc
& rDoc
)
193 if( rDoc
.GetRootFrm() ) // the layout itself ..
195 SwLayCacheIoImpl
aIo( rStream
, TRUE
);
196 // We want to save the relative index, so we need the index
197 // of the first content
198 ULONG nStartOfContent
= rDoc
.GetNodes().GetEndOfContent().
199 StartOfSectionNode()->GetIndex();
201 SwPageFrm
* pPage
= (SwPageFrm
*)rDoc
.GetRootFrm()->Lower();
203 aIo
.OpenRec( SW_LAYCACHE_IO_REC_PAGES
);
204 aIo
.OpenFlagRec( 0, 0 );
208 if( pPage
->GetPrev() )
210 SwLayoutFrm
* pLay
= pPage
->FindBodyCont();
211 SwFrm
* pTmp
= pLay
? pLay
->ContainsAny() : NULL
;
212 // We are only interested in paragraph or table frames,
213 // a section frames contains paragraphs/tables.
214 if( pTmp
&& pTmp
->IsSctFrm() )
215 pTmp
= ((SwSectionFrm
*)pTmp
)->ContainsAny();
217 if( pTmp
) // any content
219 if( pTmp
->IsTxtFrm() )
221 ULONG nNdIdx
= ((SwTxtFrm
*)pTmp
)->GetNode()->GetIndex();
222 if( nNdIdx
> nStartOfContent
)
224 /* Open Paragraph Record */
225 aIo
.OpenRec( SW_LAYCACHE_IO_REC_PARA
);
226 BOOL bFollow
= ((SwTxtFrm
*)pTmp
)->IsFollow();
227 aIo
.OpenFlagRec( bFollow
? 0x01 : 0x00,
229 nNdIdx
-= nStartOfContent
;
230 aIo
.GetStream() << static_cast<sal_uInt32
>(nNdIdx
);
232 aIo
.GetStream() << static_cast<sal_uInt32
>(((SwTxtFrm
*)pTmp
)->GetOfst());
234 /* Close Paragraph Record */
235 aIo
.CloseRec( SW_LAYCACHE_IO_REC_PARA
);
238 else if( pTmp
->IsTabFrm() )
240 SwTabFrm
* pTab
= (SwTabFrm
*)pTmp
;
241 ULONG nOfst
= STRING_LEN
;
242 if( pTab
->IsFollow() )
244 // If the table is a follow, we have to look for the
245 // master and to count all rows to get the row number
247 if( pTab
->IsFollow() )
248 pTab
= pTab
->FindMaster( true );
249 while( pTab
!= pTmp
)
251 SwFrm
* pSub
= pTab
->Lower();
255 pSub
= pSub
->GetNext();
257 pTab
= pTab
->GetFollow();
258 ASSERT( pTab
, "Table follow without master" );
264 pTab
->GetTable()->GetTableNode()->GetIndex();
265 if( nNdIdx
> nStartOfContent
)
267 /* Open Table Record */
268 aIo
.OpenRec( SW_LAYCACHE_IO_REC_TABLE
);
269 aIo
.OpenFlagRec( 0, 8 );
270 nNdIdx
-= nStartOfContent
;
271 aIo
.GetStream() << static_cast<sal_uInt32
>(nNdIdx
)
272 << static_cast<sal_uInt32
>(nOfst
);
274 /* Close Table Record */
275 aIo
.CloseRec( SW_LAYCACHE_IO_REC_TABLE
);
277 // If the table has a follow on the next page,
278 // we know already the row number and store this
280 if( pTab
->GetFollow() )
282 if( nOfst
== STRING_LEN
)
286 SwFrm
* pSub
= pTab
->Lower();
290 pSub
= pSub
->GetNext();
292 pTab
= pTab
->GetFollow();
293 SwPageFrm
*pTabPage
= pTab
->FindPageFrm();
294 if( pTabPage
!= pPage
)
296 ASSERT( pPage
->GetPhyPageNum() <
297 pTabPage
->GetPhyPageNum(),
298 "Looping Tableframes" );
302 } while ( pTab
->GetFollow() );
310 if( pPage
->GetSortedObjs() )
312 SwSortedObjs
&rObjs
= *pPage
->GetSortedObjs();
313 for ( USHORT i
= 0; i
< rObjs
.Count(); ++i
)
315 SwAnchoredObject
* pAnchoredObj
= rObjs
[i
];
316 if ( pAnchoredObj
->ISA(SwFlyFrm
) )
318 SwFlyFrm
*pFly
= static_cast<SwFlyFrm
*>(pAnchoredObj
);
319 if( pFly
->Frm().Left() != WEIT_WECH
&&
320 !pFly
->GetAnchorFrm()->FindFooterOrHeader() )
322 const SwContact
*pC
=
323 ::GetUserCall(pAnchoredObj
->GetDrawObj());
326 sal_uInt32 nOrdNum
= pAnchoredObj
->GetDrawObj()->GetOrdNum();
327 USHORT nPageNum
= pPage
->GetPhyPageNum();
328 /* Open Fly Record */
329 aIo
.OpenRec( SW_LAYCACHE_IO_REC_FLY
);
330 aIo
.OpenFlagRec( 0, 0 );
332 SwRect
&rRct
= pFly
->Frm();
333 sal_Int32 nX
= rRct
.Left() - pPage
->Frm().Left();
334 sal_Int32 nY
= rRct
.Top() - pPage
->Frm().Top();
335 aIo
.GetStream() << nPageNum
<< nOrdNum
336 << nX
<< nY
<< rRct
.Width()
338 /* Close Fly Record */
339 aIo
.CloseRec( SW_LAYCACHE_IO_REC_FLY
);
345 pPage
= (SwPageFrm
*)pPage
->GetNext();
347 aIo
.CloseRec( SW_LAYCACHE_IO_REC_PAGES
);
352 sal_Bool
SwLayoutCache::CompareLayout( const SwDoc
& rDoc
) const
354 sal_Bool bRet
= sal_True
;
355 if( pImpl
&& rDoc
.GetRootFrm() )
358 ULONG nStartOfContent
= rDoc
.GetNodes().GetEndOfContent().
359 StartOfSectionNode()->GetIndex();
360 SwPageFrm
* pPage
= (SwPageFrm
*)rDoc
.GetRootFrm()->Lower();
362 pPage
= (SwPageFrm
*)pPage
->GetNext();
365 if( nIndex
>= pImpl
->Count() )
371 SwLayoutFrm
* pLay
= pPage
->FindBodyCont();
372 SwFrm
* pTmp
= pLay
? pLay
->ContainsAny() : NULL
;
373 if( pTmp
&& pTmp
->IsSctFrm() )
374 pTmp
= ((SwSectionFrm
*)pTmp
)->ContainsAny();
377 if( pTmp
->IsTxtFrm() )
379 ULONG nNdIdx
= ((SwTxtFrm
*)pTmp
)->GetNode()->GetIndex();
380 if( nNdIdx
> nStartOfContent
)
382 BOOL bFollow
= ((SwTxtFrm
*)pTmp
)->IsFollow();
383 nNdIdx
-= nStartOfContent
;
384 if( pImpl
->GetBreakIndex( nIndex
) != nNdIdx
||
385 SW_LAYCACHE_IO_REC_PARA
!=
386 pImpl
->GetBreakType( nIndex
) ||
387 ( bFollow
? ((SwTxtFrm
*)pTmp
)->GetOfst()
388 : STRING_LEN
) != pImpl
->GetBreakOfst( nIndex
) )
396 else if( pTmp
->IsTabFrm() )
398 SwTabFrm
* pTab
= (SwTabFrm
*)pTmp
;
399 ULONG nOfst
= STRING_LEN
;
400 if( pTab
->IsFollow() )
403 if( pTab
->IsFollow() )
404 pTab
= pTab
->FindMaster( true );
405 while( pTab
!= pTmp
)
407 SwFrm
* pSub
= pTab
->Lower();
411 pSub
= pSub
->GetNext();
413 pTab
= pTab
->GetFollow();
419 pTab
->GetTable()->GetTableNode()->GetIndex();
420 if( nNdIdx
> nStartOfContent
)
422 nNdIdx
-= nStartOfContent
;
423 if( pImpl
->GetBreakIndex( nIndex
) != nNdIdx
||
424 SW_LAYCACHE_IO_REC_TABLE
!=
425 pImpl
->GetBreakType( nIndex
) ||
426 nOfst
!= pImpl
->GetBreakOfst( nIndex
) )
433 if( pTab
->GetFollow() )
435 if( nOfst
== STRING_LEN
)
439 SwFrm
* pSub
= pTab
->Lower();
443 pSub
= pSub
->GetNext();
445 pTab
= pTab
->GetFollow();
446 SwPageFrm
*pTabPage
= pTab
->FindPageFrm();
447 if( pTabPage
!= pPage
)
452 } while ( pTab
->GetFollow() );
459 pPage
= (SwPageFrm
*)pPage
->GetNext();
466 void SwLayoutCache::ClearImpl()
476 SwLayoutCache::~SwLayoutCache()
478 ASSERT( !nLockCount
, "Deleting a locked SwLayoutCache!?" );
482 /*-----------------28.5.2001 10:47------------------
484 * a help class to create not nested section frames
485 * for nested sections.
486 * --------------------------------------------------*/
488 SwActualSection::SwActualSection( SwActualSection
*pUp
,
490 SwSectionNode
*pNd
) :
497 const SwNodeIndex
*pIndex
= pSect
->GetFmt()->GetCntnt().GetCntntIdx();
498 pSectNode
= pSect
->GetFmt()->GetDoc()->GetNodes()[*pIndex
]->
503 /*-----------------28.5.2001 11:09------------------
505 * is the helper class, which utilizes the layout cache information
506 * to distribute the document content to the rigth pages.
507 * It's used by the _InsertCnt(..)-function.
508 * If there's no layout cache, the distibution to the pages is more
509 * a guess, but a guess with statistical background.
510 * --------------------------------------------------*/
512 SwLayHelper::SwLayHelper( SwDoc
*pD
, SwFrm
* &rpF
, SwFrm
* &rpP
, SwPageFrm
* &rpPg
,
513 SwLayoutFrm
* &rpL
, SwActualSection
* &rpA
, BOOL
&rB
,
514 ULONG nNodeIndex
, BOOL bCache
)
515 : rpFrm( rpF
), rpPrv( rpP
), rpPage( rpPg
), rpLay( rpL
),
516 rpActualSection( rpA
), rbBreakAfter(rB
), pDoc(pD
), nMaxParaPerPage( 25 ),
517 nParagraphCnt( bCache
? 0 : USHRT_MAX
), bFirst( bCache
)
519 pImpl
= pDoc
->GetLayoutCache() ? pDoc
->GetLayoutCache()->LockImpl() : NULL
;
522 nMaxParaPerPage
= 1000;
523 nStartOfContent
= pDoc
->GetNodes().GetEndOfContent().StartOfSectionNode()
525 nNodeIndex
-= nStartOfContent
;
528 while( nIndex
< pImpl
->Count() && (*pImpl
)[ nIndex
] < nNodeIndex
)
530 if( nIndex
>= pImpl
->Count() )
532 pDoc
->GetLayoutCache()->UnlockImpl();
539 nStartOfContent
= ULONG_MAX
;
543 SwLayHelper::~SwLayHelper()
547 ASSERT( pDoc
&& pDoc
->GetLayoutCache(), "Missing layoutcache" );
548 pDoc
->GetLayoutCache()->UnlockImpl();
552 /*-----------------23.5.2001 16:40------------------
553 * SwLayHelper::CalcPageCount() does not really calculate the page count,
554 * it returns the page count value from the layout cache, if available,
555 * otherwise it estimates the page count.
556 * --------------------------------------------------*/
558 ULONG
SwLayHelper::CalcPageCount()
561 SwLayCacheImpl
*pCache
= pDoc
->GetLayoutCache() ?
562 pDoc
->GetLayoutCache()->LockImpl() : NULL
;
565 nPgCount
= pCache
->Count() + 1;
566 pDoc
->GetLayoutCache()->UnlockImpl();
570 nPgCount
= pDoc
->GetDocStat().nPage
;
571 if ( nPgCount
<= 10 ) // no page insertion for less than 10 pages
573 ULONG nNdCount
= pDoc
->GetDocStat().nPara
;
576 //Estimates the number of paragraphs.
577 ULONG nTmp
= pDoc
->GetNodes().GetEndOfContent().GetIndex() -
578 pDoc
->GetNodes().GetEndOfExtras().GetIndex();
579 //Tables have a little overhead..
580 nTmp
-= pDoc
->GetTblFrmFmts()->Count() * 25;
582 nTmp
-= (pDoc
->GetNodes().GetEndOfAutotext().GetIndex() -
583 pDoc
->GetNodes().GetEndOfInserts().GetIndex()) / 3 * 5;
587 if ( nNdCount
> 100 ) // no estimation below this value
590 nMaxParaPerPage
= nNdCount
/ nPgCount
;
593 nMaxParaPerPage
= Max( ULONG(20),
594 ULONG(20 + nNdCount
/ 1000 * 3) );
596 const ULONG nMax
= 49;
598 const ULONG nMax
= 53;
600 nMaxParaPerPage
= Min( nMaxParaPerPage
, nMax
);
601 nPgCount
= nNdCount
/ nMaxParaPerPage
;
603 if ( nNdCount
< 1000 )
604 nPgCount
= 0;// no progress bar for small documents
605 if ( pDoc
->get(IDocumentSettingAccess::BROWSE_MODE
) )
606 nMaxParaPerPage
*= 6;
612 /*-----------------23.5.2001 16:44------------------
613 * SwLayHelper::CheckInsertPage()
614 * inserts a page and return TRUE, if
615 * - the break after flag is set
616 * - the actual content wants a break before
617 * - the maximum count of paragraph/rows is reached
619 * The break after flag is set, if the actual content
620 * wants a break after.
621 * --------------------------------------------------*/
623 BOOL
SwLayHelper::CheckInsertPage()
625 BOOL bEnd
= 0 == rpPage
->GetNext();
626 const SwAttrSet
* pAttr
= rpFrm
->GetAttrSet();
627 const SvxFmtBreakItem
& rBrk
= pAttr
->GetBreak();
628 const SwFmtPageDesc
& rDesc
= pAttr
->GetPageDesc();
629 // --> FME 2004-10-26 #118195# Do not evaluate page description if frame
630 // is a follow frame!
631 const SwPageDesc
* pDesc
= rpFrm
->IsFlowFrm() &&
632 SwFlowFrm::CastFlowFrm( rpFrm
)->IsFollow() ?
637 BOOL bBrk
= nParagraphCnt
> nMaxParaPerPage
|| rbBreakAfter
;
638 rbBreakAfter
= rBrk
.GetBreak() == SVX_BREAK_PAGE_AFTER
||
639 rBrk
.GetBreak() == SVX_BREAK_PAGE_BOTH
;
641 bBrk
= rBrk
.GetBreak() == SVX_BREAK_PAGE_BEFORE
||
642 rBrk
.GetBreak() == SVX_BREAK_PAGE_BOTH
;
648 pDesc
= rpPage
->GetPageDesc()->GetFollow();
651 if ( 0 != (nPgNum
= rDesc
.GetNumOffset()) )
652 ((SwRootFrm
*)rpPage
->GetUpper())->SetVirtPageNum(TRUE
);
654 BOOL bNextPageOdd
= !rpPage
->OnRightPage();
655 BOOL bInsertEmpty
= FALSE
;
656 if( nPgNum
&& bNextPageOdd
!= ( ( nPgNum
% 2 ) != 0 ) )
658 bNextPageOdd
= !bNextPageOdd
;
661 ::InsertNewPage( (SwPageDesc
&)*pDesc
, rpPage
->GetUpper(),
662 bNextPageOdd
, bInsertEmpty
, FALSE
, rpPage
->GetNext() );
665 ASSERT( rpPage
->GetNext(), "Keine neue Seite?" );
667 { rpPage
= (SwPageFrm
*)rpPage
->GetNext();
668 } while ( rpPage
->GetNext() );
672 ASSERT( rpPage
->GetNext(), "Keine neue Seite?" );
673 rpPage
= (SwPageFrm
*)rpPage
->GetNext();
674 if ( rpPage
->IsEmptyPage() )
676 ASSERT( rpPage
->GetNext(), "Keine neue Seite?" );
677 rpPage
= (SwPageFrm
*)rpPage
->GetNext();
680 rpLay
= rpPage
->FindBodyCont();
681 while( rpLay
->Lower() )
682 rpLay
= (SwLayoutFrm
*)rpLay
->Lower();
688 // --> OD 2006-03-22 #b6375613#
689 bool lcl_HasTextFrmAnchoredObjs( SwTxtFrm
* p_pTxtFrm
)
691 bool bHasTextFrmAnchoredObjs( false );
693 const SwSpzFrmFmts
* pSpzFrmFmts
= p_pTxtFrm
->GetTxtNode()->GetDoc()->GetSpzFrmFmts();
694 for ( USHORT i
= 0; i
< pSpzFrmFmts
->Count(); ++i
)
696 SwFrmFmt
*pFmt
= (SwFrmFmt
*)(*pSpzFrmFmts
)[i
];
697 const SwFmtAnchor
&rAnch
= pFmt
->GetAnchor();
698 if ( rAnch
.GetCntntAnchor() &&
699 ( rAnch
.GetAnchorId() == FLY_AT_CNTNT
||
700 rAnch
.GetAnchorId() == FLY_AUTO_CNTNT
) &&
701 rAnch
.GetCntntAnchor()->nNode
.GetIndex() ==
702 p_pTxtFrm
->GetTxtNode()->GetIndex() )
704 bHasTextFrmAnchoredObjs
= true;
709 return bHasTextFrmAnchoredObjs
;
712 void lcl_ApplyWorkaroundForB6375613( SwFrm
* p_pFirstFrmOnNewPage
)
714 SwTxtFrm
* pFirstTextFrmOnNewPage
= dynamic_cast<SwTxtFrm
*>(p_pFirstFrmOnNewPage
);
716 if ( pFirstTextFrmOnNewPage
&&
717 !pFirstTextFrmOnNewPage
->IsFollow() &&
718 pFirstTextFrmOnNewPage
->GetTxt().Len() == 0 &&
719 lcl_HasTextFrmAnchoredObjs( pFirstTextFrmOnNewPage
) )
721 // apply page break before at this text frame to assure, that it doesn't flow backward.
722 const SvxBreak eBreak
=
723 pFirstTextFrmOnNewPage
->GetAttrSet()->GetBreak().GetBreak();
724 if ( eBreak
== SVX_BREAK_NONE
)
726 pFirstTextFrmOnNewPage
->GetTxtNode()->LockModify();
727 SwDoc
* pDoc( pFirstTextFrmOnNewPage
->GetTxtNode()->GetDoc() );
728 IDocumentContentOperations
* pIDCO
= pFirstTextFrmOnNewPage
->GetTxtNode()->getIDocumentContentOperations();
729 const SwPaM
aTmpPaM( *(pFirstTextFrmOnNewPage
->GetTxtNode()) );
730 pIDCO
->Insert( aTmpPaM
, SvxFmtBreakItem( SVX_BREAK_PAGE_BEFORE
, RES_BREAK
), 0 );
731 pFirstTextFrmOnNewPage
->GetTxtNode()->UnlockModify();
733 uno::Reference
< document::XDocumentInfoSupplier
> xDoc(
734 pDoc
->GetDocShell()->GetBaseModel(),
736 uno::Reference
< beans::XPropertySet
> xDocInfo(
737 xDoc
->getDocumentInfo(),
741 xDocInfo
->setPropertyValue( rtl::OUString::createFromAscii("WorkaroundForB6375613Applied"), uno::makeAny( true ) );
743 catch( uno::Exception
& )
751 /*-----------------28.5.2001 11:31------------------
752 * SwLayHelper::CheckInsert
753 * is the entry point for the _InsertCnt-function.
754 * The document content index is checked either it is
755 * in the layout cache either it's time to insert a page
756 * cause the maximal estimation of content per page is reached.
757 * A really big table or long paragraph may contains more than
758 * one page, in this case the needed count of pages will inserted.
759 * --------------------------------------------------*/
761 BOOL
SwLayHelper::CheckInsert( ULONG nNodeIndex
)
764 BOOL bLongTab
= FALSE
;
765 ULONG
nMaxRowPerPage( 0 );
766 nNodeIndex
-= nStartOfContent
;
768 if( rpFrm
->IsTabFrm() )
770 //Inside a table counts every row as a paragraph
771 SwFrm
*pLow
= ((SwTabFrm
*)rpFrm
)->Lower();
776 pLow
= pLow
->GetNext();
778 nParagraphCnt
+= nRows
;
779 if( !pImpl
&& nParagraphCnt
> nMaxParaPerPage
+ 10 )
781 // OD 09.04.2003 #108698# - improve heuristics:
782 // Assume that a table, which has more than three times the quantity
783 // of maximal paragraphs per page rows, consists of rows, which have
784 // the height of a normal paragraph. Thus, allow as much rows per page
785 // as much paragraphs are allowed.
786 if ( nRows
> ( 3*nMaxParaPerPage
) )
788 nMaxRowPerPage
= nMaxParaPerPage
;
792 SwFrm
*pTmp
= ((SwTabFrm
*)rpFrm
)->Lower();
793 if( pTmp
->GetNext() )
794 pTmp
= pTmp
->GetNext();
795 pTmp
= ((SwRowFrm
*)pTmp
)->Lower();
800 pTmp
= pTmp
->GetNext();
802 nMaxRowPerPage
= Max( ULONG(2), nMaxParaPerPage
/ nCnt
);
809 if( bFirst
&& pImpl
&& nIndex
< pImpl
->Count() &&
810 pImpl
->GetBreakIndex( nIndex
) == nNodeIndex
&&
811 ( pImpl
->GetBreakOfst( nIndex
) < STRING_LEN
||
812 ( ++nIndex
< pImpl
->Count() &&
813 pImpl
->GetBreakIndex( nIndex
) == nNodeIndex
) ) )
815 #if OSL_DEBUG_LEVEL > 1
816 ULONG nBreakIndex
= ( pImpl
&& nIndex
< pImpl
->Count() ) ?
817 pImpl
->GetBreakIndex(nIndex
) : 0xffff;
820 // OD 09.04.2003 #108698# - always split a big tables.
822 ( rpFrm
->IsTabFrm() && bLongTab
)
828 if( pImpl
|| bLongTab
)
830 #if OSL_DEBUG_LEVEL > 1
831 ULONG nBrkIndex
= ( pImpl
&& nIndex
< pImpl
->Count() ) ?
832 pImpl
->GetBreakIndex(nIndex
) : 0xffff;
835 xub_StrLen nOfst
= STRING_LEN
;
836 USHORT nType
= SW_LAYCACHE_IO_REC_PAGES
;
839 rbBreakAfter
= sal_True
;
840 nOfst
= static_cast<xub_StrLen
>(nRowCount
+ nMaxRowPerPage
);
844 while( nIndex
< pImpl
->Count() &&
845 pImpl
->GetBreakIndex(nIndex
) < nNodeIndex
)
847 if( nIndex
< pImpl
->Count() &&
848 pImpl
->GetBreakIndex(nIndex
) == nNodeIndex
)
850 nType
= pImpl
->GetBreakType( nIndex
);
851 nOfst
= pImpl
->GetBreakOfst( nIndex
++ );
852 rbBreakAfter
= sal_True
;
856 if( nOfst
< STRING_LEN
)
858 sal_Bool bSplit
= sal_False
;
860 if( !bLongTab
&& rpFrm
->IsTxtFrm() &&
861 SW_LAYCACHE_IO_REC_PARA
== nType
&&
862 nOfst
<((SwTxtFrm
*)rpFrm
)->GetTxtNode()->GetTxt().Len() )
864 else if( rpFrm
->IsTabFrm() && nRowCount
< nOfst
&&
865 ( bLongTab
|| SW_LAYCACHE_IO_REC_TABLE
== nType
) )
867 nRepeat
= ((SwTabFrm
*)rpFrm
)->
868 GetTable()->GetRowsToRepeat();
869 bSplit
= nOfst
< nRows
&& nRowCount
+ nRepeat
< nOfst
;
870 bLongTab
= bLongTab
&& bSplit
;
874 rpFrm
->InsertBehind( rpLay
, rpPrv
);
875 rpFrm
->Frm().Pos() = rpLay
->Frm().Pos();
876 rpFrm
->Frm().Pos().Y() += 1;
878 if( rpFrm
->IsTabFrm() )
880 SwTabFrm
* pTab
= (SwTabFrm
*)rpFrm
;
881 // --> OD 2004-09-23 #i33629#, #i29955#
882 ::RegistFlys( pTab
->FindPageFrm(), pTab
);
884 SwFrm
*pRow
= pTab
->Lower();
885 SwTabFrm
*pFoll
= new SwTabFrm( *pTab
);
890 bDontCreateObjects
= TRUE
; //frmtool
892 // Insert new headlines:
894 SwRowFrm
* pHeadline
= 0;
895 while( nRowIdx
< nRepeat
)
897 ASSERT( pTab
->GetTable()->GetTabLines()[ nRowIdx
], "Table ohne Zeilen?" );
899 new SwRowFrm( *pTab
->GetTable()->GetTabLines()[ nRowIdx
] );
900 pHeadline
->SetRepeatedHeadline( true );
901 pHeadline
->InsertBefore( pFoll
, 0 );
902 pHeadline
->RegistFlys();
907 bDontCreateObjects
= FALSE
;
909 nRows
= nRows
+ nRepeat
;
913 while( pRow
&& nRowCount
< nOfst
)
915 pRow
= pRow
->GetNext();
920 SwFrm
* pNxt
= pRow
->GetNext();
922 pRow
->InsertBehind( pFoll
, pPrv
);
930 SwTxtFrm
*pNew
= new SwTxtFrm( ((SwTxtFrm
*)rpFrm
)->
932 pNew
->_SetIsFollow( sal_True
);
933 pNew
->ManipOfst( nOfst
);
934 pNew
->SetFollow( ((SwTxtFrm
*)rpFrm
)->GetFollow() );
935 ((SwTxtFrm
*)rpFrm
)->SetFollow( pNew
);
942 SwPageFrm
* pLastPage
= rpPage
;
943 if( CheckInsertPage() )
945 // --> OD 2006-03-21 #b6375613#
946 if ( pDoc
->ApplyWorkaroundForB6375613() )
948 lcl_ApplyWorkaroundForB6375613( rpFrm
);
951 _CheckFlyCache( pLastPage
);
952 if( rpPrv
&& rpPrv
->IsTxtFrm() && !rpPrv
->GetValidSizeFlag() )
953 rpPrv
->Frm().Height( rpPrv
->GetUpper()->Prt().Height() );
959 if ( rpActualSection
)
961 //Hatte der SectionFrm ueberhaupt Inhalt? Wenn
962 //nicht kann er gleich umgehaengt werden.
965 if ( !rpActualSection
->GetSectionFrm()->ContainsCntnt())
967 pSct
= rpActualSection
->GetSectionFrm();
972 pSct
= new SwSectionFrm(
973 *rpActualSection
->GetSectionFrm(), FALSE
);
974 rpActualSection
->GetSectionFrm()->SimpleFormat();
977 rpActualSection
->SetSectionFrm( pSct
);
978 pSct
->InsertBehind( rpLay
, 0 );
981 pSct
->Frm().Pos() = rpLay
->Frm().Pos();
982 pSct
->Frm().Pos().Y() += 1; //wg. Benachrichtigungen.
985 if ( rpLay
->Lower() && rpLay
->Lower()->IsLayoutFrm() )
986 rpLay
= rpLay
->GetNextLayoutLeaf();
989 } while( bLongTab
|| ( pImpl
&& nIndex
< pImpl
->Count() &&
990 (*pImpl
)[ nIndex
] == nNodeIndex
) );
996 struct SdrObjectCompare
998 bool operator()( const SdrObject
* pF1
, const SdrObject
* pF2
) const
1000 return pF1
->GetOrdNum() < pF2
->GetOrdNum();
1004 struct FlyCacheCompare
1006 bool operator()( const SwFlyCache
* pC1
, const SwFlyCache
* pC2
) const
1008 return pC1
->nOrdNum
< pC2
->nOrdNum
;
1012 /*-----------------28.6.2001 14:40------------------
1013 * SwLayHelper::_CheckFlyCache(..)
1014 * If a new page is inserted, the last page is analysed.
1015 * If there are text frames with default position, the fly cache
1016 * is checked, if these frames are stored in the cache.
1017 * --------------------------------------------------*/
1019 void SwLayHelper::_CheckFlyCache( SwPageFrm
* pPage
)
1021 if( !pImpl
|| !pPage
)
1023 USHORT nFlyCount
= pImpl
->GetFlyCount();
1024 // Any text frames at the page, fly cache avaiable?
1025 if( pPage
->GetSortedObjs() && nFlyIdx
< nFlyCount
)
1027 SwSortedObjs
&rObjs
= *pPage
->GetSortedObjs();
1028 USHORT nPgNum
= pPage
->GetPhyPageNum();
1033 // NOTE: This code assumes that all objects have already been
1034 // inserted into the drawing layout, so that the cached objects
1035 // can be identified by their ordnum. Unfortunately this function
1036 // is called with page n if page n+1 has been inserted. Thus
1037 // not all the objects have been inserted and the ordnums cannot
1038 // be used to identify the objects.
1041 for ( USHORT i = 0; i < rObjs.Count(); ++i ) // check objects
1043 SdrObject *pO = rObjs[i];
1044 if ( pO->ISA(SwVirtFlyDrawObj) ) // a text frame?
1046 SwFlyFrm *pFly = ((SwVirtFlyDrawObj*)pO)->GetFlyFrm();
1047 if( pFly->Frm().Left() == WEIT_WECH && pFly->GetAnchor() &&
1048 !pFly->GetAnchor()->FindFooterOrHeader() )
1049 { // Only frame with default position and not in header/footer
1050 const SwContact *pC = (SwContact*)GetUserCall(pO);
1053 ULONG nOrdNum = pO->GetOrdNum(); // the Id
1055 while( nFlyIdx < nFlyCount && ( pFlyC = pImpl->
1056 GetFlyCache(nFlyIdx) )->nPageNum < nPgNum)
1058 if( nFlyIdx < nFlyCount &&
1059 pFlyC->nPageNum == nPgNum )
1061 USHORT nIdx = nFlyIdx;
1062 while( nIdx < nFlyCount && ( pFlyC = pImpl->
1063 GetFlyCache( nIdx ) )->nPageNum == nPgNum &&
1064 pFlyC->nOrdNum != nOrdNum )
1066 if( nIdx < nFlyCount && pFlyC->nPageNum == nPgNum &&
1067 pFlyC->nOrdNum == nOrdNum )
1068 { // we get the stored information
1069 pFly->Frm().Pos().X() = pFlyC->Left() +
1070 pPage->Frm().Left();
1071 pFly->Frm().Pos().Y() = pFlyC->Top() +
1073 pFly->Frm().Width( pFlyC->Width() );
1074 pFly->Frm().Height( pFlyC->Height() );
1084 // NOTE: Here we do not use the absolute ordnums but
1085 // relative ordnums for the objects on this page.
1087 // skip fly frames from pages before the current page
1089 while( nFlyIdx
< nFlyCount
&& ( pFlyC
= pImpl
->
1090 GetFlyCache(nFlyIdx
) )->nPageNum
< nPgNum
)
1093 // sort cached objects on this page by ordnum
1094 std::set
< const SwFlyCache
*, FlyCacheCompare
> aFlyCacheSet
;
1095 USHORT nIdx
= nFlyIdx
;
1097 while( nIdx
< nFlyCount
&& ( pFlyC
= pImpl
->
1098 GetFlyCache( nIdx
) )->nPageNum
== nPgNum
)
1100 aFlyCacheSet
.insert( pFlyC
);
1104 // sort objects on this page by ordnum
1105 std::set
< const SdrObject
*, SdrObjectCompare
> aFlySet
;
1106 for ( USHORT i
= 0; i
< rObjs
.Count(); ++i
)
1108 SwAnchoredObject
* pAnchoredObj
= rObjs
[i
];
1109 if ( pAnchoredObj
->ISA(SwFlyFrm
) ) // a text frame?
1111 SwFlyFrm
*pFly
= static_cast<SwFlyFrm
*>(pAnchoredObj
);
1112 if( pFly
->GetAnchorFrm() &&
1113 !pFly
->GetAnchorFrm()->FindFooterOrHeader() )
1115 const SwContact
*pC
= ::GetUserCall( pAnchoredObj
->GetDrawObj() );
1118 aFlySet
.insert( pAnchoredObj
->GetDrawObj() );
1124 if ( aFlyCacheSet
.size() == aFlySet
.size() )
1126 std::set
< const SwFlyCache
*, FlyCacheCompare
>::iterator aFlyCacheSetIt
=
1127 aFlyCacheSet
.begin();
1128 std::set
< const SdrObject
*, SdrObjectCompare
>::iterator aFlySetIt
=
1131 while ( aFlyCacheSetIt
!= aFlyCacheSet
.end() )
1133 const SwFlyCache
* pFlyCache
= *aFlyCacheSetIt
;
1134 SwFlyFrm
* pFly
= ((SwVirtFlyDrawObj
*)*aFlySetIt
)->GetFlyFrm();
1136 if ( pFly
->Frm().Left() == WEIT_WECH
)
1138 // we get the stored information
1139 pFly
->Frm().Pos().X() = pFlyCache
->Left() +
1140 pPage
->Frm().Left();
1141 pFly
->Frm().Pos().Y() = pFlyCache
->Top() +
1143 if ( pImpl
->IsUseFlyCache() )
1145 pFly
->Frm().Width( pFlyCache
->Width() );
1146 pFly
->Frm().Height( pFlyCache
->Height() );
1157 /*-----------------28.6.2001 14:48------------------
1158 * SwLayHelper::CheckPageFlyCache(..)
1159 * looks for the given text frame in the fly cache and sets
1160 * the position and size, if possible.
1161 * The fly cache is sorted by pages and we start searching with the given page.
1162 * If we found the page number in the fly cache, we set
1163 * the rpPage parameter to the right page, if possible.
1164 * --------------------------------------------------*/
1166 BOOL
SwLayHelper::CheckPageFlyCache( SwPageFrm
* &rpPage
, SwFlyFrm
* pFly
)
1168 if( !pFly
->GetAnchorFrm() || !pFly
->GetVirtDrawObj() ||
1169 pFly
->GetAnchorFrm()->FindFooterOrHeader() )
1172 SwDoc
* pDoc
= rpPage
->GetFmt()->GetDoc();
1173 SwLayCacheImpl
*pCache
= pDoc
->GetLayoutCache() ?
1174 pDoc
->GetLayoutCache()->LockImpl() : NULL
;
1177 USHORT nPgNum
= rpPage
->GetPhyPageNum();
1179 USHORT nCnt
= pCache
->GetFlyCount();
1180 ULONG nOrdNum
= pFly
->GetVirtDrawObj()->GetOrdNum();
1181 SwFlyCache
* pFlyC
= 0;
1183 // skip fly frames from pages before the current page
1184 while( nIdx
< nCnt
&&
1185 nPgNum
> (pFlyC
= pCache
->GetFlyCache( nIdx
))->nPageNum
)
1188 while( nIdx
< nCnt
&&
1189 nOrdNum
!= (pFlyC
= pCache
->GetFlyCache( nIdx
))->nOrdNum
)
1193 SwPageFrm
*pPage
= rpPage
;
1194 while( pPage
&& pPage
->GetPhyPageNum() < pFlyC
->nPageNum
)
1195 pPage
= (SwPageFrm
*)pPage
->GetNext();
1196 // --> OD 2005-02-22 #i43266# - if the found page is an empty page,
1197 // take the previous one (take next one, if previous one doesn't exists)
1198 if ( pPage
&& pPage
->IsEmptyPage() )
1200 pPage
= static_cast<SwPageFrm
*>( pPage
->GetPrev()
1202 : pPage
->GetNext() );
1208 pFly
->Frm().Pos().X() = pFlyC
->Left() + pPage
->Frm().Left();
1209 pFly
->Frm().Pos().Y() = pFlyC
->Top() + pPage
->Frm().Top();
1210 if ( pCache
->IsUseFlyCache() )
1212 pFly
->Frm().Width( pFlyC
->Width() );
1213 pFly
->Frm().Height( pFlyC
->Height() );
1218 pDoc
->GetLayoutCache()->UnlockImpl();
1223 // -----------------------------------------------------------------------------
1225 SwLayCacheIoImpl::SwLayCacheIoImpl( SvStream
& rStrm
, BOOL bWrtMd
) :
1227 nMajorVersion(SW_LAYCACHE_IO_VERSION_MAJOR
),
1228 nMinorVersion(SW_LAYCACHE_IO_VERSION_MINOR
),
1229 bWriteMode( bWrtMd
),
1233 *pStream
<< nMajorVersion
1237 *pStream
>> nMajorVersion
1241 BOOL
SwLayCacheIoImpl::OpenRec( BYTE cType
)
1244 UINT16 nLvl
= aRecTypes
.Count();
1245 ASSERT( nLvl
== aRecSizes
.Count(), "OpenRec: Level" );
1246 UINT32 nPos
= pStream
->Tell();
1249 aRecTypes
.Insert( cType
, nLvl
);
1250 aRecSizes
.Insert( nPos
, nLvl
);
1251 *pStream
<< (UINT32
) 0;
1257 BYTE cRecTyp
= (BYTE
)nVal
;
1258 aRecTypes
.Insert( cRecTyp
, nLvl
);
1259 sal_uInt32 nSize
= nVal
>> 8;
1260 aRecSizes
.Insert( nPos
+ nSize
, nLvl
);
1261 if( !nVal
|| cRecTyp
!= cType
||
1262 pStream
->GetErrorCode() != SVSTREAM_OK
|| pStream
->IsEof() )
1264 ASSERT( nVal
, "OpenRec: Record-Header is 0" );
1265 ASSERT( cRecTyp
== cType
,
1266 "OpenRec: Wrong Record Type" );
1267 aRecTypes
[nLvl
] = 0;
1268 aRecSizes
[nLvl
] = pStream
->Tell();
1278 BOOL
SwLayCacheIoImpl::CloseRec( BYTE
)
1281 UINT16 nLvl
= aRecTypes
.Count();
1282 ASSERT( nLvl
== aRecSizes
.Count(), "CloseRec: wrong Level" );
1283 ASSERT( nLvl
, "CloseRec: no levels" );
1287 UINT32 nPos
= pStream
->Tell();
1290 UINT32 nBgn
= aRecSizes
[nLvl
];
1291 pStream
->Seek( nBgn
);
1292 UINT32 nSize
= nPos
- nBgn
;
1293 UINT32 nVal
= ( nSize
<< 8 ) | aRecTypes
[nLvl
];
1295 pStream
->Seek( nPos
);
1296 if( pStream
->GetError() != SVSTREAM_OK
)
1301 UINT32 n
= aRecSizes
[nLvl
];
1302 ASSERT( n
>= nPos
, "CloseRec: to much data read" );
1309 if( pStream
->GetErrorCode() != SVSTREAM_OK
)
1313 aRecTypes
.Remove( nLvl
, 1 );
1314 aRecSizes
.Remove( nLvl
, 1 );
1323 UINT32
SwLayCacheIoImpl::BytesLeft()
1325 UINT16 nLvl
= aRecSizes
.Count();
1327 if( !bError
&& nLvl
)
1329 UINT32 nEndPos
= aRecSizes
[ nLvl
-1 ];
1330 UINT32 nPos
= pStream
->Tell();
1331 if( nEndPos
> nPos
)
1338 BYTE
SwLayCacheIoImpl::Peek()
1343 UINT32 nPos
= pStream
->Tell();
1345 pStream
->Seek( nPos
);
1346 if( pStream
->GetErrorCode() != SVSTREAM_OK
)
1355 void SwLayCacheIoImpl::SkipRec()
1359 pStream
->Seek( aRecSizes
[aRecSizes
.Count()-1] );
1363 BYTE
SwLayCacheIoImpl::OpenFlagRec()
1365 ASSERT( !bWriteMode
, "OpenFlagRec illegal in write mode" );
1368 nFlagRecEnd
= pStream
->Tell() + ( cFlags
& 0x0F );
1369 return (cFlags
>> 4);
1372 void SwLayCacheIoImpl::OpenFlagRec( BYTE nFlags
, BYTE nLen
)
1374 ASSERT( bWriteMode
, "OpenFlagRec illegal in read mode" );
1375 ASSERT( (nFlags
& 0xF0) == 0, "illegal flags set" );
1376 ASSERT( nLen
< 16, "wrong flag record length" );
1377 BYTE cFlags
= (nFlags
<< 4) + nLen
;
1379 nFlagRecEnd
= pStream
->Tell() + nLen
;
1382 void SwLayCacheIoImpl::CloseFlagRec()
1386 ASSERT( pStream
->Tell() == nFlagRecEnd
, "Wrong amount of data written" );
1390 ASSERT( pStream
->Tell() <= nFlagRecEnd
, "To many data read" );
1391 if( pStream
->Tell() != nFlagRecEnd
)
1392 pStream
->Seek( nFlagRecEnd
);