android: Update app-specific/MIME type icons
[LibreOffice.git] / writerfilter / source / dmapper / PropertyMap.cxx
blobd28d96b1db7103629f3915b4d98b41178a72bfc1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <string_view>
24 #include "PropertyMap.hxx"
25 #include "TagLogger.hxx"
26 #include <ooxml/resourceids.hxx>
27 #include "DomainMapper_Impl.hxx"
28 #include "ConversionHelper.hxx"
29 #include <editeng/boxitem.hxx>
30 #include <i18nutil/paper.hxx>
31 #include <osl/diagnose.h>
32 #include <rtl/ustring.hxx>
33 #include <sal/log.hxx>
34 #include <com/sun/star/beans/PropertyAttribute.hpp>
35 #include <com/sun/star/beans/PropertyValue.hpp>
36 #include <com/sun/star/beans/XMultiPropertySet.hpp>
37 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
38 #include <com/sun/star/table/BorderLine2.hpp>
39 #include <com/sun/star/container/XEnumeration.hpp>
40 #include <com/sun/star/container/XEnumerationAccess.hpp>
41 #include <com/sun/star/container/XNameContainer.hpp>
42 #include <com/sun/star/style/BreakType.hpp>
43 #include <com/sun/star/style/PageStyleLayout.hpp>
44 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
45 #include <com/sun/star/table/ShadowFormat.hpp>
46 #include <com/sun/star/text/RelOrientation.hpp>
47 #include <com/sun/star/text/HoriOrientation.hpp>
48 #include <com/sun/star/text/HorizontalAdjust.hpp>
49 #include <com/sun/star/text/SizeType.hpp>
50 #include <com/sun/star/text/VertOrientation.hpp>
51 #include <com/sun/star/text/WritingMode.hpp>
52 #include <com/sun/star/text/WritingMode2.hpp>
53 #include <com/sun/star/text/XRedline.hpp>
54 #include <com/sun/star/text/XTextColumns.hpp>
55 #include <com/sun/star/text/XText.hpp>
56 #include <com/sun/star/text/XTextFrame.hpp>
57 #include <com/sun/star/text/XTextTablesSupplier.hpp>
58 #include <com/sun/star/text/TextGridMode.hpp>
59 #include <com/sun/star/text/XTextCopy.hpp>
60 #include <com/sun/star/style/VerticalAlignment.hpp>
61 #include <comphelper/sequence.hxx>
62 #include <comphelper/propertyvalue.hxx>
63 #include <comphelper/diagnose_ex.hxx>
64 #include "PropertyMapHelper.hxx"
65 #include <o3tl/sorted_vector.hxx>
66 #include <o3tl/unit_conversion.hxx>
67 #include <utility>
69 using namespace com::sun::star;
71 namespace writerfilter::dmapper {
73 uno::Sequence< beans::PropertyValue > PropertyMap::GetPropertyValues( bool bCharGrabBag )
75 using comphelper::makePropertyValue;
77 if ( !m_aValues.empty() || m_vMap.empty() )
78 return comphelper::containerToSequence( m_aValues );
80 size_t nCharGrabBag = 0;
81 size_t nParaGrabBag = 0;
82 size_t nCellGrabBag = 0;
83 size_t nRowGrabBag = 0;
85 const PropValue* pParaStyleProp = nullptr;
86 const PropValue* pCharStyleProp = nullptr;
87 const PropValue* pNumRuleProp = nullptr;
89 m_aValues.reserve( m_vMap.size() );
90 for ( const auto& rPropPair : m_vMap )
92 if ( rPropPair.second.getGrabBagType() == CHAR_GRAB_BAG )
93 nCharGrabBag++;
94 else if ( rPropPair.second.getGrabBagType() == PARA_GRAB_BAG )
95 nParaGrabBag++;
96 else if ( rPropPair.second.getGrabBagType() == CELL_GRAB_BAG )
97 nCellGrabBag++;
98 else if ( rPropPair.first == PROP_CELL_INTEROP_GRAB_BAG )
100 uno::Sequence< beans::PropertyValue > aSeq;
101 rPropPair.second.getValue() >>= aSeq;
102 nCellGrabBag += aSeq.getLength();
104 else if ( rPropPair.second.getGrabBagType() == ROW_GRAB_BAG )
105 nRowGrabBag++;
107 if ( rPropPair.first == PROP_PARA_STYLE_NAME ) pParaStyleProp = &rPropPair.second;
108 if ( rPropPair.first == PROP_CHAR_STYLE_NAME ) pCharStyleProp = &rPropPair.second;
109 if ( rPropPair.first == PROP_NUMBERING_RULES ) pNumRuleProp = &rPropPair.second;
112 // Style names have to be the first elements within the property sequence
113 // otherwise they will overwrite 'hard' attributes
114 if ( pParaStyleProp != nullptr )
115 m_aValues.push_back( makePropertyValue( getPropertyName( PROP_PARA_STYLE_NAME ), pParaStyleProp->getValue() ) );
116 if ( pCharStyleProp != nullptr )
117 m_aValues.push_back( makePropertyValue( getPropertyName( PROP_CHAR_STYLE_NAME ), pCharStyleProp->getValue() ) );
118 if ( pNumRuleProp != nullptr )
119 m_aValues.push_back( makePropertyValue(getPropertyName( PROP_NUMBERING_RULES ), pNumRuleProp->getValue() ) );
121 // If there are any grab bag properties, we need one slot for them.
122 uno::Sequence< beans::PropertyValue > aCharGrabBagValues( nCharGrabBag );
123 uno::Sequence< beans::PropertyValue > aParaGrabBagValues( nParaGrabBag );
124 uno::Sequence< beans::PropertyValue > aCellGrabBagValues( nCellGrabBag );
125 uno::Sequence< beans::PropertyValue > aRowGrabBagValues ( nRowGrabBag );
126 beans::PropertyValue* pCharGrabBagValues = aCharGrabBagValues.getArray();
127 beans::PropertyValue* pParaGrabBagValues = aParaGrabBagValues.getArray();
128 beans::PropertyValue* pCellGrabBagValues = aCellGrabBagValues.getArray();
129 beans::PropertyValue* pRowGrabBagValues = aRowGrabBagValues.getArray();
130 // Record index for the next property to be added in each grab bag.
131 sal_Int32 nRowGrabBagValue = 0;
132 sal_Int32 nCellGrabBagValue = 0;
133 sal_Int32 nParaGrabBagValue = 0;
134 sal_Int32 nCharGrabBagValue = 0;
136 for ( const auto& rPropPair : m_vMap )
138 if ( rPropPair.first != PROP_PARA_STYLE_NAME &&
139 rPropPair.first != PROP_CHAR_STYLE_NAME &&
140 rPropPair.first != PROP_NUMBERING_RULES )
142 if ( rPropPair.second.getGrabBagType() == CHAR_GRAB_BAG )
144 if ( bCharGrabBag )
146 pCharGrabBagValues[nCharGrabBagValue].Name = getPropertyName( rPropPair.first );
147 pCharGrabBagValues[nCharGrabBagValue].Value = rPropPair.second.getValue();
148 ++nCharGrabBagValue;
151 else if ( rPropPair.second.getGrabBagType() == PARA_GRAB_BAG )
153 pParaGrabBagValues[nParaGrabBagValue].Name = getPropertyName( rPropPair.first );
154 pParaGrabBagValues[nParaGrabBagValue].Value = rPropPair.second.getValue();
155 ++nParaGrabBagValue;
157 else if ( rPropPair.second.getGrabBagType() == CELL_GRAB_BAG )
159 pCellGrabBagValues[nCellGrabBagValue].Name = getPropertyName( rPropPair.first );
160 pCellGrabBagValues[nCellGrabBagValue].Value = rPropPair.second.getValue();
161 ++nCellGrabBagValue;
163 else if ( rPropPair.second.getGrabBagType() == ROW_GRAB_BAG )
165 pRowGrabBagValues[nRowGrabBagValue].Name = getPropertyName( rPropPair.first );
166 pRowGrabBagValues[nRowGrabBagValue].Value = rPropPair.second.getValue();
167 ++nRowGrabBagValue;
169 else if ( rPropPair.first == PROP_CELL_INTEROP_GRAB_BAG )
171 uno::Sequence< beans::PropertyValue > aSeq;
172 rPropPair.second.getValue() >>= aSeq;
173 std::copy(std::cbegin(aSeq), std::cend(aSeq), pCellGrabBagValues + nCellGrabBagValue);
174 nCellGrabBagValue += aSeq.getLength();
176 else
178 m_aValues.push_back( makePropertyValue( getPropertyName( rPropPair.first ), rPropPair.second.getValue() ) );
183 if ( nCharGrabBag && bCharGrabBag )
184 m_aValues.push_back( makePropertyValue( "CharInteropGrabBag", uno::Any( aCharGrabBagValues ) ) );
186 if ( nParaGrabBag )
187 m_aValues.push_back( makePropertyValue( "ParaInteropGrabBag", uno::Any( aParaGrabBagValues ) ) );
189 if ( nCellGrabBag )
190 m_aValues.push_back( makePropertyValue( "CellInteropGrabBag", uno::Any( aCellGrabBagValues ) ) );
192 if ( nRowGrabBag )
193 m_aValues.push_back( makePropertyValue( "RowInteropGrabBag", uno::Any( aRowGrabBagValues ) ) );
195 return comphelper::containerToSequence( m_aValues );
198 std::vector< PropertyIds > PropertyMap::GetPropertyIds()
200 std::vector< PropertyIds > aRet;
201 for ( const auto& rPropPair : m_vMap )
202 aRet.push_back( rPropPair.first );
203 return aRet;
206 #ifdef DBG_UTIL
207 static void lcl_AnyToTag( const uno::Any& rAny )
209 try {
210 sal_Int32 aInt = 0;
211 if ( rAny >>= aInt )
213 TagLogger::getInstance().attribute( "value", rAny );
215 else
217 TagLogger::getInstance().attribute( "unsignedValue", 0 );
220 sal_uInt32 auInt = 0;
221 rAny >>= auInt;
222 TagLogger::getInstance().attribute( "unsignedValue", auInt );
224 float aFloat = 0.0f;
225 if ( rAny >>= aFloat )
227 TagLogger::getInstance().attribute( "floatValue", rAny );
229 else
231 TagLogger::getInstance().attribute( "unsignedValue", 0 );
234 OUString aStr;
235 rAny >>= aStr;
236 TagLogger::getInstance().attribute( "stringValue", aStr );
238 catch ( ... )
242 #endif
244 void PropertyMap::Insert( PropertyIds eId, const uno::Any& rAny, bool bOverwrite, GrabBagType i_GrabBagType, bool bDocDefault )
246 #ifdef DBG_UTIL
247 const OUString& rInsert = getPropertyName(eId);
249 TagLogger::getInstance().startElement("propertyMap.insert");
250 TagLogger::getInstance().attribute("name", rInsert);
251 lcl_AnyToTag(rAny);
252 TagLogger::getInstance().endElement();
253 #endif
255 if ( !bOverwrite )
256 m_vMap.insert(std::make_pair(eId, PropValue(rAny, i_GrabBagType, bDocDefault)));
257 else
258 m_vMap[eId] = PropValue(rAny, i_GrabBagType);
260 Invalidate();
263 void PropertyMap::Erase( PropertyIds eId )
265 // Safe call to erase, it throws no exceptions, even if eId is not in m_vMap
266 m_vMap.erase(eId);
268 Invalidate();
271 std::optional< PropertyMap::Property > PropertyMap::getProperty( PropertyIds eId ) const
273 std::map< PropertyIds, PropValue >::const_iterator aIter = m_vMap.find( eId );
274 if ( aIter == m_vMap.end() )
275 return std::optional<Property>();
276 else
277 return std::make_pair( eId, aIter->second.getValue() );
280 bool PropertyMap::isSet( PropertyIds eId) const
282 return m_vMap.find( eId ) != m_vMap.end();
285 bool PropertyMap::isDocDefault( PropertyIds eId ) const
287 std::map< PropertyIds, PropValue >::const_iterator aIter = m_vMap.find( eId );
288 if ( aIter == m_vMap.end() )
289 return false;
290 else
291 return aIter->second.getIsDocDefault();
294 #ifdef DBG_UTIL
295 void PropertyMap::dumpXml() const
297 TagLogger::getInstance().startElement( "PropertyMap" );
299 for ( const auto& rPropPair : m_vMap )
301 TagLogger::getInstance().startElement( "property" );
303 TagLogger::getInstance().attribute( "name", getPropertyName( rPropPair.first ) );
305 switch ( rPropPair.first )
307 case PROP_TABLE_COLUMN_SEPARATORS:
308 lcl_DumpTableColumnSeparators( rPropPair.second.getValue() );
309 break;
310 default:
314 sal_Int32 aInt = 0;
315 rPropPair.second.getValue() >>= aInt;
316 TagLogger::getInstance().attribute( "value", aInt );
318 sal_uInt32 auInt = 0;
319 rPropPair.second.getValue() >>= auInt;
320 TagLogger::getInstance().attribute( "unsignedValue", auInt );
322 float aFloat = 0.0;
323 rPropPair.second.getValue() >>= aFloat;
324 TagLogger::getInstance().attribute( "floatValue", aFloat );
326 rPropPair.second.getValue() >>= auInt;
327 TagLogger::getInstance().attribute( "stringValue", std::u16string_view() );
329 catch ( ... )
333 break;
336 TagLogger::getInstance().endElement();
339 TagLogger::getInstance().endElement();
341 #endif
343 void PropertyMap::InsertProps( const PropertyMapPtr& rMap, const bool bOverwrite )
345 if ( !rMap )
346 return;
348 for ( const auto& rPropPair : rMap->m_vMap )
350 if ( bOverwrite || !m_vMap.count(rPropPair.first) )
352 if ( !bOverwrite && !rPropPair.second.getIsDocDefault() )
353 m_vMap.insert(std::make_pair(rPropPair.first, PropValue(rPropPair.second.getValue(), rPropPair.second.getGrabBagType(), true)));
354 else
355 m_vMap[rPropPair.first] = rPropPair.second;
359 insertTableProperties( rMap.get(), bOverwrite );
361 Invalidate();
364 void PropertyMap::insertTableProperties( const PropertyMap*, const bool )
366 #ifdef DBG_UTIL
367 TagLogger::getInstance().element( "PropertyMap.insertTableProperties" );
368 #endif
371 void PropertyMap::printProperties()
373 #ifdef DBG_UTIL
374 TagLogger::getInstance().startElement( "properties" );
376 for ( const auto& rPropPair : m_vMap )
378 SAL_INFO( "writerfilter", getPropertyName( rPropPair.first ) );
380 table::BorderLine2 aLine;
381 sal_Int32 nColor;
382 if ( rPropPair.second.getValue() >>= aLine )
384 TagLogger::getInstance().startElement( "borderline" );
385 TagLogger::getInstance().attribute( "color", aLine.Color );
386 TagLogger::getInstance().attribute( "inner", aLine.InnerLineWidth );
387 TagLogger::getInstance().attribute( "outer", aLine.OuterLineWidth );
388 TagLogger::getInstance().endElement();
390 else if ( rPropPair.second.getValue() >>= nColor )
392 TagLogger::getInstance().startElement( "color" );
393 TagLogger::getInstance().attribute( "number", nColor );
394 TagLogger::getInstance().endElement();
398 TagLogger::getInstance().endElement();
399 #else
400 (void) this; // avoid loplugin:staticmethods
401 #endif
404 SectionPropertyMap::SectionPropertyMap( bool bIsFirstSection )
405 : m_bIsFirstSection( bIsFirstSection )
406 , m_eBorderApply( BorderApply::ToAllInSection )
407 , m_eBorderOffsetFrom( BorderOffsetFrom::Text )
408 , m_bTitlePage( false )
409 , m_nColumnCount( 0 )
410 , m_nColumnDistance( 1249 )
411 , m_bSeparatorLineIsOn( false )
412 , m_bEvenlySpaced( false )
413 , m_nPageNumber( -1 )
414 , m_nPageNumberType( -1 )
415 , m_nBreakType( -1 )
416 , m_nLeftMargin( o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100) )
417 , m_nRightMargin( o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100) )
418 , m_nGutterMargin(0)
419 , m_nTopMargin( o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100) )
420 , m_nBottomMargin( o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100) )
421 , m_nHeaderTop( o3tl::convert(0.5, o3tl::Length::in, o3tl::Length::mm100) )
422 , m_nHeaderBottom( o3tl::convert(0.5, o3tl::Length::in, o3tl::Length::mm100) )
423 , m_nGridType( 0 )
424 , m_nGridLinePitch( 1 )
425 , m_nDxtCharSpace( 0 )
426 , m_bGridSnapToChars( true )
427 , m_nLnnMod( 0 )
428 , m_nLnc(NS_ooxml::LN_Value_ST_LineNumberRestart_newPage)
429 , m_ndxaLnn( 0 )
430 , m_nLnnMin( 0 )
431 , m_bDynamicHeightTop( true )
432 , m_bDynamicHeightBottom( true )
433 , m_bDefaultHeaderLinkToPrevious( true )
434 , m_bEvenPageHeaderLinkToPrevious( true )
435 , m_bFirstPageHeaderLinkToPrevious( true )
436 , m_bDefaultFooterLinkToPrevious( true )
437 , m_bEvenPageFooterLinkToPrevious( true )
438 , m_bFirstPageFooterLinkToPrevious( true )
440 #ifdef DBG_UTIL
441 static sal_Int32 nNumber = 0;
442 m_nDebugSectionNumber = nNumber++;
443 #endif
445 for ( sal_Int32 nBorder = 0; nBorder < 4; ++nBorder )
447 m_nBorderDistances[nBorder] = -1;
448 m_bBorderShadows[nBorder] = false;
450 // todo: set defaults in ApplyPropertiesToPageStyles
451 // initialize defaults
452 PaperInfo aLetter( PAPER_LETTER );
453 // page height, 1/100mm
454 Insert( PROP_HEIGHT, uno::Any( static_cast<sal_Int32>(aLetter.getHeight()) ) );
455 // page width, 1/100mm
456 Insert( PROP_WIDTH, uno::Any( static_cast<sal_Int32>(aLetter.getWidth()) ) );
457 // page left margin, 1/100 mm
458 Insert( PROP_LEFT_MARGIN, uno::Any( sal_Int32(o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100)) ) );
459 // page right margin, 1/100 mm
460 Insert( PROP_RIGHT_MARGIN, uno::Any( sal_Int32(o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100)) ) );
461 // page top margin, 1/100 mm
462 Insert( PROP_TOP_MARGIN, uno::Any( sal_Int32(o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100)) ) );
463 // page bottom margin, 1/100 mm
464 Insert( PROP_BOTTOM_MARGIN, uno::Any( sal_Int32(o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100)) ) );
465 // page style layout
466 Insert( PROP_PAGE_STYLE_LAYOUT, uno::Any( style::PageStyleLayout_ALL ) );
467 uno::Any aFalse( uno::Any( false ) );
468 Insert( PROP_GRID_DISPLAY, aFalse );
469 Insert( PROP_GRID_PRINT, aFalse );
470 Insert( PROP_GRID_MODE, uno::Any( text::TextGridMode::NONE ) );
472 if ( m_bIsFirstSection )
474 m_sFirstPageStyleName = getPropertyName( PROP_FIRST_PAGE );
475 m_sFollowPageStyleName = getPropertyName( PROP_STANDARD );
479 uno::Reference< beans::XPropertySet > SectionPropertyMap::GetPageStyle( DomainMapper_Impl& rDM_Impl,
480 bool bFirst )
482 const uno::Reference< container::XNameContainer >& xPageStyles = rDM_Impl.GetPageStyles();
483 const uno::Reference < lang::XMultiServiceFactory >& xTextFactory = rDM_Impl.GetTextFactory();
484 uno::Reference< beans::XPropertySet > xRet;
487 if ( bFirst )
489 if ( m_sFirstPageStyleName.isEmpty() && xPageStyles.is() )
491 assert( !rDM_Impl.IsInFootOrEndnote() && "Don't create useless page styles" );
492 m_sFirstPageStyleName = rDM_Impl.GetUnusedPageStyleName();
493 m_aFirstPageStyle.set( xTextFactory->createInstance( "com.sun.star.style.PageStyle" ),
494 uno::UNO_QUERY );
496 // Call insertByName() before GetPageStyle(), otherwise the
497 // first and the follow page style will have the same name, and
498 // insertByName() will fail.
499 if ( xPageStyles.is() )
500 xPageStyles->insertByName( m_sFirstPageStyleName, uno::Any( m_aFirstPageStyle ) );
502 // Ensure that m_aFollowPageStyle has been created
503 GetPageStyle( rDM_Impl, false );
504 // Chain m_aFollowPageStyle to be after m_aFirstPageStyle
505 m_aFirstPageStyle->setPropertyValue( "FollowStyle",
506 uno::Any( m_sFollowPageStyleName ) );
508 else if ( !m_aFirstPageStyle.is() && xPageStyles.is() )
510 xPageStyles->getByName( m_sFirstPageStyleName ) >>= m_aFirstPageStyle;
512 xRet = m_aFirstPageStyle;
514 else
516 if ( m_sFollowPageStyleName.isEmpty() && xPageStyles.is() )
518 assert( !rDM_Impl.IsInFootOrEndnote() && "Don't create useless page styles" );
519 m_sFollowPageStyleName = rDM_Impl.GetUnusedPageStyleName();
520 m_aFollowPageStyle.set( xTextFactory->createInstance( "com.sun.star.style.PageStyle" ),
521 uno::UNO_QUERY );
522 xPageStyles->insertByName( m_sFollowPageStyleName, uno::Any( m_aFollowPageStyle ) );
524 else if ( !m_aFollowPageStyle.is() && xPageStyles.is() )
526 xPageStyles->getByName( m_sFollowPageStyleName ) >>= m_aFollowPageStyle;
528 xRet = m_aFollowPageStyle;
532 catch ( const uno::Exception& )
534 DBG_UNHANDLED_EXCEPTION( "writerfilter" );
537 return xRet;
540 void SectionPropertyMap::SetBorder( BorderPosition ePos, sal_Int32 nLineDistance, const table::BorderLine2& rBorderLine, bool bShadow )
542 m_oBorderLines[ePos] = rBorderLine;
543 m_nBorderDistances[ePos] = nLineDistance;
544 m_bBorderShadows[ePos] = bShadow;
547 void SectionPropertyMap::ApplyBorderToPageStyles( DomainMapper_Impl& rDM_Impl,
548 BorderApply eBorderApply, BorderOffsetFrom eOffsetFrom )
551 page border applies to:
552 nIntValue & 0x07 ->
553 0 all pages in this section
554 1 first page in this section
555 2 all pages in this section but first
556 3 whole document (all sections)
557 nIntValue & 0x18 -> page border depth 0 - in front 1- in back
558 nIntValue & 0xe0 ->
559 page border offset from:
560 0 offset from text
561 1 offset from edge of page
563 uno::Reference< beans::XPropertySet > xFirst;
564 uno::Reference< beans::XPropertySet > xSecond;
565 // todo: negative spacing (from ww8par6.cxx)
566 switch ( eBorderApply )
568 case BorderApply::ToAllInSection: // all styles
569 if ( !m_sFollowPageStyleName.isEmpty() )
570 xFirst = GetPageStyle( rDM_Impl, false );
571 if ( !m_sFirstPageStyleName.isEmpty() )
572 xSecond = GetPageStyle( rDM_Impl, true );
573 break;
574 case BorderApply::ToFirstPageInSection: // first page
575 if ( !m_sFirstPageStyleName.isEmpty() )
576 xFirst = GetPageStyle( rDM_Impl, true );
577 break;
578 case BorderApply::ToAllButFirstInSection: // left and right
579 if ( !m_sFollowPageStyleName.isEmpty() )
580 xFirst = GetPageStyle( rDM_Impl, false );
581 break;
582 default:
583 return;
586 // has to be sorted like enum BorderPosition: l-r-t-b
587 const PropertyIds aBorderIds[4] =
589 PROP_LEFT_BORDER,
590 PROP_RIGHT_BORDER,
591 PROP_TOP_BORDER,
592 PROP_BOTTOM_BORDER
595 const PropertyIds aBorderDistanceIds[4] =
597 PROP_LEFT_BORDER_DISTANCE,
598 PROP_RIGHT_BORDER_DISTANCE,
599 PROP_TOP_BORDER_DISTANCE,
600 PROP_BOTTOM_BORDER_DISTANCE
603 const PropertyIds aMarginIds[4] =
605 PROP_LEFT_MARGIN,
606 PROP_RIGHT_MARGIN,
607 PROP_TOP_MARGIN,
608 PROP_BOTTOM_MARGIN
611 for ( sal_Int32 nBorder = 0; nBorder < 4; ++nBorder )
613 if ( m_oBorderLines[nBorder] )
615 const OUString & sBorderName = getPropertyName( aBorderIds[nBorder] );
616 if ( xFirst.is() )
617 xFirst->setPropertyValue( sBorderName, uno::Any( *m_oBorderLines[nBorder] ) );
618 if ( xSecond.is() )
619 xSecond->setPropertyValue( sBorderName, uno::Any( *m_oBorderLines[nBorder] ) );
621 if ( m_nBorderDistances[nBorder] >= 0 )
623 sal_uInt32 nLineWidth = 0;
624 if ( m_oBorderLines[nBorder] )
625 nLineWidth = m_oBorderLines[nBorder]->LineWidth;
626 if ( xFirst.is() )
627 SetBorderDistance( xFirst, aMarginIds[nBorder], aBorderDistanceIds[nBorder],
628 m_nBorderDistances[nBorder], eOffsetFrom, nLineWidth, rDM_Impl );
629 if ( xSecond.is() )
630 SetBorderDistance( xSecond, aMarginIds[nBorder], aBorderDistanceIds[nBorder],
631 m_nBorderDistances[nBorder], eOffsetFrom, nLineWidth, rDM_Impl );
635 if ( m_bBorderShadows[BORDER_RIGHT] )
637 table::ShadowFormat aFormat = getShadowFromBorder( *m_oBorderLines[BORDER_RIGHT] );
638 if ( xFirst.is() )
639 xFirst->setPropertyValue( getPropertyName( PROP_SHADOW_FORMAT ), uno::Any( aFormat ) );
640 if ( xSecond.is() )
641 xSecond->setPropertyValue( getPropertyName( PROP_SHADOW_FORMAT ), uno::Any( aFormat ) );
645 table::ShadowFormat PropertyMap::getShadowFromBorder( const table::BorderLine2& rBorder )
647 // In Word UI, shadow is a boolean property, in OOXML, it's a boolean
648 // property of each 4 border type, finally in Writer the border is a
649 // property of the page style, with shadow location, distance and
650 // color. See SwWW8ImplReader::SetShadow().
651 table::ShadowFormat aFormat;
652 aFormat.Color = sal_Int32(COL_BLACK);
653 aFormat.Location = table::ShadowLocation_BOTTOM_RIGHT;
654 aFormat.ShadowWidth = rBorder.LineWidth;
655 return aFormat;
658 void SectionPropertyMap::SetBorderDistance( const uno::Reference< beans::XPropertySet >& xStyle,
659 PropertyIds eMarginId,
660 PropertyIds eDistId,
661 sal_Int32 nDistance,
662 BorderOffsetFrom eOffsetFrom,
663 sal_uInt32 nLineWidth,
664 DomainMapper_Impl& rDM_Impl )
666 if (!xStyle.is())
667 return;
668 const OUString & sMarginName = getPropertyName( eMarginId );
669 const OUString & sBorderDistanceName = getPropertyName( eDistId );
670 uno::Any aMargin = xStyle->getPropertyValue( sMarginName );
671 sal_Int32 nMargin = 0;
672 aMargin >>= nMargin;
673 editeng::BorderDistanceFromWord(eOffsetFrom == BorderOffsetFrom::Edge, nMargin, nDistance,
674 nLineWidth);
676 if (eOffsetFrom == BorderOffsetFrom::Edge)
678 uno::Any aGutterMargin = xStyle->getPropertyValue( "GutterMargin" );
679 sal_Int32 nGutterMargin = 0;
680 aGutterMargin >>= nGutterMargin;
682 if (eMarginId == PROP_LEFT_MARGIN && !rDM_Impl.GetSettingsTable()->GetGutterAtTop())
684 nMargin -= nGutterMargin;
685 nDistance += nGutterMargin;
688 if (eMarginId == PROP_TOP_MARGIN && rDM_Impl.GetSettingsTable()->GetGutterAtTop())
690 nMargin -= nGutterMargin;
691 nDistance += nGutterMargin;
695 // Change the margins with the border distance
696 uno::Reference< beans::XMultiPropertySet > xMultiSet( xStyle, uno::UNO_QUERY_THROW );
697 uno::Sequence<OUString> aProperties { sMarginName, sBorderDistanceName };
698 uno::Sequence<uno::Any> aValues { uno::Any( nMargin ), uno::Any( nDistance ) };
699 xMultiSet->setPropertyValues( aProperties, aValues );
702 void SectionPropertyMap::DontBalanceTextColumns()
706 if ( m_xColumnContainer.is() )
707 m_xColumnContainer->setPropertyValue( "DontBalanceTextColumns", uno::Any( true ) );
709 catch ( const uno::Exception& )
711 TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::DontBalanceTextColumns" );
715 void SectionPropertyMap::ApplySectionProperties( const uno::Reference< beans::XPropertySet >& xSection, DomainMapper_Impl& /*rDM_Impl*/ )
719 if ( xSection.is() )
721 std::optional< PropertyMap::Property > pProp = getProperty( PROP_WRITING_MODE );
722 if ( pProp )
723 xSection->setPropertyValue( "WritingMode", pProp->second );
726 catch ( uno::Exception& )
728 DBG_UNHANDLED_EXCEPTION("writerfilter", "Exception in SectionPropertyMap::ApplySectionProperties");
732 void SectionPropertyMap::ApplyProtectionProperties( uno::Reference< beans::XPropertySet >& xSection, DomainMapper_Impl& rDM_Impl )
736 // Word implements section protection differently than LO.
737 // PROP_IS_PROTECTED only applies if global setting GetProtectForm is enabled.
738 bool bIsProtected = rDM_Impl.GetSettingsTable()->GetProtectForm();
739 if ( bIsProtected )
741 // If form protection is enabled then section protection is enabled, unless explicitly disabled
742 if ( isSet(PROP_IS_PROTECTED) )
743 getProperty(PROP_IS_PROTECTED)->second >>= bIsProtected;
744 if ( !xSection.is() )
745 xSection = rDM_Impl.appendTextSectionAfter( m_xStartingRange );
746 if ( xSection.is() )
747 xSection->setPropertyValue( getPropertyName(PROP_IS_PROTECTED), uno::Any(bIsProtected) );
750 catch ( uno::Exception& )
752 DBG_UNHANDLED_EXCEPTION("writerfilter", "ApplyProtectionProperties failed setting PROP_IS_PROTECTED");
756 uno::Reference< text::XTextColumns > SectionPropertyMap::ApplyColumnProperties( const uno::Reference< beans::XPropertySet >& xColumnContainer,
757 DomainMapper_Impl& rDM_Impl )
759 uno::Reference< text::XTextColumns > xColumns;
760 assert( m_nColumnCount > 1 && "ApplyColumnProperties called without any columns" );
763 const OUString & sTextColumns = getPropertyName( PROP_TEXT_COLUMNS );
764 if ( xColumnContainer.is() )
765 xColumnContainer->getPropertyValue( sTextColumns ) >>= xColumns;
766 uno::Reference< beans::XPropertySet > xColumnPropSet( xColumns, uno::UNO_QUERY_THROW );
767 if ( !m_bEvenlySpaced &&
768 ( sal_Int32(m_aColWidth.size()) == m_nColumnCount ) &&
769 ( (sal_Int32(m_aColDistance.size()) == m_nColumnCount - 1) || (sal_Int32(m_aColDistance.size()) == m_nColumnCount) ) )
771 // the column width in word is an absolute value, in OOo it's relative
772 // the distances are both absolute
773 sal_Int32 nColSum = 0;
774 for ( sal_Int32 nCol = 0; nCol < m_nColumnCount; ++nCol )
776 nColSum += m_aColWidth[nCol];
777 if ( nCol )
778 nColSum += m_aColDistance[nCol - 1];
781 sal_Int32 nRefValue = xColumns->getReferenceValue();
782 double fRel = nColSum ? double( nRefValue ) / double( nColSum ) : 0.0;
783 uno::Sequence< text::TextColumn > aColumns( m_nColumnCount );
784 text::TextColumn* pColumn = aColumns.getArray();
786 nColSum = 0;
787 for ( sal_Int32 nCol = 0; nCol < m_nColumnCount; ++nCol )
789 const double fLeft = nCol ? m_aColDistance[nCol - 1] / 2 : 0;
790 pColumn[nCol].LeftMargin = fLeft;
791 const double fRight = (nCol == m_nColumnCount - 1) ? 0 : m_aColDistance[nCol] / 2;
792 pColumn[nCol].RightMargin = fRight;
793 const double fWidth = m_aColWidth[nCol];
794 pColumn[nCol].Width = (fWidth + fLeft + fRight) * fRel;
795 nColSum += pColumn[nCol].Width;
797 if ( nColSum != nRefValue )
798 pColumn[m_nColumnCount - 1].Width += (nRefValue - nColSum);
799 assert( pColumn[m_nColumnCount - 1].Width >= 0 );
801 xColumns->setColumns( aColumns );
803 else
805 xColumns->setColumnCount( m_nColumnCount );
806 xColumnPropSet->setPropertyValue( getPropertyName( PROP_AUTOMATIC_DISTANCE ), uno::Any( m_nColumnDistance ) );
809 if ( m_bSeparatorLineIsOn )
811 xColumnPropSet->setPropertyValue( "SeparatorLineIsOn", uno::Any( true ) );
812 xColumnPropSet->setPropertyValue( "SeparatorLineVerticalAlignment", uno::Any( style::VerticalAlignment_TOP ) );
813 xColumnPropSet->setPropertyValue( "SeparatorLineRelativeHeight", uno::Any( static_cast<sal_Int8>(100) ) );
814 xColumnPropSet->setPropertyValue( "SeparatorLineColor", uno::Any( static_cast<sal_Int32>(COL_BLACK) ) );
815 // 1 twip -> 2 mm100.
816 xColumnPropSet->setPropertyValue( "SeparatorLineWidth", uno::Any( static_cast<sal_Int32>(2) ) );
818 xColumnContainer->setPropertyValue( sTextColumns, uno::Any( xColumns ) );
819 // Set the columns to be unbalanced if that compatibility option is set or this is the last section.
820 m_xColumnContainer = xColumnContainer;
821 if ( rDM_Impl.GetSettingsTable()->GetNoColumnBalance() || rDM_Impl.GetIsLastSectionGroup() )
822 DontBalanceTextColumns();
824 catch ( const uno::Exception& )
826 TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::ApplyColumnProperties" );
828 return xColumns;
831 bool SectionPropertyMap::HasHeader( bool bFirstPage ) const
833 bool bRet = false;
834 if ( (bFirstPage && m_aFirstPageStyle.is()) || (!bFirstPage && m_aFollowPageStyle.is()) )
836 if ( bFirstPage )
837 m_aFirstPageStyle->getPropertyValue(
838 getPropertyName( PROP_HEADER_IS_ON ) ) >>= bRet;
839 else
840 m_aFollowPageStyle->getPropertyValue(
841 getPropertyName( PROP_HEADER_IS_ON ) ) >>= bRet;
843 return bRet;
846 bool SectionPropertyMap::HasFooter( bool bFirstPage ) const
848 bool bRet = false;
849 if ( (bFirstPage && m_aFirstPageStyle.is()) || (!bFirstPage && m_aFollowPageStyle.is()) )
851 if ( bFirstPage )
852 m_aFirstPageStyle->getPropertyValue( getPropertyName( PROP_FOOTER_IS_ON ) ) >>= bRet;
853 else
854 m_aFollowPageStyle->getPropertyValue( getPropertyName( PROP_FOOTER_IS_ON ) ) >>= bRet;
856 return bRet;
859 #define MIN_HEAD_FOOT_HEIGHT 100 // minimum header/footer height
861 void SectionPropertyMap::CopyHeaderFooterTextProperty( const uno::Reference< beans::XPropertySet >& xPrevStyle,
862 const uno::Reference< beans::XPropertySet >& xStyle,
863 PropertyIds ePropId )
865 try {
866 const OUString & sName = getPropertyName( ePropId );
868 SAL_INFO( "writerfilter", "Copying " << sName );
869 uno::Reference< text::XTextCopy > xTxt;
870 if ( xStyle.is() )
871 xTxt.set( xStyle->getPropertyValue( sName ), uno::UNO_QUERY_THROW );
873 uno::Reference< text::XTextCopy > xPrevTxt;
874 if ( xPrevStyle.is() )
875 xPrevTxt.set( xPrevStyle->getPropertyValue( sName ), uno::UNO_QUERY_THROW );
877 xTxt->copyText( xPrevTxt );
879 catch ( const uno::Exception& )
881 TOOLS_INFO_EXCEPTION( "writerfilter", "An exception occurred in SectionPropertyMap::CopyHeaderFooterTextProperty( )" );
885 // Copy headers and footers from the previous page style.
886 void SectionPropertyMap::CopyHeaderFooter( const DomainMapper_Impl& rDM_Impl,
887 const uno::Reference< beans::XPropertySet >& xPrevStyle,
888 const uno::Reference< beans::XPropertySet >& xStyle,
889 bool bOmitRightHeader,
890 bool bOmitLeftHeader,
891 bool bOmitRightFooter,
892 bool bOmitLeftFooter )
894 if (!rDM_Impl.IsNewDoc())
895 { // see also DomainMapper_Impl::PushPageHeaderFooter()
896 return; // tdf#139737 SwUndoInserts cannot deal with new header/footer
898 bool bHasPrevHeader = false;
899 bool bHeaderIsShared = true;
900 const OUString & sHeaderIsOn = getPropertyName( PROP_HEADER_IS_ON );
901 const OUString & sHeaderIsShared = getPropertyName( PROP_HEADER_IS_SHARED );
902 if ( xPrevStyle.is() )
904 xPrevStyle->getPropertyValue( sHeaderIsOn ) >>= bHasPrevHeader;
905 xPrevStyle->getPropertyValue( sHeaderIsShared ) >>= bHeaderIsShared;
908 if ( bHasPrevHeader )
910 uno::Reference< beans::XMultiPropertySet > xMultiSet( xStyle, uno::UNO_QUERY_THROW );
911 uno::Sequence<OUString> aProperties { sHeaderIsOn, sHeaderIsShared };
912 uno::Sequence<uno::Any> aValues { uno::Any( true ), uno::Any( bHeaderIsShared ) };
913 xMultiSet->setPropertyValues( aProperties, aValues );
914 if ( !bOmitRightHeader )
916 CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
917 PROP_HEADER_TEXT );
919 if ( !bHeaderIsShared && !bOmitLeftHeader )
921 CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
922 PROP_HEADER_TEXT_LEFT );
926 bool bHasPrevFooter = false;
927 bool bFooterIsShared = true;
928 const OUString & sFooterIsOn = getPropertyName( PROP_FOOTER_IS_ON );
929 const OUString & sFooterIsShared = getPropertyName( PROP_FOOTER_IS_SHARED );
930 if ( xPrevStyle.is() )
932 xPrevStyle->getPropertyValue( sFooterIsOn ) >>= bHasPrevFooter;
933 xPrevStyle->getPropertyValue( sFooterIsShared ) >>= bFooterIsShared;
936 if ( !bHasPrevFooter )
937 return;
939 uno::Reference< beans::XMultiPropertySet > xMultiSet( xStyle, uno::UNO_QUERY_THROW );
940 uno::Sequence<OUString> aProperties { sFooterIsOn, sFooterIsShared };
941 uno::Sequence<uno::Any> aValues { uno::Any( true ), uno::Any( bFooterIsShared ) };
942 xMultiSet->setPropertyValues( aProperties, aValues );
943 if ( !bOmitRightFooter )
945 CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
946 PROP_FOOTER_TEXT );
948 if ( !bFooterIsShared && !bOmitLeftFooter )
950 CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
951 PROP_FOOTER_TEXT_LEFT );
955 // Copy header and footer content from the previous docx section as needed.
957 // Any headers and footers which were not defined in this docx section
958 // should be "linked" with the corresponding header or footer from the
959 // previous section. LO does not support linking of header/footer content
960 // across page styles so we just copy the content from the previous section.
961 void SectionPropertyMap::CopyLastHeaderFooter( bool bFirstPage, DomainMapper_Impl& rDM_Impl )
963 SAL_INFO( "writerfilter", "START>>> SectionPropertyMap::CopyLastHeaderFooter()" );
964 SectionPropertyMap* pLastContext = rDM_Impl.GetLastSectionContext();
965 if ( pLastContext )
967 const bool bUseEvenPages = rDM_Impl.GetSettingsTable()->GetEvenAndOddHeaders();
968 uno::Reference< beans::XPropertySet > xPrevStyle = pLastContext->GetPageStyle( rDM_Impl,
969 bFirstPage );
970 uno::Reference< beans::XPropertySet > xStyle = GetPageStyle( rDM_Impl,
971 bFirstPage );
973 if ( bFirstPage )
975 CopyHeaderFooter(rDM_Impl, xPrevStyle, xStyle,
976 !m_bFirstPageHeaderLinkToPrevious, true,
977 !m_bFirstPageFooterLinkToPrevious, true );
979 else
981 CopyHeaderFooter(rDM_Impl, xPrevStyle, xStyle,
982 !m_bDefaultHeaderLinkToPrevious,
983 !(m_bEvenPageHeaderLinkToPrevious && bUseEvenPages),
984 !m_bDefaultFooterLinkToPrevious,
985 !(m_bEvenPageFooterLinkToPrevious && bUseEvenPages));
988 SAL_INFO( "writerfilter", "END>>> SectionPropertyMap::CopyLastHeaderFooter()" );
991 void SectionPropertyMap::PrepareHeaderFooterProperties( bool bFirstPage )
993 bool bCopyFirstToFollow = bFirstPage && m_bTitlePage && m_aFollowPageStyle.is();
995 sal_Int32 nTopMargin = m_nTopMargin;
996 sal_Int32 nHeaderHeight = m_nHeaderTop;
997 if ( HasHeader( bFirstPage ) )
999 nTopMargin = m_nHeaderTop;
1000 nHeaderHeight = m_nTopMargin - m_nHeaderTop;
1002 // minimum header height 1mm
1003 if ( nHeaderHeight < MIN_HEAD_FOOT_HEIGHT )
1004 nHeaderHeight = MIN_HEAD_FOOT_HEIGHT;
1007 Insert(PROP_HEADER_IS_DYNAMIC_HEIGHT, uno::Any(m_bDynamicHeightTop));
1008 Insert(PROP_HEADER_DYNAMIC_SPACING, uno::Any(m_bDynamicHeightTop));
1009 Insert(PROP_HEADER_BODY_DISTANCE, uno::Any(nHeaderHeight - MIN_HEAD_FOOT_HEIGHT));
1010 Insert(PROP_HEADER_HEIGHT, uno::Any(nHeaderHeight));
1011 // looks like PROP_HEADER_HEIGHT = height of the header + space between the header, and the body
1013 if ( m_bDynamicHeightTop ) //fixed height header -> see WW8Par6.hxx
1015 if (bCopyFirstToFollow && HasHeader(/*bFirstPage=*/true))
1017 m_aFollowPageStyle->setPropertyValue("HeaderDynamicSpacing",
1018 getProperty(PROP_HEADER_DYNAMIC_SPACING)->second);
1019 m_aFollowPageStyle->setPropertyValue("HeaderHeight",
1020 getProperty(PROP_HEADER_HEIGHT)->second);
1024 sal_Int32 nBottomMargin = m_nBottomMargin;
1025 sal_Int32 nFooterHeight = m_nHeaderBottom;
1026 if ( HasFooter( bFirstPage ) )
1028 nBottomMargin = m_nHeaderBottom;
1029 nFooterHeight = m_nBottomMargin - m_nHeaderBottom;
1031 // minimum footer height 1mm
1032 if ( nFooterHeight < MIN_HEAD_FOOT_HEIGHT )
1033 nFooterHeight = MIN_HEAD_FOOT_HEIGHT;
1036 Insert(PROP_FOOTER_IS_DYNAMIC_HEIGHT, uno::Any(m_bDynamicHeightBottom));
1037 Insert(PROP_FOOTER_DYNAMIC_SPACING, uno::Any(m_bDynamicHeightBottom));
1038 Insert(PROP_FOOTER_BODY_DISTANCE, uno::Any(nFooterHeight - MIN_HEAD_FOOT_HEIGHT));
1039 Insert(PROP_FOOTER_HEIGHT, uno::Any(nFooterHeight));
1040 if (m_bDynamicHeightBottom) //fixed height footer -> see WW8Par6.hxx
1042 if (bCopyFirstToFollow && HasFooter(/*bFirstPage=*/true))
1044 m_aFollowPageStyle->setPropertyValue("FooterDynamicSpacing",
1045 getProperty(PROP_FOOTER_DYNAMIC_SPACING)->second);
1046 m_aFollowPageStyle->setPropertyValue("FooterHeight",
1047 getProperty(PROP_FOOTER_HEIGHT)->second);
1051 //now set the top/bottom margin for the follow page style
1052 Insert( PROP_TOP_MARGIN, uno::Any( std::max<sal_Int32>(nTopMargin, 0) ) );
1053 Insert( PROP_BOTTOM_MARGIN, uno::Any( std::max<sal_Int32>(nBottomMargin, 0) ) );
1056 static uno::Reference< beans::XPropertySet > lcl_GetRangeProperties( bool bIsFirstSection,
1057 DomainMapper_Impl& rDM_Impl,
1058 const uno::Reference< text::XTextRange >& xStartingRange )
1060 uno::Reference< beans::XPropertySet > xRangeProperties;
1061 if ( bIsFirstSection && rDM_Impl.GetBodyText().is() )
1063 uno::Reference< container::XEnumerationAccess > xEnumAccess( rDM_Impl.GetBodyText(), uno::UNO_QUERY_THROW );
1064 uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration();
1065 xRangeProperties.set( xEnum->nextElement(), uno::UNO_QUERY_THROW );
1066 if ( rDM_Impl.GetIsDummyParaAddedForTableInSection() && xEnum->hasMoreElements() )
1067 xRangeProperties.set( xEnum->nextElement(), uno::UNO_QUERY_THROW );
1069 else if ( xStartingRange.is() )
1070 xRangeProperties.set( xStartingRange, uno::UNO_QUERY_THROW );
1071 return xRangeProperties;
1074 void SectionPropertyMap::HandleMarginsHeaderFooter( bool bFirstPage, DomainMapper_Impl& rDM_Impl )
1076 Insert( PROP_LEFT_MARGIN, uno::Any( m_nLeftMargin ) );
1077 Insert( PROP_RIGHT_MARGIN, uno::Any( m_nRightMargin ) );
1078 Insert(PROP_GUTTER_MARGIN, uno::Any(m_nGutterMargin));
1080 if ( rDM_Impl.m_oBackgroundColor )
1081 Insert( PROP_BACK_COLOR, uno::Any( *rDM_Impl.m_oBackgroundColor ) );
1083 // Check for missing footnote separator only in case there is at least
1084 // one footnote.
1085 if (rDM_Impl.m_bHasFtn && !rDM_Impl.m_bHasFtnSep)
1087 // Set footnote line width to zero, document has no footnote separator.
1088 Insert(PROP_FOOTNOTE_LINE_RELATIVE_WIDTH, uno::Any(sal_Int32(0)));
1090 if ( rDM_Impl.m_bHasFtnSep )
1092 //If default paragraph style is RTL, footnote separator should be right aligned
1093 //and for RTL locales, LTR default paragraph style should present a left aligned footnote separator
1096 uno::Reference<style::XStyleFamiliesSupplier> xStylesSupplier(rDM_Impl.GetTextDocument(), uno::UNO_QUERY);
1097 if ( xStylesSupplier.is() )
1099 uno::Reference<container::XNameAccess> xStyleFamilies = xStylesSupplier->getStyleFamilies();
1100 uno::Reference<container::XNameAccess> xParagraphStyles;
1101 if ( xStyleFamilies.is() )
1102 xStyleFamilies->getByName("ParagraphStyles") >>= xParagraphStyles;
1103 uno::Reference<beans::XPropertySet> xStandard;
1104 if ( xParagraphStyles.is() )
1105 xParagraphStyles->getByName("Standard") >>= xStandard;
1106 if ( xStandard.is() )
1108 sal_Int16 aWritingMode(0);
1109 xStandard->getPropertyValue( getPropertyName(PROP_WRITING_MODE) ) >>= aWritingMode;
1110 if( aWritingMode == text::WritingMode2::RL_TB )
1111 Insert( PROP_FOOTNOTE_LINE_ADJUST, uno::Any( sal_Int16(text::HorizontalAdjust_RIGHT) ), false );
1112 else
1113 Insert( PROP_FOOTNOTE_LINE_ADJUST, uno::Any( sal_Int16(text::HorizontalAdjust_LEFT) ), false );
1117 catch ( const uno::Exception& ) {}
1120 /*** if headers/footers are available then the top/bottom margins of the
1121 header/footer are copied to the top/bottom margin of the page
1123 CopyLastHeaderFooter( bFirstPage, rDM_Impl );
1124 PrepareHeaderFooterProperties( bFirstPage );
1126 // tdf#119952: If top/bottom margin was negative during docx import,
1127 // then the header/footer and the body could be on top of each other
1128 // writer is unable to display both of them in the same position, but can be simulated
1129 // by moving the header/footer text into a flyframe anchored to the header/footer,
1130 // leaving an empty dummy header/footer.
1131 rDM_Impl.ConvertHeaderFooterToTextFrame(m_bDynamicHeightTop, m_bDynamicHeightBottom);
1134 void SectionPropertyMap::InheritOrFinalizePageStyles( DomainMapper_Impl& rDM_Impl )
1136 // if no new styles have been created for this section, inherit from the previous section,
1137 // otherwise apply this section's settings to the new style.
1138 // Ensure that FollowPage is inherited first - otherwise GetPageStyle may auto-create a follow when checking FirstPage.
1139 SectionPropertyMap* pLastContext = rDM_Impl.GetLastSectionContext();
1140 //tdf124637 TODO: identify and skip special sections (like footnotes/endnotes)
1141 if ( pLastContext && m_sFollowPageStyleName.isEmpty() )
1142 m_sFollowPageStyleName = pLastContext->GetPageStyleName();
1143 else
1145 HandleMarginsHeaderFooter( /*bFirst=*/false, rDM_Impl );
1146 GetPageStyle( rDM_Impl, /*bFirst=*/false );
1147 if ( rDM_Impl.IsNewDoc() && m_aFollowPageStyle.is() )
1148 ApplyProperties_( m_aFollowPageStyle );
1151 // FirstPageStyle may only be inherited if it will not be used or re-linked to a different follow
1152 if ( !m_bTitlePage && pLastContext && m_sFirstPageStyleName.isEmpty() )
1153 m_sFirstPageStyleName = pLastContext->GetPageStyleName( /*bFirst=*/true );
1154 else
1156 HandleMarginsHeaderFooter( /*bFirst=*/true, rDM_Impl );
1157 GetPageStyle( rDM_Impl, /*bFirst=*/true );
1158 if ( rDM_Impl.IsNewDoc() && m_aFirstPageStyle.is() )
1159 ApplyProperties_( m_aFirstPageStyle );
1161 // Chain m_aFollowPageStyle to be after m_aFirstPageStyle
1162 m_aFirstPageStyle->setPropertyValue( "FollowStyle", uno::Any( m_sFollowPageStyleName ) );
1166 void SectionPropertyMap::HandleIncreasedAnchoredObjectSpacing(DomainMapper_Impl& rDM_Impl)
1168 // Ignore Word 2010 and older.
1169 if (rDM_Impl.GetSettingsTable()->GetWordCompatibilityMode() < 15)
1170 return;
1172 sal_Int32 nPageWidth = GetPageWidth();
1173 sal_Int32 nTextAreaWidth = nPageWidth - GetLeftMargin() - GetRightMargin();
1175 std::vector<AnchoredObjectsInfo>& rAnchoredObjectAnchors = rDM_Impl.m_aAnchoredObjectAnchors;
1176 for (const auto& rAnchor : rAnchoredObjectAnchors)
1178 // Ignore this paragraph when there are not enough shapes to trigger the Word bug we
1179 // emulate.
1180 if (rAnchor.m_aAnchoredObjects.size() < 4)
1181 continue;
1183 // Ignore this paragraph if none of the objects are wrapped in the background.
1184 sal_Int32 nOpaqueCount = 0;
1185 for (const auto& rAnchored : rAnchor.m_aAnchoredObjects)
1187 // Ignore inline objects stored only for redlining.
1188 if (rAnchored.m_xRedlineForInline)
1189 continue;
1191 uno::Reference<beans::XPropertySet> xShape(rAnchored.m_xAnchoredObject, uno::UNO_QUERY);
1192 if (!xShape.is())
1194 continue;
1197 bool bOpaque = true;
1198 xShape->getPropertyValue("Opaque") >>= bOpaque;
1199 if (!bOpaque)
1201 ++nOpaqueCount;
1204 if (nOpaqueCount < 1)
1206 continue;
1209 // Analyze the anchored objects of this paragraph, now that we know the
1210 // page width.
1211 sal_Int32 nShapesWidth = 0;
1212 for (const auto& rAnchored : rAnchor.m_aAnchoredObjects)
1214 uno::Reference<drawing::XShape> xShape(rAnchored.m_xAnchoredObject, uno::UNO_QUERY);
1215 if (!xShape.is())
1216 continue;
1218 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
1219 if (!xPropertySet.is())
1220 continue;
1222 // Ignore objects with no wrapping.
1223 text::WrapTextMode eWrap = text::WrapTextMode_THROUGH;
1224 xPropertySet->getPropertyValue("Surround") >>= eWrap;
1225 if (eWrap == text::WrapTextMode_THROUGH)
1226 continue;
1228 // Use the original left margin, in case GraphicImport::lcl_sprm() reduced the doc model
1229 // one to 0.
1230 sal_Int32 nLeftMargin = rAnchored.m_nLeftMargin;
1231 sal_Int32 nRightMargin = 0;
1232 xPropertySet->getPropertyValue("RightMargin") >>= nRightMargin;
1233 nShapesWidth += xShape->getSize().Width + nLeftMargin + nRightMargin;
1236 // Ignore cases when we have enough horizontal space for the shapes.
1237 if (nTextAreaWidth > nShapesWidth)
1238 continue;
1240 sal_Int32 nHeight = 0;
1241 for (const auto& rAnchored : rAnchor.m_aAnchoredObjects)
1243 uno::Reference<drawing::XShape> xShape(rAnchored.m_xAnchoredObject, uno::UNO_QUERY);
1244 if (!xShape.is())
1245 continue;
1247 nHeight += xShape->getSize().Height;
1250 uno::Reference<beans::XPropertySet> xParagraph(rAnchor.m_xParagraph, uno::UNO_QUERY);
1251 if (xParagraph.is())
1253 sal_Int32 nTopMargin = 0;
1254 xParagraph->getPropertyValue("ParaTopMargin") >>= nTopMargin;
1255 // Increase top spacing of the paragraph to match Word layout
1256 // behavior.
1257 nTopMargin = std::max(nTopMargin, nHeight);
1258 xParagraph->setPropertyValue("ParaTopMargin", uno::Any(nTopMargin));
1261 rAnchoredObjectAnchors.clear();
1264 void BeforeConvertToTextFrame(std::deque<css::uno::Any>& rFramedRedlines, std::vector<sal_Int32>& redPos, std::vector<sal_Int32>& redLen, std::vector<OUString>& redCell, std::vector<OUString>& redTable)
1266 // convert redline ranges to cursor movement and character length
1267 for( size_t i = 0; i < rFramedRedlines.size(); i+=3)
1269 uno::Reference<text::XText> xCell;
1270 uno::Reference< text::XTextRange > xRange;
1271 rFramedRedlines[i] >>= xRange;
1272 uno::Reference< beans::XPropertySet > xRangeProperties;
1273 if ( xRange.is() )
1275 OUString sTableName;
1276 OUString sCellName;
1277 xRangeProperties.set( xRange, uno::UNO_QUERY_THROW );
1278 if (xRangeProperties->getPropertySetInfo()->hasPropertyByName("TextTable"))
1280 uno::Any aTable = xRangeProperties->getPropertyValue("TextTable");
1281 if ( aTable != uno::Any() )
1283 uno::Reference<text::XTextTable> xTable;
1284 aTable >>= xTable;
1285 uno::Reference<beans::XPropertySet> xTableProperties(xTable, uno::UNO_QUERY);
1286 xTableProperties->getPropertyValue("TableName") >>= sTableName;
1288 if (xRangeProperties->getPropertySetInfo()->hasPropertyByName("Cell"))
1290 uno::Any aCell = xRangeProperties->getPropertyValue("Cell");
1291 if ( aCell != uno::Any() )
1293 aCell >>= xCell;
1294 uno::Reference<beans::XPropertySet> xCellProperties(xCell, uno::UNO_QUERY);
1295 xCellProperties->getPropertyValue("CellName") >>= sCellName;
1299 redTable.push_back(sTableName);
1300 redCell.push_back(sCellName);
1301 bool bOk = false;
1302 if (!sTableName.isEmpty() && !sCellName.isEmpty())
1304 uno::Reference<text::XTextCursor> xRangeCursor = xCell->createTextCursorByRange( xRange );
1305 if ( xRangeCursor.is() )
1307 bOk = true;
1308 sal_Int32 nLen = xRange->getString().getLength();
1309 redLen.push_back(nLen);
1310 xRangeCursor->gotoStart(true);
1311 redPos.push_back(xRangeCursor->getString().getLength() - nLen);
1314 if (!bOk)
1316 // missing cell or failed createTextCursorByRange()
1317 redLen.push_back(-1);
1318 redPos.push_back(-1);
1324 void AfterConvertToTextFrame(DomainMapper_Impl& rDM_Impl, std::deque<css::uno::Any>& aFramedRedlines, std::vector<sal_Int32>& redPos, std::vector<sal_Int32>& redLen, std::vector<OUString>& redCell, std::vector<OUString>& redTable)
1326 uno::Reference<text::XTextTablesSupplier> xTextDocument(rDM_Impl.GetTextDocument(), uno::UNO_QUERY);
1327 uno::Reference<container::XNameAccess> xTables = xTextDocument->getTextTables();
1328 for( size_t i = 0; i < aFramedRedlines.size(); i+=3)
1330 OUString sType;
1331 beans::PropertyValues aRedlineProperties( 3 );
1332 // skip failed createTextCursorByRange()
1333 if (redPos[i/3] == -1)
1334 continue;
1335 aFramedRedlines[i+1] >>= sType;
1336 aFramedRedlines[i+2] >>= aRedlineProperties;
1337 uno::Reference<text::XTextTable> xTable(xTables->getByName(redTable[i/3]), uno::UNO_QUERY);
1338 uno::Reference<text::XText> xCell(xTable->getCellByName(redCell[i/3]), uno::UNO_QUERY);
1339 uno::Reference<text::XTextCursor> xCrsr = xCell->createTextCursor();
1340 xCrsr->goRight(redPos[i/3], false);
1341 xCrsr->goRight(redLen[i/3], true);
1342 uno::Reference < text::XRedline > xRedline( xCrsr, uno::UNO_QUERY_THROW );
1345 xRedline->makeRedline( sType, aRedlineProperties );
1347 catch (const uno::Exception&)
1349 DBG_UNHANDLED_EXCEPTION("writerfilter", "makeRedline() failed");
1354 void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl )
1356 SectionPropertyMap* pPrevSection = rDM_Impl.GetLastSectionContext();
1358 // The default section type is nextPage.
1359 if ( m_nBreakType == -1 )
1360 m_nBreakType = NS_ooxml::LN_Value_ST_SectionMark_nextPage;
1361 else if ( m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_nextColumn )
1363 // Word 2013+ seems to treat a section column break as a page break all the time.
1364 // It always acts like a page break if there are no columns, or a different number of columns.
1365 // Also, if this is the first section, the break type is basically irrelevant - works best as nextPage.
1366 if ( rDM_Impl.GetSettingsTable()->GetWordCompatibilityMode() > 14
1367 || !pPrevSection
1368 || m_nColumnCount < 2
1369 || m_nColumnCount != pPrevSection->ColumnCount()
1372 m_nBreakType = NS_ooxml::LN_Value_ST_SectionMark_nextPage;
1375 else if ( m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_continuous )
1377 // if page orientation differs from previous section, it can't be treated as continuous
1378 if ( pPrevSection )
1380 bool bIsLandscape = false;
1381 std::optional< PropertyMap::Property > pProp = getProperty( PROP_IS_LANDSCAPE );
1382 if ( pProp )
1383 pProp->second >>= bIsLandscape;
1385 bool bPrevIsLandscape = false;
1386 pProp = pPrevSection->getProperty( PROP_IS_LANDSCAPE );
1387 if ( pProp )
1388 pProp->second >>= bPrevIsLandscape;
1390 if ( bIsLandscape != bPrevIsLandscape )
1391 m_nBreakType = NS_ooxml::LN_Value_ST_SectionMark_nextPage;
1397 HandleIncreasedAnchoredObjectSpacing(rDM_Impl);
1399 catch (const uno::Exception&)
1401 DBG_UNHANDLED_EXCEPTION("writerfilter", "HandleIncreasedAnchoredObjectSpacing() failed");
1404 if ( m_nLnnMod )
1406 bool bFirst = rDM_Impl.IsLineNumberingSet();
1407 rDM_Impl.SetLineNumbering( m_nLnnMod, m_nLnc, m_ndxaLnn );
1408 if ( m_nLnnMin > 0 || (bFirst && m_nLnc == NS_ooxml::LN_Value_ST_LineNumberRestart_newSection) )
1410 //set the starting value at the beginning of the section
1413 uno::Reference< beans::XPropertySet > xRangeProperties;
1414 if ( m_xStartingRange.is() )
1416 xRangeProperties.set( m_xStartingRange, uno::UNO_QUERY_THROW );
1418 else
1420 //set the start value at the beginning of the document
1421 xRangeProperties.set( rDM_Impl.GetTextDocument()->getText()->getStart(), uno::UNO_QUERY_THROW );
1423 // Writer is 1-based, Word is 0-based.
1424 xRangeProperties->setPropertyValue(
1425 getPropertyName(PROP_PARA_LINE_NUMBER_START_VALUE),
1426 uno::Any(m_nLnnMin + 1));
1428 catch ( const uno::Exception& )
1430 DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper", "Exception in SectionPropertyMap::CloseSectionGroup");
1435 if (m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_continuous
1436 && !rDM_Impl.IsInComments())
1438 //todo: insert a section or access the already inserted section
1439 uno::Reference< beans::XPropertySet > xSection =
1440 rDM_Impl.appendTextSectionAfter( m_xStartingRange );
1441 if ( xSection.is() )
1443 if ( m_nColumnCount > 1 )
1444 ApplyColumnProperties( xSection, rDM_Impl );
1446 ApplyProtectionProperties( xSection, rDM_Impl );
1451 InheritOrFinalizePageStyles( rDM_Impl );
1452 ApplySectionProperties( xSection, rDM_Impl ); //depends on InheritOrFinalizePageStyles
1453 OUString aName = m_bTitlePage ? m_sFirstPageStyleName : m_sFollowPageStyleName;
1454 uno::Reference< beans::XPropertySet > xRangeProperties( lcl_GetRangeProperties( m_bIsFirstSection, rDM_Impl, m_xStartingRange ) );
1455 if ( m_bIsFirstSection && !aName.isEmpty() && xRangeProperties.is() )
1457 xRangeProperties->setPropertyValue( getPropertyName( PROP_PAGE_DESC_NAME ), uno::Any( aName ) );
1459 else if ((!m_bFirstPageHeaderLinkToPrevious ||
1460 !m_bFirstPageFooterLinkToPrevious ||
1461 !m_bDefaultHeaderLinkToPrevious ||
1462 !m_bDefaultFooterLinkToPrevious ||
1463 !m_bEvenPageHeaderLinkToPrevious ||
1464 !m_bEvenPageFooterLinkToPrevious)
1465 && rDM_Impl.GetCurrentXText())
1466 { // find a node in the section that has a page break and change
1467 // it to apply the page style; see "nightmare scenario" in
1468 // wwSectionManager::InsertSegments()
1469 auto xTextAppend = rDM_Impl.GetCurrentXText();
1470 uno::Reference<container::XEnumerationAccess> const xCursor(
1471 xTextAppend->createTextCursorByRange(
1472 uno::Reference<text::XTextContent>(xSection, uno::UNO_QUERY_THROW)->getAnchor()),
1473 uno::UNO_QUERY_THROW);
1474 uno::Reference<container::XEnumeration> const xEnum(
1475 xCursor->createEnumeration());
1476 bool isFound = false;
1477 while (xEnum->hasMoreElements())
1479 uno::Reference<beans::XPropertySet> xElem;
1480 xEnum->nextElement() >>= xElem;
1481 if (xElem->getPropertySetInfo()->hasPropertyByName("BreakType"))
1483 style::BreakType bt;
1484 if ((xElem->getPropertyValue("BreakType") >>= bt)
1485 && bt == style::BreakType_PAGE_BEFORE)
1487 // tdf#112201: do *not* use m_sFirstPageStyleName here!
1488 xElem->setPropertyValue(getPropertyName(PROP_PAGE_DESC_NAME),
1489 uno::Any(m_sFollowPageStyleName));
1490 isFound = true;
1491 break;
1495 uno::Reference<text::XParagraphCursor> const xPCursor(xCursor,
1496 uno::UNO_QUERY_THROW);
1497 float fCharHeight = 0;
1498 if (!isFound)
1499 { // HACK: try the last paragraph of the previous section
1500 xPCursor->gotoPreviousParagraph(false);
1501 uno::Reference<beans::XPropertySet> const xPSCursor(xCursor, uno::UNO_QUERY_THROW);
1502 style::BreakType bt;
1503 if ((xPSCursor->getPropertyValue("BreakType") >>= bt)
1504 && bt == style::BreakType_PAGE_BEFORE)
1506 xPSCursor->setPropertyValue(getPropertyName(PROP_PAGE_DESC_NAME),
1507 uno::Any(m_sFollowPageStyleName));
1508 isFound = true;
1510 else
1512 xPSCursor->getPropertyValue("CharHeight") >>= fCharHeight;
1515 if (!isFound && fCharHeight <= 1.0)
1517 // If still not found, see if the last paragraph is ~invisible, and work with
1518 // the last-in-practice paragraph.
1519 xPCursor->gotoPreviousParagraph(false);
1520 uno::Reference<beans::XPropertySet> xPropertySet(xCursor, uno::UNO_QUERY_THROW);
1521 OUString aPageDescName;
1522 if ((xPropertySet->getPropertyValue("PageDescName") >>= aPageDescName)
1523 && !aPageDescName.isEmpty())
1525 uno::Reference<beans::XPropertySet> xPageStyle(
1526 rDM_Impl.GetPageStyles()->getByName(aPageDescName), uno::UNO_QUERY);
1527 xPageStyle->setPropertyValue("FollowStyle",
1528 uno::Any(m_sFollowPageStyleName));
1533 catch ( const uno::Exception& )
1535 SAL_WARN( "writerfilter", "failed to set PageDescName!" );
1538 // If the section is of type "New column" (0x01), then simply insert a column break.
1539 // But only if there actually are columns on the page, otherwise a column break
1540 // seems to be handled like a page break by MSO.
1541 else if (m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_nextColumn
1542 && m_nColumnCount > 1 && !rDM_Impl.IsInComments())
1546 InheritOrFinalizePageStyles( rDM_Impl );
1547 /*TODO tdf#135343: Just inserting a column break sounds like the right idea, but the implementation is wrong.
1548 * Somehow, the previous column section needs to be extended to cover this new text.
1549 * Currently, it is completely broken, producing a no-column section that starts on a new page.
1551 uno::Reference< beans::XPropertySet > xRangeProperties;
1552 if ( m_xStartingRange.is() )
1554 xRangeProperties.set( m_xStartingRange, uno::UNO_QUERY_THROW );
1556 else
1558 //set the start value at the beginning of the document
1559 xRangeProperties.set( rDM_Impl.GetTextDocument()->getText()->getStart(), uno::UNO_QUERY_THROW );
1561 xRangeProperties->setPropertyValue( getPropertyName( PROP_BREAK_TYPE ), uno::Any( style::BreakType_COLUMN_BEFORE ) );
1563 catch ( const uno::Exception& ) {}
1565 else if (!rDM_Impl.IsInComments())
1567 uno::Reference< beans::XPropertySet > xSection;
1568 ApplyProtectionProperties( xSection, rDM_Impl );
1570 //get the properties and create appropriate page styles
1571 uno::Reference< beans::XPropertySet > xFollowPageStyle;
1572 //This part certainly is not needed for footnotes, so don't create unused page styles.
1573 if ( !rDM_Impl.IsInFootOrEndnote() )
1575 xFollowPageStyle.set( GetPageStyle( rDM_Impl, false ) );
1577 HandleMarginsHeaderFooter(/*bFirstPage=*/false, rDM_Impl );
1580 if ( rDM_Impl.GetSettingsTable()->GetMirrorMarginSettings() )
1582 Insert( PROP_PAGE_STYLE_LAYOUT, uno::Any( style::PageStyleLayout_MIRRORED ) );
1584 uno::Reference< text::XTextColumns > xColumns;
1585 if ( m_nColumnCount > 1 )
1587 // prefer setting column properties into a section, not a page style if at all possible.
1588 if ( !xSection.is() )
1589 xSection = rDM_Impl.appendTextSectionAfter( m_xStartingRange );
1590 if ( xSection.is() )
1591 ApplyColumnProperties( xSection, rDM_Impl );
1592 else if ( xFollowPageStyle.is() )
1593 xColumns = ApplyColumnProperties( xFollowPageStyle, rDM_Impl );
1596 // these BreakTypes are effectively page-breaks: don't evenly distribute text in columns before a page break;
1597 if ( pPrevSection && pPrevSection->ColumnCount() )
1598 pPrevSection->DontBalanceTextColumns();
1600 //prepare text grid properties
1601 sal_Int32 nHeight = 1;
1602 std::optional< PropertyMap::Property > pProp = getProperty( PROP_HEIGHT );
1603 if ( pProp )
1604 pProp->second >>= nHeight;
1606 sal_Int32 nWidth = 1;
1607 pProp = getProperty( PROP_WIDTH );
1608 if ( pProp )
1609 pProp->second >>= nWidth;
1611 sal_Int16 nWritingMode = text::WritingMode2::LR_TB;
1612 pProp = getProperty( PROP_WRITING_MODE );
1613 if ( pProp )
1614 pProp->second >>= nWritingMode;
1616 sal_Int32 nTextAreaHeight = nWritingMode == text::WritingMode2::LR_TB ?
1617 nHeight - m_nTopMargin - m_nBottomMargin :
1618 nWidth - m_nLeftMargin - m_nRightMargin;
1620 sal_Int32 nGridLinePitch = m_nGridLinePitch;
1621 //sep.dyaLinePitch
1622 if ( nGridLinePitch < 1 || nGridLinePitch > 31680 )
1624 SAL_WARN( "writerfilter", "sep.dyaLinePitch outside legal range: " << nGridLinePitch );
1625 nGridLinePitch = 1;
1628 const sal_Int32 nGridLines = nTextAreaHeight / nGridLinePitch;
1629 sal_Int16 nGridType = m_nGridType;
1630 if ( nGridLines >= 0 && nGridLines <= SAL_MAX_INT16 )
1631 Insert( PROP_GRID_LINES, uno::Any( sal_Int16(nGridLines) ) );
1632 else
1633 nGridType = text::TextGridMode::NONE;
1635 // PROP_GRID_MODE
1636 if ( nGridType == text::TextGridMode::LINES_AND_CHARS )
1638 if (!m_nDxtCharSpace)
1639 nGridType = text::TextGridMode::LINES;
1640 else
1641 Insert( PROP_GRID_SNAP_TO_CHARS, uno::Any( m_bGridSnapToChars ) );
1644 Insert( PROP_GRID_MODE, uno::Any( nGridType ) );
1646 sal_Int32 nCharWidth = 423; //240 twip/ 12 pt
1647 const StyleSheetEntryPtr pEntry = rDM_Impl.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( u"Standard" );
1648 if ( pEntry )
1650 std::optional< PropertyMap::Property > pPropHeight = pEntry->m_pProperties->getProperty( PROP_CHAR_HEIGHT_ASIAN );
1651 if ( pPropHeight )
1653 double fHeight = 0;
1654 if ( pPropHeight->second >>= fHeight )
1655 nCharWidth = ConversionHelper::convertTwipToMM100( static_cast<tools::Long>(fHeight * 20.0 + 0.5) );
1659 //dxtCharSpace
1660 if ( m_nDxtCharSpace )
1662 sal_Int32 nCharSpace = m_nDxtCharSpace;
1663 //main lives in top 20 bits, and is signed.
1664 sal_Int32 nMain = (nCharSpace & 0xFFFFF000);
1665 nMain /= 0x1000;
1666 nCharWidth += ConversionHelper::convertTwipToMM100( nMain * 20 );
1668 sal_Int32 nFraction = (nCharSpace & 0x00000FFF);
1669 nFraction = (nFraction * 20) / 0xFFF;
1670 nCharWidth += ConversionHelper::convertTwipToMM100( nFraction );
1673 if ( m_nPageNumberType >= 0 )
1674 Insert( PROP_NUMBERING_TYPE, uno::Any( m_nPageNumberType ) );
1676 // #i119558#, force to set document as standard page mode,
1677 // refer to ww8 import process function "SwWW8ImplReader::SetDocumentGrid"
1680 uno::Reference< beans::XPropertySet > xDocProperties;
1681 xDocProperties.set( rDM_Impl.GetTextDocument(), uno::UNO_QUERY_THROW );
1682 Insert(PROP_GRID_STANDARD_MODE, uno::Any(true));
1683 xDocProperties->setPropertyValue("DefaultPageMode", uno::Any(false));
1685 catch ( const uno::Exception& )
1687 DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper", "Exception in SectionPropertyMap::CloseSectionGroup");
1690 Insert( PROP_GRID_BASE_HEIGHT, uno::Any( nGridLinePitch ) );
1691 Insert( PROP_GRID_BASE_WIDTH, uno::Any( nCharWidth ) );
1692 Insert( PROP_GRID_RUBY_HEIGHT, uno::Any( sal_Int32( 0 ) ) );
1694 if ( rDM_Impl.IsNewDoc() && xFollowPageStyle.is() )
1695 ApplyProperties_( xFollowPageStyle );
1697 //todo: creating a "First Page" style depends on HasTitlePage and _fFacingPage_
1698 if ( m_bTitlePage )
1700 CopyLastHeaderFooter( true, rDM_Impl );
1701 PrepareHeaderFooterProperties( true );
1702 uno::Reference< beans::XPropertySet > xFirstPageStyle = GetPageStyle(
1703 rDM_Impl, true );
1704 if ( rDM_Impl.IsNewDoc() )
1705 ApplyProperties_( xFirstPageStyle );
1707 if ( xColumns.is() )
1708 xFirstPageStyle->setPropertyValue(
1709 getPropertyName( PROP_TEXT_COLUMNS ), uno::Any( xColumns ) );
1712 ApplyBorderToPageStyles( rDM_Impl, m_eBorderApply, m_eBorderOffsetFrom );
1716 //now apply this break at the first paragraph of this section
1717 uno::Reference< beans::XPropertySet > xRangeProperties( lcl_GetRangeProperties( m_bIsFirstSection, rDM_Impl, m_xStartingRange ) );
1719 // Handle page breaks with odd/even page numbering. We need to use an extra page style for setting the page style
1720 // to left/right, because if we set it to the normal style, we'd set it to "First Page"/"Default Style", which would
1721 // break them (all default pages would be only left or right).
1722 if ( m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_evenPage || m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_oddPage )
1724 OUString* pageStyle = m_bTitlePage ? &m_sFirstPageStyleName : &m_sFollowPageStyleName;
1725 OUString evenOddStyleName = rDM_Impl.GetUnusedPageStyleName();
1726 uno::Reference< beans::XPropertySet > evenOddStyle(
1727 rDM_Impl.GetTextFactory()->createInstance( "com.sun.star.style.PageStyle" ),
1728 uno::UNO_QUERY );
1729 // Unfortunately using setParent() does not work for page styles, so make a deep copy of the page style.
1730 uno::Reference< beans::XPropertySet > pageProperties( m_bTitlePage ? m_aFirstPageStyle : m_aFollowPageStyle );
1731 uno::Reference< beans::XPropertySetInfo > pagePropertiesInfo( pageProperties->getPropertySetInfo() );
1732 const uno::Sequence< beans::Property > propertyList( pagePropertiesInfo->getProperties() );
1733 // Ignore write-only properties.
1734 static const o3tl::sorted_vector<OUString> aDenylist
1735 = { "FooterBackGraphicURL", "BackGraphicURL", "HeaderBackGraphicURL" };
1736 for ( const auto& rProperty : propertyList )
1738 if ( (rProperty.Attributes & beans::PropertyAttribute::READONLY) == 0 )
1740 if (aDenylist.find(rProperty.Name) == aDenylist.end())
1741 evenOddStyle->setPropertyValue(
1742 rProperty.Name,
1743 pageProperties->getPropertyValue(rProperty.Name));
1746 evenOddStyle->setPropertyValue( "FollowStyle", uno::Any( *pageStyle ) );
1747 rDM_Impl.GetPageStyles()->insertByName( evenOddStyleName, uno::Any( evenOddStyle ) );
1748 evenOddStyle->setPropertyValue( "HeaderIsOn", uno::Any( false ) );
1749 evenOddStyle->setPropertyValue( "FooterIsOn", uno::Any( false ) );
1750 CopyHeaderFooter(rDM_Impl, pageProperties, evenOddStyle);
1751 *pageStyle = evenOddStyleName; // And use it instead of the original one (which is set as follow of this one).
1752 if ( m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_evenPage )
1753 evenOddStyle->setPropertyValue( getPropertyName( PROP_PAGE_STYLE_LAYOUT ), uno::Any( style::PageStyleLayout_LEFT ) );
1754 else if ( m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_oddPage )
1755 evenOddStyle->setPropertyValue( getPropertyName( PROP_PAGE_STYLE_LAYOUT ), uno::Any( style::PageStyleLayout_RIGHT ) );
1758 if (rDM_Impl.m_xAltChunkStartingRange.is())
1760 xRangeProperties.set(rDM_Impl.m_xAltChunkStartingRange, uno::UNO_QUERY);
1762 if (xRangeProperties.is() && (rDM_Impl.IsNewDoc() || rDM_Impl.IsAltChunk()))
1764 // Avoid setting page style in case of autotext: so inserting the autotext at the
1765 // end of the document does not introduce an unwanted page break.
1766 // Also avoid setting the page style at the very beginning if it still is the default page style.
1767 const OUString sPageStyle = m_bTitlePage ? m_sFirstPageStyleName : m_sFollowPageStyleName;
1768 if (!rDM_Impl.IsReadGlossaries()
1769 && !rDM_Impl.IsInFootOrEndnote()
1770 && !(m_bIsFirstSection && sPageStyle == getPropertyName( PROP_STANDARD ) && m_nPageNumber < 0)
1773 xRangeProperties->setPropertyValue(
1774 getPropertyName( PROP_PAGE_DESC_NAME ),
1775 uno::Any(sPageStyle) );
1778 if (0 <= m_nPageNumber)
1780 sal_Int16 nPageNumber = static_cast< sal_Int16 >(m_nPageNumber);
1781 xRangeProperties->setPropertyValue(getPropertyName(PROP_PAGE_NUMBER_OFFSET),
1782 uno::Any(nPageNumber));
1786 catch ( const uno::Exception& )
1788 TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::CloseSectionGroup" );
1792 // Now that the margins are known, resize relative width shapes because some shapes in LO do not support percentage-sizes
1793 sal_Int32 nParagraphWidth = GetPageWidth() - m_nLeftMargin - m_nRightMargin;
1794 if ( m_nColumnCount > 1 )
1796 // skip custom-width columns since we don't know what column the shape is in.
1797 if ( !m_aColWidth.empty() )
1798 nParagraphWidth = 0;
1799 else
1800 nParagraphWidth = (nParagraphWidth - (m_nColumnDistance * (m_nColumnCount - 1))) / m_nColumnCount;
1802 if ( nParagraphWidth > 0 )
1804 const OUString & sPropRelativeWidth = getPropertyName(PROP_RELATIVE_WIDTH);
1805 for ( const auto& xShape : m_xRelativeWidthShapes )
1807 const uno::Reference<beans::XPropertySet> xShapePropertySet( xShape, uno::UNO_QUERY );
1808 if ( xShapePropertySet->getPropertySetInfo()->hasPropertyByName(sPropRelativeWidth) )
1810 sal_uInt16 nPercent = 0;
1813 xShapePropertySet->getPropertyValue(sPropRelativeWidth) >>= nPercent;
1815 catch (const css::uno::RuntimeException& e)
1817 // May happen e.g. when text frame has no frame format
1818 // See sw/qa/extras/ooxmlimport/data/n779627.docx
1819 SAL_WARN("writerfilter", "Getting relative width failed. " << e.Message);
1821 if ( nPercent )
1823 const sal_Int32 nWidth = nParagraphWidth * nPercent / 100;
1824 xShape->setSize( awt::Size( nWidth, xShape->getSize().Height ) );
1829 m_xRelativeWidthShapes.clear();
1831 rDM_Impl.SetIsLastSectionGroup( false );
1832 rDM_Impl.SetIsFirstParagraphInSection( true );
1834 if ( !rDM_Impl.IsInFootOrEndnote() && !rDM_Impl.IsInComments() )
1836 rDM_Impl.m_bHasFtn = false;
1837 rDM_Impl.m_bHasFtnSep = false;
1841 // Clear the flag that says we should take the header/footer content from
1842 // the previous section. This should be called when we encounter a header
1843 // or footer definition for this section.
1844 void SectionPropertyMap::ClearHeaderFooterLinkToPrevious( bool bHeader, PageType eType )
1846 if ( bHeader )
1848 switch ( eType )
1850 case PAGE_FIRST: m_bFirstPageHeaderLinkToPrevious = false; break;
1851 case PAGE_LEFT: m_bEvenPageHeaderLinkToPrevious = false; break;
1852 case PAGE_RIGHT: m_bDefaultHeaderLinkToPrevious = false; break;
1853 // no default case as all enumeration values have been covered
1856 else
1858 switch ( eType )
1860 case PAGE_FIRST: m_bFirstPageFooterLinkToPrevious = false; break;
1861 case PAGE_LEFT: m_bEvenPageFooterLinkToPrevious = false; break;
1862 case PAGE_RIGHT: m_bDefaultFooterLinkToPrevious = false; break;
1867 namespace {
1869 class NamedPropertyValue
1871 private:
1872 OUString m_aName;
1874 public:
1875 explicit NamedPropertyValue( OUString i_aStr )
1876 : m_aName(std::move( i_aStr ))
1880 bool operator() ( beans::PropertyValue const & aVal )
1882 return aVal.Name == m_aName;
1888 void SectionPropertyMap::ApplyProperties_( const uno::Reference< beans::XPropertySet >& xStyle )
1890 uno::Reference< beans::XMultiPropertySet > const xMultiSet( xStyle, uno::UNO_QUERY );
1892 std::vector< OUString > vNames;
1893 std::vector< uno::Any > vValues;
1895 // Convert GetPropertyValues() value into something useful
1896 const uno::Sequence< beans::PropertyValue > vPropVals = GetPropertyValues();
1898 //Temporarily store the items that are in grab bags
1899 uno::Sequence< beans::PropertyValue > vCharVals;
1900 uno::Sequence< beans::PropertyValue > vParaVals;
1901 const beans::PropertyValue* pCharGrabBag = std::find_if( vPropVals.begin(), vPropVals.end(), NamedPropertyValue( "CharInteropGrabBag" ) );
1902 if ( pCharGrabBag != vPropVals.end() )
1903 (pCharGrabBag->Value) >>= vCharVals;
1904 const beans::PropertyValue* pParaGrabBag = std::find_if( vPropVals.begin(), vPropVals.end(), NamedPropertyValue( "ParaInteropGrabBag" ) );
1905 if ( pParaGrabBag != vPropVals.end() )
1906 (pParaGrabBag->Value) >>= vParaVals;
1908 for ( const beans::PropertyValue* pIter = vPropVals.begin(); pIter != vPropVals.end(); ++pIter )
1910 if ( pIter != pCharGrabBag && pIter != pParaGrabBag
1911 && pIter->Name != "IsProtected" //section-only property
1914 vNames.push_back( pIter->Name );
1915 vValues.push_back( pIter->Value );
1918 for ( const beans::PropertyValue & v : std::as_const(vCharVals) )
1920 vNames.push_back( v.Name );
1921 vValues.push_back( v.Value );
1923 for ( const beans::PropertyValue & v : std::as_const(vParaVals) )
1925 vNames.push_back( v.Name );
1926 vValues.push_back( v.Value );
1929 if ( xMultiSet.is() )
1933 xMultiSet->setPropertyValues( comphelper::containerToSequence( vNames ), comphelper::containerToSequence( vValues ) );
1934 return;
1936 catch ( const uno::Exception& )
1938 TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::ApplyProperties_" );
1941 for ( size_t i = 0; i < vNames.size(); ++i )
1945 if ( xStyle.is() )
1946 xStyle->setPropertyValue( vNames[i], vValues[i] );
1948 catch ( const uno::Exception& )
1950 TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::ApplyProperties_" );
1955 sal_Int32 SectionPropertyMap::GetPageWidth() const
1957 return getProperty( PROP_WIDTH )->second.get<sal_Int32>();
1960 StyleSheetPropertyMap::StyleSheetPropertyMap()
1961 : mnListLevel( -1 )
1962 , mnOutlineLevel( -1 )
1966 ParagraphProperties::ParagraphProperties()
1967 : m_bFrameMode( false )
1968 , m_nDropCap( NS_ooxml::LN_Value_doc_ST_DropCap_none )
1969 , m_nLines( 0 )
1970 , m_w( -1 )
1971 , m_h( -1 )
1972 , m_nWrap( text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE )
1973 , m_hAnchor( -1 )
1974 , m_vAnchor( -1 )
1975 , m_x( -1 )
1976 , m_bxValid( false )
1977 , m_y( -1 )
1978 , m_byValid( false )
1979 , m_hSpace( -1 )
1980 , m_vSpace( -1 )
1981 , m_hRule( -1 )
1982 , m_xAlign( -1 )
1983 , m_yAlign( -1 )
1984 , m_nDropCapLength( 0 )
1988 void ParagraphProperties::ResetFrameProperties()
1990 m_bFrameMode = false;
1991 m_nDropCap = NS_ooxml::LN_Value_doc_ST_DropCap_none;
1992 m_nLines = 0;
1993 m_w = -1;
1994 m_h = -1;
1995 m_nWrap = text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE;
1996 m_hAnchor = -1;
1997 m_vAnchor = -1;
1998 m_x = -1;
1999 m_bxValid = false;
2000 m_y = -1;
2001 m_byValid = false;
2002 m_hSpace = -1;
2003 m_vSpace = -1;
2004 m_hRule = -1;
2005 m_xAlign = -1;
2006 m_yAlign = -1;
2007 m_nDropCapLength = 0;
2010 bool TablePropertyMap::getValue( TablePropertyMapTarget eWhich, sal_Int32& nFill )
2012 if ( eWhich < TablePropertyMapTarget_MAX )
2014 if ( m_aValidValues[eWhich].bValid )
2015 nFill = m_aValidValues[eWhich].nValue;
2016 return m_aValidValues[eWhich].bValid;
2018 else
2020 OSL_FAIL( "invalid TablePropertyMapTarget" );
2021 return false;
2025 void TablePropertyMap::setValue( TablePropertyMapTarget eWhich, sal_Int32 nSet )
2027 if ( eWhich < TablePropertyMapTarget_MAX )
2029 m_aValidValues[eWhich].bValid = true;
2030 m_aValidValues[eWhich].nValue = nSet;
2032 else
2033 OSL_FAIL( "invalid TablePropertyMapTarget" );
2036 void TablePropertyMap::insertTableProperties( const PropertyMap* pMap, const bool bOverwrite )
2038 #ifdef DBG_UTIL
2039 TagLogger::getInstance().startElement( "TablePropertyMap.insertTableProperties" );
2040 pMap->dumpXml();
2041 #endif
2043 const TablePropertyMap* pSource = dynamic_cast< const TablePropertyMap* >(pMap);
2044 if ( pSource )
2046 for ( sal_Int32 eTarget = TablePropertyMapTarget_START;
2047 eTarget < TablePropertyMapTarget_MAX; ++eTarget )
2049 if ( pSource->m_aValidValues[eTarget].bValid && (bOverwrite || !m_aValidValues[eTarget].bValid) )
2051 m_aValidValues[eTarget].bValid = true;
2052 m_aValidValues[eTarget].nValue = pSource->m_aValidValues[eTarget].nValue;
2057 #ifdef DBG_UTIL
2058 dumpXml();
2059 TagLogger::getInstance().endElement();
2060 #endif
2063 } // namespace writerfilter
2065 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */