1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include "rtfdocumentimpl.hxx"
14 #include <string_view>
16 #include <com/sun/star/embed/XEmbeddedObject.hpp>
17 #include <com/sun/star/beans/PropertyAttribute.hpp>
18 #include <com/sun/star/io/WrongFormatException.hpp>
19 #include <com/sun/star/lang/XServiceInfo.hpp>
20 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
21 #include <com/sun/star/text/TextContentAnchorType.hpp>
22 #include <com/sun/star/text/XDependentTextField.hpp>
23 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
25 #include <i18nlangtag/languagetag.hxx>
26 #include <unotools/ucbstreamhelper.hxx>
27 #include <unotools/streamwrap.hxx>
28 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
29 #include <filter/msfilter/util.hxx>
30 #include <filter/msfilter/rtfutil.hxx>
31 #include <comphelper/string.hxx>
32 #include <comphelper/diagnose_ex.hxx>
33 #include <tools/globname.hxx>
34 #include <tools/datetimeutils.hxx>
35 #include <comphelper/classids.hxx>
36 #include <comphelper/embeddedobjectcontainer.hxx>
37 #include <svl/lngmisc.hxx>
38 #include <sfx2/classificationhelper.hxx>
39 #include <oox/mathml/imexport.hxx>
40 #include <ooxml/resourceids.hxx>
41 #include <oox/token/namespaces.hxx>
42 #include <oox/drawingml/drawingmltypes.hxx>
43 #include <rtl/uri.hxx>
44 #include <rtl/tencinfo.h>
45 #include <sal/log.hxx>
46 #include <osl/diagnose.h>
47 #include <oox/helper/graphichelper.hxx>
48 #include <vcl/wmfexternal.hxx>
49 #include <vcl/graph.hxx>
50 #include <vcl/settings.hxx>
51 #include <vcl/svapp.hxx>
52 #include "rtfsdrimport.hxx"
53 #include "rtfreferenceproperties.hxx"
54 #include "rtfskipdestination.hxx"
55 #include "rtftokenizer.hxx"
56 #include "rtflookahead.hxx"
57 #include "rtfcharsets.hxx"
59 using namespace com::sun::star
;
63 /// Returns an util::DateTime from a 'YYYY. MM. DD.' string.
64 util::DateTime
getDateTimeFromUserProp(std::u16string_view rString
)
67 size_t nLen
= rString
.size();
70 aRet
.Year
= o3tl::toInt32(rString
.substr(0, 4));
72 if (nLen
>= 8 && o3tl::starts_with(rString
.substr(4), u
". "))
74 aRet
.Month
= o3tl::toInt32(rString
.substr(6, 2));
76 if (nLen
>= 12 && o3tl::starts_with(rString
.substr(8), u
". "))
77 aRet
.Day
= o3tl::toInt32(rString
.substr(10, 2));
82 } // anonymous namespace
84 namespace writerfilter::rtftok
86 Id
getParagraphBorder(sal_uInt32 nIndex
)
88 static const Id aBorderIds
[]
89 = { NS_ooxml::LN_CT_PBdr_top
, NS_ooxml::LN_CT_PBdr_left
, NS_ooxml::LN_CT_PBdr_bottom
,
90 NS_ooxml::LN_CT_PBdr_right
, NS_ooxml::LN_CT_PBdr_between
};
92 return aBorderIds
[nIndex
];
95 void putNestedAttribute(RTFSprms
& rSprms
, Id nParent
, Id nId
, const RTFValue::Pointer_t
& pValue
,
96 RTFOverwrite eOverwrite
, bool bAttribute
)
98 RTFValue::Pointer_t pParent
= rSprms
.find(nParent
, /*bFirst=*/true, /*bForWrite=*/true);
101 RTFSprms aAttributes
;
102 if (nParent
== NS_ooxml::LN_CT_TcPrBase_shd
)
104 // RTF default is 'auto', see writerfilter::dmapper::CellColorHandler
105 aAttributes
.set(NS_ooxml::LN_CT_Shd_color
, new RTFValue(sal_uInt32(COL_AUTO
)));
106 aAttributes
.set(NS_ooxml::LN_CT_Shd_fill
, new RTFValue(sal_uInt32(COL_AUTO
)));
108 auto pParentValue
= new RTFValue(aAttributes
);
109 rSprms
.set(nParent
, pParentValue
, eOverwrite
);
110 pParent
= pParentValue
;
112 RTFSprms
& rAttributes
= (bAttribute
? pParent
->getAttributes() : pParent
->getSprms());
113 rAttributes
.set(nId
, pValue
, eOverwrite
);
116 void putNestedSprm(RTFSprms
& rSprms
, Id nParent
, Id nId
, const RTFValue::Pointer_t
& pValue
,
117 RTFOverwrite eOverwrite
)
119 putNestedAttribute(rSprms
, nParent
, nId
, pValue
, eOverwrite
, false);
122 RTFValue::Pointer_t
getNestedAttribute(RTFSprms
& rSprms
, Id nParent
, Id nId
)
124 RTFValue::Pointer_t pParent
= rSprms
.find(nParent
);
126 return RTFValue::Pointer_t();
127 RTFSprms
& rAttributes
= pParent
->getAttributes();
128 return rAttributes
.find(nId
);
131 RTFValue::Pointer_t
getNestedSprm(RTFSprms
& rSprms
, Id nParent
, Id nId
)
133 RTFValue::Pointer_t pParent
= rSprms
.find(nParent
);
135 return RTFValue::Pointer_t();
136 RTFSprms
& rInner
= pParent
->getSprms();
137 return rInner
.find(nId
);
140 bool eraseNestedAttribute(RTFSprms
& rSprms
, Id nParent
, Id nId
)
142 RTFValue::Pointer_t pParent
= rSprms
.find(nParent
);
144 // It doesn't even have a parent, we're done.
146 RTFSprms
& rAttributes
= pParent
->getAttributes();
147 return rAttributes
.erase(nId
);
150 RTFSprms
& getLastAttributes(RTFSprms
& rSprms
, Id nId
)
152 RTFValue::Pointer_t p
= rSprms
.find(nId
);
153 if (p
&& !p
->getSprms().empty())
154 return p
->getSprms().back().second
->getAttributes();
156 SAL_WARN("writerfilter.rtf", "trying to set property when no type is defined");
160 void putBorderProperty(RTFStack
& aStates
, Id nId
, const RTFValue::Pointer_t
& pValue
)
162 RTFSprms
* pAttributes
= nullptr;
163 if (aStates
.top().getBorderState() == RTFBorderState::PARAGRAPH_BOX
)
164 for (int i
= 0; i
< 4; i
++)
166 RTFValue::Pointer_t p
= aStates
.top().getParagraphSprms().find(getParagraphBorder(i
));
169 RTFSprms
& rAttributes
= p
->getAttributes();
170 rAttributes
.set(nId
, pValue
);
173 else if (aStates
.top().getBorderState() == RTFBorderState::CHARACTER
)
175 RTFValue::Pointer_t pPointer
176 = aStates
.top().getCharacterSprms().find(NS_ooxml::LN_EG_RPrBase_bdr
);
179 RTFSprms
& rAttributes
= pPointer
->getAttributes();
180 rAttributes
.set(nId
, pValue
);
183 // Attributes of the last border type
184 else if (aStates
.top().getBorderState() == RTFBorderState::PARAGRAPH
)
186 = &getLastAttributes(aStates
.top().getParagraphSprms(), NS_ooxml::LN_CT_PrBase_pBdr
);
187 else if (aStates
.top().getBorderState() == RTFBorderState::CELL
)
188 pAttributes
= &getLastAttributes(aStates
.top().getTableCellSprms(),
189 NS_ooxml::LN_CT_TcPrBase_tcBorders
);
190 else if (aStates
.top().getBorderState() == RTFBorderState::PAGE
)
191 pAttributes
= &getLastAttributes(aStates
.top().getSectionSprms(),
192 NS_ooxml::LN_EG_SectPrContents_pgBorders
);
193 else if (aStates
.top().getBorderState() == RTFBorderState::NONE
)
195 // this is invalid, but Word apparently clears or overrides all paragraph borders now
196 for (int i
= 0; i
< 4; ++i
)
198 auto const nBorder
= getParagraphBorder(i
);
199 RTFSprms aAttributes
;
201 aAttributes
.set(NS_ooxml::LN_CT_Border_val
,
202 new RTFValue(NS_ooxml::LN_Value_ST_Border_none
));
203 putNestedSprm(aStates
.top().getParagraphSprms(), NS_ooxml::LN_CT_PrBase_pBdr
, nBorder
,
204 new RTFValue(aAttributes
, aSprms
), RTFOverwrite::YES
);
209 pAttributes
->set(nId
, pValue
);
212 OString
DTTM22OString(tools::Long nDTTM
)
214 return DateTimeToOString(msfilter::util::DTTM2DateTime(nDTTM
));
217 static RTFSprms
lcl_getBookmarkProperties(int nPos
, const OUString
& rString
)
219 RTFSprms aAttributes
;
220 auto pPos
= new RTFValue(nPos
);
221 if (!rString
.isEmpty())
223 // If present, this should be sent first.
224 auto pString
= new RTFValue(rString
);
225 aAttributes
.set(NS_ooxml::LN_CT_Bookmark_name
, pString
);
227 aAttributes
.set(NS_ooxml::LN_CT_MarkupRangeBookmark_id
, pPos
);
231 const char* keywordToString(RTFKeyword nKeyword
)
233 for (int i
= 0; i
< nRTFControlWords
; i
++)
235 if (nKeyword
== aRTFControlWords
[i
].GetIndex())
236 return aRTFControlWords
[i
].GetKeyword();
241 static util::DateTime
lcl_getDateTime(RTFParserState
const& aState
)
243 return { 0 /*100sec*/,
249 static_cast<sal_Int16
>(aState
.getYear()),
253 static void lcl_DestinationToMath(OUStringBuffer
* pDestinationText
,
254 oox::formulaimport::XmlStreamBuilder
& rMathBuffer
, bool& rMathNor
)
256 if (!pDestinationText
)
258 OUString aStr
= pDestinationText
->makeStringAndClear();
261 rMathBuffer
.appendOpeningTag(M_TOKEN(r
));
264 rMathBuffer
.appendOpeningTag(M_TOKEN(rPr
));
265 // Same as M_TOKEN(lit)
266 rMathBuffer
.appendOpeningTag(M_TOKEN(nor
));
267 rMathBuffer
.appendClosingTag(M_TOKEN(nor
));
268 rMathBuffer
.appendClosingTag(M_TOKEN(rPr
));
271 rMathBuffer
.appendOpeningTag(M_TOKEN(t
));
272 rMathBuffer
.appendCharacters(aStr
);
273 rMathBuffer
.appendClosingTag(M_TOKEN(t
));
274 rMathBuffer
.appendClosingTag(M_TOKEN(r
));
277 RTFDocumentImpl::RTFDocumentImpl(uno::Reference
<uno::XComponentContext
> const& xContext
,
278 uno::Reference
<io::XInputStream
> const& xInputStream
,
279 uno::Reference
<lang::XComponent
> const& xDstDoc
,
280 uno::Reference
<frame::XFrame
> const& xFrame
,
281 uno::Reference
<task::XStatusIndicator
> const& xStatusIndicator
,
282 const utl::MediaDescriptor
& rMediaDescriptor
)
283 : m_xContext(xContext
)
284 , m_xInputStream(xInputStream
)
287 , m_xStatusIndicator(xStatusIndicator
)
288 , m_pMapperStream(nullptr)
289 , m_aDefaultState(this)
290 , m_bSkipUnknown(false)
292 , m_bFirstRunException(false)
295 , m_bNeedCrOrig(false)
297 , m_bNeedFinalPar(false)
299 , m_nTopLevelCells(0)
300 , m_nInheritingCells(0)
302 , m_nTopLevelTRLeft(0)
303 , m_nNestedCurrentCellX(0)
304 , m_nTopLevelCurrentCellX(0)
305 , m_nBackupTopLevelCurrentCellX(0)
306 , m_aTableBufferStack(1) // create top-level buffer already
307 , m_pSuperstream(nullptr)
309 , m_nGroupStartPos(0)
310 , m_nFormFieldType(RTFFormFieldType::NONE
)
312 , m_nCurrentFontIndex(0)
313 , m_nCurrentEncoding(-1)
314 , m_nDefaultFontIndex(-1)
315 , m_pStyleTableEntries(new RTFReferenceTable::Entries_t
)
316 , m_nCurrentStyleIndex(0)
317 , m_bFormField(false)
319 , m_bIgnoreNextContSectBreak(false)
320 , m_nResetBreakOnSectBreak(RTFKeyword::invalid
)
321 , m_bNeedSect(false) // done by checkFirstRun
322 , m_bWasInFrame(false)
323 , m_bHadPicture(false)
326 , m_nListPictureId(0)
327 , m_bIsNewDoc(!rMediaDescriptor
.getUnpackedValueOrDefault("InsertMode", false))
328 , m_rMediaDescriptor(rMediaDescriptor
)
329 , m_hasRHeader(false)
330 , m_hasFHeader(false)
331 , m_hasRFooter(false)
332 , m_hasFFooter(false)
334 OSL_ASSERT(xInputStream
.is());
335 m_pInStream
= utl::UcbStreamHelper::CreateStream(xInputStream
, true);
337 m_xModelFactory
.set(m_xDstDoc
, uno::UNO_QUERY
);
339 uno::Reference
<document::XDocumentPropertiesSupplier
> xDocumentPropertiesSupplier(
340 m_xDstDoc
, uno::UNO_QUERY
);
341 if (xDocumentPropertiesSupplier
.is())
342 m_xDocumentProperties
= xDocumentPropertiesSupplier
->getDocumentProperties();
344 m_pGraphicHelper
= std::make_shared
<oox::GraphicHelper
>(m_xContext
, xFrame
, oox::StorageRef());
346 m_pTokenizer
= new RTFTokenizer(*this, m_pInStream
.get(), m_xStatusIndicator
);
347 m_pSdrImport
= new RTFSdrImport(*this, m_xDstDoc
);
349 // unlike OOXML, this is enabled by default
350 m_aSettingsTableSprms
.set(NS_ooxml::LN_CT_Compat_splitPgBreakAndParaMark
, new RTFValue(1));
353 RTFDocumentImpl::~RTFDocumentImpl() = default;
355 SvStream
& RTFDocumentImpl::Strm() { return *m_pInStream
; }
357 void RTFDocumentImpl::setSuperstream(RTFDocumentImpl
* pSuperstream
)
359 m_pSuperstream
= pSuperstream
;
362 bool RTFDocumentImpl::isSubstream() const { return m_pSuperstream
!= nullptr; }
364 void RTFDocumentImpl::finishSubstream() { checkUnicode(/*bUnicode =*/true, /*bHex =*/true); }
366 void RTFDocumentImpl::resolveSubstream(std::size_t nPos
, Id nId
)
368 resolveSubstream(nPos
, nId
, OUString());
370 void RTFDocumentImpl::resolveSubstream(std::size_t nPos
, Id nId
, OUString
const& rIgnoreFirst
)
372 sal_uInt64
const nCurrent
= Strm().Tell();
373 // Seek to header position, parse, then seek back.
374 auto pImpl
= new RTFDocumentImpl(m_xContext
, m_xInputStream
, m_xDstDoc
, m_xFrame
,
375 m_xStatusIndicator
, m_rMediaDescriptor
);
376 pImpl
->setSuperstream(this);
377 pImpl
->m_nStreamType
= nId
;
378 pImpl
->m_aIgnoreFirst
= rIgnoreFirst
;
379 if (!m_aAuthor
.isEmpty())
381 pImpl
->m_aAuthor
= m_aAuthor
;
384 if (!m_aAuthorInitials
.isEmpty())
386 pImpl
->m_aAuthorInitials
= m_aAuthorInitials
;
387 m_aAuthorInitials
.clear();
389 pImpl
->m_nDefaultFontIndex
= m_nDefaultFontIndex
;
390 pImpl
->m_pStyleTableEntries
= m_pStyleTableEntries
;
391 pImpl
->Strm().Seek(nPos
);
392 SAL_INFO("writerfilter.rtf", "substream start");
393 Mapper().substream(nId
, pImpl
);
394 SAL_INFO("writerfilter.rtf", "substream end");
395 Strm().Seek(nCurrent
);
398 void RTFDocumentImpl::outputSettingsTable()
400 // tdf#136740: do not change target document settings when pasting
401 if (!m_bIsNewDoc
|| isSubstream())
403 writerfilter::Reference
<Properties
>::Pointer_t pProp
404 = new RTFReferenceProperties(m_aSettingsTableAttributes
, m_aSettingsTableSprms
);
405 RTFReferenceTable::Entries_t aSettingsTableEntries
;
406 aSettingsTableEntries
.insert(std::make_pair(0, pProp
));
407 writerfilter::Reference
<Table
>::Pointer_t pTable
408 = new RTFReferenceTable(std::move(aSettingsTableEntries
));
409 Mapper().table(NS_ooxml::LN_settings_settings
, pTable
);
412 void RTFDocumentImpl::checkFirstRun()
417 outputSettingsTable();
418 // start initial paragraph
420 assert(!m_bNeedSect
|| m_bFirstRunException
);
421 setNeedSect(true); // first call that succeeds
423 // set the requested default font, if there are none for each state in stack
424 RTFValue::Pointer_t pFont
425 = getNestedAttribute(m_aDefaultState
.getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts
,
426 NS_ooxml::LN_CT_Fonts_ascii
);
430 for (size_t i
= 0; i
< m_aStates
.size(); i
++)
432 RTFValue::Pointer_t pCurrentFont
433 = getNestedAttribute(m_aStates
[i
].getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts
,
434 NS_ooxml::LN_CT_Fonts_ascii
);
436 putNestedAttribute(m_aStates
[i
].getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts
,
437 NS_ooxml::LN_CT_Fonts_ascii
, pFont
);
441 void RTFDocumentImpl::setNeedPar(bool bNeedPar
) { m_bNeedPar
= bNeedPar
; }
443 void RTFDocumentImpl::setNeedSect(bool bNeedSect
)
445 if (!m_bNeedSect
&& bNeedSect
&& m_bFirstRun
)
447 RTFLookahead
aLookahead(Strm(), m_pTokenizer
->getGroupStart());
448 if (aLookahead
.hasTable() && aLookahead
.hasColumns())
450 m_bFirstRunException
= true;
454 // ignore setting before checkFirstRun - every keyword calls setNeedSect!
455 // except the case of a table in a multicolumn section
456 if (!m_bNeedSect
&& bNeedSect
&& (!m_bFirstRun
|| m_bFirstRunException
))
458 if (!m_pSuperstream
) // no sections in header/footer!
460 Mapper().startSectionGroup();
462 // set flag in substream too - otherwise multiple startParagraphGroup
463 m_bNeedSect
= bNeedSect
;
464 Mapper().startParagraphGroup();
467 else if (m_bNeedSect
&& !bNeedSect
)
469 m_bNeedSect
= bNeedSect
;
473 /// Copy rProps to rStyleAttributes and rStyleSprms, but in case of nested sprms, copy their children as toplevel sprms/attributes.
474 static void lcl_copyFlatten(RTFReferenceProperties
& rProps
, RTFSprms
& rStyleAttributes
,
475 RTFSprms
& rStyleSprms
)
477 for (auto& rSprm
: rProps
.getSprms())
479 // createStyleProperties() puts properties to rPr, but here we need a flat list.
480 if (rSprm
.first
== NS_ooxml::LN_CT_Style_rPr
)
482 // rPr can have both attributes and SPRMs, copy over both types.
483 RTFSprms
& rRPrSprms
= rSprm
.second
->getSprms();
484 for (const auto& rRPrSprm
: rRPrSprms
)
485 rStyleSprms
.set(rRPrSprm
.first
, rRPrSprm
.second
);
487 RTFSprms
& rRPrAttributes
= rSprm
.second
->getAttributes();
488 for (const auto& rRPrAttribute
: rRPrAttributes
)
489 rStyleAttributes
.set(rRPrAttribute
.first
, rRPrAttribute
.second
);
492 rStyleSprms
.set(rSprm
.first
, rSprm
.second
);
495 RTFSprms
& rAttributes
= rProps
.getAttributes();
496 for (const auto& rAttribute
: rAttributes
)
497 rStyleAttributes
.set(rAttribute
.first
, rAttribute
.second
);
500 writerfilter::Reference
<Properties
>::Pointer_t
501 RTFDocumentImpl::getProperties(const RTFSprms
& rAttributes
, RTFSprms
const& rSprms
, Id nStyleType
)
503 RTFSprms
aSprms(rSprms
);
504 RTFValue::Pointer_t pAbstractList
;
505 int nAbstractListId
= -1;
506 RTFValue::Pointer_t pNumId
507 = getNestedSprm(aSprms
, NS_ooxml::LN_CT_PPrBase_numPr
, NS_ooxml::LN_CT_NumPr_numId
);
510 // We have a numbering, look up the abstract list for property
511 // deduplication and duplication.
512 auto itNumId
= m_aListOverrideTable
.find(pNumId
->getInt());
513 if (itNumId
!= m_aListOverrideTable
.end())
515 nAbstractListId
= itNumId
->second
;
516 auto itAbstract
= m_aListTable
.find(nAbstractListId
);
517 if (itAbstract
!= m_aListTable
.end())
518 pAbstractList
= itAbstract
->second
;
524 auto it
= m_aInvalidListTableFirstIndents
.find(nAbstractListId
);
525 if (it
!= m_aInvalidListTableFirstIndents
.end())
526 aSprms
.deduplicateList(it
->second
);
530 if (!m_aStates
.empty())
531 nStyle
= m_aStates
.top().getCurrentStyleIndex();
532 auto it
= m_pStyleTableEntries
->find(nStyle
);
533 if (it
!= m_pStyleTableEntries
->end())
535 // cloneAndDeduplicate() wants to know about only a single "style", so
536 // let's merge paragraph and character style properties here.
537 auto itChar
= m_pStyleTableEntries
->end();
538 if (!m_aStates
.empty())
540 int nCharStyle
= m_aStates
.top().getCurrentCharacterStyleIndex();
541 itChar
= m_pStyleTableEntries
->find(nCharStyle
);
544 RTFSprms aStyleSprms
;
545 RTFSprms aStyleAttributes
;
546 // Ensure the paragraph style is a flat list.
547 // Take paragraph style into account for character properties as well,
548 // as paragraph style may contain character properties.
549 RTFReferenceProperties
& rProps
= *static_cast<RTFReferenceProperties
*>(it
->second
.get());
550 lcl_copyFlatten(rProps
, aStyleAttributes
, aStyleSprms
);
552 if (itChar
!= m_pStyleTableEntries
->end())
554 // Found active character style, then update aStyleSprms/Attributes.
555 if (!nStyleType
|| nStyleType
== NS_ooxml::LN_Value_ST_StyleType_character
)
557 RTFReferenceProperties
& rCharProps
558 = *static_cast<RTFReferenceProperties
*>(itChar
->second
.get());
559 lcl_copyFlatten(rCharProps
, aStyleAttributes
, aStyleSprms
);
563 RTFSprms
sprms(aSprms
.cloneAndDeduplicate(aStyleSprms
, nStyleType
, true, &aSprms
));
564 RTFSprms
attributes(rAttributes
.cloneAndDeduplicate(aStyleAttributes
, nStyleType
, true));
565 return new RTFReferenceProperties(std::move(attributes
), std::move(sprms
));
569 aSprms
.duplicateList(pAbstractList
);
570 writerfilter::Reference
<Properties
>::Pointer_t pRet
571 = new RTFReferenceProperties(rAttributes
, std::move(aSprms
));
575 void RTFDocumentImpl::checkNeedPap()
580 m_bNeedPap
= false; // reset early, so we can avoid recursion when calling ourselves
582 if (m_aStates
.empty())
585 if (!m_aStates
.top().getCurrentBuffer())
587 writerfilter::Reference
<Properties
>::Pointer_t
const pParagraphProperties(getProperties(
588 m_aStates
.top().getParagraphAttributes(), m_aStates
.top().getParagraphSprms(),
589 NS_ooxml::LN_Value_ST_StyleType_paragraph
));
591 // Writer will ignore a page break before a text frame, so guard it with empty paragraphs
592 bool hasBreakBeforeFrame
593 = m_aStates
.top().getFrame().hasProperties()
594 && m_aStates
.top().getParagraphSprms().find(NS_ooxml::LN_CT_PPrBase_pageBreakBefore
);
595 if (hasBreakBeforeFrame
)
597 dispatchSymbol(RTFKeyword::PAR
);
600 Mapper().props(pParagraphProperties
);
601 if (hasBreakBeforeFrame
)
602 dispatchSymbol(RTFKeyword::PAR
);
604 if (m_aStates
.top().getFrame().hasProperties())
606 writerfilter::Reference
<Properties
>::Pointer_t
const pFrameProperties(
607 new RTFReferenceProperties(RTFSprms(), m_aStates
.top().getFrame().getSprms()));
608 Mapper().props(pFrameProperties
);
613 auto pValue
= new RTFValue(m_aStates
.top().getParagraphAttributes(),
614 m_aStates
.top().getParagraphSprms());
615 bufferProperties(*m_aStates
.top().getCurrentBuffer(), pValue
, nullptr);
619 void RTFDocumentImpl::runProps()
621 if (!m_aStates
.top().getCurrentBuffer())
623 Reference
<Properties
>::Pointer_t
const pProperties
= getProperties(
624 m_aStates
.top().getCharacterAttributes(), m_aStates
.top().getCharacterSprms(),
625 NS_ooxml::LN_Value_ST_StyleType_character
);
626 Mapper().props(pProperties
);
630 auto pValue
= new RTFValue(m_aStates
.top().getCharacterAttributes(),
631 m_aStates
.top().getCharacterSprms());
632 bufferProperties(*m_aStates
.top().getCurrentBuffer(), pValue
, nullptr,
633 NS_ooxml::LN_Value_ST_StyleType_character
);
636 // Delete the sprm, so the trackchange range will be started only once.
637 // OTOH set a boolean flag, so we'll know we need to end the range later.
638 RTFValue::Pointer_t pTrackchange
639 = m_aStates
.top().getCharacterSprms().find(NS_ooxml::LN_trackchange
);
642 m_aStates
.top().setStartedTrackchange(true);
643 m_aStates
.top().getCharacterSprms().erase(NS_ooxml::LN_trackchange
);
647 void RTFDocumentImpl::runBreak()
649 sal_Unicode
const sBreak
[] = { 0x0d };
650 Mapper().utext(sBreak
, 1);
654 void RTFDocumentImpl::tableBreak()
656 checkFirstRun(); // ooo113308-1.rtf has a header at offset 151084 that doesn't startParagraphGroup() without this
658 Mapper().endParagraphGroup();
659 Mapper().startParagraphGroup();
662 void RTFDocumentImpl::parBreak()
666 // end previous paragraph
667 Mapper().startCharacterGroup();
669 Mapper().endCharacterGroup();
670 Mapper().endParagraphGroup();
672 m_bHadPicture
= false;
675 if (!m_bParAtEndOfSection
)
677 Mapper().startParagraphGroup();
681 void RTFDocumentImpl::sectBreak(bool bFinal
)
683 SAL_INFO("writerfilter.rtf", __func__
<< ": final? " << bFinal
<< ", needed? " << m_bNeedSect
);
684 bool bNeedSect
= m_bNeedSect
;
685 RTFValue::Pointer_t pBreak
686 = m_aStates
.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_type
);
687 bool bContinuous
= pBreak
&& pBreak
->getInt() == NS_ooxml::LN_Value_ST_SectionMark_continuous
;
688 // If there is no paragraph in this section, then insert a dummy one, as required by Writer,
689 // unless this is the end of the doc, we had nothing since the last section break and this is not a continuous one.
690 // Also, when pasting, it's fine to not have any paragraph inside the document at all.
691 if (m_bNeedPar
&& (!bFinal
|| m_bNeedSect
|| bContinuous
) && !isSubstream() && m_bIsNewDoc
)
693 m_bParAtEndOfSection
= true;
694 dispatchSymbol(RTFKeyword::PAR
);
696 // It's allowed to not have a non-table paragraph at the end of an RTF doc, add it now if required.
697 if (m_bNeedFinalPar
&& bFinal
)
699 dispatchFlag(RTFKeyword::PARD
);
700 m_bParAtEndOfSection
= true;
701 dispatchSymbol(RTFKeyword::PAR
);
702 m_bNeedSect
= bNeedSect
;
704 // testTdf148515, if RTF ends with \row, endParagraphGroup() must be called!
705 if (!m_bParAtEndOfSection
|| m_aStates
.top().getCurrentBuffer())
707 Mapper().endParagraphGroup(); // < top para context dies with page break
709 m_bParAtEndOfSection
= false;
710 // paragraph properties are *done* now - only section properties following
712 while (!m_nHeaderFooterPositions
.empty())
714 std::pair
<Id
, std::size_t> aPair
= m_nHeaderFooterPositions
.front();
715 m_nHeaderFooterPositions
.pop();
716 resolveSubstream(aPair
.second
, aPair
.first
);
719 // Normally a section break at the end of the doc is necessary. Unless the
720 // last control word in the document is a section break itself.
721 if (!bNeedSect
|| !m_bHadSect
)
723 // In case the last section is a continuous one, we don't need to output a section break.
724 if (bFinal
&& bContinuous
)
725 m_aStates
.top().getSectionSprms().erase(NS_ooxml::LN_EG_SectPrContents_type
);
728 // Section properties are a paragraph sprm.
730 = new RTFValue(m_aStates
.top().getSectionAttributes(), m_aStates
.top().getSectionSprms());
731 RTFSprms aAttributes
;
733 aSprms
.set(NS_ooxml::LN_CT_PPr_sectPr
, pValue
);
734 writerfilter::Reference
<Properties
>::Pointer_t pProperties
735 = new RTFReferenceProperties(std::move(aAttributes
), std::move(aSprms
));
737 if (bFinal
&& !m_pSuperstream
)
738 // This is the end of the document, not just the end of e.g. a header.
739 // This makes sure that dmapper can set DontBalanceTextColumns=true for this section if necessary.
740 Mapper().markLastSectionGroup();
742 // The trick is that we send properties of the previous section right now, which will be exactly what dmapper expects.
743 Mapper().props(pProperties
);
748 m_hasFHeader
= false;
749 m_hasRHeader
= false;
750 m_hasRFooter
= false;
751 m_hasFFooter
= false;
752 Mapper().endSectionGroup();
758 Color
RTFDocumentImpl::getColorTable(sal_uInt32 nIndex
)
762 if (nIndex
< m_aColorTable
.size())
763 return m_aColorTable
[nIndex
];
767 return m_pSuperstream
->getColorTable(nIndex
);
770 rtl_TextEncoding
RTFDocumentImpl::getEncoding(int nFontIndex
)
774 auto it
= m_aFontEncodings
.find(nFontIndex
);
775 if (it
!= m_aFontEncodings
.end())
776 // We have a font encoding associated to this font.
778 if (m_aDefaultState
.getCurrentEncoding() != rtl_getTextEncodingFromWindowsCharset(0))
779 // We have a default encoding.
780 return m_aDefaultState
.getCurrentEncoding();
781 // Guess based on locale.
782 return msfilter::util::getBestTextEncodingFromLocale(
783 Application::GetSettings().GetLanguageTag().getLocale());
786 return m_pSuperstream
->getEncoding(nFontIndex
);
789 OUString
RTFDocumentImpl::getFontName(int nIndex
)
792 return m_aFontNames
[nIndex
];
794 return m_pSuperstream
->getFontName(nIndex
);
797 int RTFDocumentImpl::getFontIndex(int nIndex
)
800 return std::find(m_aFontIndexes
.begin(), m_aFontIndexes
.end(), nIndex
)
801 - m_aFontIndexes
.begin();
803 return m_pSuperstream
->getFontIndex(nIndex
);
806 OUString
RTFDocumentImpl::getStyleName(int nIndex
)
811 auto it
= m_aStyleNames
.find(nIndex
);
812 if (it
!= m_aStyleNames
.end())
817 return m_pSuperstream
->getStyleName(nIndex
);
820 Id
RTFDocumentImpl::getStyleType(int nIndex
)
825 auto it
= m_aStyleTypes
.find(nIndex
);
826 if (it
!= m_aStyleTypes
.end())
831 return m_pSuperstream
->getStyleType(nIndex
);
834 RTFParserState
& RTFDocumentImpl::getDefaultState()
837 return m_aDefaultState
;
839 return m_pSuperstream
->getDefaultState();
842 oox::GraphicHelper
& RTFDocumentImpl::getGraphicHelper() { return *m_pGraphicHelper
; }
844 bool RTFDocumentImpl::isStyleSheetImport()
846 if (m_aStates
.empty())
848 Destination eDestination
= m_aStates
.top().getDestination();
849 return eDestination
== Destination::STYLESHEET
|| eDestination
== Destination::STYLEENTRY
;
852 void RTFDocumentImpl::resolve(Stream
& rMapper
)
854 m_pMapperStream
= &rMapper
;
855 switch (m_pTokenizer
->resolveParse())
858 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: finished without errors");
860 case RTFError::GROUP_UNDER
:
861 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unmatched '}'");
863 case RTFError::GROUP_OVER
:
864 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unmatched '{'");
865 throw io::WrongFormatException(m_pTokenizer
->getPosition());
867 case RTFError::UNEXPECTED_EOF
:
868 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unexpected end of file");
869 throw io::WrongFormatException(m_pTokenizer
->getPosition());
871 case RTFError::HEX_INVALID
:
872 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: invalid hex char");
873 throw io::WrongFormatException(m_pTokenizer
->getPosition());
875 case RTFError::CHAR_OVER
:
876 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: characters after last '}'");
878 case RTFError::CLASSIFICATION
:
879 SAL_INFO("writerfilter.rtf",
880 "RTFDocumentImpl::resolve: classification prevented paste");
885 void RTFDocumentImpl::resolvePict(bool const bInline
, uno::Reference
<drawing::XShape
> const& rShape
)
887 SvMemoryStream aStream
;
888 SvStream
* pStream
= nullptr;
895 // Feed the destination text to a stream.
896 auto& rDestinationTextBuffer
= m_aStates
.top().getDestinationText();
897 OString aStr
= OUStringToOString(rDestinationTextBuffer
, RTL_TEXTENCODING_ASCII_US
);
898 rDestinationTextBuffer
.setLength(0);
899 for (int i
= 0; i
< aStr
.getLength(); ++i
)
902 if (ch
!= 0x0d && ch
!= 0x0a && ch
!= 0x20)
905 sal_Int8 parsed
= msfilter::rtfutil::AsHex(ch
);
912 aStream
.WriteChar(static_cast<char>(b
));
920 pStream
= m_pBinaryData
.get();
922 if (!pStream
->Tell())
923 // No destination text? Then we'll get it later.
926 SvMemoryStream aDIBStream
;
927 if (m_aStates
.top().getPicture().eStyle
== RTFBmpStyle::DIBITMAP
)
929 // Construct a BITMAPFILEHEADER structure before the real data.
930 SvStream
& rBodyStream
= *pStream
;
931 aDIBStream
.WriteChar('B');
932 aDIBStream
.WriteChar('M');
933 // The size of the real data.
934 aDIBStream
.WriteUInt32(rBodyStream
.Tell());
936 aDIBStream
.WriteUInt32(0);
937 // The offset of the real data, i.e. the size of the header, including this number.
938 aDIBStream
.WriteUInt32(14);
940 aDIBStream
.WriteStream(rBodyStream
);
941 pStream
= &aDIBStream
;
944 // Store, and get its URL.
946 uno::Reference
<io::XInputStream
> xInputStream(new utl::OInputStreamWrapper(pStream
));
947 WmfExternal aExtHeader
;
948 aExtHeader
.mapMode
= m_aStates
.top().getPicture().eWMetafile
;
949 if (m_aStates
.top().getPicture().nGoalWidth
== 0
950 || m_aStates
.top().getPicture().nGoalHeight
== 0)
952 // Don't use the values provided by picw and pich if the desired size is provided.
954 aExtHeader
.xExt
= sal_uInt16(std::clamp
<sal_Int32
>(
955 m_aStates
.top().getPicture().nWidth
, 0,
956 SAL_MAX_UINT16
)); //TODO: better way to handle out-of-bounds values?
957 aExtHeader
.yExt
= sal_uInt16(std::clamp
<sal_Int32
>(
958 m_aStates
.top().getPicture().nHeight
, 0,
959 SAL_MAX_UINT16
)); //TODO: better way to handle out-of-bounds values?
961 WmfExternal
* pExtHeader
= &aExtHeader
;
962 uno::Reference
<lang::XServiceInfo
> xServiceInfo(m_aStates
.top().getDrawingObject().getShape(),
964 if (xServiceInfo
.is() && xServiceInfo
->supportsService("com.sun.star.text.TextFrame"))
965 pExtHeader
= nullptr;
967 uno::Reference
<graphic::XGraphic
> xGraphic
968 = m_pGraphicHelper
->importGraphic(xInputStream
, pExtHeader
);
970 if (m_aStates
.top().getPicture().eStyle
!= RTFBmpStyle::NONE
)
972 // In case of PNG/JPEG, the real size is known, don't use the values
973 // provided by picw and pich.
975 Graphic
aGraphic(xGraphic
);
976 Size
aSize(aGraphic
.GetPrefSize());
977 MapMode
aMap(MapUnit::Map100thMM
);
978 if (aGraphic
.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel
)
979 aSize
= Application::GetDefaultDevice()->PixelToLogic(aSize
, aMap
);
981 aSize
= OutputDevice::LogicToLogic(aSize
, aGraphic
.GetPrefMapMode(), aMap
);
982 m_aStates
.top().getPicture().nWidth
= aSize
.Width();
983 m_aStates
.top().getPicture().nHeight
= aSize
.Height();
986 uno::Reference
<drawing::XShape
> xShape(rShape
);
987 if (m_aStates
.top().getInShape() && xShape
.is())
989 awt::Size aSize
= xShape
->getSize();
990 if (aSize
.Width
|| aSize
.Height
)
992 // resolvePict() is processing pib structure inside shape
993 // So if shape has dimensions we should use them instead of
994 // \picwN, \pichN, \picscalexN, \picscaleyN given with picture
995 m_aStates
.top().getPicture().nGoalWidth
= aSize
.Width
;
996 m_aStates
.top().getPicture().nGoalHeight
= aSize
.Height
;
997 m_aStates
.top().getPicture().nScaleX
= 100;
998 m_aStates
.top().getPicture().nScaleY
= 100;
1002 // Wrap it in an XShape.
1005 uno::Reference
<lang::XServiceInfo
> xSI(xShape
, uno::UNO_QUERY_THROW
);
1006 if (!xSI
->supportsService("com.sun.star.drawing.GraphicObjectShape"))
1008 // it's sometimes an error to get here - but it's possible to have
1009 // a \pict inside the \shptxt of a \shp of shapeType 202 "TextBox"
1010 // and in that case xShape is the text frame; we actually need a
1011 // new GraphicObject then (example: fdo37691-1.rtf)
1012 SAL_INFO("writerfilter.rtf",
1013 "cannot set graphic on existing shape, creating a new GraphicObjectShape");
1019 if (m_xModelFactory
.is())
1020 xShape
.set(m_xModelFactory
->createInstance("com.sun.star.drawing.GraphicObjectShape"),
1022 uno::Reference
<drawing::XDrawPageSupplier
> const xDrawSupplier(m_xDstDoc
, uno::UNO_QUERY
);
1023 if (xDrawSupplier
.is())
1025 uno::Reference
<drawing::XShapes
> xShapes
= xDrawSupplier
->getDrawPage();
1027 xShapes
->add(xShape
);
1031 uno::Reference
<beans::XPropertySet
> xPropertySet(xShape
, uno::UNO_QUERY
);
1033 if (xPropertySet
.is())
1034 xPropertySet
->setPropertyValue("Graphic", uno::Any(xGraphic
));
1036 // check if the picture is in an OLE object and if the \objdata element is used
1037 // (see RTFKeyword::OBJECT in RTFDocumentImpl::dispatchDestination)
1040 // Set the object size
1043 = (m_aStates
.top().getPicture().nGoalWidth
? m_aStates
.top().getPicture().nGoalWidth
1044 : m_aStates
.top().getPicture().nWidth
);
1046 = (m_aStates
.top().getPicture().nGoalHeight
? m_aStates
.top().getPicture().nGoalHeight
1047 : m_aStates
.top().getPicture().nHeight
);
1048 xShape
->setSize(aSize
);
1050 // Replacement graphic is inline by default, see oox::vml::SimpleShape::implConvertAndInsert().
1051 xPropertySet
->setPropertyValue("AnchorType",
1052 uno::Any(text::TextContentAnchorType_AS_CHARACTER
));
1054 auto pShapeValue
= new RTFValue(xShape
);
1055 m_aObjectAttributes
.set(NS_ooxml::LN_shape
, pShapeValue
);
1059 if (m_aStates
.top().getInListpicture())
1061 // Send the shape directly, no section is started, to additional properties will be ignored anyway.
1062 Mapper().startShape(xShape
);
1063 Mapper().endShape();
1067 // Send it to the dmapper.
1069 RTFSprms aAttributes
;
1071 RTFSprms aPicAttributes
;
1072 if (m_aStates
.top().getPicture().nCropT
!= 0 || m_aStates
.top().getPicture().nCropB
!= 0
1073 || m_aStates
.top().getPicture().nCropL
!= 0 || m_aStates
.top().getPicture().nCropR
!= 0)
1075 text::GraphicCrop
const crop
{ m_aStates
.top().getPicture().nCropT
,
1076 m_aStates
.top().getPicture().nCropB
,
1077 m_aStates
.top().getPicture().nCropL
,
1078 m_aStates
.top().getPicture().nCropR
};
1079 auto const pCrop
= new RTFValue(crop
);
1080 aPicAttributes
.set(NS_ooxml::LN_CT_BlipFillProperties_srcRect
, pCrop
);
1082 auto pShapeValue
= new RTFValue(xShape
);
1083 aPicAttributes
.set(NS_ooxml::LN_shape
, pShapeValue
);
1085 RTFSprms aGraphicDataAttributes
;
1086 RTFSprms aGraphicDataSprms
;
1087 auto pPicValue
= new RTFValue(aPicAttributes
);
1088 aGraphicDataSprms
.set(NS_ooxml::LN_pic_pic
, pPicValue
);
1090 RTFSprms aGraphicAttributes
;
1091 RTFSprms aGraphicSprms
;
1092 auto pGraphicDataValue
= new RTFValue(aGraphicDataAttributes
, aGraphicDataSprms
);
1093 aGraphicSprms
.set(NS_ooxml::LN_CT_GraphicalObject_graphicData
, pGraphicDataValue
);
1095 auto pGraphicValue
= new RTFValue(aGraphicAttributes
, aGraphicSprms
);
1097 RTFSprms aExtentAttributes
;
1098 int nXExt
= (m_aStates
.top().getPicture().nGoalWidth
? m_aStates
.top().getPicture().nGoalWidth
1099 : m_aStates
.top().getPicture().nWidth
);
1100 int nYExt
= (m_aStates
.top().getPicture().nGoalHeight
? m_aStates
.top().getPicture().nGoalHeight
1101 : m_aStates
.top().getPicture().nHeight
);
1102 if (m_aStates
.top().getPicture().nScaleX
!= 100)
1103 nXExt
= (static_cast<tools::Long
>(m_aStates
.top().getPicture().nScaleX
)
1105 - (m_aStates
.top().getPicture().nCropL
+ m_aStates
.top().getPicture().nCropR
)))
1107 if (m_aStates
.top().getPicture().nScaleY
!= 100)
1108 nYExt
= (static_cast<tools::Long
>(m_aStates
.top().getPicture().nScaleY
)
1110 - (m_aStates
.top().getPicture().nCropT
+ m_aStates
.top().getPicture().nCropB
)))
1112 auto pXExtValue
= new RTFValue(oox::drawingml::convertHmmToEmu(nXExt
));
1113 auto pYExtValue
= new RTFValue(oox::drawingml::convertHmmToEmu(nYExt
));
1114 aExtentAttributes
.set(NS_ooxml::LN_CT_PositiveSize2D_cx
, pXExtValue
);
1115 aExtentAttributes
.set(NS_ooxml::LN_CT_PositiveSize2D_cy
, pYExtValue
);
1116 auto pExtentValue
= new RTFValue(aExtentAttributes
);
1118 RTFSprms aDocprAttributes
;
1119 for (const auto& rCharacterAttribute
: m_aStates
.top().getCharacterAttributes())
1120 if (rCharacterAttribute
.first
== NS_ooxml::LN_CT_NonVisualDrawingProps_name
1121 || rCharacterAttribute
.first
== NS_ooxml::LN_CT_NonVisualDrawingProps_descr
)
1122 aDocprAttributes
.set(rCharacterAttribute
.first
, rCharacterAttribute
.second
);
1123 auto pDocprValue
= new RTFValue(aDocprAttributes
);
1126 RTFSprms aInlineAttributes
;
1127 aInlineAttributes
.set(NS_ooxml::LN_CT_Inline_distT
, new RTFValue(0));
1128 aInlineAttributes
.set(NS_ooxml::LN_CT_Inline_distB
, new RTFValue(0));
1129 aInlineAttributes
.set(NS_ooxml::LN_CT_Inline_distL
, new RTFValue(0));
1130 aInlineAttributes
.set(NS_ooxml::LN_CT_Inline_distR
, new RTFValue(0));
1131 RTFSprms aInlineSprms
;
1132 aInlineSprms
.set(NS_ooxml::LN_CT_Inline_extent
, pExtentValue
);
1133 aInlineSprms
.set(NS_ooxml::LN_CT_Inline_docPr
, pDocprValue
);
1134 aInlineSprms
.set(NS_ooxml::LN_graphic_graphic
, pGraphicValue
);
1136 auto pValue
= new RTFValue(aInlineAttributes
, aInlineSprms
);
1137 aSprms
.set(NS_ooxml::LN_inline_inline
, pValue
);
1142 RTFSprms aAnchorWrapAttributes
;
1143 m_aStates
.top().getShape().getAnchorAttributes().set(
1144 NS_ooxml::LN_CT_Anchor_behindDoc
,
1145 new RTFValue((m_aStates
.top().getShape().getInBackground()) ? 1 : 0));
1146 RTFSprms aAnchorSprms
;
1147 for (const auto& rCharacterAttribute
: m_aStates
.top().getCharacterAttributes())
1149 if (rCharacterAttribute
.first
== NS_ooxml::LN_CT_WrapSquare_wrapText
)
1150 aAnchorWrapAttributes
.set(rCharacterAttribute
.first
, rCharacterAttribute
.second
);
1152 sal_Int32 nWrap
= -1;
1153 for (auto& rCharacterSprm
: m_aStates
.top().getCharacterSprms())
1155 if (rCharacterSprm
.first
== NS_ooxml::LN_EG_WrapType_wrapNone
1156 || rCharacterSprm
.first
== NS_ooxml::LN_EG_WrapType_wrapTight
)
1158 nWrap
= rCharacterSprm
.first
;
1160 // If there is a wrap polygon prepared by RTFSdrImport, pick it up here.
1161 if (rCharacterSprm
.first
== NS_ooxml::LN_EG_WrapType_wrapTight
1162 && !m_aStates
.top().getShape().getWrapPolygonSprms().empty())
1163 rCharacterSprm
.second
->getSprms().set(
1164 NS_ooxml::LN_CT_WrapTight_wrapPolygon
,
1165 new RTFValue(RTFSprms(), m_aStates
.top().getShape().getWrapPolygonSprms()));
1167 aAnchorSprms
.set(rCharacterSprm
.first
, rCharacterSprm
.second
);
1171 if (m_aStates
.top().getShape().getWrapSprm().first
!= 0)
1172 // Replay of a buffered shape, wrap sprm there has priority over
1173 // character sprms of the current state.
1174 aAnchorSprms
.set(m_aStates
.top().getShape().getWrapSprm().first
,
1175 m_aStates
.top().getShape().getWrapSprm().second
);
1177 aAnchorSprms
.set(NS_ooxml::LN_CT_Anchor_extent
, pExtentValue
);
1178 if (!aAnchorWrapAttributes
.empty() && nWrap
== -1)
1179 aAnchorSprms
.set(NS_ooxml::LN_EG_WrapType_wrapSquare
,
1180 new RTFValue(aAnchorWrapAttributes
));
1182 // See OOXMLFastContextHandler::positionOffset(), we can't just put offset values in an RTFValue.
1183 RTFSprms aPoshAttributes
;
1184 RTFSprms aPoshSprms
;
1185 if (m_aStates
.top().getShape().getHoriOrientRelationToken() > 0)
1186 aPoshAttributes
.set(
1187 NS_ooxml::LN_CT_PosH_relativeFrom
,
1188 new RTFValue(m_aStates
.top().getShape().getHoriOrientRelationToken()));
1189 if (m_aStates
.top().getShape().getLeft() != 0)
1191 Mapper().positionOffset(OUString::number(oox::drawingml::convertHmmToEmu(
1192 m_aStates
.top().getShape().getLeft())),
1193 /*bVertical=*/false);
1194 aPoshSprms
.set(NS_ooxml::LN_CT_PosH_posOffset
, new RTFValue());
1196 aAnchorSprms
.set(NS_ooxml::LN_CT_Anchor_positionH
,
1197 new RTFValue(aPoshAttributes
, aPoshSprms
));
1199 RTFSprms aPosvAttributes
;
1200 RTFSprms aPosvSprms
;
1201 if (m_aStates
.top().getShape().getVertOrientRelationToken() > 0)
1202 aPosvAttributes
.set(
1203 NS_ooxml::LN_CT_PosV_relativeFrom
,
1204 new RTFValue(m_aStates
.top().getShape().getVertOrientRelationToken()));
1205 if (m_aStates
.top().getShape().getTop() != 0)
1207 Mapper().positionOffset(OUString::number(oox::drawingml::convertHmmToEmu(
1208 m_aStates
.top().getShape().getTop())),
1209 /*bVertical=*/true);
1210 aPosvSprms
.set(NS_ooxml::LN_CT_PosV_posOffset
, new RTFValue());
1212 aAnchorSprms
.set(NS_ooxml::LN_CT_Anchor_positionV
,
1213 new RTFValue(aPosvAttributes
, aPosvSprms
));
1215 aAnchorSprms
.set(NS_ooxml::LN_CT_Anchor_docPr
, pDocprValue
);
1216 aAnchorSprms
.set(NS_ooxml::LN_graphic_graphic
, pGraphicValue
);
1218 auto pValue
= new RTFValue(m_aStates
.top().getShape().getAnchorAttributes(), aAnchorSprms
);
1219 aSprms
.set(NS_ooxml::LN_anchor_anchor
, pValue
);
1223 if (!m_aStates
.top().getCurrentBuffer())
1225 writerfilter::Reference
<Properties
>::Pointer_t pProperties
1226 = new RTFReferenceProperties(std::move(aAttributes
), std::move(aSprms
));
1227 Mapper().props(pProperties
);
1228 // Make sure we don't lose these properties with a too early reset.
1229 m_bHadPicture
= true;
1233 auto pValue
= new RTFValue(aAttributes
, aSprms
);
1234 bufferProperties(*m_aStates
.top().getCurrentBuffer(), pValue
, nullptr);
1238 RTFError
RTFDocumentImpl::resolveChars(char ch
)
1240 if (m_aStates
.top().getInternalState() == RTFInternalState::BIN
)
1242 m_pBinaryData
= std::make_shared
<SvMemoryStream
>();
1243 m_pBinaryData
->WriteChar(ch
);
1244 for (int i
= 0; i
< m_aStates
.top().getBinaryToRead() - 1; ++i
)
1246 Strm().ReadChar(ch
);
1247 m_pBinaryData
->WriteChar(ch
);
1249 m_aStates
.top().setInternalState(RTFInternalState::NORMAL
);
1250 return RTFError::OK
;
1253 OStringBuffer
aBuf(512);
1255 bool bUnicodeChecked
= false;
1256 bool bSkipped
= false;
1258 while (!Strm().eof()
1259 && (m_aStates
.top().getInternalState() == RTFInternalState::HEX
1260 || (ch
!= '{' && ch
!= '}' && ch
!= '\\')))
1262 if (m_aStates
.top().getInternalState() == RTFInternalState::HEX
1263 || (ch
!= 0x0d && ch
!= 0x0a))
1265 if (m_aStates
.top().getCharsToSkip() == 0)
1267 if (!bUnicodeChecked
)
1269 checkUnicode(/*bUnicode =*/true, /*bHex =*/false);
1270 bUnicodeChecked
= true;
1277 m_aStates
.top().getCharsToSkip()--;
1281 // read a single char if we're in hex mode
1282 if (m_aStates
.top().getInternalState() == RTFInternalState::HEX
)
1285 if (RTL_TEXTENCODING_MS_932
== m_aStates
.top().getCurrentEncoding())
1287 unsigned char uch
= ch
;
1288 if ((uch
>= 0x80 && uch
<= 0x9F) || uch
>= 0xE0)
1290 // read second byte of 2-byte Shift-JIS - may be \ { }
1291 Strm().ReadChar(ch
);
1292 if (m_aStates
.top().getCharsToSkip() == 0)
1294 // fdo#79384: Word will reject Shift-JIS following \loch
1295 // but apparently OOo could read and (worse) write such documents
1296 SAL_INFO_IF(m_aStates
.top().getRunType() != RTFParserState::RunType::DBCH
,
1297 "writerfilter.rtf", "invalid Shift-JIS without DBCH");
1298 assert(bUnicodeChecked
);
1304 // anybody who uses \ucN with Shift-JIS is insane
1305 m_aStates
.top().getCharsToSkip()--;
1310 Strm().ReadChar(ch
);
1312 if (m_aStates
.top().getInternalState() != RTFInternalState::HEX
&& !Strm().eof())
1315 if (m_aStates
.top().getInternalState() == RTFInternalState::HEX
1316 && m_aStates
.top().getDestination() != Destination::LEVELNUMBERS
)
1320 // note: apparently \'0d\'0a is interpreted as 2 breaks, not 1
1321 if ((ch
== '\r' || ch
== '\n')
1322 && m_aStates
.top().getDestination() != Destination::DOCCOMM
1323 && m_aStates
.top().getDestination() != Destination::LEVELNUMBERS
1324 && m_aStates
.top().getDestination() != Destination::LEVELTEXT
)
1326 checkUnicode(/*bUnicode =*/false, /*bHex =*/true);
1327 dispatchSymbol(RTFKeyword::PAR
);
1331 m_aHexBuffer
.append(ch
);
1334 return RTFError::OK
;
1337 if (m_aStates
.top().getDestination() == Destination::SKIP
)
1338 return RTFError::OK
;
1339 OString aStr
= aBuf
.makeStringAndClear();
1340 if (m_aStates
.top().getDestination() == Destination::LEVELNUMBERS
)
1342 if (aStr
.toChar() != ';')
1343 m_aStates
.top().getLevelNumbers().push_back(sal_Int32(ch
));
1344 return RTFError::OK
;
1347 SAL_INFO("writerfilter.rtf",
1348 "RTFDocumentImpl::resolveChars: collected '"
1349 << OStringToOUString(aStr
, m_aStates
.top().getCurrentEncoding()) << "'");
1351 if (m_aStates
.top().getDestination() == Destination::COLORTABLE
)
1353 // we hit a ';' at the end of each color entry
1354 m_aColorTable
.push_back(m_aStates
.top().getCurrentColor().GetColor());
1355 // set components back to zero
1356 m_aStates
.top().getCurrentColor() = RTFColorTableEntry();
1358 else if (!aStr
.isEmpty())
1359 m_aHexBuffer
.append(aStr
);
1361 checkUnicode(/*bUnicode =*/false, /*bHex =*/true);
1362 return RTFError::OK
;
1365 void RTFDocumentImpl::singleChar(sal_uInt8 nValue
, bool bRunProps
)
1367 sal_uInt8 sValue
[] = { nValue
};
1368 RTFBuffer_t
* pCurrentBuffer
= m_aStates
.top().getCurrentBuffer();
1370 if (!pCurrentBuffer
)
1372 Mapper().startCharacterGroup();
1376 pCurrentBuffer
->push_back(Buf_t(BUFFER_STARTRUN
, nullptr, nullptr));
1379 // Should we send run properties?
1383 if (!pCurrentBuffer
)
1385 Mapper().text(sValue
, 1);
1386 Mapper().endCharacterGroup();
1390 auto pValue
= new RTFValue(*sValue
);
1391 pCurrentBuffer
->push_back(Buf_t(BUFFER_TEXT
, pValue
, nullptr));
1392 pCurrentBuffer
->push_back(Buf_t(BUFFER_ENDRUN
, nullptr, nullptr));
1396 void RTFDocumentImpl::handleFontTableEntry()
1398 OUString aName
= m_aStates
.top().getCurrentDestinationText()->makeStringAndClear();
1400 if (aName
.isEmpty())
1403 if (aName
.endsWith(";"))
1405 aName
= aName
.copy(0, aName
.getLength() - 1);
1408 // Old documents can contain no encoding information in fontinfo,
1409 // but there can be font name suffixes: Arial CE is not a special
1410 // font, it is ordinal Arial, but with used cp 1250 encoding.
1411 // Moreover these suffixes have priority over \cpgN and \fcharsetN
1413 OUString aFontSuffix
;
1414 OUString
aNameNoSuffix(aName
);
1415 sal_Int32 nLastSpace
= aName
.lastIndexOf(' ');
1416 if (nLastSpace
>= 0)
1418 aFontSuffix
= aName
.copy(nLastSpace
+ 1);
1419 aNameNoSuffix
= aName
.copy(0, nLastSpace
);
1420 sal_Int32 nEncoding
= RTL_TEXTENCODING_DONTKNOW
;
1421 for (int i
= 0; aRTFFontNameSuffixes
[i
].codepage
!= RTL_TEXTENCODING_DONTKNOW
; i
++)
1423 if (aFontSuffix
.equalsAscii(aRTFFontNameSuffixes
[i
].suffix
))
1425 nEncoding
= aRTFFontNameSuffixes
[i
].codepage
;
1429 if (nEncoding
> RTL_TEXTENCODING_DONTKNOW
)
1431 m_nCurrentEncoding
= nEncoding
;
1432 m_aStates
.top().setCurrentEncoding(m_nCurrentEncoding
);
1436 // Unknown suffix: looks like it is just a part of font name, restore it
1437 aNameNoSuffix
= aName
;
1441 m_aFontNames
[m_nCurrentFontIndex
] = aNameNoSuffix
;
1442 if (m_nCurrentEncoding
>= 0)
1444 m_aFontEncodings
[m_nCurrentFontIndex
] = m_nCurrentEncoding
;
1445 m_nCurrentEncoding
= -1;
1447 m_aStates
.top().getTableAttributes().set(NS_ooxml::LN_CT_Font_name
,
1448 new RTFValue(aNameNoSuffix
));
1450 writerfilter::Reference
<Properties
>::Pointer_t
const pProp(new RTFReferenceProperties(
1451 m_aStates
.top().getTableAttributes(), m_aStates
.top().getTableSprms()));
1453 //See fdo#47347 initial invalid font entry properties are inserted first,
1454 //so when we attempt to insert the correct ones, there's already an
1455 //entry in the map for them, so the new ones aren't inserted.
1456 auto lb
= m_aFontTableEntries
.lower_bound(m_nCurrentFontIndex
);
1457 if (lb
!= m_aFontTableEntries
.end()
1458 && !(m_aFontTableEntries
.key_comp()(m_nCurrentFontIndex
, lb
->first
)))
1461 m_aFontTableEntries
.insert(lb
, std::make_pair(m_nCurrentFontIndex
, pProp
));
1464 void RTFDocumentImpl::text(OUString
& rString
)
1466 if (rString
.getLength() == 1 && m_aStates
.top().getDestination() != Destination::DOCCOMM
)
1468 // No cheating! Tokenizer ignores bare \r and \n, their hex \'0d / \'0a form doesn't count, either.
1469 sal_Unicode ch
= rString
[0];
1470 if (ch
== 0x0d || ch
== 0x0a)
1475 switch (m_aStates
.top().getDestination())
1477 // Note: in stylesheet and revtbl groups are mandatory
1478 case Destination::STYLEENTRY
:
1479 case Destination::LISTNAME
:
1480 case Destination::REVISIONENTRY
:
1482 // ; is the end of the entry
1484 if (rString
.endsWith(";"))
1486 rString
= rString
.copy(0, rString
.getLength() - 1);
1489 m_aStates
.top().appendDestinationText(rString
);
1492 // always clear, necessary in case of group-less fonttable
1493 OUString
const aName
1494 = m_aStates
.top().getCurrentDestinationText()->makeStringAndClear();
1495 switch (m_aStates
.top().getDestination())
1497 case Destination::STYLEENTRY
:
1499 RTFValue::Pointer_t pType
1500 = m_aStates
.top().getTableAttributes().find(NS_ooxml::LN_CT_Style_type
);
1503 // Word strips whitespace around style names.
1504 m_aStyleNames
[m_nCurrentStyleIndex
] = aName
.trim();
1505 m_aStyleTypes
[m_nCurrentStyleIndex
] = pType
->getInt();
1506 auto pValue
= new RTFValue(aName
.trim());
1507 m_aStates
.top().getTableAttributes().set(NS_ooxml::LN_CT_Style_styleId
,
1509 m_aStates
.top().getTableSprms().set(NS_ooxml::LN_CT_Style_name
, pValue
);
1511 writerfilter::Reference
<Properties
>::Pointer_t
const pProp(
1512 createStyleProperties());
1513 m_pStyleTableEntries
->insert(
1514 std::make_pair(m_nCurrentStyleIndex
, pProp
));
1517 SAL_INFO("writerfilter.rtf", "no RTF style type defined, ignoring");
1520 case Destination::LISTNAME
:
1521 // TODO: what can be done with a list name?
1523 case Destination::REVISIONENTRY
:
1524 m_aAuthors
[m_aAuthors
.size()] = aName
;
1534 case Destination::DOCVAR
:
1536 m_aStates
.top().appendDocVar(rString
);
1539 case Destination::FONTTABLE
:
1540 case Destination::FONTENTRY
:
1541 case Destination::LEVELTEXT
:
1542 case Destination::SHAPEPROPERTYNAME
:
1543 case Destination::SHAPEPROPERTYVALUE
:
1544 case Destination::BOOKMARKEND
:
1545 case Destination::PICT
:
1546 case Destination::SHAPEPROPERTYVALUEPICT
:
1547 case Destination::FORMFIELDNAME
:
1548 case Destination::FORMFIELDLIST
:
1549 case Destination::DATAFIELD
:
1550 case Destination::AUTHOR
:
1551 case Destination::KEYWORDS
:
1552 case Destination::OPERATOR
:
1553 case Destination::COMPANY
:
1554 case Destination::COMMENT
:
1555 case Destination::OBJDATA
:
1556 case Destination::OBJCLASS
:
1557 case Destination::ANNOTATIONDATE
:
1558 case Destination::ANNOTATIONAUTHOR
:
1559 case Destination::ANNOTATIONREFERENCE
:
1560 case Destination::FALT
:
1561 case Destination::PARAGRAPHNUMBERING_TEXTAFTER
:
1562 case Destination::PARAGRAPHNUMBERING_TEXTBEFORE
:
1563 case Destination::TITLE
:
1564 case Destination::SUBJECT
:
1565 case Destination::DOCCOMM
:
1566 case Destination::ATNID
:
1567 case Destination::ANNOTATIONREFERENCESTART
:
1568 case Destination::ANNOTATIONREFERENCEEND
:
1569 case Destination::MR
:
1570 case Destination::MCHR
:
1571 case Destination::MPOS
:
1572 case Destination::MVERTJC
:
1573 case Destination::MSTRIKEH
:
1574 case Destination::MDEGHIDE
:
1575 case Destination::MBEGCHR
:
1576 case Destination::MSEPCHR
:
1577 case Destination::MENDCHR
:
1578 case Destination::MSUBHIDE
:
1579 case Destination::MSUPHIDE
:
1580 case Destination::MTYPE
:
1581 case Destination::MGROW
:
1582 case Destination::INDEXENTRY
:
1583 case Destination::TOCENTRY
:
1584 case Destination::PROPNAME
:
1585 case Destination::STATICVAL
:
1586 m_aStates
.top().appendDestinationText(rString
);
1588 case Destination::GENERATOR
:
1589 // don't enlarge space sequences, eg. it was saved in LibreOffice
1590 if (!rString
.startsWithIgnoreAsciiCase("Microsoft"))
1591 m_aSettingsTableSprms
.set(NS_ooxml::LN_CT_Settings_longerSpaceSequence
,
1601 if (!m_aIgnoreFirst
.isEmpty() && m_aIgnoreFirst
== rString
)
1603 m_aIgnoreFirst
.clear();
1607 // Are we in the middle of the table definition? (No cell defs yet, but we already have some cell props.)
1608 if (m_aStates
.top().getTableCellSprms().find(NS_ooxml::LN_CT_TcPrBase_vAlign
)
1609 && m_nTopLevelCells
== 0)
1611 m_aTableBufferStack
.back().emplace_back(BUFFER_UTEXT
, new RTFValue(rString
), nullptr);
1618 // Don't return earlier, a bookmark start has to be in a paragraph group.
1619 if (m_aStates
.top().getDestination() == Destination::BOOKMARKSTART
)
1621 m_aStates
.top().appendDestinationText(rString
);
1625 RTFBuffer_t
* pCurrentBuffer
= m_aStates
.top().getCurrentBuffer();
1627 if (!pCurrentBuffer
&& m_aStates
.top().getDestination() != Destination::FOOTNOTE
)
1628 Mapper().startCharacterGroup();
1629 else if (pCurrentBuffer
)
1631 RTFValue::Pointer_t pValue
;
1632 pCurrentBuffer
->push_back(Buf_t(BUFFER_STARTRUN
, pValue
, nullptr));
1635 if (m_aStates
.top().getDestination() == Destination::NORMAL
1636 || m_aStates
.top().getDestination() == Destination::FIELDRESULT
1637 || m_aStates
.top().getDestination() == Destination::SHAPETEXT
)
1640 if (!pCurrentBuffer
)
1641 Mapper().utext(rString
.getStr(), rString
.getLength());
1644 auto pValue
= new RTFValue(rString
);
1645 pCurrentBuffer
->push_back(Buf_t(BUFFER_UTEXT
, pValue
, nullptr));
1650 if (!pCurrentBuffer
&& m_aStates
.top().getDestination() != Destination::FOOTNOTE
)
1651 Mapper().endCharacterGroup();
1652 else if (pCurrentBuffer
)
1654 RTFValue::Pointer_t pValue
;
1655 pCurrentBuffer
->push_back(Buf_t(BUFFER_ENDRUN
, pValue
, nullptr));
1659 void RTFDocumentImpl::prepareProperties(
1660 RTFParserState
& rState
, writerfilter::Reference
<Properties
>::Pointer_t
& o_rpParagraphProperties
,
1661 writerfilter::Reference
<Properties
>::Pointer_t
& o_rpFrameProperties
,
1662 writerfilter::Reference
<Properties
>::Pointer_t
& o_rpTableRowProperties
, int const nCells
,
1663 int const nCurrentCellX
)
1665 o_rpParagraphProperties
1666 = getProperties(rState
.getParagraphAttributes(), rState
.getParagraphSprms(),
1667 NS_ooxml::LN_Value_ST_StyleType_paragraph
);
1669 if (rState
.getFrame().hasProperties())
1671 o_rpFrameProperties
= new RTFReferenceProperties(RTFSprms(), rState
.getFrame().getSprms());
1675 RTFValue::Pointer_t
const pTableWidthProps
1676 = rState
.getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblW
);
1677 if (!pTableWidthProps
)
1679 auto pUnitValue
= new RTFValue(3);
1680 putNestedAttribute(rState
.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblW
,
1681 NS_ooxml::LN_CT_TblWidth_type
, pUnitValue
);
1682 auto pWValue
= new RTFValue(nCurrentCellX
);
1683 putNestedAttribute(rState
.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblW
,
1684 NS_ooxml::LN_CT_TblWidth_w
, pWValue
);
1688 rState
.getTableRowSprms().set(NS_ooxml::LN_tblRow
, new RTFValue(1));
1690 RTFValue::Pointer_t
const pCellMar
1691 = rState
.getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblCellMar
);
1694 // If no cell margins are defined, the default left/right margin is 0 in Word, but not in Writer.
1695 RTFSprms aAttributes
;
1696 aAttributes
.set(NS_ooxml::LN_CT_TblWidth_type
,
1697 new RTFValue(NS_ooxml::LN_Value_ST_TblWidth_dxa
));
1698 aAttributes
.set(NS_ooxml::LN_CT_TblWidth_w
, new RTFValue(0));
1699 putNestedSprm(rState
.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblCellMar
,
1700 NS_ooxml::LN_CT_TblCellMar_left
, new RTFValue(aAttributes
));
1701 putNestedSprm(rState
.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblCellMar
,
1702 NS_ooxml::LN_CT_TblCellMar_right
, new RTFValue(aAttributes
));
1705 o_rpTableRowProperties
1706 = new RTFReferenceProperties(rState
.getTableRowAttributes(), rState
.getTableRowSprms());
1709 void RTFDocumentImpl::sendProperties(
1710 writerfilter::Reference
<Properties
>::Pointer_t
const& pParagraphProperties
,
1711 writerfilter::Reference
<Properties
>::Pointer_t
const& pFrameProperties
,
1712 writerfilter::Reference
<Properties
>::Pointer_t
const& pTableRowProperties
)
1714 Mapper().props(pParagraphProperties
);
1716 if (pFrameProperties
)
1718 Mapper().props(pFrameProperties
);
1721 Mapper().props(pTableRowProperties
);
1726 void RTFDocumentImpl::replayRowBuffer(RTFBuffer_t
& rBuffer
, ::std::deque
<RTFSprms
>& rCellsSprms
,
1727 ::std::deque
<RTFSprms
>& rCellsAttributes
, int const nCells
)
1729 for (int i
= 0; i
< nCells
; ++i
)
1731 replayBuffer(rBuffer
, &rCellsSprms
.front(), &rCellsAttributes
.front());
1732 rCellsSprms
.pop_front();
1733 rCellsAttributes
.pop_front();
1735 for (Buf_t
& i
: rBuffer
)
1737 SAL_WARN_IF(BUFFER_CELLEND
== std::get
<0>(i
), "writerfilter.rtf", "dropping table cell!");
1739 assert(rCellsSprms
.empty());
1740 assert(rCellsAttributes
.empty());
1743 void RTFDocumentImpl::replayBuffer(RTFBuffer_t
& rBuffer
, RTFSprms
* const pSprms
,
1744 RTFSprms
const* const pAttributes
)
1746 while (!rBuffer
.empty())
1748 Buf_t
aTuple(rBuffer
.front());
1749 rBuffer
.pop_front();
1750 if (std::get
<0>(aTuple
) == BUFFER_PROPS
|| std::get
<0>(aTuple
) == BUFFER_PROPS_CHAR
)
1752 // Construct properties via getProperties() and not directly, to take care of deduplication.
1753 writerfilter::Reference
<Properties
>::Pointer_t
const pProp(getProperties(
1754 std::get
<1>(aTuple
)->getAttributes(), std::get
<1>(aTuple
)->getSprms(),
1755 std::get
<0>(aTuple
) == BUFFER_PROPS_CHAR
? NS_ooxml::LN_Value_ST_StyleType_character
1757 Mapper().props(pProp
);
1759 else if (std::get
<0>(aTuple
) == BUFFER_NESTROW
)
1761 TableRowBuffer
& rRowBuffer(*std::get
<2>(aTuple
));
1763 replayRowBuffer(rRowBuffer
.GetBuffer(), rRowBuffer
.GetCellsSprms(),
1764 rRowBuffer
.GetCellsAttributes(), rRowBuffer
.GetCells());
1766 sendProperties(rRowBuffer
.GetParaProperties(), rRowBuffer
.GetFrameProperties(),
1767 rRowBuffer
.GetRowProperties());
1769 else if (std::get
<0>(aTuple
) == BUFFER_CELLEND
)
1771 assert(pSprms
&& pAttributes
);
1772 auto pValue
= new RTFValue(1);
1773 pSprms
->set(NS_ooxml::LN_tblCell
, pValue
);
1774 writerfilter::Reference
<Properties
>::Pointer_t
const pTableCellProperties(
1775 new RTFReferenceProperties(*pAttributes
, *pSprms
));
1776 Mapper().props(pTableCellProperties
);
1780 else if (std::get
<0>(aTuple
) == BUFFER_STARTRUN
)
1781 Mapper().startCharacterGroup();
1782 else if (std::get
<0>(aTuple
) == BUFFER_TEXT
)
1784 sal_uInt8
const nValue
= std::get
<1>(aTuple
)->getInt();
1785 Mapper().text(&nValue
, 1);
1787 else if (std::get
<0>(aTuple
) == BUFFER_UTEXT
)
1789 OUString
const aString(std::get
<1>(aTuple
)->getString());
1790 Mapper().utext(aString
.getStr(), aString
.getLength());
1792 else if (std::get
<0>(aTuple
) == BUFFER_ENDRUN
)
1793 Mapper().endCharacterGroup();
1794 else if (std::get
<0>(aTuple
) == BUFFER_PAR
)
1796 else if (std::get
<0>(aTuple
) == BUFFER_STARTSHAPE
)
1797 m_pSdrImport
->resolve(std::get
<1>(aTuple
)->getShape(), false, RTFSdrImport::SHAPE
);
1798 else if (std::get
<0>(aTuple
) == BUFFER_RESOLVESHAPE
)
1800 // Make sure there is no current buffer while replaying the shape,
1801 // otherwise it gets re-buffered.
1802 RTFBuffer_t
* pCurrentBuffer
= m_aStates
.top().getCurrentBuffer();
1803 m_aStates
.top().setCurrentBuffer(nullptr);
1805 // Set current shape during replay, needed by e.g. wrap in
1807 RTFShape aShape
= m_aStates
.top().getShape();
1808 m_aStates
.top().getShape() = std::get
<1>(aTuple
)->getShape();
1810 m_pSdrImport
->resolve(std::get
<1>(aTuple
)->getShape(), true, RTFSdrImport::SHAPE
);
1811 m_aStates
.top().getShape() = aShape
;
1812 m_aStates
.top().setCurrentBuffer(pCurrentBuffer
);
1814 else if (std::get
<0>(aTuple
) == BUFFER_ENDSHAPE
)
1815 m_pSdrImport
->close();
1816 else if (std::get
<0>(aTuple
) == BUFFER_RESOLVESUBSTREAM
)
1818 RTFSprms
& rAttributes
= std::get
<1>(aTuple
)->getAttributes();
1819 std::size_t nPos
= rAttributes
.find(0)->getInt();
1820 Id nId
= rAttributes
.find(1)->getInt();
1821 OUString aCustomMark
= rAttributes
.find(2)->getString();
1822 resolveSubstream(nPos
, nId
, aCustomMark
);
1824 else if (std::get
<0>(aTuple
) == BUFFER_PICTURE
)
1825 m_aStates
.top().getPicture() = std::get
<1>(aTuple
)->getPicture();
1826 else if (std::get
<0>(aTuple
) == BUFFER_SETSTYLE
)
1828 if (!m_aStates
.empty())
1829 m_aStates
.top().setCurrentStyleIndex(std::get
<1>(aTuple
)->getInt());
1836 bool findPropertyName(const std::vector
<beans::PropertyValue
>& rProperties
, const OUString
& rName
)
1839 rProperties
.begin(), rProperties
.end(),
1840 [&rName
](const beans::PropertyValue
& rProperty
) { return rProperty
.Name
== rName
; });
1843 void RTFDocumentImpl::backupTableRowProperties()
1845 if (m_nTopLevelCurrentCellX
)
1847 m_aBackupTableRowSprms
= m_aStates
.top().getTableRowSprms();
1848 m_aBackupTableRowAttributes
= m_aStates
.top().getTableRowAttributes();
1849 m_nBackupTopLevelCurrentCellX
= m_nTopLevelCurrentCellX
;
1853 void RTFDocumentImpl::restoreTableRowProperties()
1855 m_aStates
.top().getTableRowSprms() = m_aBackupTableRowSprms
;
1856 m_aStates
.top().getTableRowAttributes() = m_aBackupTableRowAttributes
;
1857 m_nTopLevelCurrentCellX
= m_nBackupTopLevelCurrentCellX
;
1860 void RTFDocumentImpl::resetTableRowProperties()
1862 m_aStates
.top().getTableRowSprms() = m_aDefaultState
.getTableRowSprms();
1863 m_aStates
.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol
, new RTFValue(-1),
1864 RTFOverwrite::NO_APPEND
);
1865 m_aStates
.top().getTableRowAttributes() = m_aDefaultState
.getTableRowAttributes();
1866 if (Destination::NESTEDTABLEPROPERTIES
== m_aStates
.top().getDestination())
1868 m_nNestedTRLeft
= 0;
1869 m_nNestedCurrentCellX
= 0;
1873 m_nTopLevelTRLeft
= 0;
1874 m_nTopLevelCurrentCellX
= 0;
1878 RTFError
RTFDocumentImpl::dispatchToggle(RTFKeyword nKeyword
, bool bParam
, int nParam
)
1881 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
1882 RTFSkipDestination
aSkip(*this);
1884 tools::SvRef
<RTFValue
> pBoolValue(new RTFValue(int(!bParam
|| nParam
!= 0)));
1886 // Underline toggles.
1889 case RTFKeyword::UL
:
1890 nSprm
= NS_ooxml::LN_Value_ST_Underline_single
;
1892 case RTFKeyword::ULDASH
:
1893 nSprm
= NS_ooxml::LN_Value_ST_Underline_dash
;
1895 case RTFKeyword::ULDASHD
:
1896 nSprm
= NS_ooxml::LN_Value_ST_Underline_dotDash
;
1898 case RTFKeyword::ULDASHDD
:
1899 nSprm
= NS_ooxml::LN_Value_ST_Underline_dotDotDash
;
1901 case RTFKeyword::ULDB
:
1902 nSprm
= NS_ooxml::LN_Value_ST_Underline_double
;
1904 case RTFKeyword::ULHWAVE
:
1905 nSprm
= NS_ooxml::LN_Value_ST_Underline_wavyHeavy
;
1907 case RTFKeyword::ULLDASH
:
1908 nSprm
= NS_ooxml::LN_Value_ST_Underline_dashLong
;
1910 case RTFKeyword::ULTH
:
1911 nSprm
= NS_ooxml::LN_Value_ST_Underline_thick
;
1913 case RTFKeyword::ULTHD
:
1914 nSprm
= NS_ooxml::LN_Value_ST_Underline_dottedHeavy
;
1916 case RTFKeyword::ULTHDASH
:
1917 nSprm
= NS_ooxml::LN_Value_ST_Underline_dashedHeavy
;
1919 case RTFKeyword::ULTHDASHD
:
1920 nSprm
= NS_ooxml::LN_Value_ST_Underline_dashDotHeavy
;
1922 case RTFKeyword::ULTHDASHDD
:
1923 nSprm
= NS_ooxml::LN_Value_ST_Underline_dashDotDotHeavy
;
1925 case RTFKeyword::ULTHLDASH
:
1926 nSprm
= NS_ooxml::LN_Value_ST_Underline_dashLongHeavy
;
1928 case RTFKeyword::ULULDBWAVE
:
1929 nSprm
= NS_ooxml::LN_Value_ST_Underline_wavyDouble
;
1931 case RTFKeyword::ULWAVE
:
1932 nSprm
= NS_ooxml::LN_Value_ST_Underline_wave
;
1940 = new RTFValue((!bParam
|| nParam
!= 0) ? nSprm
: NS_ooxml::LN_Value_ST_Underline_none
);
1941 m_aStates
.top().getCharacterAttributes().set(NS_ooxml::LN_CT_Underline_val
, pValue
);
1942 return RTFError::OK
;
1945 // Accent characters (over dot / over comma).
1948 case RTFKeyword::ACCNONE
:
1949 nSprm
= NS_ooxml::LN_Value_ST_Em_none
;
1951 case RTFKeyword::ACCDOT
:
1952 nSprm
= NS_ooxml::LN_Value_ST_Em_dot
;
1954 case RTFKeyword::ACCCOMMA
:
1955 nSprm
= NS_ooxml::LN_Value_ST_Em_comma
;
1957 case RTFKeyword::ACCCIRCLE
:
1958 nSprm
= NS_ooxml::LN_Value_ST_Em_circle
;
1960 case RTFKeyword::ACCUNDERDOT
:
1961 nSprm
= NS_ooxml::LN_Value_ST_Em_underDot
;
1968 auto pValue
= new RTFValue((!bParam
|| nParam
!= 0) ? nSprm
: 0);
1969 m_aStates
.top().getCharacterSprms().set(NS_ooxml::LN_EG_RPrBase_em
, pValue
);
1970 return RTFError::OK
;
1973 // Trivial character sprms.
1977 case RTFKeyword::AB
:
1978 switch (m_aStates
.top().getRunType())
1980 case RTFParserState::RunType::HICH
:
1981 case RTFParserState::RunType::RTLCH_LTRCH_1
:
1982 case RTFParserState::RunType::LTRCH_RTLCH_2
:
1983 nSprm
= NS_ooxml::LN_EG_RPrBase_bCs
;
1985 case RTFParserState::RunType::NONE
:
1986 case RTFParserState::RunType::LOCH
:
1987 case RTFParserState::RunType::LTRCH_RTLCH_1
:
1988 case RTFParserState::RunType::RTLCH_LTRCH_2
:
1989 case RTFParserState::RunType::DBCH
:
1991 nSprm
= NS_ooxml::LN_EG_RPrBase_b
;
1996 case RTFKeyword::AI
:
1997 switch (m_aStates
.top().getRunType())
1999 case RTFParserState::RunType::HICH
:
2000 case RTFParserState::RunType::RTLCH_LTRCH_1
:
2001 case RTFParserState::RunType::LTRCH_RTLCH_2
:
2002 nSprm
= NS_ooxml::LN_EG_RPrBase_iCs
;
2004 case RTFParserState::RunType::NONE
:
2005 case RTFParserState::RunType::LOCH
:
2006 case RTFParserState::RunType::LTRCH_RTLCH_1
:
2007 case RTFParserState::RunType::RTLCH_LTRCH_2
:
2008 case RTFParserState::RunType::DBCH
:
2010 nSprm
= NS_ooxml::LN_EG_RPrBase_i
;
2014 case RTFKeyword::OUTL
:
2015 nSprm
= NS_ooxml::LN_EG_RPrBase_outline
;
2017 case RTFKeyword::SHAD
:
2018 nSprm
= NS_ooxml::LN_EG_RPrBase_shadow
;
2021 nSprm
= NS_ooxml::LN_EG_RPrBase_vanish
;
2023 case RTFKeyword::STRIKE
:
2024 nSprm
= NS_ooxml::LN_EG_RPrBase_strike
;
2026 case RTFKeyword::STRIKED
:
2027 nSprm
= NS_ooxml::LN_EG_RPrBase_dstrike
;
2029 case RTFKeyword::SCAPS
:
2030 nSprm
= NS_ooxml::LN_EG_RPrBase_smallCaps
;
2032 case RTFKeyword::IMPR
:
2033 nSprm
= NS_ooxml::LN_EG_RPrBase_imprint
;
2035 case RTFKeyword::CAPS
:
2036 nSprm
= NS_ooxml::LN_EG_RPrBase_caps
;
2043 if (m_aStates
.top().getDestination() == Destination::LISTLEVEL
)
2045 m_aStates
.top().getTableSprms().set(nSprm
, pBoolValue
);
2049 m_aStates
.top().getCharacterSprms().set(nSprm
, pBoolValue
);
2051 return RTFError::OK
;
2056 case RTFKeyword::ASPALPHA
:
2057 m_aStates
.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_autoSpaceDE
,
2060 case RTFKeyword::DELETED
:
2061 case RTFKeyword::REVISED
:
2064 = new RTFValue(nKeyword
== RTFKeyword::DELETED
? oox::XML_del
: oox::XML_ins
);
2065 putNestedAttribute(m_aStates
.top().getCharacterSprms(), NS_ooxml::LN_trackchange
,
2066 NS_ooxml::LN_token
, pValue
);
2069 case RTFKeyword::SBAUTO
:
2070 putNestedAttribute(m_aStates
.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_spacing
,
2071 NS_ooxml::LN_CT_Spacing_beforeAutospacing
, pBoolValue
);
2073 case RTFKeyword::SAAUTO
:
2074 putNestedAttribute(m_aStates
.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_spacing
,
2075 NS_ooxml::LN_CT_Spacing_afterAutospacing
, pBoolValue
);
2077 case RTFKeyword::FACINGP
:
2078 m_aSettingsTableSprms
.set(NS_ooxml::LN_CT_Settings_evenAndOddHeaders
, pBoolValue
);
2080 case RTFKeyword::HYPHAUTO
:
2081 m_aSettingsTableSprms
.set(NS_ooxml::LN_CT_Settings_autoHyphenation
, pBoolValue
);
2083 case RTFKeyword::HYPHPAR
:
2084 m_aStates
.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_suppressAutoHyphens
,
2085 new RTFValue(int(bParam
&& nParam
== 0)));
2089 SAL_INFO("writerfilter.rtf",
2090 "TODO handle toggle '" << keywordToString(nKeyword
) << "'");
2091 aSkip
.setParsed(false);
2095 return RTFError::OK
;
2098 RTFError
RTFDocumentImpl::pushState()
2100 //SAL_INFO("writerfilter.rtf", __func__ << " before push: " << m_pTokenizer->getGroup());
2102 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
2103 m_nGroupStartPos
= Strm().Tell();
2105 if (m_aStates
.empty())
2106 m_aStates
.push(m_aDefaultState
);
2109 // fdo#85812 group resets run type of _current_ and new state (but not RTL)
2110 if (m_aStates
.top().getRunType() != RTFParserState::RunType::LTRCH_RTLCH_2
2111 && m_aStates
.top().getRunType() != RTFParserState::RunType::RTLCH_LTRCH_2
)
2113 m_aStates
.top().setRunType(RTFParserState::RunType::NONE
);
2116 if (m_aStates
.top().getDestination() == Destination::MR
)
2117 lcl_DestinationToMath(m_aStates
.top().getCurrentDestinationText(), m_aMathBuffer
,
2119 m_aStates
.push(m_aStates
.top());
2121 m_aStates
.top().getDestinationText().setLength(0); // was copied: always reset!
2123 m_pTokenizer
->pushGroup();
2125 switch (m_aStates
.top().getDestination())
2127 case Destination::FONTTABLE
:
2128 // this is a "faked" destination for the font entry
2129 m_aStates
.top().setCurrentDestinationText(&m_aStates
.top().getDestinationText());
2130 m_aStates
.top().setDestination(Destination::FONTENTRY
);
2132 case Destination::STYLESHEET
:
2133 // this is a "faked" destination for the style sheet entry
2134 m_aStates
.top().setCurrentDestinationText(&m_aStates
.top().getDestinationText());
2135 m_aStates
.top().setDestination(Destination::STYLEENTRY
);
2137 // the *default* is \s0 i.e. paragraph style default
2138 // this will be overwritten by \sN \csN \dsN \tsN
2139 m_nCurrentStyleIndex
= 0;
2140 auto pValue
= new RTFValue(NS_ooxml::LN_Value_ST_StyleType_paragraph
);
2141 m_aStates
.top().getTableAttributes().set(NS_ooxml::LN_CT_Style_type
, pValue
);
2144 case Destination::FIELDRESULT
:
2145 case Destination::SHAPETEXT
:
2146 case Destination::FORMFIELD
:
2147 //TODO: if this is pushed then the font encoding is used which results in a broken command string
2148 // if it is not pushed to NORMAL then it is not restored in time.
2149 case Destination::FIELDINSTRUCTION
:
2150 case Destination::PICT
:
2151 m_aStates
.top().setDestination(Destination::NORMAL
);
2153 case Destination::MNUM
:
2154 case Destination::MDEN
:
2155 case Destination::ME
:
2156 case Destination::MFNAME
:
2157 case Destination::MLIM
:
2158 case Destination::MSUB
:
2159 case Destination::MSUP
:
2160 case Destination::MDEG
:
2161 case Destination::MOMATH
:
2162 m_aStates
.top().setDestination(Destination::MR
);
2164 case Destination::REVISIONTABLE
:
2165 // this is a "faked" destination for the revision table entry
2166 m_aStates
.top().setCurrentDestinationText(&m_aStates
.top().getDestinationText());
2167 m_aStates
.top().setDestination(Destination::REVISIONENTRY
);
2173 // If this is true, then ooxml:endtrackchange will be generated. Make sure
2174 // we don't generate more ooxml:endtrackchange than ooxml:trackchange: new
2175 // state does not inherit this flag.
2176 m_aStates
.top().setStartedTrackchange(false);
2178 return RTFError::OK
;
2181 writerfilter::Reference
<Properties
>::Pointer_t
RTFDocumentImpl::createStyleProperties()
2184 RTFValue::Pointer_t pBasedOn
2185 = m_aStates
.top().getTableSprms().find(NS_ooxml::LN_CT_Style_basedOn
);
2187 nBasedOn
= pBasedOn
->getInt();
2190 // No parent style, then mimic what Word does: ignore attributes which
2191 // would set a margin as formatting, but with a default value.
2192 for (const auto& nId
:
2193 { NS_ooxml::LN_CT_Ind_firstLine
, NS_ooxml::LN_CT_Ind_left
, NS_ooxml::LN_CT_Ind_right
,
2194 NS_ooxml::LN_CT_Ind_start
, NS_ooxml::LN_CT_Ind_end
})
2196 RTFValue::Pointer_t pValue
= getNestedAttribute(m_aStates
.top().getParagraphSprms(),
2197 NS_ooxml::LN_CT_PPrBase_ind
, nId
);
2198 if (pValue
&& pValue
->getInt() == 0)
2199 eraseNestedAttribute(m_aStates
.top().getParagraphSprms(),
2200 NS_ooxml::LN_CT_PPrBase_ind
, nId
);
2204 RTFValue::Pointer_t pParaProps
= new RTFValue(m_aStates
.top().getParagraphAttributes(),
2205 m_aStates
.top().getParagraphSprms());
2206 RTFValue::Pointer_t pCharProps
= new RTFValue(m_aStates
.top().getCharacterAttributes(),
2207 m_aStates
.top().getCharacterSprms());
2209 // resetSprms will clean up this modification
2210 m_aStates
.top().getTableSprms().set(NS_ooxml::LN_CT_Style_pPr
, pParaProps
);
2211 m_aStates
.top().getTableSprms().set(NS_ooxml::LN_CT_Style_rPr
, pCharProps
);
2213 writerfilter::Reference
<Properties
>::Pointer_t
pProps(new RTFReferenceProperties(
2214 m_aStates
.top().getTableAttributes(), m_aStates
.top().getTableSprms()));
2218 /** 2 different representations of the styles are needed:
2220 1) flat content, as read from the input file:
2221 stored in m_pStyleTableEntries, used as reference input for
2222 deduplication both here and for hard formatting in getProperties()
2224 2) real content, with proper override of sprms/attributes where it differs
2225 from parent style; this is produced here and sent to domain mapper
2227 RTFReferenceTable::Entries_t
RTFDocumentImpl::deduplicateStyleTable()
2229 RTFReferenceTable::Entries_t ret
;
2230 for (auto const& it
: *m_pStyleTableEntries
)
2232 auto pStyle
= it
.second
;
2233 ret
[it
.first
] = pStyle
;
2234 // ugly downcasts here, but can't easily replace the members with
2235 // RTFReferenceProperties because dmapper wants SvRef<Properties> anyway
2236 RTFValue::Pointer_t
const pBasedOn(
2237 static_cast<RTFReferenceProperties
&>(*pStyle
).getSprms().find(
2238 NS_ooxml::LN_CT_Style_basedOn
));
2241 int const nBasedOn(pBasedOn
->getInt());
2242 // don't deduplicate yourself - especially a potential problem for the default style.
2243 if (it
.first
== nBasedOn
)
2246 auto const itParent(m_pStyleTableEntries
->find(nBasedOn
)); // definition as read!
2247 if (itParent
!= m_pStyleTableEntries
->end())
2249 auto const pStyleType(
2250 static_cast<RTFReferenceProperties
&>(*pStyle
).getAttributes().find(
2251 NS_ooxml::LN_CT_Style_type
));
2253 int const nStyleType(pStyleType
->getInt());
2255 static_cast<RTFReferenceProperties
&>(*pStyle
).getSprms().cloneAndDeduplicate(
2256 static_cast<RTFReferenceProperties
&>(*itParent
->second
).getSprms(),
2258 RTFSprms
attributes(
2259 static_cast<RTFReferenceProperties
&>(*pStyle
)
2261 .cloneAndDeduplicate(
2262 static_cast<RTFReferenceProperties
&>(*itParent
->second
).getAttributes(),
2265 ret
[it
.first
] = new RTFReferenceProperties(std::move(attributes
), std::move(sprms
));
2269 SAL_WARN("writerfilter.rtf", "parent style not found: " << nBasedOn
);
2273 assert(ret
.size() == m_pStyleTableEntries
->size());
2277 void RTFDocumentImpl::resetSprms()
2279 m_aStates
.top().getTableSprms().clear();
2280 m_aStates
.top().getCharacterSprms().clear();
2281 m_aStates
.top().getParagraphSprms().clear();
2284 void RTFDocumentImpl::resetAttributes()
2286 m_aStates
.top().getTableAttributes().clear();
2287 m_aStates
.top().getCharacterAttributes().clear();
2288 m_aStates
.top().getParagraphAttributes().clear();
2291 static bool lcl_containsProperty(const uno::Sequence
<beans::Property
>& rProperties
,
2292 std::u16string_view rName
)
2294 return std::any_of(rProperties
.begin(), rProperties
.end(),
2295 [&](const beans::Property
& rProperty
) { return rProperty
.Name
== rName
; });
2298 RTFError
RTFDocumentImpl::beforePopState(RTFParserState
& rState
)
2300 switch (rState
.getDestination())
2302 //Note: in fonttbl there may or may not be groups, so process it as no groups
2303 case Destination::FONTTABLE
:
2304 case Destination::FONTENTRY
:
2306 // Some text unhandled? Seems it is last font name
2307 if (m_aStates
.top().getCurrentDestinationText()->getLength())
2308 handleFontTableEntry();
2310 if (rState
.getDestination() == Destination::FONTTABLE
)
2312 writerfilter::Reference
<Table
>::Pointer_t
const pTable(
2313 new RTFReferenceTable(m_aFontTableEntries
));
2314 Mapper().table(NS_ooxml::LN_FONTTABLE
, pTable
);
2315 if (m_nDefaultFontIndex
>= 0)
2317 auto pValue
= new RTFValue(m_aFontNames
[getFontIndex(m_nDefaultFontIndex
)]);
2318 putNestedAttribute(m_aDefaultState
.getCharacterSprms(),
2319 NS_ooxml::LN_EG_RPrBase_rFonts
, NS_ooxml::LN_CT_Fonts_ascii
,
2325 case Destination::STYLESHEET
:
2327 RTFReferenceTable::Entries_t
pStyleTableDeduplicated(deduplicateStyleTable());
2328 writerfilter::Reference
<Table
>::Pointer_t
const pTable(
2329 new RTFReferenceTable(std::move(pStyleTableDeduplicated
)));
2330 Mapper().table(NS_ooxml::LN_STYLESHEET
, pTable
);
2333 case Destination::LISTOVERRIDETABLE
:
2335 RTFSprms aListTableAttributes
;
2336 writerfilter::Reference
<Properties
>::Pointer_t pProp
2337 = new RTFReferenceProperties(std::move(aListTableAttributes
), m_aListTableSprms
);
2338 RTFReferenceTable::Entries_t aListTableEntries
;
2339 aListTableEntries
.insert(std::make_pair(0, pProp
));
2340 writerfilter::Reference
<Table
>::Pointer_t
const pTable(
2341 new RTFReferenceTable(std::move(aListTableEntries
)));
2342 Mapper().table(NS_ooxml::LN_NUMBERING
, pTable
);
2345 case Destination::LISTENTRY
:
2346 for (const auto& rListLevelEntry
: rState
.getListLevelEntries())
2347 rState
.getTableSprms().set(rListLevelEntry
.first
, rListLevelEntry
.second
,
2348 RTFOverwrite::NO_APPEND
);
2350 case Destination::FIELDINSTRUCTION
:
2352 auto pValue
= new RTFValue(m_aFormfieldAttributes
, m_aFormfieldSprms
);
2353 RTFSprms aFFAttributes
;
2355 aFFSprms
.set(NS_ooxml::LN_ffdata
, pValue
);
2356 if (!m_aStates
.top().getCurrentBuffer())
2358 writerfilter::Reference
<Properties
>::Pointer_t pProperties
2359 = new RTFReferenceProperties(std::move(aFFAttributes
), std::move(aFFSprms
));
2360 Mapper().props(pProperties
);
2364 auto pFFValue
= new RTFValue(aFFAttributes
, aFFSprms
);
2365 bufferProperties(*m_aStates
.top().getCurrentBuffer(), pFFValue
, nullptr);
2367 m_aFormfieldAttributes
.clear();
2368 m_aFormfieldSprms
.clear();
2370 if (m_aStates
.top().isFieldLocked())
2371 singleChar(cFieldLock
);
2372 singleChar(cFieldSep
, true);
2375 case Destination::FIELDRESULT
:
2376 singleChar(cFieldEnd
);
2378 if (!m_aPicturePath
.isEmpty())
2380 // Read the picture into m_aStates.top().aDestinationText.
2382 dispatchDestination(RTFKeyword::PICT
);
2383 if (m_aPicturePath
.endsWith(".png"))
2384 dispatchFlag(RTFKeyword::PNGBLIP
);
2385 OUString aFileURL
= m_rMediaDescriptor
.getUnpackedValueOrDefault(
2386 utl::MediaDescriptor::PROP_URL
, OUString());
2387 OUString aPictureURL
;
2390 aPictureURL
= rtl::Uri::convertRelToAbs(aFileURL
, m_aPicturePath
);
2392 catch (const rtl::MalformedUriException
& rException
)
2394 SAL_WARN("writerfilter.rtf",
2395 "rtl::Uri::convertRelToAbs() failed: " << rException
.getMessage());
2398 if (!aPictureURL
.isEmpty())
2400 SvFileStream
aStream(aPictureURL
, StreamMode::READ
);
2401 if (aStream
.IsOpen())
2403 OUStringBuffer aBuf
;
2404 while (aStream
.good())
2406 unsigned char ch
= 0;
2407 aStream
.ReadUChar(ch
);
2410 aBuf
.append(static_cast<sal_Int32
>(ch
), 16);
2412 m_aStates
.top().getDestinationText() = aBuf
;
2416 m_aPicturePath
.clear();
2420 case Destination::LEVELTEXT
:
2422 if (&m_aStates
.top().getDestinationText()
2423 != m_aStates
.top().getCurrentDestinationText())
2424 break; // not for nested group
2425 OUString aStr
= m_aStates
.top().getCurrentDestinationText()->makeStringAndClear();
2427 // The first character is the length of the string (the rest should be ignored).
2428 sal_Int32
nLength(aStr
.toChar());
2430 if (nLength
< aStr
.getLength())
2431 aValue
= aStr
.copy(1, nLength
);
2434 auto pValue
= new RTFValue(aValue
, true);
2435 rState
.getTableAttributes().set(NS_ooxml::LN_CT_LevelText_val
, pValue
);
2438 case Destination::LEVELNUMBERS
:
2440 bool bNestedLevelNumbers
= false;
2441 if (m_aStates
.size() > 1)
2442 // Current destination is levelnumbers and parent destination is levelnumbers as well.
2444 = m_aStates
[m_aStates
.size() - 2].getDestination() == Destination::LEVELNUMBERS
;
2445 if (!bNestedLevelNumbers
&& rState
.getTableSprms().find(NS_ooxml::LN_CT_Lvl_lvlText
))
2447 RTFSprms
& rAttributes
2448 = rState
.getTableSprms().find(NS_ooxml::LN_CT_Lvl_lvlText
)->getAttributes();
2449 RTFValue::Pointer_t pValue
= rAttributes
.find(NS_ooxml::LN_CT_LevelText_val
);
2450 if (pValue
&& rState
.getLevelNumbersValid())
2452 OUString aOrig
= pValue
->getString();
2454 OUStringBuffer
aBuf(aOrig
.getLength() * 2);
2455 sal_Int32 nReplaces
= 1;
2456 for (int i
= 0; i
< aOrig
.getLength(); i
++)
2458 if (std::find(rState
.getLevelNumbers().begin(),
2459 rState
.getLevelNumbers().end(), i
+ 1)
2460 != rState
.getLevelNumbers().end())
2463 // '1.1.1' -> '%1.%2.%3', but '1.' (with '2.' prefix omitted) is %2.
2464 aBuf
.append(sal_Int32(nReplaces
++ + rState
.getListLevelNum() + 1
2465 - rState
.getLevelNumbers().size()));
2468 aBuf
.append(aOrig
[i
]);
2471 pValue
->setString(aBuf
.makeStringAndClear());
2474 // Have a value, but levelnumbers is not valid -> ignore it.
2475 pValue
->setString(OUString());
2479 case Destination::SHAPEPROPERTYNAME
:
2480 if (&m_aStates
.top().getDestinationText()
2481 != m_aStates
.top().getCurrentDestinationText())
2482 break; // not for nested group
2483 rState
.getShape().getProperties().emplace_back(
2484 m_aStates
.top().getCurrentDestinationText()->makeStringAndClear(), OUString());
2486 case Destination::SHAPEPROPERTYVALUE
:
2487 if (!rState
.getShape().getProperties().empty())
2489 rState
.getShape().getProperties().back().second
2490 = m_aStates
.top().getCurrentDestinationText()->makeStringAndClear();
2491 if (m_aStates
.top().getHadShapeText())
2492 m_pSdrImport
->append(rState
.getShape().getProperties().back().first
,
2493 rState
.getShape().getProperties().back().second
);
2494 else if (rState
.getInShapeGroup() && !rState
.getInShape()
2495 && rState
.getShape().getProperties().back().first
== "rotation")
2497 // Rotation should be applied on the groupshape itself, not on each shape.
2498 rState
.getShape().getGroupProperties().push_back(
2499 rState
.getShape().getProperties().back());
2500 rState
.getShape().getProperties().pop_back();
2504 case Destination::PICPROP
:
2505 case Destination::SHAPEINSTRUCTION
:
2506 if (m_aStates
.size() > 1
2507 && m_aStates
[m_aStates
.size() - 2].getDestination()
2508 == Destination::SHAPEINSTRUCTION
)
2510 // Do not resolve shape if shape instruction destination is inside other shape instruction
2512 else if (!m_bObject
&& !rState
.getInListpicture() && !rState
.getHadShapeText()
2513 && (!rState
.getInShapeGroup() || rState
.getInShape()))
2515 // Don't trigger a shape import in case we're only leaving the \shpinst of the groupshape itself.
2516 RTFSdrImport::ShapeOrPict eType
2517 = (rState
.getDestination() == Destination::SHAPEINSTRUCTION
)
2518 ? RTFSdrImport::SHAPE
2519 : RTFSdrImport::PICT
;
2520 if (!m_aStates
.top().getCurrentBuffer() || eType
!= RTFSdrImport::SHAPE
)
2521 m_pSdrImport
->resolve(m_aStates
.top().getShape(), true, eType
);
2524 // Shape inside table: buffer the import to have correct anchor position.
2525 // Also buffer the RTFPicture of the state stack as it contains
2527 auto pPictureValue
= new RTFValue(m_aStates
.top().getPicture());
2528 m_aStates
.top().getCurrentBuffer()->push_back(
2529 Buf_t(BUFFER_PICTURE
, pPictureValue
, nullptr));
2530 auto pValue
= new RTFValue(m_aStates
.top().getShape());
2532 // Buffer wrap type.
2533 for (const auto& rCharacterSprm
: m_aStates
.top().getCharacterSprms())
2535 if (rCharacterSprm
.first
== NS_ooxml::LN_EG_WrapType_wrapNone
2536 || rCharacterSprm
.first
== NS_ooxml::LN_EG_WrapType_wrapTight
)
2538 m_aStates
.top().getShape().getWrapSprm() = rCharacterSprm
;
2543 m_aStates
.top().getCurrentBuffer()->push_back(
2544 Buf_t(BUFFER_RESOLVESHAPE
, pValue
, nullptr));
2547 else if (rState
.getInShapeGroup() && !rState
.getInShape())
2549 // End of a groupshape, as we're in shapegroup, but not in a real shape.
2550 for (const auto& rGroupProperty
: rState
.getShape().getGroupProperties())
2551 m_pSdrImport
->appendGroupProperty(rGroupProperty
.first
, rGroupProperty
.second
);
2552 rState
.getShape().getGroupProperties().clear();
2555 case Destination::BOOKMARKSTART
:
2557 if (&m_aStates
.top().getDestinationText()
2558 != m_aStates
.top().getCurrentDestinationText())
2559 break; // not for nested group
2560 OUString aStr
= m_aStates
.top().getCurrentDestinationText()->makeStringAndClear();
2561 int nPos
= m_aBookmarks
.size();
2562 m_aBookmarks
[aStr
] = nPos
;
2563 if (!m_aStates
.top().getCurrentBuffer())
2564 Mapper().props(new RTFReferenceProperties(lcl_getBookmarkProperties(nPos
, aStr
)));
2566 bufferProperties(*m_aStates
.top().getCurrentBuffer(),
2567 new RTFValue(lcl_getBookmarkProperties(nPos
, aStr
)), nullptr);
2570 case Destination::BOOKMARKEND
:
2572 if (&m_aStates
.top().getDestinationText()
2573 != m_aStates
.top().getCurrentDestinationText())
2574 break; // not for nested group
2575 OUString aStr
= m_aStates
.top().getCurrentDestinationText()->makeStringAndClear();
2576 if (!m_aStates
.top().getCurrentBuffer())
2577 Mapper().props(new RTFReferenceProperties(
2578 lcl_getBookmarkProperties(m_aBookmarks
[aStr
], aStr
)));
2580 bufferProperties(*m_aStates
.top().getCurrentBuffer(),
2581 new RTFValue(lcl_getBookmarkProperties(m_aBookmarks
[aStr
], aStr
)),
2585 case Destination::INDEXENTRY
:
2586 case Destination::TOCENTRY
:
2588 if (&m_aStates
.top().getDestinationText()
2589 != m_aStates
.top().getCurrentDestinationText())
2590 break; // not for nested group
2591 OUString
str(m_aStates
.top().getCurrentDestinationText()->makeStringAndClear());
2592 // dmapper expects this as a field, so let's fake something...
2593 auto const field((Destination::INDEXENTRY
== rState
.getDestination())
2594 ? std::u16string_view(u
"XE")
2595 : std::u16string_view(u
"TC"));
2596 str
= OUString::Concat(field
) + " \"" + str
.replaceAll("\"", "\\\"") + "\"";
2597 singleChar(cFieldStart
);
2598 Mapper().utext(str
.getStr(), str
.getLength());
2599 singleChar(cFieldSep
, true);
2601 singleChar(cFieldEnd
);
2604 case Destination::FORMFIELDNAME
:
2606 if (&m_aStates
.top().getDestinationText()
2607 != m_aStates
.top().getCurrentDestinationText())
2608 break; // not for nested group
2610 = new RTFValue(m_aStates
.top().getCurrentDestinationText()->makeStringAndClear());
2611 m_aFormfieldSprms
.set(NS_ooxml::LN_CT_FFData_name
, pValue
);
2614 case Destination::FORMFIELDLIST
:
2616 if (&m_aStates
.top().getDestinationText()
2617 != m_aStates
.top().getCurrentDestinationText())
2618 break; // not for nested group
2620 = new RTFValue(m_aStates
.top().getCurrentDestinationText()->makeStringAndClear());
2621 // OOXML puts these into a LN_CT_FFData_ddList but FFDataHandler should handle this too
2622 m_aFormfieldSprms
.set(NS_ooxml::LN_CT_FFDDList_listEntry
, pValue
,
2623 RTFOverwrite::NO_APPEND
);
2626 case Destination::DATAFIELD
:
2630 OUStringBuffer
* pCurrentDestinationText
2631 = m_aStates
.top().getCurrentDestinationText();
2632 if (&m_aStates
.top().getDestinationText() != pCurrentDestinationText
)
2633 break; // not for nested group
2635 = OUStringToOString(*pCurrentDestinationText
, rState
.getCurrentEncoding());
2636 pCurrentDestinationText
->setLength(0);
2641 for (int i
= 0; i
< aStr
.getLength(); ++i
)
2644 if (ch
!= 0x0d && ch
!= 0x0a)
2647 sal_Int8 parsed
= msfilter::rtfutil::AsHex(ch
);
2649 return RTFError::HEX_INVALID
;
2654 aBuf
.append(static_cast<char>(b
));
2660 aStr
= aBuf
.makeStringAndClear();
2662 // ignore the first bytes
2663 if (aStr
.getLength() > 8)
2664 aStr
= aStr
.copy(8);
2666 sal_Int32 nLength
= aStr
.toChar();
2667 if (!aStr
.isEmpty())
2668 aStr
= aStr
.copy(1);
2669 nLength
= std::min(nLength
, aStr
.getLength());
2670 OString aName
= aStr
.copy(0, nLength
);
2671 if (aStr
.getLength() > nLength
)
2672 aStr
= aStr
.copy(nLength
+ 1); // zero-terminated string
2675 // extract default text
2676 nLength
= aStr
.toChar();
2677 if (!aStr
.isEmpty())
2678 aStr
= aStr
.copy(1);
2679 auto pNValue
= new RTFValue(OStringToOUString(aName
, rState
.getCurrentEncoding()));
2680 m_aFormfieldSprms
.set(NS_ooxml::LN_CT_FFData_name
, pNValue
);
2683 OString aDefaultText
= aStr
.copy(0, std::min(nLength
, aStr
.getLength()));
2684 auto pDValue
= new RTFValue(
2685 OStringToOUString(aDefaultText
, rState
.getCurrentEncoding()));
2686 m_aFormfieldSprms
.set(NS_ooxml::LN_CT_FFTextInput_default
, pDValue
);
2689 m_bFormField
= false;
2693 case Destination::CREATIONTIME
:
2694 if (m_xDocumentProperties
.is())
2695 m_xDocumentProperties
->setCreationDate(lcl_getDateTime(rState
));
2697 case Destination::REVISIONTIME
:
2698 if (m_xDocumentProperties
.is())
2699 m_xDocumentProperties
->setModificationDate(lcl_getDateTime(rState
));
2701 case Destination::PRINTTIME
:
2702 if (m_xDocumentProperties
.is())
2703 m_xDocumentProperties
->setPrintDate(lcl_getDateTime(rState
));
2705 case Destination::AUTHOR
:
2706 if (&m_aStates
.top().getDestinationText()
2707 != m_aStates
.top().getCurrentDestinationText())
2708 break; // not for nested group
2709 if (m_xDocumentProperties
.is())
2710 m_xDocumentProperties
->setAuthor(
2711 m_aStates
.top().getCurrentDestinationText()->makeStringAndClear());
2713 case Destination::KEYWORDS
:
2714 if (&m_aStates
.top().getDestinationText()
2715 != m_aStates
.top().getCurrentDestinationText())
2716 break; // not for nested group
2717 if (m_xDocumentProperties
.is())
2719 OUStringBuffer
* pCurrentDestinationText
2720 = m_aStates
.top().getCurrentDestinationText();
2721 m_xDocumentProperties
->setKeywords(
2722 comphelper::string::convertCommaSeparated(*pCurrentDestinationText
));
2723 pCurrentDestinationText
->setLength(0);
2726 case Destination::COMMENT
:
2727 if (&m_aStates
.top().getDestinationText()
2728 != m_aStates
.top().getCurrentDestinationText())
2729 break; // not for nested group
2730 if (m_xDocumentProperties
.is())
2731 m_xDocumentProperties
->setGenerator(
2732 m_aStates
.top().getCurrentDestinationText()->makeStringAndClear());
2734 case Destination::SUBJECT
:
2735 if (&m_aStates
.top().getDestinationText()
2736 != m_aStates
.top().getCurrentDestinationText())
2737 break; // not for nested group
2738 if (m_xDocumentProperties
.is())
2739 m_xDocumentProperties
->setSubject(
2740 m_aStates
.top().getCurrentDestinationText()->makeStringAndClear());
2742 case Destination::TITLE
:
2744 if (&m_aStates
.top().getDestinationText()
2745 != m_aStates
.top().getCurrentDestinationText())
2746 break; // not for nested group
2747 if (m_xDocumentProperties
.is())
2748 m_xDocumentProperties
->setTitle(
2749 rState
.getCurrentDestinationText()->makeStringAndClear());
2753 case Destination::DOCCOMM
:
2754 if (&m_aStates
.top().getDestinationText()
2755 != m_aStates
.top().getCurrentDestinationText())
2756 break; // not for nested group
2757 if (m_xDocumentProperties
.is())
2758 m_xDocumentProperties
->setDescription(
2759 m_aStates
.top().getCurrentDestinationText()->makeStringAndClear());
2761 case Destination::OPERATOR
:
2762 case Destination::COMPANY
:
2764 if (&m_aStates
.top().getDestinationText()
2765 != m_aStates
.top().getCurrentDestinationText())
2766 break; // not for nested group
2767 OUString aName
= rState
.getDestination() == Destination::OPERATOR
? OUString("Operator")
2768 : OUString("Company");
2769 uno::Any
aValue(m_aStates
.top().getCurrentDestinationText()->makeStringAndClear());
2770 if (m_xDocumentProperties
.is())
2772 uno::Reference
<beans::XPropertyContainer
> xUserDefinedProperties
2773 = m_xDocumentProperties
->getUserDefinedProperties();
2774 uno::Reference
<beans::XPropertySet
> xPropertySet(xUserDefinedProperties
,
2776 uno::Reference
<beans::XPropertySetInfo
> xPropertySetInfo
2777 = xPropertySet
->getPropertySetInfo();
2778 if (xPropertySetInfo
->hasPropertyByName(aName
))
2779 xPropertySet
->setPropertyValue(aName
, aValue
);
2781 xUserDefinedProperties
->addProperty(aName
, beans::PropertyAttribute::REMOVABLE
,
2786 case Destination::OBJDATA
:
2788 if (&m_aStates
.top().getDestinationText()
2789 != m_aStates
.top().getCurrentDestinationText())
2790 break; // not for nested group
2792 RTFError eError
= handleEmbeddedObject();
2793 if (eError
!= RTFError::OK
)
2797 case Destination::OBJCLASS
:
2800 = new RTFValue(m_aStates
.top().getCurrentDestinationText()->makeStringAndClear());
2801 m_aOLEAttributes
.set(NS_ooxml::LN_CT_OLEObject_ProgID
, pValue
);
2804 case Destination::OBJECT
:
2808 // if the object is in a special container we will use the \result
2809 // element instead of the \objdata
2810 // (see RTFKeyword::OBJECT in RTFDocumentImpl::dispatchDestination)
2814 RTFSprms aObjectSprms
;
2815 auto pOLEValue
= new RTFValue(m_aOLEAttributes
);
2816 aObjectSprms
.set(NS_ooxml::LN_OLEObject_OLEObject
, pOLEValue
);
2818 RTFSprms aObjAttributes
;
2820 auto pValue
= new RTFValue(m_aObjectAttributes
, aObjectSprms
);
2821 aObjSprms
.set(NS_ooxml::LN_object
, pValue
);
2822 writerfilter::Reference
<Properties
>::Pointer_t pProperties
2823 = new RTFReferenceProperties(std::move(aObjAttributes
), std::move(aObjSprms
));
2824 uno::Reference
<drawing::XShape
> xShape
;
2825 RTFValue::Pointer_t pShape
= m_aObjectAttributes
.find(NS_ooxml::LN_shape
);
2828 pShape
->getAny() >>= xShape
;
2831 Mapper().startShape(xShape
);
2832 Mapper().props(pProperties
);
2833 Mapper().endShape();
2835 m_aObjectAttributes
.clear();
2836 m_aOLEAttributes
.clear();
2840 case Destination::ANNOTATIONDATE
:
2842 OUStringBuffer
* pCurrentDestinationText
= m_aStates
.top().getCurrentDestinationText();
2843 if (&m_aStates
.top().getDestinationText() != pCurrentDestinationText
)
2844 break; // not for nested group
2845 OUString
aStr(OStringToOUString(DTTM22OString(o3tl::toInt32(*pCurrentDestinationText
)),
2846 rState
.getCurrentEncoding()));
2847 pCurrentDestinationText
->setLength(0);
2848 auto pValue
= new RTFValue(aStr
);
2849 RTFSprms aAnnAttributes
;
2850 aAnnAttributes
.set(NS_ooxml::LN_CT_TrackChange_date
, pValue
);
2851 writerfilter::Reference
<Properties
>::Pointer_t pProperties
2852 = new RTFReferenceProperties(std::move(aAnnAttributes
));
2853 Mapper().props(pProperties
);
2856 case Destination::ANNOTATIONAUTHOR
:
2857 if (&m_aStates
.top().getDestinationText()
2858 != m_aStates
.top().getCurrentDestinationText())
2859 break; // not for nested group
2860 m_aAuthor
= m_aStates
.top().getCurrentDestinationText()->makeStringAndClear();
2862 case Destination::ATNID
:
2863 if (&m_aStates
.top().getDestinationText()
2864 != m_aStates
.top().getCurrentDestinationText())
2865 break; // not for nested group
2866 m_aAuthorInitials
= m_aStates
.top().getCurrentDestinationText()->makeStringAndClear();
2868 case Destination::ANNOTATIONREFERENCESTART
:
2869 case Destination::ANNOTATIONREFERENCEEND
:
2871 if (&m_aStates
.top().getDestinationText()
2872 != m_aStates
.top().getCurrentDestinationText())
2873 break; // not for nested group
2874 OUString aStr
= m_aStates
.top().getCurrentDestinationText()->makeStringAndClear();
2875 auto pValue
= new RTFValue(aStr
.toInt32());
2876 RTFSprms aAttributes
;
2877 if (rState
.getDestination() == Destination::ANNOTATIONREFERENCESTART
)
2878 aAttributes
.set(NS_ooxml::LN_EG_RangeMarkupElements_commentRangeStart
, pValue
);
2880 aAttributes
.set(NS_ooxml::LN_EG_RangeMarkupElements_commentRangeEnd
, pValue
);
2881 if (!m_aStates
.top().getCurrentBuffer())
2883 writerfilter::Reference
<Properties
>::Pointer_t pProperties
2884 = new RTFReferenceProperties(std::move(aAttributes
));
2885 Mapper().props(pProperties
);
2889 auto const pValue2
= new RTFValue(aAttributes
, RTFSprms());
2890 bufferProperties(*m_aStates
.top().getCurrentBuffer(), pValue2
, nullptr);
2894 case Destination::ANNOTATIONREFERENCE
:
2896 if (&m_aStates
.top().getDestinationText()
2897 != m_aStates
.top().getCurrentDestinationText())
2898 break; // not for nested group
2899 OUString aStr
= m_aStates
.top().getCurrentDestinationText()->makeStringAndClear();
2900 RTFSprms aAnnAttributes
;
2901 aAnnAttributes
.set(NS_ooxml::LN_CT_Markup_id
, new RTFValue(aStr
.toInt32()));
2902 Mapper().props(new RTFReferenceProperties(std::move(aAnnAttributes
)));
2905 case Destination::FALT
:
2907 if (&m_aStates
.top().getDestinationText()
2908 != m_aStates
.top().getCurrentDestinationText())
2909 break; // not for nested group
2910 OUString
aStr(m_aStates
.top().getCurrentDestinationText()->makeStringAndClear());
2911 auto pValue
= new RTFValue(aStr
);
2912 rState
.getTableSprms().set(NS_ooxml::LN_CT_Font_altName
, pValue
);
2915 case Destination::DRAWINGOBJECT
:
2916 if (m_aStates
.top().getDrawingObject().getShape().is())
2918 RTFDrawingObject
& rDrawing
= m_aStates
.top().getDrawingObject();
2919 const uno::Reference
<drawing::XShape
>& xShape(rDrawing
.getShape());
2920 const uno::Reference
<beans::XPropertySet
>& xPropertySet(rDrawing
.getPropertySet());
2922 uno::Reference
<lang::XServiceInfo
> xServiceInfo(xShape
, uno::UNO_QUERY
);
2923 bool bTextFrame
= xServiceInfo
->supportsService("com.sun.star.text.TextFrame");
2925 // The default is certainly not inline, but then what Word supports is just at-character.
2926 xPropertySet
->setPropertyValue("AnchorType",
2927 uno::Any(text::TextContentAnchorType_AT_CHARACTER
));
2931 xPropertySet
->setPropertyValue("HoriOrientPosition",
2932 uno::Any(rDrawing
.getLeft()));
2933 xPropertySet
->setPropertyValue("VertOrientPosition",
2934 uno::Any(rDrawing
.getTop()));
2938 xShape
->setPosition(awt::Point(rDrawing
.getLeft(), rDrawing
.getTop()));
2940 xShape
->setSize(awt::Size(rDrawing
.getRight(), rDrawing
.getBottom()));
2942 if (rDrawing
.getHasLineColor())
2944 uno::Any
aLineColor(sal_uInt32((rDrawing
.getLineColorR() << 16)
2945 + (rDrawing
.getLineColorG() << 8)
2946 + rDrawing
.getLineColorB()));
2947 uno::Any aLineWidth
;
2948 RTFSdrImport::resolveLineColorAndWidth(bTextFrame
, xPropertySet
, aLineColor
,
2951 if (rDrawing
.getHasFillColor())
2952 xPropertySet
->setPropertyValue(
2953 "FillColor", uno::Any(sal_uInt32((rDrawing
.getFillColorR() << 16)
2954 + (rDrawing
.getFillColorG() << 8)
2955 + rDrawing
.getFillColorB())));
2956 else if (!bTextFrame
)
2957 // If there is no fill, the Word default is 100% transparency.
2958 xPropertySet
->setPropertyValue("FillTransparence", uno::Any(sal_Int32(100)));
2960 RTFSdrImport::resolveFLine(xPropertySet
, rDrawing
.getFLine());
2962 if (!m_aStates
.top().getDrawingObject().getHadShapeText())
2964 Mapper().startShape(xShape
);
2966 Mapper().endShape();
2969 case Destination::PICT
:
2970 // fdo#79319 ignore picture data if it's really a shape
2971 if (!m_pSdrImport
->isFakePict())
2973 resolvePict(true, m_pSdrImport
->getCurrentShape());
2975 m_bNeedFinalPar
= true;
2977 case Destination::SHAPE
:
2978 m_bNeedFinalPar
= true;
2979 m_bNeedCr
= m_bNeedCrOrig
;
2980 // tdf#47036 insert paragraph break for graphic object inside text
2981 // frame at start of document - TODO: the object may actually be
2982 // anchored inside the text frame and this ends up putting the
2983 // anchor in the body, but better than losing the shape...
2984 if (rState
.getFrame().hasProperties() && m_pSdrImport
->isTextGraphicObject())
2986 // parBreak() modifies m_aStates.top() so we can't apply resetFrame() directly on aState
2989 // Save this state for later use, so we only reset frame status only for the first shape inside a frame.
2990 rState
= m_aStates
.top();
2994 case Destination::MOMATH
:
2996 m_aMathBuffer
.appendClosingTag(M_TOKEN(oMath
));
2998 SvGlobalName
aGlobalName(SO3_SM_CLASSID
);
2999 comphelper::EmbeddedObjectContainer aContainer
;
3001 uno::Reference
<embed::XEmbeddedObject
> xObject
3002 = aContainer
.CreateEmbeddedObject(aGlobalName
.GetByteSequence(), aName
);
3003 if (xObject
) // rhbz#1766990 starmath might not be available
3005 uno::Reference
<util::XCloseable
> xComponent(xObject
->getComponent(),
3006 uno::UNO_SET_THROW
);
3007 if (oox::FormulaImExportBase
* pImport
3008 = dynamic_cast<oox::FormulaImExportBase
*>(xComponent
.get()))
3009 pImport
->readFormulaOoxml(m_aMathBuffer
);
3011 auto pValue
= new RTFValue(xObject
);
3012 RTFSprms aMathAttributes
;
3013 aMathAttributes
.set(NS_ooxml::LN_starmath
, pValue
);
3014 writerfilter::Reference
<Properties
>::Pointer_t pProperties
3015 = new RTFReferenceProperties(std::move(aMathAttributes
));
3016 Mapper().props(pProperties
);
3019 m_aMathBuffer
= oox::formulaimport::XmlStreamBuilder();
3022 case Destination::MR
:
3023 lcl_DestinationToMath(m_aStates
.top().getCurrentDestinationText(), m_aMathBuffer
,
3026 case Destination::MF
:
3027 m_aMathBuffer
.appendClosingTag(M_TOKEN(f
));
3029 case Destination::MFPR
:
3030 m_aMathBuffer
.appendClosingTag(M_TOKEN(fPr
));
3032 case Destination::MCTRLPR
:
3033 m_aMathBuffer
.appendClosingTag(M_TOKEN(ctrlPr
));
3035 case Destination::MNUM
:
3036 m_aMathBuffer
.appendClosingTag(M_TOKEN(num
));
3038 case Destination::MDEN
:
3039 m_aMathBuffer
.appendClosingTag(M_TOKEN(den
));
3041 case Destination::MACC
:
3042 m_aMathBuffer
.appendClosingTag(M_TOKEN(acc
));
3044 case Destination::MACCPR
:
3045 m_aMathBuffer
.appendClosingTag(M_TOKEN(accPr
));
3047 case Destination::MCHR
:
3048 case Destination::MPOS
:
3049 case Destination::MVERTJC
:
3050 case Destination::MSTRIKEH
:
3051 case Destination::MDEGHIDE
:
3052 case Destination::MBEGCHR
:
3053 case Destination::MSEPCHR
:
3054 case Destination::MENDCHR
:
3055 case Destination::MSUBHIDE
:
3056 case Destination::MSUPHIDE
:
3057 case Destination::MTYPE
:
3058 case Destination::MGROW
:
3060 sal_Int32 nMathToken
= 0;
3061 switch (rState
.getDestination())
3063 case Destination::MCHR
:
3064 nMathToken
= M_TOKEN(chr
);
3066 case Destination::MPOS
:
3067 nMathToken
= M_TOKEN(pos
);
3069 case Destination::MVERTJC
:
3070 nMathToken
= M_TOKEN(vertJc
);
3072 case Destination::MSTRIKEH
:
3073 nMathToken
= M_TOKEN(strikeH
);
3075 case Destination::MDEGHIDE
:
3076 nMathToken
= M_TOKEN(degHide
);
3078 case Destination::MBEGCHR
:
3079 nMathToken
= M_TOKEN(begChr
);
3081 case Destination::MSEPCHR
:
3082 nMathToken
= M_TOKEN(sepChr
);
3084 case Destination::MENDCHR
:
3085 nMathToken
= M_TOKEN(endChr
);
3087 case Destination::MSUBHIDE
:
3088 nMathToken
= M_TOKEN(subHide
);
3090 case Destination::MSUPHIDE
:
3091 nMathToken
= M_TOKEN(supHide
);
3093 case Destination::MTYPE
:
3094 nMathToken
= M_TOKEN(type
);
3096 case Destination::MGROW
:
3097 nMathToken
= M_TOKEN(grow
);
3103 oox::formulaimport::XmlStream::AttributeList aAttribs
;
3104 aAttribs
[M_TOKEN(val
)]
3105 = m_aStates
.top().getCurrentDestinationText()->makeStringAndClear();
3106 m_aMathBuffer
.appendOpeningTag(nMathToken
, aAttribs
);
3107 m_aMathBuffer
.appendClosingTag(nMathToken
);
3110 case Destination::ME
:
3111 m_aMathBuffer
.appendClosingTag(M_TOKEN(e
));
3113 case Destination::MBAR
:
3114 m_aMathBuffer
.appendClosingTag(M_TOKEN(bar
));
3116 case Destination::MBARPR
:
3117 m_aMathBuffer
.appendClosingTag(M_TOKEN(barPr
));
3119 case Destination::MD
:
3120 m_aMathBuffer
.appendClosingTag(M_TOKEN(d
));
3122 case Destination::MDPR
:
3123 m_aMathBuffer
.appendClosingTag(M_TOKEN(dPr
));
3125 case Destination::MFUNC
:
3126 m_aMathBuffer
.appendClosingTag(M_TOKEN(func
));
3128 case Destination::MFUNCPR
:
3129 m_aMathBuffer
.appendClosingTag(M_TOKEN(funcPr
));
3131 case Destination::MFNAME
:
3132 m_aMathBuffer
.appendClosingTag(M_TOKEN(fName
));
3134 case Destination::MLIMLOW
:
3135 m_aMathBuffer
.appendClosingTag(M_TOKEN(limLow
));
3137 case Destination::MLIMLOWPR
:
3138 m_aMathBuffer
.appendClosingTag(M_TOKEN(limLowPr
));
3140 case Destination::MLIM
:
3141 m_aMathBuffer
.appendClosingTag(M_TOKEN(lim
));
3143 case Destination::MM
:
3144 m_aMathBuffer
.appendClosingTag(M_TOKEN(m
));
3146 case Destination::MMPR
:
3147 m_aMathBuffer
.appendClosingTag(M_TOKEN(mPr
));
3149 case Destination::MMR
:
3150 m_aMathBuffer
.appendClosingTag(M_TOKEN(mr
));
3152 case Destination::MNARY
:
3153 m_aMathBuffer
.appendClosingTag(M_TOKEN(nary
));
3155 case Destination::MNARYPR
:
3156 m_aMathBuffer
.appendClosingTag(M_TOKEN(naryPr
));
3158 case Destination::MSUB
:
3159 m_aMathBuffer
.appendClosingTag(M_TOKEN(sub
));
3161 case Destination::MSUP
:
3162 m_aMathBuffer
.appendClosingTag(M_TOKEN(sup
));
3164 case Destination::MLIMUPP
:
3165 m_aMathBuffer
.appendClosingTag(M_TOKEN(limUpp
));
3167 case Destination::MLIMUPPPR
:
3168 m_aMathBuffer
.appendClosingTag(M_TOKEN(limUppPr
));
3170 case Destination::MGROUPCHR
:
3171 m_aMathBuffer
.appendClosingTag(M_TOKEN(groupChr
));
3173 case Destination::MGROUPCHRPR
:
3174 m_aMathBuffer
.appendClosingTag(M_TOKEN(groupChrPr
));
3176 case Destination::MBORDERBOX
:
3177 m_aMathBuffer
.appendClosingTag(M_TOKEN(borderBox
));
3179 case Destination::MBORDERBOXPR
:
3180 m_aMathBuffer
.appendClosingTag(M_TOKEN(borderBoxPr
));
3182 case Destination::MRAD
:
3183 m_aMathBuffer
.appendClosingTag(M_TOKEN(rad
));
3185 case Destination::MRADPR
:
3186 m_aMathBuffer
.appendClosingTag(M_TOKEN(radPr
));
3188 case Destination::MDEG
:
3189 m_aMathBuffer
.appendClosingTag(M_TOKEN(deg
));
3191 case Destination::MSSUB
:
3192 m_aMathBuffer
.appendClosingTag(M_TOKEN(sSub
));
3194 case Destination::MSSUBPR
:
3195 m_aMathBuffer
.appendClosingTag(M_TOKEN(sSubPr
));
3197 case Destination::MSSUP
:
3198 m_aMathBuffer
.appendClosingTag(M_TOKEN(sSup
));
3200 case Destination::MSSUPPR
:
3201 m_aMathBuffer
.appendClosingTag(M_TOKEN(sSupPr
));
3203 case Destination::MSSUBSUP
:
3204 m_aMathBuffer
.appendClosingTag(M_TOKEN(sSubSup
));
3206 case Destination::MSSUBSUPPR
:
3207 m_aMathBuffer
.appendClosingTag(M_TOKEN(sSubSupPr
));
3209 case Destination::MSPRE
:
3210 m_aMathBuffer
.appendClosingTag(M_TOKEN(sPre
));
3212 case Destination::MSPREPR
:
3213 m_aMathBuffer
.appendClosingTag(M_TOKEN(sPrePr
));
3215 case Destination::MBOX
:
3216 m_aMathBuffer
.appendClosingTag(M_TOKEN(box
));
3218 case Destination::MEQARR
:
3219 m_aMathBuffer
.appendClosingTag(M_TOKEN(eqArr
));
3221 case Destination::SHAPEGROUP
:
3222 if (rState
.getCreatedShapeGroup())
3223 m_pSdrImport
->popParent();
3225 case Destination::PROPNAME
:
3226 if (&m_aStates
.top().getDestinationText()
3227 != m_aStates
.top().getCurrentDestinationText())
3228 break; // not for nested group
3229 rState
.setPropName(m_aStates
.top().getCurrentDestinationText()->makeStringAndClear());
3231 case Destination::STATICVAL
:
3232 if (&m_aStates
.top().getDestinationText()
3233 != m_aStates
.top().getCurrentDestinationText())
3234 break; // not for nested group
3235 if (m_xDocumentProperties
.is())
3237 // Find out what is the key, value type and value we want to set.
3238 uno::Reference
<beans::XPropertyContainer
> xPropertyContainer
3239 = m_xDocumentProperties
->getUserDefinedProperties();
3240 const OUString
& rKey
= m_aStates
.top().getPropName();
3242 = m_aStates
.top().getCurrentDestinationText()->makeStringAndClear();
3244 if (m_aStates
.top().getPropType() == cppu::UnoType
<OUString
>::get())
3245 aAny
<<= aStaticVal
;
3246 else if (m_aStates
.top().getPropType() == cppu::UnoType
<sal_Int32
>::get())
3247 aAny
<<= aStaticVal
.toInt32();
3248 else if (m_aStates
.top().getPropType() == cppu::UnoType
<bool>::get())
3249 aAny
<<= aStaticVal
.toBoolean();
3250 else if (m_aStates
.top().getPropType() == cppu::UnoType
<util::DateTime
>::get())
3251 aAny
<<= getDateTimeFromUserProp(aStaticVal
);
3252 else if (m_aStates
.top().getPropType() == cppu::UnoType
<double>::get())
3253 aAny
<<= aStaticVal
.toDouble();
3255 xPropertyContainer
->addProperty(rKey
, beans::PropertyAttribute::REMOVABLE
, aAny
);
3258 case Destination::USERPROPS
:
3260 // These are the imported properties.
3261 uno::Reference
<document::XDocumentProperties
> xDocumentProperties
3262 = m_xDocumentProperties
;
3264 // These are the real document properties.
3265 uno::Reference
<document::XDocumentPropertiesSupplier
> xDocumentPropertiesSupplier(
3266 m_xDstDoc
, uno::UNO_QUERY
);
3267 if (xDocumentPropertiesSupplier
.is())
3268 m_xDocumentProperties
= xDocumentPropertiesSupplier
->getDocumentProperties();
3270 if (m_xDocumentProperties
.is())
3274 // Check classification.
3275 if (!SfxClassificationHelper::ShowPasteInfo(SfxClassificationHelper::CheckPaste(
3276 xDocumentProperties
, m_xDocumentProperties
)))
3277 return RTFError::CLASSIFICATION
;
3280 uno::Reference
<beans::XPropertyContainer
> xClipboardPropertyContainer
3281 = xDocumentProperties
->getUserDefinedProperties();
3282 uno::Reference
<beans::XPropertyContainer
> xDocumentPropertyContainer
3283 = m_xDocumentProperties
->getUserDefinedProperties();
3284 uno::Reference
<beans::XPropertySet
> xClipboardPropertySet(
3285 xClipboardPropertyContainer
, uno::UNO_QUERY
);
3286 uno::Reference
<beans::XPropertySet
> xDocumentPropertySet(xDocumentPropertyContainer
,
3288 const uno::Sequence
<beans::Property
> aClipboardProperties
3289 = xClipboardPropertySet
->getPropertySetInfo()->getProperties();
3290 uno::Sequence
<beans::Property
> aDocumentProperties
3291 = xDocumentPropertySet
->getPropertySetInfo()->getProperties();
3293 for (const beans::Property
& rProperty
: aClipboardProperties
)
3295 const OUString
& rKey
= rProperty
.Name
;
3296 uno::Any aValue
= xClipboardPropertySet
->getPropertyValue(rKey
);
3300 if (lcl_containsProperty(aDocumentProperties
, rKey
))
3302 // When pasting, don't update existing properties.
3304 xDocumentPropertySet
->setPropertyValue(rKey
, aValue
);
3307 xDocumentPropertyContainer
->addProperty(
3308 rKey
, beans::PropertyAttribute::REMOVABLE
, aValue
);
3310 catch (const uno::Exception
&)
3312 TOOLS_WARN_EXCEPTION("writerfilter.rtf", "failed to set property " << rKey
);
3322 return RTFError::OK
;
3325 void RTFDocumentImpl::afterPopState(RTFParserState
& rState
)
3328 switch (rState
.getDestination())
3330 case Destination::LISTENTRY
:
3332 auto pValue
= new RTFValue(rState
.getTableAttributes(), rState
.getTableSprms());
3333 m_aListTableSprms
.set(NS_ooxml::LN_CT_Numbering_abstractNum
, pValue
,
3334 RTFOverwrite::NO_APPEND
);
3335 m_aListTable
[rState
.getCurrentListIndex()] = pValue
;
3337 m_aInvalidListTableFirstIndents
[rState
.getCurrentListIndex()]
3338 = m_aInvalidListLevelFirstIndents
;
3339 m_aInvalidListLevelFirstIndents
.clear();
3342 case Destination::PARAGRAPHNUMBERING
:
3344 RTFValue::Pointer_t pIdValue
3345 = rState
.getTableAttributes().find(NS_ooxml::LN_CT_AbstractNum_nsid
);
3346 if (pIdValue
&& !m_aStates
.empty())
3348 // Abstract numbering
3349 RTFSprms aLeveltextAttributes
;
3350 OUString aTextValue
;
3351 RTFValue::Pointer_t pTextBefore
3352 = rState
.getTableAttributes().find(NS_ooxml::LN_CT_LevelText_val
);
3354 aTextValue
+= pTextBefore
->getString();
3356 RTFValue::Pointer_t pTextAfter
3357 = rState
.getTableAttributes().find(NS_ooxml::LN_CT_LevelSuffix_val
);
3359 aTextValue
+= pTextAfter
->getString();
3360 auto pTextValue
= new RTFValue(aTextValue
);
3361 aLeveltextAttributes
.set(NS_ooxml::LN_CT_LevelText_val
, pTextValue
);
3363 RTFSprms aLevelAttributes
;
3364 RTFSprms aLevelSprms
;
3365 auto pIlvlValue
= new RTFValue(0);
3366 aLevelAttributes
.set(NS_ooxml::LN_CT_Lvl_ilvl
, pIlvlValue
);
3368 RTFValue::Pointer_t pFmtValue
3369 = rState
.getTableSprms().find(NS_ooxml::LN_CT_Lvl_numFmt
);
3371 aLevelSprms
.set(NS_ooxml::LN_CT_Lvl_numFmt
, pFmtValue
);
3373 RTFValue::Pointer_t pStartatValue
3374 = rState
.getTableSprms().find(NS_ooxml::LN_CT_Lvl_start
);
3376 aLevelSprms
.set(NS_ooxml::LN_CT_Lvl_start
, pStartatValue
);
3378 auto pLeveltextValue
= new RTFValue(aLeveltextAttributes
);
3379 aLevelSprms
.set(NS_ooxml::LN_CT_Lvl_lvlText
, pLeveltextValue
);
3380 RTFValue::Pointer_t pRunProps
3381 = rState
.getTableSprms().find(NS_ooxml::LN_CT_Lvl_rPr
);
3383 aLevelSprms
.set(NS_ooxml::LN_CT_Lvl_rPr
, pRunProps
);
3385 RTFSprms aAbstractAttributes
;
3386 RTFSprms aAbstractSprms
;
3387 aAbstractAttributes
.set(NS_ooxml::LN_CT_AbstractNum_abstractNumId
, pIdValue
);
3388 auto pLevelValue
= new RTFValue(aLevelAttributes
, aLevelSprms
);
3389 aAbstractSprms
.set(NS_ooxml::LN_CT_AbstractNum_lvl
, pLevelValue
,
3390 RTFOverwrite::NO_APPEND
);
3392 RTFSprms aListTableSprms
;
3393 auto pAbstractValue
= new RTFValue(aAbstractAttributes
, aAbstractSprms
);
3394 // It's important that Numbering_abstractNum and Numbering_num never overwrites previous values.
3395 aListTableSprms
.set(NS_ooxml::LN_CT_Numbering_abstractNum
, pAbstractValue
,
3396 RTFOverwrite::NO_APPEND
);
3399 RTFSprms aNumberingAttributes
;
3400 RTFSprms aNumberingSprms
;
3401 aNumberingAttributes
.set(NS_ooxml::LN_CT_AbstractNum_nsid
, pIdValue
);
3402 aNumberingSprms
.set(NS_ooxml::LN_CT_Num_abstractNumId
, pIdValue
);
3403 auto pNumberingValue
= new RTFValue(aNumberingAttributes
, aNumberingSprms
);
3404 aListTableSprms
.set(NS_ooxml::LN_CT_Numbering_num
, pNumberingValue
,
3405 RTFOverwrite::NO_APPEND
);
3408 RTFSprms aListTableAttributes
;
3409 writerfilter::Reference
<Properties
>::Pointer_t pProp
= new RTFReferenceProperties(
3410 std::move(aListTableAttributes
), std::move(aListTableSprms
));
3412 RTFReferenceTable::Entries_t aListTableEntries
;
3413 aListTableEntries
.insert(std::make_pair(0, pProp
));
3414 writerfilter::Reference
<Table
>::Pointer_t
const pTable(
3415 new RTFReferenceTable(std::move(aListTableEntries
)));
3416 Mapper().table(NS_ooxml::LN_NUMBERING
, pTable
);
3419 putNestedSprm(m_aStates
.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_numPr
,
3420 NS_ooxml::LN_CT_NumPr_ilvl
, pIlvlValue
, RTFOverwrite::YES_PREPEND
);
3421 putNestedSprm(m_aStates
.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_numPr
,
3422 NS_ooxml::LN_CT_NumPr_numId
, pIdValue
, RTFOverwrite::YES_PREPEND
);
3426 case Destination::PARAGRAPHNUMBERING_TEXTAFTER
:
3427 if (!m_aStates
.empty())
3429 // FIXME: don't use pDestinationText, points to popped state
3430 auto pValue
= new RTFValue(rState
.getDestinationText().makeStringAndClear(), true);
3431 m_aStates
.top().getTableAttributes().set(NS_ooxml::LN_CT_LevelSuffix_val
, pValue
);
3434 case Destination::PARAGRAPHNUMBERING_TEXTBEFORE
:
3435 if (!m_aStates
.empty())
3437 // FIXME: don't use pDestinationText, points to popped state
3438 auto pValue
= new RTFValue(rState
.getDestinationText().makeStringAndClear(), true);
3439 m_aStates
.top().getTableAttributes().set(NS_ooxml::LN_CT_LevelText_val
, pValue
);
3442 case Destination::LISTNAME
:
3444 case Destination::LISTLEVEL
:
3445 if (!m_aStates
.empty())
3447 auto pInnerValue
= new RTFValue(m_aStates
.top().getListLevelNum()++);
3448 rState
.getTableAttributes().set(NS_ooxml::LN_CT_Lvl_ilvl
, pInnerValue
);
3450 auto pValue
= new RTFValue(rState
.getTableAttributes(), rState
.getTableSprms());
3451 if (m_aStates
.top().getDestination() != Destination::LFOLEVEL
)
3452 m_aStates
.top().getListLevelEntries().set(NS_ooxml::LN_CT_AbstractNum_lvl
,
3453 pValue
, RTFOverwrite::NO_APPEND
);
3455 m_aStates
.top().getTableSprms().set(NS_ooxml::LN_CT_NumLvl_lvl
, pValue
);
3458 case Destination::LFOLEVEL
:
3459 if (!m_aStates
.empty())
3461 auto pInnerValue
= new RTFValue(m_aStates
.top().getListLevelNum()++);
3462 rState
.getTableAttributes().set(NS_ooxml::LN_CT_NumLvl_ilvl
, pInnerValue
);
3464 auto pValue
= new RTFValue(rState
.getTableAttributes(), rState
.getTableSprms());
3465 m_aStates
.top().getTableSprms().set(NS_ooxml::LN_CT_Num_lvlOverride
, pValue
,
3466 RTFOverwrite::NO_APPEND
);
3469 // list override table
3470 case Destination::LISTOVERRIDEENTRY
:
3471 if (!m_aStates
.empty())
3473 if (m_aStates
.top().getDestination() == Destination::LISTOVERRIDEENTRY
)
3475 // copy properties upwards so upper popState() inserts it
3476 m_aStates
.top().getTableAttributes() = rState
.getTableAttributes();
3477 m_aStates
.top().getTableSprms() = rState
.getTableSprms();
3481 auto pValue
= new RTFValue(rState
.getTableAttributes(), rState
.getTableSprms());
3482 m_aListTableSprms
.set(NS_ooxml::LN_CT_Numbering_num
, pValue
,
3483 RTFOverwrite::NO_APPEND
);
3484 m_aListOverrideTable
[rState
.getCurrentListOverrideIndex()]
3485 = rState
.getCurrentListIndex();
3489 case Destination::LEVELTEXT
:
3490 if (!m_aStates
.empty())
3492 auto pValue
= new RTFValue(rState
.getTableAttributes());
3493 m_aStates
.top().getTableSprms().set(NS_ooxml::LN_CT_Lvl_lvlText
, pValue
);
3496 case Destination::LEVELNUMBERS
:
3497 if (!m_aStates
.empty())
3499 m_aStates
.top().getTableSprms() = rState
.getTableSprms();
3500 if (m_aStates
.top().getDestination() == Destination::LEVELNUMBERS
3501 || m_aStates
.top().getDestination() == Destination::LISTLEVEL
)
3502 // Parent state is level number or list level, current state is
3503 // level numbers: mark parent as invalid as well if necessary.
3504 m_aStates
.top().setLevelNumbersValid(rState
.getLevelNumbersValid());
3507 case Destination::FIELDINSTRUCTION
:
3508 if (!m_aStates
.empty())
3509 m_aStates
.top().setFieldStatus(RTFFieldStatus::INSTRUCTION
);
3511 case Destination::FIELDRESULT
:
3512 if (!m_aStates
.empty())
3513 m_aStates
.top().setFieldStatus(RTFFieldStatus::RESULT
);
3515 case Destination::FIELD
:
3516 if (rState
.getFieldStatus() == RTFFieldStatus::INSTRUCTION
)
3517 singleChar(cFieldEnd
);
3519 case Destination::DOCVAR
:
3520 if (!m_aStates
.empty())
3522 OUString
docvar(rState
.getDocVar());
3523 if (m_aStates
.top().getDocVarName().isEmpty())
3525 m_aStates
.top().setDocVarName(docvar
);
3529 uno::Reference
<beans::XPropertySet
> xMaster(
3530 m_xModelFactory
->createInstance("com.sun.star.text.FieldMaster.User"),
3531 uno::UNO_QUERY_THROW
);
3532 xMaster
->setPropertyValue("Name", uno::Any(m_aStates
.top().getDocVarName()));
3533 uno::Reference
<text::XDependentTextField
> xField(
3534 m_xModelFactory
->createInstance("com.sun.star.text.TextField.User"),
3536 xField
->attachTextFieldMaster(xMaster
);
3537 xField
->getTextFieldMaster()->setPropertyValue("Content", uno::Any(docvar
));
3539 m_aStates
.top().clearDocVarName();
3543 case Destination::SHAPEPROPERTYVALUEPICT
:
3544 if (!m_aStates
.empty())
3546 m_aStates
.top().getPicture() = rState
.getPicture();
3547 // both \sp and \sv are destinations, copy the text up-ward for later
3548 m_aStates
.top().getDestinationText() = rState
.getDestinationText();
3551 case Destination::FALT
:
3552 if (!m_aStates
.empty())
3553 m_aStates
.top().getTableSprms() = rState
.getTableSprms();
3555 case Destination::SHAPEPROPERTYNAME
:
3556 case Destination::SHAPEPROPERTYVALUE
:
3557 case Destination::SHAPEPROPERTY
:
3558 if (!m_aStates
.empty())
3560 m_aStates
.top().getShape() = rState
.getShape();
3561 m_aStates
.top().getPicture() = rState
.getPicture();
3562 m_aStates
.top().getCharacterAttributes() = rState
.getCharacterAttributes();
3565 case Destination::SHAPEINSTRUCTION
:
3566 if (!m_aStates
.empty()
3567 && m_aStates
.top().getDestination() == Destination::SHAPEINSTRUCTION
)
3569 // Shape instruction inside other shape instruction: just copy new shape settings:
3570 // it will be resolved on end of topmost shape instruction destination
3571 m_aStates
.top().getShape() = rState
.getShape();
3572 m_aStates
.top().getPicture() = rState
.getPicture();
3573 m_aStates
.top().getCharacterSprms() = rState
.getCharacterSprms();
3574 m_aStates
.top().getCharacterAttributes() = rState
.getCharacterAttributes();
3577 case Destination::FLYMAINCONTENT
:
3578 case Destination::SHPPICT
:
3579 case Destination::SHAPE
:
3580 if (!m_aStates
.empty())
3582 m_aStates
.top().getFrame() = rState
.getFrame();
3583 if (rState
.getDestination() == Destination::SHPPICT
3584 && m_aStates
.top().getDestination() == Destination::LISTPICTURE
)
3586 RTFSprms aAttributes
;
3587 aAttributes
.set(NS_ooxml::LN_CT_NumPicBullet_numPicBulletId
,
3588 new RTFValue(m_nListPictureId
++));
3590 // Dummy value, real picture is already sent to dmapper.
3591 aSprms
.set(NS_ooxml::LN_CT_NumPicBullet_pict
, new RTFValue(0));
3592 auto pValue
= new RTFValue(aAttributes
, aSprms
);
3593 m_aListTableSprms
.set(NS_ooxml::LN_CT_Numbering_numPicBullet
, pValue
,
3594 RTFOverwrite::NO_APPEND
);
3598 case Destination::SHAPETEXT
:
3599 if (!m_aStates
.empty())
3601 // If we're leaving the shapetext group (it may have nested ones) and this is a shape, not an old drawingobject.
3602 if (m_aStates
.top().getDestination() != Destination::SHAPETEXT
3603 && !m_aStates
.top().getDrawingObject().getHadShapeText())
3605 m_aStates
.top().setHadShapeText(true);
3606 if (!m_aStates
.top().getCurrentBuffer())
3607 m_pSdrImport
->close();
3609 m_aStates
.top().getCurrentBuffer()->push_back(
3610 Buf_t(BUFFER_ENDSHAPE
, nullptr, nullptr));
3613 // It's allowed to declare these inside the shape text, and they
3614 // are expected to have an effect for the whole shape.
3615 if (rState
.getDrawingObject().getLeft())
3616 m_aStates
.top().getDrawingObject().setLeft(rState
.getDrawingObject().getLeft());
3617 if (rState
.getDrawingObject().getTop())
3618 m_aStates
.top().getDrawingObject().setTop(rState
.getDrawingObject().getTop());
3619 if (rState
.getDrawingObject().getRight())
3620 m_aStates
.top().getDrawingObject().setRight(
3621 rState
.getDrawingObject().getRight());
3622 if (rState
.getDrawingObject().getBottom())
3623 m_aStates
.top().getDrawingObject().setBottom(
3624 rState
.getDrawingObject().getBottom());
3627 case Destination::PROPNAME
:
3628 if (m_aStates
.top().getDestination() == Destination::USERPROPS
)
3629 m_aStates
.top().setPropName(rState
.getPropName());
3633 if (!m_aStates
.empty() && m_aStates
.top().getDestination() == Destination::PICT
)
3634 m_aStates
.top().getPicture() = rState
.getPicture();
3640 RTFError
RTFDocumentImpl::popState()
3642 //SAL_INFO("writerfilter", __func__ << " before pop: m_pTokenizer->getGroup() " << m_pTokenizer->getGroup() <<
3643 // ", dest state: " << m_aStates.top().eDestination);
3645 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
3646 RTFParserState
aState(m_aStates
.top());
3647 m_bWasInFrame
= aState
.getFrame().hasProperties();
3649 // dmapper expects some content in header/footer, so if there would be nothing, add an empty paragraph.
3650 if (m_pTokenizer
->getGroup() == 1 && m_bFirstRun
)
3652 switch (m_nStreamType
)
3654 case NS_ooxml::LN_headerl
:
3655 case NS_ooxml::LN_headerr
:
3656 case NS_ooxml::LN_headerf
:
3657 case NS_ooxml::LN_footerl
:
3658 case NS_ooxml::LN_footerr
:
3659 case NS_ooxml::LN_footerf
:
3660 dispatchSymbol(RTFKeyword::PAR
);
3665 RTFError eError
= beforePopState(aState
);
3666 if (eError
!= RTFError::OK
)
3669 // See if we need to end a track change
3670 if (aState
.getStartedTrackchange())
3673 auto pValue
= new RTFValue(0);
3674 aTCSprms
.set(NS_ooxml::LN_endtrackchange
, pValue
);
3675 if (!m_aStates
.top().getCurrentBuffer())
3676 Mapper().props(new RTFReferenceProperties(RTFSprms(), std::move(aTCSprms
)));
3678 bufferProperties(*m_aStates
.top().getCurrentBuffer(),
3679 new RTFValue(RTFSprms(), aTCSprms
), nullptr);
3682 // This is the end of the doc, see if we need to close the last section.
3683 if (m_pTokenizer
->getGroup() == 1 && !m_bFirstRun
)
3685 // \par means an empty paragraph at the end of footnotes/endnotes, but
3686 // not in case of other substreams, like headers.
3687 if (m_bNeedCr
&& m_nStreamType
!= NS_ooxml::LN_footnote
3688 && m_nStreamType
!= NS_ooxml::LN_endnote
)
3692 // Make sure all the paragraph settings are set, but do not add next paragraph
3693 Mapper().markLastParagraph();
3695 dispatchSymbol(RTFKeyword::PAR
);
3697 if (m_bNeedSect
) // may be set by dispatchSymbol above!
3699 else if (!m_pSuperstream
)
3701 Mapper().markLastSectionGroup(); // ensure it's set for \par below
3703 if (m_bNeedPar
&& !m_pSuperstream
)
3705 assert(!m_bNeedSect
);
3706 dispatchSymbol(RTFKeyword::PAR
);
3707 m_bNeedSect
= false; // reset - m_bNeedPar was set for \sect at
3708 // end of doc so don't need another one
3714 m_pTokenizer
->popGroup();
3716 afterPopState(aState
);
3718 if (aState
.getCurrentBuffer() == &m_aSuperBuffer
)
3720 OSL_ASSERT(!m_aStates
.empty() && m_aStates
.top().getCurrentBuffer() == nullptr);
3722 if (!m_aSuperBuffer
.empty())
3723 replayBuffer(m_aSuperBuffer
, nullptr, nullptr);
3726 if (!m_aStates
.empty() && m_aStates
.top().getTableRowWidthAfter() > 0
3727 && aState
.getTableRowWidthAfter() == 0)
3728 // An RTFKeyword::ROW in the inner group already parsed nTableRowWidthAfter,
3729 // don't do it again in the outer state later.
3730 m_aStates
.top().setTableRowWidthAfter(0);
3732 if (m_nResetBreakOnSectBreak
!= RTFKeyword::invalid
&& !m_aStates
.empty())
3734 // Section break type created for \page still has an effect in the
3735 // outer state as well.
3736 RTFValue::Pointer_t pType
3737 = aState
.getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_type
);
3739 m_aStates
.top().getSectionSprms().set(NS_ooxml::LN_EG_SectPrContents_type
, pType
);
3742 return RTFError::OK
;
3745 RTFError
RTFDocumentImpl::handleEmbeddedObject()
3747 OUStringBuffer
* pCurrentDestinationText
= m_aStates
.top().getCurrentDestinationText();
3748 OString aStr
= OUStringToOString(*pCurrentDestinationText
, RTL_TEXTENCODING_ASCII_US
);
3749 pCurrentDestinationText
->setLength(0);
3750 std::unique_ptr
<SvStream
> pStream(new SvMemoryStream());
3751 if (!msfilter::rtfutil::ExtractOLE2FromObjdata(aStr
, *pStream
))
3752 return RTFError::HEX_INVALID
;
3754 uno::Reference
<io::XInputStream
> xInputStream(
3755 new utl::OSeekableInputStreamWrapper(pStream
.release(), /*_bOwner=*/true));
3756 m_aOLEAttributes
.set(NS_ooxml::LN_inputstream
, new RTFValue(xInputStream
));
3758 return RTFError::OK
;
3761 bool RTFDocumentImpl::isInBackground() { return m_aStates
.top().getInBackground(); }
3763 RTFInternalState
RTFDocumentImpl::getInternalState() { return m_aStates
.top().getInternalState(); }
3765 void RTFDocumentImpl::setInternalState(RTFInternalState nInternalState
)
3767 m_aStates
.top().setInternalState(nInternalState
);
3770 Destination
RTFDocumentImpl::getDestination() { return m_aStates
.top().getDestination(); }
3772 void RTFDocumentImpl::setDestination(Destination eDestination
)
3774 m_aStates
.top().setDestination(eDestination
);
3777 // this is a questionably named method that is used only in a very special
3778 // situation where it looks like the "current" buffer is needed?
3779 void RTFDocumentImpl::setDestinationText(std::u16string_view rString
)
3781 m_aStates
.top().getDestinationText().setLength(0);
3782 m_aStates
.top().getDestinationText().append(rString
);
3785 bool RTFDocumentImpl::getSkipUnknown() { return m_bSkipUnknown
; }
3787 void RTFDocumentImpl::setSkipUnknown(bool bSkipUnknown
) { m_bSkipUnknown
= bSkipUnknown
; }
3789 static auto FilterControlChars(Destination
const destination
, OUString
const& rString
) -> OUString
3791 if (destination
== Destination::LEVELNUMBERS
|| destination
== Destination::LEVELTEXT
)
3792 { // control characters are magic here!
3795 OUStringBuffer
buf(rString
.getLength());
3796 for (sal_Int32 i
= 0; i
< rString
.getLength(); ++i
)
3798 sal_Unicode
const ch(rString
[i
]);
3799 if (!linguistic::IsControlChar(ch
) || ch
== '\r' || ch
== '\n' || ch
== '\t')
3805 SAL_INFO("writerfilter.rtf", "filtering control character");
3808 return buf
.makeStringAndClear();
3811 void RTFDocumentImpl::checkUnicode(bool bUnicode
, bool bHex
)
3813 if (bUnicode
&& !m_aUnicodeBuffer
.isEmpty())
3815 OUString aString
= m_aUnicodeBuffer
.toString();
3816 m_aUnicodeBuffer
.setLength(0);
3817 aString
= FilterControlChars(m_aStates
.top().getDestination(), aString
);
3820 if (bHex
&& !m_aHexBuffer
.isEmpty())
3822 rtl_TextEncoding nEncoding
= m_aStates
.top().getCurrentEncoding();
3823 if (nEncoding
== RTL_TEXTENCODING_SYMBOL
3824 && (m_aStates
.top().getDestination() == Destination::FONTENTRY
3825 || (m_aStates
.size() > 1
3826 && m_aStates
[m_aStates
.size() - 2].getDestination()
3827 == Destination::FIELDINSTRUCTION
)))
3828 nEncoding
= RTL_TEXTENCODING_MS_1252
;
3829 OUString aString
= OStringToOUString(m_aHexBuffer
, nEncoding
);
3830 m_aHexBuffer
.setLength(0);
3831 aString
= FilterControlChars(m_aStates
.top().getDestination(), aString
);
3836 RTFParserState::RTFParserState(RTFDocumentImpl
* pDocumentImpl
)
3837 : m_pDocumentImpl(pDocumentImpl
)
3838 , m_nInternalState(RTFInternalState::NORMAL
)
3839 , m_eDestination(Destination::NORMAL
)
3840 , m_eFieldStatus(RTFFieldStatus::NONE
)
3841 , m_bFieldLocked(false)
3842 , m_nBorderState(RTFBorderState::NONE
)
3843 , m_nCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(0))
3846 , m_nBinaryToRead(0)
3847 , m_nListLevelNum(0)
3848 , m_bLevelNumbersValid(true)
3850 , m_eRunType(RunType::NONE
)
3856 , m_pCurrentDestinationText(nullptr)
3857 , m_nCurrentStyleIndex(0)
3858 , m_nCurrentCharacterStyleIndex(-1)
3859 , m_pCurrentBuffer(nullptr)
3860 , m_bInListpicture(false)
3861 , m_bInBackground(false)
3862 , m_bHadShapeText(false)
3863 , m_bInShapeGroup(false)
3865 , m_bCreatedShapeGroup(false)
3866 , m_bStartedTrackchange(false)
3867 , m_nTableRowWidthAfter(0)
3871 void RTFDocumentImpl::resetFrame() { m_aStates
.top().getFrame() = RTFFrame(&m_aStates
.top()); }
3873 void RTFDocumentImpl::bufferProperties(RTFBuffer_t
& rBuffer
, const RTFValue::Pointer_t
& pValue
,
3874 const tools::SvRef
<TableRowBuffer
>& pTableProperties
,
3875 Id
const nStyleType
)
3877 rBuffer
.emplace_back(BUFFER_SETSTYLE
, new RTFValue(m_aStates
.top().getCurrentStyleIndex()),
3879 assert(nStyleType
== 0 || nStyleType
== NS_ooxml::LN_Value_ST_StyleType_character
);
3880 rBuffer
.emplace_back(nStyleType
== NS_ooxml::LN_Value_ST_StyleType_character
? BUFFER_PROPS_CHAR
3882 pValue
, pTableProperties
);
3885 RTFShape::RTFShape() = default;
3887 RTFDrawingObject::RTFDrawingObject() = default;
3889 RTFFrame::RTFFrame(RTFParserState
* pParserState
)
3890 : m_pDocumentImpl(pParserState
->getDocumentImpl())
3901 , m_nHRule(NS_ooxml::LN_Value_doc_ST_HeightRule_auto
)
3905 void RTFFrame::setSprm(Id nId
, Id nValue
)
3909 case NS_ooxml::LN_CT_FramePr_w
:
3912 case NS_ooxml::LN_CT_FramePr_h
:
3915 case NS_ooxml::LN_CT_FramePr_x
:
3918 case NS_ooxml::LN_CT_FramePr_y
:
3921 case NS_ooxml::LN_CT_FramePr_hSpace
:
3922 m_nHoriPadding
= nValue
;
3924 case NS_ooxml::LN_CT_FramePr_vSpace
:
3925 m_nVertPadding
= nValue
;
3927 case NS_ooxml::LN_CT_FramePr_xAlign
:
3928 m_nHoriAlign
= nValue
;
3930 case NS_ooxml::LN_CT_FramePr_hAnchor
:
3931 m_nHoriAnchor
= nValue
;
3933 case NS_ooxml::LN_CT_FramePr_yAlign
:
3934 m_nVertAlign
= nValue
;
3936 case NS_ooxml::LN_CT_FramePr_vAnchor
:
3937 m_nVertAnchor
= nValue
;
3939 case NS_ooxml::LN_CT_FramePr_wrap
:
3946 if (m_pDocumentImpl
->getFirstRun() && !m_pDocumentImpl
->isStyleSheetImport() && hasProperties())
3948 m_pDocumentImpl
->checkFirstRun();
3949 m_pDocumentImpl
->setNeedPar(false);
3953 RTFSprms
RTFFrame::getSprms()
3957 static const Id pNames
[]
3958 = { NS_ooxml::LN_CT_FramePr_x
, NS_ooxml::LN_CT_FramePr_y
,
3959 NS_ooxml::LN_CT_FramePr_hRule
, // Make sure nHRule is processed before nH
3960 NS_ooxml::LN_CT_FramePr_h
, NS_ooxml::LN_CT_FramePr_w
,
3961 NS_ooxml::LN_CT_FramePr_hSpace
, NS_ooxml::LN_CT_FramePr_vSpace
,
3962 NS_ooxml::LN_CT_FramePr_hAnchor
, NS_ooxml::LN_CT_FramePr_vAnchor
,
3963 NS_ooxml::LN_CT_FramePr_xAlign
, NS_ooxml::LN_CT_FramePr_yAlign
,
3964 NS_ooxml::LN_CT_FramePr_wrap
, NS_ooxml::LN_CT_FramePr_dropCap
,
3965 NS_ooxml::LN_CT_FramePr_lines
};
3967 for (Id nId
: pNames
)
3969 RTFValue::Pointer_t pValue
;
3973 case NS_ooxml::LN_CT_FramePr_x
:
3975 pValue
= new RTFValue(m_nX
);
3977 case NS_ooxml::LN_CT_FramePr_y
:
3979 pValue
= new RTFValue(m_nY
);
3981 case NS_ooxml::LN_CT_FramePr_h
:
3984 if (m_nHRule
== NS_ooxml::LN_Value_doc_ST_HeightRule_exact
)
3985 pValue
= new RTFValue(-m_nH
); // The negative value just sets nHRule
3987 pValue
= new RTFValue(m_nH
);
3990 case NS_ooxml::LN_CT_FramePr_w
:
3992 pValue
= new RTFValue(m_nW
);
3994 case NS_ooxml::LN_CT_FramePr_hSpace
:
3995 if (m_nHoriPadding
!= 0)
3996 pValue
= new RTFValue(m_nHoriPadding
);
3998 case NS_ooxml::LN_CT_FramePr_vSpace
:
3999 if (m_nVertPadding
!= 0)
4000 pValue
= new RTFValue(m_nVertPadding
);
4002 case NS_ooxml::LN_CT_FramePr_hAnchor
:
4004 if (m_nHoriAnchor
== 0)
4005 m_nHoriAnchor
= NS_ooxml::LN_Value_doc_ST_HAnchor_text
;
4006 pValue
= new RTFValue(m_nHoriAnchor
);
4009 case NS_ooxml::LN_CT_FramePr_vAnchor
:
4011 if (m_nVertAnchor
== 0)
4012 m_nVertAnchor
= NS_ooxml::LN_Value_doc_ST_VAnchor_margin
;
4013 pValue
= new RTFValue(m_nVertAnchor
);
4016 case NS_ooxml::LN_CT_FramePr_xAlign
:
4017 pValue
= new RTFValue(m_nHoriAlign
);
4019 case NS_ooxml::LN_CT_FramePr_yAlign
:
4020 pValue
= new RTFValue(m_nVertAlign
);
4022 case NS_ooxml::LN_CT_FramePr_hRule
:
4025 m_nHRule
= NS_ooxml::LN_Value_doc_ST_HeightRule_exact
;
4027 m_nHRule
= NS_ooxml::LN_Value_doc_ST_HeightRule_atLeast
;
4028 pValue
= new RTFValue(m_nHRule
);
4031 case NS_ooxml::LN_CT_FramePr_wrap
:
4033 pValue
= new RTFValue(*m_oWrap
);
4040 sprms
.set(nId
, pValue
);
4043 RTFSprms frameprSprms
;
4044 frameprSprms
.set(NS_ooxml::LN_CT_PPrBase_framePr
, new RTFValue(sprms
));
4045 return frameprSprms
;
4048 bool RTFFrame::hasProperties() const
4050 // tdf#153178 \dxfrtext \dfrmtxtx \dfrmtxty \wrapdefault do *not* create frame
4051 return m_nX
!= 0 || m_nY
!= 0 || m_nW
!= 0 || m_nH
!= 0
4052 || (m_nHoriAlign
&& m_nHoriAlign
!= NS_ooxml::LN_Value_doc_ST_XAlign_left
)
4053 || (m_nHoriAnchor
&& m_nHoriAnchor
!= NS_ooxml::LN_Value_doc_ST_HAnchor_text
)
4054 || (m_nVertAlign
&& m_nVertAlign
!= NS_ooxml::LN_Value_doc_ST_YAlign_inline
)
4055 || (m_nVertAnchor
&& m_nVertAnchor
!= NS_ooxml::LN_Value_doc_ST_VAnchor_margin
)
4056 || (m_oWrap
&& *m_oWrap
!= NS_ooxml::LN_Value_doc_ST_Wrap_auto
);
4059 } // namespace writerfilter
4061 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */