update emoji autocorrect entries from po-files
[LibreOffice.git] / writerfilter / source / dmapper / NumberingManager.cxx
blobe9b8a46c14c4c333f54bad259d647840500228b2
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 .
19 #include "ConversionHelper.hxx"
20 #include "NumberingManager.hxx"
21 #include "StyleSheetTable.hxx"
22 #include "PropertyIds.hxx"
24 #include <ooxml/resourceids.hxx>
26 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
27 #include <com/sun/star/container/XNameContainer.hpp>
28 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
29 #include <com/sun/star/style/NumberingType.hpp>
30 #include <com/sun/star/text/HoriOrientation.hpp>
31 #include <com/sun/star/text/PositionAndSpaceMode.hpp>
32 #include <com/sun/star/text/XChapterNumberingSupplier.hpp>
34 #include <osl/diagnose.h>
35 #include <rtl/ustring.hxx>
36 #include <comphelper/sequenceashashmap.hxx>
37 #include <comphelper/sequence.hxx>
39 using namespace com::sun::star;
41 #define MAKE_PROPVAL(NameId, Value) \
42 beans::PropertyValue(aPropNameSupplier.GetName(NameId), 0, uno::makeAny(Value), beans::PropertyState_DIRECT_VALUE )
44 #define NUMBERING_MAX_LEVELS 10
47 namespace writerfilter {
48 namespace dmapper {
50 //--------------------------------------------------- Utility functions
52 #ifdef DEBUG_WRITERFILTER
53 void lcl_printProperties( uno::Sequence< beans::PropertyValue > const & aProps )
55 sal_Int32 nLen = aProps.getLength( );
56 for ( sal_Int32 i = 0; i < nLen; i++ )
58 uno::Any aValue = aProps[i].Value;
59 sal_Int32 nValue = 0;
60 OUString sValue;
62 if ( !( aValue >>= sValue ) && ( aValue >>= nValue ) )
63 sValue = OUString::number( nValue );
65 SAL_INFO("writerfilter", "Property " << aProps[i].Name << ": " << sValue);
68 #endif
70 sal_Int32 lcl_findProperty( const uno::Sequence< beans::PropertyValue >& aProps, const OUString& sName )
72 sal_Int32 i = 0;
73 sal_Int32 nLen = aProps.getLength( );
74 sal_Int32 nPos = -1;
76 while ( nPos == -1 && i < nLen )
78 if ( aProps[i].Name.equals( sName ) )
79 nPos = i;
80 else
81 i++;
84 return nPos;
87 void lcl_mergeProperties( uno::Sequence< beans::PropertyValue >& aSrc,
88 uno::Sequence< beans::PropertyValue >& aDst )
90 for ( sal_Int32 i = 0, nSrcLen = aSrc.getLength( ); i < nSrcLen; i++ )
92 // Look for the same property in aDst
93 sal_Int32 nPos = lcl_findProperty( aDst, aSrc[i].Name );
94 if ( nPos >= 0 )
96 // Replace the property value by the one in aSrc
97 aDst[nPos] = aSrc[i];
99 else
101 // Simply add the new value
102 aDst.realloc( aDst.getLength( ) + 1 );
103 aDst[ aDst.getLength( ) - 1 ] = aSrc[i];
108 //-------------------------------------------- ListLevel implementation
109 void ListLevel::SetValue( Id nId, sal_Int32 nValue )
111 switch( nId )
113 case NS_ooxml::LN_CT_Lvl_start:
114 m_nIStartAt = nValue;
115 break;
116 case NS_ooxml::LN_CT_Lvl_numFmt:
117 m_nNFC = nValue;
118 break;
119 case NS_ooxml::LN_CT_Lvl_isLgl:
120 m_nFLegal = nValue;
121 break;
122 case NS_ooxml::LN_CT_Lvl_legacy:
123 m_nFPrevSpace = nValue;
124 break;
125 case NS_ooxml::LN_CT_Lvl_suff:
126 m_nXChFollow = nValue;
127 break;
128 case NS_ooxml::LN_CT_TabStop_pos:
129 if (nValue < 0)
131 SAL_INFO("writerfilter",
132 "unsupported list tab stop position " << nValue);
134 else
135 m_nTabstop = nValue;
136 break;
137 default:
138 OSL_FAIL( "this line should never be reached");
142 void ListLevel::SetParaStyle( std::shared_ptr< StyleSheetEntry > pStyle )
144 if (!pStyle)
145 return;
146 m_pParaStyle = pStyle;
147 // AFAICT .docx spec does not identify which numberings or paragraph
148 // styles are actually the ones to be used for outlines (chapter numbering),
149 // it only kind of says somewhere that they should be named Heading1 to Heading9.
150 const OUString styleId= pStyle->sConvertedStyleName;
151 m_outline = ( styleId.getLength() == RTL_CONSTASCII_LENGTH( "Heading 1" )
152 && styleId.match( "Heading ", 0 )
153 && styleId[ RTL_CONSTASCII_LENGTH( "Heading " ) ] >= '1'
154 && styleId[ RTL_CONSTASCII_LENGTH( "Heading " ) ] <= '9' );
157 sal_Int16 ListLevel::GetParentNumbering( const OUString& sText, sal_Int16 nLevel,
158 OUString& rPrefix, OUString& rSuffix )
160 sal_Int16 nParentNumbering = 1;
162 //now parse the text to find %n from %1 to %nLevel+1
163 //everything before the first % and the last %x is prefix and suffix
164 OUString sLevelText( sText );
165 sal_Int32 nCurrentIndex = 0;
166 sal_Int32 nFound = sLevelText.indexOf( '%', nCurrentIndex );
167 if( nFound > 0 )
169 rPrefix = sLevelText.copy( 0, nFound );
170 sLevelText = sLevelText.copy( nFound );
172 sal_Int32 nMinLevel = nLevel;
173 //now the text should either be empty or start with %
174 nFound = sLevelText.getLength( ) > 1 ? 0 : -1;
175 while( nFound >= 0 )
177 if( sLevelText.getLength() > 1 )
179 sal_Unicode cLevel = sLevelText[1];
180 if( cLevel >= '1' && cLevel <= '9' )
182 if( cLevel - '1' < nMinLevel )
183 nMinLevel = cLevel - '1';
184 //remove first char - next char is removed later
185 sLevelText = sLevelText.copy( 1 );
188 //remove old '%' or number
189 sLevelText = sLevelText.copy( 1 );
190 nCurrentIndex = 0;
191 nFound = sLevelText.indexOf( '%', nCurrentIndex );
192 //remove the text before the next %
193 if(nFound > 0)
194 sLevelText = sLevelText.copy( nFound -1 );
196 if( nMinLevel < nLevel )
198 nParentNumbering = sal_Int16( nLevel - nMinLevel + 1);
201 rSuffix = sLevelText;
203 return nParentNumbering;
206 uno::Sequence< beans::PropertyValue > ListLevel::GetProperties( )
208 uno::Sequence< beans::PropertyValue > aLevelProps = GetLevelProperties( );
209 if ( m_pParaStyle.get( ) )
210 AddParaProperties( &aLevelProps );
211 return aLevelProps;
214 static bool IgnoreForCharStyle(const OUString& aStr)
216 //Names found in PropertyIds.cxx, Lines 56-396
217 return (aStr=="Adjust" || aStr=="IndentAt" || aStr=="FirstLineIndent"
218 || aStr=="FirstLineOffset" || aStr=="LeftMargin" || aStr=="CharFontName"
221 uno::Sequence< beans::PropertyValue > ListLevel::GetCharStyleProperties( )
223 PropertyValueVector_t rProperties;
225 uno::Sequence< beans::PropertyValue > vPropVals = PropertyMap::GetPropertyValues();
226 beans::PropertyValue* aValIter = vPropVals.begin();
227 beans::PropertyValue* aEndIter = vPropVals.end();
228 for( ; aValIter != aEndIter; ++aValIter )
230 if (IgnoreForCharStyle(aValIter->Name))
231 continue;
232 else if(aValIter->Name=="CharInteropGrabBag" || aValIter->Name=="ParaInteropGrabBag") {
233 uno::Sequence<beans::PropertyValue> vGrabVals;
234 aValIter->Value >>= vGrabVals;
235 beans::PropertyValue* aGrabIter = vGrabVals.begin();
236 for(; aGrabIter!=vGrabVals.end(); ++aGrabIter) {
237 if(!IgnoreForCharStyle(aGrabIter->Name))
238 rProperties.push_back(beans::PropertyValue(aGrabIter->Name, 0, aGrabIter->Value, beans::PropertyState_DIRECT_VALUE));
241 else
242 rProperties.push_back(beans::PropertyValue(aValIter->Name, 0, aValIter->Value, beans::PropertyState_DIRECT_VALUE));
245 uno::Sequence< beans::PropertyValue > aRet( rProperties.size() );
246 beans::PropertyValue* pValues = aRet.getArray();
247 PropertyValueVector_t::const_iterator aIt = rProperties.begin();
248 PropertyValueVector_t::const_iterator aEndIt = rProperties.end();
249 for(sal_uInt32 nIndex = 0; aIt != aEndIt; ++aIt,++nIndex)
251 pValues[nIndex] = *aIt;
253 return aRet;
256 uno::Sequence< beans::PropertyValue > ListLevel::GetLevelProperties( )
258 const sal_Int16 aWWToUnoAdjust[] =
260 text::HoriOrientation::LEFT,
261 text::HoriOrientation::CENTER,
262 text::HoriOrientation::RIGHT,
265 PropertyNameSupplier& aPropNameSupplier = PropertyNameSupplier::GetPropertyNameSupplier();
266 std::vector<beans::PropertyValue> aNumberingProperties;
268 if( m_nIStartAt >= 0)
269 aNumberingProperties.push_back( MAKE_PROPVAL(PROP_START_WITH, (sal_Int16)m_nIStartAt) );
271 sal_Int16 nNumberFormat = ConversionHelper::ConvertNumberingType(m_nNFC);
272 if( m_nNFC >= 0)
274 if (!m_sGraphicURL.isEmpty() || m_sGraphicBitmap.is())
275 nNumberFormat = style::NumberingType::BITMAP;
276 else if (m_sBulletChar.isEmpty() && nNumberFormat != style::NumberingType::CHAR_SPECIAL)
277 // w:lvlText is empty, that means no numbering in Word.
278 // CHAR_SPECIAL is handled separately below.
279 nNumberFormat = style::NumberingType::NUMBER_NONE;
280 aNumberingProperties.push_back( MAKE_PROPVAL(PROP_NUMBERING_TYPE, nNumberFormat ));
283 if( m_nJC >= 0 && m_nJC <= sal::static_int_cast<sal_Int32>(sizeof(aWWToUnoAdjust) / sizeof(sal_Int16)) )
284 aNumberingProperties.push_back( MAKE_PROPVAL(PROP_ADJUST, aWWToUnoAdjust[m_nJC]));
286 if( !isOutlineNumbering())
288 // todo: this is not the bullet char
289 if( nNumberFormat == style::NumberingType::CHAR_SPECIAL )
291 if (!m_sBulletChar.isEmpty())
293 aNumberingProperties.push_back( MAKE_PROPVAL(PROP_BULLET_CHAR, m_sBulletChar.copy(0,1)));
295 else
297 // If w:lvlText's value is null - set bullet char to zero.
298 aNumberingProperties.push_back( MAKE_PROPVAL(PROP_BULLET_CHAR, sal_Unicode(0x0)));
301 if (!m_sGraphicURL.isEmpty())
302 aNumberingProperties.push_back(MAKE_PROPVAL(PROP_GRAPHIC_URL, m_sGraphicURL));
303 if (m_sGraphicBitmap.is())
304 aNumberingProperties.push_back(MAKE_PROPVAL(PROP_GRAPHIC_BITMAP, m_sGraphicBitmap));
307 aNumberingProperties.push_back( MAKE_PROPVAL( PROP_LISTTAB_STOP_POSITION, m_nTabstop ) );
309 //TODO: handling of nFLegal?
310 //TODO: nFNoRestart lower levels do not restart when higher levels are incremented, like:
311 //1.
312 //1.1
313 //2.2
314 //2.3
315 //3.4
318 if( m_nFWord6 > 0) //Word 6 compatibility
320 if( m_nFPrev == 1)
321 aNumberingProperties.push_back( MAKE_PROPVAL( PROP_PARENT_NUMBERING, (sal_Int16) NUMBERING_MAX_LEVELS ));
322 //TODO: prefixing space nFPrevSpace; - has not been used in WW8 filter
325 // TODO: sRGBXchNums; array of inherited numbers
327 // nXChFollow; following character 0 - tab, 1 - space, 2 - nothing
328 aNumberingProperties.push_back( MAKE_PROPVAL( PROP_LEVEL_FOLLOW, m_nXChFollow ));
330 const int nIds = 5;
331 PropertyIds aReadIds[nIds] =
333 PROP_ADJUST, PROP_INDENT_AT, PROP_FIRST_LINE_INDENT,
334 PROP_FIRST_LINE_OFFSET, PROP_LEFT_MARGIN
336 for(int i=0; i<nIds; ++i) {
337 boost::optional<PropertyMap::Property> aProp = getProperty(aReadIds[i]);
338 if (aProp)
339 aNumberingProperties.push_back(
340 beans::PropertyValue( aPropNameSupplier.GetName(aProp->first), 0, aProp->second, beans::PropertyState_DIRECT_VALUE )
344 boost::optional<PropertyMap::Property> aPropFont = getProperty(PROP_CHAR_FONT_NAME);
345 if(aPropFont && !isOutlineNumbering())
346 aNumberingProperties.push_back(
347 beans::PropertyValue( aPropNameSupplier.GetName(PROP_BULLET_FONT_NAME), 0, aPropFont->second, beans::PropertyState_DIRECT_VALUE )
350 return comphelper::containerToSequence(aNumberingProperties);
353 // Add the properties only if they do not already exist in the sequence.
354 void ListLevel::AddParaProperties( uno::Sequence< beans::PropertyValue >* props )
356 uno::Sequence< beans::PropertyValue >& aProps = *props;
357 PropertyNameSupplier& aPropNameSupplier = PropertyNameSupplier::GetPropertyNameSupplier();
359 OUString sFirstLineIndent = aPropNameSupplier.GetName(
360 PROP_FIRST_LINE_INDENT );
361 OUString sIndentAt = aPropNameSupplier.GetName(
362 PROP_INDENT_AT );
364 bool hasFirstLineIndent = lcl_findProperty( aProps, sFirstLineIndent );
365 bool hasIndentAt = lcl_findProperty( aProps, sIndentAt );
367 if( hasFirstLineIndent && hasIndentAt )
368 return; // has them all, nothing to add
370 uno::Sequence< beans::PropertyValue > aParaProps = m_pParaStyle->pProperties->GetPropertyValues( );
372 // ParaFirstLineIndent -> FirstLineIndent
373 // ParaLeftMargin -> IndentAt
375 OUString sParaIndent = aPropNameSupplier.GetName(
376 PROP_PARA_FIRST_LINE_INDENT );
377 OUString sParaLeftMargin = aPropNameSupplier.GetName(
378 PROP_PARA_LEFT_MARGIN );
380 sal_Int32 nLen = aParaProps.getLength( );
381 for ( sal_Int32 i = 0; i < nLen; i++ )
383 if ( !hasFirstLineIndent && aParaProps[i].Name.equals( sParaIndent ) )
385 aProps.realloc( aProps.getLength() + 1 );
386 aProps[aProps.getLength( ) - 1] = aParaProps[i];
387 aProps[aProps.getLength( ) - 1].Name = sFirstLineIndent;
389 else if ( !hasIndentAt && aParaProps[i].Name.equals( sParaLeftMargin ) )
391 aProps.realloc( aProps.getLength() + 1 );
392 aProps[aProps.getLength( ) - 1] = aParaProps[i];
393 aProps[aProps.getLength( ) - 1].Name = sIndentAt;
399 NumPicBullet::NumPicBullet()
400 : m_nId(0)
404 NumPicBullet::~NumPicBullet()
408 void NumPicBullet::SetId(sal_Int32 nId)
410 m_nId = nId;
413 void NumPicBullet::SetShape(uno::Reference<drawing::XShape> const& xShape)
415 m_xShape = xShape;
420 //--------------------------------------- AbstractListDef implementation
422 AbstractListDef::AbstractListDef( ) :
423 m_nTmpl( -1 )
424 ,m_nId( -1 )
428 AbstractListDef::~AbstractListDef( )
432 void AbstractListDef::SetValue( sal_uInt32 nSprmId, sal_Int32 nValue )
434 switch( nSprmId )
436 case NS_ooxml::LN_CT_AbstractNum_tmpl:
437 m_nTmpl = nValue;
438 break;
439 default:
440 OSL_FAIL( "this line should never be reached");
444 ListLevel::Pointer AbstractListDef::GetLevel( sal_uInt16 nLvl )
446 ListLevel::Pointer pLevel;
447 if ( m_aLevels.size( ) > nLvl )
448 pLevel = m_aLevels[ nLvl ];
449 return pLevel;
452 void AbstractListDef::AddLevel( )
454 ListLevel::Pointer pLevel( new ListLevel );
455 m_pCurrentLevel = pLevel;
456 m_aLevels.push_back( pLevel );
459 uno::Sequence< uno::Sequence< beans::PropertyValue > > AbstractListDef::GetPropertyValues( )
461 uno::Sequence< uno::Sequence< beans::PropertyValue > > result( sal_Int32( m_aLevels.size( ) ) );
462 uno::Sequence< beans::PropertyValue >* aResult = result.getArray( );
464 int nLevels = m_aLevels.size( );
465 for ( int i = 0; i < nLevels; i++ )
467 aResult[i] = m_aLevels[i]->GetProperties( );
470 return result;
473 //---------------------------------------------- ListDef implementation
475 ListDef::ListDef( ) : AbstractListDef( )
479 ListDef::~ListDef( )
483 OUString ListDef::GetStyleName( sal_Int32 nId )
485 OUString sStyleName( "WWNum" );
486 sStyleName += OUString::number( nId );
488 return sStyleName;
491 uno::Sequence< uno::Sequence< beans::PropertyValue > > ListDef::GetPropertyValues( )
493 if (!m_pAbstractDef)
494 return uno::Sequence< uno::Sequence< beans::PropertyValue > >();
496 // [1] Call the same method on the abstract list
497 uno::Sequence< uno::Sequence< beans::PropertyValue > > aAbstract = m_pAbstractDef->GetPropertyValues( );
499 // [2] Call the upper class method
500 uno::Sequence< uno::Sequence< beans::PropertyValue > > aThis = AbstractListDef::GetPropertyValues( );
502 // Merge the results of [2] in [1]
503 sal_Int32 nThisCount = aThis.getLength( );
504 sal_Int32 nAbstractCount = aAbstract.getLength( );
505 for ( sal_Int32 i = 0; i < nThisCount && i < nAbstractCount; i++ )
507 uno::Sequence< beans::PropertyValue > level = aThis[i];
508 if ( level.hasElements() )
510 // If the element contains something, merge it
511 lcl_mergeProperties( level, aAbstract[i] );
515 return aAbstract;
518 uno::Reference< container::XNameContainer > lcl_getUnoNumberingStyles(
519 uno::Reference<lang::XMultiServiceFactory> const& xFactory)
521 uno::Reference< container::XNameContainer > xStyles;
525 uno::Reference< style::XStyleFamiliesSupplier > xFamilies( xFactory, uno::UNO_QUERY_THROW );
526 uno::Any oFamily = xFamilies->getStyleFamilies( )->getByName("NumberingStyles");
528 oFamily >>= xStyles;
530 catch ( const uno::Exception & )
534 return xStyles;
537 void ListDef::CreateNumberingRules( DomainMapper& rDMapper,
538 uno::Reference<lang::XMultiServiceFactory> const& xFactory)
540 // Get the UNO Numbering styles
541 uno::Reference< container::XNameContainer > xStyles = lcl_getUnoNumberingStyles( xFactory );
543 // Do the whole thing
544 if( !m_xNumRules.is() && xFactory.is() && xStyles.is( ) )
548 // Create the numbering style
549 uno::Reference< beans::XPropertySet > xStyle (
550 xFactory->createInstance("com.sun.star.style.NumberingStyle"),
551 uno::UNO_QUERY_THROW );
553 OUString sStyleName = GetStyleName( GetId( ) );
555 xStyles->insertByName( sStyleName, makeAny( xStyle ) );
557 uno::Any oStyle = xStyles->getByName( sStyleName );
558 xStyle.set( oStyle, uno::UNO_QUERY_THROW );
560 PropertyNameSupplier& aPropNameSupplier = PropertyNameSupplier::GetPropertyNameSupplier();
562 // Get the default OOo Numbering style rules
563 uno::Any aRules = xStyle->getPropertyValue( aPropNameSupplier.GetName( PROP_NUMBERING_RULES ) );
564 aRules >>= m_xNumRules;
566 uno::Sequence< uno::Sequence< beans::PropertyValue > > aProps = GetPropertyValues( );
568 sal_Int32 nAbstLevels = m_pAbstractDef ? m_pAbstractDef->Size() : 0;
569 sal_Int16 nLevel = 0;
570 while ( nLevel < nAbstLevels )
572 ListLevel::Pointer pAbsLevel = m_pAbstractDef->GetLevel( nLevel );
573 ListLevel::Pointer pLevel = GetLevel( nLevel );
575 // Get the merged level properties
576 uno::Sequence< beans::PropertyValue > aLvlProps = aProps[sal_Int32( nLevel )];
578 #ifdef DEBUG_WRITERFILTER
579 lcl_printProperties( aLvlProps );
580 #endif
582 // Get the char style
583 uno::Sequence< beans::PropertyValue > aAbsCharStyleProps = pAbsLevel->GetCharStyleProperties( );
584 uno::Sequence< beans::PropertyValue >& rAbsCharStyleProps = aAbsCharStyleProps;
585 if ( pLevel.get( ) )
587 uno::Sequence< beans::PropertyValue > aCharStyleProps =
588 pLevel->GetCharStyleProperties( );
589 uno::Sequence< beans::PropertyValue >& rCharStyleProps = aCharStyleProps;
590 lcl_mergeProperties( rAbsCharStyleProps, rCharStyleProps );
593 if( aAbsCharStyleProps.getLength() )
595 // Change the sequence into a vector
596 PropertyValueVector_t aStyleProps;
597 for ( sal_Int32 i = 0, nLen = aAbsCharStyleProps.getLength() ; i < nLen; i++ )
599 aStyleProps.push_back( aAbsCharStyleProps[i] );
602 //create (or find) a character style containing the character
603 // attributes of the symbol and apply it to the numbering level
604 OUString sStyle = rDMapper.getOrCreateCharStyle( aStyleProps );
605 aLvlProps.realloc( aLvlProps.getLength() + 1);
606 aLvlProps[sal::static_int_cast<sal_uInt32>(aLvlProps.getLength()) - 1].Name = aPropNameSupplier.GetName( PROP_CHAR_STYLE_NAME );
607 aLvlProps[sal::static_int_cast<sal_uInt32>(aLvlProps.getLength()) - 1].Value <<= sStyle;
610 // Get the prefix / suffix / Parent numbering
611 // and add them to the level properties
612 OUString sText = pAbsLevel->GetBulletChar( );
613 // Inherit <w:lvlText> from the abstract level in case the override would be empty.
614 if (pLevel.get() && !pLevel->GetBulletChar().isEmpty())
615 sText = pLevel->GetBulletChar( );
617 OUString sPrefix;
618 OUString sSuffix;
619 OUString& rPrefix = sPrefix;
620 OUString& rSuffix = sSuffix;
621 sal_Int16 nParentNum = ListLevel::GetParentNumbering(
622 sText, nLevel, rPrefix, rSuffix );
624 aLvlProps.realloc( aLvlProps.getLength( ) + 4 );
625 aLvlProps[sal::static_int_cast<sal_uInt32>(aLvlProps.getLength()) - 4] = MAKE_PROPVAL( PROP_PREFIX, rPrefix );
627 if (sText.isEmpty())
629 // Empty <w:lvlText>? Then put a Unicode "zero width space" as a suffix, so LabelFollowedBy is still shown, as in Word.
630 // With empty suffix, Writer does not show LabelFollowedBy, either.
631 comphelper::SequenceAsHashMap aMap(aLvlProps);
632 if (aMap.find("NumberingType") != aMap.end())
634 sal_Int16 nNumberFormat = aMap["NumberingType"].get<sal_Int16>();
636 // No need for a zero width space without a real LabelFollowedBy.
637 bool bLabelFollowedBy = true;
638 auto it = std::find_if(aLvlProps.begin(), aLvlProps.end(), [](const beans::PropertyValue& rValue) { return rValue.Name == "LabelFollowedBy"; });
639 if (it != aLvlProps.end())
641 sal_Int16 nValue;
642 if (it->Value >>= nValue)
643 bLabelFollowedBy = nValue != SvxNumberFormat::NOTHING;
646 if (bLabelFollowedBy && nNumberFormat == style::NumberingType::NUMBER_NONE)
647 rSuffix = OUString(static_cast<sal_Unicode>(0x200B));
651 aLvlProps[sal::static_int_cast<sal_uInt32>(aLvlProps.getLength()) - 3] = MAKE_PROPVAL( PROP_SUFFIX, rSuffix );
652 aLvlProps[sal::static_int_cast<sal_uInt32>(aLvlProps.getLength()) - 2] = MAKE_PROPVAL( PROP_PARENT_NUMBERING, nParentNum );
654 aLvlProps[sal::static_int_cast<sal_uInt32>(aLvlProps.getLength()) - 1] = MAKE_PROPVAL( PROP_POSITION_AND_SPACE_MODE,
655 sal_Int16( text::PositionAndSpaceMode::LABEL_ALIGNMENT ) );
658 // Replace the numbering rules for the level
659 m_xNumRules->replaceByIndex( nLevel, uno::makeAny( aLvlProps ) );
661 // Handle the outline level here
662 if ( pAbsLevel->isOutlineNumbering())
664 uno::Reference< text::XChapterNumberingSupplier > xOutlines (
665 xFactory, uno::UNO_QUERY_THROW );
666 uno::Reference< container::XIndexReplace > xOutlineRules =
667 xOutlines->getChapterNumberingRules( );
669 StyleSheetEntryPtr pParaStyle = pAbsLevel->GetParaStyle( );
670 aLvlProps.realloc( aLvlProps.getLength() + 1 );
671 aLvlProps[sal::static_int_cast<sal_uInt32>(aLvlProps.getLength()) - 1] = MAKE_PROPVAL( PROP_HEADING_STYLE_NAME,
672 pParaStyle->sConvertedStyleName );
674 xOutlineRules->replaceByIndex( nLevel, uno::makeAny( aLvlProps ) );
677 nLevel++;
680 // Create the numbering style for these rules
681 OUString sNumRulesName = aPropNameSupplier.GetName( PROP_NUMBERING_RULES );
682 xStyle->setPropertyValue( sNumRulesName, uno::makeAny( m_xNumRules ) );
684 catch( const lang::IllegalArgumentException& e )
686 SAL_WARN( "writerfilter", "Exception: " << e.Message );
687 assert( !"Incorrect argument to UNO call" );
689 catch( const uno::RuntimeException& e )
691 SAL_WARN( "writerfilter", "Exception: " << e.Message );
692 assert( !"Incorrect argument to UNO call" );
694 catch( const uno::Exception& e )
696 SAL_WARN( "writerfilter", "Exception: " << e.Message );
702 //------------------------------------- NumberingManager implementation
705 ListsManager::ListsManager(DomainMapper& rDMapper,
706 const uno::Reference<lang::XMultiServiceFactory> & xFactory)
707 : LoggedProperties("ListsManager")
708 , LoggedTable("ListsManager")
709 , m_rDMapper(rDMapper)
710 , m_xFactory(xFactory)
711 , m_bIsLFOImport(false)
715 ListsManager::~ListsManager( )
717 DisposeNumPicBullets();
720 void ListsManager::DisposeNumPicBullets( )
722 uno::Reference<drawing::XShape> xShape;
723 for (std::vector<NumPicBullet::Pointer>::iterator it = m_aNumPicBullets.begin(); it != m_aNumPicBullets.end(); ++it)
725 xShape = (*it)->GetShape();
726 if (xShape.is())
728 uno::Reference<lang::XComponent> xShapeComponent(xShape, uno::UNO_QUERY);
729 xShapeComponent->dispose();
734 void ListsManager::lcl_attribute( Id nName, Value& rVal )
736 ListLevel::Pointer pCurrentLvl;
738 if (nName != NS_ooxml::LN_CT_NumPicBullet_numPicBulletId)
740 OSL_ENSURE( m_pCurrentDefinition.get(), "current entry has to be set here");
741 if(!m_pCurrentDefinition.get())
742 return ;
743 pCurrentLvl = m_pCurrentDefinition->GetCurrentLevel( );
745 else
747 SAL_WARN_IF(!m_pCurrentNumPicBullet.get(), "writerfilter", "current entry has to be set here");
748 if (!m_pCurrentNumPicBullet.get())
749 return;
751 int nIntValue = rVal.getInt();
755 switch(nName)
757 case NS_ooxml::LN_CT_LevelText_val:
759 //this strings contains the definition of the level
760 //the level number is marked as %n
761 //these numbers can be mixed randomly together with separators pre- and suffixes
762 //the Writer supports only a number of upper levels to show, separators is always a dot
763 //and each level can have a prefix and a suffix
764 if(pCurrentLvl.get())
765 pCurrentLvl->SetBulletChar( rVal.getString() );
767 break;
768 case NS_ooxml::LN_CT_Lvl_start:
769 case NS_ooxml::LN_CT_Lvl_numFmt:
770 case NS_ooxml::LN_CT_Lvl_isLgl:
771 case NS_ooxml::LN_CT_Lvl_legacy:
772 if ( pCurrentLvl.get( ) )
773 pCurrentLvl->SetValue( nName, sal_Int32( nIntValue ) );
774 break;
775 case NS_ooxml::LN_CT_Num_numId:
776 m_pCurrentDefinition->SetId( rVal.getString().toInt32( ) );
777 break;
778 case NS_ooxml::LN_CT_AbstractNum_nsid:
779 m_pCurrentDefinition->SetId( nIntValue );
780 break;
781 case NS_ooxml::LN_CT_AbstractNum_tmpl:
782 m_pCurrentDefinition->SetValue( nName, nIntValue );
783 break;
784 case NS_ooxml::LN_CT_NumLvl_ilvl:
786 //add a new level to the level vector and make it the current one
787 m_pCurrentDefinition->AddLevel();
789 writerfilter::Reference<Properties>::Pointer_t pProperties;
790 if((pProperties = rVal.getProperties()).get())
791 pProperties->resolve(*this);
793 break;
794 case NS_ooxml::LN_CT_AbstractNum_abstractNumId:
796 // This one corresponds to the AbstractNum Id definition
797 // The reference to the abstract num is in the sprm method
798 sal_Int32 nVal = rVal.getString().toInt32();
799 m_pCurrentDefinition->SetId( nVal );
801 break;
802 case NS_ooxml::LN_CT_Ind_left:
803 pCurrentLvl->Insert(
804 PROP_INDENT_AT, uno::makeAny( ConversionHelper::convertTwipToMM100( nIntValue ) ));
805 break;
806 case NS_ooxml::LN_CT_Ind_hanging:
807 pCurrentLvl->Insert(
808 PROP_FIRST_LINE_INDENT, uno::makeAny( - ConversionHelper::convertTwipToMM100( nIntValue ) ));
809 break;
810 case NS_ooxml::LN_CT_Ind_firstLine:
811 pCurrentLvl->Insert(
812 PROP_FIRST_LINE_INDENT, uno::makeAny( ConversionHelper::convertTwipToMM100( nIntValue ) ));
813 break;
814 case NS_ooxml::LN_CT_Lvl_ilvl: //overrides previous level - unsupported
815 case NS_ooxml::LN_CT_Lvl_tplc: //template code - unsupported
816 case NS_ooxml::LN_CT_Lvl_tentative: //marks level as unused in the document - unsupported
817 break;
818 case NS_ooxml::LN_CT_TabStop_pos:
820 //no paragraph attributes in ListTable char style sheets
821 if ( pCurrentLvl.get( ) )
822 pCurrentLvl->SetValue( nName,
823 ConversionHelper::convertTwipToMM100( nIntValue ) );
825 break;
826 case NS_ooxml::LN_CT_TabStop_val:
828 // TODO Do something of that
830 break;
831 case NS_ooxml::LN_CT_NumPicBullet_numPicBulletId:
832 m_pCurrentNumPicBullet->SetId(rVal.getString().toInt32());
833 break;
834 default:
835 SAL_WARN("writerfilter", "ListsManager::lcl_attribute: unhandled token: " << nName);
839 void ListsManager::lcl_sprm( Sprm& rSprm )
841 //fill the attributes of the style sheet
842 sal_uInt32 nSprmId = rSprm.getId();
843 if( m_pCurrentDefinition.get() ||
844 nSprmId == NS_ooxml::LN_CT_Numbering_abstractNum ||
845 nSprmId == NS_ooxml::LN_CT_Numbering_num ||
846 (nSprmId == NS_ooxml::LN_CT_NumPicBullet_pict && m_pCurrentNumPicBullet.get()) ||
847 nSprmId == NS_ooxml::LN_CT_Numbering_numPicBullet)
849 static bool bIsStartVisited = false;
850 sal_Int32 nIntValue = rSprm.getValue()->getInt();
851 switch( nSprmId )
853 case NS_ooxml::LN_CT_Numbering_abstractNum:
855 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
856 if(pProperties.get())
858 //create a new Abstract list entry
859 OSL_ENSURE( !m_pCurrentDefinition.get(), "current entry has to be NULL here");
860 m_pCurrentDefinition.reset( new AbstractListDef );
861 pProperties->resolve( *this );
862 //append it to the table
863 m_aAbstractLists.push_back( m_pCurrentDefinition );
864 m_pCurrentDefinition = AbstractListDef::Pointer();
867 break;
868 case NS_ooxml::LN_CT_Numbering_num:
870 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
871 if(pProperties.get())
873 // Create a new list entry
874 OSL_ENSURE( !m_pCurrentDefinition.get(), "current entry has to be NULL here");
875 ListDef::Pointer listDef( new ListDef );
876 m_pCurrentDefinition = listDef;
877 pProperties->resolve( *this );
878 //append it to the table
879 m_aLists.push_back( listDef );
881 m_pCurrentDefinition = AbstractListDef::Pointer();
884 break;
885 case NS_ooxml::LN_CT_Numbering_numPicBullet:
887 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
888 if (pProperties.get())
890 NumPicBullet::Pointer numPicBullet(new NumPicBullet());
891 m_pCurrentNumPicBullet = numPicBullet;
892 pProperties->resolve(*this);
893 m_aNumPicBullets.push_back(numPicBullet);
894 m_pCurrentNumPicBullet = NumPicBullet::Pointer();
897 break;
898 case NS_ooxml::LN_CT_NumPicBullet_pict:
900 uno::Reference<drawing::XShape> xShape = m_rDMapper.PopPendingShape();
902 // Respect only the aspect ratio of the picture, not its size.
903 awt::Size aPrefSize = xShape->getSize();
904 // See SwDefBulletConfig::InitFont(), default height is 14.
905 const int nFontHeight = 14;
906 // Point -> mm100.
907 const int nHeight = nFontHeight * 35;
908 if (aPrefSize.Height * aPrefSize.Width != 0)
910 int nWidth = (nHeight * aPrefSize.Width) / aPrefSize.Height;
911 awt::Size aSize(nWidth, nHeight);
912 xShape->setSize(aSize);
915 m_pCurrentNumPicBullet->SetShape(xShape);
917 break;
918 case NS_ooxml::LN_CT_Lvl_lvlPicBulletId:
920 uno::Reference<drawing::XShape> xShape;
921 for (std::vector<NumPicBullet::Pointer>::iterator it = m_aNumPicBullets.begin(); it != m_aNumPicBullets.end(); ++it)
923 if ((*it)->GetId() == nIntValue)
925 xShape = (*it)->GetShape();
926 break;
929 if (xShape.is())
931 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
934 uno::Any aAny = xPropertySet->getPropertyValue("GraphicURL");
935 if (aAny.has<OUString>())
936 m_pCurrentDefinition->GetCurrentLevel()->SetGraphicURL(aAny.get<OUString>());
937 } catch(const beans::UnknownPropertyException&)
941 uno::Reference< graphic::XGraphic > gr;
942 xPropertySet->getPropertyValue("Bitmap") >>= gr;
943 m_pCurrentDefinition->GetCurrentLevel()->SetGraphicBitmap( gr );
944 } catch(const beans::UnknownPropertyException&)
947 // Now that we saved the URL of the graphic, remove it from the document.
948 uno::Reference<lang::XComponent> xShapeComponent(xShape, uno::UNO_QUERY);
949 xShapeComponent->dispose();
952 break;
953 case NS_ooxml::LN_CT_Num_abstractNumId:
955 sal_Int32 nAbstractNumId = rSprm.getValue()->getInt();
956 ListDef* pListDef = dynamic_cast< ListDef* >( m_pCurrentDefinition.get( ) );
957 if ( pListDef != nullptr )
959 // The current def should be a ListDef
960 pListDef->SetAbstractDefinition(
961 GetAbstractList( nAbstractNumId ) );
964 break;
965 case NS_ooxml::LN_CT_AbstractNum_multiLevelType:
966 break;
967 case NS_ooxml::LN_CT_AbstractNum_tmpl:
968 m_pCurrentDefinition->SetValue( nSprmId, nIntValue );
969 break;
970 case NS_ooxml::LN_CT_AbstractNum_lvl:
972 m_pCurrentDefinition->AddLevel();
973 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
974 if(pProperties.get())
975 pProperties->resolve(*this);
977 break;
978 case NS_ooxml::LN_CT_Lvl_start:
979 if (m_pCurrentDefinition->GetCurrentLevel().get())
980 m_pCurrentDefinition->GetCurrentLevel( )->SetValue( nSprmId, nIntValue );
981 bIsStartVisited = true;
982 break;
983 case NS_ooxml::LN_CT_Lvl_numFmt:
984 case NS_ooxml::LN_CT_Lvl_isLgl:
985 case NS_ooxml::LN_CT_Lvl_legacy:
986 if (m_pCurrentDefinition->GetCurrentLevel().get())
988 m_pCurrentDefinition->GetCurrentLevel( )->SetValue( nSprmId, nIntValue );
989 if( !bIsStartVisited )
991 m_pCurrentDefinition->GetCurrentLevel( )->SetValue( NS_ooxml::LN_CT_Lvl_start, 0 );
992 bIsStartVisited = true;
995 break;
996 case NS_ooxml::LN_CT_Lvl_suff:
998 if (m_pCurrentDefinition->GetCurrentLevel().get())
1000 SvxNumberFormat::LabelFollowedBy value = SvxNumberFormat::LISTTAB;
1001 if( rSprm.getValue()->getString() == "tab" )
1002 value = SvxNumberFormat::LISTTAB;
1003 else if( rSprm.getValue()->getString() == "space" )
1004 value = SvxNumberFormat::SPACE;
1005 else if( rSprm.getValue()->getString() == "nothing" )
1006 value = SvxNumberFormat::NOTHING;
1007 else
1008 SAL_WARN( "writerfilter", "Unknown ST_LevelSuffix value "
1009 << rSprm.getValue()->getString());
1010 m_pCurrentDefinition->GetCurrentLevel()->SetValue( nSprmId, value );
1013 break;
1014 case NS_ooxml::LN_CT_Lvl_lvlText:
1015 case NS_ooxml::LN_CT_Lvl_rPr : //contains LN_EG_RPrBase_rFonts
1017 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
1018 if(pProperties.get())
1019 pProperties->resolve(*this);
1021 break;
1022 case NS_ooxml::LN_CT_NumLvl_lvl:
1024 // overwrite level
1025 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
1026 if(pProperties.get())
1027 pProperties->resolve(*this);
1029 break;
1030 case NS_ooxml::LN_CT_Lvl_lvlJc:
1032 sal_Int16 nValue = 0;
1033 switch (nIntValue)
1035 case NS_ooxml::LN_Value_ST_Jc_left:
1036 case NS_ooxml::LN_Value_ST_Jc_start:
1037 nValue = text::HoriOrientation::LEFT;
1038 break;
1039 case NS_ooxml::LN_Value_ST_Jc_center:
1040 nValue = text::HoriOrientation::CENTER;
1041 break;
1042 case NS_ooxml::LN_Value_ST_Jc_right:
1043 case NS_ooxml::LN_Value_ST_Jc_end:
1044 nValue = text::HoriOrientation::RIGHT;
1045 break;
1047 m_pCurrentDefinition->GetCurrentLevel( )->Insert(
1048 PROP_ADJUST, uno::makeAny( nValue ) );
1049 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
1051 break;
1052 case NS_ooxml::LN_CT_Lvl_pPr:
1053 case NS_ooxml::LN_CT_PPrBase_ind:
1055 //todo: how to handle paragraph properties within numbering levels (except LeftIndent and FirstLineIndent)?
1056 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
1057 if(pProperties.get())
1058 pProperties->resolve(*this);
1060 break;
1061 case NS_ooxml::LN_CT_PPrBase_tabs:
1062 case NS_ooxml::LN_CT_Tabs_tab:
1064 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
1065 if(pProperties.get())
1066 pProperties->resolve(*this);
1068 break;
1069 case NS_ooxml::LN_CT_Lvl_pStyle:
1071 OUString sStyleName = rSprm.getValue( )->getString( );
1072 ListLevel::Pointer pLevel = m_pCurrentDefinition->GetCurrentLevel( );
1073 StyleSheetTablePtr pStylesTable = m_rDMapper.GetStyleSheetTable( );
1074 const StyleSheetEntryPtr pStyle = pStylesTable->FindStyleSheetByISTD( sStyleName );
1075 pLevel->SetParaStyle( pStyle );
1077 break;
1078 case NS_ooxml::LN_CT_Num_lvlOverride:
1080 writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
1081 if (pProperties.get())
1082 pProperties->resolve(*this);
1084 break;
1085 case NS_ooxml::LN_CT_NumLvl_startOverride:
1087 if(m_pCurrentDefinition)
1089 if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
1090 // <w:num> -> <w:lvlOverride> -> <w:startOverride> is the non-abstract equivalent of
1091 // <w:abstractNum> -> <w:lvl> -> <w:start>
1092 pCurrentLevel->SetValue(NS_ooxml::LN_CT_Lvl_start, nIntValue);
1095 break;
1096 case NS_ooxml::LN_CT_AbstractNum_numStyleLink:
1098 OUString sStyleName = rSprm.getValue( )->getString( );
1099 AbstractListDef* pAbstractListDef = dynamic_cast< AbstractListDef* >( m_pCurrentDefinition.get( ) );
1100 pAbstractListDef->SetNumStyleLink(sStyleName);
1102 break;
1103 case NS_ooxml::LN_EG_RPrBase_rFonts: //contains font properties
1104 case NS_ooxml::LN_EG_RPrBase_color:
1105 case NS_ooxml::LN_EG_RPrBase_u:
1106 case NS_ooxml::LN_EG_RPrBase_sz:
1107 case NS_ooxml::LN_EG_RPrBase_lang:
1108 case NS_ooxml::LN_EG_RPrBase_eastAsianLayout:
1109 //no break!
1110 default:
1111 if( m_pCurrentDefinition->GetCurrentLevel( ).get())
1113 m_rDMapper.PushListProperties( m_pCurrentDefinition->GetCurrentLevel( ) );
1114 m_rDMapper.sprm( rSprm );
1115 m_rDMapper.PopListProperties();
1121 void ListsManager::lcl_entry( int /* pos */,
1122 writerfilter::Reference<Properties>::Pointer_t ref )
1124 if( m_rDMapper.IsOOXMLImport() || m_rDMapper.IsRTFImport() )
1126 ref->resolve(*this);
1128 else
1130 if ( m_bIsLFOImport )
1132 // Create ListDef's
1133 OSL_ENSURE( !m_pCurrentDefinition.get(), "current entry has to be NULL here");
1134 ListDef::Pointer pList( new ListDef() );
1135 m_pCurrentDefinition = pList;
1136 ref->resolve(*this);
1137 //append it to the table
1138 m_aLists.push_back( pList );
1139 m_pCurrentDefinition = AbstractListDef::Pointer();
1141 else
1143 // Create AbstractListDef's
1144 OSL_ENSURE( !m_pCurrentDefinition.get(), "current entry has to be NULL here");
1145 m_pCurrentDefinition.reset( new AbstractListDef( ) );
1146 ref->resolve(*this);
1147 //append it to the table
1148 m_aAbstractLists.push_back( m_pCurrentDefinition );
1149 m_pCurrentDefinition = AbstractListDef::Pointer();
1154 AbstractListDef::Pointer ListsManager::GetAbstractList( sal_Int32 nId )
1156 AbstractListDef::Pointer pAbstractList;
1158 int nLen = m_aAbstractLists.size( );
1159 int i = 0;
1160 while ( !pAbstractList.get( ) && i < nLen )
1162 if ( m_aAbstractLists[i]->GetId( ) == nId )
1164 if ( m_aAbstractLists[i]->GetNumStyleLink().getLength() > 0 )
1166 // If the abstract num has a style linked, check the linked style's number id.
1167 StyleSheetTablePtr pStylesTable = m_rDMapper.GetStyleSheetTable( );
1169 const StyleSheetEntryPtr pStyleSheetEntry =
1170 pStylesTable->FindStyleSheetByISTD( m_aAbstractLists[i]->GetNumStyleLink() );
1172 const StyleSheetPropertyMap* pStyleSheetProperties =
1173 dynamic_cast<const StyleSheetPropertyMap*>(pStyleSheetEntry ? pStyleSheetEntry->pProperties.get() : nullptr);
1175 if( pStyleSheetProperties && pStyleSheetProperties->GetNumId() >= 0 )
1177 ListDef::Pointer pList = GetList( pStyleSheetProperties->GetNumId() );
1178 if ( pList!=nullptr )
1179 return pList->GetAbstractDefinition();
1180 else
1181 pAbstractList = m_aAbstractLists[i];
1185 else
1187 pAbstractList = m_aAbstractLists[i];
1190 i++;
1193 return pAbstractList;
1196 ListDef::Pointer ListsManager::GetList( sal_Int32 nId )
1198 ListDef::Pointer pList;
1200 int nLen = m_aLists.size( );
1201 int i = 0;
1202 while ( !pList.get( ) && i < nLen )
1204 if ( m_aLists[i]->GetId( ) == nId )
1205 pList = m_aLists[i];
1206 i++;
1209 return pList;
1212 void ListsManager::CreateNumberingRules( )
1214 // Loop over the definitions
1215 std::vector< ListDef::Pointer >::iterator listIt = m_aLists.begin( );
1216 for ( ; listIt != m_aLists.end( ); ++listIt )
1218 (*listIt)->CreateNumberingRules( m_rDMapper, m_xFactory );
1224 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */