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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
21 #include <com/sun/star/beans/XPropertySet.hpp>
22 #include <com/sun/star/document/XDocumentProperties.hpp>
23 #include <com/sun/star/xml/sax/SAXException.hpp>
24 #include <ooxml/resourceids.hxx>
25 #include "DomainMapper_Impl.hxx"
26 #include "ConversionHelper.hxx"
27 #include "SdtHelper.hxx"
28 #include "DomainMapperTableHandler.hxx"
29 #include "TagLogger.hxx"
30 #include <com/sun/star/uno/XComponentContext.hpp>
31 #include <com/sun/star/graphic/XGraphic.hpp>
32 #include <com/sun/star/beans/XPropertyState.hpp>
33 #include <com/sun/star/container/XNamed.hpp>
34 #include <com/sun/star/document/PrinterIndependentLayout.hpp>
35 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
36 #include <com/sun/star/embed/XEmbeddedObject.hpp>
37 #include <com/sun/star/i18n/NumberFormatMapper.hpp>
38 #include <com/sun/star/i18n/NumberFormatIndex.hpp>
39 #include <com/sun/star/lang/XServiceInfo.hpp>
40 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
41 #include <com/sun/star/style/LineNumberPosition.hpp>
42 #include <com/sun/star/style/LineSpacing.hpp>
43 #include <com/sun/star/style/LineSpacingMode.hpp>
44 #include <com/sun/star/text/ChapterFormat.hpp>
45 #include <com/sun/star/text/FilenameDisplayFormat.hpp>
46 #include <com/sun/star/text/SetVariableType.hpp>
47 #include <com/sun/star/text/XDocumentIndex.hpp>
48 #include <com/sun/star/text/XDocumentIndexesSupplier.hpp>
49 #include <com/sun/star/text/XFootnote.hpp>
50 #include <com/sun/star/text/XEndnotesSupplier.hpp>
51 #include <com/sun/star/text/XFootnotesSupplier.hpp>
52 #include <com/sun/star/text/XLineNumberingProperties.hpp>
53 #include <com/sun/star/style/XStyle.hpp>
54 #include <com/sun/star/text/LabelFollow.hpp>
55 #include <com/sun/star/text/PageNumberType.hpp>
56 #include <com/sun/star/text/HoriOrientation.hpp>
57 #include <com/sun/star/text/VertOrientation.hpp>
58 #include <com/sun/star/text/ReferenceFieldPart.hpp>
59 #include <com/sun/star/text/RelOrientation.hpp>
60 #include <com/sun/star/text/ReferenceFieldSource.hpp>
61 #include <com/sun/star/text/SizeType.hpp>
62 #include <com/sun/star/text/TextContentAnchorType.hpp>
63 #include <com/sun/star/text/WrapTextMode.hpp>
64 #include <com/sun/star/text/XChapterNumberingSupplier.hpp>
65 #include <com/sun/star/text/XDependentTextField.hpp>
66 #include <com/sun/star/text/XParagraphCursor.hpp>
67 #include <com/sun/star/text/XRedline.hpp>
68 #include <com/sun/star/text/XTextFieldsSupplier.hpp>
69 #include <com/sun/star/text/XTextFrame.hpp>
70 #include <com/sun/star/text/RubyPosition.hpp>
71 #include <com/sun/star/text/XTextRangeCompare.hpp>
72 #include <com/sun/star/style/DropCapFormat.hpp>
73 #include <com/sun/star/util/NumberFormatter.hpp>
74 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
75 #include <com/sun/star/util/XNumberFormatter.hpp>
76 #include <com/sun/star/document/XViewDataSupplier.hpp>
77 #include <com/sun/star/container/XIndexContainer.hpp>
78 #include <com/sun/star/text/ControlCharacter.hpp>
79 #include <com/sun/star/text/XTextColumns.hpp>
80 #include <com/sun/star/awt/CharSet.hpp>
81 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
82 #include <com/sun/star/embed/XHierarchicalStorageAccess.hpp>
83 #include <com/sun/star/embed/ElementModes.hpp>
84 #include <com/sun/star/document/XImporter.hpp>
85 #include <com/sun/star/document/XFilter.hpp>
86 #include <comphelper/indexedpropertyvalues.hxx>
87 #include <editeng/flditem.hxx>
88 #include <editeng/unotext.hxx>
89 #include <o3tl/deleter.hxx>
90 #include <o3tl/safeint.hxx>
91 #include <o3tl/temporary.hxx>
92 #include <oox/mathml/imexport.hxx>
94 #include <xmloff/odffields.hxx>
95 #include <rtl/uri.hxx>
96 #include <unotools/ucbstreamhelper.hxx>
97 #include <unotools/streamwrap.hxx>
98 #include <comphelper/scopeguard.hxx>
99 #include <comphelper/string.hxx>
101 #include <dmapper/GraphicZOrderHelper.hxx>
103 #include <oox/token/tokens.hxx>
109 #include <unordered_map>
113 #include <officecfg/Office/Common.hxx>
114 #include <filter/msfilter/util.hxx>
115 #include <filter/msfilter/ww8fields.hxx>
116 #include <comphelper/sequence.hxx>
117 #include <comphelper/propertyvalue.hxx>
118 #include <comphelper/propertysequence.hxx>
119 #include <unotools/configmgr.hxx>
120 #include <unotools/mediadescriptor.hxx>
121 #include <comphelper/diagnose_ex.hxx>
122 #include <sal/log.hxx>
123 #include <o3tl/string_view.hxx>
124 #include <com/sun/star/drawing/FillStyle.hpp>
126 #include <unicode/errorcode.h>
127 #include <unicode/regex.h>
129 using namespace ::com::sun::star
;
131 namespace writerfilter::dmapper
{
133 //line numbering for header/footer
134 static void lcl_linenumberingHeaderFooter( const uno::Reference
<container::XNameContainer
>& xStyles
, const OUString
& rname
, DomainMapper_Impl
* dmapper
)
136 const StyleSheetEntryPtr pEntry
= dmapper
->GetStyleSheetTable()->FindStyleSheetByISTD( rname
);
139 const StyleSheetPropertyMap
* pStyleSheetProperties
= pEntry
->m_pProperties
.get();
140 if ( !pStyleSheetProperties
)
142 sal_Int32 nListId
= pStyleSheetProperties
->props().GetListId();
145 if( xStyles
->hasByName( rname
) )
147 uno::Reference
< style::XStyle
> xStyle
;
148 xStyles
->getByName( rname
) >>= xStyle
;
151 uno::Reference
<beans::XPropertySet
> xPropertySet( xStyle
, uno::UNO_QUERY
);
152 xPropertySet
->setPropertyValue( getPropertyName( PROP_PARA_LINE_NUMBER_COUNT
), uno::Any( nListId
>= 0 ) );
157 // Populate Dropdown Field properties from FFData structure
158 static void lcl_handleDropdownField( const uno::Reference
< beans::XPropertySet
>& rxFieldProps
, const FFDataHandler::Pointer_t
& pFFDataHandler
)
160 if ( !rxFieldProps
.is() )
163 if ( !pFFDataHandler
->getName().isEmpty() )
164 rxFieldProps
->setPropertyValue( "Name", uno::Any( pFFDataHandler
->getName() ) );
166 const FFDataHandler::DropDownEntries_t
& rEntries
= pFFDataHandler
->getDropDownEntries();
167 uno::Sequence
< OUString
> sItems( rEntries
.size() );
168 ::std::copy( rEntries
.begin(), rEntries
.end(), sItems
.getArray());
169 if ( sItems
.hasElements() )
170 rxFieldProps
->setPropertyValue( "Items", uno::Any( sItems
) );
172 sal_Int32 nResult
= pFFDataHandler
->getDropDownResult().toInt32();
173 if (nResult
> 0 && o3tl::make_unsigned(nResult
) < sItems
.size())
174 rxFieldProps
->setPropertyValue( "SelectedItem", uno::Any( std::as_const(sItems
)[ nResult
] ) );
175 if ( !pFFDataHandler
->getHelpText().isEmpty() )
176 rxFieldProps
->setPropertyValue( "Help", uno::Any( pFFDataHandler
->getHelpText() ) );
179 static void lcl_handleTextField( const uno::Reference
< beans::XPropertySet
>& rxFieldProps
, const FFDataHandler::Pointer_t
& pFFDataHandler
)
181 if ( rxFieldProps
.is() && pFFDataHandler
)
183 rxFieldProps
->setPropertyValue
184 (getPropertyName(PROP_HINT
),
185 uno::Any(pFFDataHandler
->getStatusText()));
186 rxFieldProps
->setPropertyValue
187 (getPropertyName(PROP_HELP
),
188 uno::Any(pFFDataHandler
->getHelpText()));
189 rxFieldProps
->setPropertyValue
190 (getPropertyName(PROP_CONTENT
),
191 uno::Any(pFFDataHandler
->getTextDefault()));
196 Very similar to DomainMapper_Impl::GetPropertyFromStyleSheet
197 It is focused on paragraph properties search in current & parent stylesheet entries.
198 But it will not take into account properties with listid: these are "list paragraph styles" and
199 not used in some cases.
201 static uno::Any
lcl_GetPropertyFromParaStyleSheetNoNum(PropertyIds eId
, StyleSheetEntryPtr pEntry
, const StyleSheetTablePtr
& rStyleSheet
)
205 if (pEntry
->m_pProperties
)
207 std::optional
<PropertyMap::Property
> aProperty
=
208 pEntry
->m_pProperties
->getProperty(eId
);
211 if (pEntry
->m_pProperties
->props().GetListId())
212 // It is a paragraph style with list. Paragraph list styles are not taken into account
215 return aProperty
->second
;
218 //search until the property is set or no parent is available
219 StyleSheetEntryPtr pNewEntry
;
220 if (!pEntry
->m_sBaseStyleIdentifier
.isEmpty())
221 pNewEntry
= rStyleSheet
->FindStyleSheetByISTD(pEntry
->m_sBaseStyleIdentifier
);
223 SAL_WARN_IF(pEntry
== pNewEntry
, "writerfilter.dmapper", "circular loop in style hierarchy?");
225 if (pEntry
== pNewEntry
) //fdo#49587
236 struct FieldConversion
238 const char* cFieldServiceName
;
244 typedef std::unordered_map
<OUString
, FieldConversion
> FieldConversionMap_t
;
246 /// Gives access to the parent field context of the topmost one, if there is any.
247 static FieldContextPtr
GetParentFieldContext(const std::deque
<FieldContextPtr
>& rFieldStack
)
249 if (rFieldStack
.size() < 2)
254 return rFieldStack
[rFieldStack
.size() - 2];
257 /// Decides if the pInner field inside pOuter is allowed in Writer core, depending on their type.
258 static bool IsFieldNestingAllowed(const FieldContextPtr
& pOuter
, const FieldContextPtr
& pInner
)
260 if (!pInner
->GetFieldId())
265 std::optional
<FieldId
> oOuterFieldId
= pOuter
->GetFieldId();
268 OUString aCommand
= pOuter
->GetCommand();
270 // Ignore leading space before the field name, but don't accept IFF when we check for IF.
271 while (aCommand
.getLength() > 3 && aCommand
[0] == ' ')
272 aCommand
= aCommand
.subView(1);
274 if (aCommand
.startsWith("IF "))
276 // This will be FIELD_IF once the command is closed.
277 oOuterFieldId
= FIELD_IF
;
286 switch (*oOuterFieldId
)
290 switch (*pInner
->GetFieldId())
292 case FIELD_DOCVARIABLE
:
296 case FIELD_MERGEFIELD
:
301 // LO does not currently know how to evaluate these as conditions or results
316 uno::Any
FloatingTableInfo::getPropertyValue(std::u16string_view propertyName
)
318 for( beans::PropertyValue
const & propVal
: std::as_const(m_aFrameProperties
) )
319 if( propVal
.Name
== propertyName
)
320 return propVal
.Value
;
324 DomainMapper_Impl::DomainMapper_Impl(
325 DomainMapper
& rDMapper
,
326 uno::Reference
<uno::XComponentContext
> xContext
,
327 uno::Reference
<lang::XComponent
> const& xModel
,
328 SourceDocumentType eDocumentType
,
329 utl::MediaDescriptor
const & rMediaDesc
) :
330 m_eDocumentType( eDocumentType
),
331 m_rDMapper( rDMapper
),
332 m_pOOXMLDocument(nullptr),
333 m_xTextDocument( xModel
, uno::UNO_QUERY
),
334 m_xTextFactory( xModel
, uno::UNO_QUERY
),
335 m_xComponentContext(std::move( xContext
)),
336 m_bForceGenericFields(!utl::ConfigManager::IsFuzzing() && officecfg::Office::Common::Filter::Microsoft::Import::ForceImportWWFieldsAsGenericFields::get()),
337 m_bIsDecimalComma( false ),
338 m_bSetUserFieldContent( false ),
339 m_bSetCitation( false ),
340 m_bSetDateValue( false ),
341 m_bIsFirstSection( true ),
342 m_bIsColumnBreakDeferred( false ),
343 m_bIsPageBreakDeferred( false ),
344 m_nLineBreaksDeferred( 0 ),
345 m_bSdtEndDeferred(false),
346 m_bParaSdtEndDeferred(false),
348 m_bStartTOCHeaderFooter(false),
349 m_bStartedTOC(false),
350 m_bStartIndex(false),
351 m_bStartBibliography(false),
352 m_nStartGenericField(0),
353 m_bTextInserted(false),
354 m_bTextDeleted(false),
356 m_bFrameDirectionSet(false),
357 m_bInDocDefaultsImport(false),
358 m_bInStyleSheetImport( false ),
359 m_bInNumberingImport(false),
360 m_bInAnyTableImport( false ),
361 m_eInHeaderFooterImport( HeaderFooterImportState::none
),
362 m_bDiscardHeaderFooter( false ),
363 m_bInFootOrEndnote(false),
364 m_bInFootnote(false),
365 m_bHasFootnoteStyle(false),
366 m_bCheckFootnoteStyle(false),
367 m_eSkipFootnoteState(SkipFootnoteSeparator::OFF
),
370 m_nFirstFootnoteIndex(-1),
371 m_nFirstEndnoteIndex(-1),
372 m_bLineNumberingSet( false ),
373 m_bIsInFootnoteProperties( false ),
374 m_bIsParaMarkerChange( false ),
375 m_bIsParaMarkerMove( false ),
376 m_bRedlineImageInPreviousRun( false ),
377 m_bParaChanged( false ),
378 m_bIsFirstParaInSection( true ),
379 m_bIsFirstParaInSectionAfterRedline( true ),
380 m_bDummyParaAddedForTableInSection( false ),
381 m_bDummyParaAddedForTableInSectionPage( false ),
382 m_bTextFrameInserted(false),
383 m_bIsPreviousParagraphFramed( false ),
384 m_bIsLastParaInSection( false ),
385 m_bIsLastSectionGroup( false ),
386 m_bIsInComments( false ),
387 m_bParaSectpr( false ),
388 m_bUsingEnhancedFields( false ),
390 m_bIsFirstRun(false),
391 m_bIsOutsideAParagraph(true),
392 m_nAnnotationId( -1 ),
393 m_aSmartTagHandler(m_xComponentContext
, m_xTextDocument
),
394 m_xInsertTextRange(rMediaDesc
.getUnpackedValueOrDefault("TextInsertModeRange", uno::Reference
<text::XTextRange
>())),
395 m_xAltChunkStartingRange(rMediaDesc
.getUnpackedValueOrDefault("AltChunkStartingRange", uno::Reference
<text::XTextRange
>())),
396 m_bIsInTextBox(false),
397 m_bIsNewDoc(!rMediaDesc
.getUnpackedValueOrDefault("InsertMode", false)),
398 m_bIsAltChunk(rMediaDesc
.getUnpackedValueOrDefault("AltChunkMode", false)),
399 m_bIsReadGlossaries(rMediaDesc
.getUnpackedValueOrDefault("ReadGlossaries", false)),
401 m_nTableCellDepth(0),
402 m_nLastTableCellParagraphDepth(0),
405 m_bCheckFirstFootnoteTab(false),
406 m_bIgnoreNextTab(false),
407 m_bIsSplitPara(false),
408 m_bIsActualParagraphFramed( false ),
409 m_bParaHadField(false),
410 m_bSaveParaHadField(false),
411 m_bParaAutoBefore(false),
412 m_bFirstParagraphInCell(true),
413 m_bSaveFirstParagraphInCell(false),
414 m_bParaWithInlineObject(false),
417 m_aBaseUrl
= rMediaDesc
.getUnpackedValueOrDefault(
418 utl::MediaDescriptor::PROP_DOCUMENTBASEURL
, OUString());
419 if (m_aBaseUrl
.isEmpty()) {
420 m_aBaseUrl
= rMediaDesc
.getUnpackedValueOrDefault(
421 utl::MediaDescriptor::PROP_URL
, OUString());
424 appendTableManager( );
426 if (!m_bIsNewDoc
&& !m_xBodyText
)
428 throw uno::Exception("failed to find body text of the insert position", nullptr);
431 uno::Reference
< text::XTextAppend
> xBodyTextAppend( m_xBodyText
, uno::UNO_QUERY
);
432 m_aTextAppendStack
.push(TextAppendContext(xBodyTextAppend
,
433 m_bIsNewDoc
? uno::Reference
<text::XTextCursor
>() : m_xBodyText
->createTextCursorByRange(m_xInsertTextRange
)));
435 //todo: does it makes sense to set the body text as static text interface?
436 uno::Reference
< text::XTextAppendAndConvert
> xBodyTextAppendAndConvert( m_xBodyText
, uno::UNO_QUERY
);
437 m_pTableHandler
= new DomainMapperTableHandler(xBodyTextAppendAndConvert
, *this);
438 getTableManager( ).setHandler(m_pTableHandler
);
440 getTableManager( ).startLevel();
441 m_bUsingEnhancedFields
= !utl::ConfigManager::IsFuzzing() && officecfg::Office::Common::Filter::Microsoft::Import::ImportWWFieldsAsEnhancedFields::get();
443 m_pSdtHelper
= new SdtHelper(*this, m_xComponentContext
);
445 m_aRedlines
.push(std::vector
<RedlineParamsPtr
>());
449 m_bIsFirstSection
= false;
454 DomainMapper_Impl::~DomainMapper_Impl()
457 // Don't remove last paragraph when pasting, sw expects that empty paragraph.
460 RemoveLastParagraph();
461 suppress_fun_call_w_exception(GetStyleSheetTable()->ApplyClonedTOCStyles());
463 if (hasTableManager())
465 getTableManager().endLevel();
470 writerfilter::ooxml::OOXMLDocument
* DomainMapper_Impl::getDocumentReference() const
472 return m_pOOXMLDocument
;
475 uno::Reference
< container::XNameContainer
> const & DomainMapper_Impl::GetPageStyles()
477 if(!m_xPageStyles1
.is())
479 uno::Reference
< style::XStyleFamiliesSupplier
> xSupplier( m_xTextDocument
, uno::UNO_QUERY
);
481 xSupplier
->getStyleFamilies()->getByName("PageStyles") >>= m_xPageStyles1
;
483 return m_xPageStyles1
;
486 OUString
DomainMapper_Impl::GetUnusedPageStyleName()
488 static const char DEFAULT_STYLE
[] = "Converted";
489 if (!m_xNextUnusedPageStyleNo
)
491 const uno::Sequence
< OUString
> aPageStyleNames
= GetPageStyles()->getElementNames();
492 sal_Int32 nMaxIndex
= 0;
493 // find the highest number x in each style with the name "DEFAULT_STYLE+x" and
494 // return an incremented name
496 for ( const auto& rStyleName
: aPageStyleNames
)
498 if ( rStyleName
.startsWith( DEFAULT_STYLE
) )
500 sal_Int32 nIndex
= o3tl::toInt32(rStyleName
.subView( strlen( DEFAULT_STYLE
) ));
501 if ( nIndex
> nMaxIndex
)
505 m_xNextUnusedPageStyleNo
= nMaxIndex
+ 1;
508 OUString sPageStyleName
= DEFAULT_STYLE
+ OUString::number( *m_xNextUnusedPageStyleNo
);
509 *m_xNextUnusedPageStyleNo
= *m_xNextUnusedPageStyleNo
+ 1;
510 return sPageStyleName
;
513 uno::Reference
< container::XNameContainer
> const & DomainMapper_Impl::GetCharacterStyles()
515 if(!m_xCharacterStyles
.is())
517 uno::Reference
< style::XStyleFamiliesSupplier
> xSupplier( m_xTextDocument
, uno::UNO_QUERY
);
519 xSupplier
->getStyleFamilies()->getByName("CharacterStyles") >>= m_xCharacterStyles
;
521 return m_xCharacterStyles
;
524 OUString
DomainMapper_Impl::GetUnusedCharacterStyleName()
526 static const char cListLabel
[] = "ListLabel ";
527 if (!m_xNextUnusedCharacterStyleNo
)
529 //search for all character styles with the name sListLabel + <index>
530 const uno::Sequence
< OUString
> aCharacterStyleNames
= GetCharacterStyles()->getElementNames();
531 sal_Int32 nMaxIndex
= 0;
532 for ( const auto& rStyleName
: aCharacterStyleNames
)
535 if ( rStyleName
.startsWith( cListLabel
, &sSuffix
) )
537 sal_Int32 nSuffix
= sSuffix
.toInt32();
538 if( nSuffix
> 0 && nSuffix
> nMaxIndex
)
542 m_xNextUnusedCharacterStyleNo
= nMaxIndex
+ 1;
545 OUString sPageStyleName
= cListLabel
+ OUString::number( *m_xNextUnusedCharacterStyleNo
);
546 *m_xNextUnusedCharacterStyleNo
= *m_xNextUnusedCharacterStyleNo
+ 1;
547 return sPageStyleName
;
550 uno::Reference
< text::XText
> const & DomainMapper_Impl::GetBodyText()
552 if(!m_xBodyText
.is())
554 if (m_xInsertTextRange
.is())
555 m_xBodyText
= m_xInsertTextRange
->getText();
556 else if (m_xTextDocument
.is())
557 m_xBodyText
= m_xTextDocument
->getText();
563 uno::Reference
< beans::XPropertySet
> const & DomainMapper_Impl::GetDocumentSettings()
565 if( !m_xDocumentSettings
.is() && m_xTextFactory
.is())
567 m_xDocumentSettings
.set( m_xTextFactory
->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY
);
569 return m_xDocumentSettings
;
573 void DomainMapper_Impl::SetDocumentSettingsProperty( const OUString
& rPropName
, const uno::Any
& rValue
)
575 uno::Reference
< beans::XPropertySet
> xSettings
= GetDocumentSettings();
580 xSettings
->setPropertyValue( rPropName
, rValue
);
582 catch( const uno::Exception
& )
590 void CopyPageDescNameToNextParagraph(const uno::Reference
<lang::XComponent
>& xParagraph
,
591 const uno::Reference
<text::XTextCursor
>& xCursor
)
593 // First check if xParagraph has a non-empty page style name to copy from.
594 uno::Reference
<beans::XPropertySet
> xParagraphProps(xParagraph
, uno::UNO_QUERY
);
595 if (!xParagraphProps
.is())
600 uno::Any aPageDescName
= xParagraphProps
->getPropertyValue("PageDescName");
601 OUString sPageDescName
;
602 aPageDescName
>>= sPageDescName
;
603 if (sPageDescName
.isEmpty())
608 // If so, search for the next paragraph.
609 uno::Reference
<text::XParagraphCursor
> xParaCursor(xCursor
, uno::UNO_QUERY
);
610 if (!xParaCursor
.is())
615 // Create a range till the next paragraph and then enumerate on the range.
616 if (!xParaCursor
->gotoNextParagraph(/*bExpand=*/true))
621 uno::Reference
<container::XEnumerationAccess
> xEnumerationAccess(xParaCursor
, uno::UNO_QUERY
);
622 if (!xEnumerationAccess
.is())
627 uno::Reference
<container::XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
628 if (!xEnumeration
.is())
633 xEnumeration
->nextElement();
634 if (!xEnumeration
->hasMoreElements())
639 // We found a next item in the enumeration: it's usually a paragraph, but may be a table as
641 uno::Reference
<beans::XPropertySet
> xNextParagraph(xEnumeration
->nextElement(), uno::UNO_QUERY
);
642 if (!xNextParagraph
.is())
647 // See if there is page style set already: if so, don't touch it.
648 OUString sNextPageDescName
;
649 xNextParagraph
->getPropertyValue("PageDescName") >>= sNextPageDescName
;
650 if (!sNextPageDescName
.isEmpty())
655 // Finally copy it over, so it's not lost.
656 xNextParagraph
->setPropertyValue("PageDescName", aPageDescName
);
660 void DomainMapper_Impl::RemoveDummyParaForTableInSection()
662 SetIsDummyParaAddedForTableInSection(false);
663 PropertyMapPtr pContext
= GetTopContextOfType(CONTEXT_SECTION
);
664 SectionPropertyMap
* pSectionContext
= dynamic_cast< SectionPropertyMap
* >( pContext
.get() );
665 if (!pSectionContext
)
668 if (m_aTextAppendStack
.empty())
670 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
671 if (!xTextAppend
.is())
674 uno::Reference
< text::XTextCursor
> xCursor
= xTextAppend
->createTextCursorByRange(pSectionContext
->GetStartingRange());
676 // Remove the extra NumPicBullets from the document,
677 // which get attached to the first paragraph in the
679 ListsManager::Pointer pListTable
= GetListTable();
680 pListTable
->DisposeNumPicBullets();
682 uno::Reference
<container::XEnumerationAccess
> xEnumerationAccess(xCursor
, uno::UNO_QUERY
);
683 if (xEnumerationAccess
.is() && m_aTextAppendStack
.size() == 1 )
685 uno::Reference
<container::XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
686 uno::Reference
<lang::XComponent
> xParagraph(xEnumeration
->nextElement(), uno::UNO_QUERY
);
687 // Make sure no page breaks are lost.
688 CopyPageDescNameToNextParagraph(xParagraph
, xCursor
);
689 xParagraph
->dispose();
692 void DomainMapper_Impl::AddDummyParaForTableInSection()
694 // Shapes and textboxes can't have sections.
695 if (IsInShape() || m_bIsInTextBox
)
698 if (!m_aTextAppendStack
.empty())
700 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
701 if (xTextAppend
.is())
703 xTextAppend
->finishParagraph( uno::Sequence
< beans::PropertyValue
>() );
704 SetIsDummyParaAddedForTableInSection(true);
709 static OUString
lcl_FindLastBookmark(const uno::Reference
<text::XTextCursor
>& xCursor
,
710 bool bAlreadyExpanded
)
716 // Select 1 previous element
717 if (!bAlreadyExpanded
)
718 xCursor
->goLeft(1, true);
719 comphelper::ScopeGuard
unselectGuard(
720 [xCursor
, bAlreadyExpanded
]()
722 if (!bAlreadyExpanded
)
723 xCursor
->goRight(1, true);
726 uno::Reference
<container::XEnumerationAccess
> xParaEnumAccess(xCursor
, uno::UNO_QUERY
);
727 if (!xParaEnumAccess
.is())
730 // Iterate through selection paragraphs
731 uno::Reference
<container::XEnumeration
> xParaEnum
= xParaEnumAccess
->createEnumeration();
732 if (!xParaEnum
->hasMoreElements())
735 // Iterate through first para portions
736 uno::Reference
<container::XEnumerationAccess
> xRunEnumAccess(xParaEnum
->nextElement(),
737 uno::UNO_QUERY_THROW
);
738 uno::Reference
<container::XEnumeration
> xRunEnum
= xRunEnumAccess
->createEnumeration();
739 while (xRunEnum
->hasMoreElements())
741 uno::Reference
<beans::XPropertySet
> xProps(xRunEnum
->nextElement(), uno::UNO_QUERY_THROW
);
742 uno::Any
aType(xProps
->getPropertyValue("TextPortionType"));
745 if (sType
== "Bookmark")
747 uno::Reference
<container::XNamed
> xBookmark(xProps
->getPropertyValue("Bookmark"),
748 uno::UNO_QUERY_THROW
);
749 sName
= xBookmark
->getName();
750 // Do not stop the scan here. Maybe there are 2 bookmarks?
757 void DomainMapper_Impl::RemoveLastParagraph( )
759 if (m_bDiscardHeaderFooter
)
762 if (m_aTextAppendStack
.empty())
764 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
765 if (!xTextAppend
.is())
768 if (hasTableManager() && getTableManager().getCurrentTablePosition().getLength() != 0)
770 // If we have an open floating table, then don't remove this paragraph, since that'll be the
771 // anchor of the floating table. Otherwise we would lose the table.
777 uno::Reference
< text::XTextCursor
> xCursor
;
780 xCursor
= xTextAppend
->createTextCursor();
781 xCursor
->gotoEnd(false);
784 xCursor
.set(m_aTextAppendStack
.top().xCursor
, uno::UNO_SET_THROW
);
786 // Keep the character properties of the last but one paragraph, even if
787 // it's empty. This works for headers/footers, and maybe in other cases
788 // as well, but surely not in textboxes.
789 // fdo#58327: also do this at the end of the document: when pasting,
790 // a table before the cursor position would be deleted
791 bool const bEndOfDocument(m_aTextAppendStack
.size() == 1);
793 uno::Reference
<lang::XComponent
> xParagraph
;
794 if (IsInHeaderFooter() || bEndOfDocument
)
796 if (uno::Reference
<container::XEnumerationAccess
> xEA
{ xCursor
, uno::UNO_QUERY
})
798 xParagraph
.set(xEA
->createEnumeration()->nextElement(), uno::UNO_QUERY
);
802 xCursor
->goLeft(1, true);
803 // If this is a text on a shape, possibly the text has the trailing
804 // newline removed already. RTF may also not have the trailing newline.
805 if (!(xCursor
->getString() == SAL_NEWLINE_STRING
||
806 // tdf#105444 comments need an exception, if SAL_NEWLINE_STRING defined as "\r\n"
807 (sizeof(SAL_NEWLINE_STRING
) - 1 == 2 && xCursor
->getString() == "\n")))
810 uno::Reference
<beans::XPropertySet
> xDocProps(GetTextDocument(), uno::UNO_QUERY_THROW
);
811 static constexpr OUStringLiteral
RecordChanges(u
"RecordChanges");
813 comphelper::ScopeGuard
redlineRestore(
814 [xDocProps
, aPreviousValue
= xDocProps
->getPropertyValue(RecordChanges
)]()
815 { xDocProps
->setPropertyValue(RecordChanges
, aPreviousValue
); });
817 // disable redlining, otherwise we might end up with an unwanted recorded operations
818 xDocProps
->setPropertyValue(RecordChanges
, uno::Any(false));
822 // move all anchored objects to the previous paragraph
823 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(GetTextDocument(),
824 uno::UNO_QUERY_THROW
);
825 auto xDrawPage
= xDrawPageSupplier
->getDrawPage();
826 if (xDrawPage
&& xDrawPage
->hasElements())
828 // Cursor already spans two paragraphs
829 uno::Reference
<container::XEnumerationAccess
> xEA(xCursor
,
830 uno::UNO_QUERY_THROW
);
831 auto xEnumeration
= xEA
->createEnumeration();
832 uno::Reference
<text::XTextRange
> xPrevParagraph(xEnumeration
->nextElement(),
833 uno::UNO_QUERY_THROW
);
835 uno::Reference
<text::XTextRange
> xParaRange(xParagraph
, uno::UNO_QUERY_THROW
);
836 uno::Reference
<text::XTextRangeCompare
> xRegionCompare(xParaRange
->getText(),
837 uno::UNO_QUERY_THROW
);
838 const sal_Int32 count
= xDrawPage
->getCount();
839 for (sal_Int32 i
= 0; i
< count
; ++i
)
843 uno::Reference
<text::XTextContent
> xShape(xDrawPage
->getByIndex(i
),
844 uno::UNO_QUERY_THROW
);
845 uno::Reference
<text::XTextRange
> xAnchor(xShape
->getAnchor(),
847 if (xRegionCompare
->compareRegionStarts(xAnchor
, xParaRange
) <= 0
848 && xRegionCompare
->compareRegionEnds(xAnchor
, xParaRange
) >= 0)
850 xShape
->attach(xPrevParagraph
);
853 catch (const uno::Exception
&)
855 // Can happen e.g. in compareRegion*, when the shape is in a header,
856 // and paragraph in body
861 xParagraph
->dispose();
865 // Try to find and remember last bookmark in document: it potentially
866 // can be deleted by xCursor->setString() but not by xParagraph->dispose()
867 OUString sLastBookmarkName
;
869 sLastBookmarkName
= lcl_FindLastBookmark(xCursor
, true);
871 // The cursor already selects across the paragraph break
873 xCursor
->setString(OUString());
875 // call to xCursor->setString possibly did remove final bookmark
876 // from previous paragraph. We need to restore it, if there was any.
877 if (sLastBookmarkName
.getLength())
879 OUString sBookmarkNameAfterRemoval
= lcl_FindLastBookmark(xCursor
, false);
880 if (sBookmarkNameAfterRemoval
.isEmpty())
882 // Yes, it was removed. Restore
883 uno::Reference
<text::XTextContent
> xBookmark(
884 m_xTextFactory
->createInstance("com.sun.star.text.Bookmark"),
885 uno::UNO_QUERY_THROW
);
887 uno::Reference
<container::XNamed
> xBkmNamed(xBookmark
,
888 uno::UNO_QUERY_THROW
);
889 xBkmNamed
->setName(sLastBookmarkName
);
890 xTextAppend
->insertTextContent(xCursor
, xBookmark
, !xCursor
->isCollapsed());
895 catch( const uno::Exception
& )
901 void DomainMapper_Impl::SetIsLastSectionGroup( bool bIsLast
)
903 m_bIsLastSectionGroup
= bIsLast
;
906 void DomainMapper_Impl::SetIsLastParagraphInSection( bool bIsLast
)
908 m_bIsLastParaInSection
= bIsLast
;
912 void DomainMapper_Impl::SetIsFirstParagraphInSection( bool bIsFirst
)
914 m_bIsFirstParaInSection
= bIsFirst
;
917 void DomainMapper_Impl::SetIsFirstParagraphInSectionAfterRedline( bool bIsFirstAfterRedline
)
919 m_bIsFirstParaInSectionAfterRedline
= bIsFirstAfterRedline
;
922 bool DomainMapper_Impl::GetIsFirstParagraphInSection( bool bAfterRedline
) const
924 // Anchored objects may include multiple paragraphs,
925 // and none of them should be considered the first para in section.
926 return ( bAfterRedline
? m_bIsFirstParaInSectionAfterRedline
: m_bIsFirstParaInSection
)
929 && !IsInFootOrEndnote();
932 void DomainMapper_Impl::SetIsFirstParagraphInShape(bool bIsFirst
)
934 m_bIsFirstParaInShape
= bIsFirst
;
937 void DomainMapper_Impl::SetIsDummyParaAddedForTableInSection( bool bIsAdded
)
939 m_bDummyParaAddedForTableInSection
= bIsAdded
;
940 m_bDummyParaAddedForTableInSectionPage
= bIsAdded
;
943 void DomainMapper_Impl::SetIsDummyParaAddedForTableInSectionPage( bool bIsAdded
)
945 m_bDummyParaAddedForTableInSectionPage
= bIsAdded
;
949 void DomainMapper_Impl::SetIsTextFrameInserted( bool bIsInserted
)
951 m_bTextFrameInserted
= bIsInserted
;
955 void DomainMapper_Impl::SetParaSectpr(bool bParaSectpr
)
957 m_bParaSectpr
= bParaSectpr
;
961 void DomainMapper_Impl::SetSdt(bool bSdt
)
965 if (m_bSdt
&& !m_aTextAppendStack
.empty())
967 m_xSdtEntryStart
= GetTopTextAppend()->getEnd();
971 m_xSdtEntryStart
.clear();
975 void DomainMapper_Impl::PushSdt()
977 if (m_aTextAppendStack
.empty())
982 uno::Reference
<text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
983 uno::Reference
<text::XTextCursor
> xCursor
984 = xTextAppend
->getText()->createTextCursorByRange(xTextAppend
->getEnd());
985 // Offset so the cursor is not adjusted as we import the SDT's content.
986 bool bStart
= !xCursor
->goLeft(1, /*bExpand=*/false);
987 m_xSdtStarts
.push({bStart
, OUString(), xCursor
->getStart()});
990 const std::stack
<BookmarkInsertPosition
>& DomainMapper_Impl::GetSdtStarts() const
995 void DomainMapper_Impl::PopSdt()
997 if (m_xSdtStarts
.empty())
1002 BookmarkInsertPosition aPosition
= m_xSdtStarts
.top();
1004 uno::Reference
<text::XTextRange
> xStart
= aPosition
.m_xTextRange
;
1005 uno::Reference
<text::XTextRange
> xEnd
= GetTopTextAppend()->getEnd();
1006 uno::Reference
<text::XText
> xText
= xEnd
->getText();
1007 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursorByRange(xStart
);
1010 SAL_WARN("writerfilter.dmapper", "DomainMapper_Impl::PopSdt: no start position");
1014 if (aPosition
.m_bIsStartOfText
)
1016 // Go to the start of the end's paragraph. This helps in case
1017 // DomainMapper_Impl::AddDummyParaForTableInSection() would make our range multi-paragraph,
1018 // while the intention is to keep start/end inside the same paragraph for run SDTs.
1019 uno::Reference
<text::XParagraphCursor
> xParagraphCursor(xCursor
, uno::UNO_QUERY
);
1020 if (xParagraphCursor
.is()
1021 && m_pSdtHelper
->GetSdtType() == NS_ooxml::LN_CT_SdtRun_sdtContent
)
1023 xCursor
->gotoRange(xEnd
, /*bExpand=*/false);
1024 xParagraphCursor
->gotoStartOfParagraph(/*bExpand=*/false);
1029 // Undo the goLeft() in DomainMapper_Impl::PushSdt();
1030 xCursor
->goRight(1, /*bExpand=*/false);
1032 xCursor
->gotoRange(xEnd
, /*bExpand=*/true);
1034 std::optional
<OUString
> oData
= m_pSdtHelper
->getValueFromDataBinding();
1035 if (oData
.has_value())
1037 // Data binding has a value for us, prefer that over the in-document value.
1038 xCursor
->setString(*oData
);
1040 // Such value is always a plain text string, remove the char style of the placeholder.
1041 uno::Reference
<beans::XPropertyState
> xPropertyState(xCursor
, uno::UNO_QUERY
);
1042 if (xPropertyState
.is())
1044 xPropertyState
->setPropertyToDefault("CharStyleName");
1048 uno::Reference
<text::XTextContent
> xContentControl(
1049 m_xTextFactory
->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY
);
1050 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
1051 if (m_pSdtHelper
->GetShowingPlcHdr())
1053 xContentControlProps
->setPropertyValue("ShowingPlaceHolder",
1054 uno::Any(m_pSdtHelper
->GetShowingPlcHdr()));
1057 if (!m_pSdtHelper
->GetPlaceholderDocPart().isEmpty())
1059 xContentControlProps
->setPropertyValue("PlaceholderDocPart",
1060 uno::Any(m_pSdtHelper
->GetPlaceholderDocPart()));
1063 if (!m_pSdtHelper
->GetDataBindingPrefixMapping().isEmpty())
1065 xContentControlProps
->setPropertyValue("DataBindingPrefixMappings",
1066 uno::Any(m_pSdtHelper
->GetDataBindingPrefixMapping()));
1068 if (!m_pSdtHelper
->GetDataBindingXPath().isEmpty())
1070 xContentControlProps
->setPropertyValue("DataBindingXpath",
1071 uno::Any(m_pSdtHelper
->GetDataBindingXPath()));
1073 if (!m_pSdtHelper
->GetDataBindingStoreItemID().isEmpty())
1075 xContentControlProps
->setPropertyValue("DataBindingStoreItemID",
1076 uno::Any(m_pSdtHelper
->GetDataBindingStoreItemID()));
1079 if (!m_pSdtHelper
->GetColor().isEmpty())
1081 xContentControlProps
->setPropertyValue("Color",
1082 uno::Any(m_pSdtHelper
->GetColor()));
1085 if (!m_pSdtHelper
->GetAppearance().isEmpty())
1087 xContentControlProps
->setPropertyValue("Appearance",
1088 uno::Any(m_pSdtHelper
->GetAppearance()));
1091 if (!m_pSdtHelper
->GetAlias().isEmpty())
1093 xContentControlProps
->setPropertyValue("Alias",
1094 uno::Any(m_pSdtHelper
->GetAlias()));
1097 if (!m_pSdtHelper
->GetTag().isEmpty())
1099 xContentControlProps
->setPropertyValue("Tag",
1100 uno::Any(m_pSdtHelper
->GetTag()));
1103 if (m_pSdtHelper
->GetId())
1105 xContentControlProps
->setPropertyValue("Id", uno::Any(m_pSdtHelper
->GetId()));
1108 if (m_pSdtHelper
->GetTabIndex())
1110 xContentControlProps
->setPropertyValue("TabIndex", uno::Any(m_pSdtHelper
->GetTabIndex()));
1113 if (!m_pSdtHelper
->GetLock().isEmpty())
1115 xContentControlProps
->setPropertyValue("Lock", uno::Any(m_pSdtHelper
->GetLock()));
1118 if (m_pSdtHelper
->getControlType() == SdtControlType::checkBox
)
1120 xContentControlProps
->setPropertyValue("Checkbox", uno::Any(true));
1122 xContentControlProps
->setPropertyValue("Checked", uno::Any(m_pSdtHelper
->GetChecked()));
1124 xContentControlProps
->setPropertyValue("CheckedState",
1125 uno::Any(m_pSdtHelper
->GetCheckedState()));
1127 xContentControlProps
->setPropertyValue("UncheckedState",
1128 uno::Any(m_pSdtHelper
->GetUncheckedState()));
1131 if (m_pSdtHelper
->getControlType() == SdtControlType::dropDown
1132 || m_pSdtHelper
->getControlType() == SdtControlType::comboBox
)
1134 std::vector
<OUString
>& rDisplayTexts
= m_pSdtHelper
->getDropDownDisplayTexts();
1135 std::vector
<OUString
>& rValues
= m_pSdtHelper
->getDropDownItems();
1136 if (rDisplayTexts
.size() == rValues
.size())
1138 uno::Sequence
<beans::PropertyValues
> aItems(rValues
.size());
1139 beans::PropertyValues
* pItems
= aItems
.getArray();
1140 for (size_t i
= 0; i
< rValues
.size(); ++i
)
1142 uno::Sequence
<beans::PropertyValue
> aItem
= {
1143 comphelper::makePropertyValue("DisplayText", rDisplayTexts
[i
]),
1144 comphelper::makePropertyValue("Value", rValues
[i
]),
1148 xContentControlProps
->setPropertyValue("ListItems", uno::Any(aItems
));
1149 if (m_pSdtHelper
->getControlType() == SdtControlType::dropDown
)
1151 xContentControlProps
->setPropertyValue("DropDown", uno::Any(true));
1155 xContentControlProps
->setPropertyValue("ComboBox", uno::Any(true));
1160 if (m_pSdtHelper
->getControlType() == SdtControlType::picture
)
1162 xContentControlProps
->setPropertyValue("Picture", uno::Any(true));
1165 bool bDateFromDataBinding
= false;
1166 if (m_pSdtHelper
->getControlType() == SdtControlType::datePicker
)
1168 xContentControlProps
->setPropertyValue("Date", uno::Any(true));
1169 OUString aDateFormat
= m_pSdtHelper
->getDateFormat().makeStringAndClear();
1170 xContentControlProps
->setPropertyValue("DateFormat",
1171 uno::Any(aDateFormat
.replaceAll("'", "\"")));
1172 xContentControlProps
->setPropertyValue("DateLanguage",
1173 uno::Any(m_pSdtHelper
->getLocale().makeStringAndClear()));
1174 OUString aCurrentDate
= m_pSdtHelper
->getDate().makeStringAndClear();
1175 if (oData
.has_value())
1177 aCurrentDate
= *oData
;
1178 bDateFromDataBinding
= true;
1180 xContentControlProps
->setPropertyValue("CurrentDate",
1181 uno::Any(aCurrentDate
));
1184 if (m_pSdtHelper
->getControlType() == SdtControlType::plainText
)
1186 xContentControlProps
->setPropertyValue("PlainText", uno::Any(true));
1189 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
1191 if (bDateFromDataBinding
)
1193 OUString aDateString
;
1194 xContentControlProps
->getPropertyValue("DateString") >>= aDateString
;
1195 xCursor
->setString(aDateString
);
1198 m_pSdtHelper
->clear();
1201 void DomainMapper_Impl::PushProperties(ContextType eId
)
1203 PropertyMapPtr
pInsert(eId
== CONTEXT_SECTION
?
1204 (new SectionPropertyMap( m_bIsFirstSection
)) :
1205 eId
== CONTEXT_PARAGRAPH
? new ParagraphPropertyMap
: new PropertyMap
);
1206 if(eId
== CONTEXT_SECTION
)
1208 if( m_bIsFirstSection
)
1209 m_bIsFirstSection
= false;
1210 // beginning with the second section group a section has to be inserted
1211 // into the document
1212 SectionPropertyMap
* pSectionContext_
= dynamic_cast< SectionPropertyMap
* >( pInsert
.get() );
1213 if (!m_aTextAppendStack
.empty())
1215 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
1216 if (xTextAppend
.is() && pSectionContext_
)
1217 pSectionContext_
->SetStart( xTextAppend
->getEnd() );
1220 if(eId
== CONTEXT_PARAGRAPH
&& m_bIsSplitPara
)
1222 // Some paragraph properties only apply at the beginning of the paragraph - apply only once.
1225 auto pParaContext
= static_cast<ParagraphPropertyMap
*>(GetTopContextOfType(eId
).get());
1226 pParaContext
->props().SetListId(-1);
1227 pParaContext
->Erase(PROP_NUMBERING_RULES
); // only true with column, not page break
1228 pParaContext
->Erase(PROP_NUMBERING_LEVEL
);
1229 pParaContext
->Erase(PROP_NUMBERING_TYPE
);
1230 pParaContext
->Erase(PROP_START_WITH
);
1232 pParaContext
->Insert(PROP_PARA_TOP_MARGIN
, uno::Any(sal_uInt32(0)));
1233 pParaContext
->Erase(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING
);
1234 pParaContext
->Insert(PROP_PARA_FIRST_LINE_INDENT
, uno::Any(sal_uInt32(0)));
1237 m_aPropertyStacks
[eId
].push( GetTopContextOfType(eId
));
1238 m_bIsSplitPara
= false;
1242 m_aPropertyStacks
[eId
].push( pInsert
);
1244 m_aContextStack
.push(eId
);
1246 m_pTopContext
= m_aPropertyStacks
[eId
].top();
1250 void DomainMapper_Impl::PushStyleProperties( const PropertyMapPtr
& pStyleProperties
)
1252 m_aPropertyStacks
[CONTEXT_STYLESHEET
].push( pStyleProperties
);
1253 m_aContextStack
.push(CONTEXT_STYLESHEET
);
1255 m_pTopContext
= m_aPropertyStacks
[CONTEXT_STYLESHEET
].top();
1259 void DomainMapper_Impl::PushListProperties(const PropertyMapPtr
& pListProperties
)
1261 m_aPropertyStacks
[CONTEXT_LIST
].push( pListProperties
);
1262 m_aContextStack
.push(CONTEXT_LIST
);
1263 m_pTopContext
= m_aPropertyStacks
[CONTEXT_LIST
].top();
1267 void DomainMapper_Impl::PopProperties(ContextType eId
)
1269 OSL_ENSURE(!m_aPropertyStacks
[eId
].empty(), "section stack already empty");
1270 if ( m_aPropertyStacks
[eId
].empty() )
1273 if ( eId
== CONTEXT_SECTION
)
1275 if (m_aPropertyStacks
[eId
].size() == 1) // tdf#112202 only top level !!!
1277 m_pLastSectionContext
= m_aPropertyStacks
[eId
].top();
1280 else if (eId
== CONTEXT_CHARACTER
)
1282 m_pLastCharacterContext
= m_aPropertyStacks
[eId
].top();
1283 // Sadly an assert about deferredCharacterProperties being empty is not possible
1284 // here, because appendTextPortion() may not be called for every character section.
1285 m_deferredCharacterProperties
.clear();
1288 if (!IsInFootOrEndnote() && IsInCustomFootnote() && !m_aPropertyStacks
[eId
].empty())
1290 PropertyMapPtr pRet
= m_aPropertyStacks
[eId
].top();
1291 if (pRet
->GetFootnote().is() && m_pFootnoteContext
.is())
1292 EndCustomFootnote();
1295 m_aPropertyStacks
[eId
].pop();
1296 m_aContextStack
.pop();
1297 if(!m_aContextStack
.empty() && !m_aPropertyStacks
[m_aContextStack
.top()].empty())
1299 m_pTopContext
= m_aPropertyStacks
[m_aContextStack
.top()].top();
1302 // OSL_ENSURE(eId == CONTEXT_SECTION, "this should happen at a section context end");
1303 m_pTopContext
.clear();
1308 PropertyMapPtr
DomainMapper_Impl::GetTopContextOfType(ContextType eId
)
1310 PropertyMapPtr pRet
;
1311 if(!m_aPropertyStacks
[eId
].empty())
1312 pRet
= m_aPropertyStacks
[eId
].top();
1316 bool DomainMapper_Impl::HasTopText() const
1318 return !m_aTextAppendStack
.empty();
1321 uno::Reference
< text::XTextAppend
> const & DomainMapper_Impl::GetTopTextAppend()
1323 OSL_ENSURE(!m_aTextAppendStack
.empty(), "text append stack is empty" );
1324 return m_aTextAppendStack
.top().xTextAppend
;
1327 FieldContextPtr
const & DomainMapper_Impl::GetTopFieldContext()
1329 SAL_WARN_IF(m_aFieldStack
.empty(), "writerfilter.dmapper", "Field stack is empty");
1330 return m_aFieldStack
.back();
1333 bool DomainMapper_Impl::HasTopAnchoredObjects() const
1335 return !m_aTextAppendStack
.empty() && !m_aTextAppendStack
.top().m_aAnchoredObjects
.empty();
1338 void DomainMapper_Impl::InitTabStopFromStyle( const uno::Sequence
< style::TabStop
>& rInitTabStops
)
1340 OSL_ENSURE(m_aCurrentTabStops
.empty(), "tab stops already initialized");
1341 for( const auto& rTabStop
: rInitTabStops
)
1343 m_aCurrentTabStops
.emplace_back(rTabStop
);
1347 void DomainMapper_Impl::IncorporateTabStop( const DeletableTabStop
& rTabStop
)
1349 sal_Int32 nConverted
= rTabStop
.Position
;
1350 auto aIt
= std::find_if(m_aCurrentTabStops
.begin(), m_aCurrentTabStops
.end(),
1351 [&nConverted
](const DeletableTabStop
& rCurrentTabStop
) { return rCurrentTabStop
.Position
== nConverted
; });
1352 if( aIt
!= m_aCurrentTabStops
.end() )
1354 if( rTabStop
.bDeleted
)
1355 m_aCurrentTabStops
.erase( aIt
);
1360 m_aCurrentTabStops
.push_back( rTabStop
);
1364 uno::Sequence
< style::TabStop
> DomainMapper_Impl::GetCurrentTabStopAndClear()
1366 std::vector
<style::TabStop
> aRet
;
1367 for (const DeletableTabStop
& rStop
: m_aCurrentTabStops
)
1369 if (!rStop
.bDeleted
)
1370 aRet
.push_back(rStop
);
1372 m_aCurrentTabStops
.clear();
1373 return comphelper::containerToSequence(aRet
);
1376 OUString
DomainMapper_Impl::GetCurrentParaStyleName()
1379 // use saved currParaStyleName as a fallback, in case no particular para style name applied.
1380 // tdf#134784 except in the case of first paragraph of shapes to avoid bad fallback.
1381 // TODO fix this "highly inaccurate" m_sCurrentParaStyleName
1383 sName
= m_sCurrentParaStyleName
;
1385 PropertyMapPtr pParaContext
= GetTopContextOfType(CONTEXT_PARAGRAPH
);
1386 if ( pParaContext
&& pParaContext
->isSet(PROP_PARA_STYLE_NAME
) )
1387 pParaContext
->getProperty(PROP_PARA_STYLE_NAME
)->second
>>= sName
;
1389 // In rare situations the name might still be blank, so use the default style,
1390 // despite documentation that states, "If this attribute is not specified for any style,
1391 // then no properties shall be applied to objects of the specified type."
1392 // Word, however, assigns "Normal" style even in these situations.
1393 if ( !m_bInStyleSheetImport
&& sName
.isEmpty() )
1394 sName
= GetDefaultParaStyleName();
1399 OUString
DomainMapper_Impl::GetDefaultParaStyleName()
1401 // After import the default style won't change and is frequently requested: cache the LO style name.
1402 // TODO assert !InStyleSheetImport? This function really only makes sense once import is finished anyway.
1403 if ( m_sDefaultParaStyleName
.isEmpty() )
1405 const StyleSheetEntryPtr pEntry
= GetStyleSheetTable()->FindDefaultParaStyle();
1406 if ( pEntry
&& !pEntry
->m_sConvertedStyleName
.isEmpty() )
1408 if ( !m_bInStyleSheetImport
)
1409 m_sDefaultParaStyleName
= pEntry
->m_sConvertedStyleName
;
1410 return pEntry
->m_sConvertedStyleName
;
1415 return m_sDefaultParaStyleName
;
1418 uno::Any
DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId
, StyleSheetEntryPtr pEntry
, const bool bDocDefaults
, const bool bPara
, bool* pIsDocDefault
)
1422 if(pEntry
->m_pProperties
)
1424 std::optional
<PropertyMap::Property
> aProperty
=
1425 pEntry
->m_pProperties
->getProperty(eId
);
1429 *pIsDocDefault
= pEntry
->m_pProperties
->isDocDefault(eId
);
1431 return aProperty
->second
;
1434 //search until the property is set or no parent is available
1435 StyleSheetEntryPtr pNewEntry
;
1436 if ( !pEntry
->m_sBaseStyleIdentifier
.isEmpty() )
1437 pNewEntry
= GetStyleSheetTable()->FindStyleSheetByISTD(pEntry
->m_sBaseStyleIdentifier
);
1439 SAL_WARN_IF( pEntry
== pNewEntry
, "writerfilter.dmapper", "circular loop in style hierarchy?");
1441 if (pEntry
== pNewEntry
) //fdo#49587
1446 // not found in style, try the document's DocDefault properties
1447 if ( bDocDefaults
&& bPara
)
1449 const PropertyMapPtr
& pDefaultParaProps
= GetStyleSheetTable()->GetDefaultParaProps();
1450 if ( pDefaultParaProps
)
1452 std::optional
<PropertyMap::Property
> aProperty
= pDefaultParaProps
->getProperty(eId
);
1456 *pIsDocDefault
= true;
1458 return aProperty
->second
;
1462 if ( bDocDefaults
&& isCharacterProperty(eId
) )
1464 const PropertyMapPtr
& pDefaultCharProps
= GetStyleSheetTable()->GetDefaultCharProps();
1465 if ( pDefaultCharProps
)
1467 std::optional
<PropertyMap::Property
> aProperty
= pDefaultCharProps
->getProperty(eId
);
1471 *pIsDocDefault
= true;
1473 return aProperty
->second
;
1479 *pIsDocDefault
= false;
1484 uno::Any
DomainMapper_Impl::GetPropertyFromParaStyleSheet(PropertyIds eId
)
1486 StyleSheetEntryPtr pEntry
;
1487 if ( m_bInStyleSheetImport
)
1488 pEntry
= GetStyleSheetTable()->GetCurrentEntry();
1490 pEntry
= GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(GetCurrentParaStyleName());
1491 return GetPropertyFromStyleSheet(eId
, pEntry
, /*bDocDefaults=*/true, /*bPara=*/true);
1494 uno::Any
DomainMapper_Impl::GetPropertyFromCharStyleSheet(PropertyIds eId
, const PropertyMapPtr
& rContext
)
1496 if ( m_bInStyleSheetImport
|| eId
== PROP_CHAR_STYLE_NAME
|| !isCharacterProperty(eId
) )
1499 StyleSheetEntryPtr pEntry
;
1500 OUString sCharStyleName
;
1501 if ( GetAnyProperty(PROP_CHAR_STYLE_NAME
, rContext
) >>= sCharStyleName
)
1502 pEntry
= GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(sCharStyleName
);
1503 return GetPropertyFromStyleSheet(eId
, pEntry
, /*bDocDefaults=*/false, /*bPara=*/false);
1506 uno::Any
DomainMapper_Impl::GetAnyProperty(PropertyIds eId
, const PropertyMapPtr
& rContext
)
1508 // first look in directly applied attributes
1511 std::optional
<PropertyMap::Property
> aProperty
= rContext
->getProperty(eId
);
1513 return aProperty
->second
;
1516 // then look whether it was directly applied as a paragraph property
1517 PropertyMapPtr pParaContext
= GetTopContextOfType(CONTEXT_PARAGRAPH
);
1518 if (pParaContext
&& rContext
!= pParaContext
)
1520 std::optional
<PropertyMap::Property
> aProperty
= pParaContext
->getProperty(eId
);
1522 return aProperty
->second
;
1525 // then look whether it was inherited from a directly applied character style
1526 if ( eId
!= PROP_CHAR_STYLE_NAME
&& isCharacterProperty(eId
) )
1528 uno::Any aRet
= GetPropertyFromCharStyleSheet(eId
, rContext
);
1529 if ( aRet
.hasValue() )
1533 // then look in current paragraph style, and docDefaults
1534 return GetPropertyFromParaStyleSheet(eId
);
1537 OUString
DomainMapper_Impl::GetListStyleName(sal_Int32 nListId
)
1539 auto const pList(GetListTable()->GetList( nListId
));
1540 return pList
? pList
->GetStyleName() : OUString();
1543 ListsManager::Pointer
const & DomainMapper_Impl::GetListTable()
1547 new ListsManager( m_rDMapper
, m_xTextFactory
);
1548 return m_pListTable
;
1552 void DomainMapper_Impl::deferBreak( BreakType deferredBreakType
)
1554 switch (deferredBreakType
)
1557 m_nLineBreaksDeferred
++;
1560 m_bIsColumnBreakDeferred
= true;
1563 // See SwWW8ImplReader::HandlePageBreakChar(), page break should be
1564 // ignored inside tables.
1565 if (m_nTableDepth
> 0)
1568 m_bIsPageBreakDeferred
= true;
1575 bool DomainMapper_Impl::isBreakDeferred( BreakType deferredBreakType
)
1577 switch (deferredBreakType
)
1580 return m_nLineBreaksDeferred
> 0;
1582 return m_bIsColumnBreakDeferred
;
1584 return m_bIsPageBreakDeferred
;
1590 void DomainMapper_Impl::clearDeferredBreak(BreakType deferredBreakType
)
1592 switch (deferredBreakType
)
1595 assert(m_nLineBreaksDeferred
> 0);
1596 m_nLineBreaksDeferred
--;
1599 m_bIsColumnBreakDeferred
= false;
1602 m_bIsPageBreakDeferred
= false;
1609 void DomainMapper_Impl::clearDeferredBreaks()
1611 m_nLineBreaksDeferred
= 0;
1612 m_bIsColumnBreakDeferred
= false;
1613 m_bIsPageBreakDeferred
= false;
1616 void DomainMapper_Impl::setSdtEndDeferred(bool bSdtEndDeferred
)
1618 m_bSdtEndDeferred
= bSdtEndDeferred
;
1621 bool DomainMapper_Impl::isSdtEndDeferred() const
1623 return m_bSdtEndDeferred
;
1626 void DomainMapper_Impl::setParaSdtEndDeferred(bool bParaSdtEndDeferred
)
1628 m_bParaSdtEndDeferred
= bParaSdtEndDeferred
;
1631 bool DomainMapper_Impl::isParaSdtEndDeferred() const
1633 return m_bParaSdtEndDeferred
;
1636 static void lcl_MoveBorderPropertiesToFrame(std::vector
<beans::PropertyValue
>& rFrameProperties
,
1637 uno::Reference
<text::XTextRange
> const& xStartTextRange
,
1638 uno::Reference
<text::XTextRange
> const& xEndTextRange
,
1643 if (!xStartTextRange
.is()) //rhbz#1077780
1645 uno::Reference
<text::XTextCursor
> xRangeCursor
= xStartTextRange
->getText()->createTextCursorByRange( xStartTextRange
);
1646 xRangeCursor
->gotoRange( xEndTextRange
, true );
1648 uno::Reference
<beans::XPropertySet
> xTextRangeProperties(xRangeCursor
, uno::UNO_QUERY
);
1649 if(!xTextRangeProperties
.is())
1652 static PropertyIds
const aBorderProperties
[] =
1658 PROP_LEFT_BORDER_DISTANCE
,
1659 PROP_RIGHT_BORDER_DISTANCE
,
1660 PROP_TOP_BORDER_DISTANCE
,
1661 PROP_BOTTOM_BORDER_DISTANCE
1664 // The frame width specified does not include border spacing,
1665 // so the frame needs to be increased by the left/right para border spacing amount
1666 sal_Int32 nWidth
= 0;
1667 sal_Int32 nIndexOfWidthProperty
= -1;
1668 sal_Int16 nType
= text::SizeType::FIX
;
1669 for (size_t i
= 0; nType
== text::SizeType::FIX
&& i
< rFrameProperties
.size(); ++i
)
1671 if (rFrameProperties
[i
].Name
== "WidthType")
1672 rFrameProperties
[i
].Value
>>= nType
;
1673 else if (rFrameProperties
[i
].Name
== "Width")
1674 nIndexOfWidthProperty
= i
;
1676 if (nIndexOfWidthProperty
> -1 && nType
== text::SizeType::FIX
)
1677 rFrameProperties
[nIndexOfWidthProperty
].Value
>>= nWidth
;
1679 for( size_t nProperty
= 0; nProperty
< SAL_N_ELEMENTS( aBorderProperties
); ++nProperty
)
1681 const OUString
& sPropertyName
= getPropertyName(aBorderProperties
[nProperty
]);
1682 beans::PropertyValue aValue
;
1683 aValue
.Name
= sPropertyName
;
1684 aValue
.Value
= xTextRangeProperties
->getPropertyValue(sPropertyName
);
1686 xTextRangeProperties
->setPropertyValue( sPropertyName
, uno::Any(table::BorderLine2()));
1687 else // border spacing
1689 sal_Int32 nDistance
= 0;
1690 aValue
.Value
>>= nDistance
;
1692 // left4/right5 need to be duplicated because of INVERT_BORDER_SPACING (DOCX only)
1693 // Do not duplicate the top6/bottom7 border spacing.
1694 if (nProperty
> 5 || bIsRTFImport
)
1695 aValue
.Value
<<= sal_Int32(0);
1697 // frames need to be increased by the left/right para border spacing amount
1698 // This is needed for RTF as well, but that requires other export/import fixes.
1699 if (!bIsRTFImport
&& nProperty
< 6 && nWidth
&& nDistance
)
1701 nWidth
+= nDistance
;
1702 rFrameProperties
[nIndexOfWidthProperty
].Value
<<= nWidth
;
1705 if (aValue
.Value
.hasValue())
1706 rFrameProperties
.push_back(aValue
);
1709 catch( const uno::Exception
& )
1715 static void lcl_AddRange(
1716 ParagraphPropertiesPtr
const & pToBeSavedProperties
,
1717 uno::Reference
< text::XTextAppend
> const& xTextAppend
,
1718 TextAppendContext
const & rAppendContext
)
1720 uno::Reference
<text::XParagraphCursor
> xParaCursor(
1721 xTextAppend
->createTextCursorByRange( rAppendContext
.xInsertPosition
.is() ? rAppendContext
.xInsertPosition
: xTextAppend
->getEnd()), uno::UNO_QUERY_THROW
);
1722 pToBeSavedProperties
->SetEndingRange(xParaCursor
->getStart());
1723 xParaCursor
->gotoStartOfParagraph( false );
1725 pToBeSavedProperties
->SetStartingRange(xParaCursor
->getStart());
1729 //define some default frame width - 0cm ATM: this allow the frame to be wrapped around the text
1730 constexpr sal_Int32 DEFAULT_FRAME_MIN_WIDTH
= 0;
1731 constexpr sal_Int32 DEFAULT_FRAME_MIN_HEIGHT
= 0;
1732 constexpr sal_Int32 DEFAULT_VALUE
= 0;
1734 std::vector
<css::beans::PropertyValue
>
1735 DomainMapper_Impl::MakeFrameProperties(const ParagraphProperties
& rProps
)
1737 std::vector
<beans::PropertyValue
> aFrameProperties
;
1741 // A paragraph's properties come from direct formatting or somewhere in the style hierarchy
1742 std::vector
<const ParagraphProperties
*> vProps
;
1743 vProps
.emplace_back(&rProps
);
1744 sal_Int8 nSafetyLimit
= 16;
1745 StyleSheetEntryPtr pStyle
1746 = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(rProps
.GetParaStyleName());
1747 while (nSafetyLimit
-- && pStyle
&& pStyle
->m_pProperties
)
1749 vProps
.emplace_back(&pStyle
->m_pProperties
->props());
1750 assert(pStyle
->m_sBaseStyleIdentifier
!= pStyle
->m_sStyleName
);
1751 if (pStyle
->m_sBaseStyleIdentifier
.isEmpty())
1753 pStyle
= GetStyleSheetTable()->FindStyleSheetByISTD(pStyle
->m_sBaseStyleIdentifier
);
1755 SAL_WARN_IF(!nSafetyLimit
, "writerfilter.dmapper", "Inheritance loop likely: early exit");
1758 sal_Int32 nWidth
= -1;
1759 for (const auto pProp
: vProps
)
1761 if (pProp
->Getw() < 0)
1763 nWidth
= pProp
->Getw();
1766 bool bAutoWidth
= nWidth
< 1;
1768 nWidth
= DEFAULT_FRAME_MIN_WIDTH
;
1769 aFrameProperties
.push_back(
1770 comphelper::makePropertyValue(getPropertyName(PROP_WIDTH
), nWidth
));
1771 aFrameProperties
.push_back(
1772 comphelper::makePropertyValue(getPropertyName(PROP_WIDTH_TYPE
),
1773 bAutoWidth
? text::SizeType::MIN
: text::SizeType::FIX
));
1775 bool bValidH
= false;
1776 sal_Int32 nHeight
= DEFAULT_FRAME_MIN_HEIGHT
;
1777 for (const auto pProp
: vProps
)
1779 if (pProp
->Geth() < 0)
1781 nHeight
= pProp
->Geth();
1785 aFrameProperties
.push_back(
1786 comphelper::makePropertyValue(getPropertyName(PROP_HEIGHT
), nHeight
));
1788 sal_Int16 nhRule
= -1;
1789 for (const auto pProp
: vProps
)
1791 if (pProp
->GethRule() < 0)
1793 nhRule
= pProp
->GethRule();
1798 if (bValidH
&& nHeight
)
1800 // [MS-OE376] Word uses a default value of "atLeast" for
1801 // this attribute when the value of the h attribute is not 0.
1802 nhRule
= text::SizeType::MIN
;
1806 nhRule
= text::SizeType::VARIABLE
;
1809 aFrameProperties
.push_back(
1810 comphelper::makePropertyValue(getPropertyName(PROP_SIZE_TYPE
), nhRule
));
1812 bool bValidX
= false;
1813 sal_Int32 nX
= DEFAULT_VALUE
;
1814 for (const auto pProp
: vProps
)
1816 bValidX
= pProp
->IsxValid();
1822 aFrameProperties
.push_back(
1823 comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT_POSITION
), nX
));
1825 sal_Int16 nHoriOrient
= text::HoriOrientation::NONE
;
1826 for (const auto pProp
: vProps
)
1828 if (pProp
->GetxAlign() < 0)
1830 nHoriOrient
= pProp
->GetxAlign();
1833 aFrameProperties
.push_back(
1834 comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT
), nHoriOrient
));
1836 //Default the anchor in case FramePr_hAnchor is missing ECMA 17.3.1.11
1837 sal_Int16 nHAnchor
= text::RelOrientation::FRAME
; // 'text'
1838 for (const auto pProp
: vProps
)
1840 if (pProp
->GethAnchor() < 0)
1842 nHAnchor
= pProp
->GethAnchor();
1845 aFrameProperties
.push_back(
1846 comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT_RELATION
), nHAnchor
));
1848 bool bValidY
= false;
1849 sal_Int32 nY
= DEFAULT_VALUE
;
1850 for (const auto pProp
: vProps
)
1852 bValidY
= pProp
->IsyValid();
1858 aFrameProperties
.push_back(
1859 comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_POSITION
), nY
));
1861 sal_Int16 nVertOrient
= text::VertOrientation::NONE
;
1862 for (const auto pProp
: vProps
)
1864 if (pProp
->GetyAlign() < 0)
1866 nVertOrient
= pProp
->GetyAlign();
1869 aFrameProperties
.push_back(
1870 comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT
), nVertOrient
));
1872 // Default the anchor in case FramePr_vAnchor is missing.
1873 // ECMA 17.3.1.11 says "page",
1874 // but errata documentation MS-OE376 2.1.48 Section 2.3.1.11 says "text"
1875 // while actual testing usually indicates "margin" tdf#157572 tdf#112287
1876 sal_Int16 nVAnchor
= text::RelOrientation::PAGE_PRINT_AREA
; // 'margin'
1877 if (!nY
&& (bValidY
|| nVertOrient
== text::VertOrientation::NONE
))
1879 // special cases? "auto" position defaults to "paragraph" based on testing when w:y=0
1880 nVAnchor
= text::RelOrientation::FRAME
; // 'text'
1882 for (const auto pProp
: vProps
)
1884 if (pProp
->GetvAnchor() < 0)
1886 nVAnchor
= pProp
->GetvAnchor();
1889 aFrameProperties
.push_back(
1890 comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_RELATION
), nVAnchor
));
1892 text::WrapTextMode nWrap
= text::WrapTextMode_NONE
;
1893 for (const auto pProp
: vProps
)
1895 if (pProp
->GetWrap() == text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
)
1897 nWrap
= pProp
->GetWrap();
1900 aFrameProperties
.push_back(
1901 comphelper::makePropertyValue(getPropertyName(PROP_SURROUND
), nWrap
));
1903 sal_Int32 nRightDist
= 0;
1904 sal_Int32 nLeftDist
= 0;
1905 for (const auto pProp
: vProps
)
1907 if (pProp
->GethSpace() < 0)
1909 nLeftDist
= nRightDist
= pProp
->GethSpace();
1912 aFrameProperties
.push_back(comphelper::makePropertyValue(
1913 getPropertyName(PROP_LEFT_MARGIN
),
1914 nHoriOrient
== text::HoriOrientation::LEFT
? 0 : nLeftDist
));
1915 aFrameProperties
.push_back(comphelper::makePropertyValue(
1916 getPropertyName(PROP_RIGHT_MARGIN
),
1917 nHoriOrient
== text::HoriOrientation::RIGHT
? 0 : nRightDist
));
1919 sal_Int32 nBottomDist
= 0;
1920 sal_Int32 nTopDist
= 0;
1921 for (const auto pProp
: vProps
)
1923 if (pProp
->GetvSpace() < 0)
1925 nTopDist
= nBottomDist
= pProp
->GetvSpace();
1928 aFrameProperties
.push_back(comphelper::makePropertyValue(
1929 getPropertyName(PROP_TOP_MARGIN
),
1930 nVertOrient
== text::VertOrientation::TOP
? 0 : nTopDist
));
1931 aFrameProperties
.push_back(comphelper::makePropertyValue(
1932 getPropertyName(PROP_BOTTOM_MARGIN
),
1933 nVertOrient
== text::VertOrientation::BOTTOM
? 0 : nBottomDist
));
1935 catch (const uno::Exception
&)
1939 return aFrameProperties
;
1942 void DomainMapper_Impl::CheckUnregisteredFrameConversion(bool bPreventOverlap
)
1944 if (m_aTextAppendStack
.empty())
1946 TextAppendContext
& rAppendContext
= m_aTextAppendStack
.top();
1947 // n#779642: ignore fly frame inside table as it could lead to messy situations
1948 if (!rAppendContext
.pLastParagraphProperties
)
1950 if (!rAppendContext
.pLastParagraphProperties
->IsFrameMode())
1952 if (!hasTableManager())
1954 if (getTableManager().isInTable())
1957 std::vector
<beans::PropertyValue
> aFrameProperties
1958 = MakeFrameProperties(*rAppendContext
.pLastParagraphProperties
);
1960 if (const std::optional
<sal_Int16
> nDirection
= PopFrameDirection())
1962 aFrameProperties
.push_back(
1963 comphelper::makePropertyValue(getPropertyName(PROP_FRM_DIRECTION
), *nDirection
));
1966 if (bPreventOverlap
)
1967 aFrameProperties
.push_back(comphelper::makePropertyValue("AllowOverlap", uno::Any(false)));
1969 // If there is no fill, the Word default is 100% transparency.
1970 // Otherwise CellColorHandler has priority, and this setting
1972 aFrameProperties
.push_back(comphelper::makePropertyValue(
1973 getPropertyName(PROP_BACK_COLOR_TRANSPARENCY
), sal_Int32(100)));
1975 uno::Sequence
<beans::PropertyValue
> aGrabBag(comphelper::InitPropertySequence(
1976 { { "ParaFrameProperties", uno::Any(true) } }));
1977 aFrameProperties
.push_back(comphelper::makePropertyValue("FrameInteropGrabBag", aGrabBag
));
1979 lcl_MoveBorderPropertiesToFrame(aFrameProperties
,
1980 rAppendContext
.pLastParagraphProperties
->GetStartingRange(),
1981 rAppendContext
.pLastParagraphProperties
->GetEndingRange(),
1984 //frame conversion has to be executed after table conversion, not now
1985 RegisterFrameConversion(rAppendContext
.pLastParagraphProperties
->GetStartingRange(),
1986 rAppendContext
.pLastParagraphProperties
->GetEndingRange(),
1987 std::move(aFrameProperties
));
1990 /// Check if the style or its parent has a list id, recursively.
1991 static sal_Int32
lcl_getListId(const StyleSheetEntryPtr
& rEntry
, const StyleSheetTablePtr
& rStyleTable
, bool & rNumberingFromBaseStyle
)
1993 const StyleSheetPropertyMap
* pEntryProperties
= rEntry
->m_pProperties
.get();
1994 if (!pEntryProperties
)
1997 sal_Int32 nListId
= pEntryProperties
->props().GetListId();
1998 // The style itself has a list id.
2002 // The style has no parent.
2003 if (rEntry
->m_sBaseStyleIdentifier
.isEmpty())
2006 const StyleSheetEntryPtr pParent
= rStyleTable
->FindStyleSheetByISTD(rEntry
->m_sBaseStyleIdentifier
);
2007 // No such parent style or loop in the style hierarchy.
2008 if (!pParent
|| pParent
== rEntry
)
2011 rNumberingFromBaseStyle
= true;
2013 return lcl_getListId(pParent
, rStyleTable
, rNumberingFromBaseStyle
);
2016 /// Return the paragraph's list level (from styles, unless pParacontext is provided).
2017 /// -1 indicates the level is not set anywhere. [In that case, with a numId, use 0 (level 1)]
2018 /// 9 indicates that numbering should be at body level (aka disabled) - rarely used by MSWord.
2019 /// 0-8 are the nine valid numbering levels.
2020 sal_Int16
DomainMapper_Impl::GetListLevel(const StyleSheetEntryPtr
& pEntry
,
2021 const PropertyMapPtr
& pParaContext
)
2023 sal_Int16 nListLevel
= -1;
2026 // Deliberately ignore inherited PROP_NUMBERING_LEVEL. Only trust StyleSheetEntry for that.
2027 std::optional
<PropertyMap::Property
> aLvl
= pParaContext
->getProperty(PROP_NUMBERING_LEVEL
);
2029 aLvl
->second
>>= nListLevel
;
2031 if (nListLevel
!= -1)
2038 const StyleSheetPropertyMap
* pEntryProperties
= pEntry
->m_pProperties
.get();
2039 if (!pEntryProperties
)
2042 nListLevel
= pEntryProperties
->GetListLevel();
2043 // The style itself has a list level.
2044 if (nListLevel
>= 0)
2047 // The style has no parent.
2048 if (pEntry
->m_sBaseStyleIdentifier
.isEmpty())
2051 const StyleSheetEntryPtr pParent
= GetStyleSheetTable()->FindStyleSheetByISTD(pEntry
->m_sBaseStyleIdentifier
);
2052 // No such parent style or loop in the style hierarchy.
2053 if (!pParent
|| pParent
== pEntry
)
2056 return GetListLevel(pParent
);
2059 void DomainMapper_Impl::ValidateListLevel(const OUString
& sStyleIdentifierD
)
2061 StyleSheetEntryPtr pMyStyle
= GetStyleSheetTable()->FindStyleSheetByISTD(sStyleIdentifierD
);
2065 sal_Int8 nListLevel
= GetListLevel(pMyStyle
);
2066 if (nListLevel
< 0 || nListLevel
>= WW_OUTLINE_MAX
)
2069 bool bDummy
= false;
2070 sal_Int16 nListId
= lcl_getListId(pMyStyle
, GetStyleSheetTable(), bDummy
);
2074 auto const pList(GetListTable()->GetList(nListId
));
2078 auto pLevel
= pList
->GetLevel(nListLevel
);
2079 if (!pLevel
&& pList
->GetAbstractDefinition())
2080 pLevel
= pList
->GetAbstractDefinition()->GetLevel(nListLevel
);
2084 if (!pLevel
->GetParaStyle())
2086 // First come, first served, and it hasn't been claimed yet, so claim it now.
2087 pLevel
->SetParaStyle(pMyStyle
);
2089 else if (pLevel
->GetParaStyle() != pMyStyle
)
2091 // This level is already used by another style, so prevent numbering via this style
2092 // by setting to body level (9).
2093 pMyStyle
->m_pProperties
->SetListLevel(WW_OUTLINE_MAX
);
2094 // WARNING: PROP_NUMBERING_LEVEL is now out of sync with GetListLevel()
2098 void DomainMapper_Impl::finishParagraph( const PropertyMapPtr
& pPropertyMap
, const bool bRemove
, const bool bNoNumbering
)
2100 if (m_bDiscardHeaderFooter
)
2103 if (!m_aFieldStack
.empty())
2105 FieldContextPtr pFieldContext
= m_aFieldStack
.back();
2106 if (pFieldContext
&& !pFieldContext
->IsCommandCompleted())
2108 std::vector
<OUString
> aCommandParts
= pFieldContext
->GetCommandParts();
2109 if (!aCommandParts
.empty() && aCommandParts
[0] == "IF")
2111 // Conditional text field conditions don't support linebreaks in Writer.
2116 if (pFieldContext
&& pFieldContext
->IsCommandCompleted())
2118 if (pFieldContext
->GetFieldId() == FIELD_IF
)
2120 // Conditional text fields can't contain newlines, finish the paragraph later.
2121 FieldParagraph aFinish
{pPropertyMap
, bRemove
};
2122 pFieldContext
->GetParagraphsToFinish().push_back(aFinish
);
2129 TagLogger::getInstance().startElement("finishParagraph");
2132 m_nLastTableCellParagraphDepth
= m_nTableCellDepth
;
2133 ParagraphPropertyMap
* pParaContext
= dynamic_cast< ParagraphPropertyMap
* >( pPropertyMap
.get() );
2134 if (m_aTextAppendStack
.empty())
2136 TextAppendContext
& rAppendContext
= m_aTextAppendStack
.top();
2137 uno::Reference
< text::XTextAppend
> xTextAppend(rAppendContext
.xTextAppend
);
2139 TagLogger::getInstance().attribute("isTextAppend", sal_uInt32(xTextAppend
.is()));
2142 const StyleSheetEntryPtr pEntry
= GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( GetCurrentParaStyleName() );
2143 SAL_WARN_IF(!pEntry
, "writerfilter.dmapper", "no style sheet found");
2144 const StyleSheetPropertyMap
* pStyleSheetProperties
= pEntry
? pEntry
->m_pProperties
.get() : nullptr;
2145 sal_Int32 nListId
= pParaContext
? pParaContext
->props().GetListId() : -1;
2146 bool isNumberingViaStyle(false);
2147 bool isNumberingViaRule
= nListId
> -1;
2148 if ( !bRemove
&& pStyleSheetProperties
&& pParaContext
)
2150 if (!pEntry
|| pEntry
->m_nStyleTypeCode
!= StyleType::STYLE_TYPE_PARA
) {
2151 // We could not resolve paragraph style or it is not a paragraph style
2152 // Remove this style reference, otherwise it will cause exceptions during further
2153 // processing and not all paragraph styles will be initialized.
2154 SAL_WARN("writerfilter.dmapper", "Paragraph style is incorrect. Ignored");
2155 pParaContext
->Erase(PROP_PARA_STYLE_NAME
);
2158 bool bNumberingFromBaseStyle
= false;
2159 if (!isNumberingViaRule
)
2160 nListId
= lcl_getListId(pEntry
, GetStyleSheetTable(), bNumberingFromBaseStyle
);
2162 //apply numbering level/style to paragraph if it was set at the style, but only if the paragraph itself
2163 //does not specify the numbering
2164 sal_Int16 nListLevel
= GetListLevel(pEntry
, pParaContext
);
2165 // Undefined listLevel with a valid numId is treated as a first level numbering.
2166 if (nListLevel
== -1 && nListId
> (IsOOXMLImport() ? 0 : -1))
2169 if (!bNoNumbering
&& nListLevel
>= 0 && nListLevel
< 9)
2170 pParaContext
->Insert( PROP_NUMBERING_LEVEL
, uno::Any(nListLevel
), false );
2172 auto const pList(GetListTable()->GetList(nListId
));
2173 if (pList
&& !pParaContext
->isSet(PROP_NUMBERING_STYLE_NAME
))
2175 // ListLevel 9 means Body Level/no numbering.
2176 if (bNoNumbering
|| nListLevel
== 9)
2178 pParaContext
->Insert(PROP_NUMBERING_STYLE_NAME
, uno::Any(OUString()), true);
2179 pParaContext
->Erase(PROP_NUMBERING_LEVEL
);
2181 else if ( !isNumberingViaRule
)
2183 isNumberingViaStyle
= true;
2184 // Since LO7.0/tdf#131321 fixed the loss of numbering in styles, this OUGHT to be obsolete,
2185 // but now other new/critical LO7.0 code expects it, and perhaps some corner cases still need it as well.
2186 pParaContext
->Insert(PROP_NUMBERING_STYLE_NAME
, uno::Any(pList
->GetStyleName()), true);
2190 // we have direct numbering, as well as paragraph-style numbering.
2191 // Apply the style if it uses the same list as the direct numbering,
2192 // otherwise the directly-applied-to-paragraph status will be lost,
2193 // and the priority of the numbering-style-indents will be lowered. tdf#133000
2195 if (nListId
== lcl_getListId(pEntry
, GetStyleSheetTable(), bDummy
))
2196 pParaContext
->Insert( PROP_NUMBERING_STYLE_NAME
, uno::Any(pList
->GetStyleName()), true );
2200 if ( isNumberingViaStyle
)
2202 // When numbering is defined by the paragraph style, then the para-style indents have priority.
2203 // But since import has just copied para-style's PROP_NUMBERING_STYLE_NAME directly onto the paragraph,
2204 // the numbering indents now have the priority.
2205 // So now import must also copy the para-style indents directly onto the paragraph to compensate.
2206 std::optional
<PropertyMap::Property
> oProperty
;
2207 const StyleSheetEntryPtr pParent
= (!pEntry
->m_sBaseStyleIdentifier
.isEmpty()) ? GetStyleSheetTable()->FindStyleSheetByISTD(pEntry
->m_sBaseStyleIdentifier
) : nullptr;
2208 const StyleSheetPropertyMap
* pParentProperties
= pParent
? pParent
->m_pProperties
.get() : nullptr;
2209 if (!pEntry
->m_sBaseStyleIdentifier
.isEmpty())
2211 oProperty
= pStyleSheetProperties
->getProperty(PROP_PARA_FIRST_LINE_INDENT
);
2213 // If the numbering comes from a base style, indent of the base style has also priority.
2214 || (bNumberingFromBaseStyle
&& pParentProperties
&& (oProperty
= pParentProperties
->getProperty(PROP_PARA_FIRST_LINE_INDENT
))) )
2215 pParaContext
->Insert(PROP_PARA_FIRST_LINE_INDENT
, oProperty
->second
, /*bOverwrite=*/false);
2217 oProperty
= pStyleSheetProperties
->getProperty(PROP_PARA_LEFT_MARGIN
);
2219 || (bNumberingFromBaseStyle
&& pParentProperties
&& (oProperty
= pParentProperties
->getProperty(PROP_PARA_LEFT_MARGIN
))) )
2220 pParaContext
->Insert(PROP_PARA_LEFT_MARGIN
, oProperty
->second
, /*bOverwrite=*/false);
2222 // We're inheriting properties from a numbering style. Make sure a possible right margin is inherited from the base style.
2223 sal_Int32 nParaRightMargin
;
2224 if ( pParentProperties
&& (oProperty
= pParentProperties
->getProperty(PROP_PARA_RIGHT_MARGIN
)) && (nParaRightMargin
= oProperty
->second
.get
<sal_Int32
>()) != 0 )
2226 // If we're setting the right margin, we should set the first / left margin as well from the numbering style.
2227 const sal_Int32 nFirstLineIndent
= getNumberingProperty(nListId
, nListLevel
, "FirstLineIndent");
2228 const sal_Int32 nParaLeftMargin
= getNumberingProperty(nListId
, nListLevel
, "IndentAt");
2229 if (nFirstLineIndent
!= 0)
2230 pParaContext
->Insert(PROP_PARA_FIRST_LINE_INDENT
, uno::Any(nFirstLineIndent
), /*bOverwrite=*/false);
2231 if (nParaLeftMargin
!= 0)
2232 pParaContext
->Insert(PROP_PARA_LEFT_MARGIN
, uno::Any(nParaLeftMargin
), /*bOverwrite=*/false);
2234 // Override right margin value with value from current style, if any
2235 if (pStyleSheetProperties
&& pStyleSheetProperties
->isSet(PROP_PARA_RIGHT_MARGIN
))
2236 nParaRightMargin
= pStyleSheetProperties
->getProperty(PROP_PARA_RIGHT_MARGIN
)->second
.get
<sal_Int32
>();
2238 pParaContext
->Insert(PROP_PARA_RIGHT_MARGIN
, uno::Any(nParaRightMargin
), /*bOverwrite=*/false);
2241 // Paragraph style based right paragraph indentation affects not paragraph style based lists in DOCX.
2242 // Apply it as direct formatting, also left and first line indentation of numbering to keep them.
2243 else if (isNumberingViaRule
)
2245 uno::Any aRightMargin
= GetPropertyFromParaStyleSheet(PROP_PARA_RIGHT_MARGIN
);
2246 if ( aRightMargin
!= uno::Any() )
2248 pParaContext
->Insert(PROP_PARA_RIGHT_MARGIN
, aRightMargin
, /*bOverwrite=*/false);
2250 const sal_Int32 nFirstLineIndent
= getNumberingProperty(nListId
, nListLevel
, "FirstLineIndent");
2251 const sal_Int32 nParaLeftMargin
= getNumberingProperty(nListId
, nListLevel
, "IndentAt");
2252 if (nFirstLineIndent
!= 0)
2253 pParaContext
->Insert(PROP_PARA_FIRST_LINE_INDENT
, uno::Any(nFirstLineIndent
), /*bOverwrite=*/false);
2254 if (nParaLeftMargin
!= 0)
2255 pParaContext
->Insert(PROP_PARA_LEFT_MARGIN
, uno::Any(nParaLeftMargin
), /*bOverwrite=*/false);
2259 if (nListId
== 0 && !pList
)
2261 // listid = 0 and no list definition is used in DOCX to stop numbering
2262 // defined somewhere in parent styles
2263 // And here we should explicitly set left margin and first-line margin.
2264 // They can be taken from referred style, but not from styles with listid!
2265 uno::Any aProp
= lcl_GetPropertyFromParaStyleSheetNoNum(PROP_PARA_FIRST_LINE_INDENT
, pEntry
, m_pStyleSheetTable
);
2266 if (aProp
.hasValue())
2267 pParaContext
->Insert(PROP_PARA_FIRST_LINE_INDENT
, aProp
, false);
2269 pParaContext
->Insert(PROP_PARA_FIRST_LINE_INDENT
, uno::Any(sal_uInt32(0)), false);
2271 aProp
= lcl_GetPropertyFromParaStyleSheetNoNum(PROP_PARA_LEFT_MARGIN
, pEntry
, m_pStyleSheetTable
);
2272 if (aProp
.hasValue())
2273 pParaContext
->Insert(PROP_PARA_LEFT_MARGIN
, aProp
, false);
2275 pParaContext
->Insert(PROP_PARA_LEFT_MARGIN
, uno::Any(sal_uInt32(0)), false);
2279 // apply AutoSpacing: it has priority over all other margin settings
2280 // (note that numbering with autoSpacing is handled separately later on)
2281 const bool bAllowAdjustments
= !GetSettingsTable()->GetDoNotUseHTMLParagraphAutoSpacing();
2282 sal_Int32 nBeforeAutospacing
= -1;
2283 bool bIsAutoSet
= pParaContext
&& pParaContext
->isSet(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING
);
2284 const bool bNoTopmargin
= pParaContext
&& !pParaContext
->isSet(PROP_PARA_TOP_MARGIN
);
2285 // apply INHERITED autospacing only if top margin is not set
2286 if ( bIsAutoSet
|| bNoTopmargin
)
2288 GetAnyProperty(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING
, pPropertyMap
) >>= nBeforeAutospacing
;
2289 // tdf#137655 only w:beforeAutospacing=0 was specified, but not PARA_TOP_MARGIN
2290 // (see default_spacing = -1 in processing of LN_CT_Spacing_beforeAutospacing)
2291 if ( bNoTopmargin
&& nBeforeAutospacing
== ConversionHelper::convertTwipToMM100(-1) )
2293 sal_Int32 nStyleAuto
= -1;
2294 GetPropertyFromParaStyleSheet(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING
) >>= nStyleAuto
;
2296 nBeforeAutospacing
= 0;
2299 if ( nBeforeAutospacing
> -1 && pParaContext
)
2301 if (bAllowAdjustments
)
2303 if ( GetIsFirstParagraphInShape() ||
2304 (GetIsFirstParagraphInSection() && GetSectionContext() && GetSectionContext()->IsFirstSection()) ||
2305 (m_bFirstParagraphInCell
&& m_nTableDepth
> 0 && m_nTableDepth
== m_nTableCellDepth
) )
2307 // export requires grabbag to match top_margin, so keep them in sync
2308 if (nBeforeAutospacing
&& bIsAutoSet
)
2309 pParaContext
->Insert( PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING
, uno::Any( sal_Int32(0) ),true, PARA_GRAB_BAG
);
2310 nBeforeAutospacing
= 0;
2313 pParaContext
->Insert(PROP_PARA_TOP_MARGIN
, uno::Any(nBeforeAutospacing
));
2316 sal_Int32 nAfterAutospacing
= -1;
2317 bIsAutoSet
= pParaContext
&& pParaContext
->isSet(PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING
);
2318 const bool bNoBottomMargin
= pParaContext
&& !pParaContext
->isSet(PROP_PARA_BOTTOM_MARGIN
);
2319 bool bAppliedBottomAutospacing
= false;
2320 if (bIsAutoSet
|| bNoBottomMargin
)
2322 GetAnyProperty(PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING
, pPropertyMap
) >>= nAfterAutospacing
;
2323 if (bNoBottomMargin
&& nAfterAutospacing
== ConversionHelper::convertTwipToMM100(-1))
2325 sal_Int32 nStyleAuto
= -1;
2326 GetPropertyFromParaStyleSheet(PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING
) >>= nStyleAuto
;
2328 nAfterAutospacing
= 0;
2331 if ( nAfterAutospacing
> -1 && pParaContext
)
2333 pParaContext
->Insert(PROP_PARA_BOTTOM_MARGIN
, uno::Any(nAfterAutospacing
));
2334 bAppliedBottomAutospacing
= bAllowAdjustments
;
2337 // tell TableManager to reset the bottom margin if it determines that this is the cell's last paragraph.
2338 if ( hasTableManager() && getTableManager().isInCell() )
2339 getTableManager().setCellLastParaAfterAutospacing(bAppliedBottomAutospacing
);
2341 if (xTextAppend
.is() && pParaContext
&& hasTableManager() && !getTableManager().isIgnore())
2345 /*the following combinations of previous and current frame settings can occur:
2346 (1) - no old frame and no current frame -> no special action
2347 (2) - no old frame and current DropCap -> save DropCap for later use, don't call finishParagraph
2348 remove character properties of the DropCap?
2349 (3) - no old frame and current Frame -> save Frame for later use
2350 (4) - old DropCap and no current frame -> add DropCap to the properties of the finished paragraph, delete previous setting
2351 (5) - old DropCap and current frame -> add DropCap to the properties of the finished paragraph, save current frame settings
2352 (6) - old Frame and new DropCap -> add old Frame, save DropCap for later use
2353 (7) - old Frame and new same Frame -> continue
2354 (8) - old Frame and new different Frame -> add old Frame, save new Frame for later use
2355 (9) - old Frame and no current frame -> add old Frame, delete previous settings
2357 old _and_ new DropCap must not occur
2360 // The paragraph style is vital to knowing all the frame properties.
2361 std::optional
<PropertyMap::Property
> aParaStyle
2362 = pPropertyMap
->getProperty(PROP_PARA_STYLE_NAME
);
2366 aParaStyle
->second
>>= sName
;
2367 pParaContext
->props().SetParaStyleName(sName
);
2371 pParaContext
->props().IsFrameMode() &&
2372 sal::static_int_cast
<Id
>(pParaContext
->props().GetDropCap()) != NS_ooxml::LN_Value_doc_ST_DropCap_none
;
2374 style::DropCapFormat aDrop
;
2375 ParagraphPropertiesPtr pToBeSavedProperties
;
2376 bool bKeepLastParagraphProperties
= false;
2379 uno::Reference
<text::XParagraphCursor
> xParaCursor(
2380 xTextAppend
->createTextCursorByRange(xTextAppend
->getEnd()), uno::UNO_QUERY_THROW
);
2382 xParaCursor
->gotoStartOfParagraph( true );
2383 uno::Reference
< beans::XPropertyState
> xParaProperties( xParaCursor
, uno::UNO_QUERY_THROW
);
2384 xParaProperties
->setPropertyToDefault(getPropertyName(PROP_CHAR_ESCAPEMENT
));
2385 xParaProperties
->setPropertyToDefault(getPropertyName(PROP_CHAR_HEIGHT
));
2386 //handles (2) and part of (6)
2387 pToBeSavedProperties
= new ParagraphProperties(pParaContext
->props());
2388 sal_Int32 nCount
= xParaCursor
->getString().getLength();
2389 pToBeSavedProperties
->SetDropCapLength(nCount
> 0 && nCount
< 255 ? static_cast<sal_Int8
>(nCount
) : 1);
2391 if( rAppendContext
.pLastParagraphProperties
)
2393 if( sal::static_int_cast
<Id
>(rAppendContext
.pLastParagraphProperties
->GetDropCap()) != NS_ooxml::LN_Value_doc_ST_DropCap_none
)
2395 //handles (4) and part of (5)
2396 //create a DropCap property, add it to the property sequence of finishParagraph
2397 sal_Int32 nLines
= rAppendContext
.pLastParagraphProperties
->GetLines();
2398 aDrop
.Lines
= nLines
> 0 && nLines
< SAL_MAX_INT8
? static_cast<sal_Int8
>(nLines
) : 2;
2399 aDrop
.Count
= rAppendContext
.pLastParagraphProperties
->GetDropCapLength();
2400 sal_Int32 nHSpace
= rAppendContext
.pLastParagraphProperties
->GethSpace();
2401 aDrop
.Distance
= nHSpace
> 0 && nHSpace
< SAL_MAX_INT16
? static_cast<sal_Int16
>(nHSpace
) : 0;
2403 if( pParaContext
->props().IsFrameMode() )
2404 pToBeSavedProperties
= new ParagraphProperties(pParaContext
->props());
2408 const bool bIsFrameMode(pParaContext
->props().IsFrameMode());
2409 std::vector
<beans::PropertyValue
> aCurrFrameProperties
;
2410 std::vector
<beans::PropertyValue
> aPrevFrameProperties
;
2413 aCurrFrameProperties
= MakeFrameProperties(pParaContext
->props());
2414 aPrevFrameProperties
2415 = MakeFrameProperties(*rAppendContext
.pLastParagraphProperties
);
2418 if (bIsFrameMode
&& aPrevFrameProperties
== aCurrFrameProperties
)
2421 rAppendContext
.pLastParagraphProperties
->SetEndingRange(
2422 rAppendContext
.xInsertPosition
.is() ? rAppendContext
.xInsertPosition
2423 : xTextAppend
->getEnd());
2424 bKeepLastParagraphProperties
= true;
2428 // handles (8)(9) and completes (6)
2430 // RTF has an \overlap flag (which we ignore so far)
2431 // but DOCX has nothing like that for framePr
2432 // Always allow overlap in the RTF case - so there can be no regression.
2434 // In MSO UI, there is no setting for AllowOverlap for this kind of frame.
2435 // Although they CAN overlap with other anchored things,
2436 // they do not _easily_ overlap with other framePr's,
2437 // so when one frame follows another (8), don't let the first be overlapped.
2438 bool bPreventOverlap
= !IsRTFImport() && bIsFrameMode
&& !bIsDropCap
;
2440 // Preventing overlap is emulation - so deny overlap as little as possible.
2441 sal_Int16 nVertOrient
= text::VertOrientation::NONE
;
2442 sal_Int16 nVertOrientRelation
= text::RelOrientation::FRAME
;
2443 sal_Int32 nCurrVertPos
= 0;
2444 sal_Int32 nPrevVertPos
= 0;
2445 for (size_t i
= 0; bPreventOverlap
&& i
< aCurrFrameProperties
.size(); ++i
)
2447 if (aCurrFrameProperties
[i
].Name
== "VertOrientRelation")
2449 aCurrFrameProperties
[i
].Value
>>= nVertOrientRelation
;
2450 if (nVertOrientRelation
!= text::RelOrientation::FRAME
)
2451 bPreventOverlap
= false;
2453 else if (aCurrFrameProperties
[i
].Name
== "VertOrient")
2455 aCurrFrameProperties
[i
].Value
>>= nVertOrient
;
2456 if (nVertOrient
!= text::VertOrientation::NONE
)
2457 bPreventOverlap
= false;
2459 else if (aCurrFrameProperties
[i
].Name
== "VertOrientPosition")
2461 aCurrFrameProperties
[i
].Value
>>= nCurrVertPos
;
2462 // arbitrary value. Assume it must be less than 1st line height
2463 if (nCurrVertPos
> 20 || nCurrVertPos
< -20)
2464 bPreventOverlap
= false;
2467 for (size_t i
= 0; bPreventOverlap
&& i
< aPrevFrameProperties
.size(); ++i
)
2469 if (aPrevFrameProperties
[i
].Name
== "VertOrientRelation")
2471 aPrevFrameProperties
[i
].Value
>>= nVertOrientRelation
;
2472 if (nVertOrientRelation
!= text::RelOrientation::FRAME
)
2473 bPreventOverlap
= false;
2475 else if (aPrevFrameProperties
[i
].Name
== "VertOrient")
2477 aPrevFrameProperties
[i
].Value
>>= nVertOrient
;
2478 if (nVertOrient
!= text::VertOrientation::NONE
)
2479 bPreventOverlap
= false;
2481 else if (aPrevFrameProperties
[i
].Name
== "VertOrientPosition")
2483 aPrevFrameProperties
[i
].Value
>>= nPrevVertPos
;
2484 if (nPrevVertPos
!= nCurrVertPos
)
2485 bPreventOverlap
= false;
2489 CheckUnregisteredFrameConversion(bPreventOverlap
);
2491 // If different frame properties are set on this paragraph, keep them.
2492 if (!bIsDropCap
&& bIsFrameMode
)
2494 pToBeSavedProperties
= new ParagraphProperties(pParaContext
->props());
2495 lcl_AddRange(pToBeSavedProperties
, xTextAppend
, rAppendContext
);
2502 // (1) doesn't need handling
2504 if( !bIsDropCap
&& pParaContext
->props().IsFrameMode() )
2506 pToBeSavedProperties
= new ParagraphProperties(pParaContext
->props());
2507 lcl_AddRange(pToBeSavedProperties
, xTextAppend
, rAppendContext
);
2510 std::vector
<beans::PropertyValue
> aProperties
;
2513 aProperties
= comphelper::sequenceToContainer
< std::vector
<beans::PropertyValue
> >(pPropertyMap
->GetPropertyValues());
2515 // tdf#64222 filter out the "paragraph marker" formatting and
2516 // set it as a separate paragraph property, not a empty hint at
2518 std::vector
<beans::NamedValue
> charProperties
;
2519 for (auto it
= aProperties
.begin(); it
!= aProperties
.end(); )
2521 // this condition isn't ideal but as it happens all
2522 // RES_CHRATR_* have names that start with "Char"
2523 if (it
->Name
.startsWith("Char"))
2525 charProperties
.emplace_back(it
->Name
, it
->Value
);
2526 // as testN793262 demonstrates, font size in rPr must
2527 // affect the paragraph size => also insert empty hint!
2528 // it = aProperties.erase(it);
2532 if (!charProperties
.empty())
2534 aProperties
.push_back(beans::PropertyValue("ListAutoFormat",
2535 0, uno::Any(comphelper::containerToSequence(charProperties
)), beans::PropertyState_DIRECT_VALUE
));
2540 if( aDrop
.Lines
> 1 )
2542 beans::PropertyValue aValue
;
2543 aValue
.Name
= getPropertyName(PROP_DROP_CAP_FORMAT
);
2544 aValue
.Value
<<= aDrop
;
2545 aProperties
.push_back(aValue
);
2547 uno::Reference
< text::XTextRange
> xTextRange
;
2548 if (rAppendContext
.xInsertPosition
.is())
2550 xTextRange
= xTextAppend
->finishParagraphInsert( comphelper::containerToSequence(aProperties
), rAppendContext
.xInsertPosition
);
2551 rAppendContext
.xCursor
->gotoNextParagraph(false);
2552 if (rAppendContext
.pLastParagraphProperties
)
2553 rAppendContext
.pLastParagraphProperties
->SetEndingRange(xTextRange
->getEnd());
2557 uno::Reference
<text::XTextCursor
> xCursor
;
2558 if (m_bParaHadField
&& !m_bIsInComments
&& !m_xTOCMarkerCursor
.is())
2560 // Workaround to make sure char props of the field are not lost.
2561 // Not relevant for editeng-based comments.
2562 // Not relevant for fields inside a TOC field.
2563 xCursor
= xTextAppend
->getText()->createTextCursor();
2565 xCursor
->gotoEnd(false);
2566 PropertyMapPtr
pEmpty(new PropertyMap());
2567 appendTextPortion("X", pEmpty
);
2570 // Check if top / bottom margin has to be updated, now that we know the numbering status of both the previous and
2571 // the current text node.
2572 auto itNumberingRules
= std::find_if(aProperties
.begin(), aProperties
.end(), [](const beans::PropertyValue
& rValue
)
2574 return rValue
.Name
== "NumberingRules";
2577 assert( isNumberingViaRule
== (itNumberingRules
!= aProperties
.end()) );
2578 isNumberingViaRule
= (itNumberingRules
!= aProperties
.end());
2579 if (m_xPreviousParagraph
.is() && (isNumberingViaRule
|| isNumberingViaStyle
))
2581 // This textnode has numbering. Look up the numbering style name of the current and previous paragraph.
2582 OUString aCurrentNumberingName
;
2583 OUString aPreviousNumberingName
;
2584 if (isNumberingViaRule
)
2586 assert(itNumberingRules
!= aProperties
.end() && "by definition itNumberingRules is valid if isNumberingViaRule is true");
2587 uno::Reference
<container::XNamed
> xCurrentNumberingRules(itNumberingRules
->Value
, uno::UNO_QUERY
);
2588 if (xCurrentNumberingRules
.is())
2589 aCurrentNumberingName
= xCurrentNumberingRules
->getName();
2592 uno::Reference
<container::XNamed
> xPreviousNumberingRules(m_xPreviousParagraph
->getPropertyValue("NumberingRules"), uno::UNO_QUERY_THROW
);
2593 aPreviousNumberingName
= xPreviousNumberingRules
->getName();
2595 catch (const uno::Exception
&)
2597 TOOLS_WARN_EXCEPTION("writerfilter", "DomainMapper_Impl::finishParagraph NumberingRules");
2600 else if ( m_xPreviousParagraph
->getPropertySetInfo()->hasPropertyByName("NumberingStyleName") &&
2601 // don't update before tables
2602 (m_nTableDepth
== 0 || !m_bFirstParagraphInCell
))
2604 aCurrentNumberingName
= GetListStyleName(nListId
);
2605 m_xPreviousParagraph
->getPropertyValue("NumberingStyleName") >>= aPreviousNumberingName
;
2608 // tdf#133363: remove extra auto space even for mixed list styles
2609 if (!aPreviousNumberingName
.isEmpty()
2610 && (aCurrentNumberingName
== aPreviousNumberingName
2611 || !isNumberingViaRule
))
2613 uno::Sequence
<beans::PropertyValue
> aPrevPropertiesSeq
;
2614 m_xPreviousParagraph
->getPropertyValue("ParaInteropGrabBag") >>= aPrevPropertiesSeq
;
2615 const auto & rPrevProperties
= aPrevPropertiesSeq
;
2616 bool bParaAutoBefore
= m_bParaAutoBefore
|| std::any_of(rPrevProperties
.begin(), rPrevProperties
.end(), [](const beans::PropertyValue
& rValue
)
2618 return rValue
.Name
== "ParaTopMarginBeforeAutoSpacing";
2620 // if style based spacing was set to auto in the previous paragraph, style of the actual paragraph must be the same
2621 if (bParaAutoBefore
&& !m_bParaAutoBefore
&& m_xPreviousParagraph
->getPropertySetInfo()->hasPropertyByName("ParaStyleName"))
2623 auto itParaStyle
= std::find_if(aProperties
.begin(), aProperties
.end(), [](const beans::PropertyValue
& rValue
)
2625 return rValue
.Name
== "ParaStyleName";
2627 bParaAutoBefore
= itParaStyle
!= aProperties
.end() &&
2628 m_xPreviousParagraph
->getPropertyValue("ParaStyleName") == itParaStyle
->Value
;
2630 // There was a previous textnode and it had the same numbering.
2631 if (bParaAutoBefore
)
2633 // This before spacing is set to auto, set before space to 0.
2634 auto itParaTopMargin
= std::find_if(aProperties
.begin(), aProperties
.end(), [](const beans::PropertyValue
& rValue
)
2636 return rValue
.Name
== "ParaTopMargin";
2638 if (itParaTopMargin
!= aProperties
.end())
2639 itParaTopMargin
->Value
<<= static_cast<sal_Int32
>(0);
2641 aProperties
.push_back(comphelper::makePropertyValue("ParaTopMargin", static_cast<sal_Int32
>(0)));
2644 bool bPrevParaAutoAfter
= std::any_of(rPrevProperties
.begin(), rPrevProperties
.end(), [](const beans::PropertyValue
& rValue
)
2646 return rValue
.Name
== "ParaBottomMarginAfterAutoSpacing";
2648 if (bPrevParaAutoAfter
)
2650 // Previous after spacing is set to auto, set previous after space to 0.
2651 m_xPreviousParagraph
->setPropertyValue("ParaBottomMargin", uno::Any(static_cast<sal_Int32
>(0)));
2656 // apply redlines for inline images
2657 if (IsParaWithInlineObject())
2659 for (const auto& rAnchored
: rAppendContext
.m_aAnchoredObjects
)
2661 // process only inline objects with redlining
2662 if (!rAnchored
.m_xRedlineForInline
)
2665 // select the inline image and set its redline
2666 auto xAnchorRange
= rAnchored
.m_xAnchoredObject
->getAnchor();
2667 uno::Reference
< text::XTextCursor
> xCursorOnImage
=
2668 xAnchorRange
->getText()->createTextCursorByRange(xAnchorRange
);
2669 xCursorOnImage
->goRight(1, true);
2670 CreateRedline( xCursorOnImage
, rAnchored
.m_xRedlineForInline
);
2674 xTextRange
= xTextAppend
->finishParagraph( comphelper::containerToSequence(aProperties
) );
2675 m_xPreviousParagraph
.set(xTextRange
, uno::UNO_QUERY
);
2677 if (m_xPreviousParagraph
.is() && // null for SvxUnoTextBase
2678 (isNumberingViaStyle
|| isNumberingViaRule
))
2680 assert(pParaContext
);
2681 if (ListDef::Pointer
const& pList
= m_pListTable
->GetList(nListId
))
2682 { // styles could refer to non-existing lists...
2683 AbstractListDef::Pointer
const& pAbsList
=
2684 pList
->GetAbstractDefinition();
2686 // SvxUnoTextRange doesn't have ListId
2687 m_xPreviousParagraph
->getPropertySetInfo()->hasPropertyByName("ListId"))
2690 m_xPreviousParagraph
->getPropertyValue("ListId") >>= paraId
;
2691 if (!paraId
.isEmpty()) // must be on some list?
2693 OUString
const listId
= pAbsList
->MapListId(paraId
);
2694 if (listId
!= paraId
)
2696 m_xPreviousParagraph
->setPropertyValue("ListId", uno::Any(listId
));
2701 sal_Int16 nCurrentLevel
= GetListLevel(pEntry
, pPropertyMap
);
2702 if (nCurrentLevel
== -1)
2705 const ListLevel::Pointer pListLevel
= pList
->GetLevel(nCurrentLevel
);
2708 sal_Int16 nOverrideLevel
= pListLevel
->GetStartOverride();
2709 if (nOverrideLevel
!= -1 && m_aListOverrideApplied
.find(nListId
) == m_aListOverrideApplied
.end())
2711 // Apply override: we have override instruction for this level
2712 // And this was not done for this list before: we can do this only once on first occurrence
2713 // of list with override
2714 // TODO: Not tested variant with different levels override in different lists.
2715 // Probably m_aListOverrideApplied as a set of overridden listids is not sufficient
2716 // and we need to register level overrides separately.
2717 m_xPreviousParagraph
->setPropertyValue("ParaIsNumberingRestart", uno::Any(true));
2718 m_xPreviousParagraph
->setPropertyValue("NumberingStartValue", uno::Any(nOverrideLevel
));
2719 m_aListOverrideApplied
.insert(nListId
);
2725 if (!rAppendContext
.m_aAnchoredObjects
.empty() && !IsInHeaderFooter())
2727 // Remember what objects are anchored to this paragraph.
2728 // That list is only used for Word compat purposes, and
2729 // it is only relevant for body text.
2730 AnchoredObjectsInfo aInfo
;
2731 aInfo
.m_xParagraph
= xTextRange
;
2732 aInfo
.m_aAnchoredObjects
= rAppendContext
.m_aAnchoredObjects
;
2733 m_aAnchoredObjectAnchors
.push_back(aInfo
);
2734 rAppendContext
.m_aAnchoredObjects
.clear();
2739 xCursor
->goLeft(1, true);
2740 xCursor
->setString(OUString());
2743 getTableManager( ).handle(xTextRange
);
2744 m_aSmartTagHandler
.handle(xTextRange
);
2746 if (xTextRange
.is())
2748 // Get the end of paragraph character inserted
2749 uno::Reference
< text::XTextCursor
> xCur
= xTextRange
->getText( )->createTextCursor( );
2750 if (rAppendContext
.xInsertPosition
.is())
2751 xCur
->gotoRange( rAppendContext
.xInsertPosition
, false );
2753 xCur
->gotoEnd( false );
2755 // tdf#77417 trim right white spaces in table cells in 2010 compatibility mode
2756 sal_Int32 nMode
= GetSettingsTable()->GetWordCompatibilityMode();
2757 if ( m_nTableDepth
> 0 && nMode
> 0 && nMode
<= 14 )
2760 xCur
->goLeft(1, false);
2761 while ( xCur
->goLeft(1, true) )
2763 OUString sChar
= xCur
->getString();
2764 if ( sChar
== " " || sChar
== "\t" || sChar
== OUStringChar(u
'\x00A0') )
2765 xCur
->setString("");
2770 if (rAppendContext
.xInsertPosition
.is())
2771 xCur
->gotoRange(rAppendContext
.xInsertPosition
, false);
2773 xCur
->gotoEnd(false);
2776 xCur
->goLeft( 1 , true );
2777 // Extend the redline ranges for empty paragraphs
2778 if ( !m_bParaChanged
&& m_previousRedline
)
2779 CreateRedline( xCur
, m_previousRedline
);
2780 CheckParaMarkerRedline( xCur
);
2783 css::uno::Reference
<css::beans::XPropertySet
> xParaProps(xTextRange
, uno::UNO_QUERY
);
2785 // table style precedence and not hidden shapes anchored to hidden empty table paragraphs
2786 if (xParaProps
&& !m_bIsInComments
2787 && (m_nTableDepth
> 0 || !m_aAnchoredObjectAnchors
.empty()))
2789 // table style has got bigger precedence than docDefault style
2790 // collect these pending paragraph properties to process in endTable()
2791 uno::Reference
<text::XTextCursor
> xCur
= xTextRange
->getText( )->createTextCursor( );
2792 xCur
->gotoEnd(false);
2793 xCur
->goLeft(1, false);
2794 uno::Reference
<text::XTextCursor
> xCur2
= xTextRange
->getText()->createTextCursorByRange(xCur
);
2795 uno::Reference
<text::XParagraphCursor
> xParaCursor(xCur2
, uno::UNO_QUERY_THROW
);
2796 xParaCursor
->gotoStartOfParagraph(false);
2797 if (m_nTableDepth
> 0)
2799 TableParagraph aPending
{xParaCursor
, xCur
, pParaContext
, xParaProps
};
2800 getTableManager().getCurrentParagraphs()->push_back(aPending
);
2803 // hidden empty paragraph with a not hidden shape, set as not hidden
2804 std::optional
<PropertyMap::Property
> pHidden
;
2805 if ( !m_aAnchoredObjectAnchors
.empty() && (pHidden
= pParaContext
->getProperty(PROP_CHAR_HIDDEN
)) )
2807 bool bIsHidden
= {}; // -Werror=maybe-uninitialized
2808 pHidden
->second
>>= bIsHidden
;
2812 pHidden
= GetTopContext()->getProperty(PROP_CHAR_HIDDEN
);
2814 pHidden
->second
>>= bIsHidden
;
2817 uno::Reference
<text::XTextCursor
> xCur3
= xTextRange
->getText()->createTextCursorByRange(xParaCursor
);
2818 xCur3
->goRight(1, true);
2819 if (xCur3
->getString() == SAL_NEWLINE_STRING
)
2821 uno::Reference
< beans::XPropertySet
> xProp( xCur3
, uno::UNO_QUERY
);
2822 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_HIDDEN
), uno::Any(false));
2829 // tdf#118521 set paragraph top or bottom margin based on the paragraph style
2830 // if we already set the other margin with direct formatting
2833 const bool bTopSet
= pParaContext
->isSet(PROP_PARA_TOP_MARGIN
);
2834 const bool bBottomSet
= pParaContext
->isSet(PROP_PARA_BOTTOM_MARGIN
);
2835 const bool bContextSet
= pParaContext
->isSet(PROP_PARA_CONTEXT_MARGIN
);
2836 if ( bTopSet
!= bBottomSet
|| bBottomSet
!= bContextSet
)
2841 uno::Any aMargin
= GetPropertyFromParaStyleSheet(PROP_PARA_TOP_MARGIN
);
2842 if ( aMargin
!= uno::Any() )
2843 xParaProps
->setPropertyValue("ParaTopMargin", aMargin
);
2847 uno::Any aMargin
= GetPropertyFromParaStyleSheet(PROP_PARA_BOTTOM_MARGIN
);
2848 if ( aMargin
!= uno::Any() )
2849 xParaProps
->setPropertyValue("ParaBottomMargin", aMargin
);
2853 uno::Any aMargin
= GetPropertyFromParaStyleSheet(PROP_PARA_CONTEXT_MARGIN
);
2854 if ( aMargin
!= uno::Any() )
2855 xParaProps
->setPropertyValue("ParaContextMargin", aMargin
);
2860 // Left, Right, and Hanging settings are also grouped. Ensure that all or none are set.
2863 const bool bLeftSet
= pParaContext
->isSet(PROP_PARA_LEFT_MARGIN
);
2864 const bool bRightSet
= pParaContext
->isSet(PROP_PARA_RIGHT_MARGIN
);
2865 const bool bFirstSet
= pParaContext
->isSet(PROP_PARA_FIRST_LINE_INDENT
);
2866 if (bLeftSet
!= bRightSet
|| bRightSet
!= bFirstSet
)
2870 uno::Any aMargin
= GetPropertyFromParaStyleSheet(PROP_PARA_LEFT_MARGIN
);
2871 if ( aMargin
!= uno::Any() )
2872 xParaProps
->setPropertyValue("ParaLeftMargin", aMargin
);
2873 else if (isNumberingViaStyle
)
2875 const sal_Int32 nParaLeftMargin
= getNumberingProperty(nListId
, GetListLevel(pEntry
, pPropertyMap
), "IndentAt");
2876 if (nParaLeftMargin
!= 0)
2877 xParaProps
->setPropertyValue("ParaLeftMargin", uno::Any(nParaLeftMargin
));
2882 uno::Any aMargin
= GetPropertyFromParaStyleSheet(PROP_PARA_RIGHT_MARGIN
);
2883 if ( aMargin
!= uno::Any() )
2884 xParaProps
->setPropertyValue("ParaRightMargin", aMargin
);
2888 uno::Any aMargin
= GetPropertyFromParaStyleSheet(PROP_PARA_FIRST_LINE_INDENT
);
2889 if ( aMargin
!= uno::Any() )
2890 xParaProps
->setPropertyValue("ParaFirstLineIndent", aMargin
);
2891 else if (isNumberingViaStyle
)
2893 const sal_Int32 nFirstLineIndent
= getNumberingProperty(nListId
, GetListLevel(pEntry
, pPropertyMap
), "FirstLineIndent");
2894 if (nFirstLineIndent
!= 0)
2895 xParaProps
->setPropertyValue("ParaFirstLineIndent", uno::Any(nFirstLineIndent
));
2901 if( !bKeepLastParagraphProperties
)
2902 rAppendContext
.pLastParagraphProperties
= pToBeSavedProperties
;
2904 catch(const lang::IllegalArgumentException
&)
2906 TOOLS_WARN_EXCEPTION( "writerfilter", "DomainMapper_Impl::finishParagraph" );
2908 catch(const uno::Exception
&)
2910 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "finishParagraph()" );
2915 bool bIgnoreFrameState
= IsInHeaderFooter();
2916 if( (!bIgnoreFrameState
&& pParaContext
&& pParaContext
->props().IsFrameMode()) || (bIgnoreFrameState
&& GetIsPreviousParagraphFramed()) )
2917 SetIsPreviousParagraphFramed(true);
2919 SetIsPreviousParagraphFramed(false);
2921 m_bRemoveThisParagraph
= false;
2922 if( !IsInHeaderFooter() && !IsInShape()
2923 && (!pParaContext
|| !pParaContext
->props().IsFrameMode()) )
2924 { // If the paragraph is in a frame, shape or header/footer, it's not a paragraph of the section itself.
2925 SetIsFirstParagraphInSection(false);
2926 // don't count an empty deleted paragraph as first paragraph in section to avoid of
2927 // the deletion of the next empty paragraph later, resulting loss of the associated page break
2928 if (!m_previousRedline
|| m_bParaChanged
)
2930 SetIsFirstParagraphInSectionAfterRedline(false);
2931 SetIsLastParagraphInSection(false);
2934 m_previousRedline
.clear();
2935 m_bParaChanged
= false;
2937 if (m_bIsInComments
&& pParaContext
)
2939 if (const OUString sParaId
= pParaContext
->props().GetParaId(); !sParaId
.isEmpty())
2941 if (const auto& item
= m_aCommentProps
.find(sParaId
); item
!= m_aCommentProps
.end())
2943 m_bAnnotationResolved
= item
->second
.bDone
;
2944 m_sAnnotationParent
= item
->second
.sParaIdParent
;
2946 m_sAnnotationImportedParaId
= sParaId
;
2950 if (m_bIsFirstParaInShape
)
2951 m_bIsFirstParaInShape
= false;
2955 // Reset the frame properties for the next paragraph
2956 pParaContext
->props().ResetFrameProperties();
2959 SetIsOutsideAParagraph(true);
2960 m_bParaHadField
= false;
2962 // don't overwrite m_bFirstParagraphInCell in table separator nodes
2963 // and in text boxes anchored to the first paragraph of table cells
2964 if (m_nTableDepth
> 0 && m_nTableDepth
== m_nTableCellDepth
&& !IsInShape() && !m_bIsInComments
)
2965 m_bFirstParagraphInCell
= false;
2967 m_bParaAutoBefore
= false;
2968 m_bParaWithInlineObject
= false;
2971 TagLogger::getInstance().endElement();
2976 void DomainMapper_Impl::appendTextPortion( const OUString
& rString
, const PropertyMapPtr
& pPropertyMap
)
2978 if (m_bDiscardHeaderFooter
)
2981 if (m_aTextAppendStack
.empty())
2983 // Before placing call to processDeferredCharacterProperties(), TopContextType should be CONTEXT_CHARACTER
2984 // processDeferredCharacterProperties() invokes only if character inserted
2985 if( pPropertyMap
== m_pTopContext
&& !m_deferredCharacterProperties
.empty() && (GetTopContextType() == CONTEXT_CHARACTER
) )
2986 processDeferredCharacterProperties();
2987 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
2988 if (!xTextAppend
.is() || !hasTableManager() || getTableManager().isIgnore())
2993 // If we are in comments, then disable CharGrabBag, comment text doesn't support that.
2994 uno::Sequence
< beans::PropertyValue
> aValues
= pPropertyMap
->GetPropertyValues(/*bCharGrabBag=*/!m_bIsInComments
);
2996 if (IsInTOC() || m_bStartIndex
|| m_bStartBibliography
)
2997 for( auto& rValue
: asNonConstRange(aValues
) )
2999 if (rValue
.Name
== "CharHidden")
3000 rValue
.Value
<<= false;
3003 // remove workaround for change tracked images, if they are part of a redline,
3004 // i.e. if the next run is a tracked change with the same type, author and date,
3005 // as in the change tracking of the image.
3006 if ( m_bRedlineImageInPreviousRun
)
3008 auto pCurrentRedline
= m_aRedlines
.top().size() > 0
3009 ? m_aRedlines
.top().back()
3010 : GetTopContextOfType(CONTEXT_CHARACTER
) &&
3011 GetTopContextOfType(CONTEXT_CHARACTER
)->Redlines().size() > 0
3012 ? GetTopContextOfType(CONTEXT_CHARACTER
)->Redlines().back()
3014 if ( m_previousRedline
&& pCurrentRedline
&&
3016 (m_previousRedline
->m_nToken
& 0xffff) == (pCurrentRedline
->m_nToken
& 0xffff) &&
3017 m_previousRedline
->m_sAuthor
== pCurrentRedline
->m_sAuthor
&&
3018 m_previousRedline
->m_sDate
== pCurrentRedline
->m_sDate
)
3020 uno::Reference
< text::XTextCursor
> xCursor
= xTextAppend
->getEnd()->getText( )->createTextCursor( );
3021 assert(xCursor
.is());
3022 xCursor
->gotoEnd(false);
3023 xCursor
->goLeft(2, true);
3024 if ( xCursor
->getString() == u
"" )
3026 xCursor
->goRight(1, true);
3027 xCursor
->setString("");
3028 xCursor
->gotoEnd(false);
3029 xCursor
->goLeft(1, true);
3030 xCursor
->setString("");
3034 m_bRedlineImageInPreviousRun
= false;
3037 uno::Reference
< text::XTextRange
> xTextRange
;
3038 if (m_aTextAppendStack
.top().xInsertPosition
.is())
3040 xTextRange
= xTextAppend
->insertTextPortion(rString
, aValues
, m_aTextAppendStack
.top().xInsertPosition
);
3041 m_aTextAppendStack
.top().xCursor
->gotoRange(xTextRange
->getEnd(), true);
3045 if (m_bStartTOC
|| m_bStartIndex
|| m_bStartBibliography
|| m_nStartGenericField
!= 0)
3047 if (IsInHeaderFooter() && !m_bStartTOCHeaderFooter
)
3049 xTextRange
= xTextAppend
->appendTextPortion(rString
, aValues
);
3053 m_bStartedTOC
= true;
3054 uno::Reference
< text::XTextCursor
> xTOCTextCursor
= xTextAppend
->getEnd()->getText( )->createTextCursor( );
3055 assert(xTOCTextCursor
.is());
3056 xTOCTextCursor
->gotoEnd(false);
3057 if (m_nStartGenericField
!= 0)
3059 xTOCTextCursor
->goLeft(1, false);
3061 xTextRange
= xTextAppend
->insertTextPortion(rString
, aValues
, xTOCTextCursor
);
3062 SAL_WARN_IF(!xTextRange
.is(), "writerfilter.dmapper", "insertTextPortion failed");
3063 if (!xTextRange
.is())
3064 throw uno::Exception("insertTextPortion failed", nullptr);
3065 m_bTextInserted
= true;
3066 xTOCTextCursor
->gotoRange(xTextRange
->getEnd(), true);
3067 if (m_nStartGenericField
== 0)
3069 m_aTextAppendStack
.push(TextAppendContext(xTextAppend
, xTOCTextCursor
));
3075 #if !defined(MACOSX) // TODO: check layout differences and support all platforms, if needed
3078 OUString
sDoubleSpace(" ");
3079 PropertyMapPtr pContext
= GetTopContextOfType(CONTEXT_CHARACTER
);
3080 // tdf#123703 workaround for longer space sequences of the old or compatible RTF documents
3081 if (GetSettingsTable()->GetLongerSpaceSequence() && !IsOpenFieldCommand() && (nPos
= rString
.indexOf(sDoubleSpace
)) != -1 &&
3082 // monospaced fonts have no longer space sequences, regardless of \fprq2 (not monospaced) font setting
3083 // fix for the base monospaced font Courier
3084 (!pContext
|| !pContext
->isSet(PROP_CHAR_FONT_NAME
) ||
3085 ((pContext
->getProperty(PROP_CHAR_FONT_NAME
)->second
>>= sFontName
) && sFontName
.indexOf("Courier") == -1)))
3087 // an RTF space character is longer by an extra six-em-space in an old-style RTF space sequence,
3088 // insert them to keep RTF document layout formatted by consecutive spaces
3089 const sal_Unicode aExtraSpace
[5] = { 0x2006, 0x20, 0x2006, 0x20, 0 };
3090 const sal_Unicode aExtraSpace2
[4] = { 0x20, 0x2006, 0x20, 0 };
3091 xTextRange
= xTextAppend
->appendTextPortion(rString
.replaceAll(sDoubleSpace
, aExtraSpace
, nPos
)
3092 .replaceAll(sDoubleSpace
, aExtraSpace2
, nPos
), aValues
);
3096 xTextRange
= xTextAppend
->appendTextPortion(rString
, aValues
);
3100 // reset moveFrom/moveTo data of non-terminating runs of the paragraph
3101 if ( m_pParaMarkerRedlineMove
)
3103 m_pParaMarkerRedlineMove
.clear();
3105 CheckRedline( xTextRange
);
3106 m_bParaChanged
= true;
3108 //getTableManager( ).handle(xTextRange);
3110 catch(const lang::IllegalArgumentException
&)
3112 TOOLS_WARN_EXCEPTION( "writerfilter", "DomainMapper_Impl::appendTextPortion" );
3114 catch(const uno::Exception
&)
3116 TOOLS_WARN_EXCEPTION( "writerfilter", "DomainMapper_Impl::appendTextPortion" );
3120 void DomainMapper_Impl::appendTextContent(
3121 const uno::Reference
< text::XTextContent
>& xContent
,
3122 const uno::Sequence
< beans::PropertyValue
>& xPropertyValues
3125 SAL_WARN_IF(m_aTextAppendStack
.empty(), "writerfilter.dmapper", "no text append stack");
3126 if (m_aTextAppendStack
.empty())
3128 uno::Reference
< text::XTextAppendAndConvert
> xTextAppendAndConvert( m_aTextAppendStack
.top().xTextAppend
, uno::UNO_QUERY
);
3129 OSL_ENSURE( xTextAppendAndConvert
.is(), "trying to append a text content without XTextAppendAndConvert" );
3130 if (!xTextAppendAndConvert
.is() || !hasTableManager() || getTableManager().isIgnore())
3135 if (m_aTextAppendStack
.top().xInsertPosition
.is())
3136 xTextAppendAndConvert
->insertTextContentWithProperties( xContent
, xPropertyValues
, m_aTextAppendStack
.top().xInsertPosition
);
3138 xTextAppendAndConvert
->appendTextContent( xContent
, xPropertyValues
);
3140 catch(const lang::IllegalArgumentException
&)
3143 catch(const uno::Exception
&)
3148 void DomainMapper_Impl::appendOLE( const OUString
& rStreamName
, const std::shared_ptr
<OLEHandler
>& pOLEHandler
)
3152 uno::Reference
< text::XTextContent
> xOLE( m_xTextFactory
->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW
);
3153 uno::Reference
< beans::XPropertySet
> xOLEProperties(xOLE
, uno::UNO_QUERY_THROW
);
3155 OUString aCLSID
= pOLEHandler
->getCLSID();
3156 if (aCLSID
.isEmpty())
3157 xOLEProperties
->setPropertyValue(getPropertyName( PROP_STREAM_NAME
),
3158 uno::Any( rStreamName
));
3160 xOLEProperties
->setPropertyValue("CLSID", uno::Any(aCLSID
));
3162 OUString aDrawAspect
= pOLEHandler
->GetDrawAspect();
3163 if(!aDrawAspect
.isEmpty())
3164 xOLEProperties
->setPropertyValue("DrawAspect", uno::Any(aDrawAspect
));
3166 awt::Size aSize
= pOLEHandler
->getSize();
3170 aSize
.Height
= 1000;
3171 xOLEProperties
->setPropertyValue(getPropertyName( PROP_WIDTH
),
3172 uno::Any(aSize
.Width
));
3173 xOLEProperties
->setPropertyValue(getPropertyName( PROP_HEIGHT
),
3174 uno::Any(aSize
.Height
));
3176 OUString aVisAreaWidth
= pOLEHandler
->GetVisAreaWidth();
3177 if(!aVisAreaWidth
.isEmpty())
3178 xOLEProperties
->setPropertyValue("VisibleAreaWidth", uno::Any(aVisAreaWidth
));
3180 OUString aVisAreaHeight
= pOLEHandler
->GetVisAreaHeight();
3181 if(!aVisAreaHeight
.isEmpty())
3182 xOLEProperties
->setPropertyValue("VisibleAreaHeight", uno::Any(aVisAreaHeight
));
3184 uno::Reference
< graphic::XGraphic
> xGraphic
= pOLEHandler
->getReplacement();
3185 xOLEProperties
->setPropertyValue(getPropertyName( PROP_GRAPHIC
),
3186 uno::Any(xGraphic
));
3187 uno::Reference
<beans::XPropertySet
> xReplacementProperties(pOLEHandler
->getShape(), uno::UNO_QUERY
);
3188 if (xReplacementProperties
.is())
3190 table::BorderLine2 aBorderProps
;
3191 xReplacementProperties
->getPropertyValue("LineColor") >>= aBorderProps
.Color
;
3192 xReplacementProperties
->getPropertyValue("LineWidth") >>= aBorderProps
.LineWidth
;
3193 xReplacementProperties
->getPropertyValue("LineStyle") >>= aBorderProps
.LineStyle
;
3195 if (aBorderProps
.LineStyle
) // Set line props only if LineStyle is set
3197 xOLEProperties
->setPropertyValue("RightBorder", uno::Any(aBorderProps
));
3198 xOLEProperties
->setPropertyValue("TopBorder", uno::Any(aBorderProps
));
3199 xOLEProperties
->setPropertyValue("LeftBorder", uno::Any(aBorderProps
));
3200 xOLEProperties
->setPropertyValue("BottomBorder", uno::Any(aBorderProps
));
3202 OUString pProperties
[] = {
3207 "HoriOrientPosition",
3209 "VertOrientPosition",
3210 "VertOrientRelation",
3211 "HoriOrientRelation",
3217 for (const OUString
& s
: pProperties
)
3219 const uno::Any aVal
= xReplacementProperties
->getPropertyValue(s
);
3220 xOLEProperties
->setPropertyValue(s
, aVal
);
3223 if (xReplacementProperties
->getPropertyValue("FillStyle").get
<css::drawing::FillStyle
>()
3224 != css::drawing::FillStyle::FillStyle_NONE
) // Apply fill props if style is set
3226 xOLEProperties
->setPropertyValue(
3227 "FillStyle", xReplacementProperties
->getPropertyValue("FillStyle"));
3228 xOLEProperties
->setPropertyValue(
3229 "FillColor", xReplacementProperties
->getPropertyValue("FillColor"));
3230 xOLEProperties
->setPropertyValue(
3231 "FillColor2", xReplacementProperties
->getPropertyValue("FillColor2"));
3235 // mimic the treatment of graphics here... it seems anchoring as character
3236 // gives a better ( visually ) result
3237 xOLEProperties
->setPropertyValue(getPropertyName( PROP_ANCHOR_TYPE
), uno::Any( text::TextContentAnchorType_AS_CHARACTER
) );
3238 // remove ( if valid ) associated shape ( used for graphic replacement )
3239 SAL_WARN_IF(m_aAnchoredStack
.empty(), "writerfilter.dmapper", "no anchor stack");
3240 if (!m_aAnchoredStack
.empty())
3241 m_aAnchoredStack
.top( ).bToRemove
= true;
3242 RemoveLastParagraph();
3243 SAL_WARN_IF(m_aTextAppendStack
.empty(), "writerfilter.dmapper", "no text stack");
3244 if (!m_aTextAppendStack
.empty())
3245 m_aTextAppendStack
.pop();
3247 appendTextContent( xOLE
, uno::Sequence
< beans::PropertyValue
>() );
3249 if (!aCLSID
.isEmpty())
3250 pOLEHandler
->importStream(m_xComponentContext
, GetTextDocument(), xOLE
);
3253 catch( const uno::Exception
& )
3255 TOOLS_WARN_EXCEPTION( "writerfilter", "in creation of OLE object" );
3260 void DomainMapper_Impl::appendStarMath( const Value
& val
)
3262 uno::Reference
< embed::XEmbeddedObject
> formula
;
3263 val
.getAny() >>= formula
;
3269 uno::Reference
< text::XTextContent
> xStarMath( m_xTextFactory
->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW
);
3270 uno::Reference
< beans::XPropertySet
> xStarMathProperties(xStarMath
, uno::UNO_QUERY_THROW
);
3272 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_EMBEDDED_OBJECT
),
3274 // tdf#66405: set zero margins for embedded object
3275 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN
),
3276 uno::Any(sal_Int32(0)));
3277 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN
),
3278 uno::Any(sal_Int32(0)));
3279 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_TOP_MARGIN
),
3280 uno::Any(sal_Int32(0)));
3281 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN
),
3282 uno::Any(sal_Int32(0)));
3284 uno::Reference
< uno::XInterface
> xInterface( formula
->getComponent(), uno::UNO_QUERY
);
3285 // set zero margins for object's component
3286 uno::Reference
< beans::XPropertySet
> xComponentProperties( xInterface
, uno::UNO_QUERY_THROW
);
3287 xComponentProperties
->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN
),
3288 uno::Any(sal_Int32(0)));
3289 xComponentProperties
->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN
),
3290 uno::Any(sal_Int32(0)));
3291 xComponentProperties
->setPropertyValue(getPropertyName( PROP_TOP_MARGIN
),
3292 uno::Any(sal_Int32(0)));
3293 xComponentProperties
->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN
),
3294 uno::Any(sal_Int32(0)));
3295 Size
size( 1000, 1000 );
3296 if( oox::FormulaImExportBase
* formulaimport
= dynamic_cast< oox::FormulaImExportBase
* >( xInterface
.get()))
3297 size
= formulaimport
->getFormulaSize();
3298 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_WIDTH
),
3299 uno::Any( sal_Int32(size
.Width())));
3300 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_HEIGHT
),
3301 uno::Any( sal_Int32(size
.Height())));
3302 xStarMathProperties
->setPropertyValue(getPropertyName(PROP_ANCHOR_TYPE
),
3303 uno::Any(text::TextContentAnchorType_AS_CHARACTER
));
3304 // mimic the treatment of graphics here... it seems anchoring as character
3305 // gives a better ( visually ) result
3306 appendTextContent(xStarMath
, uno::Sequence
<beans::PropertyValue
>());
3308 catch( const uno::Exception
& )
3310 TOOLS_WARN_EXCEPTION( "writerfilter", "in creation of StarMath object" );
3314 void DomainMapper_Impl::adjustLastPara(sal_Int8 nAlign
)
3316 PropertyMapPtr pLastPara
= GetTopContextOfType(dmapper::CONTEXT_PARAGRAPH
);
3317 pLastPara
->Insert(PROP_PARA_ADJUST
, uno::Any(nAlign
), true);
3320 uno::Reference
< beans::XPropertySet
> DomainMapper_Impl::appendTextSectionAfter(
3321 uno::Reference
< text::XTextRange
> const & xBefore
)
3323 uno::Reference
< beans::XPropertySet
> xRet
;
3324 if (m_aTextAppendStack
.empty())
3326 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
3327 if(xTextAppend
.is())
3331 uno::Reference
< text::XParagraphCursor
> xCursor(
3332 xTextAppend
->createTextCursorByRange( xBefore
), uno::UNO_QUERY_THROW
);
3333 //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
3334 xCursor
->gotoStartOfParagraph( false );
3335 if (m_aTextAppendStack
.top().xInsertPosition
.is())
3336 xCursor
->gotoRange( m_aTextAppendStack
.top().xInsertPosition
, true );
3338 xCursor
->gotoEnd( true );
3339 //the paragraph after this new section is already inserted
3340 xCursor
->goLeft(1, true);
3341 css::uno::Reference
<css::text::XTextRange
> xTextRange(xCursor
, css::uno::UNO_QUERY_THROW
);
3343 if (css::uno::Reference
<css::text::XDocumentIndexesSupplier
> xIndexSupplier
{
3344 GetTextDocument(), css::uno::UNO_QUERY
})
3346 css::uno::Reference
<css::text::XTextRangeCompare
> xCompare(
3347 xTextAppend
, css::uno::UNO_QUERY
);
3348 const auto xIndexAccess
= xIndexSupplier
->getDocumentIndexes();
3349 for (sal_Int32 i
= xIndexAccess
->getCount(); i
> 0; --i
)
3351 if (css::uno::Reference
<css::text::XDocumentIndex
> xIndex
{
3352 xIndexAccess
->getByIndex(i
- 1), css::uno::UNO_QUERY
})
3354 const auto xIndexTextRange
= xIndex
->getAnchor();
3355 if (xCompare
->compareRegionStarts(xTextRange
, xIndexTextRange
) == 0
3356 && xCompare
->compareRegionEnds(xTextRange
, xIndexTextRange
) == 0)
3358 // The boundaries coincide with an index: trying to attach a section
3359 // to the range will insert the section inside the index. goRight will
3360 // extend the range outside of the index, so that created section will
3361 // be around it. Alternatively we could return index section itself
3362 // instead : xRet.set(xIndex, uno::UNO_QUERY) - to set its properties,
3363 // like columns/fill.
3364 xCursor
->goRight(1, true);
3371 uno::Reference
< text::XTextContent
> xSection( m_xTextFactory
->createInstance("com.sun.star.text.TextSection"), uno::UNO_QUERY_THROW
);
3372 xSection
->attach( xTextRange
);
3373 xRet
.set(xSection
, uno::UNO_QUERY
);
3375 catch(const uno::Exception
&)
3384 void DomainMapper_Impl::appendGlossaryEntry()
3386 appendTextSectionAfter(m_xGlossaryEntryStart
);
3389 void DomainMapper_Impl::fillEmptyFrameProperties(std::vector
<beans::PropertyValue
>& rFrameProperties
, bool bSetAnchorToChar
)
3391 if (bSetAnchorToChar
)
3392 rFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_ANCHOR_TYPE
), text::TextContentAnchorType_AS_CHARACTER
));
3394 uno::Any aEmptyBorder
{table::BorderLine2()};
3395 static const std::vector
<PropertyIds
> aBorderIds
3396 = { PROP_BOTTOM_BORDER
, PROP_LEFT_BORDER
, PROP_RIGHT_BORDER
, PROP_TOP_BORDER
};
3397 for (size_t i
= 0; i
< aBorderIds
.size(); ++i
)
3398 rFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(aBorderIds
[i
]), aEmptyBorder
));
3400 static const std::vector
<PropertyIds
> aMarginIds
3401 = { PROP_BOTTOM_MARGIN
, PROP_BOTTOM_BORDER_DISTANCE
,
3402 PROP_LEFT_MARGIN
, PROP_LEFT_BORDER_DISTANCE
,
3403 PROP_RIGHT_MARGIN
, PROP_RIGHT_BORDER_DISTANCE
,
3404 PROP_TOP_MARGIN
, PROP_TOP_BORDER_DISTANCE
};
3405 for (size_t i
= 0; i
< aMarginIds
.size(); ++i
)
3406 rFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(aMarginIds
[i
]), static_cast<sal_Int32
>(0)));
3409 bool DomainMapper_Impl::IsInTOC() const
3411 if (IsInHeaderFooter())
3412 return m_bStartTOCHeaderFooter
;
3417 void DomainMapper_Impl::ConvertHeaderFooterToTextFrame(bool bDynamicHeightTop
, bool bDynamicHeightBottom
)
3419 while (!m_aHeaderFooterTextAppendStack
.empty())
3421 auto aFooterHeader
= m_aHeaderFooterTextAppendStack
.top();
3422 if ((aFooterHeader
.second
&& !bDynamicHeightTop
) || (!aFooterHeader
.second
&& !bDynamicHeightBottom
))
3424 uno::Reference
< text::XTextAppend
> xTextAppend
= aFooterHeader
.first
.xTextAppend
;
3425 uno::Reference
< text::XTextCursor
> xCursor
= xTextAppend
->createTextCursor();
3426 uno::Reference
< text::XTextRange
> xRangeStart
, xRangeEnd
;
3428 xRangeStart
= xCursor
->getStart();
3429 xCursor
->gotoEnd(false);
3430 xRangeEnd
= xCursor
->getStart();
3432 std::vector
<beans::PropertyValue
> aFrameProperties
3434 comphelper::makePropertyValue("TextWrap", css::text::WrapTextMode_THROUGH
),
3435 comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT
), text::HoriOrientation::LEFT
),
3436 comphelper::makePropertyValue(getPropertyName(PROP_OPAQUE
), false),
3437 comphelper::makePropertyValue(getPropertyName(PROP_WIDTH_TYPE
), text::SizeType::MIN
),
3438 comphelper::makePropertyValue(getPropertyName(PROP_SIZE_TYPE
), text::SizeType::MIN
),
3439 // tdf#143384 If the header/footer started with a table, convertToTextFrame could not
3440 // convert the table, because it used createTextCursor() -which ignore tables-
3441 // to set the conversion range.
3442 // This dummy property is set to make convertToTextFrame to use another CreateTextCursor
3443 // method that can be parameterized to not ignore tables.
3444 comphelper::makePropertyValue(getPropertyName(PROP_CURSOR_NOT_IGNORE_TABLES_IN_HF
), true)
3447 fillEmptyFrameProperties(aFrameProperties
, false);
3449 // If it is a footer, then orient the frame to the bottom
3450 if (!aFooterHeader
.second
)
3451 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT
), text::VertOrientation::BOTTOM
));
3453 uno::Reference
<text::XTextAppendAndConvert
> xBodyText(
3454 xRangeStart
->getText(), uno::UNO_QUERY
);
3455 xBodyText
->convertToTextFrame(xTextAppend
, xRangeEnd
,
3456 comphelper::containerToSequence(aFrameProperties
));
3458 m_aHeaderFooterTextAppendStack
.pop();
3462 void DomainMapper_Impl::PushPageHeaderFooter(bool bHeader
, SectionPropertyMap::PageType eType
)
3464 m_bSaveParaHadField
= m_bParaHadField
;
3465 m_aHeaderFooterStack
.push(HeaderFooterContext(m_bTextInserted
, m_nTableDepth
));
3466 m_bTextInserted
= false;
3469 const PropertyIds ePropIsOn
= bHeader
? PROP_HEADER_IS_ON
: PROP_FOOTER_IS_ON
;
3470 const PropertyIds ePropShared
= bHeader
? PROP_HEADER_IS_SHARED
: PROP_FOOTER_IS_SHARED
;
3471 const PropertyIds ePropTextLeft
= bHeader
? PROP_HEADER_TEXT_LEFT
: PROP_FOOTER_TEXT_LEFT
;
3472 const PropertyIds ePropText
= bHeader
? PROP_HEADER_TEXT
: PROP_FOOTER_TEXT
;
3474 m_bDiscardHeaderFooter
= true;
3475 m_eInHeaderFooterImport
3476 = bHeader
? HeaderFooterImportState::header
: HeaderFooterImportState::footer
;
3478 //get the section context
3479 PropertyMapPtr pContext
= DomainMapper_Impl::GetTopContextOfType(CONTEXT_SECTION
);
3480 //ask for the header/footer name of the given type
3481 SectionPropertyMap
* pSectionContext
= dynamic_cast< SectionPropertyMap
* >( pContext
.get() );
3482 if(!pSectionContext
)
3485 // clear the "Link To Previous" flag so that the header/footer
3486 // content is not copied from the previous section
3487 pSectionContext
->ClearHeaderFooterLinkToPrevious(bHeader
, eType
);
3491 return; // TODO sw cannot Undo insert header/footer without crashing
3494 uno::Reference
< beans::XPropertySet
> xPageStyle
=
3495 pSectionContext
->GetPageStyle(
3497 eType
== SectionPropertyMap::PAGE_FIRST
);
3498 if (!xPageStyle
.is())
3502 bool bLeft
= eType
== SectionPropertyMap::PAGE_LEFT
;
3503 bool bFirst
= eType
== SectionPropertyMap::PAGE_FIRST
;
3504 if (!bLeft
|| GetSettingsTable()->GetEvenAndOddHeaders())
3506 //switch on header/footer use
3507 xPageStyle
->setPropertyValue(
3508 getPropertyName(ePropIsOn
),
3511 // If the 'Different Even & Odd Pages' flag is turned on - do not ignore it
3512 // Even if the 'Even' header/footer is blank - the flag should be imported (so it would look in LO like in Word)
3513 if (!bFirst
&& GetSettingsTable()->GetEvenAndOddHeaders())
3514 xPageStyle
->setPropertyValue(getPropertyName(ePropShared
), uno::Any(false));
3517 uno::Reference
< text::XText
> xText
;
3518 xPageStyle
->getPropertyValue(getPropertyName(bLeft
? ePropTextLeft
: ePropText
)) >>= xText
;
3520 m_aTextAppendStack
.push(TextAppendContext(uno::Reference
< text::XTextAppend
>(xText
, uno::UNO_QUERY_THROW
),
3522 ? uno::Reference
<text::XTextCursor
>()
3523 : xText
->createTextCursorByRange(xText
->getStart())));
3524 m_aHeaderFooterTextAppendStack
.push(std::make_pair(TextAppendContext(uno::Reference
< text::XTextAppend
>(xText
, uno::UNO_QUERY_THROW
),
3526 ? uno::Reference
<text::XTextCursor
>()
3527 : xText
->createTextCursorByRange(xText
->getStart())),
3530 // If we have *hidden* header footer
3533 bool bIsShared
= false;
3534 // Turn on the headers
3535 xPageStyle
->setPropertyValue(getPropertyName(ePropIsOn
), uno::Any(true));
3536 // Store the state of the previous state of shared prop
3537 xPageStyle
->getPropertyValue(getPropertyName(ePropShared
)) >>= bIsShared
;
3538 // Turn on the shared prop in order to save the headers/footers in time
3539 xPageStyle
->setPropertyValue(getPropertyName(ePropShared
), uno::Any(false));
3540 // Add the content of the headers footers to the doc
3541 uno::Reference
<text::XText
> xText
;
3542 xPageStyle
->getPropertyValue(getPropertyName(bLeft
? ePropTextLeft
: ePropText
))
3545 m_aTextAppendStack
.push(
3546 TextAppendContext(uno::Reference
<text::XTextAppend
>(xText
, uno::UNO_QUERY_THROW
),
3547 m_bIsNewDoc
? uno::Reference
<text::XTextCursor
>()
3548 : xText
->createTextCursorByRange(xText
->getStart())));
3549 // Restore the original state of the shared prop after we stored the necessary values.
3550 xPageStyle
->setPropertyValue(getPropertyName(ePropShared
), uno::Any(bIsShared
));
3552 m_bDiscardHeaderFooter
= false; // set only on success!
3554 catch( const uno::Exception
& )
3556 DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
3560 void DomainMapper_Impl::PushPageHeader(SectionPropertyMap::PageType eType
)
3562 PushPageHeaderFooter(/* bHeader = */ true, eType
);
3565 void DomainMapper_Impl::PushPageFooter(SectionPropertyMap::PageType eType
)
3567 PushPageHeaderFooter(/* bHeader = */ false, eType
);
3570 void DomainMapper_Impl::PopPageHeaderFooter()
3572 //header and footer always have an empty paragraph at the end
3573 //this has to be removed
3574 RemoveLastParagraph( );
3576 if (!m_aTextAppendStack
.empty())
3578 if (!m_bDiscardHeaderFooter
)
3580 m_aTextAppendStack
.pop();
3582 m_bDiscardHeaderFooter
= false;
3584 m_eInHeaderFooterImport
= HeaderFooterImportState::none
;
3586 if (!m_aHeaderFooterStack
.empty())
3588 m_bTextInserted
= m_aHeaderFooterStack
.top().getTextInserted();
3589 m_nTableDepth
= m_aHeaderFooterStack
.top().getTableDepth();
3590 m_aHeaderFooterStack
.pop();
3593 m_bParaHadField
= m_bSaveParaHadField
;
3596 void DomainMapper_Impl::PushFootOrEndnote( bool bIsFootnote
)
3598 SAL_WARN_IF(m_bInFootOrEndnote
, "writerfilter.dmapper", "PushFootOrEndnote() is called from another foot or endnote");
3599 m_bInFootOrEndnote
= true;
3600 m_bInFootnote
= bIsFootnote
;
3601 m_bCheckFirstFootnoteTab
= true;
3602 m_bSaveFirstParagraphInCell
= m_bFirstParagraphInCell
;
3605 // Redlines outside the footnote should not affect footnote content
3606 m_aRedlines
.push(std::vector
< RedlineParamsPtr
>());
3608 // IMHO character styles from footnote labels should be ignored in the edit view of Writer.
3609 // This adds a hack on top of the following hack to save the style name in the context.
3610 PropertyMapPtr pTopContext
= GetTopContext();
3611 OUString sFootnoteCharStyleName
;
3612 std::optional
< PropertyMap::Property
> aProp
= pTopContext
->getProperty(PROP_CHAR_STYLE_NAME
);
3614 aProp
->second
>>= sFootnoteCharStyleName
;
3616 // Remove style reference, if any. This reference did appear here as a side effect of tdf#43017
3617 // Seems it is not required by LO, but causes side effects during editing. So remove it
3618 // for footnotes/endnotes to restore original LO behavior here.
3619 pTopContext
->Erase(PROP_CHAR_STYLE_NAME
);
3621 uno::Reference
< text::XText
> xFootnoteText
;
3622 if (GetTextFactory().is())
3623 xFootnoteText
.set( GetTextFactory()->createInstance(
3625 OUString( "com.sun.star.text.Footnote" ) : OUString( "com.sun.star.text.Endnote" )),
3626 uno::UNO_QUERY_THROW
);
3627 uno::Reference
< text::XFootnote
> xFootnote( xFootnoteText
, uno::UNO_QUERY_THROW
);
3628 pTopContext
->SetFootnote(xFootnote
, sFootnoteCharStyleName
);
3629 uno::Sequence
< beans::PropertyValue
> aFontProperties
;
3630 if (GetTopContextOfType(CONTEXT_CHARACTER
))
3631 aFontProperties
= GetTopContextOfType(CONTEXT_CHARACTER
)->GetPropertyValues();
3632 appendTextContent( uno::Reference
< text::XTextContent
>( xFootnoteText
, uno::UNO_QUERY_THROW
), aFontProperties
);
3633 m_aTextAppendStack
.push(TextAppendContext(uno::Reference
< text::XTextAppend
>( xFootnoteText
, uno::UNO_QUERY_THROW
),
3634 xFootnoteText
->createTextCursorByRange(xFootnoteText
->getStart())));
3636 // Redlines for the footnote anchor in the main text content
3637 std::vector
< RedlineParamsPtr
> aFootnoteRedline
= std::move(m_aRedlines
.top());
3639 CheckRedline( xFootnote
->getAnchor( ) );
3640 m_aRedlines
.push( aFootnoteRedline
);
3642 // Try scanning for custom footnote labels
3643 if (!sFootnoteCharStyleName
.isEmpty())
3644 StartCustomFootnote(pTopContext
);
3646 EndCustomFootnote();
3648 catch( const uno::Exception
& )
3650 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "PushFootOrEndnote");
3654 void DomainMapper_Impl::CreateRedline(uno::Reference
<text::XTextRange
> const& xRange
,
3655 const RedlineParamsPtr
& pRedline
)
3660 bool bRedlineMoved
= false;
3664 switch ( pRedline
->m_nToken
& 0xffff )
3667 sType
= getPropertyName( PROP_FORMAT
);
3670 bRedlineMoved
= true;
3671 m_pParaMarkerRedlineMove
= pRedline
.get();
3674 sType
= getPropertyName( PROP_INSERT
);
3677 bRedlineMoved
= true;
3678 m_pParaMarkerRedlineMove
= pRedline
.get();
3681 sType
= getPropertyName( PROP_DELETE
);
3683 case XML_ParagraphFormat
:
3684 sType
= getPropertyName( PROP_PARAGRAPH_FORMAT
);
3687 throw lang::IllegalArgumentException("illegal redline token type", nullptr, 0);
3689 beans::PropertyValues
aRedlineProperties( 4 );
3690 beans::PropertyValue
* pRedlineProperties
= aRedlineProperties
.getArray( );
3691 pRedlineProperties
[0].Name
= getPropertyName( PROP_REDLINE_AUTHOR
);
3692 pRedlineProperties
[0].Value
<<= pRedline
->m_sAuthor
;
3693 pRedlineProperties
[1].Name
= getPropertyName( PROP_REDLINE_DATE_TIME
);
3694 util::DateTime aDateTime
= ConversionHelper::ConvertDateStringToDateTime( pRedline
->m_sDate
);
3695 // tdf#146171 import not specified w:date (or specified as zero date "0-00-00")
3696 // as Epoch time to avoid of losing change tracking data during ODF roundtrip
3697 if ( aDateTime
.Year
== 0 && aDateTime
.Month
== 0 && aDateTime
.Day
== 0 )
3699 aDateTime
.Year
= 1970;
3700 aDateTime
.Month
= 1;
3703 pRedlineProperties
[1].Value
<<= aDateTime
;
3704 pRedlineProperties
[2].Name
= getPropertyName( PROP_REDLINE_REVERT_PROPERTIES
);
3705 pRedlineProperties
[2].Value
<<= pRedline
->m_aRevertProperties
;
3706 pRedlineProperties
[3].Name
= "RedlineMoved";
3707 pRedlineProperties
[3].Value
<<= bRedlineMoved
;
3709 if (!m_bIsActualParagraphFramed
)
3711 uno::Reference
< text::XRedline
> xRedline( xRange
, uno::UNO_QUERY_THROW
);
3712 xRedline
->makeRedline( sType
, aRedlineProperties
);
3714 // store frame and (possible floating) table redline data for restoring them after frame conversion
3715 enum StoredRedlines eType
;
3716 if (m_bIsActualParagraphFramed
|| m_nTableDepth
> 0)
3717 eType
= StoredRedlines::FRAME
;
3718 else if (IsInFootOrEndnote())
3719 eType
= IsInFootnote() ? StoredRedlines::FOOTNOTE
: StoredRedlines::ENDNOTE
;
3721 eType
= StoredRedlines::NONE
;
3723 if (eType
!= StoredRedlines::NONE
)
3725 m_aStoredRedlines
[eType
].push_back( uno::Any(xRange
) );
3726 m_aStoredRedlines
[eType
].push_back( uno::Any(sType
) );
3727 m_aStoredRedlines
[eType
].push_back( uno::Any(aRedlineProperties
) );
3730 catch( const uno::Exception
& )
3732 TOOLS_WARN_EXCEPTION( "writerfilter", "in makeRedline" );
3736 void DomainMapper_Impl::CheckParaMarkerRedline( uno::Reference
< text::XTextRange
> const& xRange
)
3738 if ( m_pParaMarkerRedline
)
3740 CreateRedline( xRange
, m_pParaMarkerRedline
);
3741 if ( m_pParaMarkerRedline
)
3743 m_pParaMarkerRedline
.clear();
3744 m_currentRedline
.clear();
3747 else if ( m_pParaMarkerRedlineMove
&& m_bIsParaMarkerMove
)
3749 // terminating moveFrom/moveTo redline removes also the paragraph mark
3750 CreateRedline( xRange
, m_pParaMarkerRedlineMove
);
3752 if ( m_pParaMarkerRedlineMove
)
3754 m_pParaMarkerRedlineMove
.clear();
3755 EndParaMarkerMove();
3759 void DomainMapper_Impl::CheckRedline( uno::Reference
< text::XTextRange
> const& xRange
)
3761 // Writer core "officially" does not like overlapping redlines, and its UNO interface is stupid enough
3762 // to not prevent that. However, in practice in fact everything appears to work fine (except for the debug warnings
3763 // about redline table corruption, which may possibly be harmless in reality). So leave this as it is, since this
3764 // is a better representation of how the changes happened. If this will ever become a problem, overlapping redlines
3765 // will need to be merged into one, just like doing the changes in the UI does, which will lose some information
3766 // (and so if that happens, it may be better to fix Writer).
3767 // Create the redlines here from lowest (formats) to highest (inserts/removals) priority, since the last one is
3768 // what Writer presents graphically, so this will show deletes as deleted text and not as just formatted text being there.
3769 bool bUsedRange
= m_aRedlines
.top().size() > 0 || (GetTopContextOfType(CONTEXT_CHARACTER
) &&
3770 GetTopContextOfType(CONTEXT_CHARACTER
)->Redlines().size() > 0);
3772 // only export ParagraphFormat, when there is no other redline in the same text portion to avoid missing redline compression,
3773 // but always export the first ParagraphFormat redline in a paragraph to keep the paragraph style change data for rejection
3774 if( (!bUsedRange
|| !m_bParaChanged
) && GetTopContextOfType(CONTEXT_PARAGRAPH
) )
3776 std::vector
<RedlineParamsPtr
>& avRedLines
= GetTopContextOfType(CONTEXT_PARAGRAPH
)->Redlines();
3777 for( const auto& rRedline
: avRedLines
)
3778 CreateRedline( xRange
, rRedline
);
3780 if( GetTopContextOfType(CONTEXT_CHARACTER
) )
3782 std::vector
<RedlineParamsPtr
>& avRedLines
= GetTopContextOfType(CONTEXT_CHARACTER
)->Redlines();
3783 for( const auto& rRedline
: avRedLines
)
3784 CreateRedline( xRange
, rRedline
);
3786 for (const auto& rRedline
: m_aRedlines
.top() )
3787 CreateRedline( xRange
, rRedline
);
3790 void DomainMapper_Impl::StartParaMarkerChange( )
3792 m_bIsParaMarkerChange
= true;
3795 void DomainMapper_Impl::EndParaMarkerChange( )
3797 m_bIsParaMarkerChange
= false;
3798 m_previousRedline
= m_currentRedline
;
3799 m_currentRedline
.clear();
3802 void DomainMapper_Impl::StartParaMarkerMove( )
3804 m_bIsParaMarkerMove
= true;
3807 void DomainMapper_Impl::EndParaMarkerMove( )
3809 m_bIsParaMarkerMove
= false;
3812 void DomainMapper_Impl::StartCustomFootnote(const PropertyMapPtr pContext
)
3814 if (pContext
== m_pFootnoteContext
)
3817 assert(pContext
->GetFootnote().is());
3818 m_bHasFootnoteStyle
= true;
3819 m_bCheckFootnoteStyle
= !pContext
->GetFootnoteStyle().isEmpty();
3820 m_pFootnoteContext
= pContext
;
3823 void DomainMapper_Impl::EndCustomFootnote()
3825 m_bHasFootnoteStyle
= false;
3826 m_bCheckFootnoteStyle
= false;
3829 void DomainMapper_Impl::PushAnnotation()
3833 m_bIsInComments
= true;
3834 if (!GetTextFactory().is())
3836 m_xAnnotationField
.set( GetTextFactory()->createInstance( "com.sun.star.text.TextField.Annotation" ),
3837 uno::UNO_QUERY_THROW
);
3838 uno::Reference
< text::XText
> xAnnotationText
;
3839 m_xAnnotationField
->getPropertyValue("TextRange") >>= xAnnotationText
;
3840 m_aTextAppendStack
.push(TextAppendContext(uno::Reference
< text::XTextAppend
>( xAnnotationText
, uno::UNO_QUERY_THROW
),
3841 m_bIsNewDoc
? uno::Reference
<text::XTextCursor
>() : xAnnotationText
->createTextCursorByRange(xAnnotationText
->getStart())));
3843 catch( const uno::Exception
&)
3845 DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
3849 static void lcl_CopyRedlines(
3850 uno::Reference
< text::XText
> const& xSrc
,
3851 std::deque
<css::uno::Any
>& rRedlines
,
3852 std::vector
<sal_Int32
>& redPos
,
3853 std::vector
<sal_Int32
>& redLen
,
3857 for( size_t i
= 0; i
< rRedlines
.size(); i
+=3)
3859 uno::Reference
< text::XTextRange
> xRange
;
3860 rRedlines
[i
] >>= xRange
;
3862 // is this a redline of the temporary footnote?
3863 uno::Reference
<text::XTextCursor
> xRangeCursor
;
3866 xRangeCursor
= xSrc
->createTextCursorByRange( xRange
);
3868 catch( const uno::Exception
& )
3871 if (xRangeCursor
.is())
3874 sal_Int32 nLen
= xRange
->getString().getLength();
3875 redLen
.push_back(nLen
);
3876 xRangeCursor
->gotoRange(xSrc
->getStart(), true);
3877 redPos
.push_back(xRangeCursor
->getString().getLength() - nLen
);
3881 // we have already found all redlines of the footnote,
3882 // skip checking the redlines of the other footnotes
3885 // failed createTextCursorByRange(), for example, table inside the frame
3886 redLen
.push_back(-1);
3887 redPos
.push_back(-1);
3892 static void lcl_PasteRedlines(
3893 uno::Reference
< text::XText
> const& xDest
,
3894 std::deque
<css::uno::Any
>& rRedlines
,
3895 std::vector
<sal_Int32
>& redPos
,
3896 std::vector
<sal_Int32
>& redLen
,
3899 // create redlines in the copied footnote
3900 for( size_t i
= 0; redIdx
> -1 && i
<= sal::static_int_cast
<size_t>(redIdx
); i
+=3)
3903 beans::PropertyValues
aRedlineProperties( 3 );
3904 // skip failed createTextCursorByRange()
3905 if (redPos
[i
/3] == -1)
3907 rRedlines
[i
+1] >>= sType
;
3908 rRedlines
[i
+2] >>= aRedlineProperties
;
3909 uno::Reference
< text::XTextCursor
> xCrsr
= xDest
->getText()->createTextCursor();
3910 xCrsr
->goRight(redPos
[i
/3], false);
3911 xCrsr
->goRight(redLen
[i
/3], true);
3912 uno::Reference
< text::XRedline
> xRedline( xCrsr
, uno::UNO_QUERY_THROW
);
3914 xRedline
->makeRedline( sType
, aRedlineProperties
);
3916 catch(const uno::Exception
&)
3918 // ignore (footnotes of tracked deletions)
3923 bool DomainMapper_Impl::CopyTemporaryNotes(
3924 uno::Reference
< text::XFootnote
> xNoteSrc
,
3925 uno::Reference
< text::XFootnote
> xNoteDest
)
3927 if (!m_bSaxError
&& xNoteSrc
!= xNoteDest
)
3929 uno::Reference
< text::XText
> xSrc( xNoteSrc
, uno::UNO_QUERY_THROW
);
3930 uno::Reference
< text::XText
> xDest( xNoteDest
, uno::UNO_QUERY_THROW
);
3931 uno::Reference
< text::XTextCopy
> xTxt
, xTxt2
;
3932 xTxt
.set( xSrc
, uno::UNO_QUERY_THROW
);
3933 xTxt2
.set( xDest
, uno::UNO_QUERY_THROW
);
3934 xTxt2
->copyText( xTxt
);
3936 // copy its redlines
3937 std::vector
<sal_Int32
> redPos
, redLen
;
3939 enum StoredRedlines eType
= IsInFootnote() ? StoredRedlines::FOOTNOTE
: StoredRedlines::ENDNOTE
;
3940 lcl_CopyRedlines(xSrc
, m_aStoredRedlines
[eType
], redPos
, redLen
, redIdx
);
3941 lcl_PasteRedlines(xDest
, m_aStoredRedlines
[eType
], redPos
, redLen
, redIdx
);
3943 // remove processed redlines
3944 for( size_t i
= 0; redIdx
> -1 && i
<= sal::static_int_cast
<size_t>(redIdx
) + 2; i
++)
3945 m_aStoredRedlines
[eType
].pop_front();
3953 void DomainMapper_Impl::RemoveTemporaryFootOrEndnotes()
3955 uno::Reference
< text::XFootnotesSupplier
> xFootnotesSupplier( GetTextDocument(), uno::UNO_QUERY
);
3956 uno::Reference
< text::XEndnotesSupplier
> xEndnotesSupplier( GetTextDocument(), uno::UNO_QUERY
);
3957 uno::Reference
< text::XFootnote
> xNote
;
3958 if (GetFootnoteCount() > 0)
3960 auto xFootnotes
= xFootnotesSupplier
->getFootnotes();
3961 if ( m_nFirstFootnoteIndex
> 0 )
3963 uno::Reference
< text::XFootnote
> xFirstNote
;
3964 xFootnotes
->getByIndex(0) >>= xFirstNote
;
3965 uno::Reference
< text::XText
> xText( xFirstNote
, uno::UNO_QUERY_THROW
);
3966 xText
->setString("");
3967 xFootnotes
->getByIndex(m_nFirstFootnoteIndex
) >>= xNote
;
3968 CopyTemporaryNotes(xNote
, xFirstNote
);
3970 for (sal_Int32 i
= GetFootnoteCount(); i
> 0; --i
)
3972 xFootnotes
->getByIndex(i
) >>= xNote
;
3973 xNote
->getAnchor()->setString("");
3976 if (GetEndnoteCount() > 0)
3978 auto xEndnotes
= xEndnotesSupplier
->getEndnotes();
3979 if ( m_nFirstEndnoteIndex
> 0 )
3981 uno::Reference
< text::XFootnote
> xFirstNote
;
3982 xEndnotes
->getByIndex(0) >>= xFirstNote
;
3983 uno::Reference
< text::XText
> xText( xFirstNote
, uno::UNO_QUERY_THROW
);
3984 xText
->setString("");
3985 xEndnotes
->getByIndex(m_nFirstEndnoteIndex
) >>= xNote
;
3986 CopyTemporaryNotes(xNote
, xFirstNote
);
3988 for (sal_Int32 i
= GetEndnoteCount(); i
> 0; --i
)
3990 xEndnotes
->getByIndex(i
) >>= xNote
;
3991 xNote
->getAnchor()->setString("");
3996 static void lcl_convertToNoteIndices(std::deque
<sal_Int32
>& rNoteIds
, sal_Int32
& rFirstNoteIndex
)
3998 // rNoteIds contains XML footnote identifiers in the loaded order of the footnotes
3999 // (the same order as in footnotes.xml), i.e. it maps temporary footnote positions to the
4000 // identifiers. For example: Ids[0] = 100; Ids[1] = -1, Ids[2] = 5.
4001 // To copy the footnotes in their final place, create an array, which map the (normalized)
4002 // footnote identifiers to the temporary footnote positions. Using the previous example,
4003 // Pos[0] = 1; Pos[1] = 2; Pos[2] = 0 (where [0], [1], [2] are the normalized
4004 // -1, 5 and 100 identifiers).
4005 std::deque
<sal_Int32
> aSortedIds
= rNoteIds
;
4006 std::sort(aSortedIds
.begin(), aSortedIds
.end());
4007 std::map
<sal_Int32
, size_t> aMapIds
;
4008 // normalize footnote identifiers to 0, 1, 2 ...
4009 for (size_t i
= 0; i
< aSortedIds
.size(); ++i
)
4010 aMapIds
[aSortedIds
[i
]] = i
;
4011 // reusing rNoteIds, create the Pos array to map normalized identifiers to the loaded positions
4012 std::deque
<sal_Int32
> aOrigNoteIds
= rNoteIds
;
4013 for (size_t i
= 0; i
< rNoteIds
.size(); ++i
)
4014 rNoteIds
[aMapIds
[aOrigNoteIds
[i
]]] = i
;
4015 rFirstNoteIndex
= rNoteIds
.front();
4016 rNoteIds
.pop_front();
4019 void DomainMapper_Impl::PopFootOrEndnote()
4021 // content of the footnotes were inserted after the first footnote in temporary footnotes,
4022 // restore the content of the actual footnote by copying its content from the first
4023 // (remaining) temporary footnote and remove the temporary footnote.
4024 uno::Reference
< text::XFootnotesSupplier
> xFootnotesSupplier( GetTextDocument(), uno::UNO_QUERY
);
4025 uno::Reference
< text::XEndnotesSupplier
> xEndnotesSupplier( GetTextDocument(), uno::UNO_QUERY
);
4026 bool bCopied
= false;
4027 if ( IsInFootOrEndnote() && ( ( IsInFootnote() && GetFootnoteCount() > -1 && xFootnotesSupplier
.is() ) ||
4028 ( !IsInFootnote() && GetEndnoteCount() > -1 && xEndnotesSupplier
.is() ) ) )
4030 uno::Reference
< text::XFootnote
> xNoteFirst
, xNoteLast
;
4031 auto xFootnotes
= xFootnotesSupplier
->getFootnotes();
4032 auto xEndnotes
= xEndnotesSupplier
->getEndnotes();
4033 if ( ( ( IsInFootnote() && xFootnotes
->getCount() > 1 &&
4034 ( xFootnotes
->getByIndex(xFootnotes
->getCount()-1) >>= xNoteLast
) ) ||
4035 ( !IsInFootnote() && xEndnotes
->getCount() > 1 &&
4036 ( xEndnotes
->getByIndex(xEndnotes
->getCount()-1) >>= xNoteLast
) )
4037 ) && xNoteLast
->getLabel().isEmpty() )
4039 // copy content of the next temporary footnote
4042 if ( IsInFootnote() && !m_aFootnoteIds
.empty() )
4044 if ( m_nFirstFootnoteIndex
== -1 )
4045 lcl_convertToNoteIndices(m_aFootnoteIds
, m_nFirstFootnoteIndex
);
4046 if (m_aFootnoteIds
.empty()) // lcl_convertToNoteIndices pops m_aFootnoteIds
4050 xFootnotes
->getByIndex(m_aFootnoteIds
.front()) >>= xNoteFirst
;
4051 m_aFootnoteIds
.pop_front();
4054 else if ( !IsInFootnote() && !m_aEndnoteIds
.empty() )
4056 if ( m_nFirstEndnoteIndex
== -1 )
4057 lcl_convertToNoteIndices(m_aEndnoteIds
, m_nFirstEndnoteIndex
);
4058 if (m_aEndnoteIds
.empty()) // lcl_convertToNoteIndices pops m_aEndnoteIds
4062 xEndnotes
->getByIndex(m_aEndnoteIds
.front()) >>= xNoteFirst
;
4063 m_aEndnoteIds
.pop_front();
4069 catch (uno::Exception
const&)
4071 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "Cannot insert footnote/endnote");
4075 bCopied
= CopyTemporaryNotes(xNoteFirst
, xNoteLast
);
4079 if (!IsRTFImport() && !bCopied
)
4080 RemoveLastParagraph();
4082 // In case the foot or endnote did not contain a tab.
4083 m_bIgnoreNextTab
= false;
4085 if (!m_aTextAppendStack
.empty())
4086 m_aTextAppendStack
.pop();
4088 if (m_aRedlines
.size() == 1)
4090 SAL_WARN("writerfilter.dmapper", "PopFootOrEndnote() is called without PushFootOrEndnote()?");
4094 m_eSkipFootnoteState
= SkipFootnoteSeparator::OFF
;
4095 m_bInFootOrEndnote
= m_bInFootnote
= false;
4096 m_pFootnoteContext
= nullptr;
4097 m_bFirstParagraphInCell
= m_bSaveFirstParagraphInCell
;
4100 void DomainMapper_Impl::PopAnnotation()
4102 RemoveLastParagraph();
4104 m_bIsInComments
= false;
4105 m_aTextAppendStack
.pop();
4109 if (m_bAnnotationResolved
)
4110 m_xAnnotationField
->setPropertyValue("Resolved", uno::Any(true));
4112 m_xAnnotationField
->setPropertyValue("ParaIdParent", uno::Any(m_sAnnotationParent
));
4113 m_xAnnotationField
->setPropertyValue("ParaId", uno::Any(m_sAnnotationImportedParaId
));
4115 // See if the annotation will be a single position or a range.
4116 if (m_nAnnotationId
== -1 || !m_aAnnotationPositions
[m_nAnnotationId
].m_xStart
.is() || !m_aAnnotationPositions
[m_nAnnotationId
].m_xEnd
.is())
4118 uno::Sequence
< beans::PropertyValue
> aEmptyProperties
;
4119 uno::Reference
< text::XTextContent
> xContent( m_xAnnotationField
, uno::UNO_QUERY_THROW
);
4120 appendTextContent( xContent
, aEmptyProperties
);
4121 CheckRedline( xContent
->getAnchor( ) );
4125 AnnotationPosition
& aAnnotationPosition
= m_aAnnotationPositions
[m_nAnnotationId
];
4126 // Create a range that points to the annotation start/end.
4127 uno::Reference
<text::XText
> const xText
= aAnnotationPosition
.m_xStart
->getText();
4128 uno::Reference
<text::XTextCursor
> const xCursor
= xText
->createTextCursorByRange(aAnnotationPosition
.m_xStart
);
4130 bool bMarker
= false;
4131 uno::Reference
<text::XTextRangeCompare
> xTextRangeCompare(xText
, uno::UNO_QUERY
);
4132 if (xTextRangeCompare
->compareRegionStarts(aAnnotationPosition
.m_xStart
, aAnnotationPosition
.m_xEnd
) == 0)
4134 // Insert a marker so that comment around an anchored image is not collapsed during
4136 xText
->insertString(xCursor
, "x", false);
4140 xCursor
->gotoRange(aAnnotationPosition
.m_xEnd
, true);
4141 uno::Reference
<text::XTextRange
> const xTextRange(xCursor
, uno::UNO_QUERY_THROW
);
4143 // Attach the annotation to the range.
4144 uno::Reference
<text::XTextAppend
> const xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
4145 xTextAppend
->insertTextContent(xTextRange
, uno::Reference
<text::XTextContent
>(m_xAnnotationField
, uno::UNO_QUERY_THROW
), !xCursor
->isCollapsed());
4149 // Remove the marker.
4150 xCursor
->goLeft(1, true);
4151 xCursor
->setString(OUString());
4154 m_aAnnotationPositions
.erase( m_nAnnotationId
);
4156 catch (uno::Exception
const&)
4158 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "Cannot insert annotation field");
4161 m_xAnnotationField
.clear();
4162 m_sAnnotationParent
.clear();
4163 m_sAnnotationImportedParaId
.clear();
4164 m_nAnnotationId
= -1;
4165 m_bAnnotationResolved
= false;
4168 void DomainMapper_Impl::PushPendingShape( const uno::Reference
< drawing::XShape
> & xShape
)
4170 m_aPendingShapes
.push_back(xShape
);
4173 uno::Reference
<drawing::XShape
> DomainMapper_Impl::PopPendingShape()
4175 uno::Reference
<drawing::XShape
> xRet
;
4176 if (!m_aPendingShapes
.empty())
4178 xRet
= m_aPendingShapes
.front();
4179 m_aPendingShapes
.pop_front();
4184 void DomainMapper_Impl::PushShapeContext( const uno::Reference
< drawing::XShape
> & xShape
)
4186 // Append these early, so the context and the table manager stack will be
4187 // in sync, even if the text append stack is empty.
4188 appendTableManager();
4189 appendTableHandler();
4190 getTableManager().startLevel();
4192 if (m_aTextAppendStack
.empty())
4194 uno::Reference
<text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
4198 uno::Reference
< lang::XServiceInfo
> xSInfo( xShape
, uno::UNO_QUERY_THROW
);
4199 if (xSInfo
->supportsService("com.sun.star.drawing.GroupShape"))
4201 // Textboxes in shapes do not support styles, so check saved style information and apply properties directly to the child shapes.
4202 const uno::Reference
<drawing::XShapes
> xShapes(xShape
, uno::UNO_QUERY
);
4203 const sal_uInt32 nShapeCount
= xShapes
.is() ? xShapes
->getCount() : 0;
4204 for ( sal_uInt32 i
= 0; i
< nShapeCount
; ++i
)
4208 uno::Reference
<text::XTextRange
> xFrame(xShapes
->getByIndex(i
), uno::UNO_QUERY
);
4209 uno::Reference
<beans::XPropertySet
> xFramePropertySet
;
4211 xFramePropertySet
.set(xFrame
, uno::UNO_QUERY_THROW
);
4212 uno::Reference
<beans::XPropertySet
> xShapePropertySet(xShapes
->getByIndex(i
), uno::UNO_QUERY_THROW
);
4214 comphelper::SequenceAsHashMap
aGrabBag( xShapePropertySet
->getPropertyValue("CharInteropGrabBag") );
4216 // only VML import has checked for style. Don't apply default parastyle properties to other imported shapes
4217 // - except for fontsize - to maintain compatibility with previous versions of LibreOffice.
4218 const bool bOnlyApplyCharHeight
= !aGrabBag
["mso-pStyle"].hasValue();
4220 OUString sStyleName
;
4221 aGrabBag
["mso-pStyle"] >>= sStyleName
;
4222 StyleSheetEntryPtr pEntry
= GetStyleSheetTable()->FindStyleSheetByISTD( sStyleName
);
4225 // Use default style even in ambiguous cases (where multiple styles were defined) since MOST styles inherit
4226 // MOST of their properties from the default style. In the ambiguous case, we have to accept some kind of compromise
4227 // and the default paragraph style ought to be the safest one... (compared to DocDefaults or program defaults)
4228 pEntry
= GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( GetDefaultParaStyleName() );
4232 // The Ids here come from oox/source/vml/vmltextbox.cxx.
4233 // It probably could safely expand to all Ids that shapes support.
4234 const PropertyIds eIds
[] = {
4236 PROP_CHAR_FONT_NAME
,
4238 PROP_CHAR_CHAR_KERNING
,
4242 const uno::Reference
<beans::XPropertyState
> xShapePropertyState(xShapePropertySet
, uno::UNO_QUERY_THROW
);
4243 for ( const auto& eId
: eIds
)
4247 if ( bOnlyApplyCharHeight
&& eId
!= PROP_CHAR_HEIGHT
)
4250 const OUString
& sPropName
= getPropertyName(eId
);
4251 if ( beans::PropertyState_DEFAULT_VALUE
== xShapePropertyState
->getPropertyState(sPropName
) )
4253 const uno::Any aProp
= GetPropertyFromStyleSheet(eId
, pEntry
, /*bDocDefaults=*/true, /*bPara=*/true);
4254 if (aProp
.hasValue())
4257 xFramePropertySet
->setPropertyValue(sPropName
, aProp
);
4259 xShapePropertySet
->setPropertyValue(sPropName
, aProp
);
4263 catch (const uno::Exception
&)
4265 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "PushShapeContext() text stylesheet property exception" );
4270 catch (const uno::Exception
&)
4272 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "PushShapeContext()" );
4276 // A GroupShape doesn't implement text::XTextRange, but appending
4277 // an empty reference to the stacks still makes sense, because this
4278 // way bToRemove can be set, and we won't end up with duplicated
4279 // shapes for OLE objects.
4280 m_aTextAppendStack
.push(TextAppendContext(uno::Reference
<text::XTextAppend
>(xShape
, uno::UNO_QUERY
), uno::Reference
<text::XTextCursor
>()));
4281 uno::Reference
<text::XTextContent
> xTxtContent(xShape
, uno::UNO_QUERY
);
4282 m_aAnchoredStack
.push(AnchoredContext(xTxtContent
));
4284 else if (xSInfo
->supportsService("com.sun.star.drawing.OLE2Shape"))
4286 // OLE2Shape from oox should be converted to a TextEmbeddedObject for sw.
4287 m_aTextAppendStack
.push(TextAppendContext(uno::Reference
<text::XTextAppend
>(xShape
, uno::UNO_QUERY
), uno::Reference
<text::XTextCursor
>()));
4288 uno::Reference
<text::XTextContent
> xTextContent(xShape
, uno::UNO_QUERY
);
4289 m_aAnchoredStack
.push(AnchoredContext(xTextContent
));
4290 uno::Reference
<beans::XPropertySet
> xShapePropertySet(xShape
, uno::UNO_QUERY
);
4292 m_xEmbedded
.set(m_xTextFactory
->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW
);
4293 uno::Reference
<beans::XPropertySet
> xEmbeddedProperties(m_xEmbedded
, uno::UNO_QUERY_THROW
);
4294 xEmbeddedProperties
->setPropertyValue(getPropertyName(PROP_EMBEDDED_OBJECT
), xShapePropertySet
->getPropertyValue(getPropertyName(PROP_EMBEDDED_OBJECT
)));
4295 xEmbeddedProperties
->setPropertyValue(getPropertyName(PROP_ANCHOR_TYPE
), uno::Any(text::TextContentAnchorType_AS_CHARACTER
));
4296 // So that the original bitmap-only shape will be replaced by the embedded object.
4297 m_aAnchoredStack
.top().bToRemove
= true;
4298 m_aTextAppendStack
.pop();
4299 appendTextContent(m_xEmbedded
, uno::Sequence
<beans::PropertyValue
>());
4303 uno::Reference
<text::XTextRange
> xShapeTextRange(xShape
, uno::UNO_QUERY_THROW
);
4304 // Add the shape to the text append stack
4305 uno::Reference
<text::XTextAppend
> xShapeTextAppend(xShape
, uno::UNO_QUERY_THROW
);
4306 uno::Reference
<text::XTextCursor
> xTextCursor
;
4309 xTextCursor
= xShapeTextRange
->getText()->createTextCursorByRange(
4310 xShapeTextRange
->getStart());
4312 TextAppendContext
aContext(xShapeTextAppend
, xTextCursor
);
4313 m_aTextAppendStack
.push(aContext
);
4315 // Add the shape to the anchored objects stack
4316 uno::Reference
< text::XTextContent
> xTxtContent( xShape
, uno::UNO_QUERY_THROW
);
4317 m_aAnchoredStack
.push( AnchoredContext(xTxtContent
) );
4319 uno::Reference
< beans::XPropertySet
> xProps( xShape
, uno::UNO_QUERY_THROW
);
4321 TagLogger::getInstance().unoPropertySet(xProps
);
4323 text::TextContentAnchorType
nAnchorType(text::TextContentAnchorType_AT_PARAGRAPH
);
4324 xProps
->getPropertyValue(getPropertyName( PROP_ANCHOR_TYPE
)) >>= nAnchorType
;
4325 bool checkZOrderStatus
= false;
4326 if (xSInfo
->supportsService("com.sun.star.text.TextFrame"))
4328 SetIsTextFrameInserted(true);
4329 // Extract the special "btLr text frame" mode, requested by oox, if needed.
4330 // Extract vml ZOrder from FrameInteropGrabBag
4331 uno::Reference
<beans::XPropertySet
> xShapePropertySet(xShape
, uno::UNO_QUERY
);
4332 uno::Sequence
<beans::PropertyValue
> aGrabBag
;
4333 xShapePropertySet
->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag
;
4335 for (const auto& rProp
: std::as_const(aGrabBag
))
4337 if (rProp
.Name
== "VML-Z-ORDER")
4339 GraphicZOrderHelper
* pZOrderHelper
= m_rDMapper
.graphicZOrderHelper();
4340 sal_Int32
zOrder(0);
4341 rProp
.Value
>>= zOrder
;
4342 xShapePropertySet
->setPropertyValue( "ZOrder", uno::Any(pZOrderHelper
->findZOrder(zOrder
)));
4343 pZOrderHelper
->addItem(xShapePropertySet
, zOrder
);
4344 xShapePropertySet
->setPropertyValue(getPropertyName( PROP_OPAQUE
), uno::Any( zOrder
>= 0 ) );
4345 checkZOrderStatus
= true;
4347 else if ( rProp
.Name
== "TxbxHasLink" )
4349 //Chaining of textboxes will happen in ~DomainMapper_Impl
4350 //i.e when all the textboxes are read and all its attributes
4351 //have been set ( basically the Name/LinkedDisplayName )
4352 //which is set in Graphic Import.
4353 m_vTextFramesForChaining
.push_back(xShape
);
4357 uno::Reference
<text::XTextContent
> xTextContent(xShape
, uno::UNO_QUERY_THROW
);
4358 uno::Reference
<text::XTextRange
> xTextRange(xTextAppend
->createTextCursorByRange(xTextAppend
->getEnd()), uno::UNO_QUERY_THROW
);
4359 xTextAppend
->insertTextContent(xTextRange
, xTextContent
, false);
4361 uno::Reference
<beans::XPropertySet
> xPropertySet(xTextContent
, uno::UNO_QUERY
);
4362 // we need to re-set this value to xTextContent, then only values are preserved.
4363 xPropertySet
->setPropertyValue("FrameInteropGrabBag",uno::Any(aGrabBag
));
4365 else if (nAnchorType
== text::TextContentAnchorType_AS_CHARACTER
)
4367 // Fix spacing for as-character objects. If the paragraph has CT_Spacing_after set,
4368 // it needs to be set on the object too, as that's what object placement code uses.
4369 PropertyMapPtr paragraphContext
= GetTopContextOfType( CONTEXT_PARAGRAPH
);
4370 std::optional
<PropertyMap::Property
> aPropMargin
= paragraphContext
->getProperty(PROP_PARA_BOTTOM_MARGIN
);
4372 xProps
->setPropertyValue( getPropertyName( PROP_BOTTOM_MARGIN
), aPropMargin
->second
);
4376 uno::Reference
<beans::XPropertySet
> xShapePropertySet(xShape
, uno::UNO_QUERY
);
4377 uno::Sequence
<beans::PropertyValue
> aGrabBag
;
4378 xShapePropertySet
->getPropertyValue("InteropGrabBag") >>= aGrabBag
;
4379 for (const auto& rProp
: std::as_const(aGrabBag
))
4381 if (rProp
.Name
== "VML-Z-ORDER")
4383 GraphicZOrderHelper
* pZOrderHelper
= m_rDMapper
.graphicZOrderHelper();
4384 sal_Int32
zOrder(0);
4385 rProp
.Value
>>= zOrder
;
4386 xShapePropertySet
->setPropertyValue( "ZOrder", uno::Any(pZOrderHelper
->findZOrder(zOrder
)));
4387 pZOrderHelper
->addItem(xShapePropertySet
, zOrder
);
4388 xShapePropertySet
->setPropertyValue(getPropertyName( PROP_OPAQUE
), uno::Any( zOrder
>= 0 ) );
4389 checkZOrderStatus
= true;
4391 else if ( rProp
.Name
== "TxbxHasLink" )
4393 //Chaining of textboxes will happen in ~DomainMapper_Impl
4394 //i.e when all the textboxes are read and all its attributes
4395 //have been set ( basically the Name/LinkedDisplayName )
4396 //which is set in Graphic Import.
4397 m_vTextFramesForChaining
.push_back(xShape
);
4401 if(IsSdtEndBefore())
4403 uno::Reference
< beans::XPropertySetInfo
> xPropSetInfo
;
4404 if(xShapePropertySet
.is())
4406 xPropSetInfo
= xShapePropertySet
->getPropertySetInfo();
4407 if (xPropSetInfo
.is() && xPropSetInfo
->hasPropertyByName("InteropGrabBag"))
4409 uno::Sequence
<beans::PropertyValue
> aShapeGrabBag( comphelper::InitPropertySequence({
4410 { "SdtEndBefore", uno::Any(true) }
4412 xShapePropertySet
->setPropertyValue("InteropGrabBag",uno::Any(aShapeGrabBag
));
4417 if (!IsInHeaderFooter() && !checkZOrderStatus
)
4418 xProps
->setPropertyValue(
4419 getPropertyName( PROP_OPAQUE
),
4422 m_bParaChanged
= true;
4423 getTableManager().setIsInShape(true);
4425 catch ( const uno::Exception
& )
4427 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "Exception when adding shape");
4431 * Updating chart height and width after reading the actual values from wp:extent
4433 void DomainMapper_Impl::UpdateEmbeddedShapeProps(const uno::Reference
< drawing::XShape
> & xShape
)
4438 uno::Reference
<beans::XPropertySet
> xEmbeddedProperties(m_xEmbedded
, uno::UNO_QUERY_THROW
);
4439 awt::Size aSize
= xShape
->getSize( );
4440 xEmbeddedProperties
->setPropertyValue(getPropertyName(PROP_WIDTH
), uno::Any(sal_Int32(aSize
.Width
)));
4441 xEmbeddedProperties
->setPropertyValue(getPropertyName(PROP_HEIGHT
), uno::Any(sal_Int32(aSize
.Height
)));
4442 uno::Reference
<beans::XPropertySet
> const xShapeProps(xShape
, uno::UNO_QUERY
);
4443 // tdf#130782 copy a11y related properties
4444 xEmbeddedProperties
->setPropertyValue(getPropertyName(PROP_DESCRIPTION
),
4445 xShapeProps
->getPropertyValue(getPropertyName(PROP_DESCRIPTION
)));
4446 xEmbeddedProperties
->setPropertyValue(getPropertyName(PROP_TITLE
),
4447 xShapeProps
->getPropertyValue(getPropertyName(PROP_TITLE
)));
4448 uno::Reference
<container::XNamed
> const xEmbedName(m_xEmbedded
, uno::UNO_QUERY
);
4449 uno::Reference
<container::XNamed
> const xShapeName(xShape
, uno::UNO_QUERY
);
4450 OUString
const name(xShapeName
->getName());
4451 if (!name
.isEmpty()) // setting empty name will throw
4455 xEmbedName
->setName(name
);
4457 catch (uno::RuntimeException
const&)
4459 // ignore - document may contain duplicates (testchartoleobjectembeddings.docx)
4464 void DomainMapper_Impl::PopShapeContext()
4466 if (hasTableManager())
4468 getTableManager().endLevel();
4471 if ( m_aAnchoredStack
.empty() )
4474 // For OLE object replacement shape, the text append context was already removed
4475 // or the OLE object couldn't be inserted.
4476 if ( !m_aAnchoredStack
.top().bToRemove
)
4478 RemoveLastParagraph();
4479 if (!m_aTextAppendStack
.empty())
4480 m_aTextAppendStack
.pop();
4483 uno::Reference
< text::XTextContent
> xObj
= m_aAnchoredStack
.top( ).xTextContent
;
4486 appendTextContent( xObj
, uno::Sequence
< beans::PropertyValue
>() );
4488 catch ( const uno::RuntimeException
& )
4490 // this is normal: the shape is already attached
4493 const uno::Reference
<drawing::XShape
> xShape( xObj
, uno::UNO_QUERY_THROW
);
4494 // Remove the shape if required (most likely replacement shape for OLE object)
4495 // or anchored to a discarded header or footer
4496 if ( m_aAnchoredStack
.top().bToRemove
|| m_bDiscardHeaderFooter
)
4500 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(m_xTextDocument
, uno::UNO_QUERY_THROW
);
4501 uno::Reference
<drawing::XDrawPage
> xDrawPage
= xDrawPageSupplier
->getDrawPage();
4502 if ( xDrawPage
.is() )
4503 xDrawPage
->remove( xShape
);
4505 catch( const uno::Exception
& )
4510 // Relative width calculations deferred until section's margins are defined.
4511 // Being cautious: only deferring undefined/minimum-width shapes in order to avoid causing potential regressions
4512 css::awt::Size aShapeSize
;
4515 aShapeSize
= xShape
->getSize();
4517 catch (const css::uno::RuntimeException
& e
)
4519 // May happen e.g. when text frame has no frame format
4520 // See sw/qa/extras/ooxmlimport/data/n779627.docx
4521 SAL_WARN("writerfilter.dmapper", "getSize failed. " << e
.Message
);
4523 if( aShapeSize
.Width
<= 2 )
4525 const uno::Reference
<beans::XPropertySet
> xShapePropertySet( xShape
, uno::UNO_QUERY
);
4526 SectionPropertyMap
* pSectionContext
= GetSectionContext();
4527 if ( pSectionContext
&& (!hasTableManager() || !getTableManager().isInTable()) &&
4528 xShapePropertySet
->getPropertySetInfo()->hasPropertyByName(getPropertyName(PROP_RELATIVE_WIDTH
)) )
4530 pSectionContext
->addRelativeWidthShape(xShape
);
4534 m_aAnchoredStack
.pop();
4537 bool DomainMapper_Impl::IsSdtEndBefore()
4539 bool bIsSdtEndBefore
= false;
4540 PropertyMapPtr pContext
= GetTopContextOfType(CONTEXT_CHARACTER
);
4543 const uno::Sequence
< beans::PropertyValue
> currentCharProps
= pContext
->GetPropertyValues();
4544 for (const auto& rCurrentCharProp
: currentCharProps
)
4546 if (rCurrentCharProp
.Name
== "CharInteropGrabBag")
4548 uno::Sequence
<beans::PropertyValue
> aCharGrabBag
;
4549 rCurrentCharProp
.Value
>>= aCharGrabBag
;
4550 for (const auto& rProp
: std::as_const(aCharGrabBag
))
4552 if(rProp
.Name
== "SdtEndBefore")
4554 rProp
.Value
>>= bIsSdtEndBefore
;
4560 return bIsSdtEndBefore
;
4563 bool DomainMapper_Impl::IsDiscardHeaderFooter() const
4565 return m_bDiscardHeaderFooter
;
4568 // called from TableManager::closeCell()
4569 void DomainMapper_Impl::ClearPreviousParagraph()
4571 // in table cells, set bottom auto margin of last paragraph to 0, except in paragraphs with numbering
4572 if ((m_nTableDepth
== (m_nTableCellDepth
+ 1))
4573 && m_xPreviousParagraph
.is()
4574 && hasTableManager() && getTableManager().isCellLastParaAfterAutospacing())
4576 uno::Reference
<container::XNamed
> xPreviousNumberingRules(m_xPreviousParagraph
->getPropertyValue("NumberingRules"), uno::UNO_QUERY
);
4577 if ( !xPreviousNumberingRules
.is() || xPreviousNumberingRules
->getName().isEmpty() )
4578 m_xPreviousParagraph
->setPropertyValue("ParaBottomMargin", uno::Any(static_cast<sal_Int32
>(0)));
4581 m_xPreviousParagraph
.clear();
4583 // next table paragraph will be first paragraph in a cell
4584 m_bFirstParagraphInCell
= true;
4587 void DomainMapper_Impl::HandleAltChunk(const OUString
& rStreamName
)
4591 // Create the import filter.
4592 uno::Reference
<lang::XMultiServiceFactory
> xMultiServiceFactory(
4593 comphelper::getProcessServiceFactory());
4594 uno::Reference
<uno::XInterface
> xDocxFilter
4595 = xMultiServiceFactory
->createInstance("com.sun.star.comp.Writer.WriterFilter");
4597 // Set the target document.
4598 uno::Reference
<document::XImporter
> xImporter(xDocxFilter
, uno::UNO_QUERY
);
4599 xImporter
->setTargetDocument(m_xTextDocument
);
4601 // Set the import parameters.
4602 uno::Reference
<embed::XHierarchicalStorageAccess
> xStorageAccess(m_xDocumentStorage
,
4604 if (!xStorageAccess
.is())
4608 // Turn the ZIP stream into a seekable one, as the importer only works with such streams.
4609 uno::Reference
<io::XStream
> xStream
= xStorageAccess
->openStreamElementByHierarchicalName(
4610 rStreamName
, embed::ElementModes::READ
);
4611 std::unique_ptr
<SvStream
> pStream
= utl::UcbStreamHelper::CreateStream(xStream
, true);
4612 SvMemoryStream aMemory
;
4613 aMemory
.WriteStream(*pStream
);
4614 uno::Reference
<io::XStream
> xInputStream
= new utl::OStreamWrapper(aMemory
);
4615 // Not handling AltChunk during paste for now.
4616 uno::Reference
<text::XTextRange
> xInsertTextRange
= GetCurrentXText()->getEnd();
4617 uno::Reference
<text::XTextRange
> xSectionStartingRange
;
4618 SectionPropertyMap
* pSectionContext
= GetSectionContext();
4619 if (pSectionContext
)
4621 xSectionStartingRange
= pSectionContext
->GetStartingRange();
4623 uno::Sequence
<beans::PropertyValue
> aDescriptor(comphelper::InitPropertySequence({
4624 { "InputStream", uno::Any(xInputStream
) },
4625 { "InsertMode", uno::Any(true) },
4626 { "TextInsertModeRange", uno::Any(xInsertTextRange
) },
4627 { "AltChunkMode", uno::Any(true) },
4628 { "AltChunkStartingRange", uno::Any(xSectionStartingRange
) },
4631 // Do the actual import.
4632 uno::Reference
<document::XFilter
> xFilter(xDocxFilter
, uno::UNO_QUERY
);
4633 xFilter
->filter(aDescriptor
);
4635 catch (const uno::Exception
& rException
)
4637 SAL_WARN("writerfilter", "DomainMapper_Impl::HandleAltChunk: failed to handle alt chunk: "
4638 << rException
.Message
);
4642 void DomainMapper_Impl::HandlePTab(sal_Int32 nAlignment
)
4644 // We only handle the case when the line already has content, so the left-aligned ptab is
4645 // equivalent to a line break.
4646 if (nAlignment
!= NS_ooxml::LN_Value_ST_PTabAlignment_left
)
4651 if (m_aTextAppendStack
.empty())
4656 uno::Reference
<text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
4657 if (!xTextAppend
.is())
4662 uno::Reference
<css::text::XTextRange
> xInsertPosition
4663 = m_aTextAppendStack
.top().xInsertPosition
;
4664 if (!xInsertPosition
.is())
4666 xInsertPosition
= xTextAppend
->getEnd();
4668 uno::Reference
<text::XTextCursor
> xCursor
4669 = xTextAppend
->createTextCursorByRange(xInsertPosition
);
4671 // Assume that we just inserted a tab character.
4672 xCursor
->goLeft(1, true);
4673 if (xCursor
->getString() != "\t")
4678 // Assume that there is some content before the tab character.
4679 uno::Reference
<text::XParagraphCursor
> xParagraphCursor(xCursor
, uno::UNO_QUERY
);
4680 if (!xParagraphCursor
.is())
4685 xCursor
->collapseToStart();
4686 xParagraphCursor
->gotoStartOfParagraph(true);
4687 if (xCursor
->isCollapsed())
4692 // Then select the tab again and replace with a line break.
4693 xCursor
->collapseToEnd();
4694 xCursor
->goRight(1, true);
4695 xTextAppend
->insertControlCharacter(xCursor
, text::ControlCharacter::LINE_BREAK
, true);
4698 void DomainMapper_Impl::HandleLineBreakClear(sal_Int32 nClear
)
4702 case NS_ooxml::LN_Value_ST_BrClear_left
:
4703 // SwLineBreakClear::LEFT
4704 m_oLineBreakClear
= 1;
4706 case NS_ooxml::LN_Value_ST_BrClear_right
:
4707 // SwLineBreakClear::RIGHT
4708 m_oLineBreakClear
= 2;
4710 case NS_ooxml::LN_Value_ST_BrClear_all
:
4711 // SwLineBreakClear::ALL
4712 m_oLineBreakClear
= 3;
4717 void DomainMapper_Impl::HandleLineBreak(const PropertyMapPtr
& pPropertyMap
)
4719 if (!m_oLineBreakClear
.has_value())
4721 appendTextPortion("\n", pPropertyMap
);
4725 if (GetTextFactory().is())
4727 uno::Reference
<text::XTextContent
> xLineBreak(
4728 GetTextFactory()->createInstance("com.sun.star.text.LineBreak"), uno::UNO_QUERY
);
4729 uno::Reference
<beans::XPropertySet
> xLineBreakProps(xLineBreak
, uno::UNO_QUERY
);
4730 xLineBreakProps
->setPropertyValue("Clear", uno::Any(*m_oLineBreakClear
));
4731 appendTextContent(xLineBreak
, pPropertyMap
->GetPropertyValues());
4733 m_oLineBreakClear
.reset();
4736 static sal_Int16
lcl_ParseNumberingType( std::u16string_view rCommand
)
4738 sal_Int16 nRet
= style::NumberingType::PAGE_DESCRIPTOR
;
4740 // The command looks like: " PAGE \* Arabic "
4741 // tdf#132185: but may as well be "PAGE \* Arabic"
4743 constexpr OUStringLiteral
rSeparator(u
"\\* ");
4744 if (size_t nStartIndex
= rCommand
.find(rSeparator
); nStartIndex
!= std::u16string_view::npos
)
4746 sal_Int32 nStartIndex2
= nStartIndex
+ rSeparator
.getLength();
4747 sNumber
= o3tl::getToken(rCommand
, 0, ' ', nStartIndex2
);
4750 if( !sNumber
.isEmpty() )
4752 //todo: might make sense to hash this list, too
4753 struct NumberingPairs
4755 const char* cWordName
;
4758 static const NumberingPairs aNumberingPairs
[] =
4760 {"Arabic", style::NumberingType::ARABIC
}
4761 ,{"ROMAN", style::NumberingType::ROMAN_UPPER
}
4762 ,{"roman", style::NumberingType::ROMAN_LOWER
}
4763 ,{"ALPHABETIC", style::NumberingType::CHARS_UPPER_LETTER
}
4764 ,{"alphabetic", style::NumberingType::CHARS_LOWER_LETTER
}
4765 ,{"CircleNum", style::NumberingType::CIRCLE_NUMBER
}
4766 ,{"ThaiArabic", style::NumberingType::CHARS_THAI
}
4767 ,{"ThaiCardText", style::NumberingType::CHARS_THAI
}
4768 ,{"ThaiLetter", style::NumberingType::CHARS_THAI
}
4769 // ,{"SBCHAR", style::NumberingType::}
4770 // ,{"DBCHAR", style::NumberingType::}
4771 // ,{"DBNUM1", style::NumberingType::}
4772 // ,{"DBNUM2", style::NumberingType::}
4773 // ,{"DBNUM3", style::NumberingType::}
4774 // ,{"DBNUM4", style::NumberingType::}
4775 ,{"Aiueo", style::NumberingType::AIU_FULLWIDTH_JA
}
4776 ,{"Iroha", style::NumberingType::IROHA_FULLWIDTH_JA
}
4777 // ,{"ZODIAC1", style::NumberingType::}
4778 // ,{"ZODIAC2", style::NumberingType::}
4779 // ,{"ZODIAC3", style::NumberingType::}
4780 // ,{"CHINESENUM1", style::NumberingType::}
4781 // ,{"CHINESENUM2", style::NumberingType::}
4782 // ,{"CHINESENUM3", style::NumberingType::}
4783 ,{"ArabicAlpha", style::NumberingType::CHARS_ARABIC
}
4784 ,{"ArabicAbjad", style::NumberingType::FULLWIDTH_ARABIC
}
4785 ,{"Ganada", style::NumberingType::HANGUL_JAMO_KO
}
4786 ,{"Chosung", style::NumberingType::HANGUL_SYLLABLE_KO
}
4787 ,{"KoreanCounting", style::NumberingType::NUMBER_HANGUL_KO
}
4788 ,{"KoreanLegal", style::NumberingType::NUMBER_LEGAL_KO
}
4789 ,{"KoreanDigital", style::NumberingType::NUMBER_DIGITAL_KO
}
4790 ,{"KoreanDigital2", style::NumberingType::NUMBER_DIGITAL2_KO
}
4792 style::NumberingType::
4794 CHARS_UPPER_LETTER_N
4795 CHARS_LOWER_LETTER_N
4804 NUMBER_TRADITIONAL_JA
4811 HANGUL_CIRCLED_JAMO_KO
4812 HANGUL_CIRCLED_SYLLABLE_KO
4818 CHARS_CYRILLIC_UPPER_LETTER_BG
4819 CHARS_CYRILLIC_LOWER_LETTER_BG
4820 CHARS_CYRILLIC_UPPER_LETTER_N_BG
4821 CHARS_CYRILLIC_LOWER_LETTER_N_BG
4822 CHARS_CYRILLIC_UPPER_LETTER_RU
4823 CHARS_CYRILLIC_LOWER_LETTER_RU
4824 CHARS_CYRILLIC_UPPER_LETTER_N_RU
4825 CHARS_CYRILLIC_LOWER_LETTER_N_RU
4826 CHARS_CYRILLIC_UPPER_LETTER_SR
4827 CHARS_CYRILLIC_LOWER_LETTER_SR
4828 CHARS_CYRILLIC_UPPER_LETTER_N_SR
4829 CHARS_CYRILLIC_LOWER_LETTER_N_SR
4830 CHARS_CYRILLIC_UPPER_LETTER_UK
4831 CHARS_CYRILLIC_LOWER_LETTER_UK
4832 CHARS_CYRILLIC_UPPER_LETTER_N_UK
4833 CHARS_CYRILLIC_LOWER_LETTER_N_UK*/
4836 for(const NumberingPairs
& rNumberingPair
: aNumberingPairs
)
4838 if( /*sCommand*/sNumber
.equalsAscii(rNumberingPair
.cWordName
))
4840 nRet
= rNumberingPair
.nType
;
4850 static OUString
lcl_ParseFormat( const OUString
& rCommand
)
4852 // The command looks like: " DATE \@"dd MMMM yyyy" or "09/02/2014"
4854 sal_Int32 delimPos
= rCommand
.indexOf("\\@");
4857 // Remove whitespace permitted by standard between \@ and "
4858 const sal_Int32 nQuoteIndex
= rCommand
.indexOf('\"');
4859 if (nQuoteIndex
!= -1)
4861 sal_Int32 wsChars
= nQuoteIndex
- delimPos
- 2;
4862 command
= rCommand
.replaceAt(delimPos
+2, wsChars
, u
"");
4866 // turn date \@ MM into date \@"MM"
4867 command
= OUString::Concat(rCommand
.subView(0, delimPos
+ 2)) + "\"" + o3tl::trim(rCommand
.subView(delimPos
+ 2)) + "\"";
4869 return OUString(msfilter::util::findQuotedText(command
, u
"\\@\"", '\"'));
4874 /*-------------------------------------------------------------------------
4875 extract a parameter (with or without quotes) between the command and the following backslash
4876 -----------------------------------------------------------------------*/
4877 static OUString
lcl_ExtractToken(std::u16string_view rCommand
,
4878 size_t & rIndex
, bool & rHaveToken
, bool & rIsSwitch
)
4883 OUStringBuffer token
;
4884 bool bQuoted(false);
4885 for (; rIndex
< rCommand
.size(); ++rIndex
)
4887 sal_Unicode
const currentChar(rCommand
[rIndex
]);
4888 switch (currentChar
)
4892 if (rIndex
== rCommand
.size() - 1)
4894 SAL_INFO("writerfilter.dmapper", "field: trailing escape");
4898 sal_Unicode
const nextChar(rCommand
[rIndex
+1]);
4899 if (bQuoted
|| '\\' == nextChar
)
4901 ++rIndex
; // read 2 chars
4902 token
.append(nextChar
);
4904 else // field switch (case insensitive)
4907 if (token
.isEmpty())
4910 rIndex
+= 2; // read 2 chars
4911 return OUString(rCommand
.substr(rIndex
- 2, 2)).toAsciiUpperCase();
4914 { // leave rIndex, read it again next time
4915 return token
.makeStringAndClear();
4921 if (bQuoted
|| !token
.isEmpty())
4928 return token
.makeStringAndClear();
4942 if (!token
.isEmpty())
4946 return token
.makeStringAndClear();
4951 if (token
.isEmpty())
4961 token
.append(currentChar
);
4965 assert(rIndex
== rCommand
.size());
4968 // MS Word allows this, so just emit a debug message
4969 SAL_INFO("writerfilter.dmapper",
4970 "field argument with unterminated quote");
4972 rHaveToken
= !token
.isEmpty();
4973 return token
.makeStringAndClear();
4976 std::tuple
<OUString
, std::vector
<OUString
>, std::vector
<OUString
> > splitFieldCommand(std::u16string_view rCommand
)
4979 std::vector
<OUString
> arguments
;
4980 std::vector
<OUString
> switches
;
4981 size_t nStartIndex(0);
4982 // tdf#54584: Field may be prepended by a backslash
4983 // This is not an escapement, but already escaped literal "\"
4984 // MS Word allows this, so just skip it
4985 if ((rCommand
.size() >= nStartIndex
+ 2) &&
4986 (rCommand
[nStartIndex
] == L
'\\') &&
4987 (rCommand
[nStartIndex
+ 1] != L
'\\') &&
4988 (rCommand
[nStartIndex
+ 1] != L
' '))
4997 OUString
const token
=
4998 lcl_ExtractToken(rCommand
, nStartIndex
, bHaveToken
, bIsSwitch
);
4999 assert(nStartIndex
<= rCommand
.size());
5002 if (sType
.isEmpty())
5004 sType
= token
.toAsciiUpperCase();
5006 else if (bIsSwitch
|| !switches
.empty())
5008 switches
.push_back(token
);
5012 arguments
.push_back(token
);
5015 } while (nStartIndex
< rCommand
.size());
5017 return std::make_tuple(sType
, arguments
, switches
);
5020 static OUString
lcl_ExtractVariableAndHint( std::u16string_view rCommand
, OUString
& rHint
)
5022 // the first word after "ASK " is the variable
5023 // the text after the variable and before a '\' is the hint
5024 // if no hint is set the variable is used as hint
5025 // the quotes of the hint have to be removed
5026 size_t nIndex
= rCommand
.find( ' ', 2); //find last space after 'ASK'
5027 if (nIndex
== std::u16string_view::npos
)
5029 while (nIndex
< rCommand
.size() && rCommand
[nIndex
] == ' ')
5031 std::u16string_view
sShortCommand( rCommand
.substr( nIndex
) ); //cut off the " ASK "
5033 sShortCommand
= o3tl::getToken(sShortCommand
, 0, '\\');
5034 sal_Int32 nIndex2
= 0;
5035 std::u16string_view sRet
= o3tl::getToken(sShortCommand
, 0, ' ', nIndex2
);
5037 rHint
= sShortCommand
.substr( nIndex2
);
5038 if( rHint
.isEmpty() )
5040 return OUString(sRet
);
5043 static size_t nextCode(std::u16string_view rCommand
, size_t pos
)
5045 bool inQuotes
= false;
5046 for (; pos
< rCommand
.size(); ++pos
)
5048 switch (rCommand
[pos
])
5051 inQuotes
= !inQuotes
;
5060 return std::u16string_view::npos
;
5063 // Returns the position of the field code
5064 static size_t findCode(std::u16string_view rCommand
, sal_Unicode cSwitch
)
5066 for (size_t i
= nextCode(rCommand
, 0); i
< rCommand
.size(); i
= nextCode(rCommand
, i
))
5067 if (rCommand
[i
] == cSwitch
)
5070 return std::u16string_view::npos
;
5073 static bool lcl_FindInCommand(
5074 std::u16string_view rCommand
,
5075 sal_Unicode cSwitch
,
5078 if (size_t i
= findCode(rCommand
, cSwitch
); i
< rCommand
.size())
5081 size_t next
= nextCode(rCommand
, i
);
5082 if (next
< rCommand
.size())
5083 --next
; // get back before the next '\\'
5084 rValue
= o3tl::trim(rCommand
.substr(i
, next
- i
));
5091 static OUString
lcl_trim(std::u16string_view sValue
)
5093 // it seems, all kind of quotation marks are allowed around index type identifiers
5094 // TODO apply this on bookmarks, too, if needed
5095 return OUString(o3tl::trim(sValue
)).replaceAll("\"","").replaceAll(u
"“", "").replaceAll(u
"”", "");
5098 /*-------------------------------------------------------------------------
5099 extract the number format from the command and apply the resulting number
5100 format to the XPropertySet
5101 -----------------------------------------------------------------------*/
5102 void DomainMapper_Impl::SetNumberFormat( const OUString
& rCommand
,
5103 uno::Reference
< beans::XPropertySet
> const& xPropertySet
,
5104 bool const bDetectFormat
)
5106 OUString sFormatString
= lcl_ParseFormat( rCommand
);
5107 // find \h - hijri/luna calendar todo: what about saka/era calendar?
5108 bool bHijri
= 0 < rCommand
.indexOf("\\h ");
5109 lang::Locale aUSLocale
;
5110 aUSLocale
.Language
= "en";
5111 aUSLocale
.Country
= "US";
5113 lang::Locale aCurrentLocale
;
5114 GetAnyProperty(PROP_CHAR_LOCALE
, GetTopContext()) >>= aCurrentLocale
;
5116 if (sFormatString
.isEmpty())
5118 // No format specified. MS Word uses different formats depending on w:lang,
5119 // "M/d/yyyy h:mm:ss AM/PM" for en-US, and "dd/MM/yyyy hh:mm:ss AM/PM" for en-GB.
5120 // ALSO SEE: ww8par5's GetWordDefaultDateStringAsUS.
5121 sal_Int32 nPos
= rCommand
.indexOf(" \\");
5122 OUString sCommand
= nPos
== -1 ? rCommand
.trim()
5123 : OUString(o3tl::trim(rCommand
.subView(0, nPos
)));
5124 if (sCommand
== "CREATEDATE" || sCommand
== "PRINTDATE" || sCommand
== "SAVEDATE")
5128 css::uno::Reference
<css::i18n::XNumberFormatCode
> const& xNumberFormatCode
=
5129 i18n::NumberFormatMapper::create(m_xComponentContext
);
5130 sFormatString
= xNumberFormatCode
->getFormatCode(
5131 css::i18n::NumberFormatIndex::DATE_SYSTEM_SHORT
, aCurrentLocale
).Code
;
5132 nPos
= sFormatString
.indexOf("YYYY");
5134 sFormatString
= sFormatString
.replaceFirst("YY", "YYYY");
5135 if (aCurrentLocale
== aUSLocale
)
5136 sFormatString
+= " h:mm:ss AM/PM";
5138 sFormatString
+= " hh:mm:ss AM/PM";
5140 catch(const uno::Exception
&)
5142 DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
5146 OUString sFormat
= ConversionHelper::ConvertMSFormatStringToSO( sFormatString
, aCurrentLocale
, bHijri
);
5147 //get the number formatter and convert the string to a format value
5151 uno::Reference
< util::XNumberFormatsSupplier
> xNumberSupplier( m_xTextDocument
, uno::UNO_QUERY_THROW
);
5154 uno::Reference
< util::XNumberFormatter
> xFormatter(util::NumberFormatter::create(m_xComponentContext
), uno::UNO_QUERY_THROW
);
5155 xFormatter
->attachNumberFormatsSupplier( xNumberSupplier
);
5156 nKey
= xFormatter
->detectNumberFormat( 0, rCommand
);
5160 nKey
= xNumberSupplier
->getNumberFormats()->addNewConverted( sFormat
, aUSLocale
, aCurrentLocale
);
5162 xPropertySet
->setPropertyValue(
5163 getPropertyName(PROP_NUMBER_FORMAT
),
5166 catch(const uno::Exception
&)
5171 static uno::Any
lcl_getGrabBagValue( const uno::Sequence
<beans::PropertyValue
>& grabBag
, OUString
const & name
)
5173 auto pProp
= std::find_if(grabBag
.begin(), grabBag
.end(),
5174 [&name
](const beans::PropertyValue
& rProp
) { return rProp
.Name
== name
; });
5175 if (pProp
!= grabBag
.end())
5176 return pProp
->Value
;
5180 //Link the text frames.
5181 void DomainMapper_Impl::ChainTextFrames()
5183 //can't link textboxes if there are not even two of them...
5184 if( 2 > m_vTextFramesForChaining
.size() )
5187 struct TextFramesForChaining
{
5188 css::uno::Reference
< css::drawing::XShape
> xShape
;
5191 OUString s_mso_next_textbox
;
5193 TextFramesForChaining() : nId(0), nSeq(0) {}
5195 typedef std::map
<OUString
, TextFramesForChaining
> ChainMap
;
5199 ChainMap aTextFramesForChainingHelper
;
5200 ::std::vector
<TextFramesForChaining
> chainingWPS
;
5201 OUString
sChainNextName("ChainNextName");
5203 //learn about ALL of the textboxes and their chaining values first - because frames are processed in no specific order.
5204 for( const auto& rTextFrame
: m_vTextFramesForChaining
)
5206 uno::Reference
<text::XTextContent
> xTextContent(rTextFrame
, uno::UNO_QUERY_THROW
);
5207 uno::Reference
<beans::XPropertySet
> xPropertySet(xTextContent
, uno::UNO_QUERY
);
5208 uno::Reference
<beans::XPropertySetInfo
> xPropertySetInfo
;
5209 if( xPropertySet
.is() )
5210 xPropertySetInfo
= xPropertySet
->getPropertySetInfo();
5211 uno::Sequence
<beans::PropertyValue
> aGrabBag
;
5212 uno::Reference
<lang::XServiceInfo
> xServiceInfo(xPropertySet
, uno::UNO_QUERY
);
5214 TextFramesForChaining aChainStruct
;
5215 OUString sShapeName
;
5216 OUString sLinkChainName
;
5218 //The chaining name and the shape name CAN be different in .docx.
5219 //MUST use LinkDisplayName/ChainName as the shape name for establishing links.
5220 if ( xServiceInfo
->supportsService("com.sun.star.text.TextFrame") )
5222 xPropertySet
->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag
;
5223 xPropertySet
->getPropertyValue("LinkDisplayName") >>= sShapeName
;
5227 xPropertySet
->getPropertyValue("InteropGrabBag") >>= aGrabBag
;
5228 xPropertySet
->getPropertyValue("ChainName") >>= sShapeName
;
5231 lcl_getGrabBagValue( aGrabBag
, "Txbx-Id") >>= aChainStruct
.nId
;
5232 lcl_getGrabBagValue( aGrabBag
, "Txbx-Seq") >>= aChainStruct
.nSeq
;
5233 lcl_getGrabBagValue( aGrabBag
, "LinkChainName") >>= sLinkChainName
;
5234 lcl_getGrabBagValue( aGrabBag
, "mso-next-textbox") >>= aChainStruct
.s_mso_next_textbox
;
5236 //Sometimes the shape names have not been imported. If not, we may have a fallback name.
5237 //Set name later, only if required for linking.
5238 aChainStruct
.shapeName
= sShapeName
;
5240 if (!sLinkChainName
.isEmpty())
5242 aChainStruct
.xShape
= rTextFrame
;
5243 aTextFramesForChainingHelper
[sLinkChainName
] = aChainStruct
;
5245 if (aChainStruct
.s_mso_next_textbox
.isEmpty())
5246 { // no VML chaining => try to chain DrawingML via IDs
5247 aChainStruct
.xShape
= rTextFrame
;
5248 if (!sLinkChainName
.isEmpty())
5249 { // for member of group shapes, TestTdf73499
5250 aChainStruct
.shapeName
= sLinkChainName
;
5252 chainingWPS
.emplace_back(aChainStruct
);
5256 //if mso-next-textbox tags are provided, create those vml-style links first. Afterwards we will make dml-style id/seq links.
5257 for (auto& msoItem
: aTextFramesForChainingHelper
)
5259 //if no mso-next-textbox, we are done.
5260 //if it points to itself, we are done.
5261 if( !msoItem
.second
.s_mso_next_textbox
.isEmpty()
5262 && msoItem
.second
.s_mso_next_textbox
!= msoItem
.first
)
5264 ChainMap::iterator nextFinder
=aTextFramesForChainingHelper
.find(msoItem
.second
.s_mso_next_textbox
);
5265 if( nextFinder
!= aTextFramesForChainingHelper
.end() )
5267 //if the frames have no name yet, then set them. LinkDisplayName / ChainName are read-only.
5268 if (msoItem
.second
.shapeName
.isEmpty())
5270 uno::Reference
< container::XNamed
> xNamed( msoItem
.second
.xShape
, uno::UNO_QUERY
);
5273 xNamed
->setName( msoItem
.first
);
5274 msoItem
.second
.shapeName
= msoItem
.first
;
5277 if (nextFinder
->second
.shapeName
.isEmpty())
5279 uno::Reference
< container::XNamed
> xNamed( nextFinder
->second
.xShape
, uno::UNO_QUERY
);
5282 xNamed
->setName( nextFinder
->first
);
5283 nextFinder
->second
.shapeName
= msoItem
.first
;
5287 uno::Reference
<text::XTextContent
> xTextContent(msoItem
.second
.xShape
, uno::UNO_QUERY_THROW
);
5288 uno::Reference
<beans::XPropertySet
> xPropertySet(xTextContent
, uno::UNO_QUERY
);
5290 //The reverse chaining happens automatically, so only one direction needs to be set
5291 xPropertySet
->setPropertyValue(sChainNextName
, uno::Any(nextFinder
->second
.shapeName
));
5293 //the last item in an mso-next-textbox chain is indistinguishable from id/seq items. Now that it is handled, remove it.
5294 if( nextFinder
->second
.s_mso_next_textbox
.isEmpty() )
5295 aTextFramesForChainingHelper
.erase(nextFinder
->first
);
5300 //TODO: Perhaps allow reverse sequences when mso-layout-flow-alt = "bottom-to-top"
5301 const sal_Int32 nDirection
= 1;
5303 //Finally - go through and attach the chains based on matching ID and incremented sequence number (dml-style).
5304 for (const auto& rOuter
: chainingWPS
)
5306 for (const auto& rInner
: chainingWPS
)
5308 if (rInner
.nId
== rOuter
.nId
)
5310 if (rInner
.nSeq
== (rOuter
.nSeq
+ nDirection
))
5312 uno::Reference
<text::XTextContent
> const xTextContent(rOuter
.xShape
, uno::UNO_QUERY_THROW
);
5313 uno::Reference
<beans::XPropertySet
> xPropertySet(xTextContent
, uno::UNO_QUERY
);
5315 //The reverse chaining happens automatically, so only one direction needs to be set
5316 xPropertySet
->setPropertyValue(sChainNextName
, uno::Any(rInner
.shapeName
));
5317 break ; //there cannot be more than one next frame
5322 m_vTextFramesForChaining
.clear(); //clear the vector
5324 catch (const uno::Exception
&)
5326 DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
5330 void DomainMapper_Impl::PushTextBoxContent()
5337 uno::Reference
<text::XTextFrame
> xTBoxFrame(
5338 m_xTextFactory
->createInstance("com.sun.star.text.TextFrame"), uno::UNO_QUERY_THROW
);
5339 uno::Reference
<container::XNamed
>(xTBoxFrame
, uno::UNO_QUERY_THROW
)
5340 ->setName("textbox" + OUString::number(m_xPendingTextBoxFrames
.size() + 1));
5341 uno::Reference
<text::XTextAppendAndConvert
>(m_aTextAppendStack
.top().xTextAppend
,
5342 uno::UNO_QUERY_THROW
)
5343 ->appendTextContent(xTBoxFrame
, beans::PropertyValues());
5344 m_xPendingTextBoxFrames
.push(xTBoxFrame
);
5346 m_aTextAppendStack
.push(TextAppendContext(uno::Reference
<text::XTextAppend
>(xTBoxFrame
, uno::UNO_QUERY_THROW
), {}));
5347 m_bIsInTextBox
= true;
5349 appendTableManager();
5350 appendTableHandler();
5351 getTableManager().startLevel();
5353 catch (uno::Exception
& e
)
5355 SAL_WARN("writerfilter.dmapper", "Exception during creating textbox (" + e
.Message
+ ")!");
5359 void DomainMapper_Impl::PopTextBoxContent()
5361 if (!m_bIsInTextBox
|| m_xPendingTextBoxFrames
.empty())
5364 if (uno::Reference
<text::XTextFrame
>(m_aTextAppendStack
.top().xTextAppend
, uno::UNO_QUERY
).is())
5366 if (hasTableManager())
5368 getTableManager().endLevel();
5371 RemoveLastParagraph();
5373 m_aTextAppendStack
.pop();
5374 m_bIsInTextBox
= false;
5378 void DomainMapper_Impl::AttachTextBoxContentToShape(css::uno::Reference
<css::drawing::XShape
> xShape
)
5380 // Without textbox or shape pointless to continue
5381 if (m_xPendingTextBoxFrames
.empty() || !xShape
)
5384 uno::Reference
< drawing::XShapes
>xGroup(xShape
, uno::UNO_QUERY
);
5385 uno::Reference
< beans::XPropertySet
>xProps(xShape
, uno::UNO_QUERY
);
5387 // If this is a group go inside
5389 for (sal_Int32 i
= 0; i
< xGroup
->getCount(); ++i
)
5390 AttachTextBoxContentToShape(
5391 uno::Reference
<drawing::XShape
>(xGroup
->getByIndex(i
), uno::UNO_QUERY
));
5393 // if this shape has to be a textbox, attach the frame
5394 if (!xProps
->getPropertyValue("TextBox").get
<bool>())
5397 // if this is a textbox there must be a waiting frame
5398 auto xTextBox
= m_xPendingTextBoxFrames
.front();
5402 // Pop the pending frames
5403 m_xPendingTextBoxFrames
.pop();
5405 // Attach the textbox to the shape
5408 xProps
->setPropertyValue("TextBoxContent", uno::Any(xTextBox
));
5412 SAL_WARN("writerfilter.dmapper", "Exception while trying to attach textboxes!");
5416 // If attaching is successful, then do the linking
5419 // Get the name of the textbox
5420 OUString sTextBoxName
;
5421 uno::Reference
<container::XNamed
> xName(xTextBox
, uno::UNO_QUERY
);
5422 if (xName
&& !xName
->getName().isEmpty())
5423 sTextBoxName
= xName
->getName();
5425 // Try to get the grabbag
5426 uno::Sequence
<beans::PropertyValue
> aOldGrabBagSeq
;
5427 if (xProps
->getPropertySetInfo()->hasPropertyByName("InteropGrabBag"))
5428 xProps
->getPropertyValue("InteropGrabBag") >>= aOldGrabBagSeq
;
5430 // If the grabbag successfully get...
5431 if (!aOldGrabBagSeq
.hasElements())
5434 // Check for the existing linking information
5435 bool bSuccess
= false;
5436 beans::PropertyValues aNewGrabBagSeq
;
5437 const auto& aHasLink
= lcl_getGrabBagValue(aOldGrabBagSeq
, "TxbxHasLink");
5439 // If there must be a link, do it
5440 if (aHasLink
.hasValue() && aHasLink
.get
<bool>())
5442 auto aLinkProp
= comphelper::makePropertyValue("LinkChainName", sTextBoxName
);
5443 for (sal_uInt32 i
= 0; i
< aOldGrabBagSeq
.size(); ++i
)
5445 aNewGrabBagSeq
.realloc(i
+ 1);
5446 // If this is the link name replace it
5447 if (!aOldGrabBagSeq
[i
].Name
.isEmpty() && !aLinkProp
.Name
.isEmpty()
5448 && (aOldGrabBagSeq
[i
].Name
== aLinkProp
.Name
))
5450 aNewGrabBagSeq
.getArray()[i
] = aLinkProp
;
5455 aNewGrabBagSeq
.getArray()[i
] = aOldGrabBagSeq
[i
];
5458 // If there was no replacement, append the linking data
5461 aNewGrabBagSeq
.realloc(aNewGrabBagSeq
.size() + 1);
5462 aNewGrabBagSeq
.getArray()[aNewGrabBagSeq
.size() - 1] = aLinkProp
;
5467 // If the linking changed the grabbag, apply the modifications
5468 if (aNewGrabBagSeq
.hasElements() && bSuccess
)
5470 xProps
->setPropertyValue("InteropGrabBag", uno::Any(aNewGrabBagSeq
));
5471 m_vTextFramesForChaining
.push_back(xShape
);
5476 SAL_WARN("writerfilter.dmapper", "Exception while trying to link textboxes!");
5480 uno::Reference
<beans::XPropertySet
> DomainMapper_Impl::FindOrCreateFieldMaster(const char* pFieldMasterService
, const OUString
& rFieldMasterName
)
5482 // query master, create if not available
5483 uno::Reference
< text::XTextFieldsSupplier
> xFieldsSupplier( GetTextDocument(), uno::UNO_QUERY_THROW
);
5484 uno::Reference
< container::XNameAccess
> xFieldMasterAccess
= xFieldsSupplier
->getTextFieldMasters();
5485 uno::Reference
< beans::XPropertySet
> xMaster
;
5486 OUString
sFieldMasterService( OUString::createFromAscii(pFieldMasterService
) );
5487 OUStringBuffer aFieldMasterName
;
5488 OUString sDatabaseDataSourceName
= GetSettingsTable()->GetCurrentDatabaseDataSource();
5489 bool bIsMergeField
= sFieldMasterService
.endsWith("Database");
5490 aFieldMasterName
.appendAscii( pFieldMasterService
);
5491 aFieldMasterName
.append('.');
5492 if ( bIsMergeField
&& !sDatabaseDataSourceName
.isEmpty() )
5494 aFieldMasterName
.append(sDatabaseDataSourceName
+ ".");
5496 aFieldMasterName
.append(rFieldMasterName
);
5497 OUString sFieldMasterName
= aFieldMasterName
.makeStringAndClear();
5498 if(xFieldMasterAccess
->hasByName(sFieldMasterName
))
5501 xMaster
.set(xFieldMasterAccess
->getByName(sFieldMasterName
), uno::UNO_QUERY_THROW
);
5503 else if( m_xTextFactory
.is() )
5506 xMaster
.set( m_xTextFactory
->createInstance(sFieldMasterService
), uno::UNO_QUERY_THROW
);
5507 if ( !bIsMergeField
|| sDatabaseDataSourceName
.isEmpty() )
5509 //set the master's name
5510 xMaster
->setPropertyValue(
5511 getPropertyName(PROP_NAME
),
5512 uno::Any(rFieldMasterName
));
5514 // set database data, based on the "databasename.tablename" of sDatabaseDataSourceName
5515 xMaster
->setPropertyValue(
5516 getPropertyName(PROP_DATABASE_NAME
),
5517 uno::Any(sDatabaseDataSourceName
.copy(0, sDatabaseDataSourceName
.indexOf('.'))));
5518 xMaster
->setPropertyValue(
5519 getPropertyName(PROP_COMMAND_TYPE
),
5520 uno::Any(sal_Int32(0)));
5521 xMaster
->setPropertyValue(
5522 getPropertyName(PROP_DATATABLE_NAME
),
5523 uno::Any(sDatabaseDataSourceName
.copy(sDatabaseDataSourceName
.indexOf('.') + 1)));
5524 xMaster
->setPropertyValue(
5525 getPropertyName(PROP_DATACOLUMN_NAME
),
5526 uno::Any(rFieldMasterName
));
5532 void DomainMapper_Impl::PushFieldContext()
5534 m_bParaHadField
= true;
5535 if(m_bDiscardHeaderFooter
)
5538 TagLogger::getInstance().element("pushFieldContext");
5541 uno::Reference
<text::XTextCursor
> xCrsr
;
5542 if (!m_aTextAppendStack
.empty())
5544 uno::Reference
<text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
5545 if (xTextAppend
.is())
5546 xCrsr
= xTextAppend
->createTextCursorByRange(
5547 m_aTextAppendStack
.top().xInsertPosition
.is()
5548 ? m_aTextAppendStack
.top().xInsertPosition
5549 : xTextAppend
->getEnd());
5552 uno::Reference
< text::XTextRange
> xStart
;
5554 xStart
= xCrsr
->getStart();
5555 m_aFieldStack
.push_back(new FieldContext(xStart
));
5557 /*-------------------------------------------------------------------------
5558 //the current field context waits for the completion of the command
5559 -----------------------------------------------------------------------*/
5560 bool DomainMapper_Impl::IsOpenFieldCommand() const
5562 return !m_aFieldStack
.empty() && !m_aFieldStack
.back()->IsCommandCompleted();
5564 /*-------------------------------------------------------------------------
5565 //the current field context waits for the completion of the command
5566 -----------------------------------------------------------------------*/
5567 bool DomainMapper_Impl::IsOpenField() const
5569 return !m_aFieldStack
.empty();
5572 // Mark top field context as containing a fixed field
5573 void DomainMapper_Impl::SetFieldLocked()
5576 m_aFieldStack
.back()->SetFieldLocked();
5579 HeaderFooterContext::HeaderFooterContext(bool bTextInserted
, sal_Int32 nTableDepth
)
5580 : m_bTextInserted(bTextInserted
)
5581 , m_nTableDepth(nTableDepth
)
5585 bool HeaderFooterContext::getTextInserted() const
5587 return m_bTextInserted
;
5590 sal_Int32
HeaderFooterContext::getTableDepth() const { return m_nTableDepth
; }
5592 FieldContext::FieldContext(uno::Reference
< text::XTextRange
> xStart
)
5593 : m_bFieldCommandCompleted(false)
5594 , m_xStartRange(std::move( xStart
))
5595 , m_bFieldLocked( false )
5596 , m_bCommandType(false)
5598 m_pProperties
= new PropertyMap();
5602 FieldContext::~FieldContext()
5606 void FieldContext::SetTextField(uno::Reference
<text::XTextField
> const& xTextField
)
5609 if (xTextField
.is())
5611 uno::Reference
<lang::XServiceInfo
> const xServiceInfo(xTextField
, uno::UNO_QUERY
);
5612 assert(xServiceInfo
.is());
5613 // those must be set by SetFormField()
5614 assert(!xServiceInfo
->supportsService("com.sun.star.text.Fieldmark")
5615 && !xServiceInfo
->supportsService("com.sun.star.text.FormFieldmark"));
5618 m_xTextField
= xTextField
;
5621 void FieldContext::CacheVariableValue(const uno::Any
& rAny
)
5623 rAny
>>= m_sVariableValue
;
5626 void FieldContext::AppendCommand(std::u16string_view rPart
)
5628 m_sCommand
[m_bCommandType
] += rPart
;
5631 ::std::vector
<OUString
> FieldContext::GetCommandParts() const
5633 ::std::vector
<OUString
> aResult
;
5634 sal_Int32 nIndex
= 0;
5635 bool bInString
= false;
5637 while (nIndex
!= -1)
5639 OUString sToken
= GetCommand().getToken(0, ' ', nIndex
);
5640 bool bInStringNext
= bInString
;
5642 if (sToken
.isEmpty())
5645 if (sToken
[0] == '"')
5647 bInStringNext
= true;
5648 sToken
= sToken
.copy(1);
5650 if (sToken
.endsWith("\""))
5652 bInStringNext
= false;
5653 sToken
= sToken
.copy(0, sToken
.getLength() - 1);
5658 sPart
+= " " + sToken
;
5661 aResult
.push_back(sPart
);
5672 aResult
.push_back(sToken
);
5676 bInString
= bInStringNext
;
5682 /*-------------------------------------------------------------------------
5683 //collect the pieces of the command
5684 -----------------------------------------------------------------------*/
5685 void DomainMapper_Impl::AppendFieldCommand(OUString
const & rPartOfCommand
)
5688 TagLogger::getInstance().startElement("appendFieldCommand");
5689 TagLogger::getInstance().chars(rPartOfCommand
);
5690 TagLogger::getInstance().endElement();
5693 FieldContextPtr pContext
= m_aFieldStack
.back();
5694 OSL_ENSURE( pContext
, "no field context available");
5697 // Set command line type: normal or deleted
5698 pContext
->SetCommandType(m_bTextDeleted
);
5699 pContext
->AppendCommand( rPartOfCommand
);
5704 typedef std::multimap
< sal_Int32
, OUString
> TOCStyleMap
;
5707 static ww::eField
GetWW8FieldId(OUString
const& rType
)
5709 std::unordered_map
<OUString
, ww::eField
> mapID
5711 {"ADDRESSBLOCK", ww::eADDRESSBLOCK
},
5712 {"ADVANCE", ww::eADVANCE
},
5714 {"AUTONUM", ww::eAUTONUM
},
5715 {"AUTONUMLGL", ww::eAUTONUMLGL
},
5716 {"AUTONUMOUT", ww::eAUTONUMOUT
},
5717 {"AUTOTEXT", ww::eAUTOTEXT
},
5718 {"AUTOTEXTLIST", ww::eAUTOTEXTLIST
},
5719 {"AUTHOR", ww::eAUTHOR
},
5720 {"BARCODE", ww::eBARCODE
},
5721 {"BIDIOUTLINE", ww::eBIDIOUTLINE
},
5722 {"DATE", ww::eDATE
},
5723 {"COMMENTS", ww::eCOMMENTS
},
5724 {"COMPARE", ww::eCOMPARE
},
5725 {"CONTROL", ww::eCONTROL
},
5726 {"CREATEDATE", ww::eCREATEDATE
},
5727 {"DATABASE", ww::eDATABASE
},
5728 {"DDEAUTOREF", ww::eDDEAUTOREF
},
5729 {"DDEREF", ww::eDDEREF
},
5730 {"DOCPROPERTY", ww::eDOCPROPERTY
},
5731 {"DOCVARIABLE", ww::eDOCVARIABLE
},
5732 {"EDITTIME", ww::eEDITTIME
},
5733 {"EMBED", ww::eEMBED
},
5735 {"FILLIN", ww::eFILLIN
},
5736 {"FILENAME", ww::eFILENAME
},
5737 {"FILESIZE", ww::eFILESIZE
},
5738 {"FOOTREF", ww::eFOOTREF
},
5739 // {"FORMULA", ww::},
5740 {"FORMCHECKBOX", ww::eFORMCHECKBOX
},
5741 {"FORMDROPDOWN", ww::eFORMDROPDOWN
},
5742 {"FORMTEXT", ww::eFORMTEXT
},
5743 {"GLOSSREF", ww::eGLOSSREF
},
5744 {"GOTOBUTTON", ww::eGOTOBUTTON
},
5745 {"GREETINGLINE", ww::eGREETINGLINE
},
5746 {"HTMLCONTROL", ww::eHTMLCONTROL
},
5747 {"HYPERLINK", ww::eHYPERLINK
},
5749 {"INFO", ww::eINFO
},
5750 {"INCLUDEPICTURE", ww::eINCLUDEPICTURE
},
5751 {"INCLUDETEXT", ww::eINCLUDETEXT
},
5752 {"INCLUDETIFF", ww::eINCLUDETIFF
},
5753 {"KEYWORDS", ww::eKEYWORDS
},
5754 {"LASTSAVEDBY", ww::eLASTSAVEDBY
},
5755 {"LINK", ww::eLINK
},
5756 {"LISTNUM", ww::eLISTNUM
},
5757 {"MACRO", ww::eMACRO
},
5758 {"MACROBUTTON", ww::eMACROBUTTON
},
5759 {"MERGEDATA", ww::eMERGEDATA
},
5760 {"MERGEFIELD", ww::eMERGEFIELD
},
5761 {"MERGEINC", ww::eMERGEINC
},
5762 {"MERGEREC", ww::eMERGEREC
},
5763 {"MERGESEQ", ww::eMERGESEQ
},
5764 {"NEXT", ww::eNEXT
},
5765 {"NEXTIF", ww::eNEXTIF
},
5766 {"NOTEREF", ww::eNOTEREF
},
5767 {"PAGE", ww::ePAGE
},
5768 {"PAGEREF", ww::ePAGEREF
},
5769 {"PLUGIN", ww::ePLUGIN
},
5770 {"PRINT", ww::ePRINT
},
5771 {"PRINTDATE", ww::ePRINTDATE
},
5772 {"PRIVATE", ww::ePRIVATE
},
5773 {"QUOTE", ww::eQUOTE
},
5776 {"REVNUM", ww::eREVNUM
},
5777 {"SAVEDATE", ww::eSAVEDATE
},
5778 {"SECTION", ww::eSECTION
},
5779 {"SECTIONPAGES", ww::eSECTIONPAGES
},
5782 {"SKIPIF", ww::eSKIPIF
},
5783 {"STYLEREF", ww::eSTYLEREF
},
5784 {"SUBSCRIBER", ww::eSUBSCRIBER
},
5785 {"SUBJECT", ww::eSUBJECT
},
5786 {"SYMBOL", ww::eSYMBOL
},
5788 {"TEMPLATE", ww::eTEMPLATE
},
5789 {"TIME", ww::eTIME
},
5790 {"TITLE", ww::eTITLE
},
5792 {"USERINITIALS", ww::eUSERINITIALS
},
5793 {"USERADDRESS", ww::eUSERADDRESS
},
5794 {"USERNAME", ww::eUSERNAME
},
5798 {"NUMCHARS", ww::eNUMCHARS
},
5799 {"NUMWORDS", ww::eNUMWORDS
},
5800 {"NUMPAGES", ww::eNUMPAGES
},
5801 {"INDEX", ww::eINDEX
},
5803 {"BIBLIOGRAPHY", ww::eBIBLIOGRAPHY
},
5804 {"CITATION", ww::eCITATION
},
5806 auto const it
= mapID
.find(rType
);
5807 return (it
== mapID
.end()) ? ww::eNONE
: it
->second
;
5810 static const FieldConversionMap_t
& lcl_GetFieldConversion()
5812 static const FieldConversionMap_t aFieldConversionMap
5814 // {"ADDRESSBLOCK", {"", FIELD_ADDRESSBLOCK }},
5815 // {"ADVANCE", {"", FIELD_ADVANCE }},
5816 {"ASK", {"SetExpression", FIELD_ASK
}},
5817 {"AUTONUM", {"SetExpression", FIELD_AUTONUM
}},
5818 {"AUTONUMLGL", {"SetExpression", FIELD_AUTONUMLGL
}},
5819 {"AUTONUMOUT", {"SetExpression", FIELD_AUTONUMOUT
}},
5820 {"AUTHOR", {"DocInfo.CreateAuthor", FIELD_AUTHOR
}},
5821 {"DATE", {"DateTime", FIELD_DATE
}},
5822 {"COMMENTS", {"DocInfo.Description", FIELD_COMMENTS
}},
5823 {"CREATEDATE", {"DocInfo.CreateDateTime", FIELD_CREATEDATE
}},
5824 {"DOCPROPERTY", {"", FIELD_DOCPROPERTY
}},
5825 {"DOCVARIABLE", {"User", FIELD_DOCVARIABLE
}},
5826 {"EDITTIME", {"DocInfo.EditTime", FIELD_EDITTIME
}},
5827 {"EQ", {"", FIELD_EQ
}},
5828 {"FILLIN", {"Input", FIELD_FILLIN
}},
5829 {"FILENAME", {"FileName", FIELD_FILENAME
}},
5830 // {"FILESIZE", {"", FIELD_FILESIZE }},
5831 {"FORMULA", {"TableFormula", FIELD_FORMULA
}},
5832 {"FORMCHECKBOX", {"", FIELD_FORMCHECKBOX
}},
5833 {"FORMDROPDOWN", {"DropDown", FIELD_FORMDROPDOWN
}},
5834 {"FORMTEXT", {"Input", FIELD_FORMTEXT
}},
5835 {"GOTOBUTTON", {"", FIELD_GOTOBUTTON
}},
5836 {"HYPERLINK", {"", FIELD_HYPERLINK
}},
5837 {"IF", {"ConditionalText", FIELD_IF
}},
5838 // {"INFO", {"", FIELD_INFO }},
5839 {"INCLUDEPICTURE", {"", FIELD_INCLUDEPICTURE
}},
5840 {"KEYWORDS", {"DocInfo.KeyWords", FIELD_KEYWORDS
}},
5841 {"LASTSAVEDBY", {"DocInfo.ChangeAuthor", FIELD_LASTSAVEDBY
}},
5842 {"MACROBUTTON", {"Macro", FIELD_MACROBUTTON
}},
5843 {"MERGEFIELD", {"Database", FIELD_MERGEFIELD
}},
5844 {"MERGEREC", {"DatabaseNumberOfSet", FIELD_MERGEREC
}},
5845 // {"MERGESEQ", {"", FIELD_MERGESEQ }},
5846 {"NEXT", {"DatabaseNextSet", FIELD_NEXT
}},
5847 {"NEXTIF", {"DatabaseNextSet", FIELD_NEXTIF
}},
5848 {"PAGE", {"PageNumber", FIELD_PAGE
}},
5849 {"PAGEREF", {"GetReference", FIELD_PAGEREF
}},
5850 {"PRINTDATE", {"DocInfo.PrintDateTime", FIELD_PRINTDATE
}},
5851 {"REF", {"GetReference", FIELD_REF
}},
5852 {"REVNUM", {"DocInfo.Revision", FIELD_REVNUM
}},
5853 {"SAVEDATE", {"DocInfo.ChangeDateTime", FIELD_SAVEDATE
}},
5854 // {"SECTION", {"", FIELD_SECTION }},
5855 // {"SECTIONPAGES", {"", FIELD_SECTIONPAGES }},
5856 {"SEQ", {"SetExpression", FIELD_SEQ
}},
5857 {"SET", {"SetExpression", FIELD_SET
}},
5858 // {"SKIPIF", {"", FIELD_SKIPIF }},
5859 // {"STYLEREF", {"", FIELD_STYLEREF }},
5860 {"SUBJECT", {"DocInfo.Subject", FIELD_SUBJECT
}},
5861 {"SYMBOL", {"", FIELD_SYMBOL
}},
5862 {"TEMPLATE", {"TemplateName", FIELD_TEMPLATE
}},
5863 {"TIME", {"DateTime", FIELD_TIME
}},
5864 {"TITLE", {"DocInfo.Title", FIELD_TITLE
}},
5865 {"USERINITIALS", {"Author", FIELD_USERINITIALS
}},
5866 // {"USERADDRESS", {"", FIELD_USERADDRESS }},
5867 {"USERNAME", {"Author", FIELD_USERNAME
}},
5870 {"TOC", {"com.sun.star.text.ContentIndex", FIELD_TOC
}},
5871 {"TC", {"com.sun.star.text.ContentIndexMark", FIELD_TC
}},
5872 {"NUMCHARS", {"CharacterCount", FIELD_NUMCHARS
}},
5873 {"NUMWORDS", {"WordCount", FIELD_NUMWORDS
}},
5874 {"NUMPAGES", {"PageCount", FIELD_NUMPAGES
}},
5875 {"INDEX", {"com.sun.star.text.DocumentIndex", FIELD_INDEX
}},
5876 {"XE", {"com.sun.star.text.DocumentIndexMark", FIELD_XE
}},
5877 {"BIBLIOGRAPHY",{"com.sun.star.text.Bibliography", FIELD_BIBLIOGRAPHY
}},
5878 {"CITATION", {"com.sun.star.text.TextField.Bibliography",FIELD_CITATION
}},
5881 return aFieldConversionMap
;
5884 static const FieldConversionMap_t
& lcl_GetEnhancedFieldConversion()
5886 static const FieldConversionMap_t aEnhancedFieldConversionMap
=
5888 {"FORMCHECKBOX", {"FormFieldmark", FIELD_FORMCHECKBOX
}},
5889 {"FORMDROPDOWN", {"FormFieldmark", FIELD_FORMDROPDOWN
}},
5890 {"FORMTEXT", {"Fieldmark", FIELD_FORMTEXT
}},
5893 return aEnhancedFieldConversionMap
;
5896 void DomainMapper_Impl::handleFieldSet
5897 (const FieldContextPtr
& pContext
,
5898 uno::Reference
< uno::XInterface
> const & xFieldInterface
,
5899 uno::Reference
< beans::XPropertySet
> const& xFieldProperties
)
5901 OUString sVariable
, sHint
;
5903 sVariable
= lcl_ExtractVariableAndHint(pContext
->GetCommand(), sHint
);
5905 // remove surrounding "" if exists
5906 if(sHint
.getLength() >= 2)
5908 std::u16string_view sTmp
= o3tl::trim(sHint
);
5909 if (o3tl::starts_with(sTmp
, u
"\"") && o3tl::ends_with(sTmp
, u
"\""))
5911 sHint
= sTmp
.substr(1, sTmp
.size() - 2);
5915 // determine field master name
5916 uno::Reference
< beans::XPropertySet
> xMaster
=
5917 FindOrCreateFieldMaster
5918 ("com.sun.star.text.FieldMaster.SetExpression", sVariable
);
5920 // a set field is a string
5921 xMaster
->setPropertyValue(getPropertyName(PROP_SUB_TYPE
), uno::Any(text::SetVariableType::STRING
));
5923 // attach the master to the field
5924 uno::Reference
< text::XDependentTextField
> xDependentField
5925 ( xFieldInterface
, uno::UNO_QUERY_THROW
);
5926 xDependentField
->attachTextFieldMaster( xMaster
);
5928 uno::Any
aAnyHint(sHint
);
5929 xFieldProperties
->setPropertyValue(getPropertyName(PROP_HINT
), aAnyHint
);
5930 xFieldProperties
->setPropertyValue(getPropertyName(PROP_CONTENT
), aAnyHint
);
5931 xFieldProperties
->setPropertyValue(getPropertyName(PROP_SUB_TYPE
), uno::Any(text::SetVariableType::STRING
));
5933 // Mimic MS Word behavior (hide the SET)
5934 xFieldProperties
->setPropertyValue(getPropertyName(PROP_IS_VISIBLE
), uno::Any(false));
5937 void DomainMapper_Impl::handleFieldAsk
5938 (const FieldContextPtr
& pContext
,
5939 uno::Reference
< uno::XInterface
> & xFieldInterface
,
5940 uno::Reference
< beans::XPropertySet
> const& xFieldProperties
)
5942 //doesn the command contain a variable name?
5943 OUString sVariable
, sHint
;
5945 sVariable
= lcl_ExtractVariableAndHint( pContext
->GetCommand(),
5947 if(!sVariable
.isEmpty())
5949 // determine field master name
5950 uno::Reference
< beans::XPropertySet
> xMaster
=
5951 FindOrCreateFieldMaster
5952 ("com.sun.star.text.FieldMaster.SetExpression", sVariable
);
5953 // An ASK field is always a string of characters
5954 xMaster
->setPropertyValue(getPropertyName(PROP_SUB_TYPE
), uno::Any(text::SetVariableType::STRING
));
5956 // attach the master to the field
5957 uno::Reference
< text::XDependentTextField
> xDependentField
5958 ( xFieldInterface
, uno::UNO_QUERY_THROW
);
5959 xDependentField
->attachTextFieldMaster( xMaster
);
5961 // set input flag at the field
5962 xFieldProperties
->setPropertyValue(
5963 getPropertyName(PROP_IS_INPUT
), uno::Any( true ));
5965 xFieldProperties
->setPropertyValue(
5966 getPropertyName(PROP_HINT
),
5968 xFieldProperties
->setPropertyValue(getPropertyName(PROP_SUB_TYPE
), uno::Any(text::SetVariableType::STRING
));
5969 // The ASK has no field value to display
5970 xFieldProperties
->setPropertyValue(getPropertyName(PROP_IS_VISIBLE
), uno::Any(false));
5974 //don't insert the field
5975 //todo: maybe import a 'normal' input field here?
5976 xFieldInterface
= nullptr;
5981 * Converts a Microsoft Word field formula into LibreOffice syntax
5982 * @param input The Microsoft Word field formula, with no leading '=' sign
5983 * @return An equivalent LibreOffice field formula
5985 OUString
DomainMapper_Impl::convertFieldFormula(const OUString
& input
) {
5987 if (!m_pSettingsTable
)
5992 OUString listSeparator
= m_pSettingsTable
->GetListSeparator();
5994 /* Replace logical condition functions with LO equivalent operators */
5995 OUString changed
= input
.replaceAll(" <> ", " NEQ ");
5996 changed
= changed
.replaceAll(" <= ", " LEQ ");
5997 changed
= changed
.replaceAll(" >= ", " GEQ ");
5998 changed
= changed
.replaceAll(" = " , " EQ ");
5999 changed
= changed
.replaceAll(" < " , " L ");
6000 changed
= changed
.replaceAll(" > " , " G ");
6002 changed
= changed
.replaceAll("<>", " NEQ ");
6003 changed
= changed
.replaceAll("<=", " LEQ ");
6004 changed
= changed
.replaceAll(">=", " GEQ ");
6005 changed
= changed
.replaceAll("=" , " EQ ");
6006 changed
= changed
.replaceAll("<" , " L ");
6007 changed
= changed
.replaceAll(">" , " G ");
6009 /* Replace function calls with infix keywords for AND(), OR(), and ROUND(). Nothing needs to be
6010 * done for NOT(). This simple regex will work properly with most common cases. However, it may
6011 * not work correctly when the arguments are nested subcalls to other functions, like
6012 * ROUND(MIN(1,2),MAX(3,4)). See TDF#134765. */
6013 icu::ErrorCode status
;
6014 icu::UnicodeString
usInput(changed
.getStr());
6015 const uint32_t rMatcherFlags
= UREGEX_CASE_INSENSITIVE
;
6016 OUString regex
= "\\b(AND|OR|ROUND)\\s*\\(\\s*([^" + listSeparator
+ "]+)\\s*" + listSeparator
+ "\\s*([^)]+)\\s*\\)";
6017 icu::UnicodeString
usRegex(regex
.getStr());
6018 icu::RegexMatcher
rmatch1(usRegex
, usInput
, rMatcherFlags
, status
);
6019 usInput
= rmatch1
.replaceAll(icu::UnicodeString("(($2) $1 ($3))"), status
);
6021 /* Assumes any remaining list separators separate arguments to functions that accept lists
6022 * (SUM, MIN, MAX, MEAN, etc.) */
6023 usInput
.findAndReplace(icu::UnicodeString(listSeparator
.getStr()), "|");
6025 /* Surround single cell references with angle brackets.
6026 * If there is ever added a function name that ends with a digit, this regex will need to be revisited. */
6027 icu::RegexMatcher
rmatch2("\\b([A-Z]{1,3}[0-9]+)\\b(?![(])", usInput
, rMatcherFlags
, status
);
6028 usInput
= rmatch2
.replaceAll(icu::UnicodeString("<$1>"), status
);
6030 /* Cell references must be upper case
6031 * TODO: convert reference to other tables, e.g. SUM(Table1 A1:B2), where "Table1" is a bookmark of the table,
6032 * TODO: also column range A:A */
6033 icu::RegexMatcher
rmatch3("(<[a-z]{1,3}[0-9]+>|\\b(above|below|left|right)\\b)", usInput
, rMatcherFlags
, status
);
6034 icu::UnicodeString replacedCellRefs
;
6035 while (rmatch3
.find(status
) && status
.isSuccess()) {
6036 rmatch3
.appendReplacement(replacedCellRefs
, rmatch3
.group(status
).toUpper(), status
);
6038 rmatch3
.appendTail(replacedCellRefs
);
6040 /* Fix up cell ranges */
6041 icu::RegexMatcher
rmatch4("<([A-Z]{1,3}[0-9]+)>:<([A-Z]{1,3}[0-9]+)>", replacedCellRefs
, rMatcherFlags
, status
);
6042 usInput
= rmatch4
.replaceAll(icu::UnicodeString("<$1:$2>"), status
);
6044 /* Fix up user defined names */
6045 icu::RegexMatcher
rmatch5("\\bDEFINED\\s*\\(<([A-Z]+[0-9]+)>\\)", usInput
, rMatcherFlags
, status
);
6046 usInput
= rmatch5
.replaceAll(icu::UnicodeString("DEFINED($1)"), status
);
6048 /* Prepare replace of ABOVE/BELOW/LEFT/RIGHT by adding spaces around them */
6049 icu::RegexMatcher
rmatch6("\\b(ABOVE|BELOW|LEFT|RIGHT)\\b", usInput
, rMatcherFlags
, status
);
6050 usInput
= rmatch6
.replaceAll(icu::UnicodeString(" $1 "), status
);
6052 /* DOCX allows to set decimal symbol independently from the locale of the document, so if
6053 * needed, convert decimal comma to get working formula in a document language (locale),
6054 * which doesn't use decimal comma */
6055 if ( m_pSettingsTable
->GetDecimalSymbol() == "," && !m_bIsDecimalComma
)
6057 icu::RegexMatcher
rmatch7("\\b([0-9]+),([0-9]+([eE][-]?[0-9]+)?)\\b", usInput
, rMatcherFlags
, status
);
6058 usInput
= rmatch7
.replaceAll(icu::UnicodeString("$1.$2"), status
);
6061 return OUString(usInput
.getTerminatedBuffer());
6064 void DomainMapper_Impl::handleFieldFormula
6065 (const FieldContextPtr
& pContext
,
6066 uno::Reference
< beans::XPropertySet
> const& xFieldProperties
)
6068 OUString command
= pContext
->GetCommand().trim();
6070 // Remove number formatting from \# to end of command
6071 // TODO: handle custom number formatting
6072 sal_Int32 delimPos
= command
.indexOf("\\#");
6075 command
= command
.replaceAt(delimPos
, command
.getLength() - delimPos
, u
"").trim();
6078 // command must contains = and at least another char
6079 if (command
.getLength() < 2)
6082 // we don't copy the = symbol from the command
6083 OUString formula
= convertFieldFormula(command
.copy(1));
6085 xFieldProperties
->setPropertyValue(getPropertyName(PROP_CONTENT
), uno::Any(formula
));
6086 xFieldProperties
->setPropertyValue(getPropertyName(PROP_NUMBER_FORMAT
), uno::Any(sal_Int32(0)));
6087 xFieldProperties
->setPropertyValue("IsShowFormula", uno::Any(false));
6089 // grab-bag the original and converted formula
6090 if (hasTableManager())
6092 TablePropertyMapPtr
pPropMap(new TablePropertyMap());
6093 pPropMap
->Insert(PROP_CELL_FORMULA
, uno::Any(command
.copy(1)), true, CELL_GRAB_BAG
);
6094 pPropMap
->Insert(PROP_CELL_FORMULA_CONVERTED
, uno::Any(formula
), true, CELL_GRAB_BAG
);
6095 getTableManager().cellProps(pPropMap
);
6099 void DomainMapper_Impl::handleRubyEQField( const FieldContextPtr
& pContext
)
6101 const OUString
& rCommand(pContext
->GetCommand());
6102 sal_Int32 nIndex
= 0, nEnd
= 0;
6104 nIndex
= rCommand
.indexOf("\\* jc" );
6108 sal_uInt32 nJc
= o3tl::toInt32(o3tl::getToken(rCommand
, 0, ' ',nIndex
));
6109 const sal_Int32 aRubyAlignValues
[] =
6111 NS_ooxml::LN_Value_ST_RubyAlign_center
,
6112 NS_ooxml::LN_Value_ST_RubyAlign_distributeLetter
,
6113 NS_ooxml::LN_Value_ST_RubyAlign_distributeSpace
,
6114 NS_ooxml::LN_Value_ST_RubyAlign_left
,
6115 NS_ooxml::LN_Value_ST_RubyAlign_right
,
6116 NS_ooxml::LN_Value_ST_RubyAlign_rightVertical
,
6118 aInfo
.nRubyAlign
= aRubyAlignValues
[(nJc
<SAL_N_ELEMENTS(aRubyAlignValues
))?nJc
:0];
6121 // we don't parse or use the font field in rCommand
6123 nIndex
= rCommand
.indexOf("\\* hps" );
6127 aInfo
.nHps
= o3tl::toInt32(o3tl::getToken(rCommand
, 0, ' ',nIndex
));
6130 nIndex
= rCommand
.indexOf("\\o");
6133 nIndex
= rCommand
.indexOf('(', nIndex
);
6136 nEnd
= rCommand
.lastIndexOf(')');
6142 std::u16string_view sRubyParts
= rCommand
.subView(nIndex
+1,nEnd
-nIndex
-1);
6144 std::u16string_view sPart1
= o3tl::getToken(sRubyParts
, 0, ',', nIndex
);
6145 std::u16string_view sPart2
= o3tl::getToken(sRubyParts
, 0, ',', nIndex
);
6148 if ((nIndex2
= sPart1
.find('(')) != std::u16string_view::npos
&& (nEnd2
= sPart1
.rfind(')')) != std::u16string_view::npos
&& nEnd2
> nIndex2
)
6150 aInfo
.sRubyText
= sPart1
.substr(nIndex2
+1,nEnd2
-nIndex2
-1);
6153 PropertyMapPtr
pRubyContext(new PropertyMap());
6154 pRubyContext
->InsertProps(GetTopContext());
6157 double fVal
= double(aInfo
.nHps
) / 2.;
6158 uno::Any
aVal( fVal
);
6160 pRubyContext
->Insert(PROP_CHAR_HEIGHT
, aVal
);
6161 pRubyContext
->Insert(PROP_CHAR_HEIGHT_ASIAN
, aVal
);
6163 PropertyValueVector_t aProps
= comphelper::sequenceToContainer
< PropertyValueVector_t
>(pRubyContext
->GetPropertyValues());
6164 aInfo
.sRubyStyle
= m_rDMapper
.getOrCreateCharStyle(aProps
, /*bAlwaysCreate=*/false);
6165 PropertyMapPtr
pCharContext(new PropertyMap());
6166 if (m_pLastCharacterContext
)
6167 pCharContext
->InsertProps(m_pLastCharacterContext
);
6168 pCharContext
->InsertProps(pContext
->getProperties());
6169 pCharContext
->Insert(PROP_RUBY_TEXT
, uno::Any( aInfo
.sRubyText
) );
6170 pCharContext
->Insert(PROP_RUBY_ADJUST
, uno::Any(static_cast<sal_Int16
>(ConversionHelper::convertRubyAlign(aInfo
.nRubyAlign
))));
6171 if ( aInfo
.nRubyAlign
== NS_ooxml::LN_Value_ST_RubyAlign_rightVertical
)
6172 pCharContext
->Insert(PROP_RUBY_POSITION
, uno::Any(css::text::RubyPosition::INTER_CHARACTER
));
6173 pCharContext
->Insert(PROP_RUBY_STYLE
, uno::Any(aInfo
.sRubyStyle
));
6174 appendTextPortion(OUString(sPart2
), pCharContext
);
6178 void DomainMapper_Impl::handleAutoNum
6179 (const FieldContextPtr
& pContext
,
6180 uno::Reference
< uno::XInterface
> const & xFieldInterface
,
6181 uno::Reference
< beans::XPropertySet
> const& xFieldProperties
)
6183 //create a sequence field master "AutoNr"
6184 uno::Reference
< beans::XPropertySet
> xMaster
=
6185 FindOrCreateFieldMaster
6186 ("com.sun.star.text.FieldMaster.SetExpression",
6189 xMaster
->setPropertyValue( getPropertyName(PROP_SUB_TYPE
),
6190 uno::Any(text::SetVariableType::SEQUENCE
));
6192 //apply the numbering type
6193 xFieldProperties
->setPropertyValue(
6194 getPropertyName(PROP_NUMBERING_TYPE
),
6195 uno::Any( lcl_ParseNumberingType(pContext
->GetCommand()) ));
6196 // attach the master to the field
6197 uno::Reference
< text::XDependentTextField
> xDependentField
6198 ( xFieldInterface
, uno::UNO_QUERY_THROW
);
6199 xDependentField
->attachTextFieldMaster( xMaster
);
6202 void DomainMapper_Impl::handleAuthor
6203 (std::u16string_view
,
6204 uno::Reference
< beans::XPropertySet
> const& xFieldProperties
,
6207 if (eFieldId
== FIELD_USERNAME
)
6208 xFieldProperties
->setPropertyValue
6209 ( getPropertyName(PROP_FULL_NAME
), uno::Any( true ));
6211 // Always set as FIXED b/c MS Word only updates these fields via user intervention (F9)
6212 // AUTHOR of course never changes and USERNAME is easily mis-used as an original author field.
6213 // Additionally, this was forced as fixed if any special case-formatting was provided.
6215 xFieldProperties
->setPropertyValue(
6216 getPropertyName( PROP_IS_FIXED
),
6218 //PROP_CURRENT_PRESENTATION is set later anyway
6222 void DomainMapper_Impl::handleDocProperty
6223 (const FieldContextPtr
& pContext
,
6224 OUString
const& rFirstParam
,
6225 uno::Reference
< uno::XInterface
> & xFieldInterface
)
6227 //some docproperties should be imported as document statistic fields, some as DocInfo fields
6228 //others should be user fields
6229 if (rFirstParam
.isEmpty())
6232 constexpr sal_uInt8 SET_ARABIC
= 0x01;
6233 constexpr sal_uInt8 SET_DATE
= 0x04;
6234 struct DocPropertyMap
6236 const char* pDocPropertyName
;
6237 const char* pServiceName
;
6240 static const DocPropertyMap aDocProperties
[] =
6242 {"CreateTime", "DocInfo.CreateDateTime", SET_DATE
},
6243 {"Characters", "CharacterCount", SET_ARABIC
},
6244 {"Comments", "DocInfo.Description", 0},
6245 {"Keywords", "DocInfo.KeyWords", 0},
6246 {"LastPrinted", "DocInfo.PrintDateTime", 0},
6247 {"LastSavedBy", "DocInfo.ChangeAuthor", 0},
6248 {"LastSavedTime", "DocInfo.ChangeDateTime", SET_DATE
},
6249 {"Paragraphs", "ParagraphCount", SET_ARABIC
},
6250 {"RevisionNumber", "DocInfo.Revision", 0},
6251 {"Subject", "DocInfo.Subject", 0},
6252 {"Template", "TemplateName", 0},
6253 {"Title", "DocInfo.Title", 0},
6254 {"TotalEditingTime", "DocInfo.EditTime", 0},
6255 {"Words", "WordCount", SET_ARABIC
}
6257 //other available DocProperties:
6258 //Bytes, Category, CharactersWithSpaces, Company
6260 //Lines, Manager, NameofApplication, ODMADocId, Pages,
6263 uno::Reference
<document::XDocumentPropertiesSupplier
> xDocumentPropertiesSupplier(m_xTextDocument
, uno::UNO_QUERY
);
6264 uno::Reference
<document::XDocumentProperties
> xDocumentProperties
= xDocumentPropertiesSupplier
->getDocumentProperties();
6265 uno::Reference
<beans::XPropertySet
> xUserDefinedProps(xDocumentProperties
->getUserDefinedProperties(), uno::UNO_QUERY_THROW
);
6266 uno::Reference
<beans::XPropertySetInfo
> xPropertySetInfo
= xUserDefinedProps
->getPropertySetInfo();
6267 //search for a field mapping
6268 OUString sFieldServiceName
;
6270 if (!xPropertySetInfo
->hasPropertyByName(rFirstParam
))
6272 for( ; nMap
< SAL_N_ELEMENTS(aDocProperties
); ++nMap
)
6274 if (rFirstParam
.equalsAscii(aDocProperties
[nMap
].pDocPropertyName
))
6276 sFieldServiceName
= OUString::createFromAscii(aDocProperties
[nMap
].pServiceName
);
6282 pContext
->CacheVariableValue(xUserDefinedProps
->getPropertyValue(rFirstParam
));
6284 OUString
sServiceName("com.sun.star.text.TextField.");
6285 bool bIsCustomField
= false;
6286 if(sFieldServiceName
.isEmpty())
6288 //create a custom property field
6289 sServiceName
+= "DocInfo.Custom";
6290 bIsCustomField
= true;
6294 sServiceName
+= sFieldServiceName
;
6296 if (m_xTextFactory
.is())
6297 xFieldInterface
= m_xTextFactory
->createInstance(sServiceName
);
6298 uno::Reference
<beans::XPropertySet
> xFieldProperties( xFieldInterface
, uno::UNO_QUERY_THROW
);
6299 if( bIsCustomField
)
6301 xFieldProperties
->setPropertyValue(
6302 getPropertyName(PROP_NAME
), uno::Any(rFirstParam
));
6303 pContext
->SetCustomField( xFieldProperties
);
6307 if(0 != (aDocProperties
[nMap
].nFlags
& SET_ARABIC
))
6308 xFieldProperties
->setPropertyValue(
6309 getPropertyName(PROP_NUMBERING_TYPE
),
6310 uno::Any( style::NumberingType::ARABIC
));
6311 else if(0 != (aDocProperties
[nMap
].nFlags
& SET_DATE
))
6313 xFieldProperties
->setPropertyValue(
6314 getPropertyName(PROP_IS_DATE
),
6316 SetNumberFormat( pContext
->GetCommand(), xFieldProperties
);
6321 static uno::Sequence
< beans::PropertyValues
> lcl_createTOXLevelHyperlinks( bool bHyperlinks
, const OUString
& sChapterNoSeparator
,
6322 const uno::Sequence
< beans::PropertyValues
>& aLevel
, const std::optional
<style::TabStop
> numtab
)
6324 //create a copy of the level and add new entries
6326 std::vector
<css::beans::PropertyValues
> aNewLevel
;
6327 aNewLevel
.reserve(aLevel
.getLength() + 5); // at most 5 added items
6329 static constexpr OUStringLiteral
tokType(u
"TokenType");
6330 static constexpr OUStringLiteral
tokHStart(u
"TokenHyperlinkStart");
6331 static constexpr OUStringLiteral
tokHEnd(u
"TokenHyperlinkEnd");
6332 static constexpr OUStringLiteral
tokPNum(u
"TokenPageNumber");
6333 static constexpr OUStringLiteral
tokENum(u
"TokenEntryNumber");
6336 aNewLevel
.push_back({ comphelper::makePropertyValue(tokType
, tokHStart
) });
6338 for (const auto& item
: aLevel
)
6341 if (auto it
= std::find_if(item
.begin(), item
.end(),
6342 [](const auto& p
) { return p
.Name
== tokType
; });
6344 it
->Value
>>= tokenType
;
6346 if (bHyperlinks
&& (tokenType
== tokHStart
|| tokenType
== tokHEnd
))
6347 continue; // We add hyperlink ourselves, so just skip existing hyperlink start / end
6349 if (!sChapterNoSeparator
.isEmpty() && tokenType
== tokPNum
)
6351 // This is an existing page number token; insert the chapter and separator before it
6352 aNewLevel
.push_back(
6353 { comphelper::makePropertyValue(tokType
, OUString("TokenChapterInfo")),
6354 comphelper::makePropertyValue("ChapterFormat", text::ChapterFormat::NUMBER
) });
6355 aNewLevel
.push_back({ comphelper::makePropertyValue(tokType
, OUString("TokenText")),
6356 comphelper::makePropertyValue("Text", sChapterNoSeparator
) });
6359 aNewLevel
.push_back(item
);
6361 if (numtab
&& tokenType
== tokENum
)
6363 // There is a fixed tab stop position needed in the level after the numbering
6364 aNewLevel
.push_back(
6365 { comphelper::makePropertyValue(tokType
, OUString("TokenTabStop")),
6366 comphelper::makePropertyValue("TabStopPosition", numtab
->Position
) });
6371 aNewLevel
.push_back({ comphelper::makePropertyValue(tokType
, tokHEnd
) });
6373 return comphelper::containerToSequence(aNewLevel
);
6376 /// Returns title of the TOC placed in paragraph(s) before TOC field inside STD-frame
6377 OUString
DomainMapper_Impl::extractTocTitle()
6379 if (!m_xSdtEntryStart
.is())
6382 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
6383 if(!xTextAppend
.is())
6386 // try-catch was added in the same way as inside appendTextSectionAfter()
6389 uno::Reference
<text::XParagraphCursor
> xCursor(xTextAppend
->createTextCursorByRange(m_xSdtEntryStart
), uno::UNO_QUERY_THROW
);
6393 //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
6394 xCursor
->gotoStartOfParagraph( false );
6395 if (m_aTextAppendStack
.top().xInsertPosition
.is())
6396 xCursor
->gotoRange( m_aTextAppendStack
.top().xInsertPosition
, true );
6398 xCursor
->gotoEnd( true );
6400 // the paragraph after this new section might have been already inserted
6401 OUString sResult
= xCursor
->getString();
6402 if (sResult
.endsWith(SAL_NEWLINE_STRING
))
6403 sResult
= sResult
.copy(0, sResult
.getLength() - SAL_N_ELEMENTS(SAL_NEWLINE_STRING
) + 1);
6407 catch(const uno::Exception
&)
6414 css::uno::Reference
<css::beans::XPropertySet
>
6415 DomainMapper_Impl::StartIndexSectionChecked(const OUString
& sServiceName
)
6419 finishParagraph(GetTopContextOfType(CONTEXT_PARAGRAPH
), false); // resets m_bParaChanged
6420 PopProperties(CONTEXT_PARAGRAPH
);
6421 PushProperties(CONTEXT_PARAGRAPH
);
6422 SetIsFirstRun(true);
6423 // The first paragraph of the index that is continuation of just finished one needs to be
6424 // removed when finished (unless more content will arrive, which will set m_bParaChanged)
6425 m_bRemoveThisParagraph
= true;
6427 const auto& xTextAppend
= GetTopTextAppend();
6428 const auto xTextRange
= xTextAppend
->getEnd();
6429 const auto xRet
= createSectionForRange(xTextRange
, xTextRange
, sServiceName
, false);
6430 if (!m_aTextAppendStack
.top().xInsertPosition
)
6434 m_bStartedTOC
= true;
6435 uno::Reference
<text::XTextCursor
> xTOCTextCursor
6436 = xTextRange
->getText()->createTextCursor();
6437 assert(xTOCTextCursor
.is());
6438 xTOCTextCursor
->gotoEnd(false);
6439 m_aTextAppendStack
.push(TextAppendContext(xTextAppend
, xTOCTextCursor
));
6441 catch (const uno::Exception
&)
6443 TOOLS_WARN_EXCEPTION("writerfilter.dmapper",
6444 "DomainMapper_Impl::StartIndexSectionChecked:");
6451 This is a heuristic to find Word's w:styleId value from localised style name.
6452 It's not clear how exactly it works, but apparently Word stores into
6453 w:styleId some filtered representation of the localised style name.
6454 Tragically there are references to the localised style name itself in TOC
6456 Hopefully this works and a complete map of >100 built-in style names
6457 localised to all languages isn't needed.
6459 static auto FilterChars(std::u16string_view
const& rStyleName
) -> OUString
6461 return msfilter::util::CreateDOCXStyleId(rStyleName
);
6464 static OUString
UnquoteFieldText(std::u16string_view s
)
6466 OUStringBuffer
result(s
.size());
6467 for (size_t i
= 0; i
< s
.size(); ++i
)
6474 if (i
< s
.size() - 1)
6478 result
.append(s
[i
]);
6481 return result
.makeStringAndClear();
6484 OUString
DomainMapper_Impl::ConvertTOCStyleName(OUString
const& rTOCStyleName
)
6486 assert(!rTOCStyleName
.isEmpty());
6487 if (auto const pStyle
= GetStyleSheetTable()->FindStyleSheetByISTD(rTOCStyleName
))
6488 { // theoretical case: what OOXML says
6489 return pStyle
->m_sStyleName
;
6491 auto const pStyle
= GetStyleSheetTable()->FindStyleSheetByISTD(FilterChars(rTOCStyleName
));
6492 if (pStyle
&& m_bIsNewDoc
)
6493 { // practical case: Word wrote i18n name to TOC field, but it doesn't
6494 // exist in styles.xml; tdf#153083 clone it for best roundtrip
6495 SAL_INFO("writerfilter.dmapper", "cloning TOC paragraph style (presumed built-in) " << rTOCStyleName
<< " from " << pStyle
->m_sStyleName
);
6496 return GetStyleSheetTable()->CloneTOCStyle(GetFontTable(), pStyle
, rTOCStyleName
);
6500 return GetStyleSheetTable()->ConvertStyleName(rTOCStyleName
);
6504 void DomainMapper_Impl::handleToc
6505 (const FieldContextPtr
& pContext
,
6506 const OUString
& sTOCServiceName
)
6509 if (IsInHeaderFooter())
6510 m_bStartTOCHeaderFooter
= true;
6511 bool bTableOfFigures
= false;
6512 bool bHyperlinks
= false;
6513 bool bFromOutline
= false;
6514 bool bFromEntries
= false;
6515 bool bHideTabLeaderPageNumbers
= false ;
6516 bool bIsTabEntry
= false ;
6517 bool bNewLine
= false ;
6518 bool bParagraphOutlineLevel
= false;
6520 sal_Int16 nMaxLevel
= 10;
6522 OUString sChapterNoSeparator
;
6523 OUString sFigureSequence
;
6524 OUString aBookmarkName
;
6526 // \a Builds a table of figures but does not include the captions's label and number
6527 if( lcl_FindInCommand( pContext
->GetCommand(), 'a', sValue
))
6528 { //make it a table of figures
6529 bTableOfFigures
= true;
6531 // \b Uses a bookmark to specify area of document from which to build table of contents
6532 if( lcl_FindInCommand( pContext
->GetCommand(), 'b', sValue
))
6534 aBookmarkName
= sValue
.trim().replaceAll("\"","");
6536 if( lcl_FindInCommand( pContext
->GetCommand(), 'c', sValue
))
6537 // \c Builds a table of figures of the given label
6539 //todo: sValue contains the label's name
6540 bTableOfFigures
= true;
6541 sFigureSequence
= sValue
.trim();
6542 sFigureSequence
= sFigureSequence
.replaceAll("\"", "").replaceAll("'","");
6544 // \d Defines the separator between sequence and page numbers
6545 if( lcl_FindInCommand( pContext
->GetCommand(), 'd', sValue
))
6547 //todo: insert the chapter number into each level and insert the separator additionally
6548 sChapterNoSeparator
= UnquoteFieldText(sValue
);
6550 // \f Builds a table of contents using TC entries instead of outline levels
6551 if( lcl_FindInCommand( pContext
->GetCommand(), 'f', sValue
))
6553 //todo: sValue can contain a TOC entry identifier - use unclear
6554 bFromEntries
= true;
6556 // \h Hyperlinks the entries and page numbers within the table of contents
6557 if( lcl_FindInCommand( pContext
->GetCommand(), 'h', sValue
))
6559 //todo: make all entries to hyperlinks
6562 // \l Defines the TC entries field level used to build a table of contents
6563 // if( lcl_FindInCommand( pContext->GetCommand(), 'l', sValue ))
6565 //todo: entries can only be included completely
6567 // \n Builds a table of contents or a range of entries, such as 1-9 in a table of contents without page numbers
6568 // if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
6570 //todo: what does the description mean?
6572 // \o Builds a table of contents by using outline levels instead of TC entries
6573 if( lcl_FindInCommand( pContext
->GetCommand(), 'o', sValue
))
6575 bFromOutline
= true;
6576 if (sValue
.isEmpty())
6577 nMaxLevel
= WW_OUTLINE_MAX
;
6580 sal_Int32 nIndex
= 0;
6581 o3tl::getToken(sValue
, 0, '-', nIndex
);
6582 nMaxLevel
= static_cast<sal_Int16
>(nIndex
!= -1 ? o3tl::toInt32(sValue
.subView(nIndex
)) : 0);
6585 // \p Defines the separator between the table entry and its page number
6586 // \s Builds a table of contents by using a sequence type
6587 // \t Builds a table of contents by using style names other than the standard outline styles
6588 if( lcl_FindInCommand( pContext
->GetCommand(), 't', sValue
))
6590 OUString sToken
= sValue
.getToken(1, '"');
6591 sTemplate
= sToken
.isEmpty() ? sValue
: sToken
;
6593 // \u Builds a table of contents by using the applied paragraph outline level
6594 if( lcl_FindInCommand( pContext
->GetCommand(), 'u', sValue
))
6596 bFromOutline
= true;
6597 bParagraphOutlineLevel
= true;
6598 //todo: what doesn 'the applied paragraph outline level' refer to?
6600 // \w Preserve tab characters within table entries
6601 if( lcl_FindInCommand( pContext
->GetCommand(), 'w', sValue
))
6603 bIsTabEntry
= true ;
6605 // \x Preserve newline characters within table entries
6606 if( lcl_FindInCommand( pContext
->GetCommand(), 'x', sValue
))
6610 // \z Hides page numbers within the table of contents when shown in Web Layout View
6611 if( lcl_FindInCommand( pContext
->GetCommand(), 'z', sValue
))
6613 bHideTabLeaderPageNumbers
= true ;
6616 //if there's no option then it should be created from outline
6617 if( !bFromOutline
&& !bFromEntries
&& sTemplate
.isEmpty() )
6618 bFromOutline
= true;
6620 const OUString aTocTitle
= extractTocTitle();
6622 uno::Reference
<beans::XPropertySet
> xTOC
;
6624 if (m_xTextFactory
.is() && ! m_aTextAppendStack
.empty())
6626 const auto& xTextAppend
= GetTopTextAppend();
6627 if (aTocTitle
.isEmpty() || bTableOfFigures
)
6629 // reset marker of the TOC title
6630 m_xSdtEntryStart
.clear();
6632 // Create section before setting m_bStartTOC: finishing paragraph
6633 // inside StartIndexSectionChecked could do the wrong thing otherwise
6634 xTOC
= StartIndexSectionChecked(bTableOfFigures
? "com.sun.star.text.IllustrationsIndex"
6637 const auto xTextCursor
= xTextAppend
->getText()->createTextCursor();
6639 xTextCursor
->gotoEnd(false);
6640 m_xTOCMarkerCursor
= xTextCursor
;
6644 // create TOC section
6645 css::uno::Reference
<css::text::XTextRange
> xTextRangeEndOfTocHeader
= GetTopTextAppend()->getEnd();
6646 xTOC
= createSectionForRange(m_xSdtEntryStart
, xTextRangeEndOfTocHeader
, sTOCServiceName
, false);
6648 // init [xTOCMarkerCursor]
6649 uno::Reference
< text::XText
> xText
= xTextAppend
->getText();
6650 uno::Reference
< text::XTextCursor
> xCrsr
= xText
->createTextCursor();
6651 m_xTOCMarkerCursor
= xCrsr
;
6653 // create header of the TOC with the TOC title inside
6654 createSectionForRange(m_xSdtEntryStart
, xTextRangeEndOfTocHeader
, "com.sun.star.text.IndexHeaderSection", true);
6659 pContext
->SetTOC(xTOC
);
6660 m_bParaHadField
= false;
6665 xTOC
->setPropertyValue(getPropertyName( PROP_TITLE
), uno::Any(aTocTitle
));
6667 if (!aBookmarkName
.isEmpty())
6668 xTOC
->setPropertyValue(getPropertyName(PROP_TOC_BOOKMARK
), uno::Any(aBookmarkName
));
6669 if (!bTableOfFigures
)
6671 xTOC
->setPropertyValue( getPropertyName( PROP_LEVEL
), uno::Any( nMaxLevel
) );
6672 xTOC
->setPropertyValue( getPropertyName( PROP_CREATE_FROM_OUTLINE
), uno::Any( bFromOutline
));
6673 xTOC
->setPropertyValue( getPropertyName( PROP_CREATE_FROM_MARKS
), uno::Any( bFromEntries
));
6674 xTOC
->setPropertyValue( getPropertyName( PROP_HIDE_TAB_LEADER_AND_PAGE_NUMBERS
), uno::Any( bHideTabLeaderPageNumbers
));
6675 xTOC
->setPropertyValue( getPropertyName( PROP_TAB_IN_TOC
), uno::Any( bIsTabEntry
));
6676 xTOC
->setPropertyValue( getPropertyName( PROP_TOC_NEW_LINE
), uno::Any( bNewLine
));
6677 xTOC
->setPropertyValue( getPropertyName( PROP_TOC_PARAGRAPH_OUTLINE_LEVEL
), uno::Any( bParagraphOutlineLevel
));
6678 if( !sTemplate
.isEmpty() )
6680 //the string contains comma separated the names and related levels
6681 //like: "Heading 1,1,Heading 2,2"
6684 sal_Int32 nPosition
= 0;
6685 auto const tsep(sTemplate
.indexOf(',') != -1 ? ',' : ';');
6686 while( nPosition
>= 0)
6688 OUString sStyleName
= sTemplate
.getToken(0, tsep
, nPosition
);
6689 //empty tokens should be skipped
6690 while( sStyleName
.isEmpty() && nPosition
> 0 )
6691 sStyleName
= sTemplate
.getToken(0, tsep
, nPosition
);
6692 nLevel
= o3tl::toInt32(o3tl::getToken(sTemplate
, 0, tsep
, nPosition
));
6695 if( !sStyleName
.isEmpty() )
6696 aMap
.emplace(nLevel
, sStyleName
);
6698 uno::Reference
< container::XIndexReplace
> xParaStyles
;
6699 xTOC
->getPropertyValue(getPropertyName(PROP_LEVEL_PARAGRAPH_STYLES
)) >>= xParaStyles
;
6700 for( nLevel
= 1; nLevel
< 10; ++nLevel
)
6702 sal_Int32 nLevelCount
= aMap
.count( nLevel
);
6705 TOCStyleMap::iterator aTOCStyleIter
= aMap
.find( nLevel
);
6707 uno::Sequence
< OUString
> aStyles( nLevelCount
);
6708 for ( auto& rStyle
: asNonConstRange(aStyles
) )
6710 // tdf#153083 must map w:styleId to w:name
6711 rStyle
= ConvertTOCStyleName(aTOCStyleIter
->second
);
6714 xParaStyles
->replaceByIndex(nLevel
- 1, uno::Any(aStyles
));
6717 xTOC
->setPropertyValue(getPropertyName(PROP_CREATE_FROM_LEVEL_PARAGRAPH_STYLES
), uno::Any( true ));
6721 uno::Reference
<container::XIndexAccess
> xChapterNumberingRules
;
6722 if (auto xSupplier
= GetTextDocument().query
<text::XChapterNumberingSupplier
>())
6723 xChapterNumberingRules
= xSupplier
->getChapterNumberingRules();
6724 uno::Reference
<container::XNameContainer
> xStyles
;
6725 if (auto xStylesSupplier
= GetTextDocument().query
<style::XStyleFamiliesSupplier
>())
6727 auto xStyleFamilies
= xStylesSupplier
->getStyleFamilies();
6728 xStyleFamilies
->getByName(getPropertyName(PROP_PARAGRAPH_STYLES
)) >>= xStyles
;
6731 uno::Reference
< container::XIndexReplace
> xLevelFormats
;
6732 xTOC
->getPropertyValue(getPropertyName(PROP_LEVEL_FORMAT
)) >>= xLevelFormats
;
6733 sal_Int32 nLevelCount
= xLevelFormats
->getCount();
6734 //start with level 1, 0 is the header level
6735 for( sal_Int32 nLevel
= 1; nLevel
< nLevelCount
; ++nLevel
)
6737 uno::Sequence
< beans::PropertyValues
> aLevel
;
6738 xLevelFormats
->getByIndex( nLevel
) >>= aLevel
;
6740 // Get the tab stops coming from the styles; store to the level definitions
6741 std::optional
<style::TabStop
> numTab
;
6742 if (xChapterNumberingRules
&& xStyles
)
6744 // This relies on the chapter numbering rules already defined
6745 // (see ListDef::CreateNumberingRules)
6746 uno::Sequence
<beans::PropertyValue
> props
;
6747 xChapterNumberingRules
->getByIndex(nLevel
- 1) >>= props
;
6748 bool bHasNumbering
= false;
6749 bool bUseTabStop
= false;
6750 for (const auto& propval
: props
)
6752 // We rely on PositionAndSpaceMode being always equal to LABEL_ALIGNMENT,
6753 // because ListDef::CreateNumberingRules doesn't create legacy lists
6754 if (propval
.Name
== "NumberingType")
6755 bHasNumbering
= propval
.Value
!= style::NumberingType::NUMBER_NONE
;
6756 else if (propval
.Name
== "LabelFollowedBy")
6757 bUseTabStop
= propval
.Value
== text::LabelFollow::LISTTAB
;
6758 // Do not use FirstLineIndent property from the rules, because it is unreliable
6760 if (bHasNumbering
&& bUseTabStop
)
6763 xTOC
->getPropertyValue("ParaStyleLevel" + OUString::number(nLevel
)) >>= style
;
6764 uno::Reference
<beans::XPropertySet
> xStyle
;
6765 if (xStyles
->getByName(style
) >>= xStyle
)
6767 if (uno::Reference
<beans::XPropertyState
> xPropState
{ xStyle
,
6770 if (xPropState
->getPropertyState("ParaTabStops")
6771 == beans::PropertyState_DIRECT_VALUE
)
6773 if (uno::Sequence
<style::TabStop
> tabStops
;
6774 xStyle
->getPropertyValue("ParaTabStops") >>= tabStops
)
6776 // If the style only has one tab stop, Word uses it for
6777 // page number, and generates the other from defaults
6778 if (tabStops
.getLength() > 1)
6779 numTab
= tabStops
[0];
6786 // Generate the default position.
6787 // Word uses multiples of 440 twips for default chapter number tab stops
6790 = o3tl::convert(440 * nLevel
, o3tl::Length::twip
, o3tl::Length::mm100
);
6795 uno::Sequence
< beans::PropertyValues
> aNewLevel
= lcl_createTOXLevelHyperlinks(
6796 bHyperlinks
, sChapterNoSeparator
,
6798 xLevelFormats
->replaceByIndex( nLevel
, uno::Any( aNewLevel
) );
6801 else // if (bTableOfFigures)
6803 if (!sFigureSequence
.isEmpty())
6804 xTOC
->setPropertyValue(getPropertyName(PROP_LABEL_CATEGORY
),
6805 uno::Any(sFigureSequence
));
6807 if (!sTemplate
.isEmpty())
6809 OUString
const sConvertedStyleName(ConvertTOCStyleName(sTemplate
));
6810 xTOC
->setPropertyValue("CreateFromParagraphStyle", uno::Any(sConvertedStyleName
));
6815 uno::Reference
< container::XIndexReplace
> xLevelFormats
;
6816 xTOC
->getPropertyValue(getPropertyName(PROP_LEVEL_FORMAT
)) >>= xLevelFormats
;
6817 uno::Sequence
< beans::PropertyValues
> aLevel
;
6818 xLevelFormats
->getByIndex( 1 ) >>= aLevel
;
6820 uno::Sequence
< beans::PropertyValues
> aNewLevel
= lcl_createTOXLevelHyperlinks(
6821 bHyperlinks
, sChapterNoSeparator
,
6823 xLevelFormats
->replaceByIndex( 1, uno::Any( aNewLevel
) );
6828 uno::Reference
<beans::XPropertySet
> DomainMapper_Impl::createSectionForRange(
6829 uno::Reference
< css::text::XTextRange
> xStart
,
6830 uno::Reference
< css::text::XTextRange
> xEnd
,
6831 const OUString
& sObjectType
,
6835 return uno::Reference
<beans::XPropertySet
>();
6837 return uno::Reference
<beans::XPropertySet
>();
6839 uno::Reference
< beans::XPropertySet
> xRet
;
6840 if (m_aTextAppendStack
.empty())
6842 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
6843 if(xTextAppend
.is())
6847 uno::Reference
< text::XParagraphCursor
> xCursor(
6848 xTextAppend
->createTextCursorByRange( xStart
), uno::UNO_QUERY_THROW
);
6849 //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
6850 xCursor
->gotoStartOfParagraph( false );
6851 xCursor
->gotoRange( xEnd
, true );
6852 //the paragraph after this new section is already inserted
6854 xCursor
->goLeft(1, true);
6855 uno::Reference
< text::XTextContent
> xSection( m_xTextFactory
->createInstance(sObjectType
), uno::UNO_QUERY_THROW
);
6858 xSection
->attach( uno::Reference
< text::XTextRange
>( xCursor
, uno::UNO_QUERY_THROW
) );
6860 catch(const uno::Exception
&)
6863 xRet
.set(xSection
, uno::UNO_QUERY
);
6865 catch(const uno::Exception
&)
6873 void DomainMapper_Impl::handleBibliography
6874 (const FieldContextPtr
& pContext
,
6875 const OUString
& sTOCServiceName
)
6877 if (m_aTextAppendStack
.empty())
6879 // tdf#130214: a workaround to avoid crash on import errors
6880 SAL_WARN("writerfilter.dmapper", "no text append stack");
6883 // Create section before setting m_bStartTOC and m_bStartBibliography: finishing paragraph
6884 // inside StartIndexSectionChecked could do the wrong thing otherwise
6885 const auto xTOC
= StartIndexSectionChecked(sTOCServiceName
);
6887 m_bStartBibliography
= true;
6890 xTOC
->setPropertyValue(getPropertyName( PROP_TITLE
), uno::Any(OUString()));
6892 pContext
->SetTOC( xTOC
);
6893 m_bParaHadField
= false;
6895 uno::Reference
< text::XTextContent
> xToInsert( xTOC
, uno::UNO_QUERY
);
6896 appendTextContent(xToInsert
, uno::Sequence
< beans::PropertyValue
>() );
6899 void DomainMapper_Impl::handleIndex
6900 (const FieldContextPtr
& pContext
,
6901 const OUString
& sTOCServiceName
)
6903 // only UserIndex can handle user index defined by \f
6904 // e.g. INDEX \f "user-index-id"
6905 OUString sUserIndex
;
6906 if ( lcl_FindInCommand( pContext
->GetCommand(), 'f', sUserIndex
) )
6907 sUserIndex
= lcl_trim(sUserIndex
);
6909 // Create section before setting m_bStartTOC and m_bStartIndex: finishing paragraph
6910 // inside StartIndexSectionChecked could do the wrong thing otherwise
6911 const auto xTOC
= StartIndexSectionChecked( sUserIndex
.isEmpty()
6913 : "com.sun.star.text.UserIndex");
6916 m_bStartIndex
= true;
6920 xTOC
->setPropertyValue(getPropertyName( PROP_TITLE
), uno::Any(OUString()));
6922 if( lcl_FindInCommand( pContext
->GetCommand(), 'r', sValue
))
6924 xTOC
->setPropertyValue("IsCommaSeparated", uno::Any(true));
6926 if( lcl_FindInCommand( pContext
->GetCommand(), 'h', sValue
))
6928 xTOC
->setPropertyValue("UseAlphabeticalSeparators", uno::Any(true));
6930 if( !sUserIndex
.isEmpty() )
6932 xTOC
->setPropertyValue("UserIndexName", uno::Any(sUserIndex
));
6935 pContext
->SetTOC( xTOC
);
6936 m_bParaHadField
= false;
6938 uno::Reference
< text::XTextContent
> xToInsert( xTOC
, uno::UNO_QUERY
);
6939 appendTextContent(xToInsert
, uno::Sequence
< beans::PropertyValue
>() );
6941 if( lcl_FindInCommand( pContext
->GetCommand(), 'c', sValue
))
6943 sValue
= sValue
.replaceAll("\"", "");
6944 uno::Reference
<text::XTextColumns
> xTextColumns
;
6947 xTOC
->getPropertyValue(getPropertyName( PROP_TEXT_COLUMNS
)) >>= xTextColumns
;
6949 if (xTextColumns
.is())
6951 xTextColumns
->setColumnCount( sValue
.toInt32() );
6952 xTOC
->setPropertyValue( getPropertyName( PROP_TEXT_COLUMNS
), uno::Any( xTextColumns
) );
6957 static auto InsertFieldmark(std::stack
<TextAppendContext
> & rTextAppendStack
,
6958 uno::Reference
<text::XFormField
> const& xFormField
,
6959 uno::Reference
<text::XTextRange
> const& xStartRange
,
6960 std::optional
<FieldId
> const oFieldId
) -> void
6962 uno::Reference
<text::XTextContent
> const xTextContent(xFormField
, uno::UNO_QUERY_THROW
);
6963 uno::Reference
<text::XTextAppend
> const& xTextAppend(rTextAppendStack
.top().xTextAppend
);
6964 uno::Reference
<text::XTextCursor
> const xCursor
=
6965 xTextAppend
->createTextCursorByRange(xStartRange
);
6966 if (rTextAppendStack
.top().xInsertPosition
.is())
6968 uno::Reference
<text::XTextRangeCompare
> const xCompare(
6969 rTextAppendStack
.top().xTextAppend
,
6971 if (xCompare
->compareRegionStarts(xStartRange
, rTextAppendStack
.top().xInsertPosition
) < 0)
6973 SAL_WARN("writerfilter.dmapper", "invalid field mark positions");
6976 xCursor
->gotoRange(rTextAppendStack
.top().xInsertPosition
, true);
6980 xCursor
->gotoEnd(true);
6982 xTextAppend
->insertTextContent(xCursor
, xTextContent
, true);
6984 && (oFieldId
== FIELD_FORMCHECKBOX
|| oFieldId
== FIELD_FORMDROPDOWN
))
6986 return; // only a single CH_TXT_ATR_FORMELEMENT!
6988 // problem: the fieldmark must be inserted in CloseFieldCommand(), because
6989 // attach() takes 2 positions, not 3!
6990 // FAIL: AppendTextNode() ignores the content index!
6991 // plan B: insert a spurious paragraph break now and join
6992 // it in PopFieldContext()!
6993 xCursor
->gotoRange(xTextContent
->getAnchor()->getEnd(), false);
6994 xCursor
->goLeft(1, false); // skip CH_TXT_ATR_FIELDEND
6995 xTextAppend
->insertControlCharacter(xCursor
, text::ControlCharacter::PARAGRAPH_BREAK
, false);
6996 xCursor
->goLeft(1, false); // back to previous paragraph
6997 rTextAppendStack
.push(TextAppendContext(xTextAppend
, xCursor
));
7000 static auto PopFieldmark(std::stack
<TextAppendContext
> & rTextAppendStack
,
7001 uno::Reference
<text::XTextCursor
> const& xCursor
,
7002 std::optional
<FieldId
> const oFieldId
) -> void
7005 && (oFieldId
== FIELD_FORMCHECKBOX
|| oFieldId
== FIELD_FORMDROPDOWN
))
7007 return; // only a single CH_TXT_ATR_FORMELEMENT!
7009 xCursor
->gotoRange(rTextAppendStack
.top().xInsertPosition
, false);
7010 xCursor
->goRight(1, true);
7011 xCursor
->setString(OUString()); // undo SplitNode from CloseFieldCommand()
7012 // note: paragraph properties will be overwritten
7013 // by finishParagraph() anyway so ignore here
7014 rTextAppendStack
.pop();
7017 void DomainMapper_Impl::CloseFieldCommand()
7019 if(m_bDiscardHeaderFooter
)
7022 TagLogger::getInstance().element("closeFieldCommand");
7025 FieldContextPtr pContext
;
7026 if(!m_aFieldStack
.empty())
7027 pContext
= m_aFieldStack
.back();
7028 OSL_ENSURE( pContext
, "no field context available");
7032 m_bSetUserFieldContent
= false;
7033 m_bSetCitation
= false;
7034 m_bSetDateValue
= false;
7035 // tdf#124472: If the normal command line is not empty, use it,
7036 // otherwise, the last active row is evaluated.
7037 if (!pContext
->GetCommandIsEmpty(false))
7038 pContext
->SetCommandType(false);
7040 const FieldConversionMap_t
& aFieldConversionMap
= lcl_GetFieldConversion();
7044 uno::Reference
< uno::XInterface
> xFieldInterface
;
7046 const auto& [sType
, vArguments
, vSwitches
]{ splitFieldCommand(pContext
->GetCommand()) };
7048 OUString
const sFirstParam(vArguments
.empty() ? OUString() : vArguments
.front());
7050 // apply font size to the form control
7051 if (!m_aTextAppendStack
.empty() && m_pLastCharacterContext
&& ( m_pLastCharacterContext
->isSet(PROP_CHAR_HEIGHT
) || m_pLastCharacterContext
->isSet(PROP_CHAR_FONT_NAME
)))
7053 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
7054 if (xTextAppend
.is())
7056 uno::Reference
< text::XTextCursor
> xCrsr
= xTextAppend
->getText()->createTextCursor();
7059 xCrsr
->gotoEnd(false);
7060 uno::Reference
< beans::XPropertySet
> xProp( xCrsr
, uno::UNO_QUERY
);
7061 if (m_pLastCharacterContext
->isSet(PROP_CHAR_HEIGHT
))
7063 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_HEIGHT
), m_pLastCharacterContext
->getProperty(PROP_CHAR_HEIGHT
)->second
);
7064 if (m_pLastCharacterContext
->isSet(PROP_CHAR_HEIGHT_COMPLEX
))
7065 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_HEIGHT_COMPLEX
), m_pLastCharacterContext
->getProperty(PROP_CHAR_HEIGHT_COMPLEX
)->second
);
7067 if (m_pLastCharacterContext
->isSet(PROP_CHAR_FONT_NAME
))
7068 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME
), m_pLastCharacterContext
->getProperty(PROP_CHAR_FONT_NAME
)->second
);
7073 FieldConversionMap_t::const_iterator
const aIt
= aFieldConversionMap
.find(sType
);
7074 if (aIt
!= aFieldConversionMap
.end()
7075 && (!m_bForceGenericFields
7076 // these need to convert ffData to properties...
7077 || (aIt
->second
.eFieldId
== FIELD_FORMCHECKBOX
)
7078 || (aIt
->second
.eFieldId
== FIELD_FORMDROPDOWN
)
7079 || (aIt
->second
.eFieldId
== FIELD_FORMTEXT
)))
7081 pContext
->SetFieldId(aIt
->second
.eFieldId
);
7082 bool bCreateEnhancedField
= false;
7083 uno::Reference
< beans::XPropertySet
> xFieldProperties
;
7084 bool bCreateField
= true;
7085 switch (aIt
->second
.eFieldId
)
7087 case FIELD_HYPERLINK
:
7088 case FIELD_DOCPROPERTY
:
7092 case FIELD_BIBLIOGRAPHY
:
7093 case FIELD_CITATION
:
7096 case FIELD_INCLUDEPICTURE
:
7098 case FIELD_GOTOBUTTON
:
7099 bCreateField
= false;
7101 case FIELD_FORMCHECKBOX
:
7102 case FIELD_FORMTEXT
:
7103 case FIELD_FORMDROPDOWN
:
7105 // If we use 'enhanced' fields then FIELD_FORMCHECKBOX,
7106 // FIELD_FORMTEXT & FIELD_FORMDROPDOWN are treated specially
7107 if ( m_bUsingEnhancedFields
)
7109 bCreateField
= false;
7110 bCreateEnhancedField
= true;
7112 // for non enhanced fields checkboxes are displayed
7113 // as an awt control not a field
7114 else if ( aIt
->second
.eFieldId
== FIELD_FORMCHECKBOX
)
7115 bCreateField
= false;
7120 FieldContextPtr pOuter
= GetParentFieldContext(m_aFieldStack
);
7123 if (!IsFieldNestingAllowed(pOuter
, m_aFieldStack
.back()))
7125 // Parent field can't host this child field: don't create a child field
7127 bCreateField
= false;
7133 if (IsInTOC() && (aIt
->second
.eFieldId
== FIELD_PAGEREF
))
7135 bCreateField
= false;
7138 if( bCreateField
|| bCreateEnhancedField
)
7140 //add the service prefix
7141 OUString
sServiceName("com.sun.star.text.");
7142 if ( bCreateEnhancedField
)
7144 const FieldConversionMap_t
& aEnhancedFieldConversionMap
= lcl_GetEnhancedFieldConversion();
7145 FieldConversionMap_t::const_iterator aEnhancedIt
=
7146 aEnhancedFieldConversionMap
.find(sType
);
7147 if ( aEnhancedIt
!= aEnhancedFieldConversionMap
.end())
7148 sServiceName
+= OUString::createFromAscii(aEnhancedIt
->second
.cFieldServiceName
);
7152 sServiceName
+= "TextField." + OUString::createFromAscii(aIt
->second
.cFieldServiceName
);
7156 TagLogger::getInstance().startElement("fieldService");
7157 TagLogger::getInstance().chars(sServiceName
);
7158 TagLogger::getInstance().endElement();
7161 if (m_xTextFactory
.is())
7163 xFieldInterface
= m_xTextFactory
->createInstance(sServiceName
);
7164 xFieldProperties
.set( xFieldInterface
, uno::UNO_QUERY_THROW
);
7167 switch( aIt
->second
.eFieldId
)
7169 case FIELD_ADDRESSBLOCK
: break;
7170 case FIELD_ADVANCE
: break;
7172 handleFieldAsk(pContext
, xFieldInterface
, xFieldProperties
);
7174 case FIELD_AUTONUM
:
7175 case FIELD_AUTONUMLGL
:
7176 case FIELD_AUTONUMOUT
:
7177 handleAutoNum(pContext
, xFieldInterface
, xFieldProperties
);
7180 case FIELD_USERNAME
:
7181 case FIELD_USERINITIALS
:
7182 handleAuthor(sFirstParam
,
7184 aIt
->second
.eFieldId
);
7187 if (xFieldProperties
.is())
7189 // Get field fixed property from the context handler
7190 if (pContext
->IsFieldLocked())
7192 xFieldProperties
->setPropertyValue(
7193 getPropertyName(PROP_IS_FIXED
),
7195 m_bSetDateValue
= true;
7198 xFieldProperties
->setPropertyValue(
7199 getPropertyName(PROP_IS_FIXED
),
7202 xFieldProperties
->setPropertyValue(
7203 getPropertyName(PROP_IS_DATE
),
7205 SetNumberFormat( pContext
->GetCommand(), xFieldProperties
);
7208 case FIELD_COMMENTS
:
7210 // OUString sParam = lcl_ExtractParameter(pContext->GetCommand(), sizeof(" COMMENTS") );
7211 // A parameter with COMMENTS shouldn't set fixed
7212 // ( or at least the binary filter doesn't )
7213 // If we set fixed then we won't export a field cmd.
7214 // Additionally the para in COMMENTS is more like an
7215 // instruction to set the document property comments
7216 // with the param ( e.g. each COMMENT with a param will
7217 // overwrite the Comments document property
7218 // #TODO implement the above too
7219 xFieldProperties
->setPropertyValue(
7220 getPropertyName( PROP_IS_FIXED
), uno::Any( false ));
7221 //PROP_CURRENT_PRESENTATION is set later anyway
7224 case FIELD_CREATEDATE
:
7225 case FIELD_PRINTDATE
:
7226 case FIELD_SAVEDATE
:
7228 if (pContext
->IsFieldLocked())
7230 xFieldProperties
->setPropertyValue(
7231 getPropertyName(PROP_IS_FIXED
), uno::Any( true ));
7233 xFieldProperties
->setPropertyValue(
7234 getPropertyName( PROP_IS_DATE
), uno::Any( true ));
7235 SetNumberFormat( pContext
->GetCommand(), xFieldProperties
);
7238 case FIELD_DOCPROPERTY
:
7239 handleDocProperty(pContext
, sFirstParam
,
7242 case FIELD_DOCVARIABLE
:
7246 //create a user field and type
7247 uno::Reference
<beans::XPropertySet
> xMaster
= FindOrCreateFieldMaster(
7248 "com.sun.star.text.FieldMaster.User", sFirstParam
);
7249 uno::Reference
<text::XDependentTextField
> xDependentField(
7250 xFieldInterface
, uno::UNO_QUERY_THROW
);
7251 xDependentField
->attachTextFieldMaster(xMaster
);
7252 m_bSetUserFieldContent
= true;
7256 case FIELD_EDITTIME
:
7257 //it's a numbering type, no number format! SetNumberFormat( pContext->GetCommand(), xFieldProperties );
7261 OUString aCommand
= pContext
->GetCommand().trim();
7263 msfilter::util::EquationResult
aResult(msfilter::util::ParseCombinedChars(aCommand
));
7264 if (!aResult
.sType
.isEmpty() && m_xTextFactory
.is())
7266 xFieldInterface
= m_xTextFactory
->createInstance("com.sun.star.text.TextField." + aResult
.sType
);
7268 uno::Reference
< beans::XPropertySet
>( xFieldInterface
,
7269 uno::UNO_QUERY_THROW
);
7270 xFieldProperties
->setPropertyValue(getPropertyName(PROP_CONTENT
), uno::Any(aResult
.sResult
));
7274 //merge Read_SubF_Ruby into filter/.../util.cxx and reuse that ?
7275 sal_Int32 nSpaceIndex
= aCommand
.indexOf(' ');
7277 aCommand
= o3tl::trim(aCommand
.subView(nSpaceIndex
));
7278 if (aCommand
.startsWith("\\s"))
7280 aCommand
= aCommand
.copy(2);
7281 if (aCommand
.startsWith("\\do"))
7283 aCommand
= aCommand
.copy(3);
7284 sal_Int32 nStartIndex
= aCommand
.indexOf('(');
7285 sal_Int32 nEndIndex
= aCommand
.indexOf(')');
7286 if (nStartIndex
> 0 && nEndIndex
> 0)
7288 // nDown is the requested "lower by" value in points.
7289 sal_Int32 nDown
= o3tl::toInt32(aCommand
.subView(0, nStartIndex
));
7290 OUString aContent
= aCommand
.copy(nStartIndex
+ 1, nEndIndex
- nStartIndex
- 1);
7291 PropertyMapPtr pCharContext
= GetTopContext();
7292 // dHeight is the font size of the current style.
7294 if ((GetPropertyFromParaStyleSheet(PROP_CHAR_HEIGHT
) >>= dHeight
) && dHeight
!= 0)
7295 // Character escapement should be given in negative percents for subscripts.
7296 pCharContext
->Insert(PROP_CHAR_ESCAPEMENT
, uno::Any( sal_Int16(- 100 * nDown
/ dHeight
) ) );
7297 appendTextPortion(aContent
, pCharContext
);
7301 else if (aCommand
.startsWith("\\* jc"))
7303 handleRubyEQField(pContext
);
7309 if (xFieldProperties
.is())
7310 xFieldProperties
->setPropertyValue(
7311 getPropertyName(PROP_HINT
), uno::Any( pContext
->GetCommand().getToken(1, '\"')));
7313 case FIELD_FILENAME
:
7315 sal_Int32 nNumberingTypeIndex
= pContext
->GetCommand().indexOf("\\p");
7316 if (xFieldProperties
.is())
7317 xFieldProperties
->setPropertyValue(
7318 getPropertyName(PROP_FILE_FORMAT
),
7319 uno::Any( nNumberingTypeIndex
> 0 ? text::FilenameDisplayFormat::FULL
: text::FilenameDisplayFormat::NAME_AND_EXT
));
7322 case FIELD_FILESIZE
: break;
7323 case FIELD_FORMULA
:
7326 handleFieldFormula(pContext
, xFieldProperties
);
7329 case FIELD_FORMCHECKBOX
:
7330 case FIELD_FORMDROPDOWN
:
7331 case FIELD_FORMTEXT
:
7333 if (bCreateEnhancedField
)
7335 FFDataHandler::Pointer_t
7336 pFFDataHandler(pContext
->getFFDataHandler());
7337 FormControlHelper::Pointer_t
7338 pFormControlHelper(new FormControlHelper
7339 (m_bUsingEnhancedFields
? aIt
->second
.eFieldId
: FIELD_FORMCHECKBOX
,
7341 m_xTextDocument
, pFFDataHandler
));
7342 pContext
->setFormControlHelper(pFormControlHelper
);
7343 uno::Reference
< text::XFormField
> xFormField( xFieldInterface
, uno::UNO_QUERY
);
7344 uno::Reference
< container::XNamed
> xNamed( xFormField
, uno::UNO_QUERY
);
7347 if ( pFFDataHandler
&& !pFFDataHandler
->getName().isEmpty() )
7348 xNamed
->setName( pFFDataHandler
->getName() );
7349 pContext
->SetFormField( xFormField
);
7351 InsertFieldmark(m_aTextAppendStack
,
7352 xFormField
, pContext
->GetStartRange(),
7353 pContext
->GetFieldId());
7357 if ( aIt
->second
.eFieldId
== FIELD_FORMDROPDOWN
)
7358 lcl_handleDropdownField( xFieldProperties
, pContext
->getFFDataHandler() );
7360 lcl_handleTextField( xFieldProperties
, pContext
->getFFDataHandler() );
7364 case FIELD_GOTOBUTTON
: break;
7365 case FIELD_HYPERLINK
:
7367 ::std::vector
<OUString
> aParts
= pContext
->GetCommandParts();
7369 // Syntax is either:
7370 // HYPERLINK "" \l "link"
7372 // HYPERLINK \l "link"
7373 // Make sure "HYPERLINK" doesn't end up as part of link in the second case.
7374 if (!aParts
.empty() && aParts
[0] == "HYPERLINK")
7375 aParts
.erase(aParts
.begin());
7377 ::std::vector
<OUString
>::const_iterator aItEnd
= aParts
.end();
7378 ::std::vector
<OUString
>::const_iterator aPartIt
= aParts
.begin();
7383 while (aPartIt
!= aItEnd
)
7385 if ( *aPartIt
== "\\l" )
7389 if (aPartIt
== aItEnd
)
7392 sURL
+= "#" + *aPartIt
;
7394 else if (*aPartIt
== "\\m" || *aPartIt
== "\\n" || *aPartIt
== "\\h")
7397 else if ( *aPartIt
== "\\o" || *aPartIt
== "\\t" )
7401 if (aPartIt
== aItEnd
)
7414 if (!sURL
.isEmpty())
7416 if (sURL
.startsWith("file:///"))
7418 // file:///absolute\\path\\to\\file => invalid file URI (Writer cannot open)
7419 // convert all double backslashes to slashes:
7420 sURL
= sURL
.replaceAll("\\\\", "/");
7422 // file:///absolute\path\to\file => invalid file URI (Writer cannot open)
7423 // convert all backslashes to slashes:
7424 sURL
= sURL
.replace('\\', '/');
7426 // Try to make absolute any relative URLs, except
7427 // for relative same-document URLs that only contain
7429 else if (!sURL
.startsWith("#")) {
7431 sURL
= rtl::Uri::convertRelToAbs(
7433 } catch (rtl::MalformedUriException
& e
) {
7435 "writerfilter.dmapper",
7436 "MalformedUriException "
7440 pContext
->SetHyperlinkURL(sURL
);
7443 if (!sTarget
.isEmpty())
7444 pContext
->SetHyperlinkTarget(sTarget
);
7449 if (vArguments
.size() < 3)
7451 SAL_WARN("writerfilter.dmapper", "IF field requires at lest 3 parameters!");
7455 if (xFieldProperties
.is())
7457 // Following code assumes that last argument in field is false value
7458 // before it - true value and everything before them is a condition
7459 OUString sCondition
;
7461 while (i
< vArguments
.size() - 2) {
7462 if (!sCondition
.isEmpty())
7464 sCondition
+= vArguments
[i
++];
7467 xFieldProperties
->setPropertyValue(
7468 "TrueContent", uno::Any(vArguments
[vArguments
.size() - 2]));
7469 xFieldProperties
->setPropertyValue(
7470 "FalseContent", uno::Any(vArguments
[vArguments
.size() - 1]));
7471 xFieldProperties
->setPropertyValue(
7472 "Condition", uno::Any(sCondition
));
7476 case FIELD_INFO
: break;
7477 case FIELD_INCLUDEPICTURE
: break;
7478 case FIELD_KEYWORDS
:
7480 if (!sFirstParam
.isEmpty())
7482 xFieldProperties
->setPropertyValue(
7483 getPropertyName( PROP_IS_FIXED
), uno::Any( true ));
7484 //PROP_CURRENT_PRESENTATION is set later anyway
7488 case FIELD_LASTSAVEDBY
:
7489 xFieldProperties
->setPropertyValue(
7490 getPropertyName(PROP_IS_FIXED
), uno::Any(true));
7492 case FIELD_MACROBUTTON
:
7494 if (xFieldProperties
.is())
7496 sal_Int32 nIndex
= sizeof(" MACROBUTTON ");
7497 OUString sCommand
= pContext
->GetCommand();
7499 //extract macro name
7500 if (sCommand
.getLength() >= nIndex
)
7502 OUString sMacro
= sCommand
.getToken(0, ' ', nIndex
);
7503 xFieldProperties
->setPropertyValue(
7504 getPropertyName(PROP_MACRO_NAME
), uno::Any( sMacro
));
7507 //extract quick help text
7508 if (sCommand
.getLength() > nIndex
+ 1)
7510 xFieldProperties
->setPropertyValue(
7511 getPropertyName(PROP_HINT
),
7512 uno::Any( sCommand
.copy( nIndex
)));
7517 case FIELD_MERGEFIELD
:
7519 //todo: create a database field and fieldmaster pointing to a column, only
7520 //create a user field and type
7521 uno::Reference
< beans::XPropertySet
> xMaster
=
7522 FindOrCreateFieldMaster("com.sun.star.text.FieldMaster.Database", sFirstParam
);
7524 // xFieldProperties->setPropertyValue(
7526 // uno::makeAny( pContext->GetCommand().copy( nIndex + 1 )));
7527 uno::Reference
< text::XDependentTextField
> xDependentField( xFieldInterface
, uno::UNO_QUERY_THROW
);
7528 xDependentField
->attachTextFieldMaster( xMaster
);
7531 case FIELD_MERGEREC
: break;
7532 case FIELD_MERGESEQ
: break;
7533 case FIELD_NEXT
: break;
7534 case FIELD_NEXTIF
: break;
7536 if (xFieldProperties
.is())
7538 xFieldProperties
->setPropertyValue(
7539 getPropertyName(PROP_NUMBERING_TYPE
),
7540 uno::Any( lcl_ParseNumberingType(pContext
->GetCommand()) ));
7541 xFieldProperties
->setPropertyValue(
7542 getPropertyName(PROP_SUB_TYPE
),
7543 uno::Any( text::PageNumberType_CURRENT
));
7549 if (xFieldProperties
.is() && !IsInTOC())
7551 bool bPageRef
= aIt
->second
.eFieldId
== FIELD_PAGEREF
;
7553 // Do we need a GetReference (default) or a GetExpression field?
7554 uno::Reference
< text::XTextFieldsSupplier
> xFieldsSupplier( GetTextDocument(), uno::UNO_QUERY
);
7555 uno::Reference
< container::XNameAccess
> xFieldMasterAccess
= xFieldsSupplier
->getTextFieldMasters();
7557 if (!xFieldMasterAccess
->hasByName(
7558 "com.sun.star.text.FieldMaster.SetExpression."
7561 xFieldProperties
->setPropertyValue(
7562 getPropertyName(PROP_REFERENCE_FIELD_SOURCE
),
7563 uno::Any( sal_Int16(text::ReferenceFieldSource::BOOKMARK
)) );
7564 xFieldProperties
->setPropertyValue(
7565 getPropertyName(PROP_SOURCE_NAME
),
7566 uno::Any(sFirstParam
) );
7567 sal_Int16 nFieldPart
= (bPageRef
? text::ReferenceFieldPart::PAGE
: text::ReferenceFieldPart::TEXT
);
7569 if( lcl_FindInCommand( pContext
->GetCommand(), 'p', sValue
))
7572 nFieldPart
= text::ReferenceFieldPart::UP_DOWN
;
7574 else if( lcl_FindInCommand( pContext
->GetCommand(), 'r', sValue
))
7577 nFieldPart
= text::ReferenceFieldPart::NUMBER
;
7579 else if( lcl_FindInCommand( pContext
->GetCommand(), 'n', sValue
))
7582 nFieldPart
= text::ReferenceFieldPart::NUMBER_NO_CONTEXT
;
7584 else if( lcl_FindInCommand( pContext
->GetCommand(), 'w', sValue
))
7586 //number-full-context
7587 nFieldPart
= text::ReferenceFieldPart::NUMBER_FULL_CONTEXT
;
7589 xFieldProperties
->setPropertyValue(
7590 getPropertyName( PROP_REFERENCE_FIELD_PART
), uno::Any( nFieldPart
));
7592 else if( m_xTextFactory
.is() )
7594 xFieldInterface
= m_xTextFactory
->createInstance("com.sun.star.text.TextField.GetExpression");
7595 xFieldProperties
.set(xFieldInterface
, uno::UNO_QUERY
);
7596 xFieldProperties
->setPropertyValue(
7597 getPropertyName(PROP_CONTENT
),
7598 uno::Any(sFirstParam
));
7599 xFieldProperties
->setPropertyValue(getPropertyName(PROP_SUB_TYPE
), uno::Any(text::SetVariableType::STRING
));
7603 case FIELD_REVNUM
: break;
7604 case FIELD_SECTION
: break;
7605 case FIELD_SECTIONPAGES
: break;
7608 // command looks like: " SEQ Table \* ARABIC "
7609 OUString
sCmd(pContext
->GetCommand());
7610 // find the sequence name, e.g. "SEQ"
7611 std::u16string_view sSeqName
= msfilter::util::findQuotedText(sCmd
, u
"SEQ ", '\\');
7612 sSeqName
= o3tl::trim(sSeqName
);
7614 // create a sequence field master using the sequence name
7615 uno::Reference
< beans::XPropertySet
> xMaster
= FindOrCreateFieldMaster(
7616 "com.sun.star.text.FieldMaster.SetExpression",
7617 OUString(sSeqName
));
7619 xMaster
->setPropertyValue(
7620 getPropertyName(PROP_SUB_TYPE
),
7621 uno::Any(text::SetVariableType::SEQUENCE
));
7623 // apply the numbering type
7624 xFieldProperties
->setPropertyValue(
7625 getPropertyName(PROP_NUMBERING_TYPE
),
7626 uno::Any( lcl_ParseNumberingType(pContext
->GetCommand()) ));
7628 // attach the master to the field
7629 uno::Reference
< text::XDependentTextField
> xDependentField( xFieldInterface
, uno::UNO_QUERY_THROW
);
7630 xDependentField
->attachTextFieldMaster( xMaster
);
7632 OUString sFormula
= OUString::Concat(sSeqName
) + "+1";
7634 if( lcl_FindInCommand( pContext
->GetCommand(), 'c', sValue
))
7636 sFormula
= sSeqName
;
7638 else if( lcl_FindInCommand( pContext
->GetCommand(), 'r', sValue
))
7642 // TODO \s isn't handled, but the spec isn't easy to understand without
7643 // an example for this one.
7644 xFieldProperties
->setPropertyValue(
7645 getPropertyName(PROP_CONTENT
),
7646 uno::Any(sFormula
));
7648 // Take care of the numeric formatting definition, default is Arabic
7649 sal_Int16 nNumberingType
= lcl_ParseNumberingType(pContext
->GetCommand());
7650 if (nNumberingType
== style::NumberingType::PAGE_DESCRIPTOR
)
7651 nNumberingType
= style::NumberingType::ARABIC
;
7652 xFieldProperties
->setPropertyValue(
7653 getPropertyName(PROP_NUMBERING_TYPE
),
7654 uno::Any(nNumberingType
));
7658 handleFieldSet(pContext
, xFieldInterface
, xFieldProperties
);
7660 case FIELD_SKIPIF
: break;
7661 case FIELD_STYLEREF
: break;
7662 case FIELD_SUBJECT
:
7664 if (!sFirstParam
.isEmpty())
7666 xFieldProperties
->setPropertyValue(
7667 getPropertyName( PROP_IS_FIXED
), uno::Any( true ));
7668 //PROP_CURRENT_PRESENTATION is set later anyway
7674 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
7675 OUString
sSymbol( sal_Unicode( sFirstParam
.startsWithIgnoreAsciiCase("0x") ? o3tl::toUInt32(sFirstParam
.subView(2),16) : sFirstParam
.toUInt32() ) );
7677 bool bHasFont
= lcl_FindInCommand( pContext
->GetCommand(), 'f', sFont
);
7680 sFont
= sFont
.trim();
7681 if (sFont
.startsWith("\""))
7682 sFont
= sFont
.copy(1);
7683 if (sFont
.endsWith("\""))
7684 sFont
= sFont
.copy(0,sFont
.getLength()-1);
7689 if (xTextAppend
.is())
7691 uno::Reference
< text::XText
> xText
= xTextAppend
->getText();
7692 uno::Reference
< text::XTextCursor
> xCrsr
= xText
->createTextCursor();
7695 xCrsr
->gotoEnd(false);
7696 xText
->insertString(xCrsr
, sSymbol
, true);
7697 uno::Reference
< beans::XPropertySet
> xProp( xCrsr
, uno::UNO_QUERY
);
7698 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_FONT_CHAR_SET
), uno::Any(awt::CharSet::SYMBOL
));
7701 uno::Any
aVal( sFont
);
7702 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME
), aVal
);
7703 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME_ASIAN
), aVal
);
7704 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME_COMPLEX
), aVal
);
7711 case FIELD_TEMPLATE
: break;
7714 if (pContext
->IsFieldLocked())
7716 xFieldProperties
->setPropertyValue(
7717 getPropertyName(PROP_IS_FIXED
),
7719 m_bSetDateValue
= true;
7721 SetNumberFormat( pContext
->GetCommand(), xFieldProperties
);
7726 if (!sFirstParam
.isEmpty())
7728 xFieldProperties
->setPropertyValue(
7729 getPropertyName( PROP_IS_FIXED
), uno::Any( true ));
7730 //PROP_CURRENT_PRESENTATION is set later anyway
7734 case FIELD_USERADDRESS
: //todo: user address collects street, city ...
7737 handleIndex(pContext
,
7738 OUString::createFromAscii(aIt
->second
.cFieldServiceName
));
7740 case FIELD_BIBLIOGRAPHY
:
7741 handleBibliography(pContext
,
7742 OUString::createFromAscii(aIt
->second
.cFieldServiceName
));
7746 OUString::createFromAscii(aIt
->second
.cFieldServiceName
));
7750 if( !m_xTextFactory
.is() )
7753 // only UserIndexMark can handle user index types defined by \f
7754 // e.g. XE "text" \f "user-index-id"
7755 OUString sUserIndex
;
7756 OUString sFieldServiceName
=
7757 lcl_FindInCommand( pContext
->GetCommand(), 'f', sUserIndex
)
7758 ? "com.sun.star.text.UserIndexMark"
7759 : OUString::createFromAscii(aIt
->second
.cFieldServiceName
);
7760 uno::Reference
< beans::XPropertySet
> xTC(
7761 m_xTextFactory
->createInstance(sFieldServiceName
),
7762 uno::UNO_QUERY_THROW
);
7764 if (!sFirstParam
.isEmpty())
7766 xTC
->setPropertyValue(sUserIndex
.isEmpty()
7767 ? OUString("PrimaryKey")
7768 : OUString("AlternativeText"),
7769 uno::Any(sFirstParam
));
7772 sUserIndex
= lcl_trim(sUserIndex
);
7773 if (!sUserIndex
.isEmpty())
7775 xTC
->setPropertyValue("UserIndexName",
7776 uno::Any(sUserIndex
));
7778 uno::Reference
< text::XTextContent
> xToInsert( xTC
, uno::UNO_QUERY
);
7779 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
7780 if (xTextAppend
.is())
7782 uno::Reference
< text::XText
> xText
= xTextAppend
->getText();
7783 uno::Reference
< text::XTextCursor
> xCrsr
= xText
->createTextCursor();
7786 xCrsr
->gotoEnd(false);
7787 xText
->insertTextContent(uno::Reference
< text::XTextRange
>( xCrsr
, uno::UNO_QUERY_THROW
), xToInsert
, false);
7792 case FIELD_CITATION
:
7794 if( !m_xTextFactory
.is() )
7797 xFieldInterface
= m_xTextFactory
->createInstance(
7798 OUString::createFromAscii(aIt
->second
.cFieldServiceName
));
7799 uno::Reference
< beans::XPropertySet
> xTC(xFieldInterface
,
7800 uno::UNO_QUERY_THROW
);
7801 OUString
sCmd(pContext
->GetCommand());//sCmd is the entire instrText including the index e.g. CITATION Kra06 \l 1033
7802 if( !sCmd
.isEmpty()){
7803 uno::Sequence
<beans::PropertyValue
> aValues( comphelper::InitPropertySequence({
7804 { "Identifier", uno::Any(sCmd
) }
7806 xTC
->setPropertyValue("Fields", uno::Any(aValues
));
7808 uno::Reference
< text::XTextContent
> xToInsert( xTC
, uno::UNO_QUERY
);
7810 uno::Sequence
<beans::PropertyValue
> aValues
7811 = m_aFieldStack
.back()->getProperties()->GetPropertyValues();
7812 appendTextContent(xToInsert
, aValues
);
7813 m_bSetCitation
= true;
7819 if( !m_xTextFactory
.is() )
7822 uno::Reference
< beans::XPropertySet
> xTC(
7823 m_xTextFactory
->createInstance(
7824 OUString::createFromAscii(aIt
->second
.cFieldServiceName
)),
7825 uno::UNO_QUERY_THROW
);
7826 if (!sFirstParam
.isEmpty())
7828 xTC
->setPropertyValue(getPropertyName(PROP_ALTERNATIVE_TEXT
),
7829 uno::Any(sFirstParam
));
7832 // \f TC entry in doc with multiple tables
7833 // if( lcl_FindInCommand( pContext->GetCommand(), 'f', sValue ))
7835 // todo: unsupported
7837 if( lcl_FindInCommand( pContext
->GetCommand(), 'l', sValue
))
7840 sal_Int32 nLevel
= sValue
.toInt32();
7841 if( !sValue
.isEmpty() && nLevel
>= 0 && nLevel
<= 10 )
7842 xTC
->setPropertyValue(getPropertyName(PROP_LEVEL
), uno::Any( static_cast<sal_Int16
>(nLevel
) ));
7844 // if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
7845 // \n Suppress page numbers
7847 //todo: unsupported feature
7849 pContext
->SetTC( xTC
);
7852 case FIELD_NUMCHARS
:
7853 case FIELD_NUMWORDS
:
7854 case FIELD_NUMPAGES
:
7855 if (xFieldProperties
.is())
7856 xFieldProperties
->setPropertyValue(
7857 getPropertyName(PROP_NUMBERING_TYPE
),
7858 uno::Any( lcl_ParseNumberingType(pContext
->GetCommand()) ));
7862 if (!bCreateEnhancedField
)
7864 pContext
->SetTextField( uno::Reference
<text::XTextField
>(xFieldInterface
, uno::UNO_QUERY
) );
7869 /* Unsupported fields will be handled here for docx file.
7870 * To handle unsupported fields used fieldmark API.
7872 OUString
aCode( pContext
->GetCommand().trim() );
7873 // Don't waste resources on wrapping shapes inside a fieldmark.
7874 if (sType
!= "SHAPE" && m_xTextFactory
.is() && !m_aTextAppendStack
.empty())
7876 xFieldInterface
= m_xTextFactory
->createInstance("com.sun.star.text.Fieldmark");
7878 uno::Reference
<text::XFormField
> const xFormField(xFieldInterface
, uno::UNO_QUERY
);
7879 InsertFieldmark(m_aTextAppendStack
, xFormField
, pContext
->GetStartRange(),
7880 pContext
->GetFieldId());
7881 xFormField
->setFieldType(ODF_UNHANDLED
);
7882 ++m_nStartGenericField
;
7883 pContext
->SetFormField( xFormField
);
7884 uno::Reference
<container::XNameContainer
> const xNameCont(xFormField
->getParameters());
7885 // note: setting the code to empty string is *required* in
7886 // m_bForceGenericFields mode, or the export will write
7887 // the ODF_UNHANDLED string!
7888 assert(!m_bForceGenericFields
|| aCode
.isEmpty());
7889 xNameCont
->insertByName(ODF_CODE_PARAM
, uno::Any(aCode
));
7890 ww::eField
const id(GetWW8FieldId(sType
));
7891 if (id
!= ww::eNONE
)
7892 { // tdf#129247 tdf#134264 set WW8 id for WW8 export
7893 xNameCont
->insertByName(ODF_ID_PARAM
, uno::Any(OUString::number(id
)));
7897 m_bParaHadField
= false;
7900 catch( const uno::Exception
& )
7902 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "Exception in CloseFieldCommand()" );
7904 pContext
->SetCommandCompleted();
7906 /*-------------------------------------------------------------------------
7907 //the _current_ fields require a string type result while TOCs accept richt results
7908 -----------------------------------------------------------------------*/
7909 bool DomainMapper_Impl::IsFieldResultAsString()
7912 OSL_ENSURE( !m_aFieldStack
.empty(), "field stack empty?");
7913 FieldContextPtr pContext
= m_aFieldStack
.back();
7914 OSL_ENSURE( pContext
, "no field context available");
7917 bRet
= pContext
->GetTextField().is()
7918 || pContext
->GetFieldId() == FIELD_FORMDROPDOWN
7919 || pContext
->GetFieldId() == FIELD_FILLIN
;
7924 FieldContextPtr pOuter
= GetParentFieldContext(m_aFieldStack
);
7927 if (!IsFieldNestingAllowed(pOuter
, m_aFieldStack
.back()))
7929 // If nesting is not allowed, then the result can only be a string.
7937 void DomainMapper_Impl::AppendFieldResult(std::u16string_view rString
)
7939 assert(!m_aFieldStack
.empty());
7940 FieldContextPtr pContext
= m_aFieldStack
.back();
7941 SAL_WARN_IF(!pContext
, "writerfilter.dmapper", "no field context");
7945 FieldContextPtr pOuter
= GetParentFieldContext(m_aFieldStack
);
7948 if (!IsFieldNestingAllowed(pOuter
, pContext
))
7950 if (pOuter
->IsCommandCompleted())
7952 // Child can't host the field result, forward to parent's result.
7953 pOuter
->AppendResult(rString
);
7959 pContext
->AppendResult(rString
);
7962 // Calculates css::DateTime based on ddddd.sssss since 1899-12-30
7963 static util::DateTime
lcl_dateTimeFromSerial(const double& dSerial
)
7965 DateTime
d(Date(30, 12, 1899));
7967 return d
.GetUNODateTime();
7970 void DomainMapper_Impl::SetFieldResult(OUString
const& rResult
)
7973 TagLogger::getInstance().startElement("setFieldResult");
7974 TagLogger::getInstance().chars(rResult
);
7977 FieldContextPtr pContext
= m_aFieldStack
.back();
7978 OSL_ENSURE( pContext
, "no field context available");
7980 if (m_aFieldStack
.size() > 1)
7982 // This is a nested field. See if the parent supports nesting on the Writer side.
7983 FieldContextPtr pParentContext
= m_aFieldStack
[m_aFieldStack
.size() - 2];
7986 std::vector
<OUString
> aParentParts
= pParentContext
->GetCommandParts();
7987 // Conditional text fields don't support nesting in Writer.
7988 if (!aParentParts
.empty() && aParentParts
[0] == "IF")
7998 uno::Reference
<text::XTextField
> xTextField
= pContext
->GetTextField();
8001 OSL_ENSURE( xTextField
.is()
8002 //||m_xTOC.is() ||m_xTC.is()
8003 //||m_sHyperlinkURL.getLength()
8004 , "DomainMapper_Impl::SetFieldResult: field not created" );
8009 if( m_bSetUserFieldContent
)
8011 // user field content has to be set at the field master
8012 uno::Reference
< text::XDependentTextField
> xDependentField( xTextField
, uno::UNO_QUERY_THROW
);
8013 xDependentField
->getTextFieldMaster()->setPropertyValue(
8014 getPropertyName(PROP_CONTENT
),
8015 uno::Any( rResult
));
8017 else if ( m_bSetCitation
)
8020 uno::Reference
< beans::XPropertySet
> xFieldProperties( xTextField
, uno::UNO_QUERY_THROW
);
8021 // In case of SetExpression, the field result contains the content of the variable.
8022 uno::Reference
<lang::XServiceInfo
> xServiceInfo(xTextField
, uno::UNO_QUERY
);
8024 bool bIsSetbiblio
= xServiceInfo
->supportsService("com.sun.star.text.TextField.Bibliography");
8027 uno::Any aProperty
= xFieldProperties
->getPropertyValue("Fields");
8028 uno::Sequence
<beans::PropertyValue
> aValues
;
8029 aProperty
>>= aValues
;
8030 beans::PropertyValue propertyVal
;
8031 sal_Int32 nTitleFoundIndex
= -1;
8032 for (sal_Int32 i
= 0; i
< aValues
.getLength(); ++i
)
8034 propertyVal
= aValues
[i
];
8035 if (propertyVal
.Name
== "Title")
8037 nTitleFoundIndex
= i
;
8041 if (nTitleFoundIndex
!= -1)
8044 uno::Any
aValue(propertyVal
.Value
);
8045 aValue
>>= titleStr
;
8046 titleStr
+= rResult
;
8047 propertyVal
.Value
<<= titleStr
;
8048 aValues
.getArray()[nTitleFoundIndex
] = propertyVal
;
8052 aValues
.realloc(aValues
.getLength() + 1);
8053 propertyVal
.Name
= "Title";
8054 propertyVal
.Value
<<= rResult
;
8055 aValues
.getArray()[aValues
.getLength() - 1] = propertyVal
;
8057 xFieldProperties
->setPropertyValue("Fields",
8061 else if ( m_bSetDateValue
)
8063 uno::Reference
< util::XNumberFormatsSupplier
> xNumberSupplier( m_xTextDocument
, uno::UNO_QUERY_THROW
);
8065 uno::Reference
<util::XNumberFormatter
> xFormatter(util::NumberFormatter::create(m_xComponentContext
), uno::UNO_QUERY_THROW
);
8066 xFormatter
->attachNumberFormatsSupplier( xNumberSupplier
);
8069 uno::Reference
< beans::XPropertySet
> xFieldProperties( xTextField
, uno::UNO_QUERY_THROW
);
8071 xFieldProperties
->getPropertyValue( "NumberFormat" ) >>= nKey
;
8072 xFieldProperties
->setPropertyValue(
8074 uno::Any( lcl_dateTimeFromSerial( xFormatter
->convertStringToNumber( nKey
, rResult
) ) ) );
8078 uno::Reference
< beans::XPropertySet
> xFieldProperties( xTextField
, uno::UNO_QUERY_THROW
);
8079 // In case of SetExpression, and Input fields the field result contains the content of the variable.
8080 uno::Reference
<lang::XServiceInfo
> xServiceInfo(xTextField
, uno::UNO_QUERY
);
8081 // there are fields with a content property, which aren't working correctly with
8082 // a generalized try catch of the content, property, so just restrict content
8083 // handling to these explicit services.
8084 const bool bHasContent
= xServiceInfo
->supportsService("com.sun.star.text.TextField.SetExpression") ||
8085 xServiceInfo
->supportsService("com.sun.star.text.TextField.Input");
8086 // If we already have content set, then use the current presentation
8090 // this will throw for field types without Content
8091 uno::Any
aValue(xFieldProperties
->getPropertyValue(
8092 getPropertyName(PROP_CONTENT
)));
8095 xFieldProperties
->setPropertyValue(
8096 getPropertyName(bHasContent
&& sValue
.isEmpty()? PROP_CONTENT
: PROP_CURRENT_PRESENTATION
),
8097 uno::Any( rResult
));
8099 // LO always automatically updates a DocInfo field from the File-Properties-Custom Prop
8100 // while MS Word requires the user to manually refresh the field (with F9).
8101 // In other words, Word lets the field to be out of sync with the controlling variable.
8102 // Marking as FIXEDFLD solves the automatic replacement problem, but of course prevents
8103 // Writer from making any changes, even on an F9 refresh.
8104 OUString sVariable
= pContext
->GetVariableValue();
8105 if (rResult
.getLength() != sVariable
.getLength())
8107 sal_Int32 nLen
= sVariable
.indexOf('\x0');
8109 sVariable
= sVariable
.copy(0, nLen
);
8111 bool bCustomFixedField
= rResult
!= sVariable
&&
8112 xServiceInfo
->supportsService("com.sun.star.text.TextField.DocInfo.Custom");
8114 if (bCustomFixedField
|| xServiceInfo
->supportsService(
8115 "com.sun.star.text.TextField.DocInfo.CreateDateTime"))
8117 // Creation time is const, don't try to update it.
8118 xFieldProperties
->setPropertyValue("IsFixed", uno::Any(true));
8122 catch( const beans::UnknownPropertyException
& )
8124 //some fields don't have a CurrentPresentation (DateTime)
8128 catch (const uno::Exception
&)
8130 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "DomainMapper_Impl::SetFieldResult");
8134 void DomainMapper_Impl::SetFieldFFData(const FFDataHandler::Pointer_t
& pFFDataHandler
)
8137 TagLogger::getInstance().startElement("setFieldFFData");
8140 if (!m_aFieldStack
.empty())
8142 FieldContextPtr pContext
= m_aFieldStack
.back();
8145 pContext
->setFFDataHandler(pFFDataHandler
);
8150 TagLogger::getInstance().endElement();
8154 void DomainMapper_Impl::PopFieldContext()
8156 if(m_bDiscardHeaderFooter
)
8159 TagLogger::getInstance().element("popFieldContext");
8162 if (m_aFieldStack
.empty())
8165 FieldContextPtr pContext
= m_aFieldStack
.back();
8166 OSL_ENSURE( pContext
, "no field context available");
8169 if( !pContext
->IsCommandCompleted() )
8170 CloseFieldCommand();
8172 if (!pContext
->GetResult().isEmpty())
8174 uno::Reference
< beans::XPropertySet
> xFieldProperties
= pContext
->GetCustomField();
8175 if(xFieldProperties
.is())
8176 SetNumberFormat( pContext
->GetResult(), xFieldProperties
, true );
8177 SetFieldResult( pContext
->GetResult() );
8180 //insert the field, TC or TOC
8181 uno::Reference
< text::XTextAppend
> xTextAppend
;
8182 if (!m_aTextAppendStack
.empty())
8183 xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
8184 if(xTextAppend
.is())
8188 uno::Reference
< text::XTextContent
> xToInsert( pContext
->GetTOC(), uno::UNO_QUERY
);
8189 if( xToInsert
.is() )
8191 if (m_bStartedTOC
|| m_bStartIndex
|| m_bStartBibliography
)
8193 // inside SDT, last empty paragraph is also part of index
8194 if (!m_bParaChanged
&& !m_xSdtEntryStart
)
8196 // End of index is the first item on a new paragraph - this paragraph
8197 // should not be part of index
8199 = xTextAppend
->createTextCursorByRange(
8200 m_aTextAppendStack
.top().xInsertPosition
.is()
8201 ? m_aTextAppendStack
.top().xInsertPosition
8202 : xTextAppend
->getEnd());
8203 xCursor
->goLeft(1, true);
8205 xCursor
->setString(OUString());
8206 // But a new paragraph should be started after the index instead
8207 if (m_bIsNewDoc
) // this check - see testTdf129402
8208 { // where finishParagraph inserts between 2 EndNode
8209 xTextAppend
->finishParagraph(css::beans::PropertyValues());
8213 xTextAppend
->finishParagraphInsert(css::beans::PropertyValues(),
8214 m_aTextAppendStack
.top().xInsertPosition
);
8217 m_bStartedTOC
= false;
8218 m_aTextAppendStack
.pop();
8219 m_bTextInserted
= false;
8220 m_bParaChanged
= true; // the paragraph must stay anyway
8222 m_bStartTOC
= false;
8223 m_bStartIndex
= false;
8224 m_bStartBibliography
= false;
8225 if (IsInHeaderFooter() && m_bStartTOCHeaderFooter
)
8226 m_bStartTOCHeaderFooter
= false;
8230 xToInsert
.set(pContext
->GetTC(), uno::UNO_QUERY
);
8231 if (!xToInsert
.is() && !IsInTOC() && !m_bStartIndex
&& !m_bStartBibliography
)
8232 xToInsert
= pContext
->GetTextField();
8233 if (xToInsert
.is() && !IsInTOC() && !m_bStartIndex
&& !m_bStartBibliography
)
8236 // Character properties of the field show up here the
8237 // last (always empty) run. Inherit character
8238 // properties from there.
8239 // Also merge in the properties from the field context,
8240 // e.g. SdtEndBefore.
8241 if (m_pLastCharacterContext
)
8242 aMap
.InsertProps(m_pLastCharacterContext
);
8243 aMap
.InsertProps(m_aFieldStack
.back()->getProperties());
8244 appendTextContent(xToInsert
, aMap
.GetPropertyValues());
8245 CheckRedline( xToInsert
->getAnchor( ) );
8249 uno::Reference
< text::XTextCursor
> xCrsr
= xTextAppend
->createTextCursorByRange(pContext
->GetStartRange());
8250 FormControlHelper::Pointer_t
pFormControlHelper(pContext
->getFormControlHelper());
8251 if (pFormControlHelper
)
8253 // xCrsr may be empty e.g. when pContext->GetStartRange() is outside of
8254 // xTextAppend, like when a field started in a parent paragraph is being
8255 // closed inside an anchored text box. It could be possible to throw an
8256 // exception here, and abort import, but Word tolerates such invalid
8257 // input, so it makes sense to do the same (tdf#152200)
8260 uno::Reference
< text::XFormField
> xFormField(pContext
->GetFormField());
8261 if (pFormControlHelper
->hasFFDataHandler())
8263 xToInsert
.set(xFormField
, uno::UNO_QUERY
);
8264 if (xFormField
.is() && xToInsert
.is())
8266 PopFieldmark(m_aTextAppendStack
, xCrsr
,
8267 pContext
->GetFieldId());
8268 pFormControlHelper
->processField(xFormField
);
8272 pFormControlHelper
->insertControl(xCrsr
);
8277 PopFieldmark(m_aTextAppendStack
, xCrsr
,
8278 pContext
->GetFieldId());
8279 uno::Reference
<lang::XComponent
>(xFormField
, uno::UNO_QUERY_THROW
)->dispose(); // presumably invalid?
8283 else if (!pContext
->GetHyperlinkURL().isEmpty() && xCrsr
.is())
8285 if (m_aTextAppendStack
.top().xInsertPosition
.is())
8287 xCrsr
->gotoRange(m_aTextAppendStack
.top().xInsertPosition
, true);
8291 xCrsr
->gotoEnd(true);
8294 // Draw components (like comments) need hyperlinks set differently
8295 SvxUnoTextRangeBase
* pDrawText
= dynamic_cast<SvxUnoTextRangeBase
*>(xCrsr
.get());
8297 pDrawText
->attachField( std::make_unique
<SvxURLField
>(pContext
->GetHyperlinkURL(), xCrsr
->getString(), SvxURLFormat::AppDefault
) );
8300 uno::Reference
< beans::XPropertySet
> xCrsrProperties( xCrsr
, uno::UNO_QUERY_THROW
);
8301 xCrsrProperties
->setPropertyValue(getPropertyName(PROP_HYPER_LINK_U_R_L
), uno::
8302 Any(pContext
->GetHyperlinkURL()));
8304 if (!pContext
->GetHyperlinkTarget().isEmpty())
8305 xCrsrProperties
->setPropertyValue("HyperLinkTarget", uno::Any(pContext
->GetHyperlinkTarget()));
8309 OUString
sDisplayName("Index Link");
8310 xCrsrProperties
->setPropertyValue("VisitedCharStyleName",uno::Any(sDisplayName
));
8311 xCrsrProperties
->setPropertyValue("UnvisitedCharStyleName",uno::Any(sDisplayName
));
8315 uno::Any aAny
= xCrsrProperties
->getPropertyValue("CharStyleName");
8317 if (css::uno::fromAny(aAny
, &charStyle
))
8319 if (charStyle
.isEmpty())
8321 xCrsrProperties
->setPropertyValue("VisitedCharStyleName", uno::Any(OUString("Default Style")));
8322 xCrsrProperties
->setPropertyValue("UnvisitedCharStyleName", uno::Any(OUString("Default Style")));
8324 else if (charStyle
.equalsIgnoreAsciiCase("Internet Link"))
8326 xCrsrProperties
->setPropertyValue("CharStyleName", uno::Any(OUString("Default Style")));
8330 xCrsrProperties
->setPropertyValue("VisitedCharStyleName", aAny
);
8331 xCrsrProperties
->setPropertyValue("UnvisitedCharStyleName", aAny
);
8337 else if (m_nStartGenericField
!= 0)
8339 --m_nStartGenericField
;
8340 PopFieldmark(m_aTextAppendStack
, xCrsr
, pContext
->GetFieldId());
8343 m_bTextInserted
= false;
8349 catch(const lang::IllegalArgumentException
&)
8351 TOOLS_WARN_EXCEPTION( "writerfilter", "PopFieldContext()" );
8353 catch(const uno::Exception
&)
8355 TOOLS_WARN_EXCEPTION( "writerfilter", "PopFieldContext()" );
8359 //TOCs have to include all the imported content
8362 std::vector
<FieldParagraph
> aParagraphsToFinish
;
8365 aParagraphsToFinish
= pContext
->GetParagraphsToFinish();
8368 //remove the field context
8369 m_aFieldStack
.pop_back();
8371 // Finish the paragraph(s) now that the field is closed.
8372 for (const auto& rFinish
: aParagraphsToFinish
)
8374 finishParagraph(rFinish
.m_pPropertyMap
, rFinish
.m_bRemove
);
8379 void DomainMapper_Impl::SetBookmarkName( const OUString
& rBookmarkName
)
8381 BookmarkMap_t::iterator aBookmarkIter
= m_aBookmarkMap
.find( m_sCurrentBkmkId
);
8382 if( aBookmarkIter
!= m_aBookmarkMap
.end() )
8384 // fields are internal bookmarks: consume redundant "normal" bookmark
8385 if ( IsOpenField() )
8387 FFDataHandler::Pointer_t
pFFDataHandler(GetTopFieldContext()->getFFDataHandler());
8388 if (pFFDataHandler
&& pFFDataHandler
->getName() == rBookmarkName
)
8390 // HACK: At the END marker, StartOrEndBookmark will START
8391 // a bookmark which will eventually be abandoned, not created.
8392 m_aBookmarkMap
.erase(aBookmarkIter
);
8397 aBookmarkIter
->second
.m_sBookmarkName
= m_sCurrentBkmkPrefix
+ rBookmarkName
;
8398 m_sCurrentBkmkPrefix
.clear();
8402 m_sCurrentBkmkName
= rBookmarkName
;
8403 m_sCurrentBkmkPrefix
.clear();
8407 // This method was used as-is for DomainMapper_Impl::startOrEndPermissionRange() implementation.
8408 void DomainMapper_Impl::StartOrEndBookmark( const OUString
& rId
)
8411 * Add the dummy paragraph to handle section properties
8412 * iff the first element in the section is a table. If the dummy para is not added yet, then add it;
8413 * So bookmark is not attached to the wrong paragraph.
8415 if(hasTableManager() && getTableManager().isInCell() && m_nTableDepth
== 0 && GetIsFirstParagraphInSection()
8416 && !GetIsDummyParaAddedForTableInSection() &&!GetIsTextFrameInserted())
8418 AddDummyParaForTableInSection();
8421 bool bIsAfterDummyPara
= GetIsDummyParaAddedForTableInSection() && GetIsFirstParagraphInSection();
8422 if (m_aTextAppendStack
.empty())
8424 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
8425 BookmarkMap_t::iterator aBookmarkIter
= m_aBookmarkMap
.find( rId
);
8426 //is the bookmark name already registered?
8429 if( aBookmarkIter
!= m_aBookmarkMap
.end() )
8431 if (m_xTextFactory
.is())
8433 uno::Reference
< text::XTextContent
> xBookmark( m_xTextFactory
->createInstance( "com.sun.star.text.Bookmark" ), uno::UNO_QUERY_THROW
);
8434 uno::Reference
< text::XTextCursor
> xCursor
;
8435 uno::Reference
< text::XText
> xText
= aBookmarkIter
->second
.m_xTextRange
->getText();
8436 if( aBookmarkIter
->second
.m_bIsStartOfText
&& !bIsAfterDummyPara
)
8438 xCursor
= xText
->createTextCursorByRange( xText
->getStart() );
8442 xCursor
= xText
->createTextCursorByRange( aBookmarkIter
->second
.m_xTextRange
);
8444 if (!aBookmarkIter
->second
.m_bIsStartOfText
)
8446 xCursor
->goRight( 1, false );
8449 xCursor
->gotoRange( xTextAppend
->getEnd(), true );
8450 // A Paragraph was recently finished, and a new Paragraph has not been started as yet
8451 // then move the bookmark-End to the earlier paragraph
8452 if (IsOutsideAParagraph())
8454 // keep bookmark range, if it doesn't exceed cell boundary
8455 uno::Reference
< text::XTextRange
> xStart
= xCursor
->getStart();
8456 xCursor
->goLeft( 1, false );
8457 if (m_nTableDepth
== 0 || !m_bFirstParagraphInCell
)
8458 xCursor
->gotoRange(xStart
, true );
8460 uno::Reference
< container::XNamed
> xBkmNamed( xBookmark
, uno::UNO_QUERY_THROW
);
8461 SAL_WARN_IF(aBookmarkIter
->second
.m_sBookmarkName
.isEmpty(), "writerfilter.dmapper", "anonymous bookmark");
8462 //todo: make sure the name is not used already!
8463 xBkmNamed
->setName( aBookmarkIter
->second
.m_sBookmarkName
);
8464 xTextAppend
->insertTextContent( uno::Reference
< text::XTextRange
>( xCursor
, uno::UNO_QUERY_THROW
), xBookmark
, !xCursor
->isCollapsed() );
8466 m_aBookmarkMap
.erase( aBookmarkIter
);
8467 m_sCurrentBkmkId
.clear();
8471 //otherwise insert a text range as marker
8472 bool bIsStart
= true;
8473 uno::Reference
< text::XTextRange
> xCurrent
;
8474 if (xTextAppend
.is())
8476 uno::Reference
<text::XTextCursor
> const xCursor
=
8477 xTextAppend
->createTextCursorByRange(
8478 m_aTextAppendStack
.top().xInsertPosition
.is()
8479 ? m_aTextAppendStack
.top().xInsertPosition
8480 : xTextAppend
->getEnd() );
8485 if (!bIsAfterDummyPara
)
8486 bIsStart
= !xCursor
->goLeft(1, false);
8487 xCurrent
= xCursor
->getStart();
8489 m_sCurrentBkmkId
= rId
;
8490 m_aBookmarkMap
.emplace( rId
, BookmarkInsertPosition( bIsStart
, m_sCurrentBkmkName
, xCurrent
) );
8491 m_sCurrentBkmkName
.clear();
8494 catch( const uno::Exception
& )
8496 //TODO: What happens to bookmarks where start and end are at different XText objects?
8500 void DomainMapper_Impl::SetMoveBookmark( bool bIsFrom
)
8502 static constexpr OUStringLiteral MoveFrom_Bookmark_NamePrefix
= u
"__RefMoveFrom__";
8503 static constexpr OUStringLiteral MoveTo_Bookmark_NamePrefix
= u
"__RefMoveTo__";
8505 m_sCurrentBkmkPrefix
= MoveFrom_Bookmark_NamePrefix
;
8507 m_sCurrentBkmkPrefix
= MoveTo_Bookmark_NamePrefix
;
8510 void DomainMapper_Impl::setPermissionRangeEd(const OUString
& user
)
8512 PermMap_t::iterator aPremIter
= m_aPermMap
.find(m_sCurrentPermId
);
8513 if (aPremIter
!= m_aPermMap
.end())
8514 aPremIter
->second
.m_Ed
= user
;
8516 m_sCurrentPermEd
= user
;
8519 void DomainMapper_Impl::setPermissionRangeEdGrp(const OUString
& group
)
8521 PermMap_t::iterator aPremIter
= m_aPermMap
.find(m_sCurrentPermId
);
8522 if (aPremIter
!= m_aPermMap
.end())
8523 aPremIter
->second
.m_EdGrp
= group
;
8525 m_sCurrentPermEdGrp
= group
;
8528 // This method is based on implementation from DomainMapper_Impl::StartOrEndBookmark()
8529 void DomainMapper_Impl::startOrEndPermissionRange(sal_Int32 permissinId
)
8532 * Add the dummy paragraph to handle section properties
8533 * if the first element in the section is a table. If the dummy para is not added yet, then add it;
8534 * So permission is not attached to the wrong paragraph.
8536 if (getTableManager().isInCell() && m_nTableDepth
== 0 && GetIsFirstParagraphInSection()
8537 && !GetIsDummyParaAddedForTableInSection() && !GetIsTextFrameInserted())
8539 AddDummyParaForTableInSection();
8542 if (m_aTextAppendStack
.empty())
8545 const bool bIsAfterDummyPara
= GetIsDummyParaAddedForTableInSection() && GetIsFirstParagraphInSection();
8547 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
8548 PermMap_t::iterator aPermIter
= m_aPermMap
.find(permissinId
);
8550 //is the bookmark name already registered?
8553 if (aPermIter
== m_aPermMap
.end())
8555 //otherwise insert a text range as marker
8556 bool bIsStart
= true;
8557 uno::Reference
< text::XTextRange
> xCurrent
;
8558 if (xTextAppend
.is())
8560 uno::Reference
< text::XTextCursor
> xCursor
= xTextAppend
->createTextCursorByRange(xTextAppend
->getEnd());
8562 if (!bIsAfterDummyPara
)
8563 bIsStart
= !xCursor
->goLeft(1, false);
8564 xCurrent
= xCursor
->getStart();
8567 // register the start of the new permission
8568 m_sCurrentPermId
= permissinId
;
8569 m_aPermMap
.emplace(permissinId
, PermInsertPosition(bIsStart
, permissinId
, m_sCurrentPermEd
, m_sCurrentPermEdGrp
, xCurrent
));
8572 m_sCurrentPermEd
.clear();
8573 m_sCurrentPermEdGrp
.clear();
8577 if (m_xTextFactory
.is())
8579 uno::Reference
< text::XTextCursor
> xCursor
;
8580 uno::Reference
< text::XText
> xText
= aPermIter
->second
.m_xTextRange
->getText();
8581 if (aPermIter
->second
.m_bIsStartOfText
&& !bIsAfterDummyPara
)
8583 xCursor
= xText
->createTextCursorByRange(xText
->getStart());
8587 xCursor
= xText
->createTextCursorByRange(aPermIter
->second
.m_xTextRange
);
8589 if (!aPermIter
->second
.m_bIsStartOfText
)
8591 xCursor
->goRight(1, false);
8594 xCursor
->gotoRange(xTextAppend
->getEnd(), true);
8595 // A Paragraph was recently finished, and a new Paragraph has not been started as yet
8596 // then move the bookmark-End to the earlier paragraph
8597 if (IsOutsideAParagraph())
8599 xCursor
->goLeft(1, false);
8602 // create a new bookmark using specific bookmark name pattern for permissions
8603 uno::Reference
< text::XTextContent
> xPerm(m_xTextFactory
->createInstance("com.sun.star.text.Bookmark"), uno::UNO_QUERY_THROW
);
8604 uno::Reference
< container::XNamed
> xPermNamed(xPerm
, uno::UNO_QUERY_THROW
);
8605 xPermNamed
->setName(aPermIter
->second
.createBookmarkName());
8608 const bool bAbsorb
= !xCursor
->isCollapsed();
8609 uno::Reference
< text::XTextRange
> xCurrent(xCursor
, uno::UNO_QUERY_THROW
);
8610 xTextAppend
->insertTextContent(xCurrent
, xPerm
, bAbsorb
);
8613 // remove processed permission
8614 m_aPermMap
.erase(aPermIter
);
8617 m_sCurrentPermId
= 0;
8618 m_sCurrentPermEd
.clear();
8619 m_sCurrentPermEdGrp
.clear();
8622 catch (const uno::Exception
&)
8624 //TODO: What happens to bookmarks where start and end are at different XText objects?
8628 void DomainMapper_Impl::AddAnnotationPosition(
8630 const sal_Int32 nAnnotationId
)
8632 if (m_aTextAppendStack
.empty())
8635 // Create a cursor, pointing to the current position.
8636 uno::Reference
<text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
8637 uno::Reference
<text::XTextRange
> xCurrent
;
8638 if (xTextAppend
.is())
8640 uno::Reference
<text::XTextCursor
> xCursor
;
8642 xCursor
= xTextAppend
->createTextCursorByRange(xTextAppend
->getEnd());
8644 xCursor
= m_aTextAppendStack
.top().xCursor
;
8646 xCurrent
= xCursor
->getStart();
8649 // And save it, to be used by PopAnnotation() later.
8650 AnnotationPosition
& aAnnotationPosition
= m_aAnnotationPositions
[ nAnnotationId
];
8653 aAnnotationPosition
.m_xStart
= xCurrent
;
8657 aAnnotationPosition
.m_xEnd
= xCurrent
;
8659 m_aAnnotationPositions
[ nAnnotationId
] = aAnnotationPosition
;
8662 GraphicImportPtr
const & DomainMapper_Impl::GetGraphicImport()
8664 if(!m_pGraphicImport
)
8666 m_pGraphicImport
= new GraphicImport(m_xComponentContext
, m_xTextFactory
, m_rDMapper
, m_eGraphicImportType
, m_aPositionOffsets
, m_aAligns
, m_aPositivePercentages
);
8668 return m_pGraphicImport
;
8670 /*-------------------------------------------------------------------------
8671 reset graphic import if the last import resulted in a shape, not a graphic
8672 -----------------------------------------------------------------------*/
8673 void DomainMapper_Impl::ResetGraphicImport()
8675 m_pGraphicImport
.clear();
8679 void DomainMapper_Impl::ImportGraphic(const writerfilter::Reference
<Properties
>::Pointer_t
& ref
)
8682 if (m_eGraphicImportType
!= IMPORT_AS_DETECTED_INLINE
&& m_eGraphicImportType
!= IMPORT_AS_DETECTED_ANCHOR
)
8683 { // this appears impossible?
8684 //create the graphic
8685 ref
->resolve( *m_pGraphicImport
);
8688 //insert it into the document at the current cursor position
8690 uno::Reference
<text::XTextContent
> xTextContent
8691 (m_pGraphicImport
->GetGraphicObject());
8693 // In case the SDT starts with the text portion of the graphic, then set the SDT properties here.
8694 bool bHasGrabBag
= false;
8695 uno::Reference
<beans::XPropertySet
> xPropertySet(xTextContent
, uno::UNO_QUERY
);
8696 if (xPropertySet
.is())
8698 uno::Reference
<beans::XPropertySetInfo
> xPropertySetInfo
= xPropertySet
->getPropertySetInfo();
8699 bHasGrabBag
= xPropertySetInfo
->hasPropertyByName("FrameInteropGrabBag");
8700 // In case we're outside a paragraph, then the SDT properties are stored in the paragraph grab-bag, not the frame one.
8701 if (!m_pSdtHelper
->isInteropGrabBagEmpty() && bHasGrabBag
&& !m_pSdtHelper
->isOutsideAParagraph())
8703 comphelper::SequenceAsHashMap
aFrameGrabBag(xPropertySet
->getPropertyValue("FrameInteropGrabBag"));
8704 aFrameGrabBag
["SdtPr"] <<= m_pSdtHelper
->getInteropGrabBagAndClear();
8705 xPropertySet
->setPropertyValue("FrameInteropGrabBag", uno::Any(aFrameGrabBag
.getAsConstPropertyValueList()));
8709 /* Set "SdtEndBefore" property on Drawing.
8710 * It is required in a case when Drawing appears immediately after first run i.e.
8711 * there is no text/space/tab in between two runs.
8712 * In this case "SdtEndBefore" property needs to be set on Drawing.
8714 if(IsSdtEndBefore())
8716 if(xPropertySet
.is() && bHasGrabBag
)
8718 uno::Sequence
<beans::PropertyValue
> aFrameGrabBag( comphelper::InitPropertySequence({
8719 { "SdtEndBefore", uno::Any(true) }
8721 xPropertySet
->setPropertyValue("FrameInteropGrabBag",uno::Any(aFrameGrabBag
));
8726 // Update the shape properties if it is embedded object.
8727 if(m_xEmbedded
.is()){
8728 if (m_pGraphicImport
->GetXShapeObject())
8729 m_pGraphicImport
->GetXShapeObject()->setPosition(
8730 m_pGraphicImport
->GetGraphicObjectPosition());
8732 uno::Reference
<drawing::XShape
> xShape
= m_pGraphicImport
->GetXShapeObject();
8733 UpdateEmbeddedShapeProps(xShape
);
8734 if (m_eGraphicImportType
== IMPORT_AS_DETECTED_ANCHOR
)
8736 uno::Reference
<beans::XPropertySet
> xEmbeddedProps(m_xEmbedded
, uno::UNO_QUERY
);
8737 xEmbeddedProps
->setPropertyValue("AnchorType", uno::Any(text::TextContentAnchorType_AT_CHARACTER
));
8738 xEmbeddedProps
->setPropertyValue("IsFollowingTextFlow", uno::Any(m_pGraphicImport
->GetLayoutInCell()));
8739 uno::Reference
<beans::XPropertySet
> xShapeProps(xShape
, uno::UNO_QUERY
);
8740 xEmbeddedProps
->setPropertyValue("HoriOrient", xShapeProps
->getPropertyValue("HoriOrient"));
8741 xEmbeddedProps
->setPropertyValue("HoriOrientPosition", xShapeProps
->getPropertyValue("HoriOrientPosition"));
8742 xEmbeddedProps
->setPropertyValue("HoriOrientRelation", xShapeProps
->getPropertyValue("HoriOrientRelation"));
8743 xEmbeddedProps
->setPropertyValue("VertOrient", xShapeProps
->getPropertyValue("VertOrient"));
8744 xEmbeddedProps
->setPropertyValue("VertOrientPosition", xShapeProps
->getPropertyValue("VertOrientPosition"));
8745 xEmbeddedProps
->setPropertyValue("VertOrientRelation", xShapeProps
->getPropertyValue("VertOrientRelation"));
8746 //tdf123873 fix missing textwrap import
8747 xEmbeddedProps
->setPropertyValue("TextWrap", xShapeProps
->getPropertyValue("TextWrap"));
8749 // GraphicZOrderHelper::findZOrder() was called already, so can just copy it over.
8750 xEmbeddedProps
->setPropertyValue("ZOrder", xShapeProps
->getPropertyValue("ZOrder"));
8753 //insert it into the document at the current cursor position
8754 OSL_ENSURE( xTextContent
.is(), "DomainMapper_Impl::ImportGraphic");
8755 if( xTextContent
.is())
8757 bool bAppend
= true;
8758 // workaround for images anchored to characters: add ZWSPs around the anchoring point
8759 if (m_eGraphicImportType
!= IMPORT_AS_DETECTED_INLINE
&& !m_aRedlines
.top().empty())
8761 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
8762 if(xTextAppend
.is())
8766 uno::Reference
< text::XText
> xText
= xTextAppend
->getText();
8767 uno::Reference
< text::XTextCursor
> xCrsr
= xText
->createTextCursor();
8768 xCrsr
->gotoEnd(false);
8769 PropertyMapPtr
pEmpty(new PropertyMap());
8770 appendTextPortion(u
"", pEmpty
);
8771 appendTextContent( xTextContent
, uno::Sequence
< beans::PropertyValue
>() );
8773 xCrsr
->gotoEnd(false);
8774 appendTextPortion(u
"", pEmpty
);
8776 m_bRedlineImageInPreviousRun
= true;
8777 m_previousRedline
= m_currentRedline
;
8779 catch( const uno::Exception
& )
8786 appendTextContent( xTextContent
, uno::Sequence
< beans::PropertyValue
>() );
8788 if (m_eGraphicImportType
== IMPORT_AS_DETECTED_ANCHOR
&& !m_aTextAppendStack
.empty())
8790 // Remember this object is anchored to the current paragraph.
8791 AnchoredObjectInfo aInfo
;
8792 aInfo
.m_xAnchoredObject
= xTextContent
;
8793 if (m_pGraphicImport
)
8795 // We still have the graphic import around, remember the original margin, so later
8796 // SectionPropertyMap::HandleIncreasedAnchoredObjectSpacing() can use it.
8797 aInfo
.m_nLeftMargin
= m_pGraphicImport
->GetLeftMarginOrig();
8799 m_aTextAppendStack
.top().m_aAnchoredObjects
.push_back(aInfo
);
8801 else if (m_eGraphicImportType
== IMPORT_AS_DETECTED_INLINE
)
8803 m_bParaWithInlineObject
= true;
8805 // store inline images with track changes, because the anchor point
8806 // to set redlining is not available yet
8807 if (!m_aTextAppendStack
.empty() && !m_aRedlines
.top().empty() )
8809 // Remember this object is anchored to the current paragraph.
8810 AnchoredObjectInfo aInfo
;
8811 aInfo
.m_xAnchoredObject
= xTextContent
;
8812 aInfo
.m_xRedlineForInline
= m_aRedlines
.top().back();
8813 m_aTextAppendStack
.top().m_aAnchoredObjects
.push_back(aInfo
);
8819 // Clear the reference, so in case the embedded object is inside a
8820 // TextFrame, we won't try to resize it (to match the size of the
8822 m_xEmbedded
.clear();
8823 m_pGraphicImport
.clear();
8827 void DomainMapper_Impl::SetLineNumbering( sal_Int32 nLnnMod
, sal_uInt32 nLnc
, sal_Int32 ndxaLnn
)
8829 if( !m_bLineNumberingSet
)
8833 uno::Reference
< text::XLineNumberingProperties
> xLineProperties( m_xTextDocument
, uno::UNO_QUERY_THROW
);
8834 uno::Reference
< beans::XPropertySet
> xProperties
= xLineProperties
->getLineNumberingProperties();
8835 uno::Any
aTrue( uno::Any( true ));
8836 xProperties
->setPropertyValue( getPropertyName( PROP_IS_ON
), aTrue
);
8837 xProperties
->setPropertyValue( getPropertyName( PROP_COUNT_EMPTY_LINES
), aTrue
);
8838 xProperties
->setPropertyValue( getPropertyName( PROP_COUNT_LINES_IN_FRAMES
), uno::Any( false ) );
8839 xProperties
->setPropertyValue( getPropertyName( PROP_INTERVAL
), uno::Any( static_cast< sal_Int16
>( nLnnMod
)));
8840 xProperties
->setPropertyValue( getPropertyName( PROP_DISTANCE
), uno::Any( ConversionHelper::convertTwipToMM100(ndxaLnn
) ));
8841 xProperties
->setPropertyValue( getPropertyName( PROP_NUMBER_POSITION
), uno::Any( style::LineNumberPosition::LEFT
));
8842 xProperties
->setPropertyValue( getPropertyName( PROP_NUMBERING_TYPE
), uno::Any( style::NumberingType::ARABIC
));
8843 xProperties
->setPropertyValue( getPropertyName( PROP_RESTART_AT_EACH_PAGE
), uno::Any( nLnc
== NS_ooxml::LN_Value_ST_LineNumberRestart_newPage
));
8845 catch( const uno::Exception
& )
8848 m_bLineNumberingSet
= true;
8849 uno::Reference
< style::XStyleFamiliesSupplier
> xStylesSupplier( GetTextDocument(), uno::UNO_QUERY_THROW
);
8850 uno::Reference
< container::XNameAccess
> xStyleFamilies
= xStylesSupplier
->getStyleFamilies();
8851 uno::Reference
<container::XNameContainer
> xStyles
;
8852 xStyleFamilies
->getByName(getPropertyName( PROP_PARAGRAPH_STYLES
)) >>= xStyles
;
8853 lcl_linenumberingHeaderFooter( xStyles
, "Header", this );
8854 lcl_linenumberingHeaderFooter( xStyles
, "Footer", this );
8858 void DomainMapper_Impl::SetPageMarginTwip( PageMarElement eElement
, sal_Int32 nValue
)
8860 nValue
= ConversionHelper::convertTwipToMM100(nValue
);
8863 case PAGE_MAR_TOP
: m_aPageMargins
.top
= nValue
; break;
8864 case PAGE_MAR_RIGHT
: m_aPageMargins
.right
= nValue
; break;
8865 case PAGE_MAR_BOTTOM
: m_aPageMargins
.bottom
= nValue
; break;
8866 case PAGE_MAR_LEFT
: m_aPageMargins
.left
= nValue
; break;
8867 case PAGE_MAR_HEADER
: m_aPageMargins
.header
= nValue
; break;
8868 case PAGE_MAR_FOOTER
: m_aPageMargins
.footer
= nValue
; break;
8869 case PAGE_MAR_GUTTER
:
8870 m_aPageMargins
.gutter
= nValue
;
8877 : top(ConversionHelper::convertTwipToMM100( sal_Int32(1440)))
8878 // This is strange, the RTF spec says it's 1800, but it's clearly 1440 in Word
8879 // OOXML seems not to specify a default value
8880 , right(ConversionHelper::convertTwipToMM100( sal_Int32(1440)))
8883 , header(ConversionHelper::convertTwipToMM100(sal_Int32(720)))
8890 void DomainMapper_Impl::RegisterFrameConversion(
8891 uno::Reference
< text::XTextRange
> const& xFrameStartRange
,
8892 uno::Reference
< text::XTextRange
> const& xFrameEndRange
,
8893 std::vector
<beans::PropertyValue
>&& rFrameProperties
8897 m_aFrameProperties
.empty() && !m_xFrameStartRange
.is() && !m_xFrameEndRange
.is(),
8898 "frame properties not removed");
8899 m_aFrameProperties
= std::move(rFrameProperties
);
8900 m_xFrameStartRange
= xFrameStartRange
;
8901 m_xFrameEndRange
= xFrameEndRange
;
8905 void DomainMapper_Impl::ExecuteFrameConversion()
8907 if( m_xFrameStartRange
.is() && m_xFrameEndRange
.is() && !m_bDiscardHeaderFooter
)
8909 std::vector
<sal_Int32
> redPos
, redLen
;
8912 uno::Reference
< text::XTextAppendAndConvert
> xTextAppendAndConvert( GetTopTextAppend(), uno::UNO_QUERY_THROW
);
8913 // convert redline ranges to cursor movement and character length
8915 lcl_CopyRedlines(GetTopTextAppend(), m_aStoredRedlines
[StoredRedlines::FRAME
], redPos
, redLen
, redIdx
);
8917 const uno::Reference
< text::XTextContent
>& xTextContent
= xTextAppendAndConvert
->convertToTextFrame(
8920 comphelper::containerToSequence(m_aFrameProperties
) );
8922 uno::Reference
< text::XText
> xDest( xTextContent
, uno::UNO_QUERY_THROW
);
8923 lcl_PasteRedlines(xDest
, m_aStoredRedlines
[StoredRedlines::FRAME
], redPos
, redLen
, redIdx
);
8925 catch( const uno::Exception
&)
8927 DBG_UNHANDLED_EXCEPTION( "writerfilter.dmapper", "Exception caught when converting to frame");
8930 m_bIsActualParagraphFramed
= false;
8932 if (redPos
.size() == m_aStoredRedlines
[StoredRedlines::FRAME
].size()/3)
8934 for( sal_Int32 i
= m_aStoredRedlines
[StoredRedlines::FRAME
].size() - 1; i
>= 0; --i
)
8936 // keep redlines of floating tables to process them in CloseSectionGroup()
8937 if ( redPos
[i
/3] != -1 )
8939 m_aStoredRedlines
[StoredRedlines::FRAME
].erase(m_aStoredRedlines
[StoredRedlines::FRAME
].begin() + i
);
8944 m_aStoredRedlines
[StoredRedlines::FRAME
].clear();
8946 m_xFrameStartRange
= nullptr;
8947 m_xFrameEndRange
= nullptr;
8948 m_aFrameProperties
.clear();
8951 void DomainMapper_Impl::AddNewRedline( sal_uInt32 sprmId
)
8953 RedlineParamsPtr
pNew( new RedlineParams
);
8954 pNew
->m_nToken
= XML_mod
;
8955 if ( !m_bIsParaMarkerChange
)
8957 // <w:rPrChange> applies to the whole <w:r>, <w:pPrChange> applies to the whole <w:p>,
8958 // so keep those two in CONTEXT_CHARACTERS and CONTEXT_PARAGRAPH, which will take
8959 // care of their scope (i.e. when they should be used and discarded).
8960 // Let's keep the rest the same way they used to be handled (explicitly dropped
8961 // from a global stack by endtrackchange), but quite possibly they should not be handled
8962 // that way either (I don't know).
8963 if( sprmId
== NS_ooxml::LN_EG_RPrContent_rPrChange
)
8964 GetTopContextOfType( CONTEXT_CHARACTER
)->Redlines().push_back( pNew
);
8965 else if( sprmId
== NS_ooxml::LN_CT_PPr_pPrChange
)
8966 GetTopContextOfType( CONTEXT_PARAGRAPH
)->Redlines().push_back( pNew
);
8967 else if( sprmId
!= NS_ooxml::LN_CT_ParaRPr_rPrChange
)
8968 m_aRedlines
.top().push_back( pNew
);
8972 m_pParaMarkerRedline
= pNew
;
8974 // Newly read data will go into this redline.
8975 m_currentRedline
= pNew
;
8978 void DomainMapper_Impl::SetCurrentRedlineIsRead()
8980 m_currentRedline
.clear();
8983 sal_Int32
DomainMapper_Impl::GetCurrentRedlineToken( ) const
8985 assert(m_currentRedline
);
8986 return m_currentRedline
->m_nToken
;
8989 void DomainMapper_Impl::SetCurrentRedlineAuthor( const OUString
& sAuthor
)
8991 if (!m_xAnnotationField
.is())
8993 if (m_currentRedline
)
8994 m_currentRedline
->m_sAuthor
= sAuthor
;
8996 SAL_INFO("writerfilter.dmapper", "numberingChange not implemented");
8999 m_xAnnotationField
->setPropertyValue("Author", uno::Any(sAuthor
));
9002 void DomainMapper_Impl::SetCurrentRedlineInitials( const OUString
& sInitials
)
9004 if (m_xAnnotationField
.is())
9005 m_xAnnotationField
->setPropertyValue("Initials", uno::Any(sInitials
));
9008 void DomainMapper_Impl::SetCurrentRedlineDate( const OUString
& sDate
)
9010 if (!m_xAnnotationField
.is())
9012 if (m_currentRedline
)
9013 m_currentRedline
->m_sDate
= sDate
;
9015 SAL_INFO("writerfilter.dmapper", "numberingChange not implemented");
9018 m_xAnnotationField
->setPropertyValue("DateTimeValue", uno::Any(ConversionHelper::ConvertDateStringToDateTime(sDate
)));
9021 void DomainMapper_Impl::SetCurrentRedlineId( sal_Int32 sId
)
9023 if (m_xAnnotationField
.is())
9025 m_nAnnotationId
= sId
;
9029 // This should be an assert, but somebody had the smart idea to reuse this function also for comments and whatnot,
9030 // and in some cases the id is actually not handled, which may be in fact a bug.
9031 if( !m_currentRedline
)
9032 SAL_INFO("writerfilter.dmapper", "no current redline");
9036 void DomainMapper_Impl::SetCurrentRedlineToken( sal_Int32 nToken
)
9038 assert(m_currentRedline
);
9039 m_currentRedline
->m_nToken
= nToken
;
9042 void DomainMapper_Impl::SetCurrentRedlineRevertProperties( const uno::Sequence
<beans::PropertyValue
>& aProperties
)
9044 assert(m_currentRedline
);
9045 m_currentRedline
->m_aRevertProperties
= aProperties
;
9049 // This removes only the last redline stored here, those stored in contexts are automatically removed when
9050 // the context is destroyed.
9051 void DomainMapper_Impl::RemoveTopRedline( )
9053 if (m_aRedlines
.top().empty())
9055 if (GetFootnoteCount() > -1 || GetEndnoteCount() > -1)
9057 SAL_WARN("writerfilter.dmapper", "RemoveTopRedline called with empty stack");
9058 throw uno::Exception("RemoveTopRedline failed", nullptr);
9060 m_aRedlines
.top().pop_back( );
9061 m_currentRedline
.clear();
9064 void DomainMapper_Impl::ApplySettingsTable()
9066 if (!(m_pSettingsTable
&& m_xTextFactory
.is()))
9071 uno::Reference
< beans::XPropertySet
> xTextDefaults(m_xTextFactory
->createInstance("com.sun.star.text.Defaults"), uno::UNO_QUERY_THROW
);
9072 sal_Int32 nDefTab
= m_pSettingsTable
->GetDefaultTabStop();
9073 xTextDefaults
->setPropertyValue( getPropertyName( PROP_TAB_STOP_DISTANCE
), uno::Any(nDefTab
) );
9074 if (m_pSettingsTable
->GetLinkStyles())
9076 // If linked styles are enabled, set paragraph defaults from Word's default template
9077 xTextDefaults
->setPropertyValue(getPropertyName(PROP_PARA_BOTTOM_MARGIN
), uno::Any(ConversionHelper::convertTwipToMM100(200)));
9078 style::LineSpacing aSpacing
;
9079 aSpacing
.Mode
= style::LineSpacingMode::PROP
;
9080 aSpacing
.Height
= sal_Int16(115);
9081 xTextDefaults
->setPropertyValue(getPropertyName(PROP_PARA_LINE_SPACING
), uno::Any(aSpacing
));
9084 if (m_pSettingsTable
->GetZoomFactor() || m_pSettingsTable
->GetView())
9086 std::vector
<beans::PropertyValue
> aViewProps
;
9087 if (m_pSettingsTable
->GetZoomFactor())
9089 aViewProps
.emplace_back("ZoomFactor", -1, uno::Any(m_pSettingsTable
->GetZoomFactor()), beans::PropertyState_DIRECT_VALUE
);
9090 aViewProps
.emplace_back("VisibleBottom", -1, uno::Any(sal_Int32(0)), beans::PropertyState_DIRECT_VALUE
);
9091 aViewProps
.emplace_back("ZoomType", -1,
9092 uno::Any(m_pSettingsTable
->GetZoomType()),
9093 beans::PropertyState_DIRECT_VALUE
);
9095 rtl::Reference
< comphelper::IndexedPropertyValuesContainer
> xBox
= new comphelper::IndexedPropertyValuesContainer();
9096 xBox
->insertByIndex(sal_Int32(0), uno::Any(comphelper::containerToSequence(aViewProps
)));
9097 uno::Reference
<document::XViewDataSupplier
> xViewDataSupplier(m_xTextDocument
, uno::UNO_QUERY
);
9098 xViewDataSupplier
->setViewData(xBox
);
9101 uno::Reference
< beans::XPropertySet
> xSettings(m_xTextFactory
->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY
);
9103 if (m_pSettingsTable
->GetDoNotExpandShiftReturn())
9104 xSettings
->setPropertyValue( "DoNotJustifyLinesWithManualBreak", uno::Any(true) );
9105 if (m_pSettingsTable
->GetUsePrinterMetrics())
9106 xSettings
->setPropertyValue("PrinterIndependentLayout", uno::Any(document::PrinterIndependentLayout::DISABLED
));
9107 if( m_pSettingsTable
->GetEmbedTrueTypeFonts())
9108 xSettings
->setPropertyValue( getPropertyName( PROP_EMBED_FONTS
), uno::Any(true) );
9109 if( m_pSettingsTable
->GetEmbedSystemFonts())
9110 xSettings
->setPropertyValue( getPropertyName( PROP_EMBED_SYSTEM_FONTS
), uno::Any(true) );
9111 xSettings
->setPropertyValue("AddParaTableSpacing", uno::Any(m_pSettingsTable
->GetDoNotUseHTMLParagraphAutoSpacing()));
9112 if (m_pSettingsTable
->GetNoLeading())
9114 xSettings
->setPropertyValue("AddExternalLeading", uno::Any(!m_pSettingsTable
->GetNoLeading()));
9116 if( m_pSettingsTable
->GetProtectForm() )
9117 xSettings
->setPropertyValue("ProtectForm", uno::Any( true ));
9118 if( m_pSettingsTable
->GetReadOnly() )
9119 xSettings
->setPropertyValue("LoadReadonly", uno::Any( true ));
9120 if (m_pSettingsTable
->GetGutterAtTop())
9122 xSettings
->setPropertyValue("GutterAtTop", uno::Any(true));
9124 uno::Sequence
<beans::PropertyValue
> aWriteProtection
9125 = m_pSettingsTable
->GetWriteProtectionSettings();
9126 if (aWriteProtection
.hasElements())
9127 xSettings
->setPropertyValue("ModifyPasswordInfo", uno::Any(aWriteProtection
));
9129 catch(const uno::Exception
&)
9134 SectionPropertyMap
* DomainMapper_Impl::GetSectionContext()
9136 SectionPropertyMap
* pSectionContext
= nullptr;
9137 //the section context is not available before the first call of startSectionGroup()
9138 if( !IsAnyTableImport() )
9140 PropertyMapPtr pContext
= GetTopContextOfType(CONTEXT_SECTION
);
9141 pSectionContext
= dynamic_cast< SectionPropertyMap
* >( pContext
.get() );
9144 return pSectionContext
;
9147 void DomainMapper_Impl::deferCharacterProperty(sal_Int32 id
, const css::uno::Any
& value
)
9149 m_deferredCharacterProperties
[ id
] = value
;
9152 void DomainMapper_Impl::processDeferredCharacterProperties(bool bCharContext
)
9154 // Actually process in DomainMapper, so that it's the same source file like normal processing.
9155 if( !m_deferredCharacterProperties
.empty())
9157 m_rDMapper
.processDeferredCharacterProperties(m_deferredCharacterProperties
, bCharContext
);
9158 m_deferredCharacterProperties
.clear();
9162 sal_Int32
DomainMapper_Impl::getNumberingProperty(const sal_Int32 nListId
, sal_Int32 nNumberingLevel
, const OUString
& aProp
)
9170 if (nNumberingLevel
< 0) // It seems it's valid to omit numbering level, and in that case it means zero.
9171 nNumberingLevel
= 0;
9173 auto const pList(GetListTable()->GetList(nListId
));
9175 const OUString aListName
= pList
->GetStyleName();
9176 const uno::Reference
< style::XStyleFamiliesSupplier
> xStylesSupplier(GetTextDocument(), uno::UNO_QUERY_THROW
);
9177 const uno::Reference
< container::XNameAccess
> xStyleFamilies
= xStylesSupplier
->getStyleFamilies();
9178 uno::Reference
<container::XNameAccess
> xNumberingStyles
;
9179 xStyleFamilies
->getByName("NumberingStyles") >>= xNumberingStyles
;
9180 const uno::Reference
<beans::XPropertySet
> xStyle(xNumberingStyles
->getByName(aListName
), uno::UNO_QUERY
);
9181 const uno::Reference
<container::XIndexAccess
> xNumberingRules(xStyle
->getPropertyValue("NumberingRules"), uno::UNO_QUERY
);
9182 if (xNumberingRules
.is())
9184 uno::Sequence
<beans::PropertyValue
> aProps
;
9185 xNumberingRules
->getByIndex(nNumberingLevel
) >>= aProps
;
9186 auto pProp
= std::find_if(std::cbegin(aProps
), std::cend(aProps
),
9187 [&aProp
](const beans::PropertyValue
& rProp
) { return rProp
.Name
== aProp
; });
9188 if (pProp
!= std::cend(aProps
))
9189 pProp
->Value
>>= nRet
;
9192 catch( const uno::Exception
& )
9194 // This can happen when the doc contains some hand-crafted invalid list level.
9200 sal_Int32
DomainMapper_Impl::getCurrentNumberingProperty(const OUString
& aProp
)
9204 std::optional
<PropertyMap::Property
> pProp
= m_pTopContext
->getProperty(PROP_NUMBERING_RULES
);
9205 uno::Reference
<container::XIndexAccess
> xNumberingRules
;
9207 xNumberingRules
.set(pProp
->second
, uno::UNO_QUERY
);
9208 pProp
= m_pTopContext
->getProperty(PROP_NUMBERING_LEVEL
);
9209 // Default numbering level is the first one.
9210 sal_Int32 nNumberingLevel
= 0;
9212 pProp
->second
>>= nNumberingLevel
;
9213 if (xNumberingRules
.is())
9215 uno::Sequence
<beans::PropertyValue
> aProps
;
9216 xNumberingRules
->getByIndex(nNumberingLevel
) >>= aProps
;
9217 auto pPropVal
= std::find_if(std::cbegin(aProps
), std::cend(aProps
),
9218 [&aProp
](const beans::PropertyValue
& rProp
) { return rProp
.Name
== aProp
; });
9219 if (pPropVal
!= std::cend(aProps
))
9220 pPropVal
->Value
>>= nRet
;
9227 void DomainMapper_Impl::enableInteropGrabBag(const OUString
& aName
)
9229 m_aInteropGrabBagName
= aName
;
9232 void DomainMapper_Impl::disableInteropGrabBag()
9234 m_aInteropGrabBagName
.clear();
9235 m_aInteropGrabBag
.clear();
9236 m_aSubInteropGrabBag
.clear();
9239 bool DomainMapper_Impl::isInteropGrabBagEnabled() const
9241 return !(m_aInteropGrabBagName
.isEmpty());
9244 void DomainMapper_Impl::appendGrabBag(std::vector
<beans::PropertyValue
>& rInteropGrabBag
, const OUString
& aKey
, const OUString
& aValue
)
9246 if (m_aInteropGrabBagName
.isEmpty())
9248 beans::PropertyValue aProperty
;
9249 aProperty
.Name
= aKey
;
9250 aProperty
.Value
<<= aValue
;
9251 rInteropGrabBag
.push_back(aProperty
);
9254 void DomainMapper_Impl::appendGrabBag(std::vector
<beans::PropertyValue
>& rInteropGrabBag
, const OUString
& aKey
, std::vector
<beans::PropertyValue
>& rValue
)
9256 if (m_aInteropGrabBagName
.isEmpty())
9258 beans::PropertyValue aProperty
;
9259 aProperty
.Name
= aKey
;
9260 aProperty
.Value
<<= comphelper::containerToSequence(rValue
);
9262 rInteropGrabBag
.push_back(aProperty
);
9265 void DomainMapper_Impl::substream(Id rName
,
9266 ::writerfilter::Reference
<Stream
>::Pointer_t
const& ref
)
9269 size_t contextSize(m_aContextStack
.size());
9270 size_t propSize
[NUMBER_OF_CONTEXTS
];
9271 for (int i
= 0; i
< NUMBER_OF_CONTEXTS
; ++i
) {
9272 propSize
[i
] = m_aPropertyStacks
[i
].size();
9276 // Save "has footnote" state, which is specific to a section in the body
9277 // text, so state from substreams is not relevant.
9278 bool bHasFtn
= m_bHasFtn
;
9280 //finalize any waiting frames before starting alternate streams
9281 CheckUnregisteredFrameConversion();
9282 ExecuteFrameConversion();
9284 appendTableManager();
9285 // Appending a TableManager resets its TableHandler, so we need to append
9286 // that as well, or tables won't be imported properly in headers/footers.
9287 appendTableHandler();
9288 getTableManager().startLevel();
9290 //import of page header/footer
9291 //Ensure that only one header/footer per section is pushed
9295 case NS_ooxml::LN_headerl
:
9296 PushPageHeader(SectionPropertyMap::PAGE_LEFT
);
9298 case NS_ooxml::LN_headerr
:
9299 PushPageHeader(SectionPropertyMap::PAGE_RIGHT
);
9301 case NS_ooxml::LN_headerf
:
9302 PushPageHeader(SectionPropertyMap::PAGE_FIRST
);
9304 case NS_ooxml::LN_footerl
:
9305 PushPageFooter(SectionPropertyMap::PAGE_LEFT
);
9307 case NS_ooxml::LN_footerr
:
9308 PushPageFooter(SectionPropertyMap::PAGE_RIGHT
);
9310 case NS_ooxml::LN_footerf
:
9311 PushPageFooter(SectionPropertyMap::PAGE_FIRST
);
9313 case NS_ooxml::LN_footnote
:
9314 case NS_ooxml::LN_endnote
:
9315 PushFootOrEndnote( NS_ooxml::LN_footnote
== rName
);
9317 case NS_ooxml::LN_annotation
:
9324 ref
->resolve(m_rDMapper
);
9326 catch (xml::sax::SAXException
const&)
9334 case NS_ooxml::LN_headerl
:
9335 case NS_ooxml::LN_headerr
:
9336 case NS_ooxml::LN_headerf
:
9337 case NS_ooxml::LN_footerl
:
9338 case NS_ooxml::LN_footerr
:
9339 case NS_ooxml::LN_footerf
:
9340 PopPageHeaderFooter();
9342 case NS_ooxml::LN_footnote
:
9343 case NS_ooxml::LN_endnote
:
9346 case NS_ooxml::LN_annotation
:
9351 getTableManager().endLevel();
9353 m_bHasFtn
= bHasFtn
;
9357 case NS_ooxml::LN_footnote
:
9358 case NS_ooxml::LN_endnote
:
9359 m_pTableHandler
->setHadFootOrEndnote(true);
9364 // check that stacks are the same as before substream
9365 assert(m_aContextStack
.size() == contextSize
);
9366 for (int i
= 0; i
< NUMBER_OF_CONTEXTS
; ++i
) {
9367 assert(m_aPropertyStacks
[i
].size() == propSize
[i
]);
9371 void DomainMapper_Impl::commentProps(const OUString
& sId
, const CommentProperties
& rProps
)
9373 m_aCommentProps
[sId
] = rProps
;
9377 bool DomainMapper_Impl::handlePreviousParagraphBorderInBetween() const
9379 if (!m_xPreviousParagraph
.is())
9382 // Connected borders ("ParaIsConnectBorder") are always on by default
9383 // and never changed by DomainMapper. Except one case when border in
9384 // between is used. So this is not the best, but easiest way to check
9385 // is previous paragraph has border in between.
9386 bool bConnectBorders
= true;
9387 m_xPreviousParagraph
->getPropertyValue(getPropertyName(PROP_PARA_CONNECT_BORDERS
)) >>= bConnectBorders
;
9389 if (bConnectBorders
)
9392 // Previous paragraph has border in between. Current one also has (since this
9393 // method is called). So current paragraph will get border above, but
9394 // also need to ensure, that no unexpected bottom border are remaining in previous
9395 // paragraph: since ParaIsConnectBorder=false it will be displayed in unexpected way.
9396 m_xPreviousParagraph
->setPropertyValue(getPropertyName(PROP_BOTTOM_BORDER
), uno::Any(table::BorderLine2()));
9401 OUString
DomainMapper_Impl::getFontNameForTheme(const Id id
)
9403 auto const& pHandler
= getThemeHandler();
9405 return pHandler
->getFontNameForTheme(id
);
9411 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */