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