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