1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
21 #include <com/sun/star/beans/XPropertySet.hpp>
22 #include <com/sun/star/beans/XPropertyContainer.hpp>
23 #include <com/sun/star/document/XDocumentProperties.hpp>
24 #include "DomainMapper_Impl.hxx"
25 #include "ConversionHelper.hxx"
26 #include "SdtHelper.hxx"
27 #include "DomainMapperTableHandler.hxx"
28 #include <com/sun/star/uno/XComponentContext.hpp>
29 #include <com/sun/star/graphic/XGraphic.hpp>
30 #include <com/sun/star/beans/XPropertyState.hpp>
31 #include <com/sun/star/container/XNamed.hpp>
32 #include <com/sun/star/document/PrinterIndependentLayout.hpp>
33 #include <com/sun/star/document/IndexedPropertyValues.hpp>
34 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
35 #include <com/sun/star/embed/XEmbeddedObject.hpp>
36 #include <com/sun/star/lang/XServiceInfo.hpp>
37 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
38 #include <com/sun/star/style/LineNumberPosition.hpp>
39 #include <com/sun/star/style/LineSpacing.hpp>
40 #include <com/sun/star/style/LineSpacingMode.hpp>
41 #include <com/sun/star/text/ChapterFormat.hpp>
42 #include <com/sun/star/text/FilenameDisplayFormat.hpp>
43 #include <com/sun/star/text/SetVariableType.hpp>
44 #include <com/sun/star/text/XDocumentIndex.hpp>
45 #include <com/sun/star/text/XDocumentIndexesSupplier.hpp>
46 #include <com/sun/star/text/XFootnote.hpp>
47 #include <com/sun/star/text/XLineNumberingProperties.hpp>
48 #include <com/sun/star/style/XStyle.hpp>
49 #include <com/sun/star/text/PageNumberType.hpp>
50 #include <com/sun/star/text/HoriOrientation.hpp>
51 #include <com/sun/star/text/VertOrientation.hpp>
52 #include <com/sun/star/text/ReferenceFieldPart.hpp>
53 #include <com/sun/star/text/RelOrientation.hpp>
54 #include <com/sun/star/text/ReferenceFieldSource.hpp>
55 #include <com/sun/star/text/SizeType.hpp>
56 #include <com/sun/star/text/TextContentAnchorType.hpp>
57 #include <com/sun/star/text/WrapTextMode.hpp>
58 #include <com/sun/star/text/XDependentTextField.hpp>
59 #include <com/sun/star/text/XParagraphCursor.hpp>
60 #include <com/sun/star/text/XRedline.hpp>
61 #include <com/sun/star/text/XTextFieldsSupplier.hpp>
62 #include <com/sun/star/text/RubyPosition.hpp>
63 #include <com/sun/star/text/XTextRangeCompare.hpp>
64 #include <com/sun/star/style/DropCapFormat.hpp>
65 #include <com/sun/star/util/NumberFormatter.hpp>
66 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
67 #include <com/sun/star/util/XNumberFormatter.hpp>
68 #include <com/sun/star/document/XViewDataSupplier.hpp>
69 #include <com/sun/star/container/XIndexContainer.hpp>
70 #include <com/sun/star/awt/XControlModel.hpp>
71 #include <com/sun/star/drawing/XControlShape.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 "GraphicHelpers.hxx"
83 #include <dmapper/GraphicZOrderHelper.hxx>
84 
85 #include <oox/token/tokens.hxx>
86 
87 #include <cmath>
88 #include <map>
89 #include <tuple>
90 #include <unordered_map>
91 
92 #include <vcl/svapp.hxx>
93 #include <vcl/outdev.hxx>
94 #include <officecfg/Office/Common.hxx>
95 #include <filter/msfilter/util.hxx>
96 #include <filter/msfilter/ww8fields.hxx>
97 #include <comphelper/sequence.hxx>
98 #include <comphelper/propertyvalue.hxx>
99 #include <comphelper/propertysequence.hxx>
100 #include <unotools/configmgr.hxx>
101 #include <unotools/mediadescriptor.hxx>
102 #include <tools/diagnose_ex.h>
103 #include <tools/lineend.hxx>
104 #include <sal/log.hxx>
105 
106 
107 using namespace ::com::sun::star;
108 using namespace oox;
109 namespace writerfilter {
110 namespace dmapper{
111 
112 //line numbering for header/footer
lcl_linenumberingHeaderFooter(const uno::Reference<container::XNameContainer> & xStyles,const OUString & rname,DomainMapper_Impl * dmapper)113 static void lcl_linenumberingHeaderFooter( const uno::Reference<container::XNameContainer>& xStyles, const OUString& rname, DomainMapper_Impl* dmapper )
114 {
115     const StyleSheetEntryPtr pEntry = dmapper->GetStyleSheetTable()->FindStyleSheetByISTD( rname );
116     if (!pEntry)
117         return;
118     const StyleSheetPropertyMap* pStyleSheetProperties = dynamic_cast<const StyleSheetPropertyMap*>( pEntry->pProperties.get() );
119     if ( !pStyleSheetProperties )
120         return;
121     sal_Int32 nListId = pStyleSheetProperties->GetListId();
122     if( xStyles.is() )
123     {
124         if( xStyles->hasByName( rname ) )
125         {
126             uno::Reference< style::XStyle > xStyle;
127             xStyles->getByName( rname ) >>= xStyle;
128             if( !xStyle.is() )
129                 return;
130             uno::Reference<beans::XPropertySet> xPropertySet( xStyle, uno::UNO_QUERY );
131             xPropertySet->setPropertyValue( getPropertyName( PROP_PARA_LINE_NUMBER_COUNT ), uno::makeAny( nListId >= 0 ) );
132         }
133     }
134 }
135 
136 // Populate Dropdown Field properties from FFData structure
lcl_handleDropdownField(const uno::Reference<beans::XPropertySet> & rxFieldProps,const FFDataHandler::Pointer_t & pFFDataHandler)137 static void lcl_handleDropdownField( const uno::Reference< beans::XPropertySet >& rxFieldProps, const FFDataHandler::Pointer_t& pFFDataHandler )
138 {
139     if ( rxFieldProps.is() )
140     {
141         if ( !pFFDataHandler->getName().isEmpty() )
142             rxFieldProps->setPropertyValue( "Name", uno::makeAny( pFFDataHandler->getName() ) );
143 
144         const FFDataHandler::DropDownEntries_t& rEntries = pFFDataHandler->getDropDownEntries();
145         uno::Sequence< OUString > sItems( rEntries.size() );
146         ::std::copy( rEntries.begin(), rEntries.end(), sItems.begin());
147         if ( sItems.hasElements() )
148             rxFieldProps->setPropertyValue( "Items", uno::makeAny( sItems ) );
149 
150         sal_Int32 nResult = pFFDataHandler->getDropDownResult().toInt32();
151         if ( nResult )
152             rxFieldProps->setPropertyValue( "SelectedItem", uno::makeAny( sItems[ nResult ] ) );
153         if ( !pFFDataHandler->getHelpText().isEmpty() )
154              rxFieldProps->setPropertyValue( "Help", uno::makeAny( pFFDataHandler->getHelpText() ) );
155     }
156 }
157 
lcl_handleTextField(const uno::Reference<beans::XPropertySet> & rxFieldProps,const FFDataHandler::Pointer_t & pFFDataHandler)158 static void lcl_handleTextField( const uno::Reference< beans::XPropertySet >& rxFieldProps, const FFDataHandler::Pointer_t& pFFDataHandler )
159 {
160     if ( rxFieldProps.is() && pFFDataHandler )
161     {
162         rxFieldProps->setPropertyValue
163             (getPropertyName(PROP_HINT),
164             uno::makeAny(pFFDataHandler->getStatusText()));
165         rxFieldProps->setPropertyValue
166             (getPropertyName(PROP_HELP),
167             uno::makeAny(pFFDataHandler->getHelpText()));
168         rxFieldProps->setPropertyValue
169             (getPropertyName(PROP_CONTENT),
170             uno::makeAny(pFFDataHandler->getTextDefault()));
171     }
172 }
173 
174 struct FieldConversion
175 {
176     const sal_Char*     cFieldServiceName;
177     FieldId const       eFieldId;
178 };
179 
180 typedef std::unordered_map<OUString, FieldConversion> FieldConversionMap_t;
181 
182 /// Gives access to the parent field context of the topmost one, if there is any.
GetParentFieldContext(const std::deque<FieldContextPtr> & rFieldStack)183 static FieldContextPtr GetParentFieldContext(const std::deque<FieldContextPtr>& rFieldStack)
184 {
185     if (rFieldStack.size() < 2)
186     {
187         return nullptr;
188     }
189 
190     return rFieldStack[rFieldStack.size() - 2];
191 }
192 
193 /// Decides if the pInner field inside pOuter is allowed in Writer core, depending on their type.
IsFieldNestingAllowed(const FieldContextPtr & pOuter,const FieldContextPtr & pInner)194 static bool IsFieldNestingAllowed(const FieldContextPtr& pOuter, const FieldContextPtr& pInner)
195 {
196     if (!pOuter->GetFieldId())
197     {
198         return true;
199     }
200 
201     if (!pInner->GetFieldId())
202     {
203         return true;
204     }
205 
206     switch (pOuter->GetFieldId().get())
207     {
208         case FIELD_IF:
209         {
210             switch (pInner->GetFieldId().get())
211             {
212                 case FIELD_MERGEFIELD:
213                 {
214                     return false;
215                 }
216                 default:
217                     break;
218             }
219             break;
220         }
221         default:
222             break;
223     }
224 
225     return true;
226 }
227 
getPropertyValue(const OUString & propertyName)228 uno::Any FloatingTableInfo::getPropertyValue(const OUString &propertyName)
229 {
230     for( beans::PropertyValue const & propVal : m_aFrameProperties )
231         if( propVal.Name == propertyName )
232             return propVal.Value ;
233     return uno::Any() ;
234 }
235 
DomainMapper_Impl(DomainMapper & rDMapper,uno::Reference<uno::XComponentContext> const & xContext,uno::Reference<lang::XComponent> const & xModel,SourceDocumentType eDocumentType,utl::MediaDescriptor const & rMediaDesc)236 DomainMapper_Impl::DomainMapper_Impl(
237             DomainMapper& rDMapper,
238             uno::Reference<uno::XComponentContext> const& xContext,
239             uno::Reference<lang::XComponent> const& xModel,
240             SourceDocumentType eDocumentType,
241             utl::MediaDescriptor const & rMediaDesc) :
242         m_eDocumentType( eDocumentType ),
243         m_rDMapper( rDMapper ),
244         m_xTextDocument( xModel, uno::UNO_QUERY ),
245         m_xTextFactory( xModel, uno::UNO_QUERY ),
246         m_xComponentContext( xContext ),
247         m_bForceGenericFields(!utl::ConfigManager::IsFuzzing() && officecfg::Office::Common::Filter::Microsoft::Import::ForceImportWWFieldsAsGenericFields::get(m_xComponentContext)),
248         m_bSetUserFieldContent( false ),
249         m_bSetCitation( false ),
250         m_bSetDateValue( false ),
251         m_bIsFirstSection( true ),
252         m_bIsColumnBreakDeferred( false ),
253         m_bIsPageBreakDeferred( false ),
254         m_bSdtEndDeferred(false),
255         m_bParaSdtEndDeferred(false),
256         m_bStartTOC(false),
257         m_bStartTOCHeaderFooter(false),
258         m_bStartedTOC(false),
259         m_bStartIndex(false),
260         m_bStartBibliography(false),
261         m_nStartGenericField(0),
262         m_bTextInserted(false),
263         m_sCurrentPermId(0),
264         m_pLastSectionContext( ),
265         m_pLastCharacterContext(),
266         m_sCurrentParaStyleName(),
267         m_sDefaultParaStyleName(),
268         m_bInStyleSheetImport( false ),
269         m_bInAnyTableImport( false ),
270         m_eInHeaderFooterImport( HeaderFooterImportState::none ),
271         m_bDiscardHeaderFooter( false ),
272         m_bInFootOrEndnote(false),
273         m_bHasFootnoteStyle(false),
274         m_bCheckFootnoteStyle(false),
275         m_eSkipFootnoteState(SkipFootnoteSeparator::OFF),
276         m_bLineNumberingSet( false ),
277         m_bIsInFootnoteProperties( false ),
278         m_bIsParaMarkerChange( false ),
279         m_bParaChanged( false ),
280         m_bIsFirstParaInSection( true ),
281         m_bIsFirstParaInSectionAfterRedline( true ),
282         m_bDummyParaAddedForTableInSection( false ),
283         m_bTextFrameInserted(false),
284         m_bIsPreviousParagraphFramed( false ),
285         m_bIsLastParaInSection( false ),
286         m_bIsLastSectionGroup( false ),
287         m_bIsInComments( false ),
288         m_bParaSectpr( false ),
289         m_bUsingEnhancedFields( false ),
290         m_bSdt(false),
291         m_bIsFirstRun(false),
292         m_bIsOutsideAParagraph(true),
293         m_xAnnotationField(),
294         m_nAnnotationId( -1 ),
295         m_aAnnotationPositions(),
296         m_aSmartTagHandler(m_xComponentContext, m_xTextDocument),
297         m_xInsertTextRange(rMediaDesc.getUnpackedValueOrDefault("TextInsertModeRange", uno::Reference<text::XTextRange>())),
298         m_bIsNewDoc(!rMediaDesc.getUnpackedValueOrDefault("InsertMode", false)),
299         m_bIsReadGlossaries(rMediaDesc.getUnpackedValueOrDefault("ReadGlossaries", false)),
300         m_bInTableStyleRunProps(false),
301         m_nTableDepth(0),
302         m_nTableCellDepth(0),
303         m_nLastTableCellParagraphDepth(0),
304         m_bHasFtn(false),
305         m_bHasFtnSep(false),
306         m_bCheckFirstFootnoteTab(false),
307         m_bIgnoreNextTab(false),
308         m_bIsSplitPara(false),
309         m_bIsActualParagraphFramed( false ),
310         m_vTextFramesForChaining(),
311         m_bParaHadField(false),
312         m_bParaAutoBefore(false),
313         m_bFirstParagraphInCell(true),
314         m_bSaveFirstParagraphInCell(false)
315 
316 {
317     m_aBaseUrl = rMediaDesc.getUnpackedValueOrDefault(
318         utl::MediaDescriptor::PROP_DOCUMENTBASEURL(), OUString());
319     if (m_aBaseUrl.isEmpty()) {
320         m_aBaseUrl = rMediaDesc.getUnpackedValueOrDefault(
321             utl::MediaDescriptor::PROP_URL(), OUString());
322     }
323 
324     appendTableManager( );
325     GetBodyText();
326     uno::Reference< text::XTextAppend > xBodyTextAppend( m_xBodyText, uno::UNO_QUERY );
327     m_aTextAppendStack.push(TextAppendContext(xBodyTextAppend,
328                 m_bIsNewDoc ? uno::Reference<text::XTextCursor>() : m_xBodyText->createTextCursorByRange(m_xInsertTextRange)));
329 
330     //todo: does it makes sense to set the body text as static text interface?
331     uno::Reference< text::XTextAppendAndConvert > xBodyTextAppendAndConvert( m_xBodyText, uno::UNO_QUERY );
332     m_pTableHandler = new DomainMapperTableHandler(xBodyTextAppendAndConvert, *this);
333     getTableManager( ).setHandler(m_pTableHandler);
334 
335     getTableManager( ).startLevel();
336     m_bUsingEnhancedFields = !utl::ConfigManager::IsFuzzing() && officecfg::Office::Common::Filter::Microsoft::Import::ImportWWFieldsAsEnhancedFields::get(m_xComponentContext);
337 
338     m_pSdtHelper = new SdtHelper(*this);
339 
340     m_aRedlines.push(std::vector<RedlineParamsPtr>());
341 }
342 
343 
~DomainMapper_Impl()344 DomainMapper_Impl::~DomainMapper_Impl()
345 {
346     ChainTextFrames();
347     // Don't remove last paragraph when pasting, sw expects that empty paragraph.
348     if (m_bIsNewDoc)
349         RemoveLastParagraph();
350     if (hasTableManager())
351     {
352         getTableManager().endLevel();
353         popTableManager();
354     }
355 }
356 
GetPageStyles()357 uno::Reference< container::XNameContainer > const &  DomainMapper_Impl::GetPageStyles()
358 {
359     if(!m_xPageStyles1.is())
360     {
361         uno::Reference< style::XStyleFamiliesSupplier > xSupplier( m_xTextDocument, uno::UNO_QUERY );
362         if (xSupplier.is())
363             xSupplier->getStyleFamilies()->getByName("PageStyles") >>= m_xPageStyles1;
364     }
365     return m_xPageStyles1;
366 }
367 
GetUnusedPageStyleName()368 OUString DomainMapper_Impl::GetUnusedPageStyleName()
369 {
370     static const char DEFAULT_STYLE[] = "Converted";
371     if (!m_xNextUnusedPageStyleNo)
372     {
373         const uno::Sequence< OUString > aPageStyleNames = GetPageStyles()->getElementNames();
374         sal_Int32         nMaxIndex       = 0;
375         // find the highest number x in each style with the name "DEFAULT_STYLE+x" and
376         // return an incremented name
377 
378         for ( const auto& rStyleName : aPageStyleNames )
379         {
380             if ( rStyleName.startsWith( DEFAULT_STYLE ) )
381             {
382                 sal_Int32 nIndex = rStyleName.copy( strlen( DEFAULT_STYLE ) ).toInt32();
383                 if ( nIndex > nMaxIndex )
384                     nMaxIndex = nIndex;
385             }
386         }
387         m_xNextUnusedPageStyleNo = nMaxIndex + 1;
388     }
389 
390     OUString sPageStyleName = DEFAULT_STYLE + OUString::number( *m_xNextUnusedPageStyleNo );
391     *m_xNextUnusedPageStyleNo = *m_xNextUnusedPageStyleNo + 1;
392     return sPageStyleName;
393 }
394 
GetBodyText()395 uno::Reference< text::XText > const & DomainMapper_Impl::GetBodyText()
396 {
397     if(!m_xBodyText.is())
398     {
399         if (m_xInsertTextRange.is())
400             m_xBodyText = m_xInsertTextRange->getText();
401         else if (m_xTextDocument.is())
402             m_xBodyText = m_xTextDocument->getText();
403     }
404     return m_xBodyText;
405 }
406 
407 
GetDocumentSettings()408 uno::Reference< beans::XPropertySet > const & DomainMapper_Impl::GetDocumentSettings()
409 {
410     if( !m_xDocumentSettings.is() && m_xTextFactory.is())
411     {
412         m_xDocumentSettings.set( m_xTextFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY );
413     }
414     return m_xDocumentSettings;
415 }
416 
417 
SetDocumentSettingsProperty(const OUString & rPropName,const uno::Any & rValue)418 void DomainMapper_Impl::SetDocumentSettingsProperty( const OUString& rPropName, const uno::Any& rValue )
419 {
420     uno::Reference< beans::XPropertySet > xSettings = GetDocumentSettings();
421     if( xSettings.is() )
422     {
423         try
424         {
425             xSettings->setPropertyValue( rPropName, rValue );
426         }
427         catch( const uno::Exception& )
428         {
429         }
430     }
431 }
RemoveDummyParaForTableInSection()432 void DomainMapper_Impl::RemoveDummyParaForTableInSection()
433 {
434     SetIsDummyParaAddedForTableInSection(false);
435     PropertyMapPtr pContext = GetTopContextOfType(CONTEXT_SECTION);
436     SectionPropertyMap* pSectionContext = dynamic_cast< SectionPropertyMap* >( pContext.get() );
437     if (!pSectionContext)
438         return;
439 
440     if (m_aTextAppendStack.empty())
441         return;
442     uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
443     if (!xTextAppend.is())
444         return;
445 
446     uno::Reference< text::XTextCursor > xCursor = xTextAppend->createTextCursorByRange(pSectionContext->GetStartingRange());
447 
448     // Remove the extra NumPicBullets from the document,
449     // which get attached to the first paragraph in the
450     // document
451     ListsManager::Pointer pListTable = GetListTable();
452     pListTable->DisposeNumPicBullets();
453 
454     uno::Reference<container::XEnumerationAccess> xEnumerationAccess(xCursor, uno::UNO_QUERY);
455     if (xEnumerationAccess.is() && m_aTextAppendStack.size() == 1 )
456     {
457         uno::Reference<container::XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration();
458         uno::Reference<lang::XComponent> xParagraph(xEnumeration->nextElement(), uno::UNO_QUERY);
459         xParagraph->dispose();
460     }
461 }
AddDummyParaForTableInSection()462 void DomainMapper_Impl::AddDummyParaForTableInSection()
463 {
464     // Shapes can't have sections.
465     if (IsInShape())
466         return;
467 
468     if (!m_aTextAppendStack.empty())
469     {
470         uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
471         uno::Reference< text::XTextCursor > xCrsr = xTextAppend->getText()->createTextCursor();
472         uno::Reference< text::XText > xText = xTextAppend->getText();
473         if(xCrsr.is() && xText.is())
474         {
475             xTextAppend->finishParagraph(  uno::Sequence< beans::PropertyValue >() );
476             SetIsDummyParaAddedForTableInSection(true);
477         }
478     }
479 }
480 
RemoveLastParagraph()481 void DomainMapper_Impl::RemoveLastParagraph( )
482 {
483     if (m_bDiscardHeaderFooter)
484         return;
485 
486     if (m_aTextAppendStack.empty())
487         return;
488     uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
489     if (!xTextAppend.is())
490         return;
491     try
492     {
493         uno::Reference< text::XTextCursor > xCursor;
494         if (m_bIsNewDoc)
495         {
496             xCursor = xTextAppend->createTextCursor();
497             xCursor->gotoEnd(false);
498         }
499         else
500             xCursor = m_aTextAppendStack.top().xCursor;
501         uno::Reference<container::XEnumerationAccess> xEnumerationAccess(xCursor, uno::UNO_QUERY);
502         // Keep the character properties of the last but one paragraph, even if
503         // it's empty. This works for headers/footers, and maybe in other cases
504         // as well, but surely not in textboxes.
505         // fdo#58327: also do this at the end of the document: when pasting,
506         // a table before the cursor position would be deleted
507         // (but only for paste/insert, not load; otherwise it can happen that
508         // flys anchored at the disposed paragraph are deleted (fdo47036.rtf))
509         bool const bEndOfDocument(m_aTextAppendStack.size() == 1);
510         if ((IsInHeaderFooter() || (bEndOfDocument && !m_bIsNewDoc))
511             && xEnumerationAccess.is())
512         {
513             uno::Reference<container::XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration();
514             uno::Reference<lang::XComponent> xParagraph(xEnumeration->nextElement(), uno::UNO_QUERY);
515             xParagraph->dispose();
516         }
517         else if (xCursor.is())
518         {
519             xCursor->goLeft( 1, true );
520             // If this is a text on a shape, possibly the text has the trailing
521             // newline removed already.
522             if (xCursor->getString() == SAL_NEWLINE_STRING ||
523                     // tdf#105444 comments need an exception, if SAL_NEWLINE_STRING defined as "\r\n"
524                     (sizeof(SAL_NEWLINE_STRING)-1 == 2 && xCursor->getString() == "\n"))
525             {
526                 uno::Reference<beans::XPropertySet> xDocProps(GetTextDocument(), uno::UNO_QUERY);
527                 const OUString aRecordChanges("RecordChanges");
528                 uno::Any aPreviousValue(xDocProps->getPropertyValue(aRecordChanges));
529 
530                 // disable redlining for this operation, otherwise we might
531                 // end up with an unwanted recorded deletion
532                 xDocProps->setPropertyValue(aRecordChanges, uno::Any(false));
533 
534                 // delete
535                 xCursor->setString(OUString());
536 
537                 // restore again
538                 xDocProps->setPropertyValue(aRecordChanges, aPreviousValue);
539             }
540         }
541     }
542     catch( const uno::Exception& )
543     {
544     }
545 }
546 
547 
SetIsLastSectionGroup(bool bIsLast)548 void DomainMapper_Impl::SetIsLastSectionGroup( bool bIsLast )
549 {
550     m_bIsLastSectionGroup = bIsLast;
551 }
552 
SetIsLastParagraphInSection(bool bIsLast)553 void DomainMapper_Impl::SetIsLastParagraphInSection( bool bIsLast )
554 {
555     m_bIsLastParaInSection = bIsLast;
556 }
557 
558 
SetIsFirstParagraphInSection(bool bIsFirst)559 void DomainMapper_Impl::SetIsFirstParagraphInSection( bool bIsFirst )
560 {
561     m_bIsFirstParaInSection = bIsFirst;
562 }
563 
SetIsFirstParagraphInSectionAfterRedline(bool bIsFirstAfterRedline)564 void DomainMapper_Impl::SetIsFirstParagraphInSectionAfterRedline( bool bIsFirstAfterRedline )
565 {
566     m_bIsFirstParaInSectionAfterRedline = bIsFirstAfterRedline;
567 }
568 
GetIsFirstParagraphInSection(bool bAfterRedline) const569 bool DomainMapper_Impl::GetIsFirstParagraphInSection( bool bAfterRedline ) const
570 {
571     // Anchored objects may include multiple paragraphs,
572     // and none of them should be considered the first para in section.
573     return ( bAfterRedline ? m_bIsFirstParaInSectionAfterRedline : m_bIsFirstParaInSection )
574                 && !IsInShape()
575                 && !m_bIsInComments
576                 && !IsInFootOrEndnote();
577 }
578 
SetIsFirstParagraphInShape(bool bIsFirst)579 void DomainMapper_Impl::SetIsFirstParagraphInShape(bool bIsFirst)
580 {
581     m_bIsFirstParaInShape = bIsFirst;
582 }
583 
SetIsDummyParaAddedForTableInSection(bool bIsAdded)584 void DomainMapper_Impl::SetIsDummyParaAddedForTableInSection( bool bIsAdded )
585 {
586     m_bDummyParaAddedForTableInSection = bIsAdded;
587 }
588 
589 
SetIsTextFrameInserted(bool bIsInserted)590 void DomainMapper_Impl::SetIsTextFrameInserted( bool bIsInserted )
591 {
592     m_bTextFrameInserted  = bIsInserted;
593 }
594 
595 
SetParaSectpr(bool bParaSectpr)596 void DomainMapper_Impl::SetParaSectpr(bool bParaSectpr)
597 {
598     m_bParaSectpr = bParaSectpr;
599 }
600 
601 
SetSdt(bool bSdt)602 void DomainMapper_Impl::SetSdt(bool bSdt)
603 {
604     m_bSdt = bSdt;
605 
606     if (m_bSdt && !m_aTextAppendStack.empty())
607     {
608         m_xStdEntryStart = GetTopTextAppend()->getEnd();
609     }
610     else
611     {
612         m_xStdEntryStart = uno::Reference< text::XTextRange >();
613     }
614 }
615 
616 
PushProperties(ContextType eId)617 void    DomainMapper_Impl::PushProperties(ContextType eId)
618 {
619     PropertyMapPtr pInsert(eId == CONTEXT_SECTION ?
620         (new SectionPropertyMap( m_bIsFirstSection )) :
621         eId == CONTEXT_PARAGRAPH ? new ParagraphPropertyMap :  new PropertyMap);
622     if(eId == CONTEXT_SECTION)
623     {
624         if( m_bIsFirstSection )
625             m_bIsFirstSection = false;
626         // beginning with the second section group a section has to be inserted
627         // into the document
628         SectionPropertyMap* pSectionContext_ = dynamic_cast< SectionPropertyMap* >( pInsert.get() );
629         if (!m_aTextAppendStack.empty())
630         {
631             uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
632             if (xTextAppend.is() && pSectionContext_)
633                 pSectionContext_->SetStart( xTextAppend->getEnd() );
634         }
635     }
636     if(eId == CONTEXT_PARAGRAPH && m_bIsSplitPara)
637     {
638         m_aPropertyStacks[eId].push( GetTopContextOfType(eId));
639         m_bIsSplitPara = false;
640     }
641     else
642     {
643         m_aPropertyStacks[eId].push( pInsert );
644     }
645     m_aContextStack.push(eId);
646 
647     m_pTopContext = m_aPropertyStacks[eId].top();
648 }
649 
650 
PushStyleProperties(const PropertyMapPtr & pStyleProperties)651 void DomainMapper_Impl::PushStyleProperties( const PropertyMapPtr& pStyleProperties )
652 {
653     m_aPropertyStacks[CONTEXT_STYLESHEET].push( pStyleProperties );
654     m_aContextStack.push(CONTEXT_STYLESHEET);
655 
656     m_pTopContext = m_aPropertyStacks[CONTEXT_STYLESHEET].top();
657 }
658 
659 
PushListProperties(const PropertyMapPtr & pListProperties)660 void DomainMapper_Impl::PushListProperties(const PropertyMapPtr& pListProperties)
661 {
662     m_aPropertyStacks[CONTEXT_LIST].push( pListProperties );
663     m_aContextStack.push(CONTEXT_LIST);
664     m_pTopContext = m_aPropertyStacks[CONTEXT_LIST].top();
665 }
666 
667 
PopProperties(ContextType eId)668 void    DomainMapper_Impl::PopProperties(ContextType eId)
669 {
670     OSL_ENSURE(!m_aPropertyStacks[eId].empty(), "section stack already empty");
671     if ( m_aPropertyStacks[eId].empty() )
672         return;
673 
674     if ( eId == CONTEXT_SECTION )
675     {
676         if (m_aPropertyStacks[eId].size() == 1) // tdf#112202 only top level !!!
677         {
678             m_pLastSectionContext = m_aPropertyStacks[eId].top();
679         }
680     }
681     else if (eId == CONTEXT_CHARACTER)
682     {
683         m_pLastCharacterContext = m_aPropertyStacks[eId].top();
684         // Sadly an assert about deferredCharacterProperties being empty is not possible
685         // here, because appendTextPortion() may not be called for every character section.
686         deferredCharacterProperties.clear();
687     }
688 
689     if (!IsInFootOrEndnote() && IsInCustomFootnote() && !m_aPropertyStacks[eId].empty())
690     {
691         PropertyMapPtr pRet = m_aPropertyStacks[eId].top();
692         if (pRet->GetFootnote().is() && m_pFootnoteContext.is())
693             EndCustomFootnote();
694     }
695 
696     m_aPropertyStacks[eId].pop();
697     m_aContextStack.pop();
698     if(!m_aContextStack.empty() && !m_aPropertyStacks[m_aContextStack.top()].empty())
699 
700             m_pTopContext = m_aPropertyStacks[m_aContextStack.top()].top();
701     else
702     {
703         // OSL_ENSURE(eId == CONTEXT_SECTION, "this should happen at a section context end");
704         m_pTopContext.clear();
705     }
706 }
707 
708 
GetTopContextOfType(ContextType eId)709 PropertyMapPtr DomainMapper_Impl::GetTopContextOfType(ContextType eId)
710 {
711     PropertyMapPtr pRet;
712     if(!m_aPropertyStacks[eId].empty())
713         pRet = m_aPropertyStacks[eId].top();
714     return pRet;
715 }
716 
HasTopText() const717 bool DomainMapper_Impl::HasTopText() const
718 {
719     return !m_aTextAppendStack.empty();
720 }
721 
GetTopTextAppend()722 uno::Reference< text::XTextAppend > const &  DomainMapper_Impl::GetTopTextAppend()
723 {
724     OSL_ENSURE(!m_aTextAppendStack.empty(), "text append stack is empty" );
725     return m_aTextAppendStack.top().xTextAppend;
726 }
727 
GetTopFieldContext()728 FieldContextPtr const &  DomainMapper_Impl::GetTopFieldContext()
729 {
730     SAL_WARN_IF(m_aFieldStack.empty(), "writerfilter.dmapper", "Field stack is empty");
731     return m_aFieldStack.back();
732 }
733 
InitTabStopFromStyle(const uno::Sequence<style::TabStop> & rInitTabStops)734 void DomainMapper_Impl::InitTabStopFromStyle( const uno::Sequence< style::TabStop >& rInitTabStops )
735 {
736     OSL_ENSURE(m_aCurrentTabStops.empty(), "tab stops already initialized");
737     for( const auto& rTabStop : rInitTabStops)
738     {
739         m_aCurrentTabStops.emplace_back(rTabStop);
740     }
741 }
742 
IncorporateTabStop(const DeletableTabStop & rTabStop)743 void DomainMapper_Impl::IncorporateTabStop( const DeletableTabStop &  rTabStop )
744 {
745     sal_Int32 nConverted = rTabStop.Position;
746     auto aIt = std::find_if(m_aCurrentTabStops.begin(), m_aCurrentTabStops.end(),
747         [&nConverted](const DeletableTabStop& rCurrentTabStop) { return rCurrentTabStop.Position == nConverted; });
748     if( aIt != m_aCurrentTabStops.end() )
749     {
750         if( rTabStop.bDeleted )
751             m_aCurrentTabStops.erase( aIt );
752         else
753             *aIt = rTabStop;
754     }
755     else
756         m_aCurrentTabStops.push_back( rTabStop );
757 }
758 
759 
GetCurrentTabStopAndClear()760 uno::Sequence< style::TabStop > DomainMapper_Impl::GetCurrentTabStopAndClear()
761 {
762     std::vector<style::TabStop> aRet;
763     for (const DeletableTabStop& rStop : m_aCurrentTabStops)
764     {
765         if (!rStop.bDeleted)
766             aRet.push_back(rStop);
767     }
768     m_aCurrentTabStops.clear();
769     return comphelper::containerToSequence(aRet);
770 }
771 
GetCurrentParaStyleName()772 OUString DomainMapper_Impl::GetCurrentParaStyleName()
773 {
774     // use saved currParaStyleName as a fallback, in case no particular para style name applied.
775     OUString sName = m_sCurrentParaStyleName;
776     PropertyMapPtr pParaContext = GetTopContextOfType(CONTEXT_PARAGRAPH);
777     if ( pParaContext && pParaContext->isSet(PROP_PARA_STYLE_NAME) )
778         pParaContext->getProperty(PROP_PARA_STYLE_NAME)->second >>= sName;
779 
780     // In rare situations the name might still be blank, so use the default style,
781     // despite documentation that states, "If this attribute is not specified for any style,
782     // then no properties shall be applied to objects of the specified type."
783     // Word, however, assigns "Normal" style even in these situations.
784     if ( !m_bInStyleSheetImport && sName.isEmpty() )
785         sName = GetDefaultParaStyleName();
786 
787     return sName;
788 }
789 
GetDefaultParaStyleName()790 OUString DomainMapper_Impl::GetDefaultParaStyleName()
791 {
792     // After import the default style won't change and is frequently requested: cache the LO style name.
793     // TODO assert !InStyleSheetImport? This function really only makes sense once import is finished anyway.
794     if ( m_sDefaultParaStyleName.isEmpty() )
795     {
796         const StyleSheetEntryPtr pEntry = GetStyleSheetTable()->FindDefaultParaStyle();
797         if ( pEntry && !pEntry->sConvertedStyleName.isEmpty() )
798         {
799             if ( !m_bInStyleSheetImport )
800                 m_sDefaultParaStyleName = pEntry->sConvertedStyleName;
801             return pEntry->sConvertedStyleName;
802         }
803         else
804             return "Standard";
805     }
806     return m_sDefaultParaStyleName;
807 }
808 
GetPropertyFromStyleSheet(PropertyIds eId,StyleSheetEntryPtr pEntry,const bool bDocDefaults,const bool bPara)809 uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara)
810 {
811     while(pEntry.get( ) )
812     {
813         if(pEntry->pProperties)
814         {
815             boost::optional<PropertyMap::Property> aProperty =
816                     pEntry->pProperties->getProperty(eId);
817             if( aProperty )
818             {
819                 return aProperty->second;
820             }
821         }
822         //search until the property is set or no parent is available
823         StyleSheetEntryPtr pNewEntry;
824         if ( !pEntry->sBaseStyleIdentifier.isEmpty() )
825             pNewEntry = GetStyleSheetTable()->FindStyleSheetByISTD(pEntry->sBaseStyleIdentifier);
826 
827         SAL_WARN_IF( pEntry == pNewEntry, "writerfilter.dmapper", "circular loop in style hierarchy?");
828 
829         if (pEntry == pNewEntry) //fdo#49587
830             break;
831 
832         pEntry = pNewEntry;
833     }
834     // not found in style, try the document's DocDefault properties
835     if ( bDocDefaults && bPara )
836     {
837         const PropertyMapPtr& pDefaultParaProps = GetStyleSheetTable()->GetDefaultParaProps();
838         if ( pDefaultParaProps )
839         {
840             boost::optional<PropertyMap::Property> aProperty = pDefaultParaProps->getProperty(eId);
841             if ( aProperty )
842                 return aProperty->second;
843         }
844     }
845     if ( bDocDefaults && isCharacterProperty(eId) )
846     {
847         const PropertyMapPtr& pDefaultCharProps = GetStyleSheetTable()->GetDefaultCharProps();
848         if ( pDefaultCharProps )
849         {
850             boost::optional<PropertyMap::Property> aProperty = pDefaultCharProps->getProperty(eId);
851             if ( aProperty )
852                 return aProperty->second;
853         }
854     }
855     return uno::Any();
856 }
857 
GetPropertyFromParaStyleSheet(PropertyIds eId)858 uno::Any DomainMapper_Impl::GetPropertyFromParaStyleSheet(PropertyIds eId)
859 {
860     StyleSheetEntryPtr pEntry;
861     if ( m_bInStyleSheetImport )
862         pEntry = GetStyleSheetTable()->GetCurrentEntry();
863     else
864         pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(GetCurrentParaStyleName());
865     return GetPropertyFromStyleSheet(eId, pEntry, /*bDocDefaults=*/true, /*bPara=*/true);
866 }
867 
GetPropertyFromCharStyleSheet(PropertyIds eId,const PropertyMapPtr & rContext)868 uno::Any DomainMapper_Impl::GetPropertyFromCharStyleSheet(PropertyIds eId, const PropertyMapPtr& rContext)
869 {
870     if ( m_bInStyleSheetImport || eId == PROP_CHAR_STYLE_NAME || !isCharacterProperty(eId) )
871         return uno::Any();
872 
873     StyleSheetEntryPtr pEntry;
874     OUString sCharStyleName;
875     if ( GetAnyProperty(PROP_CHAR_STYLE_NAME, rContext) >>= sCharStyleName )
876         pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(sCharStyleName);
877     return GetPropertyFromStyleSheet(eId, pEntry, /*bDocDefaults=*/false, /*bPara=*/false);
878 }
879 
GetAnyProperty(PropertyIds eId,const PropertyMapPtr & rContext)880 uno::Any DomainMapper_Impl::GetAnyProperty(PropertyIds eId, const PropertyMapPtr& rContext)
881 {
882     // first look in directly applied attributes
883     if ( rContext )
884     {
885         boost::optional<PropertyMap::Property> aProperty = rContext->getProperty(eId);
886         if ( aProperty )
887             return aProperty->second;
888     }
889 
890     // then look whether it was inherited from a directly applied character style
891     if ( eId != PROP_CHAR_STYLE_NAME && isCharacterProperty(eId) )
892     {
893         uno::Any aRet = GetPropertyFromCharStyleSheet(eId, rContext);
894         if ( aRet.hasValue() )
895             return aRet;
896     }
897 
898     // then look in current paragraph style, and docDefaults
899     return GetPropertyFromParaStyleSheet(eId);
900 }
901 
GetListTable()902 ListsManager::Pointer const & DomainMapper_Impl::GetListTable()
903 {
904     if(!m_pListTable)
905         m_pListTable =
906             new ListsManager( m_rDMapper, m_xTextFactory );
907     return m_pListTable;
908 }
909 
910 
deferBreak(BreakType deferredBreakType)911 void DomainMapper_Impl::deferBreak( BreakType deferredBreakType)
912 {
913     switch (deferredBreakType)
914     {
915     case COLUMN_BREAK:
916             m_bIsColumnBreakDeferred = true;
917         break;
918     case PAGE_BREAK:
919             // See SwWW8ImplReader::HandlePageBreakChar(), page break should be
920             // ignored inside tables.
921             if (m_nTableDepth > 0)
922                 return;
923 
924             m_bIsPageBreakDeferred = true;
925         break;
926     default:
927         return;
928     }
929 }
930 
isBreakDeferred(BreakType deferredBreakType)931 bool DomainMapper_Impl::isBreakDeferred( BreakType deferredBreakType )
932 {
933     switch (deferredBreakType)
934     {
935     case COLUMN_BREAK:
936         return m_bIsColumnBreakDeferred;
937     case PAGE_BREAK:
938         return m_bIsPageBreakDeferred;
939     default:
940         return false;
941     }
942 }
943 
clearDeferredBreak(BreakType deferredBreakType)944 void DomainMapper_Impl::clearDeferredBreak(BreakType deferredBreakType)
945 {
946     switch (deferredBreakType)
947     {
948     case COLUMN_BREAK:
949         m_bIsColumnBreakDeferred = false;
950         break;
951     case PAGE_BREAK:
952         m_bIsPageBreakDeferred = false;
953         break;
954     default:
955         break;
956     }
957 }
958 
clearDeferredBreaks()959 void DomainMapper_Impl::clearDeferredBreaks()
960 {
961     m_bIsColumnBreakDeferred = false;
962     m_bIsPageBreakDeferred = false;
963 }
964 
setSdtEndDeferred(bool bSdtEndDeferred)965 void DomainMapper_Impl::setSdtEndDeferred(bool bSdtEndDeferred)
966 {
967     m_bSdtEndDeferred = bSdtEndDeferred;
968 }
969 
isSdtEndDeferred() const970 bool DomainMapper_Impl::isSdtEndDeferred() const
971 {
972     return m_bSdtEndDeferred;
973 }
974 
setParaSdtEndDeferred(bool bParaSdtEndDeferred)975 void DomainMapper_Impl::setParaSdtEndDeferred(bool bParaSdtEndDeferred)
976 {
977     m_bParaSdtEndDeferred = bParaSdtEndDeferred;
978 }
979 
isParaSdtEndDeferred() const980 bool DomainMapper_Impl::isParaSdtEndDeferred() const
981 {
982     return m_bParaSdtEndDeferred;
983 }
984 
lcl_MoveBorderPropertiesToFrame(std::vector<beans::PropertyValue> & rFrameProperties,uno::Reference<text::XTextRange> const & xStartTextRange,uno::Reference<text::XTextRange> const & xEndTextRange)985 static void lcl_MoveBorderPropertiesToFrame(std::vector<beans::PropertyValue>& rFrameProperties,
986     uno::Reference<text::XTextRange> const& xStartTextRange,
987     uno::Reference<text::XTextRange> const& xEndTextRange )
988 {
989     try
990     {
991         if (!xStartTextRange.is())   //rhbz#1077780
992             return;
993         uno::Reference<text::XTextCursor> xRangeCursor = xStartTextRange->getText()->createTextCursorByRange( xStartTextRange );
994         xRangeCursor->gotoRange( xEndTextRange, true );
995 
996         uno::Reference<beans::XPropertySet> xTextRangeProperties(xRangeCursor, uno::UNO_QUERY);
997         if(!xTextRangeProperties.is())
998             return ;
999 
1000         static PropertyIds const aBorderProperties[] =
1001         {
1002             PROP_LEFT_BORDER,
1003             PROP_RIGHT_BORDER,
1004             PROP_TOP_BORDER,
1005             PROP_BOTTOM_BORDER,
1006             PROP_LEFT_BORDER_DISTANCE,
1007             PROP_RIGHT_BORDER_DISTANCE,
1008             PROP_TOP_BORDER_DISTANCE,
1009             PROP_BOTTOM_BORDER_DISTANCE
1010         };
1011 
1012         for( size_t nProperty = 0; nProperty < SAL_N_ELEMENTS( aBorderProperties ); ++nProperty)
1013         {
1014             OUString sPropertyName = getPropertyName(aBorderProperties[nProperty]);
1015             beans::PropertyValue aValue;
1016             aValue.Name = sPropertyName;
1017             aValue.Value = xTextRangeProperties->getPropertyValue(sPropertyName);
1018             rFrameProperties.push_back(aValue);
1019             if( nProperty < 4 )
1020                 xTextRangeProperties->setPropertyValue( sPropertyName, uno::makeAny(table::BorderLine2()));
1021         }
1022     }
1023     catch( const uno::Exception& )
1024     {
1025     }
1026 }
1027 
1028 
lcl_AddRangeAndStyle(ParagraphPropertiesPtr const & pToBeSavedProperties,uno::Reference<text::XTextAppend> const & xTextAppend,const PropertyMapPtr & pPropertyMap,TextAppendContext const & rAppendContext)1029 static void lcl_AddRangeAndStyle(
1030     ParagraphPropertiesPtr const & pToBeSavedProperties,
1031     uno::Reference< text::XTextAppend > const& xTextAppend,
1032     const PropertyMapPtr& pPropertyMap,
1033     TextAppendContext const & rAppendContext)
1034 {
1035     uno::Reference<text::XParagraphCursor> xParaCursor(
1036         xTextAppend->createTextCursorByRange( rAppendContext.xInsertPosition.is() ? rAppendContext.xInsertPosition : xTextAppend->getEnd()), uno::UNO_QUERY_THROW );
1037     pToBeSavedProperties->SetEndingRange(xParaCursor->getStart());
1038     xParaCursor->gotoStartOfParagraph( false );
1039 
1040     pToBeSavedProperties->SetStartingRange(xParaCursor->getStart());
1041     if(pPropertyMap)
1042     {
1043         boost::optional<PropertyMap::Property> aParaStyle = pPropertyMap->getProperty(PROP_PARA_STYLE_NAME);
1044         if( aParaStyle )
1045         {
1046             OUString sName;
1047             aParaStyle->second >>= sName;
1048             pToBeSavedProperties->SetParaStyleName(sName);
1049         }
1050     }
1051 }
1052 
1053 
1054 //define some default frame width - 0cm ATM: this allow the frame to be wrapped around the text
1055 #define DEFAULT_FRAME_MIN_WIDTH 0
1056 #define DEFAULT_FRAME_MIN_HEIGHT 0
1057 #define DEFAULT_VALUE 0
1058 
CheckUnregisteredFrameConversion()1059 void DomainMapper_Impl::CheckUnregisteredFrameConversion( )
1060 {
1061     if (m_aTextAppendStack.empty())
1062         return;
1063     TextAppendContext& rAppendContext = m_aTextAppendStack.top();
1064     // n#779642: ignore fly frame inside table as it could lead to messy situations
1065     if (!rAppendContext.pLastParagraphProperties.get())
1066         return;
1067     if (!rAppendContext.pLastParagraphProperties->IsFrameMode())
1068         return;
1069     if (!hasTableManager())
1070         return;
1071     if (getTableManager().isInTable())
1072         return;
1073     try
1074     {
1075         StyleSheetEntryPtr pParaStyle =
1076             GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(rAppendContext.pLastParagraphProperties->GetParaStyleName());
1077 
1078         std::vector<beans::PropertyValue> aFrameProperties;
1079 
1080         if ( pParaStyle.get( ) )
1081         {
1082             const ParagraphProperties* pStyleProperties = dynamic_cast<const ParagraphProperties*>( pParaStyle->pProperties.get() );
1083             if (!pStyleProperties)
1084                 return;
1085             sal_Int32 nWidth =
1086                 rAppendContext.pLastParagraphProperties->Getw() > 0 ?
1087                     rAppendContext.pLastParagraphProperties->Getw() :
1088                     pStyleProperties->Getw();
1089             bool bAutoWidth = nWidth < 1;
1090             if( bAutoWidth )
1091                 nWidth = DEFAULT_FRAME_MIN_WIDTH;
1092             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH), nWidth));
1093 
1094             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HEIGHT),
1095                 rAppendContext.pLastParagraphProperties->Geth() > 0 ?
1096                     rAppendContext.pLastParagraphProperties->Geth() :
1097                     pStyleProperties->Geth() > 0 ? pStyleProperties->Geth() : DEFAULT_FRAME_MIN_HEIGHT));
1098 
1099             sal_Int16 nhRule = sal_Int16(
1100                 rAppendContext.pLastParagraphProperties->GethRule() >= 0 ?
1101                     rAppendContext.pLastParagraphProperties->GethRule() :
1102                     pStyleProperties->GethRule());
1103             if ( nhRule < 0 )
1104             {
1105                 if ( rAppendContext.pLastParagraphProperties->Geth() >= 0 ||
1106                     pStyleProperties->GethRule() >= 0 )
1107                 {
1108                     // [MS-OE376] Word uses a default value of "atLeast" for
1109                     // this attribute when the value of the h attribute is not 0.
1110                     nhRule = text::SizeType::MIN;
1111                 }
1112                 else
1113                 {
1114                     nhRule = text::SizeType::VARIABLE;
1115                 }
1116             }
1117             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SIZE_TYPE), nhRule));
1118 
1119             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH_TYPE), bAutoWidth ?  text::SizeType::MIN : text::SizeType::FIX));
1120 
1121             sal_Int16 nHoriOrient = sal_Int16(
1122                 rAppendContext.pLastParagraphProperties->GetxAlign() >= 0 ?
1123                     rAppendContext.pLastParagraphProperties->GetxAlign() :
1124                     pStyleProperties->GetxAlign() >= 0 ? pStyleProperties->GetxAlign() : text::HoriOrientation::NONE );
1125             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT), nHoriOrient));
1126 
1127             //set a non negative default value
1128             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT_POSITION),
1129                 rAppendContext.pLastParagraphProperties->IsxValid() ?
1130                     rAppendContext.pLastParagraphProperties->Getx() :
1131                     pStyleProperties->IsxValid() ? pStyleProperties->Getx() : DEFAULT_VALUE));
1132 
1133             //Default the anchor in case FramePr_hAnchor is missing ECMA 17.3.1.11
1134             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT_RELATION), sal_Int16(
1135                 rAppendContext.pLastParagraphProperties->GethAnchor() >= 0 ?
1136                     rAppendContext.pLastParagraphProperties->GethAnchor() :
1137                 pStyleProperties->GethAnchor() >=0 ? pStyleProperties->GethAnchor() : text::RelOrientation::FRAME )));
1138 
1139             sal_Int16 nVertOrient = sal_Int16(
1140                 rAppendContext.pLastParagraphProperties->GetyAlign() >= 0 ?
1141                     rAppendContext.pLastParagraphProperties->GetyAlign() :
1142                     pStyleProperties->GetyAlign() >= 0 ? pStyleProperties->GetyAlign() : text::VertOrientation::NONE );
1143             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT), nVertOrient));
1144 
1145             //set a non negative default value
1146             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_POSITION),
1147                 rAppendContext.pLastParagraphProperties->IsyValid() ?
1148                     rAppendContext.pLastParagraphProperties->Gety() :
1149                     pStyleProperties->IsyValid() ? pStyleProperties->Gety() : DEFAULT_VALUE));
1150 
1151             //Default the anchor in case FramePr_vAnchor is missing ECMA 17.3.1.11
1152             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_RELATION), sal_Int16(
1153                 rAppendContext.pLastParagraphProperties->GetvAnchor() >= 0 ?
1154                     rAppendContext.pLastParagraphProperties->GetvAnchor() :
1155                     pStyleProperties->GetvAnchor() >= 0 ? pStyleProperties->GetvAnchor() : text::RelOrientation::FRAME )));
1156 
1157             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SURROUND),
1158                 rAppendContext.pLastParagraphProperties->GetWrap() != text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
1159                 ? rAppendContext.pLastParagraphProperties->GetWrap()
1160                 : pStyleProperties->GetWrap() != text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
1161                   ? pStyleProperties->GetWrap()
1162                   : text::WrapTextMode_NONE ));
1163 
1164             /** FDO#73546 : distL & distR should be unsigned integers <Ecma 20.4.3.6>
1165                 Swapped the array elements 11,12 & 13,14 since 11 & 12 are
1166                 LEFT & RIGHT margins and 13,14 are TOP and BOTTOM margins respectively.
1167             */
1168             sal_Int32 nRightDist;
1169             sal_Int32 nLeftDist = nRightDist =
1170                 rAppendContext.pLastParagraphProperties->GethSpace() >= 0 ?
1171                 rAppendContext.pLastParagraphProperties->GethSpace() :
1172                 pStyleProperties->GethSpace() >= 0 ? pStyleProperties->GethSpace() : 0;
1173 
1174             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_LEFT_MARGIN), nHoriOrient == text::HoriOrientation::LEFT ? 0 : nLeftDist));
1175             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_RIGHT_MARGIN), nHoriOrient == text::HoriOrientation::RIGHT ? 0 : nRightDist));
1176 
1177             sal_Int32 nBottomDist;
1178             sal_Int32 nTopDist = nBottomDist =
1179                 rAppendContext.pLastParagraphProperties->GetvSpace() >= 0 ?
1180                 rAppendContext.pLastParagraphProperties->GetvSpace() :
1181                 pStyleProperties->GetvSpace() >= 0 ? pStyleProperties->GetvSpace() : 0;
1182 
1183             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_TOP_MARGIN), nVertOrient == text::VertOrientation::TOP ? 0 : nTopDist));
1184             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_BOTTOM_MARGIN), nVertOrient == text::VertOrientation::BOTTOM ? 0 : nBottomDist));
1185             // If there is no fill, the Word default is 100% transparency.
1186             // Otherwise CellColorHandler has priority, and this setting
1187             // will be ignored.
1188             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_BACK_COLOR_TRANSPARENCY), sal_Int32(100)));
1189 
1190             uno::Sequence<beans::PropertyValue> aGrabBag( comphelper::InitPropertySequence({
1191                     { "ParaFrameProperties", uno::Any(rAppendContext.pLastParagraphProperties->IsFrameMode()) }
1192             }));
1193             aFrameProperties.push_back(comphelper::makePropertyValue("FrameInteropGrabBag", aGrabBag));
1194 
1195             lcl_MoveBorderPropertiesToFrame(aFrameProperties,
1196                 rAppendContext.pLastParagraphProperties->GetStartingRange(),
1197                 rAppendContext.pLastParagraphProperties->GetEndingRange());
1198         }
1199         else
1200         {
1201             sal_Int32 nWidth = rAppendContext.pLastParagraphProperties->Getw();
1202             bool bAutoWidth = nWidth < 1;
1203             if( bAutoWidth )
1204                 nWidth = DEFAULT_FRAME_MIN_WIDTH;
1205             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH), nWidth));
1206 
1207             sal_Int16 nhRule = sal_Int16(rAppendContext.pLastParagraphProperties->GethRule());
1208             if ( nhRule < 0 )
1209             {
1210                 if ( rAppendContext.pLastParagraphProperties->Geth() >= 0 )
1211                 {
1212                     // [MS-OE376] Word uses a default value of atLeast for
1213                     // this attribute when the value of the h attribute is not 0.
1214                     nhRule = text::SizeType::MIN;
1215                 }
1216                 else
1217                 {
1218                     nhRule = text::SizeType::VARIABLE;
1219                 }
1220             }
1221             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SIZE_TYPE), nhRule));
1222 
1223             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH_TYPE), bAutoWidth ?  text::SizeType::MIN : text::SizeType::FIX));
1224 
1225             sal_Int16 nHoriOrient = sal_Int16(
1226                 rAppendContext.pLastParagraphProperties->GetxAlign() >= 0 ?
1227                     rAppendContext.pLastParagraphProperties->GetxAlign() :
1228                     text::HoriOrientation::NONE );
1229             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT), nHoriOrient));
1230 
1231             sal_Int16 nVertOrient = sal_Int16(
1232                 rAppendContext.pLastParagraphProperties->GetyAlign() >= 0 ?
1233                     rAppendContext.pLastParagraphProperties->GetyAlign() :
1234                     text::VertOrientation::NONE );
1235             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT), nVertOrient));
1236 
1237             sal_Int32 nVertDist = rAppendContext.pLastParagraphProperties->GethSpace();
1238             if( nVertDist < 0 )
1239                 nVertDist = 0;
1240             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_LEFT_MARGIN), nVertOrient == text::VertOrientation::TOP ? 0 : nVertDist));
1241             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_RIGHT_MARGIN), nVertOrient == text::VertOrientation::BOTTOM ? 0 : nVertDist));
1242 
1243             sal_Int32 nHoriDist = rAppendContext.pLastParagraphProperties->GetvSpace();
1244             if( nHoriDist < 0 )
1245                 nHoriDist = 0;
1246             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_TOP_MARGIN), nHoriOrient == text::HoriOrientation::LEFT ? 0 : nHoriDist));
1247             aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_BOTTOM_MARGIN), nHoriOrient == text::HoriOrientation::RIGHT ? 0 : nHoriDist));
1248 
1249             if( rAppendContext.pLastParagraphProperties->Geth() > 0 )
1250                 aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HEIGHT), rAppendContext.pLastParagraphProperties->Geth()));
1251 
1252             if( rAppendContext.pLastParagraphProperties->IsxValid() )
1253                 aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT_POSITION), rAppendContext.pLastParagraphProperties->Getx()));
1254 
1255             if( rAppendContext.pLastParagraphProperties->GethAnchor() >= 0 )
1256                 aFrameProperties.push_back(comphelper::makePropertyValue("HoriOrientRelation", sal_Int16(rAppendContext.pLastParagraphProperties->GethAnchor())));
1257 
1258             if( rAppendContext.pLastParagraphProperties->IsyValid() )
1259                 aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_POSITION), rAppendContext.pLastParagraphProperties->Gety()));
1260 
1261             if( rAppendContext.pLastParagraphProperties->GetvAnchor() >= 0 )
1262                 aFrameProperties.push_back(comphelper::makePropertyValue("VertOrientRelation", sal_Int16(rAppendContext.pLastParagraphProperties->GetvAnchor())));
1263 
1264             if( rAppendContext.pLastParagraphProperties->GetWrap() >= text::WrapTextMode_NONE )
1265                 aFrameProperties.push_back(comphelper::makePropertyValue("Surround", rAppendContext.pLastParagraphProperties->GetWrap()));
1266 
1267             lcl_MoveBorderPropertiesToFrame(aFrameProperties,
1268                 rAppendContext.pLastParagraphProperties->GetStartingRange(),
1269                 rAppendContext.pLastParagraphProperties->GetEndingRange());
1270         }
1271 
1272         //frame conversion has to be executed after table conversion
1273         RegisterFrameConversion(
1274             rAppendContext.pLastParagraphProperties->GetStartingRange(),
1275             rAppendContext.pLastParagraphProperties->GetEndingRange(),
1276             aFrameProperties );
1277     }
1278     catch( const uno::Exception& )
1279     {
1280     }
1281 }
1282 
1283 /// Check if the style or its parent has a list id, recursively.
lcl_getListId(const StyleSheetEntryPtr & rEntry,const StyleSheetTablePtr & rStyleTable,bool & rNumberingFromBaseStyle)1284 static sal_Int32 lcl_getListId(const StyleSheetEntryPtr& rEntry, const StyleSheetTablePtr& rStyleTable, bool & rNumberingFromBaseStyle)
1285 {
1286     const StyleSheetPropertyMap* pEntryProperties = dynamic_cast<const StyleSheetPropertyMap*>(rEntry->pProperties.get());
1287     if (!pEntryProperties)
1288         return -1;
1289 
1290     sal_Int32 nListId = pEntryProperties->GetListId();
1291     // The style itself has a list id.
1292     if (nListId >= 0)
1293         return nListId;
1294 
1295     // The style has no parent.
1296     if (rEntry->sBaseStyleIdentifier.isEmpty())
1297         return -1;
1298 
1299     const StyleSheetEntryPtr pParent = rStyleTable->FindStyleSheetByISTD(rEntry->sBaseStyleIdentifier);
1300     // No such parent style or loop in the style hierarchy.
1301     if (!pParent || pParent == rEntry)
1302         return -1;
1303 
1304     rNumberingFromBaseStyle = true;
1305 
1306     return lcl_getListId(pParent, rStyleTable, rNumberingFromBaseStyle);
1307 }
1308 
finishParagraph(const PropertyMapPtr & pPropertyMap,const bool bRemove)1309 void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, const bool bRemove )
1310 {
1311     if (m_bDiscardHeaderFooter)
1312         return;
1313 
1314     if (!m_aFieldStack.empty())
1315     {
1316         FieldContextPtr pFieldContext = m_aFieldStack.back();
1317         if (pFieldContext && !pFieldContext->IsCommandCompleted())
1318         {
1319             std::vector<OUString> aCommandParts = pFieldContext->GetCommandParts();
1320             if (!aCommandParts.empty() && aCommandParts[0] == "IF")
1321             {
1322                 // Conditional text field conditions don't support linebreaks in Writer.
1323                 return;
1324             }
1325         }
1326 
1327         if (pFieldContext && pFieldContext->IsCommandCompleted())
1328         {
1329             if (pFieldContext->GetFieldId() == FIELD_IF)
1330             {
1331                 // Conditional text fields can't contain newlines, finish the paragraph later.
1332                 FieldParagraph aFinish{pPropertyMap, bRemove};
1333                 pFieldContext->GetParagraphsToFinish().push_back(aFinish);
1334                 return;
1335             }
1336         }
1337     }
1338 
1339 #ifdef DBG_UTIL
1340     TagLogger::getInstance().startElement("finishParagraph");
1341 #endif
1342 
1343     m_nLastTableCellParagraphDepth = m_nTableCellDepth;
1344     ParagraphPropertyMap* pParaContext = dynamic_cast< ParagraphPropertyMap* >( pPropertyMap.get() );
1345     if (m_aTextAppendStack.empty())
1346         return;
1347     TextAppendContext& rAppendContext = m_aTextAppendStack.top();
1348     uno::Reference< text::XTextAppend > xTextAppend(rAppendContext.xTextAppend);
1349 #ifdef DBG_UTIL
1350     TagLogger::getInstance().attribute("isTextAppend", sal_uInt32(xTextAppend.is()));
1351 #endif
1352 
1353     const StyleSheetEntryPtr pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( GetCurrentParaStyleName() );
1354     OSL_ENSURE( pEntry.get(), "no style sheet found" );
1355     const StyleSheetPropertyMap* pStyleSheetProperties = dynamic_cast<const StyleSheetPropertyMap*>(pEntry ? pEntry->pProperties.get() : nullptr);
1356     bool isNumberingViaStyle(false);
1357     //apply numbering to paragraph if it was set at the style, but only if the paragraph itself
1358     //does not specify the numbering
1359     if ( !bRemove && pStyleSheetProperties && pParaContext && !pParaContext->isSet(PROP_NUMBERING_RULES) )
1360     {
1361         bool bNumberingFromBaseStyle = false;
1362         sal_Int32 nListId = pEntry ? lcl_getListId(pEntry, GetStyleSheetTable(), bNumberingFromBaseStyle) : -1;
1363         auto const pList(GetListTable()->GetList(nListId));
1364         if (pList && nListId >= 0 && !pParaContext->isSet(PROP_NUMBERING_STYLE_NAME))
1365         {
1366             pParaContext->Insert( PROP_NUMBERING_STYLE_NAME, uno::makeAny( pList->GetStyleName(nListId) ), false);
1367             isNumberingViaStyle = true;
1368 
1369             // Indent properties from the paragraph style have priority
1370             // over the ones from the numbering styles in Word
1371             // but in Writer numbering styles have priority,
1372             // so insert directly into the paragraph properties to compensate.
1373             boost::optional<PropertyMap::Property> oProperty;
1374             const StyleSheetEntryPtr pParent = (!pEntry->sBaseStyleIdentifier.isEmpty()) ? GetStyleSheetTable()->FindStyleSheetByISTD(pEntry->sBaseStyleIdentifier) : nullptr;
1375             const StyleSheetPropertyMap* pParentProperties = dynamic_cast<const StyleSheetPropertyMap*>(pParent ? pParent->pProperties.get() : nullptr);
1376             if (!pEntry->sBaseStyleIdentifier.isEmpty())
1377                 if ( (oProperty = pStyleSheetProperties->getProperty(PROP_PARA_FIRST_LINE_INDENT))
1378                     // If the numbering comes from a base style, indent of the base style has also priority.
1379                     || (bNumberingFromBaseStyle && pParentProperties && (oProperty = pParentProperties->getProperty(PROP_PARA_FIRST_LINE_INDENT))) )
1380                     pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, oProperty->second, /*bOverwrite=*/false);
1381             if ( (oProperty = pStyleSheetProperties->getProperty(PROP_PARA_LEFT_MARGIN))
1382                 || (bNumberingFromBaseStyle && pParentProperties && (oProperty = pParentProperties->getProperty(PROP_PARA_LEFT_MARGIN))) )
1383                 pParaContext->Insert(PROP_PARA_LEFT_MARGIN, oProperty->second, /*bOverwrite=*/false);
1384 
1385             // We're inheriting properties from a numbering style. Make sure a possible right margin is inherited from the base style.
1386             sal_Int32 nParaRightMargin;
1387             if  ( pParentProperties && (oProperty = pParentProperties->getProperty(PROP_PARA_RIGHT_MARGIN)) && (nParaRightMargin = oProperty->second.get<sal_Int32>()) != 0 )
1388             {
1389                 // If we're setting the right margin, we should set the first / left margin as well from the numbering style.
1390                 const sal_Int32 nFirstLineIndent = getNumberingProperty(nListId, pStyleSheetProperties->GetListLevel(), "FirstLineIndent");
1391                 const sal_Int32 nParaLeftMargin  = getNumberingProperty(nListId, pStyleSheetProperties->GetListLevel(), "IndentAt");
1392                 if (nFirstLineIndent != 0)
1393                     pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, uno::makeAny(nFirstLineIndent), /*bOverwrite=*/false);
1394                 if (nParaLeftMargin != 0)
1395                     pParaContext->Insert(PROP_PARA_LEFT_MARGIN, uno::makeAny(nParaLeftMargin), /*bOverwrite=*/false);
1396 
1397                 pParaContext->Insert(PROP_PARA_RIGHT_MARGIN, uno::makeAny(nParaRightMargin), /*bOverwrite=*/false);
1398             }
1399         }
1400 
1401         if ( pStyleSheetProperties->GetListLevel() >= 0 )
1402             pParaContext->Insert( PROP_NUMBERING_LEVEL, uno::makeAny(pStyleSheetProperties->GetListLevel()), false);
1403     }
1404 
1405     // apply AutoSpacing: it has priority over all other margin settings
1406     // (note that numbering with autoSpacing is handled separately later on)
1407     const bool bAllowAdjustments = !GetSettingsTable()->GetDoNotUseHTMLParagraphAutoSpacing();
1408     sal_Int32 nBeforeAutospacing = -1;
1409     bool bIsAutoSet = pParaContext && pParaContext->isSet(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING);
1410     // apply INHERITED autospacing only if top margin is not set
1411     if ( bIsAutoSet || (pParaContext && !pParaContext->isSet(PROP_PARA_TOP_MARGIN)) )
1412         GetAnyProperty(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING, pPropertyMap) >>= nBeforeAutospacing;
1413     if ( nBeforeAutospacing > -1 && pParaContext )
1414     {
1415         if ( bAllowAdjustments )
1416         {
1417             if ( GetIsFirstParagraphInShape() ||
1418                  (GetIsFirstParagraphInSection() && GetSectionContext() && GetSectionContext()->IsFirstSection()) ||
1419                  (m_bFirstParagraphInCell && m_nTableDepth > 0 && m_nTableDepth == m_nTableCellDepth) )
1420             {
1421                 nBeforeAutospacing = 0;
1422                 // export requires grabbag to match top_margin, so keep them in sync
1423                 if ( bIsAutoSet )
1424                     pParaContext->Insert( PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING, uno::makeAny( sal_Int32(0) ),true, PARA_GRAB_BAG );
1425             }
1426         }
1427         pParaContext->Insert(PROP_PARA_TOP_MARGIN, uno::makeAny(nBeforeAutospacing));
1428     }
1429 
1430     sal_Int32 nAfterAutospacing = -1;
1431     bIsAutoSet = pParaContext && pParaContext->isSet(PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING);
1432     bool bApplyAutospacing = bIsAutoSet || (pParaContext && !pParaContext->isSet(PROP_PARA_BOTTOM_MARGIN));
1433     if ( bApplyAutospacing )
1434         GetAnyProperty(PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING, pPropertyMap) >>= nAfterAutospacing;
1435     if ( nAfterAutospacing > -1 && pParaContext )
1436     {
1437         pParaContext->Insert(PROP_PARA_BOTTOM_MARGIN, uno::makeAny(nAfterAutospacing));
1438         bApplyAutospacing = bAllowAdjustments;
1439     }
1440     else
1441         bApplyAutospacing = false;
1442 
1443     // tell TableManager to reset the bottom margin if it determines that this is the cell's last paragraph.
1444     if ( hasTableManager() && getTableManager().isInCell() )
1445         getTableManager().setCellLastParaAfterAutospacing( bApplyAutospacing );
1446 
1447 
1448     if (xTextAppend.is() && pParaContext && hasTableManager() && !getTableManager().isIgnore())
1449     {
1450         try
1451         {
1452             /*the following combinations of previous and current frame settings can occur:
1453                 (1) - no old frame and no current frame     -> no special action
1454                 (2) - no old frame and current DropCap      -> save DropCap for later use, don't call finishParagraph
1455                                                             remove character properties of the DropCap?
1456                 (3) - no old frame and current Frame        -> save Frame for later use
1457                 (4) - old DropCap and no current frame      -> add DropCap to the properties of the finished paragraph, delete previous setting
1458                 (5) - old DropCap and current frame         -> add DropCap to the properties of the finished paragraph, save current frame settings
1459                 (6) - old Frame and new DropCap             -> add old Frame, save DropCap for later use
1460                 (7) - old Frame and new same Frame          -> continue
1461                 (8) - old Frame and new different Frame     -> add old Frame, save new Frame for later use
1462                 (9) - old Frame and no current frame        -> add old Frame, delete previous settings
1463 
1464               old _and_ new DropCap must not occur
1465              */
1466 
1467             bool bIsDropCap =
1468                 pParaContext->IsFrameMode() &&
1469                 sal::static_int_cast<Id>(pParaContext->GetDropCap()) != NS_ooxml::LN_Value_doc_ST_DropCap_none;
1470 
1471             style::DropCapFormat aDrop;
1472             ParagraphPropertiesPtr pToBeSavedProperties;
1473             bool bKeepLastParagraphProperties = false;
1474             if( bIsDropCap )
1475             {
1476                 uno::Reference<text::XParagraphCursor> xParaCursor(
1477                     xTextAppend->createTextCursorByRange(xTextAppend->getEnd()), uno::UNO_QUERY_THROW);
1478                 //select paragraph
1479                 xParaCursor->gotoStartOfParagraph( true );
1480                 uno::Reference< beans::XPropertyState > xParaProperties( xParaCursor, uno::UNO_QUERY_THROW );
1481                 xParaProperties->setPropertyToDefault(getPropertyName(PROP_CHAR_ESCAPEMENT));
1482                 xParaProperties->setPropertyToDefault(getPropertyName(PROP_CHAR_HEIGHT));
1483                 //handles (2) and part of (6)
1484                 pToBeSavedProperties = new ParagraphProperties(*pParaContext);
1485                 sal_Int32 nCount = xParaCursor->getString().getLength();
1486                 pToBeSavedProperties->SetDropCapLength(nCount > 0 && nCount < 255 ? static_cast<sal_Int8>(nCount) : 1);
1487             }
1488             if( rAppendContext.pLastParagraphProperties.get() )
1489             {
1490                 if( sal::static_int_cast<Id>(rAppendContext.pLastParagraphProperties->GetDropCap()) != NS_ooxml::LN_Value_doc_ST_DropCap_none)
1491                 {
1492                     //handles (4) and part of (5)
1493                     //create a DropCap property, add it to the property sequence of finishParagraph
1494                     sal_Int32 nLines = rAppendContext.pLastParagraphProperties->GetLines();
1495                     aDrop.Lines = nLines > 0 && nLines < SAL_MAX_INT8 ? static_cast<sal_Int8>(nLines) : 2;
1496                     aDrop.Count = rAppendContext.pLastParagraphProperties->GetDropCapLength();
1497                     sal_Int32 nHSpace = rAppendContext.pLastParagraphProperties->GethSpace();
1498                     aDrop.Distance  = nHSpace > 0 && nHSpace < SAL_MAX_INT16 ? static_cast<sal_Int16>(nHSpace) : 0;
1499                     //completes (5)
1500                     if( pParaContext->IsFrameMode() )
1501                         pToBeSavedProperties = new ParagraphProperties(*pParaContext);
1502                 }
1503                 else if(*rAppendContext.pLastParagraphProperties == *pParaContext )
1504                 {
1505                     //handles (7)
1506                     rAppendContext.pLastParagraphProperties->SetEndingRange(rAppendContext.xInsertPosition.is() ? rAppendContext.xInsertPosition : xTextAppend->getEnd());
1507                     bKeepLastParagraphProperties = true;
1508                 }
1509                 else
1510                 {
1511                     //handles (8)(9) and completes (6)
1512                     CheckUnregisteredFrameConversion( );
1513 
1514                     // If different frame properties are set on this paragraph, keep them.
1515                     if ( !bIsDropCap && pParaContext->IsFrameMode() )
1516                     {
1517                         pToBeSavedProperties = new ParagraphProperties(*pParaContext);
1518                         lcl_AddRangeAndStyle(pToBeSavedProperties, xTextAppend, pPropertyMap, rAppendContext);
1519                     }
1520                 }
1521             }
1522             else
1523             {
1524                 // (1) doesn't need handling
1525 
1526                 if( !bIsDropCap && pParaContext->IsFrameMode() )
1527                 {
1528                     pToBeSavedProperties = new ParagraphProperties(*pParaContext);
1529                     lcl_AddRangeAndStyle(pToBeSavedProperties, xTextAppend, pPropertyMap, rAppendContext);
1530                 }
1531             }
1532             std::vector<beans::PropertyValue> aProperties;
1533             if (pPropertyMap.get())
1534             {
1535                 aProperties = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(pPropertyMap->GetPropertyValues());
1536             }
1537             // 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
1538             if (pPropertyMap.get() && IsOOXMLImport())
1539             {
1540                 // tdf#64222 filter out the "paragraph marker" formatting and
1541                 // set it as a separate paragraph property, not a empty hint at
1542                 // end of paragraph
1543                 std::vector<beans::NamedValue> charProperties;
1544                 for (auto it = aProperties.begin(); it != aProperties.end(); )
1545                 {
1546                     // this condition isn't ideal but as it happens all
1547                     // RES_CHRATR_* have names that start with "Char"
1548                     if (it->Name.startsWith("Char"))
1549                     {
1550                         charProperties.emplace_back(it->Name, it->Value);
1551                         // as testN793262 demonstrates, font size in rPr must
1552                         // affect the paragraph size => also insert empty hint!
1553 //                        it = aProperties.erase(it);
1554                     }
1555                     ++it;
1556                 }
1557                 if (!charProperties.empty())
1558                 {
1559                     aProperties.push_back(beans::PropertyValue("ListAutoFormat",
1560                         0, uno::makeAny(comphelper::containerToSequence(charProperties)), beans::PropertyState_DIRECT_VALUE));
1561                 }
1562             }
1563             if( !bIsDropCap )
1564             {
1565                 if( aDrop.Lines > 1 )
1566                 {
1567                     beans::PropertyValue aValue;
1568                     aValue.Name = getPropertyName(PROP_DROP_CAP_FORMAT);
1569                     aValue.Value <<= aDrop;
1570                     aProperties.push_back(aValue);
1571                 }
1572                 uno::Reference< text::XTextRange > xTextRange;
1573                 if (rAppendContext.xInsertPosition.is())
1574                 {
1575                     xTextRange = xTextAppend->finishParagraphInsert( comphelper::containerToSequence(aProperties), rAppendContext.xInsertPosition );
1576                     rAppendContext.xCursor->gotoNextParagraph(false);
1577                     if (rAppendContext.pLastParagraphProperties.get())
1578                         rAppendContext.pLastParagraphProperties->SetEndingRange(xTextRange->getEnd());
1579                 }
1580                 else
1581                 {
1582                     uno::Reference<text::XTextCursor> xCursor;
1583                     if (m_bParaHadField && !m_bIsInComments && !xTOCMarkerCursor.is())
1584                     {
1585                         // Workaround to make sure char props of the field are not lost.
1586                         // Not relevant for editeng-based comments.
1587                         // Not relevant for fields inside a TOC field.
1588                         OUString const sMarker("X");
1589                         xCursor = xTextAppend->getText()->createTextCursor();
1590                         if (xCursor.is())
1591                             xCursor->gotoEnd(false);
1592                         PropertyMapPtr pEmpty(new PropertyMap());
1593                         appendTextPortion(sMarker, pEmpty);
1594                     }
1595 
1596                     // Check if top / bottom margin has to be updated, now that we know the numbering status of both the previous and
1597                     // the current text node.
1598                     auto itNumberingRules = std::find_if(aProperties.begin(), aProperties.end(), [](const beans::PropertyValue& rValue)
1599                     {
1600                         return rValue.Name == "NumberingRules";
1601                     });
1602                     if (itNumberingRules != aProperties.end())
1603                     {
1604                         // This textnode has numbering. Look up the numbering style name of the current and previous paragraph.
1605                         OUString aCurrentNumberingRuleName;
1606                         uno::Reference<container::XNamed> xCurrentNumberingRules(itNumberingRules->Value, uno::UNO_QUERY);
1607                         if (xCurrentNumberingRules.is())
1608                             aCurrentNumberingRuleName = xCurrentNumberingRules->getName();
1609                         OUString aPreviousNumberingRuleName;
1610                         if (m_xPreviousParagraph.is())
1611                         {
1612                             uno::Reference<container::XNamed> xPreviousNumberingRules(m_xPreviousParagraph->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
1613                             if (xPreviousNumberingRules.is())
1614                                 aPreviousNumberingRuleName = xPreviousNumberingRules->getName();
1615                         }
1616 
1617                         if (!aPreviousNumberingRuleName.isEmpty() && aCurrentNumberingRuleName == aPreviousNumberingRuleName)
1618                         {
1619                             // There was a previous textnode and it had the same numbering.
1620                             if (m_bParaAutoBefore)
1621                             {
1622                                 // This before spacing is set to auto, set before space to 0.
1623                                 auto itParaTopMargin = std::find_if(aProperties.begin(), aProperties.end(), [](const beans::PropertyValue& rValue)
1624                                 {
1625                                     return rValue.Name == "ParaTopMargin";
1626                                 });
1627                                 if (itParaTopMargin != aProperties.end())
1628                                     itParaTopMargin->Value <<= static_cast<sal_Int32>(0);
1629                                 else
1630                                     aProperties.push_back(comphelper::makePropertyValue("ParaTopMargin", static_cast<sal_Int32>(0)));
1631                             }
1632                             uno::Sequence<beans::PropertyValue> aPrevPropertiesSeq;
1633                             m_xPreviousParagraph->getPropertyValue("ParaInteropGrabBag") >>= aPrevPropertiesSeq;
1634                             auto aPrevProperties = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aPrevPropertiesSeq);
1635                             bool bPrevParaAutoAfter = std::any_of(aPrevProperties.begin(), aPrevProperties.end(), [](const beans::PropertyValue& rValue)
1636                             {
1637                                 return rValue.Name == "ParaBottomMarginAfterAutoSpacing";
1638                             });
1639                             if (bPrevParaAutoAfter)
1640                             {
1641                                 // Previous after spacing is set to auto, set previous after space to 0.
1642                                 m_xPreviousParagraph->setPropertyValue("ParaBottomMargin", uno::makeAny(static_cast<sal_Int32>(0)));
1643                             }
1644                         }
1645                     }
1646 
1647                     xTextRange = xTextAppend->finishParagraph( comphelper::containerToSequence(aProperties) );
1648                     m_xPreviousParagraph.set(xTextRange, uno::UNO_QUERY);
1649 
1650                     if (m_xPreviousParagraph.is() && // null for SvxUnoTextBase
1651                         (isNumberingViaStyle || itNumberingRules != aProperties.end()))
1652                     {
1653                         assert(dynamic_cast<ParagraphPropertyMap*>(pPropertyMap.get()));
1654                         // Use lcl_getListId(), so we find the list ID in parent styles as well.
1655                         bool bNumberingFromBaseStyle = false;
1656                         sal_Int32 const nListId( isNumberingViaStyle
1657                             ? lcl_getListId(pEntry, GetStyleSheetTable(), bNumberingFromBaseStyle)
1658                             : static_cast<ParagraphPropertyMap*>(pPropertyMap.get())->GetListId());
1659                         if (ListDef::Pointer const& pList = m_pListTable->GetList(nListId))
1660                         {   // styles could refer to non-existing lists...
1661                             AbstractListDef::Pointer const& pAbsList =
1662                                     pList->GetAbstractDefinition();
1663                             if (pAbsList &&
1664                                 // SvxUnoTextRange doesn't have ListId
1665                                 m_xPreviousParagraph->getPropertySetInfo()->hasPropertyByName("ListId"))
1666                             {
1667                                 OUString paraId;
1668                                 m_xPreviousParagraph->getPropertyValue("ListId") >>= paraId;
1669                                 assert(!paraId.isEmpty()); // must be on some list?
1670                                 OUString const listId = pAbsList->MapListId(paraId);
1671                                 if (listId != paraId)
1672                                 {
1673                                     m_xPreviousParagraph->setPropertyValue("ListId", uno::makeAny(listId));
1674                                 }
1675                             }
1676                             if (pList->GetCurrentLevel())
1677                             {
1678                                 sal_Int16 nOverrideLevel = pList->GetCurrentLevel()->GetStartOverride();
1679                                 if (nOverrideLevel != -1 && m_aListOverrideApplied.find(nListId) == m_aListOverrideApplied.end())
1680                                 {
1681                                     // Apply override: we have override instruction for this level
1682                                     // And this was not done for this list before: we can do this only once on first occurrence
1683                                     // of list with override
1684                                     // TODO: Not tested variant with differen levels override in diffent lists.
1685                                     // Probably m_aListOverrideApplied as a set of overriden listids is not sufficient
1686                                     // and we need to register level overrides separately.
1687                                     m_xPreviousParagraph->setPropertyValue("ParaIsNumberingRestart", uno::makeAny(true));
1688                                     m_xPreviousParagraph->setPropertyValue("NumberingStartValue", uno::makeAny(nOverrideLevel));
1689                                     m_aListOverrideApplied.insert(nListId);
1690                                 }
1691                             }
1692                         }
1693                     }
1694 
1695                     if (!rAppendContext.m_aAnchoredObjects.empty() && !IsInHeaderFooter())
1696                     {
1697                         // Remember what objects are anchored to this paragraph.
1698                         // That list is only used for Word compat purposes, and
1699                         // it is only relevant for body text.
1700                         AnchoredObjectsInfo aInfo;
1701                         aInfo.m_xParagraph = xTextRange;
1702                         aInfo.m_aAnchoredObjects = rAppendContext.m_aAnchoredObjects;
1703                         m_aAnchoredObjectAnchors.push_back(aInfo);
1704                         rAppendContext.m_aAnchoredObjects.clear();
1705                     }
1706 
1707                     // We're no longer right after a table conversion.
1708                     m_bConvertedTable = false;
1709 
1710                     if (xCursor.is())
1711                     {
1712                         xCursor->goLeft(1, true);
1713                         xCursor->setString(OUString());
1714                     }
1715                 }
1716                 getTableManager( ).handle(xTextRange);
1717                 m_aSmartTagHandler.handle(xTextRange);
1718 
1719                 if (xTextRange.is())
1720                 {
1721                     // Get the end of paragraph character inserted
1722                     uno::Reference< text::XTextCursor > xCur = xTextRange->getText( )->createTextCursor( );
1723                     if (rAppendContext.xInsertPosition.is())
1724                         xCur->gotoRange( rAppendContext.xInsertPosition, false );
1725                     else
1726                         xCur->gotoEnd( false );
1727                     xCur->goLeft( 1 , true );
1728                     // Extend the redline ranges for empty paragraphs
1729                     if ( !m_bParaChanged && m_previousRedline.get() )
1730                         CreateRedline( xCur, m_previousRedline );
1731                     CheckParaMarkerRedline( xCur );
1732                 }
1733 
1734                 css::uno::Reference<css::beans::XPropertySet> xParaProps(xTextRange, uno::UNO_QUERY);
1735 
1736                 // table style has got bigger precedence than docDefault style
1737                 // collect these pending paragraph properties to process in endTable()
1738                 if (xParaProps && m_nTableDepth > 0)
1739                 {
1740                     TableParagraph aPending{pParaContext, xParaProps};
1741                     getTableManager().getCurrentParagraphs()->push_back(aPending);
1742                 }
1743 
1744                 // tdf#118521 set paragraph top or bottom margin based on the paragraph style
1745                 // if we already set the other margin with direct formatting
1746                 if (xParaProps)
1747                 {
1748                     const bool bTopSet = pParaContext->isSet(PROP_PARA_TOP_MARGIN);
1749                     const bool bBottomSet = pParaContext->isSet(PROP_PARA_BOTTOM_MARGIN);
1750                     const bool bContextSet = pParaContext->isSet(PROP_PARA_CONTEXT_MARGIN);
1751                     if ( !(bTopSet == bBottomSet && bBottomSet == bContextSet) )
1752                     {
1753 
1754                         if ( !bTopSet )
1755                         {
1756                             uno::Any aMargin = GetPropertyFromParaStyleSheet(PROP_PARA_TOP_MARGIN);
1757                             if ( aMargin != uno::Any() )
1758                                 xParaProps->setPropertyValue("ParaTopMargin", aMargin);
1759                         }
1760                         if ( !bBottomSet )
1761                         {
1762                             uno::Any aMargin = GetPropertyFromParaStyleSheet(PROP_PARA_BOTTOM_MARGIN);
1763                             if ( aMargin != uno::Any() )
1764                                 xParaProps->setPropertyValue("ParaBottomMargin", aMargin);
1765                         }
1766                         if ( !bContextSet )
1767                         {
1768                             uno::Any aMargin = GetPropertyFromParaStyleSheet(PROP_PARA_CONTEXT_MARGIN);
1769                             if ( aMargin != uno::Any() )
1770                                 xParaProps->setPropertyValue("ParaContextMargin", aMargin);
1771                         }
1772                     }
1773                 }
1774 
1775                 // Left, Right, and Hanging settings are also grouped. Ensure that all or none are set.
1776                 if (xParaProps)
1777                 {
1778                     const bool bLeftSet  = pParaContext->isSet(PROP_PARA_LEFT_MARGIN);
1779                     const bool bRightSet = pParaContext->isSet(PROP_PARA_RIGHT_MARGIN);
1780                     const bool bFirstSet = pParaContext->isSet(PROP_PARA_FIRST_LINE_INDENT);
1781                     if ( !(bLeftSet == bRightSet && bRightSet == bFirstSet) )
1782                     {
1783                         if ( !bLeftSet )
1784                         {
1785                             uno::Any aMargin = GetPropertyFromParaStyleSheet(PROP_PARA_LEFT_MARGIN);
1786                             if ( aMargin != uno::Any() )
1787                                 xParaProps->setPropertyValue("ParaLeftMargin", aMargin);
1788                         }
1789                         if ( !bRightSet )
1790                         {
1791                             uno::Any aMargin = GetPropertyFromParaStyleSheet(PROP_PARA_RIGHT_MARGIN);
1792                             if ( aMargin != uno::Any() )
1793                                 xParaProps->setPropertyValue("ParaRightMargin", aMargin);
1794                         }
1795                         if ( !bFirstSet )
1796                         {
1797                             uno::Any aMargin = GetPropertyFromParaStyleSheet(PROP_PARA_FIRST_LINE_INDENT);
1798                             if ( aMargin != uno::Any() )
1799                                 xParaProps->setPropertyValue("ParaFirstLineIndent", aMargin);
1800                         }
1801                     }
1802                 }
1803             }
1804             if( !bKeepLastParagraphProperties )
1805                 rAppendContext.pLastParagraphProperties = pToBeSavedProperties;
1806         }
1807         catch(const lang::IllegalArgumentException&)
1808         {
1809             OSL_FAIL( "IllegalArgumentException in DomainMapper_Impl::finishParagraph" );
1810         }
1811         catch(const uno::Exception&)
1812         {
1813             TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "finishParagraph()" );
1814         }
1815 
1816     }
1817 
1818     bool bIgnoreFrameState = IsInHeaderFooter();
1819     if( (!bIgnoreFrameState && pParaContext && pParaContext->IsFrameMode()) || (bIgnoreFrameState && GetIsPreviousParagraphFramed()) )
1820         SetIsPreviousParagraphFramed(true);
1821     else
1822         SetIsPreviousParagraphFramed(false);
1823 
1824     m_bParaChanged = false;
1825     m_bRemoveThisParagraph = false;
1826     if( !IsInHeaderFooter() && !IsInShape() && (!pParaContext || !pParaContext->IsFrameMode()) )
1827     { // If the paragraph is in a frame, shape or header/footer, it's not a paragraph of the section itself.
1828         SetIsFirstParagraphInSection(false);
1829         // count first not deleted paragraph as first paragraph in section to avoid of
1830         // its deletion later, resulting loss of the associated page break
1831         if (!m_previousRedline.get())
1832         {
1833             SetIsFirstParagraphInSectionAfterRedline(false);
1834             SetIsLastParagraphInSection(false);
1835         }
1836     }
1837     m_previousRedline.clear();
1838 
1839     if (m_bIsFirstParaInShape)
1840         m_bIsFirstParaInShape = false;
1841 
1842     if (pParaContext)
1843     {
1844         // Reset the frame properties for the next paragraph
1845         pParaContext->ResetFrameProperties();
1846     }
1847 
1848     SetIsOutsideAParagraph(true);
1849     m_bParaHadField = false;
1850 
1851     // don't overwrite m_bFirstParagraphInCell in table separator nodes
1852     if (m_nTableDepth > 0 && m_nTableDepth == m_nTableCellDepth)
1853         m_bFirstParagraphInCell = false;
1854 
1855     m_bParaAutoBefore = false;
1856 
1857 #ifdef DBG_UTIL
1858     TagLogger::getInstance().endElement();
1859 #endif
1860 
1861 }
1862 
appendTextPortion(const OUString & rString,const PropertyMapPtr & pPropertyMap)1863 void DomainMapper_Impl::appendTextPortion( const OUString& rString, const PropertyMapPtr& pPropertyMap )
1864 {
1865     if (m_bDiscardHeaderFooter)
1866         return;
1867 
1868     if (m_aTextAppendStack.empty())
1869         return;
1870     // Before placing call to processDeferredCharacterProperties(), TopContextType should be CONTEXT_CHARACTER
1871     // processDeferredCharacterProperties() invokes only if character inserted
1872     if( pPropertyMap == m_pTopContext && !deferredCharacterProperties.empty() && (GetTopContextType() == CONTEXT_CHARACTER) )
1873         processDeferredCharacterProperties();
1874     uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
1875     if (xTextAppend.is() && hasTableManager() && !getTableManager().isIgnore())
1876     {
1877         try
1878         {
1879             // If we are in comments, then disable CharGrabBag, comment text doesn't support that.
1880             uno::Sequence< beans::PropertyValue > aValues = pPropertyMap->GetPropertyValues(/*bCharGrabBag=*/!m_bIsInComments);
1881 
1882             if (m_bStartTOC || m_bStartIndex || m_bStartBibliography)
1883                 for( auto& rValue : aValues )
1884                 {
1885                     if (rValue.Name == "CharHidden")
1886                         rValue.Value <<= false;
1887                 }
1888 
1889             uno::Reference< text::XTextRange > xTextRange;
1890             if (m_aTextAppendStack.top().xInsertPosition.is())
1891             {
1892                 xTextRange = xTextAppend->insertTextPortion(rString, aValues, m_aTextAppendStack.top().xInsertPosition);
1893                 m_aTextAppendStack.top().xCursor->gotoRange(xTextRange->getEnd(), true);
1894             }
1895             else
1896             {
1897                 if (m_bStartTOC || m_bStartIndex || m_bStartBibliography || m_nStartGenericField != 0)
1898                 {
1899                     if (IsInHeaderFooter() && !m_bStartTOCHeaderFooter)
1900                     {
1901                         xTextRange = xTextAppend->appendTextPortion(rString, aValues);
1902                     }
1903                     else
1904                     {
1905                         m_bStartedTOC = true;
1906                         uno::Reference< text::XTextCursor > xTOCTextCursor = xTextAppend->getEnd()->getText( )->createTextCursor( );
1907                         assert(xTOCTextCursor.is());
1908                         xTOCTextCursor->gotoEnd(false);
1909                         if (m_nStartGenericField != 0)
1910                         {
1911                             xTOCTextCursor->goLeft(1, false);
1912                         }
1913                         xTextRange = xTextAppend->insertTextPortion(rString, aValues, xTOCTextCursor);
1914                         SAL_WARN_IF(!xTextRange.is(), "writerfilter.dmapper", "insertTextPortion failed");
1915                         if (!xTextRange.is())
1916                             throw uno::Exception("insertTextPortion failed", nullptr);
1917                         m_bTextInserted = true;
1918                         xTOCTextCursor->gotoRange(xTextRange->getEnd(), true);
1919                         mxTOCTextCursor = xTOCTextCursor;
1920                         if (m_nStartGenericField == 0)
1921                         {
1922                             m_aTextAppendStack.push(TextAppendContext(xTextAppend, xTOCTextCursor));
1923                         }
1924                     }
1925                 }
1926                 else
1927                 {
1928 #if !defined(MACOSX) // TODO: check layout differences and support all platforms, if needed
1929                     sal_Int32 nPos = 0;
1930                     OUString sFontName;
1931                     OUString sDoubleSpace("  ");
1932                     PropertyMapPtr pContext = GetTopContextOfType(CONTEXT_CHARACTER);
1933                     // tdf#123703 workaround for longer space sequences of the old or compatible RTF documents
1934                     if (GetSettingsTable()->GetLongerSpaceSequence() && !IsOpenFieldCommand() && (nPos = rString.indexOf(sDoubleSpace)) != -1 &&
1935                         // monospaced fonts have no longer space sequences, regardless of \fprq2 (not monospaced) font setting
1936                         // fix for the base monospaced font Courier
1937                         (!pContext || !pContext->isSet(PROP_CHAR_FONT_NAME) ||
1938                             ((pContext->getProperty(PROP_CHAR_FONT_NAME)->second >>= sFontName) && sFontName.indexOf("Courier") == -1)))
1939                     {
1940                         // an RTF space character is longer by an extra six-em-space in an old-style RTF space sequence,
1941                         // insert them to keep RTF document layout formatted by consecutive spaces
1942                         const sal_Unicode aExtraSpace[5] = { 0x2006, 0x20, 0x2006, 0x20, 0 };
1943                         const sal_Unicode aExtraSpace2[4] = { 0x20, 0x2006, 0x20, 0 };
1944                         xTextRange = xTextAppend->appendTextPortion(rString.replaceAll(sDoubleSpace, aExtraSpace, nPos)
1945                                                                            .replaceAll(sDoubleSpace, aExtraSpace2, nPos), aValues);
1946                     }
1947                     else
1948 #endif
1949                         xTextRange = xTextAppend->appendTextPortion(rString, aValues);
1950                 }
1951             }
1952 
1953             // reset moveFrom data of non-terminating runs of the paragraph
1954             if ( m_pParaMarkerRedlineMoveFrom.get( ) )
1955             {
1956                 m_pParaMarkerRedlineMoveFrom.clear();
1957             }
1958             CheckRedline( xTextRange );
1959             m_bParaChanged = true;
1960 
1961             //getTableManager( ).handle(xTextRange);
1962         }
1963         catch(const lang::IllegalArgumentException&)
1964         {
1965             OSL_FAIL( "IllegalArgumentException in DomainMapper_Impl::appendTextPortion" );
1966         }
1967         catch(const uno::Exception&)
1968         {
1969             OSL_FAIL( "Exception in DomainMapper_Impl::appendTextPortion" );
1970         }
1971     }
1972 }
1973 
appendTextContent(const uno::Reference<text::XTextContent> & xContent,const uno::Sequence<beans::PropertyValue> & xPropertyValues)1974 void DomainMapper_Impl::appendTextContent(
1975     const uno::Reference< text::XTextContent >& xContent,
1976     const uno::Sequence< beans::PropertyValue >& xPropertyValues
1977     )
1978 {
1979     SAL_WARN_IF(m_aTextAppendStack.empty(), "writerfilter.dmapper", "no text append stack");
1980     if (m_aTextAppendStack.empty())
1981         return;
1982     uno::Reference< text::XTextAppendAndConvert >  xTextAppendAndConvert( m_aTextAppendStack.top().xTextAppend, uno::UNO_QUERY );
1983     OSL_ENSURE( xTextAppendAndConvert.is(), "trying to append a text content without XTextAppendAndConvert" );
1984     if (xTextAppendAndConvert.is() && hasTableManager() && !getTableManager().isIgnore())
1985     {
1986         try
1987         {
1988             if (m_aTextAppendStack.top().xInsertPosition.is())
1989                 xTextAppendAndConvert->insertTextContentWithProperties( xContent, xPropertyValues, m_aTextAppendStack.top().xInsertPosition );
1990             else
1991                 xTextAppendAndConvert->appendTextContent( xContent, xPropertyValues );
1992         }
1993         catch(const lang::IllegalArgumentException&)
1994         {
1995         }
1996         catch(const uno::Exception&)
1997         {
1998         }
1999     }
2000 }
2001 
appendOLE(const OUString & rStreamName,const std::shared_ptr<OLEHandler> & pOLEHandler)2002 void DomainMapper_Impl::appendOLE( const OUString& rStreamName, const std::shared_ptr<OLEHandler>& pOLEHandler )
2003 {
2004     try
2005     {
2006         uno::Reference< text::XTextContent > xOLE( m_xTextFactory->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW );
2007         uno::Reference< beans::XPropertySet > xOLEProperties(xOLE, uno::UNO_QUERY_THROW);
2008 
2009         OUString aCLSID = pOLEHandler->getCLSID(m_xComponentContext);
2010         if (aCLSID.isEmpty())
2011             xOLEProperties->setPropertyValue(getPropertyName( PROP_STREAM_NAME ),
2012                             uno::makeAny( rStreamName ));
2013         else
2014             xOLEProperties->setPropertyValue("CLSID", uno::makeAny(aCLSID));
2015 
2016         OUString aDrawAspect = pOLEHandler->GetDrawAspect();
2017         if(!aDrawAspect.isEmpty())
2018             xOLEProperties->setPropertyValue("DrawAspect", uno::makeAny(aDrawAspect));
2019 
2020         awt::Size aSize = pOLEHandler->getSize();
2021         if( !aSize.Width )
2022             aSize.Width = 1000;
2023         if( !aSize.Height )
2024             aSize.Height = 1000;
2025         xOLEProperties->setPropertyValue(getPropertyName( PROP_WIDTH ),
2026                         uno::makeAny(aSize.Width));
2027         xOLEProperties->setPropertyValue(getPropertyName( PROP_HEIGHT ),
2028                         uno::makeAny(aSize.Height));
2029 
2030         OUString aVisAreaWidth = pOLEHandler->GetVisAreaWidth();
2031         if(!aVisAreaWidth.isEmpty())
2032             xOLEProperties->setPropertyValue("VisibleAreaWidth", uno::makeAny(aVisAreaWidth));
2033 
2034         OUString aVisAreaHeight = pOLEHandler->GetVisAreaHeight();
2035         if(!aVisAreaHeight.isEmpty())
2036             xOLEProperties->setPropertyValue("VisibleAreaHeight", uno::makeAny(aVisAreaHeight));
2037 
2038         uno::Reference< graphic::XGraphic > xGraphic = pOLEHandler->getReplacement();
2039         xOLEProperties->setPropertyValue(getPropertyName( PROP_GRAPHIC ),
2040                         uno::makeAny(xGraphic));
2041         uno::Reference<beans::XPropertySet> xReplacementProperties(pOLEHandler->getShape(), uno::UNO_QUERY);
2042         if (xReplacementProperties.is())
2043         {
2044             OUString pProperties[] = {
2045                 OUString("AnchorType"),
2046                 OUString("Surround"),
2047                 OUString("HoriOrient"),
2048                 OUString("HoriOrientPosition"),
2049                 OUString("VertOrient"),
2050                 OUString("VertOrientPosition")
2051             };
2052             for (const OUString & s : pProperties)
2053                 xOLEProperties->setPropertyValue(s, xReplacementProperties->getPropertyValue(s));
2054         }
2055         else
2056             // mimic the treatment of graphics here... it seems anchoring as character
2057             // gives a better ( visually ) result
2058             xOLEProperties->setPropertyValue(getPropertyName( PROP_ANCHOR_TYPE ),  uno::makeAny( text::TextContentAnchorType_AS_CHARACTER ) );
2059         // remove ( if valid ) associated shape ( used for graphic replacement )
2060         SAL_WARN_IF(m_aAnchoredStack.empty(), "writerfilter.dmapper", "no anchor stack");
2061         if (!m_aAnchoredStack.empty())
2062             m_aAnchoredStack.top( ).bToRemove = true;
2063         RemoveLastParagraph();
2064         SAL_WARN_IF(m_aTextAppendStack.empty(), "writerfilter.dmapper", "no text stack");
2065         if (!m_aTextAppendStack.empty())
2066             m_aTextAppendStack.pop();
2067 
2068         appendTextContent( xOLE, uno::Sequence< beans::PropertyValue >() );
2069 
2070         if (!aCLSID.isEmpty())
2071             pOLEHandler->importStream(m_xComponentContext, GetTextDocument(), xOLE);
2072 
2073     }
2074     catch( const uno::Exception& )
2075     {
2076         OSL_FAIL( "Exception in creation of OLE object" );
2077     }
2078 
2079 }
2080 
appendStarMath(const Value & val)2081 void DomainMapper_Impl::appendStarMath( const Value& val )
2082 {
2083     uno::Reference< embed::XEmbeddedObject > formula;
2084     val.getAny() >>= formula;
2085     if( formula.is() )
2086     {
2087         try
2088         {
2089             uno::Reference< text::XTextContent > xStarMath( m_xTextFactory->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW );
2090             uno::Reference< beans::XPropertySet > xStarMathProperties(xStarMath, uno::UNO_QUERY_THROW);
2091 
2092             xStarMathProperties->setPropertyValue(getPropertyName( PROP_EMBEDDED_OBJECT ),
2093                 val.getAny());
2094             // tdf#66405: set zero margins for embedded object
2095             xStarMathProperties->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN ),
2096                 uno::makeAny(sal_Int32(0)));
2097             xStarMathProperties->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN ),
2098                 uno::makeAny(sal_Int32(0)));
2099             xStarMathProperties->setPropertyValue(getPropertyName( PROP_TOP_MARGIN ),
2100                 uno::makeAny(sal_Int32(0)));
2101             xStarMathProperties->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN ),
2102                 uno::makeAny(sal_Int32(0)));
2103 
2104             uno::Reference< uno::XInterface > xInterface( formula->getComponent(), uno::UNO_QUERY );
2105             // set zero margins for object's component
2106             uno::Reference< beans::XPropertySet > xComponentProperties( xInterface, uno::UNO_QUERY_THROW );
2107             xComponentProperties->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN ),
2108                 uno::makeAny(sal_Int32(0)));
2109             xComponentProperties->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN ),
2110                 uno::makeAny(sal_Int32(0)));
2111             xComponentProperties->setPropertyValue(getPropertyName( PROP_TOP_MARGIN ),
2112                 uno::makeAny(sal_Int32(0)));
2113             xComponentProperties->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN ),
2114                 uno::makeAny(sal_Int32(0)));
2115             Size size( 1000, 1000 );
2116             if( oox::FormulaImportBase* formulaimport = dynamic_cast< oox::FormulaImportBase* >( xInterface.get()))
2117                 size = formulaimport->getFormulaSize();
2118             xStarMathProperties->setPropertyValue(getPropertyName( PROP_WIDTH ),
2119                 uno::makeAny( sal_Int32(size.Width())));
2120             xStarMathProperties->setPropertyValue(getPropertyName( PROP_HEIGHT ),
2121                 uno::makeAny( sal_Int32(size.Height())));
2122             // mimic the treatment of graphics here... it seems anchoring as character
2123             // gives a better ( visually ) result
2124             xStarMathProperties->setPropertyValue(getPropertyName( PROP_ANCHOR_TYPE ),
2125                 uno::makeAny( text::TextContentAnchorType_AS_CHARACTER ) );
2126             appendTextContent( xStarMath, uno::Sequence< beans::PropertyValue >() );
2127         }
2128         catch( const uno::Exception& )
2129         {
2130             OSL_FAIL( "Exception in creation of StarMath object" );
2131         }
2132     }
2133 }
2134 
appendTextSectionAfter(uno::Reference<text::XTextRange> const & xBefore)2135 uno::Reference< beans::XPropertySet > DomainMapper_Impl::appendTextSectionAfter(
2136                                     uno::Reference< text::XTextRange > const & xBefore )
2137 {
2138     uno::Reference< beans::XPropertySet > xRet;
2139     if (m_aTextAppendStack.empty())
2140         return xRet;
2141     uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
2142     if(xTextAppend.is())
2143     {
2144         try
2145         {
2146             uno::Reference< text::XParagraphCursor > xCursor(
2147                 xTextAppend->createTextCursorByRange( xBefore ), uno::UNO_QUERY_THROW);
2148             //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
2149             xCursor->gotoStartOfParagraph( false );
2150             if (m_aTextAppendStack.top().xInsertPosition.is())
2151                 xCursor->gotoRange( m_aTextAppendStack.top().xInsertPosition, true );
2152             else
2153                 xCursor->gotoEnd( true );
2154             //the paragraph after this new section is already inserted
2155             xCursor->goLeft(1, true);
2156             css::uno::Reference<css::text::XTextRange> xTextRange(xCursor, css::uno::UNO_QUERY_THROW);
2157 
2158             if (css::uno::Reference<css::text::XDocumentIndexesSupplier> xIndexSupplier{
2159                     GetTextDocument(), css::uno::UNO_QUERY })
2160             {
2161                 css::uno::Reference<css::text::XTextRangeCompare> xCompare(
2162                     xTextAppend, css::uno::UNO_QUERY);
2163                 const auto xIndexAccess = xIndexSupplier->getDocumentIndexes();
2164                 for (sal_Int32 i = xIndexAccess->getCount(); i > 0; --i)
2165                 {
2166                     if (css::uno::Reference<css::text::XDocumentIndex> xIndex{
2167                             xIndexAccess->getByIndex(i - 1), css::uno::UNO_QUERY })
2168                     {
2169                         const auto xIndexTextRange = xIndex->getAnchor();
2170                         if (xCompare->compareRegionStarts(xTextRange, xIndexTextRange) == 0
2171                             && xCompare->compareRegionEnds(xTextRange, xIndexTextRange) == 0)
2172                         {
2173                             // The boundaries coincide with an index: trying to attach a section
2174                             // to the range will insert the section inside the index. goRight will
2175                             // extend the range outside of the index, so that created section will
2176                             // be around it. Alternatively we could return index section itself
2177                             // instead : xRet.set(xIndex, uno::UNO_QUERY) - to set its properties,
2178                             // like columns/fill.
2179                             xCursor->goRight(1, true);
2180                             break;
2181                         }
2182                     }
2183                 }
2184             }
2185 
2186             uno::Reference< text::XTextContent > xSection( m_xTextFactory->createInstance("com.sun.star.text.TextSection"), uno::UNO_QUERY_THROW );
2187             xSection->attach( xTextRange );
2188             xRet.set(xSection, uno::UNO_QUERY );
2189         }
2190         catch(const uno::Exception&)
2191         {
2192         }
2193 
2194     }
2195 
2196     return xRet;
2197 }
2198 
appendGlossaryEntry()2199 void DomainMapper_Impl::appendGlossaryEntry()
2200 {
2201     appendTextSectionAfter(m_xGlossaryEntryStart);
2202 }
2203 
PushPageHeaderFooter(bool bHeader,SectionPropertyMap::PageType eType)2204 void DomainMapper_Impl::PushPageHeaderFooter(bool bHeader, SectionPropertyMap::PageType eType)
2205 {
2206     m_aHeaderFooterStack.push(HeaderFooterContext(m_bTextInserted));
2207     m_bTextInserted = false;
2208 
2209     const PropertyIds ePropIsOn = bHeader? PROP_HEADER_IS_ON: PROP_FOOTER_IS_ON;
2210     const PropertyIds ePropShared = bHeader? PROP_HEADER_IS_SHARED: PROP_FOOTER_IS_SHARED;
2211     const PropertyIds ePropTextLeft = bHeader? PROP_HEADER_TEXT_LEFT: PROP_FOOTER_TEXT_LEFT;
2212     const PropertyIds ePropText = bHeader? PROP_HEADER_TEXT: PROP_FOOTER_TEXT;
2213 
2214     m_bDiscardHeaderFooter = true;
2215     m_eInHeaderFooterImport
2216         = bHeader ? HeaderFooterImportState::header : HeaderFooterImportState::footer;
2217 
2218     //get the section context
2219     PropertyMapPtr pContext = DomainMapper_Impl::GetTopContextOfType(CONTEXT_SECTION);
2220     //ask for the header/footer name of the given type
2221     SectionPropertyMap* pSectionContext = dynamic_cast< SectionPropertyMap* >( pContext.get() );
2222     if(pSectionContext)
2223     {
2224         // clear the "Link To Previous" flag so that the header/footer
2225         // content is not copied from the previous section
2226         pSectionContext->ClearHeaderFooterLinkToPrevious(bHeader, eType);
2227 
2228         if (!m_bIsNewDoc)
2229         {
2230             return; // TODO sw cannot Undo insert header/footer without crashing
2231         }
2232 
2233         uno::Reference< beans::XPropertySet > xPageStyle =
2234             pSectionContext->GetPageStyle(
2235                 *this,
2236                 eType == SectionPropertyMap::PAGE_FIRST );
2237         if (!xPageStyle.is())
2238             return;
2239         try
2240         {
2241             bool bLeft = eType == SectionPropertyMap::PAGE_LEFT;
2242             bool bFirst = eType == SectionPropertyMap::PAGE_FIRST;
2243             if ((!bLeft && !GetSettingsTable()->GetEvenAndOddHeaders()) || (GetSettingsTable()->GetEvenAndOddHeaders()))
2244             {
2245                 //switch on header/footer use
2246                 xPageStyle->setPropertyValue(
2247                         getPropertyName(ePropIsOn),
2248                         uno::makeAny(true));
2249 
2250                 // If the 'Different Even & Odd Pages' flag is turned on - do not ignore it
2251                 // Even if the 'Even' header/footer is blank - the flag should be imported (so it would look in LO like in Word)
2252                 if (!bFirst && GetSettingsTable()->GetEvenAndOddHeaders())
2253                     xPageStyle->setPropertyValue(getPropertyName(ePropShared), uno::makeAny(false));
2254 
2255                 //set the interface
2256                 uno::Reference< text::XText > xText;
2257                 xPageStyle->getPropertyValue(getPropertyName(bLeft? ePropTextLeft: ePropText)) >>= xText;
2258 
2259                 m_aTextAppendStack.push(TextAppendContext(uno::Reference< text::XTextAppend >(xText, uno::UNO_QUERY_THROW),
2260                     m_bIsNewDoc
2261                         ? uno::Reference<text::XTextCursor>()
2262                         : xText->createTextCursorByRange(xText->getStart())));
2263                 m_bDiscardHeaderFooter = false; // set only on success!
2264             }
2265         }
2266         catch( const uno::Exception& )
2267         {
2268             DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
2269         }
2270     }
2271 }
2272 
PushPageHeader(SectionPropertyMap::PageType eType)2273 void DomainMapper_Impl::PushPageHeader(SectionPropertyMap::PageType eType)
2274 {
2275     PushPageHeaderFooter(/* bHeader = */ true, eType);
2276 }
2277 
PushPageFooter(SectionPropertyMap::PageType eType)2278 void DomainMapper_Impl::PushPageFooter(SectionPropertyMap::PageType eType)
2279 {
2280     PushPageHeaderFooter(/* bHeader = */ false, eType);
2281 }
2282 
PopPageHeaderFooter()2283 void DomainMapper_Impl::PopPageHeaderFooter()
2284 {
2285     //header and footer always have an empty paragraph at the end
2286     //this has to be removed
2287     RemoveLastParagraph( );
2288 
2289     if (!m_aTextAppendStack.empty())
2290     {
2291         if (!m_bDiscardHeaderFooter)
2292         {
2293             m_aTextAppendStack.pop();
2294         }
2295         m_bDiscardHeaderFooter = false;
2296     }
2297     m_eInHeaderFooterImport = HeaderFooterImportState::none;
2298 
2299     if (!m_aHeaderFooterStack.empty())
2300     {
2301         m_bTextInserted = m_aHeaderFooterStack.top().getTextInserted();
2302         m_aHeaderFooterStack.pop();
2303     }
2304 }
2305 
PushFootOrEndnote(bool bIsFootnote)2306 void DomainMapper_Impl::PushFootOrEndnote( bool bIsFootnote )
2307 {
2308     SAL_WARN_IF(m_bInFootOrEndnote, "writerfilter.dmapper", "PushFootOrEndnote() is called from another foot or endnote");
2309     m_bInFootOrEndnote = true;
2310     m_bCheckFirstFootnoteTab = true;
2311     m_bSaveFirstParagraphInCell = m_bFirstParagraphInCell;
2312     try
2313     {
2314         // Redlines outside the footnote should not affect footnote content
2315         m_aRedlines.push(std::vector< RedlineParamsPtr >());
2316 
2317         // IMHO character styles from footnote labels should be ignored in the edit view of Writer.
2318         // This adds a hack on top of the following hack to save the style name in the context.
2319         PropertyMapPtr pTopContext = GetTopContext();
2320         OUString sFootnoteCharStyleName;
2321         boost::optional< PropertyMap::Property > aProp = pTopContext->getProperty(PROP_CHAR_STYLE_NAME);
2322         if (aProp)
2323             aProp->second >>= sFootnoteCharStyleName;
2324 
2325         // Remove style reference, if any. This reference did appear here as a side effect of tdf#43017
2326         // Seems it is not required by LO, but causes side effects during editing. So remove it
2327         // for footnotes/endnotes to restore original LO behavior here.
2328         pTopContext->Erase(PROP_CHAR_STYLE_NAME);
2329 
2330         uno::Reference< text::XText > xFootnoteText;
2331         if (GetTextFactory().is())
2332             xFootnoteText.set( GetTextFactory()->createInstance(
2333             bIsFootnote ?
2334                 OUString( "com.sun.star.text.Footnote" ) : OUString( "com.sun.star.text.Endnote" )),
2335             uno::UNO_QUERY_THROW );
2336         uno::Reference< text::XFootnote > xFootnote( xFootnoteText, uno::UNO_QUERY_THROW );
2337         pTopContext->SetFootnote(xFootnote, sFootnoteCharStyleName);
2338         uno::Sequence< beans::PropertyValue > aFontProperties = pTopContext->GetPropertyValues();
2339         appendTextContent( uno::Reference< text::XTextContent >( xFootnoteText, uno::UNO_QUERY_THROW ), aFontProperties );
2340         m_aTextAppendStack.push(TextAppendContext(uno::Reference< text::XTextAppend >( xFootnoteText, uno::UNO_QUERY_THROW ),
2341                     xFootnoteText->createTextCursorByRange(xFootnoteText->getStart())));
2342 
2343         // Redlines for the footnote anchor in the main text content
2344         std::vector< RedlineParamsPtr > aFootnoteRedline = std::move(m_aRedlines.top());
2345         m_aRedlines.pop();
2346         CheckRedline( xFootnote->getAnchor( ) );
2347         m_aRedlines.push( aFootnoteRedline );
2348 
2349         // Try scanning for custom footnote labels
2350         if (!sFootnoteCharStyleName.isEmpty())
2351             StartCustomFootnote(pTopContext);
2352         else
2353             EndCustomFootnote();
2354     }
2355     catch( const uno::Exception& )
2356     {
2357         TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "PushFootOrEndnote");
2358     }
2359 }
2360 
CreateRedline(uno::Reference<text::XTextRange> const & xRange,const RedlineParamsPtr & pRedline)2361 void DomainMapper_Impl::CreateRedline(uno::Reference<text::XTextRange> const& xRange,
2362         const RedlineParamsPtr& pRedline)
2363 {
2364     if ( pRedline.get( ) )
2365     {
2366         try
2367         {
2368             OUString sType;
2369             switch ( pRedline->m_nToken & 0xffff )
2370             {
2371             case XML_mod:
2372                 sType = getPropertyName( PROP_FORMAT );
2373                 break;
2374             case XML_moveTo:
2375             case XML_ins:
2376                 sType = getPropertyName( PROP_INSERT );
2377                 break;
2378             case XML_moveFrom:
2379                 m_pParaMarkerRedlineMoveFrom = pRedline.get();
2380                 [[fallthrough]];
2381             case XML_del:
2382                 sType = getPropertyName( PROP_DELETE );
2383                 break;
2384             case XML_ParagraphFormat:
2385                 sType = getPropertyName( PROP_PARAGRAPH_FORMAT );
2386                 break;
2387             default:
2388                 throw lang::IllegalArgumentException("illegal redline token type", nullptr, 0);
2389             }
2390             beans::PropertyValues aRedlineProperties( 3 );
2391             beans::PropertyValue * pRedlineProperties = aRedlineProperties.getArray(  );
2392             pRedlineProperties[0].Name = getPropertyName( PROP_REDLINE_AUTHOR );
2393             pRedlineProperties[0].Value <<= pRedline->m_sAuthor;
2394             pRedlineProperties[1].Name = getPropertyName( PROP_REDLINE_DATE_TIME );
2395             pRedlineProperties[1].Value <<= ConversionHelper::ConvertDateStringToDateTime( pRedline->m_sDate );
2396             pRedlineProperties[2].Name = getPropertyName( PROP_REDLINE_REVERT_PROPERTIES );
2397             pRedlineProperties[2].Value <<= pRedline->m_aRevertProperties;
2398             if (!m_bIsActualParagraphFramed)
2399             {
2400                 uno::Reference < text::XRedline > xRedline( xRange, uno::UNO_QUERY_THROW );
2401                 xRedline->makeRedline( sType, aRedlineProperties );
2402             }
2403             else
2404             {
2405                 aFramedRedlines.push_back( uno::makeAny(xRange) );
2406                 aFramedRedlines.push_back( uno::makeAny(sType) );
2407                 aFramedRedlines.push_back( uno::makeAny(aRedlineProperties) );
2408             }
2409         }
2410         catch( const uno::Exception & )
2411         {
2412             OSL_FAIL( "Exception in makeRedline" );
2413         }
2414     }
2415 }
2416 
CheckParaMarkerRedline(uno::Reference<text::XTextRange> const & xRange)2417 void DomainMapper_Impl::CheckParaMarkerRedline( uno::Reference< text::XTextRange > const& xRange )
2418 {
2419     if ( m_pParaMarkerRedline.get( ) )
2420     {
2421         CreateRedline( xRange, m_pParaMarkerRedline );
2422         if ( m_pParaMarkerRedline.get( ) )
2423         {
2424             m_pParaMarkerRedline.clear();
2425             m_currentRedline.clear();
2426         }
2427     }
2428     else if ( m_pParaMarkerRedlineMoveFrom.get( ) )
2429     {
2430         // terminating moveFrom redline removes also the paragraph mark
2431         m_pParaMarkerRedlineMoveFrom->m_nToken = XML_del;
2432         CreateRedline( xRange, m_pParaMarkerRedlineMoveFrom );
2433     }
2434     if ( m_pParaMarkerRedlineMoveFrom.get( ) )
2435     {
2436         m_pParaMarkerRedlineMoveFrom.clear();
2437     }
2438 }
2439 
CheckRedline(uno::Reference<text::XTextRange> const & xRange)2440 void DomainMapper_Impl::CheckRedline( uno::Reference< text::XTextRange > const& xRange )
2441 {
2442     // Writer core "officially" does not like overlapping redlines, and its UNO interface is stupid enough
2443     // to not prevent that. However, in practice in fact everything appears to work fine (except for the debug warnings
2444     // about redline table corruption, which may possibly be harmless in reality). So leave this as it is, since this
2445     // is a better representation of how the changes happened. If this will ever become a problem, overlapping redlines
2446     // will need to be merged into one, just like doing the changes in the UI does, which will lose some information
2447     // (and so if that happens, it may be better to fix Writer).
2448     // Create the redlines here from lowest (formats) to highest (inserts/removals) priority, since the last one is
2449     // what Writer presents graphically, so this will show deletes as deleted text and not as just formatted text being there.
2450     bool bUsedRange = m_aRedlines.top().size() > 0 || (GetTopContextOfType(CONTEXT_CHARACTER) &&
2451         GetTopContextOfType(CONTEXT_CHARACTER)->Redlines().size() > 0);
2452 
2453     // only export ParagraphFormat, when there is no other redline in the same text portion to avoid missing redline compression,
2454     // but always export the first ParagraphFormat redline in a paragraph to keep the paragraph style change data for rejection
2455     if( (!bUsedRange || !m_bParaChanged) && GetTopContextOfType(CONTEXT_PARAGRAPH) )
2456     {
2457         std::vector<RedlineParamsPtr>& avRedLines = GetTopContextOfType(CONTEXT_PARAGRAPH)->Redlines();
2458         for( const auto& rRedline : avRedLines )
2459             CreateRedline( xRange, rRedline );
2460     }
2461     if( GetTopContextOfType(CONTEXT_CHARACTER) )
2462     {
2463         std::vector<RedlineParamsPtr>& avRedLines = GetTopContextOfType(CONTEXT_CHARACTER)->Redlines();
2464         for( const auto& rRedline : avRedLines )
2465             CreateRedline( xRange, rRedline );
2466     }
2467     for (const auto& rRedline : m_aRedlines.top() )
2468         CreateRedline( xRange, rRedline );
2469 }
2470 
StartParaMarkerChange()2471 void DomainMapper_Impl::StartParaMarkerChange( )
2472 {
2473     m_bIsParaMarkerChange = true;
2474 }
2475 
EndParaMarkerChange()2476 void DomainMapper_Impl::EndParaMarkerChange( )
2477 {
2478     m_bIsParaMarkerChange = false;
2479     m_previousRedline = m_currentRedline;
2480     m_currentRedline.clear();
2481 }
2482 
StartCustomFootnote(const PropertyMapPtr pContext)2483 void DomainMapper_Impl::StartCustomFootnote(const PropertyMapPtr pContext)
2484 {
2485     if (pContext == m_pFootnoteContext)
2486         return;
2487 
2488     assert(pContext->GetFootnote().is());
2489     m_bHasFootnoteStyle = true;
2490     m_bCheckFootnoteStyle = !pContext->GetFootnoteStyle().isEmpty();
2491     m_pFootnoteContext = pContext;
2492 }
2493 
EndCustomFootnote()2494 void DomainMapper_Impl::EndCustomFootnote()
2495 {
2496     m_bHasFootnoteStyle = false;
2497     m_bCheckFootnoteStyle = false;
2498 }
2499 
PushAnnotation()2500 void DomainMapper_Impl::PushAnnotation()
2501 {
2502     try
2503     {
2504         m_bIsInComments = true;
2505         if (!GetTextFactory().is())
2506             return;
2507         m_xAnnotationField.set( GetTextFactory()->createInstance( "com.sun.star.text.TextField.Annotation" ),
2508                                 uno::UNO_QUERY_THROW );
2509         uno::Reference< text::XText > xAnnotationText;
2510         m_xAnnotationField->getPropertyValue("TextRange") >>= xAnnotationText;
2511         m_aTextAppendStack.push(TextAppendContext(uno::Reference< text::XTextAppend >( xAnnotationText, uno::UNO_QUERY_THROW ),
2512                     m_bIsNewDoc ? uno::Reference<text::XTextCursor>() : xAnnotationText->createTextCursorByRange(xAnnotationText->getStart())));
2513     }
2514     catch( const uno::Exception&)
2515     {
2516         DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
2517     }
2518 }
2519 
2520 
PopFootOrEndnote()2521 void DomainMapper_Impl::PopFootOrEndnote()
2522 {
2523     if (!IsRTFImport())
2524         RemoveLastParagraph();
2525 
2526     // In case the foot or endnote did not contain a tab.
2527     m_bIgnoreNextTab = false;
2528 
2529     if (!m_aTextAppendStack.empty())
2530         m_aTextAppendStack.pop();
2531 
2532     if (m_aRedlines.size() == 1)
2533     {
2534         SAL_WARN("writerfilter.dmapper", "PopFootOrEndnote() is called without PushFootOrEndnote()?");
2535         return;
2536     }
2537     m_aRedlines.pop();
2538     m_eSkipFootnoteState = SkipFootnoteSeparator::OFF;
2539     m_bInFootOrEndnote = false;
2540     m_pFootnoteContext = nullptr;
2541     m_bFirstParagraphInCell = m_bSaveFirstParagraphInCell;
2542 }
2543 
PopAnnotation()2544 void DomainMapper_Impl::PopAnnotation()
2545 {
2546     RemoveLastParagraph();
2547 
2548     m_bIsInComments = false;
2549     m_aTextAppendStack.pop();
2550 
2551     try
2552     {
2553         // See if the annotation will be a single position or a range.
2554         if (m_nAnnotationId == -1 || !m_aAnnotationPositions[m_nAnnotationId].m_xStart.is() || !m_aAnnotationPositions[m_nAnnotationId].m_xEnd.is())
2555         {
2556             uno::Sequence< beans::PropertyValue > aEmptyProperties;
2557             uno::Reference< text::XTextContent > xContent( m_xAnnotationField, uno::UNO_QUERY_THROW );
2558             appendTextContent( xContent, aEmptyProperties );
2559             CheckRedline( xContent->getAnchor( ) );
2560         }
2561         else
2562         {
2563             AnnotationPosition& aAnnotationPosition = m_aAnnotationPositions[m_nAnnotationId];
2564             // Create a range that points to the annotation start/end.
2565             uno::Reference<text::XText> const xText = aAnnotationPosition.m_xStart->getText();
2566             uno::Reference<text::XTextCursor> const xCursor = xText->createTextCursorByRange(aAnnotationPosition.m_xStart);
2567 
2568             bool bMarker = false;
2569             uno::Reference<text::XTextRangeCompare> xTextRangeCompare(xText, uno::UNO_QUERY);
2570             if (xTextRangeCompare->compareRegionStarts(aAnnotationPosition.m_xStart, aAnnotationPosition.m_xEnd) == 0)
2571             {
2572                 // Insert a marker so that comment around an anchored image is not collapsed during
2573                 // insertion.
2574                 xText->insertString(xCursor, "x", false);
2575                 bMarker = true;
2576             }
2577 
2578             xCursor->gotoRange(aAnnotationPosition.m_xEnd, true);
2579             uno::Reference<text::XTextRange> const xTextRange(xCursor, uno::UNO_QUERY_THROW);
2580 
2581             // Attach the annotation to the range.
2582             uno::Reference<text::XTextAppend> const xTextAppend = m_aTextAppendStack.top().xTextAppend;
2583             xTextAppend->insertTextContent(xTextRange, uno::Reference<text::XTextContent>(m_xAnnotationField, uno::UNO_QUERY_THROW), !xCursor->isCollapsed());
2584 
2585             if (bMarker)
2586             {
2587                 // Remove the marker.
2588                 xCursor->goLeft(1, true);
2589                 xCursor->setString(OUString());
2590             }
2591         }
2592         m_aAnnotationPositions.erase( m_nAnnotationId );
2593     }
2594     catch (uno::Exception const&)
2595     {
2596         TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "Cannot insert annotation field");
2597     }
2598 
2599     m_xAnnotationField.clear();
2600     m_nAnnotationId = -1;
2601 }
2602 
PushPendingShape(const uno::Reference<drawing::XShape> & xShape)2603 void DomainMapper_Impl::PushPendingShape( const uno::Reference< drawing::XShape > & xShape )
2604 {
2605     m_aPendingShapes.push_back(xShape);
2606 }
2607 
PopPendingShape()2608 uno::Reference<drawing::XShape> DomainMapper_Impl::PopPendingShape()
2609 {
2610     uno::Reference<drawing::XShape> xRet;
2611     if (!m_aPendingShapes.empty())
2612     {
2613         xRet = m_aPendingShapes.front();
2614         m_aPendingShapes.pop_front();
2615     }
2616     return xRet;
2617 }
2618 
PushShapeContext(const uno::Reference<drawing::XShape> & xShape)2619 void DomainMapper_Impl::PushShapeContext( const uno::Reference< drawing::XShape > & xShape )
2620 {
2621     // Append these early, so the context and the table manager stack will be
2622     // in sync, even if the text append stack is empty.
2623     appendTableManager();
2624     appendTableHandler();
2625     getTableManager().startLevel();
2626 
2627     if (m_aTextAppendStack.empty())
2628         return;
2629     uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend;
2630 
2631     try
2632     {
2633         uno::Reference< lang::XServiceInfo > xSInfo( xShape, uno::UNO_QUERY_THROW );
2634         if (xSInfo->supportsService("com.sun.star.drawing.GroupShape"))
2635         {
2636             // A GroupShape doesn't implement text::XTextRange, but appending
2637             // an empty reference to the stacks still makes sense, because this
2638             // way bToRemove can be set, and we won't end up with duplicated
2639             // shapes for OLE objects.
2640             m_aTextAppendStack.push(TextAppendContext(uno::Reference<text::XTextAppend>(xShape, uno::UNO_QUERY), uno::Reference<text::XTextCursor>()));
2641             uno::Reference<text::XTextContent> xTxtContent(xShape, uno::UNO_QUERY);
2642             m_aAnchoredStack.push(AnchoredContext(xTxtContent));
2643         }
2644         else if (xSInfo->supportsService("com.sun.star.drawing.OLE2Shape"))
2645         {
2646             // OLE2Shape from oox should be converted to a TextEmbeddedObject for sw.
2647             m_aTextAppendStack.push(TextAppendContext(uno::Reference<text::XTextAppend>(xShape, uno::UNO_QUERY), uno::Reference<text::XTextCursor>()));
2648             uno::Reference<text::XTextContent> xTextContent(xShape, uno::UNO_QUERY);
2649             m_aAnchoredStack.push(AnchoredContext(xTextContent));
2650             uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
2651 
2652             m_xEmbedded.set(m_xTextFactory->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW);
2653             uno::Reference<beans::XPropertySet> xEmbeddedProperties(m_xEmbedded, uno::UNO_QUERY_THROW);
2654             xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_EMBEDDED_OBJECT), xShapePropertySet->getPropertyValue(getPropertyName(PROP_EMBEDDED_OBJECT)));
2655             xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_ANCHOR_TYPE), uno::makeAny(text::TextContentAnchorType_AS_CHARACTER));
2656             // So that the original bitmap-only shape will be replaced by the embedded object.
2657             m_aAnchoredStack.top().bToRemove = true;
2658             m_aTextAppendStack.pop();
2659             appendTextContent(m_xEmbedded, uno::Sequence<beans::PropertyValue>());
2660         }
2661         else
2662         {
2663             uno::Reference< text::XTextRange > xShapeText( xShape, uno::UNO_QUERY_THROW);
2664             // Add the shape to the text append stack
2665             m_aTextAppendStack.push( TextAppendContext(uno::Reference< text::XTextAppend >( xShape, uno::UNO_QUERY_THROW ),
2666                         m_bIsNewDoc ? uno::Reference<text::XTextCursor>() : m_xBodyText->createTextCursorByRange(xShapeText->getStart() )));
2667 
2668             // Add the shape to the anchored objects stack
2669             uno::Reference< text::XTextContent > xTxtContent( xShape, uno::UNO_QUERY_THROW );
2670             m_aAnchoredStack.push( AnchoredContext(xTxtContent) );
2671 
2672             uno::Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY_THROW );
2673 #ifdef DBG_UTIL
2674             TagLogger::getInstance().unoPropertySet(xProps);
2675 #endif
2676             text::TextContentAnchorType nAnchorType(text::TextContentAnchorType_AT_PARAGRAPH);
2677             xProps->getPropertyValue(getPropertyName( PROP_ANCHOR_TYPE )) >>= nAnchorType;
2678             bool checkZOrderStatus = false;
2679             if (xSInfo->supportsService("com.sun.star.text.TextFrame"))
2680             {
2681                 SetIsTextFrameInserted(true);
2682                 // Extract the special "btLr text frame" mode, requested by oox, if needed.
2683                 // Extract vml ZOrder from FrameInteropGrabBag
2684                 uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
2685                 uno::Sequence<beans::PropertyValue> aGrabBag;
2686                 xShapePropertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
2687 
2688                 for (const auto& rProp : std::as_const(aGrabBag))
2689                 {
2690                     if (rProp.Name == "VML-Z-ORDER")
2691                     {
2692                         GraphicZOrderHelper* pZOrderHelper = m_rDMapper.graphicZOrderHelper();
2693                         sal_Int32 zOrder(0);
2694                         rProp.Value >>= zOrder;
2695                         xShapePropertySet->setPropertyValue( "ZOrder", uno::makeAny(pZOrderHelper->findZOrder(zOrder)));
2696                         pZOrderHelper->addItem(xShapePropertySet, zOrder);
2697                         xShapePropertySet->setPropertyValue(getPropertyName( PROP_OPAQUE ), uno::makeAny( zOrder >= 0 ) );
2698                         checkZOrderStatus = true;
2699                     }
2700                     else if ( rProp.Name == "TxbxHasLink" )
2701                     {
2702                         //Chaining of textboxes will happen in ~DomainMapper_Impl
2703                         //i.e when all the textboxes are read and all its attributes
2704                         //have been set ( basically the Name/LinkedDisplayName )
2705                         //which is set in Graphic Import.
2706                         m_vTextFramesForChaining.push_back(xShape);
2707                     }
2708                 }
2709 
2710                 uno::Reference<text::XTextContent> xTextContent(xShape, uno::UNO_QUERY_THROW);
2711                 uno::Reference<text::XTextRange> xTextRange(xTextAppend->createTextCursorByRange(xTextAppend->getEnd()), uno::UNO_QUERY_THROW);
2712                 xTextAppend->insertTextContent(xTextRange, xTextContent, false);
2713 
2714                 uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
2715                 // we need to re-set this value to xTextContent, then only values are preserved.
2716                 xPropertySet->setPropertyValue("FrameInteropGrabBag",uno::makeAny(aGrabBag));
2717             }
2718             else if (nAnchorType == text::TextContentAnchorType_AS_CHARACTER)
2719             {
2720                 // Fix spacing for as-character objects. If the paragraph has CT_Spacing_after set,
2721                 // it needs to be set on the object too, as that's what object placement code uses.
2722                 PropertyMapPtr paragraphContext = GetTopContextOfType( CONTEXT_PARAGRAPH );
2723                 boost::optional<PropertyMap::Property> aPropMargin = paragraphContext->getProperty(PROP_PARA_BOTTOM_MARGIN);
2724                 if(aPropMargin)
2725                     xProps->setPropertyValue( getPropertyName( PROP_BOTTOM_MARGIN ), aPropMargin->second );
2726             }
2727             else
2728             {
2729                 uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
2730                 uno::Sequence<beans::PropertyValue> aGrabBag;
2731                 xShapePropertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
2732                 for (const auto& rProp : std::as_const(aGrabBag))
2733                 {
2734                     if (rProp.Name == "VML-Z-ORDER")
2735                     {
2736                         GraphicZOrderHelper* pZOrderHelper = m_rDMapper.graphicZOrderHelper();
2737                         sal_Int32 zOrder(0);
2738                         rProp.Value >>= zOrder;
2739                         xShapePropertySet->setPropertyValue( "ZOrder", uno::makeAny(pZOrderHelper->findZOrder(zOrder)));
2740                         pZOrderHelper->addItem(xShapePropertySet, zOrder);
2741                         xShapePropertySet->setPropertyValue(getPropertyName( PROP_OPAQUE ), uno::makeAny( zOrder >= 0 ) );
2742                         checkZOrderStatus = true;
2743                     }
2744                     else if ( rProp.Name == "TxbxHasLink" )
2745                     {
2746                         //Chaining of textboxes will happen in ~DomainMapper_Impl
2747                         //i.e when all the textboxes are read and all its attributes
2748                         //have been set ( basically the Name/LinkedDisplayName )
2749                         //which is set in Graphic Import.
2750                         m_vTextFramesForChaining.push_back(xShape);
2751                     }
2752                 }
2753 
2754                 if(IsSdtEndBefore())
2755                 {
2756                     uno::Reference< beans::XPropertySetInfo > xPropSetInfo;
2757                     if(xShapePropertySet.is())
2758                     {
2759                         xPropSetInfo = xShapePropertySet->getPropertySetInfo();
2760                         if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("InteropGrabBag"))
2761                         {
2762                             uno::Sequence<beans::PropertyValue> aShapeGrabBag( comphelper::InitPropertySequence({
2763                                 { "SdtEndBefore", uno::Any(true) }
2764                             }));
2765                             xShapePropertySet->setPropertyValue("InteropGrabBag",uno::makeAny(aShapeGrabBag));
2766                         }
2767                     }
2768                 }
2769             }
2770             if (!IsInHeaderFooter() && !checkZOrderStatus)
2771                 xProps->setPropertyValue(
2772                         getPropertyName( PROP_OPAQUE ),
2773                         uno::makeAny( true ) );
2774         }
2775         m_bParaChanged = true;
2776         getTableManager().setIsInShape(true);
2777     }
2778     catch ( const uno::Exception& )
2779     {
2780         TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "Exception when adding shape");
2781     }
2782 }
2783 /*
2784  * Updating chart height and width after reading the actual values from wp:extent
2785 */
UpdateEmbeddedShapeProps(const uno::Reference<drawing::XShape> & xShape)2786 void DomainMapper_Impl::UpdateEmbeddedShapeProps(const uno::Reference< drawing::XShape > & xShape)
2787 {
2788     if (!xShape.is())
2789         return;
2790 
2791     uno::Reference<beans::XPropertySet> xEmbeddedProperties(m_xEmbedded, uno::UNO_QUERY_THROW);
2792     awt::Size aSize = xShape->getSize( );
2793     xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_WIDTH), uno::makeAny(sal_Int32(aSize.Width)));
2794     xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_HEIGHT), uno::makeAny(sal_Int32(aSize.Height)));
2795 }
2796 
2797 
PopShapeContext()2798 void DomainMapper_Impl::PopShapeContext()
2799 {
2800     if (hasTableManager())
2801     {
2802         getTableManager().endLevel();
2803         popTableManager();
2804     }
2805     if ( !m_aAnchoredStack.empty() )
2806     {
2807         // For OLE object replacement shape, the text append context was already removed
2808         // or the OLE object couldn't be inserted.
2809         if ( !m_aAnchoredStack.top().bToRemove )
2810         {
2811             RemoveLastParagraph();
2812             if (!m_aTextAppendStack.empty())
2813                 m_aTextAppendStack.pop();
2814         }
2815 
2816         uno::Reference< text::XTextContent > xObj = m_aAnchoredStack.top( ).xTextContent;
2817         try
2818         {
2819             appendTextContent( xObj, uno::Sequence< beans::PropertyValue >() );
2820         }
2821         catch ( const uno::RuntimeException& )
2822         {
2823             // this is normal: the shape is already attached
2824         }
2825 
2826         const uno::Reference<drawing::XShape> xShape( xObj, uno::UNO_QUERY_THROW );
2827         // Remove the shape if required (most likely replacement shape for OLE object)
2828         // or anchored to a discarded header or footer
2829         if ( m_aAnchoredStack.top().bToRemove || m_bDiscardHeaderFooter )
2830         {
2831             try
2832             {
2833                 uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(m_xTextDocument, uno::UNO_QUERY_THROW);
2834                 uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
2835                 if ( xDrawPage.is() )
2836                     xDrawPage->remove( xShape );
2837             }
2838             catch( const uno::Exception& )
2839             {
2840             }
2841         }
2842 
2843         // Relative width calculations deferred until section's margins are defined.
2844         // Being cautious: only deferring undefined/minimum-width shapes in order to avoid causing potential regressions
2845         css::awt::Size aShapeSize;
2846         try
2847         {
2848             aShapeSize = xShape->getSize();
2849         }
2850         catch (const css::uno::RuntimeException& e)
2851         {
2852             // May happen e.g. when text frame has no frame format
2853             // See sw/qa/extras/ooxmlimport/data/n779627.docx
2854             SAL_WARN("writerfilter.dmapper", "getSize failed. " << e.Message);
2855         }
2856         if( aShapeSize.Width <= 2 )
2857         {
2858             const uno::Reference<beans::XPropertySet> xShapePropertySet( xShape, uno::UNO_QUERY );
2859             SectionPropertyMap* pSectionContext = GetSectionContext();
2860             if ( pSectionContext && (!hasTableManager() || !getTableManager().isInTable()) &&
2861                  xShapePropertySet->getPropertySetInfo()->hasPropertyByName(getPropertyName(PROP_RELATIVE_WIDTH)) )
2862             {
2863                 pSectionContext->addRelativeWidthShape(xShape);
2864             }
2865         }
2866 
2867         m_aAnchoredStack.pop();
2868     }
2869 }
2870 
IsSdtEndBefore()2871 bool DomainMapper_Impl::IsSdtEndBefore()
2872 {
2873     bool bIsSdtEndBefore = false;
2874     PropertyMapPtr pContext = GetTopContextOfType(CONTEXT_CHARACTER);
2875     if(pContext)
2876     {
2877         const uno::Sequence< beans::PropertyValue > currentCharProps = pContext->GetPropertyValues();
2878         for (const auto& rCurrentCharProp : currentCharProps)
2879         {
2880             if (rCurrentCharProp.Name == "CharInteropGrabBag")
2881             {
2882                 uno::Sequence<beans::PropertyValue> aCharGrabBag;
2883                 rCurrentCharProp.Value >>= aCharGrabBag;
2884                 for (const auto& rProp : std::as_const(aCharGrabBag))
2885                 {
2886                     if(rProp.Name == "SdtEndBefore")
2887                     {
2888                         rProp.Value >>= bIsSdtEndBefore;
2889                     }
2890                 }
2891             }
2892         }
2893     }
2894     return bIsSdtEndBefore;
2895 }
2896 
IsDiscardHeaderFooter() const2897 bool DomainMapper_Impl::IsDiscardHeaderFooter() const
2898 {
2899     return m_bDiscardHeaderFooter;
2900 }
2901 
2902 // called from TableManager::closeCell()
ClearPreviousParagraph()2903 void DomainMapper_Impl::ClearPreviousParagraph()
2904 {
2905     // in table cells, set bottom auto margin of last paragraph to 0, except in paragraphs with numbering
2906     if ((m_nTableDepth == (m_nTableCellDepth + 1))
2907         && m_xPreviousParagraph.is()
2908         && hasTableManager() && getTableManager().isCellLastParaAfterAutospacing())
2909     {
2910         uno::Reference<container::XNamed> xPreviousNumberingRules(m_xPreviousParagraph->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
2911         if ( !xPreviousNumberingRules.is() || xPreviousNumberingRules->getName().isEmpty() )
2912             m_xPreviousParagraph->setPropertyValue("ParaBottomMargin", uno::makeAny(static_cast<sal_Int32>(0)));
2913     }
2914 
2915     m_xPreviousParagraph.clear();
2916 
2917     // next table paragraph will be first paragraph in a cell
2918     m_bFirstParagraphInCell = true;
2919 }
2920 
lcl_ParseNumberingType(const OUString & rCommand)2921 static sal_Int16 lcl_ParseNumberingType( const OUString& rCommand )
2922 {
2923     sal_Int16 nRet = style::NumberingType::PAGE_DESCRIPTOR;
2924 
2925     //  The command looks like: " PAGE \* Arabic "
2926     // tdf#132185: but may as well be "PAGE \* Arabic"
2927     OUString sNumber;
2928     constexpr OUStringLiteral rSeparator("\\* ");
2929     if (sal_Int32 nStartIndex = rCommand.indexOf(rSeparator); nStartIndex >= 0)
2930     {
2931         nStartIndex += rSeparator.getLength();
2932         sNumber = rCommand.getToken(0, ' ', nStartIndex);
2933     }
2934 
2935     if( !sNumber.isEmpty() )
2936     {
2937         //todo: might make sense to hash this list, too
2938         struct NumberingPairs
2939         {
2940             const sal_Char* cWordName;
2941             sal_Int16 const nType;
2942         };
2943         static const NumberingPairs aNumberingPairs[] =
2944         {
2945             {"Arabic", style::NumberingType::ARABIC}
2946             ,{"ROMAN", style::NumberingType::ROMAN_UPPER}
2947             ,{"roman", style::NumberingType::ROMAN_LOWER}
2948             ,{"ALPHABETIC", style::NumberingType::CHARS_UPPER_LETTER}
2949             ,{"alphabetic", style::NumberingType::CHARS_LOWER_LETTER}
2950             ,{"CircleNum", style::NumberingType::CIRCLE_NUMBER}
2951             ,{"ThaiArabic", style::NumberingType::CHARS_THAI}
2952             ,{"ThaiCardText", style::NumberingType::CHARS_THAI}
2953             ,{"ThaiLetter", style::NumberingType::CHARS_THAI}
2954 //            ,{"SBCHAR", style::NumberingType::}
2955 //            ,{"DBCHAR", style::NumberingType::}
2956 //            ,{"DBNUM1", style::NumberingType::}
2957 //            ,{"DBNUM2", style::NumberingType::}
2958 //            ,{"DBNUM3", style::NumberingType::}
2959 //            ,{"DBNUM4", style::NumberingType::}
2960             ,{"Aiueo", style::NumberingType::AIU_FULLWIDTH_JA}
2961             ,{"Iroha", style::NumberingType::IROHA_FULLWIDTH_JA}
2962 //            ,{"ZODIAC1", style::NumberingType::}
2963 //            ,{"ZODIAC2", style::NumberingType::}
2964 //            ,{"ZODIAC3", style::NumberingType::}
2965 //            ,{"CHINESENUM1", style::NumberingType::}
2966 //            ,{"CHINESENUM2", style::NumberingType::}
2967 //            ,{"CHINESENUM3", style::NumberingType::}
2968             ,{"ArabicAlpha", style::NumberingType::CHARS_ARABIC}
2969             ,{"ArabicAbjad", style::NumberingType::FULLWIDTH_ARABIC}
2970 /* possible values:
2971 style::NumberingType::
2972 
2973     CHARS_UPPER_LETTER_N
2974     CHARS_LOWER_LETTER_N
2975     TRANSLITERATION
2976     NATIVE_NUMBERING
2977     CIRCLE_NUMBER
2978     NUMBER_LOWER_ZH
2979     NUMBER_UPPER_ZH
2980     NUMBER_UPPER_ZH_TW
2981     TIAN_GAN_ZH
2982     DI_ZI_ZH
2983     NUMBER_TRADITIONAL_JA
2984     AIU_HALFWIDTH_JA
2985     IROHA_HALFWIDTH_JA
2986     NUMBER_UPPER_KO
2987     NUMBER_HANGUL_KO
2988     HANGUL_JAMO_KO
2989     HANGUL_SYLLABLE_KO
2990     HANGUL_CIRCLED_JAMO_KO
2991     HANGUL_CIRCLED_SYLLABLE_KO
2992     CHARS_HEBREW
2993     CHARS_NEPALI
2994     CHARS_KHMER
2995     CHARS_LAO
2996     CHARS_TIBETAN
2997     CHARS_CYRILLIC_UPPER_LETTER_BG
2998     CHARS_CYRILLIC_LOWER_LETTER_BG
2999     CHARS_CYRILLIC_UPPER_LETTER_N_BG
3000     CHARS_CYRILLIC_LOWER_LETTER_N_BG
3001     CHARS_CYRILLIC_UPPER_LETTER_RU
3002     CHARS_CYRILLIC_LOWER_LETTER_RU
3003     CHARS_CYRILLIC_UPPER_LETTER_N_RU
3004     CHARS_CYRILLIC_LOWER_LETTER_N_RU
3005     CHARS_CYRILLIC_UPPER_LETTER_SR
3006     CHARS_CYRILLIC_LOWER_LETTER_SR
3007     CHARS_CYRILLIC_UPPER_LETTER_N_SR
3008     CHARS_CYRILLIC_LOWER_LETTER_N_SR*/
3009 
3010         };
3011         for(const NumberingPairs& rNumberingPair : aNumberingPairs)
3012         {
3013             if( /*sCommand*/sNumber.equalsAscii(rNumberingPair.cWordName ))
3014             {
3015                 nRet = rNumberingPair.nType;
3016                 break;
3017             }
3018         }
3019 
3020     }
3021     return nRet;
3022 }
3023 
3024 
lcl_ParseFormat(const OUString & rCommand)3025 static OUString lcl_ParseFormat( const OUString& rCommand )
3026 {
3027     //  The command looks like: " DATE \@"dd MMMM yyyy" or "09/02/2014"
3028     //  Remove whitespace permitted by standard between \@ and "
3029     OUString command;
3030     sal_Int32 delimPos = rCommand.indexOf("\\@");
3031     if (delimPos != -1)
3032     {
3033         sal_Int32 wsChars = rCommand.indexOf('\"') - delimPos - 2;
3034         command = rCommand.replaceAt(delimPos+2, wsChars, "");
3035     }
3036     else
3037         command = rCommand;
3038 
3039     return msfilter::util::findQuotedText(command, "\\@\"", '\"');
3040 }
3041 /*-------------------------------------------------------------------------
3042 extract a parameter (with or without quotes) between the command and the following backslash
3043   -----------------------------------------------------------------------*/
lcl_ExtractToken(OUString const & rCommand,sal_Int32 & rIndex,bool & rHaveToken,bool & rIsSwitch)3044 static OUString lcl_ExtractToken(OUString const& rCommand,
3045         sal_Int32 & rIndex, bool & rHaveToken, bool & rIsSwitch)
3046 {
3047     rHaveToken = false;
3048     rIsSwitch = false;
3049 
3050     OUStringBuffer token;
3051     bool bQuoted(false);
3052     for (; rIndex < rCommand.getLength(); ++rIndex)
3053     {
3054         sal_Unicode const currentChar(rCommand[rIndex]);
3055         switch (currentChar)
3056         {
3057             case '\\':
3058             {
3059                 if (rIndex == rCommand.getLength() - 1)
3060                 {
3061                     SAL_INFO("writerfilter.dmapper", "field: trailing escape");
3062                     ++rIndex;
3063                     return OUString();
3064                 }
3065                 sal_Unicode const nextChar(rCommand[rIndex+1]);
3066                 if (bQuoted || '\\' == nextChar)
3067                 {
3068                     ++rIndex; // read 2 chars
3069                     token.append(nextChar);
3070                 }
3071                 else // field switch (case insensitive)
3072                 {
3073                     rHaveToken = true;
3074                     if (token.isEmpty())
3075                     {
3076                         rIsSwitch = true;
3077                         rIndex += 2; // read 2 chars
3078                         return rCommand.copy(rIndex - 2, 2).toAsciiUpperCase();
3079                     }
3080                     else
3081                     {   // leave rIndex, read it again next time
3082                         return token.makeStringAndClear();
3083                     }
3084                 }
3085             }
3086             break;
3087             case '\"':
3088                 if (bQuoted || !token.isEmpty())
3089                 {
3090                     rHaveToken = true;
3091                     if (bQuoted)
3092                     {
3093                         ++rIndex;
3094                     }
3095                     return token.makeStringAndClear();
3096                 }
3097                 else
3098                 {
3099                     bQuoted = true;
3100                 }
3101             break;
3102             case ' ':
3103                 if (bQuoted)
3104                 {
3105                     token.append(' ');
3106                 }
3107                 else
3108                 {
3109                     if (!token.isEmpty())
3110                     {
3111                         rHaveToken = true;
3112                         ++rIndex;
3113                         return token.makeStringAndClear();
3114                     }
3115                 }
3116             break;
3117             case '=':
3118                 if (token.isEmpty())
3119                 {
3120                     rHaveToken = true;
3121                     ++rIndex;
3122                     return "FORMULA";
3123                 }
3124             break;
3125             default:
3126                 token.append(currentChar);
3127             break;
3128         }
3129     }
3130     assert(rIndex == rCommand.getLength());
3131     if (bQuoted)
3132     {
3133         // MS Word allows this, so just emit a debug message
3134         SAL_INFO("writerfilter.dmapper",
3135                     "field argument with unterminated quote");
3136     }
3137     rHaveToken = !token.isEmpty();
3138     return token.makeStringAndClear();
3139 }
3140 
splitFieldCommand(const OUString & rCommand)3141 std::tuple<OUString, std::vector<OUString>, std::vector<OUString> > splitFieldCommand(const OUString& rCommand)
3142 {
3143     OUString sType;
3144     std::vector<OUString> arguments;
3145     std::vector<OUString> switches;
3146     sal_Int32 nStartIndex(0);
3147     // tdf#54584: Field may be prepended by a backslash
3148     // This is not an escapement, but already escaped literal "\"
3149     // MS Word allows this, so just skip it
3150     if ((rCommand.getLength() >= nStartIndex + 2) &&
3151         (rCommand[nStartIndex] == L'\\') &&
3152         (rCommand[nStartIndex + 1] != L'\\') &&
3153         (rCommand[nStartIndex + 1] != L' '))
3154     {
3155         ++nStartIndex;
3156     }
3157 
3158     do
3159     {
3160         bool bHaveToken;
3161         bool bIsSwitch;
3162         OUString const token =
3163             lcl_ExtractToken(rCommand, nStartIndex, bHaveToken, bIsSwitch);
3164         assert(nStartIndex <= rCommand.getLength());
3165         if (bHaveToken)
3166         {
3167             if (sType.isEmpty())
3168             {
3169                 sType = token.toAsciiUpperCase();
3170             }
3171             else if (bIsSwitch || !switches.empty())
3172             {
3173                 switches.push_back(token);
3174             }
3175             else
3176             {
3177                 arguments.push_back(token);
3178             }
3179         }
3180     } while (nStartIndex < rCommand.getLength());
3181 
3182     return std::make_tuple(sType, arguments, switches);
3183 }
3184 
lcl_ExctractVariableAndHint(const OUString & rCommand,OUString & rHint)3185 static OUString lcl_ExctractVariableAndHint( const OUString& rCommand, OUString& rHint )
3186 {
3187     // the first word after "ASK " is the variable
3188     // the text after the variable and before a '\' is the hint
3189     // if no hint is set the variable is used as hint
3190     // the quotes of the hint have to be removed
3191     sal_Int32 nIndex = rCommand.indexOf( ' ', 2); //find last space after 'ASK'
3192     if (nIndex == -1)
3193         return OUString();
3194     while(rCommand[nIndex] == ' ')
3195         ++nIndex;
3196     OUString sShortCommand( rCommand.copy( nIndex ) ); //cut off the " ASK "
3197 
3198     sShortCommand = sShortCommand.getToken(0, '\\');
3199     nIndex = 0;
3200     OUString sRet = sShortCommand.getToken( 0, ' ', nIndex);
3201     if( nIndex > 0)
3202         rHint = sShortCommand.copy( nIndex );
3203     if( rHint.isEmpty() )
3204         rHint = sRet;
3205     return sRet;
3206 }
3207 
3208 
lcl_FindInCommand(const OUString & rCommand,sal_Unicode cSwitch,OUString & rValue)3209 static bool lcl_FindInCommand(
3210     const OUString& rCommand,
3211     sal_Unicode cSwitch,
3212     OUString& rValue )
3213 {
3214     bool bRet = false;
3215     OUString sSearch = "\\" + OUStringChar( cSwitch );
3216     sal_Int32 nIndex = rCommand.indexOf( sSearch  );
3217     if( nIndex >= 0 )
3218     {
3219         bRet = true;
3220         //find next '\' or end of string
3221         sal_Int32 nEndIndex = rCommand.indexOf( '\\', nIndex + 1);
3222         if( nEndIndex < 0 )
3223             nEndIndex = rCommand.getLength() ;
3224         if( nEndIndex - nIndex > 3 )
3225             rValue = rCommand.copy( nIndex + 3, nEndIndex - nIndex - 3);
3226     }
3227     return bRet;
3228 }
3229 
3230 
GetCurrentLocale(lang::Locale & rLocale)3231 void DomainMapper_Impl::GetCurrentLocale(lang::Locale& rLocale)
3232 {
3233     PropertyMapPtr pTopContext = GetTopContext();
3234     boost::optional<PropertyMap::Property> pLocale = pTopContext->getProperty(PROP_CHAR_LOCALE);
3235     if( pLocale )
3236         pLocale->second >>= rLocale;
3237     else
3238     {
3239         PropertyMapPtr pParaContext = GetTopContextOfType(CONTEXT_PARAGRAPH);
3240         pLocale = pParaContext->getProperty(PROP_CHAR_LOCALE);
3241         if( pLocale )
3242         {
3243             pLocale->second >>= rLocale;
3244         }
3245     }
3246 }
3247 
3248 /*-------------------------------------------------------------------------
3249     extract the number format from the command and apply the resulting number
3250     format to the XPropertySet
3251   -----------------------------------------------------------------------*/
SetNumberFormat(const OUString & rCommand,uno::Reference<beans::XPropertySet> const & xPropertySet,bool const bDetectFormat)3252 void DomainMapper_Impl::SetNumberFormat( const OUString& rCommand,
3253         uno::Reference< beans::XPropertySet > const& xPropertySet,
3254         bool const bDetectFormat)
3255 {
3256     OUString sFormatString = lcl_ParseFormat( rCommand );
3257     // find \h - hijri/luna calendar todo: what about saka/era calendar?
3258     bool bHijri = 0 < rCommand.indexOf("\\h ");
3259     lang::Locale aUSLocale;
3260     aUSLocale.Language = "en";
3261     aUSLocale.Country = "US";
3262 
3263     //determine current locale - todo: is it necessary to initialize this locale?
3264     lang::Locale aCurrentLocale = aUSLocale;
3265     GetCurrentLocale( aCurrentLocale );
3266     OUString sFormat = ConversionHelper::ConvertMSFormatStringToSO( sFormatString, aCurrentLocale, bHijri);
3267     //get the number formatter and convert the string to a format value
3268     try
3269     {
3270         sal_Int32 nKey = 0;
3271         uno::Reference< util::XNumberFormatsSupplier > xNumberSupplier( m_xTextDocument, uno::UNO_QUERY_THROW );
3272         if( bDetectFormat )
3273         {
3274             uno::Reference< util::XNumberFormatter> xFormatter(util::NumberFormatter::create(m_xComponentContext), uno::UNO_QUERY_THROW);
3275             xFormatter->attachNumberFormatsSupplier( xNumberSupplier );
3276             nKey = xFormatter->detectNumberFormat( 0, rCommand );
3277          }
3278         else
3279         {
3280             nKey = xNumberSupplier->getNumberFormats()->addNewConverted( sFormat, aUSLocale, aCurrentLocale );
3281         }
3282         xPropertySet->setPropertyValue(
3283             getPropertyName(PROP_NUMBER_FORMAT),
3284             uno::makeAny( nKey ));
3285     }
3286     catch(const uno::Exception&)
3287     {
3288     }
3289 }
3290 
lcl_getGrabBagValue(const uno::Sequence<beans::PropertyValue> & grabBag,OUString const & name)3291 static uno::Any lcl_getGrabBagValue( const uno::Sequence<beans::PropertyValue>& grabBag, OUString const & name )
3292 {
3293     auto pProp = std::find_if(grabBag.begin(), grabBag.end(),
3294         [&name](const beans::PropertyValue& rProp) { return rProp.Name == name; });
3295     if (pProp != grabBag.end())
3296         return pProp->Value;
3297     return uno::Any();
3298 }
3299 
3300 //Link the text frames.
ChainTextFrames()3301 void DomainMapper_Impl::ChainTextFrames()
3302 {
3303     //can't link textboxes if there are not even two of them...
3304     if( 2 > m_vTextFramesForChaining.size() )
3305         return ;
3306 
3307     struct TextFramesForChaining {
3308         css::uno::Reference< css::drawing::XShape > xShape;
3309         sal_Int32 nId;
3310         sal_Int32 nSeq;
3311         OUString s_mso_next_textbox;
3312         bool bShapeNameSet;
3313         TextFramesForChaining(): nId(0), nSeq(0), bShapeNameSet(false) {}
3314     } ;
3315     typedef std::map <OUString, TextFramesForChaining> ChainMap;
3316 
3317     try
3318     {
3319         ChainMap aTextFramesForChainingHelper;
3320         OUString sChainNextName("ChainNextName");
3321 
3322         //learn about ALL of the textboxes and their chaining values first - because frames are processed in no specific order.
3323         for( const auto& rTextFrame : m_vTextFramesForChaining )
3324         {
3325             uno::Reference<text::XTextContent>  xTextContent(rTextFrame, uno::UNO_QUERY_THROW);
3326             uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
3327             uno::Reference<beans::XPropertySetInfo> xPropertySetInfo;
3328             if( xPropertySet.is() )
3329                 xPropertySetInfo = xPropertySet->getPropertySetInfo();
3330             uno::Sequence<beans::PropertyValue> aGrabBag;
3331             uno::Reference<lang::XServiceInfo> xServiceInfo(xPropertySet, uno::UNO_QUERY);
3332 
3333             TextFramesForChaining aChainStruct;
3334             OUString sShapeName;
3335             OUString sLinkChainName;
3336 
3337             //The chaining name and the shape name CAN be different in .docx.
3338             //MUST use LinkDisplayName/ChainName as the shape name for establishing links.
3339             if ( xServiceInfo->supportsService("com.sun.star.text.TextFrame") )
3340             {
3341                 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
3342                 xPropertySet->getPropertyValue("LinkDisplayName") >>= sShapeName;
3343             }
3344             else
3345             {
3346                 xPropertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
3347                 xPropertySet->getPropertyValue("ChainName") >>= sShapeName;
3348             }
3349 
3350             lcl_getGrabBagValue( aGrabBag, "Txbx-Id")  >>= aChainStruct.nId;
3351             lcl_getGrabBagValue( aGrabBag, "Txbx-Seq") >>= aChainStruct.nSeq;
3352             lcl_getGrabBagValue( aGrabBag, "LinkChainName") >>= sLinkChainName;
3353             lcl_getGrabBagValue( aGrabBag, "mso-next-textbox") >>= aChainStruct.s_mso_next_textbox;
3354 
3355             //Sometimes the shape names have not been imported.  If not, we may have a fallback name.
3356             //Set name later, only if required for linking.
3357             if( sShapeName.isEmpty() )
3358                 aChainStruct.bShapeNameSet = false;
3359             else
3360             {
3361                 aChainStruct.bShapeNameSet = true;
3362                 sLinkChainName = sShapeName;
3363             }
3364 
3365             if( !sLinkChainName.isEmpty() )
3366             {
3367                 aChainStruct.xShape = rTextFrame;
3368                 aTextFramesForChainingHelper[sLinkChainName] = aChainStruct;
3369             }
3370         }
3371 
3372         //if mso-next-textbox tags are provided, create those vml-style links first. Afterwards we will make dml-style id/seq links.
3373         for (auto& msoItem : aTextFramesForChainingHelper)
3374         {
3375             //if no mso-next-textbox, we are done.
3376             //if it points to itself, we are done.
3377             if( !msoItem.second.s_mso_next_textbox.isEmpty()
3378                 && msoItem.second.s_mso_next_textbox != msoItem.first )
3379             {
3380                 ChainMap::iterator nextFinder=aTextFramesForChainingHelper.find(msoItem.second.s_mso_next_textbox);
3381                 if( nextFinder != aTextFramesForChainingHelper.end() )
3382                 {
3383                     //if the frames have no name yet, then set them.  LinkDisplayName / ChainName are read-only.
3384                     if( !msoItem.second.bShapeNameSet )
3385                     {
3386                         uno::Reference< container::XNamed > xNamed( msoItem.second.xShape, uno::UNO_QUERY );
3387                         if ( xNamed.is() )
3388                         {
3389                             xNamed->setName( msoItem.first );
3390                             msoItem.second.bShapeNameSet = true;
3391                         }
3392                     }
3393                     if( !nextFinder->second.bShapeNameSet )
3394                     {
3395                         uno::Reference< container::XNamed > xNamed( nextFinder->second.xShape, uno::UNO_QUERY );
3396                         if ( xNamed.is() )
3397                         {
3398                             xNamed->setName( nextFinder->first );
3399                             nextFinder->second.bShapeNameSet = true;
3400                         }
3401                     }
3402 
3403                     uno::Reference<text::XTextContent>  xTextContent(msoItem.second.xShape, uno::UNO_QUERY_THROW);
3404                     uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
3405 
3406                     //The reverse chaining happens automatically, so only one direction needs to be set
3407                     xPropertySet->setPropertyValue(sChainNextName, uno::makeAny(nextFinder->first));
3408 
3409                     //the last item in an mso-next-textbox chain is indistinguishable from id/seq items.  Now that it is handled, remove it.
3410                     if( nextFinder->second.s_mso_next_textbox.isEmpty() )
3411                         aTextFramesForChainingHelper.erase(nextFinder->first);
3412                 }
3413             }
3414         }
3415 
3416         //TODO: Perhaps allow reverse sequences when mso-layout-flow-alt = "bottom-to-top"
3417         const sal_Int32 nDirection = 1;
3418 
3419         //Finally - go through and attach the chains based on matching ID and incremented sequence number (dml-style).
3420         for (const auto& rOuter : aTextFramesForChainingHelper)
3421         {
3422             if( rOuter.second.s_mso_next_textbox.isEmpty() )  //non-empty ones already handled earlier - so skipping them now.
3423             {
3424                 for (const auto& rInner : aTextFramesForChainingHelper)
3425                 {
3426                     if ( rInner.second.nId == rOuter.second.nId )
3427                     {
3428                         if ( rInner.second.nSeq == ( rOuter.second.nSeq + nDirection ) )
3429                         {
3430                             uno::Reference<text::XTextContent>  xTextContent(rOuter.second.xShape, uno::UNO_QUERY_THROW);
3431                             uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
3432 
3433                             //The reverse chaining happens automatically, so only one direction needs to be set
3434                             xPropertySet->setPropertyValue(sChainNextName, uno::makeAny(rInner.first));
3435                             break ; //there cannot be more than one next frame
3436                         }
3437                     }
3438                 }
3439             }
3440         }
3441         m_vTextFramesForChaining.clear(); //clear the vector
3442     }
3443     catch (const uno::Exception&)
3444     {
3445         DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
3446     }
3447 }
3448 
FindOrCreateFieldMaster(const sal_Char * pFieldMasterService,const OUString & rFieldMasterName)3449 uno::Reference<beans::XPropertySet> DomainMapper_Impl::FindOrCreateFieldMaster(const sal_Char* pFieldMasterService, const OUString& rFieldMasterName)
3450 {
3451     // query master, create if not available
3452     uno::Reference< text::XTextFieldsSupplier > xFieldsSupplier( GetTextDocument(), uno::UNO_QUERY_THROW );
3453     uno::Reference< container::XNameAccess > xFieldMasterAccess = xFieldsSupplier->getTextFieldMasters();
3454     uno::Reference< beans::XPropertySet > xMaster;
3455     OUString sFieldMasterService( OUString::createFromAscii(pFieldMasterService) );
3456     OUStringBuffer aFieldMasterName;
3457     OUString sDatabaseDataSourceName = GetSettingsTable()->GetCurrentDatabaseDataSource();
3458     bool bIsMergeField = sFieldMasterService.endsWith("Database");
3459     aFieldMasterName.appendAscii( pFieldMasterService );
3460     aFieldMasterName.append('.');
3461     if ( bIsMergeField && !sDatabaseDataSourceName.isEmpty() )
3462     {
3463         aFieldMasterName.append(sDatabaseDataSourceName);
3464         aFieldMasterName.append('.');
3465     }
3466     aFieldMasterName.append(rFieldMasterName);
3467     OUString sFieldMasterName = aFieldMasterName.makeStringAndClear();
3468     if(xFieldMasterAccess->hasByName(sFieldMasterName))
3469     {
3470         //get the master
3471         xMaster.set(xFieldMasterAccess->getByName(sFieldMasterName), uno::UNO_QUERY_THROW);
3472     }
3473     else if( m_xTextFactory.is() )
3474     {
3475         //create the master
3476         xMaster.set( m_xTextFactory->createInstance(sFieldMasterService), uno::UNO_QUERY_THROW);
3477         if ( !bIsMergeField || sDatabaseDataSourceName.isEmpty() )
3478         {
3479             //set the master's name
3480             xMaster->setPropertyValue(
3481                     getPropertyName(PROP_NAME),
3482                     uno::makeAny(rFieldMasterName));
3483         } else {
3484            // set database data, based on the "databasename.tablename" of sDatabaseDataSourceName
3485            xMaster->setPropertyValue(
3486                     getPropertyName(PROP_DATABASE_NAME),
3487                     uno::makeAny(sDatabaseDataSourceName.copy(0, sDatabaseDataSourceName.indexOf('.'))));
3488            xMaster->setPropertyValue(
3489                     getPropertyName(PROP_COMMAND_TYPE),
3490                     uno::makeAny(sal_Int32(0)));
3491            xMaster->setPropertyValue(
3492                     getPropertyName(PROP_DATATABLE_NAME),
3493                     uno::makeAny(sDatabaseDataSourceName.copy(sDatabaseDataSourceName.indexOf('.') + 1)));
3494            xMaster->setPropertyValue(
3495                     getPropertyName(PROP_DATACOLUMN_NAME),
3496                     uno::makeAny(rFieldMasterName));
3497         }
3498     }
3499     return xMaster;
3500 }
3501 
PushFieldContext()3502 void DomainMapper_Impl::PushFieldContext()
3503 {
3504     m_bParaHadField = true;
3505     if(m_bDiscardHeaderFooter)
3506         return;
3507 #ifdef DBG_UTIL
3508     TagLogger::getInstance().element("pushFieldContext");
3509 #endif
3510 
3511     uno::Reference<text::XTextCursor> xCrsr;
3512     if (!m_aTextAppendStack.empty())
3513     {
3514         uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend;
3515         if (xTextAppend.is())
3516             xCrsr = xTextAppend->createTextCursorByRange(
3517                         m_aTextAppendStack.top().xInsertPosition.is()
3518                             ? m_aTextAppendStack.top().xInsertPosition
3519                             : xTextAppend->getEnd());
3520     }
3521 
3522     uno::Reference< text::XTextRange > xStart;
3523     if (xCrsr.is())
3524         xStart = xCrsr->getStart();
3525     m_aFieldStack.push_back(new FieldContext(xStart));
3526 }
3527 /*-------------------------------------------------------------------------
3528 //the current field context waits for the completion of the command
3529   -----------------------------------------------------------------------*/
IsOpenFieldCommand() const3530 bool DomainMapper_Impl::IsOpenFieldCommand() const
3531 {
3532     return !m_aFieldStack.empty() && !m_aFieldStack.back()->IsCommandCompleted();
3533 }
3534 /*-------------------------------------------------------------------------
3535 //the current field context waits for the completion of the command
3536   -----------------------------------------------------------------------*/
IsOpenField() const3537 bool DomainMapper_Impl::IsOpenField() const
3538 {
3539     return !m_aFieldStack.empty();
3540 }
3541 
3542 // Mark top field context as containing a fixed field
SetFieldLocked()3543 void DomainMapper_Impl::SetFieldLocked()
3544 {
3545     if (IsOpenField())
3546         m_aFieldStack.back()->SetFieldLocked();
3547 }
3548 
HeaderFooterContext(bool bTextInserted)3549 HeaderFooterContext::HeaderFooterContext(bool bTextInserted)
3550     : m_bTextInserted(bTextInserted)
3551 {
3552 }
3553 
getTextInserted() const3554 bool HeaderFooterContext::getTextInserted() const
3555 {
3556     return m_bTextInserted;
3557 }
3558 
FieldContext(uno::Reference<text::XTextRange> const & xStart)3559 FieldContext::FieldContext(uno::Reference< text::XTextRange > const& xStart)
3560     : m_bFieldCommandCompleted(false)
3561     , m_xStartRange( xStart )
3562     , m_bFieldLocked( false )
3563 {
3564     m_pProperties = new PropertyMap();
3565 }
3566 
3567 
~FieldContext()3568 FieldContext::~FieldContext()
3569 {
3570 }
3571 
3572 
AppendCommand(const OUString & rPart)3573 void FieldContext::AppendCommand(const OUString& rPart)
3574 {
3575     m_sCommand += rPart;
3576 }
3577 
GetCommandParts() const3578 ::std::vector<OUString> FieldContext::GetCommandParts() const
3579 {
3580     ::std::vector<OUString> aResult;
3581     sal_Int32 nIndex = 0;
3582     bool bInString = false;
3583     OUString sPart;
3584     while (nIndex != -1)
3585     {
3586         OUString sToken = GetCommand().getToken(0, ' ', nIndex);
3587         bool bInStringNext = bInString;
3588 
3589         if (sToken.isEmpty())
3590             continue;
3591 
3592         if (sToken[0] == '"')
3593         {
3594             bInStringNext = true;
3595             sToken = sToken.copy(1);
3596         }
3597         if (sToken.endsWith("\""))
3598         {
3599             bInStringNext = false;
3600             sToken = sToken.copy(0, sToken.getLength() - 1);
3601         }
3602 
3603         if (bInString)
3604         {
3605             sPart += " " + sToken;
3606             if (!bInStringNext)
3607             {
3608                 aResult.push_back(sPart);
3609             }
3610         }
3611         else
3612         {
3613             if (bInStringNext)
3614             {
3615                 sPart = sToken;
3616             }
3617             else
3618             {
3619                 aResult.push_back(sToken);
3620             }
3621         }
3622 
3623         bInString = bInStringNext;
3624     }
3625 
3626     return aResult;
3627 }
3628 
3629 /*-------------------------------------------------------------------------
3630 //collect the pieces of the command
3631   -----------------------------------------------------------------------*/
AppendFieldCommand(OUString const & rPartOfCommand)3632 void DomainMapper_Impl::AppendFieldCommand(OUString const & rPartOfCommand)
3633 {
3634 #ifdef DBG_UTIL
3635     TagLogger::getInstance().startElement("appendFieldCommand");
3636     TagLogger::getInstance().chars(rPartOfCommand);
3637     TagLogger::getInstance().endElement();
3638 #endif
3639 
3640     FieldContextPtr pContext = m_aFieldStack.back();
3641     OSL_ENSURE( pContext.get(), "no field context available");
3642     if( pContext.get() )
3643     {
3644         pContext->AppendCommand( rPartOfCommand );
3645     }
3646 }
3647 
3648 
3649 typedef std::multimap < sal_Int32, OUString > TOCStyleMap;
3650 
3651 
GetWW8FieldId(OUString const & rType)3652 static ww::eField GetWW8FieldId(OUString const& rType)
3653 {
3654     std::unordered_map<OUString, ww::eField> mapID
3655     {
3656         {"ADDRESSBLOCK",    ww::eADDRESSBLOCK},
3657         {"ADVANCE",         ww::eADVANCE},
3658         {"ASK",             ww::eASK},
3659         {"AUTONUM",         ww::eAUTONUM},
3660         {"AUTONUMLGL",      ww::eAUTONUMLGL},
3661         {"AUTONUMOUT",      ww::eAUTONUMOUT},
3662         {"AUTOTEXT",        ww::eAUTOTEXT},
3663         {"AUTOTEXTLIST",    ww::eAUTOTEXTLIST},
3664         {"AUTHOR",          ww::eAUTHOR},
3665         {"BARCODE",         ww::eBARCODE},
3666         {"BIDIOUTLINE",     ww::eBIDIOUTLINE},
3667         {"DATE",            ww::eDATE},
3668         {"COMMENTS",        ww::eCOMMENTS},
3669         {"COMPARE",         ww::eCOMPARE},
3670         {"CONTROL",         ww::eCONTROL},
3671         {"CREATEDATE",      ww::eCREATEDATE},
3672         {"DATABASE",        ww::eDATABASE},
3673         {"DDEAUTOREF",      ww::eDDEAUTOREF},
3674         {"DDEREF",          ww::eDDEREF},
3675         {"DOCPROPERTY",     ww::eDOCPROPERTY},
3676         {"DOCVARIABLE",     ww::eDOCVARIABLE},
3677         {"EDITTIME",        ww::eEDITTIME},
3678         {"EMBED",           ww::eEMBED},
3679         {"EQ",              ww::eEQ},
3680         {"FILLIN",          ww::eFILLIN},
3681         {"FILENAME",        ww::eFILENAME},
3682         {"FILESIZE",        ww::eFILESIZE},
3683         {"FOOTREF",         ww::eFOOTREF},
3684 //        {"FORMULA",         ww::},
3685         {"FORMCHECKBOX",    ww::eFORMCHECKBOX},
3686         {"FORMDROPDOWN",    ww::eFORMDROPDOWN},
3687         {"FORMTEXT",        ww::eFORMTEXT},
3688         {"GLOSSREF",        ww::eGLOSSREF},
3689         {"GOTOBUTTON",      ww::eGOTOBUTTON},
3690         {"GREETINGLINE",    ww::eGREETINGLINE},
3691         {"HTMLCONTROL",     ww::eHTMLCONTROL},
3692         {"HYPERLINK",       ww::eHYPERLINK},
3693         {"IF",              ww::eIF},
3694         {"INFO",            ww::eINFO},
3695         {"INCLUDEPICTURE",  ww::eINCLUDEPICTURE},
3696         {"INCLUDETEXT",     ww::eINCLUDETEXT},
3697         {"INCLUDETIFF",     ww::eINCLUDETIFF},
3698         {"KEYWORDS",        ww::eKEYWORDS},
3699         {"LASTSAVEDBY",     ww::eLASTSAVEDBY},
3700         {"LINK",            ww::eLINK},
3701         {"LISTNUM",         ww::eLISTNUM},
3702         {"MACRO",           ww::eMACRO},
3703         {"MACROBUTTON",     ww::eMACROBUTTON},
3704         {"MERGEDATA",       ww::eMERGEDATA},
3705         {"MERGEFIELD",      ww::eMERGEFIELD},
3706         {"MERGEINC",        ww::eMERGEINC},
3707         {"MERGEREC",        ww::eMERGEREC},
3708         {"MERGESEQ",        ww::eMERGESEQ},
3709         {"NEXT",            ww::eNEXT},
3710         {"NEXTIF",          ww::eNEXTIF},
3711         {"NOTEREF",         ww::eNOTEREF},
3712         {"PAGE",            ww::ePAGE},
3713         {"PAGEREF",         ww::ePAGEREF},
3714         {"PLUGIN",          ww::ePLUGIN},
3715         {"PRINT",           ww::ePRINT},
3716         {"PRINTDATE",       ww::ePRINTDATE},
3717         {"PRIVATE",         ww::ePRIVATE},
3718         {"QUOTE",           ww::eQUOTE},
3719         {"RD",              ww::eRD},
3720         {"REF",             ww::eREF},
3721         {"REVNUM",          ww::eREVNUM},
3722         {"SAVEDATE",        ww::eSAVEDATE},
3723         {"SECTION",         ww::eSECTION},
3724         {"SECTIONPAGES",    ww::eSECTIONPAGES},
3725         {"SEQ",             ww::eSEQ},
3726         {"SET",             ww::eSET},
3727         {"SKIPIF",          ww::eSKIPIF},
3728         {"STYLEREF",        ww::eSTYLEREF},
3729         {"SUBSCRIBER",      ww::eSUBSCRIBER},
3730         {"SUBJECT",         ww::eSUBJECT},
3731         {"SYMBOL",          ww::eSYMBOL},
3732         {"TA",              ww::eTA},
3733         {"TEMPLATE",        ww::eTEMPLATE},
3734         {"TIME",            ww::eTIME},
3735         {"TITLE",           ww::eTITLE},
3736         {"TOA",             ww::eTOA},
3737         {"USERINITIALS",    ww::eUSERINITIALS},
3738         {"USERADDRESS",     ww::eUSERADDRESS},
3739         {"USERNAME",        ww::eUSERNAME},
3740 
3741         {"TOC",             ww::eTOC},
3742         {"TC",              ww::eTC},
3743         {"NUMCHARS",        ww::eNUMCHARS},
3744         {"NUMWORDS",        ww::eNUMWORDS},
3745         {"NUMPAGES",        ww::eNUMPAGES},
3746         {"INDEX",           ww::eINDEX},
3747         {"XE",              ww::eXE},
3748         {"BIBLIOGRAPHY",    ww::eBIBLIOGRAPHY},
3749         {"CITATION",        ww::eCITATION},
3750     };
3751     auto const it = mapID.find(rType);
3752     return (it == mapID.end()) ? ww::eNONE : it->second;
3753 }
3754 
lcl_GetFieldConversion()3755 static const FieldConversionMap_t & lcl_GetFieldConversion()
3756 {
3757     static const FieldConversionMap_t aFieldConversionMap
3758     {
3759 //      {"ADDRESSBLOCK",    {"",                        FIELD_ADDRESSBLOCK  }},
3760 //      {"ADVANCE",         {"",                        FIELD_ADVANCE       }},
3761         {"ASK",             {"SetExpression",           FIELD_ASK           }},
3762         {"AUTONUM",         {"SetExpression",           FIELD_AUTONUM       }},
3763         {"AUTONUMLGL",      {"SetExpression",           FIELD_AUTONUMLGL    }},
3764         {"AUTONUMOUT",      {"SetExpression",           FIELD_AUTONUMOUT    }},
3765         {"AUTHOR",          {"DocInfo.CreateAuthor",    FIELD_AUTHOR        }},
3766         {"DATE",            {"DateTime",                FIELD_DATE          }},
3767         {"COMMENTS",        {"DocInfo.Description",     FIELD_COMMENTS      }},
3768         {"CREATEDATE",      {"DocInfo.CreateDateTime",  FIELD_CREATEDATE    }},
3769         {"DOCPROPERTY",     {"",                        FIELD_DOCPROPERTY   }},
3770         {"DOCVARIABLE",     {"User",                    FIELD_DOCVARIABLE   }},
3771         {"EDITTIME",        {"DocInfo.EditTime",        FIELD_EDITTIME      }},
3772         {"EQ",              {"",                        FIELD_EQ            }},
3773         {"FILLIN",          {"Input",                   FIELD_FILLIN        }},
3774         {"FILENAME",        {"FileName",                FIELD_FILENAME      }},
3775 //      {"FILESIZE",        {"",                        FIELD_FILESIZE      }},
3776         {"FORMULA",         {"TableFormula",            FIELD_FORMULA       }},
3777         {"FORMCHECKBOX",    {"",                        FIELD_FORMCHECKBOX  }},
3778         {"FORMDROPDOWN",    {"DropDown",                FIELD_FORMDROPDOWN  }},
3779         {"FORMTEXT",        {"Input",                   FIELD_FORMTEXT      }},
3780         {"GOTOBUTTON",      {"",                        FIELD_GOTOBUTTON    }},
3781         {"HYPERLINK",       {"",                        FIELD_HYPERLINK     }},
3782         {"IF",              {"ConditionalText",         FIELD_IF            }},
3783 //      {"INFO",            {"",                        FIELD_INFO          }},
3784         {"INCLUDEPICTURE",  {"",                        FIELD_INCLUDEPICTURE}},
3785         {"KEYWORDS",        {"DocInfo.KeyWords",        FIELD_KEYWORDS      }},
3786         {"LASTSAVEDBY",     {"DocInfo.ChangeAuthor",    FIELD_LASTSAVEDBY   }},
3787         {"MACROBUTTON",     {"Macro",                   FIELD_MACROBUTTON   }},
3788         {"MERGEFIELD",      {"Database",                FIELD_MERGEFIELD    }},
3789         {"MERGEREC",        {"DatabaseNumberOfSet",     FIELD_MERGEREC      }},
3790 //      {"MERGESEQ",        {"",                        FIELD_MERGESEQ      }},
3791         {"NEXT",            {"DatabaseNextSet",         FIELD_NEXT          }},
3792         {"NEXTIF",          {"DatabaseNextSet",         FIELD_NEXTIF        }},
3793         {"PAGE",            {"PageNumber",              FIELD_PAGE          }},
3794         {"PAGEREF",         {"GetReference",            FIELD_PAGEREF       }},
3795         {"REF",             {"GetReference",            FIELD_REF           }},
3796         {"REVNUM",          {"DocInfo.Revision",        FIELD_REVNUM        }},
3797         {"SAVEDATE",        {"DocInfo.Change",          FIELD_SAVEDATE      }},
3798 //      {"SECTION",         {"",                        FIELD_SECTION       }},
3799 //      {"SECTIONPAGES",    {"",                        FIELD_SECTIONPAGES  }},
3800         {"SEQ",             {"SetExpression",           FIELD_SEQ           }},
3801         {"SET",             {"SetExpression",           FIELD_SET           }},
3802 //      {"SKIPIF",          {"",                        FIELD_SKIPIF        }},
3803 //      {"STYLEREF",        {"",                        FIELD_STYLEREF      }},
3804         {"SUBJECT",         {"DocInfo.Subject",         FIELD_SUBJECT       }},
3805         {"SYMBOL",          {"",                        FIELD_SYMBOL        }},
3806         {"TEMPLATE",        {"TemplateName",            FIELD_TEMPLATE      }},
3807         {"TIME",            {"DateTime",                FIELD_TIME          }},
3808         {"TITLE",           {"DocInfo.Title",           FIELD_TITLE         }},
3809         {"USERINITIALS",    {"Author",                  FIELD_USERINITIALS  }},
3810 //      {"USERADDRESS",     {"",                        FIELD_USERADDRESS   }},
3811         {"USERNAME",        {"Author",                  FIELD_USERNAME      }},
3812 
3813 
3814         {"TOC",         {"com.sun.star.text.ContentIndex",          FIELD_TOC           }},
3815         {"TC",          {"com.sun.star.text.ContentIndexMark",      FIELD_TC            }},
3816         {"NUMCHARS",    {"CharacterCount",                          FIELD_NUMCHARS      }},
3817         {"NUMWORDS",    {"WordCount",                               FIELD_NUMWORDS      }},
3818         {"NUMPAGES",    {"PageCount",                               FIELD_NUMPAGES      }},
3819         {"INDEX",       {"com.sun.star.text.DocumentIndex",         FIELD_INDEX         }},
3820         {"XE",          {"com.sun.star.text.DocumentIndexMark",     FIELD_XE            }},
3821         {"BIBLIOGRAPHY",{"com.sun.star.text.Bibliography",          FIELD_BIBLIOGRAPHY  }},
3822         {"CITATION",    {"com.sun.star.text.TextField.Bibliography",FIELD_CITATION      }},
3823     };
3824 
3825     return aFieldConversionMap;
3826 }
3827 
lcl_GetEnhancedFieldConversion()3828 static const FieldConversionMap_t & lcl_GetEnhancedFieldConversion()
3829 {
3830     static const FieldConversionMap_t aEnhancedFieldConversionMap =
3831     {
3832         {"FORMCHECKBOX", {"FormFieldmark", FIELD_FORMCHECKBOX}},
3833         {"FORMDROPDOWN", {"FormFieldmark", FIELD_FORMDROPDOWN}},
3834         {"FORMTEXT",     {"Fieldmark",     FIELD_FORMTEXT}},
3835     };
3836 
3837     return aEnhancedFieldConversionMap;
3838 }
3839 
handleFieldSet(const FieldContextPtr & pContext,uno::Reference<uno::XInterface> const & xFieldInterface,uno::Reference<beans::XPropertySet> const & xFieldProperties)3840 void DomainMapper_Impl::handleFieldSet
3841     (const FieldContextPtr& pContext,
3842      uno::Reference< uno::XInterface > const & xFieldInterface,
3843      uno::Reference< beans::XPropertySet > const& xFieldProperties)
3844 {
3845     OUString sVariable, sHint;
3846 
3847     sVariable = lcl_ExctractVariableAndHint(pContext->GetCommand(), sHint);
3848 
3849     // remove surrounding "" if exists
3850     if(sHint.getLength() >= 2)
3851     {
3852         OUString sTmp = sHint.trim();
3853         if (sTmp.startsWith("\"") && sTmp.endsWith("\""))
3854         {
3855             sHint = sTmp.copy(1, sTmp.getLength() - 2);
3856         }
3857     }
3858 
3859     // determine field master name
3860     uno::Reference< beans::XPropertySet > xMaster =
3861         FindOrCreateFieldMaster
3862         ("com.sun.star.text.FieldMaster.SetExpression", sVariable );
3863 
3864     // a set field is a string
3865     xMaster->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::makeAny(text::SetVariableType::STRING));
3866 
3867     // attach the master to the field
3868     uno::Reference< text::XDependentTextField > xDependentField
3869         ( xFieldInterface, uno::UNO_QUERY_THROW );
3870     xDependentField->attachTextFieldMaster( xMaster );
3871 
3872     xFieldProperties->setPropertyValue(getPropertyName(PROP_HINT), uno::makeAny( sHint ));
3873     xFieldProperties->setPropertyValue(getPropertyName(PROP_CONTENT), uno::makeAny( sHint ));
3874     xFieldProperties->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::makeAny(text::SetVariableType::STRING));
3875 
3876     // Mimic MS Word behavior (hide the SET)
3877     xFieldProperties->setPropertyValue(getPropertyName(PROP_IS_VISIBLE), uno::makeAny(false));
3878 }
3879 
handleFieldAsk(const FieldContextPtr & pContext,uno::Reference<uno::XInterface> & xFieldInterface,uno::Reference<beans::XPropertySet> const & xFieldProperties)3880 void DomainMapper_Impl::handleFieldAsk
3881     (const FieldContextPtr& pContext,
3882      uno::Reference< uno::XInterface > & xFieldInterface,
3883      uno::Reference< beans::XPropertySet > const& xFieldProperties)
3884 {
3885     //doesn the command contain a variable name?
3886     OUString sVariable, sHint;
3887 
3888     sVariable = lcl_ExctractVariableAndHint( pContext->GetCommand(),
3889         sHint );
3890     if(!sVariable.isEmpty())
3891     {
3892         // determine field master name
3893         uno::Reference< beans::XPropertySet > xMaster =
3894             FindOrCreateFieldMaster
3895             ("com.sun.star.text.FieldMaster.SetExpression", sVariable );
3896         // An ASK field is always a string of characters
3897         xMaster->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::makeAny(text::SetVariableType::STRING));
3898 
3899         // attach the master to the field
3900         uno::Reference< text::XDependentTextField > xDependentField
3901             ( xFieldInterface, uno::UNO_QUERY_THROW );
3902         xDependentField->attachTextFieldMaster( xMaster );
3903 
3904         // set input flag at the field
3905         xFieldProperties->setPropertyValue(
3906             getPropertyName(PROP_IS_INPUT), uno::makeAny( true ));
3907         // set the prompt
3908         xFieldProperties->setPropertyValue(
3909             getPropertyName(PROP_HINT),
3910             uno::makeAny( sHint ));
3911         xFieldProperties->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::makeAny(text::SetVariableType::STRING));
3912         // The ASK has no field value to display
3913         xFieldProperties->setPropertyValue(getPropertyName(PROP_IS_VISIBLE), uno::makeAny(false));
3914     }
3915     else
3916     {
3917         //don't insert the field
3918         //todo: maybe import a 'normal' input field here?
3919         xFieldInterface = nullptr;
3920     }
3921 }
3922 
handleFieldFormula(const FieldContextPtr & pContext,uno::Reference<beans::XPropertySet> const & xFieldProperties)3923 void DomainMapper_Impl::handleFieldFormula
3924     (const FieldContextPtr& pContext,
3925      uno::Reference< beans::XPropertySet > const& xFieldProperties)
3926 {
3927     OUString command = pContext->GetCommand().trim();
3928 
3929     //  Remove number formatting from \# to end of command
3930     //  TODO: handle custom number formatting
3931     sal_Int32 delimPos = command.indexOf("\\#");
3932     if (delimPos != -1)
3933     {
3934         command = command.replaceAt(delimPos, command.getLength() - delimPos, "").trim();
3935     }
3936 
3937     // command must contains = and at least another char
3938     if (command.getLength() < 2)
3939         return;
3940 
3941     // we don't copy the = symbol from the command
3942     OUString formula = command.copy(1);
3943 
3944     xFieldProperties->setPropertyValue(getPropertyName(PROP_CONTENT), uno::makeAny(formula));
3945     xFieldProperties->setPropertyValue(getPropertyName(PROP_NUMBER_FORMAT), uno::makeAny(sal_Int32(0)));
3946     xFieldProperties->setPropertyValue("IsShowFormula", uno::makeAny(false));
3947 }
3948 
handleRubyEQField(const FieldContextPtr & pContext)3949 void  DomainMapper_Impl::handleRubyEQField( const FieldContextPtr& pContext)
3950 {
3951     const OUString & rCommand(pContext->GetCommand());
3952     sal_Int32 nIndex = 0, nEnd = 0;
3953     RubyInfo aInfo ;
3954     nIndex = rCommand.indexOf("\\* jc" );
3955     if (nIndex != -1)
3956     {
3957         nIndex += 5;
3958         sal_uInt32  nJc = rCommand.getToken(0, ' ',nIndex).toInt32();
3959         const   sal_Int32   aRubyAlignValues[] =
3960         {
3961             NS_ooxml::LN_Value_ST_RubyAlign_center,
3962             NS_ooxml::LN_Value_ST_RubyAlign_distributeLetter,
3963             NS_ooxml::LN_Value_ST_RubyAlign_distributeSpace,
3964             NS_ooxml::LN_Value_ST_RubyAlign_left,
3965             NS_ooxml::LN_Value_ST_RubyAlign_right,
3966             NS_ooxml::LN_Value_ST_RubyAlign_rightVertical,
3967         };
3968         aInfo.nRubyAlign = aRubyAlignValues[(nJc<SAL_N_ELEMENTS(aRubyAlignValues))?nJc:0];
3969     }
3970 
3971     // we don't parse or use the font field in rCommand
3972 
3973     nIndex = rCommand.indexOf("\\* hps" );
3974     if (nIndex != -1)
3975     {
3976         nIndex += 6;
3977         aInfo.nHps = rCommand.getToken(0, ' ',nIndex).toInt32();
3978     }
3979 
3980     nIndex = rCommand.indexOf("\\o");
3981     if (nIndex == -1 || (nIndex = rCommand.indexOf('(', nIndex)) == -1 || (nEnd = rCommand.lastIndexOf(')'))==-1 || nEnd <= nIndex)
3982         return;
3983 
3984     OUString sRubyParts = rCommand.copy(nIndex+1,nEnd-nIndex-1);
3985     nIndex = 0;
3986     OUString sPart1 = sRubyParts.getToken(0, ',', nIndex);
3987     OUString sPart2 = sRubyParts.getToken(0, ',', nIndex);
3988     if ((nIndex = sPart1.indexOf('(')) != -1 && (nEnd = sPart1.lastIndexOf(')'))!=-1  && nEnd > nIndex)
3989     {
3990         aInfo.sRubyText = sPart1.copy(nIndex+1,nEnd-nIndex-1);
3991     }
3992 
3993     PropertyMapPtr pRubyContext(new PropertyMap());
3994     pRubyContext->InsertProps(GetTopContext());
3995     if (aInfo.nHps > 0)
3996     {
3997         double fVal = double(aInfo.nHps) / 2.;
3998         uno::Any aVal = uno::makeAny( fVal );
3999 
4000         pRubyContext->Insert(PROP_CHAR_HEIGHT, aVal);
4001         pRubyContext->Insert(PROP_CHAR_HEIGHT_ASIAN, aVal);
4002     }
4003     PropertyValueVector_t aProps = comphelper::sequenceToContainer< PropertyValueVector_t >(pRubyContext->GetPropertyValues());
4004     aInfo.sRubyStyle = m_rDMapper.getOrCreateCharStyle(aProps, /*bAlwaysCreate=*/false);
4005     PropertyMapPtr pCharContext(new PropertyMap());
4006     if (m_pLastCharacterContext.get())
4007         pCharContext->InsertProps(m_pLastCharacterContext);
4008     pCharContext->InsertProps(pContext->getProperties());
4009     pCharContext->Insert(PROP_RUBY_TEXT, uno::makeAny( aInfo.sRubyText ) );
4010     pCharContext->Insert(PROP_RUBY_ADJUST, uno::makeAny(static_cast<sal_Int16>(ConversionHelper::convertRubyAlign(aInfo.nRubyAlign))));
4011     if ( aInfo.nRubyAlign == NS_ooxml::LN_Value_ST_RubyAlign_rightVertical )
4012         pCharContext->Insert(PROP_RUBY_POSITION, uno::makeAny(css::text::RubyPosition::INTER_CHARACTER));
4013     pCharContext->Insert(PROP_RUBY_STYLE, uno::makeAny(aInfo.sRubyStyle));
4014     appendTextPortion(sPart2, pCharContext);
4015 
4016 }
4017 
handleAutoNum(const FieldContextPtr & pContext,uno::Reference<uno::XInterface> const & xFieldInterface,uno::Reference<beans::XPropertySet> const & xFieldProperties)4018 void DomainMapper_Impl::handleAutoNum
4019     (const FieldContextPtr& pContext,
4020     uno::Reference< uno::XInterface > const & xFieldInterface,
4021     uno::Reference< beans::XPropertySet > const& xFieldProperties)
4022 {
4023     //create a sequence field master "AutoNr"
4024     uno::Reference< beans::XPropertySet > xMaster =
4025     FindOrCreateFieldMaster
4026         ("com.sun.star.text.FieldMaster.SetExpression",
4027         "AutoNr");
4028 
4029     xMaster->setPropertyValue( getPropertyName(PROP_SUB_TYPE),
4030         uno::makeAny(text::SetVariableType::SEQUENCE));
4031 
4032     //apply the numbering type
4033     xFieldProperties->setPropertyValue(
4034         getPropertyName(PROP_NUMBERING_TYPE),
4035         uno::makeAny( lcl_ParseNumberingType(pContext->GetCommand()) ));
4036         // attach the master to the field
4037     uno::Reference< text::XDependentTextField > xDependentField
4038         ( xFieldInterface, uno::UNO_QUERY_THROW );
4039     xDependentField->attachTextFieldMaster( xMaster );
4040 }
4041 
handleAuthor(OUString const & rFirstParam,uno::Reference<beans::XPropertySet> const & xFieldProperties,FieldId eFieldId)4042 void DomainMapper_Impl::handleAuthor
4043     (OUString const& rFirstParam,
4044      uno::Reference< beans::XPropertySet > const& xFieldProperties,
4045      FieldId  eFieldId )
4046 {
4047     if ( eFieldId != FIELD_USERINITIALS )
4048         xFieldProperties->setPropertyValue
4049             ( getPropertyName(PROP_FULL_NAME), uno::makeAny( true ));
4050 
4051     if (!rFirstParam.isEmpty())
4052     {
4053         xFieldProperties->setPropertyValue(
4054                 getPropertyName( PROP_IS_FIXED ),
4055                 uno::makeAny( true ));
4056         //PROP_CURRENT_PRESENTATION is set later anyway
4057     }
4058 }
4059 
handleDocProperty(const FieldContextPtr & pContext,OUString const & rFirstParam,uno::Reference<uno::XInterface> & xFieldInterface)4060     void DomainMapper_Impl::handleDocProperty
4061         (const FieldContextPtr& pContext,
4062         OUString const& rFirstParam,
4063         uno::Reference< uno::XInterface > & xFieldInterface)
4064 {
4065     //some docproperties should be imported as document statistic fields, some as DocInfo fields
4066     //others should be user fields
4067     if (rFirstParam.isEmpty())
4068         return;
4069 
4070     #define SET_ARABIC      0x01
4071     #define SET_DATE        0x04
4072     struct DocPropertyMap
4073     {
4074         const sal_Char* pDocPropertyName;
4075         const sal_Char* pServiceName;
4076         sal_uInt8 const nFlags;
4077     };
4078     static const DocPropertyMap aDocProperties[] =
4079     {
4080         {"CreateTime",       "DocInfo.CreateDateTime",  SET_DATE},
4081         {"Characters",       "CharacterCount",          SET_ARABIC},
4082         {"Comments",         "DocInfo.Description",     0},
4083         {"Keywords",         "DocInfo.KeyWords",        0},
4084         {"LastPrinted",      "DocInfo.PrintDateTime",   0},
4085         {"LastSavedBy",      "DocInfo.ChangeAuthor",    0},
4086         {"LastSavedTime",    "DocInfo.ChangeDateTime",  SET_DATE},
4087         {"Paragraphs",       "ParagraphCount",          SET_ARABIC},
4088         {"RevisionNumber",   "DocInfo.Revision",        0},
4089         {"Subject",          "DocInfo.Subject",         0},
4090         {"Template",         "TemplateName",            0},
4091         {"Title",            "DocInfo.Title",           0},
4092         {"TotalEditingTime", "DocInfo.EditTime",        0},
4093         {"Words",            "WordCount",               SET_ARABIC}
4094 
4095         //other available DocProperties:
4096         //Bytes, Category, CharactersWithSpaces, Company
4097         //HyperlinkBase,
4098         //Lines, Manager, NameofApplication, ODMADocId, Pages,
4099         //Security,
4100     };
4101     uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier(m_xTextDocument, uno::UNO_QUERY);
4102     uno::Reference<document::XDocumentProperties> xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties();
4103     uno::Reference<beans::XPropertySet>  xUserDefinedProps(xDocumentProperties->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
4104     uno::Reference<beans::XPropertySetInfo> xPropertySetInfo =  xUserDefinedProps->getPropertySetInfo();
4105     //search for a field mapping
4106     OUString sFieldServiceName;
4107     size_t nMap = 0;
4108     for( ; nMap < SAL_N_ELEMENTS(aDocProperties); ++nMap )
4109     {
4110         if ((rFirstParam.equalsAscii(aDocProperties[nMap].pDocPropertyName)) && (!xPropertySetInfo->hasPropertyByName(rFirstParam)))
4111         {
4112             sFieldServiceName =
4113             OUString::createFromAscii
4114             (aDocProperties[nMap].pServiceName);
4115             break;
4116         }
4117     }
4118     OUString sServiceName("com.sun.star.text.TextField.");
4119     bool bIsCustomField = false;
4120     if(sFieldServiceName.isEmpty())
4121     {
4122         //create a custom property field
4123         sServiceName += "DocInfo.Custom";
4124         bIsCustomField = true;
4125     }
4126     else
4127     {
4128         sServiceName += sFieldServiceName;
4129     }
4130     if (m_xTextFactory.is())
4131         xFieldInterface = m_xTextFactory->createInstance(sServiceName);
4132     uno::Reference<beans::XPropertySet> xFieldProperties( xFieldInterface, uno::UNO_QUERY_THROW);
4133     if( bIsCustomField )
4134     {
4135         xFieldProperties->setPropertyValue(
4136             getPropertyName(PROP_NAME), uno::makeAny(rFirstParam));
4137         pContext->SetCustomField( xFieldProperties );
4138     }
4139     else
4140     {
4141         if(0 != (aDocProperties[nMap].nFlags & SET_ARABIC))
4142             xFieldProperties->setPropertyValue(
4143                 getPropertyName(PROP_NUMBERING_TYPE),
4144                 uno::makeAny( style::NumberingType::ARABIC ));
4145         else if(0 != (aDocProperties[nMap].nFlags & SET_DATE))
4146         {
4147             xFieldProperties->setPropertyValue(
4148                 getPropertyName(PROP_IS_DATE),
4149                     uno::makeAny( true ));
4150             SetNumberFormat( pContext->GetCommand(), xFieldProperties );
4151         }
4152     }
4153 
4154 
4155 #undef SET_ARABIC
4156 #undef SET_DATE
4157 }
4158 
lcl_createTOXLevelHyperlinks(bool bHyperlinks,const OUString & sChapterNoSeparator,const uno::Sequence<beans::PropertyValues> & aLevel)4159 static uno::Sequence< beans::PropertyValues > lcl_createTOXLevelHyperlinks( bool bHyperlinks, const OUString& sChapterNoSeparator,
4160                                    const uno::Sequence< beans::PropertyValues >& aLevel )
4161 {
4162     //create a copy of the level and add two new entries - hyperlink start and end
4163     bool bChapterNoSeparator  = !sChapterNoSeparator.isEmpty();
4164     sal_Int32 nAdd = (bHyperlinks && bChapterNoSeparator) ? 4 : 2;
4165     uno::Sequence< beans::PropertyValues > aNewLevel( aLevel.getLength() + nAdd);
4166     beans::PropertyValues* pNewLevel = aNewLevel.getArray();
4167     if( bHyperlinks )
4168     {
4169         beans::PropertyValues aHyperlink(1);
4170         aHyperlink[0].Name = getPropertyName( PROP_TOKEN_TYPE );
4171         aHyperlink[0].Value <<= getPropertyName( PROP_TOKEN_HYPERLINK_START );
4172         pNewLevel[0] = aHyperlink;
4173         aHyperlink[0].Value <<= getPropertyName( PROP_TOKEN_HYPERLINK_END );
4174         pNewLevel[aNewLevel.getLength() -1] = aHyperlink;
4175     }
4176     if( bChapterNoSeparator )
4177     {
4178         beans::PropertyValues aChapterNo(2);
4179         aChapterNo[0].Name = getPropertyName( PROP_TOKEN_TYPE );
4180         aChapterNo[0].Value <<= getPropertyName( PROP_TOKEN_CHAPTER_INFO );
4181         aChapterNo[1].Name = getPropertyName( PROP_CHAPTER_FORMAT );
4182         //todo: is ChapterFormat::Number correct?
4183         aChapterNo[1].Value <<= sal_Int16(text::ChapterFormat::NUMBER);
4184         pNewLevel[aNewLevel.getLength() - (bHyperlinks ? 4 : 2) ] = aChapterNo;
4185 
4186         beans::PropertyValues aChapterSeparator(2);
4187         aChapterSeparator[0].Name = getPropertyName( PROP_TOKEN_TYPE );
4188         aChapterSeparator[0].Value <<= getPropertyName( PROP_TOKEN_TEXT );
4189         aChapterSeparator[1].Name = getPropertyName( PROP_TEXT );
4190         aChapterSeparator[1].Value <<= sChapterNoSeparator;
4191         pNewLevel[aNewLevel.getLength() - (bHyperlinks ? 3 : 1)] = aChapterSeparator;
4192     }
4193     //copy the 'old' entries except the last (page no)
4194     std::copy(aLevel.begin(), std::prev(aLevel.end()), std::next(aNewLevel.begin()));
4195     //copy page no entry (last or last but one depending on bHyperlinks
4196     sal_Int32 nPageNo = aNewLevel.getLength() - (bHyperlinks ? 2 : 3);
4197     pNewLevel[nPageNo] = aLevel[aLevel.getLength() - 1];
4198 
4199     return aNewLevel;
4200 }
4201 
4202 /// Returns title of the TOC placed in paragraph(s) before TOC field inside STD-frame
extractTocTitle()4203 OUString DomainMapper_Impl::extractTocTitle()
4204 {
4205     if (!m_xStdEntryStart.is())
4206         return OUString();
4207 
4208     uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
4209     if(!xTextAppend.is())
4210         return OUString();
4211 
4212     // try-catch was added in the same way as inside appendTextSectionAfter()
4213     try
4214     {
4215         uno::Reference< text::XParagraphCursor > xCursor( xTextAppend->createTextCursorByRange( m_xStdEntryStart ), uno::UNO_QUERY_THROW);
4216         if (!xCursor.is())
4217             return OUString();
4218 
4219         //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
4220         xCursor->gotoStartOfParagraph( false );
4221         if (m_aTextAppendStack.top().xInsertPosition.is())
4222             xCursor->gotoRange( m_aTextAppendStack.top().xInsertPosition, true );
4223         else
4224             xCursor->gotoEnd( true );
4225 
4226         // the paragraph after this new section might have been already inserted
4227         OUString sResult = xCursor->getString();
4228         if (sResult.endsWith(SAL_NEWLINE_STRING))
4229             sResult = sResult.copy(0, sResult.getLength() - SAL_N_ELEMENTS(SAL_NEWLINE_STRING) + 1);
4230 
4231         return sResult;
4232     }
4233     catch(const uno::Exception&)
4234     {
4235     }
4236 
4237     return OUString();
4238 }
4239 
4240 css::uno::Reference<css::beans::XPropertySet>
StartIndexSectionChecked(const OUString & sServiceName)4241 DomainMapper_Impl::StartIndexSectionChecked(const OUString& sServiceName)
4242 {
4243     if (m_bParaChanged)
4244     {
4245         finishParagraph(GetTopContextOfType(CONTEXT_PARAGRAPH), false); // resets m_bParaChanged
4246         PopProperties(CONTEXT_PARAGRAPH);
4247         PushProperties(CONTEXT_PARAGRAPH);
4248         SetIsFirstRun(true);
4249         // The first paragraph of the index that is continuation of just finished one needs to be
4250         // removed when finished (unless more content will arrive, which will set m_bParaChanged)
4251         m_bRemoveThisParagraph = true;
4252     }
4253     const auto& xTextAppend = GetTopTextAppend();
4254     const auto xTextRange = xTextAppend->getEnd();
4255     const auto xRet = createSectionForRange(xTextRange, xTextRange, sServiceName, false);
4256     if (!m_aTextAppendStack.top().xInsertPosition)
4257     {
4258         try
4259         {
4260             m_bStartedTOC = true;
4261             uno::Reference<text::XTextCursor> xTOCTextCursor
4262                 = xTextRange->getText()->createTextCursor();
4263             assert(xTOCTextCursor.is());
4264             xTOCTextCursor->gotoEnd(false);
4265             mxTOCTextCursor = xTOCTextCursor;
4266             m_aTextAppendStack.push(TextAppendContext(xTextAppend, xTOCTextCursor));
4267         }
4268         catch (const uno::Exception&)
4269         {
4270             TOOLS_WARN_EXCEPTION("writerfilter.dmapper",
4271                                  "DomainMapper_Impl::StartIndexSectionChecked:");
4272         }
4273     }
4274     return xRet;
4275 }
4276 
handleToc(const FieldContextPtr & pContext,const OUString & sTOCServiceName)4277 void DomainMapper_Impl::handleToc
4278     (const FieldContextPtr& pContext,
4279     const OUString & sTOCServiceName)
4280 {
4281     OUString sValue;
4282     if (IsInHeaderFooter())
4283         m_bStartTOCHeaderFooter = true;
4284     bool bTableOfFigures = false;
4285     bool bHyperlinks = false;
4286     bool bFromOutline = false;
4287     bool bFromEntries = false;
4288     bool bHideTabLeaderPageNumbers = false ;
4289     bool bIsTabEntry = false ;
4290     bool bNewLine = false ;
4291     bool bParagraphOutlineLevel = false;
4292 
4293     sal_Int16 nMaxLevel = 10;
4294     OUString sTemplate;
4295     OUString sChapterNoSeparator;
4296     OUString sFigureSequence;
4297     uno::Reference< beans::XPropertySet > xTOC;
4298     OUString aBookmarkName;
4299 
4300 //                  \a Builds a table of figures but does not include the captions's label and number
4301     if( lcl_FindInCommand( pContext->GetCommand(), 'a', sValue ))
4302     { //make it a table of figures
4303         bTableOfFigures = true;
4304     }
4305 //                  \b Uses a bookmark to specify area of document from which to build table of contents
4306     if( lcl_FindInCommand( pContext->GetCommand(), 'b', sValue ))
4307     {
4308         aBookmarkName = sValue.trim().replaceAll("\"","");
4309     }
4310     if( lcl_FindInCommand( pContext->GetCommand(), 'c', sValue ))
4311 //                  \c Builds a table of figures of the given label
4312     {
4313                         //todo: sValue contains the label's name
4314         bTableOfFigures = true;
4315         sFigureSequence = sValue.trim();
4316         sFigureSequence = sFigureSequence.replaceAll("\"", "").replaceAll("'","");
4317     }
4318 //                  \d Defines the separator between sequence and page numbers
4319     if( lcl_FindInCommand( pContext->GetCommand(), 'd', sValue ))
4320     {
4321                         //todo: insert the chapter number into each level and insert the separator additionally
4322         sChapterNoSeparator = sValue;
4323     }
4324 //                  \f Builds a table of contents using TC entries instead of outline levels
4325     if( lcl_FindInCommand( pContext->GetCommand(), 'f', sValue ))
4326     {
4327                         //todo: sValue can contain a TOC entry identifier - use unclear
4328         bFromEntries = true;
4329     }
4330 //                  \h Hyperlinks the entries and page numbers within the table of contents
4331     if( lcl_FindInCommand( pContext->GetCommand(), 'h', sValue ))
4332     {
4333                         //todo: make all entries to hyperlinks
4334         bHyperlinks = true;
4335     }
4336 //                  \l Defines the TC entries field level used to build a table of contents
4337 //                    if( lcl_FindInCommand( pContext->GetCommand(), 'l', sValue ))
4338 //                    {
4339                             //todo: entries can only be included completely
4340 //                    }
4341 //                  \n Builds a table of contents or a range of entries, such as 1-9 in a table of contents without page numbers
4342 //                    if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
4343 //                    {
4344                         //todo: what does the description mean?
4345 //                    }
4346 //                  \o  Builds a table of contents by using outline levels instead of TC entries
4347     if( lcl_FindInCommand( pContext->GetCommand(), 'o', sValue ))
4348     {
4349         bFromOutline = true;
4350         if (sValue.isEmpty())
4351             nMaxLevel = WW_OUTLINE_MAX;
4352         else
4353         {
4354             sal_Int32 nIndex = 0;
4355             sValue.getToken( 0, '-', nIndex );
4356             nMaxLevel = static_cast<sal_Int16>(nIndex != -1 ? sValue.copy(nIndex).toInt32() : 0);
4357         }
4358     }
4359 //                  \p Defines the separator between the table entry and its page number
4360 //                  \s  Builds a table of contents by using a sequence type
4361 //                  \t  Builds a table of contents by using style names other than the standard outline styles
4362     if( lcl_FindInCommand( pContext->GetCommand(), 't', sValue ))
4363     {
4364         OUString sToken = sValue.getToken(1, '"');
4365         sTemplate = sToken.isEmpty() ? sValue : sToken;
4366     }
4367 //                  \u  Builds a table of contents by using the applied paragraph outline level
4368     if( lcl_FindInCommand( pContext->GetCommand(), 'u', sValue ))
4369     {
4370         bFromOutline = true;
4371         bParagraphOutlineLevel = true;
4372                         //todo: what doesn 'the applied paragraph outline level' refer to?
4373     }
4374 //    \w Preserve tab characters within table entries
4375     if( lcl_FindInCommand( pContext->GetCommand(), 'w', sValue ))
4376     {
4377         bIsTabEntry = true ;
4378     }
4379 //                  \x Preserve newline characters within table entries
4380     if( lcl_FindInCommand( pContext->GetCommand(), 'x', sValue ))
4381     {
4382         bNewLine = true ;
4383     }
4384 //                  \z Hides page numbers within the table of contents when shown in Web Layout View
4385     if( lcl_FindInCommand( pContext->GetCommand(), 'z', sValue ))
4386     {
4387         bHideTabLeaderPageNumbers = true ;
4388     }
4389 
4390                     //if there's no option then it should be created from outline
4391     if( !bFromOutline && !bFromEntries && sTemplate.isEmpty()  )
4392         bFromOutline = true;
4393 
4394     const OUString aTocTitle = extractTocTitle();
4395 
4396     if (m_xTextFactory.is() && ! m_aTextAppendStack.empty())
4397     {
4398         const auto& xTextAppend = GetTopTextAppend();
4399         if (aTocTitle.isEmpty() || bTableOfFigures)
4400         {
4401             // reset marker of the TOC title
4402             m_xStdEntryStart.clear();
4403 
4404             // Create section before setting m_bStartTOC: finishing paragraph
4405             // inside StartIndexSectionChecked could do the wrong thing otherwise
4406             xTOC = StartIndexSectionChecked(bTableOfFigures ? "com.sun.star.text.IllustrationsIndex"
4407                                                             : sTOCServiceName);
4408 
4409             const auto xTextCursor = xTextAppend->getText()->createTextCursor();
4410             if (xTextCursor)
4411                 xTextCursor->gotoEnd(false);
4412             xTOCMarkerCursor = xTextCursor;
4413         }
4414         else
4415         {
4416             // create TOC section
4417             css::uno::Reference<css::text::XTextRange> xTextRangeEndOfTocHeader = GetTopTextAppend()->getEnd();
4418             xTOC = createSectionForRange(m_xStdEntryStart, xTextRangeEndOfTocHeader, sTOCServiceName, false);
4419 
4420             // init [xTOCMarkerCursor]
4421             uno::Reference< text::XText > xText = xTextAppend->getText();
4422             uno::Reference< text::XTextCursor > xCrsr = xText->createTextCursor();
4423             xTOCMarkerCursor = xCrsr;
4424 
4425             // create header of the TOC with the TOC title inside
4426             const OUString aObjectType("com.sun.star.text.IndexHeaderSection");
4427             uno::Reference<beans::XPropertySet> xIfc = createSectionForRange(m_xStdEntryStart, xTextRangeEndOfTocHeader, aObjectType, true);
4428         }
4429     }
4430 
4431     m_bStartTOC = true;
4432 
4433     if (xTOC.is())
4434         xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::makeAny(aTocTitle));
4435 
4436     if (!aBookmarkName.isEmpty())
4437         xTOC->setPropertyValue(getPropertyName(PROP_TOC_BOOKMARK), uno::makeAny(aBookmarkName));
4438     if( !bTableOfFigures && xTOC.is() )
4439     {
4440         xTOC->setPropertyValue( getPropertyName( PROP_LEVEL ), uno::makeAny( nMaxLevel ) );
4441         xTOC->setPropertyValue( getPropertyName( PROP_CREATE_FROM_OUTLINE ), uno::makeAny( bFromOutline ));
4442         xTOC->setPropertyValue( getPropertyName( PROP_CREATE_FROM_MARKS ), uno::makeAny( bFromEntries ));
4443         xTOC->setPropertyValue( getPropertyName( PROP_HIDE_TAB_LEADER_AND_PAGE_NUMBERS ), uno::makeAny( bHideTabLeaderPageNumbers ));
4444         xTOC->setPropertyValue( getPropertyName( PROP_TAB_IN_TOC ), uno::makeAny( bIsTabEntry ));
4445         xTOC->setPropertyValue( getPropertyName( PROP_TOC_NEW_LINE ), uno::makeAny( bNewLine ));
4446         xTOC->setPropertyValue( getPropertyName( PROP_TOC_PARAGRAPH_OUTLINE_LEVEL ), uno::makeAny( bParagraphOutlineLevel ));
4447         if( !sTemplate.isEmpty() )
4448         {
4449                             //the string contains comma separated the names and related levels
4450                             //like: "Heading 1,1,Heading 2,2"
4451             TOCStyleMap aMap;
4452             sal_Int32 nLevel;
4453             sal_Int32 nPosition = 0;
4454             while( nPosition >= 0)
4455             {
4456                 OUString sStyleName = sTemplate.getToken( 0, ',', nPosition );
4457                                 //empty tokens should be skipped
4458                 while( sStyleName.isEmpty() && nPosition > 0 )
4459                     sStyleName = sTemplate.getToken( 0, ',', nPosition );
4460                 nLevel = sTemplate.getToken( 0, ',', nPosition ).toInt32();
4461                 if( !nLevel )
4462                     nLevel = 1;
4463                 if( !sStyleName.isEmpty() )
4464                     aMap.emplace(nLevel, sStyleName);
4465             }
4466             uno::Reference< container::XIndexReplace> xParaStyles;
4467             xTOC->getPropertyValue(getPropertyName(PROP_LEVEL_PARAGRAPH_STYLES)) >>= xParaStyles;
4468             for( nLevel = 1; nLevel < 10; ++nLevel)
4469             {
4470                 sal_Int32 nLevelCount = aMap.count( nLevel );
4471                 if( nLevelCount  )
4472                 {
4473                     TOCStyleMap::iterator aTOCStyleIter = aMap.find( nLevel );
4474 
4475                     uno::Sequence< OUString> aStyles( nLevelCount );
4476                     for ( sal_Int32 nStyle = 0; nStyle < nLevelCount; ++nStyle, ++aTOCStyleIter )
4477                     {
4478                         aStyles[nStyle] = aTOCStyleIter->second;
4479                     }
4480                     xParaStyles->replaceByIndex(nLevel - 1, uno::makeAny(aStyles));
4481                 }
4482             }
4483             xTOC->setPropertyValue(getPropertyName(PROP_CREATE_FROM_LEVEL_PARAGRAPH_STYLES), uno::makeAny( true ));
4484 
4485         }
4486         if(bHyperlinks  || !sChapterNoSeparator.isEmpty())
4487         {
4488             uno::Reference< container::XIndexReplace> xLevelFormats;
4489             xTOC->getPropertyValue(getPropertyName(PROP_LEVEL_FORMAT)) >>= xLevelFormats;
4490             sal_Int32 nLevelCount = xLevelFormats->getCount();
4491                             //start with level 1, 0 is the header level
4492             for( sal_Int32 nLevel = 1; nLevel < nLevelCount; ++nLevel)
4493             {
4494                 uno::Sequence< beans::PropertyValues > aLevel;
4495                 xLevelFormats->getByIndex( nLevel ) >>= aLevel;
4496 
4497                 uno::Sequence< beans::PropertyValues > aNewLevel = lcl_createTOXLevelHyperlinks(
4498                                                     bHyperlinks, sChapterNoSeparator,
4499                                                     aLevel );
4500                 xLevelFormats->replaceByIndex( nLevel, uno::makeAny( aNewLevel ) );
4501             }
4502         }
4503     }
4504     else if (bTableOfFigures && xTOC.is())
4505     {
4506         if (!sFigureSequence.isEmpty())
4507             xTOC->setPropertyValue(getPropertyName(PROP_LABEL_CATEGORY),
4508                                    uno::makeAny(sFigureSequence));
4509 
4510         if ( bHyperlinks )
4511         {
4512             uno::Reference< container::XIndexReplace> xLevelFormats;
4513             xTOC->getPropertyValue(getPropertyName(PROP_LEVEL_FORMAT)) >>= xLevelFormats;
4514             uno::Sequence< beans::PropertyValues > aLevel;
4515             xLevelFormats->getByIndex( 1 ) >>= aLevel;
4516 
4517             uno::Sequence< beans::PropertyValues > aNewLevel = lcl_createTOXLevelHyperlinks(
4518                                                 bHyperlinks, sChapterNoSeparator,
4519                                                 aLevel );
4520             xLevelFormats->replaceByIndex( 1, uno::makeAny( aNewLevel ) );
4521         }
4522     }
4523     pContext->SetTOC( xTOC );
4524     m_bParaHadField = false;
4525 }
4526 
createSectionForRange(uno::Reference<css::text::XTextRange> xStart,uno::Reference<css::text::XTextRange> xEnd,const OUString & sObjectType,bool stepLeft)4527 uno::Reference<beans::XPropertySet> DomainMapper_Impl::createSectionForRange(
4528     uno::Reference< css::text::XTextRange > xStart,
4529     uno::Reference< css::text::XTextRange > xEnd,
4530     const OUString & sObjectType,
4531     bool stepLeft)
4532 {
4533     if (!xStart.is())
4534         return uno::Reference<beans::XPropertySet>();
4535     if (!xEnd.is())
4536         return uno::Reference<beans::XPropertySet>();
4537 
4538     uno::Reference< beans::XPropertySet > xRet;
4539     if (m_aTextAppendStack.empty())
4540         return xRet;
4541     uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
4542     if(xTextAppend.is())
4543     {
4544         try
4545         {
4546             uno::Reference< text::XParagraphCursor > xCursor(
4547                 xTextAppend->createTextCursorByRange( xStart ), uno::UNO_QUERY_THROW);
4548             //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
4549             xCursor->gotoStartOfParagraph( false );
4550             xCursor->gotoRange( xEnd, true );
4551             //the paragraph after this new section is already inserted
4552             if (stepLeft)
4553                 xCursor->goLeft(1, true);
4554             uno::Reference< text::XTextContent > xSection( m_xTextFactory->createInstance(sObjectType), uno::UNO_QUERY_THROW );
4555             xSection->attach( uno::Reference< text::XTextRange >( xCursor, uno::UNO_QUERY_THROW) );
4556             xRet.set(xSection, uno::UNO_QUERY );
4557         }
4558         catch(const uno::Exception&)
4559         {
4560         }
4561     }
4562 
4563     return xRet;
4564 }
4565 
handleBibliography(const FieldContextPtr & pContext,const OUString & sTOCServiceName)4566 void DomainMapper_Impl::handleBibliography
4567     (const FieldContextPtr& pContext,
4568     const OUString & sTOCServiceName)
4569 {
4570     if (m_aTextAppendStack.empty())
4571     {
4572         // tdf#130214: a workaround to avoid crash on import errors
4573         SAL_WARN("writerfilter.dmapper", "no text append stack");
4574         return;
4575     }
4576     // Create section before setting m_bStartTOC and m_bStartBibliography: finishing paragraph
4577     // inside StartIndexSectionChecked could do the wrong thing otherwise
4578     const auto xTOC = StartIndexSectionChecked(sTOCServiceName);
4579     m_bStartTOC = true;
4580     m_bStartBibliography = true;
4581 
4582     if (xTOC.is())
4583         xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::makeAny(OUString()));
4584 
4585     pContext->SetTOC( xTOC );
4586     m_bParaHadField = false;
4587 
4588     uno::Reference< text::XTextContent > xToInsert( xTOC, uno::UNO_QUERY );
4589     appendTextContent(xToInsert, uno::Sequence< beans::PropertyValue >() );
4590 }
4591 
handleIndex(const FieldContextPtr & pContext,const OUString & sTOCServiceName)4592 void DomainMapper_Impl::handleIndex
4593     (const FieldContextPtr& pContext,
4594     const OUString & sTOCServiceName)
4595 {
4596     // Create section before setting m_bStartTOC and m_bStartIndex: finishing paragraph
4597     // inside StartIndexSectionChecked could do the wrong thing otherwise
4598     const auto xTOC = StartIndexSectionChecked(sTOCServiceName);
4599 
4600     m_bStartTOC = true;
4601     m_bStartIndex = true;
4602     OUString sValue;
4603     OUString sIndexEntryType = "I"; // Default value for field flag '\f' is 'I'.
4604 
4605     if (xTOC.is())
4606     {
4607         xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::makeAny(OUString()));
4608 
4609         if( lcl_FindInCommand( pContext->GetCommand(), 'r', sValue ))
4610         {
4611             xTOC->setPropertyValue("IsCommaSeparated", uno::makeAny(true));
4612         }
4613         if( lcl_FindInCommand( pContext->GetCommand(), 'h', sValue ))
4614         {
4615             xTOC->setPropertyValue("UseAlphabeticalSeparators", uno::makeAny(true));
4616         }
4617         if( lcl_FindInCommand( pContext->GetCommand(), 'f', sValue ))
4618         {
4619             if(!sValue.isEmpty())
4620                 sIndexEntryType = sValue ;
4621             xTOC->setPropertyValue(getPropertyName( PROP_INDEX_ENTRY_TYPE ), uno::makeAny(sIndexEntryType));
4622         }
4623     }
4624     pContext->SetTOC( xTOC );
4625     m_bParaHadField = false;
4626 
4627     uno::Reference< text::XTextContent > xToInsert( xTOC, uno::UNO_QUERY );
4628     appendTextContent(xToInsert, uno::Sequence< beans::PropertyValue >() );
4629 
4630     if( lcl_FindInCommand( pContext->GetCommand(), 'c', sValue ))
4631     {
4632         sValue = sValue.replaceAll("\"", "");
4633         uno::Reference<text::XTextColumns> xTextColumns;
4634         xTOC->getPropertyValue(getPropertyName( PROP_TEXT_COLUMNS )) >>= xTextColumns;
4635         if (xTextColumns.is())
4636         {
4637             xTextColumns->setColumnCount( sValue.toInt32() );
4638             xTOC->setPropertyValue( getPropertyName( PROP_TEXT_COLUMNS ), uno::makeAny( xTextColumns ) );
4639         }
4640     }
4641 }
4642 
InsertFieldmark(std::stack<TextAppendContext> & rTextAppendStack,uno::Reference<text::XFormField> const & xFormField,uno::Reference<text::XTextRange> const & xStartRange,boost::optional<FieldId> const oFieldId)4643 static auto InsertFieldmark(std::stack<TextAppendContext> & rTextAppendStack,
4644         uno::Reference<text::XFormField> const& xFormField,
4645         uno::Reference<text::XTextRange> const& xStartRange,
4646         boost::optional<FieldId> const oFieldId) -> void
4647 {
4648     uno::Reference<text::XTextContent> const xTextContent(xFormField, uno::UNO_QUERY_THROW);
4649     uno::Reference<text::XTextAppend> const& xTextAppend(rTextAppendStack.top().xTextAppend);
4650     uno::Reference<text::XTextCursor> const xCursor =
4651         xTextAppend->createTextCursorByRange(xStartRange);
4652     if (rTextAppendStack.top().xInsertPosition.is())
4653     {
4654         uno::Reference<text::XTextRangeCompare> const xCompare(
4655                 rTextAppendStack.top().xTextAppend,
4656                 uno::UNO_QUERY);
4657         if (xCompare->compareRegionStarts(xStartRange, rTextAppendStack.top().xInsertPosition) < 0)
4658         {
4659             SAL_WARN("writerfilter.dmapper", "invalid field mark positions");
4660             assert(false);
4661         }
4662         xCursor->gotoRange(rTextAppendStack.top().xInsertPosition, true);
4663     }
4664     else
4665     {
4666         xCursor->gotoEnd(true);
4667     }
4668     xTextAppend->insertTextContent(xCursor, xTextContent, true);
4669     if (oFieldId
4670         && (oFieldId == FIELD_FORMCHECKBOX || oFieldId == FIELD_FORMDROPDOWN))
4671     {
4672         return; // only a single CH_TXT_ATR_FORMELEMENT!
4673     }
4674     // problem: the fieldmark must be inserted in CloseFieldCommand(), because
4675     //          attach() takes 2 positions, not 3!
4676     // FAIL: AppendTextNode() ignores the content index!
4677     // plan B: insert a spurious paragraph break now and join
4678     //         it in PopFieldContext()!
4679     xCursor->gotoRange(xTextContent->getAnchor()->getEnd(), false);
4680     xCursor->goLeft(1, false); // skip CH_TXT_ATR_FIELDEND
4681     xTextAppend->insertControlCharacter(xCursor, text::ControlCharacter::PARAGRAPH_BREAK, false);
4682     xCursor->goLeft(1, false); // back to previous paragraph
4683     rTextAppendStack.push(TextAppendContext(xTextAppend, xCursor));
4684 }
4685 
PopFieldmark(std::stack<TextAppendContext> & rTextAppendStack,uno::Reference<text::XTextCursor> const & xCursor,boost::optional<FieldId> const oFieldId)4686 static auto PopFieldmark(std::stack<TextAppendContext> & rTextAppendStack,
4687         uno::Reference<text::XTextCursor> const& xCursor,
4688         boost::optional<FieldId> const oFieldId) -> void
4689 {
4690     if (oFieldId
4691         && (oFieldId == FIELD_FORMCHECKBOX || oFieldId == FIELD_FORMDROPDOWN))
4692     {
4693         return; // only a single CH_TXT_ATR_FORMELEMENT!
4694     }
4695     xCursor->gotoRange(rTextAppendStack.top().xInsertPosition, false);
4696     xCursor->goRight(1, true);
4697     xCursor->setString(OUString()); // undo SplitNode from CloseFieldCommand()
4698     // note: paragraph properties will be overwritten
4699     // by finishParagraph() anyway so ignore here
4700     rTextAppendStack.pop();
4701 }
4702 
CloseFieldCommand()4703 void DomainMapper_Impl::CloseFieldCommand()
4704 {
4705     if(m_bDiscardHeaderFooter)
4706         return;
4707 #ifdef DBG_UTIL
4708     TagLogger::getInstance().element("closeFieldCommand");
4709 #endif
4710 
4711     FieldContextPtr pContext;
4712     if(!m_aFieldStack.empty())
4713         pContext = m_aFieldStack.back();
4714     OSL_ENSURE( pContext.get(), "no field context available");
4715     if( pContext.get() )
4716     {
4717         m_bSetUserFieldContent = false;
4718         m_bSetCitation = false;
4719         m_bSetDateValue = false;
4720         const FieldConversionMap_t& aFieldConversionMap = lcl_GetFieldConversion();
4721 
4722         try
4723         {
4724             uno::Reference< uno::XInterface > xFieldInterface;
4725 
4726             std::tuple<OUString, std::vector<OUString>, std::vector<OUString> > const
4727                 field(splitFieldCommand(pContext->GetCommand()));
4728             OUString const sFirstParam(std::get<1>(field).empty()
4729                     ? OUString() : std::get<1>(field).front());
4730 
4731             FieldConversionMap_t::const_iterator const aIt =
4732                 aFieldConversionMap.find(std::get<0>(field));
4733             if (aIt != aFieldConversionMap.end()
4734                 && (!m_bForceGenericFields
4735                     // these need to convert ffData to properties...
4736                         || (aIt->second.eFieldId == FIELD_FORMCHECKBOX)
4737                         || (aIt->second.eFieldId == FIELD_FORMDROPDOWN)
4738                         || (aIt->second.eFieldId == FIELD_FORMTEXT)))
4739             {
4740                 pContext->SetFieldId(aIt->second.eFieldId);
4741                 bool bCreateEnhancedField = false;
4742                 uno::Reference< beans::XPropertySet > xFieldProperties;
4743                 bool bCreateField = true;
4744                 switch (aIt->second.eFieldId)
4745                 {
4746                 case FIELD_HYPERLINK:
4747                 case FIELD_DOCPROPERTY:
4748                 case FIELD_TOC:
4749                 case FIELD_INDEX:
4750                 case FIELD_XE:
4751                 case FIELD_BIBLIOGRAPHY:
4752                 case FIELD_CITATION:
4753                 case FIELD_TC:
4754                 case FIELD_EQ:
4755                 case FIELD_INCLUDEPICTURE:
4756                 case FIELD_SYMBOL:
4757                 case FIELD_GOTOBUTTON:
4758                         bCreateField = false;
4759                         break;
4760                 case FIELD_FORMCHECKBOX :
4761                 case FIELD_FORMTEXT :
4762                 case FIELD_FORMDROPDOWN :
4763                 {
4764                     // If we use 'enhanced' fields then FIELD_FORMCHECKBOX,
4765                     // FIELD_FORMTEXT & FIELD_FORMDROPDOWN are treated specially
4766                     if ( m_bUsingEnhancedFields  )
4767                     {
4768                         bCreateField = false;
4769                         bCreateEnhancedField = true;
4770                     }
4771                     // for non enhanced fields checkboxes are displayed
4772                     // as an awt control not a field
4773                     else if ( aIt->second.eFieldId == FIELD_FORMCHECKBOX )
4774                         bCreateField = false;
4775                     break;
4776                 }
4777                 default:
4778                 {
4779                     FieldContextPtr pOuter = GetParentFieldContext(m_aFieldStack);
4780                     if (pOuter)
4781                     {
4782                         if (!IsFieldNestingAllowed(pOuter, m_aFieldStack.back()))
4783                         {
4784                             // Parent field can't host this child field: don't create a child field
4785                             // in this case.
4786                             bCreateField = false;
4787                         }
4788                     }
4789                     break;
4790                 }
4791                 }
4792                 if (m_bStartTOC && (aIt->second.eFieldId == FIELD_PAGEREF) )
4793                 {
4794                     bCreateField = false;
4795                 }
4796 
4797                 if( bCreateField || bCreateEnhancedField )
4798                 {
4799                     //add the service prefix
4800                     OUString sServiceName("com.sun.star.text.");
4801                     if ( bCreateEnhancedField )
4802                     {
4803                         const FieldConversionMap_t& aEnhancedFieldConversionMap = lcl_GetEnhancedFieldConversion();
4804                         FieldConversionMap_t::const_iterator aEnhancedIt =
4805                             aEnhancedFieldConversionMap.find(std::get<0>(field));
4806                         if ( aEnhancedIt != aEnhancedFieldConversionMap.end())
4807                             sServiceName += OUString::createFromAscii(aEnhancedIt->second.cFieldServiceName );
4808                     }
4809                     else
4810                     {
4811                         sServiceName += "TextField." + OUString::createFromAscii(aIt->second.cFieldServiceName );
4812                     }
4813 
4814 #ifdef DBG_UTIL
4815                     TagLogger::getInstance().startElement("fieldService");
4816                     TagLogger::getInstance().chars(sServiceName);
4817                     TagLogger::getInstance().endElement();
4818 #endif
4819 
4820                     if (m_xTextFactory.is())
4821                     {
4822                         xFieldInterface = m_xTextFactory->createInstance(sServiceName);
4823                         xFieldProperties.set( xFieldInterface, uno::UNO_QUERY_THROW);
4824                     }
4825                 }
4826                 switch( aIt->second.eFieldId )
4827                 {
4828                     case FIELD_ADDRESSBLOCK: break;
4829                     case FIELD_ADVANCE     : break;
4830                     case FIELD_ASK         :
4831                         handleFieldAsk(pContext, xFieldInterface, xFieldProperties);
4832                     break;
4833                     case FIELD_AUTONUM    :
4834                     case FIELD_AUTONUMLGL :
4835                     case FIELD_AUTONUMOUT :
4836                         handleAutoNum(pContext, xFieldInterface, xFieldProperties);
4837                     break;
4838                     case FIELD_AUTHOR       :
4839                     case FIELD_USERNAME     :
4840                     case FIELD_USERINITIALS :
4841                         handleAuthor(sFirstParam,
4842                             xFieldProperties,
4843                             aIt->second.eFieldId);
4844                     break;
4845                     case FIELD_DATE:
4846                     if (xFieldProperties.is())
4847                     {
4848                         // Get field fixed property from the context handler
4849                         if (pContext->IsFieldLocked())
4850                         {
4851                             xFieldProperties->setPropertyValue(
4852                                 getPropertyName(PROP_IS_FIXED),
4853                                 uno::makeAny( true ));
4854                             m_bSetDateValue = true;
4855                         }
4856                         else
4857                             xFieldProperties->setPropertyValue(
4858                                 getPropertyName(PROP_IS_FIXED),
4859                                 uno::makeAny( false ));
4860 
4861                         xFieldProperties->setPropertyValue(
4862                             getPropertyName(PROP_IS_DATE),
4863                             uno::makeAny( true ));
4864                         SetNumberFormat( pContext->GetCommand(), xFieldProperties );
4865                     }
4866                     break;
4867                     case FIELD_COMMENTS     :
4868                     {
4869                         // OUString sParam = lcl_ExtractParameter(pContext->GetCommand(), sizeof(" COMMENTS") );
4870                         // A parameter with COMMENTS shouldn't set fixed
4871                         // ( or at least the binary filter doesn't )
4872                         // If we set fixed then we won't export a field cmd.
4873                         // Additionally the para in COMMENTS is more like an
4874                         // instruction to set the document property comments
4875                         // with the param ( e.g. each COMMENT with a param will
4876                         // overwrite the Comments document property
4877                         // #TODO implement the above too
4878                         xFieldProperties->setPropertyValue(
4879                             getPropertyName( PROP_IS_FIXED ), uno::makeAny( false ));
4880                             //PROP_CURRENT_PRESENTATION is set later anyway
4881                     }
4882                     break;
4883                     case FIELD_CREATEDATE  :
4884                     {
4885                         xFieldProperties->setPropertyValue(
4886                             getPropertyName( PROP_IS_DATE ), uno::makeAny( true ));
4887                         SetNumberFormat( pContext->GetCommand(), xFieldProperties );
4888                     }
4889                     break;
4890                     case FIELD_DOCPROPERTY :
4891                         handleDocProperty(pContext, sFirstParam,
4892                                 xFieldInterface);
4893                     break;
4894                     case FIELD_DOCVARIABLE  :
4895                     {
4896                         //create a user field and type
4897                         uno::Reference< beans::XPropertySet > xMaster =
4898                             FindOrCreateFieldMaster("com.sun.star.text.FieldMaster.User", sFirstParam);
4899                         uno::Reference< text::XDependentTextField > xDependentField( xFieldInterface, uno::UNO_QUERY_THROW );
4900                         xDependentField->attachTextFieldMaster( xMaster );
4901                         m_bSetUserFieldContent = true;
4902                     }
4903                     break;
4904                     case FIELD_EDITTIME     :
4905                         //it's a numbering type, no number format! SetNumberFormat( pContext->GetCommand(), xFieldProperties );
4906                     break;
4907                     case FIELD_EQ:
4908                     {
4909                         OUString aCommand = pContext->GetCommand().trim();
4910 
4911                         msfilter::util::EquationResult aResult(msfilter::util::ParseCombinedChars(aCommand));
4912                         if (!aResult.sType.isEmpty() && m_xTextFactory.is())
4913                         {
4914                             xFieldInterface = m_xTextFactory->createInstance("com.sun.star.text.TextField." + aResult.sType);
4915                             xFieldProperties =
4916                                 uno::Reference< beans::XPropertySet >( xFieldInterface,
4917                                     uno::UNO_QUERY_THROW);
4918                             xFieldProperties->setPropertyValue(getPropertyName(PROP_CONTENT), uno::makeAny(aResult.sResult));
4919                         }
4920                         else
4921                         {
4922                             //merge Read_SubF_Ruby into filter/.../util.cxx and reuse that ?
4923                             sal_Int32 nSpaceIndex = aCommand.indexOf(' ');
4924                             if(nSpaceIndex > 0)
4925                                 aCommand = aCommand.copy(nSpaceIndex).trim();
4926                             if (aCommand.startsWith("\\s"))
4927                             {
4928                                 aCommand = aCommand.copy(2);
4929                                 if (aCommand.startsWith("\\do"))
4930                                 {
4931                                     aCommand = aCommand.copy(3);
4932                                     sal_Int32 nStartIndex = aCommand.indexOf('(');
4933                                     sal_Int32 nEndIndex = aCommand.indexOf(')');
4934                                     if (nStartIndex > 0 && nEndIndex > 0)
4935                                     {
4936                                         // nDown is the requested "lower by" value in points.
4937                                         sal_Int32 nDown = aCommand.copy(0, nStartIndex).toInt32();
4938                                         OUString aContent = aCommand.copy(nStartIndex + 1, nEndIndex - nStartIndex - 1);
4939                                         PropertyMapPtr pCharContext = GetTopContext();
4940                                         // dHeight is the font size of the current style.
4941                                         double dHeight = 0;
4942                                         if ((GetPropertyFromParaStyleSheet(PROP_CHAR_HEIGHT) >>= dHeight) && dHeight != 0)
4943                                             // Character escapement should be given in negative percents for subscripts.
4944                                             pCharContext->Insert(PROP_CHAR_ESCAPEMENT, uno::makeAny( sal_Int16(- 100 * nDown / dHeight) ) );
4945                                         appendTextPortion(aContent, pCharContext);
4946                                     }
4947                                 }
4948                             }
4949                             else if (aCommand.startsWith("\\* jc"))
4950                             {
4951                                 handleRubyEQField(pContext);
4952                             }
4953                         }
4954                     }
4955                     break;
4956                     case FIELD_FILLIN       :
4957                         if (xFieldProperties.is())
4958                             xFieldProperties->setPropertyValue(
4959                                     getPropertyName(PROP_HINT), uno::makeAny( pContext->GetCommand().getToken(1, '\"')));
4960                     break;
4961                     case FIELD_FILENAME:
4962                     {
4963                         sal_Int32 nNumberingTypeIndex = pContext->GetCommand().indexOf("\\p");
4964                         if (xFieldProperties.is())
4965                             xFieldProperties->setPropertyValue(
4966                                     getPropertyName(PROP_FILE_FORMAT),
4967                                     uno::makeAny( nNumberingTypeIndex > 0 ? text::FilenameDisplayFormat::FULL : text::FilenameDisplayFormat::NAME_AND_EXT ));
4968                     }
4969                     break;
4970                     case FIELD_FILESIZE     : break;
4971                     case FIELD_FORMULA :
4972                         handleFieldFormula(pContext, xFieldProperties);
4973                     break;
4974                     case FIELD_FORMCHECKBOX :
4975                     case FIELD_FORMDROPDOWN :
4976                     case FIELD_FORMTEXT :
4977                         {
4978                             uno::Reference< text::XTextField > xTextField( xFieldInterface, uno::UNO_QUERY );
4979                             if ( !xTextField.is() )
4980                             {
4981                                 FFDataHandler::Pointer_t
4982                                 pFFDataHandler(pContext->getFFDataHandler());
4983                                 FormControlHelper::Pointer_t
4984                                     pFormControlHelper(new FormControlHelper
4985                                                        (m_bUsingEnhancedFields ? aIt->second.eFieldId : FIELD_FORMCHECKBOX,
4986 
4987                                                         m_xTextDocument, pFFDataHandler));
4988                                 pContext->setFormControlHelper(pFormControlHelper);
4989                                 uno::Reference< text::XFormField > xFormField( xFieldInterface, uno::UNO_QUERY );
4990                                 uno::Reference< container::XNamed > xNamed( xFormField, uno::UNO_QUERY );
4991                                 if ( xNamed.is() )
4992                                 {
4993                                     if ( pFFDataHandler && !pFFDataHandler->getName().isEmpty() )
4994                                         xNamed->setName(  pFFDataHandler->getName() );
4995                                     pContext->SetFormField( xFormField );
4996                                 }
4997                                 InsertFieldmark(m_aTextAppendStack,
4998                                     xFormField, pContext->GetStartRange(),
4999                                     pContext->GetFieldId());
5000                             }
5001                             else
5002                             {
5003                                 if ( aIt->second.eFieldId == FIELD_FORMDROPDOWN )
5004                                     lcl_handleDropdownField( xFieldProperties, pContext->getFFDataHandler() );
5005                                 else
5006                                     lcl_handleTextField( xFieldProperties, pContext->getFFDataHandler() );
5007                             }
5008                         }
5009                         break;
5010                     case FIELD_GOTOBUTTON   : break;
5011                     case FIELD_HYPERLINK:
5012                     {
5013                         ::std::vector<OUString> aParts = pContext->GetCommandParts();
5014 
5015                         // Syntax is either:
5016                         // HYPERLINK "" \l "link"
5017                         // or
5018                         // HYPERLINK \l "link"
5019                         // Make sure "HYPERLINK" doesn't end up as part of link in the second case.
5020                         if (!aParts.empty() && aParts[0] == "HYPERLINK")
5021                             aParts.erase(aParts.begin());
5022 
5023                         ::std::vector<OUString>::const_iterator aItEnd = aParts.end();
5024                         ::std::vector<OUString>::const_iterator aPartIt = aParts.begin();
5025 
5026                         OUString sURL;
5027                         OUString sTarget;
5028 
5029                         while (aPartIt != aItEnd)
5030                         {
5031                             if ( *aPartIt == "\\l" )
5032                             {
5033                                 ++aPartIt;
5034 
5035                                 if (aPartIt == aItEnd)
5036                                     break;
5037 
5038                                 sURL += "#" + *aPartIt;
5039                             }
5040                             else if (*aPartIt == "\\m" || *aPartIt == "\\n" || *aPartIt == "\\h")
5041                             {
5042                             }
5043                             else if ( *aPartIt == "\\o" || *aPartIt == "\\t" )
5044                             {
5045                                 ++aPartIt;
5046 
5047                                 if (aPartIt == aItEnd)
5048                                     break;
5049 
5050                                 sTarget = *aPartIt;
5051                             }
5052                             else
5053                             {
5054                                 sURL = *aPartIt;
5055                             }
5056 
5057                             ++aPartIt;
5058                         }
5059 
5060                         if (!sURL.isEmpty())
5061                         {
5062                             if (sURL.startsWith("file:///"))
5063                             {
5064                                 // file:///absolute\\path\\to\\file => invalid file URI (Writer cannot open)
5065                                 // convert all double backslashes to slashes:
5066                                 sURL = sURL.replaceAll("\\\\", "/");
5067 
5068                                 // file:///absolute\path\to\file => invalid file URI (Writer cannot open)
5069                                 // convert all backslashes to slashes:
5070                                 sURL = sURL.replace('\\', '/');
5071                             }
5072                             // Try to make absolute any relative URLs, except
5073                             // for relative same-document URLs that only contain
5074                             // a fragment part:
5075                             else if (!sURL.startsWith("#")) {
5076                                 try {
5077                                     sURL = rtl::Uri::convertRelToAbs(
5078                                         m_aBaseUrl, sURL);
5079                                 } catch (rtl::MalformedUriException & e) {
5080                                     SAL_WARN(
5081                                         "writerfilter.dmapper",
5082                                         "MalformedUriException "
5083                                             << e.getMessage());
5084                                 }
5085                             }
5086                             pContext->SetHyperlinkURL(sURL);
5087                         }
5088 
5089                         if (!sTarget.isEmpty())
5090                             pContext->SetHyperlinkTarget(sTarget);
5091                     }
5092                     break;
5093                     case FIELD_IF           : break;
5094                     case FIELD_INFO         : break;
5095                     case FIELD_INCLUDEPICTURE: break;
5096                     case FIELD_KEYWORDS     :
5097                     {
5098                         if (!sFirstParam.isEmpty())
5099                         {
5100                             xFieldProperties->setPropertyValue(
5101                                     getPropertyName( PROP_IS_FIXED ), uno::makeAny( true ));
5102                             //PROP_CURRENT_PRESENTATION is set later anyway
5103                         }
5104                     }
5105                     break;
5106                     case FIELD_LASTSAVEDBY  : break;
5107                     case FIELD_MACROBUTTON:
5108                     {
5109                         //extract macro name
5110                         sal_Int32 nIndex = sizeof(" MACROBUTTON ");
5111                         OUString sMacro = pContext->GetCommand().getToken( 0, ' ', nIndex);
5112                         if (xFieldProperties.is())
5113                             xFieldProperties->setPropertyValue(
5114                                     getPropertyName(PROP_MACRO_NAME), uno::makeAny( sMacro ));
5115 
5116                         //extract quick help text
5117                         if(xFieldProperties.is() && pContext->GetCommand().getLength() > nIndex + 1)
5118                         {
5119                             xFieldProperties->setPropertyValue(
5120                                 getPropertyName(PROP_HINT),
5121                                 uno::makeAny( pContext->GetCommand().copy( nIndex )));
5122                         }
5123                     }
5124                     break;
5125                     case FIELD_MERGEFIELD  :
5126                     {
5127                         //todo: create a database field and fieldmaster pointing to a column, only
5128                         //create a user field and type
5129                         uno::Reference< beans::XPropertySet > xMaster =
5130                             FindOrCreateFieldMaster("com.sun.star.text.FieldMaster.Database", sFirstParam);
5131 
5132     //                    xFieldProperties->setPropertyValue(
5133     //                             "FieldCode",
5134     //                             uno::makeAny( pContext->GetCommand().copy( nIndex + 1 )));
5135                         uno::Reference< text::XDependentTextField > xDependentField( xFieldInterface, uno::UNO_QUERY_THROW );
5136                         xDependentField->attachTextFieldMaster( xMaster );
5137                     }
5138                     break;
5139                     case FIELD_MERGEREC     : break;
5140                     case FIELD_MERGESEQ     : break;
5141                     case FIELD_NEXT         : break;
5142                     case FIELD_NEXTIF       : break;
5143                     case FIELD_PAGE        :
5144                         if (xFieldProperties.is())
5145                         {
5146                             xFieldProperties->setPropertyValue(
5147                                     getPropertyName(PROP_NUMBERING_TYPE),
5148                                     uno::makeAny( lcl_ParseNumberingType(pContext->GetCommand()) ));
5149                             xFieldProperties->setPropertyValue(
5150                                     getPropertyName(PROP_SUB_TYPE),
5151                                     uno::makeAny( text::PageNumberType_CURRENT ));
5152                         }
5153 
5154                     break;
5155                     case FIELD_PAGEREF:
5156                     case FIELD_REF:
5157                     if (xFieldProperties.is() && !m_bStartTOC)
5158                     {
5159                         bool bPageRef = aIt->second.eFieldId == FIELD_PAGEREF;
5160 
5161                         // Do we need a GetReference (default) or a GetExpression field?
5162                         uno::Reference< text::XTextFieldsSupplier > xFieldsSupplier( GetTextDocument(), uno::UNO_QUERY );
5163                         uno::Reference< container::XNameAccess > xFieldMasterAccess = xFieldsSupplier->getTextFieldMasters();
5164 
5165                         if (!xFieldMasterAccess->hasByName(
5166                                 "com.sun.star.text.FieldMaster.SetExpression."
5167                                 + sFirstParam))
5168                         {
5169                         xFieldProperties->setPropertyValue(
5170                             getPropertyName(PROP_REFERENCE_FIELD_SOURCE),
5171                             uno::makeAny( sal_Int16(text::ReferenceFieldSource::BOOKMARK)) );
5172                         xFieldProperties->setPropertyValue(
5173                             getPropertyName(PROP_SOURCE_NAME),
5174                             uno::makeAny(sFirstParam) );
5175                         sal_Int16 nFieldPart = (bPageRef ? text::ReferenceFieldPart::PAGE : text::ReferenceFieldPart::TEXT);
5176                         OUString sValue;
5177                         if( lcl_FindInCommand( pContext->GetCommand(), 'p', sValue ))
5178                         {
5179                             //above-below
5180                             nFieldPart = text::ReferenceFieldPart::UP_DOWN;
5181                         }
5182                         else if( lcl_FindInCommand( pContext->GetCommand(), 'r', sValue ))
5183                         {
5184                             //number
5185                             nFieldPart = text::ReferenceFieldPart::NUMBER;
5186                         }
5187                         else if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
5188                         {
5189                             //number-no-context
5190                             nFieldPart = text::ReferenceFieldPart::NUMBER_NO_CONTEXT;
5191                         }
5192                         else if( lcl_FindInCommand( pContext->GetCommand(), 'w', sValue ))
5193                         {
5194                             //number-full-context
5195                             nFieldPart = text::ReferenceFieldPart::NUMBER_FULL_CONTEXT;
5196                         }
5197                         xFieldProperties->setPropertyValue(
5198                                 getPropertyName( PROP_REFERENCE_FIELD_PART ), uno::makeAny( nFieldPart ));
5199                         }
5200                         else if( m_xTextFactory.is() )
5201                         {
5202                             xFieldInterface = m_xTextFactory->createInstance("com.sun.star.text.TextField.GetExpression");
5203                             xFieldProperties.set(xFieldInterface, uno::UNO_QUERY);
5204                             xFieldProperties->setPropertyValue(
5205                                 getPropertyName(PROP_CONTENT),
5206                                 uno::makeAny(sFirstParam));
5207                             xFieldProperties->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::makeAny(text::SetVariableType::STRING));
5208                         }
5209                     }
5210                     break;
5211                     case FIELD_REVNUM       : break;
5212                     case FIELD_SAVEDATE     :
5213                         SetNumberFormat( pContext->GetCommand(), xFieldProperties );
5214                     break;
5215                     case FIELD_SECTION      : break;
5216                     case FIELD_SECTIONPAGES : break;
5217                     case FIELD_SEQ          :
5218                     {
5219                         // command looks like: " SEQ Table \* ARABIC "
5220                         OUString sCmd(pContext->GetCommand());
5221                         // find the sequence name, e.g. "SEQ"
5222                         OUString sSeqName = msfilter::util::findQuotedText(sCmd, "SEQ ", '\\');
5223                         sSeqName = sSeqName.trim();
5224 
5225                         // create a sequence field master using the sequence name
5226                         uno::Reference< beans::XPropertySet > xMaster = FindOrCreateFieldMaster(
5227                                     "com.sun.star.text.FieldMaster.SetExpression",
5228                                     sSeqName);
5229 
5230                         xMaster->setPropertyValue(
5231                             getPropertyName(PROP_SUB_TYPE),
5232                             uno::makeAny(text::SetVariableType::SEQUENCE));
5233 
5234                         // apply the numbering type
5235                         xFieldProperties->setPropertyValue(
5236                             getPropertyName(PROP_NUMBERING_TYPE),
5237                             uno::makeAny( lcl_ParseNumberingType(pContext->GetCommand()) ));
5238 
5239                         // attach the master to the field
5240                         uno::Reference< text::XDependentTextField > xDependentField( xFieldInterface, uno::UNO_QUERY_THROW );
5241                         xDependentField->attachTextFieldMaster( xMaster );
5242 
5243                         OUString sFormula = sSeqName + "+1";
5244                         OUString sValue;
5245                         if( lcl_FindInCommand( pContext->GetCommand(), 'c', sValue ))
5246                         {
5247                             sFormula = sSeqName;
5248                         }
5249                         else if( lcl_FindInCommand( pContext->GetCommand(), 'r', sValue ))
5250                         {
5251                             sFormula = sValue;
5252                         }
5253                         // TODO \s isn't handled, but the spec isn't easy to understand without
5254                         // an example for this one.
5255                         xFieldProperties->setPropertyValue(
5256                                 getPropertyName(PROP_CONTENT),
5257                                 uno::makeAny(sFormula));
5258 
5259                         // Take care of the numeric formatting definition, default is Arabic
5260                         sal_Int16 nNumberingType = lcl_ParseNumberingType(pContext->GetCommand());
5261                         if (nNumberingType == style::NumberingType::PAGE_DESCRIPTOR)
5262                             nNumberingType = style::NumberingType::ARABIC;
5263                         xFieldProperties->setPropertyValue(
5264                                 getPropertyName(PROP_NUMBERING_TYPE),
5265                                 uno::makeAny(nNumberingType));
5266                     }
5267                     break;
5268                     case FIELD_SET          :
5269                         handleFieldSet(pContext, xFieldInterface, xFieldProperties);
5270                     break;
5271                     case FIELD_SKIPIF       : break;
5272                     case FIELD_STYLEREF     : break;
5273                     case FIELD_SUBJECT      :
5274                     {
5275                         if (!sFirstParam.isEmpty())
5276                         {
5277                             xFieldProperties->setPropertyValue(
5278                                     getPropertyName( PROP_IS_FIXED ), uno::makeAny( true ));
5279                             //PROP_CURRENT_PRESENTATION is set later anyway
5280                         }
5281                     }
5282                     break;
5283                     case FIELD_SYMBOL:
5284                     {
5285                         uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
5286                         OUString sSymbol( sal_Unicode( sFirstParam.startsWithIgnoreAsciiCase("0x") ?  sFirstParam.copy(2).toUInt32(16) : sFirstParam.toUInt32() ) );
5287                         OUString sFont;
5288                         bool bHasFont = lcl_FindInCommand( pContext->GetCommand(), 'f', sFont);
5289                         if ( bHasFont )
5290                         {
5291                             sFont = sFont.trim();
5292                             if (sFont.startsWith("\""))
5293                                 sFont = sFont.copy(1);
5294                             if (sFont.endsWith("\""))
5295                                 sFont = sFont.copy(0,sFont.getLength()-1);
5296                         }
5297 
5298 
5299 
5300                         if (xTextAppend.is())
5301                         {
5302                             uno::Reference< text::XTextCursor > xCrsr = xTextAppend->getText()->createTextCursor();
5303                             uno::Reference< text::XText > xText = xTextAppend->getText();
5304                             if(xCrsr.is() && xText.is())
5305                             {
5306                                 xCrsr->gotoEnd(false);
5307                                 xText->insertString(xCrsr, sSymbol, true);
5308                                 uno::Reference< beans::XPropertySet > xProp( xCrsr, uno::UNO_QUERY );
5309                                 xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_CHAR_SET), uno::makeAny(awt::CharSet::SYMBOL));
5310                                 if(bHasFont)
5311                                 {
5312                                     uno::Any    aVal = uno::makeAny( sFont );
5313                                     xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME), aVal);
5314                                     xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME_ASIAN), aVal);
5315                                     xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME_COMPLEX), aVal);
5316 
5317                                 }
5318                             }
5319                         }
5320                     }
5321                     break;
5322                     case FIELD_TEMPLATE: break;
5323                     case FIELD_TIME         :
5324                     {
5325                         if (pContext->IsFieldLocked())
5326                         {
5327                             xFieldProperties->setPropertyValue(
5328                                 getPropertyName(PROP_IS_FIXED),
5329                                 uno::makeAny( true ));
5330                             m_bSetDateValue = true;
5331                         }
5332                         SetNumberFormat( pContext->GetCommand(), xFieldProperties );
5333                     }
5334                     break;
5335                     case FIELD_TITLE        :
5336                     {
5337                         if (!sFirstParam.isEmpty())
5338                         {
5339                             xFieldProperties->setPropertyValue(
5340                                     getPropertyName( PROP_IS_FIXED ), uno::makeAny( true ));
5341                             //PROP_CURRENT_PRESENTATION is set later anyway
5342                         }
5343                     }
5344                     break;
5345                     case FIELD_USERADDRESS  : //todo: user address collects street, city ...
5346                     break;
5347                     case FIELD_INDEX:
5348                         handleIndex(pContext,
5349                                   OUString::createFromAscii(aIt->second.cFieldServiceName));
5350                         break;
5351                     case FIELD_BIBLIOGRAPHY:
5352                         handleBibliography(pContext,
5353                                   OUString::createFromAscii(aIt->second.cFieldServiceName));
5354                         break;
5355                     case FIELD_TOC:
5356                         handleToc(pContext,
5357                                   OUString::createFromAscii(aIt->second.cFieldServiceName));
5358                     break;
5359                     case FIELD_XE:
5360                     {
5361                         if( !m_xTextFactory.is() )
5362                             break;
5363 
5364                         uno::Reference< beans::XPropertySet > xTC(
5365                                 m_xTextFactory->createInstance(
5366                                         OUString::createFromAscii(aIt->second.cFieldServiceName)),
5367                                         uno::UNO_QUERY_THROW);
5368                         if (!sFirstParam.isEmpty())
5369                         {
5370                             xTC->setPropertyValue("PrimaryKey",
5371                                     uno::makeAny(sFirstParam));
5372                         }
5373                         uno::Reference< text::XTextContent > xToInsert( xTC, uno::UNO_QUERY );
5374                         uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
5375                         if (xTextAppend.is())
5376                         {
5377                             uno::Reference< text::XTextCursor > xCrsr = xTextAppend->getText()->createTextCursor();
5378 
5379                             uno::Reference< text::XText > xText = xTextAppend->getText();
5380                             if(xCrsr.is() && xText.is())
5381                             {
5382                                 xCrsr->gotoEnd(false);
5383                                 xText->insertTextContent(uno::Reference< text::XTextRange >( xCrsr, uno::UNO_QUERY_THROW ), xToInsert, false);
5384                             }
5385                         }
5386                     }
5387                         break;
5388                     case FIELD_CITATION:
5389                     {
5390                         if( !m_xTextFactory.is() )
5391                             break;
5392 
5393                         xFieldInterface = m_xTextFactory->createInstance(
5394                                   OUString::createFromAscii(aIt->second.cFieldServiceName));
5395                         uno::Reference< beans::XPropertySet > xTC(xFieldInterface,
5396                                   uno::UNO_QUERY_THROW);
5397                         OUString sCmd(pContext->GetCommand());//sCmd is the entire instrText including the index e.g. CITATION Kra06 \l 1033
5398                         if( !sCmd.isEmpty()){
5399                             uno::Sequence<beans::PropertyValue> aValues( comphelper::InitPropertySequence({
5400                                 { "Identifier", uno::Any(sCmd) }
5401                             }));
5402                             xTC->setPropertyValue("Fields", uno::makeAny(aValues));
5403                         }
5404                         uno::Reference< text::XTextContent > xToInsert( xTC, uno::UNO_QUERY );
5405 
5406                         uno::Sequence<beans::PropertyValue> aValues
5407                             = m_aFieldStack.back()->getProperties()->GetPropertyValues();
5408                         appendTextContent(xToInsert, aValues);
5409                         m_bSetCitation = true;
5410                     }
5411                     break;
5412 
5413                     case FIELD_TC :
5414                     {
5415                         if( !m_xTextFactory.is() )
5416                             break;
5417 
5418                         uno::Reference< beans::XPropertySet > xTC(
5419                             m_xTextFactory->createInstance(
5420                                 OUString::createFromAscii(aIt->second.cFieldServiceName)),
5421                                 uno::UNO_QUERY_THROW);
5422                         if (!sFirstParam.isEmpty())
5423                         {
5424                             xTC->setPropertyValue(getPropertyName(PROP_ALTERNATIVE_TEXT),
5425                                 uno::makeAny(sFirstParam));
5426                         }
5427                         OUString sValue;
5428                         // \f TC entry in doc with multiple tables
5429     //                    if( lcl_FindInCommand( pContext->GetCommand(), 'f', sValue ))
5430     //                    {
5431                             // todo: unsupported
5432     //                    }
5433                         if( lcl_FindInCommand( pContext->GetCommand(), 'l', sValue ))
5434                         // \l Outline Level
5435                         {
5436                             sal_Int32 nLevel = sValue.toInt32();
5437                             if( !sValue.isEmpty() && nLevel >= 0 && nLevel <= 10 )
5438                                 xTC->setPropertyValue(getPropertyName(PROP_LEVEL), uno::makeAny( static_cast<sal_Int16>(nLevel) ));
5439                         }
5440     //                    if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
5441     //                    \n Suppress page numbers
5442     //                    {
5443                             //todo: unsupported feature
5444     //                    }
5445                         pContext->SetTC( xTC );
5446                     }
5447                     break;
5448                     case  FIELD_NUMCHARS:
5449                     case  FIELD_NUMWORDS:
5450                     case  FIELD_NUMPAGES:
5451                     if (xFieldProperties.is())
5452                         xFieldProperties->setPropertyValue(
5453                             getPropertyName(PROP_NUMBERING_TYPE),
5454                             uno::makeAny( lcl_ParseNumberingType(pContext->GetCommand()) ));
5455                     break;
5456                 }
5457             }
5458             else
5459             {
5460                 /* Unsupported fields will be handled here for docx file.
5461                  * To handle unsupported fields used fieldmark API.
5462                  */
5463                 OUString aCode( pContext->GetCommand().trim() );
5464                 // Don't waste resources on wrapping shapes inside a fieldmark.
5465                 if (std::get<0>(field) != "SHAPE" && m_xTextFactory.is() && !m_aTextAppendStack.empty())
5466                 {
5467                     xFieldInterface = m_xTextFactory->createInstance("com.sun.star.text.Fieldmark");
5468 
5469                     uno::Reference<text::XFormField> const xFormField(xFieldInterface, uno::UNO_QUERY);
5470                     InsertFieldmark(m_aTextAppendStack, xFormField, pContext->GetStartRange(),
5471                             pContext->GetFieldId());
5472                     xFormField->setFieldType(ODF_UNHANDLED);
5473                     ++m_nStartGenericField;
5474                     pContext->SetFormField( xFormField );
5475                     uno::Reference<container::XNameContainer> const xNameCont(xFormField->getParameters());
5476                     // note: setting the code to empty string is *required* in
5477                     // m_bForceGenericFields mode, or the export will write
5478                     // the ODF_UNHANDLED string!
5479                     assert(!m_bForceGenericFields || aCode.isEmpty());
5480                     xNameCont->insertByName(ODF_CODE_PARAM, uno::makeAny(aCode));
5481                     ww::eField const id(GetWW8FieldId(std::get<0>(field)));
5482                     if (id != ww::eNONE)
5483                     {   // tdf#129247 tdf#134264 set WW8 id for WW8 export
5484                         xNameCont->insertByName(ODF_ID_PARAM, uno::makeAny(OUString::number(id)));
5485                     }
5486                 }
5487                 else
5488                     m_bParaHadField = false;
5489             }
5490             //set the text field if there is any
5491             pContext->SetTextField( uno::Reference< text::XTextField >( xFieldInterface, uno::UNO_QUERY ) );
5492         }
5493         catch( const uno::Exception& )
5494         {
5495             TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "Exception in CloseFieldCommand()" );
5496         }
5497         pContext->SetCommandCompleted();
5498     }
5499 }
5500 /*-------------------------------------------------------------------------
5501 //the _current_ fields require a string type result while TOCs accept richt results
5502   -----------------------------------------------------------------------*/
IsFieldResultAsString()5503 bool DomainMapper_Impl::IsFieldResultAsString()
5504 {
5505     bool bRet = false;
5506     OSL_ENSURE( !m_aFieldStack.empty(), "field stack empty?");
5507     FieldContextPtr pContext = m_aFieldStack.back();
5508     OSL_ENSURE( pContext.get(), "no field context available");
5509     if( pContext.get() )
5510     {
5511         bRet = pContext->GetTextField().is()
5512             || pContext->GetFieldId() == FIELD_FORMDROPDOWN
5513             || pContext->GetFieldId() == FIELD_FILLIN;
5514     }
5515 
5516     if (!bRet)
5517     {
5518         FieldContextPtr pOuter = GetParentFieldContext(m_aFieldStack);
5519         if (pOuter)
5520         {
5521             if (!IsFieldNestingAllowed(pOuter, m_aFieldStack.back()))
5522             {
5523                 // Child field has no backing SwField, but the parent has: append is still possible.
5524                 bRet = pOuter->GetTextField().is();
5525             }
5526         }
5527     }
5528     return bRet;
5529 }
5530 
AppendFieldResult(OUString const & rString)5531 void DomainMapper_Impl::AppendFieldResult(OUString const& rString)
5532 {
5533     assert(!m_aFieldStack.empty());
5534     FieldContextPtr pContext = m_aFieldStack.back();
5535     SAL_WARN_IF(!pContext.get(), "writerfilter.dmapper", "no field context");
5536     if (pContext.get())
5537     {
5538         FieldContextPtr pOuter = GetParentFieldContext(m_aFieldStack);
5539         if (pOuter)
5540         {
5541             if (!IsFieldNestingAllowed(pOuter, pContext))
5542             {
5543                 // Child can't host the field result, forward to parent.
5544                 pOuter->AppendResult(rString);
5545                 return;
5546             }
5547         }
5548 
5549         pContext->AppendResult(rString);
5550     }
5551 }
5552 
5553 // Calculates css::DateTime based on ddddd.sssss since 1900-1-0
lcl_dateTimeFromSerial(const double & dSerial)5554 static util::DateTime lcl_dateTimeFromSerial(const double& dSerial)
5555 {
5556     const sal_uInt32 secondsPerDay = 86400;
5557     const sal_uInt16 secondsPerHour = 3600;
5558 
5559     DateTime d(Date(30, 12, 1899));
5560     d.AddDays( static_cast<sal_Int32>(dSerial) );
5561 
5562     double frac = std::modf(dSerial, &o3tl::temporary(double()));
5563     sal_uInt32 seconds = frac * secondsPerDay;
5564 
5565     util::DateTime date;
5566     date.Year = d.GetYear();
5567     date.Month = d.GetMonth();
5568     date.Day = d.GetDay();
5569     date.Hours = seconds / secondsPerHour;
5570     date.Minutes = (seconds % secondsPerHour) / 60;
5571     date.Seconds = seconds % 60;
5572 
5573     return date;
5574 }
5575 
SetFieldResult(OUString const & rResult)5576 void DomainMapper_Impl::SetFieldResult(OUString const& rResult)
5577 {
5578 #ifdef DBG_UTIL
5579     TagLogger::getInstance().startElement("setFieldResult");
5580     TagLogger::getInstance().chars(rResult);
5581 #endif
5582 
5583     FieldContextPtr pContext = m_aFieldStack.back();
5584     OSL_ENSURE( pContext.get(), "no field context available");
5585 
5586     if (m_aFieldStack.size() > 1)
5587     {
5588         // This is a nested field. See if the parent supports nesting on the Writer side.
5589         FieldContextPtr pParentContext = m_aFieldStack[m_aFieldStack.size() - 2];
5590         if (pParentContext)
5591         {
5592             std::vector<OUString> aParentParts = pParentContext->GetCommandParts();
5593             // Conditional text fields don't support nesting in Writer.
5594             if (!aParentParts.empty() && aParentParts[0] == "IF")
5595             {
5596                 return;
5597             }
5598         }
5599     }
5600 
5601     if( pContext.get() )
5602     {
5603         uno::Reference<text::XTextField> xTextField = pContext->GetTextField();
5604         try
5605         {
5606             OSL_ENSURE( xTextField.is()
5607             //||m_xTOC.is() ||m_xTC.is()
5608             //||m_sHyperlinkURL.getLength()
5609             , "DomainMapper_Impl::SetFieldResult: field not created" );
5610             if(xTextField.is())
5611             {
5612                 try
5613                 {
5614                     if( m_bSetUserFieldContent )
5615                     {
5616                         // user field content has to be set at the field master
5617                         uno::Reference< text::XDependentTextField > xDependentField( xTextField, uno::UNO_QUERY_THROW );
5618                         xDependentField->getTextFieldMaster()->setPropertyValue(
5619                                 getPropertyName(PROP_CONTENT),
5620                              uno::makeAny( rResult ));
5621                     }
5622                     else if ( m_bSetCitation )
5623                     {
5624 
5625                         uno::Reference< beans::XPropertySet > xFieldProperties( xTextField, uno::UNO_QUERY_THROW);
5626                         // In case of SetExpression, the field result contains the content of the variable.
5627                         uno::Reference<lang::XServiceInfo> xServiceInfo(xTextField, uno::UNO_QUERY);
5628 
5629                         bool bIsSetbiblio = xServiceInfo->supportsService("com.sun.star.text.TextField.Bibliography");
5630                         if( bIsSetbiblio )
5631                         {
5632                             uno::Any aProperty  = xFieldProperties->getPropertyValue("Fields");
5633                             uno::Sequence<beans::PropertyValue> aValues ;
5634                             aProperty >>= aValues;
5635                             beans::PropertyValue propertyVal;
5636                             sal_Int32 nTitleFoundIndex = -1;
5637                             for (sal_Int32 i = 0; i < aValues.getLength(); ++i)
5638                             {
5639                                 propertyVal = aValues[i];
5640                                 if (propertyVal.Name == "Title")
5641                                 {
5642                                     nTitleFoundIndex = i;
5643                                     break;
5644                                 }
5645                             }
5646                             if (nTitleFoundIndex != -1)
5647                             {
5648                                 OUString titleStr;
5649                                 uno::Any aValue(propertyVal.Value);
5650                                 aValue >>= titleStr;
5651                                 titleStr += rResult;
5652                                 propertyVal.Value <<= titleStr;
5653                                 aValues[nTitleFoundIndex] = propertyVal;
5654                             }
5655                             else
5656                             {
5657                                 aValues.realloc(aValues.getLength() + 1);
5658                                 propertyVal.Name = "Title";
5659                                 propertyVal.Value <<= rResult;
5660                                 aValues[aValues.getLength() - 1] = propertyVal;
5661                             }
5662                             xFieldProperties->setPropertyValue("Fields",
5663                                     uno::makeAny(aValues));
5664                         }
5665                     }
5666                     else if ( m_bSetDateValue )
5667                     {
5668                         uno::Reference< util::XNumberFormatsSupplier > xNumberSupplier( m_xTextDocument, uno::UNO_QUERY_THROW );
5669 
5670                         uno::Reference<util::XNumberFormatter> xFormatter(util::NumberFormatter::create(m_xComponentContext), uno::UNO_QUERY_THROW);
5671                         xFormatter->attachNumberFormatsSupplier( xNumberSupplier );
5672                         sal_Int32 nKey = 0;
5673 
5674                         uno::Reference< beans::XPropertySet > xFieldProperties( xTextField, uno::UNO_QUERY_THROW);
5675 
5676                         xFieldProperties->getPropertyValue( "NumberFormat" ) >>= nKey;
5677                         xFieldProperties->setPropertyValue(
5678                             "DateTimeValue",
5679                             uno::makeAny( lcl_dateTimeFromSerial( xFormatter->convertStringToNumber( nKey, rResult ) ) ) );
5680                     }
5681                     else
5682                     {
5683                         uno::Reference< beans::XPropertySet > xFieldProperties( xTextField, uno::UNO_QUERY_THROW);
5684                         // In case of SetExpression, and Input fields the field result contains the content of the variable.
5685                         uno::Reference<lang::XServiceInfo> xServiceInfo(xTextField, uno::UNO_QUERY);
5686                         // there are fields with a content property, which aren't working correctly with
5687                         // a generalized try catch of the content, property, so just restrict content
5688                         // handling to these explicit services.
5689                         const bool bHasContent = xServiceInfo->supportsService("com.sun.star.text.TextField.SetExpression") ||
5690                             xServiceInfo->supportsService("com.sun.star.text.TextField.Input");
5691                         // If we already have content set, then use the current presentation
5692                         OUString sValue;
5693                         if (bHasContent)
5694                         {
5695                             // this will throw for field types without Content
5696                             uno::Any aValue(xFieldProperties->getPropertyValue(
5697                                     getPropertyName(PROP_CONTENT)));
5698                             aValue >>= sValue;
5699                         }
5700                         xFieldProperties->setPropertyValue(
5701                                 getPropertyName(bHasContent && sValue.isEmpty()? PROP_CONTENT : PROP_CURRENT_PRESENTATION),
5702                              uno::makeAny( rResult ));
5703                     }
5704                 }
5705                 catch( const beans::UnknownPropertyException& )
5706                 {
5707                     //some fields don't have a CurrentPresentation (DateTime)
5708                 }
5709             }
5710         }
5711         catch (const uno::Exception&)
5712         {
5713             TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "DomainMapper_Impl::SetFieldResult");
5714         }
5715     }
5716 }
5717 
SetFieldFFData(const FFDataHandler::Pointer_t & pFFDataHandler)5718 void DomainMapper_Impl::SetFieldFFData(const FFDataHandler::Pointer_t& pFFDataHandler)
5719 {
5720 #ifdef DBG_UTIL
5721     TagLogger::getInstance().startElement("setFieldFFData");
5722 #endif
5723 
5724     if (!m_aFieldStack.empty())
5725     {
5726         FieldContextPtr pContext = m_aFieldStack.back();
5727         if (pContext.get())
5728         {
5729             pContext->setFFDataHandler(pFFDataHandler);
5730         }
5731     }
5732 
5733 #ifdef DBG_UTIL
5734     TagLogger::getInstance().endElement();
5735 #endif
5736 }
5737 
PopFieldContext()5738 void DomainMapper_Impl::PopFieldContext()
5739 {
5740     if(m_bDiscardHeaderFooter)
5741         return;
5742 #ifdef DBG_UTIL
5743     TagLogger::getInstance().element("popFieldContext");
5744 #endif
5745 
5746     if (m_aFieldStack.empty())
5747         return;
5748 
5749     FieldContextPtr pContext = m_aFieldStack.back();
5750     OSL_ENSURE( pContext.get(), "no field context available");
5751     if( pContext.get() )
5752     {
5753         if( !pContext->IsCommandCompleted() )
5754             CloseFieldCommand();
5755 
5756         if (!pContext->GetResult().isEmpty())
5757         {
5758            uno::Reference< beans::XPropertySet > xFieldProperties = pContext->GetCustomField();
5759            if(xFieldProperties.is())
5760               SetNumberFormat( pContext->GetResult(), xFieldProperties, true );
5761            SetFieldResult( pContext->GetResult() );
5762          }
5763 
5764         //insert the field, TC or TOC
5765         uno::Reference< text::XTextAppend >  xTextAppend;
5766         if (!m_aTextAppendStack.empty())
5767             xTextAppend = m_aTextAppendStack.top().xTextAppend;
5768         if(xTextAppend.is())
5769         {
5770             try
5771             {
5772                 uno::Reference< text::XTextCursor > xCrsr = xTextAppend->createTextCursorByRange(pContext->GetStartRange());
5773                 uno::Reference< text::XTextContent > xToInsert( pContext->GetTOC(), uno::UNO_QUERY );
5774                 if( xToInsert.is() )
5775                 {
5776                     if (m_bStartedTOC || m_bStartIndex || m_bStartBibliography)
5777                     {
5778                         // inside Std, last empty paragraph is also part of index
5779                         if (!m_bParaChanged && !m_xStdEntryStart)
5780                         {
5781                             // End of index is the first item on a new paragraph - this paragraph
5782                             // should not be part of index
5783                             auto xCursor
5784                                 = xTextAppend->createTextCursorByRange(xTextAppend->getEnd());
5785                             xCursor->gotoEnd(false);
5786                             xCursor->goLeft(1, true);
5787                             // delete
5788                             xCursor->setString(OUString());
5789                             // But a new paragraph should be started after the index instead
5790                             xTextAppend->finishParagraph(css::beans::PropertyValues());
5791                         }
5792                         m_bStartedTOC = false;
5793                         m_aTextAppendStack.pop();
5794                         m_bTextInserted = false;
5795                         m_bParaChanged = true; // the paragraph must stay anyway
5796                     }
5797                     m_bStartTOC = false;
5798                     m_bStartIndex = false;
5799                     m_bStartBibliography = false;
5800                     if (IsInHeaderFooter() && m_bStartTOCHeaderFooter)
5801                         m_bStartTOCHeaderFooter = false;
5802                 }
5803                 else
5804                 {
5805                     xToInsert.set(pContext->GetTC(), uno::UNO_QUERY);
5806                     if( !xToInsert.is() && !m_bStartTOC && !m_bStartIndex && !m_bStartBibliography )
5807                         xToInsert = pContext->GetTextField();
5808                     if( xToInsert.is() && !m_bStartTOC && !m_bStartIndex && !m_bStartBibliography)
5809                     {
5810                         PropertyMap aMap;
5811                         // Character properties of the field show up here the
5812                         // last (always empty) run. Inherit character
5813                         // properties from there.
5814                         // Also merge in the properties from the field context,
5815                         // e.g. SdtEndBefore.
5816                         if (m_pLastCharacterContext.get())
5817                             aMap.InsertProps(m_pLastCharacterContext);
5818                         aMap.InsertProps(m_aFieldStack.back()->getProperties());
5819                         appendTextContent(xToInsert, aMap.GetPropertyValues());
5820                         CheckRedline( xToInsert->getAnchor( ) );
5821                     }
5822                     else
5823                     {
5824                         FormControlHelper::Pointer_t pFormControlHelper(pContext->getFormControlHelper());
5825                         if (pFormControlHelper.get() != nullptr)
5826                         {
5827                             uno::Reference< text::XFormField > xFormField( pContext->GetFormField() );
5828                             assert(xCrsr.is());
5829                             if (pFormControlHelper->hasFFDataHandler())
5830                             {
5831                                 xToInsert.set(xFormField, uno::UNO_QUERY);
5832                                 if (xFormField.is() && xToInsert.is())
5833                                 {
5834                                     PopFieldmark(m_aTextAppendStack, xCrsr,
5835                                         pContext->GetFieldId());
5836                                     pFormControlHelper->processField( xFormField );
5837                                 }
5838                                 else
5839                                 {
5840                                     pFormControlHelper->insertControl(xCrsr);
5841                                 }
5842                             }
5843                             else
5844                             {
5845                                 PopFieldmark(m_aTextAppendStack, xCrsr,
5846                                         pContext->GetFieldId());
5847                                 uno::Reference<lang::XComponent>(xFormField, uno::UNO_QUERY_THROW)->dispose(); // presumably invalid?
5848                             }
5849                         }
5850                         else if (!pContext->GetHyperlinkURL().isEmpty() && xCrsr.is())
5851                         {
5852                             xCrsr->gotoEnd( true );
5853 
5854                             // Draw components (like comments) need hyperlinks set differently
5855                             SvxUnoTextRangeBase* pDrawText = dynamic_cast<SvxUnoTextRangeBase*>(xCrsr.get());
5856                             if ( pDrawText )
5857                                 pDrawText->attachField( std::make_unique<SvxURLField>(pContext->GetHyperlinkURL(), xCrsr->getString(), SvxURLFormat::AppDefault) );
5858                             else
5859                             {
5860                                 uno::Reference< beans::XPropertySet > xCrsrProperties( xCrsr, uno::UNO_QUERY_THROW );
5861                                 xCrsrProperties->setPropertyValue(getPropertyName(PROP_HYPER_LINK_U_R_L), uno::
5862                                                                   makeAny(pContext->GetHyperlinkURL()));
5863 
5864                                 if (!pContext->GetHyperlinkTarget().isEmpty())
5865                                     xCrsrProperties->setPropertyValue("HyperLinkTarget", uno::makeAny(pContext->GetHyperlinkTarget()));
5866 
5867                                 if (m_bStartTOC) {
5868                                     OUString sDisplayName("Index Link");
5869                                     xCrsrProperties->setPropertyValue("VisitedCharStyleName",uno::makeAny(sDisplayName));
5870                                     xCrsrProperties->setPropertyValue("UnvisitedCharStyleName",uno::makeAny(sDisplayName));
5871                                 }
5872                                 else
5873                                 {
5874                                     uno::Any aAny = xCrsrProperties->getPropertyValue("CharStyleName");
5875                                     OUString charStyle;
5876                                     if (css::uno::fromAny(aAny, &charStyle))
5877                                     {
5878                                         if (charStyle.isEmpty())
5879                                         {
5880                                             xCrsrProperties->setPropertyValue("VisitedCharStyleName", uno::makeAny(OUString("Default Style")));
5881                                             xCrsrProperties->setPropertyValue("UnvisitedCharStyleName", uno::makeAny(OUString("Default Style")));
5882                                         }
5883                                         else if (charStyle.equalsIgnoreAsciiCase("Internet Link"))
5884                                         {
5885                                             xCrsrProperties->setPropertyValue("CharStyleName", uno::makeAny(OUString("Default Style")));
5886                                         }
5887                                         else
5888                                         {
5889                                             xCrsrProperties->setPropertyValue("VisitedCharStyleName", aAny);
5890                                             xCrsrProperties->setPropertyValue("UnvisitedCharStyleName", aAny);
5891                                         }
5892                                     }
5893                                 }
5894                             }
5895                         }
5896                         else if (m_nStartGenericField != 0)
5897                         {
5898                             --m_nStartGenericField;
5899                             PopFieldmark(m_aTextAppendStack, xCrsr, pContext->GetFieldId());
5900                             if(m_bTextInserted)
5901                             {
5902                                 m_bTextInserted = false;
5903                             }
5904                         }
5905                     }
5906                 }
5907             }
5908             catch(const lang::IllegalArgumentException&)
5909             {
5910                 OSL_FAIL( "IllegalArgumentException in PopFieldContext()" );
5911             }
5912             catch(const uno::Exception&)
5913             {
5914                 OSL_FAIL( "exception in PopFieldContext()" );
5915             }
5916         }
5917 
5918         //TOCs have to include all the imported content
5919     }
5920 
5921     std::vector<FieldParagraph> aParagraphsToFinish;
5922     if (pContext)
5923     {
5924         aParagraphsToFinish = pContext->GetParagraphsToFinish();
5925     }
5926 
5927     //remove the field context
5928     m_aFieldStack.pop_back();
5929 
5930     // Finish the paragraph(s) now that the field is closed.
5931     for (const auto& rFinish : aParagraphsToFinish)
5932     {
5933         finishParagraph(rFinish.m_pPropertyMap, rFinish.m_bRemove);
5934     }
5935 }
5936 
5937 
SetBookmarkName(const OUString & rBookmarkName)5938 void DomainMapper_Impl::SetBookmarkName( const OUString& rBookmarkName )
5939 {
5940     BookmarkMap_t::iterator aBookmarkIter = m_aBookmarkMap.find( m_sCurrentBkmkId );
5941     if( aBookmarkIter != m_aBookmarkMap.end() )
5942     {
5943         // fields are internal bookmarks: consume redundant "normal" bookmark
5944         if ( IsOpenField() )
5945         {
5946             FFDataHandler::Pointer_t  pFFDataHandler(GetTopFieldContext()->getFFDataHandler());
5947             if (pFFDataHandler && pFFDataHandler->getName() == rBookmarkName)
5948             {
5949                 // HACK: At the END marker, StartOrEndBookmark will START
5950                 // a bookmark which will eventually be abandoned, not created.
5951                 m_aBookmarkMap.erase(aBookmarkIter);
5952                 return;
5953             }
5954         }
5955 
5956         aBookmarkIter->second.m_sBookmarkName = rBookmarkName;
5957     }
5958     else
5959         m_sCurrentBkmkName = rBookmarkName;
5960 }
5961 
5962 // This method was used as-is for DomainMapper_Impl::startOrEndPermissionRange() implementation.
StartOrEndBookmark(const OUString & rId)5963 void DomainMapper_Impl::StartOrEndBookmark( const OUString& rId )
5964 {
5965     /*
5966      * Add the dummy paragraph to handle section properties
5967      * iff the first element in the section is a table. If the dummy para is not added yet, then add it;
5968      * So bookmark is not attached to the wrong paragraph.
5969      */
5970     if(hasTableManager() && getTableManager().isInCell() && m_nTableDepth == 0 && GetIsFirstParagraphInSection()
5971                     && !GetIsDummyParaAddedForTableInSection() &&!GetIsTextFrameInserted())
5972     {
5973         AddDummyParaForTableInSection();
5974     }
5975 
5976     bool bIsAfterDummyPara = GetIsDummyParaAddedForTableInSection() && GetIsFirstParagraphInSection();
5977     if (m_aTextAppendStack.empty())
5978         return;
5979     uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
5980     BookmarkMap_t::iterator aBookmarkIter = m_aBookmarkMap.find( rId );
5981     //is the bookmark name already registered?
5982     try
5983     {
5984         if( aBookmarkIter != m_aBookmarkMap.end() )
5985         {
5986             if (m_xTextFactory.is())
5987             {
5988                 uno::Reference< text::XTextContent > xBookmark( m_xTextFactory->createInstance( "com.sun.star.text.Bookmark" ), uno::UNO_QUERY_THROW );
5989                 uno::Reference< text::XTextCursor > xCursor;
5990                 uno::Reference< text::XText > xText = aBookmarkIter->second.m_xTextRange->getText();
5991                 if( aBookmarkIter->second.m_bIsStartOfText && !bIsAfterDummyPara)
5992                 {
5993                     xCursor = xText->createTextCursorByRange( xText->getStart() );
5994                 }
5995                 else
5996                 {
5997                     xCursor = xText->createTextCursorByRange( aBookmarkIter->second.m_xTextRange );
5998                     xCursor->goRight( 1, false );
5999                 }
6000 
6001                 xCursor->gotoRange( xTextAppend->getEnd(), true );
6002                 // A Paragraph was recently finished, and a new Paragraph has not been started as yet
6003                 // then  move the bookmark-End to the earlier paragraph
6004                 if (IsOutsideAParagraph())
6005                 {
6006                     xCursor->goLeft( 1, false );
6007                 }
6008                 uno::Reference< container::XNamed > xBkmNamed( xBookmark, uno::UNO_QUERY_THROW );
6009                 assert(!aBookmarkIter->second.m_sBookmarkName.isEmpty());
6010                 //todo: make sure the name is not used already!
6011                 xBkmNamed->setName( aBookmarkIter->second.m_sBookmarkName );
6012                 xTextAppend->insertTextContent( uno::Reference< text::XTextRange >( xCursor, uno::UNO_QUERY_THROW), xBookmark, !xCursor->isCollapsed() );
6013             }
6014             m_aBookmarkMap.erase( aBookmarkIter );
6015             m_sCurrentBkmkId.clear();
6016         }
6017         else
6018         {
6019             //otherwise insert a text range as marker
6020             bool bIsStart = true;
6021             uno::Reference< text::XTextRange > xCurrent;
6022             if (xTextAppend.is())
6023             {
6024                 uno::Reference<text::XTextCursor> const xCursor =
6025                     xTextAppend->createTextCursorByRange(
6026                         m_aTextAppendStack.top().xInsertPosition.is()
6027                             ? m_aTextAppendStack.top().xInsertPosition
6028                             : xTextAppend->getEnd() );
6029 
6030                 if (!xCursor)
6031                     return;
6032 
6033                 if (!bIsAfterDummyPara)
6034                     bIsStart = !xCursor->goLeft(1, false);
6035                 xCurrent = xCursor->getStart();
6036             }
6037             m_sCurrentBkmkId = rId;
6038             m_aBookmarkMap.emplace( rId, BookmarkInsertPosition( bIsStart, m_sCurrentBkmkName, xCurrent ) );
6039             m_sCurrentBkmkName.clear();
6040         }
6041     }
6042     catch( const uno::Exception& )
6043     {
6044         //TODO: What happens to bookmarks where start and end are at different XText objects?
6045     }
6046 }
6047 
setPermissionRangeEd(const OUString & user)6048 void DomainMapper_Impl::setPermissionRangeEd(const OUString& user)
6049 {
6050     PermMap_t::iterator aPremIter = m_aPermMap.find(m_sCurrentPermId);
6051     if (aPremIter != m_aPermMap.end())
6052         aPremIter->second.m_Ed = user;
6053     else
6054         m_sCurrentPermEd = user;
6055 }
6056 
setPermissionRangeEdGrp(const OUString & group)6057 void DomainMapper_Impl::setPermissionRangeEdGrp(const OUString& group)
6058 {
6059     PermMap_t::iterator aPremIter = m_aPermMap.find(m_sCurrentPermId);
6060     if (aPremIter != m_aPermMap.end())
6061         aPremIter->second.m_EdGrp = group;
6062     else
6063         m_sCurrentPermEdGrp = group;
6064 }
6065 
6066 // This method is based on implementation from DomainMapper_Impl::StartOrEndBookmark()
startOrEndPermissionRange(sal_Int32 permissinId)6067 void DomainMapper_Impl::startOrEndPermissionRange(sal_Int32 permissinId)
6068 {
6069     /*
6070     * Add the dummy paragraph to handle section properties
6071     * if the first element in the section is a table. If the dummy para is not added yet, then add it;
6072     * So permission is not attached to the wrong paragraph.
6073     */
6074     if (getTableManager().isInCell() && m_nTableDepth == 0 && GetIsFirstParagraphInSection()
6075         && !GetIsDummyParaAddedForTableInSection() && !GetIsTextFrameInserted())
6076     {
6077         AddDummyParaForTableInSection();
6078     }
6079 
6080     if (m_aTextAppendStack.empty())
6081         return;
6082 
6083     const bool bIsAfterDummyPara = GetIsDummyParaAddedForTableInSection() && GetIsFirstParagraphInSection();
6084 
6085     uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
6086     PermMap_t::iterator aPermIter = m_aPermMap.find(permissinId);
6087 
6088     //is the bookmark name already registered?
6089     try
6090     {
6091         if (aPermIter == m_aPermMap.end())
6092         {
6093             //otherwise insert a text range as marker
6094             bool bIsStart = true;
6095             uno::Reference< text::XTextRange > xCurrent;
6096             if (xTextAppend.is())
6097             {
6098                 uno::Reference< text::XTextCursor > xCursor = xTextAppend->createTextCursorByRange(xTextAppend->getEnd());
6099 
6100                 if (!bIsAfterDummyPara)
6101                     bIsStart = !xCursor->goLeft(1, false);
6102                 xCurrent = xCursor->getStart();
6103             }
6104 
6105             // register the start of the new permission
6106             m_sCurrentPermId = permissinId;
6107             m_aPermMap.emplace(permissinId, PermInsertPosition(bIsStart, permissinId, m_sCurrentPermEd, m_sCurrentPermEdGrp, xCurrent));
6108 
6109             // clean up
6110             m_sCurrentPermEd.clear();
6111             m_sCurrentPermEdGrp.clear();
6112         }
6113         else
6114         {
6115             if (m_xTextFactory.is())
6116             {
6117                 uno::Reference< text::XTextCursor > xCursor;
6118                 uno::Reference< text::XText > xText = aPermIter->second.m_xTextRange->getText();
6119                 if (aPermIter->second.m_bIsStartOfText && !bIsAfterDummyPara)
6120                 {
6121                     xCursor = xText->createTextCursorByRange(xText->getStart());
6122                 }
6123                 else
6124                 {
6125                     xCursor = xText->createTextCursorByRange(aPermIter->second.m_xTextRange);
6126                     xCursor->goRight(1, false);
6127                 }
6128 
6129                 xCursor->gotoRange(xTextAppend->getEnd(), true);
6130                 // A Paragraph was recently finished, and a new Paragraph has not been started as yet
6131                 // then  move the bookmark-End to the earlier paragraph
6132                 if (IsOutsideAParagraph())
6133                 {
6134                     xCursor->goLeft(1, false);
6135                 }
6136 
6137                 // create a new bookmark using specific bookmark name pattern for permissions
6138                 uno::Reference< text::XTextContent > xPerm(m_xTextFactory->createInstance("com.sun.star.text.Bookmark"), uno::UNO_QUERY_THROW);
6139                 uno::Reference< container::XNamed > xPermNamed(xPerm, uno::UNO_QUERY_THROW);
6140                 xPermNamed->setName(aPermIter->second.createBookmarkName());
6141 
6142                 // add new bookmark
6143                 const bool bAbsorb = !xCursor->isCollapsed();
6144                 uno::Reference< text::XTextRange > xCurrent(xCursor, uno::UNO_QUERY_THROW);
6145                 xTextAppend->insertTextContent(xCurrent, xPerm, bAbsorb);
6146             }
6147 
6148             // remove processed permission
6149             m_aPermMap.erase(aPermIter);
6150 
6151             // clean up
6152             m_sCurrentPermId = 0;
6153             m_sCurrentPermEd.clear();
6154             m_sCurrentPermEdGrp.clear();
6155         }
6156     }
6157     catch (const uno::Exception&)
6158     {
6159         //TODO: What happens to bookmarks where start and end are at different XText objects?
6160     }
6161 }
6162 
AddAnnotationPosition(const bool bStart,const sal_Int32 nAnnotationId)6163 void DomainMapper_Impl::AddAnnotationPosition(
6164     const bool bStart,
6165     const sal_Int32 nAnnotationId)
6166 {
6167     if (m_aTextAppendStack.empty())
6168         return;
6169 
6170     // Create a cursor, pointing to the current position.
6171     uno::Reference<text::XTextAppend>  xTextAppend = m_aTextAppendStack.top().xTextAppend;
6172     uno::Reference<text::XTextRange> xCurrent;
6173     if (xTextAppend.is())
6174     {
6175         uno::Reference<text::XTextCursor> xCursor;
6176         if (m_bIsNewDoc)
6177             xCursor = xTextAppend->createTextCursorByRange(xTextAppend->getEnd());
6178         else
6179             xCursor = m_aTextAppendStack.top().xCursor;
6180         if (xCursor.is())
6181             xCurrent = xCursor->getStart();
6182     }
6183 
6184     // And save it, to be used by PopAnnotation() later.
6185     AnnotationPosition& aAnnotationPosition = m_aAnnotationPositions[ nAnnotationId ];
6186     if (bStart)
6187     {
6188         aAnnotationPosition.m_xStart = xCurrent;
6189     }
6190     else
6191     {
6192         aAnnotationPosition.m_xEnd = xCurrent;
6193     }
6194     m_aAnnotationPositions[ nAnnotationId ] = aAnnotationPosition;
6195 }
6196 
GetGraphicImport(GraphicImportType eGraphicImportType)6197 GraphicImportPtr const & DomainMapper_Impl::GetGraphicImport(GraphicImportType eGraphicImportType)
6198 {
6199     if(!m_pGraphicImport)
6200         m_pGraphicImport = new GraphicImport( m_xComponentContext, m_xTextFactory, m_rDMapper, eGraphicImportType, m_aPositionOffsets, m_aAligns, m_aPositivePercentages );
6201     return m_pGraphicImport;
6202 }
6203 /*-------------------------------------------------------------------------
6204     reset graphic import if the last import resulted in a shape, not a graphic
6205   -----------------------------------------------------------------------*/
ResetGraphicImport()6206 void DomainMapper_Impl::ResetGraphicImport()
6207 {
6208     m_pGraphicImport.clear();
6209 }
6210 
6211 
ImportGraphic(const writerfilter::Reference<Properties>::Pointer_t & ref,GraphicImportType eGraphicImportType)6212 void  DomainMapper_Impl::ImportGraphic(const writerfilter::Reference< Properties >::Pointer_t& ref, GraphicImportType eGraphicImportType)
6213 {
6214     GetGraphicImport(eGraphicImportType);
6215     if( eGraphicImportType != IMPORT_AS_DETECTED_INLINE && eGraphicImportType != IMPORT_AS_DETECTED_ANCHOR )
6216     {
6217         //create the graphic
6218         ref->resolve( *m_pGraphicImport );
6219     }
6220 
6221     //insert it into the document at the current cursor position
6222 
6223     uno::Reference<text::XTextContent> xTextContent
6224         (m_pGraphicImport->GetGraphicObject());
6225 
6226     // In case the SDT starts with the text portion of the graphic, then set the SDT properties here.
6227     bool bHasGrabBag = false;
6228     uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
6229     if (xPropertySet.is())
6230     {
6231         uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
6232         bHasGrabBag = xPropertySetInfo->hasPropertyByName("FrameInteropGrabBag");
6233         // In case we're outside a paragraph, then the SDT properties are stored in the paragraph grab-bag, not the frame one.
6234         if (!m_pSdtHelper->isInteropGrabBagEmpty() && bHasGrabBag && !m_pSdtHelper->isOutsideAParagraph())
6235         {
6236             comphelper::SequenceAsHashMap aFrameGrabBag(xPropertySet->getPropertyValue("FrameInteropGrabBag"));
6237             aFrameGrabBag["SdtPr"] <<= m_pSdtHelper->getInteropGrabBagAndClear();
6238             xPropertySet->setPropertyValue("FrameInteropGrabBag", uno::makeAny(aFrameGrabBag.getAsConstPropertyValueList()));
6239         }
6240     }
6241 
6242     /* Set "SdtEndBefore" property on Drawing.
6243      * It is required in a case when Drawing appears immediately after first run i.e.
6244      * there is no text/space/tab in between two runs.
6245      * In this case "SdtEndBefore" property needs to be set on Drawing.
6246      */
6247     if(IsSdtEndBefore())
6248     {
6249         if(xPropertySet.is() && bHasGrabBag)
6250         {
6251             uno::Sequence<beans::PropertyValue> aFrameGrabBag( comphelper::InitPropertySequence({
6252                 { "SdtEndBefore", uno::Any(true) }
6253             }));
6254             xPropertySet->setPropertyValue("FrameInteropGrabBag",uno::makeAny(aFrameGrabBag));
6255         }
6256     }
6257 
6258 
6259     // Update the shape properties if it is embedded object.
6260     if(m_xEmbedded.is()){
6261         if (m_pGraphicImport->GetXShapeObject())
6262                 m_pGraphicImport->GetXShapeObject()->setPosition(
6263                     m_pGraphicImport->GetGraphicObjectPosition());
6264 
6265         uno::Reference<drawing::XShape> xShape = m_pGraphicImport->GetXShapeObject();
6266         UpdateEmbeddedShapeProps(xShape);
6267         if (eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR)
6268         {
6269             uno::Reference<beans::XPropertySet> xEmbeddedProps(m_xEmbedded, uno::UNO_QUERY);
6270             xEmbeddedProps->setPropertyValue("AnchorType", uno::makeAny(text::TextContentAnchorType_AT_CHARACTER));
6271             uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
6272             xEmbeddedProps->setPropertyValue("HoriOrient", xShapeProps->getPropertyValue("HoriOrient"));
6273             xEmbeddedProps->setPropertyValue("HoriOrientPosition", xShapeProps->getPropertyValue("HoriOrientPosition"));
6274             xEmbeddedProps->setPropertyValue("HoriOrientRelation", xShapeProps->getPropertyValue("HoriOrientRelation"));
6275             xEmbeddedProps->setPropertyValue("VertOrient", xShapeProps->getPropertyValue("VertOrient"));
6276             xEmbeddedProps->setPropertyValue("VertOrientPosition", xShapeProps->getPropertyValue("VertOrientPosition"));
6277             xEmbeddedProps->setPropertyValue("VertOrientRelation", xShapeProps->getPropertyValue("VertOrientRelation"));
6278             //tdf123873 fix missing textwrap import
6279             xEmbeddedProps->setPropertyValue("TextWrap", xShapeProps->getPropertyValue("TextWrap"));
6280         }
6281     }
6282     //insert it into the document at the current cursor position
6283     OSL_ENSURE( xTextContent.is(), "DomainMapper_Impl::ImportGraphic");
6284     if( xTextContent.is())
6285     {
6286         appendTextContent( xTextContent, uno::Sequence< beans::PropertyValue >() );
6287 
6288         if (eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR && !m_aTextAppendStack.empty())
6289         {
6290             // Remember this object is anchored to the current paragraph.
6291             AnchoredObjectInfo aInfo;
6292             aInfo.m_xAnchoredObject = xTextContent;
6293             if (m_pGraphicImport)
6294             {
6295                 // We still have the graphic import around, remember the original margin, so later
6296                 // SectionPropertyMap::HandleIncreasedAnchoredObjectSpacing() can use it.
6297                 aInfo.m_nLeftMargin = m_pGraphicImport->GetLeftMarginOrig();
6298             }
6299             m_aTextAppendStack.top().m_aAnchoredObjects.push_back(aInfo);
6300         }
6301     }
6302 
6303     // Clear the reference, so in case the embedded object is inside a
6304     // TextFrame, we won't try to resize it (to match the size of the
6305     // TextFrame) here.
6306     m_xEmbedded.clear();
6307     m_pGraphicImport.clear();
6308 }
6309 
6310 
SetLineNumbering(sal_Int32 nLnnMod,sal_uInt32 nLnc,sal_Int32 ndxaLnn)6311 void DomainMapper_Impl::SetLineNumbering( sal_Int32 nLnnMod, sal_uInt32 nLnc, sal_Int32 ndxaLnn )
6312 {
6313     if( !m_bLineNumberingSet )
6314     {
6315         try
6316         {
6317             uno::Reference< text::XLineNumberingProperties > xLineProperties( m_xTextDocument, uno::UNO_QUERY_THROW );
6318             uno::Reference< beans::XPropertySet > xProperties = xLineProperties->getLineNumberingProperties();
6319             uno::Any aTrue( uno::makeAny( true ));
6320             xProperties->setPropertyValue( getPropertyName( PROP_IS_ON                  ), aTrue);
6321             xProperties->setPropertyValue( getPropertyName( PROP_COUNT_EMPTY_LINES      ), aTrue );
6322             xProperties->setPropertyValue( getPropertyName( PROP_COUNT_LINES_IN_FRAMES  ), uno::makeAny( false ) );
6323             xProperties->setPropertyValue( getPropertyName( PROP_INTERVAL               ), uno::makeAny( static_cast< sal_Int16 >( nLnnMod )));
6324             xProperties->setPropertyValue( getPropertyName( PROP_DISTANCE               ), uno::makeAny( ConversionHelper::convertTwipToMM100(ndxaLnn) ));
6325             xProperties->setPropertyValue( getPropertyName( PROP_NUMBER_POSITION        ), uno::makeAny( style::LineNumberPosition::LEFT));
6326             xProperties->setPropertyValue( getPropertyName( PROP_NUMBERING_TYPE         ), uno::makeAny( style::NumberingType::ARABIC));
6327             xProperties->setPropertyValue( getPropertyName( PROP_RESTART_AT_EACH_PAGE   ), uno::makeAny( nLnc == NS_ooxml::LN_Value_ST_LineNumberRestart_newPage ));
6328         }
6329         catch( const uno::Exception& )
6330         {}
6331     }
6332     m_bLineNumberingSet = true;
6333     uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier( GetTextDocument(), uno::UNO_QUERY_THROW );
6334     uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
6335     uno::Reference<container::XNameContainer> xStyles;
6336     xStyleFamilies->getByName(getPropertyName( PROP_PARAGRAPH_STYLES )) >>= xStyles;
6337     lcl_linenumberingHeaderFooter( xStyles, "Header", this );
6338     lcl_linenumberingHeaderFooter( xStyles, "Footer", this );
6339 }
6340 
6341 
SetPageMarginTwip(PageMarElement eElement,sal_Int32 nValue)6342 void DomainMapper_Impl::SetPageMarginTwip( PageMarElement eElement, sal_Int32 nValue )
6343 {
6344     nValue = ConversionHelper::convertTwipToMM100(nValue);
6345     switch(eElement)
6346     {
6347         case PAGE_MAR_TOP    : m_aPageMargins.top     = nValue; break;
6348         case PAGE_MAR_RIGHT  : m_aPageMargins.right   = nValue; break;
6349         case PAGE_MAR_BOTTOM : m_aPageMargins.bottom  = nValue; break;
6350         case PAGE_MAR_LEFT   : m_aPageMargins.left    = nValue; break;
6351         case PAGE_MAR_HEADER : m_aPageMargins.header  = nValue; break;
6352         case PAGE_MAR_FOOTER : m_aPageMargins.footer  = nValue; break;
6353         case PAGE_MAR_GUTTER : break;
6354     }
6355 }
6356 
6357 
PageMar()6358 PageMar::PageMar()
6359     : top(ConversionHelper::convertTwipToMM100( sal_Int32(1440)))
6360     // This is strange, the RTF spec says it's 1800, but it's clearly 1440 in Word
6361     // OOXML seems not to specify a default value
6362     , right(ConversionHelper::convertTwipToMM100( sal_Int32(1440)))
6363     , bottom(top)
6364     , left(right)
6365     , header(ConversionHelper::convertTwipToMM100(sal_Int32(720)))
6366     , footer(header)
6367 {
6368 }
6369 
6370 
RegisterFrameConversion(uno::Reference<text::XTextRange> const & xFrameStartRange,uno::Reference<text::XTextRange> const & xFrameEndRange,const std::vector<beans::PropertyValue> & rFrameProperties)6371 void DomainMapper_Impl::RegisterFrameConversion(
6372         uno::Reference< text::XTextRange > const&    xFrameStartRange,
6373         uno::Reference< text::XTextRange > const&    xFrameEndRange,
6374         const std::vector<beans::PropertyValue>& rFrameProperties
6375         )
6376 {
6377     OSL_ENSURE(
6378         m_aFrameProperties.empty() && !m_xFrameStartRange.is() && !m_xFrameEndRange.is(),
6379         "frame properties not removed");
6380     m_aFrameProperties = rFrameProperties;
6381     m_xFrameStartRange = xFrameStartRange;
6382     m_xFrameEndRange   = xFrameEndRange;
6383 }
6384 
6385 
ExecuteFrameConversion()6386 void DomainMapper_Impl::ExecuteFrameConversion()
6387 {
6388     if( m_xFrameStartRange.is() && m_xFrameEndRange.is() && !m_bDiscardHeaderFooter )
6389     {
6390         try
6391         {
6392             uno::Reference< text::XTextAppendAndConvert > xTextAppendAndConvert( GetTopTextAppend(), uno::UNO_QUERY_THROW );
6393             // convert redline ranges to cursor movement and character length
6394             std::vector<sal_Int32> redPos, redLen;
6395             for( size_t i = 0; i < aFramedRedlines.size(); i+=3)
6396             {
6397                 uno::Reference< text::XTextRange > xRange;
6398                 aFramedRedlines[i] >>= xRange;
6399                 uno::Reference<text::XTextCursor> xRangeCursor = GetTopTextAppend()->createTextCursorByRange( xRange );
6400                 if (xRangeCursor.is())
6401                 {
6402                     sal_Int32 nLen = xRange->getString().getLength();
6403                     redLen.push_back(nLen);
6404                     xRangeCursor->gotoRange(m_xFrameStartRange, true);
6405                     redPos.push_back(xRangeCursor->getString().getLength() - nLen);
6406                 }
6407                 else
6408                 {
6409                     // failed createTextCursorByRange(), for example, table inside the frame
6410                     redLen.push_back(-1);
6411                     redPos.push_back(-1);
6412                 }
6413             }
6414 
6415             const uno::Reference< text::XTextContent >& xTextContent = xTextAppendAndConvert->convertToTextFrame(
6416                 m_xFrameStartRange,
6417                 m_xFrameEndRange,
6418                 comphelper::containerToSequence(m_aFrameProperties) );
6419 
6420             // create redlines in the previous frame
6421             for( size_t i = 0; i < aFramedRedlines.size(); i+=3)
6422             {
6423                 OUString sType;
6424                 beans::PropertyValues aRedlineProperties( 3 );
6425                 // skip failed createTextCursorByRange()
6426                 if (redPos[i/3] == -1)
6427                     continue;
6428                 aFramedRedlines[i+1] >>= sType;
6429                 aFramedRedlines[i+2] >>= aRedlineProperties;
6430                 uno::Reference< text::XTextFrame > xFrame( xTextContent, uno::UNO_QUERY_THROW );
6431                 uno::Reference< text::XTextCursor > xCrsr = xFrame->getText()->createTextCursor();
6432                 xCrsr->goRight(redPos[i/3], false);
6433                 xCrsr->goRight(redLen[i/3], true);
6434                 uno::Reference < text::XRedline > xRedline( xCrsr, uno::UNO_QUERY_THROW );
6435                 xRedline->makeRedline( sType, aRedlineProperties );
6436             }
6437         }
6438         catch( const uno::Exception&)
6439         {
6440             DBG_UNHANDLED_EXCEPTION( "writerfilter.dmapper", "Exception caught when converting to frame");
6441         }
6442 
6443         m_bIsActualParagraphFramed = false;
6444         aFramedRedlines.clear();
6445     }
6446     m_xFrameStartRange = nullptr;
6447     m_xFrameEndRange = nullptr;
6448     m_aFrameProperties.clear();
6449 }
6450 
AddNewRedline(sal_uInt32 sprmId)6451 void DomainMapper_Impl::AddNewRedline( sal_uInt32 sprmId )
6452 {
6453     RedlineParamsPtr pNew( new RedlineParams );
6454     pNew->m_nToken = XML_mod;
6455     if ( !m_bIsParaMarkerChange )
6456     {
6457         // <w:rPrChange> applies to the whole <w:r>, <w:pPrChange> applies to the whole <w:p>,
6458         // so keep those two in CONTEXT_CHARACTERS and CONTEXT_PARAGRAPH, which will take
6459         // care of their scope (i.e. when they should be used and discarded).
6460         // Let's keep the rest the same way they used to be handled (explicitly dropped
6461         // from a global stack by endtrackchange), but quite possibly they should not be handled
6462         // that way either (I don't know).
6463         if( sprmId == NS_ooxml::LN_EG_RPrContent_rPrChange )
6464             GetTopContextOfType( CONTEXT_CHARACTER )->Redlines().push_back( pNew );
6465         else if( sprmId == NS_ooxml::LN_CT_PPr_pPrChange )
6466             GetTopContextOfType( CONTEXT_PARAGRAPH )->Redlines().push_back( pNew );
6467         else if( sprmId != NS_ooxml::LN_CT_ParaRPr_rPrChange )
6468             m_aRedlines.top().push_back( pNew );
6469     }
6470     else
6471     {
6472         m_pParaMarkerRedline = pNew;
6473     }
6474     // Newly read data will go into this redline.
6475     m_currentRedline = pNew;
6476 }
6477 
SetCurrentRedlineIsRead()6478 void DomainMapper_Impl::SetCurrentRedlineIsRead()
6479 {
6480     m_currentRedline.clear();
6481 }
6482 
GetCurrentRedlineToken() const6483 sal_Int32 DomainMapper_Impl::GetCurrentRedlineToken(  ) const
6484 {
6485     sal_Int32 nToken = 0;
6486     assert( m_currentRedline.get());
6487     nToken = m_currentRedline->m_nToken;
6488     return nToken;
6489 }
6490 
SetCurrentRedlineAuthor(const OUString & sAuthor)6491 void DomainMapper_Impl::SetCurrentRedlineAuthor( const OUString& sAuthor )
6492 {
6493     if (!m_xAnnotationField.is())
6494     {
6495         if (m_currentRedline.get())
6496             m_currentRedline->m_sAuthor = sAuthor;
6497         else
6498             SAL_INFO("writerfilter.dmapper", "numberingChange not implemented");
6499     }
6500     else
6501         m_xAnnotationField->setPropertyValue("Author", uno::makeAny(sAuthor));
6502 }
6503 
SetCurrentRedlineInitials(const OUString & sInitials)6504 void DomainMapper_Impl::SetCurrentRedlineInitials( const OUString& sInitials )
6505 {
6506     if (m_xAnnotationField.is())
6507         m_xAnnotationField->setPropertyValue("Initials", uno::makeAny(sInitials));
6508 }
6509 
SetCurrentRedlineDate(const OUString & sDate)6510 void DomainMapper_Impl::SetCurrentRedlineDate( const OUString& sDate )
6511 {
6512     if (!m_xAnnotationField.is())
6513     {
6514         if (m_currentRedline.get())
6515             m_currentRedline->m_sDate = sDate;
6516         else
6517             SAL_INFO("writerfilter.dmapper", "numberingChange not implemented");
6518     }
6519     else
6520         m_xAnnotationField->setPropertyValue("DateTimeValue", uno::makeAny(ConversionHelper::ConvertDateStringToDateTime(sDate)));
6521 }
6522 
SetCurrentRedlineId(sal_Int32 sId)6523 void DomainMapper_Impl::SetCurrentRedlineId( sal_Int32 sId )
6524 {
6525     if (m_xAnnotationField.is())
6526     {
6527         m_nAnnotationId = sId;
6528     }
6529     else
6530     {
6531         // This should be an assert, but somebody had the smart idea to reuse this function also for comments and whatnot,
6532         // and in some cases the id is actually not handled, which may be in fact a bug.
6533         if( !m_currentRedline.get())
6534             SAL_INFO("writerfilter.dmapper", "no current redline");
6535     }
6536 }
6537 
SetCurrentRedlineToken(sal_Int32 nToken)6538 void DomainMapper_Impl::SetCurrentRedlineToken( sal_Int32 nToken )
6539 {
6540     assert( m_currentRedline.get());
6541     m_currentRedline->m_nToken = nToken;
6542 }
6543 
SetCurrentRedlineRevertProperties(const uno::Sequence<beans::PropertyValue> & aProperties)6544 void DomainMapper_Impl::SetCurrentRedlineRevertProperties( const uno::Sequence<beans::PropertyValue>& aProperties )
6545 {
6546     assert( m_currentRedline.get());
6547     m_currentRedline->m_aRevertProperties = aProperties;
6548 }
6549 
6550 
6551 // This removes only the last redline stored here, those stored in contexts are automatically removed when
6552 // the context is destroyed.
RemoveTopRedline()6553 void DomainMapper_Impl::RemoveTopRedline( )
6554 {
6555     if (m_aRedlines.top().empty())
6556     {
6557         SAL_WARN("writerfilter.dmapper", "RemoveTopRedline called with empty stack");
6558         throw uno::Exception("RemoveTopRedline failed", nullptr);
6559     }
6560     m_aRedlines.top().pop_back( );
6561     m_currentRedline.clear();
6562 }
6563 
ApplySettingsTable()6564 void DomainMapper_Impl::ApplySettingsTable()
6565 {
6566     if (m_pSettingsTable && m_xTextFactory.is())
6567     {
6568         try
6569         {
6570             uno::Reference< beans::XPropertySet > xTextDefaults(m_xTextFactory->createInstance("com.sun.star.text.Defaults"), uno::UNO_QUERY_THROW );
6571             sal_Int32 nDefTab = m_pSettingsTable->GetDefaultTabStop();
6572             xTextDefaults->setPropertyValue( getPropertyName( PROP_TAB_STOP_DISTANCE ), uno::makeAny(nDefTab) );
6573             if (m_pSettingsTable->GetLinkStyles())
6574             {
6575                 // If linked styles are enabled, set paragraph defaults from Word's default template
6576                 xTextDefaults->setPropertyValue(getPropertyName(PROP_PARA_BOTTOM_MARGIN), uno::makeAny(ConversionHelper::convertTwipToMM100(200)));
6577                 style::LineSpacing aSpacing;
6578                 aSpacing.Mode = style::LineSpacingMode::PROP;
6579                 aSpacing.Height = sal_Int16(115);
6580                 xTextDefaults->setPropertyValue(getPropertyName(PROP_PARA_LINE_SPACING), uno::makeAny(aSpacing));
6581             }
6582 
6583             if (m_pSettingsTable->GetZoomFactor() || m_pSettingsTable->GetView())
6584             {
6585                 std::vector<beans::PropertyValue> aViewProps;
6586                 if (m_pSettingsTable->GetZoomFactor())
6587                 {
6588                     aViewProps.emplace_back("ZoomFactor", -1, uno::makeAny(m_pSettingsTable->GetZoomFactor()), beans::PropertyState_DIRECT_VALUE);
6589                     aViewProps.emplace_back("VisibleBottom", -1, uno::makeAny(sal_Int32(0)), beans::PropertyState_DIRECT_VALUE);
6590                     aViewProps.emplace_back("ZoomType", -1,
6591                                             uno::makeAny(m_pSettingsTable->GetZoomType()),
6592                                             beans::PropertyState_DIRECT_VALUE);
6593                 }
6594                 uno::Reference<container::XIndexContainer> xBox = document::IndexedPropertyValues::create(m_xComponentContext);
6595                 xBox->insertByIndex(sal_Int32(0), uno::makeAny(comphelper::containerToSequence(aViewProps)));
6596                 uno::Reference<document::XViewDataSupplier> xViewDataSupplier(m_xTextDocument, uno::UNO_QUERY);
6597                 xViewDataSupplier->setViewData(xBox);
6598             }
6599 
6600             uno::Reference< beans::XPropertySet > xSettings(m_xTextFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
6601 
6602             if (m_pSettingsTable->GetDoNotExpandShiftReturn())
6603                 xSettings->setPropertyValue( "DoNotJustifyLinesWithManualBreak", uno::makeAny(true) );
6604             if (m_pSettingsTable->GetUsePrinterMetrics())
6605                 xSettings->setPropertyValue("PrinterIndependentLayout", uno::makeAny(document::PrinterIndependentLayout::DISABLED));
6606             if( m_pSettingsTable->GetEmbedTrueTypeFonts())
6607                 xSettings->setPropertyValue( getPropertyName( PROP_EMBED_FONTS ), uno::makeAny(true) );
6608             if( m_pSettingsTable->GetEmbedSystemFonts())
6609                 xSettings->setPropertyValue( getPropertyName( PROP_EMBED_SYSTEM_FONTS ), uno::makeAny(true) );
6610             xSettings->setPropertyValue("AddParaTableSpacing", uno::makeAny(m_pSettingsTable->GetDoNotUseHTMLParagraphAutoSpacing()));
6611             if( m_pSettingsTable->GetProtectForm() )
6612                 xSettings->setPropertyValue("ProtectForm", uno::makeAny( true ));
6613         }
6614         catch(const uno::Exception&)
6615         {
6616         }
6617     }
6618 }
6619 
GetCurrentNumberingRules(sal_Int32 * pListLevel)6620 uno::Reference<container::XIndexAccess> DomainMapper_Impl::GetCurrentNumberingRules(sal_Int32* pListLevel)
6621 {
6622     uno::Reference<container::XIndexAccess> xRet;
6623     try
6624     {
6625         OUString aStyle = GetCurrentParaStyleName();
6626         if (aStyle.isEmpty())
6627             return xRet;
6628         const StyleSheetEntryPtr pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(aStyle);
6629         if (!pEntry)
6630             return xRet;
6631         const StyleSheetPropertyMap* pStyleSheetProperties = dynamic_cast<const StyleSheetPropertyMap*>(pEntry->pProperties.get());
6632         if (!pStyleSheetProperties)
6633             return xRet;
6634         sal_Int32 nListId = pStyleSheetProperties->GetListId();
6635         if (nListId < 0)
6636             return xRet;
6637         if (pListLevel)
6638             *pListLevel = pStyleSheetProperties->GetListLevel();
6639 
6640         // So we are in a paragraph style and it has numbering. Look up the relevant numbering rules.
6641         auto const pList(GetListTable()->GetList(nListId));
6642         OUString aListName;
6643         if (pList)
6644         {
6645             aListName = pList->GetStyleName(nListId);
6646         }
6647         uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier(GetTextDocument(), uno::UNO_QUERY_THROW);
6648         uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
6649         uno::Reference<container::XNameAccess> xNumberingStyles;
6650         xStyleFamilies->getByName("NumberingStyles") >>= xNumberingStyles;
6651         uno::Reference<beans::XPropertySet> xStyle(xNumberingStyles->getByName(aListName), uno::UNO_QUERY);
6652         xRet.set(xStyle->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
6653     }
6654     catch (const uno::Exception&)
6655     {
6656         TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "GetCurrentNumberingRules: exception caught");
6657     }
6658     return xRet;
6659 }
6660 
GetCurrentNumberingCharStyle()6661 uno::Reference<beans::XPropertySet> DomainMapper_Impl::GetCurrentNumberingCharStyle()
6662 {
6663     uno::Reference<beans::XPropertySet> xRet;
6664     try
6665     {
6666         sal_Int32 nListLevel = -1;
6667         uno::Reference<container::XIndexAccess> xLevels;
6668         if ( GetTopContextType() == CONTEXT_PARAGRAPH )
6669             xLevels = GetCurrentNumberingRules(&nListLevel);
6670         if (!xLevels.is())
6671         {
6672             if (IsOOXMLImport())
6673                 return xRet;
6674 
6675             PropertyMapPtr pContext = m_pTopContext;
6676             if (IsRTFImport() && !IsOpenField())
6677             {
6678                 // Looking up the paragraph context explicitly (and not just taking
6679                 // the top context) is necessary for RTF, where formatting of a run
6680                 // and of the paragraph mark is not separated.
6681                 // We know that the formatting inside a field won't affect the
6682                 // paragraph marker formatting, though.
6683                 pContext = GetTopContextOfType(CONTEXT_PARAGRAPH);
6684                 if (!pContext)
6685                     return xRet;
6686             }
6687 
6688             // In case numbering rules is not found via a style, try the direct formatting instead.
6689             boost::optional<PropertyMap::Property> oProp = pContext->getProperty(PROP_NUMBERING_RULES);
6690             if (oProp)
6691             {
6692                 xLevels.set(oProp->second, uno::UNO_QUERY);
6693                 // Found the rules, then also try to look up our numbering level.
6694                 oProp = pContext->getProperty(PROP_NUMBERING_LEVEL);
6695                 if (oProp)
6696                     oProp->second >>= nListLevel;
6697                 else
6698                     nListLevel = 0;
6699             }
6700 
6701             if (!xLevels.is())
6702                 return xRet;
6703         }
6704         uno::Sequence<beans::PropertyValue> aProps;
6705         xLevels->getByIndex(nListLevel) >>= aProps;
6706         auto pProp = std::find_if(aProps.begin(), aProps.end(),
6707             [](const beans::PropertyValue& rProp) { return rProp.Name == "CharStyleName"; });
6708         if (pProp != aProps.end())
6709         {
6710             OUString aCharStyle;
6711             pProp->Value >>= aCharStyle;
6712             uno::Reference<container::XNameAccess> xCharacterStyles;
6713             uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier(GetTextDocument(), uno::UNO_QUERY);
6714             uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
6715             xStyleFamilies->getByName("CharacterStyles") >>= xCharacterStyles;
6716             xRet.set(xCharacterStyles->getByName(aCharStyle), uno::UNO_QUERY_THROW);
6717         }
6718     }
6719     catch( const uno::Exception& )
6720     {
6721     }
6722     return xRet;
6723 }
6724 
GetSectionContext()6725 SectionPropertyMap * DomainMapper_Impl::GetSectionContext()
6726 {
6727     SectionPropertyMap* pSectionContext = nullptr;
6728     //the section context is not available before the first call of startSectionGroup()
6729     if( !IsAnyTableImport() )
6730     {
6731         PropertyMapPtr pContext = GetTopContextOfType(CONTEXT_SECTION);
6732         pSectionContext = dynamic_cast< SectionPropertyMap* >( pContext.get() );
6733     }
6734 
6735     return pSectionContext;
6736 }
6737 
deferCharacterProperty(sal_Int32 id,const css::uno::Any & value)6738 void DomainMapper_Impl::deferCharacterProperty(sal_Int32 id, const css::uno::Any& value)
6739 {
6740     deferredCharacterProperties[ id ] = value;
6741 }
6742 
processDeferredCharacterProperties()6743 void DomainMapper_Impl::processDeferredCharacterProperties()
6744 {
6745     // Actually process in DomainMapper, so that it's the same source file like normal processing.
6746     if( !deferredCharacterProperties.empty())
6747     {
6748         m_rDMapper.processDeferredCharacterProperties( deferredCharacterProperties );
6749         deferredCharacterProperties.clear();
6750     }
6751 }
6752 
getNumberingProperty(const sal_Int32 nListId,sal_Int32 nNumberingLevel,const OUString & aProp)6753 sal_Int32 DomainMapper_Impl::getNumberingProperty(const sal_Int32 nListId, sal_Int32 nNumberingLevel, const OUString& aProp)
6754 {
6755     sal_Int32 nRet = 0;
6756     if ( nListId < 0 )
6757         return nRet;
6758 
6759     try
6760     {
6761         if (nNumberingLevel < 0) // It seems it's valid to omit numbering level, and in that case it means zero.
6762             nNumberingLevel = 0;
6763 
6764         auto const pList(GetListTable()->GetList(nListId));
6765         assert(pList);
6766         const OUString aListName = pList->GetStyleName(nListId);
6767         const uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier(GetTextDocument(), uno::UNO_QUERY_THROW);
6768         const uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
6769         uno::Reference<container::XNameAccess> xNumberingStyles;
6770         xStyleFamilies->getByName("NumberingStyles") >>= xNumberingStyles;
6771         const uno::Reference<beans::XPropertySet> xStyle(xNumberingStyles->getByName(aListName), uno::UNO_QUERY);
6772         const uno::Reference<container::XIndexAccess> xNumberingRules(xStyle->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
6773         if (xNumberingRules.is())
6774         {
6775             uno::Sequence<beans::PropertyValue> aProps;
6776             xNumberingRules->getByIndex(nNumberingLevel) >>= aProps;
6777             auto pProp = std::find_if(aProps.begin(), aProps.end(),
6778                 [&aProp](const beans::PropertyValue& rProp) { return rProp.Name == aProp; });
6779             if (pProp != aProps.end())
6780                 pProp->Value >>= nRet;
6781         }
6782     }
6783     catch( const uno::Exception& )
6784     {
6785         // This can happen when the doc contains some hand-crafted invalid list level.
6786     }
6787 
6788     return nRet;
6789 }
6790 
getCurrentNumberingProperty(const OUString & aProp)6791 sal_Int32 DomainMapper_Impl::getCurrentNumberingProperty(const OUString& aProp)
6792 {
6793     sal_Int32 nRet = 0;
6794 
6795     boost::optional<PropertyMap::Property> pProp = m_pTopContext->getProperty(PROP_NUMBERING_RULES);
6796     uno::Reference<container::XIndexAccess> xNumberingRules;
6797     if (pProp)
6798         xNumberingRules.set(pProp->second, uno::UNO_QUERY);
6799     pProp = m_pTopContext->getProperty(PROP_NUMBERING_LEVEL);
6800     // Default numbering level is the first one.
6801     sal_Int32 nNumberingLevel = 0;
6802     if (pProp)
6803         pProp->second >>= nNumberingLevel;
6804     if (xNumberingRules.is())
6805     {
6806         uno::Sequence<beans::PropertyValue> aProps;
6807         xNumberingRules->getByIndex(nNumberingLevel) >>= aProps;
6808         auto pPropVal = std::find_if(aProps.begin(), aProps.end(),
6809             [&aProp](const beans::PropertyValue& rProp) { return rProp.Name == aProp; });
6810         if (pPropVal != aProps.end())
6811             pPropVal->Value >>= nRet;
6812     }
6813 
6814     return nRet;
6815 }
6816 
6817 
enableInteropGrabBag(const OUString & aName)6818 void DomainMapper_Impl::enableInteropGrabBag(const OUString& aName)
6819 {
6820     m_aInteropGrabBagName = aName;
6821 }
6822 
disableInteropGrabBag()6823 void DomainMapper_Impl::disableInteropGrabBag()
6824 {
6825     m_aInteropGrabBagName.clear();
6826     m_aInteropGrabBag.clear();
6827     m_aSubInteropGrabBag.clear();
6828 }
6829 
isInteropGrabBagEnabled() const6830 bool DomainMapper_Impl::isInteropGrabBagEnabled() const
6831 {
6832     return !(m_aInteropGrabBagName.isEmpty());
6833 }
6834 
appendGrabBag(std::vector<beans::PropertyValue> & rInteropGrabBag,const OUString & aKey,const OUString & aValue)6835 void DomainMapper_Impl::appendGrabBag(std::vector<beans::PropertyValue>& rInteropGrabBag, const OUString& aKey, const OUString& aValue)
6836 {
6837     if (m_aInteropGrabBagName.isEmpty())
6838         return;
6839     beans::PropertyValue aProperty;
6840     aProperty.Name = aKey;
6841     aProperty.Value <<= aValue;
6842     rInteropGrabBag.push_back(aProperty);
6843 }
6844 
appendGrabBag(std::vector<beans::PropertyValue> & rInteropGrabBag,const OUString & aKey,std::vector<beans::PropertyValue> & rValue)6845 void DomainMapper_Impl::appendGrabBag(std::vector<beans::PropertyValue>& rInteropGrabBag, const OUString& aKey, std::vector<beans::PropertyValue>& rValue)
6846 {
6847     if (m_aInteropGrabBagName.isEmpty())
6848         return;
6849     beans::PropertyValue aProperty;
6850     aProperty.Name = aKey;
6851     aProperty.Value <<= comphelper::containerToSequence(rValue);
6852     rValue.clear();
6853     rInteropGrabBag.push_back(aProperty);
6854 }
6855 
substream(Id rName,::writerfilter::Reference<Stream>::Pointer_t const & ref)6856 void DomainMapper_Impl::substream(Id rName,
6857         ::writerfilter::Reference<Stream>::Pointer_t const& ref)
6858 {
6859 #ifndef NDEBUG
6860     size_t contextSize(m_aContextStack.size());
6861     size_t propSize[NUMBER_OF_CONTEXTS];
6862     for (int i = 0; i < NUMBER_OF_CONTEXTS; ++i) {
6863         propSize[i] = m_aPropertyStacks[i].size();
6864     }
6865 #endif
6866 
6867     // Save "has footnote" state, which is specific to a section in the body
6868     // text, so state from substreams is not relevant.
6869     bool bHasFtn = m_bHasFtn;
6870 
6871     //finalize any waiting frames before starting alternate streams
6872     CheckUnregisteredFrameConversion();
6873     ExecuteFrameConversion();
6874 
6875     appendTableManager();
6876     // Appending a TableManager resets its TableHandler, so we need to append
6877     // that as well, or tables won't be imported properly in headers/footers.
6878     appendTableHandler();
6879     getTableManager().startLevel();
6880 
6881     //import of page header/footer
6882     //Ensure that only one header/footer per section is pushed
6883 
6884     switch( rName )
6885     {
6886     case NS_ooxml::LN_headerl:
6887             PushPageHeader(SectionPropertyMap::PAGE_LEFT);
6888         break;
6889     case NS_ooxml::LN_headerr:
6890             PushPageHeader(SectionPropertyMap::PAGE_RIGHT);
6891         break;
6892     case NS_ooxml::LN_headerf:
6893             PushPageHeader(SectionPropertyMap::PAGE_FIRST);
6894         break;
6895     case NS_ooxml::LN_footerl:
6896             PushPageFooter(SectionPropertyMap::PAGE_LEFT);
6897         break;
6898     case NS_ooxml::LN_footerr:
6899             PushPageFooter(SectionPropertyMap::PAGE_RIGHT);
6900         break;
6901     case NS_ooxml::LN_footerf:
6902             PushPageFooter(SectionPropertyMap::PAGE_FIRST);
6903         break;
6904     case NS_ooxml::LN_footnote:
6905     case NS_ooxml::LN_endnote:
6906         PushFootOrEndnote( NS_ooxml::LN_footnote == rName );
6907     break;
6908     case NS_ooxml::LN_annotation :
6909         PushAnnotation();
6910     break;
6911     }
6912     ref->resolve(m_rDMapper);
6913 
6914     switch( rName )
6915     {
6916     case NS_ooxml::LN_headerl:
6917     case NS_ooxml::LN_headerr:
6918     case NS_ooxml::LN_headerf:
6919     case NS_ooxml::LN_footerl:
6920     case NS_ooxml::LN_footerr:
6921     case NS_ooxml::LN_footerf:
6922         PopPageHeaderFooter();
6923     break;
6924     case NS_ooxml::LN_footnote:
6925     case NS_ooxml::LN_endnote:
6926         PopFootOrEndnote();
6927     break;
6928     case NS_ooxml::LN_annotation :
6929         PopAnnotation();
6930     break;
6931     }
6932 
6933     getTableManager().endLevel();
6934     popTableManager();
6935     m_bHasFtn = bHasFtn;
6936 
6937     switch(rName)
6938     {
6939     case NS_ooxml::LN_footnote:
6940     case NS_ooxml::LN_endnote:
6941         m_pTableHandler->setHadFootOrEndnote(true);
6942         m_bHasFtn = true;
6943         break;
6944     }
6945 
6946     // check that stacks are the same as before substream
6947     assert(m_aContextStack.size() == contextSize);
6948     for (int i = 0; i < NUMBER_OF_CONTEXTS; ++i) {
6949         assert(m_aPropertyStacks[i].size() == propSize[i]);
6950     }
6951 }
6952 }}
6953 
6954 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
6955