tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / xmloff / source / style / xmlnumfe.cxx
blobf09d1e94942b541b6900a24f553725a519ed1173
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <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>
41 #include <utility>
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>
51 #include <float.h>
52 #include <set>
53 #include <string_view>
54 #include <vector>
56 using namespace ::com::sun::star;
57 using namespace ::xmloff::token;
58 using namespace ::svt;
60 typedef std::set< sal_uInt32 > SvXMLuInt32Set;
62 namespace {
64 struct SvXMLEmbeddedTextEntry
66 sal_uInt16 nSourcePos; // position in NumberFormat (to skip later)
67 sal_Int32 nFormatPos; // resulting position in embedded-text element
68 OUString aText;
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;
80 DataType maData;
82 public:
84 void push_back( SvXMLEmbeddedTextEntry const& r )
86 maData.push_back(r);
89 const SvXMLEmbeddedTextEntry& operator[] ( size_t i ) const
91 return maData[i];
94 size_t size() const
96 return maData.size();
100 class SvXMLNumUsedList_Impl
102 SvXMLuInt32Set aUsed;
103 SvXMLuInt32Set aWasUsed;
104 SvXMLuInt32Set::iterator aCurrentUsedPos;
105 sal_uInt32 nUsedCount;
106 sal_uInt32 nWasUsedCount;
108 public:
109 SvXMLNumUsedList_Impl();
111 void SetUsed( sal_uInt32 nKey );
112 bool IsUsed( sal_uInt32 nKey ) const;
113 bool IsWasUsed( sal_uInt32 nKey ) const;
114 void Export();
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() :
126 nUsedCount(0),
127 nWasUsedCount(0)
131 void SvXMLNumUsedList_Impl::SetUsed( sal_uInt32 nKey )
133 if ( !IsWasUsed(nKey) )
135 std::pair<SvXMLuInt32Set::iterator, bool> aPair = aUsed.insert( nKey );
136 if (aPair.second)
137 nUsedCount++;
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 );
159 if (aPair.second)
160 nWasUsedCount++;
161 ++aItr;
163 aUsed.clear();
164 nUsedCount = 0;
167 bool SvXMLNumUsedList_Impl::GetFirstUsed(sal_uInt32& nKey)
169 bool bRet(false);
170 aCurrentUsedPos = aUsed.begin();
171 if(nUsedCount)
173 DBG_ASSERT(aCurrentUsedPos != aUsed.end(), "something went wrong");
174 nKey = *aCurrentUsedPos;
175 bRet = true;
177 return bRet;
180 bool SvXMLNumUsedList_Impl::GetNextUsed(sal_uInt32& nKey)
182 bool bRet(false);
183 if (aCurrentUsedPos != aUsed.end())
185 ++aCurrentUsedPos;
186 if (aCurrentUsedPos != aUsed.end())
188 nKey = *aCurrentUsedPos;
189 bRet = true;
192 return bRet;
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 );
206 if (aPair.second)
207 nWasUsedCount++;
211 SvXMLNumFmtExport::SvXMLNumFmtExport(
212 SvXMLExport& rExp,
213 const uno::Reference< util::XNumberFormatsSupplier >& rSupp ) :
214 m_rExport( rExp ),
215 m_sPrefix( u"N"_ustr ),
216 m_pFormatter( nullptr ),
217 m_bHasText( false )
219 // supplier must be SvNumberFormatsSupplierObj
220 SvNumberFormatsSupplierObj* pObj =
221 comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( rSupp );
222 if (pObj)
223 m_pFormatter = pObj->GetNumberFormatter();
225 if ( m_pFormatter )
227 m_pLocaleData.reset( new LocaleDataWrapper( m_pFormatter->GetComponentContext(),
228 m_pFormatter->GetLanguageTag() ) );
230 else
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(
241 SvXMLExport& rExp,
242 const css::uno::Reference< css::util::XNumberFormatsSupplier >& rSupp,
243 OUString aPrefix ) :
244 m_rExport( rExp ),
245 m_sPrefix(std::move( aPrefix )),
246 m_pFormatter( nullptr ),
247 m_bHasText( false )
249 // supplier must be SvNumberFormatsSupplierObj
250 SvNumberFormatsSupplierObj* pObj =
251 comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( rSupp );
252 if (pObj)
253 m_pFormatter = pObj->GetNumberFormatter();
255 if ( m_pFormatter )
257 m_pLocaleData.reset( new LocaleDataWrapper( m_pFormatter->GetComponentContext(),
258 m_pFormatter->GetLanguageTag() ) );
260 else
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()
274 // helper methods
276 static OUString lcl_CreateStyleName( sal_Int32 nKey, sal_Int32 nPart, bool bDefPart, std::u16string_view rPrefix )
278 if (bDefPart)
279 return rPrefix + OUString::number(nKey);
280 else
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.
320 m_bHasText = true;
323 void SvXMLNumFmtExport::FinishTextElement_Impl(bool bUseExtensionNS)
325 if ( m_bHasText )
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,
339 true, false );
340 m_rExport.Characters( m_sTextContent.makeStringAndClear() );
341 m_bHasText = false;
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,
355 true, false );
358 void SvXMLNumFmtExport::WriteCurrencyElement_Impl( const OUString& rString,
359 std::u16string_view rExt )
361 FinishTextElement_Impl();
363 if ( !rExt.empty() )
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);
369 if ( nLang < 0 )
370 nLang = -nLang;
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,
377 true, false );
378 m_rExport.Characters( rString );
381 void SvXMLNumFmtExport::WriteBooleanElement_Impl()
383 FinishTextElement_Impl();
385 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_BOOLEAN,
386 true, false );
389 void SvXMLNumFmtExport::WriteTextContentElement_Impl()
391 FinishTextElement_Impl();
393 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_TEXT_CONTENT,
394 true, false );
397 // date elements
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,
407 true, false );
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
416 if ( bText )
418 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TEXTUAL, XML_TRUE );
421 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_MONTH,
422 true, false );
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,
433 true, false );
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,
444 true, false );
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,
455 true, false );
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,
465 true, false );
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,
476 true, false );
479 // time elements
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,
488 true, false );
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,
498 true, false );
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 ) );
516 namespace {
517 void lcl_WriteBlankWidthString( std::u16string_view rBlankWidthChar, OUStringBuffer& rBlankWidthString, OUStringBuffer& rTextContent )
519 // export "_x"
520 if ( rBlankWidthString.isEmpty() )
522 rBlankWidthString.append( rBlankWidthChar );
523 if ( !rTextContent.isEmpty() )
525 // add position in rTextContent
526 rBlankWidthString.append( rTextContent.getLength() );
529 else
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() )
539 OUString aBlanks;
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
551 if ( nDecimals > 0 )
553 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
554 OUString::number( nDecimals ) );
557 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_SECONDS,
558 true, false );
561 void SvXMLNumFmtExport::WriteAMPMElement_Impl()
563 FinishTextElement_Impl();
565 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_AM_PM,
566 true, false );
569 // numbers
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
588 if ( bGrouping )
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 );
619 else
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 );
625 ++nEntry;
627 while ( nEntry < nEntryCount
628 && rEmbeddedEntries[nEntry].nFormatPos == pObj->nFormatPos );
629 --nEntry;
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,
635 true, false );
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();
648 // decimals
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,
673 rDashStr );
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,
690 true, true );
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();
703 // decimals
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 );
726 // exponent digits
727 if ( nExp >= 0 )
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 ) );
746 // exponent sign
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 );
756 // exponent string
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 );
762 if (nBlankExp > 0)
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,
772 true, false );
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();
791 // Count '0' as '?'
792 sal_Int32 nMinNumeratorDigits = aNumeratorString.replaceAll("0","?").indexOf('?');
793 sal_Int32 nZerosNumeratorDigits = aNumeratorString.indexOf('0');
794 if ( nMinNumeratorDigits >= 0 )
795 nMinNumeratorDigits = nMaxNumeratorDigits - nMinNumeratorDigits;
796 else
797 nMinNumeratorDigits = 0;
798 if ( nZerosNumeratorDigits >= 0 )
799 nZerosNumeratorDigits = nMaxNumeratorDigits - nZerosNumeratorDigits;
800 else
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;
807 else
808 nMinDenominatorDigits = 0;
809 if ( nZerosDenominatorDigits >= 0 )
810 nZerosDenominatorDigits = nMaxDenominatorDigits - nZerosDenominatorDigits;
811 else
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 );
825 // numerator digits
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 ) );
841 if ( nDenominator )
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
848 else
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,
868 true, false );
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 )
879 return;
881 // style namespace
883 OUStringBuffer aCondStr(20);
884 aCondStr.append( "value()" ); //! define constant
885 switch ( nOp )
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;
893 default:
894 OSL_FAIL("unknown operator");
896 ::rtl::math::doubleToUStringBuffer( aCondStr, fLimit,
897 rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
898 '.', true );
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,
905 m_sPrefix ) ) );
907 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_STYLE, XML_MAP,
908 true, false );
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
918 sal_Int32 nCPos = 0;
919 while (nCPos >= 0)
921 nCPos = sUpperStr.indexOf( sCurString, nCPos );
922 if (nCPos >= 0)
924 // in Quotes?
925 sal_Int32 nQ = SvNumberformat::GetQuoteEnd( sUpperStr, nCPos );
926 if ( nQ < 0 )
928 // dm can be escaped as "dm or \d
929 sal_Unicode c;
930 if ( nCPos == 0 )
931 return nCPos; // found
932 c = sUpperStr[nCPos-1];
933 if ( c != '"' && c != '\\')
935 return nCPos; // found
937 else
939 nCPos++; // continue
942 else
944 nCPos = nQ + 1; // continue after quote end
948 return -1;
951 bool SvXMLNumFmtExport::WriteTextWithCurrency_Impl( const OUString& rString,
952 const css::lang::Locale& rLocale )
954 // returns true if currency element was written
956 bool bRet = false;
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 );
965 if ( nPos >= 0 )
967 sal_Int32 nLength = rString.getLength();
968 sal_Int32 nCurLen = sCurString.getLength();
969 sal_Int32 nCont = nPos + nCurLen;
971 // text before currency symbol
972 if ( nPos > 0 )
974 AddToTextElement_Impl( rString.subView( 0, nPos ) );
976 // currency symbol (empty string -> default)
977 WriteCurrencyElement_Impl( u""_ustr, u"" );
978 bRet = true;
980 // text after currency symbol
981 if ( nCont < nLength )
983 AddToTextElement_Impl( rString.subView( nCont, nLength-nCont ) );
986 else
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
998 OUString aCalendar;
999 CalendarWrapper* pCalendar = pFormatter->GetCalendar();
1000 if (pCalendar)
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())
1008 aCalendar = *pCal;
1010 return aCalendar;
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 )
1018 return true;
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;
1038 bool bEnd = false;
1039 short nLastType = 0;
1040 while (!bEnd)
1042 short nElemType = rFormat.GetNumForType( 0, nPos );
1043 switch ( nElemType )
1045 case 0:
1046 if ( nLastType == NF_SYMBOLTYPE_STRING )
1047 bDateNoDefault = true; // text at the end -> no default date format
1048 bEnd = true; // end of format reached
1049 break;
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
1055 break;
1056 // same mapping as in SvXMLNumFormatContext::AddNfKeyword:
1057 case NF_KEY_NN: eDateDOW = XML_DEA_SHORT; break;
1058 case NF_KEY_NNN:
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;
1074 case NF_KEY_AP:
1075 case NF_KEY_AMPM: break; // AM/PM may or may not be in date/time formats -> ignore by itself
1076 default:
1077 bDateNoDefault = true; // any other element -> no default format
1079 nLastType = nElemType;
1080 ++nPos;
1083 if ( bDateNoDefault )
1084 return false; // additional elements
1085 else
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!
1101 // element name
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;
1125 switch ( nFmtType )
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
1130 // number-style.
1131 case SvNumFormatType::UNDEFINED:
1132 SAL_WARN("xmloff.style","UNDEFINED number format: '" << rFormat.GetFormatstring() << "'");
1133 [[fallthrough]];
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;
1142 break;
1143 case SvNumFormatType::PERCENT:
1144 eType = XML_PERCENTAGE_STYLE;
1145 break;
1146 case SvNumFormatType::CURRENCY:
1147 eType = XML_CURRENCY_STYLE;
1148 break;
1149 case SvNumFormatType::DATE:
1150 case SvNumFormatType::DATETIME:
1151 eType = XML_DATE_STYLE;
1152 break;
1153 case SvNumFormatType::TIME:
1154 eType = XML_TIME_STYLE;
1155 break;
1156 case SvNumFormatType::TEXT:
1157 eType = XML_TEXT_STYLE;
1158 break;
1159 case SvNumFormatType::LOGICAL:
1160 eType = XML_BOOLEAN_STYLE;
1161 break;
1162 default: break;
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
1176 if ( !bDefPart )
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
1183 // title (comment)
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
1219 if ( bAutoOrder &&
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,
1226 XML_TRUE );
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,
1236 XML_LANGUAGE );
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,
1245 XML_FALSE );
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,
1261 aAttr.Format );
1262 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_LANGUAGE,
1263 aLanguage );
1264 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_COUNTRY,
1265 aCountry );
1266 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_STYLE,
1267 aAttr.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
1279 // releases.
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,
1292 aLanguage );
1293 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_COUNTRY,
1294 aCountry );
1298 // The element
1299 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, eType,
1300 true, true );
1302 // color (properties element)
1304 const Color* pCol = rFormat.GetColor( nPart );
1305 if (pCol)
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;
1312 // format elements
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 );
1319 bAnyContent = true;
1321 else if ( eBuiltIn == NF_BOOLEAN )
1323 // boolean format contains just one boolean element
1324 WriteBooleanElement_Impl();
1325 bAnyContent = true;
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;
1332 bool bEnd = false;
1333 while (!bEnd)
1335 const short nElemType = rFormat.GetNumForType( nPart, nPos );
1336 switch (nElemType)
1338 case 0:
1339 bEnd = true; // end of format reached
1340 if (m_bHasText && m_sTextContent.isEmpty())
1341 m_bHasText = false; // don't write trailing empty text
1342 break;
1343 case NF_SYMBOLTYPE_STRING:
1345 const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1346 if (pElemStr)
1347 AddToTextElement_Impl( *pElemStr );
1349 break;
1350 case NF_KEY_BOOLEAN:
1351 WriteBooleanElement_Impl();
1352 bAnyContent = true;
1353 break;
1355 ++nPos;
1358 else
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;
1375 OUString sCurrExt;
1376 OUString aCalendar;
1377 bool bImplicitOtherCalendar = false;
1378 bool bExplicitCalendar = false;
1379 sal_uInt16 nPos = 0;
1380 bool bEnd = false;
1381 while (!bEnd)
1383 short nElemType = rFormat.GetNumForType( nPart, nPos );
1384 const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1386 switch ( nElemType )
1388 case 0:
1389 bEnd = true; // end of format reached
1390 break;
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] == '?' )
1398 nBlankExp ++;
1401 else if ( !bDecDashes && pElemStr && (*pElemStr)[0] == '-' )
1403 bDecDashes = true;
1404 nMinDecimals = 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 == '?' )
1413 nMinDecimals --;
1414 if ( aChar == '?' )
1415 bDecAlign = true;
1417 else
1418 break;
1421 if ( bInInteger && pElemStr )
1423 nIntegerSymbols += pElemStr->getLength();
1424 for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 ; i-- )
1426 if ( (*pElemStr)[i] == '?' )
1427 nBlankInteger ++;
1430 nTrailingThousands = 0;
1431 break;
1432 case NF_SYMBOLTYPE_FRACBLANK:
1433 case NF_SYMBOLTYPE_DECSEP:
1434 bInInteger = false;
1435 break;
1436 case NF_SYMBOLTYPE_THSEP:
1437 if (pElemStr)
1438 nTrailingThousands += pElemStr->getLength(); // is reset to 0 if digits follow
1439 break;
1440 case NF_SYMBOLTYPE_EXP:
1441 bExpFound = true; // following digits are exponent digits
1442 bInInteger = false;
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
1448 break;
1449 case NF_SYMBOLTYPE_CURRENCY:
1450 bCurrFound = true;
1451 break;
1452 case NF_SYMBOLTYPE_CURREXT:
1453 if (pElemStr)
1454 sCurrExt = *pElemStr;
1455 break;
1457 // E, EE, R, RR: select non-gregorian calendar
1458 // AAA, AAAA: calendar is switched at the position of the element
1459 case NF_KEY_EC:
1460 case NF_KEY_EEC:
1461 case NF_KEY_R:
1462 case NF_KEY_RR:
1463 if (aCalendar.isEmpty())
1465 aCalendar = lcl_GetDefaultCalendar( m_pFormatter, nLang );
1466 bImplicitOtherCalendar = true;
1468 break;
1470 ++nPos;
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
1484 if ( nPrecision )
1485 nEmbeddedPositionsMax += nPrecision + 1;
1486 // Enable embedded text in exponent in scientific number
1487 if ( nFmtType == SvNumFormatType::SCIENTIFIC )
1488 nEmbeddedPositionsMax += 1 + nExpDigits;
1489 nPos = 0;
1490 bEnd = false;
1491 bExpFound = false;
1492 while (!bEnd)
1494 short nElemType = rFormat.GetNumForType( nPart, nPos );
1495 const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1497 switch ( nElemType )
1499 case 0:
1500 bEnd = true; // end of format reached
1501 break;
1502 case NF_SYMBOLTYPE_DIGIT:
1503 if ( pElemStr )
1504 nDigitsPassed += pElemStr->getLength();
1505 break;
1506 case NF_SYMBOLTYPE_EXP:
1507 bExpFound = true;
1508 [[fallthrough]];
1509 case NF_SYMBOLTYPE_DECSEP:
1510 nDigitsPassed++;
1511 break;
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 )
1541 bExpSign = true;
1543 break;
1545 ++nPos;
1549 // final loop to write elements
1551 bool bNumWritten = false;
1552 bool bCurrencyWritten = false;
1553 short nPrevType = 0;
1554 nPos = 0;
1555 bEnd = false;
1556 while (!bEnd)
1558 short nElemType = rFormat.GetNumForType( nPart, nPos );
1559 const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1561 switch ( nElemType )
1563 case 0:
1564 bEnd = true; // end of format reached
1565 if (m_bHasText && m_sTextContent.isEmpty())
1566 m_bHasText = false; // don't write trailing empty text
1567 break;
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:
1573 if (pElemStr)
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 ) ) ) &&
1579 nPrecision > 0 )
1581 // decimal separator after seconds or [SS] is implied by
1582 // "decimal-places" attribute and must not be written
1583 // as text element
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 ) );
1597 bAnyContent = true;
1599 else
1600 AddToTextElement_Impl( *pElemStr );
1602 break;
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 );
1610 m_bHasText = true;
1613 break;
1614 case NF_KEY_GENERAL :
1615 WriteNumberElement_Impl( -1, -1, 1, -1, OUString(), false, 0, aEmbeddedEntries );
1616 bAnyContent = true;
1617 break;
1618 case NF_KEY_CCC:
1619 if (pElemStr)
1621 if ( bCurrencyWritten )
1622 AddToTextElement_Impl( *pElemStr ); // never more than one currency element
1623 else
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"" );
1630 bAnyContent = true;
1631 bCurrencyWritten = true;
1634 break;
1635 case NF_SYMBOLTYPE_CURRENCY:
1636 if (pElemStr)
1638 if ( bCurrencyWritten )
1639 AddToTextElement_Impl( *pElemStr ); // never more than one currency element
1640 else
1642 WriteCurrencyElement_Impl( *pElemStr, sCurrExt );
1643 bAnyContent = true;
1644 bCurrencyWritten = true;
1647 break;
1648 case NF_SYMBOLTYPE_DIGIT:
1649 if (!bNumWritten) // write number part
1651 switch ( nFmtType )
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:
1660 // decimals
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 )
1668 nDecimals = -1;
1670 // integer digits
1671 // only one built-in format has automatic integer digits
1672 sal_Int32 nInteger = nLeading;
1673 if ( eBuiltIn == NF_NUMBER_SYSTEM )
1675 nInteger = -1;
1676 nBlankInteger = -1;
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)
1687 sDashStr = " ";
1689 WriteNumberElement_Impl(nDecimals, nMinDecimals, nInteger, nBlankInteger, sDashStr.makeStringAndClear(),
1690 bThousand, nTrailingThousands, aEmbeddedEntries);
1691 bAnyContent = true;
1693 break;
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 );
1700 bAnyContent = true;
1701 break;
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.
1710 nInteger = -1;
1711 nBlankInteger = -1;
1713 WriteFractionElement_Impl( nInteger, nBlankInteger, bThousand, rFormat, nPart );
1714 bAnyContent = true;
1716 break;
1717 default: break;
1720 bNumWritten = true;
1722 break;
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 );
1733 break;
1734 case NF_SYMBOLTYPE_DEL:
1735 if ( pElemStr && *pElemStr == "@" )
1737 WriteTextContentElement_Impl();
1738 bAnyContent = true;
1740 break;
1742 case NF_SYMBOLTYPE_CALENDAR:
1743 if ( pElemStr )
1745 aCalendar = *pElemStr;
1746 bExplicitCalendar = true;
1748 break;
1750 // date elements:
1752 case NF_KEY_D:
1753 case NF_KEY_DD:
1755 bool bLong = ( nElemType == NF_KEY_DD );
1756 WriteDayElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1757 bAnyContent = true;
1759 break;
1760 case NF_KEY_DDD:
1761 case NF_KEY_DDDD:
1762 case NF_KEY_NN:
1763 case NF_KEY_NNN:
1764 case NF_KEY_NNNN:
1765 case NF_KEY_AAA:
1766 case NF_KEY_AAAA:
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 ) );
1779 bAnyContent = true;
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() );
1788 break;
1789 case NF_KEY_M:
1790 case NF_KEY_MM:
1791 case NF_KEY_MMM:
1792 case NF_KEY_MMMM:
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 );
1799 bAnyContent = true;
1801 break;
1802 case NF_KEY_YY:
1803 case NF_KEY_YYYY:
1804 case NF_KEY_EC:
1805 case NF_KEY_EEC:
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
1812 // Gregorian.
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));
1819 bAnyContent = true;
1821 break;
1822 case NF_KEY_G:
1823 case NF_KEY_GG:
1824 case NF_KEY_GGG:
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 ) );
1830 bAnyContent = true;
1831 if ( nElemType == NF_KEY_RR )
1833 // calendar attribute for RR is set in first loop
1834 WriteYearElement_Impl( aCalendar, ( bSystemDate || bLongSysDate ) );
1837 break;
1838 case NF_KEY_Q:
1839 case NF_KEY_QQ:
1841 bool bLong = ( nElemType == NF_KEY_QQ );
1842 WriteQuarterElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1843 bAnyContent = true;
1845 break;
1846 case NF_KEY_WW:
1847 WriteWeekElement_Impl( aCalendar );
1848 bAnyContent = true;
1849 break;
1851 // time elements (bSystemDate is not used):
1853 case NF_KEY_H:
1854 case NF_KEY_HH:
1855 WriteHoursElement_Impl( nElemType == NF_KEY_HH );
1856 bAnyContent = true;
1857 break;
1858 case NF_KEY_MI:
1859 case NF_KEY_MMI:
1860 WriteMinutesElement_Impl( nElemType == NF_KEY_MMI );
1861 bAnyContent = true;
1862 break;
1863 case NF_KEY_S:
1864 case NF_KEY_SS:
1865 WriteSecondsElement_Impl( ( nElemType == NF_KEY_SS ), nPrecision );
1866 bAnyContent = true;
1867 break;
1868 case NF_KEY_AMPM:
1869 case NF_KEY_AP:
1870 WriteAMPMElement_Impl(); // short/long?
1871 bAnyContent = true;
1872 break;
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] );
1880 break;
1882 nPrevType = nElemType;
1883 ++nPos;
1887 if ( !m_sTextContent.isEmpty() )
1888 bAnyContent = true; // element written in FinishTextElement_Impl
1890 FinishTextElement_Impl(); // final text element - before maps
1892 if ( !bAnyContent )
1894 // for an empty format, write an empty text element
1895 SvXMLElementExport aTElem( m_rExport, XML_NAMESPACE_NUMBER, XML_TEXT,
1896 true, false );
1899 // mapping (conditions) must be last elements
1901 if (!bDefPart)
1902 return;
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() )
1912 return;
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
1919 // conditions.
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)
1927 eOpLast = eOp1;
1928 fLimit3 = fLimit1;
1929 nLastPart = (eOp1 == NUMBERFORMAT_OP_NO) ? 0 : 1;
1931 switch ( eOpLast )
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 )
1979 bParts[1] = true;
1980 if (nUsedParts < 2)
1981 nUsedParts = 2;
1983 if ( eOp2 != NUMBERFORMAT_OP_NO )
1985 bParts[2] = true;
1986 if (nUsedParts < 3)
1987 nUsedParts = 3;
1989 if ( rFormat.HasTextFormat() )
1991 bParts[3] = true;
1992 if (nUsedParts < 4)
1993 nUsedParts = 4;
1996 for (sal_uInt16 nPart=0; nPart<XMLNUM_MAX_PARTS; ++nPart)
1998 if (bParts[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
2013 sal_uInt32 nKey;
2014 const SvNumberformat* pFormat = nullptr;
2015 bool bNext(m_pUsedList->GetFirstUsed(nKey));
2016 while(bNext)
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);
2022 if(pFormat)
2023 ExportFormat_Impl( *pFormat, nKey, nRealKey );
2024 bNext = m_pUsedList->GetNextUsed(nKey);
2026 if (!bIsAutoStyle)
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
2046 assert(pFormat);
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 );
2063 else
2065 OSL_FAIL("There is no written Data-Style");
2066 return OUString();
2070 void SvXMLNumFmtExport::SetUsed( sal_uInt32 nKey )
2072 SAL_WARN_IF( m_pFormatter == nullptr, "xmloff.style", "missing formatter" );
2073 if( !m_pFormatter )
2074 return;
2076 if (m_pFormatter->GetEntry(nKey))
2077 m_pUsedList->SetUsed( nKey );
2078 else {
2079 OSL_FAIL("no existing Numberformat found with this key");
2083 uno::Sequence<sal_Int32> SvXMLNumFmtExport::GetWasUsed() const
2085 if (m_pUsedList)
2086 return m_pUsedList->GetWasUsed();
2087 return uno::Sequence<sal_Int32>();
2090 void SvXMLNumFmtExport::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed)
2092 if (m_pUsedList)
2093 m_pUsedList->SetWasUsed(rWasUsed);
2096 static const SvNumberformat* lcl_GetFormat( SvNumberFormatter const * pFormatter,
2097 sal_uInt32 nKey )
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 )
2118 nRet = nNewKey;
2120 else
2122 OUString aFormatString( pFormat->GetFormatstring() );
2123 sal_Int32 nErrorPos;
2124 m_pFormatter->PutandConvertEntry(
2125 aFormatString,
2126 nErrorPos, nType, nNewKey,
2127 pFormat->GetLanguage(), LANGUAGE_SYSTEM, true);
2129 // success? Then use new key.
2130 if( nErrorPos == 0 )
2131 nRet = nNewKey;
2135 return nRet;
2138 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */