LibreOffice Module writerfilter (master) 1
NumberingManager.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21
22#include "ConversionHelper.hxx"
23#include "NumberingManager.hxx"
24#include "StyleSheetTable.hxx"
25#include "PropertyIds.hxx"
26
27#include <ooxml/resourceids.hxx>
28
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>
38
39#include <osl/diagnose.h>
40#include <rtl/ustring.hxx>
41#include <sal/log.hxx>
46#include <comphelper/string.hxx>
47#include <regex>
48#include <utility>
49
50using namespace com::sun::star;
51
52namespace writerfilter::dmapper {
53
54//--------------------------------------------------- Utility functions
55template <typename T>
56static beans::PropertyValue lcl_makePropVal(PropertyIds nNameID, T const & aValue)
57{
58 return comphelper::makePropertyValue(getPropertyName(nNameID), aValue);
59}
60
61static sal_Int32 lcl_findProperty( const uno::Sequence< beans::PropertyValue >& aProps, std::u16string_view sName )
62{
63 sal_Int32 i = 0;
64 sal_Int32 nLen = aProps.getLength( );
65 sal_Int32 nPos = -1;
66
67 while ( nPos == -1 && i < nLen )
68 {
69 if ( aProps[i].Name == sName )
70 nPos = i;
71 else
72 i++;
73 }
74
75 return nPos;
76}
77
80{
81 for ( const auto& rProp : aSrc )
82 {
83 // Look for the same property in aDst
84 sal_Int32 nPos = lcl_findProperty( aDst, rProp.Name );
85 if ( nPos >= 0 )
86 {
87 // Replace the property value by the one in aSrc
88 aDst.getArray()[nPos] = rProp;
89 }
90 else
91 {
92 // Simply add the new value
93 aDst.realloc( aDst.getLength( ) + 1 );
94 aDst.getArray()[ aDst.getLength( ) - 1 ] = rProp;
95 }
96 }
97}
98
99//-------------------------------------------- ListLevel implementation
100void ListLevel::SetValue( Id nId, sal_Int32 nValue )
101{
102 switch( nId )
103 {
104 case NS_ooxml::LN_CT_Lvl_start:
106 break;
107 case NS_ooxml::LN_CT_NumLvl_startOverride:
109 break;
110 case NS_ooxml::LN_CT_NumFmt_val:
111 m_nNFC = nValue;
112 break;
113 case NS_ooxml::LN_CT_Lvl_isLgl:
114 m_bIsLegal = true;
115 break;
116 case NS_ooxml::LN_CT_Lvl_legacy:
117 break;
118 case NS_ooxml::LN_CT_Lvl_suff:
120 break;
121 case NS_ooxml::LN_CT_TabStop_pos:
122 if (nValue < 0)
123 {
124 SAL_INFO("writerfilter",
125 "unsupported list tab stop position " << nValue);
126 }
127 else
129 break;
130 default:
131 OSL_FAIL( "this line should never be reached");
132 }
133 m_bHasValues = true;
134}
135
136void ListLevel::SetCustomNumberFormat(const OUString& rValue) { m_aCustomNumberFormat = rValue; }
137
138sal_Int16 ListLevel::GetNumberingType(sal_Int16 nDefault) const
139{
141}
142
144{
145 return m_bHasValues;
146}
147
149{
150 if (!pStyle)
151 return;
152 m_pParaStyle = pStyle;
153}
154
156{
158 if (m_pParaStyle)
159 AddParaProperties( &aLevelProps );
160 return aLevelProps;
161}
162
163static bool IgnoreForCharStyle(std::u16string_view aStr, const bool bIsSymbol)
164{
165 //Names found in PropertyIds.cxx, Lines 56-396
166 return (aStr==u"Adjust" || aStr==u"IndentAt" || aStr==u"FirstLineIndent"
167 || aStr==u"FirstLineOffset" || aStr==u"LeftMargin"
168 // We need font names when they are different for the bullet and for the text.
169 // But leave symbols alone, we only want to keep the font style for letters and numbers.
170 || (bIsSymbol && aStr==u"CharFontName")
171 );
172}
174{
175 PropertyValueVector_t rProperties;
176
177 const uno::Sequence< beans::PropertyValue > vPropVals = PropertyMap::GetPropertyValues();
178 const bool bIsSymbol(GetBulletChar().getLength() <= 1);
179 for( const auto& rPropNal : vPropVals )
180 if (! IgnoreForCharStyle(rPropNal.Name, bIsSymbol))
181 rProperties.emplace_back(rPropNal.Name, 0, rPropNal.Value, beans::PropertyState_DIRECT_VALUE);
182
183 return comphelper::containerToSequence(rProperties);
184}
185
187{
188 std::vector<beans::PropertyValue> aNumberingProperties;
189
190 if (m_nIStartAt >= 0)
191 aNumberingProperties.push_back(lcl_makePropVal<sal_Int16>(PROP_START_WITH, m_nIStartAt) );
192 else if (bDefaults)
193 aNumberingProperties.push_back(lcl_makePropVal<sal_Int16>(PROP_START_WITH, 0));
194
195 sal_Int16 nNumberFormat = -1;
196 if (m_nNFC == NS_ooxml::LN_Value_ST_NumberFormat_custom)
197 {
199 }
200 else
201 {
203 }
204 if( m_nNFC >= 0)
205 {
206 if (m_xGraphicBitmap.is())
207 nNumberFormat = style::NumberingType::BITMAP;
208 aNumberingProperties.push_back(lcl_makePropVal(PROP_NUMBERING_TYPE, nNumberFormat));
209 }
210
211 // todo: this is not the bullet char
212 if( nNumberFormat == style::NumberingType::CHAR_SPECIAL )
213 {
214 if (!GetBulletChar().isEmpty())
215 {
216 aNumberingProperties.push_back(lcl_makePropVal(PROP_BULLET_CHAR, m_sBulletChar->copy(0, 1)));
217 }
218 else
219 {
220 // If w:lvlText's value is null - set bullet char to zero.
221 aNumberingProperties.push_back(lcl_makePropVal<sal_Unicode>(PROP_BULLET_CHAR, 0));
222 }
223 }
224 if (m_xGraphicBitmap.is())
225 {
226 aNumberingProperties.push_back(lcl_makePropVal(PROP_GRAPHIC_BITMAP, m_xGraphicBitmap));
227 aNumberingProperties.push_back(lcl_makePropVal(PROP_GRAPHIC_SIZE, m_aGraphicSize));
228 }
229
230 if (m_nTabstop.has_value())
231 aNumberingProperties.push_back(lcl_makePropVal(PROP_LISTTAB_STOP_POSITION, *m_nTabstop));
232 else if (bDefaults)
233 aNumberingProperties.push_back(lcl_makePropVal<sal_Int16>(PROP_LISTTAB_STOP_POSITION, 0));
234
235 //TODO: handling of nFLegal?
236 //TODO: nFNoRestart lower levels do not restart when higher levels are incremented, like:
237 //1.
238 //1.1
239 //2.2
240 //2.3
241 //3.4
242
243// TODO: sRGBXchNums; array of inherited numbers
244
245// nXChFollow; following character 0 - tab, 1 - space, 2 - nothing
246 if (bDefaults || m_nXChFollow != SvxNumberFormat::LISTTAB)
247 aNumberingProperties.push_back(lcl_makePropVal(PROP_LEVEL_FOLLOW, m_nXChFollow));
248
249 PropertyIds const aReadIds[] =
250 {
253 };
254 for(PropertyIds const & rReadId : aReadIds) {
255 std::optional<PropertyMap::Property> aProp = getProperty(rReadId);
256 if (aProp)
257 aNumberingProperties.emplace_back( getPropertyName(aProp->first), 0, aProp->second, beans::PropertyState_DIRECT_VALUE );
258 else if (rReadId == PROP_FIRST_LINE_INDENT && bDefaults)
259 // Writer default is -360 twips, Word default seems to be 0.
260 aNumberingProperties.emplace_back("FirstLineIndent", 0, uno::Any(static_cast<sal_Int32>(0)), beans::PropertyState_DIRECT_VALUE);
261 else if (rReadId == PROP_INDENT_AT && bDefaults)
262 // Writer default is 720 twips, Word default seems to be 0.
263 aNumberingProperties.emplace_back("IndentAt", 0,
264 uno::Any(static_cast<sal_Int32>(0)),
265 beans::PropertyState_DIRECT_VALUE);
266 }
267
268 std::optional<PropertyMap::Property> aPropFont = getProperty(PROP_CHAR_FONT_NAME);
269 if (aPropFont)
270 aNumberingProperties.emplace_back( getPropertyName(PROP_BULLET_FONT_NAME), 0, aPropFont->second, beans::PropertyState_DIRECT_VALUE );
271
272 if (m_bIsLegal)
273 aNumberingProperties.push_back(lcl_makePropVal(PROP_LEVEL_IS_LEGAL, true));
274
275 return comphelper::containerToSequence(aNumberingProperties);
276}
277
278// Add the properties only if they do not already exist in the sequence.
280{
282
283 const OUString & sFirstLineIndent = getPropertyName(
285 const OUString & sIndentAt = getPropertyName(
287
288 bool hasFirstLineIndent = lcl_findProperty( aProps, sFirstLineIndent );
289 bool hasIndentAt = lcl_findProperty( aProps, sIndentAt );
290
291 if( hasFirstLineIndent && hasIndentAt )
292 return; // has them all, nothing to add
293
294 const uno::Sequence< beans::PropertyValue > aParaProps = m_pParaStyle->m_pProperties->GetPropertyValues( );
295
296 // ParaFirstLineIndent -> FirstLineIndent
297 // ParaLeftMargin -> IndentAt
298
299 const OUString & sParaIndent = getPropertyName(
301 const OUString & sParaLeftMargin = getPropertyName(
303
304 for ( const auto& rParaProp : aParaProps )
305 {
306 if ( !hasFirstLineIndent && rParaProp.Name == sParaIndent )
307 {
308 aProps.realloc( aProps.getLength() + 1 );
309 auto pProps = aProps.getArray();
310 pProps[aProps.getLength( ) - 1] = rParaProp;
311 pProps[aProps.getLength( ) - 1].Name = sFirstLineIndent;
312 }
313 else if ( !hasIndentAt && rParaProp.Name == sParaLeftMargin )
314 {
315 aProps.realloc( aProps.getLength() + 1 );
316 auto pProps = aProps.getArray();
317 pProps[aProps.getLength( ) - 1] = rParaProp;
318 pProps[aProps.getLength( ) - 1].Name = sIndentAt;
319 }
320
321 }
322}
323
325 : m_nId(0)
326{
327}
328
330{
331}
332
333void NumPicBullet::SetId(sal_Int32 nId)
334{
335 m_nId = nId;
336}
337
339{
340 m_xShape = xShape;
341}
342
343
344//--------------------------------------- AbstractListDef implementation
345
347 m_nId( -1 )
348{
349}
350
352{
353}
354
355void AbstractListDef::SetValue( sal_uInt32 nSprmId )
356{
357 switch( nSprmId )
358 {
359 case NS_ooxml::LN_CT_AbstractNum_tmpl:
360 break;
361 default:
362 OSL_FAIL( "this line should never be reached");
363 }
364}
365
367{
368 ListLevel::Pointer pLevel;
369 if ( m_aLevels.size( ) > nLvl )
370 pLevel = m_aLevels[ nLvl ];
371 return pLevel;
372}
373
374void AbstractListDef::AddLevel( sal_uInt16 nLvl )
375{
376 if ( nLvl >= m_aLevels.size() )
377 m_aLevels.resize( nLvl+1 );
378
379 if (!m_aLevels[nLvl])
380 {
381 m_aLevels[nLvl] = new ListLevel;
382 }
383
385}
386
388{
390 uno::Sequence< beans::PropertyValue >* aResult = result.getArray( );
391
392 int nLevels = m_aLevels.size( );
393 for ( int i = 0; i < nLevels; i++ )
394 {
395 if (m_aLevels[i])
396 aResult[i] = m_aLevels[i]->GetProperties(bDefaults);
397 }
398
399 return result;
400}
401
402const OUString& AbstractListDef::MapListId(OUString const& rId)
403{
404 if (!m_oListId)
405 {
406 m_oListId = rId;
407 }
408 return *m_oListId;
409}
410
411//---------------------------------------------- ListDef implementation
412
414{
415}
416
418{
419}
420
421const OUString & ListDef::GetStyleName(sal_Int32 const nId,
423{
424 if (xStyles.is())
425 {
426 OUString sStyleName = "WWNum" + OUString::number( nId );
427
428 while (xStyles->hasByName(sStyleName)) // unique
429 {
430 sStyleName += "a";
431 }
432
433 m_StyleName = sStyleName;
434 }
435 else
436 {
437// fails in rtftok test assert(!m_StyleName.isEmpty()); // must be inited first
438 }
439
440 return m_StyleName;
441}
442
444{
445 if (!m_pAbstractDef)
447
448 // [1] Call the same method on the abstract list
450 = m_pAbstractDef->GetPropertyValues(/*bDefaults=*/true);
451 auto aAbstractRange = asNonConstRange(aAbstract);
452
453 // [2] Call the upper class method
455 = AbstractListDef::GetPropertyValues(/*bDefaults=*/false);
456
457 // Merge the results of [2] in [1]
458 sal_Int32 nThisCount = aThis.getLength( );
459 sal_Int32 nAbstractCount = aAbstract.getLength( );
460 for ( sal_Int32 i = 0; i < nThisCount && i < nAbstractCount; i++ )
461 {
463 if (level.hasElements() && GetLevel(i)->HasValues())
464 {
465 // If the element contains something, merge it, but ignore stub overrides.
466 lcl_mergeProperties( level, aAbstractRange[i] );
467 }
468 }
469
470 return aAbstract;
471}
472
475{
477
478 try
479 {
480 uno::Reference< style::XStyleFamiliesSupplier > xFamilies( xFactory, uno::UNO_QUERY_THROW );
481 uno::Any oFamily = xFamilies->getStyleFamilies( )->getByName("NumberingStyles");
482
483 oFamily >>= xStyles;
484 }
485 catch ( const uno::Exception & )
486 {
487 }
488
489 return xStyles;
490}
491
494{
495 sal_Int16 nWeight = 0;
496 const sal_Int8 nAbstLevels = m_pAbstractDef ? m_pAbstractDef->Size() : 0;
497 for (sal_Int8 nLevel = 0; nLevel < nAbstLevels; ++nLevel)
498 {
499 const ListLevel::Pointer pAbsLevel = m_pAbstractDef->GetLevel(nLevel);
500 if (!pAbsLevel)
501 continue;
502 const StyleSheetEntryPtr pParaStyle = pAbsLevel->GetParaStyle();
503 if (!pParaStyle)
504 continue;
505 const StyleSheetPropertyMap& rProps = *pParaStyle->m_pProperties;
506 // In LO, the level's paraStyle outlineLevel always matches this listLevel.
507 // An undefined listLevel is treated as the first level.
508 sal_Int8 nListLevel = std::clamp<sal_Int8>(rProps.GetListLevel(), 0, 9);
509 if (nListLevel != nLevel || rProps.GetOutlineLevel() != nLevel)
510 return 0;
511 else if (pAbsLevel->GetNumberingType(style::NumberingType::NUMBER_NONE)
512 != style::NumberingType::NUMBER_NONE)
513 {
514 // Arbitrarily chosen weighting factors - trying to round-trip LO choices if possible.
515 // LibreOffice always saves Outline rule (usually containing heading styles) as numId 1.
516 sal_uInt16 nWeightingFactor = GetId() == 1 ? 8 : 1;
517 if (pParaStyle->m_sStyleIdentifierD.startsWith("Heading") )
518 ++nWeightingFactor;
519 nWeight += nWeightingFactor;
520 }
521 }
522 return nWeight;
523}
524
526 uno::Reference<lang::XMultiServiceFactory> const& xFactory, sal_Int16 nOutline)
527{
528 // Get the UNO Numbering styles
530
531 // Do the whole thing
532 if( !(!m_xNumRules.is() && xFactory.is() && xStyles.is( )) )
533 return;
534
535 try
536 {
537 // Create the numbering style
538 if (GetId() == nOutline)
539 m_StyleName = "Outline"; //SwNumRule.GetOutlineRuleName()
540 else
541 xStyles->insertByName(
542 GetStyleName(GetId(), xStyles),
543 css::uno::Any(xFactory->createInstance("com.sun.star.style.NumberingStyle")));
544
545 uno::Any oStyle = xStyles->getByName(GetStyleName());
546 uno::Reference< beans::XPropertySet > xStyle( oStyle, uno::UNO_QUERY_THROW );
547
548 // Get the default OOo Numbering style rules
549 uno::Any aRules = xStyle->getPropertyValue( getPropertyName( PROP_NUMBERING_RULES ) );
550 aRules >>= m_xNumRules;
551
553
554 sal_Int32 nAbstLevels = m_pAbstractDef ? m_pAbstractDef->Size() : 0;
555 sal_Int32 nLevel = 0;
556 while ( nLevel < nAbstLevels )
557 {
558 ListLevel::Pointer pAbsLevel = m_pAbstractDef->GetLevel( nLevel );
559 ListLevel::Pointer pLevel = GetLevel( nLevel );
560
561 // Get the merged level properties
562 auto aLvlProps = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aProps[nLevel]);
563
564 // Get the char style
565 auto aAbsCharStyleProps = pAbsLevel
566 ? pAbsLevel->GetCharStyleProperties()
568 if ( pLevel )
569 {
570 uno::Sequence< beans::PropertyValue >& rAbsCharStyleProps = aAbsCharStyleProps;
572 pLevel->GetCharStyleProperties( );
573 uno::Sequence< beans::PropertyValue >& rCharStyleProps = aCharStyleProps;
574 lcl_mergeProperties( rAbsCharStyleProps, rCharStyleProps );
575 }
576
577 // Change the sequence into a vector
578 auto aStyleProps
579 = comphelper::sequenceToContainer<PropertyValueVector_t>(aAbsCharStyleProps);
580
581 //create (or find) a character style containing the character
582 // attributes of the symbol and apply it to the numbering level
583 OUString sStyle = rDMapper.getOrCreateCharStyle(aStyleProps, /*bAlwaysCreate=*/true);
584 aLvlProps.push_back(
586
587 OUString sText = pAbsLevel
588 ? pAbsLevel->GetBulletChar()
589 : OUString();
590 // Inherit <w:lvlText> from the abstract level in case the override would be empty.
591 if (pLevel && pLevel->HasBulletChar())
592 sText = pLevel->GetBulletChar( );
593
595
596 aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_POSITION_AND_SPACE_MODE), sal_Int16(text::PositionAndSpaceMode::LABEL_ALIGNMENT)));
597
598 // Replace the numbering rules for the level
599 m_xNumRules->replaceByIndex(nLevel, uno::Any(comphelper::containerToSequence(aLvlProps)));
600
601 // Handle the outline level here
602 if (GetId() == nOutline && pAbsLevel && pAbsLevel->GetParaStyle())
603 {
605 xFactory, uno::UNO_QUERY_THROW );
607 xOutlines->getChapterNumberingRules( );
608
609 StyleSheetEntryPtr pParaStyle = pAbsLevel->GetParaStyle( );
610 pParaStyle->m_bAssignedAsChapterNumbering = true;
611 aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HEADING_STYLE_NAME), pParaStyle->m_sConvertedStyleName));
612
613 xOutlineRules->replaceByIndex(nLevel, uno::Any(comphelper::containerToSequence(aLvlProps)));
614 }
615
616 nLevel++;
617 }
618
619 // Create the numbering style for these rules
620 const OUString & sNumRulesName = getPropertyName( PROP_NUMBERING_RULES );
621 xStyle->setPropertyValue( sNumRulesName, uno::Any( m_xNumRules ) );
622 }
623 catch( const lang::IllegalArgumentException& )
624 {
625 TOOLS_WARN_EXCEPTION( "writerfilter", "" );
626 assert( !"Incorrect argument to UNO call" );
627 }
628 catch( const uno::RuntimeException& )
629 {
630 TOOLS_WARN_EXCEPTION( "writerfilter", "" );
631 assert( !"Incorrect argument to UNO call" );
632 }
633 catch( const uno::Exception& )
634 {
635 TOOLS_WARN_EXCEPTION( "writerfilter", "" );
636 }
637
638}
639
640//------------------------------------- NumberingManager implementation
641
642
645 : LoggedProperties("ListsManager")
646 , LoggedTable("ListsManager")
647 , m_rDMapper(rDMapper)
648 , m_xFactory(std::move(xFactory))
649{
650}
651
653{
655}
656
658{
660 for (const auto& rNumPicBullet : m_aNumPicBullets)
661 {
662 xShape = rNumPicBullet->GetShape();
663 if (xShape.is())
664 {
665 uno::Reference<lang::XComponent> xShapeComponent(xShape, uno::UNO_QUERY);
666 xShapeComponent->dispose();
667 }
668 }
669}
670
672{
673 ListLevel::Pointer pCurrentLvl;
674
675 if (nName != NS_ooxml::LN_CT_NumPicBullet_numPicBulletId)
676 {
677 OSL_ENSURE( m_pCurrentDefinition, "current entry has to be set here");
679 return ;
680 pCurrentLvl = m_pCurrentDefinition->GetCurrentLevel( );
681 }
682 else
683 {
684 SAL_WARN_IF(!m_pCurrentNumPicBullet, "writerfilter", "current entry has to be set here");
686 return;
687 }
688 int nIntValue = rVal.getInt();
689
690
691 switch(nName)
692 {
693 case NS_ooxml::LN_CT_LevelText_val:
694 {
695 if(pCurrentLvl)
696 {
697 //if the BulletChar is a soft-hyphen (0xad)
698 //replace it with a hard-hyphen (0x2d)
699 //-> this fixes missing hyphen export in PDF etc.
700 // see tdf#101626
701 std::string sLevelText( rVal.getString().replace(0xad, 0x2d).toUtf8() );
702
703 // DOCX level-text contains levels definition in format "%1.%2.%3"
704 // we need to convert it to LO internal representation: "%1%.%2%.%3%"
705 static const std::regex aTokenRegex("(%\\d)");
706 sLevelText = std::regex_replace(sLevelText, aTokenRegex, "$1%");
707 pCurrentLvl->SetBulletChar( OUString::fromUtf8(sLevelText) );
708 }
709 }
710 break;
711 case NS_ooxml::LN_CT_Lvl_start:
712 case NS_ooxml::LN_CT_Lvl_numFmt:
713 case NS_ooxml::LN_CT_NumFmt_format:
714 case NS_ooxml::LN_CT_NumFmt_val:
715 case NS_ooxml::LN_CT_Lvl_isLgl:
716 case NS_ooxml::LN_CT_Lvl_legacy:
717 if ( pCurrentLvl )
718 {
719 if (nName == NS_ooxml::LN_CT_NumFmt_format)
720 {
721 pCurrentLvl->SetCustomNumberFormat(rVal.getString());
722 }
723 else
724 {
725 pCurrentLvl->SetValue(nName, sal_Int32(nIntValue));
726 }
727 }
728 break;
729 case NS_ooxml::LN_CT_Num_numId:
730 m_pCurrentDefinition->SetId( rVal.getString().toInt32( ) );
731 break;
732 case NS_ooxml::LN_CT_AbstractNum_nsid:
733 m_pCurrentDefinition->SetId( nIntValue );
734 break;
735 case NS_ooxml::LN_CT_AbstractNum_tmpl:
737 break;
738 case NS_ooxml::LN_CT_NumLvl_ilvl:
739 //add a new level to the level vector and make it the current one
740 m_pCurrentDefinition->AddLevel(rVal.getString().toUInt32());
741 break;
742 case NS_ooxml::LN_CT_Lvl_ilvl:
743 m_pCurrentDefinition->AddLevel(rVal.getString().toUInt32());
744 break;
745 case NS_ooxml::LN_CT_AbstractNum_abstractNumId:
746 {
747 // This one corresponds to the AbstractNum Id definition
748 // The reference to the abstract num is in the sprm method
749 sal_Int32 nVal = rVal.getString().toInt32();
750 m_pCurrentDefinition->SetId( nVal );
751 }
752 break;
753 case NS_ooxml::LN_CT_Ind_start:
754 case NS_ooxml::LN_CT_Ind_left:
755 if ( pCurrentLvl )
756 pCurrentLvl->Insert(
758 break;
759 case NS_ooxml::LN_CT_Ind_hanging:
760 if ( pCurrentLvl )
761 pCurrentLvl->Insert(
763 break;
764 case NS_ooxml::LN_CT_Ind_firstLine:
765 if ( pCurrentLvl )
766 pCurrentLvl->Insert(
768 break;
769 case NS_ooxml::LN_CT_Lvl_tplc: //template code - unsupported
770 case NS_ooxml::LN_CT_Lvl_tentative: //marks level as unused in the document - unsupported
771 break;
772 case NS_ooxml::LN_CT_TabStop_pos:
773 {
774 //no paragraph attributes in ListTable char style sheets
775 if ( pCurrentLvl )
776 pCurrentLvl->SetValue( nName,
778 }
779 break;
780 case NS_ooxml::LN_CT_TabStop_val:
781 {
782 // TODO Do something of that
783 }
784 break;
785 case NS_ooxml::LN_CT_NumPicBullet_numPicBulletId:
786 m_pCurrentNumPicBullet->SetId(rVal.getString().toInt32());
787 break;
788 default:
789 SAL_WARN("writerfilter", "ListsManager::lcl_attribute: unhandled token: " << nName);
790 }
791}
792
794{
795 //fill the attributes of the style sheet
796 sal_uInt32 nSprmId = rSprm.getId();
797 if( !(m_pCurrentDefinition ||
798 nSprmId == NS_ooxml::LN_CT_Numbering_abstractNum ||
799 nSprmId == NS_ooxml::LN_CT_Numbering_num ||
800 (nSprmId == NS_ooxml::LN_CT_NumPicBullet_pict && m_pCurrentNumPicBullet) ||
801 nSprmId == NS_ooxml::LN_CT_Numbering_numPicBullet))
802 return;
803
804 static bool bIsStartVisited = false;
805 sal_Int32 nIntValue = rSprm.getValue()->getInt();
806 switch( nSprmId )
807 {
808 case NS_ooxml::LN_CT_Numbering_abstractNum:
809 {
811 if(pProperties)
812 {
813 //create a new Abstract list entry
814 OSL_ENSURE( !m_pCurrentDefinition, "current entry has to be NULL here");
816 pProperties->resolve( *this );
817 //append it to the table
820 }
821 }
822 break;
823 case NS_ooxml::LN_CT_Numbering_num:
824 {
826 if(pProperties)
827 {
828 // Create a new list entry
829 OSL_ENSURE( !m_pCurrentDefinition, "current entry has to be NULL here");
830 ListDef::Pointer listDef( new ListDef );
831 m_pCurrentDefinition = listDef.get();
832 pProperties->resolve( *this );
833 //append it to the table
834 m_aLists.push_back( listDef );
835
837 }
838 }
839 break;
840 case NS_ooxml::LN_CT_Numbering_numPicBullet:
841 {
843 if (pProperties)
844 {
845 NumPicBullet::Pointer numPicBullet(new NumPicBullet());
846 m_pCurrentNumPicBullet = numPicBullet;
847 pProperties->resolve(*this);
848 m_aNumPicBullets.push_back(numPicBullet);
850 }
851 }
852 break;
853 case NS_ooxml::LN_CT_NumPicBullet_pict:
854 {
856
857 m_pCurrentNumPicBullet->SetShape(xShape);
858 }
859 break;
860 case NS_ooxml::LN_CT_Lvl_lvlPicBulletId:
861 if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
862 {
864 for (const auto& rNumPicBullet : m_aNumPicBullets)
865 {
866 if (rNumPicBullet->GetId() == nIntValue)
867 {
868 xShape = rNumPicBullet->GetShape();
869 break;
870 }
871 }
872 if (xShape.is())
873 {
874 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
875 try
876 {
877 uno::Any aAny = xPropertySet->getPropertyValue("Graphic");
878 if (aAny.has<uno::Reference<graphic::XGraphic>>() && pCurrentLevel)
879 {
880 auto xGraphic = aAny.get<uno::Reference<graphic::XGraphic>>();
881 if (xGraphic.is())
882 {
883 uno::Reference<awt::XBitmap> xBitmap(xGraphic, uno::UNO_QUERY);
884 pCurrentLevel->SetGraphicBitmap(xBitmap);
885 }
886 }
887 }
888 catch (const beans::UnknownPropertyException&)
889 {}
890
891 // Respect only the aspect ratio of the picture, not its size.
892 awt::Size aPrefSize = xShape->getSize();
893 if ( aPrefSize.Height * aPrefSize.Width != 0 )
894 {
895 // See SwDefBulletConfig::InitFont(), default height is 14.
896 const int nFontHeight = 14;
897 // Point -> mm100.
898 const int nHeight = nFontHeight * 35;
899 int nWidth = (nHeight * aPrefSize.Width) / aPrefSize.Height;
900
901 awt::Size aSize( o3tl::toTwips(nWidth, o3tl::Length::mm100), o3tl::toTwips(nHeight, o3tl::Length::mm100) );
902 pCurrentLevel->SetGraphicSize( aSize );
903 }
904 else
905 {
906 awt::Size aSize( o3tl::toTwips(aPrefSize.Width, o3tl::Length::mm100), o3tl::toTwips(aPrefSize.Height, o3tl::Length::mm100) );
907 pCurrentLevel->SetGraphicSize( aSize );
908 }
909 }
910 }
911 break;
912 case NS_ooxml::LN_CT_Num_abstractNumId:
913 {
914 sal_Int32 nAbstractNumId = rSprm.getValue()->getInt();
915 ListDef* pListDef = dynamic_cast< ListDef* >( m_pCurrentDefinition.get( ) );
916 if ( pListDef != nullptr )
917 {
918 // The current def should be a ListDef
919 pListDef->SetAbstractDefinition(
920 GetAbstractList( nAbstractNumId ) );
921 }
922 }
923 break;
924 case NS_ooxml::LN_CT_AbstractNum_multiLevelType:
925 break;
926 case NS_ooxml::LN_CT_AbstractNum_tmpl:
927 AbstractListDef::SetValue( nSprmId );
928 break;
929 case NS_ooxml::LN_CT_AbstractNum_lvl:
930 {
932 if(pProperties)
933 pProperties->resolve(*this);
934 }
935 break;
936 case NS_ooxml::LN_CT_Lvl_start:
937 if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
938 pCurrentLevel->SetValue( nSprmId, nIntValue );
939 bIsStartVisited = true;
940 break;
941 case NS_ooxml::LN_CT_Lvl_numFmt:
942 {
944 if (pProperties)
945 {
946 pProperties->resolve(*this);
947 }
948 if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
949 {
950 if( !bIsStartVisited )
951 {
952 pCurrentLevel->SetValue( NS_ooxml::LN_CT_Lvl_start, 0 );
953 bIsStartVisited = true;
954 }
955 }
956 }
957 break;
958 case NS_ooxml::LN_CT_Lvl_isLgl:
959 case NS_ooxml::LN_CT_Lvl_legacy:
960 if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
961 {
962 pCurrentLevel->SetValue(nSprmId, nIntValue);
963 }
964 break;
965 case NS_ooxml::LN_CT_Lvl_suff:
966 {
967 if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
968 {
970 if( rSprm.getValue()->getString() == "tab" )
972 else if( rSprm.getValue()->getString() == "space" )
974 else if( rSprm.getValue()->getString() == "nothing" )
976 else
977 SAL_WARN( "writerfilter", "Unknown ST_LevelSuffix value "
978 << rSprm.getValue()->getString());
979 pCurrentLevel->SetValue( nSprmId, value );
980 }
981 }
982 break;
983 case NS_ooxml::LN_CT_Lvl_lvlText:
984 case NS_ooxml::LN_CT_Lvl_rPr : //contains LN_EG_RPrBase_rFonts
985 {
987 if(pProperties)
988 pProperties->resolve(*this);
989 }
990 break;
991 case NS_ooxml::LN_CT_NumLvl_lvl:
992 {
993 // overwrite level
995 if(pProperties)
996 pProperties->resolve(*this);
997 }
998 break;
999 case NS_ooxml::LN_CT_Lvl_lvlJc:
1000 {
1002 switch (nIntValue)
1003 {
1004 case NS_ooxml::LN_Value_ST_Jc_left:
1005 case NS_ooxml::LN_Value_ST_Jc_start:
1006 nValue = text::HoriOrientation::LEFT;
1007 break;
1008 case NS_ooxml::LN_Value_ST_Jc_center:
1009 nValue = text::HoriOrientation::CENTER;
1010 break;
1011 case NS_ooxml::LN_Value_ST_Jc_right:
1012 case NS_ooxml::LN_Value_ST_Jc_end:
1013 nValue = text::HoriOrientation::RIGHT;
1014 break;
1015 }
1016
1018 {
1019 if (ListLevel::Pointer pLevel = m_pCurrentDefinition->GetCurrentLevel())
1020 {
1021 pLevel->Insert(
1023 }
1024 }
1025 }
1026 break;
1027 case NS_ooxml::LN_CT_Lvl_pPr:
1028 case NS_ooxml::LN_CT_PPrBase_ind:
1029 {
1030 //todo: how to handle paragraph properties within numbering levels (except LeftIndent and FirstLineIndent)?
1032 if(pProperties)
1033 pProperties->resolve(*this);
1034 }
1035 break;
1036 case NS_ooxml::LN_CT_PPrBase_tabs:
1037 case NS_ooxml::LN_CT_Tabs_tab:
1038 {
1040 if(pProperties)
1041 pProperties->resolve(*this);
1042 }
1043 break;
1044 case NS_ooxml::LN_CT_Lvl_pStyle:
1045 {
1046 OUString sStyleName = rSprm.getValue( )->getString( );
1047 if (ListLevel::Pointer pLevel = m_pCurrentDefinition->GetCurrentLevel())
1048 {
1050 const StyleSheetEntryPtr pStyle = pStylesTable->FindStyleSheetByISTD( sStyleName );
1051 pLevel->SetParaStyle( pStyle );
1052 }
1053 }
1054 break;
1055 case NS_ooxml::LN_CT_Num_lvlOverride:
1056 {
1058 if (pProperties)
1059 pProperties->resolve(*this);
1060 }
1061 break;
1062 case NS_ooxml::LN_CT_NumLvl_startOverride:
1063 {
1065 {
1066 if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
1067 {
1068 pCurrentLevel->SetValue(NS_ooxml::LN_CT_NumLvl_startOverride, nIntValue);
1069 }
1070 }
1071 }
1072 break;
1073 case NS_ooxml::LN_CT_AbstractNum_numStyleLink:
1074 {
1075 OUString sStyleName = rSprm.getValue( )->getString( );
1076 m_pCurrentDefinition->SetNumStyleLink(sStyleName);
1077 }
1078 break;
1079 case NS_ooxml::LN_CT_AbstractNum_styleLink:
1080 {
1081 OUString sStyleName = rSprm.getValue()->getString();
1082 m_pCurrentDefinition->SetStyleLink(sStyleName);
1083 }
1084 break;
1085 case NS_ooxml::LN_EG_RPrBase_rFonts: //contains font properties
1086 case NS_ooxml::LN_EG_RPrBase_color:
1087 case NS_ooxml::LN_EG_RPrBase_u:
1088 case NS_ooxml::LN_EG_RPrBase_sz:
1089 case NS_ooxml::LN_EG_RPrBase_lang:
1090 case NS_ooxml::LN_EG_RPrBase_eastAsianLayout:
1091 //no break!
1092 default:
1093 if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
1094 {
1095 m_rDMapper.PushListProperties(pCurrentLevel.get());
1096 m_rDMapper.sprm( rSprm );
1098 }
1099 }
1100}
1101
1103{
1105 {
1106 ref->resolve(*this);
1107 }
1108 else
1109 {
1110 // Create AbstractListDef's
1111 OSL_ENSURE( !m_pCurrentDefinition, "current entry has to be NULL here");
1113 ref->resolve(*this);
1114 //append it to the table
1117 }
1118}
1119
1121{
1122 for (const auto& listDef : m_aAbstractLists)
1123 {
1124 if (listDef->GetId( ) == nId)
1125 {
1126 if (listDef->GetNumStyleLink().getLength() > 0)
1127 {
1128 // If the abstract num has a style linked, check the linked style's number id.
1130
1131 const StyleSheetEntryPtr pStyleSheetEntry =
1132 pStylesTable->FindStyleSheetByISTD(listDef->GetNumStyleLink() );
1133
1134 const StyleSheetPropertyMap* pStyleSheetProperties =
1135 pStyleSheetEntry ? pStyleSheetEntry->m_pProperties.get() : nullptr;
1136
1137 if( pStyleSheetProperties && pStyleSheetProperties->props().GetListId() >= 0 )
1138 {
1139 ListDef::Pointer pList = GetList( pStyleSheetProperties->props().GetListId() );
1140 if ( pList!=nullptr )
1141 return pList->GetAbstractDefinition();
1142 }
1143
1144 // In stylesheet we did not found anything useful. Try to find base abstractnum having this stylelink
1145 for (const auto & baseListDef : m_aAbstractLists)
1146 {
1147 if (baseListDef->GetStyleLink() == listDef->GetNumStyleLink())
1148 {
1149 return baseListDef;
1150 }
1151 }
1152 }
1153
1154 // Standalone abstract list
1155 return listDef;
1156 }
1157 }
1158
1159 return nullptr;
1160}
1161
1163{
1164 ListDef::Pointer pList;
1165 if (nId == -1)
1166 return pList;
1167
1168 int nLen = m_aLists.size( );
1169 int i = 0;
1170 while ( !pList && i < nLen )
1171 {
1172 if ( m_aLists[i]->GetId( ) == nId )
1173 pList = m_aLists[i];
1174 i++;
1175 }
1176
1177 // nId 0 is only valid for abstractNum, not numId (which has an abstract definition)
1178 assert(!pList || nId || !pList->GetAbstractDefinition() || m_rDMapper.IsRTFImport());
1179
1180 return pList;
1181}
1182
1184{
1185 // Try to determine which numId would best work as LO's Chapter Numbering Outline rule.
1186 // (The best fix for many import bugs is just to prevent ANY assignment as chapter numbering.)
1187 sal_Int16 nChosenAsChapterNumberingId = -1;
1188 sal_uInt16 nHighestWeight = 5; // arbitrarily chosen minimum threshold
1189 for (const auto& rList : m_aLists)
1190 {
1191 sal_uInt16 nWeight = rList->GetChapterNumberingWeight();
1192 if (nWeight > nHighestWeight)
1193 {
1194 nHighestWeight = nWeight;
1195 nChosenAsChapterNumberingId = rList->GetId();
1196 //Optimization: if the weight cannot be beaten anymore, then quit early
1197 if (nHighestWeight > 17)
1198 break;
1199 }
1200 }
1201
1202 // Loop over the definitions
1203 for ( const auto& rList : m_aLists )
1204 {
1205 rList->CreateNumberingRules(m_rDMapper, m_xFactory, nChosenAsChapterNumberingId);
1206 }
1207 m_rDMapper.GetStyleSheetTable()->ReApplyInheritedOutlineLevelFromChapterNumbering();
1208 m_rDMapper.GetStyleSheetTable()->ApplyNumberingStyleNameToParaStyles();
1209}
1210
1211}
1212
1213/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::vector< css::beans::PropertyValue > PropertyValueVector_t
css::uno::Any getProperty(sal_Int32 nPropId)
static const OUString & getPropertyName(sal_Int32 nPropId)
T * get() const
void sprm(Sprm &sprm) override
Receives a SPRM.
An SPRM: Section, Paragraph and Run Modifier.
virtual sal_uInt32 getId() const =0
Returns id of the SPRM.
virtual writerfilter::Reference< Properties >::Pointer_t getProps()=0
Returns reference to properties contained in the SPRM.
virtual Value::Pointer_t getValue()=0
Returns value of the SPRM.
virtual int getInt() const =0
Returns integer representation of the value.
virtual OUString getString() const =0
Returns string representation of the value.
css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > GetPropertyValues(bool bDefaults)
static void SetValue(sal_uInt32 nSprmId)
tools::SvRef< AbstractListDef > Pointer
::std::vector< ListLevel::Pointer > m_aLevels
ListLevel::Pointer GetLevel(sal_uInt16 nLvl)
std::optional< OUString > m_oListId
list id to use for all derived numbering definitions
const OUString & MapListId(OUString const &rId)
css::uno::Reference< css::drawing::XShape > PopPendingShape()
Return the first from the pending (not inserted to the document) shapes, if there are any.
StyleSheetTablePtr const & GetStyleSheetTable()
OUString getOrCreateCharStyle(PropertyValueVector_t &rCharProperties, bool bAlwaysCreate)
void PushListProperties(const ::tools::SvRef< PropertyMap > &pListProperties)
void SetAbstractDefinition(AbstractListDef::Pointer pAbstract)
sal_uInt16 GetChapterNumberingWeight() const
Rank the list in terms of suitability for becoming the Outline numbering rule in LO.
css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > GetMergedPropertyValues()
void CreateNumberingRules(DomainMapper &rDMapper, css::uno::Reference< css::lang::XMultiServiceFactory > const &xFactory, sal_Int16 nOutline)
const OUString & GetStyleName() const
AbstractListDef::Pointer m_pAbstractDef
css::uno::Reference< css::container::XIndexReplace > m_xNumRules
OUString m_StyleName
mapped list style name
Class representing the numbering level properties.
tools::SvRef< StyleSheetEntry > m_pParaStyle
css::uno::Reference< css::awt::XBitmap > m_xGraphicBitmap
std::optional< sal_Int32 > m_nTabstop
sal_Int16 GetNumberingType(sal_Int16 nDefault) const
void SetCustomNumberFormat(const OUString &rValue)
css::uno::Sequence< css::beans::PropertyValue > GetProperties(bool bDefaults)
css::uno::Sequence< css::beans::PropertyValue > GetCharStyleProperties()
css::uno::Sequence< css::beans::PropertyValue > GetLevelProperties(bool bDefaults)
OUString m_aCustomNumberFormat
LN_CT_NumFmt_format, in case m_nNFC is custom.
void SetParaStyle(const tools::SvRef< StyleSheetEntry > &pStyle)
void AddParaProperties(css::uno::Sequence< css::beans::PropertyValue > *pProps)
std::optional< OUString > m_sBulletChar
void SetValue(Id nId, sal_Int32 nValue)
bool HasValues() const
Determines if SetValue() was called at least once.
virtual void lcl_attribute(Id nName, Value &rVal) override
std::vector< NumPicBullet::Pointer > m_aNumPicBullets
NumPicBullet::Pointer m_pCurrentNumPicBullet
std::vector< AbstractListDef::Pointer > m_aAbstractLists
std::vector< ListDef::Pointer > m_aLists
ListDef::Pointer GetList(sal_Int32 nId)
AbstractListDef::Pointer m_pCurrentDefinition
virtual void lcl_sprm(Sprm &sprm) override
ListsManager(DomainMapper &rDMapper, css::uno::Reference< css::lang::XMultiServiceFactory > xFactory)
AbstractListDef::Pointer GetAbstractList(sal_Int32 nId)
css::uno::Reference< css::lang::XMultiServiceFactory > m_xFactory
virtual void lcl_entry(writerfilter::Reference< Properties >::Pointer_t ref) override
Represents a numbering picture bullet: an id and a graphic.
css::uno::Reference< css::drawing::XShape > m_xShape
tools::SvRef< NumPicBullet > Pointer
void SetShape(css::uno::Reference< css::drawing::XShape > const &xShape)
Any value
#define TOOLS_WARN_EXCEPTION(area, stream)
virtual sal_uInt32 GetId() const override
float u
Reference< XSingleServiceFactory > xFactory
sal_Int16 nValue
OUString sName
sal_uInt16 nPos
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
aStr
double getLength(const B2DPolygon &rCandidate)
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
int i
constexpr auto toTwips(N number, Length from)
dictionary props
sal_Int16 ConvertNumberingType(const sal_Int32 nFmt, const sal_Int16 nDefault)
sal_Int16 ConvertCustomNumberFormat(std::u16string_view rFormat)
OUString getPropertyName(PropertyIds eId)
static uno::Reference< container::XNameContainer > lcl_getUnoNumberingStyles(uno::Reference< lang::XMultiServiceFactory > const &xFactory)
static void lcl_mergeProperties(const uno::Sequence< beans::PropertyValue > &aSrc, uno::Sequence< beans::PropertyValue > &aDst)
static bool IgnoreForCharStyle(std::u16string_view aStr, const bool bIsSymbol)
static beans::PropertyValue lcl_makePropVal(PropertyIds nNameID, T const &aValue)
static sal_Int32 lcl_findProperty(const uno::Sequence< beans::PropertyValue > &aProps, std::u16string_view sName)
sal_Int16 nId
sal_uInt32 Id
OUString Name
signed char sal_Int8
Any result