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 <ooxml/resourceids.hxx>
24 #include "DomainMapper_Impl.hxx"
25 #include "ConversionHelper.hxx"
26 #include "SdtHelper.hxx"
27 #include "DomainMapperTableHandler.hxx"
28 #include "TagLogger.hxx"
29 #include <com/sun/star/uno/XComponentContext.hpp>
30 #include <com/sun/star/graphic/XGraphic.hpp>
31 #include <com/sun/star/beans/XPropertyState.hpp>
32 #include <com/sun/star/container/XNamed.hpp>
33 #include <com/sun/star/document/PrinterIndependentLayout.hpp>
34 #include <com/sun/star/document/IndexedPropertyValues.hpp>
35 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
36 #include <com/sun/star/embed/XEmbeddedObject.hpp>
37 #include <com/sun/star/lang/XServiceInfo.hpp>
38 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
39 #include <com/sun/star/style/LineNumberPosition.hpp>
40 #include <com/sun/star/style/LineSpacing.hpp>
41 #include <com/sun/star/style/LineSpacingMode.hpp>
42 #include <com/sun/star/text/ChapterFormat.hpp>
43 #include <com/sun/star/text/FilenameDisplayFormat.hpp>
44 #include <com/sun/star/text/SetVariableType.hpp>
45 #include <com/sun/star/text/XDocumentIndex.hpp>
46 #include <com/sun/star/text/XDocumentIndexesSupplier.hpp>
47 #include <com/sun/star/text/XFootnote.hpp>
48 #include <com/sun/star/text/XLineNumberingProperties.hpp>
49 #include <com/sun/star/style/XStyle.hpp>
50 #include <com/sun/star/text/PageNumberType.hpp>
51 #include <com/sun/star/text/HoriOrientation.hpp>
52 #include <com/sun/star/text/VertOrientation.hpp>
53 #include <com/sun/star/text/ReferenceFieldPart.hpp>
54 #include <com/sun/star/text/RelOrientation.hpp>
55 #include <com/sun/star/text/ReferenceFieldSource.hpp>
56 #include <com/sun/star/text/SizeType.hpp>
57 #include <com/sun/star/text/TextContentAnchorType.hpp>
58 #include <com/sun/star/text/WrapTextMode.hpp>
59 #include <com/sun/star/text/XDependentTextField.hpp>
60 #include <com/sun/star/text/XParagraphCursor.hpp>
61 #include <com/sun/star/text/XRedline.hpp>
62 #include <com/sun/star/text/XTextFieldsSupplier.hpp>
63 #include <com/sun/star/text/XTextFrame.hpp>
64 #include <com/sun/star/text/RubyPosition.hpp>
65 #include <com/sun/star/text/XTextRangeCompare.hpp>
66 #include <com/sun/star/style/DropCapFormat.hpp>
67 #include <com/sun/star/util/NumberFormatter.hpp>
68 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
69 #include <com/sun/star/util/XNumberFormatter.hpp>
70 #include <com/sun/star/document/XViewDataSupplier.hpp>
71 #include <com/sun/star/container/XIndexContainer.hpp>
72 #include <com/sun/star/text/ControlCharacter.hpp>
73 #include <com/sun/star/text/XTextColumns.hpp>
74 #include <com/sun/star/awt/CharSet.hpp>
75 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
76 #include <editeng/flditem.hxx>
77 #include <editeng/unotext.hxx>
78 #include <o3tl/temporary.hxx>
79 #include <oox/mathml/import.hxx>
80 #include <xmloff/odffields.hxx>
81 #include <rtl/uri.hxx>
82 #include <dmapper/GraphicZOrderHelper.hxx>
84 #include <oox/token/tokens.hxx>
90 #include <unordered_map>
92 #include <officecfg/Office/Common.hxx>
93 #include <filter/msfilter/util.hxx>
94 #include <comphelper/sequence.hxx>
95 #include <comphelper/propertyvalue.hxx>
96 #include <comphelper/propertysequence.hxx>
97 #include <unotools/configmgr.hxx>
98 #include <unotools/mediadescriptor.hxx>
99 #include <tools/diagnose_ex.h>
100 #include <sal/log.hxx>
103 using namespace ::com::sun::star
;
105 namespace writerfilter::dmapper
{
107 //line numbering for header/footer
108 static void lcl_linenumberingHeaderFooter( const uno::Reference
<container::XNameContainer
>& xStyles
, const OUString
& rname
, DomainMapper_Impl
* dmapper
)
110 const StyleSheetEntryPtr pEntry
= dmapper
->GetStyleSheetTable()->FindStyleSheetByISTD( rname
);
113 const StyleSheetPropertyMap
* pStyleSheetProperties
= dynamic_cast<const StyleSheetPropertyMap
*>( pEntry
->pProperties
.get() );
114 if ( !pStyleSheetProperties
)
116 sal_Int32 nListId
= pStyleSheetProperties
->GetListId();
119 if( xStyles
->hasByName( rname
) )
121 uno::Reference
< style::XStyle
> xStyle
;
122 xStyles
->getByName( rname
) >>= xStyle
;
125 uno::Reference
<beans::XPropertySet
> xPropertySet( xStyle
, uno::UNO_QUERY
);
126 xPropertySet
->setPropertyValue( getPropertyName( PROP_PARA_LINE_NUMBER_COUNT
), uno::makeAny( nListId
>= 0 ) );
131 // Populate Dropdown Field properties from FFData structure
132 static void lcl_handleDropdownField( const uno::Reference
< beans::XPropertySet
>& rxFieldProps
, const FFDataHandler::Pointer_t
& pFFDataHandler
)
134 if ( rxFieldProps
.is() )
136 if ( !pFFDataHandler
->getName().isEmpty() )
137 rxFieldProps
->setPropertyValue( "Name", uno::makeAny( pFFDataHandler
->getName() ) );
139 const FFDataHandler::DropDownEntries_t
& rEntries
= pFFDataHandler
->getDropDownEntries();
140 uno::Sequence
< OUString
> sItems( rEntries
.size() );
141 ::std::copy( rEntries
.begin(), rEntries
.end(), sItems
.begin());
142 if ( sItems
.hasElements() )
143 rxFieldProps
->setPropertyValue( "Items", uno::makeAny( sItems
) );
145 sal_Int32 nResult
= pFFDataHandler
->getDropDownResult().toInt32();
147 rxFieldProps
->setPropertyValue( "SelectedItem", uno::makeAny( sItems
[ nResult
] ) );
148 if ( !pFFDataHandler
->getHelpText().isEmpty() )
149 rxFieldProps
->setPropertyValue( "Help", uno::makeAny( pFFDataHandler
->getHelpText() ) );
153 static void lcl_handleTextField( const uno::Reference
< beans::XPropertySet
>& rxFieldProps
, const FFDataHandler::Pointer_t
& pFFDataHandler
)
155 if ( rxFieldProps
.is() && pFFDataHandler
)
157 rxFieldProps
->setPropertyValue
158 (getPropertyName(PROP_HINT
),
159 uno::makeAny(pFFDataHandler
->getStatusText()));
160 rxFieldProps
->setPropertyValue
161 (getPropertyName(PROP_HELP
),
162 uno::makeAny(pFFDataHandler
->getHelpText()));
163 rxFieldProps
->setPropertyValue
164 (getPropertyName(PROP_CONTENT
),
165 uno::makeAny(pFFDataHandler
->getTextDefault()));
171 struct FieldConversion
173 const char* cFieldServiceName
;
179 typedef std::unordered_map
<OUString
, FieldConversion
> FieldConversionMap_t
;
181 /// Gives access to the parent field context of the topmost one, if there is any.
182 static FieldContextPtr
GetParentFieldContext(const std::deque
<FieldContextPtr
>& rFieldStack
)
184 if (rFieldStack
.size() < 2)
189 return rFieldStack
[rFieldStack
.size() - 2];
192 /// Decides if the pInner field inside pOuter is allowed in Writer core, depending on their type.
193 static bool IsFieldNestingAllowed(const FieldContextPtr
& pOuter
, const FieldContextPtr
& pInner
)
195 if (!pOuter
->GetFieldId())
200 if (!pInner
->GetFieldId())
205 switch (*pOuter
->GetFieldId())
209 switch (*pInner
->GetFieldId())
211 case FIELD_MERGEFIELD
:
227 uno::Any
FloatingTableInfo::getPropertyValue(const OUString
&propertyName
)
229 for( beans::PropertyValue
const & propVal
: std::as_const(m_aFrameProperties
) )
230 if( propVal
.Name
== propertyName
)
231 return propVal
.Value
;
235 DomainMapper_Impl::DomainMapper_Impl(
236 DomainMapper
& rDMapper
,
237 uno::Reference
<uno::XComponentContext
> const& xContext
,
238 uno::Reference
<lang::XComponent
> const& xModel
,
239 SourceDocumentType eDocumentType
,
240 utl::MediaDescriptor
const & rMediaDesc
) :
241 m_eDocumentType( eDocumentType
),
242 m_rDMapper( rDMapper
),
243 m_xTextDocument( xModel
, uno::UNO_QUERY
),
244 m_xTextFactory( xModel
, uno::UNO_QUERY
),
245 m_xComponentContext( xContext
),
246 m_bForceGenericFields(!utl::ConfigManager::IsFuzzing() && officecfg::Office::Common::Filter::Microsoft::Import::ForceImportWWFieldsAsGenericFields::get(m_xComponentContext
)),
247 m_bSetUserFieldContent( false ),
248 m_bSetCitation( false ),
249 m_bSetDateValue( false ),
250 m_bIsFirstSection( true ),
251 m_bIsColumnBreakDeferred( false ),
252 m_bIsPageBreakDeferred( false ),
253 m_bSdtEndDeferred(false),
254 m_bParaSdtEndDeferred(false),
256 m_bStartTOCHeaderFooter(false),
257 m_bStartedTOC(false),
258 m_bStartIndex(false),
259 m_bStartBibliography(false),
260 m_nStartGenericField(0),
261 m_bTextInserted(false),
263 m_pLastSectionContext( ),
264 m_pLastCharacterContext(),
265 m_sCurrentParaStyleName(),
266 m_sDefaultParaStyleName(),
267 m_bInStyleSheetImport( false ),
268 m_bInAnyTableImport( false ),
269 m_eInHeaderFooterImport( HeaderFooterImportState::none
),
270 m_bDiscardHeaderFooter( false ),
271 m_bInFootOrEndnote(false),
272 m_bHasFootnoteStyle(false),
273 m_bCheckFootnoteStyle(false),
274 m_eSkipFootnoteState(SkipFootnoteSeparator::OFF
),
275 m_bLineNumberingSet( false ),
276 m_bIsInFootnoteProperties( false ),
277 m_bIsParaMarkerChange( false ),
278 m_bParaChanged( false ),
279 m_bIsFirstParaInSection( true ),
280 m_bIsFirstParaInSectionAfterRedline( true ),
281 m_bDummyParaAddedForTableInSection( false ),
282 m_bTextFrameInserted(false),
283 m_bIsPreviousParagraphFramed( false ),
284 m_bIsLastParaInSection( false ),
285 m_bIsLastSectionGroup( false ),
286 m_bIsInComments( false ),
287 m_bParaSectpr( false ),
288 m_bUsingEnhancedFields( false ),
290 m_bIsFirstRun(false),
291 m_bIsOutsideAParagraph(true),
292 m_xAnnotationField(),
293 m_nAnnotationId( -1 ),
294 m_aAnnotationPositions(),
295 m_aSmartTagHandler(m_xComponentContext
, m_xTextDocument
),
296 m_xInsertTextRange(rMediaDesc
.getUnpackedValueOrDefault("TextInsertModeRange", uno::Reference
<text::XTextRange
>())),
297 m_bIsNewDoc(!rMediaDesc
.getUnpackedValueOrDefault("InsertMode", false)),
298 m_bIsReadGlossaries(rMediaDesc
.getUnpackedValueOrDefault("ReadGlossaries", false)),
300 m_nTableCellDepth(0),
301 m_nLastTableCellParagraphDepth(0),
304 m_bCheckFirstFootnoteTab(false),
305 m_bIgnoreNextTab(false),
306 m_bIsSplitPara(false),
307 m_bIsActualParagraphFramed( false ),
308 m_vTextFramesForChaining(),
309 m_bParaHadField(false),
310 m_bParaAutoBefore(false),
311 m_bFirstParagraphInCell(true),
312 m_bSaveFirstParagraphInCell(false)
315 m_aBaseUrl
= rMediaDesc
.getUnpackedValueOrDefault(
316 utl::MediaDescriptor::PROP_DOCUMENTBASEURL(), OUString());
317 if (m_aBaseUrl
.isEmpty()) {
318 m_aBaseUrl
= rMediaDesc
.getUnpackedValueOrDefault(
319 utl::MediaDescriptor::PROP_URL(), OUString());
322 appendTableManager( );
324 uno::Reference
< text::XTextAppend
> xBodyTextAppend( m_xBodyText
, uno::UNO_QUERY
);
325 m_aTextAppendStack
.push(TextAppendContext(xBodyTextAppend
,
326 m_bIsNewDoc
? uno::Reference
<text::XTextCursor
>() : m_xBodyText
->createTextCursorByRange(m_xInsertTextRange
)));
328 //todo: does it makes sense to set the body text as static text interface?
329 uno::Reference
< text::XTextAppendAndConvert
> xBodyTextAppendAndConvert( m_xBodyText
, uno::UNO_QUERY
);
330 m_pTableHandler
= new DomainMapperTableHandler(xBodyTextAppendAndConvert
, *this);
331 getTableManager( ).setHandler(m_pTableHandler
);
333 getTableManager( ).startLevel();
334 m_bUsingEnhancedFields
= !utl::ConfigManager::IsFuzzing() && officecfg::Office::Common::Filter::Microsoft::Import::ImportWWFieldsAsEnhancedFields::get(m_xComponentContext
);
336 m_pSdtHelper
= new SdtHelper(*this);
338 m_aRedlines
.push(std::vector
<RedlineParamsPtr
>());
342 DomainMapper_Impl::~DomainMapper_Impl()
345 // Don't remove last paragraph when pasting, sw expects that empty paragraph.
347 RemoveLastParagraph();
348 if (hasTableManager())
350 getTableManager().endLevel();
355 uno::Reference
< container::XNameContainer
> const & DomainMapper_Impl::GetPageStyles()
357 if(!m_xPageStyles1
.is())
359 uno::Reference
< style::XStyleFamiliesSupplier
> xSupplier( m_xTextDocument
, uno::UNO_QUERY
);
361 xSupplier
->getStyleFamilies()->getByName("PageStyles") >>= m_xPageStyles1
;
363 return m_xPageStyles1
;
366 OUString
DomainMapper_Impl::GetUnusedPageStyleName()
368 static const char DEFAULT_STYLE
[] = "Converted";
369 if (!m_xNextUnusedPageStyleNo
)
371 const uno::Sequence
< OUString
> aPageStyleNames
= GetPageStyles()->getElementNames();
372 sal_Int32 nMaxIndex
= 0;
373 // find the highest number x in each style with the name "DEFAULT_STYLE+x" and
374 // return an incremented name
376 for ( const auto& rStyleName
: aPageStyleNames
)
378 if ( rStyleName
.startsWith( DEFAULT_STYLE
) )
380 sal_Int32 nIndex
= rStyleName
.copy( strlen( DEFAULT_STYLE
) ).toInt32();
381 if ( nIndex
> nMaxIndex
)
385 m_xNextUnusedPageStyleNo
= nMaxIndex
+ 1;
388 OUString sPageStyleName
= DEFAULT_STYLE
+ OUString::number( *m_xNextUnusedPageStyleNo
);
389 *m_xNextUnusedPageStyleNo
= *m_xNextUnusedPageStyleNo
+ 1;
390 return sPageStyleName
;
393 uno::Reference
< text::XText
> const & DomainMapper_Impl::GetBodyText()
395 if(!m_xBodyText
.is())
397 if (m_xInsertTextRange
.is())
398 m_xBodyText
= m_xInsertTextRange
->getText();
399 else if (m_xTextDocument
.is())
400 m_xBodyText
= m_xTextDocument
->getText();
406 uno::Reference
< beans::XPropertySet
> const & DomainMapper_Impl::GetDocumentSettings()
408 if( !m_xDocumentSettings
.is() && m_xTextFactory
.is())
410 m_xDocumentSettings
.set( m_xTextFactory
->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY
);
412 return m_xDocumentSettings
;
416 void DomainMapper_Impl::SetDocumentSettingsProperty( const OUString
& rPropName
, const uno::Any
& rValue
)
418 uno::Reference
< beans::XPropertySet
> xSettings
= GetDocumentSettings();
423 xSettings
->setPropertyValue( rPropName
, rValue
);
425 catch( const uno::Exception
& )
430 void DomainMapper_Impl::RemoveDummyParaForTableInSection()
432 SetIsDummyParaAddedForTableInSection(false);
433 PropertyMapPtr pContext
= GetTopContextOfType(CONTEXT_SECTION
);
434 SectionPropertyMap
* pSectionContext
= dynamic_cast< SectionPropertyMap
* >( pContext
.get() );
435 if (!pSectionContext
)
438 if (m_aTextAppendStack
.empty())
440 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
441 if (!xTextAppend
.is())
444 uno::Reference
< text::XTextCursor
> xCursor
= xTextAppend
->createTextCursorByRange(pSectionContext
->GetStartingRange());
446 // Remove the extra NumPicBullets from the document,
447 // which get attached to the first paragraph in the
449 ListsManager::Pointer pListTable
= GetListTable();
450 pListTable
->DisposeNumPicBullets();
452 uno::Reference
<container::XEnumerationAccess
> xEnumerationAccess(xCursor
, uno::UNO_QUERY
);
453 if (xEnumerationAccess
.is() && m_aTextAppendStack
.size() == 1 )
455 uno::Reference
<container::XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
456 uno::Reference
<lang::XComponent
> xParagraph(xEnumeration
->nextElement(), uno::UNO_QUERY
);
457 xParagraph
->dispose();
460 void DomainMapper_Impl::AddDummyParaForTableInSection()
462 // Shapes can't have sections.
466 if (!m_aTextAppendStack
.empty())
468 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
469 uno::Reference
< text::XTextCursor
> xCrsr
= xTextAppend
->getText()->createTextCursor();
470 uno::Reference
< text::XText
> xText
= xTextAppend
->getText();
471 if(xCrsr
.is() && xText
.is())
473 xTextAppend
->finishParagraph( uno::Sequence
< beans::PropertyValue
>() );
474 SetIsDummyParaAddedForTableInSection(true);
479 void DomainMapper_Impl::RemoveLastParagraph( )
481 if (m_bDiscardHeaderFooter
)
484 if (m_aTextAppendStack
.empty())
486 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
487 if (!xTextAppend
.is())
491 uno::Reference
< text::XTextCursor
> xCursor
;
494 xCursor
= xTextAppend
->createTextCursor();
495 xCursor
->gotoEnd(false);
498 xCursor
= m_aTextAppendStack
.top().xCursor
;
499 uno::Reference
<container::XEnumerationAccess
> xEnumerationAccess(xCursor
, uno::UNO_QUERY
);
500 // Keep the character properties of the last but one paragraph, even if
501 // it's empty. This works for headers/footers, and maybe in other cases
502 // as well, but surely not in textboxes.
503 // fdo#58327: also do this at the end of the document: when pasting,
504 // a table before the cursor position would be deleted
505 // (but only for paste/insert, not load; otherwise it can happen that
506 // flys anchored at the disposed paragraph are deleted (fdo47036.rtf))
507 bool const bEndOfDocument(m_aTextAppendStack
.size() == 1);
508 if ((IsInHeaderFooter() || (bEndOfDocument
&& !m_bIsNewDoc
))
509 && xEnumerationAccess
.is())
511 uno::Reference
<container::XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
512 uno::Reference
<lang::XComponent
> xParagraph(xEnumeration
->nextElement(), uno::UNO_QUERY
);
513 xParagraph
->dispose();
515 else if (xCursor
.is())
517 xCursor
->goLeft( 1, true );
518 // If this is a text on a shape, possibly the text has the trailing
519 // newline removed already.
520 if (xCursor
->getString() == SAL_NEWLINE_STRING
||
521 // tdf#105444 comments need an exception, if SAL_NEWLINE_STRING defined as "\r\n"
522 (sizeof(SAL_NEWLINE_STRING
)-1 == 2 && xCursor
->getString() == "\n"))
524 uno::Reference
<beans::XPropertySet
> xDocProps(GetTextDocument(), uno::UNO_QUERY
);
525 const OUString
aRecordChanges("RecordChanges");
526 uno::Any
aPreviousValue(xDocProps
->getPropertyValue(aRecordChanges
));
528 // disable redlining for this operation, otherwise we might
529 // end up with an unwanted recorded deletion
530 xDocProps
->setPropertyValue(aRecordChanges
, uno::Any(false));
533 xCursor
->setString(OUString());
536 xDocProps
->setPropertyValue(aRecordChanges
, aPreviousValue
);
540 catch( const uno::Exception
& )
546 void DomainMapper_Impl::SetIsLastSectionGroup( bool bIsLast
)
548 m_bIsLastSectionGroup
= bIsLast
;
551 void DomainMapper_Impl::SetIsLastParagraphInSection( bool bIsLast
)
553 m_bIsLastParaInSection
= bIsLast
;
557 void DomainMapper_Impl::SetIsFirstParagraphInSection( bool bIsFirst
)
559 m_bIsFirstParaInSection
= bIsFirst
;
562 void DomainMapper_Impl::SetIsFirstParagraphInSectionAfterRedline( bool bIsFirstAfterRedline
)
564 m_bIsFirstParaInSectionAfterRedline
= bIsFirstAfterRedline
;
567 bool DomainMapper_Impl::GetIsFirstParagraphInSection( bool bAfterRedline
) const
569 // Anchored objects may include multiple paragraphs,
570 // and none of them should be considered the first para in section.
571 return ( bAfterRedline
? m_bIsFirstParaInSectionAfterRedline
: m_bIsFirstParaInSection
)
574 && !IsInFootOrEndnote();
577 void DomainMapper_Impl::SetIsFirstParagraphInShape(bool bIsFirst
)
579 m_bIsFirstParaInShape
= bIsFirst
;
582 void DomainMapper_Impl::SetIsDummyParaAddedForTableInSection( bool bIsAdded
)
584 m_bDummyParaAddedForTableInSection
= bIsAdded
;
588 void DomainMapper_Impl::SetIsTextFrameInserted( bool bIsInserted
)
590 m_bTextFrameInserted
= bIsInserted
;
594 void DomainMapper_Impl::SetParaSectpr(bool bParaSectpr
)
596 m_bParaSectpr
= bParaSectpr
;
600 void DomainMapper_Impl::SetSdt(bool bSdt
)
604 if (m_bSdt
&& !m_aTextAppendStack
.empty())
606 m_xSdtEntryStart
= GetTopTextAppend()->getEnd();
610 m_xSdtEntryStart
.clear();
615 void DomainMapper_Impl::PushProperties(ContextType eId
)
617 PropertyMapPtr
pInsert(eId
== CONTEXT_SECTION
?
618 (new SectionPropertyMap( m_bIsFirstSection
)) :
619 eId
== CONTEXT_PARAGRAPH
? new ParagraphPropertyMap
: new PropertyMap
);
620 if(eId
== CONTEXT_SECTION
)
622 if( m_bIsFirstSection
)
623 m_bIsFirstSection
= false;
624 // beginning with the second section group a section has to be inserted
626 SectionPropertyMap
* pSectionContext_
= dynamic_cast< SectionPropertyMap
* >( pInsert
.get() );
627 if (!m_aTextAppendStack
.empty())
629 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
630 if (xTextAppend
.is() && pSectionContext_
)
631 pSectionContext_
->SetStart( xTextAppend
->getEnd() );
634 if(eId
== CONTEXT_PARAGRAPH
&& m_bIsSplitPara
)
636 m_aPropertyStacks
[eId
].push( GetTopContextOfType(eId
));
637 m_bIsSplitPara
= false;
641 m_aPropertyStacks
[eId
].push( pInsert
);
643 m_aContextStack
.push(eId
);
645 m_pTopContext
= m_aPropertyStacks
[eId
].top();
649 void DomainMapper_Impl::PushStyleProperties( const PropertyMapPtr
& pStyleProperties
)
651 m_aPropertyStacks
[CONTEXT_STYLESHEET
].push( pStyleProperties
);
652 m_aContextStack
.push(CONTEXT_STYLESHEET
);
654 m_pTopContext
= m_aPropertyStacks
[CONTEXT_STYLESHEET
].top();
658 void DomainMapper_Impl::PushListProperties(const PropertyMapPtr
& pListProperties
)
660 m_aPropertyStacks
[CONTEXT_LIST
].push( pListProperties
);
661 m_aContextStack
.push(CONTEXT_LIST
);
662 m_pTopContext
= m_aPropertyStacks
[CONTEXT_LIST
].top();
666 void DomainMapper_Impl::PopProperties(ContextType eId
)
668 OSL_ENSURE(!m_aPropertyStacks
[eId
].empty(), "section stack already empty");
669 if ( m_aPropertyStacks
[eId
].empty() )
672 if ( eId
== CONTEXT_SECTION
)
674 if (m_aPropertyStacks
[eId
].size() == 1) // tdf#112202 only top level !!!
676 m_pLastSectionContext
= m_aPropertyStacks
[eId
].top();
679 else if (eId
== CONTEXT_CHARACTER
)
681 m_pLastCharacterContext
= m_aPropertyStacks
[eId
].top();
682 // Sadly an assert about deferredCharacterProperties being empty is not possible
683 // here, because appendTextPortion() may not be called for every character section.
684 deferredCharacterProperties
.clear();
687 if (!IsInFootOrEndnote() && IsInCustomFootnote() && !m_aPropertyStacks
[eId
].empty())
689 PropertyMapPtr pRet
= m_aPropertyStacks
[eId
].top();
690 if (pRet
->GetFootnote().is() && m_pFootnoteContext
.is())
694 m_aPropertyStacks
[eId
].pop();
695 m_aContextStack
.pop();
696 if(!m_aContextStack
.empty() && !m_aPropertyStacks
[m_aContextStack
.top()].empty())
698 m_pTopContext
= m_aPropertyStacks
[m_aContextStack
.top()].top();
701 // OSL_ENSURE(eId == CONTEXT_SECTION, "this should happen at a section context end");
702 m_pTopContext
.clear();
707 PropertyMapPtr
DomainMapper_Impl::GetTopContextOfType(ContextType eId
)
710 if(!m_aPropertyStacks
[eId
].empty())
711 pRet
= m_aPropertyStacks
[eId
].top();
715 bool DomainMapper_Impl::HasTopText() const
717 return !m_aTextAppendStack
.empty();
720 uno::Reference
< text::XTextAppend
> const & DomainMapper_Impl::GetTopTextAppend()
722 OSL_ENSURE(!m_aTextAppendStack
.empty(), "text append stack is empty" );
723 return m_aTextAppendStack
.top().xTextAppend
;
726 FieldContextPtr
const & DomainMapper_Impl::GetTopFieldContext()
728 SAL_WARN_IF(m_aFieldStack
.empty(), "writerfilter.dmapper", "Field stack is empty");
729 return m_aFieldStack
.back();
732 void DomainMapper_Impl::InitTabStopFromStyle( const uno::Sequence
< style::TabStop
>& rInitTabStops
)
734 OSL_ENSURE(m_aCurrentTabStops
.empty(), "tab stops already initialized");
735 for( const auto& rTabStop
: rInitTabStops
)
737 m_aCurrentTabStops
.emplace_back(rTabStop
);
741 void DomainMapper_Impl::IncorporateTabStop( const DeletableTabStop
& rTabStop
)
743 sal_Int32 nConverted
= rTabStop
.Position
;
744 auto aIt
= std::find_if(m_aCurrentTabStops
.begin(), m_aCurrentTabStops
.end(),
745 [&nConverted
](const DeletableTabStop
& rCurrentTabStop
) { return rCurrentTabStop
.Position
== nConverted
; });
746 if( aIt
!= m_aCurrentTabStops
.end() )
748 if( rTabStop
.bDeleted
)
749 m_aCurrentTabStops
.erase( aIt
);
754 m_aCurrentTabStops
.push_back( rTabStop
);
758 uno::Sequence
< style::TabStop
> DomainMapper_Impl::GetCurrentTabStopAndClear()
760 std::vector
<style::TabStop
> aRet
;
761 for (const DeletableTabStop
& rStop
: m_aCurrentTabStops
)
764 aRet
.push_back(rStop
);
766 m_aCurrentTabStops
.clear();
767 return comphelper::containerToSequence(aRet
);
770 OUString
DomainMapper_Impl::GetCurrentParaStyleName()
772 // use saved currParaStyleName as a fallback, in case no particular para style name applied.
773 OUString sName
= m_sCurrentParaStyleName
;
774 PropertyMapPtr pParaContext
= GetTopContextOfType(CONTEXT_PARAGRAPH
);
775 if ( pParaContext
&& pParaContext
->isSet(PROP_PARA_STYLE_NAME
) )
776 pParaContext
->getProperty(PROP_PARA_STYLE_NAME
)->second
>>= sName
;
778 // In rare situations the name might still be blank, so use the default style,
779 // despite documentation that states, "If this attribute is not specified for any style,
780 // then no properties shall be applied to objects of the specified type."
781 // Word, however, assigns "Normal" style even in these situations.
782 if ( !m_bInStyleSheetImport
&& sName
.isEmpty() )
783 sName
= GetDefaultParaStyleName();
788 OUString
DomainMapper_Impl::GetDefaultParaStyleName()
790 // After import the default style won't change and is frequently requested: cache the LO style name.
791 // TODO assert !InStyleSheetImport? This function really only makes sense once import is finished anyway.
792 if ( m_sDefaultParaStyleName
.isEmpty() )
794 const StyleSheetEntryPtr pEntry
= GetStyleSheetTable()->FindDefaultParaStyle();
795 if ( pEntry
&& !pEntry
->sConvertedStyleName
.isEmpty() )
797 if ( !m_bInStyleSheetImport
)
798 m_sDefaultParaStyleName
= pEntry
->sConvertedStyleName
;
799 return pEntry
->sConvertedStyleName
;
804 return m_sDefaultParaStyleName
;
807 uno::Any
DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId
, StyleSheetEntryPtr pEntry
, const bool bDocDefaults
, const bool bPara
, bool* pIsDocDefault
)
809 while(pEntry
.get( ) )
811 if(pEntry
->pProperties
)
813 std::optional
<PropertyMap::Property
> aProperty
=
814 pEntry
->pProperties
->getProperty(eId
);
818 *pIsDocDefault
= pEntry
->pProperties
->isDocDefault(eId
);
820 return aProperty
->second
;
823 //search until the property is set or no parent is available
824 StyleSheetEntryPtr pNewEntry
;
825 if ( !pEntry
->sBaseStyleIdentifier
.isEmpty() )
826 pNewEntry
= GetStyleSheetTable()->FindStyleSheetByISTD(pEntry
->sBaseStyleIdentifier
);
828 SAL_WARN_IF( pEntry
== pNewEntry
, "writerfilter.dmapper", "circular loop in style hierarchy?");
830 if (pEntry
== pNewEntry
) //fdo#49587
835 // not found in style, try the document's DocDefault properties
836 if ( bDocDefaults
&& bPara
)
838 const PropertyMapPtr
& pDefaultParaProps
= GetStyleSheetTable()->GetDefaultParaProps();
839 if ( pDefaultParaProps
)
841 std::optional
<PropertyMap::Property
> aProperty
= pDefaultParaProps
->getProperty(eId
);
845 *pIsDocDefault
= true;
847 return aProperty
->second
;
851 if ( bDocDefaults
&& isCharacterProperty(eId
) )
853 const PropertyMapPtr
& pDefaultCharProps
= GetStyleSheetTable()->GetDefaultCharProps();
854 if ( pDefaultCharProps
)
856 std::optional
<PropertyMap::Property
> aProperty
= pDefaultCharProps
->getProperty(eId
);
860 *pIsDocDefault
= true;
862 return aProperty
->second
;
868 *pIsDocDefault
= false;
873 uno::Any
DomainMapper_Impl::GetPropertyFromParaStyleSheet(PropertyIds eId
)
875 StyleSheetEntryPtr pEntry
;
876 if ( m_bInStyleSheetImport
)
877 pEntry
= GetStyleSheetTable()->GetCurrentEntry();
879 pEntry
= GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(GetCurrentParaStyleName());
880 return GetPropertyFromStyleSheet(eId
, pEntry
, /*bDocDefaults=*/true, /*bPara=*/true);
883 uno::Any
DomainMapper_Impl::GetPropertyFromCharStyleSheet(PropertyIds eId
, const PropertyMapPtr
& rContext
)
885 if ( m_bInStyleSheetImport
|| eId
== PROP_CHAR_STYLE_NAME
|| !isCharacterProperty(eId
) )
888 StyleSheetEntryPtr pEntry
;
889 OUString sCharStyleName
;
890 if ( GetAnyProperty(PROP_CHAR_STYLE_NAME
, rContext
) >>= sCharStyleName
)
891 pEntry
= GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(sCharStyleName
);
892 return GetPropertyFromStyleSheet(eId
, pEntry
, /*bDocDefaults=*/false, /*bPara=*/false);
895 uno::Any
DomainMapper_Impl::GetAnyProperty(PropertyIds eId
, const PropertyMapPtr
& rContext
)
897 // first look in directly applied attributes
900 std::optional
<PropertyMap::Property
> aProperty
= rContext
->getProperty(eId
);
902 return aProperty
->second
;
905 // then look whether it was inherited from a directly applied character style
906 if ( eId
!= PROP_CHAR_STYLE_NAME
&& isCharacterProperty(eId
) )
908 uno::Any aRet
= GetPropertyFromCharStyleSheet(eId
, rContext
);
909 if ( aRet
.hasValue() )
913 // then look in current paragraph style, and docDefaults
914 return GetPropertyFromParaStyleSheet(eId
);
917 OUString
DomainMapper_Impl::GetListStyleName(sal_Int32 nListId
)
919 auto const pList(GetListTable()->GetList( nListId
));
920 return pList
? pList
->GetStyleName() : OUString();
923 ListsManager::Pointer
const & DomainMapper_Impl::GetListTable()
927 new ListsManager( m_rDMapper
, m_xTextFactory
);
932 void DomainMapper_Impl::deferBreak( BreakType deferredBreakType
)
934 switch (deferredBreakType
)
937 m_bIsColumnBreakDeferred
= true;
940 // See SwWW8ImplReader::HandlePageBreakChar(), page break should be
941 // ignored inside tables.
942 if (m_nTableDepth
> 0)
945 m_bIsPageBreakDeferred
= true;
952 bool DomainMapper_Impl::isBreakDeferred( BreakType deferredBreakType
)
954 switch (deferredBreakType
)
957 return m_bIsColumnBreakDeferred
;
959 return m_bIsPageBreakDeferred
;
965 void DomainMapper_Impl::clearDeferredBreak(BreakType deferredBreakType
)
967 switch (deferredBreakType
)
970 m_bIsColumnBreakDeferred
= false;
973 m_bIsPageBreakDeferred
= false;
980 void DomainMapper_Impl::clearDeferredBreaks()
982 m_bIsColumnBreakDeferred
= false;
983 m_bIsPageBreakDeferred
= false;
986 void DomainMapper_Impl::setSdtEndDeferred(bool bSdtEndDeferred
)
988 m_bSdtEndDeferred
= bSdtEndDeferred
;
991 bool DomainMapper_Impl::isSdtEndDeferred() const
993 return m_bSdtEndDeferred
;
996 void DomainMapper_Impl::setParaSdtEndDeferred(bool bParaSdtEndDeferred
)
998 m_bParaSdtEndDeferred
= bParaSdtEndDeferred
;
1001 bool DomainMapper_Impl::isParaSdtEndDeferred() const
1003 return m_bParaSdtEndDeferred
;
1006 static void lcl_MoveBorderPropertiesToFrame(std::vector
<beans::PropertyValue
>& rFrameProperties
,
1007 uno::Reference
<text::XTextRange
> const& xStartTextRange
,
1008 uno::Reference
<text::XTextRange
> const& xEndTextRange
)
1012 if (!xStartTextRange
.is()) //rhbz#1077780
1014 uno::Reference
<text::XTextCursor
> xRangeCursor
= xStartTextRange
->getText()->createTextCursorByRange( xStartTextRange
);
1015 xRangeCursor
->gotoRange( xEndTextRange
, true );
1017 uno::Reference
<beans::XPropertySet
> xTextRangeProperties(xRangeCursor
, uno::UNO_QUERY
);
1018 if(!xTextRangeProperties
.is())
1021 static PropertyIds
const aBorderProperties
[] =
1027 PROP_LEFT_BORDER_DISTANCE
,
1028 PROP_RIGHT_BORDER_DISTANCE
,
1029 PROP_TOP_BORDER_DISTANCE
,
1030 PROP_BOTTOM_BORDER_DISTANCE
1033 for( size_t nProperty
= 0; nProperty
< SAL_N_ELEMENTS( aBorderProperties
); ++nProperty
)
1035 OUString sPropertyName
= getPropertyName(aBorderProperties
[nProperty
]);
1036 beans::PropertyValue aValue
;
1037 aValue
.Name
= sPropertyName
;
1038 aValue
.Value
= xTextRangeProperties
->getPropertyValue(sPropertyName
);
1039 rFrameProperties
.push_back(aValue
);
1041 xTextRangeProperties
->setPropertyValue( sPropertyName
, uno::makeAny(table::BorderLine2()));
1044 catch( const uno::Exception
& )
1050 static void lcl_AddRangeAndStyle(
1051 ParagraphPropertiesPtr
const & pToBeSavedProperties
,
1052 uno::Reference
< text::XTextAppend
> const& xTextAppend
,
1053 const PropertyMapPtr
& pPropertyMap
,
1054 TextAppendContext
const & rAppendContext
)
1056 uno::Reference
<text::XParagraphCursor
> xParaCursor(
1057 xTextAppend
->createTextCursorByRange( rAppendContext
.xInsertPosition
.is() ? rAppendContext
.xInsertPosition
: xTextAppend
->getEnd()), uno::UNO_QUERY_THROW
);
1058 pToBeSavedProperties
->SetEndingRange(xParaCursor
->getStart());
1059 xParaCursor
->gotoStartOfParagraph( false );
1061 pToBeSavedProperties
->SetStartingRange(xParaCursor
->getStart());
1064 std::optional
<PropertyMap::Property
> aParaStyle
= pPropertyMap
->getProperty(PROP_PARA_STYLE_NAME
);
1068 aParaStyle
->second
>>= sName
;
1069 pToBeSavedProperties
->SetParaStyleName(sName
);
1075 //define some default frame width - 0cm ATM: this allow the frame to be wrapped around the text
1076 static constexpr sal_Int32 DEFAULT_FRAME_MIN_WIDTH
= 0;
1077 static constexpr sal_Int32 DEFAULT_FRAME_MIN_HEIGHT
= 0;
1078 static constexpr sal_Int32 DEFAULT_VALUE
= 0;
1080 void DomainMapper_Impl::CheckUnregisteredFrameConversion( )
1082 if (m_aTextAppendStack
.empty())
1084 TextAppendContext
& rAppendContext
= m_aTextAppendStack
.top();
1085 // n#779642: ignore fly frame inside table as it could lead to messy situations
1086 if (!rAppendContext
.pLastParagraphProperties
.get())
1088 if (!rAppendContext
.pLastParagraphProperties
->IsFrameMode())
1090 if (!hasTableManager())
1092 if (getTableManager().isInTable())
1096 StyleSheetEntryPtr pParaStyle
=
1097 GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(rAppendContext
.pLastParagraphProperties
->GetParaStyleName());
1099 std::vector
<beans::PropertyValue
> aFrameProperties
;
1101 if ( pParaStyle
.get( ) )
1103 const ParagraphProperties
* pStyleProperties
= dynamic_cast<const ParagraphProperties
*>( pParaStyle
->pProperties
.get() );
1104 if (!pStyleProperties
)
1107 rAppendContext
.pLastParagraphProperties
->Getw() > 0 ?
1108 rAppendContext
.pLastParagraphProperties
->Getw() :
1109 pStyleProperties
->Getw();
1110 bool bAutoWidth
= nWidth
< 1;
1112 nWidth
= DEFAULT_FRAME_MIN_WIDTH
;
1113 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH
), nWidth
));
1115 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HEIGHT
),
1116 rAppendContext
.pLastParagraphProperties
->Geth() > 0 ?
1117 rAppendContext
.pLastParagraphProperties
->Geth() :
1118 pStyleProperties
->Geth() > 0 ? pStyleProperties
->Geth() : DEFAULT_FRAME_MIN_HEIGHT
));
1120 sal_Int16 nhRule
= sal_Int16(
1121 rAppendContext
.pLastParagraphProperties
->GethRule() >= 0 ?
1122 rAppendContext
.pLastParagraphProperties
->GethRule() :
1123 pStyleProperties
->GethRule());
1126 if ( rAppendContext
.pLastParagraphProperties
->Geth() >= 0 ||
1127 pStyleProperties
->GethRule() >= 0 )
1129 // [MS-OE376] Word uses a default value of "atLeast" for
1130 // this attribute when the value of the h attribute is not 0.
1131 nhRule
= text::SizeType::MIN
;
1135 nhRule
= text::SizeType::VARIABLE
;
1138 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SIZE_TYPE
), nhRule
));
1140 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH_TYPE
), bAutoWidth
? text::SizeType::MIN
: text::SizeType::FIX
));
1142 sal_Int16 nHoriOrient
= sal_Int16(
1143 rAppendContext
.pLastParagraphProperties
->GetxAlign() >= 0 ?
1144 rAppendContext
.pLastParagraphProperties
->GetxAlign() :
1145 pStyleProperties
->GetxAlign() >= 0 ? pStyleProperties
->GetxAlign() : text::HoriOrientation::NONE
);
1146 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT
), nHoriOrient
));
1148 //set a non negative default value
1149 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT_POSITION
),
1150 rAppendContext
.pLastParagraphProperties
->IsxValid() ?
1151 rAppendContext
.pLastParagraphProperties
->Getx() :
1152 pStyleProperties
->IsxValid() ? pStyleProperties
->Getx() : DEFAULT_VALUE
));
1154 //Default the anchor in case FramePr_hAnchor is missing ECMA 17.3.1.11
1155 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT_RELATION
), sal_Int16(
1156 rAppendContext
.pLastParagraphProperties
->GethAnchor() >= 0 ?
1157 rAppendContext
.pLastParagraphProperties
->GethAnchor() :
1158 pStyleProperties
->GethAnchor() >=0 ? pStyleProperties
->GethAnchor() : text::RelOrientation::FRAME
)));
1160 sal_Int16 nVertOrient
= sal_Int16(
1161 rAppendContext
.pLastParagraphProperties
->GetyAlign() >= 0 ?
1162 rAppendContext
.pLastParagraphProperties
->GetyAlign() :
1163 pStyleProperties
->GetyAlign() >= 0 ? pStyleProperties
->GetyAlign() : text::VertOrientation::NONE
);
1164 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT
), nVertOrient
));
1166 //set a non negative default value
1167 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_POSITION
),
1168 rAppendContext
.pLastParagraphProperties
->IsyValid() ?
1169 rAppendContext
.pLastParagraphProperties
->Gety() :
1170 pStyleProperties
->IsyValid() ? pStyleProperties
->Gety() : DEFAULT_VALUE
));
1172 //Default the anchor in case FramePr_vAnchor is missing ECMA 17.3.1.11
1173 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_RELATION
), sal_Int16(
1174 rAppendContext
.pLastParagraphProperties
->GetvAnchor() >= 0 ?
1175 rAppendContext
.pLastParagraphProperties
->GetvAnchor() :
1176 pStyleProperties
->GetvAnchor() >= 0 ? pStyleProperties
->GetvAnchor() : text::RelOrientation::FRAME
)));
1178 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SURROUND
),
1179 rAppendContext
.pLastParagraphProperties
->GetWrap() != text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
1180 ? rAppendContext
.pLastParagraphProperties
->GetWrap()
1181 : pStyleProperties
->GetWrap() != text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
1182 ? pStyleProperties
->GetWrap()
1183 : text::WrapTextMode_NONE
));
1185 /** FDO#73546 : distL & distR should be unsigned integers <Ecma 20.4.3.6>
1186 Swapped the array elements 11,12 & 13,14 since 11 & 12 are
1187 LEFT & RIGHT margins and 13,14 are TOP and BOTTOM margins respectively.
1189 sal_Int32 nRightDist
;
1190 sal_Int32 nLeftDist
= nRightDist
=
1191 rAppendContext
.pLastParagraphProperties
->GethSpace() >= 0 ?
1192 rAppendContext
.pLastParagraphProperties
->GethSpace() :
1193 pStyleProperties
->GethSpace() >= 0 ? pStyleProperties
->GethSpace() : 0;
1195 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_LEFT_MARGIN
), nHoriOrient
== text::HoriOrientation::LEFT
? 0 : nLeftDist
));
1196 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_RIGHT_MARGIN
), nHoriOrient
== text::HoriOrientation::RIGHT
? 0 : nRightDist
));
1198 sal_Int32 nBottomDist
;
1199 sal_Int32 nTopDist
= nBottomDist
=
1200 rAppendContext
.pLastParagraphProperties
->GetvSpace() >= 0 ?
1201 rAppendContext
.pLastParagraphProperties
->GetvSpace() :
1202 pStyleProperties
->GetvSpace() >= 0 ? pStyleProperties
->GetvSpace() : 0;
1204 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_TOP_MARGIN
), nVertOrient
== text::VertOrientation::TOP
? 0 : nTopDist
));
1205 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_BOTTOM_MARGIN
), nVertOrient
== text::VertOrientation::BOTTOM
? 0 : nBottomDist
));
1206 // If there is no fill, the Word default is 100% transparency.
1207 // Otherwise CellColorHandler has priority, and this setting
1209 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_BACK_COLOR_TRANSPARENCY
), sal_Int32(100)));
1211 uno::Sequence
<beans::PropertyValue
> aGrabBag( comphelper::InitPropertySequence({
1212 { "ParaFrameProperties", uno::Any(rAppendContext
.pLastParagraphProperties
->IsFrameMode()) }
1214 aFrameProperties
.push_back(comphelper::makePropertyValue("FrameInteropGrabBag", aGrabBag
));
1216 lcl_MoveBorderPropertiesToFrame(aFrameProperties
,
1217 rAppendContext
.pLastParagraphProperties
->GetStartingRange(),
1218 rAppendContext
.pLastParagraphProperties
->GetEndingRange());
1222 sal_Int32 nWidth
= rAppendContext
.pLastParagraphProperties
->Getw();
1223 bool bAutoWidth
= nWidth
< 1;
1225 nWidth
= DEFAULT_FRAME_MIN_WIDTH
;
1226 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH
), nWidth
));
1228 sal_Int16 nhRule
= sal_Int16(rAppendContext
.pLastParagraphProperties
->GethRule());
1231 if ( rAppendContext
.pLastParagraphProperties
->Geth() >= 0 )
1233 // [MS-OE376] Word uses a default value of atLeast for
1234 // this attribute when the value of the h attribute is not 0.
1235 nhRule
= text::SizeType::MIN
;
1239 nhRule
= text::SizeType::VARIABLE
;
1242 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SIZE_TYPE
), nhRule
));
1244 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH_TYPE
), bAutoWidth
? text::SizeType::MIN
: text::SizeType::FIX
));
1246 sal_Int16 nHoriOrient
= sal_Int16(
1247 rAppendContext
.pLastParagraphProperties
->GetxAlign() >= 0 ?
1248 rAppendContext
.pLastParagraphProperties
->GetxAlign() :
1249 text::HoriOrientation::NONE
);
1250 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT
), nHoriOrient
));
1252 sal_Int16 nVertOrient
= sal_Int16(
1253 rAppendContext
.pLastParagraphProperties
->GetyAlign() >= 0 ?
1254 rAppendContext
.pLastParagraphProperties
->GetyAlign() :
1255 text::VertOrientation::NONE
);
1256 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT
), nVertOrient
));
1258 sal_Int32 nVertDist
= rAppendContext
.pLastParagraphProperties
->GethSpace();
1261 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_LEFT_MARGIN
), nVertOrient
== text::VertOrientation::TOP
? 0 : nVertDist
));
1262 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_RIGHT_MARGIN
), nVertOrient
== text::VertOrientation::BOTTOM
? 0 : nVertDist
));
1264 sal_Int32 nHoriDist
= rAppendContext
.pLastParagraphProperties
->GetvSpace();
1267 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_TOP_MARGIN
), nHoriOrient
== text::HoriOrientation::LEFT
? 0 : nHoriDist
));
1268 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_BOTTOM_MARGIN
), nHoriOrient
== text::HoriOrientation::RIGHT
? 0 : nHoriDist
));
1270 if( rAppendContext
.pLastParagraphProperties
->Geth() > 0 )
1271 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HEIGHT
), rAppendContext
.pLastParagraphProperties
->Geth()));
1273 if( rAppendContext
.pLastParagraphProperties
->IsxValid() )
1274 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT_POSITION
), rAppendContext
.pLastParagraphProperties
->Getx()));
1276 if( rAppendContext
.pLastParagraphProperties
->GethAnchor() >= 0 )
1277 aFrameProperties
.push_back(comphelper::makePropertyValue("HoriOrientRelation", sal_Int16(rAppendContext
.pLastParagraphProperties
->GethAnchor())));
1279 if( rAppendContext
.pLastParagraphProperties
->IsyValid() )
1280 aFrameProperties
.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_POSITION
), rAppendContext
.pLastParagraphProperties
->Gety()));
1282 if( rAppendContext
.pLastParagraphProperties
->GetvAnchor() >= 0 )
1283 aFrameProperties
.push_back(comphelper::makePropertyValue("VertOrientRelation", sal_Int16(rAppendContext
.pLastParagraphProperties
->GetvAnchor())));
1285 if( rAppendContext
.pLastParagraphProperties
->GetWrap() >= text::WrapTextMode_NONE
)
1286 aFrameProperties
.push_back(comphelper::makePropertyValue("Surround", rAppendContext
.pLastParagraphProperties
->GetWrap()));
1288 lcl_MoveBorderPropertiesToFrame(aFrameProperties
,
1289 rAppendContext
.pLastParagraphProperties
->GetStartingRange(),
1290 rAppendContext
.pLastParagraphProperties
->GetEndingRange());
1293 //frame conversion has to be executed after table conversion
1294 RegisterFrameConversion(
1295 rAppendContext
.pLastParagraphProperties
->GetStartingRange(),
1296 rAppendContext
.pLastParagraphProperties
->GetEndingRange(),
1299 catch( const uno::Exception
& )
1304 /// Check if the style or its parent has a list id, recursively.
1305 static sal_Int32
lcl_getListId(const StyleSheetEntryPtr
& rEntry
, const StyleSheetTablePtr
& rStyleTable
, bool & rNumberingFromBaseStyle
)
1307 const StyleSheetPropertyMap
* pEntryProperties
= dynamic_cast<const StyleSheetPropertyMap
*>(rEntry
->pProperties
.get());
1308 if (!pEntryProperties
)
1311 sal_Int32 nListId
= pEntryProperties
->GetListId();
1312 // The style itself has a list id.
1316 // The style has no parent.
1317 if (rEntry
->sBaseStyleIdentifier
.isEmpty())
1320 const StyleSheetEntryPtr pParent
= rStyleTable
->FindStyleSheetByISTD(rEntry
->sBaseStyleIdentifier
);
1321 // No such parent style or loop in the style hierarchy.
1322 if (!pParent
|| pParent
== rEntry
)
1325 rNumberingFromBaseStyle
= true;
1327 return lcl_getListId(pParent
, rStyleTable
, rNumberingFromBaseStyle
);
1330 void DomainMapper_Impl::finishParagraph( const PropertyMapPtr
& pPropertyMap
, const bool bRemove
)
1332 if (m_bDiscardHeaderFooter
)
1335 if (!m_aFieldStack
.empty())
1337 FieldContextPtr pFieldContext
= m_aFieldStack
.back();
1338 if (pFieldContext
&& !pFieldContext
->IsCommandCompleted())
1340 std::vector
<OUString
> aCommandParts
= pFieldContext
->GetCommandParts();
1341 if (!aCommandParts
.empty() && aCommandParts
[0] == "IF")
1343 // Conditional text field conditions don't support linebreaks in Writer.
1348 if (pFieldContext
&& pFieldContext
->IsCommandCompleted())
1350 if (pFieldContext
->GetFieldId() == FIELD_IF
)
1352 // Conditional text fields can't contain newlines, finish the paragraph later.
1353 FieldParagraph aFinish
{pPropertyMap
, bRemove
};
1354 pFieldContext
->GetParagraphsToFinish().push_back(aFinish
);
1361 TagLogger::getInstance().startElement("finishParagraph");
1364 m_nLastTableCellParagraphDepth
= m_nTableCellDepth
;
1365 ParagraphPropertyMap
* pParaContext
= dynamic_cast< ParagraphPropertyMap
* >( pPropertyMap
.get() );
1366 if (m_aTextAppendStack
.empty())
1368 TextAppendContext
& rAppendContext
= m_aTextAppendStack
.top();
1369 uno::Reference
< text::XTextAppend
> xTextAppend(rAppendContext
.xTextAppend
);
1371 TagLogger::getInstance().attribute("isTextAppend", sal_uInt32(xTextAppend
.is()));
1374 const StyleSheetEntryPtr pEntry
= GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( GetCurrentParaStyleName() );
1375 OSL_ENSURE( pEntry
.get(), "no style sheet found" );
1376 const StyleSheetPropertyMap
* pStyleSheetProperties
= dynamic_cast<const StyleSheetPropertyMap
*>(pEntry
? pEntry
->pProperties
.get() : nullptr);
1377 bool isNumberingViaStyle(false);
1378 //apply numbering to paragraph if it was set at the style, but only if the paragraph itself
1379 //does not specify the numbering
1380 if ( !bRemove
&& pStyleSheetProperties
&& pParaContext
&& !pParaContext
->isSet(PROP_NUMBERING_RULES
) )
1382 bool bNumberingFromBaseStyle
= false;
1383 sal_Int32 nListId
= pEntry
? lcl_getListId(pEntry
, GetStyleSheetTable(), bNumberingFromBaseStyle
) : -1;
1384 auto const pList(GetListTable()->GetList(nListId
));
1385 if (pList
&& nListId
>= 0 && !pParaContext
->isSet(PROP_NUMBERING_STYLE_NAME
))
1387 pParaContext
->Insert( PROP_NUMBERING_STYLE_NAME
, uno::makeAny( pList
->GetStyleName() ), false);
1388 isNumberingViaStyle
= true;
1390 // Indent properties from the paragraph style have priority
1391 // over the ones from the numbering styles in Word
1392 // but in Writer numbering styles have priority,
1393 // so insert directly into the paragraph properties to compensate.
1394 std::optional
<PropertyMap::Property
> oProperty
;
1395 const StyleSheetEntryPtr pParent
= (!pEntry
->sBaseStyleIdentifier
.isEmpty()) ? GetStyleSheetTable()->FindStyleSheetByISTD(pEntry
->sBaseStyleIdentifier
) : nullptr;
1396 const StyleSheetPropertyMap
* pParentProperties
= dynamic_cast<const StyleSheetPropertyMap
*>(pParent
? pParent
->pProperties
.get() : nullptr);
1397 if (!pEntry
->sBaseStyleIdentifier
.isEmpty())
1398 if ( (oProperty
= pStyleSheetProperties
->getProperty(PROP_PARA_FIRST_LINE_INDENT
))
1399 // If the numbering comes from a base style, indent of the base style has also priority.
1400 || (bNumberingFromBaseStyle
&& pParentProperties
&& (oProperty
= pParentProperties
->getProperty(PROP_PARA_FIRST_LINE_INDENT
))) )
1401 pParaContext
->Insert(PROP_PARA_FIRST_LINE_INDENT
, oProperty
->second
, /*bOverwrite=*/false);
1402 if ( (oProperty
= pStyleSheetProperties
->getProperty(PROP_PARA_LEFT_MARGIN
))
1403 || (bNumberingFromBaseStyle
&& pParentProperties
&& (oProperty
= pParentProperties
->getProperty(PROP_PARA_LEFT_MARGIN
))) )
1404 pParaContext
->Insert(PROP_PARA_LEFT_MARGIN
, oProperty
->second
, /*bOverwrite=*/false);
1406 // We're inheriting properties from a numbering style. Make sure a possible right margin is inherited from the base style.
1407 sal_Int32 nParaRightMargin
;
1408 if ( pParentProperties
&& (oProperty
= pParentProperties
->getProperty(PROP_PARA_RIGHT_MARGIN
)) && (nParaRightMargin
= oProperty
->second
.get
<sal_Int32
>()) != 0 )
1410 // If we're setting the right margin, we should set the first / left margin as well from the numbering style.
1411 const sal_Int32 nFirstLineIndent
= getNumberingProperty(nListId
, pStyleSheetProperties
->GetListLevel(), "FirstLineIndent");
1412 const sal_Int32 nParaLeftMargin
= getNumberingProperty(nListId
, pStyleSheetProperties
->GetListLevel(), "IndentAt");
1413 if (nFirstLineIndent
!= 0)
1414 pParaContext
->Insert(PROP_PARA_FIRST_LINE_INDENT
, uno::makeAny(nFirstLineIndent
), /*bOverwrite=*/false);
1415 if (nParaLeftMargin
!= 0)
1416 pParaContext
->Insert(PROP_PARA_LEFT_MARGIN
, uno::makeAny(nParaLeftMargin
), /*bOverwrite=*/false);
1418 pParaContext
->Insert(PROP_PARA_RIGHT_MARGIN
, uno::makeAny(nParaRightMargin
), /*bOverwrite=*/false);
1422 if ( pStyleSheetProperties
->GetListLevel() >= 0 )
1423 pParaContext
->Insert( PROP_NUMBERING_LEVEL
, uno::makeAny(pStyleSheetProperties
->GetListLevel()), false);
1426 // apply AutoSpacing: it has priority over all other margin settings
1427 // (note that numbering with autoSpacing is handled separately later on)
1428 const bool bAllowAdjustments
= !GetSettingsTable()->GetDoNotUseHTMLParagraphAutoSpacing();
1429 sal_Int32 nBeforeAutospacing
= -1;
1430 bool bIsAutoSet
= pParaContext
&& pParaContext
->isSet(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING
);
1431 // apply INHERITED autospacing only if top margin is not set
1432 if ( bIsAutoSet
|| (pParaContext
&& !pParaContext
->isSet(PROP_PARA_TOP_MARGIN
)) )
1433 GetAnyProperty(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING
, pPropertyMap
) >>= nBeforeAutospacing
;
1434 if ( nBeforeAutospacing
> -1 && pParaContext
)
1436 if ( bAllowAdjustments
)
1438 if ( GetIsFirstParagraphInShape() ||
1439 (GetIsFirstParagraphInSection() && GetSectionContext() && GetSectionContext()->IsFirstSection()) ||
1440 (m_bFirstParagraphInCell
&& m_nTableDepth
> 0 && m_nTableDepth
== m_nTableCellDepth
) )
1442 nBeforeAutospacing
= 0;
1443 // export requires grabbag to match top_margin, so keep them in sync
1445 pParaContext
->Insert( PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING
, uno::makeAny( sal_Int32(0) ),true, PARA_GRAB_BAG
);
1448 pParaContext
->Insert(PROP_PARA_TOP_MARGIN
, uno::makeAny(nBeforeAutospacing
));
1451 sal_Int32 nAfterAutospacing
= -1;
1452 bIsAutoSet
= pParaContext
&& pParaContext
->isSet(PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING
);
1453 bool bApplyAutospacing
= bIsAutoSet
|| (pParaContext
&& !pParaContext
->isSet(PROP_PARA_BOTTOM_MARGIN
));
1454 if ( bApplyAutospacing
)
1455 GetAnyProperty(PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING
, pPropertyMap
) >>= nAfterAutospacing
;
1456 if ( nAfterAutospacing
> -1 && pParaContext
)
1458 pParaContext
->Insert(PROP_PARA_BOTTOM_MARGIN
, uno::makeAny(nAfterAutospacing
));
1459 bApplyAutospacing
= bAllowAdjustments
;
1462 bApplyAutospacing
= false;
1464 // tell TableManager to reset the bottom margin if it determines that this is the cell's last paragraph.
1465 if ( hasTableManager() && getTableManager().isInCell() )
1466 getTableManager().setCellLastParaAfterAutospacing( bApplyAutospacing
);
1469 if (xTextAppend
.is() && pParaContext
&& hasTableManager() && !getTableManager().isIgnore())
1473 /*the following combinations of previous and current frame settings can occur:
1474 (1) - no old frame and no current frame -> no special action
1475 (2) - no old frame and current DropCap -> save DropCap for later use, don't call finishParagraph
1476 remove character properties of the DropCap?
1477 (3) - no old frame and current Frame -> save Frame for later use
1478 (4) - old DropCap and no current frame -> add DropCap to the properties of the finished paragraph, delete previous setting
1479 (5) - old DropCap and current frame -> add DropCap to the properties of the finished paragraph, save current frame settings
1480 (6) - old Frame and new DropCap -> add old Frame, save DropCap for later use
1481 (7) - old Frame and new same Frame -> continue
1482 (8) - old Frame and new different Frame -> add old Frame, save new Frame for later use
1483 (9) - old Frame and no current frame -> add old Frame, delete previous settings
1485 old _and_ new DropCap must not occur
1489 pParaContext
->IsFrameMode() &&
1490 sal::static_int_cast
<Id
>(pParaContext
->GetDropCap()) != NS_ooxml::LN_Value_doc_ST_DropCap_none
;
1492 style::DropCapFormat aDrop
;
1493 ParagraphPropertiesPtr pToBeSavedProperties
;
1494 bool bKeepLastParagraphProperties
= false;
1497 uno::Reference
<text::XParagraphCursor
> xParaCursor(
1498 xTextAppend
->createTextCursorByRange(xTextAppend
->getEnd()), uno::UNO_QUERY_THROW
);
1500 xParaCursor
->gotoStartOfParagraph( true );
1501 uno::Reference
< beans::XPropertyState
> xParaProperties( xParaCursor
, uno::UNO_QUERY_THROW
);
1502 xParaProperties
->setPropertyToDefault(getPropertyName(PROP_CHAR_ESCAPEMENT
));
1503 xParaProperties
->setPropertyToDefault(getPropertyName(PROP_CHAR_HEIGHT
));
1504 //handles (2) and part of (6)
1505 pToBeSavedProperties
= new ParagraphProperties(*pParaContext
);
1506 sal_Int32 nCount
= xParaCursor
->getString().getLength();
1507 pToBeSavedProperties
->SetDropCapLength(nCount
> 0 && nCount
< 255 ? static_cast<sal_Int8
>(nCount
) : 1);
1509 if( rAppendContext
.pLastParagraphProperties
.get() )
1511 if( sal::static_int_cast
<Id
>(rAppendContext
.pLastParagraphProperties
->GetDropCap()) != NS_ooxml::LN_Value_doc_ST_DropCap_none
)
1513 //handles (4) and part of (5)
1514 //create a DropCap property, add it to the property sequence of finishParagraph
1515 sal_Int32 nLines
= rAppendContext
.pLastParagraphProperties
->GetLines();
1516 aDrop
.Lines
= nLines
> 0 && nLines
< SAL_MAX_INT8
? static_cast<sal_Int8
>(nLines
) : 2;
1517 aDrop
.Count
= rAppendContext
.pLastParagraphProperties
->GetDropCapLength();
1518 sal_Int32 nHSpace
= rAppendContext
.pLastParagraphProperties
->GethSpace();
1519 aDrop
.Distance
= nHSpace
> 0 && nHSpace
< SAL_MAX_INT16
? static_cast<sal_Int16
>(nHSpace
) : 0;
1521 if( pParaContext
->IsFrameMode() )
1522 pToBeSavedProperties
= new ParagraphProperties(*pParaContext
);
1524 else if(*rAppendContext
.pLastParagraphProperties
== *pParaContext
)
1527 rAppendContext
.pLastParagraphProperties
->SetEndingRange(rAppendContext
.xInsertPosition
.is() ? rAppendContext
.xInsertPosition
: xTextAppend
->getEnd());
1528 bKeepLastParagraphProperties
= true;
1532 //handles (8)(9) and completes (6)
1533 CheckUnregisteredFrameConversion( );
1535 // If different frame properties are set on this paragraph, keep them.
1536 if ( !bIsDropCap
&& pParaContext
->IsFrameMode() )
1538 pToBeSavedProperties
= new ParagraphProperties(*pParaContext
);
1539 lcl_AddRangeAndStyle(pToBeSavedProperties
, xTextAppend
, pPropertyMap
, rAppendContext
);
1545 // (1) doesn't need handling
1547 if( !bIsDropCap
&& pParaContext
->IsFrameMode() )
1549 pToBeSavedProperties
= new ParagraphProperties(*pParaContext
);
1550 lcl_AddRangeAndStyle(pToBeSavedProperties
, xTextAppend
, pPropertyMap
, rAppendContext
);
1553 std::vector
<beans::PropertyValue
> aProperties
;
1554 if (pPropertyMap
.get())
1556 aProperties
= comphelper::sequenceToContainer
< std::vector
<beans::PropertyValue
> >(pPropertyMap
->GetPropertyValues());
1558 // TODO: this *should* work for RTF but there are test failures, maybe rtftok doesn't distinguish between formatting for the paragraph marker and for the paragraph as a whole; needs investigation
1559 if (pPropertyMap
.get() && IsOOXMLImport())
1561 // tdf#64222 filter out the "paragraph marker" formatting and
1562 // set it as a separate paragraph property, not a empty hint at
1564 std::vector
<beans::NamedValue
> charProperties
;
1565 for (auto it
= aProperties
.begin(); it
!= aProperties
.end(); )
1567 // this condition isn't ideal but as it happens all
1568 // RES_CHRATR_* have names that start with "Char"
1569 if (it
->Name
.startsWith("Char")
1570 // TODO testParagraphMark *wants* this but it's some effort to create a real SwFormatCharFormat...
1571 && !it
->Name
.startsWith("CharStyleName"))
1573 charProperties
.emplace_back(it
->Name
, it
->Value
);
1574 // as testN793262 demonstrates, font size in rPr must
1575 // affect the paragraph size => also insert empty hint!
1576 // it = aProperties.erase(it);
1580 if (!charProperties
.empty())
1582 aProperties
.push_back(beans::PropertyValue("ListAutoFormat",
1583 0, uno::makeAny(comphelper::containerToSequence(charProperties
)), beans::PropertyState_DIRECT_VALUE
));
1588 if( aDrop
.Lines
> 1 )
1590 beans::PropertyValue aValue
;
1591 aValue
.Name
= getPropertyName(PROP_DROP_CAP_FORMAT
);
1592 aValue
.Value
<<= aDrop
;
1593 aProperties
.push_back(aValue
);
1595 uno::Reference
< text::XTextRange
> xTextRange
;
1596 if (rAppendContext
.xInsertPosition
.is())
1598 xTextRange
= xTextAppend
->finishParagraphInsert( comphelper::containerToSequence(aProperties
), rAppendContext
.xInsertPosition
);
1599 rAppendContext
.xCursor
->gotoNextParagraph(false);
1600 if (rAppendContext
.pLastParagraphProperties
.get())
1601 rAppendContext
.pLastParagraphProperties
->SetEndingRange(xTextRange
->getEnd());
1605 uno::Reference
<text::XTextCursor
> xCursor
;
1606 if (m_bParaHadField
&& !m_bIsInComments
&& !xTOCMarkerCursor
.is())
1608 // Workaround to make sure char props of the field are not lost.
1609 // Not relevant for editeng-based comments.
1610 // Not relevant for fields inside a TOC field.
1611 OUString
const sMarker("X");
1612 xCursor
= xTextAppend
->getText()->createTextCursor();
1614 xCursor
->gotoEnd(false);
1615 PropertyMapPtr
pEmpty(new PropertyMap());
1616 appendTextPortion(sMarker
, pEmpty
);
1619 // Check if top / bottom margin has to be updated, now that we know the numbering status of both the previous and
1620 // the current text node.
1621 auto itNumberingRules
= std::find_if(aProperties
.begin(), aProperties
.end(), [](const beans::PropertyValue
& rValue
)
1623 return rValue
.Name
== "NumberingRules";
1625 if (itNumberingRules
!= aProperties
.end())
1627 // This textnode has numbering. Look up the numbering style name of the current and previous paragraph.
1628 OUString aCurrentNumberingRuleName
;
1629 uno::Reference
<container::XNamed
> xCurrentNumberingRules(itNumberingRules
->Value
, uno::UNO_QUERY
);
1630 if (xCurrentNumberingRules
.is())
1631 aCurrentNumberingRuleName
= xCurrentNumberingRules
->getName();
1632 OUString aPreviousNumberingRuleName
;
1633 if (m_xPreviousParagraph
.is())
1635 uno::Reference
<container::XNamed
> xPreviousNumberingRules(m_xPreviousParagraph
->getPropertyValue("NumberingRules"), uno::UNO_QUERY
);
1636 if (xPreviousNumberingRules
.is())
1637 aPreviousNumberingRuleName
= xPreviousNumberingRules
->getName();
1640 if (!aPreviousNumberingRuleName
.isEmpty() && aCurrentNumberingRuleName
== aPreviousNumberingRuleName
)
1642 // There was a previous textnode and it had the same numbering.
1643 if (m_bParaAutoBefore
)
1645 // This before spacing is set to auto, set before space to 0.
1646 auto itParaTopMargin
= std::find_if(aProperties
.begin(), aProperties
.end(), [](const beans::PropertyValue
& rValue
)
1648 return rValue
.Name
== "ParaTopMargin";
1650 if (itParaTopMargin
!= aProperties
.end())
1651 itParaTopMargin
->Value
<<= static_cast<sal_Int32
>(0);
1653 aProperties
.push_back(comphelper::makePropertyValue("ParaTopMargin", static_cast<sal_Int32
>(0)));
1655 uno::Sequence
<beans::PropertyValue
> aPrevPropertiesSeq
;
1656 m_xPreviousParagraph
->getPropertyValue("ParaInteropGrabBag") >>= aPrevPropertiesSeq
;
1657 auto aPrevProperties
= comphelper::sequenceToContainer
< std::vector
<beans::PropertyValue
> >(aPrevPropertiesSeq
);
1658 bool bPrevParaAutoAfter
= std::any_of(aPrevProperties
.begin(), aPrevProperties
.end(), [](const beans::PropertyValue
& rValue
)
1660 return rValue
.Name
== "ParaBottomMarginAfterAutoSpacing";
1662 if (bPrevParaAutoAfter
)
1664 // Previous after spacing is set to auto, set previous after space to 0.
1665 m_xPreviousParagraph
->setPropertyValue("ParaBottomMargin", uno::makeAny(static_cast<sal_Int32
>(0)));
1670 xTextRange
= xTextAppend
->finishParagraph( comphelper::containerToSequence(aProperties
) );
1671 m_xPreviousParagraph
.set(xTextRange
, uno::UNO_QUERY
);
1673 if (m_xPreviousParagraph
.is() && // null for SvxUnoTextBase
1674 (isNumberingViaStyle
|| itNumberingRules
!= aProperties
.end()))
1676 assert(dynamic_cast<ParagraphPropertyMap
*>(pPropertyMap
.get()));
1677 // Use lcl_getListId(), so we find the list ID in parent styles as well.
1678 bool bNumberingFromBaseStyle
= false;
1679 sal_Int32
const nListId( isNumberingViaStyle
1680 ? lcl_getListId(pEntry
, GetStyleSheetTable(), bNumberingFromBaseStyle
)
1681 : static_cast<ParagraphPropertyMap
*>(pPropertyMap
.get())->GetListId());
1682 if (ListDef::Pointer
const& pList
= m_pListTable
->GetList(nListId
))
1683 { // styles could refer to non-existing lists...
1684 AbstractListDef::Pointer
const& pAbsList
=
1685 pList
->GetAbstractDefinition();
1687 // SvxUnoTextRange doesn't have ListId
1688 m_xPreviousParagraph
->getPropertySetInfo()->hasPropertyByName("ListId"))
1691 m_xPreviousParagraph
->getPropertyValue("ListId") >>= paraId
;
1692 assert(!paraId
.isEmpty()); // must be on some list?
1693 OUString
const listId
= pAbsList
->MapListId(paraId
);
1694 if (listId
!= paraId
)
1696 m_xPreviousParagraph
->setPropertyValue("ListId", uno::makeAny(listId
));
1702 if (!rAppendContext
.m_aAnchoredObjects
.empty() && !IsInHeaderFooter())
1704 // Remember what objects are anchored to this paragraph.
1705 // That list is only used for Word compat purposes, and
1706 // it is only relevant for body text.
1707 AnchoredObjectsInfo aInfo
;
1708 aInfo
.m_xParagraph
= xTextRange
;
1709 aInfo
.m_aAnchoredObjects
= rAppendContext
.m_aAnchoredObjects
;
1710 m_aAnchoredObjectAnchors
.push_back(aInfo
);
1711 rAppendContext
.m_aAnchoredObjects
.clear();
1714 // We're no longer right after a table conversion.
1715 m_bConvertedTable
= false;
1719 xCursor
->goLeft(1, true);
1720 xCursor
->setString(OUString());
1723 getTableManager( ).handle(xTextRange
);
1724 m_aSmartTagHandler
.handle(xTextRange
);
1726 if (xTextRange
.is())
1728 // Get the end of paragraph character inserted
1729 uno::Reference
< text::XTextCursor
> xCur
= xTextRange
->getText( )->createTextCursor( );
1730 if (rAppendContext
.xInsertPosition
.is())
1731 xCur
->gotoRange( rAppendContext
.xInsertPosition
, false );
1733 xCur
->gotoEnd( false );
1735 // tdf#77417 trim right white spaces in table cells in 2010 compatibility mode
1736 sal_Int32 nMode
= GetSettingsTable()->GetWordCompatibilityMode();
1737 if ( m_nTableDepth
> 0 && nMode
> 0 && nMode
<= 14 )
1740 xCur
->goLeft(1, false);
1741 while ( xCur
->goLeft(1, true) )
1743 OUString sChar
= xCur
->getString();
1744 if ( sChar
== " " || sChar
== "\t" || sChar
== OUStringChar(u
'\x00A0') )
1745 xCur
->setString("");
1749 xCur
->goRight(2, false);
1752 xCur
->goLeft( 1 , true );
1753 // Extend the redline ranges for empty paragraphs
1754 if ( !m_bParaChanged
&& m_previousRedline
.get() )
1755 CreateRedline( xCur
, m_previousRedline
);
1756 CheckParaMarkerRedline( xCur
);
1759 css::uno::Reference
<css::beans::XPropertySet
> xParaProps(xTextRange
, uno::UNO_QUERY
);
1761 // table style precedence and not hidden shapes anchored to hidden empty table paragraphs
1762 if (xParaProps
&& (m_nTableDepth
> 0 || !m_aAnchoredObjectAnchors
.empty()) )
1764 // table style has got bigger precedence than docDefault style
1765 // collect these pending paragraph properties to process in endTable()
1766 uno::Reference
<text::XTextCursor
> xCur
= xTextRange
->getText( )->createTextCursor( );
1767 xCur
->gotoEnd(false);
1768 xCur
->goLeft(1, false);
1769 uno::Reference
<text::XTextCursor
> xCur2
= xTextRange
->getText()->createTextCursorByRange(xCur
);
1770 uno::Reference
<text::XParagraphCursor
> xParaCursor(xCur2
, uno::UNO_QUERY_THROW
);
1771 xParaCursor
->gotoStartOfParagraph(false);
1772 if (m_nTableDepth
> 0)
1774 TableParagraph aPending
{xParaCursor
, xCur
, pParaContext
, xParaProps
};
1775 m_aParagraphsToEndTable
.push_back(aPending
);
1778 // hidden empty paragraph with a not hidden shape, set as not hidden
1779 std::optional
<PropertyMap::Property
> pHidden
;
1780 if ( !m_aAnchoredObjectAnchors
.empty() && (pHidden
= pParaContext
->getProperty(PROP_CHAR_HIDDEN
)) )
1782 bool bIsHidden
= {}; // -Werror=maybe-uninitialized
1783 pHidden
->second
>>= bIsHidden
;
1787 pHidden
= GetTopContext()->getProperty(PROP_CHAR_HIDDEN
);
1789 pHidden
->second
>>= bIsHidden
;
1792 uno::Reference
<text::XTextCursor
> xCur3
= xTextRange
->getText()->createTextCursorByRange(xParaCursor
);
1793 xCur3
->goRight(1, true);
1794 if (xCur3
->getString() == SAL_NEWLINE_STRING
)
1796 uno::Reference
< beans::XPropertySet
> xProp( xCur3
, uno::UNO_QUERY
);
1797 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_HIDDEN
), uno::makeAny(false));
1804 // tdf#118521 set paragraph top or bottom margin based on the paragraph style
1805 // if we already set the other margin with direct formatting
1808 const bool bTopSet
= pParaContext
->isSet(PROP_PARA_TOP_MARGIN
);
1809 const bool bBottomSet
= pParaContext
->isSet(PROP_PARA_BOTTOM_MARGIN
);
1810 const bool bContextSet
= pParaContext
->isSet(PROP_PARA_CONTEXT_MARGIN
);
1811 if ( !(bTopSet
== bBottomSet
&& bBottomSet
== bContextSet
) )
1816 uno::Any aMargin
= GetPropertyFromParaStyleSheet(PROP_PARA_TOP_MARGIN
);
1817 if ( aMargin
!= uno::Any() )
1818 xParaProps
->setPropertyValue("ParaTopMargin", aMargin
);
1822 uno::Any aMargin
= GetPropertyFromParaStyleSheet(PROP_PARA_BOTTOM_MARGIN
);
1823 if ( aMargin
!= uno::Any() )
1824 xParaProps
->setPropertyValue("ParaBottomMargin", aMargin
);
1828 uno::Any aMargin
= GetPropertyFromParaStyleSheet(PROP_PARA_CONTEXT_MARGIN
);
1829 if ( aMargin
!= uno::Any() )
1830 xParaProps
->setPropertyValue("ParaContextMargin", aMargin
);
1835 // Left, Right, and Hanging settings are also grouped. Ensure that all or none are set.
1838 const bool bLeftSet
= pParaContext
->isSet(PROP_PARA_LEFT_MARGIN
);
1839 const bool bRightSet
= pParaContext
->isSet(PROP_PARA_RIGHT_MARGIN
);
1840 const bool bFirstSet
= pParaContext
->isSet(PROP_PARA_FIRST_LINE_INDENT
);
1841 if ( !(bLeftSet
== bRightSet
&& bRightSet
== bFirstSet
) )
1845 uno::Any aMargin
= GetPropertyFromParaStyleSheet(PROP_PARA_LEFT_MARGIN
);
1846 if ( aMargin
!= uno::Any() )
1847 xParaProps
->setPropertyValue("ParaLeftMargin", aMargin
);
1851 uno::Any aMargin
= GetPropertyFromParaStyleSheet(PROP_PARA_RIGHT_MARGIN
);
1852 if ( aMargin
!= uno::Any() )
1853 xParaProps
->setPropertyValue("ParaRightMargin", aMargin
);
1857 uno::Any aMargin
= GetPropertyFromParaStyleSheet(PROP_PARA_FIRST_LINE_INDENT
);
1858 if ( aMargin
!= uno::Any() )
1859 xParaProps
->setPropertyValue("ParaFirstLineIndent", aMargin
);
1864 // fix table paragraph properties
1865 if ( xTextRange
.is() && xParaProps
&& m_nTableDepth
> 0 )
1867 uno::Sequence
< beans::PropertyValue
> aParaProps
= pParaContext
->GetPropertyValues(false);
1868 uno::Reference
<text::XTextCursor
> xCur
= xTextRange
->getText()->createTextCursorByRange(xTextRange
);
1869 uno::Reference
< beans::XPropertyState
> xRunProperties( xCur
, uno::UNO_QUERY_THROW
);
1870 // tdf#90069 in tables, apply paragraph level character style also on
1871 // paragraph level to support its copy during insertion of new table rows
1872 for( const auto& rParaProp
: std::as_const(aParaProps
) )
1874 if ( rParaProp
.Name
.startsWith("Char") && rParaProp
.Name
!= "CharStyleName" && rParaProp
.Name
!= "CharInteropGrabBag" &&
1875 // all text portions contain the same value, so next setPropertyValue() won't overwrite part of them
1876 xRunProperties
->getPropertyState(rParaProp
.Name
) == css::beans::PropertyState_DIRECT_VALUE
)
1878 uno::Reference
<beans::XPropertySet
> xRunPropertySet(xCur
, uno::UNO_QUERY
);
1879 xParaProps
->setPropertyValue( rParaProp
.Name
, xRunPropertySet
->getPropertyValue(rParaProp
.Name
) );
1883 // tdf#128959 table paragraphs haven't got window and orphan controls
1884 uno::Any aAny
= uno::makeAny(static_cast<sal_Int8
>(0));
1885 xParaProps
->setPropertyValue("ParaOrphans", aAny
);
1886 xParaProps
->setPropertyValue("ParaWidows", aAny
);
1889 if( !bKeepLastParagraphProperties
)
1890 rAppendContext
.pLastParagraphProperties
= pToBeSavedProperties
;
1892 catch(const lang::IllegalArgumentException
&)
1894 OSL_FAIL( "IllegalArgumentException in DomainMapper_Impl::finishParagraph" );
1896 catch(const uno::Exception
&)
1898 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "finishParagraph()" );
1903 bool bIgnoreFrameState
= IsInHeaderFooter();
1904 if( (!bIgnoreFrameState
&& pParaContext
&& pParaContext
->IsFrameMode()) || (bIgnoreFrameState
&& GetIsPreviousParagraphFramed()) )
1905 SetIsPreviousParagraphFramed(true);
1907 SetIsPreviousParagraphFramed(false);
1909 m_bParaChanged
= false;
1910 m_bRemoveThisParagraph
= false;
1911 if( !IsInHeaderFooter() && !IsInShape() && (!pParaContext
|| !pParaContext
->IsFrameMode()) )
1912 { // If the paragraph is in a frame, shape or header/footer, it's not a paragraph of the section itself.
1913 SetIsFirstParagraphInSection(false);
1914 // count first not deleted paragraph as first paragraph in section to avoid of
1915 // its deletion later, resulting loss of the associated page break
1916 if (!m_previousRedline
.get())
1918 SetIsFirstParagraphInSectionAfterRedline(false);
1919 SetIsLastParagraphInSection(false);
1922 m_previousRedline
.clear();
1924 if (m_bIsFirstParaInShape
)
1925 m_bIsFirstParaInShape
= false;
1929 // Reset the frame properties for the next paragraph
1930 pParaContext
->ResetFrameProperties();
1933 SetIsOutsideAParagraph(true);
1934 m_bParaHadField
= false;
1936 // don't overwrite m_bFirstParagraphInCell in table separator nodes
1937 if (m_nTableDepth
> 0 && m_nTableDepth
== m_nTableCellDepth
)
1938 m_bFirstParagraphInCell
= false;
1940 m_bParaAutoBefore
= false;
1943 TagLogger::getInstance().endElement();
1948 void DomainMapper_Impl::appendTextPortion( const OUString
& rString
, const PropertyMapPtr
& pPropertyMap
)
1950 if (m_bDiscardHeaderFooter
)
1953 if (m_aTextAppendStack
.empty())
1955 // Before placing call to processDeferredCharacterProperties(), TopContextType should be CONTEXT_CHARACTER
1956 // processDeferredCharacterProperties() invokes only if character inserted
1957 if( pPropertyMap
== m_pTopContext
&& !deferredCharacterProperties
.empty() && (GetTopContextType() == CONTEXT_CHARACTER
) )
1958 processDeferredCharacterProperties();
1959 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
1960 if (xTextAppend
.is() && hasTableManager() && !getTableManager().isIgnore())
1964 // If we are in comments, then disable CharGrabBag, comment text doesn't support that.
1965 uno::Sequence
< beans::PropertyValue
> aValues
= pPropertyMap
->GetPropertyValues(/*bCharGrabBag=*/!m_bIsInComments
);
1967 if (m_bStartTOC
|| m_bStartIndex
|| m_bStartBibliography
)
1968 for( auto& rValue
: aValues
)
1970 if (rValue
.Name
== "CharHidden")
1971 rValue
.Value
<<= false;
1974 uno::Reference
< text::XTextRange
> xTextRange
;
1975 if (m_aTextAppendStack
.top().xInsertPosition
.is())
1977 xTextRange
= xTextAppend
->insertTextPortion(rString
, aValues
, m_aTextAppendStack
.top().xInsertPosition
);
1978 m_aTextAppendStack
.top().xCursor
->gotoRange(xTextRange
->getEnd(), true);
1982 if (m_bStartTOC
|| m_bStartIndex
|| m_bStartBibliography
|| m_nStartGenericField
!= 0)
1984 if (IsInHeaderFooter() && !m_bStartTOCHeaderFooter
)
1986 xTextRange
= xTextAppend
->appendTextPortion(rString
, aValues
);
1990 m_bStartedTOC
= true;
1991 uno::Reference
< text::XTextCursor
> xTOCTextCursor
= xTextAppend
->getEnd()->getText( )->createTextCursor( );
1992 xTOCTextCursor
->gotoEnd(false);
1993 if (m_nStartGenericField
!= 0)
1995 xTOCTextCursor
->goLeft(1, false);
1997 xTextRange
= xTextAppend
->insertTextPortion(rString
, aValues
, xTOCTextCursor
);
1998 SAL_WARN_IF(!xTextRange
.is(), "writerfilter.dmapper", "insertTextPortion failed");
1999 if (!xTextRange
.is())
2000 throw uno::Exception("insertTextPortion failed", nullptr);
2001 m_bTextInserted
= true;
2002 xTOCTextCursor
->gotoRange(xTextRange
->getEnd(), true);
2003 if (m_nStartGenericField
== 0)
2005 m_aTextAppendStack
.push(TextAppendContext(xTextAppend
, xTOCTextCursor
));
2011 #if !defined(MACOSX) // TODO: check layout differences and support all platforms, if needed
2014 OUString
sDoubleSpace(" ");
2015 PropertyMapPtr pContext
= GetTopContextOfType(CONTEXT_CHARACTER
);
2016 // tdf#123703 workaround for longer space sequences of the old or compatible RTF documents
2017 if (GetSettingsTable()->GetLongerSpaceSequence() && !IsOpenFieldCommand() && (nPos
= rString
.indexOf(sDoubleSpace
)) != -1 &&
2018 // monospaced fonts have no longer space sequences, regardless of \fprq2 (not monospaced) font setting
2019 // fix for the base monospaced font Courier
2020 (!pContext
|| !pContext
->isSet(PROP_CHAR_FONT_NAME
) ||
2021 ((pContext
->getProperty(PROP_CHAR_FONT_NAME
)->second
>>= sFontName
) && sFontName
.indexOf("Courier") == -1)))
2023 // an RTF space character is longer by an extra six-em-space in an old-style RTF space sequence,
2024 // insert them to keep RTF document layout formatted by consecutive spaces
2025 const sal_Unicode aExtraSpace
[5] = { 0x2006, 0x20, 0x2006, 0x20, 0 };
2026 const sal_Unicode aExtraSpace2
[4] = { 0x20, 0x2006, 0x20, 0 };
2027 xTextRange
= xTextAppend
->appendTextPortion(rString
.replaceAll(sDoubleSpace
, aExtraSpace
, nPos
)
2028 .replaceAll(sDoubleSpace
, aExtraSpace2
, nPos
), aValues
);
2032 xTextRange
= xTextAppend
->appendTextPortion(rString
, aValues
);
2036 // reset moveFrom data of non-terminating runs of the paragraph
2037 if ( m_pParaMarkerRedlineMoveFrom
.get( ) )
2039 m_pParaMarkerRedlineMoveFrom
.clear();
2041 CheckRedline( xTextRange
);
2042 m_bParaChanged
= true;
2044 //getTableManager( ).handle(xTextRange);
2046 catch(const lang::IllegalArgumentException
&)
2048 OSL_FAIL( "IllegalArgumentException in DomainMapper_Impl::appendTextPortion" );
2050 catch(const uno::Exception
&)
2052 OSL_FAIL( "Exception in DomainMapper_Impl::appendTextPortion" );
2057 void DomainMapper_Impl::appendTextContent(
2058 const uno::Reference
< text::XTextContent
>& xContent
,
2059 const uno::Sequence
< beans::PropertyValue
>& xPropertyValues
2062 SAL_WARN_IF(m_aTextAppendStack
.empty(), "writerfilter.dmapper", "no text append stack");
2063 if (m_aTextAppendStack
.empty())
2065 uno::Reference
< text::XTextAppendAndConvert
> xTextAppendAndConvert( m_aTextAppendStack
.top().xTextAppend
, uno::UNO_QUERY
);
2066 OSL_ENSURE( xTextAppendAndConvert
.is(), "trying to append a text content without XTextAppendAndConvert" );
2067 if (xTextAppendAndConvert
.is() && hasTableManager() && !getTableManager().isIgnore())
2071 if (m_aTextAppendStack
.top().xInsertPosition
.is())
2072 xTextAppendAndConvert
->insertTextContentWithProperties( xContent
, xPropertyValues
, m_aTextAppendStack
.top().xInsertPosition
);
2074 xTextAppendAndConvert
->appendTextContent( xContent
, xPropertyValues
);
2076 catch(const lang::IllegalArgumentException
&)
2079 catch(const uno::Exception
&)
2085 void DomainMapper_Impl::appendOLE( const OUString
& rStreamName
, const std::shared_ptr
<OLEHandler
>& pOLEHandler
)
2089 uno::Reference
< text::XTextContent
> xOLE( m_xTextFactory
->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW
);
2090 uno::Reference
< beans::XPropertySet
> xOLEProperties(xOLE
, uno::UNO_QUERY_THROW
);
2092 OUString aCLSID
= pOLEHandler
->getCLSID(m_xComponentContext
);
2093 if (aCLSID
.isEmpty())
2094 xOLEProperties
->setPropertyValue(getPropertyName( PROP_STREAM_NAME
),
2095 uno::makeAny( rStreamName
));
2097 xOLEProperties
->setPropertyValue("CLSID", uno::makeAny(aCLSID
));
2099 OUString aDrawAspect
= pOLEHandler
->GetDrawAspect();
2100 if(!aDrawAspect
.isEmpty())
2101 xOLEProperties
->setPropertyValue("DrawAspect", uno::makeAny(aDrawAspect
));
2103 awt::Size aSize
= pOLEHandler
->getSize();
2107 aSize
.Height
= 1000;
2108 xOLEProperties
->setPropertyValue(getPropertyName( PROP_WIDTH
),
2109 uno::makeAny(aSize
.Width
));
2110 xOLEProperties
->setPropertyValue(getPropertyName( PROP_HEIGHT
),
2111 uno::makeAny(aSize
.Height
));
2113 OUString aVisAreaWidth
= pOLEHandler
->GetVisAreaWidth();
2114 if(!aVisAreaWidth
.isEmpty())
2115 xOLEProperties
->setPropertyValue("VisibleAreaWidth", uno::makeAny(aVisAreaWidth
));
2117 OUString aVisAreaHeight
= pOLEHandler
->GetVisAreaHeight();
2118 if(!aVisAreaHeight
.isEmpty())
2119 xOLEProperties
->setPropertyValue("VisibleAreaHeight", uno::makeAny(aVisAreaHeight
));
2121 uno::Reference
< graphic::XGraphic
> xGraphic
= pOLEHandler
->getReplacement();
2122 xOLEProperties
->setPropertyValue(getPropertyName( PROP_GRAPHIC
),
2123 uno::makeAny(xGraphic
));
2124 uno::Reference
<beans::XPropertySet
> xReplacementProperties(pOLEHandler
->getShape(), uno::UNO_QUERY
);
2125 if (xReplacementProperties
.is())
2127 OUString pProperties
[] = {
2128 OUString("AnchorType"),
2129 OUString("Surround"),
2130 OUString("HoriOrient"),
2131 OUString("HoriOrientPosition"),
2132 OUString("VertOrient"),
2133 OUString("VertOrientPosition")
2135 for (const OUString
& s
: pProperties
)
2136 xOLEProperties
->setPropertyValue(s
, xReplacementProperties
->getPropertyValue(s
));
2139 // mimic the treatment of graphics here... it seems anchoring as character
2140 // gives a better ( visually ) result
2141 xOLEProperties
->setPropertyValue(getPropertyName( PROP_ANCHOR_TYPE
), uno::makeAny( text::TextContentAnchorType_AS_CHARACTER
) );
2142 // remove ( if valid ) associated shape ( used for graphic replacement )
2143 SAL_WARN_IF(m_aAnchoredStack
.empty(), "writerfilter.dmapper", "no anchor stack");
2144 if (!m_aAnchoredStack
.empty())
2145 m_aAnchoredStack
.top( ).bToRemove
= true;
2146 RemoveLastParagraph();
2147 SAL_WARN_IF(m_aTextAppendStack
.empty(), "writerfilter.dmapper", "no text stack");
2148 if (!m_aTextAppendStack
.empty())
2149 m_aTextAppendStack
.pop();
2151 appendTextContent( xOLE
, uno::Sequence
< beans::PropertyValue
>() );
2153 if (!aCLSID
.isEmpty())
2154 pOLEHandler
->importStream(m_xComponentContext
, GetTextDocument(), xOLE
);
2157 catch( const uno::Exception
& )
2159 OSL_FAIL( "Exception in creation of OLE object" );
2164 void DomainMapper_Impl::appendStarMath( const Value
& val
)
2166 uno::Reference
< embed::XEmbeddedObject
> formula
;
2167 val
.getAny() >>= formula
;
2172 uno::Reference
< text::XTextContent
> xStarMath( m_xTextFactory
->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW
);
2173 uno::Reference
< beans::XPropertySet
> xStarMathProperties(xStarMath
, uno::UNO_QUERY_THROW
);
2175 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_EMBEDDED_OBJECT
),
2177 // tdf#66405: set zero margins for embedded object
2178 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN
),
2179 uno::makeAny(sal_Int32(0)));
2180 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN
),
2181 uno::makeAny(sal_Int32(0)));
2182 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_TOP_MARGIN
),
2183 uno::makeAny(sal_Int32(0)));
2184 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN
),
2185 uno::makeAny(sal_Int32(0)));
2187 uno::Reference
< uno::XInterface
> xInterface( formula
->getComponent(), uno::UNO_QUERY
);
2188 // set zero margins for object's component
2189 uno::Reference
< beans::XPropertySet
> xComponentProperties( xInterface
, uno::UNO_QUERY_THROW
);
2190 xComponentProperties
->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN
),
2191 uno::makeAny(sal_Int32(0)));
2192 xComponentProperties
->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN
),
2193 uno::makeAny(sal_Int32(0)));
2194 xComponentProperties
->setPropertyValue(getPropertyName( PROP_TOP_MARGIN
),
2195 uno::makeAny(sal_Int32(0)));
2196 xComponentProperties
->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN
),
2197 uno::makeAny(sal_Int32(0)));
2198 Size
size( 1000, 1000 );
2199 if( oox::FormulaImportBase
* formulaimport
= dynamic_cast< oox::FormulaImportBase
* >( xInterface
.get()))
2200 size
= formulaimport
->getFormulaSize();
2201 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_WIDTH
),
2202 uno::makeAny( sal_Int32(size
.Width())));
2203 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_HEIGHT
),
2204 uno::makeAny( sal_Int32(size
.Height())));
2205 // mimic the treatment of graphics here... it seems anchoring as character
2206 // gives a better ( visually ) result
2207 xStarMathProperties
->setPropertyValue(getPropertyName( PROP_ANCHOR_TYPE
),
2208 uno::makeAny( text::TextContentAnchorType_AS_CHARACTER
) );
2209 appendTextContent( xStarMath
, uno::Sequence
< beans::PropertyValue
>() );
2211 catch( const uno::Exception
& )
2213 OSL_FAIL( "Exception in creation of StarMath object" );
2218 uno::Reference
< beans::XPropertySet
> DomainMapper_Impl::appendTextSectionAfter(
2219 uno::Reference
< text::XTextRange
> const & xBefore
)
2221 uno::Reference
< beans::XPropertySet
> xRet
;
2222 if (m_aTextAppendStack
.empty())
2224 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
2225 if(xTextAppend
.is())
2229 uno::Reference
< text::XParagraphCursor
> xCursor(
2230 xTextAppend
->createTextCursorByRange( xBefore
), uno::UNO_QUERY_THROW
);
2231 //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
2232 xCursor
->gotoStartOfParagraph( false );
2233 if (m_aTextAppendStack
.top().xInsertPosition
.is())
2234 xCursor
->gotoRange( m_aTextAppendStack
.top().xInsertPosition
, true );
2236 xCursor
->gotoEnd( true );
2237 //the paragraph after this new section is already inserted
2238 xCursor
->goLeft(1, true);
2239 css::uno::Reference
<css::text::XTextRange
> xTextRange(xCursor
, css::uno::UNO_QUERY_THROW
);
2241 if (css::uno::Reference
<css::text::XDocumentIndexesSupplier
> xIndexSupplier
{
2242 GetTextDocument(), css::uno::UNO_QUERY
})
2244 css::uno::Reference
<css::text::XTextRangeCompare
> xCompare(
2245 xTextAppend
, css::uno::UNO_QUERY
);
2246 const auto xIndexAccess
= xIndexSupplier
->getDocumentIndexes();
2247 for (sal_Int32 i
= xIndexAccess
->getCount(); i
> 0; --i
)
2249 if (css::uno::Reference
<css::text::XDocumentIndex
> xIndex
{
2250 xIndexAccess
->getByIndex(i
- 1), css::uno::UNO_QUERY
})
2252 const auto xIndexTextRange
= xIndex
->getAnchor();
2253 if (xCompare
->compareRegionStarts(xTextRange
, xIndexTextRange
) == 0
2254 && xCompare
->compareRegionEnds(xTextRange
, xIndexTextRange
) == 0)
2256 // The boundaries coincide with an index: trying to attach a section
2257 // to the range will insert the section inside the index. goRight will
2258 // extend the range outside of the index, so that created section will
2259 // be around it. Alternatively we could return index section itself
2260 // instead : xRet.set(xIndex, uno::UNO_QUERY) - to set its properties,
2261 // like columns/fill.
2262 xCursor
->goRight(1, true);
2269 uno::Reference
< text::XTextContent
> xSection( m_xTextFactory
->createInstance("com.sun.star.text.TextSection"), uno::UNO_QUERY_THROW
);
2270 xSection
->attach( xTextRange
);
2271 xRet
.set(xSection
, uno::UNO_QUERY
);
2273 catch(const uno::Exception
&)
2282 void DomainMapper_Impl::appendGlossaryEntry()
2284 appendTextSectionAfter(m_xGlossaryEntryStart
);
2287 void DomainMapper_Impl::PushPageHeaderFooter(bool bHeader
, SectionPropertyMap::PageType eType
)
2289 m_aHeaderFooterStack
.push(HeaderFooterContext(m_bTextInserted
, m_nTableDepth
));
2290 m_bTextInserted
= false;
2293 const PropertyIds ePropIsOn
= bHeader
? PROP_HEADER_IS_ON
: PROP_FOOTER_IS_ON
;
2294 const PropertyIds ePropShared
= bHeader
? PROP_HEADER_IS_SHARED
: PROP_FOOTER_IS_SHARED
;
2295 const PropertyIds ePropTextLeft
= bHeader
? PROP_HEADER_TEXT_LEFT
: PROP_FOOTER_TEXT_LEFT
;
2296 const PropertyIds ePropText
= bHeader
? PROP_HEADER_TEXT
: PROP_FOOTER_TEXT
;
2298 m_eInHeaderFooterImport
2299 = bHeader
? HeaderFooterImportState::header
: HeaderFooterImportState::footer
;
2301 //get the section context
2302 PropertyMapPtr pContext
= DomainMapper_Impl::GetTopContextOfType(CONTEXT_SECTION
);
2303 //ask for the header/footer name of the given type
2304 SectionPropertyMap
* pSectionContext
= dynamic_cast< SectionPropertyMap
* >( pContext
.get() );
2307 // clear the "Link To Previous" flag so that the header/footer
2308 // content is not copied from the previous section
2309 pSectionContext
->ClearHeaderFooterLinkToPrevious(bHeader
, eType
);
2311 uno::Reference
< beans::XPropertySet
> xPageStyle
=
2312 pSectionContext
->GetPageStyle(
2314 eType
== SectionPropertyMap::PAGE_FIRST
);
2315 if (!xPageStyle
.is())
2319 bool bLeft
= eType
== SectionPropertyMap::PAGE_LEFT
;
2320 bool bFirst
= eType
== SectionPropertyMap::PAGE_FIRST
;
2321 if ((!bLeft
&& !GetSettingsTable()->GetEvenAndOddHeaders()) || (GetSettingsTable()->GetEvenAndOddHeaders()))
2323 //switch on header/footer use
2324 xPageStyle
->setPropertyValue(
2325 getPropertyName(ePropIsOn
),
2326 uno::makeAny(true));
2328 // If the 'Different Even & Odd Pages' flag is turned on - do not ignore it
2329 // Even if the 'Even' header/footer is blank - the flag should be imported (so it would look in LO like in Word)
2330 if (!bFirst
&& GetSettingsTable()->GetEvenAndOddHeaders())
2331 xPageStyle
->setPropertyValue(getPropertyName(ePropShared
), uno::makeAny(false));
2334 uno::Reference
< text::XText
> xText
;
2335 xPageStyle
->getPropertyValue(getPropertyName(bLeft
? ePropTextLeft
: ePropText
)) >>= xText
;
2337 m_aTextAppendStack
.push(TextAppendContext(uno::Reference
< text::XTextAppend
>(xText
, uno::UNO_QUERY_THROW
),
2338 m_bIsNewDoc
? uno::Reference
<text::XTextCursor
>(): m_xBodyText
->createTextCursorByRange(xText
->getStart())));
2342 m_bDiscardHeaderFooter
= true;
2345 catch( const uno::Exception
& )
2351 void DomainMapper_Impl::PushPageHeader(SectionPropertyMap::PageType eType
)
2353 PushPageHeaderFooter(/* bHeader = */ true, eType
);
2356 void DomainMapper_Impl::PushPageFooter(SectionPropertyMap::PageType eType
)
2358 PushPageHeaderFooter(/* bHeader = */ false, eType
);
2361 void DomainMapper_Impl::PopPageHeaderFooter()
2363 //header and footer always have an empty paragraph at the end
2364 //this has to be removed
2365 RemoveLastParagraph( );
2367 if (!m_aTextAppendStack
.empty())
2369 if (!m_bDiscardHeaderFooter
)
2371 m_aTextAppendStack
.pop();
2373 m_bDiscardHeaderFooter
= false;
2375 m_eInHeaderFooterImport
= HeaderFooterImportState::none
;
2377 if (!m_aHeaderFooterStack
.empty())
2379 m_bTextInserted
= m_aHeaderFooterStack
.top().getTextInserted();
2380 m_nTableDepth
= m_aHeaderFooterStack
.top().getTableDepth();
2381 m_aHeaderFooterStack
.pop();
2385 void DomainMapper_Impl::PushFootOrEndnote( bool bIsFootnote
)
2387 SAL_WARN_IF(m_bInFootOrEndnote
, "writerfilter.dmapper", "PushFootOrEndnote() is called from another foot or endnote");
2388 m_bInFootOrEndnote
= true;
2389 m_bCheckFirstFootnoteTab
= true;
2390 m_bSaveFirstParagraphInCell
= m_bFirstParagraphInCell
;
2393 // Redlines outside the footnote should not affect footnote content
2394 m_aRedlines
.push(std::vector
< RedlineParamsPtr
>());
2396 // IMHO character styles from footnote labels should be ignored in the edit view of Writer.
2397 // This adds a hack on top of the following hack to save the style name in the context.
2398 PropertyMapPtr pTopContext
= GetTopContext();
2399 OUString sFootnoteCharStyleName
;
2400 std::optional
< PropertyMap::Property
> aProp
= pTopContext
->getProperty(PROP_CHAR_STYLE_NAME
);
2402 aProp
->second
>>= sFootnoteCharStyleName
;
2404 // Remove style reference, if any. This reference did appear here as a side effect of tdf#43017
2405 // Seems it is not required by LO, but causes side effects during editing. So remove it
2406 // for footnotes/endnotes to restore original LO behavior here.
2407 pTopContext
->Erase(PROP_CHAR_STYLE_NAME
);
2409 uno::Reference
< text::XText
> xFootnoteText
;
2410 if (GetTextFactory().is())
2411 xFootnoteText
.set( GetTextFactory()->createInstance(
2413 OUString( "com.sun.star.text.Footnote" ) : OUString( "com.sun.star.text.Endnote" )),
2414 uno::UNO_QUERY_THROW
);
2415 uno::Reference
< text::XFootnote
> xFootnote( xFootnoteText
, uno::UNO_QUERY_THROW
);
2416 pTopContext
->SetFootnote(xFootnote
, sFootnoteCharStyleName
);
2417 uno::Sequence
< beans::PropertyValue
> aFontProperties
= pTopContext
->GetPropertyValues();
2418 appendTextContent( uno::Reference
< text::XTextContent
>( xFootnoteText
, uno::UNO_QUERY_THROW
), aFontProperties
);
2419 m_aTextAppendStack
.push(TextAppendContext(uno::Reference
< text::XTextAppend
>( xFootnoteText
, uno::UNO_QUERY_THROW
),
2420 xFootnoteText
->createTextCursorByRange(xFootnoteText
->getStart())));
2422 // Redlines for the footnote anchor in the main text content
2423 std::vector
< RedlineParamsPtr
> aFootnoteRedline
= std::move(m_aRedlines
.top());
2425 CheckRedline( xFootnote
->getAnchor( ) );
2426 m_aRedlines
.push( aFootnoteRedline
);
2428 // Try scanning for custom footnote labels
2429 if (!sFootnoteCharStyleName
.isEmpty())
2430 StartCustomFootnote(pTopContext
);
2432 EndCustomFootnote();
2434 catch( const uno::Exception
& )
2436 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "PushFootOrEndnote");
2440 void DomainMapper_Impl::CreateRedline(uno::Reference
<text::XTextRange
> const& xRange
,
2441 const RedlineParamsPtr
& pRedline
)
2443 if ( pRedline
.get( ) )
2448 switch ( pRedline
->m_nToken
& 0xffff )
2451 sType
= getPropertyName( PROP_FORMAT
);
2455 sType
= getPropertyName( PROP_INSERT
);
2458 m_pParaMarkerRedlineMoveFrom
= pRedline
.get();
2461 sType
= getPropertyName( PROP_DELETE
);
2463 case XML_ParagraphFormat
:
2464 sType
= getPropertyName( PROP_PARAGRAPH_FORMAT
);
2467 throw lang::IllegalArgumentException("illegal redline token type", nullptr, 0);
2469 beans::PropertyValues
aRedlineProperties( 3 );
2470 beans::PropertyValue
* pRedlineProperties
= aRedlineProperties
.getArray( );
2471 pRedlineProperties
[0].Name
= getPropertyName( PROP_REDLINE_AUTHOR
);
2472 pRedlineProperties
[0].Value
<<= pRedline
->m_sAuthor
;
2473 pRedlineProperties
[1].Name
= getPropertyName( PROP_REDLINE_DATE_TIME
);
2474 pRedlineProperties
[1].Value
<<= ConversionHelper::ConvertDateStringToDateTime( pRedline
->m_sDate
);
2475 pRedlineProperties
[2].Name
= getPropertyName( PROP_REDLINE_REVERT_PROPERTIES
);
2476 pRedlineProperties
[2].Value
<<= pRedline
->m_aRevertProperties
;
2477 if (!m_bIsActualParagraphFramed
)
2479 uno::Reference
< text::XRedline
> xRedline( xRange
, uno::UNO_QUERY_THROW
);
2480 xRedline
->makeRedline( sType
, aRedlineProperties
);
2484 aFramedRedlines
.push_back( uno::makeAny(xRange
) );
2485 aFramedRedlines
.push_back( uno::makeAny(sType
) );
2486 aFramedRedlines
.push_back( uno::makeAny(aRedlineProperties
) );
2489 catch( const uno::Exception
& )
2491 OSL_FAIL( "Exception in makeRedline" );
2496 void DomainMapper_Impl::CheckParaMarkerRedline( uno::Reference
< text::XTextRange
> const& xRange
)
2498 if ( m_pParaMarkerRedline
.get( ) )
2500 CreateRedline( xRange
, m_pParaMarkerRedline
);
2501 if ( m_pParaMarkerRedline
.get( ) )
2503 m_pParaMarkerRedline
.clear();
2504 m_currentRedline
.clear();
2507 else if ( m_pParaMarkerRedlineMoveFrom
.get( ) )
2509 // terminating moveFrom redline removes also the paragraph mark
2510 m_pParaMarkerRedlineMoveFrom
->m_nToken
= XML_del
;
2511 CreateRedline( xRange
, m_pParaMarkerRedlineMoveFrom
);
2513 if ( m_pParaMarkerRedlineMoveFrom
.get( ) )
2515 m_pParaMarkerRedlineMoveFrom
.clear();
2519 void DomainMapper_Impl::CheckRedline( uno::Reference
< text::XTextRange
> const& xRange
)
2521 // Writer core "officially" does not like overlapping redlines, and its UNO interface is stupid enough
2522 // to not prevent that. However, in practice in fact everything appears to work fine (except for the debug warnings
2523 // about redline table corruption, which may possibly be harmless in reality). So leave this as it is, since this
2524 // is a better representation of how the changes happened. If this will ever become a problem, overlapping redlines
2525 // will need to be merged into one, just like doing the changes in the UI does, which will lose some information
2526 // (and so if that happens, it may be better to fix Writer).
2527 // Create the redlines here from lowest (formats) to highest (inserts/removals) priority, since the last one is
2528 // what Writer presents graphically, so this will show deletes as deleted text and not as just formatted text being there.
2529 bool bUsedRange
= m_aRedlines
.top().size() > 0 || (GetTopContextOfType(CONTEXT_CHARACTER
) &&
2530 GetTopContextOfType(CONTEXT_CHARACTER
)->Redlines().size() > 0);
2532 // only export ParagraphFormat, when there is no other redline in the same text portion to avoid missing redline compression,
2533 // but always export the first ParagraphFormat redline in a paragraph to keep the paragraph style change data for rejection
2534 if( (!bUsedRange
|| !m_bParaChanged
) && GetTopContextOfType(CONTEXT_PARAGRAPH
) )
2536 std::vector
<RedlineParamsPtr
>& avRedLines
= GetTopContextOfType(CONTEXT_PARAGRAPH
)->Redlines();
2537 for( const auto& rRedline
: avRedLines
)
2538 CreateRedline( xRange
, rRedline
);
2540 if( GetTopContextOfType(CONTEXT_CHARACTER
) )
2542 std::vector
<RedlineParamsPtr
>& avRedLines
= GetTopContextOfType(CONTEXT_CHARACTER
)->Redlines();
2543 for( const auto& rRedline
: avRedLines
)
2544 CreateRedline( xRange
, rRedline
);
2546 for (const auto& rRedline
: m_aRedlines
.top() )
2547 CreateRedline( xRange
, rRedline
);
2550 void DomainMapper_Impl::StartParaMarkerChange( )
2552 m_bIsParaMarkerChange
= true;
2555 void DomainMapper_Impl::EndParaMarkerChange( )
2557 m_bIsParaMarkerChange
= false;
2558 m_previousRedline
= m_currentRedline
;
2559 m_currentRedline
.clear();
2562 void DomainMapper_Impl::StartCustomFootnote(const PropertyMapPtr pContext
)
2564 if (pContext
== m_pFootnoteContext
)
2567 assert(pContext
->GetFootnote().is());
2568 m_bHasFootnoteStyle
= true;
2569 m_bCheckFootnoteStyle
= !pContext
->GetFootnoteStyle().isEmpty();
2570 m_pFootnoteContext
= pContext
;
2573 void DomainMapper_Impl::EndCustomFootnote()
2575 m_bHasFootnoteStyle
= false;
2576 m_bCheckFootnoteStyle
= false;
2579 void DomainMapper_Impl::PushAnnotation()
2583 m_bIsInComments
= true;
2584 if (!GetTextFactory().is())
2586 m_xAnnotationField
.set( GetTextFactory()->createInstance( "com.sun.star.text.TextField.Annotation" ),
2587 uno::UNO_QUERY_THROW
);
2588 uno::Reference
< text::XText
> xAnnotationText
;
2589 m_xAnnotationField
->getPropertyValue("TextRange") >>= xAnnotationText
;
2590 m_aTextAppendStack
.push(TextAppendContext(uno::Reference
< text::XTextAppend
>( xAnnotationText
, uno::UNO_QUERY_THROW
),
2591 m_bIsNewDoc
? uno::Reference
<text::XTextCursor
>() : xAnnotationText
->createTextCursorByRange(xAnnotationText
->getStart())));
2593 catch( const uno::Exception
&)
2595 DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
2600 void DomainMapper_Impl::PopFootOrEndnote()
2603 RemoveLastParagraph();
2605 // In case the foot or endnote did not contain a tab.
2606 m_bIgnoreNextTab
= false;
2608 if (!m_aTextAppendStack
.empty())
2609 m_aTextAppendStack
.pop();
2611 if (m_aRedlines
.size() == 1)
2613 SAL_WARN("writerfilter.dmapper", "PopFootOrEndnote() is called without PushFootOrEndnote()?");
2617 m_eSkipFootnoteState
= SkipFootnoteSeparator::OFF
;
2618 m_bInFootOrEndnote
= false;
2619 m_pFootnoteContext
= nullptr;
2620 m_bFirstParagraphInCell
= m_bSaveFirstParagraphInCell
;
2623 void DomainMapper_Impl::PopAnnotation()
2625 RemoveLastParagraph();
2627 m_bIsInComments
= false;
2628 m_aTextAppendStack
.pop();
2632 // See if the annotation will be a single position or a range.
2633 if (m_nAnnotationId
== -1 || !m_aAnnotationPositions
[m_nAnnotationId
].m_xStart
.is() || !m_aAnnotationPositions
[m_nAnnotationId
].m_xEnd
.is())
2635 uno::Sequence
< beans::PropertyValue
> aEmptyProperties
;
2636 uno::Reference
< text::XTextContent
> xContent( m_xAnnotationField
, uno::UNO_QUERY_THROW
);
2637 appendTextContent( xContent
, aEmptyProperties
);
2638 CheckRedline( xContent
->getAnchor( ) );
2642 AnnotationPosition
& aAnnotationPosition
= m_aAnnotationPositions
[m_nAnnotationId
];
2643 // Create a range that points to the annotation start/end.
2644 uno::Reference
<text::XText
> const xText
= aAnnotationPosition
.m_xStart
->getText();
2645 uno::Reference
<text::XTextCursor
> const xCursor
= xText
->createTextCursorByRange(aAnnotationPosition
.m_xStart
);
2647 bool bMarker
= false;
2648 uno::Reference
<text::XTextRangeCompare
> xTextRangeCompare(xText
, uno::UNO_QUERY
);
2649 if (xTextRangeCompare
->compareRegionStarts(aAnnotationPosition
.m_xStart
, aAnnotationPosition
.m_xEnd
) == 0)
2651 // Insert a marker so that comment around an anchored image is not collapsed during
2653 xText
->insertString(xCursor
, "x", false);
2657 xCursor
->gotoRange(aAnnotationPosition
.m_xEnd
, true);
2658 uno::Reference
<text::XTextRange
> const xTextRange(xCursor
, uno::UNO_QUERY_THROW
);
2660 // Attach the annotation to the range.
2661 uno::Reference
<text::XTextAppend
> const xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
2662 xTextAppend
->insertTextContent(xTextRange
, uno::Reference
<text::XTextContent
>(m_xAnnotationField
, uno::UNO_QUERY_THROW
), !xCursor
->isCollapsed());
2666 // Remove the marker.
2667 xCursor
->goLeft(1, true);
2668 xCursor
->setString(OUString());
2671 m_aAnnotationPositions
.erase( m_nAnnotationId
);
2673 catch (uno::Exception
const&)
2675 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "Cannot insert annotation field");
2678 m_xAnnotationField
.clear();
2679 m_nAnnotationId
= -1;
2682 void DomainMapper_Impl::PushPendingShape( const uno::Reference
< drawing::XShape
> & xShape
)
2684 m_aPendingShapes
.push_back(xShape
);
2687 uno::Reference
<drawing::XShape
> DomainMapper_Impl::PopPendingShape()
2689 uno::Reference
<drawing::XShape
> xRet
;
2690 if (!m_aPendingShapes
.empty())
2692 xRet
= m_aPendingShapes
.front();
2693 m_aPendingShapes
.pop_front();
2698 void DomainMapper_Impl::PushShapeContext( const uno::Reference
< drawing::XShape
> & xShape
)
2700 // Append these early, so the context and the table manager stack will be
2701 // in sync, even if the text append stack is empty.
2702 appendTableManager();
2703 appendTableHandler();
2704 getTableManager().startLevel();
2706 if (m_aTextAppendStack
.empty())
2708 uno::Reference
<text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
2712 uno::Reference
< lang::XServiceInfo
> xSInfo( xShape
, uno::UNO_QUERY_THROW
);
2713 if (xSInfo
->supportsService("com.sun.star.drawing.GroupShape"))
2715 // Textboxes in shapes do not support styles, so check saved style information and apply properties directly to the child shapes.
2716 const uno::Reference
<drawing::XShapes
> xShapes(xShape
, uno::UNO_QUERY
);
2717 const sal_uInt32 nShapeCount
= xShapes
.is() ? xShapes
->getCount() : 0;
2718 for ( sal_uInt32 i
= 0; i
< nShapeCount
; ++i
)
2722 uno::Reference
<beans::XPropertySet
> xSyncedPropertySet(xShapes
->getByIndex(i
), uno::UNO_QUERY_THROW
);
2723 comphelper::SequenceAsHashMap
aGrabBag( xSyncedPropertySet
->getPropertyValue("CharInteropGrabBag") );
2725 // only VML import has checked for style. Don't apply default parastyle properties to other imported shapes
2726 // - except for fontsize - to maintain compatibility with previous versions of LibreOffice.
2727 const bool bOnlyApplyCharHeight
= !aGrabBag
["mso-pStyle"].hasValue();
2729 OUString sStyleName
;
2730 aGrabBag
["mso-pStyle"] >>= sStyleName
;
2731 StyleSheetEntryPtr pEntry
= GetStyleSheetTable()->FindStyleSheetByISTD( sStyleName
);
2734 // Use default style even in ambiguous cases (where multiple styles were defined) since MOST styles inherit
2735 // MOST of their properties from the default style. In the ambiguous case, we have to accept some kind of compromise
2736 // and the default paragraph style ought to be the safest one... (compared to DocDefaults or program defaults)
2737 pEntry
= GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( GetDefaultParaStyleName() );
2741 // The Ids here come from oox/source/vml/vmltextbox.cxx.
2742 // It probably could safely expand to all Ids that shapes support.
2743 const PropertyIds eIds
[] = {
2745 PROP_CHAR_FONT_NAME
,
2747 PROP_CHAR_CHAR_KERNING
,
2751 const uno::Reference
<beans::XPropertyState
> xSyncedPropertyState(xSyncedPropertySet
, uno::UNO_QUERY_THROW
);
2752 for ( const auto& eId
: eIds
)
2756 if ( bOnlyApplyCharHeight
&& eId
!= PROP_CHAR_HEIGHT
)
2759 const OUString sPropName
= getPropertyName(eId
);
2760 if ( beans::PropertyState_DEFAULT_VALUE
== xSyncedPropertyState
->getPropertyState(sPropName
) )
2762 const uno::Any aProp
= GetPropertyFromStyleSheet(eId
, pEntry
, /*bDocDefaults=*/true, /*bPara=*/true);
2763 if ( aProp
.hasValue() )
2764 xSyncedPropertySet
->setPropertyValue( sPropName
, aProp
);
2767 catch (const uno::Exception
&)
2769 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "PushShapeContext() text stylesheet property exception" );
2774 catch (const uno::Exception
&)
2776 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "PushShapeContext()" );
2780 // A GroupShape doesn't implement text::XTextRange, but appending
2781 // an empty reference to the stacks still makes sense, because this
2782 // way bToRemove can be set, and we won't end up with duplicated
2783 // shapes for OLE objects.
2784 m_aTextAppendStack
.push(TextAppendContext(uno::Reference
<text::XTextAppend
>(xShape
, uno::UNO_QUERY
), uno::Reference
<text::XTextCursor
>()));
2785 uno::Reference
<text::XTextContent
> xTxtContent(xShape
, uno::UNO_QUERY
);
2786 m_aAnchoredStack
.push(AnchoredContext(xTxtContent
));
2788 else if (xSInfo
->supportsService("com.sun.star.drawing.OLE2Shape"))
2790 // OLE2Shape from oox should be converted to a TextEmbeddedObject for sw.
2791 m_aTextAppendStack
.push(TextAppendContext(uno::Reference
<text::XTextAppend
>(xShape
, uno::UNO_QUERY
), uno::Reference
<text::XTextCursor
>()));
2792 uno::Reference
<text::XTextContent
> xTextContent(xShape
, uno::UNO_QUERY
);
2793 m_aAnchoredStack
.push(AnchoredContext(xTextContent
));
2794 uno::Reference
<beans::XPropertySet
> xShapePropertySet(xShape
, uno::UNO_QUERY
);
2796 m_xEmbedded
.set(m_xTextFactory
->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW
);
2797 uno::Reference
<beans::XPropertySet
> xEmbeddedProperties(m_xEmbedded
, uno::UNO_QUERY_THROW
);
2798 xEmbeddedProperties
->setPropertyValue(getPropertyName(PROP_EMBEDDED_OBJECT
), xShapePropertySet
->getPropertyValue(getPropertyName(PROP_EMBEDDED_OBJECT
)));
2799 xEmbeddedProperties
->setPropertyValue(getPropertyName(PROP_ANCHOR_TYPE
), uno::makeAny(text::TextContentAnchorType_AS_CHARACTER
));
2800 // So that the original bitmap-only shape will be replaced by the embedded object.
2801 m_aAnchoredStack
.top().bToRemove
= true;
2802 m_aTextAppendStack
.pop();
2803 appendTextContent(m_xEmbedded
, uno::Sequence
<beans::PropertyValue
>());
2807 uno::Reference
< text::XTextRange
> xShapeText( xShape
, uno::UNO_QUERY_THROW
);
2808 // Add the shape to the text append stack
2809 m_aTextAppendStack
.push( TextAppendContext(uno::Reference
< text::XTextAppend
>( xShape
, uno::UNO_QUERY_THROW
),
2810 m_bIsNewDoc
? uno::Reference
<text::XTextCursor
>() : m_xBodyText
->createTextCursorByRange(xShapeText
->getStart() )));
2812 // Add the shape to the anchored objects stack
2813 uno::Reference
< text::XTextContent
> xTxtContent( xShape
, uno::UNO_QUERY_THROW
);
2814 m_aAnchoredStack
.push( AnchoredContext(xTxtContent
) );
2816 uno::Reference
< beans::XPropertySet
> xProps( xShape
, uno::UNO_QUERY_THROW
);
2818 TagLogger::getInstance().unoPropertySet(xProps
);
2820 text::TextContentAnchorType
nAnchorType(text::TextContentAnchorType_AT_PARAGRAPH
);
2821 xProps
->getPropertyValue(getPropertyName( PROP_ANCHOR_TYPE
)) >>= nAnchorType
;
2822 bool checkZOrderStatus
= false;
2823 if (xSInfo
->supportsService("com.sun.star.text.TextFrame"))
2825 SetIsTextFrameInserted(true);
2826 // Extract the special "btLr text frame" mode, requested by oox, if needed.
2827 // Extract vml ZOrder from FrameInteropGrabBag
2828 uno::Reference
<beans::XPropertySet
> xShapePropertySet(xShape
, uno::UNO_QUERY
);
2829 uno::Sequence
<beans::PropertyValue
> aGrabBag
;
2830 xShapePropertySet
->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag
;
2832 for (const auto& rProp
: std::as_const(aGrabBag
))
2834 if (rProp
.Name
== "VML-Z-ORDER")
2836 GraphicZOrderHelper
* pZOrderHelper
= m_rDMapper
.graphicZOrderHelper();
2837 sal_Int32
zOrder(0);
2838 rProp
.Value
>>= zOrder
;
2839 xShapePropertySet
->setPropertyValue( "ZOrder", uno::makeAny(pZOrderHelper
->findZOrder(zOrder
)));
2840 pZOrderHelper
->addItem(xShapePropertySet
, zOrder
);
2841 xShapePropertySet
->setPropertyValue(getPropertyName( PROP_OPAQUE
), uno::makeAny( zOrder
>= 0 ) );
2842 checkZOrderStatus
= true;
2844 else if ( rProp
.Name
== "TxbxHasLink" )
2846 //Chaining of textboxes will happen in ~DomainMapper_Impl
2847 //i.e when all the textboxes are read and all its attributes
2848 //have been set ( basically the Name/LinkedDisplayName )
2849 //which is set in Graphic Import.
2850 m_vTextFramesForChaining
.push_back(xShape
);
2854 uno::Reference
<text::XTextContent
> xTextContent(xShape
, uno::UNO_QUERY_THROW
);
2855 uno::Reference
<text::XTextRange
> xTextRange(xTextAppend
->createTextCursorByRange(xTextAppend
->getEnd()), uno::UNO_QUERY_THROW
);
2856 xTextAppend
->insertTextContent(xTextRange
, xTextContent
, false);
2858 uno::Reference
<beans::XPropertySet
> xPropertySet(xTextContent
, uno::UNO_QUERY
);
2859 // we need to re-set this value to xTextContent, then only values are preserved.
2860 xPropertySet
->setPropertyValue("FrameInteropGrabBag",uno::makeAny(aGrabBag
));
2862 else if (nAnchorType
== text::TextContentAnchorType_AS_CHARACTER
)
2864 // Fix spacing for as-character objects. If the paragraph has CT_Spacing_after set,
2865 // it needs to be set on the object too, as that's what object placement code uses.
2866 PropertyMapPtr paragraphContext
= GetTopContextOfType( CONTEXT_PARAGRAPH
);
2867 std::optional
<PropertyMap::Property
> aPropMargin
= paragraphContext
->getProperty(PROP_PARA_BOTTOM_MARGIN
);
2869 xProps
->setPropertyValue( getPropertyName( PROP_BOTTOM_MARGIN
), aPropMargin
->second
);
2873 uno::Reference
<beans::XPropertySet
> xShapePropertySet(xShape
, uno::UNO_QUERY
);
2874 uno::Sequence
<beans::PropertyValue
> aGrabBag
;
2875 xShapePropertySet
->getPropertyValue("InteropGrabBag") >>= aGrabBag
;
2876 for (const auto& rProp
: std::as_const(aGrabBag
))
2878 if (rProp
.Name
== "VML-Z-ORDER")
2880 GraphicZOrderHelper
* pZOrderHelper
= m_rDMapper
.graphicZOrderHelper();
2881 sal_Int32
zOrder(0);
2882 rProp
.Value
>>= zOrder
;
2883 xShapePropertySet
->setPropertyValue( "ZOrder", uno::makeAny(pZOrderHelper
->findZOrder(zOrder
)));
2884 pZOrderHelper
->addItem(xShapePropertySet
, zOrder
);
2885 xShapePropertySet
->setPropertyValue(getPropertyName( PROP_OPAQUE
), uno::makeAny( zOrder
>= 0 ) );
2886 checkZOrderStatus
= true;
2888 else if ( rProp
.Name
== "TxbxHasLink" )
2890 //Chaining of textboxes will happen in ~DomainMapper_Impl
2891 //i.e when all the textboxes are read and all its attributes
2892 //have been set ( basically the Name/LinkedDisplayName )
2893 //which is set in Graphic Import.
2894 m_vTextFramesForChaining
.push_back(xShape
);
2898 if(IsSdtEndBefore())
2900 uno::Reference
< beans::XPropertySetInfo
> xPropSetInfo
;
2901 if(xShapePropertySet
.is())
2903 xPropSetInfo
= xShapePropertySet
->getPropertySetInfo();
2904 if (xPropSetInfo
.is() && xPropSetInfo
->hasPropertyByName("InteropGrabBag"))
2906 uno::Sequence
<beans::PropertyValue
> aShapeGrabBag( comphelper::InitPropertySequence({
2907 { "SdtEndBefore", uno::Any(true) }
2909 xShapePropertySet
->setPropertyValue("InteropGrabBag",uno::makeAny(aShapeGrabBag
));
2914 if (!IsInHeaderFooter() && !checkZOrderStatus
)
2915 xProps
->setPropertyValue(
2916 getPropertyName( PROP_OPAQUE
),
2917 uno::makeAny( true ) );
2919 m_bParaChanged
= true;
2920 getTableManager().setIsInShape(true);
2922 catch ( const uno::Exception
& )
2924 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "Exception when adding shape");
2928 * Updating chart height and width after reading the actual values from wp:extent
2930 void DomainMapper_Impl::UpdateEmbeddedShapeProps(const uno::Reference
< drawing::XShape
> & xShape
)
2935 uno::Reference
<beans::XPropertySet
> xEmbeddedProperties(m_xEmbedded
, uno::UNO_QUERY_THROW
);
2936 awt::Size aSize
= xShape
->getSize( );
2937 xEmbeddedProperties
->setPropertyValue(getPropertyName(PROP_WIDTH
), uno::makeAny(sal_Int32(aSize
.Width
)));
2938 xEmbeddedProperties
->setPropertyValue(getPropertyName(PROP_HEIGHT
), uno::makeAny(sal_Int32(aSize
.Height
)));
2942 void DomainMapper_Impl::PopShapeContext()
2944 if (hasTableManager())
2946 getTableManager().endLevel();
2949 if ( !m_aAnchoredStack
.empty() )
2951 // For OLE object replacement shape, the text append context was already removed
2952 // or the OLE object couldn't be inserted.
2953 if ( !m_aAnchoredStack
.top().bToRemove
)
2955 RemoveLastParagraph();
2956 if (!m_aTextAppendStack
.empty())
2957 m_aTextAppendStack
.pop();
2960 uno::Reference
< text::XTextContent
> xObj
= m_aAnchoredStack
.top( ).xTextContent
;
2963 appendTextContent( xObj
, uno::Sequence
< beans::PropertyValue
>() );
2965 catch ( const uno::RuntimeException
& )
2967 // this is normal: the shape is already attached
2970 const uno::Reference
<drawing::XShape
> xShape( xObj
, uno::UNO_QUERY_THROW
);
2971 // Remove the shape if required (most likely replacement shape for OLE object)
2972 // or anchored to a discarded header or footer
2973 if ( m_aAnchoredStack
.top().bToRemove
|| m_bDiscardHeaderFooter
)
2977 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(m_xTextDocument
, uno::UNO_QUERY_THROW
);
2978 uno::Reference
<drawing::XDrawPage
> xDrawPage
= xDrawPageSupplier
->getDrawPage();
2979 if ( xDrawPage
.is() )
2980 xDrawPage
->remove( xShape
);
2982 catch( const uno::Exception
& )
2987 // Relative width calculations deferred until section's margins are defined.
2988 // Being cautious: only deferring undefined/minimum-width shapes in order to avoid causing potential regressions
2989 css::awt::Size aShapeSize
;
2992 aShapeSize
= xShape
->getSize();
2994 catch (const css::uno::RuntimeException
& e
)
2996 // May happen e.g. when text frame has no frame format
2997 // See sw/qa/extras/ooxmlimport/data/n779627.docx
2998 SAL_WARN("writerfilter.dmapper", "getSize failed. " << e
.Message
);
3000 if( aShapeSize
.Width
<= 2 )
3002 const uno::Reference
<beans::XPropertySet
> xShapePropertySet( xShape
, uno::UNO_QUERY
);
3003 SectionPropertyMap
* pSectionContext
= GetSectionContext();
3004 if ( pSectionContext
&& (!hasTableManager() || !getTableManager().isInTable()) &&
3005 xShapePropertySet
->getPropertySetInfo()->hasPropertyByName(getPropertyName(PROP_RELATIVE_WIDTH
)) )
3007 pSectionContext
->addRelativeWidthShape(xShape
);
3011 m_aAnchoredStack
.pop();
3015 bool DomainMapper_Impl::IsSdtEndBefore()
3017 bool bIsSdtEndBefore
= false;
3018 PropertyMapPtr pContext
= GetTopContextOfType(CONTEXT_CHARACTER
);
3021 const uno::Sequence
< beans::PropertyValue
> currentCharProps
= pContext
->GetPropertyValues();
3022 for (const auto& rCurrentCharProp
: currentCharProps
)
3024 if (rCurrentCharProp
.Name
== "CharInteropGrabBag")
3026 uno::Sequence
<beans::PropertyValue
> aCharGrabBag
;
3027 rCurrentCharProp
.Value
>>= aCharGrabBag
;
3028 for (const auto& rProp
: std::as_const(aCharGrabBag
))
3030 if(rProp
.Name
== "SdtEndBefore")
3032 rProp
.Value
>>= bIsSdtEndBefore
;
3038 return bIsSdtEndBefore
;
3041 bool DomainMapper_Impl::IsDiscardHeaderFooter() const
3043 return m_bDiscardHeaderFooter
;
3046 // called from TableManager::closeCell()
3047 void DomainMapper_Impl::ClearPreviousParagraph()
3049 // in table cells, set bottom auto margin of last paragraph to 0, except in paragraphs with numbering
3050 if ((m_nTableDepth
== (m_nTableCellDepth
+ 1))
3051 && m_xPreviousParagraph
.is()
3052 && hasTableManager() && getTableManager().isCellLastParaAfterAutospacing())
3054 uno::Reference
<container::XNamed
> xPreviousNumberingRules(m_xPreviousParagraph
->getPropertyValue("NumberingRules"), uno::UNO_QUERY
);
3055 if ( !xPreviousNumberingRules
.is() || xPreviousNumberingRules
->getName().isEmpty() )
3056 m_xPreviousParagraph
->setPropertyValue("ParaBottomMargin", uno::makeAny(static_cast<sal_Int32
>(0)));
3059 m_xPreviousParagraph
.clear();
3061 // next table paragraph will be first paragraph in a cell
3062 m_bFirstParagraphInCell
= true;
3065 static sal_Int16
lcl_ParseNumberingType( const OUString
& rCommand
)
3067 sal_Int16 nRet
= style::NumberingType::PAGE_DESCRIPTOR
;
3069 // The command looks like: " PAGE \* Arabic "
3070 OUString sNumber
= msfilter::util::findQuotedText(rCommand
, "\\* ", ' ');
3072 if( !sNumber
.isEmpty() )
3074 //todo: might make sense to hash this list, too
3075 struct NumberingPairs
3077 const sal_Char
* cWordName
;
3080 static const NumberingPairs aNumberingPairs
[] =
3082 {"Arabic", style::NumberingType::ARABIC
}
3083 ,{"ROMAN", style::NumberingType::ROMAN_UPPER
}
3084 ,{"roman", style::NumberingType::ROMAN_LOWER
}
3085 ,{"ALPHABETIC", style::NumberingType::CHARS_UPPER_LETTER
}
3086 ,{"alphabetic", style::NumberingType::CHARS_LOWER_LETTER
}
3087 ,{"CircleNum", style::NumberingType::CIRCLE_NUMBER
}
3088 ,{"ThaiArabic", style::NumberingType::CHARS_THAI
}
3089 ,{"ThaiCardText", style::NumberingType::CHARS_THAI
}
3090 ,{"ThaiLetter", style::NumberingType::CHARS_THAI
}
3091 // ,{"SBCHAR", style::NumberingType::}
3092 // ,{"DBCHAR", style::NumberingType::}
3093 // ,{"DBNUM1", style::NumberingType::}
3094 // ,{"DBNUM2", style::NumberingType::}
3095 // ,{"DBNUM3", style::NumberingType::}
3096 // ,{"DBNUM4", style::NumberingType::}
3097 ,{"Aiueo", style::NumberingType::AIU_FULLWIDTH_JA
}
3098 ,{"Iroha", style::NumberingType::IROHA_FULLWIDTH_JA
}
3099 // ,{"ZODIAC1", style::NumberingType::}
3100 // ,{"ZODIAC2", style::NumberingType::}
3101 // ,{"ZODIAC3", style::NumberingType::}
3102 // ,{"CHINESENUM1", style::NumberingType::}
3103 // ,{"CHINESENUM2", style::NumberingType::}
3104 // ,{"CHINESENUM3", style::NumberingType::}
3105 ,{"ArabicAlpha", style::NumberingType::CHARS_ARABIC
}
3106 ,{"ArabicAbjad", style::NumberingType::FULLWIDTH_ARABIC
}
3108 style::NumberingType::
3110 CHARS_UPPER_LETTER_N
3111 CHARS_LOWER_LETTER_N
3120 NUMBER_TRADITIONAL_JA
3127 HANGUL_CIRCLED_JAMO_KO
3128 HANGUL_CIRCLED_SYLLABLE_KO
3134 CHARS_CYRILLIC_UPPER_LETTER_BG
3135 CHARS_CYRILLIC_LOWER_LETTER_BG
3136 CHARS_CYRILLIC_UPPER_LETTER_N_BG
3137 CHARS_CYRILLIC_LOWER_LETTER_N_BG
3138 CHARS_CYRILLIC_UPPER_LETTER_RU
3139 CHARS_CYRILLIC_LOWER_LETTER_RU
3140 CHARS_CYRILLIC_UPPER_LETTER_N_RU
3141 CHARS_CYRILLIC_LOWER_LETTER_N_RU
3142 CHARS_CYRILLIC_UPPER_LETTER_SR
3143 CHARS_CYRILLIC_LOWER_LETTER_SR
3144 CHARS_CYRILLIC_UPPER_LETTER_N_SR
3145 CHARS_CYRILLIC_LOWER_LETTER_N_SR*/
3148 for(const NumberingPairs
& rNumberingPair
: aNumberingPairs
)
3150 if( /*sCommand*/sNumber
.equalsAscii(rNumberingPair
.cWordName
))
3152 nRet
= rNumberingPair
.nType
;
3162 static OUString
lcl_ParseFormat( const OUString
& rCommand
)
3164 // The command looks like: " DATE \@"dd MMMM yyyy" or "09/02/2014"
3165 // Remove whitespace permitted by standard between \@ and "
3167 sal_Int32 delimPos
= rCommand
.indexOf("\\@");
3170 sal_Int32 wsChars
= rCommand
.indexOf('\"') - delimPos
- 2;
3171 command
= rCommand
.replaceAt(delimPos
+2, wsChars
, "");
3176 return msfilter::util::findQuotedText(command
, "\\@\"", '\"');
3178 /*-------------------------------------------------------------------------
3179 extract a parameter (with or without quotes) between the command and the following backslash
3180 -----------------------------------------------------------------------*/
3181 static OUString
lcl_ExtractToken(OUString
const& rCommand
,
3182 sal_Int32
& rIndex
, bool & rHaveToken
, bool & rIsSwitch
)
3187 OUStringBuffer token
;
3188 bool bQuoted(false);
3189 for (; rIndex
< rCommand
.getLength(); ++rIndex
)
3191 sal_Unicode
const currentChar(rCommand
[rIndex
]);
3192 switch (currentChar
)
3196 if (rIndex
== rCommand
.getLength() - 1)
3198 SAL_INFO("writerfilter.dmapper", "field: trailing escape");
3202 sal_Unicode
const nextChar(rCommand
[rIndex
+1]);
3203 if (bQuoted
|| '\\' == nextChar
)
3205 ++rIndex
; // read 2 chars
3206 token
.append(nextChar
);
3208 else // field switch (case insensitive)
3211 if (token
.isEmpty())
3214 rIndex
+= 2; // read 2 chars
3215 return rCommand
.copy(rIndex
- 2, 2).toAsciiUpperCase();
3218 { // leave rIndex, read it again next time
3219 return token
.makeStringAndClear();
3225 if (bQuoted
|| !token
.isEmpty())
3232 return token
.makeStringAndClear();
3246 if (!token
.isEmpty())
3250 return token
.makeStringAndClear();
3255 if (token
.isEmpty())
3263 token
.append(currentChar
);
3267 assert(rIndex
== rCommand
.getLength());
3270 // MS Word allows this, so just emit a debug message
3271 SAL_INFO("writerfilter.dmapper",
3272 "field argument with unterminated quote");
3274 rHaveToken
= !token
.isEmpty();
3275 return token
.makeStringAndClear();
3278 std::tuple
<OUString
, std::vector
<OUString
>, std::vector
<OUString
> > splitFieldCommand(const OUString
& rCommand
)
3281 std::vector
<OUString
> arguments
;
3282 std::vector
<OUString
> switches
;
3283 sal_Int32
nStartIndex(0);
3284 // tdf#54584: Field may be prepended by a backslash
3285 // This is not an escapement, but already escaped literal "\"
3286 // MS Word allows this, so just skip it
3287 if ((rCommand
.getLength() >= nStartIndex
+ 2) &&
3288 (rCommand
[nStartIndex
] == L
'\\') &&
3289 (rCommand
[nStartIndex
+ 1] != L
'\\') &&
3290 (rCommand
[nStartIndex
+ 1] != L
' '))
3299 OUString
const token
=
3300 lcl_ExtractToken(rCommand
, nStartIndex
, bHaveToken
, bIsSwitch
);
3301 assert(nStartIndex
<= rCommand
.getLength());
3304 if (sType
.isEmpty())
3306 sType
= token
.toAsciiUpperCase();
3308 else if (bIsSwitch
|| !switches
.empty())
3310 switches
.push_back(token
);
3314 arguments
.push_back(token
);
3317 } while (nStartIndex
< rCommand
.getLength());
3319 return std::make_tuple(sType
, arguments
, switches
);
3322 static OUString
lcl_ExctractVariableAndHint( const OUString
& rCommand
, OUString
& rHint
)
3324 // the first word after "ASK " is the variable
3325 // the text after the variable and before a '\' is the hint
3326 // if no hint is set the variable is used as hint
3327 // the quotes of the hint have to be removed
3328 sal_Int32 nIndex
= rCommand
.indexOf( ' ', 2); //find last space after 'ASK'
3331 while(rCommand
[nIndex
] == ' ')
3333 OUString
sShortCommand( rCommand
.copy( nIndex
) ); //cut off the " ASK "
3335 sShortCommand
= sShortCommand
.getToken(0, '\\');
3337 OUString sRet
= sShortCommand
.getToken( 0, ' ', nIndex
);
3339 rHint
= sShortCommand
.copy( nIndex
);
3340 if( rHint
.isEmpty() )
3346 static bool lcl_FindInCommand(
3347 const OUString
& rCommand
,
3348 sal_Unicode cSwitch
,
3352 OUString sSearch
= "\\" + OUStringChar( cSwitch
);
3353 sal_Int32 nIndex
= rCommand
.indexOf( sSearch
);
3357 //find next '\' or end of string
3358 sal_Int32 nEndIndex
= rCommand
.indexOf( '\\', nIndex
+ 1);
3360 nEndIndex
= rCommand
.getLength() ;
3361 if( nEndIndex
- nIndex
> 3 )
3362 rValue
= rCommand
.copy( nIndex
+ 3, nEndIndex
- nIndex
- 3);
3368 void DomainMapper_Impl::GetCurrentLocale(lang::Locale
& rLocale
)
3370 PropertyMapPtr pTopContext
= GetTopContext();
3371 std::optional
<PropertyMap::Property
> pLocale
= pTopContext
->getProperty(PROP_CHAR_LOCALE
);
3373 pLocale
->second
>>= rLocale
;
3376 PropertyMapPtr pParaContext
= GetTopContextOfType(CONTEXT_PARAGRAPH
);
3377 pLocale
= pParaContext
->getProperty(PROP_CHAR_LOCALE
);
3380 pLocale
->second
>>= rLocale
;
3385 /*-------------------------------------------------------------------------
3386 extract the number format from the command and apply the resulting number
3387 format to the XPropertySet
3388 -----------------------------------------------------------------------*/
3389 void DomainMapper_Impl::SetNumberFormat( const OUString
& rCommand
,
3390 uno::Reference
< beans::XPropertySet
> const& xPropertySet
,
3391 bool const bDetectFormat
)
3393 OUString sFormatString
= lcl_ParseFormat( rCommand
);
3394 // find \h - hijri/luna calendar todo: what about saka/era calendar?
3395 bool bHijri
= 0 < rCommand
.indexOf("\\h ");
3396 lang::Locale aUSLocale
;
3397 aUSLocale
.Language
= "en";
3398 aUSLocale
.Country
= "US";
3400 //determine current locale - todo: is it necessary to initialize this locale?
3401 lang::Locale aCurrentLocale
= aUSLocale
;
3402 GetCurrentLocale( aCurrentLocale
);
3403 OUString sFormat
= ConversionHelper::ConvertMSFormatStringToSO( sFormatString
, aCurrentLocale
, bHijri
);
3404 //get the number formatter and convert the string to a format value
3408 uno::Reference
< util::XNumberFormatsSupplier
> xNumberSupplier( m_xTextDocument
, uno::UNO_QUERY_THROW
);
3411 uno::Reference
< util::XNumberFormatter
> xFormatter(util::NumberFormatter::create(m_xComponentContext
), uno::UNO_QUERY_THROW
);
3412 xFormatter
->attachNumberFormatsSupplier( xNumberSupplier
);
3413 nKey
= xFormatter
->detectNumberFormat( 0, rCommand
);
3417 nKey
= xNumberSupplier
->getNumberFormats()->addNewConverted( sFormat
, aUSLocale
, aCurrentLocale
);
3419 xPropertySet
->setPropertyValue(
3420 getPropertyName(PROP_NUMBER_FORMAT
),
3421 uno::makeAny( nKey
));
3423 catch(const uno::Exception
&)
3428 static uno::Any
lcl_getGrabBagValue( const uno::Sequence
<beans::PropertyValue
>& grabBag
, OUString
const & name
)
3430 auto pProp
= std::find_if(grabBag
.begin(), grabBag
.end(),
3431 [&name
](const beans::PropertyValue
& rProp
) { return rProp
.Name
== name
; });
3432 if (pProp
!= grabBag
.end())
3433 return pProp
->Value
;
3437 //Link the text frames.
3438 void DomainMapper_Impl::ChainTextFrames()
3440 //can't link textboxes if there are not even two of them...
3441 if( 2 > m_vTextFramesForChaining
.size() )
3444 struct TextFramesForChaining
{
3445 css::uno::Reference
< css::drawing::XShape
> xShape
;
3448 OUString s_mso_next_textbox
;
3450 TextFramesForChaining(): nId(0), nSeq(0), bShapeNameSet(false) {}
3452 typedef std::map
<OUString
, TextFramesForChaining
> ChainMap
;
3456 ChainMap aTextFramesForChainingHelper
;
3457 OUString
sChainNextName("ChainNextName");
3459 //learn about ALL of the textboxes and their chaining values first - because frames are processed in no specific order.
3460 for( const auto& rTextFrame
: m_vTextFramesForChaining
)
3462 uno::Reference
<text::XTextContent
> xTextContent(rTextFrame
, uno::UNO_QUERY_THROW
);
3463 uno::Reference
<beans::XPropertySet
> xPropertySet(xTextContent
, uno::UNO_QUERY
);
3464 uno::Reference
<beans::XPropertySetInfo
> xPropertySetInfo
;
3465 if( xPropertySet
.is() )
3466 xPropertySetInfo
= xPropertySet
->getPropertySetInfo();
3467 uno::Sequence
<beans::PropertyValue
> aGrabBag
;
3468 uno::Reference
<lang::XServiceInfo
> xServiceInfo(xPropertySet
, uno::UNO_QUERY
);
3470 TextFramesForChaining aChainStruct
;
3471 OUString sShapeName
;
3472 OUString sLinkChainName
;
3474 //The chaining name and the shape name CAN be different in .docx.
3475 //MUST use LinkDisplayName/ChainName as the shape name for establishing links.
3476 if ( xServiceInfo
->supportsService("com.sun.star.text.TextFrame") )
3478 xPropertySet
->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag
;
3479 xPropertySet
->getPropertyValue("LinkDisplayName") >>= sShapeName
;
3483 xPropertySet
->getPropertyValue("InteropGrabBag") >>= aGrabBag
;
3484 xPropertySet
->getPropertyValue("ChainName") >>= sShapeName
;
3487 lcl_getGrabBagValue( aGrabBag
, "Txbx-Id") >>= aChainStruct
.nId
;
3488 lcl_getGrabBagValue( aGrabBag
, "Txbx-Seq") >>= aChainStruct
.nSeq
;
3489 lcl_getGrabBagValue( aGrabBag
, "LinkChainName") >>= sLinkChainName
;
3490 lcl_getGrabBagValue( aGrabBag
, "mso-next-textbox") >>= aChainStruct
.s_mso_next_textbox
;
3492 //Sometimes the shape names have not been imported. If not, we may have a fallback name.
3493 //Set name later, only if required for linking.
3494 if( sShapeName
.isEmpty() )
3495 aChainStruct
.bShapeNameSet
= false;
3498 aChainStruct
.bShapeNameSet
= true;
3499 sLinkChainName
= sShapeName
;
3502 if( !sLinkChainName
.isEmpty() )
3504 aChainStruct
.xShape
= rTextFrame
;
3505 aTextFramesForChainingHelper
[sLinkChainName
] = aChainStruct
;
3509 //if mso-next-textbox tags are provided, create those vml-style links first. Afterwards we will make dml-style id/seq links.
3510 for (auto& msoItem
: aTextFramesForChainingHelper
)
3512 //if no mso-next-textbox, we are done.
3513 //if it points to itself, we are done.
3514 if( !msoItem
.second
.s_mso_next_textbox
.isEmpty()
3515 && msoItem
.second
.s_mso_next_textbox
!= msoItem
.first
)
3517 ChainMap::iterator nextFinder
=aTextFramesForChainingHelper
.find(msoItem
.second
.s_mso_next_textbox
);
3518 if( nextFinder
!= aTextFramesForChainingHelper
.end() )
3520 //if the frames have no name yet, then set them. LinkDisplayName / ChainName are read-only.
3521 if( !msoItem
.second
.bShapeNameSet
)
3523 uno::Reference
< container::XNamed
> xNamed( msoItem
.second
.xShape
, uno::UNO_QUERY
);
3526 xNamed
->setName( msoItem
.first
);
3527 msoItem
.second
.bShapeNameSet
= true;
3530 if( !nextFinder
->second
.bShapeNameSet
)
3532 uno::Reference
< container::XNamed
> xNamed( nextFinder
->second
.xShape
, uno::UNO_QUERY
);
3535 xNamed
->setName( nextFinder
->first
);
3536 nextFinder
->second
.bShapeNameSet
= true;
3540 uno::Reference
<text::XTextContent
> xTextContent(msoItem
.second
.xShape
, uno::UNO_QUERY_THROW
);
3541 uno::Reference
<beans::XPropertySet
> xPropertySet(xTextContent
, uno::UNO_QUERY
);
3543 //The reverse chaining happens automatically, so only one direction needs to be set
3544 xPropertySet
->setPropertyValue(sChainNextName
, uno::makeAny(nextFinder
->first
));
3546 //the last item in an mso-next-textbox chain is indistinguishable from id/seq items. Now that it is handled, remove it.
3547 if( nextFinder
->second
.s_mso_next_textbox
.isEmpty() )
3548 aTextFramesForChainingHelper
.erase(nextFinder
->first
);
3553 //TODO: Perhaps allow reverse sequences when mso-layout-flow-alt = "bottom-to-top"
3554 const sal_Int32 nDirection
= 1;
3556 //Finally - go through and attach the chains based on matching ID and incremented sequence number (dml-style).
3557 for (const auto& rOuter
: aTextFramesForChainingHelper
)
3559 if( rOuter
.second
.s_mso_next_textbox
.isEmpty() ) //non-empty ones already handled earlier - so skipping them now.
3561 for (const auto& rInner
: aTextFramesForChainingHelper
)
3563 if ( rInner
.second
.nId
== rOuter
.second
.nId
)
3565 if ( rInner
.second
.nSeq
== ( rOuter
.second
.nSeq
+ nDirection
) )
3567 uno::Reference
<text::XTextContent
> xTextContent(rOuter
.second
.xShape
, uno::UNO_QUERY_THROW
);
3568 uno::Reference
<beans::XPropertySet
> xPropertySet(xTextContent
, uno::UNO_QUERY
);
3570 //The reverse chaining happens automatically, so only one direction needs to be set
3571 xPropertySet
->setPropertyValue(sChainNextName
, uno::makeAny(rInner
.first
));
3572 break ; //there cannot be more than one next frame
3578 m_vTextFramesForChaining
.clear(); //clear the vector
3580 catch (const uno::Exception
&)
3582 DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
3586 uno::Reference
<beans::XPropertySet
> DomainMapper_Impl::FindOrCreateFieldMaster(const char* pFieldMasterService
, const OUString
& rFieldMasterName
)
3588 // query master, create if not available
3589 uno::Reference
< text::XTextFieldsSupplier
> xFieldsSupplier( GetTextDocument(), uno::UNO_QUERY_THROW
);
3590 uno::Reference
< container::XNameAccess
> xFieldMasterAccess
= xFieldsSupplier
->getTextFieldMasters();
3591 uno::Reference
< beans::XPropertySet
> xMaster
;
3592 OUString
sFieldMasterService( OUString::createFromAscii(pFieldMasterService
) );
3593 OUStringBuffer aFieldMasterName
;
3594 OUString sDatabaseDataSourceName
= GetSettingsTable()->GetCurrentDatabaseDataSource();
3595 bool bIsMergeField
= sFieldMasterService
.endsWith("Database");
3596 aFieldMasterName
.appendAscii( pFieldMasterService
);
3597 aFieldMasterName
.append('.');
3598 if ( bIsMergeField
&& !sDatabaseDataSourceName
.isEmpty() )
3600 aFieldMasterName
.append(sDatabaseDataSourceName
);
3601 aFieldMasterName
.append('.');
3603 aFieldMasterName
.append(rFieldMasterName
);
3604 OUString sFieldMasterName
= aFieldMasterName
.makeStringAndClear();
3605 if(xFieldMasterAccess
->hasByName(sFieldMasterName
))
3608 xMaster
.set(xFieldMasterAccess
->getByName(sFieldMasterName
), uno::UNO_QUERY_THROW
);
3610 else if( m_xTextFactory
.is() )
3613 xMaster
.set( m_xTextFactory
->createInstance(sFieldMasterService
), uno::UNO_QUERY_THROW
);
3614 if ( !bIsMergeField
|| sDatabaseDataSourceName
.isEmpty() )
3616 //set the master's name
3617 xMaster
->setPropertyValue(
3618 getPropertyName(PROP_NAME
),
3619 uno::makeAny(rFieldMasterName
));
3621 // set database data, based on the "databasename.tablename" of sDatabaseDataSourceName
3622 xMaster
->setPropertyValue(
3623 getPropertyName(PROP_DATABASE_NAME
),
3624 uno::makeAny(sDatabaseDataSourceName
.copy(0, sDatabaseDataSourceName
.indexOf('.'))));
3625 xMaster
->setPropertyValue(
3626 getPropertyName(PROP_COMMAND_TYPE
),
3627 uno::makeAny(sal_Int32(0)));
3628 xMaster
->setPropertyValue(
3629 getPropertyName(PROP_DATATABLE_NAME
),
3630 uno::makeAny(sDatabaseDataSourceName
.copy(sDatabaseDataSourceName
.indexOf('.') + 1)));
3631 xMaster
->setPropertyValue(
3632 getPropertyName(PROP_DATACOLUMN_NAME
),
3633 uno::makeAny(rFieldMasterName
));
3639 void DomainMapper_Impl::PushFieldContext()
3641 m_bParaHadField
= true;
3642 if(m_bDiscardHeaderFooter
)
3645 TagLogger::getInstance().element("pushFieldContext");
3648 uno::Reference
<text::XTextCursor
> xCrsr
;
3649 if (!m_aTextAppendStack
.empty())
3651 uno::Reference
<text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
3652 if (xTextAppend
.is())
3653 xCrsr
= xTextAppend
->createTextCursorByRange(
3654 m_aTextAppendStack
.top().xInsertPosition
.is()
3655 ? m_aTextAppendStack
.top().xInsertPosition
3656 : xTextAppend
->getEnd());
3659 uno::Reference
< text::XTextRange
> xStart
;
3661 xStart
= xCrsr
->getStart();
3662 m_aFieldStack
.push_back(new FieldContext(xStart
));
3664 /*-------------------------------------------------------------------------
3665 //the current field context waits for the completion of the command
3666 -----------------------------------------------------------------------*/
3667 bool DomainMapper_Impl::IsOpenFieldCommand() const
3669 return !m_aFieldStack
.empty() && !m_aFieldStack
.back()->IsCommandCompleted();
3671 /*-------------------------------------------------------------------------
3672 //the current field context waits for the completion of the command
3673 -----------------------------------------------------------------------*/
3674 bool DomainMapper_Impl::IsOpenField() const
3676 return !m_aFieldStack
.empty();
3679 // Mark top field context as containing a fixed field
3680 void DomainMapper_Impl::SetFieldLocked()
3683 m_aFieldStack
.back()->SetFieldLocked();
3686 HeaderFooterContext::HeaderFooterContext(bool bTextInserted
, sal_Int32 nTableDepth
)
3687 : m_bTextInserted(bTextInserted
)
3688 , m_nTableDepth(nTableDepth
)
3692 bool HeaderFooterContext::getTextInserted() const
3694 return m_bTextInserted
;
3697 sal_Int32
HeaderFooterContext::getTableDepth() const { return m_nTableDepth
; }
3699 FieldContext::FieldContext(uno::Reference
< text::XTextRange
> const& xStart
)
3700 : m_bFieldCommandCompleted(false)
3701 , m_xStartRange( xStart
)
3702 , m_bFieldLocked( false )
3704 m_pProperties
= new PropertyMap();
3708 FieldContext::~FieldContext()
3713 void FieldContext::AppendCommand(const OUString
& rPart
)
3715 m_sCommand
+= rPart
;
3718 ::std::vector
<OUString
> FieldContext::GetCommandParts() const
3720 ::std::vector
<OUString
> aResult
;
3721 sal_Int32 nIndex
= 0;
3722 bool bInString
= false;
3724 while (nIndex
!= -1)
3726 OUString sToken
= GetCommand().getToken(0, ' ', nIndex
);
3727 bool bInStringNext
= bInString
;
3729 if (sToken
.isEmpty())
3732 if (sToken
[0] == '"')
3734 bInStringNext
= true;
3735 sToken
= sToken
.copy(1);
3737 if (sToken
.endsWith("\""))
3739 bInStringNext
= false;
3740 sToken
= sToken
.copy(0, sToken
.getLength() - 1);
3745 sPart
+= " " + sToken
;
3748 aResult
.push_back(sPart
);
3759 aResult
.push_back(sToken
);
3763 bInString
= bInStringNext
;
3769 /*-------------------------------------------------------------------------
3770 //collect the pieces of the command
3771 -----------------------------------------------------------------------*/
3772 void DomainMapper_Impl::AppendFieldCommand(OUString
const & rPartOfCommand
)
3775 TagLogger::getInstance().startElement("appendFieldCommand");
3776 TagLogger::getInstance().chars(rPartOfCommand
);
3777 TagLogger::getInstance().endElement();
3780 FieldContextPtr pContext
= m_aFieldStack
.back();
3781 OSL_ENSURE( pContext
.get(), "no field context available");
3782 if( pContext
.get() )
3784 pContext
->AppendCommand( rPartOfCommand
);
3789 typedef std::multimap
< sal_Int32
, OUString
> TOCStyleMap
;
3791 static const FieldConversionMap_t
& lcl_GetFieldConversion()
3793 static const FieldConversionMap_t aFieldConversionMap
3795 // {"ADDRESSBLOCK", {"", FIELD_ADDRESSBLOCK }},
3796 // {"ADVANCE", {"", FIELD_ADVANCE }},
3797 {"ASK", {"SetExpression", FIELD_ASK
}},
3798 {"AUTONUM", {"SetExpression", FIELD_AUTONUM
}},
3799 {"AUTONUMLGL", {"SetExpression", FIELD_AUTONUMLGL
}},
3800 {"AUTONUMOUT", {"SetExpression", FIELD_AUTONUMOUT
}},
3801 {"AUTHOR", {"DocInfo.CreateAuthor", FIELD_AUTHOR
}},
3802 {"DATE", {"DateTime", FIELD_DATE
}},
3803 {"COMMENTS", {"DocInfo.Description", FIELD_COMMENTS
}},
3804 {"CREATEDATE", {"DocInfo.CreateDateTime", FIELD_CREATEDATE
}},
3805 {"DOCPROPERTY", {"", FIELD_DOCPROPERTY
}},
3806 {"DOCVARIABLE", {"User", FIELD_DOCVARIABLE
}},
3807 {"EDITTIME", {"DocInfo.EditTime", FIELD_EDITTIME
}},
3808 {"EQ", {"", FIELD_EQ
}},
3809 {"FILLIN", {"Input", FIELD_FILLIN
}},
3810 {"FILENAME", {"FileName", FIELD_FILENAME
}},
3811 // {"FILESIZE", {"", FIELD_FILESIZE }},
3812 {"FORMULA", {"TableFormula", FIELD_FORMULA
}},
3813 {"FORMCHECKBOX", {"", FIELD_FORMCHECKBOX
}},
3814 {"FORMDROPDOWN", {"DropDown", FIELD_FORMDROPDOWN
}},
3815 {"FORMTEXT", {"Input", FIELD_FORMTEXT
}},
3816 {"GOTOBUTTON", {"", FIELD_GOTOBUTTON
}},
3817 {"HYPERLINK", {"", FIELD_HYPERLINK
}},
3818 {"IF", {"ConditionalText", FIELD_IF
}},
3819 // {"INFO", {"", FIELD_INFO }},
3820 {"INCLUDEPICTURE", {"", FIELD_INCLUDEPICTURE
}},
3821 {"KEYWORDS", {"DocInfo.KeyWords", FIELD_KEYWORDS
}},
3822 {"LASTSAVEDBY", {"DocInfo.ChangeAuthor", FIELD_LASTSAVEDBY
}},
3823 {"MACROBUTTON", {"Macro", FIELD_MACROBUTTON
}},
3824 {"MERGEFIELD", {"Database", FIELD_MERGEFIELD
}},
3825 {"MERGEREC", {"DatabaseNumberOfSet", FIELD_MERGEREC
}},
3826 // {"MERGESEQ", {"", FIELD_MERGESEQ }},
3827 {"NEXT", {"DatabaseNextSet", FIELD_NEXT
}},
3828 {"NEXTIF", {"DatabaseNextSet", FIELD_NEXTIF
}},
3829 {"PAGE", {"PageNumber", FIELD_PAGE
}},
3830 {"PAGEREF", {"GetReference", FIELD_PAGEREF
}},
3831 {"REF", {"GetReference", FIELD_REF
}},
3832 {"REVNUM", {"DocInfo.Revision", FIELD_REVNUM
}},
3833 {"SAVEDATE", {"DocInfo.Change", FIELD_SAVEDATE
}},
3834 // {"SECTION", {"", FIELD_SECTION }},
3835 // {"SECTIONPAGES", {"", FIELD_SECTIONPAGES }},
3836 {"SEQ", {"SetExpression", FIELD_SEQ
}},
3837 {"SET", {"SetExpression", FIELD_SET
}},
3838 // {"SKIPIF", {"", FIELD_SKIPIF }},
3839 // {"STYLEREF", {"", FIELD_STYLEREF }},
3840 {"SUBJECT", {"DocInfo.Subject", FIELD_SUBJECT
}},
3841 {"SYMBOL", {"", FIELD_SYMBOL
}},
3842 {"TEMPLATE", {"TemplateName", FIELD_TEMPLATE
}},
3843 {"TIME", {"DateTime", FIELD_TIME
}},
3844 {"TITLE", {"DocInfo.Title", FIELD_TITLE
}},
3845 {"USERINITIALS", {"Author", FIELD_USERINITIALS
}},
3846 // {"USERADDRESS", {"", FIELD_USERADDRESS }},
3847 {"USERNAME", {"Author", FIELD_USERNAME
}},
3850 {"TOC", {"com.sun.star.text.ContentIndex", FIELD_TOC
}},
3851 {"TC", {"com.sun.star.text.ContentIndexMark", FIELD_TC
}},
3852 {"NUMCHARS", {"CharacterCount", FIELD_NUMCHARS
}},
3853 {"NUMWORDS", {"WordCount", FIELD_NUMWORDS
}},
3854 {"NUMPAGES", {"PageCount", FIELD_NUMPAGES
}},
3855 {"INDEX", {"com.sun.star.text.DocumentIndex", FIELD_INDEX
}},
3856 {"XE", {"com.sun.star.text.DocumentIndexMark", FIELD_XE
}},
3857 {"BIBLIOGRAPHY",{"com.sun.star.text.Bibliography", FIELD_BIBLIOGRAPHY
}},
3858 {"CITATION", {"com.sun.star.text.TextField.Bibliography",FIELD_CITATION
}},
3861 return aFieldConversionMap
;
3864 static const FieldConversionMap_t
& lcl_GetEnhancedFieldConversion()
3866 static const FieldConversionMap_t aEnhancedFieldConversionMap
=
3868 {"FORMCHECKBOX", {"FormFieldmark", FIELD_FORMCHECKBOX
}},
3869 {"FORMDROPDOWN", {"FormFieldmark", FIELD_FORMDROPDOWN
}},
3870 {"FORMTEXT", {"Fieldmark", FIELD_FORMTEXT
}},
3873 return aEnhancedFieldConversionMap
;
3876 void DomainMapper_Impl::handleFieldSet
3877 (const FieldContextPtr
& pContext
,
3878 uno::Reference
< uno::XInterface
> const & xFieldInterface
,
3879 uno::Reference
< beans::XPropertySet
> const& xFieldProperties
)
3881 OUString sVariable
, sHint
;
3883 sVariable
= lcl_ExctractVariableAndHint(pContext
->GetCommand(), sHint
);
3885 // remove surrounding "" if exists
3886 if(sHint
.getLength() >= 2)
3888 OUString sTmp
= sHint
.trim();
3889 if (sTmp
.startsWith("\"") && sTmp
.endsWith("\""))
3891 sHint
= sTmp
.copy(1, sTmp
.getLength() - 2);
3895 // determine field master name
3896 uno::Reference
< beans::XPropertySet
> xMaster
=
3897 FindOrCreateFieldMaster
3898 ("com.sun.star.text.FieldMaster.SetExpression", sVariable
);
3900 // a set field is a string
3901 xMaster
->setPropertyValue(getPropertyName(PROP_SUB_TYPE
), uno::makeAny(text::SetVariableType::STRING
));
3903 // attach the master to the field
3904 uno::Reference
< text::XDependentTextField
> xDependentField
3905 ( xFieldInterface
, uno::UNO_QUERY_THROW
);
3906 xDependentField
->attachTextFieldMaster( xMaster
);
3908 xFieldProperties
->setPropertyValue(getPropertyName(PROP_HINT
), uno::makeAny( sHint
));
3909 xFieldProperties
->setPropertyValue(getPropertyName(PROP_CONTENT
), uno::makeAny( sHint
));
3910 xFieldProperties
->setPropertyValue(getPropertyName(PROP_SUB_TYPE
), uno::makeAny(text::SetVariableType::STRING
));
3912 // Mimic MS Word behavior (hide the SET)
3913 xFieldProperties
->setPropertyValue(getPropertyName(PROP_IS_VISIBLE
), uno::makeAny(false));
3916 void DomainMapper_Impl::handleFieldAsk
3917 (const FieldContextPtr
& pContext
,
3918 uno::Reference
< uno::XInterface
> & xFieldInterface
,
3919 uno::Reference
< beans::XPropertySet
> const& xFieldProperties
)
3921 //doesn the command contain a variable name?
3922 OUString sVariable
, sHint
;
3924 sVariable
= lcl_ExctractVariableAndHint( pContext
->GetCommand(),
3926 if(!sVariable
.isEmpty())
3928 // determine field master name
3929 uno::Reference
< beans::XPropertySet
> xMaster
=
3930 FindOrCreateFieldMaster
3931 ("com.sun.star.text.FieldMaster.SetExpression", sVariable
);
3932 // An ASK field is always a string of characters
3933 xMaster
->setPropertyValue(getPropertyName(PROP_SUB_TYPE
), uno::makeAny(text::SetVariableType::STRING
));
3935 // attach the master to the field
3936 uno::Reference
< text::XDependentTextField
> xDependentField
3937 ( xFieldInterface
, uno::UNO_QUERY_THROW
);
3938 xDependentField
->attachTextFieldMaster( xMaster
);
3940 // set input flag at the field
3941 xFieldProperties
->setPropertyValue(
3942 getPropertyName(PROP_IS_INPUT
), uno::makeAny( true ));
3944 xFieldProperties
->setPropertyValue(
3945 getPropertyName(PROP_HINT
),
3946 uno::makeAny( sHint
));
3947 xFieldProperties
->setPropertyValue(getPropertyName(PROP_SUB_TYPE
), uno::makeAny(text::SetVariableType::STRING
));
3948 // The ASK has no field value to display
3949 xFieldProperties
->setPropertyValue(getPropertyName(PROP_IS_VISIBLE
), uno::makeAny(false));
3953 //don't insert the field
3954 //todo: maybe import a 'normal' input field here?
3955 xFieldInterface
= nullptr;
3959 void DomainMapper_Impl::handleFieldFormula
3960 (const FieldContextPtr
& pContext
,
3961 uno::Reference
< beans::XPropertySet
> const& xFieldProperties
)
3963 OUString command
= pContext
->GetCommand().trim();
3965 // Remove number formatting from \# to end of command
3966 // TODO: handle custom number formatting
3967 sal_Int32 delimPos
= command
.indexOf("\\#");
3970 command
= command
.replaceAt(delimPos
, command
.getLength() - delimPos
, "").trim();
3973 // command must contains = and at least another char
3974 if (command
.getLength() < 2)
3977 // we don't copy the = symbol from the command
3978 OUString formula
= command
.copy(1);
3980 xFieldProperties
->setPropertyValue(getPropertyName(PROP_CONTENT
), uno::makeAny(formula
));
3981 xFieldProperties
->setPropertyValue(getPropertyName(PROP_NUMBER_FORMAT
), uno::makeAny(sal_Int32(0)));
3982 xFieldProperties
->setPropertyValue("IsShowFormula", uno::makeAny(false));
3985 void DomainMapper_Impl::handleRubyEQField( const FieldContextPtr
& pContext
)
3987 const OUString
& rCommand(pContext
->GetCommand());
3988 sal_Int32 nIndex
= 0, nEnd
= 0;
3990 nIndex
= rCommand
.indexOf("\\* jc" );
3994 sal_uInt32 nJc
= rCommand
.getToken(0, ' ',nIndex
).toInt32();
3995 const sal_Int32 aRubyAlignValues
[] =
3997 NS_ooxml::LN_Value_ST_RubyAlign_center
,
3998 NS_ooxml::LN_Value_ST_RubyAlign_distributeLetter
,
3999 NS_ooxml::LN_Value_ST_RubyAlign_distributeSpace
,
4000 NS_ooxml::LN_Value_ST_RubyAlign_left
,
4001 NS_ooxml::LN_Value_ST_RubyAlign_right
,
4002 NS_ooxml::LN_Value_ST_RubyAlign_rightVertical
,
4004 aInfo
.nRubyAlign
= aRubyAlignValues
[(nJc
<SAL_N_ELEMENTS(aRubyAlignValues
))?nJc
:0];
4007 // we don't parse or use the font field in rCommand
4009 nIndex
= rCommand
.indexOf("\\* hps" );
4013 aInfo
.nHps
= rCommand
.getToken(0, ' ',nIndex
).toInt32();
4016 nIndex
= rCommand
.indexOf("\\o");
4017 if (nIndex
== -1 || (nIndex
= rCommand
.indexOf('(', nIndex
)) == -1 || (nEnd
= rCommand
.lastIndexOf(')'))==-1 || nEnd
<= nIndex
)
4020 OUString sRubyParts
= rCommand
.copy(nIndex
+1,nEnd
-nIndex
-1);
4022 OUString sPart1
= sRubyParts
.getToken(0, ',', nIndex
);
4023 OUString sPart2
= sRubyParts
.getToken(0, ',', nIndex
);
4024 if ((nIndex
= sPart1
.indexOf('(')) != -1 && (nEnd
= sPart1
.lastIndexOf(')'))!=-1 && nEnd
> nIndex
)
4026 aInfo
.sRubyText
= sPart1
.copy(nIndex
+1,nEnd
-nIndex
-1);
4029 PropertyMapPtr
pRubyContext(new PropertyMap());
4030 pRubyContext
->InsertProps(GetTopContext());
4033 double fVal
= double(aInfo
.nHps
) / 2.;
4034 uno::Any aVal
= uno::makeAny( fVal
);
4036 pRubyContext
->Insert(PROP_CHAR_HEIGHT
, aVal
);
4037 pRubyContext
->Insert(PROP_CHAR_HEIGHT_ASIAN
, aVal
);
4039 PropertyValueVector_t aProps
= comphelper::sequenceToContainer
< PropertyValueVector_t
>(pRubyContext
->GetPropertyValues());
4040 aInfo
.sRubyStyle
= m_rDMapper
.getOrCreateCharStyle(aProps
, /*bAlwaysCreate=*/false);
4041 PropertyMapPtr
pCharContext(new PropertyMap());
4042 if (m_pLastCharacterContext
.get())
4043 pCharContext
->InsertProps(m_pLastCharacterContext
);
4044 pCharContext
->InsertProps(pContext
->getProperties());
4045 pCharContext
->Insert(PROP_RUBY_TEXT
, uno::makeAny( aInfo
.sRubyText
) );
4046 pCharContext
->Insert(PROP_RUBY_ADJUST
, uno::makeAny(static_cast<sal_Int16
>(ConversionHelper::convertRubyAlign(aInfo
.nRubyAlign
))));
4047 if ( aInfo
.nRubyAlign
== NS_ooxml::LN_Value_ST_RubyAlign_rightVertical
)
4048 pCharContext
->Insert(PROP_RUBY_POSITION
, uno::makeAny(css::text::RubyPosition::INTER_CHARACTER
));
4049 pCharContext
->Insert(PROP_RUBY_STYLE
, uno::makeAny(aInfo
.sRubyStyle
));
4050 appendTextPortion(sPart2
, pCharContext
);
4054 void DomainMapper_Impl::handleAutoNum
4055 (const FieldContextPtr
& pContext
,
4056 uno::Reference
< uno::XInterface
> const & xFieldInterface
,
4057 uno::Reference
< beans::XPropertySet
> const& xFieldProperties
)
4059 //create a sequence field master "AutoNr"
4060 uno::Reference
< beans::XPropertySet
> xMaster
=
4061 FindOrCreateFieldMaster
4062 ("com.sun.star.text.FieldMaster.SetExpression",
4065 xMaster
->setPropertyValue( getPropertyName(PROP_SUB_TYPE
),
4066 uno::makeAny(text::SetVariableType::SEQUENCE
));
4068 //apply the numbering type
4069 xFieldProperties
->setPropertyValue(
4070 getPropertyName(PROP_NUMBERING_TYPE
),
4071 uno::makeAny( lcl_ParseNumberingType(pContext
->GetCommand()) ));
4072 // attach the master to the field
4073 uno::Reference
< text::XDependentTextField
> xDependentField
4074 ( xFieldInterface
, uno::UNO_QUERY_THROW
);
4075 xDependentField
->attachTextFieldMaster( xMaster
);
4078 void DomainMapper_Impl::handleAuthor
4079 (OUString
const& rFirstParam
,
4080 uno::Reference
< beans::XPropertySet
> const& xFieldProperties
,
4083 if ( eFieldId
!= FIELD_USERINITIALS
)
4084 xFieldProperties
->setPropertyValue
4085 ( getPropertyName(PROP_FULL_NAME
), uno::makeAny( true ));
4087 if (!rFirstParam
.isEmpty())
4089 xFieldProperties
->setPropertyValue(
4090 getPropertyName( PROP_IS_FIXED
),
4091 uno::makeAny( true ));
4092 //PROP_CURRENT_PRESENTATION is set later anyway
4096 void DomainMapper_Impl::handleDocProperty
4097 (const FieldContextPtr
& pContext
,
4098 OUString
const& rFirstParam
,
4099 uno::Reference
< uno::XInterface
> & xFieldInterface
)
4101 //some docproperties should be imported as document statistic fields, some as DocInfo fields
4102 //others should be user fields
4103 if (rFirstParam
.isEmpty())
4106 constexpr sal_uInt8 SET_ARABIC
= 0x01;
4107 constexpr sal_uInt8 SET_DATE
= 0x04;
4108 struct DocPropertyMap
4110 const sal_Char
* pDocPropertyName
;
4111 const sal_Char
* pServiceName
;
4114 static const DocPropertyMap aDocProperties
[] =
4116 {"CreateTime", "DocInfo.CreateDateTime", SET_DATE
},
4117 {"Characters", "CharacterCount", SET_ARABIC
},
4118 {"Comments", "DocInfo.Description", 0},
4119 {"Keywords", "DocInfo.KeyWords", 0},
4120 {"LastPrinted", "DocInfo.PrintDateTime", 0},
4121 {"LastSavedBy", "DocInfo.ChangeAuthor", 0},
4122 {"LastSavedTime", "DocInfo.ChangeDateTime", SET_DATE
},
4123 {"Paragraphs", "ParagraphCount", SET_ARABIC
},
4124 {"RevisionNumber", "DocInfo.Revision", 0},
4125 {"Subject", "DocInfo.Subject", 0},
4126 {"Template", "TemplateName", 0},
4127 {"Title", "DocInfo.Title", 0},
4128 {"TotalEditingTime", "DocInfo.EditTime", 0},
4129 {"Words", "WordCount", SET_ARABIC
}
4131 //other available DocProperties:
4132 //Bytes, Category, CharactersWithSpaces, Company
4134 //Lines, Manager, NameofApplication, ODMADocId, Pages,
4137 uno::Reference
<document::XDocumentPropertiesSupplier
> xDocumentPropertiesSupplier(m_xTextDocument
, uno::UNO_QUERY
);
4138 uno::Reference
<document::XDocumentProperties
> xDocumentProperties
= xDocumentPropertiesSupplier
->getDocumentProperties();
4139 uno::Reference
<beans::XPropertySet
> xUserDefinedProps(xDocumentProperties
->getUserDefinedProperties(), uno::UNO_QUERY_THROW
);
4140 uno::Reference
<beans::XPropertySetInfo
> xPropertySetInfo
= xUserDefinedProps
->getPropertySetInfo();
4141 //search for a field mapping
4142 OUString sFieldServiceName
;
4144 for( ; nMap
< SAL_N_ELEMENTS(aDocProperties
); ++nMap
)
4146 if ((rFirstParam
.equalsAscii(aDocProperties
[nMap
].pDocPropertyName
)) && (!xPropertySetInfo
->hasPropertyByName(rFirstParam
)))
4149 OUString::createFromAscii
4150 (aDocProperties
[nMap
].pServiceName
);
4154 OUString
sServiceName("com.sun.star.text.TextField.");
4155 bool bIsCustomField
= false;
4156 if(sFieldServiceName
.isEmpty())
4158 //create a custom property field
4159 sServiceName
+= "DocInfo.Custom";
4160 bIsCustomField
= true;
4164 sServiceName
+= sFieldServiceName
;
4166 if (m_xTextFactory
.is())
4167 xFieldInterface
= m_xTextFactory
->createInstance(sServiceName
);
4168 uno::Reference
<beans::XPropertySet
> xFieldProperties( xFieldInterface
, uno::UNO_QUERY_THROW
);
4169 if( bIsCustomField
)
4171 xFieldProperties
->setPropertyValue(
4172 getPropertyName(PROP_NAME
), uno::makeAny(rFirstParam
));
4173 pContext
->SetCustomField( xFieldProperties
);
4177 if(0 != (aDocProperties
[nMap
].nFlags
& SET_ARABIC
))
4178 xFieldProperties
->setPropertyValue(
4179 getPropertyName(PROP_NUMBERING_TYPE
),
4180 uno::makeAny( style::NumberingType::ARABIC
));
4181 else if(0 != (aDocProperties
[nMap
].nFlags
& SET_DATE
))
4183 xFieldProperties
->setPropertyValue(
4184 getPropertyName(PROP_IS_DATE
),
4185 uno::makeAny( true ));
4186 SetNumberFormat( pContext
->GetCommand(), xFieldProperties
);
4191 static uno::Sequence
< beans::PropertyValues
> lcl_createTOXLevelHyperlinks( bool bHyperlinks
, const OUString
& sChapterNoSeparator
,
4192 const uno::Sequence
< beans::PropertyValues
>& aLevel
)
4194 //create a copy of the level and add two new entries - hyperlink start and end
4195 bool bChapterNoSeparator
= !sChapterNoSeparator
.isEmpty();
4196 sal_Int32 nAdd
= (bHyperlinks
&& bChapterNoSeparator
) ? 4 : 2;
4197 uno::Sequence
< beans::PropertyValues
> aNewLevel( aLevel
.getLength() + nAdd
);
4198 beans::PropertyValues
* pNewLevel
= aNewLevel
.getArray();
4201 beans::PropertyValues
aHyperlink(1);
4202 aHyperlink
[0].Name
= getPropertyName( PROP_TOKEN_TYPE
);
4203 aHyperlink
[0].Value
<<= getPropertyName( PROP_TOKEN_HYPERLINK_START
);
4204 pNewLevel
[0] = aHyperlink
;
4205 aHyperlink
[0].Value
<<= getPropertyName( PROP_TOKEN_HYPERLINK_END
);
4206 pNewLevel
[aNewLevel
.getLength() -1] = aHyperlink
;
4208 if( bChapterNoSeparator
)
4210 beans::PropertyValues
aChapterNo(2);
4211 aChapterNo
[0].Name
= getPropertyName( PROP_TOKEN_TYPE
);
4212 aChapterNo
[0].Value
<<= getPropertyName( PROP_TOKEN_CHAPTER_INFO
);
4213 aChapterNo
[1].Name
= getPropertyName( PROP_CHAPTER_FORMAT
);
4214 //todo: is ChapterFormat::Number correct?
4215 aChapterNo
[1].Value
<<= sal_Int16(text::ChapterFormat::NUMBER
);
4216 pNewLevel
[aNewLevel
.getLength() - (bHyperlinks
? 4 : 2) ] = aChapterNo
;
4218 beans::PropertyValues
aChapterSeparator(2);
4219 aChapterSeparator
[0].Name
= getPropertyName( PROP_TOKEN_TYPE
);
4220 aChapterSeparator
[0].Value
<<= getPropertyName( PROP_TOKEN_TEXT
);
4221 aChapterSeparator
[1].Name
= getPropertyName( PROP_TEXT
);
4222 aChapterSeparator
[1].Value
<<= sChapterNoSeparator
;
4223 pNewLevel
[aNewLevel
.getLength() - (bHyperlinks
? 3 : 1)] = aChapterSeparator
;
4225 //copy the 'old' entries except the last (page no)
4226 std::copy(aLevel
.begin(), std::prev(aLevel
.end()), std::next(aNewLevel
.begin()));
4227 //copy page no entry (last or last but one depending on bHyperlinks
4228 sal_Int32 nPageNo
= aNewLevel
.getLength() - (bHyperlinks
? 2 : 3);
4229 pNewLevel
[nPageNo
] = aLevel
[aLevel
.getLength() - 1];
4234 /// Returns title of the TOC placed in paragraph(s) before TOC field inside STD-frame
4235 OUString
DomainMapper_Impl::extractTocTitle()
4237 if (!m_xSdtEntryStart
.is())
4240 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
4241 if(!xTextAppend
.is())
4244 // try-catch was added in the same way as inside appendTextSectionAfter()
4247 uno::Reference
<text::XParagraphCursor
> xCursor(xTextAppend
->createTextCursorByRange(m_xSdtEntryStart
), uno::UNO_QUERY_THROW
);
4251 //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
4252 xCursor
->gotoStartOfParagraph( false );
4253 if (m_aTextAppendStack
.top().xInsertPosition
.is())
4254 xCursor
->gotoRange( m_aTextAppendStack
.top().xInsertPosition
, true );
4256 xCursor
->gotoEnd( true );
4258 // the paragraph after this new section might have been already inserted
4259 OUString sResult
= xCursor
->getString();
4260 if (sResult
.endsWith(SAL_NEWLINE_STRING
))
4261 sResult
= sResult
.copy(0, sResult
.getLength() - SAL_N_ELEMENTS(SAL_NEWLINE_STRING
) + 1);
4265 catch(const uno::Exception
&)
4272 css::uno::Reference
<css::beans::XPropertySet
>
4273 DomainMapper_Impl::StartIndexSectionChecked(const OUString
& sServiceName
)
4277 finishParagraph(GetTopContextOfType(CONTEXT_PARAGRAPH
), false); // resets m_bParaChanged
4278 PopProperties(CONTEXT_PARAGRAPH
);
4279 PushProperties(CONTEXT_PARAGRAPH
);
4280 SetIsFirstRun(true);
4281 // The first paragraph of the index that is continuation of just finished one needs to be
4282 // removed when finished (unless more content will arrive, which will set m_bParaChanged)
4283 m_bRemoveThisParagraph
= true;
4285 const auto& xTextAppend
= GetTopTextAppend();
4286 const auto xTextRange
= xTextAppend
->getEnd();
4287 const auto xRet
= createSectionForRange(xTextRange
, xTextRange
, sServiceName
, false);
4288 if (!m_aTextAppendStack
.top().xInsertPosition
)
4292 m_bStartedTOC
= true;
4293 uno::Reference
<text::XTextCursor
> xTOCTextCursor
4294 = xTextRange
->getText()->createTextCursor();
4295 xTOCTextCursor
->gotoEnd(false);
4296 m_aTextAppendStack
.push(TextAppendContext(xTextAppend
, xTOCTextCursor
));
4298 catch (const uno::Exception
&)
4300 TOOLS_WARN_EXCEPTION("writerfilter.dmapper",
4301 "DomainMapper_Impl::StartIndexSectionChecked:");
4307 void DomainMapper_Impl::handleToc
4308 (const FieldContextPtr
& pContext
,
4309 const OUString
& sTOCServiceName
)
4312 if (IsInHeaderFooter())
4313 m_bStartTOCHeaderFooter
= true;
4314 bool bTableOfFigures
= false;
4315 bool bHyperlinks
= false;
4316 bool bFromOutline
= false;
4317 bool bFromEntries
= false;
4318 bool bHideTabLeaderPageNumbers
= false ;
4319 bool bIsTabEntry
= false ;
4320 bool bNewLine
= false ;
4321 bool bParagraphOutlineLevel
= false;
4323 sal_Int16 nMaxLevel
= 10;
4325 OUString sChapterNoSeparator
;
4326 OUString sFigureSequence
;
4327 uno::Reference
< beans::XPropertySet
> xTOC
;
4328 OUString aBookmarkName
;
4330 // \a Builds a table of figures but does not include the captions's label and number
4331 if( lcl_FindInCommand( pContext
->GetCommand(), 'a', sValue
))
4332 { //make it a table of figures
4333 bTableOfFigures
= true;
4335 // \b Uses a bookmark to specify area of document from which to build table of contents
4336 if( lcl_FindInCommand( pContext
->GetCommand(), 'b', sValue
))
4338 aBookmarkName
= sValue
.trim().replaceAll("\"","");
4340 if( lcl_FindInCommand( pContext
->GetCommand(), 'c', sValue
))
4341 // \c Builds a table of figures of the given label
4343 //todo: sValue contains the label's name
4344 bTableOfFigures
= true;
4345 sFigureSequence
= sValue
.trim();
4346 sFigureSequence
= sFigureSequence
.replaceAll("\"", "").replaceAll("'","");
4348 // \d Defines the separator between sequence and page numbers
4349 if( lcl_FindInCommand( pContext
->GetCommand(), 'd', sValue
))
4351 //todo: insert the chapter number into each level and insert the separator additionally
4352 sChapterNoSeparator
= sValue
;
4354 // \f Builds a table of contents using TC entries instead of outline levels
4355 if( lcl_FindInCommand( pContext
->GetCommand(), 'f', sValue
))
4357 //todo: sValue can contain a TOC entry identifier - use unclear
4358 bFromEntries
= true;
4360 // \h Hyperlinks the entries and page numbers within the table of contents
4361 if( lcl_FindInCommand( pContext
->GetCommand(), 'h', sValue
))
4363 //todo: make all entries to hyperlinks
4366 // \l Defines the TC entries field level used to build a table of contents
4367 // if( lcl_FindInCommand( pContext->GetCommand(), 'l', sValue ))
4369 //todo: entries can only be included completely
4371 // \n Builds a table of contents or a range of entries, such as 1-9 in a table of contents without page numbers
4372 // if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
4374 //todo: what does the description mean?
4376 // \o Builds a table of contents by using outline levels instead of TC entries
4377 if( lcl_FindInCommand( pContext
->GetCommand(), 'o', sValue
))
4379 bFromOutline
= true;
4380 if (sValue
.isEmpty())
4381 nMaxLevel
= WW_OUTLINE_MAX
;
4384 sal_Int32 nIndex
= 0;
4385 sValue
.getToken( 0, '-', nIndex
);
4386 nMaxLevel
= static_cast<sal_Int16
>(nIndex
!= -1 ? sValue
.copy(nIndex
).toInt32() : 0);
4389 // \p Defines the separator between the table entry and its page number
4390 // \s Builds a table of contents by using a sequence type
4391 // \t Builds a table of contents by using style names other than the standard outline styles
4392 if( lcl_FindInCommand( pContext
->GetCommand(), 't', sValue
))
4394 OUString sToken
= sValue
.getToken(1, '"');
4395 sTemplate
= sToken
.isEmpty() ? sValue
: sToken
;
4397 // \u Builds a table of contents by using the applied paragraph outline level
4398 if( lcl_FindInCommand( pContext
->GetCommand(), 'u', sValue
))
4400 bFromOutline
= true;
4401 bParagraphOutlineLevel
= true;
4402 //todo: what doesn 'the applied paragraph outline level' refer to?
4404 // \w Preserve tab characters within table entries
4405 if( lcl_FindInCommand( pContext
->GetCommand(), 'w', sValue
))
4407 bIsTabEntry
= true ;
4409 // \x Preserve newline characters within table entries
4410 if( lcl_FindInCommand( pContext
->GetCommand(), 'x', sValue
))
4414 // \z Hides page numbers within the table of contents when shown in Web Layout View
4415 if( lcl_FindInCommand( pContext
->GetCommand(), 'z', sValue
))
4417 bHideTabLeaderPageNumbers
= true ;
4420 //if there's no option then it should be created from outline
4421 if( !bFromOutline
&& !bFromEntries
&& sTemplate
.isEmpty() )
4422 bFromOutline
= true;
4424 const OUString aTocTitle
= extractTocTitle();
4426 if (m_xTextFactory
.is() && ! m_aTextAppendStack
.empty())
4428 const auto& xTextAppend
= GetTopTextAppend();
4429 if (aTocTitle
.isEmpty() || bTableOfFigures
)
4431 // reset marker of the TOC title
4432 m_xSdtEntryStart
.clear();
4434 // Create section before setting m_bStartTOC: finishing paragraph
4435 // inside StartIndexSectionChecked could do the wrong thing otherwise
4436 xTOC
= StartIndexSectionChecked(bTableOfFigures
? "com.sun.star.text.IllustrationsIndex"
4439 const auto xTextCursor
= xTextAppend
->getText()->createTextCursor();
4441 xTextCursor
->gotoEnd(false);
4442 xTOCMarkerCursor
= xTextCursor
;
4446 // create TOC section
4447 css::uno::Reference
<css::text::XTextRange
> xTextRangeEndOfTocHeader
= GetTopTextAppend()->getEnd();
4448 xTOC
= createSectionForRange(m_xSdtEntryStart
, xTextRangeEndOfTocHeader
, sTOCServiceName
, false);
4450 // init [xTOCMarkerCursor]
4451 uno::Reference
< text::XText
> xText
= xTextAppend
->getText();
4452 uno::Reference
< text::XTextCursor
> xCrsr
= xText
->createTextCursor();
4453 xTOCMarkerCursor
= xCrsr
;
4455 // create header of the TOC with the TOC title inside
4456 const OUString
aObjectType("com.sun.star.text.IndexHeaderSection");
4457 createSectionForRange(m_xSdtEntryStart
, xTextRangeEndOfTocHeader
, aObjectType
, true);
4464 xTOC
->setPropertyValue(getPropertyName( PROP_TITLE
), uno::makeAny(aTocTitle
));
4466 if (!aBookmarkName
.isEmpty())
4467 xTOC
->setPropertyValue(getPropertyName(PROP_TOC_BOOKMARK
), uno::makeAny(aBookmarkName
));
4468 if( !bTableOfFigures
&& xTOC
.is() )
4470 xTOC
->setPropertyValue( getPropertyName( PROP_LEVEL
), uno::makeAny( nMaxLevel
) );
4471 xTOC
->setPropertyValue( getPropertyName( PROP_CREATE_FROM_OUTLINE
), uno::makeAny( bFromOutline
));
4472 xTOC
->setPropertyValue( getPropertyName( PROP_CREATE_FROM_MARKS
), uno::makeAny( bFromEntries
));
4473 xTOC
->setPropertyValue( getPropertyName( PROP_HIDE_TAB_LEADER_AND_PAGE_NUMBERS
), uno::makeAny( bHideTabLeaderPageNumbers
));
4474 xTOC
->setPropertyValue( getPropertyName( PROP_TAB_IN_TOC
), uno::makeAny( bIsTabEntry
));
4475 xTOC
->setPropertyValue( getPropertyName( PROP_TOC_NEW_LINE
), uno::makeAny( bNewLine
));
4476 xTOC
->setPropertyValue( getPropertyName( PROP_TOC_PARAGRAPH_OUTLINE_LEVEL
), uno::makeAny( bParagraphOutlineLevel
));
4477 if( !sTemplate
.isEmpty() )
4479 //the string contains comma separated the names and related levels
4480 //like: "Heading 1,1,Heading 2,2"
4483 sal_Int32 nPosition
= 0;
4484 while( nPosition
>= 0)
4486 OUString sStyleName
= sTemplate
.getToken( 0, ',', nPosition
);
4487 //empty tokens should be skipped
4488 while( sStyleName
.isEmpty() && nPosition
> 0 )
4489 sStyleName
= sTemplate
.getToken( 0, ',', nPosition
);
4490 nLevel
= sTemplate
.getToken( 0, ',', nPosition
).toInt32();
4493 if( !sStyleName
.isEmpty() )
4494 aMap
.emplace(nLevel
, sStyleName
);
4496 uno::Reference
< container::XIndexReplace
> xParaStyles
;
4497 xTOC
->getPropertyValue(getPropertyName(PROP_LEVEL_PARAGRAPH_STYLES
)) >>= xParaStyles
;
4498 for( nLevel
= 1; nLevel
< 10; ++nLevel
)
4500 sal_Int32 nLevelCount
= aMap
.count( nLevel
);
4503 TOCStyleMap::iterator aTOCStyleIter
= aMap
.find( nLevel
);
4505 uno::Sequence
< OUString
> aStyles( nLevelCount
);
4506 for ( sal_Int32 nStyle
= 0; nStyle
< nLevelCount
; ++nStyle
, ++aTOCStyleIter
)
4508 aStyles
[nStyle
] = aTOCStyleIter
->second
;
4510 xParaStyles
->replaceByIndex(nLevel
- 1, uno::makeAny(aStyles
));
4513 xTOC
->setPropertyValue(getPropertyName(PROP_CREATE_FROM_LEVEL_PARAGRAPH_STYLES
), uno::makeAny( true ));
4516 if(bHyperlinks
|| !sChapterNoSeparator
.isEmpty())
4518 uno::Reference
< container::XIndexReplace
> xLevelFormats
;
4519 xTOC
->getPropertyValue(getPropertyName(PROP_LEVEL_FORMAT
)) >>= xLevelFormats
;
4520 sal_Int32 nLevelCount
= xLevelFormats
->getCount();
4521 //start with level 1, 0 is the header level
4522 for( sal_Int32 nLevel
= 1; nLevel
< nLevelCount
; ++nLevel
)
4524 uno::Sequence
< beans::PropertyValues
> aLevel
;
4525 xLevelFormats
->getByIndex( nLevel
) >>= aLevel
;
4527 uno::Sequence
< beans::PropertyValues
> aNewLevel
= lcl_createTOXLevelHyperlinks(
4528 bHyperlinks
, sChapterNoSeparator
,
4530 xLevelFormats
->replaceByIndex( nLevel
, uno::makeAny( aNewLevel
) );
4534 else if (bTableOfFigures
&& xTOC
.is())
4536 if (!sFigureSequence
.isEmpty())
4537 xTOC
->setPropertyValue(getPropertyName(PROP_LABEL_CATEGORY
),
4538 uno::makeAny(sFigureSequence
));
4542 uno::Reference
< container::XIndexReplace
> xLevelFormats
;
4543 xTOC
->getPropertyValue(getPropertyName(PROP_LEVEL_FORMAT
)) >>= xLevelFormats
;
4544 uno::Sequence
< beans::PropertyValues
> aLevel
;
4545 xLevelFormats
->getByIndex( 1 ) >>= aLevel
;
4547 uno::Sequence
< beans::PropertyValues
> aNewLevel
= lcl_createTOXLevelHyperlinks(
4548 bHyperlinks
, sChapterNoSeparator
,
4550 xLevelFormats
->replaceByIndex( 1, uno::makeAny( aNewLevel
) );
4553 pContext
->SetTOC( xTOC
);
4554 m_bParaHadField
= false;
4557 uno::Reference
<beans::XPropertySet
> DomainMapper_Impl::createSectionForRange(
4558 uno::Reference
< css::text::XTextRange
> xStart
,
4559 uno::Reference
< css::text::XTextRange
> xEnd
,
4560 const OUString
& sObjectType
,
4564 return uno::Reference
<beans::XPropertySet
>();
4566 return uno::Reference
<beans::XPropertySet
>();
4568 uno::Reference
< beans::XPropertySet
> xRet
;
4569 if (m_aTextAppendStack
.empty())
4571 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
4572 if(xTextAppend
.is())
4576 uno::Reference
< text::XParagraphCursor
> xCursor(
4577 xTextAppend
->createTextCursorByRange( xStart
), uno::UNO_QUERY_THROW
);
4578 //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
4579 xCursor
->gotoStartOfParagraph( false );
4580 xCursor
->gotoRange( xEnd
, true );
4581 //the paragraph after this new section is already inserted
4583 xCursor
->goLeft(1, true);
4584 uno::Reference
< text::XTextContent
> xSection( m_xTextFactory
->createInstance(sObjectType
), uno::UNO_QUERY_THROW
);
4585 xSection
->attach( uno::Reference
< text::XTextRange
>( xCursor
, uno::UNO_QUERY_THROW
) );
4586 xRet
.set(xSection
, uno::UNO_QUERY
);
4588 catch(const uno::Exception
&)
4596 void DomainMapper_Impl::handleBibliography
4597 (const FieldContextPtr
& pContext
,
4598 const OUString
& sTOCServiceName
)
4600 if (m_aTextAppendStack
.empty())
4602 // tdf#130214: a workaround to avoid crash on import errors
4603 SAL_WARN("writerfilter.dmapper", "no text append stack");
4606 // Create section before setting m_bStartTOC and m_bStartBibliography: finishing paragraph
4607 // inside StartIndexSectionChecked could do the wrong thing otherwise
4608 const auto xTOC
= StartIndexSectionChecked(sTOCServiceName
);
4610 m_bStartBibliography
= true;
4613 xTOC
->setPropertyValue(getPropertyName( PROP_TITLE
), uno::makeAny(OUString()));
4615 pContext
->SetTOC( xTOC
);
4616 m_bParaHadField
= false;
4618 uno::Reference
< text::XTextContent
> xToInsert( xTOC
, uno::UNO_QUERY
);
4619 appendTextContent(xToInsert
, uno::Sequence
< beans::PropertyValue
>() );
4622 void DomainMapper_Impl::handleIndex
4623 (const FieldContextPtr
& pContext
,
4624 const OUString
& sTOCServiceName
)
4626 // Create section before setting m_bStartTOC and m_bStartIndex: finishing paragraph
4627 // inside StartIndexSectionChecked could do the wrong thing otherwise
4628 const auto xTOC
= StartIndexSectionChecked(sTOCServiceName
);
4631 m_bStartIndex
= true;
4633 OUString sIndexEntryType
= "I"; // Default value for field flag '\f' is 'I'.
4637 xTOC
->setPropertyValue(getPropertyName( PROP_TITLE
), uno::makeAny(OUString()));
4639 if( lcl_FindInCommand( pContext
->GetCommand(), 'r', sValue
))
4641 xTOC
->setPropertyValue("IsCommaSeparated", uno::makeAny(true));
4643 if( lcl_FindInCommand( pContext
->GetCommand(), 'h', sValue
))
4645 xTOC
->setPropertyValue("UseAlphabeticalSeparators", uno::makeAny(true));
4647 if( lcl_FindInCommand( pContext
->GetCommand(), 'f', sValue
))
4649 if(!sValue
.isEmpty())
4650 sIndexEntryType
= sValue
;
4651 xTOC
->setPropertyValue(getPropertyName( PROP_INDEX_ENTRY_TYPE
), uno::makeAny(sIndexEntryType
));
4654 pContext
->SetTOC( xTOC
);
4655 m_bParaHadField
= false;
4657 uno::Reference
< text::XTextContent
> xToInsert( xTOC
, uno::UNO_QUERY
);
4658 appendTextContent(xToInsert
, uno::Sequence
< beans::PropertyValue
>() );
4660 if( lcl_FindInCommand( pContext
->GetCommand(), 'c', sValue
))
4662 sValue
= sValue
.replaceAll("\"", "");
4663 uno::Reference
<text::XTextColumns
> xTextColumns
;
4664 xTOC
->getPropertyValue(getPropertyName( PROP_TEXT_COLUMNS
)) >>= xTextColumns
;
4665 if (xTextColumns
.is())
4667 xTextColumns
->setColumnCount( sValue
.toInt32() );
4668 xTOC
->setPropertyValue( getPropertyName( PROP_TEXT_COLUMNS
), uno::makeAny( xTextColumns
) );
4673 static auto InsertFieldmark(std::stack
<TextAppendContext
> & rTextAppendStack
,
4674 uno::Reference
<text::XFormField
> const& xFormField
,
4675 uno::Reference
<text::XTextRange
> const& xStartRange
,
4676 std::optional
<FieldId
> const oFieldId
) -> void
4678 uno::Reference
<text::XTextContent
> const xTextContent(xFormField
, uno::UNO_QUERY_THROW
);
4679 uno::Reference
<text::XTextAppend
> const& xTextAppend(rTextAppendStack
.top().xTextAppend
);
4680 uno::Reference
<text::XTextCursor
> const xCursor
=
4681 xTextAppend
->createTextCursorByRange(xStartRange
);
4682 if (rTextAppendStack
.top().xInsertPosition
.is())
4684 uno::Reference
<text::XTextRangeCompare
> const xCompare(
4685 rTextAppendStack
.top().xTextAppend
,
4687 if (xCompare
->compareRegionStarts(xStartRange
, rTextAppendStack
.top().xInsertPosition
) < 0)
4689 SAL_WARN("writerfilter.dmapper", "invalid field mark positions");
4692 xCursor
->gotoRange(rTextAppendStack
.top().xInsertPosition
, true);
4696 xCursor
->gotoEnd(true);
4698 xTextAppend
->insertTextContent(xCursor
, xTextContent
, true);
4700 && (oFieldId
== FIELD_FORMCHECKBOX
|| oFieldId
== FIELD_FORMDROPDOWN
))
4702 return; // only a single CH_TXT_ATR_FORMELEMENT!
4704 // problem: the fieldmark must be inserted in CloseFieldCommand(), because
4705 // attach() takes 2 positions, not 3!
4706 // FAIL: AppendTextNode() ignores the content index!
4707 // plan B: insert a spurious paragraph break now and join
4708 // it in PopFieldContext()!
4709 xCursor
->gotoRange(xTextContent
->getAnchor()->getEnd(), false);
4710 xCursor
->goLeft(1, false); // skip CH_TXT_ATR_FIELDEND
4711 xTextAppend
->insertControlCharacter(xCursor
, text::ControlCharacter::PARAGRAPH_BREAK
, false);
4712 xCursor
->goLeft(1, false); // back to previous paragraph
4713 rTextAppendStack
.push(TextAppendContext(xTextAppend
, xCursor
));
4716 static auto PopFieldmark(std::stack
<TextAppendContext
> & rTextAppendStack
,
4717 uno::Reference
<text::XTextCursor
> const& xCursor
,
4718 std::optional
<FieldId
> const oFieldId
) -> void
4721 && (oFieldId
== FIELD_FORMCHECKBOX
|| oFieldId
== FIELD_FORMDROPDOWN
))
4723 return; // only a single CH_TXT_ATR_FORMELEMENT!
4725 xCursor
->gotoRange(rTextAppendStack
.top().xInsertPosition
, false);
4726 xCursor
->goRight(1, true);
4727 xCursor
->setString(OUString()); // undo SplitNode from CloseFieldCommand()
4728 // note: paragraph properties will be overwritten
4729 // by finishParagraph() anyway so ignore here
4730 rTextAppendStack
.pop();
4733 void DomainMapper_Impl::CloseFieldCommand()
4735 if(m_bDiscardHeaderFooter
)
4738 TagLogger::getInstance().element("closeFieldCommand");
4741 FieldContextPtr pContext
;
4742 if(!m_aFieldStack
.empty())
4743 pContext
= m_aFieldStack
.back();
4744 OSL_ENSURE( pContext
.get(), "no field context available");
4745 if( pContext
.get() )
4747 m_bSetUserFieldContent
= false;
4748 m_bSetCitation
= false;
4749 m_bSetDateValue
= false;
4750 const FieldConversionMap_t
& aFieldConversionMap
= lcl_GetFieldConversion();
4754 uno::Reference
< uno::XInterface
> xFieldInterface
;
4756 const auto& [sType
, vArguments
, vSwitches
]{ splitFieldCommand(pContext
->GetCommand()) };
4758 OUString
const sFirstParam(vArguments
.empty() ? OUString() : vArguments
.front());
4760 // apply font size to the form control
4761 if ( m_pLastCharacterContext
.get() && m_pLastCharacterContext
->isSet(PROP_CHAR_HEIGHT
) )
4763 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
4764 if (xTextAppend
.is())
4766 uno::Reference
< text::XTextCursor
> xCrsr
= xTextAppend
->getText()->createTextCursor();
4767 uno::Reference
< text::XText
> xText
= xTextAppend
->getText();
4768 if(xCrsr
.is() && xText
.is())
4770 xCrsr
->gotoEnd(false);
4771 uno::Reference
< beans::XPropertySet
> xProp( xCrsr
, uno::UNO_QUERY
);
4772 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_HEIGHT
), m_pLastCharacterContext
->getProperty(PROP_CHAR_HEIGHT
)->second
);
4773 if ( m_pLastCharacterContext
->isSet(PROP_CHAR_HEIGHT_COMPLEX
) )
4774 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_HEIGHT_COMPLEX
), m_pLastCharacterContext
->getProperty(PROP_CHAR_HEIGHT_COMPLEX
)->second
);
4779 FieldConversionMap_t::const_iterator
const aIt
= aFieldConversionMap
.find(sType
);
4780 if (aIt
!= aFieldConversionMap
.end()
4781 && (!m_bForceGenericFields
4782 // these need to convert ffData to properties...
4783 || (aIt
->second
.eFieldId
== FIELD_FORMCHECKBOX
)
4784 || (aIt
->second
.eFieldId
== FIELD_FORMDROPDOWN
)
4785 || (aIt
->second
.eFieldId
== FIELD_FORMTEXT
)))
4787 pContext
->SetFieldId(aIt
->second
.eFieldId
);
4788 bool bCreateEnhancedField
= false;
4789 uno::Reference
< beans::XPropertySet
> xFieldProperties
;
4790 bool bCreateField
= true;
4791 switch (aIt
->second
.eFieldId
)
4793 case FIELD_HYPERLINK
:
4794 case FIELD_DOCPROPERTY
:
4798 case FIELD_BIBLIOGRAPHY
:
4799 case FIELD_CITATION
:
4802 case FIELD_INCLUDEPICTURE
:
4804 case FIELD_GOTOBUTTON
:
4805 bCreateField
= false;
4807 case FIELD_FORMCHECKBOX
:
4808 case FIELD_FORMTEXT
:
4809 case FIELD_FORMDROPDOWN
:
4811 // If we use 'enhanced' fields then FIELD_FORMCHECKBOX,
4812 // FIELD_FORMTEXT & FIELD_FORMDROPDOWN are treated specially
4813 if ( m_bUsingEnhancedFields
)
4815 bCreateField
= false;
4816 bCreateEnhancedField
= true;
4818 // for non enhanced fields checkboxes are displayed
4819 // as an awt control not a field
4820 else if ( aIt
->second
.eFieldId
== FIELD_FORMCHECKBOX
)
4821 bCreateField
= false;
4826 FieldContextPtr pOuter
= GetParentFieldContext(m_aFieldStack
);
4829 if (!IsFieldNestingAllowed(pOuter
, m_aFieldStack
.back()))
4831 // Parent field can't host this child field: don't create a child field
4833 bCreateField
= false;
4839 if (m_bStartTOC
&& (aIt
->second
.eFieldId
== FIELD_PAGEREF
) )
4841 bCreateField
= false;
4844 if( bCreateField
|| bCreateEnhancedField
)
4846 //add the service prefix
4847 OUString
sServiceName("com.sun.star.text.");
4848 if ( bCreateEnhancedField
)
4850 const FieldConversionMap_t
& aEnhancedFieldConversionMap
= lcl_GetEnhancedFieldConversion();
4851 FieldConversionMap_t::const_iterator aEnhancedIt
=
4852 aEnhancedFieldConversionMap
.find(sType
);
4853 if ( aEnhancedIt
!= aEnhancedFieldConversionMap
.end())
4854 sServiceName
+= OUString::createFromAscii(aEnhancedIt
->second
.cFieldServiceName
);
4858 sServiceName
+= "TextField." + OUString::createFromAscii(aIt
->second
.cFieldServiceName
);
4862 TagLogger::getInstance().startElement("fieldService");
4863 TagLogger::getInstance().chars(sServiceName
);
4864 TagLogger::getInstance().endElement();
4867 if (m_xTextFactory
.is())
4869 xFieldInterface
= m_xTextFactory
->createInstance(sServiceName
);
4870 xFieldProperties
.set( xFieldInterface
, uno::UNO_QUERY_THROW
);
4873 switch( aIt
->second
.eFieldId
)
4875 case FIELD_ADDRESSBLOCK
: break;
4876 case FIELD_ADVANCE
: break;
4878 handleFieldAsk(pContext
, xFieldInterface
, xFieldProperties
);
4880 case FIELD_AUTONUM
:
4881 case FIELD_AUTONUMLGL
:
4882 case FIELD_AUTONUMOUT
:
4883 handleAutoNum(pContext
, xFieldInterface
, xFieldProperties
);
4886 case FIELD_USERNAME
:
4887 case FIELD_USERINITIALS
:
4888 handleAuthor(sFirstParam
,
4890 aIt
->second
.eFieldId
);
4893 if (xFieldProperties
.is())
4895 // Get field fixed property from the context handler
4896 if (pContext
->IsFieldLocked())
4898 xFieldProperties
->setPropertyValue(
4899 getPropertyName(PROP_IS_FIXED
),
4900 uno::makeAny( true ));
4901 m_bSetDateValue
= true;
4904 xFieldProperties
->setPropertyValue(
4905 getPropertyName(PROP_IS_FIXED
),
4906 uno::makeAny( false ));
4908 xFieldProperties
->setPropertyValue(
4909 getPropertyName(PROP_IS_DATE
),
4910 uno::makeAny( true ));
4911 SetNumberFormat( pContext
->GetCommand(), xFieldProperties
);
4914 case FIELD_COMMENTS
:
4916 // OUString sParam = lcl_ExtractParameter(pContext->GetCommand(), sizeof(" COMMENTS") );
4917 // A parameter with COMMENTS shouldn't set fixed
4918 // ( or at least the binary filter doesn't )
4919 // If we set fixed then we won't export a field cmd.
4920 // Additionally the para in COMMENTS is more like an
4921 // instruction to set the document property comments
4922 // with the param ( e.g. each COMMENT with a param will
4923 // overwrite the Comments document property
4924 // #TODO implement the above too
4925 xFieldProperties
->setPropertyValue(
4926 getPropertyName( PROP_IS_FIXED
), uno::makeAny( false ));
4927 //PROP_CURRENT_PRESENTATION is set later anyway
4930 case FIELD_CREATEDATE
:
4932 xFieldProperties
->setPropertyValue(
4933 getPropertyName( PROP_IS_DATE
), uno::makeAny( true ));
4934 SetNumberFormat( pContext
->GetCommand(), xFieldProperties
);
4937 case FIELD_DOCPROPERTY
:
4938 handleDocProperty(pContext
, sFirstParam
,
4941 case FIELD_DOCVARIABLE
:
4943 //create a user field and type
4944 uno::Reference
< beans::XPropertySet
> xMaster
=
4945 FindOrCreateFieldMaster("com.sun.star.text.FieldMaster.User", sFirstParam
);
4946 uno::Reference
< text::XDependentTextField
> xDependentField( xFieldInterface
, uno::UNO_QUERY_THROW
);
4947 xDependentField
->attachTextFieldMaster( xMaster
);
4948 m_bSetUserFieldContent
= true;
4951 case FIELD_EDITTIME
:
4952 //it's a numbering type, no number format! SetNumberFormat( pContext->GetCommand(), xFieldProperties );
4956 OUString aCommand
= pContext
->GetCommand().trim();
4958 msfilter::util::EquationResult
aResult(msfilter::util::ParseCombinedChars(aCommand
));
4959 if (!aResult
.sType
.isEmpty() && m_xTextFactory
.is())
4961 xFieldInterface
= m_xTextFactory
->createInstance("com.sun.star.text.TextField." + aResult
.sType
);
4963 uno::Reference
< beans::XPropertySet
>( xFieldInterface
,
4964 uno::UNO_QUERY_THROW
);
4965 xFieldProperties
->setPropertyValue(getPropertyName(PROP_CONTENT
), uno::makeAny(aResult
.sResult
));
4969 //merge Read_SubF_Ruby into filter/.../util.cxx and reuse that ?
4970 sal_Int32 nSpaceIndex
= aCommand
.indexOf(' ');
4972 aCommand
= aCommand
.copy(nSpaceIndex
).trim();
4973 if (aCommand
.startsWith("\\s"))
4975 aCommand
= aCommand
.copy(2);
4976 if (aCommand
.startsWith("\\do"))
4978 aCommand
= aCommand
.copy(3);
4979 sal_Int32 nStartIndex
= aCommand
.indexOf('(');
4980 sal_Int32 nEndIndex
= aCommand
.indexOf(')');
4981 if (nStartIndex
> 0 && nEndIndex
> 0)
4983 // nDown is the requested "lower by" value in points.
4984 sal_Int32 nDown
= aCommand
.copy(0, nStartIndex
).toInt32();
4985 OUString aContent
= aCommand
.copy(nStartIndex
+ 1, nEndIndex
- nStartIndex
- 1);
4986 PropertyMapPtr pCharContext
= GetTopContext();
4987 // dHeight is the font size of the current style.
4989 if ((GetPropertyFromParaStyleSheet(PROP_CHAR_HEIGHT
) >>= dHeight
) && dHeight
!= 0)
4990 // Character escapement should be given in negative percents for subscripts.
4991 pCharContext
->Insert(PROP_CHAR_ESCAPEMENT
, uno::makeAny( sal_Int16(- 100 * nDown
/ dHeight
) ) );
4992 appendTextPortion(aContent
, pCharContext
);
4996 else if (aCommand
.startsWith("\\* jc"))
4998 handleRubyEQField(pContext
);
5004 if (xFieldProperties
.is())
5005 xFieldProperties
->setPropertyValue(
5006 getPropertyName(PROP_HINT
), uno::makeAny( pContext
->GetCommand().getToken(1, '\"')));
5008 case FIELD_FILENAME
:
5010 sal_Int32 nNumberingTypeIndex
= pContext
->GetCommand().indexOf("\\p");
5011 if (xFieldProperties
.is())
5012 xFieldProperties
->setPropertyValue(
5013 getPropertyName(PROP_FILE_FORMAT
),
5014 uno::makeAny( nNumberingTypeIndex
> 0 ? text::FilenameDisplayFormat::FULL
: text::FilenameDisplayFormat::NAME_AND_EXT
));
5017 case FIELD_FILESIZE
: break;
5018 case FIELD_FORMULA
:
5019 handleFieldFormula(pContext
, xFieldProperties
);
5021 case FIELD_FORMCHECKBOX
:
5022 case FIELD_FORMDROPDOWN
:
5023 case FIELD_FORMTEXT
:
5025 uno::Reference
< text::XTextField
> xTextField( xFieldInterface
, uno::UNO_QUERY
);
5026 if ( !xTextField
.is() )
5028 FFDataHandler::Pointer_t
5029 pFFDataHandler(pContext
->getFFDataHandler());
5030 FormControlHelper::Pointer_t
5031 pFormControlHelper(new FormControlHelper
5032 (m_bUsingEnhancedFields
? aIt
->second
.eFieldId
: FIELD_FORMCHECKBOX
,
5034 m_xTextDocument
, pFFDataHandler
));
5035 pContext
->setFormControlHelper(pFormControlHelper
);
5036 uno::Reference
< text::XFormField
> xFormField( xFieldInterface
, uno::UNO_QUERY
);
5037 uno::Reference
< container::XNamed
> xNamed( xFormField
, uno::UNO_QUERY
);
5040 if ( pFFDataHandler
&& !pFFDataHandler
->getName().isEmpty() )
5041 xNamed
->setName( pFFDataHandler
->getName() );
5042 pContext
->SetFormField( xFormField
);
5044 InsertFieldmark(m_aTextAppendStack
,
5045 xFormField
, pContext
->GetStartRange(),
5046 pContext
->GetFieldId());
5050 if ( aIt
->second
.eFieldId
== FIELD_FORMDROPDOWN
)
5051 lcl_handleDropdownField( xFieldProperties
, pContext
->getFFDataHandler() );
5053 lcl_handleTextField( xFieldProperties
, pContext
->getFFDataHandler() );
5057 case FIELD_GOTOBUTTON
: break;
5058 case FIELD_HYPERLINK
:
5060 ::std::vector
<OUString
> aParts
= pContext
->GetCommandParts();
5062 // Syntax is either:
5063 // HYPERLINK "" \l "link"
5065 // HYPERLINK \l "link"
5066 // Make sure "HYPERLINK" doesn't end up as part of link in the second case.
5067 if (!aParts
.empty() && aParts
[0] == "HYPERLINK")
5068 aParts
.erase(aParts
.begin());
5070 ::std::vector
<OUString
>::const_iterator aItEnd
= aParts
.end();
5071 ::std::vector
<OUString
>::const_iterator aPartIt
= aParts
.begin();
5076 while (aPartIt
!= aItEnd
)
5078 if ( *aPartIt
== "\\l" )
5082 if (aPartIt
== aItEnd
)
5085 sURL
+= "#" + *aPartIt
;
5087 else if (*aPartIt
== "\\m" || *aPartIt
== "\\n" || *aPartIt
== "\\h")
5090 else if ( *aPartIt
== "\\o" || *aPartIt
== "\\t" )
5094 if (aPartIt
== aItEnd
)
5107 if (!sURL
.isEmpty())
5109 if (sURL
.startsWith("file:///"))
5111 // file:///absolute\\path\\to\\file => invalid file URI (Writer cannot open)
5112 // convert all double backslashes to slashes:
5113 sURL
= sURL
.replaceAll("\\\\", "/");
5115 // file:///absolute\path\to\file => invalid file URI (Writer cannot open)
5116 // convert all backslashes to slashes:
5117 sURL
= sURL
.replace('\\', '/');
5119 // Try to make absolute any relative URLs, except
5120 // for relative same-document URLs that only contain
5122 else if (!sURL
.startsWith("#")) {
5124 sURL
= rtl::Uri::convertRelToAbs(
5126 } catch (rtl::MalformedUriException
& e
) {
5128 "writerfilter.dmapper",
5129 "MalformedUriException "
5133 pContext
->SetHyperlinkURL(sURL
);
5136 if (!sTarget
.isEmpty())
5137 pContext
->SetHyperlinkTarget(sTarget
);
5140 case FIELD_IF
: break;
5141 case FIELD_INFO
: break;
5142 case FIELD_INCLUDEPICTURE
: break;
5143 case FIELD_KEYWORDS
:
5145 if (!sFirstParam
.isEmpty())
5147 xFieldProperties
->setPropertyValue(
5148 getPropertyName( PROP_IS_FIXED
), uno::makeAny( true ));
5149 //PROP_CURRENT_PRESENTATION is set later anyway
5153 case FIELD_LASTSAVEDBY
: break;
5154 case FIELD_MACROBUTTON
:
5156 //extract macro name
5157 sal_Int32 nIndex
= sizeof(" MACROBUTTON ");
5158 OUString sMacro
= pContext
->GetCommand().getToken( 0, ' ', nIndex
);
5159 if (xFieldProperties
.is())
5160 xFieldProperties
->setPropertyValue(
5161 getPropertyName(PROP_MACRO_NAME
), uno::makeAny( sMacro
));
5163 //extract quick help text
5164 if(xFieldProperties
.is() && pContext
->GetCommand().getLength() > nIndex
+ 1)
5166 xFieldProperties
->setPropertyValue(
5167 getPropertyName(PROP_HINT
),
5168 uno::makeAny( pContext
->GetCommand().copy( nIndex
)));
5172 case FIELD_MERGEFIELD
:
5174 //todo: create a database field and fieldmaster pointing to a column, only
5175 //create a user field and type
5176 uno::Reference
< beans::XPropertySet
> xMaster
=
5177 FindOrCreateFieldMaster("com.sun.star.text.FieldMaster.Database", sFirstParam
);
5179 // xFieldProperties->setPropertyValue(
5181 // uno::makeAny( pContext->GetCommand().copy( nIndex + 1 )));
5182 uno::Reference
< text::XDependentTextField
> xDependentField( xFieldInterface
, uno::UNO_QUERY_THROW
);
5183 xDependentField
->attachTextFieldMaster( xMaster
);
5186 case FIELD_MERGEREC
: break;
5187 case FIELD_MERGESEQ
: break;
5188 case FIELD_NEXT
: break;
5189 case FIELD_NEXTIF
: break;
5191 if (xFieldProperties
.is())
5193 xFieldProperties
->setPropertyValue(
5194 getPropertyName(PROP_NUMBERING_TYPE
),
5195 uno::makeAny( lcl_ParseNumberingType(pContext
->GetCommand()) ));
5196 xFieldProperties
->setPropertyValue(
5197 getPropertyName(PROP_SUB_TYPE
),
5198 uno::makeAny( text::PageNumberType_CURRENT
));
5204 if (xFieldProperties
.is() && !m_bStartTOC
)
5206 bool bPageRef
= aIt
->second
.eFieldId
== FIELD_PAGEREF
;
5208 // Do we need a GetReference (default) or a GetExpression field?
5209 uno::Reference
< text::XTextFieldsSupplier
> xFieldsSupplier( GetTextDocument(), uno::UNO_QUERY
);
5210 uno::Reference
< container::XNameAccess
> xFieldMasterAccess
= xFieldsSupplier
->getTextFieldMasters();
5212 if (!xFieldMasterAccess
->hasByName(
5213 "com.sun.star.text.FieldMaster.SetExpression."
5216 xFieldProperties
->setPropertyValue(
5217 getPropertyName(PROP_REFERENCE_FIELD_SOURCE
),
5218 uno::makeAny( sal_Int16(text::ReferenceFieldSource::BOOKMARK
)) );
5219 xFieldProperties
->setPropertyValue(
5220 getPropertyName(PROP_SOURCE_NAME
),
5221 uno::makeAny(sFirstParam
) );
5222 sal_Int16 nFieldPart
= (bPageRef
? text::ReferenceFieldPart::PAGE
: text::ReferenceFieldPart::TEXT
);
5224 if( lcl_FindInCommand( pContext
->GetCommand(), 'p', sValue
))
5227 nFieldPart
= text::ReferenceFieldPart::UP_DOWN
;
5229 else if( lcl_FindInCommand( pContext
->GetCommand(), 'r', sValue
))
5232 nFieldPart
= text::ReferenceFieldPart::NUMBER
;
5234 else if( lcl_FindInCommand( pContext
->GetCommand(), 'n', sValue
))
5237 nFieldPart
= text::ReferenceFieldPart::NUMBER_NO_CONTEXT
;
5239 else if( lcl_FindInCommand( pContext
->GetCommand(), 'w', sValue
))
5241 //number-full-context
5242 nFieldPart
= text::ReferenceFieldPart::NUMBER_FULL_CONTEXT
;
5244 xFieldProperties
->setPropertyValue(
5245 getPropertyName( PROP_REFERENCE_FIELD_PART
), uno::makeAny( nFieldPart
));
5247 else if( m_xTextFactory
.is() )
5249 xFieldInterface
= m_xTextFactory
->createInstance("com.sun.star.text.TextField.GetExpression");
5250 xFieldProperties
.set(xFieldInterface
, uno::UNO_QUERY
);
5251 xFieldProperties
->setPropertyValue(
5252 getPropertyName(PROP_CONTENT
),
5253 uno::makeAny(sFirstParam
));
5254 xFieldProperties
->setPropertyValue(getPropertyName(PROP_SUB_TYPE
), uno::makeAny(text::SetVariableType::STRING
));
5258 case FIELD_REVNUM
: break;
5259 case FIELD_SAVEDATE
:
5260 SetNumberFormat( pContext
->GetCommand(), xFieldProperties
);
5262 case FIELD_SECTION
: break;
5263 case FIELD_SECTIONPAGES
: break;
5266 // command looks like: " SEQ Table \* ARABIC "
5267 OUString
sCmd(pContext
->GetCommand());
5268 // find the sequence name, e.g. "SEQ"
5269 OUString sSeqName
= msfilter::util::findQuotedText(sCmd
, "SEQ ", '\\');
5270 sSeqName
= sSeqName
.trim();
5272 // create a sequence field master using the sequence name
5273 uno::Reference
< beans::XPropertySet
> xMaster
= FindOrCreateFieldMaster(
5274 "com.sun.star.text.FieldMaster.SetExpression",
5277 xMaster
->setPropertyValue(
5278 getPropertyName(PROP_SUB_TYPE
),
5279 uno::makeAny(text::SetVariableType::SEQUENCE
));
5281 // apply the numbering type
5282 xFieldProperties
->setPropertyValue(
5283 getPropertyName(PROP_NUMBERING_TYPE
),
5284 uno::makeAny( lcl_ParseNumberingType(pContext
->GetCommand()) ));
5286 // attach the master to the field
5287 uno::Reference
< text::XDependentTextField
> xDependentField( xFieldInterface
, uno::UNO_QUERY_THROW
);
5288 xDependentField
->attachTextFieldMaster( xMaster
);
5290 OUString sFormula
= sSeqName
+ "+1";
5292 if( lcl_FindInCommand( pContext
->GetCommand(), 'c', sValue
))
5294 sFormula
= sSeqName
;
5296 else if( lcl_FindInCommand( pContext
->GetCommand(), 'r', sValue
))
5300 // TODO \s isn't handled, but the spec isn't easy to understand without
5301 // an example for this one.
5302 xFieldProperties
->setPropertyValue(
5303 getPropertyName(PROP_CONTENT
),
5304 uno::makeAny(sFormula
));
5306 // Take care of the numeric formatting definition, default is Arabic
5307 sal_Int16 nNumberingType
= lcl_ParseNumberingType(pContext
->GetCommand());
5308 if (nNumberingType
== style::NumberingType::PAGE_DESCRIPTOR
)
5309 nNumberingType
= style::NumberingType::ARABIC
;
5310 xFieldProperties
->setPropertyValue(
5311 getPropertyName(PROP_NUMBERING_TYPE
),
5312 uno::makeAny(nNumberingType
));
5316 handleFieldSet(pContext
, xFieldInterface
, xFieldProperties
);
5318 case FIELD_SKIPIF
: break;
5319 case FIELD_STYLEREF
: break;
5320 case FIELD_SUBJECT
:
5322 if (!sFirstParam
.isEmpty())
5324 xFieldProperties
->setPropertyValue(
5325 getPropertyName( PROP_IS_FIXED
), uno::makeAny( true ));
5326 //PROP_CURRENT_PRESENTATION is set later anyway
5332 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
5333 OUString
sSymbol( sal_Unicode( sFirstParam
.startsWithIgnoreAsciiCase("0x") ? sFirstParam
.copy(2).toUInt32(16) : sFirstParam
.toUInt32() ) );
5335 bool bHasFont
= lcl_FindInCommand( pContext
->GetCommand(), 'f', sFont
);
5338 sFont
= sFont
.trim();
5339 if (sFont
.startsWith("\""))
5340 sFont
= sFont
.copy(1);
5341 if (sFont
.endsWith("\""))
5342 sFont
= sFont
.copy(0,sFont
.getLength()-1);
5347 if (xTextAppend
.is())
5349 uno::Reference
< text::XTextCursor
> xCrsr
= xTextAppend
->getText()->createTextCursor();
5350 uno::Reference
< text::XText
> xText
= xTextAppend
->getText();
5351 if(xCrsr
.is() && xText
.is())
5353 xCrsr
->gotoEnd(false);
5354 xText
->insertString(xCrsr
, sSymbol
, true);
5355 uno::Reference
< beans::XPropertySet
> xProp( xCrsr
, uno::UNO_QUERY
);
5356 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_FONT_CHAR_SET
), uno::makeAny(awt::CharSet::SYMBOL
));
5359 uno::Any aVal
= uno::makeAny( sFont
);
5360 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME
), aVal
);
5361 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME_ASIAN
), aVal
);
5362 xProp
->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME_COMPLEX
), aVal
);
5369 case FIELD_TEMPLATE
: break;
5372 if (pContext
->IsFieldLocked())
5374 xFieldProperties
->setPropertyValue(
5375 getPropertyName(PROP_IS_FIXED
),
5376 uno::makeAny( true ));
5377 m_bSetDateValue
= true;
5379 SetNumberFormat( pContext
->GetCommand(), xFieldProperties
);
5384 if (!sFirstParam
.isEmpty())
5386 xFieldProperties
->setPropertyValue(
5387 getPropertyName( PROP_IS_FIXED
), uno::makeAny( true ));
5388 //PROP_CURRENT_PRESENTATION is set later anyway
5392 case FIELD_USERADDRESS
: //todo: user address collects street, city ...
5395 handleIndex(pContext
,
5396 OUString::createFromAscii(aIt
->second
.cFieldServiceName
));
5398 case FIELD_BIBLIOGRAPHY
:
5399 handleBibliography(pContext
,
5400 OUString::createFromAscii(aIt
->second
.cFieldServiceName
));
5404 OUString::createFromAscii(aIt
->second
.cFieldServiceName
));
5408 if( !m_xTextFactory
.is() )
5411 uno::Reference
< beans::XPropertySet
> xTC(
5412 m_xTextFactory
->createInstance(
5413 OUString::createFromAscii(aIt
->second
.cFieldServiceName
)),
5414 uno::UNO_QUERY_THROW
);
5415 if (!sFirstParam
.isEmpty())
5417 xTC
->setPropertyValue("PrimaryKey",
5418 uno::makeAny(sFirstParam
));
5420 uno::Reference
< text::XTextContent
> xToInsert( xTC
, uno::UNO_QUERY
);
5421 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
5422 if (xTextAppend
.is())
5424 uno::Reference
< text::XTextCursor
> xCrsr
= xTextAppend
->getText()->createTextCursor();
5426 uno::Reference
< text::XText
> xText
= xTextAppend
->getText();
5427 if(xCrsr
.is() && xText
.is())
5429 xCrsr
->gotoEnd(false);
5430 xText
->insertTextContent(uno::Reference
< text::XTextRange
>( xCrsr
, uno::UNO_QUERY_THROW
), xToInsert
, false);
5435 case FIELD_CITATION
:
5437 if( !m_xTextFactory
.is() )
5440 xFieldInterface
= m_xTextFactory
->createInstance(
5441 OUString::createFromAscii(aIt
->second
.cFieldServiceName
));
5442 uno::Reference
< beans::XPropertySet
> xTC(xFieldInterface
,
5443 uno::UNO_QUERY_THROW
);
5444 OUString
sCmd(pContext
->GetCommand());//sCmd is the entire instrText including the index e.g. CITATION Kra06 \l 1033
5445 if( !sCmd
.isEmpty()){
5446 uno::Sequence
<beans::PropertyValue
> aValues( comphelper::InitPropertySequence({
5447 { "Identifier", uno::Any(sCmd
) }
5449 xTC
->setPropertyValue("Fields", uno::makeAny(aValues
));
5451 uno::Reference
< text::XTextContent
> xToInsert( xTC
, uno::UNO_QUERY
);
5453 uno::Sequence
<beans::PropertyValue
> aValues
5454 = m_aFieldStack
.back()->getProperties()->GetPropertyValues();
5455 appendTextContent(xToInsert
, aValues
);
5456 m_bSetCitation
= true;
5462 if( !m_xTextFactory
.is() )
5465 uno::Reference
< beans::XPropertySet
> xTC(
5466 m_xTextFactory
->createInstance(
5467 OUString::createFromAscii(aIt
->second
.cFieldServiceName
)),
5468 uno::UNO_QUERY_THROW
);
5469 if (!sFirstParam
.isEmpty())
5471 xTC
->setPropertyValue(getPropertyName(PROP_ALTERNATIVE_TEXT
),
5472 uno::makeAny(sFirstParam
));
5475 // \f TC entry in doc with multiple tables
5476 // if( lcl_FindInCommand( pContext->GetCommand(), 'f', sValue ))
5478 // todo: unsupported
5480 if( lcl_FindInCommand( pContext
->GetCommand(), 'l', sValue
))
5483 sal_Int32 nLevel
= sValue
.toInt32();
5484 if( !sValue
.isEmpty() && nLevel
>= 0 && nLevel
<= 10 )
5485 xTC
->setPropertyValue(getPropertyName(PROP_LEVEL
), uno::makeAny( static_cast<sal_Int16
>(nLevel
) ));
5487 // if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
5488 // \n Suppress page numbers
5490 //todo: unsupported feature
5492 pContext
->SetTC( xTC
);
5495 case FIELD_NUMCHARS
:
5496 case FIELD_NUMWORDS
:
5497 case FIELD_NUMPAGES
:
5498 if (xFieldProperties
.is())
5499 xFieldProperties
->setPropertyValue(
5500 getPropertyName(PROP_NUMBERING_TYPE
),
5501 uno::makeAny( lcl_ParseNumberingType(pContext
->GetCommand()) ));
5507 /* Unsupported fields will be handled here for docx file.
5508 * To handle unsupported fields used fieldmark API.
5510 OUString
aCode( pContext
->GetCommand().trim() );
5511 // Don't waste resources on wrapping shapes inside a fieldmark.
5512 if (sType
!= "SHAPE" && m_xTextFactory
.is() && !m_aTextAppendStack
.empty())
5514 xFieldInterface
= m_xTextFactory
->createInstance("com.sun.star.text.Fieldmark");
5516 uno::Reference
<text::XFormField
> const xFormField(xFieldInterface
, uno::UNO_QUERY
);
5517 InsertFieldmark(m_aTextAppendStack
, xFormField
, pContext
->GetStartRange(),
5518 pContext
->GetFieldId());
5519 xFormField
->setFieldType(ODF_UNHANDLED
);
5520 ++m_nStartGenericField
;
5521 pContext
->SetFormField( xFormField
);
5522 uno::Reference
<container::XNameContainer
> const xNameCont(xFormField
->getParameters());
5523 // note: setting the code to empty string is *required* in
5524 // m_bForceGenericFields mode, or the export will write
5525 // the ODF_UNHANDLED string!
5526 assert(!m_bForceGenericFields
|| aCode
.isEmpty());
5527 xNameCont
->insertByName(ODF_CODE_PARAM
, uno::makeAny(aCode
));
5528 if (sType
== "CONTROL")
5529 { // tdf#129247 HACK probably this should be imported as something else, like in ww8?
5530 xNameCont
->insertByName(ODF_ID_PARAM
, uno::makeAny(OUString::number(87))); // ww8::eCONTROL
5534 m_bParaHadField
= false;
5536 //set the text field if there is any
5537 pContext
->SetTextField( uno::Reference
< text::XTextField
>( xFieldInterface
, uno::UNO_QUERY
) );
5539 catch( const uno::Exception
& )
5541 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "Exception in CloseFieldCommand()" );
5543 pContext
->SetCommandCompleted();
5546 /*-------------------------------------------------------------------------
5547 //the _current_ fields require a string type result while TOCs accept richt results
5548 -----------------------------------------------------------------------*/
5549 bool DomainMapper_Impl::IsFieldResultAsString()
5552 OSL_ENSURE( !m_aFieldStack
.empty(), "field stack empty?");
5553 FieldContextPtr pContext
= m_aFieldStack
.back();
5554 OSL_ENSURE( pContext
.get(), "no field context available");
5555 if( pContext
.get() )
5557 bRet
= pContext
->GetTextField().is()
5558 || pContext
->GetFieldId() == FIELD_FORMDROPDOWN
5559 || pContext
->GetFieldId() == FIELD_FILLIN
;
5564 FieldContextPtr pOuter
= GetParentFieldContext(m_aFieldStack
);
5567 if (!IsFieldNestingAllowed(pOuter
, m_aFieldStack
.back()))
5569 // Child field has no backing SwField, but the parent has: append is still possible.
5570 bRet
= pOuter
->GetTextField().is();
5577 void DomainMapper_Impl::AppendFieldResult(OUString
const& rString
)
5579 assert(!m_aFieldStack
.empty());
5580 FieldContextPtr pContext
= m_aFieldStack
.back();
5581 SAL_WARN_IF(!pContext
.get(), "writerfilter.dmapper", "no field context");
5584 FieldContextPtr pOuter
= GetParentFieldContext(m_aFieldStack
);
5587 if (!IsFieldNestingAllowed(pOuter
, pContext
))
5589 // Child can't host the field result, forward to parent.
5590 pOuter
->AppendResult(rString
);
5595 pContext
->AppendResult(rString
);
5599 // Calculates css::DateTime based on ddddd.sssss since 1900-1-0
5600 static util::DateTime
lcl_dateTimeFromSerial(const double& dSerial
)
5602 const sal_uInt32 secondsPerDay
= 86400;
5603 const sal_uInt16 secondsPerHour
= 3600;
5605 DateTime
d(Date(30, 12, 1899));
5606 d
.AddDays( static_cast<sal_Int32
>(dSerial
) );
5608 double frac
= std::modf(dSerial
, &o3tl::temporary(double()));
5609 sal_uInt32 seconds
= frac
* secondsPerDay
;
5611 util::DateTime date
;
5612 date
.Year
= d
.GetYear();
5613 date
.Month
= d
.GetMonth();
5614 date
.Day
= d
.GetDay();
5615 date
.Hours
= seconds
/ secondsPerHour
;
5616 date
.Minutes
= (seconds
% secondsPerHour
) / 60;
5617 date
.Seconds
= seconds
% 60;
5622 void DomainMapper_Impl::SetFieldResult(OUString
const& rResult
)
5625 TagLogger::getInstance().startElement("setFieldResult");
5626 TagLogger::getInstance().chars(rResult
);
5629 FieldContextPtr pContext
= m_aFieldStack
.back();
5630 OSL_ENSURE( pContext
.get(), "no field context available");
5632 if (m_aFieldStack
.size() > 1)
5634 // This is a nested field. See if the parent supports nesting on the Writer side.
5635 FieldContextPtr pParentContext
= m_aFieldStack
[m_aFieldStack
.size() - 2];
5638 std::vector
<OUString
> aParentParts
= pParentContext
->GetCommandParts();
5639 // Conditional text fields don't support nesting in Writer.
5640 if (!aParentParts
.empty() && aParentParts
[0] == "IF")
5647 if( pContext
.get() )
5649 uno::Reference
<text::XTextField
> xTextField
= pContext
->GetTextField();
5652 OSL_ENSURE( xTextField
.is()
5653 //||m_xTOC.is() ||m_xTC.is()
5654 //||m_sHyperlinkURL.getLength()
5655 , "DomainMapper_Impl::SetFieldResult: field not created" );
5660 if( m_bSetUserFieldContent
)
5662 // user field content has to be set at the field master
5663 uno::Reference
< text::XDependentTextField
> xDependentField( xTextField
, uno::UNO_QUERY_THROW
);
5664 xDependentField
->getTextFieldMaster()->setPropertyValue(
5665 getPropertyName(PROP_CONTENT
),
5666 uno::makeAny( rResult
));
5668 else if ( m_bSetCitation
)
5671 uno::Reference
< beans::XPropertySet
> xFieldProperties( xTextField
, uno::UNO_QUERY_THROW
);
5672 // In case of SetExpression, the field result contains the content of the variable.
5673 uno::Reference
<lang::XServiceInfo
> xServiceInfo(xTextField
, uno::UNO_QUERY
);
5675 bool bIsSetbiblio
= xServiceInfo
->supportsService("com.sun.star.text.TextField.Bibliography");
5678 uno::Any aProperty
= xFieldProperties
->getPropertyValue("Fields");
5679 uno::Sequence
<beans::PropertyValue
> aValues
;
5680 aProperty
>>= aValues
;
5681 beans::PropertyValue propertyVal
;
5682 sal_Int32 nTitleFoundIndex
= -1;
5683 for (sal_Int32 i
= 0; i
< aValues
.getLength(); ++i
)
5685 propertyVal
= aValues
[i
];
5686 if (propertyVal
.Name
== "Title")
5688 nTitleFoundIndex
= i
;
5692 if (nTitleFoundIndex
!= -1)
5695 uno::Any
aValue(propertyVal
.Value
);
5696 aValue
>>= titleStr
;
5697 titleStr
+= rResult
;
5698 propertyVal
.Value
<<= titleStr
;
5699 aValues
[nTitleFoundIndex
] = propertyVal
;
5703 aValues
.realloc(aValues
.getLength() + 1);
5704 propertyVal
.Name
= "Title";
5705 propertyVal
.Value
<<= rResult
;
5706 aValues
[aValues
.getLength() - 1] = propertyVal
;
5708 xFieldProperties
->setPropertyValue("Fields",
5709 uno::makeAny(aValues
));
5712 else if ( m_bSetDateValue
)
5714 uno::Reference
< util::XNumberFormatsSupplier
> xNumberSupplier( m_xTextDocument
, uno::UNO_QUERY_THROW
);
5716 uno::Reference
<util::XNumberFormatter
> xFormatter(util::NumberFormatter::create(m_xComponentContext
), uno::UNO_QUERY_THROW
);
5717 xFormatter
->attachNumberFormatsSupplier( xNumberSupplier
);
5720 uno::Reference
< beans::XPropertySet
> xFieldProperties( xTextField
, uno::UNO_QUERY_THROW
);
5722 xFieldProperties
->getPropertyValue( "NumberFormat" ) >>= nKey
;
5723 xFieldProperties
->setPropertyValue(
5725 uno::makeAny( lcl_dateTimeFromSerial( xFormatter
->convertStringToNumber( nKey
, rResult
) ) ) );
5729 uno::Reference
< beans::XPropertySet
> xFieldProperties( xTextField
, uno::UNO_QUERY_THROW
);
5730 // In case of SetExpression, and Input fields the field result contains the content of the variable.
5731 uno::Reference
<lang::XServiceInfo
> xServiceInfo(xTextField
, uno::UNO_QUERY
);
5732 // there are fields with a content property, which aren't working correctly with
5733 // a generalized try catch of the content, property, so just restrict content
5734 // handling to these explicit services.
5735 const bool bHasContent
= xServiceInfo
->supportsService("com.sun.star.text.TextField.SetExpression") ||
5736 xServiceInfo
->supportsService("com.sun.star.text.TextField.Input");
5737 // If we already have content set, then use the current presentation
5741 // this will throw for field types without Content
5742 uno::Any
aValue(xFieldProperties
->getPropertyValue(
5743 getPropertyName(PROP_CONTENT
)));
5746 xFieldProperties
->setPropertyValue(
5747 getPropertyName(bHasContent
&& sValue
.isEmpty()? PROP_CONTENT
: PROP_CURRENT_PRESENTATION
),
5748 uno::makeAny( rResult
));
5751 catch( const beans::UnknownPropertyException
& )
5753 //some fields don't have a CurrentPresentation (DateTime)
5757 catch (const uno::Exception
&)
5759 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "DomainMapper_Impl::SetFieldResult");
5764 void DomainMapper_Impl::SetFieldFFData(const FFDataHandler::Pointer_t
& pFFDataHandler
)
5767 TagLogger::getInstance().startElement("setFieldFFData");
5770 if (!m_aFieldStack
.empty())
5772 FieldContextPtr pContext
= m_aFieldStack
.back();
5775 pContext
->setFFDataHandler(pFFDataHandler
);
5780 TagLogger::getInstance().endElement();
5784 void DomainMapper_Impl::PopFieldContext()
5786 if(m_bDiscardHeaderFooter
)
5789 TagLogger::getInstance().element("popFieldContext");
5792 if (m_aFieldStack
.empty())
5795 FieldContextPtr pContext
= m_aFieldStack
.back();
5796 OSL_ENSURE( pContext
.get(), "no field context available");
5797 if( pContext
.get() )
5799 if( !pContext
->IsCommandCompleted() )
5800 CloseFieldCommand();
5802 if (!pContext
->GetResult().isEmpty())
5804 uno::Reference
< beans::XPropertySet
> xFieldProperties
= pContext
->GetCustomField();
5805 if(xFieldProperties
.is())
5806 SetNumberFormat( pContext
->GetResult(), xFieldProperties
, true );
5807 SetFieldResult( pContext
->GetResult() );
5810 //insert the field, TC or TOC
5811 uno::Reference
< text::XTextAppend
> xTextAppend
;
5812 if (!m_aTextAppendStack
.empty())
5813 xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
5814 if(xTextAppend
.is())
5818 uno::Reference
< text::XTextCursor
> xCrsr
= xTextAppend
->createTextCursorByRange(pContext
->GetStartRange());
5819 uno::Reference
< text::XTextContent
> xToInsert( pContext
->GetTOC(), uno::UNO_QUERY
);
5820 if( xToInsert
.is() )
5822 if (m_bStartedTOC
|| m_bStartIndex
|| m_bStartBibliography
)
5824 // inside SDT, last empty paragraph is also part of index
5825 if (!m_bParaChanged
&& !m_xSdtEntryStart
)
5827 // End of index is the first item on a new paragraph - this paragraph
5828 // should not be part of index
5830 = xTextAppend
->createTextCursorByRange(xTextAppend
->getEnd());
5831 xCursor
->gotoEnd(false);
5832 xCursor
->goLeft(1, true);
5834 xCursor
->setString(OUString());
5835 // But a new paragraph should be started after the index instead
5836 xTextAppend
->finishParagraph(css::beans::PropertyValues());
5838 m_bStartedTOC
= false;
5839 m_aTextAppendStack
.pop();
5840 m_bTextInserted
= false;
5841 m_bParaChanged
= true; // the paragraph must stay anyway
5843 m_bStartTOC
= false;
5844 m_bStartIndex
= false;
5845 m_bStartBibliography
= false;
5846 if (IsInHeaderFooter() && m_bStartTOCHeaderFooter
)
5847 m_bStartTOCHeaderFooter
= false;
5851 xToInsert
.set(pContext
->GetTC(), uno::UNO_QUERY
);
5852 if( !xToInsert
.is() && !m_bStartTOC
&& !m_bStartIndex
&& !m_bStartBibliography
)
5853 xToInsert
= pContext
->GetTextField();
5854 if( xToInsert
.is() && !m_bStartTOC
&& !m_bStartIndex
&& !m_bStartBibliography
)
5857 // Character properties of the field show up here the
5858 // last (always empty) run. Inherit character
5859 // properties from there.
5860 // Also merge in the properties from the field context,
5861 // e.g. SdtEndBefore.
5862 if (m_pLastCharacterContext
.get())
5863 aMap
.InsertProps(m_pLastCharacterContext
);
5864 aMap
.InsertProps(m_aFieldStack
.back()->getProperties());
5865 appendTextContent(xToInsert
, aMap
.GetPropertyValues());
5866 CheckRedline( xToInsert
->getAnchor( ) );
5870 FormControlHelper::Pointer_t
pFormControlHelper(pContext
->getFormControlHelper());
5871 if (pFormControlHelper
.get() != nullptr)
5873 uno::Reference
< text::XFormField
> xFormField( pContext
->GetFormField() );
5875 if (pFormControlHelper
->hasFFDataHandler())
5877 xToInsert
.set(xFormField
, uno::UNO_QUERY
);
5878 if (xFormField
.is() && xToInsert
.is())
5880 PopFieldmark(m_aTextAppendStack
, xCrsr
,
5881 pContext
->GetFieldId());
5882 pFormControlHelper
->processField( xFormField
);
5886 pFormControlHelper
->insertControl(xCrsr
);
5891 PopFieldmark(m_aTextAppendStack
, xCrsr
,
5892 pContext
->GetFieldId());
5893 uno::Reference
<lang::XComponent
>(xFormField
, uno::UNO_QUERY_THROW
)->dispose(); // presumably invalid?
5896 else if (!pContext
->GetHyperlinkURL().isEmpty() && xCrsr
.is())
5898 xCrsr
->gotoEnd( true );
5900 // Draw components (like comments) need hyperlinks set differently
5901 SvxUnoTextRangeBase
* pDrawText
= dynamic_cast<SvxUnoTextRangeBase
*>(xCrsr
.get());
5903 pDrawText
->attachField( std::make_unique
<SvxURLField
>(pContext
->GetHyperlinkURL(), xCrsr
->getString(), SvxURLFormat::AppDefault
) );
5906 uno::Reference
< beans::XPropertySet
> xCrsrProperties( xCrsr
, uno::UNO_QUERY_THROW
);
5907 xCrsrProperties
->setPropertyValue(getPropertyName(PROP_HYPER_LINK_U_R_L
), uno::
5908 makeAny(pContext
->GetHyperlinkURL()));
5910 if (!pContext
->GetHyperlinkTarget().isEmpty())
5911 xCrsrProperties
->setPropertyValue("HyperLinkTarget", uno::makeAny(pContext
->GetHyperlinkTarget()));
5914 OUString
sDisplayName("Index Link");
5915 xCrsrProperties
->setPropertyValue("VisitedCharStyleName",uno::makeAny(sDisplayName
));
5916 xCrsrProperties
->setPropertyValue("UnvisitedCharStyleName",uno::makeAny(sDisplayName
));
5920 uno::Any aAny
= xCrsrProperties
->getPropertyValue("CharStyleName");
5922 if (css::uno::fromAny(aAny
, &charStyle
))
5924 if (charStyle
.isEmpty())
5926 xCrsrProperties
->setPropertyValue("VisitedCharStyleName", uno::makeAny(OUString("Default Style")));
5927 xCrsrProperties
->setPropertyValue("UnvisitedCharStyleName", uno::makeAny(OUString("Default Style")));
5929 else if (charStyle
.equalsIgnoreAsciiCase("Internet Link"))
5931 xCrsrProperties
->setPropertyValue("CharStyleName", uno::makeAny(OUString("Default Style")));
5935 xCrsrProperties
->setPropertyValue("VisitedCharStyleName", aAny
);
5936 xCrsrProperties
->setPropertyValue("UnvisitedCharStyleName", aAny
);
5942 else if (m_nStartGenericField
!= 0)
5944 --m_nStartGenericField
;
5945 PopFieldmark(m_aTextAppendStack
, xCrsr
, pContext
->GetFieldId());
5948 m_bTextInserted
= false;
5954 catch(const lang::IllegalArgumentException
&)
5956 OSL_FAIL( "IllegalArgumentException in PopFieldContext()" );
5958 catch(const uno::Exception
&)
5960 OSL_FAIL( "exception in PopFieldContext()" );
5964 //TOCs have to include all the imported content
5967 std::vector
<FieldParagraph
> aParagraphsToFinish
;
5970 aParagraphsToFinish
= pContext
->GetParagraphsToFinish();
5973 //remove the field context
5974 m_aFieldStack
.pop_back();
5976 // Finish the paragraph(s) now that the field is closed.
5977 for (const auto& rFinish
: aParagraphsToFinish
)
5979 finishParagraph(rFinish
.m_pPropertyMap
, rFinish
.m_bRemove
);
5984 void DomainMapper_Impl::SetBookmarkName( const OUString
& rBookmarkName
)
5986 BookmarkMap_t::iterator aBookmarkIter
= m_aBookmarkMap
.find( m_sCurrentBkmkId
);
5987 if( aBookmarkIter
!= m_aBookmarkMap
.end() )
5989 // fields are internal bookmarks: consume redundant "normal" bookmark
5990 if ( IsOpenField() )
5992 FFDataHandler::Pointer_t
pFFDataHandler(GetTopFieldContext()->getFFDataHandler());
5993 if (pFFDataHandler
&& pFFDataHandler
->getName() == rBookmarkName
)
5995 // HACK: At the END marker, StartOrEndBookmark will START
5996 // a bookmark which will eventually be abandoned, not created.
5997 m_aBookmarkMap
.erase(aBookmarkIter
);
6002 aBookmarkIter
->second
.m_sBookmarkName
= rBookmarkName
;
6005 m_sCurrentBkmkName
= rBookmarkName
;
6008 // This method was used as-is for DomainMapper_Impl::startOrEndPermissionRange() implementation.
6009 void DomainMapper_Impl::StartOrEndBookmark( const OUString
& rId
)
6012 * Add the dummy paragraph to handle section properties
6013 * iff the first element in the section is a table. If the dummy para is not added yet, then add it;
6014 * So bookmark is not attached to the wrong paragraph.
6016 if(hasTableManager() && getTableManager().isInCell() && m_nTableDepth
== 0 && GetIsFirstParagraphInSection()
6017 && !GetIsDummyParaAddedForTableInSection() &&!GetIsTextFrameInserted())
6019 AddDummyParaForTableInSection();
6022 bool bIsAfterDummyPara
= GetIsDummyParaAddedForTableInSection() && GetIsFirstParagraphInSection();
6023 if (m_aTextAppendStack
.empty())
6025 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
6026 BookmarkMap_t::iterator aBookmarkIter
= m_aBookmarkMap
.find( rId
);
6027 //is the bookmark name already registered?
6030 if( aBookmarkIter
!= m_aBookmarkMap
.end() )
6032 if (m_xTextFactory
.is())
6034 uno::Reference
< text::XTextContent
> xBookmark( m_xTextFactory
->createInstance( "com.sun.star.text.Bookmark" ), uno::UNO_QUERY_THROW
);
6035 uno::Reference
< text::XTextCursor
> xCursor
;
6036 uno::Reference
< text::XText
> xText
= aBookmarkIter
->second
.m_xTextRange
->getText();
6037 if( aBookmarkIter
->second
.m_bIsStartOfText
&& !bIsAfterDummyPara
)
6039 xCursor
= xText
->createTextCursorByRange( xText
->getStart() );
6043 xCursor
= xText
->createTextCursorByRange( aBookmarkIter
->second
.m_xTextRange
);
6044 xCursor
->goRight( 1, false );
6047 xCursor
->gotoRange( xTextAppend
->getEnd(), true );
6048 // A Paragraph was recently finished, and a new Paragraph has not been started as yet
6049 // then move the bookmark-End to the earlier paragraph
6050 if (IsOutsideAParagraph())
6052 xCursor
->goLeft( 1, false );
6054 uno::Reference
< container::XNamed
> xBkmNamed( xBookmark
, uno::UNO_QUERY_THROW
);
6055 assert(!aBookmarkIter
->second
.m_sBookmarkName
.isEmpty());
6056 //todo: make sure the name is not used already!
6057 xBkmNamed
->setName( aBookmarkIter
->second
.m_sBookmarkName
);
6058 xTextAppend
->insertTextContent( uno::Reference
< text::XTextRange
>( xCursor
, uno::UNO_QUERY_THROW
), xBookmark
, !xCursor
->isCollapsed() );
6060 m_aBookmarkMap
.erase( aBookmarkIter
);
6061 m_sCurrentBkmkId
.clear();
6065 //otherwise insert a text range as marker
6066 bool bIsStart
= true;
6067 uno::Reference
< text::XTextRange
> xCurrent
;
6068 if (xTextAppend
.is())
6070 uno::Reference
<text::XTextCursor
> const xCursor
=
6071 xTextAppend
->createTextCursorByRange(
6072 m_aTextAppendStack
.top().xInsertPosition
.is()
6073 ? m_aTextAppendStack
.top().xInsertPosition
6074 : xTextAppend
->getEnd() );
6079 if (!bIsAfterDummyPara
)
6080 bIsStart
= !xCursor
->goLeft(1, false);
6081 xCurrent
= xCursor
->getStart();
6083 m_sCurrentBkmkId
= rId
;
6084 m_aBookmarkMap
.emplace( rId
, BookmarkInsertPosition( bIsStart
, m_sCurrentBkmkName
, xCurrent
) );
6085 m_sCurrentBkmkName
.clear();
6088 catch( const uno::Exception
& )
6090 //TODO: What happens to bookmarks where start and end are at different XText objects?
6094 void DomainMapper_Impl::setPermissionRangeEd(const OUString
& user
)
6096 PermMap_t::iterator aPremIter
= m_aPermMap
.find(m_sCurrentPermId
);
6097 if (aPremIter
!= m_aPermMap
.end())
6098 aPremIter
->second
.m_Ed
= user
;
6100 m_sCurrentPermEd
= user
;
6103 void DomainMapper_Impl::setPermissionRangeEdGrp(const OUString
& group
)
6105 PermMap_t::iterator aPremIter
= m_aPermMap
.find(m_sCurrentPermId
);
6106 if (aPremIter
!= m_aPermMap
.end())
6107 aPremIter
->second
.m_EdGrp
= group
;
6109 m_sCurrentPermEdGrp
= group
;
6112 // This method is based on implementation from DomainMapper_Impl::StartOrEndBookmark()
6113 void DomainMapper_Impl::startOrEndPermissionRange(sal_Int32 permissinId
)
6116 * Add the dummy paragraph to handle section properties
6117 * if the first element in the section is a table. If the dummy para is not added yet, then add it;
6118 * So permission is not attached to the wrong paragraph.
6120 if (getTableManager().isInCell() && m_nTableDepth
== 0 && GetIsFirstParagraphInSection()
6121 && !GetIsDummyParaAddedForTableInSection() && !GetIsTextFrameInserted())
6123 AddDummyParaForTableInSection();
6126 if (m_aTextAppendStack
.empty())
6129 const bool bIsAfterDummyPara
= GetIsDummyParaAddedForTableInSection() && GetIsFirstParagraphInSection();
6131 uno::Reference
< text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
6132 PermMap_t::iterator aPermIter
= m_aPermMap
.find(permissinId
);
6134 //is the bookmark name already registered?
6137 if (aPermIter
== m_aPermMap
.end())
6139 //otherwise insert a text range as marker
6140 bool bIsStart
= true;
6141 uno::Reference
< text::XTextRange
> xCurrent
;
6142 if (xTextAppend
.is())
6144 uno::Reference
< text::XTextCursor
> xCursor
= xTextAppend
->createTextCursorByRange(xTextAppend
->getEnd());
6146 if (!bIsAfterDummyPara
)
6147 bIsStart
= !xCursor
->goLeft(1, false);
6148 xCurrent
= xCursor
->getStart();
6151 // register the start of the new permission
6152 m_sCurrentPermId
= permissinId
;
6153 m_aPermMap
.emplace(permissinId
, PermInsertPosition(bIsStart
, permissinId
, m_sCurrentPermEd
, m_sCurrentPermEdGrp
, xCurrent
));
6156 m_sCurrentPermEd
.clear();
6157 m_sCurrentPermEdGrp
.clear();
6161 if (m_xTextFactory
.is())
6163 uno::Reference
< text::XTextCursor
> xCursor
;
6164 uno::Reference
< text::XText
> xText
= aPermIter
->second
.m_xTextRange
->getText();
6165 if (aPermIter
->second
.m_bIsStartOfText
&& !bIsAfterDummyPara
)
6167 xCursor
= xText
->createTextCursorByRange(xText
->getStart());
6171 xCursor
= xText
->createTextCursorByRange(aPermIter
->second
.m_xTextRange
);
6172 xCursor
->goRight(1, false);
6175 xCursor
->gotoRange(xTextAppend
->getEnd(), true);
6176 // A Paragraph was recently finished, and a new Paragraph has not been started as yet
6177 // then move the bookmark-End to the earlier paragraph
6178 if (IsOutsideAParagraph())
6180 xCursor
->goLeft(1, false);
6183 // create a new bookmark using specific bookmark name pattern for permissions
6184 uno::Reference
< text::XTextContent
> xPerm(m_xTextFactory
->createInstance("com.sun.star.text.Bookmark"), uno::UNO_QUERY_THROW
);
6185 uno::Reference
< container::XNamed
> xPermNamed(xPerm
, uno::UNO_QUERY_THROW
);
6186 xPermNamed
->setName(aPermIter
->second
.createBookmarkName());
6189 const bool bAbsorb
= !xCursor
->isCollapsed();
6190 uno::Reference
< text::XTextRange
> xCurrent(xCursor
, uno::UNO_QUERY_THROW
);
6191 xTextAppend
->insertTextContent(xCurrent
, xPerm
, bAbsorb
);
6194 // remove processed permission
6195 m_aPermMap
.erase(aPermIter
);
6198 m_sCurrentPermId
= 0;
6199 m_sCurrentPermEd
.clear();
6200 m_sCurrentPermEdGrp
.clear();
6203 catch (const uno::Exception
&)
6205 //TODO: What happens to bookmarks where start and end are at different XText objects?
6209 void DomainMapper_Impl::AddAnnotationPosition(
6211 const sal_Int32 nAnnotationId
)
6213 if (m_aTextAppendStack
.empty())
6216 // Create a cursor, pointing to the current position.
6217 uno::Reference
<text::XTextAppend
> xTextAppend
= m_aTextAppendStack
.top().xTextAppend
;
6218 uno::Reference
<text::XTextRange
> xCurrent
;
6219 if (xTextAppend
.is())
6221 uno::Reference
<text::XTextCursor
> xCursor
;
6223 xCursor
= xTextAppend
->createTextCursorByRange(xTextAppend
->getEnd());
6225 xCursor
= m_aTextAppendStack
.top().xCursor
;
6227 xCurrent
= xCursor
->getStart();
6230 // And save it, to be used by PopAnnotation() later.
6231 AnnotationPosition
& aAnnotationPosition
= m_aAnnotationPositions
[ nAnnotationId
];
6234 aAnnotationPosition
.m_xStart
= xCurrent
;
6238 aAnnotationPosition
.m_xEnd
= xCurrent
;
6240 m_aAnnotationPositions
[ nAnnotationId
] = aAnnotationPosition
;
6243 GraphicImportPtr
const & DomainMapper_Impl::GetGraphicImport(GraphicImportType eGraphicImportType
)
6245 if(!m_pGraphicImport
)
6246 m_pGraphicImport
= new GraphicImport( m_xComponentContext
, m_xTextFactory
, m_rDMapper
, eGraphicImportType
, m_aPositionOffsets
, m_aAligns
, m_aPositivePercentages
);
6247 return m_pGraphicImport
;
6249 /*-------------------------------------------------------------------------
6250 reset graphic import if the last import resulted in a shape, not a graphic
6251 -----------------------------------------------------------------------*/
6252 void DomainMapper_Impl::ResetGraphicImport()
6254 m_pGraphicImport
.clear();
6258 void DomainMapper_Impl::ImportGraphic(const writerfilter::Reference
< Properties
>::Pointer_t
& ref
, GraphicImportType eGraphicImportType
)
6260 GetGraphicImport(eGraphicImportType
);
6261 if( eGraphicImportType
!= IMPORT_AS_DETECTED_INLINE
&& eGraphicImportType
!= IMPORT_AS_DETECTED_ANCHOR
)
6263 //create the graphic
6264 ref
->resolve( *m_pGraphicImport
);
6267 //insert it into the document at the current cursor position
6269 uno::Reference
<text::XTextContent
> xTextContent
6270 (m_pGraphicImport
->GetGraphicObject());
6272 // In case the SDT starts with the text portion of the graphic, then set the SDT properties here.
6273 bool bHasGrabBag
= false;
6274 uno::Reference
<beans::XPropertySet
> xPropertySet(xTextContent
, uno::UNO_QUERY
);
6275 if (xPropertySet
.is())
6277 uno::Reference
<beans::XPropertySetInfo
> xPropertySetInfo
= xPropertySet
->getPropertySetInfo();
6278 bHasGrabBag
= xPropertySetInfo
->hasPropertyByName("FrameInteropGrabBag");
6279 // In case we're outside a paragraph, then the SDT properties are stored in the paragraph grab-bag, not the frame one.
6280 if (!m_pSdtHelper
->isInteropGrabBagEmpty() && bHasGrabBag
&& !m_pSdtHelper
->isOutsideAParagraph())
6282 comphelper::SequenceAsHashMap
aFrameGrabBag(xPropertySet
->getPropertyValue("FrameInteropGrabBag"));
6283 aFrameGrabBag
["SdtPr"] <<= m_pSdtHelper
->getInteropGrabBagAndClear();
6284 xPropertySet
->setPropertyValue("FrameInteropGrabBag", uno::makeAny(aFrameGrabBag
.getAsConstPropertyValueList()));
6288 /* Set "SdtEndBefore" property on Drawing.
6289 * It is required in a case when Drawing appears immediately after first run i.e.
6290 * there is no text/space/tab in between two runs.
6291 * In this case "SdtEndBefore" property needs to be set on Drawing.
6293 if(IsSdtEndBefore())
6295 if(xPropertySet
.is() && bHasGrabBag
)
6297 uno::Sequence
<beans::PropertyValue
> aFrameGrabBag( comphelper::InitPropertySequence({
6298 { "SdtEndBefore", uno::Any(true) }
6300 xPropertySet
->setPropertyValue("FrameInteropGrabBag",uno::makeAny(aFrameGrabBag
));
6305 // Update the shape properties if it is embedded object.
6306 if(m_xEmbedded
.is()){
6307 if (m_pGraphicImport
->GetXShapeObject())
6308 m_pGraphicImport
->GetXShapeObject()->setPosition(
6309 m_pGraphicImport
->GetGraphicObjectPosition());
6311 uno::Reference
<drawing::XShape
> xShape
= m_pGraphicImport
->GetXShapeObject();
6312 UpdateEmbeddedShapeProps(xShape
);
6313 if (eGraphicImportType
== IMPORT_AS_DETECTED_ANCHOR
)
6315 uno::Reference
<beans::XPropertySet
> xEmbeddedProps(m_xEmbedded
, uno::UNO_QUERY
);
6316 xEmbeddedProps
->setPropertyValue("AnchorType", uno::makeAny(text::TextContentAnchorType_AT_CHARACTER
));
6317 uno::Reference
<beans::XPropertySet
> xShapeProps(xShape
, uno::UNO_QUERY
);
6318 xEmbeddedProps
->setPropertyValue("HoriOrient", xShapeProps
->getPropertyValue("HoriOrient"));
6319 xEmbeddedProps
->setPropertyValue("HoriOrientPosition", xShapeProps
->getPropertyValue("HoriOrientPosition"));
6320 xEmbeddedProps
->setPropertyValue("HoriOrientRelation", xShapeProps
->getPropertyValue("HoriOrientRelation"));
6321 xEmbeddedProps
->setPropertyValue("VertOrient", xShapeProps
->getPropertyValue("VertOrient"));
6322 xEmbeddedProps
->setPropertyValue("VertOrientPosition", xShapeProps
->getPropertyValue("VertOrientPosition"));
6323 xEmbeddedProps
->setPropertyValue("VertOrientRelation", xShapeProps
->getPropertyValue("VertOrientRelation"));
6324 //tdf123873 fix missing textwrap import
6325 xEmbeddedProps
->setPropertyValue("TextWrap", xShapeProps
->getPropertyValue("TextWrap"));
6328 //insert it into the document at the current cursor position
6329 OSL_ENSURE( xTextContent
.is(), "DomainMapper_Impl::ImportGraphic");
6330 if( xTextContent
.is())
6332 appendTextContent( xTextContent
, uno::Sequence
< beans::PropertyValue
>() );
6334 if (eGraphicImportType
== IMPORT_AS_DETECTED_ANCHOR
&& !m_aTextAppendStack
.empty())
6336 // Remember this object is anchored to the current paragraph.
6337 AnchoredObjectInfo aInfo
;
6338 aInfo
.m_xAnchoredObject
= xTextContent
;
6339 if (m_pGraphicImport
)
6341 // We still have the graphic import around, remember the original margin, so later
6342 // SectionPropertyMap::HandleIncreasedAnchoredObjectSpacing() can use it.
6343 aInfo
.m_nLeftMargin
= m_pGraphicImport
->GetLeftMarginOrig();
6345 m_aTextAppendStack
.top().m_aAnchoredObjects
.push_back(aInfo
);
6349 // Clear the reference, so in case the embedded object is inside a
6350 // TextFrame, we won't try to resize it (to match the size of the
6352 m_xEmbedded
.clear();
6353 m_pGraphicImport
.clear();
6357 void DomainMapper_Impl::SetLineNumbering( sal_Int32 nLnnMod
, sal_uInt32 nLnc
, sal_Int32 ndxaLnn
)
6359 if( !m_bLineNumberingSet
)
6363 uno::Reference
< text::XLineNumberingProperties
> xLineProperties( m_xTextDocument
, uno::UNO_QUERY_THROW
);
6364 uno::Reference
< beans::XPropertySet
> xProperties
= xLineProperties
->getLineNumberingProperties();
6365 uno::Any
aTrue( uno::makeAny( true ));
6366 xProperties
->setPropertyValue( getPropertyName( PROP_IS_ON
), aTrue
);
6367 xProperties
->setPropertyValue( getPropertyName( PROP_COUNT_EMPTY_LINES
), aTrue
);
6368 xProperties
->setPropertyValue( getPropertyName( PROP_COUNT_LINES_IN_FRAMES
), uno::makeAny( false ) );
6369 xProperties
->setPropertyValue( getPropertyName( PROP_INTERVAL
), uno::makeAny( static_cast< sal_Int16
>( nLnnMod
)));
6370 xProperties
->setPropertyValue( getPropertyName( PROP_DISTANCE
), uno::makeAny( ConversionHelper::convertTwipToMM100(ndxaLnn
) ));
6371 xProperties
->setPropertyValue( getPropertyName( PROP_NUMBER_POSITION
), uno::makeAny( style::LineNumberPosition::LEFT
));
6372 xProperties
->setPropertyValue( getPropertyName( PROP_NUMBERING_TYPE
), uno::makeAny( style::NumberingType::ARABIC
));
6373 xProperties
->setPropertyValue( getPropertyName( PROP_RESTART_AT_EACH_PAGE
), uno::makeAny( nLnc
== NS_ooxml::LN_Value_ST_LineNumberRestart_newPage
));
6375 catch( const uno::Exception
& )
6378 m_bLineNumberingSet
= true;
6379 uno::Reference
< style::XStyleFamiliesSupplier
> xStylesSupplier( GetTextDocument(), uno::UNO_QUERY_THROW
);
6380 uno::Reference
< container::XNameAccess
> xStyleFamilies
= xStylesSupplier
->getStyleFamilies();
6381 uno::Reference
<container::XNameContainer
> xStyles
;
6382 xStyleFamilies
->getByName(getPropertyName( PROP_PARAGRAPH_STYLES
)) >>= xStyles
;
6383 lcl_linenumberingHeaderFooter( xStyles
, "Header", this );
6384 lcl_linenumberingHeaderFooter( xStyles
, "Footer", this );
6388 void DomainMapper_Impl::SetPageMarginTwip( PageMarElement eElement
, sal_Int32 nValue
)
6390 nValue
= ConversionHelper::convertTwipToMM100(nValue
);
6393 case PAGE_MAR_TOP
: m_aPageMargins
.top
= nValue
; break;
6394 case PAGE_MAR_RIGHT
: m_aPageMargins
.right
= nValue
; break;
6395 case PAGE_MAR_BOTTOM
: m_aPageMargins
.bottom
= nValue
; break;
6396 case PAGE_MAR_LEFT
: m_aPageMargins
.left
= nValue
; break;
6397 case PAGE_MAR_HEADER
: m_aPageMargins
.header
= nValue
; break;
6398 case PAGE_MAR_FOOTER
: m_aPageMargins
.footer
= nValue
; break;
6399 case PAGE_MAR_GUTTER
: break;
6405 : top(ConversionHelper::convertTwipToMM100( sal_Int32(1440)))
6406 // This is strange, the RTF spec says it's 1800, but it's clearly 1440 in Word
6407 // OOXML seems not to specify a default value
6408 , right(ConversionHelper::convertTwipToMM100( sal_Int32(1440)))
6411 , header(ConversionHelper::convertTwipToMM100(sal_Int32(720)))
6417 void DomainMapper_Impl::RegisterFrameConversion(
6418 uno::Reference
< text::XTextRange
> const& xFrameStartRange
,
6419 uno::Reference
< text::XTextRange
> const& xFrameEndRange
,
6420 const std::vector
<beans::PropertyValue
>& rFrameProperties
6424 m_aFrameProperties
.empty() && !m_xFrameStartRange
.is() && !m_xFrameEndRange
.is(),
6425 "frame properties not removed");
6426 m_aFrameProperties
= rFrameProperties
;
6427 m_xFrameStartRange
= xFrameStartRange
;
6428 m_xFrameEndRange
= xFrameEndRange
;
6432 void DomainMapper_Impl::ExecuteFrameConversion()
6434 if( m_xFrameStartRange
.is() && m_xFrameEndRange
.is() && !m_bDiscardHeaderFooter
)
6438 uno::Reference
< text::XTextAppendAndConvert
> xTextAppendAndConvert( GetTopTextAppend(), uno::UNO_QUERY_THROW
);
6439 // convert redline ranges to cursor movement and character length
6440 std::vector
<sal_Int32
> redPos
, redLen
;
6441 for( size_t i
= 0; i
< aFramedRedlines
.size(); i
+=3)
6443 uno::Reference
< text::XTextRange
> xRange
;
6444 aFramedRedlines
[i
] >>= xRange
;
6445 uno::Reference
<text::XTextCursor
> xRangeCursor
= GetTopTextAppend()->createTextCursorByRange( xRange
);
6446 if (xRangeCursor
.is())
6448 sal_Int32 nLen
= xRange
->getString().getLength();
6449 redLen
.push_back(nLen
);
6450 xRangeCursor
->gotoRange(m_xFrameStartRange
, true);
6451 redPos
.push_back(xRangeCursor
->getString().getLength() - nLen
);
6455 // failed createTextCursorByRange(), for example, table inside the frame
6456 redLen
.push_back(-1);
6457 redPos
.push_back(-1);
6461 const uno::Reference
< text::XTextContent
>& xTextContent
= xTextAppendAndConvert
->convertToTextFrame(
6464 comphelper::containerToSequence(m_aFrameProperties
) );
6466 // create redlines in the previous frame
6467 for( size_t i
= 0; i
< aFramedRedlines
.size(); i
+=3)
6470 beans::PropertyValues
aRedlineProperties( 3 );
6471 // skip failed createTextCursorByRange()
6472 if (redPos
[i
/3] == -1)
6474 aFramedRedlines
[i
+1] >>= sType
;
6475 aFramedRedlines
[i
+2] >>= aRedlineProperties
;
6476 uno::Reference
< text::XTextFrame
> xFrame( xTextContent
, uno::UNO_QUERY_THROW
);
6477 uno::Reference
< text::XTextCursor
> xCrsr
= xFrame
->getText()->createTextCursor();
6478 xCrsr
->goRight(redPos
[i
/3], false);
6479 xCrsr
->goRight(redLen
[i
/3], true);
6480 uno::Reference
< text::XRedline
> xRedline( xCrsr
, uno::UNO_QUERY_THROW
);
6481 xRedline
->makeRedline( sType
, aRedlineProperties
);
6484 catch( const uno::Exception
&)
6486 DBG_UNHANDLED_EXCEPTION( "writerfilter.dmapper", "Exception caught when converting to frame");
6489 m_bIsActualParagraphFramed
= false;
6490 aFramedRedlines
.clear();
6492 m_xFrameStartRange
= nullptr;
6493 m_xFrameEndRange
= nullptr;
6494 m_aFrameProperties
.clear();
6497 void DomainMapper_Impl::AddNewRedline( sal_uInt32 sprmId
)
6499 RedlineParamsPtr
pNew( new RedlineParams
);
6500 pNew
->m_nToken
= XML_mod
;
6501 if ( !m_bIsParaMarkerChange
)
6503 // <w:rPrChange> applies to the whole <w:r>, <w:pPrChange> applies to the whole <w:p>,
6504 // so keep those two in CONTEXT_CHARACTERS and CONTEXT_PARAGRAPH, which will take
6505 // care of their scope (i.e. when they should be used and discarded).
6506 // Let's keep the rest the same way they used to be handled (explicitly dropped
6507 // from a global stack by endtrackchange), but quite possibly they should not be handled
6508 // that way either (I don't know).
6509 if( sprmId
== NS_ooxml::LN_EG_RPrContent_rPrChange
)
6510 GetTopContextOfType( CONTEXT_CHARACTER
)->Redlines().push_back( pNew
);
6511 else if( sprmId
== NS_ooxml::LN_CT_PPr_pPrChange
)
6512 GetTopContextOfType( CONTEXT_PARAGRAPH
)->Redlines().push_back( pNew
);
6513 else if( sprmId
!= NS_ooxml::LN_CT_ParaRPr_rPrChange
)
6514 m_aRedlines
.top().push_back( pNew
);
6518 m_pParaMarkerRedline
= pNew
;
6520 // Newly read data will go into this redline.
6521 m_currentRedline
= pNew
;
6524 void DomainMapper_Impl::SetCurrentRedlineIsRead()
6526 m_currentRedline
.clear();
6529 sal_Int32
DomainMapper_Impl::GetCurrentRedlineToken( ) const
6531 return m_currentRedline
->m_nToken
;
6534 void DomainMapper_Impl::SetCurrentRedlineAuthor( const OUString
& sAuthor
)
6536 if (!m_xAnnotationField
.is())
6538 if (m_currentRedline
.get())
6539 m_currentRedline
->m_sAuthor
= sAuthor
;
6541 SAL_INFO("writerfilter.dmapper", "numberingChange not implemented");
6544 m_xAnnotationField
->setPropertyValue("Author", uno::makeAny(sAuthor
));
6547 void DomainMapper_Impl::SetCurrentRedlineInitials( const OUString
& sInitials
)
6549 if (m_xAnnotationField
.is())
6550 m_xAnnotationField
->setPropertyValue("Initials", uno::makeAny(sInitials
));
6553 void DomainMapper_Impl::SetCurrentRedlineDate( const OUString
& sDate
)
6555 if (!m_xAnnotationField
.is())
6557 if (m_currentRedline
.get())
6558 m_currentRedline
->m_sDate
= sDate
;
6560 SAL_INFO("writerfilter.dmapper", "numberingChange not implemented");
6563 m_xAnnotationField
->setPropertyValue("DateTimeValue", uno::makeAny(ConversionHelper::ConvertDateStringToDateTime(sDate
)));
6566 void DomainMapper_Impl::SetCurrentRedlineId( sal_Int32 sId
)
6568 if (m_xAnnotationField
.is())
6570 m_nAnnotationId
= sId
;
6574 // This should be an assert, but somebody had the smart idea to reuse this function also for comments and whatnot,
6575 // and in some cases the id is actually not handled, which may be in fact a bug.
6576 if( !m_currentRedline
.get())
6577 SAL_INFO("writerfilter.dmapper", "no current redline");
6581 void DomainMapper_Impl::SetCurrentRedlineToken( sal_Int32 nToken
)
6583 m_currentRedline
->m_nToken
= nToken
;
6586 void DomainMapper_Impl::SetCurrentRedlineRevertProperties( const uno::Sequence
<beans::PropertyValue
>& aProperties
)
6588 m_currentRedline
->m_aRevertProperties
= aProperties
;
6592 // This removes only the last redline stored here, those stored in contexts are automatically removed when
6593 // the context is destroyed.
6594 void DomainMapper_Impl::RemoveTopRedline( )
6596 if (m_aRedlines
.top().empty())
6598 SAL_WARN("writerfilter.dmapper", "RemoveTopRedline called with empty stack");
6599 throw uno::Exception("RemoveTopRedline failed", nullptr);
6601 m_aRedlines
.top().pop_back( );
6602 m_currentRedline
.clear();
6605 void DomainMapper_Impl::ApplySettingsTable()
6607 if (m_pSettingsTable
&& m_xTextFactory
.is())
6611 uno::Reference
< beans::XPropertySet
> xTextDefaults(m_xTextFactory
->createInstance("com.sun.star.text.Defaults"), uno::UNO_QUERY_THROW
);
6612 sal_Int32 nDefTab
= m_pSettingsTable
->GetDefaultTabStop();
6613 xTextDefaults
->setPropertyValue( getPropertyName( PROP_TAB_STOP_DISTANCE
), uno::makeAny(nDefTab
) );
6614 if (m_pSettingsTable
->GetLinkStyles())
6616 // If linked styles are enabled, set paragraph defaults from Word's default template
6617 xTextDefaults
->setPropertyValue(getPropertyName(PROP_PARA_BOTTOM_MARGIN
), uno::makeAny(ConversionHelper::convertTwipToMM100(200)));
6618 style::LineSpacing aSpacing
;
6619 aSpacing
.Mode
= style::LineSpacingMode::PROP
;
6620 aSpacing
.Height
= sal_Int16(115);
6621 xTextDefaults
->setPropertyValue(getPropertyName(PROP_PARA_LINE_SPACING
), uno::makeAny(aSpacing
));
6624 if (m_pSettingsTable
->GetZoomFactor() || m_pSettingsTable
->GetView())
6626 std::vector
<beans::PropertyValue
> aViewProps
;
6627 if (m_pSettingsTable
->GetZoomFactor())
6629 aViewProps
.emplace_back("ZoomFactor", -1, uno::makeAny(m_pSettingsTable
->GetZoomFactor()), beans::PropertyState_DIRECT_VALUE
);
6630 aViewProps
.emplace_back("VisibleBottom", -1, uno::makeAny(sal_Int32(0)), beans::PropertyState_DIRECT_VALUE
);
6631 aViewProps
.emplace_back("ZoomType", -1,
6632 uno::makeAny(m_pSettingsTable
->GetZoomType()),
6633 beans::PropertyState_DIRECT_VALUE
);
6635 uno::Reference
<container::XIndexContainer
> xBox
= document::IndexedPropertyValues::create(m_xComponentContext
);
6636 xBox
->insertByIndex(sal_Int32(0), uno::makeAny(comphelper::containerToSequence(aViewProps
)));
6637 uno::Reference
<document::XViewDataSupplier
> xViewDataSupplier(m_xTextDocument
, uno::UNO_QUERY
);
6638 xViewDataSupplier
->setViewData(xBox
);
6641 uno::Reference
< beans::XPropertySet
> xSettings(m_xTextFactory
->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY
);
6643 if (m_pSettingsTable
->GetDoNotExpandShiftReturn())
6644 xSettings
->setPropertyValue( "DoNotJustifyLinesWithManualBreak", uno::makeAny(true) );
6645 if (m_pSettingsTable
->GetUsePrinterMetrics())
6646 xSettings
->setPropertyValue("PrinterIndependentLayout", uno::makeAny(document::PrinterIndependentLayout::DISABLED
));
6647 if( m_pSettingsTable
->GetEmbedTrueTypeFonts())
6648 xSettings
->setPropertyValue( getPropertyName( PROP_EMBED_FONTS
), uno::makeAny(true) );
6649 if( m_pSettingsTable
->GetEmbedSystemFonts())
6650 xSettings
->setPropertyValue( getPropertyName( PROP_EMBED_SYSTEM_FONTS
), uno::makeAny(true) );
6651 xSettings
->setPropertyValue("AddParaTableSpacing", uno::makeAny(m_pSettingsTable
->GetDoNotUseHTMLParagraphAutoSpacing()));
6652 if( m_pSettingsTable
->GetProtectForm() )
6653 xSettings
->setPropertyValue("ProtectForm", uno::makeAny( true ));
6654 if( m_pSettingsTable
->GetReadOnly() )
6655 xSettings
->setPropertyValue("LoadReadonly", uno::makeAny( true ));
6657 catch(const uno::Exception
&)
6663 uno::Reference
<container::XIndexAccess
> DomainMapper_Impl::GetCurrentNumberingRules(sal_Int32
* pListLevel
)
6665 uno::Reference
<container::XIndexAccess
> xRet
;
6668 OUString aStyle
= GetCurrentParaStyleName();
6669 if (aStyle
.isEmpty())
6671 const StyleSheetEntryPtr pEntry
= GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(aStyle
);
6674 const StyleSheetPropertyMap
* pStyleSheetProperties
= dynamic_cast<const StyleSheetPropertyMap
*>(pEntry
->pProperties
.get());
6675 if (!pStyleSheetProperties
)
6677 sal_Int32 nListId
= pStyleSheetProperties
->GetListId();
6681 *pListLevel
= pStyleSheetProperties
->GetListLevel();
6683 // So we are in a paragraph style and it has numbering. Look up the relevant numbering rules.
6684 auto const pList(GetListTable()->GetList(nListId
));
6688 aListName
= pList
->GetStyleName();
6690 uno::Reference
< style::XStyleFamiliesSupplier
> xStylesSupplier(GetTextDocument(), uno::UNO_QUERY_THROW
);
6691 uno::Reference
< container::XNameAccess
> xStyleFamilies
= xStylesSupplier
->getStyleFamilies();
6692 uno::Reference
<container::XNameAccess
> xNumberingStyles
;
6693 xStyleFamilies
->getByName("NumberingStyles") >>= xNumberingStyles
;
6694 uno::Reference
<beans::XPropertySet
> xStyle(xNumberingStyles
->getByName(aListName
), uno::UNO_QUERY
);
6695 xRet
.set(xStyle
->getPropertyValue("NumberingRules"), uno::UNO_QUERY
);
6697 catch (const uno::Exception
&)
6699 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "GetCurrentNumberingRules: exception caught");
6704 uno::Reference
<beans::XPropertySet
> DomainMapper_Impl::GetCurrentNumberingCharStyle()
6706 uno::Reference
<beans::XPropertySet
> xRet
;
6709 sal_Int32 nListLevel
= -1;
6710 uno::Reference
<container::XIndexAccess
> xLevels
;
6711 if ( GetTopContextType() == CONTEXT_PARAGRAPH
)
6712 xLevels
= GetCurrentNumberingRules(&nListLevel
);
6715 if (IsOOXMLImport())
6718 PropertyMapPtr pContext
= m_pTopContext
;
6719 if (IsRTFImport() && !IsOpenField())
6721 // Looking up the paragraph context explicitly (and not just taking
6722 // the top context) is necessary for RTF, where formatting of a run
6723 // and of the paragraph mark is not separated.
6724 // We know that the formatting inside a field won't affect the
6725 // paragraph marker formatting, though.
6726 pContext
= GetTopContextOfType(CONTEXT_PARAGRAPH
);
6731 // In case numbering rules is not found via a style, try the direct formatting instead.
6732 std::optional
<PropertyMap::Property
> oProp
= pContext
->getProperty(PROP_NUMBERING_RULES
);
6735 xLevels
.set(oProp
->second
, uno::UNO_QUERY
);
6736 // Found the rules, then also try to look up our numbering level.
6737 oProp
= pContext
->getProperty(PROP_NUMBERING_LEVEL
);
6739 oProp
->second
>>= nListLevel
;
6747 uno::Sequence
<beans::PropertyValue
> aProps
;
6748 xLevels
->getByIndex(nListLevel
) >>= aProps
;
6749 auto pProp
= std::find_if(aProps
.begin(), aProps
.end(),
6750 [](const beans::PropertyValue
& rProp
) { return rProp
.Name
== "CharStyleName"; });
6751 if (pProp
!= aProps
.end())
6753 OUString aCharStyle
;
6754 pProp
->Value
>>= aCharStyle
;
6755 uno::Reference
<container::XNameAccess
> xCharacterStyles
;
6756 uno::Reference
< style::XStyleFamiliesSupplier
> xStylesSupplier(GetTextDocument(), uno::UNO_QUERY
);
6757 uno::Reference
< container::XNameAccess
> xStyleFamilies
= xStylesSupplier
->getStyleFamilies();
6758 xStyleFamilies
->getByName("CharacterStyles") >>= xCharacterStyles
;
6759 xRet
.set(xCharacterStyles
->getByName(aCharStyle
), uno::UNO_QUERY_THROW
);
6762 catch( const uno::Exception
& )
6768 SectionPropertyMap
* DomainMapper_Impl::GetSectionContext()
6770 SectionPropertyMap
* pSectionContext
= nullptr;
6771 //the section context is not available before the first call of startSectionGroup()
6772 if( !IsAnyTableImport() )
6774 PropertyMapPtr pContext
= GetTopContextOfType(CONTEXT_SECTION
);
6775 pSectionContext
= dynamic_cast< SectionPropertyMap
* >( pContext
.get() );
6778 return pSectionContext
;
6781 void DomainMapper_Impl::deferCharacterProperty(sal_Int32 id
, const css::uno::Any
& value
)
6783 deferredCharacterProperties
[ id
] = value
;
6786 void DomainMapper_Impl::processDeferredCharacterProperties()
6788 // Actually process in DomainMapper, so that it's the same source file like normal processing.
6789 if( !deferredCharacterProperties
.empty())
6791 m_rDMapper
.processDeferredCharacterProperties( deferredCharacterProperties
);
6792 deferredCharacterProperties
.clear();
6796 sal_Int32
DomainMapper_Impl::getNumberingProperty(const sal_Int32 nListId
, sal_Int32 nNumberingLevel
, const OUString
& aProp
)
6804 if (nNumberingLevel
< 0) // It seems it's valid to omit numbering level, and in that case it means zero.
6805 nNumberingLevel
= 0;
6807 auto const pList(GetListTable()->GetList(nListId
));
6808 const OUString aListName
= pList
->GetStyleName();
6809 const uno::Reference
< style::XStyleFamiliesSupplier
> xStylesSupplier(GetTextDocument(), uno::UNO_QUERY_THROW
);
6810 const uno::Reference
< container::XNameAccess
> xStyleFamilies
= xStylesSupplier
->getStyleFamilies();
6811 uno::Reference
<container::XNameAccess
> xNumberingStyles
;
6812 xStyleFamilies
->getByName("NumberingStyles") >>= xNumberingStyles
;
6813 const uno::Reference
<beans::XPropertySet
> xStyle(xNumberingStyles
->getByName(aListName
), uno::UNO_QUERY
);
6814 const uno::Reference
<container::XIndexAccess
> xNumberingRules(xStyle
->getPropertyValue("NumberingRules"), uno::UNO_QUERY
);
6815 if (xNumberingRules
.is())
6817 uno::Sequence
<beans::PropertyValue
> aProps
;
6818 xNumberingRules
->getByIndex(nNumberingLevel
) >>= aProps
;
6819 auto pProp
= std::find_if(aProps
.begin(), aProps
.end(),
6820 [&aProp
](const beans::PropertyValue
& rProp
) { return rProp
.Name
== aProp
; });
6821 if (pProp
!= aProps
.end())
6822 pProp
->Value
>>= nRet
;
6825 catch( const uno::Exception
& )
6827 // This can happen when the doc contains some hand-crafted invalid list level.
6833 sal_Int32
DomainMapper_Impl::getCurrentNumberingProperty(const OUString
& aProp
)
6837 std::optional
<PropertyMap::Property
> pProp
= m_pTopContext
->getProperty(PROP_NUMBERING_RULES
);
6838 uno::Reference
<container::XIndexAccess
> xNumberingRules
;
6840 xNumberingRules
.set(pProp
->second
, uno::UNO_QUERY
);
6841 pProp
= m_pTopContext
->getProperty(PROP_NUMBERING_LEVEL
);
6842 // Default numbering level is the first one.
6843 sal_Int32 nNumberingLevel
= 0;
6845 pProp
->second
>>= nNumberingLevel
;
6846 if (xNumberingRules
.is())
6848 uno::Sequence
<beans::PropertyValue
> aProps
;
6849 xNumberingRules
->getByIndex(nNumberingLevel
) >>= aProps
;
6850 auto pPropVal
= std::find_if(aProps
.begin(), aProps
.end(),
6851 [&aProp
](const beans::PropertyValue
& rProp
) { return rProp
.Name
== aProp
; });
6852 if (pPropVal
!= aProps
.end())
6853 pPropVal
->Value
>>= nRet
;
6860 void DomainMapper_Impl::enableInteropGrabBag(const OUString
& aName
)
6862 m_aInteropGrabBagName
= aName
;
6865 void DomainMapper_Impl::disableInteropGrabBag()
6867 m_aInteropGrabBagName
.clear();
6868 m_aInteropGrabBag
.clear();
6869 m_aSubInteropGrabBag
.clear();
6872 bool DomainMapper_Impl::isInteropGrabBagEnabled() const
6874 return !(m_aInteropGrabBagName
.isEmpty());
6877 void DomainMapper_Impl::appendGrabBag(std::vector
<beans::PropertyValue
>& rInteropGrabBag
, const OUString
& aKey
, const OUString
& aValue
)
6879 if (m_aInteropGrabBagName
.isEmpty())
6881 beans::PropertyValue aProperty
;
6882 aProperty
.Name
= aKey
;
6883 aProperty
.Value
<<= aValue
;
6884 rInteropGrabBag
.push_back(aProperty
);
6887 void DomainMapper_Impl::appendGrabBag(std::vector
<beans::PropertyValue
>& rInteropGrabBag
, const OUString
& aKey
, std::vector
<beans::PropertyValue
>& rValue
)
6889 if (m_aInteropGrabBagName
.isEmpty())
6891 beans::PropertyValue aProperty
;
6892 aProperty
.Name
= aKey
;
6893 aProperty
.Value
<<= comphelper::containerToSequence(rValue
);
6895 rInteropGrabBag
.push_back(aProperty
);
6898 void DomainMapper_Impl::substream(Id rName
,
6899 ::writerfilter::Reference
<Stream
>::Pointer_t
const& ref
)
6902 size_t contextSize(m_aContextStack
.size());
6903 size_t propSize
[NUMBER_OF_CONTEXTS
];
6904 for (int i
= 0; i
< NUMBER_OF_CONTEXTS
; ++i
) {
6905 propSize
[i
] = m_aPropertyStacks
[i
].size();
6909 // Save "has footnote" state, which is specific to a section in the body
6910 // text, so state from substreams is not relevant.
6911 bool bHasFtn
= m_bHasFtn
;
6913 //finalize any waiting frames before starting alternate streams
6914 CheckUnregisteredFrameConversion();
6915 ExecuteFrameConversion();
6917 appendTableManager();
6918 // Appending a TableManager resets its TableHandler, so we need to append
6919 // that as well, or tables won't be imported properly in headers/footers.
6920 appendTableHandler();
6921 getTableManager().startLevel();
6923 //import of page header/footer
6924 //Ensure that only one header/footer per section is pushed
6928 case NS_ooxml::LN_headerl
:
6929 PushPageHeader(SectionPropertyMap::PAGE_LEFT
);
6931 case NS_ooxml::LN_headerr
:
6932 PushPageHeader(SectionPropertyMap::PAGE_RIGHT
);
6934 case NS_ooxml::LN_headerf
:
6935 PushPageHeader(SectionPropertyMap::PAGE_FIRST
);
6937 case NS_ooxml::LN_footerl
:
6938 PushPageFooter(SectionPropertyMap::PAGE_LEFT
);
6940 case NS_ooxml::LN_footerr
:
6941 PushPageFooter(SectionPropertyMap::PAGE_RIGHT
);
6943 case NS_ooxml::LN_footerf
:
6944 PushPageFooter(SectionPropertyMap::PAGE_FIRST
);
6946 case NS_ooxml::LN_footnote
:
6947 case NS_ooxml::LN_endnote
:
6948 PushFootOrEndnote( NS_ooxml::LN_footnote
== rName
);
6950 case NS_ooxml::LN_annotation
:
6954 ref
->resolve(m_rDMapper
);
6958 case NS_ooxml::LN_headerl
:
6959 case NS_ooxml::LN_headerr
:
6960 case NS_ooxml::LN_headerf
:
6961 case NS_ooxml::LN_footerl
:
6962 case NS_ooxml::LN_footerr
:
6963 case NS_ooxml::LN_footerf
:
6964 PopPageHeaderFooter();
6966 case NS_ooxml::LN_footnote
:
6967 case NS_ooxml::LN_endnote
:
6970 case NS_ooxml::LN_annotation
:
6975 getTableManager().endLevel();
6977 m_bHasFtn
= bHasFtn
;
6981 case NS_ooxml::LN_footnote
:
6982 case NS_ooxml::LN_endnote
:
6983 m_pTableHandler
->setHadFootOrEndnote(true);
6988 // check that stacks are the same as before substream
6989 assert(m_aContextStack
.size() == contextSize
);
6990 for (int i
= 0; i
< NUMBER_OF_CONTEXTS
; ++i
) {
6991 assert(m_aPropertyStacks
[i
].size() == propSize
[i
]);
6996 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */