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