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