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