diff options
Diffstat (limited to 'writerfilter/source/rtftok/rtfdocumentimpl.cxx')
-rw-r--r-- | writerfilter/source/rtftok/rtfdocumentimpl.cxx | 4062 |
1 files changed, 0 insertions, 4062 deletions
diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx b/writerfilter/source/rtftok/rtfdocumentimpl.cxx deleted file mode 100644 index ab700ff0dc14..000000000000 --- a/writerfilter/source/rtftok/rtfdocumentimpl.cxx +++ /dev/null @@ -1,4062 +0,0 @@ -/* -*- 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/. - */ - -#include "rtfdocumentimpl.hxx" - -#include <algorithm> -#include <memory> -#include <string_view> - -#include <com/sun/star/embed/XEmbeddedObject.hpp> -#include <com/sun/star/beans/PropertyAttribute.hpp> -#include <com/sun/star/io/WrongFormatException.hpp> -#include <com/sun/star/lang/XServiceInfo.hpp> -#include <com/sun/star/lang/XMultiServiceFactory.hpp> -#include <com/sun/star/text/TextContentAnchorType.hpp> -#include <com/sun/star/text/XDependentTextField.hpp> -#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> - -#include <i18nlangtag/languagetag.hxx> -#include <unotools/ucbstreamhelper.hxx> -#include <unotools/streamwrap.hxx> -#include <com/sun/star/drawing/XDrawPageSupplier.hpp> -#include <filter/msfilter/util.hxx> -#include <filter/msfilter/rtfutil.hxx> -#include <comphelper/string.hxx> -#include <comphelper/diagnose_ex.hxx> -#include <tools/globname.hxx> -#include <tools/datetimeutils.hxx> -#include <comphelper/classids.hxx> -#include <comphelper/embeddedobjectcontainer.hxx> -#include <svl/lngmisc.hxx> -#include <sfx2/classificationhelper.hxx> -#include <oox/mathml/imexport.hxx> -#include <ooxml/resourceids.hxx> -#include <oox/token/namespaces.hxx> -#include <oox/drawingml/drawingmltypes.hxx> -#include <rtl/uri.hxx> -#include <rtl/tencinfo.h> -#include <sal/log.hxx> -#include <osl/diagnose.h> -#include <oox/helper/graphichelper.hxx> -#include <vcl/wmfexternal.hxx> -#include <vcl/graph.hxx> -#include <vcl/settings.hxx> -#include <vcl/svapp.hxx> -#include "rtfsdrimport.hxx" -#include "rtfreferenceproperties.hxx" -#include "rtfskipdestination.hxx" -#include "rtftokenizer.hxx" -#include "rtflookahead.hxx" -#include "rtfcharsets.hxx" - -using namespace com::sun::star; - -namespace -{ -/// Returns an util::DateTime from a 'YYYY. MM. DD.' string. -util::DateTime getDateTimeFromUserProp(std::u16string_view rString) -{ - util::DateTime aRet; - size_t nLen = rString.size(); - if (nLen >= 4) - { - aRet.Year = o3tl::toInt32(rString.substr(0, 4)); - - if (nLen >= 8 && o3tl::starts_with(rString.substr(4), u". ")) - { - aRet.Month = o3tl::toInt32(rString.substr(6, 2)); - - if (nLen >= 12 && o3tl::starts_with(rString.substr(8), u". ")) - aRet.Day = o3tl::toInt32(rString.substr(10, 2)); - } - } - return aRet; -} -} // anonymous namespace - -namespace writerfilter::rtftok -{ -Id getParagraphBorder(sal_uInt32 nIndex) -{ - static const Id aBorderIds[] - = { NS_ooxml::LN_CT_PBdr_top, NS_ooxml::LN_CT_PBdr_left, NS_ooxml::LN_CT_PBdr_bottom, - NS_ooxml::LN_CT_PBdr_right, NS_ooxml::LN_CT_PBdr_between }; - - return aBorderIds[nIndex]; -} - -void putNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId, const RTFValue::Pointer_t& pValue, - RTFOverwrite eOverwrite, bool bAttribute) -{ - RTFValue::Pointer_t pParent = rSprms.find(nParent, /*bFirst=*/true, /*bForWrite=*/true); - if (!pParent) - { - RTFSprms aAttributes; - if (nParent == NS_ooxml::LN_CT_TcPrBase_shd) - { - // RTF default is 'auto', see writerfilter::dmapper::CellColorHandler - aAttributes.set(NS_ooxml::LN_CT_Shd_color, new RTFValue(sal_uInt32(COL_AUTO))); - aAttributes.set(NS_ooxml::LN_CT_Shd_fill, new RTFValue(sal_uInt32(COL_AUTO))); - } - auto pParentValue = new RTFValue(aAttributes); - rSprms.set(nParent, pParentValue, eOverwrite); - pParent = pParentValue; - } - RTFSprms& rAttributes = (bAttribute ? pParent->getAttributes() : pParent->getSprms()); - rAttributes.set(nId, pValue, eOverwrite); -} - -void putNestedSprm(RTFSprms& rSprms, Id nParent, Id nId, const RTFValue::Pointer_t& pValue, - RTFOverwrite eOverwrite) -{ - putNestedAttribute(rSprms, nParent, nId, pValue, eOverwrite, false); -} - -RTFValue::Pointer_t getNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId) -{ - RTFValue::Pointer_t pParent = rSprms.find(nParent); - if (!pParent) - return RTFValue::Pointer_t(); - RTFSprms& rAttributes = pParent->getAttributes(); - return rAttributes.find(nId); -} - -RTFValue::Pointer_t getNestedSprm(RTFSprms& rSprms, Id nParent, Id nId) -{ - RTFValue::Pointer_t pParent = rSprms.find(nParent); - if (!pParent) - return RTFValue::Pointer_t(); - RTFSprms& rInner = pParent->getSprms(); - return rInner.find(nId); -} - -bool eraseNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId) -{ - RTFValue::Pointer_t pParent = rSprms.find(nParent); - if (!pParent) - // It doesn't even have a parent, we're done. - return false; - RTFSprms& rAttributes = pParent->getAttributes(); - return rAttributes.erase(nId); -} - -RTFSprms& getLastAttributes(RTFSprms& rSprms, Id nId) -{ - RTFValue::Pointer_t p = rSprms.find(nId); - if (p && !p->getSprms().empty()) - return p->getSprms().back().second->getAttributes(); - - SAL_WARN("writerfilter.rtf", "trying to set property when no type is defined"); - return rSprms; -} - -void putBorderProperty(RTFStack& aStates, Id nId, const RTFValue::Pointer_t& pValue) -{ - RTFSprms* pAttributes = nullptr; - if (aStates.top().getBorderState() == RTFBorderState::PARAGRAPH_BOX) - for (int i = 0; i < 4; i++) - { - RTFValue::Pointer_t p = aStates.top().getParagraphSprms().find(getParagraphBorder(i)); - if (p) - { - RTFSprms& rAttributes = p->getAttributes(); - rAttributes.set(nId, pValue); - } - } - else if (aStates.top().getBorderState() == RTFBorderState::CHARACTER) - { - RTFValue::Pointer_t pPointer - = aStates.top().getCharacterSprms().find(NS_ooxml::LN_EG_RPrBase_bdr); - if (pPointer) - { - RTFSprms& rAttributes = pPointer->getAttributes(); - rAttributes.set(nId, pValue); - } - } - // Attributes of the last border type - else if (aStates.top().getBorderState() == RTFBorderState::PARAGRAPH) - pAttributes - = &getLastAttributes(aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PrBase_pBdr); - else if (aStates.top().getBorderState() == RTFBorderState::CELL) - pAttributes = &getLastAttributes(aStates.top().getTableCellSprms(), - NS_ooxml::LN_CT_TcPrBase_tcBorders); - else if (aStates.top().getBorderState() == RTFBorderState::PAGE) - pAttributes = &getLastAttributes(aStates.top().getSectionSprms(), - NS_ooxml::LN_EG_SectPrContents_pgBorders); - else if (aStates.top().getBorderState() == RTFBorderState::NONE) - { - // this is invalid, but Word apparently clears or overrides all paragraph borders now - for (int i = 0; i < 4; ++i) - { - auto const nBorder = getParagraphBorder(i); - RTFSprms aAttributes; - RTFSprms aSprms; - aAttributes.set(NS_ooxml::LN_CT_Border_val, - new RTFValue(NS_ooxml::LN_Value_ST_Border_none)); - putNestedSprm(aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PrBase_pBdr, nBorder, - new RTFValue(aAttributes, aSprms), RTFOverwrite::YES); - } - } - - if (pAttributes) - pAttributes->set(nId, pValue); -} - -OString DTTM22OString(tools::Long nDTTM) -{ - return DateTimeToOString(msfilter::util::DTTM2DateTime(nDTTM)); -} - -static RTFSprms lcl_getBookmarkProperties(int nPos, const OUString& rString) -{ - RTFSprms aAttributes; - auto pPos = new RTFValue(nPos); - if (!rString.isEmpty()) - { - // If present, this should be sent first. - auto pString = new RTFValue(rString); - aAttributes.set(NS_ooxml::LN_CT_Bookmark_name, pString); - } - aAttributes.set(NS_ooxml::LN_CT_MarkupRangeBookmark_id, pPos); - return aAttributes; -} - -const char* keywordToString(RTFKeyword nKeyword) -{ - for (int i = 0; i < nRTFControlWords; i++) - { - if (nKeyword == aRTFControlWords[i].GetIndex()) - return aRTFControlWords[i].GetKeyword(); - } - return nullptr; -} - -static util::DateTime lcl_getDateTime(RTFParserState const& aState) -{ - return { 0 /*100sec*/, - 0 /*sec*/, - aState.getMinute(), - aState.getHour(), - aState.getDay(), - aState.getMonth(), - static_cast<sal_Int16>(aState.getYear()), - false }; -} - -static void lcl_DestinationToMath(OUStringBuffer* pDestinationText, - oox::formulaimport::XmlStreamBuilder& rMathBuffer, bool& rMathNor) -{ - if (!pDestinationText) - return; - OUString aStr = pDestinationText->makeStringAndClear(); - if (aStr.isEmpty()) - return; - rMathBuffer.appendOpeningTag(M_TOKEN(r)); - if (rMathNor) - { - rMathBuffer.appendOpeningTag(M_TOKEN(rPr)); - // Same as M_TOKEN(lit) - rMathBuffer.appendOpeningTag(M_TOKEN(nor)); - rMathBuffer.appendClosingTag(M_TOKEN(nor)); - rMathBuffer.appendClosingTag(M_TOKEN(rPr)); - rMathNor = false; - } - rMathBuffer.appendOpeningTag(M_TOKEN(t)); - rMathBuffer.appendCharacters(aStr); - rMathBuffer.appendClosingTag(M_TOKEN(t)); - rMathBuffer.appendClosingTag(M_TOKEN(r)); -} - -RTFDocumentImpl::RTFDocumentImpl(uno::Reference<uno::XComponentContext> const& xContext, - uno::Reference<io::XInputStream> const& xInputStream, - uno::Reference<lang::XComponent> const& xDstDoc, - uno::Reference<frame::XFrame> const& xFrame, - uno::Reference<task::XStatusIndicator> const& xStatusIndicator, - const utl::MediaDescriptor& rMediaDescriptor) - : m_xContext(xContext) - , m_xInputStream(xInputStream) - , m_xDstDoc(xDstDoc) - , m_xFrame(xFrame) - , m_xStatusIndicator(xStatusIndicator) - , m_pMapperStream(nullptr) - , m_aDefaultState(this) - , m_bSkipUnknown(false) - , m_bFirstRun(true) - , m_bFirstRunException(false) - , m_bNeedPap(true) - , m_bNeedCr(false) - , m_bNeedCrOrig(false) - , m_bNeedPar(true) - , m_bNeedFinalPar(false) - , m_nNestedCells(0) - , m_nTopLevelCells(0) - , m_nInheritingCells(0) - , m_nNestedTRLeft(0) - , m_nTopLevelTRLeft(0) - , m_nNestedCurrentCellX(0) - , m_nTopLevelCurrentCellX(0) - , m_nBackupTopLevelCurrentCellX(0) - , m_aTableBufferStack(1) // create top-level buffer already - , m_pSuperstream(nullptr) - , m_nStreamType(0) - , m_nGroupStartPos(0) - , m_nFormFieldType(RTFFormFieldType::NONE) - , m_bObject(false) - , m_nCurrentFontIndex(0) - , m_nCurrentEncoding(-1) - , m_nDefaultFontIndex(-1) - , m_pStyleTableEntries(new RTFReferenceTable::Entries_t) - , m_nCurrentStyleIndex(0) - , m_bFormField(false) - , m_bMathNor(false) - , m_bIgnoreNextContSectBreak(false) - , m_nResetBreakOnSectBreak(RTFKeyword::invalid) - , m_bNeedSect(false) // done by checkFirstRun - , m_bWasInFrame(false) - , m_bHadPicture(false) - , m_bHadSect(false) - , m_nCellxMax(0) - , m_nListPictureId(0) - , m_bIsNewDoc(!rMediaDescriptor.getUnpackedValueOrDefault("InsertMode", false)) - , m_rMediaDescriptor(rMediaDescriptor) - , m_hasRHeader(false) - , m_hasFHeader(false) - , m_hasRFooter(false) - , m_hasFFooter(false) -{ - OSL_ASSERT(xInputStream.is()); - m_pInStream = utl::UcbStreamHelper::CreateStream(xInputStream, true); - - m_xModelFactory.set(m_xDstDoc, uno::UNO_QUERY); - - uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier( - m_xDstDoc, uno::UNO_QUERY); - if (xDocumentPropertiesSupplier.is()) - m_xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties(); - - m_pGraphicHelper = std::make_shared<oox::GraphicHelper>(m_xContext, xFrame, oox::StorageRef()); - - m_pTokenizer = new RTFTokenizer(*this, m_pInStream.get(), m_xStatusIndicator); - m_pSdrImport = new RTFSdrImport(*this, m_xDstDoc); - - // unlike OOXML, this is enabled by default - m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Compat_splitPgBreakAndParaMark, new RTFValue(1)); -} - -RTFDocumentImpl::~RTFDocumentImpl() = default; - -SvStream& RTFDocumentImpl::Strm() { return *m_pInStream; } - -void RTFDocumentImpl::setSuperstream(RTFDocumentImpl* pSuperstream) -{ - m_pSuperstream = pSuperstream; -} - -bool RTFDocumentImpl::isSubstream() const { return m_pSuperstream != nullptr; } - -void RTFDocumentImpl::finishSubstream() { checkUnicode(/*bUnicode =*/true, /*bHex =*/true); } - -void RTFDocumentImpl::resolveSubstream(std::size_t nPos, Id nId) -{ - resolveSubstream(nPos, nId, OUString()); -} -void RTFDocumentImpl::resolveSubstream(std::size_t nPos, Id nId, OUString const& rIgnoreFirst) -{ - sal_uInt64 const nCurrent = Strm().Tell(); - // Seek to header position, parse, then seek back. - auto pImpl = new RTFDocumentImpl(m_xContext, m_xInputStream, m_xDstDoc, m_xFrame, - m_xStatusIndicator, m_rMediaDescriptor); - pImpl->setSuperstream(this); - pImpl->m_nStreamType = nId; - pImpl->m_aIgnoreFirst = rIgnoreFirst; - if (!m_aAuthor.isEmpty()) - { - pImpl->m_aAuthor = m_aAuthor; - m_aAuthor.clear(); - } - if (!m_aAuthorInitials.isEmpty()) - { - pImpl->m_aAuthorInitials = m_aAuthorInitials; - m_aAuthorInitials.clear(); - } - pImpl->m_nDefaultFontIndex = m_nDefaultFontIndex; - pImpl->m_pStyleTableEntries = m_pStyleTableEntries; - pImpl->Strm().Seek(nPos); - SAL_INFO("writerfilter.rtf", "substream start"); - Mapper().substream(nId, pImpl); - SAL_INFO("writerfilter.rtf", "substream end"); - Strm().Seek(nCurrent); -} - -void RTFDocumentImpl::outputSettingsTable() -{ - // tdf#136740: do not change target document settings when pasting - if (!m_bIsNewDoc || isSubstream()) - return; - writerfilter::Reference<Properties>::Pointer_t pProp - = new RTFReferenceProperties(m_aSettingsTableAttributes, m_aSettingsTableSprms); - RTFReferenceTable::Entries_t aSettingsTableEntries; - aSettingsTableEntries.insert(std::make_pair(0, pProp)); - writerfilter::Reference<Table>::Pointer_t pTable - = new RTFReferenceTable(std::move(aSettingsTableEntries)); - Mapper().table(NS_ooxml::LN_settings_settings, pTable); -} - -void RTFDocumentImpl::checkFirstRun() -{ - if (!m_bFirstRun) - return; - - outputSettingsTable(); - // start initial paragraph - m_bFirstRun = false; - assert(!m_bNeedSect || m_bFirstRunException); - setNeedSect(true); // first call that succeeds - - // set the requested default font, if there are none for each state in stack - RTFValue::Pointer_t pFont - = getNestedAttribute(m_aDefaultState.getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts, - NS_ooxml::LN_CT_Fonts_ascii); - if (!pFont) - return; - - for (size_t i = 0; i < m_aStates.size(); i++) - { - RTFValue::Pointer_t pCurrentFont - = getNestedAttribute(m_aStates[i].getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts, - NS_ooxml::LN_CT_Fonts_ascii); - if (!pCurrentFont) - putNestedAttribute(m_aStates[i].getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts, - NS_ooxml::LN_CT_Fonts_ascii, pFont); - } -} - -void RTFDocumentImpl::setNeedPar(bool bNeedPar) { m_bNeedPar = bNeedPar; } - -void RTFDocumentImpl::setNeedSect(bool bNeedSect) -{ - if (!m_bNeedSect && bNeedSect && m_bFirstRun) - { - RTFLookahead aLookahead(Strm(), m_pTokenizer->getGroupStart()); - if (aLookahead.hasTable() && aLookahead.hasColumns()) - { - m_bFirstRunException = true; - } - } - - // ignore setting before checkFirstRun - every keyword calls setNeedSect! - // except the case of a table in a multicolumn section - if (!m_bNeedSect && bNeedSect && (!m_bFirstRun || m_bFirstRunException)) - { - if (!m_pSuperstream) // no sections in header/footer! - { - Mapper().startSectionGroup(); - } - // set flag in substream too - otherwise multiple startParagraphGroup - m_bNeedSect = bNeedSect; - Mapper().startParagraphGroup(); - setNeedPar(true); - } - else if (m_bNeedSect && !bNeedSect) - { - m_bNeedSect = bNeedSect; - } -} - -/// Copy rProps to rStyleAttributes and rStyleSprms, but in case of nested sprms, copy their children as toplevel sprms/attributes. -static void lcl_copyFlatten(RTFReferenceProperties& rProps, RTFSprms& rStyleAttributes, - RTFSprms& rStyleSprms) -{ - for (auto& rSprm : rProps.getSprms()) - { - // createStyleProperties() puts properties to rPr, but here we need a flat list. - if (rSprm.first == NS_ooxml::LN_CT_Style_rPr) - { - // rPr can have both attributes and SPRMs, copy over both types. - RTFSprms& rRPrSprms = rSprm.second->getSprms(); - for (const auto& rRPrSprm : rRPrSprms) - rStyleSprms.set(rRPrSprm.first, rRPrSprm.second); - - RTFSprms& rRPrAttributes = rSprm.second->getAttributes(); - for (const auto& rRPrAttribute : rRPrAttributes) - rStyleAttributes.set(rRPrAttribute.first, rRPrAttribute.second); - } - else - rStyleSprms.set(rSprm.first, rSprm.second); - } - - RTFSprms& rAttributes = rProps.getAttributes(); - for (const auto& rAttribute : rAttributes) - rStyleAttributes.set(rAttribute.first, rAttribute.second); -} - -writerfilter::Reference<Properties>::Pointer_t -RTFDocumentImpl::getProperties(const RTFSprms& rAttributes, RTFSprms const& rSprms, Id nStyleType) -{ - RTFSprms aSprms(rSprms); - RTFValue::Pointer_t pAbstractList; - int nAbstractListId = -1; - RTFValue::Pointer_t pNumId - = getNestedSprm(aSprms, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_numId); - if (pNumId) - { - // We have a numbering, look up the abstract list for property - // deduplication and duplication. - auto itNumId = m_aListOverrideTable.find(pNumId->getInt()); - if (itNumId != m_aListOverrideTable.end()) - { - nAbstractListId = itNumId->second; - auto itAbstract = m_aListTable.find(nAbstractListId); - if (itAbstract != m_aListTable.end()) - pAbstractList = itAbstract->second; - } - } - - if (pAbstractList) - { - auto it = m_aInvalidListTableFirstIndents.find(nAbstractListId); - if (it != m_aInvalidListTableFirstIndents.end()) - aSprms.deduplicateList(it->second); - } - - int nStyle = 0; - if (!m_aStates.empty()) - nStyle = m_aStates.top().getCurrentStyleIndex(); - auto it = m_pStyleTableEntries->find(nStyle); - if (it != m_pStyleTableEntries->end()) - { - // cloneAndDeduplicate() wants to know about only a single "style", so - // let's merge paragraph and character style properties here. - auto itChar = m_pStyleTableEntries->end(); - if (!m_aStates.empty()) - { - int nCharStyle = m_aStates.top().getCurrentCharacterStyleIndex(); - itChar = m_pStyleTableEntries->find(nCharStyle); - } - - RTFSprms aStyleSprms; - RTFSprms aStyleAttributes; - // Ensure the paragraph style is a flat list. - // Take paragraph style into account for character properties as well, - // as paragraph style may contain character properties. - RTFReferenceProperties& rProps = *static_cast<RTFReferenceProperties*>(it->second.get()); - lcl_copyFlatten(rProps, aStyleAttributes, aStyleSprms); - - if (itChar != m_pStyleTableEntries->end()) - { - // Found active character style, then update aStyleSprms/Attributes. - if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_character) - { - RTFReferenceProperties& rCharProps - = *static_cast<RTFReferenceProperties*>(itChar->second.get()); - lcl_copyFlatten(rCharProps, aStyleAttributes, aStyleSprms); - } - } - - RTFSprms sprms(aSprms.cloneAndDeduplicate(aStyleSprms, nStyleType, true, &aSprms)); - RTFSprms attributes(rAttributes.cloneAndDeduplicate(aStyleAttributes, nStyleType, true)); - return new RTFReferenceProperties(std::move(attributes), std::move(sprms)); - } - - if (pAbstractList) - aSprms.duplicateList(pAbstractList); - writerfilter::Reference<Properties>::Pointer_t pRet - = new RTFReferenceProperties(rAttributes, std::move(aSprms)); - return pRet; -} - -void RTFDocumentImpl::checkNeedPap() -{ - if (!m_bNeedPap) - return; - - m_bNeedPap = false; // reset early, so we can avoid recursion when calling ourselves - - if (m_aStates.empty()) - return; - - if (!m_aStates.top().getCurrentBuffer()) - { - writerfilter::Reference<Properties>::Pointer_t const pParagraphProperties(getProperties( - m_aStates.top().getParagraphAttributes(), m_aStates.top().getParagraphSprms(), - NS_ooxml::LN_Value_ST_StyleType_paragraph)); - - // Writer will ignore a page break before a text frame, so guard it with empty paragraphs - const bool bIsInFrame = m_aStates.top().getFrame().hasProperties(); - bool hasBreakBeforeFrame - = bIsInFrame - && m_aStates.top().getParagraphSprms().find(NS_ooxml::LN_CT_PPrBase_pageBreakBefore); - if (hasBreakBeforeFrame) - { - dispatchSymbol(RTFKeyword::PAR); - m_bNeedPap = false; - } - Mapper().props(pParagraphProperties); - if (hasBreakBeforeFrame) - dispatchSymbol(RTFKeyword::PAR); - - if (bIsInFrame) - { - writerfilter::Reference<Properties>::Pointer_t const pFrameProperties( - new RTFReferenceProperties(RTFSprms(), m_aStates.top().getFrame().getSprms())); - Mapper().props(pFrameProperties); - } - } - else - { - auto pValue = new RTFValue(m_aStates.top().getParagraphAttributes(), - m_aStates.top().getParagraphSprms()); - bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue, nullptr); - } -} - -void RTFDocumentImpl::runProps() -{ - if (!m_aStates.top().getCurrentBuffer()) - { - Reference<Properties>::Pointer_t const pProperties = getProperties( - m_aStates.top().getCharacterAttributes(), m_aStates.top().getCharacterSprms(), - NS_ooxml::LN_Value_ST_StyleType_character); - Mapper().props(pProperties); - } - else - { - auto pValue = new RTFValue(m_aStates.top().getCharacterAttributes(), - m_aStates.top().getCharacterSprms()); - bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue, nullptr, - NS_ooxml::LN_Value_ST_StyleType_character); - } - - // Delete the sprm, so the trackchange range will be started only once. - // OTOH set a boolean flag, so we'll know we need to end the range later. - RTFValue::Pointer_t pTrackchange - = m_aStates.top().getCharacterSprms().find(NS_ooxml::LN_trackchange); - if (pTrackchange) - { - m_aStates.top().setStartedTrackchange(true); - m_aStates.top().getCharacterSprms().erase(NS_ooxml::LN_trackchange); - } -} - -void RTFDocumentImpl::runBreak() -{ - sal_Unicode const sBreak[] = { 0x0d }; - Mapper().utext(sBreak, 1); - m_bNeedCr = false; -} - -void RTFDocumentImpl::tableBreak() -{ - checkFirstRun(); // ooo113308-1.rtf has a header at offset 151084 that doesn't startParagraphGroup() without this - runBreak(); - Mapper().endParagraphGroup(); - Mapper().startParagraphGroup(); -} - -void RTFDocumentImpl::parBreak() -{ - checkFirstRun(); - checkNeedPap(); - // end previous paragraph - Mapper().startCharacterGroup(); - runBreak(); - Mapper().endCharacterGroup(); - Mapper().endParagraphGroup(); - - m_bHadPicture = false; - - // start new one - if (!m_bParAtEndOfSection) - { - Mapper().startParagraphGroup(); - } -} - -void RTFDocumentImpl::sectBreak(bool bFinal) -{ - SAL_INFO("writerfilter.rtf", __func__ << ": final? " << bFinal << ", needed? " << m_bNeedSect); - bool bNeedSect = m_bNeedSect; - RTFValue::Pointer_t pBreak - = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_type); - bool bContinuous = pBreak && pBreak->getInt() == NS_ooxml::LN_Value_ST_SectionMark_continuous; - // If there is no paragraph in this section, then insert a dummy one, as required by Writer, - // unless this is the end of the doc, we had nothing since the last section break and this is not a continuous one. - // Also, when pasting, it's fine to not have any paragraph inside the document at all. - if (m_bNeedPar && (!bFinal || m_bNeedSect || bContinuous) && !isSubstream() && m_bIsNewDoc) - { - m_bParAtEndOfSection = true; - dispatchSymbol(RTFKeyword::PAR); - } - // It's allowed to not have a non-table paragraph at the end of an RTF doc, add it now if required. - if (m_bNeedFinalPar && bFinal) - { - dispatchFlag(RTFKeyword::PARD); - m_bParAtEndOfSection = true; - dispatchSymbol(RTFKeyword::PAR); - m_bNeedSect = bNeedSect; - } - // testTdf148515, if RTF ends with \row, endParagraphGroup() must be called! - if (!m_bParAtEndOfSection || m_aStates.top().getCurrentBuffer()) - { - Mapper().endParagraphGroup(); // < top para context dies with page break - } - m_bParAtEndOfSection = false; - // paragraph properties are *done* now - only section properties following - - while (!m_nHeaderFooterPositions.empty()) - { - std::pair<Id, std::size_t> aPair = m_nHeaderFooterPositions.front(); - m_nHeaderFooterPositions.pop(); - resolveSubstream(aPair.second, aPair.first); - } - - // Normally a section break at the end of the doc is necessary. Unless the - // last control word in the document is a section break itself. - if (!bNeedSect || !m_bHadSect) - { - // In case the last section is a continuous one, we don't need to output a section break. - if (bFinal && bContinuous) - m_aStates.top().getSectionSprms().erase(NS_ooxml::LN_EG_SectPrContents_type); - } - - // Section properties are a paragraph sprm. - auto pValue - = new RTFValue(m_aStates.top().getSectionAttributes(), m_aStates.top().getSectionSprms()); - RTFSprms aAttributes; - RTFSprms aSprms; - aSprms.set(NS_ooxml::LN_CT_PPr_sectPr, pValue); - writerfilter::Reference<Properties>::Pointer_t pProperties - = new RTFReferenceProperties(std::move(aAttributes), std::move(aSprms)); - - if (bFinal && !m_pSuperstream) - // This is the end of the document, not just the end of e.g. a header. - // This makes sure that dmapper can set DontBalanceTextColumns=true for this section if necessary. - Mapper().markLastSectionGroup(); - - // The trick is that we send properties of the previous section right now, which will be exactly what dmapper expects. - Mapper().props(pProperties); - - // End Section - if (!m_pSuperstream) - { - m_hasFHeader = false; - m_hasRHeader = false; - m_hasRFooter = false; - m_hasFFooter = false; - Mapper().endSectionGroup(); - } - m_bNeedPar = false; - m_bNeedSect = false; -} - -Color RTFDocumentImpl::getColorTable(sal_uInt32 nIndex) -{ - if (!m_pSuperstream) - { - if (nIndex < m_aColorTable.size()) - return m_aColorTable[nIndex]; - return 0; - } - - return m_pSuperstream->getColorTable(nIndex); -} - -rtl_TextEncoding RTFDocumentImpl::getEncoding(int nFontIndex) -{ - if (!m_pSuperstream) - { - auto it = m_aFontEncodings.find(nFontIndex); - if (it != m_aFontEncodings.end()) - // We have a font encoding associated to this font. - return it->second; - if (m_aDefaultState.getCurrentEncoding() != rtl_getTextEncodingFromWindowsCharset(0)) - // We have a default encoding. - return m_aDefaultState.getCurrentEncoding(); - // Guess based on locale. - return msfilter::util::getBestTextEncodingFromLocale( - Application::GetSettings().GetLanguageTag().getLocale()); - } - - return m_pSuperstream->getEncoding(nFontIndex); -} - -OUString RTFDocumentImpl::getFontName(int nIndex) -{ - if (!m_pSuperstream) - return m_aFontNames[nIndex]; - - return m_pSuperstream->getFontName(nIndex); -} - -int RTFDocumentImpl::getFontIndex(int nIndex) -{ - if (!m_pSuperstream) - return std::find(m_aFontIndexes.begin(), m_aFontIndexes.end(), nIndex) - - m_aFontIndexes.begin(); - - return m_pSuperstream->getFontIndex(nIndex); -} - -OUString RTFDocumentImpl::getStyleName(int nIndex) -{ - if (!m_pSuperstream) - { - OUString aRet; - auto it = m_aStyleNames.find(nIndex); - if (it != m_aStyleNames.end()) - aRet = it->second; - return aRet; - } - - return m_pSuperstream->getStyleName(nIndex); -} - -Id RTFDocumentImpl::getStyleType(int nIndex) -{ - if (!m_pSuperstream) - { - Id nRet = 0; - auto it = m_aStyleTypes.find(nIndex); - if (it != m_aStyleTypes.end()) - nRet = it->second; - return nRet; - } - - return m_pSuperstream->getStyleType(nIndex); -} - -RTFParserState& RTFDocumentImpl::getDefaultState() -{ - if (!m_pSuperstream) - return m_aDefaultState; - - return m_pSuperstream->getDefaultState(); -} - -oox::GraphicHelper& RTFDocumentImpl::getGraphicHelper() { return *m_pGraphicHelper; } - -bool RTFDocumentImpl::isStyleSheetImport() -{ - if (m_aStates.empty()) - return false; - Destination eDestination = m_aStates.top().getDestination(); - return eDestination == Destination::STYLESHEET || eDestination == Destination::STYLEENTRY; -} - -void RTFDocumentImpl::resolve(Stream& rMapper) -{ - m_pMapperStream = &rMapper; - switch (m_pTokenizer->resolveParse()) - { - case RTFError::OK: - SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: finished without errors"); - break; - case RTFError::GROUP_UNDER: - SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unmatched '}'"); - break; - case RTFError::GROUP_OVER: - SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unmatched '{'"); - throw io::WrongFormatException(m_pTokenizer->getPosition()); - break; - case RTFError::UNEXPECTED_EOF: - SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unexpected end of file"); - throw io::WrongFormatException(m_pTokenizer->getPosition()); - break; - case RTFError::HEX_INVALID: - SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: invalid hex char"); - throw io::WrongFormatException(m_pTokenizer->getPosition()); - break; - case RTFError::CHAR_OVER: - SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: characters after last '}'"); - break; - case RTFError::CLASSIFICATION: - SAL_INFO("writerfilter.rtf", - "RTFDocumentImpl::resolve: classification prevented paste"); - break; - } -} - -void RTFDocumentImpl::resolvePict(bool const bInline, uno::Reference<drawing::XShape> const& rShape) -{ - SvMemoryStream aStream; - SvStream* pStream = nullptr; - if (!m_pBinaryData) - { - pStream = &aStream; - int b = 0; - int count = 2; - - // Feed the destination text to a stream. - auto& rDestinationTextBuffer = m_aStates.top().getDestinationText(); - OString aStr = OUStringToOString(rDestinationTextBuffer, RTL_TEXTENCODING_ASCII_US); - rDestinationTextBuffer.setLength(0); - for (int i = 0; i < aStr.getLength(); ++i) - { - char ch = aStr[i]; - if (ch != 0x0d && ch != 0x0a && ch != 0x20) - { - b = b << 4; - sal_Int8 parsed = msfilter::rtfutil::AsHex(ch); - if (parsed == -1) - return; - b += parsed; - count--; - if (!count) - { - aStream.WriteChar(static_cast<char>(b)); - count = 2; - b = 0; - } - } - } - } - else - pStream = m_pBinaryData.get(); - - if (!pStream->Tell()) - // No destination text? Then we'll get it later. - return; - - SvMemoryStream aDIBStream; - if (m_aStates.top().getPicture().eStyle == RTFBmpStyle::DIBITMAP) - { - // Construct a BITMAPFILEHEADER structure before the real data. - SvStream& rBodyStream = *pStream; - aDIBStream.WriteChar('B'); - aDIBStream.WriteChar('M'); - // The size of the real data. - aDIBStream.WriteUInt32(rBodyStream.Tell()); - // Reserved. - aDIBStream.WriteUInt32(0); - // The offset of the real data, i.e. the size of the header, including this number. - aDIBStream.WriteUInt32(14); - rBodyStream.Seek(0); - aDIBStream.WriteStream(rBodyStream); - pStream = &aDIBStream; - } - - // Store, and get its URL. - pStream->Seek(0); - uno::Reference<io::XInputStream> xInputStream(new utl::OInputStreamWrapper(pStream)); - WmfExternal aExtHeader; - aExtHeader.mapMode = m_aStates.top().getPicture().eWMetafile; - if (m_aStates.top().getPicture().nGoalWidth == 0 - || m_aStates.top().getPicture().nGoalHeight == 0) - { - // Don't use the values provided by picw and pich if the desired size is provided. - - aExtHeader.xExt = sal_uInt16(std::clamp<sal_Int32>( - m_aStates.top().getPicture().nWidth, 0, - SAL_MAX_UINT16)); //TODO: better way to handle out-of-bounds values? - aExtHeader.yExt = sal_uInt16(std::clamp<sal_Int32>( - m_aStates.top().getPicture().nHeight, 0, - SAL_MAX_UINT16)); //TODO: better way to handle out-of-bounds values? - } - WmfExternal* pExtHeader = &aExtHeader; - uno::Reference<lang::XServiceInfo> xServiceInfo(m_aStates.top().getDrawingObject().getShape(), - uno::UNO_QUERY); - if (xServiceInfo.is() && xServiceInfo->supportsService("com.sun.star.text.TextFrame")) - pExtHeader = nullptr; - - uno::Reference<graphic::XGraphic> xGraphic - = m_pGraphicHelper->importGraphic(xInputStream, pExtHeader); - - if (m_aStates.top().getPicture().eStyle != RTFBmpStyle::NONE) - { - // In case of PNG/JPEG, the real size is known, don't use the values - // provided by picw and pich. - - Graphic aGraphic(xGraphic); - Size aSize(aGraphic.GetPrefSize()); - MapMode aMap(MapUnit::Map100thMM); - if (aGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel) - aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMap); - else - aSize = OutputDevice::LogicToLogic(aSize, aGraphic.GetPrefMapMode(), aMap); - m_aStates.top().getPicture().nWidth = aSize.Width(); - m_aStates.top().getPicture().nHeight = aSize.Height(); - } - - uno::Reference<drawing::XShape> xShape(rShape); - if (m_aStates.top().getInShape() && xShape.is()) - { - awt::Size aSize = xShape->getSize(); - if (aSize.Width || aSize.Height) - { - // resolvePict() is processing pib structure inside shape - // So if shape has dimensions we should use them instead of - // \picwN, \pichN, \picscalexN, \picscaleyN given with picture - m_aStates.top().getPicture().nGoalWidth = aSize.Width; - m_aStates.top().getPicture().nGoalHeight = aSize.Height; - m_aStates.top().getPicture().nScaleX = 100; - m_aStates.top().getPicture().nScaleY = 100; - } - } - - // Wrap it in an XShape. - if (xShape.is()) - { - uno::Reference<lang::XServiceInfo> xSI(xShape, uno::UNO_QUERY_THROW); - if (!xSI->supportsService("com.sun.star.drawing.GraphicObjectShape")) - { - // it's sometimes an error to get here - but it's possible to have - // a \pict inside the \shptxt of a \shp of shapeType 202 "TextBox" - // and in that case xShape is the text frame; we actually need a - // new GraphicObject then (example: fdo37691-1.rtf) - SAL_INFO("writerfilter.rtf", - "cannot set graphic on existing shape, creating a new GraphicObjectShape"); - xShape.clear(); - } - } - if (!xShape.is()) - { - if (m_xModelFactory.is()) - xShape.set(m_xModelFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), - uno::UNO_QUERY); - uno::Reference<drawing::XDrawPageSupplier> const xDrawSupplier(m_xDstDoc, uno::UNO_QUERY); - if (xDrawSupplier.is()) - { - uno::Reference<drawing::XShapes> xShapes = xDrawSupplier->getDrawPage(); - if (xShapes.is()) - xShapes->add(xShape); - } - } - - uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY); - - if (xPropertySet.is()) - xPropertySet->setPropertyValue("Graphic", uno::Any(xGraphic)); - - // check if the picture is in an OLE object and if the \objdata element is used - // (see RTFKeyword::OBJECT in RTFDocumentImpl::dispatchDestination) - if (m_bObject) - { - // Set the object size - awt::Size aSize; - aSize.Width - = (m_aStates.top().getPicture().nGoalWidth ? m_aStates.top().getPicture().nGoalWidth - : m_aStates.top().getPicture().nWidth); - aSize.Height - = (m_aStates.top().getPicture().nGoalHeight ? m_aStates.top().getPicture().nGoalHeight - : m_aStates.top().getPicture().nHeight); - xShape->setSize(aSize); - - // Replacement graphic is inline by default, see oox::vml::SimpleShape::implConvertAndInsert(). - xPropertySet->setPropertyValue("AnchorType", - uno::Any(text::TextContentAnchorType_AS_CHARACTER)); - - auto pShapeValue = new RTFValue(xShape); - m_aObjectAttributes.set(NS_ooxml::LN_shape, pShapeValue); - return; - } - - if (m_aStates.top().getInListpicture()) - { - // Send the shape directly, no section is started, to additional properties will be ignored anyway. - Mapper().startShape(xShape); - Mapper().endShape(); - return; - } - - // Send it to the dmapper. - RTFSprms aSprms; - RTFSprms aAttributes; - // shape attribute - RTFSprms aPicAttributes; - if (m_aStates.top().getPicture().nCropT != 0 || m_aStates.top().getPicture().nCropB != 0 - || m_aStates.top().getPicture().nCropL != 0 || m_aStates.top().getPicture().nCropR != 0) - { - text::GraphicCrop const crop{ m_aStates.top().getPicture().nCropT, - m_aStates.top().getPicture().nCropB, - m_aStates.top().getPicture().nCropL, - m_aStates.top().getPicture().nCropR }; - auto const pCrop = new RTFValue(crop); - aPicAttributes.set(NS_ooxml::LN_CT_BlipFillProperties_srcRect, pCrop); - } - auto pShapeValue = new RTFValue(xShape); - aPicAttributes.set(NS_ooxml::LN_shape, pShapeValue); - // pic sprm - RTFSprms aGraphicDataAttributes; - RTFSprms aGraphicDataSprms; - auto pPicValue = new RTFValue(aPicAttributes); - aGraphicDataSprms.set(NS_ooxml::LN_pic_pic, pPicValue); - // graphicData sprm - RTFSprms aGraphicAttributes; - RTFSprms aGraphicSprms; - auto pGraphicDataValue = new RTFValue(aGraphicDataAttributes, aGraphicDataSprms); - aGraphicSprms.set(NS_ooxml::LN_CT_GraphicalObject_graphicData, pGraphicDataValue); - // graphic sprm - auto pGraphicValue = new RTFValue(aGraphicAttributes, aGraphicSprms); - // extent sprm - RTFSprms aExtentAttributes; - int nXExt = (m_aStates.top().getPicture().nGoalWidth ? m_aStates.top().getPicture().nGoalWidth - : m_aStates.top().getPicture().nWidth); - int nYExt = (m_aStates.top().getPicture().nGoalHeight ? m_aStates.top().getPicture().nGoalHeight - : m_aStates.top().getPicture().nHeight); - if (m_aStates.top().getPicture().nScaleX != 100) - nXExt = (static_cast<tools::Long>(m_aStates.top().getPicture().nScaleX) - * (nXExt - - (m_aStates.top().getPicture().nCropL + m_aStates.top().getPicture().nCropR))) - / 100L; - if (m_aStates.top().getPicture().nScaleY != 100) - nYExt = (static_cast<tools::Long>(m_aStates.top().getPicture().nScaleY) - * (nYExt - - (m_aStates.top().getPicture().nCropT + m_aStates.top().getPicture().nCropB))) - / 100L; - auto pXExtValue = new RTFValue(oox::drawingml::convertHmmToEmu(nXExt)); - auto pYExtValue = new RTFValue(oox::drawingml::convertHmmToEmu(nYExt)); - aExtentAttributes.set(NS_ooxml::LN_CT_PositiveSize2D_cx, pXExtValue); - aExtentAttributes.set(NS_ooxml::LN_CT_PositiveSize2D_cy, pYExtValue); - auto pExtentValue = new RTFValue(aExtentAttributes); - // docpr sprm - RTFSprms aDocprAttributes; - for (const auto& rCharacterAttribute : m_aStates.top().getCharacterAttributes()) - if (rCharacterAttribute.first == NS_ooxml::LN_CT_NonVisualDrawingProps_name - || rCharacterAttribute.first == NS_ooxml::LN_CT_NonVisualDrawingProps_descr) - aDocprAttributes.set(rCharacterAttribute.first, rCharacterAttribute.second); - auto pDocprValue = new RTFValue(aDocprAttributes); - if (bInline) - { - RTFSprms aInlineAttributes; - aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distT, new RTFValue(0)); - aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distB, new RTFValue(0)); - aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distL, new RTFValue(0)); - aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distR, new RTFValue(0)); - RTFSprms aInlineSprms; - aInlineSprms.set(NS_ooxml::LN_CT_Inline_extent, pExtentValue); - aInlineSprms.set(NS_ooxml::LN_CT_Inline_docPr, pDocprValue); - aInlineSprms.set(NS_ooxml::LN_graphic_graphic, pGraphicValue); - // inline sprm - auto pValue = new RTFValue(aInlineAttributes, aInlineSprms); - aSprms.set(NS_ooxml::LN_inline_inline, pValue); - } - else // anchored - { - // wrap sprm - RTFSprms aAnchorWrapAttributes; - m_aStates.top().getShape().getAnchorAttributes().set( - NS_ooxml::LN_CT_Anchor_behindDoc, - new RTFValue((m_aStates.top().getShape().getInBackground()) ? 1 : 0)); - RTFSprms aAnchorSprms; - for (const auto& rCharacterAttribute : m_aStates.top().getCharacterAttributes()) - { - if (rCharacterAttribute.first == NS_ooxml::LN_CT_WrapSquare_wrapText) - aAnchorWrapAttributes.set(rCharacterAttribute.first, rCharacterAttribute.second); - } - sal_Int32 nWrap = -1; - for (auto& rCharacterSprm : m_aStates.top().getCharacterSprms()) - { - if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapNone - || rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight) - { - nWrap = rCharacterSprm.first; - - // If there is a wrap polygon prepared by RTFSdrImport, pick it up here. - if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight - && !m_aStates.top().getShape().getWrapPolygonSprms().empty()) - rCharacterSprm.second->getSprms().set( - NS_ooxml::LN_CT_WrapTight_wrapPolygon, - new RTFValue(RTFSprms(), m_aStates.top().getShape().getWrapPolygonSprms())); - - aAnchorSprms.set(rCharacterSprm.first, rCharacterSprm.second); - } - } - - if (m_aStates.top().getShape().getWrapSprm().first != 0) - // Replay of a buffered shape, wrap sprm there has priority over - // character sprms of the current state. - aAnchorSprms.set(m_aStates.top().getShape().getWrapSprm().first, - m_aStates.top().getShape().getWrapSprm().second); - - aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_extent, pExtentValue); - if (!aAnchorWrapAttributes.empty() && nWrap == -1) - aAnchorSprms.set(NS_ooxml::LN_EG_WrapType_wrapSquare, - new RTFValue(aAnchorWrapAttributes)); - - // See OOXMLFastContextHandler::positionOffset(), we can't just put offset values in an RTFValue. - RTFSprms aPoshAttributes; - RTFSprms aPoshSprms; - if (m_aStates.top().getShape().getHoriOrientRelationToken() > 0) - aPoshAttributes.set( - NS_ooxml::LN_CT_PosH_relativeFrom, - new RTFValue(m_aStates.top().getShape().getHoriOrientRelationToken())); - if (m_aStates.top().getShape().getLeft() != 0) - { - Mapper().positionOffset(OUString::number(oox::drawingml::convertHmmToEmu( - m_aStates.top().getShape().getLeft())), - /*bVertical=*/false); - aPoshSprms.set(NS_ooxml::LN_CT_PosH_posOffset, new RTFValue()); - } - aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_positionH, - new RTFValue(aPoshAttributes, aPoshSprms)); - - RTFSprms aPosvAttributes; - RTFSprms aPosvSprms; - if (m_aStates.top().getShape().getVertOrientRelationToken() > 0) - aPosvAttributes.set( - NS_ooxml::LN_CT_PosV_relativeFrom, - new RTFValue(m_aStates.top().getShape().getVertOrientRelationToken())); - if (m_aStates.top().getShape().getTop() != 0) - { - Mapper().positionOffset(OUString::number(oox::drawingml::convertHmmToEmu( - m_aStates.top().getShape().getTop())), - /*bVertical=*/true); - aPosvSprms.set(NS_ooxml::LN_CT_PosV_posOffset, new RTFValue()); - } - aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_positionV, - new RTFValue(aPosvAttributes, aPosvSprms)); - - aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_docPr, pDocprValue); - aAnchorSprms.set(NS_ooxml::LN_graphic_graphic, pGraphicValue); - // anchor sprm - auto pValue = new RTFValue(m_aStates.top().getShape().getAnchorAttributes(), aAnchorSprms); - aSprms.set(NS_ooxml::LN_anchor_anchor, pValue); - } - checkFirstRun(); - - if (!m_aStates.top().getCurrentBuffer()) - { - writerfilter::Reference<Properties>::Pointer_t pProperties - = new RTFReferenceProperties(std::move(aAttributes), std::move(aSprms)); - Mapper().props(pProperties); - // Make sure we don't lose these properties with a too early reset. - m_bHadPicture = true; - } - else - { - auto pValue = new RTFValue(aAttributes, aSprms); - bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue, nullptr); - } -} - -RTFError RTFDocumentImpl::resolveChars(char ch) -{ - if (m_aStates.top().getInternalState() == RTFInternalState::BIN) - { - m_pBinaryData = std::make_shared<SvMemoryStream>(); - m_pBinaryData->WriteChar(ch); - for (int i = 0; i < m_aStates.top().getBinaryToRead() - 1; ++i) - { - Strm().ReadChar(ch); - m_pBinaryData->WriteChar(ch); - } - m_aStates.top().setInternalState(RTFInternalState::NORMAL); - return RTFError::OK; - } - - OStringBuffer aBuf(512); - - bool bUnicodeChecked = false; - bool bSkipped = false; - - while (!Strm().eof() - && (m_aStates.top().getInternalState() == RTFInternalState::HEX - || (ch != '{' && ch != '}' && ch != '\\'))) - { - if (m_aStates.top().getInternalState() == RTFInternalState::HEX - || (ch != 0x0d && ch != 0x0a)) - { - if (m_aStates.top().getCharsToSkip() == 0) - { - if (!bUnicodeChecked) - { - checkUnicode(/*bUnicode =*/true, /*bHex =*/false); - bUnicodeChecked = true; - } - aBuf.append(ch); - } - else - { - bSkipped = true; - m_aStates.top().getCharsToSkip()--; - } - } - - // read a single char if we're in hex mode - if (m_aStates.top().getInternalState() == RTFInternalState::HEX) - break; - - if (RTL_TEXTENCODING_MS_932 == m_aStates.top().getCurrentEncoding()) - { - unsigned char uch = ch; - if ((uch >= 0x80 && uch <= 0x9F) || uch >= 0xE0) - { - // read second byte of 2-byte Shift-JIS - may be \ { } - Strm().ReadChar(ch); - if (m_aStates.top().getCharsToSkip() == 0) - { - // fdo#79384: Word will reject Shift-JIS following \loch - // but apparently OOo could read and (worse) write such documents - SAL_INFO_IF(m_aStates.top().getRunType() != RTFParserState::RunType::DBCH, - "writerfilter.rtf", "invalid Shift-JIS without DBCH"); - assert(bUnicodeChecked); - aBuf.append(ch); - } - else - { - assert(bSkipped); - // anybody who uses \ucN with Shift-JIS is insane - m_aStates.top().getCharsToSkip()--; - } - } - } - - Strm().ReadChar(ch); - } - if (m_aStates.top().getInternalState() != RTFInternalState::HEX && !Strm().eof()) - Strm().SeekRel(-1); - - if (m_aStates.top().getInternalState() == RTFInternalState::HEX - && m_aStates.top().getDestination() != Destination::LEVELNUMBERS) - { - if (!bSkipped) - { - // note: apparently \'0d\'0a is interpreted as 2 breaks, not 1 - if ((ch == '\r' || ch == '\n') - && m_aStates.top().getDestination() != Destination::DOCCOMM - && m_aStates.top().getDestination() != Destination::LEVELNUMBERS - && m_aStates.top().getDestination() != Destination::LEVELTEXT) - { - checkUnicode(/*bUnicode =*/false, /*bHex =*/true); - dispatchSymbol(RTFKeyword::PAR); - } - else - { - m_aHexBuffer.append(ch); - } - } - return RTFError::OK; - } - - if (m_aStates.top().getDestination() == Destination::SKIP) - return RTFError::OK; - OString aStr = aBuf.makeStringAndClear(); - if (m_aStates.top().getDestination() == Destination::LEVELNUMBERS) - { - if (aStr.toChar() != ';') - m_aStates.top().getLevelNumbers().push_back(sal_Int32(ch)); - return RTFError::OK; - } - - SAL_INFO("writerfilter.rtf", - "RTFDocumentImpl::resolveChars: collected '" - << OStringToOUString(aStr, m_aStates.top().getCurrentEncoding()) << "'"); - - if (m_aStates.top().getDestination() == Destination::COLORTABLE) - { - // we hit a ';' at the end of each color entry - m_aColorTable.push_back(m_aStates.top().getCurrentColor().GetColor()); - // set components back to zero - m_aStates.top().getCurrentColor() = RTFColorTableEntry(); - } - else if (!aStr.isEmpty()) - m_aHexBuffer.append(aStr); - - checkUnicode(/*bUnicode =*/false, /*bHex =*/true); - return RTFError::OK; -} - -void RTFDocumentImpl::singleChar(sal_uInt8 nValue, bool bRunProps) -{ - sal_uInt8 sValue[] = { nValue }; - RTFBuffer_t* pCurrentBuffer = m_aStates.top().getCurrentBuffer(); - - if (!pCurrentBuffer) - { - Mapper().startCharacterGroup(); - } - else - { - pCurrentBuffer->emplace_back(BUFFER_STARTRUN, nullptr, nullptr); - } - - // Should we send run properties? - if (bRunProps) - runProps(); - - if (!pCurrentBuffer) - { - Mapper().text(sValue, 1); - Mapper().endCharacterGroup(); - } - else - { - auto pValue = new RTFValue(*sValue); - pCurrentBuffer->emplace_back(BUFFER_TEXT, pValue, nullptr); - pCurrentBuffer->emplace_back(BUFFER_ENDRUN, nullptr, nullptr); - } -} - -void RTFDocumentImpl::handleFontTableEntry() -{ - OUString aName = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); - - if (aName.isEmpty()) - return; - - if (aName.endsWith(";")) - { - aName = aName.copy(0, aName.getLength() - 1); - } - - // Old documents can contain no encoding information in fontinfo, - // but there can be font name suffixes: Arial CE is not a special - // font, it is ordinal Arial, but with used cp 1250 encoding. - // Moreover these suffixes have priority over \cpgN and \fcharsetN - // in MS Word. - OUString aFontSuffix; - OUString aNameNoSuffix(aName); - sal_Int32 nLastSpace = aName.lastIndexOf(' '); - if (nLastSpace >= 0) - { - aFontSuffix = aName.copy(nLastSpace + 1); - aNameNoSuffix = aName.copy(0, nLastSpace); - sal_Int32 nEncoding = RTL_TEXTENCODING_DONTKNOW; - for (int i = 0; aRTFFontNameSuffixes[i].codepage != RTL_TEXTENCODING_DONTKNOW; i++) - { - if (aFontSuffix.equalsAscii(aRTFFontNameSuffixes[i].suffix)) - { - nEncoding = aRTFFontNameSuffixes[i].codepage; - break; - } - } - if (nEncoding > RTL_TEXTENCODING_DONTKNOW) - { - m_nCurrentEncoding = nEncoding; - m_aStates.top().setCurrentEncoding(m_nCurrentEncoding); - } - else - { - // Unknown suffix: looks like it is just a part of font name, restore it - aNameNoSuffix = aName; - } - } - - m_aFontNames[m_nCurrentFontIndex] = aNameNoSuffix; - if (m_nCurrentEncoding >= 0) - { - m_aFontEncodings[m_nCurrentFontIndex] = m_nCurrentEncoding; - m_nCurrentEncoding = -1; - } - m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Font_name, - new RTFValue(aNameNoSuffix)); - - writerfilter::Reference<Properties>::Pointer_t const pProp(new RTFReferenceProperties( - m_aStates.top().getTableAttributes(), m_aStates.top().getTableSprms())); - - //See fdo#47347 initial invalid font entry properties are inserted first, - //so when we attempt to insert the correct ones, there's already an - //entry in the map for them, so the new ones aren't inserted. - auto lb = m_aFontTableEntries.lower_bound(m_nCurrentFontIndex); - if (lb != m_aFontTableEntries.end() - && !(m_aFontTableEntries.key_comp()(m_nCurrentFontIndex, lb->first))) - lb->second = pProp; - else - m_aFontTableEntries.insert(lb, std::make_pair(m_nCurrentFontIndex, pProp)); -} - -void RTFDocumentImpl::text(OUString& rString) -{ - if (rString.getLength() == 1 && m_aStates.top().getDestination() != Destination::DOCCOMM) - { - // No cheating! Tokenizer ignores bare \r and \n, their hex \'0d / \'0a form doesn't count, either. - sal_Unicode ch = rString[0]; - if (ch == 0x0d || ch == 0x0a) - return; - } - - bool bRet = true; - switch (m_aStates.top().getDestination()) - { - // Note: in stylesheet and revtbl groups are mandatory - case Destination::STYLEENTRY: - case Destination::LISTNAME: - case Destination::REVISIONENTRY: - { - // ; is the end of the entry - bool bEnd = false; - if (rString.endsWith(";")) - { - rString = rString.copy(0, rString.getLength() - 1); - bEnd = true; - } - m_aStates.top().appendDestinationText(rString); - if (bEnd) - { - // always clear, necessary in case of group-less fonttable - OUString const aName - = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); - switch (m_aStates.top().getDestination()) - { - case Destination::STYLEENTRY: - { - RTFValue::Pointer_t pType - = m_aStates.top().getTableAttributes().find(NS_ooxml::LN_CT_Style_type); - if (pType) - { - // Word strips whitespace around style names. - m_aStyleNames[m_nCurrentStyleIndex] = aName.trim(); - m_aStyleTypes[m_nCurrentStyleIndex] = pType->getInt(); - auto pValue = new RTFValue(aName.trim()); - m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Style_styleId, - pValue); - m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Style_name, pValue); - - writerfilter::Reference<Properties>::Pointer_t const pProp( - createStyleProperties()); - m_pStyleTableEntries->insert( - std::make_pair(m_nCurrentStyleIndex, pProp)); - } - else - SAL_INFO("writerfilter.rtf", "no RTF style type defined, ignoring"); - break; - } - case Destination::LISTNAME: - // TODO: what can be done with a list name? - break; - case Destination::REVISIONENTRY: - m_aAuthors[m_aAuthors.size()] = aName; - break; - default: - break; - } - resetAttributes(); - resetSprms(); - } - } - break; - case Destination::DOCVAR: - { - m_aStates.top().appendDocVar(rString); - } - break; - case Destination::FONTTABLE: - case Destination::FONTENTRY: - case Destination::LEVELTEXT: - case Destination::SHAPEPROPERTYNAME: - case Destination::SHAPEPROPERTYVALUE: - case Destination::BOOKMARKEND: - case Destination::PICT: - case Destination::SHAPEPROPERTYVALUEPICT: - case Destination::FORMFIELDNAME: - case Destination::FORMFIELDLIST: - case Destination::DATAFIELD: - case Destination::AUTHOR: - case Destination::KEYWORDS: - case Destination::OPERATOR: - case Destination::COMPANY: - case Destination::COMMENT: - case Destination::OBJDATA: - case Destination::OBJCLASS: - case Destination::ANNOTATIONDATE: - case Destination::ANNOTATIONAUTHOR: - case Destination::ANNOTATIONREFERENCE: - case Destination::FALT: - case Destination::PARAGRAPHNUMBERING_TEXTAFTER: - case Destination::PARAGRAPHNUMBERING_TEXTBEFORE: - case Destination::TITLE: - case Destination::SUBJECT: - case Destination::DOCCOMM: - case Destination::ATNID: - case Destination::ANNOTATIONREFERENCESTART: - case Destination::ANNOTATIONREFERENCEEND: - case Destination::MR: - case Destination::MCHR: - case Destination::MPOS: - case Destination::MVERTJC: - case Destination::MSTRIKEH: - case Destination::MDEGHIDE: - case Destination::MBEGCHR: - case Destination::MSEPCHR: - case Destination::MENDCHR: - case Destination::MSUBHIDE: - case Destination::MSUPHIDE: - case Destination::MTYPE: - case Destination::MGROW: - case Destination::INDEXENTRY: - case Destination::TOCENTRY: - case Destination::PROPNAME: - case Destination::STATICVAL: - m_aStates.top().appendDestinationText(rString); - break; - case Destination::GENERATOR: - // don't enlarge space sequences, eg. it was saved in LibreOffice - if (!rString.startsWithIgnoreAsciiCase("Microsoft")) - m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_longerSpaceSequence, - new RTFValue(0)); - break; - default: - bRet = false; - break; - } - if (bRet) - return; - - if (!m_aIgnoreFirst.isEmpty() && m_aIgnoreFirst == rString) - { - m_aIgnoreFirst.clear(); - return; - } - - // Are we in the middle of the table definition? (No cell defs yet, but we already have some cell props.) - if (m_aStates.top().getTableCellSprms().find(NS_ooxml::LN_CT_TcPrBase_vAlign) - && m_nTopLevelCells == 0) - { - m_aTableBufferStack.back().emplace_back(BUFFER_UTEXT, new RTFValue(rString), nullptr); - return; - } - - checkFirstRun(); - checkNeedPap(); - - // Don't return earlier, a bookmark start has to be in a paragraph group. - if (m_aStates.top().getDestination() == Destination::BOOKMARKSTART) - { - m_aStates.top().appendDestinationText(rString); - return; - } - - RTFBuffer_t* pCurrentBuffer = m_aStates.top().getCurrentBuffer(); - - if (!pCurrentBuffer && m_aStates.top().getDestination() != Destination::FOOTNOTE) - Mapper().startCharacterGroup(); - else if (pCurrentBuffer) - { - RTFValue::Pointer_t pValue; - pCurrentBuffer->emplace_back(BUFFER_STARTRUN, pValue, nullptr); - } - - if (m_aStates.top().getDestination() == Destination::NORMAL - || m_aStates.top().getDestination() == Destination::FIELDRESULT - || m_aStates.top().getDestination() == Destination::SHAPETEXT) - runProps(); - - if (!pCurrentBuffer) - Mapper().utext(rString.getStr(), rString.getLength()); - else - { - auto pValue = new RTFValue(rString); - pCurrentBuffer->emplace_back(BUFFER_UTEXT, pValue, nullptr); - } - - m_bNeedCr = true; - - if (!pCurrentBuffer && m_aStates.top().getDestination() != Destination::FOOTNOTE) - Mapper().endCharacterGroup(); - else if (pCurrentBuffer) - { - RTFValue::Pointer_t pValue; - pCurrentBuffer->emplace_back(BUFFER_ENDRUN, pValue, nullptr); - } -} - -void RTFDocumentImpl::prepareProperties( - RTFParserState& rState, writerfilter::Reference<Properties>::Pointer_t& o_rpParagraphProperties, - writerfilter::Reference<Properties>::Pointer_t& o_rpFrameProperties, - writerfilter::Reference<Properties>::Pointer_t& o_rpTableRowProperties, int const nCells, - int const nCurrentCellX) -{ - o_rpParagraphProperties - = getProperties(rState.getParagraphAttributes(), rState.getParagraphSprms(), - NS_ooxml::LN_Value_ST_StyleType_paragraph); - - if (rState.getFrame().hasProperties()) - { - o_rpFrameProperties = new RTFReferenceProperties(RTFSprms(), rState.getFrame().getSprms()); - } - - // Table width. - RTFValue::Pointer_t const pTableWidthProps - = rState.getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblW); - if (!pTableWidthProps) - { - auto pUnitValue = new RTFValue(3); - putNestedAttribute(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblW, - NS_ooxml::LN_CT_TblWidth_type, pUnitValue); - auto pWValue = new RTFValue(nCurrentCellX); - putNestedAttribute(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblW, - NS_ooxml::LN_CT_TblWidth_w, pWValue); - } - - if (nCells > 0) - rState.getTableRowSprms().set(NS_ooxml::LN_tblRow, new RTFValue(1)); - - RTFValue::Pointer_t const pCellMar - = rState.getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblCellMar); - if (!pCellMar) - { - // If no cell margins are defined, the default left/right margin is 0 in Word, but not in Writer. - RTFSprms aAttributes; - aAttributes.set(NS_ooxml::LN_CT_TblWidth_type, - new RTFValue(NS_ooxml::LN_Value_ST_TblWidth_dxa)); - aAttributes.set(NS_ooxml::LN_CT_TblWidth_w, new RTFValue(0)); - putNestedSprm(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblCellMar, - NS_ooxml::LN_CT_TblCellMar_left, new RTFValue(aAttributes)); - putNestedSprm(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblCellMar, - NS_ooxml::LN_CT_TblCellMar_right, new RTFValue(aAttributes)); - } - - o_rpTableRowProperties - = new RTFReferenceProperties(rState.getTableRowAttributes(), rState.getTableRowSprms()); -} - -void RTFDocumentImpl::sendProperties( - writerfilter::Reference<Properties>::Pointer_t const& pParagraphProperties, - writerfilter::Reference<Properties>::Pointer_t const& pFrameProperties, - writerfilter::Reference<Properties>::Pointer_t const& pTableRowProperties) -{ - Mapper().props(pParagraphProperties); - - if (pFrameProperties) - { - Mapper().props(pFrameProperties); - } - - Mapper().props(pTableRowProperties); - - tableBreak(); -} - -void RTFDocumentImpl::replayRowBuffer(RTFBuffer_t& rBuffer, ::std::deque<RTFSprms>& rCellsSprms, - ::std::deque<RTFSprms>& rCellsAttributes, int const nCells) -{ - for (int i = 0; i < nCells; ++i) - { - replayBuffer(rBuffer, &rCellsSprms.front(), &rCellsAttributes.front()); - rCellsSprms.pop_front(); - rCellsAttributes.pop_front(); - } - for (Buf_t& i : rBuffer) - { - SAL_WARN_IF(BUFFER_CELLEND == std::get<0>(i), "writerfilter.rtf", "dropping table cell!"); - } - assert(rCellsSprms.empty()); - assert(rCellsAttributes.empty()); -} - -void RTFDocumentImpl::replayBuffer(RTFBuffer_t& rBuffer, RTFSprms* const pSprms, - RTFSprms const* const pAttributes) -{ - while (!rBuffer.empty()) - { - Buf_t aTuple(rBuffer.front()); - rBuffer.pop_front(); - if (std::get<0>(aTuple) == BUFFER_PROPS || std::get<0>(aTuple) == BUFFER_PROPS_CHAR) - { - // Construct properties via getProperties() and not directly, to take care of deduplication. - writerfilter::Reference<Properties>::Pointer_t const pProp(getProperties( - std::get<1>(aTuple)->getAttributes(), std::get<1>(aTuple)->getSprms(), - std::get<0>(aTuple) == BUFFER_PROPS_CHAR ? NS_ooxml::LN_Value_ST_StyleType_character - : 0)); - Mapper().props(pProp); - } - else if (std::get<0>(aTuple) == BUFFER_NESTROW) - { - TableRowBuffer& rRowBuffer(*std::get<2>(aTuple)); - - replayRowBuffer(rRowBuffer.GetBuffer(), rRowBuffer.GetCellsSprms(), - rRowBuffer.GetCellsAttributes(), rRowBuffer.GetCells()); - - sendProperties(rRowBuffer.GetParaProperties(), rRowBuffer.GetFrameProperties(), - rRowBuffer.GetRowProperties()); - } - else if (std::get<0>(aTuple) == BUFFER_CELLEND) - { - assert(pSprms && pAttributes); - auto pValue = new RTFValue(1); - pSprms->set(NS_ooxml::LN_tblCell, pValue); - writerfilter::Reference<Properties>::Pointer_t const pTableCellProperties( - new RTFReferenceProperties(*pAttributes, *pSprms)); - Mapper().props(pTableCellProperties); - tableBreak(); - break; - } - else if (std::get<0>(aTuple) == BUFFER_STARTRUN) - Mapper().startCharacterGroup(); - else if (std::get<0>(aTuple) == BUFFER_TEXT) - { - sal_uInt8 const nValue = std::get<1>(aTuple)->getInt(); - Mapper().text(&nValue, 1); - } - else if (std::get<0>(aTuple) == BUFFER_UTEXT) - { - OUString const aString(std::get<1>(aTuple)->getString()); - Mapper().utext(aString.getStr(), aString.getLength()); - } - else if (std::get<0>(aTuple) == BUFFER_ENDRUN) - Mapper().endCharacterGroup(); - else if (std::get<0>(aTuple) == BUFFER_PAR) - parBreak(); - else if (std::get<0>(aTuple) == BUFFER_STARTSHAPE) - m_pSdrImport->resolve(std::get<1>(aTuple)->getShape(), false, RTFSdrImport::SHAPE); - else if (std::get<0>(aTuple) == BUFFER_RESOLVESHAPE) - { - // Make sure there is no current buffer while replaying the shape, - // otherwise it gets re-buffered. - RTFBuffer_t* pCurrentBuffer = m_aStates.top().getCurrentBuffer(); - m_aStates.top().setCurrentBuffer(nullptr); - - // Set current shape during replay, needed by e.g. wrap in - // background. - RTFShape aShape = m_aStates.top().getShape(); - m_aStates.top().getShape() = std::get<1>(aTuple)->getShape(); - - m_pSdrImport->resolve(std::get<1>(aTuple)->getShape(), true, RTFSdrImport::SHAPE); - m_aStates.top().getShape() = std::move(aShape); - m_aStates.top().setCurrentBuffer(pCurrentBuffer); - } - else if (std::get<0>(aTuple) == BUFFER_ENDSHAPE) - m_pSdrImport->close(); - else if (std::get<0>(aTuple) == BUFFER_RESOLVESUBSTREAM) - { - RTFSprms& rAttributes = std::get<1>(aTuple)->getAttributes(); - std::size_t nPos = rAttributes.find(0)->getInt(); - Id nId = rAttributes.find(1)->getInt(); - OUString aCustomMark = rAttributes.find(2)->getString(); - resolveSubstream(nPos, nId, aCustomMark); - } - else if (std::get<0>(aTuple) == BUFFER_PICTURE) - m_aStates.top().getPicture() = std::get<1>(aTuple)->getPicture(); - else if (std::get<0>(aTuple) == BUFFER_SETSTYLE) - { - if (!m_aStates.empty()) - m_aStates.top().setCurrentStyleIndex(std::get<1>(aTuple)->getInt()); - } - else - assert(false); - } -} - -bool findPropertyName(const std::vector<beans::PropertyValue>& rProperties, const OUString& rName) -{ - return std::any_of( - rProperties.begin(), rProperties.end(), - [&rName](const beans::PropertyValue& rProperty) { return rProperty.Name == rName; }); -} - -void RTFDocumentImpl::backupTableRowProperties() -{ - if (m_nTopLevelCurrentCellX) - { - m_aBackupTableRowSprms = m_aStates.top().getTableRowSprms(); - m_aBackupTableRowAttributes = m_aStates.top().getTableRowAttributes(); - m_nBackupTopLevelCurrentCellX = m_nTopLevelCurrentCellX; - } -} - -void RTFDocumentImpl::restoreTableRowProperties() -{ - m_aStates.top().getTableRowSprms() = m_aBackupTableRowSprms; - m_aStates.top().getTableRowAttributes() = m_aBackupTableRowAttributes; - m_nTopLevelCurrentCellX = m_nBackupTopLevelCurrentCellX; -} - -void RTFDocumentImpl::resetTableRowProperties() -{ - m_aStates.top().getTableRowSprms() = m_aDefaultState.getTableRowSprms(); - m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol, new RTFValue(-1), - RTFOverwrite::NO_APPEND); - m_aStates.top().getTableRowAttributes() = m_aDefaultState.getTableRowAttributes(); - if (Destination::NESTEDTABLEPROPERTIES == m_aStates.top().getDestination()) - { - m_nNestedTRLeft = 0; - m_nNestedCurrentCellX = 0; - } - else - { - m_nTopLevelTRLeft = 0; - m_nTopLevelCurrentCellX = 0; - } -} - -RTFError RTFDocumentImpl::dispatchToggle(RTFKeyword nKeyword, bool bParam, int nParam) -{ - setNeedSect(true); - checkUnicode(/*bUnicode =*/true, /*bHex =*/true); - RTFSkipDestination aSkip(*this); - int nSprm = -1; - tools::SvRef<RTFValue> pBoolValue(new RTFValue(int(!bParam || nParam != 0))); - - // Underline toggles. - switch (nKeyword) - { - case RTFKeyword::UL: - nSprm = NS_ooxml::LN_Value_ST_Underline_single; - break; - case RTFKeyword::ULDASH: - nSprm = NS_ooxml::LN_Value_ST_Underline_dash; - break; - case RTFKeyword::ULDASHD: - nSprm = NS_ooxml::LN_Value_ST_Underline_dotDash; - break; - case RTFKeyword::ULDASHDD: - nSprm = NS_ooxml::LN_Value_ST_Underline_dotDotDash; - break; - case RTFKeyword::ULDB: - nSprm = NS_ooxml::LN_Value_ST_Underline_double; - break; - case RTFKeyword::ULHWAVE: - nSprm = NS_ooxml::LN_Value_ST_Underline_wavyHeavy; - break; - case RTFKeyword::ULLDASH: - nSprm = NS_ooxml::LN_Value_ST_Underline_dashLong; - break; - case RTFKeyword::ULTH: - nSprm = NS_ooxml::LN_Value_ST_Underline_thick; - break; - case RTFKeyword::ULTHD: - nSprm = NS_ooxml::LN_Value_ST_Underline_dottedHeavy; - break; - case RTFKeyword::ULTHDASH: - nSprm = NS_ooxml::LN_Value_ST_Underline_dashedHeavy; - break; - case RTFKeyword::ULTHDASHD: - nSprm = NS_ooxml::LN_Value_ST_Underline_dashDotHeavy; - break; - case RTFKeyword::ULTHDASHDD: - nSprm = NS_ooxml::LN_Value_ST_Underline_dashDotDotHeavy; - break; - case RTFKeyword::ULTHLDASH: - nSprm = NS_ooxml::LN_Value_ST_Underline_dashLongHeavy; - break; - case RTFKeyword::ULULDBWAVE: - nSprm = NS_ooxml::LN_Value_ST_Underline_wavyDouble; - break; - case RTFKeyword::ULWAVE: - nSprm = NS_ooxml::LN_Value_ST_Underline_wave; - break; - default: - break; - } - if (nSprm >= 0) - { - auto pValue - = new RTFValue((!bParam || nParam != 0) ? nSprm : NS_ooxml::LN_Value_ST_Underline_none); - m_aStates.top().getCharacterAttributes().set(NS_ooxml::LN_CT_Underline_val, pValue); - return RTFError::OK; - } - - // Accent characters (over dot / over comma). - switch (nKeyword) - { - case RTFKeyword::ACCNONE: - nSprm = NS_ooxml::LN_Value_ST_Em_none; - break; - case RTFKeyword::ACCDOT: - nSprm = NS_ooxml::LN_Value_ST_Em_dot; - break; - case RTFKeyword::ACCCOMMA: - nSprm = NS_ooxml::LN_Value_ST_Em_comma; - break; - case RTFKeyword::ACCCIRCLE: - nSprm = NS_ooxml::LN_Value_ST_Em_circle; - break; - case RTFKeyword::ACCUNDERDOT: - nSprm = NS_ooxml::LN_Value_ST_Em_underDot; - break; - default: - break; - } - if (nSprm >= 0) - { - auto pValue = new RTFValue((!bParam || nParam != 0) ? nSprm : 0); - m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_RPrBase_em, pValue); - return RTFError::OK; - } - - // Trivial character sprms. - switch (nKeyword) - { - case RTFKeyword::B: - case RTFKeyword::AB: - switch (m_aStates.top().getRunType()) - { - case RTFParserState::RunType::HICH: - case RTFParserState::RunType::RTLCH_LTRCH_1: - case RTFParserState::RunType::LTRCH_RTLCH_2: - nSprm = NS_ooxml::LN_EG_RPrBase_bCs; - break; - case RTFParserState::RunType::NONE: - case RTFParserState::RunType::LOCH: - case RTFParserState::RunType::LTRCH_RTLCH_1: - case RTFParserState::RunType::RTLCH_LTRCH_2: - case RTFParserState::RunType::DBCH: - default: - nSprm = NS_ooxml::LN_EG_RPrBase_b; - break; - } - break; - case RTFKeyword::I: - case RTFKeyword::AI: - switch (m_aStates.top().getRunType()) - { - case RTFParserState::RunType::HICH: - case RTFParserState::RunType::RTLCH_LTRCH_1: - case RTFParserState::RunType::LTRCH_RTLCH_2: - nSprm = NS_ooxml::LN_EG_RPrBase_iCs; - break; - case RTFParserState::RunType::NONE: - case RTFParserState::RunType::LOCH: - case RTFParserState::RunType::LTRCH_RTLCH_1: - case RTFParserState::RunType::RTLCH_LTRCH_2: - case RTFParserState::RunType::DBCH: - default: - nSprm = NS_ooxml::LN_EG_RPrBase_i; - break; - } - break; - case RTFKeyword::OUTL: - nSprm = NS_ooxml::LN_EG_RPrBase_outline; - break; - case RTFKeyword::SHAD: - nSprm = NS_ooxml::LN_EG_RPrBase_shadow; - break; - case RTFKeyword::V: - nSprm = NS_ooxml::LN_EG_RPrBase_vanish; - break; - case RTFKeyword::STRIKE: - nSprm = NS_ooxml::LN_EG_RPrBase_strike; - break; - case RTFKeyword::STRIKED: - nSprm = NS_ooxml::LN_EG_RPrBase_dstrike; - break; - case RTFKeyword::SCAPS: - nSprm = NS_ooxml::LN_EG_RPrBase_smallCaps; - break; - case RTFKeyword::IMPR: - nSprm = NS_ooxml::LN_EG_RPrBase_imprint; - break; - case RTFKeyword::CAPS: - nSprm = NS_ooxml::LN_EG_RPrBase_caps; - break; - default: - break; - } - if (nSprm >= 0) - { - if (m_aStates.top().getDestination() == Destination::LISTLEVEL) - { - m_aStates.top().getTableSprms().set(nSprm, pBoolValue); - } - else - { - m_aStates.top().getCharacterSprms().set(nSprm, pBoolValue); - } - return RTFError::OK; - } - - switch (nKeyword) - { - case RTFKeyword::ASPALPHA: - m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_autoSpaceDE, - pBoolValue); - break; - case RTFKeyword::DELETED: - case RTFKeyword::REVISED: - { - auto pValue - = new RTFValue(nKeyword == RTFKeyword::DELETED ? oox::XML_del : oox::XML_ins); - putNestedAttribute(m_aStates.top().getCharacterSprms(), NS_ooxml::LN_trackchange, - NS_ooxml::LN_token, pValue); - } - break; - case RTFKeyword::SBAUTO: - putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_spacing, - NS_ooxml::LN_CT_Spacing_beforeAutospacing, pBoolValue); - break; - case RTFKeyword::SAAUTO: - putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_spacing, - NS_ooxml::LN_CT_Spacing_afterAutospacing, pBoolValue); - break; - case RTFKeyword::FACINGP: - m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_evenAndOddHeaders, pBoolValue); - break; - case RTFKeyword::HYPHAUTO: - m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_autoHyphenation, pBoolValue); - break; - case RTFKeyword::HYPHPAR: - m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_suppressAutoHyphens, - new RTFValue(int(bParam && nParam == 0))); - break; - default: - { - SAL_INFO("writerfilter.rtf", - "TODO handle toggle '" << keywordToString(nKeyword) << "'"); - aSkip.setParsed(false); - } - break; - } - return RTFError::OK; -} - -RTFError RTFDocumentImpl::pushState() -{ - //SAL_INFO("writerfilter.rtf", __func__ << " before push: " << m_pTokenizer->getGroup()); - - checkUnicode(/*bUnicode =*/true, /*bHex =*/true); - m_nGroupStartPos = Strm().Tell(); - - if (m_aStates.empty()) - m_aStates.push(m_aDefaultState); - else - { - // fdo#85812 group resets run type of _current_ and new state (but not RTL) - if (m_aStates.top().getRunType() != RTFParserState::RunType::LTRCH_RTLCH_2 - && m_aStates.top().getRunType() != RTFParserState::RunType::RTLCH_LTRCH_2) - { - m_aStates.top().setRunType(RTFParserState::RunType::NONE); - } - - if (m_aStates.top().getDestination() == Destination::MR) - lcl_DestinationToMath(m_aStates.top().getCurrentDestinationText(), m_aMathBuffer, - m_bMathNor); - m_aStates.push(m_aStates.top()); - } - m_aStates.top().getDestinationText().setLength(0); // was copied: always reset! - - m_pTokenizer->pushGroup(); - - switch (m_aStates.top().getDestination()) - { - case Destination::FONTTABLE: - // this is a "faked" destination for the font entry - m_aStates.top().setCurrentDestinationText(&m_aStates.top().getDestinationText()); - m_aStates.top().setDestination(Destination::FONTENTRY); - break; - case Destination::STYLESHEET: - // this is a "faked" destination for the style sheet entry - m_aStates.top().setCurrentDestinationText(&m_aStates.top().getDestinationText()); - m_aStates.top().setDestination(Destination::STYLEENTRY); - { - // the *default* is \s0 i.e. paragraph style default - // this will be overwritten by \sN \csN \dsN \tsN - m_nCurrentStyleIndex = 0; - auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_StyleType_paragraph); - m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Style_type, pValue); - } - break; - case Destination::FIELDRESULT: - case Destination::SHAPETEXT: - case Destination::FORMFIELD: - //TODO: if this is pushed then the font encoding is used which results in a broken command string - // if it is not pushed to NORMAL then it is not restored in time. - case Destination::FIELDINSTRUCTION: - case Destination::PICT: - m_aStates.top().setDestination(Destination::NORMAL); - break; - case Destination::MNUM: - case Destination::MDEN: - case Destination::ME: - case Destination::MFNAME: - case Destination::MLIM: - case Destination::MSUB: - case Destination::MSUP: - case Destination::MDEG: - case Destination::MOMATH: - m_aStates.top().setDestination(Destination::MR); - break; - case Destination::REVISIONTABLE: - // this is a "faked" destination for the revision table entry - m_aStates.top().setCurrentDestinationText(&m_aStates.top().getDestinationText()); - m_aStates.top().setDestination(Destination::REVISIONENTRY); - break; - default: - break; - } - - // If this is true, then ooxml:endtrackchange will be generated. Make sure - // we don't generate more ooxml:endtrackchange than ooxml:trackchange: new - // state does not inherit this flag. - m_aStates.top().setStartedTrackchange(false); - - return RTFError::OK; -} - -writerfilter::Reference<Properties>::Pointer_t RTFDocumentImpl::createStyleProperties() -{ - int nBasedOn = 0; - RTFValue::Pointer_t pBasedOn - = m_aStates.top().getTableSprms().find(NS_ooxml::LN_CT_Style_basedOn); - if (pBasedOn) - nBasedOn = pBasedOn->getInt(); - if (nBasedOn == 0) - { - // No parent style, then mimic what Word does: ignore attributes which - // would set a margin as formatting, but with a default value. - for (const auto& nId : - { NS_ooxml::LN_CT_Ind_firstLine, NS_ooxml::LN_CT_Ind_left, NS_ooxml::LN_CT_Ind_right, - NS_ooxml::LN_CT_Ind_start, NS_ooxml::LN_CT_Ind_end }) - { - RTFValue::Pointer_t pValue = getNestedAttribute(m_aStates.top().getParagraphSprms(), - NS_ooxml::LN_CT_PPrBase_ind, nId); - if (pValue && pValue->getInt() == 0) - eraseNestedAttribute(m_aStates.top().getParagraphSprms(), - NS_ooxml::LN_CT_PPrBase_ind, nId); - } - } - - RTFValue::Pointer_t pParaProps = new RTFValue(m_aStates.top().getParagraphAttributes(), - m_aStates.top().getParagraphSprms()); - RTFValue::Pointer_t pCharProps = new RTFValue(m_aStates.top().getCharacterAttributes(), - m_aStates.top().getCharacterSprms()); - - // resetSprms will clean up this modification - m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Style_pPr, pParaProps); - m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Style_rPr, pCharProps); - - writerfilter::Reference<Properties>::Pointer_t pProps(new RTFReferenceProperties( - m_aStates.top().getTableAttributes(), m_aStates.top().getTableSprms())); - return pProps; -} - -/** 2 different representations of the styles are needed: - - 1) flat content, as read from the input file: - stored in m_pStyleTableEntries, used as reference input for - deduplication both here and for hard formatting in getProperties() - - 2) real content, with proper override of sprms/attributes where it differs - from parent style; this is produced here and sent to domain mapper - */ -RTFReferenceTable::Entries_t RTFDocumentImpl::deduplicateStyleTable() -{ - RTFReferenceTable::Entries_t ret; - for (auto const& it : *m_pStyleTableEntries) - { - auto pStyle = it.second; - ret[it.first] = pStyle; - // ugly downcasts here, but can't easily replace the members with - // RTFReferenceProperties because dmapper wants SvRef<Properties> anyway - RTFValue::Pointer_t const pBasedOn( - static_cast<RTFReferenceProperties&>(*pStyle).getSprms().find( - NS_ooxml::LN_CT_Style_basedOn)); - if (pBasedOn) - { - int const nBasedOn(pBasedOn->getInt()); - // don't deduplicate yourself - especially a potential problem for the default style. - if (it.first == nBasedOn) - continue; - - auto const itParent(m_pStyleTableEntries->find(nBasedOn)); // definition as read! - if (itParent != m_pStyleTableEntries->end()) - { - auto const pStyleType( - static_cast<RTFReferenceProperties&>(*pStyle).getAttributes().find( - NS_ooxml::LN_CT_Style_type)); - assert(pStyleType); - int const nStyleType(pStyleType->getInt()); - RTFSprms sprms( - static_cast<RTFReferenceProperties&>(*pStyle).getSprms().cloneAndDeduplicate( - static_cast<RTFReferenceProperties&>(*itParent->second).getSprms(), - nStyleType)); - RTFSprms attributes( - static_cast<RTFReferenceProperties&>(*pStyle) - .getAttributes() - .cloneAndDeduplicate( - static_cast<RTFReferenceProperties&>(*itParent->second).getAttributes(), - nStyleType)); - - ret[it.first] = new RTFReferenceProperties(std::move(attributes), std::move(sprms)); - } - else - { - SAL_WARN("writerfilter.rtf", "parent style not found: " << nBasedOn); - } - } - } - assert(ret.size() == m_pStyleTableEntries->size()); - return ret; -} - -void RTFDocumentImpl::resetSprms() -{ - m_aStates.top().getTableSprms().clear(); - m_aStates.top().getCharacterSprms().clear(); - m_aStates.top().getParagraphSprms().clear(); -} - -void RTFDocumentImpl::resetAttributes() -{ - m_aStates.top().getTableAttributes().clear(); - m_aStates.top().getCharacterAttributes().clear(); - m_aStates.top().getParagraphAttributes().clear(); -} - -static bool lcl_containsProperty(const uno::Sequence<beans::Property>& rProperties, - std::u16string_view rName) -{ - return std::any_of(rProperties.begin(), rProperties.end(), - [&](const beans::Property& rProperty) { return rProperty.Name == rName; }); -} - -RTFError RTFDocumentImpl::beforePopState(RTFParserState& rState) -{ - switch (rState.getDestination()) - { - //Note: in fonttbl there may or may not be groups, so process it as no groups - case Destination::FONTTABLE: - case Destination::FONTENTRY: - { - // Some text unhandled? Seems it is last font name - if (m_aStates.top().getCurrentDestinationText()->getLength()) - handleFontTableEntry(); - - if (rState.getDestination() == Destination::FONTTABLE) - { - writerfilter::Reference<Table>::Pointer_t const pTable( - new RTFReferenceTable(m_aFontTableEntries)); - Mapper().table(NS_ooxml::LN_FONTTABLE, pTable); - if (m_nDefaultFontIndex >= 0) - { - auto pValue = new RTFValue(m_aFontNames[getFontIndex(m_nDefaultFontIndex)]); - putNestedAttribute(m_aDefaultState.getCharacterSprms(), - NS_ooxml::LN_EG_RPrBase_rFonts, NS_ooxml::LN_CT_Fonts_ascii, - pValue); - } - } - } - break; - case Destination::STYLESHEET: - { - RTFReferenceTable::Entries_t pStyleTableDeduplicated(deduplicateStyleTable()); - writerfilter::Reference<Table>::Pointer_t const pTable( - new RTFReferenceTable(std::move(pStyleTableDeduplicated))); - Mapper().table(NS_ooxml::LN_STYLESHEET, pTable); - } - break; - case Destination::LISTOVERRIDETABLE: - { - RTFSprms aListTableAttributes; - writerfilter::Reference<Properties>::Pointer_t pProp - = new RTFReferenceProperties(std::move(aListTableAttributes), m_aListTableSprms); - RTFReferenceTable::Entries_t aListTableEntries; - aListTableEntries.insert(std::make_pair(0, pProp)); - writerfilter::Reference<Table>::Pointer_t const pTable( - new RTFReferenceTable(std::move(aListTableEntries))); - Mapper().table(NS_ooxml::LN_NUMBERING, pTable); - } - break; - case Destination::LISTENTRY: - for (const auto& rListLevelEntry : rState.getListLevelEntries()) - rState.getTableSprms().set(rListLevelEntry.first, rListLevelEntry.second, - RTFOverwrite::NO_APPEND); - break; - case Destination::FIELDINSTRUCTION: - { - auto pValue = new RTFValue(m_aFormfieldAttributes, m_aFormfieldSprms); - RTFSprms aFFAttributes; - RTFSprms aFFSprms; - aFFSprms.set(NS_ooxml::LN_ffdata, pValue); - if (!m_aStates.top().getCurrentBuffer()) - { - writerfilter::Reference<Properties>::Pointer_t pProperties - = new RTFReferenceProperties(std::move(aFFAttributes), std::move(aFFSprms)); - Mapper().props(pProperties); - } - else - { - auto pFFValue = new RTFValue(aFFAttributes, aFFSprms); - bufferProperties(*m_aStates.top().getCurrentBuffer(), pFFValue, nullptr); - } - m_aFormfieldAttributes.clear(); - m_aFormfieldSprms.clear(); - - if (m_aStates.top().isFieldLocked()) - singleChar(cFieldLock); - singleChar(cFieldSep, true); - } - break; - case Destination::FIELDRESULT: - singleChar(cFieldEnd); - - if (!m_aPicturePath.isEmpty()) - { - // Read the picture into m_aStates.top().aDestinationText. - pushState(); - dispatchDestination(RTFKeyword::PICT); - if (m_aPicturePath.endsWith(".png")) - dispatchFlag(RTFKeyword::PNGBLIP); - OUString aFileURL = m_rMediaDescriptor.getUnpackedValueOrDefault( - utl::MediaDescriptor::PROP_URL, OUString()); - OUString aPictureURL; - try - { - aPictureURL = rtl::Uri::convertRelToAbs(aFileURL, m_aPicturePath); - } - catch (const rtl::MalformedUriException& rException) - { - SAL_WARN("writerfilter.rtf", - "rtl::Uri::convertRelToAbs() failed: " << rException.getMessage()); - } - - if (!aPictureURL.isEmpty()) - { - SvFileStream aStream(aPictureURL, StreamMode::READ); - if (aStream.IsOpen()) - { - OUStringBuffer aBuf; - while (aStream.good()) - { - unsigned char ch = 0; - aStream.ReadUChar(ch); - if (ch < 16) - aBuf.append("0"); - aBuf.append(static_cast<sal_Int32>(ch), 16); - } - m_aStates.top().getDestinationText() = aBuf; - } - } - popState(); - m_aPicturePath.clear(); - } - - break; - case Destination::LEVELTEXT: - { - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); - - // The first character is the length of the string (the rest should be ignored). - sal_Int32 nLength(aStr.toChar()); - OUString aValue; - if (nLength < aStr.getLength()) - aValue = aStr.copy(1, nLength); - else - aValue = aStr; - auto pValue = new RTFValue(aValue, true); - rState.getTableAttributes().set(NS_ooxml::LN_CT_LevelText_val, pValue); - } - break; - case Destination::LEVELNUMBERS: - { - bool bNestedLevelNumbers = false; - if (m_aStates.size() > 1) - // Current destination is levelnumbers and parent destination is levelnumbers as well. - bNestedLevelNumbers - = m_aStates[m_aStates.size() - 2].getDestination() == Destination::LEVELNUMBERS; - if (!bNestedLevelNumbers && rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_lvlText)) - { - RTFSprms& rAttributes - = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_lvlText)->getAttributes(); - RTFValue::Pointer_t pValue = rAttributes.find(NS_ooxml::LN_CT_LevelText_val); - if (pValue && rState.getLevelNumbersValid()) - { - OUString aOrig = pValue->getString(); - - OUStringBuffer aBuf(aOrig.getLength() * 2); - sal_Int32 nReplaces = 1; - for (int i = 0; i < aOrig.getLength(); i++) - { - if (std::find(rState.getLevelNumbers().begin(), - rState.getLevelNumbers().end(), i + 1) - != rState.getLevelNumbers().end()) - { - aBuf.append('%'); - // '1.1.1' -> '%1.%2.%3', but '1.' (with '2.' prefix omitted) is %2. - aBuf.append(sal_Int32(nReplaces++ + rState.getListLevelNum() + 1 - - rState.getLevelNumbers().size())); - } - else - aBuf.append(aOrig[i]); - } - - pValue->setString(aBuf.makeStringAndClear()); - } - else if (pValue) - // Have a value, but levelnumbers is not valid -> ignore it. - pValue->setString(OUString()); - } - break; - } - case Destination::SHAPEPROPERTYNAME: - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - rState.getShape().getProperties().emplace_back( - m_aStates.top().getCurrentDestinationText()->makeStringAndClear(), OUString()); - break; - case Destination::SHAPEPROPERTYVALUE: - if (!rState.getShape().getProperties().empty()) - { - rState.getShape().getProperties().back().second - = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); - if (m_aStates.top().getHadShapeText()) - m_pSdrImport->append(rState.getShape().getProperties().back().first, - rState.getShape().getProperties().back().second); - else if (rState.getInShapeGroup() && !rState.getInShape() - && rState.getShape().getProperties().back().first == "rotation") - { - // Rotation should be applied on the groupshape itself, not on each shape. - rState.getShape().getGroupProperties().push_back( - rState.getShape().getProperties().back()); - rState.getShape().getProperties().pop_back(); - } - } - break; - case Destination::PICPROP: - case Destination::SHAPEINSTRUCTION: - if (m_aStates.size() > 1 - && m_aStates[m_aStates.size() - 2].getDestination() - == Destination::SHAPEINSTRUCTION) - { - // Do not resolve shape if shape instruction destination is inside other shape instruction - } - else if (!m_bObject && !rState.getInListpicture() && !rState.getHadShapeText() - && (!rState.getInShapeGroup() || rState.getInShape())) - { - // Don't trigger a shape import in case we're only leaving the \shpinst of the groupshape itself. - RTFSdrImport::ShapeOrPict eType - = (rState.getDestination() == Destination::SHAPEINSTRUCTION) - ? RTFSdrImport::SHAPE - : RTFSdrImport::PICT; - if (!m_aStates.top().getCurrentBuffer() || eType != RTFSdrImport::SHAPE) - m_pSdrImport->resolve(m_aStates.top().getShape(), true, eType); - else - { - // Shape inside table: buffer the import to have correct anchor position. - // Also buffer the RTFPicture of the state stack as it contains - // the shape size. - auto pPictureValue = new RTFValue(m_aStates.top().getPicture()); - m_aStates.top().getCurrentBuffer()->emplace_back(BUFFER_PICTURE, pPictureValue, - nullptr); - auto pValue = new RTFValue(m_aStates.top().getShape()); - - // Buffer wrap type. - for (const auto& rCharacterSprm : m_aStates.top().getCharacterSprms()) - { - if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapNone - || rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight) - { - m_aStates.top().getShape().getWrapSprm() = rCharacterSprm; - break; - } - } - - m_aStates.top().getCurrentBuffer()->emplace_back(BUFFER_RESOLVESHAPE, pValue, - nullptr); - } - } - else if (rState.getInShapeGroup() && !rState.getInShape()) - { - // End of a groupshape, as we're in shapegroup, but not in a real shape. - for (const auto& rGroupProperty : rState.getShape().getGroupProperties()) - m_pSdrImport->appendGroupProperty(rGroupProperty.first, rGroupProperty.second); - rState.getShape().getGroupProperties().clear(); - } - break; - case Destination::BOOKMARKSTART: - { - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); - int nPos = m_aBookmarks.size(); - m_aBookmarks[aStr] = nPos; - if (!m_aStates.top().getCurrentBuffer()) - Mapper().props(new RTFReferenceProperties(lcl_getBookmarkProperties(nPos, aStr))); - else - bufferProperties(*m_aStates.top().getCurrentBuffer(), - new RTFValue(lcl_getBookmarkProperties(nPos, aStr)), nullptr); - } - break; - case Destination::BOOKMARKEND: - { - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); - if (!m_aStates.top().getCurrentBuffer()) - Mapper().props(new RTFReferenceProperties( - lcl_getBookmarkProperties(m_aBookmarks[aStr], aStr))); - else - bufferProperties(*m_aStates.top().getCurrentBuffer(), - new RTFValue(lcl_getBookmarkProperties(m_aBookmarks[aStr], aStr)), - nullptr); - } - break; - case Destination::INDEXENTRY: - case Destination::TOCENTRY: - { - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - OUString str(m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); - // dmapper expects this as a field, so let's fake something... - auto const field((Destination::INDEXENTRY == rState.getDestination()) - ? std::u16string_view(u"XE") - : std::u16string_view(u"TC")); - str = OUString::Concat(field) + " \"" + str.replaceAll("\"", "\\\"") + "\""; - singleChar(cFieldStart); - Mapper().utext(str.getStr(), str.getLength()); - singleChar(cFieldSep, true); - // no result - singleChar(cFieldEnd); - } - break; - case Destination::FORMFIELDNAME: - { - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - auto pValue - = new RTFValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); - m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFData_name, pValue); - } - break; - case Destination::FORMFIELDLIST: - { - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - auto pValue - = new RTFValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); - // OOXML puts these into a LN_CT_FFData_ddList but FFDataHandler should handle this too - m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFDDList_listEntry, pValue, - RTFOverwrite::NO_APPEND); - } - break; - case Destination::DATAFIELD: - { - if (m_bFormField) - { - OUStringBuffer* pCurrentDestinationText - = m_aStates.top().getCurrentDestinationText(); - if (&m_aStates.top().getDestinationText() != pCurrentDestinationText) - break; // not for nested group - OString aStr - = OUStringToOString(*pCurrentDestinationText, rState.getCurrentEncoding()); - pCurrentDestinationText->setLength(0); - // decode hex dump - OStringBuffer aBuf; - int b = 0; - int count = 2; - for (int i = 0; i < aStr.getLength(); ++i) - { - char ch = aStr[i]; - if (ch != 0x0d && ch != 0x0a) - { - b = b << 4; - sal_Int8 parsed = msfilter::rtfutil::AsHex(ch); - if (parsed == -1) - return RTFError::HEX_INVALID; - b += parsed; - count--; - if (!count) - { - aBuf.append(static_cast<char>(b)); - count = 2; - b = 0; - } - } - } - aStr = aBuf.makeStringAndClear(); - - // ignore the first bytes - if (aStr.getLength() > 8) - aStr = aStr.copy(8); - // extract name - sal_Int32 nLength = aStr.toChar(); - if (!aStr.isEmpty()) - aStr = aStr.copy(1); - nLength = std::min(nLength, aStr.getLength()); - OString aName = aStr.copy(0, nLength); - if (aStr.getLength() > nLength) - aStr = aStr.copy(nLength + 1); // zero-terminated string - else - aStr.clear(); - // extract default text - nLength = aStr.toChar(); - if (!aStr.isEmpty()) - aStr = aStr.copy(1); - auto pNValue = new RTFValue(OStringToOUString(aName, rState.getCurrentEncoding())); - m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFData_name, pNValue); - if (nLength > 0) - { - OString aDefaultText = aStr.copy(0, std::min(nLength, aStr.getLength())); - auto pDValue = new RTFValue( - OStringToOUString(aDefaultText, rState.getCurrentEncoding())); - m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFTextInput_default, pDValue); - } - - m_bFormField = false; - } - } - break; - case Destination::CREATIONTIME: - if (m_xDocumentProperties.is()) - m_xDocumentProperties->setCreationDate(lcl_getDateTime(rState)); - break; - case Destination::REVISIONTIME: - if (m_xDocumentProperties.is()) - m_xDocumentProperties->setModificationDate(lcl_getDateTime(rState)); - break; - case Destination::PRINTTIME: - if (m_xDocumentProperties.is()) - m_xDocumentProperties->setPrintDate(lcl_getDateTime(rState)); - break; - case Destination::AUTHOR: - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - if (m_xDocumentProperties.is()) - m_xDocumentProperties->setAuthor( - m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); - break; - case Destination::KEYWORDS: - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - if (m_xDocumentProperties.is()) - { - OUStringBuffer* pCurrentDestinationText - = m_aStates.top().getCurrentDestinationText(); - m_xDocumentProperties->setKeywords( - comphelper::string::convertCommaSeparated(*pCurrentDestinationText)); - pCurrentDestinationText->setLength(0); - } - break; - case Destination::COMMENT: - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - if (m_xDocumentProperties.is()) - m_xDocumentProperties->setGenerator( - m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); - break; - case Destination::SUBJECT: - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - if (m_xDocumentProperties.is()) - m_xDocumentProperties->setSubject( - m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); - break; - case Destination::TITLE: - { - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - if (m_xDocumentProperties.is()) - m_xDocumentProperties->setTitle( - rState.getCurrentDestinationText()->makeStringAndClear()); - } - break; - - case Destination::DOCCOMM: - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - if (m_xDocumentProperties.is()) - m_xDocumentProperties->setDescription( - m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); - break; - case Destination::OPERATOR: - case Destination::COMPANY: - { - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - OUString aName = rState.getDestination() == Destination::OPERATOR ? OUString("Operator") - : OUString("Company"); - uno::Any aValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); - if (m_xDocumentProperties.is()) - { - uno::Reference<beans::XPropertyContainer> xUserDefinedProperties - = m_xDocumentProperties->getUserDefinedProperties(); - uno::Reference<beans::XPropertySet> xPropertySet(xUserDefinedProperties, - uno::UNO_QUERY); - uno::Reference<beans::XPropertySetInfo> xPropertySetInfo - = xPropertySet->getPropertySetInfo(); - if (xPropertySetInfo->hasPropertyByName(aName)) - xPropertySet->setPropertyValue(aName, aValue); - else - xUserDefinedProperties->addProperty(aName, beans::PropertyAttribute::REMOVABLE, - aValue); - } - } - break; - case Destination::OBJDATA: - { - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - - RTFError eError = handleEmbeddedObject(); - if (eError != RTFError::OK) - return eError; - } - break; - case Destination::OBJCLASS: - { - auto pValue - = new RTFValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); - m_aOLEAttributes.set(NS_ooxml::LN_CT_OLEObject_ProgID, pValue); - break; - } - case Destination::OBJECT: - { - if (!m_bObject) - { - // if the object is in a special container we will use the \result - // element instead of the \objdata - // (see RTFKeyword::OBJECT in RTFDocumentImpl::dispatchDestination) - break; - } - - RTFSprms aObjectSprms; - auto pOLEValue = new RTFValue(m_aOLEAttributes); - aObjectSprms.set(NS_ooxml::LN_OLEObject_OLEObject, pOLEValue); - - RTFSprms aObjAttributes; - RTFSprms aObjSprms; - auto pValue = new RTFValue(m_aObjectAttributes, aObjectSprms); - aObjSprms.set(NS_ooxml::LN_object, pValue); - writerfilter::Reference<Properties>::Pointer_t pProperties - = new RTFReferenceProperties(std::move(aObjAttributes), std::move(aObjSprms)); - uno::Reference<drawing::XShape> xShape; - RTFValue::Pointer_t pShape = m_aObjectAttributes.find(NS_ooxml::LN_shape); - OSL_ASSERT(pShape); - if (pShape) - pShape->getAny() >>= xShape; - if (xShape.is()) - { - Mapper().startShape(xShape); - Mapper().props(pProperties); - Mapper().endShape(); - } - m_aObjectAttributes.clear(); - m_aOLEAttributes.clear(); - m_bObject = false; - } - break; - case Destination::ANNOTATIONDATE: - { - OUStringBuffer* pCurrentDestinationText = m_aStates.top().getCurrentDestinationText(); - if (&m_aStates.top().getDestinationText() != pCurrentDestinationText) - break; // not for nested group - OUString aStr(OStringToOUString(DTTM22OString(o3tl::toInt32(*pCurrentDestinationText)), - rState.getCurrentEncoding())); - pCurrentDestinationText->setLength(0); - auto pValue = new RTFValue(aStr); - RTFSprms aAnnAttributes; - aAnnAttributes.set(NS_ooxml::LN_CT_TrackChange_date, pValue); - writerfilter::Reference<Properties>::Pointer_t pProperties - = new RTFReferenceProperties(std::move(aAnnAttributes)); - Mapper().props(pProperties); - } - break; - case Destination::ANNOTATIONAUTHOR: - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - m_aAuthor = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); - break; - case Destination::ATNID: - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - m_aAuthorInitials = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); - break; - case Destination::ANNOTATIONREFERENCESTART: - case Destination::ANNOTATIONREFERENCEEND: - { - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); - auto pValue = new RTFValue(aStr.toInt32()); - RTFSprms aAttributes; - if (rState.getDestination() == Destination::ANNOTATIONREFERENCESTART) - aAttributes.set(NS_ooxml::LN_EG_RangeMarkupElements_commentRangeStart, pValue); - else - aAttributes.set(NS_ooxml::LN_EG_RangeMarkupElements_commentRangeEnd, pValue); - if (!m_aStates.top().getCurrentBuffer()) - { - writerfilter::Reference<Properties>::Pointer_t pProperties - = new RTFReferenceProperties(std::move(aAttributes)); - Mapper().props(pProperties); - } - else - { - auto const pValue2 = new RTFValue(aAttributes, RTFSprms()); - bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue2, nullptr); - } - } - break; - case Destination::ANNOTATIONREFERENCE: - { - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); - RTFSprms aAnnAttributes; - aAnnAttributes.set(NS_ooxml::LN_CT_Markup_id, new RTFValue(aStr.toInt32())); - Mapper().props(new RTFReferenceProperties(std::move(aAnnAttributes))); - } - break; - case Destination::FALT: - { - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - OUString aStr(m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); - auto pValue = new RTFValue(aStr); - rState.getTableSprms().set(NS_ooxml::LN_CT_Font_altName, pValue); - } - break; - case Destination::DRAWINGOBJECT: - if (m_aStates.top().getDrawingObject().getShape().is()) - { - RTFDrawingObject& rDrawing = m_aStates.top().getDrawingObject(); - const uno::Reference<drawing::XShape>& xShape(rDrawing.getShape()); - const uno::Reference<beans::XPropertySet>& xPropertySet(rDrawing.getPropertySet()); - - uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY); - bool bTextFrame = xServiceInfo->supportsService("com.sun.star.text.TextFrame"); - - // The default is certainly not inline, but then what Word supports is just at-character. - xPropertySet->setPropertyValue("AnchorType", - uno::Any(text::TextContentAnchorType_AT_CHARACTER)); - - if (bTextFrame) - { - xPropertySet->setPropertyValue("HoriOrientPosition", - uno::Any(rDrawing.getLeft())); - xPropertySet->setPropertyValue("VertOrientPosition", - uno::Any(rDrawing.getTop())); - } - else - { - xShape->setPosition(awt::Point(rDrawing.getLeft(), rDrawing.getTop())); - } - xShape->setSize(awt::Size(rDrawing.getRight(), rDrawing.getBottom())); - - if (rDrawing.getHasLineColor()) - { - uno::Any aLineColor(sal_uInt32((rDrawing.getLineColorR() << 16) - + (rDrawing.getLineColorG() << 8) - + rDrawing.getLineColorB())); - uno::Any aLineWidth; - RTFSdrImport::resolveLineColorAndWidth(bTextFrame, xPropertySet, aLineColor, - aLineWidth); - } - if (rDrawing.getHasFillColor()) - xPropertySet->setPropertyValue( - "FillColor", uno::Any(sal_uInt32((rDrawing.getFillColorR() << 16) - + (rDrawing.getFillColorG() << 8) - + rDrawing.getFillColorB()))); - else if (!bTextFrame) - // If there is no fill, the Word default is 100% transparency. - xPropertySet->setPropertyValue("FillTransparence", uno::Any(sal_Int32(100))); - - RTFSdrImport::resolveFLine(xPropertySet, rDrawing.getFLine()); - - if (!m_aStates.top().getDrawingObject().getHadShapeText()) - { - Mapper().startShape(xShape); - } - Mapper().endShape(); - } - break; - case Destination::PICT: - // fdo#79319 ignore picture data if it's really a shape - if (!m_pSdrImport->isFakePict()) - { - resolvePict(true, m_pSdrImport->getCurrentShape()); - } - m_bNeedFinalPar = true; - break; - case Destination::SHAPE: - m_bNeedFinalPar = true; - m_bNeedCr = m_bNeedCrOrig; - // tdf#47036 insert paragraph break for graphic object inside text - // frame at start of document - TODO: the object may actually be - // anchored inside the text frame and this ends up putting the - // anchor in the body, but better than losing the shape... - if (rState.getFrame().hasProperties() && m_pSdrImport->isTextGraphicObject()) - { - // parBreak() modifies m_aStates.top() so we can't apply resetFrame() directly on aState - resetFrame(); - parBreak(); - // Save this state for later use, so we only reset frame status only for the first shape inside a frame. - rState = m_aStates.top(); - m_bNeedPap = true; - } - break; - case Destination::MOMATH: - { - m_aMathBuffer.appendClosingTag(M_TOKEN(oMath)); - - SvGlobalName aGlobalName(SO3_SM_CLASSID); - comphelper::EmbeddedObjectContainer aContainer; - OUString aName; - uno::Reference<embed::XEmbeddedObject> xObject - = aContainer.CreateEmbeddedObject(aGlobalName.GetByteSequence(), aName); - if (xObject) // rhbz#1766990 starmath might not be available - { - uno::Reference<util::XCloseable> xComponent(xObject->getComponent(), - uno::UNO_SET_THROW); - if (oox::FormulaImExportBase* pImport - = dynamic_cast<oox::FormulaImExportBase*>(xComponent.get())) - pImport->readFormulaOoxml(m_aMathBuffer); - - auto pValue = new RTFValue(xObject); - RTFSprms aMathAttributes; - aMathAttributes.set(NS_ooxml::LN_starmath, pValue); - writerfilter::Reference<Properties>::Pointer_t pProperties - = new RTFReferenceProperties(std::move(aMathAttributes)); - Mapper().props(pProperties); - } - - m_aMathBuffer = oox::formulaimport::XmlStreamBuilder(); - } - break; - case Destination::MR: - lcl_DestinationToMath(m_aStates.top().getCurrentDestinationText(), m_aMathBuffer, - m_bMathNor); - break; - case Destination::MF: - m_aMathBuffer.appendClosingTag(M_TOKEN(f)); - break; - case Destination::MFPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(fPr)); - break; - case Destination::MCTRLPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(ctrlPr)); - break; - case Destination::MNUM: - m_aMathBuffer.appendClosingTag(M_TOKEN(num)); - break; - case Destination::MDEN: - m_aMathBuffer.appendClosingTag(M_TOKEN(den)); - break; - case Destination::MACC: - m_aMathBuffer.appendClosingTag(M_TOKEN(acc)); - break; - case Destination::MACCPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(accPr)); - break; - case Destination::MCHR: - case Destination::MPOS: - case Destination::MVERTJC: - case Destination::MSTRIKEH: - case Destination::MDEGHIDE: - case Destination::MBEGCHR: - case Destination::MSEPCHR: - case Destination::MENDCHR: - case Destination::MSUBHIDE: - case Destination::MSUPHIDE: - case Destination::MTYPE: - case Destination::MGROW: - { - sal_Int32 nMathToken = 0; - switch (rState.getDestination()) - { - case Destination::MCHR: - nMathToken = M_TOKEN(chr); - break; - case Destination::MPOS: - nMathToken = M_TOKEN(pos); - break; - case Destination::MVERTJC: - nMathToken = M_TOKEN(vertJc); - break; - case Destination::MSTRIKEH: - nMathToken = M_TOKEN(strikeH); - break; - case Destination::MDEGHIDE: - nMathToken = M_TOKEN(degHide); - break; - case Destination::MBEGCHR: - nMathToken = M_TOKEN(begChr); - break; - case Destination::MSEPCHR: - nMathToken = M_TOKEN(sepChr); - break; - case Destination::MENDCHR: - nMathToken = M_TOKEN(endChr); - break; - case Destination::MSUBHIDE: - nMathToken = M_TOKEN(subHide); - break; - case Destination::MSUPHIDE: - nMathToken = M_TOKEN(supHide); - break; - case Destination::MTYPE: - nMathToken = M_TOKEN(type); - break; - case Destination::MGROW: - nMathToken = M_TOKEN(grow); - break; - default: - break; - } - - oox::formulaimport::XmlStream::AttributeList aAttribs; - aAttribs[M_TOKEN(val)] - = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); - m_aMathBuffer.appendOpeningTag(nMathToken, aAttribs); - m_aMathBuffer.appendClosingTag(nMathToken); - } - break; - case Destination::ME: - m_aMathBuffer.appendClosingTag(M_TOKEN(e)); - break; - case Destination::MBAR: - m_aMathBuffer.appendClosingTag(M_TOKEN(bar)); - break; - case Destination::MBARPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(barPr)); - break; - case Destination::MD: - m_aMathBuffer.appendClosingTag(M_TOKEN(d)); - break; - case Destination::MDPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(dPr)); - break; - case Destination::MFUNC: - m_aMathBuffer.appendClosingTag(M_TOKEN(func)); - break; - case Destination::MFUNCPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(funcPr)); - break; - case Destination::MFNAME: - m_aMathBuffer.appendClosingTag(M_TOKEN(fName)); - break; - case Destination::MLIMLOW: - m_aMathBuffer.appendClosingTag(M_TOKEN(limLow)); - break; - case Destination::MLIMLOWPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(limLowPr)); - break; - case Destination::MLIM: - m_aMathBuffer.appendClosingTag(M_TOKEN(lim)); - break; - case Destination::MM: - m_aMathBuffer.appendClosingTag(M_TOKEN(m)); - break; - case Destination::MMPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(mPr)); - break; - case Destination::MMR: - m_aMathBuffer.appendClosingTag(M_TOKEN(mr)); - break; - case Destination::MNARY: - m_aMathBuffer.appendClosingTag(M_TOKEN(nary)); - break; - case Destination::MNARYPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(naryPr)); - break; - case Destination::MSUB: - m_aMathBuffer.appendClosingTag(M_TOKEN(sub)); - break; - case Destination::MSUP: - m_aMathBuffer.appendClosingTag(M_TOKEN(sup)); - break; - case Destination::MLIMUPP: - m_aMathBuffer.appendClosingTag(M_TOKEN(limUpp)); - break; - case Destination::MLIMUPPPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(limUppPr)); - break; - case Destination::MGROUPCHR: - m_aMathBuffer.appendClosingTag(M_TOKEN(groupChr)); - break; - case Destination::MGROUPCHRPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(groupChrPr)); - break; - case Destination::MBORDERBOX: - m_aMathBuffer.appendClosingTag(M_TOKEN(borderBox)); - break; - case Destination::MBORDERBOXPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(borderBoxPr)); - break; - case Destination::MRAD: - m_aMathBuffer.appendClosingTag(M_TOKEN(rad)); - break; - case Destination::MRADPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(radPr)); - break; - case Destination::MDEG: - m_aMathBuffer.appendClosingTag(M_TOKEN(deg)); - break; - case Destination::MSSUB: - m_aMathBuffer.appendClosingTag(M_TOKEN(sSub)); - break; - case Destination::MSSUBPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(sSubPr)); - break; - case Destination::MSSUP: - m_aMathBuffer.appendClosingTag(M_TOKEN(sSup)); - break; - case Destination::MSSUPPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(sSupPr)); - break; - case Destination::MSSUBSUP: - m_aMathBuffer.appendClosingTag(M_TOKEN(sSubSup)); - break; - case Destination::MSSUBSUPPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(sSubSupPr)); - break; - case Destination::MSPRE: - m_aMathBuffer.appendClosingTag(M_TOKEN(sPre)); - break; - case Destination::MSPREPR: - m_aMathBuffer.appendClosingTag(M_TOKEN(sPrePr)); - break; - case Destination::MBOX: - m_aMathBuffer.appendClosingTag(M_TOKEN(box)); - break; - case Destination::MEQARR: - m_aMathBuffer.appendClosingTag(M_TOKEN(eqArr)); - break; - case Destination::SHAPEGROUP: - if (rState.getCreatedShapeGroup()) - m_pSdrImport->popParent(); - break; - case Destination::PROPNAME: - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - rState.setPropName(m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); - break; - case Destination::STATICVAL: - if (&m_aStates.top().getDestinationText() - != m_aStates.top().getCurrentDestinationText()) - break; // not for nested group - if (m_xDocumentProperties.is()) - { - // Find out what is the key, value type and value we want to set. - uno::Reference<beans::XPropertyContainer> xPropertyContainer - = m_xDocumentProperties->getUserDefinedProperties(); - const OUString& rKey = m_aStates.top().getPropName(); - OUString aStaticVal - = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); - uno::Any aAny; - if (m_aStates.top().getPropType() == cppu::UnoType<OUString>::get()) - aAny <<= aStaticVal; - else if (m_aStates.top().getPropType() == cppu::UnoType<sal_Int32>::get()) - aAny <<= aStaticVal.toInt32(); - else if (m_aStates.top().getPropType() == cppu::UnoType<bool>::get()) - aAny <<= aStaticVal.toBoolean(); - else if (m_aStates.top().getPropType() == cppu::UnoType<util::DateTime>::get()) - aAny <<= getDateTimeFromUserProp(aStaticVal); - else if (m_aStates.top().getPropType() == cppu::UnoType<double>::get()) - aAny <<= aStaticVal.toDouble(); - - xPropertyContainer->addProperty(rKey, beans::PropertyAttribute::REMOVABLE, aAny); - } - break; - case Destination::USERPROPS: - { - // These are the imported properties. - uno::Reference<document::XDocumentProperties> xDocumentProperties - = m_xDocumentProperties; - - // These are the real document properties. - uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier( - m_xDstDoc, uno::UNO_QUERY); - if (xDocumentPropertiesSupplier.is()) - m_xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties(); - - if (m_xDocumentProperties.is()) - { - if (!m_bIsNewDoc) - { - // Check classification. - if (!SfxClassificationHelper::ShowPasteInfo(SfxClassificationHelper::CheckPaste( - xDocumentProperties, m_xDocumentProperties))) - return RTFError::CLASSIFICATION; - } - - uno::Reference<beans::XPropertyContainer> xClipboardPropertyContainer - = xDocumentProperties->getUserDefinedProperties(); - uno::Reference<beans::XPropertyContainer> xDocumentPropertyContainer - = m_xDocumentProperties->getUserDefinedProperties(); - uno::Reference<beans::XPropertySet> xClipboardPropertySet( - xClipboardPropertyContainer, uno::UNO_QUERY); - uno::Reference<beans::XPropertySet> xDocumentPropertySet(xDocumentPropertyContainer, - uno::UNO_QUERY); - const uno::Sequence<beans::Property> aClipboardProperties - = xClipboardPropertySet->getPropertySetInfo()->getProperties(); - uno::Sequence<beans::Property> aDocumentProperties - = xDocumentPropertySet->getPropertySetInfo()->getProperties(); - - for (const beans::Property& rProperty : aClipboardProperties) - { - const OUString& rKey = rProperty.Name; - uno::Any aValue = xClipboardPropertySet->getPropertyValue(rKey); - - try - { - if (lcl_containsProperty(aDocumentProperties, rKey)) - { - // When pasting, don't update existing properties. - if (!m_bIsNewDoc) - xDocumentPropertySet->setPropertyValue(rKey, aValue); - } - else - xDocumentPropertyContainer->addProperty( - rKey, beans::PropertyAttribute::REMOVABLE, aValue); - } - catch (const uno::Exception&) - { - TOOLS_WARN_EXCEPTION("writerfilter.rtf", "failed to set property " << rKey); - } - } - } - } - break; - default: - break; - } - - return RTFError::OK; -} - -void RTFDocumentImpl::afterPopState(RTFParserState& rState) -{ - // list table - switch (rState.getDestination()) - { - case Destination::LISTENTRY: - { - auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms()); - m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_abstractNum, pValue, - RTFOverwrite::NO_APPEND); - m_aListTable[rState.getCurrentListIndex()] = pValue; - m_nListLevel = -1; - m_aInvalidListTableFirstIndents[rState.getCurrentListIndex()] - = m_aInvalidListLevelFirstIndents; - m_aInvalidListLevelFirstIndents.clear(); - } - break; - case Destination::PARAGRAPHNUMBERING: - { - RTFValue::Pointer_t pIdValue - = rState.getTableAttributes().find(NS_ooxml::LN_CT_AbstractNum_nsid); - if (pIdValue && !m_aStates.empty()) - { - // Abstract numbering - RTFSprms aLeveltextAttributes; - OUString aTextValue; - RTFValue::Pointer_t pTextBefore - = rState.getTableAttributes().find(NS_ooxml::LN_CT_LevelText_val); - if (pTextBefore) - aTextValue += pTextBefore->getString(); - aTextValue += "%1"; - RTFValue::Pointer_t pTextAfter - = rState.getTableAttributes().find(NS_ooxml::LN_CT_LevelSuffix_val); - if (pTextAfter) - aTextValue += pTextAfter->getString(); - auto pTextValue = new RTFValue(aTextValue); - aLeveltextAttributes.set(NS_ooxml::LN_CT_LevelText_val, pTextValue); - - RTFSprms aLevelAttributes; - RTFSprms aLevelSprms; - auto pIlvlValue = new RTFValue(0); - aLevelAttributes.set(NS_ooxml::LN_CT_Lvl_ilvl, pIlvlValue); - - RTFValue::Pointer_t pFmtValue - = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_numFmt); - if (pFmtValue) - aLevelSprms.set(NS_ooxml::LN_CT_Lvl_numFmt, pFmtValue); - - RTFValue::Pointer_t pStartatValue - = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_start); - if (pStartatValue) - aLevelSprms.set(NS_ooxml::LN_CT_Lvl_start, pStartatValue); - - auto pLeveltextValue = new RTFValue(aLeveltextAttributes); - aLevelSprms.set(NS_ooxml::LN_CT_Lvl_lvlText, pLeveltextValue); - RTFValue::Pointer_t pRunProps - = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_rPr); - if (pRunProps) - aLevelSprms.set(NS_ooxml::LN_CT_Lvl_rPr, pRunProps); - - RTFSprms aAbstractAttributes; - RTFSprms aAbstractSprms; - aAbstractAttributes.set(NS_ooxml::LN_CT_AbstractNum_abstractNumId, pIdValue); - auto pLevelValue = new RTFValue(aLevelAttributes, aLevelSprms); - aAbstractSprms.set(NS_ooxml::LN_CT_AbstractNum_lvl, pLevelValue, - RTFOverwrite::NO_APPEND); - - RTFSprms aListTableSprms; - auto pAbstractValue = new RTFValue(aAbstractAttributes, aAbstractSprms); - // It's important that Numbering_abstractNum and Numbering_num never overwrites previous values. - aListTableSprms.set(NS_ooxml::LN_CT_Numbering_abstractNum, pAbstractValue, - RTFOverwrite::NO_APPEND); - - // Numbering - RTFSprms aNumberingAttributes; - RTFSprms aNumberingSprms; - aNumberingAttributes.set(NS_ooxml::LN_CT_AbstractNum_nsid, pIdValue); - aNumberingSprms.set(NS_ooxml::LN_CT_Num_abstractNumId, pIdValue); - auto pNumberingValue = new RTFValue(aNumberingAttributes, aNumberingSprms); - aListTableSprms.set(NS_ooxml::LN_CT_Numbering_num, pNumberingValue, - RTFOverwrite::NO_APPEND); - - // Table - RTFSprms aListTableAttributes; - writerfilter::Reference<Properties>::Pointer_t pProp = new RTFReferenceProperties( - std::move(aListTableAttributes), std::move(aListTableSprms)); - - RTFReferenceTable::Entries_t aListTableEntries; - aListTableEntries.insert(std::make_pair(0, pProp)); - writerfilter::Reference<Table>::Pointer_t const pTable( - new RTFReferenceTable(std::move(aListTableEntries))); - Mapper().table(NS_ooxml::LN_NUMBERING, pTable); - - // Use it - putNestedSprm(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_numPr, - NS_ooxml::LN_CT_NumPr_ilvl, pIlvlValue, RTFOverwrite::YES_PREPEND); - putNestedSprm(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_numPr, - NS_ooxml::LN_CT_NumPr_numId, pIdValue, RTFOverwrite::YES_PREPEND); - } - } - break; - case Destination::PARAGRAPHNUMBERING_TEXTAFTER: - if (!m_aStates.empty()) - { - // FIXME: don't use pDestinationText, points to popped state - auto pValue = new RTFValue(rState.getDestinationText().makeStringAndClear(), true); - m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_LevelSuffix_val, pValue); - } - break; - case Destination::PARAGRAPHNUMBERING_TEXTBEFORE: - if (!m_aStates.empty()) - { - // FIXME: don't use pDestinationText, points to popped state - auto pValue = new RTFValue(rState.getDestinationText().makeStringAndClear(), true); - m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_LevelText_val, pValue); - } - break; - case Destination::LISTNAME: - break; - case Destination::LISTLEVEL: - if (!m_aStates.empty()) - { - auto pInnerValue = new RTFValue(m_aStates.top().getListLevelNum()++); - rState.getTableAttributes().set(NS_ooxml::LN_CT_Lvl_ilvl, pInnerValue); - - auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms()); - if (m_aStates.top().getDestination() != Destination::LFOLEVEL) - m_aStates.top().getListLevelEntries().set(NS_ooxml::LN_CT_AbstractNum_lvl, - pValue, RTFOverwrite::NO_APPEND); - else - m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_NumLvl_lvl, pValue); - } - break; - case Destination::LFOLEVEL: - if (!m_aStates.empty()) - { - auto pInnerValue = new RTFValue(m_aStates.top().getListLevelNum()++); - rState.getTableAttributes().set(NS_ooxml::LN_CT_NumLvl_ilvl, pInnerValue); - - auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms()); - m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Num_lvlOverride, pValue, - RTFOverwrite::NO_APPEND); - } - break; - // list override table - case Destination::LISTOVERRIDEENTRY: - if (!m_aStates.empty()) - { - if (m_aStates.top().getDestination() == Destination::LISTOVERRIDEENTRY) - { - // copy properties upwards so upper popState() inserts it - m_aStates.top().getTableAttributes() = rState.getTableAttributes(); - m_aStates.top().getTableSprms() = rState.getTableSprms(); - } - else - { - auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms()); - m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_num, pValue, - RTFOverwrite::NO_APPEND); - m_aListOverrideTable[rState.getCurrentListOverrideIndex()] - = rState.getCurrentListIndex(); - } - } - break; - case Destination::LEVELTEXT: - if (!m_aStates.empty()) - { - auto pValue = new RTFValue(rState.getTableAttributes()); - m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Lvl_lvlText, pValue); - } - break; - case Destination::LEVELNUMBERS: - if (!m_aStates.empty()) - { - m_aStates.top().getTableSprms() = rState.getTableSprms(); - if (m_aStates.top().getDestination() == Destination::LEVELNUMBERS - || m_aStates.top().getDestination() == Destination::LISTLEVEL) - // Parent state is level number or list level, current state is - // level numbers: mark parent as invalid as well if necessary. - m_aStates.top().setLevelNumbersValid(rState.getLevelNumbersValid()); - } - break; - case Destination::FIELDINSTRUCTION: - if (!m_aStates.empty()) - m_aStates.top().setFieldStatus(RTFFieldStatus::INSTRUCTION); - break; - case Destination::FIELDRESULT: - if (!m_aStates.empty()) - m_aStates.top().setFieldStatus(RTFFieldStatus::RESULT); - break; - case Destination::FIELD: - if (rState.getFieldStatus() == RTFFieldStatus::INSTRUCTION) - singleChar(cFieldEnd); - break; - case Destination::DOCVAR: - if (!m_aStates.empty()) - { - OUString docvar(rState.getDocVar()); - if (m_aStates.top().getDocVarName().isEmpty()) - { - m_aStates.top().setDocVarName(docvar); - } - else - { - uno::Reference<beans::XPropertySet> xMaster( - m_xModelFactory->createInstance("com.sun.star.text.FieldMaster.User"), - uno::UNO_QUERY_THROW); - xMaster->setPropertyValue("Name", uno::Any(m_aStates.top().getDocVarName())); - uno::Reference<text::XDependentTextField> xField( - m_xModelFactory->createInstance("com.sun.star.text.TextField.User"), - uno::UNO_QUERY); - xField->attachTextFieldMaster(xMaster); - xField->getTextFieldMaster()->setPropertyValue("Content", uno::Any(docvar)); - - m_aStates.top().clearDocVarName(); - } - } - break; - case Destination::SHAPEPROPERTYVALUEPICT: - if (!m_aStates.empty()) - { - m_aStates.top().getPicture() = rState.getPicture(); - // both \sp and \sv are destinations, copy the text up-ward for later - m_aStates.top().getDestinationText() = rState.getDestinationText(); - } - break; - case Destination::FALT: - if (!m_aStates.empty()) - m_aStates.top().getTableSprms() = rState.getTableSprms(); - break; - case Destination::SHAPEPROPERTYNAME: - case Destination::SHAPEPROPERTYVALUE: - case Destination::SHAPEPROPERTY: - if (!m_aStates.empty()) - { - m_aStates.top().getShape() = rState.getShape(); - m_aStates.top().getPicture() = rState.getPicture(); - m_aStates.top().getCharacterAttributes() = rState.getCharacterAttributes(); - } - break; - case Destination::SHAPEINSTRUCTION: - if (!m_aStates.empty() - && m_aStates.top().getDestination() == Destination::SHAPEINSTRUCTION) - { - // Shape instruction inside other shape instruction: just copy new shape settings: - // it will be resolved on end of topmost shape instruction destination - m_aStates.top().getShape() = rState.getShape(); - m_aStates.top().getPicture() = rState.getPicture(); - m_aStates.top().getCharacterSprms() = rState.getCharacterSprms(); - m_aStates.top().getCharacterAttributes() = rState.getCharacterAttributes(); - } - break; - case Destination::FLYMAINCONTENT: - case Destination::SHPPICT: - case Destination::SHAPE: - if (!m_aStates.empty()) - { - m_aStates.top().getFrame() = rState.getFrame(); - if (rState.getDestination() == Destination::SHPPICT - && m_aStates.top().getDestination() == Destination::LISTPICTURE) - { - RTFSprms aAttributes; - aAttributes.set(NS_ooxml::LN_CT_NumPicBullet_numPicBulletId, - new RTFValue(m_nListPictureId++)); - RTFSprms aSprms; - // Dummy value, real picture is already sent to dmapper. - aSprms.set(NS_ooxml::LN_CT_NumPicBullet_pict, new RTFValue(0)); - auto pValue = new RTFValue(aAttributes, aSprms); - m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_numPicBullet, pValue, - RTFOverwrite::NO_APPEND); - } - } - break; - case Destination::SHAPETEXT: - if (!m_aStates.empty()) - { - // If we're leaving the shapetext group (it may have nested ones) and this is a shape, not an old drawingobject. - if (m_aStates.top().getDestination() != Destination::SHAPETEXT - && !m_aStates.top().getDrawingObject().getHadShapeText()) - { - m_aStates.top().setHadShapeText(true); - if (!m_aStates.top().getCurrentBuffer()) - m_pSdrImport->close(); - else - m_aStates.top().getCurrentBuffer()->emplace_back(BUFFER_ENDSHAPE, nullptr, - nullptr); - } - - // It's allowed to declare these inside the shape text, and they - // are expected to have an effect for the whole shape. - if (rState.getDrawingObject().getLeft()) - m_aStates.top().getDrawingObject().setLeft(rState.getDrawingObject().getLeft()); - if (rState.getDrawingObject().getTop()) - m_aStates.top().getDrawingObject().setTop(rState.getDrawingObject().getTop()); - if (rState.getDrawingObject().getRight()) - m_aStates.top().getDrawingObject().setRight( - rState.getDrawingObject().getRight()); - if (rState.getDrawingObject().getBottom()) - m_aStates.top().getDrawingObject().setBottom( - rState.getDrawingObject().getBottom()); - } - break; - case Destination::PROPNAME: - if (m_aStates.top().getDestination() == Destination::USERPROPS) - m_aStates.top().setPropName(rState.getPropName()); - break; - default: - { - if (!m_aStates.empty() && m_aStates.top().getDestination() == Destination::PICT) - m_aStates.top().getPicture() = rState.getPicture(); - } - break; - } -} - -RTFError RTFDocumentImpl::popState() -{ - //SAL_INFO("writerfilter", __func__ << " before pop: m_pTokenizer->getGroup() " << m_pTokenizer->getGroup() << - // ", dest state: " << m_aStates.top().eDestination); - - checkUnicode(/*bUnicode =*/true, /*bHex =*/true); - RTFParserState aState(m_aStates.top()); - m_bWasInFrame = aState.getFrame().hasProperties(); - - // dmapper expects some content in header/footer, so if there would be nothing, add an empty paragraph. - if (m_pTokenizer->getGroup() == 1 && m_bFirstRun) - { - switch (m_nStreamType) - { - case NS_ooxml::LN_headerl: - case NS_ooxml::LN_headerr: - case NS_ooxml::LN_headerf: - case NS_ooxml::LN_footerl: - case NS_ooxml::LN_footerr: - case NS_ooxml::LN_footerf: - dispatchSymbol(RTFKeyword::PAR); - break; - } - } - - RTFError eError = beforePopState(aState); - if (eError != RTFError::OK) - return eError; - - // See if we need to end a track change - if (aState.getStartedTrackchange()) - { - RTFSprms aTCSprms; - auto pValue = new RTFValue(0); - aTCSprms.set(NS_ooxml::LN_endtrackchange, pValue); - if (!m_aStates.top().getCurrentBuffer()) - Mapper().props(new RTFReferenceProperties(RTFSprms(), std::move(aTCSprms))); - else - bufferProperties(*m_aStates.top().getCurrentBuffer(), - new RTFValue(RTFSprms(), aTCSprms), nullptr); - } - - // This is the end of the doc, see if we need to close the last section. - if (m_pTokenizer->getGroup() == 1 && !m_bFirstRun) - { - // \par means an empty paragraph at the end of footnotes/endnotes, but - // not in case of other substreams, like headers. - if (m_bNeedCr && m_nStreamType != NS_ooxml::LN_footnote - && m_nStreamType != NS_ooxml::LN_endnote) - { - if (!m_bIsNewDoc) - { - // Make sure all the paragraph settings are set, but do not add next paragraph - Mapper().markLastParagraph(); - } - dispatchSymbol(RTFKeyword::PAR); - } - if (m_bNeedSect) // may be set by dispatchSymbol above! - sectBreak(true); - else if (!m_pSuperstream) - { - Mapper().markLastSectionGroup(); // ensure it's set for \par below - } - if (m_bNeedPar && !m_pSuperstream) - { - assert(!m_bNeedSect); - dispatchSymbol(RTFKeyword::PAR); - m_bNeedSect = false; // reset - m_bNeedPar was set for \sect at - // end of doc so don't need another one - } - } - - m_aStates.pop(); - - m_pTokenizer->popGroup(); - - afterPopState(aState); - - if (aState.getCurrentBuffer() == &m_aSuperBuffer) - { - OSL_ASSERT(!m_aStates.empty() && m_aStates.top().getCurrentBuffer() == nullptr); - - if (!m_aSuperBuffer.empty()) - replayBuffer(m_aSuperBuffer, nullptr, nullptr); - } - - if (!m_aStates.empty() && m_aStates.top().getTableRowWidthAfter() > 0 - && aState.getTableRowWidthAfter() == 0) - // An RTFKeyword::ROW in the inner group already parsed nTableRowWidthAfter, - // don't do it again in the outer state later. - m_aStates.top().setTableRowWidthAfter(0); - - if (m_nResetBreakOnSectBreak != RTFKeyword::invalid && !m_aStates.empty()) - { - // Section break type created for \page still has an effect in the - // outer state as well. - RTFValue::Pointer_t pType - = aState.getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_type); - if (pType) - m_aStates.top().getSectionSprms().set(NS_ooxml::LN_EG_SectPrContents_type, pType); - } - - return RTFError::OK; -} - -RTFError RTFDocumentImpl::handleEmbeddedObject() -{ - OUStringBuffer* pCurrentDestinationText = m_aStates.top().getCurrentDestinationText(); - OString aStr = OUStringToOString(*pCurrentDestinationText, RTL_TEXTENCODING_ASCII_US); - pCurrentDestinationText->setLength(0); - std::unique_ptr<SvStream> pStream(new SvMemoryStream()); - if (!msfilter::rtfutil::ExtractOLE2FromObjdata(aStr, *pStream)) - return RTFError::HEX_INVALID; - - uno::Reference<io::XInputStream> xInputStream( - new utl::OSeekableInputStreamWrapper(pStream.release(), /*_bOwner=*/true)); - m_aOLEAttributes.set(NS_ooxml::LN_inputstream, new RTFValue(xInputStream)); - - return RTFError::OK; -} - -bool RTFDocumentImpl::isInBackground() { return m_aStates.top().getInBackground(); } - -RTFInternalState RTFDocumentImpl::getInternalState() { return m_aStates.top().getInternalState(); } - -void RTFDocumentImpl::setInternalState(RTFInternalState nInternalState) -{ - m_aStates.top().setInternalState(nInternalState); -} - -Destination RTFDocumentImpl::getDestination() { return m_aStates.top().getDestination(); } - -void RTFDocumentImpl::setDestination(Destination eDestination) -{ - m_aStates.top().setDestination(eDestination); -} - -// this is a questionably named method that is used only in a very special -// situation where it looks like the "current" buffer is needed? -void RTFDocumentImpl::setDestinationText(std::u16string_view rString) -{ - m_aStates.top().getDestinationText().setLength(0); - m_aStates.top().getDestinationText().append(rString); -} - -bool RTFDocumentImpl::getSkipUnknown() { return m_bSkipUnknown; } - -void RTFDocumentImpl::setSkipUnknown(bool bSkipUnknown) { m_bSkipUnknown = bSkipUnknown; } - -static auto FilterControlChars(Destination const destination, OUString const& rString) -> OUString -{ - if (destination == Destination::LEVELNUMBERS || destination == Destination::LEVELTEXT) - { // control characters are magic here! - return rString; - } - OUStringBuffer buf(rString.getLength()); - for (sal_Int32 i = 0; i < rString.getLength(); ++i) - { - sal_Unicode const ch(rString[i]); - if (!linguistic::IsControlChar(ch) || ch == '\r' || ch == '\n' || ch == '\t') - { - buf.append(ch); - } - else - { - SAL_INFO("writerfilter.rtf", "filtering control character"); - } - } - return buf.makeStringAndClear(); -} - -void RTFDocumentImpl::checkUnicode(bool bUnicode, bool bHex) -{ - if (bUnicode && !m_aUnicodeBuffer.isEmpty()) - { - OUString aString = m_aUnicodeBuffer.toString(); - m_aUnicodeBuffer.setLength(0); - aString = FilterControlChars(m_aStates.top().getDestination(), aString); - text(aString); - } - if (bHex && !m_aHexBuffer.isEmpty()) - { - rtl_TextEncoding nEncoding = m_aStates.top().getCurrentEncoding(); - if (nEncoding == RTL_TEXTENCODING_SYMBOL - && (m_aStates.top().getDestination() == Destination::FONTENTRY - || (m_aStates.size() > 1 - && m_aStates[m_aStates.size() - 2].getDestination() - == Destination::FIELDINSTRUCTION))) - nEncoding = RTL_TEXTENCODING_MS_1252; - OUString aString = OStringToOUString(m_aHexBuffer, nEncoding); - m_aHexBuffer.setLength(0); - aString = FilterControlChars(m_aStates.top().getDestination(), aString); - text(aString); - } -} - -RTFParserState::RTFParserState(RTFDocumentImpl* pDocumentImpl) - : m_pDocumentImpl(pDocumentImpl) - , m_nInternalState(RTFInternalState::NORMAL) - , m_eDestination(Destination::NORMAL) - , m_eFieldStatus(RTFFieldStatus::NONE) - , m_bFieldLocked(false) - , m_nBorderState(RTFBorderState::NONE) - , m_nCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(0)) - , m_nUc(1) - , m_nCharsToSkip(0) - , m_nBinaryToRead(0) - , m_nListLevelNum(0) - , m_bLevelNumbersValid(true) - , m_aFrame(this) - , m_eRunType(RunType::NONE) - , m_nYear(0) - , m_nMonth(0) - , m_nDay(0) - , m_nHour(0) - , m_nMinute(0) - , m_pCurrentDestinationText(nullptr) - , m_nCurrentStyleIndex(0) - , m_nCurrentCharacterStyleIndex(-1) - , m_pCurrentBuffer(nullptr) - , m_bInListpicture(false) - , m_bInBackground(false) - , m_bHadShapeText(false) - , m_bInShapeGroup(false) - , m_bInShape(false) - , m_bCreatedShapeGroup(false) - , m_bStartedTrackchange(false) - , m_nTableRowWidthAfter(0) -{ -} - -void RTFDocumentImpl::resetFrame() { m_aStates.top().getFrame() = RTFFrame(&m_aStates.top()); } - -void RTFDocumentImpl::bufferProperties(RTFBuffer_t& rBuffer, const RTFValue::Pointer_t& pValue, - const tools::SvRef<TableRowBuffer>& pTableProperties, - Id const nStyleType) -{ - rBuffer.emplace_back(BUFFER_SETSTYLE, new RTFValue(m_aStates.top().getCurrentStyleIndex()), - nullptr); - assert(nStyleType == 0 || nStyleType == NS_ooxml::LN_Value_ST_StyleType_character); - rBuffer.emplace_back(nStyleType == NS_ooxml::LN_Value_ST_StyleType_character ? BUFFER_PROPS_CHAR - : BUFFER_PROPS, - pValue, pTableProperties); -} - -RTFShape::RTFShape() = default; - -RTFDrawingObject::RTFDrawingObject() = default; - -RTFFrame::RTFFrame(RTFParserState* pParserState) - : m_pDocumentImpl(pParserState->getDocumentImpl()) - , m_nX(0) - , m_nY(0) - , m_nW(0) - , m_nH(0) - , m_nHoriPadding(0) - , m_nVertPadding(0) - , m_nHoriAlign(0) - , m_nHoriAnchor(0) - , m_nVertAlign(0) - , m_nVertAnchor(0) - , m_nHRule(NS_ooxml::LN_Value_doc_ST_HeightRule_auto) -{ -} - -void RTFFrame::setSprm(Id nId, Id nValue) -{ - switch (nId) - { - case NS_ooxml::LN_CT_FramePr_w: - m_nW = nValue; - break; - case NS_ooxml::LN_CT_FramePr_h: - m_nH = nValue; - break; - case NS_ooxml::LN_CT_FramePr_x: - m_nX = nValue; - break; - case NS_ooxml::LN_CT_FramePr_y: - m_nY = nValue; - break; - case NS_ooxml::LN_CT_FramePr_hSpace: - m_nHoriPadding = nValue; - break; - case NS_ooxml::LN_CT_FramePr_vSpace: - m_nVertPadding = nValue; - break; - case NS_ooxml::LN_CT_FramePr_xAlign: - m_nHoriAlign = nValue; - break; - case NS_ooxml::LN_CT_FramePr_hAnchor: - m_nHoriAnchor = nValue; - break; - case NS_ooxml::LN_CT_FramePr_yAlign: - m_nVertAlign = nValue; - break; - case NS_ooxml::LN_CT_FramePr_vAnchor: - m_nVertAnchor = nValue; - break; - case NS_ooxml::LN_CT_FramePr_wrap: - m_oWrap = nValue; - break; - default: - break; - } - - if (m_pDocumentImpl->getFirstRun() && !m_pDocumentImpl->isStyleSheetImport() && hasProperties()) - { - m_pDocumentImpl->checkFirstRun(); - m_pDocumentImpl->setNeedPar(false); - } -} - -RTFSprms RTFFrame::getSprms() -{ - RTFSprms sprms; - - static const Id pNames[] - = { NS_ooxml::LN_CT_FramePr_x, NS_ooxml::LN_CT_FramePr_y, - NS_ooxml::LN_CT_FramePr_hRule, // Make sure nHRule is processed before nH - NS_ooxml::LN_CT_FramePr_h, NS_ooxml::LN_CT_FramePr_w, - NS_ooxml::LN_CT_FramePr_hSpace, NS_ooxml::LN_CT_FramePr_vSpace, - NS_ooxml::LN_CT_FramePr_hAnchor, NS_ooxml::LN_CT_FramePr_vAnchor, - NS_ooxml::LN_CT_FramePr_xAlign, NS_ooxml::LN_CT_FramePr_yAlign, - NS_ooxml::LN_CT_FramePr_wrap, NS_ooxml::LN_CT_FramePr_dropCap, - NS_ooxml::LN_CT_FramePr_lines }; - - for (Id nId : pNames) - { - RTFValue::Pointer_t pValue; - - switch (nId) - { - case NS_ooxml::LN_CT_FramePr_x: - if (m_nX != 0) - pValue = new RTFValue(m_nX); - break; - case NS_ooxml::LN_CT_FramePr_y: - if (m_nY != 0) - pValue = new RTFValue(m_nY); - break; - case NS_ooxml::LN_CT_FramePr_h: - if (m_nH != 0) - { - if (m_nHRule == NS_ooxml::LN_Value_doc_ST_HeightRule_exact) - pValue = new RTFValue(-m_nH); // The negative value just sets nHRule - else - pValue = new RTFValue(m_nH); - } - break; - case NS_ooxml::LN_CT_FramePr_w: - if (m_nW != 0) - pValue = new RTFValue(m_nW); - break; - case NS_ooxml::LN_CT_FramePr_hSpace: - if (m_nHoriPadding != 0) - pValue = new RTFValue(m_nHoriPadding); - break; - case NS_ooxml::LN_CT_FramePr_vSpace: - if (m_nVertPadding != 0) - pValue = new RTFValue(m_nVertPadding); - break; - case NS_ooxml::LN_CT_FramePr_hAnchor: - { - if (m_nHoriAnchor == 0) - m_nHoriAnchor = NS_ooxml::LN_Value_doc_ST_HAnchor_text; - pValue = new RTFValue(m_nHoriAnchor); - } - break; - case NS_ooxml::LN_CT_FramePr_vAnchor: - { - if (m_nVertAnchor == 0) - m_nVertAnchor = NS_ooxml::LN_Value_doc_ST_VAnchor_margin; - pValue = new RTFValue(m_nVertAnchor); - } - break; - case NS_ooxml::LN_CT_FramePr_xAlign: - pValue = new RTFValue(m_nHoriAlign); - break; - case NS_ooxml::LN_CT_FramePr_yAlign: - pValue = new RTFValue(m_nVertAlign); - break; - case NS_ooxml::LN_CT_FramePr_hRule: - { - if (m_nH < 0) - m_nHRule = NS_ooxml::LN_Value_doc_ST_HeightRule_exact; - else if (m_nH > 0) - m_nHRule = NS_ooxml::LN_Value_doc_ST_HeightRule_atLeast; - pValue = new RTFValue(m_nHRule); - } - break; - case NS_ooxml::LN_CT_FramePr_wrap: - if (m_oWrap) - pValue = new RTFValue(*m_oWrap); - break; - default: - break; - } - - if (pValue) - sprms.set(nId, pValue); - } - - RTFSprms frameprSprms; - frameprSprms.set(NS_ooxml::LN_CT_PPrBase_framePr, new RTFValue(sprms)); - return frameprSprms; -} - -bool RTFFrame::hasProperties() const -{ - // tdf#153178 \dxfrtext \dfrmtxtx \dfrmtxty \wrapdefault do *not* create frame - return m_nX != 0 || m_nY != 0 || m_nW != 0 || m_nH != 0 - || (m_nHoriAlign && m_nHoriAlign != NS_ooxml::LN_Value_doc_ST_XAlign_left) - || (m_nHoriAnchor && m_nHoriAnchor != NS_ooxml::LN_Value_doc_ST_HAnchor_text) - || (m_nVertAlign && m_nVertAlign != NS_ooxml::LN_Value_doc_ST_YAlign_inline) - || (m_nVertAnchor && m_nVertAnchor != NS_ooxml::LN_Value_doc_ST_VAnchor_margin) - || (m_oWrap && *m_oWrap != NS_ooxml::LN_Value_doc_ST_Wrap_auto); -} - -} // namespace writerfilter - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |