1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <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>
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
)
94 else if ( rPropPair
.second
.getGrabBagType() == PARA_GRAB_BAG
)
96 else if ( rPropPair
.second
.getGrabBagType() == CELL_GRAB_BAG
)
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
)
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
)
146 pCharGrabBagValues
[nCharGrabBagValue
].Name
= getPropertyName( rPropPair
.first
);
147 pCharGrabBagValues
[nCharGrabBagValue
].Value
= rPropPair
.second
.getValue();
151 else if ( rPropPair
.second
.getGrabBagType() == PARA_GRAB_BAG
)
153 pParaGrabBagValues
[nParaGrabBagValue
].Name
= getPropertyName( rPropPair
.first
);
154 pParaGrabBagValues
[nParaGrabBagValue
].Value
= rPropPair
.second
.getValue();
157 else if ( rPropPair
.second
.getGrabBagType() == CELL_GRAB_BAG
)
159 pCellGrabBagValues
[nCellGrabBagValue
].Name
= getPropertyName( rPropPair
.first
);
160 pCellGrabBagValues
[nCellGrabBagValue
].Value
= rPropPair
.second
.getValue();
163 else if ( rPropPair
.second
.getGrabBagType() == ROW_GRAB_BAG
)
165 pRowGrabBagValues
[nRowGrabBagValue
].Name
= getPropertyName( rPropPair
.first
);
166 pRowGrabBagValues
[nRowGrabBagValue
].Value
= rPropPair
.second
.getValue();
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();
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
) ) );
187 m_aValues
.push_back( makePropertyValue( "ParaInteropGrabBag", uno::Any( aParaGrabBagValues
) ) );
190 m_aValues
.push_back( makePropertyValue( "CellInteropGrabBag", uno::Any( aCellGrabBagValues
) ) );
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
);
207 static void lcl_AnyToTag( const uno::Any
& rAny
)
213 TagLogger::getInstance().attribute( "value", rAny
);
217 TagLogger::getInstance().attribute( "unsignedValue", 0 );
220 sal_uInt32 auInt
= 0;
222 TagLogger::getInstance().attribute( "unsignedValue", auInt
);
225 if ( rAny
>>= aFloat
)
227 TagLogger::getInstance().attribute( "floatValue", rAny
);
231 TagLogger::getInstance().attribute( "unsignedValue", 0 );
236 TagLogger::getInstance().attribute( "stringValue", aStr
);
244 void PropertyMap::Insert( PropertyIds eId
, const uno::Any
& rAny
, bool bOverwrite
, GrabBagType i_GrabBagType
, bool bDocDefault
)
247 const OUString
& rInsert
= getPropertyName(eId
);
249 TagLogger::getInstance().startElement("propertyMap.insert");
250 TagLogger::getInstance().attribute("name", rInsert
);
252 TagLogger::getInstance().endElement();
256 m_vMap
.insert(std::make_pair(eId
, PropValue(rAny
, i_GrabBagType
, bDocDefault
)));
258 m_vMap
[eId
] = PropValue(rAny
, i_GrabBagType
);
263 void PropertyMap::Erase( PropertyIds eId
)
265 // Safe call to erase, it throws no exceptions, even if eId is not in m_vMap
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
>();
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() )
291 return aIter
->second
.getIsDocDefault();
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() );
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
);
323 rPropPair
.second
.getValue() >>= aFloat
;
324 TagLogger::getInstance().attribute( "floatValue", aFloat
);
326 rPropPair
.second
.getValue() >>= auInt
;
327 TagLogger::getInstance().attribute( "stringValue", std::u16string_view() );
336 TagLogger::getInstance().endElement();
339 TagLogger::getInstance().endElement();
343 void PropertyMap::InsertProps( const PropertyMapPtr
& rMap
, const bool bOverwrite
)
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)));
355 m_vMap
[rPropPair
.first
] = rPropPair
.second
;
359 insertTableProperties( rMap
.get(), bOverwrite
);
364 void PropertyMap::insertTableProperties( const PropertyMap
*, const bool )
367 TagLogger::getInstance().element( "PropertyMap.insertTableProperties" );
371 void PropertyMap::printProperties()
374 TagLogger::getInstance().startElement( "properties" );
376 for ( const auto& rPropPair
: m_vMap
)
378 SAL_INFO( "writerfilter", getPropertyName( rPropPair
.first
) );
380 table::BorderLine2 aLine
;
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();
400 (void) this; // avoid loplugin:staticmethods
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 )
416 , m_nLeftMargin( o3tl::convert(1, o3tl::Length::in
, o3tl::Length::mm100
) )
417 , m_nRightMargin( o3tl::convert(1, o3tl::Length::in
, o3tl::Length::mm100
) )
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
) )
424 , m_nGridLinePitch( 1 )
425 , m_nDxtCharSpace( 0 )
426 , m_bGridSnapToChars( true )
428 , m_nLnc(NS_ooxml::LN_Value_ST_LineNumberRestart_newPage
)
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 )
441 static sal_Int32 nNumber
= 0;
442 m_nDebugSectionNumber
= nNumber
++;
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
)) ) );
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
,
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
;
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" ),
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
;
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" ),
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" );
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:
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
559 page border offset from:
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 );
574 case BorderApply::ToFirstPageInSection
: // first page
575 if ( !m_sFirstPageStyleName
.isEmpty() )
576 xFirst
= GetPageStyle( rDM_Impl
, true );
578 case BorderApply::ToAllButFirstInSection
: // left and right
579 if ( !m_sFollowPageStyleName
.isEmpty() )
580 xFirst
= GetPageStyle( rDM_Impl
, false );
586 // has to be sorted like enum BorderPosition: l-r-t-b
587 const PropertyIds aBorderIds
[4] =
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] =
611 for ( sal_Int32 nBorder
= 0; nBorder
< 4; ++nBorder
)
613 if ( m_oBorderLines
[nBorder
] )
615 const OUString
& sBorderName
= getPropertyName( aBorderIds
[nBorder
] );
617 xFirst
->setPropertyValue( sBorderName
, uno::Any( *m_oBorderLines
[nBorder
] ) );
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
;
627 SetBorderDistance( xFirst
, aMarginIds
[nBorder
], aBorderDistanceIds
[nBorder
],
628 m_nBorderDistances
[nBorder
], eOffsetFrom
, nLineWidth
, rDM_Impl
);
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
] );
639 xFirst
->setPropertyValue( getPropertyName( PROP_SHADOW_FORMAT
), uno::Any( aFormat
) );
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
;
658 void SectionPropertyMap::SetBorderDistance( const uno::Reference
< beans::XPropertySet
>& xStyle
,
659 PropertyIds eMarginId
,
662 BorderOffsetFrom eOffsetFrom
,
663 sal_uInt32 nLineWidth
,
664 DomainMapper_Impl
& rDM_Impl
)
668 const OUString
& sMarginName
= getPropertyName( eMarginId
);
669 const OUString
& sBorderDistanceName
= getPropertyName( eDistId
);
670 uno::Any aMargin
= xStyle
->getPropertyValue( sMarginName
);
671 sal_Int32 nMargin
= 0;
673 editeng::BorderDistanceFromWord(eOffsetFrom
== BorderOffsetFrom::Edge
, nMargin
, nDistance
,
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*/ )
721 std::optional
< PropertyMap::Property
> pProp
= getProperty( PROP_WRITING_MODE
);
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();
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
);
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
];
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();
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
);
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" );
831 bool SectionPropertyMap::HasHeader( bool bFirstPage
) const
834 if ( (bFirstPage
&& m_aFirstPageStyle
.is()) || (!bFirstPage
&& m_aFollowPageStyle
.is()) )
837 m_aFirstPageStyle
->getPropertyValue(
838 getPropertyName( PROP_HEADER_IS_ON
) ) >>= bRet
;
840 m_aFollowPageStyle
->getPropertyValue(
841 getPropertyName( PROP_HEADER_IS_ON
) ) >>= bRet
;
846 bool SectionPropertyMap::HasFooter( bool bFirstPage
) const
849 if ( (bFirstPage
&& m_aFirstPageStyle
.is()) || (!bFirstPage
&& m_aFollowPageStyle
.is()) )
852 m_aFirstPageStyle
->getPropertyValue( getPropertyName( PROP_FOOTER_IS_ON
) ) >>= bRet
;
854 m_aFollowPageStyle
->getPropertyValue( getPropertyName( PROP_FOOTER_IS_ON
) ) >>= 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
)
866 const OUString
& sName
= getPropertyName( ePropId
);
868 SAL_INFO( "writerfilter", "Copying " << sName
);
869 uno::Reference
< text::XTextCopy
> xTxt
;
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
,
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
)
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
,
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();
967 const bool bUseEvenPages
= rDM_Impl
.GetSettingsTable()->GetEvenAndOddHeaders();
968 uno::Reference
< beans::XPropertySet
> xPrevStyle
= pLastContext
->GetPageStyle( rDM_Impl
,
970 uno::Reference
< beans::XPropertySet
> xStyle
= GetPageStyle( rDM_Impl
,
975 CopyHeaderFooter(rDM_Impl
, xPrevStyle
, xStyle
,
976 !m_bFirstPageHeaderLinkToPrevious
, true,
977 !m_bFirstPageFooterLinkToPrevious
, true );
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
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 );
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();
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 );
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)
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
1180 if (rAnchor
.m_aAnchoredObjects
.size() < 4)
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
)
1191 uno::Reference
<beans::XPropertySet
> xShape(rAnchored
.m_xAnchoredObject
, uno::UNO_QUERY
);
1197 bool bOpaque
= true;
1198 xShape
->getPropertyValue("Opaque") >>= bOpaque
;
1204 if (nOpaqueCount
< 1)
1209 // Analyze the anchored objects of this paragraph, now that we know the
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
);
1218 uno::Reference
<beans::XPropertySet
> xPropertySet(xShape
, uno::UNO_QUERY
);
1219 if (!xPropertySet
.is())
1222 // Ignore objects with no wrapping.
1223 text::WrapTextMode eWrap
= text::WrapTextMode_THROUGH
;
1224 xPropertySet
->getPropertyValue("Surround") >>= eWrap
;
1225 if (eWrap
== text::WrapTextMode_THROUGH
)
1228 // Use the original left margin, in case GraphicImport::lcl_sprm() reduced the doc model
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
)
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
);
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
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
;
1275 OUString sTableName
;
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
;
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() )
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
);
1302 if (!sTableName
.isEmpty() && !sCellName
.isEmpty())
1304 uno::Reference
<text::XTextCursor
> xRangeCursor
= xCell
->createTextCursorByRange( xRange
);
1305 if ( xRangeCursor
.is() )
1308 sal_Int32 nLen
= xRange
->getString().getLength();
1309 redLen
.push_back(nLen
);
1310 xRangeCursor
->gotoStart(true);
1311 redPos
.push_back(xRangeCursor
->getString().getLength() - nLen
);
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)
1331 beans::PropertyValues
aRedlineProperties( 3 );
1332 // skip failed createTextCursorByRange()
1333 if (redPos
[i
/3] == -1)
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
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
1380 bool bIsLandscape
= false;
1381 std::optional
< PropertyMap::Property
> pProp
= getProperty( PROP_IS_LANDSCAPE
);
1383 pProp
->second
>>= bIsLandscape
;
1385 bool bPrevIsLandscape
= false;
1386 pProp
= pPrevSection
->getProperty( PROP_IS_LANDSCAPE
);
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");
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
);
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
));
1495 uno::Reference
<text::XParagraphCursor
> const xPCursor(xCursor
,
1496 uno::UNO_QUERY_THROW
);
1497 float fCharHeight
= 0;
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
));
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
);
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
);
1604 pProp
->second
>>= nHeight
;
1606 sal_Int32 nWidth
= 1;
1607 pProp
= getProperty( PROP_WIDTH
);
1609 pProp
->second
>>= nWidth
;
1611 sal_Int16 nWritingMode
= text::WritingMode2::LR_TB
;
1612 pProp
= getProperty( PROP_WRITING_MODE
);
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
;
1622 if ( nGridLinePitch
< 1 || nGridLinePitch
> 31680 )
1624 SAL_WARN( "writerfilter", "sep.dyaLinePitch outside legal range: " << nGridLinePitch
);
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
) ) );
1633 nGridType
= text::TextGridMode::NONE
;
1636 if ( nGridType
== text::TextGridMode::LINES_AND_CHARS
)
1638 if (!m_nDxtCharSpace
)
1639 nGridType
= text::TextGridMode::LINES
;
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" );
1650 std::optional
< PropertyMap::Property
> pPropHeight
= pEntry
->m_pProperties
->getProperty( PROP_CHAR_HEIGHT_ASIAN
);
1654 if ( pPropHeight
->second
>>= fHeight
)
1655 nCharWidth
= ConversionHelper::convertTwipToMM100( static_cast<tools::Long
>(fHeight
* 20.0 + 0.5) );
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);
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_
1700 CopyLastHeaderFooter( true, rDM_Impl
);
1701 PrepareHeaderFooterProperties( true );
1702 uno::Reference
< beans::XPropertySet
> xFirstPageStyle
= GetPageStyle(
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" ),
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(
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;
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
);
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
)
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
1860 case PAGE_FIRST
: m_bFirstPageFooterLinkToPrevious
= false; break;
1861 case PAGE_LEFT
: m_bEvenPageFooterLinkToPrevious
= false; break;
1862 case PAGE_RIGHT
: m_bDefaultFooterLinkToPrevious
= false; break;
1869 class NamedPropertyValue
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
) );
1936 catch ( const uno::Exception
& )
1938 TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::ApplyProperties_" );
1941 for ( size_t i
= 0; i
< vNames
.size(); ++i
)
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()
1962 , mnOutlineLevel( -1 )
1966 ParagraphProperties::ParagraphProperties()
1967 : m_bFrameMode( false )
1968 , m_nDropCap( NS_ooxml::LN_Value_doc_ST_DropCap_none
)
1972 , m_nWrap( text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
)
1976 , m_bxValid( false )
1978 , m_byValid( false )
1984 , m_nDropCapLength( 0 )
1988 void ParagraphProperties::ResetFrameProperties()
1990 m_bFrameMode
= false;
1991 m_nDropCap
= NS_ooxml::LN_Value_doc_ST_DropCap_none
;
1995 m_nWrap
= text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
;
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
;
2020 OSL_FAIL( "invalid TablePropertyMapTarget" );
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
;
2033 OSL_FAIL( "invalid TablePropertyMapTarget" );
2036 void TablePropertyMap::insertTableProperties( const PropertyMap
* pMap
, const bool bOverwrite
)
2039 TagLogger::getInstance().startElement( "TablePropertyMap.insertTableProperties" );
2043 const TablePropertyMap
* pSource
= dynamic_cast< const TablePropertyMap
* >(pMap
);
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
;
2059 TagLogger::getInstance().endElement();
2063 } // namespace writerfilter
2065 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */