LibreOffice Module writerfilter (master) 1
DomainMapper_Impl.cxx
Go to the documentation of this file.
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/document/XDocumentProperties.hpp>
23#include <com/sun/star/xml/sax/SAXException.hpp>
24#include <ooxml/resourceids.hxx>
25#include "DomainMapper_Impl.hxx"
26#include "ConversionHelper.hxx"
27#include "SdtHelper.hxx"
29#include "TagLogger.hxx"
30#include <com/sun/star/uno/XComponentContext.hpp>
31#include <com/sun/star/graphic/XGraphic.hpp>
32#include <com/sun/star/beans/XPropertyState.hpp>
33#include <com/sun/star/container/XNamed.hpp>
34#include <com/sun/star/document/PrinterIndependentLayout.hpp>
35#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
36#include <com/sun/star/embed/XEmbeddedObject.hpp>
37#include <com/sun/star/i18n/NumberFormatMapper.hpp>
38#include <com/sun/star/i18n/NumberFormatIndex.hpp>
39#include <com/sun/star/lang/XServiceInfo.hpp>
40#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
41#include <com/sun/star/style/LineNumberPosition.hpp>
42#include <com/sun/star/style/LineSpacing.hpp>
43#include <com/sun/star/style/LineSpacingMode.hpp>
44#include <com/sun/star/text/ChapterFormat.hpp>
45#include <com/sun/star/text/FilenameDisplayFormat.hpp>
46#include <com/sun/star/text/SetVariableType.hpp>
47#include <com/sun/star/text/XDocumentIndex.hpp>
48#include <com/sun/star/text/XDocumentIndexesSupplier.hpp>
49#include <com/sun/star/text/XFootnote.hpp>
50#include <com/sun/star/text/XEndnotesSupplier.hpp>
51#include <com/sun/star/text/XFootnotesSupplier.hpp>
52#include <com/sun/star/text/XLineNumberingProperties.hpp>
53#include <com/sun/star/style/XStyle.hpp>
54#include <com/sun/star/text/LabelFollow.hpp>
55#include <com/sun/star/text/PageNumberType.hpp>
56#include <com/sun/star/text/HoriOrientation.hpp>
57#include <com/sun/star/text/VertOrientation.hpp>
58#include <com/sun/star/text/ReferenceFieldPart.hpp>
59#include <com/sun/star/text/RelOrientation.hpp>
60#include <com/sun/star/text/ReferenceFieldSource.hpp>
61#include <com/sun/star/text/SizeType.hpp>
62#include <com/sun/star/text/TextContentAnchorType.hpp>
63#include <com/sun/star/text/WrapTextMode.hpp>
64#include <com/sun/star/text/XChapterNumberingSupplier.hpp>
65#include <com/sun/star/text/XDependentTextField.hpp>
66#include <com/sun/star/text/XParagraphCursor.hpp>
67#include <com/sun/star/text/XRedline.hpp>
68#include <com/sun/star/text/XTextFieldsSupplier.hpp>
69#include <com/sun/star/text/XTextFrame.hpp>
70#include <com/sun/star/text/RubyPosition.hpp>
71#include <com/sun/star/text/XTextRangeCompare.hpp>
72#include <com/sun/star/style/DropCapFormat.hpp>
73#include <com/sun/star/util/NumberFormatter.hpp>
74#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
75#include <com/sun/star/util/XNumberFormatter.hpp>
76#include <com/sun/star/document/XViewDataSupplier.hpp>
77#include <com/sun/star/container/XIndexContainer.hpp>
78#include <com/sun/star/text/ControlCharacter.hpp>
79#include <com/sun/star/text/XTextColumns.hpp>
80#include <com/sun/star/awt/CharSet.hpp>
81#include <com/sun/star/lang/XMultiServiceFactory.hpp>
82#include <com/sun/star/embed/XHierarchicalStorageAccess.hpp>
83#include <com/sun/star/embed/ElementModes.hpp>
84#include <com/sun/star/document/XImporter.hpp>
85#include <com/sun/star/document/XFilter.hpp>
87#include <editeng/flditem.hxx>
88#include <editeng/unotext.hxx>
89#include <o3tl/deleter.hxx>
90#include <o3tl/safeint.hxx>
91#include <o3tl/temporary.hxx>
93#include <utility>
94#include <xmloff/odffields.hxx>
95#include <rtl/uri.hxx>
99#include <comphelper/string.hxx>
100
102
103#include <oox/token/tokens.hxx>
104
105#include <cmath>
106#include <optional>
107#include <map>
108#include <tuple>
109#include <unordered_map>
110#include <regex>
111#include <algorithm>
112
113#include <officecfg/Office/Common.hxx>
119#include <unotools/configmgr.hxx>
122#include <sal/log.hxx>
123#include <o3tl/string_view.hxx>
124#include <com/sun/star/drawing/FillStyle.hpp>
125
126#include <unicode/errorcode.h>
127#include <unicode/regex.h>
128
129using namespace ::com::sun::star;
130using namespace oox;
131namespace writerfilter::dmapper{
132
133//line numbering for header/footer
134static void lcl_linenumberingHeaderFooter( const uno::Reference<container::XNameContainer>& xStyles, const OUString& rname, DomainMapper_Impl* dmapper )
135{
136 const StyleSheetEntryPtr pEntry = dmapper->GetStyleSheetTable()->FindStyleSheetByISTD( rname );
137 if (!pEntry)
138 return;
139 const StyleSheetPropertyMap* pStyleSheetProperties = pEntry->m_pProperties.get();
140 if ( !pStyleSheetProperties )
141 return;
142 sal_Int32 nListId = pStyleSheetProperties->props().GetListId();
143 if( xStyles.is() )
144 {
145 if( xStyles->hasByName( rname ) )
146 {
148 xStyles->getByName( rname ) >>= xStyle;
149 if( !xStyle.is() )
150 return;
151 uno::Reference<beans::XPropertySet> xPropertySet( xStyle, uno::UNO_QUERY );
152 xPropertySet->setPropertyValue( getPropertyName( PROP_PARA_LINE_NUMBER_COUNT ), uno::Any( nListId >= 0 ) );
153 }
154 }
155}
156
157// Populate Dropdown Field properties from FFData structure
158static void lcl_handleDropdownField( const uno::Reference< beans::XPropertySet >& rxFieldProps, const FFDataHandler::Pointer_t& pFFDataHandler )
159{
160 if ( !rxFieldProps.is() )
161 return;
162
163 if ( !pFFDataHandler->getName().isEmpty() )
164 rxFieldProps->setPropertyValue( "Name", uno::Any( pFFDataHandler->getName() ) );
165
166 const FFDataHandler::DropDownEntries_t& rEntries = pFFDataHandler->getDropDownEntries();
167 uno::Sequence< OUString > sItems( rEntries.size() );
168 ::std::copy( rEntries.begin(), rEntries.end(), sItems.getArray());
169 if ( sItems.hasElements() )
170 rxFieldProps->setPropertyValue( "Items", uno::Any( sItems ) );
171
172 sal_Int32 nResult = pFFDataHandler->getDropDownResult().toInt32();
173 if (nResult > 0 && o3tl::make_unsigned(nResult) < sItems.size())
174 rxFieldProps->setPropertyValue( "SelectedItem", uno::Any( std::as_const(sItems)[ nResult ] ) );
175 if ( !pFFDataHandler->getHelpText().isEmpty() )
176 rxFieldProps->setPropertyValue( "Help", uno::Any( pFFDataHandler->getHelpText() ) );
177}
178
179static void lcl_handleTextField( const uno::Reference< beans::XPropertySet >& rxFieldProps, const FFDataHandler::Pointer_t& pFFDataHandler )
180{
181 if ( rxFieldProps.is() && pFFDataHandler )
182 {
183 rxFieldProps->setPropertyValue
185 uno::Any(pFFDataHandler->getStatusText()));
186 rxFieldProps->setPropertyValue
188 uno::Any(pFFDataHandler->getHelpText()));
189 rxFieldProps->setPropertyValue
191 uno::Any(pFFDataHandler->getTextDefault()));
192 }
193}
194
202{
203 while (pEntry)
204 {
205 if (pEntry->m_pProperties)
206 {
207 std::optional<PropertyMap::Property> aProperty =
208 pEntry->m_pProperties->getProperty(eId);
209 if (aProperty)
210 {
211 if (pEntry->m_pProperties->props().GetListId())
212 // It is a paragraph style with list. Paragraph list styles are not taken into account
213 return uno::Any();
214 else
215 return aProperty->second;
216 }
217 }
218 //search until the property is set or no parent is available
219 StyleSheetEntryPtr pNewEntry;
220 if (!pEntry->m_sBaseStyleIdentifier.isEmpty())
221 pNewEntry = rStyleSheet->FindStyleSheetByISTD(pEntry->m_sBaseStyleIdentifier);
222
223 SAL_WARN_IF(pEntry == pNewEntry, "writerfilter.dmapper", "circular loop in style hierarchy?");
224
225 if (pEntry == pNewEntry) //fdo#49587
226 break;
227
228 pEntry = pNewEntry;
229 }
230 return uno::Any();
231}
232
233
234namespace {
235
236struct FieldConversion
237{
238 const char* cFieldServiceName;
240};
241
242}
243
244typedef std::unordered_map<OUString, FieldConversion> FieldConversionMap_t;
245
247static FieldContextPtr GetParentFieldContext(const std::deque<FieldContextPtr>& rFieldStack)
248{
249 if (rFieldStack.size() < 2)
250 {
251 return nullptr;
252 }
253
254 return rFieldStack[rFieldStack.size() - 2];
255}
256
258static bool IsFieldNestingAllowed(const FieldContextPtr& pOuter, const FieldContextPtr& pInner)
259{
260 if (!pInner->GetFieldId())
261 {
262 return true;
263 }
264
265 std::optional<FieldId> oOuterFieldId = pOuter->GetFieldId();
266 if (!oOuterFieldId)
267 {
268 OUString aCommand = pOuter->GetCommand();
269
270 // Ignore leading space before the field name, but don't accept IFF when we check for IF.
271 while (aCommand.getLength() > 3 && aCommand[0] == ' ')
272 aCommand = aCommand.subView(1);
273
274 if (aCommand.startsWith("IF "))
275 {
276 // This will be FIELD_IF once the command is closed.
277 oOuterFieldId = FIELD_IF;
278 }
279 }
280
281 if (!oOuterFieldId)
282 {
283 return true;
284 }
285
286 switch (*oOuterFieldId)
287 {
288 case FIELD_IF:
289 {
290 switch (*pInner->GetFieldId())
291 {
293 case FIELD_FORMTEXT:
294 case FIELD_FORMULA:
295 case FIELD_IF:
296 case FIELD_MERGEFIELD:
297 case FIELD_REF:
298 case FIELD_PAGE:
299 case FIELD_NUMPAGES:
300 {
301 // LO does not currently know how to evaluate these as conditions or results
302 return false;
303 }
304 default:
305 break;
306 }
307 break;
308 }
309 default:
310 break;
311 }
312
313 return true;
314}
315
317 DomainMapper& rDMapper,
320 SourceDocumentType eDocumentType,
321 utl::MediaDescriptor const & rMediaDesc) :
322 m_eDocumentType( eDocumentType ),
323 m_rDMapper( rDMapper ),
324 m_pOOXMLDocument(nullptr),
325 m_xTextDocument( xModel, uno::UNO_QUERY ),
326 m_xTextFactory( xModel, uno::UNO_QUERY ),
327 m_xComponentContext(std::move( xContext )),
328 m_bForceGenericFields(!utl::ConfigManager::IsFuzzing() && officecfg::Office::Common::Filter::Microsoft::Import::ForceImportWWFieldsAsGenericFields::get()),
329 m_bIsDecimalComma( false ),
330 m_bSetUserFieldContent( false ),
331 m_bSetCitation( false ),
332 m_bSetDateValue( false ),
333 m_bIsFirstSection( true ),
334 m_bIsColumnBreakDeferred( false ),
335 m_bIsPageBreakDeferred( false ),
336 m_nLineBreaksDeferred( 0 ),
337 m_bIsBreakDeferredByAnchor(false),
338 m_bSdtEndDeferred(false),
339 m_bParaSdtEndDeferred(false),
340 m_bStartTOC(false),
341 m_bStartTOCHeaderFooter(false),
342 m_bStartedTOC(false),
343 m_bStartIndex(false),
344 m_bStartBibliography(false),
345 m_nStartGenericField(0),
346 m_bTextInserted(false),
347 m_bTextDeleted(false),
348 m_sCurrentPermId(0),
349 m_bFrameDirectionSet(false),
350 m_bInDocDefaultsImport(false),
351 m_bInStyleSheetImport( false ),
352 m_bInNumberingImport(false),
353 m_bInAnyTableImport( false ),
354 m_eInHeaderFooterImport( HeaderFooterImportState::none ),
355 m_bDiscardHeaderFooter( false ),
356 m_bInFootOrEndnote(false),
357 m_bInFootnote(false),
358 m_bHasFootnoteStyle(false),
359 m_bCheckFootnoteStyle(false),
360 m_eSkipFootnoteState(SkipFootnoteSeparator::OFF),
361 m_nFootnotes(-1),
362 m_nEndnotes(-1),
363 m_nFirstFootnoteIndex(-1),
364 m_nFirstEndnoteIndex(-1),
365 m_bLineNumberingSet( false ),
366 m_bIsInFootnoteProperties( false ),
367 m_bIsParaMarkerChange( false ),
368 m_bIsParaMarkerMove( false ),
369 m_bRedlineImageInPreviousRun( false ),
370 m_bParaChanged( false ),
371 m_bIsFirstParaInSection( true ),
372 m_bIsFirstParaInSectionAfterRedline( true ),
373 m_bDummyParaAddedForTableInSection( false ),
374 m_bDummyParaAddedForTableInSectionPage( false ),
375 m_bTextFrameInserted(false),
376 m_bIsPreviousParagraphFramed( false ),
377 m_bIsLastParaInSection( false ),
378 m_bIsLastSectionGroup( false ),
379 m_bIsInComments( false ),
380 m_bParaSectpr( false ),
381 m_bUsingEnhancedFields( false ),
382 m_bSdt(false),
383 m_bIsFirstRun(false),
384 m_bIsOutsideAParagraph(true),
385 m_nAnnotationId( -1 ),
386 m_aSmartTagHandler(m_xComponentContext, m_xTextDocument),
387 m_xInsertTextRange(rMediaDesc.getUnpackedValueOrDefault("TextInsertModeRange", uno::Reference<text::XTextRange>())),
388 m_xAltChunkStartingRange(rMediaDesc.getUnpackedValueOrDefault("AltChunkStartingRange", uno::Reference<text::XTextRange>())),
389 m_bIsInTextBox(false),
390 m_bIsNewDoc(!rMediaDesc.getUnpackedValueOrDefault("InsertMode", false)),
391 m_bIsAltChunk(rMediaDesc.getUnpackedValueOrDefault("AltChunkMode", false)),
392 m_bIsReadGlossaries(rMediaDesc.getUnpackedValueOrDefault("ReadGlossaries", false)),
393 m_nTableDepth(0),
394 m_nTableCellDepth(0),
395 m_nLastTableCellParagraphDepth(0),
396 m_bHasFtn(false),
397 m_bHasFtnSep(false),
398 m_bCheckFirstFootnoteTab(false),
399 m_bIgnoreNextTab(false),
400 m_bIsSplitPara(false),
401 m_bIsActualParagraphFramed( false ),
402 m_bParaHadField(false),
403 m_bSaveParaHadField(false),
404 m_bParaAutoBefore(false),
405 m_bFirstParagraphInCell(true),
406 m_bSaveFirstParagraphInCell(false),
407 m_bParaWithInlineObject(false),
408 m_bSaxError(false)
409{
410 m_aBaseUrl = rMediaDesc.getUnpackedValueOrDefault(
412 if (m_aBaseUrl.isEmpty()) {
413 m_aBaseUrl = rMediaDesc.getUnpackedValueOrDefault(
415 }
416
418 GetBodyText();
419 if (!m_bIsNewDoc && !m_xBodyText)
420 {
421 throw uno::Exception("failed to find body text of the insert position", nullptr);
422 }
423
424 uno::Reference< text::XTextAppend > xBodyTextAppend( m_xBodyText, uno::UNO_QUERY );
425 m_aTextAppendStack.push(TextAppendContext(xBodyTextAppend,
427
428 //todo: does it makes sense to set the body text as static text interface?
429 uno::Reference< text::XTextAppendAndConvert > xBodyTextAppendAndConvert( m_xBodyText, uno::UNO_QUERY );
430 m_pTableHandler = new DomainMapperTableHandler(xBodyTextAppendAndConvert, *this);
432
434 m_bUsingEnhancedFields = !utl::ConfigManager::IsFuzzing() && officecfg::Office::Common::Filter::Microsoft::Import::ImportWWFieldsAsEnhancedFields::get();
435
437
438 m_aRedlines.push(std::vector<RedlineParamsPtr>());
439
440 if (m_bIsAltChunk)
441 {
442 m_bIsFirstSection = false;
443 }
444}
445
446
448{
450 // Don't remove last paragraph when pasting, sw expects that empty paragraph.
451 if (m_bIsNewDoc)
452 {
454 suppress_fun_call_w_exception(GetStyleSheetTable()->ApplyClonedTOCStyles());
455 }
456 if (hasTableManager())
457 {
460 }
461}
462
464{
465 return m_pOOXMLDocument;
466}
467
469{
470 if(!m_xPageStyles1.is())
471 {
473 if (xSupplier.is())
474 xSupplier->getStyleFamilies()->getByName("PageStyles") >>= m_xPageStyles1;
475 }
476 return m_xPageStyles1;
477}
478
480{
481 static const char DEFAULT_STYLE[] = "Converted";
483 {
484 const uno::Sequence< OUString > aPageStyleNames = GetPageStyles()->getElementNames();
485 sal_Int32 nMaxIndex = 0;
486 // find the highest number x in each style with the name "DEFAULT_STYLE+x" and
487 // return an incremented name
488
489 for ( const auto& rStyleName : aPageStyleNames )
490 {
491 if ( rStyleName.startsWith( DEFAULT_STYLE ) )
492 {
493 sal_Int32 nIndex = o3tl::toInt32(rStyleName.subView( strlen( DEFAULT_STYLE ) ));
494 if ( nIndex > nMaxIndex )
495 nMaxIndex = nIndex;
496 }
497 }
498 m_xNextUnusedPageStyleNo = nMaxIndex + 1;
499 }
500
501 OUString sPageStyleName = DEFAULT_STYLE + OUString::number( *m_xNextUnusedPageStyleNo );
503 return sPageStyleName;
504}
505
507{
508 if(!m_xCharacterStyles.is())
509 {
511 if (xSupplier.is())
512 xSupplier->getStyleFamilies()->getByName("CharacterStyles") >>= m_xCharacterStyles;
513 }
514 return m_xCharacterStyles;
515}
516
518{
519 static const char cListLabel[] = "ListLabel ";
521 {
522 //search for all character styles with the name sListLabel + <index>
523 const uno::Sequence< OUString > aCharacterStyleNames = GetCharacterStyles()->getElementNames();
524 sal_Int32 nMaxIndex = 0;
525 for ( const auto& rStyleName : aCharacterStyleNames )
526 {
527 OUString sSuffix;
528 if ( rStyleName.startsWith( cListLabel, &sSuffix ) )
529 {
530 sal_Int32 nSuffix = sSuffix.toInt32();
531 if( nSuffix > 0 && nSuffix > nMaxIndex )
532 nMaxIndex = nSuffix;
533 }
534 }
535 m_xNextUnusedCharacterStyleNo = nMaxIndex + 1;
536 }
537
538 OUString sPageStyleName = cListLabel + OUString::number( *m_xNextUnusedCharacterStyleNo );
540 return sPageStyleName;
541}
542
544{
545 if(!m_xBodyText.is())
546 {
547 if (m_xInsertTextRange.is())
548 m_xBodyText = m_xInsertTextRange->getText();
549 else if (m_xTextDocument.is())
550 m_xBodyText = m_xTextDocument->getText();
551 }
552 return m_xBodyText;
553}
554
555
557{
558 if( !m_xDocumentSettings.is() && m_xTextFactory.is())
559 {
560 m_xDocumentSettings.set( m_xTextFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY );
561 }
562 return m_xDocumentSettings;
563}
564
565
566void DomainMapper_Impl::SetDocumentSettingsProperty( const OUString& rPropName, const uno::Any& rValue )
567{
569 if( xSettings.is() )
570 {
571 try
572 {
573 xSettings->setPropertyValue( rPropName, rValue );
574 }
575 catch( const uno::Exception& )
576 {
577 }
578 }
579}
580
581namespace
582{
583void CopyPageDescNameToNextParagraph(const uno::Reference<lang::XComponent>& xParagraph,
585{
586 // First check if xParagraph has a non-empty page style name to copy from.
587 uno::Reference<beans::XPropertySet> xParagraphProps(xParagraph, uno::UNO_QUERY);
588 if (!xParagraphProps.is())
589 {
590 return;
591 }
592
593 uno::Any aPageDescName = xParagraphProps->getPropertyValue("PageDescName");
594 OUString sPageDescName;
595 aPageDescName >>= sPageDescName;
596 if (sPageDescName.isEmpty())
597 {
598 return;
599 }
600
601 // If so, search for the next paragraph.
602 uno::Reference<text::XParagraphCursor> xParaCursor(xCursor, uno::UNO_QUERY);
603 if (!xParaCursor.is())
604 {
605 return;
606 }
607
608 // Create a range till the next paragraph and then enumerate on the range.
609 if (!xParaCursor->gotoNextParagraph(/*bExpand=*/true))
610 {
611 return;
612 }
613
614 uno::Reference<container::XEnumerationAccess> xEnumerationAccess(xParaCursor, uno::UNO_QUERY);
615 if (!xEnumerationAccess.is())
616 {
617 return;
618 }
619
620 uno::Reference<container::XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration();
621 if (!xEnumeration.is())
622 {
623 return;
624 }
625
626 xEnumeration->nextElement();
627 if (!xEnumeration->hasMoreElements())
628 {
629 return;
630 }
631
632 // We found a next item in the enumeration: it's usually a paragraph, but may be a table as
633 // well.
634 uno::Reference<beans::XPropertySet> xNextParagraph(xEnumeration->nextElement(), uno::UNO_QUERY);
635 if (!xNextParagraph.is())
636 {
637 return;
638 }
639
640 // See if there is page style set already: if so, don't touch it.
641 OUString sNextPageDescName;
642 xNextParagraph->getPropertyValue("PageDescName") >>= sNextPageDescName;
643 if (!sNextPageDescName.isEmpty())
644 {
645 return;
646 }
647
648 // Finally copy it over, so it's not lost.
649 xNextParagraph->setPropertyValue("PageDescName", aPageDescName);
650}
651}
652
654{
657 SectionPropertyMap* pSectionContext = dynamic_cast< SectionPropertyMap* >( pContext.get() );
658 if (!pSectionContext)
659 return;
660
661 if (m_aTextAppendStack.empty())
662 return;
663 uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
664 if (!xTextAppend.is())
665 return;
666
667 uno::Reference< text::XTextCursor > xCursor = xTextAppend->createTextCursorByRange(pSectionContext->GetStartingRange());
668
669 // Remove the extra NumPicBullets from the document,
670 // which get attached to the first paragraph in the
671 // document
672 ListsManager::Pointer pListTable = GetListTable();
673 pListTable->DisposeNumPicBullets();
674
675 uno::Reference<container::XEnumerationAccess> xEnumerationAccess(xCursor, uno::UNO_QUERY);
676 if (xEnumerationAccess.is() && m_aTextAppendStack.size() == 1 )
677 {
678 uno::Reference<container::XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration();
679 uno::Reference<lang::XComponent> xParagraph(xEnumeration->nextElement(), uno::UNO_QUERY);
680 // Make sure no page breaks are lost.
681 CopyPageDescNameToNextParagraph(xParagraph, xCursor);
682 xParagraph->dispose();
683 }
684}
686{
687 // Shapes and textboxes can't have sections.
688 if (IsInShape() || m_bIsInTextBox)
689 return;
690
691 if (!m_aTextAppendStack.empty())
692 {
693 uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
694 if (xTextAppend.is())
695 {
696 xTextAppend->finishParagraph( uno::Sequence< beans::PropertyValue >() );
698 }
699 }
700}
701
703 bool bAlreadyExpanded)
704 {
705 OUString sName;
706 if (!xCursor.is())
707 return sName;
708
709 // Select 1 previous element
710 if (!bAlreadyExpanded)
711 xCursor->goLeft(1, true);
712 comphelper::ScopeGuard unselectGuard(
713 [xCursor, bAlreadyExpanded]()
714 {
715 if (!bAlreadyExpanded)
716 xCursor->goRight(1, true);
717 });
718
719 uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xCursor, uno::UNO_QUERY);
720 if (!xParaEnumAccess.is())
721 return sName;
722
723 // Iterate through selection paragraphs
724 uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
725 if (!xParaEnum->hasMoreElements())
726 return sName;
727
728 // Iterate through first para portions
729 uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParaEnum->nextElement(),
730 uno::UNO_QUERY_THROW);
731 uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
732 while (xRunEnum->hasMoreElements())
733 {
734 uno::Reference<beans::XPropertySet> xProps(xRunEnum->nextElement(), uno::UNO_QUERY_THROW);
735 uno::Any aType(xProps->getPropertyValue("TextPortionType"));
736 OUString sType;
737 aType >>= sType;
738 if (sType == "Bookmark")
739 {
740 uno::Reference<container::XNamed> xBookmark(xProps->getPropertyValue("Bookmark"),
741 uno::UNO_QUERY_THROW);
742 sName = xBookmark->getName();
743 // Do not stop the scan here. Maybe there are 2 bookmarks?
744 }
745 }
746
747 return sName;
748 }
749
751{
753 return;
754
755 if (m_aTextAppendStack.empty())
756 return;
757 uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
758 if (!xTextAppend.is())
759 return;
760
761 if (hasTableManager() && getTableManager().getCurrentTablePosition().getLength() != 0)
762 {
763 // If we have an open floating table, then don't remove this paragraph, since that'll be the
764 // anchor of the floating table. Otherwise we would lose the table.
765 return;
766 }
767
768 try
769 {
771 if (m_bIsNewDoc)
772 {
773 xCursor = xTextAppend->createTextCursor();
774 xCursor->gotoEnd(false);
775 }
776 else
777 xCursor.set(m_aTextAppendStack.top().xCursor, uno::UNO_SET_THROW);
778
779 // Keep the character properties of the last but one paragraph, even if
780 // it's empty. This works for headers/footers, and maybe in other cases
781 // as well, but surely not in textboxes.
782 // fdo#58327: also do this at the end of the document: when pasting,
783 // a table before the cursor position would be deleted
784 bool const bEndOfDocument(m_aTextAppendStack.size() == 1);
785
787 if (IsInHeaderFooter() || bEndOfDocument)
788 {
789 if (uno::Reference<container::XEnumerationAccess> xEA{ xCursor, uno::UNO_QUERY })
790 {
791 xParagraph.set(xEA->createEnumeration()->nextElement(), uno::UNO_QUERY);
792 }
793 }
794
795 xCursor->goLeft(1, true);
796 // If this is a text on a shape, possibly the text has the trailing
797 // newline removed already. RTF may also not have the trailing newline.
798 if (!(xCursor->getString() == SAL_NEWLINE_STRING ||
799 // tdf#105444 comments need an exception, if SAL_NEWLINE_STRING defined as "\r\n"
800 (sizeof(SAL_NEWLINE_STRING) - 1 == 2 && xCursor->getString() == "\n")))
801 return;
802
803 uno::Reference<beans::XPropertySet> xDocProps(GetTextDocument(), uno::UNO_QUERY_THROW);
804 static constexpr OUStringLiteral RecordChanges(u"RecordChanges");
805
806 comphelper::ScopeGuard redlineRestore(
807 [xDocProps, aPreviousValue = xDocProps->getPropertyValue(RecordChanges)]()
808 { xDocProps->setPropertyValue(RecordChanges, aPreviousValue); });
809
810 // disable redlining, otherwise we might end up with an unwanted recorded operations
811 xDocProps->setPropertyValue(RecordChanges, uno::Any(false));
812
813 if (xParagraph)
814 {
815 // move all anchored objects to the previous paragraph
817 uno::UNO_QUERY_THROW);
818 auto xDrawPage = xDrawPageSupplier->getDrawPage();
819 if (xDrawPage && xDrawPage->hasElements())
820 {
821 // Cursor already spans two paragraphs
823 uno::UNO_QUERY_THROW);
824 auto xEnumeration = xEA->createEnumeration();
825 uno::Reference<text::XTextRange> xPrevParagraph(xEnumeration->nextElement(),
826 uno::UNO_QUERY_THROW);
827
828 uno::Reference<text::XTextRange> xParaRange(xParagraph, uno::UNO_QUERY_THROW);
829 uno::Reference<text::XTextRangeCompare> xRegionCompare(xParaRange->getText(),
830 uno::UNO_QUERY_THROW);
831 const sal_Int32 count = xDrawPage->getCount();
832 for (sal_Int32 i = 0; i < count; ++i)
833 {
834 try
835 {
836 uno::Reference<text::XTextContent> xShape(xDrawPage->getByIndex(i),
837 uno::UNO_QUERY_THROW);
838 uno::Reference<text::XTextRange> xAnchor(xShape->getAnchor(),
839 uno::UNO_SET_THROW);
840 if (xRegionCompare->compareRegionStarts(xAnchor, xParaRange) <= 0
841 && xRegionCompare->compareRegionEnds(xAnchor, xParaRange) >= 0)
842 {
843 xShape->attach(xPrevParagraph);
844 }
845 }
846 catch (const uno::Exception&)
847 {
848 // Can happen e.g. in compareRegion*, when the shape is in a header,
849 // and paragraph in body
850 }
851 }
852 }
853
854 xParagraph->dispose();
855 }
856 else
857 {
858 // Try to find and remember last bookmark in document: it potentially
859 // can be deleted by xCursor->setString() but not by xParagraph->dispose()
860 OUString sLastBookmarkName;
861 if (bEndOfDocument)
862 sLastBookmarkName = lcl_FindLastBookmark(xCursor, true);
863
864 // The cursor already selects across the paragraph break
865 // delete
866 xCursor->setString(OUString());
867
868 // call to xCursor->setString possibly did remove final bookmark
869 // from previous paragraph. We need to restore it, if there was any.
870 if (sLastBookmarkName.getLength())
871 {
872 OUString sBookmarkNameAfterRemoval = lcl_FindLastBookmark(xCursor, false);
873 if (sBookmarkNameAfterRemoval.isEmpty())
874 {
875 // Yes, it was removed. Restore
877 m_xTextFactory->createInstance("com.sun.star.text.Bookmark"),
878 uno::UNO_QUERY_THROW);
879
880 uno::Reference<container::XNamed> xBkmNamed(xBookmark,
881 uno::UNO_QUERY_THROW);
882 xBkmNamed->setName(sLastBookmarkName);
883 xTextAppend->insertTextContent(xCursor, xBookmark, !xCursor->isCollapsed());
884 }
885 }
886 }
887 }
888 catch( const uno::Exception& )
889 {
890 }
891}
892
893
895{
896 m_bIsLastSectionGroup = bIsLast;
897}
898
900{
901 m_bIsLastParaInSection = bIsLast;
902}
903
904
906{
907 m_bIsFirstParaInSection = bIsFirst;
908}
909
911{
912 m_bIsFirstParaInSectionAfterRedline = bIsFirstAfterRedline;
913}
914
916{
917 // Anchored objects may include multiple paragraphs,
918 // and none of them should be considered the first para in section.
920 && !IsInShape()
922 && !IsInFootOrEndnote();
923}
924
926{
927 m_bIsFirstParaInShape = bIsFirst;
928}
929
931{
934}
935
937{
939}
940
941
943{
944 m_bTextFrameInserted = bIsInserted;
945}
946
947
949{
950 m_bParaSectpr = bParaSectpr;
951}
952
953
955{
956 m_bSdt = bSdt;
957
958 if (m_bSdt && !m_aTextAppendStack.empty())
959 {
961 }
962 else
963 {
964 m_xSdtEntryStart.clear();
965 }
966}
967
969{
970 if (m_aTextAppendStack.empty())
971 {
972 return;
973 }
974
975 uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend;
977 = xTextAppend->getText()->createTextCursorByRange(xTextAppend->getEnd());
978 // Offset so the cursor is not adjusted as we import the SDT's content.
979 bool bStart = !xCursor->goLeft(1, /*bExpand=*/false);
980 m_xSdtStarts.push({bStart, OUString(), xCursor->getStart()});
981}
982
983const std::stack<BookmarkInsertPosition>& DomainMapper_Impl::GetSdtStarts() const
984{
985 return m_xSdtStarts;
986}
987
989{
990 if (m_xSdtStarts.empty())
991 {
992 return;
993 }
994
995 BookmarkInsertPosition aPosition = m_xSdtStarts.top();
996 m_xSdtStarts.pop();
999 uno::Reference<text::XText> xText = xEnd->getText();
1000 uno::Reference<text::XTextCursor> xCursor = xText->createTextCursorByRange(xStart);
1001 if (!xCursor)
1002 {
1003 SAL_WARN("writerfilter.dmapper", "DomainMapper_Impl::PopSdt: no start position");
1004 return;
1005 }
1006
1007 if (aPosition.m_bIsStartOfText)
1008 {
1009 // Go to the start of the end's paragraph. This helps in case
1010 // DomainMapper_Impl::AddDummyParaForTableInSection() would make our range multi-paragraph,
1011 // while the intention is to keep start/end inside the same paragraph for run SDTs.
1012 uno::Reference<text::XParagraphCursor> xParagraphCursor(xCursor, uno::UNO_QUERY);
1013 if (xParagraphCursor.is()
1014 && m_pSdtHelper->GetSdtType() == NS_ooxml::LN_CT_SdtRun_sdtContent)
1015 {
1016 xCursor->gotoRange(xEnd, /*bExpand=*/false);
1017 xParagraphCursor->gotoStartOfParagraph(/*bExpand=*/false);
1018 }
1019 }
1020 else
1021 {
1022 // Undo the goLeft() in DomainMapper_Impl::PushSdt();
1023 xCursor->goRight(1, /*bExpand=*/false);
1024 }
1025 xCursor->gotoRange(xEnd, /*bExpand=*/true);
1026
1027 std::optional<OUString> oData = m_pSdtHelper->getValueFromDataBinding();
1028 if (oData.has_value())
1029 {
1030 // Data binding has a value for us, prefer that over the in-document value.
1031 xCursor->setString(*oData);
1032
1033 // Such value is always a plain text string, remove the char style of the placeholder.
1034 uno::Reference<beans::XPropertyState> xPropertyState(xCursor, uno::UNO_QUERY);
1035 if (xPropertyState.is())
1036 {
1037 xPropertyState->setPropertyToDefault("CharStyleName");
1038 }
1039 }
1040
1041 uno::Reference<text::XTextContent> xContentControl(
1042 m_xTextFactory->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
1043 uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
1044 if (m_pSdtHelper->GetShowingPlcHdr())
1045 {
1046 xContentControlProps->setPropertyValue("ShowingPlaceHolder",
1047 uno::Any(m_pSdtHelper->GetShowingPlcHdr()));
1048 }
1049
1050 if (!m_pSdtHelper->GetPlaceholderDocPart().isEmpty())
1051 {
1052 xContentControlProps->setPropertyValue("PlaceholderDocPart",
1053 uno::Any(m_pSdtHelper->GetPlaceholderDocPart()));
1054 }
1055
1056 if (!m_pSdtHelper->GetDataBindingPrefixMapping().isEmpty())
1057 {
1058 xContentControlProps->setPropertyValue("DataBindingPrefixMappings",
1059 uno::Any(m_pSdtHelper->GetDataBindingPrefixMapping()));
1060 }
1061 if (!m_pSdtHelper->GetDataBindingXPath().isEmpty())
1062 {
1063 xContentControlProps->setPropertyValue("DataBindingXpath",
1064 uno::Any(m_pSdtHelper->GetDataBindingXPath()));
1065 }
1066 if (!m_pSdtHelper->GetDataBindingStoreItemID().isEmpty())
1067 {
1068 xContentControlProps->setPropertyValue("DataBindingStoreItemID",
1069 uno::Any(m_pSdtHelper->GetDataBindingStoreItemID()));
1070 }
1071
1072 if (!m_pSdtHelper->GetColor().isEmpty())
1073 {
1074 xContentControlProps->setPropertyValue("Color",
1075 uno::Any(m_pSdtHelper->GetColor()));
1076 }
1077
1078 if (!m_pSdtHelper->GetAppearance().isEmpty())
1079 {
1080 xContentControlProps->setPropertyValue("Appearance",
1081 uno::Any(m_pSdtHelper->GetAppearance()));
1082 }
1083
1084 if (!m_pSdtHelper->GetAlias().isEmpty())
1085 {
1086 xContentControlProps->setPropertyValue("Alias",
1087 uno::Any(m_pSdtHelper->GetAlias()));
1088 }
1089
1090 if (!m_pSdtHelper->GetTag().isEmpty())
1091 {
1092 xContentControlProps->setPropertyValue("Tag",
1093 uno::Any(m_pSdtHelper->GetTag()));
1094 }
1095
1096 if (m_pSdtHelper->GetId())
1097 {
1098 xContentControlProps->setPropertyValue("Id", uno::Any(m_pSdtHelper->GetId()));
1099 }
1100
1101 if (m_pSdtHelper->GetTabIndex())
1102 {
1103 xContentControlProps->setPropertyValue("TabIndex", uno::Any(m_pSdtHelper->GetTabIndex()));
1104 }
1105
1106 if (!m_pSdtHelper->GetLock().isEmpty())
1107 {
1108 xContentControlProps->setPropertyValue("Lock", uno::Any(m_pSdtHelper->GetLock()));
1109 }
1110
1111 if (m_pSdtHelper->getControlType() == SdtControlType::checkBox)
1112 {
1113 xContentControlProps->setPropertyValue("Checkbox", uno::Any(true));
1114
1115 xContentControlProps->setPropertyValue("Checked", uno::Any(m_pSdtHelper->GetChecked()));
1116
1117 xContentControlProps->setPropertyValue("CheckedState",
1118 uno::Any(m_pSdtHelper->GetCheckedState()));
1119
1120 xContentControlProps->setPropertyValue("UncheckedState",
1121 uno::Any(m_pSdtHelper->GetUncheckedState()));
1122 }
1123
1124 if (m_pSdtHelper->getControlType() == SdtControlType::dropDown
1125 || m_pSdtHelper->getControlType() == SdtControlType::comboBox)
1126 {
1127 std::vector<OUString>& rDisplayTexts = m_pSdtHelper->getDropDownDisplayTexts();
1128 std::vector<OUString>& rValues = m_pSdtHelper->getDropDownItems();
1129 if (rDisplayTexts.size() == rValues.size())
1130 {
1131 uno::Sequence<beans::PropertyValues> aItems(rValues.size());
1132 beans::PropertyValues* pItems = aItems.getArray();
1133 for (size_t i = 0; i < rValues.size(); ++i)
1134 {
1136 comphelper::makePropertyValue("DisplayText", rDisplayTexts[i]),
1137 comphelper::makePropertyValue("Value", rValues[i]),
1138 };
1139 pItems[i] = aItem;
1140 }
1141 xContentControlProps->setPropertyValue("ListItems", uno::Any(aItems));
1142 if (m_pSdtHelper->getControlType() == SdtControlType::dropDown)
1143 {
1144 xContentControlProps->setPropertyValue("DropDown", uno::Any(true));
1145 }
1146 else
1147 {
1148 xContentControlProps->setPropertyValue("ComboBox", uno::Any(true));
1149 }
1150 }
1151 }
1152
1153 if (m_pSdtHelper->getControlType() == SdtControlType::picture)
1154 {
1155 xContentControlProps->setPropertyValue("Picture", uno::Any(true));
1156 }
1157
1158 bool bDateFromDataBinding = false;
1159 if (m_pSdtHelper->getControlType() == SdtControlType::datePicker)
1160 {
1161 xContentControlProps->setPropertyValue("Date", uno::Any(true));
1162 OUString aDateFormat = m_pSdtHelper->getDateFormat().makeStringAndClear();
1163 xContentControlProps->setPropertyValue("DateFormat",
1164 uno::Any(aDateFormat.replaceAll("'", "\"")));
1165 xContentControlProps->setPropertyValue("DateLanguage",
1166 uno::Any(m_pSdtHelper->getLocale().makeStringAndClear()));
1167 OUString aCurrentDate = m_pSdtHelper->getDate().makeStringAndClear();
1168 if (oData.has_value())
1169 {
1170 aCurrentDate = *oData;
1171 bDateFromDataBinding = true;
1172 }
1173 xContentControlProps->setPropertyValue("CurrentDate",
1174 uno::Any(aCurrentDate));
1175 }
1176
1177 if (m_pSdtHelper->getControlType() == SdtControlType::plainText)
1178 {
1179 xContentControlProps->setPropertyValue("PlainText", uno::Any(true));
1180 }
1181
1182 xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
1183
1184 if (bDateFromDataBinding)
1185 {
1186 OUString aDateString;
1187 xContentControlProps->getPropertyValue("DateString") >>= aDateString;
1188 xCursor->setString(aDateString);
1189 }
1190
1191 m_pSdtHelper->clear();
1192}
1193
1195{
1196 PropertyMapPtr pInsert(eId == CONTEXT_SECTION ?
1199 if(eId == CONTEXT_SECTION)
1200 {
1201 if( m_bIsFirstSection )
1202 m_bIsFirstSection = false;
1203 // beginning with the second section group a section has to be inserted
1204 // into the document
1205 SectionPropertyMap* pSectionContext_ = dynamic_cast< SectionPropertyMap* >( pInsert.get() );
1206 if (!m_aTextAppendStack.empty())
1207 {
1208 uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
1209 if (xTextAppend.is() && pSectionContext_)
1210 pSectionContext_->SetStart( xTextAppend->getEnd() );
1211 }
1212 }
1213 if(eId == CONTEXT_PARAGRAPH && m_bIsSplitPara)
1214 {
1215 // Some paragraph properties only apply at the beginning of the paragraph - apply only once.
1216 if (!m_bIsFirstRun)
1217 {
1218 auto pParaContext = static_cast<ParagraphPropertyMap*>(GetTopContextOfType(eId).get());
1219 pParaContext->props().SetListId(-1);
1220 pParaContext->Erase(PROP_NUMBERING_RULES); // only true with column, not page break
1221 pParaContext->Erase(PROP_NUMBERING_LEVEL);
1222 pParaContext->Erase(PROP_NUMBERING_TYPE);
1223 pParaContext->Erase(PROP_START_WITH);
1224
1225 pParaContext->Insert(PROP_PARA_TOP_MARGIN, uno::Any(sal_uInt32(0)));
1226 pParaContext->Erase(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING);
1227 pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, uno::Any(sal_uInt32(0)));
1228 }
1229
1230 m_aPropertyStacks[eId].push( GetTopContextOfType(eId));
1231 m_bIsSplitPara = false;
1232 }
1233 else
1234 {
1235 m_aPropertyStacks[eId].push( pInsert );
1236 }
1237 m_aContextStack.push(eId);
1238
1239 m_pTopContext = m_aPropertyStacks[eId].top();
1240}
1241
1242
1244{
1245 m_aPropertyStacks[CONTEXT_STYLESHEET].push( pStyleProperties );
1247
1249}
1250
1251
1253{
1254 m_aPropertyStacks[CONTEXT_LIST].push( pListProperties );
1257}
1258
1259
1261{
1262 OSL_ENSURE(!m_aPropertyStacks[eId].empty(), "section stack already empty");
1263 if ( m_aPropertyStacks[eId].empty() )
1264 return;
1265
1266 if ( eId == CONTEXT_SECTION )
1267 {
1268 if (m_aPropertyStacks[eId].size() == 1) // tdf#112202 only top level !!!
1269 {
1271 }
1272 }
1273 else if (eId == CONTEXT_CHARACTER)
1274 {
1276 // Sadly an assert about deferredCharacterProperties being empty is not possible
1277 // here, because appendTextPortion() may not be called for every character section.
1279 }
1280
1281 if (!IsInFootOrEndnote() && IsInCustomFootnote() && !m_aPropertyStacks[eId].empty())
1282 {
1283 PropertyMapPtr pRet = m_aPropertyStacks[eId].top();
1284 if (pRet->GetFootnote().is() && m_pFootnoteContext.is())
1286 }
1287
1288 m_aPropertyStacks[eId].pop();
1289 m_aContextStack.pop();
1290 if(!m_aContextStack.empty() && !m_aPropertyStacks[m_aContextStack.top()].empty())
1291
1293 else
1294 {
1295 // OSL_ENSURE(eId == CONTEXT_SECTION, "this should happen at a section context end");
1297 }
1298}
1299
1300
1302{
1303 PropertyMapPtr pRet;
1304 if(!m_aPropertyStacks[eId].empty())
1305 pRet = m_aPropertyStacks[eId].top();
1306 return pRet;
1307}
1308
1310{
1311 return !m_aTextAppendStack.empty();
1312}
1313
1315{
1316 OSL_ENSURE(!m_aTextAppendStack.empty(), "text append stack is empty" );
1317 return m_aTextAppendStack.top().xTextAppend;
1318}
1319
1321{
1322 SAL_WARN_IF(m_aFieldStack.empty(), "writerfilter.dmapper", "Field stack is empty");
1323 return m_aFieldStack.back();
1324}
1325
1327{
1328 return !m_aTextAppendStack.empty() && !m_aTextAppendStack.top().m_aAnchoredObjects.empty();
1329}
1330
1332{
1333 OSL_ENSURE(m_aCurrentTabStops.empty(), "tab stops already initialized");
1334 for( const auto& rTabStop : rInitTabStops)
1335 {
1336 m_aCurrentTabStops.emplace_back(rTabStop);
1337 }
1338}
1339
1341{
1342 sal_Int32 nConverted = rTabStop.Position;
1343 auto aIt = std::find_if(m_aCurrentTabStops.begin(), m_aCurrentTabStops.end(),
1344 [&nConverted](const DeletableTabStop& rCurrentTabStop) { return rCurrentTabStop.Position == nConverted; });
1345 if( aIt != m_aCurrentTabStops.end() )
1346 {
1347 if( rTabStop.bDeleted )
1348 m_aCurrentTabStops.erase( aIt );
1349 else
1350 *aIt = rTabStop;
1351 }
1352 else
1353 m_aCurrentTabStops.push_back( rTabStop );
1354}
1355
1356
1358{
1359 std::vector<style::TabStop> aRet;
1360 for (const DeletableTabStop& rStop : m_aCurrentTabStops)
1361 {
1362 if (!rStop.bDeleted)
1363 aRet.push_back(rStop);
1364 }
1365 m_aCurrentTabStops.clear();
1367}
1368
1370{
1371 OUString sName;
1372 // use saved currParaStyleName as a fallback, in case no particular para style name applied.
1373 // tdf#134784 except in the case of first paragraph of shapes to avoid bad fallback.
1374 // TODO fix this "highly inaccurate" m_sCurrentParaStyleName
1375 if ( !IsInShape() )
1377
1379 if ( pParaContext && pParaContext->isSet(PROP_PARA_STYLE_NAME) )
1380 pParaContext->getProperty(PROP_PARA_STYLE_NAME)->second >>= sName;
1381
1382 // In rare situations the name might still be blank, so use the default style,
1383 // despite documentation that states, "If this attribute is not specified for any style,
1384 // then no properties shall be applied to objects of the specified type."
1385 // Word, however, assigns "Normal" style even in these situations.
1386 if ( !m_bInStyleSheetImport && sName.isEmpty() )
1388
1389 return sName;
1390}
1391
1393{
1394 // After import the default style won't change and is frequently requested: cache the LO style name.
1395 // TODO assert !InStyleSheetImport? This function really only makes sense once import is finished anyway.
1396 if ( m_sDefaultParaStyleName.isEmpty() )
1397 {
1398 const StyleSheetEntryPtr pEntry = GetStyleSheetTable()->FindDefaultParaStyle();
1399 if ( pEntry && !pEntry->m_sConvertedStyleName.isEmpty() )
1400 {
1401 if ( !m_bInStyleSheetImport )
1402 m_sDefaultParaStyleName = pEntry->m_sConvertedStyleName;
1403 return pEntry->m_sConvertedStyleName;
1404 }
1405 else
1406 return "Standard";
1407 }
1409}
1410
1411uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara, bool* pIsDocDefault)
1412{
1413 while(pEntry)
1414 {
1415 if(pEntry->m_pProperties)
1416 {
1417 std::optional<PropertyMap::Property> aProperty =
1418 pEntry->m_pProperties->getProperty(eId);
1419 if( aProperty )
1420 {
1421 if (pIsDocDefault)
1422 *pIsDocDefault = pEntry->m_pProperties->isDocDefault(eId);
1423
1424 return aProperty->second;
1425 }
1426 }
1427 //search until the property is set or no parent is available
1428 StyleSheetEntryPtr pNewEntry;
1429 if ( !pEntry->m_sBaseStyleIdentifier.isEmpty() )
1430 pNewEntry = GetStyleSheetTable()->FindStyleSheetByISTD(pEntry->m_sBaseStyleIdentifier);
1431
1432 SAL_WARN_IF( pEntry == pNewEntry, "writerfilter.dmapper", "circular loop in style hierarchy?");
1433
1434 if (pEntry == pNewEntry) //fdo#49587
1435 break;
1436
1437 pEntry = pNewEntry;
1438 }
1439 // not found in style, try the document's DocDefault properties
1440 if ( bDocDefaults && bPara )
1441 {
1442 const PropertyMapPtr& pDefaultParaProps = GetStyleSheetTable()->GetDefaultParaProps();
1443 if ( pDefaultParaProps )
1444 {
1445 std::optional<PropertyMap::Property> aProperty = pDefaultParaProps->getProperty(eId);
1446 if ( aProperty )
1447 {
1448 if (pIsDocDefault)
1449 *pIsDocDefault = true;
1450
1451 return aProperty->second;
1452 }
1453 }
1454 }
1455 if ( bDocDefaults && isCharacterProperty(eId) )
1456 {
1457 const PropertyMapPtr& pDefaultCharProps = GetStyleSheetTable()->GetDefaultCharProps();
1458 if ( pDefaultCharProps )
1459 {
1460 std::optional<PropertyMap::Property> aProperty = pDefaultCharProps->getProperty(eId);
1461 if ( aProperty )
1462 {
1463 if (pIsDocDefault)
1464 *pIsDocDefault = true;
1465
1466 return aProperty->second;
1467 }
1468 }
1469 }
1470
1471 if (pIsDocDefault)
1472 *pIsDocDefault = false;
1473
1474 return uno::Any();
1475}
1476
1478{
1479 StyleSheetEntryPtr pEntry;
1481 pEntry = GetStyleSheetTable()->GetCurrentEntry();
1482 else
1483 pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(GetCurrentParaStyleName());
1484 return GetPropertyFromStyleSheet(eId, pEntry, /*bDocDefaults=*/true, /*bPara=*/true);
1485}
1486
1488{
1490 return uno::Any();
1491
1492 StyleSheetEntryPtr pEntry;
1493 OUString sCharStyleName;
1495 pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(sCharStyleName);
1496 return GetPropertyFromStyleSheet(eId, pEntry, /*bDocDefaults=*/false, /*bPara=*/false);
1497}
1498
1500{
1501 // first look in directly applied attributes
1502 if ( rContext )
1503 {
1504 std::optional<PropertyMap::Property> aProperty = rContext->getProperty(eId);
1505 if ( aProperty )
1506 return aProperty->second;
1507 }
1508
1509 // then look whether it was directly applied as a paragraph property
1511 if (pParaContext && rContext != pParaContext)
1512 {
1513 std::optional<PropertyMap::Property> aProperty = pParaContext->getProperty(eId);
1514 if (aProperty)
1515 return aProperty->second;
1516 }
1517
1518 // then look whether it was inherited from a directly applied character style
1519 if ( eId != PROP_CHAR_STYLE_NAME && isCharacterProperty(eId) )
1520 {
1521 uno::Any aRet = GetPropertyFromCharStyleSheet(eId, rContext);
1522 if ( aRet.hasValue() )
1523 return aRet;
1524 }
1525
1526 // then look in current paragraph style, and docDefaults
1528}
1529
1530OUString DomainMapper_Impl::GetListStyleName(sal_Int32 nListId)
1531{
1532 auto const pList(GetListTable()->GetList( nListId ));
1533 return pList ? pList->GetStyleName() : OUString();
1534}
1535
1537{
1538 if(!m_pListTable)
1539 m_pListTable =
1541 return m_pListTable;
1542}
1543
1544
1546{
1547 switch (deferredBreakType)
1548 {
1549 case LINE_BREAK:
1551 break;
1552 case COLUMN_BREAK:
1554 break;
1555 case PAGE_BREAK:
1556 // See SwWW8ImplReader::HandlePageBreakChar(), page break should be
1557 // ignored inside tables.
1558 if (m_nTableDepth > 0)
1559 return;
1560
1562 break;
1563 default:
1564 return;
1565 }
1566}
1567
1569{
1570 switch (deferredBreakType)
1571 {
1572 case LINE_BREAK:
1573 return m_nLineBreaksDeferred > 0;
1574 case COLUMN_BREAK:
1576 case PAGE_BREAK:
1578 default:
1579 return false;
1580 }
1581}
1582
1584{
1585 switch (deferredBreakType)
1586 {
1587 case LINE_BREAK:
1588 assert(m_nLineBreaksDeferred > 0);
1590 break;
1591 case COLUMN_BREAK:
1593 break;
1594 case PAGE_BREAK:
1595 m_bIsPageBreakDeferred = false;
1597 break;
1598 default:
1599 break;
1600 }
1601}
1602
1604{
1607 m_bIsPageBreakDeferred = false;
1609}
1610
1612{
1614}
1615
1617{
1619}
1620
1622{
1623 m_bSdtEndDeferred = bSdtEndDeferred;
1624}
1625
1627{
1628 return m_bSdtEndDeferred;
1629}
1630
1631void DomainMapper_Impl::setParaSdtEndDeferred(bool bParaSdtEndDeferred)
1632{
1633 m_bParaSdtEndDeferred = bParaSdtEndDeferred;
1634}
1635
1637{
1638 return m_bParaSdtEndDeferred;
1639}
1640
1641static void lcl_MoveBorderPropertiesToFrame(std::vector<beans::PropertyValue>& rFrameProperties,
1642 uno::Reference<text::XTextRange> const& xStartTextRange,
1643 uno::Reference<text::XTextRange> const& xEndTextRange,
1644 bool bIsRTFImport)
1645{
1646 try
1647 {
1648 if (!xStartTextRange.is()) //rhbz#1077780
1649 return;
1650 uno::Reference<text::XTextCursor> xRangeCursor = xStartTextRange->getText()->createTextCursorByRange( xStartTextRange );
1651 xRangeCursor->gotoRange( xEndTextRange, true );
1652
1653 uno::Reference<beans::XPropertySet> xTextRangeProperties(xRangeCursor, uno::UNO_QUERY);
1654 if(!xTextRangeProperties.is())
1655 return ;
1656
1657 static PropertyIds const aBorderProperties[] =
1658 {
1667 };
1668
1669 // The frame width specified does not include border spacing,
1670 // so the frame needs to be increased by the left/right para border spacing amount
1671 sal_Int32 nWidth = 0;
1672 sal_Int32 nIndexOfWidthProperty = -1;
1673 sal_Int16 nType = text::SizeType::FIX;
1674 for (size_t i = 0; nType == text::SizeType::FIX && i < rFrameProperties.size(); ++i)
1675 {
1676 if (rFrameProperties[i].Name == "WidthType")
1677 rFrameProperties[i].Value >>= nType;
1678 else if (rFrameProperties[i].Name == "Width")
1679 nIndexOfWidthProperty = i;
1680 }
1681 if (nIndexOfWidthProperty > -1 && nType == text::SizeType::FIX)
1682 rFrameProperties[nIndexOfWidthProperty].Value >>= nWidth;
1683
1684 for( size_t nProperty = 0; nProperty < SAL_N_ELEMENTS( aBorderProperties ); ++nProperty)
1685 {
1686 const OUString & sPropertyName = getPropertyName(aBorderProperties[nProperty]);
1687 beans::PropertyValue aValue;
1688 aValue.Name = sPropertyName;
1689 aValue.Value = xTextRangeProperties->getPropertyValue(sPropertyName);
1690 if( nProperty < 4 )
1691 xTextRangeProperties->setPropertyValue( sPropertyName, uno::Any(table::BorderLine2()));
1692 else // border spacing
1693 {
1694 sal_Int32 nDistance = 0;
1695 aValue.Value >>= nDistance;
1696
1697 // left4/right5 need to be duplicated because of INVERT_BORDER_SPACING (DOCX only)
1698 // Do not duplicate the top6/bottom7 border spacing.
1699 if (nProperty > 5 || bIsRTFImport)
1700 aValue.Value <<= sal_Int32(0);
1701
1702 // frames need to be increased by the left/right para border spacing amount
1703 // This is needed for RTF as well, but that requires other export/import fixes.
1704 if (!bIsRTFImport && nProperty < 6 && nWidth && nDistance)
1705 {
1706 nWidth += nDistance;
1707 rFrameProperties[nIndexOfWidthProperty].Value <<= nWidth;
1708 }
1709 }
1710 if (aValue.Value.hasValue())
1711 rFrameProperties.push_back(aValue);
1712 }
1713 }
1714 catch( const uno::Exception& )
1715 {
1716 }
1717}
1718
1719
1720static void lcl_AddRange(
1721 ParagraphPropertiesPtr const & pToBeSavedProperties,
1722 uno::Reference< text::XTextAppend > const& xTextAppend,
1723 TextAppendContext const & rAppendContext)
1724{
1726 xTextAppend->createTextCursorByRange( rAppendContext.xInsertPosition.is() ? rAppendContext.xInsertPosition : xTextAppend->getEnd()), uno::UNO_QUERY_THROW );
1727 pToBeSavedProperties->SetEndingRange(xParaCursor->getStart());
1728 xParaCursor->gotoStartOfParagraph( false );
1729
1730 pToBeSavedProperties->SetStartingRange(xParaCursor->getStart());
1731}
1732
1733
1734//define some default frame width - 0cm ATM: this allow the frame to be wrapped around the text
1735constexpr sal_Int32 DEFAULT_FRAME_MIN_WIDTH = 0;
1736constexpr sal_Int32 DEFAULT_FRAME_MIN_HEIGHT = 0;
1737constexpr sal_Int32 DEFAULT_VALUE = 0;
1738
1739std::vector<css::beans::PropertyValue>
1741{
1742 std::vector<beans::PropertyValue> aFrameProperties;
1743
1744 try
1745 {
1746 // A paragraph's properties come from direct formatting or somewhere in the style hierarchy
1747 std::vector<const ParagraphProperties*> vProps;
1748 vProps.emplace_back(&rProps);
1749 sal_Int8 nSafetyLimit = 16;
1750 StyleSheetEntryPtr pStyle
1751 = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(rProps.GetParaStyleName());
1752 while (nSafetyLimit-- && pStyle && pStyle->m_pProperties)
1753 {
1754 vProps.emplace_back(&pStyle->m_pProperties->props());
1755 assert(pStyle->m_sBaseStyleIdentifier != pStyle->m_sStyleName);
1756 if (pStyle->m_sBaseStyleIdentifier.isEmpty())
1757 break;
1758 pStyle = GetStyleSheetTable()->FindStyleSheetByISTD(pStyle->m_sBaseStyleIdentifier);
1759 }
1760 SAL_WARN_IF(!nSafetyLimit, "writerfilter.dmapper", "Inheritance loop likely: early exit");
1761
1762
1763 sal_Int32 nWidth = -1;
1764 for (const auto pProp : vProps)
1765 {
1766 if (pProp->Getw() < 0)
1767 continue;
1768 nWidth = pProp->Getw();
1769 break;
1770 }
1771 bool bAutoWidth = nWidth < 1;
1772 if (bAutoWidth)
1773 nWidth = DEFAULT_FRAME_MIN_WIDTH;
1774 aFrameProperties.push_back(
1776 aFrameProperties.push_back(
1778 bAutoWidth ? text::SizeType::MIN : text::SizeType::FIX));
1779
1780 bool bValidH = false;
1781 sal_Int32 nHeight = DEFAULT_FRAME_MIN_HEIGHT;
1782 for (const auto pProp : vProps)
1783 {
1784 if (pProp->Geth() < 0)
1785 continue;
1786 nHeight = pProp->Geth();
1787 bValidH = true;
1788 break;
1789 }
1790 aFrameProperties.push_back(
1792
1793 sal_Int16 nhRule = -1;
1794 for (const auto pProp : vProps)
1795 {
1796 if (pProp->GethRule() < 0)
1797 continue;
1798 nhRule = pProp->GethRule();
1799 break;
1800 }
1801 if (nhRule < 0)
1802 {
1803 if (bValidH && nHeight)
1804 {
1805 // [MS-OE376] Word uses a default value of "atLeast" for
1806 // this attribute when the value of the h attribute is not 0.
1807 nhRule = text::SizeType::MIN;
1808 }
1809 else
1810 {
1811 nhRule = text::SizeType::VARIABLE;
1812 }
1813 }
1814 aFrameProperties.push_back(
1816
1817 bool bValidX = false;
1818 sal_Int32 nX = DEFAULT_VALUE;
1819 for (const auto pProp : vProps)
1820 {
1821 bValidX = pProp->IsxValid();
1822 if (!bValidX)
1823 continue;
1824 nX = pProp->Getx();
1825 break;
1826 }
1827 aFrameProperties.push_back(
1829
1830 sal_Int16 nHoriOrient = text::HoriOrientation::NONE;
1831 for (const auto pProp : vProps)
1832 {
1833 if (pProp->GetxAlign() < 0)
1834 continue;
1835 nHoriOrient = pProp->GetxAlign();
1836 break;
1837 }
1838 aFrameProperties.push_back(
1840
1841 //Default the anchor in case FramePr_hAnchor is missing ECMA 17.3.1.11
1842 sal_Int16 nHAnchor = text::RelOrientation::FRAME; // 'text'
1843 for (const auto pProp : vProps)
1844 {
1845 if (pProp->GethAnchor() < 0)
1846 continue;
1847 nHAnchor = pProp->GethAnchor();
1848 break;
1849 }
1850 aFrameProperties.push_back(
1852
1853 bool bValidY = false;
1854 sal_Int32 nY = DEFAULT_VALUE;
1855 for (const auto pProp : vProps)
1856 {
1857 bValidY = pProp->IsyValid();
1858 if (!bValidY)
1859 continue;
1860 nY = pProp->Gety();
1861 break;
1862 }
1863 aFrameProperties.push_back(
1865
1866 sal_Int16 nVertOrient = text::VertOrientation::NONE;
1867 for (const auto pProp : vProps)
1868 {
1869 if (pProp->GetyAlign() < 0)
1870 continue;
1871 nVertOrient = pProp->GetyAlign();
1872 break;
1873 }
1874 aFrameProperties.push_back(
1876
1877 //Default the anchor in case FramePr_vAnchor is missing ECMA 17.3.1.11
1878 sal_Int16 nVAnchor = text::RelOrientation::FRAME; // 'text'
1879 // vAlign is ignored if vAnchor is set to 'text'. So, if w:y is not defined,
1880 // but there is a defined vAlign, then a missing vAnchor should become 'margin'.
1881 if (!bValidY && nVertOrient)
1882 {
1883 nVAnchor = text::RelOrientation::PAGE_PRINT_AREA; // 'margin'
1884 }
1885 for (const auto pProp : vProps)
1886 {
1887 if (pProp->GetvAnchor() < 0)
1888 continue;
1889 nVAnchor = pProp->GetvAnchor();
1890 break;
1891 }
1892 aFrameProperties.push_back(
1894
1895 text::WrapTextMode nWrap = text::WrapTextMode_NONE;
1896 for (const auto pProp : vProps)
1897 {
1898 if (pProp->GetWrap() == text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE)
1899 continue;
1900 nWrap = pProp->GetWrap();
1901 break;
1902 }
1903 aFrameProperties.push_back(
1905
1906 sal_Int32 nRightDist = 0;
1907 sal_Int32 nLeftDist = 0;
1908 for (const auto pProp : vProps)
1909 {
1910 if (pProp->GethSpace() < 0)
1911 continue;
1912 nLeftDist = nRightDist = pProp->GethSpace();
1913 break;
1914 }
1915 aFrameProperties.push_back(comphelper::makePropertyValue(
1917 nHoriOrient == text::HoriOrientation::LEFT ? 0 : nLeftDist));
1918 aFrameProperties.push_back(comphelper::makePropertyValue(
1920 nHoriOrient == text::HoriOrientation::RIGHT ? 0 : nRightDist));
1921
1922 sal_Int32 nBottomDist = 0;
1923 sal_Int32 nTopDist = 0;
1924 for (const auto pProp : vProps)
1925 {
1926 if (pProp->GetvSpace() < 0)
1927 continue;
1928 nTopDist = nBottomDist = pProp->GetvSpace();
1929 break;
1930 }
1931 aFrameProperties.push_back(comphelper::makePropertyValue(
1933 nVertOrient == text::VertOrientation::TOP ? 0 : nTopDist));
1934 aFrameProperties.push_back(comphelper::makePropertyValue(
1936 nVertOrient == text::VertOrientation::BOTTOM ? 0 : nBottomDist));
1937 }
1938 catch (const uno::Exception&)
1939 {
1940 }
1941
1942 return aFrameProperties;
1943}
1944
1946{
1947 if (m_aTextAppendStack.empty())
1948 return;
1949 TextAppendContext& rAppendContext = m_aTextAppendStack.top();
1950 // n#779642: ignore fly frame inside table as it could lead to messy situations
1951 if (!rAppendContext.pLastParagraphProperties)
1952 return;
1953 if (!rAppendContext.pLastParagraphProperties->IsFrameMode())
1954 return;
1955 if (!hasTableManager())
1956 return;
1957 if (getTableManager().isInTable())
1958 return;
1959
1960 std::vector<beans::PropertyValue> aFrameProperties
1962
1963 if (const std::optional<sal_Int16> nDirection = PopFrameDirection())
1964 {
1965 aFrameProperties.push_back(
1967 }
1968
1969 if (bPreventOverlap)
1970 aFrameProperties.push_back(comphelper::makePropertyValue("AllowOverlap", uno::Any(false)));
1971
1972 // If there is no fill, the Word default is 100% transparency.
1973 // Otherwise CellColorHandler has priority, and this setting
1974 // will be ignored.
1975 aFrameProperties.push_back(comphelper::makePropertyValue(
1977
1979 { { "ParaFrameProperties", uno::Any(true) } }));
1980 aFrameProperties.push_back(comphelper::makePropertyValue("FrameInteropGrabBag", aGrabBag));
1981
1982 lcl_MoveBorderPropertiesToFrame(aFrameProperties,
1983 rAppendContext.pLastParagraphProperties->GetStartingRange(),
1984 rAppendContext.pLastParagraphProperties->GetEndingRange(),
1985 IsRTFImport());
1986
1987 //frame conversion has to be executed after table conversion, not now
1988 RegisterFrameConversion(rAppendContext.pLastParagraphProperties->GetStartingRange(),
1989 rAppendContext.pLastParagraphProperties->GetEndingRange(),
1990 std::move(aFrameProperties));
1991}
1992
1994static sal_Int32 lcl_getListId(const StyleSheetEntryPtr& rEntry, const StyleSheetTablePtr& rStyleTable, bool & rNumberingFromBaseStyle)
1995{
1996 const StyleSheetPropertyMap* pEntryProperties = rEntry->m_pProperties.get();
1997 if (!pEntryProperties)
1998 return -1;
1999
2000 sal_Int32 nListId = pEntryProperties->props().GetListId();
2001 // The style itself has a list id.
2002 if (nListId >= 0)
2003 return nListId;
2004
2005 // The style has no parent.
2006 if (rEntry->m_sBaseStyleIdentifier.isEmpty())
2007 return -1;
2008
2009 const StyleSheetEntryPtr pParent = rStyleTable->FindStyleSheetByISTD(rEntry->m_sBaseStyleIdentifier);
2010 // No such parent style or loop in the style hierarchy.
2011 if (!pParent || pParent == rEntry)
2012 return -1;
2013
2014 rNumberingFromBaseStyle = true;
2015
2016 return lcl_getListId(pParent, rStyleTable, rNumberingFromBaseStyle);
2017}
2018
2024 const PropertyMapPtr& pParaContext)
2025{
2026 sal_Int16 nListLevel = -1;
2027 if (pParaContext)
2028 {
2029 // Deliberately ignore inherited PROP_NUMBERING_LEVEL. Only trust StyleSheetEntry for that.
2030 std::optional<PropertyMap::Property> aLvl = pParaContext->getProperty(PROP_NUMBERING_LEVEL);
2031 if (aLvl)
2032 aLvl->second >>= nListLevel;
2033
2034 if (nListLevel != -1)
2035 return nListLevel;
2036 }
2037
2038 if (!pEntry)
2039 return -1;
2040
2041 const StyleSheetPropertyMap* pEntryProperties = pEntry->m_pProperties.get();
2042 if (!pEntryProperties)
2043 return -1;
2044
2045 nListLevel = pEntryProperties->GetListLevel();
2046 // The style itself has a list level.
2047 if (nListLevel >= 0)
2048 return nListLevel;
2049
2050 // The style has no parent.
2051 if (pEntry->m_sBaseStyleIdentifier.isEmpty())
2052 return -1;
2053
2054 const StyleSheetEntryPtr pParent = GetStyleSheetTable()->FindStyleSheetByISTD(pEntry->m_sBaseStyleIdentifier);
2055 // No such parent style or loop in the style hierarchy.
2056 if (!pParent || pParent == pEntry)
2057 return -1;
2058
2059 return GetListLevel(pParent);
2060}
2061
2062void DomainMapper_Impl::ValidateListLevel(const OUString& sStyleIdentifierD)
2063{
2064 StyleSheetEntryPtr pMyStyle = GetStyleSheetTable()->FindStyleSheetByISTD(sStyleIdentifierD);
2065 if (!pMyStyle)
2066 return;
2067
2068 sal_Int8 nListLevel = GetListLevel(pMyStyle);
2069 if (nListLevel < 0 || nListLevel >= WW_OUTLINE_MAX)
2070 return;
2071
2072 bool bDummy = false;
2073 sal_Int16 nListId = lcl_getListId(pMyStyle, GetStyleSheetTable(), bDummy);
2074 if (nListId < 1)
2075 return;
2076
2077 auto const pList(GetListTable()->GetList(nListId));
2078 if (!pList)
2079 return;
2080
2081 auto pLevel = pList->GetLevel(nListLevel);
2082 if (!pLevel && pList->GetAbstractDefinition())
2083 pLevel = pList->GetAbstractDefinition()->GetLevel(nListLevel);
2084 if (!pLevel)
2085 return;
2086
2087 if (!pLevel->GetParaStyle())
2088 {
2089 // First come, first served, and it hasn't been claimed yet, so claim it now.
2090 pLevel->SetParaStyle(pMyStyle);
2091 }
2092 else if (pLevel->GetParaStyle() != pMyStyle)
2093 {
2094 // This level is already used by another style, so prevent numbering via this style
2095 // by setting to body level (9).
2096 pMyStyle->m_pProperties->SetListLevel(WW_OUTLINE_MAX);
2097 // WARNING: PROP_NUMBERING_LEVEL is now out of sync with GetListLevel()
2098 }
2099}
2100
2101void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, const bool bRemove, const bool bNoNumbering )
2102{
2104 return;
2105
2106 if (!m_aFieldStack.empty())
2107 {
2108 FieldContextPtr pFieldContext = m_aFieldStack.back();
2109 if (pFieldContext && !pFieldContext->IsCommandCompleted())
2110 {
2111 std::vector<OUString> aCommandParts = pFieldContext->GetCommandParts();
2112 if (!aCommandParts.empty() && aCommandParts[0] == "IF")
2113 {
2114 // Conditional text field conditions don't support linebreaks in Writer.
2115 return;
2116 }
2117 }
2118
2119 if (pFieldContext && pFieldContext->IsCommandCompleted())
2120 {
2121 if (pFieldContext->GetFieldId() == FIELD_IF)
2122 {
2123 // Conditional text fields can't contain newlines, finish the paragraph later.
2124 FieldParagraph aFinish{pPropertyMap, bRemove};
2125 pFieldContext->GetParagraphsToFinish().push_back(aFinish);
2126 return;
2127 }
2128 }
2129 }
2130
2131#ifdef DBG_UTIL
2132 TagLogger::getInstance().startElement("finishParagraph");
2133#endif
2134
2136 ParagraphPropertyMap* pParaContext = dynamic_cast< ParagraphPropertyMap* >( pPropertyMap.get() );
2137 if (m_aTextAppendStack.empty())
2138 return;
2139 TextAppendContext& rAppendContext = m_aTextAppendStack.top();
2140 uno::Reference< text::XTextAppend > xTextAppend(rAppendContext.xTextAppend);
2141#ifdef DBG_UTIL
2142 TagLogger::getInstance().attribute("isTextAppend", sal_uInt32(xTextAppend.is()));
2143#endif
2144
2145 const StyleSheetEntryPtr pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( GetCurrentParaStyleName() );
2146 SAL_WARN_IF(!pEntry, "writerfilter.dmapper", "no style sheet found");
2147 const StyleSheetPropertyMap* pStyleSheetProperties = pEntry ? pEntry->m_pProperties.get() : nullptr;
2148 sal_Int32 nListId = pParaContext ? pParaContext->props().GetListId() : -1;
2149 bool isNumberingViaStyle(false);
2150 bool isNumberingViaRule = nListId > -1;
2151 if ( !bRemove && pStyleSheetProperties && pParaContext )
2152 {
2153 if (!pEntry || pEntry->m_nStyleTypeCode != StyleType::STYLE_TYPE_PARA) {
2154 // We could not resolve paragraph style or it is not a paragraph style
2155 // Remove this style reference, otherwise it will cause exceptions during further
2156 // processing and not all paragraph styles will be initialized.
2157 SAL_WARN("writerfilter.dmapper", "Paragraph style is incorrect. Ignored");
2158 pParaContext->Erase(PROP_PARA_STYLE_NAME);
2159 }
2160
2161 bool bNumberingFromBaseStyle = false;
2162 if (!isNumberingViaRule)
2163 nListId = lcl_getListId(pEntry, GetStyleSheetTable(), bNumberingFromBaseStyle);
2164
2165 //apply numbering level/style to paragraph if it was set at the style, but only if the paragraph itself
2166 //does not specify the numbering
2167 sal_Int16 nListLevel = GetListLevel(pEntry, pParaContext);
2168 // Undefined listLevel with a valid numId is treated as a first level numbering.
2169 if (nListLevel == -1 && nListId > (IsOOXMLImport() ? 0 : -1))
2170 nListLevel = 0;
2171
2172 if (!bNoNumbering && nListLevel >= 0 && nListLevel < 9)
2173 pParaContext->Insert( PROP_NUMBERING_LEVEL, uno::Any(nListLevel), false );
2174
2175 auto const pList(GetListTable()->GetList(nListId));
2176 if (pList && !pParaContext->isSet(PROP_NUMBERING_STYLE_NAME))
2177 {
2178 // ListLevel 9 means Body Level/no numbering.
2179 if (bNoNumbering || nListLevel == 9)
2180 {
2181 pParaContext->Insert(PROP_NUMBERING_STYLE_NAME, uno::Any(OUString()), true);
2182 pParaContext->Erase(PROP_NUMBERING_LEVEL);
2183 }
2184 else if ( !isNumberingViaRule )
2185 {
2186 isNumberingViaStyle = true;
2187 // Since LO7.0/tdf#131321 fixed the loss of numbering in styles, this OUGHT to be obsolete,
2188 // but now other new/critical LO7.0 code expects it, and perhaps some corner cases still need it as well.
2189 pParaContext->Insert(PROP_NUMBERING_STYLE_NAME, uno::Any(pList->GetStyleName()), true);
2190 }
2191 else
2192 {
2193 // we have direct numbering, as well as paragraph-style numbering.
2194 // Apply the style if it uses the same list as the direct numbering,
2195 // otherwise the directly-applied-to-paragraph status will be lost,
2196 // and the priority of the numbering-style-indents will be lowered. tdf#133000
2197 bool bDummy;
2198 if (nListId == lcl_getListId(pEntry, GetStyleSheetTable(), bDummy))
2199 pParaContext->Insert( PROP_NUMBERING_STYLE_NAME, uno::Any(pList->GetStyleName()), true );
2200 }
2201 }
2202
2203 if ( isNumberingViaStyle )
2204 {
2205 // When numbering is defined by the paragraph style, then the para-style indents have priority.
2206 // But since import has just copied para-style's PROP_NUMBERING_STYLE_NAME directly onto the paragraph,
2207 // the numbering indents now have the priority.
2208 // So now import must also copy the para-style indents directly onto the paragraph to compensate.
2209 std::optional<PropertyMap::Property> oProperty;
2210 const StyleSheetEntryPtr pParent = (!pEntry->m_sBaseStyleIdentifier.isEmpty()) ? GetStyleSheetTable()->FindStyleSheetByISTD(pEntry->m_sBaseStyleIdentifier) : nullptr;
2211 const StyleSheetPropertyMap* pParentProperties = pParent ? pParent->m_pProperties.get() : nullptr;
2212 if (!pEntry->m_sBaseStyleIdentifier.isEmpty())
2213 {
2214 oProperty = pStyleSheetProperties->getProperty(PROP_PARA_FIRST_LINE_INDENT);
2215 if ( oProperty
2216 // If the numbering comes from a base style, indent of the base style has also priority.
2217 || (bNumberingFromBaseStyle && pParentProperties && (oProperty = pParentProperties->getProperty(PROP_PARA_FIRST_LINE_INDENT))) )
2218 pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, oProperty->second, /*bOverwrite=*/false);
2219 }
2220 oProperty = pStyleSheetProperties->getProperty(PROP_PARA_LEFT_MARGIN);
2221 if ( oProperty
2222 || (bNumberingFromBaseStyle && pParentProperties && (oProperty = pParentProperties->getProperty(PROP_PARA_LEFT_MARGIN))) )
2223 pParaContext->Insert(PROP_PARA_LEFT_MARGIN, oProperty->second, /*bOverwrite=*/false);
2224
2225 // We're inheriting properties from a numbering style. Make sure a possible right margin is inherited from the base style.
2226 sal_Int32 nParaRightMargin;
2227 if ( pParentProperties && (oProperty = pParentProperties->getProperty(PROP_PARA_RIGHT_MARGIN)) && (nParaRightMargin = oProperty->second.get<sal_Int32>()) != 0 )
2228 {
2229 // If we're setting the right margin, we should set the first / left margin as well from the numbering style.
2230 const sal_Int32 nFirstLineIndent = getNumberingProperty(nListId, nListLevel, "FirstLineIndent");
2231 const sal_Int32 nParaLeftMargin = getNumberingProperty(nListId, nListLevel, "IndentAt");
2232 if (nFirstLineIndent != 0)
2233 pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, uno::Any(nFirstLineIndent), /*bOverwrite=*/false);
2234 if (nParaLeftMargin != 0)
2235 pParaContext->Insert(PROP_PARA_LEFT_MARGIN, uno::Any(nParaLeftMargin), /*bOverwrite=*/false);
2236
2237 // Override right margin value with value from current style, if any
2238 if (pStyleSheetProperties && pStyleSheetProperties->isSet(PROP_PARA_RIGHT_MARGIN))
2239 nParaRightMargin = pStyleSheetProperties->getProperty(PROP_PARA_RIGHT_MARGIN)->second.get<sal_Int32>();
2240
2241 pParaContext->Insert(PROP_PARA_RIGHT_MARGIN, uno::Any(nParaRightMargin), /*bOverwrite=*/false);
2242 }
2243 }
2244 // Paragraph style based right paragraph indentation affects not paragraph style based lists in DOCX.
2245 // Apply it as direct formatting, also left and first line indentation of numbering to keep them.
2246 else if (isNumberingViaRule)
2247 {
2249 if ( aRightMargin != uno::Any() )
2250 {
2251 pParaContext->Insert(PROP_PARA_RIGHT_MARGIN, aRightMargin, /*bOverwrite=*/false);
2252
2253 const sal_Int32 nFirstLineIndent = getNumberingProperty(nListId, nListLevel, "FirstLineIndent");
2254 const sal_Int32 nParaLeftMargin = getNumberingProperty(nListId, nListLevel, "IndentAt");
2255 if (nFirstLineIndent != 0)
2256 pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, uno::Any(nFirstLineIndent), /*bOverwrite=*/false);
2257 if (nParaLeftMargin != 0)
2258 pParaContext->Insert(PROP_PARA_LEFT_MARGIN, uno::Any(nParaLeftMargin), /*bOverwrite=*/false);
2259 }
2260 }
2261
2262 if (nListId == 0 && !pList)
2263 {
2264 // listid = 0 and no list definition is used in DOCX to stop numbering
2265 // defined somewhere in parent styles
2266 // And here we should explicitly set left margin and first-line margin.
2267 // They can be taken from referred style, but not from styles with listid!
2269 if (aProp.hasValue())
2270 pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, aProp, false);
2271 else
2272 pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, uno::Any(sal_uInt32(0)), false);
2273
2275 if (aProp.hasValue())
2276 pParaContext->Insert(PROP_PARA_LEFT_MARGIN, aProp, false);
2277 else
2278 pParaContext->Insert(PROP_PARA_LEFT_MARGIN, uno::Any(sal_uInt32(0)), false);
2279 }
2280 }
2281
2282 // apply AutoSpacing: it has priority over all other margin settings
2283 // (note that numbering with autoSpacing is handled separately later on)
2284 const bool bAllowAdjustments = !GetSettingsTable()->GetDoNotUseHTMLParagraphAutoSpacing();
2285 sal_Int32 nBeforeAutospacing = -1;
2286 bool bIsAutoSet = pParaContext && pParaContext->isSet(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING);
2287 const bool bNoTopmargin = pParaContext && !pParaContext->isSet(PROP_PARA_TOP_MARGIN);
2288 // apply INHERITED autospacing only if top margin is not set
2289 if ( bIsAutoSet || bNoTopmargin )
2290 {
2291 GetAnyProperty(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING, pPropertyMap) >>= nBeforeAutospacing;
2292 // tdf#137655 only w:beforeAutospacing=0 was specified, but not PARA_TOP_MARGIN
2293 // (see default_spacing = -1 in processing of LN_CT_Spacing_beforeAutospacing)
2294 if ( bNoTopmargin && nBeforeAutospacing == ConversionHelper::convertTwipToMM100(-1) )
2295 {
2296 sal_Int32 nStyleAuto = -1;
2298 if (nStyleAuto > 0)
2299 nBeforeAutospacing = 0;
2300 }
2301 }
2302 if ( nBeforeAutospacing > -1 && pParaContext )
2303 {
2304 if (bAllowAdjustments)
2305 {
2307 (GetIsFirstParagraphInSection() && GetSectionContext() && GetSectionContext()->IsFirstSection()) ||
2309 {
2310 // export requires grabbag to match top_margin, so keep them in sync
2311 if (nBeforeAutospacing && bIsAutoSet)
2312 pParaContext->Insert( PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING, uno::Any( sal_Int32(0) ),true, PARA_GRAB_BAG );
2313 nBeforeAutospacing = 0;
2314 }
2315 }
2316 pParaContext->Insert(PROP_PARA_TOP_MARGIN, uno::Any(nBeforeAutospacing));
2317 }
2318
2319 sal_Int32 nAfterAutospacing = -1;
2320 bIsAutoSet = pParaContext && pParaContext->isSet(PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING);
2321 const bool bNoBottomMargin = pParaContext && !pParaContext->isSet(PROP_PARA_BOTTOM_MARGIN);
2322 bool bAppliedBottomAutospacing = false;
2323 if (bIsAutoSet || bNoBottomMargin)
2324 {
2325 GetAnyProperty(PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING, pPropertyMap) >>= nAfterAutospacing;
2326 if (bNoBottomMargin && nAfterAutospacing == ConversionHelper::convertTwipToMM100(-1))
2327 {
2328 sal_Int32 nStyleAuto = -1;
2330 if (nStyleAuto > 0)
2331 nAfterAutospacing = 0;
2332 }
2333 }
2334 if ( nAfterAutospacing > -1 && pParaContext )
2335 {
2336 pParaContext->Insert(PROP_PARA_BOTTOM_MARGIN, uno::Any(nAfterAutospacing));
2337 bAppliedBottomAutospacing = bAllowAdjustments;
2338 }
2339
2340 // tell TableManager to reset the bottom margin if it determines that this is the cell's last paragraph.
2341 if ( hasTableManager() && getTableManager().isInCell() )
2342 getTableManager().setCellLastParaAfterAutospacing(bAppliedBottomAutospacing);
2343
2344 if (xTextAppend.is() && pParaContext && hasTableManager() && !getTableManager().isIgnore())
2345 {
2346 try
2347 {
2348 /*the following combinations of previous and current frame settings can occur:
2349 (1) - no old frame and no current frame -> no special action
2350 (2) - no old frame and current DropCap -> save DropCap for later use, don't call finishParagraph
2351 remove character properties of the DropCap?
2352 (3) - no old frame and current Frame -> save Frame for later use
2353 (4) - old DropCap and no current frame -> add DropCap to the properties of the finished paragraph, delete previous setting
2354 (5) - old DropCap and current frame -> add DropCap to the properties of the finished paragraph, save current frame settings
2355 (6) - old Frame and new DropCap -> add old Frame, save DropCap for later use
2356 (7) - old Frame and new same Frame -> continue
2357 (8) - old Frame and new different Frame -> add old Frame, save new Frame for later use
2358 (9) - old Frame and no current frame -> add old Frame, delete previous settings
2359
2360 old _and_ new DropCap must not occur
2361 */
2362
2363 // The paragraph style is vital to knowing all the frame properties.
2364 std::optional<PropertyMap::Property> aParaStyle
2365 = pPropertyMap->getProperty(PROP_PARA_STYLE_NAME);
2366 if (aParaStyle)
2367 {
2368 OUString sName;
2369 aParaStyle->second >>= sName;
2370 pParaContext->props().SetParaStyleName(sName);
2371 }
2372
2373 bool bIsDropCap =
2374 pParaContext->props().IsFrameMode() &&
2375 sal::static_int_cast<Id>(pParaContext->props().GetDropCap()) != NS_ooxml::LN_Value_doc_ST_DropCap_none;
2376
2377 style::DropCapFormat aDrop;
2378 ParagraphPropertiesPtr pToBeSavedProperties;
2379 bool bKeepLastParagraphProperties = false;
2380 if( bIsDropCap )
2381 {
2383 xTextAppend->createTextCursorByRange(xTextAppend->getEnd()), uno::UNO_QUERY_THROW);
2384 //select paragraph
2385 xParaCursor->gotoStartOfParagraph( true );
2386 uno::Reference< beans::XPropertyState > xParaProperties( xParaCursor, uno::UNO_QUERY_THROW );
2387 xParaProperties->setPropertyToDefault(getPropertyName(PROP_CHAR_ESCAPEMENT));
2388 xParaProperties->setPropertyToDefault(getPropertyName(PROP_CHAR_HEIGHT));
2389 //handles (2) and part of (6)
2390 pToBeSavedProperties = new ParagraphProperties(pParaContext->props());
2391 sal_Int32 nCount = xParaCursor->getString().getLength();
2392 pToBeSavedProperties->SetDropCapLength(nCount > 0 && nCount < 255 ? static_cast<sal_Int8>(nCount) : 1);
2393 }
2394 if( rAppendContext.pLastParagraphProperties )
2395 {
2396 if( sal::static_int_cast<Id>(rAppendContext.pLastParagraphProperties->GetDropCap()) != NS_ooxml::LN_Value_doc_ST_DropCap_none)
2397 {
2398 //handles (4) and part of (5)
2399 //create a DropCap property, add it to the property sequence of finishParagraph
2400 sal_Int32 nLines = rAppendContext.pLastParagraphProperties->GetLines();
2401 aDrop.Lines = nLines > 0 && nLines < SAL_MAX_INT8 ? static_cast<sal_Int8>(nLines) : 2;
2402 aDrop.Count = rAppendContext.pLastParagraphProperties->GetDropCapLength();
2403 sal_Int32 nHSpace = rAppendContext.pLastParagraphProperties->GethSpace();
2404 aDrop.Distance = nHSpace > 0 && nHSpace < SAL_MAX_INT16 ? static_cast<sal_Int16>(nHSpace) : 0;
2405 //completes (5)
2406 if( pParaContext->props().IsFrameMode() )
2407 pToBeSavedProperties = new ParagraphProperties(pParaContext->props());
2408 }
2409 else
2410 {
2411 const bool bIsFrameMode(pParaContext->props().IsFrameMode());
2412 std::vector<beans::PropertyValue> aCurrFrameProperties;
2413 std::vector<beans::PropertyValue> aPrevFrameProperties;
2414 if (bIsFrameMode)
2415 {
2416 aCurrFrameProperties = MakeFrameProperties(pParaContext->props());
2417 aPrevFrameProperties
2419 }
2420
2421 if (bIsFrameMode && aPrevFrameProperties == aCurrFrameProperties)
2422 {
2423 //handles (7)
2424 rAppendContext.pLastParagraphProperties->SetEndingRange(
2425 rAppendContext.xInsertPosition.is() ? rAppendContext.xInsertPosition
2426 : xTextAppend->getEnd());
2427 bKeepLastParagraphProperties = true;
2428 }
2429 else
2430 {
2431 // handles (8)(9) and completes (6)
2432
2433 // RTF has an \overlap flag (which we ignore so far)
2434 // but DOCX has nothing like that for framePr
2435 // Always allow overlap in the RTF case - so there can be no regression.
2436
2437 // In MSO UI, there is no setting for AllowOverlap for this kind of frame.
2438 // Although they CAN overlap with other anchored things,
2439 // they do not _easily_ overlap with other framePr's,
2440 // so when one frame follows another (8), don't let the first be overlapped.
2441 bool bPreventOverlap = !IsRTFImport() && bIsFrameMode && !bIsDropCap;
2442
2443 // Preventing overlap is emulation - so deny overlap as little as possible.
2444 sal_Int16 nVertOrient = text::VertOrientation::NONE;
2445 sal_Int16 nVertOrientRelation = text::RelOrientation::FRAME;
2446 sal_Int32 nCurrVertPos = 0;
2447 sal_Int32 nPrevVertPos = 0;
2448 for (size_t i = 0; bPreventOverlap && i < aCurrFrameProperties.size(); ++i)
2449 {
2450 if (aCurrFrameProperties[i].Name == "VertOrientRelation")
2451 {
2452 aCurrFrameProperties[i].Value >>= nVertOrientRelation;
2453 if (nVertOrientRelation != text::RelOrientation::FRAME)
2454 bPreventOverlap = false;
2455 }
2456 else if (aCurrFrameProperties[i].Name == "VertOrient")
2457 {
2458 aCurrFrameProperties[i].Value >>= nVertOrient;
2459 if (nVertOrient != text::VertOrientation::NONE)
2460 bPreventOverlap = false;
2461 }
2462 else if (aCurrFrameProperties[i].Name == "VertOrientPosition")
2463 {
2464 aCurrFrameProperties[i].Value >>= nCurrVertPos;
2465 // arbitrary value. Assume it must be less than 1st line height
2466 if (nCurrVertPos > 20 || nCurrVertPos < -20)
2467 bPreventOverlap = false;
2468 }
2469 }
2470 for (size_t i = 0; bPreventOverlap && i < aPrevFrameProperties.size(); ++i)
2471 {
2472 if (aPrevFrameProperties[i].Name == "VertOrientRelation")
2473 {
2474 aPrevFrameProperties[i].Value >>= nVertOrientRelation;
2475 if (nVertOrientRelation != text::RelOrientation::FRAME)
2476 bPreventOverlap = false;
2477 }
2478 else if (aPrevFrameProperties[i].Name == "VertOrient")
2479 {
2480 aPrevFrameProperties[i].Value >>= nVertOrient;
2481 if (nVertOrient != text::VertOrientation::NONE)
2482 bPreventOverlap = false;
2483 }
2484 else if (aPrevFrameProperties[i].Name == "VertOrientPosition")
2485 {
2486 aPrevFrameProperties[i].Value >>= nPrevVertPos;
2487 if (nPrevVertPos != nCurrVertPos)
2488 bPreventOverlap = false;
2489 }
2490 }
2491
2492 CheckUnregisteredFrameConversion(bPreventOverlap);
2493
2494 // If different frame properties are set on this paragraph, keep them.
2495 if (!bIsDropCap && bIsFrameMode)
2496 {
2497 pToBeSavedProperties = new ParagraphProperties(pParaContext->props());
2498 lcl_AddRange(pToBeSavedProperties, xTextAppend, rAppendContext);
2499 }
2500 }
2501 }
2502 }
2503 else
2504 {
2505 // (1) doesn't need handling
2506
2507 if( !bIsDropCap && pParaContext->props().IsFrameMode() )
2508 {
2509 pToBeSavedProperties = new ParagraphProperties(pParaContext->props());
2510 lcl_AddRange(pToBeSavedProperties, xTextAppend, rAppendContext);
2511 }
2512 }
2513 std::vector<beans::PropertyValue> aProperties;
2514 if (pPropertyMap)
2515 {
2516 aProperties = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(pPropertyMap->GetPropertyValues());
2517
2518 // tdf#64222 filter out the "paragraph marker" formatting and
2519 // set it as a separate paragraph property, not a empty hint at
2520 // end of paragraph
2521 std::vector<beans::NamedValue> charProperties;
2522 for (auto it = aProperties.begin(); it != aProperties.end(); )
2523 {
2524 // this condition isn't ideal but as it happens all
2525 // RES_CHRATR_* have names that start with "Char"
2526 if (it->Name.startsWith("Char"))
2527 {
2528 charProperties.emplace_back(it->Name, it->Value);
2529 // as testN793262 demonstrates, font size in rPr must
2530 // affect the paragraph size => also insert empty hint!
2531// it = aProperties.erase(it);
2532 }
2533 ++it;
2534 }
2535 if (!charProperties.empty())
2536 {
2537 aProperties.push_back(beans::PropertyValue("ListAutoFormat",
2538 0, uno::Any(comphelper::containerToSequence(charProperties)), beans::PropertyState_DIRECT_VALUE));
2539 }
2540 }
2541 if( !bIsDropCap )
2542 {
2543 if( aDrop.Lines > 1 )
2544 {
2545 beans::PropertyValue aValue;
2547 aValue.Value <<= aDrop;
2548 aProperties.push_back(aValue);
2549 }
2551 if (rAppendContext.xInsertPosition.is())
2552 {
2553 xTextRange = xTextAppend->finishParagraphInsert( comphelper::containerToSequence(aProperties), rAppendContext.xInsertPosition );
2554 rAppendContext.xCursor->gotoNextParagraph(false);
2555 if (rAppendContext.pLastParagraphProperties)
2556 rAppendContext.pLastParagraphProperties->SetEndingRange(xTextRange->getEnd());
2557 }
2558 else
2559 {
2562 {
2563 // Workaround to make sure char props of the field are not lost.
2564 // Not relevant for editeng-based comments.
2565 // Not relevant for fields inside a TOC field.
2566 xCursor = xTextAppend->getText()->createTextCursor();
2567 if (xCursor.is())
2568 xCursor->gotoEnd(false);
2569 PropertyMapPtr pEmpty(new PropertyMap());
2570 appendTextPortion("X", pEmpty);
2571 }
2572
2573 // Check if top / bottom margin has to be updated, now that we know the numbering status of both the previous and
2574 // the current text node.
2575 auto itNumberingRules = std::find_if(aProperties.begin(), aProperties.end(), [](const beans::PropertyValue& rValue)
2576 {
2577 return rValue.Name == "NumberingRules";
2578 });
2579
2580 assert( isNumberingViaRule == (itNumberingRules != aProperties.end()) );
2581 isNumberingViaRule = (itNumberingRules != aProperties.end());
2582 if (m_xPreviousParagraph.is() && (isNumberingViaRule || isNumberingViaStyle))
2583 {
2584 // This textnode has numbering. Look up the numbering style name of the current and previous paragraph.
2585 OUString aCurrentNumberingName;
2586 OUString aPreviousNumberingName;
2587 if (isNumberingViaRule)
2588 {
2589 assert(itNumberingRules != aProperties.end() && "by definition itNumberingRules is valid if isNumberingViaRule is true");
2590 uno::Reference<container::XNamed> xCurrentNumberingRules(itNumberingRules->Value, uno::UNO_QUERY);
2591 if (xCurrentNumberingRules.is())
2592 aCurrentNumberingName = xCurrentNumberingRules->getName();
2593 try
2594 {
2595 uno::Reference<container::XNamed> xPreviousNumberingRules(m_xPreviousParagraph->getPropertyValue("NumberingRules"), uno::UNO_QUERY_THROW);
2596 aPreviousNumberingName = xPreviousNumberingRules->getName();
2597 }
2598 catch (const uno::Exception&)
2599 {
2600 TOOLS_WARN_EXCEPTION("writerfilter", "DomainMapper_Impl::finishParagraph NumberingRules");
2601 }
2602 }
2603 else if ( m_xPreviousParagraph->getPropertySetInfo()->hasPropertyByName("NumberingStyleName") &&
2604 // don't update before tables
2606 {
2607 aCurrentNumberingName = GetListStyleName(nListId);
2608 m_xPreviousParagraph->getPropertyValue("NumberingStyleName") >>= aPreviousNumberingName;
2609 }
2610
2611 // tdf#133363: remove extra auto space even for mixed list styles
2612 if (!aPreviousNumberingName.isEmpty()
2613 && (aCurrentNumberingName == aPreviousNumberingName
2614 || !isNumberingViaRule))
2615 {
2616 uno::Sequence<beans::PropertyValue> aPrevPropertiesSeq;
2617 m_xPreviousParagraph->getPropertyValue("ParaInteropGrabBag") >>= aPrevPropertiesSeq;
2618 const auto & rPrevProperties = aPrevPropertiesSeq;
2619 bool bParaAutoBefore = m_bParaAutoBefore || std::any_of(rPrevProperties.begin(), rPrevProperties.end(), [](const beans::PropertyValue& rValue)
2620 {
2621 return rValue.Name == "ParaTopMarginBeforeAutoSpacing";
2622 });
2623 // if style based spacing was set to auto in the previous paragraph, style of the actual paragraph must be the same
2624 if (bParaAutoBefore && !m_bParaAutoBefore && m_xPreviousParagraph->getPropertySetInfo()->hasPropertyByName("ParaStyleName"))
2625 {
2626 auto itParaStyle = std::find_if(aProperties.begin(), aProperties.end(), [](const beans::PropertyValue& rValue)
2627 {
2628 return rValue.Name == "ParaStyleName";
2629 });
2630 bParaAutoBefore = itParaStyle != aProperties.end() &&
2631 m_xPreviousParagraph->getPropertyValue("ParaStyleName") == itParaStyle->Value;
2632 }
2633 // There was a previous textnode and it had the same numbering.
2634 if (bParaAutoBefore)
2635 {
2636 // This before spacing is set to auto, set before space to 0.
2637 auto itParaTopMargin = std::find_if(aProperties.begin(), aProperties.end(), [](const beans::PropertyValue& rValue)
2638 {
2639 return rValue.Name == "ParaTopMargin";
2640 });
2641 if (itParaTopMargin != aProperties.end())
2642 itParaTopMargin->Value <<= static_cast<sal_Int32>(0);
2643 else
2644 aProperties.push_back(comphelper::makePropertyValue("ParaTopMargin", static_cast<sal_Int32>(0)));
2645 }
2646
2647 bool bPrevParaAutoAfter = std::any_of(rPrevProperties.begin(), rPrevProperties.end(), [](const beans::PropertyValue& rValue)
2648 {
2649 return rValue.Name == "ParaBottomMarginAfterAutoSpacing";
2650 });
2651 if (bPrevParaAutoAfter)
2652 {
2653 // Previous after spacing is set to auto, set previous after space to 0.
2654 m_xPreviousParagraph->setPropertyValue("ParaBottomMargin", uno::Any(static_cast<sal_Int32>(0)));
2655 }
2656 }
2657 }
2658
2659 // apply redlines for inline images
2661 {
2662 for (const auto& rAnchored : rAppendContext.m_aAnchoredObjects)
2663 {
2664 // process only inline objects with redlining
2665 if (!rAnchored.m_xRedlineForInline)
2666 continue;
2667
2668 // select the inline image and set its redline
2669 auto xAnchorRange = rAnchored.m_xAnchoredObject->getAnchor();
2671 xAnchorRange->getText()->createTextCursorByRange(xAnchorRange);
2672 xCursorOnImage->goRight(1, true);
2673 CreateRedline( xCursorOnImage, rAnchored.m_xRedlineForInline );
2674 }
2675 }
2676
2677 xTextRange = xTextAppend->finishParagraph( comphelper::containerToSequence(aProperties) );
2678 m_xPreviousParagraph.set(xTextRange, uno::UNO_QUERY);
2679
2680 if (m_xPreviousParagraph.is() && // null for SvxUnoTextBase
2681 (isNumberingViaStyle || isNumberingViaRule))
2682 {
2683 assert(pParaContext);
2684 if (ListDef::Pointer const& pList = m_pListTable->GetList(nListId))
2685 { // styles could refer to non-existing lists...
2686 AbstractListDef::Pointer const& pAbsList =
2687 pList->GetAbstractDefinition();
2688 if (pAbsList &&
2689 // SvxUnoTextRange doesn't have ListId
2690 m_xPreviousParagraph->getPropertySetInfo()->hasPropertyByName("ListId"))
2691 {
2692 OUString paraId;
2693 m_xPreviousParagraph->getPropertyValue("ListId") >>= paraId;
2694 if (!paraId.isEmpty()) // must be on some list?
2695 {
2696 OUString const listId = pAbsList->MapListId(paraId);
2697 if (listId != paraId)
2698 {
2699 m_xPreviousParagraph->setPropertyValue("ListId", uno::Any(listId));
2700 }
2701 }
2702 }
2703
2704 sal_Int16 nCurrentLevel = GetListLevel(pEntry, pPropertyMap);
2705 if (nCurrentLevel == -1)
2706 nCurrentLevel = 0;
2707
2708 const ListLevel::Pointer pListLevel = pList->GetLevel(nCurrentLevel);
2709 if (pListLevel)
2710 {
2711 sal_Int16 nOverrideLevel = pListLevel->GetStartOverride();
2712 if (nOverrideLevel != -1 && m_aListOverrideApplied.find(nListId) == m_aListOverrideApplied.end())
2713 {
2714 // Apply override: we have override instruction for this level
2715 // And this was not done for this list before: we can do this only once on first occurrence
2716 // of list with override
2717 // TODO: Not tested variant with different levels override in different lists.
2718 // Probably m_aListOverrideApplied as a set of overridden listids is not sufficient
2719 // and we need to register level overrides separately.
2720 m_xPreviousParagraph->setPropertyValue("ParaIsNumberingRestart", uno::Any(true));
2721 m_xPreviousParagraph->setPropertyValue("NumberingStartValue", uno::Any(nOverrideLevel));
2723 }
2724 }
2725 }
2726 }
2727
2728 if (!rAppendContext.m_aAnchoredObjects.empty() && !IsInHeaderFooter())
2729 {
2730 // Remember what objects are anchored to this paragraph.
2731 // That list is only used for Word compat purposes, and
2732 // it is only relevant for body text.
2733 AnchoredObjectsInfo aInfo;
2734 aInfo.m_xParagraph = xTextRange;
2735 aInfo.m_aAnchoredObjects = rAppendContext.m_aAnchoredObjects;
2736 m_aAnchoredObjectAnchors.push_back(aInfo);
2737 rAppendContext.m_aAnchoredObjects.clear();
2738 }
2739
2740 if (xCursor.is())
2741 {
2742 xCursor->goLeft(1, true);
2743 xCursor->setString(OUString());
2744 }
2745 }
2746 getTableManager( ).handle(xTextRange);
2747 m_aSmartTagHandler.handle(xTextRange);
2748
2749 if (xTextRange.is())
2750 {
2751 // Get the end of paragraph character inserted
2752 uno::Reference< text::XTextCursor > xCur = xTextRange->getText( )->createTextCursor( );
2753 if (rAppendContext.xInsertPosition.is())
2754 xCur->gotoRange( rAppendContext.xInsertPosition, false );
2755 else
2756 xCur->gotoEnd( false );
2757
2758 // tdf#77417 trim right white spaces in table cells in 2010 compatibility mode
2759 sal_Int32 nMode = GetSettingsTable()->GetWordCompatibilityMode();
2760 if ( m_nTableDepth > 0 && nMode > 0 && nMode <= 14 )
2761 {
2762 // skip new line
2763 xCur->goLeft(1, false);
2764 while ( xCur->goLeft(1, true) )
2765 {
2766 OUString sChar = xCur->getString();
2767 if ( sChar == " " || sChar == "\t" || sChar == OUStringChar(u'\x00A0') )
2768 xCur->setString("");
2769 else
2770 break;
2771 }
2772
2773 if (rAppendContext.xInsertPosition.is())
2774 xCur->gotoRange(rAppendContext.xInsertPosition, false);
2775 else
2776 xCur->gotoEnd(false);
2777 }
2778
2779 xCur->goLeft( 1 , true );
2780 // Extend the redline ranges for empty paragraphs
2783 CheckParaMarkerRedline( xCur );
2784 }
2785
2786 css::uno::Reference<css::beans::XPropertySet> xParaProps(xTextRange, uno::UNO_QUERY);
2787
2788 // table style precedence and not hidden shapes anchored to hidden empty table paragraphs
2789 if (xParaProps && !m_bIsInComments
2790 && (m_nTableDepth > 0 || !m_aAnchoredObjectAnchors.empty()))
2791 {
2792 // table style has got bigger precedence than docDefault style
2793 // collect these pending paragraph properties to process in endTable()
2794 uno::Reference<text::XTextCursor> xCur = xTextRange->getText( )->createTextCursor( );
2795 xCur->gotoEnd(false);
2796 xCur->goLeft(1, false);
2797 uno::Reference<text::XTextCursor> xCur2 = xTextRange->getText()->createTextCursorByRange(xCur);
2798 uno::Reference<text::XParagraphCursor> xParaCursor(xCur2, uno::UNO_QUERY_THROW);
2799 xParaCursor->gotoStartOfParagraph(false);
2800 if (m_nTableDepth > 0)
2801 {
2802 TableParagraph aPending{xParaCursor, xCur, pParaContext, xParaProps};
2803 getTableManager().getCurrentParagraphs()->push_back(aPending);
2804 }
2805
2806 // hidden empty paragraph with a not hidden shape, set as not hidden
2807 std::optional<PropertyMap::Property> pHidden;
2808 if ( !m_aAnchoredObjectAnchors.empty() && (pHidden = pParaContext->getProperty(PROP_CHAR_HIDDEN)) )
2809 {
2810 bool bIsHidden = {}; // -Werror=maybe-uninitialized
2811 pHidden->second >>= bIsHidden;
2812 if (bIsHidden)
2813 {
2814 bIsHidden = false;
2815 pHidden = GetTopContext()->getProperty(PROP_CHAR_HIDDEN);
2816 if (pHidden)
2817 pHidden->second >>= bIsHidden;
2818 if (!bIsHidden)
2819 {
2820 uno::Reference<text::XTextCursor> xCur3 = xTextRange->getText()->createTextCursorByRange(xParaCursor);
2821 xCur3->goRight(1, true);
2822 if (xCur3->getString() == SAL_NEWLINE_STRING)
2823 {
2824 uno::Reference< beans::XPropertySet > xProp( xCur3, uno::UNO_QUERY );
2825 xProp->setPropertyValue(getPropertyName(PROP_CHAR_HIDDEN), uno::Any(false));
2826 }
2827 }
2828 }
2829 }
2830 }
2831
2832 // tdf#118521 set paragraph top or bottom margin based on the paragraph style
2833 // if we already set the other margin with direct formatting
2834 if (xParaProps)
2835 {
2836 const bool bTopSet = pParaContext->isSet(PROP_PARA_TOP_MARGIN);
2837 const bool bBottomSet = pParaContext->isSet(PROP_PARA_BOTTOM_MARGIN);
2838 const bool bContextSet = pParaContext->isSet(PROP_PARA_CONTEXT_MARGIN);
2839 if ( bTopSet != bBottomSet || bBottomSet != bContextSet )
2840 {
2841
2842 if ( !bTopSet )
2843 {
2845 if ( aMargin != uno::Any() )
2846 xParaProps->setPropertyValue("ParaTopMargin", aMargin);
2847 }
2848 if ( !bBottomSet )
2849 {
2851 if ( aMargin != uno::Any() )
2852 xParaProps->setPropertyValue("ParaBottomMargin", aMargin);
2853 }
2854 if ( !bContextSet )
2855 {
2857 if ( aMargin != uno::Any() )
2858 xParaProps->setPropertyValue("ParaContextMargin", aMargin);
2859 }
2860 }
2861 }
2862
2863 // Left, Right, and Hanging settings are also grouped. Ensure that all or none are set.
2864 if (xParaProps)
2865 {
2866 const bool bLeftSet = pParaContext->isSet(PROP_PARA_LEFT_MARGIN);
2867 const bool bRightSet = pParaContext->isSet(PROP_PARA_RIGHT_MARGIN);
2868 const bool bFirstSet = pParaContext->isSet(PROP_PARA_FIRST_LINE_INDENT);
2869 if (bLeftSet != bRightSet || bRightSet != bFirstSet)
2870 {
2871 if ( !bLeftSet )
2872 {
2874 if ( aMargin != uno::Any() )
2875 xParaProps->setPropertyValue("ParaLeftMargin", aMargin);
2876 else if (isNumberingViaStyle)
2877 {
2878 const sal_Int32 nParaLeftMargin = getNumberingProperty(nListId, GetListLevel(pEntry, pPropertyMap), "IndentAt");
2879 if (nParaLeftMargin != 0)
2880 xParaProps->setPropertyValue("ParaLeftMargin", uno::Any(nParaLeftMargin));
2881 }
2882 }
2883 if ( !bRightSet )
2884 {
2886 if ( aMargin != uno::Any() )
2887 xParaProps->setPropertyValue("ParaRightMargin", aMargin);
2888 }
2889 if ( !bFirstSet )
2890 {
2892 if ( aMargin != uno::Any() )
2893 xParaProps->setPropertyValue("ParaFirstLineIndent", aMargin);
2894 else if (isNumberingViaStyle)
2895 {
2896 const sal_Int32 nFirstLineIndent = getNumberingProperty(nListId, GetListLevel(pEntry, pPropertyMap), "FirstLineIndent");
2897 if (nFirstLineIndent != 0)
2898 xParaProps->setPropertyValue("ParaFirstLineIndent", uno::Any(nFirstLineIndent));
2899 }
2900 }
2901 }
2902 }
2903 }
2904 if( !bKeepLastParagraphProperties )
2905 rAppendContext.pLastParagraphProperties = pToBeSavedProperties;
2906 }
2907 catch(const lang::IllegalArgumentException&)
2908 {
2909 TOOLS_WARN_EXCEPTION( "writerfilter", "DomainMapper_Impl::finishParagraph" );
2910 }
2911 catch(const uno::Exception&)
2912 {
2913 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "finishParagraph()" );
2914 }
2915
2916 }
2917
2918 bool bIgnoreFrameState = IsInHeaderFooter();
2919 if( (!bIgnoreFrameState && pParaContext && pParaContext->props().IsFrameMode()) || (bIgnoreFrameState && GetIsPreviousParagraphFramed()) )
2921 else
2923
2924 m_bRemoveThisParagraph = false;
2925 if( !IsInHeaderFooter() && !IsInShape()
2926 && (!pParaContext || !pParaContext->props().IsFrameMode()) )
2927 { // If the paragraph is in a frame, shape or header/footer, it's not a paragraph of the section itself.
2929 // don't count an empty deleted paragraph as first paragraph in section to avoid of
2930 // the deletion of the next empty paragraph later, resulting loss of the associated page break
2932 {
2935 }
2936 }
2938 m_bParaChanged = false;
2939
2940 if (m_bIsInComments && pParaContext)
2941 {
2942 if (const OUString sParaId = pParaContext->props().GetParaId(); !sParaId.isEmpty())
2943 {
2944 if (const auto& item = m_aCommentProps.find(sParaId); item != m_aCommentProps.end())
2945 {
2946 m_bAnnotationResolved = item->second.bDone;
2947 m_sAnnotationParent = item->second.sParaIdParent;
2948 }
2950 }
2951 }
2952
2954 m_bIsFirstParaInShape = false;
2955
2956 if (pParaContext)
2957 {
2958 // Reset the frame properties for the next paragraph
2959 pParaContext->props().ResetFrameProperties();
2960 }
2961
2963 m_bParaHadField = false;
2964
2965 // don't overwrite m_bFirstParagraphInCell in table separator nodes
2966 // and in text boxes anchored to the first paragraph of table cells
2969
2970 m_bParaAutoBefore = false;
2972
2973#ifdef DBG_UTIL
2975#endif
2976
2977}
2978
2979void DomainMapper_Impl::appendTextPortion( const OUString& rString, const PropertyMapPtr& pPropertyMap )
2980{
2982 return;
2983
2984 if (m_aTextAppendStack.empty())
2985 return;
2986 // Before placing call to processDeferredCharacterProperties(), TopContextType should be CONTEXT_CHARACTER
2987 // processDeferredCharacterProperties() invokes only if character inserted
2988 if( pPropertyMap == m_pTopContext && !m_deferredCharacterProperties.empty() && (GetTopContextType() == CONTEXT_CHARACTER) )
2990 uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
2991 if (!xTextAppend.is() || !hasTableManager() || getTableManager().isIgnore())
2992 return;
2993
2994 try
2995 {
2996 // If we are in comments, then disable CharGrabBag, comment text doesn't support that.
2997 uno::Sequence< beans::PropertyValue > aValues = pPropertyMap->GetPropertyValues(/*bCharGrabBag=*/!m_bIsInComments);
2998
3000 for( auto& rValue : asNonConstRange(aValues) )
3001 {
3002 if (rValue.Name == "CharHidden")
3003 rValue.Value <<= false;
3004 }
3005
3006 // remove workaround for change tracked images, if they are part of a redline,
3007 // i.e. if the next run is a tracked change with the same type, author and date,
3008 // as in the change tracking of the image.
3010 {
3011 auto pCurrentRedline = m_aRedlines.top().size() > 0
3012 ? m_aRedlines.top().back()
3014 GetTopContextOfType(CONTEXT_CHARACTER)->Redlines().size() > 0
3015 ? GetTopContextOfType(CONTEXT_CHARACTER)->Redlines().back()
3016 : nullptr;
3017 if ( m_previousRedline && pCurrentRedline &&
3018 // same redline
3019 (m_previousRedline->m_nToken & 0xffff) == (pCurrentRedline->m_nToken & 0xffff) &&
3020 m_previousRedline->m_sAuthor == pCurrentRedline->m_sAuthor &&
3021 m_previousRedline->m_sDate == pCurrentRedline->m_sDate )
3022 {
3023 uno::Reference< text::XTextCursor > xCursor = xTextAppend->getEnd()->getText( )->createTextCursor( );
3024 assert(xCursor.is());
3025 xCursor->gotoEnd(false);
3026 xCursor->goLeft(2, true);
3027 if ( xCursor->getString() == u"​​" )
3028 {
3029 xCursor->goRight(1, true);
3030 xCursor->setString("");
3031 xCursor->gotoEnd(false);
3032 xCursor->goLeft(1, true);
3033 xCursor->setString("");
3034 }
3035 }
3036
3038 }
3039
3041 if (m_aTextAppendStack.top().xInsertPosition.is())
3042 {
3043 xTextRange = xTextAppend->insertTextPortion(rString, aValues, m_aTextAppendStack.top().xInsertPosition);
3044 m_aTextAppendStack.top().xCursor->gotoRange(xTextRange->getEnd(), true);
3045 }
3046 else
3047 {
3049 {
3051 {
3052 xTextRange = xTextAppend->appendTextPortion(rString, aValues);
3053 }
3054 else
3055 {
3056 m_bStartedTOC = true;
3057 uno::Reference< text::XTextCursor > xTOCTextCursor = xTextAppend->getEnd()->getText( )->createTextCursor( );
3058 assert(xTOCTextCursor.is());
3059 xTOCTextCursor->gotoEnd(false);
3060 if (m_nStartGenericField != 0)
3061 {
3062 xTOCTextCursor->goLeft(1, false);
3063 }
3064 xTextRange = xTextAppend->insertTextPortion(rString, aValues, xTOCTextCursor);
3065 SAL_WARN_IF(!xTextRange.is(), "writerfilter.dmapper", "insertTextPortion failed");
3066 if (!xTextRange.is())
3067 throw uno::Exception("insertTextPortion failed", nullptr);
3068 m_bTextInserted = true;
3069 xTOCTextCursor->gotoRange(xTextRange->getEnd(), true);
3070 if (m_nStartGenericField == 0)
3071 {
3072 m_aTextAppendStack.push(TextAppendContext(xTextAppend, xTOCTextCursor));
3073 }
3074 }
3075 }
3076 else
3077 {
3078#if !defined(MACOSX) // TODO: check layout differences and support all platforms, if needed
3079 sal_Int32 nPos = 0;
3080 OUString sFontName;
3081 OUString sDoubleSpace(" ");
3083 // tdf#123703 workaround for longer space sequences of the old or compatible RTF documents
3084 if (GetSettingsTable()->GetLongerSpaceSequence() && !IsOpenFieldCommand() && (nPos = rString.indexOf(sDoubleSpace)) != -1 &&
3085 // monospaced fonts have no longer space sequences, regardless of \fprq2 (not monospaced) font setting
3086 // fix for the base monospaced font Courier
3087 (!pContext || !pContext->isSet(PROP_CHAR_FONT_NAME) ||
3088 ((pContext->getProperty(PROP_CHAR_FONT_NAME)->second >>= sFontName) && sFontName.indexOf("Courier") == -1)))
3089 {
3090 // an RTF space character is longer by an extra six-em-space in an old-style RTF space sequence,
3091 // insert them to keep RTF document layout formatted by consecutive spaces
3092 const sal_Unicode aExtraSpace[5] = { 0x2006, 0x20, 0x2006, 0x20, 0 };
3093 const sal_Unicode aExtraSpace2[4] = { 0x20, 0x2006, 0x20, 0 };
3094 xTextRange = xTextAppend->appendTextPortion(rString.replaceAll(sDoubleSpace, aExtraSpace, nPos)
3095 .replaceAll(sDoubleSpace, aExtraSpace2, nPos), aValues);
3096 }
3097 else
3098#endif
3099 xTextRange = xTextAppend->appendTextPortion(rString, aValues);
3100 }
3101 }
3102
3103 // reset moveFrom/moveTo data of non-terminating runs of the paragraph
3105 {
3107 }
3108 CheckRedline( xTextRange );
3109 m_bParaChanged = true;
3110
3111 //getTableManager( ).handle(xTextRange);
3112 }
3113 catch(const lang::IllegalArgumentException&)
3114 {
3115 TOOLS_WARN_EXCEPTION( "writerfilter", "DomainMapper_Impl::appendTextPortion" );
3116 }
3117 catch(const uno::Exception&)
3118 {
3119 TOOLS_WARN_EXCEPTION( "writerfilter", "DomainMapper_Impl::appendTextPortion" );
3120 }
3121}
3122
3125 const uno::Sequence< beans::PropertyValue >& xPropertyValues
3126 )
3127{
3128 SAL_WARN_IF(m_aTextAppendStack.empty(), "writerfilter.dmapper", "no text append stack");
3129 if (m_aTextAppendStack.empty())
3130 return;
3131 uno::Reference< text::XTextAppendAndConvert > xTextAppendAndConvert( m_aTextAppendStack.top().xTextAppend, uno::UNO_QUERY );
3132 OSL_ENSURE( xTextAppendAndConvert.is(), "trying to append a text content without XTextAppendAndConvert" );
3133 if (!xTextAppendAndConvert.is() || !hasTableManager() || getTableManager().isIgnore())
3134 return;
3135
3136 try
3137 {
3138 if (m_aTextAppendStack.top().xInsertPosition.is())
3139 xTextAppendAndConvert->insertTextContentWithProperties( xContent, xPropertyValues, m_aTextAppendStack.top().xInsertPosition );
3140 else
3141 xTextAppendAndConvert->appendTextContent( xContent, xPropertyValues );
3142 }
3143 catch(const lang::IllegalArgumentException&)
3144 {
3145 }
3146 catch(const uno::Exception&)
3147 {
3148 }
3149}
3150
3151void DomainMapper_Impl::appendOLE( const OUString& rStreamName, const std::shared_ptr<OLEHandler>& pOLEHandler )
3152{
3153 try
3154 {
3155 uno::Reference< text::XTextContent > xOLE( m_xTextFactory->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW );
3156 uno::Reference< beans::XPropertySet > xOLEProperties(xOLE, uno::UNO_QUERY_THROW);
3157
3158 OUString aCLSID = pOLEHandler->getCLSID();
3159 if (aCLSID.isEmpty())
3160 xOLEProperties->setPropertyValue(getPropertyName( PROP_STREAM_NAME ),
3161 uno::Any( rStreamName ));
3162 else
3163 xOLEProperties->setPropertyValue("CLSID", uno::Any(aCLSID));
3164
3165 OUString aDrawAspect = pOLEHandler->GetDrawAspect();
3166 if(!aDrawAspect.isEmpty())
3167 xOLEProperties->setPropertyValue("DrawAspect", uno::Any(aDrawAspect));
3168
3169 awt::Size aSize = pOLEHandler->getSize();
3170 if( !aSize.Width )
3171 aSize.Width = 1000;
3172 if( !aSize.Height )
3173 aSize.Height = 1000;
3174 xOLEProperties->setPropertyValue(getPropertyName( PROP_WIDTH ),
3175 uno::Any(aSize.Width));
3176 xOLEProperties->setPropertyValue(getPropertyName( PROP_HEIGHT ),
3177 uno::Any(aSize.Height));
3178
3179 OUString aVisAreaWidth = pOLEHandler->GetVisAreaWidth();
3180 if(!aVisAreaWidth.isEmpty())
3181 xOLEProperties->setPropertyValue("VisibleAreaWidth", uno::Any(aVisAreaWidth));
3182
3183 OUString aVisAreaHeight = pOLEHandler->GetVisAreaHeight();
3184 if(!aVisAreaHeight.isEmpty())
3185 xOLEProperties->setPropertyValue("VisibleAreaHeight", uno::Any(aVisAreaHeight));
3186
3187 uno::Reference< graphic::XGraphic > xGraphic = pOLEHandler->getReplacement();
3188 xOLEProperties->setPropertyValue(getPropertyName( PROP_GRAPHIC ),
3189 uno::Any(xGraphic));
3190 uno::Reference<beans::XPropertySet> xReplacementProperties(pOLEHandler->getShape(), uno::UNO_QUERY);
3191 if (xReplacementProperties.is())
3192 {
3193 table::BorderLine2 aBorderProps;
3194 xReplacementProperties->getPropertyValue("LineColor") >>= aBorderProps.Color;
3195 xReplacementProperties->getPropertyValue("LineWidth") >>= aBorderProps.LineWidth;
3196 xReplacementProperties->getPropertyValue("LineStyle") >>= aBorderProps.LineStyle;
3197
3198 if (aBorderProps.LineStyle) // Set line props only if LineStyle is set
3199 {
3200 xOLEProperties->setPropertyValue("RightBorder", uno::Any(aBorderProps));
3201 xOLEProperties->setPropertyValue("TopBorder", uno::Any(aBorderProps));
3202 xOLEProperties->setPropertyValue("LeftBorder", uno::Any(aBorderProps));
3203 xOLEProperties->setPropertyValue("BottomBorder", uno::Any(aBorderProps));
3204 }
3205 OUString pProperties[] = {
3206 "AnchorType",
3207 "Surround",
3208 "SurroundContour",
3209 "HoriOrient",
3210 "HoriOrientPosition",
3211 "VertOrient",
3212 "VertOrientPosition",
3213 "VertOrientRelation",
3214 "HoriOrientRelation",
3215 "LeftMargin",
3216 "RightMargin",
3217 "TopMargin",
3218 "BottomMargin"
3219 };
3220 for (const OUString& s : pProperties)
3221 {
3222 const uno::Any aVal = xReplacementProperties->getPropertyValue(s);
3223 xOLEProperties->setPropertyValue(s, aVal);
3224 }
3225
3226 if (xReplacementProperties->getPropertyValue("FillStyle").get<css::drawing::FillStyle>()
3227 != css::drawing::FillStyle::FillStyle_NONE) // Apply fill props if style is set
3228 {
3229 xOLEProperties->setPropertyValue(
3230 "FillStyle", xReplacementProperties->getPropertyValue("FillStyle"));
3231 xOLEProperties->setPropertyValue(
3232 "FillColor", xReplacementProperties->getPropertyValue("FillColor"));
3233 xOLEProperties->setPropertyValue(
3234 "FillColor2", xReplacementProperties->getPropertyValue("FillColor2"));
3235 }
3236 }
3237 else
3238 // mimic the treatment of graphics here... it seems anchoring as character
3239 // gives a better ( visually ) result
3240 xOLEProperties->setPropertyValue(getPropertyName( PROP_ANCHOR_TYPE ), uno::Any( text::TextContentAnchorType_AS_CHARACTER ) );
3241 // remove ( if valid ) associated shape ( used for graphic replacement )
3242 SAL_WARN_IF(m_aAnchoredStack.empty(), "writerfilter.dmapper", "no anchor stack");
3243 if (!m_aAnchoredStack.empty())
3244 m_aAnchoredStack.top( ).bToRemove = true;
3246 SAL_WARN_IF(m_aTextAppendStack.empty(), "writerfilter.dmapper", "no text stack");
3247 if (!m_aTextAppendStack.empty())
3248 m_aTextAppendStack.pop();
3249
3251
3252 if (!aCLSID.isEmpty())
3253 pOLEHandler->importStream(m_xComponentContext, GetTextDocument(), xOLE);
3254
3255 }
3256 catch( const uno::Exception& )
3257 {
3258 TOOLS_WARN_EXCEPTION( "writerfilter", "in creation of OLE object" );
3259 }
3260
3261}
3262
3264{
3266 val.getAny() >>= formula;
3267 if( !formula.is() )
3268 return;
3269
3270 try
3271 {
3272 uno::Reference< text::XTextContent > xStarMath( m_xTextFactory->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW );
3273 uno::Reference< beans::XPropertySet > xStarMathProperties(xStarMath, uno::UNO_QUERY_THROW);
3274
3275 xStarMathProperties->setPropertyValue(getPropertyName( PROP_EMBEDDED_OBJECT ),
3276 val.getAny());
3277 // tdf#66405: set zero margins for embedded object
3278 xStarMathProperties->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN ),
3279 uno::Any(sal_Int32(0)));
3280 xStarMathProperties->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN ),
3281 uno::Any(sal_Int32(0)));
3282 xStarMathProperties->setPropertyValue(getPropertyName( PROP_TOP_MARGIN ),
3283 uno::Any(sal_Int32(0)));
3284 xStarMathProperties->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN ),
3285 uno::Any(sal_Int32(0)));
3286
3287 uno::Reference< uno::XInterface > xInterface( formula->getComponent(), uno::UNO_QUERY );
3288 // set zero margins for object's component
3289 uno::Reference< beans::XPropertySet > xComponentProperties( xInterface, uno::UNO_QUERY_THROW );
3290 xComponentProperties->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN ),
3291 uno::Any(sal_Int32(0)));
3292 xComponentProperties->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN ),
3293 uno::Any(sal_Int32(0)));
3294 xComponentProperties->setPropertyValue(getPropertyName( PROP_TOP_MARGIN ),
3295 uno::Any(sal_Int32(0)));
3296 xComponentProperties->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN ),
3297 uno::Any(sal_Int32(0)));
3298 Size size( 1000, 1000 );
3299 if( oox::FormulaImExportBase* formulaimport = dynamic_cast< oox::FormulaImExportBase* >( xInterface.get()))
3300 size = formulaimport->getFormulaSize();
3301 xStarMathProperties->setPropertyValue(getPropertyName( PROP_WIDTH ),
3302 uno::Any( sal_Int32(size.Width())));
3303 xStarMathProperties->setPropertyValue(getPropertyName( PROP_HEIGHT ),
3304 uno::Any( sal_Int32(size.Height())));
3305 xStarMathProperties->setPropertyValue(getPropertyName(PROP_ANCHOR_TYPE),
3306 uno::Any(text::TextContentAnchorType_AS_CHARACTER));
3307 // mimic the treatment of graphics here... it seems anchoring as character
3308 // gives a better ( visually ) result
3310 }
3311 catch( const uno::Exception& )
3312 {
3313 TOOLS_WARN_EXCEPTION( "writerfilter", "in creation of StarMath object" );
3314 }
3315}
3316
3318{
3320 pLastPara->Insert(PROP_PARA_ADJUST, uno::Any(nAlign), true);
3321}
3322
3324 uno::Reference< text::XTextRange > const & xBefore )
3325{
3327 if (m_aTextAppendStack.empty())
3328 return xRet;
3329 uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
3330 if(xTextAppend.is())
3331 {
3332 try
3333 {
3335 xTextAppend->createTextCursorByRange( xBefore ), uno::UNO_QUERY_THROW);
3336 //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
3337 xCursor->gotoStartOfParagraph( false );
3338 if (m_aTextAppendStack.top().xInsertPosition.is())
3339 xCursor->gotoRange( m_aTextAppendStack.top().xInsertPosition, true );
3340 else
3341 xCursor->gotoEnd( true );
3342 //the paragraph after this new section is already inserted
3343 xCursor->goLeft(1, true);
3344 css::uno::Reference<css::text::XTextRange> xTextRange(xCursor, css::uno::UNO_QUERY_THROW);
3345
3346 if (css::uno::Reference<css::text::XDocumentIndexesSupplier> xIndexSupplier{
3347 GetTextDocument(), css::uno::UNO_QUERY })
3348 {
3349 css::uno::Reference<css::text::XTextRangeCompare> xCompare(
3350 xTextAppend, css::uno::UNO_QUERY);
3351 const auto xIndexAccess = xIndexSupplier->getDocumentIndexes();
3352 for (sal_Int32 i = xIndexAccess->getCount(); i > 0; --i)
3353 {
3354 if (css::uno::Reference<css::text::XDocumentIndex> xIndex{
3355 xIndexAccess->getByIndex(i - 1), css::uno::UNO_QUERY })
3356 {
3357 const auto xIndexTextRange = xIndex->getAnchor();
3358 if (xCompare->compareRegionStarts(xTextRange, xIndexTextRange) == 0
3359 && xCompare->compareRegionEnds(xTextRange, xIndexTextRange) == 0)
3360 {
3361 // The boundaries coincide with an index: trying to attach a section
3362 // to the range will insert the section inside the index. goRight will
3363 // extend the range outside of the index, so that created section will
3364 // be around it. Alternatively we could return index section itself
3365 // instead : xRet.set(xIndex, uno::UNO_QUERY) - to set its properties,
3366 // like columns/fill.
3367 xCursor->goRight(1, true);
3368 break;
3369 }
3370 }
3371 }
3372 }
3373
3374 uno::Reference< text::XTextContent > xSection( m_xTextFactory->createInstance("com.sun.star.text.TextSection"), uno::UNO_QUERY_THROW );
3375 xSection->attach( xTextRange );
3376 xRet.set(xSection, uno::UNO_QUERY );
3377 }
3378 catch(const uno::Exception&)
3379 {
3380 }
3381
3382 }
3383
3384 return xRet;
3385}
3386
3388{
3390}
3391
3392void DomainMapper_Impl::fillEmptyFrameProperties(std::vector<beans::PropertyValue>& rFrameProperties, bool bSetAnchorToChar)
3393{
3394 if (bSetAnchorToChar)
3395 rFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_ANCHOR_TYPE), text::TextContentAnchorType_AS_CHARACTER));
3396
3397 uno::Any aEmptyBorder{table::BorderLine2()};
3398 static const std::vector<PropertyIds> aBorderIds
3400 for (size_t i = 0; i < aBorderIds.size(); ++i)
3401 rFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(aBorderIds[i]), aEmptyBorder));
3402
3403 static const std::vector<PropertyIds> aMarginIds
3408 for (size_t i = 0; i < aMarginIds.size(); ++i)
3409 rFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(aMarginIds[i]), static_cast<sal_Int32>(0)));
3410}
3411
3413{
3414 if (IsInHeaderFooter())
3416 else
3417 return m_bStartTOC;
3418}
3419
3420void DomainMapper_Impl::ConvertHeaderFooterToTextFrame(bool bDynamicHeightTop, bool bDynamicHeightBottom)
3421{
3422 while (!m_aHeaderFooterTextAppendStack.empty())
3423 {
3424 auto aFooterHeader = m_aHeaderFooterTextAppendStack.top();
3425 if ((aFooterHeader.second && !bDynamicHeightTop) || (!aFooterHeader.second && !bDynamicHeightBottom))
3426 {
3427 uno::Reference< text::XTextAppend > xTextAppend = aFooterHeader.first.xTextAppend;
3428 uno::Reference< text::XTextCursor > xCursor = xTextAppend->createTextCursor();
3429 uno::Reference< text::XTextRange > xRangeStart, xRangeEnd;
3430
3431 xRangeStart = xCursor->getStart();
3432 xCursor->gotoEnd(false);
3433 xRangeEnd = xCursor->getStart();
3434
3435 std::vector<beans::PropertyValue> aFrameProperties
3436 {
3437 comphelper::makePropertyValue("TextWrap", css::text::WrapTextMode_THROUGH),
3438 comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT), text::HoriOrientation::LEFT),
3442 // tdf#143384 If the header/footer started with a table, convertToTextFrame could not
3443 // convert the table, because it used createTextCursor() -which ignore tables-
3444 // to set the conversion range.
3445 // This dummy property is set to make convertToTextFrame to use another CreateTextCursor
3446 // method that can be parameterized to not ignore tables.
3448 };
3449
3450 fillEmptyFrameProperties(aFrameProperties, false);
3451
3452 // If it is a footer, then orient the frame to the bottom
3453 if (!aFooterHeader.second)
3454 aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT), text::VertOrientation::BOTTOM));
3455
3457 xRangeStart->getText(), uno::UNO_QUERY);
3458 xBodyText->convertToTextFrame(xTextAppend, xRangeEnd,
3459 comphelper::containerToSequence(aFrameProperties));
3460 }
3462 }
3463}
3464
3466{
3469 m_bTextInserted = false;
3470 m_nTableDepth = 0;
3471
3472 const PropertyIds ePropIsOn = bHeader? PROP_HEADER_IS_ON: PROP_FOOTER_IS_ON;
3473 const PropertyIds ePropShared = bHeader? PROP_HEADER_IS_SHARED: PROP_FOOTER_IS_SHARED;
3474 const PropertyIds ePropTextLeft = bHeader? PROP_HEADER_TEXT_LEFT: PROP_FOOTER_TEXT_LEFT;
3475 const PropertyIds ePropText = bHeader? PROP_HEADER_TEXT: PROP_FOOTER_TEXT;
3476
3480
3481 //get the section context
3483 //ask for the header/footer name of the given type
3484 SectionPropertyMap* pSectionContext = dynamic_cast< SectionPropertyMap* >( pContext.get() );
3485 if(!pSectionContext)
3486 return;
3487
3488 // clear the "Link To Previous" flag so that the header/footer
3489 // content is not copied from the previous section
3490 pSectionContext->ClearHeaderFooterLinkToPrevious(bHeader, eType);
3491
3492 if (!m_bIsNewDoc)
3493 {
3494 return; // TODO sw cannot Undo insert header/footer without crashing
3495 }
3496
3498 pSectionContext->GetPageStyle(
3499 *this,
3501 if (!xPageStyle.is())
3502 return;
3503 try
3504 {
3505 bool bLeft = eType == SectionPropertyMap::PAGE_LEFT;
3506 bool bFirst = eType == SectionPropertyMap::PAGE_FIRST;
3507 if (!bLeft || GetSettingsTable()->GetEvenAndOddHeaders())
3508 {
3509 //switch on header/footer use
3510 xPageStyle->setPropertyValue(
3511 getPropertyName(ePropIsOn),
3512 uno::Any(true));
3513
3514 // If the 'Different Even & Odd Pages' flag is turned on - do not ignore it
3515 // Even if the 'Even' header/footer is blank - the flag should be imported (so it would look in LO like in Word)
3516 if (!bFirst && GetSettingsTable()->GetEvenAndOddHeaders())
3517 xPageStyle->setPropertyValue(getPropertyName(ePropShared), uno::Any(false));
3518
3519 //set the interface
3521 xPageStyle->getPropertyValue(getPropertyName(bLeft? ePropTextLeft: ePropText)) >>= xText;
3522
3526 : xText->createTextCursorByRange(xText->getStart())));
3527 m_aHeaderFooterTextAppendStack.push(std::make_pair(TextAppendContext(uno::Reference< text::XTextAppend >(xText, uno::UNO_QUERY_THROW),
3530 : xText->createTextCursorByRange(xText->getStart())),
3531 bHeader));
3532 }
3533 // If we have *hidden* header footer
3534 else
3535 {
3536 bool bIsShared = false;
3537 // Turn on the headers
3538 xPageStyle->setPropertyValue(getPropertyName(ePropIsOn), uno::Any(true));
3539 // Store the state of the previous state of shared prop
3540 xPageStyle->getPropertyValue(getPropertyName(ePropShared)) >>= bIsShared;
3541 // Turn on the shared prop in order to save the headers/footers in time
3542 xPageStyle->setPropertyValue(getPropertyName(ePropShared), uno::Any(false));
3543 // Add the content of the headers footers to the doc
3545 xPageStyle->getPropertyValue(getPropertyName(bLeft ? ePropTextLeft : ePropText))
3546 >>= xText;
3547
3548 m_aTextAppendStack.push(
3549 TextAppendContext(uno::Reference<text::XTextAppend>(xText, uno::UNO_QUERY_THROW),
3551 : xText->createTextCursorByRange(xText->getStart())));
3552 // Restore the original state of the shared prop after we stored the necessary values.
3553 xPageStyle->setPropertyValue(getPropertyName(ePropShared), uno::Any(bIsShared));
3554 }
3555 m_bDiscardHeaderFooter = false; // set only on success!
3556 }
3557 catch( const uno::Exception& )
3558 {
3559 DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
3560 }
3561}
3562
3564{
3565 PushPageHeaderFooter(/* bHeader = */ true, eType);
3566}
3567
3569{
3570 PushPageHeaderFooter(/* bHeader = */ false, eType);
3571}
3572
3574{
3575 //header and footer always have an empty paragraph at the end
3576 //this has to be removed
3578
3579 if (!m_aTextAppendStack.empty())
3580 {
3582 {
3583 m_aTextAppendStack.pop();
3584 }
3585 m_bDiscardHeaderFooter = false;
3586 }
3588
3589 if (!m_aHeaderFooterStack.empty())
3590 {
3591 m_bTextInserted = m_aHeaderFooterStack.top().getTextInserted();
3592 m_nTableDepth = m_aHeaderFooterStack.top().getTableDepth();
3594 }
3595
3597}
3598
3600{
3601 SAL_WARN_IF(m_bInFootOrEndnote, "writerfilter.dmapper", "PushFootOrEndnote() is called from another foot or endnote");
3602 m_bInFootOrEndnote = true;
3603 m_bInFootnote = bIsFootnote;
3606 try
3607 {
3608 // Redlines outside the footnote should not affect footnote content
3609 m_aRedlines.push(std::vector< RedlineParamsPtr >());
3610
3611 // IMHO character styles from footnote labels should be ignored in the edit view of Writer.
3612 // This adds a hack on top of the following hack to save the style name in the context.
3613 PropertyMapPtr pTopContext = GetTopContext();
3614 OUString sFootnoteCharStyleName;
3615 std::optional< PropertyMap::Property > aProp = pTopContext->getProperty(PROP_CHAR_STYLE_NAME);
3616 if (aProp)
3617 aProp->second >>= sFootnoteCharStyleName;
3618
3619 // Remove style reference, if any. This reference did appear here as a side effect of tdf#43017
3620 // Seems it is not required by LO, but causes side effects during editing. So remove it
3621 // for footnotes/endnotes to restore original LO behavior here.
3622 pTopContext->Erase(PROP_CHAR_STYLE_NAME);
3623
3624 uno::Reference< text::XText > xFootnoteText;
3625 if (GetTextFactory().is())
3626 xFootnoteText.set( GetTextFactory()->createInstance(
3627 bIsFootnote ?
3628 OUString( "com.sun.star.text.Footnote" ) : OUString( "com.sun.star.text.Endnote" )),
3629 uno::UNO_QUERY_THROW );
3630 uno::Reference< text::XFootnote > xFootnote( xFootnoteText, uno::UNO_QUERY_THROW );
3631 pTopContext->SetFootnote(xFootnote, sFootnoteCharStyleName);
3634 aFontProperties = GetTopContextOfType(CONTEXT_CHARACTER)->GetPropertyValues();
3635 appendTextContent( uno::Reference< text::XTextContent >( xFootnoteText, uno::UNO_QUERY_THROW ), aFontProperties );
3636 m_aTextAppendStack.push(TextAppendContext(uno::Reference< text::XTextAppend >( xFootnoteText, uno::UNO_QUERY_THROW ),
3637 xFootnoteText->createTextCursorByRange(xFootnoteText->getStart())));
3638
3639 // Redlines for the footnote anchor in the main text content
3640 std::vector< RedlineParamsPtr > aFootnoteRedline = std::move(m_aRedlines.top());
3641 m_aRedlines.pop();
3642 CheckRedline( xFootnote->getAnchor( ) );
3643 m_aRedlines.push( aFootnoteRedline );
3644
3645 // Try scanning for custom footnote labels
3646 if (!sFootnoteCharStyleName.isEmpty())
3647 StartCustomFootnote(pTopContext);
3648 else
3650 }
3651 catch( const uno::Exception& )
3652 {
3653 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "PushFootOrEndnote");
3654 }
3655}
3656
3658 const RedlineParamsPtr& pRedline)
3659{
3660 if ( !pRedline )
3661 return;
3662
3663 bool bRedlineMoved = false;
3664 try
3665 {
3666 OUString sType;
3667 switch ( pRedline->m_nToken & 0xffff )
3668 {
3669 case XML_mod:
3671 break;
3672 case XML_moveTo:
3673 bRedlineMoved = true;
3674 m_pParaMarkerRedlineMove = pRedline.get();
3675 [[fallthrough]];
3676 case XML_ins:
3678 break;
3679 case XML_moveFrom:
3680 bRedlineMoved = true;
3681 m_pParaMarkerRedlineMove = pRedline.get();
3682 [[fallthrough]];
3683 case XML_del:
3685 break;
3686 case XML_ParagraphFormat:
3688 break;
3689 default:
3690 throw lang::IllegalArgumentException("illegal redline token type", nullptr, 0);
3691 }
3692 beans::PropertyValues aRedlineProperties( 4 );
3693 beans::PropertyValue * pRedlineProperties = aRedlineProperties.getArray( );
3694 pRedlineProperties[0].Name = getPropertyName( PROP_REDLINE_AUTHOR );
3695 pRedlineProperties[0].Value <<= pRedline->m_sAuthor;
3696 pRedlineProperties[1].Name = getPropertyName( PROP_REDLINE_DATE_TIME );
3697 util::DateTime aDateTime = ConversionHelper::ConvertDateStringToDateTime( pRedline->m_sDate );
3698 // tdf#146171 import not specified w:date (or specified as zero date "0-00-00")
3699 // as Epoch time to avoid of losing change tracking data during ODF roundtrip
3700 if ( aDateTime.Year == 0 && aDateTime.Month == 0 && aDateTime.Day == 0 )
3701 {
3702 aDateTime.Year = 1970;
3703 aDateTime.Month = 1;
3704 aDateTime.Day = 1;
3705 }
3706 pRedlineProperties[1].Value <<= aDateTime;
3707 pRedlineProperties[2].Name = getPropertyName( PROP_REDLINE_REVERT_PROPERTIES );
3708 pRedlineProperties[2].Value <<= pRedline->m_aRevertProperties;
3709 pRedlineProperties[3].Name = "RedlineMoved";
3710 pRedlineProperties[3].Value <<= bRedlineMoved;
3711
3713 {
3714 uno::Reference < text::XRedline > xRedline( xRange, uno::UNO_QUERY_THROW );
3715 xRedline->makeRedline( sType, aRedlineProperties );
3716 }
3717 // store frame and (possible floating) table redline data for restoring them after frame conversion
3718 enum StoredRedlines eType;
3721 else if (IsInFootOrEndnote())
3722 eType = IsInFootnote() ? StoredRedlines::FOOTNOTE : StoredRedlines::ENDNOTE;
3723 else
3725
3727 {
3728 m_aStoredRedlines[eType].push_back( uno::Any(xRange) );
3729 m_aStoredRedlines[eType].push_back( uno::Any(sType) );
3730 m_aStoredRedlines[eType].push_back( uno::Any(aRedlineProperties) );
3731 }
3732 }
3733 catch( const uno::Exception & )
3734 {
3735 TOOLS_WARN_EXCEPTION( "writerfilter", "in makeRedline" );
3736 }
3737}
3738
3740{
3742 {
3745 {
3748 }
3749 }
3751 {
3752 // terminating moveFrom/moveTo redline removes also the paragraph mark
3754 }
3756 {
3759 }
3760}
3761
3763{
3764 // Writer core "officially" does not like overlapping redlines, and its UNO interface is stupid enough
3765 // to not prevent that. However, in practice in fact everything appears to work fine (except for the debug warnings
3766 // about redline table corruption, which may possibly be harmless in reality). So leave this as it is, since this
3767 // is a better representation of how the changes happened. If this will ever become a problem, overlapping redlines
3768 // will need to be merged into one, just like doing the changes in the UI does, which will lose some information
3769 // (and so if that happens, it may be better to fix Writer).
3770 // Create the redlines here from lowest (formats) to highest (inserts/removals) priority, since the last one is
3771 // what Writer presents graphically, so this will show deletes as deleted text and not as just formatted text being there.
3772 bool bUsedRange = m_aRedlines.top().size() > 0 || (GetTopContextOfType(CONTEXT_CHARACTER) &&
3773 GetTopContextOfType(CONTEXT_CHARACTER)->Redlines().size() > 0);
3774
3775 // only export ParagraphFormat, when there is no other redline in the same text portion to avoid missing redline compression,
3776 // but always export the first ParagraphFormat redline in a paragraph to keep the paragraph style change data for rejection
3777 if( (!bUsedRange || !m_bParaChanged) && GetTopContextOfType(CONTEXT_PARAGRAPH) )
3778 {
3779 std::vector<RedlineParamsPtr>& avRedLines = GetTopContextOfType(CONTEXT_PARAGRAPH)->Redlines();
3780 for( const auto& rRedline : avRedLines )
3781 CreateRedline( xRange, rRedline );
3782 }
3784 {
3785 std::vector<RedlineParamsPtr>& avRedLines = GetTopContextOfType(CONTEXT_CHARACTER)->Redlines();
3786 for( const auto& rRedline : avRedLines )
3787 CreateRedline( xRange, rRedline );
3788 }
3789 for (const auto& rRedline : m_aRedlines.top() )
3790 CreateRedline( xRange, rRedline );
3791}
3792
3794{
3795 m_bIsParaMarkerChange = true;
3796}
3797
3799{
3800 m_bIsParaMarkerChange = false;
3803}
3804
3806{
3807 m_bIsParaMarkerMove = true;
3808}
3809
3811{
3812 m_bIsParaMarkerMove = false;
3813}
3814
3816{
3817 if (pContext == m_pFootnoteContext)
3818 return;
3819
3820 assert(pContext->GetFootnote().is());
3821 m_bHasFootnoteStyle = true;
3822 m_bCheckFootnoteStyle = !pContext->GetFootnoteStyle().isEmpty();
3823 m_pFootnoteContext = pContext;
3824}
3825
3827{
3828 m_bHasFootnoteStyle = false;
3829 m_bCheckFootnoteStyle = false;
3830}
3831
3833{
3834 try
3835 {
3836 m_bIsInComments = true;
3837 if (!GetTextFactory().is())
3838 return;
3839 m_xAnnotationField.set( GetTextFactory()->createInstance( "com.sun.star.text.TextField.Annotation" ),
3840 uno::UNO_QUERY_THROW );
3841 uno::Reference< text::XText > xAnnotationText;
3842 m_xAnnotationField->getPropertyValue("TextRange") >>= xAnnotationText;
3843 m_aTextAppendStack.push(TextAppendContext(uno::Reference< text::XTextAppend >( xAnnotationText, uno::UNO_QUERY_THROW ),
3844 m_bIsNewDoc ? uno::Reference<text::XTextCursor>() : xAnnotationText->createTextCursorByRange(xAnnotationText->getStart())));
3845 }
3846 catch( const uno::Exception&)
3847 {
3848 DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
3849 }
3850}
3851
3854 std::deque<css::uno::Any>& rRedlines,
3855 std::vector<sal_Int32>& redPos,
3856 std::vector<sal_Int32>& redLen,
3857 sal_Int32& redIdx)
3858{
3859 redIdx = -1;
3860 for( size_t i = 0; i < rRedlines.size(); i+=3)
3861 {
3863 rRedlines[i] >>= xRange;
3864
3865 // is this a redline of the temporary footnote?
3867 try
3868 {
3869 xRangeCursor = xSrc->createTextCursorByRange( xRange );
3870 }
3871 catch( const uno::Exception& )
3872 {
3873 }
3874 if (xRangeCursor.is())
3875 {
3876 redIdx = i;
3877 sal_Int32 nLen = xRange->getString().getLength();
3878 redLen.push_back(nLen);
3879 xRangeCursor->gotoRange(xSrc->getStart(), true);
3880 redPos.push_back(xRangeCursor->getString().getLength() - nLen);
3881 }
3882 else
3883 {
3884 // we have already found all redlines of the footnote,
3885 // skip checking the redlines of the other footnotes
3886 if (redIdx > -1)
3887 break;
3888 // failed createTextCursorByRange(), for example, table inside the frame
3889 redLen.push_back(-1);
3890 redPos.push_back(-1);
3891 }
3892 }
3893}
3894
3896 uno::Reference< text::XText > const& xDest,
3897 std::deque<css::uno::Any>& rRedlines,
3898 std::vector<sal_Int32>& redPos,
3899 std::vector<sal_Int32>& redLen,
3900 sal_Int32 redIdx)
3901{
3902 // create redlines in the copied footnote
3903 for( size_t i = 0; redIdx > -1 && i <= sal::static_int_cast<size_t>(redIdx); i+=3)
3904 {
3905 OUString sType;
3906 beans::PropertyValues aRedlineProperties( 3 );
3907 // skip failed createTextCursorByRange()
3908 if (redPos[i/3] == -1)
3909 continue;
3910 rRedlines[i+1] >>= sType;
3911 rRedlines[i+2] >>= aRedlineProperties;
3912 uno::Reference< text::XTextCursor > xCrsr = xDest->getText()->createTextCursor();
3913 xCrsr->goRight(redPos[i/3], false);
3914 xCrsr->goRight(redLen[i/3], true);
3915 uno::Reference < text::XRedline > xRedline( xCrsr, uno::UNO_QUERY_THROW );
3916 try {
3917 xRedline->makeRedline( sType, aRedlineProperties );
3918 }
3919 catch(const uno::Exception&)
3920 {
3921 // ignore (footnotes of tracked deletions)
3922 }
3923 }
3924}
3925
3929{
3930 if (!m_bSaxError && xNoteSrc != xNoteDest)
3931 {
3932 uno::Reference< text::XText > xSrc( xNoteSrc, uno::UNO_QUERY_THROW );
3933 uno::Reference< text::XText > xDest( xNoteDest, uno::UNO_QUERY_THROW );
3935 xTxt.set( xSrc, uno::UNO_QUERY_THROW );
3936 xTxt2.set( xDest, uno::UNO_QUERY_THROW );
3937 xTxt2->copyText( xTxt );
3938
3939 // copy its redlines
3940 std::vector<sal_Int32> redPos, redLen;
3941 sal_Int32 redIdx;
3942 enum StoredRedlines eType = IsInFootnote() ? StoredRedlines::FOOTNOTE : StoredRedlines::ENDNOTE;
3943 lcl_CopyRedlines(xSrc, m_aStoredRedlines[eType], redPos, redLen, redIdx);
3944 lcl_PasteRedlines(xDest, m_aStoredRedlines[eType], redPos, redLen, redIdx);
3945
3946 // remove processed redlines
3947 for( size_t i = 0; redIdx > -1 && i <= sal::static_int_cast<size_t>(redIdx) + 2; i++)
3948 m_aStoredRedlines[eType].pop_front();
3949
3950 return true;
3951 }
3952
3953 return false;
3954}
3955
3957{
3958 uno::Reference< text::XFootnotesSupplier> xFootnotesSupplier( GetTextDocument(), uno::UNO_QUERY );
3959 uno::Reference< text::XEndnotesSupplier> xEndnotesSupplier( GetTextDocument(), uno::UNO_QUERY );
3961 if (GetFootnoteCount() > 0)
3962 {
3963 auto xFootnotes = xFootnotesSupplier->getFootnotes();
3964 if ( m_nFirstFootnoteIndex > 0 )
3965 {
3967 xFootnotes->getByIndex(0) >>= xFirstNote;
3968 uno::Reference< text::XText > xText( xFirstNote, uno::UNO_QUERY_THROW );
3969 xText->setString("");
3970 xFootnotes->getByIndex(m_nFirstFootnoteIndex) >>= xNote;
3971 CopyTemporaryNotes(xNote, xFirstNote);
3972 }
3973 for (sal_Int32 i = GetFootnoteCount(); i > 0; --i)
3974 {
3975 xFootnotes->getByIndex(i) >>= xNote;
3976 xNote->getAnchor()->setString("");
3977 }
3978 }
3979 if (GetEndnoteCount() > 0)
3980 {
3981 auto xEndnotes = xEndnotesSupplier->getEndnotes();
3982 if ( m_nFirstEndnoteIndex > 0 )
3983 {
3985 xEndnotes->getByIndex(0) >>= xFirstNote;
3986 uno::Reference< text::XText > xText( xFirstNote, uno::UNO_QUERY_THROW );
3987 xText->setString("");
3988 xEndnotes->getByIndex(m_nFirstEndnoteIndex) >>= xNote;
3989 CopyTemporaryNotes(xNote, xFirstNote);
3990 }
3991 for (sal_Int32 i = GetEndnoteCount(); i > 0; --i)
3992 {
3993 xEndnotes->getByIndex(i) >>= xNote;
3994 xNote->getAnchor()->setString("");
3995 }
3996 }
3997}
3998
3999static void lcl_convertToNoteIndices(std::deque<sal_Int32>& rNoteIds, sal_Int32& rFirstNoteIndex)
4000{
4001 // rNoteIds contains XML footnote identifiers in the loaded order of the footnotes
4002 // (the same order as in footnotes.xml), i.e. it maps temporary footnote positions to the
4003 // identifiers. For example: Ids[0] = 100; Ids[1] = -1, Ids[2] = 5.
4004 // To copy the footnotes in their final place, create an array, which map the (normalized)
4005 // footnote identifiers to the temporary footnote positions. Using the previous example,
4006 // Pos[0] = 1; Pos[1] = 2; Pos[2] = 0 (where [0], [1], [2] are the normalized
4007 // -1, 5 and 100 identifiers).
4008 std::deque<sal_Int32> aSortedIds = rNoteIds;
4009 std::sort(aSortedIds.begin(), aSortedIds.end());
4010 std::map<sal_Int32, size_t> aMapIds;
4011 // normalize footnote identifiers to 0, 1, 2 ...
4012 for (size_t i = 0; i < aSortedIds.size(); ++i)
4013 aMapIds[aSortedIds[i]] = i;
4014 // reusing rNoteIds, create the Pos array to map normalized identifiers to the loaded positions
4015 std::deque<sal_Int32> aOrigNoteIds = rNoteIds;
4016 for (size_t i = 0; i < rNoteIds.size(); ++i)
4017 rNoteIds[aMapIds[aOrigNoteIds[i]]] = i;
4018 rFirstNoteIndex = rNoteIds.front();
4019 rNoteIds.pop_front();
4020}
4021
4023{
4024 // content of the footnotes were inserted after the first footnote in temporary footnotes,
4025 // restore the content of the actual footnote by copying its content from the first
4026 // (remaining) temporary footnote and remove the temporary footnote.
4027 uno::Reference< text::XFootnotesSupplier> xFootnotesSupplier( GetTextDocument(), uno::UNO_QUERY );
4028 uno::Reference< text::XEndnotesSupplier> xEndnotesSupplier( GetTextDocument(), uno::UNO_QUERY );
4029 bool bCopied = false;
4030 if ( IsInFootOrEndnote() && ( ( IsInFootnote() && GetFootnoteCount() > -1 && xFootnotesSupplier.is() ) ||
4031 ( !IsInFootnote() && GetEndnoteCount() > -1 && xEndnotesSupplier.is() ) ) )
4032 {
4033 uno::Reference< text::XFootnote > xNoteFirst, xNoteLast;
4034 auto xFootnotes = xFootnotesSupplier->getFootnotes();
4035 auto xEndnotes = xEndnotesSupplier->getEndnotes();
4036 if ( ( ( IsInFootnote() && xFootnotes->getCount() > 1 &&
4037 ( xFootnotes->getByIndex(xFootnotes->getCount()-1) >>= xNoteLast ) ) ||
4038 ( !IsInFootnote() && xEndnotes->getCount() > 1 &&
4039 ( xEndnotes->getByIndex(xEndnotes->getCount()-1) >>= xNoteLast ) )
4040 ) && xNoteLast->getLabel().isEmpty() )
4041 {
4042 // copy content of the next temporary footnote
4043 try
4044 {
4045 if ( IsInFootnote() && !m_aFootnoteIds.empty() )
4046 {
4047 if ( m_nFirstFootnoteIndex == -1 )
4049 if (m_aFootnoteIds.empty()) // lcl_convertToNoteIndices pops m_aFootnoteIds
4050 m_bSaxError = true;
4051 else
4052 {
4053 xFootnotes->getByIndex(m_aFootnoteIds.front()) >>= xNoteFirst;
4054 m_aFootnoteIds.pop_front();
4055 }
4056 }
4057 else if ( !IsInFootnote() && !m_aEndnoteIds.empty() )
4058 {
4059 if ( m_nFirstEndnoteIndex == -1 )
4061 if (m_aEndnoteIds.empty()) // lcl_convertToNoteIndices pops m_aEndnoteIds
4062 m_bSaxError = true;
4063 else
4064 {
4065 xEndnotes->getByIndex(m_aEndnoteIds.front()) >>= xNoteFirst;
4066 m_aEndnoteIds.pop_front();
4067 }
4068 }
4069 else
4070 m_bSaxError = true;
4071 }
4072 catch (uno::Exception const&)
4073 {
4074 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "Cannot insert footnote/endnote");
4075 m_bSaxError = true;
4076 }
4077
4078 bCopied = CopyTemporaryNotes(xNoteFirst, xNoteLast);
4079 }
4080 }
4081
4082 if (!IsRTFImport() && !bCopied)
4084
4085 // In case the foot or endnote did not contain a tab.
4086 m_bIgnoreNextTab = false;
4087
4088 if (!m_aTextAppendStack.empty())
4089 m_aTextAppendStack.pop();
4090
4091 if (m_aRedlines.size() == 1)
4092 {
4093 SAL_WARN("writerfilter.dmapper", "PopFootOrEndnote() is called without PushFootOrEndnote()?");
4094 return;
4095 }
4096 m_aRedlines.pop();
4099 m_pFootnoteContext = nullptr;
4101}
4102
4104{
4106
4107 m_bIsInComments = false;
4108 m_aTextAppendStack.pop();
4109
4110 try
4111 {
4113 m_xAnnotationField->setPropertyValue("Resolved", uno::Any(true));
4114
4115 m_xAnnotationField->setPropertyValue("ParaIdParent", uno::Any(m_sAnnotationParent));
4116 m_xAnnotationField->setPropertyValue("ParaId", uno::Any(m_sAnnotationImportedParaId));
4117
4118 // See if the annotation will be a single position or a range.
4120 {
4122 uno::Reference< text::XTextContent > xContent( m_xAnnotationField, uno::UNO_QUERY_THROW );
4123 appendTextContent( xContent, aEmptyProperties );
4124 CheckRedline( xContent->getAnchor( ) );
4125 }
4126 else
4127 {
4129 // Create a range that points to the annotation start/end.
4130 uno::Reference<text::XText> const xText = aAnnotationPosition.m_xStart->getText();
4131 uno::Reference<text::XTextCursor> const xCursor = xText->createTextCursorByRange(aAnnotationPosition.m_xStart);
4132
4133 bool bMarker = false;
4134 uno::Reference<text::XTextRangeCompare> xTextRangeCompare(xText, uno::UNO_QUERY);
4135 if (xTextRangeCompare->compareRegionStarts(aAnnotationPosition.m_xStart, aAnnotationPosition.m_xEnd) == 0)
4136 {
4137 // Insert a marker so that comment around an anchored image is not collapsed during
4138 // insertion.
4139 xText->insertString(xCursor, "x", false);
4140 bMarker = true;
4141 }
4142
4143 xCursor->gotoRange(aAnnotationPosition.m_xEnd, true);
4144 uno::Reference<text::XTextRange> const xTextRange(xCursor, uno::UNO_QUERY_THROW);
4145
4146 // Attach the annotation to the range.
4147 uno::Reference<text::XTextAppend> const xTextAppend = m_aTextAppendStack.top().xTextAppend;
4148 xTextAppend->insertTextContent(xTextRange, uno::Reference<text::XTextContent>(m_xAnnotationField, uno::UNO_QUERY_THROW), !xCursor->isCollapsed());
4149
4150 if (bMarker)
4151 {
4152 // Remove the marker.
4153 xCursor->goLeft(1, true);
4154 xCursor->setString(OUString());
4155 }
4156 }
4158 }
4159 catch (uno::Exception const&)
4160 {
4161 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "Cannot insert annotation field");
4162 }
4163
4164 m_xAnnotationField.clear();
4165 m_sAnnotationParent.clear();
4167 m_nAnnotationId = -1;
4168 m_bAnnotationResolved = false;
4169}
4170
4172{
4173 m_aPendingShapes.push_back(xShape);
4174}
4175
4177{
4179 if (!m_aPendingShapes.empty())
4180 {
4181 xRet = m_aPendingShapes.front();
4182 m_aPendingShapes.pop_front();
4183 }
4184 return xRet;
4185}
4186
4188{
4189 // Append these early, so the context and the table manager stack will be
4190 // in sync, even if the text append stack is empty.
4194
4195 if (m_aTextAppendStack.empty())
4196 return;
4197 uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend;
4198
4199 try
4200 {
4201 uno::Reference< lang::XServiceInfo > xSInfo( xShape, uno::UNO_QUERY_THROW );
4202 if (xSInfo->supportsService("com.sun.star.drawing.GroupShape"))
4203 {
4204 // Textboxes in shapes do not support styles, so check saved style information and apply properties directly to the child shapes.
4205 const uno::Reference<drawing::XShapes> xShapes(xShape, uno::UNO_QUERY);
4206 const sal_uInt32 nShapeCount = xShapes.is() ? xShapes->getCount() : 0;
4207 for ( sal_uInt32 i = 0; i < nShapeCount; ++i )
4208 {
4209 try
4210 {
4211 uno::Reference<text::XTextRange> xFrame(xShapes->getByIndex(i), uno::UNO_QUERY);
4212 uno::Reference<beans::XPropertySet> xFramePropertySet;
4213 if (xFrame)
4214 xFramePropertySet.set(xFrame, uno::UNO_QUERY_THROW);
4215 uno::Reference<beans::XPropertySet> xShapePropertySet(xShapes->getByIndex(i), uno::UNO_QUERY_THROW);
4216
4217 comphelper::SequenceAsHashMap aGrabBag( xShapePropertySet->getPropertyValue("CharInteropGrabBag") );
4218
4219 // only VML import has checked for style. Don't apply default parastyle properties to other imported shapes
4220 // - except for fontsize - to maintain compatibility with previous versions of LibreOffice.
4221 const bool bOnlyApplyCharHeight = !aGrabBag["mso-pStyle"].hasValue();
4222
4223 OUString sStyleName;
4224 aGrabBag["mso-pStyle"] >>= sStyleName;
4225 StyleSheetEntryPtr pEntry = GetStyleSheetTable()->FindStyleSheetByISTD( sStyleName );
4226 if ( !pEntry )
4227 {
4228 // Use default style even in ambiguous cases (where multiple styles were defined) since MOST styles inherit
4229 // MOST of their properties from the default style. In the ambiguous case, we have to accept some kind of compromise
4230 // and the default paragraph style ought to be the safest one... (compared to DocDefaults or program defaults)
4231 pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( GetDefaultParaStyleName() );
4232 }
4233 if ( pEntry )
4234 {
4235 // The Ids here come from oox/source/vml/vmltextbox.cxx.
4236 // It probably could safely expand to all Ids that shapes support.
4237 const PropertyIds eIds[] = {
4244 };
4245 const uno::Reference<beans::XPropertyState> xShapePropertyState(xShapePropertySet, uno::UNO_QUERY_THROW);
4246 for ( const auto& eId : eIds )
4247 {
4248 try
4249 {
4250 if ( bOnlyApplyCharHeight && eId != PROP_CHAR_HEIGHT )
4251 continue;
4252
4253 const OUString & sPropName = getPropertyName(eId);
4254 if ( beans::PropertyState_DEFAULT_VALUE == xShapePropertyState->getPropertyState(sPropName) )
4255 {
4256 const uno::Any aProp = GetPropertyFromStyleSheet(eId, pEntry, /*bDocDefaults=*/true, /*bPara=*/true);
4257 if (aProp.hasValue())
4258 {
4259 if (xFrame)
4260 xFramePropertySet->setPropertyValue(sPropName, aProp);
4261 else
4262 xShapePropertySet->setPropertyValue(sPropName, aProp);
4263 }
4264 }
4265 }
4266 catch (const uno::Exception&)
4267 {
4268 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "PushShapeContext() text stylesheet property exception" );
4269 }
4270 }
4271 }
4272 }
4273 catch (const uno::Exception&)
4274 {
4275 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "PushShapeContext()" );
4276 }
4277 }
4278
4279 // A GroupShape doesn't implement text::XTextRange, but appending
4280 // an empty reference to the stacks still makes sense, because this
4281 // way bToRemove can be set, and we won't end up with duplicated
4282 // shapes for OLE objects.
4284 uno::Reference<text::XTextContent> xTxtContent(xShape, uno::UNO_QUERY);
4285 m_aAnchoredStack.push(AnchoredContext(xTxtContent));
4286 }
4287 else if (xSInfo->supportsService("com.sun.star.drawing.OLE2Shape"))
4288 {
4289 // OLE2Shape from oox should be converted to a TextEmbeddedObject for sw.
4291 uno::Reference<text::XTextContent> xTextContent(xShape, uno::UNO_QUERY);
4292 m_aAnchoredStack.push(AnchoredContext(xTextContent));
4293 uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
4294
4295 m_xEmbedded.set(m_xTextFactory->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW);
4296 uno::Reference<beans::XPropertySet> xEmbeddedProperties(m_xEmbedded, uno::UNO_QUERY_THROW);
4297 xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_EMBEDDED_OBJECT), xShapePropertySet->getPropertyValue(getPropertyName(PROP_EMBEDDED_OBJECT)));
4298 xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_ANCHOR_TYPE), uno::Any(text::TextContentAnchorType_AS_CHARACTER));
4299 // So that the original bitmap-only shape will be replaced by the embedded object.
4300 m_aAnchoredStack.top().bToRemove = true;
4301 m_aTextAppendStack.pop();
4303 }
4304 else
4305 {
4306 uno::Reference<text::XTextRange> xShapeTextRange(xShape, uno::UNO_QUERY_THROW);
4307 // Add the shape to the text append stack
4308 uno::Reference<text::XTextAppend> xShapeTextAppend(xShape, uno::UNO_QUERY_THROW);
4310 if (!m_bIsNewDoc)
4311 {
4312 xTextCursor = xShapeTextRange->getText()->createTextCursorByRange(
4313 xShapeTextRange->getStart());
4314 }
4315 TextAppendContext aContext(xShapeTextAppend, xTextCursor);
4316 m_aTextAppendStack.push(aContext);
4317
4318 // Add the shape to the anchored objects stack
4319 uno::Reference< text::XTextContent > xTxtContent( xShape, uno::UNO_QUERY_THROW );
4320 m_aAnchoredStack.push( AnchoredContext(xTxtContent) );
4321
4322 uno::Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY_THROW );
4323#ifdef DBG_UTIL
4325#endif
4326 text::TextContentAnchorType nAnchorType(text::TextContentAnchorType_AT_PARAGRAPH);
4327 xProps->getPropertyValue(getPropertyName( PROP_ANCHOR_TYPE )) >>= nAnchorType;
4328 bool checkZOrderStatus = false;
4329 if (xSInfo->supportsService("com.sun.star.text.TextFrame"))
4330 {
4332 // Extract the special "btLr text frame" mode, requested by oox, if needed.
4333 // Extract vml ZOrder from FrameInteropGrabBag
4334 uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
4336 xShapePropertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
4337
4338 for (const auto& rProp : std::as_const(aGrabBag))
4339 {
4340 if (rProp.Name == "VML-Z-ORDER")
4341 {
4343 sal_Int32 zOrder(0);
4344 rProp.Value >>= zOrder;
4345 xShapePropertySet->setPropertyValue( "ZOrder", uno::Any(pZOrderHelper->findZOrder(zOrder)));
4346 pZOrderHelper->addItem(xShapePropertySet, zOrder);
4347 xShapePropertySet->setPropertyValue(getPropertyName( PROP_OPAQUE ), uno::Any( zOrder >= 0 ) );
4348 checkZOrderStatus = true;
4349 }
4350 else if ( rProp.Name == "TxbxHasLink" )
4351 {
4352 //Chaining of textboxes will happen in ~DomainMapper_Impl
4353 //i.e when all the textboxes are read and all its attributes
4354 //have been set ( basically the Name/LinkedDisplayName )
4355 //which is set in Graphic Import.
4356 m_vTextFramesForChaining.push_back(xShape);
4357 }
4358 }
4359
4360 uno::Reference<text::XTextContent> xTextContent(xShape, uno::UNO_QUERY_THROW);
4361 uno::Reference<text::XTextRange> xTextRange(xTextAppend->createTextCursorByRange(xTextAppend->getEnd()), uno::UNO_QUERY_THROW);
4362 xTextAppend->insertTextContent(xTextRange, xTextContent, false);
4363
4364 uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
4365 // we need to re-set this value to xTextContent, then only values are preserved.
4366 xPropertySet->setPropertyValue("FrameInteropGrabBag",uno::Any(aGrabBag));
4367 }
4368 else if (nAnchorType == text::TextContentAnchorType_AS_CHARACTER)
4369 {
4370 // Fix spacing for as-character objects. If the paragraph has CT_Spacing_after set,
4371 // it needs to be set on the object too, as that's what object placement code uses.
4373 std::optional<PropertyMap::Property> aPropMargin = paragraphContext->getProperty(PROP_PARA_BOTTOM_MARGIN);
4374 if(aPropMargin)
4375 xProps->setPropertyValue( getPropertyName( PROP_BOTTOM_MARGIN ), aPropMargin->second );
4376 }
4377 else
4378 {
4379 uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
4381 xShapePropertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
4382 for (const auto& rProp : std::as_const(aGrabBag))
4383 {
4384 if (rProp.Name == "VML-Z-ORDER")
4385 {
4387 sal_Int32 zOrder(0);
4388 rProp.Value >>= zOrder;
4389 xShapePropertySet->setPropertyValue( "ZOrder", uno::Any(pZOrderHelper->findZOrder(zOrder)));
4390 pZOrderHelper->addItem(xShapePropertySet, zOrder);
4391 xShapePropertySet->setPropertyValue(getPropertyName( PROP_OPAQUE ), uno::Any( zOrder >= 0 ) );
4392 checkZOrderStatus = true;
4393 }
4394 else if ( rProp.Name == "TxbxHasLink" )
4395 {
4396 //Chaining of textboxes will happen in ~DomainMapper_Impl
4397 //i.e when all the textboxes are read and all its attributes
4398 //have been set ( basically the Name/LinkedDisplayName )
4399 //which is set in Graphic Import.
4400 m_vTextFramesForChaining.push_back(xShape);
4401 }
4402 }
4403
4404 if(IsSdtEndBefore())
4405 {
4407 if(xShapePropertySet.is())
4408 {
4409 xPropSetInfo = xShapePropertySet->getPropertySetInfo();
4410 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("InteropGrabBag"))
4411 {
4413 { "SdtEndBefore", uno::Any(true) }
4414 }));
4415 xShapePropertySet->setPropertyValue("InteropGrabBag",uno::Any(aShapeGrabBag));
4416 }
4417 }
4418 }
4419 }
4420 if (!IsInHeaderFooter() && !checkZOrderStatus)
4421 xProps->setPropertyValue(
4423 uno::Any( true ) );
4424 }
4425 m_bParaChanged = true;
4427 }
4428 catch ( const uno::Exception& )
4429 {
4430 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "Exception when adding shape");
4431 }
4432}
4433/*
4434 * Updating chart height and width after reading the actual values from wp:extent
4435*/
4437{
4438 if (!xShape.is())
4439 return;
4440
4441 uno::Reference<beans::XPropertySet> xEmbeddedProperties(m_xEmbedded, uno::UNO_QUERY_THROW);
4442 awt::Size aSize = xShape->getSize( );
4443 xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_WIDTH), uno::Any(sal_Int32(aSize.Width)));
4444 xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_HEIGHT), uno::Any(sal_Int32(aSize.Height)));
4445 uno::Reference<beans::XPropertySet> const xShapeProps(xShape, uno::UNO_QUERY);
4446 // tdf#130782 copy a11y related properties
4447 xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_DESCRIPTION),
4448 xShapeProps->getPropertyValue(getPropertyName(PROP_DESCRIPTION)));
4449 xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_TITLE),
4450 xShapeProps->getPropertyValue(getPropertyName(PROP_TITLE)));
4451 uno::Reference<container::XNamed> const xEmbedName(m_xEmbedded, uno::UNO_QUERY);
4452 uno::Reference<container::XNamed> const xShapeName(xShape, uno::UNO_QUERY);
4453 OUString const name(xShapeName->getName());
4454 if (!name.isEmpty()) // setting empty name will throw
4455 {
4456 try
4457 {
4458 xEmbedName->setName(name);
4459 }
4460 catch (uno::RuntimeException const&)
4461 {
4462 // ignore - document may contain duplicates (testchartoleobjectembeddings.docx)
4463 }
4464 }
4465}
4466
4468{
4469 if (hasTableManager())
4470 {
4473 }
4474 if ( m_aAnchoredStack.empty() )
4475 return;
4476
4477 // For OLE object replacement shape, the text append context was already removed
4478 // or the OLE object couldn't be inserted.
4479 if ( !m_aAnchoredStack.top().bToRemove )
4480 {
4482 if (!m_aTextAppendStack.empty())
4483 m_aTextAppendStack.pop();
4484 }
4485
4486 uno::Reference< text::XTextContent > xObj = m_aAnchoredStack.top( ).xTextContent;
4487 try
4488 {
4490 }
4491 catch ( const uno::RuntimeException& )
4492 {
4493 // this is normal: the shape is already attached
4494 }
4495
4496 const uno::Reference<drawing::XShape> xShape( xObj, uno::UNO_QUERY_THROW );
4497 // Remove the shape if required (most likely replacement shape for OLE object)
4498 // or anchored to a discarded header or footer
4499 if ( m_aAnchoredStack.top().bToRemove || m_bDiscardHeaderFooter )
4500 {
4501 try
4502 {
4503 uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(m_xTextDocument, uno::UNO_QUERY_THROW);
4504 uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
4505 if ( xDrawPage.is() )
4506 xDrawPage->remove( xShape );
4507 }
4508 catch( const uno::Exception& )
4509 {
4510 }
4511 }
4512
4513 // Relative width calculations deferred until section's margins are defined.
4514 // Being cautious: only deferring undefined/minimum-width shapes in order to avoid causing potential regressions
4515 css::awt::Size aShapeSize;
4516 try
4517 {
4518 aShapeSize = xShape->getSize();
4519 }
4520 catch (const css::uno::RuntimeException& e)
4521 {
4522 // May happen e.g. when text frame has no frame format
4523 // See sw/qa/extras/ooxmlimport/data/n779627.docx
4524 SAL_WARN("writerfilter.dmapper", "getSize failed. " << e.Message);
4525 }
4526 if( aShapeSize.Width <= 2 )
4527 {
4528 const uno::Reference<beans::XPropertySet> xShapePropertySet( xShape, uno::UNO_QUERY );
4529 SectionPropertyMap* pSectionContext = GetSectionContext();
4530 if ( pSectionContext && (!hasTableManager() || !getTableManager().isInTable()) &&
4531 xShapePropertySet->getPropertySetInfo()->hasPropertyByName(getPropertyName(PROP_RELATIVE_WIDTH)) )
4532 {
4533 pSectionContext->addRelativeWidthShape(xShape);
4534 }
4535 }
4536
4537 m_aAnchoredStack.pop();
4538}
4539
4541{
4542 bool bIsSdtEndBefore = false;
4544 if(pContext)
4545 {
4546 const uno::Sequence< beans::PropertyValue > currentCharProps = pContext->GetPropertyValues();
4547 for (const auto& rCurrentCharProp : currentCharProps)
4548 {
4549 if (rCurrentCharProp.Name == "CharInteropGrabBag")
4550 {
4552 rCurrentCharProp.Value >>= aCharGrabBag;
4553 for (const auto& rProp : std::as_const(aCharGrabBag))
4554 {
4555 if(rProp.Name == "SdtEndBefore")
4556 {
4557 rProp.Value >>= bIsSdtEndBefore;
4558 }
4559 }
4560 }
4561 }
4562 }
4563 return bIsSdtEndBefore;
4564}
4565
4567{
4569}
4570
4571// called from TableManager::closeCell()
4573{
4574 // in table cells, set bottom auto margin of last paragraph to 0, except in paragraphs with numbering
4575 if ((m_nTableDepth == (m_nTableCellDepth + 1))
4576 && m_xPreviousParagraph.is()
4577 && hasTableManager() && getTableManager().isCellLastParaAfterAutospacing())
4578 {
4579 uno::Reference<container::XNamed> xPreviousNumberingRules(m_xPreviousParagraph->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
4580 if ( !xPreviousNumberingRules.is() || xPreviousNumberingRules->getName().isEmpty() )
4581 m_xPreviousParagraph->setPropertyValue("ParaBottomMargin", uno::Any(static_cast<sal_Int32>(0)));
4582 }
4583
4584 m_xPreviousParagraph.clear();
4585
4586 // next table paragraph will be first paragraph in a cell
4588}
4589
4590void DomainMapper_Impl::HandleAltChunk(const OUString& rStreamName)
4591{
4592 try
4593 {
4594 // Create the import filter.
4595 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(
4598 = xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.WriterFilter");
4599
4600 // Set the target document.
4601 uno::Reference<document::XImporter> xImporter(xDocxFilter, uno::UNO_QUERY);
4602 xImporter->setTargetDocument(m_xTextDocument);
4603
4604 // Set the import parameters.
4606 uno::UNO_QUERY);
4607 if (!xStorageAccess.is())
4608 {
4609 return;
4610 }
4611 // Turn the ZIP stream into a seekable one, as the importer only works with such streams.
4612 uno::Reference<io::XStream> xStream = xStorageAccess->openStreamElementByHierarchicalName(
4613 rStreamName, embed::ElementModes::READ);
4614 std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream(xStream, true);
4615 SvMemoryStream aMemory;
4616 aMemory.WriteStream(*pStream);
4617 uno::Reference<io::XStream> xInputStream = new utl::OStreamWrapper(aMemory);
4618 // Not handling AltChunk during paste for now.
4619 uno::Reference<text::XTextRange> xInsertTextRange = GetCurrentXText()->getEnd();
4620 uno::Reference<text::XTextRange> xSectionStartingRange;
4621 SectionPropertyMap* pSectionContext = GetSectionContext();
4622 if (pSectionContext)
4623 {
4624 xSectionStartingRange = pSectionContext->GetStartingRange();
4625 }
4627 { "InputStream", uno::Any(xInputStream) },
4628 { "InsertMode", uno::Any(true) },
4629 { "TextInsertModeRange", uno::Any(xInsertTextRange) },
4630 { "AltChunkMode", uno::Any(true) },
4631 { "AltChunkStartingRange", uno::Any(xSectionStartingRange) },
4632 }));
4633
4634 // Do the actual import.
4635 uno::Reference<document::XFilter> xFilter(xDocxFilter, uno::UNO_QUERY);
4636 xFilter->filter(aDescriptor);
4637 }
4638 catch (const uno::Exception& rException)
4639 {
4640 SAL_WARN("writerfilter", "DomainMapper_Impl::HandleAltChunk: failed to handle alt chunk: "
4641 << rException.Message);
4642 }
4643}
4644
4645void DomainMapper_Impl::HandlePTab(sal_Int32 nAlignment)
4646{
4647 // We only handle the case when the line already has content, so the left-aligned ptab is
4648 // equivalent to a line break.
4649 if (nAlignment != NS_ooxml::LN_Value_ST_PTabAlignment_left)
4650 {
4651 return;
4652 }
4653
4654 if (m_aTextAppendStack.empty())
4655 {
4656 return;
4657 }
4658
4659 uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend;
4660 if (!xTextAppend.is())
4661 {
4662 return;
4663 }
4664
4666 = m_aTextAppendStack.top().xInsertPosition;
4667 if (!xInsertPosition.is())
4668 {
4669 xInsertPosition = xTextAppend->getEnd();
4670 }
4672 = xTextAppend->createTextCursorByRange(xInsertPosition);
4673
4674 // Assume that we just inserted a tab character.
4675 xCursor->goLeft(1, true);
4676 if (xCursor->getString() != "\t")
4677 {
4678 return;
4679 }
4680
4681 // Assume that there is some content before the tab character.
4682 uno::Reference<text::XParagraphCursor> xParagraphCursor(xCursor, uno::UNO_QUERY);
4683 if (!xParagraphCursor.is())
4684 {
4685 return;
4686 }
4687
4688 xCursor->collapseToStart();
4689 xParagraphCursor->gotoStartOfParagraph(true);
4690 if (xCursor->isCollapsed())
4691 {
4692 return;
4693 }
4694
4695 // Then select the tab again and replace with a line break.
4696 xCursor->collapseToEnd();
4697 xCursor->goRight(1, true);
4698 xTextAppend->insertControlCharacter(xCursor, text::ControlCharacter::LINE_BREAK, true);
4699}
4700
4702{
4703 switch (nClear)
4704 {
4705 case NS_ooxml::LN_Value_ST_BrClear_left:
4706 // SwLineBreakClear::LEFT
4708 break;
4709 case NS_ooxml::LN_Value_ST_BrClear_right:
4710 // SwLineBreakClear::RIGHT
4712 break;
4713 case NS_ooxml::LN_Value_ST_BrClear_all:
4714 // SwLineBreakClear::ALL
4716 break;
4717 }
4718}
4719
4721{
4722 if (!m_oLineBreakClear.has_value())
4723 {
4724 appendTextPortion("\n", pPropertyMap);
4725 return;
4726 }
4727
4728 if (GetTextFactory().is())
4729 {
4731 GetTextFactory()->createInstance("com.sun.star.text.LineBreak"), uno::UNO_QUERY);
4732 uno::Reference<beans::XPropertySet> xLineBreakProps(xLineBreak, uno::UNO_QUERY);
4733 xLineBreakProps->setPropertyValue("Clear", uno::Any(*m_oLineBreakClear));
4734 appendTextContent(xLineBreak, pPropertyMap->GetPropertyValues());
4735 }
4736 m_oLineBreakClear.reset();
4737}
4738
4739static sal_Int16 lcl_ParseNumberingType( std::u16string_view rCommand )
4740{
4741 sal_Int16 nRet = style::NumberingType::PAGE_DESCRIPTOR;
4742
4743 // The command looks like: " PAGE \* Arabic "
4744 // tdf#132185: but may as well be "PAGE \* Arabic"
4745 OUString sNumber;
4746 constexpr OUStringLiteral rSeparator(u"\\* ");
4747 if (size_t nStartIndex = rCommand.find(rSeparator); nStartIndex != std::u16string_view::npos)
4748 {
4749 sal_Int32 nStartIndex2 = nStartIndex + rSeparator.getLength();
4750 sNumber = o3tl::getToken(rCommand, 0, ' ', nStartIndex2);
4751 }
4752
4753 if( !sNumber.isEmpty() )
4754 {
4755 //todo: might make sense to hash this list, too
4756 struct NumberingPairs
4757 {
4758 const char* cWordName;
4759 sal_Int16 nType;
4760 };
4761 static const NumberingPairs aNumberingPairs[] =
4762 {
4763 {"Arabic", style::NumberingType::ARABIC}
4764 ,{"ROMAN", style::NumberingType::ROMAN_UPPER}
4765 ,{"roman", style::NumberingType::ROMAN_LOWER}
4766 ,{"ALPHABETIC", style::NumberingType::CHARS_UPPER_LETTER}
4767 ,{"alphabetic", style::NumberingType::CHARS_LOWER_LETTER}
4768 ,{"CircleNum", style::NumberingType::CIRCLE_NUMBER}
4769 ,{"ThaiArabic", style::NumberingType::CHARS_THAI}
4770 ,{"ThaiCardText", style::NumberingType::CHARS_THAI}
4771 ,{"ThaiLetter", style::NumberingType::CHARS_THAI}
4772// ,{"SBCHAR", style::NumberingType::}
4773// ,{"DBCHAR", style::NumberingType::}
4774// ,{"DBNUM1", style::NumberingType::}
4775// ,{"DBNUM2", style::NumberingType::}
4776// ,{"DBNUM3", style::NumberingType::}
4777// ,{"DBNUM4", style::NumberingType::}
4778 ,{"Aiueo", style::NumberingType::AIU_FULLWIDTH_JA}
4779 ,{"Iroha", style::NumberingType::IROHA_FULLWIDTH_JA}
4780// ,{"ZODIAC1", style::NumberingType::}
4781// ,{"ZODIAC2", style::NumberingType::}
4782// ,{"ZODIAC3", style::NumberingType::}
4783// ,{"CHINESENUM1", style::NumberingType::}
4784// ,{"CHINESENUM2", style::NumberingType::}
4785// ,{"CHINESENUM3", style::NumberingType::}
4786 ,{"ArabicAlpha", style::NumberingType::CHARS_ARABIC}
4787 ,{"ArabicAbjad", style::NumberingType::FULLWIDTH_ARABIC}
4788 ,{"Ganada", style::NumberingType::HANGUL_JAMO_KO}
4789 ,{"Chosung", style::NumberingType::HANGUL_SYLLABLE_KO}
4790 ,{"KoreanCounting", style::NumberingType::NUMBER_HANGUL_KO}
4791 ,{"KoreanLegal", style::NumberingType::NUMBER_LEGAL_KO}
4792 ,{"KoreanDigital", style::NumberingType::NUMBER_DIGITAL_KO}
4793 ,{"KoreanDigital2", style::NumberingType::NUMBER_DIGITAL2_KO}
4794/* possible values:
4795style::NumberingType::
4796
4797 CHARS_UPPER_LETTER_N
4798 CHARS_LOWER_LETTER_N
4799 TRANSLITERATION
4800 NATIVE_NUMBERING
4801 CIRCLE_NUMBER
4802 NUMBER_LOWER_ZH
4803 NUMBER_UPPER_ZH
4804 NUMBER_UPPER_ZH_TW
4805 TIAN_GAN_ZH
4806 DI_ZI_ZH
4807 NUMBER_TRADITIONAL_JA
4808 AIU_HALFWIDTH_JA
4809 IROHA_HALFWIDTH_JA
4810 NUMBER_UPPER_KO
4811 NUMBER_HANGUL_KO
4812 HANGUL_JAMO_KO
4813 HANGUL_SYLLABLE_KO
4814 HANGUL_CIRCLED_JAMO_KO
4815 HANGUL_CIRCLED_SYLLABLE_KO
4816 CHARS_HEBREW
4817 CHARS_NEPALI
4818 CHARS_KHMER
4819 CHARS_LAO
4820 CHARS_TIBETAN
4821 CHARS_CYRILLIC_UPPER_LETTER_BG
4822 CHARS_CYRILLIC_LOWER_LETTER_BG
4823 CHARS_CYRILLIC_UPPER_LETTER_N_BG
4824 CHARS_CYRILLIC_LOWER_LETTER_N_BG
4825 CHARS_CYRILLIC_UPPER_LETTER_RU
4826 CHARS_CYRILLIC_LOWER_LETTER_RU
4827 CHARS_CYRILLIC_UPPER_LETTER_N_RU
4828 CHARS_CYRILLIC_LOWER_LETTER_N_RU
4829 CHARS_CYRILLIC_UPPER_LETTER_SR
4830 CHARS_CYRILLIC_LOWER_LETTER_SR
4831 CHARS_CYRILLIC_UPPER_LETTER_N_SR
4832 CHARS_CYRILLIC_LOWER_LETTER_N_SR
4833 CHARS_CYRILLIC_UPPER_LETTER_UK
4834 CHARS_CYRILLIC_LOWER_LETTER_UK
4835 CHARS_CYRILLIC_UPPER_LETTER_N_UK
4836 CHARS_CYRILLIC_LOWER_LETTER_N_UK*/
4837
4838 };
4839 for(const NumberingPairs& rNumberingPair : aNumberingPairs)
4840 {
4841 if( /*sCommand*/sNumber.equalsAscii(rNumberingPair.cWordName ))
4842 {
4843 nRet = rNumberingPair.nType;
4844 break;
4845 }
4846 }
4847
4848 }
4849 return nRet;
4850}
4851
4852
4853static OUString lcl_ParseFormat( const OUString& rCommand )
4854{
4855 // The command looks like: " DATE \@"dd MMMM yyyy" or "09/02/2014"
4856 OUString command;
4857 sal_Int32 delimPos = rCommand.indexOf("\\@");
4858 if (delimPos != -1)
4859 {
4860 // Remove whitespace permitted by standard between \@ and "
4861 const sal_Int32 nQuoteIndex = rCommand.indexOf('\"');
4862 if (nQuoteIndex != -1)
4863 {
4864 sal_Int32 wsChars = nQuoteIndex - delimPos - 2;
4865 command = rCommand.replaceAt(delimPos+2, wsChars, u"");
4866 }
4867 else
4868 {
4869 // turn date \@ MM into date \@"MM"
4870 command = OUString::Concat(rCommand.subView(0, delimPos + 2)) + "\"" + o3tl::trim(rCommand.subView(delimPos + 2)) + "\"";
4871 }
4872 return OUString(msfilter::util::findQuotedText(command, u"\\@\"", '\"'));
4873 }
4874
4875 return OUString();
4876}
4877/*-------------------------------------------------------------------------
4878extract a parameter (with or without quotes) between the command and the following backslash
4879 -----------------------------------------------------------------------*/
4880static OUString lcl_ExtractToken(std::u16string_view rCommand,
4881 size_t & rIndex, bool & rHaveToken, bool & rIsSwitch)
4882{
4883 rHaveToken = false;
4884 rIsSwitch = false;
4885
4886 OUStringBuffer token;
4887 bool bQuoted(false);
4888 for (; rIndex < rCommand.size(); ++rIndex)
4889 {
4890 sal_Unicode const currentChar(rCommand[rIndex]);
4891 switch (currentChar)
4892 {
4893 case '\\':
4894 {
4895 if (rIndex == rCommand.size() - 1)
4896 {
4897 SAL_INFO("writerfilter.dmapper", "field: trailing escape");
4898 ++rIndex;
4899 return OUString();
4900 }
4901 sal_Unicode const nextChar(rCommand[rIndex+1]);
4902 if (bQuoted || '\\' == nextChar)
4903 {
4904 ++rIndex; // read 2 chars
4905 token.append(nextChar);
4906 }
4907 else // field switch (case insensitive)
4908 {
4909 rHaveToken = true;
4910 if (token.isEmpty())
4911 {
4912 rIsSwitch = true;
4913 rIndex += 2; // read 2 chars
4914 return OUString(rCommand.substr(rIndex - 2, 2)).toAsciiUpperCase();
4915 }
4916 else
4917 { // leave rIndex, read it again next time
4918 return token.makeStringAndClear();
4919 }
4920 }
4921 }
4922 break;
4923 case '\"':
4924 if (bQuoted || !token.isEmpty())
4925 {
4926 rHaveToken = true;
4927 if (bQuoted)
4928 {
4929 ++rIndex;
4930 }
4931 return token.makeStringAndClear();
4932 }
4933 else
4934 {
4935 bQuoted = true;
4936 }
4937 break;
4938 case ' ':
4939 if (bQuoted)
4940 {
4941 token.append(' ');
4942 }
4943 else
4944 {
4945 if (!token.isEmpty())
4946 {
4947 rHaveToken = true;
4948 ++rIndex;
4949 return token.makeStringAndClear();
4950 }
4951 }
4952 break;
4953 case '=':
4954 if (token.isEmpty())
4955 {
4956 rHaveToken = true;
4957 ++rIndex;
4958 return "FORMULA";
4959 }
4960 else
4961 token.append('=');
4962 break;
4963 default:
4964 token.append(currentChar);
4965 break;
4966 }
4967 }
4968 assert(rIndex == rCommand.size());
4969 if (bQuoted)
4970 {
4971 // MS Word allows this, so just emit a debug message
4972 SAL_INFO("writerfilter.dmapper",
4973 "field argument with unterminated quote");
4974 }
4975 rHaveToken = !token.isEmpty();
4976 return token.makeStringAndClear();
4977}
4978
4979std::tuple<OUString, std::vector<OUString>, std::vector<OUString> > splitFieldCommand(std::u16string_view rCommand)
4980{
4981 OUString sType;
4982 std::vector<OUString> arguments;
4983 std::vector<OUString> switches;
4984 size_t nStartIndex(0);
4985 // tdf#54584: Field may be prepended by a backslash
4986 // This is not an escapement, but already escaped literal "\"
4987 // MS Word allows this, so just skip it
4988 if ((rCommand.size() >= nStartIndex + 2) &&
4989 (rCommand[nStartIndex] == L'\\') &&
4990 (rCommand[nStartIndex + 1] != L'\\') &&
4991 (rCommand[nStartIndex + 1] != L' '))
4992 {
4993 ++nStartIndex;
4994 }
4995
4996 do
4997 {
4998 bool bHaveToken;
4999 bool bIsSwitch;
5000 OUString const token =
5001 lcl_ExtractToken(rCommand, nStartIndex, bHaveToken, bIsSwitch);
5002 assert(nStartIndex <= rCommand.size());
5003 if (bHaveToken)
5004 {
5005 if (sType.isEmpty())
5006 {
5007 sType = token.toAsciiUpperCase();
5008 }
5009 else if (bIsSwitch || !switches.empty())
5010 {
5011 switches.push_back(token);
5012 }
5013 else
5014 {
5015 arguments.push_back(token);
5016 }
5017 }
5018 } while (nStartIndex < rCommand.size());
5019
5020 return std::make_tuple(sType, arguments, switches);
5021}
5022
5023static OUString lcl_ExtractVariableAndHint( std::u16string_view rCommand, OUString& rHint )
5024{
5025 // the first word after "ASK " is the variable
5026 // the text after the variable and before a '\' is the hint
5027 // if no hint is set the variable is used as hint
5028 // the quotes of the hint have to be removed
5029 size_t nIndex = rCommand.find( ' ', 2); //find last space after 'ASK'
5030 if (nIndex == std::u16string_view::npos)
5031 return OUString();
5032 while (nIndex < rCommand.size() && rCommand[nIndex] == ' ')
5033 ++nIndex;
5034 std::u16string_view sShortCommand( rCommand.substr( nIndex ) ); //cut off the " ASK "
5035
5036 sShortCommand = o3tl::getToken(sShortCommand, 0, '\\');
5037 sal_Int32 nIndex2 = 0;
5038 std::u16string_view sRet = o3tl::getToken(sShortCommand, 0, ' ', nIndex2);
5039 if( nIndex2 > 0)
5040 rHint = sShortCommand.substr( nIndex2 );
5041 if( rHint.isEmpty() )
5042 rHint = sRet;
5043 return OUString(sRet);
5044}
5045
5046static size_t nextCode(std::u16string_view rCommand, size_t pos)
5047{
5048 bool inQuotes = false;
5049 for (; pos < rCommand.size(); ++pos)
5050 {
5051 switch (rCommand[pos])
5052 {
5053 case '"':
5054 inQuotes = !inQuotes;
5055 break;
5056 case '\\':
5057 ++pos;
5058 if (!inQuotes)
5059 return pos;
5060 break;
5061 }
5062 }
5063 return std::u16string_view::npos;
5064}
5065
5066// Returns the position of the field code
5067static size_t findCode(std::u16string_view rCommand, sal_Unicode cSwitch)
5068{
5069 for (size_t i = nextCode(rCommand, 0); i < rCommand.size(); i = nextCode(rCommand, i))
5070 if (rCommand[i] == cSwitch)
5071 return i;
5072
5073 return std::u16string_view::npos;
5074}
5075
5077 std::u16string_view rCommand,
5078 sal_Unicode cSwitch,
5079 OUString& rValue )
5080{
5081 if (size_t i = findCode(rCommand, cSwitch); i < rCommand.size())
5082 {
5083 ++i;
5084 size_t next = nextCode(rCommand, i);
5085 if (next < rCommand.size())
5086 --next; // get back before the next '\\'
5087 rValue = o3tl::trim(rCommand.substr(i, next - i));
5088 return true;
5089 }
5090
5091 return false;
5092}
5093
5094static OUString lcl_trim(std::u16string_view sValue)
5095{
5096 // it seems, all kind of quotation marks are allowed around index type identifiers
5097 // TODO apply this on bookmarks, too, if needed
5098 return OUString(o3tl::trim(sValue)).replaceAll("\"","").replaceAll(u"“", "").replaceAll(u"”", "");
5099}
5100
5101/*-------------------------------------------------------------------------
5102 extract the number format from the command and apply the resulting number
5103 format to the XPropertySet
5104 -----------------------------------------------------------------------*/
5105void DomainMapper_Impl::SetNumberFormat( const OUString& rCommand,
5106 uno::Reference< beans::XPropertySet > const& xPropertySet,
5107 bool const bDetectFormat)
5108{
5109 OUString sFormatString = lcl_ParseFormat( rCommand );
5110 // find \h - hijri/luna calendar todo: what about saka/era calendar?
5111 bool bHijri = 0 < rCommand.indexOf("\\h ");
5112 lang::Locale aUSLocale;
5113 aUSLocale.Language = "en";
5114 aUSLocale.Country = "US";
5115
5116 lang::Locale aCurrentLocale;
5117 GetAnyProperty(PROP_CHAR_LOCALE, GetTopContext()) >>= aCurrentLocale;
5118
5119 if (sFormatString.isEmpty())
5120 {
5121 // No format specified. MS Word uses different formats depending on w:lang,
5122 // "M/d/yyyy h:mm:ss AM/PM" for en-US, and "dd/MM/yyyy hh:mm:ss AM/PM" for en-GB.
5123 // ALSO SEE: ww8par5's GetWordDefaultDateStringAsUS.
5124 sal_Int32 nPos = rCommand.indexOf(" \\");
5125 OUString sCommand = nPos == -1 ? rCommand.trim()
5126 : OUString(o3tl::trim(rCommand.subView(0, nPos)));
5127 if (sCommand == "CREATEDATE" || sCommand == "PRINTDATE" || sCommand == "SAVEDATE")
5128 {
5129 try
5130 {
5131 css::uno::Reference<css::i18n::XNumberFormatCode> const& xNumberFormatCode =
5132 i18n::NumberFormatMapper::create(m_xComponentContext);
5133 sFormatString = xNumberFormatCode->getFormatCode(
5134 css::i18n::NumberFormatIndex::DATE_SYSTEM_SHORT, aCurrentLocale).Code;
5135 nPos = sFormatString.indexOf("YYYY");
5136 if (nPos == -1)
5137 sFormatString = sFormatString.replaceFirst("YY", "YYYY");
5138 if (aCurrentLocale == aUSLocale)
5139 sFormatString += " h:mm:ss AM/PM";
5140 else
5141 sFormatString += " hh:mm:ss AM/PM";
5142 }
5143 catch(const uno::Exception&)
5144 {
5145 DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
5146 }
5147 }
5148 }
5149 OUString sFormat = ConversionHelper::ConvertMSFormatStringToSO( sFormatString, aCurrentLocale, bHijri);
5150 //get the number formatter and convert the string to a format value
5151 try
5152 {
5153 sal_Int32 nKey = 0;
5154 uno::Reference< util::XNumberFormatsSupplier > xNumberSupplier( m_xTextDocument, uno::UNO_QUERY_THROW );
5155 if( bDetectFormat )
5156 {
5157 uno::Reference< util::XNumberFormatter> xFormatter(util::NumberFormatter::create(m_xComponentContext), uno::UNO_QUERY_THROW);
5158 xFormatter->attachNumberFormatsSupplier( xNumberSupplier );
5159 nKey = xFormatter->detectNumberFormat( 0, rCommand );
5160 }
5161 else
5162 {
5163 nKey = xNumberSupplier->getNumberFormats()->addNewConverted( sFormat, aUSLocale, aCurrentLocale );
5164 }
5165 xPropertySet->setPropertyValue(
5167 uno::Any( nKey ));
5168 }
5169 catch(const uno::Exception&)
5170 {
5171 }
5172}
5173
5174static uno::Any lcl_getGrabBagValue( const uno::Sequence<beans::PropertyValue>& grabBag, OUString const & name )
5175{
5176 auto pProp = std::find_if(grabBag.begin(), grabBag.end(),
5177 [&name](const beans::PropertyValue& rProp) { return rProp.Name == name; });
5178 if (pProp != grabBag.end())
5179 return pProp->Value;
5180 return uno::Any();
5181}
5182
5183//Link the text frames.
5185{
5186 //can't link textboxes if there are not even two of them...
5187 if( 2 > m_vTextFramesForChaining.size() )
5188 return ;
5189
5190 struct TextFramesForChaining {
5191 css::uno::Reference< css::drawing::XShape > xShape;
5192 sal_Int32 nId;
5193 sal_Int32 nSeq;
5194 OUString s_mso_next_textbox;
5195 OUString shapeName;
5196 TextFramesForChaining() : nId(0), nSeq(0) {}
5197 } ;
5198 typedef std::map <OUString, TextFramesForChaining> ChainMap;
5199
5200 try
5201 {
5202 ChainMap aTextFramesForChainingHelper;
5203 ::std::vector<TextFramesForChaining> chainingWPS;
5204 OUString sChainNextName("ChainNextName");
5205
5206 //learn about ALL of the textboxes and their chaining values first - because frames are processed in no specific order.
5207 for( const auto& rTextFrame : m_vTextFramesForChaining )
5208 {
5209 uno::Reference<text::XTextContent> xTextContent(rTextFrame, uno::UNO_QUERY_THROW);
5210 uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
5212 if( xPropertySet.is() )
5213 xPropertySetInfo = xPropertySet->getPropertySetInfo();
5215 uno::Reference<lang::XServiceInfo> xServiceInfo(xPropertySet, uno::UNO_QUERY);
5216
5217 TextFramesForChaining aChainStruct;
5218 OUString sShapeName;
5219 OUString sLinkChainName;
5220
5221 //The chaining name and the shape name CAN be different in .docx.
5222 //MUST use LinkDisplayName/ChainName as the shape name for establishing links.
5223 if ( xServiceInfo->supportsService("com.sun.star.text.TextFrame") )
5224 {
5225 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
5226 xPropertySet->getPropertyValue("LinkDisplayName") >>= sShapeName;
5227 }
5228 else
5229 {
5230 xPropertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
5231 xPropertySet->getPropertyValue("ChainName") >>= sShapeName;
5232 }
5233
5234 lcl_getGrabBagValue( aGrabBag, "Txbx-Id") >>= aChainStruct.nId;
5235 lcl_getGrabBagValue( aGrabBag, "Txbx-Seq") >>= aChainStruct.nSeq;
5236 lcl_getGrabBagValue( aGrabBag, "LinkChainName") >>= sLinkChainName;
5237 lcl_getGrabBagValue( aGrabBag, "mso-next-textbox") >>= aChainStruct.s_mso_next_textbox;
5238
5239 //Sometimes the shape names have not been imported. If not, we may have a fallback name.
5240 //Set name later, only if required for linking.
5241 aChainStruct.shapeName = sShapeName;
5242
5243 if (!sLinkChainName.isEmpty())
5244 {
5245 aChainStruct.xShape = rTextFrame;
5246 aTextFramesForChainingHelper[sLinkChainName] = aChainStruct;
5247 }
5248 if (aChainStruct.s_mso_next_textbox.isEmpty())
5249 { // no VML chaining => try to chain DrawingML via IDs
5250 aChainStruct.xShape = rTextFrame;
5251 if (!sLinkChainName.isEmpty())
5252 { // for member of group shapes, TestTdf73499
5253 aChainStruct.shapeName = sLinkChainName;
5254 }
5255 chainingWPS.emplace_back(aChainStruct);
5256 }
5257 }
5258
5259 //if mso-next-textbox tags are provided, create those vml-style links first. Afterwards we will make dml-style id/seq links.
5260 for (auto& msoItem : aTextFramesForChainingHelper)
5261 {
5262 //if no mso-next-textbox, we are done.
5263 //if it points to itself, we are done.
5264 if( !msoItem.second.s_mso_next_textbox.isEmpty()
5265 && msoItem.second.s_mso_next_textbox != msoItem.first )
5266 {
5267 ChainMap::iterator nextFinder=aTextFramesForChainingHelper.find(msoItem.second.s_mso_next_textbox);
5268 if( nextFinder != aTextFramesForChainingHelper.end() )
5269 {
5270 //if the frames have no name yet, then set them. LinkDisplayName / ChainName are read-only.
5271 if (msoItem.second.shapeName.isEmpty())
5272 {
5273 uno::Reference< container::XNamed > xNamed( msoItem.second.xShape, uno::UNO_QUERY );
5274 if ( xNamed.is() )
5275 {
5276 xNamed->setName( msoItem.first );
5277 msoItem.second.shapeName = msoItem.first;
5278 }
5279 }
5280 if (nextFinder->second.shapeName.isEmpty())
5281 {
5282 uno::Reference< container::XNamed > xNamed( nextFinder->second.xShape, uno::UNO_QUERY );
5283 if ( xNamed.is() )
5284 {
5285 xNamed->setName( nextFinder->first );
5286 nextFinder->second.shapeName = msoItem.first;
5287 }
5288 }
5289
5290 uno::Reference<text::XTextContent> xTextContent(msoItem.second.xShape, uno::UNO_QUERY_THROW);
5291 uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
5292
5293 //The reverse chaining happens automatically, so only one direction needs to be set
5294 xPropertySet->setPropertyValue(sChainNextName, uno::Any(nextFinder->second.shapeName));
5295
5296 //the last item in an mso-next-textbox chain is indistinguishable from id/seq items. Now that it is handled, remove it.
5297 if( nextFinder->second.s_mso_next_textbox.isEmpty() )
5298 aTextFramesForChainingHelper.erase(nextFinder->first);
5299 }
5300 }
5301 }
5302
5303 //TODO: Perhaps allow reverse sequences when mso-layout-flow-alt = "bottom-to-top"
5304 const sal_Int32 nDirection = 1;
5305
5306 //Finally - go through and attach the chains based on matching ID and incremented sequence number (dml-style).
5307 for (const auto& rOuter : chainingWPS)
5308 {
5309 for (const auto& rInner : chainingWPS)
5310 {
5311 if (rInner.nId == rOuter.nId)
5312 {
5313 if (rInner.nSeq == (rOuter.nSeq + nDirection))
5314 {
5315 uno::Reference<text::XTextContent> const xTextContent(rOuter.xShape, uno::UNO_QUERY_THROW);
5316 uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
5317
5318 //The reverse chaining happens automatically, so only one direction needs to be set
5319 xPropertySet->setPropertyValue(sChainNextName, uno::Any(rInner.shapeName));
5320 break ; //there cannot be more than one next frame
5321 }
5322 }
5323 }
5324 }
5325 m_vTextFramesForChaining.clear(); //clear the vector
5326 }
5327 catch (const uno::Exception&)
5328 {
5329 DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
5330 }
5331}
5332
5334{
5335 if (m_bIsInTextBox)
5336 return;
5337
5338 try
5339 {
5341 m_xTextFactory->createInstance("com.sun.star.text.TextFrame"), uno::UNO_QUERY_THROW);
5342 uno::Reference<container::XNamed>(xTBoxFrame, uno::UNO_QUERY_THROW)
5343 ->setName("textbox" + OUString::number(m_xPendingTextBoxFrames.size() + 1));
5345 uno::UNO_QUERY_THROW)
5346 ->appendTextContent(xTBoxFrame, beans::PropertyValues());
5347 m_xPendingTextBoxFrames.push(xTBoxFrame);
5348
5349 m_aTextAppendStack.push(TextAppendContext(uno::Reference<text::XTextAppend>(xTBoxFrame, uno::UNO_QUERY_THROW), {}));
5350 m_bIsInTextBox = true;
5351
5355 }
5356 catch (uno::Exception& e)
5357 {
5358 SAL_WARN("writerfilter.dmapper", "Exception during creating textbox (" + e.Message + ")!");
5359 }
5360}
5361
5363{
5365 return;
5366
5367 if (uno::Reference<text::XTextFrame>(m_aTextAppendStack.top().xTextAppend, uno::UNO_QUERY).is())
5368 {
5369 if (hasTableManager())
5370 {
5373 }
5375
5376 m_aTextAppendStack.pop();
5377 m_bIsInTextBox = false;
5378 }
5379}
5380
5381void DomainMapper_Impl::AttachTextBoxContentToShape(css::uno::Reference<css::drawing::XShape> xShape)
5382{
5383 // Without textbox or shape pointless to continue
5384 if (m_xPendingTextBoxFrames.empty() || !xShape)
5385 return;
5386
5387 uno::Reference< drawing::XShapes >xGroup(xShape, uno::UNO_QUERY);
5388 uno::Reference< beans::XPropertySet >xProps(xShape, uno::UNO_QUERY);
5389
5390 // If this is a group go inside
5391 if (xGroup)
5392 for (sal_Int32 i = 0; i < xGroup->getCount(); ++i)
5394 uno::Reference<drawing::XShape>(xGroup->getByIndex(i), uno::UNO_QUERY));
5395
5396 // if this shape has to be a textbox, attach the frame
5397 if (!xProps->getPropertyValue("TextBox").get<bool>())
5398 return;
5399
5400 // if this is a textbox there must be a waiting frame
5401 auto xTextBox = m_xPendingTextBoxFrames.front();
5402 if (!xTextBox)
5403 return;
5404
5405 // Pop the pending frames
5407
5408 // Attach the textbox to the shape
5409 try
5410 {
5411 xProps->setPropertyValue("TextBoxContent", uno::Any(xTextBox));
5412 }
5413 catch (...)
5414 {
5415 SAL_WARN("writerfilter.dmapper", "Exception while trying to attach textboxes!");
5416 return;
5417 }
5418
5419 // If attaching is successful, then do the linking
5420 try
5421 {
5422 // Get the name of the textbox
5423 OUString sTextBoxName;
5424 uno::Reference<container::XNamed> xName(xTextBox, uno::UNO_QUERY);
5425 if (xName && !xName->getName().isEmpty())
5426 sTextBoxName = xName->getName();
5427
5428 // Try to get the grabbag
5430 if (xProps->getPropertySetInfo()->hasPropertyByName("InteropGrabBag"))
5431 xProps->getPropertyValue("InteropGrabBag") >>= aOldGrabBagSeq;
5432
5433 // If the grabbag successfully get...
5434 if (!aOldGrabBagSeq.hasElements())
5435 return;
5436
5437 // Check for the existing linking information
5438 bool bSuccess = false;
5439 beans::PropertyValues aNewGrabBagSeq;
5440 const auto& aHasLink = lcl_getGrabBagValue(aOldGrabBagSeq, "TxbxHasLink");
5441
5442 // If there must be a link, do it
5443 if (aHasLink.hasValue() && aHasLink.get<bool>())
5444 {
5445 auto aLinkProp = comphelper::makePropertyValue("LinkChainName", sTextBoxName);
5446 for (sal_uInt32 i = 0; i < aOldGrabBagSeq.size(); ++i)
5447 {
5448 aNewGrabBagSeq.realloc(i + 1);
5449 // If this is the link name replace it
5450 if (!aOldGrabBagSeq[i].Name.isEmpty() && !aLinkProp.Name.isEmpty()
5451 && (aOldGrabBagSeq[i].Name == aLinkProp.Name))
5452 {
5453 aNewGrabBagSeq.getArray()[i] = aLinkProp;
5454 bSuccess = true;
5455 }
5456 // else copy
5457 else
5458 aNewGrabBagSeq.getArray()[i] = aOldGrabBagSeq[i];
5459 }
5460
5461 // If there was no replacement, append the linking data
5462 if (!bSuccess)
5463 {
5464 aNewGrabBagSeq.realloc(aNewGrabBagSeq.size() + 1);
5465 aNewGrabBagSeq.getArray()[aNewGrabBagSeq.size() - 1] = aLinkProp;
5466 bSuccess = true;
5467 }
5468 }
5469
5470 // If the linking changed the grabbag, apply the modifications
5471 if (aNewGrabBagSeq.hasElements() && bSuccess)
5472 {
5473 xProps->setPropertyValue("InteropGrabBag", uno::Any(aNewGrabBagSeq));
5474 m_vTextFramesForChaining.push_back(xShape);
5475 }
5476 }
5477 catch (...)
5478 {
5479 SAL_WARN("writerfilter.dmapper", "Exception while trying to link textboxes!");
5480 }
5481}
5482
5483uno::Reference<beans::XPropertySet> DomainMapper_Impl::FindOrCreateFieldMaster(const char* pFieldMasterService, const OUString& rFieldMasterName)
5484{
5485 // query master, create if not available
5486 uno::Reference< text::XTextFieldsSupplier > xFieldsSupplier( GetTextDocument(), uno::UNO_QUERY_THROW );
5487 uno::Reference< container::XNameAccess > xFieldMasterAccess = xFieldsSupplier->getTextFieldMasters();
5489 OUString sFieldMasterService( OUString::createFromAscii(pFieldMasterService) );
5490 OUStringBuffer aFieldMasterName;
5491 OUString sDatabaseDataSourceName = GetSettingsTable()->GetCurrentDatabaseDataSource();
5492 bool bIsMergeField = sFieldMasterService.endsWith("Database");
5493 aFieldMasterName.appendAscii( pFieldMasterService );
5494 aFieldMasterName.append('.');
5495 if ( bIsMergeField && !sDatabaseDataSourceName.isEmpty() )
5496 {
5497 aFieldMasterName.append(sDatabaseDataSourceName + ".");
5498 }
5499 aFieldMasterName.append(rFieldMasterName);
5500 OUString sFieldMasterName = aFieldMasterName.makeStringAndClear();
5501 if(xFieldMasterAccess->hasByName(sFieldMasterName))
5502 {
5503 //get the master
5504 xMaster.set(xFieldMasterAccess->getByName(sFieldMasterName), uno::UNO_QUERY_THROW);
5505 }
5506 else if( m_xTextFactory.is() )
5507 {
5508 //create the master
5509 xMaster.set( m_xTextFactory->createInstance(sFieldMasterService), uno::UNO_QUERY_THROW);
5510 if ( !bIsMergeField || sDatabaseDataSourceName.isEmpty() )
5511 {
5512 //set the master's name
5513 xMaster->setPropertyValue(
5515 uno::Any(rFieldMasterName));
5516 } else {
5517 // set database data, based on the "databasename.tablename" of sDatabaseDataSourceName
5518 xMaster->setPropertyValue(
5520 uno::Any(sDatabaseDataSourceName.copy(0, sDatabaseDataSourceName.indexOf('.'))));
5521 xMaster->setPropertyValue(
5523 uno::Any(sal_Int32(0)));
5524 xMaster->setPropertyValue(
5526 uno::Any(sDatabaseDataSourceName.copy(sDatabaseDataSourceName.indexOf('.') + 1)));
5527 xMaster->setPropertyValue(
5529 uno::Any(rFieldMasterName));
5530 }
5531 }
5532 return xMaster;
5533}
5534
5536{
5537 m_bParaHadField = true;
5539 return;
5540#ifdef DBG_UTIL
5541 TagLogger::getInstance().element("pushFieldContext");
5542#endif
5543
5545 if (!m_aTextAppendStack.empty())
5546 {
5547 uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend;
5548 if (xTextAppend.is())
5549 xCrsr = xTextAppend->createTextCursorByRange(
5550 m_aTextAppendStack.top().xInsertPosition.is()
5551 ? m_aTextAppendStack.top().xInsertPosition
5552 : xTextAppend->getEnd());
5553 }
5554
5556 if (xCrsr.is())
5557 xStart = xCrsr->getStart();
5558 m_aFieldStack.push_back(new FieldContext(xStart));
5559}
5560/*-------------------------------------------------------------------------
5561//the current field context waits for the completion of the command
5562 -----------------------------------------------------------------------*/
5564{
5565 return !m_aFieldStack.empty() && !m_aFieldStack.back()->IsCommandCompleted();
5566}
5567/*-------------------------------------------------------------------------
5568//the current field context waits for the completion of the command
5569 -----------------------------------------------------------------------*/
5571{
5572 return !m_aFieldStack.empty();
5573}
5574
5575// Mark top field context as containing a fixed field
5577{
5578 if (IsOpenField())
5579 m_aFieldStack.back()->SetFieldLocked();
5580}
5581
5582HeaderFooterContext::HeaderFooterContext(bool bTextInserted, sal_Int32 nTableDepth)
5583 : m_bTextInserted(bTextInserted)
5584 , m_nTableDepth(nTableDepth)
5585{
5586}
5587
5589{
5590 return m_bTextInserted;
5591}
5592
5594
5596 : m_bFieldCommandCompleted(false)
5597 , m_xStartRange(std::move( xStart ))
5598 , m_bFieldLocked( false )
5599 , m_bCommandType(false)
5600{
5601 m_pProperties = new PropertyMap();
5602}
5603
5604
5606{
5607}
5608
5610{
5611#ifndef NDEBUG
5612 if (xTextField.is())
5613 {
5614 uno::Reference<lang::XServiceInfo> const xServiceInfo(xTextField, uno::UNO_QUERY);
5615 assert(xServiceInfo.is());
5616 // those must be set by SetFormField()
5617 assert(!xServiceInfo->supportsService("com.sun.star.text.Fieldmark")
5618 && !xServiceInfo->supportsService("com.sun.star.text.FormFieldmark"));
5619 }
5620#endif
5621 m_xTextField = xTextField;
5622}
5623
5625{
5626 rAny >>= m_sVariableValue;
5627}
5628
5629void FieldContext::AppendCommand(std::u16string_view rPart)
5630{
5631 m_sCommand[m_bCommandType] += rPart;
5632}
5633
5634::std::vector<OUString> FieldContext::GetCommandParts() const
5635{
5636 ::std::vector<OUString> aResult;
5637 sal_Int32 nIndex = 0;
5638 bool bInString = false;
5639 OUString sPart;
5640 while (nIndex != -1)
5641 {
5642 OUString sToken = GetCommand().getToken(0, ' ', nIndex);
5643 bool bInStringNext = bInString;
5644
5645 if (sToken.isEmpty())
5646 continue;
5647
5648 if (sToken[0] == '"')
5649 {
5650 bInStringNext = true;
5651 sToken = sToken.copy(1);
5652 }
5653 if (sToken.endsWith("\""))
5654 {
5655 bInStringNext = false;
5656 sToken = sToken.copy(0, sToken.getLength() - 1);
5657 }
5658
5659 if (bInString)
5660 {
5661 sPart += " " + sToken;
5662 if (!bInStringNext)
5663 {
5664 aResult.push_back(sPart);
5665 }
5666 }
5667 else
5668 {
5669 if (bInStringNext)
5670 {
5671 sPart = sToken;
5672 }
5673 else
5674 {
5675 aResult.push_back(sToken);
5676 }
5677 }
5678
5679 bInString = bInStringNext;
5680 }
5681
5682 return aResult;
5683}
5684
5685/*-------------------------------------------------------------------------
5686//collect the pieces of the command
5687 -----------------------------------------------------------------------*/
5688void DomainMapper_Impl::AppendFieldCommand(OUString const & rPartOfCommand)
5689{
5690#ifdef DBG_UTIL
5691 TagLogger::getInstance().startElement("appendFieldCommand");
5692 TagLogger::getInstance().chars(rPartOfCommand);
5694#endif
5695
5696 FieldContextPtr pContext = m_aFieldStack.back();
5697 OSL_ENSURE( pContext, "no field context available");
5698 if( pContext )
5699 {
5700 // Set command line type: normal or deleted
5701 pContext->SetCommandType(m_bTextDeleted);
5702 pContext->AppendCommand( rPartOfCommand );
5703 }
5704}
5705
5706
5707typedef std::multimap < sal_Int32, OUString > TOCStyleMap;
5708
5709
5710static ww::eField GetWW8FieldId(OUString const& rType)
5711{
5712 std::unordered_map<OUString, ww::eField> mapID
5713 {
5714 {"ADDRESSBLOCK", ww::eADDRESSBLOCK},
5715 {"ADVANCE", ww::eADVANCE},
5716 {"ASK", ww::eASK},
5717 {"AUTONUM", ww::eAUTONUM},
5718 {"AUTONUMLGL", ww::eAUTONUMLGL},
5719 {"AUTONUMOUT", ww::eAUTONUMOUT},
5720 {"AUTOTEXT", ww::eAUTOTEXT},
5721 {"AUTOTEXTLIST", ww::eAUTOTEXTLIST},
5722 {"AUTHOR", ww::eAUTHOR},
5723 {"BARCODE", ww::eBARCODE},
5724 {"BIDIOUTLINE", ww::eBIDIOUTLINE},
5725 {"DATE", ww::eDATE},
5726 {"COMMENTS", ww::eCOMMENTS},
5727 {"COMPARE", ww::eCOMPARE},
5728 {"CONTROL", ww::eCONTROL},
5729 {"CREATEDATE", ww::eCREATEDATE},
5730 {"DATABASE", ww::eDATABASE},
5731 {"DDEAUTOREF", ww::eDDEAUTOREF},
5732 {"DDEREF", ww::eDDEREF},
5733 {"DOCPROPERTY", ww::eDOCPROPERTY},
5734 {"DOCVARIABLE", ww::eDOCVARIABLE},
5735 {"EDITTIME", ww::eEDITTIME},
5736 {"EMBED", ww::eEMBED},
5737 {"EQ", ww::eEQ},
5738 {"FILLIN", ww::eFILLIN},
5739 {"FILENAME", ww::eFILENAME},
5740 {"FILESIZE", ww::eFILESIZE},
5741 {"FOOTREF", ww::eFOOTREF},
5742// {"FORMULA", ww::},
5743 {"FORMCHECKBOX", ww::eFORMCHECKBOX},
5744 {"FORMDROPDOWN", ww::eFORMDROPDOWN},
5745 {"FORMTEXT", ww::eFORMTEXT},
5746 {"GLOSSREF", ww::eGLOSSREF},
5747 {"GOTOBUTTON", ww::eGOTOBUTTON},
5748 {"GREETINGLINE", ww::eGREETINGLINE},
5749 {"HTMLCONTROL", ww::eHTMLCONTROL},
5750 {"HYPERLINK", ww::eHYPERLINK},
5751 {"IF", ww::eIF},
5752 {"INFO", ww::eINFO},
5753 {"INCLUDEPICTURE", ww::eINCLUDEPICTURE},
5754 {"INCLUDETEXT", ww::eINCLUDETEXT},
5755 {"INCLUDETIFF", ww::eINCLUDETIFF},
5756 {"KEYWORDS", ww::eKEYWORDS},
5757 {"LASTSAVEDBY", ww::eLASTSAVEDBY},
5758 {"LINK", ww::eLINK},
5759 {"LISTNUM", ww::eLISTNUM},
5760 {"MACRO", ww::eMACRO},
5761 {"MACROBUTTON", ww::eMACROBUTTON},
5762 {"MERGEDATA", ww::eMERGEDATA},
5763 {"MERGEFIELD", ww::eMERGEFIELD},
5764 {"MERGEINC", ww::eMERGEINC},
5765 {"MERGEREC", ww::eMERGEREC},
5766 {"MERGESEQ", ww::eMERGESEQ},
5767 {"NEXT", ww::eNEXT},
5768 {"NEXTIF", ww::eNEXTIF},
5769 {"NOTEREF", ww::eNOTEREF},
5770 {"PAGE", ww::ePAGE},
5771 {"PAGEREF", ww::ePAGEREF},
5772 {"PLUGIN", ww::ePLUGIN},
5773 {"PRINT", ww::ePRINT},
5774 {"PRINTDATE", ww::ePRINTDATE},
5775 {"PRIVATE", ww::ePRIVATE},
5776 {"QUOTE", ww::eQUOTE},
5777 {"RD", ww::eRD},
5778 {"REF", ww::eREF},
5779 {"REVNUM", ww::eREVNUM},
5780 {"SAVEDATE", ww::eSAVEDATE},
5781 {"SECTION", ww::eSECTION},
5782 {"SECTIONPAGES", ww::eSECTIONPAGES},
5783 {"SEQ", ww::eSEQ},
5784 {"SET", ww::eSET},
5785 {"SKIPIF", ww::eSKIPIF},
5786 {"STYLEREF", ww::eSTYLEREF},
5787 {"SUBSCRIBER", ww::eSUBSCRIBER},
5788 {"SUBJECT", ww::eSUBJECT},
5789 {"SYMBOL", ww::eSYMBOL},
5790 {"TA", ww::eTA},
5791 {"TEMPLATE", ww::eTEMPLATE},
5792 {"TIME", ww::eTIME},
5793 {"TITLE", ww::eTITLE},
5794 {"TOA", ww::eTOA},
5795 {"USERINITIALS", ww::eUSERINITIALS},
5796 {"USERADDRESS", ww::eUSERADDRESS},
5797 {"USERNAME", ww::eUSERNAME},
5798
5799 {"TOC", ww::eTOC},
5800 {"TC", ww::eTC},
5801 {"NUMCHARS", ww::eNUMCHARS},
5802 {"NUMWORDS", ww::eNUMWORDS},
5803 {"NUMPAGES", ww::eNUMPAGES},
5804 {"INDEX", ww::eINDEX},
5805 {"XE", ww::eXE},
5806 {"BIBLIOGRAPHY", ww::eBIBLIOGRAPHY},
5807 {"CITATION", ww::eCITATION},
5808 };
5809 auto const it = mapID.find(rType);
5810 return (it == mapID.end()) ? ww::eNONE : it->second;
5811}
5812
5814{
5815 static const FieldConversionMap_t aFieldConversionMap
5816 {
5817// {"ADDRESSBLOCK", {"", FIELD_ADDRESSBLOCK }},
5818// {"ADVANCE", {"", FIELD_ADVANCE }},
5819 {"ASK", {"SetExpression", FIELD_ASK }},
5820 {"AUTONUM", {"SetExpression", FIELD_AUTONUM }},
5821 {"AUTONUMLGL", {"SetExpression", FIELD_AUTONUMLGL }},
5822 {"AUTONUMOUT", {"SetExpression", FIELD_AUTONUMOUT }},
5823 {"AUTHOR", {"DocInfo.CreateAuthor", FIELD_AUTHOR }},
5824 {"DATE", {"DateTime", FIELD_DATE }},
5825 {"COMMENTS", {"DocInfo.Description", FIELD_COMMENTS }},
5826 {"CREATEDATE", {"DocInfo.CreateDateTime", FIELD_CREATEDATE }},
5827 {"DOCPROPERTY", {"", FIELD_DOCPROPERTY }},
5828 {"DOCVARIABLE", {"User", FIELD_DOCVARIABLE }},
5829 {"EDITTIME", {"DocInfo.EditTime", FIELD_EDITTIME }},
5830 {"EQ", {"", FIELD_EQ }},
5831 {"FILLIN", {"Input", FIELD_FILLIN }},
5832 {"FILENAME", {"FileName", FIELD_FILENAME }},
5833// {"FILESIZE", {"", FIELD_FILESIZE }},
5834 {"FORMULA", {"TableFormula", FIELD_FORMULA }},
5835 {"FORMCHECKBOX", {"", FIELD_FORMCHECKBOX }},
5836 {"FORMDROPDOWN", {"DropDown", FIELD_FORMDROPDOWN }},
5837 {"FORMTEXT", {"Input", FIELD_FORMTEXT }},
5838 {"GOTOBUTTON", {"", FIELD_GOTOBUTTON }},
5839 {"HYPERLINK", {"", FIELD_HYPERLINK }},
5840 {"IF", {"ConditionalText", FIELD_IF }},
5841// {"INFO", {"", FIELD_INFO }},
5842 {"INCLUDEPICTURE", {"", FIELD_INCLUDEPICTURE}},
5843 {"KEYWORDS", {"DocInfo.KeyWords", FIELD_KEYWORDS }},
5844 {"LASTSAVEDBY", {"DocInfo.ChangeAuthor", FIELD_LASTSAVEDBY }},
5845 {"MACROBUTTON", {"Macro", FIELD_MACROBUTTON }},
5846 {"MERGEFIELD", {"Database", FIELD_MERGEFIELD }},
5847 {"MERGEREC", {"DatabaseNumberOfSet", FIELD_MERGEREC }},
5848// {"MERGESEQ", {"", FIELD_MERGESEQ }},
5849 {"NEXT", {"DatabaseNextSet", FIELD_NEXT }},
5850 {"NEXTIF", {"DatabaseNextSet", FIELD_NEXTIF }},
5851 {"PAGE", {"PageNumber", FIELD_PAGE }},
5852 {"PAGEREF", {"GetReference", FIELD_PAGEREF }},
5853 {"PRINTDATE", {"DocInfo.PrintDateTime", FIELD_PRINTDATE }},
5854 {"REF", {"GetReference", FIELD_REF }},
5855 {"REVNUM", {"DocInfo.Revision", FIELD_REVNUM }},
5856 {"SAVEDATE", {"DocInfo.ChangeDateTime", FIELD_SAVEDATE }},
5857// {"SECTION", {"", FIELD_SECTION }},
5858// {"SECTIONPAGES", {"", FIELD_SECTIONPAGES }},
5859 {"SEQ", {"SetExpression", FIELD_SEQ }},
5860 {"SET", {"SetExpression", FIELD_SET }},
5861// {"SKIPIF", {"", FIELD_SKIPIF }},
5862// {"STYLEREF", {"", FIELD_STYLEREF }},
5863 {"SUBJECT", {"DocInfo.Subject", FIELD_SUBJECT }},
5864 {"SYMBOL", {"", FIELD_SYMBOL }},
5865 {"TEMPLATE", {"TemplateName", FIELD_TEMPLATE }},
5866 {"TIME", {"DateTime", FIELD_TIME }},
5867 {"TITLE", {"DocInfo.Title", FIELD_TITLE }},
5868 {"USERINITIALS", {"Author", FIELD_USERINITIALS }},
5869// {"USERADDRESS", {"", FIELD_USERADDRESS }},
5870 {"USERNAME", {"Author", FIELD_USERNAME }},
5871
5872
5873 {"TOC", {"com.sun.star.text.ContentIndex", FIELD_TOC }},
5874 {"TC", {"com.sun.star.text.ContentIndexMark", FIELD_TC }},
5875 {"NUMCHARS", {"CharacterCount", FIELD_NUMCHARS }},
5876 {"NUMWORDS", {"WordCount", FIELD_NUMWORDS }},
5877 {"NUMPAGES", {"PageCount", FIELD_NUMPAGES }},
5878 {"INDEX", {"com.sun.star.text.DocumentIndex", FIELD_INDEX }},
5879 {"XE", {"com.sun.star.text.DocumentIndexMark", FIELD_XE }},
5880 {"BIBLIOGRAPHY",{"com.sun.star.text.Bibliography", FIELD_BIBLIOGRAPHY }},
5881 {"CITATION", {"com.sun.star.text.TextField.Bibliography",FIELD_CITATION }},
5882 };
5883
5884 return aFieldConversionMap;
5885}
5886
5888{
5889 static const FieldConversionMap_t aEnhancedFieldConversionMap =
5890 {
5891 {"FORMCHECKBOX", {"FormFieldmark", FIELD_FORMCHECKBOX}},
5892 {"FORMDROPDOWN", {"FormFieldmark", FIELD_FORMDROPDOWN}},
5893 {"FORMTEXT", {"Fieldmark", FIELD_FORMTEXT}},
5894 };
5895
5896 return aEnhancedFieldConversionMap;
5897}
5898
5900 (const FieldContextPtr& pContext,
5901 uno::Reference< uno::XInterface > const & xFieldInterface,
5902 uno::Reference< beans::XPropertySet > const& xFieldProperties)
5903{
5904 OUString sVariable, sHint;
5905
5906 sVariable = lcl_ExtractVariableAndHint(pContext->GetCommand(), sHint);
5907
5908 // remove surrounding "" if exists
5909 if(sHint.getLength() >= 2)
5910 {
5911 std::u16string_view sTmp = o3tl::trim(sHint);
5912 if (o3tl::starts_with(sTmp, u"\"") && o3tl::ends_with(sTmp, u"\""))
5913 {
5914 sHint = sTmp.substr(1, sTmp.size() - 2);
5915 }
5916 }
5917
5918 // determine field master name
5921 ("com.sun.star.text.FieldMaster.SetExpression", sVariable);
5922
5923 // a set field is a string
5924 xMaster->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::Any(text::SetVariableType::STRING));
5925
5926 // attach the master to the field
5928 ( xFieldInterface, uno::UNO_QUERY_THROW );
5929 xDependentField->attachTextFieldMaster( xMaster );
5930
5931 uno::Any aAnyHint(sHint);
5932 xFieldProperties->setPropertyValue(getPropertyName(PROP_HINT), aAnyHint);
5933 xFieldProperties->setPropertyValue(getPropertyName(PROP_CONTENT), aAnyHint);
5934 xFieldProperties->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::Any(text::SetVariableType::STRING));
5935
5936 // Mimic MS Word behavior (hide the SET)
5937 xFieldProperties->setPropertyValue(getPropertyName(PROP_IS_VISIBLE), uno::Any(false));
5938}
5939
5941 (const FieldContextPtr& pContext,
5942 uno::Reference< uno::XInterface > & xFieldInterface,
5943 uno::Reference< beans::XPropertySet > const& xFieldProperties)
5944{
5945 //doesn the command contain a variable name?
5946 OUString sVariable, sHint;
5947
5948 sVariable = lcl_ExtractVariableAndHint( pContext->GetCommand(),
5949 sHint );
5950 if(!sVariable.isEmpty())
5951 {
5952 // determine field master name
5955 ("com.sun.star.text.FieldMaster.SetExpression", sVariable );
5956 // An ASK field is always a string of characters
5957 xMaster->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::Any(text::SetVariableType::STRING));
5958
5959 // attach the master to the field
5961 ( xFieldInterface, uno::UNO_QUERY_THROW );
5962 xDependentField->attachTextFieldMaster( xMaster );
5963
5964 // set input flag at the field
5965 xFieldProperties->setPropertyValue(
5967 // set the prompt
5968 xFieldProperties->setPropertyValue(
5970 uno::Any( sHint ));
5971 xFieldProperties->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::Any(text::SetVariableType::STRING));
5972 // The ASK has no field value to display
5973 xFieldProperties->setPropertyValue(getPropertyName(PROP_IS_VISIBLE), uno::Any(false));
5974 }
5975 else
5976 {
5977 //don't insert the field
5978 //todo: maybe import a 'normal' input field here?
5979 xFieldInterface = nullptr;
5980 }
5981}
5982
5988OUString DomainMapper_Impl::convertFieldFormula(const OUString& input) {
5989
5990 if (!m_pSettingsTable)
5991 {
5992 return input;
5993 }
5994
5995 OUString listSeparator = m_pSettingsTable->GetListSeparator();
5996
5997 /* Replace logical condition functions with LO equivalent operators */
5998 OUString changed = input.replaceAll(" <> ", " NEQ ");
5999 changed = changed.replaceAll(" <= ", " LEQ ");
6000 changed = changed.replaceAll(" >= ", " GEQ ");
6001 changed = changed.replaceAll(" = " , " EQ ");
6002 changed = changed.replaceAll(" < " , " L ");
6003 changed = changed.replaceAll(" > " , " G ");
6004
6005 changed = changed.replaceAll("<>", " NEQ ");
6006 changed = changed.replaceAll("<=", " LEQ ");
6007 changed = changed.replaceAll(">=", " GEQ ");
6008 changed = changed.replaceAll("=" , " EQ ");
6009 changed = changed.replaceAll("<" , " L ");
6010 changed = changed.replaceAll(">" , " G ");
6011
6012 /* Replace function calls with infix keywords for AND(), OR(), and ROUND(). Nothing needs to be
6013 * done for NOT(). This simple regex will work properly with most common cases. However, it may
6014 * not work correctly when the arguments are nested subcalls to other functions, like
6015 * ROUND(MIN(1,2),MAX(3,4)). See TDF#134765. */
6016 icu::ErrorCode status;
6017 icu::UnicodeString usInput(changed.getStr());
6018 const uint32_t rMatcherFlags = UREGEX_CASE_INSENSITIVE;
6019 OUString regex = "\\b(AND|OR|ROUND)\\s*\\(\\s*([^" + listSeparator + "]+)\\s*" + listSeparator + "\\s*([^)]+)\\s*\\)";
6020 icu::UnicodeString usRegex(regex.getStr());
6021 icu::RegexMatcher rmatch1(usRegex, usInput, rMatcherFlags, status);
6022 usInput = rmatch1.replaceAll(icu::UnicodeString("(($2) $1 ($3))"), status);
6023
6024 /* Assumes any remaining list separators separate arguments to functions that accept lists
6025 * (SUM, MIN, MAX, MEAN, etc.) */
6026 usInput.findAndReplace(icu::UnicodeString(listSeparator.getStr()), "|");
6027
6028 /* Surround single cell references with angle brackets.
6029 * If there is ever added a function name that ends with a digit, this regex will need to be revisited. */
6030 icu::RegexMatcher rmatch2("\\b([A-Z]{1,3}[0-9]+)\\b(?![(])", usInput, rMatcherFlags, status);
6031 usInput = rmatch2.replaceAll(icu::UnicodeString("<$1>"), status);
6032
6033 /* Cell references must be upper case
6034 * TODO: convert reference to other tables, e.g. SUM(Table1 A1:B2), where "Table1" is a bookmark of the table,
6035 * TODO: also column range A:A */
6036 icu::RegexMatcher rmatch3("(<[a-z]{1,3}[0-9]+>|\\b(above|below|left|right)\\b)", usInput, rMatcherFlags, status);
6037 icu::UnicodeString replacedCellRefs;
6038 while (rmatch3.find(status) && status.isSuccess()) {
6039 rmatch3.appendReplacement(replacedCellRefs, rmatch3.group(status).toUpper(), status);
6040 }
6041 rmatch3.appendTail(replacedCellRefs);
6042
6043 /* Fix up cell ranges */
6044 icu::RegexMatcher rmatch4("<([A-Z]{1,3}[0-9]+)>:<([A-Z]{1,3}[0-9]+)>", replacedCellRefs, rMatcherFlags, status);
6045 usInput = rmatch4.replaceAll(icu::UnicodeString("<$1:$2>"), status);
6046
6047 /* Fix up user defined names */
6048 icu::RegexMatcher rmatch5("\\bDEFINED\\s*\\(<([A-Z]+[0-9]+)>\\)", usInput, rMatcherFlags, status);
6049 usInput = rmatch5.replaceAll(icu::UnicodeString("DEFINED($1)"), status);
6050
6051 /* Prepare replace of ABOVE/BELOW/LEFT/RIGHT by adding spaces around them */
6052 icu::RegexMatcher rmatch6("\\b(ABOVE|BELOW|LEFT|RIGHT)\\b", usInput, rMatcherFlags, status);
6053 usInput = rmatch6.replaceAll(icu::UnicodeString(" $1 "), status);
6054
6055 /* DOCX allows to set decimal symbol independently from the locale of the document, so if
6056 * needed, convert decimal comma to get working formula in a document language (locale),
6057 * which doesn't use decimal comma */
6058 if ( m_pSettingsTable->GetDecimalSymbol() == "," && !m_bIsDecimalComma )
6059 {
6060 icu::RegexMatcher rmatch7("\\b([0-9]+),([0-9]+([eE][-]?[0-9]+)?)\\b", usInput, rMatcherFlags, status);
6061 usInput = rmatch7.replaceAll(icu::UnicodeString("$1.$2"), status);
6062 }
6063
6064 return OUString(usInput.getTerminatedBuffer());
6065}
6066
6068 (const FieldContextPtr& pContext,
6069 uno::Reference< beans::XPropertySet > const& xFieldProperties)
6070{
6071 OUString command = pContext->GetCommand().trim();
6072
6073 // Remove number formatting from \# to end of command
6074 // TODO: handle custom number formatting
6075 sal_Int32 delimPos = command.indexOf("\\#");
6076 if (delimPos != -1)
6077 {
6078 command = command.replaceAt(delimPos, command.getLength() - delimPos, u"").trim();
6079 }
6080
6081 // command must contains = and at least another char
6082 if (command.getLength() < 2)
6083 return;
6084
6085 // we don't copy the = symbol from the command
6086 OUString formula = convertFieldFormula(command.copy(1));
6087
6088 xFieldProperties->setPropertyValue(getPropertyName(PROP_CONTENT), uno::Any(formula));
6089 xFieldProperties->setPropertyValue(getPropertyName(PROP_NUMBER_FORMAT), uno::Any(sal_Int32(0)));
6090 xFieldProperties->setPropertyValue("IsShowFormula", uno::Any(false));
6091
6092 // grab-bag the original and converted formula
6093 if (hasTableManager())
6094 {
6095 TablePropertyMapPtr pPropMap(new TablePropertyMap());
6096 pPropMap->Insert(PROP_CELL_FORMULA, uno::Any(command.copy(1)), true, CELL_GRAB_BAG);
6097 pPropMap->Insert(PROP_CELL_FORMULA_CONVERTED, uno::Any(formula), true, CELL_GRAB_BAG);
6098 getTableManager().cellProps(pPropMap);
6099 }
6100}
6101
6103{
6104 const OUString & rCommand(pContext->GetCommand());
6105 sal_Int32 nIndex = 0, nEnd = 0;
6106 RubyInfo aInfo ;
6107 nIndex = rCommand.indexOf("\\* jc" );
6108 if (nIndex != -1)
6109 {
6110 nIndex += 5;
6111 sal_uInt32 nJc = o3tl::toInt32(o3tl::getToken(rCommand, 0, ' ',nIndex));
6112 const sal_Int32 aRubyAlignValues[] =
6113 {
6114 NS_ooxml::LN_Value_ST_RubyAlign_center,
6115 NS_ooxml::LN_Value_ST_RubyAlign_distributeLetter,
6116 NS_ooxml::LN_Value_ST_RubyAlign_distributeSpace,
6117 NS_ooxml::LN_Value_ST_RubyAlign_left,
6118 NS_ooxml::LN_Value_ST_RubyAlign_right,
6119 NS_ooxml::LN_Value_ST_RubyAlign_rightVertical,
6120 };
6121 aInfo.nRubyAlign = aRubyAlignValues[(nJc<SAL_N_ELEMENTS(aRubyAlignValues))?nJc:0];
6122 }
6123
6124 // we don't parse or use the font field in rCommand
6125
6126 nIndex = rCommand.indexOf("\\* hps" );
6127 if (nIndex != -1)
6128 {
6129 nIndex += 6;
6130 aInfo.nHps = o3tl::toInt32(o3tl::getToken(rCommand, 0, ' ',nIndex));
6131 }
6132
6133 nIndex = rCommand.indexOf("\\o");
6134 if (nIndex == -1)
6135 return;
6136 nIndex = rCommand.indexOf('(', nIndex);
6137 if (nIndex == -1)
6138 return;
6139 nEnd = rCommand.lastIndexOf(')');
6140 if (nEnd == -1)
6141 return;
6142 if (nEnd <= nIndex)
6143 return;
6144
6145 std::u16string_view sRubyParts = rCommand.subView(nIndex+1,nEnd-nIndex-1);
6146 nIndex = 0;
6147 std::u16string_view sPart1 = o3tl::getToken(sRubyParts, 0, ',', nIndex);
6148 std::u16string_view sPart2 = o3tl::getToken(sRubyParts, 0, ',', nIndex);
6149 size_t nIndex2 = 0;
6150 size_t nEnd2 = 0;
6151 if ((nIndex2 = sPart1.find('(')) != std::u16string_view::npos && (nEnd2 = sPart1.rfind(')')) != std::u16string_view::npos && nEnd2 > nIndex2)
6152 {
6153 aInfo.sRubyText = sPart1.substr(nIndex2+1,nEnd2-nIndex2-1);
6154 }
6155
6156 PropertyMapPtr pRubyContext(new PropertyMap());
6157 pRubyContext->InsertProps(GetTopContext());
6158 if (aInfo.nHps > 0)
6159 {
6160 double fVal = double(aInfo.nHps) / 2.;
6161 uno::Any aVal( fVal );
6162
6163 pRubyContext->Insert(PROP_CHAR_HEIGHT, aVal);
6164 pRubyContext->Insert(PROP_CHAR_HEIGHT_ASIAN, aVal);
6165 }
6166 PropertyValueVector_t aProps = comphelper::sequenceToContainer< PropertyValueVector_t >(pRubyContext->GetPropertyValues());
6167 aInfo.sRubyStyle = m_rDMapper.getOrCreateCharStyle(aProps, /*bAlwaysCreate=*/false);
6168 PropertyMapPtr pCharContext(new PropertyMap());
6170 pCharContext->InsertProps(m_pLastCharacterContext);
6171 pCharContext->InsertProps(pContext->getProperties());
6172 pCharContext->Insert(PROP_RUBY_TEXT, uno::Any( aInfo.sRubyText ) );
6173 pCharContext->Insert(PROP_RUBY_ADJUST, uno::Any(static_cast<sal_Int16>(ConversionHelper::convertRubyAlign(aInfo.nRubyAlign))));
6174 if ( aInfo.nRubyAlign == NS_ooxml::LN_Value_ST_RubyAlign_rightVertical )
6175 pCharContext->Insert(PROP_RUBY_POSITION, uno::Any(css::text::RubyPosition::INTER_CHARACTER));
6176 pCharContext->Insert(PROP_RUBY_STYLE, uno::Any(aInfo.sRubyStyle));
6177 appendTextPortion(OUString(sPart2), pCharContext);
6178
6179}
6180
6182 (const FieldContextPtr& pContext,
6183 uno::Reference< uno::XInterface > const & xFieldInterface,
6184 uno::Reference< beans::XPropertySet > const& xFieldProperties)
6185{
6186 //create a sequence field master "AutoNr"
6189 ("com.sun.star.text.FieldMaster.SetExpression",
6190 "AutoNr");
6191
6192 xMaster->setPropertyValue( getPropertyName(PROP_SUB_TYPE),
6193 uno::Any(text::SetVariableType::SEQUENCE));
6194
6195 //apply the numbering type
6196 xFieldProperties->setPropertyValue(
6198 uno::Any( lcl_ParseNumberingType(pContext->GetCommand()) ));
6199 // attach the master to the field
6201 ( xFieldInterface, uno::UNO_QUERY_THROW );
6202 xDependentField->attachTextFieldMaster( xMaster );
6203}
6204
6206 (std::u16string_view,
6207 uno::Reference< beans::XPropertySet > const& xFieldProperties,
6209{
6210 if (eFieldId == FIELD_USERNAME)
6211 xFieldProperties->setPropertyValue
6213
6214 // Always set as FIXED b/c MS Word only updates these fields via user intervention (F9)
6215 // AUTHOR of course never changes and USERNAME is easily mis-used as an original author field.
6216 // Additionally, this was forced as fixed if any special case-formatting was provided.
6217 {
6218 xFieldProperties->setPropertyValue(
6220 uno::Any( true ));
6221 //PROP_CURRENT_PRESENTATION is set later anyway
6222 }
6223}
6224
6226 (const FieldContextPtr& pContext,
6227 OUString const& rFirstParam,
6228 uno::Reference< uno::XInterface > & xFieldInterface)
6229{
6230 //some docproperties should be imported as document statistic fields, some as DocInfo fields
6231 //others should be user fields
6232 if (rFirstParam.isEmpty())
6233 return;
6234
6235 constexpr sal_uInt8 SET_ARABIC = 0x01;
6236 constexpr sal_uInt8 SET_DATE = 0x04;
6237 struct DocPropertyMap
6238 {
6239 const char* pDocPropertyName;
6240 const char* pServiceName;
6241 sal_uInt8 nFlags;
6242 };
6243 static const DocPropertyMap aDocProperties[] =
6244 {
6245 {"CreateTime", "DocInfo.CreateDateTime", SET_DATE},
6246 {"Characters", "CharacterCount", SET_ARABIC},
6247 {"Comments", "DocInfo.Description", 0},
6248 {"Keywords", "DocInfo.KeyWords", 0},
6249 {"LastPrinted", "DocInfo.PrintDateTime", 0},
6250 {"LastSavedBy", "DocInfo.ChangeAuthor", 0},
6251 {"LastSavedTime", "DocInfo.ChangeDateTime", SET_DATE},
6252 {"Paragraphs", "ParagraphCount", SET_ARABIC},
6253 {"RevisionNumber", "DocInfo.Revision", 0},
6254 {"Subject", "DocInfo.Subject", 0},
6255 {"Template", "TemplateName", 0},
6256 {"Title", "DocInfo.Title", 0},
6257 {"TotalEditingTime", "DocInfo.EditTime", 0},
6258 {"Words", "WordCount", SET_ARABIC}
6259
6260 //other available DocProperties:
6261 //Bytes, Category, CharactersWithSpaces, Company
6262 //HyperlinkBase,
6263 //Lines, Manager, NameofApplication, ODMADocId, Pages,
6264 //Security,
6265 };
6266 uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier(m_xTextDocument, uno::UNO_QUERY);
6267 uno::Reference<document::XDocumentProperties> xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties();
6268 uno::Reference<beans::XPropertySet> xUserDefinedProps(xDocumentProperties->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
6269 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xUserDefinedProps->getPropertySetInfo();
6270 //search for a field mapping
6271 OUString sFieldServiceName;
6272 size_t nMap = 0;
6273 if (!xPropertySetInfo->hasPropertyByName(rFirstParam))
6274 {
6275 for( ; nMap < SAL_N_ELEMENTS(aDocProperties); ++nMap )
6276 {
6277 if (rFirstParam.equalsAscii(aDocProperties[nMap].pDocPropertyName))
6278 {
6279 sFieldServiceName = OUString::createFromAscii(aDocProperties[nMap].pServiceName);
6280 break;
6281 }
6282 }
6283 }
6284 else
6285 pContext->CacheVariableValue(xUserDefinedProps->getPropertyValue(rFirstParam));
6286
6287 OUString sServiceName("com.sun.star.text.TextField.");
6288 bool bIsCustomField = false;
6289 if(sFieldServiceName.isEmpty())
6290 {
6291 //create a custom property field
6292 sServiceName += "DocInfo.Custom";
6293 bIsCustomField = true;
6294 }
6295 else
6296 {
6297 sServiceName += sFieldServiceName;
6298 }
6299 if (m_xTextFactory.is())
6300 xFieldInterface = m_xTextFactory->createInstance(sServiceName);
6301 uno::Reference<beans::XPropertySet> xFieldProperties( xFieldInterface, uno::UNO_QUERY_THROW);
6302 if( bIsCustomField )
6303 {
6304 xFieldProperties->setPropertyValue(
6305 getPropertyName(PROP_NAME), uno::Any(rFirstParam));
6306 pContext->SetCustomField( xFieldProperties );
6307 }
6308 else
6309 {
6310 if(0 != (aDocProperties[nMap].nFlags & SET_ARABIC))
6311 xFieldProperties->setPropertyValue(
6313 uno::Any( style::NumberingType::ARABIC ));
6314 else if(0 != (aDocProperties[nMap].nFlags & SET_DATE))
6315 {
6316 xFieldProperties->setPropertyValue(
6318 uno::Any( true ));
6319 SetNumberFormat( pContext->GetCommand(), xFieldProperties );
6320 }
6321 }
6322}
6323
6324static uno::Sequence< beans::PropertyValues > lcl_createTOXLevelHyperlinks( bool bHyperlinks, const OUString& sChapterNoSeparator,
6325 const uno::Sequence< beans::PropertyValues >& aLevel, const std::optional<style::TabStop> numtab)
6326{
6327 //create a copy of the level and add new entries
6328
6329 std::vector<css::beans::PropertyValues> aNewLevel;
6330 aNewLevel.reserve(aLevel.getLength() + 5); // at most 5 added items
6331
6332 static constexpr OUStringLiteral tokType(u"TokenType");
6333 static constexpr OUStringLiteral tokHStart(u"TokenHyperlinkStart");
6334 static constexpr OUStringLiteral tokHEnd(u"TokenHyperlinkEnd");
6335 static constexpr OUStringLiteral tokPNum(u"TokenPageNumber");
6336 static constexpr OUStringLiteral tokENum(u"TokenEntryNumber");
6337
6338 if (bHyperlinks)
6339 aNewLevel.push_back({ comphelper::makePropertyValue(tokType, tokHStart) });
6340
6341 for (const auto& item : aLevel)
6342 {
6343 OUString tokenType;
6344 if (auto it = std::find_if(item.begin(), item.end(),
6345 [](const auto& p) { return p.Name == tokType; });
6346 it != item.end())
6347 it->Value >>= tokenType;
6348
6349 if (bHyperlinks && (tokenType == tokHStart || tokenType == tokHEnd))
6350 continue; // We add hyperlink ourselves, so just skip existing hyperlink start / end
6351
6352 if (!sChapterNoSeparator.isEmpty() && tokenType == tokPNum)
6353 {
6354 // This is an existing page number token; insert the chapter and separator before it
6355 aNewLevel.push_back(
6356 { comphelper::makePropertyValue(tokType, OUString("TokenChapterInfo")),
6357 comphelper::makePropertyValue("ChapterFormat", text::ChapterFormat::NUMBER) });
6358 aNewLevel.push_back({ comphelper::makePropertyValue(tokType, OUString("TokenText")),
6359 comphelper::makePropertyValue("Text", sChapterNoSeparator) });
6360 }
6361
6362 aNewLevel.push_back(item);
6363
6364 if (numtab && tokenType == tokENum)
6365 {
6366 // There is a fixed tab stop position needed in the level after the numbering
6367 aNewLevel.push_back(
6368 { comphelper::makePropertyValue(tokType, OUString("TokenTabStop")),
6369 comphelper::makePropertyValue("TabStopPosition", numtab->Position) });
6370 }
6371 }
6372
6373 if (bHyperlinks)
6374 aNewLevel.push_back({ comphelper::makePropertyValue(tokType, tokHEnd) });
6375
6376 return comphelper::containerToSequence(aNewLevel);
6377}
6378
6381{
6382 if (!m_xSdtEntryStart.is())
6383 return OUString();
6384
6385 uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
6386 if(!xTextAppend.is())
6387 return OUString();
6388
6389 // try-catch was added in the same way as inside appendTextSectionAfter()
6390 try
6391 {
6392 uno::Reference<text::XParagraphCursor> xCursor(xTextAppend->createTextCursorByRange(m_xSdtEntryStart), uno::UNO_QUERY_THROW);
6393 if (!xCursor.is())
6394 return OUString();
6395
6396 //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
6397 xCursor->gotoStartOfParagraph( false );
6398 if (m_aTextAppendStack.top().xInsertPosition.is())
6399 xCursor->gotoRange( m_aTextAppendStack.top().xInsertPosition, true );
6400 else
6401 xCursor->gotoEnd( true );
6402
6403 // the paragraph after this new section might have been already inserted
6404 OUString sResult = xCursor->getString();
6405 if (sResult.endsWith(SAL_NEWLINE_STRING))
6406 sResult = sResult.copy(0, sResult.getLength() - SAL_N_ELEMENTS(SAL_NEWLINE_STRING) + 1);
6407
6408 return sResult;
6409 }
6410 catch(const uno::Exception&)
6411 {
6412 }
6413
6414 return OUString();
6415}
6416
6417css::uno::Reference<css::beans::XPropertySet>
6419{
6420 if (m_bParaChanged)
6421 {
6422 finishParagraph(GetTopContextOfType(CONTEXT_PARAGRAPH), false); // resets m_bParaChanged
6425 SetIsFirstRun(true);
6426 // The first paragraph of the index that is continuation of just finished one needs to be
6427 // removed when finished (unless more content will arrive, which will set m_bParaChanged)
6429 }
6430 const auto& xTextAppend = GetTopTextAppend();
6431 const auto xTextRange = xTextAppend->getEnd();
6432 const auto xRet = createSectionForRange(xTextRange, xTextRange, sServiceName, false);
6433 if (!m_aTextAppendStack.top().xInsertPosition)
6434 {
6435 try
6436 {
6437 m_bStartedTOC = true;
6439 = xTextRange->getText()->createTextCursor();
6440 assert(xTOCTextCursor.is());
6441 xTOCTextCursor->gotoEnd(false);
6442 m_aTextAppendStack.push(TextAppendContext(xTextAppend, xTOCTextCursor));
6443 }
6444 catch (const uno::Exception&)
6445 {
6446 TOOLS_WARN_EXCEPTION("writerfilter.dmapper",
6447 "DomainMapper_Impl::StartIndexSectionChecked:");
6448 }
6449 }
6450 return xRet;
6451}
6452
6462static auto FilterChars(std::u16string_view const& rStyleName) -> OUString
6463{
6464 return msfilter::util::CreateDOCXStyleId(rStyleName);
6465}
6466
6467static OUString UnquoteFieldText(std::u16string_view s)
6468{
6469 OUStringBuffer result(s.size());
6470 for (size_t i = 0; i < s.size(); ++i)
6471 {
6472 switch (s[i])
6473 {
6474 case '"':
6475 continue;
6476 case '\\':
6477 if (i < s.size() - 1)
6478 ++i;
6479 [[fallthrough]];
6480 default:
6481 result.append(s[i]);
6482 }
6483 }
6484 return result.makeStringAndClear();
6485}
6486
6487OUString DomainMapper_Impl::ConvertTOCStyleName(OUString const& rTOCStyleName)
6488{
6489 assert(!rTOCStyleName.isEmpty());
6490 if (auto const pStyle = GetStyleSheetTable()->FindStyleSheetByISTD(rTOCStyleName))
6491 { // theoretical case: what OOXML says
6492 return pStyle->m_sStyleName;
6493 }
6494 auto const pStyle = GetStyleSheetTable()->FindStyleSheetByISTD(FilterChars(rTOCStyleName));
6495 if (pStyle && m_bIsNewDoc)
6496 { // practical case: Word wrote i18n name to TOC field, but it doesn't
6497 // exist in styles.xml; tdf#153083 clone it for best roundtrip
6498 SAL_INFO("writerfilter.dmapper", "cloning TOC paragraph style (presumed built-in) " << rTOCStyleName << " from " << pStyle->m_sStyleName);
6499 return GetStyleSheetTable()->CloneTOCStyle(GetFontTable(), pStyle, rTOCStyleName);
6500 }
6501 else
6502 {
6503 return GetStyleSheetTable()->ConvertStyleName(rTOCStyleName);
6504 }
6505}
6506
6508 (const FieldContextPtr& pContext,
6509 const OUString & sTOCServiceName)
6510{
6511 OUString sValue;
6512 if (IsInHeaderFooter())
6514 bool bTableOfFigures = false;
6515 bool bHyperlinks = false;
6516 bool bFromOutline = false;
6517 bool bFromEntries = false;
6518 bool bHideTabLeaderPageNumbers = false ;
6519 bool bIsTabEntry = false ;
6520 bool bNewLine = false ;
6521 bool bParagraphOutlineLevel = false;
6522
6523 sal_Int16 nMaxLevel = 10;
6524 OUString sTemplate;
6525 OUString sChapterNoSeparator;
6526 OUString sFigureSequence;
6527 OUString aBookmarkName;
6528
6529// \a Builds a table of figures but does not include the captions's label and number
6530 if( lcl_FindInCommand( pContext->GetCommand(), 'a', sValue ))
6531 { //make it a table of figures
6532 bTableOfFigures = true;
6533 }
6534// \b Uses a bookmark to specify area of document from which to build table of contents
6535 if( lcl_FindInCommand( pContext->GetCommand(), 'b', sValue ))
6536 {
6537 aBookmarkName = sValue.trim().replaceAll("\"","");
6538 }
6539 if( lcl_FindInCommand( pContext->GetCommand(), 'c', sValue ))
6540// \c Builds a table of figures of the given label
6541 {
6542 //todo: sValue contains the label's name
6543 bTableOfFigures = true;
6544 sFigureSequence = sValue.trim();
6545 sFigureSequence = sFigureSequence.replaceAll("\"", "").replaceAll("'","");
6546 }
6547// \d Defines the separator between sequence and page numbers
6548 if( lcl_FindInCommand( pContext->GetCommand(), 'd', sValue ))
6549 {
6550 //todo: insert the chapter number into each level and insert the separator additionally
6551 sChapterNoSeparator = UnquoteFieldText(sValue);
6552 }
6553// \f Builds a table of contents using TC entries instead of outline levels
6554 if( lcl_FindInCommand( pContext->GetCommand(), 'f', sValue ))
6555 {
6556 //todo: sValue can contain a TOC entry identifier - use unclear
6557 bFromEntries = true;
6558 }
6559// \h Hyperlinks the entries and page numbers within the table of contents
6560 if( lcl_FindInCommand( pContext->GetCommand(), 'h', sValue ))
6561 {
6562 //todo: make all entries to hyperlinks
6563 bHyperlinks = true;
6564 }
6565// \l Defines the TC entries field level used to build a table of contents
6566// if( lcl_FindInCommand( pContext->GetCommand(), 'l', sValue ))
6567// {
6568 //todo: entries can only be included completely
6569// }
6570// \n Builds a table of contents or a range of entries, such as 1-9 in a table of contents without page numbers
6571// if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
6572// {
6573 //todo: what does the description mean?
6574// }
6575// \o Builds a table of contents by using outline levels instead of TC entries
6576 if( lcl_FindInCommand( pContext->GetCommand(), 'o', sValue ))
6577 {
6578 bFromOutline = true;
6579 if (sValue.isEmpty())
6580 nMaxLevel = WW_OUTLINE_MAX;
6581 else
6582 {
6583 sal_Int32 nIndex = 0;
6584 o3tl::getToken(sValue, 0, '-', nIndex );
6585 nMaxLevel = static_cast<sal_Int16>(nIndex != -1 ? o3tl::toInt32(sValue.subView(nIndex)) : 0);
6586 }
6587 }
6588// \p Defines the separator between the table entry and its page number
6589// \s Builds a table of contents by using a sequence type
6590// \t Builds a table of contents by using style names other than the standard outline styles
6591 if( lcl_FindInCommand( pContext->GetCommand(), 't', sValue ))
6592 {
6593 OUString sToken = sValue.getToken(1, '"');
6594 sTemplate = sToken.isEmpty() ? sValue : sToken;
6595 }
6596// \u Builds a table of contents by using the applied paragraph outline level
6597 if( lcl_FindInCommand( pContext->GetCommand(), 'u', sValue ))
6598 {
6599 bFromOutline = true;
6600 bParagraphOutlineLevel = true;
6601 //todo: what doesn 'the applied paragraph outline level' refer to?
6602 }
6603// \w Preserve tab characters within table entries
6604 if( lcl_FindInCommand( pContext->GetCommand(), 'w', sValue ))
6605 {
6606 bIsTabEntry = true ;
6607 }
6608// \x Preserve newline characters within table entries
6609 if( lcl_FindInCommand( pContext->GetCommand(), 'x', sValue ))
6610 {
6611 bNewLine = true ;
6612 }
6613// \z Hides page numbers within the table of contents when shown in Web Layout View
6614 if( lcl_FindInCommand( pContext->GetCommand(), 'z', sValue ))
6615 {
6616 bHideTabLeaderPageNumbers = true ;
6617 }
6618
6619 //if there's no option then it should be created from outline
6620 if( !bFromOutline && !bFromEntries && sTemplate.isEmpty() )
6621 bFromOutline = true;
6622
6623 const OUString aTocTitle = extractTocTitle();
6624
6626
6627 if (m_xTextFactory.is() && ! m_aTextAppendStack.empty())
6628 {
6629 const auto& xTextAppend = GetTopTextAppend();
6630 if (aTocTitle.isEmpty() || bTableOfFigures)
6631 {
6632 // reset marker of the TOC title
6633 m_xSdtEntryStart.clear();
6634
6635 // Create section before setting m_bStartTOC: finishing paragraph
6636 // inside StartIndexSectionChecked could do the wrong thing otherwise
6637 xTOC = StartIndexSectionChecked(bTableOfFigures ? "com.sun.star.text.IllustrationsIndex"
6638 : sTOCServiceName);
6639
6640 const auto xTextCursor = xTextAppend->getText()->createTextCursor();
6641 if (xTextCursor)
6642 xTextCursor->gotoEnd(false);
6643 m_xTOCMarkerCursor = xTextCursor;
6644 }
6645 else
6646 {
6647 // create TOC section
6648 css::uno::Reference<css::text::XTextRange> xTextRangeEndOfTocHeader = GetTopTextAppend()->getEnd();
6649 xTOC = createSectionForRange(m_xSdtEntryStart, xTextRangeEndOfTocHeader, sTOCServiceName, false);
6650
6651 // init [xTOCMarkerCursor]
6652 uno::Reference< text::XText > xText = xTextAppend->getText();
6653 uno::Reference< text::XTextCursor > xCrsr = xText->createTextCursor();
6654 m_xTOCMarkerCursor = xCrsr;
6655
6656 // create header of the TOC with the TOC title inside
6657 createSectionForRange(m_xSdtEntryStart, xTextRangeEndOfTocHeader, "com.sun.star.text.IndexHeaderSection", true);
6658 }
6659 }
6660
6661 m_bStartTOC = true;
6662 pContext->SetTOC(xTOC);
6663 m_bParaHadField = false;
6664
6665 if (!xTOC)
6666 return;
6667
6668 xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::Any(aTocTitle));
6669
6670 if (!aBookmarkName.isEmpty())
6671 xTOC->setPropertyValue(getPropertyName(PROP_TOC_BOOKMARK), uno::Any(aBookmarkName));
6672 if (!bTableOfFigures)
6673 {
6674 xTOC->setPropertyValue( getPropertyName( PROP_LEVEL ), uno::Any( nMaxLevel ) );
6675 xTOC->setPropertyValue( getPropertyName( PROP_CREATE_FROM_OUTLINE ), uno::Any( bFromOutline ));
6676 xTOC->setPropertyValue( getPropertyName( PROP_CREATE_FROM_MARKS ), uno::Any( bFromEntries ));
6677 xTOC->setPropertyValue( getPropertyName( PROP_HIDE_TAB_LEADER_AND_PAGE_NUMBERS ), uno::Any( bHideTabLeaderPageNumbers ));
6678 xTOC->setPropertyValue( getPropertyName( PROP_TAB_IN_TOC ), uno::Any( bIsTabEntry ));
6679 xTOC->setPropertyValue( getPropertyName( PROP_TOC_NEW_LINE ), uno::Any( bNewLine ));
6680 xTOC->setPropertyValue( getPropertyName( PROP_TOC_PARAGRAPH_OUTLINE_LEVEL ), uno::Any( bParagraphOutlineLevel ));
6681 if( !sTemplate.isEmpty() )
6682 {
6683 //the string contains comma separated the names and related levels
6684 //like: "Heading 1,1,Heading 2,2"
6686 sal_Int32 nLevel;
6687 sal_Int32 nPosition = 0;
6688 auto const tsep(sTemplate.indexOf(',') != -1 ? ',' : ';');
6689 while( nPosition >= 0)
6690 {
6691 OUString sStyleName = sTemplate.getToken(0, tsep, nPosition);
6692 //empty tokens should be skipped
6693 while( sStyleName.isEmpty() && nPosition > 0 )
6694 sStyleName = sTemplate.getToken(0, tsep, nPosition);
6695 nLevel = o3tl::toInt32(o3tl::getToken(sTemplate, 0, tsep, nPosition ));
6696 if( !nLevel )
6697 nLevel = 1;
6698 if( !sStyleName.isEmpty() )
6699 aMap.emplace(nLevel, sStyleName);
6700 }
6702 xTOC->getPropertyValue(getPropertyName(PROP_LEVEL_PARAGRAPH_STYLES)) >>= xParaStyles;
6703 for( nLevel = 1; nLevel < 10; ++nLevel)
6704 {
6705 sal_Int32 nLevelCount = aMap.count( nLevel );
6706 if( nLevelCount )
6707 {
6708 TOCStyleMap::iterator aTOCStyleIter = aMap.find( nLevel );
6709
6710 uno::Sequence< OUString> aStyles( nLevelCount );
6711 for ( auto& rStyle : asNonConstRange(aStyles) )
6712 {
6713 // tdf#153083 must map w:styleId to w:name
6714 rStyle = ConvertTOCStyleName(aTOCStyleIter->second);
6715 ++aTOCStyleIter;
6716 }
6717 xParaStyles->replaceByIndex(nLevel - 1, uno::Any(aStyles));
6718 }
6719 }
6720 xTOC->setPropertyValue(getPropertyName(PROP_CREATE_FROM_LEVEL_PARAGRAPH_STYLES), uno::Any( true ));
6721
6722 }
6723
6724 uno::Reference<container::XIndexAccess> xChapterNumberingRules;
6725 if (auto xSupplier = GetTextDocument().query<text::XChapterNumberingSupplier>())
6726 xChapterNumberingRules = xSupplier->getChapterNumberingRules();
6728 if (auto xStylesSupplier = GetTextDocument().query<style::XStyleFamiliesSupplier>())
6729 {
6730 auto xStyleFamilies = xStylesSupplier->getStyleFamilies();
6731 xStyleFamilies->getByName(getPropertyName(PROP_PARAGRAPH_STYLES)) >>= xStyles;
6732 }
6733
6735 xTOC->getPropertyValue(getPropertyName(PROP_LEVEL_FORMAT)) >>= xLevelFormats;
6736 sal_Int32 nLevelCount = xLevelFormats->getCount();
6737 //start with level 1, 0 is the header level
6738 for( sal_Int32 nLevel = 1; nLevel < nLevelCount; ++nLevel)
6739 {
6741 xLevelFormats->getByIndex( nLevel ) >>= aLevel;
6742
6743 // Get the tab stops coming from the styles; store to the level definitions
6744 std::optional<style::TabStop> numTab;
6745 if (xChapterNumberingRules && xStyles)
6746 {
6747 // This relies on the chapter numbering rules already defined
6748 // (see ListDef::CreateNumberingRules)
6750 xChapterNumberingRules->getByIndex(nLevel - 1) >>= props;
6751 bool bHasNumbering = false;
6752 bool bUseTabStop = false;
6753 for (const auto& propval : props)
6754 {
6755 // We rely on PositionAndSpaceMode being always equal to LABEL_ALIGNMENT,
6756 // because ListDef::CreateNumberingRules doesn't create legacy lists
6757 if (propval.Name == "NumberingType")
6758 bHasNumbering = propval.Value != style::NumberingType::NUMBER_NONE;
6759 else if (propval.Name == "LabelFollowedBy")
6760 bUseTabStop = propval.Value == text::LabelFollow::LISTTAB;
6761 // Do not use FirstLineIndent property from the rules, because it is unreliable
6762 }
6763 if (bHasNumbering && bUseTabStop)
6764 {
6765 OUString style;
6766 xTOC->getPropertyValue("ParaStyleLevel" + OUString::number(nLevel)) >>= style;
6768 if (xStyles->getByName(style) >>= xStyle)
6769 {
6770 if (uno::Reference<beans::XPropertyState> xPropState{ xStyle,
6771 uno::UNO_QUERY })
6772 {
6773 if (xPropState->getPropertyState("ParaTabStops")
6774 == beans::PropertyState_DIRECT_VALUE)
6775 {
6776 if (uno::Sequence<style::TabStop> tabStops;
6777 xStyle->getPropertyValue("ParaTabStops") >>= tabStops)
6778 {
6779 // If the style only has one tab stop, Word uses it for
6780 // page number, and generates the other from defaults
6781 if (tabStops.getLength() > 1)
6782 numTab = tabStops[0];
6783 }
6784 }
6785 }
6786 }
6787 if (!numTab)
6788 {
6789 // Generate the default position.
6790 // Word uses multiples of 440 twips for default chapter number tab stops
6791 numTab.emplace();
6792 numTab->Position
6794 }
6795 }
6796 }
6797
6799 bHyperlinks, sChapterNoSeparator,
6800 aLevel, numTab);
6801 xLevelFormats->replaceByIndex( nLevel, uno::Any( aNewLevel ) );
6802 }
6803 }
6804 else // if (bTableOfFigures)
6805 {
6806 if (!sFigureSequence.isEmpty())
6807 xTOC->setPropertyValue(getPropertyName(PROP_LABEL_CATEGORY),
6808 uno::Any(sFigureSequence));
6809
6810 if (!sTemplate.isEmpty())
6811 {
6812 OUString const sConvertedStyleName(ConvertTOCStyleName(sTemplate));
6813 xTOC->setPropertyValue("CreateFromParagraphStyle", uno::Any(sConvertedStyleName));
6814 }
6815
6816 if ( bHyperlinks )
6817 {
6819 xTOC->getPropertyValue(getPropertyName(PROP_LEVEL_FORMAT)) >>= xLevelFormats;
6821 xLevelFormats->getByIndex( 1 ) >>= aLevel;
6822
6824 bHyperlinks, sChapterNoSeparator,
6825 aLevel, {});
6826 xLevelFormats->replaceByIndex( 1, uno::Any( aNewLevel ) );
6827 }
6828 }
6829}
6830
6834 const OUString & sObjectType,
6835 bool stepLeft)
6836{
6837 if (!xStart.is())
6839 if (!xEnd.is())
6841
6843 if (m_aTextAppendStack.empty())
6844 return xRet;
6845 uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
6846 if(xTextAppend.is())
6847 {
6848 try
6849 {
6851 xTextAppend->createTextCursorByRange( xStart ), uno::UNO_QUERY_THROW);
6852 //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
6853 xCursor->gotoStartOfParagraph( false );
6854 xCursor->gotoRange( xEnd, true );
6855 //the paragraph after this new section is already inserted
6856 if (stepLeft)
6857 xCursor->goLeft(1, true);
6858 uno::Reference< text::XTextContent > xSection( m_xTextFactory->createInstance(sObjectType), uno::UNO_QUERY_THROW );
6859 try
6860 {
6861 xSection->attach( uno::Reference< text::XTextRange >( xCursor, uno::UNO_QUERY_THROW) );
6862 }
6863 catch(const uno::Exception&)
6864 {
6865 }
6866 xRet.set(xSection, uno::UNO_QUERY );
6867 }
6868 catch(const uno::Exception&)
6869 {
6870 }
6871 }
6872
6873 return xRet;
6874}
6875
6877 (const FieldContextPtr& pContext,
6878 const OUString & sTOCServiceName)
6879{
6880 if (m_aTextAppendStack.empty())
6881 {
6882 // tdf#130214: a workaround to avoid crash on import errors
6883 SAL_WARN("writerfilter.dmapper", "no text append stack");
6884 return;
6885 }
6886 // Create section before setting m_bStartTOC and m_bStartBibliography: finishing paragraph
6887 // inside StartIndexSectionChecked could do the wrong thing otherwise
6888 const auto xTOC = StartIndexSectionChecked(sTOCServiceName);
6889 m_bStartTOC = true;
6890 m_bStartBibliography = true;
6891
6892 if (xTOC.is())
6893 xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::Any(OUString()));
6894
6895 pContext->SetTOC( xTOC );
6896 m_bParaHadField = false;
6897
6898 uno::Reference< text::XTextContent > xToInsert( xTOC, uno::UNO_QUERY );
6900}
6901
6903 (const FieldContextPtr& pContext,
6904 const OUString & sTOCServiceName)
6905{
6906 // only UserIndex can handle user index defined by \f
6907 // e.g. INDEX \f "user-index-id"
6908 OUString sUserIndex;
6909 if ( lcl_FindInCommand( pContext->GetCommand(), 'f', sUserIndex ) )
6910 sUserIndex = lcl_trim(sUserIndex);
6911
6912 // Create section before setting m_bStartTOC and m_bStartIndex: finishing paragraph
6913 // inside StartIndexSectionChecked could do the wrong thing otherwise
6914 const auto xTOC = StartIndexSectionChecked( sUserIndex.isEmpty()
6915 ? sTOCServiceName
6916 : "com.sun.star.text.UserIndex");
6917
6918 m_bStartTOC = true;
6919 m_bStartIndex = true;
6920 OUString sValue;
6921 if (xTOC.is())
6922 {
6923 xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::Any(OUString()));
6924
6925 if( lcl_FindInCommand( pContext->GetCommand(), 'r', sValue ))
6926 {
6927 xTOC->setPropertyValue("IsCommaSeparated", uno::Any(true));
6928 }
6929 if( lcl_FindInCommand( pContext->GetCommand(), 'h', sValue ))
6930 {
6931 xTOC->setPropertyValue("UseAlphabeticalSeparators", uno::Any(true));
6932 }
6933 if( !sUserIndex.isEmpty() )
6934 {
6935 xTOC->setPropertyValue("UserIndexName", uno::Any(sUserIndex));
6936 }
6937 }
6938 pContext->SetTOC( xTOC );
6939 m_bParaHadField = false;
6940
6941 uno::Reference< text::XTextContent > xToInsert( xTOC, uno::UNO_QUERY );
6943
6944 if( lcl_FindInCommand( pContext->GetCommand(), 'c', sValue ))
6945 {
6946 sValue = sValue.replaceAll("\"", "");
6948 if (xTOC.is())
6949 {
6950 xTOC->getPropertyValue(getPropertyName( PROP_TEXT_COLUMNS )) >>= xTextColumns;
6951 }
6952 if (xTextColumns.is())
6953 {
6954 xTextColumns->setColumnCount( sValue.toInt32() );
6955 xTOC->setPropertyValue( getPropertyName( PROP_TEXT_COLUMNS ), uno::Any( xTextColumns ) );
6956 }
6957 }
6958}
6959
6960static auto InsertFieldmark(std::stack<TextAppendContext> & rTextAppendStack,
6961 uno::Reference<text::XFormField> const& xFormField,
6962 uno::Reference<text::XTextRange> const& xStartRange,
6963 std::optional<FieldId> const oFieldId) -> void
6964{
6965 uno::Reference<text::XTextContent> const xTextContent(xFormField, uno::UNO_QUERY_THROW);
6966 uno::Reference<text::XTextAppend> const& xTextAppend(rTextAppendStack.top().xTextAppend);
6967 uno::Reference<text::XTextCursor> const xCursor =
6968 xTextAppend->createTextCursorByRange(xStartRange);
6969 if (rTextAppendStack.top().xInsertPosition.is())
6970 {
6972 rTextAppendStack.top().xTextAppend,
6973 uno::UNO_QUERY);
6974 if (xCompare->compareRegionStarts(xStartRange, rTextAppendStack.top().xInsertPosition) < 0)
6975 {
6976 SAL_WARN("writerfilter.dmapper", "invalid field mark positions");
6977 assert(false);
6978 }
6979 xCursor->gotoRange(rTextAppendStack.top().xInsertPosition, true);
6980 }
6981 else
6982 {
6983 xCursor->gotoEnd(true);
6984 }
6985 xTextAppend->insertTextContent(xCursor, xTextContent, true);
6986 if (oFieldId
6987 && (oFieldId == FIELD_FORMCHECKBOX || oFieldId == FIELD_FORMDROPDOWN))
6988 {
6989 return; // only a single CH_TXT_ATR_FORMELEMENT!
6990 }
6991 // problem: the fieldmark must be inserted in CloseFieldCommand(), because
6992 // attach() takes 2 positions, not 3!
6993 // FAIL: AppendTextNode() ignores the content index!
6994 // plan B: insert a spurious paragraph break now and join
6995 // it in PopFieldContext()!
6996 xCursor->gotoRange(xTextContent->getAnchor()->getEnd(), false);
6997 xCursor->goLeft(1, false); // skip CH_TXT_ATR_FIELDEND
6998 xTextAppend->insertControlCharacter(xCursor, text::ControlCharacter::PARAGRAPH_BREAK, false);
6999 xCursor->goLeft(1, false); // back to previous paragraph
7000 rTextAppendStack.push(TextAppendContext(xTextAppend, xCursor));
7001}
7002
7003static auto PopFieldmark(std::stack<TextAppendContext> & rTextAppendStack,
7004 uno::Reference<text::XTextCursor> const& xCursor,
7005 std::optional<FieldId> const oFieldId) -> void
7006{
7007 if (oFieldId
7008 && (oFieldId == FIELD_FORMCHECKBOX || oFieldId == FIELD_FORMDROPDOWN))
7009 {
7010 return; // only a single CH_TXT_ATR_FORMELEMENT!
7011 }
7012 xCursor->gotoRange(rTextAppendStack.top().xInsertPosition, false);
7013 xCursor->goRight(1, true);
7014 xCursor->setString(OUString()); // undo SplitNode from CloseFieldCommand()
7015 // note: paragraph properties will be overwritten
7016 // by finishParagraph() anyway so ignore here
7017 rTextAppendStack.pop();
7018}
7019
7021{
7023 return;
7024#ifdef DBG_UTIL
7025 TagLogger::getInstance().element("closeFieldCommand");
7026#endif
7027
7028 FieldContextPtr pContext;
7029 if(!m_aFieldStack.empty())
7030 pContext = m_aFieldStack.back();
7031 OSL_ENSURE( pContext, "no field context available");
7032 if( !pContext )
7033 return;
7034
7035 m_bSetUserFieldContent = false;
7036 m_bSetCitation = false;
7037 m_bSetDateValue = false;
7038 // tdf#124472: If the normal command line is not empty, use it,
7039 // otherwise, the last active row is evaluated.
7040 if (!pContext->GetCommandIsEmpty(false))
7041 pContext->SetCommandType(false);
7042
7043 const FieldConversionMap_t& aFieldConversionMap = lcl_GetFieldConversion();
7044
7045 try
7046 {
7047 uno::Reference< uno::XInterface > xFieldInterface;
7048
7049 const auto& [sType, vArguments, vSwitches]{ splitFieldCommand(pContext->GetCommand()) };
7050 (void)vSwitches;
7051 OUString const sFirstParam(vArguments.empty() ? OUString() : vArguments.front());
7052
7053 // apply font size to the form control
7055 {
7056 uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
7057 if (xTextAppend.is())
7058 {
7059 uno::Reference< text::XTextCursor > xCrsr = xTextAppend->getText()->createTextCursor();
7060 if (xCrsr.is())
7061 {
7062 xCrsr->gotoEnd(false);
7063 uno::Reference< beans::XPropertySet > xProp( xCrsr, uno::UNO_QUERY );
7065 {
7066 xProp->setPropertyValue(getPropertyName(PROP_CHAR_HEIGHT), m_pLastCharacterContext->getProperty(PROP_CHAR_HEIGHT)->second);
7068 xProp->setPropertyValue(getPropertyName(PROP_CHAR_HEIGHT_COMPLEX), m_pLastCharacterContext->getProperty(PROP_CHAR_HEIGHT_COMPLEX)->second);
7069 }
7071 xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME), m_pLastCharacterContext->getProperty(PROP_CHAR_FONT_NAME)->second);
7072 }
7073 }
7074 }
7075
7076 FieldConversionMap_t::const_iterator const aIt = aFieldConversionMap.find(sType);
7077 if (aIt != aFieldConversionMap.end()
7079 // these need to convert ffData to properties...
7080 || (aIt->second.eFieldId == FIELD_FORMCHECKBOX)
7081 || (aIt->second.eFieldId == FIELD_FORMDROPDOWN)
7082 || (aIt->second.eFieldId == FIELD_FORMTEXT)))
7083 {
7084 pContext->SetFieldId(aIt->second.eFieldId);
7085 bool bCreateEnhancedField = false;
7087 bool bCreateField = true;
7088 switch (aIt->second.eFieldId)
7089 {
7090 case FIELD_HYPERLINK:
7091 case FIELD_DOCPROPERTY:
7092 case FIELD_TOC:
7093 case FIELD_INDEX:
7094 case FIELD_XE:
7095 case FIELD_BIBLIOGRAPHY:
7096 case FIELD_CITATION:
7097 case FIELD_TC:
7098 case FIELD_EQ:
7100 case FIELD_SYMBOL:
7101 case FIELD_GOTOBUTTON:
7102 bCreateField = false;
7103 break;
7104 case FIELD_FORMCHECKBOX :
7105 case FIELD_FORMTEXT :
7106 case FIELD_FORMDROPDOWN :
7107 {
7108 // If we use 'enhanced' fields then FIELD_FORMCHECKBOX,
7109 // FIELD_FORMTEXT & FIELD_FORMDROPDOWN are treated specially
7111 {
7112 bCreateField = false;
7113 bCreateEnhancedField = true;
7114 }
7115 // for non enhanced fields checkboxes are displayed
7116 // as an awt control not a field
7117 else if ( aIt->second.eFieldId == FIELD_FORMCHECKBOX )
7118 bCreateField = false;
7119 break;
7120 }
7121 default:
7122 {
7124 if (pOuter)
7125 {
7126 if (!IsFieldNestingAllowed(pOuter, m_aFieldStack.back()))
7127 {
7128 // Parent field can't host this child field: don't create a child field
7129 // in this case.
7130 bCreateField = false;
7131 }
7132 }
7133 break;
7134 }
7135 }
7136 if (IsInTOC() && (aIt->second.eFieldId == FIELD_PAGEREF))
7137 {
7138 bCreateField = false;
7139 }
7140
7141 if( bCreateField || bCreateEnhancedField )
7142 {
7143 //add the service prefix
7144 OUString sServiceName("com.sun.star.text.");
7145 if ( bCreateEnhancedField )
7146 {
7147 const FieldConversionMap_t& aEnhancedFieldConversionMap = lcl_GetEnhancedFieldConversion();
7148 FieldConversionMap_t::const_iterator aEnhancedIt =
7149 aEnhancedFieldConversionMap.find(sType);
7150 if ( aEnhancedIt != aEnhancedFieldConversionMap.end())
7151 sServiceName += OUString::createFromAscii(aEnhancedIt->second.cFieldServiceName );
7152 }
7153 else
7154 {
7155 sServiceName += "TextField." + OUString::createFromAscii(aIt->second.cFieldServiceName );
7156 }
7157
7158#ifdef DBG_UTIL
7159 TagLogger::getInstance().startElement("fieldService");
7162#endif
7163
7164 if (m_xTextFactory.is())
7165 {
7166 xFieldInterface = m_xTextFactory->createInstance(sServiceName);
7167 xFieldProperties.set( xFieldInterface, uno::UNO_QUERY_THROW);
7168 }
7169 }
7170 switch( aIt->second.eFieldId )
7171 {
7172 case FIELD_ADDRESSBLOCK: break;
7173 case FIELD_ADVANCE : break;
7174 case FIELD_ASK :
7175 handleFieldAsk(pContext, xFieldInterface, xFieldProperties);
7176 break;
7177 case FIELD_AUTONUM :
7178 case FIELD_AUTONUMLGL :
7179 case FIELD_AUTONUMOUT :
7180 handleAutoNum(pContext, xFieldInterface, xFieldProperties);
7181 break;
7182 case FIELD_AUTHOR :
7183 case FIELD_USERNAME :
7184 case FIELD_USERINITIALS :
7185 handleAuthor(sFirstParam,
7186 xFieldProperties,
7187 aIt->second.eFieldId);
7188 break;
7189 case FIELD_DATE:
7190 if (xFieldProperties.is())
7191 {
7192 // Get field fixed property from the context handler
7193 if (pContext->IsFieldLocked())
7194 {
7195 xFieldProperties->setPropertyValue(
7197 uno::Any( true ));
7198 m_bSetDateValue = true;
7199 }
7200 else
7201 xFieldProperties->setPropertyValue(
7203 uno::Any( false ));
7204
7205 xFieldProperties->setPropertyValue(
7207 uno::Any( true ));
7208 SetNumberFormat( pContext->GetCommand(), xFieldProperties );
7209 }
7210 break;
7211 case FIELD_COMMENTS :
7212 {
7213 // OUString sParam = lcl_ExtractParameter(pContext->GetCommand(), sizeof(" COMMENTS") );
7214 // A parameter with COMMENTS shouldn't set fixed
7215 // ( or at least the binary filter doesn't )
7216 // If we set fixed then we won't export a field cmd.
7217 // Additionally the para in COMMENTS is more like an
7218 // instruction to set the document property comments
7219 // with the param ( e.g. each COMMENT with a param will
7220 // overwrite the Comments document property
7221 // #TODO implement the above too
7222 xFieldProperties->setPropertyValue(
7224 //PROP_CURRENT_PRESENTATION is set later anyway
7225 }
7226 break;
7227 case FIELD_CREATEDATE :
7228 case FIELD_PRINTDATE:
7229 case FIELD_SAVEDATE:
7230 {
7231 if (pContext->IsFieldLocked())
7232 {
7233 xFieldProperties->setPropertyValue(
7235 }
7236 xFieldProperties->setPropertyValue(
7238 SetNumberFormat( pContext->GetCommand(), xFieldProperties );
7239 }
7240 break;
7241 case FIELD_DOCPROPERTY :
7242 handleDocProperty(pContext, sFirstParam,
7243 xFieldInterface);
7244 break;
7245 case FIELD_DOCVARIABLE :
7246 {
7247 if (bCreateField)
7248 {
7249 //create a user field and type
7251 "com.sun.star.text.FieldMaster.User", sFirstParam);
7253 xFieldInterface, uno::UNO_QUERY_THROW);
7254 xDependentField->attachTextFieldMaster(xMaster);
7256 }
7257 }
7258 break;
7259 case FIELD_EDITTIME :
7260 //it's a numbering type, no number format! SetNumberFormat( pContext->GetCommand(), xFieldProperties );
7261 break;
7262 case FIELD_EQ:
7263 {
7264 OUString aCommand = pContext->GetCommand().trim();
7265
7267 if (!aResult.sType.isEmpty() && m_xTextFactory.is())
7268 {
7269 xFieldInterface = m_xTextFactory->createInstance("com.sun.star.text.TextField." + aResult.sType);
7270 xFieldProperties =
7272 uno::UNO_QUERY_THROW);
7273 xFieldProperties->setPropertyValue(getPropertyName(PROP_CONTENT), uno::Any(aResult.sResult));
7274 }
7275 else
7276 {
7277 //merge Read_SubF_Ruby into filter/.../util.cxx and reuse that ?
7278 sal_Int32 nSpaceIndex = aCommand.indexOf(' ');
7279 if(nSpaceIndex > 0)
7280 aCommand = o3tl::trim(aCommand.subView(nSpaceIndex));
7281 if (aCommand.startsWith("\\s"))
7282 {
7283 aCommand = aCommand.copy(2);
7284 if (aCommand.startsWith("\\do"))
7285 {
7286 aCommand = aCommand.copy(3);
7287 sal_Int32 nStartIndex = aCommand.indexOf('(');
7288 sal_Int32 nEndIndex = aCommand.indexOf(')');
7289 if (nStartIndex > 0 && nEndIndex > 0)
7290 {
7291 // nDown is the requested "lower by" value in points.
7292 sal_Int32 nDown = o3tl::toInt32(aCommand.subView(0, nStartIndex));
7293 OUString aContent = aCommand.copy(nStartIndex + 1, nEndIndex - nStartIndex - 1);
7294 PropertyMapPtr pCharContext = GetTopContext();
7295 // dHeight is the font size of the current style.
7296 double dHeight = 0;
7297 if ((GetPropertyFromParaStyleSheet(PROP_CHAR_HEIGHT) >>= dHeight) && dHeight != 0)
7298 // Character escapement should be given in negative percents for subscripts.
7299 pCharContext->Insert(PROP_CHAR_ESCAPEMENT, uno::Any( sal_Int16(- 100 * nDown / dHeight) ) );
7300 appendTextPortion(aContent, pCharContext);
7301 }
7302 }
7303 }
7304 else if (aCommand.startsWith("\\* jc"))
7305 {
7306 handleRubyEQField(pContext);
7307 }
7308 }
7309 }
7310 break;
7311 case FIELD_FILLIN :
7312 if (xFieldProperties.is())
7313 xFieldProperties->setPropertyValue(
7314 getPropertyName(PROP_HINT), uno::Any( pContext->GetCommand().getToken(1, '\"')));
7315 break;
7316 case FIELD_FILENAME:
7317 {
7318 sal_Int32 nNumberingTypeIndex = pContext->GetCommand().indexOf("\\p");
7319 if (xFieldProperties.is())
7320 xFieldProperties->setPropertyValue(
7322 uno::Any( nNumberingTypeIndex > 0 ? text::FilenameDisplayFormat::FULL : text::FilenameDisplayFormat::NAME_AND_EXT ));
7323 }
7324 break;
7325 case FIELD_FILESIZE : break;
7326 case FIELD_FORMULA :
7327 if (bCreateField)
7328 {
7329 handleFieldFormula(pContext, xFieldProperties);
7330 }
7331 break;
7332 case FIELD_FORMCHECKBOX :
7333 case FIELD_FORMDROPDOWN :
7334 case FIELD_FORMTEXT :
7335 {
7336 if (bCreateEnhancedField)
7337 {
7339 pFFDataHandler(pContext->getFFDataHandler());
7341 pFormControlHelper(new FormControlHelper
7342 (m_bUsingEnhancedFields ? aIt->second.eFieldId : FIELD_FORMCHECKBOX,
7343
7344 m_xTextDocument, pFFDataHandler));
7345 pContext->setFormControlHelper(pFormControlHelper);
7346 uno::Reference< text::XFormField > xFormField( xFieldInterface, uno::UNO_QUERY );
7347 uno::Reference< container::XNamed > xNamed( xFormField, uno::UNO_QUERY );
7348 if ( xNamed.is() )
7349 {
7350 if ( pFFDataHandler && !pFFDataHandler->getName().isEmpty() )
7351 xNamed->setName( pFFDataHandler->getName() );
7352 pContext->SetFormField( xFormField );
7353 }
7355 xFormField, pContext->GetStartRange(),
7356 pContext->GetFieldId());
7357 }
7358 else
7359 {
7360 if ( aIt->second.eFieldId == FIELD_FORMDROPDOWN )
7361 lcl_handleDropdownField( xFieldProperties, pContext->getFFDataHandler() );
7362 else
7363 lcl_handleTextField( xFieldProperties, pContext->getFFDataHandler() );
7364 }
7365 }
7366 break;
7367 case FIELD_GOTOBUTTON : break;
7368 case FIELD_HYPERLINK:
7369 {
7370 ::std::vector<OUString> aParts = pContext->GetCommandParts();
7371
7372 // Syntax is either:
7373 // HYPERLINK "" \l "link"
7374 // or
7375 // HYPERLINK \l "link"
7376 // Make sure "HYPERLINK" doesn't end up as part of link in the second case.
7377 if (!aParts.empty() && aParts[0] == "HYPERLINK")
7378 aParts.erase(aParts.begin());
7379
7380 ::std::vector<OUString>::const_iterator aItEnd = aParts.end();
7381 ::std::vector<OUString>::const_iterator aPartIt = aParts.begin();
7382
7383 OUString sURL;
7384 OUString sTarget;
7385
7386 while (aPartIt != aItEnd)
7387 {
7388 if ( *aPartIt == "\\l" )
7389 {
7390 ++aPartIt;
7391
7392 if (aPartIt == aItEnd)
7393 break;
7394
7395 sURL += "#" + *aPartIt;
7396 }
7397 else if (*aPartIt == "\\m" || *aPartIt == "\\n" || *aPartIt == "\\h")
7398 {
7399 }
7400 else if ( *aPartIt == "\\o" || *aPartIt == "\\t" )
7401 {
7402 ++aPartIt;
7403
7404 if (aPartIt == aItEnd)
7405 break;
7406
7407 sTarget = *aPartIt;
7408 }
7409 else
7410 {
7411 sURL = *aPartIt;
7412 }
7413
7414 ++aPartIt;
7415 }
7416
7417 if (!sURL.isEmpty())
7418 {
7419 if (sURL.startsWith("file:///"))
7420 {
7421 // file:///absolute\\path\\to\\file => invalid file URI (Writer cannot open)
7422 // convert all double backslashes to slashes:
7423 sURL = sURL.replaceAll("\\\\", "/");
7424
7425 // file:///absolute\path\to\file => invalid file URI (Writer cannot open)
7426 // convert all backslashes to slashes:
7427 sURL = sURL.replace('\\', '/');
7428 }
7429 // Try to make absolute any relative URLs, except
7430 // for relative same-document URLs that only contain
7431 // a fragment part:
7432 else if (!sURL.startsWith("#")) {
7433 try {
7434 sURL = rtl::Uri::convertRelToAbs(
7435 m_aBaseUrl, sURL);
7436 } catch (rtl::MalformedUriException & e) {
7437 SAL_WARN(
7438 "writerfilter.dmapper",
7439 "MalformedUriException "
7440 << e.getMessage());
7441 }
7442 }
7443 pContext->SetHyperlinkURL(sURL);
7444 }
7445
7446 if (!sTarget.isEmpty())
7447 pContext->SetHyperlinkTarget(sTarget);
7448 }
7449 break;
7450 case FIELD_IF:
7451 {
7452 if (vArguments.size() < 3)
7453 {
7454 SAL_WARN("writerfilter.dmapper", "IF field requires at least 3 parameters!");
7455 break;
7456 }
7457
7458 if (xFieldProperties.is())
7459 {
7460 // Following code assumes that last argument in field is false value
7461 // before it - true value and everything before them is a condition
7462 OUString sCondition;
7463 size_t i = 0;
7464 while (i < vArguments.size() - 2) {
7465 if (!sCondition.isEmpty())
7466 sCondition += " ";
7467 sCondition += vArguments[i++];
7468 }
7469
7470 xFieldProperties->setPropertyValue(
7471 "TrueContent", uno::Any(vArguments[vArguments.size() - 2]));
7472 xFieldProperties->setPropertyValue(
7473 "FalseContent", uno::Any(vArguments[vArguments.size() - 1]));
7474 xFieldProperties->setPropertyValue(
7475 "Condition", uno::Any(sCondition));
7476 }
7477 }
7478 break;
7479 case FIELD_INFO : break;
7480 case FIELD_INCLUDEPICTURE: break;
7481 case FIELD_KEYWORDS :
7482 {
7483 if (!sFirstParam.isEmpty())
7484 {
7485 xFieldProperties->setPropertyValue(
7487 //PROP_CURRENT_PRESENTATION is set later anyway
7488 }
7489 }
7490 break;
7491 case FIELD_LASTSAVEDBY :
7492 xFieldProperties->setPropertyValue(
7494 break;
7495 case FIELD_MACROBUTTON:
7496 {
7497 if (xFieldProperties.is())
7498 {
7499 sal_Int32 nIndex = sizeof(" MACROBUTTON ");
7500 OUString sCommand = pContext->GetCommand();
7501
7502 //extract macro name
7503 if (sCommand.getLength() >= nIndex)
7504 {
7505 OUString sMacro = sCommand.getToken(0, ' ', nIndex);
7506 xFieldProperties->setPropertyValue(
7508 }
7509
7510 //extract quick help text
7511 if (sCommand.getLength() > nIndex + 1)
7512 {
7513 xFieldProperties->setPropertyValue(
7515 uno::Any( sCommand.copy( nIndex )));
7516 }
7517 }
7518 }
7519 break;
7520 case FIELD_MERGEFIELD :
7521 {
7522 //todo: create a database field and fieldmaster pointing to a column, only
7523 //create a user field and type
7525 FindOrCreateFieldMaster("com.sun.star.text.FieldMaster.Database", sFirstParam);
7526
7527// xFieldProperties->setPropertyValue(
7528// "FieldCode",
7529// uno::makeAny( pContext->GetCommand().copy( nIndex + 1 )));
7530 uno::Reference< text::XDependentTextField > xDependentField( xFieldInterface, uno::UNO_QUERY_THROW );
7531 xDependentField->attachTextFieldMaster( xMaster );
7532 }
7533 break;
7534 case FIELD_MERGEREC : break;
7535 case FIELD_MERGESEQ : break;
7536 case FIELD_NEXT : break;
7537 case FIELD_NEXTIF : break;
7538 case FIELD_PAGE :
7539 if (xFieldProperties.is())
7540 {
7541 xFieldProperties->setPropertyValue(
7543 uno::Any( lcl_ParseNumberingType(pContext->GetCommand()) ));
7544 xFieldProperties->setPropertyValue(
7546 uno::Any( text::PageNumberType_CURRENT ));
7547 }
7548
7549 break;
7550 case FIELD_PAGEREF:
7551 case FIELD_REF:
7552 if (xFieldProperties.is() && !IsInTOC())
7553 {
7554 bool bPageRef = aIt->second.eFieldId == FIELD_PAGEREF;
7555
7556 // Do we need a GetReference (default) or a GetExpression field?
7557 uno::Reference< text::XTextFieldsSupplier > xFieldsSupplier( GetTextDocument(), uno::UNO_QUERY );
7558 uno::Reference< container::XNameAccess > xFieldMasterAccess = xFieldsSupplier->getTextFieldMasters();
7559
7560 if (!xFieldMasterAccess->hasByName(
7561 "com.sun.star.text.FieldMaster.SetExpression."
7562 + sFirstParam))
7563 {
7564 xFieldProperties->setPropertyValue(
7566 uno::Any( sal_Int16(text::ReferenceFieldSource::BOOKMARK)) );
7567 xFieldProperties->setPropertyValue(
7569 uno::Any(sFirstParam) );
7570 sal_Int16 nFieldPart = (bPageRef ? text::ReferenceFieldPart::PAGE : text::ReferenceFieldPart::TEXT);
7571 OUString sValue;
7572 if( lcl_FindInCommand( pContext->GetCommand(), 'p', sValue ))
7573 {
7574 //above-below
7575 nFieldPart = text::ReferenceFieldPart::UP_DOWN;
7576 }
7577 else if( lcl_FindInCommand( pContext->GetCommand(), 'r', sValue ))
7578 {
7579 //number
7580 nFieldPart = text::ReferenceFieldPart::NUMBER;
7581 }
7582 else if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
7583 {
7584 //number-no-context
7585 nFieldPart = text::ReferenceFieldPart::NUMBER_NO_CONTEXT;
7586 }
7587 else if( lcl_FindInCommand( pContext->GetCommand(), 'w', sValue ))
7588 {
7589 //number-full-context
7590 nFieldPart = text::ReferenceFieldPart::NUMBER_FULL_CONTEXT;
7591 }
7592 xFieldProperties->setPropertyValue(
7594 }
7595 else if( m_xTextFactory.is() )
7596 {
7597 xFieldInterface = m_xTextFactory->createInstance("com.sun.star.text.TextField.GetExpression");
7598 xFieldProperties.set(xFieldInterface, uno::UNO_QUERY);
7599 xFieldProperties->setPropertyValue(
7601 uno::Any(sFirstParam));
7602 xFieldProperties->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::Any(text::SetVariableType::STRING));
7603 }
7604 }
7605 break;
7606 case FIELD_REVNUM : break;
7607 case FIELD_SECTION : break;
7608 case FIELD_SECTIONPAGES : break;
7609 case FIELD_SEQ :
7610 {
7611 // command looks like: " SEQ Table \* ARABIC "
7612 OUString sCmd(pContext->GetCommand());
7613 // find the sequence name, e.g. "SEQ"
7614 std::u16string_view sSeqName = msfilter::util::findQuotedText(sCmd, u"SEQ ", '\\');
7615 sSeqName = o3tl::trim(sSeqName);
7616
7617 // create a sequence field master using the sequence name
7619 "com.sun.star.text.FieldMaster.SetExpression",
7620 OUString(sSeqName));
7621
7622 xMaster->setPropertyValue(
7624 uno::Any(text::SetVariableType::SEQUENCE));
7625
7626 // apply the numbering type
7627 xFieldProperties->setPropertyValue(
7629 uno::Any( lcl_ParseNumberingType(pContext->GetCommand()) ));
7630
7631 // attach the master to the field
7632 uno::Reference< text::XDependentTextField > xDependentField( xFieldInterface, uno::UNO_QUERY_THROW );
7633 xDependentField->attachTextFieldMaster( xMaster );
7634
7635 OUString sFormula = OUString::Concat(sSeqName) + "+1";
7636 OUString sValue;
7637 if( lcl_FindInCommand( pContext->GetCommand(), 'c', sValue ))
7638 {
7639 sFormula = sSeqName;
7640 }
7641 else if( lcl_FindInCommand( pContext->GetCommand(), 'r', sValue ))
7642 {
7643 sFormula = sValue;
7644 }
7645 // TODO \s isn't handled, but the spec isn't easy to understand without
7646 // an example for this one.
7647 xFieldProperties->setPropertyValue(
7650
7651 // Take care of the numeric formatting definition, default is Arabic
7652 sal_Int16 nNumberingType = lcl_ParseNumberingType(pContext->GetCommand());
7653 if (nNumberingType == style::NumberingType::PAGE_DESCRIPTOR)
7654 nNumberingType = style::NumberingType::ARABIC;
7655 xFieldProperties->setPropertyValue(
7657 uno::Any(nNumberingType));
7658 }
7659 break;
7660 case FIELD_SET :
7661 handleFieldSet(pContext, xFieldInterface, xFieldProperties);
7662 break;
7663 case FIELD_SKIPIF : break;
7664 case FIELD_STYLEREF : break;
7665 case FIELD_SUBJECT :
7666 {
7667 if (!sFirstParam.isEmpty())
7668 {
7669 xFieldProperties->setPropertyValue(
7671 //PROP_CURRENT_PRESENTATION is set later anyway
7672 }
7673 }
7674 break;
7675 case FIELD_SYMBOL:
7676 {
7677 uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
7678 OUString sSymbol( sal_Unicode( sFirstParam.startsWithIgnoreAsciiCase("0x") ? o3tl::toUInt32(sFirstParam.subView(2),16) : sFirstParam.toUInt32() ) );
7679 OUString sFont;
7680 bool bHasFont = lcl_FindInCommand( pContext->GetCommand(), 'f', sFont);
7681 if ( bHasFont )
7682 {
7683 sFont = sFont.trim();
7684 if (sFont.startsWith("\""))
7685 sFont = sFont.copy(1);
7686 if (sFont.endsWith("\""))
7687 sFont = sFont.copy(0,sFont.getLength()-1);
7688 }
7689
7690
7691
7692 if (xTextAppend.is())
7693 {
7694 uno::Reference< text::XText > xText = xTextAppend->getText();
7695 uno::Reference< text::XTextCursor > xCrsr = xText->createTextCursor();
7696 if (xCrsr.is())
7697 {
7698 xCrsr->gotoEnd(false);
7699 xText->insertString(xCrsr, sSymbol, true);
7700 uno::Reference< beans::XPropertySet > xProp( xCrsr, uno::UNO_QUERY );
7701 xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_CHAR_SET), uno::Any(awt::CharSet::SYMBOL));
7702 if(bHasFont)
7703 {
7704 uno::Any aVal( sFont );
7705 xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME), aVal);
7706 xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME_ASIAN), aVal);
7707 xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME_COMPLEX), aVal);
7708
7709 }
7710 }
7711 }
7712 }
7713 break;
7714 case FIELD_TEMPLATE: break;
7715 case FIELD_TIME :
7716 {
7717 if (pContext->IsFieldLocked())
7718 {
7719 xFieldProperties->setPropertyValue(
7721 uno::Any( true ));
7722 m_bSetDateValue = true;
7723 }
7724 SetNumberFormat( pContext->GetCommand(), xFieldProperties );
7725 }
7726 break;
7727 case FIELD_TITLE :
7728 {
7729 if (!sFirstParam.isEmpty())
7730 {
7731 xFieldProperties->setPropertyValue(
7733 //PROP_CURRENT_PRESENTATION is set later anyway
7734 }
7735 }
7736 break;
7737 case FIELD_USERADDRESS : //todo: user address collects street, city ...
7738 break;
7739 case FIELD_INDEX:
7740 handleIndex(pContext,
7741 OUString::createFromAscii(aIt->second.cFieldServiceName));
7742 break;
7743 case FIELD_BIBLIOGRAPHY:
7744 handleBibliography(pContext,
7745 OUString::createFromAscii(aIt->second.cFieldServiceName));
7746 break;
7747 case FIELD_TOC:
7748 handleToc(pContext,
7749 OUString::createFromAscii(aIt->second.cFieldServiceName));
7750 break;
7751 case FIELD_XE:
7752 {
7753 if( !m_xTextFactory.is() )
7754 break;
7755
7756 // only UserIndexMark can handle user index types defined by \f
7757 // e.g. XE "text" \f "user-index-id"
7758 OUString sUserIndex;
7759 OUString sFieldServiceName =
7760 lcl_FindInCommand( pContext->GetCommand(), 'f', sUserIndex )
7761 ? "com.sun.star.text.UserIndexMark"
7762 : OUString::createFromAscii(aIt->second.cFieldServiceName);
7764 m_xTextFactory->createInstance(sFieldServiceName),
7765 uno::UNO_QUERY_THROW);
7766
7767 if (!sFirstParam.isEmpty())
7768 {
7769 xTC->setPropertyValue(sUserIndex.isEmpty()
7770 ? OUString("PrimaryKey")
7771 : OUString("AlternativeText"),
7772 uno::Any(sFirstParam));
7773 }
7774
7775 sUserIndex = lcl_trim(sUserIndex);
7776 if (!sUserIndex.isEmpty())
7777 {
7778 xTC->setPropertyValue("UserIndexName",
7779 uno::Any(sUserIndex));
7780 }
7781 uno::Reference< text::XTextContent > xToInsert( xTC, uno::UNO_QUERY );
7782 uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
7783 if (xTextAppend.is())
7784 {
7785 uno::Reference< text::XText > xText = xTextAppend->getText();
7786 uno::Reference< text::XTextCursor > xCrsr = xText->createTextCursor();
7787 if (xCrsr.is())
7788 {
7789 xCrsr->gotoEnd(false);
7790 xText->insertTextContent(uno::Reference< text::XTextRange >( xCrsr, uno::UNO_QUERY_THROW ), xToInsert, false);
7791 }
7792 }
7793 }
7794 break;
7795 case FIELD_CITATION:
7796 {
7797 if( !m_xTextFactory.is() )
7798 break;
7799
7800 xFieldInterface = m_xTextFactory->createInstance(
7801 OUString::createFromAscii(aIt->second.cFieldServiceName));
7802 uno::Reference< beans::XPropertySet > xTC(xFieldInterface,
7803 uno::UNO_QUERY_THROW);
7804 OUString sCmd(pContext->GetCommand());//sCmd is the entire instrText including the index e.g. CITATION Kra06 \l 1033
7805 if( !sCmd.isEmpty()){
7807 { "Identifier", uno::Any(sCmd) }
7808 }));
7809 xTC->setPropertyValue("Fields", uno::Any(aValues));
7810 }
7811 uno::Reference< text::XTextContent > xToInsert( xTC, uno::UNO_QUERY );
7812
7814 = m_aFieldStack.back()->getProperties()->GetPropertyValues();
7815 appendTextContent(xToInsert, aValues);
7816 m_bSetCitation = true;
7817 }
7818 break;
7819
7820 case FIELD_TC :
7821 {
7822 if( !m_xTextFactory.is() )
7823 break;
7824
7826 m_xTextFactory->createInstance(
7827 OUString::createFromAscii(aIt->second.cFieldServiceName)),
7828 uno::UNO_QUERY_THROW);
7829 if (!sFirstParam.isEmpty())
7830 {
7831 xTC->setPropertyValue(getPropertyName(PROP_ALTERNATIVE_TEXT),
7832 uno::Any(sFirstParam));
7833 }
7834 OUString sValue;
7835 // \f TC entry in doc with multiple tables
7836// if( lcl_FindInCommand( pContext->GetCommand(), 'f', sValue ))
7837// {
7838 // todo: unsupported
7839// }
7840 if( lcl_FindInCommand( pContext->GetCommand(), 'l', sValue ))
7841 // \l Outline Level
7842 {
7843 sal_Int32 nLevel = sValue.toInt32();
7844 if( !sValue.isEmpty() && nLevel >= 0 && nLevel <= 10 )
7845 xTC->setPropertyValue(getPropertyName(PROP_LEVEL), uno::Any( static_cast<sal_Int16>(nLevel) ));
7846 }
7847// if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
7848// \n Suppress page numbers
7849// {
7850 //todo: unsupported feature
7851// }
7852 pContext->SetTC( xTC );
7853 }
7854 break;
7855 case FIELD_NUMCHARS:
7856 case FIELD_NUMWORDS:
7857 case FIELD_NUMPAGES:
7858 if (xFieldProperties.is())
7859 xFieldProperties->setPropertyValue(
7861 uno::Any( lcl_ParseNumberingType(pContext->GetCommand()) ));
7862 break;
7863 }
7864
7865 if (!bCreateEnhancedField)
7866 {
7867 pContext->SetTextField( uno::Reference<text::XTextField>(xFieldInterface, uno::UNO_QUERY) );
7868 }
7869 }
7870 else
7871 {
7872 /* Unsupported fields will be handled here for docx file.
7873 * To handle unsupported fields used fieldmark API.
7874 */
7875 OUString aCode( pContext->GetCommand().trim() );
7876 // Don't waste resources on wrapping shapes inside a fieldmark.
7877 if (sType != "SHAPE" && m_xTextFactory.is() && !m_aTextAppendStack.empty())
7878 {
7879 xFieldInterface = m_xTextFactory->createInstance("com.sun.star.text.Fieldmark");
7880
7881 uno::Reference<text::XFormField> const xFormField(xFieldInterface, uno::UNO_QUERY);
7882 InsertFieldmark(m_aTextAppendStack, xFormField, pContext->GetStartRange(),
7883 pContext->GetFieldId());
7884 xFormField->setFieldType(ODF_UNHANDLED);
7886 pContext->SetFormField( xFormField );
7887 uno::Reference<container::XNameContainer> const xNameCont(xFormField->getParameters());
7888 // note: setting the code to empty string is *required* in
7889 // m_bForceGenericFields mode, or the export will write
7890 // the ODF_UNHANDLED string!
7891 assert(!m_bForceGenericFields || aCode.isEmpty());
7892 xNameCont->insertByName(ODF_CODE_PARAM, uno::Any(aCode));
7893 ww::eField const id(GetWW8FieldId(sType));
7894 if (id != ww::eNONE)
7895 { // tdf#129247 tdf#134264 set WW8 id for WW8 export
7896 xNameCont->insertByName(ODF_ID_PARAM, uno::Any(OUString::number(id)));
7897 }
7898 }
7899 else
7900 m_bParaHadField = false;
7901 }
7902 }
7903 catch( const uno::Exception& )
7904 {
7905 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "Exception in CloseFieldCommand()" );
7906 }
7907 pContext->SetCommandCompleted();
7908}
7909/*-------------------------------------------------------------------------
7910//the _current_ fields require a string type result while TOCs accept richt results
7911 -----------------------------------------------------------------------*/
7913{
7914 bool bRet = false;
7915 OSL_ENSURE( !m_aFieldStack.empty(), "field stack empty?");
7916 FieldContextPtr pContext = m_aFieldStack.back();
7917 OSL_ENSURE( pContext, "no field context available");
7918 if( pContext )
7919 {
7920 bRet = pContext->GetTextField().is()
7921 || pContext->GetFieldId() == FIELD_FORMDROPDOWN
7922 || pContext->GetFieldId() == FIELD_FILLIN;
7923 }
7924
7925 if (!bRet)
7926 {
7928 if (pOuter)
7929 {
7930 if (!IsFieldNestingAllowed(pOuter, m_aFieldStack.back()))
7931 {
7932 // If nesting is not allowed, then the result can only be a string.
7933 bRet = true;
7934 }
7935 }
7936 }
7937 return bRet;
7938}
7939
7940void DomainMapper_Impl::AppendFieldResult(std::u16string_view rString)
7941{
7942 assert(!m_aFieldStack.empty());
7943 FieldContextPtr pContext = m_aFieldStack.back();
7944 SAL_WARN_IF(!pContext, "writerfilter.dmapper", "no field context");
7945 if (!pContext)
7946 return;
7947
7949 if (pOuter)
7950 {
7951 if (!IsFieldNestingAllowed(pOuter, pContext))
7952 {
7953 if (pOuter->IsCommandCompleted())
7954 {
7955 // Child can't host the field result, forward to parent's result.
7956 pOuter->AppendResult(rString);
7957 }
7958 return;
7959 }
7960 }
7961
7962 pContext->AppendResult(rString);
7963}
7964
7965// Calculates css::DateTime based on ddddd.sssss since 1899-12-30
7966static util::DateTime lcl_dateTimeFromSerial(const double& dSerial)
7967{
7968 DateTime d(Date(30, 12, 1899));
7969 d.AddTime(dSerial);
7970 return d.GetUNODateTime();
7971}
7972
7973void DomainMapper_Impl::SetFieldResult(OUString const& rResult)
7974{
7975#ifdef DBG_UTIL
7976 TagLogger::getInstance().startElement("setFieldResult");
7977 TagLogger::getInstance().chars(rResult);
7978#endif
7979
7980 FieldContextPtr pContext = m_aFieldStack.back();
7981 OSL_ENSURE( pContext, "no field context available");
7982
7983 if (m_aFieldStack.size() > 1)
7984 {
7985 // This is a nested field. See if the parent supports nesting on the Writer side.
7986 FieldContextPtr pParentContext = m_aFieldStack[m_aFieldStack.size() - 2];
7987 if (pParentContext)
7988 {
7989 std::vector<OUString> aParentParts = pParentContext->GetCommandParts();
7990 // Conditional text fields don't support nesting in Writer.
7991 if (!aParentParts.empty() && aParentParts[0] == "IF")
7992 {
7993 return;
7994 }
7995 }
7996 }
7997
7998 if( !pContext )
7999 return;
8000
8001 uno::Reference<text::XTextField> xTextField = pContext->GetTextField();
8002 try
8003 {
8004 OSL_ENSURE( xTextField.is()
8005 //||m_xTOC.is() ||m_xTC.is()
8006 //||m_sHyperlinkURL.getLength()
8007 , "DomainMapper_Impl::SetFieldResult: field not created" );
8008 if(xTextField.is())
8009 {
8010 try
8011 {
8013 {
8014 // user field content has to be set at the field master
8015 uno::Reference< text::XDependentTextField > xDependentField( xTextField, uno::UNO_QUERY_THROW );
8016 xDependentField->getTextFieldMaster()->setPropertyValue(
8018 uno::Any( rResult ));
8019 }
8020 else if ( m_bSetCitation )
8021 {
8022
8023 uno::Reference< beans::XPropertySet > xFieldProperties( xTextField, uno::UNO_QUERY_THROW);
8024 // In case of SetExpression, the field result contains the content of the variable.
8025 uno::Reference<lang::XServiceInfo> xServiceInfo(xTextField, uno::UNO_QUERY);
8026
8027 bool bIsSetbiblio = xServiceInfo->supportsService("com.sun.star.text.TextField.Bibliography");
8028 if( bIsSetbiblio )
8029 {
8030 uno::Any aProperty = xFieldProperties->getPropertyValue("Fields");
8032 aProperty >>= aValues;
8033 beans::PropertyValue propertyVal;
8034 sal_Int32 nTitleFoundIndex = -1;
8035 for (sal_Int32 i = 0; i < aValues.getLength(); ++i)
8036 {
8037 propertyVal = aValues[i];
8038 if (propertyVal.Name == "Title")
8039 {
8040 nTitleFoundIndex = i;
8041 break;
8042 }
8043 }
8044 if (nTitleFoundIndex != -1)
8045 {
8046 OUString titleStr;
8047 uno::Any aValue(propertyVal.Value);
8048 aValue >>= titleStr;
8049 titleStr += rResult;
8050 propertyVal.Value <<= titleStr;
8051 aValues.getArray()[nTitleFoundIndex] = propertyVal;
8052 }
8053 else
8054 {
8055 aValues.realloc(aValues.getLength() + 1);
8056 propertyVal.Name = "Title";
8057 propertyVal.Value <<= rResult;
8058 aValues.getArray()[aValues.getLength() - 1] = propertyVal;
8059 }
8060 xFieldProperties->setPropertyValue("Fields",
8061 uno::Any(aValues));
8062 }
8063 }
8064 else if ( m_bSetDateValue )
8065 {
8066 uno::Reference< util::XNumberFormatsSupplier > xNumberSupplier( m_xTextDocument, uno::UNO_QUERY_THROW );
8067
8068 uno::Reference<util::XNumberFormatter> xFormatter(util::NumberFormatter::create(m_xComponentContext), uno::UNO_QUERY_THROW);
8069 xFormatter->attachNumberFormatsSupplier( xNumberSupplier );
8070 sal_Int32 nKey = 0;
8071
8072 uno::Reference< beans::XPropertySet > xFieldProperties( xTextField, uno::UNO_QUERY_THROW);
8073
8074 xFieldProperties->getPropertyValue( "NumberFormat" ) >>= nKey;
8075 xFieldProperties->setPropertyValue(
8076 "DateTimeValue",
8077 uno::Any( lcl_dateTimeFromSerial( xFormatter->convertStringToNumber( nKey, rResult ) ) ) );
8078 }
8079 else
8080 {
8081 uno::Reference< beans::XPropertySet > xFieldProperties( xTextField, uno::UNO_QUERY_THROW);
8082 // In case of SetExpression, and Input fields the field result contains the content of the variable.
8083 uno::Reference<lang::XServiceInfo> xServiceInfo(xTextField, uno::UNO_QUERY);
8084 // there are fields with a content property, which aren't working correctly with
8085 // a generalized try catch of the content, property, so just restrict content
8086 // handling to these explicit services.
8087 const bool bHasContent = xServiceInfo->supportsService("com.sun.star.text.TextField.SetExpression") ||
8088 xServiceInfo->supportsService("com.sun.star.text.TextField.Input");
8089 // If we already have content set, then use the current presentation
8090 OUString sValue;
8091 if (bHasContent)
8092 {
8093 // this will throw for field types without Content
8094 uno::Any aValue(xFieldProperties->getPropertyValue(
8096 aValue >>= sValue;
8097 }
8098 xFieldProperties->setPropertyValue(
8099 getPropertyName(bHasContent && sValue.isEmpty()? PROP_CONTENT : PROP_CURRENT_PRESENTATION),
8100 uno::Any( rResult ));
8101
8102 // LO always automatically updates a DocInfo field from the File-Properties-Custom Prop
8103 // while MS Word requires the user to manually refresh the field (with F9).
8104 // In other words, Word lets the field to be out of sync with the controlling variable.
8105 // Marking as FIXEDFLD solves the automatic replacement problem, but of course prevents
8106 // Writer from making any changes, even on an F9 refresh.
8107 OUString sVariable = pContext->GetVariableValue();
8108 if (rResult.getLength() != sVariable.getLength())
8109 {
8110 sal_Int32 nLen = sVariable.indexOf('\x0');
8111 if (nLen >= 0)
8112 sVariable = sVariable.copy(0, nLen);
8113 }
8114 bool bCustomFixedField = rResult != sVariable &&
8115 xServiceInfo->supportsService("com.sun.star.text.TextField.DocInfo.Custom");
8116
8117 if (bCustomFixedField || xServiceInfo->supportsService(
8118 "com.sun.star.text.TextField.DocInfo.CreateDateTime"))
8119 {
8120 // Creation time is const, don't try to update it.
8121 xFieldProperties->setPropertyValue("IsFixed", uno::Any(true));
8122 }
8123 }
8124 }
8125 catch( const beans::UnknownPropertyException& )
8126 {
8127 //some fields don't have a CurrentPresentation (DateTime)
8128 }
8129 }
8130 }
8131 catch (const uno::Exception&)
8132 {
8133 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "DomainMapper_Impl::SetFieldResult");
8134 }
8135}
8136
8138{
8139#ifdef DBG_UTIL
8140 TagLogger::getInstance().startElement("setFieldFFData");
8141#endif
8142
8143 if (!m_aFieldStack.empty())
8144 {
8145 FieldContextPtr pContext = m_aFieldStack.back();
8146 if (pContext)
8147 {
8148 pContext->setFFDataHandler(pFFDataHandler);
8149 }
8150 }
8151
8152#ifdef DBG_UTIL
8154#endif
8155}
8156
8158{
8160 return;
8161#ifdef DBG_UTIL
8162 TagLogger::getInstance().element("popFieldContext");
8163#endif
8164
8165 if (m_aFieldStack.empty())
8166 return;
8167
8168 FieldContextPtr pContext = m_aFieldStack.back();
8169 OSL_ENSURE( pContext, "no field context available");
8170 if( pContext )
8171 {
8172 if( !pContext->IsCommandCompleted() )
8174
8175 if (!pContext->GetResult().isEmpty())
8176 {
8177 uno::Reference< beans::XPropertySet > xFieldProperties = pContext->GetCustomField();
8178 if(xFieldProperties.is())
8179 SetNumberFormat( pContext->GetResult(), xFieldProperties, true );
8180 SetFieldResult( pContext->GetResult() );
8181 }
8182
8183 //insert the field, TC or TOC
8185 if (!m_aTextAppendStack.empty())
8186 xTextAppend = m_aTextAppendStack.top().xTextAppend;
8187 if(xTextAppend.is())
8188 {
8189 try
8190 {
8191 uno::Reference< text::XTextContent > xToInsert( pContext->GetTOC(), uno::UNO_QUERY );
8192 if( xToInsert.is() )
8193 {
8195 {
8196 // inside SDT, last empty paragraph is also part of index
8198 {
8199 // End of index is the first item on a new paragraph - this paragraph
8200 // should not be part of index
8201 auto xCursor
8202 = xTextAppend->createTextCursorByRange(
8203 m_aTextAppendStack.top().xInsertPosition.is()
8204 ? m_aTextAppendStack.top().xInsertPosition
8205 : xTextAppend->getEnd());
8206 xCursor->goLeft(1, true);
8207 // delete
8208 xCursor->setString(OUString());
8209 // But a new paragraph should be started after the index instead
8210 if (m_bIsNewDoc) // this check - see testTdf129402
8211 { // where finishParagraph inserts between 2 EndNode
8212 xTextAppend->finishParagraph(css::beans::PropertyValues());
8213 }
8214 else
8215 {
8216 xTextAppend->finishParagraphInsert(css::beans::PropertyValues(),
8217 m_aTextAppendStack.top().xInsertPosition);
8218 }
8219 }
8220 m_bStartedTOC = false;
8221 m_aTextAppendStack.pop();
8222 m_bTextInserted = false;
8223 m_bParaChanged = true; // the paragraph must stay anyway
8224 }
8225 m_bStartTOC = false;
8226 m_bStartIndex = false;
8227 m_bStartBibliography = false;
8230 }
8231 else
8232 {
8233 xToInsert.set(pContext->GetTC(), uno::UNO_QUERY);
8234 if (!xToInsert.is() && !IsInTOC() && !m_bStartIndex && !m_bStartBibliography)
8235 xToInsert = pContext->GetTextField();
8236 if (xToInsert.is() && !IsInTOC() && !m_bStartIndex && !m_bStartBibliography)
8237 {
8239 // Character properties of the field show up here the
8240 // last (always empty) run. Inherit character
8241 // properties from there.
8242 // Also merge in the properties from the field context,
8243 // e.g. SdtEndBefore.
8245 aMap.InsertProps(m_pLastCharacterContext);
8246 aMap.InsertProps(m_aFieldStack.back()->getProperties());
8247 appendTextContent(xToInsert, aMap.GetPropertyValues());
8248 CheckRedline( xToInsert->getAnchor( ) );
8249 }
8250 else
8251 {
8252 uno::Reference< text::XTextCursor > xCrsr = xTextAppend->createTextCursorByRange(pContext->GetStartRange());
8253 FormControlHelper::Pointer_t pFormControlHelper(pContext->getFormControlHelper());
8254 if (pFormControlHelper)
8255 {
8256 // xCrsr may be empty e.g. when pContext->GetStartRange() is outside of
8257 // xTextAppend, like when a field started in a parent paragraph is being
8258 // closed inside an anchored text box. It could be possible to throw an
8259 // exception here, and abort import, but Word tolerates such invalid
8260 // input, so it makes sense to do the same (tdf#152200)
8261 if (xCrsr.is())
8262 {
8263 uno::Reference< text::XFormField > xFormField(pContext->GetFormField());
8264 if (pFormControlHelper->hasFFDataHandler())
8265 {
8266 xToInsert.set(xFormField, uno::UNO_QUERY);
8267 if (xFormField.is() && xToInsert.is())
8268 {
8270 pContext->GetFieldId());
8271 pFormControlHelper->processField(xFormField);
8272 }
8273 else
8274 {
8275 pFormControlHelper->insertControl(xCrsr);
8276 }
8277 }
8278 else
8279 {
8281 pContext->GetFieldId());
8282 uno::Reference<lang::XComponent>(xFormField, uno::UNO_QUERY_THROW)->dispose(); // presumably invalid?
8283 }
8284 }
8285 }
8286 else if (!pContext->GetHyperlinkURL().isEmpty() && xCrsr.is())
8287 {
8288 if (m_aTextAppendStack.top().xInsertPosition.is())
8289 {
8290 xCrsr->gotoRange(m_aTextAppendStack.top().xInsertPosition, true);
8291 }
8292 else
8293 {
8294 xCrsr->gotoEnd(true);
8295 }
8296
8297 // Draw components (like comments) need hyperlinks set differently
8298 SvxUnoTextRangeBase* pDrawText = dynamic_cast<SvxUnoTextRangeBase*>(xCrsr.get());
8299 if ( pDrawText )
8300 pDrawText->attachField( std::make_unique<SvxURLField>(pContext->GetHyperlinkURL(), xCrsr->getString(), SvxURLFormat::AppDefault) );
8301 else
8302 {
8303 uno::Reference< beans::XPropertySet > xCrsrProperties( xCrsr, uno::UNO_QUERY_THROW );
8304 xCrsrProperties->setPropertyValue(getPropertyName(PROP_HYPER_LINK_U_R_L), uno::
8305 Any(pContext->GetHyperlinkURL()));
8306
8307 if (!pContext->GetHyperlinkTarget().isEmpty())
8308 xCrsrProperties->setPropertyValue("HyperLinkTarget", uno::Any(pContext->GetHyperlinkTarget()));
8309
8310 if (IsInTOC())
8311 {
8312 OUString sDisplayName("Index Link");
8313 xCrsrProperties->setPropertyValue("VisitedCharStyleName",uno::Any(sDisplayName));
8314 xCrsrProperties->setPropertyValue("UnvisitedCharStyleName",uno::Any(sDisplayName));
8315 }
8316 else
8317 {
8318 uno::Any aAny = xCrsrProperties->getPropertyValue("CharStyleName");
8319 OUString charStyle;
8320 if (css::uno::fromAny(aAny, &charStyle))
8321 {
8322 if (charStyle.isEmpty())
8323 {
8324 xCrsrProperties->setPropertyValue("VisitedCharStyleName", uno::Any(OUString("Default Style")));
8325 xCrsrProperties->setPropertyValue("UnvisitedCharStyleName", uno::Any(OUString("Default Style")));
8326 }
8327 else if (charStyle.equalsIgnoreAsciiCase("Internet Link"))
8328 {
8329 xCrsrProperties->setPropertyValue("CharStyleName", uno::Any(OUString("Default Style")));
8330 }
8331 else
8332 {
8333 xCrsrProperties->setPropertyValue("VisitedCharStyleName", aAny);
8334 xCrsrProperties->setPropertyValue("UnvisitedCharStyleName", aAny);
8335 }
8336 }
8337 }
8338 }
8339 }
8340 else if (m_nStartGenericField != 0)
8341 {
8343 PopFieldmark(m_aTextAppendStack, xCrsr, pContext->GetFieldId());
8344 if(m_bTextInserted)
8345 {
8346 m_bTextInserted = false;
8347 }
8348 }
8349 }
8350 }
8351 }
8352 catch(const lang::IllegalArgumentException&)
8353 {
8354 TOOLS_WARN_EXCEPTION( "writerfilter", "PopFieldContext()" );
8355 }
8356 catch(const uno::Exception&)
8357 {
8358 TOOLS_WARN_EXCEPTION( "writerfilter", "PopFieldContext()" );
8359 }
8360 }
8361
8362 //TOCs have to include all the imported content
8363 }
8364
8365 std::vector<FieldParagraph> aParagraphsToFinish;
8366 if (pContext)
8367 {
8368 aParagraphsToFinish = pContext->GetParagraphsToFinish();
8369 }
8370
8371 //remove the field context
8372 m_aFieldStack.pop_back();
8373
8374 // Finish the paragraph(s) now that the field is closed.
8375 for (const auto& rFinish : aParagraphsToFinish)
8376 {
8377 finishParagraph(rFinish.m_pPropertyMap, rFinish.m_bRemove);
8378 }
8379}
8380
8381
8382void DomainMapper_Impl::SetBookmarkName( const OUString& rBookmarkName )
8383{
8384 BookmarkMap_t::iterator aBookmarkIter = m_aBookmarkMap.find( m_sCurrentBkmkId );
8385 if( aBookmarkIter != m_aBookmarkMap.end() )
8386 {
8387 // fields are internal bookmarks: consume redundant "normal" bookmark
8388 if ( IsOpenField() )
8389 {
8390 FFDataHandler::Pointer_t pFFDataHandler(GetTopFieldContext()->getFFDataHandler());
8391 if (pFFDataHandler && pFFDataHandler->getName() == rBookmarkName)
8392 {
8393 // HACK: At the END marker, StartOrEndBookmark will START
8394 // a bookmark which will eventually be abandoned, not created.
8395 m_aBookmarkMap.erase(aBookmarkIter);
8396 return;
8397 }
8398 }
8399
8400 aBookmarkIter->second.m_sBookmarkName = m_sCurrentBkmkPrefix + rBookmarkName;
8401 m_sCurrentBkmkPrefix.clear();
8402 }
8403 else
8404 {
8405 m_sCurrentBkmkName = rBookmarkName;
8406 m_sCurrentBkmkPrefix.clear();
8407 }
8408}
8409
8410// This method was used as-is for DomainMapper_Impl::startOrEndPermissionRange() implementation.
8411void DomainMapper_Impl::StartOrEndBookmark( const OUString& rId )
8412{
8413 /*
8414 * Add the dummy paragraph to handle section properties
8415 * iff the first element in the section is a table. If the dummy para is not added yet, then add it;
8416 * So bookmark is not attached to the wrong paragraph.
8417 */
8420 {
8422 }
8423
8425 if (m_aTextAppendStack.empty())
8426 return;
8427 uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
8428 BookmarkMap_t::iterator aBookmarkIter = m_aBookmarkMap.find( rId );
8429 //is the bookmark name already registered?
8430 try
8431 {
8432 if( aBookmarkIter != m_aBookmarkMap.end() )
8433 {
8434 if (m_xTextFactory.is())
8435 {
8436 uno::Reference< text::XTextContent > xBookmark( m_xTextFactory->createInstance( "com.sun.star.text.Bookmark" ), uno::UNO_QUERY_THROW );
8438 uno::Reference< text::XText > xText = aBookmarkIter->second.m_xTextRange->getText();
8439 if( aBookmarkIter->second.m_bIsStartOfText && !bIsAfterDummyPara)
8440 {
8441 xCursor = xText->createTextCursorByRange( xText->getStart() );
8442 }
8443 else
8444 {
8445 xCursor = xText->createTextCursorByRange( aBookmarkIter->second.m_xTextRange );
8446 }
8447 if (!aBookmarkIter->second.m_bIsStartOfText)
8448 {
8449 xCursor->goRight( 1, false );
8450 }
8451
8452 xCursor->gotoRange( xTextAppend->getEnd(), true );
8453 // A Paragraph was recently finished, and a new Paragraph has not been started as yet
8454 // then move the bookmark-End to the earlier paragraph
8455 if (IsOutsideAParagraph())
8456 {
8457 // keep bookmark range, if it doesn't exceed cell boundary
8458 uno::Reference< text::XTextRange > xStart = xCursor->getStart();
8459 xCursor->goLeft( 1, false );
8461 xCursor->gotoRange(xStart, true );
8462 }
8463 uno::Reference< container::XNamed > xBkmNamed( xBookmark, uno::UNO_QUERY_THROW );
8464 SAL_WARN_IF(aBookmarkIter->second.m_sBookmarkName.isEmpty(), "writerfilter.dmapper", "anonymous bookmark");
8465 //todo: make sure the name is not used already!
8466 xBkmNamed->setName( aBookmarkIter->second.m_sBookmarkName );
8467 xTextAppend->insertTextContent( uno::Reference< text::XTextRange >( xCursor, uno::UNO_QUERY_THROW), xBookmark, !xCursor->isCollapsed() );
8468 }
8469 m_aBookmarkMap.erase( aBookmarkIter );
8470 m_sCurrentBkmkId.clear();
8471 }
8472 else
8473 {
8474 //otherwise insert a text range as marker
8475 bool bIsStart = true;
8477 if (xTextAppend.is())
8478 {
8479 uno::Reference<text::XTextCursor> const xCursor =
8480 xTextAppend->createTextCursorByRange(
8481 m_aTextAppendStack.top().xInsertPosition.is()
8482 ? m_aTextAppendStack.top().xInsertPosition
8483 : xTextAppend->getEnd() );
8484
8485 if (!xCursor)
8486 return;
8487
8488 if (!bIsAfterDummyPara)
8489 bIsStart = !xCursor->goLeft(1, false);
8490 xCurrent = xCursor->getStart();
8491 }
8492 m_sCurrentBkmkId = rId;
8493 m_aBookmarkMap.emplace( rId, BookmarkInsertPosition( bIsStart, m_sCurrentBkmkName, xCurrent ) );
8494 m_sCurrentBkmkName.clear();
8495 }
8496 }
8497 catch( const uno::Exception& )
8498 {
8499 //TODO: What happens to bookmarks where start and end are at different XText objects?
8500 }
8501}
8502
8504{
8505 static constexpr OUStringLiteral MoveFrom_Bookmark_NamePrefix = u"__RefMoveFrom__";
8506 static constexpr OUStringLiteral MoveTo_Bookmark_NamePrefix = u"__RefMoveTo__";
8507 if ( bIsFrom )
8508 m_sCurrentBkmkPrefix = MoveFrom_Bookmark_NamePrefix;
8509 else
8510 m_sCurrentBkmkPrefix = MoveTo_Bookmark_NamePrefix;
8511}
8512
8514{
8515 PermMap_t::iterator aPremIter = m_aPermMap.find(m_sCurrentPermId);
8516 if (aPremIter != m_aPermMap.end())
8517 aPremIter->second.m_Ed = user;
8518 else
8519 m_sCurrentPermEd = user;
8520}
8521
8523{
8524 PermMap_t::iterator aPremIter = m_aPermMap.find(m_sCurrentPermId);
8525 if (aPremIter != m_aPermMap.end())
8526 aPremIter->second.m_EdGrp = group;
8527 else
8529}
8530
8531// This method is based on implementation from DomainMapper_Impl::StartOrEndBookmark()
8533{
8534 /*
8535 * Add the dummy paragraph to handle section properties
8536 * if the first element in the section is a table. If the dummy para is not added yet, then add it;
8537 * So permission is not attached to the wrong paragraph.
8538 */
8539 if (getTableManager().isInCell() && m_nTableDepth == 0 && GetIsFirstParagraphInSection()
8541 {
8543 }
8544
8545 if (m_aTextAppendStack.empty())
8546 return;
8547
8548 const bool bIsAfterDummyPara = GetIsDummyParaAddedForTableInSection() && GetIsFirstParagraphInSection();
8549
8550 uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
8551 PermMap_t::iterator aPermIter = m_aPermMap.find(permissinId);
8552
8553 //is the bookmark name already registered?
8554 try
8555 {
8556 if (aPermIter == m_aPermMap.end())
8557 {
8558 //otherwise insert a text range as marker
8559 bool bIsStart = true;
8561 if (xTextAppend.is())
8562 {
8563 uno::Reference< text::XTextCursor > xCursor = xTextAppend->createTextCursorByRange(xTextAppend->getEnd());
8564
8565 if (!bIsAfterDummyPara)
8566 bIsStart = !xCursor->goLeft(1, false);
8567 xCurrent = xCursor->getStart();
8568 }
8569
8570 // register the start of the new permission
8571 m_sCurrentPermId = permissinId;
8572 m_aPermMap.emplace(permissinId, PermInsertPosition(bIsStart, permissinId, m_sCurrentPermEd, m_sCurrentPermEdGrp, xCurrent));
8573
8574 // clean up
8575 m_sCurrentPermEd.clear();
8576 m_sCurrentPermEdGrp.clear();
8577 }
8578 else
8579 {
8580 if (m_xTextFactory.is())
8581 {
8583 uno::Reference< text::XText > xText = aPermIter->second.m_xTextRange->getText();
8584 if (aPermIter->second.m_bIsStartOfText && !bIsAfterDummyPara)
8585 {
8586 xCursor = xText->createTextCursorByRange(xText->getStart());
8587 }
8588 else
8589 {
8590 xCursor = xText->createTextCursorByRange(aPermIter->second.m_xTextRange);
8591 }
8592 if (!aPermIter->second.m_bIsStartOfText)
8593 {
8594 xCursor->goRight(1, false);
8595 }
8596
8597 xCursor->gotoRange(xTextAppend->getEnd(), true);
8598 // A Paragraph was recently finished, and a new Paragraph has not been started as yet
8599 // then move the bookmark-End to the earlier paragraph
8600 if (IsOutsideAParagraph())
8601 {
8602 xCursor->goLeft(1, false);
8603 }
8604
8605 // create a new bookmark using specific bookmark name pattern for permissions
8606 uno::Reference< text::XTextContent > xPerm(m_xTextFactory->createInstance("com.sun.star.text.Bookmark"), uno::UNO_QUERY_THROW);
8607 uno::Reference< container::XNamed > xPermNamed(xPerm, uno::UNO_QUERY_THROW);
8608 xPermNamed->setName(aPermIter->second.createBookmarkName());
8609
8610 // add new bookmark
8611 const bool bAbsorb = !xCursor->isCollapsed();
8612 uno::Reference< text::XTextRange > xCurrent(xCursor, uno::UNO_QUERY_THROW);
8613 xTextAppend->insertTextContent(xCurrent, xPerm, bAbsorb);
8614 }
8615
8616 // remove processed permission
8617 m_aPermMap.erase(aPermIter);
8618
8619 // clean up
8620 m_sCurrentPermId = 0;
8621 m_sCurrentPermEd.clear();
8622 m_sCurrentPermEdGrp.clear();
8623 }
8624 }
8625 catch (const uno::Exception&)
8626 {
8627 //TODO: What happens to bookmarks where start and end are at different XText objects?
8628 }
8629}
8630
8632 const bool bStart,
8633 const sal_Int32 nAnnotationId)
8634{
8635 if (m_aTextAppendStack.empty())
8636 return;
8637
8638 // Create a cursor, pointing to the current position.
8639 uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend;
8641 if (xTextAppend.is())
8642 {
8644 if (m_bIsNewDoc)
8645 xCursor = xTextAppend->createTextCursorByRange(xTextAppend->getEnd());
8646 else
8647 xCursor = m_aTextAppendStack.top().xCursor;
8648 if (xCursor.is())
8649 xCurrent = xCursor->getStart();
8650 }
8651
8652 // And save it, to be used by PopAnnotation() later.
8653 AnnotationPosition& aAnnotationPosition = m_aAnnotationPositions[ nAnnotationId ];
8654 if (bStart)
8655 {
8656 aAnnotationPosition.m_xStart = xCurrent;
8657 }
8658 else
8659 {
8660 aAnnotationPosition.m_xEnd = xCurrent;
8661 }
8662 m_aAnnotationPositions[ nAnnotationId ] = aAnnotationPosition;
8663}
8664
8666{
8667 if(!m_pGraphicImport)
8668 {
8670 }
8671 return m_pGraphicImport;
8672}
8673/*-------------------------------------------------------------------------
8674 reset graphic import if the last import resulted in a shape, not a graphic
8675 -----------------------------------------------------------------------*/
8677{
8679}
8680
8681
8683{
8686 { // this appears impossible?
8687 //create the graphic
8688 ref->resolve( *m_pGraphicImport );
8689 }
8690
8691 //insert it into the document at the current cursor position
8692
8694 (m_pGraphicImport->GetGraphicObject());
8695
8696 // In case the SDT starts with the text portion of the graphic, then set the SDT properties here.
8697 bool bHasGrabBag = false;
8698 uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
8699 if (xPropertySet.is())
8700 {
8701 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
8702 bHasGrabBag = xPropertySetInfo->hasPropertyByName("FrameInteropGrabBag");
8703 // In case we're outside a paragraph, then the SDT properties are stored in the paragraph grab-bag, not the frame one.
8704 if (!m_pSdtHelper->isInteropGrabBagEmpty() && bHasGrabBag && !m_pSdtHelper->isOutsideAParagraph())
8705 {
8706 comphelper::SequenceAsHashMap aFrameGrabBag(xPropertySet->getPropertyValue("FrameInteropGrabBag"));
8707 aFrameGrabBag["SdtPr"] <<= m_pSdtHelper->getInteropGrabBagAndClear();
8708 xPropertySet->setPropertyValue("FrameInteropGrabBag", uno::Any(aFrameGrabBag.getAsConstPropertyValueList()));
8709 }
8710 }
8711
8712 /* Set "SdtEndBefore" property on Drawing.
8713 * It is required in a case when Drawing appears immediately after first run i.e.
8714 * there is no text/space/tab in between two runs.
8715 * In this case "SdtEndBefore" property needs to be set on Drawing.
8716 */
8717 if(IsSdtEndBefore())
8718 {
8719 if(xPropertySet.is() && bHasGrabBag)
8720 {
8722 { "SdtEndBefore", uno::Any(true) }
8723 }));
8724 xPropertySet->setPropertyValue("FrameInteropGrabBag",uno::Any(aFrameGrabBag));
8725 }
8726 }
8727
8728
8729 // Update the shape properties if it is embedded object.
8730 if(m_xEmbedded.is()){
8731 if (m_pGraphicImport->GetXShapeObject())
8732 m_pGraphicImport->GetXShapeObject()->setPosition(
8733 m_pGraphicImport->GetGraphicObjectPosition());
8734
8735 uno::Reference<drawing::XShape> xShape = m_pGraphicImport->GetXShapeObject();
8738 {
8739 uno::Reference<beans::XPropertySet> xEmbeddedProps(m_xEmbedded, uno::UNO_QUERY);
8740 xEmbeddedProps->setPropertyValue("AnchorType", uno::Any(text::TextContentAnchorType_AT_CHARACTER));
8741 xEmbeddedProps->setPropertyValue("IsFollowingTextFlow", uno::Any(m_pGraphicImport->GetLayoutInCell()));
8742 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
8743 xEmbeddedProps->setPropertyValue("HoriOrient", xShapeProps->getPropertyValue("HoriOrient"));
8744 xEmbeddedProps->setPropertyValue("HoriOrientPosition", xShapeProps->getPropertyValue("HoriOrientPosition"));
8745 xEmbeddedProps->setPropertyValue("HoriOrientRelation", xShapeProps->getPropertyValue("HoriOrientRelation"));
8746 xEmbeddedProps->setPropertyValue("VertOrient", xShapeProps->getPropertyValue("VertOrient"));
8747 xEmbeddedProps->setPropertyValue("VertOrientPosition", xShapeProps->getPropertyValue("VertOrientPosition"));
8748 xEmbeddedProps->setPropertyValue("VertOrientRelation", xShapeProps->getPropertyValue("VertOrientRelation"));
8749 //tdf123873 fix missing textwrap import
8750 xEmbeddedProps->setPropertyValue("TextWrap", xShapeProps->getPropertyValue("TextWrap"));
8751
8752 // GraphicZOrderHelper::findZOrder() was called already, so can just copy it over.
8753 xEmbeddedProps->setPropertyValue("ZOrder", xShapeProps->getPropertyValue("ZOrder"));
8754 }
8755 }
8756 //insert it into the document at the current cursor position
8757 OSL_ENSURE( xTextContent.is(), "DomainMapper_Impl::ImportGraphic");
8758 if( xTextContent.is())
8759 {
8760 bool bAppend = true;
8761 // workaround for images anchored to characters: add ZWSPs around the anchoring point
8763 {
8764 uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
8765 if(xTextAppend.is())
8766 {
8767 try
8768 {
8769 uno::Reference< text::XText > xText = xTextAppend->getText();
8770 uno::Reference< text::XTextCursor > xCrsr = xText->createTextCursor();
8771 xCrsr->gotoEnd(false);
8772 PropertyMapPtr pEmpty(new PropertyMap());
8773 appendTextPortion(u"​", pEmpty);
8775 bAppend = false;
8776 xCrsr->gotoEnd(false);
8777 appendTextPortion(u"​", pEmpty);
8778
8781 }
8782 catch( const uno::Exception& )
8783 {
8784 }
8785 }
8786 }
8787
8788 if ( bAppend )
8790
8792 {
8793 // Remember this object is anchored to the current paragraph.
8794 AnchoredObjectInfo aInfo;
8795 aInfo.m_xAnchoredObject = xTextContent;
8796 if (m_pGraphicImport)
8797 {
8798 // We still have the graphic import around, remember the original margin, so later
8799 // SectionPropertyMap::HandleIncreasedAnchoredObjectSpacing() can use it.
8800 aInfo.m_nLeftMargin = m_pGraphicImport->GetLeftMarginOrig();
8801 }
8802 m_aTextAppendStack.top().m_aAnchoredObjects.push_back(aInfo);
8803 }
8805 {
8807
8808 // store inline images with track changes, because the anchor point
8809 // to set redlining is not available yet
8810 if (!m_aTextAppendStack.empty() && !m_aRedlines.top().empty() )
8811 {
8812 // Remember this object is anchored to the current paragraph.
8813 AnchoredObjectInfo aInfo;
8814 aInfo.m_xAnchoredObject = xTextContent;
8815 aInfo.m_xRedlineForInline = m_aRedlines.top().back();
8816 m_aTextAppendStack.top().m_aAnchoredObjects.push_back(aInfo);
8817 }
8818
8819 }
8820 }
8821
8822 // Clear the reference, so in case the embedded object is inside a
8823 // TextFrame, we won't try to resize it (to match the size of the
8824 // TextFrame) here.
8825 m_xEmbedded.clear();
8827}
8828
8829
8830void DomainMapper_Impl::SetLineNumbering( sal_Int32 nLnnMod, sal_uInt32 nLnc, sal_Int32 ndxaLnn )
8831{
8832 if( !m_bLineNumberingSet )
8833 {
8834 try
8835 {
8836 uno::Reference< text::XLineNumberingProperties > xLineProperties( m_xTextDocument, uno::UNO_QUERY_THROW );
8837 uno::Reference< beans::XPropertySet > xProperties = xLineProperties->getLineNumberingProperties();
8838 uno::Any aTrue( uno::Any( true ));
8839 xProperties->setPropertyValue( getPropertyName( PROP_IS_ON ), aTrue);
8840 xProperties->setPropertyValue( getPropertyName( PROP_COUNT_EMPTY_LINES ), aTrue );
8841 xProperties->setPropertyValue( getPropertyName( PROP_COUNT_LINES_IN_FRAMES ), uno::Any( false ) );
8842 xProperties->setPropertyValue( getPropertyName( PROP_INTERVAL ), uno::Any( static_cast< sal_Int16 >( nLnnMod )));
8843 xProperties->setPropertyValue( getPropertyName( PROP_DISTANCE ), uno::Any( ConversionHelper::convertTwipToMM100(ndxaLnn) ));
8844 xProperties->setPropertyValue( getPropertyName( PROP_NUMBER_POSITION ), uno::Any( style::LineNumberPosition::LEFT));
8845 xProperties->setPropertyValue( getPropertyName( PROP_NUMBERING_TYPE ), uno::Any( style::NumberingType::ARABIC));
8846 xProperties->setPropertyValue( getPropertyName( PROP_RESTART_AT_EACH_PAGE ), uno::Any( nLnc == NS_ooxml::LN_Value_ST_LineNumberRestart_newPage ));
8847 }
8848 catch( const uno::Exception& )
8849 {}
8850 }
8851 m_bLineNumberingSet = true;
8852 uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier( GetTextDocument(), uno::UNO_QUERY_THROW );
8853 uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
8855 xStyleFamilies->getByName(getPropertyName( PROP_PARAGRAPH_STYLES )) >>= xStyles;
8856 lcl_linenumberingHeaderFooter( xStyles, "Header", this );
8857 lcl_linenumberingHeaderFooter( xStyles, "Footer", this );
8858}
8859
8860
8862{
8864 switch(eElement)
8865 {
8866 case PAGE_MAR_TOP : m_aPageMargins.top = nValue; break;
8867 case PAGE_MAR_RIGHT : m_aPageMargins.right = nValue; break;
8869 case PAGE_MAR_LEFT : m_aPageMargins.left = nValue; break;
8872 case PAGE_MAR_GUTTER:
8874 break;
8875 }
8876}
8877
8878
8880 : top(ConversionHelper::convertTwipToMM100( sal_Int32(1440)))
8881 // This is strange, the RTF spec says it's 1800, but it's clearly 1440 in Word
8882 // OOXML seems not to specify a default value
8883 , right(ConversionHelper::convertTwipToMM100( sal_Int32(1440)))
8884 , bottom(top)
8885 , left(right)
8886 , header(ConversionHelper::convertTwipToMM100(sal_Int32(720)))
8887 , footer(header)
8888 , gutter(0)
8889{
8890}
8891
8892
8894 uno::Reference< text::XTextRange > const& xFrameStartRange,
8895 uno::Reference< text::XTextRange > const& xFrameEndRange,
8896 std::vector<beans::PropertyValue>&& rFrameProperties
8897 )
8898{
8899 OSL_ENSURE(
8900 m_aFrameProperties.empty() && !m_xFrameStartRange.is() && !m_xFrameEndRange.is(),
8901 "frame properties not removed");
8902 m_aFrameProperties = std::move(rFrameProperties);
8903 m_xFrameStartRange = xFrameStartRange;
8904 m_xFrameEndRange = xFrameEndRange;
8905}
8906
8907
8909{
8911 {
8912 std::vector<sal_Int32> redPos, redLen;
8913 try
8914 {
8915 uno::Reference< text::XTextAppendAndConvert > xTextAppendAndConvert( GetTopTextAppend(), uno::UNO_QUERY_THROW );
8916 // convert redline ranges to cursor movement and character length
8917 sal_Int32 redIdx;
8919
8920 const uno::Reference< text::XTextContent >& xTextContent = xTextAppendAndConvert->convertToTextFrame(
8924
8925 uno::Reference< text::XText > xDest( xTextContent, uno::UNO_QUERY_THROW );
8926 lcl_PasteRedlines(xDest, m_aStoredRedlines[StoredRedlines::FRAME], redPos, redLen, redIdx);
8927 }
8928 catch( const uno::Exception&)
8929 {
8930 DBG_UNHANDLED_EXCEPTION( "writerfilter.dmapper", "Exception caught when converting to frame");
8931 }
8932
8934
8935 if (redPos.size() == m_aStoredRedlines[StoredRedlines::FRAME].size()/3)
8936 {
8937 for( sal_Int32 i = m_aStoredRedlines[StoredRedlines::FRAME].size() - 1; i >= 0; --i)
8938 {
8939 // keep redlines of floating tables to process them in CloseSectionGroup()
8940 if ( redPos[i/3] != -1 )
8941 {
8943 }
8944 }
8945 }
8946 else
8948 }
8949 m_xFrameStartRange = nullptr;
8950 m_xFrameEndRange = nullptr;
8951 m_aFrameProperties.clear();
8952}
8953
8954void DomainMapper_Impl::AddNewRedline( sal_uInt32 sprmId )
8955{
8956 RedlineParamsPtr pNew( new RedlineParams );
8957 pNew->m_nToken = XML_mod;
8958 if ( !m_bIsParaMarkerChange )
8959 {
8960 // <w:rPrChange> applies to the whole <w:r>, <w:pPrChange> applies to the whole <w:p>,
8961 // so keep those two in CONTEXT_CHARACTERS and CONTEXT_PARAGRAPH, which will take
8962 // care of their scope (i.e. when they should be used and discarded).
8963 // Let's keep the rest the same way they used to be handled (explicitly dropped
8964 // from a global stack by endtrackchange), but quite possibly they should not be handled
8965 // that way either (I don't know).
8966 if( sprmId == NS_ooxml::LN_EG_RPrContent_rPrChange )
8967 GetTopContextOfType( CONTEXT_CHARACTER )->Redlines().push_back( pNew );
8968 else if( sprmId == NS_ooxml::LN_CT_PPr_pPrChange )
8969 GetTopContextOfType( CONTEXT_PARAGRAPH )->Redlines().push_back( pNew );
8970 else if( sprmId != NS_ooxml::LN_CT_ParaRPr_rPrChange )
8971 m_aRedlines.top().push_back( pNew );
8972 }
8973 else
8974 {
8975 m_pParaMarkerRedline = pNew;
8976 }
8977 // Newly read data will go into this redline.
8978 m_currentRedline = pNew;
8979}
8980
8982{
8984}
8985
8987{
8988 assert(m_currentRedline);
8989 return m_currentRedline->m_nToken;
8990}
8991
8992void DomainMapper_Impl::SetCurrentRedlineAuthor( const OUString& sAuthor )
8993{
8994 if (!m_xAnnotationField.is())
8995 {
8996 if (m_currentRedline)
8997 m_currentRedline->m_sAuthor = sAuthor;
8998 else
8999 SAL_INFO("writerfilter.dmapper", "numberingChange not implemented");
9000 }
9001 else
9002 m_xAnnotationField->setPropertyValue("Author", uno::Any(sAuthor));
9003}
9004
9005void DomainMapper_Impl::SetCurrentRedlineInitials( const OUString& sInitials )
9006{
9007 if (m_xAnnotationField.is())
9008 m_xAnnotationField->setPropertyValue("Initials", uno::Any(sInitials));
9009}
9010
9011void DomainMapper_Impl::SetCurrentRedlineDate( const OUString& sDate )
9012{
9013 if (!m_xAnnotationField.is())
9014 {
9015 if (m_currentRedline)
9016 m_currentRedline->m_sDate = sDate;
9017 else
9018 SAL_INFO("writerfilter.dmapper", "numberingChange not implemented");
9019 }
9020 else
9021 m_xAnnotationField->setPropertyValue("DateTimeValue", uno::Any(ConversionHelper::ConvertDateStringToDateTime(sDate)));
9022}
9023
9025{
9026 if (m_xAnnotationField.is())
9027 {
9029 }
9030 else
9031 {
9032 // This should be an assert, but somebody had the smart idea to reuse this function also for comments and whatnot,
9033 // and in some cases the id is actually not handled, which may be in fact a bug.
9034 if( !m_currentRedline)
9035 SAL_INFO("writerfilter.dmapper", "no current redline");
9036 }
9037}
9038
9040{
9041 assert(m_currentRedline);
9042 m_currentRedline->m_nToken = nToken;
9043}
9044
9046{
9047 assert(m_currentRedline);
9048 m_currentRedline->m_aRevertProperties = aProperties;
9049}
9050
9051
9052// This removes only the last redline stored here, those stored in contexts are automatically removed when
9053// the context is destroyed.
9055{
9056 if (m_aRedlines.top().empty())
9057 {
9058 if (GetFootnoteCount() > -1 || GetEndnoteCount() > -1)
9059 return;
9060 SAL_WARN("writerfilter.dmapper", "RemoveTopRedline called with empty stack");
9061 throw uno::Exception("RemoveTopRedline failed", nullptr);
9062 }
9063 m_aRedlines.top().pop_back( );
9065}
9066
9068{
9069 if (!(m_pSettingsTable && m_xTextFactory.is()))
9070 return;
9071
9072 try
9073 {
9074 uno::Reference< beans::XPropertySet > xTextDefaults(m_xTextFactory->createInstance("com.sun.star.text.Defaults"), uno::UNO_QUERY_THROW );
9075 sal_Int32 nDefTab = m_pSettingsTable->GetDefaultTabStop();
9076 xTextDefaults->setPropertyValue( getPropertyName( PROP_TAB_STOP_DISTANCE ), uno::Any(nDefTab) );
9077 if (m_pSettingsTable->GetLinkStyles())
9078 {
9079 // If linked styles are enabled, set paragraph defaults from Word's default template
9081 style::LineSpacing aSpacing;
9082 aSpacing.Mode = style::LineSpacingMode::PROP;
9083 aSpacing.Height = sal_Int16(115);
9084 xTextDefaults->setPropertyValue(getPropertyName(PROP_PARA_LINE_SPACING), uno::Any(aSpacing));
9085 }
9086
9087 if (m_pSettingsTable->GetZoomFactor() || m_pSettingsTable->GetView())
9088 {
9089 std::vector<beans::PropertyValue> aViewProps;
9090 if (m_pSettingsTable->GetZoomFactor())
9091 {
9092 aViewProps.emplace_back("ZoomFactor", -1, uno::Any(m_pSettingsTable->GetZoomFactor()), beans::PropertyState_DIRECT_VALUE);
9093 aViewProps.emplace_back("VisibleBottom", -1, uno::Any(sal_Int32(0)), beans::PropertyState_DIRECT_VALUE);
9094 aViewProps.emplace_back("ZoomType", -1,
9095 uno::Any(m_pSettingsTable->GetZoomType()),
9096 beans::PropertyState_DIRECT_VALUE);
9097 }
9099 xBox->insertByIndex(sal_Int32(0), uno::Any(comphelper::containerToSequence(aViewProps)));
9100 uno::Reference<document::XViewDataSupplier> xViewDataSupplier(m_xTextDocument, uno::UNO_QUERY);
9101 xViewDataSupplier->setViewData(xBox);
9102 }
9103
9104 uno::Reference< beans::XPropertySet > xSettings(m_xTextFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
9105
9106 if (m_pSettingsTable->GetDoNotExpandShiftReturn())
9107 xSettings->setPropertyValue( "DoNotJustifyLinesWithManualBreak", uno::Any(true) );
9108 if (m_pSettingsTable->GetUsePrinterMetrics())
9109 xSettings->setPropertyValue("PrinterIndependentLayout", uno::Any(document::PrinterIndependentLayout::DISABLED));
9110 if( m_pSettingsTable->GetEmbedTrueTypeFonts())
9111 xSettings->setPropertyValue( getPropertyName( PROP_EMBED_FONTS ), uno::Any(true) );
9112 if( m_pSettingsTable->GetEmbedSystemFonts())
9113 xSettings->setPropertyValue( getPropertyName( PROP_EMBED_SYSTEM_FONTS ), uno::Any(true) );
9114 xSettings->setPropertyValue("AddParaTableSpacing", uno::Any(m_pSettingsTable->GetDoNotUseHTMLParagraphAutoSpacing()));
9115 if (m_pSettingsTable->GetNoLeading())
9116 {
9117 xSettings->setPropertyValue("AddExternalLeading", uno::Any(!m_pSettingsTable->GetNoLeading()));
9118 }
9119 if( m_pSettingsTable->GetProtectForm() )
9120 xSettings->setPropertyValue("ProtectForm", uno::Any( true ));
9121 if( m_pSettingsTable->GetReadOnly() )
9122 xSettings->setPropertyValue("LoadReadonly", uno::Any( true ));
9123 if (m_pSettingsTable->GetGutterAtTop())
9124 {
9125 xSettings->setPropertyValue("GutterAtTop", uno::Any(true));
9126 }
9128 = m_pSettingsTable->GetWriteProtectionSettings();
9129 if (aWriteProtection.hasElements())
9130 xSettings->setPropertyValue("ModifyPasswordInfo", uno::Any(aWriteProtection));
9131 }
9132 catch(const uno::Exception&)
9133 {
9134 }
9135}
9136
9138{
9139 SectionPropertyMap* pSectionContext = nullptr;
9140 //the section context is not available before the first call of startSectionGroup()
9141 if( !IsAnyTableImport() )
9142 {
9144 pSectionContext = dynamic_cast< SectionPropertyMap* >( pContext.get() );
9145 }
9146
9147 return pSectionContext;
9148}
9149
9150void DomainMapper_Impl::deferCharacterProperty(sal_Int32 id, const css::uno::Any& value)
9151{
9153}
9154
9156{
9157 // Actually process in DomainMapper, so that it's the same source file like normal processing.
9158 if( !m_deferredCharacterProperties.empty())
9159 {
9162 }
9163}
9164
9165sal_Int32 DomainMapper_Impl::getNumberingProperty(const sal_Int32 nListId, sal_Int32 nNumberingLevel, const OUString& aProp)
9166{
9167 sal_Int32 nRet = 0;
9168 if ( nListId < 0 )
9169 return nRet;
9170
9171 try
9172 {
9173 if (nNumberingLevel < 0) // It seems it's valid to omit numbering level, and in that case it means zero.
9174 nNumberingLevel = 0;
9175
9176 auto const pList(GetListTable()->GetList(nListId));
9177 assert(pList);
9178 const OUString aListName = pList->GetStyleName();
9179 const uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier(GetTextDocument(), uno::UNO_QUERY_THROW);
9180 const uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
9182 xStyleFamilies->getByName("NumberingStyles") >>= xNumberingStyles;
9183 const uno::Reference<beans::XPropertySet> xStyle(xNumberingStyles->getByName(aListName), uno::UNO_QUERY);
9184 const uno::Reference<container::XIndexAccess> xNumberingRules(xStyle->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
9185 if (xNumberingRules.is())
9186 {
9188 xNumberingRules->getByIndex(nNumberingLevel) >>= aProps;
9189 auto pProp = std::find_if(std::cbegin(aProps), std::cend(aProps),
9190 [&aProp](const beans::PropertyValue& rProp) { return rProp.Name == aProp; });
9191 if (pProp != std::cend(aProps))
9192 pProp->Value >>= nRet;
9193 }
9194 }
9195 catch( const uno::Exception& )
9196 {
9197 // This can happen when the doc contains some hand-crafted invalid list level.
9198 }
9199
9200 return nRet;
9201}
9202
9204{
9205 sal_Int32 nRet = 0;
9206
9207 std::optional<PropertyMap::Property> pProp = m_pTopContext->getProperty(PROP_NUMBERING_RULES);
9209 if (pProp)
9210 xNumberingRules.set(pProp->second, uno::UNO_QUERY);
9211 pProp = m_pTopContext->getProperty(PROP_NUMBERING_LEVEL);
9212 // Default numbering level is the first one.
9213 sal_Int32 nNumberingLevel = 0;
9214 if (pProp)
9215 pProp->second >>= nNumberingLevel;
9216 if (xNumberingRules.is())
9217 {
9219 xNumberingRules->getByIndex(nNumberingLevel) >>= aProps;
9220 auto pPropVal = std::find_if(std::cbegin(aProps), std::cend(aProps),
9221 [&aProp](const beans::PropertyValue& rProp) { return rProp.Name == aProp; });
9222 if (pPropVal != std::cend(aProps))
9223 pPropVal->Value >>= nRet;
9224 }
9225
9226 return nRet;
9227}
9228
9229
9231{
9233}
9234
9236{
9237 m_aInteropGrabBagName.clear();
9238 m_aInteropGrabBag.clear();
9239 m_aSubInteropGrabBag.clear();
9240}
9241
9243{
9244 return !(m_aInteropGrabBagName.isEmpty());
9245}
9246
9247void DomainMapper_Impl::appendGrabBag(std::vector<beans::PropertyValue>& rInteropGrabBag, const OUString& aKey, const OUString& aValue)
9248{
9249 if (m_aInteropGrabBagName.isEmpty())
9250 return;
9251 beans::PropertyValue aProperty;
9252 aProperty.Name = aKey;
9253 aProperty.Value <<= aValue;
9254 rInteropGrabBag.push_back(aProperty);
9255}
9256
9257void DomainMapper_Impl::appendGrabBag(std::vector<beans::PropertyValue>& rInteropGrabBag, const OUString& aKey, std::vector<beans::PropertyValue>& rValue)
9258{
9259 if (m_aInteropGrabBagName.isEmpty())
9260 return;
9261 beans::PropertyValue aProperty;
9262 aProperty.Name = aKey;
9263 aProperty.Value <<= comphelper::containerToSequence(rValue);
9264 rValue.clear();
9265 rInteropGrabBag.push_back(aProperty);
9266}
9267
9270{
9271#ifndef NDEBUG
9272 size_t contextSize(m_aContextStack.size());
9273 size_t propSize[NUMBER_OF_CONTEXTS];
9274 for (int i = 0; i < NUMBER_OF_CONTEXTS; ++i) {
9275 propSize[i] = m_aPropertyStacks[i].size();
9276 }
9277#endif
9278
9279 // Save "has footnote" state, which is specific to a section in the body
9280 // text, so state from substreams is not relevant.
9281 bool bHasFtn = m_bHasFtn;
9282
9283 //finalize any waiting frames before starting alternate streams
9286
9288 // Appending a TableManager resets its TableHandler, so we need to append
9289 // that as well, or tables won't be imported properly in headers/footers.
9292
9293 //import of page header/footer
9294 //Ensure that only one header/footer per section is pushed
9295
9296 switch( rName )
9297 {
9298 case NS_ooxml::LN_headerl:
9300 break;
9301 case NS_ooxml::LN_headerr:
9303 break;
9304 case NS_ooxml::LN_headerf:
9306 break;
9307 case NS_ooxml::LN_footerl:
9309 break;
9310 case NS_ooxml::LN_footerr:
9312 break;
9313 case NS_ooxml::LN_footerf:
9315 break;
9316 case NS_ooxml::LN_footnote:
9317 case NS_ooxml::LN_endnote:
9318 PushFootOrEndnote( NS_ooxml::LN_footnote == rName );
9319 break;
9320 case NS_ooxml::LN_annotation :
9322 break;
9323 }
9324
9325 try
9326 {
9327 ref->resolve(m_rDMapper);
9328 }
9329 catch (xml::sax::SAXException const&)
9330 {
9331 m_bSaxError = true;
9332 throw;
9333 }
9334
9335 switch( rName )
9336 {
9337 case NS_ooxml::LN_headerl:
9338 case NS_ooxml::LN_headerr:
9339 case NS_ooxml::LN_headerf:
9340 case NS_ooxml::LN_footerl:
9341 case NS_ooxml::LN_footerr:
9342 case NS_ooxml::LN_footerf:
9344 break;
9345 case NS_ooxml::LN_footnote:
9346 case NS_ooxml::LN_endnote:
9348 break;
9349 case NS_ooxml::LN_annotation :
9350 PopAnnotation();
9351 break;
9352 }
9353
9356 m_bHasFtn = bHasFtn;
9357
9358 switch(rName)
9359 {
9360 case NS_ooxml::LN_footnote:
9361 case NS_ooxml::LN_endnote:
9362 m_pTableHandler->setHadFootOrEndnote(true);
9363 m_bHasFtn = true;
9364 break;
9365 }
9366
9367 // check that stacks are the same as before substream
9368 assert(m_aContextStack.size() == contextSize);
9369 for (int i = 0; i < NUMBER_OF_CONTEXTS; ++i) {
9370 assert(m_aPropertyStacks[i].size() == propSize[i]);
9371 }
9372}
9373
9374void DomainMapper_Impl::commentProps(const OUString& sId, const CommentProperties& rProps)
9375{
9376 m_aCommentProps[sId] = rProps;
9377}
9378
9379
9381{
9382 if (!m_xPreviousParagraph.is())
9383 return false;
9384
9385 // Connected borders ("ParaIsConnectBorder") are always on by default
9386 // and never changed by DomainMapper. Except one case when border in
9387 // between is used. So this is not the best, but easiest way to check
9388 // is previous paragraph has border in between.
9389 bool bConnectBorders = true;
9390 m_xPreviousParagraph->getPropertyValue(getPropertyName(PROP_PARA_CONNECT_BORDERS)) >>= bConnectBorders;
9391
9392 if (bConnectBorders)
9393 return false;
9394
9395 // Previous paragraph has border in between. Current one also has (since this
9396 // method is called). So current paragraph will get border above, but
9397 // also need to ensure, that no unexpected bottom border are remaining in previous
9398 // paragraph: since ParaIsConnectBorder=false it will be displayed in unexpected way.
9399 m_xPreviousParagraph->setPropertyValue(getPropertyName(PROP_BOTTOM_BORDER), uno::Any(table::BorderLine2()));
9400
9401 return true;
9402}
9403
9405{
9406 auto const& pHandler = getThemeHandler();
9407 if (pHandler)
9408 return pHandler->getFontNameForTheme(id);
9409 return OUString();
9410}
9411
9412}
9413
9414/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::vector< css::beans::PropertyValue > PropertyValueVector_t
FieldId eFieldId
const char * cFieldServiceName
OptionalString sType
constexpr sal_Int8 header[]
#define WW_OUTLINE_MAX
OUString sCharStyleName
PropertiesInfo aProperties
Reference< XInputStream > xStream
HRESULT createInstance(REFIID iid, Ifc **ppIfc)
constexpr OUStringLiteral sServiceName
double d
SvStream & WriteStream(SvStream &rStream)
css::uno::Sequence< css::beans::PropertyValue > getAsConstPropertyValueList() const
const_iterator find(const Value &x) const
const_iterator end() const
std::pair< const_iterator, bool > insert(Value &&x)
css::uno::Any getProperty(sal_Int32 nPropId)
T * get() const
bool is() const
static bool IsFuzzing()
static constexpr OUStringLiteral PROP_URL
static constexpr OUStringLiteral PROP_DOCUMENTBASEURL
static std::unique_ptr< SvStream > CreateStream(const OUString &rFileName, StreamMode eOpenMode, css::uno::Reference< css::awt::XWindow > xParentWin=nullptr)
Reference to a resource that generates events and sends them to a handler.
static TagLogger & getInstance()
Definition: TagLogger.cxx:95
void startElement(const std::string &name)
Definition: TagLogger.cxx:140
void chars(const std::string &chars)
Definition: TagLogger.cxx:215
void attribute(const std::string &name, const std::string &value)
Definition: TagLogger.cxx:150
void element(const std::string &name)
Definition: TagLogger.cxx:102
void unoPropertySet(const css::uno::Reference< css::beans::XPropertySet > &rPropSet)
Definition: TagLogger.cxx:108
virtual css::uno::Any getAny() const =0
Returns representation of the value as uno::Any.
Class to handle events generated by TableManager::resolveCurrentTable().
virtual void startLevel() override
Start a new table level.
virtual void cellProps(const TablePropertyMapPtr &pProps) override
Handle properties of the current cell.
virtual void endLevel() override
End a table level.
css::uno::Reference< css::text::XTextAppend > GetCurrentXText()
css::uno::Reference< css::container::XNameContainer > m_xCharacterStyles
void SetLineNumbering(sal_Int32 nLnnMod, sal_uInt32 nLnc, sal_Int32 ndxaLnn)
sal_Int32 getCurrentNumberingProperty(const OUString &aProp)
Get a property of the current numbering style's current level.
void handleRubyEQField(const FieldContextPtr &pContext)
bool m_bParaAutoBefore
Current paragraph has automatic before spacing.
std::vector< css::beans::PropertyValue > m_aFrameProperties
css::uno::Reference< css::text::XTextDocument > m_xTextDocument
void SetNumberFormat(const OUString &rCommand, css::uno::Reference< css::beans::XPropertySet > const &xPropertySet, bool bDetectFormat=false)
std::vector< css::beans::PropertyValue > m_aInteropGrabBag
A toplevel dmapper grabbag, like 'pPr'.
void commentProps(const OUString &sId, const CommentProperties &rProps)
void SetPageMarginTwip(PageMarElement eElement, sal_Int32 nValue)
void SetDocumentSettingsProperty(const OUString &rPropName, const css::uno::Any &rValue)
css::uno::Reference< css::lang::XMultiServiceFactory > m_xTextFactory
css::uno::Reference< css::drawing::XShape > PopPendingShape()
Get the first pending shape, if there are any.
bool handlePreviousParagraphBorderInBetween() const
Check if previous paragraph has borders in between and do the border magic to it if so.
bool m_bIsSplitPara
If the document needs to split paragraph.
void appendTextContent(const css::uno::Reference< css::text::XTextContent > &, const css::uno::Sequence< css::beans::PropertyValue > &)
bool isBreakDeferred(BreakType deferredBreakType)
void RegisterFrameConversion(css::uno::Reference< css::text::XTextRange > const &xFrameStartRange, css::uno::Reference< css::text::XTextRange > const &xFrameEndRange, std::vector< css::beans::PropertyValue > &&aFrameProperties)
void HandleLineBreak(const PropertyMapPtr &pPropertyMap)
Handles <w:br>.
void setPermissionRangeEdGrp(const OUString &group)
bool IsSdtEndBefore()
Check if "SdtEndBefore" property is set.
std::optional< sal_Int16 > PopFrameDirection()
void HandlePTab(sal_Int32 nAlignment)
Handles <w:ptab>.
OUString GetListStyleName(sal_Int32 nListId)
ListsManager::Pointer const & GetListTable()
void InitTabStopFromStyle(const css::uno::Sequence< css::style::TabStop > &rInitTabStops)
std::stack< HeaderFooterContext > m_aHeaderFooterStack
std::queue< OUString > m_aPositivePercentages
ST_PositivePercentage values we received.
std::stack< std::vector< RedlineParamsPtr > > m_aRedlines
bool m_bSdt
If the current paragraph is inside a structured document element.
bool m_bFirstParagraphInCell
Current paragraph in a table is first paragraph of a cell.
std::unordered_map< sal_Int32, AnnotationPosition > m_aAnnotationPositions
OUString ConvertTOCStyleName(OUString const &)
void IncorporateTabStop(const DeletableTabStop &aTabStop)
const std::stack< BookmarkInsertPosition > & GetSdtStarts() const
Gives access to the currently open run/inline SDTs.
sal_Int16 GetListLevel(const StyleSheetEntryPtr &pEntry, const PropertyMapPtr &pParaContext=nullptr)
Return the paragraph's list level (from styles, unless pParacontext is provided).
void CheckParaMarkerRedline(css::uno::Reference< css::text::XTextRange > const &xRange)
css::uno::Reference< css::text::XTextRange > m_xSdtEntryStart
void ValidateListLevel(const OUString &sStyleIdentifierD)
void SetCurrentRedlineInitials(const OUString &sInitials)
PropertyMapPtr GetTopContextOfType(ContextType eId)
void SetFieldResult(OUString const &rResult)
void handleFieldFormula(const FieldContextPtr &pContext, css::uno::Reference< css::beans::XPropertySet > const &xFieldProperties)
void enableInteropGrabBag(const OUString &aName)
Enable, disable and check status of grabbags.
writerfilter::ooxml::OOXMLDocument * getDocumentReference() const
enum writerfilter::dmapper::DomainMapper_Impl::HeaderFooterImportState m_eInHeaderFooterImport
OUString m_aInteropGrabBagName
Name of m_aInteropGrabBag.
std::deque< FieldContextPtr > m_aFieldStack
std::unordered_map< OUString, CommentProperties > m_aCommentProps
void deferBreak(BreakType deferredBreakType)
css::uno::Reference< css::text::XText > m_xBodyText
const css::uno::Reference< css::text::XTextDocument > & GetTextDocument() const
static void fillEmptyFrameProperties(std::vector< css::beans::PropertyValue > &rFrameProperties, bool bSetAnchorToChar)
std::stack< PropertyMapPtr > m_aPropertyStacks[NUMBER_OF_CONTEXTS]
void SetSdt(bool bSdt)
Setter method for m_bSdt.
std::stack< BookmarkInsertPosition > m_xSdtStarts
const css::uno::Reference< css::lang::XMultiServiceFactory > & GetTextFactory() const
bool m_bParaChanged
If the current paragraph has any runs.
void CreateRedline(css::uno::Reference< css::text::XTextRange > const &xRange, const RedlineParamsPtr &pRedline)
void HandleLineBreakClear(sal_Int32 nClear)
Handles <w:br w:clear="...">.
void appendGrabBag(std::vector< css::beans::PropertyValue > &rInteropGrabBag, const OUString &aKey, const OUString &aValue)
Append a property to a sub-grabbag if necessary (e.g. 'lineRule', 'auto')
css::uno::Reference< css::beans::XPropertySet > m_xPreviousParagraph
sal_Int32 m_nTableDepth
This contains the raw table depth.
void CheckRedline(css::uno::Reference< css::text::XTextRange > const &xRange)
void SetIsDummyParaAddedForTableInSectionPage(bool bIsAdded)
void SetIsTextFrameInserted(bool bIsInserted)
Track if a textframe has been inserted into this section.
css::uno::Reference< css::text::XTextRange > m_xGlossaryEntryStart
bool m_bSdtEndDeferred
If we want to set "sdt end" on the next character context.
o3tl::sorted_vector< sal_Int32 > m_aListOverrideApplied
std::stack< std::pair< TextAppendContext, bool > > m_aHeaderFooterTextAppendStack
std::deque< css::uno::Reference< css::drawing::XShape > > m_aPendingShapes
void handleFieldAsk(const FieldContextPtr &pContext, css::uno::Reference< css::uno::XInterface > &xFieldInterface, css::uno::Reference< css::beans::XPropertySet > const &xFieldProperties)
void PushPageFooter(SectionPropertyMap::PageType eType)
css::uno::Any GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara, bool *bIsDocDefault=nullptr)
void PushListProperties(const PropertyMapPtr &pListProperties)
void SetCurrentRedlineRevertProperties(const css::uno::Sequence< css::beans::PropertyValue > &aProperties)
void deferCharacterProperty(sal_Int32 id, const css::uno::Any &value)
Used for attributes/sprms which cannot be evaluated immediately (e.g.
std::vector< css::beans::PropertyValue > MakeFrameProperties(const ParagraphProperties &rProps)
css::uno::Reference< css::embed::XStorage > m_xDocumentStorage
SkipFootnoteSeparator m_eSkipFootnoteState
Skip paragraphs from the <w:separator/> footnote.
void AttachTextBoxContentToShape(css::uno::Reference< css::drawing::XShape > xShape)
bool m_bParaSdtEndDeferred
If we want to set "paragraph sdt end" on the next paragraph context.
css::uno::Sequence< css::style::TabStop > GetCurrentTabStopAndClear()
void setPermissionRangeEd(const OUString &user)
css::uno::Reference< css::beans::XPropertySet > StartIndexSectionChecked(const OUString &sServiceName)
static void handleAuthor(std::u16string_view rFirstParam, css::uno::Reference< css::beans::XPropertySet > const &xFieldProperties, FieldId eFieldId)
::std::vector< DeletableTabStop > m_aCurrentTabStops
void AppendFieldCommand(OUString const &rPartOfCommand)
std::vector< css::beans::PropertyValue > m_aSubInteropGrabBag
A sub-grabbag of m_aInteropGrabBag, like 'spacing'.
css::uno::Reference< css::container::XNameContainer > const & GetCharacterStyles()
DomainMapper_Impl(DomainMapper &rDMapper, css::uno::Reference< css::uno::XComponentContext > xContext, css::uno::Reference< css::lang::XComponent > const &xModel, SourceDocumentType eDocumentType, utl::MediaDescriptor const &rMediaDesc)
DomainMapperTableManager & getTableManager()
void StartCustomFootnote(const PropertyMapPtr pContext)
css::uno::Reference< css::beans::XPropertySet > FindOrCreateFieldMaster(const char *pFieldMasterService, const OUString &rFieldMasterName)
const PropertyMapPtr & GetTopContext() const
void PopFieldContext()
The end of field is reached (cFieldEnd appeared) - the command might still be open.
css::uno::Reference< css::beans::XPropertySet > appendTextSectionAfter(css::uno::Reference< css::text::XTextRange > const &xBefore)
void PushPendingShape(const css::uno::Reference< css::drawing::XShape > &xShape)
Add a pending shape: it's currently inserted into the document, but it should be removed before the i...
void SetIsFirstParagraphInSectionAfterRedline(bool bIsFirstAfterRedline)
bool GetIsFirstParagraphInSection(bool bAfterRedline=false) const
void PushStyleProperties(const PropertyMapPtr &pStyleProperties)
void handleBibliography(const FieldContextPtr &pContext, const OUString &sTOCServiceName)
css::uno::Reference< css::text::XText > const & GetBodyText()
void handleFieldSet(const FieldContextPtr &pContext, css::uno::Reference< css::uno::XInterface > const &xFieldInterface, css::uno::Reference< css::beans::XPropertySet > const &xFieldProperties)
bool m_bIsActualParagraphFramed
Handle redline text portions in a frame, footnotes and redlines: store their data,...
css::uno::Reference< css::text::XTextAppend > const & GetTopTextAppend()
sal_Int32 m_nTableCellDepth
Raw table cell depth.
std::map< sal_Int32, css::uno::Any > m_deferredCharacterProperties
css::uno::Any GetPropertyFromParaStyleSheet(PropertyIds eId)
void AppendFieldResult(std::u16string_view rResult)
void SetCurrentRedlineAuthor(const OUString &sAuthor)
std::optional< sal_Int16 > m_oLineBreakClear
void CheckUnregisteredFrameConversion(bool bPreventOverlap=false)
void substream(Id rName, ::writerfilter::Reference< Stream >::Pointer_t const &ref)
std::vector< AnchoredObjectsInfo > m_aAnchoredObjectAnchors
Paragraphs with anchored objects in the current section.
std::deque< css::uno::Any > m_aStoredRedlines[StoredRedlines::NONE]
css::uno::Reference< css::text::XTextRange > m_xInsertTextRange
void PushPageHeaderFooter(bool bHeader, SectionPropertyMap::PageType eType)
css::uno::Reference< css::beans::XPropertySet > m_xDocumentSettings
css::uno::Reference< css::beans::XPropertySet > m_xAnnotationField
void PushShapeContext(const css::uno::Reference< css::drawing::XShape > &xShape)
std::pair< OUString, OUString > m_aAligns
ST_AlignH/V values we received.
std::pair< OUString, OUString > m_aPositionOffsets
ST_PositionOffset values we received.
std::unique_ptr< ThemeHandler > const & getThemeHandler()
bool m_bStartedTOC
If we got any text that is the pre-rendered result of the TOC field.
void handleToc(const FieldContextPtr &pContext, const OUString &sTOCServiceName)
void PushFieldContext()
A field context starts with a cFieldStart.
css::uno::Any GetPropertyFromCharStyleSheet(PropertyIds eId, const PropertyMapPtr &rContext)
void appendOLE(const OUString &rStreamName, const std::shared_ptr< OLEHandler > &pOleHandler)
void appendTextPortion(const OUString &rString, const PropertyMapPtr &pPropertyMap)
css::uno::Reference< css::text::XTextCursor > m_xTOCMarkerCursor
void appendGlossaryEntry()
AutoText import: each entry is placed in the separate section.
css::uno::Any GetAnyProperty(PropertyIds eId, const PropertyMapPtr &rContext)
void AddAnnotationPosition(const bool bStart, const sal_Int32 nAnnotationId)
void handleIndex(const FieldContextPtr &pContext, const OUString &sTOCServiceName)
css::uno::Reference< css::text::XTextContent > m_xEmbedded
std::vector< css::uno::Reference< css::drawing::XShape > > m_vTextFramesForChaining
css::uno::Reference< css::container::XNameContainer > const & GetPageStyles()
bool CopyTemporaryNotes(css::uno::Reference< css::text::XFootnote > xNoteSrc, css::uno::Reference< css::text::XFootnote > xNoteDest)
void startOrEndPermissionRange(sal_Int32 permissinId)
void handleAutoNum(const FieldContextPtr &pContext, css::uno::Reference< css::uno::XInterface > const &xFieldInterface, css::uno::Reference< css::beans::XPropertySet > const &xFieldProperties)
void SetFieldFFData(const FFDataHandler::Pointer_t &pFFDataHandler)
void processDeferredCharacterProperties(bool bCharContext=true)
Processes properties deferred using deferCharacterProperty().
bool m_bHasFtn
If the current section has footnotes.
bool m_bParaSectpr
If the current paragraph contains section property definitions.
bool m_bParaWithInlineObject
Current paragraph had at least one inline object in it.
std::stack< TextAppendContext > m_aTextAppendStack
void UpdateEmbeddedShapeProps(const css::uno::Reference< css::drawing::XShape > &xShape)
css::uno::Reference< css::text::XTextRange > m_xFrameStartRange
css::uno::Reference< css::text::XTextRange > m_xFrameEndRange
bool m_bIsDecimalComma
Type of decimal symbol associated to the document language in Writer locale definition.
void finishParagraph(const PropertyMapPtr &pPropertyMap, const bool bRemove=false, const bool bNoNumbering=false)
void PushPageHeader(SectionPropertyMap::PageType eType)
OUString convertFieldFormula(const OUString &input)
Converts a Microsoft Word field formula into LibreOffice syntax.
StyleSheetTablePtr const & GetStyleSheetTable()
tools::SvRef< DomainMapperTableHandler > m_pTableHandler
OUString extractTocTitle()
Returns title of the TOC placed in paragraph(s) before TOC field inside STD-frame.
std::queue< css::uno::Reference< css::text::XTextFrame > > m_xPendingTextBoxFrames
sal_Int32 getNumberingProperty(const sal_Int32 nListId, sal_Int32 nListLevel, const OUString &aProp)
void clearDeferredBreak(BreakType deferredBreakType)
css::uno::Reference< css::beans::XPropertySet > const & GetDocumentSettings()
sal_Int32 m_nLastTableCellParagraphDepth
Table cell depth of the last finished paragraph.
void ImportGraphic(const writerfilter::Reference< Properties >::Pointer_t &)
bool m_bSaxError
SAXException was seen so document will be abandoned.
bool m_bRemoveThisParagraph
This is a continuation of already finished paragraph - e.g., first in an index section.
void handleDocProperty(const FieldContextPtr &pContext, OUString const &rFirstParam, css::uno::Reference< css::uno::XInterface > &xFieldInterface)
bool m_bCheckFirstFootnoteTab
If the next tab should be ignored, used for footnotes.
void HandleAltChunk(const OUString &rStreamName)
Handles <w:altChunk>.
bool m_bParaHadField
Current paragraph had at least one field in it.
writerfilter::ooxml::OOXMLDocument * m_pOOXMLDocument
css::uno::Reference< css::container::XNameContainer > m_xPageStyles1
void SetCurrentRedlineDate(const OUString &sDate)
void SetBookmarkName(const OUString &rBookmarkName)
void setParaSdtEndDeferred(bool bParaSdtEndDeferred)
void ClearPreviousParagraph()
Forget about the previous paragraph, as it's not inside the same start/end node.
void CloseFieldCommand()
The field command has to be closed (cFieldSep appeared).
std::stack< AnchoredContext > m_aAnchoredStack
css::uno::Reference< css::beans::XPropertySet > createSectionForRange(css::uno::Reference< css::text::XTextRange > xStart, css::uno::Reference< css::text::XTextRange > xEnd, const OUString &sObjectType, bool stepLeft)
css::uno::Reference< css::uno::XComponentContext > m_xComponentContext
GraphicZOrderHelper * graphicZOrderHelper()
OUString getOrCreateCharStyle(PropertyValueVector_t &rCharProperties, bool bAlwaysCreate)
void processDeferredCharacterProperties(const std::map< sal_Int32, css::uno::Any > &rDeferredCharacterProperties, bool bCharContext=true)
::std::vector< OUString > DropDownEntries_t
FieldContext(css::uno::Reference< css::text::XTextRange > xStart)
void AppendCommand(std::u16string_view rPart)
css::uno::Reference< css::text::XTextField > m_xTextField
PropertyMapPtr m_pProperties
(Character) properties of the field itself.
::std::vector< OUString > GetCommandParts() const
void CacheVariableValue(const css::uno::Any &rAny)
void SetTextField(css::uno::Reference< css::text::XTextField > const &xTextField)
sal_Int32 findZOrder(sal_Int32 relativeHeight, bool bOldStyle=false)
void addItem(css::uno::Reference< css::beans::XPropertySet > const &props, sal_Int32 relativeHeight)
Storage for state that is relevant outside a header/footer, but not inside it.
HeaderFooterContext(bool bTextInserted, sal_Int32 nTableDepth)
This class provides access to the defined numbering styles.
const OUString & GetParaStyleName() const
void SetParaStyleName(const OUString &rSet)
Helper to create form controls from w:sdt tokens.
Definition: SdtHelper.hxx:61
const css::uno::Reference< css::text::XTextRange > & GetStartingRange() const
void ClearHeaderFooterLinkToPrevious(bool bHeader, PageType eType)
void addRelativeWidthShape(css::uno::Reference< css::drawing::XShape > xShape)
css::uno::Reference< css::beans::XPropertySet > GetPageStyle(DomainMapper_Impl &rDM_Impl, bool bFirst)
void SetStart(const css::uno::Reference< css::text::XTextRange > &xRange)
void handle(const css::uno::Reference< css::text::XTextRange > &xParagraph)
Set m_aAttributes as RDF statements on xParagraph.
void handle(const css::uno::Reference< css::text::XTextRange > &rHandle)
Set the current handle.
void setCellLastParaAfterAutospacing(bool bIsAfterAutospacing)
void setHandler(const tools::SvRef< DomainMapperTableHandler > &pTableDataHandler)
Set handler for resolveCurrentTable.
Any value
#define SAL_NEWLINE_STRING
int nCount
#define suppress_fun_call_w_exception(expr)
#define TOOLS_WARN_EXCEPTION(area, stream)
#define DBG_UNHANDLED_EXCEPTION(...)
Reference< XComponentContext > const m_xComponentContext
OString sFormula
OString right
OString top
OString bottom
float u
DocumentType eType
sal_Int16 nValue
OUString sName
OUString sDisplayName
const char * name
sal_Int32 nIndex
OUString aName
void * p
sal_uInt16 nPos
void SAL_CALL changed(::sal_Int32 ID, const css::uno::Sequence< css::beans::NamedValue > &Properties) override
OUString sSuffix
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
#define SAL_N_ELEMENTS(arr)
if(aStr !=aBuf) UpdateName_Impl(m_xFollowLb.get()
def text(shape, orig_st)
std::vector< HistoryItem > GetList(EHistoryType eHistory)
double getLength(const B2DPolygon &rCandidate)
size
none
Reference< XMultiServiceFactory > getProcessServiceFactory()
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
css::uno::Sequence< css::beans::PropertyValue > InitPropertySequence(::std::initializer_list< ::std::pair< OUString, css::uno::Any > > vInit)
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
int i
std::u16string_view findQuotedText(std::u16string_view rCommand, std::u16string_view cStartQuote, const sal_Unicode uEndQuote)
EquationResult ParseCombinedChars(const OUString &rStr)
OUString CreateDOCXStyleId(std::u16string_view const aName)
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
std::basic_string_view< charT, traits > trim(std::basic_string_view< charT, traits > str)
constexpr bool ends_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept
sal_Int32 toInt32(std::u16string_view str, sal_Int16 radix=10)
constexpr bool starts_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
constexpr Point convert(const Point &rPoint, o3tl::Length eFrom, o3tl::Length eTo)
enumrange< T >::Iterator begin(enumrange< T >)
sal_uInt32 toUInt32(std::u16string_view str, sal_Int16 radix=10)
group
dictionary props
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
OUString ConvertMSFormatStringToSO(const OUString &rFormat, lang::Locale &rLocale, bool bHijri)
text::RubyAdjust convertRubyAlign(sal_Int32 nIntValue)
util::DateTime ConvertDateStringToDateTime(std::u16string_view rDateTime)
static sal_Int16 lcl_ParseNumberingType(std::u16string_view rCommand)
static void lcl_CopyRedlines(uno::Reference< text::XText > const &xSrc, std::deque< css::uno::Any > &rRedlines, std::vector< sal_Int32 > &redPos, std::vector< sal_Int32 > &redLen, sal_Int32 &redIdx)
static OUString UnquoteFieldText(std::u16string_view s)
static bool IsFieldNestingAllowed(const FieldContextPtr &pOuter, const FieldContextPtr &pInner)
Decides if the pInner field inside pOuter is allowed in Writer core, depending on their type.
static void lcl_PasteRedlines(uno::Reference< text::XText > const &xDest, std::deque< css::uno::Any > &rRedlines, std::vector< sal_Int32 > &redPos, std::vector< sal_Int32 > &redLen, sal_Int32 redIdx)
OUString getPropertyName(PropertyIds eId)
static const FieldConversionMap_t & lcl_GetFieldConversion()
static void lcl_handleTextField(const uno::Reference< beans::XPropertySet > &rxFieldProps, const FFDataHandler::Pointer_t &pFFDataHandler)
constexpr sal_Int32 DEFAULT_FRAME_MIN_WIDTH
static OUString lcl_FindLastBookmark(const uno::Reference< text::XTextCursor > &xCursor, bool bAlreadyExpanded)
static auto InsertFieldmark(std::stack< TextAppendContext > &rTextAppendStack, uno::Reference< text::XFormField > const &xFormField, uno::Reference< text::XTextRange > const &xStartRange, std::optional< FieldId > const oFieldId) -> void
bool isCharacterProperty(const PropertyIds eId)
static uno::Sequence< beans::PropertyValues > lcl_createTOXLevelHyperlinks(bool bHyperlinks, const OUString &sChapterNoSeparator, const uno::Sequence< beans::PropertyValues > &aLevel, const std::optional< style::TabStop > numtab)
static auto FilterChars(std::u16string_view const &rStyleName) -> OUString
This is a heuristic to find Word's w:styleId value from localised style name.
static size_t nextCode(std::u16string_view rCommand, size_t pos)
std::unordered_map< OUString, FieldConversion > FieldConversionMap_t
static OUString lcl_ExtractToken(std::u16string_view rCommand, size_t &rIndex, bool &rHaveToken, bool &rIsSwitch)
static util::DateTime lcl_dateTimeFromSerial(const double &dSerial)
static auto PopFieldmark(std::stack< TextAppendContext > &rTextAppendStack, uno::Reference< text::XTextCursor > const &xCursor, std::optional< FieldId > const oFieldId) -> void
static void lcl_convertToNoteIndices(std::deque< sal_Int32 > &rNoteIds, sal_Int32 &rFirstNoteIndex)
static FieldContextPtr GetParentFieldContext(const std::deque< FieldContextPtr > &rFieldStack)
Gives access to the parent field context of the topmost one, if there is any.
std::tuple< OUString, std::vector< OUString >, std::vector< OUString > > splitFieldCommand(std::u16string_view rCommand)
static OUString lcl_ParseFormat(const OUString &rCommand)
static const FieldConversionMap_t & lcl_GetEnhancedFieldConversion()
@ PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING
static void lcl_AddRange(ParagraphPropertiesPtr const &pToBeSavedProperties, uno::Reference< text::XTextAppend > const &xTextAppend, TextAppendContext const &rAppendContext)
SkipFootnoteSeparator
Two special footnotes are a separator line, and a continuation line.
constexpr sal_Int32 DEFAULT_FRAME_MIN_HEIGHT
static void lcl_MoveBorderPropertiesToFrame(std::vector< beans::PropertyValue > &rFrameProperties, uno::Reference< text::XTextRange > const &xStartTextRange, uno::Reference< text::XTextRange > const &xEndTextRange, bool bIsRTFImport)
static sal_Int32 lcl_getListId(const StyleSheetEntryPtr &rEntry, const StyleSheetTablePtr &rStyleTable, bool &rNumberingFromBaseStyle)
Check if the style or its parent has a list id, recursively.
static OUString lcl_ExtractVariableAndHint(std::u16string_view rCommand, OUString &rHint)
static bool lcl_FindInCommand(std::u16string_view rCommand, sal_Unicode cSwitch, OUString &rValue)
static uno::Any lcl_GetPropertyFromParaStyleSheetNoNum(PropertyIds eId, StyleSheetEntryPtr pEntry, const StyleSheetTablePtr &rStyleSheet)
Very similar to DomainMapper_Impl::GetPropertyFromStyleSheet It is focused on paragraph properties se...
static OUString lcl_trim(std::u16string_view sValue)
std::multimap< sal_Int32, OUString > TOCStyleMap
ContextType
property stack element
static uno::Any lcl_getGrabBagValue(const uno::Sequence< beans::PropertyValue > &grabBag, OUString const &name)
@ FIELD_XE
Bibliography.
Definition: FieldTypes.hxx:293
static ww::eField GetWW8FieldId(OUString const &rType)
static void lcl_linenumberingHeaderFooter(const uno::Reference< container::XNameContainer > &xStyles, const OUString &rname, DomainMapper_Impl *dmapper)
constexpr sal_Int32 DEFAULT_VALUE
static size_t findCode(std::u16string_view rCommand, sal_Unicode cSwitch)
static void lcl_handleDropdownField(const uno::Reference< beans::XPropertySet > &rxFieldProps, const FFDataHandler::Pointer_t &pFFDataHandler)
eFOOTREF
eAUTONUMLGL
eMERGEDATA
eKEYWORDS
eMERGEFIELD
eINCLUDETIFF
eDOCVARIABLE
eGOTOBUTTON
ePRINTDATE
eADDRESSBLOCK
eFORMCHECKBOX
eSUBJECT
eNUMPAGES
eCREATEDATE
eDOCPROPERTY
eCONTROL
eNOTEREF
eSTYLEREF
eBIDIOUTLINE
eHTMLCONTROL
ePRIVATE
eCITATION
eEDITTIME
eUSERNAME
eDDEAUTOREF
eGLOSSREF
eTEMPLATE
ePAGEREF
eCOMPARE
eUSERINITIALS
eFORMTEXT
eAUTONUM
eBARCODE
eAUTOTEXT
eAUTONUMOUT
eNUMCHARS
eMERGEREC
eINCLUDEPICTURE
eMERGESEQ
eNUMWORDS
eLASTSAVEDBY
eFILESIZE
eAUTOTEXTLIST
eDATABASE
eLISTNUM
eFILENAME
eSECTIONPAGES
eINCLUDETEXT
eMERGEINC
eMACROBUTTON
eBIBLIOGRAPHY
eSAVEDATE
eHYPERLINK
eSUBSCRIBER
eADVANCE
eUSERADDRESS
eGREETINGLINE
eCOMMENTS
eSECTION
eFORMDROPDOWN
HashMap_OWString_Interface aMap
constexpr OUStringLiteral ODF_CODE_PARAM
constexpr OUStringLiteral ODF_UNHANDLED
constexpr OUStringLiteral ODF_ID_PARAM
sal_Int16 nId
DefTokenId nToken
QPRO_FUNC_TYPE nType
sal_uInt32 Id
bool hasValue()
Object Value
A container for the extended comment properties linked to the last paragraph of a comment.
Stores original/in-file-format info about a single anchored object.
css::uno::Reference< css::text::XTextContent > m_xAnchoredObject
Stores info about objects anchored to a given paragraph.
std::vector< AnchoredObjectInfo > m_aAnchoredObjects
css::uno::Reference< css::text::XTextRange > m_xParagraph
Stores the start/end positions of an annotation before its insertion.
css::uno::Reference< css::text::XTextRange > m_xEnd
css::uno::Reference< css::text::XTextRange > m_xStart
helper to remember bookmark start position
css::uno::Reference< css::text::XTextRange > m_xTextRange
Information about a paragraph to be finished after a field end.
Information about a paragraph to be finished after a table end.
css::uno::Reference< css::text::XTextRange > xInsertPosition
std::vector< AnchoredObjectInfo > m_aAnchoredObjects
Objects anchored to the current paragraph, may affect the paragraph spacing.
css::uno::Reference< css::text::XTextAppend > xTextAppend
css::uno::Reference< css::text::XParagraphCursor > xCursor
Reference< XFrame > xFrame
Reference< XModel > xModel
OUString aCommand
OUString Name
unsigned char sal_uInt8
sal_uInt16 sal_Unicode
signed char sal_Int8
Any result
OUString sId
sal_uInt64 left
size_t pos