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 <comphelper/sequence.hxx>
21 #include <comphelper/string.hxx>
22 #include <svl/numformat.hxx>
23 #include <svl/zforlist.hxx>
24 #include <svl/zformat.hxx>
25 #include <svl/numuno.hxx>
26 #include <i18nlangtag/mslangid.hxx>
27 #include <i18nlangtag/languagetag.hxx>
28 #include <tools/debug.hxx>
29 #include <rtl/math.hxx>
30 #include <unotools/calendarwrapper.hxx>
31 #include <unotools/charclass.hxx>
32 #include <com/sun/star/lang/Locale.hpp>
33 #include <rtl/ustrbuf.hxx>
34 #include <sal/log.hxx>
35 #include <osl/diagnose.h>
36 #include <tools/color.hxx>
37 #include <sax/tools/converter.hxx>
39 #include <com/sun/star/i18n/NativeNumberXmlAttributes2.hpp>
42 #include <xmloff/xmlnumfe.hxx>
43 #include <xmloff/xmlnamespace.hxx>
44 #include <xmloff/xmlnumfi.hxx>
46 #include <svl/nfsymbol.hxx>
47 #include <xmloff/xmltoken.hxx>
48 #include <xmloff/xmlexp.hxx>
49 #include <o3tl/string_view.hxx>
53 #include <string_view>
56 using namespace ::com::sun::star
;
57 using namespace ::xmloff::token
;
58 using namespace ::svt
;
60 typedef std::set
< sal_uInt32
> SvXMLuInt32Set
;
64 struct SvXMLEmbeddedTextEntry
66 sal_uInt16 nSourcePos
; // position in NumberFormat (to skip later)
67 sal_Int32 nFormatPos
; // resulting position in embedded-text element
69 bool isBlankWidth
; // "_x"
71 SvXMLEmbeddedTextEntry( sal_uInt16 nSP
, sal_Int32 nFP
, OUString aT
, bool bBW
= false ) :
72 nSourcePos(nSP
), nFormatPos(nFP
), aText(std::move(aT
)), isBlankWidth( bBW
) {}
77 class SvXMLEmbeddedTextEntryArr
79 typedef std::vector
<SvXMLEmbeddedTextEntry
> DataType
;
84 void push_back( SvXMLEmbeddedTextEntry
const& r
)
89 const SvXMLEmbeddedTextEntry
& operator[] ( size_t i
) const
100 class SvXMLNumUsedList_Impl
102 SvXMLuInt32Set aUsed
;
103 SvXMLuInt32Set aWasUsed
;
104 SvXMLuInt32Set::iterator aCurrentUsedPos
;
105 sal_uInt32 nUsedCount
;
106 sal_uInt32 nWasUsedCount
;
109 SvXMLNumUsedList_Impl();
111 void SetUsed( sal_uInt32 nKey
);
112 bool IsUsed( sal_uInt32 nKey
) const;
113 bool IsWasUsed( sal_uInt32 nKey
) const;
116 bool GetFirstUsed(sal_uInt32
& nKey
);
117 bool GetNextUsed(sal_uInt32
& nKey
);
119 uno::Sequence
<sal_Int32
> GetWasUsed() const;
120 void SetWasUsed(const uno::Sequence
<sal_Int32
>& rWasUsed
);
123 //! SvXMLNumUsedList_Impl should be optimized!
125 SvXMLNumUsedList_Impl::SvXMLNumUsedList_Impl() :
131 void SvXMLNumUsedList_Impl::SetUsed( sal_uInt32 nKey
)
133 if ( !IsWasUsed(nKey
) )
135 std::pair
<SvXMLuInt32Set::iterator
, bool> aPair
= aUsed
.insert( nKey
);
141 bool SvXMLNumUsedList_Impl::IsUsed( sal_uInt32 nKey
) const
143 SvXMLuInt32Set::const_iterator aItr
= aUsed
.find(nKey
);
144 return (aItr
!= aUsed
.end());
147 bool SvXMLNumUsedList_Impl::IsWasUsed( sal_uInt32 nKey
) const
149 SvXMLuInt32Set::const_iterator aItr
= aWasUsed
.find(nKey
);
150 return (aItr
!= aWasUsed
.end());
153 void SvXMLNumUsedList_Impl::Export()
155 SvXMLuInt32Set::const_iterator aItr
= aUsed
.begin();
156 while (aItr
!= aUsed
.end())
158 std::pair
<SvXMLuInt32Set::const_iterator
, bool> aPair
= aWasUsed
.insert( *aItr
);
167 bool SvXMLNumUsedList_Impl::GetFirstUsed(sal_uInt32
& nKey
)
170 aCurrentUsedPos
= aUsed
.begin();
173 DBG_ASSERT(aCurrentUsedPos
!= aUsed
.end(), "something went wrong");
174 nKey
= *aCurrentUsedPos
;
180 bool SvXMLNumUsedList_Impl::GetNextUsed(sal_uInt32
& nKey
)
183 if (aCurrentUsedPos
!= aUsed
.end())
186 if (aCurrentUsedPos
!= aUsed
.end())
188 nKey
= *aCurrentUsedPos
;
195 uno::Sequence
<sal_Int32
> SvXMLNumUsedList_Impl::GetWasUsed() const
197 return comphelper::containerToSequence
<sal_Int32
>(aWasUsed
);
200 void SvXMLNumUsedList_Impl::SetWasUsed(const uno::Sequence
<sal_Int32
>& rWasUsed
)
202 DBG_ASSERT(nWasUsedCount
== 0, "WasUsed should be empty");
203 for (const auto nWasUsed
: rWasUsed
)
205 std::pair
<SvXMLuInt32Set::const_iterator
, bool> aPair
= aWasUsed
.insert( nWasUsed
);
211 SvXMLNumFmtExport::SvXMLNumFmtExport(
213 const uno::Reference
< util::XNumberFormatsSupplier
>& rSupp
) :
215 m_sPrefix( u
"N"_ustr
),
216 m_pFormatter( nullptr ),
219 // supplier must be SvNumberFormatsSupplierObj
220 SvNumberFormatsSupplierObj
* pObj
=
221 comphelper::getFromUnoTunnel
<SvNumberFormatsSupplierObj
>( rSupp
);
223 m_pFormatter
= pObj
->GetNumberFormatter();
227 m_pLocaleData
.reset( new LocaleDataWrapper( m_pFormatter
->GetComponentContext(),
228 m_pFormatter
->GetLanguageTag() ) );
232 LanguageTag
aLanguageTag( MsLangId::getConfiguredSystemLanguage() );
234 m_pLocaleData
.reset( new LocaleDataWrapper( m_rExport
.getComponentContext(), std::move(aLanguageTag
) ) );
237 m_pUsedList
.reset(new SvXMLNumUsedList_Impl
);
240 SvXMLNumFmtExport::SvXMLNumFmtExport(
242 const css::uno::Reference
< css::util::XNumberFormatsSupplier
>& rSupp
,
245 m_sPrefix(std::move( aPrefix
)),
246 m_pFormatter( nullptr ),
249 // supplier must be SvNumberFormatsSupplierObj
250 SvNumberFormatsSupplierObj
* pObj
=
251 comphelper::getFromUnoTunnel
<SvNumberFormatsSupplierObj
>( rSupp
);
253 m_pFormatter
= pObj
->GetNumberFormatter();
257 m_pLocaleData
.reset( new LocaleDataWrapper( m_pFormatter
->GetComponentContext(),
258 m_pFormatter
->GetLanguageTag() ) );
262 LanguageTag
aLanguageTag( MsLangId::getConfiguredSystemLanguage() );
264 m_pLocaleData
.reset( new LocaleDataWrapper( m_rExport
.getComponentContext(), std::move(aLanguageTag
) ) );
267 m_pUsedList
.reset(new SvXMLNumUsedList_Impl
);
270 SvXMLNumFmtExport::~SvXMLNumFmtExport()
276 static OUString
lcl_CreateStyleName( sal_Int32 nKey
, sal_Int32 nPart
, bool bDefPart
, std::u16string_view rPrefix
)
279 return rPrefix
+ OUString::number(nKey
);
281 return rPrefix
+ OUString::number(nKey
) + "P" + OUString::number( nPart
);
284 void SvXMLNumFmtExport::AddCalendarAttr_Impl( const OUString
& rCalendar
)
286 if ( !rCalendar
.isEmpty() )
288 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_CALENDAR
, rCalendar
);
292 void SvXMLNumFmtExport::AddStyleAttr_Impl( bool bLong
)
294 if ( bLong
) // short is default
296 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_STYLE
, XML_LONG
);
300 void SvXMLNumFmtExport::AddLanguageAttr_Impl( LanguageType nLang
)
302 if ( nLang
!= LANGUAGE_SYSTEM
)
304 m_rExport
.AddLanguageTagAttributes( XML_NAMESPACE_NUMBER
, XML_NAMESPACE_NUMBER
,
305 LanguageTag( nLang
), false);
309 // methods to write individual elements within a format
311 void SvXMLNumFmtExport::AddToTextElement_Impl( std::u16string_view rString
)
313 // append to sTextContent, write element in FinishTextElement_Impl
314 // to avoid several text elements following each other
316 m_sTextContent
.append( rString
);
317 // Also empty string leads to a number:text element as it may separate
318 // keywords of the same letter (e.g. MM""MMM) that otherwise would be
319 // concatenated when reading back in.
323 void SvXMLNumFmtExport::FinishTextElement_Impl(bool bUseExtensionNS
)
327 if ( !m_sBlankWidthString
.isEmpty() )
329 // Export only for 1.3 with extensions and later.
330 SvtSaveOptions::ODFSaneDefaultVersion eVersion
= m_rExport
.getSaneDefaultVersion();
331 if (eVersion
> SvtSaveOptions::ODFSVER_013
&& ( (eVersion
& SvtSaveOptions::ODFSVER_EXTENDED
) != 0 ))
333 m_rExport
.AddAttribute( XML_NAMESPACE_LO_EXT
, XML_BLANK_WIDTH_CHAR
,
334 m_sBlankWidthString
.makeStringAndClear() );
337 sal_uInt16 nNS
= bUseExtensionNS
? XML_NAMESPACE_LO_EXT
: XML_NAMESPACE_NUMBER
;
338 SvXMLElementExport
aElem( m_rExport
, nNS
, XML_TEXT
,
340 m_rExport
.Characters( m_sTextContent
.makeStringAndClear() );
345 void SvXMLNumFmtExport::WriteColorElement_Impl( const Color
& rColor
)
347 FinishTextElement_Impl();
349 OUStringBuffer
aColStr( 7 );
350 ::sax::Converter::convertColor( aColStr
, rColor
);
351 m_rExport
.AddAttribute( XML_NAMESPACE_FO
, XML_COLOR
,
352 aColStr
.makeStringAndClear() );
354 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_STYLE
, XML_TEXT_PROPERTIES
,
358 void SvXMLNumFmtExport::WriteCurrencyElement_Impl( const OUString
& rString
,
359 std::u16string_view rExt
)
361 FinishTextElement_Impl();
365 // rExt should be a 16-bit hex value max FFFF which may contain a
366 // leading "-" separator (that is not a minus sign, but toInt32 can be
367 // used to parse it, with post-processing as necessary):
368 sal_Int32 nLang
= o3tl::toInt32(rExt
, 16);
371 SAL_WARN_IF(nLang
> 0xFFFF, "xmloff.style", "Out of range Lang Id: " << nLang
<< " from input string: " << OUString(rExt
));
372 AddLanguageAttr_Impl( LanguageType(nLang
& 0xFFFF) ); // adds to pAttrList
375 SvXMLElementExport
aElem( m_rExport
,
376 XML_NAMESPACE_NUMBER
, XML_CURRENCY_SYMBOL
,
378 m_rExport
.Characters( rString
);
381 void SvXMLNumFmtExport::WriteBooleanElement_Impl()
383 FinishTextElement_Impl();
385 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_BOOLEAN
,
389 void SvXMLNumFmtExport::WriteTextContentElement_Impl()
391 FinishTextElement_Impl();
393 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_TEXT_CONTENT
,
399 void SvXMLNumFmtExport::WriteDayElement_Impl( const OUString
& rCalendar
, bool bLong
)
401 FinishTextElement_Impl();
403 AddCalendarAttr_Impl( rCalendar
); // adds to pAttrList
404 AddStyleAttr_Impl( bLong
); // adds to pAttrList
406 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_DAY
,
410 void SvXMLNumFmtExport::WriteMonthElement_Impl( const OUString
& rCalendar
, bool bLong
, bool bText
)
412 FinishTextElement_Impl();
414 AddCalendarAttr_Impl( rCalendar
); // adds to pAttrList
415 AddStyleAttr_Impl( bLong
); // adds to pAttrList
418 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_TEXTUAL
, XML_TRUE
);
421 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_MONTH
,
425 void SvXMLNumFmtExport::WriteYearElement_Impl( const OUString
& rCalendar
, bool bLong
)
427 FinishTextElement_Impl();
429 AddCalendarAttr_Impl( rCalendar
); // adds to pAttrList
430 AddStyleAttr_Impl( bLong
); // adds to pAttrList
432 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_YEAR
,
436 void SvXMLNumFmtExport::WriteEraElement_Impl( const OUString
& rCalendar
, bool bLong
)
438 FinishTextElement_Impl();
440 AddCalendarAttr_Impl( rCalendar
); // adds to pAttrList
441 AddStyleAttr_Impl( bLong
); // adds to pAttrList
443 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_ERA
,
447 void SvXMLNumFmtExport::WriteDayOfWeekElement_Impl( const OUString
& rCalendar
, bool bLong
)
449 FinishTextElement_Impl();
451 AddCalendarAttr_Impl( rCalendar
); // adds to pAttrList
452 AddStyleAttr_Impl( bLong
); // adds to pAttrList
454 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_DAY_OF_WEEK
,
458 void SvXMLNumFmtExport::WriteWeekElement_Impl( const OUString
& rCalendar
)
460 FinishTextElement_Impl();
462 AddCalendarAttr_Impl( rCalendar
); // adds to pAttrList
464 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_WEEK_OF_YEAR
,
468 void SvXMLNumFmtExport::WriteQuarterElement_Impl( const OUString
& rCalendar
, bool bLong
)
470 FinishTextElement_Impl();
472 AddCalendarAttr_Impl( rCalendar
); // adds to pAttrList
473 AddStyleAttr_Impl( bLong
); // adds to pAttrList
475 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_QUARTER
,
481 void SvXMLNumFmtExport::WriteHoursElement_Impl( bool bLong
)
483 FinishTextElement_Impl();
485 AddStyleAttr_Impl( bLong
); // adds to pAttrList
487 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_HOURS
,
491 void SvXMLNumFmtExport::WriteMinutesElement_Impl( bool bLong
)
493 FinishTextElement_Impl();
495 AddStyleAttr_Impl( bLong
); // adds to pAttrList
497 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_MINUTES
,
501 void SvXMLNumFmtExport::WriteRepeatedElement_Impl( sal_Unicode nChar
)
503 // Export only for 1.2 with extensions or 1.3 and later.
504 SvtSaveOptions::ODFSaneDefaultVersion eVersion
= m_rExport
.getSaneDefaultVersion();
505 if (eVersion
> SvtSaveOptions::ODFSVER_012
)
507 FinishTextElement_Impl(eVersion
< SvtSaveOptions::ODFSVER_013
);
508 // OFFICE-3765 For 1.2+ use loext namespace, for 1.3 use number namespace.
509 SvXMLElementExport
aElem( m_rExport
,
510 ((eVersion
< SvtSaveOptions::ODFSVER_013
) ? XML_NAMESPACE_LO_EXT
: XML_NAMESPACE_NUMBER
),
511 XML_FILL_CHARACTER
, true, false );
512 m_rExport
.Characters( OUString( nChar
) );
517 void lcl_WriteBlankWidthString( std::u16string_view rBlankWidthChar
, OUStringBuffer
& rBlankWidthString
, OUStringBuffer
& rTextContent
)
520 if ( rBlankWidthString
.isEmpty() )
522 rBlankWidthString
.append( rBlankWidthChar
);
523 if ( !rTextContent
.isEmpty() )
525 // add position in rTextContent
526 rBlankWidthString
.append( rTextContent
.getLength() );
531 // add "_" as separator if there are several blank width char
532 rBlankWidthString
.append( "_" );
533 rBlankWidthString
.append( rBlankWidthChar
);
534 rBlankWidthString
.append( rTextContent
.getLength() );
536 // for previous versions, turn "_x" into the number of spaces used for x in InsertBlanks in the NumberFormat
537 if ( !rBlankWidthChar
.empty() )
540 SvNumberformat::InsertBlanks( aBlanks
, 0, rBlankWidthChar
[0] );
541 rTextContent
.append( aBlanks
);
546 void SvXMLNumFmtExport::WriteSecondsElement_Impl( bool bLong
, sal_uInt16 nDecimals
)
548 FinishTextElement_Impl();
550 AddStyleAttr_Impl( bLong
); // adds to pAttrList
553 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_DECIMAL_PLACES
,
554 OUString::number( nDecimals
) );
557 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_SECONDS
,
561 void SvXMLNumFmtExport::WriteAMPMElement_Impl()
563 FinishTextElement_Impl();
565 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_AM_PM
,
571 void SvXMLNumFmtExport::WriteIntegerElement_Impl(
572 sal_Int32 nInteger
, sal_Int32 nBlankInteger
, bool bGrouping
)
574 // integer digits: '0' and '?'
575 if ( nInteger
>= 0 ) // negative = automatic
577 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_MIN_INTEGER_DIGITS
,
578 OUString::number( nInteger
) );
580 SvtSaveOptions::ODFSaneDefaultVersion eVersion
= m_rExport
.getSaneDefaultVersion();
581 // blank integer digits: '?'
582 if ( nBlankInteger
> 0 && ( (eVersion
& SvtSaveOptions::ODFSVER_EXTENDED
) != 0 ) )
584 m_rExport
.AddAttribute( XML_NAMESPACE_LO_EXT
, XML_MAX_BLANK_INTEGER_DIGITS
,
585 OUString::number( nBlankInteger
) );
587 // (automatic) grouping separator
590 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_GROUPING
, XML_TRUE
);
594 void SvXMLNumFmtExport::WriteEmbeddedEntries_Impl( const SvXMLEmbeddedTextEntryArr
& rEmbeddedEntries
)
596 auto nEntryCount
= rEmbeddedEntries
.size();
597 SvtSaveOptions::ODFSaneDefaultVersion eVersion
= m_rExport
.getSaneDefaultVersion();
598 for (decltype(nEntryCount
) nEntry
=0; nEntry
< nEntryCount
; ++nEntry
)
600 const SvXMLEmbeddedTextEntry
* pObj
= &rEmbeddedEntries
[nEntry
];
602 // position attribute
603 // position == 0 is between first integer digit and decimal separator
604 // position < 0 is inside decimal part
605 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_POSITION
,
606 OUString::number( pObj
->nFormatPos
) );
608 // text as element content
609 OUStringBuffer aContent
;
610 OUStringBuffer aBlankWidthString
;
613 pObj
= &rEmbeddedEntries
[nEntry
];
614 if ( pObj
->isBlankWidth
)
616 // (#i20396# the spaces may also be in embedded-text elements)
617 lcl_WriteBlankWidthString( pObj
->aText
, aBlankWidthString
, aContent
);
621 // The array can contain several elements for the same position in the number.
622 // Literal texts are merged into a single embedded-text element.
623 aContent
.append( pObj
->aText
);
627 while ( nEntry
< nEntryCount
628 && rEmbeddedEntries
[nEntry
].nFormatPos
== pObj
->nFormatPos
);
631 // Export only for 1.3 with extensions and later.
632 if ( !aBlankWidthString
.isEmpty() && eVersion
> SvtSaveOptions::ODFSVER_013
&& ( (eVersion
& SvtSaveOptions::ODFSVER_EXTENDED
) != 0 ) )
633 m_rExport
.AddAttribute( XML_NAMESPACE_LO_EXT
, XML_BLANK_WIDTH_CHAR
, aBlankWidthString
.makeStringAndClear() );
634 SvXMLElementExport
aChildElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_EMBEDDED_TEXT
,
636 m_rExport
.Characters( aContent
.makeStringAndClear() );
640 void SvXMLNumFmtExport::WriteNumberElement_Impl(
641 sal_Int32 nDecimals
, sal_Int32 nMinDecimals
,
642 sal_Int32 nInteger
, sal_Int32 nBlankInteger
, const OUString
& rDashStr
,
643 bool bGrouping
, sal_Int32 nTrailingThousands
,
644 const SvXMLEmbeddedTextEntryArr
& rEmbeddedEntries
)
646 FinishTextElement_Impl();
649 if ( nDecimals
>= 0 ) // negative = automatic
651 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_DECIMAL_PLACES
,
652 OUString::number( nDecimals
) );
655 if ( nMinDecimals
>= 0 ) // negative = automatic
657 // Export only for 1.2 with extensions or 1.3 and later.
658 SvtSaveOptions::ODFSaneDefaultVersion eVersion
= m_rExport
.getSaneDefaultVersion();
659 if (eVersion
> SvtSaveOptions::ODFSVER_012
)
661 // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
662 m_rExport
.AddAttribute(
663 ((eVersion
< SvtSaveOptions::ODFSVER_013
) ? XML_NAMESPACE_LO_EXT
: XML_NAMESPACE_NUMBER
),
664 XML_MIN_DECIMAL_PLACES
,
665 OUString::number( nMinDecimals
) );
668 // decimal replacement (dashes) or variable decimals (#)
669 if ( !rDashStr
.isEmpty() || nMinDecimals
< nDecimals
)
671 // full variable decimals means an empty replacement string
672 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_DECIMAL_REPLACEMENT
,
676 WriteIntegerElement_Impl( nInteger
, nBlankInteger
, bGrouping
);
678 // display-factor if there are trailing thousands separators
679 if ( nTrailingThousands
)
681 // each separator character removes three digits
682 double fFactor
= ::rtl::math::pow10Exp( 1.0, 3 * nTrailingThousands
);
684 OUStringBuffer aFactStr
;
685 ::sax::Converter::convertDouble( aFactStr
, fFactor
);
686 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_DISPLAY_FACTOR
, aFactStr
.makeStringAndClear() );
689 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_NUMBER
,
692 // number:embedded-text as child elements
693 WriteEmbeddedEntries_Impl( rEmbeddedEntries
);
696 void SvXMLNumFmtExport::WriteScientificElement_Impl(
697 sal_Int32 nDecimals
, sal_Int32 nMinDecimals
, sal_Int32 nInteger
, sal_Int32 nBlankInteger
,
698 bool bGrouping
, sal_Int32 nExp
, sal_Int32 nExpInterval
, bool bExpSign
, bool bExponentLowercase
, sal_Int32 nBlankExp
,
699 const SvXMLEmbeddedTextEntryArr
& rEmbeddedEntries
)
701 FinishTextElement_Impl();
704 if ( nDecimals
>= 0 ) // negative = automatic
706 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_DECIMAL_PLACES
,
707 OUString::number( nDecimals
) );
710 SvtSaveOptions::ODFSaneDefaultVersion eVersion
= m_rExport
.getSaneDefaultVersion();
711 if ( nMinDecimals
>= 0 ) // negative = automatic
713 // Export only for 1.2 with extensions or 1.3 and later.
714 if (eVersion
> SvtSaveOptions::ODFSVER_012
)
716 // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
717 m_rExport
.AddAttribute(
718 ((eVersion
< SvtSaveOptions::ODFSVER_013
) ? XML_NAMESPACE_LO_EXT
: XML_NAMESPACE_NUMBER
),
719 XML_MIN_DECIMAL_PLACES
,
720 OUString::number( nMinDecimals
) );
724 WriteIntegerElement_Impl( nInteger
, nBlankInteger
, bGrouping
);
729 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_MIN_EXPONENT_DIGITS
,
730 OUString::number( nExp
) );
733 // exponent interval for engineering notation
734 if ( nExpInterval
>= 0 )
736 // Export only for 1.2 with extensions or 1.3 and later.
737 if (eVersion
> SvtSaveOptions::ODFSVER_012
)
739 // OFFICE-1828 For 1.2+ use loext namespace, for 1.3 use number namespace.
740 m_rExport
.AddAttribute(
741 ((eVersion
< SvtSaveOptions::ODFSVER_013
) ? XML_NAMESPACE_LO_EXT
: XML_NAMESPACE_NUMBER
),
742 XML_EXPONENT_INTERVAL
, OUString::number( nExpInterval
) );
747 // Export only for 1.2 with extensions or 1.3 and later.
748 if (eVersion
> SvtSaveOptions::ODFSVER_012
)
750 // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
751 m_rExport
.AddAttribute(
752 ((eVersion
< SvtSaveOptions::ODFSVER_013
) ? XML_NAMESPACE_LO_EXT
: XML_NAMESPACE_NUMBER
),
753 XML_FORCED_EXPONENT_SIGN
,
754 bExpSign
? XML_TRUE
: XML_FALSE
);
757 // Export only for 1.x with extensions
758 if (eVersion
& SvtSaveOptions::ODFSVER_EXTENDED
)
760 if (bExponentLowercase
)
761 m_rExport
.AddAttribute( XML_NAMESPACE_LO_EXT
, XML_EXPONENT_LOWERCASE
, XML_TRUE
);
764 if (nBlankExp
>= nExp
)
765 nBlankExp
= nExp
- 1; // preserve at least one '0' in exponent
766 m_rExport
.AddAttribute( XML_NAMESPACE_LO_EXT
, XML_BLANK_EXPONENT_DIGITS
, OUString::number( nBlankExp
) );
770 SvXMLElementExport
aElem( m_rExport
,
771 XML_NAMESPACE_NUMBER
, XML_SCIENTIFIC_NUMBER
,
774 // number:embedded-text as child elements
775 // Export only for 1.x with extensions
776 if (eVersion
& SvtSaveOptions::ODFSVER_EXTENDED
)
777 WriteEmbeddedEntries_Impl( rEmbeddedEntries
);
780 void SvXMLNumFmtExport::WriteFractionElement_Impl(
781 sal_Int32 nInteger
, sal_Int32 nBlankInteger
, bool bGrouping
,
782 const SvNumberformat
& rFormat
, sal_uInt16 nPart
)
784 FinishTextElement_Impl();
785 WriteIntegerElement_Impl( nInteger
, nBlankInteger
, bGrouping
);
787 const OUString aNumeratorString
= rFormat
.GetNumeratorString( nPart
);
788 const OUString aDenominatorString
= rFormat
.GetDenominatorString( nPart
);
789 const OUString aIntegerFractionDelimiterString
= rFormat
.GetIntegerFractionDelimiterString( nPart
);
790 sal_Int32 nMaxNumeratorDigits
= aNumeratorString
.getLength();
792 sal_Int32 nMinNumeratorDigits
= aNumeratorString
.replaceAll("0","?").indexOf('?');
793 sal_Int32 nZerosNumeratorDigits
= aNumeratorString
.indexOf('0');
794 if ( nMinNumeratorDigits
>= 0 )
795 nMinNumeratorDigits
= nMaxNumeratorDigits
- nMinNumeratorDigits
;
797 nMinNumeratorDigits
= 0;
798 if ( nZerosNumeratorDigits
>= 0 )
799 nZerosNumeratorDigits
= nMaxNumeratorDigits
- nZerosNumeratorDigits
;
801 nZerosNumeratorDigits
= 0;
802 sal_Int32 nMaxDenominatorDigits
= aDenominatorString
.getLength();
803 sal_Int32 nMinDenominatorDigits
= aDenominatorString
.replaceAll("0","?").indexOf('?');
804 sal_Int32 nZerosDenominatorDigits
= aDenominatorString
.indexOf('0');
805 if ( nMinDenominatorDigits
>= 0 )
806 nMinDenominatorDigits
= nMaxDenominatorDigits
- nMinDenominatorDigits
;
808 nMinDenominatorDigits
= 0;
809 if ( nZerosDenominatorDigits
>= 0 )
810 nZerosDenominatorDigits
= nMaxDenominatorDigits
- nZerosDenominatorDigits
;
812 nZerosDenominatorDigits
= 0;
813 sal_Int32 nDenominator
= aDenominatorString
.toInt32();
815 SvtSaveOptions::ODFSaneDefaultVersion eVersion
= m_rExport
.getSaneDefaultVersion();
817 // integer/fraction delimiter
818 if ( !aIntegerFractionDelimiterString
.isEmpty() && aIntegerFractionDelimiterString
!= " "
819 && ((eVersion
& SvtSaveOptions::ODFSVER_EXTENDED
) != 0) )
820 { // Export only for 1.2/1.3 with extensions.
821 m_rExport
.AddAttribute( XML_NAMESPACE_LO_EXT
, XML_INTEGER_FRACTION_DELIMITER
,
822 aIntegerFractionDelimiterString
);
826 if ( nMinNumeratorDigits
== 0 ) // at least one digit to keep compatibility with previous versions
827 nMinNumeratorDigits
++;
828 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_MIN_NUMERATOR_DIGITS
,
829 OUString::number( nMinNumeratorDigits
) );
830 // Export only for 1.2/1.3 with extensions.
831 if ((eVersion
& SvtSaveOptions::ODFSVER_EXTENDED
) != 0)
833 // For extended ODF use loext namespace
834 m_rExport
.AddAttribute( XML_NAMESPACE_LO_EXT
, XML_MAX_NUMERATOR_DIGITS
,
835 OUString::number( nMaxNumeratorDigits
) );
837 if ( nZerosNumeratorDigits
&& ((eVersion
& SvtSaveOptions::ODFSVER_EXTENDED
) != 0) )
838 m_rExport
.AddAttribute( XML_NAMESPACE_LO_EXT
, XML_ZEROS_NUMERATOR_DIGITS
,
839 OUString::number( nZerosNumeratorDigits
) );
843 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_DENOMINATOR_VALUE
,
844 OUString::number( nDenominator
) );
846 // it's not necessary to export nDenominatorDigits
847 // if we have a forced denominator
850 if ( nMinDenominatorDigits
== 0 ) // at least one digit to keep compatibility with previous versions
851 nMinDenominatorDigits
++;
852 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_MIN_DENOMINATOR_DIGITS
,
853 OUString::number( nMinDenominatorDigits
) );
854 if (eVersion
> SvtSaveOptions::ODFSVER_012
)
856 // OFFICE-3695 For 1.2+ use loext namespace, for 1.3 use number namespace.
857 m_rExport
.AddAttribute(
858 ((eVersion
< SvtSaveOptions::ODFSVER_013
) ? XML_NAMESPACE_LO_EXT
: XML_NAMESPACE_NUMBER
),
859 XML_MAX_DENOMINATOR_VALUE
,
860 OUString::number( pow ( 10.0, nMaxDenominatorDigits
) - 1 ) ); // 9, 99 or 999
862 if ( nZerosDenominatorDigits
&& ((eVersion
& SvtSaveOptions::ODFSVER_EXTENDED
) != 0) )
863 m_rExport
.AddAttribute( XML_NAMESPACE_LO_EXT
, XML_ZEROS_DENOMINATOR_DIGITS
,
864 OUString::number( nZerosDenominatorDigits
) );
867 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_FRACTION
,
871 // mapping (condition)
873 void SvXMLNumFmtExport::WriteMapElement_Impl( sal_Int32 nOp
, double fLimit
,
874 sal_Int32 nKey
, sal_Int32 nPart
)
876 FinishTextElement_Impl();
878 if ( nOp
== NUMBERFORMAT_OP_NO
)
883 OUStringBuffer
aCondStr(20);
884 aCondStr
.append( "value()" ); //! define constant
887 case NUMBERFORMAT_OP_EQ
: aCondStr
.append( '=' ); break;
888 case NUMBERFORMAT_OP_NE
: aCondStr
.append( "!=" ); break;
889 case NUMBERFORMAT_OP_LT
: aCondStr
.append( '<' ); break;
890 case NUMBERFORMAT_OP_LE
: aCondStr
.append( "<=" ); break;
891 case NUMBERFORMAT_OP_GT
: aCondStr
.append( '>' ); break;
892 case NUMBERFORMAT_OP_GE
: aCondStr
.append( ">=" ); break;
894 OSL_FAIL("unknown operator");
896 ::rtl::math::doubleToUStringBuffer( aCondStr
, fLimit
,
897 rtl_math_StringFormat_Automatic
, rtl_math_DecimalPlaces_Max
,
900 m_rExport
.AddAttribute( XML_NAMESPACE_STYLE
, XML_CONDITION
,
901 aCondStr
.makeStringAndClear() );
903 m_rExport
.AddAttribute( XML_NAMESPACE_STYLE
, XML_APPLY_STYLE_NAME
,
904 m_rExport
.EncodeStyleName( lcl_CreateStyleName( nKey
, nPart
, false,
907 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_STYLE
, XML_MAP
,
911 // for old (automatic) currency formats: parse currency symbol from text
913 static sal_Int32
lcl_FindSymbol( const OUString
& sUpperStr
, std::u16string_view sCurString
)
915 // search for currency symbol
916 // Quoting as in ImpSvNumberformatScan::Symbol_Division
921 nCPos
= sUpperStr
.indexOf( sCurString
, nCPos
);
925 sal_Int32 nQ
= SvNumberformat::GetQuoteEnd( sUpperStr
, nCPos
);
928 // dm can be escaped as "dm or \d
931 return nCPos
; // found
932 c
= sUpperStr
[nCPos
-1];
933 if ( c
!= '"' && c
!= '\\')
935 return nCPos
; // found
944 nCPos
= nQ
+ 1; // continue after quote end
951 bool SvXMLNumFmtExport::WriteTextWithCurrency_Impl( const OUString
& rString
,
952 const css::lang::Locale
& rLocale
)
954 // returns true if currency element was written
958 LanguageTag
aLanguageTag( rLocale
);
959 m_pFormatter
->ChangeIntl( aLanguageTag
.getLanguageType( false) );
960 OUString sCurString
, sDummy
;
961 m_pFormatter
->GetCompatibilityCurrency( sCurString
, sDummy
);
963 OUString sUpperStr
= m_pFormatter
->GetCharClass()->uppercase(rString
);
964 sal_Int32 nPos
= lcl_FindSymbol( sUpperStr
, sCurString
);
967 sal_Int32 nLength
= rString
.getLength();
968 sal_Int32 nCurLen
= sCurString
.getLength();
969 sal_Int32 nCont
= nPos
+ nCurLen
;
971 // text before currency symbol
974 AddToTextElement_Impl( rString
.subView( 0, nPos
) );
976 // currency symbol (empty string -> default)
977 WriteCurrencyElement_Impl( u
""_ustr
, u
"" );
980 // text after currency symbol
981 if ( nCont
< nLength
)
983 AddToTextElement_Impl( rString
.subView( nCont
, nLength
-nCont
) );
988 AddToTextElement_Impl( rString
); // simple text
991 return bRet
; // true: currency element written
994 static OUString
lcl_GetDefaultCalendar( SvNumberFormatter
const * pFormatter
, LanguageType nLang
)
996 // get name of first non-gregorian calendar for the language
999 CalendarWrapper
* pCalendar
= pFormatter
->GetCalendar();
1002 lang::Locale
aLocale( LanguageTag::convertToLocale( nLang
) );
1004 const uno::Sequence
<OUString
> aCals
= pCalendar
->getAllCalendars( aLocale
);
1005 auto pCal
= std::find_if(aCals
.begin(), aCals
.end(),
1006 [](const OUString
& rCal
) { return rCal
!= "gregorian"; });
1007 if (pCal
!= aCals
.end())
1013 static bool lcl_IsInEmbedded( const SvXMLEmbeddedTextEntryArr
& rEmbeddedEntries
, sal_uInt16 nPos
)
1015 auto nCount
= rEmbeddedEntries
.size();
1016 for (decltype(nCount
) i
=0; i
<nCount
; i
++)
1017 if ( rEmbeddedEntries
[i
].nSourcePos
== nPos
)
1020 return false; // not found
1023 static bool lcl_IsDefaultDateFormat( const SvNumberformat
& rFormat
, bool bSystemDate
, NfIndexTableOffset eBuiltIn
)
1025 // make an extra loop to collect date elements, to check if it is a default format
1026 // before adding the automatic-order attribute
1028 SvXMLDateElementAttributes eDateDOW
= XML_DEA_NONE
;
1029 SvXMLDateElementAttributes eDateDay
= XML_DEA_NONE
;
1030 SvXMLDateElementAttributes eDateMonth
= XML_DEA_NONE
;
1031 SvXMLDateElementAttributes eDateYear
= XML_DEA_NONE
;
1032 SvXMLDateElementAttributes eDateHours
= XML_DEA_NONE
;
1033 SvXMLDateElementAttributes eDateMins
= XML_DEA_NONE
;
1034 SvXMLDateElementAttributes eDateSecs
= XML_DEA_NONE
;
1035 bool bDateNoDefault
= false;
1037 sal_uInt16 nPos
= 0;
1039 short nLastType
= 0;
1042 short nElemType
= rFormat
.GetNumForType( 0, nPos
);
1043 switch ( nElemType
)
1046 if ( nLastType
== NF_SYMBOLTYPE_STRING
)
1047 bDateNoDefault
= true; // text at the end -> no default date format
1048 bEnd
= true; // end of format reached
1050 case NF_SYMBOLTYPE_STRING
:
1051 case NF_SYMBOLTYPE_DATESEP
:
1052 case NF_SYMBOLTYPE_TIMESEP
:
1053 case NF_SYMBOLTYPE_TIME100SECSEP
:
1054 // text is ignored, except at the end
1056 // same mapping as in SvXMLNumFormatContext::AddNfKeyword:
1057 case NF_KEY_NN
: eDateDOW
= XML_DEA_SHORT
; break;
1059 case NF_KEY_NNNN
: eDateDOW
= XML_DEA_LONG
; break;
1060 case NF_KEY_D
: eDateDay
= XML_DEA_SHORT
; break;
1061 case NF_KEY_DD
: eDateDay
= XML_DEA_LONG
; break;
1062 case NF_KEY_M
: eDateMonth
= XML_DEA_SHORT
; break;
1063 case NF_KEY_MM
: eDateMonth
= XML_DEA_LONG
; break;
1064 case NF_KEY_MMM
: eDateMonth
= XML_DEA_TEXTSHORT
; break;
1065 case NF_KEY_MMMM
: eDateMonth
= XML_DEA_TEXTLONG
; break;
1066 case NF_KEY_YY
: eDateYear
= XML_DEA_SHORT
; break;
1067 case NF_KEY_YYYY
: eDateYear
= XML_DEA_LONG
; break;
1068 case NF_KEY_H
: eDateHours
= XML_DEA_SHORT
; break;
1069 case NF_KEY_HH
: eDateHours
= XML_DEA_LONG
; break;
1070 case NF_KEY_MI
: eDateMins
= XML_DEA_SHORT
; break;
1071 case NF_KEY_MMI
: eDateMins
= XML_DEA_LONG
; break;
1072 case NF_KEY_S
: eDateSecs
= XML_DEA_SHORT
; break;
1073 case NF_KEY_SS
: eDateSecs
= XML_DEA_LONG
; break;
1075 case NF_KEY_AMPM
: break; // AM/PM may or may not be in date/time formats -> ignore by itself
1077 bDateNoDefault
= true; // any other element -> no default format
1079 nLastType
= nElemType
;
1083 if ( bDateNoDefault
)
1084 return false; // additional elements
1087 NfIndexTableOffset eFound
= static_cast<NfIndexTableOffset
>(SvXMLNumFmtDefaults::GetDefaultDateFormat(
1088 eDateDOW
, eDateDay
, eDateMonth
, eDateYear
, eDateHours
, eDateMins
, eDateSecs
, bSystemDate
));
1090 return ( eFound
== eBuiltIn
);
1094 // export one part (condition)
1096 void SvXMLNumFmtExport::ExportPart_Impl( const SvNumberformat
& rFormat
, sal_uInt32 nKey
, sal_uInt32 nRealKey
,
1097 sal_uInt16 nPart
, bool bDefPart
)
1099 //! for the default part, pass the conditions from the other parts!
1103 NfIndexTableOffset eBuiltIn
= SvNumberFormatter::GetIndexTableOffset( nRealKey
);
1105 SvNumFormatType nFmtType
= SvNumFormatType::ALL
;
1106 bool bThousand
= false;
1107 sal_uInt16 nPrecision
= 0;
1108 sal_uInt16 nLeading
= 0;
1109 rFormat
.GetNumForInfo( nPart
, nFmtType
, bThousand
, nPrecision
, nLeading
);
1110 nFmtType
&= ~SvNumFormatType::DEFINED
;
1112 // special treatment of builtin formats that aren't detected by normal parsing
1113 // (the same formats that get the type set in SvNumberFormatter::ImpGenerateFormats)
1114 if ( eBuiltIn
== NF_NUMBER_STANDARD
)
1115 nFmtType
= SvNumFormatType::NUMBER
;
1116 else if ( eBuiltIn
== NF_BOOLEAN
)
1117 nFmtType
= SvNumFormatType::LOGICAL
;
1118 else if ( eBuiltIn
== NF_TEXT
)
1119 nFmtType
= SvNumFormatType::TEXT
;
1121 // #101606# An empty subformat is a valid number-style resulting in an
1122 // empty display string for the condition of the subformat.
1124 XMLTokenEnum eType
= XML_TOKEN_INVALID
;
1127 // Type UNDEFINED likely is a crappy format string for that we could
1128 // not decide on any format type (and maybe could try harder?), but the
1129 // resulting XMLTokenEnum should be something valid, so make that
1131 case SvNumFormatType::UNDEFINED
:
1132 SAL_WARN("xmloff.style","UNDEFINED number format: '" << rFormat
.GetFormatstring() << "'");
1134 // Type is 0 if a format contains no recognized elements
1135 // (like text only) - this is handled as a number-style.
1136 case SvNumFormatType::ALL
:
1137 case SvNumFormatType::EMPTY
:
1138 case SvNumFormatType::NUMBER
:
1139 case SvNumFormatType::SCIENTIFIC
:
1140 case SvNumFormatType::FRACTION
:
1141 eType
= XML_NUMBER_STYLE
;
1143 case SvNumFormatType::PERCENT
:
1144 eType
= XML_PERCENTAGE_STYLE
;
1146 case SvNumFormatType::CURRENCY
:
1147 eType
= XML_CURRENCY_STYLE
;
1149 case SvNumFormatType::DATE
:
1150 case SvNumFormatType::DATETIME
:
1151 eType
= XML_DATE_STYLE
;
1153 case SvNumFormatType::TIME
:
1154 eType
= XML_TIME_STYLE
;
1156 case SvNumFormatType::TEXT
:
1157 eType
= XML_TEXT_STYLE
;
1159 case SvNumFormatType::LOGICAL
:
1160 eType
= XML_BOOLEAN_STYLE
;
1164 SAL_WARN_IF( eType
== XML_TOKEN_INVALID
, "xmloff.style", "unknown format type" );
1166 OUString sAttrValue
;
1167 bool bUserDef( rFormat
.GetType() & SvNumFormatType::DEFINED
);
1169 // common attributes for format
1171 // format name (generated from key) - style namespace
1172 m_rExport
.AddAttribute( XML_NAMESPACE_STYLE
, XML_NAME
,
1173 lcl_CreateStyleName( nKey
, nPart
, bDefPart
, m_sPrefix
) );
1175 // "volatile" attribute for styles used only in maps
1177 m_rExport
.AddAttribute( XML_NAMESPACE_STYLE
, XML_VOLATILE
, XML_TRUE
);
1179 // language / country
1180 LanguageType nLang
= rFormat
.GetLanguage();
1181 AddLanguageAttr_Impl( nLang
); // adds to pAttrList
1184 // titles for builtin formats are not written
1185 sAttrValue
= rFormat
.GetComment();
1186 if ( !sAttrValue
.isEmpty() && bUserDef
&& bDefPart
)
1188 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_TITLE
, sAttrValue
);
1191 // automatic ordering for currency and date formats
1192 // only used for some built-in formats
1193 bool bAutoOrder
= ( eBuiltIn
== NF_CURRENCY_1000INT
|| eBuiltIn
== NF_CURRENCY_1000DEC2
||
1194 eBuiltIn
== NF_CURRENCY_1000INT_RED
|| eBuiltIn
== NF_CURRENCY_1000DEC2_RED
||
1195 eBuiltIn
== NF_CURRENCY_1000DEC2_DASHED
||
1196 eBuiltIn
== NF_DATE_SYSTEM_SHORT
|| eBuiltIn
== NF_DATE_SYSTEM_LONG
||
1197 eBuiltIn
== NF_DATE_SYS_MMYY
|| eBuiltIn
== NF_DATE_SYS_DDMMM
||
1198 eBuiltIn
== NF_DATE_SYS_DDMMYYYY
|| eBuiltIn
== NF_DATE_SYS_DDMMYY
||
1199 eBuiltIn
== NF_DATE_SYS_DMMMYY
|| eBuiltIn
== NF_DATE_SYS_DMMMYYYY
||
1200 eBuiltIn
== NF_DATE_SYS_DMMMMYYYY
|| eBuiltIn
== NF_DATE_SYS_NNDMMMYY
||
1201 eBuiltIn
== NF_DATE_SYS_NNDMMMMYYYY
|| eBuiltIn
== NF_DATE_SYS_NNNNDMMMMYYYY
||
1202 eBuiltIn
== NF_DATETIME_SYSTEM_SHORT_HHMM
|| eBuiltIn
== NF_DATETIME_SYS_DDMMYYYY_HHMM
||
1203 eBuiltIn
== NF_DATETIME_SYS_DDMMYYYY_HHMMSS
);
1205 // format source (for date and time formats)
1206 // only used for some built-in formats
1207 bool bSystemDate
= ( eBuiltIn
== NF_DATE_SYSTEM_SHORT
||
1208 eBuiltIn
== NF_DATE_SYSTEM_LONG
||
1209 eBuiltIn
== NF_DATETIME_SYSTEM_SHORT_HHMM
);
1210 bool bLongSysDate
= ( eBuiltIn
== NF_DATE_SYSTEM_LONG
);
1212 // check if the format definition matches the key
1213 if ( bAutoOrder
&& ( nFmtType
== SvNumFormatType::DATE
|| nFmtType
== SvNumFormatType::DATETIME
) &&
1214 !lcl_IsDefaultDateFormat( rFormat
, bSystemDate
, eBuiltIn
) )
1216 bAutoOrder
= bSystemDate
= bLongSysDate
= false; // don't write automatic-order attribute then
1220 ( nFmtType
== SvNumFormatType::CURRENCY
|| nFmtType
== SvNumFormatType::DATE
|| nFmtType
== SvNumFormatType::DATETIME
) )
1222 // #85109# format type must be checked to avoid dtd errors if
1223 // locale data contains other format types at the built-in positions
1225 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_AUTOMATIC_ORDER
,
1229 if ( bSystemDate
&& bAutoOrder
&&
1230 ( nFmtType
== SvNumFormatType::DATE
|| nFmtType
== SvNumFormatType::DATETIME
) )
1232 // #85109# format type must be checked to avoid dtd errors if
1233 // locale data contains other format types at the built-in positions
1235 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_FORMAT_SOURCE
,
1239 // overflow for time formats as in [hh]:mm
1240 // controlled by bThousand from number format info
1241 // default for truncate-on-overflow is true
1242 if ( nFmtType
== SvNumFormatType::TIME
&& bThousand
)
1244 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_TRUNCATE_ON_OVERFLOW
,
1248 // Native number transliteration
1249 css::i18n::NativeNumberXmlAttributes2 aAttr
;
1250 rFormat
.GetNatNumXml( aAttr
, nPart
, m_pFormatter
->GetNatNum() );
1251 if ( !aAttr
.Format
.isEmpty() )
1253 assert(aAttr
.Spellout
.isEmpty()); // mutually exclusive
1255 /* FIXME-BCP47: ODF defines no transliteration-script or
1256 * transliteration-rfc-language-tag */
1257 LanguageTag
aLanguageTag( aAttr
.Locale
);
1258 OUString aLanguage
, aScript
, aCountry
;
1259 aLanguageTag
.getIsoLanguageScriptCountry( aLanguage
, aScript
, aCountry
);
1260 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_TRANSLITERATION_FORMAT
,
1262 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_TRANSLITERATION_LANGUAGE
,
1264 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_TRANSLITERATION_COUNTRY
,
1266 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_TRANSLITERATION_STYLE
,
1270 SvtSaveOptions::ODFSaneDefaultVersion eVersion
= m_rExport
.getSaneDefaultVersion();
1271 if ( !aAttr
.Spellout
.isEmpty() )
1273 const bool bWriteSpellout
= aAttr
.Format
.isEmpty();
1274 assert(bWriteSpellout
); // mutually exclusive
1276 // Export only for 1.2 and later with extensions
1277 // Also ensure that duplicated transliteration-language and
1278 // transliteration-country attributes never escape into the wild with
1280 if ( (eVersion
& SvtSaveOptions::ODFSVER_EXTENDED
) && bWriteSpellout
)
1282 /* FIXME-BCP47: ODF defines no transliteration-script or
1283 * transliteration-rfc-language-tag */
1284 LanguageTag
aLanguageTag( aAttr
.Locale
);
1285 OUString aLanguage
, aScript
, aCountry
;
1286 aLanguageTag
.getIsoLanguageScriptCountry( aLanguage
, aScript
, aCountry
);
1287 // For 1.2/1.3+ use loext namespace.
1288 m_rExport
.AddAttribute( /*((eVersion < SvtSaveOptions::ODFSVER_)
1289 ? */ XML_NAMESPACE_LO_EXT
/*: XML_NAMESPACE_NUMBER)*/,
1290 XML_TRANSLITERATION_SPELLOUT
, aAttr
.Spellout
);
1291 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_TRANSLITERATION_LANGUAGE
,
1293 m_rExport
.AddAttribute( XML_NAMESPACE_NUMBER
, XML_TRANSLITERATION_COUNTRY
,
1299 SvXMLElementExport
aElem( m_rExport
, XML_NAMESPACE_NUMBER
, eType
,
1302 // color (properties element)
1304 const Color
* pCol
= rFormat
.GetColor( nPart
);
1306 WriteColorElement_Impl(*pCol
);
1308 // detect if there is "real" content, excluding color and maps
1309 //! move to implementation of Write... methods?
1310 bool bAnyContent
= false;
1314 SvXMLEmbeddedTextEntryArr aEmbeddedEntries
;
1315 if ( eBuiltIn
== NF_NUMBER_STANDARD
)
1317 // default number format contains just one number element
1318 WriteNumberElement_Impl( -1, -1, 1, -1, OUString(), false, 0, aEmbeddedEntries
);
1321 else if ( eBuiltIn
== NF_BOOLEAN
)
1323 // boolean format contains just one boolean element
1324 WriteBooleanElement_Impl();
1327 else if (eType
== XML_BOOLEAN_STYLE
)
1329 // <number:boolean-style> may contain only <number:boolean> and
1330 // <number:text> elements.
1331 sal_uInt16 nPos
= 0;
1335 const short nElemType
= rFormat
.GetNumForType( nPart
, nPos
);
1339 bEnd
= true; // end of format reached
1340 if (m_bHasText
&& m_sTextContent
.isEmpty())
1341 m_bHasText
= false; // don't write trailing empty text
1343 case NF_SYMBOLTYPE_STRING
:
1345 const OUString
* pElemStr
= rFormat
.GetNumForString( nPart
, nPos
);
1347 AddToTextElement_Impl( *pElemStr
);
1350 case NF_KEY_BOOLEAN
:
1351 WriteBooleanElement_Impl();
1360 // first loop to collect attributes
1362 bool bDecDashes
= false;
1363 bool bExpFound
= false;
1364 bool bCurrFound
= false;
1365 bool bInInteger
= true;
1366 bool bExpSign
= true;
1367 bool bExponentLowercase
= false; // 'e' or 'E' for scientific notation
1368 bool bDecAlign
= false; // decimal alignment with "?"
1369 sal_Int32 nExpDigits
= 0; // '0' and '?' in exponent
1370 sal_Int32 nBlankExp
= 0; // only '?' in exponent
1371 sal_Int32 nIntegerSymbols
= 0; // for embedded-text, including "#"
1372 sal_Int32 nTrailingThousands
= 0; // thousands-separators after all digits
1373 sal_Int32 nMinDecimals
= nPrecision
;
1374 sal_Int32 nBlankInteger
= 0;
1377 bool bImplicitOtherCalendar
= false;
1378 bool bExplicitCalendar
= false;
1379 sal_uInt16 nPos
= 0;
1383 short nElemType
= rFormat
.GetNumForType( nPart
, nPos
);
1384 const OUString
* pElemStr
= rFormat
.GetNumForString( nPart
, nPos
);
1386 switch ( nElemType
)
1389 bEnd
= true; // end of format reached
1391 case NF_SYMBOLTYPE_DIGIT
:
1392 if ( bExpFound
&& pElemStr
)
1394 nExpDigits
+= pElemStr
->getLength();
1395 for ( sal_Int32 i
= pElemStr
->getLength()-1; i
>= 0 ; i
-- )
1397 if ( (*pElemStr
)[i
] == '?' )
1401 else if ( !bDecDashes
&& pElemStr
&& (*pElemStr
)[0] == '-' )
1406 else if ( nFmtType
!= SvNumFormatType::FRACTION
&& !bInInteger
&& pElemStr
)
1408 for ( sal_Int32 i
= pElemStr
->getLength()-1; i
>= 0 ; i
-- )
1410 sal_Unicode aChar
= (*pElemStr
)[i
];
1411 if ( aChar
== '#' || aChar
== '?' )
1421 if ( bInInteger
&& pElemStr
)
1423 nIntegerSymbols
+= pElemStr
->getLength();
1424 for ( sal_Int32 i
= pElemStr
->getLength()-1; i
>= 0 ; i
-- )
1426 if ( (*pElemStr
)[i
] == '?' )
1430 nTrailingThousands
= 0;
1432 case NF_SYMBOLTYPE_FRACBLANK
:
1433 case NF_SYMBOLTYPE_DECSEP
:
1436 case NF_SYMBOLTYPE_THSEP
:
1438 nTrailingThousands
+= pElemStr
->getLength(); // is reset to 0 if digits follow
1440 case NF_SYMBOLTYPE_EXP
:
1441 bExpFound
= true; // following digits are exponent digits
1443 if ( pElemStr
&& ( pElemStr
->getLength() == 1
1444 || ( pElemStr
->getLength() == 2 && (*pElemStr
)[1] == '-' ) ) )
1445 bExpSign
= false; // for 0.00E0 or 0.00E-00
1446 if ( pElemStr
&& (*pElemStr
)[0] == 'e' )
1447 bExponentLowercase
= true; // for 0.00e+00
1449 case NF_SYMBOLTYPE_CURRENCY
:
1452 case NF_SYMBOLTYPE_CURREXT
:
1454 sCurrExt
= *pElemStr
;
1457 // E, EE, R, RR: select non-gregorian calendar
1458 // AAA, AAAA: calendar is switched at the position of the element
1463 if (aCalendar
.isEmpty())
1465 aCalendar
= lcl_GetDefaultCalendar( m_pFormatter
, nLang
);
1466 bImplicitOtherCalendar
= true;
1473 // collect strings for embedded-text (must be known before number element is written)
1474 bool bAllowEmbedded
= ( nFmtType
== SvNumFormatType::ALL
|| nFmtType
== SvNumFormatType::NUMBER
||
1475 nFmtType
== SvNumFormatType::CURRENCY
||
1476 // Export only for 1.x with extensions
1477 ( nFmtType
== SvNumFormatType::SCIENTIFIC
&& (eVersion
& SvtSaveOptions::ODFSVER_EXTENDED
) )||
1478 nFmtType
== SvNumFormatType::PERCENT
);
1479 if ( bAllowEmbedded
)
1481 sal_Int32 nDigitsPassed
= 0;
1482 sal_Int32 nEmbeddedPositionsMax
= nIntegerSymbols
;
1483 // Enable embedded text in decimal part only if there's a decimal part
1485 nEmbeddedPositionsMax
+= nPrecision
+ 1;
1486 // Enable embedded text in exponent in scientific number
1487 if ( nFmtType
== SvNumFormatType::SCIENTIFIC
)
1488 nEmbeddedPositionsMax
+= 1 + nExpDigits
;
1494 short nElemType
= rFormat
.GetNumForType( nPart
, nPos
);
1495 const OUString
* pElemStr
= rFormat
.GetNumForString( nPart
, nPos
);
1497 switch ( nElemType
)
1500 bEnd
= true; // end of format reached
1502 case NF_SYMBOLTYPE_DIGIT
:
1504 nDigitsPassed
+= pElemStr
->getLength();
1506 case NF_SYMBOLTYPE_EXP
:
1509 case NF_SYMBOLTYPE_DECSEP
:
1512 case NF_SYMBOLTYPE_STRING
:
1513 case NF_SYMBOLTYPE_BLANK
:
1514 case NF_SYMBOLTYPE_PERCENT
:
1515 if ( 0 < nDigitsPassed
&& nDigitsPassed
< nEmbeddedPositionsMax
&& pElemStr
)
1517 // text (literal or underscore) within the integer (>=0) or decimal (<0) part of a number:number element
1519 OUString aEmbeddedStr
;
1520 bool bSaveBlankWidthSymbol
= false;
1521 if ( nElemType
== NF_SYMBOLTYPE_STRING
|| nElemType
== NF_SYMBOLTYPE_PERCENT
)
1523 aEmbeddedStr
= *pElemStr
;
1525 else if (pElemStr
->getLength() >= 2)
1527 if ( eVersion
> SvtSaveOptions::ODFSVER_013
&& ( (eVersion
& SvtSaveOptions::ODFSVER_EXTENDED
) != 0 ) )
1529 aEmbeddedStr
= pElemStr
->copy( 1, 1 );
1530 bSaveBlankWidthSymbol
= true;
1532 else // turn "_x" into the number of spaces used for x in InsertBlanks in the NumberFormat
1533 SvNumberformat::InsertBlanks( aEmbeddedStr
, 0, (*pElemStr
)[1] );
1535 sal_Int32 nEmbedPos
= nIntegerSymbols
- nDigitsPassed
;
1537 aEmbeddedEntries
.push_back(
1538 SvXMLEmbeddedTextEntry( nPos
, nEmbedPos
, aEmbeddedStr
, bSaveBlankWidthSymbol
));
1539 // exponent sign is required with embedded text in exponent
1540 if ( bExpFound
&& !bExpSign
)
1549 // final loop to write elements
1551 bool bNumWritten
= false;
1552 bool bCurrencyWritten
= false;
1553 short nPrevType
= 0;
1558 short nElemType
= rFormat
.GetNumForType( nPart
, nPos
);
1559 const OUString
* pElemStr
= rFormat
.GetNumForString( nPart
, nPos
);
1561 switch ( nElemType
)
1564 bEnd
= true; // end of format reached
1565 if (m_bHasText
&& m_sTextContent
.isEmpty())
1566 m_bHasText
= false; // don't write trailing empty text
1568 case NF_SYMBOLTYPE_STRING
:
1569 case NF_SYMBOLTYPE_DATESEP
:
1570 case NF_SYMBOLTYPE_TIMESEP
:
1571 case NF_SYMBOLTYPE_TIME100SECSEP
:
1572 case NF_SYMBOLTYPE_PERCENT
:
1575 if ( ( nElemType
== NF_SYMBOLTYPE_TIME100SECSEP
) &&
1576 ( nPrevType
== NF_KEY_S
|| nPrevType
== NF_KEY_SS
||
1577 ( nPos
> 0 && (*rFormat
.GetNumForString( nPart
, nPos
-1 ))[0] == ']' &&
1578 ( nFmtType
== SvNumFormatType::TIME
|| nFmtType
== SvNumFormatType::DATETIME
) ) ) &&
1581 // decimal separator after seconds or [SS] is implied by
1582 // "decimal-places" attribute and must not be written
1584 //! difference between '.' and ',' is lost here
1586 else if ( lcl_IsInEmbedded( aEmbeddedEntries
, nPos
) )
1588 // text is written as embedded-text child of the number,
1589 // don't create a text element
1591 else if ( nFmtType
== SvNumFormatType::CURRENCY
&& !bCurrFound
&& !bCurrencyWritten
)
1593 // automatic currency symbol is implemented as part of
1594 // normal text -> search for the symbol
1595 bCurrencyWritten
= WriteTextWithCurrency_Impl( *pElemStr
,
1596 LanguageTag::convertToLocale( nLang
) );
1600 AddToTextElement_Impl( *pElemStr
);
1603 case NF_SYMBOLTYPE_BLANK
:
1604 if ( pElemStr
&& !lcl_IsInEmbedded( aEmbeddedEntries
, nPos
) )
1606 if ( pElemStr
->getLength() == 2 )
1608 OUString aBlankWidthChar
= pElemStr
->copy( 1 );
1609 lcl_WriteBlankWidthString( aBlankWidthChar
, m_sBlankWidthString
, m_sTextContent
);
1614 case NF_KEY_GENERAL
:
1615 WriteNumberElement_Impl( -1, -1, 1, -1, OUString(), false, 0, aEmbeddedEntries
);
1621 if ( bCurrencyWritten
)
1622 AddToTextElement_Impl( *pElemStr
); // never more than one currency element
1625 //! must be different from short automatic format
1626 //! but should still be empty (meaning automatic)
1627 // pElemStr is "CCC"
1629 WriteCurrencyElement_Impl( *pElemStr
, u
"" );
1631 bCurrencyWritten
= true;
1635 case NF_SYMBOLTYPE_CURRENCY
:
1638 if ( bCurrencyWritten
)
1639 AddToTextElement_Impl( *pElemStr
); // never more than one currency element
1642 WriteCurrencyElement_Impl( *pElemStr
, sCurrExt
);
1644 bCurrencyWritten
= true;
1648 case NF_SYMBOLTYPE_DIGIT
:
1649 if (!bNumWritten
) // write number part
1653 // for type 0 (not recognized as a special type),
1654 // write a "normal" number
1655 case SvNumFormatType::ALL
:
1656 case SvNumFormatType::NUMBER
:
1657 case SvNumFormatType::CURRENCY
:
1658 case SvNumFormatType::PERCENT
:
1661 // only some built-in formats have automatic decimals
1662 sal_Int32 nDecimals
= nPrecision
; // from GetFormatSpecialInfo
1663 if ( eBuiltIn
== NF_NUMBER_STANDARD
||
1664 eBuiltIn
== NF_CURRENCY_1000DEC2
||
1665 eBuiltIn
== NF_CURRENCY_1000DEC2_RED
||
1666 eBuiltIn
== NF_CURRENCY_1000DEC2_CCC
||
1667 eBuiltIn
== NF_CURRENCY_1000DEC2_DASHED
)
1671 // only one built-in format has automatic integer digits
1672 sal_Int32 nInteger
= nLeading
;
1673 if ( eBuiltIn
== NF_NUMBER_SYSTEM
)
1679 // string for decimal replacement
1680 // has to be taken from nPrecision
1681 // (positive number even for automatic decimals)
1682 OUStringBuffer sDashStr
;
1683 if (bDecDashes
&& nPrecision
> 0)
1684 comphelper::string::padToLength(sDashStr
, nPrecision
, '-');
1685 // "?" in decimal part are replaced by space character
1686 if (bDecAlign
&& nPrecision
> 0)
1689 WriteNumberElement_Impl(nDecimals
, nMinDecimals
, nInteger
, nBlankInteger
, sDashStr
.makeStringAndClear(),
1690 bThousand
, nTrailingThousands
, aEmbeddedEntries
);
1694 case SvNumFormatType::SCIENTIFIC
:
1695 // #i43959# for scientific numbers, count all integer symbols ("0", "?" and "#")
1696 // as integer digits: use nIntegerSymbols instead of nLeading
1697 // nIntegerSymbols represents exponent interval (for engineering notation)
1698 WriteScientificElement_Impl( nPrecision
, nMinDecimals
, nLeading
, nBlankInteger
, bThousand
, nExpDigits
, nIntegerSymbols
, bExpSign
,
1699 bExponentLowercase
, nBlankExp
, aEmbeddedEntries
);
1702 case SvNumFormatType::FRACTION
:
1704 sal_Int32 nInteger
= nLeading
;
1705 if ( rFormat
.GetNumForNumberElementCount( nPart
) == 3 )
1707 // If there is only two numbers + fraction in format string
1708 // the fraction doesn't have an integer part, and no
1709 // min-integer-digits attribute must be written.
1713 WriteFractionElement_Impl( nInteger
, nBlankInteger
, bThousand
, rFormat
, nPart
);
1723 case NF_SYMBOLTYPE_DECSEP
:
1724 if ( pElemStr
&& nPrecision
== 0 )
1726 // A decimal separator after the number, without following decimal digits,
1727 // isn't modelled as part of the number element, so it's written as text
1728 // (the distinction between a quoted and non-quoted, locale-dependent
1729 // character is lost here).
1731 AddToTextElement_Impl( *pElemStr
);
1734 case NF_SYMBOLTYPE_DEL
:
1735 if ( pElemStr
&& *pElemStr
== "@" )
1737 WriteTextContentElement_Impl();
1742 case NF_SYMBOLTYPE_CALENDAR
:
1745 aCalendar
= *pElemStr
;
1746 bExplicitCalendar
= true;
1755 bool bLong
= ( nElemType
== NF_KEY_DD
);
1756 WriteDayElement_Impl( aCalendar
, ( bSystemDate
? bLongSysDate
: bLong
) );
1768 OUString aCalAttr
= aCalendar
;
1769 if ( nElemType
== NF_KEY_AAA
|| nElemType
== NF_KEY_AAAA
)
1771 // calendar attribute for AAA and AAAA is switched only for this element
1772 if (aCalAttr
.isEmpty())
1773 aCalAttr
= lcl_GetDefaultCalendar( m_pFormatter
, nLang
);
1776 bool bLong
= ( nElemType
== NF_KEY_NNN
|| nElemType
== NF_KEY_NNNN
||
1777 nElemType
== NF_KEY_DDDD
|| nElemType
== NF_KEY_AAAA
);
1778 WriteDayOfWeekElement_Impl( aCalAttr
, ( bSystemDate
? bLongSysDate
: bLong
) );
1780 if ( nElemType
== NF_KEY_NNNN
)
1782 // write additional text element for separator
1783 m_pLocaleData
.reset( new LocaleDataWrapper( m_pFormatter
->GetComponentContext(),
1784 LanguageTag( nLang
) ) );
1785 AddToTextElement_Impl( m_pLocaleData
->getLongDateDayOfWeekSep() );
1793 case NF_KEY_MMMMM
: //! first letter of month name, no attribute available
1795 bool bLong
= ( nElemType
== NF_KEY_MM
|| nElemType
== NF_KEY_MMMM
);
1796 bool bText
= ( nElemType
== NF_KEY_MMM
|| nElemType
== NF_KEY_MMMM
||
1797 nElemType
== NF_KEY_MMMMM
);
1798 WriteMonthElement_Impl( aCalendar
, ( bSystemDate
? bLongSysDate
: bLong
), bText
);
1806 case NF_KEY_R
: //! R acts as EE, no attribute available
1808 //! distinguish EE and R
1809 // Calendar attribute for E and EE and R is set in
1810 // first loop. If set and not an explicit calendar and
1811 // YY or YYYY is encountered, switch temporarily to
1813 bool bLong
= ( nElemType
== NF_KEY_YYYY
|| nElemType
== NF_KEY_EEC
||
1814 nElemType
== NF_KEY_R
);
1815 WriteYearElement_Impl(
1816 ((bImplicitOtherCalendar
&& !bExplicitCalendar
1817 && (nElemType
== NF_KEY_YY
|| nElemType
== NF_KEY_YYYY
)) ? u
"gregorian"_ustr
: aCalendar
),
1818 (bSystemDate
? bLongSysDate
: bLong
));
1825 case NF_KEY_RR
: //! RR acts as GGGEE, no attribute available
1827 //! distinguish GG and GGG and RR
1828 bool bLong
= ( nElemType
== NF_KEY_GGG
|| nElemType
== NF_KEY_RR
);
1829 WriteEraElement_Impl( aCalendar
, ( bSystemDate
? bLongSysDate
: bLong
) );
1831 if ( nElemType
== NF_KEY_RR
)
1833 // calendar attribute for RR is set in first loop
1834 WriteYearElement_Impl( aCalendar
, ( bSystemDate
|| bLongSysDate
) );
1841 bool bLong
= ( nElemType
== NF_KEY_QQ
);
1842 WriteQuarterElement_Impl( aCalendar
, ( bSystemDate
? bLongSysDate
: bLong
) );
1847 WriteWeekElement_Impl( aCalendar
);
1851 // time elements (bSystemDate is not used):
1855 WriteHoursElement_Impl( nElemType
== NF_KEY_HH
);
1860 WriteMinutesElement_Impl( nElemType
== NF_KEY_MMI
);
1865 WriteSecondsElement_Impl( ( nElemType
== NF_KEY_SS
), nPrecision
);
1870 WriteAMPMElement_Impl(); // short/long?
1873 case NF_SYMBOLTYPE_STAR
:
1874 // export only if ODF 1.2 extensions are enabled
1875 if (m_rExport
.getSaneDefaultVersion() > SvtSaveOptions::ODFSVER_012
)
1877 if ( pElemStr
&& pElemStr
->getLength() > 1 )
1878 WriteRepeatedElement_Impl( (*pElemStr
)[1] );
1882 nPrevType
= nElemType
;
1887 if ( !m_sTextContent
.isEmpty() )
1888 bAnyContent
= true; // element written in FinishTextElement_Impl
1890 FinishTextElement_Impl(); // final text element - before maps
1894 // for an empty format, write an empty text element
1895 SvXMLElementExport
aTElem( m_rExport
, XML_NAMESPACE_NUMBER
, XML_TEXT
,
1899 // mapping (conditions) must be last elements
1904 SvNumberformatLimitOps eOp1
, eOp2
;
1905 double fLimit1
, fLimit2
;
1906 rFormat
.GetConditions( eOp1
, fLimit1
, eOp2
, fLimit2
);
1908 WriteMapElement_Impl( eOp1
, fLimit1
, nKey
, 0 );
1909 WriteMapElement_Impl( eOp2
, fLimit2
, nKey
, 1 );
1911 if ( !rFormat
.HasTextFormat() )
1914 // 4th part is for text -> make an "all other numbers" condition for the 3rd part
1915 // by reversing the 2nd condition.
1916 // For a trailing text format like 0;@ that has no conditions
1917 // use a "less or equal than biggest" condition for the number
1918 // part, ODF can't store subformats (style maps) without
1921 SvNumberformatLimitOps eOp3
= NUMBERFORMAT_OP_NO
;
1922 double fLimit3
= fLimit2
;
1923 sal_uInt16 nLastPart
= 2;
1924 SvNumberformatLimitOps eOpLast
= eOp2
;
1925 if (eOp2
== NUMBERFORMAT_OP_NO
)
1929 nLastPart
= (eOp1
== NUMBERFORMAT_OP_NO
) ? 0 : 1;
1933 case NUMBERFORMAT_OP_EQ
: eOp3
= NUMBERFORMAT_OP_NE
; break;
1934 case NUMBERFORMAT_OP_NE
: eOp3
= NUMBERFORMAT_OP_EQ
; break;
1935 case NUMBERFORMAT_OP_LT
: eOp3
= NUMBERFORMAT_OP_GE
; break;
1936 case NUMBERFORMAT_OP_LE
: eOp3
= NUMBERFORMAT_OP_GT
; break;
1937 case NUMBERFORMAT_OP_GT
: eOp3
= NUMBERFORMAT_OP_LE
; break;
1938 case NUMBERFORMAT_OP_GE
: eOp3
= NUMBERFORMAT_OP_LT
; break;
1939 case NUMBERFORMAT_OP_NO
: eOp3
= NUMBERFORMAT_OP_LE
; fLimit3
= DBL_MAX
; break;
1942 if ( fLimit1
== fLimit2
&&
1943 ( ( eOp1
== NUMBERFORMAT_OP_LT
&& eOp2
== NUMBERFORMAT_OP_GT
) ||
1944 ( eOp1
== NUMBERFORMAT_OP_GT
&& eOp2
== NUMBERFORMAT_OP_LT
) ) )
1946 // For <x and >x, add =x as last condition
1947 // (just for readability, <=x would be valid, too)
1949 eOp3
= NUMBERFORMAT_OP_EQ
;
1952 WriteMapElement_Impl( eOp3
, fLimit3
, nKey
, nLastPart
);
1955 // export one format
1957 void SvXMLNumFmtExport::ExportFormat_Impl( const SvNumberformat
& rFormat
, sal_uInt32 nKey
, sal_uInt32 nRealKey
)
1959 const sal_uInt16 XMLNUM_MAX_PARTS
= 4;
1960 bool bParts
[XMLNUM_MAX_PARTS
] = { false, false, false, false };
1961 sal_uInt16 nUsedParts
= 0;
1962 for (sal_uInt16 nPart
=0; nPart
<XMLNUM_MAX_PARTS
; ++nPart
)
1964 if (rFormat
.GetNumForInfoScannedType( nPart
) != SvNumFormatType::UNDEFINED
)
1966 bParts
[nPart
] = true;
1967 nUsedParts
= nPart
+ 1;
1971 SvNumberformatLimitOps eOp1
, eOp2
;
1972 double fLimit1
, fLimit2
;
1973 rFormat
.GetConditions( eOp1
, fLimit1
, eOp2
, fLimit2
);
1975 // if conditions are set, even empty formats must be written
1977 if ( eOp1
!= NUMBERFORMAT_OP_NO
)
1983 if ( eOp2
!= NUMBERFORMAT_OP_NO
)
1989 if ( rFormat
.HasTextFormat() )
1996 for (sal_uInt16 nPart
=0; nPart
<XMLNUM_MAX_PARTS
; ++nPart
)
2000 bool bDefault
= ( nPart
+1 == nUsedParts
); // last = default
2001 ExportPart_Impl( rFormat
, nKey
, nRealKey
, nPart
, bDefault
);
2006 // export method called by application
2008 void SvXMLNumFmtExport::Export( bool bIsAutoStyle
)
2010 if ( !m_pFormatter
)
2011 return; // no formatter -> no entries
2014 const SvNumberformat
* pFormat
= nullptr;
2015 bool bNext(m_pUsedList
->GetFirstUsed(nKey
));
2018 // ODF has its notation of system formats, so obtain the "real" already
2019 // substituted format but use the original key for style name.
2020 sal_uInt32 nRealKey
= nKey
;
2021 pFormat
= m_pFormatter
->GetSubstitutedEntry( nKey
, nRealKey
);
2023 ExportFormat_Impl( *pFormat
, nKey
, nRealKey
);
2024 bNext
= m_pUsedList
->GetNextUsed(nKey
);
2028 std::vector
<LanguageType
> aLanguages
;
2029 m_pFormatter
->GetUsedLanguages( aLanguages
);
2030 for (const auto& nLang
: aLanguages
)
2032 sal_uInt32 nDefaultIndex
= 0;
2033 SvNumberFormatTable
& rTable
= m_pFormatter
->GetEntryTable(
2034 SvNumFormatType::DEFINED
, nDefaultIndex
, nLang
);
2035 for (const auto& rTableEntry
: rTable
)
2037 nKey
= rTableEntry
.first
;
2038 pFormat
= rTableEntry
.second
;
2039 if (!m_pUsedList
->IsUsed(nKey
))
2041 DBG_ASSERT((pFormat
->GetType() & SvNumFormatType::DEFINED
), "a not user defined numberformat found");
2042 sal_uInt32 nRealKey
= nKey
;
2043 if (pFormat
->IsSubstituted())
2045 pFormat
= m_pFormatter
->GetSubstitutedEntry( nKey
, nRealKey
); // export the "real" format
2048 // user-defined and used formats are exported
2049 ExportFormat_Impl( *pFormat
, nKey
, nRealKey
);
2050 // if it is a user-defined Format it will be added else nothing will happen
2051 m_pUsedList
->SetUsed(nKey
);
2056 m_pUsedList
->Export();
2059 OUString
SvXMLNumFmtExport::GetStyleName( sal_uInt32 nKey
)
2061 if(m_pUsedList
->IsUsed(nKey
) || m_pUsedList
->IsWasUsed(nKey
))
2062 return lcl_CreateStyleName( nKey
, 0, true, m_sPrefix
);
2065 OSL_FAIL("There is no written Data-Style");
2070 void SvXMLNumFmtExport::SetUsed( sal_uInt32 nKey
)
2072 SAL_WARN_IF( m_pFormatter
== nullptr, "xmloff.style", "missing formatter" );
2076 if (m_pFormatter
->GetEntry(nKey
))
2077 m_pUsedList
->SetUsed( nKey
);
2079 OSL_FAIL("no existing Numberformat found with this key");
2083 uno::Sequence
<sal_Int32
> SvXMLNumFmtExport::GetWasUsed() const
2086 return m_pUsedList
->GetWasUsed();
2087 return uno::Sequence
<sal_Int32
>();
2090 void SvXMLNumFmtExport::SetWasUsed(const uno::Sequence
<sal_Int32
>& rWasUsed
)
2093 m_pUsedList
->SetWasUsed(rWasUsed
);
2096 static const SvNumberformat
* lcl_GetFormat( SvNumberFormatter
const * pFormatter
,
2099 return ( pFormatter
!= nullptr ) ? pFormatter
->GetEntry( nKey
) : nullptr;
2102 sal_uInt32
SvXMLNumFmtExport::ForceSystemLanguage( sal_uInt32 nKey
)
2104 sal_uInt32 nRet
= nKey
;
2106 const SvNumberformat
* pFormat
= lcl_GetFormat( m_pFormatter
, nKey
);
2107 if( pFormat
!= nullptr )
2109 SAL_WARN_IF( m_pFormatter
== nullptr, "xmloff.style", "format without formatter?" );
2111 SvNumFormatType nType
= pFormat
->GetType();
2113 sal_uInt32 nNewKey
= m_pFormatter
->GetFormatForLanguageIfBuiltIn(
2114 nKey
, LANGUAGE_SYSTEM
);
2116 if( nNewKey
!= nKey
)
2122 OUString
aFormatString( pFormat
->GetFormatstring() );
2123 sal_Int32 nErrorPos
;
2124 m_pFormatter
->PutandConvertEntry(
2126 nErrorPos
, nType
, nNewKey
,
2127 pFormat
->GetLanguage(), LANGUAGE_SYSTEM
, true);
2129 // success? Then use new key.
2130 if( nErrorPos
== 0 )
2138 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */