lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / writerfilter / source / rtftok / rtfdocumentimpl.cxx
blob6c97111bed717cbd06e4dd896b1cc84cf6e26e41
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"
11 #include <memory>
12 #include <com/sun/star/embed/XEmbeddedObject.hpp>
13 #include <com/sun/star/beans/PropertyAttribute.hpp>
14 #include <com/sun/star/io/WrongFormatException.hpp>
15 #include <com/sun/star/lang/XServiceInfo.hpp>
16 #include <com/sun/star/text/WrapTextMode.hpp>
17 #include <com/sun/star/text/TextContentAnchorType.hpp>
18 #include <o3tl/clamp.hxx>
19 #include <unotools/ucbstreamhelper.hxx>
20 #include <unotools/streamwrap.hxx>
21 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
22 #include <filter/msfilter/util.hxx>
23 #include <filter/msfilter/rtfutil.hxx>
24 #include <comphelper/string.hxx>
25 #include <tools/globname.hxx>
26 #include <tools/datetimeutils.hxx>
27 #include <comphelper/classids.hxx>
28 #include <comphelper/embeddedobjectcontainer.hxx>
29 #include <sfx2/sfxbasemodel.hxx>
30 #include <sfx2/classificationhelper.hxx>
31 #include <oox/mathml/import.hxx>
32 #include <ooxml/resourceids.hxx>
33 #include <oox/token/namespaces.hxx>
34 #include <oox/drawingml/drawingmltypes.hxx>
35 #include <rtl/uri.hxx>
36 #include <rtl/tencinfo.h>
37 #include <sal/log.hxx>
38 #include <osl/diagnose.h>
39 #include <oox/helper/graphichelper.hxx>
40 #include <vcl/wmfexternal.hxx>
41 #include <vcl/graph.hxx>
42 #include "rtfsdrimport.hxx"
43 #include "rtfreferenceproperties.hxx"
44 #include "rtfskipdestination.hxx"
45 #include "rtftokenizer.hxx"
46 #include "rtflookahead.hxx"
48 using namespace com::sun::star;
50 namespace
52 /// Returns an util::DateTime from a 'YYYY. MM. DD.' string.
53 util::DateTime getDateTimeFromUserProp(const OUString& rString)
55 util::DateTime aRet;
56 sal_Int32 nLen = rString.getLength();
57 if (nLen >= 4)
59 aRet.Year = rString.copy(0, 4).toInt32();
61 if (nLen >= 8 && rString.match(". ", 4))
63 aRet.Month = rString.copy(6, 2).toInt32();
65 if (nLen >= 12 && rString.match(". ", 8))
66 aRet.Day = rString.copy(10, 2).toInt32();
69 return aRet;
71 } // anonymous namespace
73 namespace writerfilter
75 namespace rtftok
77 Id getParagraphBorder(sal_uInt32 nIndex)
79 static const Id aBorderIds[] = { NS_ooxml::LN_CT_PBdr_top, NS_ooxml::LN_CT_PBdr_left,
80 NS_ooxml::LN_CT_PBdr_bottom, NS_ooxml::LN_CT_PBdr_right };
82 return aBorderIds[nIndex];
85 void putNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId, const RTFValue::Pointer_t& pValue,
86 RTFOverwrite eOverwrite, bool bAttribute)
88 RTFValue::Pointer_t pParent = rSprms.find(nParent, /*bFirst=*/true, /*bForWrite=*/true);
89 if (!pParent.get())
91 RTFSprms aAttributes;
92 if (nParent == NS_ooxml::LN_CT_TcPrBase_shd)
94 // RTF default is 'auto', see writerfilter::dmapper::CellColorHandler
95 aAttributes.set(NS_ooxml::LN_CT_Shd_color, new RTFValue(sal_uInt32(COL_AUTO)));
96 aAttributes.set(NS_ooxml::LN_CT_Shd_fill, new RTFValue(sal_uInt32(COL_AUTO)));
98 auto pParentValue = new RTFValue(aAttributes);
99 rSprms.set(nParent, pParentValue, eOverwrite);
100 pParent = pParentValue;
102 RTFSprms& rAttributes = (bAttribute ? pParent->getAttributes() : pParent->getSprms());
103 rAttributes.set(nId, pValue, eOverwrite);
106 void putNestedSprm(RTFSprms& rSprms, Id nParent, Id nId, const RTFValue::Pointer_t& pValue,
107 RTFOverwrite eOverwrite)
109 putNestedAttribute(rSprms, nParent, nId, pValue, eOverwrite, false);
112 RTFValue::Pointer_t getNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId)
114 RTFValue::Pointer_t pParent = rSprms.find(nParent);
115 if (!pParent)
116 return RTFValue::Pointer_t();
117 RTFSprms& rAttributes = pParent->getAttributes();
118 return rAttributes.find(nId);
121 RTFValue::Pointer_t getNestedSprm(RTFSprms& rSprms, Id nParent, Id nId)
123 RTFValue::Pointer_t pParent = rSprms.find(nParent);
124 if (!pParent)
125 return RTFValue::Pointer_t();
126 RTFSprms& rInner = pParent->getSprms();
127 return rInner.find(nId);
130 bool eraseNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId)
132 RTFValue::Pointer_t pParent = rSprms.find(nParent);
133 if (!pParent.get())
134 // It doesn't even have a parent, we're done.
135 return false;
136 RTFSprms& rAttributes = pParent->getAttributes();
137 return rAttributes.erase(nId);
140 RTFSprms& getLastAttributes(RTFSprms& rSprms, Id nId)
142 RTFValue::Pointer_t p = rSprms.find(nId);
143 if (p.get() && !p->getSprms().empty())
144 return p->getSprms().back().second->getAttributes();
146 SAL_WARN("writerfilter.rtf", "trying to set property when no type is defined");
147 return rSprms;
150 void putBorderProperty(RTFStack& aStates, Id nId, const RTFValue::Pointer_t& pValue)
152 RTFSprms* pAttributes = nullptr;
153 if (aStates.top().nBorderState == RTFBorderState::PARAGRAPH_BOX)
154 for (int i = 0; i < 4; i++)
156 RTFValue::Pointer_t p = aStates.top().aParagraphSprms.find(getParagraphBorder(i));
157 if (p)
159 RTFSprms& rAttributes = p->getAttributes();
160 rAttributes.set(nId, pValue);
163 else if (aStates.top().nBorderState == RTFBorderState::CHARACTER)
165 RTFValue::Pointer_t pPointer
166 = aStates.top().aCharacterSprms.find(NS_ooxml::LN_EG_RPrBase_bdr);
167 if (pPointer)
169 RTFSprms& rAttributes = pPointer->getAttributes();
170 rAttributes.set(nId, pValue);
173 // Attributes of the last border type
174 else if (aStates.top().nBorderState == RTFBorderState::PARAGRAPH)
175 pAttributes
176 = &getLastAttributes(aStates.top().aParagraphSprms, NS_ooxml::LN_CT_PrBase_pBdr);
177 else if (aStates.top().nBorderState == RTFBorderState::CELL)
178 pAttributes
179 = &getLastAttributes(aStates.top().aTableCellSprms, NS_ooxml::LN_CT_TcPrBase_tcBorders);
180 else if (aStates.top().nBorderState == RTFBorderState::PAGE)
181 pAttributes = &getLastAttributes(aStates.top().aSectionSprms,
182 NS_ooxml::LN_EG_SectPrContents_pgBorders);
183 if (pAttributes)
184 pAttributes->set(nId, pValue);
187 OString DTTM22OString(long nDTTM)
189 return DateTimeToOString(msfilter::util::DTTM2DateTime(nDTTM));
192 static RTFSprms lcl_getBookmarkProperties(int nPos, OUString& rString)
194 RTFSprms aAttributes;
195 auto pPos = new RTFValue(nPos);
196 if (!rString.isEmpty())
198 // If present, this should be sent first.
199 auto pString = new RTFValue(rString);
200 aAttributes.set(NS_ooxml::LN_CT_Bookmark_name, pString);
202 aAttributes.set(NS_ooxml::LN_CT_MarkupRangeBookmark_id, pPos);
203 return aAttributes;
206 const char* keywordToString(RTFKeyword nKeyword)
208 for (int i = 0; i < nRTFControlWords; i++)
210 if (nKeyword == aRTFControlWords[i].nIndex)
211 return aRTFControlWords[i].sKeyword;
213 return nullptr;
216 static util::DateTime lcl_getDateTime(RTFParserState const& aState)
218 return { 0 /*100sec*/, 0 /*sec*/, aState.nMinute, aState.nHour,
219 aState.nDay, aState.nMonth, aState.nYear, false };
222 static void lcl_DestinationToMath(OUStringBuffer* pDestinationText,
223 oox::formulaimport::XmlStreamBuilder& rMathBuffer, bool& rMathNor)
225 if (!pDestinationText)
226 return;
227 OUString aStr = pDestinationText->makeStringAndClear();
228 if (aStr.isEmpty())
229 return;
230 rMathBuffer.appendOpeningTag(M_TOKEN(r));
231 if (rMathNor)
233 rMathBuffer.appendOpeningTag(M_TOKEN(rPr));
234 // Same as M_TOKEN(lit)
235 rMathBuffer.appendOpeningTag(M_TOKEN(nor));
236 rMathBuffer.appendClosingTag(M_TOKEN(nor));
237 rMathBuffer.appendClosingTag(M_TOKEN(rPr));
238 rMathNor = false;
240 rMathBuffer.appendOpeningTag(M_TOKEN(t));
241 rMathBuffer.appendCharacters(aStr);
242 rMathBuffer.appendClosingTag(M_TOKEN(t));
243 rMathBuffer.appendClosingTag(M_TOKEN(r));
246 RTFDocumentImpl::RTFDocumentImpl(uno::Reference<uno::XComponentContext> const& xContext,
247 uno::Reference<io::XInputStream> const& xInputStream,
248 uno::Reference<lang::XComponent> const& xDstDoc,
249 uno::Reference<frame::XFrame> const& xFrame,
250 uno::Reference<task::XStatusIndicator> const& xStatusIndicator,
251 const utl::MediaDescriptor& rMediaDescriptor)
252 : m_xContext(xContext)
253 , m_xInputStream(xInputStream)
254 , m_xDstDoc(xDstDoc)
255 , m_xFrame(xFrame)
256 , m_xStatusIndicator(xStatusIndicator)
257 , m_pMapperStream(nullptr)
258 , m_aDefaultState(this)
259 , m_bSkipUnknown(false)
260 , m_bFirstRun(true)
261 , m_bFirstRunException(false)
262 , m_bNeedPap(true)
263 , m_bNeedCr(false)
264 , m_bNeedCrOrig(false)
265 , m_bNeedPar(true)
266 , m_bNeedFinalPar(false)
267 , m_nNestedCells(0)
268 , m_nTopLevelCells(0)
269 , m_nInheritingCells(0)
270 , m_nNestedTRLeft(0)
271 , m_nTopLevelTRLeft(0)
272 , m_nNestedCurrentCellX(0)
273 , m_nTopLevelCurrentCellX(0)
274 , m_nBackupTopLevelCurrentCellX(0)
275 , m_aTableBufferStack(1) // create top-level buffer already
276 , m_pSuperstream(nullptr)
277 , m_nStreamType(0)
278 , m_nGroupStartPos(0)
279 , m_nFormFieldType(RTFFormFieldType::NONE)
280 , m_bObject(false)
281 , m_nCurrentFontIndex(0)
282 , m_nCurrentEncoding(-1)
283 , m_nDefaultFontIndex(-1)
284 , m_nCurrentStyleIndex(0)
285 , m_bFormField(false)
286 , m_bMathNor(false)
287 , m_bIgnoreNextContSectBreak(false)
288 , m_nResetBreakOnSectBreak(RTF_invalid)
289 , m_bNeedSect(false) // done by checkFirstRun
290 , m_bWasInFrame(false)
291 , m_bHadPicture(false)
292 , m_bHadSect(false)
293 , m_nCellxMax(0)
294 , m_nListPictureId(0)
295 , m_bIsNewDoc(!rMediaDescriptor.getUnpackedValueOrDefault("InsertMode", false))
296 , m_rMediaDescriptor(rMediaDescriptor)
297 , m_hasRHeader(false)
298 , m_hasFHeader(false)
299 , m_hasRFooter(false)
300 , m_hasFFooter(false)
301 , m_bAfterCellBeforeRow(false)
303 OSL_ASSERT(xInputStream.is());
304 m_pInStream = utl::UcbStreamHelper::CreateStream(xInputStream, true);
306 m_xModelFactory.set(m_xDstDoc, uno::UNO_QUERY);
308 uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier(
309 m_xDstDoc, uno::UNO_QUERY);
310 if (xDocumentPropertiesSupplier.is())
311 m_xDocumentProperties.set(xDocumentPropertiesSupplier->getDocumentProperties(),
312 uno::UNO_QUERY);
314 m_pGraphicHelper.reset(new oox::GraphicHelper(m_xContext, xFrame, oox::StorageRef()));
316 m_pTokenizer = new RTFTokenizer(*this, m_pInStream.get(), m_xStatusIndicator);
317 m_pSdrImport = new RTFSdrImport(*this, m_xDstDoc);
320 RTFDocumentImpl::~RTFDocumentImpl() = default;
322 SvStream& RTFDocumentImpl::Strm() { return *m_pInStream; }
324 void RTFDocumentImpl::setSuperstream(RTFDocumentImpl* pSuperstream)
326 m_pSuperstream = pSuperstream;
329 bool RTFDocumentImpl::isSubstream() const { return m_pSuperstream != nullptr; }
331 void RTFDocumentImpl::finishSubstream() { checkUnicode(/*bUnicode =*/true, /*bHex =*/true); }
333 void RTFDocumentImpl::resolveSubstream(std::size_t nPos, Id nId)
335 resolveSubstream(nPos, nId, OUString());
337 void RTFDocumentImpl::resolveSubstream(std::size_t nPos, Id nId, OUString const& rIgnoreFirst)
339 sal_uInt64 const nCurrent = Strm().Tell();
340 // Seek to header position, parse, then seek back.
341 auto pImpl = new RTFDocumentImpl(m_xContext, m_xInputStream, m_xDstDoc, m_xFrame,
342 m_xStatusIndicator, m_rMediaDescriptor);
343 pImpl->setSuperstream(this);
344 pImpl->m_nStreamType = nId;
345 pImpl->m_aIgnoreFirst = rIgnoreFirst;
346 if (!m_aAuthor.isEmpty())
348 pImpl->m_aAuthor = m_aAuthor;
349 m_aAuthor.clear();
351 if (!m_aAuthorInitials.isEmpty())
353 pImpl->m_aAuthorInitials = m_aAuthorInitials;
354 m_aAuthorInitials.clear();
356 pImpl->m_nDefaultFontIndex = m_nDefaultFontIndex;
357 pImpl->Strm().Seek(nPos);
358 SAL_INFO("writerfilter.rtf", "substream start");
359 Mapper().substream(nId, pImpl);
360 SAL_INFO("writerfilter.rtf", "substream end");
361 Strm().Seek(nCurrent);
364 void RTFDocumentImpl::outputSettingsTable()
366 writerfilter::Reference<Properties>::Pointer_t pProp
367 = new RTFReferenceProperties(m_aSettingsTableAttributes, m_aSettingsTableSprms);
368 RTFReferenceTable::Entries_t aSettingsTableEntries;
369 aSettingsTableEntries.insert(std::make_pair(0, pProp));
370 writerfilter::Reference<Table>::Pointer_t pTable = new RTFReferenceTable(aSettingsTableEntries);
371 Mapper().table(NS_ooxml::LN_settings_settings, pTable);
374 void RTFDocumentImpl::checkFirstRun()
376 if (m_bFirstRun)
378 outputSettingsTable();
379 // start initial paragraph
380 m_bFirstRun = false;
381 assert(!m_bNeedSect || m_bFirstRunException);
382 setNeedSect(true); // first call that succeeds
384 // set the requested default font, if there are none
385 RTFValue::Pointer_t pFont
386 = getNestedAttribute(m_aDefaultState.aCharacterSprms, NS_ooxml::LN_EG_RPrBase_rFonts,
387 NS_ooxml::LN_CT_Fonts_ascii);
388 RTFValue::Pointer_t pCurrentFont
389 = getNestedAttribute(m_aStates.top().aCharacterSprms, NS_ooxml::LN_EG_RPrBase_rFonts,
390 NS_ooxml::LN_CT_Fonts_ascii);
391 if (pFont && !pCurrentFont)
392 putNestedAttribute(m_aStates.top().aCharacterSprms, NS_ooxml::LN_EG_RPrBase_rFonts,
393 NS_ooxml::LN_CT_Fonts_ascii, pFont);
397 void RTFDocumentImpl::setNeedPar(bool bNeedPar) { m_bNeedPar = bNeedPar; }
399 void RTFDocumentImpl::setNeedSect(bool bNeedSect)
401 if (!m_bNeedSect && bNeedSect && m_bFirstRun)
403 RTFLookahead aLookahead(Strm(), m_pTokenizer->getGroupStart());
404 if (aLookahead.hasTable() && aLookahead.hasColumns())
406 m_bFirstRunException = true;
410 // ignore setting before checkFirstRun - every keyword calls setNeedSect!
411 // except the case of a table in a multicolumn section
412 if (!m_bNeedSect && bNeedSect && (!m_bFirstRun || m_bFirstRunException))
414 if (!m_pSuperstream) // no sections in header/footer!
416 Mapper().startSectionGroup();
418 // set flag in substream too - otherwise multiple startParagraphGroup
419 m_bNeedSect = bNeedSect;
420 Mapper().startParagraphGroup();
421 setNeedPar(true);
423 else if (m_bNeedSect && !bNeedSect)
425 m_bNeedSect = bNeedSect;
429 /// Copy rProps to rStyleAttributes and rStyleSprms, but in case of nested sprms, copy their children as toplevel sprms/attributes.
430 static void lcl_copyFlatten(RTFReferenceProperties& rProps, RTFSprms& rStyleAttributes,
431 RTFSprms& rStyleSprms)
433 for (auto& rSprm : rProps.getSprms())
435 // createStyleProperties() puts properties to rPr, but here we need a flat list.
436 if (rSprm.first == NS_ooxml::LN_CT_Style_rPr)
438 // rPr can have both attributes and SPRMs, copy over both types.
439 RTFSprms& rRPrSprms = rSprm.second->getSprms();
440 for (auto& rRPrSprm : rRPrSprms)
441 rStyleSprms.set(rRPrSprm.first, rRPrSprm.second);
443 RTFSprms& rRPrAttributes = rSprm.second->getAttributes();
444 for (auto& rRPrAttribute : rRPrAttributes)
445 rStyleAttributes.set(rRPrAttribute.first, rRPrAttribute.second);
447 else
448 rStyleSprms.set(rSprm.first, rSprm.second);
451 RTFSprms& rAttributes = rProps.getAttributes();
452 for (auto& rAttribute : rAttributes)
453 rStyleAttributes.set(rAttribute.first, rAttribute.second);
456 writerfilter::Reference<Properties>::Pointer_t
457 RTFDocumentImpl::getProperties(RTFSprms& rAttributes, RTFSprms const& rSprms, Id nStyleType)
459 RTFSprms aSprms(rSprms);
460 RTFValue::Pointer_t pAbstractList;
461 int nAbstractListId = -1;
462 RTFValue::Pointer_t pNumId
463 = getNestedSprm(aSprms, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_numId);
464 if (pNumId)
466 // We have a numbering, look up the abstract list for property
467 // deduplication and duplication.
468 auto itNumId = m_aListOverrideTable.find(pNumId->getInt());
469 if (itNumId != m_aListOverrideTable.end())
471 nAbstractListId = itNumId->second;
472 auto itAbstract = m_aListTable.find(nAbstractListId);
473 if (itAbstract != m_aListTable.end())
474 pAbstractList = itAbstract->second;
478 if (pAbstractList)
480 auto it = m_aInvalidListTableFirstIndents.find(nAbstractListId);
481 if (it != m_aInvalidListTableFirstIndents.end())
482 aSprms.deduplicateList(it->second);
485 int nStyle = 0;
486 if (!m_aStates.empty())
487 nStyle = m_aStates.top().nCurrentStyleIndex;
488 auto it = m_aStyleTableEntries.find(nStyle);
489 if (it != m_aStyleTableEntries.end())
491 // cloneAndDeduplicate() wants to know about only a single "style", so
492 // let's merge paragraph and character style properties here.
493 auto itChar = m_aStyleTableEntries.end();
494 if (!m_aStates.empty())
496 int nCharStyle = m_aStates.top().nCurrentCharacterStyleIndex;
497 itChar = m_aStyleTableEntries.find(nCharStyle);
500 RTFSprms aStyleSprms;
501 RTFSprms aStyleAttributes;
502 // Ensure the paragraph style is a flat list.
503 // Take paragraph style into account for character properties as well,
504 // as paragraph style may contain character properties.
505 RTFReferenceProperties& rProps = *static_cast<RTFReferenceProperties*>(it->second.get());
506 lcl_copyFlatten(rProps, aStyleAttributes, aStyleSprms);
508 if (itChar != m_aStyleTableEntries.end())
510 // Found active character style, then update aStyleSprms/Attributes.
511 if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_character)
513 RTFReferenceProperties& rCharProps
514 = *static_cast<RTFReferenceProperties*>(itChar->second.get());
515 lcl_copyFlatten(rCharProps, aStyleAttributes, aStyleSprms);
519 // Get rid of direct formatting what is already in the style.
520 RTFSprms const sprms(aSprms.cloneAndDeduplicate(aStyleSprms, nStyleType, true));
521 RTFSprms const attributes(
522 rAttributes.cloneAndDeduplicate(aStyleAttributes, nStyleType, true));
523 return new RTFReferenceProperties(attributes, sprms);
526 if (pAbstractList)
527 aSprms.duplicateList(pAbstractList);
528 writerfilter::Reference<Properties>::Pointer_t pRet
529 = new RTFReferenceProperties(rAttributes, aSprms);
530 return pRet;
533 void RTFDocumentImpl::checkNeedPap()
535 if (m_bNeedPap)
537 m_bNeedPap = false; // reset early, so we can avoid recursion when calling ourselves
539 if (m_aStates.empty())
540 return;
542 if (!m_aStates.top().pCurrentBuffer)
544 writerfilter::Reference<Properties>::Pointer_t const pParagraphProperties(
545 getProperties(m_aStates.top().aParagraphAttributes, m_aStates.top().aParagraphSprms,
546 NS_ooxml::LN_Value_ST_StyleType_paragraph));
548 // Writer will ignore a page break before a text frame, so guard it with empty paragraphs
549 bool hasBreakBeforeFrame
550 = m_aStates.top().aFrame.hasProperties()
551 && m_aStates.top()
552 .aParagraphSprms.find(NS_ooxml::LN_CT_PPrBase_pageBreakBefore)
553 .get();
554 if (hasBreakBeforeFrame)
556 dispatchSymbol(RTF_PAR);
557 m_bNeedPap = false;
559 Mapper().props(pParagraphProperties);
560 if (hasBreakBeforeFrame)
561 dispatchSymbol(RTF_PAR);
563 if (m_aStates.top().aFrame.hasProperties())
565 writerfilter::Reference<Properties>::Pointer_t const pFrameProperties(
566 new RTFReferenceProperties(RTFSprms(), m_aStates.top().aFrame.getSprms()));
567 Mapper().props(pFrameProperties);
570 else
572 auto pValue = new RTFValue(m_aStates.top().aParagraphAttributes,
573 m_aStates.top().aParagraphSprms);
574 bufferProperties(*m_aStates.top().pCurrentBuffer, pValue, nullptr);
579 void RTFDocumentImpl::runProps()
581 if (!m_aStates.top().pCurrentBuffer)
583 Reference<Properties>::Pointer_t const pProperties
584 = getProperties(m_aStates.top().aCharacterAttributes, m_aStates.top().aCharacterSprms,
585 NS_ooxml::LN_Value_ST_StyleType_character);
586 Mapper().props(pProperties);
588 else
590 auto pValue
591 = new RTFValue(m_aStates.top().aCharacterAttributes, m_aStates.top().aCharacterSprms);
592 bufferProperties(*m_aStates.top().pCurrentBuffer, pValue, nullptr);
595 // Delete the sprm, so the trackchange range will be started only once.
596 // OTOH set a boolean flag, so we'll know we need to end the range later.
597 RTFValue::Pointer_t pTrackchange
598 = m_aStates.top().aCharacterSprms.find(NS_ooxml::LN_trackchange);
599 if (pTrackchange)
601 m_aStates.top().bStartedTrackchange = true;
602 m_aStates.top().aCharacterSprms.erase(NS_ooxml::LN_trackchange);
606 void RTFDocumentImpl::runBreak()
608 sal_uInt8 const sBreak[] = { 0xd };
609 Mapper().text(sBreak, 1);
610 m_bNeedCr = false;
613 void RTFDocumentImpl::tableBreak()
615 runBreak();
616 Mapper().endParagraphGroup();
617 Mapper().startParagraphGroup();
620 void RTFDocumentImpl::parBreak()
622 checkFirstRun();
623 checkNeedPap();
624 // end previous paragraph
625 Mapper().startCharacterGroup();
626 runBreak();
627 Mapper().endCharacterGroup();
628 Mapper().endParagraphGroup();
630 m_bHadPicture = false;
632 // start new one
633 Mapper().startParagraphGroup();
636 void RTFDocumentImpl::sectBreak(bool bFinal)
638 SAL_INFO("writerfilter.rtf",
639 OSL_THIS_FUNC << ": final? " << bFinal << ", needed? " << m_bNeedSect);
640 bool bNeedSect = m_bNeedSect;
641 RTFValue::Pointer_t pBreak
642 = m_aStates.top().aSectionSprms.find(NS_ooxml::LN_EG_SectPrContents_type);
643 bool bContinuous
644 = pBreak.get()
645 && pBreak->getInt()
646 == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_continuous);
647 // If there is no paragraph in this section, then insert a dummy one, as required by Writer,
648 // unless this is the end of the doc, we had nothing since the last section break and this is not a continuous one.
649 // Also, when pasting, it's fine to not have any paragraph inside the document at all.
650 if (m_bNeedPar && !(bFinal && !m_bNeedSect && !bContinuous) && !isSubstream() && m_bIsNewDoc)
651 dispatchSymbol(RTF_PAR);
652 // It's allowed to not have a non-table paragraph at the end of an RTF doc, add it now if required.
653 if (m_bNeedFinalPar && bFinal)
655 dispatchFlag(RTF_PARD);
656 dispatchSymbol(RTF_PAR);
657 m_bNeedSect = bNeedSect;
659 while (!m_nHeaderFooterPositions.empty())
661 std::pair<Id, std::size_t> aPair = m_nHeaderFooterPositions.front();
662 m_nHeaderFooterPositions.pop();
663 resolveSubstream(aPair.second, aPair.first);
666 // Normally a section break at the end of the doc is necessary. Unless the
667 // last control word in the document is a section break itself.
668 if (!bNeedSect || !m_bHadSect)
670 // In case the last section is a continuous one, we don't need to output a section break.
671 if (bFinal && bContinuous)
672 m_aStates.top().aSectionSprms.erase(NS_ooxml::LN_EG_SectPrContents_type);
675 // Section properties are a paragraph sprm.
676 auto pValue = new RTFValue(m_aStates.top().aSectionAttributes, m_aStates.top().aSectionSprms);
677 RTFSprms aAttributes;
678 RTFSprms aSprms;
679 aSprms.set(NS_ooxml::LN_CT_PPr_sectPr, pValue);
680 writerfilter::Reference<Properties>::Pointer_t pProperties
681 = new RTFReferenceProperties(aAttributes, aSprms);
683 if (bFinal && !m_pSuperstream)
684 // This is the end of the document, not just the end of e.g. a header.
685 // This makes sure that dmapper can set DontBalanceTextColumns=true for this section if necessary.
686 Mapper().markLastSectionGroup();
688 // The trick is that we send properties of the previous section right now, which will be exactly what dmapper expects.
689 Mapper().props(pProperties);
690 Mapper().endParagraphGroup();
692 // End Section
693 if (!m_pSuperstream)
695 m_hasFHeader = false;
696 m_hasRHeader = false;
697 m_hasRFooter = false;
698 m_hasFFooter = false;
699 Mapper().endSectionGroup();
701 m_bNeedPar = false;
702 m_bNeedSect = false;
705 Color RTFDocumentImpl::getColorTable(sal_uInt32 nIndex)
707 if (!m_pSuperstream)
709 if (nIndex < m_aColorTable.size())
710 return m_aColorTable[nIndex];
711 return 0;
714 return m_pSuperstream->getColorTable(nIndex);
717 rtl_TextEncoding RTFDocumentImpl::getEncoding(int nFontIndex)
719 if (!m_pSuperstream)
721 auto it = m_aFontEncodings.find(nFontIndex);
722 if (it != m_aFontEncodings.end())
723 // We have a font encoding associated to this font.
724 return it->second;
725 if (m_aDefaultState.nCurrentEncoding != rtl_getTextEncodingFromWindowsCharset(0))
726 // We have a default encoding.
727 return m_aDefaultState.nCurrentEncoding;
728 // Guess based on locale.
729 return msfilter::util::getBestTextEncodingFromLocale(
730 Application::GetSettings().GetLanguageTag().getLocale());
733 return m_pSuperstream->getEncoding(nFontIndex);
736 OUString RTFDocumentImpl::getFontName(int nIndex)
738 if (!m_pSuperstream)
739 return m_aFontNames[nIndex];
741 return m_pSuperstream->getFontName(nIndex);
744 int RTFDocumentImpl::getFontIndex(int nIndex)
746 if (!m_pSuperstream)
747 return std::find(m_aFontIndexes.begin(), m_aFontIndexes.end(), nIndex)
748 - m_aFontIndexes.begin();
750 return m_pSuperstream->getFontIndex(nIndex);
753 OUString RTFDocumentImpl::getStyleName(int nIndex)
755 if (!m_pSuperstream)
757 OUString aRet;
758 if (m_aStyleNames.find(nIndex) != m_aStyleNames.end())
759 aRet = m_aStyleNames[nIndex];
760 return aRet;
763 return m_pSuperstream->getStyleName(nIndex);
766 Id RTFDocumentImpl::getStyleType(int nIndex)
768 if (!m_pSuperstream)
770 Id nRet = 0;
771 if (m_aStyleTypes.find(nIndex) != m_aStyleTypes.end())
772 nRet = m_aStyleTypes[nIndex];
773 return nRet;
776 return m_pSuperstream->getStyleType(nIndex);
779 RTFParserState& RTFDocumentImpl::getDefaultState()
781 if (!m_pSuperstream)
782 return m_aDefaultState;
784 return m_pSuperstream->getDefaultState();
787 oox::GraphicHelper& RTFDocumentImpl::getGraphicHelper() { return *m_pGraphicHelper; }
789 bool RTFDocumentImpl::isStyleSheetImport()
791 if (m_aStates.empty())
792 return false;
793 Destination eDestination = m_aStates.top().eDestination;
794 return eDestination == Destination::STYLESHEET || eDestination == Destination::STYLEENTRY;
797 void RTFDocumentImpl::resolve(Stream& rMapper)
799 m_pMapperStream = &rMapper;
800 switch (m_pTokenizer->resolveParse())
802 case RTFError::OK:
803 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: finished without errors");
804 break;
805 case RTFError::GROUP_UNDER:
806 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unmatched '}'");
807 break;
808 case RTFError::GROUP_OVER:
809 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unmatched '{'");
810 throw io::WrongFormatException(m_pTokenizer->getPosition());
811 break;
812 case RTFError::UNEXPECTED_EOF:
813 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unexpected end of file");
814 throw io::WrongFormatException(m_pTokenizer->getPosition());
815 break;
816 case RTFError::HEX_INVALID:
817 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: invalid hex char");
818 throw io::WrongFormatException(m_pTokenizer->getPosition());
819 break;
820 case RTFError::CHAR_OVER:
821 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: characters after last '}'");
822 break;
823 case RTFError::CLASSIFICATION:
824 SAL_INFO("writerfilter.rtf",
825 "RTFDocumentImpl::resolve: classification prevented paste");
826 break;
830 void RTFDocumentImpl::resolvePict(bool const bInline, uno::Reference<drawing::XShape> const& rShape)
832 SvMemoryStream aStream;
833 SvStream* pStream = nullptr;
834 if (!m_pBinaryData.get())
836 pStream = &aStream;
837 int b = 0, count = 2;
839 // Feed the destination text to a stream.
840 OString aStr = OUStringToOString(m_aStates.top().aDestinationText.makeStringAndClear(),
841 RTL_TEXTENCODING_ASCII_US);
842 for (int i = 0; i < aStr.getLength(); ++i)
844 char ch = aStr[i];
845 if (ch != 0x0d && ch != 0x0a && ch != 0x20)
847 b = b << 4;
848 sal_Int8 parsed = msfilter::rtfutil::AsHex(ch);
849 if (parsed == -1)
850 return;
851 b += parsed;
852 count--;
853 if (!count)
855 aStream.WriteChar(static_cast<char>(b));
856 count = 2;
857 b = 0;
862 else
863 pStream = m_pBinaryData.get();
865 if (!pStream->Tell())
866 // No destination text? Then we'll get it later.
867 return;
869 SvMemoryStream aDIBStream;
870 if (m_aStates.top().aPicture.eStyle == RTFBmpStyle::DIBITMAP)
872 // Construct a BITMAPFILEHEADER structure before the real data.
873 SvStream& rBodyStream = *pStream;
874 aDIBStream.WriteChar('B');
875 aDIBStream.WriteChar('M');
876 // The size of the real data.
877 aDIBStream.WriteUInt32(rBodyStream.Tell());
878 // Reserved.
879 aDIBStream.WriteUInt32(0);
880 // The offset of the real data, i.e. the size of the header, including this number.
881 aDIBStream.WriteUInt32(14);
882 rBodyStream.Seek(0);
883 aDIBStream.WriteStream(rBodyStream);
884 pStream = &aDIBStream;
887 // Store, and get its URL.
888 pStream->Seek(0);
889 uno::Reference<io::XInputStream> xInputStream(new utl::OInputStreamWrapper(pStream));
890 WmfExternal aExtHeader;
891 aExtHeader.mapMode = m_aStates.top().aPicture.eWMetafile;
892 aExtHeader.xExt = sal_uInt16(
893 o3tl::clamp<sal_Int32>(m_aStates.top().aPicture.nWidth, 0,
894 SAL_MAX_UINT16)); //TODO: better way to handle out-of-bounds values?
895 aExtHeader.yExt = sal_uInt16(
896 o3tl::clamp<sal_Int32>(m_aStates.top().aPicture.nHeight, 0,
897 SAL_MAX_UINT16)); //TODO: better way to handle out-of-bounds values?
898 WmfExternal* pExtHeader = &aExtHeader;
899 uno::Reference<lang::XServiceInfo> xServiceInfo(m_aStates.top().aDrawingObject.xShape,
900 uno::UNO_QUERY);
901 if (xServiceInfo.is() && xServiceInfo->supportsService("com.sun.star.text.TextFrame"))
902 pExtHeader = nullptr;
904 uno::Reference<graphic::XGraphic> xGraphic;
905 xGraphic = m_pGraphicHelper->importGraphic(xInputStream, pExtHeader);
907 if (m_aStates.top().aPicture.eStyle != RTFBmpStyle::NONE)
909 // In case of PNG/JPEG, the real size is known, don't use the values
910 // provided by picw and pich.
912 Graphic aGraphic(xGraphic);
913 Size aSize(aGraphic.GetPrefSize());
914 MapMode aMap(MapUnit::Map100thMM);
915 if (aGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel)
916 aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMap);
917 else
918 aSize = OutputDevice::LogicToLogic(aSize, aGraphic.GetPrefMapMode(), aMap);
919 m_aStates.top().aPicture.nWidth = aSize.Width();
920 m_aStates.top().aPicture.nHeight = aSize.Height();
923 // Wrap it in an XShape.
924 uno::Reference<drawing::XShape> xShape(rShape);
925 if (xShape.is())
927 uno::Reference<lang::XServiceInfo> xSI(xShape, uno::UNO_QUERY_THROW);
928 if (!xSI->supportsService("com.sun.star.drawing.GraphicObjectShape"))
930 // it's sometimes an error to get here - but it's possible to have
931 // a \pict inside the \shptxt of a \shp of shapeType 202 "TextBox"
932 // and in that case xShape is the text frame; we actually need a
933 // new GraphicObject then (example: fdo37691-1.rtf)
934 SAL_INFO("writerfilter.rtf",
935 "cannot set graphic on existing shape, creating a new GraphicObjectShape");
936 xShape.clear();
939 if (!xShape.is())
941 if (m_xModelFactory.is())
942 xShape.set(m_xModelFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"),
943 uno::UNO_QUERY);
944 uno::Reference<drawing::XDrawPageSupplier> const xDrawSupplier(m_xDstDoc, uno::UNO_QUERY);
945 if (xDrawSupplier.is())
947 uno::Reference<drawing::XShapes> xShapes(xDrawSupplier->getDrawPage(), uno::UNO_QUERY);
948 if (xShapes.is())
949 xShapes->add(xShape);
953 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
955 if (xPropertySet.is())
956 xPropertySet->setPropertyValue("Graphic", uno::Any(xGraphic));
958 // check if the picture is in an OLE object and if the \objdata element is used
959 // (see RTF_OBJECT in RTFDocumentImpl::dispatchDestination)
960 if (m_bObject)
962 // Set the object size
963 awt::Size aSize;
964 aSize.Width = (m_aStates.top().aPicture.nGoalWidth ? m_aStates.top().aPicture.nGoalWidth
965 : m_aStates.top().aPicture.nWidth);
966 aSize.Height = (m_aStates.top().aPicture.nGoalHeight ? m_aStates.top().aPicture.nGoalHeight
967 : m_aStates.top().aPicture.nHeight);
968 xShape->setSize(aSize);
970 // Replacement graphic is inline by default, see oox::vml::SimpleShape::implConvertAndInsert().
971 xPropertySet->setPropertyValue("AnchorType",
972 uno::makeAny(text::TextContentAnchorType_AS_CHARACTER));
974 auto pShapeValue = new RTFValue(xShape);
975 m_aObjectAttributes.set(NS_ooxml::LN_shape, pShapeValue);
976 return;
979 if (m_aStates.top().bInListpicture)
981 // Send the shape directly, no section is started, to additional properties will be ignored anyway.
982 Mapper().startShape(xShape);
983 Mapper().endShape();
984 return;
987 // Send it to the dmapper.
988 RTFSprms aSprms;
989 RTFSprms aAttributes;
990 // shape attribute
991 RTFSprms aPicAttributes;
992 auto pShapeValue = new RTFValue(xShape);
993 aPicAttributes.set(NS_ooxml::LN_shape, pShapeValue);
994 // pic sprm
995 RTFSprms aGraphicDataAttributes;
996 RTFSprms aGraphicDataSprms;
997 auto pPicValue = new RTFValue(aPicAttributes);
998 aGraphicDataSprms.set(NS_ooxml::LN_pic_pic, pPicValue);
999 // graphicData sprm
1000 RTFSprms aGraphicAttributes;
1001 RTFSprms aGraphicSprms;
1002 auto pGraphicDataValue = new RTFValue(aGraphicDataAttributes, aGraphicDataSprms);
1003 aGraphicSprms.set(NS_ooxml::LN_CT_GraphicalObject_graphicData, pGraphicDataValue);
1004 // graphic sprm
1005 auto pGraphicValue = new RTFValue(aGraphicAttributes, aGraphicSprms);
1006 // extent sprm
1007 RTFSprms aExtentAttributes;
1008 int nXExt, nYExt;
1009 nXExt = (m_aStates.top().aPicture.nGoalWidth ? m_aStates.top().aPicture.nGoalWidth
1010 : m_aStates.top().aPicture.nWidth);
1011 nYExt = (m_aStates.top().aPicture.nGoalHeight ? m_aStates.top().aPicture.nGoalHeight
1012 : m_aStates.top().aPicture.nHeight);
1013 if (m_aStates.top().aPicture.nScaleX != 100)
1014 nXExt = (static_cast<long>(m_aStates.top().aPicture.nScaleX)
1015 * (nXExt - (m_aStates.top().aPicture.nCropL + m_aStates.top().aPicture.nCropR)))
1016 / 100L;
1017 if (m_aStates.top().aPicture.nScaleY != 100)
1018 nYExt = (static_cast<long>(m_aStates.top().aPicture.nScaleY)
1019 * (nYExt - (m_aStates.top().aPicture.nCropT + m_aStates.top().aPicture.nCropB)))
1020 / 100L;
1021 if (m_aStates.top().bInShape)
1023 // Picture in shape: it looks like pib picture, so we will stretch the picture to shape size (tdf#49893)
1024 nXExt = m_aStates.top().aShape.nRight - m_aStates.top().aShape.nLeft;
1025 nYExt = m_aStates.top().aShape.nBottom - m_aStates.top().aShape.nTop;
1027 auto pXExtValue = new RTFValue(oox::drawingml::convertHmmToEmu(nXExt));
1028 auto pYExtValue = new RTFValue(oox::drawingml::convertHmmToEmu(nYExt));
1029 aExtentAttributes.set(NS_ooxml::LN_CT_PositiveSize2D_cx, pXExtValue);
1030 aExtentAttributes.set(NS_ooxml::LN_CT_PositiveSize2D_cy, pYExtValue);
1031 auto pExtentValue = new RTFValue(aExtentAttributes);
1032 // docpr sprm
1033 RTFSprms aDocprAttributes;
1034 for (auto& rCharacterAttribute : m_aStates.top().aCharacterAttributes)
1035 if (rCharacterAttribute.first == NS_ooxml::LN_CT_NonVisualDrawingProps_name
1036 || rCharacterAttribute.first == NS_ooxml::LN_CT_NonVisualDrawingProps_descr)
1037 aDocprAttributes.set(rCharacterAttribute.first, rCharacterAttribute.second);
1038 auto pDocprValue = new RTFValue(aDocprAttributes);
1039 if (bInline)
1041 RTFSprms aInlineAttributes;
1042 aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distT, new RTFValue(0));
1043 aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distB, new RTFValue(0));
1044 aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distL, new RTFValue(0));
1045 aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distR, new RTFValue(0));
1046 RTFSprms aInlineSprms;
1047 aInlineSprms.set(NS_ooxml::LN_CT_Inline_extent, pExtentValue);
1048 aInlineSprms.set(NS_ooxml::LN_CT_Inline_docPr, pDocprValue);
1049 aInlineSprms.set(NS_ooxml::LN_graphic_graphic, pGraphicValue);
1050 // inline sprm
1051 auto pValue = new RTFValue(aInlineAttributes, aInlineSprms);
1052 aSprms.set(NS_ooxml::LN_inline_inline, pValue);
1054 else // anchored
1056 // wrap sprm
1057 RTFSprms aAnchorWrapAttributes;
1058 m_aStates.top().aShape.aAnchorAttributes.set(
1059 NS_ooxml::LN_CT_Anchor_behindDoc,
1060 new RTFValue((m_aStates.top().aShape.bInBackground) ? 1 : 0));
1061 RTFSprms aAnchorSprms;
1062 for (auto& rCharacterAttribute : m_aStates.top().aCharacterAttributes)
1064 if (rCharacterAttribute.first == NS_ooxml::LN_CT_WrapSquare_wrapText)
1065 aAnchorWrapAttributes.set(rCharacterAttribute.first, rCharacterAttribute.second);
1067 sal_Int32 nWrap = -1;
1068 for (auto& rCharacterSprm : m_aStates.top().aCharacterSprms)
1070 if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapNone
1071 || rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight)
1073 nWrap = rCharacterSprm.first;
1075 // If there is a wrap polygon prepared by RTFSdrImport, pick it up here.
1076 if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight
1077 && !m_aStates.top().aShape.aWrapPolygonSprms.empty())
1078 rCharacterSprm.second->getSprms().set(
1079 NS_ooxml::LN_CT_WrapTight_wrapPolygon,
1080 new RTFValue(RTFSprms(), m_aStates.top().aShape.aWrapPolygonSprms));
1082 aAnchorSprms.set(rCharacterSprm.first, rCharacterSprm.second);
1086 if (m_aStates.top().aShape.aWrapSprm.first != 0)
1087 // Replay of a buffered shape, wrap sprm there has priority over
1088 // character sprms of the current state.
1089 aAnchorSprms.set(m_aStates.top().aShape.aWrapSprm.first,
1090 m_aStates.top().aShape.aWrapSprm.second);
1092 aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_extent, pExtentValue);
1093 if (!aAnchorWrapAttributes.empty() && nWrap == -1)
1094 aAnchorSprms.set(NS_ooxml::LN_EG_WrapType_wrapSquare,
1095 new RTFValue(aAnchorWrapAttributes));
1097 // See OOXMLFastContextHandler::positionOffset(), we can't just put offset values in an RTFValue.
1098 RTFSprms aPoshAttributes;
1099 RTFSprms aPoshSprms;
1100 if (m_aStates.top().aShape.nHoriOrientRelationToken > 0)
1101 aPoshAttributes.set(NS_ooxml::LN_CT_PosH_relativeFrom,
1102 new RTFValue(m_aStates.top().aShape.nHoriOrientRelationToken));
1103 if (m_aStates.top().aShape.nLeft != 0)
1105 Mapper().positionOffset(
1106 OUString::number(oox::drawingml::convertHmmToEmu(m_aStates.top().aShape.nLeft)),
1107 /*bVertical=*/false);
1108 aPoshSprms.set(NS_ooxml::LN_CT_PosH_posOffset, new RTFValue());
1110 aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_positionH,
1111 new RTFValue(aPoshAttributes, aPoshSprms));
1113 RTFSprms aPosvAttributes;
1114 RTFSprms aPosvSprms;
1115 if (m_aStates.top().aShape.nVertOrientRelationToken > 0)
1116 aPosvAttributes.set(NS_ooxml::LN_CT_PosV_relativeFrom,
1117 new RTFValue(m_aStates.top().aShape.nVertOrientRelationToken));
1118 if (m_aStates.top().aShape.nTop != 0)
1120 Mapper().positionOffset(
1121 OUString::number(oox::drawingml::convertHmmToEmu(m_aStates.top().aShape.nTop)),
1122 /*bVertical=*/true);
1123 aPosvSprms.set(NS_ooxml::LN_CT_PosV_posOffset, new RTFValue());
1125 aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_positionV,
1126 new RTFValue(aPosvAttributes, aPosvSprms));
1128 aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_docPr, pDocprValue);
1129 aAnchorSprms.set(NS_ooxml::LN_graphic_graphic, pGraphicValue);
1130 // anchor sprm
1131 auto pValue = new RTFValue(m_aStates.top().aShape.aAnchorAttributes, aAnchorSprms);
1132 aSprms.set(NS_ooxml::LN_anchor_anchor, pValue);
1134 writerfilter::Reference<Properties>::Pointer_t pProperties
1135 = new RTFReferenceProperties(aAttributes, aSprms);
1136 checkFirstRun();
1138 if (!m_aStates.top().pCurrentBuffer)
1140 Mapper().props(pProperties);
1141 // Make sure we don't lose these properties with a too early reset.
1142 m_bHadPicture = true;
1144 else
1146 auto pValue = new RTFValue(aAttributes, aSprms);
1147 bufferProperties(*m_aStates.top().pCurrentBuffer, pValue, nullptr);
1151 RTFError RTFDocumentImpl::resolveChars(char ch)
1153 if (m_aStates.top().nInternalState == RTFInternalState::BIN)
1155 m_pBinaryData.reset(new SvMemoryStream());
1156 m_pBinaryData->WriteChar(ch);
1157 for (int i = 0; i < m_aStates.top().nBinaryToRead - 1; ++i)
1159 Strm().ReadChar(ch);
1160 m_pBinaryData->WriteChar(ch);
1162 m_aStates.top().nInternalState = RTFInternalState::NORMAL;
1163 return RTFError::OK;
1166 OStringBuffer aBuf;
1168 bool bUnicodeChecked = false;
1169 bool bSkipped = false;
1171 while (!Strm().eof()
1172 && (m_aStates.top().nInternalState == RTFInternalState::HEX
1173 || (ch != '{' && ch != '}' && ch != '\\')))
1175 if (m_aStates.top().nInternalState == RTFInternalState::HEX || (ch != 0x0d && ch != 0x0a))
1177 if (m_aStates.top().nCharsToSkip == 0)
1179 if (!bUnicodeChecked)
1181 checkUnicode(/*bUnicode =*/true, /*bHex =*/false);
1182 bUnicodeChecked = true;
1184 aBuf.append(ch);
1186 else
1188 bSkipped = true;
1189 m_aStates.top().nCharsToSkip--;
1193 // read a single char if we're in hex mode
1194 if (m_aStates.top().nInternalState == RTFInternalState::HEX)
1195 break;
1197 if (RTL_TEXTENCODING_MS_932 == m_aStates.top().nCurrentEncoding)
1199 unsigned char uch = ch;
1200 if ((uch >= 0x80 && uch <= 0x9F) || uch >= 0xE0)
1202 // read second byte of 2-byte Shift-JIS - may be \ { }
1203 Strm().ReadChar(ch);
1204 if (m_aStates.top().nCharsToSkip == 0)
1206 // fdo#79384: Word will reject Shift-JIS following \loch
1207 // but apparently OOo could read and (worse) write such documents
1208 SAL_INFO_IF(m_aStates.top().eRunType != RTFParserState::RunType::DBCH,
1209 "writerfilter.rtf", "invalid Shift-JIS without DBCH");
1210 assert(bUnicodeChecked);
1211 aBuf.append(ch);
1213 else
1215 assert(bSkipped);
1216 // anybody who uses \ucN with Shift-JIS is insane
1217 m_aStates.top().nCharsToSkip--;
1222 Strm().ReadChar(ch);
1224 if (m_aStates.top().nInternalState != RTFInternalState::HEX && !Strm().eof())
1225 Strm().SeekRel(-1);
1227 if (m_aStates.top().nInternalState == RTFInternalState::HEX
1228 && m_aStates.top().eDestination != Destination::LEVELNUMBERS)
1230 if (!bSkipped)
1232 // note: apparently \'0d\'0a is interpreted as 2 breaks, not 1
1233 if ((ch == '\r' || ch == '\n') && m_aStates.top().eDestination != Destination::DOCCOMM
1234 && m_aStates.top().eDestination != Destination::LEVELNUMBERS
1235 && m_aStates.top().eDestination != Destination::LEVELTEXT)
1237 checkUnicode(/*bUnicode =*/false, /*bHex =*/true);
1238 dispatchSymbol(RTF_PAR);
1240 else
1242 m_aHexBuffer.append(ch);
1245 return RTFError::OK;
1248 if (m_aStates.top().eDestination == Destination::SKIP)
1249 return RTFError::OK;
1250 OString aStr = aBuf.makeStringAndClear();
1251 if (m_aStates.top().eDestination == Destination::LEVELNUMBERS)
1253 if (aStr.toChar() != ';')
1254 m_aStates.top().aLevelNumbers.push_back(sal_Int32(ch));
1255 return RTFError::OK;
1258 OUString aOUStr(OStringToOUString(aStr, m_aStates.top().nCurrentEncoding));
1259 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolveChars: collected '" << aOUStr << "'");
1261 if (m_aStates.top().eDestination == Destination::COLORTABLE)
1263 // we hit a ';' at the end of each color entry
1264 m_aColorTable.push_back(m_aStates.top().aCurrentColor.GetColor());
1265 // set components back to zero
1266 m_aStates.top().aCurrentColor = RTFColorTableEntry();
1268 else if (!aStr.isEmpty())
1269 m_aHexBuffer.append(aStr);
1271 checkUnicode(/*bUnicode =*/false, /*bHex =*/true);
1272 return RTFError::OK;
1275 bool RTFFrame::inFrame() { return m_nW > 0 || m_nH > 0 || m_nX > 0 || m_nY > 0; }
1277 void RTFDocumentImpl::singleChar(sal_uInt8 nValue, bool bRunProps)
1279 sal_uInt8 sValue[] = { nValue };
1280 RTFBuffer_t* pCurrentBuffer = m_aStates.top().pCurrentBuffer;
1282 if (!pCurrentBuffer)
1284 Mapper().startCharacterGroup();
1285 // Should we send run properties?
1286 if (bRunProps)
1287 runProps();
1288 Mapper().text(sValue, 1);
1289 Mapper().endCharacterGroup();
1291 else
1293 pCurrentBuffer->push_back(Buf_t(BUFFER_STARTRUN, nullptr, nullptr));
1294 auto pValue = new RTFValue(*sValue);
1295 pCurrentBuffer->push_back(Buf_t(BUFFER_TEXT, pValue, nullptr));
1296 pCurrentBuffer->push_back(Buf_t(BUFFER_ENDRUN, nullptr, nullptr));
1300 void RTFDocumentImpl::text(OUString& rString)
1302 if (rString.getLength() == 1 && m_aStates.top().eDestination != Destination::DOCCOMM)
1304 // No cheating! Tokenizer ignores bare \r and \n, their hex \'0d / \'0a form doesn't count, either.
1305 sal_Unicode ch = rString[0];
1306 if (ch == 0x0d || ch == 0x0a)
1307 return;
1310 bool bRet = true;
1311 switch (m_aStates.top().eDestination)
1313 // Note: in fonttbl there may or may not be groups; in stylesheet
1314 // and revtbl groups are mandatory
1315 case Destination::FONTTABLE:
1316 case Destination::FONTENTRY:
1317 case Destination::STYLEENTRY:
1318 case Destination::LISTNAME:
1319 case Destination::REVISIONENTRY:
1321 // ; is the end of the entry
1322 bool bEnd = false;
1323 if (rString.endsWith(";"))
1325 rString = rString.copy(0, rString.getLength() - 1);
1326 bEnd = true;
1328 m_aStates.top().appendDestinationText(rString);
1329 if (bEnd)
1331 // always clear, necessary in case of group-less fonttable
1332 OUString const aName = m_aStates.top().pDestinationText->makeStringAndClear();
1333 switch (m_aStates.top().eDestination)
1335 case Destination::FONTTABLE:
1336 case Destination::FONTENTRY:
1338 m_aFontNames[m_nCurrentFontIndex] = aName;
1339 if (m_nCurrentEncoding >= 0)
1341 m_aFontEncodings[m_nCurrentFontIndex] = m_nCurrentEncoding;
1342 m_nCurrentEncoding = -1;
1344 m_aStates.top().aTableAttributes.set(NS_ooxml::LN_CT_Font_name,
1345 new RTFValue(aName));
1347 writerfilter::Reference<Properties>::Pointer_t const pProp(
1348 new RTFReferenceProperties(m_aStates.top().aTableAttributes,
1349 m_aStates.top().aTableSprms));
1351 //See fdo#47347 initial invalid font entry properties are inserted first,
1352 //so when we attempt to insert the correct ones, there's already an
1353 //entry in the map for them, so the new ones aren't inserted.
1354 auto lb = m_aFontTableEntries.lower_bound(m_nCurrentFontIndex);
1355 if (lb != m_aFontTableEntries.end()
1356 && !(m_aFontTableEntries.key_comp()(m_nCurrentFontIndex, lb->first)))
1357 lb->second = pProp;
1358 else
1359 m_aFontTableEntries.insert(lb,
1360 std::make_pair(m_nCurrentFontIndex, pProp));
1362 break;
1363 case Destination::STYLEENTRY:
1365 RTFValue::Pointer_t pType
1366 = m_aStates.top().aTableAttributes.find(NS_ooxml::LN_CT_Style_type);
1367 if (pType)
1369 // Word strips whitespace around style names.
1370 m_aStyleNames[m_nCurrentStyleIndex] = aName.trim();
1371 m_aStyleTypes[m_nCurrentStyleIndex] = pType->getInt();
1372 auto pValue = new RTFValue(aName.trim());
1373 m_aStates.top().aTableAttributes.set(NS_ooxml::LN_CT_Style_styleId,
1374 pValue);
1375 m_aStates.top().aTableSprms.set(NS_ooxml::LN_CT_Style_name, pValue);
1377 writerfilter::Reference<Properties>::Pointer_t const pProp(
1378 createStyleProperties());
1379 m_aStyleTableEntries.insert(
1380 std::make_pair(m_nCurrentStyleIndex, pProp));
1382 else
1383 SAL_INFO("writerfilter.rtf", "no RTF style type defined, ignoring");
1384 break;
1386 case Destination::LISTNAME:
1387 // TODO: what can be done with a list name?
1388 break;
1389 case Destination::REVISIONENTRY:
1390 m_aAuthors[m_aAuthors.size()] = aName;
1391 break;
1392 default:
1393 break;
1395 resetAttributes();
1396 resetSprms();
1399 break;
1400 case Destination::LEVELTEXT:
1401 case Destination::SHAPEPROPERTYNAME:
1402 case Destination::SHAPEPROPERTYVALUE:
1403 case Destination::BOOKMARKEND:
1404 case Destination::PICT:
1405 case Destination::SHAPEPROPERTYVALUEPICT:
1406 case Destination::FORMFIELDNAME:
1407 case Destination::FORMFIELDLIST:
1408 case Destination::DATAFIELD:
1409 case Destination::AUTHOR:
1410 case Destination::KEYWORDS:
1411 case Destination::OPERATOR:
1412 case Destination::COMPANY:
1413 case Destination::COMMENT:
1414 case Destination::OBJDATA:
1415 case Destination::OBJCLASS:
1416 case Destination::ANNOTATIONDATE:
1417 case Destination::ANNOTATIONAUTHOR:
1418 case Destination::ANNOTATIONREFERENCE:
1419 case Destination::FALT:
1420 case Destination::PARAGRAPHNUMBERING_TEXTAFTER:
1421 case Destination::PARAGRAPHNUMBERING_TEXTBEFORE:
1422 case Destination::TITLE:
1423 case Destination::SUBJECT:
1424 case Destination::DOCCOMM:
1425 case Destination::ATNID:
1426 case Destination::ANNOTATIONREFERENCESTART:
1427 case Destination::ANNOTATIONREFERENCEEND:
1428 case Destination::MR:
1429 case Destination::MCHR:
1430 case Destination::MPOS:
1431 case Destination::MVERTJC:
1432 case Destination::MSTRIKEH:
1433 case Destination::MDEGHIDE:
1434 case Destination::MBEGCHR:
1435 case Destination::MSEPCHR:
1436 case Destination::MENDCHR:
1437 case Destination::MSUBHIDE:
1438 case Destination::MSUPHIDE:
1439 case Destination::MTYPE:
1440 case Destination::MGROW:
1441 case Destination::INDEXENTRY:
1442 case Destination::TOCENTRY:
1443 case Destination::PROPNAME:
1444 case Destination::STATICVAL:
1445 m_aStates.top().appendDestinationText(rString);
1446 break;
1447 default:
1448 bRet = false;
1449 break;
1451 if (bRet)
1452 return;
1454 if (!m_aIgnoreFirst.isEmpty() && m_aIgnoreFirst == rString)
1456 m_aIgnoreFirst.clear();
1457 return;
1460 // Are we in the middle of the table definition? (No cell defs yet, but we already have some cell props.)
1461 if (m_aStates.top().aTableCellSprms.find(NS_ooxml::LN_CT_TcPrBase_vAlign).get()
1462 && m_nTopLevelCells == 0)
1464 m_aTableBufferStack.back().emplace_back(
1465 Buf_t(BUFFER_UTEXT, new RTFValue(rString), nullptr));
1466 return;
1469 checkFirstRun();
1470 checkNeedPap();
1472 // Don't return earlier, a bookmark start has to be in a paragraph group.
1473 if (m_aStates.top().eDestination == Destination::BOOKMARKSTART)
1475 m_aStates.top().appendDestinationText(rString);
1476 return;
1479 RTFBuffer_t* pCurrentBuffer = m_aStates.top().pCurrentBuffer;
1481 if (!pCurrentBuffer && m_aStates.top().eDestination != Destination::FOOTNOTE)
1482 Mapper().startCharacterGroup();
1483 else if (pCurrentBuffer)
1485 RTFValue::Pointer_t pValue;
1486 pCurrentBuffer->push_back(Buf_t(BUFFER_STARTRUN, pValue, nullptr));
1489 if (m_aStates.top().eDestination == Destination::NORMAL
1490 || m_aStates.top().eDestination == Destination::FIELDRESULT
1491 || m_aStates.top().eDestination == Destination::SHAPETEXT)
1492 runProps();
1494 if (!pCurrentBuffer)
1495 Mapper().utext(reinterpret_cast<sal_uInt8 const*>(rString.getStr()), rString.getLength());
1496 else
1498 auto pValue = new RTFValue(rString);
1499 pCurrentBuffer->push_back(Buf_t(BUFFER_UTEXT, pValue, nullptr));
1502 m_bNeedCr = true;
1504 if (!pCurrentBuffer && m_aStates.top().eDestination != Destination::FOOTNOTE)
1505 Mapper().endCharacterGroup();
1506 else if (pCurrentBuffer)
1508 RTFValue::Pointer_t pValue;
1509 pCurrentBuffer->push_back(Buf_t(BUFFER_ENDRUN, pValue, nullptr));
1513 void RTFDocumentImpl::prepareProperties(
1514 RTFParserState& rState, writerfilter::Reference<Properties>::Pointer_t& o_rpParagraphProperties,
1515 writerfilter::Reference<Properties>::Pointer_t& o_rpFrameProperties,
1516 writerfilter::Reference<Properties>::Pointer_t& o_rpTableRowProperties, int const nCells,
1517 int const nCurrentCellX)
1519 o_rpParagraphProperties = getProperties(rState.aParagraphAttributes, rState.aParagraphSprms,
1520 NS_ooxml::LN_Value_ST_StyleType_paragraph);
1522 if (rState.aFrame.hasProperties())
1524 o_rpFrameProperties = new RTFReferenceProperties(RTFSprms(), rState.aFrame.getSprms());
1527 // Table width.
1528 RTFValue::Pointer_t const pTableWidthProps
1529 = rState.aTableRowSprms.find(NS_ooxml::LN_CT_TblPrBase_tblW);
1530 if (!pTableWidthProps.get())
1532 auto pUnitValue = new RTFValue(3);
1533 putNestedAttribute(rState.aTableRowSprms, NS_ooxml::LN_CT_TblPrBase_tblW,
1534 NS_ooxml::LN_CT_TblWidth_type, pUnitValue);
1535 auto pWValue = new RTFValue(nCurrentCellX);
1536 putNestedAttribute(rState.aTableRowSprms, NS_ooxml::LN_CT_TblPrBase_tblW,
1537 NS_ooxml::LN_CT_TblWidth_w, pWValue);
1540 if (nCells > 0)
1541 rState.aTableRowSprms.set(NS_ooxml::LN_tblRow, new RTFValue(1));
1543 RTFValue::Pointer_t const pCellMar
1544 = rState.aTableRowSprms.find(NS_ooxml::LN_CT_TblPrBase_tblCellMar);
1545 if (!pCellMar.get())
1547 // If no cell margins are defined, the default left/right margin is 0 in Word, but not in Writer.
1548 RTFSprms aAttributes;
1549 aAttributes.set(NS_ooxml::LN_CT_TblWidth_type,
1550 new RTFValue(NS_ooxml::LN_Value_ST_TblWidth_dxa));
1551 aAttributes.set(NS_ooxml::LN_CT_TblWidth_w, new RTFValue(0));
1552 putNestedSprm(rState.aTableRowSprms, NS_ooxml::LN_CT_TblPrBase_tblCellMar,
1553 NS_ooxml::LN_CT_TblCellMar_left, new RTFValue(aAttributes));
1554 putNestedSprm(rState.aTableRowSprms, NS_ooxml::LN_CT_TblPrBase_tblCellMar,
1555 NS_ooxml::LN_CT_TblCellMar_right, new RTFValue(aAttributes));
1558 o_rpTableRowProperties
1559 = new RTFReferenceProperties(rState.aTableRowAttributes, rState.aTableRowSprms);
1562 void RTFDocumentImpl::sendProperties(
1563 writerfilter::Reference<Properties>::Pointer_t const& pParagraphProperties,
1564 writerfilter::Reference<Properties>::Pointer_t const& pFrameProperties,
1565 writerfilter::Reference<Properties>::Pointer_t const& pTableRowProperties)
1567 Mapper().props(pParagraphProperties);
1569 if (pFrameProperties)
1571 Mapper().props(pFrameProperties);
1574 Mapper().props(pTableRowProperties);
1576 tableBreak();
1579 void RTFDocumentImpl::replayRowBuffer(RTFBuffer_t& rBuffer, ::std::deque<RTFSprms>& rCellsSrpms,
1580 ::std::deque<RTFSprms>& rCellsAttributes, int const nCells)
1582 for (int i = 0; i < nCells; ++i)
1584 replayBuffer(rBuffer, &rCellsSrpms.front(), &rCellsAttributes.front());
1585 rCellsSrpms.pop_front();
1586 rCellsAttributes.pop_front();
1588 for (Buf_t& i : rBuffer)
1590 SAL_WARN_IF(BUFFER_CELLEND == std::get<0>(i), "writerfilter.rtf", "dropping table cell!");
1592 assert(rCellsSrpms.empty());
1593 assert(rCellsAttributes.empty());
1596 void RTFDocumentImpl::replayBuffer(RTFBuffer_t& rBuffer, RTFSprms* const pSprms,
1597 RTFSprms const* const pAttributes)
1599 while (!rBuffer.empty())
1601 Buf_t aTuple(rBuffer.front());
1602 rBuffer.pop_front();
1603 if (std::get<0>(aTuple) == BUFFER_PROPS)
1605 // Construct properties via getProperties() and not directly, to take care of deduplication.
1606 writerfilter::Reference<Properties>::Pointer_t const pProp(getProperties(
1607 std::get<1>(aTuple)->getAttributes(), std::get<1>(aTuple)->getSprms(), 0));
1608 Mapper().props(pProp);
1610 else if (std::get<0>(aTuple) == BUFFER_NESTROW)
1612 TableRowBuffer& rRowBuffer(*std::get<2>(aTuple));
1614 replayRowBuffer(rRowBuffer.buffer, rRowBuffer.cellsSprms, rRowBuffer.cellsAttributes,
1615 rRowBuffer.nCells);
1617 sendProperties(rRowBuffer.pParaProperties, rRowBuffer.pFrameProperties,
1618 rRowBuffer.pRowProperties);
1620 else if (std::get<0>(aTuple) == BUFFER_CELLEND)
1622 assert(pSprms && pAttributes);
1623 auto pValue = new RTFValue(1);
1624 pSprms->set(NS_ooxml::LN_tblCell, pValue);
1625 writerfilter::Reference<Properties>::Pointer_t const pTableCellProperties(
1626 new RTFReferenceProperties(*pAttributes, *pSprms));
1627 Mapper().props(pTableCellProperties);
1628 tableBreak();
1629 break;
1631 else if (std::get<0>(aTuple) == BUFFER_STARTRUN)
1632 Mapper().startCharacterGroup();
1633 else if (std::get<0>(aTuple) == BUFFER_TEXT)
1635 sal_uInt8 const nValue = std::get<1>(aTuple)->getInt();
1636 Mapper().text(&nValue, 1);
1638 else if (std::get<0>(aTuple) == BUFFER_UTEXT)
1640 OUString const aString(std::get<1>(aTuple)->getString());
1641 Mapper().utext(reinterpret_cast<sal_uInt8 const*>(aString.getStr()),
1642 aString.getLength());
1644 else if (std::get<0>(aTuple) == BUFFER_ENDRUN)
1645 Mapper().endCharacterGroup();
1646 else if (std::get<0>(aTuple) == BUFFER_PAR)
1647 parBreak();
1648 else if (std::get<0>(aTuple) == BUFFER_STARTSHAPE)
1649 m_pSdrImport->resolve(std::get<1>(aTuple)->getShape(), false, RTFSdrImport::SHAPE);
1650 else if (std::get<0>(aTuple) == BUFFER_RESOLVESHAPE)
1652 // Make sure there is no current buffer while replaying the shape,
1653 // otherwise it gets re-buffered.
1654 RTFBuffer_t* pCurrentBuffer = m_aStates.top().pCurrentBuffer;
1655 m_aStates.top().pCurrentBuffer = nullptr;
1657 // Set current shape during replay, needed by e.g. wrap in
1658 // background.
1659 m_aStates.top().aShape = std::get<1>(aTuple)->getShape();
1661 m_pSdrImport->resolve(std::get<1>(aTuple)->getShape(), true, RTFSdrImport::SHAPE);
1662 m_aStates.top().pCurrentBuffer = pCurrentBuffer;
1664 else if (std::get<0>(aTuple) == BUFFER_ENDSHAPE)
1665 m_pSdrImport->close();
1666 else if (std::get<0>(aTuple) == BUFFER_RESOLVESUBSTREAM)
1668 RTFSprms& rAttributes = std::get<1>(aTuple)->getAttributes();
1669 std::size_t nPos = rAttributes.find(0)->getInt();
1670 Id nId = rAttributes.find(1)->getInt();
1671 OUString aCustomMark = rAttributes.find(2)->getString();
1672 resolveSubstream(nPos, nId, aCustomMark);
1674 else if (std::get<0>(aTuple) == BUFFER_PICTURE)
1675 m_aStates.top().aPicture = std::get<1>(aTuple)->getPicture();
1676 else if (std::get<0>(aTuple) == BUFFER_SETSTYLE)
1678 if (!m_aStates.empty())
1679 m_aStates.top().nCurrentStyleIndex = std::get<1>(aTuple)->getInt();
1681 else
1682 assert(false);
1686 bool findPropertyName(const std::vector<beans::PropertyValue>& rProperties, const OUString& rName)
1688 for (auto& rProperty : rProperties)
1690 if (rProperty.Name == rName)
1691 return true;
1693 return false;
1696 void RTFDocumentImpl::backupTableRowProperties()
1698 if (m_nTopLevelCurrentCellX)
1700 m_aBackupTableRowSprms = m_aStates.top().aTableRowSprms;
1701 m_aBackupTableRowAttributes = m_aStates.top().aTableRowAttributes;
1702 m_nBackupTopLevelCurrentCellX = m_nTopLevelCurrentCellX;
1706 void RTFDocumentImpl::restoreTableRowProperties()
1708 m_aStates.top().aTableRowSprms = m_aBackupTableRowSprms;
1709 m_aStates.top().aTableRowAttributes = m_aBackupTableRowAttributes;
1710 m_nTopLevelCurrentCellX = m_nBackupTopLevelCurrentCellX;
1713 void RTFDocumentImpl::resetTableRowProperties()
1715 m_aStates.top().aTableRowSprms = m_aDefaultState.aTableRowSprms;
1716 m_aStates.top().aTableRowSprms.set(NS_ooxml::LN_CT_TblGridBase_gridCol, new RTFValue(-1),
1717 RTFOverwrite::NO_APPEND);
1718 m_aStates.top().aTableRowAttributes = m_aDefaultState.aTableRowAttributes;
1719 if (Destination::NESTEDTABLEPROPERTIES == m_aStates.top().eDestination)
1721 m_nNestedTRLeft = 0;
1722 m_nNestedCurrentCellX = 0;
1724 else
1726 m_nTopLevelTRLeft = 0;
1727 m_nTopLevelCurrentCellX = 0;
1731 RTFError RTFDocumentImpl::dispatchToggle(RTFKeyword nKeyword, bool bParam, int nParam)
1733 setNeedSect(true);
1734 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
1735 RTFSkipDestination aSkip(*this);
1736 int nSprm = -1;
1737 tools::SvRef<RTFValue> pBoolValue(new RTFValue(int(!bParam || nParam != 0)));
1739 // Underline toggles.
1740 switch (nKeyword)
1742 case RTF_UL:
1743 nSprm = NS_ooxml::LN_Value_ST_Underline_single;
1744 break;
1745 case RTF_ULDASH:
1746 nSprm = NS_ooxml::LN_Value_ST_Underline_dash;
1747 break;
1748 case RTF_ULDASHD:
1749 nSprm = NS_ooxml::LN_Value_ST_Underline_dotDash;
1750 break;
1751 case RTF_ULDASHDD:
1752 nSprm = NS_ooxml::LN_Value_ST_Underline_dotDotDash;
1753 break;
1754 case RTF_ULDB:
1755 nSprm = NS_ooxml::LN_Value_ST_Underline_double;
1756 break;
1757 case RTF_ULHWAVE:
1758 nSprm = NS_ooxml::LN_Value_ST_Underline_wavyHeavy;
1759 break;
1760 case RTF_ULLDASH:
1761 nSprm = NS_ooxml::LN_Value_ST_Underline_dashLong;
1762 break;
1763 case RTF_ULTH:
1764 nSprm = NS_ooxml::LN_Value_ST_Underline_thick;
1765 break;
1766 case RTF_ULTHD:
1767 nSprm = NS_ooxml::LN_Value_ST_Underline_dottedHeavy;
1768 break;
1769 case RTF_ULTHDASH:
1770 nSprm = NS_ooxml::LN_Value_ST_Underline_dashedHeavy;
1771 break;
1772 case RTF_ULTHDASHD:
1773 nSprm = NS_ooxml::LN_Value_ST_Underline_dashDotHeavy;
1774 break;
1775 case RTF_ULTHDASHDD:
1776 nSprm = NS_ooxml::LN_Value_ST_Underline_dashDotDotHeavy;
1777 break;
1778 case RTF_ULTHLDASH:
1779 nSprm = NS_ooxml::LN_Value_ST_Underline_dashLongHeavy;
1780 break;
1781 case RTF_ULULDBWAVE:
1782 nSprm = NS_ooxml::LN_Value_ST_Underline_wavyDouble;
1783 break;
1784 case RTF_ULWAVE:
1785 nSprm = NS_ooxml::LN_Value_ST_Underline_wave;
1786 break;
1787 default:
1788 break;
1790 if (nSprm >= 0)
1792 auto pValue
1793 = new RTFValue((!bParam || nParam != 0) ? nSprm : NS_ooxml::LN_Value_ST_Underline_none);
1794 m_aStates.top().aCharacterAttributes.set(NS_ooxml::LN_CT_Underline_val, pValue);
1795 return RTFError::OK;
1798 // Accent characters (over dot / over comma).
1799 switch (nKeyword)
1801 case RTF_ACCNONE:
1802 nSprm = NS_ooxml::LN_Value_ST_Em_none;
1803 break;
1804 case RTF_ACCDOT:
1805 nSprm = NS_ooxml::LN_Value_ST_Em_dot;
1806 break;
1807 case RTF_ACCCOMMA:
1808 nSprm = NS_ooxml::LN_Value_ST_Em_comma;
1809 break;
1810 case RTF_ACCCIRCLE:
1811 nSprm = NS_ooxml::LN_Value_ST_Em_circle;
1812 break;
1813 case RTF_ACCUNDERDOT:
1814 nSprm = NS_ooxml::LN_Value_ST_Em_underDot;
1815 break;
1816 default:
1817 break;
1819 if (nSprm >= 0)
1821 auto pValue = new RTFValue((!bParam || nParam != 0) ? nSprm : 0);
1822 m_aStates.top().aCharacterSprms.set(NS_ooxml::LN_EG_RPrBase_em, pValue);
1823 return RTFError::OK;
1826 // Trivial character sprms.
1827 switch (nKeyword)
1829 case RTF_B:
1830 case RTF_AB:
1831 nSprm = (m_aStates.top().isRightToLeft
1832 || m_aStates.top().eRunType == RTFParserState::RunType::HICH)
1833 ? NS_ooxml::LN_EG_RPrBase_bCs
1834 : NS_ooxml::LN_EG_RPrBase_b;
1835 break;
1836 case RTF_I:
1837 case RTF_AI:
1838 nSprm = (m_aStates.top().isRightToLeft
1839 || m_aStates.top().eRunType == RTFParserState::RunType::HICH)
1840 ? NS_ooxml::LN_EG_RPrBase_iCs
1841 : NS_ooxml::LN_EG_RPrBase_i;
1842 break;
1843 case RTF_OUTL:
1844 nSprm = NS_ooxml::LN_EG_RPrBase_outline;
1845 break;
1846 case RTF_SHAD:
1847 nSprm = NS_ooxml::LN_EG_RPrBase_shadow;
1848 break;
1849 case RTF_V:
1850 nSprm = NS_ooxml::LN_EG_RPrBase_vanish;
1851 break;
1852 case RTF_STRIKE:
1853 nSprm = NS_ooxml::LN_EG_RPrBase_strike;
1854 break;
1855 case RTF_STRIKED:
1856 nSprm = NS_ooxml::LN_EG_RPrBase_dstrike;
1857 break;
1858 case RTF_SCAPS:
1859 nSprm = NS_ooxml::LN_EG_RPrBase_smallCaps;
1860 break;
1861 case RTF_IMPR:
1862 nSprm = NS_ooxml::LN_EG_RPrBase_imprint;
1863 break;
1864 case RTF_CAPS:
1865 nSprm = NS_ooxml::LN_EG_RPrBase_caps;
1866 break;
1867 default:
1868 break;
1870 if (nSprm >= 0)
1872 m_aStates.top().aCharacterSprms.set(nSprm, pBoolValue);
1873 return RTFError::OK;
1876 switch (nKeyword)
1878 case RTF_ASPALPHA:
1879 m_aStates.top().aParagraphSprms.set(NS_ooxml::LN_CT_PPrBase_autoSpaceDE, pBoolValue);
1880 break;
1881 case RTF_DELETED:
1882 case RTF_REVISED:
1884 auto pValue = new RTFValue(nKeyword == RTF_DELETED ? oox::XML_del : oox::XML_ins);
1885 putNestedAttribute(m_aStates.top().aCharacterSprms, NS_ooxml::LN_trackchange,
1886 NS_ooxml::LN_token, pValue);
1888 break;
1889 case RTF_SBAUTO:
1890 putNestedAttribute(m_aStates.top().aParagraphSprms, NS_ooxml::LN_CT_PPrBase_spacing,
1891 NS_ooxml::LN_CT_Spacing_beforeAutospacing, pBoolValue);
1892 break;
1893 case RTF_SAAUTO:
1894 putNestedAttribute(m_aStates.top().aParagraphSprms, NS_ooxml::LN_CT_PPrBase_spacing,
1895 NS_ooxml::LN_CT_Spacing_afterAutospacing, pBoolValue);
1896 break;
1897 case RTF_FACINGP:
1898 m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_evenAndOddHeaders, pBoolValue);
1899 break;
1900 case RTF_HYPHAUTO:
1901 m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_autoHyphenation, pBoolValue);
1902 break;
1903 case RTF_HYPHPAR:
1904 m_aStates.top().aParagraphSprms.set(NS_ooxml::LN_CT_PPrBase_suppressAutoHyphens,
1905 new RTFValue(int(bParam && nParam == 0)));
1906 break;
1907 default:
1909 SAL_INFO("writerfilter.rtf",
1910 "TODO handle toggle '" << keywordToString(nKeyword) << "'");
1911 aSkip.setParsed(false);
1913 break;
1915 return RTFError::OK;
1918 RTFError RTFDocumentImpl::pushState()
1920 //SAL_INFO("writerfilter.rtf", OSL_THIS_FUNC << " before push: " << m_pTokenizer->getGroup());
1922 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
1923 m_nGroupStartPos = Strm().Tell();
1925 if (m_aStates.empty())
1926 m_aStates.push(m_aDefaultState);
1927 else
1929 // fdo#85812 group resets run type of _current_ and new state (but not RTL)
1930 m_aStates.top().eRunType = RTFParserState::RunType::LOCH;
1932 if (m_aStates.top().eDestination == Destination::MR)
1933 lcl_DestinationToMath(m_aStates.top().pDestinationText, m_aMathBuffer, m_bMathNor);
1934 m_aStates.push(m_aStates.top());
1936 m_aStates.top().aDestinationText.setLength(0); // was copied: always reset!
1938 m_pTokenizer->pushGroup();
1940 switch (m_aStates.top().eDestination)
1942 case Destination::FONTTABLE:
1943 // this is a "faked" destination for the font entry
1944 m_aStates.top().pDestinationText = &m_aStates.top().aDestinationText;
1945 m_aStates.top().eDestination = Destination::FONTENTRY;
1946 break;
1947 case Destination::STYLESHEET:
1948 // this is a "faked" destination for the style sheet entry
1949 m_aStates.top().pDestinationText = &m_aStates.top().aDestinationText;
1950 m_aStates.top().eDestination = Destination::STYLEENTRY;
1952 // the *default* is \s0 i.e. paragraph style default
1953 // this will be overwritten by \sN \csN \dsN \tsN
1954 m_nCurrentStyleIndex = 0;
1955 auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_StyleType_paragraph);
1956 m_aStates.top().aTableAttributes.set(NS_ooxml::LN_CT_Style_type, pValue);
1958 break;
1959 case Destination::FIELDRESULT:
1960 case Destination::SHAPETEXT:
1961 case Destination::FORMFIELD:
1962 case Destination::FIELDINSTRUCTION:
1963 case Destination::PICT:
1964 m_aStates.top().eDestination = Destination::NORMAL;
1965 break;
1966 case Destination::MNUM:
1967 case Destination::MDEN:
1968 case Destination::ME:
1969 case Destination::MFNAME:
1970 case Destination::MLIM:
1971 case Destination::MSUB:
1972 case Destination::MSUP:
1973 case Destination::MDEG:
1974 case Destination::MOMATH:
1975 m_aStates.top().eDestination = Destination::MR;
1976 break;
1977 case Destination::REVISIONTABLE:
1978 // this is a "faked" destination for the revision table entry
1979 m_aStates.top().pDestinationText = &m_aStates.top().aDestinationText;
1980 m_aStates.top().eDestination = Destination::REVISIONENTRY;
1981 break;
1982 default:
1983 break;
1986 // If this is true, then ooxml:endtrackchange will be generated. Make sure
1987 // we don't generate more ooxml:endtrackchange than ooxml:trackchange: new
1988 // state does not inherit this flag.
1989 m_aStates.top().bStartedTrackchange = false;
1991 return RTFError::OK;
1994 writerfilter::Reference<Properties>::Pointer_t RTFDocumentImpl::createStyleProperties()
1996 int nBasedOn = 0;
1997 RTFValue::Pointer_t pBasedOn = m_aStates.top().aTableSprms.find(NS_ooxml::LN_CT_Style_basedOn);
1998 if (pBasedOn)
1999 nBasedOn = pBasedOn->getInt();
2000 if (nBasedOn == 0)
2002 // No parent style, then mimic what Word does: ignore attributes which
2003 // would set a margin as formatting, but with a default value.
2004 for (const auto& nId :
2005 { NS_ooxml::LN_CT_Ind_firstLine, NS_ooxml::LN_CT_Ind_left, NS_ooxml::LN_CT_Ind_right,
2006 NS_ooxml::LN_CT_Ind_start, NS_ooxml::LN_CT_Ind_end })
2008 RTFValue::Pointer_t pValue = getNestedAttribute(m_aStates.top().aParagraphSprms,
2009 NS_ooxml::LN_CT_PPrBase_ind, nId);
2010 if (pValue && pValue->getInt() == 0)
2011 eraseNestedAttribute(m_aStates.top().aParagraphSprms, NS_ooxml::LN_CT_PPrBase_ind,
2012 nId);
2016 RTFValue::Pointer_t pParaProps
2017 = new RTFValue(m_aStates.top().aParagraphAttributes, m_aStates.top().aParagraphSprms);
2018 RTFValue::Pointer_t pCharProps
2019 = new RTFValue(m_aStates.top().aCharacterAttributes, m_aStates.top().aCharacterSprms);
2021 // resetSprms will clean up this modification
2022 m_aStates.top().aTableSprms.set(NS_ooxml::LN_CT_Style_pPr, pParaProps);
2023 m_aStates.top().aTableSprms.set(NS_ooxml::LN_CT_Style_rPr, pCharProps);
2025 writerfilter::Reference<Properties>::Pointer_t const pProps(
2026 new RTFReferenceProperties(m_aStates.top().aTableAttributes, m_aStates.top().aTableSprms));
2027 return pProps;
2030 /** 2 different representations of the styles are needed:
2032 1) flat content, as read from the input file:
2033 stored in m_aStyleTableEntries, used as reference input for
2034 deduplication both here and for hard formatting in getProperties()
2036 2) real content, with proper override of sprms/attributes where it differs
2037 from parent style; this is produced here and sent to domain mapper
2039 RTFReferenceTable::Entries_t RTFDocumentImpl::deduplicateStyleTable()
2041 RTFReferenceTable::Entries_t ret;
2042 for (auto const& it : m_aStyleTableEntries)
2044 auto pStyle = it.second;
2045 // ugly downcasts here, but can't easily replace the members with
2046 // RTFReferenceProperties because dmapper wants SvRef<Properties> anyway
2047 RTFValue::Pointer_t const pBasedOn(
2048 static_cast<RTFReferenceProperties&>(*pStyle).getSprms().find(
2049 NS_ooxml::LN_CT_Style_basedOn));
2050 if (pBasedOn)
2052 int const nBasedOn(pBasedOn->getInt());
2053 auto const itParent(m_aStyleTableEntries.find(nBasedOn)); // definition as read!
2054 if (itParent != m_aStyleTableEntries.end())
2056 auto const pStyleType(
2057 static_cast<RTFReferenceProperties&>(*pStyle).getAttributes().find(
2058 NS_ooxml::LN_CT_Style_type));
2059 assert(pStyleType);
2060 int const nStyleType(pStyleType->getInt());
2061 RTFSprms const sprms(
2062 static_cast<RTFReferenceProperties&>(*pStyle).getSprms().cloneAndDeduplicate(
2063 static_cast<RTFReferenceProperties&>(*itParent->second).getSprms(),
2064 nStyleType));
2065 RTFSprms const attributes(
2066 static_cast<RTFReferenceProperties&>(*pStyle)
2067 .getAttributes()
2068 .cloneAndDeduplicate(
2069 static_cast<RTFReferenceProperties&>(*itParent->second).getAttributes(),
2070 nStyleType));
2072 pStyle = new RTFReferenceProperties(attributes, sprms);
2074 else
2076 SAL_WARN("writerfilter.rtf", "parent style not found: " << nBasedOn);
2079 ret[it.first] = pStyle;
2081 assert(ret.size() == m_aStyleTableEntries.size());
2082 return ret;
2085 void RTFDocumentImpl::resetSprms()
2087 m_aStates.top().aTableSprms.clear();
2088 m_aStates.top().aCharacterSprms.clear();
2089 m_aStates.top().aParagraphSprms.clear();
2092 void RTFDocumentImpl::resetAttributes()
2094 m_aStates.top().aTableAttributes.clear();
2095 m_aStates.top().aCharacterAttributes.clear();
2096 m_aStates.top().aParagraphAttributes.clear();
2099 static bool lcl_containsProperty(const uno::Sequence<beans::Property>& rProperties,
2100 const OUString& rName)
2102 return std::any_of(rProperties.begin(), rProperties.end(),
2103 [&](const beans::Property& rProperty) { return rProperty.Name == rName; });
2106 RTFError RTFDocumentImpl::popState()
2108 //SAL_INFO("writerfilter", OSL_THIS_FUNC << " before pop: m_pTokenizer->getGroup() " << m_pTokenizer->getGroup() <<
2109 // ", dest state: " << m_aStates.top().eDestination);
2111 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
2112 RTFParserState aState(m_aStates.top());
2113 m_bWasInFrame = aState.aFrame.inFrame();
2115 // dmapper expects some content in header/footer, so if there would be nothing, add an empty paragraph.
2116 if (m_pTokenizer->getGroup() == 1 && m_bFirstRun)
2118 switch (m_nStreamType)
2120 case NS_ooxml::LN_headerl:
2121 case NS_ooxml::LN_headerr:
2122 case NS_ooxml::LN_headerf:
2123 case NS_ooxml::LN_footerl:
2124 case NS_ooxml::LN_footerr:
2125 case NS_ooxml::LN_footerf:
2126 dispatchSymbol(RTF_PAR);
2127 break;
2131 switch (aState.eDestination)
2133 case Destination::FONTTABLE:
2135 writerfilter::Reference<Table>::Pointer_t const pTable(
2136 new RTFReferenceTable(m_aFontTableEntries));
2137 Mapper().table(NS_ooxml::LN_FONTTABLE, pTable);
2138 if (m_nDefaultFontIndex >= 0)
2140 auto pValue = new RTFValue(m_aFontNames[getFontIndex(m_nDefaultFontIndex)]);
2141 putNestedAttribute(m_aDefaultState.aCharacterSprms, NS_ooxml::LN_EG_RPrBase_rFonts,
2142 NS_ooxml::LN_CT_Fonts_ascii, pValue);
2145 break;
2146 case Destination::STYLESHEET:
2148 RTFReferenceTable::Entries_t const pStyleTableDeduplicated(deduplicateStyleTable());
2149 writerfilter::Reference<Table>::Pointer_t const pTable(
2150 new RTFReferenceTable(pStyleTableDeduplicated));
2151 Mapper().table(NS_ooxml::LN_STYLESHEET, pTable);
2153 break;
2154 case Destination::LISTOVERRIDETABLE:
2156 RTFSprms aListTableAttributes;
2157 writerfilter::Reference<Properties>::Pointer_t pProp
2158 = new RTFReferenceProperties(aListTableAttributes, m_aListTableSprms);
2159 RTFReferenceTable::Entries_t aListTableEntries;
2160 aListTableEntries.insert(std::make_pair(0, pProp));
2161 writerfilter::Reference<Table>::Pointer_t const pTable(
2162 new RTFReferenceTable(aListTableEntries));
2163 Mapper().table(NS_ooxml::LN_NUMBERING, pTable);
2165 break;
2166 case Destination::LISTENTRY:
2167 for (auto& rListLevelEntry : aState.aListLevelEntries)
2168 aState.aTableSprms.set(rListLevelEntry.first, rListLevelEntry.second,
2169 RTFOverwrite::NO_APPEND);
2170 break;
2171 case Destination::FIELDINSTRUCTION:
2173 auto pValue = new RTFValue(m_aFormfieldAttributes, m_aFormfieldSprms);
2174 RTFSprms aFFAttributes;
2175 RTFSprms aFFSprms;
2176 aFFSprms.set(NS_ooxml::LN_ffdata, pValue);
2177 if (!m_aStates.top().pCurrentBuffer)
2179 writerfilter::Reference<Properties>::Pointer_t pProperties
2180 = new RTFReferenceProperties(aFFAttributes, aFFSprms);
2181 Mapper().props(pProperties);
2183 else
2185 auto pFFValue = new RTFValue(aFFAttributes, aFFSprms);
2186 bufferProperties(*m_aStates.top().pCurrentBuffer, pFFValue, nullptr);
2188 m_aFormfieldAttributes.clear();
2189 m_aFormfieldSprms.clear();
2190 singleChar(cFieldSep);
2192 break;
2193 case Destination::FIELDRESULT:
2194 singleChar(cFieldEnd);
2196 if (!m_aPicturePath.isEmpty())
2198 // Read the picture into m_aStates.top().aDestinationText.
2199 pushState();
2200 dispatchDestination(RTF_PICT);
2201 if (m_aPicturePath.endsWith(".png"))
2202 dispatchFlag(RTF_PNGBLIP);
2203 OUString aFileURL = m_rMediaDescriptor.getUnpackedValueOrDefault(
2204 utl::MediaDescriptor::PROP_URL(), OUString());
2205 OUString aPictureURL;
2208 aPictureURL = rtl::Uri::convertRelToAbs(aFileURL, m_aPicturePath);
2210 catch (const rtl::MalformedUriException& rException)
2212 SAL_WARN("writerfilter.rtf",
2213 "rtl::Uri::convertRelToAbs() failed: " << rException.getMessage());
2216 if (!aPictureURL.isEmpty())
2218 SvFileStream aStream(aPictureURL, StreamMode::READ);
2219 if (aStream.IsOpen())
2221 OUStringBuffer aBuf;
2222 while (aStream.good())
2224 unsigned char ch = 0;
2225 aStream.ReadUChar(ch);
2226 if (ch < 16)
2227 aBuf.append("0");
2228 aBuf.append(OUString::number(ch, 16));
2230 m_aStates.top().aDestinationText = aBuf;
2233 popState();
2234 m_aPicturePath.clear();
2237 break;
2238 case Destination::LEVELTEXT:
2240 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2241 break; // not for nested group
2242 OUString aStr = m_aStates.top().pDestinationText->makeStringAndClear();
2244 // The first character is the length of the string (the rest should be ignored).
2245 sal_Int32 nLength(aStr.toChar());
2246 OUString aValue;
2247 if (nLength < aStr.getLength())
2248 aValue = aStr.copy(1, nLength);
2249 else
2250 aValue = aStr;
2251 auto pValue = new RTFValue(aValue, true);
2252 aState.aTableAttributes.set(NS_ooxml::LN_CT_LevelText_val, pValue);
2254 break;
2255 case Destination::LEVELNUMBERS:
2257 bool bNestedLevelNumbers = false;
2258 if (m_aStates.size() > 1)
2259 // Current destination is levelnumbers and parent destination is levelnumbers as well.
2260 bNestedLevelNumbers
2261 = m_aStates[m_aStates.size() - 2].eDestination == Destination::LEVELNUMBERS;
2262 if (!bNestedLevelNumbers && aState.aTableSprms.find(NS_ooxml::LN_CT_Lvl_lvlText))
2264 RTFSprms& rAttributes
2265 = aState.aTableSprms.find(NS_ooxml::LN_CT_Lvl_lvlText)->getAttributes();
2266 RTFValue::Pointer_t pValue = rAttributes.find(NS_ooxml::LN_CT_LevelText_val);
2267 if (pValue && aState.bLevelNumbersValid)
2269 OUString aOrig = pValue->getString();
2271 OUStringBuffer aBuf;
2272 sal_Int32 nReplaces = 1;
2273 for (int i = 0; i < aOrig.getLength(); i++)
2275 if (std::find(aState.aLevelNumbers.begin(), aState.aLevelNumbers.end(),
2276 i + 1)
2277 != aState.aLevelNumbers.end())
2279 aBuf.append('%');
2280 // '1.1.1' -> '%1.%2.%3', but '1.' (with '2.' prefix omitted) is %2.
2281 aBuf.append(sal_Int32(nReplaces++ + aState.nListLevelNum + 1
2282 - aState.aLevelNumbers.size()));
2284 else
2285 aBuf.appendCopy(aOrig, i, 1);
2288 pValue->setString(aBuf.makeStringAndClear());
2290 else if (pValue)
2291 // Have a value, but levelnumbers is not valid -> ignore it.
2292 pValue->setString(OUString());
2294 break;
2296 case Destination::SHAPEPROPERTYNAME:
2297 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2298 break; // not for nested group
2299 aState.aShape.aProperties.emplace_back(
2300 m_aStates.top().pDestinationText->makeStringAndClear(), OUString());
2301 break;
2302 case Destination::SHAPEPROPERTYVALUE:
2303 if (!aState.aShape.aProperties.empty())
2305 aState.aShape.aProperties.back().second
2306 = m_aStates.top().pDestinationText->makeStringAndClear();
2307 if (m_aStates.top().bHadShapeText)
2308 m_pSdrImport->append(aState.aShape.aProperties.back().first,
2309 aState.aShape.aProperties.back().second);
2310 else if (aState.bInShapeGroup && !aState.bInShape
2311 && aState.aShape.aProperties.back().first == "rotation")
2313 // Rotation should be applied on the groupshape itself, not on each shape.
2314 aState.aShape.aGroupProperties.push_back(aState.aShape.aProperties.back());
2315 aState.aShape.aProperties.pop_back();
2318 break;
2319 case Destination::PICPROP:
2320 case Destination::SHAPEINSTRUCTION:
2321 if (m_aStates.size() > 1
2322 && m_aStates[m_aStates.size() - 2].eDestination == Destination::SHAPEINSTRUCTION)
2324 // Do not resolve shape if shape instruction destination is inside other shape instruction
2326 else if (!m_bObject && !aState.bInListpicture && !aState.bHadShapeText
2327 && !(aState.bInShapeGroup && !aState.bInShape))
2329 // Don't trigger a shape import in case we're only leaving the \shpinst of the groupshape itself.
2330 RTFSdrImport::ShapeOrPict eType
2331 = (aState.eDestination == Destination::SHAPEINSTRUCTION) ? RTFSdrImport::SHAPE
2332 : RTFSdrImport::PICT;
2333 if (!m_aStates.top().pCurrentBuffer || eType != RTFSdrImport::SHAPE)
2334 m_pSdrImport->resolve(m_aStates.top().aShape, true, eType);
2335 else
2337 // Shape inside table: buffer the import to have correct anchor position.
2338 // Also buffer the RTFPicture of the state stack as it contains
2339 // the shape size.
2340 auto pPictureValue = new RTFValue(m_aStates.top().aPicture);
2341 m_aStates.top().pCurrentBuffer->push_back(
2342 Buf_t(BUFFER_PICTURE, pPictureValue, nullptr));
2343 auto pValue = new RTFValue(m_aStates.top().aShape);
2345 // Buffer wrap type.
2346 for (auto& rCharacterSprm : m_aStates.top().aCharacterSprms)
2348 if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapNone
2349 || rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight)
2351 m_aStates.top().aShape.aWrapSprm = rCharacterSprm;
2352 break;
2356 m_aStates.top().pCurrentBuffer->push_back(
2357 Buf_t(BUFFER_RESOLVESHAPE, pValue, nullptr));
2360 else if (aState.bInShapeGroup && !aState.bInShape)
2362 // End of a groupshape, as we're in shapegroup, but not in a real shape.
2363 for (auto& rGroupProperty : aState.aShape.aGroupProperties)
2364 m_pSdrImport->appendGroupProperty(rGroupProperty.first, rGroupProperty.second);
2365 aState.aShape.aGroupProperties.clear();
2367 break;
2368 case Destination::BOOKMARKSTART:
2370 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2371 break; // not for nested group
2372 OUString aStr = m_aStates.top().pDestinationText->makeStringAndClear();
2373 int nPos = m_aBookmarks.size();
2374 m_aBookmarks[aStr] = nPos;
2375 if (!m_aStates.top().pCurrentBuffer)
2376 Mapper().props(new RTFReferenceProperties(lcl_getBookmarkProperties(nPos, aStr)));
2377 else
2378 bufferProperties(*m_aStates.top().pCurrentBuffer,
2379 new RTFValue(lcl_getBookmarkProperties(nPos, aStr)), nullptr);
2381 break;
2382 case Destination::BOOKMARKEND:
2384 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2385 break; // not for nested group
2386 OUString aStr = m_aStates.top().pDestinationText->makeStringAndClear();
2387 if (!m_aStates.top().pCurrentBuffer)
2388 Mapper().props(new RTFReferenceProperties(
2389 lcl_getBookmarkProperties(m_aBookmarks[aStr], aStr)));
2390 else
2391 bufferProperties(*m_aStates.top().pCurrentBuffer,
2392 new RTFValue(lcl_getBookmarkProperties(m_aBookmarks[aStr], aStr)),
2393 nullptr);
2395 break;
2396 case Destination::INDEXENTRY:
2397 case Destination::TOCENTRY:
2399 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2400 break; // not for nested group
2401 OUString str(m_aStates.top().pDestinationText->makeStringAndClear());
2402 // dmapper expects this as a field, so let's fake something...
2403 OUString const field((Destination::INDEXENTRY == aState.eDestination)
2404 ? OUStringLiteral("XE")
2405 : OUStringLiteral("TC"));
2406 str = field + " \"" + str.replaceAll("\"", "\\\"") + "\"";
2407 singleChar(cFieldStart);
2408 Mapper().utext(reinterpret_cast<sal_uInt8 const*>(str.getStr()), str.getLength());
2409 singleChar(cFieldSep);
2410 // no result
2411 singleChar(cFieldEnd);
2413 break;
2414 case Destination::FORMFIELDNAME:
2416 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2417 break; // not for nested group
2418 auto pValue = new RTFValue(m_aStates.top().pDestinationText->makeStringAndClear());
2419 m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFData_name, pValue);
2421 break;
2422 case Destination::FORMFIELDLIST:
2424 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2425 break; // not for nested group
2426 auto pValue = new RTFValue(m_aStates.top().pDestinationText->makeStringAndClear());
2427 m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFDDList_listEntry, pValue);
2429 break;
2430 case Destination::DATAFIELD:
2432 if (m_bFormField)
2434 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2435 break; // not for nested group
2436 OString aStr
2437 = OUStringToOString(m_aStates.top().pDestinationText->makeStringAndClear(),
2438 aState.nCurrentEncoding);
2439 // decode hex dump
2440 OStringBuffer aBuf;
2441 int b = 0, count = 2;
2442 for (int i = 0; i < aStr.getLength(); ++i)
2444 char ch = aStr[i];
2445 if (ch != 0x0d && ch != 0x0a)
2447 b = b << 4;
2448 sal_Int8 parsed = msfilter::rtfutil::AsHex(ch);
2449 if (parsed == -1)
2450 return RTFError::HEX_INVALID;
2451 b += parsed;
2452 count--;
2453 if (!count)
2455 aBuf.append(static_cast<char>(b));
2456 count = 2;
2457 b = 0;
2461 aStr = aBuf.makeStringAndClear();
2463 // ignore the first bytes
2464 if (aStr.getLength() > 8)
2465 aStr = aStr.copy(8);
2466 // extract name
2467 sal_Int32 nLength = aStr.toChar();
2468 if (!aStr.isEmpty())
2469 aStr = aStr.copy(1);
2470 nLength = std::min(nLength, aStr.getLength());
2471 OString aName = aStr.copy(0, nLength);
2472 if (aStr.getLength() > nLength)
2473 aStr = aStr.copy(nLength + 1); // zero-terminated string
2474 else
2475 aStr.clear();
2476 // extract default text
2477 nLength = aStr.toChar();
2478 if (!aStr.isEmpty())
2479 aStr = aStr.copy(1);
2480 auto pNValue = new RTFValue(OStringToOUString(aName, aState.nCurrentEncoding));
2481 m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFData_name, pNValue);
2482 if (nLength > 0)
2484 OString aDefaultText = aStr.copy(0, std::min(nLength, aStr.getLength()));
2485 auto pDValue
2486 = new RTFValue(OStringToOUString(aDefaultText, aState.nCurrentEncoding));
2487 m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFTextInput_default, pDValue);
2490 m_bFormField = false;
2493 break;
2494 case Destination::CREATIONTIME:
2495 if (m_xDocumentProperties.is())
2496 m_xDocumentProperties->setCreationDate(lcl_getDateTime(aState));
2497 break;
2498 case Destination::REVISIONTIME:
2499 if (m_xDocumentProperties.is())
2500 m_xDocumentProperties->setModificationDate(lcl_getDateTime(aState));
2501 break;
2502 case Destination::PRINTTIME:
2503 if (m_xDocumentProperties.is())
2504 m_xDocumentProperties->setPrintDate(lcl_getDateTime(aState));
2505 break;
2506 case Destination::AUTHOR:
2507 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2508 break; // not for nested group
2509 if (m_xDocumentProperties.is())
2510 m_xDocumentProperties->setAuthor(
2511 m_aStates.top().pDestinationText->makeStringAndClear());
2512 break;
2513 case Destination::KEYWORDS:
2514 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2515 break; // not for nested group
2516 if (m_xDocumentProperties.is())
2517 m_xDocumentProperties->setKeywords(comphelper::string::convertCommaSeparated(
2518 m_aStates.top().pDestinationText->makeStringAndClear()));
2519 break;
2520 case Destination::COMMENT:
2521 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2522 break; // not for nested group
2523 if (m_xDocumentProperties.is())
2524 m_xDocumentProperties->setGenerator(
2525 m_aStates.top().pDestinationText->makeStringAndClear());
2526 break;
2527 case Destination::SUBJECT:
2528 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2529 break; // not for nested group
2530 if (m_xDocumentProperties.is())
2531 m_xDocumentProperties->setSubject(
2532 m_aStates.top().pDestinationText->makeStringAndClear());
2533 break;
2534 case Destination::TITLE:
2536 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2537 break; // not for nested group
2538 if (m_xDocumentProperties.is())
2539 m_xDocumentProperties->setTitle(aState.pDestinationText->makeStringAndClear());
2541 break;
2543 case Destination::DOCCOMM:
2544 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2545 break; // not for nested group
2546 if (m_xDocumentProperties.is())
2547 m_xDocumentProperties->setDescription(
2548 m_aStates.top().pDestinationText->makeStringAndClear());
2549 break;
2550 case Destination::OPERATOR:
2551 case Destination::COMPANY:
2553 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2554 break; // not for nested group
2555 OUString aName = aState.eDestination == Destination::OPERATOR ? OUString("Operator")
2556 : OUString("Company");
2557 uno::Any aValue = uno::makeAny(m_aStates.top().pDestinationText->makeStringAndClear());
2558 if (m_xDocumentProperties.is())
2560 uno::Reference<beans::XPropertyContainer> xUserDefinedProperties
2561 = m_xDocumentProperties->getUserDefinedProperties();
2562 uno::Reference<beans::XPropertySet> xPropertySet(xUserDefinedProperties,
2563 uno::UNO_QUERY);
2564 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo
2565 = xPropertySet->getPropertySetInfo();
2566 if (xPropertySetInfo->hasPropertyByName(aName))
2567 xPropertySet->setPropertyValue(aName, aValue);
2568 else
2569 xUserDefinedProperties->addProperty(aName, beans::PropertyAttribute::REMOVABLE,
2570 aValue);
2573 break;
2574 case Destination::OBJDATA:
2576 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2577 break; // not for nested group
2579 RTFError eError = handleEmbeddedObject();
2580 if (eError != RTFError::OK)
2581 return eError;
2583 break;
2584 case Destination::OBJCLASS:
2586 auto pValue = new RTFValue(m_aStates.top().pDestinationText->makeStringAndClear());
2587 m_aOLEAttributes.set(NS_ooxml::LN_CT_OLEObject_ProgID, pValue);
2588 break;
2590 case Destination::OBJECT:
2592 if (!m_bObject)
2594 // if the object is in a special container we will use the \result
2595 // element instead of the \objdata
2596 // (see RTF_OBJECT in RTFDocumentImpl::dispatchDestination)
2597 break;
2600 RTFSprms aObjectSprms;
2601 auto pOLEValue = new RTFValue(m_aOLEAttributes);
2602 aObjectSprms.set(NS_ooxml::LN_OLEObject_OLEObject, pOLEValue);
2604 RTFSprms aObjAttributes;
2605 RTFSprms aObjSprms;
2606 auto pValue = new RTFValue(m_aObjectAttributes, aObjectSprms);
2607 aObjSprms.set(NS_ooxml::LN_object, pValue);
2608 writerfilter::Reference<Properties>::Pointer_t pProperties
2609 = new RTFReferenceProperties(aObjAttributes, aObjSprms);
2610 uno::Reference<drawing::XShape> xShape;
2611 RTFValue::Pointer_t pShape = m_aObjectAttributes.find(NS_ooxml::LN_shape);
2612 OSL_ASSERT(pShape.get());
2613 if (pShape)
2614 pShape->getAny() >>= xShape;
2615 if (xShape.is())
2617 Mapper().startShape(xShape);
2618 Mapper().props(pProperties);
2619 Mapper().endShape();
2621 m_aObjectAttributes.clear();
2622 m_aOLEAttributes.clear();
2623 m_bObject = false;
2625 break;
2626 case Destination::ANNOTATIONDATE:
2628 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2629 break; // not for nested group
2630 OUString aStr(OStringToOUString(
2631 DTTM22OString(m_aStates.top().pDestinationText->makeStringAndClear().toInt32()),
2632 aState.nCurrentEncoding));
2633 auto pValue = new RTFValue(aStr);
2634 RTFSprms aAnnAttributes;
2635 aAnnAttributes.set(NS_ooxml::LN_CT_TrackChange_date, pValue);
2636 writerfilter::Reference<Properties>::Pointer_t pProperties
2637 = new RTFReferenceProperties(aAnnAttributes);
2638 Mapper().props(pProperties);
2640 break;
2641 case Destination::ANNOTATIONAUTHOR:
2642 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2643 break; // not for nested group
2644 m_aAuthor = m_aStates.top().pDestinationText->makeStringAndClear();
2645 break;
2646 case Destination::ATNID:
2647 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2648 break; // not for nested group
2649 m_aAuthorInitials = m_aStates.top().pDestinationText->makeStringAndClear();
2650 break;
2651 case Destination::ANNOTATIONREFERENCESTART:
2652 case Destination::ANNOTATIONREFERENCEEND:
2654 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2655 break; // not for nested group
2656 OUString aStr = m_aStates.top().pDestinationText->makeStringAndClear();
2657 auto pValue = new RTFValue(aStr.toInt32());
2658 RTFSprms aAttributes;
2659 if (aState.eDestination == Destination::ANNOTATIONREFERENCESTART)
2660 aAttributes.set(NS_ooxml::LN_EG_RangeMarkupElements_commentRangeStart, pValue);
2661 else
2662 aAttributes.set(NS_ooxml::LN_EG_RangeMarkupElements_commentRangeEnd, pValue);
2663 writerfilter::Reference<Properties>::Pointer_t pProperties
2664 = new RTFReferenceProperties(aAttributes);
2665 Mapper().props(pProperties);
2667 break;
2668 case Destination::ANNOTATIONREFERENCE:
2670 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2671 break; // not for nested group
2672 OUString aStr = m_aStates.top().pDestinationText->makeStringAndClear();
2673 RTFSprms aAnnAttributes;
2674 aAnnAttributes.set(NS_ooxml::LN_CT_Markup_id, new RTFValue(aStr.toInt32()));
2675 Mapper().props(new RTFReferenceProperties(aAnnAttributes));
2677 break;
2678 case Destination::FALT:
2680 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2681 break; // not for nested group
2682 OUString aStr(m_aStates.top().pDestinationText->makeStringAndClear());
2683 auto pValue = new RTFValue(aStr);
2684 aState.aTableSprms.set(NS_ooxml::LN_CT_Font_altName, pValue);
2686 break;
2687 case Destination::DRAWINGOBJECT:
2688 if (m_aStates.top().aDrawingObject.xShape.is())
2690 RTFDrawingObject& rDrawing = m_aStates.top().aDrawingObject;
2691 uno::Reference<drawing::XShape> xShape(rDrawing.xShape);
2692 uno::Reference<beans::XPropertySet> xPropertySet(rDrawing.xPropertySet);
2694 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY);
2695 bool bTextFrame = xServiceInfo->supportsService("com.sun.star.text.TextFrame");
2697 // The default is certainly not inline, but then what Word supports is just at-character.
2698 xPropertySet->setPropertyValue(
2699 "AnchorType", uno::makeAny(text::TextContentAnchorType_AT_CHARACTER));
2701 if (bTextFrame)
2703 xPropertySet->setPropertyValue("HoriOrientPosition",
2704 uno::makeAny(rDrawing.nLeft));
2705 xPropertySet->setPropertyValue("VertOrientPosition",
2706 uno::makeAny(rDrawing.nTop));
2708 else
2710 xShape->setPosition(awt::Point(rDrawing.nLeft, rDrawing.nTop));
2712 xShape->setSize(awt::Size(rDrawing.nRight, rDrawing.nBottom));
2714 if (rDrawing.bHasLineColor)
2716 uno::Any aLineColor = uno::makeAny(sal_uInt32((rDrawing.nLineColorR << 16)
2717 + (rDrawing.nLineColorG << 8)
2718 + rDrawing.nLineColorB));
2719 uno::Any aLineWidth;
2720 RTFSdrImport::resolveLineColorAndWidth(bTextFrame, xPropertySet, aLineColor,
2721 aLineWidth);
2723 if (rDrawing.bHasFillColor)
2724 xPropertySet->setPropertyValue(
2725 "FillColor", uno::makeAny(sal_uInt32((rDrawing.nFillColorR << 16)
2726 + (rDrawing.nFillColorG << 8)
2727 + rDrawing.nFillColorB)));
2728 else if (!bTextFrame)
2729 // If there is no fill, the Word default is 100% transparency.
2730 xPropertySet->setPropertyValue("FillTransparence",
2731 uno::makeAny(sal_Int32(100)));
2733 RTFSdrImport::resolveFLine(xPropertySet, rDrawing.nFLine);
2735 if (!m_aStates.top().aDrawingObject.bHadShapeText)
2737 Mapper().startShape(xShape);
2739 Mapper().endShape();
2741 break;
2742 case Destination::PICT:
2743 // fdo#79319 ignore picture data if it's really a shape
2744 if (!m_pSdrImport->isFakePict())
2746 resolvePict(true, m_pSdrImport->getCurrentShape());
2748 m_bNeedFinalPar = true;
2749 break;
2750 case Destination::SHAPE:
2751 m_bNeedFinalPar = true;
2752 m_bNeedCr = m_bNeedCrOrig;
2753 if (aState.aFrame.inFrame())
2755 // parBreak() modifies m_aStates.top() so we can't apply resetFrame() directly on aState
2756 resetFrame();
2757 parBreak();
2758 // Save this state for later use, so we only reset frame status only for the first shape inside a frame.
2759 aState = m_aStates.top();
2760 m_bNeedPap = true;
2762 break;
2763 case Destination::MOMATH:
2765 m_aMathBuffer.appendClosingTag(M_TOKEN(oMath));
2767 SvGlobalName aGlobalName(SO3_SM_CLASSID);
2768 comphelper::EmbeddedObjectContainer aContainer;
2769 OUString aName;
2770 uno::Reference<embed::XEmbeddedObject> xObject
2771 = aContainer.CreateEmbeddedObject(aGlobalName.GetByteSequence(), aName);
2772 uno::Reference<util::XCloseable> xComponent(xObject->getComponent(),
2773 uno::UNO_QUERY_THROW);
2774 // gcc4.4 (and 4.3 and possibly older) have a problem with dynamic_cast directly to the target class,
2775 // so help it with an intermediate cast. I'm not sure what exactly the problem is, seems to be unrelated
2776 // to RTLD_GLOBAL, so most probably a gcc bug.
2777 auto& rImport = dynamic_cast<oox::FormulaImportBase&>(
2778 dynamic_cast<SfxBaseModel&>(*xComponent.get()));
2779 rImport.readFormulaOoxml(m_aMathBuffer);
2780 auto pValue = new RTFValue(xObject);
2781 RTFSprms aMathAttributes;
2782 aMathAttributes.set(NS_ooxml::LN_starmath, pValue);
2783 writerfilter::Reference<Properties>::Pointer_t pProperties
2784 = new RTFReferenceProperties(aMathAttributes);
2785 Mapper().props(pProperties);
2786 m_aMathBuffer = oox::formulaimport::XmlStreamBuilder();
2788 break;
2789 case Destination::MR:
2790 lcl_DestinationToMath(m_aStates.top().pDestinationText, m_aMathBuffer, m_bMathNor);
2791 break;
2792 case Destination::MF:
2793 m_aMathBuffer.appendClosingTag(M_TOKEN(f));
2794 break;
2795 case Destination::MFPR:
2796 m_aMathBuffer.appendClosingTag(M_TOKEN(fPr));
2797 break;
2798 case Destination::MCTRLPR:
2799 m_aMathBuffer.appendClosingTag(M_TOKEN(ctrlPr));
2800 break;
2801 case Destination::MNUM:
2802 m_aMathBuffer.appendClosingTag(M_TOKEN(num));
2803 break;
2804 case Destination::MDEN:
2805 m_aMathBuffer.appendClosingTag(M_TOKEN(den));
2806 break;
2807 case Destination::MACC:
2808 m_aMathBuffer.appendClosingTag(M_TOKEN(acc));
2809 break;
2810 case Destination::MACCPR:
2811 m_aMathBuffer.appendClosingTag(M_TOKEN(accPr));
2812 break;
2813 case Destination::MCHR:
2814 case Destination::MPOS:
2815 case Destination::MVERTJC:
2816 case Destination::MSTRIKEH:
2817 case Destination::MDEGHIDE:
2818 case Destination::MBEGCHR:
2819 case Destination::MSEPCHR:
2820 case Destination::MENDCHR:
2821 case Destination::MSUBHIDE:
2822 case Destination::MSUPHIDE:
2823 case Destination::MTYPE:
2824 case Destination::MGROW:
2826 sal_Int32 nMathToken = 0;
2827 switch (aState.eDestination)
2829 case Destination::MCHR:
2830 nMathToken = M_TOKEN(chr);
2831 break;
2832 case Destination::MPOS:
2833 nMathToken = M_TOKEN(pos);
2834 break;
2835 case Destination::MVERTJC:
2836 nMathToken = M_TOKEN(vertJc);
2837 break;
2838 case Destination::MSTRIKEH:
2839 nMathToken = M_TOKEN(strikeH);
2840 break;
2841 case Destination::MDEGHIDE:
2842 nMathToken = M_TOKEN(degHide);
2843 break;
2844 case Destination::MBEGCHR:
2845 nMathToken = M_TOKEN(begChr);
2846 break;
2847 case Destination::MSEPCHR:
2848 nMathToken = M_TOKEN(sepChr);
2849 break;
2850 case Destination::MENDCHR:
2851 nMathToken = M_TOKEN(endChr);
2852 break;
2853 case Destination::MSUBHIDE:
2854 nMathToken = M_TOKEN(subHide);
2855 break;
2856 case Destination::MSUPHIDE:
2857 nMathToken = M_TOKEN(supHide);
2858 break;
2859 case Destination::MTYPE:
2860 nMathToken = M_TOKEN(type);
2861 break;
2862 case Destination::MGROW:
2863 nMathToken = M_TOKEN(grow);
2864 break;
2865 default:
2866 break;
2869 oox::formulaimport::XmlStream::AttributeList aAttribs;
2870 aAttribs[M_TOKEN(val)] = m_aStates.top().pDestinationText->makeStringAndClear();
2871 m_aMathBuffer.appendOpeningTag(nMathToken, aAttribs);
2872 m_aMathBuffer.appendClosingTag(nMathToken);
2874 break;
2875 case Destination::ME:
2876 m_aMathBuffer.appendClosingTag(M_TOKEN(e));
2877 break;
2878 case Destination::MBAR:
2879 m_aMathBuffer.appendClosingTag(M_TOKEN(bar));
2880 break;
2881 case Destination::MBARPR:
2882 m_aMathBuffer.appendClosingTag(M_TOKEN(barPr));
2883 break;
2884 case Destination::MD:
2885 m_aMathBuffer.appendClosingTag(M_TOKEN(d));
2886 break;
2887 case Destination::MDPR:
2888 m_aMathBuffer.appendClosingTag(M_TOKEN(dPr));
2889 break;
2890 case Destination::MFUNC:
2891 m_aMathBuffer.appendClosingTag(M_TOKEN(func));
2892 break;
2893 case Destination::MFUNCPR:
2894 m_aMathBuffer.appendClosingTag(M_TOKEN(funcPr));
2895 break;
2896 case Destination::MFNAME:
2897 m_aMathBuffer.appendClosingTag(M_TOKEN(fName));
2898 break;
2899 case Destination::MLIMLOW:
2900 m_aMathBuffer.appendClosingTag(M_TOKEN(limLow));
2901 break;
2902 case Destination::MLIMLOWPR:
2903 m_aMathBuffer.appendClosingTag(M_TOKEN(limLowPr));
2904 break;
2905 case Destination::MLIM:
2906 m_aMathBuffer.appendClosingTag(M_TOKEN(lim));
2907 break;
2908 case Destination::MM:
2909 m_aMathBuffer.appendClosingTag(M_TOKEN(m));
2910 break;
2911 case Destination::MMPR:
2912 m_aMathBuffer.appendClosingTag(M_TOKEN(mPr));
2913 break;
2914 case Destination::MMR:
2915 m_aMathBuffer.appendClosingTag(M_TOKEN(mr));
2916 break;
2917 case Destination::MNARY:
2918 m_aMathBuffer.appendClosingTag(M_TOKEN(nary));
2919 break;
2920 case Destination::MNARYPR:
2921 m_aMathBuffer.appendClosingTag(M_TOKEN(naryPr));
2922 break;
2923 case Destination::MSUB:
2924 m_aMathBuffer.appendClosingTag(M_TOKEN(sub));
2925 break;
2926 case Destination::MSUP:
2927 m_aMathBuffer.appendClosingTag(M_TOKEN(sup));
2928 break;
2929 case Destination::MLIMUPP:
2930 m_aMathBuffer.appendClosingTag(M_TOKEN(limUpp));
2931 break;
2932 case Destination::MLIMUPPPR:
2933 m_aMathBuffer.appendClosingTag(M_TOKEN(limUppPr));
2934 break;
2935 case Destination::MGROUPCHR:
2936 m_aMathBuffer.appendClosingTag(M_TOKEN(groupChr));
2937 break;
2938 case Destination::MGROUPCHRPR:
2939 m_aMathBuffer.appendClosingTag(M_TOKEN(groupChrPr));
2940 break;
2941 case Destination::MBORDERBOX:
2942 m_aMathBuffer.appendClosingTag(M_TOKEN(borderBox));
2943 break;
2944 case Destination::MBORDERBOXPR:
2945 m_aMathBuffer.appendClosingTag(M_TOKEN(borderBoxPr));
2946 break;
2947 case Destination::MRAD:
2948 m_aMathBuffer.appendClosingTag(M_TOKEN(rad));
2949 break;
2950 case Destination::MRADPR:
2951 m_aMathBuffer.appendClosingTag(M_TOKEN(radPr));
2952 break;
2953 case Destination::MDEG:
2954 m_aMathBuffer.appendClosingTag(M_TOKEN(deg));
2955 break;
2956 case Destination::MSSUB:
2957 m_aMathBuffer.appendClosingTag(M_TOKEN(sSub));
2958 break;
2959 case Destination::MSSUBPR:
2960 m_aMathBuffer.appendClosingTag(M_TOKEN(sSubPr));
2961 break;
2962 case Destination::MSSUP:
2963 m_aMathBuffer.appendClosingTag(M_TOKEN(sSup));
2964 break;
2965 case Destination::MSSUPPR:
2966 m_aMathBuffer.appendClosingTag(M_TOKEN(sSupPr));
2967 break;
2968 case Destination::MSSUBSUP:
2969 m_aMathBuffer.appendClosingTag(M_TOKEN(sSubSup));
2970 break;
2971 case Destination::MSSUBSUPPR:
2972 m_aMathBuffer.appendClosingTag(M_TOKEN(sSubSupPr));
2973 break;
2974 case Destination::MSPRE:
2975 m_aMathBuffer.appendClosingTag(M_TOKEN(sPre));
2976 break;
2977 case Destination::MSPREPR:
2978 m_aMathBuffer.appendClosingTag(M_TOKEN(sPrePr));
2979 break;
2980 case Destination::MBOX:
2981 m_aMathBuffer.appendClosingTag(M_TOKEN(box));
2982 break;
2983 case Destination::MEQARR:
2984 m_aMathBuffer.appendClosingTag(M_TOKEN(eqArr));
2985 break;
2986 case Destination::SHAPEGROUP:
2987 if (aState.bCreatedShapeGroup)
2988 m_pSdrImport->popParent();
2989 break;
2990 case Destination::PROPNAME:
2991 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2992 break; // not for nested group
2993 aState.aPropName = m_aStates.top().pDestinationText->makeStringAndClear();
2994 break;
2995 case Destination::STATICVAL:
2996 if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText)
2997 break; // not for nested group
2998 if (m_xDocumentProperties.is())
3000 // Find out what is the key, value type and value we want to set.
3001 uno::Reference<beans::XPropertyContainer> xPropertyContainer
3002 = m_xDocumentProperties->getUserDefinedProperties();
3003 const OUString& rKey = m_aStates.top().aPropName;
3004 OUString aStaticVal = m_aStates.top().pDestinationText->makeStringAndClear();
3005 uno::Any aAny;
3006 if (m_aStates.top().aPropType == cppu::UnoType<OUString>::get())
3007 aAny <<= aStaticVal;
3008 else if (m_aStates.top().aPropType == cppu::UnoType<sal_Int32>::get())
3009 aAny <<= aStaticVal.toInt32();
3010 else if (m_aStates.top().aPropType == cppu::UnoType<bool>::get())
3011 aAny <<= aStaticVal.toBoolean();
3012 else if (m_aStates.top().aPropType == cppu::UnoType<util::DateTime>::get())
3013 aAny <<= getDateTimeFromUserProp(aStaticVal);
3014 else if (m_aStates.top().aPropType == cppu::UnoType<double>::get())
3015 aAny <<= aStaticVal.toDouble();
3017 xPropertyContainer->addProperty(rKey, beans::PropertyAttribute::REMOVABLE, aAny);
3019 break;
3020 case Destination::USERPROPS:
3022 // These are the imported properties.
3023 uno::Reference<document::XDocumentProperties> xDocumentProperties
3024 = m_xDocumentProperties;
3026 // These are the real document properties.
3027 uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier(
3028 m_xDstDoc, uno::UNO_QUERY);
3029 if (xDocumentPropertiesSupplier.is())
3030 m_xDocumentProperties.set(xDocumentPropertiesSupplier->getDocumentProperties(),
3031 uno::UNO_QUERY);
3033 if (m_xDocumentProperties.is())
3035 if (!m_bIsNewDoc)
3037 // Check classification.
3038 if (!SfxClassificationHelper::ShowPasteInfo(SfxClassificationHelper::CheckPaste(
3039 xDocumentProperties, m_xDocumentProperties)))
3040 return RTFError::CLASSIFICATION;
3043 uno::Reference<beans::XPropertyContainer> xClipboardPropertyContainer
3044 = xDocumentProperties->getUserDefinedProperties();
3045 uno::Reference<beans::XPropertyContainer> xDocumentPropertyContainer
3046 = m_xDocumentProperties->getUserDefinedProperties();
3047 uno::Reference<beans::XPropertySet> xClipboardPropertySet(
3048 xClipboardPropertyContainer, uno::UNO_QUERY);
3049 uno::Reference<beans::XPropertySet> xDocumentPropertySet(xDocumentPropertyContainer,
3050 uno::UNO_QUERY);
3051 uno::Sequence<beans::Property> aClipboardProperties
3052 = xClipboardPropertySet->getPropertySetInfo()->getProperties();
3053 uno::Sequence<beans::Property> aDocumentProperties
3054 = xDocumentPropertySet->getPropertySetInfo()->getProperties();
3056 for (const beans::Property& rProperty : aClipboardProperties)
3058 const OUString& rKey = rProperty.Name;
3059 uno::Any aValue = xClipboardPropertySet->getPropertyValue(rKey);
3063 if (lcl_containsProperty(aDocumentProperties, rKey))
3065 // When pasting, don't update existing properties.
3066 if (!m_bIsNewDoc)
3067 xDocumentPropertySet->setPropertyValue(rKey, aValue);
3069 else
3070 xDocumentPropertyContainer->addProperty(
3071 rKey, beans::PropertyAttribute::REMOVABLE, aValue);
3073 catch (const uno::Exception& rException)
3075 SAL_WARN("writerfilter.rtf",
3076 "failed to set property " << rKey << ": " << rException);
3081 break;
3082 default:
3083 break;
3086 // See if we need to end a track change
3087 if (aState.bStartedTrackchange)
3089 RTFSprms aTCSprms;
3090 auto pValue = new RTFValue(0);
3091 aTCSprms.set(NS_ooxml::LN_endtrackchange, pValue);
3092 if (!m_aStates.top().pCurrentBuffer)
3093 Mapper().props(new RTFReferenceProperties(RTFSprms(), aTCSprms));
3094 else
3095 bufferProperties(*m_aStates.top().pCurrentBuffer, new RTFValue(RTFSprms(), aTCSprms),
3096 nullptr);
3099 // This is the end of the doc, see if we need to close the last section.
3100 if (m_pTokenizer->getGroup() == 1 && !m_bFirstRun)
3102 // \par means an empty paragraph at the end of footnotes/endnotes, but
3103 // not in case of other substreams, like headers.
3104 if (m_bNeedCr
3105 && !(m_nStreamType == NS_ooxml::LN_footnote || m_nStreamType == NS_ooxml::LN_endnote)
3106 && m_bIsNewDoc)
3107 dispatchSymbol(RTF_PAR);
3108 if (m_bNeedSect) // may be set by dispatchSymbol above!
3109 sectBreak(true);
3112 m_aStates.pop();
3114 m_pTokenizer->popGroup();
3116 // list table
3117 switch (aState.eDestination)
3119 case Destination::LISTENTRY:
3121 auto pValue = new RTFValue(aState.aTableAttributes, aState.aTableSprms);
3122 m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_abstractNum, pValue,
3123 RTFOverwrite::NO_APPEND);
3124 m_aListTable[aState.nCurrentListIndex] = pValue;
3125 m_nListLevel = -1;
3126 m_aInvalidListTableFirstIndents[aState.nCurrentListIndex]
3127 = m_aInvalidListLevelFirstIndents;
3128 m_aInvalidListLevelFirstIndents.clear();
3130 break;
3131 case Destination::PARAGRAPHNUMBERING:
3133 RTFValue::Pointer_t pIdValue
3134 = aState.aTableAttributes.find(NS_ooxml::LN_CT_AbstractNum_nsid);
3135 if (pIdValue.get() && !m_aStates.empty())
3137 // Abstract numbering
3138 RTFSprms aLeveltextAttributes;
3139 OUString aTextValue;
3140 RTFValue::Pointer_t pTextBefore
3141 = aState.aTableAttributes.find(NS_ooxml::LN_CT_LevelText_val);
3142 if (pTextBefore)
3143 aTextValue += pTextBefore->getString();
3144 aTextValue += "%1";
3145 RTFValue::Pointer_t pTextAfter
3146 = aState.aTableAttributes.find(NS_ooxml::LN_CT_LevelSuffix_val);
3147 if (pTextAfter)
3148 aTextValue += pTextAfter->getString();
3149 auto pTextValue = new RTFValue(aTextValue);
3150 aLeveltextAttributes.set(NS_ooxml::LN_CT_LevelText_val, pTextValue);
3152 RTFSprms aLevelAttributes;
3153 RTFSprms aLevelSprms;
3154 auto pIlvlValue = new RTFValue(0);
3155 aLevelAttributes.set(NS_ooxml::LN_CT_Lvl_ilvl, pIlvlValue);
3157 RTFValue::Pointer_t pFmtValue = aState.aTableSprms.find(NS_ooxml::LN_CT_Lvl_numFmt);
3158 if (pFmtValue)
3159 aLevelSprms.set(NS_ooxml::LN_CT_Lvl_numFmt, pFmtValue);
3161 RTFValue::Pointer_t pStartatValue
3162 = aState.aTableSprms.find(NS_ooxml::LN_CT_Lvl_start);
3163 if (pStartatValue)
3164 aLevelSprms.set(NS_ooxml::LN_CT_Lvl_start, pStartatValue);
3166 auto pLeveltextValue = new RTFValue(aLeveltextAttributes);
3167 aLevelSprms.set(NS_ooxml::LN_CT_Lvl_lvlText, pLeveltextValue);
3168 RTFValue::Pointer_t pRunProps = aState.aTableSprms.find(NS_ooxml::LN_CT_Lvl_rPr);
3169 if (pRunProps)
3170 aLevelSprms.set(NS_ooxml::LN_CT_Lvl_rPr, pRunProps);
3172 RTFSprms aAbstractAttributes;
3173 RTFSprms aAbstractSprms;
3174 aAbstractAttributes.set(NS_ooxml::LN_CT_AbstractNum_abstractNumId, pIdValue);
3175 auto pLevelValue = new RTFValue(aLevelAttributes, aLevelSprms);
3176 aAbstractSprms.set(NS_ooxml::LN_CT_AbstractNum_lvl, pLevelValue,
3177 RTFOverwrite::NO_APPEND);
3179 RTFSprms aListTableSprms;
3180 auto pAbstractValue = new RTFValue(aAbstractAttributes, aAbstractSprms);
3181 // It's important that Numbering_abstractNum and Numbering_num never overwrites previous values.
3182 aListTableSprms.set(NS_ooxml::LN_CT_Numbering_abstractNum, pAbstractValue,
3183 RTFOverwrite::NO_APPEND);
3185 // Numbering
3186 RTFSprms aNumberingAttributes;
3187 RTFSprms aNumberingSprms;
3188 aNumberingAttributes.set(NS_ooxml::LN_CT_AbstractNum_nsid, pIdValue);
3189 aNumberingSprms.set(NS_ooxml::LN_CT_Num_abstractNumId, pIdValue);
3190 auto pNumberingValue = new RTFValue(aNumberingAttributes, aNumberingSprms);
3191 aListTableSprms.set(NS_ooxml::LN_CT_Numbering_num, pNumberingValue,
3192 RTFOverwrite::NO_APPEND);
3194 // Table
3195 RTFSprms aListTableAttributes;
3196 writerfilter::Reference<Properties>::Pointer_t pProp
3197 = new RTFReferenceProperties(aListTableAttributes, aListTableSprms);
3199 RTFReferenceTable::Entries_t aListTableEntries;
3200 aListTableEntries.insert(std::make_pair(0, pProp));
3201 writerfilter::Reference<Table>::Pointer_t const pTable(
3202 new RTFReferenceTable(aListTableEntries));
3203 Mapper().table(NS_ooxml::LN_NUMBERING, pTable);
3205 // Use it
3206 putNestedSprm(m_aStates.top().aParagraphSprms, NS_ooxml::LN_CT_PPrBase_numPr,
3207 NS_ooxml::LN_CT_NumPr_ilvl, pIlvlValue);
3208 putNestedSprm(m_aStates.top().aParagraphSprms, NS_ooxml::LN_CT_PPrBase_numPr,
3209 NS_ooxml::LN_CT_NumPr_numId, pIdValue);
3212 break;
3213 case Destination::PARAGRAPHNUMBERING_TEXTAFTER:
3214 if (!m_aStates.empty())
3216 // FIXME: don't use pDestinationText, points to popped state
3217 auto pValue = new RTFValue(aState.aDestinationText.makeStringAndClear(), true);
3218 m_aStates.top().aTableAttributes.set(NS_ooxml::LN_CT_LevelSuffix_val, pValue);
3220 break;
3221 case Destination::PARAGRAPHNUMBERING_TEXTBEFORE:
3222 if (!m_aStates.empty())
3224 // FIXME: don't use pDestinationText, points to popped state
3225 auto pValue = new RTFValue(aState.aDestinationText.makeStringAndClear(), true);
3226 m_aStates.top().aTableAttributes.set(NS_ooxml::LN_CT_LevelText_val, pValue);
3228 break;
3229 case Destination::LISTNAME:
3230 break;
3231 case Destination::LISTLEVEL:
3232 if (!m_aStates.empty())
3234 auto pInnerValue = new RTFValue(m_aStates.top().nListLevelNum++);
3235 aState.aTableAttributes.set(NS_ooxml::LN_CT_Lvl_ilvl, pInnerValue);
3237 auto pValue = new RTFValue(aState.aTableAttributes, aState.aTableSprms);
3238 if (m_aStates.top().eDestination != Destination::LFOLEVEL)
3239 m_aStates.top().aListLevelEntries.set(NS_ooxml::LN_CT_AbstractNum_lvl, pValue,
3240 RTFOverwrite::NO_APPEND);
3241 else
3242 m_aStates.top().aTableSprms.set(NS_ooxml::LN_CT_NumLvl_lvl, pValue);
3244 break;
3245 case Destination::LFOLEVEL:
3246 if (!m_aStates.empty())
3248 auto pInnerValue = new RTFValue(m_aStates.top().nListLevelNum++);
3249 aState.aTableAttributes.set(NS_ooxml::LN_CT_NumLvl_ilvl, pInnerValue);
3251 auto pValue = new RTFValue(aState.aTableAttributes, aState.aTableSprms);
3252 m_aStates.top().aTableSprms.set(NS_ooxml::LN_CT_Num_lvlOverride, pValue,
3253 RTFOverwrite::NO_APPEND);
3255 break;
3256 // list override table
3257 case Destination::LISTOVERRIDEENTRY:
3258 if (!m_aStates.empty())
3260 if (m_aStates.top().eDestination == Destination::LISTOVERRIDEENTRY)
3262 // copy properties upwards so upper popState() inserts it
3263 m_aStates.top().aTableAttributes = aState.aTableAttributes;
3264 m_aStates.top().aTableSprms = aState.aTableSprms;
3266 else
3268 auto pValue = new RTFValue(aState.aTableAttributes, aState.aTableSprms);
3269 m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_num, pValue,
3270 RTFOverwrite::NO_APPEND);
3271 m_aListOverrideTable[aState.nCurrentListOverrideIndex]
3272 = aState.nCurrentListIndex;
3275 break;
3276 case Destination::LEVELTEXT:
3277 if (!m_aStates.empty())
3279 auto pValue = new RTFValue(aState.aTableAttributes);
3280 m_aStates.top().aTableSprms.set(NS_ooxml::LN_CT_Lvl_lvlText, pValue);
3282 break;
3283 case Destination::LEVELNUMBERS:
3284 if (!m_aStates.empty())
3286 m_aStates.top().aTableSprms = aState.aTableSprms;
3287 if (m_aStates.top().eDestination == Destination::LEVELNUMBERS
3288 || m_aStates.top().eDestination == Destination::LISTLEVEL)
3289 // Parent state is level number or list level, current state is
3290 // level numbers: mark parent as invalid as well if necessary.
3291 m_aStates.top().bLevelNumbersValid = aState.bLevelNumbersValid;
3293 break;
3294 case Destination::FIELDINSTRUCTION:
3295 if (!m_aStates.empty())
3296 m_aStates.top().eFieldStatus = RTFFieldStatus::INSTRUCTION;
3297 break;
3298 case Destination::FIELDRESULT:
3299 if (!m_aStates.empty())
3300 m_aStates.top().eFieldStatus = RTFFieldStatus::RESULT;
3301 break;
3302 case Destination::FIELD:
3303 if (aState.eFieldStatus == RTFFieldStatus::INSTRUCTION)
3304 singleChar(cFieldEnd);
3305 break;
3306 case Destination::SHAPEPROPERTYVALUEPICT:
3307 if (!m_aStates.empty())
3309 m_aStates.top().aPicture = aState.aPicture;
3310 // both \sp and \sv are destinations, copy the text up-ward for later
3311 m_aStates.top().aDestinationText = aState.aDestinationText;
3313 break;
3314 case Destination::FALT:
3315 if (!m_aStates.empty())
3316 m_aStates.top().aTableSprms = aState.aTableSprms;
3317 break;
3318 case Destination::SHAPEPROPERTYNAME:
3319 case Destination::SHAPEPROPERTYVALUE:
3320 case Destination::SHAPEPROPERTY:
3321 if (!m_aStates.empty())
3323 m_aStates.top().aShape = aState.aShape;
3324 m_aStates.top().aPicture = aState.aPicture;
3325 m_aStates.top().aCharacterAttributes = aState.aCharacterAttributes;
3327 break;
3328 case Destination::SHAPEINSTRUCTION:
3329 if (!m_aStates.empty() && m_aStates.top().eDestination == Destination::SHAPEINSTRUCTION)
3331 // Shape instruction inside other shape instruction: just copy new shape settings:
3332 // it will be resolved on end of topmost shape instruction destination
3333 m_aStates.top().aShape = aState.aShape;
3334 m_aStates.top().aPicture = aState.aPicture;
3335 m_aStates.top().aCharacterSprms = aState.aCharacterSprms;
3336 m_aStates.top().aCharacterAttributes = aState.aCharacterAttributes;
3338 break;
3339 case Destination::FLYMAINCONTENT:
3340 case Destination::SHPPICT:
3341 case Destination::SHAPE:
3342 if (!m_aStates.empty())
3344 m_aStates.top().aFrame = aState.aFrame;
3345 if (aState.eDestination == Destination::SHPPICT
3346 && m_aStates.top().eDestination == Destination::LISTPICTURE)
3348 RTFSprms aAttributes;
3349 aAttributes.set(NS_ooxml::LN_CT_NumPicBullet_numPicBulletId,
3350 new RTFValue(m_nListPictureId++));
3351 RTFSprms aSprms;
3352 // Dummy value, real picture is already sent to dmapper.
3353 aSprms.set(NS_ooxml::LN_CT_NumPicBullet_pict, new RTFValue(0));
3354 auto pValue = new RTFValue(aAttributes, aSprms);
3355 m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_numPicBullet, pValue,
3356 RTFOverwrite::NO_APPEND);
3359 break;
3360 case Destination::SHAPETEXT:
3361 if (!m_aStates.empty())
3363 // If we're leaving the shapetext group (it may have nested ones) and this is a shape, not an old drawingobject.
3364 if (m_aStates.top().eDestination != Destination::SHAPETEXT
3365 && !m_aStates.top().aDrawingObject.bHadShapeText)
3367 m_aStates.top().bHadShapeText = true;
3368 if (!m_aStates.top().pCurrentBuffer)
3369 m_pSdrImport->close();
3370 else
3371 m_aStates.top().pCurrentBuffer->push_back(
3372 Buf_t(BUFFER_ENDSHAPE, nullptr, nullptr));
3375 // It's allowed to declare these inside the shape text, and they
3376 // are expected to have an effect for the whole shape.
3377 if (aState.aDrawingObject.nLeft)
3378 m_aStates.top().aDrawingObject.nLeft = aState.aDrawingObject.nLeft;
3379 if (aState.aDrawingObject.nTop)
3380 m_aStates.top().aDrawingObject.nTop = aState.aDrawingObject.nTop;
3381 if (aState.aDrawingObject.nRight)
3382 m_aStates.top().aDrawingObject.nRight = aState.aDrawingObject.nRight;
3383 if (aState.aDrawingObject.nBottom)
3384 m_aStates.top().aDrawingObject.nBottom = aState.aDrawingObject.nBottom;
3386 break;
3387 case Destination::PROPNAME:
3388 if (m_aStates.top().eDestination == Destination::USERPROPS)
3389 m_aStates.top().aPropName = aState.aPropName;
3390 break;
3391 default:
3393 if (!m_aStates.empty() && m_aStates.top().eDestination == Destination::PICT)
3394 m_aStates.top().aPicture = aState.aPicture;
3396 break;
3399 if (aState.pCurrentBuffer == &m_aSuperBuffer)
3401 OSL_ASSERT(!m_aStates.empty() && m_aStates.top().pCurrentBuffer == nullptr);
3403 if (!m_aSuperBuffer.empty())
3404 replayBuffer(m_aSuperBuffer, nullptr, nullptr);
3407 if (!m_aStates.empty() && m_aStates.top().nTableRowWidthAfter > 0
3408 && aState.nTableRowWidthAfter == 0)
3409 // An RTF_ROW in the inner group already parsed nTableRowWidthAfter,
3410 // don't do it again in the outer state later.
3411 m_aStates.top().nTableRowWidthAfter = 0;
3413 if (m_nResetBreakOnSectBreak != RTF_invalid && !m_aStates.empty())
3415 // Section break type created for \page still has an effect in the
3416 // outer state as well.
3417 RTFValue::Pointer_t pType = aState.aSectionSprms.find(NS_ooxml::LN_EG_SectPrContents_type);
3418 if (pType)
3419 m_aStates.top().aSectionSprms.set(NS_ooxml::LN_EG_SectPrContents_type, pType);
3422 return RTFError::OK;
3425 RTFError RTFDocumentImpl::handleEmbeddedObject()
3427 OString aStr = OUStringToOString(m_aStates.top().pDestinationText->makeStringAndClear(),
3428 RTL_TEXTENCODING_ASCII_US);
3429 std::unique_ptr<SvStream> pStream(new SvMemoryStream());
3430 if (!msfilter::rtfutil::ExtractOLE2FromObjdata(aStr, *pStream))
3431 return RTFError::HEX_INVALID;
3433 uno::Reference<io::XInputStream> xInputStream(
3434 new utl::OSeekableInputStreamWrapper(pStream.release(), /*_bOwner=*/true));
3435 auto pStreamValue = new RTFValue(xInputStream);
3436 m_aOLEAttributes.set(NS_ooxml::LN_inputstream, pStreamValue);
3438 return RTFError::OK;
3441 bool RTFDocumentImpl::isInBackground() { return m_aStates.top().bInBackground; }
3443 RTFInternalState RTFDocumentImpl::getInternalState() { return m_aStates.top().nInternalState; }
3445 void RTFDocumentImpl::setInternalState(RTFInternalState nInternalState)
3447 m_aStates.top().nInternalState = nInternalState;
3450 Destination RTFDocumentImpl::getDestination() { return m_aStates.top().eDestination; }
3452 void RTFDocumentImpl::setDestination(Destination eDestination)
3454 m_aStates.top().eDestination = eDestination;
3457 // this is a questionably named method that is used only in a very special
3458 // situation where it looks like the "current" buffer is needed?
3459 void RTFDocumentImpl::setDestinationText(OUString const& rString)
3461 m_aStates.top().aDestinationText.setLength(0);
3462 m_aStates.top().aDestinationText.append(rString);
3465 bool RTFDocumentImpl::getSkipUnknown() { return m_bSkipUnknown; }
3467 void RTFDocumentImpl::setSkipUnknown(bool bSkipUnknown) { m_bSkipUnknown = bSkipUnknown; }
3469 void RTFDocumentImpl::checkUnicode(bool bUnicode, bool bHex)
3471 if (bUnicode && !m_aUnicodeBuffer.isEmpty())
3473 OUString aString = m_aUnicodeBuffer.makeStringAndClear();
3474 text(aString);
3476 if (bHex && !m_aHexBuffer.isEmpty())
3478 rtl_TextEncoding nEncoding = m_aStates.top().nCurrentEncoding;
3479 if (m_aStates.top().eDestination == Destination::FONTENTRY
3480 && m_aStates.top().nCurrentEncoding == RTL_TEXTENCODING_SYMBOL)
3481 nEncoding = RTL_TEXTENCODING_MS_1252;
3482 OUString aString = OStringToOUString(m_aHexBuffer.makeStringAndClear(), nEncoding);
3483 text(aString);
3487 RTFParserState::RTFParserState(RTFDocumentImpl* pDocumentImpl)
3488 : m_pDocumentImpl(pDocumentImpl)
3489 , nInternalState(RTFInternalState::NORMAL)
3490 , eDestination(Destination::NORMAL)
3491 , eFieldStatus(RTFFieldStatus::NONE)
3492 , nBorderState(RTFBorderState::NONE)
3493 , nCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(0))
3494 , nUc(1)
3495 , nCharsToSkip(0)
3496 , nBinaryToRead(0)
3497 , nListLevelNum(0)
3498 , bLevelNumbersValid(true)
3499 , aFrame(this)
3500 , eRunType(RunType::LOCH)
3501 , isRightToLeft(false)
3502 , nYear(0)
3503 , nMonth(0)
3504 , nDay(0)
3505 , nHour(0)
3506 , nMinute(0)
3507 , pDestinationText(nullptr)
3508 , nCurrentStyleIndex(-1)
3509 , nCurrentCharacterStyleIndex(-1)
3510 , pCurrentBuffer(nullptr)
3511 , bInListpicture(false)
3512 , bInBackground(false)
3513 , bHadShapeText(false)
3514 , bInShapeGroup(false)
3515 , bInShape(false)
3516 , bCreatedShapeGroup(false)
3517 , bStartedTrackchange(false)
3518 , nTableRowWidthAfter(0)
3522 void RTFDocumentImpl::resetFrame() { m_aStates.top().aFrame = RTFFrame(&m_aStates.top()); }
3524 void RTFDocumentImpl::bufferProperties(RTFBuffer_t& rBuffer, const RTFValue::Pointer_t& pValue,
3525 const tools::SvRef<TableRowBuffer>& pTableProperties)
3527 rBuffer.emplace_back(
3528 Buf_t(BUFFER_SETSTYLE, new RTFValue(m_aStates.top().nCurrentStyleIndex), nullptr));
3529 rBuffer.emplace_back(Buf_t(BUFFER_PROPS, pValue, pTableProperties));
3532 RTFPicture::RTFPicture() = default;
3534 RTFShape::RTFShape() = default;
3536 RTFDrawingObject::RTFDrawingObject() = default;
3538 RTFFrame::RTFFrame(RTFParserState* pParserState)
3539 : m_pDocumentImpl(pParserState->m_pDocumentImpl)
3540 , m_nX(0)
3541 , m_nY(0)
3542 , m_nW(0)
3543 , m_nH(0)
3544 , m_nHoriPadding(0)
3545 , m_nVertPadding(0)
3546 , m_nHoriAlign(0)
3547 , m_nHoriAnchor(0)
3548 , m_nVertAlign(0)
3549 , m_nVertAnchor(0)
3550 , m_nHRule(NS_ooxml::LN_Value_doc_ST_HeightRule_auto)
3554 void RTFFrame::setSprm(Id nId, Id nValue)
3556 if (m_pDocumentImpl->getFirstRun() && !m_pDocumentImpl->isStyleSheetImport())
3558 m_pDocumentImpl->checkFirstRun();
3559 m_pDocumentImpl->setNeedPar(false);
3561 switch (nId)
3563 case NS_ooxml::LN_CT_FramePr_w:
3564 m_nW = nValue;
3565 break;
3566 case NS_ooxml::LN_CT_FramePr_h:
3567 m_nH = nValue;
3568 break;
3569 case NS_ooxml::LN_CT_FramePr_x:
3570 m_nX = nValue;
3571 break;
3572 case NS_ooxml::LN_CT_FramePr_y:
3573 m_nY = nValue;
3574 break;
3575 case NS_ooxml::LN_CT_FramePr_hSpace:
3576 m_nHoriPadding = nValue;
3577 break;
3578 case NS_ooxml::LN_CT_FramePr_vSpace:
3579 m_nVertPadding = nValue;
3580 break;
3581 case NS_ooxml::LN_CT_FramePr_xAlign:
3582 m_nHoriAlign = nValue;
3583 break;
3584 case NS_ooxml::LN_CT_FramePr_hAnchor:
3585 m_nHoriAnchor = nValue;
3586 break;
3587 case NS_ooxml::LN_CT_FramePr_yAlign:
3588 m_nVertAlign = nValue;
3589 break;
3590 case NS_ooxml::LN_CT_FramePr_vAnchor:
3591 m_nVertAnchor = nValue;
3592 break;
3593 case NS_ooxml::LN_CT_FramePr_wrap:
3594 m_oWrap = nValue;
3595 break;
3596 default:
3597 break;
3601 RTFSprms RTFFrame::getSprms()
3603 RTFSprms sprms;
3605 static const Id pNames[]
3606 = { NS_ooxml::LN_CT_FramePr_x, NS_ooxml::LN_CT_FramePr_y,
3607 NS_ooxml::LN_CT_FramePr_hRule, // Make sure nHRule is processed before nH
3608 NS_ooxml::LN_CT_FramePr_h, NS_ooxml::LN_CT_FramePr_w,
3609 NS_ooxml::LN_CT_FramePr_hSpace, NS_ooxml::LN_CT_FramePr_vSpace,
3610 NS_ooxml::LN_CT_FramePr_hAnchor, NS_ooxml::LN_CT_FramePr_vAnchor,
3611 NS_ooxml::LN_CT_FramePr_xAlign, NS_ooxml::LN_CT_FramePr_yAlign,
3612 NS_ooxml::LN_CT_FramePr_wrap, NS_ooxml::LN_CT_FramePr_dropCap,
3613 NS_ooxml::LN_CT_FramePr_lines };
3615 for (Id nId : pNames)
3617 RTFValue::Pointer_t pValue;
3619 switch (nId)
3621 case NS_ooxml::LN_CT_FramePr_x:
3622 if (m_nX != 0)
3623 pValue = new RTFValue(m_nX);
3624 break;
3625 case NS_ooxml::LN_CT_FramePr_y:
3626 if (m_nY != 0)
3627 pValue = new RTFValue(m_nY);
3628 break;
3629 case NS_ooxml::LN_CT_FramePr_h:
3630 if (m_nH != 0)
3632 if (m_nHRule == NS_ooxml::LN_Value_doc_ST_HeightRule_exact)
3633 pValue = new RTFValue(-m_nH); // The negative value just sets nHRule
3634 else
3635 pValue = new RTFValue(m_nH);
3637 break;
3638 case NS_ooxml::LN_CT_FramePr_w:
3639 if (m_nW != 0)
3640 pValue = new RTFValue(m_nW);
3641 break;
3642 case NS_ooxml::LN_CT_FramePr_hSpace:
3643 if (m_nHoriPadding != 0)
3644 pValue = new RTFValue(m_nHoriPadding);
3645 break;
3646 case NS_ooxml::LN_CT_FramePr_vSpace:
3647 if (m_nVertPadding != 0)
3648 pValue = new RTFValue(m_nVertPadding);
3649 break;
3650 case NS_ooxml::LN_CT_FramePr_hAnchor:
3652 if (m_nHoriAnchor == 0)
3653 m_nHoriAnchor = NS_ooxml::LN_Value_doc_ST_HAnchor_margin;
3654 pValue = new RTFValue(m_nHoriAnchor);
3656 break;
3657 case NS_ooxml::LN_CT_FramePr_vAnchor:
3659 if (m_nVertAnchor == 0)
3660 m_nVertAnchor = NS_ooxml::LN_Value_doc_ST_VAnchor_margin;
3661 pValue = new RTFValue(m_nVertAnchor);
3663 break;
3664 case NS_ooxml::LN_CT_FramePr_xAlign:
3665 pValue = new RTFValue(m_nHoriAlign);
3666 break;
3667 case NS_ooxml::LN_CT_FramePr_yAlign:
3668 pValue = new RTFValue(m_nVertAlign);
3669 break;
3670 case NS_ooxml::LN_CT_FramePr_hRule:
3672 if (m_nH < 0)
3673 m_nHRule = NS_ooxml::LN_Value_doc_ST_HeightRule_exact;
3674 else if (m_nH > 0)
3675 m_nHRule = NS_ooxml::LN_Value_doc_ST_HeightRule_atLeast;
3676 pValue = new RTFValue(m_nHRule);
3678 break;
3679 case NS_ooxml::LN_CT_FramePr_wrap:
3680 if (m_oWrap)
3681 pValue = new RTFValue(*m_oWrap);
3682 break;
3683 default:
3684 break;
3687 if (pValue)
3688 sprms.set(nId, pValue);
3691 RTFSprms frameprSprms;
3692 frameprSprms.set(NS_ooxml::LN_CT_PPrBase_framePr, new RTFValue(sprms));
3693 return frameprSprms;
3696 bool RTFFrame::hasProperties()
3698 return m_nX != 0 || m_nY != 0 || m_nW != 0 || m_nH != 0 || m_nHoriPadding != 0
3699 || m_nVertPadding != 0 || m_nHoriAlign != 0 || m_nHoriAnchor != 0 || m_nVertAlign != 0
3700 || m_nVertAnchor != 0;
3703 } // namespace rtftok
3704 } // namespace writerfilter
3706 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */