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"
12 #include <com/sun/star/embed/XEmbeddedObject.hpp>
13 #include <com/sun/star/beans/PropertyAttribute.hpp>
14 #include <com/sun/star/io/WrongFormatException.hpp>
15 #include <com/sun/star/lang/XServiceInfo.hpp>
16 #include <com/sun/star/text/WrapTextMode.hpp>
17 #include <com/sun/star/text/TextContentAnchorType.hpp>
18 #include <o3tl/clamp.hxx>
19 #include <unotools/ucbstreamhelper.hxx>
20 #include <unotools/streamwrap.hxx>
21 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
22 #include <filter/msfilter/util.hxx>
23 #include <filter/msfilter/rtfutil.hxx>
24 #include <comphelper/string.hxx>
25 #include <tools/globname.hxx>
26 #include <tools/datetimeutils.hxx>
27 #include <comphelper/classids.hxx>
28 #include <comphelper/embeddedobjectcontainer.hxx>
29 #include <sfx2/sfxbasemodel.hxx>
30 #include <sfx2/classificationhelper.hxx>
31 #include <oox/mathml/import.hxx>
32 #include <ooxml/resourceids.hxx>
33 #include <oox/token/namespaces.hxx>
34 #include <oox/drawingml/drawingmltypes.hxx>
35 #include <rtl/uri.hxx>
36 #include <rtl/tencinfo.h>
37 #include <sal/log.hxx>
38 #include <osl/diagnose.h>
39 #include <oox/helper/graphichelper.hxx>
40 #include <vcl/wmfexternal.hxx>
41 #include <vcl/graph.hxx>
42 #include "rtfsdrimport.hxx"
43 #include "rtfreferenceproperties.hxx"
44 #include "rtfskipdestination.hxx"
45 #include "rtftokenizer.hxx"
46 #include "rtflookahead.hxx"
48 using namespace com::sun::star
;
52 /// Returns an util::DateTime from a 'YYYY. MM. DD.' string.
53 util::DateTime
getDateTimeFromUserProp(const OUString
& rString
)
56 sal_Int32 nLen
= rString
.getLength();
59 aRet
.Year
= rString
.copy(0, 4).toInt32();
61 if (nLen
>= 8 && rString
.match(". ", 4))
63 aRet
.Month
= rString
.copy(6, 2).toInt32();
65 if (nLen
>= 12 && rString
.match(". ", 8))
66 aRet
.Day
= rString
.copy(10, 2).toInt32();
71 } // anonymous namespace
73 namespace writerfilter
77 Id
getParagraphBorder(sal_uInt32 nIndex
)
79 static const Id aBorderIds
[] = { NS_ooxml::LN_CT_PBdr_top
, NS_ooxml::LN_CT_PBdr_left
,
80 NS_ooxml::LN_CT_PBdr_bottom
, NS_ooxml::LN_CT_PBdr_right
};
82 return aBorderIds
[nIndex
];
85 void putNestedAttribute(RTFSprms
& rSprms
, Id nParent
, Id nId
, const RTFValue::Pointer_t
& pValue
,
86 RTFOverwrite eOverwrite
, bool bAttribute
)
88 RTFValue::Pointer_t pParent
= rSprms
.find(nParent
, /*bFirst=*/true, /*bForWrite=*/true);
92 if (nParent
== NS_ooxml::LN_CT_TcPrBase_shd
)
94 // RTF default is 'auto', see writerfilter::dmapper::CellColorHandler
95 aAttributes
.set(NS_ooxml::LN_CT_Shd_color
, new RTFValue(sal_uInt32(COL_AUTO
)));
96 aAttributes
.set(NS_ooxml::LN_CT_Shd_fill
, new RTFValue(sal_uInt32(COL_AUTO
)));
98 auto pParentValue
= new RTFValue(aAttributes
);
99 rSprms
.set(nParent
, pParentValue
, eOverwrite
);
100 pParent
= pParentValue
;
102 RTFSprms
& rAttributes
= (bAttribute
? pParent
->getAttributes() : pParent
->getSprms());
103 rAttributes
.set(nId
, pValue
, eOverwrite
);
106 void putNestedSprm(RTFSprms
& rSprms
, Id nParent
, Id nId
, const RTFValue::Pointer_t
& pValue
,
107 RTFOverwrite eOverwrite
)
109 putNestedAttribute(rSprms
, nParent
, nId
, pValue
, eOverwrite
, false);
112 RTFValue::Pointer_t
getNestedAttribute(RTFSprms
& rSprms
, Id nParent
, Id nId
)
114 RTFValue::Pointer_t pParent
= rSprms
.find(nParent
);
116 return RTFValue::Pointer_t();
117 RTFSprms
& rAttributes
= pParent
->getAttributes();
118 return rAttributes
.find(nId
);
121 RTFValue::Pointer_t
getNestedSprm(RTFSprms
& rSprms
, Id nParent
, Id nId
)
123 RTFValue::Pointer_t pParent
= rSprms
.find(nParent
);
125 return RTFValue::Pointer_t();
126 RTFSprms
& rInner
= pParent
->getSprms();
127 return rInner
.find(nId
);
130 bool eraseNestedAttribute(RTFSprms
& rSprms
, Id nParent
, Id nId
)
132 RTFValue::Pointer_t pParent
= rSprms
.find(nParent
);
134 // It doesn't even have a parent, we're done.
136 RTFSprms
& rAttributes
= pParent
->getAttributes();
137 return rAttributes
.erase(nId
);
140 RTFSprms
& getLastAttributes(RTFSprms
& rSprms
, Id nId
)
142 RTFValue::Pointer_t p
= rSprms
.find(nId
);
143 if (p
.get() && !p
->getSprms().empty())
144 return p
->getSprms().back().second
->getAttributes();
146 SAL_WARN("writerfilter.rtf", "trying to set property when no type is defined");
150 void putBorderProperty(RTFStack
& aStates
, Id nId
, const RTFValue::Pointer_t
& pValue
)
152 RTFSprms
* pAttributes
= nullptr;
153 if (aStates
.top().nBorderState
== RTFBorderState::PARAGRAPH_BOX
)
154 for (int i
= 0; i
< 4; i
++)
156 RTFValue::Pointer_t p
= aStates
.top().aParagraphSprms
.find(getParagraphBorder(i
));
159 RTFSprms
& rAttributes
= p
->getAttributes();
160 rAttributes
.set(nId
, pValue
);
163 else if (aStates
.top().nBorderState
== RTFBorderState::CHARACTER
)
165 RTFValue::Pointer_t pPointer
166 = aStates
.top().aCharacterSprms
.find(NS_ooxml::LN_EG_RPrBase_bdr
);
169 RTFSprms
& rAttributes
= pPointer
->getAttributes();
170 rAttributes
.set(nId
, pValue
);
173 // Attributes of the last border type
174 else if (aStates
.top().nBorderState
== RTFBorderState::PARAGRAPH
)
176 = &getLastAttributes(aStates
.top().aParagraphSprms
, NS_ooxml::LN_CT_PrBase_pBdr
);
177 else if (aStates
.top().nBorderState
== RTFBorderState::CELL
)
179 = &getLastAttributes(aStates
.top().aTableCellSprms
, NS_ooxml::LN_CT_TcPrBase_tcBorders
);
180 else if (aStates
.top().nBorderState
== RTFBorderState::PAGE
)
181 pAttributes
= &getLastAttributes(aStates
.top().aSectionSprms
,
182 NS_ooxml::LN_EG_SectPrContents_pgBorders
);
184 pAttributes
->set(nId
, pValue
);
187 OString
DTTM22OString(long nDTTM
)
189 return DateTimeToOString(msfilter::util::DTTM2DateTime(nDTTM
));
192 static RTFSprms
lcl_getBookmarkProperties(int nPos
, OUString
& rString
)
194 RTFSprms aAttributes
;
195 auto pPos
= new RTFValue(nPos
);
196 if (!rString
.isEmpty())
198 // If present, this should be sent first.
199 auto pString
= new RTFValue(rString
);
200 aAttributes
.set(NS_ooxml::LN_CT_Bookmark_name
, pString
);
202 aAttributes
.set(NS_ooxml::LN_CT_MarkupRangeBookmark_id
, pPos
);
206 const char* keywordToString(RTFKeyword nKeyword
)
208 for (int i
= 0; i
< nRTFControlWords
; i
++)
210 if (nKeyword
== aRTFControlWords
[i
].nIndex
)
211 return aRTFControlWords
[i
].sKeyword
;
216 static util::DateTime
lcl_getDateTime(RTFParserState
const& aState
)
218 return { 0 /*100sec*/, 0 /*sec*/, aState
.nMinute
, aState
.nHour
,
219 aState
.nDay
, aState
.nMonth
, aState
.nYear
, false };
222 static void lcl_DestinationToMath(OUStringBuffer
* pDestinationText
,
223 oox::formulaimport::XmlStreamBuilder
& rMathBuffer
, bool& rMathNor
)
225 if (!pDestinationText
)
227 OUString aStr
= pDestinationText
->makeStringAndClear();
230 rMathBuffer
.appendOpeningTag(M_TOKEN(r
));
233 rMathBuffer
.appendOpeningTag(M_TOKEN(rPr
));
234 // Same as M_TOKEN(lit)
235 rMathBuffer
.appendOpeningTag(M_TOKEN(nor
));
236 rMathBuffer
.appendClosingTag(M_TOKEN(nor
));
237 rMathBuffer
.appendClosingTag(M_TOKEN(rPr
));
240 rMathBuffer
.appendOpeningTag(M_TOKEN(t
));
241 rMathBuffer
.appendCharacters(aStr
);
242 rMathBuffer
.appendClosingTag(M_TOKEN(t
));
243 rMathBuffer
.appendClosingTag(M_TOKEN(r
));
246 RTFDocumentImpl::RTFDocumentImpl(uno::Reference
<uno::XComponentContext
> const& xContext
,
247 uno::Reference
<io::XInputStream
> const& xInputStream
,
248 uno::Reference
<lang::XComponent
> const& xDstDoc
,
249 uno::Reference
<frame::XFrame
> const& xFrame
,
250 uno::Reference
<task::XStatusIndicator
> const& xStatusIndicator
,
251 const utl::MediaDescriptor
& rMediaDescriptor
)
252 : m_xContext(xContext
)
253 , m_xInputStream(xInputStream
)
256 , m_xStatusIndicator(xStatusIndicator
)
257 , m_pMapperStream(nullptr)
258 , m_aDefaultState(this)
259 , m_bSkipUnknown(false)
261 , m_bFirstRunException(false)
264 , m_bNeedCrOrig(false)
266 , m_bNeedFinalPar(false)
268 , m_nTopLevelCells(0)
269 , m_nInheritingCells(0)
271 , m_nTopLevelTRLeft(0)
272 , m_nNestedCurrentCellX(0)
273 , m_nTopLevelCurrentCellX(0)
274 , m_nBackupTopLevelCurrentCellX(0)
275 , m_aTableBufferStack(1) // create top-level buffer already
276 , m_pSuperstream(nullptr)
278 , m_nGroupStartPos(0)
279 , m_nFormFieldType(RTFFormFieldType::NONE
)
281 , m_nCurrentFontIndex(0)
282 , m_nCurrentEncoding(-1)
283 , m_nDefaultFontIndex(-1)
284 , m_nCurrentStyleIndex(0)
285 , m_bFormField(false)
287 , m_bIgnoreNextContSectBreak(false)
288 , m_nResetBreakOnSectBreak(RTF_invalid
)
289 , m_bNeedSect(false) // done by checkFirstRun
290 , m_bWasInFrame(false)
291 , m_bHadPicture(false)
294 , m_nListPictureId(0)
295 , m_bIsNewDoc(!rMediaDescriptor
.getUnpackedValueOrDefault("InsertMode", false))
296 , m_rMediaDescriptor(rMediaDescriptor
)
297 , m_hasRHeader(false)
298 , m_hasFHeader(false)
299 , m_hasRFooter(false)
300 , m_hasFFooter(false)
301 , m_bAfterCellBeforeRow(false)
303 OSL_ASSERT(xInputStream
.is());
304 m_pInStream
= utl::UcbStreamHelper::CreateStream(xInputStream
, true);
306 m_xModelFactory
.set(m_xDstDoc
, uno::UNO_QUERY
);
308 uno::Reference
<document::XDocumentPropertiesSupplier
> xDocumentPropertiesSupplier(
309 m_xDstDoc
, uno::UNO_QUERY
);
310 if (xDocumentPropertiesSupplier
.is())
311 m_xDocumentProperties
.set(xDocumentPropertiesSupplier
->getDocumentProperties(),
314 m_pGraphicHelper
.reset(new oox::GraphicHelper(m_xContext
, xFrame
, oox::StorageRef()));
316 m_pTokenizer
= new RTFTokenizer(*this, m_pInStream
.get(), m_xStatusIndicator
);
317 m_pSdrImport
= new RTFSdrImport(*this, m_xDstDoc
);
320 RTFDocumentImpl::~RTFDocumentImpl() = default;
322 SvStream
& RTFDocumentImpl::Strm() { return *m_pInStream
; }
324 void RTFDocumentImpl::setSuperstream(RTFDocumentImpl
* pSuperstream
)
326 m_pSuperstream
= pSuperstream
;
329 bool RTFDocumentImpl::isSubstream() const { return m_pSuperstream
!= nullptr; }
331 void RTFDocumentImpl::finishSubstream() { checkUnicode(/*bUnicode =*/true, /*bHex =*/true); }
333 void RTFDocumentImpl::resolveSubstream(std::size_t nPos
, Id nId
)
335 resolveSubstream(nPos
, nId
, OUString());
337 void RTFDocumentImpl::resolveSubstream(std::size_t nPos
, Id nId
, OUString
const& rIgnoreFirst
)
339 sal_uInt64
const nCurrent
= Strm().Tell();
340 // Seek to header position, parse, then seek back.
341 auto pImpl
= new RTFDocumentImpl(m_xContext
, m_xInputStream
, m_xDstDoc
, m_xFrame
,
342 m_xStatusIndicator
, m_rMediaDescriptor
);
343 pImpl
->setSuperstream(this);
344 pImpl
->m_nStreamType
= nId
;
345 pImpl
->m_aIgnoreFirst
= rIgnoreFirst
;
346 if (!m_aAuthor
.isEmpty())
348 pImpl
->m_aAuthor
= m_aAuthor
;
351 if (!m_aAuthorInitials
.isEmpty())
353 pImpl
->m_aAuthorInitials
= m_aAuthorInitials
;
354 m_aAuthorInitials
.clear();
356 pImpl
->m_nDefaultFontIndex
= m_nDefaultFontIndex
;
357 pImpl
->Strm().Seek(nPos
);
358 SAL_INFO("writerfilter.rtf", "substream start");
359 Mapper().substream(nId
, pImpl
);
360 SAL_INFO("writerfilter.rtf", "substream end");
361 Strm().Seek(nCurrent
);
364 void RTFDocumentImpl::outputSettingsTable()
366 writerfilter::Reference
<Properties
>::Pointer_t pProp
367 = new RTFReferenceProperties(m_aSettingsTableAttributes
, m_aSettingsTableSprms
);
368 RTFReferenceTable::Entries_t aSettingsTableEntries
;
369 aSettingsTableEntries
.insert(std::make_pair(0, pProp
));
370 writerfilter::Reference
<Table
>::Pointer_t pTable
= new RTFReferenceTable(aSettingsTableEntries
);
371 Mapper().table(NS_ooxml::LN_settings_settings
, pTable
);
374 void RTFDocumentImpl::checkFirstRun()
378 outputSettingsTable();
379 // start initial paragraph
381 assert(!m_bNeedSect
|| m_bFirstRunException
);
382 setNeedSect(true); // first call that succeeds
384 // set the requested default font, if there are none
385 RTFValue::Pointer_t pFont
386 = getNestedAttribute(m_aDefaultState
.aCharacterSprms
, NS_ooxml::LN_EG_RPrBase_rFonts
,
387 NS_ooxml::LN_CT_Fonts_ascii
);
388 RTFValue::Pointer_t pCurrentFont
389 = getNestedAttribute(m_aStates
.top().aCharacterSprms
, NS_ooxml::LN_EG_RPrBase_rFonts
,
390 NS_ooxml::LN_CT_Fonts_ascii
);
391 if (pFont
&& !pCurrentFont
)
392 putNestedAttribute(m_aStates
.top().aCharacterSprms
, NS_ooxml::LN_EG_RPrBase_rFonts
,
393 NS_ooxml::LN_CT_Fonts_ascii
, pFont
);
397 void RTFDocumentImpl::setNeedPar(bool bNeedPar
) { m_bNeedPar
= bNeedPar
; }
399 void RTFDocumentImpl::setNeedSect(bool bNeedSect
)
401 if (!m_bNeedSect
&& bNeedSect
&& m_bFirstRun
)
403 RTFLookahead
aLookahead(Strm(), m_pTokenizer
->getGroupStart());
404 if (aLookahead
.hasTable() && aLookahead
.hasColumns())
406 m_bFirstRunException
= true;
410 // ignore setting before checkFirstRun - every keyword calls setNeedSect!
411 // except the case of a table in a multicolumn section
412 if (!m_bNeedSect
&& bNeedSect
&& (!m_bFirstRun
|| m_bFirstRunException
))
414 if (!m_pSuperstream
) // no sections in header/footer!
416 Mapper().startSectionGroup();
418 // set flag in substream too - otherwise multiple startParagraphGroup
419 m_bNeedSect
= bNeedSect
;
420 Mapper().startParagraphGroup();
423 else if (m_bNeedSect
&& !bNeedSect
)
425 m_bNeedSect
= bNeedSect
;
429 /// Copy rProps to rStyleAttributes and rStyleSprms, but in case of nested sprms, copy their children as toplevel sprms/attributes.
430 static void lcl_copyFlatten(RTFReferenceProperties
& rProps
, RTFSprms
& rStyleAttributes
,
431 RTFSprms
& rStyleSprms
)
433 for (auto& rSprm
: rProps
.getSprms())
435 // createStyleProperties() puts properties to rPr, but here we need a flat list.
436 if (rSprm
.first
== NS_ooxml::LN_CT_Style_rPr
)
438 // rPr can have both attributes and SPRMs, copy over both types.
439 RTFSprms
& rRPrSprms
= rSprm
.second
->getSprms();
440 for (auto& rRPrSprm
: rRPrSprms
)
441 rStyleSprms
.set(rRPrSprm
.first
, rRPrSprm
.second
);
443 RTFSprms
& rRPrAttributes
= rSprm
.second
->getAttributes();
444 for (auto& rRPrAttribute
: rRPrAttributes
)
445 rStyleAttributes
.set(rRPrAttribute
.first
, rRPrAttribute
.second
);
448 rStyleSprms
.set(rSprm
.first
, rSprm
.second
);
451 RTFSprms
& rAttributes
= rProps
.getAttributes();
452 for (auto& rAttribute
: rAttributes
)
453 rStyleAttributes
.set(rAttribute
.first
, rAttribute
.second
);
456 writerfilter::Reference
<Properties
>::Pointer_t
457 RTFDocumentImpl::getProperties(RTFSprms
& rAttributes
, RTFSprms
const& rSprms
, Id nStyleType
)
459 RTFSprms
aSprms(rSprms
);
460 RTFValue::Pointer_t pAbstractList
;
461 int nAbstractListId
= -1;
462 RTFValue::Pointer_t pNumId
463 = getNestedSprm(aSprms
, NS_ooxml::LN_CT_PPrBase_numPr
, NS_ooxml::LN_CT_NumPr_numId
);
466 // We have a numbering, look up the abstract list for property
467 // deduplication and duplication.
468 auto itNumId
= m_aListOverrideTable
.find(pNumId
->getInt());
469 if (itNumId
!= m_aListOverrideTable
.end())
471 nAbstractListId
= itNumId
->second
;
472 auto itAbstract
= m_aListTable
.find(nAbstractListId
);
473 if (itAbstract
!= m_aListTable
.end())
474 pAbstractList
= itAbstract
->second
;
480 auto it
= m_aInvalidListTableFirstIndents
.find(nAbstractListId
);
481 if (it
!= m_aInvalidListTableFirstIndents
.end())
482 aSprms
.deduplicateList(it
->second
);
486 if (!m_aStates
.empty())
487 nStyle
= m_aStates
.top().nCurrentStyleIndex
;
488 auto it
= m_aStyleTableEntries
.find(nStyle
);
489 if (it
!= m_aStyleTableEntries
.end())
491 // cloneAndDeduplicate() wants to know about only a single "style", so
492 // let's merge paragraph and character style properties here.
493 auto itChar
= m_aStyleTableEntries
.end();
494 if (!m_aStates
.empty())
496 int nCharStyle
= m_aStates
.top().nCurrentCharacterStyleIndex
;
497 itChar
= m_aStyleTableEntries
.find(nCharStyle
);
500 RTFSprms aStyleSprms
;
501 RTFSprms aStyleAttributes
;
502 // Ensure the paragraph style is a flat list.
503 // Take paragraph style into account for character properties as well,
504 // as paragraph style may contain character properties.
505 RTFReferenceProperties
& rProps
= *static_cast<RTFReferenceProperties
*>(it
->second
.get());
506 lcl_copyFlatten(rProps
, aStyleAttributes
, aStyleSprms
);
508 if (itChar
!= m_aStyleTableEntries
.end())
510 // Found active character style, then update aStyleSprms/Attributes.
511 if (!nStyleType
|| nStyleType
== NS_ooxml::LN_Value_ST_StyleType_character
)
513 RTFReferenceProperties
& rCharProps
514 = *static_cast<RTFReferenceProperties
*>(itChar
->second
.get());
515 lcl_copyFlatten(rCharProps
, aStyleAttributes
, aStyleSprms
);
519 // Get rid of direct formatting what is already in the style.
520 RTFSprms
const sprms(aSprms
.cloneAndDeduplicate(aStyleSprms
, nStyleType
, true));
521 RTFSprms
const attributes(
522 rAttributes
.cloneAndDeduplicate(aStyleAttributes
, nStyleType
, true));
523 return new RTFReferenceProperties(attributes
, sprms
);
527 aSprms
.duplicateList(pAbstractList
);
528 writerfilter::Reference
<Properties
>::Pointer_t pRet
529 = new RTFReferenceProperties(rAttributes
, aSprms
);
533 void RTFDocumentImpl::checkNeedPap()
537 m_bNeedPap
= false; // reset early, so we can avoid recursion when calling ourselves
539 if (m_aStates
.empty())
542 if (!m_aStates
.top().pCurrentBuffer
)
544 writerfilter::Reference
<Properties
>::Pointer_t
const pParagraphProperties(
545 getProperties(m_aStates
.top().aParagraphAttributes
, m_aStates
.top().aParagraphSprms
,
546 NS_ooxml::LN_Value_ST_StyleType_paragraph
));
548 // Writer will ignore a page break before a text frame, so guard it with empty paragraphs
549 bool hasBreakBeforeFrame
550 = m_aStates
.top().aFrame
.hasProperties()
552 .aParagraphSprms
.find(NS_ooxml::LN_CT_PPrBase_pageBreakBefore
)
554 if (hasBreakBeforeFrame
)
556 dispatchSymbol(RTF_PAR
);
559 Mapper().props(pParagraphProperties
);
560 if (hasBreakBeforeFrame
)
561 dispatchSymbol(RTF_PAR
);
563 if (m_aStates
.top().aFrame
.hasProperties())
565 writerfilter::Reference
<Properties
>::Pointer_t
const pFrameProperties(
566 new RTFReferenceProperties(RTFSprms(), m_aStates
.top().aFrame
.getSprms()));
567 Mapper().props(pFrameProperties
);
572 auto pValue
= new RTFValue(m_aStates
.top().aParagraphAttributes
,
573 m_aStates
.top().aParagraphSprms
);
574 bufferProperties(*m_aStates
.top().pCurrentBuffer
, pValue
, nullptr);
579 void RTFDocumentImpl::runProps()
581 if (!m_aStates
.top().pCurrentBuffer
)
583 Reference
<Properties
>::Pointer_t
const pProperties
584 = getProperties(m_aStates
.top().aCharacterAttributes
, m_aStates
.top().aCharacterSprms
,
585 NS_ooxml::LN_Value_ST_StyleType_character
);
586 Mapper().props(pProperties
);
591 = new RTFValue(m_aStates
.top().aCharacterAttributes
, m_aStates
.top().aCharacterSprms
);
592 bufferProperties(*m_aStates
.top().pCurrentBuffer
, pValue
, nullptr);
595 // Delete the sprm, so the trackchange range will be started only once.
596 // OTOH set a boolean flag, so we'll know we need to end the range later.
597 RTFValue::Pointer_t pTrackchange
598 = m_aStates
.top().aCharacterSprms
.find(NS_ooxml::LN_trackchange
);
601 m_aStates
.top().bStartedTrackchange
= true;
602 m_aStates
.top().aCharacterSprms
.erase(NS_ooxml::LN_trackchange
);
606 void RTFDocumentImpl::runBreak()
608 sal_uInt8
const sBreak
[] = { 0xd };
609 Mapper().text(sBreak
, 1);
613 void RTFDocumentImpl::tableBreak()
616 Mapper().endParagraphGroup();
617 Mapper().startParagraphGroup();
620 void RTFDocumentImpl::parBreak()
624 // end previous paragraph
625 Mapper().startCharacterGroup();
627 Mapper().endCharacterGroup();
628 Mapper().endParagraphGroup();
630 m_bHadPicture
= false;
633 Mapper().startParagraphGroup();
636 void RTFDocumentImpl::sectBreak(bool bFinal
)
638 SAL_INFO("writerfilter.rtf",
639 OSL_THIS_FUNC
<< ": final? " << bFinal
<< ", needed? " << m_bNeedSect
);
640 bool bNeedSect
= m_bNeedSect
;
641 RTFValue::Pointer_t pBreak
642 = m_aStates
.top().aSectionSprms
.find(NS_ooxml::LN_EG_SectPrContents_type
);
646 == static_cast<sal_Int32
>(NS_ooxml::LN_Value_ST_SectionMark_continuous
);
647 // If there is no paragraph in this section, then insert a dummy one, as required by Writer,
648 // unless this is the end of the doc, we had nothing since the last section break and this is not a continuous one.
649 // Also, when pasting, it's fine to not have any paragraph inside the document at all.
650 if (m_bNeedPar
&& !(bFinal
&& !m_bNeedSect
&& !bContinuous
) && !isSubstream() && m_bIsNewDoc
)
651 dispatchSymbol(RTF_PAR
);
652 // It's allowed to not have a non-table paragraph at the end of an RTF doc, add it now if required.
653 if (m_bNeedFinalPar
&& bFinal
)
655 dispatchFlag(RTF_PARD
);
656 dispatchSymbol(RTF_PAR
);
657 m_bNeedSect
= bNeedSect
;
659 while (!m_nHeaderFooterPositions
.empty())
661 std::pair
<Id
, std::size_t> aPair
= m_nHeaderFooterPositions
.front();
662 m_nHeaderFooterPositions
.pop();
663 resolveSubstream(aPair
.second
, aPair
.first
);
666 // Normally a section break at the end of the doc is necessary. Unless the
667 // last control word in the document is a section break itself.
668 if (!bNeedSect
|| !m_bHadSect
)
670 // In case the last section is a continuous one, we don't need to output a section break.
671 if (bFinal
&& bContinuous
)
672 m_aStates
.top().aSectionSprms
.erase(NS_ooxml::LN_EG_SectPrContents_type
);
675 // Section properties are a paragraph sprm.
676 auto pValue
= new RTFValue(m_aStates
.top().aSectionAttributes
, m_aStates
.top().aSectionSprms
);
677 RTFSprms aAttributes
;
679 aSprms
.set(NS_ooxml::LN_CT_PPr_sectPr
, pValue
);
680 writerfilter::Reference
<Properties
>::Pointer_t pProperties
681 = new RTFReferenceProperties(aAttributes
, aSprms
);
683 if (bFinal
&& !m_pSuperstream
)
684 // This is the end of the document, not just the end of e.g. a header.
685 // This makes sure that dmapper can set DontBalanceTextColumns=true for this section if necessary.
686 Mapper().markLastSectionGroup();
688 // The trick is that we send properties of the previous section right now, which will be exactly what dmapper expects.
689 Mapper().props(pProperties
);
690 Mapper().endParagraphGroup();
695 m_hasFHeader
= false;
696 m_hasRHeader
= false;
697 m_hasRFooter
= false;
698 m_hasFFooter
= false;
699 Mapper().endSectionGroup();
705 Color
RTFDocumentImpl::getColorTable(sal_uInt32 nIndex
)
709 if (nIndex
< m_aColorTable
.size())
710 return m_aColorTable
[nIndex
];
714 return m_pSuperstream
->getColorTable(nIndex
);
717 rtl_TextEncoding
RTFDocumentImpl::getEncoding(int nFontIndex
)
721 auto it
= m_aFontEncodings
.find(nFontIndex
);
722 if (it
!= m_aFontEncodings
.end())
723 // We have a font encoding associated to this font.
725 if (m_aDefaultState
.nCurrentEncoding
!= rtl_getTextEncodingFromWindowsCharset(0))
726 // We have a default encoding.
727 return m_aDefaultState
.nCurrentEncoding
;
728 // Guess based on locale.
729 return msfilter::util::getBestTextEncodingFromLocale(
730 Application::GetSettings().GetLanguageTag().getLocale());
733 return m_pSuperstream
->getEncoding(nFontIndex
);
736 OUString
RTFDocumentImpl::getFontName(int nIndex
)
739 return m_aFontNames
[nIndex
];
741 return m_pSuperstream
->getFontName(nIndex
);
744 int RTFDocumentImpl::getFontIndex(int nIndex
)
747 return std::find(m_aFontIndexes
.begin(), m_aFontIndexes
.end(), nIndex
)
748 - m_aFontIndexes
.begin();
750 return m_pSuperstream
->getFontIndex(nIndex
);
753 OUString
RTFDocumentImpl::getStyleName(int nIndex
)
758 if (m_aStyleNames
.find(nIndex
) != m_aStyleNames
.end())
759 aRet
= m_aStyleNames
[nIndex
];
763 return m_pSuperstream
->getStyleName(nIndex
);
766 Id
RTFDocumentImpl::getStyleType(int nIndex
)
771 if (m_aStyleTypes
.find(nIndex
) != m_aStyleTypes
.end())
772 nRet
= m_aStyleTypes
[nIndex
];
776 return m_pSuperstream
->getStyleType(nIndex
);
779 RTFParserState
& RTFDocumentImpl::getDefaultState()
782 return m_aDefaultState
;
784 return m_pSuperstream
->getDefaultState();
787 oox::GraphicHelper
& RTFDocumentImpl::getGraphicHelper() { return *m_pGraphicHelper
; }
789 bool RTFDocumentImpl::isStyleSheetImport()
791 if (m_aStates
.empty())
793 Destination eDestination
= m_aStates
.top().eDestination
;
794 return eDestination
== Destination::STYLESHEET
|| eDestination
== Destination::STYLEENTRY
;
797 void RTFDocumentImpl::resolve(Stream
& rMapper
)
799 m_pMapperStream
= &rMapper
;
800 switch (m_pTokenizer
->resolveParse())
803 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: finished without errors");
805 case RTFError::GROUP_UNDER
:
806 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unmatched '}'");
808 case RTFError::GROUP_OVER
:
809 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unmatched '{'");
810 throw io::WrongFormatException(m_pTokenizer
->getPosition());
812 case RTFError::UNEXPECTED_EOF
:
813 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unexpected end of file");
814 throw io::WrongFormatException(m_pTokenizer
->getPosition());
816 case RTFError::HEX_INVALID
:
817 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: invalid hex char");
818 throw io::WrongFormatException(m_pTokenizer
->getPosition());
820 case RTFError::CHAR_OVER
:
821 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: characters after last '}'");
823 case RTFError::CLASSIFICATION
:
824 SAL_INFO("writerfilter.rtf",
825 "RTFDocumentImpl::resolve: classification prevented paste");
830 void RTFDocumentImpl::resolvePict(bool const bInline
, uno::Reference
<drawing::XShape
> const& rShape
)
832 SvMemoryStream aStream
;
833 SvStream
* pStream
= nullptr;
834 if (!m_pBinaryData
.get())
837 int b
= 0, count
= 2;
839 // Feed the destination text to a stream.
840 OString aStr
= OUStringToOString(m_aStates
.top().aDestinationText
.makeStringAndClear(),
841 RTL_TEXTENCODING_ASCII_US
);
842 for (int i
= 0; i
< aStr
.getLength(); ++i
)
845 if (ch
!= 0x0d && ch
!= 0x0a && ch
!= 0x20)
848 sal_Int8 parsed
= msfilter::rtfutil::AsHex(ch
);
855 aStream
.WriteChar(static_cast<char>(b
));
863 pStream
= m_pBinaryData
.get();
865 if (!pStream
->Tell())
866 // No destination text? Then we'll get it later.
869 SvMemoryStream aDIBStream
;
870 if (m_aStates
.top().aPicture
.eStyle
== RTFBmpStyle::DIBITMAP
)
872 // Construct a BITMAPFILEHEADER structure before the real data.
873 SvStream
& rBodyStream
= *pStream
;
874 aDIBStream
.WriteChar('B');
875 aDIBStream
.WriteChar('M');
876 // The size of the real data.
877 aDIBStream
.WriteUInt32(rBodyStream
.Tell());
879 aDIBStream
.WriteUInt32(0);
880 // The offset of the real data, i.e. the size of the header, including this number.
881 aDIBStream
.WriteUInt32(14);
883 aDIBStream
.WriteStream(rBodyStream
);
884 pStream
= &aDIBStream
;
887 // Store, and get its URL.
889 uno::Reference
<io::XInputStream
> xInputStream(new utl::OInputStreamWrapper(pStream
));
890 WmfExternal aExtHeader
;
891 aExtHeader
.mapMode
= m_aStates
.top().aPicture
.eWMetafile
;
892 aExtHeader
.xExt
= sal_uInt16(
893 o3tl::clamp
<sal_Int32
>(m_aStates
.top().aPicture
.nWidth
, 0,
894 SAL_MAX_UINT16
)); //TODO: better way to handle out-of-bounds values?
895 aExtHeader
.yExt
= sal_uInt16(
896 o3tl::clamp
<sal_Int32
>(m_aStates
.top().aPicture
.nHeight
, 0,
897 SAL_MAX_UINT16
)); //TODO: better way to handle out-of-bounds values?
898 WmfExternal
* pExtHeader
= &aExtHeader
;
899 uno::Reference
<lang::XServiceInfo
> xServiceInfo(m_aStates
.top().aDrawingObject
.xShape
,
901 if (xServiceInfo
.is() && xServiceInfo
->supportsService("com.sun.star.text.TextFrame"))
902 pExtHeader
= nullptr;
904 uno::Reference
<graphic::XGraphic
> xGraphic
;
905 xGraphic
= m_pGraphicHelper
->importGraphic(xInputStream
, pExtHeader
);
907 if (m_aStates
.top().aPicture
.eStyle
!= RTFBmpStyle::NONE
)
909 // In case of PNG/JPEG, the real size is known, don't use the values
910 // provided by picw and pich.
912 Graphic
aGraphic(xGraphic
);
913 Size
aSize(aGraphic
.GetPrefSize());
914 MapMode
aMap(MapUnit::Map100thMM
);
915 if (aGraphic
.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel
)
916 aSize
= Application::GetDefaultDevice()->PixelToLogic(aSize
, aMap
);
918 aSize
= OutputDevice::LogicToLogic(aSize
, aGraphic
.GetPrefMapMode(), aMap
);
919 m_aStates
.top().aPicture
.nWidth
= aSize
.Width();
920 m_aStates
.top().aPicture
.nHeight
= aSize
.Height();
923 // Wrap it in an XShape.
924 uno::Reference
<drawing::XShape
> xShape(rShape
);
927 uno::Reference
<lang::XServiceInfo
> xSI(xShape
, uno::UNO_QUERY_THROW
);
928 if (!xSI
->supportsService("com.sun.star.drawing.GraphicObjectShape"))
930 // it's sometimes an error to get here - but it's possible to have
931 // a \pict inside the \shptxt of a \shp of shapeType 202 "TextBox"
932 // and in that case xShape is the text frame; we actually need a
933 // new GraphicObject then (example: fdo37691-1.rtf)
934 SAL_INFO("writerfilter.rtf",
935 "cannot set graphic on existing shape, creating a new GraphicObjectShape");
941 if (m_xModelFactory
.is())
942 xShape
.set(m_xModelFactory
->createInstance("com.sun.star.drawing.GraphicObjectShape"),
944 uno::Reference
<drawing::XDrawPageSupplier
> const xDrawSupplier(m_xDstDoc
, uno::UNO_QUERY
);
945 if (xDrawSupplier
.is())
947 uno::Reference
<drawing::XShapes
> xShapes(xDrawSupplier
->getDrawPage(), uno::UNO_QUERY
);
949 xShapes
->add(xShape
);
953 uno::Reference
<beans::XPropertySet
> xPropertySet(xShape
, uno::UNO_QUERY
);
955 if (xPropertySet
.is())
956 xPropertySet
->setPropertyValue("Graphic", uno::Any(xGraphic
));
958 // check if the picture is in an OLE object and if the \objdata element is used
959 // (see RTF_OBJECT in RTFDocumentImpl::dispatchDestination)
962 // Set the object size
964 aSize
.Width
= (m_aStates
.top().aPicture
.nGoalWidth
? m_aStates
.top().aPicture
.nGoalWidth
965 : m_aStates
.top().aPicture
.nWidth
);
966 aSize
.Height
= (m_aStates
.top().aPicture
.nGoalHeight
? m_aStates
.top().aPicture
.nGoalHeight
967 : m_aStates
.top().aPicture
.nHeight
);
968 xShape
->setSize(aSize
);
970 // Replacement graphic is inline by default, see oox::vml::SimpleShape::implConvertAndInsert().
971 xPropertySet
->setPropertyValue("AnchorType",
972 uno::makeAny(text::TextContentAnchorType_AS_CHARACTER
));
974 auto pShapeValue
= new RTFValue(xShape
);
975 m_aObjectAttributes
.set(NS_ooxml::LN_shape
, pShapeValue
);
979 if (m_aStates
.top().bInListpicture
)
981 // Send the shape directly, no section is started, to additional properties will be ignored anyway.
982 Mapper().startShape(xShape
);
987 // Send it to the dmapper.
989 RTFSprms aAttributes
;
991 RTFSprms aPicAttributes
;
992 auto pShapeValue
= new RTFValue(xShape
);
993 aPicAttributes
.set(NS_ooxml::LN_shape
, pShapeValue
);
995 RTFSprms aGraphicDataAttributes
;
996 RTFSprms aGraphicDataSprms
;
997 auto pPicValue
= new RTFValue(aPicAttributes
);
998 aGraphicDataSprms
.set(NS_ooxml::LN_pic_pic
, pPicValue
);
1000 RTFSprms aGraphicAttributes
;
1001 RTFSprms aGraphicSprms
;
1002 auto pGraphicDataValue
= new RTFValue(aGraphicDataAttributes
, aGraphicDataSprms
);
1003 aGraphicSprms
.set(NS_ooxml::LN_CT_GraphicalObject_graphicData
, pGraphicDataValue
);
1005 auto pGraphicValue
= new RTFValue(aGraphicAttributes
, aGraphicSprms
);
1007 RTFSprms aExtentAttributes
;
1009 nXExt
= (m_aStates
.top().aPicture
.nGoalWidth
? m_aStates
.top().aPicture
.nGoalWidth
1010 : m_aStates
.top().aPicture
.nWidth
);
1011 nYExt
= (m_aStates
.top().aPicture
.nGoalHeight
? m_aStates
.top().aPicture
.nGoalHeight
1012 : m_aStates
.top().aPicture
.nHeight
);
1013 if (m_aStates
.top().aPicture
.nScaleX
!= 100)
1014 nXExt
= (static_cast<long>(m_aStates
.top().aPicture
.nScaleX
)
1015 * (nXExt
- (m_aStates
.top().aPicture
.nCropL
+ m_aStates
.top().aPicture
.nCropR
)))
1017 if (m_aStates
.top().aPicture
.nScaleY
!= 100)
1018 nYExt
= (static_cast<long>(m_aStates
.top().aPicture
.nScaleY
)
1019 * (nYExt
- (m_aStates
.top().aPicture
.nCropT
+ m_aStates
.top().aPicture
.nCropB
)))
1021 if (m_aStates
.top().bInShape
)
1023 // Picture in shape: it looks like pib picture, so we will stretch the picture to shape size (tdf#49893)
1024 nXExt
= m_aStates
.top().aShape
.nRight
- m_aStates
.top().aShape
.nLeft
;
1025 nYExt
= m_aStates
.top().aShape
.nBottom
- m_aStates
.top().aShape
.nTop
;
1027 auto pXExtValue
= new RTFValue(oox::drawingml::convertHmmToEmu(nXExt
));
1028 auto pYExtValue
= new RTFValue(oox::drawingml::convertHmmToEmu(nYExt
));
1029 aExtentAttributes
.set(NS_ooxml::LN_CT_PositiveSize2D_cx
, pXExtValue
);
1030 aExtentAttributes
.set(NS_ooxml::LN_CT_PositiveSize2D_cy
, pYExtValue
);
1031 auto pExtentValue
= new RTFValue(aExtentAttributes
);
1033 RTFSprms aDocprAttributes
;
1034 for (auto& rCharacterAttribute
: m_aStates
.top().aCharacterAttributes
)
1035 if (rCharacterAttribute
.first
== NS_ooxml::LN_CT_NonVisualDrawingProps_name
1036 || rCharacterAttribute
.first
== NS_ooxml::LN_CT_NonVisualDrawingProps_descr
)
1037 aDocprAttributes
.set(rCharacterAttribute
.first
, rCharacterAttribute
.second
);
1038 auto pDocprValue
= new RTFValue(aDocprAttributes
);
1041 RTFSprms aInlineAttributes
;
1042 aInlineAttributes
.set(NS_ooxml::LN_CT_Inline_distT
, new RTFValue(0));
1043 aInlineAttributes
.set(NS_ooxml::LN_CT_Inline_distB
, new RTFValue(0));
1044 aInlineAttributes
.set(NS_ooxml::LN_CT_Inline_distL
, new RTFValue(0));
1045 aInlineAttributes
.set(NS_ooxml::LN_CT_Inline_distR
, new RTFValue(0));
1046 RTFSprms aInlineSprms
;
1047 aInlineSprms
.set(NS_ooxml::LN_CT_Inline_extent
, pExtentValue
);
1048 aInlineSprms
.set(NS_ooxml::LN_CT_Inline_docPr
, pDocprValue
);
1049 aInlineSprms
.set(NS_ooxml::LN_graphic_graphic
, pGraphicValue
);
1051 auto pValue
= new RTFValue(aInlineAttributes
, aInlineSprms
);
1052 aSprms
.set(NS_ooxml::LN_inline_inline
, pValue
);
1057 RTFSprms aAnchorWrapAttributes
;
1058 m_aStates
.top().aShape
.aAnchorAttributes
.set(
1059 NS_ooxml::LN_CT_Anchor_behindDoc
,
1060 new RTFValue((m_aStates
.top().aShape
.bInBackground
) ? 1 : 0));
1061 RTFSprms aAnchorSprms
;
1062 for (auto& rCharacterAttribute
: m_aStates
.top().aCharacterAttributes
)
1064 if (rCharacterAttribute
.first
== NS_ooxml::LN_CT_WrapSquare_wrapText
)
1065 aAnchorWrapAttributes
.set(rCharacterAttribute
.first
, rCharacterAttribute
.second
);
1067 sal_Int32 nWrap
= -1;
1068 for (auto& rCharacterSprm
: m_aStates
.top().aCharacterSprms
)
1070 if (rCharacterSprm
.first
== NS_ooxml::LN_EG_WrapType_wrapNone
1071 || rCharacterSprm
.first
== NS_ooxml::LN_EG_WrapType_wrapTight
)
1073 nWrap
= rCharacterSprm
.first
;
1075 // If there is a wrap polygon prepared by RTFSdrImport, pick it up here.
1076 if (rCharacterSprm
.first
== NS_ooxml::LN_EG_WrapType_wrapTight
1077 && !m_aStates
.top().aShape
.aWrapPolygonSprms
.empty())
1078 rCharacterSprm
.second
->getSprms().set(
1079 NS_ooxml::LN_CT_WrapTight_wrapPolygon
,
1080 new RTFValue(RTFSprms(), m_aStates
.top().aShape
.aWrapPolygonSprms
));
1082 aAnchorSprms
.set(rCharacterSprm
.first
, rCharacterSprm
.second
);
1086 if (m_aStates
.top().aShape
.aWrapSprm
.first
!= 0)
1087 // Replay of a buffered shape, wrap sprm there has priority over
1088 // character sprms of the current state.
1089 aAnchorSprms
.set(m_aStates
.top().aShape
.aWrapSprm
.first
,
1090 m_aStates
.top().aShape
.aWrapSprm
.second
);
1092 aAnchorSprms
.set(NS_ooxml::LN_CT_Anchor_extent
, pExtentValue
);
1093 if (!aAnchorWrapAttributes
.empty() && nWrap
== -1)
1094 aAnchorSprms
.set(NS_ooxml::LN_EG_WrapType_wrapSquare
,
1095 new RTFValue(aAnchorWrapAttributes
));
1097 // See OOXMLFastContextHandler::positionOffset(), we can't just put offset values in an RTFValue.
1098 RTFSprms aPoshAttributes
;
1099 RTFSprms aPoshSprms
;
1100 if (m_aStates
.top().aShape
.nHoriOrientRelationToken
> 0)
1101 aPoshAttributes
.set(NS_ooxml::LN_CT_PosH_relativeFrom
,
1102 new RTFValue(m_aStates
.top().aShape
.nHoriOrientRelationToken
));
1103 if (m_aStates
.top().aShape
.nLeft
!= 0)
1105 Mapper().positionOffset(
1106 OUString::number(oox::drawingml::convertHmmToEmu(m_aStates
.top().aShape
.nLeft
)),
1107 /*bVertical=*/false);
1108 aPoshSprms
.set(NS_ooxml::LN_CT_PosH_posOffset
, new RTFValue());
1110 aAnchorSprms
.set(NS_ooxml::LN_CT_Anchor_positionH
,
1111 new RTFValue(aPoshAttributes
, aPoshSprms
));
1113 RTFSprms aPosvAttributes
;
1114 RTFSprms aPosvSprms
;
1115 if (m_aStates
.top().aShape
.nVertOrientRelationToken
> 0)
1116 aPosvAttributes
.set(NS_ooxml::LN_CT_PosV_relativeFrom
,
1117 new RTFValue(m_aStates
.top().aShape
.nVertOrientRelationToken
));
1118 if (m_aStates
.top().aShape
.nTop
!= 0)
1120 Mapper().positionOffset(
1121 OUString::number(oox::drawingml::convertHmmToEmu(m_aStates
.top().aShape
.nTop
)),
1122 /*bVertical=*/true);
1123 aPosvSprms
.set(NS_ooxml::LN_CT_PosV_posOffset
, new RTFValue());
1125 aAnchorSprms
.set(NS_ooxml::LN_CT_Anchor_positionV
,
1126 new RTFValue(aPosvAttributes
, aPosvSprms
));
1128 aAnchorSprms
.set(NS_ooxml::LN_CT_Anchor_docPr
, pDocprValue
);
1129 aAnchorSprms
.set(NS_ooxml::LN_graphic_graphic
, pGraphicValue
);
1131 auto pValue
= new RTFValue(m_aStates
.top().aShape
.aAnchorAttributes
, aAnchorSprms
);
1132 aSprms
.set(NS_ooxml::LN_anchor_anchor
, pValue
);
1134 writerfilter::Reference
<Properties
>::Pointer_t pProperties
1135 = new RTFReferenceProperties(aAttributes
, aSprms
);
1138 if (!m_aStates
.top().pCurrentBuffer
)
1140 Mapper().props(pProperties
);
1141 // Make sure we don't lose these properties with a too early reset.
1142 m_bHadPicture
= true;
1146 auto pValue
= new RTFValue(aAttributes
, aSprms
);
1147 bufferProperties(*m_aStates
.top().pCurrentBuffer
, pValue
, nullptr);
1151 RTFError
RTFDocumentImpl::resolveChars(char ch
)
1153 if (m_aStates
.top().nInternalState
== RTFInternalState::BIN
)
1155 m_pBinaryData
.reset(new SvMemoryStream());
1156 m_pBinaryData
->WriteChar(ch
);
1157 for (int i
= 0; i
< m_aStates
.top().nBinaryToRead
- 1; ++i
)
1159 Strm().ReadChar(ch
);
1160 m_pBinaryData
->WriteChar(ch
);
1162 m_aStates
.top().nInternalState
= RTFInternalState::NORMAL
;
1163 return RTFError::OK
;
1168 bool bUnicodeChecked
= false;
1169 bool bSkipped
= false;
1171 while (!Strm().eof()
1172 && (m_aStates
.top().nInternalState
== RTFInternalState::HEX
1173 || (ch
!= '{' && ch
!= '}' && ch
!= '\\')))
1175 if (m_aStates
.top().nInternalState
== RTFInternalState::HEX
|| (ch
!= 0x0d && ch
!= 0x0a))
1177 if (m_aStates
.top().nCharsToSkip
== 0)
1179 if (!bUnicodeChecked
)
1181 checkUnicode(/*bUnicode =*/true, /*bHex =*/false);
1182 bUnicodeChecked
= true;
1189 m_aStates
.top().nCharsToSkip
--;
1193 // read a single char if we're in hex mode
1194 if (m_aStates
.top().nInternalState
== RTFInternalState::HEX
)
1197 if (RTL_TEXTENCODING_MS_932
== m_aStates
.top().nCurrentEncoding
)
1199 unsigned char uch
= ch
;
1200 if ((uch
>= 0x80 && uch
<= 0x9F) || uch
>= 0xE0)
1202 // read second byte of 2-byte Shift-JIS - may be \ { }
1203 Strm().ReadChar(ch
);
1204 if (m_aStates
.top().nCharsToSkip
== 0)
1206 // fdo#79384: Word will reject Shift-JIS following \loch
1207 // but apparently OOo could read and (worse) write such documents
1208 SAL_INFO_IF(m_aStates
.top().eRunType
!= RTFParserState::RunType::DBCH
,
1209 "writerfilter.rtf", "invalid Shift-JIS without DBCH");
1210 assert(bUnicodeChecked
);
1216 // anybody who uses \ucN with Shift-JIS is insane
1217 m_aStates
.top().nCharsToSkip
--;
1222 Strm().ReadChar(ch
);
1224 if (m_aStates
.top().nInternalState
!= RTFInternalState::HEX
&& !Strm().eof())
1227 if (m_aStates
.top().nInternalState
== RTFInternalState::HEX
1228 && m_aStates
.top().eDestination
!= Destination::LEVELNUMBERS
)
1232 // note: apparently \'0d\'0a is interpreted as 2 breaks, not 1
1233 if ((ch
== '\r' || ch
== '\n') && m_aStates
.top().eDestination
!= Destination::DOCCOMM
1234 && m_aStates
.top().eDestination
!= Destination::LEVELNUMBERS
1235 && m_aStates
.top().eDestination
!= Destination::LEVELTEXT
)
1237 checkUnicode(/*bUnicode =*/false, /*bHex =*/true);
1238 dispatchSymbol(RTF_PAR
);
1242 m_aHexBuffer
.append(ch
);
1245 return RTFError::OK
;
1248 if (m_aStates
.top().eDestination
== Destination::SKIP
)
1249 return RTFError::OK
;
1250 OString aStr
= aBuf
.makeStringAndClear();
1251 if (m_aStates
.top().eDestination
== Destination::LEVELNUMBERS
)
1253 if (aStr
.toChar() != ';')
1254 m_aStates
.top().aLevelNumbers
.push_back(sal_Int32(ch
));
1255 return RTFError::OK
;
1258 OUString
aOUStr(OStringToOUString(aStr
, m_aStates
.top().nCurrentEncoding
));
1259 SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolveChars: collected '" << aOUStr
<< "'");
1261 if (m_aStates
.top().eDestination
== Destination::COLORTABLE
)
1263 // we hit a ';' at the end of each color entry
1264 m_aColorTable
.push_back(m_aStates
.top().aCurrentColor
.GetColor());
1265 // set components back to zero
1266 m_aStates
.top().aCurrentColor
= RTFColorTableEntry();
1268 else if (!aStr
.isEmpty())
1269 m_aHexBuffer
.append(aStr
);
1271 checkUnicode(/*bUnicode =*/false, /*bHex =*/true);
1272 return RTFError::OK
;
1275 bool RTFFrame::inFrame() { return m_nW
> 0 || m_nH
> 0 || m_nX
> 0 || m_nY
> 0; }
1277 void RTFDocumentImpl::singleChar(sal_uInt8 nValue
, bool bRunProps
)
1279 sal_uInt8 sValue
[] = { nValue
};
1280 RTFBuffer_t
* pCurrentBuffer
= m_aStates
.top().pCurrentBuffer
;
1282 if (!pCurrentBuffer
)
1284 Mapper().startCharacterGroup();
1285 // Should we send run properties?
1288 Mapper().text(sValue
, 1);
1289 Mapper().endCharacterGroup();
1293 pCurrentBuffer
->push_back(Buf_t(BUFFER_STARTRUN
, nullptr, nullptr));
1294 auto pValue
= new RTFValue(*sValue
);
1295 pCurrentBuffer
->push_back(Buf_t(BUFFER_TEXT
, pValue
, nullptr));
1296 pCurrentBuffer
->push_back(Buf_t(BUFFER_ENDRUN
, nullptr, nullptr));
1300 void RTFDocumentImpl::text(OUString
& rString
)
1302 if (rString
.getLength() == 1 && m_aStates
.top().eDestination
!= Destination::DOCCOMM
)
1304 // No cheating! Tokenizer ignores bare \r and \n, their hex \'0d / \'0a form doesn't count, either.
1305 sal_Unicode ch
= rString
[0];
1306 if (ch
== 0x0d || ch
== 0x0a)
1311 switch (m_aStates
.top().eDestination
)
1313 // Note: in fonttbl there may or may not be groups; in stylesheet
1314 // and revtbl groups are mandatory
1315 case Destination::FONTTABLE
:
1316 case Destination::FONTENTRY
:
1317 case Destination::STYLEENTRY
:
1318 case Destination::LISTNAME
:
1319 case Destination::REVISIONENTRY
:
1321 // ; is the end of the entry
1323 if (rString
.endsWith(";"))
1325 rString
= rString
.copy(0, rString
.getLength() - 1);
1328 m_aStates
.top().appendDestinationText(rString
);
1331 // always clear, necessary in case of group-less fonttable
1332 OUString
const aName
= m_aStates
.top().pDestinationText
->makeStringAndClear();
1333 switch (m_aStates
.top().eDestination
)
1335 case Destination::FONTTABLE
:
1336 case Destination::FONTENTRY
:
1338 m_aFontNames
[m_nCurrentFontIndex
] = aName
;
1339 if (m_nCurrentEncoding
>= 0)
1341 m_aFontEncodings
[m_nCurrentFontIndex
] = m_nCurrentEncoding
;
1342 m_nCurrentEncoding
= -1;
1344 m_aStates
.top().aTableAttributes
.set(NS_ooxml::LN_CT_Font_name
,
1345 new RTFValue(aName
));
1347 writerfilter::Reference
<Properties
>::Pointer_t
const pProp(
1348 new RTFReferenceProperties(m_aStates
.top().aTableAttributes
,
1349 m_aStates
.top().aTableSprms
));
1351 //See fdo#47347 initial invalid font entry properties are inserted first,
1352 //so when we attempt to insert the correct ones, there's already an
1353 //entry in the map for them, so the new ones aren't inserted.
1354 auto lb
= m_aFontTableEntries
.lower_bound(m_nCurrentFontIndex
);
1355 if (lb
!= m_aFontTableEntries
.end()
1356 && !(m_aFontTableEntries
.key_comp()(m_nCurrentFontIndex
, lb
->first
)))
1359 m_aFontTableEntries
.insert(lb
,
1360 std::make_pair(m_nCurrentFontIndex
, pProp
));
1363 case Destination::STYLEENTRY
:
1365 RTFValue::Pointer_t pType
1366 = m_aStates
.top().aTableAttributes
.find(NS_ooxml::LN_CT_Style_type
);
1369 // Word strips whitespace around style names.
1370 m_aStyleNames
[m_nCurrentStyleIndex
] = aName
.trim();
1371 m_aStyleTypes
[m_nCurrentStyleIndex
] = pType
->getInt();
1372 auto pValue
= new RTFValue(aName
.trim());
1373 m_aStates
.top().aTableAttributes
.set(NS_ooxml::LN_CT_Style_styleId
,
1375 m_aStates
.top().aTableSprms
.set(NS_ooxml::LN_CT_Style_name
, pValue
);
1377 writerfilter::Reference
<Properties
>::Pointer_t
const pProp(
1378 createStyleProperties());
1379 m_aStyleTableEntries
.insert(
1380 std::make_pair(m_nCurrentStyleIndex
, pProp
));
1383 SAL_INFO("writerfilter.rtf", "no RTF style type defined, ignoring");
1386 case Destination::LISTNAME
:
1387 // TODO: what can be done with a list name?
1389 case Destination::REVISIONENTRY
:
1390 m_aAuthors
[m_aAuthors
.size()] = aName
;
1400 case Destination::LEVELTEXT
:
1401 case Destination::SHAPEPROPERTYNAME
:
1402 case Destination::SHAPEPROPERTYVALUE
:
1403 case Destination::BOOKMARKEND
:
1404 case Destination::PICT
:
1405 case Destination::SHAPEPROPERTYVALUEPICT
:
1406 case Destination::FORMFIELDNAME
:
1407 case Destination::FORMFIELDLIST
:
1408 case Destination::DATAFIELD
:
1409 case Destination::AUTHOR
:
1410 case Destination::KEYWORDS
:
1411 case Destination::OPERATOR
:
1412 case Destination::COMPANY
:
1413 case Destination::COMMENT
:
1414 case Destination::OBJDATA
:
1415 case Destination::OBJCLASS
:
1416 case Destination::ANNOTATIONDATE
:
1417 case Destination::ANNOTATIONAUTHOR
:
1418 case Destination::ANNOTATIONREFERENCE
:
1419 case Destination::FALT
:
1420 case Destination::PARAGRAPHNUMBERING_TEXTAFTER
:
1421 case Destination::PARAGRAPHNUMBERING_TEXTBEFORE
:
1422 case Destination::TITLE
:
1423 case Destination::SUBJECT
:
1424 case Destination::DOCCOMM
:
1425 case Destination::ATNID
:
1426 case Destination::ANNOTATIONREFERENCESTART
:
1427 case Destination::ANNOTATIONREFERENCEEND
:
1428 case Destination::MR
:
1429 case Destination::MCHR
:
1430 case Destination::MPOS
:
1431 case Destination::MVERTJC
:
1432 case Destination::MSTRIKEH
:
1433 case Destination::MDEGHIDE
:
1434 case Destination::MBEGCHR
:
1435 case Destination::MSEPCHR
:
1436 case Destination::MENDCHR
:
1437 case Destination::MSUBHIDE
:
1438 case Destination::MSUPHIDE
:
1439 case Destination::MTYPE
:
1440 case Destination::MGROW
:
1441 case Destination::INDEXENTRY
:
1442 case Destination::TOCENTRY
:
1443 case Destination::PROPNAME
:
1444 case Destination::STATICVAL
:
1445 m_aStates
.top().appendDestinationText(rString
);
1454 if (!m_aIgnoreFirst
.isEmpty() && m_aIgnoreFirst
== rString
)
1456 m_aIgnoreFirst
.clear();
1460 // Are we in the middle of the table definition? (No cell defs yet, but we already have some cell props.)
1461 if (m_aStates
.top().aTableCellSprms
.find(NS_ooxml::LN_CT_TcPrBase_vAlign
).get()
1462 && m_nTopLevelCells
== 0)
1464 m_aTableBufferStack
.back().emplace_back(
1465 Buf_t(BUFFER_UTEXT
, new RTFValue(rString
), nullptr));
1472 // Don't return earlier, a bookmark start has to be in a paragraph group.
1473 if (m_aStates
.top().eDestination
== Destination::BOOKMARKSTART
)
1475 m_aStates
.top().appendDestinationText(rString
);
1479 RTFBuffer_t
* pCurrentBuffer
= m_aStates
.top().pCurrentBuffer
;
1481 if (!pCurrentBuffer
&& m_aStates
.top().eDestination
!= Destination::FOOTNOTE
)
1482 Mapper().startCharacterGroup();
1483 else if (pCurrentBuffer
)
1485 RTFValue::Pointer_t pValue
;
1486 pCurrentBuffer
->push_back(Buf_t(BUFFER_STARTRUN
, pValue
, nullptr));
1489 if (m_aStates
.top().eDestination
== Destination::NORMAL
1490 || m_aStates
.top().eDestination
== Destination::FIELDRESULT
1491 || m_aStates
.top().eDestination
== Destination::SHAPETEXT
)
1494 if (!pCurrentBuffer
)
1495 Mapper().utext(reinterpret_cast<sal_uInt8
const*>(rString
.getStr()), rString
.getLength());
1498 auto pValue
= new RTFValue(rString
);
1499 pCurrentBuffer
->push_back(Buf_t(BUFFER_UTEXT
, pValue
, nullptr));
1504 if (!pCurrentBuffer
&& m_aStates
.top().eDestination
!= Destination::FOOTNOTE
)
1505 Mapper().endCharacterGroup();
1506 else if (pCurrentBuffer
)
1508 RTFValue::Pointer_t pValue
;
1509 pCurrentBuffer
->push_back(Buf_t(BUFFER_ENDRUN
, pValue
, nullptr));
1513 void RTFDocumentImpl::prepareProperties(
1514 RTFParserState
& rState
, writerfilter::Reference
<Properties
>::Pointer_t
& o_rpParagraphProperties
,
1515 writerfilter::Reference
<Properties
>::Pointer_t
& o_rpFrameProperties
,
1516 writerfilter::Reference
<Properties
>::Pointer_t
& o_rpTableRowProperties
, int const nCells
,
1517 int const nCurrentCellX
)
1519 o_rpParagraphProperties
= getProperties(rState
.aParagraphAttributes
, rState
.aParagraphSprms
,
1520 NS_ooxml::LN_Value_ST_StyleType_paragraph
);
1522 if (rState
.aFrame
.hasProperties())
1524 o_rpFrameProperties
= new RTFReferenceProperties(RTFSprms(), rState
.aFrame
.getSprms());
1528 RTFValue::Pointer_t
const pTableWidthProps
1529 = rState
.aTableRowSprms
.find(NS_ooxml::LN_CT_TblPrBase_tblW
);
1530 if (!pTableWidthProps
.get())
1532 auto pUnitValue
= new RTFValue(3);
1533 putNestedAttribute(rState
.aTableRowSprms
, NS_ooxml::LN_CT_TblPrBase_tblW
,
1534 NS_ooxml::LN_CT_TblWidth_type
, pUnitValue
);
1535 auto pWValue
= new RTFValue(nCurrentCellX
);
1536 putNestedAttribute(rState
.aTableRowSprms
, NS_ooxml::LN_CT_TblPrBase_tblW
,
1537 NS_ooxml::LN_CT_TblWidth_w
, pWValue
);
1541 rState
.aTableRowSprms
.set(NS_ooxml::LN_tblRow
, new RTFValue(1));
1543 RTFValue::Pointer_t
const pCellMar
1544 = rState
.aTableRowSprms
.find(NS_ooxml::LN_CT_TblPrBase_tblCellMar
);
1545 if (!pCellMar
.get())
1547 // If no cell margins are defined, the default left/right margin is 0 in Word, but not in Writer.
1548 RTFSprms aAttributes
;
1549 aAttributes
.set(NS_ooxml::LN_CT_TblWidth_type
,
1550 new RTFValue(NS_ooxml::LN_Value_ST_TblWidth_dxa
));
1551 aAttributes
.set(NS_ooxml::LN_CT_TblWidth_w
, new RTFValue(0));
1552 putNestedSprm(rState
.aTableRowSprms
, NS_ooxml::LN_CT_TblPrBase_tblCellMar
,
1553 NS_ooxml::LN_CT_TblCellMar_left
, new RTFValue(aAttributes
));
1554 putNestedSprm(rState
.aTableRowSprms
, NS_ooxml::LN_CT_TblPrBase_tblCellMar
,
1555 NS_ooxml::LN_CT_TblCellMar_right
, new RTFValue(aAttributes
));
1558 o_rpTableRowProperties
1559 = new RTFReferenceProperties(rState
.aTableRowAttributes
, rState
.aTableRowSprms
);
1562 void RTFDocumentImpl::sendProperties(
1563 writerfilter::Reference
<Properties
>::Pointer_t
const& pParagraphProperties
,
1564 writerfilter::Reference
<Properties
>::Pointer_t
const& pFrameProperties
,
1565 writerfilter::Reference
<Properties
>::Pointer_t
const& pTableRowProperties
)
1567 Mapper().props(pParagraphProperties
);
1569 if (pFrameProperties
)
1571 Mapper().props(pFrameProperties
);
1574 Mapper().props(pTableRowProperties
);
1579 void RTFDocumentImpl::replayRowBuffer(RTFBuffer_t
& rBuffer
, ::std::deque
<RTFSprms
>& rCellsSrpms
,
1580 ::std::deque
<RTFSprms
>& rCellsAttributes
, int const nCells
)
1582 for (int i
= 0; i
< nCells
; ++i
)
1584 replayBuffer(rBuffer
, &rCellsSrpms
.front(), &rCellsAttributes
.front());
1585 rCellsSrpms
.pop_front();
1586 rCellsAttributes
.pop_front();
1588 for (Buf_t
& i
: rBuffer
)
1590 SAL_WARN_IF(BUFFER_CELLEND
== std::get
<0>(i
), "writerfilter.rtf", "dropping table cell!");
1592 assert(rCellsSrpms
.empty());
1593 assert(rCellsAttributes
.empty());
1596 void RTFDocumentImpl::replayBuffer(RTFBuffer_t
& rBuffer
, RTFSprms
* const pSprms
,
1597 RTFSprms
const* const pAttributes
)
1599 while (!rBuffer
.empty())
1601 Buf_t
aTuple(rBuffer
.front());
1602 rBuffer
.pop_front();
1603 if (std::get
<0>(aTuple
) == BUFFER_PROPS
)
1605 // Construct properties via getProperties() and not directly, to take care of deduplication.
1606 writerfilter::Reference
<Properties
>::Pointer_t
const pProp(getProperties(
1607 std::get
<1>(aTuple
)->getAttributes(), std::get
<1>(aTuple
)->getSprms(), 0));
1608 Mapper().props(pProp
);
1610 else if (std::get
<0>(aTuple
) == BUFFER_NESTROW
)
1612 TableRowBuffer
& rRowBuffer(*std::get
<2>(aTuple
));
1614 replayRowBuffer(rRowBuffer
.buffer
, rRowBuffer
.cellsSprms
, rRowBuffer
.cellsAttributes
,
1617 sendProperties(rRowBuffer
.pParaProperties
, rRowBuffer
.pFrameProperties
,
1618 rRowBuffer
.pRowProperties
);
1620 else if (std::get
<0>(aTuple
) == BUFFER_CELLEND
)
1622 assert(pSprms
&& pAttributes
);
1623 auto pValue
= new RTFValue(1);
1624 pSprms
->set(NS_ooxml::LN_tblCell
, pValue
);
1625 writerfilter::Reference
<Properties
>::Pointer_t
const pTableCellProperties(
1626 new RTFReferenceProperties(*pAttributes
, *pSprms
));
1627 Mapper().props(pTableCellProperties
);
1631 else if (std::get
<0>(aTuple
) == BUFFER_STARTRUN
)
1632 Mapper().startCharacterGroup();
1633 else if (std::get
<0>(aTuple
) == BUFFER_TEXT
)
1635 sal_uInt8
const nValue
= std::get
<1>(aTuple
)->getInt();
1636 Mapper().text(&nValue
, 1);
1638 else if (std::get
<0>(aTuple
) == BUFFER_UTEXT
)
1640 OUString
const aString(std::get
<1>(aTuple
)->getString());
1641 Mapper().utext(reinterpret_cast<sal_uInt8
const*>(aString
.getStr()),
1642 aString
.getLength());
1644 else if (std::get
<0>(aTuple
) == BUFFER_ENDRUN
)
1645 Mapper().endCharacterGroup();
1646 else if (std::get
<0>(aTuple
) == BUFFER_PAR
)
1648 else if (std::get
<0>(aTuple
) == BUFFER_STARTSHAPE
)
1649 m_pSdrImport
->resolve(std::get
<1>(aTuple
)->getShape(), false, RTFSdrImport::SHAPE
);
1650 else if (std::get
<0>(aTuple
) == BUFFER_RESOLVESHAPE
)
1652 // Make sure there is no current buffer while replaying the shape,
1653 // otherwise it gets re-buffered.
1654 RTFBuffer_t
* pCurrentBuffer
= m_aStates
.top().pCurrentBuffer
;
1655 m_aStates
.top().pCurrentBuffer
= nullptr;
1657 // Set current shape during replay, needed by e.g. wrap in
1659 m_aStates
.top().aShape
= std::get
<1>(aTuple
)->getShape();
1661 m_pSdrImport
->resolve(std::get
<1>(aTuple
)->getShape(), true, RTFSdrImport::SHAPE
);
1662 m_aStates
.top().pCurrentBuffer
= pCurrentBuffer
;
1664 else if (std::get
<0>(aTuple
) == BUFFER_ENDSHAPE
)
1665 m_pSdrImport
->close();
1666 else if (std::get
<0>(aTuple
) == BUFFER_RESOLVESUBSTREAM
)
1668 RTFSprms
& rAttributes
= std::get
<1>(aTuple
)->getAttributes();
1669 std::size_t nPos
= rAttributes
.find(0)->getInt();
1670 Id nId
= rAttributes
.find(1)->getInt();
1671 OUString aCustomMark
= rAttributes
.find(2)->getString();
1672 resolveSubstream(nPos
, nId
, aCustomMark
);
1674 else if (std::get
<0>(aTuple
) == BUFFER_PICTURE
)
1675 m_aStates
.top().aPicture
= std::get
<1>(aTuple
)->getPicture();
1676 else if (std::get
<0>(aTuple
) == BUFFER_SETSTYLE
)
1678 if (!m_aStates
.empty())
1679 m_aStates
.top().nCurrentStyleIndex
= std::get
<1>(aTuple
)->getInt();
1686 bool findPropertyName(const std::vector
<beans::PropertyValue
>& rProperties
, const OUString
& rName
)
1688 for (auto& rProperty
: rProperties
)
1690 if (rProperty
.Name
== rName
)
1696 void RTFDocumentImpl::backupTableRowProperties()
1698 if (m_nTopLevelCurrentCellX
)
1700 m_aBackupTableRowSprms
= m_aStates
.top().aTableRowSprms
;
1701 m_aBackupTableRowAttributes
= m_aStates
.top().aTableRowAttributes
;
1702 m_nBackupTopLevelCurrentCellX
= m_nTopLevelCurrentCellX
;
1706 void RTFDocumentImpl::restoreTableRowProperties()
1708 m_aStates
.top().aTableRowSprms
= m_aBackupTableRowSprms
;
1709 m_aStates
.top().aTableRowAttributes
= m_aBackupTableRowAttributes
;
1710 m_nTopLevelCurrentCellX
= m_nBackupTopLevelCurrentCellX
;
1713 void RTFDocumentImpl::resetTableRowProperties()
1715 m_aStates
.top().aTableRowSprms
= m_aDefaultState
.aTableRowSprms
;
1716 m_aStates
.top().aTableRowSprms
.set(NS_ooxml::LN_CT_TblGridBase_gridCol
, new RTFValue(-1),
1717 RTFOverwrite::NO_APPEND
);
1718 m_aStates
.top().aTableRowAttributes
= m_aDefaultState
.aTableRowAttributes
;
1719 if (Destination::NESTEDTABLEPROPERTIES
== m_aStates
.top().eDestination
)
1721 m_nNestedTRLeft
= 0;
1722 m_nNestedCurrentCellX
= 0;
1726 m_nTopLevelTRLeft
= 0;
1727 m_nTopLevelCurrentCellX
= 0;
1731 RTFError
RTFDocumentImpl::dispatchToggle(RTFKeyword nKeyword
, bool bParam
, int nParam
)
1734 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
1735 RTFSkipDestination
aSkip(*this);
1737 tools::SvRef
<RTFValue
> pBoolValue(new RTFValue(int(!bParam
|| nParam
!= 0)));
1739 // Underline toggles.
1743 nSprm
= NS_ooxml::LN_Value_ST_Underline_single
;
1746 nSprm
= NS_ooxml::LN_Value_ST_Underline_dash
;
1749 nSprm
= NS_ooxml::LN_Value_ST_Underline_dotDash
;
1752 nSprm
= NS_ooxml::LN_Value_ST_Underline_dotDotDash
;
1755 nSprm
= NS_ooxml::LN_Value_ST_Underline_double
;
1758 nSprm
= NS_ooxml::LN_Value_ST_Underline_wavyHeavy
;
1761 nSprm
= NS_ooxml::LN_Value_ST_Underline_dashLong
;
1764 nSprm
= NS_ooxml::LN_Value_ST_Underline_thick
;
1767 nSprm
= NS_ooxml::LN_Value_ST_Underline_dottedHeavy
;
1770 nSprm
= NS_ooxml::LN_Value_ST_Underline_dashedHeavy
;
1773 nSprm
= NS_ooxml::LN_Value_ST_Underline_dashDotHeavy
;
1775 case RTF_ULTHDASHDD
:
1776 nSprm
= NS_ooxml::LN_Value_ST_Underline_dashDotDotHeavy
;
1779 nSprm
= NS_ooxml::LN_Value_ST_Underline_dashLongHeavy
;
1781 case RTF_ULULDBWAVE
:
1782 nSprm
= NS_ooxml::LN_Value_ST_Underline_wavyDouble
;
1785 nSprm
= NS_ooxml::LN_Value_ST_Underline_wave
;
1793 = new RTFValue((!bParam
|| nParam
!= 0) ? nSprm
: NS_ooxml::LN_Value_ST_Underline_none
);
1794 m_aStates
.top().aCharacterAttributes
.set(NS_ooxml::LN_CT_Underline_val
, pValue
);
1795 return RTFError::OK
;
1798 // Accent characters (over dot / over comma).
1802 nSprm
= NS_ooxml::LN_Value_ST_Em_none
;
1805 nSprm
= NS_ooxml::LN_Value_ST_Em_dot
;
1808 nSprm
= NS_ooxml::LN_Value_ST_Em_comma
;
1811 nSprm
= NS_ooxml::LN_Value_ST_Em_circle
;
1813 case RTF_ACCUNDERDOT
:
1814 nSprm
= NS_ooxml::LN_Value_ST_Em_underDot
;
1821 auto pValue
= new RTFValue((!bParam
|| nParam
!= 0) ? nSprm
: 0);
1822 m_aStates
.top().aCharacterSprms
.set(NS_ooxml::LN_EG_RPrBase_em
, pValue
);
1823 return RTFError::OK
;
1826 // Trivial character sprms.
1831 nSprm
= (m_aStates
.top().isRightToLeft
1832 || m_aStates
.top().eRunType
== RTFParserState::RunType::HICH
)
1833 ? NS_ooxml::LN_EG_RPrBase_bCs
1834 : NS_ooxml::LN_EG_RPrBase_b
;
1838 nSprm
= (m_aStates
.top().isRightToLeft
1839 || m_aStates
.top().eRunType
== RTFParserState::RunType::HICH
)
1840 ? NS_ooxml::LN_EG_RPrBase_iCs
1841 : NS_ooxml::LN_EG_RPrBase_i
;
1844 nSprm
= NS_ooxml::LN_EG_RPrBase_outline
;
1847 nSprm
= NS_ooxml::LN_EG_RPrBase_shadow
;
1850 nSprm
= NS_ooxml::LN_EG_RPrBase_vanish
;
1853 nSprm
= NS_ooxml::LN_EG_RPrBase_strike
;
1856 nSprm
= NS_ooxml::LN_EG_RPrBase_dstrike
;
1859 nSprm
= NS_ooxml::LN_EG_RPrBase_smallCaps
;
1862 nSprm
= NS_ooxml::LN_EG_RPrBase_imprint
;
1865 nSprm
= NS_ooxml::LN_EG_RPrBase_caps
;
1872 m_aStates
.top().aCharacterSprms
.set(nSprm
, pBoolValue
);
1873 return RTFError::OK
;
1879 m_aStates
.top().aParagraphSprms
.set(NS_ooxml::LN_CT_PPrBase_autoSpaceDE
, pBoolValue
);
1884 auto pValue
= new RTFValue(nKeyword
== RTF_DELETED
? oox::XML_del
: oox::XML_ins
);
1885 putNestedAttribute(m_aStates
.top().aCharacterSprms
, NS_ooxml::LN_trackchange
,
1886 NS_ooxml::LN_token
, pValue
);
1890 putNestedAttribute(m_aStates
.top().aParagraphSprms
, NS_ooxml::LN_CT_PPrBase_spacing
,
1891 NS_ooxml::LN_CT_Spacing_beforeAutospacing
, pBoolValue
);
1894 putNestedAttribute(m_aStates
.top().aParagraphSprms
, NS_ooxml::LN_CT_PPrBase_spacing
,
1895 NS_ooxml::LN_CT_Spacing_afterAutospacing
, pBoolValue
);
1898 m_aSettingsTableSprms
.set(NS_ooxml::LN_CT_Settings_evenAndOddHeaders
, pBoolValue
);
1901 m_aSettingsTableSprms
.set(NS_ooxml::LN_CT_Settings_autoHyphenation
, pBoolValue
);
1904 m_aStates
.top().aParagraphSprms
.set(NS_ooxml::LN_CT_PPrBase_suppressAutoHyphens
,
1905 new RTFValue(int(bParam
&& nParam
== 0)));
1909 SAL_INFO("writerfilter.rtf",
1910 "TODO handle toggle '" << keywordToString(nKeyword
) << "'");
1911 aSkip
.setParsed(false);
1915 return RTFError::OK
;
1918 RTFError
RTFDocumentImpl::pushState()
1920 //SAL_INFO("writerfilter.rtf", OSL_THIS_FUNC << " before push: " << m_pTokenizer->getGroup());
1922 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
1923 m_nGroupStartPos
= Strm().Tell();
1925 if (m_aStates
.empty())
1926 m_aStates
.push(m_aDefaultState
);
1929 // fdo#85812 group resets run type of _current_ and new state (but not RTL)
1930 m_aStates
.top().eRunType
= RTFParserState::RunType::LOCH
;
1932 if (m_aStates
.top().eDestination
== Destination::MR
)
1933 lcl_DestinationToMath(m_aStates
.top().pDestinationText
, m_aMathBuffer
, m_bMathNor
);
1934 m_aStates
.push(m_aStates
.top());
1936 m_aStates
.top().aDestinationText
.setLength(0); // was copied: always reset!
1938 m_pTokenizer
->pushGroup();
1940 switch (m_aStates
.top().eDestination
)
1942 case Destination::FONTTABLE
:
1943 // this is a "faked" destination for the font entry
1944 m_aStates
.top().pDestinationText
= &m_aStates
.top().aDestinationText
;
1945 m_aStates
.top().eDestination
= Destination::FONTENTRY
;
1947 case Destination::STYLESHEET
:
1948 // this is a "faked" destination for the style sheet entry
1949 m_aStates
.top().pDestinationText
= &m_aStates
.top().aDestinationText
;
1950 m_aStates
.top().eDestination
= Destination::STYLEENTRY
;
1952 // the *default* is \s0 i.e. paragraph style default
1953 // this will be overwritten by \sN \csN \dsN \tsN
1954 m_nCurrentStyleIndex
= 0;
1955 auto pValue
= new RTFValue(NS_ooxml::LN_Value_ST_StyleType_paragraph
);
1956 m_aStates
.top().aTableAttributes
.set(NS_ooxml::LN_CT_Style_type
, pValue
);
1959 case Destination::FIELDRESULT
:
1960 case Destination::SHAPETEXT
:
1961 case Destination::FORMFIELD
:
1962 case Destination::FIELDINSTRUCTION
:
1963 case Destination::PICT
:
1964 m_aStates
.top().eDestination
= Destination::NORMAL
;
1966 case Destination::MNUM
:
1967 case Destination::MDEN
:
1968 case Destination::ME
:
1969 case Destination::MFNAME
:
1970 case Destination::MLIM
:
1971 case Destination::MSUB
:
1972 case Destination::MSUP
:
1973 case Destination::MDEG
:
1974 case Destination::MOMATH
:
1975 m_aStates
.top().eDestination
= Destination::MR
;
1977 case Destination::REVISIONTABLE
:
1978 // this is a "faked" destination for the revision table entry
1979 m_aStates
.top().pDestinationText
= &m_aStates
.top().aDestinationText
;
1980 m_aStates
.top().eDestination
= Destination::REVISIONENTRY
;
1986 // If this is true, then ooxml:endtrackchange will be generated. Make sure
1987 // we don't generate more ooxml:endtrackchange than ooxml:trackchange: new
1988 // state does not inherit this flag.
1989 m_aStates
.top().bStartedTrackchange
= false;
1991 return RTFError::OK
;
1994 writerfilter::Reference
<Properties
>::Pointer_t
RTFDocumentImpl::createStyleProperties()
1997 RTFValue::Pointer_t pBasedOn
= m_aStates
.top().aTableSprms
.find(NS_ooxml::LN_CT_Style_basedOn
);
1999 nBasedOn
= pBasedOn
->getInt();
2002 // No parent style, then mimic what Word does: ignore attributes which
2003 // would set a margin as formatting, but with a default value.
2004 for (const auto& nId
:
2005 { NS_ooxml::LN_CT_Ind_firstLine
, NS_ooxml::LN_CT_Ind_left
, NS_ooxml::LN_CT_Ind_right
,
2006 NS_ooxml::LN_CT_Ind_start
, NS_ooxml::LN_CT_Ind_end
})
2008 RTFValue::Pointer_t pValue
= getNestedAttribute(m_aStates
.top().aParagraphSprms
,
2009 NS_ooxml::LN_CT_PPrBase_ind
, nId
);
2010 if (pValue
&& pValue
->getInt() == 0)
2011 eraseNestedAttribute(m_aStates
.top().aParagraphSprms
, NS_ooxml::LN_CT_PPrBase_ind
,
2016 RTFValue::Pointer_t pParaProps
2017 = new RTFValue(m_aStates
.top().aParagraphAttributes
, m_aStates
.top().aParagraphSprms
);
2018 RTFValue::Pointer_t pCharProps
2019 = new RTFValue(m_aStates
.top().aCharacterAttributes
, m_aStates
.top().aCharacterSprms
);
2021 // resetSprms will clean up this modification
2022 m_aStates
.top().aTableSprms
.set(NS_ooxml::LN_CT_Style_pPr
, pParaProps
);
2023 m_aStates
.top().aTableSprms
.set(NS_ooxml::LN_CT_Style_rPr
, pCharProps
);
2025 writerfilter::Reference
<Properties
>::Pointer_t
const pProps(
2026 new RTFReferenceProperties(m_aStates
.top().aTableAttributes
, m_aStates
.top().aTableSprms
));
2030 /** 2 different representations of the styles are needed:
2032 1) flat content, as read from the input file:
2033 stored in m_aStyleTableEntries, used as reference input for
2034 deduplication both here and for hard formatting in getProperties()
2036 2) real content, with proper override of sprms/attributes where it differs
2037 from parent style; this is produced here and sent to domain mapper
2039 RTFReferenceTable::Entries_t
RTFDocumentImpl::deduplicateStyleTable()
2041 RTFReferenceTable::Entries_t ret
;
2042 for (auto const& it
: m_aStyleTableEntries
)
2044 auto pStyle
= it
.second
;
2045 // ugly downcasts here, but can't easily replace the members with
2046 // RTFReferenceProperties because dmapper wants SvRef<Properties> anyway
2047 RTFValue::Pointer_t
const pBasedOn(
2048 static_cast<RTFReferenceProperties
&>(*pStyle
).getSprms().find(
2049 NS_ooxml::LN_CT_Style_basedOn
));
2052 int const nBasedOn(pBasedOn
->getInt());
2053 auto const itParent(m_aStyleTableEntries
.find(nBasedOn
)); // definition as read!
2054 if (itParent
!= m_aStyleTableEntries
.end())
2056 auto const pStyleType(
2057 static_cast<RTFReferenceProperties
&>(*pStyle
).getAttributes().find(
2058 NS_ooxml::LN_CT_Style_type
));
2060 int const nStyleType(pStyleType
->getInt());
2061 RTFSprms
const sprms(
2062 static_cast<RTFReferenceProperties
&>(*pStyle
).getSprms().cloneAndDeduplicate(
2063 static_cast<RTFReferenceProperties
&>(*itParent
->second
).getSprms(),
2065 RTFSprms
const attributes(
2066 static_cast<RTFReferenceProperties
&>(*pStyle
)
2068 .cloneAndDeduplicate(
2069 static_cast<RTFReferenceProperties
&>(*itParent
->second
).getAttributes(),
2072 pStyle
= new RTFReferenceProperties(attributes
, sprms
);
2076 SAL_WARN("writerfilter.rtf", "parent style not found: " << nBasedOn
);
2079 ret
[it
.first
] = pStyle
;
2081 assert(ret
.size() == m_aStyleTableEntries
.size());
2085 void RTFDocumentImpl::resetSprms()
2087 m_aStates
.top().aTableSprms
.clear();
2088 m_aStates
.top().aCharacterSprms
.clear();
2089 m_aStates
.top().aParagraphSprms
.clear();
2092 void RTFDocumentImpl::resetAttributes()
2094 m_aStates
.top().aTableAttributes
.clear();
2095 m_aStates
.top().aCharacterAttributes
.clear();
2096 m_aStates
.top().aParagraphAttributes
.clear();
2099 static bool lcl_containsProperty(const uno::Sequence
<beans::Property
>& rProperties
,
2100 const OUString
& rName
)
2102 return std::any_of(rProperties
.begin(), rProperties
.end(),
2103 [&](const beans::Property
& rProperty
) { return rProperty
.Name
== rName
; });
2106 RTFError
RTFDocumentImpl::popState()
2108 //SAL_INFO("writerfilter", OSL_THIS_FUNC << " before pop: m_pTokenizer->getGroup() " << m_pTokenizer->getGroup() <<
2109 // ", dest state: " << m_aStates.top().eDestination);
2111 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
2112 RTFParserState
aState(m_aStates
.top());
2113 m_bWasInFrame
= aState
.aFrame
.inFrame();
2115 // dmapper expects some content in header/footer, so if there would be nothing, add an empty paragraph.
2116 if (m_pTokenizer
->getGroup() == 1 && m_bFirstRun
)
2118 switch (m_nStreamType
)
2120 case NS_ooxml::LN_headerl
:
2121 case NS_ooxml::LN_headerr
:
2122 case NS_ooxml::LN_headerf
:
2123 case NS_ooxml::LN_footerl
:
2124 case NS_ooxml::LN_footerr
:
2125 case NS_ooxml::LN_footerf
:
2126 dispatchSymbol(RTF_PAR
);
2131 switch (aState
.eDestination
)
2133 case Destination::FONTTABLE
:
2135 writerfilter::Reference
<Table
>::Pointer_t
const pTable(
2136 new RTFReferenceTable(m_aFontTableEntries
));
2137 Mapper().table(NS_ooxml::LN_FONTTABLE
, pTable
);
2138 if (m_nDefaultFontIndex
>= 0)
2140 auto pValue
= new RTFValue(m_aFontNames
[getFontIndex(m_nDefaultFontIndex
)]);
2141 putNestedAttribute(m_aDefaultState
.aCharacterSprms
, NS_ooxml::LN_EG_RPrBase_rFonts
,
2142 NS_ooxml::LN_CT_Fonts_ascii
, pValue
);
2146 case Destination::STYLESHEET
:
2148 RTFReferenceTable::Entries_t
const pStyleTableDeduplicated(deduplicateStyleTable());
2149 writerfilter::Reference
<Table
>::Pointer_t
const pTable(
2150 new RTFReferenceTable(pStyleTableDeduplicated
));
2151 Mapper().table(NS_ooxml::LN_STYLESHEET
, pTable
);
2154 case Destination::LISTOVERRIDETABLE
:
2156 RTFSprms aListTableAttributes
;
2157 writerfilter::Reference
<Properties
>::Pointer_t pProp
2158 = new RTFReferenceProperties(aListTableAttributes
, m_aListTableSprms
);
2159 RTFReferenceTable::Entries_t aListTableEntries
;
2160 aListTableEntries
.insert(std::make_pair(0, pProp
));
2161 writerfilter::Reference
<Table
>::Pointer_t
const pTable(
2162 new RTFReferenceTable(aListTableEntries
));
2163 Mapper().table(NS_ooxml::LN_NUMBERING
, pTable
);
2166 case Destination::LISTENTRY
:
2167 for (auto& rListLevelEntry
: aState
.aListLevelEntries
)
2168 aState
.aTableSprms
.set(rListLevelEntry
.first
, rListLevelEntry
.second
,
2169 RTFOverwrite::NO_APPEND
);
2171 case Destination::FIELDINSTRUCTION
:
2173 auto pValue
= new RTFValue(m_aFormfieldAttributes
, m_aFormfieldSprms
);
2174 RTFSprms aFFAttributes
;
2176 aFFSprms
.set(NS_ooxml::LN_ffdata
, pValue
);
2177 if (!m_aStates
.top().pCurrentBuffer
)
2179 writerfilter::Reference
<Properties
>::Pointer_t pProperties
2180 = new RTFReferenceProperties(aFFAttributes
, aFFSprms
);
2181 Mapper().props(pProperties
);
2185 auto pFFValue
= new RTFValue(aFFAttributes
, aFFSprms
);
2186 bufferProperties(*m_aStates
.top().pCurrentBuffer
, pFFValue
, nullptr);
2188 m_aFormfieldAttributes
.clear();
2189 m_aFormfieldSprms
.clear();
2190 singleChar(cFieldSep
);
2193 case Destination::FIELDRESULT
:
2194 singleChar(cFieldEnd
);
2196 if (!m_aPicturePath
.isEmpty())
2198 // Read the picture into m_aStates.top().aDestinationText.
2200 dispatchDestination(RTF_PICT
);
2201 if (m_aPicturePath
.endsWith(".png"))
2202 dispatchFlag(RTF_PNGBLIP
);
2203 OUString aFileURL
= m_rMediaDescriptor
.getUnpackedValueOrDefault(
2204 utl::MediaDescriptor::PROP_URL(), OUString());
2205 OUString aPictureURL
;
2208 aPictureURL
= rtl::Uri::convertRelToAbs(aFileURL
, m_aPicturePath
);
2210 catch (const rtl::MalformedUriException
& rException
)
2212 SAL_WARN("writerfilter.rtf",
2213 "rtl::Uri::convertRelToAbs() failed: " << rException
.getMessage());
2216 if (!aPictureURL
.isEmpty())
2218 SvFileStream
aStream(aPictureURL
, StreamMode::READ
);
2219 if (aStream
.IsOpen())
2221 OUStringBuffer aBuf
;
2222 while (aStream
.good())
2224 unsigned char ch
= 0;
2225 aStream
.ReadUChar(ch
);
2228 aBuf
.append(OUString::number(ch
, 16));
2230 m_aStates
.top().aDestinationText
= aBuf
;
2234 m_aPicturePath
.clear();
2238 case Destination::LEVELTEXT
:
2240 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2241 break; // not for nested group
2242 OUString aStr
= m_aStates
.top().pDestinationText
->makeStringAndClear();
2244 // The first character is the length of the string (the rest should be ignored).
2245 sal_Int32
nLength(aStr
.toChar());
2247 if (nLength
< aStr
.getLength())
2248 aValue
= aStr
.copy(1, nLength
);
2251 auto pValue
= new RTFValue(aValue
, true);
2252 aState
.aTableAttributes
.set(NS_ooxml::LN_CT_LevelText_val
, pValue
);
2255 case Destination::LEVELNUMBERS
:
2257 bool bNestedLevelNumbers
= false;
2258 if (m_aStates
.size() > 1)
2259 // Current destination is levelnumbers and parent destination is levelnumbers as well.
2261 = m_aStates
[m_aStates
.size() - 2].eDestination
== Destination::LEVELNUMBERS
;
2262 if (!bNestedLevelNumbers
&& aState
.aTableSprms
.find(NS_ooxml::LN_CT_Lvl_lvlText
))
2264 RTFSprms
& rAttributes
2265 = aState
.aTableSprms
.find(NS_ooxml::LN_CT_Lvl_lvlText
)->getAttributes();
2266 RTFValue::Pointer_t pValue
= rAttributes
.find(NS_ooxml::LN_CT_LevelText_val
);
2267 if (pValue
&& aState
.bLevelNumbersValid
)
2269 OUString aOrig
= pValue
->getString();
2271 OUStringBuffer aBuf
;
2272 sal_Int32 nReplaces
= 1;
2273 for (int i
= 0; i
< aOrig
.getLength(); i
++)
2275 if (std::find(aState
.aLevelNumbers
.begin(), aState
.aLevelNumbers
.end(),
2277 != aState
.aLevelNumbers
.end())
2280 // '1.1.1' -> '%1.%2.%3', but '1.' (with '2.' prefix omitted) is %2.
2281 aBuf
.append(sal_Int32(nReplaces
++ + aState
.nListLevelNum
+ 1
2282 - aState
.aLevelNumbers
.size()));
2285 aBuf
.appendCopy(aOrig
, i
, 1);
2288 pValue
->setString(aBuf
.makeStringAndClear());
2291 // Have a value, but levelnumbers is not valid -> ignore it.
2292 pValue
->setString(OUString());
2296 case Destination::SHAPEPROPERTYNAME
:
2297 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2298 break; // not for nested group
2299 aState
.aShape
.aProperties
.emplace_back(
2300 m_aStates
.top().pDestinationText
->makeStringAndClear(), OUString());
2302 case Destination::SHAPEPROPERTYVALUE
:
2303 if (!aState
.aShape
.aProperties
.empty())
2305 aState
.aShape
.aProperties
.back().second
2306 = m_aStates
.top().pDestinationText
->makeStringAndClear();
2307 if (m_aStates
.top().bHadShapeText
)
2308 m_pSdrImport
->append(aState
.aShape
.aProperties
.back().first
,
2309 aState
.aShape
.aProperties
.back().second
);
2310 else if (aState
.bInShapeGroup
&& !aState
.bInShape
2311 && aState
.aShape
.aProperties
.back().first
== "rotation")
2313 // Rotation should be applied on the groupshape itself, not on each shape.
2314 aState
.aShape
.aGroupProperties
.push_back(aState
.aShape
.aProperties
.back());
2315 aState
.aShape
.aProperties
.pop_back();
2319 case Destination::PICPROP
:
2320 case Destination::SHAPEINSTRUCTION
:
2321 if (m_aStates
.size() > 1
2322 && m_aStates
[m_aStates
.size() - 2].eDestination
== Destination::SHAPEINSTRUCTION
)
2324 // Do not resolve shape if shape instruction destination is inside other shape instruction
2326 else if (!m_bObject
&& !aState
.bInListpicture
&& !aState
.bHadShapeText
2327 && !(aState
.bInShapeGroup
&& !aState
.bInShape
))
2329 // Don't trigger a shape import in case we're only leaving the \shpinst of the groupshape itself.
2330 RTFSdrImport::ShapeOrPict eType
2331 = (aState
.eDestination
== Destination::SHAPEINSTRUCTION
) ? RTFSdrImport::SHAPE
2332 : RTFSdrImport::PICT
;
2333 if (!m_aStates
.top().pCurrentBuffer
|| eType
!= RTFSdrImport::SHAPE
)
2334 m_pSdrImport
->resolve(m_aStates
.top().aShape
, true, eType
);
2337 // Shape inside table: buffer the import to have correct anchor position.
2338 // Also buffer the RTFPicture of the state stack as it contains
2340 auto pPictureValue
= new RTFValue(m_aStates
.top().aPicture
);
2341 m_aStates
.top().pCurrentBuffer
->push_back(
2342 Buf_t(BUFFER_PICTURE
, pPictureValue
, nullptr));
2343 auto pValue
= new RTFValue(m_aStates
.top().aShape
);
2345 // Buffer wrap type.
2346 for (auto& rCharacterSprm
: m_aStates
.top().aCharacterSprms
)
2348 if (rCharacterSprm
.first
== NS_ooxml::LN_EG_WrapType_wrapNone
2349 || rCharacterSprm
.first
== NS_ooxml::LN_EG_WrapType_wrapTight
)
2351 m_aStates
.top().aShape
.aWrapSprm
= rCharacterSprm
;
2356 m_aStates
.top().pCurrentBuffer
->push_back(
2357 Buf_t(BUFFER_RESOLVESHAPE
, pValue
, nullptr));
2360 else if (aState
.bInShapeGroup
&& !aState
.bInShape
)
2362 // End of a groupshape, as we're in shapegroup, but not in a real shape.
2363 for (auto& rGroupProperty
: aState
.aShape
.aGroupProperties
)
2364 m_pSdrImport
->appendGroupProperty(rGroupProperty
.first
, rGroupProperty
.second
);
2365 aState
.aShape
.aGroupProperties
.clear();
2368 case Destination::BOOKMARKSTART
:
2370 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2371 break; // not for nested group
2372 OUString aStr
= m_aStates
.top().pDestinationText
->makeStringAndClear();
2373 int nPos
= m_aBookmarks
.size();
2374 m_aBookmarks
[aStr
] = nPos
;
2375 if (!m_aStates
.top().pCurrentBuffer
)
2376 Mapper().props(new RTFReferenceProperties(lcl_getBookmarkProperties(nPos
, aStr
)));
2378 bufferProperties(*m_aStates
.top().pCurrentBuffer
,
2379 new RTFValue(lcl_getBookmarkProperties(nPos
, aStr
)), nullptr);
2382 case Destination::BOOKMARKEND
:
2384 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2385 break; // not for nested group
2386 OUString aStr
= m_aStates
.top().pDestinationText
->makeStringAndClear();
2387 if (!m_aStates
.top().pCurrentBuffer
)
2388 Mapper().props(new RTFReferenceProperties(
2389 lcl_getBookmarkProperties(m_aBookmarks
[aStr
], aStr
)));
2391 bufferProperties(*m_aStates
.top().pCurrentBuffer
,
2392 new RTFValue(lcl_getBookmarkProperties(m_aBookmarks
[aStr
], aStr
)),
2396 case Destination::INDEXENTRY
:
2397 case Destination::TOCENTRY
:
2399 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2400 break; // not for nested group
2401 OUString
str(m_aStates
.top().pDestinationText
->makeStringAndClear());
2402 // dmapper expects this as a field, so let's fake something...
2403 OUString
const field((Destination::INDEXENTRY
== aState
.eDestination
)
2404 ? OUStringLiteral("XE")
2405 : OUStringLiteral("TC"));
2406 str
= field
+ " \"" + str
.replaceAll("\"", "\\\"") + "\"";
2407 singleChar(cFieldStart
);
2408 Mapper().utext(reinterpret_cast<sal_uInt8
const*>(str
.getStr()), str
.getLength());
2409 singleChar(cFieldSep
);
2411 singleChar(cFieldEnd
);
2414 case Destination::FORMFIELDNAME
:
2416 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2417 break; // not for nested group
2418 auto pValue
= new RTFValue(m_aStates
.top().pDestinationText
->makeStringAndClear());
2419 m_aFormfieldSprms
.set(NS_ooxml::LN_CT_FFData_name
, pValue
);
2422 case Destination::FORMFIELDLIST
:
2424 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2425 break; // not for nested group
2426 auto pValue
= new RTFValue(m_aStates
.top().pDestinationText
->makeStringAndClear());
2427 m_aFormfieldSprms
.set(NS_ooxml::LN_CT_FFDDList_listEntry
, pValue
);
2430 case Destination::DATAFIELD
:
2434 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2435 break; // not for nested group
2437 = OUStringToOString(m_aStates
.top().pDestinationText
->makeStringAndClear(),
2438 aState
.nCurrentEncoding
);
2441 int b
= 0, count
= 2;
2442 for (int i
= 0; i
< aStr
.getLength(); ++i
)
2445 if (ch
!= 0x0d && ch
!= 0x0a)
2448 sal_Int8 parsed
= msfilter::rtfutil::AsHex(ch
);
2450 return RTFError::HEX_INVALID
;
2455 aBuf
.append(static_cast<char>(b
));
2461 aStr
= aBuf
.makeStringAndClear();
2463 // ignore the first bytes
2464 if (aStr
.getLength() > 8)
2465 aStr
= aStr
.copy(8);
2467 sal_Int32 nLength
= aStr
.toChar();
2468 if (!aStr
.isEmpty())
2469 aStr
= aStr
.copy(1);
2470 nLength
= std::min(nLength
, aStr
.getLength());
2471 OString aName
= aStr
.copy(0, nLength
);
2472 if (aStr
.getLength() > nLength
)
2473 aStr
= aStr
.copy(nLength
+ 1); // zero-terminated string
2476 // extract default text
2477 nLength
= aStr
.toChar();
2478 if (!aStr
.isEmpty())
2479 aStr
= aStr
.copy(1);
2480 auto pNValue
= new RTFValue(OStringToOUString(aName
, aState
.nCurrentEncoding
));
2481 m_aFormfieldSprms
.set(NS_ooxml::LN_CT_FFData_name
, pNValue
);
2484 OString aDefaultText
= aStr
.copy(0, std::min(nLength
, aStr
.getLength()));
2486 = new RTFValue(OStringToOUString(aDefaultText
, aState
.nCurrentEncoding
));
2487 m_aFormfieldSprms
.set(NS_ooxml::LN_CT_FFTextInput_default
, pDValue
);
2490 m_bFormField
= false;
2494 case Destination::CREATIONTIME
:
2495 if (m_xDocumentProperties
.is())
2496 m_xDocumentProperties
->setCreationDate(lcl_getDateTime(aState
));
2498 case Destination::REVISIONTIME
:
2499 if (m_xDocumentProperties
.is())
2500 m_xDocumentProperties
->setModificationDate(lcl_getDateTime(aState
));
2502 case Destination::PRINTTIME
:
2503 if (m_xDocumentProperties
.is())
2504 m_xDocumentProperties
->setPrintDate(lcl_getDateTime(aState
));
2506 case Destination::AUTHOR
:
2507 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2508 break; // not for nested group
2509 if (m_xDocumentProperties
.is())
2510 m_xDocumentProperties
->setAuthor(
2511 m_aStates
.top().pDestinationText
->makeStringAndClear());
2513 case Destination::KEYWORDS
:
2514 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2515 break; // not for nested group
2516 if (m_xDocumentProperties
.is())
2517 m_xDocumentProperties
->setKeywords(comphelper::string::convertCommaSeparated(
2518 m_aStates
.top().pDestinationText
->makeStringAndClear()));
2520 case Destination::COMMENT
:
2521 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2522 break; // not for nested group
2523 if (m_xDocumentProperties
.is())
2524 m_xDocumentProperties
->setGenerator(
2525 m_aStates
.top().pDestinationText
->makeStringAndClear());
2527 case Destination::SUBJECT
:
2528 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2529 break; // not for nested group
2530 if (m_xDocumentProperties
.is())
2531 m_xDocumentProperties
->setSubject(
2532 m_aStates
.top().pDestinationText
->makeStringAndClear());
2534 case Destination::TITLE
:
2536 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2537 break; // not for nested group
2538 if (m_xDocumentProperties
.is())
2539 m_xDocumentProperties
->setTitle(aState
.pDestinationText
->makeStringAndClear());
2543 case Destination::DOCCOMM
:
2544 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2545 break; // not for nested group
2546 if (m_xDocumentProperties
.is())
2547 m_xDocumentProperties
->setDescription(
2548 m_aStates
.top().pDestinationText
->makeStringAndClear());
2550 case Destination::OPERATOR
:
2551 case Destination::COMPANY
:
2553 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2554 break; // not for nested group
2555 OUString aName
= aState
.eDestination
== Destination::OPERATOR
? OUString("Operator")
2556 : OUString("Company");
2557 uno::Any aValue
= uno::makeAny(m_aStates
.top().pDestinationText
->makeStringAndClear());
2558 if (m_xDocumentProperties
.is())
2560 uno::Reference
<beans::XPropertyContainer
> xUserDefinedProperties
2561 = m_xDocumentProperties
->getUserDefinedProperties();
2562 uno::Reference
<beans::XPropertySet
> xPropertySet(xUserDefinedProperties
,
2564 uno::Reference
<beans::XPropertySetInfo
> xPropertySetInfo
2565 = xPropertySet
->getPropertySetInfo();
2566 if (xPropertySetInfo
->hasPropertyByName(aName
))
2567 xPropertySet
->setPropertyValue(aName
, aValue
);
2569 xUserDefinedProperties
->addProperty(aName
, beans::PropertyAttribute::REMOVABLE
,
2574 case Destination::OBJDATA
:
2576 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2577 break; // not for nested group
2579 RTFError eError
= handleEmbeddedObject();
2580 if (eError
!= RTFError::OK
)
2584 case Destination::OBJCLASS
:
2586 auto pValue
= new RTFValue(m_aStates
.top().pDestinationText
->makeStringAndClear());
2587 m_aOLEAttributes
.set(NS_ooxml::LN_CT_OLEObject_ProgID
, pValue
);
2590 case Destination::OBJECT
:
2594 // if the object is in a special container we will use the \result
2595 // element instead of the \objdata
2596 // (see RTF_OBJECT in RTFDocumentImpl::dispatchDestination)
2600 RTFSprms aObjectSprms
;
2601 auto pOLEValue
= new RTFValue(m_aOLEAttributes
);
2602 aObjectSprms
.set(NS_ooxml::LN_OLEObject_OLEObject
, pOLEValue
);
2604 RTFSprms aObjAttributes
;
2606 auto pValue
= new RTFValue(m_aObjectAttributes
, aObjectSprms
);
2607 aObjSprms
.set(NS_ooxml::LN_object
, pValue
);
2608 writerfilter::Reference
<Properties
>::Pointer_t pProperties
2609 = new RTFReferenceProperties(aObjAttributes
, aObjSprms
);
2610 uno::Reference
<drawing::XShape
> xShape
;
2611 RTFValue::Pointer_t pShape
= m_aObjectAttributes
.find(NS_ooxml::LN_shape
);
2612 OSL_ASSERT(pShape
.get());
2614 pShape
->getAny() >>= xShape
;
2617 Mapper().startShape(xShape
);
2618 Mapper().props(pProperties
);
2619 Mapper().endShape();
2621 m_aObjectAttributes
.clear();
2622 m_aOLEAttributes
.clear();
2626 case Destination::ANNOTATIONDATE
:
2628 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2629 break; // not for nested group
2630 OUString
aStr(OStringToOUString(
2631 DTTM22OString(m_aStates
.top().pDestinationText
->makeStringAndClear().toInt32()),
2632 aState
.nCurrentEncoding
));
2633 auto pValue
= new RTFValue(aStr
);
2634 RTFSprms aAnnAttributes
;
2635 aAnnAttributes
.set(NS_ooxml::LN_CT_TrackChange_date
, pValue
);
2636 writerfilter::Reference
<Properties
>::Pointer_t pProperties
2637 = new RTFReferenceProperties(aAnnAttributes
);
2638 Mapper().props(pProperties
);
2641 case Destination::ANNOTATIONAUTHOR
:
2642 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2643 break; // not for nested group
2644 m_aAuthor
= m_aStates
.top().pDestinationText
->makeStringAndClear();
2646 case Destination::ATNID
:
2647 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2648 break; // not for nested group
2649 m_aAuthorInitials
= m_aStates
.top().pDestinationText
->makeStringAndClear();
2651 case Destination::ANNOTATIONREFERENCESTART
:
2652 case Destination::ANNOTATIONREFERENCEEND
:
2654 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2655 break; // not for nested group
2656 OUString aStr
= m_aStates
.top().pDestinationText
->makeStringAndClear();
2657 auto pValue
= new RTFValue(aStr
.toInt32());
2658 RTFSprms aAttributes
;
2659 if (aState
.eDestination
== Destination::ANNOTATIONREFERENCESTART
)
2660 aAttributes
.set(NS_ooxml::LN_EG_RangeMarkupElements_commentRangeStart
, pValue
);
2662 aAttributes
.set(NS_ooxml::LN_EG_RangeMarkupElements_commentRangeEnd
, pValue
);
2663 writerfilter::Reference
<Properties
>::Pointer_t pProperties
2664 = new RTFReferenceProperties(aAttributes
);
2665 Mapper().props(pProperties
);
2668 case Destination::ANNOTATIONREFERENCE
:
2670 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2671 break; // not for nested group
2672 OUString aStr
= m_aStates
.top().pDestinationText
->makeStringAndClear();
2673 RTFSprms aAnnAttributes
;
2674 aAnnAttributes
.set(NS_ooxml::LN_CT_Markup_id
, new RTFValue(aStr
.toInt32()));
2675 Mapper().props(new RTFReferenceProperties(aAnnAttributes
));
2678 case Destination::FALT
:
2680 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2681 break; // not for nested group
2682 OUString
aStr(m_aStates
.top().pDestinationText
->makeStringAndClear());
2683 auto pValue
= new RTFValue(aStr
);
2684 aState
.aTableSprms
.set(NS_ooxml::LN_CT_Font_altName
, pValue
);
2687 case Destination::DRAWINGOBJECT
:
2688 if (m_aStates
.top().aDrawingObject
.xShape
.is())
2690 RTFDrawingObject
& rDrawing
= m_aStates
.top().aDrawingObject
;
2691 uno::Reference
<drawing::XShape
> xShape(rDrawing
.xShape
);
2692 uno::Reference
<beans::XPropertySet
> xPropertySet(rDrawing
.xPropertySet
);
2694 uno::Reference
<lang::XServiceInfo
> xServiceInfo(xShape
, uno::UNO_QUERY
);
2695 bool bTextFrame
= xServiceInfo
->supportsService("com.sun.star.text.TextFrame");
2697 // The default is certainly not inline, but then what Word supports is just at-character.
2698 xPropertySet
->setPropertyValue(
2699 "AnchorType", uno::makeAny(text::TextContentAnchorType_AT_CHARACTER
));
2703 xPropertySet
->setPropertyValue("HoriOrientPosition",
2704 uno::makeAny(rDrawing
.nLeft
));
2705 xPropertySet
->setPropertyValue("VertOrientPosition",
2706 uno::makeAny(rDrawing
.nTop
));
2710 xShape
->setPosition(awt::Point(rDrawing
.nLeft
, rDrawing
.nTop
));
2712 xShape
->setSize(awt::Size(rDrawing
.nRight
, rDrawing
.nBottom
));
2714 if (rDrawing
.bHasLineColor
)
2716 uno::Any aLineColor
= uno::makeAny(sal_uInt32((rDrawing
.nLineColorR
<< 16)
2717 + (rDrawing
.nLineColorG
<< 8)
2718 + rDrawing
.nLineColorB
));
2719 uno::Any aLineWidth
;
2720 RTFSdrImport::resolveLineColorAndWidth(bTextFrame
, xPropertySet
, aLineColor
,
2723 if (rDrawing
.bHasFillColor
)
2724 xPropertySet
->setPropertyValue(
2725 "FillColor", uno::makeAny(sal_uInt32((rDrawing
.nFillColorR
<< 16)
2726 + (rDrawing
.nFillColorG
<< 8)
2727 + rDrawing
.nFillColorB
)));
2728 else if (!bTextFrame
)
2729 // If there is no fill, the Word default is 100% transparency.
2730 xPropertySet
->setPropertyValue("FillTransparence",
2731 uno::makeAny(sal_Int32(100)));
2733 RTFSdrImport::resolveFLine(xPropertySet
, rDrawing
.nFLine
);
2735 if (!m_aStates
.top().aDrawingObject
.bHadShapeText
)
2737 Mapper().startShape(xShape
);
2739 Mapper().endShape();
2742 case Destination::PICT
:
2743 // fdo#79319 ignore picture data if it's really a shape
2744 if (!m_pSdrImport
->isFakePict())
2746 resolvePict(true, m_pSdrImport
->getCurrentShape());
2748 m_bNeedFinalPar
= true;
2750 case Destination::SHAPE
:
2751 m_bNeedFinalPar
= true;
2752 m_bNeedCr
= m_bNeedCrOrig
;
2753 if (aState
.aFrame
.inFrame())
2755 // parBreak() modifies m_aStates.top() so we can't apply resetFrame() directly on aState
2758 // Save this state for later use, so we only reset frame status only for the first shape inside a frame.
2759 aState
= m_aStates
.top();
2763 case Destination::MOMATH
:
2765 m_aMathBuffer
.appendClosingTag(M_TOKEN(oMath
));
2767 SvGlobalName
aGlobalName(SO3_SM_CLASSID
);
2768 comphelper::EmbeddedObjectContainer aContainer
;
2770 uno::Reference
<embed::XEmbeddedObject
> xObject
2771 = aContainer
.CreateEmbeddedObject(aGlobalName
.GetByteSequence(), aName
);
2772 uno::Reference
<util::XCloseable
> xComponent(xObject
->getComponent(),
2773 uno::UNO_QUERY_THROW
);
2774 // gcc4.4 (and 4.3 and possibly older) have a problem with dynamic_cast directly to the target class,
2775 // so help it with an intermediate cast. I'm not sure what exactly the problem is, seems to be unrelated
2776 // to RTLD_GLOBAL, so most probably a gcc bug.
2777 auto& rImport
= dynamic_cast<oox::FormulaImportBase
&>(
2778 dynamic_cast<SfxBaseModel
&>(*xComponent
.get()));
2779 rImport
.readFormulaOoxml(m_aMathBuffer
);
2780 auto pValue
= new RTFValue(xObject
);
2781 RTFSprms aMathAttributes
;
2782 aMathAttributes
.set(NS_ooxml::LN_starmath
, pValue
);
2783 writerfilter::Reference
<Properties
>::Pointer_t pProperties
2784 = new RTFReferenceProperties(aMathAttributes
);
2785 Mapper().props(pProperties
);
2786 m_aMathBuffer
= oox::formulaimport::XmlStreamBuilder();
2789 case Destination::MR
:
2790 lcl_DestinationToMath(m_aStates
.top().pDestinationText
, m_aMathBuffer
, m_bMathNor
);
2792 case Destination::MF
:
2793 m_aMathBuffer
.appendClosingTag(M_TOKEN(f
));
2795 case Destination::MFPR
:
2796 m_aMathBuffer
.appendClosingTag(M_TOKEN(fPr
));
2798 case Destination::MCTRLPR
:
2799 m_aMathBuffer
.appendClosingTag(M_TOKEN(ctrlPr
));
2801 case Destination::MNUM
:
2802 m_aMathBuffer
.appendClosingTag(M_TOKEN(num
));
2804 case Destination::MDEN
:
2805 m_aMathBuffer
.appendClosingTag(M_TOKEN(den
));
2807 case Destination::MACC
:
2808 m_aMathBuffer
.appendClosingTag(M_TOKEN(acc
));
2810 case Destination::MACCPR
:
2811 m_aMathBuffer
.appendClosingTag(M_TOKEN(accPr
));
2813 case Destination::MCHR
:
2814 case Destination::MPOS
:
2815 case Destination::MVERTJC
:
2816 case Destination::MSTRIKEH
:
2817 case Destination::MDEGHIDE
:
2818 case Destination::MBEGCHR
:
2819 case Destination::MSEPCHR
:
2820 case Destination::MENDCHR
:
2821 case Destination::MSUBHIDE
:
2822 case Destination::MSUPHIDE
:
2823 case Destination::MTYPE
:
2824 case Destination::MGROW
:
2826 sal_Int32 nMathToken
= 0;
2827 switch (aState
.eDestination
)
2829 case Destination::MCHR
:
2830 nMathToken
= M_TOKEN(chr
);
2832 case Destination::MPOS
:
2833 nMathToken
= M_TOKEN(pos
);
2835 case Destination::MVERTJC
:
2836 nMathToken
= M_TOKEN(vertJc
);
2838 case Destination::MSTRIKEH
:
2839 nMathToken
= M_TOKEN(strikeH
);
2841 case Destination::MDEGHIDE
:
2842 nMathToken
= M_TOKEN(degHide
);
2844 case Destination::MBEGCHR
:
2845 nMathToken
= M_TOKEN(begChr
);
2847 case Destination::MSEPCHR
:
2848 nMathToken
= M_TOKEN(sepChr
);
2850 case Destination::MENDCHR
:
2851 nMathToken
= M_TOKEN(endChr
);
2853 case Destination::MSUBHIDE
:
2854 nMathToken
= M_TOKEN(subHide
);
2856 case Destination::MSUPHIDE
:
2857 nMathToken
= M_TOKEN(supHide
);
2859 case Destination::MTYPE
:
2860 nMathToken
= M_TOKEN(type
);
2862 case Destination::MGROW
:
2863 nMathToken
= M_TOKEN(grow
);
2869 oox::formulaimport::XmlStream::AttributeList aAttribs
;
2870 aAttribs
[M_TOKEN(val
)] = m_aStates
.top().pDestinationText
->makeStringAndClear();
2871 m_aMathBuffer
.appendOpeningTag(nMathToken
, aAttribs
);
2872 m_aMathBuffer
.appendClosingTag(nMathToken
);
2875 case Destination::ME
:
2876 m_aMathBuffer
.appendClosingTag(M_TOKEN(e
));
2878 case Destination::MBAR
:
2879 m_aMathBuffer
.appendClosingTag(M_TOKEN(bar
));
2881 case Destination::MBARPR
:
2882 m_aMathBuffer
.appendClosingTag(M_TOKEN(barPr
));
2884 case Destination::MD
:
2885 m_aMathBuffer
.appendClosingTag(M_TOKEN(d
));
2887 case Destination::MDPR
:
2888 m_aMathBuffer
.appendClosingTag(M_TOKEN(dPr
));
2890 case Destination::MFUNC
:
2891 m_aMathBuffer
.appendClosingTag(M_TOKEN(func
));
2893 case Destination::MFUNCPR
:
2894 m_aMathBuffer
.appendClosingTag(M_TOKEN(funcPr
));
2896 case Destination::MFNAME
:
2897 m_aMathBuffer
.appendClosingTag(M_TOKEN(fName
));
2899 case Destination::MLIMLOW
:
2900 m_aMathBuffer
.appendClosingTag(M_TOKEN(limLow
));
2902 case Destination::MLIMLOWPR
:
2903 m_aMathBuffer
.appendClosingTag(M_TOKEN(limLowPr
));
2905 case Destination::MLIM
:
2906 m_aMathBuffer
.appendClosingTag(M_TOKEN(lim
));
2908 case Destination::MM
:
2909 m_aMathBuffer
.appendClosingTag(M_TOKEN(m
));
2911 case Destination::MMPR
:
2912 m_aMathBuffer
.appendClosingTag(M_TOKEN(mPr
));
2914 case Destination::MMR
:
2915 m_aMathBuffer
.appendClosingTag(M_TOKEN(mr
));
2917 case Destination::MNARY
:
2918 m_aMathBuffer
.appendClosingTag(M_TOKEN(nary
));
2920 case Destination::MNARYPR
:
2921 m_aMathBuffer
.appendClosingTag(M_TOKEN(naryPr
));
2923 case Destination::MSUB
:
2924 m_aMathBuffer
.appendClosingTag(M_TOKEN(sub
));
2926 case Destination::MSUP
:
2927 m_aMathBuffer
.appendClosingTag(M_TOKEN(sup
));
2929 case Destination::MLIMUPP
:
2930 m_aMathBuffer
.appendClosingTag(M_TOKEN(limUpp
));
2932 case Destination::MLIMUPPPR
:
2933 m_aMathBuffer
.appendClosingTag(M_TOKEN(limUppPr
));
2935 case Destination::MGROUPCHR
:
2936 m_aMathBuffer
.appendClosingTag(M_TOKEN(groupChr
));
2938 case Destination::MGROUPCHRPR
:
2939 m_aMathBuffer
.appendClosingTag(M_TOKEN(groupChrPr
));
2941 case Destination::MBORDERBOX
:
2942 m_aMathBuffer
.appendClosingTag(M_TOKEN(borderBox
));
2944 case Destination::MBORDERBOXPR
:
2945 m_aMathBuffer
.appendClosingTag(M_TOKEN(borderBoxPr
));
2947 case Destination::MRAD
:
2948 m_aMathBuffer
.appendClosingTag(M_TOKEN(rad
));
2950 case Destination::MRADPR
:
2951 m_aMathBuffer
.appendClosingTag(M_TOKEN(radPr
));
2953 case Destination::MDEG
:
2954 m_aMathBuffer
.appendClosingTag(M_TOKEN(deg
));
2956 case Destination::MSSUB
:
2957 m_aMathBuffer
.appendClosingTag(M_TOKEN(sSub
));
2959 case Destination::MSSUBPR
:
2960 m_aMathBuffer
.appendClosingTag(M_TOKEN(sSubPr
));
2962 case Destination::MSSUP
:
2963 m_aMathBuffer
.appendClosingTag(M_TOKEN(sSup
));
2965 case Destination::MSSUPPR
:
2966 m_aMathBuffer
.appendClosingTag(M_TOKEN(sSupPr
));
2968 case Destination::MSSUBSUP
:
2969 m_aMathBuffer
.appendClosingTag(M_TOKEN(sSubSup
));
2971 case Destination::MSSUBSUPPR
:
2972 m_aMathBuffer
.appendClosingTag(M_TOKEN(sSubSupPr
));
2974 case Destination::MSPRE
:
2975 m_aMathBuffer
.appendClosingTag(M_TOKEN(sPre
));
2977 case Destination::MSPREPR
:
2978 m_aMathBuffer
.appendClosingTag(M_TOKEN(sPrePr
));
2980 case Destination::MBOX
:
2981 m_aMathBuffer
.appendClosingTag(M_TOKEN(box
));
2983 case Destination::MEQARR
:
2984 m_aMathBuffer
.appendClosingTag(M_TOKEN(eqArr
));
2986 case Destination::SHAPEGROUP
:
2987 if (aState
.bCreatedShapeGroup
)
2988 m_pSdrImport
->popParent();
2990 case Destination::PROPNAME
:
2991 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2992 break; // not for nested group
2993 aState
.aPropName
= m_aStates
.top().pDestinationText
->makeStringAndClear();
2995 case Destination::STATICVAL
:
2996 if (&m_aStates
.top().aDestinationText
!= m_aStates
.top().pDestinationText
)
2997 break; // not for nested group
2998 if (m_xDocumentProperties
.is())
3000 // Find out what is the key, value type and value we want to set.
3001 uno::Reference
<beans::XPropertyContainer
> xPropertyContainer
3002 = m_xDocumentProperties
->getUserDefinedProperties();
3003 const OUString
& rKey
= m_aStates
.top().aPropName
;
3004 OUString aStaticVal
= m_aStates
.top().pDestinationText
->makeStringAndClear();
3006 if (m_aStates
.top().aPropType
== cppu::UnoType
<OUString
>::get())
3007 aAny
<<= aStaticVal
;
3008 else if (m_aStates
.top().aPropType
== cppu::UnoType
<sal_Int32
>::get())
3009 aAny
<<= aStaticVal
.toInt32();
3010 else if (m_aStates
.top().aPropType
== cppu::UnoType
<bool>::get())
3011 aAny
<<= aStaticVal
.toBoolean();
3012 else if (m_aStates
.top().aPropType
== cppu::UnoType
<util::DateTime
>::get())
3013 aAny
<<= getDateTimeFromUserProp(aStaticVal
);
3014 else if (m_aStates
.top().aPropType
== cppu::UnoType
<double>::get())
3015 aAny
<<= aStaticVal
.toDouble();
3017 xPropertyContainer
->addProperty(rKey
, beans::PropertyAttribute::REMOVABLE
, aAny
);
3020 case Destination::USERPROPS
:
3022 // These are the imported properties.
3023 uno::Reference
<document::XDocumentProperties
> xDocumentProperties
3024 = m_xDocumentProperties
;
3026 // These are the real document properties.
3027 uno::Reference
<document::XDocumentPropertiesSupplier
> xDocumentPropertiesSupplier(
3028 m_xDstDoc
, uno::UNO_QUERY
);
3029 if (xDocumentPropertiesSupplier
.is())
3030 m_xDocumentProperties
.set(xDocumentPropertiesSupplier
->getDocumentProperties(),
3033 if (m_xDocumentProperties
.is())
3037 // Check classification.
3038 if (!SfxClassificationHelper::ShowPasteInfo(SfxClassificationHelper::CheckPaste(
3039 xDocumentProperties
, m_xDocumentProperties
)))
3040 return RTFError::CLASSIFICATION
;
3043 uno::Reference
<beans::XPropertyContainer
> xClipboardPropertyContainer
3044 = xDocumentProperties
->getUserDefinedProperties();
3045 uno::Reference
<beans::XPropertyContainer
> xDocumentPropertyContainer
3046 = m_xDocumentProperties
->getUserDefinedProperties();
3047 uno::Reference
<beans::XPropertySet
> xClipboardPropertySet(
3048 xClipboardPropertyContainer
, uno::UNO_QUERY
);
3049 uno::Reference
<beans::XPropertySet
> xDocumentPropertySet(xDocumentPropertyContainer
,
3051 uno::Sequence
<beans::Property
> aClipboardProperties
3052 = xClipboardPropertySet
->getPropertySetInfo()->getProperties();
3053 uno::Sequence
<beans::Property
> aDocumentProperties
3054 = xDocumentPropertySet
->getPropertySetInfo()->getProperties();
3056 for (const beans::Property
& rProperty
: aClipboardProperties
)
3058 const OUString
& rKey
= rProperty
.Name
;
3059 uno::Any aValue
= xClipboardPropertySet
->getPropertyValue(rKey
);
3063 if (lcl_containsProperty(aDocumentProperties
, rKey
))
3065 // When pasting, don't update existing properties.
3067 xDocumentPropertySet
->setPropertyValue(rKey
, aValue
);
3070 xDocumentPropertyContainer
->addProperty(
3071 rKey
, beans::PropertyAttribute::REMOVABLE
, aValue
);
3073 catch (const uno::Exception
& rException
)
3075 SAL_WARN("writerfilter.rtf",
3076 "failed to set property " << rKey
<< ": " << rException
);
3086 // See if we need to end a track change
3087 if (aState
.bStartedTrackchange
)
3090 auto pValue
= new RTFValue(0);
3091 aTCSprms
.set(NS_ooxml::LN_endtrackchange
, pValue
);
3092 if (!m_aStates
.top().pCurrentBuffer
)
3093 Mapper().props(new RTFReferenceProperties(RTFSprms(), aTCSprms
));
3095 bufferProperties(*m_aStates
.top().pCurrentBuffer
, new RTFValue(RTFSprms(), aTCSprms
),
3099 // This is the end of the doc, see if we need to close the last section.
3100 if (m_pTokenizer
->getGroup() == 1 && !m_bFirstRun
)
3102 // \par means an empty paragraph at the end of footnotes/endnotes, but
3103 // not in case of other substreams, like headers.
3105 && !(m_nStreamType
== NS_ooxml::LN_footnote
|| m_nStreamType
== NS_ooxml::LN_endnote
)
3107 dispatchSymbol(RTF_PAR
);
3108 if (m_bNeedSect
) // may be set by dispatchSymbol above!
3114 m_pTokenizer
->popGroup();
3117 switch (aState
.eDestination
)
3119 case Destination::LISTENTRY
:
3121 auto pValue
= new RTFValue(aState
.aTableAttributes
, aState
.aTableSprms
);
3122 m_aListTableSprms
.set(NS_ooxml::LN_CT_Numbering_abstractNum
, pValue
,
3123 RTFOverwrite::NO_APPEND
);
3124 m_aListTable
[aState
.nCurrentListIndex
] = pValue
;
3126 m_aInvalidListTableFirstIndents
[aState
.nCurrentListIndex
]
3127 = m_aInvalidListLevelFirstIndents
;
3128 m_aInvalidListLevelFirstIndents
.clear();
3131 case Destination::PARAGRAPHNUMBERING
:
3133 RTFValue::Pointer_t pIdValue
3134 = aState
.aTableAttributes
.find(NS_ooxml::LN_CT_AbstractNum_nsid
);
3135 if (pIdValue
.get() && !m_aStates
.empty())
3137 // Abstract numbering
3138 RTFSprms aLeveltextAttributes
;
3139 OUString aTextValue
;
3140 RTFValue::Pointer_t pTextBefore
3141 = aState
.aTableAttributes
.find(NS_ooxml::LN_CT_LevelText_val
);
3143 aTextValue
+= pTextBefore
->getString();
3145 RTFValue::Pointer_t pTextAfter
3146 = aState
.aTableAttributes
.find(NS_ooxml::LN_CT_LevelSuffix_val
);
3148 aTextValue
+= pTextAfter
->getString();
3149 auto pTextValue
= new RTFValue(aTextValue
);
3150 aLeveltextAttributes
.set(NS_ooxml::LN_CT_LevelText_val
, pTextValue
);
3152 RTFSprms aLevelAttributes
;
3153 RTFSprms aLevelSprms
;
3154 auto pIlvlValue
= new RTFValue(0);
3155 aLevelAttributes
.set(NS_ooxml::LN_CT_Lvl_ilvl
, pIlvlValue
);
3157 RTFValue::Pointer_t pFmtValue
= aState
.aTableSprms
.find(NS_ooxml::LN_CT_Lvl_numFmt
);
3159 aLevelSprms
.set(NS_ooxml::LN_CT_Lvl_numFmt
, pFmtValue
);
3161 RTFValue::Pointer_t pStartatValue
3162 = aState
.aTableSprms
.find(NS_ooxml::LN_CT_Lvl_start
);
3164 aLevelSprms
.set(NS_ooxml::LN_CT_Lvl_start
, pStartatValue
);
3166 auto pLeveltextValue
= new RTFValue(aLeveltextAttributes
);
3167 aLevelSprms
.set(NS_ooxml::LN_CT_Lvl_lvlText
, pLeveltextValue
);
3168 RTFValue::Pointer_t pRunProps
= aState
.aTableSprms
.find(NS_ooxml::LN_CT_Lvl_rPr
);
3170 aLevelSprms
.set(NS_ooxml::LN_CT_Lvl_rPr
, pRunProps
);
3172 RTFSprms aAbstractAttributes
;
3173 RTFSprms aAbstractSprms
;
3174 aAbstractAttributes
.set(NS_ooxml::LN_CT_AbstractNum_abstractNumId
, pIdValue
);
3175 auto pLevelValue
= new RTFValue(aLevelAttributes
, aLevelSprms
);
3176 aAbstractSprms
.set(NS_ooxml::LN_CT_AbstractNum_lvl
, pLevelValue
,
3177 RTFOverwrite::NO_APPEND
);
3179 RTFSprms aListTableSprms
;
3180 auto pAbstractValue
= new RTFValue(aAbstractAttributes
, aAbstractSprms
);
3181 // It's important that Numbering_abstractNum and Numbering_num never overwrites previous values.
3182 aListTableSprms
.set(NS_ooxml::LN_CT_Numbering_abstractNum
, pAbstractValue
,
3183 RTFOverwrite::NO_APPEND
);
3186 RTFSprms aNumberingAttributes
;
3187 RTFSprms aNumberingSprms
;
3188 aNumberingAttributes
.set(NS_ooxml::LN_CT_AbstractNum_nsid
, pIdValue
);
3189 aNumberingSprms
.set(NS_ooxml::LN_CT_Num_abstractNumId
, pIdValue
);
3190 auto pNumberingValue
= new RTFValue(aNumberingAttributes
, aNumberingSprms
);
3191 aListTableSprms
.set(NS_ooxml::LN_CT_Numbering_num
, pNumberingValue
,
3192 RTFOverwrite::NO_APPEND
);
3195 RTFSprms aListTableAttributes
;
3196 writerfilter::Reference
<Properties
>::Pointer_t pProp
3197 = new RTFReferenceProperties(aListTableAttributes
, aListTableSprms
);
3199 RTFReferenceTable::Entries_t aListTableEntries
;
3200 aListTableEntries
.insert(std::make_pair(0, pProp
));
3201 writerfilter::Reference
<Table
>::Pointer_t
const pTable(
3202 new RTFReferenceTable(aListTableEntries
));
3203 Mapper().table(NS_ooxml::LN_NUMBERING
, pTable
);
3206 putNestedSprm(m_aStates
.top().aParagraphSprms
, NS_ooxml::LN_CT_PPrBase_numPr
,
3207 NS_ooxml::LN_CT_NumPr_ilvl
, pIlvlValue
);
3208 putNestedSprm(m_aStates
.top().aParagraphSprms
, NS_ooxml::LN_CT_PPrBase_numPr
,
3209 NS_ooxml::LN_CT_NumPr_numId
, pIdValue
);
3213 case Destination::PARAGRAPHNUMBERING_TEXTAFTER
:
3214 if (!m_aStates
.empty())
3216 // FIXME: don't use pDestinationText, points to popped state
3217 auto pValue
= new RTFValue(aState
.aDestinationText
.makeStringAndClear(), true);
3218 m_aStates
.top().aTableAttributes
.set(NS_ooxml::LN_CT_LevelSuffix_val
, pValue
);
3221 case Destination::PARAGRAPHNUMBERING_TEXTBEFORE
:
3222 if (!m_aStates
.empty())
3224 // FIXME: don't use pDestinationText, points to popped state
3225 auto pValue
= new RTFValue(aState
.aDestinationText
.makeStringAndClear(), true);
3226 m_aStates
.top().aTableAttributes
.set(NS_ooxml::LN_CT_LevelText_val
, pValue
);
3229 case Destination::LISTNAME
:
3231 case Destination::LISTLEVEL
:
3232 if (!m_aStates
.empty())
3234 auto pInnerValue
= new RTFValue(m_aStates
.top().nListLevelNum
++);
3235 aState
.aTableAttributes
.set(NS_ooxml::LN_CT_Lvl_ilvl
, pInnerValue
);
3237 auto pValue
= new RTFValue(aState
.aTableAttributes
, aState
.aTableSprms
);
3238 if (m_aStates
.top().eDestination
!= Destination::LFOLEVEL
)
3239 m_aStates
.top().aListLevelEntries
.set(NS_ooxml::LN_CT_AbstractNum_lvl
, pValue
,
3240 RTFOverwrite::NO_APPEND
);
3242 m_aStates
.top().aTableSprms
.set(NS_ooxml::LN_CT_NumLvl_lvl
, pValue
);
3245 case Destination::LFOLEVEL
:
3246 if (!m_aStates
.empty())
3248 auto pInnerValue
= new RTFValue(m_aStates
.top().nListLevelNum
++);
3249 aState
.aTableAttributes
.set(NS_ooxml::LN_CT_NumLvl_ilvl
, pInnerValue
);
3251 auto pValue
= new RTFValue(aState
.aTableAttributes
, aState
.aTableSprms
);
3252 m_aStates
.top().aTableSprms
.set(NS_ooxml::LN_CT_Num_lvlOverride
, pValue
,
3253 RTFOverwrite::NO_APPEND
);
3256 // list override table
3257 case Destination::LISTOVERRIDEENTRY
:
3258 if (!m_aStates
.empty())
3260 if (m_aStates
.top().eDestination
== Destination::LISTOVERRIDEENTRY
)
3262 // copy properties upwards so upper popState() inserts it
3263 m_aStates
.top().aTableAttributes
= aState
.aTableAttributes
;
3264 m_aStates
.top().aTableSprms
= aState
.aTableSprms
;
3268 auto pValue
= new RTFValue(aState
.aTableAttributes
, aState
.aTableSprms
);
3269 m_aListTableSprms
.set(NS_ooxml::LN_CT_Numbering_num
, pValue
,
3270 RTFOverwrite::NO_APPEND
);
3271 m_aListOverrideTable
[aState
.nCurrentListOverrideIndex
]
3272 = aState
.nCurrentListIndex
;
3276 case Destination::LEVELTEXT
:
3277 if (!m_aStates
.empty())
3279 auto pValue
= new RTFValue(aState
.aTableAttributes
);
3280 m_aStates
.top().aTableSprms
.set(NS_ooxml::LN_CT_Lvl_lvlText
, pValue
);
3283 case Destination::LEVELNUMBERS
:
3284 if (!m_aStates
.empty())
3286 m_aStates
.top().aTableSprms
= aState
.aTableSprms
;
3287 if (m_aStates
.top().eDestination
== Destination::LEVELNUMBERS
3288 || m_aStates
.top().eDestination
== Destination::LISTLEVEL
)
3289 // Parent state is level number or list level, current state is
3290 // level numbers: mark parent as invalid as well if necessary.
3291 m_aStates
.top().bLevelNumbersValid
= aState
.bLevelNumbersValid
;
3294 case Destination::FIELDINSTRUCTION
:
3295 if (!m_aStates
.empty())
3296 m_aStates
.top().eFieldStatus
= RTFFieldStatus::INSTRUCTION
;
3298 case Destination::FIELDRESULT
:
3299 if (!m_aStates
.empty())
3300 m_aStates
.top().eFieldStatus
= RTFFieldStatus::RESULT
;
3302 case Destination::FIELD
:
3303 if (aState
.eFieldStatus
== RTFFieldStatus::INSTRUCTION
)
3304 singleChar(cFieldEnd
);
3306 case Destination::SHAPEPROPERTYVALUEPICT
:
3307 if (!m_aStates
.empty())
3309 m_aStates
.top().aPicture
= aState
.aPicture
;
3310 // both \sp and \sv are destinations, copy the text up-ward for later
3311 m_aStates
.top().aDestinationText
= aState
.aDestinationText
;
3314 case Destination::FALT
:
3315 if (!m_aStates
.empty())
3316 m_aStates
.top().aTableSprms
= aState
.aTableSprms
;
3318 case Destination::SHAPEPROPERTYNAME
:
3319 case Destination::SHAPEPROPERTYVALUE
:
3320 case Destination::SHAPEPROPERTY
:
3321 if (!m_aStates
.empty())
3323 m_aStates
.top().aShape
= aState
.aShape
;
3324 m_aStates
.top().aPicture
= aState
.aPicture
;
3325 m_aStates
.top().aCharacterAttributes
= aState
.aCharacterAttributes
;
3328 case Destination::SHAPEINSTRUCTION
:
3329 if (!m_aStates
.empty() && m_aStates
.top().eDestination
== Destination::SHAPEINSTRUCTION
)
3331 // Shape instruction inside other shape instruction: just copy new shape settings:
3332 // it will be resolved on end of topmost shape instruction destination
3333 m_aStates
.top().aShape
= aState
.aShape
;
3334 m_aStates
.top().aPicture
= aState
.aPicture
;
3335 m_aStates
.top().aCharacterSprms
= aState
.aCharacterSprms
;
3336 m_aStates
.top().aCharacterAttributes
= aState
.aCharacterAttributes
;
3339 case Destination::FLYMAINCONTENT
:
3340 case Destination::SHPPICT
:
3341 case Destination::SHAPE
:
3342 if (!m_aStates
.empty())
3344 m_aStates
.top().aFrame
= aState
.aFrame
;
3345 if (aState
.eDestination
== Destination::SHPPICT
3346 && m_aStates
.top().eDestination
== Destination::LISTPICTURE
)
3348 RTFSprms aAttributes
;
3349 aAttributes
.set(NS_ooxml::LN_CT_NumPicBullet_numPicBulletId
,
3350 new RTFValue(m_nListPictureId
++));
3352 // Dummy value, real picture is already sent to dmapper.
3353 aSprms
.set(NS_ooxml::LN_CT_NumPicBullet_pict
, new RTFValue(0));
3354 auto pValue
= new RTFValue(aAttributes
, aSprms
);
3355 m_aListTableSprms
.set(NS_ooxml::LN_CT_Numbering_numPicBullet
, pValue
,
3356 RTFOverwrite::NO_APPEND
);
3360 case Destination::SHAPETEXT
:
3361 if (!m_aStates
.empty())
3363 // If we're leaving the shapetext group (it may have nested ones) and this is a shape, not an old drawingobject.
3364 if (m_aStates
.top().eDestination
!= Destination::SHAPETEXT
3365 && !m_aStates
.top().aDrawingObject
.bHadShapeText
)
3367 m_aStates
.top().bHadShapeText
= true;
3368 if (!m_aStates
.top().pCurrentBuffer
)
3369 m_pSdrImport
->close();
3371 m_aStates
.top().pCurrentBuffer
->push_back(
3372 Buf_t(BUFFER_ENDSHAPE
, nullptr, nullptr));
3375 // It's allowed to declare these inside the shape text, and they
3376 // are expected to have an effect for the whole shape.
3377 if (aState
.aDrawingObject
.nLeft
)
3378 m_aStates
.top().aDrawingObject
.nLeft
= aState
.aDrawingObject
.nLeft
;
3379 if (aState
.aDrawingObject
.nTop
)
3380 m_aStates
.top().aDrawingObject
.nTop
= aState
.aDrawingObject
.nTop
;
3381 if (aState
.aDrawingObject
.nRight
)
3382 m_aStates
.top().aDrawingObject
.nRight
= aState
.aDrawingObject
.nRight
;
3383 if (aState
.aDrawingObject
.nBottom
)
3384 m_aStates
.top().aDrawingObject
.nBottom
= aState
.aDrawingObject
.nBottom
;
3387 case Destination::PROPNAME
:
3388 if (m_aStates
.top().eDestination
== Destination::USERPROPS
)
3389 m_aStates
.top().aPropName
= aState
.aPropName
;
3393 if (!m_aStates
.empty() && m_aStates
.top().eDestination
== Destination::PICT
)
3394 m_aStates
.top().aPicture
= aState
.aPicture
;
3399 if (aState
.pCurrentBuffer
== &m_aSuperBuffer
)
3401 OSL_ASSERT(!m_aStates
.empty() && m_aStates
.top().pCurrentBuffer
== nullptr);
3403 if (!m_aSuperBuffer
.empty())
3404 replayBuffer(m_aSuperBuffer
, nullptr, nullptr);
3407 if (!m_aStates
.empty() && m_aStates
.top().nTableRowWidthAfter
> 0
3408 && aState
.nTableRowWidthAfter
== 0)
3409 // An RTF_ROW in the inner group already parsed nTableRowWidthAfter,
3410 // don't do it again in the outer state later.
3411 m_aStates
.top().nTableRowWidthAfter
= 0;
3413 if (m_nResetBreakOnSectBreak
!= RTF_invalid
&& !m_aStates
.empty())
3415 // Section break type created for \page still has an effect in the
3416 // outer state as well.
3417 RTFValue::Pointer_t pType
= aState
.aSectionSprms
.find(NS_ooxml::LN_EG_SectPrContents_type
);
3419 m_aStates
.top().aSectionSprms
.set(NS_ooxml::LN_EG_SectPrContents_type
, pType
);
3422 return RTFError::OK
;
3425 RTFError
RTFDocumentImpl::handleEmbeddedObject()
3427 OString aStr
= OUStringToOString(m_aStates
.top().pDestinationText
->makeStringAndClear(),
3428 RTL_TEXTENCODING_ASCII_US
);
3429 std::unique_ptr
<SvStream
> pStream(new SvMemoryStream());
3430 if (!msfilter::rtfutil::ExtractOLE2FromObjdata(aStr
, *pStream
))
3431 return RTFError::HEX_INVALID
;
3433 uno::Reference
<io::XInputStream
> xInputStream(
3434 new utl::OSeekableInputStreamWrapper(pStream
.release(), /*_bOwner=*/true));
3435 auto pStreamValue
= new RTFValue(xInputStream
);
3436 m_aOLEAttributes
.set(NS_ooxml::LN_inputstream
, pStreamValue
);
3438 return RTFError::OK
;
3441 bool RTFDocumentImpl::isInBackground() { return m_aStates
.top().bInBackground
; }
3443 RTFInternalState
RTFDocumentImpl::getInternalState() { return m_aStates
.top().nInternalState
; }
3445 void RTFDocumentImpl::setInternalState(RTFInternalState nInternalState
)
3447 m_aStates
.top().nInternalState
= nInternalState
;
3450 Destination
RTFDocumentImpl::getDestination() { return m_aStates
.top().eDestination
; }
3452 void RTFDocumentImpl::setDestination(Destination eDestination
)
3454 m_aStates
.top().eDestination
= eDestination
;
3457 // this is a questionably named method that is used only in a very special
3458 // situation where it looks like the "current" buffer is needed?
3459 void RTFDocumentImpl::setDestinationText(OUString
const& rString
)
3461 m_aStates
.top().aDestinationText
.setLength(0);
3462 m_aStates
.top().aDestinationText
.append(rString
);
3465 bool RTFDocumentImpl::getSkipUnknown() { return m_bSkipUnknown
; }
3467 void RTFDocumentImpl::setSkipUnknown(bool bSkipUnknown
) { m_bSkipUnknown
= bSkipUnknown
; }
3469 void RTFDocumentImpl::checkUnicode(bool bUnicode
, bool bHex
)
3471 if (bUnicode
&& !m_aUnicodeBuffer
.isEmpty())
3473 OUString aString
= m_aUnicodeBuffer
.makeStringAndClear();
3476 if (bHex
&& !m_aHexBuffer
.isEmpty())
3478 rtl_TextEncoding nEncoding
= m_aStates
.top().nCurrentEncoding
;
3479 if (m_aStates
.top().eDestination
== Destination::FONTENTRY
3480 && m_aStates
.top().nCurrentEncoding
== RTL_TEXTENCODING_SYMBOL
)
3481 nEncoding
= RTL_TEXTENCODING_MS_1252
;
3482 OUString aString
= OStringToOUString(m_aHexBuffer
.makeStringAndClear(), nEncoding
);
3487 RTFParserState::RTFParserState(RTFDocumentImpl
* pDocumentImpl
)
3488 : m_pDocumentImpl(pDocumentImpl
)
3489 , nInternalState(RTFInternalState::NORMAL
)
3490 , eDestination(Destination::NORMAL
)
3491 , eFieldStatus(RTFFieldStatus::NONE
)
3492 , nBorderState(RTFBorderState::NONE
)
3493 , nCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(0))
3498 , bLevelNumbersValid(true)
3500 , eRunType(RunType::LOCH
)
3501 , isRightToLeft(false)
3507 , pDestinationText(nullptr)
3508 , nCurrentStyleIndex(-1)
3509 , nCurrentCharacterStyleIndex(-1)
3510 , pCurrentBuffer(nullptr)
3511 , bInListpicture(false)
3512 , bInBackground(false)
3513 , bHadShapeText(false)
3514 , bInShapeGroup(false)
3516 , bCreatedShapeGroup(false)
3517 , bStartedTrackchange(false)
3518 , nTableRowWidthAfter(0)
3522 void RTFDocumentImpl::resetFrame() { m_aStates
.top().aFrame
= RTFFrame(&m_aStates
.top()); }
3524 void RTFDocumentImpl::bufferProperties(RTFBuffer_t
& rBuffer
, const RTFValue::Pointer_t
& pValue
,
3525 const tools::SvRef
<TableRowBuffer
>& pTableProperties
)
3527 rBuffer
.emplace_back(
3528 Buf_t(BUFFER_SETSTYLE
, new RTFValue(m_aStates
.top().nCurrentStyleIndex
), nullptr));
3529 rBuffer
.emplace_back(Buf_t(BUFFER_PROPS
, pValue
, pTableProperties
));
3532 RTFPicture::RTFPicture() = default;
3534 RTFShape::RTFShape() = default;
3536 RTFDrawingObject::RTFDrawingObject() = default;
3538 RTFFrame::RTFFrame(RTFParserState
* pParserState
)
3539 : m_pDocumentImpl(pParserState
->m_pDocumentImpl
)
3550 , m_nHRule(NS_ooxml::LN_Value_doc_ST_HeightRule_auto
)
3554 void RTFFrame::setSprm(Id nId
, Id nValue
)
3556 if (m_pDocumentImpl
->getFirstRun() && !m_pDocumentImpl
->isStyleSheetImport())
3558 m_pDocumentImpl
->checkFirstRun();
3559 m_pDocumentImpl
->setNeedPar(false);
3563 case NS_ooxml::LN_CT_FramePr_w
:
3566 case NS_ooxml::LN_CT_FramePr_h
:
3569 case NS_ooxml::LN_CT_FramePr_x
:
3572 case NS_ooxml::LN_CT_FramePr_y
:
3575 case NS_ooxml::LN_CT_FramePr_hSpace
:
3576 m_nHoriPadding
= nValue
;
3578 case NS_ooxml::LN_CT_FramePr_vSpace
:
3579 m_nVertPadding
= nValue
;
3581 case NS_ooxml::LN_CT_FramePr_xAlign
:
3582 m_nHoriAlign
= nValue
;
3584 case NS_ooxml::LN_CT_FramePr_hAnchor
:
3585 m_nHoriAnchor
= nValue
;
3587 case NS_ooxml::LN_CT_FramePr_yAlign
:
3588 m_nVertAlign
= nValue
;
3590 case NS_ooxml::LN_CT_FramePr_vAnchor
:
3591 m_nVertAnchor
= nValue
;
3593 case NS_ooxml::LN_CT_FramePr_wrap
:
3601 RTFSprms
RTFFrame::getSprms()
3605 static const Id pNames
[]
3606 = { NS_ooxml::LN_CT_FramePr_x
, NS_ooxml::LN_CT_FramePr_y
,
3607 NS_ooxml::LN_CT_FramePr_hRule
, // Make sure nHRule is processed before nH
3608 NS_ooxml::LN_CT_FramePr_h
, NS_ooxml::LN_CT_FramePr_w
,
3609 NS_ooxml::LN_CT_FramePr_hSpace
, NS_ooxml::LN_CT_FramePr_vSpace
,
3610 NS_ooxml::LN_CT_FramePr_hAnchor
, NS_ooxml::LN_CT_FramePr_vAnchor
,
3611 NS_ooxml::LN_CT_FramePr_xAlign
, NS_ooxml::LN_CT_FramePr_yAlign
,
3612 NS_ooxml::LN_CT_FramePr_wrap
, NS_ooxml::LN_CT_FramePr_dropCap
,
3613 NS_ooxml::LN_CT_FramePr_lines
};
3615 for (Id nId
: pNames
)
3617 RTFValue::Pointer_t pValue
;
3621 case NS_ooxml::LN_CT_FramePr_x
:
3623 pValue
= new RTFValue(m_nX
);
3625 case NS_ooxml::LN_CT_FramePr_y
:
3627 pValue
= new RTFValue(m_nY
);
3629 case NS_ooxml::LN_CT_FramePr_h
:
3632 if (m_nHRule
== NS_ooxml::LN_Value_doc_ST_HeightRule_exact
)
3633 pValue
= new RTFValue(-m_nH
); // The negative value just sets nHRule
3635 pValue
= new RTFValue(m_nH
);
3638 case NS_ooxml::LN_CT_FramePr_w
:
3640 pValue
= new RTFValue(m_nW
);
3642 case NS_ooxml::LN_CT_FramePr_hSpace
:
3643 if (m_nHoriPadding
!= 0)
3644 pValue
= new RTFValue(m_nHoriPadding
);
3646 case NS_ooxml::LN_CT_FramePr_vSpace
:
3647 if (m_nVertPadding
!= 0)
3648 pValue
= new RTFValue(m_nVertPadding
);
3650 case NS_ooxml::LN_CT_FramePr_hAnchor
:
3652 if (m_nHoriAnchor
== 0)
3653 m_nHoriAnchor
= NS_ooxml::LN_Value_doc_ST_HAnchor_margin
;
3654 pValue
= new RTFValue(m_nHoriAnchor
);
3657 case NS_ooxml::LN_CT_FramePr_vAnchor
:
3659 if (m_nVertAnchor
== 0)
3660 m_nVertAnchor
= NS_ooxml::LN_Value_doc_ST_VAnchor_margin
;
3661 pValue
= new RTFValue(m_nVertAnchor
);
3664 case NS_ooxml::LN_CT_FramePr_xAlign
:
3665 pValue
= new RTFValue(m_nHoriAlign
);
3667 case NS_ooxml::LN_CT_FramePr_yAlign
:
3668 pValue
= new RTFValue(m_nVertAlign
);
3670 case NS_ooxml::LN_CT_FramePr_hRule
:
3673 m_nHRule
= NS_ooxml::LN_Value_doc_ST_HeightRule_exact
;
3675 m_nHRule
= NS_ooxml::LN_Value_doc_ST_HeightRule_atLeast
;
3676 pValue
= new RTFValue(m_nHRule
);
3679 case NS_ooxml::LN_CT_FramePr_wrap
:
3681 pValue
= new RTFValue(*m_oWrap
);
3688 sprms
.set(nId
, pValue
);
3691 RTFSprms frameprSprms
;
3692 frameprSprms
.set(NS_ooxml::LN_CT_PPrBase_framePr
, new RTFValue(sprms
));
3693 return frameprSprms
;
3696 bool RTFFrame::hasProperties()
3698 return m_nX
!= 0 || m_nY
!= 0 || m_nW
!= 0 || m_nH
!= 0 || m_nHoriPadding
!= 0
3699 || m_nVertPadding
!= 0 || m_nHoriAlign
!= 0 || m_nHoriAnchor
!= 0 || m_nVertAlign
!= 0
3700 || m_nVertAnchor
!= 0;
3703 } // namespace rtftok
3704 } // namespace writerfilter
3706 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */