LibreOffice Module writerfilter (master) 1
PropertyMap.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21
22#include <string_view>
23
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>
64#include "PropertyMapHelper.hxx"
67#include <utility>
68
69using namespace com::sun::star;
70
71namespace writerfilter::dmapper {
72
73uno::Sequence< beans::PropertyValue > PropertyMap::GetPropertyValues( bool bCharGrabBag )
74{
76
77 if ( !m_aValues.empty() || m_vMap.empty() )
79
80 size_t nCharGrabBag = 0;
81 size_t nParaGrabBag = 0;
82 size_t nCellGrabBag = 0;
83 size_t nRowGrabBag = 0;
84
85 const PropValue* pParaStyleProp = nullptr;
86 const PropValue* pCharStyleProp = nullptr;
87 const PropValue* pNumRuleProp = nullptr;
88
89 m_aValues.reserve( m_vMap.size() );
90 for ( const auto& rPropPair : m_vMap )
91 {
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 )
99 {
101 rPropPair.second.getValue() >>= aSeq;
102 nCellGrabBag += aSeq.getLength();
103 }
104 else if ( rPropPair.second.getGrabBagType() == ROW_GRAB_BAG )
105 nRowGrabBag++;
106
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;
110 }
111
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 )
120
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;
135
136 for ( const auto& rPropPair : m_vMap )
137 {
138 if ( rPropPair.first != PROP_PARA_STYLE_NAME &&
139 rPropPair.first != PROP_CHAR_STYLE_NAME &&
140 rPropPair.first != PROP_NUMBERING_RULES )
141 {
142 if ( rPropPair.second.getGrabBagType() == CHAR_GRAB_BAG )
143 {
144 if ( bCharGrabBag )
145 {
146 pCharGrabBagValues[nCharGrabBagValue].Name = getPropertyName( rPropPair.first );
147 pCharGrabBagValues[nCharGrabBagValue].Value = rPropPair.second.getValue();
148 ++nCharGrabBagValue;
149 }
150 }
151 else if ( rPropPair.second.getGrabBagType() == PARA_GRAB_BAG )
152 {
153 pParaGrabBagValues[nParaGrabBagValue].Name = getPropertyName( rPropPair.first );
154 pParaGrabBagValues[nParaGrabBagValue].Value = rPropPair.second.getValue();
155 ++nParaGrabBagValue;
156 }
157 else if ( rPropPair.second.getGrabBagType() == CELL_GRAB_BAG )
158 {
159 pCellGrabBagValues[nCellGrabBagValue].Name = getPropertyName( rPropPair.first );
160 pCellGrabBagValues[nCellGrabBagValue].Value = rPropPair.second.getValue();
161 ++nCellGrabBagValue;
162 }
163 else if ( rPropPair.second.getGrabBagType() == ROW_GRAB_BAG )
164 {
165 pRowGrabBagValues[nRowGrabBagValue].Name = getPropertyName( rPropPair.first );
166 pRowGrabBagValues[nRowGrabBagValue].Value = rPropPair.second.getValue();
167 ++nRowGrabBagValue;
168 }
169 else if ( rPropPair.first == PROP_CELL_INTEROP_GRAB_BAG )
170 {
172 rPropPair.second.getValue() >>= aSeq;
173 std::copy(std::cbegin(aSeq), std::cend(aSeq), pCellGrabBagValues + nCellGrabBagValue);
174 nCellGrabBagValue += aSeq.getLength();
175 }
176 else
177 {
178 m_aValues.push_back( makePropertyValue( getPropertyName( rPropPair.first ), rPropPair.second.getValue() ) );
179 }
180 }
181 }
182
183 if ( nCharGrabBag && bCharGrabBag )
184 m_aValues.push_back( makePropertyValue( "CharInteropGrabBag", uno::Any( aCharGrabBagValues ) ) );
185
186 if ( nParaGrabBag )
187 m_aValues.push_back( makePropertyValue( "ParaInteropGrabBag", uno::Any( aParaGrabBagValues ) ) );
188
189 if ( nCellGrabBag )
190 m_aValues.push_back( makePropertyValue( "CellInteropGrabBag", uno::Any( aCellGrabBagValues ) ) );
191
192 if ( nRowGrabBag )
193 m_aValues.push_back( makePropertyValue( "RowInteropGrabBag", uno::Any( aRowGrabBagValues ) ) );
194
196}
197
198std::vector< PropertyIds > PropertyMap::GetPropertyIds()
199{
200 std::vector< PropertyIds > aRet;
201 for ( const auto& rPropPair : m_vMap )
202 aRet.push_back( rPropPair.first );
203 return aRet;
204}
205
206#ifdef DBG_UTIL
207static void lcl_AnyToTag( const uno::Any& rAny )
208{
209 try {
210 sal_Int32 aInt = 0;
211 if ( rAny >>= aInt )
212 {
213 TagLogger::getInstance().attribute( "value", rAny );
214 }
215 else
216 {
217 TagLogger::getInstance().attribute( "unsignedValue", 0 );
218 }
219
220 sal_uInt32 auInt = 0;
221 rAny >>= auInt;
222 TagLogger::getInstance().attribute( "unsignedValue", auInt );
223
224 float aFloat = 0.0f;
225 if ( rAny >>= aFloat )
226 {
227 TagLogger::getInstance().attribute( "floatValue", rAny );
228 }
229 else
230 {
231 TagLogger::getInstance().attribute( "unsignedValue", 0 );
232 }
233
234 OUString aStr;
235 rAny >>= aStr;
236 TagLogger::getInstance().attribute( "stringValue", aStr );
237 }
238 catch ( ... )
239 {
240 }
241}
242#endif
243
244void PropertyMap::Insert( PropertyIds eId, const uno::Any& rAny, bool bOverwrite, GrabBagType i_GrabBagType, bool bDocDefault )
245{
246#ifdef DBG_UTIL
247 const OUString& rInsert = getPropertyName(eId);
248
249 TagLogger::getInstance().startElement("propertyMap.insert");
250 TagLogger::getInstance().attribute("name", rInsert);
251 lcl_AnyToTag(rAny);
252 TagLogger::getInstance().endElement();
253#endif
254
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);
259
260 Invalidate();
261}
262
263void PropertyMap::Erase( PropertyIds eId )
264{
265 // Safe call to erase, it throws no exceptions, even if eId is not in m_vMap
266 m_vMap.erase(eId);
267
268 Invalidate();
269}
270
271std::optional< PropertyMap::Property > PropertyMap::getProperty( PropertyIds eId ) const
272{
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() );
278}
279
280bool PropertyMap::isSet( PropertyIds eId) const
281{
282 return m_vMap.find( eId ) != m_vMap.end();
283}
284
285bool PropertyMap::isDocDefault( PropertyIds eId ) const
286{
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();
292}
293
294#ifdef DBG_UTIL
295void PropertyMap::dumpXml() const
296{
297 TagLogger::getInstance().startElement( "PropertyMap" );
298
299 for ( const auto& rPropPair : m_vMap )
300 {
301 TagLogger::getInstance().startElement( "property" );
302
303 TagLogger::getInstance().attribute( "name", getPropertyName( rPropPair.first ) );
304
305 switch ( rPropPair.first )
306 {
308 lcl_DumpTableColumnSeparators( rPropPair.second.getValue() );
309 break;
310 default:
311 {
312 try
313 {
314 sal_Int32 aInt = 0;
315 rPropPair.second.getValue() >>= aInt;
316 TagLogger::getInstance().attribute( "value", aInt );
317
318 sal_uInt32 auInt = 0;
319 rPropPair.second.getValue() >>= auInt;
320 TagLogger::getInstance().attribute( "unsignedValue", auInt );
321
322 float aFloat = 0.0;
323 rPropPair.second.getValue() >>= aFloat;
324 TagLogger::getInstance().attribute( "floatValue", aFloat );
325
326 rPropPair.second.getValue() >>= auInt;
327 TagLogger::getInstance().attribute( "stringValue", std::u16string_view() );
328 }
329 catch ( ... )
330 {
331 }
332 }
333 break;
334 }
335
336 TagLogger::getInstance().endElement();
337 }
338
339 TagLogger::getInstance().endElement();
340}
341#endif
342
343void PropertyMap::InsertProps( const PropertyMapPtr& rMap, const bool bOverwrite )
344{
345 if ( !rMap )
346 return;
347
348 for ( const auto& rPropPair : rMap->m_vMap )
349 {
350 if ( bOverwrite || !m_vMap.count(rPropPair.first) )
351 {
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;
356 }
357 }
358
359 insertTableProperties( rMap.get(), bOverwrite );
360
361 Invalidate();
362}
363
364void PropertyMap::insertTableProperties( const PropertyMap*, const bool )
365{
366#ifdef DBG_UTIL
367 TagLogger::getInstance().element( "PropertyMap.insertTableProperties" );
368#endif
369}
370
371void PropertyMap::printProperties()
372{
373#ifdef DBG_UTIL
374 TagLogger::getInstance().startElement( "properties" );
375
376 for ( const auto& rPropPair : m_vMap )
377 {
378 SAL_INFO( "writerfilter", getPropertyName( rPropPair.first ) );
379
380 table::BorderLine2 aLine;
381 sal_Int32 nColor;
382 if ( rPropPair.second.getValue() >>= aLine )
383 {
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();
389 }
390 else if ( rPropPair.second.getValue() >>= nColor )
391 {
392 TagLogger::getInstance().startElement( "color" );
393 TagLogger::getInstance().attribute( "number", nColor );
394 TagLogger::getInstance().endElement();
395 }
396 }
397
398 TagLogger::getInstance().endElement();
399#else
400 (void) this; // avoid loplugin:staticmethods
401#endif
402}
403
404SectionPropertyMap::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 )
439{
440#ifdef DBG_UTIL
441 static sal_Int32 nNumber = 0;
442 m_nDebugSectionNumber = nNumber++;
443#endif
444
445 for ( sal_Int32 nBorder = 0; nBorder < 4; ++nBorder )
446 {
448 m_bBorderShadows[nBorder] = false;
449 }
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
459 // page right margin, 1/100 mm
461 // page top margin, 1/100 mm
463 // page bottom margin, 1/100 mm
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 );
471
472 if ( m_bIsFirstSection )
473 {
476 }
477}
478
480 bool bFirst )
481{
482 const uno::Reference< container::XNameContainer >& xPageStyles = rDM_Impl.GetPageStyles();
483 const uno::Reference < lang::XMultiServiceFactory >& xTextFactory = rDM_Impl.GetTextFactory();
485 try
486 {
487 if ( bFirst )
488 {
489 if ( m_sFirstPageStyleName.isEmpty() && xPageStyles.is() )
490 {
491 assert( !rDM_Impl.IsInFootOrEndnote() && "Don't create useless page styles" );
493 m_aFirstPageStyle.set( xTextFactory->createInstance( "com.sun.star.style.PageStyle" ),
494 uno::UNO_QUERY );
495
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 ) );
501
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",
507 }
508 else if ( !m_aFirstPageStyle.is() && xPageStyles.is() )
509 {
510 xPageStyles->getByName( m_sFirstPageStyleName ) >>= m_aFirstPageStyle;
511 }
512 xRet = m_aFirstPageStyle;
513 }
514 else
515 {
516 if ( m_sFollowPageStyleName.isEmpty() && xPageStyles.is() )
517 {
518 assert( !rDM_Impl.IsInFootOrEndnote() && "Don't create useless page styles" );
520 m_aFollowPageStyle.set( xTextFactory->createInstance( "com.sun.star.style.PageStyle" ),
521 uno::UNO_QUERY );
522 xPageStyles->insertByName( m_sFollowPageStyleName, uno::Any( m_aFollowPageStyle ) );
523 }
524 else if ( !m_aFollowPageStyle.is() && xPageStyles.is() )
525 {
526 xPageStyles->getByName( m_sFollowPageStyleName ) >>= m_aFollowPageStyle;
527 }
528 xRet = m_aFollowPageStyle;
529 }
530
531 }
532 catch ( const uno::Exception& )
533 {
534 DBG_UNHANDLED_EXCEPTION( "writerfilter" );
535 }
536
537 return xRet;
538}
539
540void SectionPropertyMap::SetBorder( BorderPosition ePos, sal_Int32 nLineDistance, const table::BorderLine2& rBorderLine, bool bShadow )
541{
542 m_oBorderLines[ePos] = rBorderLine;
543 m_nBorderDistances[ePos] = nLineDistance;
544 m_bBorderShadows[ePos] = bShadow;
545}
546
548 BorderApply eBorderApply, BorderOffsetFrom eOffsetFrom )
549{
550 /*
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
562 */
565 // todo: negative spacing (from ww8par6.cxx)
566 switch ( eBorderApply )
567 {
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;
584 }
585
586 // has to be sorted like enum BorderPosition: l-r-t-b
587 const PropertyIds aBorderIds[4] =
588 {
593 };
594
595 const PropertyIds aBorderDistanceIds[4] =
596 {
601 };
602
603 const PropertyIds aMarginIds[4] =
604 {
609 };
610
611 for ( sal_Int32 nBorder = 0; nBorder < 4; ++nBorder )
612 {
613 if ( m_oBorderLines[nBorder] )
614 {
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] ) );
620 }
621 if ( m_nBorderDistances[nBorder] >= 0 )
622 {
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 );
632 }
633 }
634
636 {
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 ) );
642 }
643}
644
645table::ShadowFormat PropertyMap::getShadowFromBorder( const table::BorderLine2& rBorder )
646{
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;
656}
657
658void 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 )
665{
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);
675
676 if (eOffsetFrom == BorderOffsetFrom::Edge)
677 {
678 uno::Any aGutterMargin = xStyle->getPropertyValue( "GutterMargin" );
679 sal_Int32 nGutterMargin = 0;
680 aGutterMargin >>= nGutterMargin;
681
682 if (eMarginId == PROP_LEFT_MARGIN && !rDM_Impl.GetSettingsTable()->GetGutterAtTop())
683 {
684 nMargin -= nGutterMargin;
685 nDistance += nGutterMargin;
686 }
687
688 if (eMarginId == PROP_TOP_MARGIN && rDM_Impl.GetSettingsTable()->GetGutterAtTop())
689 {
690 nMargin -= nGutterMargin;
691 nDistance += nGutterMargin;
692 }
693 }
694
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 );
700}
701
702void SectionPropertyMap::DontBalanceTextColumns()
703{
704 try
705 {
706 if ( m_xColumnContainer.is() )
707 m_xColumnContainer->setPropertyValue( "DontBalanceTextColumns", uno::Any( true ) );
708 }
709 catch ( const uno::Exception& )
710 {
711 TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::DontBalanceTextColumns" );
712 }
713}
714
715void SectionPropertyMap::ApplySectionProperties( const uno::Reference< beans::XPropertySet >& xSection, DomainMapper_Impl& /*rDM_Impl*/ )
716{
717 try
718 {
719 if ( xSection.is() )
720 {
721 std::optional< PropertyMap::Property > pProp = getProperty( PROP_WRITING_MODE );
722 if ( pProp )
723 xSection->setPropertyValue( "WritingMode", pProp->second );
724 }
725 }
726 catch ( uno::Exception& )
727 {
728 DBG_UNHANDLED_EXCEPTION("writerfilter", "Exception in SectionPropertyMap::ApplySectionProperties");
729 }
730}
731
732void SectionPropertyMap::ApplyProtectionProperties( uno::Reference< beans::XPropertySet >& xSection, DomainMapper_Impl& rDM_Impl )
733{
734 try
735 {
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 )
740 {
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) );
748 }
749 }
750 catch ( uno::Exception& )
751 {
752 DBG_UNHANDLED_EXCEPTION("writerfilter", "ApplyProtectionProperties failed setting PROP_IS_PROTECTED");
753 }
754}
755
756uno::Reference< text::XTextColumns > SectionPropertyMap::ApplyColumnProperties( const uno::Reference< beans::XPropertySet >& xColumnContainer,
757 DomainMapper_Impl& rDM_Impl )
758{
760 assert( m_nColumnCount > 1 && "ApplyColumnProperties called without any columns" );
761 try
762 {
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) ) )
770 {
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 )
775 {
776 nColSum += m_aColWidth[nCol];
777 if ( nCol )
778 nColSum += m_aColDistance[nCol - 1];
779 }
780
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();
785
786 nColSum = 0;
787 for ( sal_Int32 nCol = 0; nCol < m_nColumnCount; ++nCol )
788 {
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;
796 }
797 if ( nColSum != nRefValue )
798 pColumn[m_nColumnCount - 1].Width += (nRefValue - nColSum);
799 assert( pColumn[m_nColumnCount - 1].Width >= 0 );
800
801 xColumns->setColumns( aColumns );
802 }
803 else
804 {
805 xColumns->setColumnCount( m_nColumnCount );
806 xColumnPropSet->setPropertyValue( getPropertyName( PROP_AUTOMATIC_DISTANCE ), uno::Any( m_nColumnDistance ) );
807 }
808
809 if ( m_bSeparatorLineIsOn )
810 {
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) ) );
817 }
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();
823 }
824 catch ( const uno::Exception& )
825 {
826 TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::ApplyColumnProperties" );
827 }
828 return xColumns;
829}
830
831bool SectionPropertyMap::HasHeader( bool bFirstPage ) const
832{
833 bool bRet = false;
834 if ( (bFirstPage && m_aFirstPageStyle.is()) || (!bFirstPage && m_aFollowPageStyle.is()) )
835 {
836 if ( bFirstPage )
837 m_aFirstPageStyle->getPropertyValue(
839 else
840 m_aFollowPageStyle->getPropertyValue(
842 }
843 return bRet;
844}
845
846bool SectionPropertyMap::HasFooter( bool bFirstPage ) const
847{
848 bool bRet = false;
849 if ( (bFirstPage && m_aFirstPageStyle.is()) || (!bFirstPage && m_aFollowPageStyle.is()) )
850 {
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;
855 }
856 return bRet;
857}
858
859#define MIN_HEAD_FOOT_HEIGHT 100 // minimum header/footer height
860
861void SectionPropertyMap::CopyHeaderFooterTextProperty( const uno::Reference< beans::XPropertySet >& xPrevStyle,
863 PropertyIds ePropId )
864{
865 try {
866 const OUString & sName = getPropertyName( ePropId );
867
868 SAL_INFO( "writerfilter", "Copying " << sName );
870 if ( xStyle.is() )
871 xTxt.set( xStyle->getPropertyValue( sName ), uno::UNO_QUERY_THROW );
872
874 if ( xPrevStyle.is() )
875 xPrevTxt.set( xPrevStyle->getPropertyValue( sName ), uno::UNO_QUERY_THROW );
876
877 xTxt->copyText( xPrevTxt );
878 }
879 catch ( const uno::Exception& )
880 {
881 TOOLS_INFO_EXCEPTION( "writerfilter", "An exception occurred in SectionPropertyMap::CopyHeaderFooterTextProperty( )" );
882 }
883}
884
885// Copy headers and footers from the previous page style.
886void SectionPropertyMap::CopyHeaderFooter( const DomainMapper_Impl& rDM_Impl,
889 bool bOmitRightHeader,
890 bool bOmitLeftHeader,
891 bool bOmitRightFooter,
892 bool bOmitLeftFooter )
893{
894 if (!rDM_Impl.IsNewDoc())
895 { // see also DomainMapper_Impl::PushPageHeaderFooter()
896 return; // tdf#139737 SwUndoInserts cannot deal with new header/footer
897 }
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() )
903 {
904 xPrevStyle->getPropertyValue( sHeaderIsOn ) >>= bHasPrevHeader;
905 xPrevStyle->getPropertyValue( sHeaderIsShared ) >>= bHeaderIsShared;
906 }
907
908 if ( bHasPrevHeader )
909 {
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 )
915 {
916 CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
918 }
919 if ( !bHeaderIsShared && !bOmitLeftHeader )
920 {
921 CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
923 }
924 }
925
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() )
931 {
932 xPrevStyle->getPropertyValue( sFooterIsOn ) >>= bHasPrevFooter;
933 xPrevStyle->getPropertyValue( sFooterIsShared ) >>= bFooterIsShared;
934 }
935
936 if ( !bHasPrevFooter )
937 return;
938
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 )
944 {
945 CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
947 }
948 if ( !bFooterIsShared && !bOmitLeftFooter )
949 {
950 CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
952 }
953}
954
955// Copy header and footer content from the previous docx section as needed.
956//
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.
961void SectionPropertyMap::CopyLastHeaderFooter( bool bFirstPage, DomainMapper_Impl& rDM_Impl )
962{
963 SAL_INFO( "writerfilter", "START>>> SectionPropertyMap::CopyLastHeaderFooter()" );
964 SectionPropertyMap* pLastContext = rDM_Impl.GetLastSectionContext();
965 if ( pLastContext )
966 {
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 );
972
973 if ( bFirstPage )
974 {
975 CopyHeaderFooter(rDM_Impl, xPrevStyle, xStyle,
976 !m_bFirstPageHeaderLinkToPrevious, true,
977 !m_bFirstPageFooterLinkToPrevious, true );
978 }
979 else
980 {
981 CopyHeaderFooter(rDM_Impl, xPrevStyle, xStyle,
982 !m_bDefaultHeaderLinkToPrevious,
983 !(m_bEvenPageHeaderLinkToPrevious && bUseEvenPages),
984 !m_bDefaultFooterLinkToPrevious,
985 !(m_bEvenPageFooterLinkToPrevious && bUseEvenPages));
986 }
987 }
988 SAL_INFO( "writerfilter", "END>>> SectionPropertyMap::CopyLastHeaderFooter()" );
989}
990
991void SectionPropertyMap::PrepareHeaderFooterProperties( bool bFirstPage )
992{
993 bool bCopyFirstToFollow = bFirstPage && m_bTitlePage && m_aFollowPageStyle.is();
994
995 sal_Int32 nTopMargin = m_nTopMargin;
996 sal_Int32 nHeaderHeight = m_nHeaderTop;
997 if ( HasHeader( bFirstPage ) )
998 {
999 nTopMargin = m_nHeaderTop;
1000 nHeaderHeight = m_nTopMargin - m_nHeaderTop;
1001
1002 // minimum header height 1mm
1003 if ( nHeaderHeight < MIN_HEAD_FOOT_HEIGHT )
1004 nHeaderHeight = MIN_HEAD_FOOT_HEIGHT;
1005 }
1006
1007 Insert(PROP_HEADER_IS_DYNAMIC_HEIGHT, uno::Any(m_bDynamicHeightTop));
1008 Insert(PROP_HEADER_DYNAMIC_SPACING, uno::Any(m_bDynamicHeightTop));
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
1012
1013 if ( m_bDynamicHeightTop ) //fixed height header -> see WW8Par6.hxx
1014 {
1015 if (bCopyFirstToFollow && HasHeader(/*bFirstPage=*/true))
1016 {
1017 m_aFollowPageStyle->setPropertyValue("HeaderDynamicSpacing",
1019 m_aFollowPageStyle->setPropertyValue("HeaderHeight",
1021 }
1022 }
1023
1024 sal_Int32 nBottomMargin = m_nBottomMargin;
1025 sal_Int32 nFooterHeight = m_nHeaderBottom;
1026 if ( HasFooter( bFirstPage ) )
1027 {
1028 nBottomMargin = m_nHeaderBottom;
1029 nFooterHeight = m_nBottomMargin - m_nHeaderBottom;
1030
1031 // minimum footer height 1mm
1032 if ( nFooterHeight < MIN_HEAD_FOOT_HEIGHT )
1033 nFooterHeight = MIN_HEAD_FOOT_HEIGHT;
1034 }
1035
1036 Insert(PROP_FOOTER_IS_DYNAMIC_HEIGHT, uno::Any(m_bDynamicHeightBottom));
1037 Insert(PROP_FOOTER_DYNAMIC_SPACING, uno::Any(m_bDynamicHeightBottom));
1039 Insert(PROP_FOOTER_HEIGHT, uno::Any(nFooterHeight));
1040 if (m_bDynamicHeightBottom) //fixed height footer -> see WW8Par6.hxx
1041 {
1042 if (bCopyFirstToFollow && HasFooter(/*bFirstPage=*/true))
1043 {
1044 m_aFollowPageStyle->setPropertyValue("FooterDynamicSpacing",
1046 m_aFollowPageStyle->setPropertyValue("FooterHeight",
1048 }
1049 }
1050
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) ) );
1054}
1055
1057 DomainMapper_Impl& rDM_Impl,
1058 const uno::Reference< text::XTextRange >& xStartingRange )
1059{
1061 if ( bIsFirstSection && rDM_Impl.GetBodyText().is() )
1062 {
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 );
1068 }
1069 else if ( xStartingRange.is() )
1070 xRangeProperties.set( xStartingRange, uno::UNO_QUERY_THROW );
1071 return xRangeProperties;
1072}
1073
1074void SectionPropertyMap::HandleMarginsHeaderFooter( bool bFirstPage, DomainMapper_Impl& rDM_Impl )
1075{
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));
1079
1080 if ( rDM_Impl.m_oBackgroundColor )
1082
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)
1086 {
1087 // Set footnote line width to zero, document has no footnote separator.
1089 }
1090 if ( rDM_Impl.m_bHasFtnSep )
1091 {
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
1094 try
1095 {
1096 uno::Reference<style::XStyleFamiliesSupplier> xStylesSupplier(rDM_Impl.GetTextDocument(), uno::UNO_QUERY);
1097 if ( xStylesSupplier.is() )
1098 {
1099 uno::Reference<container::XNameAccess> xStyleFamilies = xStylesSupplier->getStyleFamilies();
1101 if ( xStyleFamilies.is() )
1102 xStyleFamilies->getByName("ParagraphStyles") >>= xParagraphStyles;
1104 if ( xParagraphStyles.is() )
1105 xParagraphStyles->getByName("Standard") >>= xStandard;
1106 if ( xStandard.is() )
1107 {
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 );
1114 }
1115 }
1116 }
1117 catch ( const uno::Exception& ) {}
1118 }
1119
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
1122 */
1123 CopyLastHeaderFooter( bFirstPage, rDM_Impl );
1124 PrepareHeaderFooterProperties( bFirstPage );
1125
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);
1132}
1133
1134void SectionPropertyMap::InheritOrFinalizePageStyles( DomainMapper_Impl& rDM_Impl )
1135{
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
1144 {
1145 HandleMarginsHeaderFooter( /*bFirst=*/false, rDM_Impl );
1146 GetPageStyle( rDM_Impl, /*bFirst=*/false );
1147 if ( rDM_Impl.IsNewDoc() && m_aFollowPageStyle.is() )
1148 ApplyProperties_( m_aFollowPageStyle );
1149 }
1150
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
1155 {
1156 HandleMarginsHeaderFooter( /*bFirst=*/true, rDM_Impl );
1157 GetPageStyle( rDM_Impl, /*bFirst=*/true );
1158 if ( rDM_Impl.IsNewDoc() && m_aFirstPageStyle.is() )
1159 ApplyProperties_( m_aFirstPageStyle );
1160
1161 // Chain m_aFollowPageStyle to be after m_aFirstPageStyle
1162 m_aFirstPageStyle->setPropertyValue( "FollowStyle", uno::Any( m_sFollowPageStyleName ) );
1163 }
1164}
1165
1166void SectionPropertyMap::HandleIncreasedAnchoredObjectSpacing(DomainMapper_Impl& rDM_Impl)
1167{
1168 // Ignore Word 2010 and older.
1169 if (rDM_Impl.GetSettingsTable()->GetWordCompatibilityMode() < 15)
1170 return;
1171
1172 sal_Int32 nPageWidth = GetPageWidth();
1173 sal_Int32 nTextAreaWidth = nPageWidth - GetLeftMargin() - GetRightMargin();
1174
1175 std::vector<AnchoredObjectsInfo>& rAnchoredObjectAnchors = rDM_Impl.m_aAnchoredObjectAnchors;
1176 for (const auto& rAnchor : rAnchoredObjectAnchors)
1177 {
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;
1182
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)
1186 {
1187 // Ignore inline objects stored only for redlining.
1188 if (rAnchored.m_xRedlineForInline)
1189 continue;
1190
1191 uno::Reference<beans::XPropertySet> xShape(rAnchored.m_xAnchoredObject, uno::UNO_QUERY);
1192 if (!xShape.is())
1193 {
1194 continue;
1195 }
1196
1197 bool bOpaque = true;
1198 xShape->getPropertyValue("Opaque") >>= bOpaque;
1199 if (!bOpaque)
1200 {
1201 ++nOpaqueCount;
1202 }
1203 }
1204 if (nOpaqueCount < 1)
1205 {
1206 continue;
1207 }
1208
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)
1213 {
1214 uno::Reference<drawing::XShape> xShape(rAnchored.m_xAnchoredObject, uno::UNO_QUERY);
1215 if (!xShape.is())
1216 continue;
1217
1218 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
1219 if (!xPropertySet.is())
1220 continue;
1221
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;
1227
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;
1234 }
1235
1236 // Ignore cases when we have enough horizontal space for the shapes.
1237 if (nTextAreaWidth > nShapesWidth)
1238 continue;
1239
1240 sal_Int32 nHeight = 0;
1241 for (const auto& rAnchored : rAnchor.m_aAnchoredObjects)
1242 {
1243 uno::Reference<drawing::XShape> xShape(rAnchored.m_xAnchoredObject, uno::UNO_QUERY);
1244 if (!xShape.is())
1245 continue;
1246
1247 nHeight += xShape->getSize().Height;
1248 }
1249
1250 uno::Reference<beans::XPropertySet> xParagraph(rAnchor.m_xParagraph, uno::UNO_QUERY);
1251 if (xParagraph.is())
1252 {
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));
1259 }
1260 }
1261 rAnchoredObjectAnchors.clear();
1262}
1263
1264void 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)
1265{
1266 // convert redline ranges to cursor movement and character length
1267 for( size_t i = 0; i < rFramedRedlines.size(); i+=3)
1268 {
1271 rFramedRedlines[i] >>= xRange;
1273 if ( xRange.is() )
1274 {
1275 OUString sTableName;
1276 OUString sCellName;
1277 xRangeProperties.set( xRange, uno::UNO_QUERY_THROW );
1278 if (xRangeProperties->getPropertySetInfo()->hasPropertyByName("TextTable"))
1279 {
1280 uno::Any aTable = xRangeProperties->getPropertyValue("TextTable");
1281 if ( aTable != uno::Any() )
1282 {
1284 aTable >>= xTable;
1285 uno::Reference<beans::XPropertySet> xTableProperties(xTable, uno::UNO_QUERY);
1286 xTableProperties->getPropertyValue("TableName") >>= sTableName;
1287 }
1288 if (xRangeProperties->getPropertySetInfo()->hasPropertyByName("Cell"))
1289 {
1290 uno::Any aCell = xRangeProperties->getPropertyValue("Cell");
1291 if ( aCell != uno::Any() )
1292 {
1293 aCell >>= xCell;
1294 uno::Reference<beans::XPropertySet> xCellProperties(xCell, uno::UNO_QUERY);
1295 xCellProperties->getPropertyValue("CellName") >>= sCellName;
1296 }
1297 }
1298 }
1299 redTable.push_back(sTableName);
1300 redCell.push_back(sCellName);
1301 bool bOk = false;
1302 if (!sTableName.isEmpty() && !sCellName.isEmpty())
1303 {
1304 uno::Reference<text::XTextCursor> xRangeCursor = xCell->createTextCursorByRange( xRange );
1305 if ( xRangeCursor.is() )
1306 {
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);
1312 }
1313 }
1314 if (!bOk)
1315 {
1316 // missing cell or failed createTextCursorByRange()
1317 redLen.push_back(-1);
1318 redPos.push_back(-1);
1319 }
1320 }
1321 }
1322}
1323
1324void 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)
1325{
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)
1329 {
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 );
1343 try
1344 {
1345 xRedline->makeRedline( sType, aRedlineProperties );
1346 }
1347 catch (const uno::Exception&)
1348 {
1349 DBG_UNHANDLED_EXCEPTION("writerfilter", "makeRedline() failed");
1350 }
1351 }
1352}
1353
1354void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl )
1355{
1356 SectionPropertyMap* pPrevSection = rDM_Impl.GetLastSectionContext();
1357
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 )
1362 {
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()
1370 )
1371 {
1372 m_nBreakType = NS_ooxml::LN_Value_ST_SectionMark_nextPage;
1373 }
1374 }
1375 else if ( m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_continuous )
1376 {
1377 // if page orientation differs from previous section, it can't be treated as continuous
1378 if ( pPrevSection )
1379 {
1380 bool bIsLandscape = false;
1381 std::optional< PropertyMap::Property > pProp = getProperty( PROP_IS_LANDSCAPE );
1382 if ( pProp )
1383 pProp->second >>= bIsLandscape;
1384
1385 bool bPrevIsLandscape = false;
1386 pProp = pPrevSection->getProperty( PROP_IS_LANDSCAPE );
1387 if ( pProp )
1388 pProp->second >>= bPrevIsLandscape;
1389
1390 if ( bIsLandscape != bPrevIsLandscape )
1391 m_nBreakType = NS_ooxml::LN_Value_ST_SectionMark_nextPage;
1392 }
1393 }
1394
1395 try
1396 {
1397 HandleIncreasedAnchoredObjectSpacing(rDM_Impl);
1398 }
1399 catch (const uno::Exception&)
1400 {
1401 DBG_UNHANDLED_EXCEPTION("writerfilter", "HandleIncreasedAnchoredObjectSpacing() failed");
1402 }
1403
1404 if ( m_nLnnMod )
1405 {
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) )
1409 {
1410 //set the starting value at the beginning of the section
1411 try
1412 {
1414 if ( m_xStartingRange.is() )
1415 {
1416 xRangeProperties.set( m_xStartingRange, uno::UNO_QUERY_THROW );
1417 }
1418 else
1419 {
1420 //set the start value at the beginning of the document
1421 xRangeProperties.set( rDM_Impl.GetTextDocument()->getText()->getStart(), uno::UNO_QUERY_THROW );
1422 }
1423 // Writer is 1-based, Word is 0-based.
1424 xRangeProperties->setPropertyValue(
1426 uno::Any(m_nLnnMin + 1));
1427 }
1428 catch ( const uno::Exception& )
1429 {
1430 DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper", "Exception in SectionPropertyMap::CloseSectionGroup");
1431 }
1432 }
1433 }
1434
1435 if (m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_continuous
1436 && !rDM_Impl.IsInComments())
1437 {
1438 //todo: insert a section or access the already inserted section
1440 rDM_Impl.appendTextSectionAfter( m_xStartingRange );
1441 if ( xSection.is() )
1442 {
1443 if ( m_nColumnCount > 1 )
1444 ApplyColumnProperties( xSection, rDM_Impl );
1445
1446 ApplyProtectionProperties( xSection, rDM_Impl );
1447 }
1448
1449 try
1450 {
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() )
1456 {
1457 xRangeProperties->setPropertyValue( getPropertyName( PROP_PAGE_DESC_NAME ), uno::Any( aName ) );
1458 }
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();
1471 xTextAppend->createTextCursorByRange(
1472 uno::Reference<text::XTextContent>(xSection, uno::UNO_QUERY_THROW)->getAnchor()),
1473 uno::UNO_QUERY_THROW);
1475 xCursor->createEnumeration());
1476 bool isFound = false;
1477 while (xEnum->hasMoreElements())
1478 {
1480 xEnum->nextElement() >>= xElem;
1481 if (xElem->getPropertySetInfo()->hasPropertyByName("BreakType"))
1482 {
1484 if ((xElem->getPropertyValue("BreakType") >>= bt)
1485 && bt == style::BreakType_PAGE_BEFORE)
1486 {
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;
1492 }
1493 }
1494 }
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);
1503 if ((xPSCursor->getPropertyValue("BreakType") >>= bt)
1504 && bt == style::BreakType_PAGE_BEFORE)
1505 {
1506 xPSCursor->setPropertyValue(getPropertyName(PROP_PAGE_DESC_NAME),
1507 uno::Any(m_sFollowPageStyleName));
1508 isFound = true;
1509 }
1510 else
1511 {
1512 xPSCursor->getPropertyValue("CharHeight") >>= fCharHeight;
1513 }
1514 }
1515 if (!isFound && fCharHeight <= 1.0)
1516 {
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())
1524 {
1526 rDM_Impl.GetPageStyles()->getByName(aPageDescName), uno::UNO_QUERY);
1527 xPageStyle->setPropertyValue("FollowStyle",
1528 uno::Any(m_sFollowPageStyleName));
1529 }
1530 }
1531 }
1532 }
1533 catch ( const uno::Exception& )
1534 {
1535 SAL_WARN( "writerfilter", "failed to set PageDescName!" );
1536 }
1537 }
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())
1543 {
1544 try
1545 {
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.
1550 */
1552 if ( m_xStartingRange.is() )
1553 {
1554 xRangeProperties.set( m_xStartingRange, uno::UNO_QUERY_THROW );
1555 }
1556 else
1557 {
1558 //set the start value at the beginning of the document
1559 xRangeProperties.set( rDM_Impl.GetTextDocument()->getText()->getStart(), uno::UNO_QUERY_THROW );
1560 }
1561 xRangeProperties->setPropertyValue( getPropertyName( PROP_BREAK_TYPE ), uno::Any( style::BreakType_COLUMN_BEFORE ) );
1562 }
1563 catch ( const uno::Exception& ) {}
1564 }
1565 else if (!rDM_Impl.IsInComments())
1566 {
1568 ApplyProtectionProperties( xSection, rDM_Impl );
1569
1570 //get the properties and create appropriate page styles
1572 //This part certainly is not needed for footnotes, so don't create unused page styles.
1573 if ( !rDM_Impl.IsInFootOrEndnote() )
1574 {
1575 xFollowPageStyle.set( GetPageStyle( rDM_Impl, false ) );
1576
1577 HandleMarginsHeaderFooter(/*bFirstPage=*/false, rDM_Impl );
1578 }
1579
1580 if ( rDM_Impl.GetSettingsTable()->GetMirrorMarginSettings() )
1581 {
1582 Insert( PROP_PAGE_STYLE_LAYOUT, uno::Any( style::PageStyleLayout_MIRRORED ) );
1583 }
1585 if ( m_nColumnCount > 1 )
1586 {
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 );
1594 }
1595
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();
1599
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;
1605
1606 sal_Int32 nWidth = 1;
1607 pProp = getProperty( PROP_WIDTH );
1608 if ( pProp )
1609 pProp->second >>= nWidth;
1610
1611 sal_Int16 nWritingMode = text::WritingMode2::LR_TB;
1612 pProp = getProperty( PROP_WRITING_MODE );
1613 if ( pProp )
1614 pProp->second >>= nWritingMode;
1615
1616 sal_Int32 nTextAreaHeight = nWritingMode == text::WritingMode2::LR_TB ?
1617 nHeight - m_nTopMargin - m_nBottomMargin :
1618 nWidth - m_nLeftMargin - m_nRightMargin;
1619
1620 sal_Int32 nGridLinePitch = m_nGridLinePitch;
1621 //sep.dyaLinePitch
1622 if ( nGridLinePitch < 1 || nGridLinePitch > 31680 )
1623 {
1624 SAL_WARN( "writerfilter", "sep.dyaLinePitch outside legal range: " << nGridLinePitch );
1625 nGridLinePitch = 1;
1626 }
1627
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;
1634
1635 // PROP_GRID_MODE
1636 if ( nGridType == text::TextGridMode::LINES_AND_CHARS )
1637 {
1638 if (!m_nDxtCharSpace)
1639 nGridType = text::TextGridMode::LINES;
1640 else
1641 Insert( PROP_GRID_SNAP_TO_CHARS, uno::Any( m_bGridSnapToChars ) );
1642 }
1643
1644 Insert( PROP_GRID_MODE, uno::Any( nGridType ) );
1645
1646 sal_Int32 nCharWidth = 423; //240 twip/ 12 pt
1647 const StyleSheetEntryPtr pEntry = rDM_Impl.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( u"Standard" );
1648 if ( pEntry )
1649 {
1650 std::optional< PropertyMap::Property > pPropHeight = pEntry->m_pProperties->getProperty( PROP_CHAR_HEIGHT_ASIAN );
1651 if ( pPropHeight )
1652 {
1653 double fHeight = 0;
1654 if ( pPropHeight->second >>= fHeight )
1655 nCharWidth = ConversionHelper::convertTwipToMM100( static_cast<tools::Long>(fHeight * 20.0 + 0.5) );
1656 }
1657 }
1658
1659 //dxtCharSpace
1660 if ( m_nDxtCharSpace )
1661 {
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 );
1667
1668 sal_Int32 nFraction = (nCharSpace & 0x00000FFF);
1669 nFraction = (nFraction * 20) / 0xFFF;
1670 nCharWidth += ConversionHelper::convertTwipToMM100( nFraction );
1671 }
1672
1673 if ( m_nPageNumberType >= 0 )
1674 Insert( PROP_NUMBERING_TYPE, uno::Any( m_nPageNumberType ) );
1675
1676 // #i119558#, force to set document as standard page mode,
1677 // refer to ww8 import process function "SwWW8ImplReader::SetDocumentGrid"
1678 try
1679 {
1681 xDocProperties.set( rDM_Impl.GetTextDocument(), uno::UNO_QUERY_THROW );
1683 xDocProperties->setPropertyValue("DefaultPageMode", uno::Any(false));
1684 }
1685 catch ( const uno::Exception& )
1686 {
1687 DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper", "Exception in SectionPropertyMap::CloseSectionGroup");
1688 }
1689
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 ) ) );
1693
1694 if ( rDM_Impl.IsNewDoc() && xFollowPageStyle.is() )
1695 ApplyProperties_( xFollowPageStyle );
1696
1697 //todo: creating a "First Page" style depends on HasTitlePage and _fFacingPage_
1698 if ( m_bTitlePage )
1699 {
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 );
1706
1707 if ( xColumns.is() )
1708 xFirstPageStyle->setPropertyValue(
1709 getPropertyName( PROP_TEXT_COLUMNS ), uno::Any( xColumns ) );
1710 }
1711
1712 ApplyBorderToPageStyles( rDM_Impl, m_eBorderApply, m_eBorderOffsetFrom );
1713
1714 try
1715 {
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 ) );
1718
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 )
1723 {
1724 OUString* pageStyle = m_bTitlePage ? &m_sFirstPageStyleName : &m_sFollowPageStyleName;
1725 OUString evenOddStyleName = rDM_Impl.GetUnusedPageStyleName();
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 )
1737 {
1738 if ( (rProperty.Attributes & beans::PropertyAttribute::READONLY) == 0 )
1739 {
1740 if (aDenylist.find(rProperty.Name) == aDenylist.end())
1741 evenOddStyle->setPropertyValue(
1742 rProperty.Name,
1743 pageProperties->getPropertyValue(rProperty.Name));
1744 }
1745 }
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 ) );
1756 }
1757
1758 if (rDM_Impl.m_xAltChunkStartingRange.is())
1759 {
1760 xRangeProperties.set(rDM_Impl.m_xAltChunkStartingRange, uno::UNO_QUERY);
1761 }
1762 if (xRangeProperties.is() && (rDM_Impl.IsNewDoc() || rDM_Impl.IsAltChunk()))
1763 {
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)
1771 )
1772 {
1773 xRangeProperties->setPropertyValue(
1775 uno::Any(sPageStyle) );
1776 }
1777
1778 if (0 <= m_nPageNumber)
1779 {
1780 sal_Int16 nPageNumber = static_cast< sal_Int16 >(m_nPageNumber);
1781 xRangeProperties->setPropertyValue(getPropertyName(PROP_PAGE_NUMBER_OFFSET),
1782 uno::Any(nPageNumber));
1783 }
1784 }
1785 }
1786 catch ( const uno::Exception& )
1787 {
1788 TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::CloseSectionGroup" );
1789 }
1790 }
1791
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 )
1795 {
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;
1801 }
1802 if ( nParagraphWidth > 0 )
1803 {
1804 const OUString & sPropRelativeWidth = getPropertyName(PROP_RELATIVE_WIDTH);
1805 for ( const auto& xShape : m_xRelativeWidthShapes )
1806 {
1807 const uno::Reference<beans::XPropertySet> xShapePropertySet( xShape, uno::UNO_QUERY );
1808 if ( xShapePropertySet->getPropertySetInfo()->hasPropertyByName(sPropRelativeWidth) )
1809 {
1810 sal_uInt16 nPercent = 0;
1811 try
1812 {
1813 xShapePropertySet->getPropertyValue(sPropRelativeWidth) >>= nPercent;
1814 }
1815 catch (const css::uno::RuntimeException& e)
1816 {
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);
1820 }
1821 if ( nPercent )
1822 {
1823 const sal_Int32 nWidth = nParagraphWidth * nPercent / 100;
1824 xShape->setSize( awt::Size( nWidth, xShape->getSize().Height ) );
1825 }
1826 }
1827 }
1828 }
1829 m_xRelativeWidthShapes.clear();
1830
1831 rDM_Impl.SetIsLastSectionGroup( false );
1832 rDM_Impl.SetIsFirstParagraphInSection( true );
1833
1834 if ( !rDM_Impl.IsInFootOrEndnote() && !rDM_Impl.IsInComments() )
1835 {
1836 rDM_Impl.m_bHasFtn = false;
1837 rDM_Impl.m_bHasFtnSep = false;
1838 }
1839}
1840
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.
1844void SectionPropertyMap::ClearHeaderFooterLinkToPrevious( bool bHeader, PageType eType )
1845{
1846 if ( bHeader )
1847 {
1848 switch ( eType )
1849 {
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
1854 }
1855 }
1856 else
1857 {
1858 switch ( eType )
1859 {
1860 case PAGE_FIRST: m_bFirstPageFooterLinkToPrevious = false; break;
1861 case PAGE_LEFT: m_bEvenPageFooterLinkToPrevious = false; break;
1862 case PAGE_RIGHT: m_bDefaultFooterLinkToPrevious = false; break;
1863 }
1864 }
1865}
1866
1867namespace {
1868
1869class NamedPropertyValue
1870{
1871private:
1872 OUString m_aName;
1873
1874public:
1875 explicit NamedPropertyValue( OUString i_aStr )
1876 : m_aName(std::move( i_aStr ))
1877 {
1878 }
1879
1880 bool operator() ( beans::PropertyValue const & aVal )
1881 {
1882 return aVal.Name == m_aName;
1883 }
1884};
1885
1886}
1887
1888void SectionPropertyMap::ApplyProperties_( const uno::Reference< beans::XPropertySet >& xStyle )
1889{
1890 uno::Reference< beans::XMultiPropertySet > const xMultiSet( xStyle, uno::UNO_QUERY );
1891
1892 std::vector< OUString > vNames;
1893 std::vector< uno::Any > vValues;
1894 {
1895 // Convert GetPropertyValues() value into something useful
1896 const uno::Sequence< beans::PropertyValue > vPropVals = GetPropertyValues();
1897
1898 //Temporarily store the items that are in grab bags
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;
1907
1908 for ( const beans::PropertyValue* pIter = vPropVals.begin(); pIter != vPropVals.end(); ++pIter )
1909 {
1910 if ( pIter != pCharGrabBag && pIter != pParaGrabBag
1911 && pIter->Name != "IsProtected" //section-only property
1912 )
1913 {
1914 vNames.push_back( pIter->Name );
1915 vValues.push_back( pIter->Value );
1916 }
1917 }
1918 for ( const beans::PropertyValue & v : std::as_const(vCharVals) )
1919 {
1920 vNames.push_back( v.Name );
1921 vValues.push_back( v.Value );
1922 }
1923 for ( const beans::PropertyValue & v : std::as_const(vParaVals) )
1924 {
1925 vNames.push_back( v.Name );
1926 vValues.push_back( v.Value );
1927 }
1928 }
1929 if ( xMultiSet.is() )
1930 {
1931 try
1932 {
1933 xMultiSet->setPropertyValues( comphelper::containerToSequence( vNames ), comphelper::containerToSequence( vValues ) );
1934 return;
1935 }
1936 catch ( const uno::Exception& )
1937 {
1938 TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::ApplyProperties_" );
1939 }
1940 }
1941 for ( size_t i = 0; i < vNames.size(); ++i )
1942 {
1943 try
1944 {
1945 if ( xStyle.is() )
1946 xStyle->setPropertyValue( vNames[i], vValues[i] );
1947 }
1948 catch ( const uno::Exception& )
1949 {
1950 TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::ApplyProperties_" );
1951 }
1952 }
1953}
1954
1955sal_Int32 SectionPropertyMap::GetPageWidth() const
1956{
1957 return getProperty( PROP_WIDTH )->second.get<sal_Int32>();
1958}
1959
1960StyleSheetPropertyMap::StyleSheetPropertyMap()
1961 : mnListLevel( -1 )
1962 , mnOutlineLevel( -1 )
1963{
1964}
1965
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 )
1985{
1986}
1987
1989{
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;
2008}
2009
2011{
2012 if ( eWhich < TablePropertyMapTarget_MAX )
2013 {
2014 if ( m_aValidValues[eWhich].bValid )
2015 nFill = m_aValidValues[eWhich].nValue;
2016 return m_aValidValues[eWhich].bValid;
2017 }
2018 else
2019 {
2020 OSL_FAIL( "invalid TablePropertyMapTarget" );
2021 return false;
2022 }
2023}
2024
2026{
2027 if ( eWhich < TablePropertyMapTarget_MAX )
2028 {
2029 m_aValidValues[eWhich].bValid = true;
2030 m_aValidValues[eWhich].nValue = nSet;
2031 }
2032 else
2033 OSL_FAIL( "invalid TablePropertyMapTarget" );
2034}
2035
2036void TablePropertyMap::insertTableProperties( const PropertyMap* pMap, const bool bOverwrite )
2037{
2038#ifdef DBG_UTIL
2039 TagLogger::getInstance().startElement( "TablePropertyMap.insertTableProperties" );
2040 pMap->dumpXml();
2041#endif
2042
2043 const TablePropertyMap* pSource = dynamic_cast< const TablePropertyMap* >(pMap);
2044 if ( pSource )
2045 {
2046 for ( sal_Int32 eTarget = TablePropertyMapTarget_START;
2047 eTarget < TablePropertyMapTarget_MAX; ++eTarget )
2048 {
2049 if ( pSource->m_aValidValues[eTarget].bValid && (bOverwrite || !m_aValidValues[eTarget].bValid) )
2050 {
2051 m_aValidValues[eTarget].bValid = true;
2052 m_aValidValues[eTarget].nValue = pSource->m_aValidValues[eTarget].nValue;
2053 }
2054 }
2055 }
2056
2057#ifdef DBG_UTIL
2058 dumpXml();
2060#endif
2061}
2062
2063} // namespace writerfilter
2064
2065/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::chart::ChartAxisLabelPosition ePos
OptionalString sType
sal_Int32 nLineWidth
#define MIN_HEAD_FOOT_HEIGHT
OUString m_aName
std::vector< beans::PropertyValue > m_aValues
PropertiesInfo aProperties
tools::Long getWidth() const
tools::Long getHeight() const
const_iterator find(const Value &x) const
const_iterator end() const
css::uno::Any getProperty(sal_Int32 nPropId)
static const OUString & getPropertyName(sal_Int32 nPropId)
T * get() const
static TagLogger & getInstance()
Definition: TagLogger.cxx:95
void startElement(const std::string &name)
Definition: TagLogger.cxx:140
css::uno::Reference< css::text::XTextAppend > GetCurrentXText()
void SetLineNumbering(sal_Int32 nLnnMod, sal_uInt32 nLnc, sal_Int32 ndxaLnn)
const css::uno::Reference< css::text::XTextDocument > & GetTextDocument() const
const css::uno::Reference< css::lang::XMultiServiceFactory > & GetTextFactory() const
bool m_bHasFtnSep
If the current section has a footnote separator.
std::optional< sal_Int32 > m_oBackgroundColor
Document background color, applied to every page style.
css::uno::Reference< css::beans::XPropertySet > appendTextSectionAfter(css::uno::Reference< css::text::XTextRange > const &xBefore)
bool IsNewDoc() const
If we're importing into a new document, or just pasting to an existing one.
css::uno::Reference< css::text::XText > const & GetBodyText()
std::vector< AnchoredObjectsInfo > m_aAnchoredObjectAnchors
Paragraphs with anchored objects in the current section.
css::uno::Reference< css::container::XNameContainer > const & GetPageStyles()
bool m_bHasFtn
If the current section has footnotes.
StyleSheetTablePtr const & GetStyleSheetTable()
css::uno::Reference< css::text::XTextRange > m_xAltChunkStartingRange
bool IsReadGlossaries() const
If we're importing autotext.
const css::uno::Any & getValue() const
css::uno::Reference< css::beans::XPropertySet > m_aFollowPageStyle
void ApplyBorderToPageStyles(DomainMapper_Impl &rDM_Impl, BorderApply eBorderApply, BorderOffsetFrom eOffsetFrom)
static void SetBorderDistance(const css::uno::Reference< css::beans::XPropertySet > &xStyle, PropertyIds eMarginId, PropertyIds eDistId, sal_Int32 nDistance, BorderOffsetFrom eOffsetFrom, sal_uInt32 nLineWidth, DomainMapper_Impl &rDM_Impl)
void SetBorder(BorderPosition ePos, sal_Int32 nLineDistance, const css::table::BorderLine2 &rBorderLine, bool bShadow)
css::uno::Reference< css::beans::XPropertySet > m_aFirstPageStyle
const OUString & GetPageStyleName(bool bFirstPage=false)
css::uno::Reference< css::beans::XPropertySet > GetPageStyle(DomainMapper_Impl &rDM_Impl, bool bFirst)
std::optional< css::table::BorderLine2 > m_oBorderLines[4]
virtual void insertTableProperties(const PropertyMap *, const bool bOverwrite=true) override
bool getValue(TablePropertyMapTarget eWhich, sal_Int32 &nFill)
void setValue(TablePropertyMapTarget eWhich, sal_Int32 nSet)
ValidValue m_aValidValues[TablePropertyMapTarget_MAX]
constexpr ::Color COL_BLACK(0x00, 0x00, 0x00)
#define TOOLS_WARN_EXCEPTION(area, stream)
#define TOOLS_INFO_EXCEPTION(area, stream)
#define DBG_UNHANDLED_EXCEPTION(...)
virtual void Insert(SotClipboardFormatId nFormat, const OUString &rFormatName) override
float v
float u
DocumentType eType
OUString sName
OUString aName
Sequence< sal_Int8 > aSeq
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
aStr
def text(shape, orig_st)
tools::Long const nRightMargin
tools::Long const nBottomMargin
tools::Long const nTopMargin
tools::Long const nBorder
tools::Long const nLeftMargin
PROP_WRITING_MODE
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
void BorderDistanceFromWord(bool bFromEdge, sal_Int32 &nMargin, sal_Int32 &nBorderDistance, sal_Int32 nBorderWidth)
convert
int i
Length
constexpr Point convert(const Point &rPoint, o3tl::Length eFrom, o3tl::Length eTo)
SVX_DLLPUBLIC OUString getProperty(css::uno::Reference< css::beans::XPropertyContainer > const &rxPropertyContainer, OUString const &rName)
long Long
OUString getPropertyName(PropertyIds eId)
void lcl_DumpTableColumnSeparators(const uno::Any &rTableColumnSeparators)
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)
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)
static void lcl_AnyToTag(const uno::Any &rAny)
static uno::Reference< beans::XPropertySet > lcl_GetRangeProperties(bool bIsFirstSection, DomainMapper_Impl &rDM_Impl, const uno::Reference< text::XTextRange > &xStartingRange)
PAPER_LETTER
PAGE_FIRST
#define SAL_MAX_INT16
signed char sal_Int8