1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include "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
{
50 //--------------------------------------------------- Utility functions
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
)
60 sal_Int32 nLen
= aProps
.getLength( );
63 while ( nPos
== -1 && i
< nLen
)
65 if ( aProps
[i
].Name
== sName
)
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
);
83 // Replace the property value by the one in aSrc
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
)
100 case NS_ooxml::LN_CT_Lvl_start
:
101 m_nIStartAt
= nValue
;
103 case NS_ooxml::LN_CT_Lvl_numFmt
:
106 case NS_ooxml::LN_CT_Lvl_isLgl
:
108 case NS_ooxml::LN_CT_Lvl_legacy
:
110 case NS_ooxml::LN_CT_Lvl_suff
:
111 m_nXChFollow
= nValue
;
113 case NS_ooxml::LN_CT_TabStop_pos
:
116 SAL_INFO("writerfilter",
117 "unsupported list tab stop position " << nValue
);
123 OSL_FAIL( "this line should never be reached");
128 bool ListLevel::HasValues() const
133 void ListLevel::SetParaStyle( const tools::SvRef
< StyleSheetEntry
>& pStyle
)
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
);
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;
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 );
182 nFound
= sLevelText
.indexOf( '%', nCurrentIndex
);
183 //remove the text before the next %
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
);
201 AddParaProperties( &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
))
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
);
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)));
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:
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
);
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(
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()
365 NumPicBullet::~NumPicBullet()
369 void NumPicBullet::SetId(sal_Int32 nId
)
374 void NumPicBullet::SetShape(uno::Reference
<drawing::XShape
> const& xShape
)
380 //--------------------------------------- AbstractListDef implementation
382 AbstractListDef::AbstractListDef( ) :
387 AbstractListDef::~AbstractListDef( )
391 void AbstractListDef::SetValue( sal_uInt32 nSprmId
)
395 case NS_ooxml::LN_CT_AbstractNum_tmpl
:
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
];
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
);
431 //---------------------------------------------- ListDef implementation
433 ListDef::ListDef( ) : AbstractListDef( )
441 OUString
ListDef::GetStyleName( sal_Int32 nId
)
443 OUString
sStyleName( "WWNum" );
444 sStyleName
+= OUString::number( nId
);
449 uno::Sequence
<uno::Sequence
<beans::PropertyValue
>> ListDef::GetMergedPropertyValues()
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
] );
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");
490 catch ( const uno::Exception
& )
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( );
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( );
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
));
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())
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
)));
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();
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())
690 pCurrentLvl
= m_pCurrentDefinition
->GetCurrentLevel( );
694 SAL_WARN_IF(!m_pCurrentNumPicBullet
.get(), "writerfilter", "current entry has to be set here");
695 if (!m_pCurrentNumPicBullet
.get())
698 int nIntValue
= rVal
.getInt();
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.
716 pCurrentLvl
->SetBulletChar( rVal
.getString().replace( 0xad, 0x2d ) );
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
) );
727 case NS_ooxml::LN_CT_Num_numId
:
728 m_pCurrentDefinition
->SetId( rVal
.getString().toInt32( ) );
730 case NS_ooxml::LN_CT_AbstractNum_nsid
:
731 m_pCurrentDefinition
->SetId( nIntValue
);
733 case NS_ooxml::LN_CT_AbstractNum_tmpl
:
734 AbstractListDef::SetValue( nName
);
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);
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
);
754 case NS_ooxml::LN_CT_Ind_start
:
755 case NS_ooxml::LN_CT_Ind_left
:
756 if ( pCurrentLvl
.get( ) )
758 PROP_INDENT_AT
, uno::makeAny( ConversionHelper::convertTwipToMM100( nIntValue
) ));
760 case NS_ooxml::LN_CT_Ind_hanging
:
761 if ( pCurrentLvl
.get( ) )
763 PROP_FIRST_LINE_INDENT
, uno::makeAny( - ConversionHelper::convertTwipToMM100( nIntValue
) ));
765 case NS_ooxml::LN_CT_Ind_firstLine
:
766 if ( pCurrentLvl
.get( ) )
768 PROP_FIRST_LINE_INDENT
, uno::makeAny( ConversionHelper::convertTwipToMM100( nIntValue
) ));
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
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
) );
782 case NS_ooxml::LN_CT_TabStop_val
:
784 // TODO Do something of that
787 case NS_ooxml::LN_CT_NumPicBullet_numPicBulletId
:
788 m_pCurrentNumPicBullet
->SetId(rVal
.getString().toInt32());
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();
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();
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();
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();
854 case NS_ooxml::LN_CT_NumPicBullet_pict
:
856 uno::Reference
<drawing::XShape
> xShape
= m_rDMapper
.PopPendingShape();
858 m_pCurrentNumPicBullet
->SetShape(xShape
);
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();
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
>>();
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;
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
);
907 awt::Size
aSize( convertMm100ToTwip(aPrefSize
.Width
), convertMm100ToTwip(aPrefSize
.Height
) );
908 pCurrentLevel
->SetGraphicSize( aSize
);
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
) );
925 case NS_ooxml::LN_CT_AbstractNum_multiLevelType
:
927 case NS_ooxml::LN_CT_AbstractNum_tmpl
:
928 AbstractListDef::SetValue( nSprmId
);
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);
938 case NS_ooxml::LN_CT_Lvl_start
:
939 if (ListLevel::Pointer pCurrentLevel
= m_pCurrentDefinition
->GetCurrentLevel())
940 pCurrentLevel
->SetValue( nSprmId
, nIntValue
);
941 bIsStartVisited
= true;
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;
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
;
968 SAL_WARN( "writerfilter", "Unknown ST_LevelSuffix value "
969 << rSprm
.getValue()->getString());
970 pCurrentLevel
->SetValue( nSprmId
, value
);
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);
982 case NS_ooxml::LN_CT_NumLvl_lvl
:
985 writerfilter::Reference
<Properties
>::Pointer_t pProperties
= rSprm
.getProps();
986 if(pProperties
.get())
987 pProperties
->resolve(*this);
990 case NS_ooxml::LN_CT_Lvl_lvlJc
:
992 sal_Int16 nValue
= text::HoriOrientation::NONE
;
995 case NS_ooxml::LN_Value_ST_Jc_left
:
996 case NS_ooxml::LN_Value_ST_Jc_start
:
997 nValue
= text::HoriOrientation::LEFT
;
999 case NS_ooxml::LN_Value_ST_Jc_center
:
1000 nValue
= text::HoriOrientation::CENTER
;
1002 case NS_ooxml::LN_Value_ST_Jc_right
:
1003 case NS_ooxml::LN_Value_ST_Jc_end
:
1004 nValue
= text::HoriOrientation::RIGHT
;
1008 if (nValue
!= text::HoriOrientation::NONE
)
1010 if (ListLevel::Pointer pLevel
= m_pCurrentDefinition
->GetCurrentLevel())
1013 PROP_ADJUST
, uno::makeAny( nValue
) );
1014 writerfilter::Reference
<Properties
>::Pointer_t pProperties
= rSprm
.getProps();
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);
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);
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
);
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);
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
);
1067 case NS_ooxml::LN_CT_AbstractNum_numStyleLink
:
1069 OUString sStyleName
= rSprm
.getValue( )->getString( );
1070 m_pCurrentDefinition
->SetNumStyleLink(sStyleName
);
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
:
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);
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( );
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();
1137 pAbstractList
= m_aAbstractLists
[i
];
1143 pAbstractList
= m_aAbstractLists
[i
];
1149 return pAbstractList
;
1152 ListDef::Pointer
ListsManager::GetList( sal_Int32 nId
)
1154 ListDef::Pointer pList
;
1156 int nLen
= m_aLists
.size( );
1158 while ( !pList
.get( ) && i
< nLen
)
1160 if ( m_aLists
[i
]->GetId( ) == nId
)
1161 pList
= m_aLists
[i
];
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: */