diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/filter/writer | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/filter/writer')
-rw-r--r-- | sw/source/filter/writer/writer.cxx | 505 | ||||
-rw-r--r-- | sw/source/filter/writer/wrt_fn.cxx | 158 | ||||
-rw-r--r-- | sw/source/filter/writer/wrtswtbl.cxx | 866 |
3 files changed, 1529 insertions, 0 deletions
diff --git a/sw/source/filter/writer/writer.cxx b/sw/source/filter/writer/writer.cxx new file mode 100644 index 0000000000..1671f27e0b --- /dev/null +++ b/sw/source/filter/writer/writer.cxx @@ -0,0 +1,505 @@ +/* -*- 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 <memory> +#include <hintids.hxx> + +#include <sot/storage.hxx> +#include <sfx2/docfile.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/eeitem.hxx> +#include <osl/diagnose.h> +#include <shellio.hxx> +#include <doc.hxx> +#include <docary.hxx> +#include <IMark.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <numrule.hxx> +#include <swerror.h> +#include <com/sun/star/ucb/ContentCreationException.hpp> + +using namespace css; + +typedef std::multimap<SwNodeOffset, const ::sw::mark::IMark*> SwBookmarkNodeTable; + +struct Writer_Impl +{ + SvStream * m_pStream; + + std::map<OUString, OUString> maFileNameMap; + std::vector<const SvxFontItem*> aFontRemoveLst; + SwBookmarkNodeTable aBkmkNodePos; + + Writer_Impl(); + + void RemoveFontList( SwDoc& rDoc ); + void InsertBkmk( const ::sw::mark::IMark& rBkmk ); +}; + +Writer_Impl::Writer_Impl() + : m_pStream(nullptr) +{ +} + +void Writer_Impl::RemoveFontList( SwDoc& rDoc ) +{ + for( const auto& rpFontItem : aFontRemoveLst ) + { + rDoc.GetAttrPool().DirectRemoveItemFromPool( *rpFontItem ); + } +} + +void Writer_Impl::InsertBkmk(const ::sw::mark::IMark& rBkmk) +{ + SwNodeOffset nNd = rBkmk.GetMarkPos().GetNodeIndex(); + + aBkmkNodePos.emplace( nNd, &rBkmk ); + + if(rBkmk.IsExpanded() && rBkmk.GetOtherMarkPos().GetNodeIndex() != nNd) + { + nNd = rBkmk.GetOtherMarkPos().GetNodeIndex(); + aBkmkNodePos.emplace( nNd, &rBkmk ); + } +} + +/* + * This module is the central collection point for all writer-filters + * and is a DLL ! + * + * So that the Writer can work with different writers, the output-functions + * of the content carrying objects have to be mapped to the various + * output-functions. + * + * For that, to inquire its output function, every object can be gripped + * via the which-value in a table. + * These functions are available in the corresponding Writer-DLL's. + */ + +Writer::Writer() + : m_pImpl(std::make_unique<Writer_Impl>()) + , m_pOrigFileName(nullptr), m_pDoc(nullptr), m_pOrigPam(nullptr) + , m_bHideDeleteRedlines(false) +{ + m_bWriteAll = m_bShowProgress = m_bUCS2_WithStartChar = true; + m_bASCII_NoLastLineEnd = m_bASCII_ParaAsBlank = m_bASCII_ParaAsCR = + m_bWriteClipboardDoc = m_bWriteOnlyFirstTable = m_bBlock = + m_bOrganizerMode = false; + m_bExportParagraphNumbering = true; +} + +Writer::~Writer() +{ +} + +/* + * Document Interface Access + */ +IDocumentSettingAccess& Writer::getIDocumentSettingAccess() { return m_pDoc->getIDocumentSettingAccess(); } +const IDocumentSettingAccess& Writer::getIDocumentSettingAccess() const { return m_pDoc->getIDocumentSettingAccess(); } +IDocumentStylePoolAccess& Writer::getIDocumentStylePoolAccess() { return m_pDoc->getIDocumentStylePoolAccess(); } +const IDocumentStylePoolAccess& Writer::getIDocumentStylePoolAccess() const { return m_pDoc->getIDocumentStylePoolAccess(); } + +void Writer::ResetWriter() +{ + m_pImpl->RemoveFontList( *m_pDoc ); + m_pImpl.reset(new Writer_Impl); + + if( m_pCurrentPam ) + { + while (m_pCurrentPam->GetNext() != m_pCurrentPam.get()) + delete m_pCurrentPam->GetNext(); + m_pCurrentPam.reset(); + } + m_pCurrentPam = nullptr; + m_pOrigFileName = nullptr; + m_pDoc = nullptr; + + m_bShowProgress = m_bUCS2_WithStartChar = true; + m_bASCII_NoLastLineEnd = m_bASCII_ParaAsBlank = m_bASCII_ParaAsCR = + m_bWriteClipboardDoc = m_bWriteOnlyFirstTable = m_bBlock = + m_bOrganizerMode = false; +} + +bool Writer::CopyNextPam( SwPaM ** ppPam ) +{ + if( (*ppPam)->GetNext() == m_pOrigPam ) + { + *ppPam = m_pOrigPam; // set back to the beginning pam + return false; // end of the ring + } + + // otherwise copy the next value from the next Pam + *ppPam = (*ppPam)->GetNext(); + + *m_pCurrentPam->GetPoint() = *(*ppPam)->Start(); + *m_pCurrentPam->GetMark() = *(*ppPam)->End(); + + return true; +} + +// search the next Bookmark-Position from the Bookmark-Table + +sal_Int32 Writer::FindPos_Bkmk(const SwPosition& rPos) const +{ + const IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess(); + const IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findFirstBookmarkStartsAfter(rPos); + if(ppBkmk != pMarkAccess->getBookmarksEnd()) + return ppBkmk - pMarkAccess->getBookmarksBegin(); + return -1; +} + +std::shared_ptr<SwUnoCursor> +Writer::NewUnoCursor(SwDoc & rDoc, SwNodeOffset const nStartIdx, SwNodeOffset const nEndIdx) +{ + SwNodes *const pNds = &rDoc.GetNodes(); + + SwNodeIndex aStt( *pNds, nStartIdx ); + SwContentNode* pCNode = aStt.GetNode().GetContentNode(); + if( !pCNode && nullptr == pNds->GoNext( &aStt ) ) + { + OSL_FAIL( "No more ContentNode at StartPos" ); + } + + auto const pNew = rDoc.CreateUnoCursor(SwPosition(aStt), false); + pNew->SetMark(); + aStt = nEndIdx; + pCNode = aStt.GetNode().GetContentNode(); + if (!pCNode) + pCNode = SwNodes::GoPrevious(&aStt); + assert(pCNode && "No more ContentNode at StartPos"); + pNew->GetPoint()->AssignEndIndex( *pCNode ); + return pNew; +} + +// Stream-specific +SvStream& Writer::Strm() +{ + assert(m_pImpl->m_pStream && "Oh-oh. Writer with no Stream!"); + return *m_pImpl->m_pStream; +} + +void Writer::SetStream(SvStream *const pStream) +{ + m_pImpl->m_pStream = pStream; +} + +ErrCodeMsg Writer::Write( SwPaM& rPaM, SvStream& rStrm, const OUString* pFName ) +{ + if ( IsStgWriter() ) + { + ErrCodeMsg nResult = ERRCODE_ABORT; + try + { + tools::SvRef<SotStorage> aRef = new SotStorage( rStrm ); + nResult = Write( rPaM, *aRef, pFName ); + if ( nResult == ERRCODE_NONE ) + aRef->Commit(); + } + catch (const css::ucb::ContentCreationException &) + { + TOOLS_WARN_EXCEPTION("sw", "Writer::Write caught"); + } + return nResult; + } + + m_pDoc = &rPaM.GetDoc(); + m_pOrigFileName = pFName; + m_pImpl->m_pStream = &rStrm; + + // Copy PaM, so that it can be modified + m_pCurrentPam = m_pDoc->CreateUnoCursor(*rPaM.End(), false); + m_pCurrentPam->SetMark(); + *m_pCurrentPam->GetPoint() = *rPaM.Start(); + // for comparison secure to the current Pam + m_pOrigPam = &rPaM; + + ErrCode nRet = WriteStream(); + + ResetWriter(); + + return nRet; +} + +void Writer::SetupFilterOptions(SfxMedium& /*rMedium*/) +{} + +ErrCodeMsg Writer::Write( SwPaM& rPam, SfxMedium& rMedium, const OUString* pFileName ) +{ + SetupFilterOptions(rMedium); + // This method must be overridden in SwXMLWriter a storage from medium will be used there. + // The microsoft format can write to storage but the storage will be based on the stream. + return Write( rPam, *rMedium.GetOutStream(), pFileName ); +} + +ErrCodeMsg Writer::Write( SwPaM& /*rPam*/, SotStorage&, const OUString* ) +{ + OSL_ENSURE( false, "Write in Storages on a stream?" ); + return ERR_SWG_WRITE_ERROR; +} + +ErrCodeMsg Writer::Write( SwPaM&, const uno::Reference < embed::XStorage >&, const OUString*, SfxMedium* ) +{ + OSL_ENSURE( false, "Write in Storages on a stream?" ); + return ERR_SWG_WRITE_ERROR; +} + +bool Writer::CopyLocalFileToINet( OUString& rFileNm ) +{ + if( !m_pOrigFileName ) // can be happen, by example if we + return false; // write into the clipboard + + bool bRet = false; + INetURLObject aFileUrl( rFileNm ), aTargetUrl( *m_pOrigFileName ); + + if (!(INetProtocol::File == aFileUrl.GetProtocol() + && (INetProtocol::Http == aTargetUrl.GetProtocol() + || INetProtocol::Https == aTargetUrl.GetProtocol() + || INetProtocol::VndSunStarWebdav == aTargetUrl.GetProtocol() + || INetProtocol::Smb == aTargetUrl.GetProtocol() + || INetProtocol::Sftp == aTargetUrl.GetProtocol() + || INetProtocol::Cmis == aTargetUrl.GetProtocol()))) + { + return bRet; + } + + // has the file been moved? + std::map<OUString, OUString>::iterator it = m_pImpl->maFileNameMap.find( rFileNm ); + if ( it != m_pImpl->maFileNameMap.end() ) + { + rFileNm = it->second; + return true; + } + + OUString aSrc = rFileNm; + OUString aDest = aTargetUrl.GetPartBeforeLastName() + aFileUrl.GetLastName(); + + SfxMedium aSrcFile( aSrc, StreamMode::READ ); + SfxMedium aDstFile( aDest, StreamMode::WRITE | StreamMode::SHARE_DENYNONE ); + + aDstFile.GetOutStream()->WriteStream( *aSrcFile.GetInStream() ); + + aSrcFile.Close(); + aDstFile.Commit(); + + bRet = ERRCODE_NONE == aDstFile.GetErrorIgnoreWarning(); + + if( bRet ) + { + m_pImpl->maFileNameMap.insert( std::make_pair( aSrc, aDest ) ); + rFileNm = aDest; + } + + return bRet; +} + +void Writer::PutNumFormatFontsInAttrPool() +{ + // then there are a few fonts in the NumRules + // These put into the Pool. After this does they have a RefCount > 1 + // it can be removed - it is already in the Pool + SfxItemPool& rPool = m_pDoc->GetAttrPool(); + const SwNumRuleTable& rListTable = m_pDoc->GetNumRuleTable(); + const SwNumFormat* pFormat; + const vcl::Font* pDefFont = &numfunc::GetDefBulletFont(); + bool bCheck = false; + + for( size_t nGet = rListTable.size(); nGet; ) + { + SwNumRule const*const pRule = rListTable[ --nGet ]; + if (m_pDoc->IsUsed(*pRule)) + { + for( sal_uInt8 nLvl = 0; nLvl < MAXLEVEL; ++nLvl ) + { + if( SVX_NUM_CHAR_SPECIAL == (pFormat = &pRule->Get( nLvl ))->GetNumberingType() || + SVX_NUM_BITMAP == pFormat->GetNumberingType() ) + { + std::optional<vcl::Font> pFont = pFormat->GetBulletFont(); + if( !pFont ) + pFont = *pDefFont; + + if( bCheck ) + { + if( *pFont == *pDefFont ) + continue; + } + else if( *pFont == *pDefFont ) + bCheck = true; + + AddFontItem( rPool, SvxFontItem( pFont->GetFamilyType(), + pFont->GetFamilyName(), pFont->GetStyleName(), + pFont->GetPitch(), pFont->GetCharSet(), RES_CHRATR_FONT )); + } + } + } + } +} + +void Writer::PutEditEngFontsInAttrPool() +{ + SfxItemPool& rPool = m_pDoc->GetAttrPool(); + if( rPool.GetSecondaryPool() ) + { + AddFontItems_( rPool, EE_CHAR_FONTINFO ); + AddFontItems_( rPool, EE_CHAR_FONTINFO_CJK ); + AddFontItems_( rPool, EE_CHAR_FONTINFO_CTL ); + } +} + +void Writer::AddFontItems_( SfxItemPool& rPool, sal_uInt16 nW ) +{ + const SvxFontItem* pFont = static_cast<const SvxFontItem*>(&rPool.GetDefaultItem( nW )); + AddFontItem( rPool, *pFont ); + + pFont = static_cast<const SvxFontItem*>(rPool.GetPoolDefaultItem( nW )); + if( nullptr != pFont ) + AddFontItem( rPool, *pFont ); + + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nW)) + AddFontItem( rPool, *static_cast<const SvxFontItem*>(pItem) ); +} + +void Writer::AddFontItem( SfxItemPool& rPool, const SvxFontItem& rFont ) +{ + const SvxFontItem* pItem; + if( RES_CHRATR_FONT != rFont.Which() ) + { + SvxFontItem aFont( rFont ); + aFont.SetWhich( RES_CHRATR_FONT ); + pItem = &rPool.DirectPutItemInPool( aFont ); + } + else + pItem = &rPool.DirectPutItemInPool( rFont ); + + if( 1 < pItem->GetRefCount() ) + rPool.DirectRemoveItemFromPool( *pItem ); + else + { + m_pImpl->aFontRemoveLst.push_back( pItem ); + } +} + +// build a bookmark table, which is sort by the node position. The +// OtherPos of the bookmarks also inserted. +void Writer::CreateBookmarkTable() +{ + const IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess(); + for(IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->getBookmarksBegin(); + ppBkmk != pMarkAccess->getBookmarksEnd(); + ++ppBkmk) + { + m_pImpl->InsertBkmk(**ppBkmk); + } +} + +// search all Bookmarks in the range and return it in the Array +bool Writer::GetBookmarks(const SwContentNode& rNd, sal_Int32 nStt, + sal_Int32 nEnd, std::vector< const ::sw::mark::IMark* >& rArr) +{ + OSL_ENSURE( rArr.empty(), "there are still entries available" ); + + SwNodeOffset nNd = rNd.GetIndex(); + std::pair<SwBookmarkNodeTable::const_iterator, SwBookmarkNodeTable::const_iterator> aIterPair + = m_pImpl->aBkmkNodePos.equal_range( nNd ); + if( aIterPair.first != aIterPair.second ) + { + // there exist some bookmarks, search now all which is in the range + if( !nStt && nEnd == rNd.Len() ) + // all + for( SwBookmarkNodeTable::const_iterator it = aIterPair.first; it != aIterPair.second; ++it ) + rArr.push_back( it->second ); + else + { + for( SwBookmarkNodeTable::const_iterator it = aIterPair.first; it != aIterPair.second; ++it ) + { + const ::sw::mark::IMark& rBkmk = *(it->second); + sal_Int32 nContent; + if( rBkmk.GetMarkPos().GetNode() == rNd && + (nContent = rBkmk.GetMarkPos().GetContentIndex() ) >= nStt && + nContent < nEnd ) + { + rArr.push_back( &rBkmk ); + } + else if( rBkmk.IsExpanded() && + (rNd == rBkmk.GetOtherMarkPos().GetNode()) && + (nContent = rBkmk.GetOtherMarkPos().GetContentIndex()) >= nStt && + nContent < nEnd ) + { + rArr.push_back( &rBkmk ); + } + } + } + } + return !rArr.empty(); +} + +// Storage-specific +ErrCode StgWriter::WriteStream() +{ + OSL_ENSURE( false, "Write in Storages on a stream?" ); + return ERR_SWG_WRITE_ERROR; +} + +ErrCodeMsg StgWriter::Write( SwPaM& rPaM, SotStorage& rStg, const OUString* pFName ) +{ + SetStream(nullptr); + m_pStg = &rStg; + m_pDoc = &rPaM.GetDoc(); + m_pOrigFileName = pFName; + + // Copy PaM, so that it can be modified + m_pCurrentPam = m_pDoc->CreateUnoCursor(*rPaM.End(), false); + m_pCurrentPam->SetMark(); + *m_pCurrentPam->GetPoint() = *rPaM.Start(); + // for comparison secure to the current Pam + m_pOrigPam = &rPaM; + + ErrCodeMsg nRet = WriteStorage(); + + m_pStg = nullptr; + ResetWriter(); + + return nRet; +} + +ErrCodeMsg StgWriter::Write( SwPaM& rPaM, const uno::Reference < embed::XStorage >& rStg, const OUString* pFName, SfxMedium* pMedium ) +{ + SetStream(nullptr); + m_pStg = nullptr; + m_xStg = rStg; + m_pDoc = &rPaM.GetDoc(); + m_pOrigFileName = pFName; + + // Copy PaM, so that it can be modified + m_pCurrentPam = m_pDoc->CreateUnoCursor(*rPaM.End(), false); + m_pCurrentPam->SetMark(); + *m_pCurrentPam->GetPoint() = *rPaM.Start(); + // for comparison secure to the current Pam + m_pOrigPam = &rPaM; + + ErrCodeMsg nRet = pMedium ? WriteMedium( *pMedium ) : WriteStorage(); + + m_pStg = nullptr; + ResetWriter(); + + return nRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/writer/wrt_fn.cxx b/sw/source/filter/writer/wrt_fn.cxx new file mode 100644 index 0000000000..e566aab4ec --- /dev/null +++ b/sw/source/filter/writer/wrt_fn.cxx @@ -0,0 +1,158 @@ +/* -*- 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 <svl/itemiter.hxx> +#include <svl/whiter.hxx> +#include <osl/diagnose.h> + +// tdf#94088 SdrAllFillAttributesHelper needed +#include <svx/unobrushitemhelper.hxx> + +#include <shellio.hxx> +#include <wrt_fn.hxx> +#include <node.hxx> + +SwHTMLWriter& Out( const SwAttrFnTab pTab, const SfxPoolItem& rHt, SwHTMLWriter & rWrt ) +{ + sal_uInt16 nId = rHt.Which(); + OSL_ENSURE( nId < POOLATTR_END && nId >= POOLATTR_BEGIN, "SwAttrFnTab::Out()" ); + FnAttrOut pOut = pTab[ nId - RES_CHRATR_BEGIN]; + if( nullptr != pOut ) + (*pOut)( rWrt, rHt ); + return rWrt; + +} + +SwHTMLWriter& Out_SfxItemSet( const SwAttrFnTab pTab, SwHTMLWriter& rWrt, + const SfxItemSet& rSet, bool bDeep ) +{ + // at first give the own attributes out + const SfxItemPool& rPool = *rSet.GetPool(); + const SfxItemSet* pSet = &rSet; + if( !pSet->Count() ) // Optimizing - empty Sets + { + if( !bDeep ) + return rWrt; + while( nullptr != ( pSet = pSet->GetParent() ) && !pSet->Count() ) + ; + if( !pSet ) + return rWrt; + } + const SfxPoolItem* pItem(nullptr); + FnAttrOut pOut; + + // tdf#94088 check if any FillAttribute is used [XATTR_FILL_FIRST .. XATTR_FILL_LAST] + // while processing the items + bool bFillItemUsed = false; + + if( !bDeep || !pSet->GetParent() ) + { + OSL_ENSURE( rSet.Count(), "It has been handled already, right?" ); + SfxItemIter aIter( *pSet ); + pItem = aIter.GetCurItem(); + do { + // pTab only covers POOLATTR_BEGIN..POOLATTR_END. + if( pItem->Which() <= POOLATTR_END ) + { + pOut = pTab[ pItem->Which() - RES_CHRATR_BEGIN]; + if( nullptr != pOut ) + { + (*pOut)( rWrt, *pItem ); + } + } + else if(XATTR_FILLSTYLE == pItem->Which()) + { + bFillItemUsed = true; + } + } while ((pItem = aIter.NextItem())); + } + else + { + SfxWhichIter aIter( *pSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while( nWhich ) + { + if( SfxItemState::SET == aIter.GetItemState( bDeep, &pItem ) && + ( *pItem != rPool.GetDefaultItem( nWhich ) + || ( pSet->GetParent() && + *pItem != pSet->GetParent()->Get( nWhich )) + )) + { + pOut = pTab[ nWhich - RES_CHRATR_BEGIN]; + if( nullptr != pOut ) + { + (*pOut)( rWrt, *pItem ); + } + else if(XATTR_FILLSTYLE == pItem->Which()) + { + bFillItemUsed = true; + } + } + nWhich = aIter.NextWhich(); + } + } + + if(bFillItemUsed) + { + // tdf#94088 if used, construct a SvxBrushItem and export it using the + // existing mechanisms. + // This is the right place in the future if the adapted fill attributes + // may be handled more directly in HTML export to handle them. + const std::unique_ptr<SvxBrushItem> aSvxBrushItem(getSvxBrushItemFromSourceSet(*pSet, RES_BACKGROUND, bDeep)); + + pOut = pTab[RES_BACKGROUND - RES_CHRATR_BEGIN]; + if( nullptr != pOut ) + { + (*pOut)( rWrt, *aSvxBrushItem ); + } + } + + return rWrt; +} + +Writer& Out( const SwNodeFnTab pTab, SwNode& rNode, Writer & rWrt ) +{ + // It must be a ContentNode! + SwContentNode * pCNd = rNode.GetContentNode(); + if( !pCNd ) + return rWrt; + + sal_uInt16 nId = RES_TXTNODE; + switch (pCNd->GetNodeType()) + { + case SwNodeType::Text: + nId = RES_TXTNODE; + break; + case SwNodeType::Grf: + nId = RES_GRFNODE; + break; + case SwNodeType::Ole: + nId = RES_OLENODE; + break; + default: + OSL_FAIL("What kind of node is it now?"); + break; + } + FnNodeOut pOut = pTab[ nId - RES_NODE_BEGIN ]; + if( nullptr != pOut ) + (*pOut)( rWrt, *pCNd ); + return rWrt; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/writer/wrtswtbl.cxx b/sw/source/filter/writer/wrtswtbl.cxx new file mode 100644 index 0000000000..223ccf9626 --- /dev/null +++ b/sw/source/filter/writer/wrtswtbl.cxx @@ -0,0 +1,866 @@ +/* -*- 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 <memory> +#include <hintids.hxx> +#include <editeng/borderline.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/brushitem.hxx> +#include <tools/fract.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <wrtswtbl.hxx> +#include <swtable.hxx> +#include <frmfmt.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <htmltbl.hxx> + +using ::editeng::SvxBorderLine; +using namespace ::com::sun::star; + +sal_Int16 SwWriteTableCell::GetVertOri() const +{ + sal_Int16 eCellVertOri = text::VertOrientation::TOP; + if( m_pBox->GetSttNd() ) + { + const SfxItemSet& rItemSet = m_pBox->GetFrameFormat()->GetAttrSet(); + if( const SwFormatVertOrient *pItem = rItemSet.GetItemIfSet( RES_VERT_ORIENT, false ) ) + { + sal_Int16 eBoxVertOri = pItem->GetVertOrient(); + if( text::VertOrientation::CENTER==eBoxVertOri || text::VertOrientation::BOTTOM==eBoxVertOri) + eCellVertOri = eBoxVertOri; + } + } + + return eCellVertOri; +} + +SwWriteTableRow::SwWriteTableRow( tools::Long nPosition, bool bUseLayoutHeights ) + : m_pBackground(nullptr), m_nPos(nPosition), mbUseLayoutHeights(bUseLayoutHeights), + m_bTopBorder(true), m_bBottomBorder(true) +{ +} + +SwWriteTableCell *SwWriteTableRow::AddCell( const SwTableBox *pBox, + sal_uInt16 nRow, sal_uInt16 nCol, + sal_uInt16 nRowSpan, sal_uInt16 nColSpan, + tools::Long nHeight, + const SvxBrushItem *pBackgroundBrush ) +{ + SwWriteTableCell *pCell = + new SwWriteTableCell( pBox, nRow, nCol, nRowSpan, nColSpan, + nHeight, pBackgroundBrush ); + m_Cells.push_back(std::unique_ptr<SwWriteTableCell>(pCell)); + + return pCell; +} + +SwWriteTableCol::SwWriteTableCol(sal_uInt32 nPosition) + : m_nPos(nPosition), m_nWidthOpt(0), m_bRelWidthOpt(false), + m_bLeftBorder(true), m_bRightBorder(true) +{ +} + +sal_uInt32 SwWriteTable::GetBoxWidth( const SwTableBox *pBox ) +{ + const SwFrameFormat *pFormat = pBox->GetFrameFormat(); + const SwFormatFrameSize& aFrameSize= + pFormat->GetFormatAttr( RES_FRM_SIZE ); + + return sal::static_int_cast<sal_uInt32>(aFrameSize.GetSize().Width()); +} + +tools::Long SwWriteTable::GetLineHeight( const SwTableLine *pLine ) +{ +#ifdef DBG_UTIL + bool bOldGetLineHeightCalled = m_bGetLineHeightCalled; + m_bGetLineHeightCalled = true; +#endif + + tools::Long nHeight = 0; + if( m_bUseLayoutHeights ) + { + // At first we try to get the height of the layout. + bool bLayoutAvailable = false; + nHeight = pLine->GetTableLineHeight(bLayoutAvailable); + if( nHeight > 0 ) + return nHeight; + + // If no layout is found, we assume that the heights are fixed. + // #i60390# - in some cases we still want to continue + // to use the layout heights even if one of the rows has a height of 0 + // ('hidden' rows) + m_bUseLayoutHeights = bLayoutAvailable; + +#ifdef DBG_UTIL + SAL_WARN_IF( !bLayoutAvailable && bOldGetLineHeightCalled, "sw", "Layout invalid?" ); +#endif + } + + const SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + for( auto pBox : rBoxes ) + { + if( pBox->GetSttNd() ) + { + if( nHeight < ROW_DFLT_HEIGHT ) + nHeight = ROW_DFLT_HEIGHT; + } + else + { + tools::Long nTmp = 0; + const SwTableLines &rLines = pBox->GetTabLines(); + for( size_t nLine=0; nLine<rLines.size(); nLine++ ) + { + nTmp += GetLineHeight( rLines[nLine] ); + } + if( nHeight < nTmp ) + nHeight = nTmp; + } + } + + return nHeight; +} + +tools::Long SwWriteTable::GetLineHeight( const SwTableBox *pBox ) +{ + const SwTableLine *pLine = pBox->GetUpper(); + + if( !pLine ) + return 0; + + const SwFrameFormat *pLineFrameFormat = pLine->GetFrameFormat(); + const SfxItemSet& rItemSet = pLineFrameFormat->GetAttrSet(); + + tools::Long nHeight = 0; + if( const SwFormatFrameSize* pItem = rItemSet.GetItemIfSet( RES_FRM_SIZE ) ) + nHeight = pItem->GetHeight(); + + return nHeight; +} + +const SvxBrushItem *SwWriteTable::GetLineBrush( const SwTableBox *pBox, + SwWriteTableRow *pRow ) +{ + const SwTableLine *pLine = pBox->GetUpper(); + + while( pLine ) + { + const SwFrameFormat *pLineFrameFormat = pLine->GetFrameFormat(); + const SfxItemSet& rItemSet = pLineFrameFormat->GetAttrSet(); + + if( const SvxBrushItem* pItem = rItemSet.GetItemIfSet( RES_BACKGROUND, false ) ) + { + if( !pLine->GetUpper() ) + { + if( !pRow->GetBackground() ) + pRow->SetBackground( pItem ); + pItem = nullptr; + } + + return pItem; + } + + pBox = pLine->GetUpper(); + pLine = pBox ? pBox->GetUpper() : nullptr; + } + + return nullptr; +} + +void SwWriteTable::MergeBorders( const SvxBorderLine* pBorderLine, + bool bTable ) +{ + if( Color(ColorTransparency, 0xffffffff) == m_nBorderColor ) + { + if( !pBorderLine->GetColor().IsRGBEqual( COL_GRAY ) ) + m_nBorderColor = pBorderLine->GetColor(); + } + + if( !m_bCollectBorderWidth ) + return; + + const sal_uInt16 nOutWidth = pBorderLine->GetOutWidth(); + if( bTable ) + { + if( nOutWidth && (!m_nBorder || nOutWidth < m_nBorder) ) + m_nBorder = nOutWidth; + } + else + { + if( nOutWidth && (!m_nInnerBorder || nOutWidth < m_nInnerBorder) ) + m_nInnerBorder = nOutWidth; + } + + const sal_uInt16 nDist = pBorderLine->GetInWidth() ? pBorderLine->GetDistance() + : 0; + if( nDist && (!m_nCellSpacing || nDist < m_nCellSpacing) ) + m_nCellSpacing = nDist; +} + +sal_uInt16 SwWriteTable::MergeBoxBorders( const SwTableBox *pBox, + size_t const nRow, size_t const nCol, + sal_uInt16 nRowSpan, sal_uInt16 nColSpan, + sal_uInt16& rTopBorder, + sal_uInt16 &rBottomBorder ) +{ + sal_uInt16 nBorderMask = 0; + + const SwFrameFormat *pFrameFormat = pBox->GetFrameFormat(); + const SvxBoxItem& rBoxItem = pFrameFormat->GetFormatAttr( RES_BOX ); + + if( rBoxItem.GetTop() ) + { + nBorderMask |= 1; + MergeBorders( rBoxItem.GetTop(), nRow==0 ); + rTopBorder = rBoxItem.GetTop()->GetOutWidth(); + } + + if( rBoxItem.GetLeft() ) + { + nBorderMask |= 4; + MergeBorders( rBoxItem.GetLeft(), nCol==0 ); + } + + if( rBoxItem.GetBottom() ) + { + nBorderMask |= 2; + MergeBorders( rBoxItem.GetBottom(), nRow+nRowSpan==m_aRows.size() ); + rBottomBorder = rBoxItem.GetBottom()->GetOutWidth(); + } + + if( rBoxItem.GetRight() ) + { + nBorderMask |= 8; + MergeBorders( rBoxItem.GetRight(), nCol+nColSpan==m_aCols.size() ); + } + + // If any distance is set, the smallest one is used. This holds for + // the four distance of a box as well as for the distances of different + // boxes. + if( m_bCollectBorderWidth ) + { + sal_uInt16 nDist = rBoxItem.GetDistance( SvxBoxItemLine::TOP ); + if( nDist && (!m_nCellPadding || nDist < m_nCellPadding) ) + m_nCellPadding = nDist; + nDist = rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ); + if( nDist && (!m_nCellPadding || nDist < m_nCellPadding) ) + m_nCellPadding = nDist; + nDist = rBoxItem.GetDistance( SvxBoxItemLine::LEFT ); + if( nDist && (!m_nCellPadding || nDist < m_nCellPadding) ) + m_nCellPadding = nDist; + nDist = rBoxItem.GetDistance( SvxBoxItemLine::RIGHT ); + if( nDist && (!m_nCellPadding || nDist < m_nCellPadding) ) + m_nCellPadding = nDist; + } + + return nBorderMask; +} + +sal_uInt32 SwWriteTable::GetRawWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const +{ + sal_uInt32 nWidth = m_aCols[nCol+nColSpan-1]->GetPos(); + if( nCol > 0 ) + nWidth = nWidth - m_aCols[nCol-1]->GetPos(); + + return nWidth; +} + +sal_uInt16 SwWriteTable::GetLeftSpace( sal_uInt16 nCol ) const +{ + sal_uInt16 nSpace = m_nCellPadding + m_nCellSpacing; + + // Additional subtract the line thickness in the first column. + if( nCol==0 ) + { + nSpace = nSpace + m_nLeftSub; + + const SwWriteTableCol *pCol = m_aCols[nCol].get(); + if( pCol->HasLeftBorder() ) + nSpace = nSpace + m_nBorder; + } + + return nSpace; +} + +sal_uInt16 +SwWriteTable::GetRightSpace(size_t const nCol, sal_uInt16 nColSpan) const +{ + sal_uInt16 nSpace = m_nCellPadding; + + // Additional subtract in the last column CELLSPACING and + // line thickness once again. + if( nCol+nColSpan==m_aCols.size() ) + { + nSpace += (m_nCellSpacing + m_nRightSub); + + const SwWriteTableCol *pCol = m_aCols[nCol+nColSpan-1].get(); + if( pCol->HasRightBorder() ) + nSpace = nSpace + m_nBorder; + } + + return nSpace; +} + +sal_uInt16 SwWriteTable::GetAbsWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const +{ + sal_uInt32 nWidth = GetRawWidth( nCol, nColSpan ); + if( m_nBaseWidth != m_nTabWidth ) + { + nWidth *= m_nTabWidth; + nWidth /= m_nBaseWidth; + } + + nWidth -= GetLeftSpace( nCol ) + GetRightSpace( nCol, nColSpan ); + + OSL_ENSURE( nWidth > 0, "Column Width <= 0. OK?" ); + return nWidth > 0 ? o3tl::narrowing<sal_uInt16>(nWidth) : 0; +} + +sal_uInt16 SwWriteTable::GetRelWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const +{ + tools::Long nWidth = GetRawWidth( nCol, nColSpan ); + + return o3tl::narrowing<sal_uInt16>(static_cast<tools::Long>(Fraction( nWidth*256 + GetBaseWidth()/2, + GetBaseWidth() ))); +} + +sal_uInt16 SwWriteTable::GetPercentWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const +{ + tools::Long nWidth = GetRawWidth( nCol, nColSpan ); + + // Looks funny, but is nothing more than + // [(100 * nWidth) + .5] without rounding errors + return o3tl::narrowing<sal_uInt16>(static_cast<tools::Long>(Fraction( nWidth*100 + GetBaseWidth()/2, + GetBaseWidth() ))); +} + +tools::Long SwWriteTable::GetAbsHeight(tools::Long nRawHeight, size_t const nRow, + sal_uInt16 nRowSpan ) const +{ + nRawHeight -= (2*m_nCellPadding + m_nCellSpacing); + + // Additional subtract in the first column CELLSPACING and + // line thickness once again. + const SwWriteTableRow *pRow = nullptr; + if( nRow==0 ) + { + nRawHeight -= m_nCellSpacing; + pRow = m_aRows[nRow].get(); + if( pRow->HasTopBorder() ) + nRawHeight -= m_nBorder; + } + + // Subtract the line thickness in the last column + if( nRow+nRowSpan==m_aRows.size() ) + { + if( !pRow || nRowSpan > 1 ) + pRow = m_aRows[nRow+nRowSpan-1].get(); + if( pRow->HasBottomBorder() ) + nRawHeight -= m_nBorder; + } + + OSL_ENSURE( nRawHeight > 0, "Row Height <= 0. OK?" ); + return std::max<tools::Long>(nRawHeight, 0); +} + +bool SwWriteTable::ShouldExpandSub(const SwTableBox *pBox, bool /*bExpandedBefore*/, + sal_uInt16 nDepth) const +{ + return !pBox->GetSttNd() && nDepth > 0; +} + +// FIXME: the degree of coupling between this method and +// FillTableRowsCols which is called immediately afterwards +// is -extremely- unpleasant and potentially problematic. + +void SwWriteTable::CollectTableRowsCols( tools::Long nStartRPos, + sal_uInt32 nStartCPos, + tools::Long nParentLineHeight, + sal_uInt32 nParentLineWidth, + const SwTableLines& rLines, + sal_uInt16 nDepth ) +{ + bool bSubExpanded = false; + const SwTableLines::size_type nLines = rLines.size(); + +#if OSL_DEBUG_LEVEL > 0 + sal_uInt32 nEndCPos = 0; +#endif + + tools::Long nRPos = nStartRPos; + for( SwTableLines::size_type nLine = 0; nLine < nLines; ++nLine ) + { + /*const*/ SwTableLine *pLine = rLines[nLine]; + + tools::Long nOldRPos = nRPos; + + if( nLine < nLines-1 || nParentLineHeight==0 ) + { + tools::Long nLineHeight = GetLineHeight( pLine ); + nRPos += nLineHeight; + if( nParentLineHeight && nStartRPos + nParentLineHeight <= nRPos ) + { + /* If you have corrupt line height information, e.g. breaking rows in complex table + layout, you may run into this robust code. + It's not allowed that subrows leaves their parentrow. If this would happen the line + height of subrow is reduced to a part of the remaining height */ + OSL_FAIL( "Corrupt line height I" ); + nRPos -= nLineHeight; + nLineHeight = nStartRPos + nParentLineHeight - nRPos; // remaining parent height + nLineHeight /= nLines - nLine; // divided through the number of remaining sub rows + nRPos += nLineHeight; + } + std::unique_ptr<SwWriteTableRow> pRow(new SwWriteTableRow( nRPos, m_bUseLayoutHeights)); + m_aRows.insert( std::move(pRow) ); + } + else + { +#if OSL_DEBUG_LEVEL > 0 + tools::Long nCheckPos = nRPos + GetLineHeight( pLine ); +#endif + nRPos = nStartRPos + nParentLineHeight; +#if OSL_DEBUG_LEVEL > 0 + SwWriteTableRow aSrchRow( nRPos, m_bUseLayoutHeights ); + OSL_ENSURE( std::find_if(m_aRows.begin(), m_aRows.end(), + [&](std::unique_ptr<SwWriteTableRow> const & p) + { return *p == aSrchRow; }) != m_aRows.end(), "Parent-Row not found" ); + SwWriteTableRow aRowCheckPos(nCheckPos,m_bUseLayoutHeights); + SwWriteTableRow aRowRPos(nRPos,m_bUseLayoutHeights); + OSL_ENSURE( !m_bUseLayoutHeights || + aRowCheckPos == aRowRPos, + "Height of the rows does not correspond with the parent" ); +#endif + } + + // If necessary insert a column for all boxes of the row + const SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + const SwTableBoxes::size_type nBoxes = rBoxes.size(); + + sal_uInt32 nCPos = nStartCPos; + for( SwTableBoxes::size_type nBox=0; nBox<nBoxes; ++nBox ) + { + const SwTableBox *pBox = rBoxes[nBox]; + + sal_uInt32 nOldCPos = nCPos; + + if( nBox < nBoxes-1 || (nParentLineWidth==0 && nLine==0) ) + { + nCPos = nCPos + GetBoxWidth( pBox ); + std::unique_ptr<SwWriteTableCol> pCol(new SwWriteTableCol( nCPos )); + + m_aCols.insert( std::move(pCol) ); + + if( nBox==nBoxes-1 ) + { + OSL_ENSURE( nLine==0 && nParentLineWidth==0, + "Now the parent width will be flattened!" ); + nParentLineWidth = nCPos-nStartCPos; + } + } + else + { +#if OSL_DEBUG_LEVEL > 0 + sal_uInt32 nCheckPos = nCPos + GetBoxWidth( pBox ); + if( !nEndCPos ) + { + nEndCPos = nCheckPos; + } + else + { + OSL_ENSURE( SwWriteTableCol(nCheckPos) == + SwWriteTableCol(nEndCPos), + "Cell includes rows of different widths" ); + } +#endif + nCPos = nStartCPos + nParentLineWidth; + +#if OSL_DEBUG_LEVEL > 0 + SwWriteTableCol aSrchCol( nCPos ); + OSL_ENSURE( m_aCols.find( &aSrchCol ) != m_aCols.end(), + "Parent-Cell not found" ); + OSL_ENSURE( SwWriteTableCol(nCheckPos) == + SwWriteTableCol(nCPos), + "Width of the cells does not correspond with the parent" ); +#endif + } + + if( ShouldExpandSub( pBox, bSubExpanded, nDepth ) ) + { + CollectTableRowsCols( nOldRPos, nOldCPos, + nRPos - nOldRPos, + nCPos - nOldCPos, + pBox->GetTabLines(), + nDepth-1 ); + bSubExpanded = true; + } + } + } +} + +void SwWriteTable::FillTableRowsCols( tools::Long nStartRPos, sal_uInt16 nStartRow, + sal_uInt32 nStartCPos, sal_uInt16 nStartCol, + tools::Long nParentLineHeight, + sal_uInt32 nParentLineWidth, + const SwTableLines& rLines, + const SvxBrushItem* pParentBrush, + sal_uInt16 nDepth, + sal_uInt16 nNumOfHeaderRows ) +{ + const SwTableLines::size_type nLines = rLines.size(); + bool bSubExpanded = false; + + // Specifying the border + tools::Long nRPos = nStartRPos; + sal_uInt16 nRow = nStartRow; + + for( SwTableLines::size_type nLine = 0; nLine < nLines; ++nLine ) + { + const SwTableLine *pLine = rLines[nLine]; + + // Determine the position of the last covered row + tools::Long nOldRPos = nRPos; + if( nLine < nLines-1 || nParentLineHeight==0 ) + { + tools::Long nLineHeight = GetLineHeight( pLine ); + nRPos += nLineHeight; + if( nParentLineHeight && nStartRPos + nParentLineHeight <= nRPos ) + { + /* See comment in CollectTableRowCols */ + OSL_FAIL( "Corrupt line height II" ); + nRPos -= nLineHeight; + nLineHeight = nStartRPos + nParentLineHeight - nRPos; // remaining parent height + nLineHeight /= nLines - nLine; // divided through the number of remaining sub rows + nRPos += nLineHeight; + } + } + else + nRPos = nStartRPos + nParentLineHeight; + + // And their index + sal_uInt16 nOldRow = nRow; + SwWriteTableRow aSrchRow( nRPos,m_bUseLayoutHeights ); + SwWriteTableRows::const_iterator it2 = std::find_if(m_aRows.begin(), m_aRows.end(), + [&](std::unique_ptr<SwWriteTableRow> const &p) + { return *p == aSrchRow; }); + + // coupled methods out of sync ... + assert( it2 != m_aRows.end() ); + nRow = it2 - m_aRows.begin(); + + OSL_ENSURE( nOldRow <= nRow, "Don't look back!" ); + if( nOldRow > nRow ) + { + nOldRow = nRow; + if( nOldRow ) + --nOldRow; + } + + SwWriteTableRow *pRow = m_aRows[nOldRow].get(); + SwWriteTableRow *pEndRow = m_aRows[nRow].get(); + if( nLine+1==nNumOfHeaderRows && nParentLineHeight==0 ) + m_nHeadEndRow = nRow; + + const SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + + const SwFrameFormat *pLineFrameFormat = pLine->GetFrameFormat(); + const SfxItemSet& rItemSet = pLineFrameFormat->GetAttrSet(); + + tools::Long nHeight = 0; + if( const SwFormatFrameSize* pFrameSizeItem = rItemSet.GetItemIfSet( RES_FRM_SIZE )) + nHeight = pFrameSizeItem->GetHeight(); + + const SvxBrushItem *pBrushItem, *pLineBrush = pParentBrush; + if( const SvxBrushItem* pTmpBrush = rItemSet.GetItemIfSet( RES_BACKGROUND, false ) ) + { + pLineBrush = pTmpBrush; + + // If the row spans the entire table, we can + // print out the background to the row. Otherwise + // we have to print out into the cell. + bool bOutAtRow = !nParentLineWidth; + if( !bOutAtRow && nStartCPos==0 ) + { + SwWriteTableCol aCol( nParentLineWidth ); + bOutAtRow = m_aCols.find( &aCol ) == (m_aCols.end() - 1); + } + if( bOutAtRow ) + { + pRow->SetBackground( pLineBrush ); + pBrushItem = nullptr; + } + else + pBrushItem = pLineBrush; + } + else + { + pRow->SetBackground( pLineBrush ); + pBrushItem = nullptr; + } + + const SwTableBoxes::size_type nBoxes = rBoxes.size(); + sal_uInt32 nCPos = nStartCPos; + sal_uInt16 nCol = nStartCol; + + for( SwTableBoxes::size_type nBox=0; nBox<nBoxes; ++nBox ) + { + const SwTableBox *pBox = rBoxes[nBox]; + + // Determine the position of the last covered column + sal_uInt32 nOldCPos = nCPos; + if( nBox < nBoxes-1 || (nParentLineWidth==0 && nLine==0) ) + { + nCPos = nCPos + GetBoxWidth( pBox ); + if( nBox==nBoxes-1 ) + nParentLineWidth = nCPos - nStartCPos; + } + else + nCPos = nStartCPos + nParentLineWidth; + + // And their index + sal_uInt16 nOldCol = nCol; + SwWriteTableCol aSrchCol( nCPos ); + SwWriteTableCols::const_iterator it = m_aCols.find( &aSrchCol ); + OSL_ENSURE( it != m_aCols.end(), "missing column" ); + if(it != m_aCols.end()) + { + // if find fails for some nCPos value then it used to set nCol value with size of aCols. + nCol = it - m_aCols.begin(); + } + + if( !ShouldExpandSub( pBox, bSubExpanded, nDepth ) ) + { + sal_uInt16 nRowSpan = nRow - nOldRow + 1; + + // The new table model may have true row span attributes + const sal_Int32 nAttrRowSpan = pBox->getRowSpan(); + if ( 1 < nAttrRowSpan ) + nRowSpan = o3tl::narrowing<sal_uInt16>(nAttrRowSpan); + else if ( nAttrRowSpan < 1 ) + nRowSpan = 0; + + SAL_WARN_IF(nCol < nOldCol, "sw.filter", "unexpected " << nCol << " < " << nOldCol); + sal_uInt16 nColSpan = nCol >= nOldCol ? nCol - nOldCol + 1 : 1; + pRow->AddCell( pBox, nOldRow, nOldCol, + nRowSpan, nColSpan, nHeight, + pBrushItem ); + nHeight = 0; // The height requires only to be written once + + if( pBox->GetSttNd() ) + { + sal_uInt16 nTopBorder = USHRT_MAX, nBottomBorder = USHRT_MAX; + sal_uInt16 nBorderMask = MergeBoxBorders(pBox, nOldRow, nOldCol, + nRowSpan, nColSpan, nTopBorder, nBottomBorder); + + // #i30094# add a sanity check here to ensure that + // we don't access an invalid aCols[] as &nCol + // above can be changed. + if (!(nBorderMask & 4) && nOldCol < m_aCols.size()) + { + SwWriteTableCol *pCol = m_aCols[nOldCol].get(); + OSL_ENSURE(pCol, "No TableCol found, panic!"); + if (pCol) + pCol->m_bLeftBorder = false; + } + + if (!(nBorderMask & 8)) + { + SwWriteTableCol *pCol = m_aCols[nCol].get(); + OSL_ENSURE(pCol, "No TableCol found, panic!"); + if (pCol) + pCol->m_bRightBorder = false; + } + + if (!(nBorderMask & 1)) + pRow->SetTopBorder(false); + + if (!(nBorderMask & 2)) + pEndRow->SetBottomBorder(false); + } + } + else + { + FillTableRowsCols( nOldRPos, nOldRow, nOldCPos, nOldCol, + nRPos-nOldRPos, nCPos-nOldCPos, + pBox->GetTabLines(), + pLineBrush, nDepth-1, + nNumOfHeaderRows ); + bSubExpanded = true; + } + + nCol++; // The next cell begins in the next column + } + + nRow++; + } +} + +SwWriteTable::SwWriteTable(const SwTable* pTable, const SwTableLines& rLines, tools::Long nWidth, + sal_uInt32 nBWidth, bool bRel, sal_uInt16 nMaxDepth, sal_uInt16 nLSub, sal_uInt16 nRSub, sal_uInt32 nNumOfRowsToRepeat) + : m_pTable(pTable), m_nBorderColor(ColorTransparency, sal_uInt32(-1)), m_nCellSpacing(0), m_nCellPadding(0), m_nBorder(0), + m_nInnerBorder(0), m_nBaseWidth(nBWidth), m_nHeadEndRow(USHRT_MAX), + m_nLeftSub(nLSub), m_nRightSub(nRSub), m_nTabWidth(nWidth), m_bRelWidths(bRel), + m_bUseLayoutHeights(true), +#ifdef DBG_UTIL + m_bGetLineHeightCalled(false), +#endif + m_bColTags(true), m_bLayoutExport(false), + m_bCollectBorderWidth(true) +{ + sal_uInt32 nParentWidth = m_nBaseWidth + m_nLeftSub + m_nRightSub; + + // First the table structure set. Behind the table is in each + // case the end of a column + std::unique_ptr<SwWriteTableCol> pCol(new SwWriteTableCol( nParentWidth )); + m_aCols.insert( std::move(pCol) ); + m_bUseLayoutHeights = true; + CollectTableRowsCols( 0, 0, 0, nParentWidth, rLines, nMaxDepth - 1 ); + + // FIXME: awfully GetLineHeight writes to this in its first call + // and proceeds to return a rather odd number fdo#62336, we have to + // behave identically since the code in FillTableRowsCols duplicates + // and is highly coupled to CollectTableRowsCols - sadly. + m_bUseLayoutHeights = true; + // And now fill with life + FillTableRowsCols( 0, 0, 0, 0, 0, nParentWidth, rLines, nullptr, nMaxDepth - 1, static_cast< sal_uInt16 >(nNumOfRowsToRepeat) ); + + // Adjust some Twip values to pixel boundaries + if( !m_nBorder ) + m_nBorder = m_nInnerBorder; +} + +SwWriteTable::SwWriteTable(const SwTable* pTable, const SwHTMLTableLayout *pLayoutInfo) + : m_pTable(pTable), m_nBorderColor(ColorTransparency, sal_uInt32(-1)), m_nCellSpacing(0), m_nCellPadding(0), m_nBorder(0), + m_nInnerBorder(0), m_nBaseWidth(pLayoutInfo->GetWidthOption()), m_nHeadEndRow(0), + m_nLeftSub(0), m_nRightSub(0), m_nTabWidth(pLayoutInfo->GetWidthOption()), + m_bRelWidths(pLayoutInfo->HasPercentWidthOption()), m_bUseLayoutHeights(false), +#ifdef DBG_UTIL + m_bGetLineHeightCalled(false), +#endif + m_bColTags(pLayoutInfo->HasColTags()), m_bLayoutExport(true), + m_bCollectBorderWidth(pLayoutInfo->HaveBordersChanged()) +{ + if( !m_bCollectBorderWidth ) + { + m_nBorder = pLayoutInfo->GetBorder(); + m_nCellPadding = pLayoutInfo->GetCellPadding(); + m_nCellSpacing = pLayoutInfo->GetCellSpacing(); + } + + const sal_uInt16 nCols = pLayoutInfo->GetColCount(); + const sal_uInt16 nRows = pLayoutInfo->GetRowCount(); + + // First set the table structure. + for( sal_uInt16 nCol=0; nCol<nCols; ++nCol ) + { + std::unique_ptr<SwWriteTableCol> pCol( + new SwWriteTableCol( (nCol+1)*COL_DFLT_WIDTH )); + + if( m_bColTags ) + { + const SwHTMLTableLayoutColumn *pLayoutCol = + pLayoutInfo->GetColumn( nCol ); + pCol->SetWidthOpt( pLayoutCol->GetWidthOption(), + pLayoutCol->IsRelWidthOption() ); + } + + m_aCols.insert( std::move(pCol) ); + } + + for( sal_uInt16 nRow=0; nRow<nRows; ++nRow ) + { + std::unique_ptr<SwWriteTableRow> pRow( + new SwWriteTableRow( (nRow+1)*ROW_DFLT_HEIGHT, m_bUseLayoutHeights )); + m_aRows.insert( std::move(pRow) ); + } + + // And now fill with life + for( sal_uInt16 nRow=0; nRow<nRows; ++nRow ) + { + SwWriteTableRow *pRow = m_aRows[nRow].get(); + + bool bHeightExported = false; + for( sal_uInt16 nCol=0; nCol<nCols; nCol++ ) + { + const SwHTMLTableLayoutCell *pLayoutCell = + pLayoutInfo->GetCell( nRow, nCol ); + + const SwHTMLTableLayoutCnts *pLayoutCnts = + pLayoutCell->GetContents().get(); + + // The cell begins actually a row above or further forward? + if( ( nRow>0 && pLayoutCnts == pLayoutInfo->GetCell(nRow-1,nCol) + ->GetContents().get() ) || + ( nCol>0 && pLayoutCnts == pLayoutInfo->GetCell(nRow,nCol-1) + ->GetContents().get() ) ) + { + continue; + } + + const sal_uInt16 nRowSpan = pLayoutCell->GetRowSpan(); + const sal_uInt16 nColSpan = pLayoutCell->GetColSpan(); + const SwTableBox *pBox = pLayoutCnts->GetTableBox(); + OSL_ENSURE( pBox, + "Table in Table can not be exported over layout" ); + + tools::Long nHeight = bHeightExported ? 0 : GetLineHeight( pBox ); + const SvxBrushItem *pBrushItem = GetLineBrush( pBox, pRow ); + + SwWriteTableCell *pCell = + pRow->AddCell( pBox, nRow, nCol, nRowSpan, nColSpan, + nHeight, pBrushItem ); + pCell->SetWidthOpt( pLayoutCell->GetWidthOption(), + pLayoutCell->IsPercentWidthOption() ); + + sal_uInt16 nTopBorder = USHRT_MAX, nBottomBorder = USHRT_MAX; + sal_uInt16 nBorderMask = + MergeBoxBorders( pBox, nRow, nCol, nRowSpan, nColSpan, + nTopBorder, nBottomBorder ); + + SwWriteTableCol *pCol = m_aCols[nCol].get(); + if( !(nBorderMask & 4) ) + pCol->m_bLeftBorder = false; + + pCol = m_aCols[nCol+nColSpan-1].get(); + if( !(nBorderMask & 8) ) + pCol->m_bRightBorder = false; + + if( !(nBorderMask & 1) ) + pRow->SetTopBorder(false); + + SwWriteTableRow *pEndRow = m_aRows[nRow+nRowSpan-1].get(); + if( !(nBorderMask & 2) ) + pEndRow->SetBottomBorder(false); + + // The height requires only to be written once + if( nHeight ) + bHeightExported = true; + } + } + + // Adjust some Twip values to pixel boundaries + if( m_bCollectBorderWidth && !m_nBorder ) + m_nBorder = m_nInnerBorder; +} + +SwWriteTable::~SwWriteTable() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |