lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / writerfilter / source / dmapper / NumberingManager.cxx
blob2d5605a271dc2dc2a51ebba36bf1219e6c81501f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include "ConversionHelper.hxx"
23 #include "NumberingManager.hxx"
24 #include "StyleSheetTable.hxx"
25 #include "PropertyIds.hxx"
27 #include <ooxml/resourceids.hxx>
29 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
30 #include <com/sun/star/container/XNameContainer.hpp>
31 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
32 #include <com/sun/star/style/NumberingType.hpp>
33 #include <com/sun/star/text/HoriOrientation.hpp>
34 #include <com/sun/star/text/PositionAndSpaceMode.hpp>
35 #include <com/sun/star/text/XChapterNumberingSupplier.hpp>
36 #include <com/sun/star/graphic/XGraphic.hpp>
37 #include <com/sun/star/awt/XBitmap.hpp>
39 #include <osl/diagnose.h>
40 #include <rtl/ustring.hxx>
41 #include <sal/log.hxx>
42 #include <comphelper/sequence.hxx>
43 #include <comphelper/propertyvalue.hxx>
45 using namespace com::sun::star;
47 namespace writerfilter {
48 namespace dmapper {
50 //--------------------------------------------------- Utility functions
51 template <typename T>
52 static beans::PropertyValue lcl_makePropVal(PropertyIds nNameID, T const & aValue)
54 return {getPropertyName(nNameID), 0, uno::makeAny(aValue), beans::PropertyState_DIRECT_VALUE};
57 static sal_Int32 lcl_findProperty( const uno::Sequence< beans::PropertyValue >& aProps, const OUString& sName )
59 sal_Int32 i = 0;
60 sal_Int32 nLen = aProps.getLength( );
61 sal_Int32 nPos = -1;
63 while ( nPos == -1 && i < nLen )
65 if ( aProps[i].Name == sName )
66 nPos = i;
67 else
68 i++;
71 return nPos;
74 static void lcl_mergeProperties( uno::Sequence< beans::PropertyValue >& aSrc,
75 uno::Sequence< beans::PropertyValue >& aDst )
77 for ( sal_Int32 i = 0, nSrcLen = aSrc.getLength( ); i < nSrcLen; i++ )
79 // Look for the same property in aDst
80 sal_Int32 nPos = lcl_findProperty( aDst, aSrc[i].Name );
81 if ( nPos >= 0 )
83 // Replace the property value by the one in aSrc
84 aDst[nPos] = aSrc[i];
86 else
88 // Simply add the new value
89 aDst.realloc( aDst.getLength( ) + 1 );
90 aDst[ aDst.getLength( ) - 1 ] = aSrc[i];
95 //-------------------------------------------- ListLevel implementation
96 void ListLevel::SetValue( Id nId, sal_Int32 nValue )
98 switch( nId )
100 case NS_ooxml::LN_CT_Lvl_start:
101 m_nIStartAt = nValue;
102 break;
103 case NS_ooxml::LN_CT_Lvl_numFmt:
104 m_nNFC = nValue;
105 break;
106 case NS_ooxml::LN_CT_Lvl_isLgl:
107 break;
108 case NS_ooxml::LN_CT_Lvl_legacy:
109 break;
110 case NS_ooxml::LN_CT_Lvl_suff:
111 m_nXChFollow = nValue;
112 break;
113 case NS_ooxml::LN_CT_TabStop_pos:
114 if (nValue < 0)
116 SAL_INFO("writerfilter",
117 "unsupported list tab stop position " << nValue);
119 else
120 m_nTabstop = nValue;
121 break;
122 default:
123 OSL_FAIL( "this line should never be reached");
125 m_bHasValues = true;
128 bool ListLevel::HasValues() const
130 return m_bHasValues;
133 void ListLevel::SetParaStyle( const tools::SvRef< StyleSheetEntry >& pStyle )
135 if (!pStyle)
136 return;
137 m_pParaStyle = pStyle;
138 // AFAICT .docx spec does not identify which numberings or paragraph
139 // styles are actually the ones to be used for outlines (chapter numbering),
140 // it only kind of says somewhere that they should be named Heading1 to Heading9.
141 const OUString styleId= pStyle->sConvertedStyleName;
142 m_outline = ( styleId.getLength() == RTL_CONSTASCII_LENGTH( "Heading 1" )
143 && styleId.match( "Heading ", 0 )
144 && styleId[ RTL_CONSTASCII_LENGTH( "Heading " ) ] >= '1'
145 && styleId[ RTL_CONSTASCII_LENGTH( "Heading " ) ] <= '9' );
148 sal_Int16 ListLevel::GetParentNumbering( const OUString& sText, sal_Int16 nLevel,
149 OUString& rPrefix, OUString& rSuffix )
151 sal_Int16 nParentNumbering = 1;
153 //now parse the text to find %n from %1 to %nLevel+1
154 //everything before the first % and the last %x is prefix and suffix
155 OUString sLevelText( sText );
156 sal_Int32 nCurrentIndex = 0;
157 sal_Int32 nFound = sLevelText.indexOf( '%', nCurrentIndex );
158 if( nFound > 0 )
160 rPrefix = sLevelText.copy( 0, nFound );
161 sLevelText = sLevelText.copy( nFound );
163 sal_Int32 nMinLevel = nLevel;
164 //now the text should either be empty or start with %
165 nFound = sLevelText.getLength( ) > 1 ? 0 : -1;
166 while( nFound >= 0 )
168 if( sLevelText.getLength() > 1 )
170 sal_Unicode cLevel = sLevelText[1];
171 if( cLevel >= '1' && cLevel <= '9' )
173 if( cLevel - '1' < nMinLevel )
174 nMinLevel = cLevel - '1';
175 //remove first char - next char is removed later
176 sLevelText = sLevelText.copy( 1 );
179 //remove old '%' or number
180 sLevelText = sLevelText.copy( 1 );
181 nCurrentIndex = 0;
182 nFound = sLevelText.indexOf( '%', nCurrentIndex );
183 //remove the text before the next %
184 if(nFound > 0)
185 sLevelText = sLevelText.copy( nFound -1 );
187 if( nMinLevel < nLevel )
189 nParentNumbering = sal_Int16( nLevel - nMinLevel + 1);
192 rSuffix = sLevelText;
194 return nParentNumbering;
197 uno::Sequence<beans::PropertyValue> ListLevel::GetProperties(bool bDefaults)
199 uno::Sequence<beans::PropertyValue> aLevelProps = GetLevelProperties(bDefaults);
200 if (m_pParaStyle)
201 AddParaProperties( &aLevelProps );
202 return aLevelProps;
205 static bool IgnoreForCharStyle(const OUString& aStr)
207 //Names found in PropertyIds.cxx, Lines 56-396
208 return (aStr=="Adjust" || aStr=="IndentAt" || aStr=="FirstLineIndent"
209 || aStr=="FirstLineOffset" || aStr=="LeftMargin" || aStr=="CharFontName"
212 uno::Sequence< beans::PropertyValue > ListLevel::GetCharStyleProperties( )
214 PropertyValueVector_t rProperties;
216 uno::Sequence< beans::PropertyValue > vPropVals = PropertyMap::GetPropertyValues();
217 beans::PropertyValue* aValIter = vPropVals.begin();
218 beans::PropertyValue* aEndIter = vPropVals.end();
219 for( ; aValIter != aEndIter; ++aValIter )
221 if (IgnoreForCharStyle(aValIter->Name))
222 continue;
223 else if ( aValIter->Name != "CharInteropGrabBag" && aValIter->Name != "ParaInteropGrabBag" )
224 rProperties.emplace_back(aValIter->Name, 0, aValIter->Value, beans::PropertyState_DIRECT_VALUE);
227 return comphelper::containerToSequence(rProperties);
230 uno::Sequence<beans::PropertyValue> ListLevel::GetLevelProperties(bool bDefaults)
232 std::vector<beans::PropertyValue> aNumberingProperties;
234 if( m_nIStartAt >= 0)
235 aNumberingProperties.push_back(lcl_makePropVal<sal_Int16>(PROP_START_WITH, m_nIStartAt) );
237 sal_Int16 nNumberFormat = ConversionHelper::ConvertNumberingType(m_nNFC);
238 if( m_nNFC >= 0)
240 if (m_xGraphicBitmap.is())
241 nNumberFormat = style::NumberingType::BITMAP;
242 else if (m_sBulletChar.isEmpty() && nNumberFormat != style::NumberingType::CHAR_SPECIAL)
243 // w:lvlText is empty, that means no numbering in Word.
244 // CHAR_SPECIAL is handled separately below.
245 nNumberFormat = style::NumberingType::NUMBER_NONE;
246 aNumberingProperties.push_back(lcl_makePropVal(PROP_NUMBERING_TYPE, nNumberFormat));
250 if( !isOutlineNumbering())
252 // todo: this is not the bullet char
253 if( nNumberFormat == style::NumberingType::CHAR_SPECIAL )
255 if (!m_sBulletChar.isEmpty())
257 aNumberingProperties.push_back(lcl_makePropVal(PROP_BULLET_CHAR, m_sBulletChar.copy(0, 1)));
259 else
261 // If w:lvlText's value is null - set bullet char to zero.
262 aNumberingProperties.push_back(lcl_makePropVal<sal_Unicode>(PROP_BULLET_CHAR, 0));
265 if (m_xGraphicBitmap.is())
267 aNumberingProperties.push_back(lcl_makePropVal(PROP_GRAPHIC_BITMAP, m_xGraphicBitmap));
268 aNumberingProperties.push_back(lcl_makePropVal(PROP_GRAPHIC_SIZE, m_aGraphicSize));
272 if (bDefaults || m_nTabstop != 0)
273 aNumberingProperties.push_back(lcl_makePropVal(PROP_LISTTAB_STOP_POSITION, m_nTabstop));
275 //TODO: handling of nFLegal?
276 //TODO: nFNoRestart lower levels do not restart when higher levels are incremented, like:
277 //1.
278 //1.1
279 //2.2
280 //2.3
281 //3.4
283 // TODO: sRGBXchNums; array of inherited numbers
285 // nXChFollow; following character 0 - tab, 1 - space, 2 - nothing
286 if (bDefaults || m_nXChFollow != SvxNumberFormat::LISTTAB)
287 aNumberingProperties.push_back(lcl_makePropVal(PROP_LEVEL_FOLLOW, m_nXChFollow));
289 PropertyIds const aReadIds[] =
291 PROP_ADJUST, PROP_INDENT_AT, PROP_FIRST_LINE_INDENT,
292 PROP_FIRST_LINE_OFFSET, PROP_LEFT_MARGIN
294 for(PropertyIds const & rReadId : aReadIds) {
295 boost::optional<PropertyMap::Property> aProp = getProperty(rReadId);
296 if (aProp)
297 aNumberingProperties.emplace_back( getPropertyName(aProp->first), 0, aProp->second, beans::PropertyState_DIRECT_VALUE );
298 else if (rReadId == PROP_FIRST_LINE_INDENT && bDefaults)
299 // Writer default is -360 twips, Word default seems to be 0.
300 aNumberingProperties.emplace_back("FirstLineIndent", 0, uno::makeAny(static_cast<sal_Int32>(0)), beans::PropertyState_DIRECT_VALUE);
301 else if (rReadId == PROP_INDENT_AT && bDefaults)
302 // Writer default is 720 twips, Word default seems to be 0.
303 aNumberingProperties.emplace_back("IndentAt", 0,
304 uno::makeAny(static_cast<sal_Int32>(0)),
305 beans::PropertyState_DIRECT_VALUE);
308 boost::optional<PropertyMap::Property> aPropFont = getProperty(PROP_CHAR_FONT_NAME);
309 if(aPropFont && !isOutlineNumbering())
310 aNumberingProperties.emplace_back( getPropertyName(PROP_BULLET_FONT_NAME), 0, aPropFont->second, beans::PropertyState_DIRECT_VALUE );
312 return comphelper::containerToSequence(aNumberingProperties);
315 // Add the properties only if they do not already exist in the sequence.
316 void ListLevel::AddParaProperties( uno::Sequence< beans::PropertyValue >* props )
318 uno::Sequence< beans::PropertyValue >& aProps = *props;
320 OUString sFirstLineIndent = getPropertyName(
321 PROP_FIRST_LINE_INDENT );
322 OUString sIndentAt = getPropertyName(
323 PROP_INDENT_AT );
325 bool hasFirstLineIndent = lcl_findProperty( aProps, sFirstLineIndent );
326 bool hasIndentAt = lcl_findProperty( aProps, sIndentAt );
328 if( hasFirstLineIndent && hasIndentAt )
329 return; // has them all, nothing to add
331 uno::Sequence< beans::PropertyValue > aParaProps = m_pParaStyle->pProperties->GetPropertyValues( );
333 // ParaFirstLineIndent -> FirstLineIndent
334 // ParaLeftMargin -> IndentAt
336 OUString sParaIndent = getPropertyName(
337 PROP_PARA_FIRST_LINE_INDENT );
338 OUString sParaLeftMargin = getPropertyName(
339 PROP_PARA_LEFT_MARGIN );
341 sal_Int32 nLen = aParaProps.getLength( );
342 for ( sal_Int32 i = 0; i < nLen; i++ )
344 if ( !hasFirstLineIndent && aParaProps[i].Name == sParaIndent )
346 aProps.realloc( aProps.getLength() + 1 );
347 aProps[aProps.getLength( ) - 1] = aParaProps[i];
348 aProps[aProps.getLength( ) - 1].Name = sFirstLineIndent;
350 else if ( !hasIndentAt && aParaProps[i].Name == sParaLeftMargin )
352 aProps.realloc( aProps.getLength() + 1 );
353 aProps[aProps.getLength( ) - 1] = aParaProps[i];
354 aProps[aProps.getLength( ) - 1].Name = sIndentAt;
360 NumPicBullet::NumPicBullet()
361 : m_nId(0)
365 NumPicBullet::~NumPicBullet()
369 void NumPicBullet::SetId(sal_Int32 nId)
371 m_nId = nId;
374 void NumPicBullet::SetShape(uno::Reference<drawing::XShape> const& xShape)
376 m_xShape = xShape;
380 //--------------------------------------- AbstractListDef implementation
382 AbstractListDef::AbstractListDef( ) :
383 m_nId( -1 )
387 AbstractListDef::~AbstractListDef( )
391 void AbstractListDef::SetValue( sal_uInt32 nSprmId )
393 switch( nSprmId )
395 case NS_ooxml::LN_CT_AbstractNum_tmpl:
396 break;
397 default:
398 OSL_FAIL( "this line should never be reached");
402 ListLevel::Pointer AbstractListDef::GetLevel( sal_uInt16 nLvl )
404 ListLevel::Pointer pLevel;
405 if ( m_aLevels.size( ) > nLvl )
406 pLevel = m_aLevels[ nLvl ];
407 return pLevel;
410 void AbstractListDef::AddLevel( )
412 ListLevel::Pointer pLevel( new ListLevel );
413 m_pCurrentLevel = pLevel;
414 m_aLevels.push_back( pLevel );
417 uno::Sequence<uno::Sequence<beans::PropertyValue>> AbstractListDef::GetPropertyValues(bool bDefaults)
419 uno::Sequence< uno::Sequence< beans::PropertyValue > > result( sal_Int32( m_aLevels.size( ) ) );
420 uno::Sequence< beans::PropertyValue >* aResult = result.getArray( );
422 int nLevels = m_aLevels.size( );
423 for ( int i = 0; i < nLevels; i++ )
425 aResult[i] = m_aLevels[i]->GetProperties(bDefaults);
428 return result;
431 //---------------------------------------------- ListDef implementation
433 ListDef::ListDef( ) : AbstractListDef( )
437 ListDef::~ListDef( )
441 OUString ListDef::GetStyleName( sal_Int32 nId )
443 OUString sStyleName( "WWNum" );
444 sStyleName += OUString::number( nId );
446 return sStyleName;
449 uno::Sequence<uno::Sequence<beans::PropertyValue>> ListDef::GetMergedPropertyValues()
451 if (!m_pAbstractDef)
452 return uno::Sequence< uno::Sequence< beans::PropertyValue > >();
454 // [1] Call the same method on the abstract list
455 uno::Sequence<uno::Sequence<beans::PropertyValue>> aAbstract
456 = m_pAbstractDef->GetPropertyValues(/*bDefaults=*/true);
458 // [2] Call the upper class method
459 uno::Sequence<uno::Sequence<beans::PropertyValue>> aThis
460 = AbstractListDef::GetPropertyValues(/*bDefaults=*/false);
462 // Merge the results of [2] in [1]
463 sal_Int32 nThisCount = aThis.getLength( );
464 sal_Int32 nAbstractCount = aAbstract.getLength( );
465 for ( sal_Int32 i = 0; i < nThisCount && i < nAbstractCount; i++ )
467 uno::Sequence< beans::PropertyValue > level = aThis[i];
468 if (level.hasElements() && GetLevel(i)->HasValues())
470 // If the element contains something, merge it, but ignore stub overrides.
471 lcl_mergeProperties( level, aAbstract[i] );
475 return aAbstract;
478 static uno::Reference< container::XNameContainer > lcl_getUnoNumberingStyles(
479 uno::Reference<lang::XMultiServiceFactory> const& xFactory)
481 uno::Reference< container::XNameContainer > xStyles;
485 uno::Reference< style::XStyleFamiliesSupplier > xFamilies( xFactory, uno::UNO_QUERY_THROW );
486 uno::Any oFamily = xFamilies->getStyleFamilies( )->getByName("NumberingStyles");
488 oFamily >>= xStyles;
490 catch ( const uno::Exception & )
494 return xStyles;
497 void ListDef::CreateNumberingRules( DomainMapper& rDMapper,
498 uno::Reference<lang::XMultiServiceFactory> const& xFactory)
500 // Get the UNO Numbering styles
501 uno::Reference< container::XNameContainer > xStyles = lcl_getUnoNumberingStyles( xFactory );
503 // Do the whole thing
504 if( !m_xNumRules.is() && xFactory.is() && xStyles.is( ) )
508 // Create the numbering style
509 uno::Reference< beans::XPropertySet > xStyle (
510 xFactory->createInstance("com.sun.star.style.NumberingStyle"),
511 uno::UNO_QUERY_THROW );
513 OUString sStyleName = GetStyleName( GetId( ) );
515 xStyles->insertByName( sStyleName, makeAny( xStyle ) );
517 uno::Any oStyle = xStyles->getByName( sStyleName );
518 xStyle.set( oStyle, uno::UNO_QUERY_THROW );
520 // Get the default OOo Numbering style rules
521 uno::Any aRules = xStyle->getPropertyValue( getPropertyName( PROP_NUMBERING_RULES ) );
522 aRules >>= m_xNumRules;
524 uno::Sequence<uno::Sequence<beans::PropertyValue>> aProps = GetMergedPropertyValues();
526 sal_Int32 nAbstLevels = m_pAbstractDef ? m_pAbstractDef->Size() : 0;
527 sal_Int32 nLevel = 0;
528 while ( nLevel < nAbstLevels )
530 ListLevel::Pointer pAbsLevel = m_pAbstractDef->GetLevel( nLevel );
531 ListLevel::Pointer pLevel = GetLevel( nLevel );
533 // Get the merged level properties
534 auto aLvlProps = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aProps[nLevel]);
536 // Get the char style
537 uno::Sequence< beans::PropertyValue > aAbsCharStyleProps = pAbsLevel->GetCharStyleProperties( );
538 if ( pLevel.get( ) )
540 uno::Sequence< beans::PropertyValue >& rAbsCharStyleProps = aAbsCharStyleProps;
541 uno::Sequence< beans::PropertyValue > aCharStyleProps =
542 pLevel->GetCharStyleProperties( );
543 uno::Sequence< beans::PropertyValue >& rCharStyleProps = aCharStyleProps;
544 lcl_mergeProperties( rAbsCharStyleProps, rCharStyleProps );
547 if( aAbsCharStyleProps.getLength() )
549 // Change the sequence into a vector
550 PropertyValueVector_t aStyleProps;
551 for ( sal_Int32 i = 0, nLen = aAbsCharStyleProps.getLength() ; i < nLen; i++ )
553 aStyleProps.push_back( aAbsCharStyleProps[i] );
556 //create (or find) a character style containing the character
557 // attributes of the symbol and apply it to the numbering level
558 OUString sStyle = rDMapper.getOrCreateCharStyle( aStyleProps, /*bAlwaysCreate=*/true );
559 aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_CHAR_STYLE_NAME), sStyle));
562 // Get the prefix / suffix / Parent numbering
563 // and add them to the level properties
564 OUString sText = pAbsLevel->GetBulletChar( );
565 // Inherit <w:lvlText> from the abstract level in case the override would be empty.
566 if (pLevel.get() && !pLevel->GetBulletChar().isEmpty())
567 sText = pLevel->GetBulletChar( );
569 OUString sPrefix;
570 OUString sSuffix;
571 OUString& rPrefix = sPrefix;
572 OUString& rSuffix = sSuffix;
573 sal_Int16 nParentNum = ListLevel::GetParentNumbering(
574 sText, nLevel, rPrefix, rSuffix );
576 aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_PREFIX), rPrefix));
578 if (sText.isEmpty())
580 // Empty <w:lvlText>? Then put a Unicode "zero width space" as a suffix, so LabelFollowedBy is still shown, as in Word.
581 // With empty suffix, Writer does not show LabelFollowedBy, either.
582 auto it = std::find_if(aLvlProps.begin(), aLvlProps.end(), [](const beans::PropertyValue& rValue) { return rValue.Name == "NumberingType"; });
583 if (it != aLvlProps.end())
585 sal_Int16 nNumberFormat = it->Value.get<sal_Int16>();
587 // No need for a zero width space without a real LabelFollowedBy.
588 bool bLabelFollowedBy = true;
589 it = std::find_if(aLvlProps.begin(), aLvlProps.end(), [](const beans::PropertyValue& rValue) { return rValue.Name == "LabelFollowedBy"; });
590 if (it != aLvlProps.end())
592 sal_Int16 nValue;
593 if (it->Value >>= nValue)
594 bLabelFollowedBy = nValue != SvxNumberFormat::NOTHING;
597 if (bLabelFollowedBy && nNumberFormat == style::NumberingType::NUMBER_NONE)
598 rSuffix = OUString(u'\x200B');
602 aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SUFFIX), rSuffix));
603 aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_PARENT_NUMBERING), nParentNum));
605 aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_POSITION_AND_SPACE_MODE), sal_Int16(text::PositionAndSpaceMode::LABEL_ALIGNMENT)));
608 // Replace the numbering rules for the level
609 m_xNumRules->replaceByIndex(nLevel, uno::makeAny(comphelper::containerToSequence(aLvlProps)));
611 // Handle the outline level here
612 if ( pAbsLevel->isOutlineNumbering())
614 uno::Reference< text::XChapterNumberingSupplier > xOutlines (
615 xFactory, uno::UNO_QUERY_THROW );
616 uno::Reference< container::XIndexReplace > xOutlineRules =
617 xOutlines->getChapterNumberingRules( );
619 StyleSheetEntryPtr pParaStyle = pAbsLevel->GetParaStyle( );
620 aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HEADING_STYLE_NAME), pParaStyle->sConvertedStyleName));
622 xOutlineRules->replaceByIndex(nLevel, uno::makeAny(comphelper::containerToSequence(aLvlProps)));
625 nLevel++;
628 // Create the numbering style for these rules
629 OUString sNumRulesName = getPropertyName( PROP_NUMBERING_RULES );
630 xStyle->setPropertyValue( sNumRulesName, uno::makeAny( m_xNumRules ) );
632 catch( const lang::IllegalArgumentException& e )
634 SAL_WARN( "writerfilter", e );
635 assert( !"Incorrect argument to UNO call" );
637 catch( const uno::RuntimeException& e )
639 SAL_WARN( "writerfilter", e );
640 assert( !"Incorrect argument to UNO call" );
642 catch( const uno::Exception& e )
644 SAL_WARN( "writerfilter", e );
650 //------------------------------------- NumberingManager implementation
653 ListsManager::ListsManager(DomainMapper& rDMapper,
654 const uno::Reference<lang::XMultiServiceFactory> & xFactory)
655 : LoggedProperties("ListsManager")
656 , LoggedTable("ListsManager")
657 , m_rDMapper(rDMapper)
658 , m_xFactory(xFactory)
662 ListsManager::~ListsManager( )
664 DisposeNumPicBullets();
667 void ListsManager::DisposeNumPicBullets( )
669 uno::Reference<drawing::XShape> xShape;
670 for (const auto& rNumPicBullet : m_aNumPicBullets)
672 xShape = rNumPicBullet->GetShape();
673 if (xShape.is())
675 uno::Reference<lang::XComponent> xShapeComponent(xShape, uno::UNO_QUERY);
676 xShapeComponent->dispose();
681 void ListsManager::lcl_attribute( Id nName, Value& rVal )
683 ListLevel::Pointer pCurrentLvl;
685 if (nName != NS_ooxml::LN_CT_NumPicBullet_numPicBulletId)
687 OSL_ENSURE( m_pCurrentDefinition.get(), "current entry has to be set here");
688 if(!m_pCurrentDefinition.get())
689 return ;
690 pCurrentLvl = m_pCurrentDefinition->GetCurrentLevel( );
692 else
694 SAL_WARN_IF(!m_pCurrentNumPicBullet.get(), "writerfilter", "current entry has to be set here");
695 if (!m_pCurrentNumPicBullet.get())
696 return;
698 int nIntValue = rVal.getInt();
701 switch(nName)
703 case NS_ooxml::LN_CT_LevelText_val:
705 //this strings contains the definition of the level
706 //the level number is marked as %n
707 //these numbers can be mixed randomly together with separators pre- and suffixes
708 //the Writer supports only a number of upper levels to show, separators is always a dot
709 //and each level can have a prefix and a suffix
710 if(pCurrentLvl.get())
712 //if the BulletChar is a soft-hyphen (0xad)
713 //replace it with a hard-hyphen (0x2d)
714 //-> this fixes missing hyphen export in PDF etc.
715 // see tdf#101626
716 pCurrentLvl->SetBulletChar( rVal.getString().replace( 0xad, 0x2d ) );
719 break;
720 case NS_ooxml::LN_CT_Lvl_start:
721 case NS_ooxml::LN_CT_Lvl_numFmt:
722 case NS_ooxml::LN_CT_Lvl_isLgl:
723 case NS_ooxml::LN_CT_Lvl_legacy:
724 if ( pCurrentLvl.get( ) )
725 pCurrentLvl->SetValue( nName, sal_Int32( nIntValue ) );
726 break;
727 case NS_ooxml::LN_CT_Num_numId:
728 m_pCurrentDefinition->SetId( rVal.getString().toInt32( ) );
729 break;
730 case NS_ooxml::LN_CT_AbstractNum_nsid:
731 m_pCurrentDefinition->SetId( nIntValue );
732 break;
733 case NS_ooxml::LN_CT_AbstractNum_tmpl:
734 AbstractListDef::SetValue( nName );
735 break;
736 case NS_ooxml::LN_CT_NumLvl_ilvl:
738 //add a new level to the level vector and make it the current one
739 m_pCurrentDefinition->AddLevel();
741 writerfilter::Reference<Properties>::Pointer_t pProperties;
742 if((pProperties = rVal.getProperties()).get())
743 pProperties->resolve(*this);
745 break;
746 case NS_ooxml::LN_CT_AbstractNum_abstractNumId:
748 // This one corresponds to the AbstractNum Id definition
749 // The reference to the abstract num is in the sprm method
750 sal_Int32 nVal = rVal.getString().toInt32();
751 m_pCurrentDefinition->SetId( nVal );
753 break;
754 case NS_ooxml::LN_CT_Ind_start:
755 case NS_ooxml::LN_CT_Ind_left:
756 if ( pCurrentLvl.get( ) )
757 pCurrentLvl->Insert(
758 PROP_INDENT_AT, uno::makeAny( ConversionHelper::convertTwipToMM100( nIntValue ) ));
759 break;
760 case NS_ooxml::LN_CT_Ind_hanging:
761 if ( pCurrentLvl.get( ) )
762 pCurrentLvl->Insert(
763 PROP_FIRST_LINE_INDENT, uno::makeAny( - ConversionHelper::convertTwipToMM100( nIntValue ) ));
764 break;
765 case NS_ooxml::LN_CT_Ind_firstLine:
766 if ( pCurrentLvl.get( ) )
767 pCurrentLvl->Insert(
768 PROP_FIRST_LINE_INDENT, uno::makeAny( ConversionHelper::convertTwipToMM100( nIntValue ) ));
769 break;
770 case NS_ooxml::LN_CT_Lvl_ilvl: //overrides previous level - unsupported
771 case NS_ooxml::LN_CT_Lvl_tplc: //template code - unsupported
772 case NS_ooxml::LN_CT_Lvl_tentative: //marks level as unused in the document - unsupported
773 break;
774 case NS_ooxml::LN_CT_TabStop_pos:
776 //no paragraph attributes in ListTable char style sheets
777 if ( pCurrentLvl.get( ) )
778 pCurrentLvl->SetValue( nName,
779 ConversionHelper::convertTwipToMM100( nIntValue ) );
781 break;
782 case NS_ooxml::LN_CT_TabStop_val:
784 // TODO Do something of that
786 break;
787 case NS_ooxml::LN_CT_NumPicBullet_numPicBulletId:
788 m_pCurrentNumPicBullet->SetId(rVal.getString().toInt32());
789 break;
790 default:
791 SAL_WARN("writerfilter", "ListsManager::lcl_attribute: unhandled token: " << nName);
795 void ListsManager::lcl_sprm( Sprm& rSprm )
797 //fill the attributes of the style sheet
798 sal_uInt32 nSprmId = rSprm.getId();
799 if( m_pCurrentDefinition.get() ||
800 nSprmId == NS_ooxml::LN_CT_Numbering_abstractNum ||
801 nSprmId == NS_ooxml::LN_CT_Numbering_num ||
802 (nSprmId == NS_ooxml::LN_CT_NumPicBullet_pict && m_pCurrentNumPicBullet.get()) ||
803 nSprmId == NS_ooxml::LN_CT_Numbering_numPicBullet)
805 static bool bIsStartVisited = false;
806 sal_Int32 nIntValue = rSprm.getValue()->getInt();
807 switch( nSprmId )
809 case NS_ooxml::LN_CT_Numbering_abstractNum:
811 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
812 if(pProperties.get())
814 //create a new Abstract list entry
815 OSL_ENSURE( !m_pCurrentDefinition.get(), "current entry has to be NULL here");
816 m_pCurrentDefinition = new AbstractListDef;
817 pProperties->resolve( *this );
818 //append it to the table
819 m_aAbstractLists.push_back( m_pCurrentDefinition );
820 m_pCurrentDefinition = AbstractListDef::Pointer();
823 break;
824 case NS_ooxml::LN_CT_Numbering_num:
826 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
827 if(pProperties.get())
829 // Create a new list entry
830 OSL_ENSURE( !m_pCurrentDefinition.get(), "current entry has to be NULL here");
831 ListDef::Pointer listDef( new ListDef );
832 m_pCurrentDefinition = listDef.get();
833 pProperties->resolve( *this );
834 //append it to the table
835 m_aLists.push_back( listDef );
837 m_pCurrentDefinition = AbstractListDef::Pointer();
840 break;
841 case NS_ooxml::LN_CT_Numbering_numPicBullet:
843 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
844 if (pProperties.get())
846 NumPicBullet::Pointer numPicBullet(new NumPicBullet());
847 m_pCurrentNumPicBullet = numPicBullet;
848 pProperties->resolve(*this);
849 m_aNumPicBullets.push_back(numPicBullet);
850 m_pCurrentNumPicBullet = NumPicBullet::Pointer();
853 break;
854 case NS_ooxml::LN_CT_NumPicBullet_pict:
856 uno::Reference<drawing::XShape> xShape = m_rDMapper.PopPendingShape();
858 m_pCurrentNumPicBullet->SetShape(xShape);
860 break;
861 case NS_ooxml::LN_CT_Lvl_lvlPicBulletId:
862 if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
864 uno::Reference<drawing::XShape> xShape;
865 for (const auto& rNumPicBullet : m_aNumPicBullets)
867 if (rNumPicBullet->GetId() == nIntValue)
869 xShape = rNumPicBullet->GetShape();
870 break;
873 if (xShape.is())
875 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
878 uno::Any aAny = xPropertySet->getPropertyValue("Graphic");
879 if (aAny.has<uno::Reference<graphic::XGraphic>>() && pCurrentLevel)
881 auto xGraphic = aAny.get<uno::Reference<graphic::XGraphic>>();
882 if (xGraphic.is())
884 uno::Reference<awt::XBitmap> xBitmap(xGraphic, uno::UNO_QUERY);
885 pCurrentLevel->SetGraphicBitmap(xBitmap);
889 catch (const beans::UnknownPropertyException&)
892 // Respect only the aspect ratio of the picture, not its size.
893 awt::Size aPrefSize = xShape->getSize();
894 // See SwDefBulletConfig::InitFont(), default height is 14.
895 const int nFontHeight = 14;
896 // Point -> mm100.
897 const int nHeight = nFontHeight * 35;
898 if ( aPrefSize.Height * aPrefSize.Width != 0 )
900 int nWidth = (nHeight * aPrefSize.Width) / aPrefSize.Height;
902 awt::Size aSize( convertMm100ToTwip(nWidth), convertMm100ToTwip(nHeight) );
903 pCurrentLevel->SetGraphicSize( aSize );
905 else
907 awt::Size aSize( convertMm100ToTwip(aPrefSize.Width), convertMm100ToTwip(aPrefSize.Height) );
908 pCurrentLevel->SetGraphicSize( aSize );
912 break;
913 case NS_ooxml::LN_CT_Num_abstractNumId:
915 sal_Int32 nAbstractNumId = rSprm.getValue()->getInt();
916 ListDef* pListDef = dynamic_cast< ListDef* >( m_pCurrentDefinition.get( ) );
917 if ( pListDef != nullptr )
919 // The current def should be a ListDef
920 pListDef->SetAbstractDefinition(
921 GetAbstractList( nAbstractNumId ) );
924 break;
925 case NS_ooxml::LN_CT_AbstractNum_multiLevelType:
926 break;
927 case NS_ooxml::LN_CT_AbstractNum_tmpl:
928 AbstractListDef::SetValue( nSprmId );
929 break;
930 case NS_ooxml::LN_CT_AbstractNum_lvl:
932 m_pCurrentDefinition->AddLevel();
933 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
934 if(pProperties.get())
935 pProperties->resolve(*this);
937 break;
938 case NS_ooxml::LN_CT_Lvl_start:
939 if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
940 pCurrentLevel->SetValue( nSprmId, nIntValue );
941 bIsStartVisited = true;
942 break;
943 case NS_ooxml::LN_CT_Lvl_numFmt:
944 case NS_ooxml::LN_CT_Lvl_isLgl:
945 case NS_ooxml::LN_CT_Lvl_legacy:
946 if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
948 pCurrentLevel->SetValue( nSprmId, nIntValue );
949 if( !bIsStartVisited )
951 pCurrentLevel->SetValue( NS_ooxml::LN_CT_Lvl_start, 0 );
952 bIsStartVisited = true;
955 break;
956 case NS_ooxml::LN_CT_Lvl_suff:
958 if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
960 SvxNumberFormat::LabelFollowedBy value = SvxNumberFormat::LISTTAB;
961 if( rSprm.getValue()->getString() == "tab" )
962 value = SvxNumberFormat::LISTTAB;
963 else if( rSprm.getValue()->getString() == "space" )
964 value = SvxNumberFormat::SPACE;
965 else if( rSprm.getValue()->getString() == "nothing" )
966 value = SvxNumberFormat::NOTHING;
967 else
968 SAL_WARN( "writerfilter", "Unknown ST_LevelSuffix value "
969 << rSprm.getValue()->getString());
970 pCurrentLevel->SetValue( nSprmId, value );
973 break;
974 case NS_ooxml::LN_CT_Lvl_lvlText:
975 case NS_ooxml::LN_CT_Lvl_rPr : //contains LN_EG_RPrBase_rFonts
977 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
978 if(pProperties.get())
979 pProperties->resolve(*this);
981 break;
982 case NS_ooxml::LN_CT_NumLvl_lvl:
984 // overwrite level
985 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
986 if(pProperties.get())
987 pProperties->resolve(*this);
989 break;
990 case NS_ooxml::LN_CT_Lvl_lvlJc:
992 sal_Int16 nValue = text::HoriOrientation::NONE;
993 switch (nIntValue)
995 case NS_ooxml::LN_Value_ST_Jc_left:
996 case NS_ooxml::LN_Value_ST_Jc_start:
997 nValue = text::HoriOrientation::LEFT;
998 break;
999 case NS_ooxml::LN_Value_ST_Jc_center:
1000 nValue = text::HoriOrientation::CENTER;
1001 break;
1002 case NS_ooxml::LN_Value_ST_Jc_right:
1003 case NS_ooxml::LN_Value_ST_Jc_end:
1004 nValue = text::HoriOrientation::RIGHT;
1005 break;
1008 if (nValue != text::HoriOrientation::NONE)
1010 if (ListLevel::Pointer pLevel = m_pCurrentDefinition->GetCurrentLevel())
1012 pLevel->Insert(
1013 PROP_ADJUST, uno::makeAny( nValue ) );
1014 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
1018 break;
1019 case NS_ooxml::LN_CT_Lvl_pPr:
1020 case NS_ooxml::LN_CT_PPrBase_ind:
1022 //todo: how to handle paragraph properties within numbering levels (except LeftIndent and FirstLineIndent)?
1023 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
1024 if(pProperties.get())
1025 pProperties->resolve(*this);
1027 break;
1028 case NS_ooxml::LN_CT_PPrBase_tabs:
1029 case NS_ooxml::LN_CT_Tabs_tab:
1031 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
1032 if(pProperties.get())
1033 pProperties->resolve(*this);
1035 break;
1036 case NS_ooxml::LN_CT_Lvl_pStyle:
1038 OUString sStyleName = rSprm.getValue( )->getString( );
1039 if (ListLevel::Pointer pLevel = m_pCurrentDefinition->GetCurrentLevel())
1041 StyleSheetTablePtr pStylesTable = m_rDMapper.GetStyleSheetTable( );
1042 const StyleSheetEntryPtr pStyle = pStylesTable->FindStyleSheetByISTD( sStyleName );
1043 pLevel->SetParaStyle( pStyle );
1046 break;
1047 case NS_ooxml::LN_CT_Num_lvlOverride:
1049 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
1050 if (pProperties.get())
1051 pProperties->resolve(*this);
1053 break;
1054 case NS_ooxml::LN_CT_NumLvl_startOverride:
1056 if(m_pCurrentDefinition)
1058 if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
1060 // <w:num> -> <w:lvlOverride> -> <w:startOverride> is the non-abstract equivalent of
1061 // <w:abstractNum> -> <w:lvl> -> <w:start>
1062 pCurrentLevel->SetValue(NS_ooxml::LN_CT_Lvl_start, nIntValue);
1066 break;
1067 case NS_ooxml::LN_CT_AbstractNum_numStyleLink:
1069 OUString sStyleName = rSprm.getValue( )->getString( );
1070 m_pCurrentDefinition->SetNumStyleLink(sStyleName);
1072 break;
1073 case NS_ooxml::LN_EG_RPrBase_rFonts: //contains font properties
1074 case NS_ooxml::LN_EG_RPrBase_color:
1075 case NS_ooxml::LN_EG_RPrBase_u:
1076 case NS_ooxml::LN_EG_RPrBase_sz:
1077 case NS_ooxml::LN_EG_RPrBase_lang:
1078 case NS_ooxml::LN_EG_RPrBase_eastAsianLayout:
1079 //no break!
1080 default:
1081 if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
1083 m_rDMapper.PushListProperties(pCurrentLevel.get());
1084 m_rDMapper.sprm( rSprm );
1085 m_rDMapper.PopListProperties();
1091 void ListsManager::lcl_entry( int /* pos */,
1092 writerfilter::Reference<Properties>::Pointer_t ref )
1094 if( m_rDMapper.IsOOXMLImport() || m_rDMapper.IsRTFImport() )
1096 ref->resolve(*this);
1098 else
1100 // Create AbstractListDef's
1101 OSL_ENSURE( !m_pCurrentDefinition.get(), "current entry has to be NULL here");
1102 m_pCurrentDefinition = new AbstractListDef( );
1103 ref->resolve(*this);
1104 //append it to the table
1105 m_aAbstractLists.push_back( m_pCurrentDefinition );
1106 m_pCurrentDefinition = AbstractListDef::Pointer();
1110 AbstractListDef::Pointer ListsManager::GetAbstractList( sal_Int32 nId )
1112 AbstractListDef::Pointer pAbstractList;
1114 int nLen = m_aAbstractLists.size( );
1115 int i = 0;
1116 while ( !pAbstractList.get( ) && i < nLen )
1118 if ( m_aAbstractLists[i]->GetId( ) == nId )
1120 if ( m_aAbstractLists[i]->GetNumStyleLink().getLength() > 0 )
1122 // If the abstract num has a style linked, check the linked style's number id.
1123 StyleSheetTablePtr pStylesTable = m_rDMapper.GetStyleSheetTable( );
1125 const StyleSheetEntryPtr pStyleSheetEntry =
1126 pStylesTable->FindStyleSheetByISTD( m_aAbstractLists[i]->GetNumStyleLink() );
1128 const StyleSheetPropertyMap* pStyleSheetProperties =
1129 dynamic_cast<const StyleSheetPropertyMap*>(pStyleSheetEntry ? pStyleSheetEntry->pProperties.get() : nullptr);
1131 if( pStyleSheetProperties && pStyleSheetProperties->GetNumId() >= 0 )
1133 ListDef::Pointer pList = GetList( pStyleSheetProperties->GetNumId() );
1134 if ( pList!=nullptr )
1135 return pList->GetAbstractDefinition();
1136 else
1137 pAbstractList = m_aAbstractLists[i];
1141 else
1143 pAbstractList = m_aAbstractLists[i];
1146 i++;
1149 return pAbstractList;
1152 ListDef::Pointer ListsManager::GetList( sal_Int32 nId )
1154 ListDef::Pointer pList;
1156 int nLen = m_aLists.size( );
1157 int i = 0;
1158 while ( !pList.get( ) && i < nLen )
1160 if ( m_aLists[i]->GetId( ) == nId )
1161 pList = m_aLists[i];
1162 i++;
1165 return pList;
1168 void ListsManager::CreateNumberingRules( )
1170 // Loop over the definitions
1171 for ( const auto& rList : m_aLists )
1173 rList->CreateNumberingRules( m_rDMapper, m_xFactory );
1179 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */