1208 lines
44 KiB
C++
1208 lines
44 KiB
C++
/* -*- 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 .
|
|
*/
|
|
|
|
#include <editeng/formatbreakitem.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <osl/diagnose.h>
|
|
#include <tools/stream.hxx>
|
|
#include <doc.hxx>
|
|
#include <IDocumentStatistics.hxx>
|
|
#include <IDocumentLayoutAccess.hxx>
|
|
#include <docstat.hxx>
|
|
#include <fmtpdsc.hxx>
|
|
#include <laycache.hxx>
|
|
#include "layhelp.hxx"
|
|
#include <pagefrm.hxx>
|
|
#include <rootfrm.hxx>
|
|
#include <txtfrm.hxx>
|
|
#include <swtable.hxx>
|
|
#include <tabfrm.hxx>
|
|
#include <rowfrm.hxx>
|
|
#include <sectfrm.hxx>
|
|
#include <fmtcntnt.hxx>
|
|
#include <pagedesc.hxx>
|
|
#include <frmtool.hxx>
|
|
#include <dflyobj.hxx>
|
|
#include <dcontact.hxx>
|
|
#include <viewopt.hxx>
|
|
#include <flyfrm.hxx>
|
|
#include <sortedobjs.hxx>
|
|
#include <ndindex.hxx>
|
|
#include <node.hxx>
|
|
#include <ndtxt.hxx>
|
|
#include <frameformats.hxx>
|
|
|
|
#include <limits>
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
SwLayoutCache::SwLayoutCache() : m_nLockCount( 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.
|
|
*/
|
|
|
|
void SwLayoutCache::Read( SvStream &rStream )
|
|
{
|
|
if( !m_pImpl )
|
|
{
|
|
m_pImpl.reset( new SwLayCacheImpl );
|
|
if( !m_pImpl->Read( rStream ) )
|
|
{
|
|
m_pImpl.reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
void SwLayCacheImpl::Insert( sal_uInt16 nType, SwNodeOffset nIndex, sal_Int32 nOffset )
|
|
{
|
|
m_aType.push_back( nType );
|
|
mIndices.push_back( nIndex );
|
|
m_aOffset.push_back( nOffset );
|
|
}
|
|
|
|
bool SwLayCacheImpl::Read( SvStream& rStream )
|
|
{
|
|
SwLayCacheIoImpl aIo( rStream, false );
|
|
if( aIo.GetMajorVersion() > SW_LAYCACHE_IO_VERSION_MAJOR )
|
|
return false;
|
|
|
|
// 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;
|
|
|
|
aIo.OpenRec( SW_LAYCACHE_IO_REC_PAGES );
|
|
aIo.OpenFlagRec();
|
|
aIo.CloseFlagRec();
|
|
while( aIo.BytesLeft() && !aIo.HasError() )
|
|
{
|
|
sal_uInt32 nIndex(0), nOffset(0);
|
|
|
|
switch( aIo.Peek() )
|
|
{
|
|
case SW_LAYCACHE_IO_REC_PARA:
|
|
{
|
|
aIo.OpenRec( SW_LAYCACHE_IO_REC_PARA );
|
|
sal_uInt8 cFlags = aIo.OpenFlagRec();
|
|
aIo.GetStream().ReadUInt32( nIndex );
|
|
if( (cFlags & 0x01) != 0 )
|
|
aIo.GetStream().ReadUInt32( nOffset );
|
|
else
|
|
nOffset = COMPLETE_STRING;
|
|
aIo.CloseFlagRec();
|
|
Insert( SW_LAYCACHE_IO_REC_PARA, SwNodeOffset(nIndex), static_cast<sal_Int32>(nOffset) );
|
|
aIo.CloseRec();
|
|
break;
|
|
}
|
|
case SW_LAYCACHE_IO_REC_TABLE:
|
|
aIo.OpenRec( SW_LAYCACHE_IO_REC_TABLE );
|
|
aIo.OpenFlagRec();
|
|
aIo.GetStream().ReadUInt32( nIndex )
|
|
.ReadUInt32( nOffset );
|
|
Insert( SW_LAYCACHE_IO_REC_TABLE, SwNodeOffset(nIndex), static_cast<sal_Int32>(nOffset) );
|
|
aIo.CloseFlagRec();
|
|
aIo.CloseRec();
|
|
break;
|
|
case SW_LAYCACHE_IO_REC_FLY:
|
|
{
|
|
aIo.OpenRec( SW_LAYCACHE_IO_REC_FLY );
|
|
aIo.OpenFlagRec();
|
|
aIo.CloseFlagRec();
|
|
sal_Int32 nX(0), nY(0), nW(0), nH(0);
|
|
sal_uInt16 nPgNum(0);
|
|
aIo.GetStream().ReadUInt16( nPgNum ).ReadUInt32( nIndex )
|
|
.ReadInt32( nX ).ReadInt32( nY ).ReadInt32( nW ).ReadInt32( nH );
|
|
m_FlyCache.emplace_back( nPgNum, nIndex, nX, nY, nW, nH );
|
|
aIo.CloseRec();
|
|
break;
|
|
}
|
|
default:
|
|
aIo.SkipRec();
|
|
break;
|
|
}
|
|
}
|
|
aIo.CloseRec();
|
|
|
|
return !aIo.HasError();
|
|
}
|
|
|
|
/** 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();
|
|
}
|
|
}
|
|
else if( 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();
|
|
}
|
|
|
|
#ifdef DBG_UTIL
|
|
bool SwLayoutCache::CompareLayout( const SwDoc& rDoc ) const
|
|
{
|
|
if( !m_pImpl )
|
|
return true;
|
|
const SwRootFrame *pRootFrame = rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
|
|
if( pRootFrame )
|
|
{
|
|
size_t nIndex = 0;
|
|
SwNodeOffset nStartOfContent = rDoc.GetNodes().GetEndOfContent().
|
|
StartOfSectionNode()->GetIndex();
|
|
const SwPageFrame* pPage = static_cast<const SwPageFrame*>(pRootFrame->Lower());
|
|
if( pPage )
|
|
pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
|
|
while( pPage )
|
|
{
|
|
if( nIndex >= m_pImpl->size() )
|
|
return false;
|
|
|
|
const SwLayoutFrame* pLay = pPage->FindBodyCont();
|
|
const SwFrame* pTmp = pLay ? pLay->ContainsAny() : nullptr;
|
|
if( pTmp && pTmp->IsSctFrame() )
|
|
pTmp = static_cast<const SwSectionFrame*>(pTmp)->ContainsAny();
|
|
if( pTmp )
|
|
{
|
|
if( pTmp->IsTextFrame() )
|
|
{
|
|
|
|
SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTmp));
|
|
assert(!pFrame->GetMergedPara());
|
|
SwNodeOffset nNdIdx = pFrame->GetTextNodeFirst()->GetIndex();
|
|
if( nNdIdx > nStartOfContent )
|
|
{
|
|
bool bFollow = static_cast<const SwTextFrame*>(pTmp)->IsFollow();
|
|
nNdIdx -= nStartOfContent;
|
|
if( m_pImpl->GetBreakIndex( nIndex ) != nNdIdx ||
|
|
SW_LAYCACHE_IO_REC_PARA !=
|
|
m_pImpl->GetBreakType( nIndex ) ||
|
|
(bFollow
|
|
? sal_Int32(static_cast<const SwTextFrame*>(pTmp)->GetOffset())
|
|
: COMPLETE_STRING) != m_pImpl->GetBreakOfst(nIndex))
|
|
{
|
|
return false;
|
|
}
|
|
++nIndex;
|
|
}
|
|
}
|
|
else if( pTmp->IsTabFrame() )
|
|
{
|
|
const SwTabFrame* pTab = static_cast<const SwTabFrame*>(pTmp);
|
|
sal_Int32 nOfst = COMPLETE_STRING;
|
|
if( pTab->IsFollow() )
|
|
{
|
|
nOfst = 0;
|
|
if( pTab->IsFollow() )
|
|
pTab = pTab->FindMaster( true );
|
|
while( pTab != pTmp )
|
|
{
|
|
const SwFrame* pSub = pTab->Lower();
|
|
while( pSub )
|
|
{
|
|
++nOfst;
|
|
pSub = pSub->GetNext();
|
|
}
|
|
pTab = pTab->GetFollow();
|
|
}
|
|
}
|
|
do
|
|
{
|
|
SwNodeOffset nNdIdx =
|
|
pTab->GetTable()->GetTableNode()->GetIndex();
|
|
if( nNdIdx > nStartOfContent )
|
|
{
|
|
nNdIdx -= nStartOfContent;
|
|
if( m_pImpl->GetBreakIndex( nIndex ) != nNdIdx ||
|
|
SW_LAYCACHE_IO_REC_TABLE !=
|
|
m_pImpl->GetBreakType( nIndex ) ||
|
|
nOfst != m_pImpl->GetBreakOfst( nIndex ) )
|
|
{
|
|
return false;
|
|
}
|
|
++nIndex;
|
|
}
|
|
if( pTab->GetFollow() )
|
|
{
|
|
if( nOfst == COMPLETE_STRING )
|
|
nOfst = 0;
|
|
do
|
|
{
|
|
const SwFrame* pSub = pTab->Lower();
|
|
while( pSub )
|
|
{
|
|
++nOfst;
|
|
pSub = pSub->GetNext();
|
|
}
|
|
pTab = pTab->GetFollow();
|
|
assert(pTab && "Table follow without master");
|
|
const SwPageFrame *pTabPage = pTab->FindPageFrame();
|
|
if( pTabPage != pPage )
|
|
{
|
|
pPage = pTabPage;
|
|
break;
|
|
}
|
|
} while ( pTab->GetFollow() );
|
|
}
|
|
else
|
|
break;
|
|
} while( pTab );
|
|
}
|
|
}
|
|
pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
void SwLayoutCache::ClearImpl()
|
|
{
|
|
if( !IsLocked() )
|
|
{
|
|
m_pImpl.reset();
|
|
}
|
|
}
|
|
|
|
SwLayoutCache::~SwLayoutCache()
|
|
{
|
|
OSL_ENSURE( !m_nLockCount, "Deleting a locked SwLayoutCache!?" );
|
|
}
|
|
|
|
/// helper class to create not nested section frames for nested sections.
|
|
SwActualSection::SwActualSection( SwActualSection *pUp,
|
|
SwSectionFrame *pSect,
|
|
SwSectionNode *pNd ) :
|
|
m_pUpper( pUp ),
|
|
m_pSectFrame( pSect ),
|
|
m_pSectNode( pNd )
|
|
{
|
|
if ( !m_pSectNode )
|
|
{
|
|
const SwNodeIndex *pIndex = pSect->GetFormat()->GetContent().GetContentIdx();
|
|
m_pSectNode = pIndex->GetNode().FindSectionNode();
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
bool sanityCheckLayoutCache(SwLayCacheImpl const& rCache,
|
|
SwNodes const& rNodes, SwNodeOffset nNodeIndex)
|
|
{
|
|
auto const nStartOfContent(rNodes.GetEndOfContent().StartOfSectionNode()->GetIndex());
|
|
nNodeIndex -= nStartOfContent;
|
|
auto const nMaxIndex(rNodes.GetEndOfContent().GetIndex() - nStartOfContent);
|
|
for (size_t nIndex = 0; nIndex < rCache.size(); ++nIndex)
|
|
{
|
|
auto const nBreakIndex(rCache.GetBreakIndex(nIndex));
|
|
if (nBreakIndex < nNodeIndex || nMaxIndex <= nBreakIndex)
|
|
{
|
|
SAL_WARN("sw.layout",
|
|
"invalid node index in layout-cache: " << nBreakIndex);
|
|
return false;
|
|
}
|
|
auto const nBreakType(rCache.GetBreakType(nIndex));
|
|
switch (nBreakType)
|
|
{
|
|
case SW_LAYCACHE_IO_REC_PARA:
|
|
if (!rNodes[nBreakIndex + nStartOfContent]->IsTextNode())
|
|
{
|
|
SAL_WARN("sw.layout",
|
|
"invalid node of type 'P' in layout-cache");
|
|
return false;
|
|
}
|
|
break;
|
|
case SW_LAYCACHE_IO_REC_TABLE:
|
|
if (!rNodes[nBreakIndex + nStartOfContent]->IsTableNode())
|
|
{
|
|
SAL_WARN("sw.layout",
|
|
"invalid node of type 'T' in layout-cache");
|
|
return false;
|
|
}
|
|
break;
|
|
default:
|
|
assert(false); // Read shouldn't have inserted that
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
/** helper class, which utilizes the layout cache information
|
|
* to distribute the document content to the right pages.
|
|
* It's used by the InsertCnt_(..)-function.
|
|
* If there's no layout cache, the distribution to the pages is more
|
|
* a guess, but a guess with statistical background.
|
|
*/
|
|
SwLayHelper::SwLayHelper( SwDoc *pD, SwFrame* &rpF, SwFrame* &rpP, SwPageFrame* &rpPg,
|
|
SwLayoutFrame* &rpL, std::unique_ptr<SwActualSection> &rpA,
|
|
SwNodeOffset nNodeIndex, bool bCache )
|
|
: mrpFrame( rpF )
|
|
, mrpPrv( rpP )
|
|
, mrpPage( rpPg )
|
|
, mrpLay( rpL )
|
|
, mrpActualSection( rpA )
|
|
, mbBreakAfter(false)
|
|
, mpDoc(pD)
|
|
, mnMaxParaPerPage( 25 )
|
|
, mnParagraphCnt( bCache ? 0 : USHRT_MAX )
|
|
, mnFlyIdx( 0 )
|
|
, mbFirst( bCache )
|
|
{
|
|
mpImpl = mpDoc->GetLayoutCache() ? mpDoc->GetLayoutCache()->LockImpl() : nullptr;
|
|
if( mpImpl )
|
|
{
|
|
SwNodes const& rNodes(mpDoc->GetNodes());
|
|
if (sanityCheckLayoutCache(*mpImpl, rNodes, nNodeIndex))
|
|
{
|
|
mnIndex = 0;
|
|
mnStartOfContent = rNodes.GetEndOfContent().StartOfSectionNode()->GetIndex();
|
|
mnMaxParaPerPage = 1000;
|
|
}
|
|
else
|
|
{
|
|
mpDoc->GetLayoutCache()->UnlockImpl();
|
|
mpImpl = nullptr;
|
|
mnIndex = std::numeric_limits<size_t>::max();
|
|
mnStartOfContent = SwNodeOffset(USHRT_MAX);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mnIndex = std::numeric_limits<size_t>::max();
|
|
mnStartOfContent = NODE_OFFSET_MAX;
|
|
}
|
|
}
|
|
|
|
SwLayHelper::~SwLayHelper()
|
|
{
|
|
if( mpImpl )
|
|
{
|
|
assert(mpDoc && mpDoc->GetLayoutCache() && "Missing layoutcache");
|
|
mpDoc->GetLayoutCache()->UnlockImpl();
|
|
}
|
|
}
|
|
|
|
/** 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 = mpDoc->GetLayoutCache() ?
|
|
mpDoc->GetLayoutCache()->LockImpl() : nullptr;
|
|
if( pCache )
|
|
{
|
|
nPgCount = pCache->size() + 1;
|
|
mpDoc->GetLayoutCache()->UnlockImpl();
|
|
}
|
|
else
|
|
{
|
|
nPgCount = mpDoc->getIDocumentStatistics().GetDocStat().nPage;
|
|
if ( nPgCount <= 10 ) // no page insertion for less than 10 pages
|
|
nPgCount = 0;
|
|
sal_Int32 nNdCount = mpDoc->getIDocumentStatistics().GetDocStat().nPara;
|
|
if ( nNdCount <= 1 )
|
|
{
|
|
//Estimates the number of paragraphs.
|
|
SwNodeOffset nTmp = mpDoc->GetNodes().GetEndOfContent().GetIndex() -
|
|
mpDoc->GetNodes().GetEndOfExtras().GetIndex();
|
|
//Tables have a little overhead...
|
|
nTmp -= SwNodeOffset(mpDoc->GetTableFrameFormats()->size() * 25);
|
|
//Fly frames, too ..
|
|
nTmp -= (mpDoc->GetNodes().GetEndOfAutotext().GetIndex() -
|
|
mpDoc->GetNodes().GetEndOfInserts().GetIndex()) / SwNodeOffset(3 * 5);
|
|
if ( nTmp > SwNodeOffset(0) )
|
|
nNdCount = sal_Int32(nTmp);
|
|
}
|
|
if ( nNdCount > 100 ) // no estimation below this value
|
|
{
|
|
if ( nPgCount > 0 )
|
|
{ // tdf#129529 avoid 0...
|
|
mnMaxParaPerPage = std::max<sal_uLong>(3, nNdCount / nPgCount);
|
|
}
|
|
else
|
|
{
|
|
mnMaxParaPerPage = std::max( sal_uLong(20),
|
|
sal_uLong(20 + nNdCount / 1000 * 3) );
|
|
const sal_uLong nMax = 53;
|
|
mnMaxParaPerPage = std::min( mnMaxParaPerPage, nMax );
|
|
nPgCount = nNdCount / mnMaxParaPerPage;
|
|
}
|
|
if ( nNdCount < 1000 )
|
|
nPgCount = 0;// no progress bar for small documents
|
|
SwViewShell *pSh = nullptr;
|
|
if( mrpLay && mrpLay->getRootFrame() )
|
|
pSh = mrpLay->getRootFrame()->GetCurrShell();
|
|
if( pSh && pSh->GetViewOptions()->getBrowseMode() )
|
|
mnMaxParaPerPage *= 6;
|
|
}
|
|
}
|
|
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,
|
|
bool const 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();
|
|
|
|
bool bBrk = isForceBreak || rIsBreakAfter;
|
|
rIsBreakAfter = rBrk.GetBreak() == SvxBreak::PageAfter ||
|
|
rBrk.GetBreak() == SvxBreak::PageBoth;
|
|
if ( !bBrk )
|
|
bBrk = rBrk.GetBreak() == SvxBreak::PageBefore ||
|
|
rBrk.GetBreak() == SvxBreak::PageBoth;
|
|
|
|
if ( bBrk || pDesc )
|
|
{
|
|
::std::optional<sal_uInt16> oPgNum;
|
|
if ( !pDesc )
|
|
{
|
|
pDesc = rpPage->GetPageDesc()->GetFollow();
|
|
}
|
|
else
|
|
{
|
|
oPgNum = rDesc.GetNumOffset();
|
|
if ( oPgNum )
|
|
static_cast<SwRootFrame*>(rpPage->GetUpper())->SetVirtPageNum(true);
|
|
}
|
|
bool bNextPageRight = !rpPage->OnRightPage();
|
|
bool bInsertEmpty = false;
|
|
assert(rpPage->GetUpper()->GetLower());
|
|
if (oPgNum && bNextPageRight != IsRightPageByNumber(
|
|
*static_cast<SwRootFrame*>(rpPage->GetUpper()), *oPgNum))
|
|
{
|
|
bNextPageRight = !bNextPageRight;
|
|
bInsertEmpty = true;
|
|
}
|
|
// If the page style is changing, we'll have a first page.
|
|
bool bNextPageFirst = pDesc != rpPage->GetPageDesc();
|
|
::InsertNewPage( const_cast<SwPageDesc&>(*pDesc), rpPage->GetUpper(),
|
|
bNextPageRight, bNextPageFirst, bInsertEmpty, false, rpPage->GetNext());
|
|
if ( bEnd )
|
|
{
|
|
OSL_ENSURE( rpPage->GetNext(), "No new page?" );
|
|
do
|
|
{
|
|
rpPage = static_cast<SwPageFrame*>(rpPage->GetNext());
|
|
} while (rpPage->GetNext());
|
|
}
|
|
else
|
|
{
|
|
OSL_ENSURE( rpPage->GetNext(), "No new page?" );
|
|
rpPage = static_cast<SwPageFrame*>(rpPage->GetNext());
|
|
if (rpPage->IsEmptyPage())
|
|
{
|
|
OSL_ENSURE( rpPage->GetNext(), "No new page?" );
|
|
rpPage = static_cast<SwPageFrame*>(rpPage->GetNext());
|
|
}
|
|
}
|
|
rpLay = rpPage->FindBodyCont();
|
|
while (rpLay->Lower())
|
|
rpLay = static_cast<SwLayoutFrame*>(rpLay->Lower());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** entry point for the InsertCnt_-function.
|
|
* The document content index is checked either it is
|
|
* in the layout cache either it's time to insert a page
|
|
* cause the maximal estimation of content per page is reached.
|
|
* A really big table or long paragraph may contains more than
|
|
* one page, in this case the needed count of pages will inserted.
|
|
*/
|
|
bool SwLayHelper::CheckInsert( SwNodeOffset nNodeIndex )
|
|
{
|
|
bool bRet = false;
|
|
bool bLongTab = false;
|
|
sal_uLong nMaxRowPerPage( 0 );
|
|
nNodeIndex -= mnStartOfContent;
|
|
sal_uInt16 nRows( 0 );
|
|
if( mrpFrame->IsTabFrame() )
|
|
{
|
|
//Inside a table counts every row as a paragraph
|
|
SwFrame *pLow = static_cast<SwTabFrame*>(mrpFrame)->Lower();
|
|
nRows = 0;
|
|
do
|
|
{
|
|
++nRows;
|
|
pLow = pLow->GetNext();
|
|
} while ( pLow );
|
|
mnParagraphCnt += nRows;
|
|
if( !mpImpl && mnParagraphCnt > mnMaxParaPerPage + 10 )
|
|
{
|
|
// OD 09.04.2003 #108698# - improve heuristics:
|
|
// Assume that a table, which has more than three times the quantity
|
|
// of maximal paragraphs per page rows, consists of rows, which have
|
|
// the height of a normal paragraph. Thus, allow as much rows per page
|
|
// as much paragraphs are allowed.
|
|
if ( nRows > ( 3*mnMaxParaPerPage ) )
|
|
{
|
|
nMaxRowPerPage = mnMaxParaPerPage;
|
|
}
|
|
else
|
|
{
|
|
SwFrame *pTmp = static_cast<SwTabFrame*>(mrpFrame)->Lower();
|
|
if( pTmp->GetNext() )
|
|
pTmp = pTmp->GetNext();
|
|
pTmp = static_cast<SwRowFrame*>(pTmp)->Lower();
|
|
sal_uInt16 nCnt = 0;
|
|
do
|
|
{
|
|
++nCnt;
|
|
pTmp = pTmp->GetNext();
|
|
} while( pTmp );
|
|
nMaxRowPerPage = std::max( sal_uLong(2), mnMaxParaPerPage / nCnt );
|
|
}
|
|
bLongTab = true;
|
|
}
|
|
}
|
|
else
|
|
++mnParagraphCnt;
|
|
if( mbFirst && mpImpl && mnIndex < mpImpl->size() &&
|
|
mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex &&
|
|
( mpImpl->GetBreakOfst( mnIndex ) < COMPLETE_STRING ||
|
|
( ++mnIndex < mpImpl->size() &&
|
|
mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex ) ) )
|
|
mbFirst = false;
|
|
// OD 09.04.2003 #108698# - always split a big tables.
|
|
if ( !mbFirst ||
|
|
( mrpFrame->IsTabFrame() && bLongTab )
|
|
)
|
|
{
|
|
sal_Int32 nRowCount = 0;
|
|
do
|
|
{
|
|
if( mpImpl || bLongTab )
|
|
{
|
|
sal_Int32 nOfst = COMPLETE_STRING;
|
|
sal_uInt16 nType = SW_LAYCACHE_IO_REC_PAGES;
|
|
if( bLongTab )
|
|
{
|
|
mbBreakAfter = true;
|
|
nOfst = static_cast<sal_Int32>(nRowCount + nMaxRowPerPage);
|
|
}
|
|
else
|
|
{
|
|
while( mnIndex < mpImpl->size() &&
|
|
mpImpl->GetBreakIndex(mnIndex) < nNodeIndex)
|
|
++mnIndex;
|
|
if( mnIndex < mpImpl->size() &&
|
|
mpImpl->GetBreakIndex(mnIndex) == nNodeIndex )
|
|
{
|
|
nType = mpImpl->GetBreakType( mnIndex );
|
|
nOfst = mpImpl->GetBreakOfst( mnIndex++ );
|
|
mbBreakAfter = true;
|
|
}
|
|
}
|
|
|
|
if( nOfst < COMPLETE_STRING )
|
|
{
|
|
bool bSplit = false;
|
|
sal_uInt16 nRepeat( 0 );
|
|
if( !bLongTab && mrpFrame->IsTextFrame() &&
|
|
SW_LAYCACHE_IO_REC_PARA == nType &&
|
|
nOfst < static_cast<SwTextFrame*>(mrpFrame)->GetText().getLength())
|
|
bSplit = true;
|
|
else if( mrpFrame->IsTabFrame() && nRowCount < nOfst &&
|
|
( bLongTab || SW_LAYCACHE_IO_REC_TABLE == nType ) )
|
|
{
|
|
nRepeat = static_cast<SwTabFrame*>(mrpFrame)->
|
|
GetTable()->GetRowsToRepeat();
|
|
bSplit = nOfst < nRows && nRowCount + nRepeat < nOfst;
|
|
bLongTab = bLongTab && bSplit;
|
|
}
|
|
if( bSplit )
|
|
{
|
|
mrpFrame->InsertBehind( mrpLay, mrpPrv );
|
|
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mrpFrame);
|
|
aFrm.Pos() = mrpLay->getFrameArea().Pos();
|
|
aFrm.Pos().AdjustY(1 );
|
|
}
|
|
|
|
mrpPrv = mrpFrame;
|
|
if( mrpFrame->IsTabFrame() )
|
|
{
|
|
SwTabFrame* pTab = static_cast<SwTabFrame*>(mrpFrame);
|
|
// #i33629#, #i29955#
|
|
::RegistFlys( pTab->FindPageFrame(), pTab );
|
|
SwFrame *pRow = pTab->Lower();
|
|
SwTabFrame *pFoll = new SwTabFrame( *pTab );
|
|
|
|
SwFrame *pPrv;
|
|
if( nRepeat > 0 )
|
|
{
|
|
sw::FlyCreationSuppressor aSuppressor;
|
|
// Insert new headlines:
|
|
sal_uInt16 nRowIdx = 0;
|
|
SwRowFrame* pHeadline = nullptr;
|
|
while( nRowIdx < nRepeat )
|
|
{
|
|
OSL_ENSURE( pTab->GetTable()->GetTabLines()[ nRowIdx ], "Table without rows?" );
|
|
pHeadline =
|
|
new SwRowFrame( *pTab->GetTable()->GetTabLines()[ nRowIdx ], pTab );
|
|
pHeadline->SetRepeatedHeadline( true );
|
|
pHeadline->InsertBefore( pFoll, nullptr );
|
|
pHeadline->RegistFlys();
|
|
|
|
++nRowIdx;
|
|
}
|
|
pPrv = pHeadline;
|
|
nRows = nRows + nRepeat;
|
|
}
|
|
else
|
|
pPrv = nullptr;
|
|
while( pRow && nRowCount < nOfst )
|
|
{
|
|
pRow = pRow->GetNext();
|
|
++nRowCount;
|
|
}
|
|
while ( pRow )
|
|
{
|
|
SwFrame* pNxt = pRow->GetNext();
|
|
pRow->RemoveFromLayout();
|
|
pRow->InsertBehind( pFoll, pPrv );
|
|
pPrv = pRow;
|
|
pRow = pNxt;
|
|
}
|
|
mrpFrame = pFoll;
|
|
}
|
|
else
|
|
{
|
|
SwTextFrame *const pNew = static_cast<SwTextFrame*>(
|
|
static_cast<SwTextFrame*>(mrpFrame)
|
|
->GetTextNodeFirst()->MakeFrame(mrpFrame));
|
|
pNew->ManipOfst( TextFrameIndex(nOfst) );
|
|
pNew->SetFollow( static_cast<SwTextFrame*>(mrpFrame)->GetFollow() );
|
|
static_cast<SwTextFrame*>(mrpFrame)->SetFollow( pNew );
|
|
mrpFrame = pNew;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SwPageFrame* pLastPage = mrpPage;
|
|
if (CheckInsertPage(mrpPage, mrpLay, mrpFrame, mbBreakAfter, mnMaxParaPerPage < mnParagraphCnt))
|
|
{
|
|
CheckFlyCache_( pLastPage );
|
|
if( mrpPrv && mrpPrv->IsTextFrame() && !mrpPrv->isFrameAreaSizeValid() )
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mrpPrv);
|
|
aFrm.Height( mrpPrv->GetUpper()->getFramePrintArea().Height() );
|
|
}
|
|
|
|
bRet = true;
|
|
mrpPrv = nullptr;
|
|
mnParagraphCnt = 0;
|
|
|
|
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
|
|
}
|
|
|
|
mrpLay = pSct;
|
|
if ( mrpLay->Lower() && mrpLay->Lower()->IsLayoutFrame() )
|
|
mrpLay = mrpLay->GetNextLayoutLeaf();
|
|
}
|
|
}
|
|
} while( bLongTab || ( mpImpl && mnIndex < mpImpl->size() &&
|
|
mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex ) );
|
|
}
|
|
mbFirst = false;
|
|
return bRet;
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct SdrObjectCompare
|
|
{
|
|
bool operator()( const SdrObject* pF1, const SdrObject* pF2 ) const
|
|
{
|
|
return pF1->GetOrdNum() < pF2->GetOrdNum();
|
|
}
|
|
};
|
|
|
|
struct FlyCacheCompare
|
|
{
|
|
bool operator()( const SwFlyCache* pC1, const SwFlyCache* pC2 ) const
|
|
{
|
|
return pC1->nOrdNum < pC2->nOrdNum;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
SwSortedObjs &rObjs = *pPage->GetSortedObjs();
|
|
sal_uInt16 nPgNum = pPage->GetPhyPageNum();
|
|
|
|
// NOTE: Here we do not use the absolute ordnums but
|
|
// relative ordnums for the objects on this page.
|
|
|
|
// skip fly frames from pages before the current page
|
|
while( mnFlyIdx < nFlyCount &&
|
|
mpImpl->GetFlyCache(mnFlyIdx).nPageNum < nPgNum )
|
|
++mnFlyIdx;
|
|
|
|
// sort cached objects on this page by ordnum
|
|
o3tl::sorted_vector< const SwFlyCache*, FlyCacheCompare > aFlyCacheSet;
|
|
size_t nIdx = mnFlyIdx;
|
|
|
|
SwFlyCache* pFlyC;
|
|
while( nIdx < nFlyCount &&
|
|
( pFlyC = &mpImpl->GetFlyCache( nIdx ) )->nPageNum == nPgNum )
|
|
{
|
|
aFlyCacheSet.insert( pFlyC );
|
|
++nIdx;
|
|
}
|
|
|
|
// sort objects on this page by ordnum
|
|
o3tl::sorted_vector< const SdrObject*, SdrObjectCompare > aFlySet;
|
|
for (SwAnchoredObject* pAnchoredObj : rObjs)
|
|
{
|
|
if (SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame()) // a text frame?
|
|
{
|
|
if( pFly->GetAnchorFrame() &&
|
|
!pFly->GetAnchorFrame()->FindFooterOrHeader() )
|
|
{
|
|
const SwContact *pC = ::GetUserCall( pAnchoredObj->GetDrawObj() );
|
|
if( pC )
|
|
{
|
|
aFlySet.insert( pAnchoredObj->GetDrawObj() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( aFlyCacheSet.size() != aFlySet.size() )
|
|
return;
|
|
|
|
auto aFlySetIt = aFlySet.begin();
|
|
|
|
for ( const SwFlyCache* pFlyCache : aFlyCacheSet )
|
|
{
|
|
SwFlyFrame* pFly = const_cast<SwVirtFlyDrawObj*>(static_cast<const SwVirtFlyDrawObj*>(*aFlySetIt))->GetFlyFrame();
|
|
|
|
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() );
|
|
|
|
if ( mpImpl->IsUseFlyCache() )
|
|
{
|
|
aFrm.Width( pFlyCache->Width() );
|
|
aFrm.Height( pFlyCache->Height() );
|
|
}
|
|
}
|
|
|
|
++aFlySetIt;
|
|
}
|
|
}
|
|
|
|
SwLayCacheIoImpl::SwLayCacheIoImpl( SvStream& rStrm, bool bWrtMd ) :
|
|
m_pStream( &rStrm ),
|
|
m_nFlagRecEnd ( 0 ),
|
|
m_nMajorVersion(SW_LAYCACHE_IO_VERSION_MAJOR),
|
|
m_nMinorVersion(SW_LAYCACHE_IO_VERSION_MINOR),
|
|
m_bWriteMode( bWrtMd ),
|
|
m_bError( false )
|
|
{
|
|
if( m_bWriteMode )
|
|
m_pStream->WriteUInt16( m_nMajorVersion )
|
|
.WriteUInt16( m_nMinorVersion );
|
|
|
|
else
|
|
m_pStream->ReadUInt16( m_nMajorVersion )
|
|
.ReadUInt16( m_nMinorVersion );
|
|
}
|
|
|
|
void SwLayCacheIoImpl::OpenRec( sal_uInt8 cType )
|
|
{
|
|
sal_uInt64 nPos = m_pStream->Tell();
|
|
if( m_bWriteMode )
|
|
{
|
|
m_aRecords.emplace_back(cType, nPos );
|
|
m_pStream->WriteUInt32( 0 );
|
|
}
|
|
else
|
|
{
|
|
sal_uInt32 nVal(0);
|
|
m_pStream->ReadUInt32( nVal );
|
|
sal_uInt8 cRecTyp = static_cast<sal_uInt8>(nVal);
|
|
if (!nVal || cRecTyp != cType || !m_pStream->good())
|
|
{
|
|
OSL_ENSURE( nVal, "OpenRec: Record-Header is 0" );
|
|
OSL_ENSURE( cRecTyp == cType, "OpenRec: Wrong Record Type" );
|
|
m_aRecords.emplace_back(0, m_pStream->Tell() );
|
|
m_bError = true;
|
|
}
|
|
else
|
|
{
|
|
sal_uInt32 nSize = nVal >> 8;
|
|
m_aRecords.emplace_back(cRecTyp, nPos+nSize );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Close record
|
|
void SwLayCacheIoImpl::CloseRec()
|
|
{
|
|
bool bRes = true;
|
|
OSL_ENSURE( !m_aRecords.empty(), "CloseRec: no levels" );
|
|
if( !m_aRecords.empty() )
|
|
{
|
|
sal_uInt64 nPos = m_pStream->Tell();
|
|
if( m_bWriteMode )
|
|
{
|
|
sal_uInt32 nBgn = m_aRecords.back().size;
|
|
m_pStream->Seek( nBgn );
|
|
sal_uInt32 nSize = nPos - nBgn;
|
|
sal_uInt32 nVal = ( nSize << 8 ) | m_aRecords.back().type;
|
|
m_pStream->WriteUInt32( nVal );
|
|
m_pStream->Seek( nPos );
|
|
if( m_pStream->GetError() != ERRCODE_NONE )
|
|
bRes = false;
|
|
}
|
|
else
|
|
{
|
|
sal_uInt32 n = m_aRecords.back().size;
|
|
OSL_ENSURE( n >= nPos, "CloseRec: too much data read" );
|
|
if( n != nPos )
|
|
{
|
|
m_pStream->Seek( n );
|
|
if( n < nPos )
|
|
bRes = false;
|
|
}
|
|
if( m_pStream->GetErrorCode() != ERRCODE_NONE )
|
|
bRes = false;
|
|
}
|
|
m_aRecords.pop_back();
|
|
}
|
|
|
|
if( !bRes )
|
|
m_bError = true;
|
|
}
|
|
|
|
sal_uInt32 SwLayCacheIoImpl::BytesLeft()
|
|
{
|
|
sal_uInt32 n = 0;
|
|
if( !m_bError && !m_aRecords.empty() )
|
|
{
|
|
sal_uInt32 nEndPos = m_aRecords.back().size;
|
|
sal_uInt64 nPos = m_pStream->Tell();
|
|
if( nEndPos > nPos )
|
|
n = nEndPos - nPos;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
sal_uInt8 SwLayCacheIoImpl::Peek()
|
|
{
|
|
sal_uInt8 c(0);
|
|
if( !m_bError )
|
|
{
|
|
sal_uInt64 nPos = m_pStream->Tell();
|
|
m_pStream->ReadUChar( c );
|
|
m_pStream->Seek( nPos );
|
|
if( m_pStream->GetErrorCode() != ERRCODE_NONE )
|
|
{
|
|
c = 0;
|
|
m_bError = true;
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
void SwLayCacheIoImpl::SkipRec()
|
|
{
|
|
sal_uInt8 c = Peek();
|
|
OpenRec( c );
|
|
m_pStream->Seek( m_aRecords.back().size );
|
|
CloseRec();
|
|
}
|
|
|
|
sal_uInt8 SwLayCacheIoImpl::OpenFlagRec()
|
|
{
|
|
OSL_ENSURE( !m_bWriteMode, "OpenFlagRec illegal in write mode" );
|
|
sal_uInt8 cFlags(0);
|
|
m_pStream->ReadUChar( cFlags );
|
|
m_nFlagRecEnd = m_pStream->Tell() + ( cFlags & 0x0F );
|
|
return (cFlags >> 4);
|
|
}
|
|
|
|
void SwLayCacheIoImpl::OpenFlagRec( sal_uInt8 nFlags, sal_uInt8 nLen )
|
|
{
|
|
OSL_ENSURE( m_bWriteMode, "OpenFlagRec illegal in read mode" );
|
|
OSL_ENSURE( (nFlags & 0xF0) == 0, "illegal flags set" );
|
|
OSL_ENSURE( nLen < 16, "wrong flag record length" );
|
|
sal_uInt8 cFlags = (nFlags << 4) + nLen;
|
|
m_pStream->WriteUChar( cFlags );
|
|
m_nFlagRecEnd = m_pStream->Tell() + nLen;
|
|
}
|
|
|
|
void SwLayCacheIoImpl::CloseFlagRec()
|
|
{
|
|
if( m_bWriteMode )
|
|
{
|
|
OSL_ENSURE( m_pStream->Tell() == m_nFlagRecEnd, "Wrong amount of data written" );
|
|
}
|
|
else
|
|
{
|
|
OSL_ENSURE( m_pStream->Tell() <= m_nFlagRecEnd, "Too many data read" );
|
|
if( m_pStream->Tell() != m_nFlagRecEnd )
|
|
m_pStream->Seek( m_nFlagRecEnd );
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|