/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
/* * Reading and writing of the layout cache. * The layout cache is not necessary, but it improves * the performance and reduces the text flow during * the formatting. * The layout cache contains the index of the paragraphs/tables * at the top of every page, so it's possible to create * the right count of pages and to distribute the document content * to this pages before the formatting starts.
*/
// Due to an evil bug in the layout cache (#102759#), we cannot trust the // sizes of fly frames which have been written using the "old" layout cache. // This flag should indicate that we do not want to trust the width and // height of fly frames
m_bUseFlyCache = aIo.GetMinorVersion() >= 1;
/** writes the index (more precise: the difference between * the index and the first index of the document content) * of the first paragraph/table at the top of every page. * If at the top of a page is the rest of a paragraph/table * from the bottom of the previous page, the character/row * number is stored, too. * The position, size and page number of the text frames * are stored, too
*/ void SwLayoutCache::Write( SvStream &rStream, const SwDoc& rDoc )
{ if( !rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ) // the layout itself .. return;
SwLayCacheIoImpl aIo( rStream, true ); // We want to save the relative index, so we need the index // of the first content
SwNodeOffset nStartOfContent = rDoc.GetNodes().GetEndOfContent().
StartOfSectionNode()->GetIndex(); // The first page...
SwPageFrame* pPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->Lower()));
aIo.OpenRec( SW_LAYCACHE_IO_REC_PAGES );
aIo.OpenFlagRec( 0, 0 );
aIo.CloseFlagRec(); while( pPage )
{ if( pPage->GetPrev() )
{
SwLayoutFrame* pLay = pPage->FindBodyCont();
SwFrame* pTmp = pLay ? pLay->ContainsAny() : nullptr; // We are only interested in paragraph or table frames, // a section frames contains paragraphs/tables. if( pTmp && pTmp->IsSctFrame() )
pTmp = static_cast<SwSectionFrame*>(pTmp)->ContainsAny();
if( pTmp ) // any content
{ if( pTmp->IsTextFrame() )
{
SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTmp));
assert(!pFrame->GetMergedPara());
SwNodeOffset nNdIdx = pFrame->GetTextNodeFirst()->GetIndex(); if( nNdIdx > nStartOfContent )
{ /* Open Paragraph Record */
aIo.OpenRec( SW_LAYCACHE_IO_REC_PARA ); bool bFollow = static_cast<SwTextFrame*>(pTmp)->IsFollow();
aIo.OpenFlagRec( bFollow ? 0x01 : 0x00,
bFollow ? 8 : 4 );
nNdIdx -= nStartOfContent;
aIo.GetStream().WriteUInt32( sal_Int32(nNdIdx) ); if( bFollow )
aIo.GetStream().WriteUInt32( sal_Int32(static_cast<SwTextFrame*>(pTmp)->GetOffset()) );
aIo.CloseFlagRec(); /* Close Paragraph Record */
aIo.CloseRec();
}
} elseif( pTmp->IsTabFrame() )
{
SwTabFrame* pTab = static_cast<SwTabFrame*>(pTmp);
assert(pTab);
sal_uLong nOfst = COMPLETE_STRING; if( pTab->IsFollow() )
{ // If the table is a follow, we have to look for the // master and to count all rows to get the row number
nOfst = 0; if( pTab->IsFollow() )
pTab = pTab->FindMaster( true ); while( pTab != pTmp )
{
SwFrame* pSub = pTab->Lower(); while( pSub )
{
++nOfst;
pSub = pSub->GetNext();
}
pTab = pTab->GetFollow();
assert(pTab && "Table follow without master");
}
} while (true)
{
SwNodeOffset nNdIdx =
pTab->GetTable()->GetTableNode()->GetIndex(); if( nNdIdx > nStartOfContent )
{ /* Open Table Record */
aIo.OpenRec( SW_LAYCACHE_IO_REC_TABLE );
aIo.OpenFlagRec( 0, 8 );
nNdIdx -= nStartOfContent;
aIo.GetStream().WriteUInt32( sal_Int32(nNdIdx) )
.WriteUInt32( nOfst );
aIo.CloseFlagRec(); /* Close Table Record */
aIo.CloseRec();
} // If the table has a follow on the next page, // we know already the row number and store this // immediately. if( pTab->GetFollow() )
{ if( nOfst == sal_uLong(COMPLETE_STRING) )
nOfst = 0; do
{
SwFrame* pSub = pTab->Lower(); while( pSub )
{
++nOfst;
pSub = pSub->GetNext();
}
pTab = pTab->GetFollow();
SwPageFrame *pTabPage = pTab->FindPageFrame(); if( pTabPage != pPage )
{
OSL_ENSURE( pPage->GetPhyPageNum() <
pTabPage->GetPhyPageNum(), "Looping Tableframes" );
pPage = pTabPage; break;
}
} while ( pTab->GetFollow() );
} else break;
}
}
}
} if( pPage->GetSortedObjs() )
{
SwSortedObjs &rObjs = *pPage->GetSortedObjs(); for (SwAnchoredObject* pAnchoredObj : rObjs)
{ if (SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame())
{ if( pFly->getFrameArea().Left() != FAR_AWAY &&
!pFly->GetAnchorFrame()->FindFooterOrHeader() )
{ const SwContact *pC =
::GetUserCall(pAnchoredObj->GetDrawObj()); if( pC )
{
sal_uInt32 nOrdNum = pAnchoredObj->GetDrawObj()->GetOrdNum();
sal_uInt16 nPageNum = pPage->GetPhyPageNum(); /* Open Fly Record */
aIo.OpenRec( SW_LAYCACHE_IO_REC_FLY );
aIo.OpenFlagRec( 0, 0 );
aIo.CloseFlagRec(); const SwRect& rRct = pFly->getFrameArea();
sal_Int32 nX = rRct.Left() - pPage->getFrameArea().Left();
sal_Int32 nY = rRct.Top() - pPage->getFrameArea().Top();
aIo.GetStream().WriteUInt16( nPageNum ).WriteUInt32( nOrdNum )
.WriteInt32( nX ).WriteInt32( nY )
.WriteInt32( rRct.Width() )
.WriteInt32( rRct.Height() ); /* Close Fly Record */
aIo.CloseRec();
}
}
}
}
}
pPage = static_cast<SwPageFrame*>(pPage->GetNext());
}
aIo.CloseRec();
}
/** Does NOT really calculate the page count, * it returns the page count value from the layout cache, if available, * otherwise it estimates the page count.
*/
sal_uLong SwLayHelper::CalcPageCount()
{
sal_uLong nPgCount;
SwLayCacheImpl *pCache = mrDoc.GetLayoutCache() ?
mrDoc.GetLayoutCache()->LockImpl() : nullptr; if( pCache )
{
nPgCount = pCache->size() + 1;
mrDoc.GetLayoutCache()->UnlockImpl();
} else
{
nPgCount = mrDoc.getIDocumentStatistics().GetDocStat().nPage; if ( nPgCount <= 10 ) // no page insertion for less than 10 pages
nPgCount = 0;
sal_Int32 nNdCount = mrDoc.getIDocumentStatistics().GetDocStat().nPara; if ( nNdCount <= 1 )
{ //Estimates the number of paragraphs.
SwNodeOffset nTmp = mrDoc.GetNodes().GetEndOfContent().GetIndex() -
mrDoc.GetNodes().GetEndOfExtras().GetIndex(); //Tables have a little overhead...
nTmp -= SwNodeOffset(mrDoc.GetTableFrameFormats()->size() * 25); //Fly frames, too ..
nTmp -= (mrDoc.GetNodes().GetEndOfAutotext().GetIndex() -
mrDoc.GetNodes().GetEndOfInserts().GetIndex()) / SwNodeOffset(3 * 5); if ( nTmp > SwNodeOffset(0) )
nNdCount = sal_Int32(nTmp);
} if ( nNdCount < 1000 )
nPgCount = 0;// no progress bar for small documents
} return nPgCount;
}
/** * inserts a page and return true, if * - the break after flag is set * - the actual content wants a break before * - the maximum count of paragraph/rows is reached * * The break after flag is set, if the actual content * wants a break after.
*/ bool SwLayHelper::CheckInsertPage(
SwPageFrame *& rpPage,
SwLayoutFrame *& rpLay,
SwFrame *& rpFrame, bool & rIsBreakAfter, boolconst isForceBreak)
{ bool bEnd = nullptr == rpPage->GetNext(); const SvxFormatBreakItem& rBrk = rpFrame->GetBreakItem(); const SwFormatPageDesc& rDesc = rpFrame->GetPageDescItem(); // #118195# Do not evaluate page description if frame // is a follow frame! const SwPageDesc* pDesc = rpFrame->IsFlowFrame()
&& SwFlowFrame::CastFlowFrame(rpFrame)->IsFollow()
? nullptr
: rDesc.GetPageDesc();
if ( mrpActualSection )
{ //Did the SectionFrame even have a content? If not, we can //directly put it somewhere else
SwSectionFrame *pSct; bool bInit = false; if ( !mrpActualSection->GetSectionFrame()->ContainsContent())
{
pSct = mrpActualSection->GetSectionFrame();
pSct->RemoveFromLayout();
} else
{
pSct = new SwSectionFrame(
*mrpActualSection->GetSectionFrame(), false );
mrpActualSection->GetSectionFrame()->SimpleFormat();
bInit = true;
}
mrpActualSection->SetSectionFrame( pSct );
pSct->InsertBehind( mrpLay, nullptr );
if( bInit )
{
pSct->Init();
}
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pSct);
aFrm.Pos() = mrpLay->getFrameArea().Pos();
aFrm.Pos().AdjustY(1 ); //because of the notifications
}
/** * If a new page is inserted, the last page is analysed. * If there are text frames with default position, the fly cache * is checked, if these frames are stored in the cache.
*/ void SwLayHelper::CheckFlyCache_( SwPageFrame* pPage )
{ if( !mpImpl || !pPage ) return; const size_t nFlyCount = mpImpl->GetFlyCount(); // Any text frames at the page, fly cache available? if( !(pPage->GetSortedObjs() && mnFlyIdx < nFlyCount) ) return;
if ( pFly->getFrameArea().Left() == FAR_AWAY )
{ // we get the stored information
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFly);
aFrm.Pos().setX( pFlyCache->Left() + pPage->getFrameArea().Left() );
aFrm.Pos().setY( pFlyCache->Top() + pPage->getFrameArea().Top() );
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.