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