1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  */
9 
10 #include "rtfdocumentimpl.hxx"
11 #include <algorithm>
12 #include <memory>
13 #include <com/sun/star/embed/XEmbeddedObject.hpp>
14 #include <com/sun/star/beans/PropertyAttribute.hpp>
15 #include <com/sun/star/io/WrongFormatException.hpp>
16 #include <com/sun/star/lang/XServiceInfo.hpp>
17 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
18 #include <com/sun/star/text/TextContentAnchorType.hpp>
19 #include <i18nlangtag/languagetag.hxx>
20 #include <unotools/ucbstreamhelper.hxx>
21 #include <unotools/streamwrap.hxx>
22 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
23 #include <filter/msfilter/util.hxx>
24 #include <filter/msfilter/rtfutil.hxx>
25 #include <comphelper/string.hxx>
26 #include <tools/diagnose_ex.h>
27 #include <tools/globname.hxx>
28 #include <tools/datetimeutils.hxx>
29 #include <comphelper/classids.hxx>
30 #include <comphelper/embeddedobjectcontainer.hxx>
31 #include <svl/lngmisc.hxx>
32 #include <sfx2/sfxbasemodel.hxx>
33 #include <sfx2/classificationhelper.hxx>
34 #include <oox/mathml/import.hxx>
35 #include <ooxml/resourceids.hxx>
36 #include <oox/token/namespaces.hxx>
37 #include <oox/drawingml/drawingmltypes.hxx>
38 #include <rtl/uri.hxx>
39 #include <rtl/tencinfo.h>
40 #include <sal/log.hxx>
41 #include <osl/diagnose.h>
42 #include <oox/helper/graphichelper.hxx>
43 #include <vcl/wmfexternal.hxx>
44 #include <vcl/graph.hxx>
45 #include <vcl/settings.hxx>
46 #include <vcl/svapp.hxx>
47 #include "rtfsdrimport.hxx"
48 #include "rtfreferenceproperties.hxx"
49 #include "rtfskipdestination.hxx"
50 #include "rtftokenizer.hxx"
51 #include "rtflookahead.hxx"
52 
53 using namespace com::sun::star;
54 
55 namespace
56 {
57 /// Returns an util::DateTime from a 'YYYY. MM. DD.' string.
getDateTimeFromUserProp(const OUString & rString)58 util::DateTime getDateTimeFromUserProp(const OUString& rString)
59 {
60     util::DateTime aRet;
61     sal_Int32 nLen = rString.getLength();
62     if (nLen >= 4)
63     {
64         aRet.Year = rString.copy(0, 4).toInt32();
65 
66         if (nLen >= 8 && rString.match(". ", 4))
67         {
68             aRet.Month = rString.copy(6, 2).toInt32();
69 
70             if (nLen >= 12 && rString.match(". ", 8))
71                 aRet.Day = rString.copy(10, 2).toInt32();
72         }
73     }
74     return aRet;
75 }
76 } // anonymous namespace
77 
78 namespace writerfilter::rtftok
79 {
getParagraphBorder(sal_uInt32 nIndex)80 Id getParagraphBorder(sal_uInt32 nIndex)
81 {
82     static const Id aBorderIds[] = { NS_ooxml::LN_CT_PBdr_top, NS_ooxml::LN_CT_PBdr_left,
83                                      NS_ooxml::LN_CT_PBdr_bottom, NS_ooxml::LN_CT_PBdr_right };
84 
85     return aBorderIds[nIndex];
86 }
87 
putNestedAttribute(RTFSprms & rSprms,Id nParent,Id nId,const RTFValue::Pointer_t & pValue,RTFOverwrite eOverwrite,bool bAttribute)88 void putNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId, const RTFValue::Pointer_t& pValue,
89                         RTFOverwrite eOverwrite, bool bAttribute)
90 {
91     RTFValue::Pointer_t pParent = rSprms.find(nParent, /*bFirst=*/true, /*bForWrite=*/true);
92     if (!pParent)
93     {
94         RTFSprms aAttributes;
95         if (nParent == NS_ooxml::LN_CT_TcPrBase_shd)
96         {
97             // RTF default is 'auto', see writerfilter::dmapper::CellColorHandler
98             aAttributes.set(NS_ooxml::LN_CT_Shd_color, new RTFValue(sal_uInt32(COL_AUTO)));
99             aAttributes.set(NS_ooxml::LN_CT_Shd_fill, new RTFValue(sal_uInt32(COL_AUTO)));
100         }
101         auto pParentValue = new RTFValue(aAttributes);
102         rSprms.set(nParent, pParentValue, eOverwrite);
103         pParent = pParentValue;
104     }
105     RTFSprms& rAttributes = (bAttribute ? pParent->getAttributes() : pParent->getSprms());
106     rAttributes.set(nId, pValue, eOverwrite);
107 }
108 
putNestedSprm(RTFSprms & rSprms,Id nParent,Id nId,const RTFValue::Pointer_t & pValue,RTFOverwrite eOverwrite)109 void putNestedSprm(RTFSprms& rSprms, Id nParent, Id nId, const RTFValue::Pointer_t& pValue,
110                    RTFOverwrite eOverwrite)
111 {
112     putNestedAttribute(rSprms, nParent, nId, pValue, eOverwrite, false);
113 }
114 
getNestedAttribute(RTFSprms & rSprms,Id nParent,Id nId)115 RTFValue::Pointer_t getNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId)
116 {
117     RTFValue::Pointer_t pParent = rSprms.find(nParent);
118     if (!pParent)
119         return RTFValue::Pointer_t();
120     RTFSprms& rAttributes = pParent->getAttributes();
121     return rAttributes.find(nId);
122 }
123 
getNestedSprm(RTFSprms & rSprms,Id nParent,Id nId)124 RTFValue::Pointer_t getNestedSprm(RTFSprms& rSprms, Id nParent, Id nId)
125 {
126     RTFValue::Pointer_t pParent = rSprms.find(nParent);
127     if (!pParent)
128         return RTFValue::Pointer_t();
129     RTFSprms& rInner = pParent->getSprms();
130     return rInner.find(nId);
131 }
132 
eraseNestedAttribute(RTFSprms & rSprms,Id nParent,Id nId)133 bool eraseNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId)
134 {
135     RTFValue::Pointer_t pParent = rSprms.find(nParent);
136     if (!pParent)
137         // It doesn't even have a parent, we're done.
138         return false;
139     RTFSprms& rAttributes = pParent->getAttributes();
140     return rAttributes.erase(nId);
141 }
142 
getLastAttributes(RTFSprms & rSprms,Id nId)143 RTFSprms& getLastAttributes(RTFSprms& rSprms, Id nId)
144 {
145     RTFValue::Pointer_t p = rSprms.find(nId);
146     if (p && !p->getSprms().empty())
147         return p->getSprms().back().second->getAttributes();
148 
149     SAL_WARN("writerfilter.rtf", "trying to set property when no type is defined");
150     return rSprms;
151 }
152 
putBorderProperty(RTFStack & aStates,Id nId,const RTFValue::Pointer_t & pValue)153 void putBorderProperty(RTFStack& aStates, Id nId, const RTFValue::Pointer_t& pValue)
154 {
155     RTFSprms* pAttributes = nullptr;
156     if (aStates.top().getBorderState() == RTFBorderState::PARAGRAPH_BOX)
157         for (int i = 0; i < 4; i++)
158         {
159             RTFValue::Pointer_t p = aStates.top().getParagraphSprms().find(getParagraphBorder(i));
160             if (p)
161             {
162                 RTFSprms& rAttributes = p->getAttributes();
163                 rAttributes.set(nId, pValue);
164             }
165         }
166     else if (aStates.top().getBorderState() == RTFBorderState::CHARACTER)
167     {
168         RTFValue::Pointer_t pPointer
169             = aStates.top().getCharacterSprms().find(NS_ooxml::LN_EG_RPrBase_bdr);
170         if (pPointer)
171         {
172             RTFSprms& rAttributes = pPointer->getAttributes();
173             rAttributes.set(nId, pValue);
174         }
175     }
176     // Attributes of the last border type
177     else if (aStates.top().getBorderState() == RTFBorderState::PARAGRAPH)
178         pAttributes
179             = &getLastAttributes(aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PrBase_pBdr);
180     else if (aStates.top().getBorderState() == RTFBorderState::CELL)
181         pAttributes = &getLastAttributes(aStates.top().getTableCellSprms(),
182                                          NS_ooxml::LN_CT_TcPrBase_tcBorders);
183     else if (aStates.top().getBorderState() == RTFBorderState::PAGE)
184         pAttributes = &getLastAttributes(aStates.top().getSectionSprms(),
185                                          NS_ooxml::LN_EG_SectPrContents_pgBorders);
186     if (pAttributes)
187         pAttributes->set(nId, pValue);
188 }
189 
DTTM22OString(tools::Long nDTTM)190 OString DTTM22OString(tools::Long nDTTM)
191 {
192     return DateTimeToOString(msfilter::util::DTTM2DateTime(nDTTM));
193 }
194 
lcl_getBookmarkProperties(int nPos,const OUString & rString)195 static RTFSprms lcl_getBookmarkProperties(int nPos, const OUString& rString)
196 {
197     RTFSprms aAttributes;
198     auto pPos = new RTFValue(nPos);
199     if (!rString.isEmpty())
200     {
201         // If present, this should be sent first.
202         auto pString = new RTFValue(rString);
203         aAttributes.set(NS_ooxml::LN_CT_Bookmark_name, pString);
204     }
205     aAttributes.set(NS_ooxml::LN_CT_MarkupRangeBookmark_id, pPos);
206     return aAttributes;
207 }
208 
keywordToString(RTFKeyword nKeyword)209 const char* keywordToString(RTFKeyword nKeyword)
210 {
211     for (int i = 0; i < nRTFControlWords; i++)
212     {
213         if (nKeyword == aRTFControlWords[i].GetIndex())
214             return aRTFControlWords[i].GetKeyword();
215     }
216     return nullptr;
217 }
218 
lcl_getDateTime(RTFParserState const & aState)219 static util::DateTime lcl_getDateTime(RTFParserState const& aState)
220 {
221     return { 0 /*100sec*/,
222              0 /*sec*/,
223              aState.getMinute(),
224              aState.getHour(),
225              aState.getDay(),
226              aState.getMonth(),
227              static_cast<sal_Int16>(aState.getYear()),
228              false };
229 }
230 
lcl_DestinationToMath(OUStringBuffer * pDestinationText,oox::formulaimport::XmlStreamBuilder & rMathBuffer,bool & rMathNor)231 static void lcl_DestinationToMath(OUStringBuffer* pDestinationText,
232                                   oox::formulaimport::XmlStreamBuilder& rMathBuffer, bool& rMathNor)
233 {
234     if (!pDestinationText)
235         return;
236     OUString aStr = pDestinationText->makeStringAndClear();
237     if (aStr.isEmpty())
238         return;
239     rMathBuffer.appendOpeningTag(M_TOKEN(r));
240     if (rMathNor)
241     {
242         rMathBuffer.appendOpeningTag(M_TOKEN(rPr));
243         // Same as M_TOKEN(lit)
244         rMathBuffer.appendOpeningTag(M_TOKEN(nor));
245         rMathBuffer.appendClosingTag(M_TOKEN(nor));
246         rMathBuffer.appendClosingTag(M_TOKEN(rPr));
247         rMathNor = false;
248     }
249     rMathBuffer.appendOpeningTag(M_TOKEN(t));
250     rMathBuffer.appendCharacters(aStr);
251     rMathBuffer.appendClosingTag(M_TOKEN(t));
252     rMathBuffer.appendClosingTag(M_TOKEN(r));
253 }
254 
RTFDocumentImpl(uno::Reference<uno::XComponentContext> const & xContext,uno::Reference<io::XInputStream> const & xInputStream,uno::Reference<lang::XComponent> const & xDstDoc,uno::Reference<frame::XFrame> const & xFrame,uno::Reference<task::XStatusIndicator> const & xStatusIndicator,const utl::MediaDescriptor & rMediaDescriptor)255 RTFDocumentImpl::RTFDocumentImpl(uno::Reference<uno::XComponentContext> const& xContext,
256                                  uno::Reference<io::XInputStream> const& xInputStream,
257                                  uno::Reference<lang::XComponent> const& xDstDoc,
258                                  uno::Reference<frame::XFrame> const& xFrame,
259                                  uno::Reference<task::XStatusIndicator> const& xStatusIndicator,
260                                  const utl::MediaDescriptor& rMediaDescriptor)
261     : m_xContext(xContext)
262     , m_xInputStream(xInputStream)
263     , m_xDstDoc(xDstDoc)
264     , m_xFrame(xFrame)
265     , m_xStatusIndicator(xStatusIndicator)
266     , m_pMapperStream(nullptr)
267     , m_aDefaultState(this)
268     , m_bSkipUnknown(false)
269     , m_bFirstRun(true)
270     , m_bFirstRunException(false)
271     , m_bNeedPap(true)
272     , m_bNeedCr(false)
273     , m_bNeedCrOrig(false)
274     , m_bNeedPar(true)
275     , m_bNeedFinalPar(false)
276     , m_nNestedCells(0)
277     , m_nTopLevelCells(0)
278     , m_nInheritingCells(0)
279     , m_nNestedTRLeft(0)
280     , m_nTopLevelTRLeft(0)
281     , m_nNestedCurrentCellX(0)
282     , m_nTopLevelCurrentCellX(0)
283     , m_nBackupTopLevelCurrentCellX(0)
284     , m_aTableBufferStack(1) // create top-level buffer already
285     , m_pSuperstream(nullptr)
286     , m_nStreamType(0)
287     , m_nGroupStartPos(0)
288     , m_nFormFieldType(RTFFormFieldType::NONE)
289     , m_bObject(false)
290     , m_nCurrentFontIndex(0)
291     , m_nCurrentEncoding(-1)
292     , m_nDefaultFontIndex(-1)
293     , m_nCurrentStyleIndex(0)
294     , m_bFormField(false)
295     , m_bMathNor(false)
296     , m_bIgnoreNextContSectBreak(false)
297     , m_nResetBreakOnSectBreak(RTFKeyword::invalid)
298     , m_bNeedSect(false) // done by checkFirstRun
299     , m_bWasInFrame(false)
300     , m_bHadPicture(false)
301     , m_bHadSect(false)
302     , m_nCellxMax(0)
303     , m_nListPictureId(0)
304     , m_bIsNewDoc(!rMediaDescriptor.getUnpackedValueOrDefault("InsertMode", false))
305     , m_rMediaDescriptor(rMediaDescriptor)
306     , m_hasRHeader(false)
307     , m_hasFHeader(false)
308     , m_hasRFooter(false)
309     , m_hasFFooter(false)
310     , m_bAfterCellBeforeRow(false)
311 {
312     OSL_ASSERT(xInputStream.is());
313     m_pInStream = utl::UcbStreamHelper::CreateStream(xInputStream, true);
314 
315     m_xModelFactory.set(m_xDstDoc, uno::UNO_QUERY);
316 
317     uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier(
318         m_xDstDoc, uno::UNO_QUERY);
319     if (xDocumentPropertiesSupplier.is())
320         m_xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties();
321 
322     m_pGraphicHelper = std::make_shared<oox::GraphicHelper>(m_xContext, xFrame, oox::StorageRef());
323 
324     m_pTokenizer = new RTFTokenizer(*this, m_pInStream.get(), m_xStatusIndicator);
325     m_pSdrImport = new RTFSdrImport(*this, m_xDstDoc);
326 }
327 
328 RTFDocumentImpl::~RTFDocumentImpl() = default;
329 
Strm()330 SvStream& RTFDocumentImpl::Strm() { return *m_pInStream; }
331 
setSuperstream(RTFDocumentImpl * pSuperstream)332 void RTFDocumentImpl::setSuperstream(RTFDocumentImpl* pSuperstream)
333 {
334     m_pSuperstream = pSuperstream;
335 }
336 
isSubstream() const337 bool RTFDocumentImpl::isSubstream() const { return m_pSuperstream != nullptr; }
338 
finishSubstream()339 void RTFDocumentImpl::finishSubstream() { checkUnicode(/*bUnicode =*/true, /*bHex =*/true); }
340 
resolveSubstream(std::size_t nPos,Id nId)341 void RTFDocumentImpl::resolveSubstream(std::size_t nPos, Id nId)
342 {
343     resolveSubstream(nPos, nId, OUString());
344 }
resolveSubstream(std::size_t nPos,Id nId,OUString const & rIgnoreFirst)345 void RTFDocumentImpl::resolveSubstream(std::size_t nPos, Id nId, OUString const& rIgnoreFirst)
346 {
347     sal_uInt64 const nCurrent = Strm().Tell();
348     // Seek to header position, parse, then seek back.
349     auto pImpl = new RTFDocumentImpl(m_xContext, m_xInputStream, m_xDstDoc, m_xFrame,
350                                      m_xStatusIndicator, m_rMediaDescriptor);
351     pImpl->setSuperstream(this);
352     pImpl->m_nStreamType = nId;
353     pImpl->m_aIgnoreFirst = rIgnoreFirst;
354     if (!m_aAuthor.isEmpty())
355     {
356         pImpl->m_aAuthor = m_aAuthor;
357         m_aAuthor.clear();
358     }
359     if (!m_aAuthorInitials.isEmpty())
360     {
361         pImpl->m_aAuthorInitials = m_aAuthorInitials;
362         m_aAuthorInitials.clear();
363     }
364     pImpl->m_nDefaultFontIndex = m_nDefaultFontIndex;
365     pImpl->Strm().Seek(nPos);
366     SAL_INFO("writerfilter.rtf", "substream start");
367     Mapper().substream(nId, pImpl);
368     SAL_INFO("writerfilter.rtf", "substream end");
369     Strm().Seek(nCurrent);
370 }
371 
outputSettingsTable()372 void RTFDocumentImpl::outputSettingsTable()
373 {
374     // tdf#136740: do not change target document settings when pasting
375     if (!m_bIsNewDoc)
376         return;
377     writerfilter::Reference<Properties>::Pointer_t pProp
378         = new RTFReferenceProperties(m_aSettingsTableAttributes, m_aSettingsTableSprms);
379     RTFReferenceTable::Entries_t aSettingsTableEntries;
380     aSettingsTableEntries.insert(std::make_pair(0, pProp));
381     writerfilter::Reference<Table>::Pointer_t pTable = new RTFReferenceTable(aSettingsTableEntries);
382     Mapper().table(NS_ooxml::LN_settings_settings, pTable);
383 }
384 
checkFirstRun()385 void RTFDocumentImpl::checkFirstRun()
386 {
387     if (!m_bFirstRun)
388         return;
389 
390     outputSettingsTable();
391     // start initial paragraph
392     m_bFirstRun = false;
393     assert(!m_bNeedSect || m_bFirstRunException);
394     setNeedSect(true); // first call that succeeds
395 
396     // set the requested default font, if there are none
397     RTFValue::Pointer_t pFont
398         = getNestedAttribute(m_aDefaultState.getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts,
399                              NS_ooxml::LN_CT_Fonts_ascii);
400     RTFValue::Pointer_t pCurrentFont
401         = getNestedAttribute(m_aStates.top().getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts,
402                              NS_ooxml::LN_CT_Fonts_ascii);
403     if (pFont && !pCurrentFont)
404         putNestedAttribute(m_aStates.top().getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts,
405                            NS_ooxml::LN_CT_Fonts_ascii, pFont);
406 }
407 
setNeedPar(bool bNeedPar)408 void RTFDocumentImpl::setNeedPar(bool bNeedPar) { m_bNeedPar = bNeedPar; }
409 
setNeedSect(bool bNeedSect)410 void RTFDocumentImpl::setNeedSect(bool bNeedSect)
411 {
412     if (!m_bNeedSect && bNeedSect && m_bFirstRun)
413     {
414         RTFLookahead aLookahead(Strm(), m_pTokenizer->getGroupStart());
415         if (aLookahead.hasTable() && aLookahead.hasColumns())
416         {
417             m_bFirstRunException = true;
418         }
419     }
420 
421     // ignore setting before checkFirstRun - every keyword calls setNeedSect!
422     // except the case of a table in a multicolumn section
423     if (!m_bNeedSect && bNeedSect && (!m_bFirstRun || m_bFirstRunException))
424     {
425         if (!m_pSuperstream) // no sections in header/footer!
426         {
427             Mapper().startSectionGroup();
428         }
429         // set flag in substream too - otherwise multiple startParagraphGroup
430         m_bNeedSect = bNeedSect;
431         Mapper().startParagraphGroup();
432         setNeedPar(true);
433     }
434     else if (m_bNeedSect && !bNeedSect)
435     {
436         m_bNeedSect = bNeedSect;
437     }
438 }
439 
440 /// Copy rProps to rStyleAttributes and rStyleSprms, but in case of nested sprms, copy their children as toplevel sprms/attributes.
lcl_copyFlatten(RTFReferenceProperties & rProps,RTFSprms & rStyleAttributes,RTFSprms & rStyleSprms)441 static void lcl_copyFlatten(RTFReferenceProperties& rProps, RTFSprms& rStyleAttributes,
442                             RTFSprms& rStyleSprms)
443 {
444     for (auto& rSprm : rProps.getSprms())
445     {
446         // createStyleProperties() puts properties to rPr, but here we need a flat list.
447         if (rSprm.first == NS_ooxml::LN_CT_Style_rPr)
448         {
449             // rPr can have both attributes and SPRMs, copy over both types.
450             RTFSprms& rRPrSprms = rSprm.second->getSprms();
451             for (const auto& rRPrSprm : rRPrSprms)
452                 rStyleSprms.set(rRPrSprm.first, rRPrSprm.second);
453 
454             RTFSprms& rRPrAttributes = rSprm.second->getAttributes();
455             for (const auto& rRPrAttribute : rRPrAttributes)
456                 rStyleAttributes.set(rRPrAttribute.first, rRPrAttribute.second);
457         }
458         else
459             rStyleSprms.set(rSprm.first, rSprm.second);
460     }
461 
462     RTFSprms& rAttributes = rProps.getAttributes();
463     for (const auto& rAttribute : rAttributes)
464         rStyleAttributes.set(rAttribute.first, rAttribute.second);
465 }
466 
467 writerfilter::Reference<Properties>::Pointer_t
getProperties(const RTFSprms & rAttributes,RTFSprms const & rSprms,Id nStyleType)468 RTFDocumentImpl::getProperties(const RTFSprms& rAttributes, RTFSprms const& rSprms, Id nStyleType)
469 {
470     RTFSprms aSprms(rSprms);
471     RTFValue::Pointer_t pAbstractList;
472     int nAbstractListId = -1;
473     RTFValue::Pointer_t pNumId
474         = getNestedSprm(aSprms, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_numId);
475     if (pNumId)
476     {
477         // We have a numbering, look up the abstract list for property
478         // deduplication and duplication.
479         auto itNumId = m_aListOverrideTable.find(pNumId->getInt());
480         if (itNumId != m_aListOverrideTable.end())
481         {
482             nAbstractListId = itNumId->second;
483             auto itAbstract = m_aListTable.find(nAbstractListId);
484             if (itAbstract != m_aListTable.end())
485                 pAbstractList = itAbstract->second;
486         }
487     }
488 
489     if (pAbstractList)
490     {
491         auto it = m_aInvalidListTableFirstIndents.find(nAbstractListId);
492         if (it != m_aInvalidListTableFirstIndents.end())
493             aSprms.deduplicateList(it->second);
494     }
495 
496     int nStyle = 0;
497     if (!m_aStates.empty())
498         nStyle = m_aStates.top().getCurrentStyleIndex();
499     auto it = m_aStyleTableEntries.find(nStyle);
500     if (it != m_aStyleTableEntries.end())
501     {
502         // cloneAndDeduplicate() wants to know about only a single "style", so
503         // let's merge paragraph and character style properties here.
504         auto itChar = m_aStyleTableEntries.end();
505         if (!m_aStates.empty())
506         {
507             int nCharStyle = m_aStates.top().getCurrentCharacterStyleIndex();
508             itChar = m_aStyleTableEntries.find(nCharStyle);
509         }
510 
511         RTFSprms aStyleSprms;
512         RTFSprms aStyleAttributes;
513         // Ensure the paragraph style is a flat list.
514         // Take paragraph style into account for character properties as well,
515         // as paragraph style may contain character properties.
516         RTFReferenceProperties& rProps = *static_cast<RTFReferenceProperties*>(it->second.get());
517         lcl_copyFlatten(rProps, aStyleAttributes, aStyleSprms);
518 
519         if (itChar != m_aStyleTableEntries.end())
520         {
521             // Found active character style, then update aStyleSprms/Attributes.
522             if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_character)
523             {
524                 RTFReferenceProperties& rCharProps
525                     = *static_cast<RTFReferenceProperties*>(itChar->second.get());
526                 lcl_copyFlatten(rCharProps, aStyleAttributes, aStyleSprms);
527             }
528         }
529 
530         // Get rid of direct formatting what is already in the style.
531         RTFSprms const sprms(aSprms.cloneAndDeduplicate(aStyleSprms, nStyleType, true, &aSprms));
532         RTFSprms const attributes(
533             rAttributes.cloneAndDeduplicate(aStyleAttributes, nStyleType, true));
534         return new RTFReferenceProperties(attributes, sprms);
535     }
536 
537     if (pAbstractList)
538         aSprms.duplicateList(pAbstractList);
539     writerfilter::Reference<Properties>::Pointer_t pRet
540         = new RTFReferenceProperties(rAttributes, aSprms);
541     return pRet;
542 }
543 
checkNeedPap()544 void RTFDocumentImpl::checkNeedPap()
545 {
546     if (!m_bNeedPap)
547         return;
548 
549     m_bNeedPap = false; // reset early, so we can avoid recursion when calling ourselves
550 
551     if (m_aStates.empty())
552         return;
553 
554     if (!m_aStates.top().getCurrentBuffer())
555     {
556         writerfilter::Reference<Properties>::Pointer_t const pParagraphProperties(getProperties(
557             m_aStates.top().getParagraphAttributes(), m_aStates.top().getParagraphSprms(),
558             NS_ooxml::LN_Value_ST_StyleType_paragraph));
559 
560         // Writer will ignore a page break before a text frame, so guard it with empty paragraphs
561         bool hasBreakBeforeFrame
562             = m_aStates.top().getFrame().hasProperties()
563               && m_aStates.top().getParagraphSprms().find(NS_ooxml::LN_CT_PPrBase_pageBreakBefore);
564         if (hasBreakBeforeFrame)
565         {
566             dispatchSymbol(RTFKeyword::PAR);
567             m_bNeedPap = false;
568         }
569         Mapper().props(pParagraphProperties);
570         if (hasBreakBeforeFrame)
571             dispatchSymbol(RTFKeyword::PAR);
572 
573         if (m_aStates.top().getFrame().hasProperties())
574         {
575             writerfilter::Reference<Properties>::Pointer_t const pFrameProperties(
576                 new RTFReferenceProperties(RTFSprms(), m_aStates.top().getFrame().getSprms()));
577             Mapper().props(pFrameProperties);
578         }
579     }
580     else
581     {
582         auto pValue = new RTFValue(m_aStates.top().getParagraphAttributes(),
583                                    m_aStates.top().getParagraphSprms());
584         bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue, nullptr);
585     }
586 }
587 
runProps()588 void RTFDocumentImpl::runProps()
589 {
590     if (!m_aStates.top().getCurrentBuffer())
591     {
592         Reference<Properties>::Pointer_t const pProperties = getProperties(
593             m_aStates.top().getCharacterAttributes(), m_aStates.top().getCharacterSprms(),
594             NS_ooxml::LN_Value_ST_StyleType_character);
595         Mapper().props(pProperties);
596     }
597     else
598     {
599         auto pValue = new RTFValue(m_aStates.top().getCharacterAttributes(),
600                                    m_aStates.top().getCharacterSprms());
601         bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue, nullptr);
602     }
603 
604     // Delete the sprm, so the trackchange range will be started only once.
605     // OTOH set a boolean flag, so we'll know we need to end the range later.
606     RTFValue::Pointer_t pTrackchange
607         = m_aStates.top().getCharacterSprms().find(NS_ooxml::LN_trackchange);
608     if (pTrackchange)
609     {
610         m_aStates.top().setStartedTrackchange(true);
611         m_aStates.top().getCharacterSprms().erase(NS_ooxml::LN_trackchange);
612     }
613 }
614 
runBreak()615 void RTFDocumentImpl::runBreak()
616 {
617     sal_uInt8 const sBreak[] = { 0xd };
618     Mapper().text(sBreak, 1);
619     m_bNeedCr = false;
620 }
621 
tableBreak()622 void RTFDocumentImpl::tableBreak()
623 {
624     runBreak();
625     Mapper().endParagraphGroup();
626     Mapper().startParagraphGroup();
627 }
628 
parBreak()629 void RTFDocumentImpl::parBreak()
630 {
631     checkFirstRun();
632     checkNeedPap();
633     // end previous paragraph
634     Mapper().startCharacterGroup();
635     runBreak();
636     Mapper().endCharacterGroup();
637     Mapper().endParagraphGroup();
638 
639     m_bHadPicture = false;
640 
641     // start new one
642     Mapper().startParagraphGroup();
643 }
644 
sectBreak(bool bFinal)645 void RTFDocumentImpl::sectBreak(bool bFinal)
646 {
647     SAL_INFO("writerfilter.rtf", __func__ << ": final? " << bFinal << ", needed? " << m_bNeedSect);
648     bool bNeedSect = m_bNeedSect;
649     RTFValue::Pointer_t pBreak
650         = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_type);
651     bool bContinuous
652         = pBreak
653           && pBreak->getInt()
654                  == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_continuous);
655     // If there is no paragraph in this section, then insert a dummy one, as required by Writer,
656     // unless this is the end of the doc, we had nothing since the last section break and this is not a continuous one.
657     // Also, when pasting, it's fine to not have any paragraph inside the document at all.
658     if (m_bNeedPar && (!bFinal || m_bNeedSect || bContinuous) && !isSubstream() && m_bIsNewDoc)
659         dispatchSymbol(RTFKeyword::PAR);
660     // It's allowed to not have a non-table paragraph at the end of an RTF doc, add it now if required.
661     if (m_bNeedFinalPar && bFinal)
662     {
663         dispatchFlag(RTFKeyword::PARD);
664         dispatchSymbol(RTFKeyword::PAR);
665         m_bNeedSect = bNeedSect;
666     }
667     while (!m_nHeaderFooterPositions.empty())
668     {
669         std::pair<Id, std::size_t> aPair = m_nHeaderFooterPositions.front();
670         m_nHeaderFooterPositions.pop();
671         resolveSubstream(aPair.second, aPair.first);
672     }
673 
674     // Normally a section break at the end of the doc is necessary. Unless the
675     // last control word in the document is a section break itself.
676     if (!bNeedSect || !m_bHadSect)
677     {
678         // In case the last section is a continuous one, we don't need to output a section break.
679         if (bFinal && bContinuous)
680             m_aStates.top().getSectionSprms().erase(NS_ooxml::LN_EG_SectPrContents_type);
681     }
682 
683     // Section properties are a paragraph sprm.
684     auto pValue
685         = new RTFValue(m_aStates.top().getSectionAttributes(), m_aStates.top().getSectionSprms());
686     RTFSprms aAttributes;
687     RTFSprms aSprms;
688     aSprms.set(NS_ooxml::LN_CT_PPr_sectPr, pValue);
689     writerfilter::Reference<Properties>::Pointer_t pProperties
690         = new RTFReferenceProperties(aAttributes, aSprms);
691 
692     if (bFinal && !m_pSuperstream)
693         // This is the end of the document, not just the end of e.g. a header.
694         // This makes sure that dmapper can set DontBalanceTextColumns=true for this section if necessary.
695         Mapper().markLastSectionGroup();
696 
697     // The trick is that we send properties of the previous section right now, which will be exactly what dmapper expects.
698     Mapper().props(pProperties);
699     Mapper().endParagraphGroup();
700 
701     // End Section
702     if (!m_pSuperstream)
703     {
704         m_hasFHeader = false;
705         m_hasRHeader = false;
706         m_hasRFooter = false;
707         m_hasFFooter = false;
708         Mapper().endSectionGroup();
709     }
710     m_bNeedPar = false;
711     m_bNeedSect = false;
712 }
713 
getColorTable(sal_uInt32 nIndex)714 Color RTFDocumentImpl::getColorTable(sal_uInt32 nIndex)
715 {
716     if (!m_pSuperstream)
717     {
718         if (nIndex < m_aColorTable.size())
719             return m_aColorTable[nIndex];
720         return 0;
721     }
722 
723     return m_pSuperstream->getColorTable(nIndex);
724 }
725 
getEncoding(int nFontIndex)726 rtl_TextEncoding RTFDocumentImpl::getEncoding(int nFontIndex)
727 {
728     if (!m_pSuperstream)
729     {
730         auto it = m_aFontEncodings.find(nFontIndex);
731         if (it != m_aFontEncodings.end())
732             // We have a font encoding associated to this font.
733             return it->second;
734         if (m_aDefaultState.getCurrentEncoding() != rtl_getTextEncodingFromWindowsCharset(0))
735             // We have a default encoding.
736             return m_aDefaultState.getCurrentEncoding();
737         // Guess based on locale.
738         return msfilter::util::getBestTextEncodingFromLocale(
739             Application::GetSettings().GetLanguageTag().getLocale());
740     }
741 
742     return m_pSuperstream->getEncoding(nFontIndex);
743 }
744 
getFontName(int nIndex)745 OUString RTFDocumentImpl::getFontName(int nIndex)
746 {
747     if (!m_pSuperstream)
748         return m_aFontNames[nIndex];
749 
750     return m_pSuperstream->getFontName(nIndex);
751 }
752 
getFontIndex(int nIndex)753 int RTFDocumentImpl::getFontIndex(int nIndex)
754 {
755     if (!m_pSuperstream)
756         return std::find(m_aFontIndexes.begin(), m_aFontIndexes.end(), nIndex)
757                - m_aFontIndexes.begin();
758 
759     return m_pSuperstream->getFontIndex(nIndex);
760 }
761 
getStyleName(int nIndex)762 OUString RTFDocumentImpl::getStyleName(int nIndex)
763 {
764     if (!m_pSuperstream)
765     {
766         OUString aRet;
767         if (m_aStyleNames.find(nIndex) != m_aStyleNames.end())
768             aRet = m_aStyleNames[nIndex];
769         return aRet;
770     }
771 
772     return m_pSuperstream->getStyleName(nIndex);
773 }
774 
getStyleType(int nIndex)775 Id RTFDocumentImpl::getStyleType(int nIndex)
776 {
777     if (!m_pSuperstream)
778     {
779         Id nRet = 0;
780         if (m_aStyleTypes.find(nIndex) != m_aStyleTypes.end())
781             nRet = m_aStyleTypes[nIndex];
782         return nRet;
783     }
784 
785     return m_pSuperstream->getStyleType(nIndex);
786 }
787 
getDefaultState()788 RTFParserState& RTFDocumentImpl::getDefaultState()
789 {
790     if (!m_pSuperstream)
791         return m_aDefaultState;
792 
793     return m_pSuperstream->getDefaultState();
794 }
795 
getGraphicHelper()796 oox::GraphicHelper& RTFDocumentImpl::getGraphicHelper() { return *m_pGraphicHelper; }
797 
isStyleSheetImport()798 bool RTFDocumentImpl::isStyleSheetImport()
799 {
800     if (m_aStates.empty())
801         return false;
802     Destination eDestination = m_aStates.top().getDestination();
803     return eDestination == Destination::STYLESHEET || eDestination == Destination::STYLEENTRY;
804 }
805 
resolve(Stream & rMapper)806 void RTFDocumentImpl::resolve(Stream& rMapper)
807 {
808     m_pMapperStream = &rMapper;
809     switch (m_pTokenizer->resolveParse())
810     {
811         case RTFError::OK:
812             SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: finished without errors");
813             break;
814         case RTFError::GROUP_UNDER:
815             SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unmatched '}'");
816             break;
817         case RTFError::GROUP_OVER:
818             SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unmatched '{'");
819             throw io::WrongFormatException(m_pTokenizer->getPosition());
820             break;
821         case RTFError::UNEXPECTED_EOF:
822             SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unexpected end of file");
823             throw io::WrongFormatException(m_pTokenizer->getPosition());
824             break;
825         case RTFError::HEX_INVALID:
826             SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: invalid hex char");
827             throw io::WrongFormatException(m_pTokenizer->getPosition());
828             break;
829         case RTFError::CHAR_OVER:
830             SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: characters after last '}'");
831             break;
832         case RTFError::CLASSIFICATION:
833             SAL_INFO("writerfilter.rtf",
834                      "RTFDocumentImpl::resolve: classification prevented paste");
835             break;
836     }
837 }
838 
resolvePict(bool const bInline,uno::Reference<drawing::XShape> const & rShape)839 void RTFDocumentImpl::resolvePict(bool const bInline, uno::Reference<drawing::XShape> const& rShape)
840 {
841     SvMemoryStream aStream;
842     SvStream* pStream = nullptr;
843     if (!m_pBinaryData)
844     {
845         pStream = &aStream;
846         int b = 0;
847         int count = 2;
848 
849         // Feed the destination text to a stream.
850         OString aStr = OUStringToOString(m_aStates.top().getDestinationText().makeStringAndClear(),
851                                          RTL_TEXTENCODING_ASCII_US);
852         for (int i = 0; i < aStr.getLength(); ++i)
853         {
854             char ch = aStr[i];
855             if (ch != 0x0d && ch != 0x0a && ch != 0x20)
856             {
857                 b = b << 4;
858                 sal_Int8 parsed = msfilter::rtfutil::AsHex(ch);
859                 if (parsed == -1)
860                     return;
861                 b += parsed;
862                 count--;
863                 if (!count)
864                 {
865                     aStream.WriteChar(static_cast<char>(b));
866                     count = 2;
867                     b = 0;
868                 }
869             }
870         }
871     }
872     else
873         pStream = m_pBinaryData.get();
874 
875     if (!pStream->Tell())
876         // No destination text? Then we'll get it later.
877         return;
878 
879     SvMemoryStream aDIBStream;
880     if (m_aStates.top().getPicture().eStyle == RTFBmpStyle::DIBITMAP)
881     {
882         // Construct a BITMAPFILEHEADER structure before the real data.
883         SvStream& rBodyStream = *pStream;
884         aDIBStream.WriteChar('B');
885         aDIBStream.WriteChar('M');
886         // The size of the real data.
887         aDIBStream.WriteUInt32(rBodyStream.Tell());
888         // Reserved.
889         aDIBStream.WriteUInt32(0);
890         // The offset of the real data, i.e. the size of the header, including this number.
891         aDIBStream.WriteUInt32(14);
892         rBodyStream.Seek(0);
893         aDIBStream.WriteStream(rBodyStream);
894         pStream = &aDIBStream;
895     }
896 
897     // Store, and get its URL.
898     pStream->Seek(0);
899     uno::Reference<io::XInputStream> xInputStream(new utl::OInputStreamWrapper(pStream));
900     WmfExternal aExtHeader;
901     aExtHeader.mapMode = m_aStates.top().getPicture().eWMetafile;
902     if (m_aStates.top().getPicture().nGoalWidth == 0
903         || m_aStates.top().getPicture().nGoalHeight == 0)
904     {
905         // Don't use the values provided by picw and pich if the desired size is provided.
906 
907         aExtHeader.xExt = sal_uInt16(std::clamp<sal_Int32>(
908             m_aStates.top().getPicture().nWidth, 0,
909             SAL_MAX_UINT16)); //TODO: better way to handle out-of-bounds values?
910         aExtHeader.yExt = sal_uInt16(std::clamp<sal_Int32>(
911             m_aStates.top().getPicture().nHeight, 0,
912             SAL_MAX_UINT16)); //TODO: better way to handle out-of-bounds values?
913     }
914     WmfExternal* pExtHeader = &aExtHeader;
915     uno::Reference<lang::XServiceInfo> xServiceInfo(m_aStates.top().getDrawingObject().getShape(),
916                                                     uno::UNO_QUERY);
917     if (xServiceInfo.is() && xServiceInfo->supportsService("com.sun.star.text.TextFrame"))
918         pExtHeader = nullptr;
919 
920     uno::Reference<graphic::XGraphic> xGraphic
921         = m_pGraphicHelper->importGraphic(xInputStream, pExtHeader);
922 
923     if (m_aStates.top().getPicture().eStyle != RTFBmpStyle::NONE)
924     {
925         // In case of PNG/JPEG, the real size is known, don't use the values
926         // provided by picw and pich.
927 
928         Graphic aGraphic(xGraphic);
929         Size aSize(aGraphic.GetPrefSize());
930         MapMode aMap(MapUnit::Map100thMM);
931         if (aGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel)
932             aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMap);
933         else
934             aSize = OutputDevice::LogicToLogic(aSize, aGraphic.GetPrefMapMode(), aMap);
935         m_aStates.top().getPicture().nWidth = aSize.Width();
936         m_aStates.top().getPicture().nHeight = aSize.Height();
937     }
938 
939     // Wrap it in an XShape.
940     uno::Reference<drawing::XShape> xShape(rShape);
941     if (xShape.is())
942     {
943         uno::Reference<lang::XServiceInfo> xSI(xShape, uno::UNO_QUERY_THROW);
944         if (!xSI->supportsService("com.sun.star.drawing.GraphicObjectShape"))
945         {
946             // it's sometimes an error to get here - but it's possible to have
947             // a \pict inside the \shptxt of a \shp of shapeType 202 "TextBox"
948             // and in that case xShape is the text frame; we actually need a
949             // new GraphicObject then (example: fdo37691-1.rtf)
950             SAL_INFO("writerfilter.rtf",
951                      "cannot set graphic on existing shape, creating a new GraphicObjectShape");
952             xShape.clear();
953         }
954     }
955     if (!xShape.is())
956     {
957         if (m_xModelFactory.is())
958             xShape.set(m_xModelFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"),
959                        uno::UNO_QUERY);
960         uno::Reference<drawing::XDrawPageSupplier> const xDrawSupplier(m_xDstDoc, uno::UNO_QUERY);
961         if (xDrawSupplier.is())
962         {
963             uno::Reference<drawing::XShapes> xShapes = xDrawSupplier->getDrawPage();
964             if (xShapes.is())
965                 xShapes->add(xShape);
966         }
967     }
968 
969     uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
970 
971     if (xPropertySet.is())
972         xPropertySet->setPropertyValue("Graphic", uno::Any(xGraphic));
973 
974     // check if the picture is in an OLE object and if the \objdata element is used
975     // (see RTFKeyword::OBJECT in RTFDocumentImpl::dispatchDestination)
976     if (m_bObject)
977     {
978         // Set the object size
979         awt::Size aSize;
980         aSize.Width
981             = (m_aStates.top().getPicture().nGoalWidth ? m_aStates.top().getPicture().nGoalWidth
982                                                        : m_aStates.top().getPicture().nWidth);
983         aSize.Height
984             = (m_aStates.top().getPicture().nGoalHeight ? m_aStates.top().getPicture().nGoalHeight
985                                                         : m_aStates.top().getPicture().nHeight);
986         xShape->setSize(aSize);
987 
988         // Replacement graphic is inline by default, see oox::vml::SimpleShape::implConvertAndInsert().
989         xPropertySet->setPropertyValue("AnchorType",
990                                        uno::makeAny(text::TextContentAnchorType_AS_CHARACTER));
991 
992         auto pShapeValue = new RTFValue(xShape);
993         m_aObjectAttributes.set(NS_ooxml::LN_shape, pShapeValue);
994         return;
995     }
996 
997     if (m_aStates.top().getInListpicture())
998     {
999         // Send the shape directly, no section is started, to additional properties will be ignored anyway.
1000         Mapper().startShape(xShape);
1001         Mapper().endShape();
1002         return;
1003     }
1004 
1005     // Send it to the dmapper.
1006     RTFSprms aSprms;
1007     RTFSprms aAttributes;
1008     // shape attribute
1009     RTFSprms aPicAttributes;
1010     auto pShapeValue = new RTFValue(xShape);
1011     aPicAttributes.set(NS_ooxml::LN_shape, pShapeValue);
1012     // pic sprm
1013     RTFSprms aGraphicDataAttributes;
1014     RTFSprms aGraphicDataSprms;
1015     auto pPicValue = new RTFValue(aPicAttributes);
1016     aGraphicDataSprms.set(NS_ooxml::LN_pic_pic, pPicValue);
1017     // graphicData sprm
1018     RTFSprms aGraphicAttributes;
1019     RTFSprms aGraphicSprms;
1020     auto pGraphicDataValue = new RTFValue(aGraphicDataAttributes, aGraphicDataSprms);
1021     aGraphicSprms.set(NS_ooxml::LN_CT_GraphicalObject_graphicData, pGraphicDataValue);
1022     // graphic sprm
1023     auto pGraphicValue = new RTFValue(aGraphicAttributes, aGraphicSprms);
1024     // extent sprm
1025     RTFSprms aExtentAttributes;
1026     int nXExt = (m_aStates.top().getPicture().nGoalWidth ? m_aStates.top().getPicture().nGoalWidth
1027                                                          : m_aStates.top().getPicture().nWidth);
1028     int nYExt = (m_aStates.top().getPicture().nGoalHeight ? m_aStates.top().getPicture().nGoalHeight
1029                                                           : m_aStates.top().getPicture().nHeight);
1030     if (m_aStates.top().getPicture().nScaleX != 100)
1031         nXExt = (static_cast<tools::Long>(m_aStates.top().getPicture().nScaleX)
1032                  * (nXExt
1033                     - (m_aStates.top().getPicture().nCropL + m_aStates.top().getPicture().nCropR)))
1034                 / 100L;
1035     if (m_aStates.top().getPicture().nScaleY != 100)
1036         nYExt = (static_cast<tools::Long>(m_aStates.top().getPicture().nScaleY)
1037                  * (nYExt
1038                     - (m_aStates.top().getPicture().nCropT + m_aStates.top().getPicture().nCropB)))
1039                 / 100L;
1040     if (m_aStates.top().getInShape())
1041     {
1042         // Picture in shape: it looks like pib picture, so we will stretch the picture to shape size (tdf#49893)
1043         nXExt = m_aStates.top().getShape().getRight() - m_aStates.top().getShape().getLeft();
1044         nYExt = m_aStates.top().getShape().getBottom() - m_aStates.top().getShape().getTop();
1045     }
1046     auto pXExtValue = new RTFValue(oox::drawingml::convertHmmToEmu(nXExt));
1047     auto pYExtValue = new RTFValue(oox::drawingml::convertHmmToEmu(nYExt));
1048     aExtentAttributes.set(NS_ooxml::LN_CT_PositiveSize2D_cx, pXExtValue);
1049     aExtentAttributes.set(NS_ooxml::LN_CT_PositiveSize2D_cy, pYExtValue);
1050     auto pExtentValue = new RTFValue(aExtentAttributes);
1051     // docpr sprm
1052     RTFSprms aDocprAttributes;
1053     for (const auto& rCharacterAttribute : m_aStates.top().getCharacterAttributes())
1054         if (rCharacterAttribute.first == NS_ooxml::LN_CT_NonVisualDrawingProps_name
1055             || rCharacterAttribute.first == NS_ooxml::LN_CT_NonVisualDrawingProps_descr)
1056             aDocprAttributes.set(rCharacterAttribute.first, rCharacterAttribute.second);
1057     auto pDocprValue = new RTFValue(aDocprAttributes);
1058     if (bInline)
1059     {
1060         RTFSprms aInlineAttributes;
1061         aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distT, new RTFValue(0));
1062         aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distB, new RTFValue(0));
1063         aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distL, new RTFValue(0));
1064         aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distR, new RTFValue(0));
1065         RTFSprms aInlineSprms;
1066         aInlineSprms.set(NS_ooxml::LN_CT_Inline_extent, pExtentValue);
1067         aInlineSprms.set(NS_ooxml::LN_CT_Inline_docPr, pDocprValue);
1068         aInlineSprms.set(NS_ooxml::LN_graphic_graphic, pGraphicValue);
1069         // inline sprm
1070         auto pValue = new RTFValue(aInlineAttributes, aInlineSprms);
1071         aSprms.set(NS_ooxml::LN_inline_inline, pValue);
1072     }
1073     else // anchored
1074     {
1075         // wrap sprm
1076         RTFSprms aAnchorWrapAttributes;
1077         m_aStates.top().getShape().getAnchorAttributes().set(
1078             NS_ooxml::LN_CT_Anchor_behindDoc,
1079             new RTFValue((m_aStates.top().getShape().getInBackground()) ? 1 : 0));
1080         RTFSprms aAnchorSprms;
1081         for (const auto& rCharacterAttribute : m_aStates.top().getCharacterAttributes())
1082         {
1083             if (rCharacterAttribute.first == NS_ooxml::LN_CT_WrapSquare_wrapText)
1084                 aAnchorWrapAttributes.set(rCharacterAttribute.first, rCharacterAttribute.second);
1085         }
1086         sal_Int32 nWrap = -1;
1087         for (auto& rCharacterSprm : m_aStates.top().getCharacterSprms())
1088         {
1089             if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapNone
1090                 || rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight)
1091             {
1092                 nWrap = rCharacterSprm.first;
1093 
1094                 // If there is a wrap polygon prepared by RTFSdrImport, pick it up here.
1095                 if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight
1096                     && !m_aStates.top().getShape().getWrapPolygonSprms().empty())
1097                     rCharacterSprm.second->getSprms().set(
1098                         NS_ooxml::LN_CT_WrapTight_wrapPolygon,
1099                         new RTFValue(RTFSprms(), m_aStates.top().getShape().getWrapPolygonSprms()));
1100 
1101                 aAnchorSprms.set(rCharacterSprm.first, rCharacterSprm.second);
1102             }
1103         }
1104 
1105         if (m_aStates.top().getShape().getWrapSprm().first != 0)
1106             // Replay of a buffered shape, wrap sprm there has priority over
1107             // character sprms of the current state.
1108             aAnchorSprms.set(m_aStates.top().getShape().getWrapSprm().first,
1109                              m_aStates.top().getShape().getWrapSprm().second);
1110 
1111         aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_extent, pExtentValue);
1112         if (!aAnchorWrapAttributes.empty() && nWrap == -1)
1113             aAnchorSprms.set(NS_ooxml::LN_EG_WrapType_wrapSquare,
1114                              new RTFValue(aAnchorWrapAttributes));
1115 
1116         // See OOXMLFastContextHandler::positionOffset(), we can't just put offset values in an RTFValue.
1117         RTFSprms aPoshAttributes;
1118         RTFSprms aPoshSprms;
1119         if (m_aStates.top().getShape().getHoriOrientRelationToken() > 0)
1120             aPoshAttributes.set(
1121                 NS_ooxml::LN_CT_PosH_relativeFrom,
1122                 new RTFValue(m_aStates.top().getShape().getHoriOrientRelationToken()));
1123         if (m_aStates.top().getShape().getLeft() != 0)
1124         {
1125             Mapper().positionOffset(OUString::number(oox::drawingml::convertHmmToEmu(
1126                                         m_aStates.top().getShape().getLeft())),
1127                                     /*bVertical=*/false);
1128             aPoshSprms.set(NS_ooxml::LN_CT_PosH_posOffset, new RTFValue());
1129         }
1130         aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_positionH,
1131                          new RTFValue(aPoshAttributes, aPoshSprms));
1132 
1133         RTFSprms aPosvAttributes;
1134         RTFSprms aPosvSprms;
1135         if (m_aStates.top().getShape().getVertOrientRelationToken() > 0)
1136             aPosvAttributes.set(
1137                 NS_ooxml::LN_CT_PosV_relativeFrom,
1138                 new RTFValue(m_aStates.top().getShape().getVertOrientRelationToken()));
1139         if (m_aStates.top().getShape().getTop() != 0)
1140         {
1141             Mapper().positionOffset(OUString::number(oox::drawingml::convertHmmToEmu(
1142                                         m_aStates.top().getShape().getTop())),
1143                                     /*bVertical=*/true);
1144             aPosvSprms.set(NS_ooxml::LN_CT_PosV_posOffset, new RTFValue());
1145         }
1146         aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_positionV,
1147                          new RTFValue(aPosvAttributes, aPosvSprms));
1148 
1149         aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_docPr, pDocprValue);
1150         aAnchorSprms.set(NS_ooxml::LN_graphic_graphic, pGraphicValue);
1151         // anchor sprm
1152         auto pValue = new RTFValue(m_aStates.top().getShape().getAnchorAttributes(), aAnchorSprms);
1153         aSprms.set(NS_ooxml::LN_anchor_anchor, pValue);
1154     }
1155     writerfilter::Reference<Properties>::Pointer_t pProperties
1156         = new RTFReferenceProperties(aAttributes, aSprms);
1157     checkFirstRun();
1158 
1159     if (!m_aStates.top().getCurrentBuffer())
1160     {
1161         Mapper().props(pProperties);
1162         // Make sure we don't lose these properties with a too early reset.
1163         m_bHadPicture = true;
1164     }
1165     else
1166     {
1167         auto pValue = new RTFValue(aAttributes, aSprms);
1168         bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue, nullptr);
1169     }
1170 }
1171 
resolveChars(char ch)1172 RTFError RTFDocumentImpl::resolveChars(char ch)
1173 {
1174     if (m_aStates.top().getInternalState() == RTFInternalState::BIN)
1175     {
1176         m_pBinaryData = std::make_shared<SvMemoryStream>();
1177         m_pBinaryData->WriteChar(ch);
1178         for (int i = 0; i < m_aStates.top().getBinaryToRead() - 1; ++i)
1179         {
1180             Strm().ReadChar(ch);
1181             m_pBinaryData->WriteChar(ch);
1182         }
1183         m_aStates.top().setInternalState(RTFInternalState::NORMAL);
1184         return RTFError::OK;
1185     }
1186 
1187     OStringBuffer aBuf(512);
1188 
1189     bool bUnicodeChecked = false;
1190     bool bSkipped = false;
1191 
1192     while (!Strm().eof()
1193            && (m_aStates.top().getInternalState() == RTFInternalState::HEX
1194                || (ch != '{' && ch != '}' && ch != '\\')))
1195     {
1196         if (m_aStates.top().getInternalState() == RTFInternalState::HEX
1197             || (ch != 0x0d && ch != 0x0a))
1198         {
1199             if (m_aStates.top().getCharsToSkip() == 0)
1200             {
1201                 if (!bUnicodeChecked)
1202                 {
1203                     checkUnicode(/*bUnicode =*/true, /*bHex =*/false);
1204                     bUnicodeChecked = true;
1205                 }
1206                 aBuf.append(ch);
1207             }
1208             else
1209             {
1210                 bSkipped = true;
1211                 m_aStates.top().getCharsToSkip()--;
1212             }
1213         }
1214 
1215         // read a single char if we're in hex mode
1216         if (m_aStates.top().getInternalState() == RTFInternalState::HEX)
1217             break;
1218 
1219         if (RTL_TEXTENCODING_MS_932 == m_aStates.top().getCurrentEncoding())
1220         {
1221             unsigned char uch = ch;
1222             if ((uch >= 0x80 && uch <= 0x9F) || uch >= 0xE0)
1223             {
1224                 // read second byte of 2-byte Shift-JIS - may be \ { }
1225                 Strm().ReadChar(ch);
1226                 if (m_aStates.top().getCharsToSkip() == 0)
1227                 {
1228                     // fdo#79384: Word will reject Shift-JIS following \loch
1229                     // but apparently OOo could read and (worse) write such documents
1230                     SAL_INFO_IF(m_aStates.top().getRunType() != RTFParserState::RunType::DBCH,
1231                                 "writerfilter.rtf", "invalid Shift-JIS without DBCH");
1232                     assert(bUnicodeChecked);
1233                     aBuf.append(ch);
1234                 }
1235                 else
1236                 {
1237                     assert(bSkipped);
1238                     // anybody who uses \ucN with Shift-JIS is insane
1239                     m_aStates.top().getCharsToSkip()--;
1240                 }
1241             }
1242         }
1243 
1244         Strm().ReadChar(ch);
1245     }
1246     if (m_aStates.top().getInternalState() != RTFInternalState::HEX && !Strm().eof())
1247         Strm().SeekRel(-1);
1248 
1249     if (m_aStates.top().getInternalState() == RTFInternalState::HEX
1250         && m_aStates.top().getDestination() != Destination::LEVELNUMBERS)
1251     {
1252         if (!bSkipped)
1253         {
1254             // note: apparently \'0d\'0a is interpreted as 2 breaks, not 1
1255             if ((ch == '\r' || ch == '\n')
1256                 && m_aStates.top().getDestination() != Destination::DOCCOMM
1257                 && m_aStates.top().getDestination() != Destination::LEVELNUMBERS
1258                 && m_aStates.top().getDestination() != Destination::LEVELTEXT)
1259             {
1260                 checkUnicode(/*bUnicode =*/false, /*bHex =*/true);
1261                 dispatchSymbol(RTFKeyword::PAR);
1262             }
1263             else
1264             {
1265                 m_aHexBuffer.append(ch);
1266             }
1267         }
1268         return RTFError::OK;
1269     }
1270 
1271     if (m_aStates.top().getDestination() == Destination::SKIP)
1272         return RTFError::OK;
1273     OString aStr = aBuf.makeStringAndClear();
1274     if (m_aStates.top().getDestination() == Destination::LEVELNUMBERS)
1275     {
1276         if (aStr.toChar() != ';')
1277             m_aStates.top().getLevelNumbers().push_back(sal_Int32(ch));
1278         return RTFError::OK;
1279     }
1280 
1281     SAL_INFO("writerfilter.rtf",
1282              "RTFDocumentImpl::resolveChars: collected '"
1283                  << OStringToOUString(aStr, m_aStates.top().getCurrentEncoding()) << "'");
1284 
1285     if (m_aStates.top().getDestination() == Destination::COLORTABLE)
1286     {
1287         // we hit a ';' at the end of each color entry
1288         m_aColorTable.push_back(m_aStates.top().getCurrentColor().GetColor());
1289         // set components back to zero
1290         m_aStates.top().getCurrentColor() = RTFColorTableEntry();
1291     }
1292     else if (!aStr.isEmpty())
1293         m_aHexBuffer.append(aStr);
1294 
1295     checkUnicode(/*bUnicode =*/false, /*bHex =*/true);
1296     return RTFError::OK;
1297 }
1298 
inFrame() const1299 bool RTFFrame::inFrame() const { return m_nW > 0 || m_nH > 0 || m_nX > 0 || m_nY > 0; }
1300 
singleChar(sal_uInt8 nValue,bool bRunProps)1301 void RTFDocumentImpl::singleChar(sal_uInt8 nValue, bool bRunProps)
1302 {
1303     sal_uInt8 sValue[] = { nValue };
1304     RTFBuffer_t* pCurrentBuffer = m_aStates.top().getCurrentBuffer();
1305 
1306     if (!pCurrentBuffer)
1307     {
1308         Mapper().startCharacterGroup();
1309         // Should we send run properties?
1310         if (bRunProps)
1311             runProps();
1312         Mapper().text(sValue, 1);
1313         Mapper().endCharacterGroup();
1314     }
1315     else
1316     {
1317         pCurrentBuffer->push_back(Buf_t(BUFFER_STARTRUN, nullptr, nullptr));
1318         auto pValue = new RTFValue(*sValue);
1319         pCurrentBuffer->push_back(Buf_t(BUFFER_TEXT, pValue, nullptr));
1320         pCurrentBuffer->push_back(Buf_t(BUFFER_ENDRUN, nullptr, nullptr));
1321     }
1322 }
1323 
text(OUString & rString)1324 void RTFDocumentImpl::text(OUString& rString)
1325 {
1326     if (rString.getLength() == 1 && m_aStates.top().getDestination() != Destination::DOCCOMM)
1327     {
1328         // No cheating! Tokenizer ignores bare \r and \n, their hex \'0d / \'0a form doesn't count, either.
1329         sal_Unicode ch = rString[0];
1330         if (ch == 0x0d || ch == 0x0a)
1331             return;
1332     }
1333 
1334     bool bRet = true;
1335     switch (m_aStates.top().getDestination())
1336     {
1337         // Note: in fonttbl there may or may not be groups; in stylesheet
1338         // and revtbl groups are mandatory
1339         case Destination::FONTTABLE:
1340         case Destination::FONTENTRY:
1341         case Destination::STYLEENTRY:
1342         case Destination::LISTNAME:
1343         case Destination::REVISIONENTRY:
1344         {
1345             // ; is the end of the entry
1346             bool bEnd = false;
1347             if (rString.endsWith(";"))
1348             {
1349                 rString = rString.copy(0, rString.getLength() - 1);
1350                 bEnd = true;
1351             }
1352             m_aStates.top().appendDestinationText(rString);
1353             if (bEnd)
1354             {
1355                 // always clear, necessary in case of group-less fonttable
1356                 OUString const aName
1357                     = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
1358                 switch (m_aStates.top().getDestination())
1359                 {
1360                     case Destination::FONTTABLE:
1361                     case Destination::FONTENTRY:
1362                     {
1363                         m_aFontNames[m_nCurrentFontIndex] = aName;
1364                         if (m_nCurrentEncoding >= 0)
1365                         {
1366                             m_aFontEncodings[m_nCurrentFontIndex] = m_nCurrentEncoding;
1367                             m_nCurrentEncoding = -1;
1368                         }
1369                         m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Font_name,
1370                                                                  new RTFValue(aName));
1371 
1372                         writerfilter::Reference<Properties>::Pointer_t const pProp(
1373                             new RTFReferenceProperties(m_aStates.top().getTableAttributes(),
1374                                                        m_aStates.top().getTableSprms()));
1375 
1376                         //See fdo#47347 initial invalid font entry properties are inserted first,
1377                         //so when we attempt to insert the correct ones, there's already an
1378                         //entry in the map for them, so the new ones aren't inserted.
1379                         auto lb = m_aFontTableEntries.lower_bound(m_nCurrentFontIndex);
1380                         if (lb != m_aFontTableEntries.end()
1381                             && !(m_aFontTableEntries.key_comp()(m_nCurrentFontIndex, lb->first)))
1382                             lb->second = pProp;
1383                         else
1384                             m_aFontTableEntries.insert(lb,
1385                                                        std::make_pair(m_nCurrentFontIndex, pProp));
1386                     }
1387                     break;
1388                     case Destination::STYLEENTRY:
1389                     {
1390                         RTFValue::Pointer_t pType
1391                             = m_aStates.top().getTableAttributes().find(NS_ooxml::LN_CT_Style_type);
1392                         if (pType)
1393                         {
1394                             // Word strips whitespace around style names.
1395                             m_aStyleNames[m_nCurrentStyleIndex] = aName.trim();
1396                             m_aStyleTypes[m_nCurrentStyleIndex] = pType->getInt();
1397                             auto pValue = new RTFValue(aName.trim());
1398                             m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Style_styleId,
1399                                                                      pValue);
1400                             m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Style_name, pValue);
1401 
1402                             writerfilter::Reference<Properties>::Pointer_t const pProp(
1403                                 createStyleProperties());
1404                             m_aStyleTableEntries.insert(
1405                                 std::make_pair(m_nCurrentStyleIndex, pProp));
1406                         }
1407                         else
1408                             SAL_INFO("writerfilter.rtf", "no RTF style type defined, ignoring");
1409                         break;
1410                     }
1411                     case Destination::LISTNAME:
1412                         // TODO: what can be done with a list name?
1413                         break;
1414                     case Destination::REVISIONENTRY:
1415                         m_aAuthors[m_aAuthors.size()] = aName;
1416                         break;
1417                     default:
1418                         break;
1419                 }
1420                 resetAttributes();
1421                 resetSprms();
1422             }
1423         }
1424         break;
1425         case Destination::LEVELTEXT:
1426         case Destination::SHAPEPROPERTYNAME:
1427         case Destination::SHAPEPROPERTYVALUE:
1428         case Destination::BOOKMARKEND:
1429         case Destination::PICT:
1430         case Destination::SHAPEPROPERTYVALUEPICT:
1431         case Destination::FORMFIELDNAME:
1432         case Destination::FORMFIELDLIST:
1433         case Destination::DATAFIELD:
1434         case Destination::AUTHOR:
1435         case Destination::KEYWORDS:
1436         case Destination::OPERATOR:
1437         case Destination::COMPANY:
1438         case Destination::COMMENT:
1439         case Destination::OBJDATA:
1440         case Destination::OBJCLASS:
1441         case Destination::ANNOTATIONDATE:
1442         case Destination::ANNOTATIONAUTHOR:
1443         case Destination::ANNOTATIONREFERENCE:
1444         case Destination::FALT:
1445         case Destination::PARAGRAPHNUMBERING_TEXTAFTER:
1446         case Destination::PARAGRAPHNUMBERING_TEXTBEFORE:
1447         case Destination::TITLE:
1448         case Destination::SUBJECT:
1449         case Destination::DOCCOMM:
1450         case Destination::ATNID:
1451         case Destination::ANNOTATIONREFERENCESTART:
1452         case Destination::ANNOTATIONREFERENCEEND:
1453         case Destination::MR:
1454         case Destination::MCHR:
1455         case Destination::MPOS:
1456         case Destination::MVERTJC:
1457         case Destination::MSTRIKEH:
1458         case Destination::MDEGHIDE:
1459         case Destination::MBEGCHR:
1460         case Destination::MSEPCHR:
1461         case Destination::MENDCHR:
1462         case Destination::MSUBHIDE:
1463         case Destination::MSUPHIDE:
1464         case Destination::MTYPE:
1465         case Destination::MGROW:
1466         case Destination::INDEXENTRY:
1467         case Destination::TOCENTRY:
1468         case Destination::PROPNAME:
1469         case Destination::STATICVAL:
1470             m_aStates.top().appendDestinationText(rString);
1471             break;
1472         case Destination::GENERATOR:
1473             // don't enlarge space sequences, eg. it was saved in LibreOffice
1474             if (!rString.startsWithIgnoreAsciiCase("Microsoft"))
1475                 m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_longerSpaceSequence,
1476                                           new RTFValue(0));
1477             break;
1478         default:
1479             bRet = false;
1480             break;
1481     }
1482     if (bRet)
1483         return;
1484 
1485     if (!m_aIgnoreFirst.isEmpty() && m_aIgnoreFirst == rString)
1486     {
1487         m_aIgnoreFirst.clear();
1488         return;
1489     }
1490 
1491     // Are we in the middle of the table definition? (No cell defs yet, but we already have some cell props.)
1492     if (m_aStates.top().getTableCellSprms().find(NS_ooxml::LN_CT_TcPrBase_vAlign)
1493         && m_nTopLevelCells == 0)
1494     {
1495         m_aTableBufferStack.back().emplace_back(BUFFER_UTEXT, new RTFValue(rString), nullptr);
1496         return;
1497     }
1498 
1499     checkFirstRun();
1500     checkNeedPap();
1501 
1502     // Don't return earlier, a bookmark start has to be in a paragraph group.
1503     if (m_aStates.top().getDestination() == Destination::BOOKMARKSTART)
1504     {
1505         m_aStates.top().appendDestinationText(rString);
1506         return;
1507     }
1508 
1509     RTFBuffer_t* pCurrentBuffer = m_aStates.top().getCurrentBuffer();
1510 
1511     if (!pCurrentBuffer && m_aStates.top().getDestination() != Destination::FOOTNOTE)
1512         Mapper().startCharacterGroup();
1513     else if (pCurrentBuffer)
1514     {
1515         RTFValue::Pointer_t pValue;
1516         pCurrentBuffer->push_back(Buf_t(BUFFER_STARTRUN, pValue, nullptr));
1517     }
1518 
1519     if (m_aStates.top().getDestination() == Destination::NORMAL
1520         || m_aStates.top().getDestination() == Destination::FIELDRESULT
1521         || m_aStates.top().getDestination() == Destination::SHAPETEXT)
1522         runProps();
1523 
1524     if (!pCurrentBuffer)
1525         Mapper().utext(reinterpret_cast<sal_uInt8 const*>(rString.getStr()), rString.getLength());
1526     else
1527     {
1528         auto pValue = new RTFValue(rString);
1529         pCurrentBuffer->push_back(Buf_t(BUFFER_UTEXT, pValue, nullptr));
1530     }
1531 
1532     m_bNeedCr = true;
1533 
1534     if (!pCurrentBuffer && m_aStates.top().getDestination() != Destination::FOOTNOTE)
1535         Mapper().endCharacterGroup();
1536     else if (pCurrentBuffer)
1537     {
1538         RTFValue::Pointer_t pValue;
1539         pCurrentBuffer->push_back(Buf_t(BUFFER_ENDRUN, pValue, nullptr));
1540     }
1541 }
1542 
prepareProperties(RTFParserState & rState,writerfilter::Reference<Properties>::Pointer_t & o_rpParagraphProperties,writerfilter::Reference<Properties>::Pointer_t & o_rpFrameProperties,writerfilter::Reference<Properties>::Pointer_t & o_rpTableRowProperties,int const nCells,int const nCurrentCellX)1543 void RTFDocumentImpl::prepareProperties(
1544     RTFParserState& rState, writerfilter::Reference<Properties>::Pointer_t& o_rpParagraphProperties,
1545     writerfilter::Reference<Properties>::Pointer_t& o_rpFrameProperties,
1546     writerfilter::Reference<Properties>::Pointer_t& o_rpTableRowProperties, int const nCells,
1547     int const nCurrentCellX)
1548 {
1549     o_rpParagraphProperties
1550         = getProperties(rState.getParagraphAttributes(), rState.getParagraphSprms(),
1551                         NS_ooxml::LN_Value_ST_StyleType_paragraph);
1552 
1553     if (rState.getFrame().hasProperties())
1554     {
1555         o_rpFrameProperties = new RTFReferenceProperties(RTFSprms(), rState.getFrame().getSprms());
1556     }
1557 
1558     // Table width.
1559     RTFValue::Pointer_t const pTableWidthProps
1560         = rState.getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblW);
1561     if (!pTableWidthProps)
1562     {
1563         auto pUnitValue = new RTFValue(3);
1564         putNestedAttribute(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblW,
1565                            NS_ooxml::LN_CT_TblWidth_type, pUnitValue);
1566         auto pWValue = new RTFValue(nCurrentCellX);
1567         putNestedAttribute(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblW,
1568                            NS_ooxml::LN_CT_TblWidth_w, pWValue);
1569     }
1570 
1571     if (nCells > 0)
1572         rState.getTableRowSprms().set(NS_ooxml::LN_tblRow, new RTFValue(1));
1573 
1574     RTFValue::Pointer_t const pCellMar
1575         = rState.getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblCellMar);
1576     if (!pCellMar)
1577     {
1578         // If no cell margins are defined, the default left/right margin is 0 in Word, but not in Writer.
1579         RTFSprms aAttributes;
1580         aAttributes.set(NS_ooxml::LN_CT_TblWidth_type,
1581                         new RTFValue(NS_ooxml::LN_Value_ST_TblWidth_dxa));
1582         aAttributes.set(NS_ooxml::LN_CT_TblWidth_w, new RTFValue(0));
1583         putNestedSprm(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblCellMar,
1584                       NS_ooxml::LN_CT_TblCellMar_left, new RTFValue(aAttributes));
1585         putNestedSprm(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblCellMar,
1586                       NS_ooxml::LN_CT_TblCellMar_right, new RTFValue(aAttributes));
1587     }
1588 
1589     o_rpTableRowProperties
1590         = new RTFReferenceProperties(rState.getTableRowAttributes(), rState.getTableRowSprms());
1591 }
1592 
sendProperties(writerfilter::Reference<Properties>::Pointer_t const & pParagraphProperties,writerfilter::Reference<Properties>::Pointer_t const & pFrameProperties,writerfilter::Reference<Properties>::Pointer_t const & pTableRowProperties)1593 void RTFDocumentImpl::sendProperties(
1594     writerfilter::Reference<Properties>::Pointer_t const& pParagraphProperties,
1595     writerfilter::Reference<Properties>::Pointer_t const& pFrameProperties,
1596     writerfilter::Reference<Properties>::Pointer_t const& pTableRowProperties)
1597 {
1598     Mapper().props(pParagraphProperties);
1599 
1600     if (pFrameProperties)
1601     {
1602         Mapper().props(pFrameProperties);
1603     }
1604 
1605     Mapper().props(pTableRowProperties);
1606 
1607     tableBreak();
1608 }
1609 
replayRowBuffer(RTFBuffer_t & rBuffer,::std::deque<RTFSprms> & rCellsSrpms,::std::deque<RTFSprms> & rCellsAttributes,int const nCells)1610 void RTFDocumentImpl::replayRowBuffer(RTFBuffer_t& rBuffer, ::std::deque<RTFSprms>& rCellsSrpms,
1611                                       ::std::deque<RTFSprms>& rCellsAttributes, int const nCells)
1612 {
1613     for (int i = 0; i < nCells; ++i)
1614     {
1615         replayBuffer(rBuffer, &rCellsSrpms.front(), &rCellsAttributes.front());
1616         rCellsSrpms.pop_front();
1617         rCellsAttributes.pop_front();
1618     }
1619     for (Buf_t& i : rBuffer)
1620     {
1621         SAL_WARN_IF(BUFFER_CELLEND == std::get<0>(i), "writerfilter.rtf", "dropping table cell!");
1622     }
1623     assert(rCellsSrpms.empty());
1624     assert(rCellsAttributes.empty());
1625 }
1626 
replayBuffer(RTFBuffer_t & rBuffer,RTFSprms * const pSprms,RTFSprms const * const pAttributes)1627 void RTFDocumentImpl::replayBuffer(RTFBuffer_t& rBuffer, RTFSprms* const pSprms,
1628                                    RTFSprms const* const pAttributes)
1629 {
1630     while (!rBuffer.empty())
1631     {
1632         Buf_t aTuple(rBuffer.front());
1633         rBuffer.pop_front();
1634         if (std::get<0>(aTuple) == BUFFER_PROPS)
1635         {
1636             // Construct properties via getProperties() and not directly, to take care of deduplication.
1637             writerfilter::Reference<Properties>::Pointer_t const pProp(getProperties(
1638                 std::get<1>(aTuple)->getAttributes(), std::get<1>(aTuple)->getSprms(), 0));
1639             Mapper().props(pProp);
1640         }
1641         else if (std::get<0>(aTuple) == BUFFER_NESTROW)
1642         {
1643             TableRowBuffer& rRowBuffer(*std::get<2>(aTuple));
1644 
1645             replayRowBuffer(rRowBuffer.GetBuffer(), rRowBuffer.GetCellsSprms(),
1646                             rRowBuffer.GetCellsAttributes(), rRowBuffer.GetCells());
1647 
1648             sendProperties(rRowBuffer.GetParaProperties(), rRowBuffer.GetFrameProperties(),
1649                            rRowBuffer.GetRowProperties());
1650         }
1651         else if (std::get<0>(aTuple) == BUFFER_CELLEND)
1652         {
1653             assert(pSprms && pAttributes);
1654             auto pValue = new RTFValue(1);
1655             pSprms->set(NS_ooxml::LN_tblCell, pValue);
1656             writerfilter::Reference<Properties>::Pointer_t const pTableCellProperties(
1657                 new RTFReferenceProperties(*pAttributes, *pSprms));
1658             Mapper().props(pTableCellProperties);
1659             tableBreak();
1660             break;
1661         }
1662         else if (std::get<0>(aTuple) == BUFFER_STARTRUN)
1663             Mapper().startCharacterGroup();
1664         else if (std::get<0>(aTuple) == BUFFER_TEXT)
1665         {
1666             sal_uInt8 const nValue = std::get<1>(aTuple)->getInt();
1667             Mapper().text(&nValue, 1);
1668         }
1669         else if (std::get<0>(aTuple) == BUFFER_UTEXT)
1670         {
1671             OUString const aString(std::get<1>(aTuple)->getString());
1672             Mapper().utext(reinterpret_cast<sal_uInt8 const*>(aString.getStr()),
1673                            aString.getLength());
1674         }
1675         else if (std::get<0>(aTuple) == BUFFER_ENDRUN)
1676             Mapper().endCharacterGroup();
1677         else if (std::get<0>(aTuple) == BUFFER_PAR)
1678             parBreak();
1679         else if (std::get<0>(aTuple) == BUFFER_STARTSHAPE)
1680             m_pSdrImport->resolve(std::get<1>(aTuple)->getShape(), false, RTFSdrImport::SHAPE);
1681         else if (std::get<0>(aTuple) == BUFFER_RESOLVESHAPE)
1682         {
1683             // Make sure there is no current buffer while replaying the shape,
1684             // otherwise it gets re-buffered.
1685             RTFBuffer_t* pCurrentBuffer = m_aStates.top().getCurrentBuffer();
1686             m_aStates.top().setCurrentBuffer(nullptr);
1687 
1688             // Set current shape during replay, needed by e.g. wrap in
1689             // background.
1690             m_aStates.top().getShape() = std::get<1>(aTuple)->getShape();
1691 
1692             m_pSdrImport->resolve(std::get<1>(aTuple)->getShape(), true, RTFSdrImport::SHAPE);
1693             m_aStates.top().setCurrentBuffer(pCurrentBuffer);
1694         }
1695         else if (std::get<0>(aTuple) == BUFFER_ENDSHAPE)
1696             m_pSdrImport->close();
1697         else if (std::get<0>(aTuple) == BUFFER_RESOLVESUBSTREAM)
1698         {
1699             RTFSprms& rAttributes = std::get<1>(aTuple)->getAttributes();
1700             std::size_t nPos = rAttributes.find(0)->getInt();
1701             Id nId = rAttributes.find(1)->getInt();
1702             OUString aCustomMark = rAttributes.find(2)->getString();
1703             resolveSubstream(nPos, nId, aCustomMark);
1704         }
1705         else if (std::get<0>(aTuple) == BUFFER_PICTURE)
1706             m_aStates.top().getPicture() = std::get<1>(aTuple)->getPicture();
1707         else if (std::get<0>(aTuple) == BUFFER_SETSTYLE)
1708         {
1709             if (!m_aStates.empty())
1710                 m_aStates.top().setCurrentStyleIndex(std::get<1>(aTuple)->getInt());
1711         }
1712         else
1713             assert(false);
1714     }
1715 }
1716 
findPropertyName(const std::vector<beans::PropertyValue> & rProperties,const OUString & rName)1717 bool findPropertyName(const std::vector<beans::PropertyValue>& rProperties, const OUString& rName)
1718 {
1719     return std::any_of(
1720         rProperties.begin(), rProperties.end(),
1721         [&rName](const beans::PropertyValue& rProperty) { return rProperty.Name == rName; });
1722 }
1723 
backupTableRowProperties()1724 void RTFDocumentImpl::backupTableRowProperties()
1725 {
1726     if (m_nTopLevelCurrentCellX)
1727     {
1728         m_aBackupTableRowSprms = m_aStates.top().getTableRowSprms();
1729         m_aBackupTableRowAttributes = m_aStates.top().getTableRowAttributes();
1730         m_nBackupTopLevelCurrentCellX = m_nTopLevelCurrentCellX;
1731     }
1732 }
1733 
restoreTableRowProperties()1734 void RTFDocumentImpl::restoreTableRowProperties()
1735 {
1736     m_aStates.top().getTableRowSprms() = m_aBackupTableRowSprms;
1737     m_aStates.top().getTableRowAttributes() = m_aBackupTableRowAttributes;
1738     m_nTopLevelCurrentCellX = m_nBackupTopLevelCurrentCellX;
1739 }
1740 
resetTableRowProperties()1741 void RTFDocumentImpl::resetTableRowProperties()
1742 {
1743     m_aStates.top().getTableRowSprms() = m_aDefaultState.getTableRowSprms();
1744     m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol, new RTFValue(-1),
1745                                            RTFOverwrite::NO_APPEND);
1746     m_aStates.top().getTableRowAttributes() = m_aDefaultState.getTableRowAttributes();
1747     if (Destination::NESTEDTABLEPROPERTIES == m_aStates.top().getDestination())
1748     {
1749         m_nNestedTRLeft = 0;
1750         m_nNestedCurrentCellX = 0;
1751     }
1752     else
1753     {
1754         m_nTopLevelTRLeft = 0;
1755         m_nTopLevelCurrentCellX = 0;
1756     }
1757 }
1758 
dispatchToggle(RTFKeyword nKeyword,bool bParam,int nParam)1759 RTFError RTFDocumentImpl::dispatchToggle(RTFKeyword nKeyword, bool bParam, int nParam)
1760 {
1761     setNeedSect(true);
1762     checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
1763     RTFSkipDestination aSkip(*this);
1764     int nSprm = -1;
1765     tools::SvRef<RTFValue> pBoolValue(new RTFValue(int(!bParam || nParam != 0)));
1766 
1767     // Underline toggles.
1768     switch (nKeyword)
1769     {
1770         case RTFKeyword::UL:
1771             nSprm = NS_ooxml::LN_Value_ST_Underline_single;
1772             break;
1773         case RTFKeyword::ULDASH:
1774             nSprm = NS_ooxml::LN_Value_ST_Underline_dash;
1775             break;
1776         case RTFKeyword::ULDASHD:
1777             nSprm = NS_ooxml::LN_Value_ST_Underline_dotDash;
1778             break;
1779         case RTFKeyword::ULDASHDD:
1780             nSprm = NS_ooxml::LN_Value_ST_Underline_dotDotDash;
1781             break;
1782         case RTFKeyword::ULDB:
1783             nSprm = NS_ooxml::LN_Value_ST_Underline_double;
1784             break;
1785         case RTFKeyword::ULHWAVE:
1786             nSprm = NS_ooxml::LN_Value_ST_Underline_wavyHeavy;
1787             break;
1788         case RTFKeyword::ULLDASH:
1789             nSprm = NS_ooxml::LN_Value_ST_Underline_dashLong;
1790             break;
1791         case RTFKeyword::ULTH:
1792             nSprm = NS_ooxml::LN_Value_ST_Underline_thick;
1793             break;
1794         case RTFKeyword::ULTHD:
1795             nSprm = NS_ooxml::LN_Value_ST_Underline_dottedHeavy;
1796             break;
1797         case RTFKeyword::ULTHDASH:
1798             nSprm = NS_ooxml::LN_Value_ST_Underline_dashedHeavy;
1799             break;
1800         case RTFKeyword::ULTHDASHD:
1801             nSprm = NS_ooxml::LN_Value_ST_Underline_dashDotHeavy;
1802             break;
1803         case RTFKeyword::ULTHDASHDD:
1804             nSprm = NS_ooxml::LN_Value_ST_Underline_dashDotDotHeavy;
1805             break;
1806         case RTFKeyword::ULTHLDASH:
1807             nSprm = NS_ooxml::LN_Value_ST_Underline_dashLongHeavy;
1808             break;
1809         case RTFKeyword::ULULDBWAVE:
1810             nSprm = NS_ooxml::LN_Value_ST_Underline_wavyDouble;
1811             break;
1812         case RTFKeyword::ULWAVE:
1813             nSprm = NS_ooxml::LN_Value_ST_Underline_wave;
1814             break;
1815         default:
1816             break;
1817     }
1818     if (nSprm >= 0)
1819     {
1820         auto pValue
1821             = new RTFValue((!bParam || nParam != 0) ? nSprm : NS_ooxml::LN_Value_ST_Underline_none);
1822         m_aStates.top().getCharacterAttributes().set(NS_ooxml::LN_CT_Underline_val, pValue);
1823         return RTFError::OK;
1824     }
1825 
1826     // Accent characters (over dot / over comma).
1827     switch (nKeyword)
1828     {
1829         case RTFKeyword::ACCNONE:
1830             nSprm = NS_ooxml::LN_Value_ST_Em_none;
1831             break;
1832         case RTFKeyword::ACCDOT:
1833             nSprm = NS_ooxml::LN_Value_ST_Em_dot;
1834             break;
1835         case RTFKeyword::ACCCOMMA:
1836             nSprm = NS_ooxml::LN_Value_ST_Em_comma;
1837             break;
1838         case RTFKeyword::ACCCIRCLE:
1839             nSprm = NS_ooxml::LN_Value_ST_Em_circle;
1840             break;
1841         case RTFKeyword::ACCUNDERDOT:
1842             nSprm = NS_ooxml::LN_Value_ST_Em_underDot;
1843             break;
1844         default:
1845             break;
1846     }
1847     if (nSprm >= 0)
1848     {
1849         auto pValue = new RTFValue((!bParam || nParam != 0) ? nSprm : 0);
1850         m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_RPrBase_em, pValue);
1851         return RTFError::OK;
1852     }
1853 
1854     // Trivial character sprms.
1855     switch (nKeyword)
1856     {
1857         case RTFKeyword::B:
1858         case RTFKeyword::AB:
1859             switch (m_aStates.top().getRunType())
1860             {
1861                 case RTFParserState::RunType::HICH:
1862                 case RTFParserState::RunType::RTLCH_LTRCH_1:
1863                 case RTFParserState::RunType::LTRCH_RTLCH_2:
1864                 case RTFParserState::RunType::DBCH:
1865                     nSprm = NS_ooxml::LN_EG_RPrBase_bCs;
1866                     break;
1867                 case RTFParserState::RunType::NONE:
1868                 case RTFParserState::RunType::LOCH:
1869                 case RTFParserState::RunType::LTRCH_RTLCH_1:
1870                 case RTFParserState::RunType::RTLCH_LTRCH_2:
1871                 default:
1872                     nSprm = NS_ooxml::LN_EG_RPrBase_b;
1873                     break;
1874             }
1875             break;
1876         case RTFKeyword::I:
1877         case RTFKeyword::AI:
1878             switch (m_aStates.top().getRunType())
1879             {
1880                 case RTFParserState::RunType::HICH:
1881                 case RTFParserState::RunType::RTLCH_LTRCH_1:
1882                 case RTFParserState::RunType::LTRCH_RTLCH_2:
1883                 case RTFParserState::RunType::DBCH:
1884                     nSprm = NS_ooxml::LN_EG_RPrBase_iCs;
1885                     break;
1886                 case RTFParserState::RunType::NONE:
1887                 case RTFParserState::RunType::LOCH:
1888                 case RTFParserState::RunType::LTRCH_RTLCH_1:
1889                 case RTFParserState::RunType::RTLCH_LTRCH_2:
1890                 default:
1891                     nSprm = NS_ooxml::LN_EG_RPrBase_i;
1892                     break;
1893             }
1894             break;
1895         case RTFKeyword::OUTL:
1896             nSprm = NS_ooxml::LN_EG_RPrBase_outline;
1897             break;
1898         case RTFKeyword::SHAD:
1899             nSprm = NS_ooxml::LN_EG_RPrBase_shadow;
1900             break;
1901         case RTFKeyword::V:
1902             nSprm = NS_ooxml::LN_EG_RPrBase_vanish;
1903             break;
1904         case RTFKeyword::STRIKE:
1905             nSprm = NS_ooxml::LN_EG_RPrBase_strike;
1906             break;
1907         case RTFKeyword::STRIKED:
1908             nSprm = NS_ooxml::LN_EG_RPrBase_dstrike;
1909             break;
1910         case RTFKeyword::SCAPS:
1911             nSprm = NS_ooxml::LN_EG_RPrBase_smallCaps;
1912             break;
1913         case RTFKeyword::IMPR:
1914             nSprm = NS_ooxml::LN_EG_RPrBase_imprint;
1915             break;
1916         case RTFKeyword::CAPS:
1917             nSprm = NS_ooxml::LN_EG_RPrBase_caps;
1918             break;
1919         default:
1920             break;
1921     }
1922     if (nSprm >= 0)
1923     {
1924         m_aStates.top().getCharacterSprms().set(nSprm, pBoolValue);
1925         return RTFError::OK;
1926     }
1927 
1928     switch (nKeyword)
1929     {
1930         case RTFKeyword::ASPALPHA:
1931             m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_autoSpaceDE,
1932                                                     pBoolValue);
1933             break;
1934         case RTFKeyword::DELETED:
1935         case RTFKeyword::REVISED:
1936         {
1937             auto pValue
1938                 = new RTFValue(nKeyword == RTFKeyword::DELETED ? oox::XML_del : oox::XML_ins);
1939             putNestedAttribute(m_aStates.top().getCharacterSprms(), NS_ooxml::LN_trackchange,
1940                                NS_ooxml::LN_token, pValue);
1941         }
1942         break;
1943         case RTFKeyword::SBAUTO:
1944             putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_spacing,
1945                                NS_ooxml::LN_CT_Spacing_beforeAutospacing, pBoolValue);
1946             break;
1947         case RTFKeyword::SAAUTO:
1948             putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_spacing,
1949                                NS_ooxml::LN_CT_Spacing_afterAutospacing, pBoolValue);
1950             break;
1951         case RTFKeyword::FACINGP:
1952             m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_evenAndOddHeaders, pBoolValue);
1953             break;
1954         case RTFKeyword::HYPHAUTO:
1955             m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_autoHyphenation, pBoolValue);
1956             break;
1957         case RTFKeyword::HYPHPAR:
1958             m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_suppressAutoHyphens,
1959                                                     new RTFValue(int(bParam && nParam == 0)));
1960             break;
1961         default:
1962         {
1963             SAL_INFO("writerfilter.rtf",
1964                      "TODO handle toggle '" << keywordToString(nKeyword) << "'");
1965             aSkip.setParsed(false);
1966         }
1967         break;
1968     }
1969     return RTFError::OK;
1970 }
1971 
pushState()1972 RTFError RTFDocumentImpl::pushState()
1973 {
1974     //SAL_INFO("writerfilter.rtf", __func__ << " before push: " << m_pTokenizer->getGroup());
1975 
1976     checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
1977     m_nGroupStartPos = Strm().Tell();
1978 
1979     if (m_aStates.empty())
1980         m_aStates.push(m_aDefaultState);
1981     else
1982     {
1983         // fdo#85812 group resets run type of _current_ and new state (but not RTL)
1984         if (m_aStates.top().getRunType() != RTFParserState::RunType::LTRCH_RTLCH_2
1985             && m_aStates.top().getRunType() != RTFParserState::RunType::RTLCH_LTRCH_2)
1986         {
1987             m_aStates.top().setRunType(RTFParserState::RunType::NONE);
1988         }
1989 
1990         if (m_aStates.top().getDestination() == Destination::MR)
1991             lcl_DestinationToMath(m_aStates.top().getCurrentDestinationText(), m_aMathBuffer,
1992                                   m_bMathNor);
1993         m_aStates.push(m_aStates.top());
1994     }
1995     m_aStates.top().getDestinationText().setLength(0); // was copied: always reset!
1996 
1997     m_pTokenizer->pushGroup();
1998 
1999     switch (m_aStates.top().getDestination())
2000     {
2001         case Destination::FONTTABLE:
2002             // this is a "faked" destination for the font entry
2003             m_aStates.top().setCurrentDestinationText(&m_aStates.top().getDestinationText());
2004             m_aStates.top().setDestination(Destination::FONTENTRY);
2005             break;
2006         case Destination::STYLESHEET:
2007             // this is a "faked" destination for the style sheet entry
2008             m_aStates.top().setCurrentDestinationText(&m_aStates.top().getDestinationText());
2009             m_aStates.top().setDestination(Destination::STYLEENTRY);
2010             {
2011                 // the *default* is \s0 i.e. paragraph style default
2012                 // this will be overwritten by \sN \csN \dsN \tsN
2013                 m_nCurrentStyleIndex = 0;
2014                 auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_StyleType_paragraph);
2015                 m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Style_type, pValue);
2016             }
2017             break;
2018         case Destination::FIELDRESULT:
2019         case Destination::SHAPETEXT:
2020         case Destination::FORMFIELD:
2021         case Destination::FIELDINSTRUCTION:
2022         case Destination::PICT:
2023             m_aStates.top().setDestination(Destination::NORMAL);
2024             break;
2025         case Destination::MNUM:
2026         case Destination::MDEN:
2027         case Destination::ME:
2028         case Destination::MFNAME:
2029         case Destination::MLIM:
2030         case Destination::MSUB:
2031         case Destination::MSUP:
2032         case Destination::MDEG:
2033         case Destination::MOMATH:
2034             m_aStates.top().setDestination(Destination::MR);
2035             break;
2036         case Destination::REVISIONTABLE:
2037             // this is a "faked" destination for the revision table entry
2038             m_aStates.top().setCurrentDestinationText(&m_aStates.top().getDestinationText());
2039             m_aStates.top().setDestination(Destination::REVISIONENTRY);
2040             break;
2041         default:
2042             break;
2043     }
2044 
2045     // If this is true, then ooxml:endtrackchange will be generated.  Make sure
2046     // we don't generate more ooxml:endtrackchange than ooxml:trackchange: new
2047     // state does not inherit this flag.
2048     m_aStates.top().setStartedTrackchange(false);
2049 
2050     return RTFError::OK;
2051 }
2052 
createStyleProperties()2053 writerfilter::Reference<Properties>::Pointer_t RTFDocumentImpl::createStyleProperties()
2054 {
2055     int nBasedOn = 0;
2056     RTFValue::Pointer_t pBasedOn
2057         = m_aStates.top().getTableSprms().find(NS_ooxml::LN_CT_Style_basedOn);
2058     if (pBasedOn)
2059         nBasedOn = pBasedOn->getInt();
2060     if (nBasedOn == 0)
2061     {
2062         // No parent style, then mimic what Word does: ignore attributes which
2063         // would set a margin as formatting, but with a default value.
2064         for (const auto& nId :
2065              { NS_ooxml::LN_CT_Ind_firstLine, NS_ooxml::LN_CT_Ind_left, NS_ooxml::LN_CT_Ind_right,
2066                NS_ooxml::LN_CT_Ind_start, NS_ooxml::LN_CT_Ind_end })
2067         {
2068             RTFValue::Pointer_t pValue = getNestedAttribute(m_aStates.top().getParagraphSprms(),
2069                                                             NS_ooxml::LN_CT_PPrBase_ind, nId);
2070             if (pValue && pValue->getInt() == 0)
2071                 eraseNestedAttribute(m_aStates.top().getParagraphSprms(),
2072                                      NS_ooxml::LN_CT_PPrBase_ind, nId);
2073         }
2074     }
2075 
2076     RTFValue::Pointer_t pParaProps = new RTFValue(m_aStates.top().getParagraphAttributes(),
2077                                                   m_aStates.top().getParagraphSprms());
2078     RTFValue::Pointer_t pCharProps = new RTFValue(m_aStates.top().getCharacterAttributes(),
2079                                                   m_aStates.top().getCharacterSprms());
2080 
2081     // resetSprms will clean up this modification
2082     m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Style_pPr, pParaProps);
2083     m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Style_rPr, pCharProps);
2084 
2085     writerfilter::Reference<Properties>::Pointer_t pProps(new RTFReferenceProperties(
2086         m_aStates.top().getTableAttributes(), m_aStates.top().getTableSprms()));
2087     return pProps;
2088 }
2089 
2090 /** 2 different representations of the styles are needed:
2091 
2092     1) flat content, as read from the input file:
2093        stored in m_aStyleTableEntries, used as reference input for
2094        deduplication both here and for hard formatting in getProperties()
2095 
2096     2) real content, with proper override of sprms/attributes where it differs
2097        from parent style; this is produced here and sent to domain mapper
2098  */
deduplicateStyleTable()2099 RTFReferenceTable::Entries_t RTFDocumentImpl::deduplicateStyleTable()
2100 {
2101     RTFReferenceTable::Entries_t ret;
2102     for (auto const& it : m_aStyleTableEntries)
2103     {
2104         auto pStyle = it.second;
2105         ret[it.first] = pStyle;
2106         // ugly downcasts here, but can't easily replace the members with
2107         // RTFReferenceProperties because dmapper wants SvRef<Properties> anyway
2108         RTFValue::Pointer_t const pBasedOn(
2109             static_cast<RTFReferenceProperties&>(*pStyle).getSprms().find(
2110                 NS_ooxml::LN_CT_Style_basedOn));
2111         if (pBasedOn)
2112         {
2113             int const nBasedOn(pBasedOn->getInt());
2114             // don't deduplicate yourself - especially a potential problem for the default style.
2115             if (it.first == nBasedOn)
2116                 continue;
2117 
2118             auto const itParent(m_aStyleTableEntries.find(nBasedOn)); // definition as read!
2119             if (itParent != m_aStyleTableEntries.end())
2120             {
2121                 auto const pStyleType(
2122                     static_cast<RTFReferenceProperties&>(*pStyle).getAttributes().find(
2123                         NS_ooxml::LN_CT_Style_type));
2124                 assert(pStyleType);
2125                 int const nStyleType(pStyleType->getInt());
2126                 RTFSprms const sprms(
2127                     static_cast<RTFReferenceProperties&>(*pStyle).getSprms().cloneAndDeduplicate(
2128                         static_cast<RTFReferenceProperties&>(*itParent->second).getSprms(),
2129                         nStyleType));
2130                 RTFSprms const attributes(
2131                     static_cast<RTFReferenceProperties&>(*pStyle)
2132                         .getAttributes()
2133                         .cloneAndDeduplicate(
2134                             static_cast<RTFReferenceProperties&>(*itParent->second).getAttributes(),
2135                             nStyleType));
2136 
2137                 ret[it.first] = new RTFReferenceProperties(attributes, sprms);
2138             }
2139             else
2140             {
2141                 SAL_WARN("writerfilter.rtf", "parent style not found: " << nBasedOn);
2142             }
2143         }
2144     }
2145     assert(ret.size() == m_aStyleTableEntries.size());
2146     return ret;
2147 }
2148 
resetSprms()2149 void RTFDocumentImpl::resetSprms()
2150 {
2151     m_aStates.top().getTableSprms().clear();
2152     m_aStates.top().getCharacterSprms().clear();
2153     m_aStates.top().getParagraphSprms().clear();
2154 }
2155 
resetAttributes()2156 void RTFDocumentImpl::resetAttributes()
2157 {
2158     m_aStates.top().getTableAttributes().clear();
2159     m_aStates.top().getCharacterAttributes().clear();
2160     m_aStates.top().getParagraphAttributes().clear();
2161 }
2162 
lcl_containsProperty(const uno::Sequence<beans::Property> & rProperties,std::u16string_view rName)2163 static bool lcl_containsProperty(const uno::Sequence<beans::Property>& rProperties,
2164                                  std::u16string_view rName)
2165 {
2166     return std::any_of(rProperties.begin(), rProperties.end(),
2167                        [&](const beans::Property& rProperty) { return rProperty.Name == rName; });
2168 }
2169 
beforePopState(RTFParserState & rState)2170 RTFError RTFDocumentImpl::beforePopState(RTFParserState& rState)
2171 {
2172     switch (rState.getDestination())
2173     {
2174         case Destination::FONTTABLE:
2175         {
2176             writerfilter::Reference<Table>::Pointer_t const pTable(
2177                 new RTFReferenceTable(m_aFontTableEntries));
2178             Mapper().table(NS_ooxml::LN_FONTTABLE, pTable);
2179             if (m_nDefaultFontIndex >= 0)
2180             {
2181                 auto pValue = new RTFValue(m_aFontNames[getFontIndex(m_nDefaultFontIndex)]);
2182                 putNestedAttribute(m_aDefaultState.getCharacterSprms(),
2183                                    NS_ooxml::LN_EG_RPrBase_rFonts, NS_ooxml::LN_CT_Fonts_ascii,
2184                                    pValue);
2185             }
2186         }
2187         break;
2188         case Destination::STYLESHEET:
2189         {
2190             RTFReferenceTable::Entries_t const pStyleTableDeduplicated(deduplicateStyleTable());
2191             writerfilter::Reference<Table>::Pointer_t const pTable(
2192                 new RTFReferenceTable(pStyleTableDeduplicated));
2193             Mapper().table(NS_ooxml::LN_STYLESHEET, pTable);
2194         }
2195         break;
2196         case Destination::LISTOVERRIDETABLE:
2197         {
2198             RTFSprms aListTableAttributes;
2199             writerfilter::Reference<Properties>::Pointer_t pProp
2200                 = new RTFReferenceProperties(aListTableAttributes, m_aListTableSprms);
2201             RTFReferenceTable::Entries_t aListTableEntries;
2202             aListTableEntries.insert(std::make_pair(0, pProp));
2203             writerfilter::Reference<Table>::Pointer_t const pTable(
2204                 new RTFReferenceTable(aListTableEntries));
2205             Mapper().table(NS_ooxml::LN_NUMBERING, pTable);
2206         }
2207         break;
2208         case Destination::LISTENTRY:
2209             for (const auto& rListLevelEntry : rState.getListLevelEntries())
2210                 rState.getTableSprms().set(rListLevelEntry.first, rListLevelEntry.second,
2211                                            RTFOverwrite::NO_APPEND);
2212             break;
2213         case Destination::FIELDINSTRUCTION:
2214         {
2215             auto pValue = new RTFValue(m_aFormfieldAttributes, m_aFormfieldSprms);
2216             RTFSprms aFFAttributes;
2217             RTFSprms aFFSprms;
2218             aFFSprms.set(NS_ooxml::LN_ffdata, pValue);
2219             if (!m_aStates.top().getCurrentBuffer())
2220             {
2221                 writerfilter::Reference<Properties>::Pointer_t pProperties
2222                     = new RTFReferenceProperties(aFFAttributes, aFFSprms);
2223                 Mapper().props(pProperties);
2224             }
2225             else
2226             {
2227                 auto pFFValue = new RTFValue(aFFAttributes, aFFSprms);
2228                 bufferProperties(*m_aStates.top().getCurrentBuffer(), pFFValue, nullptr);
2229             }
2230             m_aFormfieldAttributes.clear();
2231             m_aFormfieldSprms.clear();
2232 
2233             if (m_aStates.top().isFieldLocked())
2234                 singleChar(cFieldLock);
2235             singleChar(cFieldSep);
2236         }
2237         break;
2238         case Destination::FIELDRESULT:
2239             singleChar(cFieldEnd);
2240 
2241             if (!m_aPicturePath.isEmpty())
2242             {
2243                 // Read the picture into m_aStates.top().aDestinationText.
2244                 pushState();
2245                 dispatchDestination(RTFKeyword::PICT);
2246                 if (m_aPicturePath.endsWith(".png"))
2247                     dispatchFlag(RTFKeyword::PNGBLIP);
2248                 OUString aFileURL = m_rMediaDescriptor.getUnpackedValueOrDefault(
2249                     utl::MediaDescriptor::PROP_URL(), OUString());
2250                 OUString aPictureURL;
2251                 try
2252                 {
2253                     aPictureURL = rtl::Uri::convertRelToAbs(aFileURL, m_aPicturePath);
2254                 }
2255                 catch (const rtl::MalformedUriException& rException)
2256                 {
2257                     SAL_WARN("writerfilter.rtf",
2258                              "rtl::Uri::convertRelToAbs() failed: " << rException.getMessage());
2259                 }
2260 
2261                 if (!aPictureURL.isEmpty())
2262                 {
2263                     SvFileStream aStream(aPictureURL, StreamMode::READ);
2264                     if (aStream.IsOpen())
2265                     {
2266                         OUStringBuffer aBuf;
2267                         while (aStream.good())
2268                         {
2269                             unsigned char ch = 0;
2270                             aStream.ReadUChar(ch);
2271                             if (ch < 16)
2272                                 aBuf.append("0");
2273                             aBuf.append(static_cast<sal_Int32>(ch), 16);
2274                         }
2275                         m_aStates.top().getDestinationText() = aBuf;
2276                     }
2277                 }
2278                 popState();
2279                 m_aPicturePath.clear();
2280             }
2281 
2282             break;
2283         case Destination::LEVELTEXT:
2284         {
2285             if (&m_aStates.top().getDestinationText()
2286                 != m_aStates.top().getCurrentDestinationText())
2287                 break; // not for nested group
2288             OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2289 
2290             // The first character is the length of the string (the rest should be ignored).
2291             sal_Int32 nLength(aStr.toChar());
2292             OUString aValue;
2293             if (nLength < aStr.getLength())
2294                 aValue = aStr.copy(1, nLength);
2295             else
2296                 aValue = aStr;
2297             auto pValue = new RTFValue(aValue, true);
2298             rState.getTableAttributes().set(NS_ooxml::LN_CT_LevelText_val, pValue);
2299         }
2300         break;
2301         case Destination::LEVELNUMBERS:
2302         {
2303             bool bNestedLevelNumbers = false;
2304             if (m_aStates.size() > 1)
2305                 // Current destination is levelnumbers and parent destination is levelnumbers as well.
2306                 bNestedLevelNumbers
2307                     = m_aStates[m_aStates.size() - 2].getDestination() == Destination::LEVELNUMBERS;
2308             if (!bNestedLevelNumbers && rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_lvlText))
2309             {
2310                 RTFSprms& rAttributes
2311                     = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_lvlText)->getAttributes();
2312                 RTFValue::Pointer_t pValue = rAttributes.find(NS_ooxml::LN_CT_LevelText_val);
2313                 if (pValue && rState.getLevelNumbersValid())
2314                 {
2315                     OUString aOrig = pValue->getString();
2316 
2317                     OUStringBuffer aBuf(aOrig.getLength() * 2);
2318                     sal_Int32 nReplaces = 1;
2319                     for (int i = 0; i < aOrig.getLength(); i++)
2320                     {
2321                         if (std::find(rState.getLevelNumbers().begin(),
2322                                       rState.getLevelNumbers().end(), i + 1)
2323                             != rState.getLevelNumbers().end())
2324                         {
2325                             aBuf.append('%');
2326                             // '1.1.1' -> '%1.%2.%3', but '1.' (with '2.' prefix omitted) is %2.
2327                             aBuf.append(sal_Int32(nReplaces++ + rState.getListLevelNum() + 1
2328                                                   - rState.getLevelNumbers().size()));
2329                         }
2330                         else
2331                             aBuf.append(aOrig[i]);
2332                     }
2333 
2334                     pValue->setString(aBuf.makeStringAndClear());
2335                 }
2336                 else if (pValue)
2337                     // Have a value, but levelnumbers is not valid -> ignore it.
2338                     pValue->setString(OUString());
2339             }
2340             break;
2341         }
2342         case Destination::SHAPEPROPERTYNAME:
2343             if (&m_aStates.top().getDestinationText()
2344                 != m_aStates.top().getCurrentDestinationText())
2345                 break; // not for nested group
2346             rState.getShape().getProperties().emplace_back(
2347                 m_aStates.top().getCurrentDestinationText()->makeStringAndClear(), OUString());
2348             break;
2349         case Destination::SHAPEPROPERTYVALUE:
2350             if (!rState.getShape().getProperties().empty())
2351             {
2352                 rState.getShape().getProperties().back().second
2353                     = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2354                 if (m_aStates.top().getHadShapeText())
2355                     m_pSdrImport->append(rState.getShape().getProperties().back().first,
2356                                          rState.getShape().getProperties().back().second);
2357                 else if (rState.getInShapeGroup() && !rState.getInShape()
2358                          && rState.getShape().getProperties().back().first == "rotation")
2359                 {
2360                     // Rotation should be applied on the groupshape itself, not on each shape.
2361                     rState.getShape().getGroupProperties().push_back(
2362                         rState.getShape().getProperties().back());
2363                     rState.getShape().getProperties().pop_back();
2364                 }
2365             }
2366             break;
2367         case Destination::PICPROP:
2368         case Destination::SHAPEINSTRUCTION:
2369             if (m_aStates.size() > 1
2370                 && m_aStates[m_aStates.size() - 2].getDestination()
2371                        == Destination::SHAPEINSTRUCTION)
2372             {
2373                 // Do not resolve shape if shape instruction destination is inside other shape instruction
2374             }
2375             else if (!m_bObject && !rState.getInListpicture() && !rState.getHadShapeText()
2376                      && (!rState.getInShapeGroup() || rState.getInShape()))
2377             {
2378                 // Don't trigger a shape import in case we're only leaving the \shpinst of the groupshape itself.
2379                 RTFSdrImport::ShapeOrPict eType
2380                     = (rState.getDestination() == Destination::SHAPEINSTRUCTION)
2381                           ? RTFSdrImport::SHAPE
2382                           : RTFSdrImport::PICT;
2383                 if (!m_aStates.top().getCurrentBuffer() || eType != RTFSdrImport::SHAPE)
2384                     m_pSdrImport->resolve(m_aStates.top().getShape(), true, eType);
2385                 else
2386                 {
2387                     // Shape inside table: buffer the import to have correct anchor position.
2388                     // Also buffer the RTFPicture of the state stack as it contains
2389                     // the shape size.
2390                     auto pPictureValue = new RTFValue(m_aStates.top().getPicture());
2391                     m_aStates.top().getCurrentBuffer()->push_back(
2392                         Buf_t(BUFFER_PICTURE, pPictureValue, nullptr));
2393                     auto pValue = new RTFValue(m_aStates.top().getShape());
2394 
2395                     // Buffer wrap type.
2396                     for (const auto& rCharacterSprm : m_aStates.top().getCharacterSprms())
2397                     {
2398                         if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapNone
2399                             || rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight)
2400                         {
2401                             m_aStates.top().getShape().getWrapSprm() = rCharacterSprm;
2402                             break;
2403                         }
2404                     }
2405 
2406                     m_aStates.top().getCurrentBuffer()->push_back(
2407                         Buf_t(BUFFER_RESOLVESHAPE, pValue, nullptr));
2408                 }
2409             }
2410             else if (rState.getInShapeGroup() && !rState.getInShape())
2411             {
2412                 // End of a groupshape, as we're in shapegroup, but not in a real shape.
2413                 for (const auto& rGroupProperty : rState.getShape().getGroupProperties())
2414                     m_pSdrImport->appendGroupProperty(rGroupProperty.first, rGroupProperty.second);
2415                 rState.getShape().getGroupProperties().clear();
2416             }
2417             break;
2418         case Destination::BOOKMARKSTART:
2419         {
2420             if (&m_aStates.top().getDestinationText()
2421                 != m_aStates.top().getCurrentDestinationText())
2422                 break; // not for nested group
2423             OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2424             int nPos = m_aBookmarks.size();
2425             m_aBookmarks[aStr] = nPos;
2426             if (!m_aStates.top().getCurrentBuffer())
2427                 Mapper().props(new RTFReferenceProperties(lcl_getBookmarkProperties(nPos, aStr)));
2428             else
2429                 bufferProperties(*m_aStates.top().getCurrentBuffer(),
2430                                  new RTFValue(lcl_getBookmarkProperties(nPos, aStr)), nullptr);
2431         }
2432         break;
2433         case Destination::BOOKMARKEND:
2434         {
2435             if (&m_aStates.top().getDestinationText()
2436                 != m_aStates.top().getCurrentDestinationText())
2437                 break; // not for nested group
2438             OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2439             if (!m_aStates.top().getCurrentBuffer())
2440                 Mapper().props(new RTFReferenceProperties(
2441                     lcl_getBookmarkProperties(m_aBookmarks[aStr], aStr)));
2442             else
2443                 bufferProperties(*m_aStates.top().getCurrentBuffer(),
2444                                  new RTFValue(lcl_getBookmarkProperties(m_aBookmarks[aStr], aStr)),
2445                                  nullptr);
2446         }
2447         break;
2448         case Destination::INDEXENTRY:
2449         case Destination::TOCENTRY:
2450         {
2451             if (&m_aStates.top().getDestinationText()
2452                 != m_aStates.top().getCurrentDestinationText())
2453                 break; // not for nested group
2454             OUString str(m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2455             // dmapper expects this as a field, so let's fake something...
2456             auto const field((Destination::INDEXENTRY == rState.getDestination())
2457                                  ? OUStringLiteral(u"XE")
2458                                  : OUStringLiteral(u"TC"));
2459             str = field + " \"" + str.replaceAll("\"", "\\\"") + "\"";
2460             singleChar(cFieldStart);
2461             Mapper().utext(reinterpret_cast<sal_uInt8 const*>(str.getStr()), str.getLength());
2462             singleChar(cFieldSep);
2463             // no result
2464             singleChar(cFieldEnd);
2465         }
2466         break;
2467         case Destination::FORMFIELDNAME:
2468         {
2469             if (&m_aStates.top().getDestinationText()
2470                 != m_aStates.top().getCurrentDestinationText())
2471                 break; // not for nested group
2472             auto pValue
2473                 = new RTFValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2474             m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFData_name, pValue);
2475         }
2476         break;
2477         case Destination::FORMFIELDLIST:
2478         {
2479             if (&m_aStates.top().getDestinationText()
2480                 != m_aStates.top().getCurrentDestinationText())
2481                 break; // not for nested group
2482             auto pValue
2483                 = new RTFValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2484             m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFDDList_listEntry, pValue);
2485         }
2486         break;
2487         case Destination::DATAFIELD:
2488         {
2489             if (m_bFormField)
2490             {
2491                 if (&m_aStates.top().getDestinationText()
2492                     != m_aStates.top().getCurrentDestinationText())
2493                     break; // not for nested group
2494                 OString aStr = OUStringToOString(
2495                     m_aStates.top().getCurrentDestinationText()->makeStringAndClear(),
2496                     rState.getCurrentEncoding());
2497                 // decode hex dump
2498                 OStringBuffer aBuf;
2499                 int b = 0;
2500                 int count = 2;
2501                 for (int i = 0; i < aStr.getLength(); ++i)
2502                 {
2503                     char ch = aStr[i];
2504                     if (ch != 0x0d && ch != 0x0a)
2505                     {
2506                         b = b << 4;
2507                         sal_Int8 parsed = msfilter::rtfutil::AsHex(ch);
2508                         if (parsed == -1)
2509                             return RTFError::HEX_INVALID;
2510                         b += parsed;
2511                         count--;
2512                         if (!count)
2513                         {
2514                             aBuf.append(static_cast<char>(b));
2515                             count = 2;
2516                             b = 0;
2517                         }
2518                     }
2519                 }
2520                 aStr = aBuf.makeStringAndClear();
2521 
2522                 // ignore the first bytes
2523                 if (aStr.getLength() > 8)
2524                     aStr = aStr.copy(8);
2525                 // extract name
2526                 sal_Int32 nLength = aStr.toChar();
2527                 if (!aStr.isEmpty())
2528                     aStr = aStr.copy(1);
2529                 nLength = std::min(nLength, aStr.getLength());
2530                 OString aName = aStr.copy(0, nLength);
2531                 if (aStr.getLength() > nLength)
2532                     aStr = aStr.copy(nLength + 1); // zero-terminated string
2533                 else
2534                     aStr.clear();
2535                 // extract default text
2536                 nLength = aStr.toChar();
2537                 if (!aStr.isEmpty())
2538                     aStr = aStr.copy(1);
2539                 auto pNValue = new RTFValue(OStringToOUString(aName, rState.getCurrentEncoding()));
2540                 m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFData_name, pNValue);
2541                 if (nLength > 0)
2542                 {
2543                     OString aDefaultText = aStr.copy(0, std::min(nLength, aStr.getLength()));
2544                     auto pDValue = new RTFValue(
2545                         OStringToOUString(aDefaultText, rState.getCurrentEncoding()));
2546                     m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFTextInput_default, pDValue);
2547                 }
2548 
2549                 m_bFormField = false;
2550             }
2551         }
2552         break;
2553         case Destination::CREATIONTIME:
2554             if (m_xDocumentProperties.is())
2555                 m_xDocumentProperties->setCreationDate(lcl_getDateTime(rState));
2556             break;
2557         case Destination::REVISIONTIME:
2558             if (m_xDocumentProperties.is())
2559                 m_xDocumentProperties->setModificationDate(lcl_getDateTime(rState));
2560             break;
2561         case Destination::PRINTTIME:
2562             if (m_xDocumentProperties.is())
2563                 m_xDocumentProperties->setPrintDate(lcl_getDateTime(rState));
2564             break;
2565         case Destination::AUTHOR:
2566             if (&m_aStates.top().getDestinationText()
2567                 != m_aStates.top().getCurrentDestinationText())
2568                 break; // not for nested group
2569             if (m_xDocumentProperties.is())
2570                 m_xDocumentProperties->setAuthor(
2571                     m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2572             break;
2573         case Destination::KEYWORDS:
2574             if (&m_aStates.top().getDestinationText()
2575                 != m_aStates.top().getCurrentDestinationText())
2576                 break; // not for nested group
2577             if (m_xDocumentProperties.is())
2578                 m_xDocumentProperties->setKeywords(comphelper::string::convertCommaSeparated(
2579                     m_aStates.top().getCurrentDestinationText()->makeStringAndClear()));
2580             break;
2581         case Destination::COMMENT:
2582             if (&m_aStates.top().getDestinationText()
2583                 != m_aStates.top().getCurrentDestinationText())
2584                 break; // not for nested group
2585             if (m_xDocumentProperties.is())
2586                 m_xDocumentProperties->setGenerator(
2587                     m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2588             break;
2589         case Destination::SUBJECT:
2590             if (&m_aStates.top().getDestinationText()
2591                 != m_aStates.top().getCurrentDestinationText())
2592                 break; // not for nested group
2593             if (m_xDocumentProperties.is())
2594                 m_xDocumentProperties->setSubject(
2595                     m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2596             break;
2597         case Destination::TITLE:
2598         {
2599             if (&m_aStates.top().getDestinationText()
2600                 != m_aStates.top().getCurrentDestinationText())
2601                 break; // not for nested group
2602             if (m_xDocumentProperties.is())
2603                 m_xDocumentProperties->setTitle(
2604                     rState.getCurrentDestinationText()->makeStringAndClear());
2605         }
2606         break;
2607 
2608         case Destination::DOCCOMM:
2609             if (&m_aStates.top().getDestinationText()
2610                 != m_aStates.top().getCurrentDestinationText())
2611                 break; // not for nested group
2612             if (m_xDocumentProperties.is())
2613                 m_xDocumentProperties->setDescription(
2614                     m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2615             break;
2616         case Destination::OPERATOR:
2617         case Destination::COMPANY:
2618         {
2619             if (&m_aStates.top().getDestinationText()
2620                 != m_aStates.top().getCurrentDestinationText())
2621                 break; // not for nested group
2622             OUString aName = rState.getDestination() == Destination::OPERATOR ? OUString("Operator")
2623                                                                               : OUString("Company");
2624             uno::Any aValue
2625                 = uno::makeAny(m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2626             if (m_xDocumentProperties.is())
2627             {
2628                 uno::Reference<beans::XPropertyContainer> xUserDefinedProperties
2629                     = m_xDocumentProperties->getUserDefinedProperties();
2630                 uno::Reference<beans::XPropertySet> xPropertySet(xUserDefinedProperties,
2631                                                                  uno::UNO_QUERY);
2632                 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo
2633                     = xPropertySet->getPropertySetInfo();
2634                 if (xPropertySetInfo->hasPropertyByName(aName))
2635                     xPropertySet->setPropertyValue(aName, aValue);
2636                 else
2637                     xUserDefinedProperties->addProperty(aName, beans::PropertyAttribute::REMOVABLE,
2638                                                         aValue);
2639             }
2640         }
2641         break;
2642         case Destination::OBJDATA:
2643         {
2644             if (&m_aStates.top().getDestinationText()
2645                 != m_aStates.top().getCurrentDestinationText())
2646                 break; // not for nested group
2647 
2648             RTFError eError = handleEmbeddedObject();
2649             if (eError != RTFError::OK)
2650                 return eError;
2651         }
2652         break;
2653         case Destination::OBJCLASS:
2654         {
2655             auto pValue
2656                 = new RTFValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2657             m_aOLEAttributes.set(NS_ooxml::LN_CT_OLEObject_ProgID, pValue);
2658             break;
2659         }
2660         case Destination::OBJECT:
2661         {
2662             if (!m_bObject)
2663             {
2664                 // if the object is in a special container we will use the \result
2665                 // element instead of the \objdata
2666                 // (see RTFKeyword::OBJECT in RTFDocumentImpl::dispatchDestination)
2667                 break;
2668             }
2669 
2670             RTFSprms aObjectSprms;
2671             auto pOLEValue = new RTFValue(m_aOLEAttributes);
2672             aObjectSprms.set(NS_ooxml::LN_OLEObject_OLEObject, pOLEValue);
2673 
2674             RTFSprms aObjAttributes;
2675             RTFSprms aObjSprms;
2676             auto pValue = new RTFValue(m_aObjectAttributes, aObjectSprms);
2677             aObjSprms.set(NS_ooxml::LN_object, pValue);
2678             writerfilter::Reference<Properties>::Pointer_t pProperties
2679                 = new RTFReferenceProperties(aObjAttributes, aObjSprms);
2680             uno::Reference<drawing::XShape> xShape;
2681             RTFValue::Pointer_t pShape = m_aObjectAttributes.find(NS_ooxml::LN_shape);
2682             OSL_ASSERT(pShape);
2683             if (pShape)
2684                 pShape->getAny() >>= xShape;
2685             if (xShape.is())
2686             {
2687                 Mapper().startShape(xShape);
2688                 Mapper().props(pProperties);
2689                 Mapper().endShape();
2690             }
2691             m_aObjectAttributes.clear();
2692             m_aOLEAttributes.clear();
2693             m_bObject = false;
2694         }
2695         break;
2696         case Destination::ANNOTATIONDATE:
2697         {
2698             if (&m_aStates.top().getDestinationText()
2699                 != m_aStates.top().getCurrentDestinationText())
2700                 break; // not for nested group
2701             OUString aStr(OStringToOUString(
2702                 DTTM22OString(
2703                     m_aStates.top().getCurrentDestinationText()->makeStringAndClear().toInt32()),
2704                 rState.getCurrentEncoding()));
2705             auto pValue = new RTFValue(aStr);
2706             RTFSprms aAnnAttributes;
2707             aAnnAttributes.set(NS_ooxml::LN_CT_TrackChange_date, pValue);
2708             writerfilter::Reference<Properties>::Pointer_t pProperties
2709                 = new RTFReferenceProperties(aAnnAttributes);
2710             Mapper().props(pProperties);
2711         }
2712         break;
2713         case Destination::ANNOTATIONAUTHOR:
2714             if (&m_aStates.top().getDestinationText()
2715                 != m_aStates.top().getCurrentDestinationText())
2716                 break; // not for nested group
2717             m_aAuthor = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2718             break;
2719         case Destination::ATNID:
2720             if (&m_aStates.top().getDestinationText()
2721                 != m_aStates.top().getCurrentDestinationText())
2722                 break; // not for nested group
2723             m_aAuthorInitials = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2724             break;
2725         case Destination::ANNOTATIONREFERENCESTART:
2726         case Destination::ANNOTATIONREFERENCEEND:
2727         {
2728             if (&m_aStates.top().getDestinationText()
2729                 != m_aStates.top().getCurrentDestinationText())
2730                 break; // not for nested group
2731             OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2732             auto pValue = new RTFValue(aStr.toInt32());
2733             RTFSprms aAttributes;
2734             if (rState.getDestination() == Destination::ANNOTATIONREFERENCESTART)
2735                 aAttributes.set(NS_ooxml::LN_EG_RangeMarkupElements_commentRangeStart, pValue);
2736             else
2737                 aAttributes.set(NS_ooxml::LN_EG_RangeMarkupElements_commentRangeEnd, pValue);
2738             writerfilter::Reference<Properties>::Pointer_t pProperties
2739                 = new RTFReferenceProperties(aAttributes);
2740             if (!m_aStates.top().getCurrentBuffer())
2741             {
2742                 Mapper().props(pProperties);
2743             }
2744             else
2745             {
2746                 auto const pValue2 = new RTFValue(aAttributes, RTFSprms());
2747                 bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue2, nullptr);
2748             }
2749         }
2750         break;
2751         case Destination::ANNOTATIONREFERENCE:
2752         {
2753             if (&m_aStates.top().getDestinationText()
2754                 != m_aStates.top().getCurrentDestinationText())
2755                 break; // not for nested group
2756             OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2757             RTFSprms aAnnAttributes;
2758             aAnnAttributes.set(NS_ooxml::LN_CT_Markup_id, new RTFValue(aStr.toInt32()));
2759             Mapper().props(new RTFReferenceProperties(aAnnAttributes));
2760         }
2761         break;
2762         case Destination::FALT:
2763         {
2764             if (&m_aStates.top().getDestinationText()
2765                 != m_aStates.top().getCurrentDestinationText())
2766                 break; // not for nested group
2767             OUString aStr(m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2768             auto pValue = new RTFValue(aStr);
2769             rState.getTableSprms().set(NS_ooxml::LN_CT_Font_altName, pValue);
2770         }
2771         break;
2772         case Destination::DRAWINGOBJECT:
2773             if (m_aStates.top().getDrawingObject().getShape().is())
2774             {
2775                 RTFDrawingObject& rDrawing = m_aStates.top().getDrawingObject();
2776                 const uno::Reference<drawing::XShape>& xShape(rDrawing.getShape());
2777                 const uno::Reference<beans::XPropertySet>& xPropertySet(rDrawing.getPropertySet());
2778 
2779                 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY);
2780                 bool bTextFrame = xServiceInfo->supportsService("com.sun.star.text.TextFrame");
2781 
2782                 // The default is certainly not inline, but then what Word supports is just at-character.
2783                 xPropertySet->setPropertyValue(
2784                     "AnchorType", uno::makeAny(text::TextContentAnchorType_AT_CHARACTER));
2785 
2786                 if (bTextFrame)
2787                 {
2788                     xPropertySet->setPropertyValue("HoriOrientPosition",
2789                                                    uno::makeAny(rDrawing.getLeft()));
2790                     xPropertySet->setPropertyValue("VertOrientPosition",
2791                                                    uno::makeAny(rDrawing.getTop()));
2792                 }
2793                 else
2794                 {
2795                     xShape->setPosition(awt::Point(rDrawing.getLeft(), rDrawing.getTop()));
2796                 }
2797                 xShape->setSize(awt::Size(rDrawing.getRight(), rDrawing.getBottom()));
2798 
2799                 if (rDrawing.getHasLineColor())
2800                 {
2801                     uno::Any aLineColor = uno::makeAny(sal_uInt32((rDrawing.getLineColorR() << 16)
2802                                                                   + (rDrawing.getLineColorG() << 8)
2803                                                                   + rDrawing.getLineColorB()));
2804                     uno::Any aLineWidth;
2805                     RTFSdrImport::resolveLineColorAndWidth(bTextFrame, xPropertySet, aLineColor,
2806                                                            aLineWidth);
2807                 }
2808                 if (rDrawing.getHasFillColor())
2809                     xPropertySet->setPropertyValue(
2810                         "FillColor", uno::makeAny(sal_uInt32((rDrawing.getFillColorR() << 16)
2811                                                              + (rDrawing.getFillColorG() << 8)
2812                                                              + rDrawing.getFillColorB())));
2813                 else if (!bTextFrame)
2814                     // If there is no fill, the Word default is 100% transparency.
2815                     xPropertySet->setPropertyValue("FillTransparence",
2816                                                    uno::makeAny(sal_Int32(100)));
2817 
2818                 RTFSdrImport::resolveFLine(xPropertySet, rDrawing.getFLine());
2819 
2820                 if (!m_aStates.top().getDrawingObject().getHadShapeText())
2821                 {
2822                     Mapper().startShape(xShape);
2823                 }
2824                 Mapper().endShape();
2825             }
2826             break;
2827         case Destination::PICT:
2828             // fdo#79319 ignore picture data if it's really a shape
2829             if (!m_pSdrImport->isFakePict())
2830             {
2831                 resolvePict(true, m_pSdrImport->getCurrentShape());
2832             }
2833             m_bNeedFinalPar = true;
2834             break;
2835         case Destination::SHAPE:
2836             m_bNeedFinalPar = true;
2837             m_bNeedCr = m_bNeedCrOrig;
2838             if (rState.getFrame().inFrame())
2839             {
2840                 // parBreak() modifies m_aStates.top() so we can't apply resetFrame() directly on aState
2841                 resetFrame();
2842                 parBreak();
2843                 // Save this state for later use, so we only reset frame status only for the first shape inside a frame.
2844                 rState = m_aStates.top();
2845                 m_bNeedPap = true;
2846             }
2847             break;
2848         case Destination::MOMATH:
2849         {
2850             m_aMathBuffer.appendClosingTag(M_TOKEN(oMath));
2851 
2852             SvGlobalName aGlobalName(SO3_SM_CLASSID);
2853             comphelper::EmbeddedObjectContainer aContainer;
2854             OUString aName;
2855             uno::Reference<embed::XEmbeddedObject> xObject
2856                 = aContainer.CreateEmbeddedObject(aGlobalName.GetByteSequence(), aName);
2857             if (xObject) // rhbz#1766990 starmath might not be available
2858             {
2859                 uno::Reference<util::XCloseable> xComponent(xObject->getComponent(),
2860                                                             uno::UNO_SET_THROW);
2861                 // gcc4.4 (and 4.3 and possibly older) have a problem with dynamic_cast directly to the target class,
2862                 // so help it with an intermediate cast. I'm not sure what exactly the problem is, seems to be unrelated
2863                 // to RTLD_GLOBAL, so most probably a gcc bug.
2864                 auto& rImport = dynamic_cast<oox::FormulaImportBase&>(
2865                     dynamic_cast<SfxBaseModel&>(*xComponent));
2866                 rImport.readFormulaOoxml(m_aMathBuffer);
2867 
2868                 auto pValue = new RTFValue(xObject);
2869                 RTFSprms aMathAttributes;
2870                 aMathAttributes.set(NS_ooxml::LN_starmath, pValue);
2871                 writerfilter::Reference<Properties>::Pointer_t pProperties
2872                     = new RTFReferenceProperties(aMathAttributes);
2873                 Mapper().props(pProperties);
2874             }
2875 
2876             m_aMathBuffer = oox::formulaimport::XmlStreamBuilder();
2877         }
2878         break;
2879         case Destination::MR:
2880             lcl_DestinationToMath(m_aStates.top().getCurrentDestinationText(), m_aMathBuffer,
2881                                   m_bMathNor);
2882             break;
2883         case Destination::MF:
2884             m_aMathBuffer.appendClosingTag(M_TOKEN(f));
2885             break;
2886         case Destination::MFPR:
2887             m_aMathBuffer.appendClosingTag(M_TOKEN(fPr));
2888             break;
2889         case Destination::MCTRLPR:
2890             m_aMathBuffer.appendClosingTag(M_TOKEN(ctrlPr));
2891             break;
2892         case Destination::MNUM:
2893             m_aMathBuffer.appendClosingTag(M_TOKEN(num));
2894             break;
2895         case Destination::MDEN:
2896             m_aMathBuffer.appendClosingTag(M_TOKEN(den));
2897             break;
2898         case Destination::MACC:
2899             m_aMathBuffer.appendClosingTag(M_TOKEN(acc));
2900             break;
2901         case Destination::MACCPR:
2902             m_aMathBuffer.appendClosingTag(M_TOKEN(accPr));
2903             break;
2904         case Destination::MCHR:
2905         case Destination::MPOS:
2906         case Destination::MVERTJC:
2907         case Destination::MSTRIKEH:
2908         case Destination::MDEGHIDE:
2909         case Destination::MBEGCHR:
2910         case Destination::MSEPCHR:
2911         case Destination::MENDCHR:
2912         case Destination::MSUBHIDE:
2913         case Destination::MSUPHIDE:
2914         case Destination::MTYPE:
2915         case Destination::MGROW:
2916         {
2917             sal_Int32 nMathToken = 0;
2918             switch (rState.getDestination())
2919             {
2920                 case Destination::MCHR:
2921                     nMathToken = M_TOKEN(chr);
2922                     break;
2923                 case Destination::MPOS:
2924                     nMathToken = M_TOKEN(pos);
2925                     break;
2926                 case Destination::MVERTJC:
2927                     nMathToken = M_TOKEN(vertJc);
2928                     break;
2929                 case Destination::MSTRIKEH:
2930                     nMathToken = M_TOKEN(strikeH);
2931                     break;
2932                 case Destination::MDEGHIDE:
2933                     nMathToken = M_TOKEN(degHide);
2934                     break;
2935                 case Destination::MBEGCHR:
2936                     nMathToken = M_TOKEN(begChr);
2937                     break;
2938                 case Destination::MSEPCHR:
2939                     nMathToken = M_TOKEN(sepChr);
2940                     break;
2941                 case Destination::MENDCHR:
2942                     nMathToken = M_TOKEN(endChr);
2943                     break;
2944                 case Destination::MSUBHIDE:
2945                     nMathToken = M_TOKEN(subHide);
2946                     break;
2947                 case Destination::MSUPHIDE:
2948                     nMathToken = M_TOKEN(supHide);
2949                     break;
2950                 case Destination::MTYPE:
2951                     nMathToken = M_TOKEN(type);
2952                     break;
2953                 case Destination::MGROW:
2954                     nMathToken = M_TOKEN(grow);
2955                     break;
2956                 default:
2957                     break;
2958             }
2959 
2960             oox::formulaimport::XmlStream::AttributeList aAttribs;
2961             aAttribs[M_TOKEN(val)]
2962                 = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2963             m_aMathBuffer.appendOpeningTag(nMathToken, aAttribs);
2964             m_aMathBuffer.appendClosingTag(nMathToken);
2965         }
2966         break;
2967         case Destination::ME:
2968             m_aMathBuffer.appendClosingTag(M_TOKEN(e));
2969             break;
2970         case Destination::MBAR:
2971             m_aMathBuffer.appendClosingTag(M_TOKEN(bar));
2972             break;
2973         case Destination::MBARPR:
2974             m_aMathBuffer.appendClosingTag(M_TOKEN(barPr));
2975             break;
2976         case Destination::MD:
2977             m_aMathBuffer.appendClosingTag(M_TOKEN(d));
2978             break;
2979         case Destination::MDPR:
2980             m_aMathBuffer.appendClosingTag(M_TOKEN(dPr));
2981             break;
2982         case Destination::MFUNC:
2983             m_aMathBuffer.appendClosingTag(M_TOKEN(func));
2984             break;
2985         case Destination::MFUNCPR:
2986             m_aMathBuffer.appendClosingTag(M_TOKEN(funcPr));
2987             break;
2988         case Destination::MFNAME:
2989             m_aMathBuffer.appendClosingTag(M_TOKEN(fName));
2990             break;
2991         case Destination::MLIMLOW:
2992             m_aMathBuffer.appendClosingTag(M_TOKEN(limLow));
2993             break;
2994         case Destination::MLIMLOWPR:
2995             m_aMathBuffer.appendClosingTag(M_TOKEN(limLowPr));
2996             break;
2997         case Destination::MLIM:
2998             m_aMathBuffer.appendClosingTag(M_TOKEN(lim));
2999             break;
3000         case Destination::MM:
3001             m_aMathBuffer.appendClosingTag(M_TOKEN(m));
3002             break;
3003         case Destination::MMPR:
3004             m_aMathBuffer.appendClosingTag(M_TOKEN(mPr));
3005             break;
3006         case Destination::MMR:
3007             m_aMathBuffer.appendClosingTag(M_TOKEN(mr));
3008             break;
3009         case Destination::MNARY:
3010             m_aMathBuffer.appendClosingTag(M_TOKEN(nary));
3011             break;
3012         case Destination::MNARYPR:
3013             m_aMathBuffer.appendClosingTag(M_TOKEN(naryPr));
3014             break;
3015         case Destination::MSUB:
3016             m_aMathBuffer.appendClosingTag(M_TOKEN(sub));
3017             break;
3018         case Destination::MSUP:
3019             m_aMathBuffer.appendClosingTag(M_TOKEN(sup));
3020             break;
3021         case Destination::MLIMUPP:
3022             m_aMathBuffer.appendClosingTag(M_TOKEN(limUpp));
3023             break;
3024         case Destination::MLIMUPPPR:
3025             m_aMathBuffer.appendClosingTag(M_TOKEN(limUppPr));
3026             break;
3027         case Destination::MGROUPCHR:
3028             m_aMathBuffer.appendClosingTag(M_TOKEN(groupChr));
3029             break;
3030         case Destination::MGROUPCHRPR:
3031             m_aMathBuffer.appendClosingTag(M_TOKEN(groupChrPr));
3032             break;
3033         case Destination::MBORDERBOX:
3034             m_aMathBuffer.appendClosingTag(M_TOKEN(borderBox));
3035             break;
3036         case Destination::MBORDERBOXPR:
3037             m_aMathBuffer.appendClosingTag(M_TOKEN(borderBoxPr));
3038             break;
3039         case Destination::MRAD:
3040             m_aMathBuffer.appendClosingTag(M_TOKEN(rad));
3041             break;
3042         case Destination::MRADPR:
3043             m_aMathBuffer.appendClosingTag(M_TOKEN(radPr));
3044             break;
3045         case Destination::MDEG:
3046             m_aMathBuffer.appendClosingTag(M_TOKEN(deg));
3047             break;
3048         case Destination::MSSUB:
3049             m_aMathBuffer.appendClosingTag(M_TOKEN(sSub));
3050             break;
3051         case Destination::MSSUBPR:
3052             m_aMathBuffer.appendClosingTag(M_TOKEN(sSubPr));
3053             break;
3054         case Destination::MSSUP:
3055             m_aMathBuffer.appendClosingTag(M_TOKEN(sSup));
3056             break;
3057         case Destination::MSSUPPR:
3058             m_aMathBuffer.appendClosingTag(M_TOKEN(sSupPr));
3059             break;
3060         case Destination::MSSUBSUP:
3061             m_aMathBuffer.appendClosingTag(M_TOKEN(sSubSup));
3062             break;
3063         case Destination::MSSUBSUPPR:
3064             m_aMathBuffer.appendClosingTag(M_TOKEN(sSubSupPr));
3065             break;
3066         case Destination::MSPRE:
3067             m_aMathBuffer.appendClosingTag(M_TOKEN(sPre));
3068             break;
3069         case Destination::MSPREPR:
3070             m_aMathBuffer.appendClosingTag(M_TOKEN(sPrePr));
3071             break;
3072         case Destination::MBOX:
3073             m_aMathBuffer.appendClosingTag(M_TOKEN(box));
3074             break;
3075         case Destination::MEQARR:
3076             m_aMathBuffer.appendClosingTag(M_TOKEN(eqArr));
3077             break;
3078         case Destination::SHAPEGROUP:
3079             if (rState.getCreatedShapeGroup())
3080                 m_pSdrImport->popParent();
3081             break;
3082         case Destination::PROPNAME:
3083             if (&m_aStates.top().getDestinationText()
3084                 != m_aStates.top().getCurrentDestinationText())
3085                 break; // not for nested group
3086             rState.setPropName(m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
3087             break;
3088         case Destination::STATICVAL:
3089             if (&m_aStates.top().getDestinationText()
3090                 != m_aStates.top().getCurrentDestinationText())
3091                 break; // not for nested group
3092             if (m_xDocumentProperties.is())
3093             {
3094                 // Find out what is the key, value type and value we want to set.
3095                 uno::Reference<beans::XPropertyContainer> xPropertyContainer
3096                     = m_xDocumentProperties->getUserDefinedProperties();
3097                 const OUString& rKey = m_aStates.top().getPropName();
3098                 OUString aStaticVal
3099                     = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
3100                 uno::Any aAny;
3101                 if (m_aStates.top().getPropType() == cppu::UnoType<OUString>::get())
3102                     aAny <<= aStaticVal;
3103                 else if (m_aStates.top().getPropType() == cppu::UnoType<sal_Int32>::get())
3104                     aAny <<= aStaticVal.toInt32();
3105                 else if (m_aStates.top().getPropType() == cppu::UnoType<bool>::get())
3106                     aAny <<= aStaticVal.toBoolean();
3107                 else if (m_aStates.top().getPropType() == cppu::UnoType<util::DateTime>::get())
3108                     aAny <<= getDateTimeFromUserProp(aStaticVal);
3109                 else if (m_aStates.top().getPropType() == cppu::UnoType<double>::get())
3110                     aAny <<= aStaticVal.toDouble();
3111 
3112                 xPropertyContainer->addProperty(rKey, beans::PropertyAttribute::REMOVABLE, aAny);
3113             }
3114             break;
3115         case Destination::USERPROPS:
3116         {
3117             // These are the imported properties.
3118             uno::Reference<document::XDocumentProperties> xDocumentProperties
3119                 = m_xDocumentProperties;
3120 
3121             // These are the real document properties.
3122             uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier(
3123                 m_xDstDoc, uno::UNO_QUERY);
3124             if (xDocumentPropertiesSupplier.is())
3125                 m_xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties();
3126 
3127             if (m_xDocumentProperties.is())
3128             {
3129                 if (!m_bIsNewDoc)
3130                 {
3131                     // Check classification.
3132                     if (!SfxClassificationHelper::ShowPasteInfo(SfxClassificationHelper::CheckPaste(
3133                             xDocumentProperties, m_xDocumentProperties)))
3134                         return RTFError::CLASSIFICATION;
3135                 }
3136 
3137                 uno::Reference<beans::XPropertyContainer> xClipboardPropertyContainer
3138                     = xDocumentProperties->getUserDefinedProperties();
3139                 uno::Reference<beans::XPropertyContainer> xDocumentPropertyContainer
3140                     = m_xDocumentProperties->getUserDefinedProperties();
3141                 uno::Reference<beans::XPropertySet> xClipboardPropertySet(
3142                     xClipboardPropertyContainer, uno::UNO_QUERY);
3143                 uno::Reference<beans::XPropertySet> xDocumentPropertySet(xDocumentPropertyContainer,
3144                                                                          uno::UNO_QUERY);
3145                 const uno::Sequence<beans::Property> aClipboardProperties
3146                     = xClipboardPropertySet->getPropertySetInfo()->getProperties();
3147                 uno::Sequence<beans::Property> aDocumentProperties
3148                     = xDocumentPropertySet->getPropertySetInfo()->getProperties();
3149 
3150                 for (const beans::Property& rProperty : aClipboardProperties)
3151                 {
3152                     const OUString& rKey = rProperty.Name;
3153                     uno::Any aValue = xClipboardPropertySet->getPropertyValue(rKey);
3154 
3155                     try
3156                     {
3157                         if (lcl_containsProperty(aDocumentProperties, rKey))
3158                         {
3159                             // When pasting, don't update existing properties.
3160                             if (!m_bIsNewDoc)
3161                                 xDocumentPropertySet->setPropertyValue(rKey, aValue);
3162                         }
3163                         else
3164                             xDocumentPropertyContainer->addProperty(
3165                                 rKey, beans::PropertyAttribute::REMOVABLE, aValue);
3166                     }
3167                     catch (const uno::Exception&)
3168                     {
3169                         TOOLS_WARN_EXCEPTION("writerfilter.rtf", "failed to set property " << rKey);
3170                     }
3171                 }
3172             }
3173         }
3174         break;
3175         default:
3176             break;
3177     }
3178 
3179     return RTFError::OK;
3180 }
3181 
afterPopState(RTFParserState & rState)3182 void RTFDocumentImpl::afterPopState(RTFParserState& rState)
3183 {
3184     // list table
3185     switch (rState.getDestination())
3186     {
3187         case Destination::LISTENTRY:
3188         {
3189             auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms());
3190             m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_abstractNum, pValue,
3191                                   RTFOverwrite::NO_APPEND);
3192             m_aListTable[rState.getCurrentListIndex()] = pValue;
3193             m_nListLevel = -1;
3194             m_aInvalidListTableFirstIndents[rState.getCurrentListIndex()]
3195                 = m_aInvalidListLevelFirstIndents;
3196             m_aInvalidListLevelFirstIndents.clear();
3197         }
3198         break;
3199         case Destination::PARAGRAPHNUMBERING:
3200         {
3201             RTFValue::Pointer_t pIdValue
3202                 = rState.getTableAttributes().find(NS_ooxml::LN_CT_AbstractNum_nsid);
3203             if (pIdValue && !m_aStates.empty())
3204             {
3205                 // Abstract numbering
3206                 RTFSprms aLeveltextAttributes;
3207                 OUString aTextValue;
3208                 RTFValue::Pointer_t pTextBefore
3209                     = rState.getTableAttributes().find(NS_ooxml::LN_CT_LevelText_val);
3210                 if (pTextBefore)
3211                     aTextValue += pTextBefore->getString();
3212                 aTextValue += "%1";
3213                 RTFValue::Pointer_t pTextAfter
3214                     = rState.getTableAttributes().find(NS_ooxml::LN_CT_LevelSuffix_val);
3215                 if (pTextAfter)
3216                     aTextValue += pTextAfter->getString();
3217                 auto pTextValue = new RTFValue(aTextValue);
3218                 aLeveltextAttributes.set(NS_ooxml::LN_CT_LevelText_val, pTextValue);
3219 
3220                 RTFSprms aLevelAttributes;
3221                 RTFSprms aLevelSprms;
3222                 auto pIlvlValue = new RTFValue(0);
3223                 aLevelAttributes.set(NS_ooxml::LN_CT_Lvl_ilvl, pIlvlValue);
3224 
3225                 RTFValue::Pointer_t pFmtValue
3226                     = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_numFmt);
3227                 if (pFmtValue)
3228                     aLevelSprms.set(NS_ooxml::LN_CT_Lvl_numFmt, pFmtValue);
3229 
3230                 RTFValue::Pointer_t pStartatValue
3231                     = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_start);
3232                 if (pStartatValue)
3233                     aLevelSprms.set(NS_ooxml::LN_CT_Lvl_start, pStartatValue);
3234 
3235                 auto pLeveltextValue = new RTFValue(aLeveltextAttributes);
3236                 aLevelSprms.set(NS_ooxml::LN_CT_Lvl_lvlText, pLeveltextValue);
3237                 RTFValue::Pointer_t pRunProps
3238                     = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_rPr);
3239                 if (pRunProps)
3240                     aLevelSprms.set(NS_ooxml::LN_CT_Lvl_rPr, pRunProps);
3241 
3242                 RTFSprms aAbstractAttributes;
3243                 RTFSprms aAbstractSprms;
3244                 aAbstractAttributes.set(NS_ooxml::LN_CT_AbstractNum_abstractNumId, pIdValue);
3245                 auto pLevelValue = new RTFValue(aLevelAttributes, aLevelSprms);
3246                 aAbstractSprms.set(NS_ooxml::LN_CT_AbstractNum_lvl, pLevelValue,
3247                                    RTFOverwrite::NO_APPEND);
3248 
3249                 RTFSprms aListTableSprms;
3250                 auto pAbstractValue = new RTFValue(aAbstractAttributes, aAbstractSprms);
3251                 // It's important that Numbering_abstractNum and Numbering_num never overwrites previous values.
3252                 aListTableSprms.set(NS_ooxml::LN_CT_Numbering_abstractNum, pAbstractValue,
3253                                     RTFOverwrite::NO_APPEND);
3254 
3255                 // Numbering
3256                 RTFSprms aNumberingAttributes;
3257                 RTFSprms aNumberingSprms;
3258                 aNumberingAttributes.set(NS_ooxml::LN_CT_AbstractNum_nsid, pIdValue);
3259                 aNumberingSprms.set(NS_ooxml::LN_CT_Num_abstractNumId, pIdValue);
3260                 auto pNumberingValue = new RTFValue(aNumberingAttributes, aNumberingSprms);
3261                 aListTableSprms.set(NS_ooxml::LN_CT_Numbering_num, pNumberingValue,
3262                                     RTFOverwrite::NO_APPEND);
3263 
3264                 // Table
3265                 RTFSprms aListTableAttributes;
3266                 writerfilter::Reference<Properties>::Pointer_t pProp
3267                     = new RTFReferenceProperties(aListTableAttributes, aListTableSprms);
3268 
3269                 RTFReferenceTable::Entries_t aListTableEntries;
3270                 aListTableEntries.insert(std::make_pair(0, pProp));
3271                 writerfilter::Reference<Table>::Pointer_t const pTable(
3272                     new RTFReferenceTable(aListTableEntries));
3273                 Mapper().table(NS_ooxml::LN_NUMBERING, pTable);
3274 
3275                 // Use it
3276                 putNestedSprm(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_numPr,
3277                               NS_ooxml::LN_CT_NumPr_ilvl, pIlvlValue);
3278                 putNestedSprm(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_numPr,
3279                               NS_ooxml::LN_CT_NumPr_numId, pIdValue);
3280             }
3281         }
3282         break;
3283         case Destination::PARAGRAPHNUMBERING_TEXTAFTER:
3284             if (!m_aStates.empty())
3285             {
3286                 // FIXME: don't use pDestinationText, points to popped state
3287                 auto pValue = new RTFValue(rState.getDestinationText().makeStringAndClear(), true);
3288                 m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_LevelSuffix_val, pValue);
3289             }
3290             break;
3291         case Destination::PARAGRAPHNUMBERING_TEXTBEFORE:
3292             if (!m_aStates.empty())
3293             {
3294                 // FIXME: don't use pDestinationText, points to popped state
3295                 auto pValue = new RTFValue(rState.getDestinationText().makeStringAndClear(), true);
3296                 m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_LevelText_val, pValue);
3297             }
3298             break;
3299         case Destination::LISTNAME:
3300             break;
3301         case Destination::LISTLEVEL:
3302             if (!m_aStates.empty())
3303             {
3304                 auto pInnerValue = new RTFValue(m_aStates.top().getListLevelNum()++);
3305                 rState.getTableAttributes().set(NS_ooxml::LN_CT_Lvl_ilvl, pInnerValue);
3306 
3307                 auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms());
3308                 if (m_aStates.top().getDestination() != Destination::LFOLEVEL)
3309                     m_aStates.top().getListLevelEntries().set(NS_ooxml::LN_CT_AbstractNum_lvl,
3310                                                               pValue, RTFOverwrite::NO_APPEND);
3311                 else
3312                     m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_NumLvl_lvl, pValue);
3313             }
3314             break;
3315         case Destination::LFOLEVEL:
3316             if (!m_aStates.empty())
3317             {
3318                 auto pInnerValue = new RTFValue(m_aStates.top().getListLevelNum()++);
3319                 rState.getTableAttributes().set(NS_ooxml::LN_CT_NumLvl_ilvl, pInnerValue);
3320 
3321                 auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms());
3322                 m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Num_lvlOverride, pValue,
3323                                                     RTFOverwrite::NO_APPEND);
3324             }
3325             break;
3326         // list override table
3327         case Destination::LISTOVERRIDEENTRY:
3328             if (!m_aStates.empty())
3329             {
3330                 if (m_aStates.top().getDestination() == Destination::LISTOVERRIDEENTRY)
3331                 {
3332                     // copy properties upwards so upper popState() inserts it
3333                     m_aStates.top().getTableAttributes() = rState.getTableAttributes();
3334                     m_aStates.top().getTableSprms() = rState.getTableSprms();
3335                 }
3336                 else
3337                 {
3338                     auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms());
3339                     m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_num, pValue,
3340                                           RTFOverwrite::NO_APPEND);
3341                     m_aListOverrideTable[rState.getCurrentListOverrideIndex()]
3342                         = rState.getCurrentListIndex();
3343                 }
3344             }
3345             break;
3346         case Destination::LEVELTEXT:
3347             if (!m_aStates.empty())
3348             {
3349                 auto pValue = new RTFValue(rState.getTableAttributes());
3350                 m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Lvl_lvlText, pValue);
3351             }
3352             break;
3353         case Destination::LEVELNUMBERS:
3354             if (!m_aStates.empty())
3355             {
3356                 m_aStates.top().getTableSprms() = rState.getTableSprms();
3357                 if (m_aStates.top().getDestination() == Destination::LEVELNUMBERS
3358                     || m_aStates.top().getDestination() == Destination::LISTLEVEL)
3359                     // Parent state is level number or list level, current state is
3360                     // level numbers: mark parent as invalid as well if necessary.
3361                     m_aStates.top().setLevelNumbersValid(rState.getLevelNumbersValid());
3362             }
3363             break;
3364         case Destination::FIELDINSTRUCTION:
3365             if (!m_aStates.empty())
3366                 m_aStates.top().setFieldStatus(RTFFieldStatus::INSTRUCTION);
3367             break;
3368         case Destination::FIELDRESULT:
3369             if (!m_aStates.empty())
3370                 m_aStates.top().setFieldStatus(RTFFieldStatus::RESULT);
3371             break;
3372         case Destination::FIELD:
3373             if (rState.getFieldStatus() == RTFFieldStatus::INSTRUCTION)
3374                 singleChar(cFieldEnd);
3375             break;
3376         case Destination::SHAPEPROPERTYVALUEPICT:
3377             if (!m_aStates.empty())
3378             {
3379                 m_aStates.top().getPicture() = rState.getPicture();
3380                 // both \sp and \sv are destinations, copy the text up-ward for later
3381                 m_aStates.top().getDestinationText() = rState.getDestinationText();
3382             }
3383             break;
3384         case Destination::FALT:
3385             if (!m_aStates.empty())
3386                 m_aStates.top().getTableSprms() = rState.getTableSprms();
3387             break;
3388         case Destination::SHAPEPROPERTYNAME:
3389         case Destination::SHAPEPROPERTYVALUE:
3390         case Destination::SHAPEPROPERTY:
3391             if (!m_aStates.empty())
3392             {
3393                 m_aStates.top().getShape() = rState.getShape();
3394                 m_aStates.top().getPicture() = rState.getPicture();
3395                 m_aStates.top().getCharacterAttributes() = rState.getCharacterAttributes();
3396             }
3397             break;
3398         case Destination::SHAPEINSTRUCTION:
3399             if (!m_aStates.empty()
3400                 && m_aStates.top().getDestination() == Destination::SHAPEINSTRUCTION)
3401             {
3402                 // Shape instruction inside other shape instruction: just copy new shape settings:
3403                 // it will be resolved on end of topmost shape instruction destination
3404                 m_aStates.top().getShape() = rState.getShape();
3405                 m_aStates.top().getPicture() = rState.getPicture();
3406                 m_aStates.top().getCharacterSprms() = rState.getCharacterSprms();
3407                 m_aStates.top().getCharacterAttributes() = rState.getCharacterAttributes();
3408             }
3409             break;
3410         case Destination::FLYMAINCONTENT:
3411         case Destination::SHPPICT:
3412         case Destination::SHAPE:
3413             if (!m_aStates.empty())
3414             {
3415                 m_aStates.top().getFrame() = rState.getFrame();
3416                 if (rState.getDestination() == Destination::SHPPICT
3417                     && m_aStates.top().getDestination() == Destination::LISTPICTURE)
3418                 {
3419                     RTFSprms aAttributes;
3420                     aAttributes.set(NS_ooxml::LN_CT_NumPicBullet_numPicBulletId,
3421                                     new RTFValue(m_nListPictureId++));
3422                     RTFSprms aSprms;
3423                     // Dummy value, real picture is already sent to dmapper.
3424                     aSprms.set(NS_ooxml::LN_CT_NumPicBullet_pict, new RTFValue(0));
3425                     auto pValue = new RTFValue(aAttributes, aSprms);
3426                     m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_numPicBullet, pValue,
3427                                           RTFOverwrite::NO_APPEND);
3428                 }
3429             }
3430             break;
3431         case Destination::SHAPETEXT:
3432             if (!m_aStates.empty())
3433             {
3434                 // If we're leaving the shapetext group (it may have nested ones) and this is a shape, not an old drawingobject.
3435                 if (m_aStates.top().getDestination() != Destination::SHAPETEXT
3436                     && !m_aStates.top().getDrawingObject().getHadShapeText())
3437                 {
3438                     m_aStates.top().setHadShapeText(true);
3439                     if (!m_aStates.top().getCurrentBuffer())
3440                         m_pSdrImport->close();
3441                     else
3442                         m_aStates.top().getCurrentBuffer()->push_back(
3443                             Buf_t(BUFFER_ENDSHAPE, nullptr, nullptr));
3444                 }
3445 
3446                 // It's allowed to declare these inside the shape text, and they
3447                 // are expected to have an effect for the whole shape.
3448                 if (rState.getDrawingObject().getLeft())
3449                     m_aStates.top().getDrawingObject().setLeft(rState.getDrawingObject().getLeft());
3450                 if (rState.getDrawingObject().getTop())
3451                     m_aStates.top().getDrawingObject().setTop(rState.getDrawingObject().getTop());
3452                 if (rState.getDrawingObject().getRight())
3453                     m_aStates.top().getDrawingObject().setRight(
3454                         rState.getDrawingObject().getRight());
3455                 if (rState.getDrawingObject().getBottom())
3456                     m_aStates.top().getDrawingObject().setBottom(
3457                         rState.getDrawingObject().getBottom());
3458             }
3459             break;
3460         case Destination::PROPNAME:
3461             if (m_aStates.top().getDestination() == Destination::USERPROPS)
3462                 m_aStates.top().setPropName(rState.getPropName());
3463             break;
3464         default:
3465         {
3466             if (!m_aStates.empty() && m_aStates.top().getDestination() == Destination::PICT)
3467                 m_aStates.top().getPicture() = rState.getPicture();
3468         }
3469         break;
3470     }
3471 }
3472 
popState()3473 RTFError RTFDocumentImpl::popState()
3474 {
3475     //SAL_INFO("writerfilter", __func__ << " before pop: m_pTokenizer->getGroup() " << m_pTokenizer->getGroup() <<
3476     //                         ", dest state: " << m_aStates.top().eDestination);
3477 
3478     checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
3479     RTFParserState aState(m_aStates.top());
3480     m_bWasInFrame = aState.getFrame().inFrame();
3481 
3482     // dmapper expects some content in header/footer, so if there would be nothing, add an empty paragraph.
3483     if (m_pTokenizer->getGroup() == 1 && m_bFirstRun)
3484     {
3485         switch (m_nStreamType)
3486         {
3487             case NS_ooxml::LN_headerl:
3488             case NS_ooxml::LN_headerr:
3489             case NS_ooxml::LN_headerf:
3490             case NS_ooxml::LN_footerl:
3491             case NS_ooxml::LN_footerr:
3492             case NS_ooxml::LN_footerf:
3493                 dispatchSymbol(RTFKeyword::PAR);
3494                 break;
3495         }
3496     }
3497 
3498     RTFError eError = beforePopState(aState);
3499     if (eError != RTFError::OK)
3500         return eError;
3501 
3502     // See if we need to end a track change
3503     if (aState.getStartedTrackchange())
3504     {
3505         RTFSprms aTCSprms;
3506         auto pValue = new RTFValue(0);
3507         aTCSprms.set(NS_ooxml::LN_endtrackchange, pValue);
3508         if (!m_aStates.top().getCurrentBuffer())
3509             Mapper().props(new RTFReferenceProperties(RTFSprms(), aTCSprms));
3510         else
3511             bufferProperties(*m_aStates.top().getCurrentBuffer(),
3512                              new RTFValue(RTFSprms(), aTCSprms), nullptr);
3513     }
3514 
3515     // This is the end of the doc, see if we need to close the last section.
3516     if (m_pTokenizer->getGroup() == 1 && !m_bFirstRun)
3517     {
3518         // \par means an empty paragraph at the end of footnotes/endnotes, but
3519         // not in case of other substreams, like headers.
3520         if (m_bNeedCr && m_nStreamType != NS_ooxml::LN_footnote
3521             && m_nStreamType != NS_ooxml::LN_endnote && m_bIsNewDoc)
3522             dispatchSymbol(RTFKeyword::PAR);
3523         if (m_bNeedSect) // may be set by dispatchSymbol above!
3524             sectBreak(true);
3525     }
3526 
3527     m_aStates.pop();
3528 
3529     m_pTokenizer->popGroup();
3530 
3531     afterPopState(aState);
3532 
3533     if (aState.getCurrentBuffer() == &m_aSuperBuffer)
3534     {
3535         OSL_ASSERT(!m_aStates.empty() && m_aStates.top().getCurrentBuffer() == nullptr);
3536 
3537         if (!m_aSuperBuffer.empty())
3538             replayBuffer(m_aSuperBuffer, nullptr, nullptr);
3539     }
3540 
3541     if (!m_aStates.empty() && m_aStates.top().getTableRowWidthAfter() > 0
3542         && aState.getTableRowWidthAfter() == 0)
3543         // An RTFKeyword::ROW in the inner group already parsed nTableRowWidthAfter,
3544         // don't do it again in the outer state later.
3545         m_aStates.top().setTableRowWidthAfter(0);
3546 
3547     if (m_nResetBreakOnSectBreak != RTFKeyword::invalid && !m_aStates.empty())
3548     {
3549         // Section break type created for \page still has an effect in the
3550         // outer state as well.
3551         RTFValue::Pointer_t pType
3552             = aState.getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_type);
3553         if (pType)
3554             m_aStates.top().getSectionSprms().set(NS_ooxml::LN_EG_SectPrContents_type, pType);
3555     }
3556 
3557     return RTFError::OK;
3558 }
3559 
handleEmbeddedObject()3560 RTFError RTFDocumentImpl::handleEmbeddedObject()
3561 {
3562     OString aStr
3563         = OUStringToOString(m_aStates.top().getCurrentDestinationText()->makeStringAndClear(),
3564                             RTL_TEXTENCODING_ASCII_US);
3565     std::unique_ptr<SvStream> pStream(new SvMemoryStream());
3566     if (!msfilter::rtfutil::ExtractOLE2FromObjdata(aStr, *pStream))
3567         return RTFError::HEX_INVALID;
3568 
3569     uno::Reference<io::XInputStream> xInputStream(
3570         new utl::OSeekableInputStreamWrapper(pStream.release(), /*_bOwner=*/true));
3571     auto pStreamValue = new RTFValue(xInputStream);
3572     m_aOLEAttributes.set(NS_ooxml::LN_inputstream, pStreamValue);
3573 
3574     return RTFError::OK;
3575 }
3576 
isInBackground()3577 bool RTFDocumentImpl::isInBackground() { return m_aStates.top().getInBackground(); }
3578 
getInternalState()3579 RTFInternalState RTFDocumentImpl::getInternalState() { return m_aStates.top().getInternalState(); }
3580 
setInternalState(RTFInternalState nInternalState)3581 void RTFDocumentImpl::setInternalState(RTFInternalState nInternalState)
3582 {
3583     m_aStates.top().setInternalState(nInternalState);
3584 }
3585 
getDestination()3586 Destination RTFDocumentImpl::getDestination() { return m_aStates.top().getDestination(); }
3587 
setDestination(Destination eDestination)3588 void RTFDocumentImpl::setDestination(Destination eDestination)
3589 {
3590     m_aStates.top().setDestination(eDestination);
3591 }
3592 
3593 // this is a questionably named method that is used only in a very special
3594 // situation where it looks like the "current" buffer is needed?
setDestinationText(std::u16string_view rString)3595 void RTFDocumentImpl::setDestinationText(std::u16string_view rString)
3596 {
3597     m_aStates.top().getDestinationText().setLength(0);
3598     m_aStates.top().getDestinationText().append(rString);
3599 }
3600 
getSkipUnknown()3601 bool RTFDocumentImpl::getSkipUnknown() { return m_bSkipUnknown; }
3602 
setSkipUnknown(bool bSkipUnknown)3603 void RTFDocumentImpl::setSkipUnknown(bool bSkipUnknown) { m_bSkipUnknown = bSkipUnknown; }
3604 
FilterControlChars(Destination const destination,OUString const & rString)3605 static auto FilterControlChars(Destination const destination, OUString const& rString) -> OUString
3606 {
3607     if (destination == Destination::LEVELNUMBERS || destination == Destination::LEVELTEXT)
3608     { // control characters are magic here!
3609         return rString;
3610     }
3611     OUStringBuffer buf(rString.getLength());
3612     for (sal_Int32 i = 0; i < rString.getLength(); ++i)
3613     {
3614         sal_Unicode const ch(rString[i]);
3615         if (!linguistic::IsControlChar(ch) || ch == '\r' || ch == '\n' || ch == '\t')
3616         {
3617             buf.append(ch);
3618         }
3619         else
3620         {
3621             SAL_INFO("writerfilter.rtf", "filtering control character");
3622         }
3623     }
3624     return buf.makeStringAndClear();
3625 }
3626 
checkUnicode(bool bUnicode,bool bHex)3627 void RTFDocumentImpl::checkUnicode(bool bUnicode, bool bHex)
3628 {
3629     if (bUnicode && !m_aUnicodeBuffer.isEmpty())
3630     {
3631         OUString aString = m_aUnicodeBuffer.toString();
3632         m_aUnicodeBuffer.setLength(0);
3633         aString = FilterControlChars(m_aStates.top().getDestination(), aString);
3634         text(aString);
3635     }
3636     if (bHex && !m_aHexBuffer.isEmpty())
3637     {
3638         rtl_TextEncoding nEncoding = m_aStates.top().getCurrentEncoding();
3639         if (m_aStates.top().getDestination() == Destination::FONTENTRY
3640             && m_aStates.top().getCurrentEncoding() == RTL_TEXTENCODING_SYMBOL)
3641             nEncoding = RTL_TEXTENCODING_MS_1252;
3642         OUString aString = OStringToOUString(m_aHexBuffer.toString(), nEncoding);
3643         m_aHexBuffer.setLength(0);
3644         aString = FilterControlChars(m_aStates.top().getDestination(), aString);
3645         text(aString);
3646     }
3647 }
3648 
RTFParserState(RTFDocumentImpl * pDocumentImpl)3649 RTFParserState::RTFParserState(RTFDocumentImpl* pDocumentImpl)
3650     : m_pDocumentImpl(pDocumentImpl)
3651     , m_nInternalState(RTFInternalState::NORMAL)
3652     , m_eDestination(Destination::NORMAL)
3653     , m_eFieldStatus(RTFFieldStatus::NONE)
3654     , m_bFieldLocked(false)
3655     , m_nBorderState(RTFBorderState::NONE)
3656     , m_nCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(0))
3657     , m_nUc(1)
3658     , m_nCharsToSkip(0)
3659     , m_nBinaryToRead(0)
3660     , m_nListLevelNum(0)
3661     , m_bLevelNumbersValid(true)
3662     , m_aFrame(this)
3663     , m_eRunType(RunType::NONE)
3664     , m_nYear(0)
3665     , m_nMonth(0)
3666     , m_nDay(0)
3667     , m_nHour(0)
3668     , m_nMinute(0)
3669     , m_pCurrentDestinationText(nullptr)
3670     , m_nCurrentStyleIndex(-1)
3671     , m_nCurrentCharacterStyleIndex(-1)
3672     , m_pCurrentBuffer(nullptr)
3673     , m_bInListpicture(false)
3674     , m_bInBackground(false)
3675     , m_bHadShapeText(false)
3676     , m_bInShapeGroup(false)
3677     , m_bInShape(false)
3678     , m_bCreatedShapeGroup(false)
3679     , m_bStartedTrackchange(false)
3680     , m_nTableRowWidthAfter(0)
3681 {
3682 }
3683 
resetFrame()3684 void RTFDocumentImpl::resetFrame() { m_aStates.top().getFrame() = RTFFrame(&m_aStates.top()); }
3685 
bufferProperties(RTFBuffer_t & rBuffer,const RTFValue::Pointer_t & pValue,const tools::SvRef<TableRowBuffer> & pTableProperties)3686 void RTFDocumentImpl::bufferProperties(RTFBuffer_t& rBuffer, const RTFValue::Pointer_t& pValue,
3687                                        const tools::SvRef<TableRowBuffer>& pTableProperties)
3688 {
3689     rBuffer.emplace_back(BUFFER_SETSTYLE, new RTFValue(m_aStates.top().getCurrentStyleIndex()),
3690                          nullptr);
3691     rBuffer.emplace_back(BUFFER_PROPS, pValue, pTableProperties);
3692 }
3693 
3694 RTFShape::RTFShape() = default;
3695 
3696 RTFDrawingObject::RTFDrawingObject() = default;
3697 
RTFFrame(RTFParserState * pParserState)3698 RTFFrame::RTFFrame(RTFParserState* pParserState)
3699     : m_pDocumentImpl(pParserState->getDocumentImpl())
3700     , m_nX(0)
3701     , m_nY(0)
3702     , m_nW(0)
3703     , m_nH(0)
3704     , m_nHoriPadding(0)
3705     , m_nVertPadding(0)
3706     , m_nHoriAlign(0)
3707     , m_nHoriAnchor(0)
3708     , m_nVertAlign(0)
3709     , m_nVertAnchor(0)
3710     , m_nHRule(NS_ooxml::LN_Value_doc_ST_HeightRule_auto)
3711 {
3712 }
3713 
setSprm(Id nId,Id nValue)3714 void RTFFrame::setSprm(Id nId, Id nValue)
3715 {
3716     if (m_pDocumentImpl->getFirstRun() && !m_pDocumentImpl->isStyleSheetImport())
3717     {
3718         m_pDocumentImpl->checkFirstRun();
3719         m_pDocumentImpl->setNeedPar(false);
3720     }
3721     switch (nId)
3722     {
3723         case NS_ooxml::LN_CT_FramePr_w:
3724             m_nW = nValue;
3725             break;
3726         case NS_ooxml::LN_CT_FramePr_h:
3727             m_nH = nValue;
3728             break;
3729         case NS_ooxml::LN_CT_FramePr_x:
3730             m_nX = nValue;
3731             break;
3732         case NS_ooxml::LN_CT_FramePr_y:
3733             m_nY = nValue;
3734             break;
3735         case NS_ooxml::LN_CT_FramePr_hSpace:
3736             m_nHoriPadding = nValue;
3737             break;
3738         case NS_ooxml::LN_CT_FramePr_vSpace:
3739             m_nVertPadding = nValue;
3740             break;
3741         case NS_ooxml::LN_CT_FramePr_xAlign:
3742             m_nHoriAlign = nValue;
3743             break;
3744         case NS_ooxml::LN_CT_FramePr_hAnchor:
3745             m_nHoriAnchor = nValue;
3746             break;
3747         case NS_ooxml::LN_CT_FramePr_yAlign:
3748             m_nVertAlign = nValue;
3749             break;
3750         case NS_ooxml::LN_CT_FramePr_vAnchor:
3751             m_nVertAnchor = nValue;
3752             break;
3753         case NS_ooxml::LN_CT_FramePr_wrap:
3754             m_oWrap = nValue;
3755             break;
3756         default:
3757             break;
3758     }
3759 }
3760 
getSprms()3761 RTFSprms RTFFrame::getSprms()
3762 {
3763     RTFSprms sprms;
3764 
3765     static const Id pNames[]
3766         = { NS_ooxml::LN_CT_FramePr_x,       NS_ooxml::LN_CT_FramePr_y,
3767             NS_ooxml::LN_CT_FramePr_hRule, // Make sure nHRule is processed before nH
3768             NS_ooxml::LN_CT_FramePr_h,       NS_ooxml::LN_CT_FramePr_w,
3769             NS_ooxml::LN_CT_FramePr_hSpace,  NS_ooxml::LN_CT_FramePr_vSpace,
3770             NS_ooxml::LN_CT_FramePr_hAnchor, NS_ooxml::LN_CT_FramePr_vAnchor,
3771             NS_ooxml::LN_CT_FramePr_xAlign,  NS_ooxml::LN_CT_FramePr_yAlign,
3772             NS_ooxml::LN_CT_FramePr_wrap,    NS_ooxml::LN_CT_FramePr_dropCap,
3773             NS_ooxml::LN_CT_FramePr_lines };
3774 
3775     for (Id nId : pNames)
3776     {
3777         RTFValue::Pointer_t pValue;
3778 
3779         switch (nId)
3780         {
3781             case NS_ooxml::LN_CT_FramePr_x:
3782                 if (m_nX != 0)
3783                     pValue = new RTFValue(m_nX);
3784                 break;
3785             case NS_ooxml::LN_CT_FramePr_y:
3786                 if (m_nY != 0)
3787                     pValue = new RTFValue(m_nY);
3788                 break;
3789             case NS_ooxml::LN_CT_FramePr_h:
3790                 if (m_nH != 0)
3791                 {
3792                     if (m_nHRule == NS_ooxml::LN_Value_doc_ST_HeightRule_exact)
3793                         pValue = new RTFValue(-m_nH); // The negative value just sets nHRule
3794                     else
3795                         pValue = new RTFValue(m_nH);
3796                 }
3797                 break;
3798             case NS_ooxml::LN_CT_FramePr_w:
3799                 if (m_nW != 0)
3800                     pValue = new RTFValue(m_nW);
3801                 break;
3802             case NS_ooxml::LN_CT_FramePr_hSpace:
3803                 if (m_nHoriPadding != 0)
3804                     pValue = new RTFValue(m_nHoriPadding);
3805                 break;
3806             case NS_ooxml::LN_CT_FramePr_vSpace:
3807                 if (m_nVertPadding != 0)
3808                     pValue = new RTFValue(m_nVertPadding);
3809                 break;
3810             case NS_ooxml::LN_CT_FramePr_hAnchor:
3811             {
3812                 if (m_nHoriAnchor == 0)
3813                     m_nHoriAnchor = NS_ooxml::LN_Value_doc_ST_HAnchor_margin;
3814                 pValue = new RTFValue(m_nHoriAnchor);
3815             }
3816             break;
3817             case NS_ooxml::LN_CT_FramePr_vAnchor:
3818             {
3819                 if (m_nVertAnchor == 0)
3820                     m_nVertAnchor = NS_ooxml::LN_Value_doc_ST_VAnchor_margin;
3821                 pValue = new RTFValue(m_nVertAnchor);
3822             }
3823             break;
3824             case NS_ooxml::LN_CT_FramePr_xAlign:
3825                 pValue = new RTFValue(m_nHoriAlign);
3826                 break;
3827             case NS_ooxml::LN_CT_FramePr_yAlign:
3828                 pValue = new RTFValue(m_nVertAlign);
3829                 break;
3830             case NS_ooxml::LN_CT_FramePr_hRule:
3831             {
3832                 if (m_nH < 0)
3833                     m_nHRule = NS_ooxml::LN_Value_doc_ST_HeightRule_exact;
3834                 else if (m_nH > 0)
3835                     m_nHRule = NS_ooxml::LN_Value_doc_ST_HeightRule_atLeast;
3836                 pValue = new RTFValue(m_nHRule);
3837             }
3838             break;
3839             case NS_ooxml::LN_CT_FramePr_wrap:
3840                 if (m_oWrap)
3841                     pValue = new RTFValue(*m_oWrap);
3842                 break;
3843             default:
3844                 break;
3845         }
3846 
3847         if (pValue)
3848             sprms.set(nId, pValue);
3849     }
3850 
3851     RTFSprms frameprSprms;
3852     frameprSprms.set(NS_ooxml::LN_CT_PPrBase_framePr, new RTFValue(sprms));
3853     return frameprSprms;
3854 }
3855 
hasProperties() const3856 bool RTFFrame::hasProperties() const
3857 {
3858     return m_nX != 0 || m_nY != 0 || m_nW != 0 || m_nH != 0 || m_nHoriPadding != 0
3859            || m_nVertPadding != 0 || m_nHoriAlign != 0 || m_nHoriAnchor != 0 || m_nVertAlign != 0
3860            || m_nVertAnchor != 0;
3861 }
3862 
3863 } // namespace writerfilter
3864 
3865 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3866