LibreOffice Module writerfilter (master) 1
rtfdocumentimpl.cxx
Go to the documentation of this file.
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
12#include <algorithm>
13#include <memory>
14#include <string_view>
15
16#include <com/sun/star/embed/XEmbeddedObject.hpp>
17#include <com/sun/star/beans/PropertyAttribute.hpp>
18#include <com/sun/star/io/WrongFormatException.hpp>
19#include <com/sun/star/lang/XServiceInfo.hpp>
20#include <com/sun/star/lang/XMultiServiceFactory.hpp>
21#include <com/sun/star/text/TextContentAnchorType.hpp>
22#include <com/sun/star/text/XDependentTextField.hpp>
23#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
24
28#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
31#include <comphelper/string.hxx>
33#include <tools/globname.hxx>
37#include <svl/lngmisc.hxx>
40#include <ooxml/resourceids.hxx>
41#include <oox/token/namespaces.hxx>
43#include <rtl/uri.hxx>
44#include <rtl/tencinfo.h>
45#include <sal/log.hxx>
46#include <osl/diagnose.h>
48#include <vcl/wmfexternal.hxx>
49#include <vcl/graph.hxx>
50#include <vcl/settings.hxx>
51#include <vcl/svapp.hxx>
52#include "rtfsdrimport.hxx"
55#include "rtftokenizer.hxx"
56#include "rtflookahead.hxx"
57#include "rtfcharsets.hxx"
58
59using namespace com::sun::star;
60
61namespace
62{
64util::DateTime getDateTimeFromUserProp(std::u16string_view rString)
65{
66 util::DateTime aRet;
67 size_t nLen = rString.size();
68 if (nLen >= 4)
69 {
70 aRet.Year = o3tl::toInt32(rString.substr(0, 4));
71
72 if (nLen >= 8 && o3tl::starts_with(rString.substr(4), u". "))
73 {
74 aRet.Month = o3tl::toInt32(rString.substr(6, 2));
75
76 if (nLen >= 12 && o3tl::starts_with(rString.substr(8), u". "))
77 aRet.Day = o3tl::toInt32(rString.substr(10, 2));
78 }
79 }
80 return aRet;
81}
82} // anonymous namespace
83
85{
86Id getParagraphBorder(sal_uInt32 nIndex)
87{
88 static const Id aBorderIds[]
89 = { NS_ooxml::LN_CT_PBdr_top, NS_ooxml::LN_CT_PBdr_left, NS_ooxml::LN_CT_PBdr_bottom,
90 NS_ooxml::LN_CT_PBdr_right, NS_ooxml::LN_CT_PBdr_between };
91
92 return aBorderIds[nIndex];
93}
94
95void putNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId, const RTFValue::Pointer_t& pValue,
96 RTFOverwrite eOverwrite, bool bAttribute)
97{
98 RTFValue::Pointer_t pParent = rSprms.find(nParent, /*bFirst=*/true, /*bForWrite=*/true);
99 if (!pParent)
100 {
101 RTFSprms aAttributes;
102 if (nParent == NS_ooxml::LN_CT_TcPrBase_shd)
103 {
104 // RTF default is 'auto', see writerfilter::dmapper::CellColorHandler
105 aAttributes.set(NS_ooxml::LN_CT_Shd_color, new RTFValue(sal_uInt32(COL_AUTO)));
106 aAttributes.set(NS_ooxml::LN_CT_Shd_fill, new RTFValue(sal_uInt32(COL_AUTO)));
107 }
108 auto pParentValue = new RTFValue(aAttributes);
109 rSprms.set(nParent, pParentValue, eOverwrite);
110 pParent = pParentValue;
111 }
112 RTFSprms& rAttributes = (bAttribute ? pParent->getAttributes() : pParent->getSprms());
113 rAttributes.set(nId, pValue, eOverwrite);
114}
115
116void putNestedSprm(RTFSprms& rSprms, Id nParent, Id nId, const RTFValue::Pointer_t& pValue,
117 RTFOverwrite eOverwrite)
118{
119 putNestedAttribute(rSprms, nParent, nId, pValue, eOverwrite, false);
120}
121
123{
124 RTFValue::Pointer_t pParent = rSprms.find(nParent);
125 if (!pParent)
126 return RTFValue::Pointer_t();
127 RTFSprms& rAttributes = pParent->getAttributes();
128 return rAttributes.find(nId);
129}
130
132{
133 RTFValue::Pointer_t pParent = rSprms.find(nParent);
134 if (!pParent)
135 return RTFValue::Pointer_t();
136 RTFSprms& rInner = pParent->getSprms();
137 return rInner.find(nId);
138}
139
140bool eraseNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId)
141{
142 RTFValue::Pointer_t pParent = rSprms.find(nParent);
143 if (!pParent)
144 // It doesn't even have a parent, we're done.
145 return false;
146 RTFSprms& rAttributes = pParent->getAttributes();
147 return rAttributes.erase(nId);
148}
149
151{
152 RTFValue::Pointer_t p = rSprms.find(nId);
153 if (p && !p->getSprms().empty())
154 return p->getSprms().back().second->getAttributes();
155
156 SAL_WARN("writerfilter.rtf", "trying to set property when no type is defined");
157 return rSprms;
158}
159
160void putBorderProperty(RTFStack& aStates, Id nId, const RTFValue::Pointer_t& pValue)
161{
162 RTFSprms* pAttributes = nullptr;
164 for (int i = 0; i < 4; i++)
165 {
167 if (p)
168 {
169 RTFSprms& rAttributes = p->getAttributes();
170 rAttributes.set(nId, pValue);
171 }
172 }
173 else if (aStates.top().getBorderState() == RTFBorderState::CHARACTER)
174 {
175 RTFValue::Pointer_t pPointer
176 = aStates.top().getCharacterSprms().find(NS_ooxml::LN_EG_RPrBase_bdr);
177 if (pPointer)
178 {
179 RTFSprms& rAttributes = pPointer->getAttributes();
180 rAttributes.set(nId, pValue);
181 }
182 }
183 // Attributes of the last border type
184 else if (aStates.top().getBorderState() == RTFBorderState::PARAGRAPH)
185 pAttributes
186 = &getLastAttributes(aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PrBase_pBdr);
187 else if (aStates.top().getBorderState() == RTFBorderState::CELL)
188 pAttributes = &getLastAttributes(aStates.top().getTableCellSprms(),
189 NS_ooxml::LN_CT_TcPrBase_tcBorders);
190 else if (aStates.top().getBorderState() == RTFBorderState::PAGE)
191 pAttributes = &getLastAttributes(aStates.top().getSectionSprms(),
192 NS_ooxml::LN_EG_SectPrContents_pgBorders);
193 else if (aStates.top().getBorderState() == RTFBorderState::NONE)
194 {
195 // this is invalid, but Word apparently clears or overrides all paragraph borders now
196 for (int i = 0; i < 4; ++i)
197 {
198 auto const nBorder = getParagraphBorder(i);
199 RTFSprms aAttributes;
200 RTFSprms aSprms;
201 aAttributes.set(NS_ooxml::LN_CT_Border_val,
202 new RTFValue(NS_ooxml::LN_Value_ST_Border_none));
203 putNestedSprm(aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PrBase_pBdr, nBorder,
204 new RTFValue(aAttributes, aSprms), RTFOverwrite::YES);
205 }
206 }
207
208 if (pAttributes)
209 pAttributes->set(nId, pValue);
210}
211
213{
215}
216
217static RTFSprms lcl_getBookmarkProperties(int nPos, const OUString& rString)
218{
219 RTFSprms aAttributes;
220 auto pPos = new RTFValue(nPos);
221 if (!rString.isEmpty())
222 {
223 // If present, this should be sent first.
224 auto pString = new RTFValue(rString);
225 aAttributes.set(NS_ooxml::LN_CT_Bookmark_name, pString);
226 }
227 aAttributes.set(NS_ooxml::LN_CT_MarkupRangeBookmark_id, pPos);
228 return aAttributes;
229}
230
231const char* keywordToString(RTFKeyword nKeyword)
232{
233 for (int i = 0; i < nRTFControlWords; i++)
234 {
235 if (nKeyword == aRTFControlWords[i].GetIndex())
236 return aRTFControlWords[i].GetKeyword();
237 }
238 return nullptr;
239}
240
241static util::DateTime lcl_getDateTime(RTFParserState const& aState)
242{
243 return { 0 /*100sec*/,
244 0 /*sec*/,
245 aState.getMinute(),
246 aState.getHour(),
247 aState.getDay(),
248 aState.getMonth(),
249 static_cast<sal_Int16>(aState.getYear()),
250 false };
251}
252
253static void lcl_DestinationToMath(OUStringBuffer* pDestinationText,
254 oox::formulaimport::XmlStreamBuilder& rMathBuffer, bool& rMathNor)
255{
256 if (!pDestinationText)
257 return;
258 OUString aStr = pDestinationText->makeStringAndClear();
259 if (aStr.isEmpty())
260 return;
261 rMathBuffer.appendOpeningTag(M_TOKEN(r));
262 if (rMathNor)
263 {
264 rMathBuffer.appendOpeningTag(M_TOKEN(rPr));
265 // Same as M_TOKEN(lit)
266 rMathBuffer.appendOpeningTag(M_TOKEN(nor));
267 rMathBuffer.appendClosingTag(M_TOKEN(nor));
268 rMathBuffer.appendClosingTag(M_TOKEN(rPr));
269 rMathNor = false;
270 }
271 rMathBuffer.appendOpeningTag(M_TOKEN(t));
272 rMathBuffer.appendCharacters(aStr);
273 rMathBuffer.appendClosingTag(M_TOKEN(t));
274 rMathBuffer.appendClosingTag(M_TOKEN(r));
275}
276
278 uno::Reference<io::XInputStream> const& xInputStream,
280 uno::Reference<frame::XFrame> const& xFrame,
281 uno::Reference<task::XStatusIndicator> const& xStatusIndicator,
282 const utl::MediaDescriptor& rMediaDescriptor)
283 : m_xContext(xContext)
284 , m_xInputStream(xInputStream)
285 , m_xDstDoc(xDstDoc)
287 , m_xStatusIndicator(xStatusIndicator)
288 , m_pMapperStream(nullptr)
289 , m_aDefaultState(this)
290 , m_bSkipUnknown(false)
291 , m_bFirstRun(true)
292 , m_bFirstRunException(false)
293 , m_bNeedPap(true)
294 , m_bNeedCr(false)
295 , m_bNeedCrOrig(false)
296 , m_bNeedPar(true)
297 , m_bNeedFinalPar(false)
298 , m_nNestedCells(0)
299 , m_nTopLevelCells(0)
300 , m_nInheritingCells(0)
301 , m_nNestedTRLeft(0)
302 , m_nTopLevelTRLeft(0)
303 , m_nNestedCurrentCellX(0)
304 , m_nTopLevelCurrentCellX(0)
305 , m_nBackupTopLevelCurrentCellX(0)
306 , m_aTableBufferStack(1) // create top-level buffer already
307 , m_pSuperstream(nullptr)
308 , m_nStreamType(0)
309 , m_nGroupStartPos(0)
310 , m_nFormFieldType(RTFFormFieldType::NONE)
311 , m_bObject(false)
312 , m_nCurrentFontIndex(0)
313 , m_nCurrentEncoding(-1)
314 , m_nDefaultFontIndex(-1)
315 , m_pStyleTableEntries(new RTFReferenceTable::Entries_t)
316 , m_nCurrentStyleIndex(0)
317 , m_bFormField(false)
318 , m_bMathNor(false)
319 , m_bIgnoreNextContSectBreak(false)
320 , m_nResetBreakOnSectBreak(RTFKeyword::invalid)
321 , m_bNeedSect(false) // done by checkFirstRun
322 , m_bWasInFrame(false)
323 , m_bHadPicture(false)
324 , m_bHadSect(false)
325 , m_nCellxMax(0)
326 , m_nListPictureId(0)
327 , m_bIsNewDoc(!rMediaDescriptor.getUnpackedValueOrDefault("InsertMode", false))
328 , m_rMediaDescriptor(rMediaDescriptor)
329 , m_hasRHeader(false)
330 , m_hasFHeader(false)
331 , m_hasRFooter(false)
332 , m_hasFFooter(false)
333 , m_bAfterCellBeforeRow(false)
334{
335 OSL_ASSERT(xInputStream.is());
337
338 m_xModelFactory.set(m_xDstDoc, uno::UNO_QUERY);
339
341 m_xDstDoc, uno::UNO_QUERY);
342 if (xDocumentPropertiesSupplier.is())
343 m_xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties();
344
345 m_pGraphicHelper = std::make_shared<oox::GraphicHelper>(m_xContext, xFrame, oox::StorageRef());
346
348 m_pSdrImport = new RTFSdrImport(*this, m_xDstDoc);
349}
350
352
354
356{
357 m_pSuperstream = pSuperstream;
358}
359
360bool RTFDocumentImpl::isSubstream() const { return m_pSuperstream != nullptr; }
361
362void RTFDocumentImpl::finishSubstream() { checkUnicode(/*bUnicode =*/true, /*bHex =*/true); }
363
364void RTFDocumentImpl::resolveSubstream(std::size_t nPos, Id nId)
365{
366 resolveSubstream(nPos, nId, OUString());
367}
368void RTFDocumentImpl::resolveSubstream(std::size_t nPos, Id nId, OUString const& rIgnoreFirst)
369{
370 sal_uInt64 const nCurrent = Strm().Tell();
371 // Seek to header position, parse, then seek back.
374 pImpl->setSuperstream(this);
375 pImpl->m_nStreamType = nId;
376 pImpl->m_aIgnoreFirst = rIgnoreFirst;
377 if (!m_aAuthor.isEmpty())
378 {
379 pImpl->m_aAuthor = m_aAuthor;
380 m_aAuthor.clear();
381 }
382 if (!m_aAuthorInitials.isEmpty())
383 {
384 pImpl->m_aAuthorInitials = m_aAuthorInitials;
385 m_aAuthorInitials.clear();
386 }
387 pImpl->m_nDefaultFontIndex = m_nDefaultFontIndex;
388 pImpl->m_pStyleTableEntries = m_pStyleTableEntries;
389 pImpl->Strm().Seek(nPos);
390 SAL_INFO("writerfilter.rtf", "substream start");
391 Mapper().substream(nId, pImpl);
392 SAL_INFO("writerfilter.rtf", "substream end");
393 Strm().Seek(nCurrent);
394}
395
397{
398 // tdf#136740: do not change target document settings when pasting
399 if (!m_bIsNewDoc)
400 return;
403 RTFReferenceTable::Entries_t aSettingsTableEntries;
404 aSettingsTableEntries.insert(std::make_pair(0, pProp));
406 = new RTFReferenceTable(std::move(aSettingsTableEntries));
407 Mapper().table(NS_ooxml::LN_settings_settings, pTable);
408}
409
411{
412 if (!m_bFirstRun)
413 return;
414
416 // start initial paragraph
417 m_bFirstRun = false;
419 setNeedSect(true); // first call that succeeds
420
421 // set the requested default font, if there are none for each state in stack
423 = getNestedAttribute(m_aDefaultState.getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts,
424 NS_ooxml::LN_CT_Fonts_ascii);
425 if (!pFont)
426 return;
427
428 for (size_t i = 0; i < m_aStates.size(); i++)
429 {
430 RTFValue::Pointer_t pCurrentFont
431 = getNestedAttribute(m_aStates[i].getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts,
432 NS_ooxml::LN_CT_Fonts_ascii);
433 if (!pCurrentFont)
434 putNestedAttribute(m_aStates[i].getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts,
435 NS_ooxml::LN_CT_Fonts_ascii, pFont);
436 }
437}
438
439void RTFDocumentImpl::setNeedPar(bool bNeedPar) { m_bNeedPar = bNeedPar; }
440
442{
443 if (!m_bNeedSect && bNeedSect && m_bFirstRun)
444 {
445 RTFLookahead aLookahead(Strm(), m_pTokenizer->getGroupStart());
446 if (aLookahead.hasTable() && aLookahead.hasColumns())
447 {
449 }
450 }
451
452 // ignore setting before checkFirstRun - every keyword calls setNeedSect!
453 // except the case of a table in a multicolumn section
454 if (!m_bNeedSect && bNeedSect && (!m_bFirstRun || m_bFirstRunException))
455 {
456 if (!m_pSuperstream) // no sections in header/footer!
457 {
459 }
460 // set flag in substream too - otherwise multiple startParagraphGroup
461 m_bNeedSect = bNeedSect;
463 setNeedPar(true);
464 }
465 else if (m_bNeedSect && !bNeedSect)
466 {
467 m_bNeedSect = bNeedSect;
468 }
469}
470
472static void lcl_copyFlatten(RTFReferenceProperties& rProps, RTFSprms& rStyleAttributes,
473 RTFSprms& rStyleSprms)
474{
475 for (auto& rSprm : rProps.getSprms())
476 {
477 // createStyleProperties() puts properties to rPr, but here we need a flat list.
478 if (rSprm.first == NS_ooxml::LN_CT_Style_rPr)
479 {
480 // rPr can have both attributes and SPRMs, copy over both types.
481 RTFSprms& rRPrSprms = rSprm.second->getSprms();
482 for (const auto& rRPrSprm : rRPrSprms)
483 rStyleSprms.set(rRPrSprm.first, rRPrSprm.second);
484
485 RTFSprms& rRPrAttributes = rSprm.second->getAttributes();
486 for (const auto& rRPrAttribute : rRPrAttributes)
487 rStyleAttributes.set(rRPrAttribute.first, rRPrAttribute.second);
488 }
489 else
490 rStyleSprms.set(rSprm.first, rSprm.second);
491 }
492
493 RTFSprms& rAttributes = rProps.getAttributes();
494 for (const auto& rAttribute : rAttributes)
495 rStyleAttributes.set(rAttribute.first, rAttribute.second);
496}
497
499RTFDocumentImpl::getProperties(const RTFSprms& rAttributes, RTFSprms const& rSprms, Id nStyleType)
500{
501 RTFSprms aSprms(rSprms);
502 RTFValue::Pointer_t pAbstractList;
503 int nAbstractListId = -1;
505 = getNestedSprm(aSprms, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_numId);
506 if (pNumId)
507 {
508 // We have a numbering, look up the abstract list for property
509 // deduplication and duplication.
510 auto itNumId = m_aListOverrideTable.find(pNumId->getInt());
511 if (itNumId != m_aListOverrideTable.end())
512 {
513 nAbstractListId = itNumId->second;
514 auto itAbstract = m_aListTable.find(nAbstractListId);
515 if (itAbstract != m_aListTable.end())
516 pAbstractList = itAbstract->second;
517 }
518 }
519
520 if (pAbstractList)
521 {
522 auto it = m_aInvalidListTableFirstIndents.find(nAbstractListId);
523 if (it != m_aInvalidListTableFirstIndents.end())
524 aSprms.deduplicateList(it->second);
525 }
526
527 int nStyle = 0;
528 if (!m_aStates.empty())
530 auto it = m_pStyleTableEntries->find(nStyle);
531 if (it != m_pStyleTableEntries->end())
532 {
533 // cloneAndDeduplicate() wants to know about only a single "style", so
534 // let's merge paragraph and character style properties here.
535 auto itChar = m_pStyleTableEntries->end();
536 if (!m_aStates.empty())
537 {
538 int nCharStyle = m_aStates.top().getCurrentCharacterStyleIndex();
539 itChar = m_pStyleTableEntries->find(nCharStyle);
540 }
541
542 RTFSprms aStyleSprms;
543 RTFSprms aStyleAttributes;
544 // Ensure the paragraph style is a flat list.
545 // Take paragraph style into account for character properties as well,
546 // as paragraph style may contain character properties.
547 RTFReferenceProperties& rProps = *static_cast<RTFReferenceProperties*>(it->second.get());
548 lcl_copyFlatten(rProps, aStyleAttributes, aStyleSprms);
549
550 if (itChar != m_pStyleTableEntries->end())
551 {
552 // Found active character style, then update aStyleSprms/Attributes.
553 if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_character)
554 {
555 RTFReferenceProperties& rCharProps
556 = *static_cast<RTFReferenceProperties*>(itChar->second.get());
557 lcl_copyFlatten(rCharProps, aStyleAttributes, aStyleSprms);
558 }
559 }
560
561 // Get rid of direct formatting what is already in the style.
562 RTFSprms sprms(aSprms.cloneAndDeduplicate(aStyleSprms, nStyleType, true, &aSprms));
563 RTFSprms attributes(rAttributes.cloneAndDeduplicate(aStyleAttributes, nStyleType, true));
564 return new RTFReferenceProperties(std::move(attributes), std::move(sprms));
565 }
566
567 if (pAbstractList)
568 aSprms.duplicateList(pAbstractList);
570 = new RTFReferenceProperties(rAttributes, std::move(aSprms));
571 return pRet;
572}
573
575{
576 if (!m_bNeedPap)
577 return;
578
579 m_bNeedPap = false; // reset early, so we can avoid recursion when calling ourselves
580
581 if (m_aStates.empty())
582 return;
583
585 {
588 NS_ooxml::LN_Value_ST_StyleType_paragraph));
589
590 // Writer will ignore a page break before a text frame, so guard it with empty paragraphs
591 bool hasBreakBeforeFrame
593 && m_aStates.top().getParagraphSprms().find(NS_ooxml::LN_CT_PPrBase_pageBreakBefore);
594 if (hasBreakBeforeFrame)
595 {
597 m_bNeedPap = false;
598 }
599 Mapper().props(pParagraphProperties);
600 if (hasBreakBeforeFrame)
602
604 {
607 Mapper().props(pFrameProperties);
608 }
609 }
610 else
611 {
612 auto pValue = new RTFValue(m_aStates.top().getParagraphAttributes(),
614 bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue, nullptr);
615 }
616}
617
619{
621 {
624 NS_ooxml::LN_Value_ST_StyleType_character);
625 Mapper().props(pProperties);
626 }
627 else
628 {
629 auto pValue = new RTFValue(m_aStates.top().getCharacterAttributes(),
631 bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue, nullptr,
632 NS_ooxml::LN_Value_ST_StyleType_character);
633 }
634
635 // Delete the sprm, so the trackchange range will be started only once.
636 // OTOH set a boolean flag, so we'll know we need to end the range later.
637 RTFValue::Pointer_t pTrackchange
638 = m_aStates.top().getCharacterSprms().find(NS_ooxml::LN_trackchange);
639 if (pTrackchange)
640 {
642 m_aStates.top().getCharacterSprms().erase(NS_ooxml::LN_trackchange);
643 }
644}
645
647{
648 sal_uInt8 const sBreak[] = { 0xd };
649 Mapper().text(sBreak, 1);
650 m_bNeedCr = false;
651}
652
654{
655 runBreak();
658}
659
661{
663 checkNeedPap();
664 // end previous paragraph
666 runBreak();
669
670 m_bHadPicture = false;
671
672 // start new one
674}
675
677{
678 SAL_INFO("writerfilter.rtf", __func__ << ": final? " << bFinal << ", needed? " << m_bNeedSect);
679 bool bNeedSect = m_bNeedSect;
681 = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_type);
682 bool bContinuous = pBreak && pBreak->getInt() == NS_ooxml::LN_Value_ST_SectionMark_continuous;
683 // If there is no paragraph in this section, then insert a dummy one, as required by Writer,
684 // unless this is the end of the doc, we had nothing since the last section break and this is not a continuous one.
685 // Also, when pasting, it's fine to not have any paragraph inside the document at all.
686 if (m_bNeedPar && (!bFinal || m_bNeedSect || bContinuous) && !isSubstream() && m_bIsNewDoc)
688 // It's allowed to not have a non-table paragraph at the end of an RTF doc, add it now if required.
689 if (m_bNeedFinalPar && bFinal)
690 {
693 m_bNeedSect = bNeedSect;
694 }
695 while (!m_nHeaderFooterPositions.empty())
696 {
697 std::pair<Id, std::size_t> aPair = m_nHeaderFooterPositions.front();
699 resolveSubstream(aPair.second, aPair.first);
700 }
701
702 // Normally a section break at the end of the doc is necessary. Unless the
703 // last control word in the document is a section break itself.
704 if (!bNeedSect || !m_bHadSect)
705 {
706 // In case the last section is a continuous one, we don't need to output a section break.
707 if (bFinal && bContinuous)
708 m_aStates.top().getSectionSprms().erase(NS_ooxml::LN_EG_SectPrContents_type);
709 }
710
711 // Section properties are a paragraph sprm.
712 auto pValue
714 RTFSprms aAttributes;
715 RTFSprms aSprms;
716 aSprms.set(NS_ooxml::LN_CT_PPr_sectPr, pValue);
718 = new RTFReferenceProperties(std::move(aAttributes), std::move(aSprms));
719
720 if (bFinal && !m_pSuperstream)
721 // This is the end of the document, not just the end of e.g. a header.
722 // This makes sure that dmapper can set DontBalanceTextColumns=true for this section if necessary.
724
725 // The trick is that we send properties of the previous section right now, which will be exactly what dmapper expects.
726 Mapper().props(pProperties);
728
729 // End Section
730 if (!m_pSuperstream)
731 {
732 m_hasFHeader = false;
733 m_hasRHeader = false;
734 m_hasRFooter = false;
735 m_hasFFooter = false;
737 }
738 m_bNeedPar = false;
739 m_bNeedSect = false;
740}
741
743{
744 if (!m_pSuperstream)
745 {
746 if (nIndex < m_aColorTable.size())
747 return m_aColorTable[nIndex];
748 return 0;
749 }
750
752}
753
754rtl_TextEncoding RTFDocumentImpl::getEncoding(int nFontIndex)
755{
756 if (!m_pSuperstream)
757 {
758 auto it = m_aFontEncodings.find(nFontIndex);
759 if (it != m_aFontEncodings.end())
760 // We have a font encoding associated to this font.
761 return it->second;
762 if (m_aDefaultState.getCurrentEncoding() != rtl_getTextEncodingFromWindowsCharset(0))
763 // We have a default encoding.
765 // Guess based on locale.
767 Application::GetSettings().GetLanguageTag().getLocale());
768 }
769
770 return m_pSuperstream->getEncoding(nFontIndex);
771}
772
774{
775 if (!m_pSuperstream)
776 return m_aFontNames[nIndex];
777
779}
780
782{
783 if (!m_pSuperstream)
784 return std::find(m_aFontIndexes.begin(), m_aFontIndexes.end(), nIndex)
785 - m_aFontIndexes.begin();
786
788}
789
791{
792 if (!m_pSuperstream)
793 {
794 OUString aRet;
795 if (m_aStyleNames.find(nIndex) != m_aStyleNames.end())
796 aRet = m_aStyleNames[nIndex];
797 return aRet;
798 }
799
801}
802
804{
805 if (!m_pSuperstream)
806 {
807 Id nRet = 0;
808 if (m_aStyleTypes.find(nIndex) != m_aStyleTypes.end())
809 nRet = m_aStyleTypes[nIndex];
810 return nRet;
811 }
812
814}
815
817{
818 if (!m_pSuperstream)
819 return m_aDefaultState;
820
822}
823
825
827{
828 if (m_aStates.empty())
829 return false;
830 Destination eDestination = m_aStates.top().getDestination();
831 return eDestination == Destination::STYLESHEET || eDestination == Destination::STYLEENTRY;
832}
833
835{
836 m_pMapperStream = &rMapper;
837 switch (m_pTokenizer->resolveParse())
838 {
839 case RTFError::OK:
840 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: finished without errors");
841 break;
843 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unmatched '}'");
844 break;
846 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unmatched '{'");
847 throw io::WrongFormatException(m_pTokenizer->getPosition());
848 break;
850 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unexpected end of file");
851 throw io::WrongFormatException(m_pTokenizer->getPosition());
852 break;
854 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: invalid hex char");
855 throw io::WrongFormatException(m_pTokenizer->getPosition());
856 break;
858 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: characters after last '}'");
859 break;
861 SAL_INFO("writerfilter.rtf",
862 "RTFDocumentImpl::resolve: classification prevented paste");
863 break;
864 }
865}
866
868{
869 SvMemoryStream aStream;
870 SvStream* pStream = nullptr;
871 if (!m_pBinaryData)
872 {
873 pStream = &aStream;
874 int b = 0;
875 int count = 2;
876
877 // Feed the destination text to a stream.
878 auto& rDestinationTextBuffer = m_aStates.top().getDestinationText();
879 OString aStr = OUStringToOString(rDestinationTextBuffer, RTL_TEXTENCODING_ASCII_US);
880 rDestinationTextBuffer.setLength(0);
881 for (int i = 0; i < aStr.getLength(); ++i)
882 {
883 char ch = aStr[i];
884 if (ch != 0x0d && ch != 0x0a && ch != 0x20)
885 {
886 b = b << 4;
888 if (parsed == -1)
889 return;
890 b += parsed;
891 count--;
892 if (!count)
893 {
894 aStream.WriteChar(static_cast<char>(b));
895 count = 2;
896 b = 0;
897 }
898 }
899 }
900 }
901 else
902 pStream = m_pBinaryData.get();
903
904 if (!pStream->Tell())
905 // No destination text? Then we'll get it later.
906 return;
907
908 SvMemoryStream aDIBStream;
910 {
911 // Construct a BITMAPFILEHEADER structure before the real data.
912 SvStream& rBodyStream = *pStream;
913 aDIBStream.WriteChar('B');
914 aDIBStream.WriteChar('M');
915 // The size of the real data.
916 aDIBStream.WriteUInt32(rBodyStream.Tell());
917 // Reserved.
918 aDIBStream.WriteUInt32(0);
919 // The offset of the real data, i.e. the size of the header, including this number.
920 aDIBStream.WriteUInt32(14);
921 rBodyStream.Seek(0);
922 aDIBStream.WriteStream(rBodyStream);
923 pStream = &aDIBStream;
924 }
925
926 // Store, and get its URL.
927 pStream->Seek(0);
929 WmfExternal aExtHeader;
930 aExtHeader.mapMode = m_aStates.top().getPicture().eWMetafile;
933 {
934 // Don't use the values provided by picw and pich if the desired size is provided.
935
936 aExtHeader.xExt = sal_uInt16(std::clamp<sal_Int32>(
938 SAL_MAX_UINT16)); //TODO: better way to handle out-of-bounds values?
939 aExtHeader.yExt = sal_uInt16(std::clamp<sal_Int32>(
941 SAL_MAX_UINT16)); //TODO: better way to handle out-of-bounds values?
942 }
943 WmfExternal* pExtHeader = &aExtHeader;
945 uno::UNO_QUERY);
946 if (xServiceInfo.is() && xServiceInfo->supportsService("com.sun.star.text.TextFrame"))
947 pExtHeader = nullptr;
948
950 = m_pGraphicHelper->importGraphic(xInputStream, pExtHeader);
951
953 {
954 // In case of PNG/JPEG, the real size is known, don't use the values
955 // provided by picw and pich.
956
957 Graphic aGraphic(xGraphic);
958 Size aSize(aGraphic.GetPrefSize());
959 MapMode aMap(MapUnit::Map100thMM);
960 if (aGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel)
962 else
963 aSize = OutputDevice::LogicToLogic(aSize, aGraphic.GetPrefMapMode(), aMap);
964 m_aStates.top().getPicture().nWidth = aSize.Width();
965 m_aStates.top().getPicture().nHeight = aSize.Height();
966 }
967
968 uno::Reference<drawing::XShape> xShape(rShape);
969 if (m_aStates.top().getInShape() && xShape.is())
970 {
971 awt::Size aSize = xShape->getSize();
972 if (aSize.Width || aSize.Height)
973 {
974 // resolvePict() is processing pib structure inside shape
975 // So if shape has dimensions we should use them instead of
976 // \picwN, \pichN, \picscalexN, \picscaleyN given with picture
977 m_aStates.top().getPicture().nGoalWidth = aSize.Width;
978 m_aStates.top().getPicture().nGoalHeight = aSize.Height;
981 }
982 }
983
984 // Wrap it in an XShape.
985 if (xShape.is())
986 {
987 uno::Reference<lang::XServiceInfo> xSI(xShape, uno::UNO_QUERY_THROW);
988 if (!xSI->supportsService("com.sun.star.drawing.GraphicObjectShape"))
989 {
990 // it's sometimes an error to get here - but it's possible to have
991 // a \pict inside the \shptxt of a \shp of shapeType 202 "TextBox"
992 // and in that case xShape is the text frame; we actually need a
993 // new GraphicObject then (example: fdo37691-1.rtf)
994 SAL_INFO("writerfilter.rtf",
995 "cannot set graphic on existing shape, creating a new GraphicObjectShape");
996 xShape.clear();
997 }
998 }
999 if (!xShape.is())
1000 {
1001 if (m_xModelFactory.is())
1002 xShape.set(m_xModelFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"),
1003 uno::UNO_QUERY);
1004 uno::Reference<drawing::XDrawPageSupplier> const xDrawSupplier(m_xDstDoc, uno::UNO_QUERY);
1005 if (xDrawSupplier.is())
1006 {
1007 uno::Reference<drawing::XShapes> xShapes = xDrawSupplier->getDrawPage();
1008 if (xShapes.is())
1009 xShapes->add(xShape);
1010 }
1011 }
1012
1013 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
1014
1015 if (xPropertySet.is())
1016 xPropertySet->setPropertyValue("Graphic", uno::Any(xGraphic));
1017
1018 // check if the picture is in an OLE object and if the \objdata element is used
1019 // (see RTFKeyword::OBJECT in RTFDocumentImpl::dispatchDestination)
1020 if (m_bObject)
1021 {
1022 // Set the object size
1023 awt::Size aSize;
1024 aSize.Width
1027 aSize.Height
1030 xShape->setSize(aSize);
1031
1032 // Replacement graphic is inline by default, see oox::vml::SimpleShape::implConvertAndInsert().
1033 xPropertySet->setPropertyValue("AnchorType",
1034 uno::Any(text::TextContentAnchorType_AS_CHARACTER));
1035
1036 auto pShapeValue = new RTFValue(xShape);
1037 m_aObjectAttributes.set(NS_ooxml::LN_shape, pShapeValue);
1038 return;
1039 }
1040
1042 {
1043 // Send the shape directly, no section is started, to additional properties will be ignored anyway.
1044 Mapper().startShape(xShape);
1045 Mapper().endShape();
1046 return;
1047 }
1048
1049 // Send it to the dmapper.
1050 RTFSprms aSprms;
1051 RTFSprms aAttributes;
1052 // shape attribute
1053 RTFSprms aPicAttributes;
1054 auto pShapeValue = new RTFValue(xShape);
1055 aPicAttributes.set(NS_ooxml::LN_shape, pShapeValue);
1056 // pic sprm
1057 RTFSprms aGraphicDataAttributes;
1058 RTFSprms aGraphicDataSprms;
1059 auto pPicValue = new RTFValue(aPicAttributes);
1060 aGraphicDataSprms.set(NS_ooxml::LN_pic_pic, pPicValue);
1061 // graphicData sprm
1062 RTFSprms aGraphicAttributes;
1063 RTFSprms aGraphicSprms;
1064 auto pGraphicDataValue = new RTFValue(aGraphicDataAttributes, aGraphicDataSprms);
1065 aGraphicSprms.set(NS_ooxml::LN_CT_GraphicalObject_graphicData, pGraphicDataValue);
1066 // graphic sprm
1067 auto pGraphicValue = new RTFValue(aGraphicAttributes, aGraphicSprms);
1068 // extent sprm
1069 RTFSprms aExtentAttributes;
1074 if (m_aStates.top().getPicture().nScaleX != 100)
1075 nXExt = (static_cast<tools::Long>(m_aStates.top().getPicture().nScaleX)
1076 * (nXExt
1078 / 100L;
1079 if (m_aStates.top().getPicture().nScaleY != 100)
1080 nYExt = (static_cast<tools::Long>(m_aStates.top().getPicture().nScaleY)
1081 * (nYExt
1083 / 100L;
1084 auto pXExtValue = new RTFValue(oox::drawingml::convertHmmToEmu(nXExt));
1085 auto pYExtValue = new RTFValue(oox::drawingml::convertHmmToEmu(nYExt));
1086 aExtentAttributes.set(NS_ooxml::LN_CT_PositiveSize2D_cx, pXExtValue);
1087 aExtentAttributes.set(NS_ooxml::LN_CT_PositiveSize2D_cy, pYExtValue);
1088 auto pExtentValue = new RTFValue(aExtentAttributes);
1089 // docpr sprm
1090 RTFSprms aDocprAttributes;
1091 for (const auto& rCharacterAttribute : m_aStates.top().getCharacterAttributes())
1092 if (rCharacterAttribute.first == NS_ooxml::LN_CT_NonVisualDrawingProps_name
1093 || rCharacterAttribute.first == NS_ooxml::LN_CT_NonVisualDrawingProps_descr)
1094 aDocprAttributes.set(rCharacterAttribute.first, rCharacterAttribute.second);
1095 auto pDocprValue = new RTFValue(aDocprAttributes);
1096 if (bInline)
1097 {
1098 RTFSprms aInlineAttributes;
1099 aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distT, new RTFValue(0));
1100 aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distB, new RTFValue(0));
1101 aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distL, new RTFValue(0));
1102 aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distR, new RTFValue(0));
1103 RTFSprms aInlineSprms;
1104 aInlineSprms.set(NS_ooxml::LN_CT_Inline_extent, pExtentValue);
1105 aInlineSprms.set(NS_ooxml::LN_CT_Inline_docPr, pDocprValue);
1106 aInlineSprms.set(NS_ooxml::LN_graphic_graphic, pGraphicValue);
1107 // inline sprm
1108 auto pValue = new RTFValue(aInlineAttributes, aInlineSprms);
1109 aSprms.set(NS_ooxml::LN_inline_inline, pValue);
1110 }
1111 else // anchored
1112 {
1113 // wrap sprm
1114 RTFSprms aAnchorWrapAttributes;
1116 NS_ooxml::LN_CT_Anchor_behindDoc,
1117 new RTFValue((m_aStates.top().getShape().getInBackground()) ? 1 : 0));
1118 RTFSprms aAnchorSprms;
1119 for (const auto& rCharacterAttribute : m_aStates.top().getCharacterAttributes())
1120 {
1121 if (rCharacterAttribute.first == NS_ooxml::LN_CT_WrapSquare_wrapText)
1122 aAnchorWrapAttributes.set(rCharacterAttribute.first, rCharacterAttribute.second);
1123 }
1124 sal_Int32 nWrap = -1;
1125 for (auto& rCharacterSprm : m_aStates.top().getCharacterSprms())
1126 {
1127 if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapNone
1128 || rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight)
1129 {
1130 nWrap = rCharacterSprm.first;
1131
1132 // If there is a wrap polygon prepared by RTFSdrImport, pick it up here.
1133 if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight
1135 rCharacterSprm.second->getSprms().set(
1136 NS_ooxml::LN_CT_WrapTight_wrapPolygon,
1138
1139 aAnchorSprms.set(rCharacterSprm.first, rCharacterSprm.second);
1140 }
1141 }
1142
1143 if (m_aStates.top().getShape().getWrapSprm().first != 0)
1144 // Replay of a buffered shape, wrap sprm there has priority over
1145 // character sprms of the current state.
1146 aAnchorSprms.set(m_aStates.top().getShape().getWrapSprm().first,
1147 m_aStates.top().getShape().getWrapSprm().second);
1148
1149 aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_extent, pExtentValue);
1150 if (!aAnchorWrapAttributes.empty() && nWrap == -1)
1151 aAnchorSprms.set(NS_ooxml::LN_EG_WrapType_wrapSquare,
1152 new RTFValue(aAnchorWrapAttributes));
1153
1154 // See OOXMLFastContextHandler::positionOffset(), we can't just put offset values in an RTFValue.
1155 RTFSprms aPoshAttributes;
1156 RTFSprms aPoshSprms;
1158 aPoshAttributes.set(
1159 NS_ooxml::LN_CT_PosH_relativeFrom,
1161 if (m_aStates.top().getShape().getLeft() != 0)
1162 {
1164 m_aStates.top().getShape().getLeft())),
1165 /*bVertical=*/false);
1166 aPoshSprms.set(NS_ooxml::LN_CT_PosH_posOffset, new RTFValue());
1167 }
1168 aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_positionH,
1169 new RTFValue(aPoshAttributes, aPoshSprms));
1170
1171 RTFSprms aPosvAttributes;
1172 RTFSprms aPosvSprms;
1174 aPosvAttributes.set(
1175 NS_ooxml::LN_CT_PosV_relativeFrom,
1177 if (m_aStates.top().getShape().getTop() != 0)
1178 {
1180 m_aStates.top().getShape().getTop())),
1181 /*bVertical=*/true);
1182 aPosvSprms.set(NS_ooxml::LN_CT_PosV_posOffset, new RTFValue());
1183 }
1184 aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_positionV,
1185 new RTFValue(aPosvAttributes, aPosvSprms));
1186
1187 aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_docPr, pDocprValue);
1188 aAnchorSprms.set(NS_ooxml::LN_graphic_graphic, pGraphicValue);
1189 // anchor sprm
1190 auto pValue = new RTFValue(m_aStates.top().getShape().getAnchorAttributes(), aAnchorSprms);
1191 aSprms.set(NS_ooxml::LN_anchor_anchor, pValue);
1192 }
1193 checkFirstRun();
1194
1196 {
1198 = new RTFReferenceProperties(std::move(aAttributes), std::move(aSprms));
1199 Mapper().props(pProperties);
1200 // Make sure we don't lose these properties with a too early reset.
1201 m_bHadPicture = true;
1202 }
1203 else
1204 {
1205 auto pValue = new RTFValue(aAttributes, aSprms);
1206 bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue, nullptr);
1207 }
1208}
1209
1211{
1213 {
1214 m_pBinaryData = std::make_shared<SvMemoryStream>();
1215 m_pBinaryData->WriteChar(ch);
1216 for (int i = 0; i < m_aStates.top().getBinaryToRead() - 1; ++i)
1217 {
1218 Strm().ReadChar(ch);
1219 m_pBinaryData->WriteChar(ch);
1220 }
1222 return RTFError::OK;
1223 }
1224
1225 OStringBuffer aBuf(512);
1226
1227 bool bUnicodeChecked = false;
1228 bool bSkipped = false;
1229
1230 while (!Strm().eof()
1232 || (ch != '{' && ch != '}' && ch != '\\')))
1233 {
1235 || (ch != 0x0d && ch != 0x0a))
1236 {
1237 if (m_aStates.top().getCharsToSkip() == 0)
1238 {
1239 if (!bUnicodeChecked)
1240 {
1241 checkUnicode(/*bUnicode =*/true, /*bHex =*/false);
1242 bUnicodeChecked = true;
1243 }
1244 aBuf.append(ch);
1245 }
1246 else
1247 {
1248 bSkipped = true;
1250 }
1251 }
1252
1253 // read a single char if we're in hex mode
1255 break;
1256
1257 if (RTL_TEXTENCODING_MS_932 == m_aStates.top().getCurrentEncoding())
1258 {
1259 unsigned char uch = ch;
1260 if ((uch >= 0x80 && uch <= 0x9F) || uch >= 0xE0)
1261 {
1262 // read second byte of 2-byte Shift-JIS - may be \ { }
1263 Strm().ReadChar(ch);
1264 if (m_aStates.top().getCharsToSkip() == 0)
1265 {
1266 // fdo#79384: Word will reject Shift-JIS following \loch
1267 // but apparently OOo could read and (worse) write such documents
1269 "writerfilter.rtf", "invalid Shift-JIS without DBCH");
1270 assert(bUnicodeChecked);
1271 aBuf.append(ch);
1272 }
1273 else
1274 {
1275 assert(bSkipped);
1276 // anybody who uses \ucN with Shift-JIS is insane
1278 }
1279 }
1280 }
1281
1282 Strm().ReadChar(ch);
1283 }
1285 Strm().SeekRel(-1);
1286
1289 {
1290 if (!bSkipped)
1291 {
1292 // note: apparently \'0d\'0a is interpreted as 2 breaks, not 1
1293 if ((ch == '\r' || ch == '\n')
1297 {
1298 checkUnicode(/*bUnicode =*/false, /*bHex =*/true);
1300 }
1301 else
1302 {
1303 m_aHexBuffer.append(ch);
1304 }
1305 }
1306 return RTFError::OK;
1307 }
1308
1310 return RTFError::OK;
1311 OString aStr = aBuf.makeStringAndClear();
1313 {
1314 if (aStr.toChar() != ';')
1315 m_aStates.top().getLevelNumbers().push_back(sal_Int32(ch));
1316 return RTFError::OK;
1317 }
1318
1319 SAL_INFO("writerfilter.rtf",
1320 "RTFDocumentImpl::resolveChars: collected '"
1321 << OStringToOUString(aStr, m_aStates.top().getCurrentEncoding()) << "'");
1322
1324 {
1325 // we hit a ';' at the end of each color entry
1327 // set components back to zero
1329 }
1330 else if (!aStr.isEmpty())
1331 m_aHexBuffer.append(aStr);
1332
1333 checkUnicode(/*bUnicode =*/false, /*bHex =*/true);
1334 return RTFError::OK;
1335}
1336
1337bool RTFFrame::inFrame() const { return m_nW > 0 || m_nH > 0 || m_nX > 0 || m_nY > 0; }
1338
1339void RTFDocumentImpl::singleChar(sal_uInt8 nValue, bool bRunProps)
1340{
1341 sal_uInt8 sValue[] = { nValue };
1342 RTFBuffer_t* pCurrentBuffer = m_aStates.top().getCurrentBuffer();
1343
1344 if (!pCurrentBuffer)
1345 {
1347 }
1348 else
1349 {
1350 pCurrentBuffer->push_back(Buf_t(BUFFER_STARTRUN, nullptr, nullptr));
1351 }
1352
1353 // Should we send run properties?
1354 if (bRunProps)
1355 runProps();
1356
1357 if (!pCurrentBuffer)
1358 {
1359 Mapper().text(sValue, 1);
1361 }
1362 else
1363 {
1364 auto pValue = new RTFValue(*sValue);
1365 pCurrentBuffer->push_back(Buf_t(BUFFER_TEXT, pValue, nullptr));
1366 pCurrentBuffer->push_back(Buf_t(BUFFER_ENDRUN, nullptr, nullptr));
1367 }
1368}
1369
1371{
1372 OUString aName = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
1373
1374 if (aName.isEmpty())
1375 return;
1376
1377 if (aName.endsWith(";"))
1378 {
1379 aName = aName.copy(0, aName.getLength() - 1);
1380 }
1381
1382 // Old documents can contain no encoding information in fontinfo,
1383 // but there can be font name suffixes: Arial CE is not a special
1384 // font, it is ordinal Arial, but with used cp 1250 encoding.
1385 // Moreover these suffixes have priority over \cpgN and \fcharsetN
1386 // in MS Word.
1387 OUString aFontSuffix;
1388 OUString aNameNoSuffix(aName);
1389 sal_Int32 nLastSpace = aName.lastIndexOf(' ');
1390 if (nLastSpace >= 0)
1391 {
1392 aFontSuffix = aName.copy(nLastSpace + 1);
1393 aNameNoSuffix = aName.copy(0, nLastSpace);
1394 sal_Int32 nEncoding = RTL_TEXTENCODING_DONTKNOW;
1395 for (int i = 0; aRTFFontNameSuffixes[i].codepage != RTL_TEXTENCODING_DONTKNOW; i++)
1396 {
1397 if (aFontSuffix.equalsAscii(aRTFFontNameSuffixes[i].suffix))
1398 {
1399 nEncoding = aRTFFontNameSuffixes[i].codepage;
1400 break;
1401 }
1402 }
1403 if (nEncoding > RTL_TEXTENCODING_DONTKNOW)
1404 {
1405 m_nCurrentEncoding = nEncoding;
1407 }
1408 else
1409 {
1410 // Unknown suffix: looks like it is just a part of font name, restore it
1411 aNameNoSuffix = aName;
1412 }
1413 }
1414
1415 m_aFontNames[m_nCurrentFontIndex] = aNameNoSuffix;
1416 if (m_nCurrentEncoding >= 0)
1417 {
1419 m_nCurrentEncoding = -1;
1420 }
1421 m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Font_name,
1422 new RTFValue(aNameNoSuffix));
1423
1426
1427 //See fdo#47347 initial invalid font entry properties are inserted first,
1428 //so when we attempt to insert the correct ones, there's already an
1429 //entry in the map for them, so the new ones aren't inserted.
1430 auto lb = m_aFontTableEntries.lower_bound(m_nCurrentFontIndex);
1431 if (lb != m_aFontTableEntries.end()
1432 && !(m_aFontTableEntries.key_comp()(m_nCurrentFontIndex, lb->first)))
1433 lb->second = pProp;
1434 else
1435 m_aFontTableEntries.insert(lb, std::make_pair(m_nCurrentFontIndex, pProp));
1436}
1437
1438void RTFDocumentImpl::text(OUString& rString)
1439{
1440 if (rString.getLength() == 1 && m_aStates.top().getDestination() != Destination::DOCCOMM)
1441 {
1442 // No cheating! Tokenizer ignores bare \r and \n, their hex \'0d / \'0a form doesn't count, either.
1443 sal_Unicode ch = rString[0];
1444 if (ch == 0x0d || ch == 0x0a)
1445 return;
1446 }
1447
1448 bool bRet = true;
1449 switch (m_aStates.top().getDestination())
1450 {
1451 // Note: in stylesheet and revtbl groups are mandatory
1455 {
1456 // ; is the end of the entry
1457 bool bEnd = false;
1458 if (rString.endsWith(";"))
1459 {
1460 rString = rString.copy(0, rString.getLength() - 1);
1461 bEnd = true;
1462 }
1464 if (bEnd)
1465 {
1466 // always clear, necessary in case of group-less fonttable
1467 OUString const aName
1468 = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
1469 switch (m_aStates.top().getDestination())
1470 {
1472 {
1474 = m_aStates.top().getTableAttributes().find(NS_ooxml::LN_CT_Style_type);
1475 if (pType)
1476 {
1477 // Word strips whitespace around style names.
1479 m_aStyleTypes[m_nCurrentStyleIndex] = pType->getInt();
1480 auto pValue = new RTFValue(aName.trim());
1481 m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Style_styleId,
1482 pValue);
1483 m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Style_name, pValue);
1484
1487 m_pStyleTableEntries->insert(
1488 std::make_pair(m_nCurrentStyleIndex, pProp));
1489 }
1490 else
1491 SAL_INFO("writerfilter.rtf", "no RTF style type defined, ignoring");
1492 break;
1493 }
1495 // TODO: what can be done with a list name?
1496 break;
1498 m_aAuthors[m_aAuthors.size()] = aName;
1499 break;
1500 default:
1501 break;
1502 }
1504 resetSprms();
1505 }
1506 }
1507 break;
1509 {
1510 m_aStates.top().appendDocVar(rString);
1511 }
1512 break;
1519 case Destination::PICT:
1534 case Destination::FALT:
1537 case Destination::TITLE:
1540 case Destination::ATNID:
1543 case Destination::MR:
1544 case Destination::MCHR:
1545 case Destination::MPOS:
1554 case Destination::MTYPE:
1555 case Destination::MGROW:
1561 break;
1563 // don't enlarge space sequences, eg. it was saved in LibreOffice
1564 if (!rString.startsWithIgnoreAsciiCase("Microsoft"))
1565 m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_longerSpaceSequence,
1566 new RTFValue(0));
1567 break;
1568 default:
1569 bRet = false;
1570 break;
1571 }
1572 if (bRet)
1573 return;
1574
1575 if (!m_aIgnoreFirst.isEmpty() && m_aIgnoreFirst == rString)
1576 {
1577 m_aIgnoreFirst.clear();
1578 return;
1579 }
1580
1581 // Are we in the middle of the table definition? (No cell defs yet, but we already have some cell props.)
1582 if (m_aStates.top().getTableCellSprms().find(NS_ooxml::LN_CT_TcPrBase_vAlign)
1583 && m_nTopLevelCells == 0)
1584 {
1585 m_aTableBufferStack.back().emplace_back(BUFFER_UTEXT, new RTFValue(rString), nullptr);
1586 return;
1587 }
1588
1589 checkFirstRun();
1590 checkNeedPap();
1591
1592 // Don't return earlier, a bookmark start has to be in a paragraph group.
1594 {
1596 return;
1597 }
1598
1599 RTFBuffer_t* pCurrentBuffer = m_aStates.top().getCurrentBuffer();
1600
1601 if (!pCurrentBuffer && m_aStates.top().getDestination() != Destination::FOOTNOTE)
1603 else if (pCurrentBuffer)
1604 {
1605 RTFValue::Pointer_t pValue;
1606 pCurrentBuffer->push_back(Buf_t(BUFFER_STARTRUN, pValue, nullptr));
1607 }
1608
1612 runProps();
1613
1614 if (!pCurrentBuffer)
1615 Mapper().utext(reinterpret_cast<sal_uInt8 const*>(rString.getStr()), rString.getLength());
1616 else
1617 {
1618 auto pValue = new RTFValue(rString);
1619 pCurrentBuffer->push_back(Buf_t(BUFFER_UTEXT, pValue, nullptr));
1620 }
1621
1622 m_bNeedCr = true;
1623
1624 if (!pCurrentBuffer && m_aStates.top().getDestination() != Destination::FOOTNOTE)
1626 else if (pCurrentBuffer)
1627 {
1628 RTFValue::Pointer_t pValue;
1629 pCurrentBuffer->push_back(Buf_t(BUFFER_ENDRUN, pValue, nullptr));
1630 }
1631}
1632
1634 RTFParserState& rState, writerfilter::Reference<Properties>::Pointer_t& o_rpParagraphProperties,
1636 writerfilter::Reference<Properties>::Pointer_t& o_rpTableRowProperties, int const nCells,
1637 int const nCurrentCellX)
1638{
1639 o_rpParagraphProperties
1641 NS_ooxml::LN_Value_ST_StyleType_paragraph);
1642
1643 if (rState.getFrame().hasProperties())
1644 {
1645 o_rpFrameProperties = new RTFReferenceProperties(RTFSprms(), rState.getFrame().getSprms());
1646 }
1647
1648 // Table width.
1649 RTFValue::Pointer_t const pTableWidthProps
1650 = rState.getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblW);
1651 if (!pTableWidthProps)
1652 {
1653 auto pUnitValue = new RTFValue(3);
1654 putNestedAttribute(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblW,
1655 NS_ooxml::LN_CT_TblWidth_type, pUnitValue);
1656 auto pWValue = new RTFValue(nCurrentCellX);
1657 putNestedAttribute(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblW,
1658 NS_ooxml::LN_CT_TblWidth_w, pWValue);
1659 }
1660
1661 if (nCells > 0)
1662 rState.getTableRowSprms().set(NS_ooxml::LN_tblRow, new RTFValue(1));
1663
1664 RTFValue::Pointer_t const pCellMar
1665 = rState.getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblCellMar);
1666 if (!pCellMar)
1667 {
1668 // If no cell margins are defined, the default left/right margin is 0 in Word, but not in Writer.
1669 RTFSprms aAttributes;
1670 aAttributes.set(NS_ooxml::LN_CT_TblWidth_type,
1671 new RTFValue(NS_ooxml::LN_Value_ST_TblWidth_dxa));
1672 aAttributes.set(NS_ooxml::LN_CT_TblWidth_w, new RTFValue(0));
1673 putNestedSprm(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblCellMar,
1674 NS_ooxml::LN_CT_TblCellMar_left, new RTFValue(aAttributes));
1675 putNestedSprm(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblCellMar,
1676 NS_ooxml::LN_CT_TblCellMar_right, new RTFValue(aAttributes));
1677 }
1678
1679 o_rpTableRowProperties
1681}
1682
1684 writerfilter::Reference<Properties>::Pointer_t const& pParagraphProperties,
1685 writerfilter::Reference<Properties>::Pointer_t const& pFrameProperties,
1686 writerfilter::Reference<Properties>::Pointer_t const& pTableRowProperties)
1687{
1688 Mapper().props(pParagraphProperties);
1689
1690 if (pFrameProperties)
1691 {
1692 Mapper().props(pFrameProperties);
1693 }
1694
1695 Mapper().props(pTableRowProperties);
1696
1697 tableBreak();
1698}
1699
1700void RTFDocumentImpl::replayRowBuffer(RTFBuffer_t& rBuffer, ::std::deque<RTFSprms>& rCellsSprms,
1701 ::std::deque<RTFSprms>& rCellsAttributes, int const nCells)
1702{
1703 for (int i = 0; i < nCells; ++i)
1704 {
1705 replayBuffer(rBuffer, &rCellsSprms.front(), &rCellsAttributes.front());
1706 rCellsSprms.pop_front();
1707 rCellsAttributes.pop_front();
1708 }
1709 for (Buf_t& i : rBuffer)
1710 {
1711 SAL_WARN_IF(BUFFER_CELLEND == std::get<0>(i), "writerfilter.rtf", "dropping table cell!");
1712 }
1713 assert(rCellsSprms.empty());
1714 assert(rCellsAttributes.empty());
1715}
1716
1718 RTFSprms const* const pAttributes)
1719{
1720 while (!rBuffer.empty())
1721 {
1722 Buf_t aTuple(rBuffer.front());
1723 rBuffer.pop_front();
1724 if (std::get<0>(aTuple) == BUFFER_PROPS || std::get<0>(aTuple) == BUFFER_PROPS_CHAR)
1725 {
1726 // Construct properties via getProperties() and not directly, to take care of deduplication.
1728 std::get<1>(aTuple)->getAttributes(), std::get<1>(aTuple)->getSprms(),
1729 std::get<0>(aTuple) == BUFFER_PROPS_CHAR ? NS_ooxml::LN_Value_ST_StyleType_character
1730 : 0));
1731 Mapper().props(pProp);
1732 }
1733 else if (std::get<0>(aTuple) == BUFFER_NESTROW)
1734 {
1735 TableRowBuffer& rRowBuffer(*std::get<2>(aTuple));
1736
1737 replayRowBuffer(rRowBuffer.GetBuffer(), rRowBuffer.GetCellsSprms(),
1738 rRowBuffer.GetCellsAttributes(), rRowBuffer.GetCells());
1739
1740 sendProperties(rRowBuffer.GetParaProperties(), rRowBuffer.GetFrameProperties(),
1741 rRowBuffer.GetRowProperties());
1742 }
1743 else if (std::get<0>(aTuple) == BUFFER_CELLEND)
1744 {
1745 assert(pSprms && pAttributes);
1746 auto pValue = new RTFValue(1);
1747 pSprms->set(NS_ooxml::LN_tblCell, pValue);
1748 writerfilter::Reference<Properties>::Pointer_t const pTableCellProperties(
1749 new RTFReferenceProperties(*pAttributes, *pSprms));
1750 Mapper().props(pTableCellProperties);
1751 tableBreak();
1752 break;
1753 }
1754 else if (std::get<0>(aTuple) == BUFFER_STARTRUN)
1756 else if (std::get<0>(aTuple) == BUFFER_TEXT)
1757 {
1758 sal_uInt8 const nValue = std::get<1>(aTuple)->getInt();
1759 Mapper().text(&nValue, 1);
1760 }
1761 else if (std::get<0>(aTuple) == BUFFER_UTEXT)
1762 {
1763 OUString const aString(std::get<1>(aTuple)->getString());
1764 Mapper().utext(reinterpret_cast<sal_uInt8 const*>(aString.getStr()),
1765 aString.getLength());
1766 }
1767 else if (std::get<0>(aTuple) == BUFFER_ENDRUN)
1769 else if (std::get<0>(aTuple) == BUFFER_PAR)
1770 parBreak();
1771 else if (std::get<0>(aTuple) == BUFFER_STARTSHAPE)
1772 m_pSdrImport->resolve(std::get<1>(aTuple)->getShape(), false, RTFSdrImport::SHAPE);
1773 else if (std::get<0>(aTuple) == BUFFER_RESOLVESHAPE)
1774 {
1775 // Make sure there is no current buffer while replaying the shape,
1776 // otherwise it gets re-buffered.
1777 RTFBuffer_t* pCurrentBuffer = m_aStates.top().getCurrentBuffer();
1778 m_aStates.top().setCurrentBuffer(nullptr);
1779
1780 // Set current shape during replay, needed by e.g. wrap in
1781 // background.
1782 RTFShape aShape = m_aStates.top().getShape();
1783 m_aStates.top().getShape() = std::get<1>(aTuple)->getShape();
1784
1785 m_pSdrImport->resolve(std::get<1>(aTuple)->getShape(), true, RTFSdrImport::SHAPE);
1786 m_aStates.top().getShape() = aShape;
1787 m_aStates.top().setCurrentBuffer(pCurrentBuffer);
1788 }
1789 else if (std::get<0>(aTuple) == BUFFER_ENDSHAPE)
1790 m_pSdrImport->close();
1791 else if (std::get<0>(aTuple) == BUFFER_RESOLVESUBSTREAM)
1792 {
1793 RTFSprms& rAttributes = std::get<1>(aTuple)->getAttributes();
1794 std::size_t nPos = rAttributes.find(0)->getInt();
1795 Id nId = rAttributes.find(1)->getInt();
1796 OUString aCustomMark = rAttributes.find(2)->getString();
1797 resolveSubstream(nPos, nId, aCustomMark);
1798 }
1799 else if (std::get<0>(aTuple) == BUFFER_PICTURE)
1800 m_aStates.top().getPicture() = std::get<1>(aTuple)->getPicture();
1801 else if (std::get<0>(aTuple) == BUFFER_SETSTYLE)
1802 {
1803 if (!m_aStates.empty())
1804 m_aStates.top().setCurrentStyleIndex(std::get<1>(aTuple)->getInt());
1805 }
1806 else
1807 assert(false);
1808 }
1809}
1810
1811bool findPropertyName(const std::vector<beans::PropertyValue>& rProperties, const OUString& rName)
1812{
1813 return std::any_of(
1814 rProperties.begin(), rProperties.end(),
1815 [&rName](const beans::PropertyValue& rProperty) { return rProperty.Name == rName; });
1816}
1817
1819{
1821 {
1825 }
1826}
1827
1829{
1833}
1834
1836{
1838 m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol, new RTFValue(-1),
1842 {
1843 m_nNestedTRLeft = 0;
1845 }
1846 else
1847 {
1850 }
1851}
1852
1853RTFError RTFDocumentImpl::dispatchToggle(RTFKeyword nKeyword, bool bParam, int nParam)
1854{
1855 setNeedSect(true);
1856 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
1857 RTFSkipDestination aSkip(*this);
1858 int nSprm = -1;
1859 tools::SvRef<RTFValue> pBoolValue(new RTFValue(int(!bParam || nParam != 0)));
1860
1861 // Underline toggles.
1862 switch (nKeyword)
1863 {
1864 case RTFKeyword::UL:
1865 nSprm = NS_ooxml::LN_Value_ST_Underline_single;
1866 break;
1867 case RTFKeyword::ULDASH:
1868 nSprm = NS_ooxml::LN_Value_ST_Underline_dash;
1869 break;
1871 nSprm = NS_ooxml::LN_Value_ST_Underline_dotDash;
1872 break;
1874 nSprm = NS_ooxml::LN_Value_ST_Underline_dotDotDash;
1875 break;
1876 case RTFKeyword::ULDB:
1877 nSprm = NS_ooxml::LN_Value_ST_Underline_double;
1878 break;
1880 nSprm = NS_ooxml::LN_Value_ST_Underline_wavyHeavy;
1881 break;
1883 nSprm = NS_ooxml::LN_Value_ST_Underline_dashLong;
1884 break;
1885 case RTFKeyword::ULTH:
1886 nSprm = NS_ooxml::LN_Value_ST_Underline_thick;
1887 break;
1888 case RTFKeyword::ULTHD:
1889 nSprm = NS_ooxml::LN_Value_ST_Underline_dottedHeavy;
1890 break;
1892 nSprm = NS_ooxml::LN_Value_ST_Underline_dashedHeavy;
1893 break;
1895 nSprm = NS_ooxml::LN_Value_ST_Underline_dashDotHeavy;
1896 break;
1898 nSprm = NS_ooxml::LN_Value_ST_Underline_dashDotDotHeavy;
1899 break;
1901 nSprm = NS_ooxml::LN_Value_ST_Underline_dashLongHeavy;
1902 break;
1904 nSprm = NS_ooxml::LN_Value_ST_Underline_wavyDouble;
1905 break;
1906 case RTFKeyword::ULWAVE:
1907 nSprm = NS_ooxml::LN_Value_ST_Underline_wave;
1908 break;
1909 default:
1910 break;
1911 }
1912 if (nSprm >= 0)
1913 {
1914 auto pValue
1915 = new RTFValue((!bParam || nParam != 0) ? nSprm : NS_ooxml::LN_Value_ST_Underline_none);
1916 m_aStates.top().getCharacterAttributes().set(NS_ooxml::LN_CT_Underline_val, pValue);
1917 return RTFError::OK;
1918 }
1919
1920 // Accent characters (over dot / over comma).
1921 switch (nKeyword)
1922 {
1924 nSprm = NS_ooxml::LN_Value_ST_Em_none;
1925 break;
1926 case RTFKeyword::ACCDOT:
1927 nSprm = NS_ooxml::LN_Value_ST_Em_dot;
1928 break;
1930 nSprm = NS_ooxml::LN_Value_ST_Em_comma;
1931 break;
1933 nSprm = NS_ooxml::LN_Value_ST_Em_circle;
1934 break;
1936 nSprm = NS_ooxml::LN_Value_ST_Em_underDot;
1937 break;
1938 default:
1939 break;
1940 }
1941 if (nSprm >= 0)
1942 {
1943 auto pValue = new RTFValue((!bParam || nParam != 0) ? nSprm : 0);
1944 m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_RPrBase_em, pValue);
1945 return RTFError::OK;
1946 }
1947
1948 // Trivial character sprms.
1949 switch (nKeyword)
1950 {
1951 case RTFKeyword::B:
1952 case RTFKeyword::AB:
1953 switch (m_aStates.top().getRunType())
1954 {
1958 nSprm = NS_ooxml::LN_EG_RPrBase_bCs;
1959 break;
1965 default:
1966 nSprm = NS_ooxml::LN_EG_RPrBase_b;
1967 break;
1968 }
1969 break;
1970 case RTFKeyword::I:
1971 case RTFKeyword::AI:
1972 switch (m_aStates.top().getRunType())
1973 {
1977 nSprm = NS_ooxml::LN_EG_RPrBase_iCs;
1978 break;
1984 default:
1985 nSprm = NS_ooxml::LN_EG_RPrBase_i;
1986 break;
1987 }
1988 break;
1989 case RTFKeyword::OUTL:
1990 nSprm = NS_ooxml::LN_EG_RPrBase_outline;
1991 break;
1992 case RTFKeyword::SHAD:
1993 nSprm = NS_ooxml::LN_EG_RPrBase_shadow;
1994 break;
1995 case RTFKeyword::V:
1996 nSprm = NS_ooxml::LN_EG_RPrBase_vanish;
1997 break;
1998 case RTFKeyword::STRIKE:
1999 nSprm = NS_ooxml::LN_EG_RPrBase_strike;
2000 break;
2002 nSprm = NS_ooxml::LN_EG_RPrBase_dstrike;
2003 break;
2004 case RTFKeyword::SCAPS:
2005 nSprm = NS_ooxml::LN_EG_RPrBase_smallCaps;
2006 break;
2007 case RTFKeyword::IMPR:
2008 nSprm = NS_ooxml::LN_EG_RPrBase_imprint;
2009 break;
2010 case RTFKeyword::CAPS:
2011 nSprm = NS_ooxml::LN_EG_RPrBase_caps;
2012 break;
2013 default:
2014 break;
2015 }
2016 if (nSprm >= 0)
2017 {
2019 {
2020 m_aStates.top().getTableSprms().set(nSprm, pBoolValue);
2021 }
2022 else
2023 {
2024 m_aStates.top().getCharacterSprms().set(nSprm, pBoolValue);
2025 }
2026 return RTFError::OK;
2027 }
2028
2029 switch (nKeyword)
2030 {
2032 m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_autoSpaceDE,
2033 pBoolValue);
2034 break;
2037 {
2038 auto pValue
2039 = new RTFValue(nKeyword == RTFKeyword::DELETED ? oox::XML_del : oox::XML_ins);
2040 putNestedAttribute(m_aStates.top().getCharacterSprms(), NS_ooxml::LN_trackchange,
2041 NS_ooxml::LN_token, pValue);
2042 }
2043 break;
2044 case RTFKeyword::SBAUTO:
2045 putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_spacing,
2046 NS_ooxml::LN_CT_Spacing_beforeAutospacing, pBoolValue);
2047 break;
2048 case RTFKeyword::SAAUTO:
2049 putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_spacing,
2050 NS_ooxml::LN_CT_Spacing_afterAutospacing, pBoolValue);
2051 break;
2053 m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_evenAndOddHeaders, pBoolValue);
2054 break;
2056 m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_autoHyphenation, pBoolValue);
2057 break;
2059 m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_suppressAutoHyphens,
2060 new RTFValue(int(bParam && nParam == 0)));
2061 break;
2062 default:
2063 {
2064 SAL_INFO("writerfilter.rtf",
2065 "TODO handle toggle '" << keywordToString(nKeyword) << "'");
2066 aSkip.setParsed(false);
2067 }
2068 break;
2069 }
2070 return RTFError::OK;
2071}
2072
2074{
2075 //SAL_INFO("writerfilter.rtf", __func__ << " before push: " << m_pTokenizer->getGroup());
2076
2077 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
2079
2080 if (m_aStates.empty())
2082 else
2083 {
2084 // fdo#85812 group resets run type of _current_ and new state (but not RTL)
2087 {
2089 }
2090
2093 m_bMathNor);
2095 }
2096 m_aStates.top().getDestinationText().setLength(0); // was copied: always reset!
2097
2098 m_pTokenizer->pushGroup();
2099
2100 switch (m_aStates.top().getDestination())
2101 {
2103 // this is a "faked" destination for the font entry
2106 break;
2108 // this is a "faked" destination for the style sheet entry
2111 {
2112 // the *default* is \s0 i.e. paragraph style default
2113 // this will be overwritten by \sN \csN \dsN \tsN
2115 auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_StyleType_paragraph);
2116 m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Style_type, pValue);
2117 }
2118 break;
2123 case Destination::PICT:
2125 break;
2126 case Destination::MNUM:
2127 case Destination::MDEN:
2128 case Destination::ME:
2130 case Destination::MLIM:
2131 case Destination::MSUB:
2132 case Destination::MSUP:
2133 case Destination::MDEG:
2136 break;
2138 // this is a "faked" destination for the revision table entry
2141 break;
2142 default:
2143 break;
2144 }
2145
2146 // If this is true, then ooxml:endtrackchange will be generated. Make sure
2147 // we don't generate more ooxml:endtrackchange than ooxml:trackchange: new
2148 // state does not inherit this flag.
2150
2151 return RTFError::OK;
2152}
2153
2155{
2156 int nBasedOn = 0;
2157 RTFValue::Pointer_t pBasedOn
2158 = m_aStates.top().getTableSprms().find(NS_ooxml::LN_CT_Style_basedOn);
2159 if (pBasedOn)
2160 nBasedOn = pBasedOn->getInt();
2161 if (nBasedOn == 0)
2162 {
2163 // No parent style, then mimic what Word does: ignore attributes which
2164 // would set a margin as formatting, but with a default value.
2165 for (const auto& nId :
2166 { NS_ooxml::LN_CT_Ind_firstLine, NS_ooxml::LN_CT_Ind_left, NS_ooxml::LN_CT_Ind_right,
2167 NS_ooxml::LN_CT_Ind_start, NS_ooxml::LN_CT_Ind_end })
2168 {
2170 NS_ooxml::LN_CT_PPrBase_ind, nId);
2171 if (pValue && pValue->getInt() == 0)
2173 NS_ooxml::LN_CT_PPrBase_ind, nId);
2174 }
2175 }
2176
2181
2182 // resetSprms will clean up this modification
2183 m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Style_pPr, pParaProps);
2184 m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Style_rPr, pCharProps);
2185
2188 return pProps;
2189}
2190
2201{
2203 for (auto const& it : *m_pStyleTableEntries)
2204 {
2205 auto pStyle = it.second;
2206 ret[it.first] = pStyle;
2207 // ugly downcasts here, but can't easily replace the members with
2208 // RTFReferenceProperties because dmapper wants SvRef<Properties> anyway
2209 RTFValue::Pointer_t const pBasedOn(
2210 static_cast<RTFReferenceProperties&>(*pStyle).getSprms().find(
2211 NS_ooxml::LN_CT_Style_basedOn));
2212 if (pBasedOn)
2213 {
2214 int const nBasedOn(pBasedOn->getInt());
2215 // don't deduplicate yourself - especially a potential problem for the default style.
2216 if (it.first == nBasedOn)
2217 continue;
2218
2219 auto const itParent(m_pStyleTableEntries->find(nBasedOn)); // definition as read!
2220 if (itParent != m_pStyleTableEntries->end())
2221 {
2222 auto const pStyleType(
2223 static_cast<RTFReferenceProperties&>(*pStyle).getAttributes().find(
2224 NS_ooxml::LN_CT_Style_type));
2225 assert(pStyleType);
2226 int const nStyleType(pStyleType->getInt());
2227 RTFSprms sprms(
2228 static_cast<RTFReferenceProperties&>(*pStyle).getSprms().cloneAndDeduplicate(
2229 static_cast<RTFReferenceProperties&>(*itParent->second).getSprms(),
2230 nStyleType));
2231 RTFSprms attributes(
2232 static_cast<RTFReferenceProperties&>(*pStyle)
2233 .getAttributes()
2234 .cloneAndDeduplicate(
2235 static_cast<RTFReferenceProperties&>(*itParent->second).getAttributes(),
2236 nStyleType));
2237
2238 ret[it.first] = new RTFReferenceProperties(std::move(attributes), std::move(sprms));
2239 }
2240 else
2241 {
2242 SAL_WARN("writerfilter.rtf", "parent style not found: " << nBasedOn);
2243 }
2244 }
2245 }
2246 assert(ret.size() == m_pStyleTableEntries->size());
2247 return ret;
2248}
2249
2251{
2255}
2256
2258{
2262}
2263
2265 std::u16string_view rName)
2266{
2267 return std::any_of(rProperties.begin(), rProperties.end(),
2268 [&](const beans::Property& rProperty) { return rProperty.Name == rName; });
2269}
2270
2272{
2273 switch (rState.getDestination())
2274 {
2275 //Note: in fonttbl there may or may not be groups, so process it as no groups
2278 {
2279 // Some text unhandled? Seems it is last font name
2280 if (m_aStates.top().getCurrentDestinationText()->getLength())
2282
2283 if (rState.getDestination() == Destination::FONTTABLE)
2284 {
2287 Mapper().table(NS_ooxml::LN_FONTTABLE, pTable);
2288 if (m_nDefaultFontIndex >= 0)
2289 {
2292 NS_ooxml::LN_EG_RPrBase_rFonts, NS_ooxml::LN_CT_Fonts_ascii,
2293 pValue);
2294 }
2295 }
2296 }
2297 break;
2299 {
2300 RTFReferenceTable::Entries_t pStyleTableDeduplicated(deduplicateStyleTable());
2302 new RTFReferenceTable(std::move(pStyleTableDeduplicated)));
2303 Mapper().table(NS_ooxml::LN_STYLESHEET, pTable);
2304 }
2305 break;
2307 {
2308 RTFSprms aListTableAttributes;
2310 = new RTFReferenceProperties(std::move(aListTableAttributes), m_aListTableSprms);
2311 RTFReferenceTable::Entries_t aListTableEntries;
2312 aListTableEntries.insert(std::make_pair(0, pProp));
2314 new RTFReferenceTable(std::move(aListTableEntries)));
2315 Mapper().table(NS_ooxml::LN_NUMBERING, pTable);
2316 }
2317 break;
2319 for (const auto& rListLevelEntry : rState.getListLevelEntries())
2320 rState.getTableSprms().set(rListLevelEntry.first, rListLevelEntry.second,
2322 break;
2324 {
2326 RTFSprms aFFAttributes;
2327 RTFSprms aFFSprms;
2328 aFFSprms.set(NS_ooxml::LN_ffdata, pValue);
2330 {
2332 = new RTFReferenceProperties(std::move(aFFAttributes), std::move(aFFSprms));
2333 Mapper().props(pProperties);
2334 }
2335 else
2336 {
2337 auto pFFValue = new RTFValue(aFFAttributes, aFFSprms);
2338 bufferProperties(*m_aStates.top().getCurrentBuffer(), pFFValue, nullptr);
2339 }
2342
2343 if (m_aStates.top().isFieldLocked())
2346 }
2347 break;
2350
2351 if (!m_aPicturePath.isEmpty())
2352 {
2353 // Read the picture into m_aStates.top().aDestinationText.
2354 pushState();
2356 if (m_aPicturePath.endsWith(".png"))
2358 OUString aFileURL = m_rMediaDescriptor.getUnpackedValueOrDefault(
2359 utl::MediaDescriptor::PROP_URL, OUString());
2360 OUString aPictureURL;
2361 try
2362 {
2363 aPictureURL = rtl::Uri::convertRelToAbs(aFileURL, m_aPicturePath);
2364 }
2365 catch (const rtl::MalformedUriException& rException)
2366 {
2367 SAL_WARN("writerfilter.rtf",
2368 "rtl::Uri::convertRelToAbs() failed: " << rException.getMessage());
2369 }
2370
2371 if (!aPictureURL.isEmpty())
2372 {
2373 SvFileStream aStream(aPictureURL, StreamMode::READ);
2374 if (aStream.IsOpen())
2375 {
2376 OUStringBuffer aBuf;
2377 while (aStream.good())
2378 {
2379 unsigned char ch = 0;
2380 aStream.ReadUChar(ch);
2381 if (ch < 16)
2382 aBuf.append("0");
2383 aBuf.append(static_cast<sal_Int32>(ch), 16);
2384 }
2386 }
2387 }
2388 popState();
2389 m_aPicturePath.clear();
2390 }
2391
2392 break;
2394 {
2397 break; // not for nested group
2398 OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2399
2400 // The first character is the length of the string (the rest should be ignored).
2401 sal_Int32 nLength(aStr.toChar());
2402 OUString aValue;
2403 if (nLength < aStr.getLength())
2404 aValue = aStr.copy(1, nLength);
2405 else
2406 aValue = aStr;
2407 auto pValue = new RTFValue(aValue, true);
2408 rState.getTableAttributes().set(NS_ooxml::LN_CT_LevelText_val, pValue);
2409 }
2410 break;
2412 {
2413 bool bNestedLevelNumbers = false;
2414 if (m_aStates.size() > 1)
2415 // Current destination is levelnumbers and parent destination is levelnumbers as well.
2416 bNestedLevelNumbers
2417 = m_aStates[m_aStates.size() - 2].getDestination() == Destination::LEVELNUMBERS;
2418 if (!bNestedLevelNumbers && rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_lvlText))
2419 {
2420 RTFSprms& rAttributes
2421 = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_lvlText)->getAttributes();
2422 RTFValue::Pointer_t pValue = rAttributes.find(NS_ooxml::LN_CT_LevelText_val);
2423 if (pValue && rState.getLevelNumbersValid())
2424 {
2425 OUString aOrig = pValue->getString();
2426
2427 OUStringBuffer aBuf(aOrig.getLength() * 2);
2428 sal_Int32 nReplaces = 1;
2429 for (int i = 0; i < aOrig.getLength(); i++)
2430 {
2431 if (std::find(rState.getLevelNumbers().begin(),
2432 rState.getLevelNumbers().end(), i + 1)
2433 != rState.getLevelNumbers().end())
2434 {
2435 aBuf.append('%');
2436 // '1.1.1' -> '%1.%2.%3', but '1.' (with '2.' prefix omitted) is %2.
2437 aBuf.append(sal_Int32(nReplaces++ + rState.getListLevelNum() + 1
2438 - rState.getLevelNumbers().size()));
2439 }
2440 else
2441 aBuf.append(aOrig[i]);
2442 }
2443
2444 pValue->setString(aBuf.makeStringAndClear());
2445 }
2446 else if (pValue)
2447 // Have a value, but levelnumbers is not valid -> ignore it.
2448 pValue->setString(OUString());
2449 }
2450 break;
2451 }
2455 break; // not for nested group
2456 rState.getShape().getProperties().emplace_back(
2457 m_aStates.top().getCurrentDestinationText()->makeStringAndClear(), OUString());
2458 break;
2460 if (!rState.getShape().getProperties().empty())
2461 {
2462 rState.getShape().getProperties().back().second
2463 = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2465 m_pSdrImport->append(rState.getShape().getProperties().back().first,
2466 rState.getShape().getProperties().back().second);
2467 else if (rState.getInShapeGroup() && !rState.getInShape()
2468 && rState.getShape().getProperties().back().first == "rotation")
2469 {
2470 // Rotation should be applied on the groupshape itself, not on each shape.
2471 rState.getShape().getGroupProperties().push_back(
2472 rState.getShape().getProperties().back());
2473 rState.getShape().getProperties().pop_back();
2474 }
2475 }
2476 break;
2479 if (m_aStates.size() > 1
2480 && m_aStates[m_aStates.size() - 2].getDestination()
2482 {
2483 // Do not resolve shape if shape instruction destination is inside other shape instruction
2484 }
2485 else if (!m_bObject && !rState.getInListpicture() && !rState.getHadShapeText()
2486 && (!rState.getInShapeGroup() || rState.getInShape()))
2487 {
2488 // Don't trigger a shape import in case we're only leaving the \shpinst of the groupshape itself.
2494 m_pSdrImport->resolve(m_aStates.top().getShape(), true, eType);
2495 else
2496 {
2497 // Shape inside table: buffer the import to have correct anchor position.
2498 // Also buffer the RTFPicture of the state stack as it contains
2499 // the shape size.
2500 auto pPictureValue = new RTFValue(m_aStates.top().getPicture());
2501 m_aStates.top().getCurrentBuffer()->push_back(
2502 Buf_t(BUFFER_PICTURE, pPictureValue, nullptr));
2503 auto pValue = new RTFValue(m_aStates.top().getShape());
2504
2505 // Buffer wrap type.
2506 for (const auto& rCharacterSprm : m_aStates.top().getCharacterSprms())
2507 {
2508 if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapNone
2509 || rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight)
2510 {
2511 m_aStates.top().getShape().getWrapSprm() = rCharacterSprm;
2512 break;
2513 }
2514 }
2515
2516 m_aStates.top().getCurrentBuffer()->push_back(
2517 Buf_t(BUFFER_RESOLVESHAPE, pValue, nullptr));
2518 }
2519 }
2520 else if (rState.getInShapeGroup() && !rState.getInShape())
2521 {
2522 // End of a groupshape, as we're in shapegroup, but not in a real shape.
2523 for (const auto& rGroupProperty : rState.getShape().getGroupProperties())
2524 m_pSdrImport->appendGroupProperty(rGroupProperty.first, rGroupProperty.second);
2525 rState.getShape().getGroupProperties().clear();
2526 }
2527 break;
2529 {
2532 break; // not for nested group
2533 OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2534 int nPos = m_aBookmarks.size();
2538 else
2541 }
2542 break;
2544 {
2547 break; // not for nested group
2548 OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2552 else
2555 nullptr);
2556 }
2557 break;
2560 {
2563 break; // not for nested group
2564 OUString str(m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2565 // dmapper expects this as a field, so let's fake something...
2566 auto const field((Destination::INDEXENTRY == rState.getDestination())
2567 ? std::u16string_view(u"XE")
2568 : std::u16string_view(u"TC"));
2569 str = OUString::Concat(field) + " \"" + str.replaceAll("\"", "\\\"") + "\"";
2571 Mapper().utext(reinterpret_cast<sal_uInt8 const*>(str.getStr()), str.getLength());
2573 // no result
2575 }
2576 break;
2578 {
2581 break; // not for nested group
2582 auto pValue
2583 = new RTFValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2584 m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFData_name, pValue);
2585 }
2586 break;
2588 {
2591 break; // not for nested group
2592 auto pValue
2593 = new RTFValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2594 // OOXML puts these into a LN_CT_FFData_ddList but FFDataHandler should handle this too
2595 m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFDDList_listEntry, pValue,
2597 }
2598 break;
2600 {
2601 if (m_bFormField)
2602 {
2603 OUStringBuffer* pCurrentDestinationText
2605 if (&m_aStates.top().getDestinationText() != pCurrentDestinationText)
2606 break; // not for nested group
2607 OString aStr
2608 = OUStringToOString(*pCurrentDestinationText, rState.getCurrentEncoding());
2609 pCurrentDestinationText->setLength(0);
2610 // decode hex dump
2611 OStringBuffer aBuf;
2612 int b = 0;
2613 int count = 2;
2614 for (int i = 0; i < aStr.getLength(); ++i)
2615 {
2616 char ch = aStr[i];
2617 if (ch != 0x0d && ch != 0x0a)
2618 {
2619 b = b << 4;
2621 if (parsed == -1)
2622 return RTFError::HEX_INVALID;
2623 b += parsed;
2624 count--;
2625 if (!count)
2626 {
2627 aBuf.append(static_cast<char>(b));
2628 count = 2;
2629 b = 0;
2630 }
2631 }
2632 }
2633 aStr = aBuf.makeStringAndClear();
2634
2635 // ignore the first bytes
2636 if (aStr.getLength() > 8)
2637 aStr = aStr.copy(8);
2638 // extract name
2639 sal_Int32 nLength = aStr.toChar();
2640 if (!aStr.isEmpty())
2641 aStr = aStr.copy(1);
2642 nLength = std::min(nLength, aStr.getLength());
2643 OString aName = aStr.copy(0, nLength);
2644 if (aStr.getLength() > nLength)
2645 aStr = aStr.copy(nLength + 1); // zero-terminated string
2646 else
2647 aStr.clear();
2648 // extract default text
2649 nLength = aStr.toChar();
2650 if (!aStr.isEmpty())
2651 aStr = aStr.copy(1);
2652 auto pNValue = new RTFValue(OStringToOUString(aName, rState.getCurrentEncoding()));
2653 m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFData_name, pNValue);
2654 if (nLength > 0)
2655 {
2656 OString aDefaultText = aStr.copy(0, std::min(nLength, aStr.getLength()));
2657 auto pDValue = new RTFValue(
2658 OStringToOUString(aDefaultText, rState.getCurrentEncoding()));
2659 m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFTextInput_default, pDValue);
2660 }
2661
2662 m_bFormField = false;
2663 }
2664 }
2665 break;
2667 if (m_xDocumentProperties.is())
2668 m_xDocumentProperties->setCreationDate(lcl_getDateTime(rState));
2669 break;
2671 if (m_xDocumentProperties.is())
2672 m_xDocumentProperties->setModificationDate(lcl_getDateTime(rState));
2673 break;
2675 if (m_xDocumentProperties.is())
2676 m_xDocumentProperties->setPrintDate(lcl_getDateTime(rState));
2677 break;
2681 break; // not for nested group
2682 if (m_xDocumentProperties.is())
2683 m_xDocumentProperties->setAuthor(
2684 m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2685 break;
2689 break; // not for nested group
2690 if (m_xDocumentProperties.is())
2691 {
2692 OUStringBuffer* pCurrentDestinationText
2694 m_xDocumentProperties->setKeywords(
2695 comphelper::string::convertCommaSeparated(*pCurrentDestinationText));
2696 pCurrentDestinationText->setLength(0);
2697 }
2698 break;
2702 break; // not for nested group
2703 if (m_xDocumentProperties.is())
2704 m_xDocumentProperties->setGenerator(
2705 m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2706 break;
2710 break; // not for nested group
2711 if (m_xDocumentProperties.is())
2712 m_xDocumentProperties->setSubject(
2713 m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2714 break;
2715 case Destination::TITLE:
2716 {
2719 break; // not for nested group
2720 if (m_xDocumentProperties.is())
2721 m_xDocumentProperties->setTitle(
2722 rState.getCurrentDestinationText()->makeStringAndClear());
2723 }
2724 break;
2725
2729 break; // not for nested group
2730 if (m_xDocumentProperties.is())
2731 m_xDocumentProperties->setDescription(
2732 m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2733 break;
2736 {
2739 break; // not for nested group
2740 OUString aName = rState.getDestination() == Destination::OPERATOR ? OUString("Operator")
2741 : OUString("Company");
2742 uno::Any aValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2743 if (m_xDocumentProperties.is())
2744 {
2745 uno::Reference<beans::XPropertyContainer> xUserDefinedProperties
2746 = m_xDocumentProperties->getUserDefinedProperties();
2747 uno::Reference<beans::XPropertySet> xPropertySet(xUserDefinedProperties,
2748 uno::UNO_QUERY);
2750 = xPropertySet->getPropertySetInfo();
2751 if (xPropertySetInfo->hasPropertyByName(aName))
2752 xPropertySet->setPropertyValue(aName, aValue);
2753 else
2754 xUserDefinedProperties->addProperty(aName, beans::PropertyAttribute::REMOVABLE,
2755 aValue);
2756 }
2757 }
2758 break;
2760 {
2763 break; // not for nested group
2764
2765 RTFError eError = handleEmbeddedObject();
2766 if (eError != RTFError::OK)
2767 return eError;
2768 }
2769 break;
2771 {
2772 auto pValue
2773 = new RTFValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2774 m_aOLEAttributes.set(NS_ooxml::LN_CT_OLEObject_ProgID, pValue);
2775 break;
2776 }
2778 {
2779 if (!m_bObject)
2780 {
2781 // if the object is in a special container we will use the \result
2782 // element instead of the \objdata
2783 // (see RTFKeyword::OBJECT in RTFDocumentImpl::dispatchDestination)
2784 break;
2785 }
2786
2787 RTFSprms aObjectSprms;
2788 auto pOLEValue = new RTFValue(m_aOLEAttributes);
2789 aObjectSprms.set(NS_ooxml::LN_OLEObject_OLEObject, pOLEValue);
2790
2791 RTFSprms aObjAttributes;
2792 RTFSprms aObjSprms;
2793 auto pValue = new RTFValue(m_aObjectAttributes, aObjectSprms);
2794 aObjSprms.set(NS_ooxml::LN_object, pValue);
2796 = new RTFReferenceProperties(std::move(aObjAttributes), std::move(aObjSprms));
2798 RTFValue::Pointer_t pShape = m_aObjectAttributes.find(NS_ooxml::LN_shape);
2799 OSL_ASSERT(pShape);
2800 if (pShape)
2801 pShape->getAny() >>= xShape;
2802 if (xShape.is())
2803 {
2804 Mapper().startShape(xShape);
2805 Mapper().props(pProperties);
2806 Mapper().endShape();
2807 }
2810 m_bObject = false;
2811 }
2812 break;
2814 {
2815 OUStringBuffer* pCurrentDestinationText = m_aStates.top().getCurrentDestinationText();
2816 if (&m_aStates.top().getDestinationText() != pCurrentDestinationText)
2817 break; // not for nested group
2818 OUString aStr(OStringToOUString(DTTM22OString(o3tl::toInt32(*pCurrentDestinationText)),
2819 rState.getCurrentEncoding()));
2820 pCurrentDestinationText->setLength(0);
2821 auto pValue = new RTFValue(aStr);
2822 RTFSprms aAnnAttributes;
2823 aAnnAttributes.set(NS_ooxml::LN_CT_TrackChange_date, pValue);
2825 = new RTFReferenceProperties(std::move(aAnnAttributes));
2826 Mapper().props(pProperties);
2827 }
2828 break;
2832 break; // not for nested group
2833 m_aAuthor = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2834 break;
2835 case Destination::ATNID:
2838 break; // not for nested group
2839 m_aAuthorInitials = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2840 break;
2843 {
2846 break; // not for nested group
2847 OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2848 auto pValue = new RTFValue(aStr.toInt32());
2849 RTFSprms aAttributes;
2851 aAttributes.set(NS_ooxml::LN_EG_RangeMarkupElements_commentRangeStart, pValue);
2852 else
2853 aAttributes.set(NS_ooxml::LN_EG_RangeMarkupElements_commentRangeEnd, pValue);
2855 {
2857 = new RTFReferenceProperties(std::move(aAttributes));
2858 Mapper().props(pProperties);
2859 }
2860 else
2861 {
2862 auto const pValue2 = new RTFValue(aAttributes, RTFSprms());
2863 bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue2, nullptr);
2864 }
2865 }
2866 break;
2868 {
2871 break; // not for nested group
2872 OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
2873 RTFSprms aAnnAttributes;
2874 aAnnAttributes.set(NS_ooxml::LN_CT_Markup_id, new RTFValue(aStr.toInt32()));
2875 Mapper().props(new RTFReferenceProperties(std::move(aAnnAttributes)));
2876 }
2877 break;
2878 case Destination::FALT:
2879 {
2882 break; // not for nested group
2883 OUString aStr(m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
2884 auto pValue = new RTFValue(aStr);
2885 rState.getTableSprms().set(NS_ooxml::LN_CT_Font_altName, pValue);
2886 }
2887 break;
2889 if (m_aStates.top().getDrawingObject().getShape().is())
2890 {
2892 const uno::Reference<drawing::XShape>& xShape(rDrawing.getShape());
2893 const uno::Reference<beans::XPropertySet>& xPropertySet(rDrawing.getPropertySet());
2894
2895 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY);
2896 bool bTextFrame = xServiceInfo->supportsService("com.sun.star.text.TextFrame");
2897
2898 // The default is certainly not inline, but then what Word supports is just at-character.
2899 xPropertySet->setPropertyValue("AnchorType",
2900 uno::Any(text::TextContentAnchorType_AT_CHARACTER));
2901
2902 if (bTextFrame)
2903 {
2904 xPropertySet->setPropertyValue("HoriOrientPosition",
2905 uno::Any(rDrawing.getLeft()));
2906 xPropertySet->setPropertyValue("VertOrientPosition",
2907 uno::Any(rDrawing.getTop()));
2908 }
2909 else
2910 {
2911 xShape->setPosition(awt::Point(rDrawing.getLeft(), rDrawing.getTop()));
2912 }
2913 xShape->setSize(awt::Size(rDrawing.getRight(), rDrawing.getBottom()));
2914
2915 if (rDrawing.getHasLineColor())
2916 {
2917 uno::Any aLineColor(sal_uInt32((rDrawing.getLineColorR() << 16)
2918 + (rDrawing.getLineColorG() << 8)
2919 + rDrawing.getLineColorB()));
2920 uno::Any aLineWidth;
2921 RTFSdrImport::resolveLineColorAndWidth(bTextFrame, xPropertySet, aLineColor,
2922 aLineWidth);
2923 }
2924 if (rDrawing.getHasFillColor())
2925 xPropertySet->setPropertyValue(
2926 "FillColor", uno::Any(sal_uInt32((rDrawing.getFillColorR() << 16)
2927 + (rDrawing.getFillColorG() << 8)
2928 + rDrawing.getFillColorB())));
2929 else if (!bTextFrame)
2930 // If there is no fill, the Word default is 100% transparency.
2931 xPropertySet->setPropertyValue("FillTransparence", uno::Any(sal_Int32(100)));
2932
2933 RTFSdrImport::resolveFLine(xPropertySet, rDrawing.getFLine());
2934
2936 {
2937 Mapper().startShape(xShape);
2938 }
2939 Mapper().endShape();
2940 }
2941 break;
2942 case Destination::PICT:
2943 // fdo#79319 ignore picture data if it's really a shape
2944 if (!m_pSdrImport->isFakePict())
2945 {
2946 resolvePict(true, m_pSdrImport->getCurrentShape());
2947 }
2948 m_bNeedFinalPar = true;
2949 break;
2950 case Destination::SHAPE:
2951 m_bNeedFinalPar = true;
2953 if (rState.getFrame().inFrame())
2954 {
2955 // parBreak() modifies m_aStates.top() so we can't apply resetFrame() directly on aState
2956 resetFrame();
2957 parBreak();
2958 // Save this state for later use, so we only reset frame status only for the first shape inside a frame.
2959 rState = m_aStates.top();
2960 m_bNeedPap = true;
2961 }
2962 break;
2964 {
2965 m_aMathBuffer.appendClosingTag(M_TOKEN(oMath));
2966
2967 SvGlobalName aGlobalName(SO3_SM_CLASSID);
2969 OUString aName;
2971 = aContainer.CreateEmbeddedObject(aGlobalName.GetByteSequence(), aName);
2972 if (xObject) // rhbz#1766990 starmath might not be available
2973 {
2974 uno::Reference<util::XCloseable> xComponent(xObject->getComponent(),
2975 uno::UNO_SET_THROW);
2976 if (oox::FormulaImExportBase* pImport
2977 = dynamic_cast<oox::FormulaImExportBase*>(xComponent.get()))
2978 pImport->readFormulaOoxml(m_aMathBuffer);
2979
2980 auto pValue = new RTFValue(xObject);
2981 RTFSprms aMathAttributes;
2982 aMathAttributes.set(NS_ooxml::LN_starmath, pValue);
2984 = new RTFReferenceProperties(std::move(aMathAttributes));
2985 Mapper().props(pProperties);
2986 }
2987
2989 }
2990 break;
2991 case Destination::MR:
2993 m_bMathNor);
2994 break;
2995 case Destination::MF:
2996 m_aMathBuffer.appendClosingTag(M_TOKEN(f));
2997 break;
2998 case Destination::MFPR:
2999 m_aMathBuffer.appendClosingTag(M_TOKEN(fPr));
3000 break;
3002 m_aMathBuffer.appendClosingTag(M_TOKEN(ctrlPr));
3003 break;
3004 case Destination::MNUM:
3005 m_aMathBuffer.appendClosingTag(M_TOKEN(num));
3006 break;
3007 case Destination::MDEN:
3008 m_aMathBuffer.appendClosingTag(M_TOKEN(den));
3009 break;
3010 case Destination::MACC:
3011 m_aMathBuffer.appendClosingTag(M_TOKEN(acc));
3012 break;
3014 m_aMathBuffer.appendClosingTag(M_TOKEN(accPr));
3015 break;
3016 case Destination::MCHR:
3017 case Destination::MPOS:
3026 case Destination::MTYPE:
3027 case Destination::MGROW:
3028 {
3029 sal_Int32 nMathToken = 0;
3030 switch (rState.getDestination())
3031 {
3032 case Destination::MCHR:
3033 nMathToken = M_TOKEN(chr);
3034 break;
3035 case Destination::MPOS:
3036 nMathToken = M_TOKEN(pos);
3037 break;
3039 nMathToken = M_TOKEN(vertJc);
3040 break;
3042 nMathToken = M_TOKEN(strikeH);
3043 break;
3045 nMathToken = M_TOKEN(degHide);
3046 break;
3048 nMathToken = M_TOKEN(begChr);
3049 break;
3051 nMathToken = M_TOKEN(sepChr);
3052 break;
3054 nMathToken = M_TOKEN(endChr);
3055 break;
3057 nMathToken = M_TOKEN(subHide);
3058 break;
3060 nMathToken = M_TOKEN(supHide);
3061 break;
3062 case Destination::MTYPE:
3063 nMathToken = M_TOKEN(type);
3064 break;
3065 case Destination::MGROW:
3066 nMathToken = M_TOKEN(grow);
3067 break;
3068 default:
3069 break;
3070 }
3071
3073 aAttribs[M_TOKEN(val)]
3074 = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
3075 m_aMathBuffer.appendOpeningTag(nMathToken, aAttribs);
3076 m_aMathBuffer.appendClosingTag(nMathToken);
3077 }
3078 break;
3079 case Destination::ME:
3080 m_aMathBuffer.appendClosingTag(M_TOKEN(e));
3081 break;
3082 case Destination::MBAR:
3083 m_aMathBuffer.appendClosingTag(M_TOKEN(bar));
3084 break;
3086 m_aMathBuffer.appendClosingTag(M_TOKEN(barPr));
3087 break;
3088 case Destination::MD:
3090 break;
3091 case Destination::MDPR:
3092 m_aMathBuffer.appendClosingTag(M_TOKEN(dPr));
3093 break;
3094 case Destination::MFUNC:
3095 m_aMathBuffer.appendClosingTag(M_TOKEN(func));
3096 break;
3098 m_aMathBuffer.appendClosingTag(M_TOKEN(funcPr));
3099 break;
3101 m_aMathBuffer.appendClosingTag(M_TOKEN(fName));
3102 break;
3104 m_aMathBuffer.appendClosingTag(M_TOKEN(limLow));
3105 break;
3107 m_aMathBuffer.appendClosingTag(M_TOKEN(limLowPr));
3108 break;
3109 case Destination::MLIM:
3110 m_aMathBuffer.appendClosingTag(M_TOKEN(lim));
3111 break;
3112 case Destination::MM:
3114 break;
3115 case Destination::MMPR:
3116 m_aMathBuffer.appendClosingTag(M_TOKEN(mPr));
3117 break;
3118 case Destination::MMR:
3119 m_aMathBuffer.appendClosingTag(M_TOKEN(mr));
3120 break;
3121 case Destination::MNARY:
3122 m_aMathBuffer.appendClosingTag(M_TOKEN(nary));
3123 break;
3125 m_aMathBuffer.appendClosingTag(M_TOKEN(naryPr));
3126 break;
3127 case Destination::MSUB:
3128 m_aMathBuffer.appendClosingTag(M_TOKEN(sub));
3129 break;
3130 case Destination::MSUP:
3131 m_aMathBuffer.appendClosingTag(M_TOKEN(sup));
3132 break;
3134 m_aMathBuffer.appendClosingTag(M_TOKEN(limUpp));
3135 break;
3137 m_aMathBuffer.appendClosingTag(M_TOKEN(limUppPr));
3138 break;
3140 m_aMathBuffer.appendClosingTag(M_TOKEN(groupChr));
3141 break;
3143 m_aMathBuffer.appendClosingTag(M_TOKEN(groupChrPr));
3144 break;
3146 m_aMathBuffer.appendClosingTag(M_TOKEN(borderBox));
3147 break;
3149 m_aMathBuffer.appendClosingTag(M_TOKEN(borderBoxPr));
3150 break;
3151 case Destination::MRAD:
3152 m_aMathBuffer.appendClosingTag(M_TOKEN(rad));
3153 break;
3155 m_aMathBuffer.appendClosingTag(M_TOKEN(radPr));
3156 break;
3157 case Destination::MDEG:
3158 m_aMathBuffer.appendClosingTag(M_TOKEN(deg));
3159 break;
3160 case Destination::MSSUB:
3161 m_aMathBuffer.appendClosingTag(M_TOKEN(sSub));
3162 break;
3164 m_aMathBuffer.appendClosingTag(M_TOKEN(sSubPr));
3165 break;
3166 case Destination::MSSUP:
3167 m_aMathBuffer.appendClosingTag(M_TOKEN(sSup));
3168 break;
3170 m_aMathBuffer.appendClosingTag(M_TOKEN(sSupPr));
3171 break;
3173 m_aMathBuffer.appendClosingTag(M_TOKEN(sSubSup));
3174 break;
3176 m_aMathBuffer.appendClosingTag(M_TOKEN(sSubSupPr));
3177 break;
3178 case Destination::MSPRE:
3179 m_aMathBuffer.appendClosingTag(M_TOKEN(sPre));
3180 break;
3182 m_aMathBuffer.appendClosingTag(M_TOKEN(sPrePr));
3183 break;
3184 case Destination::MBOX:
3185 m_aMathBuffer.appendClosingTag(M_TOKEN(box));
3186 break;
3188 m_aMathBuffer.appendClosingTag(M_TOKEN(eqArr));
3189 break;
3191 if (rState.getCreatedShapeGroup())
3192 m_pSdrImport->popParent();
3193 break;
3197 break; // not for nested group
3198 rState.setPropName(m_aStates.top().getCurrentDestinationText()->makeStringAndClear());
3199 break;
3203 break; // not for nested group
3204 if (m_xDocumentProperties.is())
3205 {
3206 // Find out what is the key, value type and value we want to set.
3208 = m_xDocumentProperties->getUserDefinedProperties();
3209 const OUString& rKey = m_aStates.top().getPropName();
3210 OUString aStaticVal
3211 = m_aStates.top().getCurrentDestinationText()->makeStringAndClear();
3212 uno::Any aAny;
3214 aAny <<= aStaticVal;
3216 aAny <<= aStaticVal.toInt32();
3218 aAny <<= aStaticVal.toBoolean();
3220 aAny <<= getDateTimeFromUserProp(aStaticVal);
3222 aAny <<= aStaticVal.toDouble();
3223
3224 xPropertyContainer->addProperty(rKey, beans::PropertyAttribute::REMOVABLE, aAny);
3225 }
3226 break;
3228 {
3229 // These are the imported properties.
3232
3233 // These are the real document properties.
3235 m_xDstDoc, uno::UNO_QUERY);
3236 if (xDocumentPropertiesSupplier.is())
3237 m_xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties();
3238
3239 if (m_xDocumentProperties.is())
3240 {
3241 if (!m_bIsNewDoc)
3242 {
3243 // Check classification.
3245 xDocumentProperties, m_xDocumentProperties)))
3247 }
3248
3249 uno::Reference<beans::XPropertyContainer> xClipboardPropertyContainer
3250 = xDocumentProperties->getUserDefinedProperties();
3251 uno::Reference<beans::XPropertyContainer> xDocumentPropertyContainer
3252 = m_xDocumentProperties->getUserDefinedProperties();
3253 uno::Reference<beans::XPropertySet> xClipboardPropertySet(
3254 xClipboardPropertyContainer, uno::UNO_QUERY);
3255 uno::Reference<beans::XPropertySet> xDocumentPropertySet(xDocumentPropertyContainer,
3256 uno::UNO_QUERY);
3257 const uno::Sequence<beans::Property> aClipboardProperties
3258 = xClipboardPropertySet->getPropertySetInfo()->getProperties();
3259 uno::Sequence<beans::Property> aDocumentProperties
3260 = xDocumentPropertySet->getPropertySetInfo()->getProperties();
3261
3262 for (const beans::Property& rProperty : aClipboardProperties)
3263 {
3264 const OUString& rKey = rProperty.Name;
3265 uno::Any aValue = xClipboardPropertySet->getPropertyValue(rKey);
3266
3267 try
3268 {
3269 if (lcl_containsProperty(aDocumentProperties, rKey))
3270 {
3271 // When pasting, don't update existing properties.
3272 if (!m_bIsNewDoc)
3273 xDocumentPropertySet->setPropertyValue(rKey, aValue);
3274 }
3275 else
3276 xDocumentPropertyContainer->addProperty(
3277 rKey, beans::PropertyAttribute::REMOVABLE, aValue);
3278 }
3279 catch (const uno::Exception&)
3280 {
3281 TOOLS_WARN_EXCEPTION("writerfilter.rtf", "failed to set property " << rKey);
3282 }
3283 }
3284 }
3285 }
3286 break;
3287 default:
3288 break;
3289 }
3290
3291 return RTFError::OK;
3292}
3293
3295{
3296 // list table
3297 switch (rState.getDestination())
3298 {
3300 {
3301 auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms());
3302 m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_abstractNum, pValue,
3304 m_aListTable[rState.getCurrentListIndex()] = pValue;
3305 m_nListLevel = -1;
3309 }
3310 break;
3312 {
3313 RTFValue::Pointer_t pIdValue
3314 = rState.getTableAttributes().find(NS_ooxml::LN_CT_AbstractNum_nsid);
3315 if (pIdValue && !m_aStates.empty())
3316 {
3317 // Abstract numbering
3318 RTFSprms aLeveltextAttributes;
3319 OUString aTextValue;
3320 RTFValue::Pointer_t pTextBefore
3321 = rState.getTableAttributes().find(NS_ooxml::LN_CT_LevelText_val);
3322 if (pTextBefore)
3323 aTextValue += pTextBefore->getString();
3324 aTextValue += "%1";
3325 RTFValue::Pointer_t pTextAfter
3326 = rState.getTableAttributes().find(NS_ooxml::LN_CT_LevelSuffix_val);
3327 if (pTextAfter)
3328 aTextValue += pTextAfter->getString();
3329 auto pTextValue = new RTFValue(aTextValue);
3330 aLeveltextAttributes.set(NS_ooxml::LN_CT_LevelText_val, pTextValue);
3331
3332 RTFSprms aLevelAttributes;
3333 RTFSprms aLevelSprms;
3334 auto pIlvlValue = new RTFValue(0);
3335 aLevelAttributes.set(NS_ooxml::LN_CT_Lvl_ilvl, pIlvlValue);
3336
3337 RTFValue::Pointer_t pFmtValue
3338 = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_numFmt);
3339 if (pFmtValue)
3340 aLevelSprms.set(NS_ooxml::LN_CT_Lvl_numFmt, pFmtValue);
3341
3342 RTFValue::Pointer_t pStartatValue
3343 = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_start);
3344 if (pStartatValue)
3345 aLevelSprms.set(NS_ooxml::LN_CT_Lvl_start, pStartatValue);
3346
3347 auto pLeveltextValue = new RTFValue(aLeveltextAttributes);
3348 aLevelSprms.set(NS_ooxml::LN_CT_Lvl_lvlText, pLeveltextValue);
3349 RTFValue::Pointer_t pRunProps
3350 = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_rPr);
3351 if (pRunProps)
3352 aLevelSprms.set(NS_ooxml::LN_CT_Lvl_rPr, pRunProps);
3353
3354 RTFSprms aAbstractAttributes;
3355 RTFSprms aAbstractSprms;
3356 aAbstractAttributes.set(NS_ooxml::LN_CT_AbstractNum_abstractNumId, pIdValue);
3357 auto pLevelValue = new RTFValue(aLevelAttributes, aLevelSprms);
3358 aAbstractSprms.set(NS_ooxml::LN_CT_AbstractNum_lvl, pLevelValue,
3360
3361 RTFSprms aListTableSprms;
3362 auto pAbstractValue = new RTFValue(aAbstractAttributes, aAbstractSprms);
3363 // It's important that Numbering_abstractNum and Numbering_num never overwrites previous values.
3364 aListTableSprms.set(NS_ooxml::LN_CT_Numbering_abstractNum, pAbstractValue,
3366
3367 // Numbering
3368 RTFSprms aNumberingAttributes;
3369 RTFSprms aNumberingSprms;
3370 aNumberingAttributes.set(NS_ooxml::LN_CT_AbstractNum_nsid, pIdValue);
3371 aNumberingSprms.set(NS_ooxml::LN_CT_Num_abstractNumId, pIdValue);
3372 auto pNumberingValue = new RTFValue(aNumberingAttributes, aNumberingSprms);
3373 aListTableSprms.set(NS_ooxml::LN_CT_Numbering_num, pNumberingValue,
3375
3376 // Table
3377 RTFSprms aListTableAttributes;
3379 std::move(aListTableAttributes), std::move(aListTableSprms));
3380
3381 RTFReferenceTable::Entries_t aListTableEntries;
3382 aListTableEntries.insert(std::make_pair(0, pProp));
3384 new RTFReferenceTable(std::move(aListTableEntries)));
3385 Mapper().table(NS_ooxml::LN_NUMBERING, pTable);
3386
3387 // Use it
3388 putNestedSprm(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_numPr,
3389 NS_ooxml::LN_CT_NumPr_ilvl, pIlvlValue, RTFOverwrite::YES_PREPEND);
3390 putNestedSprm(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_numPr,
3391 NS_ooxml::LN_CT_NumPr_numId, pIdValue, RTFOverwrite::YES_PREPEND);
3392 }
3393 }
3394 break;
3396 if (!m_aStates.empty())
3397 {
3398 // FIXME: don't use pDestinationText, points to popped state
3399 auto pValue = new RTFValue(rState.getDestinationText().makeStringAndClear(), true);
3400 m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_LevelSuffix_val, pValue);
3401 }
3402 break;
3404 if (!m_aStates.empty())
3405 {
3406 // FIXME: don't use pDestinationText, points to popped state
3407 auto pValue = new RTFValue(rState.getDestinationText().makeStringAndClear(), true);
3408 m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_LevelText_val, pValue);
3409 }
3410 break;
3412 break;
3414 if (!m_aStates.empty())
3415 {
3416 auto pInnerValue = new RTFValue(m_aStates.top().getListLevelNum()++);
3417 rState.getTableAttributes().set(NS_ooxml::LN_CT_Lvl_ilvl, pInnerValue);
3418
3419 auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms());
3421 m_aStates.top().getListLevelEntries().set(NS_ooxml::LN_CT_AbstractNum_lvl,
3422 pValue, RTFOverwrite::NO_APPEND);
3423 else
3424 m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_NumLvl_lvl, pValue);
3425 }
3426 break;
3428 if (!m_aStates.empty())
3429 {
3430 auto pInnerValue = new RTFValue(m_aStates.top().getListLevelNum()++);
3431 rState.getTableAttributes().set(NS_ooxml::LN_CT_NumLvl_ilvl, pInnerValue);
3432
3433 auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms());
3434 m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Num_lvlOverride, pValue,
3436 }
3437 break;
3438 // list override table
3440 if (!m_aStates.empty())
3441 {
3443 {
3444 // copy properties upwards so upper popState() inserts it
3447 }
3448 else
3449 {
3450 auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms());
3451 m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_num, pValue,
3454 = rState.getCurrentListIndex();
3455 }
3456 }
3457 break;
3459 if (!m_aStates.empty())
3460 {
3461 auto pValue = new RTFValue(rState.getTableAttributes());
3462 m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Lvl_lvlText, pValue);
3463 }
3464 break;
3466 if (!m_aStates.empty())
3467 {
3471 // Parent state is level number or list level, current state is
3472 // level numbers: mark parent as invalid as well if necessary.
3474 }
3475 break;
3477 if (!m_aStates.empty())
3479 break;
3481 if (!m_aStates.empty())
3483 break;
3484 case Destination::FIELD:
3487 break;
3489 if (!m_aStates.empty())
3490 {
3491 OUString docvar(rState.getDocVar());
3492 if (m_aStates.top().getDocVarName().isEmpty())
3493 {
3494 m_aStates.top().setDocVarName(docvar);
3495 }
3496 else
3497 {
3499 m_xModelFactory->createInstance("com.sun.star.text.FieldMaster.User"),
3500 uno::UNO_QUERY_THROW);
3501 xMaster->setPropertyValue("Name", uno::Any(m_aStates.top().getDocVarName()));
3503 m_xModelFactory->createInstance("com.sun.star.text.TextField.User"),
3504 uno::UNO_QUERY);
3505 xField->attachTextFieldMaster(xMaster);
3506 xField->getTextFieldMaster()->setPropertyValue("Content", uno::Any(docvar));
3507
3509 }
3510 }
3511 break;
3513 if (!m_aStates.empty())
3514 {
3515 m_aStates.top().getPicture() = rState.getPicture();
3516 // both \sp and \sv are destinations, copy the text up-ward for later
3518 }
3519 break;
3520 case Destination::FALT:
3521 if (!m_aStates.empty())
3523 break;
3527 if (!m_aStates.empty())
3528 {
3529 m_aStates.top().getShape() = rState.getShape();
3530 m_aStates.top().getPicture() = rState.getPicture();
3532 }
3533 break;
3535 if (!m_aStates.empty()
3537 {
3538 // Shape instruction inside other shape instruction: just copy new shape settings:
3539 // it will be resolved on end of topmost shape instruction destination
3540 m_aStates.top().getShape() = rState.getShape();
3541 m_aStates.top().getPicture() = rState.getPicture();
3544 }
3545 break;
3548 case Destination::SHAPE:
3549 if (!m_aStates.empty())
3550 {
3551 m_aStates.top().getFrame() = rState.getFrame();
3552 if (rState.getDestination() == Destination::SHPPICT
3554 {
3555 RTFSprms aAttributes;
3556 aAttributes.set(NS_ooxml::LN_CT_NumPicBullet_numPicBulletId,
3557 new RTFValue(m_nListPictureId++));
3558 RTFSprms aSprms;
3559 // Dummy value, real picture is already sent to dmapper.
3560 aSprms.set(NS_ooxml::LN_CT_NumPicBullet_pict, new RTFValue(0));
3561 auto pValue = new RTFValue(aAttributes, aSprms);
3562 m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_numPicBullet, pValue,
3564 }
3565 }
3566 break;
3568 if (!m_aStates.empty())
3569 {
3570 // If we're leaving the shapetext group (it may have nested ones) and this is a shape, not an old drawingobject.
3573 {
3576 m_pSdrImport->close();
3577 else
3578 m_aStates.top().getCurrentBuffer()->push_back(
3579 Buf_t(BUFFER_ENDSHAPE, nullptr, nullptr));
3580 }
3581
3582 // It's allowed to declare these inside the shape text, and they
3583 // are expected to have an effect for the whole shape.
3584 if (rState.getDrawingObject().getLeft())
3586 if (rState.getDrawingObject().getTop())
3588 if (rState.getDrawingObject().getRight())
3590 rState.getDrawingObject().getRight());
3591 if (rState.getDrawingObject().getBottom())
3593 rState.getDrawingObject().getBottom());
3594 }
3595 break;
3599 break;
3600 default:
3601 {
3603 m_aStates.top().getPicture() = rState.getPicture();
3604 }
3605 break;
3606 }
3607}
3608
3610{
3611 //SAL_INFO("writerfilter", __func__ << " before pop: m_pTokenizer->getGroup() " << m_pTokenizer->getGroup() <<
3612 // ", dest state: " << m_aStates.top().eDestination);
3613
3614 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
3615 RTFParserState aState(m_aStates.top());
3616 m_bWasInFrame = aState.getFrame().inFrame();
3617
3618 // dmapper expects some content in header/footer, so if there would be nothing, add an empty paragraph.
3619 if (m_pTokenizer->getGroup() == 1 && m_bFirstRun)
3620 {
3621 switch (m_nStreamType)
3622 {
3623 case NS_ooxml::LN_headerl:
3624 case NS_ooxml::LN_headerr:
3625 case NS_ooxml::LN_headerf:
3626 case NS_ooxml::LN_footerl:
3627 case NS_ooxml::LN_footerr:
3628 case NS_ooxml::LN_footerf:
3630 break;
3631 }
3632 }
3633
3634 RTFError eError = beforePopState(aState);
3635 if (eError != RTFError::OK)
3636 return eError;
3637
3638 // See if we need to end a track change
3639 if (aState.getStartedTrackchange())
3640 {
3641 RTFSprms aTCSprms;
3642 auto pValue = new RTFValue(0);
3643 aTCSprms.set(NS_ooxml::LN_endtrackchange, pValue);
3645 Mapper().props(new RTFReferenceProperties(RTFSprms(), std::move(aTCSprms)));
3646 else
3648 new RTFValue(RTFSprms(), aTCSprms), nullptr);
3649 }
3650
3651 // This is the end of the doc, see if we need to close the last section.
3652 if (m_pTokenizer->getGroup() == 1 && !m_bFirstRun)
3653 {
3654 // \par means an empty paragraph at the end of footnotes/endnotes, but
3655 // not in case of other substreams, like headers.
3656 if (m_bNeedCr && m_nStreamType != NS_ooxml::LN_footnote
3657 && m_nStreamType != NS_ooxml::LN_endnote && m_bIsNewDoc)
3659 if (m_bNeedSect) // may be set by dispatchSymbol above!
3660 sectBreak(true);
3661 }
3662
3663 m_aStates.pop();
3664
3665 m_pTokenizer->popGroup();
3666
3667 afterPopState(aState);
3668
3669 if (aState.getCurrentBuffer() == &m_aSuperBuffer)
3670 {
3671 OSL_ASSERT(!m_aStates.empty() && m_aStates.top().getCurrentBuffer() == nullptr);
3672
3673 if (!m_aSuperBuffer.empty())
3674 replayBuffer(m_aSuperBuffer, nullptr, nullptr);
3675 }
3676
3678 && aState.getTableRowWidthAfter() == 0)
3679 // An RTFKeyword::ROW in the inner group already parsed nTableRowWidthAfter,
3680 // don't do it again in the outer state later.
3682
3684 {
3685 // Section break type created for \page still has an effect in the
3686 // outer state as well.
3688 = aState.getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_type);
3689 if (pType)
3690 m_aStates.top().getSectionSprms().set(NS_ooxml::LN_EG_SectPrContents_type, pType);
3691 }
3692
3693 return RTFError::OK;
3694}
3695
3697{
3698 OUStringBuffer* pCurrentDestinationText = m_aStates.top().getCurrentDestinationText();
3699 OString aStr = OUStringToOString(*pCurrentDestinationText, RTL_TEXTENCODING_ASCII_US);
3700 pCurrentDestinationText->setLength(0);
3701 std::unique_ptr<SvStream> pStream(new SvMemoryStream());
3703 return RTFError::HEX_INVALID;
3704
3706 new utl::OSeekableInputStreamWrapper(pStream.release(), /*_bOwner=*/true));
3707 auto pStreamValue = new RTFValue(xInputStream);
3708 m_aOLEAttributes.set(NS_ooxml::LN_inputstream, pStreamValue);
3709
3710 return RTFError::OK;
3711}
3712
3714
3716
3718{
3719 m_aStates.top().setInternalState(nInternalState);
3720}
3721
3723
3725{
3726 m_aStates.top().setDestination(eDestination);
3727}
3728
3729// this is a questionably named method that is used only in a very special
3730// situation where it looks like the "current" buffer is needed?
3731void RTFDocumentImpl::setDestinationText(std::u16string_view rString)
3732{
3733 m_aStates.top().getDestinationText().setLength(0);
3734 m_aStates.top().getDestinationText().append(rString);
3735}
3736
3738
3739void RTFDocumentImpl::setSkipUnknown(bool bSkipUnknown) { m_bSkipUnknown = bSkipUnknown; }
3740
3741static auto FilterControlChars(Destination const destination, OUString const& rString) -> OUString
3742{
3743 if (destination == Destination::LEVELNUMBERS || destination == Destination::LEVELTEXT)
3744 { // control characters are magic here!
3745 return rString;
3746 }
3747 OUStringBuffer buf(rString.getLength());
3748 for (sal_Int32 i = 0; i < rString.getLength(); ++i)
3749 {
3750 sal_Unicode const ch(rString[i]);
3751 if (!linguistic::IsControlChar(ch) || ch == '\r' || ch == '\n' || ch == '\t')
3752 {
3753 buf.append(ch);
3754 }
3755 else
3756 {
3757 SAL_INFO("writerfilter.rtf", "filtering control character");
3758 }
3759 }
3760 return buf.makeStringAndClear();
3761}
3762
3763void RTFDocumentImpl::checkUnicode(bool bUnicode, bool bHex)
3764{
3765 if (bUnicode && !m_aUnicodeBuffer.isEmpty())
3766 {
3767 OUString aString = m_aUnicodeBuffer.toString();
3768 m_aUnicodeBuffer.setLength(0);
3769 aString = FilterControlChars(m_aStates.top().getDestination(), aString);
3770 text(aString);
3771 }
3772 if (bHex && !m_aHexBuffer.isEmpty())
3773 {
3774 rtl_TextEncoding nEncoding = m_aStates.top().getCurrentEncoding();
3776 && m_aStates.top().getCurrentEncoding() == RTL_TEXTENCODING_SYMBOL)
3777 nEncoding = RTL_TEXTENCODING_MS_1252;
3778 OUString aString = OStringToOUString(m_aHexBuffer, nEncoding);
3779 m_aHexBuffer.setLength(0);
3780 aString = FilterControlChars(m_aStates.top().getDestination(), aString);
3781 text(aString);
3782 }
3783}
3784
3786 : m_pDocumentImpl(pDocumentImpl)
3787 , m_nInternalState(RTFInternalState::NORMAL)
3788 , m_eDestination(Destination::NORMAL)
3789 , m_eFieldStatus(RTFFieldStatus::NONE)
3790 , m_bFieldLocked(false)
3791 , m_nBorderState(RTFBorderState::NONE)
3792 , m_nCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(0))
3793 , m_nUc(1)
3794 , m_nCharsToSkip(0)
3795 , m_nBinaryToRead(0)
3796 , m_nListLevelNum(0)
3797 , m_bLevelNumbersValid(true)
3798 , m_aFrame(this)
3799 , m_eRunType(RunType::NONE)
3800 , m_nYear(0)
3801 , m_nMonth(0)
3802 , m_nDay(0)
3803 , m_nHour(0)
3804 , m_nMinute(0)
3805 , m_pCurrentDestinationText(nullptr)
3806 , m_nCurrentStyleIndex(0)
3807 , m_nCurrentCharacterStyleIndex(-1)
3808 , m_pCurrentBuffer(nullptr)
3809 , m_bInListpicture(false)
3810 , m_bInBackground(false)
3811 , m_bHadShapeText(false)
3812 , m_bInShapeGroup(false)
3813 , m_bInShape(false)
3814 , m_bCreatedShapeGroup(false)
3815 , m_bStartedTrackchange(false)
3816 , m_nTableRowWidthAfter(0)
3817{
3818}
3819
3821
3823 const tools::SvRef<TableRowBuffer>& pTableProperties,
3824 Id const nStyleType)
3825{
3826 rBuffer.emplace_back(BUFFER_SETSTYLE, new RTFValue(m_aStates.top().getCurrentStyleIndex()),
3827 nullptr);
3828 assert(nStyleType == 0 || nStyleType == NS_ooxml::LN_Value_ST_StyleType_character);
3829 rBuffer.emplace_back(nStyleType == NS_ooxml::LN_Value_ST_StyleType_character ? BUFFER_PROPS_CHAR
3830 : BUFFER_PROPS,
3831 pValue, pTableProperties);
3832}
3833
3834RTFShape::RTFShape() = default;
3835
3837
3839 : m_pDocumentImpl(pParserState->getDocumentImpl())
3840 , m_nX(0)
3841 , m_nY(0)
3842 , m_nW(0)
3843 , m_nH(0)
3844 , m_nHoriPadding(0)
3845 , m_nVertPadding(0)
3846 , m_nHoriAlign(0)
3847 , m_nHoriAnchor(0)
3848 , m_nVertAlign(0)
3849 , m_nVertAnchor(0)
3850 , m_nHRule(NS_ooxml::LN_Value_doc_ST_HeightRule_auto)
3851{
3852}
3853
3854void RTFFrame::setSprm(Id nId, Id nValue)
3855{
3857 {
3860 }
3861 switch (nId)
3862 {
3863 case NS_ooxml::LN_CT_FramePr_w:
3864 m_nW = nValue;
3865 break;
3866 case NS_ooxml::LN_CT_FramePr_h:
3867 m_nH = nValue;
3868 break;
3869 case NS_ooxml::LN_CT_FramePr_x:
3870 m_nX = nValue;
3871 break;
3872 case NS_ooxml::LN_CT_FramePr_y:
3873 m_nY = nValue;
3874 break;
3875 case NS_ooxml::LN_CT_FramePr_hSpace:
3877 break;
3878 case NS_ooxml::LN_CT_FramePr_vSpace:
3880 break;
3881 case NS_ooxml::LN_CT_FramePr_xAlign:
3883 break;
3884 case NS_ooxml::LN_CT_FramePr_hAnchor:
3886 break;
3887 case NS_ooxml::LN_CT_FramePr_yAlign:
3889 break;
3890 case NS_ooxml::LN_CT_FramePr_vAnchor:
3892 break;
3893 case NS_ooxml::LN_CT_FramePr_wrap:
3894 m_oWrap = nValue;
3895 break;
3896 default:
3897 break;
3898 }
3899}
3900
3902{
3903 RTFSprms sprms;
3904
3905 static const Id pNames[]
3906 = { NS_ooxml::LN_CT_FramePr_x, NS_ooxml::LN_CT_FramePr_y,
3907 NS_ooxml::LN_CT_FramePr_hRule, // Make sure nHRule is processed before nH
3908 NS_ooxml::LN_CT_FramePr_h, NS_ooxml::LN_CT_FramePr_w,
3909 NS_ooxml::LN_CT_FramePr_hSpace, NS_ooxml::LN_CT_FramePr_vSpace,
3910 NS_ooxml::LN_CT_FramePr_hAnchor, NS_ooxml::LN_CT_FramePr_vAnchor,
3911 NS_ooxml::LN_CT_FramePr_xAlign, NS_ooxml::LN_CT_FramePr_yAlign,
3912 NS_ooxml::LN_CT_FramePr_wrap, NS_ooxml::LN_CT_FramePr_dropCap,
3913 NS_ooxml::LN_CT_FramePr_lines };
3914
3915 for (Id nId : pNames)
3916 {
3917 RTFValue::Pointer_t pValue;
3918
3919 switch (nId)
3920 {
3921 case NS_ooxml::LN_CT_FramePr_x:
3922 if (m_nX != 0)
3923 pValue = new RTFValue(m_nX);
3924 break;
3925 case NS_ooxml::LN_CT_FramePr_y:
3926 if (m_nY != 0)
3927 pValue = new RTFValue(m_nY);
3928 break;
3929 case NS_ooxml::LN_CT_FramePr_h:
3930 if (m_nH != 0)
3931 {
3932 if (m_nHRule == NS_ooxml::LN_Value_doc_ST_HeightRule_exact)
3933 pValue = new RTFValue(-m_nH); // The negative value just sets nHRule
3934 else
3935 pValue = new RTFValue(m_nH);
3936 }
3937 break;
3938 case NS_ooxml::LN_CT_FramePr_w:
3939 if (m_nW != 0)
3940 pValue = new RTFValue(m_nW);
3941 break;
3942 case NS_ooxml::LN_CT_FramePr_hSpace:
3943 if (m_nHoriPadding != 0)
3944 pValue = new RTFValue(m_nHoriPadding);
3945 break;
3946 case NS_ooxml::LN_CT_FramePr_vSpace:
3947 if (m_nVertPadding != 0)
3948 pValue = new RTFValue(m_nVertPadding);
3949 break;
3950 case NS_ooxml::LN_CT_FramePr_hAnchor:
3951 {
3952 if (m_nHoriAnchor == 0)
3953 m_nHoriAnchor = NS_ooxml::LN_Value_doc_ST_HAnchor_margin;
3954 pValue = new RTFValue(m_nHoriAnchor);
3955 }
3956 break;
3957 case NS_ooxml::LN_CT_FramePr_vAnchor:
3958 {
3959 if (m_nVertAnchor == 0)
3960 m_nVertAnchor = NS_ooxml::LN_Value_doc_ST_VAnchor_margin;
3961 pValue = new RTFValue(m_nVertAnchor);
3962 }
3963 break;
3964 case NS_ooxml::LN_CT_FramePr_xAlign:
3965 pValue = new RTFValue(m_nHoriAlign);
3966 break;
3967 case NS_ooxml::LN_CT_FramePr_yAlign:
3968 pValue = new RTFValue(m_nVertAlign);
3969 break;
3970 case NS_ooxml::LN_CT_FramePr_hRule:
3971 {
3972 if (m_nH < 0)
3973 m_nHRule = NS_ooxml::LN_Value_doc_ST_HeightRule_exact;
3974 else if (m_nH > 0)
3975 m_nHRule = NS_ooxml::LN_Value_doc_ST_HeightRule_atLeast;
3976 pValue = new RTFValue(m_nHRule);
3977 }
3978 break;
3979 case NS_ooxml::LN_CT_FramePr_wrap:
3980 if (m_oWrap)
3981 pValue = new RTFValue(*m_oWrap);
3982 break;
3983 default:
3984 break;
3985 }
3986
3987 if (pValue)
3988 sprms.set(nId, pValue);
3989 }
3990
3991 RTFSprms frameprSprms;
3992 frameprSprms.set(NS_ooxml::LN_CT_PPrBase_framePr, new RTFValue(sprms));
3993 return frameprSprms;
3994}
3995
3997{
3998 return m_nX != 0 || m_nY != 0 || m_nW != 0 || m_nH != 0 || m_nHoriPadding != 0
3999 || m_nVertPadding != 0 || m_nHoriAlign != 0 || m_nHoriAnchor != 0 || m_nVertAlign != 0
4000 || m_nVertAnchor != 0;
4001}
4002
4003} // namespace writerfilter
4004
4005/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XComponentContext > m_xContext
XPropertyListType t
css::uno::Reference< css::lang::XComponent > m_xFrame
double d
static OutputDevice * GetDefaultDevice()
static const AllSettings & GetSettings()
Size GetPrefSize() const
MapMode GetPrefMapMode() const
MapUnit GetMapUnit() const
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
SAL_WARN_UNUSED_RESULT Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
static bool ShowPasteInfo(SfxClassificationCheckPasteResult eResult)
static SfxClassificationCheckPasteResult CheckPaste(const css::uno::Reference< css::document::XDocumentProperties > &xSource, const css::uno::Reference< css::document::XDocumentProperties > &xDestination)
constexpr tools::Long Height() const
constexpr tools::Long Width() const
bool IsOpen() const
css::uno::Sequence< sal_Int8 > GetByteSequence() const
sal_uInt64 Tell() const
bool good() const
SvStream & WriteUInt32(sal_uInt32 nUInt32)
SvStream & ReadChar(char &rChar)
sal_uInt64 Seek(sal_uInt64 nPos)
SvStream & WriteChar(char nChar)
sal_uInt64 SeekRel(sal_Int64 nPos)
SvStream & ReadUChar(unsigned char &rChar)
SvStream & WriteStream(SvStream &rStream)
css::uno::Reference< css::embed::XEmbeddedObject > CreateEmbeddedObject(const css::uno::Sequence< sal_Int8 > &, OUString &, OUString const *pBaseURL=nullptr)
#define SO3_SM_CLASSID
void appendOpeningTag(int token, const css::uno::Reference< css::xml::sax::XFastAttributeList > &attributes=css::uno::Reference< css::xml::sax::XFastAttributeList >())
void appendCharacters(std::u16string_view characters)
static constexpr OUStringLiteral PROP_URL
static std::unique_ptr< SvStream > CreateStream(const OUString &rFileName, StreamMode eOpenMode, css::uno::Reference< css::awt::XWindow > xParentWin=nullptr)
Reference to a resource that generates events and sends them to a handler.
Handler for a stream.
virtual void endParagraphGroup()=0
Receives end mark for group with the same paragraph properties.
virtual void endCharacterGroup()=0
Receives end mark for group with the same character properties.
virtual void props(writerfilter::Reference< Properties >::Pointer_t ref)=0
Receives properties of the current run of text.
virtual void markLastSectionGroup()
The current section is the last one in this body text.
virtual void startSectionGroup()=0
Receives start mark for group with the same section properties.
virtual void endSectionGroup()=0
Receives end mark for group with the same section properties.
virtual void startParagraphGroup()=0
Receives start mark for group with the same paragraph properties.
virtual void text(const sal_uInt8 *data, size_t len)=0
Receives 8-bit per character text.
virtual void startShape(css::uno::Reference< css::drawing::XShape > const &xShape)=0
Receives a shape.
virtual void startCharacterGroup()=0
Receives start mark for group with the same character properties.
virtual void table(Id name, writerfilter::Reference< Table >::Pointer_t ref)=0
Receives table.
virtual void substream(Id name, writerfilter::Reference< Stream >::Pointer_t ref)=0
Receives a substream.
virtual void positionOffset(const OUString &rText, bool bVertical)=0
Offset in EMUs for a shape.
virtual void utext(const sal_uInt8 *data, size_t len)=0
Receives 16-bit per character text.
virtual void endShape()=0
Implementation of the RTFDocument interface.
void replayRowBuffer(RTFBuffer_t &rBuffer, ::std::deque< RTFSprms > &rCellsSprms, ::std::deque< RTFSprms > &rCellsAttributes, int nCells)
void resolvePict(bool bInline, css::uno::Reference< css::drawing::XShape > const &rShape)
Resolve a picture: If not inline, then anchored.
void resolve(Stream &rMapper) override
Resolves this document to a stream handler.
int m_nNestedCurrentCellX
Current cellx value (nested table)
void setNeedSect(bool bNeedSect)
If we need a final section break at the end of the document.
void afterPopState(RTFParserState &rState)
RTFSprms m_aSettingsTableAttributes
The settings table attributes.
bool m_bHadPicture
A picture was seen in the current paragraph.
RTFDocumentImpl * m_pSuperstream
Superstream of this substream.
bool m_bObject
If we are in an object group and if the we use its \objdata element.
css::uno::Reference< css::frame::XFrame > const & m_xFrame
std::deque< RTFBuffer_t > m_aTableBufferStack
Buffered table cells, till cell definitions are not reached.
int m_nDefaultFontIndex
Raw default font index, use getFont() on it to get a real one.
std::map< int, RTFValue::Pointer_t > m_aListTable
Maps listtable indexes to listtable entries.
std::map< int, OUString > m_aFontNames
Font index <-> name map.
writerfilter::Reference< Properties >::Pointer_t getProperties(const RTFSprms &rAttributes, RTFSprms const &rSprms, Id nStyleType)
RTFSprms m_aOLEAttributes
OLE attributes are attributes of the ooxml:OLEObject_OLEObject sprm.
bool isStyleSheetImport()
Are we inside the stylesheet table?
OUString m_aIgnoreFirst
Ignore the first occurrence of this text.
std::map< int, int > m_aInvalidListLevelFirstIndents
Maps List level indexes to removed values in the current list entry.
int m_nListPictureId
ID of the next \listlevel picture.
OUString m_aAuthor
Annotation author of the next annotation.
bool m_bNeedFinalPar
If set, an empty paragraph will be added at the end of the document.
std::map< int, int > m_aListOverrideTable
Maps between listoverridetable and listtable indexes.
OUString m_aPicturePath
For the INCLUDEPICTURE field's argument.
RTFError dispatchDestination(RTFKeyword nKeyword) override
RTFKeyword m_nResetBreakOnSectBreak
clean up a synthetic page break, see RTF_PAGE if inactive value is -1, otherwise the RTF_SKB* to rest...
void resetFrame()
Resets m_aStates.top().aFrame.
const utl::MediaDescriptor & m_rMediaDescriptor
The media descriptor contains e.g. the base URL of the document.
RTFError dispatchSymbol(RTFKeyword nKeyword) override
std::queue< std::pair< Id, std::size_t > > m_nHeaderFooterPositions
css::uno::Reference< css::task::XStatusIndicator > const & m_xStatusIndicator
RTFSprms m_aSettingsTableSprms
The settings table sprms.
int m_nCurrentEncoding
Used only during font table parsing till we don't know the font name.
bool m_bNeedCrOrig
Original value of m_bNeedCr – saved/restored before/after textframes.
OUString getFontName(int nIndex)
Return the name of the font, based on a dmapper index.
bool m_bHadSect
The document has multiple sections.
RTFReferenceTable::Entries_t m_aFontTableEntries
css::uno::Reference< css::uno::XComponentContext > const & m_xContext
RTFError handleEmbeddedObject()
Turns the destination text into an input stream of the current OLE attributes.
void singleChar(sal_uInt8 nValue, bool bRunProps=false)
Id m_nStreamType
Type of the stream: header, footer, footnote, etc.
int m_nTopLevelCurrentCellX
Current cellx value (top-level table)
bool m_bFirstRunException
except in the case of tables in initial multicolumn section (global for assertion)
bool getFirstRun() const
If the initial paragraph is started.
bool m_bFirstRun
to start initial paragraph / section after font/style tables
void checkUnicode(bool bUnicode, bool bHex)
If we have some unicode or hex characters to send.
RTFBuffer_t m_aSuperBuffer
Buffered superscript, till footnote is reached (or not).
std::unique_ptr< SvStream > m_pInStream
css::uno::Reference< css::lang::XMultiServiceFactory > m_xModelFactory
OStringBuffer m_aHexBuffer
Same for hex characters.
std::map< int, rtl_TextEncoding > m_aFontEncodings
Font index <-> encoding map, not part of the parser state.
RTFInternalState getInternalState() override
Id getStyleType(int nIndex)
Return the style type of an RTF style index.
bool m_bIsNewDoc
New document means not pasting into an existing one.
std::shared_ptr< SvStream > m_pBinaryData
If the data for a picture is a binary one, it's stored here.
RTFParserState & getDefaultState()
Get the default parser state.
void setDestination(Destination eDestination) override
std::map< int, std::map< int, int > > m_aInvalidListTableFirstIndents
Maps list table indexes to levels (and their removed values).
std::map< OUString, int > m_aBookmarks
Bookmark name <-> index map.
int m_nListLevel
Index of the current list level in a list table entry.
void bufferProperties(RTFBuffer_t &rBuffer, const RTFValue::Pointer_t &pValue, const tools::SvRef< TableRowBuffer > &pTableProperties, Id nStyleType=0)
Buffers properties to be sent later.
oox::formulaimport::XmlStreamBuilder m_aMathBuffer
Formula import.
std::map< int, Id > m_aStyleTypes
Maps style indexes to style types.
std::shared_ptr< oox::GraphicHelper > m_pGraphicHelper
OUString getStyleName(int nIndex)
Return the style name of an RTF style index.
std::vector< Color > m_aColorTable
Color index <-> RGB color value map.
OUString m_aAuthorInitials
Initials of author of the next annotation.
css::uno::Reference< css::io::XInputStream > const & m_xInputStream
writerfilter::Reference< Properties >::Pointer_t createStyleProperties()
void sendProperties(writerfilter::Reference< Properties >::Pointer_t const &pParagraphProperties, writerfilter::Reference< Properties >::Pointer_t const &pFrameProperties, writerfilter::Reference< Properties >::Pointer_t const &pTableRowProperties)
Send the passed properties to dmapper.
int getFontIndex(int nIndex)
Return the dmapper index of an RTF index for fonts.
bool m_bMathNor
Normal text property, that is math italic and math spacing are not applied to the current run.
RTFDocumentImpl(css::uno::Reference< css::uno::XComponentContext > const &xContext, css::uno::Reference< css::io::XInputStream > const &xInputStream, css::uno::Reference< css::lang::XComponent > const &xDstDoc, css::uno::Reference< css::frame::XFrame > const &xFrame, css::uno::Reference< css::task::XStatusIndicator > const &xStatusIndicator, const utl::MediaDescriptor &rMediaDescriptor)
void setInternalState(RTFInternalState nInternalState) override
bool m_bNeedCr
If we need to emit a CR at the end of substream.
css::uno::Reference< css::lang::XComponent > const & m_xDstDoc
int m_nTopLevelCells
cell props buffer for top-level table, reset by \row
tools::SvRef< RTFTokenizer > m_pTokenizer
void 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 nCells, int nCurrentCellX)
RTFParserState m_aDefaultState
Read by RTF_PARD.
void checkFirstRun()
If this is the first run of the document, starts the initial paragraph.
void replayBuffer(RTFBuffer_t &rBuffer, RTFSprms *pSprms, RTFSprms const *pAttributes)
std::map< int, OUString > m_aAuthors
Revision index <-> author map.
RTFReferenceTable::Entries_t deduplicateStyleTable()
implement non-obvious RTF specific style inheritance
tools::SvRef< RTFSdrImport > m_pSdrImport
RTFError beforePopState(RTFParserState &rState)
void setSkipUnknown(bool bSkipUnknown) override
void setSuperstream(RTFDocumentImpl *pSuperstream)
RTFError dispatchFlag(RTFKeyword nKeyword) override
std::map< int, OUString > m_aStyleNames
Maps style indexes to style names.
void resolveSubstream(std::size_t nPos, Id nId)
rtl_TextEncoding getEncoding(int nFontIndex)
Return the encoding associated with a font index.
RTFError dispatchToggle(RTFKeyword nKeyword, bool bParam, int nParam) override
void setNeedPar(bool bNeedPar)
If we need to add a dummy paragraph before a section break.
void setDestinationText(std::u16string_view rString)
bool m_bNeedPap
If paragraph properties should be emitted on next run.
std::shared_ptr< RTFReferenceTable::Entries_t > m_pStyleTableEntries
To avoid copying entries between DomainMapper instances it is stored as pointer.
std::vector< int > m_aFontIndexes
Maps the non-continuous font indexes to the continuous dmapper indexes.
bool m_hasRHeader
Flags for ensuring that only one header and footer is added per section.
RTFError resolveChars(char ch) override
RTFSprms m_aListTableSprms
The list table and list override table combined.
bool m_bWasInFrame
If aFrame.inFrame() was true in the previous state.
bool m_bNeedSect
If a section break is needed before the end of the doc (false right after a section break).
void outputSettingsTable()
Send NS_ooxml::LN_settings_settings to dmapper.
Color getColorTable(sal_uInt32 nIndex)
css::uno::Reference< css::document::XDocumentProperties > m_xDocumentProperties
Stores the properties of a drawing object.
const css::uno::Reference< css::beans::XPropertySet > & getPropertySet() const
const css::uno::Reference< css::drawing::XShape > & getShape() const
Stores the properties of a frame.
RTFFrame(RTFParserState *pParserState)
bool inFrame() const
If we got tokens indicating we're in a frame.
RTFSprms getSprms()
Convert the stored properties to Sprms.
void setSprm(Id nId, Id nValue)
Store a property.
This acts like an importer, but used for looking ahead, e.g.
State of the parser, which gets saved / restored when changing groups.
void setStartedTrackchange(bool bStartedTrackchange)
void setCurrentEncoding(rtl_TextEncoding nCurrentEncoding)
RTFParserState(RTFDocumentImpl *pDocumentImpl)
void setDocVarName(OUString &aDocVarName)
OUString const & getPropName() const
void setCurrentStyleIndex(int nCurrentStyleIndex)
RTFInternalState getInternalState() const
void setLevelNumbersValid(bool bLevelNumbersValid)
void setCurrentBuffer(RTFBuffer_t *pCurrentBuffer)
rtl_TextEncoding getCurrentEncoding() const
css::uno::Type const & getPropType() const
void setDestination(Destination eDestination)
void setPropName(const OUString &rPropName)
void setTableRowWidthAfter(int nTableRowWidthAfter)
OUStringBuffer * getCurrentDestinationText() const
void setFieldStatus(RTFFieldStatus eFieldStatus)
RunType
Maps to OOXML's ascii, cs or eastAsia.
std::vector< sal_Int32 > & getLevelNumbers()
void appendDestinationText(std::u16string_view rString)
void setHadShapeText(bool bHadShapeText)
void setInternalState(RTFInternalState nInternalState)
void setCurrentDestinationText(OUStringBuffer *pDestinationText)
Sends RTFSprm instances to DomainMapper.
Sends tables (e.g. font table) to the domain mapper.
std::map< int, writerfilter::Reference< Properties >::Pointer_t > Entries_t
Handles the import of drawings using RTF markup.
static void resolveFLine(css::uno::Reference< css::beans::XPropertySet > const &xPropertySet, sal_Int32 nFLine)
static void resolveLineColorAndWidth(bool bTextFrame, const css::uno::Reference< css::beans::XPropertySet > &xPropertySet, css::uno::Any const &rLineColor, css::uno::Any const &rLineWidth)
Set line color and line width on the shape, using the relevant API depending on if the shape is a tex...
Stores the properties of a shape.
std::vector< std::pair< OUString, OUString > > & getProperties()
void setBottom(sal_Int32 nBottom)
sal_uInt32 getHoriOrientRelationToken() const
std::pair< Id, RTFValue::Pointer_t > & getWrapSprm()
std::vector< std::pair< OUString, OUString > > & getGroupProperties()
sal_uInt32 getVertOrientRelationToken() const
void setRight(sal_Int32 nRight)
Skips a destination after a not parsed control word if it was prefixed with *.
A list of RTFSprm with a copy constructor that performs a deep copy.
Definition: rtfsprm.hxx:39
RTFValue::Pointer_t find(Id nKeyword, bool bFirst=true, bool bForWrite=false)
Definition: rtfsprm.cxx:74
void deduplicateList(const std::map< int, int > &rInvalidListLevelFirstIndents)
Removes duplicated values based on in-list properties.
Definition: rtfsprm.cxx:353
void set(Id nKeyword, const RTFValue::Pointer_t &pValue, RTFOverwrite eOverwrite=RTFOverwrite::YES)
Does the same as ->push_back(), except that it can overwrite or ignore existing entries.
Definition: rtfsprm.cxx:98
bool erase(Id nKeyword)
Definition: rtfsprm.cxx:136
RTFSprms cloneAndDeduplicate(RTFSprms &rReference, Id nStyleType, bool bImplicitPPr=false, RTFSprms *pDirect=nullptr) const
Removes elements which are already in the reference set.
Definition: rtfsprm.cxx:412
void duplicateList(const RTFValue::Pointer_t &pAbstract)
Inserts default values to override attributes of pAbstract.
Definition: rtfsprm.cxx:378
RTF tokenizer that separates control words from text.
Value of an RTF keyword.
Definition: rtfvalue.hxx:33
tools::SvRef< RTFValue > Pointer_t
Definition: rtfvalue.hxx:41
writerfilter::Reference< Properties >::Pointer_t & GetParaProperties()
std::deque< RTFSprms > & GetCellsSprms()
std::deque< RTFSprms > & GetCellsAttributes()
writerfilter::Reference< Properties >::Pointer_t & GetRowProperties()
writerfilter::Reference< Properties >::Pointer_t & GetFrameProperties()
constexpr ::Color COL_AUTO(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
TOOLS_DLLPUBLIC OString DateTimeToOString(const DateTime &rDateTime)
#define TOOLS_WARN_EXCEPTION(area, stream)
float u
NORMAL
DocumentType eType
sal_Int16 nValue
sal_Int32 nIndex
OUString aName
void * p
sal_uInt16 nPos
#define SAL_INFO_IF(condition, area, stream)
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
aStr
aBuf
NONE
tools::Long const nBorder
const LanguageTag & getLocale()
OUString convertCommaSeparated(uno::Sequence< OUString > const &i_rSeq)
OUString getString(const Any &_rAny)
int i
bool IsControlChar(sal_Unicode cChar)
bool ExtractOLE2FromObjdata(const OString &rObjdata, SvStream &rOle2)
int AsHex(char ch)
DateTime DTTM2DateTime(tools::Long lDTTM)
rtl_TextEncoding getBestTextEncodingFromLocale(const css::lang::Locale &rLocale)
m
sal_Int32 toInt32(std::u16string_view str, sal_Int16 radix=10)
constexpr bool starts_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept
sal_Int64 convertHmmToEmu(sal_Int32 nValue)
std::shared_ptr< StorageBase > StorageRef
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
long Long
static void lcl_copyFlatten(RTFReferenceProperties &rProps, RTFSprms &rStyleAttributes, RTFSprms &rStyleSprms)
Copy rProps to rStyleAttributes and rStyleSprms, but in case of nested sprms, copy their children as ...
bool findPropertyName(const std::vector< beans::PropertyValue > &rProperties, const OUString &rName)
RTFValue::Pointer_t getNestedAttribute(RTFSprms &rSprms, Id nParent, Id nId)
Looks up the nParent then the nested nId attribute in rSprms.
static RTFSprms lcl_getBookmarkProperties(int nPos, const OUString &rString)
RTFFontNameSuffix const aRTFFontNameSuffixes[]
Definition: rtfcharsets.cxx:56
std::tuple< RTFBufferTypes, RTFValue::Pointer_t, tools::SvRef< TableRowBuffer > > Buf_t
A buffer storing dmapper calls.
RTFSymbol const aRTFControlWords[]
void putNestedAttribute(RTFSprms &rSprms, Id nParent, Id nId, const RTFValue::Pointer_t &pValue, RTFOverwrite eOverwrite, bool bAttribute)
bool eraseNestedAttribute(RTFSprms &rSprms, Id nParent, Id nId)
@ NO_APPEND
No, always append the value to the end of the list.
@ YES_PREPEND
Yes, always prepend the value to the start of the list and remove existing entries.
@ YES
Yes, if an existing key is found, overwrite it.
void putNestedSprm(RTFSprms &rSprms, Id nParent, Id nId, const RTFValue::Pointer_t &pValue, RTFOverwrite eOverwrite)
RTFValue::Pointer_t getNestedSprm(RTFSprms &rSprms, Id nParent, Id nId)
Looks up the nParent then the nested nId sprm in rSprms.
Destination
An RTF destination state is the last open destination control word.
static util::DateTime lcl_getDateTime(RTFParserState const &aState)
RTFFormFieldType
Form field types.
Id getParagraphBorder(sal_uInt32 nIndex)
OString DTTM22OString(tools::Long nDTTM)
@ BUFFER_RESOLVESHAPE
Imports a shape.
@ BUFFER_PROPS
Stores properties, should be created only in bufferProperties().
@ BUFFER_PICTURE
Restores RTFParserState::aPicture.
static auto FilterControlChars(Destination const destination, OUString const &rString) -> OUString
RTFSprms & getLastAttributes(RTFSprms &rSprms, Id nId)
const char * keywordToString(RTFKeyword nKeyword)
static void lcl_DestinationToMath(OUStringBuffer *pDestinationText, oox::formulaimport::XmlStreamBuilder &rMathBuffer, bool &rMathNor)
std::deque< Buf_t > RTFBuffer_t
static bool lcl_containsProperty(const uno::Sequence< beans::Property > &rProperties, std::u16string_view rName)
void putBorderProperty(RTFStack &aStates, Id nId, const RTFValue::Pointer_t &pValue)
const sal_uInt8 cFieldSep
const sal_uInt8 cFieldStart
const sal_uInt8 cFieldEnd
const sal_uInt8 cFieldLock
HashMap_OWString_Interface aMap
sal_Int16 nId
sal_uInt32 Id
sal_uInt16 yExt
sal_uInt16 mapMode
sal_uInt16 xExt
An RTF stack is similar to std::stack, except that it has an operator[].
void push(RTFParserState const &rState)
Reference< XFrame > xFrame
unsigned char sal_uInt8
#define SAL_MAX_UINT16
sal_uInt16 sal_Unicode
signed char sal_Int8
ResultType type
size_t pos
sal_Int32 nLength