diff options
Diffstat (limited to 'sw/source/filter/xml')
39 files changed, 17707 insertions, 0 deletions
diff --git a/sw/source/filter/xml/XMLRedlineImportHelper.cxx b/sw/source/filter/xml/XMLRedlineImportHelper.cxx new file mode 100644 index 0000000000..1c7df7bee9 --- /dev/null +++ b/sw/source/filter/xml/XMLRedlineImportHelper.cxx @@ -0,0 +1,842 @@ +/* -*- 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 <sal/config.h> +#include <sal/log.hxx> + +#include <cstddef> + +#include "XMLRedlineImportHelper.hxx" +#include <unotextcursor.hxx> +#include <unotextrange.hxx> +#include <unocrsr.hxx> +#include <ndtxt.hxx> +#include <doc.hxx> +#include <IDocumentContentOperations.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <tools/datetime.hxx> +#include <poolfmt.hxx> +#include <fmtanchr.hxx> +#include <ftnidx.hxx> +#include <txtftn.hxx> +#include <unoredline.hxx> +#include <DocumentRedlineManager.hxx> +#include "xmlimp.hxx" +#include <comphelper/servicehelper.hxx> +#include <o3tl/any.hxx> +#include <xmloff/xmltoken.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::xmloff::token; + +using ::com::sun::star::text::XTextCursor; +using ::com::sun::star::text::XTextRange; +using ::com::sun::star::text::XWordCursor; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::beans::XPropertySetInfo; +// collision with tools/DateTime: use UNO DateTime as util::DateTime +// using util::DateTime; + +// a few helper functions +static SwDoc* lcl_GetDocViaTunnel( Reference<XTextCursor> const & rCursor ) +{ + OTextCursorHelper *const pXCursor = + dynamic_cast<OTextCursorHelper*>(rCursor.get()); + OSL_ENSURE( pXCursor, "OTextCursorHelper missing" ); + return pXCursor ? pXCursor->GetDoc() : nullptr; +} + +static SwDoc* lcl_GetDocViaTunnel( Reference<XTextRange> const & rRange ) +{ + SwXTextRange *const pXRange = dynamic_cast<SwXTextRange*>(rRange.get()); + OSL_ENSURE(pXRange, "missing SwXTextRange for XTextRange"); + // #i115174#: this may be a SvxUnoTextRange + // OSL_ENSURE( pXRange, "SwXTextRange missing" ); + return pXRange ? &pXRange->GetDoc() : nullptr; +} + +// XTextRangeOrNodeIndexPosition: store a position into the text +// *either* as an XTextRange or as an SwNodeIndex. The reason is that +// we must store either pointers to StartNodes (because redlines may +// start on start nodes) or to a text position, and there appears to +// be no existing type that could do both. Things are complicated by +// the matter that (e.g in section import) we delete a few characters, +// which may cause bookmarks (as used by XTextRange) to be deleted. + +namespace { + +class XTextRangeOrNodeIndexPosition +{ + Reference<XTextRange> m_xRange; + std::optional<SwNodeIndex> m_oIndex; // pIndex will point to the *previous* node + +public: + XTextRangeOrNodeIndexPosition(); + + void Set( Reference<XTextRange> const & rRange ); + void Set( SwNode const & rIndex ); + void SetAsNodeIndex( Reference<XTextRange> const & rRange ); + + void CopyPositionInto(SwPosition& rPos, SwDoc & rDoc); + SwDoc* GetDoc(); + + bool IsValid() const; +}; + +} + +XTextRangeOrNodeIndexPosition::XTextRangeOrNodeIndexPosition() +{ +} + +void XTextRangeOrNodeIndexPosition::Set( Reference<XTextRange> const & rRange ) +{ + m_xRange = rRange->getStart(); // set bookmark + m_oIndex.reset(); +} + +void XTextRangeOrNodeIndexPosition::Set( SwNode const & rIndex ) +{ + m_oIndex = rIndex; + --(*m_oIndex) ; // previous node!!! + m_xRange = nullptr; +} + +void XTextRangeOrNodeIndexPosition::SetAsNodeIndex( + Reference<XTextRange> const & rRange ) +{ + // XTextRange -> XTunnel -> SwXTextRange + SwDoc* pDoc = lcl_GetDocViaTunnel(rRange); + + if (!pDoc) + { + SAL_WARN("sw", "no SwDoc"); + return; + } + + // SwXTextRange -> PaM + SwUnoInternalPaM aPaM(*pDoc); + bool bSuccess = ::sw::XTextRangeToSwPaM(aPaM, rRange); + OSL_ENSURE(bSuccess, "illegal range"); + + // PaM -> Index + Set(aPaM.GetPoint()->GetNode()); +} + +void +XTextRangeOrNodeIndexPosition::CopyPositionInto(SwPosition& rPos, SwDoc & rDoc) +{ + OSL_ENSURE(IsValid(), "Can't get Position"); + + // create PAM from start cursor (if no node index is present) + if (!m_oIndex.has_value()) + { + SwUnoInternalPaM aUnoPaM(rDoc); + bool bSuccess = ::sw::XTextRangeToSwPaM(aUnoPaM, m_xRange); + OSL_ENSURE(bSuccess, "illegal range"); + + rPos = *aUnoPaM.GetPoint(); + } + else + { + rPos.Assign( m_oIndex->GetNode(), SwNodeOffset(1) ); // pIndex points to previous index !!! + } +} + +SwDoc* XTextRangeOrNodeIndexPosition::GetDoc() +{ + OSL_ENSURE(IsValid(), "Can't get Doc"); + + return m_oIndex.has_value() ? &m_oIndex->GetNodes().GetDoc() : lcl_GetDocViaTunnel(m_xRange); +} + +bool XTextRangeOrNodeIndexPosition::IsValid() const +{ + return m_xRange.is() || m_oIndex.has_value(); +} + +// RedlineInfo: temporary storage for redline data +class RedlineInfo +{ +public: + RedlineInfo(); + ~RedlineInfo(); + + // redline type (insert, delete, ...) + RedlineType eType; + + // info fields: + OUString sAuthor; // change author string + OUString sComment; // change comment string + util::DateTime aDateTime; // change DateTime + OUString sMovedID; // change move id string + bool bMergeLastParagraph; // the SwRangeRedline::IsDelLastPara flag + + // each position can may be either empty, an XTextRange, or an SwNodeIndex + + // start pos of anchor (may be empty) + XTextRangeOrNodeIndexPosition aAnchorStart; + + // end pos of anchor (may be empty) + XTextRangeOrNodeIndexPosition aAnchorEnd; + + // index of content node (maybe NULL) + SwNodeIndex* pContentIndex; + + // next redline info (for hierarchical redlines) + RedlineInfo* pNextRedline; + + // store whether we expect an adjustment for this redline + bool bNeedsAdjustment; +}; + +RedlineInfo::RedlineInfo() : + eType(RedlineType::Insert), + bMergeLastParagraph( false ), + pContentIndex(nullptr), + pNextRedline(nullptr), + bNeedsAdjustment( false ) +{ +} + +RedlineInfo::~RedlineInfo() +{ + delete pContentIndex; + delete pNextRedline; +} + +constexpr OUString g_sShowChanges = u"ShowChanges"_ustr; +constexpr OUString g_sRecordChanges = u"RecordChanges"_ustr; +constexpr OUString g_sRedlineProtectionKey = u"RedlineProtectionKey"_ustr; + +XMLRedlineImportHelper::XMLRedlineImportHelper( + SwXMLImport & rImport, + bool bNoRedlinesPlease, + const Reference<XPropertySet> & rModel, + const Reference<XPropertySet> & rImportInfo ) + : m_rImport(rImport), + m_sInsertion( GetXMLToken( XML_INSERTION )), + m_sDeletion( GetXMLToken( XML_DELETION )), + m_sFormatChange( GetXMLToken( XML_FORMAT_CHANGE )), + m_bIgnoreRedlines(bNoRedlinesPlease), + m_xModelPropertySet(rModel), + m_xImportInfoPropertySet(rImportInfo) +{ + // check to see if redline mode is handled outside of component + bool bHandleShowChanges = true; + bool bHandleRecordChanges = true; + bool bHandleProtectionKey = true; + if ( m_xImportInfoPropertySet.is() ) + { + Reference<XPropertySetInfo> xInfo = + m_xImportInfoPropertySet->getPropertySetInfo(); + + bHandleShowChanges = ! xInfo->hasPropertyByName( g_sShowChanges ); + bHandleRecordChanges = ! xInfo->hasPropertyByName( g_sRecordChanges ); + bHandleProtectionKey = ! xInfo->hasPropertyByName( g_sRedlineProtectionKey ); + } + + // get redline mode + m_bShowChanges = *o3tl::doAccess<bool>( + ( bHandleShowChanges ? m_xModelPropertySet : m_xImportInfoPropertySet ) + ->getPropertyValue( g_sShowChanges )); + m_bRecordChanges = *o3tl::doAccess<bool>( + ( bHandleRecordChanges ? m_xModelPropertySet : m_xImportInfoPropertySet ) + ->getPropertyValue( g_sRecordChanges )); + { + Any aAny = (bHandleProtectionKey ? m_xModelPropertySet + : m_xImportInfoPropertySet ) + ->getPropertyValue( g_sRedlineProtectionKey ); + aAny >>= m_aProtectionKey; + } + + // set redline mode to "don't record changes" + if( bHandleRecordChanges ) + { + m_xModelPropertySet->setPropertyValue( g_sRecordChanges, Any(false) ); + } +} + +XMLRedlineImportHelper::~XMLRedlineImportHelper() +{ + // delete all left over (and obviously incomplete) RedlineInfos (and map) + for( const auto& rEntry : m_aRedlineMap ) + { + RedlineInfo* pInfo = rEntry.second; + + // left-over redlines. Insert them if possible (but assert), + // and delete the incomplete ones. Finally, delete it. + if( IsReady(pInfo) ) + { + OSL_FAIL("forgotten RedlineInfo; now inserted"); + InsertIntoDocument( pInfo ); + } + else + { + // try if only the adjustment was missing + pInfo->bNeedsAdjustment = false; + if( IsReady(pInfo) ) + { + OSL_FAIL("RedlineInfo without adjustment; now inserted"); + InsertIntoDocument( pInfo ); + } + else + { + // this situation occurs if redlines aren't closed + // (i.e. end without start, or start without + // end). This may well be a problem in the file, + // rather than the code. + OSL_FAIL("incomplete redline (maybe file was corrupt); " + "now deleted"); + } + } + delete pInfo; + } + m_aRedlineMap.clear(); + + // set redline mode, either to info property set, or directly to + // the document + bool bHandleShowChanges = true; + bool bHandleRecordChanges = true; + bool bHandleProtectionKey = true; + if ( m_xImportInfoPropertySet.is() ) + { + Reference<XPropertySetInfo> xInfo = + m_xImportInfoPropertySet->getPropertySetInfo(); + + bHandleShowChanges = ! xInfo->hasPropertyByName( g_sShowChanges ); + bHandleRecordChanges = ! xInfo->hasPropertyByName( g_sRecordChanges ); + bHandleProtectionKey = ! xInfo->hasPropertyByName( g_sRedlineProtectionKey ); + } + + // set redline mode & key + try + { + Any aAny; + + aAny <<= m_bShowChanges; + if ( bHandleShowChanges ) + { + aAny <<= true; + m_xModelPropertySet->setPropertyValue( g_sShowChanges, aAny ); + // TODO maybe we need some property for the view-setting? + SwDoc *const pDoc(m_rImport.getDoc()); + assert(pDoc); + pDoc->GetDocumentRedlineManager().SetHideRedlines(!m_bShowChanges); + } + else + m_xImportInfoPropertySet->setPropertyValue( g_sShowChanges, aAny ); + + aAny <<= m_bRecordChanges; + if ( bHandleRecordChanges ) + m_xModelPropertySet->setPropertyValue( g_sRecordChanges, aAny ); + else + m_xImportInfoPropertySet->setPropertyValue( g_sRecordChanges, aAny ); + + aAny <<= m_aProtectionKey; + if ( bHandleProtectionKey ) + m_xModelPropertySet->setPropertyValue( g_sRedlineProtectionKey, aAny ); + else + m_xImportInfoPropertySet->setPropertyValue( g_sRedlineProtectionKey, aAny); + } + catch (const uno::RuntimeException &) // fdo#65882 + { + SAL_WARN( "sw", "potentially benign ordering issue during shutdown" ); + } +} + +void XMLRedlineImportHelper::Add( + std::u16string_view rType, + const OUString& rId, + const OUString& rAuthor, + const OUString& rComment, + const util::DateTime& rDateTime, + const OUString& rMovedID, + bool bMergeLastPara) +{ + // we need to do the following: + // 1) parse type string + // 2) create RedlineInfo and fill it with data + // 3) check for existing redline with same ID + // 3a) insert redline into map + // 3b) attach to existing redline + + // ad 1) + RedlineType eType; + if (rType == m_sInsertion) + { + eType = RedlineType::Insert; + } + else if (rType == m_sDeletion) + { + eType = RedlineType::Delete; + } + else if (rType == m_sFormatChange) + { + eType = RedlineType::Format; + } + else + { + // no proper type found: early out! + return; + } + + // ad 2) create a new RedlineInfo + RedlineInfo* pInfo = new RedlineInfo(); + + // fill entries + pInfo->eType = eType; + pInfo->sAuthor = rAuthor; + pInfo->sComment = rComment; + pInfo->aDateTime = rDateTime; + pInfo->sMovedID = rMovedID; + pInfo->bMergeLastParagraph = bMergeLastPara; + + //reserve MoveID so it won't be reused by others + if (!rMovedID.isEmpty()) + { + SwDoc* const pDoc(m_rImport.getDoc()); + assert(pDoc); + pDoc->GetDocumentRedlineManager().GetRedlineTable().setMovedIDIfNeeded(rMovedID.toInt32()); + } + + // ad 3) + auto itPair = m_aRedlineMap.emplace(rId, pInfo); + if (itPair.second) + return; + + // 3b) we already have a redline with this name: hierarchical redlines + // insert pInfo as last element in the chain. + // (hierarchy sanity checking happens on inserting into the document) + + // find last element + RedlineInfo* pInfoChain; + for( pInfoChain = itPair.first->second; + nullptr != pInfoChain->pNextRedline; + pInfoChain = pInfoChain->pNextRedline) ; // empty loop + + // insert as last element + pInfoChain->pNextRedline = pInfo; +} + +Reference<XTextCursor> XMLRedlineImportHelper::CreateRedlineTextSection( + Reference<XTextCursor> const & xOldCursor, + const OUString& rId) +{ + Reference<XTextCursor> xReturn; + + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + // get RedlineInfo + RedlineMapType::iterator aFind = m_aRedlineMap.find(rId); + if (m_aRedlineMap.end() != aFind) + { + // get document from old cursor (via tunnel) + SwDoc* pDoc = lcl_GetDocViaTunnel(xOldCursor); + + if (!pDoc) + { + SAL_WARN("sw", "no SwDoc => cannot create section."); + return nullptr; + } + + // create text section for redline + SwTextFormatColl *pColl = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool + (RES_POOLCOLL_STANDARD, false ); + SwStartNode* pRedlineNode = pDoc->GetNodes().MakeTextSection( + pDoc->GetNodes().GetEndOfRedlines(), + SwNormalStartNode, + pColl); + + // remember node-index in RedlineInfo + SwNodeIndex aIndex(*pRedlineNode); + aFind->second->pContentIndex = new SwNodeIndex(aIndex); + + // create XText for document + rtl::Reference<SwXRedlineText> pXText = new SwXRedlineText(pDoc, aIndex); + + // create (UNO-) cursor + SwPosition aPos(*pRedlineNode); + rtl::Reference<SwXTextCursor> pXCursor = + new SwXTextCursor(*pDoc, pXText, CursorType::Redline, aPos); + pXCursor->GetCursor().Move(fnMoveForward, GoInNode); + // cast to avoid ambiguity + xReturn = static_cast<text::XWordCursor*>(pXCursor.get()); + } + // else: unknown redline -> Ignore + + return xReturn; +} + +void XMLRedlineImportHelper::SetCursor( + const OUString& rId, + bool bStart, + Reference<XTextRange> const & rRange, + bool bIsOutsideOfParagraph) +{ + RedlineMapType::iterator aFind = m_aRedlineMap.find(rId); + if (m_aRedlineMap.end() == aFind) + return; + + // RedlineInfo found; now set Cursor + RedlineInfo* pInfo = aFind->second; + if (bIsOutsideOfParagraph) + { + // outside of paragraph: remember SwNodeIndex + if (bStart) + { + pInfo->aAnchorStart.SetAsNodeIndex(rRange); + } + else + { + pInfo->aAnchorEnd.SetAsNodeIndex(rRange); + } + + // also remember that we expect an adjustment for this redline + pInfo->bNeedsAdjustment = true; + } + else + { + // inside of a paragraph: use regular XTextRanges (bookmarks) + if (bStart) + pInfo->aAnchorStart.Set(rRange); + else + pInfo->aAnchorEnd.Set(rRange); + } + + // if this Cursor was the last missing info, we insert the + // node into the document + // then we can remove the entry from the map and destroy the object + if (IsReady(pInfo)) + { + InsertIntoDocument(pInfo); + m_aRedlineMap.erase(rId); + delete pInfo; + } + // else: unknown Id -> ignore +} + +void XMLRedlineImportHelper::AdjustStartNodeCursor( + const OUString& rId) /// ID used in RedlineAdd() call +{ + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + // start + end nodes are treated the same. For either it's + // necessary that the target node already exists. + + RedlineMapType::iterator aFind = m_aRedlineMap.find(rId); + if (m_aRedlineMap.end() == aFind) + return; + + // RedlineInfo found; now set Cursor + RedlineInfo* pInfo = aFind->second; + + pInfo->bNeedsAdjustment = false; + + // if now ready, insert into document + if( IsReady(pInfo) ) + { + InsertIntoDocument(pInfo); + m_aRedlineMap.erase(rId); + delete pInfo; + } + // else: can't find redline -> ignore +} + +inline bool XMLRedlineImportHelper::IsReady(const RedlineInfo* pRedline) +{ + // we can insert a redline if we have start & end, and we don't + // expect adjustments for either of these + return ( pRedline->aAnchorEnd.IsValid() && + pRedline->aAnchorStart.IsValid() && + !pRedline->bNeedsAdjustment ); +} + +/// recursively check if rPos or its anchor (if in fly or footnote) is in redline section +static auto RecursiveContains(SwStartNode const& rRedlineSection, SwNode const& rPos) -> bool +{ + if (rRedlineSection.GetIndex() <= rPos.GetIndex() + && rPos.GetIndex() <= rRedlineSection.EndOfSectionIndex()) + { + return true; + } + // loop to iterate "up" in the node tree and find an anchored XText + for (SwStartNode const* pStartNode = rPos.StartOfSectionNode(); + pStartNode != nullptr && pStartNode->GetIndex() != SwNodeOffset(0); + pStartNode = pStartNode->StartOfSectionNode()) + { + switch (pStartNode->GetStartNodeType()) + { + case SwNormalStartNode: + case SwTableBoxStartNode: + continue; + break; + case SwFlyStartNode: + { + SwFrameFormat const*const pFormat(pStartNode->GetFlyFormat()); + assert(pFormat); + SwFormatAnchor const& rAnchor(pFormat->GetAnchor()); + if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE) + { + return false; + } + else if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_FLY) + { // anchor is on a start node, avoid skipping it: + pStartNode = rAnchor.GetAnchorNode()->GetStartNode(); + assert(pStartNode); + // pass the next node to recursive call - it will call + // call StartOfSectionNode on it and go back to pStartNode + SwNodeIndex const next(*pStartNode, +1); + return RecursiveContains(rRedlineSection, next.GetNode()); + } + else + { + return RecursiveContains(rRedlineSection, *rAnchor.GetAnchorNode()); + } + } + break; + case SwFootnoteStartNode: + { // sigh ... need to search + for (SwTextFootnote const*const pFootnote : rRedlineSection.GetDoc().GetFootnoteIdxs()) + { + if (pStartNode == pFootnote->GetStartNode()->GetNode().GetStartNode()) + { + return RecursiveContains(rRedlineSection, pFootnote->GetTextNode()); + } + } + assert(false); + } + break; + case SwHeaderStartNode: + case SwFooterStartNode: + return false; // headers aren't anchored + break; + default: + assert(false); + break; + } + } + return false; +} + +void XMLRedlineImportHelper::InsertIntoDocument(RedlineInfo* pRedlineInfo) +{ + OSL_ENSURE(nullptr != pRedlineInfo, "need redline info"); + OSL_ENSURE(IsReady(pRedlineInfo), "redline info not complete yet!"); + + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + // Insert the Redline as described by pRedlineInfo into the + // document. If we are in insert mode, don't insert any redlines + // (and delete 'deleted' inline redlines) + + // get the document (from one of the positions) + SwDoc* pDoc = pRedlineInfo->aAnchorStart.GetDoc(); + + if (!pDoc) + { + SAL_WARN("sw", "no SwDoc => cannot insert redline."); + return; + } + + // now create the PaM for the redline + SwPaM aPaM(pDoc->GetNodes().GetEndOfContent()); + pRedlineInfo->aAnchorStart.CopyPositionInto(*aPaM.GetPoint(), *pDoc); + aPaM.SetMark(); + pRedlineInfo->aAnchorEnd.CopyPositionInto(*aPaM.GetPoint(), *pDoc); + + // collapse PaM if (start == end) + if (*aPaM.GetPoint() == *aPaM.GetMark()) + { + aPaM.DeleteMark(); + } + + // cover three cases: + // 1) empty redlines (no range, no content) + // 2) check for: + // a) bIgnoreRedline (e.g. insert mode) + // b) illegal PaM range (CheckNodesRange()) + // c) redline with empty content section (quite useless) + // 3) normal case: insert redline + SwTextNode const* pTempNode(nullptr); + if( !aPaM.HasMark() && (pRedlineInfo->pContentIndex == nullptr) ) + { + // these redlines have no function, and will thus be ignored (just as + // in sw3io), so no action here + } + else if ( m_bIgnoreRedlines || + !CheckNodesRange( aPaM.GetPoint()->GetNode(), + aPaM.GetMark()->GetNode(), + true ) + || (pRedlineInfo->pContentIndex + && (pRedlineInfo->pContentIndex->GetIndex() + 2 + == pRedlineInfo->pContentIndex->GetNode().EndOfSectionIndex()) + && (pTempNode = pDoc->GetNodes()[pRedlineInfo->pContentIndex->GetIndex() + 1]->GetTextNode()) != nullptr + && pTempNode->GetText().isEmpty() + && !pTempNode->GetpSwpHints() + && pTempNode->GetAnchoredFlys().empty())) + { + // ignore redline (e.g. file loaded in insert mode): + // delete 'deleted' redlines and forget about the whole thing + if (RedlineType::Delete == pRedlineInfo->eType) + { + pDoc->getIDocumentContentOperations().DeleteRange(aPaM); + // And what about the "deleted nodes"? + // They have to be deleted as well (#i80689)! + if( m_bIgnoreRedlines && pRedlineInfo->pContentIndex != nullptr ) + { + const SwNodeIndex& rIdx( *pRedlineInfo->pContentIndex ); + const SwNode* pEnd = rIdx.GetNode().EndOfSectionNode(); + if( pEnd ) + { + SwPaM aDel( rIdx.GetNode(), 0, *pEnd, 1 ); + pDoc->getIDocumentContentOperations().DeleteRange(aDel); + } + } + } + } + else if (pRedlineInfo->pContentIndex != nullptr + // should be enough to check 1 position of aPaM bc CheckNodesRange() above + && RecursiveContains(*pRedlineInfo->pContentIndex->GetNode().GetStartNode(), aPaM.GetPoint()->GetNode())) + { + SAL_WARN("sw.xml", "Recursive change tracking, removing"); + // reuse aPaM to remove it from nodes that will be deleted + aPaM.GetPoint()->Assign(pRedlineInfo->pContentIndex->GetNode()); + aPaM.DeleteMark(); + pDoc->getIDocumentContentOperations().DeleteSection(&aPaM.GetPoint()->GetNode()); + } + else + { + // regular file loading: insert redline + + // create redline (using pRedlineData which gets copied in SwRangeRedline()) + SwRedlineData* pRedlineData = ConvertRedline(pRedlineInfo, pDoc); + SwRangeRedline* pRedline = + new SwRangeRedline( pRedlineData, *aPaM.GetPoint(), + !pRedlineInfo->bMergeLastParagraph ); + + // tdf#107292 fix order of delete redlines at the same position by removing + // the already inserted redlines temporarily and inserting them back in reverse + // order after inserting pRedline + std::vector<const SwRangeRedline*> aSwapRedlines; + if ( RedlineType::Delete == pRedlineInfo->eType ) + { + SwRedlineTable::size_type n = 0; + while ( const SwRangeRedline* pRedline2 = + pDoc->getIDocumentRedlineAccess().GetRedline( *pRedline->Start(), &n ) ) + { + SwRedlineTable& aRedlineTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable(); + aSwapRedlines.push_back(pRedline2); + aRedlineTable.Remove(n); + } + } + + // set mark + if( aPaM.HasMark() ) + { + pRedline->SetMark(); + *(pRedline->GetMark()) = *aPaM.GetMark(); + } + + // set content node (if necessary) + if (nullptr != pRedlineInfo->pContentIndex) + { + SwNodeOffset nPoint = aPaM.GetPoint()->GetNodeIndex(); + if( nPoint < pRedlineInfo->pContentIndex->GetIndex() || + nPoint > pRedlineInfo->pContentIndex->GetNode().EndOfSectionIndex() ) + pRedline->SetContentIdx(*pRedlineInfo->pContentIndex); + else + SAL_WARN("sw", "Recursive change tracking"); + } + + // set redline mode (without doing the associated book-keeping) + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::On); + pDoc->getIDocumentRedlineAccess().AppendRedline(pRedline, false); + + // restore the correct order of the delete redlines at the same position + for (auto i = aSwapRedlines.rbegin(); i != aSwapRedlines.rend(); ++i) + pDoc->getIDocumentRedlineAccess().AppendRedline(const_cast<SwRangeRedline*>(*i), false); + + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::NONE); + } +} + +SwRedlineData* XMLRedlineImportHelper::ConvertRedline( + RedlineInfo* pRedlineInfo, + SwDoc* pDoc) +{ + // convert info: + // 1) Author String -> Author ID (default to zero) + std::size_t nAuthorId = (nullptr == pDoc) ? 0 : + pDoc->getIDocumentRedlineAccess().InsertRedlineAuthor( pRedlineInfo->sAuthor ); + + // 2) util::DateTime -> DateTime + DateTime aDT( DateTime::EMPTY ); + aDT.SetYear( pRedlineInfo->aDateTime.Year ); + aDT.SetMonth( pRedlineInfo->aDateTime.Month ); + aDT.SetDay( pRedlineInfo->aDateTime.Day ); + aDT.SetHour( pRedlineInfo->aDateTime.Hours ); + aDT.SetMin( pRedlineInfo->aDateTime.Minutes ); + aDT.SetSec( pRedlineInfo->aDateTime.Seconds ); + aDT.SetNanoSec( pRedlineInfo->aDateTime.NanoSeconds ); + + sal_uInt32 nMovedID = pRedlineInfo->sMovedID.toInt32(); + + // 3) recursively convert next redline + // ( check presence and sanity of hierarchical redline info ) + SwRedlineData* pNext = nullptr; + if ( (nullptr != pRedlineInfo->pNextRedline) && + (RedlineType::Delete == pRedlineInfo->eType) && + (RedlineType::Insert == pRedlineInfo->pNextRedline->eType) ) + { + pNext = ConvertRedline(pRedlineInfo->pNextRedline, pDoc); + } + + // create redline data + SwRedlineData* pData = new SwRedlineData(pRedlineInfo->eType, + nAuthorId, aDT, nMovedID, + pRedlineInfo->sComment, + pNext); // next data (if available) + + return pData; +} + +void XMLRedlineImportHelper::SetShowChanges( bool bShow ) +{ + m_bShowChanges = bShow; +} + +void XMLRedlineImportHelper::SetRecordChanges( bool bRecord ) +{ + m_bRecordChanges = bRecord; +} + +void XMLRedlineImportHelper::SetProtectionKey( + const Sequence<sal_Int8> & rKey ) +{ + m_aProtectionKey = rKey; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/XMLRedlineImportHelper.hxx b/sw/source/filter/xml/XMLRedlineImportHelper.hxx new file mode 100644 index 0000000000..8a15f53dfb --- /dev/null +++ b/sw/source/filter/xml/XMLRedlineImportHelper.hxx @@ -0,0 +1,134 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLREDLINEIMPORTHELPER_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLREDLINEIMPORTHELPER_HXX + +#include <rtl/ustring.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/uno/Sequence.h> +#include <com/sun/star/util/DateTime.hpp> +#include <redline.hxx> + +#include <map> + +class SvXMLImport; +class RedlineInfo; +class SwRedlineData; +class SwDoc; +class SwXMLImport; +namespace com::sun::star { + namespace text { class XTextCursor; } + namespace text { class XTextRange; } + namespace frame { class XModel; } +} + +typedef std::map< OUString, RedlineInfo* > RedlineMapType; + +class XMLRedlineImportHelper final +{ + SwXMLImport & m_rImport; + + const OUString m_sInsertion; + const OUString m_sDeletion; + const OUString m_sFormatChange; + + RedlineMapType m_aRedlineMap; + + // if true, no redlines should be inserted into document + // (This typically happen when a document is loaded in 'insert'-mode.) + bool m_bIgnoreRedlines; + + // save information for saving and reconstruction of the redline mode + css::uno::Reference<css::beans::XPropertySet> m_xModelPropertySet; + css::uno::Reference<css::beans::XPropertySet> m_xImportInfoPropertySet; + bool m_bShowChanges; + bool m_bRecordChanges; + css::uno::Sequence<sal_Int8> m_aProtectionKey; + +public: + + XMLRedlineImportHelper( + SwXMLImport & rImport, + bool bIgnoreRedlines, // ignore redlines mode + // property sets of model + import info for saving + restoring the + // redline mode + const css::uno::Reference<css::beans::XPropertySet> & rModel, + const css::uno::Reference<css::beans::XPropertySet> & rImportInfoSet ); + ~XMLRedlineImportHelper(); + + // create a redline object + // (The redline will be inserted into the document after both start + // and end cursor has been set.) + void Add( + std::u16string_view rType, // redline type (insert, del,... ) + const OUString& rId, // use to identify this redline + const OUString& rAuthor, // name of the author + const OUString& rComment, // redline comment + const css::util::DateTime& rDateTime, // date+time + const OUString& rMovedID, // redline move id + bool bMergeLastParagraph); // merge last paragraph? + + // create a text section for the redline, and return an + // XText/XTextCursor that may be used to write into it. + css::uno::Reference<css::text::XTextCursor> CreateRedlineTextSection( + css::uno::Reference<css::text::XTextCursor> const & xOldCursor, // needed to get the document + const OUString& rId); // ID used to RedlineAdd() call + + // Set start or end position for a redline in the text body. + // Accepts XTextRange objects. + void SetCursor( + const OUString& rId, // ID used in RedlineAdd() call + bool bStart, // start or end Range + css::uno::Reference<css::text::XTextRange> const & rRange, // the actual XTextRange + // text range is (from an XML view) outside of a paragraph + // (i.e. before a table) + bool bIsOutsideOfParagraph); + + /** + * Adjust the start (end) position for a redline that begins in a + * start node. It takes the cursor positions _inside_ the redlined + * element (e.g. section or table). + */ + void AdjustStartNodeCursor( + const OUString& rId); // ID used in RedlineAdd() call + + // set redline mode: show changes + void SetShowChanges( bool bShowChanges ); + + // set redline mode: record changes + void SetRecordChanges( bool bRecordChanges ); + + // set redline protection key + void SetProtectionKey(const css::uno::Sequence<sal_Int8> & rKey ); + +private: + + static inline bool IsReady(const RedlineInfo* pRedline); + + void InsertIntoDocument(RedlineInfo* pRedline); + + SwRedlineData* ConvertRedline( + RedlineInfo* pRedline, // RedlineInfo to be converted + SwDoc* pDoc); // document needed for Author-ID conversion +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/swxml.cxx b/sw/source/filter/xml/swxml.cxx new file mode 100644 index 0000000000..16ae089113 --- /dev/null +++ b/sw/source/filter/xml/swxml.cxx @@ -0,0 +1,1017 @@ +/* -*- 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 <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/xml/sax/InputSource.hpp> +#include <com/sun/star/xml/sax/Parser.hpp> +#include <com/sun/star/xml/sax/SAXParseException.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/document/NamedPropertyValues.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/packages/zip/ZipIOException.hpp> +#include <com/sun/star/packages/WrongPasswordException.hpp> +#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <o3tl/any.hxx> +#include <vcl/errinf.hxx> +#include <sfx2/docfile.hxx> +#include <svtools/sfxecode.hxx> +#include <svl/stritem.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <svx/xmlgrhlp.hxx> +#include <svx/xmleohlp.hxx> +#include <comphelper/fileformat.h> +#include <comphelper/genericpropertyset.hxx> +#include <comphelper/propertysetinfo.hxx> +#include <sal/log.hxx> +#include <sfx2/frame.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <swerror.h> +#include <fltini.hxx> +#include <drawdoc.hxx> +#include <doc.hxx> +#include <docfunc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <DocumentRedlineManager.hxx> +#include <docary.hxx> +#include <docsh.hxx> +#include <unotextrange.hxx> +#include <SwXMLSectionList.hxx> + +#include <SwStyleNameMapper.hxx> +#include <poolfmt.hxx> +#include <numrule.hxx> +#include <paratr.hxx> +#include <fmtrowsplt.hxx> + +#include <svx/svdpage.hxx> +#include <svx/svditer.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdograf.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/sfxsids.hrc> +#include <istyleaccess.hxx> + +#include <sfx2/DocumentMetadataAccess.hxx> +#include <comphelper/diagnose_ex.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::lang; + +static void lcl_EnsureValidPam( SwPaM& rPam ) +{ + if( rPam.GetPointContentNode() != nullptr ) + { + // set proper point content + if( rPam.GetPointContentNode() != rPam.GetPoint()->GetContentNode() ) + { + rPam.GetPoint()->nContent.Assign( rPam.GetPointContentNode(), 0 ); + } + // else: point was already valid + + // if mark is invalid, we delete it + if( ( rPam.GetMarkContentNode() == nullptr ) || + ( rPam.GetMarkContentNode() != rPam.GetMark()->GetContentNode() ) ) + { + rPam.DeleteMark(); + } + } + else + { + // point is not valid, so move it into the first content + rPam.DeleteMark(); + rPam.GetPoint()->Assign( + *rPam.GetDoc().GetNodes().GetEndOfContent().StartOfSectionNode() ); + rPam.GetPoint()->Adjust(SwNodeOffset(+1)); + rPam.Move( fnMoveForward, GoInContent ); // go into content + } +} + +XMLReader::XMLReader() +{ +} + +SwReaderType XMLReader::GetReaderType() +{ + return SwReaderType::Storage; +} + +namespace +{ + +/// read a component (file + filter version) +ErrCodeMsg ReadThroughComponent( + uno::Reference<io::XInputStream> const & xInputStream, + uno::Reference<XComponent> const & xModelComponent, + const OUString& rStreamName, + uno::Reference<uno::XComponentContext> const & rxContext, + const char* pFilterName, + const Sequence<Any>& rFilterArguments, + const OUString& rName, + bool bMustBeSuccessful, + bool bEncrypted ) +{ + OSL_ENSURE(xInputStream.is(), "input stream missing"); + OSL_ENSURE(xModelComponent.is(), "document missing"); + OSL_ENSURE(rxContext.is(), "factory missing"); + OSL_ENSURE(nullptr != pFilterName,"I need a service name for the component!"); + + // prepare ParserInputSource + xml::sax::InputSource aParserInput; + aParserInput.sSystemId = rName; + aParserInput.aInputStream = xInputStream; + + // get filter + const OUString aFilterName(OUString::createFromAscii(pFilterName)); + uno::Reference< XInterface > xFilter = + rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(aFilterName, rFilterArguments, rxContext); + SAL_WARN_IF(!xFilter.is(), "sw.filter", "Can't instantiate filter component: " << aFilterName); + if( !xFilter.is() ) + return ERR_SWG_READ_ERROR; + // the underlying SvXMLImport implements XFastParser, XImporter, XFastDocumentHandler + uno::Reference< xml::sax::XFastParser > xFastParser(xFilter, UNO_QUERY); + uno::Reference< xml::sax::XDocumentHandler > xDocumentHandler; + if (!xFastParser) + xDocumentHandler.set(xFilter, UNO_QUERY); + if (!xDocumentHandler && !xFastParser) + { + SAL_WARN("sd", "service does not implement XFastParser or XDocumentHandler"); + assert(false); + return ERR_SWG_READ_ERROR; + } + + // connect model and filter + uno::Reference < XImporter > xImporter( xFilter, UNO_QUERY ); + xImporter->setTargetDocument( xModelComponent ); + + // finally, parse the stream + try + { + if (xFastParser) + xFastParser->parseStream( aParserInput ); + else + { + uno::Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(rxContext); + // connect parser and filter + xParser->setDocumentHandler( xDocumentHandler ); + xParser->parseStream( aParserInput ); + } + } + catch( xml::sax::SAXParseException& r) + { + css::uno::Any ex( cppu::getCaughtException() ); + // sax parser sends wrapped exceptions, + // try to find the original one + xml::sax::SAXException aSaxEx = *static_cast<xml::sax::SAXException*>(&r); + bool bTryChild = true; + + while( bTryChild ) + { + xml::sax::SAXException aTmp; + if ( aSaxEx.WrappedException >>= aTmp ) + aSaxEx = aTmp; + else + bTryChild = false; + } + + packages::zip::ZipIOException aBrokenPackage; + if ( aSaxEx.WrappedException >>= aBrokenPackage ) + return ERRCODE_IO_BROKENPACKAGE; + + if( bEncrypted ) + return ERRCODE_SFX_WRONGPASSWORD; + + SAL_WARN( "sw", "SAX parse exception caught while importing: " << exceptionToString(ex) ); + + const OUString sErr( OUString::number( r.LineNumber ) + + "," + + OUString::number( r.ColumnNumber ) ); + + if( !rStreamName.isEmpty() ) + { + return ErrCodeMsg( + (bMustBeSuccessful ? ERR_FORMAT_FILE_ROWCOL + : WARN_FORMAT_FILE_ROWCOL), + rStreamName, sErr, + DialogMask::ButtonsOk | DialogMask::MessageError ); + } + else + { + OSL_ENSURE( bMustBeSuccessful, "Warnings are not supported" ); + return ErrCodeMsg( ERR_FORMAT_ROWCOL, sErr, + DialogMask::ButtonsOk | DialogMask::MessageError ); + } + } + catch(const xml::sax::SAXException& r) + { + css::uno::Any ex( cppu::getCaughtException() ); + packages::zip::ZipIOException aBrokenPackage; + if ( r.WrappedException >>= aBrokenPackage ) + return ERRCODE_IO_BROKENPACKAGE; + + if( bEncrypted ) + return ERRCODE_SFX_WRONGPASSWORD; + + SAL_WARN( "sw", "uno exception caught while importing: " << exceptionToString(ex) ); + return ERR_SWG_READ_ERROR; + } + catch(const packages::zip::ZipIOException&) + { + TOOLS_WARN_EXCEPTION( "sw", "uno exception caught while importing" ); + return ERRCODE_IO_BROKENPACKAGE; + } + catch(const io::IOException&) + { + TOOLS_WARN_EXCEPTION( "sw", "uno exception caught while importing" ); + return ERR_SWG_READ_ERROR; + } + catch(const uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sw", "uno exception caught while importing" ); + return ERR_SWG_READ_ERROR; + } + + // success! + return ERRCODE_NONE; +} + +// read a component (storage version) +ErrCodeMsg ReadThroughComponent( + uno::Reference<embed::XStorage> const & xStorage, + uno::Reference<XComponent> const & xModelComponent, + const char* pStreamName, + uno::Reference<uno::XComponentContext> const & rxContext, + const char* pFilterName, + const Sequence<Any>& rFilterArguments, + const OUString& rName, + bool bMustBeSuccessful) +{ + OSL_ENSURE(xStorage.is(), "Need storage!"); + OSL_ENSURE(nullptr != pStreamName, "Please, please, give me a name!"); + + // open stream (and set parser input) + OUString sStreamName = OUString::createFromAscii(pStreamName); + bool bContainsStream = false; + try + { + bContainsStream = xStorage->isStreamElement(sStreamName); + } + catch( container::NoSuchElementException& ) + { + } + + if (!bContainsStream ) + { + // stream name not found! return immediately with OK signal + return ERRCODE_NONE; + } + + // set Base URL + uno::Reference< beans::XPropertySet > xInfoSet; + if( rFilterArguments.hasElements() ) + rFilterArguments.getConstArray()[0] >>= xInfoSet; + OSL_ENSURE( xInfoSet.is(), "missing property set" ); + if( xInfoSet.is() ) + { + xInfoSet->setPropertyValue( "StreamName", Any( sStreamName ) ); + } + + try + { + // get input stream + uno::Reference <io::XStream> xStream = xStorage->openStreamElement( sStreamName, embed::ElementModes::READ ); + uno::Reference <beans::XPropertySet > xProps( xStream, uno::UNO_QUERY ); + + Any aAny = xProps->getPropertyValue("Encrypted"); + + std::optional<const bool> b = o3tl::tryAccess<bool>(aAny); + bool bEncrypted = b.has_value() && *b; + + uno::Reference <io::XInputStream> xInputStream = xStream->getInputStream(); + + // read from the stream + return ReadThroughComponent( + xInputStream, xModelComponent, sStreamName, rxContext, + pFilterName, rFilterArguments, + rName, bMustBeSuccessful, bEncrypted ); + } + catch ( packages::WrongPasswordException& ) + { + return ERRCODE_SFX_WRONGPASSWORD; + } + catch( packages::zip::ZipIOException& ) + { + return ERRCODE_IO_BROKENPACKAGE; + } + catch ( uno::Exception& ) + { + OSL_FAIL( "Error on import" ); + // TODO/LATER: error handling + } + + return ERR_SWG_READ_ERROR; +} + +} + +// #i44177# +static void lcl_AdjustOutlineStylesForOOo(SwDoc& _rDoc) +{ + // array containing the names of the default outline styles ('Heading 1', + // 'Heading 2', ..., 'Heading 10') + OUString aDefOutlStyleNames[ MAXLEVEL ]; + { + OUString sStyleName; + for ( sal_uInt8 i = 0; i < MAXLEVEL; ++i ) + { + sStyleName = + SwStyleNameMapper::GetProgName( RES_POOLCOLL_HEADLINE1 + i, + sStyleName ); + aDefOutlStyleNames[i] = sStyleName; + } + } + + // array indicating, which outline level already has a style assigned. + bool aOutlineLevelAssigned[ MAXLEVEL ]; + // array of the default outline styles, which are created for the document. + SwTextFormatColl* aCreatedDefaultOutlineStyles[ MAXLEVEL ]; + + { + for ( sal_uInt8 i = 0; i < MAXLEVEL; ++i ) + { + aOutlineLevelAssigned[ i ] = false; + aCreatedDefaultOutlineStyles[ i ] = nullptr; + } + } + + // determine, which outline level has already a style assigned and + // which of the default outline styles is created. + const SwTextFormatColls& rColls = *(_rDoc.GetTextFormatColls()); + for ( size_t n = 1; n < rColls.size(); ++n ) + { + SwTextFormatColl* pColl = rColls[ n ]; + if ( pColl->IsAssignedToListLevelOfOutlineStyle() ) + { + aOutlineLevelAssigned[ pColl->GetAssignedOutlineStyleLevel() ] = true; + } + + for ( sal_uInt8 i = 0; i < MAXLEVEL; ++i ) + { + if ( aCreatedDefaultOutlineStyles[ i ] == nullptr && + pColl->GetName() == aDefOutlStyleNames[i] ) + { + aCreatedDefaultOutlineStyles[ i ] = pColl; + break; + } + } + } + + // assign already created default outline style to outline level, which + // doesn't have a style assigned to it. + const SwNumRule* pOutlineRule = _rDoc.GetOutlineNumRule(); + for ( sal_uInt8 i = 0; i < MAXLEVEL; ++i ) + { + // #i73361# + // Do not change assignment of already created default outline style + // to a certain outline level. + if ( !aOutlineLevelAssigned[ i ] && + aCreatedDefaultOutlineStyles[ i ] != nullptr && + ! aCreatedDefaultOutlineStyles[ i ]->IsAssignedToListLevelOfOutlineStyle() ) + { + // apply outline level at created default outline style + aCreatedDefaultOutlineStyles[ i ]->AssignToListLevelOfOutlineStyle(i); + + // apply outline numbering rule, if none is set. + const SwNumRuleItem& rItem = + aCreatedDefaultOutlineStyles[ i ]->GetFormatAttr( RES_PARATR_NUMRULE, false ); + if ( rItem.GetValue().isEmpty() ) + { + SwNumRuleItem aItem( pOutlineRule->GetName() ); + aCreatedDefaultOutlineStyles[ i ]->SetFormatAttr( aItem ); + } + } + } +} + +static void lcl_ConvertSdrOle2ObjsToSdrGrafObjs(SwDoc& _rDoc) +{ + if ( !(_rDoc.getIDocumentDrawModelAccess().GetDrawModel() && + _rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 )) ) + return; + + const SdrPage& rSdrPage( *(_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 )) ); + + // iterate recursive with group objects over all shapes on the draw page + SdrObjListIter aIter( &rSdrPage ); + while( aIter.IsMore() ) + { + SdrOle2Obj* pOle2Obj = dynamic_cast< SdrOle2Obj* >( aIter.Next() ); + if( pOle2Obj ) + { + // found an ole2 shape + SdrObjList* pObjList = pOle2Obj->getParentSdrObjListFromSdrObject(); + + // get its graphic + Graphic aGraphic; + pOle2Obj->Connect(); + const Graphic* pGraphic = pOle2Obj->GetGraphic(); + if( pGraphic ) + aGraphic = *pGraphic; + pOle2Obj->Disconnect(); + + // create new graphic shape with the ole graphic and shape size + rtl::Reference<SdrGrafObj> pGraphicObj = new SdrGrafObj( + pOle2Obj->getSdrModelFromSdrObject(), + aGraphic, + pOle2Obj->GetCurrentBoundRect()); + + // apply layer of ole2 shape at graphic shape + pGraphicObj->SetLayer( pOle2Obj->GetLayer() ); + + // replace ole2 shape with the new graphic object and delete the ol2 shape + pObjList->ReplaceObject( pGraphicObj.get(), pOle2Obj->GetOrdNum() ); + } + } +} + +ErrCodeMsg XMLReader::Read( SwDoc &rDoc, const OUString& rBaseURL, SwPaM &rPaM, const OUString & rName ) +{ + // needed for relative URLs, but in clipboard copy/paste there may be none + // and also there is the SwXMLTextBlocks special case + SAL_INFO_IF(rBaseURL.isEmpty(), "sw.filter", "sw::XMLReader: no base URL"); + + // Get service factory + uno::Reference< uno::XComponentContext > xContext = + comphelper::getProcessComponentContext(); + + uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler; + rtl::Reference<SvXMLGraphicHelper> xGraphicHelper; + uno::Reference< document::XEmbeddedObjectResolver > xObjectResolver; + rtl::Reference<SvXMLEmbeddedObjectHelper> xObjectHelper; + + // get the input stream (storage or stream) + uno::Reference<embed::XStorage> xStorage; + if( m_pMedium ) + xStorage = m_pMedium->GetStorage(); + else + xStorage = m_xStorage; + + if( !xStorage.is() ) + return ERR_SWG_READ_ERROR; + + xGraphicHelper = SvXMLGraphicHelper::Create( xStorage, + SvXMLGraphicHelperMode::Read ); + xGraphicStorageHandler = xGraphicHelper.get(); + SfxObjectShell *pPersist = rDoc.GetPersist(); + if( pPersist ) + { + xObjectHelper = SvXMLEmbeddedObjectHelper::Create( + xStorage, *pPersist, + SvXMLEmbeddedObjectHelperMode::Read ); + xObjectResolver = xObjectHelper.get(); + } + + // Get the docshell, the model, and finally the model's component + SwDocShell *pDocSh = rDoc.GetDocShell(); + OSL_ENSURE( pDocSh, "XMLReader::Read: got no doc shell" ); + if( !pDocSh ) + return ERR_SWG_READ_ERROR; + uno::Reference< lang::XComponent > xModelComp = pDocSh->GetModel(); + OSL_ENSURE( xModelComp.is(), + "XMLReader::Read: got no model" ); + if( !xModelComp.is() ) + return ERR_SWG_READ_ERROR; + + // create and prepare the XPropertySet that gets passed through + // the components, and the XStatusIndicator that shows progress to + // the user. + + // create XPropertySet with three properties for status indicator + static comphelper::PropertyMapEntry const aInfoMap[] = + { + { OUString("ProgressRange"), 0, + ::cppu::UnoType<sal_Int32>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressMax"), 0, + ::cppu::UnoType<sal_Int32>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressCurrent"), 0, + ::cppu::UnoType<sal_Int32>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("NumberStyles"), 0, + cppu::UnoType<container::XNameContainer>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("RecordChanges"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("ShowChanges"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("RedlineProtectionKey"), 0, + cppu::UnoType<Sequence<sal_Int8>>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("PrivateData"), 0, + cppu::UnoType<XInterface>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("BaseURI"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamRelPath"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamName"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + // properties for insert modes + { OUString("StyleInsertModeFamilies"), 0, + cppu::UnoType<Sequence<OUString>>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StyleInsertModeOverwrite"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("TextInsertModeRange"), 0, + cppu::UnoType<text::XTextRange>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("AutoTextMode"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("OrganizerMode"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + + // #i28749# - Add property, which indicates, if the + // shape position attributes are given in horizontal left-to-right layout. + // This is the case for the OpenOffice.org file format. + { OUString("ShapePositionInHoriL2R"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + + { OUString("BuildId"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + + // Add property, which indicates, if a text document in OpenOffice.org + // file format is read. + // Note: Text documents read via the binary filter are also finally + // read using the OpenOffice.org file format. Thus, e.g. for text + // documents in StarOffice 5.2 binary file format this property + // will be true. + { OUString("TextDocInOOoFileFormat"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("SourceStorage"), 0, cppu::UnoType<embed::XStorage>::get(), + css::beans::PropertyAttribute::MAYBEVOID, 0 }, + }; + uno::Reference< beans::XPropertySet > xInfoSet( + comphelper::GenericPropertySet_CreateInstance( + new comphelper::PropertySetInfo( aInfoMap ) ) ); + + // get BuildId from parent container if available + uno::Reference< container::XChild > xChild( xModelComp, uno::UNO_QUERY ); + if( xChild.is() ) + { + uno::Reference< beans::XPropertySet > xParentSet( xChild->getParent(), uno::UNO_QUERY ); + if( xParentSet.is() ) + { + uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xParentSet->getPropertySetInfo() ); + static constexpr OUString sPropName(u"BuildId"_ustr ); + if( xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(sPropName) ) + { + xInfoSet->setPropertyValue( sPropName, xParentSet->getPropertyValue(sPropName) ); + } + } + } + + // try to get an XStatusIndicator from the Medium + uno::Reference<task::XStatusIndicator> xStatusIndicator; + + if (pDocSh->GetMedium()) + { + const SfxUnoAnyItem* pItem = + pDocSh->GetMedium()->GetItemSet().GetItem(SID_PROGRESS_STATUSBAR_CONTROL); + if (pItem) + { + pItem->GetValue() >>= xStatusIndicator; + } + } + + // set progress range and start status indicator + sal_Int32 nProgressRange(1000000); + if (xStatusIndicator.is()) + { + xStatusIndicator->start(SvxResId(RID_SVXSTR_DOC_LOAD), nProgressRange); + } + uno::Any aProgRange; + aProgRange <<= nProgressRange; + xInfoSet->setPropertyValue("ProgressRange", aProgRange); + + Reference< container::XNameAccess > xLateInitSettings( document::NamedPropertyValues::create(xContext), UNO_QUERY_THROW ); + beans::NamedValue aLateInitSettings( "LateInitSettings", Any( xLateInitSettings ) ); + + xInfoSet->setPropertyValue( "SourceStorage", Any( xStorage ) ); + + // prepare filter arguments, WARNING: the order is important! + Sequence<Any> aFilterArgs{ Any(xInfoSet), + Any(xStatusIndicator), + Any(xGraphicStorageHandler), + Any(xObjectResolver), + Any(aLateInitSettings) }; + + Sequence<Any> aEmptyArgs{ Any(xInfoSet), + Any(xStatusIndicator) }; + + // prepare for special modes + if( m_aOption.IsFormatsOnly() ) + { + sal_Int32 nCount = + (m_aOption.IsFrameFormats() ? 1 : 0) + + (m_aOption.IsPageDescs() ? 1 : 0) + + (m_aOption.IsTextFormats() ? 2 : 0) + + (m_aOption.IsNumRules() ? 1 : 0); + + Sequence< OUString> aFamiliesSeq( nCount ); + OUString *pSeq = aFamiliesSeq.getArray(); + if( m_aOption.IsFrameFormats() ) + // SfxStyleFamily::Frame; + *pSeq++ = "FrameStyles"; + if( m_aOption.IsPageDescs() ) + // SfxStyleFamily::Page; + *pSeq++ = "PageStyles"; + if( m_aOption.IsTextFormats() ) + { + // (SfxStyleFamily::Char|SfxStyleFamily::Para); + *pSeq++ = "CharacterStyles"; + *pSeq++ = "ParagraphStyles"; + } + if( m_aOption.IsNumRules() ) + // SfxStyleFamily::Pseudo; + *pSeq++ = "NumberingStyles"; + + xInfoSet->setPropertyValue( "StyleInsertModeFamilies", + Any(aFamiliesSeq) ); + + xInfoSet->setPropertyValue( "StyleInsertModeOverwrite", Any(!m_aOption.IsMerge()) ); + } + else if( m_bInsertMode ) + { + const rtl::Reference<SwXTextRange> xInsertTextRange = + SwXTextRange::CreateXTextRange(rDoc, *rPaM.GetPoint(), nullptr); + xInfoSet->setPropertyValue( "TextInsertModeRange", + Any(uno::Reference<text::XTextRange>(xInsertTextRange)) ); + } + else + { + rPaM.GetBound().nContent.Assign(nullptr, 0); + rPaM.GetBound(false).nContent.Assign(nullptr, 0); + } + + if( IsBlockMode() ) + { + xInfoSet->setPropertyValue( "AutoTextMode", Any(true) ); + } + if( IsOrganizerMode() ) + { + xInfoSet->setPropertyValue( "OrganizerMode", Any(true) ); + } + + // Set base URI + // there is ambiguity which medium should be used here + // for now the own medium has a preference + SfxMedium* pMedDescrMedium = m_pMedium ? m_pMedium : pDocSh->GetMedium(); + OSL_ENSURE( pMedDescrMedium, "There is no medium to get MediaDescriptor from!" ); + + xInfoSet->setPropertyValue( "BaseURI", Any( rBaseURL ) ); + + // TODO/LATER: separate links from usual embedded objects + OUString StreamPath; + if( SfxObjectCreateMode::EMBEDDED == rDoc.GetDocShell()->GetCreateMode() ) + { + if (pMedDescrMedium) + { + const SfxStringItem* pDocHierarchItem = + pMedDescrMedium->GetItemSet().GetItem(SID_DOC_HIERARCHICALNAME); + if ( pDocHierarchItem ) + StreamPath = pDocHierarchItem->GetValue(); + } + else + { + StreamPath = "dummyObjectName"; + } + + if( !StreamPath.isEmpty() ) + { + xInfoSet->setPropertyValue( "StreamRelPath", Any( StreamPath ) ); + } + } + + rtl::Reference<SwDoc> aHoldRef(&rDoc); // prevent deletion + ErrCodeMsg nRet = ERRCODE_NONE; + + // save redline mode into import info property set + static constexpr OUString sShowChanges(u"ShowChanges"_ustr); + static constexpr OUString sRecordChanges(u"RecordChanges"_ustr); + static constexpr OUString sRedlineProtectionKey(u"RedlineProtectionKey"_ustr); + xInfoSet->setPropertyValue( sShowChanges, + Any(IDocumentRedlineAccess::IsShowChanges(rDoc.getIDocumentRedlineAccess().GetRedlineFlags())) ); + xInfoSet->setPropertyValue( sRecordChanges, + Any(IDocumentRedlineAccess::IsRedlineOn(rDoc.getIDocumentRedlineAccess().GetRedlineFlags())) ); + xInfoSet->setPropertyValue( sRedlineProtectionKey, + Any(rDoc.getIDocumentRedlineAccess().GetRedlinePassword()) ); + + // force redline mode to "none" + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::NONE ); + + const bool bOASIS = ( SotStorage::GetVersion( xStorage ) > SOFFICE_FILEFORMAT_60 ); + // #i28749# - set property <ShapePositionInHoriL2R> + { + const bool bShapePositionInHoriL2R = !bOASIS; + xInfoSet->setPropertyValue( + "ShapePositionInHoriL2R", + Any( bShapePositionInHoriL2R ) ); + } + { + const bool bTextDocInOOoFileFormat = !bOASIS; + xInfoSet->setPropertyValue( + "TextDocInOOoFileFormat", + Any( bTextDocInOOoFileFormat ) ); + } + + ErrCode nWarnRDF = ERRCODE_NONE; + if ( !(IsOrganizerMode() || IsBlockMode() || m_aOption.IsFormatsOnly() || + m_bInsertMode) ) + { + // RDF metadata - must be read before styles/content + // N.B.: embedded documents have their own manifest.rdf! + try + { + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(xModelComp, + uno::UNO_QUERY_THROW); + const uno::Reference<frame::XModel> xModel(xModelComp, + uno::UNO_QUERY_THROW); + const uno::Reference<rdf::XURI> xBaseURI( ::sfx2::createBaseURI( + xContext, xModel, rBaseURL, StreamPath) ); + const uno::Reference<task::XInteractionHandler> xHandler( + pDocSh->GetMedium()->GetInteractionHandler() ); + xDMA->loadMetadataFromStorage(xStorage, xBaseURI, xHandler); + } + catch (const lang::WrappedTargetException & e) + { + ucb::InteractiveAugmentedIOException iaioe; + if (e.TargetException >>= iaioe) + { + // import error that was not ignored by InteractionHandler! + nWarnRDF = ERR_SWG_READ_ERROR; + } + else + { + nWarnRDF = WARN_SWG_FEATURES_LOST; // uhh... something wrong? + } + } + catch (uno::Exception &) + { + nWarnRDF = WARN_SWG_FEATURES_LOST; // uhh... something went wrong? + } + } + + // read storage streams + + // #i103539#: always read meta.xml for generator + ErrCodeMsg const nWarn = ReadThroughComponent( + xStorage, xModelComp, "meta.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisMetaImporter" + : "com.sun.star.comp.Writer.XMLMetaImporter"), + aEmptyArgs, rName, false ); + + ErrCodeMsg nWarn2 = ERRCODE_NONE; + if( !(IsOrganizerMode() || IsBlockMode() || m_aOption.IsFormatsOnly() || + m_bInsertMode) ) + { + nWarn2 = ReadThroughComponent( + xStorage, xModelComp, "settings.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisSettingsImporter" + : "com.sun.star.comp.Writer.XMLSettingsImporter"), + aFilterArgs, rName, false ); + } + + nRet = ReadThroughComponent( + xStorage, xModelComp, "styles.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisStylesImporter" + : "com.sun.star.comp.Writer.XMLStylesImporter"), + aFilterArgs, rName, true ); + + if( !nRet && !(IsOrganizerMode() || m_aOption.IsFormatsOnly()) ) + nRet = ReadThroughComponent( + xStorage, xModelComp, "content.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisContentImporter" + : "com.sun.star.comp.Writer.XMLContentImporter"), + aFilterArgs, rName, true ); + + if( !IsOrganizerMode() && !IsBlockMode() && !m_bInsertMode && + !m_aOption.IsFormatsOnly() && + // sw_redlinehide: disable layout cache for now + *o3tl::doAccess<bool>(xInfoSet->getPropertyValue(sShowChanges)) && + // sw_fieldmarkhide: also disable if there is a fieldmark + rDoc.getIDocumentMarkAccess()->getFieldmarksBegin() + == rDoc.getIDocumentMarkAccess()->getFieldmarksEnd()) + { + try + { + uno::Reference < io::XStream > xStm = xStorage->openStreamElement( "layout-cache", embed::ElementModes::READ ); + std::unique_ptr<SvStream> pStrm2 = utl::UcbStreamHelper::CreateStream( xStm ); + if( !pStrm2->GetError() ) + rDoc.ReadLayoutCache( *pStrm2 ); + } + catch (const uno::Exception&) + { + } + } + + // Notify math objects + if( m_bInsertMode ) + rDoc.PrtOLENotify( false ); + else if ( rDoc.IsOLEPrtNotifyPending() ) + rDoc.PrtOLENotify( true ); + + nRet = nRet ? nRet : (nWarn ? nWarn : (nWarn2 ? nWarn2 : nWarnRDF ) ); + + ::svx::DropUnusedNamedItems(xModelComp); + + m_aOption.ResetAllFormatsOnly(); + + // redline password + Any aAny = xInfoSet->getPropertyValue( sRedlineProtectionKey ); + Sequence<sal_Int8> aKey; + aAny >>= aKey; + rDoc.getIDocumentRedlineAccess().SetRedlinePassword( aKey ); + + // restore redline mode from import info property set + RedlineFlags nRedlineFlags = RedlineFlags::ShowInsert; + aAny = xInfoSet->getPropertyValue( sShowChanges ); + if ( *o3tl::doAccess<bool>(aAny) ) + nRedlineFlags |= RedlineFlags::ShowDelete; + aAny = xInfoSet->getPropertyValue( sRecordChanges ); + if ( *o3tl::doAccess<bool>(aAny) || aKey.hasElements() ) + nRedlineFlags |= RedlineFlags::On; + + // ... restore redline mode + // (First set bogus mode to make sure the mode in getIDocumentRedlineAccess().SetRedlineFlags() + // is different from its previous mode.) + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ~nRedlineFlags ); + // must set flags to show delete so that CompressRedlines works + rDoc.getIDocumentRedlineAccess().SetRedlineFlags(nRedlineFlags|RedlineFlags::ShowDelete); + // tdf#83260 ensure that the first call of CompressRedlines after loading + // the document is a no-op by calling it now + rDoc.getIDocumentRedlineAccess().CompressRedlines(); + // can't set it on the layout or view shell because it doesn't exist yet + rDoc.GetDocumentRedlineManager().SetHideRedlines(!(nRedlineFlags & RedlineFlags::ShowDelete)); + + lcl_EnsureValidPam( rPaM ); // move Pam into valid content + + if( xGraphicHelper ) + xGraphicHelper->dispose(); + xGraphicHelper.clear(); + xGraphicStorageHandler = nullptr; + if( xObjectHelper ) + xObjectHelper->dispose(); + xObjectHelper.clear(); + xObjectResolver = nullptr; + aHoldRef.clear(); + + if ( !bOASIS ) + { + // #i44177# - assure that for documents in OpenOffice.org + // file format the relation between outline numbering rule and styles is + // filled-up accordingly. + // Note: The OpenOffice.org file format, which has no content that applies + // a certain style, which is related to the outline numbering rule, + // has lost the information, that this certain style is related to + // the outline numbering rule. + // #i70748# - only for templates + if ( m_pMedium && m_pMedium->GetFilter() && + m_pMedium->GetFilter()->IsOwnTemplateFormat() ) + { + lcl_AdjustOutlineStylesForOOo( rDoc ); + } + // Fix #i58251#: Unfortunately is the static default different to SO7 behaviour, + // so we have to set a dynamic default after importing SO7 + rDoc.SetDefault(SwFormatRowSplit(false)); + } + + rDoc.PropagateOutlineRule(); + + // #i62875# + if ( rDoc.getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE) && !docfunc::ExistsDrawObjs( rDoc ) ) + { + rDoc.getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE, false); + } + + // Convert all instances of <SdrOle2Obj> into <SdrGrafObj>, because the + // Writer doesn't support such objects. + lcl_ConvertSdrOle2ObjsToSdrGrafObjs( rDoc ); + + // set BuildId on XModel for later OLE object loading + if( xInfoSet.is() ) + { + uno::Reference< beans::XPropertySet > xModelSet( xModelComp, uno::UNO_QUERY ); + if( xModelSet.is() ) + { + uno::Reference< beans::XPropertySetInfo > xModelSetInfo( xModelSet->getPropertySetInfo() ); + static constexpr OUString sName(u"BuildId"_ustr ); + if( xModelSetInfo.is() && xModelSetInfo->hasPropertyByName(sName) ) + { + xModelSet->setPropertyValue( sName, xInfoSet->getPropertyValue(sName) ); + } + } + } + + // tdf#115815 restore annotation ranges stored in temporary bookmarks + rDoc.getIDocumentMarkAccess()->restoreAnnotationMarks(); + + if (xStatusIndicator.is()) + { + xStatusIndicator->end(); + } + + rDoc.GetIStyleAccess().clearCaches(); // Clear Automatic-Style-Caches(shared_pointer!) + return nRet; +} + + // read the sections of the document, which is equal to the medium. + // returns the count of it +size_t XMLReader::GetSectionList( SfxMedium& rMedium, + std::vector<OUString>& rStrings) const +{ + uno::Reference< uno::XComponentContext > xContext = + comphelper::getProcessComponentContext(); + uno::Reference < embed::XStorage > xStg2; + if( ( xStg2 = rMedium.GetStorage() ).is() ) + { + try + { + xml::sax::InputSource aParserInput; + static constexpr OUString sDocName( u"content.xml"_ustr ); + aParserInput.sSystemId = sDocName; + + uno::Reference < io::XStream > xStm = xStg2->openStreamElement( sDocName, embed::ElementModes::READ ); + aParserInput.aInputStream = xStm->getInputStream(); + + // get filter + rtl::Reference< SwXMLSectionList > xImport = new SwXMLSectionList( xContext, rStrings ); + + // parse + xImport->parseStream( aParserInput ); + } + catch( xml::sax::SAXParseException& ) + { + TOOLS_WARN_EXCEPTION("sw", ""); + // re throw ? + } + catch( xml::sax::SAXException& ) + { + TOOLS_WARN_EXCEPTION("sw", ""); + // re throw ? + } + catch( io::IOException& ) + { + TOOLS_WARN_EXCEPTION("sw", ""); + // re throw ? + } + catch( packages::WrongPasswordException& ) + { + // re throw ? + } + } + return rStrings.size(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/wrtxml.cxx b/sw/source/filter/xml/wrtxml.cxx new file mode 100644 index 0000000000..bfb1366566 --- /dev/null +++ b/sw/source/filter/xml/wrtxml.cxx @@ -0,0 +1,586 @@ +/* -*- 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 <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/document/XExporter.hpp> +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/frame/XModule.hpp> + +#include <officecfg/Office/Common.hxx> +#include <comphelper/fileformat.h> +#include <comphelper/processfactory.hxx> +#include <comphelper/genericpropertyset.hxx> +#include <comphelper/propertysetinfo.hxx> +#include <vcl/errinf.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <svx/xmlgrhlp.hxx> +#include <svx/xmleohlp.hxx> +#include <svl/stritem.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/sfxsids.hrc> +#include <pam.hxx> +#include <doc.hxx> +#include <docfunc.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <IDocumentStatistics.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <rootfrm.hxx> +#include <docstat.hxx> +#include <docsh.hxx> + +#include <xmloff/shapeexport.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <swerror.h> +#include "wrtxml.hxx" +#include "zorder.hxx" +#include <strings.hrc> + +#include <comphelper/documentconstants.hxx> +#include <com/sun/star/rdf/XDocumentMetadataAccess.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; + +SwXMLWriter::SwXMLWriter( const OUString& rBaseURL ) +{ + SetBaseURL( rBaseURL ); +} + +SwXMLWriter::~SwXMLWriter() +{ +} + +ErrCodeMsg SwXMLWriter::Write_(const SfxItemSet* pMediumItemSet) +{ + uno::Reference<task::XStatusIndicator> xStatusIndicator; + OUString aDocHierarchicalName; + bool bNoEmbDS(false); + + if (pMediumItemSet) + { + const SfxUnoAnyItem* pStatusBarItem = + pMediumItemSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL); + if (pStatusBarItem) + pStatusBarItem->GetValue() >>= xStatusIndicator; + const SfxStringItem* pDocHierarchItem = + pMediumItemSet->GetItem(SID_DOC_HIERARCHICALNAME); + if (pDocHierarchItem) + aDocHierarchicalName = pDocHierarchItem->GetValue(); + const SfxBoolItem* pNoEmbDS = pMediumItemSet->GetItem(SID_NO_EMBEDDED_DS); + if (pNoEmbDS) + bNoEmbDS = pNoEmbDS->GetValue(); + } + + // Get service factory + uno::Reference< uno::XComponentContext > xContext = + comphelper::getProcessComponentContext(); + + // Get data sink ... + uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler; + rtl::Reference<SvXMLGraphicHelper> xGraphicHelper ; + uno::Reference< document::XEmbeddedObjectResolver > xObjectResolver; + rtl::Reference<SvXMLEmbeddedObjectHelper> xObjectHelper; + + OSL_ENSURE( m_xStg.is(), "Where is my storage?" ); + xGraphicHelper = SvXMLGraphicHelper::Create( m_xStg, + SvXMLGraphicHelperMode::Write ); + xGraphicStorageHandler = xGraphicHelper.get(); + + SfxObjectShell *pPersist = m_pDoc->GetPersist(); + if( pPersist ) + { + xObjectHelper = SvXMLEmbeddedObjectHelper::Create( + m_xStg, *pPersist, + SvXMLEmbeddedObjectHelperMode::Write ); + xObjectResolver = xObjectHelper.get(); + } + + // create and prepare the XPropertySet that gets passed through + // the components, and the XStatusIndicator that shows progress to + // the user. + + // create XPropertySet with three properties for status indicator + static comphelper::PropertyMapEntry const aInfoMap[] = + { + { OUString("ProgressRange"), 0, + ::cppu::UnoType<sal_Int32>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressMax"), 0, + ::cppu::UnoType<sal_Int32>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressCurrent"), 0, + ::cppu::UnoType<sal_Int32>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("WrittenNumberStyles"), 0, + cppu::UnoType<uno::Sequence<sal_Int32>>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("UsePrettyPrinting"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ShowChanges"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("RedlineProtectionKey"), 0, + cppu::UnoType<Sequence<sal_Int8>>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("BaseURI"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamRelPath"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamName"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("AutoTextMode"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StyleNames"), 0, + cppu::UnoType<Sequence<OUString>>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StyleFamilies"), 0, + cppu::UnoType<Sequence<sal_Int32>>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + // #i69627# + { OUString("OutlineStyleAsNormalListStyle"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("TargetStorage"),0, cppu::UnoType<embed::XStorage>::get(), + css::beans::PropertyAttribute::MAYBEVOID, 0 }, + // tdf#144532 + { OUString("NoEmbDataSet"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + }; + uno::Reference< beans::XPropertySet > xInfoSet( + comphelper::GenericPropertySet_CreateInstance( + new comphelper::PropertySetInfo( aInfoMap ) ) ); + + xInfoSet->setPropertyValue( "TargetStorage", Any( m_xStg ) ); + + xInfoSet->setPropertyValue("NoEmbDataSet", Any(bNoEmbDS)); + + if (m_bShowProgress) + { + // set progress range and start status indicator + sal_Int32 nProgressRange(1000000); + if (xStatusIndicator.is()) + { + xStatusIndicator->start(SwResId( STR_STATSTR_SWGWRITE), + nProgressRange); + } + xInfoSet->setPropertyValue("ProgressRange", Any(nProgressRange)); + + xInfoSet->setPropertyValue("ProgressMax", Any(static_cast < sal_Int32 >( -1 ))); + } + + xInfoSet->setPropertyValue( "UsePrettyPrinting", Any(officecfg::Office::Common::Save::Document::PrettyPrinting::get()) ); + + uno::Reference<lang::XComponent> const xModelComp(m_pDoc->GetDocShell()->GetModel()); + uno::Reference<drawing::XDrawPageSupplier> const xDPS(xModelComp, uno::UNO_QUERY); + assert(xDPS.is()); + xmloff::FixZOrder(xDPS->getDrawPage(), sw::GetZOrderLayer(m_pDoc->getIDocumentDrawModelAccess())); + + // save show redline mode ... + RedlineFlags const nOrigRedlineFlags = m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + RedlineFlags nRedlineFlags(nOrigRedlineFlags); + bool isShowChanges; + // TODO: ideally this would be stored per-view... + SwRootFrame const*const pLayout(m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout()); + isShowChanges = pLayout == nullptr || !pLayout->IsHideRedlines(); + xInfoSet->setPropertyValue("ShowChanges", Any(isShowChanges)); + // ... and hide redlines for export + nRedlineFlags &= ~RedlineFlags::ShowMask; + nRedlineFlags |= RedlineFlags::ShowInsert; + m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags( nRedlineFlags ); + + // Set base URI + xInfoSet->setPropertyValue( "BaseURI", Any( GetBaseURL() ) ); + + if( SfxObjectCreateMode::EMBEDDED == m_pDoc->GetDocShell()->GetCreateMode() ) + { + const OUString aName( !aDocHierarchicalName.isEmpty() + ? aDocHierarchicalName + : OUString( "dummyObjectName" ) ); + + xInfoSet->setPropertyValue( "StreamRelPath", Any( aName ) ); + } + + if( m_bBlock ) + { + xInfoSet->setPropertyValue( "AutoTextMode", Any(true) ); + } + + // #i69627# + const bool bOASIS = ( SotStorage::GetVersion( m_xStg ) > SOFFICE_FILEFORMAT_60 ); + if ( bOASIS && + docfunc::HasOutlineStyleToBeWrittenAsNormalListStyle( *m_pDoc ) ) + { + xInfoSet->setPropertyValue( "OutlineStyleAsNormalListStyle", Any( true ) ); + } + + // filter arguments + // - graphics + object resolver for styles + content + // - status indicator + // - info property set + // - else empty + sal_Int32 nArgs = 1; + if( xStatusIndicator.is() ) + nArgs++; + + Sequence < Any > aEmptyArgs( nArgs ); + Any *pArgs = aEmptyArgs.getArray(); + *pArgs++ <<= xInfoSet; + if( xStatusIndicator.is() ) + *pArgs++ <<= xStatusIndicator; + + if( xGraphicStorageHandler.is() ) + nArgs++; + if( xObjectResolver.is() ) + nArgs++; + + Sequence < Any > aFilterArgs( nArgs ); + pArgs = aFilterArgs.getArray(); + *pArgs++ <<= xInfoSet; + if( xGraphicStorageHandler.is() ) + *pArgs++ <<= xGraphicStorageHandler; + if( xObjectResolver.is() ) + *pArgs++ <<= xObjectResolver; + if( xStatusIndicator.is() ) + *pArgs++ <<= xStatusIndicator; + + PutNumFormatFontsInAttrPool(); + PutEditEngFontsInAttrPool(); + + // properties + Sequence < PropertyValue > aProps( m_pOrigFileName ? 1 : 0 ); + if( m_pOrigFileName ) + { + PropertyValue *pProps = aProps.getArray(); + pProps->Name = "FileName"; + pProps->Value <<= *m_pOrigFileName; + } + + // export sub streams for package, else full stream into a file + bool bWarn = false; + + // RDF metadata: export if ODF >= 1.2 + // N.B.: embedded documents have their own manifest.rdf! + if ( bOASIS ) + { + const uno::Reference<beans::XPropertySet> xPropSet(m_xStg, + uno::UNO_QUERY_THROW); + try + { + OUString Version; + // ODF >= 1.2 + if ((xPropSet->getPropertyValue("Version") >>= Version) + && Version != ODFVER_010_TEXT + && Version != ODFVER_011_TEXT) + { + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA( + xModelComp, uno::UNO_QUERY_THROW); + xDMA->storeMetadataToStorage(m_xStg); + } + } + catch (beans::UnknownPropertyException &) + { /* ignore */ } + catch (uno::Exception &) + { + bWarn = true; + } + } + + bool bStoreMeta = ( SfxObjectCreateMode::EMBEDDED != m_pDoc->GetDocShell()->GetCreateMode() ); + if ( !bStoreMeta ) + { + try + { + Reference< frame::XModule > xModule( xModelComp, UNO_QUERY ); + if ( xModule.is() ) + { + const OUString aModuleID = xModule->getIdentifier(); + bStoreMeta = !aModuleID.isEmpty() && + ( aModuleID == "com.sun.star.sdb.FormDesign" || + aModuleID == "com.sun.star.sdb.TextReportDesign" ); + } + } + catch( uno::Exception& ) + {} + } + + OUString sWarnFile; + if( !m_bOrganizerMode && !m_bBlock && bStoreMeta ) + { + if( !WriteThroughComponent( + xModelComp, "meta.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisMetaExporter" + : "com.sun.star.comp.Writer.XMLMetaExporter"), + aEmptyArgs, aProps ) ) + { + bWarn = true; + sWarnFile = "meta.xml"; + } + } + + if( !m_bBlock ) + { + if( !WriteThroughComponent( + xModelComp, "settings.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisSettingsExporter" + : "com.sun.star.comp.Writer.XMLSettingsExporter"), + aEmptyArgs, aProps ) ) + { + if( !bWarn ) + { + bWarn = true; + sWarnFile = "settings.xml"; + } + } + } + + bool bErr = false; + + OUString sErrFile; + if( !WriteThroughComponent( + xModelComp, "styles.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisStylesExporter" + : "com.sun.star.comp.Writer.XMLStylesExporter"), + aFilterArgs, aProps ) ) + { + bErr = true; + sErrFile = "styles.xml"; + } + + if( !m_bOrganizerMode && !bErr ) + { + if( !WriteThroughComponent( + xModelComp, "content.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisContentExporter" + : "com.sun.star.comp.Writer.XMLContentExporter"), + aFilterArgs, aProps ) ) + { + bErr = true; + sErrFile = "content.xml"; + } + } + + if( m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && m_pDoc->getIDocumentStatistics().GetDocStat().nPage > 1 && + !(m_bOrganizerMode || m_bBlock || bErr || + // sw_redlinehide: disable layout cache for now + m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout()->HasMergedParas())) + { + try + { + uno::Reference < io::XStream > xStm = m_xStg->openStreamElement( "layout-cache", embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); + std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream( xStm ); + if( !pStream->GetError() ) + { + uno::Reference < beans::XPropertySet > xSet( xStm, UNO_QUERY ); + uno::Any aAny2; + aAny2 <<= OUString("application/binary"); + xSet->setPropertyValue("MediaType", aAny2 ); + m_pDoc->WriteLayoutCache( *pStream ); + } + } + catch ( uno::Exception& ) + { + } + } + + if( xGraphicHelper ) + xGraphicHelper->dispose(); + xGraphicHelper.clear(); + xGraphicStorageHandler = nullptr; + + if( xObjectHelper ) + xObjectHelper->dispose(); + xObjectHelper.clear(); + xObjectResolver = nullptr; + + // restore redline mode + nRedlineFlags = m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + nRedlineFlags &= ~RedlineFlags::ShowMask; + nRedlineFlags |= RedlineFlags::ShowInsert; + nRedlineFlags |= nOrigRedlineFlags & RedlineFlags::ShowMask; + m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags( nRedlineFlags ); + + // tdf#115815 restore annotation ranges collapsed by hide redlines + m_pDoc->getIDocumentMarkAccess()->restoreAnnotationMarks(); + + if (xStatusIndicator.is()) + { + xStatusIndicator->end(); + } + + if( bErr ) + { + if( !sErrFile.isEmpty() ) + return ErrCodeMsg( ERR_WRITE_ERROR_FILE, sErrFile, + DialogMask::ButtonsOk | DialogMask::MessageError ); + return ERR_SWG_WRITE_ERROR; + } + else if( bWarn ) + { + if( !sWarnFile.isEmpty() ) + return ErrCodeMsg( WARN_WRITE_ERROR_FILE, sWarnFile, + DialogMask::ButtonsOk | DialogMask::MessageError ); + return WARN_SWG_FEATURES_LOST; + } + + return ERRCODE_NONE; +} + +ErrCodeMsg SwXMLWriter::WriteStorage() +{ + return Write_(nullptr); +} + +ErrCodeMsg SwXMLWriter::WriteMedium( SfxMedium& aTargetMedium ) +{ + return Write_(&aTargetMedium.GetItemSet()); +} + +ErrCodeMsg SwXMLWriter::Write( SwPaM& rPaM, SfxMedium& rMed, + const OUString* pFileName ) +{ + return IsStgWriter() + ? static_cast<StgWriter *>(this)->Write( rPaM, rMed.GetOutputStorage(), pFileName, &rMed ) + : static_cast<Writer *>(this)->Write( rPaM, *rMed.GetOutStream(), pFileName ); +} + +bool SwXMLWriter::WriteThroughComponent( + const uno::Reference<XComponent> & xComponent, + const char* pStreamName, + const uno::Reference<uno::XComponentContext> & rxContext, + const char* pServiceName, + const Sequence<Any> & rArguments, + const Sequence<beans::PropertyValue> & rMediaDesc ) +{ + OSL_ENSURE( m_xStg.is(), "Need storage!" ); + OSL_ENSURE( nullptr != pStreamName, "Need stream name!" ); + OSL_ENSURE( nullptr != pServiceName, "Need service name!" ); + + SAL_INFO( "sw.filter", "SwXMLWriter::WriteThroughComponent : stream " << pStreamName ); + // open stream + bool bRet = false; + try + { + const OUString sStreamName = OUString::createFromAscii( pStreamName ); + uno::Reference<io::XStream> xStream = + m_xStg->openStreamElement( sStreamName, + embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); + + uno::Reference <beans::XPropertySet > xSet( xStream, uno::UNO_QUERY ); + if( !xSet.is() ) + return false; + + xSet->setPropertyValue("MediaType", Any(OUString("text/xml")) ); + + // even plain stream should be encrypted in encrypted documents + xSet->setPropertyValue( "UseCommonStoragePasswordEncryption", Any(true) ); + + // set buffer and create outputstream + uno::Reference< io::XOutputStream > xOutputStream = xStream->getOutputStream(); + + // set Base URL + uno::Reference< beans::XPropertySet > xInfoSet; + if( rArguments.hasElements() ) + rArguments.getConstArray()[0] >>= xInfoSet; + OSL_ENSURE( xInfoSet.is(), "missing property set" ); + if( xInfoSet.is() ) + { + xInfoSet->setPropertyValue( "StreamName", Any( sStreamName ) ); + } + + // write the stuff + bRet = WriteThroughComponent( + xOutputStream, xComponent, rxContext, + pServiceName, rArguments, rMediaDesc ); + } + catch ( uno::Exception& ) + { + } + + return bRet; + +} + +bool SwXMLWriter::WriteThroughComponent( + const uno::Reference<io::XOutputStream> & xOutputStream, + const uno::Reference<XComponent> & xComponent, + const uno::Reference<XComponentContext> & rxContext, + const char* pServiceName, + const Sequence<Any> & rArguments, + const Sequence<PropertyValue> & rMediaDesc ) +{ + OSL_ENSURE( xOutputStream.is(), "I really need an output stream!" ); + OSL_ENSURE( xComponent.is(), "Need component!" ); + OSL_ENSURE( nullptr != pServiceName, "Need component name!" ); + + // get component + uno::Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(rxContext); + SAL_INFO( "sw.filter", "SAX-Writer created" ); + // connect XML writer to output stream + xSaxWriter->setOutputStream( xOutputStream ); + + // prepare arguments (prepend doc handler to given arguments) + Sequence<Any> aArgs( 1 + rArguments.getLength() ); + auto pArgs = aArgs.getArray(); + *pArgs <<= xSaxWriter; + std::copy(rArguments.begin(), rArguments.end(), std::next(pArgs)); + + // get filter component + uno::Reference< document::XExporter > xExporter( + rxContext->getServiceManager()->createInstanceWithArgumentsAndContext( + OUString::createFromAscii(pServiceName), aArgs, rxContext), UNO_QUERY); + OSL_ENSURE( xExporter.is(), + "can't instantiate export filter component" ); + if( !xExporter.is() ) + return false; + SAL_INFO( "sw.filter", pServiceName << " instantiated." ); + // connect model and filter + xExporter->setSourceDocument( xComponent ); + + // filter! + SAL_INFO( "sw.filter", "call filter()" ); + uno::Reference<XFilter> xFilter( xExporter, UNO_QUERY ); + return xFilter->filter( rMediaDesc ); +} + +void GetXMLWriter( + [[maybe_unused]] std::u16string_view /*rName*/, const OUString& rBaseURL, WriterRef& xRet ) +{ + xRet = new SwXMLWriter( rBaseURL ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/wrtxml.hxx b/sw/source/filter/xml/wrtxml.hxx new file mode 100644 index 0000000000..abbdd7aa7b --- /dev/null +++ b/sw/source/filter/xml/wrtxml.hxx @@ -0,0 +1,87 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_SW_SOURCE_FILTER_XML_WRTXML_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_WRTXML_HXX + +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <shellio.hxx> + +class SwPaM; +class SfxMedium; + +namespace com::sun::star { + namespace uno { template<class A> class Reference; } + namespace uno { template<class A> class Sequence; } + namespace uno { class Any; } + namespace lang { class XComponent; } + namespace lang { class XMultiServiceFactory; } + namespace beans { struct PropertyValue; } +} + +class SwXMLWriter : public StgWriter +{ + ErrCodeMsg Write_(const SfxItemSet* pMediumItemSet); + + using StgWriter::Write; + +protected: + virtual ErrCodeMsg WriteStorage() override; + virtual ErrCodeMsg WriteMedium( SfxMedium& aTargetMedium ) override; + +public: + + SwXMLWriter( const OUString& rBaseURL ); + virtual ~SwXMLWriter() override; + + virtual ErrCodeMsg Write( SwPaM&, SfxMedium&, const OUString* ) override; + +private: + + // helper methods to write XML streams + + // write a single XML stream into the package + bool WriteThroughComponent( + // the component we export + const css::uno::Reference<css::lang::XComponent> & xComponent, + const char* pStreamName, // the stream name + // service factory for pServiceName + const css::uno::Reference<css::uno::XComponentContext> & rFactory, + const char* pServiceName, // service name of the component + // the argument (XInitialization) + const css::uno::Sequence<css::uno::Any> & rArguments, + // output descriptor + const css::uno::Sequence<css::beans::PropertyValue> & rMediaDesc ); + + // write a single output stream + // (to be called either directly or by WriteThroughComponent(...)) + static bool WriteThroughComponent( + const css::uno::Reference<css::io::XOutputStream> & xOutputStream, + const css::uno::Reference<css::lang::XComponent> & xComponent, + const css::uno::Reference<css::uno::XComponentContext> & rFactory, + const char* pServiceName, + const css::uno::Sequence<css::uno::Any> & rArguments, + const css::uno::Sequence<css::beans::PropertyValue> & rMediaDesc ); +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_WRTXML_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlbrsh.cxx b/sw/source/filter/xml/xmlbrsh.cxx new file mode 100644 index 0000000000..b31dc0cfa4 --- /dev/null +++ b/sw/source/filter/xml/xmlbrsh.cxx @@ -0,0 +1,194 @@ +/* -*- 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/memberids.h> +#include <vcl/graph.hxx> + +#include <sal/log.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlimp.hxx> +#include <xmloff/XMLBase64ImportContext.hxx> +#include <editeng/brushitem.hxx> +#include <xmloff/xmluconv.hxx> + +#include "xmlbrshi.hxx" +#include "xmlbrshe.hxx" +#include "xmlexp.hxx" +#include "xmlimpit.hxx" +#include "xmlexpit.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::xmloff::token; + +void SwXMLBrushItemImportContext::ProcessAttrs( + const uno::Reference< xml::sax::XFastAttributeList >& xAttrList, + const SvXMLUnitConverter& rUnitConv ) +{ + for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) ) + { + const OUString sValue = aIter.toString(); + + switch( aIter.getToken() ) + { + case XML_ELEMENT(XLINK, XML_HREF): + m_xGraphic = GetImport().loadGraphicByURL(sValue); + break; + case XML_ELEMENT(XLINK, XML_TYPE): + case XML_ELEMENT(XLINK, XML_ACTUATE): + case XML_ELEMENT(XLINK, XML_SHOW): + break; + case XML_ELEMENT(STYLE, XML_POSITION): + SvXMLImportItemMapper::PutXMLValue( + *m_pItem, sValue, MID_GRAPHIC_POSITION, rUnitConv ); + break; + case XML_ELEMENT(STYLE, XML_REPEAT): + SvXMLImportItemMapper::PutXMLValue( + *m_pItem, sValue, MID_GRAPHIC_REPEAT, rUnitConv ); + break; + case XML_ELEMENT(STYLE, XML_FILTER_NAME): + SvXMLImportItemMapper::PutXMLValue( + *m_pItem, sValue, MID_GRAPHIC_FILTER, rUnitConv ); + break; + default: + XMLOFF_WARN_UNKNOWN("sw", aIter); + } + } + +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLBrushItemImportContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + if ((nElement & TOKEN_MASK) == xmloff::token::XML_BINARY_DATA) + { + if (!m_xBase64Stream.is()) + { + m_xBase64Stream = GetImport().GetStreamForGraphicObjectURLFromBase64(); + if (m_xBase64Stream.is()) + return new XMLBase64ImportContext(GetImport(), m_xBase64Stream); + } + } + XMLOFF_WARN_UNKNOWN_ELEMENT("sw", nElement); + return nullptr; +} + +void SwXMLBrushItemImportContext::endFastElement(sal_Int32 ) +{ + if (m_xBase64Stream.is()) + { + m_xGraphic = GetImport().loadGraphicFromBase64(m_xBase64Stream); + m_xBase64Stream = nullptr; + } + + if (m_xGraphic.is()) + { + Graphic aGraphic(m_xGraphic); + SvxGraphicPosition eOldGraphicPos = m_pItem->GetGraphicPos(); + m_pItem->SetGraphic(aGraphic); + if (GPOS_NONE == eOldGraphicPos && GPOS_NONE != m_pItem->GetGraphicPos()) + m_pItem->SetGraphicPos(GPOS_TILED); + } + + if (!(m_pItem->GetGraphic())) + m_pItem->SetGraphicPos(GPOS_NONE); + else if (GPOS_NONE == m_pItem->GetGraphicPos()) + m_pItem->SetGraphicPos(GPOS_TILED); +} + +SwXMLBrushItemImportContext::SwXMLBrushItemImportContext( + SvXMLImport& rImport, sal_Int32 /*nElement*/, + const uno::Reference< xml::sax::XFastAttributeList >& xAttrList, + const SvXMLUnitConverter& rUnitConv, + const SvxBrushItem& rItem ) : + SvXMLImportContext( rImport ), + m_pItem( new SvxBrushItem( rItem ) ) +{ + // delete any graphic that is existing + m_pItem->SetGraphicPos( GPOS_NONE ); + + ProcessAttrs( xAttrList, rUnitConv ); +} + +SwXMLBrushItemImportContext::SwXMLBrushItemImportContext( + SvXMLImport& rImport, sal_Int32 /*nElement*/, + const uno::Reference< xml::sax::XFastAttributeList > & xAttrList, + const SvXMLUnitConverter& rUnitConv, + sal_uInt16 nWhich ) : + SvXMLImportContext( rImport ), + m_pItem( new SvxBrushItem( nWhich ) ) +{ + ProcessAttrs( xAttrList, rUnitConv ); +} + +SwXMLBrushItemImportContext::~SwXMLBrushItemImportContext() +{ +} + +SwXMLBrushItemExport::SwXMLBrushItemExport( SwXMLExport& rExp ) : + m_rExport( rExp ) +{ +} + +void SwXMLBrushItemExport::exportXML( const SvxBrushItem& rItem ) +{ + GetExport().CheckAttrList(); + + uno::Reference<graphic::XGraphic> xGraphic; + + const Graphic* pGraphic = rItem.GetGraphic(); + + if (pGraphic) + xGraphic = pGraphic->GetXGraphic(); + + if (xGraphic.is()) + { + OUString sMimeType; + OUString sValue = GetExport().AddEmbeddedXGraphic(xGraphic, sMimeType); + if (!sValue.isEmpty()) + { + GetExport().AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sValue ); + GetExport().AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + GetExport().AddAttribute( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD ); + } + + const SvXMLUnitConverter& rUnitConv = GetExport().GetTwipUnitConverter(); + if (SvXMLExportItemMapper::QueryXMLValue(rItem, sValue, MID_GRAPHIC_POSITION, rUnitConv)) + GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_POSITION, sValue ); + + if (SvXMLExportItemMapper::QueryXMLValue(rItem, sValue, MID_GRAPHIC_REPEAT, rUnitConv)) + GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_REPEAT, sValue ); + + if (SvXMLExportItemMapper::QueryXMLValue(rItem, sValue, MID_GRAPHIC_FILTER, rUnitConv)) + GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_FILTER_NAME, sValue ); + } + + { + SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_STYLE, XML_BACKGROUND_IMAGE, + true, true ); + if (xGraphic.is()) + { + // optional office:binary-data + GetExport().AddEmbeddedXGraphicAsBase64(xGraphic); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlbrshe.hxx b/sw/source/filter/xml/xmlbrshe.hxx new file mode 100644 index 0000000000..0253a6f068 --- /dev/null +++ b/sw/source/filter/xml/xmlbrshe.hxx @@ -0,0 +1,41 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLBRSHE_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLBRSHE_HXX + +class SvxBrushItem; +class SwXMLExport; + +class SwXMLBrushItemExport +{ + SwXMLExport& m_rExport; + + SwXMLExport& GetExport() { return m_rExport; } + +public: + explicit SwXMLBrushItemExport(SwXMLExport& rExport); + + // core API + void exportXML(const SvxBrushItem& rItem); +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLBRSHE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlbrshi.hxx b/sw/source/filter/xml/xmlbrshi.hxx new file mode 100644 index 0000000000..0e5cb43c84 --- /dev/null +++ b/sw/source/filter/xml/xmlbrshi.hxx @@ -0,0 +1,77 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLBRSHI_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLBRSHI_HXX + +#include <memory> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> + +#include <xmloff/xmlictxt.hxx> + +class SvXMLImport; +class SvXMLUnitConverter; +class SvxBrushItem; + +namespace com::sun::star { + namespace io { class XOutputStream; } +} + +class SwXMLBrushItemImportContext : public SvXMLImportContext +{ +private: + css::uno::Reference<css::io::XOutputStream> m_xBase64Stream; + css::uno::Reference<css::graphic::XGraphic> m_xGraphic; + + std::unique_ptr<SvxBrushItem> m_pItem; + + void ProcessAttrs( + const css::uno::Reference<css::xml::sax::XFastAttributeList > & xAttrList, + const SvXMLUnitConverter& rUnitConv ); + +public: + + SwXMLBrushItemImportContext( + SvXMLImport& rImport, + sal_Int32 nElement, + const css::uno::Reference<css::xml::sax::XFastAttributeList > & xAttrList, + const SvXMLUnitConverter& rUnitConv, + const SvxBrushItem& rItem ); + + SwXMLBrushItemImportContext( + SvXMLImport& rImport, + sal_Int32 nElement, + const css::uno::Reference<css::xml::sax::XFastAttributeList > & xAttrList, + const SvXMLUnitConverter& rUnitConv, + sal_uInt16 nWhich ); + + virtual ~SwXMLBrushItemImportContext() override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + const SvxBrushItem& GetItem() const { return *m_pItem; } +}; + +#endif // _XMLBRSHI_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlexp.cxx b/sw/source/filter/xml/xmlexp.cxx new file mode 100644 index 0000000000..434eb8c073 --- /dev/null +++ b/sw/source/filter/xml/xmlexp.cxx @@ -0,0 +1,623 @@ +/* -*- 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 <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/xforms/XFormsSupplier.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <comphelper/indexedpropertyvalues.hxx> +#include <osl/diagnose.h> +#include <o3tl/any.hxx> +#include <sax/tools/converter.hxx> +#include <svx/svdpage.hxx> +#include <svx/xmleohlp.hxx> +#include <svx/xmlgrhlp.hxx> +#include <editeng/eeitem.hxx> +#include <svx/svddef.hxx> +#include <tools/UnitConversion.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <editeng/xmlcnitm.hxx> +#include <xmloff/ProgressBarHelper.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/xformsexport.hxx> +#include <drawdoc.hxx> +#include <doc.hxx> +#include <swmodule.hxx> +#include <docsh.hxx> +#include <viewsh.hxx> +#include <rootfrm.hxx> +#include <docstat.hxx> +#include <swerror.h> +#include <unotext.hxx> +#include "xmltexte.hxx" +#include "xmlexp.hxx" +#include "xmlexpit.hxx" +#include "zorder.hxx" +#include <comphelper/processfactory.hxx> +#include <docary.hxx> +#include <frameformats.hxx> +#include <comphelper/servicehelper.hxx> +#include <vcl/svapp.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentStatistics.hxx> +#include <IDocumentLayoutAccess.hxx> + + +#include <pausethreadstarting.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::xforms; +using namespace ::xmloff::token; + +SwXMLExport::SwXMLExport( + const uno::Reference< uno::XComponentContext >& rContext, + OUString const & implementationName, SvXMLExportFlags nExportFlags) +: SvXMLExport( rContext, implementationName, util::MeasureUnit::INCH, XML_TEXT, + nExportFlags ), + m_bBlock( false ), + m_bShowProgress( true ), + m_bSavedShowChanges( false ), + m_pDoc( nullptr ) +{ + InitItemExport(); +} + +ErrCode SwXMLExport::exportDoc( enum XMLTokenEnum eClass ) +{ + if( !GetModel().is() ) + return ERR_SWG_WRITE_ERROR; + + SwPauseThreadStarting aPauseThreadStarting; // #i73788# + + // from here, we use core interfaces -> lock Solar-Mutex + SolarMutexGuard aGuard; + + { + Reference<XPropertySet> rInfoSet = getExportInfo(); + if( rInfoSet.is() ) + { + static constexpr OUString sAutoTextMode(u"AutoTextMode"_ustr); + if( rInfoSet->getPropertySetInfo()->hasPropertyByName( + sAutoTextMode ) ) + { + Any aAny = rInfoSet->getPropertyValue(sAutoTextMode); + if( auto b = o3tl::tryAccess<bool>(aAny) ) + { + if( *b ) + m_bBlock = true; + } + } + } + } + + SwDoc *pDoc = getDoc(); + if (!pDoc) + return ERR_SWG_WRITE_ERROR; + + if( getExportFlags() & (SvXMLExportFlags::FONTDECLS|SvXMLExportFlags::STYLES| + SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT)) + { + if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) + { + GetNamespaceMap_().Add( + GetXMLToken(XML_NP_OFFICE_EXT), + GetXMLToken(XML_N_OFFICE_EXT), + XML_NAMESPACE_OFFICE_EXT); + } + + GetTextParagraphExport()->SetBlockMode( m_bBlock ); + + const SfxItemPool& rPool = pDoc->GetAttrPool(); + sal_uInt16 aWhichIds[5] = { RES_UNKNOWNATR_CONTAINER, + RES_TXTATR_UNKNOWN_CONTAINER, + SDRATTR_XMLATTRIBUTES, + EE_PARA_XMLATTRIBS, + EE_CHAR_XMLATTRIBS }; + + const int nWhichIds = rPool.GetSecondaryPool() ? 5 : 2; + for( int j=0; j < nWhichIds; ++j ) + { + const sal_uInt16 nWhichId = aWhichIds[j]; + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nWhichId)) + { + auto pUnknown = dynamic_cast<const SvXMLAttrContainerItem*>( pItem ); + OSL_ENSURE( pUnknown, "illegal attribute container item" ); + if( pUnknown && (pUnknown->GetAttrCount() > 0) ) + { + sal_uInt16 nIdx = pUnknown->GetFirstNamespaceIndex(); + while( USHRT_MAX != nIdx ) + { + GetNamespaceMap_().Add( pUnknown->GetPrefix( nIdx ), + pUnknown->GetNamespace( nIdx ) ); + nIdx = pUnknown->GetNextNamespaceIndex( nIdx ); + } + } + } + } + } + + sal_uInt16 const eUnit = SvXMLUnitConverter::GetMeasureUnit( + SW_MOD()->GetMetric(pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE))); + if (GetMM100UnitConverter().GetXMLMeasureUnit() != eUnit ) + { + GetMM100UnitConverter().SetXMLMeasureUnit( eUnit ); + m_pTwipUnitConverter->SetXMLMeasureUnit( eUnit ); + } + + if( getExportFlags() & SvXMLExportFlags::META) + { + // Update doc stat, so that correct values are exported and + // the progress works correctly. + pDoc->getIDocumentStatistics().UpdateDocStat( false, true ); + } + if( m_bShowProgress ) + { + ProgressBarHelper *pProgress = GetProgressBarHelper(); + if( -1 == pProgress->GetReference() ) + { + // progress isn't initialized: + // We assume that the whole doc is exported, and the following + // durations: + // - meta information: 2 + // - settings: 4 (TODO: not now!) + // - styles (except page styles): 2 + // - page styles: 2 (TODO: not now!) + 2 for each paragraph + // - paragraph: 2 (1 for automatic styles and one for content) + + // count each item once, and then multiply by two to reach the + // figures given above + // The styles in pDoc also count the default style that never + // gets exported -> subtract one. + sal_Int32 nRef = 1; // meta.xml + nRef += pDoc->GetCharFormats()->size() - 1; + nRef += pDoc->GetFrameFormats()->size() - 1; + nRef += pDoc->GetTextFormatColls()->size() - 1; + nRef *= 2; // for the above styles, xmloff will increment by 2! + // #i93174#: count all paragraphs for the progress bar + nRef += pDoc->getIDocumentStatistics().GetUpdatedDocStat( false, true ).nAllPara; // 1: only content, no autostyle + pProgress->SetReference( nRef ); + pProgress->SetValue( 0 ); + } + } + + if( getExportFlags() & (SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT)) + { + //We depend on the correctness of OrdNums. + SwDrawModel* pModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel(); + if( pModel ) + pModel->GetPage( 0 )->RecalcObjOrdNums(); + } + + // adjust document class (eClass) + if (pDoc->getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT)) + { + eClass = XML_TEXT_GLOBAL; + + // additionally, we take care of the save-linked-sections-thingy + mbSaveLinkedSections = pDoc->getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS); + } + // MIB: 03/26/04: The Label information is saved in the settings, so + // we don't need it here. + // else: keep default pClass that we received + + rtl::Reference<SvXMLGraphicHelper> xGraphicStorageHandler; + if (!GetGraphicStorageHandler().is()) + { + xGraphicStorageHandler = SvXMLGraphicHelper::Create(SvXMLGraphicHelperMode::Write, GetImageFilterName()); + SetGraphicStorageHandler(xGraphicStorageHandler); + } + + rtl::Reference<SvXMLEmbeddedObjectHelper> xEmbeddedResolver; + if( !GetEmbeddedResolver().is() ) + { + SfxObjectShell *pPersist = pDoc->GetPersist(); + if( pPersist ) + { + xEmbeddedResolver = SvXMLEmbeddedObjectHelper::Create( + *pPersist, + SvXMLEmbeddedObjectHelperMode::Write ); + SetEmbeddedResolver( xEmbeddedResolver ); + } + } + + // set redline mode if we export STYLES or CONTENT, unless redline + // mode is taken care of outside (through info XPropertySet) + bool bSaveRedline = + bool( getExportFlags() & (SvXMLExportFlags::CONTENT|SvXMLExportFlags::STYLES) ); + if( bSaveRedline ) + { + // if the info property set has a ShowChanges property, + // then change tracking is taken care of on the outside, + // so we don't have to! + Reference<XPropertySet> rInfoSet = getExportInfo(); + if( rInfoSet.is() ) + { + bSaveRedline = ! rInfoSet->getPropertySetInfo()->hasPropertyByName( + "ShowChanges" ); + } + } + RedlineFlags nRedlineFlags = RedlineFlags::NONE; + SwRootFrame const*const pLayout(m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout()); + m_bSavedShowChanges = pLayout == nullptr || !pLayout->IsHideRedlines(); + if( bSaveRedline ) + { + // tdf#133487 call this once in flat-ODF case + uno::Reference<drawing::XDrawPageSupplier> const xDPS(GetModel(), uno::UNO_QUERY); + assert(xDPS.is()); + xmloff::FixZOrder(xDPS->getDrawPage(), sw::GetZOrderLayer(m_pDoc->getIDocumentDrawModelAccess())); + + // now save and switch redline mode + nRedlineFlags = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags( + ( nRedlineFlags & RedlineFlags::ShowMask ) | RedlineFlags::ShowInsert ); + } + + ErrCode nRet = SvXMLExport::exportDoc( eClass ); + + // now we can restore the redline mode (if we changed it previously) + if( bSaveRedline ) + { + pDoc->getIDocumentRedlineAccess().SetRedlineFlags( nRedlineFlags ); + } + + if (xGraphicStorageHandler) + xGraphicStorageHandler->dispose(); + xGraphicStorageHandler.clear(); + if( xEmbeddedResolver ) + xEmbeddedResolver->dispose(); + xEmbeddedResolver.clear(); + + OSL_ENSURE( !m_pTableLines, "there are table columns infos left" ); + + return nRet; +} + +XMLTextParagraphExport* SwXMLExport::CreateTextParagraphExport() +{ + return new SwXMLTextParagraphExport(*this, *GetAutoStylePool()); +} + +XMLShapeExport* SwXMLExport::CreateShapeExport() +{ + XMLShapeExport* pShapeExport = new XMLShapeExport( *this, XMLTextParagraphExport::CreateShapeExtPropMapper( *this ) ); + Reference < XDrawPageSupplier > xDPS( GetModel(), UNO_QUERY ); + if( xDPS.is() ) + { + Reference < XShapes > xShapes = xDPS->getDrawPage(); + pShapeExport->seekShapes( xShapes ); + } + + return pShapeExport; +} + +SwXMLExport::~SwXMLExport() +{ + DeleteTableLines(); + FinitItemExport(); +} + +void SwXMLExport::ExportFontDecls_() +{ + GetFontAutoStylePool(); // make sure the pool is created + SvXMLExport::ExportFontDecls_(); +} + +void SwXMLExport::GetViewSettings(Sequence<PropertyValue>& aProps) +{ + aProps.realloc(7); + // Currently exporting 9 properties + PropertyValue *pValue = aProps.getArray(); + + rtl::Reference< comphelper::IndexedPropertyValuesContainer > xBox = new comphelper::IndexedPropertyValuesContainer(); + pValue[0].Name = "Views"; + pValue[0].Value <<= uno::Reference< container::XIndexContainer >(xBox); + + SwDoc *pDoc = getDoc(); + const tools::Rectangle rRect = + pDoc->GetDocShell()->GetVisArea( ASPECT_CONTENT ); + bool bTwip = pDoc->GetDocShell()->GetMapUnit ( ) == MapUnit::MapTwip; + + OSL_ENSURE( bTwip, "Map unit for visible area is not in TWIPS!" ); + + pValue[1].Name = "ViewAreaTop"; + pValue[1].Value <<= bTwip ? convertTwipToMm100 ( rRect.Top() ) : rRect.Top(); + + pValue[2].Name = "ViewAreaLeft"; + pValue[2].Value <<= bTwip ? convertTwipToMm100 ( rRect.Left() ) : rRect.Left(); + + pValue[3].Name = "ViewAreaWidth"; + pValue[3].Value <<= bTwip ? convertTwipToMm100 ( rRect.GetWidth() ) : rRect.GetWidth(); + + pValue[4].Name = "ViewAreaHeight"; + pValue[4].Value <<= bTwip ? convertTwipToMm100 ( rRect.GetHeight() ) : rRect.GetHeight(); + + // "show redline mode" cannot simply be read from the document + // since it gets changed during execution. If it's in the info + // XPropertySet, we take it from there. + bool bShowRedlineChanges = m_bSavedShowChanges; + Reference<XPropertySet> xInfoSet( getExportInfo() ); + if ( xInfoSet.is() ) + { + static constexpr OUString sShowChanges( u"ShowChanges"_ustr ); + if( xInfoSet->getPropertySetInfo()->hasPropertyByName( sShowChanges ) ) + { + bShowRedlineChanges = *o3tl::doAccess<bool>(xInfoSet-> + getPropertyValue( sShowChanges )); + } + } + + pValue[5].Name = "ShowRedlineChanges"; + pValue[5].Value <<= bShowRedlineChanges; + + pValue[6].Name = "InBrowseMode"; + pValue[6].Value <<= pDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE); +} + +void SwXMLExport::GetConfigurationSettings( Sequence < PropertyValue >& rProps) +{ + Reference< XMultiServiceFactory > xFac( GetModel(), UNO_QUERY ); + if (!xFac.is()) + return; + + Reference< XPropertySet > xProps( xFac->createInstance("com.sun.star.document.Settings"), UNO_QUERY ); + if (!xProps.is()) + return; + + static const std::initializer_list<std::u16string_view> vOmitFalseValues = { + u"DoNotBreakWrappedTables", + u"AllowTextAfterFloatingTableBreak", + }; + SvXMLUnitConverter::convertPropertySet( rProps, xProps, &vOmitFalseValues ); + + // tdf#144532 if NoEmbDataSet was set, to indicate not to write an embedded + // database for the case of a temporary mail merge preview document, then + // also filter out the "EmbeddedDatabaseName" property from the document + // settings so that when the temp mailmerge preview document is closed it + // doesn't unregister the database of the same name which was registered by + // the document this is a copy of + Reference<XPropertySet> rInfoSet = getExportInfo(); + + if (!rInfoSet.is() || !rInfoSet->getPropertySetInfo()->hasPropertyByName(u"NoEmbDataSet"_ustr)) + return; + + Any aAny = rInfoSet->getPropertyValue(u"NoEmbDataSet"_ustr); + bool bNoEmbDataSet = *o3tl::doAccess<bool>(aAny); + if (!bNoEmbDataSet) + return; + + Sequence<PropertyValue> aFilteredProps(rProps.getLength()); + auto aFilteredPropsRange = asNonConstRange(aFilteredProps); + sal_Int32 nFilteredPropLen = 0; + for (sal_Int32 i = 0; i < rProps.getLength(); ++i) + { + if (rProps[i].Name == "EmbeddedDatabaseName") + continue; + aFilteredPropsRange[nFilteredPropLen] = rProps[i]; + ++nFilteredPropLen; + } + aFilteredProps.realloc(nFilteredPropLen); + std::swap(rProps, aFilteredProps); +} + +sal_Int32 SwXMLExport::GetDocumentSpecificSettings( std::vector< SettingsGroup >& _out_rSettings ) +{ + // the only doc-specific settings group we know so far are the XForms settings + uno::Sequence<beans::PropertyValue> aXFormsSettings; + Reference< XFormsSupplier > xXFormsSupp( GetModel(), UNO_QUERY ); + Reference< XNameAccess > xXForms; + if ( xXFormsSupp.is() ) + xXForms = xXFormsSupp->getXForms().get(); + if ( xXForms.is() ) + { + getXFormsSettings( xXForms, aXFormsSettings ); + _out_rSettings.emplace_back( XML_XFORM_MODEL_SETTINGS, aXFormsSettings ); + } + + return aXFormsSettings.getLength() + SvXMLExport::GetDocumentSpecificSettings( _out_rSettings ); +} + +void SwXMLExport::SetBodyAttributes() +{ + // export use of soft page breaks + SwDoc *pDoc = getDoc(); + if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && + pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->GetPageCount() > 1 ) + { + OUStringBuffer sBuffer; + ::sax::Converter::convertBool(sBuffer, true); + AddAttribute(XML_NAMESPACE_TEXT, XML_USE_SOFT_PAGE_BREAKS, + sBuffer.makeStringAndClear()); + } +} + +void SwXMLExport::ExportContent_() +{ + // export forms + Reference<XDrawPageSupplier> xDrawPageSupplier(GetModel(), UNO_QUERY); + if (xDrawPageSupplier.is()) + { + // export only if we actually have elements + Reference<XDrawPage> xPage = xDrawPageSupplier->getDrawPage(); + if (xPage.is()) + { + // prevent export of form controls which are embedded in mute sections + GetTextParagraphExport()->PreventExportOfControlsInMuteSections( + xPage, GetFormExport() ); + + // #i36597# + if ( xmloff::OFormLayerXMLExport::pageContainsForms( xPage ) || GetFormExport()->documentContainsXForms() ) + { + ::xmloff::OOfficeFormsExport aOfficeForms(*this); + + GetFormExport()->exportXForms(); + + GetFormExport()->seekPage(xPage); + GetFormExport()->exportForms(xPage); + } + } + } + + Reference<XPropertySet> xPropSet(GetModel(), UNO_QUERY); + if (xPropSet.is()) + { + Any aAny = xPropSet->getPropertyValue( "TwoDigitYear" ); + aAny <<= sal_Int16(1930); + + sal_Int16 nYear = 0; + aAny >>= nYear; + if (nYear != 1930 ) + { + AddAttribute(XML_NAMESPACE_TABLE, XML_NULL_YEAR, OUString::number(nYear)); + SvXMLElementExport aCalcSettings(*this, XML_NAMESPACE_TABLE, XML_CALCULATION_SETTINGS, true, true); + } + } + + GetTextParagraphExport()->exportTrackedChanges( false ); + GetTextParagraphExport()->exportTextDeclarations(); + Reference < XTextDocument > xTextDoc( GetModel(), UNO_QUERY ); + Reference < XText > xText = xTextDoc->getText(); + + GetTextParagraphExport()->exportFramesBoundToPage( m_bShowProgress ); + GetTextParagraphExport()->exportText( xText, m_bShowProgress ); +} + +SwDoc* SwXMLExport::getDoc() +{ + if( m_pDoc != nullptr ) + return m_pDoc; + Reference < XTextDocument > xTextDoc( GetModel(), UNO_QUERY ); + if (!xTextDoc) + { + SAL_WARN("sw.filter", "Problem of mismatching filter for export."); + return nullptr; + } + + Reference < XText > xText = xTextDoc->getText(); + SwXText* pText = dynamic_cast<SwXText*>(xText.get()); + assert( pText != nullptr ); + m_pDoc = pText->GetDoc(); + assert( m_pDoc != nullptr ); + return m_pDoc; +} + +const SwDoc* SwXMLExport::getDoc() const +{ + return const_cast< SwXMLExport* >( this )->getDoc(); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLExporter", + SvXMLExportFlags::ALL)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLStylesExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLStylesExporter", + SvXMLExportFlags::STYLES | SvXMLExportFlags::MASTERSTYLES | SvXMLExportFlags::AUTOSTYLES | + SvXMLExportFlags::FONTDECLS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLContentExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLContentExporter", + SvXMLExportFlags::SCRIPTS | SvXMLExportFlags::CONTENT | SvXMLExportFlags::AUTOSTYLES | + SvXMLExportFlags::FONTDECLS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLMetaExporter", + SvXMLExportFlags::META)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLSettingsExporter", + SvXMLExportFlags::SETTINGS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisExporter", + SvXMLExportFlags::ALL | SvXMLExportFlags::OASIS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisStylesExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisStylesExporter", + SvXMLExportFlags::STYLES | SvXMLExportFlags::MASTERSTYLES | SvXMLExportFlags::AUTOSTYLES | + SvXMLExportFlags::FONTDECLS | SvXMLExportFlags::OASIS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisContentExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisContentExporter", + SvXMLExportFlags::AUTOSTYLES | SvXMLExportFlags::CONTENT | SvXMLExportFlags::SCRIPTS | + SvXMLExportFlags::FONTDECLS | SvXMLExportFlags::OASIS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisMetaExporter", + SvXMLExportFlags::META | SvXMLExportFlags::OASIS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisSettingsExporter", + SvXMLExportFlags::SETTINGS | SvXMLExportFlags::OASIS)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlexp.hxx b/sw/source/filter/xml/xmlexp.hxx new file mode 100644 index 0000000000..6e095396db --- /dev/null +++ b/sw/source/filter/xml/xmlexp.hxx @@ -0,0 +1,146 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLEXP_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLEXP_HXX + +#include <xmloff/xmlexp.hxx> +#include "xmlitmap.hxx" +#include <xmloff/xmltoken.hxx> + +#include <optional> +#include <string_view> +#include <vector> + +class SwDoc; +class SwFormat; +class SwFrameFormat; +class SvXMLUnitConverter; +class SvXMLExportItemMapper; +class SvXMLAutoStylePoolP; +class SwTableLine; +class SwTableLines; +class SwTableBox; +class SwXMLTableColumn_Impl; +class SwXMLTableLines_Impl; +class SwXMLTableColumnsSortByWidth_Impl; +class SwXMLTableFrameFormatsSort_Impl; +class SwXMLTableInfo_Impl; +class SwTableNode; +class XMLPropertySetMapper; +class SwXMLTableLines_Impl; + +typedef std::vector< SwXMLTableLines_Impl* > SwXMLTableLinesCache_Impl; + +class SwXMLExport : public SvXMLExport +{ + std::unique_ptr<SvXMLUnitConverter> m_pTwipUnitConverter; + std::unique_ptr<SvXMLExportItemMapper> m_pTableItemMapper; + std::unique_ptr<SwXMLTableLinesCache_Impl> m_pTableLines; + + SvXMLItemMapEntriesRef m_xTableItemMap; + SvXMLItemMapEntriesRef m_xTableRowItemMap; + SvXMLItemMapEntriesRef m_xTableCellItemMap; + + bool m_bBlock : 1; // export text block? + bool m_bShowProgress : 1; + bool m_bSavedShowChanges : 1; + + SwDoc* m_pDoc; // cached for getDoc() + + void InitItemExport(); + void FinitItemExport(); + void ExportTableLinesAutoStyles( const SwTableLines& rLines, + sal_uInt32 nAbsWidth, + sal_uInt32 nBaseWidth, + std::u16string_view rNamePrefix, + SwXMLTableColumnsSortByWidth_Impl& rExpCols, + SwXMLTableFrameFormatsSort_Impl& rExpRows, + SwXMLTableFrameFormatsSort_Impl& rExpCells, + SwXMLTableInfo_Impl& rTableInfo, + bool bTop=false ); + + void ExportFormat(const SwFormat& rFormat, enum ::xmloff::token::XMLTokenEnum eClass, + ::std::optional<OUString> const oStyleName); + void ExportTableFormat( const SwFrameFormat& rFormat, sal_uInt32 nAbsWidth ); + + void ExportTableColumnStyle( const SwXMLTableColumn_Impl& rCol ); + void ExportTableBox( const SwTableBox& rBox, sal_uInt32 nColSpan, sal_uInt32 nRowSpan, + SwXMLTableInfo_Impl& rTableInfo ); + void ExportTableLine( const SwTableLine& rLine, + const SwXMLTableLines_Impl& rLines, + SwXMLTableInfo_Impl& rTableInfo ); + void ExportTableLines( const SwTableLines& rLines, + SwXMLTableInfo_Impl& rTableInfo, + sal_uInt32 nHeaderRows = 0 ); + + void exportTheme(); + + virtual void ExportMeta_() override; + virtual void ExportFontDecls_() override; + virtual void ExportStyles_( bool bUsed ) override; + virtual void ExportAutoStyles_() override; + virtual void ExportMasterStyles_() override; + virtual void SetBodyAttributes() override; + virtual void ExportContent_() override; + virtual void GetViewSettings(css::uno::Sequence<css::beans::PropertyValue>& aProps) override; + virtual void GetConfigurationSettings(css::uno::Sequence<css::beans::PropertyValue>& aProps) override; + virtual sal_Int32 GetDocumentSpecificSettings( std::vector< SettingsGroup >& _out_rSettings ) override; + +private: + void DeleteTableLines(); +protected: + + virtual XMLTextParagraphExport* CreateTextParagraphExport() override; + virtual SvXMLAutoStylePoolP* CreateAutoStylePool() override; + virtual XMLPageExport* CreatePageExport() override; + virtual XMLShapeExport* CreateShapeExport() override; + virtual XMLFontAutoStylePool* CreateFontAutoStylePool() override; + +public: + SwXMLExport( + const css::uno::Reference< css::uno::XComponentContext >& rContext, + OUString const & implementationName, SvXMLExportFlags nExportFlags); + + virtual ~SwXMLExport() override; + + void collectAutoStyles() override; + + virtual ErrCode exportDoc( enum ::xmloff::token::XMLTokenEnum eClass = ::xmloff::token::XML_TOKEN_INVALID ) override; + + inline const SvXMLUnitConverter& GetTwipUnitConverter() const; + + void ExportTableAutoStyles( const SwTableNode& rTableNd ); + void ExportTable( const SwTableNode& rTableNd ); + + bool IsShowProgress() const { return m_bShowProgress; } + void SetShowProgress( bool b ) { m_bShowProgress = b; } + + const SwDoc* getDoc() const; + SwDoc* getDoc(); +}; + +inline const SvXMLUnitConverter& SwXMLExport::GetTwipUnitConverter() const +{ + return *m_pTwipUnitConverter; +} + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLEXP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlexpit.cxx b/sw/source/filter/xml/xmlexpit.cxx new file mode 100644 index 0000000000..a39d12228c --- /dev/null +++ b/sw/source/filter/xml/xmlexpit.cxx @@ -0,0 +1,1137 @@ +/* -*- 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 "xmlexpit.hxx" + +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> +#include <sax/tools/converter.hxx> +#include <svl/poolitem.hxx> +#include <svl/itemset.hxx> +#include <utility> +#include <xmloff/xmluconv.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/prhdlfac.hxx> +#include <xmloff/xmltypes.hxx> +#include <editeng/xmlcnitm.hxx> +#include <xmloff/xmlexp.hxx> +#include <xmloff/xmlprhdl.hxx> +#include <editeng/memberids.h> +#include <hintids.hxx> +#include <unomid.h> +#include <svx/unomid.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/prntitem.hxx> +#include <fmtpdsc.hxx> +#include <fmtornt.hxx> +#include <fmtfsize.hxx> + +#include "xmlithlp.hxx" + +#include <fmtrowsplt.hxx> + +using ::editeng::SvxBorderLine; +using namespace ::com::sun::star; +using namespace ::xmloff::token; +using uno::Any; + +// fills the given attribute list with the items in the given set +void SvXMLExportItemMapper::exportXML( const SvXMLExport& rExport, + comphelper::AttributeList& rAttrList, + const SfxItemSet& rSet, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + std::vector<sal_uInt16> *pIndexArray ) const +{ + const sal_uInt16 nCount = mrMapEntries->getCount(); + sal_uInt16 nIndex = 0; + + while( nIndex < nCount ) + { + SvXMLItemMapEntry const & rEntry = mrMapEntries->getByIndex( nIndex ); + + // we have a valid map entry here, so lets use it... + if( 0 == (rEntry.nMemberId & MID_SW_FLAG_NO_ITEM_EXPORT) ) + { + const SfxPoolItem* pItem = GetItem( rSet, rEntry.nWhichId ); + // do we have an item? + if(pItem) + { + if( 0 != (rEntry.nMemberId & MID_SW_FLAG_ELEMENT_ITEM_EXPORT) ) + { + // element items do not add any properties, + // we export it later + if( pIndexArray ) + pIndexArray->push_back( nIndex ); + + } + else + { + exportXML( rExport, rAttrList, *pItem, rEntry, rUnitConverter, + rNamespaceMap, &rSet ); + } + } + } + else + { + OSL_FAIL( "no item not handled in xml export" ); + } + nIndex++; + } +} + +void SvXMLExportItemMapper::exportXML(const SvXMLExport&, + comphelper::AttributeList& rAttrList, + const SfxPoolItem& rItem, + const SvXMLItemMapEntry& rEntry, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + const SfxItemSet *pSet ) const +{ + if( 0 != (rEntry.nMemberId & MID_SW_FLAG_SPECIAL_ITEM_EXPORT) ) + { + if( dynamic_cast<const SwFormatRowSplit*>( &rItem) != nullptr ) + { + OUString aValue; + bool bAddAttribute = true; + if( rEntry.nNameSpace == XML_NAMESPACE_STYLE ) + { + bAddAttribute = false; + } + else + { + OUStringBuffer aOut; + const SfxBoolItem* pSplit = dynamic_cast<const SfxBoolItem*>( &rItem ); + assert(pSplit && "Wrong Which-ID"); + const sal_uInt16 eEnum = (pSplit && pSplit->GetValue()) ? 1 : 0; + SvXMLUnitConverter::convertEnum( aOut, eEnum, aXML_KeepTogetherType ); + aValue = aOut.makeStringAndClear(); + } + if( bAddAttribute ) + { + const OUString sName( rNamespaceMap.GetQNameByKey( rEntry.nNameSpace, + GetXMLToken(rEntry.eLocalName) ) ); + rAttrList.AddAttribute( sName, aValue ); + } + } + + if (const SvXMLAttrContainerItem *pUnknown = dynamic_cast<const SvXMLAttrContainerItem*>(&rItem)) + { + std::unique_ptr<SvXMLNamespaceMap> pNewNamespaceMap; + const SvXMLNamespaceMap *pNamespaceMap = &rNamespaceMap; + + const sal_uInt16 nCount = pUnknown->GetAttrCount(); + for( sal_uInt16 i=0; i < nCount; i++ ) + { + const OUString sPrefix( pUnknown->GetAttrPrefix( i ) ); + if( !sPrefix.isEmpty() ) + { + const OUString sNamespace( pUnknown->GetAttrNamespace( i ) ); + + // if the prefix isn't defined yet or has another meaning, + // we have to redefine it now. + const sal_uInt16 nIdx = pNamespaceMap->GetIndexByPrefix( sPrefix ); + if( USHRT_MAX == nIdx || + pNamespaceMap->GetNameByIndex( nIdx ) != sNamespace ) + { + if( !pNewNamespaceMap ) + { + pNewNamespaceMap.reset( + new SvXMLNamespaceMap( rNamespaceMap )); + pNamespaceMap = pNewNamespaceMap.get(); + } + pNewNamespaceMap->Add( sPrefix, sNamespace ); + + rAttrList.AddAttribute( GetXMLToken(XML_XMLNS) + ":" + sPrefix, + sNamespace ); + } + + rAttrList.AddAttribute( sPrefix + ":" + pUnknown->GetAttrLName(i), + pUnknown->GetAttrValue(i) ); + } + else + { + rAttrList.AddAttribute( pUnknown->GetAttrLName(i), + pUnknown->GetAttrValue(i) ); + } + } + } + else + { + handleSpecialItem( rAttrList, rEntry, rItem, rUnitConverter, + rNamespaceMap, pSet ); + } + } + else if( 0 == (rEntry.nMemberId & MID_SW_FLAG_ELEMENT_ITEM_EXPORT) ) + { + bool bDone = false; + switch (rItem.Which()) + { + case RES_FRAMEDIR: + { + // Write bt-lr and tb-rl90 to the extension namespace, handle other values + // below. + auto pDirection = static_cast<const SvxFrameDirectionItem*>(&rItem); + if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT + && pDirection->GetValue() == SvxFrameDirection::Vertical_LR_BT) + { + const OUString sName(rNamespaceMap.GetQNameByKey( + XML_NAMESPACE_LO_EXT, GetXMLToken(XML_WRITING_MODE))); + rAttrList.AddAttribute(sName, GetXMLToken(XML_BT_LR)); + } + if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT + || pDirection->GetValue() == SvxFrameDirection::Vertical_LR_BT) + bDone = true; + + if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT + && pDirection->GetValue() == SvxFrameDirection::Vertical_RL_TB90) + { + const OUString sName(rNamespaceMap.GetQNameByKey( + XML_NAMESPACE_LO_EXT, GetXMLToken(XML_WRITING_MODE))); + rAttrList.AddAttribute(sName, GetXMLToken(XML_TB_RL90)); + } + if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT + || pDirection->GetValue() == SvxFrameDirection::Vertical_RL_TB90) + bDone = true; + break; + } + } + + if (!bDone) + { + OUString aValue; + if( QueryXMLValue(rItem, aValue, + static_cast< sal_uInt16 >( + rEntry.nMemberId & MID_SW_FLAG_MASK ), + rUnitConverter ) ) + { + const OUString sName( + rNamespaceMap.GetQNameByKey( rEntry.nNameSpace, + GetXMLToken(rEntry.eLocalName))); + rAttrList.AddAttribute( sName, aValue ); + } + } + } +} + +void SvXMLExportItemMapper::exportElementItems( + SvXMLExport& rExport, + const SfxItemSet &rSet, + const std::vector<sal_uInt16> &rIndexArray ) const +{ + const size_t nCount = rIndexArray.size(); + + bool bItemsExported = false; + for( size_t nIndex = 0; nIndex < nCount; ++nIndex ) + { + const sal_uInt16 nElement = rIndexArray[ nIndex ]; + SvXMLItemMapEntry const & rEntry = mrMapEntries->getByIndex( nElement ); + OSL_ENSURE( 0 != (rEntry.nMemberId & MID_SW_FLAG_ELEMENT_ITEM_EXPORT), + "wrong mid flag!" ); + + const SfxPoolItem* pItem = GetItem( rSet, rEntry.nWhichId ); + // do we have an item? + if(pItem) + { + rExport.IgnorableWhitespace(); + handleElementItem( rEntry, *pItem ); + bItemsExported = true; + } + } + + if( bItemsExported ) + rExport.IgnorableWhitespace(); +} + +/** returns the item with the given WhichId from the given ItemSet if it's + set +*/ +const SfxPoolItem* SvXMLExportItemMapper::GetItem( const SfxItemSet& rSet, + sal_uInt16 nWhichId) +{ + // first get item from itemset + const SfxPoolItem* pItem; + SfxItemState eState = rSet.GetItemState( nWhichId, false, &pItem ); + + if( SfxItemState::SET == eState ) + { + return pItem; + } + else + { + return nullptr; + } +} + +SvXMLExportItemMapper::SvXMLExportItemMapper( SvXMLItemMapEntriesRef rMapEntries ) + : mrMapEntries(std::move(rMapEntries)) +{ +} + +SvXMLExportItemMapper::~SvXMLExportItemMapper() +{ +} + +void SvXMLExportItemMapper::exportXML( SvXMLExport& rExport, + const SfxItemSet& rSet, + const SvXMLUnitConverter& rUnitConverter, + XMLTokenEnum ePropToken ) const +{ + std::vector<sal_uInt16> aIndexArray; + + exportXML( rExport, rExport.GetAttrList(), rSet, rUnitConverter, + rExport.GetNamespaceMap(), &aIndexArray ); + + if( rExport.GetAttrList().getLength() > 0 || !aIndexArray.empty() ) + { + rExport.IgnorableWhitespace(); + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_STYLE, ePropToken, + false, false ); + exportElementItems( rExport, rSet, aIndexArray ); + } +} + +/** this method is called for every item that has the + MID_SW_FLAG_SPECIAL_ITEM_EXPORT flag set */ +void SvXMLExportItemMapper::handleSpecialItem( comphelper::AttributeList& /*rAttrList*/, + const SvXMLItemMapEntry& /*rEntry*/, + const SfxPoolItem& /*rItem*/, + const SvXMLUnitConverter& /*rUnitConverter*/, + const SvXMLNamespaceMap& /*rNamespaceMap*/, + const SfxItemSet* /*pSet*/ /* = NULL */ ) const +{ + OSL_FAIL( "special item not handled in xml export" ); +} + +/** this method is called for every item that has the + MID_SW_FLAG_ELEMENT_EXPORT flag set */ +void SvXMLExportItemMapper::handleElementItem( + const SvXMLItemMapEntry& /*rEntry*/, + const SfxPoolItem& /*rItem*/ ) const +{ + OSL_FAIL( "element item not handled in xml export" ); +} + +static bool lcl_isOdfDoubleLine( const SvxBorderLine* pLine ) +{ + bool bIsOdfDouble = false; + switch (pLine->GetBorderLineStyle()) + { + case SvxBorderLineStyle::DOUBLE: + case SvxBorderLineStyle::THINTHICK_SMALLGAP: + case SvxBorderLineStyle::THINTHICK_MEDIUMGAP: + case SvxBorderLineStyle::THINTHICK_LARGEGAP: + case SvxBorderLineStyle::THICKTHIN_SMALLGAP: + case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP: + case SvxBorderLineStyle::THICKTHIN_LARGEGAP: + bIsOdfDouble = true; + break; + default: + break; + } + return bIsOdfDouble; +} + +bool SvXMLExportItemMapper::QueryXMLValue( + const SfxPoolItem& rItem, + OUString& rValue, + sal_uInt16 nMemberId, + const SvXMLUnitConverter& rUnitConverter ) +{ + bool bOk = false; + OUStringBuffer aOut; + + switch ( rItem.Which() ) + { + case RES_MARGIN_FIRSTLINE: + case RES_MARGIN_TEXTLEFT: + case RES_MARGIN_RIGHT: + assert(false); // is only called for frame formats? + break; + + case RES_LR_SPACE: + { + const SvxLRSpaceItem& rLRSpace = dynamic_cast<const SvxLRSpaceItem&>(rItem); + + bOk = true; + switch( nMemberId ) + { + case MID_L_MARGIN: + if (rLRSpace.GetPropLeft() != 100) + { + ::sax::Converter::convertPercent( + aOut, rLRSpace.GetPropLeft() ); + } + else + { + rUnitConverter.convertMeasureToXML( + aOut, rLRSpace.GetLeft() ); + } + break; + + case MID_R_MARGIN: + if (rLRSpace.GetPropRight() != 100) + { + ::sax::Converter::convertPercent( + aOut, rLRSpace.GetPropRight() ); + } + else + { + rUnitConverter.convertMeasureToXML( + aOut, rLRSpace.GetRight() ); + } + break; + + case MID_FIRST_AUTO: + if (rLRSpace.IsAutoFirst()) + { + ::sax::Converter::convertBool( + aOut, rLRSpace.IsAutoFirst() ); + } + else + bOk = false; + break; + + case MID_FIRST_LINE_INDENT: + if (!rLRSpace.IsAutoFirst()) + { + if (rLRSpace.GetPropTextFirstLineOffset() != 100) + { + ::sax::Converter::convertPercent( + aOut, rLRSpace.GetPropTextFirstLineOffset() ); + } + else + { + rUnitConverter.convertMeasureToXML( + aOut, rLRSpace.GetTextFirstLineOffset() ); + } + } + else + bOk = false; + break; + + default: + OSL_FAIL( "unknown member id!"); + bOk = false; + break; + } + } + break; + + case RES_UL_SPACE: + { + const SvxULSpaceItem& rULSpace = dynamic_cast<const SvxULSpaceItem&>(rItem); + + switch( nMemberId ) + { + case MID_UP_MARGIN: + if (rULSpace.GetPropUpper() != 100) + { + ::sax::Converter::convertPercent( + aOut, rULSpace.GetPropUpper() ); + } + else + { + rUnitConverter.convertMeasureToXML( + aOut, rULSpace.GetUpper() ); + } + break; + + case MID_LO_MARGIN: + if (rULSpace.GetPropLower() != 100) + { + ::sax::Converter::convertPercent( + aOut, rULSpace.GetPropLower() ); + } + else + { + rUnitConverter.convertMeasureToXML( + aOut, rULSpace.GetLower() ); + } + break; + + default: + OSL_FAIL("unknown MemberId"); + }; + + bOk = true; + } + break; + + case RES_SHADOW: + { + const SvxShadowItem* pShadow = dynamic_cast<const SvxShadowItem*>( &rItem ); + assert(pShadow && "Wrong Which-ID"); + if (pShadow) + { + sal_Int32 nX = 1, nY = 1; + switch( pShadow->GetLocation() ) + { + case SvxShadowLocation::TopLeft: + nX = -1; + nY = -1; + break; + case SvxShadowLocation::TopRight: + nY = -1; + break; + case SvxShadowLocation::BottomLeft: + nX = -1; + break; + case SvxShadowLocation::BottomRight: + break; + case SvxShadowLocation::NONE: + default: + rValue = GetXMLToken(XML_NONE); + return true; + } + + nX *= pShadow->GetWidth(); + nY *= pShadow->GetWidth(); + + ::sax::Converter::convertColor(aOut, pShadow->GetColor()); + aOut.append( ' ' ); + rUnitConverter.convertMeasureToXML( aOut, nX ); + aOut.append( ' ' ); + rUnitConverter.convertMeasureToXML( aOut, nY ); + + bOk = true; + } + } + break; + + case RES_BOX: + { + const SvxBoxItem* pBox = dynamic_cast<const SvxBoxItem*>( &rItem ); + assert(pBox && "Wrong Which-ID"); + if (pBox) + { + /** + xml -> MemberId + + border-padding ALL_BORDER_PADDING + border-padding-before LEFT_BORDER_PADDING + border-padding-after RIGHT_BORDER_PADDING + border-padding-start TOP_BORDER_PADDING + border-padding-end BOTTOM_BORDER_PADDING + + border ALL_BORDER + border-before LEFT_BORDER + border-after RIGHT_BORDER + border-start TOP_BORDER + border-end BOTTOM_BORDER + + border-line-width ALL_BORDER_LINE_WIDTH + border-line-width-before LEFT_BORDER_LINE_WIDTH + border-line-width-after RIGHT_BORDER_LINE_WIDTH + border-line-width-start TOP_BORDER_LINE_WIDTH + border-line-width-end BOTTOM_BORDER_LINE_WIDTH + */ + + const SvxBorderLine* pLeft = pBox->GetLeft(); + const SvxBorderLine* pRight = pBox->GetRight(); + const SvxBorderLine* pTop = pBox->GetTop(); + const SvxBorderLine* pBottom = pBox->GetBottom(); + const sal_uInt16 nTopDist = pBox->GetDistance( SvxBoxItemLine::TOP ); + const sal_uInt16 nBottomDist = pBox->GetDistance( SvxBoxItemLine::BOTTOM ); + const sal_uInt16 nLeftDist = pBox->GetDistance( SvxBoxItemLine::LEFT ); + const sal_uInt16 nRightDist = pBox->GetDistance( SvxBoxItemLine::RIGHT ); + + // check if we need to export it + switch( nMemberId ) + { + case ALL_BORDER_PADDING: + case LEFT_BORDER_PADDING: + case RIGHT_BORDER_PADDING: + case TOP_BORDER_PADDING: + case BOTTOM_BORDER_PADDING: + { + bool bEqual = nLeftDist == nRightDist && + nLeftDist == nTopDist && + nLeftDist == nBottomDist; + // don't export individual paddings if all paddings are equal and + // don't export all padding if some paddings are not equal + if( (bEqual && ALL_BORDER_PADDING != nMemberId) || + (!bEqual && ALL_BORDER_PADDING == nMemberId) ) + return false; + } + break; + case ALL_BORDER: + case LEFT_BORDER: + case RIGHT_BORDER: + case TOP_BORDER: + case BOTTOM_BORDER: + { + bool bEqual = ( nullptr == pTop && nullptr == pBottom && + nullptr == pLeft && nullptr == pRight ) || + ( pTop && pBottom && pLeft && pRight && + *pTop == *pBottom && *pTop == *pLeft && + *pTop == *pRight ); + + // don't export individual borders if all are the same and + // don't export all borders if some are not equal + if( (bEqual && ALL_BORDER != nMemberId) || + (!bEqual && ALL_BORDER == nMemberId) ) + return false; + } + break; + case ALL_BORDER_LINE_WIDTH: + case LEFT_BORDER_LINE_WIDTH: + case RIGHT_BORDER_LINE_WIDTH: + case TOP_BORDER_LINE_WIDTH: + case BOTTOM_BORDER_LINE_WIDTH: + { + // if no line is set, there is nothing to export + if( !pTop && !pBottom && !pLeft && !pRight ) + return false; + + bool bEqual = nullptr != pTop && + nullptr != pBottom && + nullptr != pLeft && + nullptr != pRight; + + if( bEqual ) + { + const sal_uInt16 nDistance = pTop->GetDistance(); + const sal_uInt16 nInWidth = pTop->GetInWidth(); + const sal_uInt16 nOutWidth = pTop->GetOutWidth(); + const tools::Long nWidth = pTop->GetWidth(); + + bEqual = nDistance == pLeft->GetDistance() && + nInWidth == pLeft->GetInWidth() && + nOutWidth == pLeft->GetOutWidth() && + nWidth == pLeft->GetWidth() && + nDistance == pRight->GetDistance() && + nInWidth == pRight->GetInWidth() && + nOutWidth == pRight->GetOutWidth() && + nWidth == pRight->GetWidth() && + nDistance == pBottom->GetDistance() && + nInWidth == pBottom->GetInWidth() && + nOutWidth == pBottom->GetOutWidth() && + nWidth == pBottom->GetWidth(); + } + + switch( nMemberId ) + { + case ALL_BORDER_LINE_WIDTH: + if( !bEqual || pTop->GetDistance() == 0 || + !lcl_isOdfDoubleLine( pTop ) ) + return false; + break; + case LEFT_BORDER_LINE_WIDTH: + if( bEqual || nullptr == pLeft || + 0 == pLeft->GetDistance() || + !lcl_isOdfDoubleLine( pLeft ) ) + return false; + break; + case RIGHT_BORDER_LINE_WIDTH: + if( bEqual || nullptr == pRight || + 0 == pRight->GetDistance() || + !lcl_isOdfDoubleLine( pRight ) ) + return false; + break; + case TOP_BORDER_LINE_WIDTH: + if( bEqual || nullptr == pTop || + 0 == pTop->GetDistance() || + !lcl_isOdfDoubleLine( pTop ) ) + return false; + break; + case BOTTOM_BORDER_LINE_WIDTH: + if( bEqual || nullptr == pBottom || + 0 == pBottom->GetDistance() || + !lcl_isOdfDoubleLine( pBottom ) ) + return false; + break; + } + } + break; + } + + // now export it export + switch( nMemberId ) + { + // padding + case ALL_BORDER_PADDING: + case LEFT_BORDER_PADDING: + rUnitConverter.convertMeasureToXML( aOut, nLeftDist ); + break; + case RIGHT_BORDER_PADDING: + rUnitConverter.convertMeasureToXML( aOut, nRightDist ); + break; + case TOP_BORDER_PADDING: + rUnitConverter.convertMeasureToXML( aOut, nTopDist ); + break; + case BOTTOM_BORDER_PADDING: + rUnitConverter.convertMeasureToXML( aOut, nBottomDist ); + break; + + // border + case ALL_BORDER: + case LEFT_BORDER: + case RIGHT_BORDER: + case TOP_BORDER: + case BOTTOM_BORDER: + { + const SvxBorderLine* pLine; + switch( nMemberId ) + { + case ALL_BORDER: + case LEFT_BORDER: + pLine = pLeft; + break; + case RIGHT_BORDER: + pLine = pRight; + break; + case TOP_BORDER: + pLine = pTop; + break; + case BOTTOM_BORDER: + pLine = pBottom; + break; + default: + pLine = nullptr; + break; + } + + if( nullptr != pLine ) + { + sal_Int32 nWidth = pLine->GetWidth(); + + enum XMLTokenEnum eStyle = XML_SOLID; + bool bNoBorder = false; + switch (pLine->GetBorderLineStyle()) + { + case SvxBorderLineStyle::SOLID: + eStyle = XML_SOLID; + break; + case SvxBorderLineStyle::DOTTED: + eStyle = XML_DOTTED; + break; + case SvxBorderLineStyle::DASHED: + eStyle = XML_DASHED; + break; + case SvxBorderLineStyle::FINE_DASHED: + eStyle = XML_FINE_DASHED; + break; + case SvxBorderLineStyle::DASH_DOT: + eStyle = XML_DASH_DOT; + break; + case SvxBorderLineStyle::DASH_DOT_DOT: + eStyle = XML_DASH_DOT_DOT; + break; + case SvxBorderLineStyle::DOUBLE_THIN: + eStyle = XML_DOUBLE_THIN; + break; + case SvxBorderLineStyle::DOUBLE: + case SvxBorderLineStyle::THINTHICK_SMALLGAP: + case SvxBorderLineStyle::THINTHICK_MEDIUMGAP: + case SvxBorderLineStyle::THINTHICK_LARGEGAP: + case SvxBorderLineStyle::THICKTHIN_SMALLGAP: + case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP: + case SvxBorderLineStyle::THICKTHIN_LARGEGAP: + eStyle = XML_DOUBLE; + break; + case SvxBorderLineStyle::EMBOSSED: + eStyle = XML_RIDGE; + break; + case SvxBorderLineStyle::ENGRAVED: + eStyle = XML_GROOVE; + break; + case SvxBorderLineStyle::INSET: + eStyle = XML_INSET; + break; + case SvxBorderLineStyle::OUTSET: + eStyle = XML_OUTSET; + break; + default: + bNoBorder = true; + } + + if ( !bNoBorder ) + { + ::sax::Converter::convertMeasure(aOut, nWidth, + util::MeasureUnit::TWIP, + util::MeasureUnit::POINT); + aOut.append( " " + + GetXMLToken( eStyle ) + + " " ); + ::sax::Converter::convertColor(aOut, + pLine->GetColor()); + } + } + else + { + aOut.append( GetXMLToken(XML_NONE) ); + } + } + break; + + // width + case ALL_BORDER_LINE_WIDTH: + case LEFT_BORDER_LINE_WIDTH: + case RIGHT_BORDER_LINE_WIDTH: + case TOP_BORDER_LINE_WIDTH: + case BOTTOM_BORDER_LINE_WIDTH: + const SvxBorderLine* pLine; + switch( nMemberId ) + { + case ALL_BORDER_LINE_WIDTH: + case LEFT_BORDER_LINE_WIDTH: + pLine = pLeft; + break; + case RIGHT_BORDER_LINE_WIDTH: + pLine = pRight; + break; + case TOP_BORDER_LINE_WIDTH: + pLine = pTop; + break; + case BOTTOM_BORDER_LINE_WIDTH: + pLine = pBottom; + break; + default: + return false; + } + rUnitConverter.convertMeasureToXML( aOut, pLine->GetInWidth() ); + aOut.append( ' ' ); + rUnitConverter.convertMeasureToXML( aOut, pLine->GetDistance() ); + aOut.append( ' ' ); + rUnitConverter.convertMeasureToXML( aOut, pLine->GetOutWidth() ); + break; + } + bOk = true; + } + } + break; + + case RES_BREAK: + { + const SvxFormatBreakItem& rFormatBreak = dynamic_cast<const SvxFormatBreakItem&>(rItem); + + sal_uInt16 eEnum = 0; + + switch( nMemberId ) + { + case MID_BREAK_BEFORE: + switch (rFormatBreak.GetBreak()) + { + case SvxBreak::ColumnBefore: + eEnum = 1; + break; + case SvxBreak::PageBefore: + eEnum = 2; + break; + case SvxBreak::NONE: + eEnum = 0; + break; + default: + return false; + } + break; + case MID_BREAK_AFTER: + switch (rFormatBreak.GetBreak()) + { + case SvxBreak::ColumnAfter: + eEnum = 1; + break; + case SvxBreak::PageAfter: + eEnum = 2; + break; + case SvxBreak::NONE: + eEnum = 0; + break; + default: + return false; + } + break; + } + + bOk = SvXMLUnitConverter::convertEnum( aOut, eEnum, psXML_BreakType ); + } + break; + + case RES_KEEP: + { + const SvxFormatKeepItem* pFormatKeep = dynamic_cast<const SvxFormatKeepItem*>( &rItem ); + assert(pFormatKeep && "Wrong Which-ID"); + if (pFormatKeep) + { + aOut.append( pFormatKeep->GetValue() + ? GetXMLToken( XML_ALWAYS ) + : GetXMLToken( XML_AUTO ) ); + bOk = true; + } + } + break; + + case RES_PRINT: + { + const SvxPrintItem* pHasTextChangesOnly = dynamic_cast<const SvxPrintItem*>( &rItem ); + if (pHasTextChangesOnly && !pHasTextChangesOnly->GetValue()) + { + aOut.append( "false" ); + bOk = true; + } + } + break; + + case RES_BACKGROUND: + { + const SvxBrushItem& rBrush = dynamic_cast<const SvxBrushItem&>(rItem); + + // note: the graphic is only exported if nMemberId equals + // MID_GRAPHIC... + // If not, only the color or transparency is exported + + switch( nMemberId ) + { + case MID_BACK_COLOR: + if ( rBrush.GetColor().IsTransparent() ) + aOut.append( GetXMLToken(XML_TRANSPARENT) ); + else + { + ::sax::Converter::convertColor(aOut, + rBrush.GetColor()); + } + bOk = true; + break; + + case MID_GRAPHIC_POSITION: + switch (rBrush.GetGraphicPos()) + { + case GPOS_LT: + case GPOS_MT: + case GPOS_RT: + aOut.append( GetXMLToken(XML_TOP) ); + bOk = true; + break; + case GPOS_LM: + case GPOS_MM: + case GPOS_RM: + aOut.append( GetXMLToken(XML_CENTER) ); + bOk = true; + break; + case GPOS_LB: + case GPOS_MB: + case GPOS_RB: + aOut.append( GetXMLToken(XML_BOTTOM) ); + bOk = true; + break; + default: + ; + } + + if( bOk ) + { + aOut.append( ' ' ); + + switch (rBrush.GetGraphicPos()) + { + case GPOS_LT: + case GPOS_LB: + case GPOS_LM: + aOut.append( GetXMLToken(XML_LEFT) ); + break; + case GPOS_MT: + case GPOS_MM: + case GPOS_MB: + aOut.append( GetXMLToken(XML_CENTER) ); + break; + case GPOS_RM: + case GPOS_RT: + case GPOS_RB: + aOut.append( GetXMLToken(XML_RIGHT) ); + break; + default: + ; + } + } + break; + + case MID_GRAPHIC_REPEAT: + { + SvxGraphicPosition eGraphicPos = rBrush.GetGraphicPos(); + if( GPOS_AREA == eGraphicPos ) + { + aOut.append( GetXMLToken(XML_STRETCH) ); + bOk = true; + } + else if( GPOS_NONE != eGraphicPos && GPOS_TILED != eGraphicPos ) + { + aOut.append( GetXMLToken(XML_BACKGROUND_NO_REPEAT) ); + bOk = true; + } + } + break; + + case MID_GRAPHIC_FILTER: + if (rBrush.GetGraphicPos() != GPOS_NONE && + !rBrush.GetGraphicFilter().isEmpty()) + { + aOut.append(rBrush.GetGraphicFilter()); + bOk = true; + } + break; + } + } + break; + + case RES_PAGEDESC: + { + const SwFormatPageDesc& rPageDesc = dynamic_cast<const SwFormatPageDesc&>(rItem); + + if( MID_PAGEDESC_PAGENUMOFFSET==nMemberId ) + { + ::std::optional<sal_uInt16> oNumOffset = rPageDesc.GetNumOffset(); + if (oNumOffset && *oNumOffset > 0) + { + // #i114163# positiveInteger only! + sal_Int32 const number(*oNumOffset); + aOut.append(number); + } + else + { + aOut.append(GetXMLToken(XML_AUTO)); + } + bOk = true; + } + } + break; + + case RES_LAYOUT_SPLIT: + case RES_ROW_SPLIT: + { + const SfxBoolItem* pSplit = dynamic_cast<const SfxBoolItem*>( &rItem ); + assert(pSplit && "Wrong Which-ID"); + if (pSplit) + { + ::sax::Converter::convertBool( aOut, pSplit->GetValue() ); + bOk = true; + } + } + break; + + case RES_HORI_ORIENT: + { + const SwFormatHoriOrient* pHoriOrient = dynamic_cast<const SwFormatHoriOrient*>( &rItem ); + assert(pHoriOrient && "Wrong Which-ID"); + if (pHoriOrient) + { + SvXMLUnitConverter::convertEnum( aOut, pHoriOrient->GetHoriOrient(), + aXMLTableAlignMap ); + bOk = true; + } + } + break; + + case RES_VERT_ORIENT: + { + const SwFormatVertOrient* pVertOrient = dynamic_cast<const SwFormatVertOrient*>( &rItem ); + assert(pVertOrient && "Wrong Which-ID"); + + SvXMLUnitConverter::convertEnum( aOut, pVertOrient->GetVertOrient(), + aXMLTableVAlignMap ); + bOk = true; + } + break; + + case RES_FRM_SIZE: + { + const SwFormatFrameSize& rFrameSize = dynamic_cast<const SwFormatFrameSize&>(rItem); + + bool bOutHeight = false; + switch( nMemberId ) + { + case MID_FRMSIZE_REL_WIDTH: + if (rFrameSize.GetWidthPercent()) + { + ::sax::Converter::convertPercent( + aOut, rFrameSize.GetWidthPercent() ); + bOk = true; + } + break; + case MID_FRMSIZE_MIN_HEIGHT: + if( SwFrameSize::Minimum == rFrameSize.GetHeightSizeType() ) + bOutHeight = true; + break; + case MID_FRMSIZE_FIX_HEIGHT: + if( SwFrameSize::Fixed == rFrameSize.GetHeightSizeType() ) + bOutHeight = true; + break; + } + + if( bOutHeight ) + { + rUnitConverter.convertMeasureToXML(aOut, rFrameSize.GetHeight()); + bOk = true; + } + } + break; + + case RES_FRAMEDIR: + { + Any aAny; + bOk = rItem.QueryValue( aAny ); + if( bOk ) + { + std::unique_ptr<XMLPropertyHandler> pWritingModeHandler = + XMLPropertyHandlerFactory::CreatePropertyHandler( + XML_TYPE_TEXT_WRITING_MODE_WITH_DEFAULT ); + OUString sValue; + bOk = pWritingModeHandler->exportXML( sValue, aAny, + rUnitConverter ); + if( bOk ) + aOut.append( sValue ); + } + } + break; + + case RES_COLLAPSING_BORDERS: + { + const SfxBoolItem* pBorders = dynamic_cast<const SfxBoolItem*>( &rItem ); + assert(pBorders && "Wrong RES-ID"); + if (pBorders) + { + aOut.append( pBorders->GetValue() + ? GetXMLToken( XML_COLLAPSING ) + : GetXMLToken( XML_SEPARATING ) ); + bOk = true; + } + } + break; + + default: + OSL_FAIL("GetXMLValue not implemented for this item."); + break; + } + + if ( bOk ) + rValue = aOut.makeStringAndClear(); + + return bOk; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlexpit.hxx b/sw/source/filter/xml/xmlexpit.hxx new file mode 100644 index 0000000000..b0e10fb0ab --- /dev/null +++ b/sw/source/filter/xml/xmlexpit.hxx @@ -0,0 +1,100 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLEXPIT_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLEXPIT_HXX + +#include <xmloff/xmlexppr.hxx> +#include "xmlitmap.hxx" +#include <vector> + +class SvXMLUnitConverter; +class SfxPoolItem; +class SfxItemSet; +namespace comphelper { class AttributeList; } +class SvXMLNamespaceMap; +class SvXMLExport; + +class SvXMLExportItemMapper +{ + SvXMLItemMapEntriesRef mrMapEntries; + +protected: + /** fills the given attribute list with the items in the given set */ + void exportXML( const SvXMLExport& rExport, + comphelper::AttributeList& rAttrList, + const SfxItemSet& rSet, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + std::vector<sal_uInt16> *pIndexArray ) const; + + void exportXML( const SvXMLExport& rExport, + comphelper::AttributeList& rAttrList, + const SfxPoolItem& rItem, + const SvXMLItemMapEntry &rEntry, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + const SfxItemSet *pSet ) const; + + void exportElementItems( SvXMLExport& rExport, + const SfxItemSet &rSet, + const std::vector<sal_uInt16> &rIndexArray ) const; + + static const SfxPoolItem* GetItem( const SfxItemSet &rSet, + sal_uInt16 nWhichId ); + +public: + explicit SvXMLExportItemMapper( SvXMLItemMapEntriesRef rMapEntries ); + virtual ~SvXMLExportItemMapper(); + + void exportXML( SvXMLExport& rExport, + const SfxItemSet& rSet, + const SvXMLUnitConverter& rUnitConverter, + ::xmloff::token::XMLTokenEnum ePropToken ) const; + + /** this method is called for every item that has the + MID_SW_FLAG_SPECIAL_ITEM_EXPORT flag set */ + virtual void handleSpecialItem( comphelper::AttributeList& rAttrList, + const SvXMLItemMapEntry& rEntry, + const SfxPoolItem& rItem, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + const SfxItemSet *pSet ) const; + + /** this method is called for every item that has the + MID_SW_FLAG_ELEMENT_EXPORT flag set */ + virtual void handleElementItem( const SvXMLItemMapEntry& rEntry, + const SfxPoolItem& rItem ) const; + + inline void setMapEntries( SvXMLItemMapEntriesRef rMapEntries ); + + static bool QueryXMLValue( const SfxPoolItem& rItem, + OUString& rValue, sal_uInt16 nMemberId, + const SvXMLUnitConverter& rUnitConverter ); +}; + +inline void +SvXMLExportItemMapper::setMapEntries( SvXMLItemMapEntriesRef rMapEntries ) +{ + mrMapEntries = rMapEntries; +} + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLEXPIT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlfmt.cxx b/sw/source/filter/xml/xmlfmt.cxx new file mode 100644 index 0000000000..9bd4d2a5f4 --- /dev/null +++ b/sw/source/filter/xml/xmlfmt.cxx @@ -0,0 +1,1079 @@ +/* -*- 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 <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <comphelper/diagnose_ex.hxx> +#include <fmtcol.hxx> +#include <hints.hxx> +#include <doc.hxx> +#include <docary.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <unoprnms.hxx> +#include <fmtpdsc.hxx> +#include <pagedesc.hxx> +#include <xmloff/maptype.hxx> +#include <xmloff/xmlnumfi.hxx> +#include <xmloff/xmlprmap.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlstyle.hxx> +#include <xmloff/txtstyli.hxx> +#include <xmloff/txtimp.hxx> +#include <xmloff/families.hxx> +#include <xmloff/XMLTextMasterStylesContext.hxx> +#include <xmloff/XMLTextShapeStyleContext.hxx> +#include <xmloff/XMLGraphicsDefaultStyle.hxx> +#include <xmloff/XMLDrawingPageStyleContext.hxx> +#include <xmloff/XMLTextMasterPageContext.hxx> +#include <xmloff/table/XMLTableImport.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include "xmlimp.hxx" +#include <cellatr.hxx> +#include <SwStyleNameMapper.hxx> +#include <ccoll.hxx> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::xmloff::token; + +namespace { + +class SwXMLConditionParser_Impl +{ + OUString m_sInput; + + Master_CollCondition m_nCondition; + sal_uInt32 m_nSubCondition; + + sal_Int32 m_nPos; + sal_Int32 m_nLength; + + inline bool SkipWS(); + inline bool MatchChar( sal_Unicode c ); + inline bool MatchName( OUString& rName ); + inline bool MatchNumber( sal_uInt32& rNumber ); + +public: + + explicit SwXMLConditionParser_Impl( const OUString& rInp ); + + bool IsValid() const { return Master_CollCondition::NONE != m_nCondition; } + + Master_CollCondition GetCondition() const { return m_nCondition; } + sal_uInt32 GetSubCondition() const { return m_nSubCondition; } +}; + +} + +inline bool SwXMLConditionParser_Impl::SkipWS() +{ + while( m_nPos < m_nLength && ' ' == m_sInput[m_nPos] ) + m_nPos++; + return true; +} + +inline bool SwXMLConditionParser_Impl::MatchChar( sal_Unicode c ) +{ + bool bRet = false; + if( m_nPos < m_nLength && c == m_sInput[m_nPos] ) + { + m_nPos++; + bRet = true; + } + return bRet; +} + +inline bool SwXMLConditionParser_Impl::MatchName( OUString& rName ) +{ + OUStringBuffer sBuffer( m_nLength ); + while( m_nPos < m_nLength && + ( ('a' <= m_sInput[m_nPos] && m_sInput[m_nPos] <= 'z') || + '-' == m_sInput[m_nPos] ) ) + { + sBuffer.append( m_sInput[m_nPos] ); + m_nPos++; + } + rName = sBuffer.makeStringAndClear(); + return !rName.isEmpty(); +} + +inline bool SwXMLConditionParser_Impl::MatchNumber( sal_uInt32& rNumber ) +{ + OUStringBuffer sBuffer( m_nLength ); + while( m_nPos < m_nLength && '0' <= m_sInput[m_nPos] && m_sInput[m_nPos] <= '9' ) + { + sBuffer.append( m_sInput[m_nPos] ); + m_nPos++; + } + + OUString sNum( sBuffer.makeStringAndClear() ); + if( !sNum.isEmpty() ) + rNumber = sNum.toInt32(); + return !sNum.isEmpty(); +} + +SwXMLConditionParser_Impl::SwXMLConditionParser_Impl( const OUString& rInp ) : + m_sInput( rInp ), + m_nCondition( Master_CollCondition::NONE ), + m_nSubCondition( 0 ), + m_nPos( 0 ), + m_nLength( rInp.getLength() ) +{ + OUString sFunc; + bool bHasSub = false; + sal_uInt32 nSub = 0; + bool bOK = SkipWS() && MatchName( sFunc ) && SkipWS() && + MatchChar( '(' ) && SkipWS() && MatchChar( ')' ) && SkipWS(); + if( bOK && MatchChar( '=' ) ) + { + bOK = SkipWS() && MatchNumber( nSub ) && SkipWS(); + bHasSub = true; + } + + bOK &= m_nPos == m_nLength; + + if( !bOK ) + return; + + if( IsXMLToken( sFunc, XML_ENDNOTE ) && !bHasSub ) + m_nCondition = Master_CollCondition::PARA_IN_ENDNOTE; + else if( IsXMLToken( sFunc, XML_FOOTER ) && !bHasSub ) + m_nCondition = Master_CollCondition::PARA_IN_FOOTER; + else if( IsXMLToken( sFunc, XML_FOOTNOTE ) && !bHasSub ) + m_nCondition = Master_CollCondition::PARA_IN_FOOTNOTE; + else if( IsXMLToken( sFunc, XML_HEADER ) && !bHasSub ) + m_nCondition = Master_CollCondition::PARA_IN_HEADER; + else if( IsXMLToken( sFunc, XML_LIST_LEVEL) && + nSub >=1 && nSub <= MAXLEVEL ) + { + m_nCondition = Master_CollCondition::PARA_IN_LIST; + m_nSubCondition = nSub-1; + } + else if( IsXMLToken( sFunc, XML_OUTLINE_LEVEL) && + nSub >=1 && nSub <= MAXLEVEL ) + { + m_nCondition = Master_CollCondition::PARA_IN_OUTLINE; + m_nSubCondition = nSub-1; + } + else if( IsXMLToken( sFunc, XML_SECTION ) && !bHasSub ) + { + m_nCondition = Master_CollCondition::PARA_IN_SECTION; + } + else if( IsXMLToken( sFunc, XML_TABLE ) && !bHasSub ) + { + m_nCondition = Master_CollCondition::PARA_IN_TABLEBODY; + } + else if( IsXMLToken( sFunc, XML_TABLE_HEADER ) && !bHasSub ) + { + m_nCondition = Master_CollCondition::PARA_IN_TABLEHEAD; + } + else if( IsXMLToken( sFunc, XML_TEXT_BOX ) && !bHasSub ) + { + m_nCondition = Master_CollCondition::PARA_IN_FRAME; + } +} + +namespace { + +class SwXMLConditionContext_Impl : public SvXMLImportContext +{ + Master_CollCondition m_nCondition; + sal_uInt32 m_nSubCondition; + + OUString m_sApplyStyle; + +public: + + SwXMLConditionContext_Impl( + SvXMLImport& rImport, sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList > & xAttrList ); + + bool IsValid() const { return Master_CollCondition::NONE != m_nCondition; } + + Master_CollCondition getCondition() const { return m_nCondition; } + sal_uInt32 getSubCondition() const { return m_nSubCondition; } + OUString const &getApplyStyle() const { return m_sApplyStyle; } +}; + +} + +SwXMLConditionContext_Impl::SwXMLConditionContext_Impl( + SvXMLImport& rImport, sal_Int32 /*nElement*/, + const uno::Reference< xml::sax::XFastAttributeList > & xAttrList ) : + SvXMLImportContext( rImport ), + m_nCondition( Master_CollCondition::NONE ), + m_nSubCondition( 0 ) +{ + for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList )) + { + OUString sValue = aIter.toString(); + switch (aIter.getToken()) + { + case XML_ELEMENT(STYLE, XML_CONDITION): + { + SwXMLConditionParser_Impl aCondParser( sValue ); + if( aCondParser.IsValid() ) + { + m_nCondition = aCondParser.GetCondition(); + m_nSubCondition = aCondParser.GetSubCondition(); + } + break; + } + case XML_ELEMENT(STYLE, XML_APPLY_STYLE_NAME): + m_sApplyStyle = sValue; + break; + default: + XMLOFF_WARN_UNKNOWN("sw", aIter); + } + } +} + +typedef std::vector<rtl::Reference<SwXMLConditionContext_Impl>> SwXMLConditions_Impl; + +namespace { + +class SwXMLTextStyleContext_Impl : public XMLTextStyleContext +{ + std::unique_ptr<SwXMLConditions_Impl> m_pConditions; + +protected: + + virtual uno::Reference < style::XStyle > Create() override; + virtual void Finish( bool bOverwrite ) override; + +public: + + + SwXMLTextStyleContext_Impl( SwXMLImport& rImport, + XmlStyleFamily nFamily, + SvXMLStylesContext& rStyles ); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; +}; + +} + +uno::Reference < style::XStyle > SwXMLTextStyleContext_Impl::Create() +{ + uno::Reference < style::XStyle > xNewStyle; + if( m_pConditions && XmlStyleFamily::TEXT_PARAGRAPH == GetFamily() ) + { + uno::Reference< lang::XMultiServiceFactory > xFactory( GetImport().GetModel(), + uno::UNO_QUERY ); + if( xFactory.is() ) + { + uno::Reference < uno::XInterface > xIfc = + xFactory->createInstance( "com.sun.star.style.ConditionalParagraphStyle" ); + if( xIfc.is() ) + xNewStyle.set( xIfc, uno::UNO_QUERY ); + } + } + else + { + xNewStyle = XMLTextStyleContext::Create(); + } + + return xNewStyle; +} + +void +SwXMLTextStyleContext_Impl::Finish( bool bOverwrite ) +{ + if( m_pConditions && XmlStyleFamily::TEXT_PARAGRAPH == GetFamily() && GetStyle().is() ) + { + CommandStruct const*const pCommands = SwCondCollItem::GetCmds(); + + Reference< XPropertySet > xPropSet( GetStyle(), UNO_QUERY ); + + uno::Sequence< beans::NamedValue > aSeq( m_pConditions->size() ); + auto aSeqRange = asNonConstRange(aSeq); + + for (std::vector<rtl::Reference<SwXMLConditionContext_Impl>>::size_type i = 0; + i < m_pConditions->size(); ++i) + { + assert((*m_pConditions)[i]->IsValid()); // checked before inserting + Master_CollCondition nCond = (*m_pConditions)[i]->getCondition(); + sal_uInt32 nSubCond = (*m_pConditions)[i]->getSubCondition(); + + for (size_t j = 0; j < COND_COMMAND_COUNT; ++j) + { + if (pCommands[j].nCnd == nCond && + pCommands[j].nSubCond == nSubCond) + { + aSeqRange[i].Name = GetCommandContextByIndex( j ); + aSeqRange[i].Value <<= GetImport().GetStyleDisplayName( + GetFamily(), (*m_pConditions)[i]->getApplyStyle() ); + break; + } + } + } + + try + { + xPropSet->setPropertyValue(UNO_NAME_PARA_STYLE_CONDITIONS, uno::Any(aSeq)); + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("sw.xml", "exception when setting ParaStyleConditions"); + } + } + XMLTextStyleContext::Finish( bOverwrite ); +} + +SwXMLTextStyleContext_Impl::SwXMLTextStyleContext_Impl( SwXMLImport& rImport, + XmlStyleFamily nFamily, + SvXMLStylesContext& rStyles ) : + XMLTextStyleContext( rImport, rStyles, nFamily ) +{ +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLTextStyleContext_Impl::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + if( nElement == XML_ELEMENT(STYLE, XML_MAP) ) + { + rtl::Reference<SwXMLConditionContext_Impl> xCond{ + new SwXMLConditionContext_Impl( GetImport(), nElement, xAttrList )}; + if( xCond->IsValid() ) + { + if( !m_pConditions ) + m_pConditions = std::make_unique<SwXMLConditions_Impl>(); + m_pConditions->push_back( xCond ); + } + return xCond; + } + + return XMLTextStyleContext::createFastChildContext( nElement, xAttrList ); +} + +namespace { + +class SwXMLCellStyleContext : public XMLPropStyleContext +{ + OUString m_sDataStyleName; + void AddDataFormat(); +public: + using XMLPropStyleContext::XMLPropStyleContext; + virtual void FillPropertySet(const css::uno::Reference<css::beans::XPropertySet>& rPropSet) override; + virtual void SetAttribute(sal_Int32 nElement, const OUString& rValue) override; +}; + +class SwXMLItemSetStyleContext_Impl : public SvXMLStyleContext +{ + OUString m_sMasterPageName; + std::optional<SfxItemSet> m_oItemSet; + SwXMLTextStyleContext_Impl *m_pTextStyle; + SvXMLStylesContext &m_rStyles; + + OUString m_sDataStyleName; + + bool m_bHasMasterPageName : 1; + bool m_bPageDescConnected : 1; + bool m_bDataStyleIsResolved; + + SvXMLImportContext *CreateItemSetContext( + sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList > & xAttrList); + +protected: + + virtual void SetAttribute( sal_Int32 nElement, + const OUString& rValue ) override; + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } + +public: + + + SwXMLItemSetStyleContext_Impl( + SwXMLImport& rImport, + SvXMLStylesContext& rStylesC, + XmlStyleFamily nFamily); + + virtual void CreateAndInsert( bool bOverwrite ) override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + + // The item set may be empty! + SfxItemSet *GetItemSet() { return m_oItemSet ? &*m_oItemSet : nullptr; } + + bool HasMasterPageName() const { return m_bHasMasterPageName; } + + bool IsPageDescConnected() const { return m_bPageDescConnected; } + void ConnectPageDesc(); + + bool ResolveDataStyleName(); +}; + +} + +void SwXMLCellStyleContext::AddDataFormat() +{ + if (m_sDataStyleName.isEmpty() || IsDefaultStyle()) + return; + + const SvXMLNumFormatContext* pStyle = static_cast<const SvXMLNumFormatContext*>( + GetStyles()->FindStyleChildContext(XmlStyleFamily::DATA_STYLE, m_sDataStyleName, true)); + + if (!pStyle) + { + SAL_WARN("sw.xml", "not possible to get data style " << m_sDataStyleName); + return; + } + + sal_Int32 nNumberFormat = const_cast<SvXMLNumFormatContext*>(pStyle)->GetKey(); + if (nNumberFormat < 0) + return; + + rtl::Reference<SvXMLImportPropertyMapper> xPropertyMapper(GetStyles()->GetImportPropertyMapper(GetFamily())); + if (!xPropertyMapper.is()) + { + SAL_WARN("sw.xml", "there is no import prop mapper"); + return; + } + + const rtl::Reference<XMLPropertySetMapper>& xPropertySetMapper(xPropertyMapper->getPropertySetMapper()); + sal_Int32 nIndex = xPropertySetMapper->GetEntryIndex(XML_NAMESPACE_STYLE, GetXMLToken(XML_DATA_STYLE_NAME), 0); + if (nIndex < 0) + { + SAL_WARN("sw.xml", "could not find id for " << GetXMLToken(XML_DATA_STYLE_NAME)); + return; + } + + auto aIter = std::find_if(GetProperties().begin(), GetProperties().end(), + [&nIndex](const XMLPropertyState& rProp) { + return rProp.mnIndex == nIndex; + }); + + if (aIter != GetProperties().end()) + aIter->maValue <<= nNumberFormat; + else + GetProperties().push_back(XMLPropertyState(nIndex, Any(nNumberFormat))); +} + +void SwXMLCellStyleContext::FillPropertySet(const css::uno::Reference<css::beans::XPropertySet>& rPropSet) +{ + AddDataFormat(); + XMLPropStyleContext::FillPropertySet(rPropSet); +} + +void SwXMLCellStyleContext::SetAttribute(sal_Int32 nElement, const OUString& rValue) +{ + if ((nElement & TOKEN_MASK) == XML_DATA_STYLE_NAME) + m_sDataStyleName = rValue; + else + XMLPropStyleContext::SetAttribute(nElement, rValue); +} + +void SwXMLItemSetStyleContext_Impl::SetAttribute( sal_Int32 nElement, + const OUString& rValue ) +{ + switch(nElement) + { + case XML_ELEMENT(STYLE, XML_MASTER_PAGE_NAME): + { + m_sMasterPageName = rValue; + m_bHasMasterPageName = true; + break; + } + case XML_ELEMENT(STYLE, XML_DATA_STYLE_NAME): + { + // if we have a valid data style name + if (!rValue.isEmpty()) + { + m_sDataStyleName = rValue; + m_bDataStyleIsResolved = false; // needs to be resolved + } + break; + } + default: + SvXMLStyleContext::SetAttribute( nElement, rValue ); + } +} + +SvXMLImportContext *SwXMLItemSetStyleContext_Impl::CreateItemSetContext( + sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList > & xAttrList ) +{ + OSL_ENSURE( !m_oItemSet, + "SwXMLItemSetStyleContext_Impl::CreateItemSetContext: item set exists" ); + + SvXMLImportContext *pContext = nullptr; + + SwDoc* pDoc = GetSwImport().getDoc(); + + SfxItemPool& rItemPool = pDoc->GetAttrPool(); + switch( GetFamily() ) + { + case XmlStyleFamily::TABLE_TABLE: + m_oItemSet.emplace( rItemPool, aTableSetRange ); + break; + case XmlStyleFamily::TABLE_COLUMN: + m_oItemSet.emplace( rItemPool, svl::Items<RES_FRM_SIZE, RES_FRM_SIZE> ); + break; + case XmlStyleFamily::TABLE_ROW: + m_oItemSet.emplace( rItemPool, aTableLineSetRange ); + break; + case XmlStyleFamily::TABLE_CELL: + m_oItemSet.emplace( rItemPool, aTableBoxSetRange ); + break; + default: + OSL_ENSURE( false, + "SwXMLItemSetStyleContext_Impl::CreateItemSetContext: unknown family" ); + break; + } + if( m_oItemSet ) + pContext = GetSwImport().CreateTableItemImportContext( + nElement, xAttrList, GetFamily(), + *m_oItemSet ); + if( !pContext ) + { + m_oItemSet.reset(); + } + + return pContext; +} + + +SwXMLItemSetStyleContext_Impl::SwXMLItemSetStyleContext_Impl( SwXMLImport& rImport, + SvXMLStylesContext& rStylesC, + XmlStyleFamily nFamily ) : + SvXMLStyleContext( rImport, nFamily ), + m_pTextStyle( nullptr ), + m_rStyles( rStylesC ), + m_bHasMasterPageName( false ), + m_bPageDescConnected( false ), + m_bDataStyleIsResolved( true ) +{ +} + +void SwXMLItemSetStyleContext_Impl::CreateAndInsert( bool bOverwrite ) +{ + if( m_pTextStyle ) + m_pTextStyle->CreateAndInsert( bOverwrite ); +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLItemSetStyleContext_Impl::createFastChildContext( + sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList > & xAttrList ) +{ + switch (nElement) + { + case XML_ELEMENT(STYLE, XML_TABLE_PROPERTIES): + case XML_ELEMENT(STYLE, XML_TABLE_COLUMN_PROPERTIES): + case XML_ELEMENT(STYLE, XML_TABLE_ROW_PROPERTIES): + case XML_ELEMENT(STYLE, XML_TABLE_CELL_PROPERTIES): + return CreateItemSetContext( nElement, xAttrList ); + case XML_ELEMENT(STYLE, XML_TEXT_PROPERTIES): + case XML_ELEMENT(STYLE, XML_PARAGRAPH_PROPERTIES): + { + if( !m_pTextStyle ) + { + m_pTextStyle = new SwXMLTextStyleContext_Impl( GetSwImport(), XmlStyleFamily::TEXT_PARAGRAPH, m_rStyles ); + rtl::Reference<sax_fastparser::FastAttributeList> xTmpAttrList = new sax_fastparser::FastAttributeList(nullptr); + xTmpAttrList->add(XML_ELEMENT(STYLE, XML_NAME), GetName().toUtf8() ); + m_pTextStyle->startFastElement( nElement, xTmpAttrList ); + m_rStyles.AddStyle( *m_pTextStyle ); + } + return m_pTextStyle->createFastChildContext( nElement, xAttrList ); + } + default: + XMLOFF_WARN_UNKNOWN_ELEMENT("sw", nElement); + } + + return nullptr; +} + +void SwXMLItemSetStyleContext_Impl::ConnectPageDesc() +{ + if( m_bPageDescConnected || !HasMasterPageName() ) + return; + m_bPageDescConnected = true; + + SwDoc *pDoc = GetSwImport().getDoc(); + + // #i40788# - first determine the display name of the page style, + // then map this name to the corresponding user interface name. + OUString sName = GetImport().GetStyleDisplayName( XmlStyleFamily::MASTER_PAGE, + m_sMasterPageName ); + SwStyleNameMapper::FillUIName( sName, + sName, + SwGetPoolIdFromName::PageDesc); + SwPageDesc *pPageDesc = pDoc->FindPageDesc(sName); + if( !pPageDesc ) + { + // If the page style is a pool style, then we maybe have to create it + // first if it hasn't been used by now. + const sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( sName, SwGetPoolIdFromName::PageDesc ); + if( USHRT_MAX != nPoolId ) + pPageDesc = pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( nPoolId, false ); + } + + if( !pPageDesc ) + return; + + if( !m_oItemSet ) + { + SfxItemPool& rItemPool = pDoc->GetAttrPool(); + m_oItemSet.emplace( rItemPool, aTableSetRange ); + } + + std::unique_ptr<SwFormatPageDesc> pFormatPageDesc; + if( const SwFormatPageDesc* pItem = m_oItemSet->GetItemIfSet( RES_PAGEDESC, false ) ) + { + if( pItem->GetPageDesc() != pPageDesc ) + pFormatPageDesc.reset(new SwFormatPageDesc( *pItem )); + } + else + pFormatPageDesc.reset(new SwFormatPageDesc()); + + if( pFormatPageDesc ) + { + pFormatPageDesc->RegisterToPageDesc( *pPageDesc ); + m_oItemSet->Put( std::move(pFormatPageDesc) ); + } +} + +bool SwXMLItemSetStyleContext_Impl::ResolveDataStyleName() +{ + // resolve, if not already done + if (! m_bDataStyleIsResolved) + { + // get the format key + sal_Int32 nFormat = + GetImport().GetTextImport()->GetDataStyleKey(m_sDataStyleName); + + // if the key is valid, insert Item into ItemSet + if( -1 != nFormat ) + { + if( !m_oItemSet ) + { + SwDoc *pDoc = GetSwImport().getDoc(); + + SfxItemPool& rItemPool = pDoc->GetAttrPool(); + m_oItemSet.emplace( rItemPool, aTableBoxSetRange ); + } + SwTableBoxNumFormat aNumFormatItem(nFormat); + m_oItemSet->Put(aNumFormatItem); + } + + // now resolved + m_bDataStyleIsResolved = true; + return true; + } + else + { + // was already resolved; nothing to do + return false; + } +} + +namespace { + +class SwXMLStylesContext_Impl : public SvXMLStylesContext +{ + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } + const SwXMLImport& GetSwImport() const + { return static_cast<const SwXMLImport&>(GetImport()); } + +protected: + + using SvXMLStylesContext::CreateStyleChildContext; + virtual SvXMLStyleContext *CreateStyleChildContext( sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override; + + using SvXMLStylesContext::CreateStyleStyleChildContext; + virtual SvXMLStyleContext *CreateStyleStyleChildContext( XmlStyleFamily nFamily, + sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList > & xAttrList ) override; + using SvXMLStylesContext::CreateDefaultStyleStyleChildContext; + virtual SvXMLStyleContext *CreateDefaultStyleStyleChildContext( + XmlStyleFamily nFamily, sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList > & xAttrList ) override; + // HACK + virtual rtl::Reference < SvXMLImportPropertyMapper > GetImportPropertyMapper( + XmlStyleFamily nFamily ) const override; + + virtual uno::Reference < container::XNameContainer > + GetStylesContainer( XmlStyleFamily nFamily ) const override; + virtual OUString GetServiceName( XmlStyleFamily nFamily ) const override; + // HACK + +public: + + SwXMLStylesContext_Impl( + SwXMLImport& rImport, + bool bAuto ); + + virtual bool InsertStyleFamily( XmlStyleFamily nFamily ) const override; + + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; +}; + +} + +SvXMLStyleContext *SwXMLStylesContext_Impl::CreateStyleChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) +{ + SvXMLStyleContext* pContext = nullptr; + + if(nElement == XML_ELEMENT(TABLE, XML_TABLE_TEMPLATE)) + { + rtl::Reference<XMLTableImport> xTableImport = GetImport().GetShapeImport()->GetShapeTableImport(); + pContext = xTableImport->CreateTableTemplateContext(nElement, xAttrList); + } + if (!pContext) + pContext = SvXMLStylesContext::CreateStyleChildContext(nElement, xAttrList); + + return pContext; +} + +SvXMLStyleContext *SwXMLStylesContext_Impl::CreateStyleStyleChildContext( + XmlStyleFamily nFamily, sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList > & xAttrList ) +{ + SvXMLStyleContext *pStyle = nullptr; + + switch( nFamily ) + { + case XmlStyleFamily::TEXT_PARAGRAPH: + pStyle = new SwXMLTextStyleContext_Impl( GetSwImport(), nFamily, *this ); + break; + case XmlStyleFamily::TABLE_TABLE: + case XmlStyleFamily::TABLE_COLUMN: + case XmlStyleFamily::TABLE_ROW: + case XmlStyleFamily::TABLE_CELL: + // Distinguish real and automatic styles. + if (IsAutomaticStyle()) + pStyle = new SwXMLItemSetStyleContext_Impl(GetSwImport(), *this, nFamily); + else if (nFamily == XmlStyleFamily::TABLE_CELL) // Real cell styles are used for table-template import. + pStyle = new SwXMLCellStyleContext(GetSwImport(), *this, nFamily); + else + SAL_WARN("sw.xml", "Context does not exists for non automatic table, column or row style."); + break; + case XmlStyleFamily::SD_GRAPHICS_ID: + // As long as there are no element items, we can use the text + // style class. + pStyle = new XMLTextShapeStyleContext( GetImport(), *this, nFamily ); + break; + case XmlStyleFamily::SD_DRAWINGPAGE_ID: + pStyle = new XMLDrawingPageStyleContext(GetImport(), + *this, g_MasterPageContextIDs, g_MasterPageFamilies); + break; + default: + pStyle = SvXMLStylesContext::CreateStyleStyleChildContext( nFamily, + nElement, + xAttrList ); + break; + } + + return pStyle; +} + +SvXMLStyleContext *SwXMLStylesContext_Impl::CreateDefaultStyleStyleChildContext( + XmlStyleFamily nFamily, sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList > & xAttrList ) +{ + SvXMLStyleContext *pStyle = nullptr; + + switch( nFamily ) + { + case XmlStyleFamily::TEXT_PARAGRAPH: + case XmlStyleFamily::TABLE_TABLE: + case XmlStyleFamily::TABLE_ROW: + pStyle = new XMLTextStyleContext( GetImport(), + *this, nFamily, + true ); + break; + case XmlStyleFamily::SD_GRAPHICS_ID: + // There are no writer specific defaults for graphic styles! + pStyle = new XMLGraphicsDefaultStyle( GetImport(), *this ); + break; + default: + pStyle = SvXMLStylesContext::CreateDefaultStyleStyleChildContext( nFamily, + nElement, + xAttrList ); + break; + } + + return pStyle; +} + +SwXMLStylesContext_Impl::SwXMLStylesContext_Impl( + SwXMLImport& rImport, + bool bAuto ) : + SvXMLStylesContext( rImport, bAuto ) +{ +} + +bool SwXMLStylesContext_Impl::InsertStyleFamily( XmlStyleFamily nFamily ) const +{ + const SwXMLImport& rSwImport = GetSwImport(); + const SfxStyleFamily nStyleFamilyMask = rSwImport.GetStyleFamilyMask(); + + bool bIns = true; + switch( nFamily ) + { + case XmlStyleFamily::TEXT_PARAGRAPH: + bIns = bool(nStyleFamilyMask & SfxStyleFamily::Para); + break; + case XmlStyleFamily::TEXT_TEXT: + bIns = bool(nStyleFamilyMask & SfxStyleFamily::Char); + break; + case XmlStyleFamily::SD_GRAPHICS_ID: + bIns = bool(nStyleFamilyMask & SfxStyleFamily::Frame); + break; + case XmlStyleFamily::TEXT_LIST: + bIns = bool(nStyleFamilyMask & SfxStyleFamily::Pseudo); + break; + case XmlStyleFamily::TEXT_OUTLINE: + case XmlStyleFamily::TEXT_FOOTNOTECONFIG: + case XmlStyleFamily::TEXT_ENDNOTECONFIG: + case XmlStyleFamily::TEXT_LINENUMBERINGCONFIG: + case XmlStyleFamily::TEXT_BIBLIOGRAPHYCONFIG: + bIns = !(rSwImport.IsInsertMode() || rSwImport.IsStylesOnlyMode() || + rSwImport.IsBlockMode()); + break; + default: + bIns = SvXMLStylesContext::InsertStyleFamily( nFamily ); + break; + } + + return bIns; +} + +rtl::Reference < SvXMLImportPropertyMapper > SwXMLStylesContext_Impl::GetImportPropertyMapper( + XmlStyleFamily nFamily ) const +{ + rtl::Reference < SvXMLImportPropertyMapper > xMapper; + if( nFamily == XmlStyleFamily::TABLE_TABLE ) + xMapper = XMLTextImportHelper::CreateTableDefaultExtPropMapper( + const_cast<SwXMLStylesContext_Impl*>( this )->GetImport() ); + else if( nFamily == XmlStyleFamily::TABLE_ROW ) + xMapper = XMLTextImportHelper::CreateTableRowDefaultExtPropMapper( + const_cast<SwXMLStylesContext_Impl*>( this )->GetImport() ); + else if( nFamily == XmlStyleFamily::TABLE_CELL ) + xMapper = XMLTextImportHelper::CreateTableCellExtPropMapper( + const_cast<SwXMLStylesContext_Impl*>( this )->GetImport() ); + else if (nFamily == XmlStyleFamily::SD_DRAWINGPAGE_ID) + { + xMapper = XMLTextImportHelper::CreateDrawingPageExtPropMapper( + const_cast<SwXMLStylesContext_Impl*>(this)->GetImport()); + } + else + xMapper = SvXMLStylesContext::GetImportPropertyMapper( nFamily ); + return xMapper; +} + +uno::Reference < container::XNameContainer > SwXMLStylesContext_Impl::GetStylesContainer( + XmlStyleFamily nFamily ) const +{ + uno::Reference < container::XNameContainer > xStyles; + if( XmlStyleFamily::SD_GRAPHICS_ID == nFamily ) + xStyles = const_cast<SvXMLImport *>(&GetImport())->GetTextImport()->GetFrameStyles(); + else if( XmlStyleFamily::TABLE_CELL == nFamily ) + xStyles = const_cast<SvXMLImport *>(&GetImport())->GetTextImport()->GetCellStyles(); + + if (!xStyles.is()) + xStyles = SvXMLStylesContext::GetStylesContainer( nFamily ); + + return xStyles; +} + +OUString SwXMLStylesContext_Impl::GetServiceName( XmlStyleFamily nFamily ) const +{ + if( XmlStyleFamily::SD_GRAPHICS_ID == nFamily ) + return "com.sun.star.style.FrameStyle"; + else if( XmlStyleFamily::TABLE_CELL == nFamily ) + return "com.sun.star.style.CellStyle"; + + return SvXMLStylesContext::GetServiceName( nFamily ); +} + +void SwXMLStylesContext_Impl::endFastElement(sal_Int32 ) +{ + GetSwImport().InsertStyles( IsAutomaticStyle() ); + if (!IsAutomaticStyle()) + GetImport().GetShapeImport()->GetShapeTableImport()->finishStyles(); +} + +namespace { + +class SwXMLMasterStylesContext_Impl : public XMLTextMasterStylesContext +{ +protected: + virtual bool InsertStyleFamily( XmlStyleFamily nFamily ) const override; + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } + const SwXMLImport& GetSwImport() const + { return static_cast<const SwXMLImport&>(GetImport()); } + +public: + + + SwXMLMasterStylesContext_Impl( SwXMLImport& rImport ); + + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; +}; + +} + +SwXMLMasterStylesContext_Impl::SwXMLMasterStylesContext_Impl( + SwXMLImport& rImport ) : + XMLTextMasterStylesContext( rImport ) +{ +} + +bool SwXMLMasterStylesContext_Impl::InsertStyleFamily( XmlStyleFamily nFamily ) const +{ + bool bIns; + + const SwXMLImport& rSwImport = GetSwImport(); + const SfxStyleFamily nStyleFamilyMask = rSwImport.GetStyleFamilyMask(); + if( XmlStyleFamily::MASTER_PAGE == nFamily ) + bIns = bool(nStyleFamilyMask & SfxStyleFamily::Page); + else + bIns = XMLTextMasterStylesContext::InsertStyleFamily( nFamily ); + + return bIns; +} + +void SwXMLMasterStylesContext_Impl::endFastElement(sal_Int32 ) +{ + FinishStyles( !GetSwImport().IsInsertMode() ); + GetSwImport().FinishStyles(); +} + +SvXMLImportContext *SwXMLImport::CreateStylesContext( + bool bAuto ) +{ + SvXMLStylesContext *pContext = new SwXMLStylesContext_Impl( *this, bAuto ); + if( bAuto ) + SetAutoStyles( pContext ); + else + SetStyles( pContext ); + + return pContext; +} + +SvXMLImportContext *SwXMLImport::CreateMasterStylesContext() +{ + SvXMLStylesContext *pContext = + new SwXMLMasterStylesContext_Impl( *this ); + SetMasterStyles( pContext ); + + return pContext; +} + +void SwXMLImport::InsertStyles( bool bAuto ) +{ + if( bAuto && GetAutoStyles() ) + GetAutoStyles()->CopyAutoStylesToDoc(); + if( !bAuto && GetStyles() ) + GetStyles()->CopyStylesToDoc( !IsInsertMode(), false ); +} + +void SwXMLImport::FinishStyles() +{ + if( GetStyles() ) + GetStyles()->FinishStyles( !IsInsertMode() ); +} + +void SwXMLImport::UpdateTextCollConditions( SwDoc *pDoc ) +{ + if( !pDoc ) + pDoc = getDoc(); + + const SwTextFormatColls& rColls = *pDoc->GetTextFormatColls(); + const size_t nCount = rColls.size(); + for( size_t i=0; i < nCount; ++i ) + { + SwTextFormatColl *pColl = rColls[i]; + if( pColl && RES_CONDTXTFMTCOLL == pColl->Which() ) + { + const SwFormatCollConditions& rConditions = + static_cast<const SwConditionTextFormatColl *>(pColl)->GetCondColls(); + bool bSendModify = false; + for( size_t j=0; j < rConditions.size() && !bSendModify; ++j ) + { + const SwCollCondition& rCond = *rConditions[j]; + switch( rCond.GetCondition() ) + { + case Master_CollCondition::PARA_IN_TABLEHEAD: + case Master_CollCondition::PARA_IN_TABLEBODY: + case Master_CollCondition::PARA_IN_FOOTER: + case Master_CollCondition::PARA_IN_HEADER: + bSendModify = true; + break; + default: break; + } + } + if(bSendModify) + pColl->GetNotifier().Broadcast(sw::CondCollCondChg(*pColl)); + } + } +} + +bool SwXMLImport::FindAutomaticStyle( + XmlStyleFamily nFamily, + const OUString& rName, + const SfxItemSet **ppItemSet ) const +{ + SwXMLItemSetStyleContext_Impl *pStyle = nullptr; + if( GetAutoStyles() ) + { + pStyle = const_cast<SwXMLItemSetStyleContext_Impl*>(dynamic_cast< const SwXMLItemSetStyleContext_Impl* >( + GetAutoStyles()-> + FindStyleChildContext( nFamily, rName, + true ) ) ); + if( pStyle ) + { + if( ppItemSet ) + { + if( XmlStyleFamily::TABLE_TABLE == pStyle->GetFamily() && + pStyle->HasMasterPageName() && + !pStyle->IsPageDescConnected() ) + pStyle->ConnectPageDesc(); + (*ppItemSet) = pStyle->GetItemSet(); + + // resolve data style name late + if( XmlStyleFamily::TABLE_CELL == pStyle->GetFamily() && + pStyle->ResolveDataStyleName() ) + { + (*ppItemSet) = pStyle->GetItemSet(); + } + + } + } + } + + return pStyle != nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlfmte.cxx b/sw/source/filter/xml/xmlfmte.cxx new file mode 100644 index 0000000000..8e20f46e42 --- /dev/null +++ b/sw/source/filter/xml/xmlfmte.cxx @@ -0,0 +1,389 @@ +/* -*- 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 <com/sun/star/text/XTextDocument.hpp> +#include <xmloff/xmlnamespace.hxx> +#include "xmlexpit.hxx" +#include <xmloff/namespacemap.hxx> +#include <xmloff/XMLTextListAutoStylePool.hxx> +#include <xmloff/XMLTextMasterPageExport.hxx> +#include <xmloff/table/XMLTableExport.hxx> + +#include <xmloff/txtprmap.hxx> +#include <xmloff/xmlaustp.hxx> +#include <xmloff/families.hxx> +#include <xmloff/maptype.hxx> +#include <format.hxx> +#include <fmtpdsc.hxx> +#include <pagedesc.hxx> +#include <cellatr.hxx> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/util/Color.hpp> +#include "xmlexp.hxx" +#include <SwStyleNameMapper.hxx> +#include <osl/diagnose.h> +#include <comphelper/sequenceashashmap.hxx> + +#include <svx/unoapi.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdmodel.hxx> +#include <docmodel/theme/ThemeColorType.hxx> +#include <docmodel/theme/Theme.hxx> + + +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::lang; +using namespace ::xmloff::token; + +void SwXMLExport::ExportFormat(const SwFormat& rFormat, enum XMLTokenEnum eFamily, + ::std::optional<OUString> const oStyleName) +{ + // <style:style ...> + CheckAttrList(); + + // style:family="..." + OSL_ENSURE( RES_FRMFMT==rFormat.Which(), "frame format expected" ); + if( RES_FRMFMT != rFormat.Which() ) + return; + OSL_ENSURE( eFamily != XML_TOKEN_INVALID, "family must be specified" ); + // style:name="..." + assert(oStyleName || (eFamily != XML_TABLE_ROW && eFamily != XML_TABLE_CELL)); + bool bEncoded = false; + OUString const name(oStyleName ? *oStyleName : rFormat.GetName()); + AddAttribute(XML_NAMESPACE_STYLE, XML_NAME, EncodeStyleName(name, &bEncoded)); + if( bEncoded ) + { + AddAttribute(XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, name); + } + + if( eFamily != XML_TOKEN_INVALID ) + AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, eFamily ); + +#if OSL_DEBUG_LEVEL > 0 + // style:parent-style-name="..." (if it's not the default only) + const SwFormat* pParent = rFormat.DerivedFrom(); + // Only adopt parent name, if it's not the default + OSL_ENSURE( !pParent || pParent->IsDefault(), "unexpected parent" ); + + OSL_ENSURE( USHRT_MAX == rFormat.GetPoolFormatId(), "pool ids aren't supported" ); + OSL_ENSURE( USHRT_MAX == rFormat.GetPoolHelpId(), "help ids aren't supported" ); + OSL_ENSURE( USHRT_MAX == rFormat.GetPoolHelpId() || + UCHAR_MAX == rFormat.GetPoolHlpFileId(), "help file ids aren't supported" ); +#endif + + // style:master-page-name + if( RES_FRMFMT == rFormat.Which() && XML_TABLE == eFamily ) + { + if( const SwFormatPageDesc* pItem = rFormat.GetAttrSet().GetItemIfSet( RES_PAGEDESC, + false ) ) + { + OUString sName; + const SwPageDesc *pPageDesc = pItem->GetPageDesc(); + if( pPageDesc ) + SwStyleNameMapper::FillProgName( + pPageDesc->GetName(), + sName, + SwGetPoolIdFromName::PageDesc); + AddAttribute( XML_NAMESPACE_STYLE, XML_MASTER_PAGE_NAME, + EncodeStyleName( sName ) ); + } + } + + if( XML_TABLE_CELL == eFamily ) + { + OSL_ENSURE(RES_FRMFMT == rFormat.Which(), "only frame format"); + + if( const SwTableBoxNumFormat *pItem = + rFormat.GetAttrSet().GetItemIfSet( RES_BOXATR_FORMAT, false ) ) + { + sal_Int32 nFormat = static_cast<sal_Int32>(pItem->GetValue()); + + if ( (nFormat != -1) && (nFormat != static_cast<sal_Int32>(getSwDefaultTextFormat())) ) + { + // if we have a format, register and then export + // (Careful: here we assume that data styles will be + // written after cell styles) + addDataStyle(nFormat); + OUString sDataStyleName = getDataStyleName(nFormat); + if( !sDataStyleName.isEmpty() ) + AddAttribute( XML_NAMESPACE_STYLE, XML_DATA_STYLE_NAME, + sDataStyleName ); + } + } + } + + { + SvXMLElementExport aElem( *this, XML_NAMESPACE_STYLE, XML_STYLE, + true, true ); + + SvXMLItemMapEntriesRef xItemMap; + XMLTokenEnum ePropToken = XML_TABLE_PROPERTIES; + if( XML_TABLE == eFamily ) + { + xItemMap = m_xTableItemMap; + } + else if( XML_TABLE_ROW == eFamily ) + { + xItemMap = m_xTableRowItemMap; + ePropToken = XML_TABLE_ROW_PROPERTIES; + } + else if( XML_TABLE_CELL == eFamily ) + { + xItemMap = m_xTableCellItemMap; + ePropToken = XML_TABLE_CELL_PROPERTIES; + } + + if( xItemMap.is() ) + { + m_pTableItemMapper->setMapEntries( xItemMap ); + m_pTableItemMapper->exportXML( *this, + rFormat.GetAttrSet(), + GetTwipUnitConverter(), + ePropToken ); + } + } +} + +void SwXMLExport::ExportStyles_( bool bUsed ) +{ + SvXMLExport::ExportStyles_( bUsed ); + + // drawing defaults + GetShapeExport()->ExportGraphicDefaults(); + + GetTextParagraphExport()->exportTextStyles( bUsed + ,IsShowProgress() + ); + collectDataStyles(true); + exportDataStyles(); + GetShapeExport()->GetShapeTableExport()->exportTableStyles(); + //page defaults + GetPageExport()->exportDefaultStyle(); + + // Theme + exportTheme(); +} + +void SwXMLExport::exportTheme() +{ + if ((getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0) + return; + + uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(GetModel(), UNO_QUERY); + if (!xDrawPageSupplier.is()) + return; + + uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage(); + if (!xDrawPage.is()) + return; + + SdrPage* pPage = GetSdrPageFromXDrawPage(xDrawPage); + SAL_WARN_IF(!pPage, "oox", "Can't get SdrPage from XDrawPage"); + + if (!pPage) + return; + + auto const& pTheme = pPage->getSdrModelFromSdrPage().getTheme(); + if (!pTheme) + return; + + ExportThemeElement(pTheme); +} + +void SwXMLExport::collectAutoStyles() +{ + SvXMLExport::collectAutoStyles(); + + if (mbAutoStylesCollected) + return; + + // The order in which styles are collected *MUST* be the same as + // the order in which they are exported. Otherwise, caching will + // fail. + if( getExportFlags() & (SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT) ) + { + if( !(getExportFlags() & SvXMLExportFlags::CONTENT) ) + { + // only master pages are exported => styles for frames bound + // to frames (but none for frames bound to pages) need to be + // collected. + // TODO: exclude PageBoundFrames on export + } + } + + // exported in _ExportMasterStyles + if( getExportFlags() & SvXMLExportFlags::MASTERSTYLES ) + GetPageExport()->collectAutoStyles( false ); + + + // exported in ExportContent_ + if( getExportFlags() & SvXMLExportFlags::CONTENT ) + { + // collect form autostyle + // (do this before collectTextAutoStyles, 'cause the shapes need the results of the work + // done by examineForms) + Reference<XDrawPageSupplier> xDrawPageSupplier( GetModel(), UNO_QUERY ); + if (xDrawPageSupplier.is() && GetFormExport().is()) + { + Reference<XDrawPage> xPage = xDrawPageSupplier->getDrawPage(); + if (xPage.is()) + GetFormExport()->examineForms(xPage); + } + + GetTextParagraphExport()->collectTextAutoStylesOptimized( m_bShowProgress ); + } + + mbAutoStylesCollected = true; +} + +void SwXMLExport::ExportAutoStyles_() +{ + collectAutoStyles(); + + // if we don't export styles (i.e. in content stream only, but not + // in single-stream case), then we can save ourselves a bit of + // work and memory by not collecting field masters + if( !(getExportFlags() & SvXMLExportFlags::STYLES) ) + GetTextParagraphExport()->exportUsedDeclarations(); + + // exported in ExportContent_ + if( getExportFlags() & SvXMLExportFlags::CONTENT ) + { + GetTextParagraphExport()->exportTrackedChanges( true ); + } + + GetTextParagraphExport()->exportTextAutoStyles(); + GetShapeExport()->exportAutoStyles(); + if( getExportFlags() & SvXMLExportFlags::MASTERSTYLES ) + GetPageExport()->exportAutoStyles(); + + // we rely on data styles being written after cell styles in the + // ExportFormat() method; so be careful when changing order. + exportAutoDataStyles(); + + SvXMLExportFlags nContentAutostyles = SvXMLExportFlags::CONTENT | SvXMLExportFlags::AUTOSTYLES; + if ( ( getExportFlags() & nContentAutostyles ) == nContentAutostyles ) + GetFormExport()->exportAutoStyles(); +} + +XMLPageExport* SwXMLExport::CreatePageExport() +{ + return new XMLTextMasterPageExport( *this ); +} + +void SwXMLExport::ExportMasterStyles_() +{ + // export master styles + GetPageExport()->exportMasterStyles( false ); +} + +namespace { + +class SwXMLAutoStylePoolP : public SvXMLAutoStylePoolP +{ + SvXMLExport& m_rExport; + const OUString m_sListStyleName; + const OUString m_sMasterPageName; + +protected: + + virtual void exportStyleAttributes( + comphelper::AttributeList& rAttrList, + XmlStyleFamily nFamily, + const std::vector< XMLPropertyState >& rProperties, + const SvXMLExportPropertyMapper& rPropExp + , const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap + ) const override; +public: + + explicit SwXMLAutoStylePoolP( SvXMLExport& rExport ); +}; + +} + +void SwXMLAutoStylePoolP::exportStyleAttributes( + comphelper::AttributeList& rAttrList, + XmlStyleFamily nFamily, + const std::vector< XMLPropertyState >& rProperties, + const SvXMLExportPropertyMapper& rPropExp + , const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap + ) const +{ + SvXMLAutoStylePoolP::exportStyleAttributes( rAttrList, nFamily, rProperties, rPropExp, rUnitConverter, rNamespaceMap); + + if( XmlStyleFamily::TEXT_PARAGRAPH != nFamily ) + return; + + for( const auto& rProperty : rProperties ) + { + if (rProperty.mnIndex != -1) // #i26762# + { + switch( rPropExp.getPropertySetMapper()-> + GetEntryContextId( rProperty.mnIndex ) ) + { + case CTF_NUMBERINGSTYLENAME: + { + OUString sStyleName; + rProperty.maValue >>= sStyleName; + // #i70748# - export also empty list styles + if( !sStyleName.isEmpty() ) + { + OUString sTmp = m_rExport.GetTextParagraphExport()->GetListAutoStylePool().Find( sStyleName ); + if( !sTmp.isEmpty() ) + sStyleName = sTmp; + } + GetExport().AddAttribute( XML_NAMESPACE_STYLE, + m_sListStyleName, + GetExport().EncodeStyleName( sStyleName ) ); + } + break; + case CTF_PAGEDESCNAME: + { + OUString sStyleName; + rProperty.maValue >>= sStyleName; + GetExport().AddAttribute( XML_NAMESPACE_STYLE, + m_sMasterPageName, + GetExport().EncodeStyleName( sStyleName ) ); + } + break; + } + } + } +} + +SwXMLAutoStylePoolP::SwXMLAutoStylePoolP(SvXMLExport& rExp ) : + SvXMLAutoStylePoolP( rExp ), + m_rExport( rExp ), + m_sListStyleName( GetXMLToken( XML_LIST_STYLE_NAME ) ), + m_sMasterPageName( GetXMLToken( XML_MASTER_PAGE_NAME ) ) +{ +} + +SvXMLAutoStylePoolP* SwXMLExport::CreateAutoStylePool() +{ + return new SwXMLAutoStylePoolP( *this ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlfonte.cxx b/sw/source/filter/xml/xmlfonte.cxx new file mode 100644 index 0000000000..c6a9c89cb6 --- /dev/null +++ b/sw/source/filter/xml/xmlfonte.cxx @@ -0,0 +1,143 @@ +/* -*- 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 <hintids.hxx> +#include <xmloff/XMLFontAutoStylePool.hxx> +#include <editeng/fontitem.hxx> +#include <doc.hxx> +#include "xmlexp.hxx" +#include "xmlimp.hxx" +#include <IDocumentSettingAccess.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::text; + +namespace { + +class SwXMLFontAutoStylePool_Impl: public XMLFontAutoStylePool +{ +public: + SwXMLFontAutoStylePool_Impl(SwXMLExport& rExport, bool bFontEmbedding); +}; + +} + +namespace +{ +sal_Int32 CompareTo(sal_Int32 nA, sal_Int32 nB) +{ + if (nA < nB) + { + return -1; + } + + if (nA > nB) + { + return 1; + } + + return 0; +} +} + +SwXMLFontAutoStylePool_Impl::SwXMLFontAutoStylePool_Impl(SwXMLExport& _rExport, bool bFontEmbedding) + : XMLFontAutoStylePool(_rExport, bFontEmbedding) +{ + sal_uInt16 const aWhichIds[3] = { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, + RES_CHRATR_CTL_FONT }; + + const SfxItemPool& rPool = _rExport.getDoc()->GetAttrPool(); + std::vector<const SvxFontItem *> aFonts; + for(sal_uInt16 nWhichId : aWhichIds) + { + const SvxFontItem& rFont = + static_cast<const SvxFontItem&>(rPool.GetDefaultItem( nWhichId )); + aFonts.push_back(&rFont); + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nWhichId)) + { + auto pFont = static_cast<const SvxFontItem *>(pItem); + aFonts.push_back(pFont); + } + } + + std::sort(aFonts.begin(), aFonts.end(), + [](const SvxFontItem* pA, const SvxFontItem* pB) -> bool + { + sal_Int32 nRet = pA->GetFamilyName().compareTo(pB->GetFamilyName()); + if (nRet != 0) + { + return nRet < 0; + } + + nRet = pA->GetStyleName().compareTo(pB->GetStyleName()); + if (nRet != 0) + { + return nRet < 0; + } + + nRet = CompareTo(pA->GetFamily(), pB->GetFamily()); + if (nRet != 0) + { + return nRet < 0; + } + + nRet = CompareTo(pA->GetPitch(), pB->GetPitch()); + if (nRet != 0) + { + return nRet < 0; + } + + return pA->GetCharSet() < pB->GetCharSet(); + }); + for (const auto& pFont : aFonts) + { + Add(pFont->GetFamilyName(), pFont->GetStyleName(), pFont->GetFamily(), pFont->GetPitch(), + pFont->GetCharSet()); + } + + auto const & pDocument = _rExport.getDoc(); + + m_bEmbedUsedOnly = pDocument->getIDocumentSettingAccess().get(DocumentSettingId::EMBED_USED_FONTS); + m_bEmbedLatinScript = pDocument->getIDocumentSettingAccess().get(DocumentSettingId::EMBED_LATIN_SCRIPT_FONTS); + m_bEmbedAsianScript = pDocument->getIDocumentSettingAccess().get(DocumentSettingId::EMBED_ASIAN_SCRIPT_FONTS); + m_bEmbedComplexScript = pDocument->getIDocumentSettingAccess().get(DocumentSettingId::EMBED_COMPLEX_SCRIPT_FONTS); + +} + +XMLFontAutoStylePool* SwXMLExport::CreateFontAutoStylePool() +{ + bool blockFontEmbedding = false; + // We write font info to both content.xml and styles.xml, but they are both + // written by different SwXMLExport instance, and would therefore write each + // font file twice without complicated checking for duplicates, so handle + // the embedding only in one of them. + if( !( getExportFlags() & SvXMLExportFlags::CONTENT) ) + blockFontEmbedding = true; + if( !getDoc()->getIDocumentSettingAccess().get( DocumentSettingId::EMBED_FONTS )) + blockFontEmbedding = true; + return new SwXMLFontAutoStylePool_Impl( *this, !blockFontEmbedding ); +} + +void SwXMLImport::NotifyContainsEmbeddedFont() +{ + getDoc()->getIDocumentSettingAccess().set( DocumentSettingId::EMBED_FONTS, true ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlimp.cxx b/sw/source/filter/xml/xmlimp.cxx new file mode 100644 index 0000000000..81ddfbbb5f --- /dev/null +++ b/sw/source/filter/xml/xmlimp.cxx @@ -0,0 +1,1924 @@ +/* -*- 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 <sal/config.h> +#include <sal/log.hxx> + +#include <cassert> + +#include <com/sun/star/document/PrinterIndependentLayout.hpp> +#include <com/sun/star/document/XExporter.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/text/XTextRange.hpp> + +#include <o3tl/any.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlictxt.hxx> +#include <xmloff/txtimp.hxx> +#include <xmloff/XMLTextShapeImportHelper.hxx> +#include <xmloff/XMLFontStylesContext.hxx> +#include <xmloff/ProgressBarHelper.hxx> +#include <doc.hxx> +#include <drawdoc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentListsAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <TextCursorHelper.hxx> +#include <unotext.hxx> +#include <unotextrange.hxx> +#include <poolfmt.hxx> +#include <ndtxt.hxx> +#include <editsh.hxx> +#include <strings.hrc> +#include <svl/stritem.hxx> +#include "xmlimp.hxx" +#include "xmlimpit.hxx" +#include "xmltexti.hxx" +#include <list.hxx> +#include <swdll.hxx> +#include <xmloff/DocumentSettingsContext.hxx> +#include <docsh.hxx> +#include <svx/xmlgrhlp.hxx> +#include <svx/xmleohlp.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/sfxmodelfactory.hxx> +#include <xmloff/xmluconv.hxx> +#include <unotools/fcm.hxx> +#include <unotools/mediadescriptor.hxx> +#include <unotools/streamwrap.hxx> +#include <unotools/tempfile.hxx> +#include <tools/UnitConversion.hxx> +#include <comphelper/diagnose_ex.hxx> + +#include <vcl/svapp.hxx> +#include <unotxdoc.hxx> +#include <numrule.hxx> + +#include <xmloff/xmlmetai.hxx> +#include <xmloff/xformsimport.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <officecfg/Office/Common.hxx> + +#include <unordered_set> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::xforms; +using namespace ::xmloff::token; + +namespace { + +class SwXMLBodyContext_Impl : public SvXMLImportContext +{ + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } + +public: + + SwXMLBodyContext_Impl( SwXMLImport& rImport ); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; +}; + +} + +SwXMLBodyContext_Impl::SwXMLBodyContext_Impl( SwXMLImport& rImport ) : + SvXMLImportContext( rImport ) +{ + // tdf#107211: if at this point we don't have a defined char style "Default" + // or "Default Style", add a mapping for it as it is not written + // into the file since it's not really a style but "no style" + // (hence referencing it actually makes no sense except for hyperlinks + // which default to something other than "Default") + OUString const sDefault(SwResId(STR_POOLCHR_STANDARD)); + uno::Reference<container::XNameContainer> const& xStyles( + rImport.GetTextImport()->GetTextStyles()); + if (!xStyles->hasByName("Default")) + { // this old name was used before LO 4.0 + rImport.AddStyleDisplayName(XmlStyleFamily::TEXT_TEXT, "Default", sDefault); + } + if (!xStyles->hasByName("Default_20_Style")) + { // this new name contains a space which is converted to _20_ on export + rImport.AddStyleDisplayName(XmlStyleFamily::TEXT_TEXT, "Default_20_Style", sDefault); + } + bool isEncoded(false); + OUString const defaultEncoded( + rImport.GetMM100UnitConverter().encodeStyleName(sDefault, &isEncoded)); + if (isEncoded && defaultEncoded != "Default_20_Style" + && !xStyles->hasByName(defaultEncoded)) + { // new name may contain a space which is converted to _20_ on export + rImport.AddStyleDisplayName(XmlStyleFamily::TEXT_TEXT, defaultEncoded, sDefault); + } +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLBodyContext_Impl::createFastChildContext( + sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + return GetSwImport().CreateBodyContentContext(); +} + +namespace { + +// #i69629# +// enhance class <SwXMLDocContext_Impl> in order to be able to create subclasses +// NB: virtually inherit so we can multiply inherit properly +// in SwXMLOfficeDocContext_Impl +class SwXMLDocContext_Impl : public virtual SvXMLImportContext +{ + sal_Int32 mnElement; + +protected: // #i69629# + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } + +public: + SwXMLDocContext_Impl( SwXMLImport& rImport, sal_Int32 nElement ); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; +}; + +} + +SwXMLDocContext_Impl::SwXMLDocContext_Impl( SwXMLImport& rImport, sal_Int32 nElement ) : + SvXMLImportContext( rImport ), mnElement(nElement) +{ +} + +uno::Reference< xml::sax::XFastContextHandler > SAL_CALL SwXMLDocContext_Impl::createFastChildContext( + sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + switch (nElement) + { + case XML_ELEMENT(OFFICE, XML_SCRIPTS): + return GetSwImport().CreateScriptContext(); + case XML_ELEMENT(OFFICE, XML_SETTINGS): + return new XMLDocumentSettingsContext( GetImport() ); + case XML_ELEMENT(OFFICE, XML_STYLES): + GetSwImport().GetProgressBarHelper()->Increment( PROGRESS_BAR_STEP ); + return GetSwImport().CreateStylesContext( false ); + case XML_ELEMENT(OFFICE, XML_AUTOMATIC_STYLES): + // don't use the autostyles from the styles-document for the progress + if ( mnElement != 0 && (mnElement & TOKEN_MASK) != XML_DOCUMENT_STYLES ) + GetSwImport().GetProgressBarHelper()->Increment + ( PROGRESS_BAR_STEP ); + return GetSwImport().CreateStylesContext( true ); + case XML_ELEMENT(OFFICE, XML_MASTER_STYLES): + return GetSwImport().CreateMasterStylesContext(); + case XML_ELEMENT(OFFICE, XML_FONT_FACE_DECLS): + return GetSwImport().CreateFontDeclsContext(); + case XML_ELEMENT(OFFICE, XML_META): + OSL_FAIL(" XML_ELEMENT(OFFICE, XML_META): should not have come here, maybe document is invalid?"); + break; + case XML_ELEMENT(OFFICE, XML_BODY): + GetSwImport().GetProgressBarHelper()->Increment( PROGRESS_BAR_STEP ); + return new SwXMLBodyContext_Impl( GetSwImport() ); + case XML_ELEMENT(XFORMS, XML_MODEL): + return createXFormsModelContext(GetImport()); + default: + XMLOFF_WARN_UNKNOWN_ELEMENT("sw", nElement); + } + return nullptr; +} + +namespace { + +// #i69629# - new subclass <SwXMLOfficeDocContext_Impl> of class <SwXMLDocContext_Impl> +class SwXMLOfficeDocContext_Impl : + public SwXMLDocContext_Impl, public SvXMLMetaDocumentContext +{ +public: + + SwXMLOfficeDocContext_Impl( SwXMLImport& rImport, sal_Int32 nElement, + const Reference< document::XDocumentProperties >& xDocProps); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& Attribs ) override; +}; + +} + +SwXMLOfficeDocContext_Impl::SwXMLOfficeDocContext_Impl( + SwXMLImport& rImport, + sal_Int32 nElement, + const Reference< document::XDocumentProperties >& xDocProps) : + SvXMLImportContext( rImport ), + SwXMLDocContext_Impl( rImport, nElement ), + SvXMLMetaDocumentContext( rImport, xDocProps ) +{ +} + +uno::Reference< xml::sax::XFastContextHandler > SAL_CALL SwXMLOfficeDocContext_Impl::createFastChildContext( + sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) +{ + // assign paragraph styles to list levels of outline style after all styles + // are imported and finished. This is the case, when <office:body> starts + // in flat OpenDocument file format. + { + if( nElement == XML_ELEMENT( OFFICE, XML_BODY ) ) + { + GetImport().GetTextImport()->SetOutlineStyles( true ); + } + } + + // behave like meta base class iff we encounter office:meta + if ( nElement == XML_ELEMENT( OFFICE, XML_META ) ) { + return SvXMLMetaDocumentContext::createFastChildContext( + nElement, xAttrList ); + } else { + return SwXMLDocContext_Impl::createFastChildContext( + nElement, xAttrList ); + } +} + +namespace { + +// #i69629# - new subclass <SwXMLDocStylesContext_Impl> of class <SwXMLDocContext_Impl> +class SwXMLDocStylesContext_Impl : public SwXMLDocContext_Impl +{ +public: + + SwXMLDocStylesContext_Impl( SwXMLImport& rImport, sal_Int32 nElement ); + + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; +}; + +} + +SwXMLDocStylesContext_Impl::SwXMLDocStylesContext_Impl( SwXMLImport& rImport, sal_Int32 nElement ) : + SvXMLImportContext( rImport ), + SwXMLDocContext_Impl( rImport, nElement ) +{ +} + +void SwXMLDocStylesContext_Impl::endFastElement(sal_Int32 ) +{ + // assign paragraph styles to list levels of outline style after all styles + // are imported and finished. + SwXMLImport& rSwImport = dynamic_cast<SwXMLImport&>( GetImport()); + GetImport().GetTextImport()->SetOutlineStyles( + bool(rSwImport.GetStyleFamilyMask() & SfxStyleFamily::Para)); +} + +SvXMLImportContext *SwXMLImport::CreateFastContext( sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + SvXMLImportContext *pContext = nullptr; + + switch (nElement) + { + case XML_ELEMENT( OFFICE, XML_DOCUMENT_META ): + pContext = CreateMetaContext(nElement); + break; + case XML_ELEMENT( OFFICE, XML_DOCUMENT ): + { + uno::Reference<document::XDocumentProperties> const xDocProps( + GetDocumentProperties()); + // flat OpenDocument file format + pContext = new SwXMLOfficeDocContext_Impl( *this, nElement, xDocProps ); + } + break; + // #i69629# - own subclasses for <office:document> and <office:document-styles> + case XML_ELEMENT(OFFICE, XML_DOCUMENT_SETTINGS): + case XML_ELEMENT(OFFICE, XML_DOCUMENT_CONTENT): + pContext = new SwXMLDocContext_Impl( *this, nElement ); + break; + case XML_ELEMENT(OFFICE, XML_DOCUMENT_STYLES): + pContext = new SwXMLDocStylesContext_Impl( *this, nElement ); + break; + } + return pContext; +} + +SwXMLImport::SwXMLImport( + const uno::Reference< uno::XComponentContext >& rContext, + OUString const & implementationName, SvXMLImportFlags nImportFlags) +: SvXMLImport( rContext, implementationName, nImportFlags ), + m_nStyleFamilyMask( SfxStyleFamily::All ), + m_bLoadDoc( true ), + m_bInsert( false ), + m_bBlock( false ), + m_bOrganizerMode( false ), + m_bInititedXForms( false ), + m_pDoc( nullptr ), + m_sDefTableName(SwResId(STR_TABLE_DEFNAME)) +{ + InitItemImport(); +} + +SwXMLImport::~SwXMLImport() noexcept +{ + if (HasShapeImport()) + { + SAL_WARN("sw", "endDocument skipped, dropping shapes now to avoid dangling SvTextShapeImportHelper pointing to this"); + ClearShapeImport(); + } + FinitItemImport(); + // Call cleanup() here because the destruction of some stuff like XMLRedlineImportHelper will cast + // to cast their mrImport to SwXMLImport and that is illegal after this destructor is done. + cleanup(); +} + +void SwXMLImport::setTextInsertMode( + const Reference< XTextRange > & rInsertPos ) +{ + m_bInsert = true; + + Reference < XText > xText = rInsertPos->getText(); + Reference < XTextCursor > xTextCursor = + xText->createTextCursorByRange( rInsertPos ); + GetTextImport()->SetCursor( xTextCursor ); +} + +void SwXMLImport::setStyleInsertMode( SfxStyleFamily nFamilies, + bool bOverwrite ) +{ + m_bInsert = !bOverwrite; + m_nStyleFamilyMask = nFamilies; + m_bLoadDoc = false; +} + +static OTextCursorHelper *lcl_xml_GetSwXTextCursor( const Reference < XTextCursor >& rTextCursor ) +{ + OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(rTextCursor.get()); + OSL_ENSURE( pTextCursor, "SwXTextCursor missing" ); + return pTextCursor; +} + +void SwXMLImport::startDocument() +{ + // delegate to parent + SvXMLImport::startDocument(); + + OSL_ENSURE( GetModel().is(), "model is missing" ); + if( !GetModel().is() ) + return; + + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + Reference< XPropertySet > xImportInfo( getImportInfo() ); + Reference< XPropertySetInfo > xPropertySetInfo; + if( xImportInfo.is() ) + xPropertySetInfo = xImportInfo->getPropertySetInfo(); + if( xPropertySetInfo.is() ) + { + Any aAny; + // insert style mode? + OUString sStyleInsertModeFamilies("StyleInsertModeFamilies"); + if( xPropertySetInfo->hasPropertyByName(sStyleInsertModeFamilies) ) + { + aAny = xImportInfo->getPropertyValue(sStyleInsertModeFamilies); + Sequence< OUString> aFamiliesSeq; + if( aAny >>= aFamiliesSeq ) + { + SfxStyleFamily nFamilyMask = SfxStyleFamily::None; + for( const OUString& rFamily : std::as_const(aFamiliesSeq) ) + { + if( rFamily=="FrameStyles" ) + nFamilyMask |= SfxStyleFamily::Frame; + else if( rFamily=="PageStyles" ) + nFamilyMask |= SfxStyleFamily::Page; + else if( rFamily=="CharacterStyles" ) + nFamilyMask |= SfxStyleFamily::Char; + else if( rFamily=="ParagraphStyles" ) + nFamilyMask |= SfxStyleFamily::Para; + else if( rFamily=="NumberingStyles" ) + nFamilyMask |= SfxStyleFamily::Pseudo; + } + + bool bOverwrite = false; + static constexpr OUString sStyleInsertModeOverwrite(u"StyleInsertModeOverwrite"_ustr); + if( xPropertySetInfo->hasPropertyByName(sStyleInsertModeOverwrite) ) + { + aAny = xImportInfo->getPropertyValue(sStyleInsertModeOverwrite); + if( auto b = o3tl::tryAccess<bool>(aAny) ) + { + if( *b ) + bOverwrite = true; + } + } + + setStyleInsertMode( nFamilyMask, bOverwrite ); + } + } + + // text insert mode? + static constexpr OUString sTextInsertModeRange(u"TextInsertModeRange"_ustr); + if( xPropertySetInfo->hasPropertyByName(sTextInsertModeRange) ) + { + aAny = xImportInfo->getPropertyValue(sTextInsertModeRange); + Reference<XTextRange> xInsertTextRange; + if( aAny >>= xInsertTextRange ) + setTextInsertMode( xInsertTextRange ); + } + + // auto text mode + static constexpr OUString sAutoTextMode(u"AutoTextMode"_ustr); + if( xPropertySetInfo->hasPropertyByName(sAutoTextMode) ) + { + aAny = xImportInfo->getPropertyValue(sAutoTextMode); + if( auto b = o3tl::tryAccess<bool>(aAny) ) + { + if( *b ) + m_bBlock = true; + } + } + + // organizer mode + static constexpr OUString sOrganizerMode(u"OrganizerMode"_ustr); + if( xPropertySetInfo->hasPropertyByName(sOrganizerMode) ) + { + aAny = xImportInfo->getPropertyValue(sOrganizerMode); + if( auto b = o3tl::tryAccess<bool>(aAny) ) + { + if( *b ) + m_bOrganizerMode = true; + } + } + + // default document properties + static constexpr OUString sDefSettings(u"DefaultDocumentSettings"_ustr); + if (xPropertySetInfo->hasPropertyByName(sDefSettings)) + { + aAny = xImportInfo->getPropertyValue(sDefSettings); + Sequence<PropertyValue> aProps; + if (aAny >>= aProps) + { + Reference<lang::XMultiServiceFactory> xFac(GetModel(), UNO_QUERY); + Reference<XPropertySet> xProps( + xFac->createInstance("com.sun.star.document.Settings"), UNO_QUERY); + Reference<XPropertySetInfo> xInfo(xProps->getPropertySetInfo()); + + if (xProps.is() && xInfo.is()) + { + for (const auto& rProp : std::as_const(aProps)) + { + if (xInfo->hasPropertyByName(rProp.Name)) + { + xProps->setPropertyValue(rProp.Name, rProp.Value); + } + } + } + } + } + } + + // There only is a text cursor by now if we are in insert mode. In any + // other case we have to create one at the start of the document. + // We also might change into the insert mode later, so we have to make + // sure to first set the insert mode and then create the text import + // helper. Otherwise it won't have the insert flag set! + OTextCursorHelper *pTextCursor = nullptr; + Reference < XTextCursor > xTextCursor; + if( HasTextImport() ) + xTextCursor = GetTextImport()->GetCursor(); + if( !xTextCursor.is() ) + { + Reference < XTextDocument > xTextDoc( GetModel(), UNO_QUERY ); + Reference < XText > xText = xTextDoc->getText(); + xTextCursor = xText->createTextCursor(); + SwCursorShell *pCursorSh = nullptr; + SwDoc *pDoc = nullptr; + if( SvXMLImportFlags::ALL == getImportFlags() ) + { + pTextCursor = lcl_xml_GetSwXTextCursor( xTextCursor ); + OSL_ENSURE( pTextCursor, "SwXTextCursor missing" ); + if( !pTextCursor ) + return; + + pDoc = pTextCursor->GetDoc(); + OSL_ENSURE( pDoc, "SwDoc missing" ); + if( !pDoc ) + return; + + // Is there an edit shell. If yes, then we are currently inserting + // a document. We then have to insert at the current edit shell's + // cursor position. That not quite clean code, but there is no other + // way currently. + pCursorSh = pDoc->GetEditShell(); + } + if( pCursorSh ) + { + const rtl::Reference<SwXTextRange> xInsertTextRange( + SwXTextRange::CreateXTextRange( + *pDoc, *pCursorSh->GetCursor()->GetPoint(), nullptr ) ); + setTextInsertMode( xInsertTextRange ); + xTextCursor = GetTextImport()->GetCursor(); + pTextCursor = nullptr; + } + else + GetTextImport()->SetCursor( xTextCursor ); + } + + if( !(getImportFlags() & (SvXMLImportFlags::CONTENT|SvXMLImportFlags::MASTERSTYLES)) ) + return; + + if( !pTextCursor ) + pTextCursor = lcl_xml_GetSwXTextCursor( xTextCursor ); + OSL_ENSURE( pTextCursor, "SwXTextCursor missing" ); + if( !pTextCursor ) + return; + + SwDoc *pDoc = pTextCursor->GetDoc(); + OSL_ENSURE( pDoc, "SwDoc missing" ); + if( !pDoc ) + return; + + if (SvXMLImportFlags::ALL == getImportFlags()) + { + // for flat ODF - this is done in SwReader::Read() for package ODF + pDoc->SetInReading(true); + pDoc->SetInXMLImport(true); + } + + if( (getImportFlags() & SvXMLImportFlags::CONTENT) && !IsStylesOnlyMode() ) + { + m_oSttNdIdx.emplace( pDoc->GetNodes() ); + if( IsInsertMode() ) + { + SwPaM *pPaM = pTextCursor->GetPaM(); + const SwPosition* pPos = pPaM->GetPoint(); + + // Split once and remember the node that has been split. + pDoc->getIDocumentContentOperations().SplitNode( *pPos, false ); + *m_oSttNdIdx = pPos->GetNodeIndex()-1; + + // Split again. + pDoc->getIDocumentContentOperations().SplitNode( *pPos, false ); + + // Insert all content into the new node + pPaM->Move( fnMoveBackward ); + pDoc->SetTextFormatColl + ( *pPaM, pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false ) ); + } + } + + // We need a draw model to be able to set the z order + pDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); // #i52858# - method name changed + + // SJ: #i49801# locking the model to disable repaints + SwDrawModel* pDrawModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel(); + if ( pDrawModel ) + pDrawModel->setLock(true); + + if (!GetGraphicStorageHandler().is()) + { + m_xGraphicStorageHandler = SvXMLGraphicHelper::Create(SvXMLGraphicHelperMode::Read); + SetGraphicStorageHandler(m_xGraphicStorageHandler); + } + + if( !GetEmbeddedResolver().is() ) + { + SfxObjectShell *pPersist = pDoc->GetPersist(); + if( pPersist ) + { + m_xEmbeddedResolver = SvXMLEmbeddedObjectHelper::Create( + *pPersist, + SvXMLEmbeddedObjectHelperMode::Read ); + SetEmbeddedResolver( m_xEmbeddedResolver ); + } + } +} + +void SwXMLImport::endDocument() +{ + OSL_ENSURE( GetModel().is(), "model missing; maybe startDocument wasn't called?" ); + if( !GetModel().is() ) + return; + + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + if (m_xGraphicStorageHandler) + m_xGraphicStorageHandler->dispose(); + m_xGraphicStorageHandler.clear(); + + if( m_xEmbeddedResolver ) + m_xEmbeddedResolver->dispose(); + m_xEmbeddedResolver.clear(); + // Clear the shape import to sort the shapes (and not in the + // destructor that might be called after the import has finished + // for Java filters. + if( HasShapeImport() ) + ClearShapeImport(); + + SwDoc *pDoc = nullptr; + if( (getImportFlags() & SvXMLImportFlags::CONTENT) && !IsStylesOnlyMode() ) + { + Reference<XInterface> xCursorTunnel( GetTextImport()->GetCursor(), + UNO_QUERY); + assert(xCursorTunnel.is() && "missing XUnoTunnel for Cursor"); + OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(xCursorTunnel.get()); + assert(pTextCursor && "SwXTextCursor missing"); + SwPaM *pPaM = pTextCursor->GetPaM(); + if( IsInsertMode() && m_oSttNdIdx->GetIndex() ) + { + // If we are in insert mode, join the split node that is in front + // of the new content with the first new node. Or in other words: + // Revert the first split node. + SwTextNode* pTextNode = m_oSttNdIdx->GetNode().GetTextNode(); + SwNodeIndex aNxtIdx( *m_oSttNdIdx ); + if( pTextNode && pTextNode->CanJoinNext( &aNxtIdx ) && + m_oSttNdIdx->GetIndex() + 1 == aNxtIdx.GetIndex() ) + { + // If the PaM points to the first new node, move the PaM to the + // end of the previous node. + if( pPaM->GetPoint()->GetNode() == aNxtIdx.GetNode() ) + { + pPaM->GetPoint()->Assign( *pTextNode, + pTextNode->GetText().getLength()); + } + +#if OSL_DEBUG_LEVEL > 0 + // !!! This should be impossible !!!! + OSL_ENSURE( m_oSttNdIdx->GetIndex()+1 != + pPaM->GetBound().GetNodeIndex(), + "PaM.Bound1 point to new node " ); + OSL_ENSURE( m_oSttNdIdx->GetIndex()+1 != + pPaM->GetBound( false ).GetNodeIndex(), + "PaM.Bound2 points to new node" ); + + if( m_oSttNdIdx->GetIndex()+1 == + pPaM->GetBound().GetNodeIndex() ) + { + const sal_Int32 nCntPos = + pPaM->GetBound().GetContentIndex(); + pPaM->GetBound().SetContent( + pTextNode->GetText().getLength() + nCntPos ); + } + if( m_oSttNdIdx->GetIndex()+1 == + pPaM->GetBound( false ).GetNodeIndex() ) + { + const sal_Int32 nCntPos = + pPaM->GetBound( false ).GetContentIndex(); + pPaM->GetBound( false ).SetContent( + pTextNode->GetText().getLength() + nCntPos ); + } +#endif + // If the first new node isn't empty, convert the node's text + // attributes into hints. Otherwise, set the new node's + // paragraph style at the previous (empty) node. + SwTextNode* pDelNd = aNxtIdx.GetNode().GetTextNode(); + if (!pTextNode->GetText().isEmpty()) + pDelNd->FormatToTextAttr( pTextNode ); + else + { + pTextNode->ResetAttr(RES_CHRATR_BEGIN, RES_CHRATR_END); + pTextNode->ChgFormatColl( pDelNd->GetTextColl() ); + if (!pDelNd->GetNoCondAttr(RES_PARATR_LIST_ID, /*bInParents=*/false)) + { + // MergeListsAtDocumentInsertPosition() will deal with lists below, copy + // paragraph direct formatting otherwise. + pDelNd->CopyCollFormat(*pTextNode); + } + } + pTextNode->JoinNext(); + } + } + + SwPosition* pPos = pPaM->GetPoint(); + OSL_ENSURE( !pPos->GetContentIndex(), "last paragraph isn't empty" ); + if( !pPos->GetContentIndex() ) + { + SwTextNode* pCurrNd; + SwNodeOffset nNodeIdx = pPos->GetNodeIndex(); + pDoc = &pPaM->GetDoc(); + + OSL_ENSURE( pPos->GetNode().IsContentNode(), + "insert position is not a content node" ); + if( !IsInsertMode() ) + { + // If we're not in insert mode, the last node is deleted. + const SwNode *pPrev = pDoc->GetNodes()[nNodeIdx -1]; + if( pPrev->IsContentNode() || + ( pPrev->IsEndNode() && + pPrev->StartOfSectionNode()->IsSectionNode() ) ) + { + SwContentNode* pCNd = pPaM->GetPointContentNode(); + if( pCNd && pCNd->StartOfSectionIndex()+2 < + pCNd->EndOfSectionIndex() ) + { + SwNode& rDelNode = pPaM->GetPoint()->GetNode(); + // move so we don't have a dangling SwContentIndex to the deleted node + pPaM->GetPoint()->Adjust(SwNodeOffset(+1)); + if (pPaM->HasMark()) + pPaM->GetMark()->Adjust(SwNodeOffset(+1)); + pDoc->GetNodes().Delete( rDelNode ); + } + } + } + else if( nullptr != (pCurrNd = pDoc->GetNodes()[nNodeIdx]->GetTextNode()) ) + { + // Id we're in insert mode, the empty node is joined with + // the next and the previous one. + if( pCurrNd->CanJoinNext( pPos )) + { + SwTextNode* pNextNd = pPos->GetNode().GetTextNode(); + bool endNodeFound = pDoc->GetNodes()[nNodeIdx-1]->IsEndNode(); + SwNode *pLastPar = pDoc->GetNodes()[nNodeIdx -2]; + if ( !pLastPar->IsTextNode() ) { + pLastPar = pDoc->GetNodes()[nNodeIdx -1]; + } + if ( !endNodeFound && pLastPar->IsTextNode() ) + { + pNextNd->ChgFormatColl(pLastPar->GetTextNode()->GetTextColl()); + } + + pPaM->SetMark(); pPaM->DeleteMark(); + pNextNd->JoinPrev(); + + // Remove line break that has been inserted by the import, + // but only if one has been inserted and + // no endNode found to avoid removing section + if( pNextNd->CanJoinPrev(/* &pPos->nNode*/ ) && !endNodeFound && + *m_oSttNdIdx != pPos->GetNode() ) + { + pNextNd->JoinPrev(); + } + } + else if (pCurrNd->GetText().isEmpty()) + { + pPaM->SetMark(); pPaM->DeleteMark(); + SwNode& rDelNode = pPos->GetNode(); + // move so we don't have a dangling SwContentIndex to the deleted node + pPaM->GetPoint()->Adjust(SwNodeOffset(+1)); + pDoc->GetNodes().Delete( rDelNode ); + pPaM->Move( fnMoveBackward ); + } + } + + // tdf#113877 + // when we insert one document with list inside into another one with list at the insert position, + // the resulting numbering in these lists is not consequent. + // + // Main document: + // 1. One + // 2. Two + // 3. Three + // 4. <-- insert position + // + // Inserted document: + // 1. One + // 2. Two + // 3. Three + // 4. + // + // Expected result + // 1. One + // 2. Two + // 3. Three + // 4. One + // 5. Two + // 6. Three + // 7. + // + MergeListsAtDocumentInsertPosition(pDoc); + } + } + + /* Was called too early. Moved from SwXMLBodyContext_Impl::EndElement */ + + GetTextImport()->RedlineAdjustStartNodeCursor(); + + if( (getImportFlags() & SvXMLImportFlags::CONTENT) || + ((getImportFlags() & SvXMLImportFlags::MASTERSTYLES) && IsStylesOnlyMode()) ) + { + // pDoc might be 0. In this case UpdateTextCollCondition is looking + // for it itself. + UpdateTextCollConditions( pDoc ); + } + + GetTextImport()->ResetCursor(); + + m_oSttNdIdx.reset(); + + // tdf#150753: pDoc may be null e.g. when the package lacks content.xml; + // we should not forget to tidy up here, including unlocking draw model + if (!pDoc) + pDoc = getDoc(); + assert(pDoc); + // SJ: #i49801# -> now permitting repaints + if (getImportFlags() == SvXMLImportFlags::ALL) + { + // Notify math objects. If we are in the package filter this will + // be done by the filter object itself + if (IsInsertMode()) + pDoc->PrtOLENotify(false); + else if (pDoc->IsOLEPrtNotifyPending()) + pDoc->PrtOLENotify(true); + + assert(pDoc->IsInReading()); + assert(pDoc->IsInXMLImport()); + pDoc->SetInReading(false); + pDoc->SetInXMLImport(false); + } + + SwDrawModel* pDrawModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel(); + if (pDrawModel) + pDrawModel->setLock(false); + + // #i90243# + if ( m_bInititedXForms ) + { + Reference< xforms::XFormsSupplier > xFormsSupp( GetModel(), UNO_QUERY ); + Reference< XNameAccess > xXForms; + if ( xFormsSupp.is() ) + xXForms = xFormsSupp->getXForms().get(); + + if ( xXForms.is() ) + { + try + { + Sequence< beans::PropertyValue > aXFormsSettings; + + const OUString& sXFormsSettingsName( GetXMLToken( XML_XFORM_MODEL_SETTINGS ) ); + if ( m_xLateInitSettings.is() && m_xLateInitSettings->hasByName( sXFormsSettingsName ) ) + { + OSL_VERIFY( m_xLateInitSettings->getByName( sXFormsSettingsName ) >>= aXFormsSettings ); + applyXFormsSettings( xXForms, aXFormsSettings ); + } + } + catch( const Exception& ) + { + } + } + } + + for (SwNodeOffset i(0); i < pDoc->GetNodes().Count(); ++i) + { + if (SwTableNode *const pTableNode = pDoc->GetNodes()[i]->GetTableNode()) + { + if (!pTableNode->GetTable().IsNewModel() + && pTableNode->GetTable().CanConvertSubtables()) + { + pTableNode->GetTable().ConvertSubtables(); + } + } + // don't skip to the end; nested tables could have subtables too... + } + + // delegate to parent: takes care of error handling + SvXMLImport::endDocument(); + ClearTextImport(); +} + +// tdf#113877 +// when we insert one document with list inside into another one with list at the insert position, +// the resulting numbering in these lists is not consequent. +// +// CASE-1: Main document: +// 1. One +// 2. Two +// 3. Three +// 4. <-- insert position +// +// Inserted document: +// 1. One +// 2. Two +// 3. Three +// 4. +// +// Expected result +// 1. One +// 2. Two +// 3. Three +// 4. One +// 5. Two +// 6. Three +// 7. +// +// CASE-2: Main document: +// 1. One +// 2. Two +// 3. Three +// 4. <-- insert position +// +// Inserted document: +// A) One +// B) Two +// C) Three +// D) +// +// Expected result +// 1. One +// 2. Two +// 3. Three +// 4. One +// A) Two +// B) Three +// 5. +// +void SwXMLImport::MergeListsAtDocumentInsertPosition(SwDoc *pDoc) +{ + // 1. check environment + if (! pDoc) + return; + + if (! IsInsertMode() || ! m_oSttNdIdx->GetIndex()) + return; + + SwNodeOffset index(1); + + // the last node of the main document where we have inserted a document + SwNode* const node1 = pDoc->GetNodes()[m_oSttNdIdx->GetIndex() + 0]; + + // the first node of the inserted document + SwNode* node2 = pDoc->GetNodes()[m_oSttNdIdx->GetIndex() + index]; + + if (! (node1 && node2 + && (node1->GetNodeType() == node2->GetNodeType()) + && (node1->IsTextNode() == node2->IsTextNode()) + )) + { + // not a text node at insert position + return; + } + + // 2. get the first node of the inserted document, + // which will be used to detect if inside inserted document a new list was started after the first list + const SfxPoolItem* pListId2Initial = nullptr; + { + SwContentNode* contentNode1 = static_cast<SwContentNode *>(node1); + SwContentNode* contentNode2 = static_cast<SwContentNode *>(node2); + + // check if both lists have the same list properties + const SfxPoolItem* pListId1 = contentNode1->GetNoCondAttr( RES_PARATR_LIST_ID, false ); + const SfxPoolItem* pListId2 = contentNode2->GetNoCondAttr( RES_PARATR_LIST_ID, false ); + + if (! pListId1) + return; + if (! pListId2) + return; + + auto pStringListId1 = dynamic_cast<const SfxStringItem*>(pListId1); + assert(pStringListId1); + const OUString& sListId1 = pStringListId1->GetValue(); + auto pStringListId2 = dynamic_cast<const SfxStringItem*>(pListId2); + assert(pStringListId2); + const OUString& sListId2 = pStringListId2->GetValue(); + + const SwList* pList1 = pDoc->getIDocumentListsAccess().getListByName( sListId1 ); + const SwList* pList2 = pDoc->getIDocumentListsAccess().getListByName( sListId2 ); + + if (! pList1) + return; + if (! pList2) + return; + + const OUString& sDefaultListStyleName1 = pList1->GetDefaultListStyleName(); + const OUString& sDefaultListStyleName2 = pList2->GetDefaultListStyleName(); + + if (sDefaultListStyleName1 != sDefaultListStyleName2) + { + const SwNumRule* pNumRule1 = pDoc->FindNumRulePtr( sDefaultListStyleName1 ); + const SwNumRule* pNumRule2 = pDoc->FindNumRulePtr( sDefaultListStyleName2 ); + + if (pNumRule1 && pNumRule2) + { + // check style of the each list level + for( sal_uInt8 n = 0; n < MAXLEVEL; ++n ) + { + if( pNumRule1->Get( n ) != pNumRule2->Get( n ) ) + { + return; + } + } + + // our list should be merged + pListId2Initial = pListId2; + } + } + else + { + // our list should be merged + pListId2Initial = pListId2; + } + } + + if (! pListId2Initial) + { + // two lists have different styles => they should not be merged + return; + } + + // 3. merge two lists + while ( + node1 && node2 + && (node1->GetNodeType() == node2->GetNodeType()) + && (node1->IsTextNode() == node2->IsTextNode()) + ) + { + SwContentNode* contentNode1 = static_cast<SwContentNode *>( node1 ); + SwContentNode* contentNode2 = static_cast<SwContentNode *>( node2 ); + + const SfxPoolItem* pListId1 = contentNode1->GetNoCondAttr( RES_PARATR_LIST_ID, false ); + const SfxPoolItem* pListId2 = contentNode2->GetNoCondAttr( RES_PARATR_LIST_ID, false ); + + if (! pListId1) + return; + if (! pListId2) + return; + + if (*pListId2Initial != *pListId2) + { + // no more list items of the first list inside inserted document + return; + } + + // set list style to this list element + contentNode2->SetAttr(*pListId1); + + // get next item + index++; + if (index >= pDoc->GetNodes().Count()) + { + // no more items + return; + } + + node2 = pDoc->GetNodes()[m_oSttNdIdx->GetIndex() + index]; + } +} + +namespace { + +// Locally derive XMLTextShapeImportHelper, so we can take care of the +// form import This is Writer, but not text specific, so it should go +// here! +class SvTextShapeImportHelper : public XMLTextShapeImportHelper +{ + // hold own reference form import helper, because the SvxImport + // stored in the superclass, from whom we originally got the + // reference, is already destroyed when we want to use it in the + // destructor + rtl::Reference< ::xmloff::OFormLayerXMLImport > rFormImport; + + // hold reference to the one page (if it exists) for calling startPage() + // and endPage. If !xPage.is(), then this document doesn't have a + // XDrawPage. + Reference<drawing::XDrawPage> xPage; + +public: + explicit SvTextShapeImportHelper(SvXMLImport& rImp); + virtual ~SvTextShapeImportHelper() override; +}; + +} + +SvTextShapeImportHelper::SvTextShapeImportHelper(SvXMLImport& rImp) : + XMLTextShapeImportHelper(rImp) +{ + Reference<drawing::XDrawPageSupplier> xSupplier(rImp.GetModel(),UNO_QUERY); + if (xSupplier.is()) + { + if (rImp.GetFormImport().is()) + { + rImp.GetFormImport()->startPage(xSupplier->getDrawPage()); + rFormImport = rImp.GetFormImport(); + } + + xPage = xSupplier->getDrawPage(); + XMLShapeImportHelper::startPage( xPage ); + } +} + +SvTextShapeImportHelper::~SvTextShapeImportHelper() +{ + rFormImport->endPage(); + + if (xPage.is()) + { + XMLShapeImportHelper::endPage(xPage); + } +} + +XMLTextImportHelper* SwXMLImport::CreateTextImport() +{ + return new SwXMLTextImportHelper( GetModel(), *this, getImportInfo(), + IsInsertMode(), + IsStylesOnlyMode(), + IsBlockMode(), m_bOrganizerMode ); +} + +XMLShapeImportHelper* SwXMLImport::CreateShapeImport() +{ + return new SvTextShapeImportHelper( *this ); +} + +SvXMLImportContext *SwXMLImport::CreateFontDeclsContext() +{ + XMLFontStylesContext *pFSContext = + new XMLFontStylesContext( *this, osl_getThreadTextEncoding() ); + SetFontDecls( pFSContext ); + return pFSContext; +} + +void SwXMLImport::SetViewSettings(const Sequence < PropertyValue > & aViewProps) +{ + if (IsInsertMode() || IsStylesOnlyMode() || IsBlockMode() || m_bOrganizerMode || !GetModel().is() ) + return; + + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + SwDoc *pDoc = getDoc(); + tools::Rectangle aRect; + if( pDoc->GetDocShell() ) + aRect = pDoc->GetDocShell()->GetVisArea( ASPECT_CONTENT ); + //TODO/LATER: why that cast?! + //aRect = ((SfxInPlaceObject *)pDoc->GetDocShell())->GetVisArea(); + + sal_Int64 nTmp = 0; + bool bShowRedlineChanges = false, bBrowseMode = false; + bool bChangeShowRedline = false, bChangeBrowseMode = false; + + //TODO/LATER: why that cast?! + bool bTwip = pDoc->GetDocShell()->GetMapUnit ( ) == MapUnit::MapTwip; + //sal_Bool bTwip = pDoc->GetDocShell()->SfxInPlaceObject::GetMapUnit ( ) == MapUnit::MapTwip; + + for (const PropertyValue& rValue : aViewProps) + { + if ( rValue.Name == "ViewAreaTop" ) + { + rValue.Value >>= nTmp; + aRect.SetPosY(bTwip ? sanitiseMm100ToTwip(nTmp) : nTmp); + } + else if ( rValue.Name == "ViewAreaLeft" ) + { + rValue.Value >>= nTmp; + aRect.SetPosX(bTwip ? sanitiseMm100ToTwip(nTmp) : nTmp); + } + else if ( rValue.Name == "ViewAreaWidth" ) + { + rValue.Value >>= nTmp; + Size aSize( aRect.GetSize() ); + aSize.setWidth(bTwip ? sanitiseMm100ToTwip(nTmp) : nTmp); + aRect.SetSize( aSize ); + } + else if ( rValue.Name == "ViewAreaHeight" ) + { + rValue.Value >>= nTmp; + Size aSize( aRect.GetSize() ); + aSize.setHeight(bTwip ? sanitiseMm100ToTwip(nTmp) : nTmp); + aRect.SetSize( aSize ); + } + else if ( rValue.Name == "ShowRedlineChanges" ) + { + bShowRedlineChanges = *o3tl::doAccess<bool>(rValue.Value); + bChangeShowRedline = true; + } +// Headers and footers are not displayed in BrowseView anymore + else if ( rValue.Name == "InBrowseMode" ) + { + bBrowseMode = *o3tl::doAccess<bool>(rValue.Value); + bChangeBrowseMode = true; + } + } + if( pDoc->GetDocShell() ) + pDoc->GetDocShell()->SetVisArea ( aRect ); + + if (bChangeBrowseMode) + pDoc->getIDocumentSettingAccess().set(DocumentSettingId::BROWSE_MODE, bBrowseMode ); + + if (bChangeShowRedline) + GetTextImport()->SetShowChanges( bShowRedlineChanges ); +} + +// Note: this will be called only if there are OOo elements in settings.xml. +// So if a setting is missing there we can assume that it was written +// by an OOo/LO version that is older than the introduction of the setting! +void SwXMLImport::SetConfigurationSettings(const Sequence < PropertyValue > & aConfigProps) +{ + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + Reference< lang::XMultiServiceFactory > xFac( GetModel(), UNO_QUERY ); + if( !xFac.is() ) + return; + + Reference< XPropertySet > xProps( xFac->createInstance("com.sun.star.document.Settings"), UNO_QUERY ); + if( !xProps.is() ) + return; + + Reference< XPropertySetInfo > xInfo( xProps->getPropertySetInfo() ); + if( !xInfo.is() ) + return; + + std::unordered_set< OUString > aExcludeAlways; + aExcludeAlways.insert("LinkUpdateMode"); + // this should contain things that are actually user-settable, via Tools->Options + std::unordered_set< OUString > aExcludeWhenNotLoadingUserSettings { + "ForbiddenCharacters", + "IsKernAsianPunctuation", + "CharacterCompressionType", + "FieldAutoUpdate", + "ChartAutoUpdate", + "AddParaTableSpacing", + "AddParaTableSpacingAtStart", + "PrintAnnotationMode", + "PrintBlackFonts", + "PrintControls", + "PrintDrawings", + "PrintGraphics", + "PrintHiddenText", + "PrintLeftPages", + "PrintPageBackground", + "PrintProspect", + "PrintReversed", + "PrintRightPages", + "PrintFaxName", + "PrintPaperFromSetup", + "PrintTables", + "PrintTextPlaceholder", + "PrintSingleJobs", + "UpdateFromTemplate", + "PrinterIndependentLayout", + "PrintEmptyPages", + "ConsiderTextWrapOnObjPos", + "DoNotJustifyLinesWithManualBreak", + "ProtectForm", + "MsWordCompTrailingBlanks", + "SubtractFlysAnchoredAtFlys", + "EmptyDbFieldHidesPara", + "UseVariableWidthNBSP", + }; + + bool bAreUserSettingsFromDocument = officecfg::Office::Common::Load::UserDefinedSettings::get(); + + // for some properties we don't want to use the application + // default if they're missing. So we watch for them in the loop + // below, and set them if not found + bool bPrinterIndependentLayout = false; + bool bUseOldNumbering = false; + bool bAddExternalLeading = false; + bool bAddParaSpacingToTableCells = false; + bool bAddParaLineSpacingToTableCells = false; + bool bUseFormerLineSpacing = false; + bool bUseFormerObjectPositioning = false; + bool bUseFormerTextWrapping = false; + bool bConsiderWrapOnObjPos = false; + bool bIgnoreFirstLineIndentInNumbering = false; + bool bDoNotJustifyLinesWithManualBreak = false; + bool bDoNotResetParaAttrsForNumFont = false; + bool bDoNotCaptureDrawObjsOnPage( false ); + bool bClipAsCharacterAnchoredWriterFlyFrames( false ); + bool bUnixForceZeroExtLeading = false; + bool bSmallCapsPercentage66 = false; + bool bTabOverflow = false; + bool bTabOverMarginValue = false; + bool bPropLineSpacingShrinksFirstLine = false; + bool bSubtractFlysAnchoredAtFlys = false; + bool bEmptyDbFieldHidesPara = false; + bool bCollapseEmptyCellPara = false; + bool bAutoFirstLineIndentDisregardLineSpace = false; + bool bHyphenateURLs = false; + bool bDoNotBreakWrappedTables = false; + bool bAllowTextAfterFloatingTableBreak = false; + bool bDropCapPunctuation = false; + + const PropertyValue* currentDatabaseDataSource = nullptr; + const PropertyValue* currentDatabaseCommand = nullptr; + const PropertyValue* currentDatabaseCommandType = nullptr; + const PropertyValue* embeddedDatabaseName = nullptr; + + for( const PropertyValue& rValue : aConfigProps ) + { + bool bSet = aExcludeAlways.find(rValue.Name) == aExcludeAlways.end(); + if( bSet && !bAreUserSettingsFromDocument + && (aExcludeWhenNotLoadingUserSettings.find(rValue.Name) + != aExcludeWhenNotLoadingUserSettings.end()) ) + { + bSet = false; + } + + if( bSet ) + { + try + { + if( xInfo->hasPropertyByName( rValue.Name ) ) + { + if( rValue.Name == "RedlineProtectionKey" ) + { + Sequence<sal_Int8> aKey; + rValue.Value >>= aKey; + GetTextImport()->SetChangesProtectionKey( aKey ); + } + else + { + // HACK: Setting these out of order does not work. + if( rValue.Name == "CurrentDatabaseDataSource" ) + currentDatabaseDataSource = &rValue; + else if( rValue.Name == "CurrentDatabaseCommand" ) + currentDatabaseCommand = &rValue; + else if( rValue.Name == "CurrentDatabaseCommandType" ) + currentDatabaseCommandType = &rValue; + else if (rValue.Name == "EmbeddedDatabaseName") + embeddedDatabaseName = &rValue; + else + xProps->setPropertyValue( rValue.Name, rValue.Value ); + } + } + + // did we find any of the non-default cases? + if ( rValue.Name == "PrinterIndependentLayout" ) + bPrinterIndependentLayout = true; + else if ( rValue.Name == "AddExternalLeading" ) + bAddExternalLeading = true; + else if ( rValue.Name == "AddParaSpacingToTableCells" ) + bAddParaSpacingToTableCells = true; + else if ( rValue.Name == "AddParaLineSpacingToTableCells" ) + bAddParaLineSpacingToTableCells = true; + else if ( rValue.Name == "UseFormerLineSpacing" ) + bUseFormerLineSpacing = true; + else if ( rValue.Name == "UseFormerObjectPositioning" ) + bUseFormerObjectPositioning = true; + else if ( rValue.Name == "UseFormerTextWrapping" ) + bUseFormerTextWrapping = true; + else if ( rValue.Name == "UseOldNumbering" ) + bUseOldNumbering = true; + else if ( rValue.Name == "ConsiderTextWrapOnObjPos" ) + bConsiderWrapOnObjPos = true; + else if ( rValue.Name == "IgnoreFirstLineIndentInNumbering" ) + bIgnoreFirstLineIndentInNumbering = true; + else if ( rValue.Name == "DoNotJustifyLinesWithManualBreak" ) + bDoNotJustifyLinesWithManualBreak = true; + else if ( rValue.Name == "DoNotResetParaAttrsForNumFont" ) + bDoNotResetParaAttrsForNumFont = true; + else if ( rValue.Name == "DoNotCaptureDrawObjsOnPage" ) + bDoNotCaptureDrawObjsOnPage = true; + else if ( rValue.Name == "ClipAsCharacterAnchoredWriterFlyFrames" ) + bClipAsCharacterAnchoredWriterFlyFrames = true; + else if ( rValue.Name == "UnxForceZeroExtLeading" ) + bUnixForceZeroExtLeading = true; + else if ( rValue.Name == "SmallCapsPercentage66" ) + bSmallCapsPercentage66 = true; + else if ( rValue.Name == "TabOverflow" ) + bTabOverflow = true; + else if ( rValue.Name == "TabOverMargin" ) + { + rValue.Value >>= bTabOverMarginValue; + } + else if ( rValue.Name == "PropLineSpacingShrinksFirstLine" ) + bPropLineSpacingShrinksFirstLine = true; + else if (rValue.Name == "SubtractFlysAnchoredAtFlys") + bSubtractFlysAnchoredAtFlys = true; + else if (rValue.Name == "EmptyDbFieldHidesPara") + bEmptyDbFieldHidesPara = true; + else if (rValue.Name == "CollapseEmptyCellPara") + bCollapseEmptyCellPara = true; + else if (rValue.Name == "AutoFirstLineIndentDisregardLineSpace") + bAutoFirstLineIndentDisregardLineSpace = true; + else if (rValue.Name == "HyphenateURLs") + { + bHyphenateURLs = true; + } + else if (rValue.Name == "DoNotBreakWrappedTables") + { + rValue.Value >>= bDoNotBreakWrappedTables; + } + else if (rValue.Name == "AllowTextAfterFloatingTableBreak") + { + rValue.Value >>= bAllowTextAfterFloatingTableBreak; + } + else if ( rValue.Name == "DropCapPunctuation" ) + bDropCapPunctuation = true; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sw", "SwXMLImport::SetConfigurationSettings" ); + } + } + } + + try + { + if( currentDatabaseDataSource != nullptr ) + xProps->setPropertyValue( currentDatabaseDataSource->Name, currentDatabaseDataSource->Value ); + if( currentDatabaseCommand != nullptr ) + xProps->setPropertyValue( currentDatabaseCommand->Name, currentDatabaseCommand->Value ); + if( currentDatabaseCommandType != nullptr ) + xProps->setPropertyValue( currentDatabaseCommandType->Name, currentDatabaseCommandType->Value ); + if (embeddedDatabaseName) + xProps->setPropertyValue(embeddedDatabaseName->Name, embeddedDatabaseName->Value); + } catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sw", "SwXMLImport::SetConfigurationSettings" ); + } + + // finally, treat the non-default cases + // introduce boolean, that indicates a document, written by version prior SO8. + // If user settings are not loaded, we can't know if this is an old document. Better to assume no? + const bool bDocumentPriorSO8 = !bConsiderWrapOnObjPos && bAreUserSettingsFromDocument; + + // Use old behaviour if this setting didn't exist, but only if this setting is being read from the document. + // (Obviously the setting doesn't exist if we are explicitly ignoring it, so then stick with program/user defaults) + if(!bPrinterIndependentLayout && bAreUserSettingsFromDocument) + { + xProps->setPropertyValue( "PrinterIndependentLayout", Any(sal_Int16(document::PrinterIndependentLayout::DISABLED)) ); + } + + if( ! bAddExternalLeading ) + { + xProps->setPropertyValue( "AddExternalLeading", Any( false ) ); + } + + if( ! bUseFormerLineSpacing ) + { + xProps->setPropertyValue( "UseFormerLineSpacing", Any( true ) ); + } + + if( !bUseFormerObjectPositioning ) + { + xProps->setPropertyValue( "UseFormerObjectPositioning", Any( true ) ); + } + + if( !bUseOldNumbering ) + { + xProps->setPropertyValue( "UseOldNumbering", Any(true) ); + } + + if( !bAddParaSpacingToTableCells ) + { + xProps->setPropertyValue( "AddParaSpacingToTableCells", + Any( false ) ); + } + if (!bAddParaLineSpacingToTableCells) + { + xProps->setPropertyValue("AddParaLineSpacingToTableCells", Any(false)); + } + + if( !bUseFormerTextWrapping ) + { + xProps->setPropertyValue( "UseFormerTextWrapping", Any( true ) ); + } + + if (!bConsiderWrapOnObjPos && bAreUserSettingsFromDocument) + { + xProps->setPropertyValue( "ConsiderTextWrapOnObjPos", Any( false ) ); + } + + // #i47448# + // For SO7pp4, part of the 'new numbering' stuff has been backported from + // SO8. Unfortunately, only part of it and by using the same compatibility option + // like in SO8. Therefore documents generated with SO7pp4, containing + // numbered paragraphs with first line indent differ between SO7pp4 and + // SO8. In order to fix this for SO8pp1, I introduce a new compatibility + // flag 'bIgnoreFirstLineIndentInNumbering'. This flag has to be set for all + // documents < SO8, but not for SO8. So if the property is not present, the + // flag will be set to 'true'. SO8 documents surely have the + // 'ConsiderWrapOnObjPos' property set (no matter if 'true' or 'false'), + // therefore the correct condition to set this flag is this: + if( !bIgnoreFirstLineIndentInNumbering && bDocumentPriorSO8 ) + { + xProps->setPropertyValue( "IgnoreFirstLineIndentInNumbering", + Any( true ) ); + } + + // This flag has to be set for all documents < SO8 + if ( !bDoNotJustifyLinesWithManualBreak && bDocumentPriorSO8 ) + { + xProps->setPropertyValue( "DoNotJustifyLinesWithManualBreak", + Any( true ) ); + } + + // This flag has to be set for all documents < SO8 + if ( !bDoNotResetParaAttrsForNumFont && bDocumentPriorSO8 ) + { + xProps->setPropertyValue( "DoNotResetParaAttrsForNumFont", + Any( true ) ); + } + + // This flag has to be set for all documents < SO8 + if ( !bDoNotCaptureDrawObjsOnPage && bDocumentPriorSO8 ) + { + xProps->setPropertyValue( "DoNotCaptureDrawObjsOnPage", + Any( true ) ); + } + + // This flag has to be set for all documents < SO8 + if ( !bClipAsCharacterAnchoredWriterFlyFrames && bDocumentPriorSO8 ) + { + xProps->setPropertyValue( "ClipAsCharacterAnchoredWriterFlyFrames", + Any( true ) ); + } + + if ( !bUnixForceZeroExtLeading ) + { + xProps->setPropertyValue( "UnxForceZeroExtLeading", Any( true ) ); + } + + // Old LO versions had 66 as the value for small caps percentage, later changed to 80. + // In order to keep backwards compatibility, SmallCapsPercentage66 option is written to .odt + // files, and the default for new documents is 'false'. Files without this option + // are considered to be old files, so set the compatibility option too. + if ( !bSmallCapsPercentage66 ) + { + xProps->setPropertyValue( "SmallCapsPercentage66", Any( true ) ); + } + + if ( !bTabOverflow ) + { + xProps->setPropertyValue( "TabOverflow", Any( false ) ); + } + + if (bTabOverMarginValue) + // Let TabOverMargin imply the new default for + // PrinterIndependentLayout, knowing the first is set by Word import + // filters and Word defaults to our new default as well. + xProps->setPropertyValue( + "PrinterIndependentLayout", + uno::Any(document::PrinterIndependentLayout::HIGH_RESOLUTION)); + + if (!bPropLineSpacingShrinksFirstLine) + xProps->setPropertyValue("PropLineSpacingShrinksFirstLine", Any(false)); + + if (!bSubtractFlysAnchoredAtFlys && bAreUserSettingsFromDocument) + xProps->setPropertyValue("SubtractFlysAnchoredAtFlys", Any(true)); + + if (!bEmptyDbFieldHidesPara && bAreUserSettingsFromDocument) + xProps->setPropertyValue("EmptyDbFieldHidesPara", Any(false)); + + if (!bCollapseEmptyCellPara) + xProps->setPropertyValue("CollapseEmptyCellPara", Any(false)); + + if (!bAutoFirstLineIndentDisregardLineSpace) + xProps->setPropertyValue("AutoFirstLineIndentDisregardLineSpace", Any(false)); + + if (!bHyphenateURLs) + { + xProps->setPropertyValue("HyphenateURLs", Any(true)); + } + + if (bDoNotBreakWrappedTables) + { + xProps->setPropertyValue("DoNotBreakWrappedTables", Any(true)); + } + + if (bAllowTextAfterFloatingTableBreak) + { + xProps->setPropertyValue("AllowTextAfterFloatingTableBreak", Any(true)); + } + + // LO 7.4 and previous versions had different drop cap punctuation: very long dashes. + // In order to keep backwards compatibility, DropCapPunctuation option is written to .odt + // files, and the default for new documents is 'true'. Files without this option + // are considered to be old files, so set the compatibility option too. + if ( !bDropCapPunctuation ) + { + xProps->setPropertyValue( "DropCapPunctuation", Any( false ) ); + } + + SwDoc *pDoc = getDoc(); + SfxPrinter *pPrinter = pDoc->getIDocumentDeviceAccess().getPrinter( false ); + if( pPrinter ) + { + // If the printer is known, then the OLE objects will + // already have correct sizes, and we don't have to call + // PrtOLENotify again. Otherwise we have to call it. + // The flag might be set from setting the printer, so it + // it is required to clear it. + pDoc->SetOLEPrtNotifyPending( !pPrinter->IsKnown() ); + } +} + +void SwXMLImport::SetDocumentSpecificSettings( + const OUString& _rSettingsGroupName, + const Sequence< PropertyValue>& _rSettings ) +{ + // the only doc-specific settings group we know so far are the XForms settings + if ( !IsXMLToken( _rSettingsGroupName, XML_XFORM_MODEL_SETTINGS ) ) + return; + + // preserve the settings for a later iteration - we are currently reading the settings.xml, + // the content.xml will be read later, by another instance of SwXMLImport + OSL_ENSURE( m_xLateInitSettings.is(), "SwXMLImport::SetDocumentSpecificSettings: no storage for those settings!" ); + if ( !m_xLateInitSettings.is() ) + return; + + try + { + if ( m_xLateInitSettings->hasByName( _rSettingsGroupName ) ) + { + m_xLateInitSettings->replaceByName( _rSettingsGroupName, Any( _rSettings ) ); + OSL_FAIL( "SwXMLImport::SetDocumentSpecificSettings: already have settings for this model!" ); + } + else + m_xLateInitSettings->insertByName( _rSettingsGroupName, Any( _rSettings ) ); + } + catch( const Exception& ) + { + } +} + +void SwXMLImport::initialize( + const Sequence<Any>& aArguments ) +{ + // delegate to super class + SvXMLImport::initialize(aArguments); + + // we are only looking for a NamedValue "LateInitSettings" + for(const auto& rArgument : aArguments) + { + beans::NamedValue aNamedValue; + if ( rArgument >>= aNamedValue ) + { + if (aNamedValue.Name == "LateInitSettings") + { + OSL_VERIFY( aNamedValue.Value >>= m_xLateInitSettings ); + } + } + } +} + +void SwXMLImport::initXForms() +{ + // obtain SwDoc + auto pXTextDocument = comphelper::getFromUnoTunnel<SwXTextDocument>(GetModel()); + if( pXTextDocument == nullptr ) + return; + + SwDoc *pDoc = pXTextDocument->GetDocShell()->GetDoc(); + + // init XForms (if not already done) + // (no default model, since we'll load the models) + if( ! pDoc->isXForms() ) + pDoc->initXForms( false ); + + m_bInititedXForms = true; +} + +SwDoc* SwXMLImport::getDoc() +{ + if( m_pDoc != nullptr ) + return m_pDoc; + Reference < XTextDocument > xTextDoc( GetModel(), UNO_QUERY ); + Reference < XText > xText = xTextDoc->getText(); + SwXText* pText = dynamic_cast<SwXText*>(xText.get()); + assert( pText != nullptr ); + m_pDoc = pText->GetDoc(); + assert( m_pDoc != nullptr ); + return m_pDoc; +} + +const SwDoc* SwXMLImport::getDoc() const +{ + return const_cast< SwXMLImport* >( this )->getDoc(); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisImporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisImporter", + SvXMLImportFlags::ALL)); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisStylesImporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisStylesImporter", + SvXMLImportFlags::STYLES | SvXMLImportFlags::MASTERSTYLES | SvXMLImportFlags::AUTOSTYLES | + SvXMLImportFlags::FONTDECLS)); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisContentImporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisContentImporter", + SvXMLImportFlags::CONTENT | SvXMLImportFlags::SCRIPTS | SvXMLImportFlags::AUTOSTYLES | + SvXMLImportFlags::FONTDECLS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisMetaImporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisMetaImporter", + SvXMLImportFlags::META)); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisSettingsImporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisSettingsImporter", + SvXMLImportFlags::SETTINGS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportFODT(SvStream &rStream) +{ + SwGlobals::ensure(); + + SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL)); + xDocSh->DoInitNew(); + uno::Reference<frame::XModel> xModel(xDocSh->GetModel()); + + uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory()); + uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream)); + uno::Reference<uno::XInterface> xInterface(xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.XmlFilterAdaptor"), uno::UNO_SET_THROW); + + css::uno::Sequence<OUString> aUserData + { + "com.sun.star.comp.filter.OdfFlatXml", + "", + "com.sun.star.comp.Writer.XMLOasisImporter", + "com.sun.star.comp.Writer.XMLOasisExporter", + "", + "", + "true" + }; + uno::Sequence<beans::PropertyValue> aAdaptorArgs(comphelper::InitPropertySequence( + { + { "UserData", uno::Any(aUserData) }, + })); + css::uno::Sequence<uno::Any> aOuterArgs{ uno::Any(aAdaptorArgs) }; + + uno::Reference<lang::XInitialization> xInit(xInterface, uno::UNO_QUERY_THROW); + xInit->initialize(aOuterArgs); + + uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW); + uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence( + { + { "InputStream", uno::Any(xStream) }, + { "URL", uno::Any(OUString("private:stream")) }, + })); + xImporter->setTargetDocument(xModel); + + uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW); + //SetLoading hack because the document properties will be re-initted + //by the xml filter and during the init, while it's considered uninitialized, + //setting a property will inform the document it's modified, which attempts + //to update the properties, which throws cause the properties are uninitialized + xDocSh->SetLoading(SfxLoadedFlags::NONE); + bool ret = xFilter->filter(aArgs); + xDocSh->SetLoading(SfxLoadedFlags::ALL); + + xDocSh->DoClose(); + + return ret; +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestPDFExportFODT(SvStream &rStream) +{ + // do the same sort of check as FilterDetect::detect + OString const str(read_uInt8s_ToOString(rStream, 4000)); + rStream.Seek(STREAM_SEEK_TO_BEGIN); + OUString resultString(str.getStr(), str.getLength(), RTL_TEXTENCODING_ASCII_US, + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT|RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT|RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT); + if (!resultString.startsWith("<?xml") || resultString.indexOf("office:mimetype=\"application/vnd.oasis.opendocument.text\"") == -1) + return false; + + Reference<css::frame::XDesktop2> xDesktop = css::frame::Desktop::create(comphelper::getProcessComponentContext()); + Reference<css::frame::XFrame> xTargetFrame = xDesktop->findFrame("_blank", 0); + + Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext()); + Reference<css::frame::XModel2> xModel(xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.text.TextDocument", xContext), UNO_QUERY_THROW); + + Reference<css::frame::XLoadable> xModelLoad(xModel, UNO_QUERY_THROW); + xModelLoad->initNew(); + + uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory()); + uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream)); + uno::Reference<uno::XInterface> xInterface(xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.XmlFilterAdaptor"), uno::UNO_SET_THROW); + + css::uno::Sequence<OUString> aUserData + { + "com.sun.star.comp.filter.OdfFlatXml", + "", + "com.sun.star.comp.Writer.XMLOasisImporter", + "com.sun.star.comp.Writer.XMLOasisExporter", + "", + "", + "true" + }; + uno::Sequence<beans::PropertyValue> aAdaptorArgs(comphelper::InitPropertySequence( + { + { "UserData", uno::Any(aUserData) }, + })); + css::uno::Sequence<uno::Any> aOuterArgs{ uno::Any(aAdaptorArgs) }; + + uno::Reference<lang::XInitialization> xInit(xInterface, uno::UNO_QUERY_THROW); + xInit->initialize(aOuterArgs); + + uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW); + uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence( + { + { "InputStream", uno::Any(xStream) }, + { "URL", uno::Any(OUString("private:stream")) }, + })); + xImporter->setTargetDocument(xModel); + + uno::Reference<document::XFilter> xFODTFilter(xInterface, uno::UNO_QUERY_THROW); + bool ret = xFODTFilter->filter(aArgs); + + if (ret) + { + css::uno::Reference<css::frame::XController2> xController(xModel->createDefaultViewController(xTargetFrame), UNO_SET_THROW); + utl::ConnectFrameControllerModel(xTargetFrame, xController, xModel); + + utl::TempFileFast aTempFile; + + uno::Reference<document::XFilter> xPDFFilter( + xMultiServiceFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY); + uno::Reference<document::XExporter> xExporter(xPDFFilter, uno::UNO_QUERY); + xExporter->setSourceDocument(xModel); + + uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(*aTempFile.GetStream(StreamMode::READWRITE))); + + // ofz#60533 fuzzer learned to use fo:font-size="842pt" which generate timeouts trying + // to export thousands of pages from minimal input size + uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence({ + { "PageRange", uno::Any(OUString("1-100")) } + })); + uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence({ + { "FilterName", uno::Any(OUString("writer_pdf_Export")) }, + { "OutputStream", uno::Any(xOutputStream) }, + { "FilterData", uno::Any(aFilterData) } + })); + xPDFFilter->filter(aDescriptor); + } + + css::uno::Reference<css::util::XCloseable> xClose(xModel, css::uno::UNO_QUERY); + xClose->close(false); + + return ret; +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportDOCX(SvStream &rStream) +{ + SwGlobals::ensure(); + + SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL)); + xDocSh->DoInitNew(); + uno::Reference<frame::XModel> xModel(xDocSh->GetModel()); + + uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory()); + uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream)); + + uno::Reference<document::XFilter> xFilter(xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.WriterFilter"), uno::UNO_QUERY_THROW); + + uno::Reference<document::XImporter> xImporter(xFilter, uno::UNO_QUERY_THROW); + uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence( + { + { "InputStream", uno::Any(xStream) }, + { "InputMode", uno::Any(true) }, + })); + xImporter->setTargetDocument(xModel); + + //SetLoading hack because the document properties will be re-initted + //by the xml filter and during the init, while it's considered uninitialized, + //setting a property will inform the document it's modified, which attempts + //to update the properties, which throws cause the properties are uninitialized + xDocSh->SetLoading(SfxLoadedFlags::NONE); + bool ret = false; + try + { + ret = xFilter->filter(aArgs); + } + catch (...) + { + } + xDocSh->SetLoading(SfxLoadedFlags::ALL); + + xDocSh->DoClose(); + + return ret; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlimp.hxx b/sw/source/filter/xml/xmlimp.hxx new file mode 100644 index 0000000000..4c528155f2 --- /dev/null +++ b/sw/source/filter/xml/xmlimp.hxx @@ -0,0 +1,187 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLIMP_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLIMP_HXX + +#include <sal/config.h> + +#include <memory> +#include <optional> + +#include <com/sun/star/document/XDocumentProperties.hpp> + +#include <xmloff/xmlictxt.hxx> +#include <xmloff/xmlimp.hxx> + +#include "xmlitmap.hxx" +#include <o3tl/typed_flags_set.hxx> +#include <ndindex.hxx> + +class SwDoc; +class SvXMLUnitConverter; +class SvXMLTokenMap; +class SvXMLImportItemMapper; +class SfxItemSet; +class XMLTextImportHelper; +class SvXMLGraphicHelper; +class SvXMLEmbeddedObjectHelper; +enum class SfxStyleFamily; + +// define, how many steps ( = paragraphs ) the progress bar should advance +// for styles, autostyles and settings + meta +#define PROGRESS_BAR_STEP 20 + +// we only need this scoped enum to be flags here, in sw +namespace o3tl +{ + template<> struct typed_flags<SfxStyleFamily> : is_typed_flags<SfxStyleFamily, 0xffff> {}; +} + +class SwXMLImport: public SvXMLImport +{ + std::optional<SwNodeIndex> m_oSttNdIdx; + + std::unique_ptr<SvXMLUnitConverter> m_pTwipUnitConv; + std::unique_ptr<SvXMLImportItemMapper> m_pTableItemMapper;// paragraph item import + + rtl::Reference<SvXMLGraphicHelper> m_xGraphicStorageHandler; + + rtl::Reference<SvXMLEmbeddedObjectHelper> m_xEmbeddedResolver; + + SvXMLItemMapEntriesRef m_xTableItemMap; + SvXMLItemMapEntriesRef m_xTableColItemMap; + SvXMLItemMapEntriesRef m_xTableRowItemMap; + SvXMLItemMapEntriesRef m_xTableCellItemMap; + css::uno::Reference< css::container::XNameContainer > + m_xLateInitSettings; + + SfxStyleFamily m_nStyleFamilyMask;// Mask of styles to load + bool m_bLoadDoc : 1; // Load doc or styles only + bool m_bInsert : 1; // Insert mode. If styles are + // loaded only false means that + // existing styles will be + // overwritten. + bool m_bBlock : 1; // Load text block + bool m_bOrganizerMode : 1; + bool m_bInititedXForms : 1; + + SwDoc* m_pDoc; // cached for getDoc() + + // Optimization for new table name lookup + OUString m_sDefTableName; // See STR_TABLE_DEFNAME + std::map<OUString, sal_uInt32> m_aTableNameMap; // Last used indices for duplicating table names + + void InitItemImport(); + void FinitItemImport(); + void UpdateTextCollConditions( SwDoc *pDoc ); + + void setTextInsertMode( + const css::uno::Reference< css::text::XTextRange > & rInsertPos ); + void setStyleInsertMode( SfxStyleFamily nFamilies, + bool bOverwrite ); + +protected: + + virtual SvXMLImportContext *CreateFastContext( sal_Int32 nElement, + const ::css::uno::Reference< ::css::xml::sax::XFastAttributeList >& xAttrList ) override; + + virtual XMLTextImportHelper* CreateTextImport() override; + + virtual XMLShapeImportHelper* CreateShapeImport() override; + +public: + SwXMLImport( + const css::uno::Reference< css::uno::XComponentContext >& rContext, + OUString const & implementationName, SvXMLImportFlags nImportFlags); + + virtual ~SwXMLImport() noexcept override; + + // css::xml::sax::XDocumentHandler + virtual void SAL_CALL startDocument() override; + virtual void SAL_CALL endDocument() override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + void InsertStyles( bool bAuto ); + void FinishStyles(); + + // namespace office + + // NB: in contrast to other CreateFooContexts, this particular one handles + // the root element (i.e. office:document-meta) + SvXMLImportContext *CreateMetaContext( const sal_Int32 nElement ); + SvXMLImportContext *CreateScriptContext(); + SvXMLImportContext *CreateStylesContext( bool bAuto ); + SvXMLImportContext *CreateMasterStylesContext(); + SvXMLImportContext *CreateFontDeclsContext(); + SvXMLImportContext *CreateBodyContentContext(); + SfxStyleFamily GetStyleFamilyMask() const { return m_nStyleFamilyMask; } + bool IsInsertMode() const { return m_bInsert; } + bool IsStylesOnlyMode() const { return !m_bLoadDoc; } + bool IsBlockMode() const { return m_bBlock; } + + inline const SvXMLImportItemMapper& GetTableItemMapper() const; + inline SvXMLImportItemMapper& GetTableItemMapper(); + SvXMLImportContext *CreateTableItemImportContext( sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList, + XmlStyleFamily nSubFamily, SfxItemSet& rItemSet ); + + bool FindAutomaticStyle( XmlStyleFamily nFamily, + const OUString& rName, + const SfxItemSet **ppItemSet ) const; + void MergeListsAtDocumentInsertPosition(SwDoc *pDoc); + + virtual void SetStatistics( + const css::uno::Sequence< css::beans::NamedValue> & i_rStats) override; + virtual void SetViewSettings(const css::uno::Sequence<css::beans::PropertyValue>& aViewProps) override; + virtual void SetConfigurationSettings(const css::uno::Sequence<css::beans::PropertyValue>& aConfigProps) override; + virtual void SetDocumentSpecificSettings(const OUString& _rSettingsGroupName, + const css::uno::Sequence<css::beans::PropertyValue>& _rSettings) override; + + // initialize XForms + virtual void initXForms() override; + + // get the document properties, but only if they actually need importing + css::uno::Reference<css::document::XDocumentProperties> + GetDocumentProperties() const; + + virtual void NotifyContainsEmbeddedFont() override; + + const SwDoc* getDoc() const; + SwDoc* getDoc(); + + const OUString& GetDefTableName() { return m_sDefTableName; } + std::map<OUString, sal_uInt32>& GetTableNameMap() { return m_aTableNameMap; } +}; + +inline const SvXMLImportItemMapper& SwXMLImport::GetTableItemMapper() const +{ + return *m_pTableItemMapper; +} + +inline SvXMLImportItemMapper& SwXMLImport::GetTableItemMapper() +{ + return *m_pTableItemMapper; +} + +#endif // _XMLIMP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlimpit.cxx b/sw/source/filter/xml/xmlimpit.cxx new file mode 100644 index 0000000000..eaabf789e2 --- /dev/null +++ b/sw/source/filter/xml/xmlimpit.cxx @@ -0,0 +1,1056 @@ +/* -*- 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 "xmlimpit.hxx" + +#include <sal/log.hxx> +#include <sax/tools/converter.hxx> +#include <utility> +#include <xmloff/xmluconv.hxx> +#include <svl/itempool.hxx> +#include <svl/poolitem.hxx> +#include <svl/itemset.hxx> +#include <xmloff/namespacemap.hxx> +#include <editeng/xmlcnitm.hxx> +#include <editeng/memberids.h> +#include <osl/diagnose.h> + +#include <hintids.hxx> +#include <unomid.h> +#include <svx/unomid.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/frmdir.hxx> +#include <fmtpdsc.hxx> +#include <fmtornt.hxx> +#include <fmtfsize.hxx> + +#include <xmloff/prhdlfac.hxx> +#include <xmloff/xmltypes.hxx> +#include <xmloff/xmlprhdl.hxx> +#include <xmloff/xmlimp.hxx> +#include <xmloff/xmlnamespace.hxx> +#include "xmlithlp.hxx" +#include <com/sun/star/uno/Any.hxx> + +using ::editeng::SvxBorderLine; +using namespace ::com::sun::star; +using namespace ::xmloff::token; +using uno::Any; + +constexpr sal_uInt16 nUnknownWhich = RES_UNKNOWNATR_CONTAINER; + +SvXMLImportItemMapper::SvXMLImportItemMapper( + SvXMLItemMapEntriesRef aMapEntries ) : + mrMapEntries(std::move( aMapEntries )) +{ +} + +SvXMLImportItemMapper::~SvXMLImportItemMapper() +{ +} + +void +SvXMLImportItemMapper::setMapEntries( SvXMLItemMapEntriesRef rMapEntries ) +{ + mrMapEntries = std::move(rMapEntries); +} + +// fills the given itemset with the attributes in the given list +void SvXMLImportItemMapper::importXML( SfxItemSet& rSet, + uno::Reference< xml::sax::XFastAttributeList > const & xAttrList, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap ) +{ + std::unique_ptr<SvXMLAttrContainerItem> pUnknownItem; + for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList )) + { + if( IsTokenInNamespace(aIter.getToken(), XML_NAMESPACE_XMLNS) ) + continue; + + sal_Int32 nToken = aIter.getToken(); + const OUString sValue = aIter.toString(); + + // find a map entry for this attribute + sal_Int32 nLookupToken = nToken; + // compatibility namespaces need to be transformed into current namespace before looking up + if (IsTokenInNamespace(nLookupToken, XML_NAMESPACE_FO_COMPAT)) + nLookupToken = XML_ELEMENT(FO, (nLookupToken & TOKEN_MASK)); + SvXMLItemMapEntry const * pEntry = mrMapEntries->getByName( nLookupToken ); + + if( pEntry ) + { + // we have a valid map entry here, so lets use it... + if( 0 == (pEntry->nMemberId & (MID_SW_FLAG_NO_ITEM_IMPORT| + MID_SW_FLAG_ELEMENT_ITEM_IMPORT)) ) + { + // first get item from itemset + const SfxPoolItem* pItem = nullptr; + SfxItemState eState = rSet.GetItemState( pEntry->nWhichId, true, + &pItem ); + + // if it's not set, try the pool + if (SfxItemState::SET != eState && SfxItemPool::IsWhich(pEntry->nWhichId)) + pItem = &rSet.GetPool()->GetDefaultItem(pEntry->nWhichId); + + // do we have an item? + if(eState >= SfxItemState::DEFAULT && pItem) + { + std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone()); + bool bPut = false; + + if( 0 == (pEntry->nMemberId&MID_SW_FLAG_SPECIAL_ITEM_IMPORT) ) + { + bPut = PutXMLValue( *pNewItem, sValue, + o3tl::narrowing<sal_uInt16>( pEntry->nMemberId & MID_SW_FLAG_MASK ), + rUnitConverter ); + + } + else + { + bPut = handleSpecialItem( *pEntry, *pNewItem, rSet, + sValue, rUnitConverter ); + } + + if( bPut ) + rSet.Put( std::move(pNewItem) ); + } + else + { + OSL_FAIL( "Could not get a needed item for xml import!" ); + } + } + else if( 0 != (pEntry->nMemberId & MID_SW_FLAG_NO_ITEM_IMPORT) ) + { + handleNoItem( *pEntry, rSet, sValue, rUnitConverter, + rNamespaceMap ); + } + } + else + { + if( !pUnknownItem ) + { + const SfxPoolItem* pItem = nullptr; + if( SfxItemState::SET == rSet.GetItemState( nUnknownWhich, true, + &pItem ) ) + { + pUnknownItem.reset( static_cast<SvXMLAttrContainerItem*>( pItem->Clone() ) ); + } + else + { + pUnknownItem.reset( new SvXMLAttrContainerItem( nUnknownWhich ) ); + } + } + if( pUnknownItem ) + { + if( IsTokenInNamespace(nToken, XML_NAMESPACE_NONE) ) + pUnknownItem->AddAttr( SvXMLImport::getNameFromToken( nToken ), sValue ); + else + { + const OUString& rAttrNamespacePrefix = SvXMLImport::getNamespacePrefixFromToken(nToken, &rNamespaceMap); + OUString sAttrName = SvXMLImport::getNameFromToken( nToken ); + if ( !rAttrNamespacePrefix.isEmpty() ) + sAttrName = rAttrNamespacePrefix + SvXMLImport::aNamespaceSeparator + sAttrName; + OUString aLocalName, aPrefix, aNamespace; + rNamespaceMap.GetKeyByAttrName( sAttrName, &aPrefix, &aLocalName, + &aNamespace ); + if ( !rAttrNamespacePrefix.isEmpty() ) + pUnknownItem->AddAttr( rAttrNamespacePrefix, aNamespace, aLocalName, + sValue ); + else + pUnknownItem->AddAttr( aLocalName, sValue ); + } + } + } + } + + importXMLUnknownAttributes(rSet, xAttrList, rUnitConverter, pUnknownItem); + + if( pUnknownItem ) + { + rSet.Put( *pUnknownItem ); + } + + finished(rSet, rUnitConverter); +} + +void SvXMLImportItemMapper::importXMLUnknownAttributes( SfxItemSet& rSet, + uno::Reference< xml::sax::XFastAttributeList > const & xAttrList, + const SvXMLUnitConverter& rUnitConverter, + std::unique_ptr<SvXMLAttrContainerItem>& pUnknownItem) +{ + const css::uno::Sequence< css::xml::Attribute > unknownAttributes = xAttrList->getUnknownAttributes(); + for (const auto & rAttribute : unknownAttributes) + { + if( !pUnknownItem ) + { + const SfxPoolItem* pItem = nullptr; + if( SfxItemState::SET == rSet.GetItemState( nUnknownWhich, true, + &pItem ) ) + { + pUnknownItem.reset( static_cast<SvXMLAttrContainerItem*>( pItem->Clone() ) ); + } + else + { + pUnknownItem.reset( new SvXMLAttrContainerItem( nUnknownWhich ) ); + } + } + if( pUnknownItem ) + { + if( rAttribute.NamespaceURL.isEmpty() ) + pUnknownItem->AddAttr( rAttribute.Name, rAttribute.Value ); + else + { + OUString sPrefix; + OUString sName = rAttribute.Name; + int i = sName.indexOf(':'); + if (i != -1) + { + sPrefix = sName.copy(0, i-1); + sName = sName.copy(i+1); + } + // the sax parser doesn't reject these, strangely + if (sName.indexOf(':') == -1) + pUnknownItem->AddAttr( sPrefix, rAttribute.NamespaceURL, sName, + rAttribute.Value ); + else + SAL_WARN("sw", "ignoring dodgy attribute: " + rAttribute.Name); + } + } + } + + if( pUnknownItem ) + { + rSet.Put( *pUnknownItem ); + } + + finished(rSet, rUnitConverter); +} + +/** this method is called for every item that has the + MID_SW_FLAG_SPECIAL_ITEM_IMPORT flag set */ +bool +SvXMLImportItemMapper::handleSpecialItem( const SvXMLItemMapEntry& /*rEntry*/, + SfxPoolItem& /*rItem*/, + SfxItemSet& /*rSet*/, + const OUString& /*rValue*/, + const SvXMLUnitConverter& /*rUnitConverter*/ ) +{ + OSL_FAIL( "unsupported special item in xml import" ); + return false; +} + +/** this method is called for every item that has the + MID_SW_FLAG_NO_ITEM_IMPORT flag set */ +bool SvXMLImportItemMapper::handleNoItem( const SvXMLItemMapEntry& /*rEntry*/, + SfxItemSet& /*rSet*/, + const OUString& /*rValue*/, + const SvXMLUnitConverter& /*rUnitConverter*/, + const SvXMLNamespaceMap& /*rNamespaceMap*/ ) +{ + OSL_FAIL( "unsupported no item in xml import" ); + return false; +} + +void +SvXMLImportItemMapper::finished(SfxItemSet &, SvXMLUnitConverter const&) const +{ + // nothing to do here +} + +namespace { + +struct BoxHolder +{ + std::unique_ptr<SvxBorderLine> pTop; + std::unique_ptr<SvxBorderLine> pBottom; + std::unique_ptr<SvxBorderLine> pLeft; + std::unique_ptr<SvxBorderLine> pRight; + + BoxHolder(BoxHolder const&) = delete; + BoxHolder& operator=(BoxHolder const&) = delete; + + explicit BoxHolder(SvxBoxItem const & rBox) + { + if (rBox.GetTop()) + pTop.reset(new SvxBorderLine( *rBox.GetTop() )); + if (rBox.GetBottom()) + pBottom.reset(new SvxBorderLine( *rBox.GetBottom() )); + if (rBox.GetLeft()) + pLeft.reset(new SvxBorderLine( *rBox.GetLeft() )); + if (rBox.GetRight()) + pRight.reset(new SvxBorderLine( *rBox.GetRight() )); + } +}; + +} + +// put an XML-string value into an item +bool SvXMLImportItemMapper::PutXMLValue( + SfxPoolItem& rItem, + const OUString& rValue, + sal_uInt16 nMemberId, + const SvXMLUnitConverter& rUnitConverter ) +{ + bool bOk = false; + + switch (rItem.Which()) + { + case RES_MARGIN_FIRSTLINE: + case RES_MARGIN_TEXTLEFT: + case RES_MARGIN_RIGHT: + assert(false); // is only called for frame formats? + break; + + case RES_LR_SPACE: + { + SvxLRSpaceItem& rLRSpace = dynamic_cast<SvxLRSpaceItem&>(rItem); + + switch( nMemberId ) + { + case MID_L_MARGIN: + case MID_R_MARGIN: + { + sal_Int32 nProp = 100; + sal_Int32 nAbs = 0; + + if( rValue.indexOf( '%' ) != -1 ) + bOk = ::sax::Converter::convertPercent(nProp, rValue); + else + bOk = rUnitConverter.convertMeasureToCore(nAbs, rValue); + + if( bOk ) + { + switch( nMemberId ) + { + case MID_L_MARGIN: + rLRSpace.SetTextLeft( nAbs, o3tl::narrowing<sal_uInt16>(nProp) ); + break; + case MID_R_MARGIN: + rLRSpace.SetRight( nAbs, o3tl::narrowing<sal_uInt16>(nProp) ); + break; + } + } + } + break; + + case MID_FIRST_LINE_INDENT: + { + assert(false); // it looks like this can't be called? (frame formats only, aTableItemMap) + sal_Int32 nProp = 100; + sal_Int32 nAbs = 0; + + if( rValue.indexOf( '%' ) != -1 ) + bOk = ::sax::Converter::convertPercent(nProp, rValue); + else + bOk = rUnitConverter.convertMeasureToCore(nAbs, rValue, + -0x7fff, 0x7fff ); + + rLRSpace.SetTextFirstLineOffset( static_cast<short>(nAbs), o3tl::narrowing<sal_uInt16>(nProp) ); + } + break; + + case MID_FIRST_AUTO: + { + assert(false); // it looks like this can't be called? (frame formats only, aTableItemMap) + bool bAutoFirst(false); + bOk = ::sax::Converter::convertBool( bAutoFirst, rValue ); + if( bOk ) + rLRSpace.SetAutoFirst( bAutoFirst ); + } + break; + + default: + OSL_FAIL( "unknown member id!"); + } + } + break; + + case RES_UL_SPACE: + { + SvxULSpaceItem& rULSpace = dynamic_cast<SvxULSpaceItem&>(rItem); + + sal_Int32 nProp = 100; + sal_Int32 nAbs = 0; + + if( rValue.indexOf( '%' ) != -1 ) + bOk = ::sax::Converter::convertPercent( nProp, rValue ); + else + bOk = rUnitConverter.convertMeasureToCore( nAbs, rValue ); + + switch( nMemberId ) + { + case MID_UP_MARGIN: + rULSpace.SetUpper( o3tl::narrowing<sal_uInt16>(nAbs), o3tl::narrowing<sal_uInt16>(nProp) ); + break; + case MID_LO_MARGIN: + rULSpace.SetLower( o3tl::narrowing<sal_uInt16>(nAbs), o3tl::narrowing<sal_uInt16>(nProp) ); + break; + default: + OSL_FAIL("unknown MemberId"); + } + } + break; + + case RES_SHADOW: + { + SvxShadowItem& rShadow = dynamic_cast<SvxShadowItem&>(rItem); + + bool bColorFound = false; + bool bOffsetFound = false; + + SvXMLTokenEnumerator aTokenEnum( rValue ); + + Color aColor( 128,128, 128 ); + rShadow.SetLocation( SvxShadowLocation::BottomRight ); + + std::u16string_view aToken; + while( aTokenEnum.getNextToken( aToken ) ) + { + if( IsXMLToken( aToken, XML_NONE ) ) + { + rShadow.SetLocation( SvxShadowLocation::NONE ); + bOk = true; + } + else if( !bColorFound && aToken.substr(0,1) == u"#" ) + { + bOk = ::sax::Converter::convertColor( aColor, aToken ); + if( !bOk ) + return false; + + bColorFound = true; + } + else if( !bOffsetFound ) + { + sal_Int32 nX = 0, nY = 0; + + bOk = rUnitConverter.convertMeasureToCore( nX, aToken ); + if( bOk && aTokenEnum.getNextToken( aToken ) ) + bOk = rUnitConverter.convertMeasureToCore( nY, aToken ); + + if( bOk ) + { + if( nX < 0 ) + { + if( nY < 0 ) + { + rShadow.SetLocation( SvxShadowLocation::TopLeft ); + } + else + { + rShadow.SetLocation( SvxShadowLocation::BottomLeft ); + } + } + else + { + if( nY < 0 ) + { + rShadow.SetLocation( SvxShadowLocation::TopRight ); + } + else + { + rShadow.SetLocation( SvxShadowLocation::BottomRight ); + } + } + + if( nX < 0 ) nX *= -1; + if( nY < 0 ) nY *= -1; + + rShadow.SetWidth( static_cast< sal_uInt16 >( (nX + nY) >> 1 ) ); + } + } + } + + if( bOk && ( bColorFound || bOffsetFound ) ) + { + rShadow.SetColor(aColor); + } + else + bOk = false; + } + break; + + case RES_BOX: + { + SvxBoxItem& rBox = dynamic_cast<SvxBoxItem&>(rItem); + + // copy SvxBorderLines + BoxHolder aBoxes(rBox); + + sal_Int32 nTemp; + + switch( nMemberId ) + { + case ALL_BORDER_PADDING: + case LEFT_BORDER_PADDING: + case RIGHT_BORDER_PADDING: + case TOP_BORDER_PADDING: + case BOTTOM_BORDER_PADDING: + if (!rUnitConverter.convertMeasureToCore( nTemp, rValue, + 0, 0xffff )) + { + return false; + } + + if( nMemberId == LEFT_BORDER_PADDING || + nMemberId == ALL_BORDER_PADDING ) + rBox.SetDistance( o3tl::narrowing<sal_uInt16>(nTemp), SvxBoxItemLine::LEFT ); + if( nMemberId == RIGHT_BORDER_PADDING || + nMemberId == ALL_BORDER_PADDING ) + rBox.SetDistance( o3tl::narrowing<sal_uInt16>(nTemp), SvxBoxItemLine::RIGHT ); + if( nMemberId == TOP_BORDER_PADDING || + nMemberId == ALL_BORDER_PADDING ) + rBox.SetDistance( o3tl::narrowing<sal_uInt16>(nTemp), SvxBoxItemLine::TOP ); + if( nMemberId == BOTTOM_BORDER_PADDING || + nMemberId == ALL_BORDER_PADDING ) + rBox.SetDistance( o3tl::narrowing<sal_uInt16>(nTemp), SvxBoxItemLine::BOTTOM); + break; + + case ALL_BORDER: + case LEFT_BORDER: + case RIGHT_BORDER: + case TOP_BORDER: + case BOTTOM_BORDER: + { + bool bHasStyle = false; + bool bHasWidth = false; + bool bHasColor = false; + + sal_uInt16 nStyle = USHRT_MAX; + sal_uInt16 nWidth = 0; + sal_uInt16 nNamedWidth = USHRT_MAX; + + Color aColor( COL_BLACK ); + + if( !sw_frmitems_parseXMLBorder( rValue, rUnitConverter, + bHasStyle, nStyle, + bHasWidth, nWidth, nNamedWidth, + bHasColor, aColor ) ) + return false; + + if( TOP_BORDER == nMemberId || ALL_BORDER == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pTop, + bHasStyle, nStyle, + bHasWidth, nWidth, nNamedWidth, + bHasColor, aColor ); + + if( BOTTOM_BORDER == nMemberId || ALL_BORDER == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pBottom, + bHasStyle, nStyle, + bHasWidth, nWidth, nNamedWidth, + bHasColor, aColor ); + + if( LEFT_BORDER == nMemberId || ALL_BORDER == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pLeft, + bHasStyle, nStyle, + bHasWidth, nWidth, nNamedWidth, + bHasColor, aColor ); + + if( RIGHT_BORDER == nMemberId || ALL_BORDER == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pRight, + bHasStyle, nStyle, + bHasWidth, nWidth, nNamedWidth, + bHasColor, aColor ); + } + break; + case ALL_BORDER_LINE_WIDTH: + case LEFT_BORDER_LINE_WIDTH: + case RIGHT_BORDER_LINE_WIDTH: + case TOP_BORDER_LINE_WIDTH: + case BOTTOM_BORDER_LINE_WIDTH: + { + SvXMLTokenEnumerator aTokenEnum( rValue ); + + sal_Int32 nInWidth, nDistance, nOutWidth; + + std::u16string_view aToken; + if( !aTokenEnum.getNextToken( aToken ) ) + return false; + + if (!rUnitConverter.convertMeasureToCore(nInWidth, aToken)) + return false; + + if( !aTokenEnum.getNextToken( aToken ) ) + return false; + + if (!rUnitConverter.convertMeasureToCore(nDistance, aToken)) + return false; + + if( !aTokenEnum.getNextToken( aToken ) ) + return false; + + if (!rUnitConverter.convertMeasureToCore(nOutWidth, aToken)) + return false; + + // #i61946: accept line style even it's not part of our "normal" set of line styles + sal_uInt16 nWidth = 0; + + if( TOP_BORDER_LINE_WIDTH == nMemberId || + ALL_BORDER_LINE_WIDTH == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pTop, nWidth, + static_cast< sal_uInt16 >( nOutWidth ), + static_cast< sal_uInt16 >( nInWidth ), + static_cast< sal_uInt16 >( nDistance ) ); + + if( BOTTOM_BORDER_LINE_WIDTH == nMemberId || + ALL_BORDER_LINE_WIDTH == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pBottom, nWidth, + static_cast< sal_uInt16 >( nOutWidth ), + static_cast< sal_uInt16 >( nInWidth ), + static_cast< sal_uInt16 >( nDistance ) ); + + if( LEFT_BORDER_LINE_WIDTH == nMemberId || + ALL_BORDER_LINE_WIDTH == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pLeft, nWidth, + static_cast< sal_uInt16 >( nOutWidth ), + static_cast< sal_uInt16 >( nInWidth ), + static_cast< sal_uInt16 >( nDistance ) ); + + if( RIGHT_BORDER_LINE_WIDTH == nMemberId || + ALL_BORDER_LINE_WIDTH == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pRight, nWidth, + static_cast< sal_uInt16 >( nOutWidth ), + static_cast< sal_uInt16 >( nInWidth ), + static_cast< sal_uInt16 >( nDistance ) ); + } + break; + } + + rBox.SetLine( aBoxes.pTop.get(), SvxBoxItemLine::TOP ); + rBox.SetLine( aBoxes.pBottom.get(), SvxBoxItemLine::BOTTOM ); + rBox.SetLine( aBoxes.pLeft.get(), SvxBoxItemLine::LEFT ); + rBox.SetLine( aBoxes.pRight.get(), SvxBoxItemLine::RIGHT ); + + bOk = true; + } + break; + + case RES_BREAK: + { + SvxFormatBreakItem& rFormatBreak = dynamic_cast<SvxFormatBreakItem&>(rItem); + sal_uInt16 eEnum{}; + + if( !SvXMLUnitConverter::convertEnum( eEnum, rValue, psXML_BreakType ) ) + return false; + + if( eEnum == 0 ) + { + rFormatBreak.SetValue( SvxBreak::NONE ); + bOk = true; + } + else + { + switch( nMemberId ) + { + case MID_BREAK_BEFORE: + rFormatBreak.SetValue( eEnum == 1 ? + SvxBreak::ColumnBefore : + SvxBreak::PageBefore ); + break; + case MID_BREAK_AFTER: + rFormatBreak.SetValue( eEnum == 1 ? + SvxBreak::ColumnAfter : + SvxBreak::PageAfter ); + break; + } + bOk = true; + } + } + break; + + case RES_KEEP: + { + SvxFormatKeepItem& rFormatKeep = dynamic_cast<SvxFormatKeepItem&>(rItem); + + if( IsXMLToken( rValue, XML_ALWAYS ) || + IsXMLToken( rValue, XML_TRUE ) ) + { + rFormatKeep.SetValue( true ); + bOk = true; + } + else if( IsXMLToken( rValue, XML_AUTO ) || + IsXMLToken( rValue, XML_FALSE ) ) + { + rFormatKeep.SetValue( false ); + bOk = true; + } + } + break; + + case RES_BACKGROUND: + { + SvxBrushItem& rBrush = dynamic_cast<SvxBrushItem&>(rItem); + + Color aTempColor; + switch( nMemberId ) + { + case MID_BACK_COLOR: + if( IsXMLToken( rValue, XML_TRANSPARENT ) ) + { + rBrush.GetColor().SetAlpha(0); + bOk = true; + } + else if (::sax::Converter::convertColor(aTempColor, rValue)) + { + aTempColor.SetAlpha(255); + rBrush.SetColor( aTempColor ); + bOk = true; + } + break; + + case MID_GRAPHIC_REPEAT: + { + SvxGraphicPosition eGraphicPos = rBrush.GetGraphicPos(); + SvxGraphicPosition nPos = GPOS_NONE; + if( SvXMLUnitConverter::convertEnum( nPos, rValue, + psXML_BrushRepeat ) ) + { + if( GPOS_MM != nPos || GPOS_NONE == eGraphicPos || + GPOS_AREA == eGraphicPos || GPOS_TILED == eGraphicPos ) + rBrush.SetGraphicPos( nPos ); + bOk = true; + } + } + break; + + case MID_GRAPHIC_POSITION: + { + SvxGraphicPosition ePos = GPOS_NONE, eTmp; + SvxGraphicPosition nTmp; + SvXMLTokenEnumerator aTokenEnum( rValue ); + std::u16string_view aToken; + bool bHori = false, bVert = false; + bOk = true; + while( bOk && aTokenEnum.getNextToken( aToken ) ) + { + if( bHori && bVert ) + { + bOk = false; + } + else if( std::u16string_view::npos != aToken.find( '%' ) ) + { + sal_Int32 nPrc = 50; + if (::sax::Converter::convertPercent(nPrc, aToken)) + { + if( !bHori ) + { + ePos = nPrc < 25 ? GPOS_LT : + (nPrc < 75 ? GPOS_MM : GPOS_RB); + bHori = true; + } + else + { + eTmp = nPrc < 25 ? GPOS_LT: + (nPrc < 75 ? GPOS_LM : GPOS_LB); + sw_frmitems_MergeXMLVertPos( ePos, eTmp ); + bVert = true; + } + } + else + { + // wrong percentage + bOk = false; + } + } + else if( IsXMLToken( aToken, XML_CENTER ) ) + { + if( bHori ) + sw_frmitems_MergeXMLVertPos( ePos, GPOS_MM ); + else if ( bVert ) + sw_frmitems_MergeXMLHoriPos( ePos, GPOS_MM ); + else + ePos = GPOS_MM; + } + else if( SvXMLUnitConverter::convertEnum( nTmp, aToken, + psXML_BrushHoriPos ) ) + { + if( bVert ) + sw_frmitems_MergeXMLHoriPos( + ePos, nTmp ); + else if( !bHori ) + ePos = nTmp; + else + bOk = false; + bHori = true; + } + else if( SvXMLUnitConverter::convertEnum( nTmp, aToken, + psXML_BrushVertPos ) ) + { + if( bHori ) + sw_frmitems_MergeXMLVertPos( + ePos, nTmp ); + else if( !bVert ) + ePos = nTmp; + else + bOk = false; + bVert = true; + } + else + { + bOk = false; + } + } + + if( GPOS_NONE == ePos ) bOk = false; + if( bOk ) + rBrush.SetGraphicPos( ePos ); + } + break; + + case MID_GRAPHIC_FILTER: + rBrush.SetGraphicFilter( rValue ); + bOk = true; + break; + } + } + break; + + case RES_PAGEDESC: + { + SwFormatPageDesc& rPageDesc = dynamic_cast<SwFormatPageDesc&>(rItem); + + if( MID_PAGEDESC_PAGENUMOFFSET==nMemberId ) + { + sal_Int32 nVal; + bOk = ::sax::Converter::convertNumber( + nVal, rValue, 0, USHRT_MAX); + // i#114163 tdf#77111: OOo < 3.3 had a bug where it wrote + // "auto" as "0" for tables - now that we support a real offset + // 0, this fake "0" MUST NOT be imported as offset 0! + if( bOk && nVal > 0 ) + rPageDesc.SetNumOffset( o3tl::narrowing<sal_uInt16>(nVal) ); + } + } + break; + + case RES_LAYOUT_SPLIT: + case RES_ROW_SPLIT: + { + SfxBoolItem& rSplit = dynamic_cast<SfxBoolItem&>(rItem); + + if( IsXMLToken( rValue, XML_AUTO ) || + IsXMLToken( rValue, XML_TRUE ) ) + { + rSplit.SetValue( true ); + bOk = true; + } + else if( IsXMLToken( rValue, XML_ALWAYS ) || + IsXMLToken( rValue, XML_FALSE ) ) + { + rSplit.SetValue( false ); + bOk = true; + } + } + break; + + case RES_PRINT: + { + SfxBoolItem& rHasTextChangesOnly = dynamic_cast<SfxBoolItem&>(rItem); + + if( IsXMLToken( rValue, XML_TRUE ) ) + { + rHasTextChangesOnly.SetValue( true ); + bOk = true; + } + else if( IsXMLToken( rValue, XML_FALSE ) ) + { + rHasTextChangesOnly.SetValue( false ); + bOk = true; + } + } + break; + + case RES_HORI_ORIENT: + { + SwFormatHoriOrient& rHoriOrient = dynamic_cast<SwFormatHoriOrient&>(rItem); + + sal_Int16 nValue; + bOk = SvXMLUnitConverter::convertEnum( nValue, rValue, + aXMLTableAlignMap ); + if( bOk ) + rHoriOrient.SetHoriOrient( nValue ); + } + break; + + case RES_VERT_ORIENT: + { + SwFormatVertOrient& rVertOrient = dynamic_cast<SwFormatVertOrient&>(rItem); + + sal_Int16 nValue; + bOk = SvXMLUnitConverter::convertEnum( nValue, rValue, + aXMLTableVAlignMap ); + if( bOk ) + rVertOrient.SetVertOrient( nValue ); + //#i8855# text::VertOrientation::NONE is stored as empty string and should be applied here + else if(rValue.isEmpty()) + { + rVertOrient.SetVertOrient( text::VertOrientation::NONE ); + bOk = true; + } + } + break; + + case RES_FRM_SIZE: + { + SwFormatFrameSize& rFrameSize = dynamic_cast<SwFormatFrameSize&>(rItem); + + bool bSetHeight = false; + bool bSetWidth = false; + bool bSetSizeType = false; + SwFrameSize eSizeType = SwFrameSize::Variable; + sal_Int32 nMin = MINLAY; + + switch( nMemberId ) + { + case MID_FRMSIZE_REL_WIDTH: + { + sal_Int32 nValue; + bOk = ::sax::Converter::convertPercent( nValue, rValue ); + if( bOk ) + { + if( nValue < 1 ) + nValue = 1; + else if( nValue > 100 ) + nValue = 100; + + rFrameSize.SetWidthPercent( static_cast<sal_Int8>(nValue) ); + } + } + break; + case MID_FRMSIZE_WIDTH: + bSetWidth = true; + break; + case MID_FRMSIZE_MIN_HEIGHT: + eSizeType = SwFrameSize::Minimum; + bSetHeight = true; + nMin = 1; + bSetSizeType = true; + break; + case MID_FRMSIZE_FIX_HEIGHT: + eSizeType = SwFrameSize::Fixed; + bSetHeight = true; + nMin = 1; + bSetSizeType = true; + break; + case MID_FRMSIZE_COL_WIDTH: + eSizeType = SwFrameSize::Fixed; + bSetWidth = true; + bSetSizeType = true; + break; + case MID_FRMSIZE_REL_COL_WIDTH: + { + sal_Int32 nPos = rValue.indexOf( '*' ); + if( -1 != nPos ) + { + sal_Int32 nValue = rValue.toInt32(); + if( nValue < MINLAY ) + nValue = MINLAY; + else if( nValue > SAL_MAX_UINT16 ) + nValue = SAL_MAX_UINT16; + + rFrameSize.SetWidth( o3tl::narrowing<sal_uInt16>(nValue) ); + rFrameSize.SetHeightSizeType( SwFrameSize::Variable ); + bOk = true; + } + } + break; + } + + if( bSetHeight || bSetWidth ) + { + sal_Int32 nValue; + bOk = rUnitConverter.convertMeasureToCore(nValue, rValue, nMin, + USHRT_MAX ); + if( bOk ) + { + if( bSetWidth ) + rFrameSize.SetWidth( o3tl::narrowing<sal_uInt16>(nValue) ); + if( bSetHeight ) + rFrameSize.SetHeight( o3tl::narrowing<sal_uInt16>(nValue) ); + if( bSetSizeType ) + rFrameSize.SetHeightSizeType( eSizeType ); + } + } + } + break; + + case RES_FRAMEDIR: + { + if (IsXMLToken(rValue, XML_BT_LR)) + { + // Read bt-lr from the extension namespace, handle other values + // below. + Any aAny; + aAny <<= static_cast<sal_uInt16>(SvxFrameDirection::Vertical_LR_BT); + bOk = rItem.PutValue(aAny, 0); + } + else if (IsXMLToken(rValue, XML_TB_RL90)) + { + // Read tb-rl90 from the extension namespace. + Any aAny; + aAny <<= static_cast<sal_uInt16>(SvxFrameDirection::Vertical_RL_TB90); + bOk = rItem.PutValue(aAny, 0); + } + else + { + std::unique_ptr<XMLPropertyHandler> pWritingModeHandler = + XMLPropertyHandlerFactory::CreatePropertyHandler( + XML_TYPE_TEXT_WRITING_MODE_WITH_DEFAULT ); + Any aAny; + bOk = pWritingModeHandler->importXML( rValue, aAny, + rUnitConverter ); + if( bOk ) + bOk = rItem.PutValue( aAny, 0 ); + } + } + break; + + case RES_COLLAPSING_BORDERS: + { + SfxBoolItem& rBorders = dynamic_cast<SfxBoolItem&>(rItem); + + if( IsXMLToken( rValue, XML_COLLAPSING ) ) + { + rBorders.SetValue(true); + bOk = true; + } + else if( IsXMLToken( rValue, XML_SEPARATING ) ) + { + rBorders.SetValue(false); + bOk = true; + } + else + bOk = false; + } + break; + + default: + OSL_FAIL("Item not implemented!"); + break; + } + + return bOk; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlimpit.hxx b/sw/source/filter/xml/xmlimpit.hxx new file mode 100644 index 0000000000..8227546a62 --- /dev/null +++ b/sw/source/filter/xml/xmlimpit.hxx @@ -0,0 +1,93 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLIMPIT_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLIMPIT_HXX + +#include <com/sun/star/xml/sax/XFastAttributeList.hpp> +#include "xmlitmap.hxx" + +class SvXMLUnitConverter; +class SfxPoolItem; +class SfxItemSet; +class SvXMLNamespaceMap; +struct SvXMLItemMapEntry; +class SvXMLAttrContainerItem; + +class SvXMLImportItemMapper +{ + SvXMLItemMapEntriesRef mrMapEntries; + +public: + explicit SvXMLImportItemMapper( SvXMLItemMapEntriesRef aMapEntries ); + virtual ~SvXMLImportItemMapper(); + + /** fills the given itemset with the attributes in the given list */ + void importXML( SfxItemSet& rSet, + css::uno::Reference< css::xml::sax::XFastAttributeList > const & xAttrList, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap ); + + /** this method is called for every item that has the + MID_SW_FLAG_SPECIAL_ITEM_IMPORT flag set */ + virtual bool handleSpecialItem( const SvXMLItemMapEntry& rEntry, + SfxPoolItem& rItem, + SfxItemSet& rSet, + const OUString& rValue, + const SvXMLUnitConverter& rUnitConverter ); + + /** this method is called for every item that has the + MID_SW_FLAG_NO_ITEM_IMPORT flag set */ + virtual bool handleNoItem( const SvXMLItemMapEntry& rEntry, + SfxItemSet& rSet, + const OUString& rValue, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap ); + + /** This method is called when all attributes have benn processed. It + * may be used to remove items that are incomplete */ + virtual void finished(SfxItemSet & rSet, + SvXMLUnitConverter const& rUnitConverter) const; + + virtual void setMapEntries( SvXMLItemMapEntriesRef rMapEntries ); + inline const SvXMLItemMapEntriesRef& getMapEntries() const; + + /** This method is called for every item that should be set based + upon an XML attribute value. */ + static bool PutXMLValue( + SfxPoolItem& rItem, + const OUString& rValue, + sal_uInt16 nMemberId, + const SvXMLUnitConverter& rUnitConverter ); +private: + void importXMLUnknownAttributes( SfxItemSet& rSet, + css::uno::Reference< css::xml::sax::XFastAttributeList > const & xAttrList, + const SvXMLUnitConverter& rUnitConverter, + std::unique_ptr<SvXMLAttrContainerItem>& pUnknownItem ); + +}; + +inline const SvXMLItemMapEntriesRef& +SvXMLImportItemMapper::getMapEntries() const +{ + return mrMapEntries; +} + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLIMPIT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlitem.cxx b/sw/source/filter/xml/xmlitem.cxx new file mode 100644 index 0000000000..ff98d2ff69 --- /dev/null +++ b/sw/source/filter/xml/xmlitem.cxx @@ -0,0 +1,104 @@ +/* -*- 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/brushitem.hxx> +#include <sal/log.hxx> +#include <xmloff/xmlimp.hxx> +#include "xmlimpit.hxx" +#include "xmlitem.hxx" +#include "xmlbrshi.hxx" +#include <hintids.hxx> + +using namespace ::com::sun::star; + +SwXMLItemSetContext::SwXMLItemSetContext( SvXMLImport& rImp, sal_Int32 /*nElement*/, + const uno::Reference< xml::sax::XFastAttributeList >& xAttrList, + SfxItemSet& rISet, + SvXMLImportItemMapper& rIMap, + const SvXMLUnitConverter& rUnitConverter ): + SvXMLImportContext( rImp ), + m_rItemSet( rISet ), + m_rIMapper( rIMap ), + m_rUnitConv( rUnitConverter ) +{ + rIMap.importXML( m_rItemSet, xAttrList, m_rUnitConv, + GetImport().GetNamespaceMap() ); +} + +SwXMLItemSetContext::~SwXMLItemSetContext() +{ + if( m_xBackground.is() ) + { + const SvxBrushItem& rItem = m_xBackground->GetItem(); + m_rItemSet.Put( rItem ); + } +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLItemSetContext::createFastChildContext( + sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) +{ + SvXMLItemMapEntriesRef xMapEntries = m_rIMapper.getMapEntries(); + SvXMLItemMapEntry const * pEntry = xMapEntries->getByName( nElement ); + + if( pEntry && 0 != (pEntry->nMemberId & MID_SW_FLAG_ELEMENT_ITEM_IMPORT) ) + { + return createFastChildContext( nElement, xAttrList, *pEntry ); + } + else + XMLOFF_WARN_UNKNOWN_ELEMENT("sw", nElement); + return nullptr; +} + +/** This method is called from this instance implementation of + CreateChildContext if the element matches an entry in the + SvXMLImportItemMapper with the mid flag MID_SW_FLAG_ELEMENT +*/ +SvXMLImportContextRef SwXMLItemSetContext::createFastChildContext( sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList >& xAttrList, + const SvXMLItemMapEntry& rEntry ) +{ + rtl::Reference<SwXMLBrushItemImportContext> xContext; + + switch( rEntry.nWhichId ) + { + case RES_BACKGROUND: + { + if( const SvxBrushItem* pItem = m_rItemSet.GetItemIfSet( RES_BACKGROUND, + false ) ) + { + xContext = new SwXMLBrushItemImportContext( + GetImport(), nElement, xAttrList, + m_rUnitConv, *pItem ); + } + else + { + xContext = new SwXMLBrushItemImportContext( + GetImport(), nElement, xAttrList, + m_rUnitConv, RES_BACKGROUND ); + } + m_xBackground = xContext; + } + break; + } + + return xContext; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlitem.hxx b/sw/source/filter/xml/xmlitem.hxx new file mode 100644 index 0000000000..cd6f9c8db6 --- /dev/null +++ b/sw/source/filter/xml/xmlitem.hxx @@ -0,0 +1,64 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLITEM_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLITEM_HXX + +#include <com/sun/star/xml/sax/XFastAttributeList.hpp> +#include <svl/itemset.hxx> +#include <xmloff/xmlictxt.hxx> + +class SfxItemSet; +class SvXMLImportItemMapper; +class SvXMLUnitConverter; +struct SvXMLItemMapEntry; +class SwXMLBrushItemImportContext; + +class SwXMLItemSetContext final : public SvXMLImportContext +{ + SfxItemSet &m_rItemSet; + const SvXMLImportItemMapper &m_rIMapper; + const SvXMLUnitConverter &m_rUnitConv; + rtl::Reference<SwXMLBrushItemImportContext> m_xBackground; + +public: + + SwXMLItemSetContext( SvXMLImport& rImport, sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList, + SfxItemSet& rItemSet, + SvXMLImportItemMapper& rIMap, + const SvXMLUnitConverter& rUnitConv ); + + virtual ~SwXMLItemSetContext() override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + +private: + // This method is called from this instance implementation of + // createFastChildContext if the element matches an entry in the + // SvXMLImportItemMapper with the mid flag MID_SW_FLAG_ELEMENT_ITEM_IMPORT + SvXMLImportContextRef createFastChildContext( sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList, + const SvXMLItemMapEntry& rEntry ); +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLITEM_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmliteme.cxx b/sw/source/filter/xml/xmliteme.cxx new file mode 100644 index 0000000000..3d19a9d6dc --- /dev/null +++ b/sw/source/filter/xml/xmliteme.cxx @@ -0,0 +1,246 @@ +/* -*- 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 <com/sun/star/util/MeasureUnit.hpp> + +#include <hintids.hxx> +#include <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> +#include <utility> +#include <xmloff/xmluconv.hxx> +#include "xmlexpit.hxx" +#include <xmloff/namespacemap.hxx> +#include "xmlbrshe.hxx" +#include <editeng/brushitem.hxx> +#include <fmtornt.hxx> +#include <unomid.h> +#include <frmfmt.hxx> +#include "xmlexp.hxx" +#include <editeng/memberids.h> +#include <editeng/prntitem.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::xmloff::token; + +namespace { + +class SwXMLTableItemMapper_Impl: public SvXMLExportItemMapper +{ + SwXMLBrushItemExport m_aBrushItemExport; + +protected: + + sal_uInt32 m_nAbsWidth; + + static void AddAttribute( sal_uInt16 nPrefix, enum XMLTokenEnum eLName, + const OUString& rValue, + const SvXMLNamespaceMap& rNamespaceMap, + comphelper::AttributeList& rAttrList ); + +public: + + SwXMLTableItemMapper_Impl( + SvXMLItemMapEntriesRef rMapEntries, + SwXMLExport& rExp ); + + virtual void handleSpecialItem( comphelper::AttributeList& rAttrList, + const SvXMLItemMapEntry& rEntry, + const SfxPoolItem& rItem, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + const SfxItemSet *pSet ) const override; + + virtual void handleElementItem( + const SvXMLItemMapEntry& rEntry, + const SfxPoolItem& rItem ) const override; + + inline void SetAbsWidth( sal_uInt32 nAbs ); +}; + +} + +SwXMLTableItemMapper_Impl::SwXMLTableItemMapper_Impl( + SvXMLItemMapEntriesRef rMapEntries, + SwXMLExport& rExp ) : + SvXMLExportItemMapper( std::move(rMapEntries) ), + m_aBrushItemExport( rExp ), + m_nAbsWidth( USHRT_MAX ) +{ +} + +void SwXMLTableItemMapper_Impl::AddAttribute( sal_uInt16 nPrefix, + enum XMLTokenEnum eLName, + const OUString& rValue, + const SvXMLNamespaceMap& rNamespaceMap, + comphelper::AttributeList& rAttrList ) +{ + OUString sName( rNamespaceMap.GetQNameByKey( nPrefix, + GetXMLToken(eLName) ) ); + rAttrList.AddAttribute( sName, rValue ); +} + +void SwXMLTableItemMapper_Impl::handleSpecialItem( + comphelper::AttributeList& rAttrList, + const SvXMLItemMapEntry& rEntry, + const SfxPoolItem& rItem, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + const SfxItemSet *pSet ) const +{ + switch( rEntry.nWhichId ) + { + + case RES_PRINT: + { + const SvxPrintItem *pItem; + if( pSet && + (pItem = pSet->GetItemIfSet( RES_PRINT )) ) + { + bool bHasTextChangesOnly = pItem->GetValue(); + if ( !bHasTextChangesOnly ) + { + OUString sValue; + sal_uInt16 nMemberId = + static_cast<sal_uInt16>( rEntry.nMemberId & MID_SW_FLAG_MASK ); + + if( SvXMLExportItemMapper::QueryXMLValue( + rItem, sValue, nMemberId, rUnitConverter ) ) + { + AddAttribute( rEntry.nNameSpace, rEntry.eLocalName, + sValue, rNamespaceMap, rAttrList ); + } + } + } + } + break; + + case RES_LR_SPACE: + { + const SwFormatHoriOrient *pItem; + if( pSet && + (pItem = pSet->GetItemIfSet( RES_HORI_ORIENT )) ) + { + sal_Int16 eHoriOrient = pItem->GetHoriOrient(); + bool bExport = false; + sal_uInt16 nMemberId = + o3tl::narrowing<sal_uInt16>( rEntry.nMemberId & MID_SW_FLAG_MASK ); + switch( nMemberId ) + { + case MID_L_MARGIN: + bExport = text::HoriOrientation::NONE == eHoriOrient || + text::HoriOrientation::LEFT_AND_WIDTH == eHoriOrient; + break; + case MID_R_MARGIN: + bExport = text::HoriOrientation::NONE == eHoriOrient; + break; + } + OUString sValue; + if( bExport && SvXMLExportItemMapper::QueryXMLValue( + rItem, sValue, nMemberId, rUnitConverter ) ) + { + AddAttribute( rEntry.nNameSpace, rEntry.eLocalName, sValue, + rNamespaceMap, rAttrList ); + } + } + } + break; + + case RES_FRM_SIZE: + { + sal_uInt16 nMemberId = + o3tl::narrowing<sal_uInt16>( rEntry.nMemberId & MID_SW_FLAG_MASK ); + switch( nMemberId ) + { + case MID_FRMSIZE_WIDTH: + if( m_nAbsWidth ) + { + OUStringBuffer sBuffer; + rUnitConverter.convertMeasureToXML( sBuffer, m_nAbsWidth ); + AddAttribute( rEntry.nNameSpace, rEntry.eLocalName, + sBuffer.makeStringAndClear(), + rNamespaceMap, rAttrList ); + } + break; + case MID_FRMSIZE_REL_WIDTH: + { + OUString sValue; + if( SvXMLExportItemMapper::QueryXMLValue( + rItem, sValue, nMemberId, rUnitConverter ) ) + { + AddAttribute( rEntry.nNameSpace, rEntry.eLocalName, + sValue, rNamespaceMap, rAttrList ); + } + } + break; + } + } + break; + } +} + +/** this method is called for every item that has the + MID_SW_FLAG_ELEMENT_EXPORT flag set */ +void SwXMLTableItemMapper_Impl::handleElementItem( + const SvXMLItemMapEntry& rEntry, + const SfxPoolItem& rItem ) const +{ + switch( rEntry.nWhichId ) + { + case RES_BACKGROUND: + { + const_cast<SwXMLTableItemMapper_Impl *>(this)->m_aBrushItemExport.exportXML( + static_cast<const SvxBrushItem&>(rItem) ); + } + break; + } +} + +inline void SwXMLTableItemMapper_Impl::SetAbsWidth( sal_uInt32 nAbs ) +{ + m_nAbsWidth = nAbs; +} + +void SwXMLExport::InitItemExport() +{ + m_pTwipUnitConverter.reset(new SvXMLUnitConverter(getComponentContext(), + util::MeasureUnit::TWIP, GetMM100UnitConverter().GetXMLMeasureUnit(), + getSaneDefaultVersion())); + + m_xTableItemMap = new SvXMLItemMapEntries( aXMLTableItemMap ); + m_xTableRowItemMap = new SvXMLItemMapEntries( aXMLTableRowItemMap ); + m_xTableCellItemMap = new SvXMLItemMapEntries( aXMLTableCellItemMap ); + + m_pTableItemMapper.reset(new SwXMLTableItemMapper_Impl( m_xTableItemMap, *this )); +} + +void SwXMLExport::FinitItemExport() +{ + m_pTableItemMapper.reset(); + m_pTwipUnitConverter.reset(); +} + +void SwXMLExport::ExportTableFormat( const SwFrameFormat& rFormat, sal_uInt32 nAbsWidth ) +{ + static_cast<SwXMLTableItemMapper_Impl *>(m_pTableItemMapper.get()) + ->SetAbsWidth( nAbsWidth ); + ExportFormat(rFormat, XML_TABLE, {}); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlitemi.cxx b/sw/source/filter/xml/xmlitemi.cxx new file mode 100644 index 0000000000..9281604c4f --- /dev/null +++ b/sw/source/filter/xml/xmlitemi.cxx @@ -0,0 +1,278 @@ +/* -*- 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 <rtl/ustring.hxx> +#include <osl/diagnose.h> + +#include <com/sun/star/util/MeasureUnit.hpp> + +#include <xmloff/xmluconv.hxx> +#include <xmloff/families.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> + +#include <editeng/memberids.h> +#include <svl/itemset.hxx> +#include <svl/itempool.hxx> + +#include <hintids.hxx> +#include <unomid.h> +#include "xmlimp.hxx" +#include "xmlitmap.hxx" +#include "xmlimpit.hxx" +#include "xmlitem.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +class SwXMLImportTableItemMapper_Impl: public SvXMLImportItemMapper +{ + +public: + + explicit SwXMLImportTableItemMapper_Impl(const SvXMLItemMapEntriesRef& rMapEntries); + + virtual bool handleSpecialItem( const SvXMLItemMapEntry& rEntry, + SfxPoolItem& rItem, + SfxItemSet& rSet, + const OUString& rValue, + const SvXMLUnitConverter& rUnitConverter ) override; + + virtual bool + handleNoItem(SvXMLItemMapEntry const& rEntry, + SfxItemSet & rSet, + OUString const& rValue, + SvXMLUnitConverter const& rUnitConverter, + SvXMLNamespaceMap const& rNamespaceMap) override; + + virtual void finished(SfxItemSet & rSet, + SvXMLUnitConverter const& rUnitConverter) const override; + + virtual void setMapEntries( SvXMLItemMapEntriesRef rMapEntries ) override; + +private: + void Reset(); + + OUString m_FoMarginValue; + enum { LEFT = 0, RIGHT = 1, TOP = 2, BOTTOM = 3 }; + bool m_bHaveMargin[4]; +}; + +} + +SwXMLImportTableItemMapper_Impl::SwXMLImportTableItemMapper_Impl( + const SvXMLItemMapEntriesRef& rMapEntries ) : + SvXMLImportItemMapper( rMapEntries ) +{ + Reset(); +} + +void SwXMLImportTableItemMapper_Impl::Reset() +{ + m_FoMarginValue.clear(); + for (int i = 0; i < 3; ++i) + { + m_bHaveMargin[i] = false; + } +} + +void SwXMLImportTableItemMapper_Impl::setMapEntries( + SvXMLItemMapEntriesRef rMapEntries ) +{ + Reset(); + SvXMLImportItemMapper::setMapEntries(rMapEntries); +} + +bool SwXMLImportTableItemMapper_Impl::handleSpecialItem( + const SvXMLItemMapEntry& rEntry, + SfxPoolItem& rItem, + SfxItemSet& rItemSet, + const OUString& rValue, + const SvXMLUnitConverter& rUnitConv ) +{ + bool bRet = false; + sal_uInt16 nMemberId = static_cast< sal_Int16 >(rEntry.nMemberId & MID_SW_FLAG_MASK); + switch( rItem.Which() ) + { + case RES_LR_SPACE: + switch (nMemberId) + { + case MID_L_MARGIN: + m_bHaveMargin[LEFT] = true; + break; + case MID_R_MARGIN: + m_bHaveMargin[RIGHT] = true; + break; + } + bRet = SvXMLImportItemMapper::PutXMLValue( + rItem, rValue, nMemberId, rUnitConv); + break; + case RES_UL_SPACE: + switch (nMemberId) + { + case MID_UP_MARGIN: + m_bHaveMargin[TOP] = true; + break; + case MID_LO_MARGIN: + m_bHaveMargin[BOTTOM] = true; + break; + } + bRet = SvXMLImportItemMapper::PutXMLValue( + rItem, rValue, nMemberId, rUnitConv); + break; + case RES_FRM_SIZE: + switch( nMemberId ) + { + case MID_FRMSIZE_COL_WIDTH: + // If the item is existing already, a relative value has been set + // already that must be preserved. + if( SfxItemState::SET != rItemSet.GetItemState( RES_FRM_SIZE, + false ) ) + bRet = SvXMLImportItemMapper::PutXMLValue( + rItem, rValue, nMemberId, rUnitConv ); + break; + } + } + + return bRet; +} + +bool SwXMLImportTableItemMapper_Impl::handleNoItem( + SvXMLItemMapEntry const& rEntry, + SfxItemSet & rSet, + OUString const& rValue, + SvXMLUnitConverter const& rUnitConverter, + SvXMLNamespaceMap const& rNamespaceMap) +{ + if ((XML_NAMESPACE_FO == rEntry.nNameSpace) && + (xmloff::token::XML_MARGIN == rEntry.eLocalName)) + { + m_FoMarginValue = rValue; + return true; + } + else + { + return SvXMLImportItemMapper::handleNoItem( + rEntry, rSet, rValue, rUnitConverter, rNamespaceMap); + } +} + +void SwXMLImportTableItemMapper_Impl::finished( + SfxItemSet & rSet, SvXMLUnitConverter const& rUnitConverter) const +{ + if (m_FoMarginValue.isEmpty()) + return; + + sal_uInt16 const Ids[4][2] = { + { RES_LR_SPACE, MID_L_MARGIN }, + { RES_LR_SPACE, MID_R_MARGIN }, + { RES_UL_SPACE, MID_UP_MARGIN }, + { RES_UL_SPACE, MID_LO_MARGIN }, + }; + for (int i = 0; i < 4; ++i) + { + if (m_bHaveMargin[i]) + { + continue; // already read fo:margin-top etc. + } + // first get item from itemset + SfxPoolItem const* pItem = nullptr; + SfxItemState eState = + rSet.GetItemState(Ids[i][0], true, &pItem); + + // if not set, try the pool + if ((SfxItemState::SET != eState) && SfxItemPool::IsWhich(Ids[i][0])) + { + pItem = &rSet.GetPool()->GetDefaultItem(Ids[i][0]); + } + + // do we have an item? + if (eState >= SfxItemState::DEFAULT && pItem) + { + std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone()); + bool const bPut = PutXMLValue( + *pNewItem, m_FoMarginValue, Ids[i][1], rUnitConverter); + if (bPut) + { + rSet.Put(std::move(pNewItem)); + } + } + else + { + OSL_ENSURE(false, "could not get item"); + } + } +} + +void SwXMLImport::InitItemImport() +{ + m_pTwipUnitConv.reset( new SvXMLUnitConverter( GetComponentContext(), + util::MeasureUnit::TWIP, util::MeasureUnit::TWIP, + SvtSaveOptions::ODFSVER_LATEST_EXTENDED) ); + + m_xTableItemMap = new SvXMLItemMapEntries( aXMLTableItemMap ); + m_xTableColItemMap = new SvXMLItemMapEntries( aXMLTableColItemMap ); + m_xTableRowItemMap = new SvXMLItemMapEntries( aXMLTableRowItemMap ); + m_xTableCellItemMap = new SvXMLItemMapEntries( aXMLTableCellItemMap ); + + m_pTableItemMapper.reset( new SwXMLImportTableItemMapper_Impl( m_xTableItemMap ) ); +} + +void SwXMLImport::FinitItemImport() +{ + m_pTableItemMapper.reset(); + m_pTwipUnitConv.reset(); +} + +SvXMLImportContext *SwXMLImport::CreateTableItemImportContext( + sal_Int32 nElement, + const Reference< xml::sax::XFastAttributeList > & xAttrList, + XmlStyleFamily nFamily, + SfxItemSet& rItemSet ) +{ + SvXMLItemMapEntriesRef xItemMap; + + switch( nFamily ) + { + case XmlStyleFamily::TABLE_TABLE: + xItemMap = m_xTableItemMap; + break; + case XmlStyleFamily::TABLE_COLUMN: + xItemMap = m_xTableColItemMap; + break; + case XmlStyleFamily::TABLE_ROW: + xItemMap = m_xTableRowItemMap; + break; + case XmlStyleFamily::TABLE_CELL: + xItemMap = m_xTableCellItemMap; + break; + default: break; + } + + m_pTableItemMapper->setMapEntries( xItemMap ); + + return new SwXMLItemSetContext( *this, nElement, + xAttrList, rItemSet, + GetTableItemMapper(), + *m_pTwipUnitConv ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlitemm.cxx b/sw/source/filter/xml/xmlitemm.cxx new file mode 100644 index 0000000000..0d25da02c1 --- /dev/null +++ b/sw/source/filter/xml/xmlitemm.cxx @@ -0,0 +1,286 @@ +/* -*- 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/memberids.h> +#include <hintids.hxx> +#include <svx/unomid.hxx> +#include <xmloff/xmlnamespace.hxx> +#include "xmlitmap.hxx" +#include <xmloff/xmltoken.hxx> +#include <o3tl/safeint.hxx> + +#include <unomid.h> + +using namespace ::xmloff::token; + +#define MAP_ENTRY( p, l, w, m ) \ + { XML_NAMESPACE_##p, XML_##l, o3tl::narrowing<sal_uInt16>(w), m } +#define M_E_SI( p, l, w, m ) \ + { XML_NAMESPACE_##p, XML_##l, o3tl::narrowing<sal_uInt16>(w), MID_SW_FLAG_SPECIAL_ITEM_IMPORT|m } +#define M_E_SE( p, l, w, m ) \ + { XML_NAMESPACE_##p, XML_##l, o3tl::narrowing<sal_uInt16>(w), MID_SW_FLAG_SPECIAL_ITEM_EXPORT|m } +#define M_E_SIE( p, l, w, m ) \ + { XML_NAMESPACE_##p, XML_##l, o3tl::narrowing<sal_uInt16>(w), MID_SW_FLAG_SPECIAL_ITEM_EXPORT|MID_SW_FLAG_SPECIAL_ITEM_IMPORT|m } + +const SvXMLItemMapEntry aTableItemMap[] = +{ + // RES_FILL_ORDER + // not required + // RES_FRM_SIZE + M_E_SE( STYLE, WIDTH, RES_FRM_SIZE, MID_FRMSIZE_WIDTH ), + M_E_SE( STYLE, REL_WIDTH, RES_FRM_SIZE, MID_FRMSIZE_REL_WIDTH ), + // RES_PAPER_BIN + // not required + M_E_SE( FO, MARGIN, 0xFFFF/*invalid*/, MID_SW_FLAG_NO_ITEM_IMPORT), + M_E_SIE( FO, MARGIN_LEFT, RES_LR_SPACE, MID_L_MARGIN ), + M_E_SIE( FO, MARGIN_RIGHT, RES_LR_SPACE, MID_R_MARGIN ), + // RES_UL_SPACE + M_E_SI( FO, MARGIN_TOP, RES_UL_SPACE, MID_UP_MARGIN ), + M_E_SI( FO, MARGIN_BOTTOM, RES_UL_SPACE, MID_LO_MARGIN ), + // RES_PAGEDESC + MAP_ENTRY( STYLE, PAGE_NUMBER, RES_PAGEDESC, MID_PAGEDESC_PAGENUMOFFSET), + // RES_BREAK + MAP_ENTRY( FO, BREAK_BEFORE, RES_BREAK, MID_BREAK_BEFORE ), + MAP_ENTRY( FO, BREAK_AFTER, RES_BREAK, MID_BREAK_AFTER ), + // RES_CNTNT + // not required + // RES_HEADER + // not required + // RES_FOOTER + // not required + // RES_PRINT + // not required + // RES_OPAQUE + // not required + // RES_PROTECT + // not required + // RES_SURROUND + // not required + // RES_VERT_ORIENT + // not required + // RES_HORI_ORIENT + MAP_ENTRY( TABLE, ALIGN, RES_HORI_ORIENT, 0 ), + // RES_ANCHOR + // not required + // RES_BACKGROUND + MAP_ENTRY( FO, BACKGROUND_COLOR, RES_BACKGROUND, MID_BACK_COLOR ), + MAP_ENTRY( STYLE, BACKGROUND_IMAGE, RES_BACKGROUND, MID_SW_FLAG_ELEMENT_ITEM ), + // RES_BOX + // not required + // RES_SHADOW + MAP_ENTRY( STYLE, SHADOW, RES_SHADOW, 0 ), + // RES_FRMMACRO + // not required + // RES_COL + // not required + // RES_KEEP + MAP_ENTRY( FO, KEEP_WITH_NEXT, RES_KEEP, 0 ), + // RES_URL + // not required + // RES_EDIT_IN_READONLY + // not required + // RES_LAYOUT_SPLIT + MAP_ENTRY( STYLE, MAY_BREAK_BETWEEN_ROWS, RES_LAYOUT_SPLIT, 0 ), + // RES_CHAIN + // not required + // RES_LINENUMBER + // not required + // RES_FTN_AT_TXTEND + // not required + // RES_END_AT_TXTEND + // not required + // RES_UNKNOWNATR_CONTAINER + M_E_SE( TEXT, XMLNS, RES_UNKNOWNATR_CONTAINER, 0 ), + + // RES_FRAMEDIR + MAP_ENTRY( STYLE, WRITING_MODE, RES_FRAMEDIR, 0 ), + + // RES_COLLAPSING_BORDERS + MAP_ENTRY( TABLE, BORDER_MODEL, RES_COLLAPSING_BORDERS, 0 ) +}; +const std::span<SvXMLItemMapEntry const> aXMLTableItemMap(aTableItemMap); + +const SvXMLItemMapEntry aTableColItemMap[] = +{ + M_E_SI( STYLE, COLUMN_WIDTH, RES_FRM_SIZE, MID_FRMSIZE_COL_WIDTH ), + MAP_ENTRY( STYLE, REL_COLUMN_WIDTH, RES_FRM_SIZE, MID_FRMSIZE_REL_COL_WIDTH ), +}; +const std::span<SvXMLItemMapEntry const> aXMLTableColItemMap(aTableColItemMap); + +const SvXMLItemMapEntry aTableRowItemMap[] = +{ + // RES_FILL_ORDER + // not required + // RES_FRM_SIZE + MAP_ENTRY( STYLE, ROW_HEIGHT, RES_FRM_SIZE, MID_FRMSIZE_FIX_HEIGHT ), + MAP_ENTRY( STYLE, MIN_ROW_HEIGHT, RES_FRM_SIZE, MID_FRMSIZE_MIN_HEIGHT ), + // RES_PAPER_BIN + // not required + // RES_LR_SPACE + // not required + // RES_UL_SPACE + // not required + // RES_PAGEDESC + // not required + // RES_BREAK + // not required + // RES_CNTNT + // not required + // RES_HEADER + // not required + // RES_FOOTER + // not required + // RES_PRINT + // M_E_SE( STYLE, TEXT_CHANGES_ONLY, RES_PRINT, 0 ), + M_E_SE( LO_EXT, TEXT_CHANGES_ONLY, RES_PRINT, 0 ), + // RES_OPAQUE + // not required + // RES_PROTECT + // not required + // RES_SURROUND + // not required + // RES_VERT_ORIENT + // not required + // RES_HORI_ORIENT + // not required + // RES_ANCHOR + // not required + // RES_BACKGROUND + MAP_ENTRY( FO, BACKGROUND_COLOR, RES_BACKGROUND, MID_BACK_COLOR ), + MAP_ENTRY( STYLE, BACKGROUND_IMAGE, RES_BACKGROUND, MID_SW_FLAG_ELEMENT_ITEM ), + // RES_BOX + // not required + // RES_ANCHOR + // not required + // RES_SHADOW + // not required + // RES_FRMMACRO + // not required + // RES_COL + // not required + // RES_KEEP + // not required + // RES_URL + // not required + // RES_EDIT_IN_READONLY + // not required + // RES_LAYOUT_SPLIT + M_E_SE( STYLE, KEEP_TOGETHER, RES_ROW_SPLIT, 0 ), + M_E_SE( FO, KEEP_TOGETHER, RES_ROW_SPLIT, 0 ), + // RES_CHAIN + // not required + // RES_LINENUMBER + // not required + // RES_FTN_AT_TXTEND + // not required + // RES_END_AT_TXTEND + // not required + // RES_UNKNOWNATR_CONTAINER + M_E_SE( TEXT, XMLNS, RES_UNKNOWNATR_CONTAINER, 0 ) +}; +const std::span<SvXMLItemMapEntry const> aXMLTableRowItemMap(aTableRowItemMap); + +const SvXMLItemMapEntry aTableCellItemMap[] = +{ + // RES_FILL_ORDER + // not required + // RES_FRM_SIZE + // not required + // RES_PAPER_BIN + // not required + // RES_LR_SPACE + // not required + // RES_UL_SPACE + // not required + // RES_PAGEDESC + // not required + // RES_BREAK + // not required + // RES_CNTNT + // not required + // RES_HEADER + // not required + // RES_FOOTER + // not required + // RES_PRINT + // M_E_SE( STYLE, TEXT_CHANGES_ONLY, RES_PRINT, 0 ), + M_E_SE( LO_EXT, TEXT_CHANGES_ONLY, RES_PRINT, 0 ), + // RES_OPAQUE + // not required + // RES_PROTECT + // not required + // RES_SURROUND + // not required + // RES_VERT_ORIENT + MAP_ENTRY( STYLE, VERTICAL_ALIGN, RES_VERT_ORIENT, 0 ), + // RES_HORI_ORIENT + // not required + // RES_ANCHOR + // not required + // RES_BACKGROUND + MAP_ENTRY( FO, BACKGROUND_COLOR, RES_BACKGROUND, MID_BACK_COLOR ), + MAP_ENTRY( STYLE, BACKGROUND_IMAGE, RES_BACKGROUND, MID_SW_FLAG_ELEMENT_ITEM ), + // RES_BOX + MAP_ENTRY( STYLE, BORDER_LINE_WIDTH, RES_BOX, ALL_BORDER_LINE_WIDTH ), + MAP_ENTRY( STYLE, BORDER_LINE_WIDTH_LEFT, RES_BOX, LEFT_BORDER_LINE_WIDTH ), + MAP_ENTRY( STYLE, BORDER_LINE_WIDTH_RIGHT, RES_BOX, RIGHT_BORDER_LINE_WIDTH ), + MAP_ENTRY( STYLE, BORDER_LINE_WIDTH_TOP, RES_BOX, TOP_BORDER_LINE_WIDTH ), + MAP_ENTRY( STYLE, BORDER_LINE_WIDTH_BOTTOM, RES_BOX, BOTTOM_BORDER_LINE_WIDTH ), + MAP_ENTRY( FO, PADDING, RES_BOX, ALL_BORDER_PADDING ), + MAP_ENTRY( FO, PADDING_LEFT, RES_BOX, LEFT_BORDER_PADDING ), + MAP_ENTRY( FO, PADDING_RIGHT, RES_BOX, RIGHT_BORDER_PADDING ), + MAP_ENTRY( FO, PADDING_TOP, RES_BOX, TOP_BORDER_PADDING ), + MAP_ENTRY( FO, PADDING_BOTTOM, RES_BOX, BOTTOM_BORDER_PADDING ), + MAP_ENTRY( FO, BORDER, RES_BOX, ALL_BORDER ), + MAP_ENTRY( FO, BORDER_LEFT, RES_BOX, LEFT_BORDER ), + MAP_ENTRY( FO, BORDER_RIGHT, RES_BOX, RIGHT_BORDER ), + MAP_ENTRY( FO, BORDER_TOP, RES_BOX, TOP_BORDER ), + MAP_ENTRY( FO, BORDER_BOTTOM, RES_BOX, BOTTOM_BORDER ), + // RES_SHADOW + // not required + // RES_FRMMACRO + // not required + // RES_COL + // not required + // RES_KEEP + // not required + // RES_URL + // not required + // RES_EDIT_IN_READONLY + // not required + // RES_LAYOUT_SPLIT + // not required + // RES_CHAIN + // not required + // RES_LINENUMBER + // not required + // RES_FTN_AT_TXTEND + // not required + // RES_END_AT_TXTEND + // not required + // RES_UNKNOWNATR_CONTAINER + M_E_SE( TEXT, XMLNS, RES_UNKNOWNATR_CONTAINER, 0 ), + + // RES_FRAMEDIR + MAP_ENTRY( STYLE, WRITING_MODE, RES_FRAMEDIR, 0 ), + MAP_ENTRY( LO_EXT, WRITING_MODE, RES_FRAMEDIR, 0 ), +}; +const std::span<SvXMLItemMapEntry const> aXMLTableCellItemMap(aTableCellItemMap); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlithlp.cxx b/sw/source/filter/xml/xmlithlp.cxx new file mode 100644 index 0000000000..d629765d78 --- /dev/null +++ b/sw/source/filter/xml/xmlithlp.cxx @@ -0,0 +1,333 @@ +/* -*- 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 <limits.h> + +#include "xmlithlp.hxx" +#include <sax/tools/converter.hxx> +#include <editeng/borderline.hxx> +#include <editeng/brushitem.hxx> + +#include <xmloff/xmluconv.hxx> +#include <osl/diagnose.h> +#include <o3tl/safeint.hxx> + +#include <com/sun/star/table/BorderLineStyle.hpp> +#include <com/sun/star/text/HoriOrientation.hpp> +#include <com/sun/star/text/VertOrientation.hpp> + +using ::editeng::SvxBorderLine; +using namespace ::xmloff::token; +using namespace ::com::sun::star; + +#define SVX_XML_BORDER_WIDTH_THIN 0 +#define SVX_XML_BORDER_WIDTH_MIDDLE 1 +#define SVX_XML_BORDER_WIDTH_THICK 2 + +const struct SvXMLEnumMapEntry<sal_uInt16> psXML_BorderStyles[] = +{ + { XML_NONE, table::BorderLineStyle::NONE }, + { XML_HIDDEN, table::BorderLineStyle::NONE }, + { XML_SOLID, table::BorderLineStyle::SOLID }, + { XML_DOUBLE, table::BorderLineStyle::DOUBLE }, + { XML_DOUBLE_THIN, table::BorderLineStyle::DOUBLE_THIN }, + { XML_DOTTED, table::BorderLineStyle::DOTTED }, + { XML_DASHED, table::BorderLineStyle::DASHED }, + { XML_FINE_DASHED, table::BorderLineStyle::FINE_DASHED }, + { XML_DASH_DOT, table::BorderLineStyle::DASH_DOT }, + { XML_DASH_DOT_DOT, table::BorderLineStyle::DASH_DOT_DOT }, + { XML_GROOVE, table::BorderLineStyle::ENGRAVED }, + { XML_RIDGE, table::BorderLineStyle::EMBOSSED }, + { XML_INSET, table::BorderLineStyle::INSET }, + { XML_OUTSET, table::BorderLineStyle::OUTSET }, + { XML_TOKEN_INVALID, 0 } +}; + +const struct SvXMLEnumMapEntry<sal_uInt16> psXML_NamedBorderWidths[] = +{ + { XML_THIN, SVX_XML_BORDER_WIDTH_THIN }, + { XML_MIDDLE, SVX_XML_BORDER_WIDTH_MIDDLE }, + { XML_THICK, SVX_XML_BORDER_WIDTH_THICK }, + { XML_TOKEN_INVALID, 0 } +}; +// mapping tables to map external xml input to internal box line widths + +const sal_uInt16 aBorderWidths[] = { + SvxBorderLineWidth::Hairline, + SvxBorderLineWidth::VeryThin, + SvxBorderLineWidth::Thin +}; + +bool sw_frmitems_parseXMLBorder( std::u16string_view rValue, + const SvXMLUnitConverter& rUnitConverter, + bool& rHasStyle, sal_uInt16& rStyle, + bool& rHasWidth, sal_uInt16& rWidth, + sal_uInt16& rNamedWidth, + bool& rHasColor, Color& rColor ) +{ + std::u16string_view aToken; + SvXMLTokenEnumerator aTokens( rValue ); + + rHasStyle = false; + rHasWidth = false; + rHasColor = false; + + rStyle = USHRT_MAX; + rWidth = 0; + rNamedWidth = USHRT_MAX; + + sal_Int32 nTemp; + while( aTokens.getNextToken( aToken ) && !aToken.empty() ) + { + if( !rHasWidth && + SvXMLUnitConverter::convertEnum( rNamedWidth, aToken, + psXML_NamedBorderWidths ) ) + { + rHasWidth = true; + } + else if( !rHasStyle && + SvXMLUnitConverter::convertEnum( rStyle, aToken, + psXML_BorderStyles ) ) + { + rHasStyle = true; + } + else if (!rHasColor && ::sax::Converter::convertColor(rColor, aToken)) + { + rHasColor = true; + } + else if( !rHasWidth && + rUnitConverter.convertMeasureToCore(nTemp, aToken, 0, USHRT_MAX)) + { + rWidth = o3tl::narrowing<sal_uInt16>(nTemp); + rHasWidth = true; + } + else + { + // misformed + return false; + } + } + + return rHasStyle || rHasWidth || rHasColor; +} + +static void sw_frmitems_setXMLBorderStyle( SvxBorderLine& rLine, sal_uInt16 nStyle ) +{ + SvxBorderLineStyle eStyle = SvxBorderLineStyle::NONE; + if ( nStyle != table::BorderLineStyle::NONE ) + eStyle = SvxBorderLineStyle( nStyle ); + rLine.SetBorderLineStyle(eStyle); +} + +bool sw_frmitems_setXMLBorder( std::unique_ptr<SvxBorderLine>& rpLine, + bool bHasStyle, sal_uInt16 nStyle, + bool bHasWidth, sal_uInt16 nWidth, + sal_uInt16 nNamedWidth, + bool bHasColor, const Color& rColor ) +{ + // first of all, delete an empty line + if( (bHasStyle && table::BorderLineStyle::NONE == nStyle) || + (bHasWidth && USHRT_MAX == nNamedWidth && 0 == nWidth) ) + { + bool bRet = nullptr != rpLine; + rpLine.reset(); + return bRet; + } + + // if there is no line and no style and no with, there will never be a line + if( !rpLine && !(bHasStyle && bHasWidth) ) + return false; + + // We now do know that there will be a line + if( !rpLine ) + rpLine.reset(new SvxBorderLine); + + if( ( bHasWidth && + (USHRT_MAX != nNamedWidth || (nWidth != rpLine->GetWidth() ) ) ) || + ( bHasStyle && + ((table::BorderLineStyle::SOLID == nStyle && rpLine->GetDistance()) || + (table::BorderLineStyle::DOUBLE == nStyle && !rpLine->GetDistance())) ) ) + { + bool bDouble = (bHasWidth && table::BorderLineStyle::DOUBLE == nStyle ) || + rpLine->GetDistance(); + + // fdo#38542: for double borders, do not override the width + // set via style:border-line-width{,-left,-right,-top,-bottom} + if (!bDouble || !rpLine->GetWidth()) + { + // The width has to be changed + if (bHasWidth && USHRT_MAX != nNamedWidth) + { + if (bDouble) + { + rpLine->SetBorderLineStyle( SvxBorderLineStyle::DOUBLE ); + } + rpLine->SetWidth( aBorderWidths[nNamedWidth] ); + } + else + { + if (!bHasWidth) + nWidth = rpLine->GetScaledWidth(); + + rpLine->SetWidth( nWidth ); + } + } + sw_frmitems_setXMLBorderStyle( *rpLine, nStyle ); + } + + // set color + if( bHasColor ) + rpLine->SetColor( rColor ); + + return true; +} + +void sw_frmitems_setXMLBorder( std::unique_ptr<SvxBorderLine>& rpLine, + sal_uInt16 nWidth, sal_uInt16 nOutWidth, + sal_uInt16 nInWidth, sal_uInt16 nDistance ) +{ + if( !rpLine ) + rpLine.reset(new SvxBorderLine); + + if( nWidth > 0 ) + rpLine->SetWidth( nWidth ); + else + rpLine->GuessLinesWidths(SvxBorderLineStyle::DOUBLE, + nOutWidth, nInWidth, nDistance); +} + +const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushRepeat[] = +{ + { XML_REPEAT, GPOS_TILED }, + { XML_BACKGROUND_NO_REPEAT, GPOS_MM }, + { XML_STRETCH, GPOS_AREA }, + { XML_TOKEN_INVALID, SvxGraphicPosition(0) } +}; + +const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushHoriPos[] = +{ + { XML_LEFT, GPOS_LM }, + { XML_RIGHT, GPOS_RM }, + { XML_TOKEN_INVALID, SvxGraphicPosition(0) } +}; + +const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushVertPos[] = +{ + { XML_TOP, GPOS_MT }, + { XML_BOTTOM, GPOS_MB }, + { XML_TOKEN_INVALID, SvxGraphicPosition(0) } +}; + +void sw_frmitems_MergeXMLHoriPos( SvxGraphicPosition& ePos, + SvxGraphicPosition eHori ) +{ + OSL_ENSURE( GPOS_LM==eHori || GPOS_MM==eHori || GPOS_RM==eHori, + "sw_frmitems_MergeXMLHoriPos: vertical pos must be middle" ); + + switch( ePos ) + { + case GPOS_LT: + case GPOS_MT: + case GPOS_RT: + ePos = GPOS_LM==eHori ? GPOS_LT : (GPOS_MM==eHori ? GPOS_MT : GPOS_RT); + break; + + case GPOS_LM: + case GPOS_MM: + case GPOS_RM: + ePos = eHori; + break; + + case GPOS_LB: + case GPOS_MB: + case GPOS_RB: + ePos = GPOS_LM==eHori ? GPOS_LB : (GPOS_MM==eHori ? GPOS_MB : GPOS_RB); + break; + default: + ; + } +} + +void sw_frmitems_MergeXMLVertPos( SvxGraphicPosition& ePos, + SvxGraphicPosition eVert ) +{ + OSL_ENSURE( GPOS_MT==eVert || GPOS_MM==eVert || GPOS_MB==eVert, + "sw_frmitems_MergeXMLVertPos: horizontal pos must be middle" ); + + switch( ePos ) + { + case GPOS_LT: + case GPOS_LM: + case GPOS_LB: + ePos = GPOS_MT==eVert ? GPOS_LT : (GPOS_MM==eVert ? GPOS_LM : GPOS_LB); + break; + + case GPOS_MT: + case GPOS_MM: + case GPOS_MB: + ePos = eVert; + break; + + case GPOS_RT: + case GPOS_RM: + case GPOS_RB: + ePos = GPOS_MT==eVert ? GPOS_RT : (GPOS_MM==eVert ? GPOS_RM : GPOS_RB); + break; + default: + ; + } +} + +const struct SvXMLEnumMapEntry<sal_uInt16> psXML_BreakType[] = +{ + { XML_AUTO, 0 }, + { XML_COLUMN, 1 }, + { XML_PAGE, 2 }, + { XML_EVEN_PAGE, 2 }, + { XML_ODD_PAGE, 2 }, + { XML_TOKEN_INVALID, 0} +}; + +const struct SvXMLEnumMapEntry<sal_Int16> aXMLTableAlignMap[] = +{ + { XML_LEFT, text::HoriOrientation::LEFT }, + { XML_LEFT, text::HoriOrientation::LEFT_AND_WIDTH }, + { XML_CENTER, text::HoriOrientation::CENTER }, + { XML_RIGHT, text::HoriOrientation::RIGHT }, + { XML_MARGINS, text::HoriOrientation::FULL }, + { XML_MARGINS, text::HoriOrientation::NONE }, + { XML_TOKEN_INVALID, 0 } +}; + +const struct SvXMLEnumMapEntry<sal_Int16> aXMLTableVAlignMap[] = +{ + { XML_TOP, text::VertOrientation::TOP }, + { XML_MIDDLE, text::VertOrientation::CENTER }, + { XML_BOTTOM, text::VertOrientation::BOTTOM }, + { XML_TOKEN_INVALID, 0 } +}; + +const struct SvXMLEnumMapEntry<sal_uInt16> aXML_KeepTogetherType[] = +{ + { XML_ALWAYS, 0 }, + { XML_AUTO, 1 }, + { XML_TOKEN_INVALID, 0} +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlithlp.hxx b/sw/source/filter/xml/xmlithlp.hxx new file mode 100644 index 0000000000..99d3acd21d --- /dev/null +++ b/sw/source/filter/xml/xmlithlp.hxx @@ -0,0 +1,70 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLITHLP_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLITHLP_HXX + +#include <sal/types.h> +#include <xmloff/xmlement.hxx> +#include <editeng/brushitem.hxx> + +namespace editeng { class SvxBorderLine; } + +template<typename EnumT> struct SvXMLEnumMapEntry; +class SvXMLUnitConverter; +class Color; + +/** Define various helper variables and functions for xmlimpit.cxx and + * xmlexpit.cxx. */ +bool sw_frmitems_parseXMLBorder( std::u16string_view rValue, + const SvXMLUnitConverter& rUnitConverter, + bool& rHasStyle, sal_uInt16& rStyle, + bool& rHasWidth, sal_uInt16& rWidth, + sal_uInt16& rNamedWidth, + bool& rHasColor, Color& rColor ); + +bool sw_frmitems_setXMLBorder( std::unique_ptr<editeng::SvxBorderLine>& rpLine, + bool bHasStyle, sal_uInt16 nStyle, + bool bHasWidth, sal_uInt16 nWidth, + sal_uInt16 nNamedWidth, + bool bHasColor, const Color& rColor ); + +void sw_frmitems_setXMLBorder( std::unique_ptr<editeng::SvxBorderLine>& rpLine, + sal_uInt16 nWidth, sal_uInt16 nOutWidth, + sal_uInt16 nInWidth, sal_uInt16 nDistance ); + +void sw_frmitems_MergeXMLHoriPos( SvxGraphicPosition& ePos, + SvxGraphicPosition eHori ); + +void sw_frmitems_MergeXMLVertPos( SvxGraphicPosition& ePos, + SvxGraphicPosition eVert ); + +extern const struct SvXMLEnumMapEntry<sal_uInt16> psXML_BorderStyles[]; +extern const struct SvXMLEnumMapEntry<sal_uInt16> psXML_NamedBorderWidths[]; +extern const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushRepeat[]; +extern const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushHoriPos[]; +extern const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushVertPos[]; +extern const struct SvXMLEnumMapEntry<sal_uInt16> psXML_BreakType[]; +extern const struct SvXMLEnumMapEntry<sal_Int16> aXMLTableAlignMap[]; +extern const struct SvXMLEnumMapEntry<sal_Int16> aXMLTableVAlignMap[]; +extern const struct SvXMLEnumMapEntry<sal_uInt16> aXML_KeepTogetherType[]; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlitmap.hxx b/sw/source/filter/xml/xmlitmap.hxx new file mode 100644 index 0000000000..5c7f731cc1 --- /dev/null +++ b/sw/source/filter/xml/xmlitmap.hxx @@ -0,0 +1,87 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLITMAP_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLITMAP_HXX + +#include <sal/types.h> +#include <tools/ref.hxx> +#include <xmloff/xmltoken.hxx> +#include <memory> +#include <span> + +#define MID_SW_FLAG_MASK 0x0000ffff + +// this flags are used in the item mapper for import and export +#define MID_SW_FLAG_SPECIAL_ITEM_IMPORT 0x80000000 +#define MID_SW_FLAG_NO_ITEM_IMPORT 0x40000000 +#define MID_SW_FLAG_SPECIAL_ITEM_EXPORT 0x20000000 +#define MID_SW_FLAG_NO_ITEM_EXPORT 0x10000000 +#define MID_SW_FLAG_ELEMENT_ITEM_IMPORT 0x08000000 +#define MID_SW_FLAG_ELEMENT_ITEM_EXPORT 0x04000000 +#define MID_SW_FLAG_ELEMENT_ITEM 0x0c000000 // both import and export + +struct SvXMLItemMapEntry +{ + sal_uInt16 nNameSpace; // declares the Namespace in which this item + // exists + sal_uInt16 nWhichId; // the WhichId to identify the item + // in the pool + enum ::xmloff::token::XMLTokenEnum const eLocalName; + // the local name for the item inside + // the Namespace (as an XMLTokenEnum) + sal_uInt32 nMemberId; // the memberid specifies which part + // of the item should be imported or + // exported with this Namespace + // and localName + SvXMLItemMapEntry( + sal_uInt16 nameSpace, + enum ::xmloff::token::XMLTokenEnum localName, + sal_uInt16 whichId, + sal_uInt32 memberId) + : nNameSpace(nameSpace), nWhichId(whichId), eLocalName(localName), nMemberId(memberId) {} +}; + +/** this class manages an array of SvXMLItemMapEntry. It is + used for optimizing the static array on startup of import + or export */ +class SvXMLItemMapEntries final : public SvRefBase +{ + std::span<SvXMLItemMapEntry const> mpEntries; + +public: + explicit SvXMLItemMapEntries(std::span<SvXMLItemMapEntry const> pEntries) : mpEntries(pEntries) {} + virtual ~SvXMLItemMapEntries() override; + + SvXMLItemMapEntry const * getByName( sal_Int32 nElement ) const; + SvXMLItemMapEntry const & getByIndex( sal_uInt16 nIndex ) const { return mpEntries[nIndex]; } + + sal_uInt16 getCount() const { return mpEntries.size(); } +}; + +typedef tools::SvRef<SvXMLItemMapEntries> SvXMLItemMapEntriesRef; + +extern const std::span<SvXMLItemMapEntry const> aXMLTableItemMap; +extern const std::span<SvXMLItemMapEntry const> aXMLTableColItemMap; +extern const std::span<SvXMLItemMapEntry const> aXMLTableRowItemMap; +extern const std::span<SvXMLItemMapEntry const> aXMLTableCellItemMap; + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLITMAP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlitmpr.cxx b/sw/source/filter/xml/xmlitmpr.cxx new file mode 100644 index 0000000000..e7cf7ec90d --- /dev/null +++ b/sw/source/filter/xml/xmlitmpr.cxx @@ -0,0 +1,39 @@ +/* -*- 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 "xmlitmap.hxx" +#include <xmloff/xmlimp.hxx> + +SvXMLItemMapEntries::~SvXMLItemMapEntries() +{ +} + +SvXMLItemMapEntry const * SvXMLItemMapEntries::getByName( sal_Int32 nElement ) const +{ + for (const SvXMLItemMapEntry& rEntry : mpEntries) + { + if( IsTokenInNamespace(nElement, rEntry.nNameSpace) && + (nElement & TOKEN_MASK) == rEntry.eLocalName ) + return &rEntry; + } + + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlmeta.cxx b/sw/source/filter/xml/xmlmeta.cxx new file mode 100644 index 0000000000..925fea5d9a --- /dev/null +++ b/sw/source/filter/xml/xmlmeta.cxx @@ -0,0 +1,175 @@ +/* -*- 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 <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <osl/diagnose.h> +#include <xmloff/xmlmetai.hxx> +#include <xmloff/ProgressBarHelper.hxx> +#include <xmloff/xmltkmap.hxx> +#include <o3tl/safeint.hxx> +#include <xmloff/xmluconv.hxx> +#include <docstat.hxx> +#include <doc.hxx> +#include <IDocumentStatistics.hxx> +#include "xmlimp.hxx" +#include "xmlexp.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::text; +using namespace ::xmloff::token; + +uno::Reference<document::XDocumentProperties> +SwXMLImport::GetDocumentProperties() const +{ + if (m_bOrganizerMode || IsStylesOnlyMode() || + IsBlockMode() || IsInsertMode()) + { + return nullptr; + } + uno::Reference<document::XDocumentPropertiesSupplier> const xDPS( + GetModel(), UNO_QUERY_THROW); + return xDPS->getDocumentProperties(); +} + +SvXMLImportContext *SwXMLImport::CreateMetaContext( + const sal_Int32 /*nElement*/ ) +{ + SvXMLImportContext *pContext = nullptr; + + if (getImportFlags() & SvXMLImportFlags::META) + { + uno::Reference<document::XDocumentProperties> const xDocProps( + GetDocumentProperties()); + pContext = new SvXMLMetaDocumentContext(*this, xDocProps); + } + + return pContext; +} + +namespace { + +enum SvXMLTokenMapAttrs +{ + XML_TOK_META_STAT_TABLE = 1, + XML_TOK_META_STAT_IMAGE = 2, + XML_TOK_META_STAT_OLE = 4, + XML_TOK_META_STAT_PAGE = 8, + XML_TOK_META_STAT_PARA = 16, + XML_TOK_META_STAT_WORD = 32, + XML_TOK_META_STAT_CHAR = 64, + XML_TOK_META_STAT_NON_WHITE_SPACE_CHAR = 128, + XML_TOK_META_STAT_END=XML_TOK_UNKNOWN +}; + +struct statistic { + SvXMLTokenMapAttrs token; + const char* name; + sal_uInt16 SwDocStat::* target16; + sal_uLong SwDocStat::* target32; /* or 64, on LP64 platforms */ +}; + +} + +const struct statistic s_stats [] = { + { XML_TOK_META_STAT_TABLE, "TableCount", &SwDocStat::nTable, nullptr }, + { XML_TOK_META_STAT_IMAGE, "ImageCount", &SwDocStat::nGrf, nullptr }, + { XML_TOK_META_STAT_OLE, "ObjectCount", &SwDocStat::nOLE, nullptr }, + { XML_TOK_META_STAT_PAGE, "PageCount", nullptr, &SwDocStat::nPage }, + { XML_TOK_META_STAT_PARA, "ParagraphCount", nullptr, &SwDocStat::nPara }, + { XML_TOK_META_STAT_WORD, "WordCount", nullptr, &SwDocStat::nWord }, + { XML_TOK_META_STAT_CHAR, "CharacterCount", nullptr, &SwDocStat::nChar }, + { XML_TOK_META_STAT_NON_WHITE_SPACE_CHAR, "NonWhitespaceCharacterCount", nullptr, &SwDocStat::nCharExcludingSpaces }, + { XML_TOK_META_STAT_END, nullptr, nullptr, nullptr } +}; + +void SwXMLImport::SetStatistics( + const Sequence< beans::NamedValue > & i_rStats) +{ + if( IsStylesOnlyMode() || IsInsertMode() ) + return; + + SvXMLImport::SetStatistics(i_rStats); + + SwDoc *pDoc = getDoc(); + SwDocStat aDocStat( pDoc->getIDocumentStatistics().GetDocStat() ); + + sal_uInt32 nTokens = 0; + + for (const auto& rStat : i_rStats) { + for (struct statistic const* pStat = s_stats; pStat->name != nullptr; + ++pStat) { + if (rStat.Name.equalsAscii(pStat->name)) { + sal_Int32 val = 0; + if (rStat.Value >>= val) { + if (pStat->target16 != nullptr) { + aDocStat.*(pStat->target16) + = o3tl::narrowing<sal_uInt16> (val); + } else { + aDocStat.*(pStat->target32) + = static_cast<sal_uInt32> (val); + } + nTokens |= pStat->token; + } else { + OSL_FAIL("SwXMLImport::SetStatistics: invalid entry"); + } + } + } + } + + if( nTokens ) + pDoc->getIDocumentStatistics().SetDocStat( aDocStat ); + + // set progress bar reference to #paragraphs. If not available, + // use #pages*10, or guesstimate 250 paragraphs. Additionally + // guesstimate PROGRESS_BAR_STEPS each for meta+settings, styles, + // and autostyles. + bool bSetFallback = true; + sal_Int32 nProgressReference = sal_Int32(); // silence C4701 + const sal_Int32 nProgressReferenceWriggleRoom = 3 * PROGRESS_BAR_STEP; + if (nTokens & XML_TOK_META_STAT_PARA) + { + nProgressReference = static_cast<sal_Int32>(aDocStat.nPara); + bSetFallback = false; + } + else if (nTokens & XML_TOK_META_STAT_PAGE) + bSetFallback = o3tl::checked_multiply<sal_Int32>(aDocStat.nPage, 10, nProgressReference); + if (!bSetFallback) + bSetFallback = o3tl::checked_add(nProgressReference, nProgressReferenceWriggleRoom, nProgressReference); + if (bSetFallback) + nProgressReference = 250 + nProgressReferenceWriggleRoom; + ProgressBarHelper* pProgress = GetProgressBarHelper(); + pProgress->SetReference(nProgressReference); + pProgress->SetValue( 0 ); +} + +void SwXMLExport::ExportMeta_() +{ + SvXMLExport::ExportMeta_(); + + if( !m_bBlock && IsShowProgress() ) + { + ProgressBarHelper *pProgress = GetProgressBarHelper(); + pProgress->SetValue( pProgress->GetValue() + 2 ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlscript.cxx b/sw/source/filter/xml/xmlscript.cxx new file mode 100644 index 0000000000..07a94e8a5a --- /dev/null +++ b/sw/source/filter/xml/xmlscript.cxx @@ -0,0 +1,37 @@ +/* -*- 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 <xmloff/xmlscripti.hxx> +#include "xmlimp.hxx" + +using namespace ::com::sun::star; + +SvXMLImportContext* SwXMLImport::CreateScriptContext() +{ + SvXMLImportContext* pContext = nullptr; + + if (!(IsStylesOnlyMode() || IsInsertMode())) + { + pContext = new XMLScriptContext(*this, GetModel()); + } + + return pContext; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltble.cxx b/sw/source/filter/xml/xmltble.cxx new file mode 100644 index 0000000000..1341bbbe37 --- /dev/null +++ b/sw/source/filter/xml/xmltble.cxx @@ -0,0 +1,1266 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <com/sun/star/text/XTextTable.hpp> +#include <com/sun/star/text/XTextSection.hpp> + +#include <hintids.hxx> +#include <rtl/ustrbuf.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/numehelp.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/prntitem.hxx> +#include <editeng/xmlcnitm.hxx> +#include <fmtrowsplt.hxx> +#include <editeng/frmdiritem.hxx> +#include <swtable.hxx> +#include <doc.hxx> +#include <frmfmt.hxx> +#include <wrtswtbl.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <cellatr.hxx> +#include <ddefld.hxx> +#include <swddetbl.hxx> +#include <xmloff/namespacemap.hxx> +#include <sfx2/linkmgr.hxx> +#include <unotbl.hxx> +#include "xmltexte.hxx" +#include "xmlexp.hxx" +#include <o3tl/any.hxx> +#include <o3tl/sorted_vector.hxx> +#include <textboxhelper.hxx> +#include <SwStyleNameMapper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::xmloff::token; +using table::XCell; +using std::vector; +using std::advance; + + +class SwXMLTableColumn_Impl : public SwWriteTableCol +{ + OUString m_sStyleName; + sal_uInt32 m_nRelWidth; + +public: + + explicit SwXMLTableColumn_Impl(sal_uInt32 nPosition) + : SwWriteTableCol(nPosition) + , m_nRelWidth(0) + {}; + + void SetStyleName( const OUString& rName ) { m_sStyleName = rName; } + const OUString& GetStyleName() const { return m_sStyleName; } + + void SetRelWidth( sal_uInt32 nSet ) { m_nRelWidth = nSet; } + sal_uInt32 GetRelWidth() const { return m_nRelWidth; } +}; + +namespace { + +struct SwXMLTableColumnCmpWidth_Impl +{ + bool operator()( SwXMLTableColumn_Impl* const& lhs, SwXMLTableColumn_Impl* const& rhs ) const + { + sal_Int32 n = static_cast<sal_Int32>(lhs->GetWidthOpt()) - static_cast<sal_Int32>(rhs->GetWidthOpt()); + if( !n ) + n = static_cast<sal_Int32>(lhs->GetRelWidth()) - static_cast<sal_Int32>(rhs->GetRelWidth()); + return n < 0; + } +}; + +class SwXMLTableColumns_Impl : public o3tl::sorted_vector<std::unique_ptr<SwXMLTableColumn_Impl>, o3tl::less_uniqueptr_to<SwXMLTableColumn_Impl> > { +}; + +} + +class SwXMLTableColumnsSortByWidth_Impl : public o3tl::sorted_vector<SwXMLTableColumn_Impl*, SwXMLTableColumnCmpWidth_Impl> {}; + +class SwXMLTableLines_Impl +{ + SwXMLTableColumns_Impl m_aCols; + const SwTableLines *m_pLines; + sal_uInt32 m_nWidth; + +public: + + explicit SwXMLTableLines_Impl( const SwTableLines& rLines ); + + sal_uInt32 GetWidth() const { return m_nWidth; } + const SwTableLines *GetLines() const { return m_pLines; } + + const SwXMLTableColumns_Impl& GetColumns() const { return m_aCols; } +}; + +SwXMLTableLines_Impl::SwXMLTableLines_Impl( const SwTableLines& rLines ) : + m_pLines( &rLines ), + m_nWidth( 0 ) +{ +#if OSL_DEBUG_LEVEL > 0 + sal_uInt32 nEndCPos = 0U; +#endif + const size_t nLines = rLines.size(); + for( size_t nLine=0U; nLine<nLines; ++nLine ) + { + const SwTableLine *pLine = rLines[nLine]; + const SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + const size_t nBoxes = rBoxes.size(); + + sal_uInt32 nCPos = 0U; + for( size_t nBox=0U; nBox<nBoxes; ++nBox ) + { + const SwTableBox *pBox = rBoxes[nBox]; + + if( nBox < nBoxes-1U || m_nWidth==0 ) + { + nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox ); + std::unique_ptr<SwXMLTableColumn_Impl> pCol( + new SwXMLTableColumn_Impl( nCPos )); + + m_aCols.insert( std::move(pCol) ); + + if( nBox==nBoxes-1U ) + { + OSL_ENSURE( nLine==0U && m_nWidth==0, + "parent width will be lost" ); + m_nWidth = nCPos; + } + } + else + { +#if OSL_DEBUG_LEVEL > 0 + sal_uInt32 nCheckPos = + nCPos + SwWriteTable::GetBoxWidth( pBox ); + if( !nEndCPos ) + { + nEndCPos = nCheckPos; + } +#endif + nCPos = m_nWidth; +#if OSL_DEBUG_LEVEL > 0 + SwXMLTableColumn_Impl aCol( m_nWidth ); + OSL_ENSURE( m_aCols.find(&aCol) != m_aCols.end(), "couldn't find last column" ); + OSL_ENSURE( SwXMLTableColumn_Impl(nCheckPos) == + SwXMLTableColumn_Impl(nCPos), + "rows have different total widths" ); +#endif + } + } + } +} + +typedef vector< SwFrameFormat* > SwXMLFrameFormats_Impl; + +class SwXMLTableFrameFormatsSort_Impl +{ +private: + SwXMLFrameFormats_Impl m_aFormatList; + SwXMLTextParagraphExport::FormatMap & m_rFormatMap; + +public: + SwXMLTableFrameFormatsSort_Impl(SwXMLTextParagraphExport::FormatMap & rFormatMap) + : m_rFormatMap(rFormatMap) + {} + ::std::optional<OUString> AddRow(SwFrameFormat& rFrameFormat, std::u16string_view rNamePrefix, sal_uInt32 nLine ); + ::std::optional<OUString> AddCell(SwFrameFormat& rFrameFormat, std::u16string_view rNamePrefix, + sal_uInt32 nCol, sal_uInt32 nRow, bool bTop ); +}; + +::std::optional<OUString> SwXMLTableFrameFormatsSort_Impl::AddRow(SwFrameFormat& rFrameFormat, + std::u16string_view rNamePrefix, + sal_uInt32 nLine ) +{ + const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet(); + + const SwFormatFrameSize *pFrameSize = rItemSet.GetItemIfSet( RES_FRM_SIZE, false ); + const SwFormatRowSplit* pRowSplit = rItemSet.GetItemIfSet( RES_ROW_SPLIT, false ); + const SvxBrushItem *pBrush = rItemSet.GetItemIfSet( RES_BACKGROUND, false ); + const SvxPrintItem *pHasTextChangesOnly = rItemSet.GetItemIfSet( RES_PRINT, false); + + // empty styles have not to be exported + if( !pFrameSize && !pBrush && !pRowSplit && !pHasTextChangesOnly ) + { + m_rFormatMap.try_emplace(&rFrameFormat); // empty just to enable assert + return {}; + } + + // order is: -/brush, size/-, size/brush + SwXMLFrameFormats_Impl::iterator i; + for( i = m_aFormatList.begin(); i < m_aFormatList.end(); ++i ) + { + const SwFormatFrameSize *pTestFrameSize = nullptr; + const SwFormatRowSplit* pTestRowSplit = nullptr; + const SvxBrushItem *pTestBrush = nullptr; + const SvxPrintItem *pTestHasTextChangesOnly = nullptr; + const SwFrameFormat *pTestFormat = *i; + const SfxItemSet& rTestSet = pTestFormat->GetAttrSet(); + if( const SwFormatFrameSize* pItem = rTestSet.GetItemIfSet( RES_FRM_SIZE, false ) ) + { + if( !pFrameSize ) + break; + + pTestFrameSize = pItem; + } + else + { + if( pFrameSize ) + continue; + } + + if( const SvxBrushItem* pItem = rTestSet.GetItemIfSet( RES_BACKGROUND, false) ) + { + if( !pBrush ) + break; + + pTestBrush = pItem; + } + else + { + if( pBrush ) + continue; + } + + if( const SwFormatRowSplit* pItem = rTestSet.GetItemIfSet( RES_ROW_SPLIT, false ) ) + { + if( !pRowSplit ) + break; + + pTestRowSplit = pItem; + } + else + { + if( pRowSplit ) + continue; + } + + if( const SvxPrintItem* pItem = rTestSet.GetItemIfSet( RES_PRINT, false ) ) + { + if( !pHasTextChangesOnly ) + break; + + pTestHasTextChangesOnly = pItem; + } + else + { + if( pHasTextChangesOnly ) + continue; + } + + if( pFrameSize && + ( pFrameSize->GetHeightSizeType() != pTestFrameSize->GetHeightSizeType() || + pFrameSize->GetHeight() != pTestFrameSize->GetHeight() ) ) + continue; + + if( pBrush && (*pBrush != *pTestBrush) ) + continue; + + if( pRowSplit && (!pRowSplit->GetValue() != !pTestRowSplit->GetValue()) ) + continue; + + if( pHasTextChangesOnly && (!pHasTextChangesOnly->GetValue() != !pTestHasTextChangesOnly->GetValue()) ) + continue; + + // found! + auto const oName(m_rFormatMap.find(pTestFormat)->second); + assert(oName); + m_rFormatMap.try_emplace(&rFrameFormat, oName); + return {}; + } + + { + OUString const name(OUString::Concat(rNamePrefix) + "." + OUString::number(nLine+1)); + m_rFormatMap.try_emplace(&rFrameFormat, name); + if ( i != m_aFormatList.end() ) ++i; + m_aFormatList.insert( i, &rFrameFormat ); + return ::std::optional<OUString>(name); + } +} + +static OUString lcl_xmltble_appendBoxPrefix(std::u16string_view rNamePrefix, + sal_uInt32 nCol, sal_uInt32 nRow, bool bTop ) +{ + if( bTop ) + { + OUString sTmp; + sw_GetTableBoxColStr( o3tl::narrowing<sal_uInt16>(nCol), sTmp ); + return OUString::Concat(rNamePrefix) + "." + sTmp + OUString::number(nRow + 1); + } + return OUString::Concat(rNamePrefix) + + "." + OUString::number(nCol + 1) + + "." + OUString::number(nRow + 1); +} + +::std::optional<OUString> SwXMLTableFrameFormatsSort_Impl::AddCell(SwFrameFormat& rFrameFormat, + std::u16string_view rNamePrefix, + sal_uInt32 nCol, sal_uInt32 nRow, bool bTop ) +{ + const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet(); + const SwFormatVertOrient *pVertOrient = rItemSet.GetItemIfSet( RES_VERT_ORIENT, false ); + const SvxBrushItem *pBrush = rItemSet.GetItemIfSet( RES_BACKGROUND, false ); + const SvxBoxItem *pBox = rItemSet.GetItemIfSet( RES_BOX, false ); + const SwTableBoxNumFormat *pNumFormat = rItemSet.GetItemIfSet( RES_BOXATR_FORMAT, + false ); + const SvxFrameDirectionItem *pFrameDir = rItemSet.GetItemIfSet( RES_FRAMEDIR, + false ); + const SvXMLAttrContainerItem *pAttCnt = rItemSet.GetItemIfSet( RES_UNKNOWNATR_CONTAINER, + false ); + const SvxPrintItem *pHasTextChangesOnly = rItemSet.GetItemIfSet( RES_PRINT, false); + + // empty styles have not to be exported + if( !pVertOrient && !pBrush && !pBox && !pNumFormat && !pFrameDir && !pAttCnt && + !pHasTextChangesOnly ) + { + m_rFormatMap.try_emplace(&rFrameFormat); // empty just to enable assert + return {}; + } + + // order is: -/-/-/num, + // -/-/box/-, -/-/box/num, + // -/brush/-/-, -/brush/-/num, -/brush/box/-, -/brush/box/num, + // vert/-/-/-, vert/-/-/num, vert/-/box/-, ver/-/box/num, + // vert/brush/-/-, vert/brush/-/num, vert/brush/box/-, + // vert/brush/box/num + SwXMLFrameFormats_Impl::iterator i; + for( i = m_aFormatList.begin(); i < m_aFormatList.end(); ++i ) + { + const SwFormatVertOrient *pTestVertOrient = nullptr; + const SvxBrushItem *pTestBrush = nullptr; + const SvxBoxItem *pTestBox = nullptr; + const SwTableBoxNumFormat *pTestNumFormat = nullptr; + const SvxFrameDirectionItem *pTestFrameDir = nullptr; + const SvXMLAttrContainerItem *pTestAttCnt = nullptr; + const SvxPrintItem *pTestHasTextChangesOnly = rItemSet.GetItemIfSet( RES_PRINT, false); + const SwFrameFormat* pTestFormat = *i; + const SfxItemSet& rTestSet = pTestFormat->GetAttrSet(); + if( const SwFormatVertOrient* pItem = rTestSet.GetItemIfSet( RES_VERT_ORIENT, false ) ) + { + if( !pVertOrient ) + break; + + pTestVertOrient = pItem; + } + else + { + if( pVertOrient ) + continue; + } + + if( const SvxBrushItem* pItem = rTestSet.GetItemIfSet( RES_BACKGROUND, false ) ) + { + if( !pBrush ) + break; + + pTestBrush = pItem; + } + else + { + if( pBrush ) + continue; + } + + if( const SvxBoxItem* pItem = rTestSet.GetItemIfSet( RES_BOX, false ) ) + { + if( !pBox ) + break; + + pTestBox = pItem; + } + else + { + if( pBox ) + continue; + } + + if ( const SwTableBoxNumFormat* pItem = rTestSet.GetItemIfSet( RES_BOXATR_FORMAT, + false ) ) + { + if( !pNumFormat ) + break; + + pTestNumFormat = pItem; + } + else + { + if( pNumFormat ) + continue; + + } + + if ( const SvxFrameDirectionItem* pItem = rTestSet.GetItemIfSet( RES_FRAMEDIR, + false ) ) + { + if( !pFrameDir ) + break; + + pTestFrameDir = pItem; + } + else + { + if( pFrameDir ) + continue; + + } + + if ( const SvXMLAttrContainerItem* pItem = rTestSet.GetItemIfSet( RES_UNKNOWNATR_CONTAINER, + false ) ) + { + if( !pAttCnt ) + break; + + pTestAttCnt = pItem; + } + else + { + if ( pAttCnt ) + continue; + + } + + if( const SvxPrintItem* pItem = rTestSet.GetItemIfSet( RES_PRINT, false ) ) + { + if( !pHasTextChangesOnly ) + break; + + pTestHasTextChangesOnly = pItem; + } + else + { + if( pHasTextChangesOnly ) + continue; + } + + if( pVertOrient && + pVertOrient->GetVertOrient() != pTestVertOrient->GetVertOrient() ) + continue; + + if( pBrush && ( *pBrush != *pTestBrush ) ) + continue; + + if( pBox && ( *pBox != *pTestBox ) ) + continue; + + if( pNumFormat && pNumFormat->GetValue() != pTestNumFormat->GetValue() ) + continue; + + if( pFrameDir && pFrameDir->GetValue() != pTestFrameDir->GetValue() ) + continue; + + if( pAttCnt && ( *pAttCnt != *pTestAttCnt ) ) + continue; + + if( pHasTextChangesOnly && (!pHasTextChangesOnly->GetValue() != !pTestHasTextChangesOnly->GetValue()) ) + continue; + + // found! + auto const oName(m_rFormatMap.find(pTestFormat)->second); + assert(oName); + m_rFormatMap.try_emplace(&rFrameFormat, oName); + return {}; + } + + { + OUString const name(lcl_xmltble_appendBoxPrefix(rNamePrefix, nCol, nRow, bTop)); + m_rFormatMap.try_emplace(&rFrameFormat, name); + if ( i != m_aFormatList.end() ) ++i; + m_aFormatList.insert( i, &rFrameFormat ); + return ::std::optional<OUString>(name); + } +} + +class SwXMLTableInfo_Impl +{ + const SwTable *m_pTable; + Reference<XTextSection> m_xBaseSection; + bool m_bBaseSectionValid; + sal_uInt32 m_nPrefix; + SwXMLTextParagraphExport::FormatMap const& m_rLineFormats; + SwXMLTextParagraphExport::FormatMap const& m_rBoxFormats; + +public: + + inline SwXMLTableInfo_Impl( const SwTable *pTable, sal_uInt16 nPrefix, + SwXMLTextParagraphExport::FormatMap const& rLineFormats, + SwXMLTextParagraphExport::FormatMap const& rBoxFormats) + : m_pTable(pTable) + , m_bBaseSectionValid(false) + , m_nPrefix(nPrefix) + , m_rLineFormats(rLineFormats) + , m_rBoxFormats(rBoxFormats) + { + } + + const SwTable *GetTable() const { return m_pTable; } + const SwFrameFormat *GetTableFormat() const { return m_pTable->GetFrameFormat(); } + + bool IsBaseSectionValid() const { return m_bBaseSectionValid; } + const Reference<XTextSection>& GetBaseSection() const { return m_xBaseSection; } + inline void SetBaseSection( const Reference < XTextSection >& rBase ); + /// The namespace (table or loext) that should be used for the elements. + sal_uInt16 GetPrefix() const { return m_nPrefix; } + SwXMLTextParagraphExport::FormatMap const& GetLineFormats() const { return m_rLineFormats; } + SwXMLTextParagraphExport::FormatMap const& GetBoxFormats() const { return m_rBoxFormats; } +}; + +inline void SwXMLTableInfo_Impl::SetBaseSection( + const Reference < XTextSection >& rBaseSection ) +{ + m_xBaseSection = rBaseSection; + m_bBaseSectionValid = true; +} + +void SwXMLExport::ExportTableColumnStyle( const SwXMLTableColumn_Impl& rCol ) +{ + // <style:style ...> + CheckAttrList(); + + // style:name="..." + bool bEncoded = false; + AddAttribute( XML_NAMESPACE_STYLE, XML_NAME, + EncodeStyleName( rCol.GetStyleName(), &bEncoded ) ); + if( bEncoded ) + AddAttribute( XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, rCol.GetStyleName() ); + + // style:family="table-column" + AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, XML_TABLE_COLUMN ); + + { + SvXMLElementExport aElem( *this, XML_NAMESPACE_STYLE, XML_STYLE, true, + true ); + if( rCol.GetWidthOpt() ) + { + OUStringBuffer sValue; + GetTwipUnitConverter().convertMeasureToXML( sValue, + rCol.GetWidthOpt() ); + AddAttribute( XML_NAMESPACE_STYLE, XML_COLUMN_WIDTH, + sValue.makeStringAndClear() ); + } + if( rCol.GetRelWidth() ) + { + OUString sValue = OUString::number(static_cast<sal_Int32>(rCol.GetRelWidth()) ) + "*"; + AddAttribute( XML_NAMESPACE_STYLE, XML_REL_COLUMN_WIDTH, + sValue ); + } + + { + SvXMLElementExport aElemExport( *this, XML_NAMESPACE_STYLE, + XML_TABLE_COLUMN_PROPERTIES, + true, true ); + } + } +} + +void SwXMLExport::ExportTableLinesAutoStyles( const SwTableLines& rLines, + sal_uInt32 nAbsWidth, sal_uInt32 nBaseWidth, + std::u16string_view rNamePrefix, + SwXMLTableColumnsSortByWidth_Impl& rExpCols, + SwXMLTableFrameFormatsSort_Impl& rExpRows, + SwXMLTableFrameFormatsSort_Impl& rExpCells, + SwXMLTableInfo_Impl& rTableInfo, + bool bTop ) +{ + // pass 1: calculate columns + SwXMLTableLines_Impl *pLines = new SwXMLTableLines_Impl( rLines ); + if( !m_pTableLines ) + m_pTableLines.reset(new SwXMLTableLinesCache_Impl); + + m_pTableLines->push_back( pLines ); + + // pass 2: export column styles + { + const SwXMLTableColumns_Impl& rCols = pLines->GetColumns(); + sal_uInt32 nCPos = 0U; + const size_t nColumns = rCols.size(); + for( size_t nColumn=0U; nColumn<nColumns; ++nColumn ) + { + SwXMLTableColumn_Impl *pColumn = rCols[nColumn].get(); + + sal_uInt32 nOldCPos = nCPos; + nCPos = pColumn->GetPos(); + + sal_uInt32 nWidth = nCPos - nOldCPos; + + // If a base width is given, the table has either an automatic + // or margin alignment, or a percentage width. In either case, + // relative widths should be exported. + if( nBaseWidth ) + { + pColumn->SetRelWidth( nWidth ); + } + + // If an absolute width is given, the table either has a fixed + // width, or the current width is known from the layout. In the + // later case, a base width is set in addition and must be used + // to "absolutize" the relative column width. + if( nAbsWidth ) + { + sal_uInt32 nColAbsWidth = nWidth; + if( nBaseWidth ) + { + nColAbsWidth *= nAbsWidth; + nColAbsWidth += (nBaseWidth/2UL); + nColAbsWidth /= nBaseWidth; + } + pColumn->SetWidthOpt( nColAbsWidth, false ); + } + + SwXMLTableColumnsSortByWidth_Impl::const_iterator it = rExpCols.find( pColumn ); + if( it != rExpCols.end() ) + { + pColumn->SetStyleName( (*it)->GetStyleName() ); + } + else + { + if( bTop ) + { + OUString sTmp; + sw_GetTableBoxColStr( nColumn, sTmp ); + pColumn->SetStyleName( OUString::Concat(rNamePrefix) + "." + sTmp ); + } + else + { + pColumn->SetStyleName( + OUString::Concat(rNamePrefix) + "." + OUString::number(nColumn + 1U) ); + } + ExportTableColumnStyle( *pColumn ); + rExpCols.insert( pColumn ); + } + } + } + + // pass 3: export line/rows + const size_t nLines = rLines.size(); + for( size_t nLine=0U; nLine<nLines; ++nLine ) + { + SwTableLine *pLine = rLines[nLine]; + + SwFrameFormat *pFrameFormat = pLine->GetFrameFormat(); + if (auto oNew = rExpRows.AddRow(*pFrameFormat, rNamePrefix, nLine)) + { + ExportFormat(*pFrameFormat, XML_TABLE_ROW, std::move(oNew)); + } + + const SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + const size_t nBoxes = rBoxes.size(); + + sal_uInt32 nCPos = 0U; + size_t nCol = 0U; + for( size_t nBox=0U; nBox<nBoxes; nBox++ ) + { + SwTableBox *pBox = rBoxes[nBox]; + + if( nBox < nBoxes-1U ) + nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox ); + else + nCPos = pLines->GetWidth(); + + // and their index + const size_t nOldCol = nCol; + SwXMLTableColumn_Impl aCol( nCPos ); + SwXMLTableColumns_Impl::const_iterator it = pLines->GetColumns().find( &aCol ); + OSL_ENSURE( it != pLines->GetColumns().end(), "couldn't find column" ); + nCol = it - pLines->GetColumns().begin(); + + const SwStartNode *pBoxSttNd = pBox->GetSttNd(); + if( pBoxSttNd ) + { + SwFrameFormat *pFrameFormat2 = pBox->GetFrameFormat(); + if (auto oNew = rExpCells.AddCell(*pFrameFormat2, rNamePrefix, nOldCol, nLine, + bTop) ) + { + ExportFormat(*pFrameFormat2, XML_TABLE_CELL, std::move(oNew)); + } + + rtl::Reference < SwXCell > xCell = SwXCell::CreateXCell( + const_cast<SwFrameFormat *>(rTableInfo.GetTableFormat()), + pBox, + const_cast<SwTable *>(rTableInfo.GetTable()) ); + if (xCell.is()) + { + if( !rTableInfo.IsBaseSectionValid() ) + { + Any aAny = xCell->getPropertyValue("TextSection"); + Reference < XTextSection > xTextSection; + aAny >>= xTextSection; + rTableInfo.SetBaseSection( xTextSection ); + } + + const bool bExportContent = bool(getExportFlags() & SvXMLExportFlags::CONTENT ); + if ( !bExportContent ) + { + // AUTOSTYLES - not needed anymore if we are currently exporting content.xml + GetTextParagraphExport()->collectTextAutoStyles( + xCell, rTableInfo.GetBaseSection(), IsShowProgress() ); + } + } + else { + OSL_FAIL("here should be a XCell"); + } + } + else + { + ExportTableLinesAutoStyles( pBox->GetTabLines(), + nAbsWidth, nBaseWidth, + lcl_xmltble_appendBoxPrefix( rNamePrefix, + nOldCol, nLine, bTop ), + rExpCols, rExpRows, rExpCells, + rTableInfo ); + } + + nCol++; + } + } +} + +void SwXMLExport::ExportTableAutoStyles(const SwTableNode& rTableNd) +{ + auto & rFormats(static_cast<SwXMLTextParagraphExport *>(GetTextParagraphExport().get())->GetTableFormats()); + auto const it(rFormats.find(&rTableNd)); + assert(it != rFormats.end()); + SwXMLTextParagraphExport::FormatMap & rRowFormats(it->second.first); + SwXMLTextParagraphExport::FormatMap & rBoxFormats(it->second.second); + const SwTable& rTable = rTableNd.GetTable(); + const SwFrameFormat *pTableFormat = rTable.GetFrameFormat(); + + if( !pTableFormat ) + return; + + sal_Int16 eTabHoriOri = pTableFormat->GetHoriOrient().GetHoriOrient(); + const SwFormatFrameSize& rFrameSize = pTableFormat->GetFrameSize(); + + sal_uInt32 nAbsWidth = rFrameSize.GetSize().Width(); + sal_uInt32 nBaseWidth = 0; + sal_Int8 nPercentWidth = rFrameSize.GetWidthPercent(); + + bool bFixAbsWidth = nPercentWidth != 0 || /*text::*/HoriOrientation::NONE == eTabHoriOri + || /*text::*/HoriOrientation::FULL == eTabHoriOri; + if( bFixAbsWidth ) + { + nBaseWidth = nAbsWidth; + nAbsWidth = pTableFormat->FindLayoutRect(true).Width(); + if( !nAbsWidth ) + { + // TODO? + } + } + ExportTableFormat( *pTableFormat, nAbsWidth ); + + SwXMLTableColumnsSortByWidth_Impl aExpCols; + SwXMLTableFrameFormatsSort_Impl aExpRows(rRowFormats); + SwXMLTableFrameFormatsSort_Impl aExpCells(rBoxFormats); + SwXMLTableInfo_Impl aTableInfo(&rTable, XML_NAMESPACE_TABLE, rRowFormats, rBoxFormats); + ExportTableLinesAutoStyles( rTable.GetTabLines(), nAbsWidth, nBaseWidth, + pTableFormat->GetName(), aExpCols, aExpRows, aExpCells, + aTableInfo, true); + +} + +void SwXMLExport::ExportTableBox( const SwTableBox& rBox, + sal_uInt32 nColSpan, + sal_uInt32 nRowSpan, + SwXMLTableInfo_Impl& rTableInfo ) +{ + const SwStartNode *pBoxSttNd = rBox.GetSttNd(); + if( pBoxSttNd ) + { + const SwFrameFormat *pFrameFormat = rBox.GetFrameFormat(); + if( pFrameFormat ) + { + auto const it(rTableInfo.GetBoxFormats().find(pFrameFormat)); + assert(it != rTableInfo.GetBoxFormats().end()); + if (it->second) + { + assert(!it->second->isEmpty()); + AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(*it->second)); + } + } + } + + if( nRowSpan != 1 ) + { + AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_SPANNED, + OUString::number(nRowSpan) ); + } + + if( nColSpan != 1 ) + { + AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_SPANNED, + OUString::number(nColSpan) ); + } + + { + if( pBoxSttNd ) + { + // start node -> normal cell + // get cell range for table + rtl::Reference<SwXCell> xCell = SwXCell::CreateXCell( const_cast<SwFrameFormat *>(rTableInfo.GetTableFormat()), + const_cast<SwTableBox *>(&rBox), + const_cast<SwTable *>(rTableInfo.GetTable()) ); + + if (xCell.is()) + { + // get formula (and protection) + const OUString sCellFormula = xCell->getFormula(); + + // if this cell has a formula, export it + // (with value and number format) + if (!sCellFormula.isEmpty()) + { + const OUString sQValue = + GetNamespaceMap().GetQNameByKey( + XML_NAMESPACE_OOOW, sCellFormula, false ); + // formula + AddAttribute(XML_NAMESPACE_TABLE, XML_FORMULA, sQValue ); + } + + // value and format (if NumberFormat != -1) + sal_Int32 nNumberFormat = 0; + Any aAny = xCell->getPropertyValue("NumberFormat"); + aAny >>= nNumberFormat; + + if (static_cast<sal_Int32>(getSwDefaultTextFormat()) == nNumberFormat) + { + // text format + AddAttribute( XML_NAMESPACE_OFFICE, + XML_VALUE_TYPE, XML_STRING ); + } + else if ( (-1 != nNumberFormat) && !xCell->getString().isEmpty() ) + { + // number format key: + // (export values only if cell contains text;) + XMLNumberFormatAttributesExportHelper:: + SetNumberFormatAttributes( + *this, nNumberFormat, xCell->getValue() ); + } + // else: invalid key; ignore + + // cell protection + aAny = xCell->getPropertyValue("IsProtected"); + if (*o3tl::doAccess<bool>(aAny)) + { + AddAttribute( XML_NAMESPACE_TABLE, XML_PROTECTED, + XML_TRUE ); + } + + if( !rTableInfo.IsBaseSectionValid() ) + { + aAny = xCell->getPropertyValue("TextSection"); + Reference < XTextSection > xTextSection; + aAny >>= xTextSection; + rTableInfo.SetBaseSection( xTextSection ); + } + + // export cell element + SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(), + XML_TABLE_CELL, true, true ); + + // export cell content + GetTextParagraphExport()->exportText( xCell, + rTableInfo.GetBaseSection(), + IsShowProgress() ); + } + else + { + OSL_FAIL("here should be a XCell"); + ClearAttrList(); + } + } + else + { + // no start node -> merged cells: export subtable in cell + SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, + XML_TABLE_CELL, true, true ); + { + AddAttribute( XML_NAMESPACE_TABLE, XML_IS_SUB_TABLE, + GetXMLToken( XML_TRUE ) ); + + SvXMLElementExport aElemExport( *this, XML_NAMESPACE_TABLE, + XML_TABLE, true, true ); + ExportTableLines( rBox.GetTabLines(), rTableInfo ); + } + } + } +} + +void SwXMLExport::ExportTableLine( const SwTableLine& rLine, + const SwXMLTableLines_Impl& rLines, + SwXMLTableInfo_Impl& rTableInfo ) +{ + if( rLine.hasSoftPageBreak() ) + { + SvXMLElementExport aElem( *this, XML_NAMESPACE_TEXT, + XML_SOFT_PAGE_BREAK, true, true ); + } + const SwFrameFormat *pFrameFormat = rLine.GetFrameFormat(); + if( pFrameFormat ) + { + auto const it(rTableInfo.GetLineFormats().find(pFrameFormat)); + assert(it != rTableInfo.GetLineFormats().end()); + if (it->second) + { + assert(!it->second->isEmpty()); + AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(*it->second)); + } + } + + { + SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(), XML_TABLE_ROW, true, true ); + const SwTableBoxes& rBoxes = rLine.GetTabBoxes(); + const size_t nBoxes = rBoxes.size(); + + sal_uInt32 nCPos = 0U; + size_t nCol = 0U; + for( size_t nBox=0U; nBox<nBoxes; ++nBox ) + { + const SwTableBox *pBox = rBoxes[nBox]; + + // NEW TABLES + const sal_Int32 nRowSpan = pBox->getRowSpan(); + if( nRowSpan < 1 ) + { + // Export style of covered cell, it includes border information. + const SwFrameFormat* pFormat = pBox->GetFrameFormat(); + if (pFormat) + { + auto const it(rTableInfo.GetBoxFormats().find(pFormat)); + assert(it != rTableInfo.GetBoxFormats().end()); + if (it->second) + { + assert(!it->second->isEmpty()); + AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(*it->second)); + } + } + + SvXMLElementExport aElem2( *this, rTableInfo.GetPrefix(), + XML_COVERED_TABLE_CELL, true, + false ); + } + + if( nBox < nBoxes-1U ) + nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox ); + else + nCPos = rLines.GetWidth(); + + // and their index + const size_t nOldCol = nCol; + SwXMLTableColumn_Impl aCol( nCPos ); + SwXMLTableColumns_Impl::const_iterator it = rLines.GetColumns().find( &aCol ); + OSL_ENSURE( it != rLines.GetColumns().end(), "couldn't find column" ); + nCol = it - rLines.GetColumns().begin(); + + // #i95726# - Some fault tolerance, if table is somehow corrupted. + if ( nCol < nOldCol ) + { + OSL_FAIL( "table and/or table information seems to be corrupted." ); + // NOTE: nOldCol is not necessarily a valid index into + // GetColumns(), but that doesn't matter here + nCol = nOldCol; + } + + const sal_uInt32 nColSpan = nCol - nOldCol + 1U; + + if ( nRowSpan >= 1 ) + ExportTableBox( *pBox, nColSpan, static_cast< sal_uInt32 >(nRowSpan), rTableInfo ); + + for( size_t i=nOldCol; i<nCol; ++i ) + { + SvXMLElementExport aElemExport( *this, rTableInfo.GetPrefix(), + XML_COVERED_TABLE_CELL, true, + false ); + } + + nCol++; + } + } +} + +void SwXMLExport::ExportTableLines( const SwTableLines& rLines, + SwXMLTableInfo_Impl& rTableInfo, + sal_uInt32 nHeaderRows ) +{ + OSL_ENSURE( m_pTableLines && !m_pTableLines->empty(), + "SwXMLExport::ExportTableLines: table columns infos missing" ); + if( !m_pTableLines || m_pTableLines->empty() ) + return; + + SwXMLTableLines_Impl* pLines = nullptr; + size_t nInfoPos; + for( nInfoPos=0; nInfoPos < m_pTableLines->size(); nInfoPos++ ) + { + if( m_pTableLines->at( nInfoPos )->GetLines() == &rLines ) + { + pLines = m_pTableLines->at( nInfoPos ); + break; + } + } + OSL_ENSURE( pLines, + "SwXMLExport::ExportTableLines: table columns info missing" ); + OSL_ENSURE( 0==nInfoPos, + "SwXMLExport::ExportTableLines: table columns infos are unsorted" ); + if( !pLines ) + return; + + SwXMLTableLinesCache_Impl::iterator it = m_pTableLines->begin(); + advance( it, nInfoPos ); + m_pTableLines->erase( it ); + + if( m_pTableLines->empty() ) + m_pTableLines.reset(); + + // pass 2: export columns + const SwXMLTableColumns_Impl& rCols = pLines->GetColumns(); + size_t nColumn = 0U; + const size_t nColumns = rCols.size(); + sal_Int32 nColRep = 1; + SwXMLTableColumn_Impl *pColumn = (nColumns > 0) ? rCols.front().get() : nullptr; + while( pColumn ) + { + nColumn++; + SwXMLTableColumn_Impl *pNextColumn = + (nColumn < nColumns) ? rCols[nColumn].get() : nullptr; + if( pNextColumn && + pNextColumn->GetStyleName() == pColumn->GetStyleName() ) + { + nColRep++; + } + else + { + AddAttribute( XML_NAMESPACE_TABLE, XML_STYLE_NAME, + EncodeStyleName(pColumn->GetStyleName()) ); + + if( nColRep > 1 ) + { + AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, + OUString::number(nColRep) ); + } + + { + SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(), XML_TABLE_COLUMN, true, true ); + } + + nColRep = 1; + } + pColumn = pNextColumn; + } + + // pass 3: export line/rows + const size_t nLines = rLines.size(); + // export header rows, if present + if( nHeaderRows > 0 ) + { + SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, + XML_TABLE_HEADER_ROWS, true, true ); + + OSL_ENSURE( nHeaderRows <= nLines, "more headers then lines?" ); + for( size_t nLine = 0U; nLine < nHeaderRows; ++nLine ) + ExportTableLine( *(rLines[nLine]), *pLines, rTableInfo ); + } + // export remaining rows + for( size_t nLine = nHeaderRows; nLine < nLines; ++nLine ) + { + ExportTableLine( *(rLines[nLine]), *pLines, rTableInfo ); + } + + delete pLines; +} + +void SwXMLExport::ExportTable( const SwTableNode& rTableNd ) +{ + ::std::optional<sal_uInt16> oPrefix = XML_NAMESPACE_TABLE; + if (const SwFrameFormat* pFlyFormat = rTableNd.GetFlyFormat()) + { + if (SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT)) + { + // TODO ODF 1.4 OFFICE-3761 + if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) + { + oPrefix = XML_NAMESPACE_LO_EXT; + } + else + { + oPrefix.reset(); // no export to OASIS namespace yet + } + } + } + + if (!oPrefix) + return; + + const SwTable& rTable = rTableNd.GetTable(); + const SwFrameFormat *pTableFormat = rTable.GetFrameFormat(); + if (pTableFormat && !pTableFormat->GetName().isEmpty()) + { + AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, pTableFormat->GetName()); + AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, + EncodeStyleName(pTableFormat->GetName())); + } + + // table:template-name= + if (!rTable.GetTableStyleName().isEmpty()) + { + OUString sStyleName; + SwStyleNameMapper::FillProgName(rTable.GetTableStyleName(), sStyleName, SwGetPoolIdFromName::TabStyle); + AddAttribute(XML_NAMESPACE_TABLE, XML_TEMPLATE_NAME, sStyleName); + } + + SvXMLElementExport aElem(*this, *oPrefix, XML_TABLE, true, true); + + // export DDE source (if this is a DDE table) + if ( auto pSwDdeTable = dynamic_cast<const SwDDETable*>( &rTable) ) + { + // get DDE Field Type (contains the DDE connection) + const SwDDEFieldType* pDDEFieldType = pSwDdeTable->GetDDEFieldType(); + + // connection name + AddAttribute( XML_NAMESPACE_OFFICE, XML_NAME, + pDDEFieldType->GetName() ); + + // DDE command + const OUString& sCmd = pDDEFieldType->GetCmd(); + sal_Int32 nIdx{ 0 }; + AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_APPLICATION, + sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) ); + AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_ITEM, + sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) ); + AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_TOPIC, + sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) ); + + // auto update + if (pDDEFieldType->GetType() == SfxLinkUpdateMode::ALWAYS) + { + AddAttribute( XML_NAMESPACE_OFFICE, + XML_AUTOMATIC_UPDATE, XML_TRUE ); + } + + // DDE source element (always empty) + SvXMLElementExport aSource(*this, XML_NAMESPACE_OFFICE, + XML_DDE_SOURCE, true, false); + } + + auto const& rFormats(static_cast<SwXMLTextParagraphExport const*>(GetTextParagraphExport().get())->GetTableFormats()); + auto const it(rFormats.find(&rTableNd)); + assert(it != rFormats.end()); + SwXMLTableInfo_Impl aTableInfo(&rTable, *oPrefix, it->second.first, it->second.second); + ExportTableLines( rTable.GetTabLines(), aTableInfo, rTable.GetRowsToRepeat() ); +} + +void SwXMLTextParagraphExport::exportTableAutoStyles() { + // note: maTableNodes is used here only to keep the iteration order as before + for (const auto* pTableNode : maTableNodes) + { + static_cast<SwXMLExport&>(GetExport()).ExportTableAutoStyles(*pTableNode); + } +} + +void SwXMLTextParagraphExport::CollectTableLinesAutoStyles(const SwTableLines& rLines, + SwFrameFormat& rFormat, bool _bProgress) +{ + // Follow SwXMLExport::ExportTableLines/ExportTableLine/ExportTableBox + for (const SwTableLine* pLine : rLines) + { + for (SwTableBox* pBox : pLine->GetTabBoxes()) + { + if (pBox->getRowSpan() <= 0) + continue; + if (pBox->GetSttNd()) + { + if (rtl::Reference<SwXCell> xCell = SwXCell::CreateXCell(&rFormat, pBox)) + exportText(xCell, true /*bAutoStyles*/, _bProgress, true /*bExportParagraph*/); + } + else + { + // no start node -> merged cells: export subtable in cell + CollectTableLinesAutoStyles(pBox->GetTabLines(), rFormat, _bProgress); + } + } + } +} + +void SwXMLTextParagraphExport::exportTable( + const Reference < XTextContent > & rTextContent, + bool bAutoStyles, bool _bProgress ) +{ + bool bOldShowProgress = static_cast<SwXMLExport&>(GetExport()).IsShowProgress(); + static_cast<SwXMLExport&>(GetExport()).SetShowProgress( _bProgress ); + + Reference < XTextTable > xTextTable( rTextContent, UNO_QUERY ); + OSL_ENSURE( xTextTable.is(), "text table missing" ); + if( xTextTable.is() ) + { + SwXTextTable* pXTable = dynamic_cast<SwXTextTable*>(rTextContent.get()); + if( pXTable ) + { + SwFrameFormat *const pFormat = pXTable->GetFrameFormat(); + OSL_ENSURE( pFormat, "table format missing" ); + const SwTable *pTable = SwTable::FindTable( pFormat ); + OSL_ENSURE( pTable, "table missing" ); + const SwTableNode *pTableNd = pTable->GetTableNode(); + OSL_ENSURE( pTableNd, "table node missing" ); + if( bAutoStyles ) + { + // AUTOSTYLES: Optimization: Do not export table autostyle if + // we are currently exporting the content.xml stuff and + // the table is located in header/footer: + // During the flat XML export (used e.g. by .sdw-export) + // ALL flags are set at the same time. + const bool bExportStyles = bool( GetExport().getExportFlags() & SvXMLExportFlags::STYLES ); + if (!isAutoStylesCollected() + && (bExportStyles || !pFormat->GetDoc()->IsInHeaderFooter(*pTableNd))) + { + maTableNodes.push_back(pTableNd); + m_TableFormats.try_emplace(pTableNd); + // Collect all tables inside cells of this table, too + CollectTableLinesAutoStyles(pTable->GetTabLines(), *pFormat, _bProgress); + } + } + else + { + static_cast<SwXMLExport&>(GetExport()).ExportTable( *pTableNd ); + } + } + } + + static_cast<SwXMLExport&>(GetExport()).SetShowProgress( bOldShowProgress ); +} + +void SwXMLExport::DeleteTableLines() +{ + if ( m_pTableLines ) + { + for (SwXMLTableLines_Impl* p : *m_pTableLines) + delete p; + m_pTableLines->clear(); + m_pTableLines.reset(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltbli.cxx b/sw/source/filter/xml/xmltbli.cxx new file mode 100644 index 0000000000..b2d312cad2 --- /dev/null +++ b/sw/source/filter/xml/xmltbli.cxx @@ -0,0 +1,2757 @@ +/* -*- 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 <hintids.hxx> + +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/text/XTextTable.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <comphelper/servicehelper.hxx> +#include <o3tl/numeric.hxx> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> +#include <svl/itemset.hxx> +#include <svl/numformat.hxx> +#include <svl/zformat.hxx> +#include <sax/tools/converter.hxx> +#include <unotools/configmgr.hxx> +#include <utility> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/namespacemap.hxx> + +#include <xmloff/families.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/i18nmap.hxx> +#include <editeng/protitem.hxx> +#include <editeng/lrspitem.hxx> +#include <poolfmt.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <fmtfordr.hxx> +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <swtable.hxx> +#include <swtblfmt.hxx> +#include <pam.hxx> +#include <unoprnms.hxx> +#include <unotbl.hxx> +#include <unotextrange.hxx> +#include <cellatr.hxx> +#include <swddetbl.hxx> +#include <ddefld.hxx> +#include <sfx2/linkmgr.hxx> +#include "xmlimp.hxx" +#include "xmltbli.hxx" +#include <vcl/svapp.hxx> +#include <ndtxt.hxx> +#include <unotextcursor.hxx> +#include <SwStyleNameMapper.hxx> +#include <IDocumentSettingAccess.hxx> + +#include <algorithm> +#include <vector> +#include <memory> + +#include <limits.h> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::table; +using namespace ::com::sun::star::xml::sax; +using namespace ::xmloff::token; + +class SwXMLTableCell_Impl +{ + OUString m_aStyleName; + + OUString m_StringValue; + + OUString m_sFormula; // cell formula; valid if length > 0 + double m_dValue; // formula value + + rtl::Reference<SwXMLTableContext> m_xSubTable; + + const SwStartNode *m_pStartNode; + sal_uInt32 m_nRowSpan; + sal_uInt32 m_nColSpan; + + bool m_bProtected : 1; + bool m_bHasValue; // determines whether dValue attribute is valid + bool mbCovered; + bool m_bHasStringValue; + +public: + + SwXMLTableCell_Impl( sal_uInt32 nRSpan=1, sal_uInt32 nCSpan=1 ) : + m_dValue( 0.0 ), + m_pStartNode( nullptr ), + m_nRowSpan( nRSpan ), + m_nColSpan( nCSpan ), + m_bProtected( false ), + m_bHasValue( false ), + mbCovered( false ) + , m_bHasStringValue(false) + {} + + inline void Set( const OUString& rStyleName, + sal_uInt32 nRSpan, sal_uInt32 nCSpan, + const SwStartNode *pStNd, SwXMLTableContext *pTable, + bool bProtect, + const OUString* pFormula, + bool bHasValue, + bool bCovered, + double dVal, + OUString const*const pStringValue); + + bool IsUsed() const { return m_pStartNode!=nullptr || + m_xSubTable.is() || m_bProtected;} + + sal_uInt32 GetRowSpan() const { return m_nRowSpan; } + void SetRowSpan( sal_uInt32 nSet ) { m_nRowSpan = nSet; } + sal_uInt32 GetColSpan() const { return m_nColSpan; } + void SetStyleName(const OUString& rStyleName) { m_aStyleName = rStyleName; } + const OUString& GetStyleName() const { return m_aStyleName; } + const OUString& GetFormula() const { return m_sFormula; } + double GetValue() const { return m_dValue; } + bool HasValue() const { return m_bHasValue; } + bool IsProtected() const { return m_bProtected; } + bool IsCovered() const { return mbCovered; } + bool HasStringValue() const { return m_bHasStringValue; } + OUString const* GetStringValue() const { + return m_bHasStringValue ? &m_StringValue : nullptr; + } + + const SwStartNode *GetStartNode() const { return m_pStartNode; } + inline void SetStartNode( const SwStartNode *pSttNd ); + + inline SwXMLTableContext *GetSubTable() const; + + inline void Dispose(); +}; + +inline void SwXMLTableCell_Impl::Set( const OUString& rStyleName, + sal_uInt32 nRSpan, sal_uInt32 nCSpan, + const SwStartNode *pStNd, + SwXMLTableContext *pTable, + bool bProtect, + const OUString* pFormula, + bool bHasVal, + bool bCov, + double dVal, + OUString const*const pStringValue ) +{ + m_aStyleName = rStyleName; + m_nRowSpan = nRSpan; + m_nColSpan = nCSpan; + m_pStartNode = pStNd; + m_xSubTable = pTable; + m_dValue = dVal; + m_bHasValue = bHasVal; + mbCovered = bCov; + if (pStringValue) + { + m_StringValue = *pStringValue; + } + m_bHasStringValue = (pStringValue != nullptr); + m_bProtected = bProtect; + + // set formula, if valid + if (pFormula != nullptr) + { + m_sFormula = *pFormula; + } +} + +inline void SwXMLTableCell_Impl::SetStartNode( const SwStartNode *pSttNd ) +{ + m_pStartNode = pSttNd; + m_xSubTable = nullptr; +} + +inline SwXMLTableContext *SwXMLTableCell_Impl::GetSubTable() const +{ + return m_xSubTable.get(); +} + +inline void SwXMLTableCell_Impl::Dispose() +{ + if( m_xSubTable.is() ) + m_xSubTable = nullptr; +} + +class SwXMLTableRow_Impl +{ + OUString m_aStyleName; + OUString m_aDefaultCellStyleName; + std::vector<std::unique_ptr<SwXMLTableCell_Impl>> m_Cells; + bool m_bSplitable; + +public: + + SwXMLTableRow_Impl( OUString aStyleName, sal_uInt32 nCells, + const OUString *pDfltCellStyleName = nullptr ); + + inline SwXMLTableCell_Impl *GetCell( sal_uInt32 nCol ); + + inline void Set( const OUString& rStyleName, + const OUString& rDfltCellStyleName ); + + void Expand( sal_uInt32 nCells, bool bOneCell ); + + void SetSplitable( bool bSet ) { m_bSplitable = bSet; } + bool IsSplitable() const { return m_bSplitable; } + + const OUString& GetStyleName() const { return m_aStyleName; } + const OUString& GetDefaultCellStyleName() const { return m_aDefaultCellStyleName; } + + void Dispose(); +}; + +SwXMLTableRow_Impl::SwXMLTableRow_Impl( OUString aStyleName, + sal_uInt32 nCells, + const OUString *pDfltCellStyleName ) : + m_aStyleName(std::move( aStyleName )), + m_bSplitable( false ) +{ + if( pDfltCellStyleName ) + m_aDefaultCellStyleName = *pDfltCellStyleName; + OSL_ENSURE( nCells <= USHRT_MAX, + "SwXMLTableRow_Impl::SwXMLTableRow_Impl: too many cells" ); + if( nCells > USHRT_MAX ) + nCells = USHRT_MAX; + + for( sal_uInt32 i=0U; i<nCells; ++i ) + { + m_Cells.push_back(std::make_unique<SwXMLTableCell_Impl>()); + } +} + +inline SwXMLTableCell_Impl *SwXMLTableRow_Impl::GetCell( sal_uInt32 nCol ) +{ + OSL_ENSURE( nCol < USHRT_MAX, + "SwXMLTableRow_Impl::GetCell: column number is too big" ); + // #i95726# - some fault tolerance + OSL_ENSURE( nCol < m_Cells.size(), + "SwXMLTableRow_Impl::GetCell: column number is out of bound" ); + return nCol < m_Cells.size() ? m_Cells[nCol].get() : nullptr; +} + +void SwXMLTableRow_Impl::Expand( sal_uInt32 nCells, bool bOneCell ) +{ + OSL_ENSURE( nCells <= USHRT_MAX, + "SwXMLTableRow_Impl::Expand: too many cells" ); + if( nCells > USHRT_MAX ) + nCells = USHRT_MAX; + + sal_uInt32 nColSpan = nCells - m_Cells.size(); + for (size_t i = m_Cells.size(); i < nCells; ++i) + { + m_Cells.push_back(std::make_unique<SwXMLTableCell_Impl>( + 1UL, bOneCell ? nColSpan : 1UL)); + nColSpan--; + } + + OSL_ENSURE( nCells <= m_Cells.size(), + "SwXMLTableRow_Impl::Expand: wrong number of cells" ); +} + +inline void SwXMLTableRow_Impl::Set( const OUString& rStyleName, + const OUString& rDfltCellStyleName ) +{ + m_aStyleName = rStyleName; + m_aDefaultCellStyleName = rDfltCellStyleName; +} + +void SwXMLTableRow_Impl::Dispose() +{ + for (auto & pCell : m_Cells) + { + pCell->Dispose(); + } +} + +namespace { + +class SwXMLTableCellContext_Impl : public SvXMLImportContext +{ + OUString m_aStyleName; + OUString m_sFormula; + OUString m_sSaveParaDefault; + OUString m_StringValue; + + rtl::Reference<SwXMLTableContext> m_xMyTable; + + double m_fValue; + bool m_bHasValue; + bool m_bHasStringValue; + bool m_bValueTypeIsString; + bool m_bProtect; + + sal_uInt32 m_nRowSpan; + sal_uInt32 m_nColSpan; + sal_uInt32 m_nColRepeat; + + bool m_bHasTextContent : 1; + bool m_bHasTableContent : 1; + + SwXMLTableContext *GetTable() { return m_xMyTable.get(); } + + bool HasContent() const { return m_bHasTextContent || m_bHasTableContent; } + inline void InsertContent_(); + inline void InsertContent(); + inline void InsertContent( SwXMLTableContext *pTable ); + +public: + + SwXMLTableCellContext_Impl( + SwXMLImport& rImport, sal_Int32 nElement, + const Reference< xml::sax::XFastAttributeList > & xAttrList, + SwXMLTableContext *pTable ); + + virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext( + sal_Int32 nElement, + const Reference< xml::sax::XFastAttributeList > & xAttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } +}; + +/// Handles <table:covered-table-cell>. +class SwXMLCoveredTableCellContext : public SvXMLImportContext +{ +public: + SwXMLCoveredTableCellContext(SwXMLImport& rImport, + const Reference<xml::sax::XFastAttributeList>& xAttrList, + SwXMLTableContext& rTable); +}; + +SwXMLCoveredTableCellContext::SwXMLCoveredTableCellContext( + SwXMLImport& rImport, const Reference<xml::sax::XFastAttributeList>& xAttrList, + SwXMLTableContext& rTable) + : SvXMLImportContext(rImport) +{ + OUString aStyleName; + for (auto& rIter : sax_fastparser::castToFastAttributeList(xAttrList)) + { + switch (rIter.getToken()) + { + case XML_ELEMENT(TABLE, XML_STYLE_NAME): + aStyleName = rIter.toString(); + break; + } + } + + if (!aStyleName.isEmpty()) + { + rTable.InsertCoveredCell(aStyleName); + } +} +} + +SwXMLTableCellContext_Impl::SwXMLTableCellContext_Impl( + SwXMLImport& rImport, sal_Int32 /*nElement*/, + const Reference< xml::sax::XFastAttributeList > & xAttrList, + SwXMLTableContext *pTable ) : + SvXMLImportContext( rImport ), + m_xMyTable( pTable ), + m_fValue( 0.0 ), + m_bHasValue( false ), + m_bHasStringValue(false), + m_bValueTypeIsString(false), + m_bProtect( false ), + m_nRowSpan( 1 ), + m_nColSpan( 1 ), + m_nColRepeat( 1 ), + m_bHasTextContent( false ), + m_bHasTableContent( false ) +{ + m_sSaveParaDefault = GetImport().GetTextImport()->GetCellParaStyleDefault(); + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch( aIter.getToken() ) + { + case XML_ELEMENT(TABLE, XML_STYLE_NAME): + m_aStyleName = aIter.toString(); + GetImport().GetTextImport()->SetCellParaStyleDefault(m_aStyleName); + break; + case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_SPANNED): + m_nColSpan = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32())); + if (m_nColSpan > 256) + { + SAL_INFO("sw.xml", "ignoring huge table:number-columns-spanned " << m_nColSpan); + m_nColSpan = 1; + } + break; + case XML_ELEMENT(TABLE, XML_NUMBER_ROWS_SPANNED): + m_nRowSpan = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32())); + if (m_nRowSpan > 8192 || (m_nRowSpan > 256 && utl::ConfigManager::IsFuzzing())) + { + SAL_INFO("sw.xml", "ignoring huge table:number-rows-spanned " << m_nRowSpan); + m_nRowSpan = 1; + } + break; + case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_REPEATED): + m_nColRepeat = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32())); + if (m_nColRepeat > 256) + { + SAL_INFO("sw.xml", "ignoring huge table:number-columns-repeated " << m_nColRepeat); + m_nColRepeat = 1; + } + break; + case XML_ELEMENT(TABLE, XML_FORMULA): + { + OUString sTmp; + const sal_uInt16 nPrefix2 = GetImport().GetNamespaceMap(). + GetKeyByAttrValueQName(aIter.toString(), &sTmp); + m_sFormula = XML_NAMESPACE_OOOW == nPrefix2 ? sTmp : aIter.toString(); + } + break; + case XML_ELEMENT(OFFICE, XML_VALUE): + { + // Writer wrongly uses DBL_MAX to flag error but fails to + // check for it after import, so check that here, tdf#139126. + double fTmp; + if (::sax::Converter::convertDouble(fTmp, aIter.toView()) && fTmp < DBL_MAX) + { + m_fValue = fTmp; + m_bHasValue = true; + } + } + break; + case XML_ELEMENT(OFFICE, XML_TIME_VALUE): + { + double fTmp; + if (::sax::Converter::convertDuration(fTmp, aIter.toView())) + { + m_fValue = fTmp; + m_bHasValue = true; + } + } + break; + case XML_ELEMENT(OFFICE, XML_DATE_VALUE): + { + double fTmp; + if (GetImport().GetMM100UnitConverter().convertDateTime(fTmp, + aIter.toView())) + { + m_fValue = fTmp; + m_bHasValue = true; + } + } + break; + case XML_ELEMENT(OFFICE, XML_BOOLEAN_VALUE): + { + bool bTmp(false); + if (::sax::Converter::convertBool(bTmp, aIter.toView())) + { + m_fValue = (bTmp ? 1.0 : 0.0); + m_bHasValue = true; + } + } + break; + case XML_ELEMENT(TABLE, XML_PROTECT): // for backwards compatibility with SRC629 (and before) + case XML_ELEMENT(TABLE, XML_PROTECTED): + { + bool bTmp(false); + if (::sax::Converter::convertBool(bTmp, aIter.toView())) + { + m_bProtect = bTmp; + } + } + break; + case XML_ELEMENT(OFFICE, XML_STRING_VALUE): + { + m_StringValue = aIter.toString(); + m_bHasStringValue = true; + } + break; + case XML_ELEMENT(OFFICE, XML_VALUE_TYPE): + { + if ("string" == aIter.toView()) + { + m_bValueTypeIsString = true; + } + // ignore other types - it would be correct to require + // matching value-type and $type-value attributes, + // but we've been reading those without checking forever. + } + break; + default: + SAL_WARN("sw", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << "=" << aIter.toString()); + } + } +} + +inline void SwXMLTableCellContext_Impl::InsertContent_() +{ + SwStartNode const*const pStartNode( GetTable()->InsertTableSection(nullptr, + (m_bHasStringValue && m_bValueTypeIsString && + !m_aStyleName.isEmpty()) ? & m_aStyleName : nullptr) ); + GetTable()->InsertCell( m_aStyleName, m_nRowSpan, m_nColSpan, + pStartNode, + nullptr, m_bProtect, &m_sFormula, m_bHasValue, m_fValue, + (m_bHasStringValue && m_bValueTypeIsString) ? &m_StringValue : nullptr); +} + +inline void SwXMLTableCellContext_Impl::InsertContent() +{ + OSL_ENSURE( !HasContent(), "content already there" ); + m_bHasTextContent = true; + InsertContent_(); +} + +inline void SwXMLTableCellContext_Impl::InsertContent( + SwXMLTableContext *pTable ) +{ + GetTable()->InsertCell( m_aStyleName, m_nRowSpan, m_nColSpan, nullptr, pTable, m_bProtect ); + m_bHasTableContent = true; +} + +css::uno::Reference<css::xml::sax::XFastContextHandler> SwXMLTableCellContext_Impl::createFastChildContext( + sal_Int32 nElement, + const Reference< xml::sax::XFastAttributeList > & xAttrList ) +{ + SvXMLImportContext *pContext = nullptr; + + bool bSubTable = false; + if( nElement == XML_ELEMENT(TABLE, XML_TABLE) ) + { + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + if( aIter.getToken() == XML_ELEMENT(TABLE, XML_IS_SUB_TABLE) ) + { + if ( IsXMLToken( aIter, XML_TRUE ) ) + bSubTable = true; + } + else + XMLOFF_WARN_UNKNOWN("sw", aIter); + //FIXME: RDFa + } + } + + if( bSubTable ) + { + if( !HasContent() ) + { + SwXMLTableContext *pTableContext = + new SwXMLTableContext( GetSwImport(), GetTable() ); + pContext = pTableContext; + if( GetTable()->IsValid() ) + InsertContent( pTableContext ); + + GetTable()->SetHasSubTables( true ); + } + } + else + { + if( GetTable()->IsValid() && !HasContent() ) + InsertContent(); + // fdo#60842: "office:string-value" overrides text content -> no import + if (!(m_bValueTypeIsString && m_bHasStringValue)) + { + pContext = GetImport().GetTextImport()->CreateTextChildContext( + GetImport(), nElement, xAttrList, + XMLTextType::Cell ); + } + } + + return pContext; +} + +void SwXMLTableCellContext_Impl::endFastElement(sal_Int32 ) +{ + if( GetTable()->IsValid() ) + { + if( m_bHasTextContent ) + { + GetImport().GetTextImport()->DeleteParagraph(); + if( m_nColRepeat > 1 && m_nColSpan == 1 ) + { + // The original text is invalid after deleting the last + // paragraph + Reference < XTextCursor > xSrcTextCursor = + GetImport().GetTextImport()->GetText()->createTextCursor(); + xSrcTextCursor->gotoEnd( true ); + + // Until we have an API for copying we have to use the core. + OTextCursorHelper *pSrcTextCursor = dynamic_cast<OTextCursorHelper*>(xSrcTextCursor.get()); + assert(pSrcTextCursor && "SwXTextCursor missing"); + SwDoc *pDoc = pSrcTextCursor->GetDoc(); + const SwPaM *pSrcPaM = pSrcTextCursor->GetPaM(); + + while( m_nColRepeat > 1 && GetTable()->IsInsertCellPossible() ) + { + InsertContent_(); + + OTextCursorHelper *pDstTextCursor = dynamic_cast<OTextCursorHelper*>(GetImport().GetTextImport()->GetCursor().get()); + assert(pDstTextCursor && "SwXTextCursor missing"); + SwPaM aSrcPaM(*pSrcPaM->GetMark(), *pSrcPaM->GetPoint()); + SwPosition aDstPos( *pDstTextCursor->GetPaM()->GetPoint() ); + pDoc->getIDocumentContentOperations().CopyRange(aSrcPaM, aDstPos, SwCopyFlags::CheckPosInFly); + + m_nColRepeat--; + } + } + } + else if( !m_bHasTableContent ) + { + InsertContent(); + if( m_nColRepeat > 1 && m_nColSpan == 1 ) + { + while( m_nColRepeat > 1 && GetTable()->IsInsertCellPossible() ) + { + InsertContent_(); + m_nColRepeat--; + } + } + } + } + GetImport().GetTextImport()->SetCellParaStyleDefault(m_sSaveParaDefault); +} + +namespace { + +class SwXMLTableColContext_Impl : public SvXMLImportContext +{ + rtl::Reference<SwXMLTableContext> m_xMyTable; + + SwXMLTableContext *GetTable() { return m_xMyTable.get(); } + +public: + + SwXMLTableColContext_Impl( + SwXMLImport& rImport, + const Reference< xml::sax::XFastAttributeList > & xAttrList, + SwXMLTableContext *pTable ); + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } +}; + +} + +SwXMLTableColContext_Impl::SwXMLTableColContext_Impl( + SwXMLImport& rImport, + const Reference< xml::sax::XFastAttributeList > & xAttrList, + SwXMLTableContext *pTable ) : + SvXMLImportContext( rImport ), + m_xMyTable( pTable ) +{ + sal_uInt32 nColRep = 1; + OUString aStyleName, aDfltCellStyleName; + + for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) ) + { + switch (aIter.getToken()) + { + case XML_ELEMENT(TABLE, XML_STYLE_NAME): + aStyleName = aIter.toString(); + break; + case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_REPEATED): + { + nColRep = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32())); + if (nColRep > 256) + { + SAL_INFO("sw.xml", "ignoring huge table:number-columns-repeated " << nColRep); + nColRep = 1; + } + break; + } + case XML_ELEMENT(TABLE, XML_DEFAULT_CELL_STYLE_NAME): + aDfltCellStyleName = aIter.toString(); + break; + case XML_ELEMENT(XML, XML_ID): + { + //FIXME where to put this??? columns do not actually exist in writer... + break; + } + default: + XMLOFF_WARN_UNKNOWN("sw", aIter); + } + } + + sal_Int32 nWidth = MINLAY; + bool bRelWidth = true; + if( !aStyleName.isEmpty() ) + { + const SwFormatFrameSize *pSize; + const SfxItemSet *pAutoItemSet = nullptr; + if( GetSwImport().FindAutomaticStyle( + XmlStyleFamily::TABLE_COLUMN, + aStyleName, &pAutoItemSet ) && + pAutoItemSet && + (pSize = pAutoItemSet->GetItemIfSet( RES_FRM_SIZE, false )) ) + { + nWidth = pSize->GetWidth(); + bRelWidth = SwFrameSize::Variable == pSize->GetHeightSizeType(); + } + } + + if( nWidth ) + { + while( nColRep-- && GetTable()->IsInsertColPossible() ) + GetTable()->InsertColumn( nWidth, bRelWidth, &aDfltCellStyleName ); + } +} + +namespace { + +class SwXMLTableColsContext_Impl : public SvXMLImportContext +{ + rtl::Reference<SwXMLTableContext> m_xMyTable; + + SwXMLTableContext *GetTable() { return m_xMyTable.get(); } + +public: + + SwXMLTableColsContext_Impl( + SwXMLImport& rImport, + SwXMLTableContext *pTable ); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override; + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } +}; + +} + +SwXMLTableColsContext_Impl::SwXMLTableColsContext_Impl( + SwXMLImport& rImport, + SwXMLTableContext *pTable ) : + SvXMLImportContext( rImport ), + m_xMyTable( pTable ) +{ +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLTableColsContext_Impl::createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) +{ + SvXMLImportContext *pContext = nullptr; + + if( nElement == XML_ELEMENT(TABLE, XML_TABLE_COLUMN) && + GetTable()->IsInsertColPossible() ) + pContext = new SwXMLTableColContext_Impl( GetSwImport(), xAttrList, GetTable() ); + else + XMLOFF_WARN_UNKNOWN_ELEMENT("sw", nElement); + + return pContext; +} + +namespace { + +class SwXMLTableRowContext_Impl : public SvXMLImportContext +{ + rtl::Reference<SwXMLTableContext> m_xMyTable; + + sal_uInt32 m_nRowRepeat; + + SwXMLTableContext *GetTable() { return m_xMyTable.get(); } + +public: + + SwXMLTableRowContext_Impl( + SwXMLImport& rImport, sal_Int32 nElement, + const Reference< xml::sax::XFastAttributeList > & xAttrList, + SwXMLTableContext *pTable, bool bInHead=false ); + + virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext( sal_Int32 nElement, + const Reference< xml::sax::XFastAttributeList > & xAttrList ) override; + + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } +}; + +} + +SwXMLTableRowContext_Impl::SwXMLTableRowContext_Impl( SwXMLImport& rImport, + sal_Int32 /*nElement*/, + const Reference< xml::sax::XFastAttributeList > & xAttrList, + SwXMLTableContext *pTable, + bool bInHead ) : + SvXMLImportContext( rImport ), + m_xMyTable( pTable ), + m_nRowRepeat( 1 ) +{ + OUString aStyleName, aDfltCellStyleName; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch(aIter.getToken()) + { + case XML_ELEMENT(TABLE, XML_STYLE_NAME): + aStyleName = aIter.toString(); + break; + case XML_ELEMENT(STYLE, XML_NUMBER_ROWS_REPEATED): + { + m_nRowRepeat = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32())); + if (m_nRowRepeat > 8192 || (m_nRowRepeat > 256 && utl::ConfigManager::IsFuzzing())) + { + SAL_INFO("sw.xml", "ignoring huge table:number-rows-repeated " << m_nRowRepeat); + m_nRowRepeat = 1; + } + break; + } + case XML_ELEMENT(STYLE, XML_DEFAULT_CELL_STYLE_NAME): + aDfltCellStyleName = aIter.toString(); + break; + case XML_ELEMENT(XML, XML_ID): + break; + default: + XMLOFF_WARN_UNKNOWN("sw", aIter); + } + } + if( GetTable()->IsValid() ) + GetTable()->InsertRow( aStyleName, aDfltCellStyleName, bInHead ); +} + +void SwXMLTableRowContext_Impl::endFastElement(sal_Int32 ) +{ + if( GetTable()->IsValid() ) + { + GetTable()->FinishRow(); + + if( m_nRowRepeat > 1 ) + GetTable()->InsertRepRows( m_nRowRepeat ); + } +} + +css::uno::Reference<css::xml::sax::XFastContextHandler> SwXMLTableRowContext_Impl::createFastChildContext( + sal_Int32 nElement, + const Reference< xml::sax::XFastAttributeList > & xAttrList ) +{ + SvXMLImportContext *pContext = nullptr; + + if( nElement == XML_ELEMENT(TABLE, XML_TABLE_CELL) || + nElement == XML_ELEMENT(LO_EXT, XML_TABLE_CELL) ) + { + if( !GetTable()->IsValid() || GetTable()->IsInsertCellPossible() ) + pContext = new SwXMLTableCellContext_Impl( GetSwImport(), nElement, + xAttrList, + GetTable() ); + } + else if( nElement == XML_ELEMENT(TABLE, XML_COVERED_TABLE_CELL) || + nElement == XML_ELEMENT(LO_EXT, XML_COVERED_TABLE_CELL) ) + { + if (GetTable()->IsValid() && GetTable()->IsInsertCoveredCellPossible()) + { + pContext = new SwXMLCoveredTableCellContext(GetSwImport(), xAttrList, *GetTable()); + } + else + { + pContext = new SvXMLImportContext(GetImport()); + } + } + else + SAL_WARN("sw", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement)); + + return pContext; +} + +namespace { + +class SwXMLTableRowsContext_Impl : public SvXMLImportContext +{ + rtl::Reference<SwXMLTableContext> m_xMyTable; + + bool m_bHeader; + + SwXMLTableContext *GetTable() { return m_xMyTable.get(); } + +public: + + SwXMLTableRowsContext_Impl( SwXMLImport& rImport, + SwXMLTableContext *pTable, + bool bHead ); + + virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext( + sal_Int32 nElement, + const Reference< xml::sax::XFastAttributeList > & xAttrList ) override; + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } +}; + +} + +SwXMLTableRowsContext_Impl::SwXMLTableRowsContext_Impl( SwXMLImport& rImport, + SwXMLTableContext *pTable, + bool bHead ) : + SvXMLImportContext( rImport ), + m_xMyTable( pTable ), + m_bHeader( bHead ) +{ +} + +css::uno::Reference<css::xml::sax::XFastContextHandler> SwXMLTableRowsContext_Impl::createFastChildContext( + sal_Int32 nElement, + const Reference< xml::sax::XFastAttributeList > & xAttrList ) +{ + if( nElement== XML_ELEMENT(TABLE, XML_TABLE_ROW ) && + GetTable()->IsInsertRowPossible() ) + return new SwXMLTableRowContext_Impl( GetSwImport(), nElement, + xAttrList, + GetTable(), + m_bHeader ); + SAL_WARN("sw", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement)); + return nullptr; +} + +class SwXMLDDETableContext_Impl : public SvXMLImportContext +{ + OUString m_sConnectionName; + OUString m_sDDEApplication; + OUString m_sDDEItem; + OUString m_sDDETopic; + bool m_bIsAutomaticUpdate; + +public: + + + SwXMLDDETableContext_Impl(SwXMLImport& rImport); + + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const Reference<xml::sax::XFastAttributeList> & xAttrList) override; + + OUString& GetConnectionName() { return m_sConnectionName; } + OUString& GetDDEApplication() { return m_sDDEApplication; } + OUString& GetDDEItem() { return m_sDDEItem; } + OUString& GetDDETopic() { return m_sDDETopic; } + bool GetIsAutomaticUpdate() const { return m_bIsAutomaticUpdate; } +}; + + +SwXMLDDETableContext_Impl::SwXMLDDETableContext_Impl(SwXMLImport& rImport) : + SvXMLImportContext(rImport), + m_bIsAutomaticUpdate(false) +{ +} + +void SwXMLDDETableContext_Impl::startFastElement( + sal_Int32 /*nElement*/, + const Reference<xml::sax::XFastAttributeList> & xAttrList) +{ + for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) ) + { + switch (aIter.getToken()) + { + case XML_ELEMENT(OFFICE, XML_DDE_APPLICATION): + m_sDDEApplication = aIter.toString(); + break; + case XML_ELEMENT(OFFICE, XML_DDE_TOPIC): + m_sDDETopic = aIter.toString(); + break; + case XML_ELEMENT(OFFICE, XML_DDE_ITEM): + m_sDDEItem = aIter.toString(); + break; + case XML_ELEMENT(OFFICE, XML_NAME): + m_sConnectionName = aIter.toString(); + break; + case XML_ELEMENT(OFFICE, XML_AUTOMATIC_UPDATE): + { + bool bTmp(false); + if (::sax::Converter::convertBool(bTmp, aIter.toView())) + { + m_bIsAutomaticUpdate = bTmp; + } + break; + } + default: + XMLOFF_WARN_UNKNOWN("sw", aIter); + } + // else: unknown attribute namespace + } +} + +// generate a new name for DDE field type (called by lcl_GetDDEFieldType below) +static OUString lcl_GenerateFieldTypeName(const OUString& sPrefix, SwTableNode* pTableNode) +{ + const OUString sPrefixStr(sPrefix.isEmpty() ? OUString("_") : sPrefix); + + // increase count until we find a name that is not yet taken + OUString sName; + sal_Int32 nCount = 0; + do + { + // this is crazy, but just in case all names are taken: exit gracefully + if (nCount == SAL_MAX_INT32) + return sName; + + ++nCount; + sName = sPrefixStr + OUString::number(nCount); + } + while (nullptr != pTableNode->GetDoc().getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Dde, sName, false)); + + return sName; +} + +// set table properties +static SwDDEFieldType* lcl_GetDDEFieldType(SwXMLDDETableContext_Impl* pContext, + SwTableNode* pTableNode) +{ + // make command string + const OUString sCommand(pContext->GetDDEApplication() + + OUStringChar(sfx2::cTokenSeparator) + + pContext->GetDDEItem() + + OUStringChar(sfx2::cTokenSeparator) + + pContext->GetDDETopic()); + + const SfxLinkUpdateMode nType = pContext->GetIsAutomaticUpdate() + ? SfxLinkUpdateMode::ALWAYS + : SfxLinkUpdateMode::ONCALL; + + OUString sName(pContext->GetConnectionName()); + + // field type to be returned + SwDDEFieldType* pType = nullptr; + + // valid name? + if (sName.isEmpty()) + { + sName = lcl_GenerateFieldTypeName(pContext->GetDDEApplication(), + pTableNode); + } + else + { + // check for existing DDE field type with the same name + SwDDEFieldType* pOldType = static_cast<SwDDEFieldType*>(pTableNode->GetDoc().getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Dde, sName, false)); + if (nullptr != pOldType) + { + // same values -> return old type + if ( (pOldType->GetCmd() == sCommand) && + (pOldType->GetType() == nType) ) + { + // same name, same values -> return old type! + pType = pOldType; + } + else + { + // same name, different values -> think of new name + sName = lcl_GenerateFieldTypeName(pContext->GetDDEApplication(), + pTableNode); + } + } + // no old type -> create new one + } + + // create new field type (unless we already have one) + if (nullptr == pType) + { + // create new field type and return + SwDDEFieldType aDDEFieldType(sName, sCommand, nType); + pType = static_cast<SwDDEFieldType*>(pTableNode-> + GetDoc().getIDocumentFieldsAccess().InsertFieldType(aDDEFieldType)); + } + + OSL_ENSURE(nullptr != pType, "We really want a SwDDEFieldType here!"); + return pType; +} + +class TableBoxIndex +{ +public: + OUString msName; + sal_Int32 mnWidth; + bool mbProtected; + + TableBoxIndex( OUString aName, sal_Int32 nWidth, + bool bProtected ) : + msName(std::move( aName )), + mnWidth( nWidth ), + mbProtected( bProtected ) + { } + + bool operator== ( const TableBoxIndex& rArg ) const + { + return (rArg.mnWidth == mnWidth) && + (rArg.mbProtected == mbProtected) && + (rArg.msName == msName); + } +}; + +class TableBoxIndexHasher +{ +public: + size_t operator() (const TableBoxIndex& rArg) const + { + return rArg.msName.hashCode() + rArg.mnWidth + (rArg.mbProtected ? 1 : 0); + } +}; + +const SwXMLTableCell_Impl *SwXMLTableContext::GetCell( sal_uInt32 nRow, + sal_uInt32 nCol ) const +{ + return (*m_pRows)[nRow]->GetCell( nCol ); +} + +SwXMLTableCell_Impl *SwXMLTableContext::GetCell( sal_uInt32 nRow, + sal_uInt32 nCol ) +{ + return (*m_pRows)[nRow]->GetCell( nCol ); +} + + +SwXMLTableContext::SwXMLTableContext( SwXMLImport& rImport, + const Reference< xml::sax::XFastAttributeList > & xAttrList ) : + XMLTextTableContext( rImport ), + m_pRows( new SwXMLTableRows_Impl ), + m_pTableNode( nullptr ), + m_pBox1( nullptr ), + m_bOwnsBox1( false ), + m_pSttNd1( nullptr ), + m_pBoxFormat( nullptr ), + m_pLineFormat( nullptr ), + m_bFirstSection( true ), + m_bRelWidth( true ), + m_bHasSubTables( false ), + m_nHeaderRows( 0 ), + m_nCurRow( 0 ), + m_nCurCol( 0 ), + m_nNonMergedCurCol( 0 ), + m_nWidth( 0 ) +{ + OUString aName; + OUString sXmlId; + + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + const OUString sValue = aIter.toString(); + switch(aIter.getToken()) + { + case XML_ELEMENT(TABLE, XML_STYLE_NAME): + m_aStyleName = sValue; + break; + case XML_ELEMENT(TABLE, XML_NAME): + aName = sValue; + break; + case XML_ELEMENT(TABLE, XML_DEFAULT_CELL_STYLE_NAME): + m_aDfltCellStyleName = sValue; + break; + case XML_ELEMENT(TABLE, XML_TEMPLATE_NAME): + m_aTemplateName = sValue; + break; + case XML_ELEMENT(XML, XML_ID): + sXmlId = sValue; + break; + default: + XMLOFF_WARN_UNKNOWN("sw", aIter); + } + } + + SwDoc *pDoc = GetSwImport().getDoc(); + + OUString sTableName; + if( !aName.isEmpty() ) + { + const SwTableFormat *pTableFormat = pDoc->FindTableFormatByName( aName ); + if( !pTableFormat ) + sTableName = aName; + } + if( sTableName.isEmpty() ) + { + // Optimization: use import's own map to create unique names, because + // SwDoc::GetUniqueTableName scans all the already present tables, + // builds a bitset using rather complex rules, and that has quadratic + // complexity. Try once, then fallback to SwDoc::GetUniqueTableName + auto& tableNameMap = rImport.GetTableNameMap(); + sal_Int32 nextIx = ++tableNameMap[aName]; + OUString test = aName.isEmpty() + ? OUString(rImport.GetDefTableName() + OUString::number(nextIx)) + : OUString(aName + "_" + OUString::number(nextIx)); + if (const SwTableFormat* pExisting = pDoc->FindTableFormatByName(test); !pExisting) + sTableName = test; + else + sTableName = pDoc->GetUniqueTableName(); + GetImport().GetTextImport() + ->GetRenameMap().Add( XML_TEXT_RENAME_TYPE_TABLE, aName, sTableName ); + } + + Reference< XTextTable > xTable; + SwXTextTable *pXTable = nullptr; + Reference<XMultiServiceFactory> xFactory( GetImport().GetModel(), + UNO_QUERY ); + OSL_ENSURE( xFactory.is(), "factory missing" ); + if( xFactory.is() ) + { + Reference<XInterface> xIfc = xFactory->createInstance( "com.sun.star.text.TextTable" ); + OSL_ENSURE( xIfc.is(), "Couldn't create a table" ); + + if( xIfc.is() ) + xTable.set( xIfc, UNO_QUERY ); + } + + if( xTable.is() ) + { + xTable->initialize( 1, 1 ); + if (auto xPropSet = xTable.query<css::beans::XPropertySet>()) + xPropSet->setPropertyValue(UNO_NAME_TABLE_NAME, css::uno::Any(sTableName)); + + try + { + m_xTextContent = xTable; + GetImport().GetTextImport()->InsertTextContent( m_xTextContent ); + } + catch( IllegalArgumentException& ) + { + xTable = nullptr; + } + } + + if( xTable.is() ) + { + //FIXME + // xml:id for RDF metadata + GetImport().SetXmlId(xTable, sXmlId); + + pXTable = dynamic_cast<SwXTextTable*>(xTable.get()); + + Reference < XCellRange > xCellRange( xTable, UNO_QUERY ); + Reference < XCell > xCell = xCellRange->getCellByPosition( 0, 0 ); + Reference < XText> xText( xCell, UNO_QUERY ); + m_xOldCursor = GetImport().GetTextImport()->GetCursor(); + GetImport().GetTextImport()->SetCursor( xText->createTextCursor() ); + + // take care of open redlines for tables + GetImport().GetTextImport()->RedlineAdjustStartNodeCursor(); + } + if( !pXTable ) + return; + + SwFrameFormat *const pTableFrameFormat = pXTable->GetFrameFormat(); + OSL_ENSURE( pTableFrameFormat, "table format missing" ); + SwTable *pTable = SwTable::FindTable( pTableFrameFormat ); + OSL_ENSURE( pTable, "table missing" ); + m_pTableNode = pTable->GetTableNode(); + OSL_ENSURE( m_pTableNode, "table node missing" ); + + SwTableLine *pLine1 = m_pTableNode->GetTable().GetTabLines()[0U]; + m_pBox1 = pLine1->GetTabBoxes()[0U]; + m_pSttNd1 = m_pBox1->GetSttNd(); +} + +SwXMLTableContext::SwXMLTableContext( SwXMLImport& rImport, + SwXMLTableContext *pTable ) : + XMLTextTableContext( rImport ), + m_pRows( new SwXMLTableRows_Impl ), + m_pTableNode( pTable->m_pTableNode ), + m_pBox1( nullptr ), + m_bOwnsBox1( false ), + m_pSttNd1( nullptr ), + m_pBoxFormat( nullptr ), + m_pLineFormat( nullptr ), + m_xParentTable( pTable ), + m_bFirstSection( false ), + m_bRelWidth( true ), + m_bHasSubTables( false ), + m_nHeaderRows( 0 ), + m_nCurRow( 0 ), + m_nCurCol( 0 ), + m_nNonMergedCurCol( 0 ), + m_nWidth( 0 ) +{ +} + +SwXMLTableContext::~SwXMLTableContext() +{ + if (m_bOwnsBox1) + delete m_pBox1; + m_xColumnDefaultCellStyleNames.reset(); + m_pSharedBoxFormats.reset(); + m_pRows.reset(); + + // close redlines on table end nodes + GetImport().GetTextImport()->RedlineAdjustStartNodeCursor(); +} + +css::uno::Reference<css::xml::sax::XFastContextHandler> SwXMLTableContext::createFastChildContext( + sal_Int32 nElement, + const Reference< xml::sax::XFastAttributeList > & xAttrList ) +{ + bool bHeader = false; + switch (nElement) + { + case XML_ELEMENT(TABLE, XML_TABLE_ROW): + case XML_ELEMENT(LO_EXT, XML_TABLE_ROW): + if( IsInsertRowPossible() ) + return new SwXMLTableRowContext_Impl( GetSwImport(), nElement, xAttrList, this ); + break; + case XML_ELEMENT(TABLE, XML_TABLE_HEADER_ROWS): + bHeader = true; + [[fallthrough]]; + case XML_ELEMENT(TABLE, XML_TABLE_ROWS): + return new SwXMLTableRowsContext_Impl( GetSwImport(), this, bHeader ); + case XML_ELEMENT(TABLE, XML_TABLE_HEADER_COLUMNS): + case XML_ELEMENT(TABLE, XML_TABLE_COLUMNS): + // There are slight differences between <table:table-columns> and + // <table:table-columns-groups>. However, none of these are + // supported in Writer (they are Calc-only features), so we + // support column groups by simply using the <table:table-columns> + // token for column groups, too. + case XML_ELEMENT(TABLE, XML_TABLE_COLUMN_GROUP): + if( IsValid() ) + return new SwXMLTableColsContext_Impl( GetSwImport(), this ); + break; + case XML_ELEMENT(TABLE, XML_TABLE_COLUMN): + case XML_ELEMENT(LO_EXT, XML_TABLE_COLUMN): + if( IsValid() && IsInsertColPossible() ) + return new SwXMLTableColContext_Impl( GetSwImport(), xAttrList, + this ); + break; + case XML_ELEMENT(OFFICE, XML_DDE_SOURCE): + // save context for later processing (discard old context, if approp.) + if( IsValid() ) + { + m_xDDESource.set(new SwXMLDDETableContext_Impl( GetSwImport() )); + return m_xDDESource; + } + break; + } + return nullptr; +} + +void SwXMLTableContext::InsertColumn( sal_Int32 nWidth2, bool bRelWidth2, + const OUString *pDfltCellStyleName ) +{ + OSL_ENSURE( m_nCurCol < USHRT_MAX, + "SwXMLTableContext::InsertColumn: no space left" ); + if( m_nCurCol >= USHRT_MAX ) + return; + + if( nWidth2 < MINLAY ) + nWidth2 = MINLAY; + else if( nWidth2 > MAX_WIDTH ) + nWidth2 = MAX_WIDTH; + m_aColumnWidths.emplace_back(nWidth2, bRelWidth2 ); + if( !((pDfltCellStyleName && !pDfltCellStyleName->isEmpty()) || + m_xColumnDefaultCellStyleNames) ) + return; + + if( !m_xColumnDefaultCellStyleNames ) + { + m_xColumnDefaultCellStyleNames.emplace(); + sal_uLong nCount = m_aColumnWidths.size() - 1; + while( nCount-- ) + m_xColumnDefaultCellStyleNames->push_back(OUString()); + } + + if(pDfltCellStyleName) + m_xColumnDefaultCellStyleNames->push_back(*pDfltCellStyleName); + else + m_xColumnDefaultCellStyleNames->push_back(OUString()); +} + +sal_Int32 SwXMLTableContext::GetColumnWidth( sal_uInt32 nCol, + sal_uInt32 nColSpan ) const +{ + sal_uInt32 nLast = nCol+nColSpan; + if( nLast > m_aColumnWidths.size() ) + nLast = m_aColumnWidths.size(); + + sal_Int32 nWidth2 = 0; + for( sal_uInt32 i=nCol; i < nLast; ++i ) + nWidth2 += m_aColumnWidths[i].width; + + return nWidth2; +} + +OUString SwXMLTableContext::GetColumnDefaultCellStyleName( sal_uInt32 nCol ) const +{ + if( m_xColumnDefaultCellStyleNames && nCol < m_xColumnDefaultCellStyleNames->size()) + return (*m_xColumnDefaultCellStyleNames)[static_cast<size_t>(nCol)]; + + return OUString(); +} + +void SwXMLTableContext::InsertCell( const OUString& rStyleName, + sal_uInt32 nRowSpan, sal_uInt32 nColSpan, + const SwStartNode *pStartNode, + SwXMLTableContext *pTable, + bool bProtect, + const OUString* pFormula, + bool bHasValue, + double fValue, + OUString const*const pStringValue ) +{ + OSL_ENSURE( m_nCurCol < GetColumnCount(), + "SwXMLTableContext::InsertCell: row is full" ); + OSL_ENSURE( m_nCurRow < USHRT_MAX, + "SwXMLTableContext::InsertCell: table is full" ); + if( m_nCurCol >= USHRT_MAX || m_nCurRow > USHRT_MAX ) + return; + + OSL_ENSURE( nRowSpan >=1, "SwXMLTableContext::InsertCell: row span is 0" ); + if( 0 == nRowSpan ) + nRowSpan = 1; + OSL_ENSURE( nColSpan >=1, "SwXMLTableContext::InsertCell: col span is 0" ); + if( 0 == nColSpan ) + nColSpan = 1; + + // Until it is possible to add columns here, fix the column span. + sal_uInt32 nColsReq = m_nCurCol + nColSpan; + if( nColsReq > GetColumnCount() ) + { + nColSpan = GetColumnCount() - m_nCurCol; + nColsReq = GetColumnCount(); + } + + // Check whether there are cells from a previous line already that reach + // into the current row. + if( m_nCurRow > 0 && nColSpan > 1 ) + { + SwXMLTableRow_Impl *pCurRow = (*m_pRows)[m_nCurRow].get(); + sal_uInt32 nLastCol = GetColumnCount() < nColsReq ? GetColumnCount() + : nColsReq; + for( sal_uInt32 i=m_nCurCol+1; i<nLastCol; ++i ) + { + if( pCurRow->GetCell(i)->IsUsed() ) + { + // If this cell is used, the column span is truncated + nColSpan = i - m_nCurCol; + nColsReq = i; + break; + } + } + } + + sal_uInt32 nRowsReq = m_nCurRow + nRowSpan; + if( nRowsReq > USHRT_MAX ) + { + nRowSpan = USHRT_MAX - m_nCurRow; + nRowsReq = USHRT_MAX; + } + + // Add columns (if # required columns greater than # columns): + // This should never happen, since we require column definitions! + if ( nColsReq > GetColumnCount() ) + { + for( sal_uInt32 i=GetColumnCount(); i<nColsReq; ++i ) + { + m_aColumnWidths.emplace_back(MINLAY, true ); + } + // adjust columns in *all* rows, if columns must be inserted + for (size_t i = 0; i < m_pRows->size(); ++i) + (*m_pRows)[i]->Expand( nColsReq, i<m_nCurRow ); + } + + // Add rows + if (m_pRows->size() < nRowsReq) + { + for (size_t i = m_pRows->size(); i < nRowsReq; ++i) + m_pRows->push_back(std::make_unique<SwXMLTableRow_Impl>( + "", GetColumnCount())); + } + + OUString sStyleName( rStyleName ); + if( sStyleName.isEmpty() ) + { + sStyleName = (*m_pRows)[m_nCurRow]->GetDefaultCellStyleName(); + if( sStyleName.isEmpty() && m_xColumnDefaultCellStyleNames ) + { + sStyleName = GetColumnDefaultCellStyleName( m_nCurCol ); + if( sStyleName.isEmpty() ) + sStyleName = m_aDfltCellStyleName; + } + } + + // Fill the cells + for( sal_uInt32 i=nColSpan; i>0; --i ) + { + for( sal_uInt32 j=nRowSpan; j>0; --j ) + { + const bool bCovered = i != nColSpan || j != nRowSpan; + SwXMLTableCell_Impl *pCell = GetCell( nRowsReq-j, nColsReq-i ); + if (!pCell) + throw css::lang::IndexOutOfBoundsException(); + pCell->Set( sStyleName, j, i, pStartNode, + pTable, bProtect, pFormula, bHasValue, bCovered, fValue, + pStringValue ); + } + } + + // Set current col to the next (free) column + m_nCurCol = nColsReq; + m_nNonMergedCurCol = nColsReq; + while( m_nCurCol<GetColumnCount() && GetCell(m_nCurRow,m_nCurCol)->IsUsed() ) + m_nCurCol++; +} + +void SwXMLTableContext::InsertCoveredCell(const OUString& rStyleName) +{ + const IDocumentSettingAccess& rIDSA = GetSwImport().getDoc()->getIDocumentSettingAccess(); + bool bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP); + if (!bWordTableCell) + { + // Compatibility flag not active, ignore formatting of covered cells. + return; + } + + SwXMLTableCell_Impl* pCell = GetCell(m_nCurRow, m_nNonMergedCurCol); + ++m_nNonMergedCurCol; + if (!pCell) + { + return; + } + + pCell->SetStyleName(rStyleName); +} + +void SwXMLTableContext::InsertRow( const OUString& rStyleName, + const OUString& rDfltCellStyleName, + bool bInHead ) +{ + OSL_ENSURE( m_nCurRow < USHRT_MAX, + "SwXMLTableContext::InsertRow: no space left" ); + if( m_nCurRow >= USHRT_MAX ) + return; + + // Make sure there is at least one column. + if( 0==m_nCurRow && 0 == GetColumnCount() ) + InsertColumn( USHRT_MAX, true ); + + if (m_nCurRow < m_pRows->size()) + { + // The current row has already been inserted because of a row span + // of a previous row. + (*m_pRows)[m_nCurRow]->Set( + rStyleName, rDfltCellStyleName ); + } + else + { + // add a new row + m_pRows->push_back(std::make_unique<SwXMLTableRow_Impl>( + rStyleName, GetColumnCount(), + &rDfltCellStyleName)); + } + + // We start at the first column ... + m_nCurCol=0; + m_nNonMergedCurCol = 0; + + // ... but this cell may be occupied already. + while( m_nCurCol<GetColumnCount() && GetCell(m_nCurRow,m_nCurCol)->IsUsed() ) + m_nCurCol++; + + if( bInHead && m_nHeaderRows == m_nCurRow ) + m_nHeaderRows++; +} + +void SwXMLTableContext::InsertRepRows( sal_uInt32 nCount ) +{ + const SwXMLTableRow_Impl *pSrcRow = (*m_pRows)[m_nCurRow-1].get(); + while( nCount > 1 && IsInsertRowPossible() ) + { + InsertRow( pSrcRow->GetStyleName(), pSrcRow->GetDefaultCellStyleName(), + false ); + while( m_nCurCol < GetColumnCount() ) + { + if( !GetCell(m_nCurRow,m_nCurCol)->IsUsed() ) + { + const SwXMLTableCell_Impl *pSrcCell = + GetCell( m_nCurRow-1, m_nCurCol ); + InsertCell( pSrcCell->GetStyleName(), 1U, + pSrcCell->GetColSpan(), + InsertTableSection(), + nullptr, pSrcCell->IsProtected(), + &pSrcCell->GetFormula(), + pSrcCell->HasValue(), pSrcCell->GetValue(), + pSrcCell->GetStringValue() ); + } + } + FinishRow(); + nCount--; + } +} + +void SwXMLTableContext::FinishRow() +{ + // Insert an empty cell at the end of the line if the row is not complete + if( m_nCurCol < GetColumnCount() ) + { + InsertCell( "", 1U, GetColumnCount() - m_nCurCol, + InsertTableSection() ); + } + + // Move to the next row. + m_nCurRow++; +} + +const SwStartNode *SwXMLTableContext::GetPrevStartNode( sal_uInt32 nRow, + sal_uInt32 nCol ) const +{ + const SwXMLTableCell_Impl *pPrevCell = nullptr; + if( GetColumnCount() == nCol ) + { + // The last cell is the right one here. + pPrevCell = GetCell( m_pRows->size() - 1U, GetColumnCount() - 1 ); + } + else if( nCol > 0 ) + { + // The previous cell in this row. + pPrevCell = GetCell( nRow, nCol-1 ); + } + else if( nRow > 0 ) + { + // The last cell from the previous row. + pPrevCell = GetCell( nRow-1, GetColumnCount()-1 ); + } + + const SwStartNode *pSttNd = nullptr; + if( pPrevCell ) + { + if( pPrevCell->GetStartNode() ) + pSttNd = pPrevCell->GetStartNode(); + // #i95726# - Some fault tolerance +// else + else if ( pPrevCell->GetSubTable() ) + pSttNd = pPrevCell->GetSubTable()->GetLastStartNode(); + + OSL_ENSURE( pSttNd != nullptr, + "table corrupt" ); + } + + return pSttNd; +} + +void SwXMLTableContext::FixRowSpan( sal_uInt32 nRow, sal_uInt32 nCol, + sal_uInt32 nColSpan ) +{ + sal_uInt32 nLastCol = nCol + nColSpan; + for( sal_uInt32 i = nCol; i < nLastCol; i++ ) + { + sal_uInt32 j = nRow; + sal_uInt32 nRowSpan = 1; + SwXMLTableCell_Impl *pCell = GetCell( j, i ); + while( pCell && pCell->GetRowSpan() > 1 ) + { + pCell->SetRowSpan( nRowSpan++ ); + pCell = j > 0 ? GetCell( --j, i ) : nullptr; + } + } +} + +void SwXMLTableContext::ReplaceWithEmptyCell( sal_uInt32 nRow, sal_uInt32 nCol, bool bRows ) +{ + const SwStartNode *pPrevSttNd = GetPrevStartNode( nRow, nCol ); + const SwStartNode *pSttNd = InsertTableSection( pPrevSttNd ); + + const SwXMLTableCell_Impl *pCell = GetCell( nRow, nCol ); + sal_uInt32 nLastRow = bRows ? nRow + pCell->GetRowSpan() : nRow + 1; + sal_uInt32 nLastCol = nCol + pCell->GetColSpan(); + + for( sal_uInt32 i=nRow; i<nLastRow; i++ ) + { + SwXMLTableRow_Impl *pRow = (*m_pRows)[i].get(); + for( sal_uInt32 j=nCol; j<nLastCol; j++ ) + pRow->GetCell( j )->SetStartNode( pSttNd ); + } + +} + +SwTableBox *SwXMLTableContext::NewTableBox( const SwStartNode *pStNd, + SwTableLine *pUpper ) +{ + // The topmost table is the only table that maintains the two members + // pBox1 and bFirstSection. + if( m_xParentTable.is() ) + return m_xParentTable->NewTableBox( pStNd, pUpper ); + + SwTableBox *pBox; + + if( m_pBox1 && + m_pBox1->GetSttNd() == pStNd ) + { + // if the StartNode is equal to the StartNode of the initially + // created box, we use this box + pBox = m_pBox1; + pBox->SetUpper( pUpper ); + m_pBox1 = nullptr; + m_bOwnsBox1 = false; + } + else + pBox = new SwTableBox( m_pBoxFormat, *pStNd, pUpper ); + + return pBox; +} + +SwTableBoxFormat* SwXMLTableContext::GetSharedBoxFormat( + SwTableBox* pBox, + const OUString& rStyleName, + sal_Int32 nColumnWidth, + bool bProtected, + bool bMayShare, + bool& bNew, + bool* pModifyLocked ) +{ + if ( m_pSharedBoxFormats == nullptr ) + m_pSharedBoxFormats.reset(new map_BoxFormat); + + SwTableBoxFormat* pBoxFormat2; + + TableBoxIndex aKey( rStyleName, nColumnWidth, bProtected ); + map_BoxFormat::iterator aIter = m_pSharedBoxFormats->find( aKey ); + if ( aIter == m_pSharedBoxFormats->end() ) + { + // unknown format so far -> construct a new one + + // get the old format, and reset all attributes + // (but preserve FillOrder) + pBoxFormat2 = static_cast<SwTableBoxFormat*>(pBox->ClaimFrameFormat()); + SwFormatFillOrder aFillOrder( pBoxFormat2->GetFillOrder() ); + pBoxFormat2->ResetAllFormatAttr(); // #i73790# - method renamed + pBoxFormat2->SetFormatAttr( aFillOrder ); + bNew = true; // it's a new format now + + // share this format, if allowed + if ( bMayShare ) + (*m_pSharedBoxFormats)[ aKey ] = pBoxFormat2; + } + else + { + // set the shared format + pBoxFormat2 = aIter->second; + pBox->ChgFrameFormat( pBoxFormat2, /*bNeedToReregister*/false ); + bNew = false; // copied from an existing format + + // claim it, if we are not allowed to share + if ( !bMayShare ) + pBoxFormat2 = static_cast<SwTableBoxFormat*>(pBox->ClaimFrameFormat()); + } + + // lock format (if so desired) + if ( pModifyLocked != nullptr ) + { + (*pModifyLocked) = pBoxFormat2->IsModifyLocked(); + pBoxFormat2->LockModify(); + } + + return pBoxFormat2; +} + +SwTableBox *SwXMLTableContext::MakeTableBox( SwTableLine *pUpper, + sal_uInt32 nTopRow, + sal_uInt32 nLeftCol, + sal_uInt32 nBottomRow, + sal_uInt32 nRightCol ) +{ + //FIXME: here would be a great place to handle XmlId for cell + SwTableBox *pBox = new SwTableBox( m_pBoxFormat, 0, pUpper ); + + sal_uInt32 nColSpan = nRightCol - nLeftCol; + sal_Int32 nColWidth = GetColumnWidth( nLeftCol, nColSpan ); + + // TODO: Share formats! + SwFrameFormat *pFrameFormat = pBox->ClaimFrameFormat(); + SwFormatFillOrder aFillOrder( pFrameFormat->GetFillOrder() ); + pFrameFormat->ResetAllFormatAttr(); // #i73790# - method renamed + pFrameFormat->SetFormatAttr( aFillOrder ); + + pFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nColWidth ) ); + + SwTableLines& rLines = pBox->GetTabLines(); + bool bSplitted = false; + + while( !bSplitted ) + { + sal_uInt32 nStartRow = nTopRow; + sal_uInt32 i; + + for( i = nTopRow; i < nBottomRow; i++ ) + { + // Could the table be split behind the current row? + bool bSplit = true; + SwXMLTableRow_Impl *pRow = (*m_pRows)[i].get(); + for( sal_uInt32 j=nLeftCol; j<nRightCol; j++ ) + { + bSplit = ( 1 == pRow->GetCell(j)->GetRowSpan() ); + if( !bSplit ) + break; + } + if( bSplit && (nStartRow>nTopRow || i+1<nBottomRow) ) + { + SwTableLine *pLine = + MakeTableLine( pBox, nStartRow, nLeftCol, i+1, + nRightCol ); + + rLines.push_back( pLine ); + + nStartRow = i+1; + bSplitted = true; + } + } + if( !bSplitted ) + { + // No splitting was possible. That for, we have to force it. + // Ruthless! + + nStartRow = nTopRow; + while( nStartRow < nBottomRow ) + { + sal_uInt32 nMaxRowSpan = 0; + SwXMLTableRow_Impl *pStartRow = (*m_pRows)[nStartRow].get(); + const SwXMLTableCell_Impl *pCell; + for( i=nLeftCol; i<nRightCol; i++ ) + { + pCell = pStartRow->GetCell(i); + if( pCell->GetRowSpan() > nMaxRowSpan ) + nMaxRowSpan = pCell->GetRowSpan(); + } + + nStartRow += nMaxRowSpan; + if( nStartRow<nBottomRow ) + { + SwXMLTableRow_Impl *pPrevRow = (*m_pRows)[nStartRow - 1U].get(); + i = nLeftCol; + while( i < nRightCol ) + { + if( pPrevRow->GetCell(i)->GetRowSpan() > 1 ) + { + const SwXMLTableCell_Impl *pCell2 = + GetCell( nStartRow, i ); + const sal_uInt32 nColSpan2 = pCell2->GetColSpan(); + FixRowSpan( nStartRow-1, i, nColSpan2 ); + ReplaceWithEmptyCell( nStartRow, i, true ); + i += nColSpan2; + } + else + { + i++; + } + } + } + } + // and now start over again... + } + } + + return pBox; +} + +SwTableBox *SwXMLTableContext::MakeTableBox( + SwTableLine *pUpper, const SwXMLTableCell_Impl *pCell, + sal_uInt32 nLeftCol, sal_uInt32 nRightCol ) +{ + //FIXME: here would be a great place to handle XmlId for cell + SwTableBox *pBox; + sal_uInt32 nColSpan = nRightCol - nLeftCol; + sal_Int32 nColWidth = GetColumnWidth( nLeftCol, nColSpan ); + + if( pCell->GetStartNode() ) + { + pBox = NewTableBox( pCell->GetStartNode(), pUpper ); + } + else + { + // and it is a table: therefore we build a new box and + // put the rows of the table into the rows of the box + pBox = new SwTableBox( m_pBoxFormat, 0, pUpper ); + pCell->GetSubTable()->MakeTable( pBox, nColWidth ); + } + + // Share formats! + const OUString sStyleName = pCell->GetStyleName(); + bool bModifyLocked; + bool bNew; + SwTableBoxFormat *pBoxFormat2 = GetSharedBoxFormat( + pBox, sStyleName, nColWidth, pCell->IsProtected(), + pCell->GetStartNode() && pCell->GetFormula().isEmpty() && + ! pCell->HasValue(), + bNew, &bModifyLocked ); + + // if a new format was created, then we need to set the style + if ( bNew ) + { + // set style + const SfxItemSet *pAutoItemSet = nullptr; + if( pCell->GetStartNode() && !sStyleName.isEmpty() && + GetSwImport().FindAutomaticStyle( + XmlStyleFamily::TABLE_CELL, sStyleName, &pAutoItemSet ) ) + { + if( pAutoItemSet ) + pBoxFormat2->SetFormatAttr( *pAutoItemSet ); + } + } + + if( pCell->GetStartNode() ) + { + if (pCell->HasStringValue()) + { + SwNodeIndex const aNodeIndex(*(pCell->GetStartNode()), 1); + SwTextNode *const pTextNode(aNodeIndex.GetNode().GetTextNode()); + SAL_WARN_IF(!pTextNode, "sw", "Should have a text node in cell?"); + if (pTextNode) + { + SAL_WARN_IF(!pTextNode->GetText().isEmpty(), "sw", + "why text here?"); + pTextNode->InsertText(*pCell->GetStringValue(), + SwContentIndex(pTextNode, 0)); + } + } + + // try to rescue broken documents with a certain pattern + // if: 1) the cell has a default number format (number 0) + // 2) the call has no formula + // 3) the value is 0.0 + // 4) the text doesn't look anything like 0.0 + // [read: length > 10, or length smaller 10 and no 0 in it] + // then make it a text cell! + bool bSuppressNumericContent = false; + if( pCell->HasValue() && (pCell->GetValue() == 0.0) && + pCell->GetFormula().isEmpty() && + !sStyleName.isEmpty() ) + { + // default num format? + if( const SwTableBoxNumFormat* pNumFormat = pBoxFormat2->GetItemIfSet( RES_BOXATR_FORMAT, false ) ) + { + if (pNumFormat && (pNumFormat->GetValue() % SV_COUNTRY_LANGUAGE_OFFSET) == 0) + { + // only one text node? + SwNodeIndex aNodeIndex( *(pCell->GetStartNode()), 1 ); + if( ( aNodeIndex.GetNode().EndOfSectionIndex() - + aNodeIndex.GetNode().StartOfSectionIndex() ) == SwNodeOffset(2) ) + { + SwTextNode* pTextNode= aNodeIndex.GetNode().GetTextNode(); + if( pTextNode != nullptr ) + { + // check text: does it look like some form of 0.0? + const OUString& rText = pTextNode->GetText(); + if( ( rText.getLength() > 10 ) || + ( rText.indexOf( '0' ) == -1 ) ) + { + bSuppressNumericContent = true; + } + } + } + else + bSuppressNumericContent = true; // several nodes + } + } + } + + if( bSuppressNumericContent ) + { + // suppress numeric content? Then reset number format! + pBoxFormat2->ResetFormatAttr( RES_BOXATR_FORMULA ); + pBoxFormat2->ResetFormatAttr( RES_BOXATR_FORMAT ); + pBoxFormat2->ResetFormatAttr( RES_BOXATR_VALUE ); + } + else + { + // the normal case: set formula and value (if available) + + const OUString& rFormula = pCell->GetFormula(); + if (!rFormula.isEmpty()) + { + // formula cell: insert formula if valid + SwTableBoxFormula aFormulaItem( rFormula ); + pBoxFormat2->SetFormatAttr( aFormulaItem ); + } + else if (!pCell->HasValue() && pCell->HasStringValue()) + { + // Check for another inconsistency: + // No value but a non-textual format, i.e. a number format + // Solution: the number format will be removed, + // the cell gets the default text format. + if( const SwTableBoxNumFormat* pNumFormat = m_pBoxFormat->GetItemIfSet( RES_BOXATR_FORMAT, false ) ) + { + const SwDoc* pDoc = m_pBoxFormat->GetDoc(); + const SvNumberFormatter* pNumberFormatter = pDoc ? + pDoc->GetNumberFormatter() : nullptr; + if( pNumFormat != nullptr && pNumberFormatter && + !pNumberFormatter->GetEntry( pNumFormat->GetValue() )->IsTextFormat() ) + m_pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT ); + } + } + // always insert value, even if default + if( pCell->HasValue() ) + { + SwTableBoxValue aValueItem( pCell->GetValue() ); + pBoxFormat2->SetFormatAttr( aValueItem ); + } + } + + // update cell content depend on the default language + pBox->ActualiseValueBox(); + } + + // table cell protection + if( pCell->IsProtected() ) + { + SvxProtectItem aProtectItem( RES_PROTECT ); + aProtectItem.SetContentProtect( true ); + pBoxFormat2->SetFormatAttr( aProtectItem ); + } + + // restore old modify-lock state + if (! bModifyLocked) + pBoxFormat2->UnlockModify(); + + pBoxFormat2->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nColWidth ) ); + + return pBox; +} + +SwTableLine *SwXMLTableContext::MakeTableLine( SwTableBox *pUpper, + sal_uInt32 nTopRow, + sal_uInt32 nLeftCol, + sal_uInt32 nBottomRow, + sal_uInt32 nRightCol ) +{ + //FIXME: here would be a great place to handle XmlId for row + SwTableLine *pLine; + if( !pUpper && 0UL==nTopRow ) + { + pLine = m_pTableNode->GetTable().GetTabLines()[0U]; + } + else + { + pLine = new SwTableLine( m_pLineFormat, 0, pUpper ); + } + + // TODO: Share formats! + SwFrameFormat *pFrameFormat = pLine->ClaimFrameFormat(); + SwFormatFillOrder aFillOrder( pFrameFormat->GetFillOrder() ); + pFrameFormat->ResetAllFormatAttr(); // #i73790# - method renamed + pFrameFormat->SetFormatAttr( aFillOrder ); + + const SfxItemSet *pAutoItemSet = nullptr; + const OUString& rStyleName = (*m_pRows)[nTopRow]->GetStyleName(); + if( 1 == (nBottomRow - nTopRow) && + !rStyleName.isEmpty() && + GetSwImport().FindAutomaticStyle( + XmlStyleFamily::TABLE_ROW, rStyleName, &pAutoItemSet ) ) + { + if( pAutoItemSet ) + pFrameFormat->SetFormatAttr( *pAutoItemSet ); + } + + SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + + sal_uInt32 nStartCol = nLeftCol; + while( nStartCol < nRightCol ) + { + for( sal_uInt32 nRow=nTopRow; nRow<nBottomRow; nRow++ ) + (*m_pRows)[nRow]->SetSplitable( true ); + + sal_uInt32 nCol = nStartCol; + sal_uInt32 nSplitCol = nRightCol; + bool bSplitted = false; + while( !bSplitted ) + { + OSL_ENSURE( nCol < nRightCol, "Ran too far" ); + + // Can be split after current HTML table column? + // If yes, can the created region still be split to + // rows if the next column is added to it? + bool bSplit = true; + bool bHoriSplitMayContinue = false; + bool bHoriSplitPossible = false; + + if ( m_bHasSubTables ) + { + // Convert row spans if the table has subtables: + for( sal_uInt32 nRow=nTopRow; nRow<nBottomRow; nRow++ ) + { + SwXMLTableCell_Impl *pCell = GetCell(nRow,nCol); + // Could the table fragment be split horizontally behind + // the current line? + bool bHoriSplit = (*m_pRows)[nRow]->IsSplitable() && + nRow+1 < nBottomRow && + 1 == pCell->GetRowSpan(); + (*m_pRows)[nRow]->SetSplitable( bHoriSplit ); + + // Could the table fragment be split vertically behind the + // current column (uptp the current line? + bSplit &= ( 1 == pCell->GetColSpan() ); + if( bSplit ) + { + bHoriSplitPossible |= bHoriSplit; + + // Could the current table fragment be split + // horizontally behind the next column, too? + bHoriSplit &= (nCol+1 < nRightCol && + 1 == GetCell(nRow,nCol+1)->GetRowSpan()); + bHoriSplitMayContinue |= bHoriSplit; + } + } + } + else + { + // No subtables: we use the new table model. + SwXMLTableCell_Impl *pCell = GetCell(nTopRow,nCol); + + // #i95726# - some fault tolerance + if ( pCell == nullptr ) + { + OSL_FAIL( "table seems to be corrupt." ); + break; + } + + // Could the table fragment be split vertically behind the + // current column (uptp the current line? + bSplit = 1 == pCell->GetColSpan(); + } + +#if OSL_DEBUG_LEVEL > 0 + if( nCol == nRightCol-1 ) + { + OSL_ENSURE( bSplit, "Split-Flag wrong" ); + if ( m_bHasSubTables ) + { + OSL_ENSURE( !bHoriSplitMayContinue, + "HoriSplitMayContinue-Flag wrong" ); + SwXMLTableCell_Impl *pTmpCell = GetCell( nTopRow, nStartCol ); + OSL_ENSURE( pTmpCell->GetRowSpan() != (nBottomRow-nTopRow) || + !bHoriSplitPossible, "HoriSplitPossible-Flag wrong" ); + } + } +#endif + + OSL_ENSURE( !m_bHasSubTables || !bHoriSplitMayContinue || bHoriSplitPossible, + "bHoriSplitMayContinue, but not bHoriSplitPossible" ); + + if( bSplit ) + { + SwTableBox* pBox = nullptr; + SwXMLTableCell_Impl *pCell = GetCell( nTopRow, nStartCol ); + // #i95726# - some fault tolerance + if( ( !m_bHasSubTables || ( pCell->GetRowSpan() == (nBottomRow-nTopRow) ) ) && + pCell->GetColSpan() == (nCol+1-nStartCol) && + ( pCell->GetStartNode() || pCell->GetSubTable() ) ) + { + // insert new empty cell for covered cells: + sal_Int32 nBoxRowSpan = 1; + if ( !m_bHasSubTables ) + { + nBoxRowSpan = pCell->GetRowSpan(); + if ( pCell->IsCovered() ) + { + nBoxRowSpan = -1 * nBoxRowSpan; + ReplaceWithEmptyCell( nTopRow, nStartCol, false ); + } + } + + // The remaining box neither contains lines nor rows (i.e. + // is a content box + nSplitCol = nCol + 1; + + pBox = MakeTableBox( pLine, pCell, nStartCol, nSplitCol ); + + if ( 1 != nBoxRowSpan ) + pBox->setRowSpan( nBoxRowSpan ); + + bSplitted = true; + } + else if( m_bHasSubTables && bHoriSplitPossible && bHoriSplitMayContinue ) + { + // The table fragment could be split behind the current + // column, and the remaining fragment could be divided + // into lines. Anyway, it could be that this applies to + // the next column, too. That for, we check the next + // column but remember the current one as a good place to + // split. + nSplitCol = nCol + 1; + } + else if ( m_bHasSubTables ) + { + // If the table resulting table fragment could be divided + // into lines if splitting behind the current column, but + // this doesn't apply for thr next column, we split begind + // the current column. This applies for the last column, + // too. + // If the resulting box cannot be split into rows, + // the split at the last split position we remembered. + if( bHoriSplitPossible || nSplitCol > nCol+1 ) + { + OSL_ENSURE( !bHoriSplitMayContinue, + "bHoriSplitMayContinue==true" ); + OSL_ENSURE( bHoriSplitPossible || nSplitCol == nRightCol, + "bHoriSplitPossible flag should be set" ); + + nSplitCol = nCol + 1; + } + + pBox = MakeTableBox( pLine, nTopRow, nStartCol, + nBottomRow, nSplitCol ); + bSplitted = true; + } + + OSL_ENSURE( m_bHasSubTables || pBox, "Colspan trouble" ); + + if( pBox ) + rBoxes.push_back( pBox ); + } + nCol++; + } + nStartCol = nSplitCol; + } + + return pLine; +} + +void SwXMLTableContext::MakeTable_( SwTableBox *pBox ) +{ + // fix column widths + sal_uInt32 nCols = GetColumnCount(); + + // If there are empty rows (because of some row span of previous rows) + // the have to be deleted. The previous rows have to be truncated. + + if (m_pRows->size() > m_nCurRow) + { + SwXMLTableRow_Impl *pPrevRow = (*m_pRows)[m_nCurRow - 1U].get(); + const SwXMLTableCell_Impl *pCell; + for( size_t i = 0; i < m_aColumnWidths.size(); ++i ) + { + pCell = pPrevRow->GetCell(i); + if( pCell->GetRowSpan() > 1 ) + { + FixRowSpan( m_nCurRow-1, i, 1UL ); + } + } + for (size_t i = m_pRows->size() - 1; i >= m_nCurRow; --i) + m_pRows->pop_back(); + } + + if (m_pRows->empty()) + { + InsertCell( "", 1U, nCols, InsertTableSection() ); + } + + // TODO: Do we have to keep both values, the relative and the absolute + // width? + sal_Int32 nAbsWidth = 0; + sal_Int32 nMinAbsColWidth = 0; + sal_Int32 nRelWidth = 0; + sal_Int32 nMinRelColWidth = 0; + sal_uInt32 nRelCols = 0; + for( const auto& rCol : m_aColumnWidths) + { + if( rCol.isRelative ) + { + nRelWidth += rCol.width; + if( 0 == nMinRelColWidth || rCol.width < nMinRelColWidth ) + nMinRelColWidth = rCol.width; + nRelCols++; + } + else + { + nAbsWidth += rCol.width; + if( 0 == nMinAbsColWidth || rCol.width < nMinAbsColWidth ) + nMinAbsColWidth = rCol.width; + } + } + sal_uInt32 nAbsCols = nCols - nRelCols; + + if( m_bRelWidth ) + { + // If there a columns that have an absolute width, we have to + // calculate a relative one for them. + if( nAbsCols > 0 ) + { + // All column that have absolute widths get relative widths; + // these widths relate to each over like the original absolute + // widths. The smallest column gets a width that has the same + // value as the smallest column that has a relative width + // already. + if( 0 == nMinRelColWidth ) + nMinRelColWidth = nMinAbsColWidth; + + for( auto& rCol : m_aColumnWidths) + { + if( !rCol.isRelative ) + { + if (nMinAbsColWidth == 0) + throw o3tl::divide_by_zero(); + sal_Int32 nVal; + if (o3tl::checked_multiply<sal_Int32>(rCol.width, nMinRelColWidth, nVal)) + throw std::overflow_error("overflow in multiply"); + sal_Int32 nRelCol = nVal / nMinAbsColWidth; + rCol.width = nRelCol; + rCol.isRelative = true; + nRelWidth += nRelCol; + nAbsCols--; + if (nAbsCols <= 0) + break; + } + } + } + + if( !m_nWidth ) + { + // This happens only for percentage values for the table itself. + // In this case, the columns get the correct width even if + // the sum of the relative widths is smaller than the available + // width in TWIP. Therefore, we can use the relative width. + m_nWidth = std::min(nRelWidth, MAX_WIDTH); + } + if( nRelWidth != m_nWidth && nRelWidth && nCols ) + { + double n = static_cast<double>(m_nWidth) / static_cast<double>(nRelWidth); + nRelWidth = 0; + for( auto colIter = m_aColumnWidths.begin(); colIter != (m_aColumnWidths.end() - 1); ++colIter) + { + sal_Int32 nW = static_cast<sal_Int32>( colIter->width * n); + colIter->width = o3tl::narrowing<sal_uInt16>(nW); + nRelWidth += nW; + } + m_aColumnWidths.back().width = (m_nWidth-nRelWidth); + } + } + else + { + // If there are columns that have relative widths, we have to + // calculate an absolute widths for them. + if( nRelCols > 0 ) + { + // The absolute space that is available for all columns with a + // relative width. + sal_Int32 nAbsForRelWidth = + m_nWidth > nAbsWidth ? m_nWidth - nAbsWidth : sal_Int32(0L); + + // The relative width that has to be distributed in addition to + // equally widthed columns. + sal_Int32 nExtraRel = nRelWidth - (nRelCols * nMinRelColWidth); + + // The absolute space that may be distributed in addition to + // minimum widthed columns. + sal_Int32 nMinAbs = nRelCols * MINLAY; + sal_Int32 nExtraAbs = + nAbsForRelWidth > nMinAbs ? nAbsForRelWidth - nMinAbs : sal_Int32(0L); + + bool bMin = false; // Do all columns get the minimum width? + bool bMinExtra = false; // Do all columns get the minimum width plus + // some extra space? + + if( nAbsForRelWidth <= nMinAbs ) + { + // If there is not enough space left for all columns to + // get the minimum width, they get the minimum width, anyway. + nAbsForRelWidth = nMinAbs; + bMin = true; + } + else if( nAbsForRelWidth <= (nRelWidth * MINLAY) / + nMinRelColWidth ) + { + // If there is enough space for all columns to get the + // minimum width, but not to get a width that takes the + // relative width into account, each column gets the minimum + // width plus some extra space that is based on the additional + // space that is available. + bMinExtra = true; + } + // Otherwise, if there is enough space for every column, every + // column gets this space. + + for( auto& rCol : m_aColumnWidths ) + { + if( rCol.isRelative ) + { + sal_Int32 nAbsCol; + if( 1 == nRelCols ) + { + // The last column that has a relative width gets + // all absolute space that is left. + nAbsCol = nAbsForRelWidth; + } + else + { + if( bMin ) + { + nAbsCol = MINLAY; + } + else if( bMinExtra ) + { + sal_Int32 nExtraRelCol = rCol.width - nMinRelColWidth; + nAbsCol = MINLAY + (nExtraRelCol * nExtraAbs) / + nExtraRel; + } + else + { + nAbsCol = ( rCol.width * nAbsForRelWidth) / nRelWidth; + } + } + rCol.width = nAbsCol; + rCol.isRelative = false; + nAbsForRelWidth -= nAbsCol; + nAbsWidth += nAbsCol; + nRelCols--; + if (nRelCols <= 0) + break; + } + } + } + + if( nCols && nAbsWidth ) + { + if( nAbsWidth < m_nWidth ) + { + // If the table's width is larger than the sum of the absolute + // column widths, every column get some extra width. + sal_Int32 nExtraAbs = m_nWidth - nAbsWidth; + sal_Int32 nAbsLastCol = m_aColumnWidths.back().width + nExtraAbs; + for( auto colIter = m_aColumnWidths.begin(); colIter != (m_aColumnWidths.end() - 1); ++colIter ) + { + sal_Int32 nAbsCol = colIter->width; + sal_Int32 nExtraAbsCol = (nAbsCol * nExtraAbs) / + nAbsWidth; + nAbsCol += nExtraAbsCol; + colIter->width = nAbsCol; + nAbsLastCol -= nExtraAbsCol; + } + m_aColumnWidths.back().width = nAbsLastCol; + } + else if( nAbsWidth > m_nWidth ) + { + // If the table's width is smaller than the sum of the absolute + // column widths, every column needs to shrink. + // Every column gets the minimum width plus some extra width. + sal_Int32 nExtraAbs = m_nWidth - (nCols * MINLAY); + sal_Int32 nAbsLastCol = MINLAY + nExtraAbs; + for( auto colIter = m_aColumnWidths.begin(); colIter != (m_aColumnWidths.end() - 1); ++colIter ) + { + sal_Int32 nAbsCol = colIter->width; + sal_Int32 nExtraAbsCol = (nAbsCol * nExtraAbs) / + nAbsWidth; + nAbsCol = MINLAY + nExtraAbsCol; + colIter->width = nAbsCol; + nAbsLastCol -= nExtraAbsCol; + } + m_aColumnWidths.back().width = nAbsLastCol; + } + } + } + + SwTableLines& rLines = + pBox ? pBox->GetTabLines() + : m_pTableNode->GetTable().GetTabLines(); + + sal_uInt32 nStartRow = 0; + sal_uInt32 nRows = m_pRows->size(); + for(sal_uInt32 i=0; i<nRows; ++i ) + { + // Could we split the table behind the current line? + bool bSplit = true; + if ( m_bHasSubTables ) + { + SwXMLTableRow_Impl *pRow = (*m_pRows)[i].get(); + for( sal_uInt32 j=0; j<nCols; j++ ) + { + bSplit = ( 1 == pRow->GetCell(j)->GetRowSpan() ); + if( !bSplit ) + break; + } + } + + if( bSplit ) + { + SwTableLine *pLine = + MakeTableLine( pBox, nStartRow, 0UL, i+1, nCols ); + if( pBox || nStartRow>0 ) + rLines.push_back( pLine ); + nStartRow = i+1; + } + } +} + +void SwXMLTableContext::MakeTable() +{ + // this method will modify the document directly -> lock SolarMutex + // This will call all other MakeTable*(..) methods, so + // those don't need to be locked separately. + SolarMutexGuard aGuard; + + // #i97274# handle invalid tables + if (!m_pRows || m_pRows->empty() || !GetColumnCount()) + { + OSL_FAIL("invalid table: no cells; deleting..."); + m_pTableNode->GetDoc().getIDocumentContentOperations().DeleteSection( m_pTableNode ); + m_pTableNode = nullptr; + m_pBox1 = nullptr; + m_bOwnsBox1 = false; + m_pSttNd1 = nullptr; + return; + } + + SwXMLImport& rSwImport = GetSwImport(); + + SwFrameFormat *pFrameFormat = m_pTableNode->GetTable().GetFrameFormat(); + + sal_Int16 eHoriOrient = text::HoriOrientation::FULL; + bool bSetHoriOrient = false; + + sal_uInt8 nPercentWidth = 0U; + + OUString sStyleName; + SwStyleNameMapper::FillUIName( m_aTemplateName, sStyleName, SwGetPoolIdFromName::TabStyle ); + m_pTableNode->GetTable().SetTableStyleName( sStyleName ); + m_pTableNode->GetTable().SetRowsToRepeat( m_nHeaderRows ); + m_pTableNode->GetTable().SetTableModel( !m_bHasSubTables ); + + const SfxItemSet *pAutoItemSet = nullptr; + if( !m_aStyleName.isEmpty() && + rSwImport.FindAutomaticStyle( + XmlStyleFamily::TABLE_TABLE, m_aStyleName, &pAutoItemSet ) && + pAutoItemSet ) + { + const SvxLRSpaceItem *pLRSpace = + pAutoItemSet->GetItemIfSet( RES_LR_SPACE, false ); + + if( const SwFormatHoriOrient* pItem = pAutoItemSet->GetItemIfSet( RES_HORI_ORIENT, false ) ) + { + eHoriOrient = pItem->GetHoriOrient(); + switch( eHoriOrient ) + { + case text::HoriOrientation::FULL: + if( pLRSpace ) + { + eHoriOrient = text::HoriOrientation::NONE; + bSetHoriOrient = true; + } + break; + case text::HoriOrientation::LEFT: + if( pLRSpace ) + { + eHoriOrient = text::HoriOrientation::LEFT_AND_WIDTH; + bSetHoriOrient = true; + } + break; + default: + ; + } + } + else + { + bSetHoriOrient = true; + } + + const SwFormatFrameSize *pSize = + pAutoItemSet->GetItemIfSet( RES_FRM_SIZE, false ); + + switch( eHoriOrient ) + { + case text::HoriOrientation::FULL: + case text::HoriOrientation::NONE: + // For text::HoriOrientation::NONE we would prefer to use the sum + // of the relative column widths as reference width. + // Unfortunately this works only if this sum interpreted as + // twip value is larger than the space that is available. + // We don't know that space, so we have to use MAX_WIDTH, too. + // Even if a size is specified, it will be ignored! + m_nWidth = MAX_WIDTH; + break; + default: + if( pSize ) + { + if( pSize->GetWidthPercent() ) + { + // The width will be set in MakeTable_ + nPercentWidth = pSize->GetWidthPercent(); + } + else + { + m_nWidth = pSize->GetWidth(); + sal_Int32 const min = static_cast<sal_Int32>( + std::min<sal_uInt32>(GetColumnCount() * MINLAY, MAX_WIDTH)); + if( m_nWidth < min ) + { + m_nWidth = min; + } + else if( m_nWidth > MAX_WIDTH ) + { + m_nWidth = MAX_WIDTH; + } + m_bRelWidth = false; + } + } + else + { + eHoriOrient = text::HoriOrientation::LEFT_AND_WIDTH == eHoriOrient + ? text::HoriOrientation::NONE : text::HoriOrientation::FULL; + bSetHoriOrient = true; + m_nWidth = MAX_WIDTH; + } + break; + } + + pFrameFormat->SetFormatAttr( *pAutoItemSet ); + } + else + { + bSetHoriOrient = true; + m_nWidth = MAX_WIDTH; + } + + SwTableLine *pLine1 = m_pTableNode->GetTable().GetTabLines()[0U]; + assert(m_pBox1 == pLine1->GetTabBoxes()[0] && !m_bOwnsBox1 && "Why is box 1 change?"); + m_pBox1->m_pStartNode = m_pSttNd1; + pLine1->GetTabBoxes().erase( pLine1->GetTabBoxes().begin() ); + m_bOwnsBox1 = true; + + m_pLineFormat = static_cast<SwTableLineFormat*>(pLine1->GetFrameFormat()); + m_pBoxFormat = static_cast<SwTableBoxFormat*>(m_pBox1->GetFrameFormat()); + + MakeTable_(); + + if( bSetHoriOrient ) + pFrameFormat->SetFormatAttr( SwFormatHoriOrient( 0, eHoriOrient ) ); + + // This must be after the call to MakeTable_, because nWidth might be + // changed there. + pFrameFormat->LockModify(); + SwFormatFrameSize aSize( SwFrameSize::Variable, m_nWidth ); + aSize.SetWidthPercent( nPercentWidth ); + pFrameFormat->SetFormatAttr( aSize ); + pFrameFormat->UnlockModify(); + + for (std::unique_ptr<SwXMLTableRow_Impl> & rRow : *m_pRows) + rRow->Dispose(); + + // now that table is complete, change into DDE table (if appropriate) + if (m_xDDESource.is()) + { + // change existing table into DDE table: + // 1) Get DDE field type (get data from dde-source context), + SwDDEFieldType* pFieldType = lcl_GetDDEFieldType( m_xDDESource.get(), + m_pTableNode ); + + // 2) release the DDE source context, + m_xDDESource.clear(); + + // 3) create new DDE table, and + std::unique_ptr<SwDDETable> pDDETable( new SwDDETable( m_pTableNode->GetTable(), + pFieldType, false ) ); + + // 4) set new (DDE)table at node. + m_pTableNode->SetNewTable(std::move(pDDETable), false); + } + + // ??? this is always false: root frame is only created in SwViewShell::Init + if( m_pTableNode->GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell() ) + { + m_pTableNode->DelFrames(); + m_pTableNode->MakeOwnFrames(); + } +} + +void SwXMLTableContext::MakeTable( SwTableBox *pBox, sal_Int32 nW ) +{ + //FIXME: here would be a great place to handle XmlId for subtable + m_pLineFormat = GetParentTable()->m_pLineFormat; + m_pBoxFormat = GetParentTable()->m_pBoxFormat; + m_nWidth = nW; + m_bRelWidth = GetParentTable()->m_bRelWidth; + + MakeTable_( pBox ); + + for (std::unique_ptr<SwXMLTableRow_Impl> & rpRow : *m_pRows) + { + // i#113600, to break the cyclic reference to SwXMLTableContext object + rpRow->Dispose(); + } +} + +const SwStartNode *SwXMLTableContext::InsertTableSection( + const SwStartNode *const pPrevSttNd, + OUString const*const pStringValueStyleName) +{ + // The topmost table is the only table that maintains the two members + // pBox1 and bFirstSection. + if( m_xParentTable.is() ) + return m_xParentTable->InsertTableSection(pPrevSttNd, pStringValueStyleName); + + const SwStartNode *pStNd; + + if( m_bFirstSection ) + { + Reference<XInterface> xCursorTunnel( GetImport().GetTextImport()->GetCursor(), + UNO_QUERY); + OSL_ENSURE( xCursorTunnel.is(), "missing XUnoTunnel for Cursor" ); + OTextCursorHelper *pTextCursor = dynamic_cast<OTextCursorHelper*>(xCursorTunnel.get()); + assert(pTextCursor && "SwXTextCursor missing"); + + // The Cursor already is in the first section + pStNd = pTextCursor->GetPaM()->GetPointNode().FindTableBoxStartNode(); + m_bFirstSection = false; + GetImport().GetTextImport()->SetStyleAndAttrs( GetImport(), + GetImport().GetTextImport()->GetCursor(), "Standard", true ); + } + else + { + SwDoc* pDoc = GetSwImport().getDoc(); + const SwEndNode *pEndNd = pPrevSttNd ? pPrevSttNd->EndOfSectionNode() + : m_pTableNode->EndOfSectionNode(); + // #i78921# - make code robust + OSL_ENSURE( pDoc, "<SwXMLTableContext::InsertTableSection(..)> - no <pDoc> at <SwXTextCursor> instance - <SwXTextCurosr> doesn't seem to be registered at a <SwUnoCursor> instance." ); + if ( !pDoc ) + { + pDoc = &const_cast<SwDoc&>(pEndNd->GetDoc()); + } + SwNodeOffset nOffset(pPrevSttNd ? 1 : 0); + SwNodeIndex aIdx( *pEndNd, nOffset ); + SwTextFormatColl *pColl = + pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD, false ); + pStNd = pDoc->GetNodes().MakeTextSection( aIdx.GetNode(), SwTableBoxStartNode, + pColl ); + // Consider the case that a table is defined without a row. + if( !pPrevSttNd && m_pBox1 != nullptr ) + { + m_pBox1->m_pStartNode = pStNd; + SwContentNode *pCNd = pDoc->GetNodes()[ pStNd->GetIndex() + 1 ] + ->GetContentNode(); + SwFrameFormat *const pTableFormat = m_pTableNode->GetTable().GetFrameFormat(); + rtl::Reference<SwXCell> xParent = SwXCell::CreateXCell( pTableFormat, m_pBox1 ); + DBG_TESTSOLARMUTEX(); + SwPaM aPam(*pCNd, *pCNd); + rtl::Reference<SwXTextCursor> xTextCursor = + new SwXTextCursor(*pDoc, xParent, CursorType::TableText, + *aPam.GetPoint(), aPam.GetMark()); + GetImport().GetTextImport()->SetCursor( static_cast<XWordCursor*>(xTextCursor.get()) ); + } + } + + if (pStringValueStyleName) + { // fdo#62147: apply style to paragraph on string-value cell + GetImport().GetTextImport()->SetStyleAndAttrs( GetImport(), + GetImport().GetTextImport()->GetCursor(), *pStringValueStyleName, + true, false, -1, false); // parameters same as sCellParaStyleName + } + + return pStNd; +} + +void SwXMLTableContext::endFastElement(sal_Int32 ) +{ + if( IsValid() && !m_xParentTable.is() ) + { + MakeTable(); + GetImport().GetTextImport()->SetCursor( m_xOldCursor ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltbli.hxx b/sw/source/filter/xml/xmltbli.hxx new file mode 100644 index 0000000000..400fc8ba0c --- /dev/null +++ b/sw/source/filter/xml/xmltbli.hxx @@ -0,0 +1,214 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLTBLI_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLTBLI_HXX + +#include <xmloff/XMLTextTableContext.hxx> + +#include "xmlimp.hxx" + +#include <memory> +#include <unordered_map> +#include <vector> + +class SwXMLImport; +class SwTableNode; +class SwTableBox; +class SwTableLine; +class SwStartNode; +class SwTableBoxFormat; +class SwTableLineFormat; +class SwXMLTableCell_Impl; +class SwXMLTableRow_Impl; +typedef std::vector<std::unique_ptr<SwXMLTableRow_Impl>> SwXMLTableRows_Impl; +class SwXMLDDETableContext_Impl; +class TableBoxIndexHasher; +class TableBoxIndex; + +namespace com::sun::star { + namespace text { class XTextContent; } + namespace text { class XTextCursor; } +} + +class SwXMLTableContext : public XMLTextTableContext +{ + OUString m_aStyleName; + OUString m_aDfltCellStyleName; + OUString m_aTemplateName; + + //! Holds basic information about a column's width. + struct ColumnWidthInfo { + sal_uInt16 width; //!< Column width (absolute or relative). + bool isRelative; //!< True for a relative width, false for absolute. + ColumnWidthInfo(sal_uInt16 wdth, bool isRel) : width(wdth), isRelative(isRel) {}; + }; + std::vector<ColumnWidthInfo> m_aColumnWidths; + std::optional<std::vector<OUString>> m_xColumnDefaultCellStyleNames; + + css::uno::Reference< css::text::XTextCursor > m_xOldCursor; + css::uno::Reference< css::text::XTextContent > m_xTextContent; + + std::unique_ptr<SwXMLTableRows_Impl> m_pRows; + + SwTableNode *m_pTableNode; + SwTableBox *m_pBox1; + bool m_bOwnsBox1; + const SwStartNode *m_pSttNd1; + + SwTableBoxFormat *m_pBoxFormat; + SwTableLineFormat *m_pLineFormat; + + // hash map of shared format, indexed by the (XML) style name, + // the column width, and protection flag + typedef std::unordered_map<TableBoxIndex,SwTableBoxFormat*, + TableBoxIndexHasher> map_BoxFormat; + std::unique_ptr<map_BoxFormat> m_pSharedBoxFormats; + + rtl::Reference<SwXMLTableContext> m_xParentTable; // if table is a sub table + + rtl::Reference<SwXMLDDETableContext_Impl> m_xDDESource; + + bool m_bFirstSection : 1; + bool m_bRelWidth : 1; + bool m_bHasSubTables : 1; + + sal_uInt16 m_nHeaderRows; + sal_uInt32 m_nCurRow; + sal_uInt32 m_nCurCol; + /// Same as m_nCurCol, but not incremented multiple times for table cells with row span. + sal_uInt32 m_nNonMergedCurCol; + sal_Int32 m_nWidth; + + // The maximum table width (i.e., maximum value for m_nWidth); must be >= MINLAY and must also + // fit into ColumnWidthInfo::width (of type sal_uInt16), see e.g. the emplacement of + // MINLAY<=nWidth2<=MAX_WIDTH into m_aColumnWidths in SwXMLTableContext::InsertColumn: + static constexpr sal_Int32 MAX_WIDTH = SAL_MAX_UINT16; + + SwTableBox *NewTableBox( const SwStartNode *pStNd, + SwTableLine *pUpper ); + SwTableBox *MakeTableBox( SwTableLine *pUpper, + const SwXMLTableCell_Impl *pStartNode, + sal_uInt32 nLeftCol, sal_uInt32 nRightCol ); + SwTableBox *MakeTableBox( SwTableLine *pUpper, + sal_uInt32 nTopRow, sal_uInt32 nLeftCol, + sal_uInt32 nBottomRow, sal_uInt32 nRightCol ); + SwTableLine *MakeTableLine( SwTableBox *pUpper, + sal_uInt32 nTopRow, sal_uInt32 nLeftCol, + sal_uInt32 nBottomRow, sal_uInt32 nRightCol ); + + void MakeTable_( SwTableBox *pBox=nullptr ); + void MakeTable( SwTableBox *pBox, sal_Int32 nWidth ); + void MakeTable(); + + inline SwXMLTableContext *GetParentTable() const; + + const SwStartNode *GetPrevStartNode( sal_uInt32 nRow, + sal_uInt32 nCol ) const; + inline const SwStartNode *GetLastStartNode() const; + void FixRowSpan( sal_uInt32 nRow, sal_uInt32 nCol, sal_uInt32 nColSpan ); + void ReplaceWithEmptyCell( sal_uInt32 nRow, sal_uInt32 nCol, bool bRows ); + + /** sets the appropriate SwTableBoxFormat at pBox. */ + SwTableBoxFormat* GetSharedBoxFormat( + SwTableBox* pBox, /// the table box + const OUString& rStyleName, /// XML style name + sal_Int32 nColumnWidth, /// width of column + bool bProtected, /// is cell protected? + bool bMayShare, /// may the format be shared (no value, formula...) + bool& bNew, /// true, if the format it not from the cache + bool* pModifyLocked ); /// if set, call pBox->LockModify() and return old lock status + +public: + + + SwXMLTableContext( SwXMLImport& rImport, + const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ); + SwXMLTableContext( SwXMLImport& rImport, + SwXMLTableContext *pTable ); + + virtual ~SwXMLTableContext() override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 Element, + const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override; + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } + + void InsertColumn( sal_Int32 nWidth, bool bRelWidth, + const OUString *pDfltCellStyleName = nullptr ); + sal_Int32 GetColumnWidth( sal_uInt32 nCol, sal_uInt32 nColSpan ) const; + OUString GetColumnDefaultCellStyleName( sal_uInt32 nCol ) const; + inline sal_uInt32 GetColumnCount() const; + + bool IsInsertCellPossible() const { return m_nCurCol < GetColumnCount(); } + + /// Determines if it's OK to insert a covered cell, given the total column count. + bool IsInsertCoveredCellPossible() const { return m_nNonMergedCurCol < GetColumnCount(); } + + bool IsInsertColPossible() const { return m_nCurCol < USHRT_MAX; } + bool IsInsertRowPossible() const { return m_nCurRow < USHRT_MAX; } + bool IsValid() const { return m_pTableNode != nullptr; } + + void InsertCell( const OUString& rStyleName, + sal_uInt32 nRowSpan, sal_uInt32 nColSpan, + const SwStartNode *pStNd, + SwXMLTableContext *pTable=nullptr, + bool bIsProtected = false, + const OUString *pFormula=nullptr, + bool bHasValue = false, + double fValue = 0.0, + OUString const*const pStringValue = nullptr); + + /// Sets formatting of an already created covered cell. + void InsertCoveredCell(const OUString& rStyleName); + + void InsertRow( const OUString& rStyleName, + const OUString& rDfltCellStyleName, + bool bInHead ); + void FinishRow(); + void InsertRepRows( sal_uInt32 nCount ); + const SwXMLTableCell_Impl *GetCell( sal_uInt32 nRow, sal_uInt32 nCol ) const; + SwXMLTableCell_Impl *GetCell( sal_uInt32 nRow, sal_uInt32 nCol ); + const SwStartNode *InsertTableSection(const SwStartNode *pPrevSttNd = nullptr, + OUString const* pStringValueStyleName = nullptr); + + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + void SetHasSubTables( bool bNew ) { m_bHasSubTables = bNew; } +}; + +inline SwXMLTableContext *SwXMLTableContext::GetParentTable() const +{ + return m_xParentTable.get(); +} + +inline sal_uInt32 SwXMLTableContext::GetColumnCount() const +{ + return m_aColumnWidths.size(); +} + +inline const SwStartNode *SwXMLTableContext::GetLastStartNode() const +{ + return GetPrevStartNode( 0UL, GetColumnCount() ); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltext.cxx b/sw/source/filter/xml/xmltext.cxx new file mode 100644 index 0000000000..115dc1a523 --- /dev/null +++ b/sw/source/filter/xml/xmltext.cxx @@ -0,0 +1,77 @@ +/* -*- 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 "xmlimp.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; + +namespace { + +class SwXMLBodyContentContext_Impl : public SvXMLImportContext +{ + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } + +public: + + SwXMLBodyContentContext_Impl( SwXMLImport& rImport ); + + css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + + // The body element's text:global attribute can be ignored, because + // we must have the correct object shell already. + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; +}; + +} + +SwXMLBodyContentContext_Impl::SwXMLBodyContentContext_Impl( SwXMLImport& rImport ) : + SvXMLImportContext( rImport ) +{ +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLBodyContentContext_Impl::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + return GetSwImport().GetTextImport()->CreateTextChildContext( + GetImport(), nElement, xAttrList, + XMLTextType::Body ); +} + +void SwXMLBodyContentContext_Impl::endFastElement(sal_Int32 ) +{ + /* Code moved to SwXMLOmport::endDocument */ + GetImport().GetTextImport()->SetOutlineStyles( false ); +} + +SvXMLImportContext *SwXMLImport::CreateBodyContentContext() +{ + SvXMLImportContext *pContext = nullptr; + + if( !IsStylesOnlyMode() ) + pContext = new SwXMLBodyContentContext_Impl( *this ); + + return pContext; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltexte.cxx b/sw/source/filter/xml/xmltexte.cxx new file mode 100644 index 0000000000..9c68a12f70 --- /dev/null +++ b/sw/source/filter/xml/xmltexte.cxx @@ -0,0 +1,549 @@ +/* -*- 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 <sal/config.h> + +#include <comphelper/classids.hxx> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XLinkageSupport.hpp> +#include <com/sun/star/document/XEmbeddedObjectSupplier.hpp> +#include <xmloff/families.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/txtprmap.hxx> +#include <xmloff/maptype.hxx> +#include <xmloff/xmlexppr.hxx> + +#include <ndole.hxx> +#include <fmtcntnt.hxx> +#include <unoframe.hxx> +#include "xmlexp.hxx" +#include "xmltexte.hxx" +#include <SwAppletImpl.hxx> +#include <ndindex.hxx> + +#include <osl/diagnose.h> +#include <sot/exchange.hxx> +#include <svl/urihelper.hxx> +#include <sfx2/frmdescr.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::io; +using namespace ::xmloff::token; + +namespace { + +enum SvEmbeddedObjectTypes +{ + SV_EMBEDDED_OWN, + SV_EMBEDDED_OUTPLACE, + SV_EMBEDDED_APPLET, + SV_EMBEDDED_PLUGIN, + SV_EMBEDDED_FRAME +}; + +} + +SwNoTextNode *SwXMLTextParagraphExport::GetNoTextNode( + const Reference < XPropertySet >& rPropSet ) +{ + SwXFrame* pFrame = dynamic_cast<SwXFrame*>(rPropSet.get()); + assert(pFrame && "SwXFrame missing"); + SwFrameFormat *pFrameFormat = pFrame->GetFrameFormat(); + const SwFormatContent& rContent = pFrameFormat->GetContent(); + const SwNodeIndex *pNdIdx = rContent.GetContentIdx(); + return pNdIdx->GetNodes()[pNdIdx->GetIndex() + 1]->GetNoTextNode(); +} + +constexpr OUString gsEmbeddedObjectProtocol( u"vnd.sun.star.EmbeddedObject:"_ustr ); + +SwXMLTextParagraphExport::SwXMLTextParagraphExport( + SwXMLExport& rExp, + SvXMLAutoStylePoolP& _rAutoStylePool ) : + XMLTextParagraphExport( rExp, _rAutoStylePool ), + m_aAppletClassId( SO3_APPLET_CLASSID ), + m_aPluginClassId( SO3_PLUGIN_CLASSID ), + m_aIFrameClassId( SO3_IFRAME_CLASSID ) +{ +} + +SwXMLTextParagraphExport::~SwXMLTextParagraphExport() +{ +} + +static void lcl_addURL ( SvXMLExport &rExport, const OUString &rURL, + bool bToRel = true ) +{ + const OUString sRelURL = ( bToRel && !rURL.isEmpty() ) + ? URIHelper::simpleNormalizedMakeRelative(rExport.GetOrigFileName(), rURL) + : rURL; + + if (!sRelURL.isEmpty()) + { + rExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_HREF, sRelURL ); + rExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + rExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED ); + rExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD ); + } +} + +static void lcl_addAspect( + const svt::EmbeddedObjectRef& rObj, + std::vector<XMLPropertyState>& rStates, + const rtl::Reference < XMLPropertySetMapper >& rMapper ) +{ + sal_Int64 nAspect = rObj.GetViewAspect(); + if ( nAspect ) + rStates.emplace_back( rMapper->FindEntryIndex( CTF_OLE_DRAW_ASPECT ), uno::Any( nAspect ) ); +} + +static void lcl_addOutplaceProperties( + const svt::EmbeddedObjectRef& rObj, + std::vector<XMLPropertyState>& rStates, + const rtl::Reference < XMLPropertySetMapper >& rMapper ) +{ + MapMode aMode( MapUnit::Map100thMM ); // the API expects this map mode for the embedded objects + Size aSize = rObj.GetSize( &aMode ); // get the size in the requested map mode + + if( !(aSize.Width() && aSize.Height()) ) + return; + + rStates.emplace_back( rMapper->FindEntryIndex( CTF_OLE_VIS_AREA_LEFT ), Any(sal_Int32(0)) ); + rStates.emplace_back( rMapper->FindEntryIndex( CTF_OLE_VIS_AREA_TOP ), Any(sal_Int32(0)) ); + rStates.emplace_back( rMapper->FindEntryIndex( CTF_OLE_VIS_AREA_WIDTH ), Any(static_cast<sal_Int32>(aSize.Width())) ); + rStates.emplace_back( rMapper->FindEntryIndex( CTF_OLE_VIS_AREA_HEIGHT ), Any(static_cast<sal_Int32>(aSize.Height())) ); +} + +static void lcl_addFrameProperties( + const uno::Reference < embed::XEmbeddedObject >& xObj, + std::vector<XMLPropertyState>& rStates, + const rtl::Reference < XMLPropertySetMapper >& rMapper ) +{ + if ( !::svt::EmbeddedObjectRef::TryRunningState( xObj ) ) + return; + + uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY ); + if ( !xSet.is() ) + return; + + bool bIsAutoScroll = false, bIsScrollingMode = false; + Any aAny = xSet->getPropertyValue("FrameIsAutoScroll"); + aAny >>= bIsAutoScroll; + if ( !bIsAutoScroll ) + { + aAny = xSet->getPropertyValue("FrameIsScrollingMode"); + aAny >>= bIsScrollingMode; + } + + bool bIsBorderSet = false, bIsAutoBorder = false; + aAny = xSet->getPropertyValue("FrameIsAutoBorder"); + aAny >>= bIsAutoBorder; + if ( !bIsAutoBorder ) + { + aAny = xSet->getPropertyValue("FrameIsBorder"); + aAny >>= bIsBorderSet; + } + + sal_Int32 nWidth, nHeight; + aAny = xSet->getPropertyValue("FrameMarginWidth"); + aAny >>= nWidth; + aAny = xSet->getPropertyValue("FrameMarginHeight"); + aAny >>= nHeight; + + if( !bIsAutoScroll ) + rStates.emplace_back( rMapper->FindEntryIndex( CTF_FRAME_DISPLAY_SCROLLBAR ), Any(bIsScrollingMode) ); + if( !bIsAutoBorder ) + rStates.emplace_back( rMapper->FindEntryIndex( CTF_FRAME_DISPLAY_BORDER ), Any(bIsBorderSet) ); + if( SIZE_NOT_SET != nWidth ) + rStates.emplace_back( rMapper->FindEntryIndex( CTF_FRAME_MARGIN_HORI ), Any(nWidth) ); + if( SIZE_NOT_SET != nHeight ) + rStates.emplace_back( rMapper->FindEntryIndex( CTF_FRAME_MARGIN_VERT ), Any(nHeight) ); +} + +void SwXMLTextParagraphExport::_collectTextEmbeddedAutoStyles( + const Reference < XPropertySet > & rPropSet ) +{ + SwOLENode *pOLENd = GetNoTextNode( rPropSet )->GetOLENode(); + svt::EmbeddedObjectRef& rObjRef = pOLENd->GetOLEObj().GetObject(); + if( !rObjRef.is() ) + return; + + std::vector<XMLPropertyState> aStates; + aStates.reserve(8); + SvGlobalName aClassId( rObjRef->getClassID() ); + + if( m_aIFrameClassId == aClassId ) + { + lcl_addFrameProperties( rObjRef.GetObject(), aStates, + GetAutoFramePropMapper()->getPropertySetMapper() ); + } + else if ( !SotExchange::IsInternal( aClassId ) ) + { + lcl_addOutplaceProperties( rObjRef, aStates, + GetAutoFramePropMapper()->getPropertySetMapper() ); + } + + lcl_addAspect( rObjRef, aStates, + GetAutoFramePropMapper()->getPropertySetMapper() ); + + Add( XmlStyleFamily::TEXT_FRAME, rPropSet, aStates ); +} + +void SwXMLTextParagraphExport::_exportTextEmbedded( + const Reference < XPropertySet > & rPropSet, + const Reference < XPropertySetInfo > & rPropSetInfo ) +{ + SwOLENode *pOLENd = GetNoTextNode( rPropSet )->GetOLENode(); + SwOLEObj& rOLEObj = pOLENd->GetOLEObj(); + svt::EmbeddedObjectRef& rObjRef = rOLEObj.GetObject(); + if( !rObjRef.is() ) + return; + + SvGlobalName aClassId( rObjRef->getClassID() ); + + SvEmbeddedObjectTypes nType = SV_EMBEDDED_OWN; + if( m_aPluginClassId == aClassId ) + { + nType = SV_EMBEDDED_PLUGIN; + } + else if( m_aAppletClassId == aClassId ) + { + nType = SV_EMBEDDED_APPLET; + } + else if( m_aIFrameClassId == aClassId ) + { + nType = SV_EMBEDDED_FRAME; + } + else if ( !SotExchange::IsInternal( aClassId ) ) + { + nType = SV_EMBEDDED_OUTPLACE; + } + + enum XMLTokenEnum eElementName = XML__UNKNOWN_; + SvXMLExport &rXMLExport = GetExport(); + + // First the stuff common to each of Applet/Plugin/Floating Frame + OUString sStyle; + Any aAny; + if( rPropSetInfo->hasPropertyByName( gsFrameStyleName ) ) + { + aAny = rPropSet->getPropertyValue( gsFrameStyleName ); + aAny >>= sStyle; + } + + std::vector<XMLPropertyState> aStates; + aStates.reserve(8); + switch( nType ) + { + case SV_EMBEDDED_FRAME: + lcl_addFrameProperties( rObjRef.GetObject(), aStates, + GetAutoFramePropMapper()->getPropertySetMapper() ); + break; + case SV_EMBEDDED_OUTPLACE: + lcl_addOutplaceProperties( rObjRef, aStates, + GetAutoFramePropMapper()->getPropertySetMapper() ); + break; + default: + ; + } + + lcl_addAspect( rObjRef, aStates, + GetAutoFramePropMapper()->getPropertySetMapper() ); + + const OUString sAutoStyle = Find( XmlStyleFamily::TEXT_FRAME, + rPropSet, sStyle, aStates ); + aStates.clear(); + + if( !sAutoStyle.isEmpty() ) + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_STYLE_NAME, sAutoStyle ); + addTextFrameAttributes( rPropSet, false ); + + SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_DRAW, + XML_FRAME, false, true ); + + switch (nType) + { + case SV_EMBEDDED_OUTPLACE: + case SV_EMBEDDED_OWN: + if( !(rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED) ) + { + OUString sURL; + + bool bIsOwnLink = false; + if( SV_EMBEDDED_OWN == nType ) + { + try + { + uno::Reference< embed::XLinkageSupport > xLinkage( rObjRef.GetObject(), uno::UNO_QUERY ); + bIsOwnLink = xLinkage.is() && xLinkage->isLink(); + if ( bIsOwnLink ) + sURL = xLinkage->getLinkURL(); + } + catch(const uno::Exception&) + { + // TODO/LATER: error handling + OSL_FAIL( "Link detection or retrieving of the URL of OOo link is failed!" ); + } + } + + if ( !bIsOwnLink ) + { + sURL = gsEmbeddedObjectProtocol + rOLEObj.GetCurrentPersistName(); + } + + sURL = GetExport().AddEmbeddedObject( sURL ); + lcl_addURL( rXMLExport, sURL, false ); + } + if( SV_EMBEDDED_OWN == nType && !pOLENd->GetChartTableName().isEmpty() ) + { + OUString sRange( pOLENd->GetChartTableName() ); + OUStringBuffer aBuffer( sRange.getLength() + 2 ); + for( sal_Int32 i=0; i < sRange.getLength(); i++ ) + { + sal_Unicode c = sRange[i]; + switch( c ) + { + case ' ': + case '.': + case '\'': + case '\\': + if( aBuffer.isEmpty() ) + { + aBuffer.append( OUString::Concat("\'") + sRange.subView(0, i) ); + } + if( '\'' == c || '\\' == c ) + aBuffer.append( '\\' ); + [[fallthrough]]; + default: + if( !aBuffer.isEmpty() ) + aBuffer.append( c ); + } + } + if( !aBuffer.isEmpty() ) + { + aBuffer.append( '\'' ); + sRange = aBuffer.makeStringAndClear(); + } + + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NOTIFY_ON_UPDATE_OF_RANGES, + sRange ); + } + eElementName = SV_EMBEDDED_OUTPLACE==nType ? XML_OBJECT_OLE + : XML_OBJECT; + break; + case SV_EMBEDDED_APPLET: + { + // It's an applet! + if( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) ) + { + uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY ); + OUString aStr; + Any aAny2 = xSet->getPropertyValue("AppletCodeBase"); + aAny2 >>= aStr; + if (!aStr.isEmpty() ) + lcl_addURL(rXMLExport, aStr); + + aAny2 = xSet->getPropertyValue("AppletName"); + aAny2 >>= aStr; + if (!aStr.isEmpty()) + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_APPLET_NAME, aStr ); + + aAny2 = xSet->getPropertyValue("AppletCode"); + aAny2 >>= aStr; + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CODE, aStr ); + + bool bScript = false; + aAny2 = xSet->getPropertyValue("AppletIsScript"); + aAny2 >>= bScript; + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_MAY_SCRIPT, bScript ? XML_TRUE : XML_FALSE ); + + uno::Sequence < beans::PropertyValue > aProps; + aAny2 = xSet->getPropertyValue("AppletCommands"); + aAny2 >>= aProps; + + sal_Int32 i = aProps.getLength(); + while ( i > 0 ) + { + const beans::PropertyValue& aProp = aProps[--i]; + const SwHtmlOptType nType2 = SwApplet_Impl::GetOptionType( aProp.Name, true ); + if ( nType2 == SwHtmlOptType::TAG) + { + OUString aStr2; + aProp.Value >>= aStr2; + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, aProp.Name, aStr2); + } + } + + eElementName = XML_APPLET; + } + } + break; + case SV_EMBEDDED_PLUGIN: + { + // It's a plugin! + if ( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) ) + { + uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY ); + OUString aStr; + Any aAny2 = xSet->getPropertyValue("PluginURL"); + aAny2 >>= aStr; + lcl_addURL( rXMLExport, aStr ); + + aAny2 = xSet->getPropertyValue("PluginMimeType"); + aAny2 >>= aStr; + if (!aStr.isEmpty()) + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_MIME_TYPE, aStr ); + eElementName = XML_PLUGIN; + } + } + break; + case SV_EMBEDDED_FRAME: + { + // It's a floating frame! + if ( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) ) + { + uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY ); + OUString aStr; + Any aAny2 = xSet->getPropertyValue("FrameURL"); + aAny2 >>= aStr; + + lcl_addURL( rXMLExport, aStr ); + + aAny2 = xSet->getPropertyValue("FrameName"); + aAny2 >>= aStr; + + if (!aStr.isEmpty()) + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_FRAME_NAME, aStr ); + eElementName = XML_FLOATING_FRAME; + } + } + break; + default: + OSL_ENSURE( false, "unknown object type! Base class should have been called!" ); + } + + { + SvXMLElementExport aElementExport( rXMLExport, XML_NAMESPACE_DRAW, eElementName, + false, true ); + switch( nType ) + { + case SV_EMBEDDED_OWN: + if( rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED ) + { + Reference < XEmbeddedObjectSupplier > xEOS( rPropSet, UNO_QUERY ); + OSL_ENSURE( xEOS.is(), "no embedded object supplier for own object" ); + Reference < XComponent > xComp = xEOS->getEmbeddedObject(); + rXMLExport.ExportEmbeddedOwnObject( xComp ); + } + break; + case SV_EMBEDDED_OUTPLACE: + if( rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED ) + { + OUString sURL( gsEmbeddedObjectProtocol + rOLEObj.GetCurrentPersistName() ); + + if ( !( rXMLExport.getExportFlags() & SvXMLExportFlags::OASIS ) ) + sURL += "?oasis=false"; + + rXMLExport.AddEmbeddedObjectAsBase64( sURL ); + } + break; + case SV_EMBEDDED_APPLET: + { + if ( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) ) + { + uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY ); + uno::Sequence < beans::PropertyValue > aProps; + aAny = xSet->getPropertyValue("AppletCommands"); + aAny >>= aProps; + + sal_Int32 i = aProps.getLength(); + while ( i > 0 ) + { + const beans::PropertyValue& aProp = aProps[--i]; + const SwHtmlOptType nType2 = SwApplet_Impl::GetOptionType( aProp.Name, true ); + if (SwHtmlOptType::PARAM == nType2 || SwHtmlOptType::SIZE == nType2 ) + { + OUString aStr; + aProp.Value >>= aStr; + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, aProp.Name ); + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_VALUE, aStr ); + SvXMLElementExport aElementExport2( rXMLExport, XML_NAMESPACE_DRAW, XML_PARAM, false, true ); + } + } + } + } + break; + case SV_EMBEDDED_PLUGIN: + { + if ( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) ) + { + uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY ); + uno::Sequence < beans::PropertyValue > aProps; + aAny = xSet->getPropertyValue("PluginCommands"); + aAny >>= aProps; + + sal_Int32 i = aProps.getLength(); + while ( i > 0 ) + { + const beans::PropertyValue& aProp = aProps[--i]; + const SwHtmlOptType nType2 = SwApplet_Impl::GetOptionType( aProp.Name, false ); + if ( nType2 == SwHtmlOptType::TAG) + { + OUString aStr; + aProp.Value >>= aStr; + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, aProp.Name ); + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_VALUE, aStr ); + SvXMLElementExport aElementExport2( rXMLExport, XML_NAMESPACE_DRAW, XML_PARAM, false, true ); + } + } + } + } + break; + default: + break; + } + } + if( SV_EMBEDDED_OUTPLACE==nType || SV_EMBEDDED_OWN==nType ) + { + OUString sURL = XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE + rOLEObj.GetCurrentPersistName(); + if( !(rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED) ) + { + sURL = GetExport().AddEmbeddedObject( sURL ); + lcl_addURL( rXMLExport, sURL, false ); + } + + SvXMLElementExport aElementExport( GetExport(), XML_NAMESPACE_DRAW, + XML_IMAGE, false, true ); + + if( rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED ) + GetExport().AddEmbeddedObjectAsBase64( sURL ); + } + + // Lastly the stuff common to each of Applet/Plugin/Floating Frame + exportEvents( rPropSet ); + exportTitleAndDescription( rPropSet, rPropSetInfo ); // #i73249# + exportContour( rPropSet, rPropSetInfo ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltexte.hxx b/sw/source/filter/xml/xmltexte.hxx new file mode 100644 index 0000000000..09ce6c46f4 --- /dev/null +++ b/sw/source/filter/xml/xmltexte.hxx @@ -0,0 +1,84 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTE_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTE_HXX + +#include <xmloff/txtparae.hxx> +#include <tools/globname.hxx> + +#include <optional> +#include <unordered_map> + +#define XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE "vnd.sun.star.GraphicObject:" + +class SwXMLExport; +class SvXMLAutoStylePoolP; +class SwNoTextNode; +class SwTableNode; +class SwTableLines; +namespace com::sun::star::style { class XStyle; } + +class SwXMLTextParagraphExport : public XMLTextParagraphExport +{ + const SvGlobalName m_aAppletClassId; + const SvGlobalName m_aPluginClassId; + const SvGlobalName m_aIFrameClassId; + + // Collected autostyles for use in exportTextAutoStyles + std::vector<const SwTableNode*> maTableNodes; +public: + typedef ::std::unordered_map<SwFrameFormat const*, ::std::optional<OUString>> FormatMap; +private: + ::std::unordered_map<SwTableNode const*, ::std::pair<FormatMap, FormatMap>> m_TableFormats; + + static SwNoTextNode *GetNoTextNode( + const css::uno::Reference < css::beans::XPropertySet >& rPropSet ); + + void CollectTableLinesAutoStyles(const SwTableLines& rLines, SwFrameFormat& rFormat, + bool bProgress); + +protected: + virtual void _collectTextEmbeddedAutoStyles( + const css::uno::Reference< css::beans::XPropertySet > & rPropSet ) override; + virtual void _exportTextEmbedded( + const css::uno::Reference< css::beans::XPropertySet > & rPropSet, + const css::uno::Reference< css::beans::XPropertySetInfo > & rPropSetInfo ) override; + + virtual void exportTable( + const css::uno::Reference< css::text::XTextContent > & rTextContent, + bool bAutoStyles, bool bProgress ) override; + + virtual void exportTableAutoStyles() override; + +public: + SwXMLTextParagraphExport( + SwXMLExport& rExp, + SvXMLAutoStylePoolP& rAutoStylePool ); + virtual ~SwXMLTextParagraphExport() override; + + ::std::unordered_map<SwTableNode const*, ::std::pair<FormatMap, FormatMap>> const& + GetTableFormats() const { return m_TableFormats; } + ::std::unordered_map<SwTableNode const*, ::std::pair<FormatMap, FormatMap>> & + GetTableFormats() { return m_TableFormats; } +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltexti.cxx b/sw/source/filter/xml/xmltexti.cxx new file mode 100644 index 0000000000..508cb428dc --- /dev/null +++ b/sw/source/filter/xml/xmltexti.cxx @@ -0,0 +1,997 @@ +/* -*- 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 <comphelper/storagehelper.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/servicehelper.hxx> +#include <com/sun/star/embed/EmbeddedObjectCreator.hpp> +#include <com/sun/star/embed/OOoEmbeddedObjectFactory.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <o3tl/any.hxx> +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <comphelper/classids.hxx> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <xmloff/prstylei.hxx> +#include <xmloff/maptype.hxx> +#include <xmloff/xmlprmap.hxx> +#include <xmloff/txtprmap.hxx> +#include <xmloff/i18nmap.hxx> +#include <xmloff/xmlimppr.hxx> +#include <TextCursorHelper.hxx> +#include <unoframe.hxx> +#include <doc.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentContentOperations.hxx> +#include <fmtfsize.hxx> +#include <fmtanchr.hxx> +#include <fmtcntnt.hxx> +#include "xmlimp.hxx" +#include "xmltbli.hxx" +#include "xmltexti.hxx" +#include "XMLRedlineImportHelper.hxx" +#include <xmloff/XMLFilterServiceNames.h> +#include <SwAppletImpl.hxx> +#include <ndole.hxx> +#include <docsh.hxx> +#include <sfx2/docfile.hxx> +#include <vcl/svapp.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <svtools/embedhlp.hxx> +#include <svl/urihelper.hxx> +#include <sfx2/frmdescr.hxx> +#include <tools/globname.hxx> + +#include <algorithm> +#include <utility> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::beans; +using namespace xml::sax; + +const std::pair<OUString, SvGUID> aServiceMap[] = { + { XML_IMPORT_FILTER_WRITER, { SO3_SW_CLASSID } }, + { XML_IMPORT_FILTER_CALC, { SO3_SC_CLASSID } }, + { XML_IMPORT_FILTER_DRAW, { SO3_SDRAW_CLASSID } }, + { XML_IMPORT_FILTER_IMPRESS, { SO3_SIMPRESS_CLASSID } }, + { XML_IMPORT_FILTER_CHART, { SO3_SCH_CLASSID } }, + { XML_IMPORT_FILTER_MATH, { SO3_SM_CLASSID } }, +}; +static void lcl_putHeightAndWidth ( SfxItemSet &rItemSet, + sal_Int32 nHeight, sal_Int32 nWidth, + Size *pTwipSize = nullptr ) +{ + if( nWidth > 0 && nHeight > 0 ) + { + nWidth = o3tl::toTwips(nWidth, o3tl::Length::mm100); + if( nWidth < MINFLY ) + nWidth = MINFLY; + nHeight = o3tl::toTwips(nHeight, o3tl::Length::mm100); + if( nHeight < MINFLY ) + nHeight = MINFLY; + rItemSet.Put( SwFormatFrameSize( SwFrameSize::Fixed, nWidth, nHeight ) ); + } + + SwFormatAnchor aAnchor( RndStdIds::FLY_AT_CHAR ); + rItemSet.Put( aAnchor ); + + if( pTwipSize ) + { + pTwipSize->setWidth( nWidth ); + pTwipSize->setHeight( nHeight); + } +} + +static void lcl_setObjectVisualArea( const uno::Reference< embed::XEmbeddedObject >& xObj, + sal_Int64 nAspect, + const Size& aVisSize, + const MapUnit& aUnit ) +{ + if( !(xObj.is() && nAspect != embed::Aspects::MSOLE_ICON) ) + return; + + // convert the visual area to the objects units + MapUnit aObjUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) ); + Size aObjVisSize = OutputDevice::LogicToLogic(aVisSize, MapMode(aUnit), MapMode(aObjUnit)); + awt::Size aSz; + aSz.Width = aObjVisSize.Width(); + aSz.Height = aObjVisSize.Height(); + + try + { + xObj->setVisualAreaSize( nAspect, aSz ); + } + catch( uno::Exception& ) + { + OSL_FAIL( "Couldn't set visual area of the object!" ); + } +} + +SwXMLTextImportHelper::SwXMLTextImportHelper( + const uno::Reference < XModel>& rModel, + SwXMLImport& rImport, + const uno::Reference<XPropertySet> & rInfoSet, + bool bInsertM, bool bStylesOnlyM, + bool bBlockM, bool bOrganizerM ) : + XMLTextImportHelper( rModel, rImport, bInsertM, bStylesOnlyM, true/*bProgress*/, + bBlockM, bOrganizerM ), + m_pRedlineHelper( nullptr ) +{ + uno::Reference<XPropertySet> xDocPropSet( rModel, UNO_QUERY ); + m_pRedlineHelper = new XMLRedlineImportHelper(rImport, + bInsertM || bBlockM, xDocPropSet, rInfoSet ); +} + +SwXMLTextImportHelper::~SwXMLTextImportHelper() +{ + // the redline helper destructor sets properties on the document + // and may throw an exception while doing so... catch this + try + { + delete m_pRedlineHelper; + } + catch ( const RuntimeException& ) + { + // ignore + } +} + +SvXMLImportContext *SwXMLTextImportHelper::CreateTableChildContext( + SvXMLImport& rImport, + sal_Int32 /*nElement*/, + const uno::Reference< XFastAttributeList > & xAttrList ) +{ + return new SwXMLTableContext( static_cast<SwXMLImport&>(rImport), xAttrList ); +} + +bool SwXMLTextImportHelper::IsInHeaderFooter() const +{ + OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(const_cast<SwXMLTextImportHelper *>(this)->GetCursor().get()); + SAL_WARN_IF(!pTextCursor, "sw.uno", "SwXTextCursor missing"); + SwDoc *pDoc = pTextCursor ? pTextCursor->GetDoc() : nullptr; + + return pDoc && pDoc->IsInHeaderFooter( pTextCursor->GetPaM()->GetPoint()->GetNode() ); +} + +static SwOLENode *lcl_GetOLENode( const SwFrameFormat *pFrameFormat ) +{ + SwOLENode *pOLENd = nullptr; + if( pFrameFormat ) + { + const SwFormatContent& rContent = pFrameFormat->GetContent(); + const SwNodeIndex *pNdIdx = rContent.GetContentIdx(); + pOLENd = pNdIdx->GetNodes()[pNdIdx->GetIndex() + 1]->GetOLENode(); + } + OSL_ENSURE( pOLENd, "Where is the OLE node" ); + return pOLENd; +} + +uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertOLEObject( + SvXMLImport& rImport, + const OUString& rHRef, + const OUString& rStyleName, + const OUString& rTableName, + sal_Int32 nWidth, sal_Int32 nHeight ) +{ + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + uno::Reference < XPropertySet > xPropSet; + + sal_Int32 nPos = rHRef.indexOf( ':' ); + if( -1 == nPos ) + return xPropSet; + + OUString aObjName( rHRef.copy( nPos+1) ); + + if( aObjName.isEmpty() ) + return xPropSet; + + OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(GetCursor().get()); + SAL_WARN_IF(!pTextCursor, "sw.uno", "SwXTextCursor missing"); + SwDoc *pDoc = static_cast<SwXMLImport&>(rImport).getDoc(); + + SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END> aItemSet( pDoc->GetAttrPool() ); + Size aTwipSize( 0, 0 ); + tools::Rectangle aVisArea( 0, 0, nWidth, nHeight ); + lcl_putHeightAndWidth( aItemSet, nHeight, nWidth, + &aTwipSize ); + + SwFrameFormat *pFrameFormat = nullptr; + SwOLENode *pOLENd = nullptr; + if( rHRef.startsWith("vnd.sun.star.ServiceName:") ) + { + bool bInsert = false; + SvGlobalName aClassName; + for (const auto& [sFilterService, rCLASSID] : aServiceMap) + { + if (aObjName == sFilterService) + { + aClassName = SvGlobalName(rCLASSID); + bInsert = true; + break; + } + } + + if( bInsert ) + { + uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage(); + try + { + // create object with desired ClassId + uno::Sequence < sal_Int8 > aClass( aClassName.GetByteSequence() ); + uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() ); + uno::Sequence<beans::PropertyValue> aObjArgs( comphelper::InitPropertySequence({ + { "DefaultParentBaseURL", Any(GetXMLImport().GetBaseURL()) } + })); + uno::Reference < embed::XEmbeddedObject > xObj( xFactory->createInstanceInitNew( + aClass, OUString(), xStorage, "DummyName", aObjArgs), uno::UNO_QUERY ); + if ( xObj.is() ) + { + //TODO/LATER: is it enough to only set the VisAreaSize? + lcl_setObjectVisualArea( xObj, embed::Aspects::MSOLE_CONTENT, aTwipSize, MapUnit::MapTwip ); + } + + if( pTextCursor ) + { + pFrameFormat = pDoc->getIDocumentContentOperations().InsertEmbObject( + *pTextCursor->GetPaM(), + ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT), + &aItemSet); + pOLENd = lcl_GetOLENode( pFrameFormat ); + } + + if( pOLENd ) + aObjName = pOLENd->GetOLEObj().GetCurrentPersistName(); + } + catch ( uno::Exception& ) + { + } + } + } + else + { + // check whether an object with this name already exists in the document + OUString aName; + SwIterator<SwContentNode,SwFormatColl> aIter( *pDoc->GetDfltGrfFormatColl() ); + for( SwContentNode* pNd = aIter.First(); pNd; pNd = aIter.Next() ) + { + SwOLENode* pExistingOLENd = pNd->GetOLENode(); + if( pExistingOLENd ) + { + OUString aExistingName = pExistingOLENd->GetOLEObj().GetCurrentPersistName(); + if ( aExistingName == aObjName ) + { + OSL_FAIL( "The document contains duplicate object references, means it is partially broken, please let developers know how this document was generated!" ); + + OUString aTmpName = pDoc->GetPersist()->GetEmbeddedObjectContainer().CreateUniqueObjectName(); + try + { + pDoc->GetPersist()->GetStorage()->copyElementTo( aObjName, + pDoc->GetPersist()->GetStorage(), + aTmpName ); + aName = aTmpName; + } + catch ( uno::Exception& ) + { + OSL_FAIL( "Couldn't create a copy of the object!" ); + } + + break; + } + } + } + + if ( aName.isEmpty() ) + aName = aObjName; + + // the correct aspect will be set later + // TODO/LATER: Actually it should be set here + if( pTextCursor ) + { + pFrameFormat = pDoc->getIDocumentContentOperations().InsertOLE( *pTextCursor->GetPaM(), aName, embed::Aspects::MSOLE_CONTENT, &aItemSet, nullptr ); + pOLENd = lcl_GetOLENode( pFrameFormat ); + } + aObjName = aName; + } + + if( !pFrameFormat ) + return xPropSet; + + if( IsInsertMode() ) + { + if( !pOLENd ) + pOLENd = lcl_GetOLENode( pFrameFormat ); + if( pOLENd ) + pOLENd->SetOLESizeInvalid( true ); + } + + xPropSet = SwXTextEmbeddedObject::CreateXTextEmbeddedObject( + *pDoc, pFrameFormat); + if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() ) + { + // req for z-order + SwXFrame::GetOrCreateSdrObject(*static_cast<SwFlyFrameFormat*>(pFrameFormat)); + } + if( !rTableName.isEmpty() ) + { + const SwFormatContent& rContent = pFrameFormat->GetContent(); + const SwNodeIndex *pNdIdx = rContent.GetContentIdx(); + SwOLENode *pOLENode = pNdIdx->GetNodes()[pNdIdx->GetIndex() + 1]->GetOLENode(); + OSL_ENSURE( pOLENode, "Where is the OLE node" ); + + OUStringBuffer aBuffer( rTableName.getLength() ); + bool bQuoted = false; + bool bEscape = false; + bool bError = false; + for( sal_Int32 i=0; i < rTableName.getLength(); i++ ) + { + bool bEndOfNameFound = false; + sal_Unicode c = rTableName[i]; + switch( c ) + { + case '\'': + if( bEscape ) + { + aBuffer.append( c ); + bEscape = false; + } + else if( bQuoted ) + { + bEndOfNameFound = true; + } + else if( 0 == i ) + { + bQuoted = true; + } + else + { + bError = true; + } + break; + case '\\': + if( bEscape ) + { + aBuffer.append( c ); + bEscape = false; + } + else + { + bEscape = true; + } + break; + case ' ': + case '.': + if( !bQuoted ) + { + bEndOfNameFound = true; + } + else + { + aBuffer.append( c ); + bEscape = false; + } + break; + default: + { + aBuffer.append( c ); + bEscape = false; + } + break; + } + if( bError || bEndOfNameFound ) + break; + } + if( !bError ) + { + OUString sTableName( aBuffer.makeStringAndClear() ); + pOLENode->SetChartTableName( GetRenameMap().Get( XML_TEXT_RENAME_TYPE_TABLE, sTableName ) ); + } + } + + sal_Int64 nDrawAspect = 0; + const XMLPropStyleContext *pStyle = nullptr; + bool bHasSizeProps = false; + if( !rStyleName.isEmpty() ) + { + pStyle = FindAutoFrameStyle( rStyleName ); + if( pStyle ) + { + rtl::Reference < SvXMLImportPropertyMapper > xImpPrMap = + pStyle->GetStyles() + ->GetImportPropertyMapper(pStyle->GetFamily()); + OSL_ENSURE( xImpPrMap.is(), "Where is the import prop mapper?" ); + if( xImpPrMap.is() ) + { + rtl::Reference<XMLPropertySetMapper> rPropMapper = + xImpPrMap->getPropertySetMapper(); + + sal_Int32 nCount = pStyle->GetProperties().size(); + for( sal_Int32 i=0; i < nCount; i++ ) + { + const XMLPropertyState& rProp = pStyle->GetProperties()[i]; + sal_Int32 nIdx = rProp.mnIndex; + if( -1 == nIdx ) + continue; + + switch( rPropMapper->GetEntryContextId(nIdx) ) + { + case CTF_OLE_VIS_AREA_LEFT: + { + sal_Int32 nVal = 0; + rProp.maValue >>= nVal; + aVisArea.SetPosX( nVal ); + } + break; + case CTF_OLE_VIS_AREA_TOP: + { + sal_Int32 nVal = 0; + rProp.maValue >>= nVal; + aVisArea.SetPosY( nVal ); + } + break; + case CTF_OLE_VIS_AREA_WIDTH: + { + sal_Int32 nVal = 0; + rProp.maValue >>= nVal; + aVisArea.setWidth( nVal ); + bHasSizeProps = true; + } + break; + case CTF_OLE_VIS_AREA_HEIGHT: + { + sal_Int32 nVal = 0; + rProp.maValue >>= nVal; + aVisArea.setHeight( nVal ); + bHasSizeProps = true; + } + break; + case CTF_OLE_DRAW_ASPECT: + { + rProp.maValue >>= nDrawAspect; + + if ( !nDrawAspect ) + nDrawAspect = embed::Aspects::MSOLE_CONTENT; + + if ( pOLENd ) + pOLENd->GetOLEObj().GetObject().SetViewAspect( nDrawAspect ); + } + break; + } + } + } + } + } + + if ( bHasSizeProps ) + { + uno::Reference < embed::XEmbeddedObject > xObj = + pDoc->GetPersist()->GetEmbeddedObjectContainer().GetEmbeddedObject( aObjName ); + if( xObj.is() ) + lcl_setObjectVisualArea( xObj, ( nDrawAspect ? nDrawAspect : embed::Aspects::MSOLE_CONTENT ), + aVisArea.GetSize(), MapUnit::Map100thMM ); + } + + return xPropSet; +} + +uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertOOoLink( + SvXMLImport& rImport, + const OUString& rHRef, + const OUString& /*rStyleName*/, + const OUString& /*rTableName*/, + sal_Int32 nWidth, sal_Int32 nHeight ) +{ + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + uno::Reference < XPropertySet > xPropSet; + + OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(GetCursor().get()); + assert( pTextCursor && "SwXTextCursor missing" ); + SwDoc *pDoc = static_cast<SwXMLImport&>(rImport).getDoc(); + + SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END> aItemSet( pDoc->GetAttrPool() ); + Size aTwipSize( 0, 0 ); + lcl_putHeightAndWidth( aItemSet, nHeight, nWidth, + &aTwipSize ); + + // We'll need a (valid) URL. If we don't have do not insert the link and return early. + // Copy URL into URL object on the way. + INetURLObject aURLObj; + bool bValidURL = !rHRef.isEmpty() && + aURLObj.SetURL( URIHelper::SmartRel2Abs( + INetURLObject( GetXMLImport().GetBaseURL() ), rHRef ) ); + if( !bValidURL ) + return xPropSet; + + uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage(); + try + { + // create object with desired ClassId + uno::Reference < embed::XEmbeddedObjectCreator > xFactory = + embed::OOoEmbeddedObjectFactory::create(::comphelper::getProcessComponentContext()); + + uno::Sequence< beans::PropertyValue > aMediaDescriptor{ comphelper::makePropertyValue( + "URL", aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )) }; + + if (SfxMedium* pMedium = pDoc->GetDocShell() ? pDoc->GetDocShell()->GetMedium() : nullptr) + { + uno::Reference< task::XInteractionHandler > xInteraction = pMedium->GetInteractionHandler(); + if ( xInteraction.is() ) + { + aMediaDescriptor.realloc( 2 ); + auto pMediaDescriptor = aMediaDescriptor.getArray(); + pMediaDescriptor[1].Name = "InteractionHandler"; + pMediaDescriptor[1].Value <<= xInteraction; + } + + const auto nLen = aMediaDescriptor.getLength() + 1; + aMediaDescriptor.realloc(nLen); + auto pMediaDescriptor = aMediaDescriptor.getArray(); + pMediaDescriptor[nLen - 1].Name = "Referer"; + pMediaDescriptor[nLen - 1].Value <<= pMedium->GetName(); + } + + uno::Reference < embed::XEmbeddedObject > xObj( + xFactory->createInstanceLink( + xStorage, "DummyName", aMediaDescriptor, uno::Sequence< beans::PropertyValue >() ), + uno::UNO_QUERY_THROW ); + + { + SwFrameFormat *const pFrameFormat = + pDoc->getIDocumentContentOperations().InsertEmbObject( + *pTextCursor->GetPaM(), + ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT), + &aItemSet ); + + // TODO/LATER: in future may need a way to set replacement image url to the link ( may be even to the object ), needs oasis cws??? + + xPropSet = SwXTextEmbeddedObject::CreateXTextEmbeddedObject( + *pDoc, pFrameFormat); + if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() ) + { + SwXFrame::GetOrCreateSdrObject(* + static_cast<SwFlyFrameFormat*>(pFrameFormat)); // req for z-order + } + } + } + catch ( uno::Exception& ) + { + } + + // TODO/LATER: should the rStyleName and rTableName be handled as for usual embedded object? + + return xPropSet; +} + +uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertApplet( + const OUString &rName, + const OUString &rCode, + bool bMayScript, + const OUString& rHRef, + sal_Int32 nWidth, sal_Int32 nHeight ) +{ + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + uno::Reference < XPropertySet > xPropSet; + OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(GetCursor().get()); + assert( pTextCursor && "SwXTextCursor missing" ); + SwDoc *pDoc = pTextCursor->GetDoc(); + + SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END> aItemSet( pDoc->GetAttrPool() ); + lcl_putHeightAndWidth( aItemSet, nHeight, nWidth); + + SwApplet_Impl aAppletImpl ( std::move(aItemSet) ); + + OUString sCodeBase; + if( !rHRef.isEmpty() ) + sCodeBase = GetXMLImport().GetAbsoluteReference( rHRef ); + + aAppletImpl.CreateApplet ( rCode, rName, bMayScript, sCodeBase, GetXMLImport().GetDocumentBase() ); + + // set the size of the applet + lcl_setObjectVisualArea( aAppletImpl.GetApplet(), + embed::Aspects::MSOLE_CONTENT, + Size( nWidth, nHeight ), + MapUnit::Map100thMM ); + + SwFrameFormat *const pFrameFormat = + pDoc->getIDocumentContentOperations().InsertEmbObject( *pTextCursor->GetPaM(), + ::svt::EmbeddedObjectRef(aAppletImpl.GetApplet(), embed::Aspects::MSOLE_CONTENT), + &aAppletImpl.GetItemSet()); + xPropSet = SwXTextEmbeddedObject::CreateXTextEmbeddedObject( + *pDoc, pFrameFormat); + if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() ) + { + // req for z-order + SwXFrame::GetOrCreateSdrObject(*static_cast<SwFlyFrameFormat*>(pFrameFormat)); + } + + return xPropSet; +} + +uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertPlugin( + const OUString &rMimeType, + const OUString& rHRef, + sal_Int32 nWidth, sal_Int32 nHeight ) +{ + uno::Reference < XPropertySet > xPropSet; + OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(GetCursor().get()); + assert( pTextCursor && "SwXTextCursor missing" ); + SwDoc *pDoc = pTextCursor->GetDoc(); + + SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END> aItemSet( pDoc->GetAttrPool() ); + lcl_putHeightAndWidth( aItemSet, nHeight, nWidth); + + // We'll need a (valid) URL, or we need a MIME type. If we don't have + // either, do not insert plugin and return early. Copy URL into URL object + // on the way. + INetURLObject aURLObj; + + bool bValidURL = !rHRef.isEmpty() && + aURLObj.SetURL( URIHelper::SmartRel2Abs( INetURLObject( GetXMLImport().GetBaseURL() ), rHRef ) ); + bool bValidMimeType = !rMimeType.isEmpty(); + if( !bValidURL && !bValidMimeType ) + return xPropSet; + + uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage(); + try + { + // create object with desired ClassId + uno::Sequence < sal_Int8 > aClass( SvGlobalName( SO3_PLUGIN_CLASSID ).GetByteSequence() ); + uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() ); + uno::Reference < embed::XEmbeddedObject > xObj( xFactory->createInstanceInitNew( + aClass, OUString(), xStorage, "DummyName", + uno::Sequence < beans::PropertyValue >() ), uno::UNO_QUERY ); + + // set size to the object + lcl_setObjectVisualArea( xObj, + embed::Aspects::MSOLE_CONTENT, + Size( nWidth, nHeight ), + MapUnit::Map100thMM ); + + if ( svt::EmbeddedObjectRef::TryRunningState( xObj ) ) + { + uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY ); + if ( xSet.is() ) + { + if( bValidURL ) + xSet->setPropertyValue("PluginURL", + Any( aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) ); + if( bValidMimeType ) + xSet->setPropertyValue("PluginMimeType", + Any( rMimeType ) ); + } + + SwFrameFormat *const pFrameFormat = + pDoc->getIDocumentContentOperations().InsertEmbObject( + *pTextCursor->GetPaM(), + ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT), + &aItemSet); + xPropSet = SwXTextEmbeddedObject::CreateXTextEmbeddedObject( + *pDoc, pFrameFormat); + if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() ) + { + SwXFrame::GetOrCreateSdrObject(* + static_cast<SwFlyFrameFormat*>(pFrameFormat)); // req for z-order + } + } + } + catch ( uno::Exception& ) + { + } + + return xPropSet; +} +uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertFloatingFrame( + const OUString& rName, + const OUString& rHRef, + const OUString& rStyleName, + sal_Int32 nWidth, sal_Int32 nHeight ) +{ + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + uno::Reference < XPropertySet > xPropSet; + OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(GetCursor().get()); + assert( pTextCursor && "SwXTextCursor missing" ); + SwDoc *pDoc = pTextCursor->GetDoc(); + + SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END> aItemSet( pDoc->GetAttrPool() ); + lcl_putHeightAndWidth( aItemSet, nHeight, nWidth); + + ScrollingMode eScrollMode = ScrollingMode::Auto; + bool bHasBorder = false; + bool bIsBorderSet = false; + Size aMargin( SIZE_NOT_SET, SIZE_NOT_SET ); + const XMLPropStyleContext *pStyle = nullptr; + if( !rStyleName.isEmpty() ) + { + pStyle = FindAutoFrameStyle( rStyleName ); + if( pStyle ) + { + rtl::Reference < SvXMLImportPropertyMapper > xImpPrMap = + pStyle->GetStyles() + ->GetImportPropertyMapper(pStyle->GetFamily()); + OSL_ENSURE( xImpPrMap.is(), "Where is the import prop mapper?" ); + if( xImpPrMap.is() ) + { + rtl::Reference<XMLPropertySetMapper> rPropMapper = + xImpPrMap->getPropertySetMapper(); + + sal_Int32 nCount = pStyle->GetProperties().size(); + for( sal_Int32 i=0; i < nCount; i++ ) + { + const XMLPropertyState& rProp = pStyle->GetProperties()[i]; + sal_Int32 nIdx = rProp.mnIndex; + if( -1 == nIdx ) + continue; + + switch( rPropMapper->GetEntryContextId(nIdx) ) + { + case CTF_FRAME_DISPLAY_SCROLLBAR: + { + bool bYes = *o3tl::doAccess<bool>(rProp.maValue); + eScrollMode = bYes ? ScrollingMode::Yes : ScrollingMode::No; + } + break; + case CTF_FRAME_DISPLAY_BORDER: + { + bHasBorder = *o3tl::doAccess<bool>(rProp.maValue); + bIsBorderSet = true; + } + break; + case CTF_FRAME_MARGIN_HORI: + { + sal_Int32 nVal = SIZE_NOT_SET; + rProp.maValue >>= nVal; + aMargin.setWidth( nVal ); + } + break; + case CTF_FRAME_MARGIN_VERT: + { + sal_Int32 nVal = SIZE_NOT_SET; + rProp.maValue >>= nVal; + aMargin.setHeight( nVal ); + } + break; + } + } + } + } + } + + uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage(); + try + { + // create object with desired ClassId + uno::Sequence < sal_Int8 > aClass( SvGlobalName( SO3_IFRAME_CLASSID ).GetByteSequence() ); + uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() ); + uno::Reference < embed::XEmbeddedObject > xObj( xFactory->createInstanceInitNew( + aClass, OUString(), xStorage, "DummyName", + uno::Sequence < beans::PropertyValue >() ), uno::UNO_QUERY ); + + // set size to the object + lcl_setObjectVisualArea( xObj, + embed::Aspects::MSOLE_CONTENT, + Size( nWidth, nHeight ), + MapUnit::Map100thMM ); + + if ( svt::EmbeddedObjectRef::TryRunningState( xObj ) ) + { + uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY ); + if ( xSet.is() ) + { + OUString sHRef = URIHelper::SmartRel2Abs( + INetURLObject( GetXMLImport().GetBaseURL() ), rHRef ); + + if (INetURLObject(sHRef).IsExoticProtocol()) + GetXMLImport().NotifyMacroEventRead(); + + xSet->setPropertyValue("FrameURL", + Any( sHRef ) ); + + xSet->setPropertyValue("FrameName", + Any( rName ) ); + + if ( eScrollMode == ScrollingMode::Auto ) + xSet->setPropertyValue("FrameIsAutoScroll", + Any( true ) ); + else + xSet->setPropertyValue("FrameIsScrollingMode", + Any( eScrollMode == ScrollingMode::Yes ) ); + + if ( bIsBorderSet ) + xSet->setPropertyValue("FrameIsBorder", + Any( bHasBorder ) ); + else + xSet->setPropertyValue("FrameIsAutoBorder", + Any( true ) ); + + xSet->setPropertyValue("FrameMarginWidth", + Any( sal_Int32( aMargin.Width() ) ) ); + + xSet->setPropertyValue("FrameMarginHeight", + Any( sal_Int32( aMargin.Height() ) ) ); + } + + SwFrameFormat *const pFrameFormat = + pDoc->getIDocumentContentOperations().InsertEmbObject( + *pTextCursor->GetPaM(), + ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT), + &aItemSet); + xPropSet = SwXTextEmbeddedObject::CreateXTextEmbeddedObject( + *pDoc, pFrameFormat); + if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() ) + { + // req for z-order + SwXFrame::GetOrCreateSdrObject(* + static_cast<SwFlyFrameFormat*>(pFrameFormat)); + } + } + } + catch ( uno::Exception& ) + { + } + + return xPropSet; +} + +void SwXMLTextImportHelper::endAppletOrPlugin( + const uno::Reference < XPropertySet > &rPropSet, + std::map < const OUString, OUString > &rParamMap) +{ + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + SwXFrame* pFrame = dynamic_cast<SwXFrame*>(rPropSet.get()); + assert(pFrame && "SwXFrame missing"); + SwFrameFormat *pFrameFormat = pFrame->GetFrameFormat(); + const SwFormatContent& rContent = pFrameFormat->GetContent(); + const SwNodeIndex *pNdIdx = rContent.GetContentIdx(); + SwOLENode *pOLENd = pNdIdx->GetNodes()[pNdIdx->GetIndex() + 1]->GetNoTextNode()->GetOLENode(); + SwOLEObj& rOLEObj = pOLENd->GetOLEObj(); + + uno::Reference < embed::XEmbeddedObject > xEmbObj( rOLEObj.GetOleRef() ); + if ( !svt::EmbeddedObjectRef::TryRunningState( xEmbObj ) ) + return; + + uno::Reference < beans::XPropertySet > xSet( xEmbObj->getComponent(), uno::UNO_QUERY ); + if ( !xSet.is() ) + return; + + const sal_Int32 nCount = rParamMap.size(); + uno::Sequence< beans::PropertyValue > aCommandSequence( nCount ); + + std::transform(rParamMap.begin(), rParamMap.end(), aCommandSequence.getArray(), + [](const auto& rParam) + { + return beans::PropertyValue(/* Name */ rParam.first, + /* Handle */ -1, + /* Value */ uno::Any(rParam.second), + /* State */ beans::PropertyState_DIRECT_VALUE); + }); + + // unfortunately the names of the properties are depending on the object + OUString aParaName("AppletCommands"); + try + { + xSet->setPropertyValue( aParaName, Any( aCommandSequence ) ); + } + catch ( uno::Exception& ) + { + aParaName = "PluginCommands"; + try + { + xSet->setPropertyValue( aParaName, Any( aCommandSequence ) ); + } + catch ( uno::Exception& ) + { + } + } +} + +// redlining helper methods +// (override to provide the real implementation) +void SwXMLTextImportHelper::RedlineAdd( + const OUString& rType, + const OUString& rId, + const OUString& rAuthor, + const OUString& rComment, + const util::DateTime& rDateTime, + const OUString& rMovedID, + bool bMergeLastPara) +{ + // create redline helper on demand + OSL_ENSURE(nullptr != m_pRedlineHelper, "helper should have been created in constructor"); + if (nullptr != m_pRedlineHelper) + m_pRedlineHelper->Add(rType, rId, rAuthor, rComment, rDateTime, rMovedID, + bMergeLastPara); +} + +uno::Reference<XTextCursor> SwXMLTextImportHelper::RedlineCreateText( + uno::Reference<XTextCursor> & rOldCursor, + const OUString& rId) +{ + uno::Reference<XTextCursor> xRet; + + if (nullptr != m_pRedlineHelper) + { + xRet = m_pRedlineHelper->CreateRedlineTextSection(rOldCursor, rId); + } + + return xRet; +} + +void SwXMLTextImportHelper::RedlineSetCursor( + const OUString& rId, + bool bStart, + bool bIsOutsideOfParagraph) +{ + if (nullptr != m_pRedlineHelper) { + uno::Reference<XTextRange> xTextRange( GetCursor()->getStart() ); + m_pRedlineHelper->SetCursor(rId, bStart, xTextRange, + bIsOutsideOfParagraph); + } + // else: ignore redline (wasn't added before, else we'd have a helper) +} + +void SwXMLTextImportHelper::RedlineAdjustStartNodeCursor() +{ + OUString rId = GetOpenRedlineId(); + if ((nullptr != m_pRedlineHelper) && !rId.isEmpty()) + { + m_pRedlineHelper->AdjustStartNodeCursor(rId); + ResetOpenRedlineId(); + } + // else: ignore redline (wasn't added before, or no open redline ID +} + +void SwXMLTextImportHelper::SetShowChanges( bool bShowChanges ) +{ + if ( nullptr != m_pRedlineHelper ) + m_pRedlineHelper->SetShowChanges( bShowChanges ); +} + +void SwXMLTextImportHelper::SetRecordChanges( bool bRecordChanges ) +{ + if ( nullptr != m_pRedlineHelper ) + m_pRedlineHelper->SetRecordChanges( bRecordChanges ); +} + +void SwXMLTextImportHelper::SetChangesProtectionKey( + const Sequence<sal_Int8> & rKey ) +{ + if ( nullptr != m_pRedlineHelper ) + m_pRedlineHelper->SetProtectionKey( rKey ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltexti.hxx b/sw/source/filter/xml/xmltexti.hxx new file mode 100644 index 0000000000..886ce4c7ac --- /dev/null +++ b/sw/source/filter/xml/xmltexti.hxx @@ -0,0 +1,111 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTI_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTI_HXX + +#include <xmloff/txtimp.hxx> + +class XMLRedlineImportHelper; +class SvXMLImport; + +class SwXMLTextImportHelper : public XMLTextImportHelper +{ + XMLRedlineImportHelper *m_pRedlineHelper; + +protected: + virtual SvXMLImportContext *CreateTableChildContext( + SvXMLImport& rImport, + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override; + +public: + SwXMLTextImportHelper( + const css::uno::Reference<css::frame::XModel>& rModel, + SwXMLImport& rImport, + const css::uno::Reference<css::beans::XPropertySet>& rInfoSet, + bool bInsertM, bool bStylesOnlyM, + bool bBlockM, bool bOrganizerM ); + virtual ~SwXMLTextImportHelper() override; + + virtual css::uno::Reference<css::beans::XPropertySet> + createAndInsertOLEObject( SvXMLImport& rImport, + const OUString& rHRef, + const OUString& rStyleName, + const OUString& rTableName, + sal_Int32 nWidth, sal_Int32 nHeight ) override; + virtual css::uno::Reference<css::beans::XPropertySet> + createAndInsertOOoLink( SvXMLImport& rImport, + const OUString& rHRef, + const OUString& rStyleName, + const OUString& rTableName, + sal_Int32 nWidth, sal_Int32 nHeight ) override; + virtual css::uno::Reference<css::beans::XPropertySet> + createAndInsertApplet( + const OUString &rName, + const OUString &rCode, + bool bMayScript, + const OUString& rHRef, + sal_Int32 nWidth, sal_Int32 nHeight ) override; + + virtual css::uno::Reference<css::beans::XPropertySet> + createAndInsertPlugin( + const OUString &rMimeType, + const OUString& rHRef, + sal_Int32 nWidth, sal_Int32 nHeight ) override; + + virtual css::uno::Reference<css::beans::XPropertySet> + createAndInsertFloatingFrame( + const OUString &rName, + const OUString &rHRef, + const OUString &rStyleName, + sal_Int32 nWidth, sal_Int32 nHeight ) override; + + virtual void endAppletOrPlugin( + const css::uno::Reference < css::beans::XPropertySet > &rPropSet, + std::map < const OUString, OUString > &rParamMap) override; + + virtual bool IsInHeaderFooter() const override; + + // redlining helper methods + // (here is the real implementation) + virtual void RedlineAdd( + const OUString& rType, /// redline type (insert, del,... ) + const OUString& rId, /// use to identify this redline + const OUString& rAuthor, /// name of the author + const OUString& rComment, /// redline comment + const css::util::DateTime& rDateTime, /// date+time + const OUString& rMovedID, /// redline move id, to find moveFrom/MoveTo parts + bool bMergeLastPara) override; /// merge last paragraph + virtual css::uno::Reference<css::text::XTextCursor> RedlineCreateText( + css::uno::Reference<css::text::XTextCursor> & rOldCursor, /// needed to get the document + const OUString& rId) override; /// ID used to RedlineAdd() call + virtual void RedlineSetCursor( + const OUString& rId, /// ID used to RedlineAdd() call + bool bStart, /// start or end Cursor + bool bIsOutsideOfParagraph) override; + virtual void RedlineAdjustStartNodeCursor() override; + virtual void SetShowChanges( bool bShowChanges ) override; + virtual void SetRecordChanges( bool bRecordChanges ) override; + virtual void SetChangesProtectionKey( + const css::uno::Sequence<sal_Int8> & rKey ) override; +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTI_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/zorder.hxx b/sw/source/filter/xml/zorder.hxx new file mode 100644 index 0000000000..11cf17ef9c --- /dev/null +++ b/sw/source/filter/xml/zorder.hxx @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#pragma once + +#include <IDocumentDrawModelAccess.hxx> + +#include <o3tl/any.hxx> +#include <o3tl/unreachable.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> + +namespace sw +{ +struct GetZOrderLayer +{ + GetZOrderLayer(IDocumentDrawModelAccess const& rIDDMA) + : m_nHeavenId(rIDDMA.GetHeavenId().get()) + , m_nHellId(rIDDMA.GetHellId().get()) + , m_nControlsId(rIDDMA.GetControlsId().get()) + , m_nInvisibleHeavenId(rIDDMA.GetInvisibleHeavenId().get()) + , m_nInvisibleHellId(rIDDMA.GetInvisibleHellId().get()) + , m_nInvisibleControlsId(rIDDMA.GetInvisibleControlsId().get()) + { + } + + auto operator()(css::uno::Reference<css::beans::XPropertySet> const& xShape) -> unsigned int + { + sal_Int16 nLayerID(0); + if (xShape->getPropertySetInfo()->hasPropertyByName("LayerID")) + { + xShape->getPropertyValue("LayerID") >>= nLayerID; + if (nLayerID == m_nHellId || nLayerID == m_nInvisibleHellId) + { + return 0; + } + else if (nLayerID == m_nHeavenId || nLayerID == m_nInvisibleHeavenId) + { + return 1; + } + else if (nLayerID == m_nControlsId || nLayerID == m_nInvisibleControlsId) + { + return 2; + } + O3TL_UNREACHABLE; + } + else // SwXFrame only has "Opaque" + { + if (*o3tl::doAccess<bool>(xShape->getPropertyValue("Opaque"))) + { + return 1; + } + else + { + return 0; + } + } + } + +private: + sal_Int16 m_nHeavenId; + sal_Int16 m_nHellId; + sal_Int16 m_nControlsId; + sal_Int16 m_nInvisibleHeavenId; + sal_Int16 m_nInvisibleHellId; + sal_Int16 m_nInvisibleControlsId; +}; + +} // namespace sw + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ |