bump product version to 7.6.3.2-android
[LibreOffice.git] / xmloff / source / style / xmlnumfe.cxx
blob59e9f7714916f414525198c402d02d98f19447b6
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;
70 SvXMLEmbeddedTextEntry( sal_uInt16 nSP, sal_Int32 nFP, OUString aT ) :
71 nSourcePos(nSP), nFormatPos(nFP), aText(std::move(aT)) {}
76 class SvXMLEmbeddedTextEntryArr
78 typedef std::vector<SvXMLEmbeddedTextEntry> DataType;
79 DataType maData;
81 public:
83 void push_back( SvXMLEmbeddedTextEntry const& r )
85 maData.push_back(r);
88 const SvXMLEmbeddedTextEntry& operator[] ( size_t i ) const
90 return maData[i];
93 size_t size() const
95 return maData.size();
99 class SvXMLNumUsedList_Impl
101 SvXMLuInt32Set aUsed;
102 SvXMLuInt32Set aWasUsed;
103 SvXMLuInt32Set::iterator aCurrentUsedPos;
104 sal_uInt32 nUsedCount;
105 sal_uInt32 nWasUsedCount;
107 public:
108 SvXMLNumUsedList_Impl();
110 void SetUsed( sal_uInt32 nKey );
111 bool IsUsed( sal_uInt32 nKey ) const;
112 bool IsWasUsed( sal_uInt32 nKey ) const;
113 void Export();
115 bool GetFirstUsed(sal_uInt32& nKey);
116 bool GetNextUsed(sal_uInt32& nKey);
118 uno::Sequence<sal_Int32> GetWasUsed() const;
119 void SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed);
122 //! SvXMLNumUsedList_Impl should be optimized!
124 SvXMLNumUsedList_Impl::SvXMLNumUsedList_Impl() :
125 nUsedCount(0),
126 nWasUsedCount(0)
130 void SvXMLNumUsedList_Impl::SetUsed( sal_uInt32 nKey )
132 if ( !IsWasUsed(nKey) )
134 std::pair<SvXMLuInt32Set::iterator, bool> aPair = aUsed.insert( nKey );
135 if (aPair.second)
136 nUsedCount++;
140 bool SvXMLNumUsedList_Impl::IsUsed( sal_uInt32 nKey ) const
142 SvXMLuInt32Set::const_iterator aItr = aUsed.find(nKey);
143 return (aItr != aUsed.end());
146 bool SvXMLNumUsedList_Impl::IsWasUsed( sal_uInt32 nKey ) const
148 SvXMLuInt32Set::const_iterator aItr = aWasUsed.find(nKey);
149 return (aItr != aWasUsed.end());
152 void SvXMLNumUsedList_Impl::Export()
154 SvXMLuInt32Set::const_iterator aItr = aUsed.begin();
155 while (aItr != aUsed.end())
157 std::pair<SvXMLuInt32Set::const_iterator, bool> aPair = aWasUsed.insert( *aItr );
158 if (aPair.second)
159 nWasUsedCount++;
160 ++aItr;
162 aUsed.clear();
163 nUsedCount = 0;
166 bool SvXMLNumUsedList_Impl::GetFirstUsed(sal_uInt32& nKey)
168 bool bRet(false);
169 aCurrentUsedPos = aUsed.begin();
170 if(nUsedCount)
172 DBG_ASSERT(aCurrentUsedPos != aUsed.end(), "something went wrong");
173 nKey = *aCurrentUsedPos;
174 bRet = true;
176 return bRet;
179 bool SvXMLNumUsedList_Impl::GetNextUsed(sal_uInt32& nKey)
181 bool bRet(false);
182 if (aCurrentUsedPos != aUsed.end())
184 ++aCurrentUsedPos;
185 if (aCurrentUsedPos != aUsed.end())
187 nKey = *aCurrentUsedPos;
188 bRet = true;
191 return bRet;
194 uno::Sequence<sal_Int32> SvXMLNumUsedList_Impl::GetWasUsed() const
196 return comphelper::containerToSequence<sal_Int32>(aWasUsed);
199 void SvXMLNumUsedList_Impl::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed)
201 DBG_ASSERT(nWasUsedCount == 0, "WasUsed should be empty");
202 for (const auto nWasUsed : rWasUsed)
204 std::pair<SvXMLuInt32Set::const_iterator, bool> aPair = aWasUsed.insert( nWasUsed );
205 if (aPair.second)
206 nWasUsedCount++;
210 SvXMLNumFmtExport::SvXMLNumFmtExport(
211 SvXMLExport& rExp,
212 const uno::Reference< util::XNumberFormatsSupplier >& rSupp ) :
213 m_rExport( rExp ),
214 m_sPrefix( OUString("N") ),
215 m_pFormatter( nullptr ),
216 m_bHasText( false )
218 // supplier must be SvNumberFormatsSupplierObj
219 SvNumberFormatsSupplierObj* pObj =
220 comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( rSupp );
221 if (pObj)
222 m_pFormatter = pObj->GetNumberFormatter();
224 if ( m_pFormatter )
226 m_pLocaleData.reset( new LocaleDataWrapper( m_pFormatter->GetComponentContext(),
227 m_pFormatter->GetLanguageTag() ) );
229 else
231 LanguageTag aLanguageTag( MsLangId::getConfiguredSystemLanguage() );
233 m_pLocaleData.reset( new LocaleDataWrapper( m_rExport.getComponentContext(), std::move(aLanguageTag) ) );
236 m_pUsedList.reset(new SvXMLNumUsedList_Impl);
239 SvXMLNumFmtExport::SvXMLNumFmtExport(
240 SvXMLExport& rExp,
241 const css::uno::Reference< css::util::XNumberFormatsSupplier >& rSupp,
242 OUString aPrefix ) :
243 m_rExport( rExp ),
244 m_sPrefix(std::move( aPrefix )),
245 m_pFormatter( nullptr ),
246 m_bHasText( false )
248 // supplier must be SvNumberFormatsSupplierObj
249 SvNumberFormatsSupplierObj* pObj =
250 comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( rSupp );
251 if (pObj)
252 m_pFormatter = pObj->GetNumberFormatter();
254 if ( m_pFormatter )
256 m_pLocaleData.reset( new LocaleDataWrapper( m_pFormatter->GetComponentContext(),
257 m_pFormatter->GetLanguageTag() ) );
259 else
261 LanguageTag aLanguageTag( MsLangId::getConfiguredSystemLanguage() );
263 m_pLocaleData.reset( new LocaleDataWrapper( m_rExport.getComponentContext(), std::move(aLanguageTag) ) );
266 m_pUsedList.reset(new SvXMLNumUsedList_Impl);
269 SvXMLNumFmtExport::~SvXMLNumFmtExport()
273 // helper methods
275 static OUString lcl_CreateStyleName( sal_Int32 nKey, sal_Int32 nPart, bool bDefPart, std::u16string_view rPrefix )
277 if (bDefPart)
278 return rPrefix + OUString::number(nKey);
279 else
280 return rPrefix + OUString::number(nKey) + "P" + OUString::number( nPart );
283 void SvXMLNumFmtExport::AddCalendarAttr_Impl( const OUString& rCalendar )
285 if ( !rCalendar.isEmpty() )
287 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_CALENDAR, rCalendar );
291 void SvXMLNumFmtExport::AddStyleAttr_Impl( bool bLong )
293 if ( bLong ) // short is default
295 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_STYLE, XML_LONG );
299 void SvXMLNumFmtExport::AddLanguageAttr_Impl( LanguageType nLang )
301 if ( nLang != LANGUAGE_SYSTEM )
303 m_rExport.AddLanguageTagAttributes( XML_NAMESPACE_NUMBER, XML_NAMESPACE_NUMBER,
304 LanguageTag( nLang), false);
308 // methods to write individual elements within a format
310 void SvXMLNumFmtExport::AddToTextElement_Impl( std::u16string_view rString )
312 // append to sTextContent, write element in FinishTextElement_Impl
313 // to avoid several text elements following each other
315 m_sTextContent.append( rString );
316 // Also empty string leads to a number:text element as it may separate
317 // keywords of the same letter (e.g. MM""MMM) that otherwise would be
318 // concatenated when reading back in.
319 m_bHasText = true;
322 void SvXMLNumFmtExport::FinishTextElement_Impl(bool bUseExtensionNS)
324 if ( m_bHasText )
326 sal_uInt16 nNS = bUseExtensionNS ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER;
327 SvXMLElementExport aElem( m_rExport, nNS, XML_TEXT,
328 true, false );
329 m_rExport.Characters( m_sTextContent.makeStringAndClear() );
330 m_bHasText = false;
334 void SvXMLNumFmtExport::WriteColorElement_Impl( const Color& rColor )
336 FinishTextElement_Impl();
338 OUStringBuffer aColStr( 7 );
339 ::sax::Converter::convertColor( aColStr, rColor );
340 m_rExport.AddAttribute( XML_NAMESPACE_FO, XML_COLOR,
341 aColStr.makeStringAndClear() );
343 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_STYLE, XML_TEXT_PROPERTIES,
344 true, false );
347 void SvXMLNumFmtExport::WriteCurrencyElement_Impl( const OUString& rString,
348 std::u16string_view rExt )
350 FinishTextElement_Impl();
352 if ( !rExt.empty() )
354 // rExt should be a 16-bit hex value max FFFF which may contain a
355 // leading "-" separator (that is not a minus sign, but toInt32 can be
356 // used to parse it, with post-processing as necessary):
357 sal_Int32 nLang = o3tl::toInt32(rExt, 16);
358 if ( nLang < 0 )
359 nLang = -nLang;
360 SAL_WARN_IF(nLang > 0xFFFF, "xmloff.style", "Out of range Lang Id: " << nLang << " from input string: " << OUString(rExt));
361 AddLanguageAttr_Impl( LanguageType(nLang & 0xFFFF) ); // adds to pAttrList
364 SvXMLElementExport aElem( m_rExport,
365 XML_NAMESPACE_NUMBER, XML_CURRENCY_SYMBOL,
366 true, false );
367 m_rExport.Characters( rString );
370 void SvXMLNumFmtExport::WriteBooleanElement_Impl()
372 FinishTextElement_Impl();
374 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_BOOLEAN,
375 true, false );
378 void SvXMLNumFmtExport::WriteTextContentElement_Impl()
380 FinishTextElement_Impl();
382 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_TEXT_CONTENT,
383 true, false );
386 // date elements
388 void SvXMLNumFmtExport::WriteDayElement_Impl( const OUString& rCalendar, bool bLong )
390 FinishTextElement_Impl();
392 AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
393 AddStyleAttr_Impl( bLong ); // adds to pAttrList
395 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_DAY,
396 true, false );
399 void SvXMLNumFmtExport::WriteMonthElement_Impl( const OUString& rCalendar, bool bLong, bool bText )
401 FinishTextElement_Impl();
403 AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
404 AddStyleAttr_Impl( bLong ); // adds to pAttrList
405 if ( bText )
407 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TEXTUAL, XML_TRUE );
410 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_MONTH,
411 true, false );
414 void SvXMLNumFmtExport::WriteYearElement_Impl( const OUString& rCalendar, bool bLong )
416 FinishTextElement_Impl();
418 AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
419 AddStyleAttr_Impl( bLong ); // adds to pAttrList
421 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_YEAR,
422 true, false );
425 void SvXMLNumFmtExport::WriteEraElement_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_ERA,
433 true, false );
436 void SvXMLNumFmtExport::WriteDayOfWeekElement_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_DAY_OF_WEEK,
444 true, false );
447 void SvXMLNumFmtExport::WriteWeekElement_Impl( const OUString& rCalendar )
449 FinishTextElement_Impl();
451 AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
453 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_WEEK_OF_YEAR,
454 true, false );
457 void SvXMLNumFmtExport::WriteQuarterElement_Impl( const OUString& rCalendar, bool bLong )
459 FinishTextElement_Impl();
461 AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
462 AddStyleAttr_Impl( bLong ); // adds to pAttrList
464 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_QUARTER,
465 true, false );
468 // time elements
470 void SvXMLNumFmtExport::WriteHoursElement_Impl( bool bLong )
472 FinishTextElement_Impl();
474 AddStyleAttr_Impl( bLong ); // adds to pAttrList
476 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_HOURS,
477 true, false );
480 void SvXMLNumFmtExport::WriteMinutesElement_Impl( bool bLong )
482 FinishTextElement_Impl();
484 AddStyleAttr_Impl( bLong ); // adds to pAttrList
486 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_MINUTES,
487 true, false );
490 void SvXMLNumFmtExport::WriteRepeatedElement_Impl( sal_Unicode nChar )
492 // Export only for 1.2 with extensions or 1.3 and later.
493 SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
494 if (eVersion > SvtSaveOptions::ODFSVER_012)
496 FinishTextElement_Impl(eVersion < SvtSaveOptions::ODFSVER_013);
497 // OFFICE-3765 For 1.2+ use loext namespace, for 1.3 use number namespace.
498 SvXMLElementExport aElem( m_rExport,
499 ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
500 XML_FILL_CHARACTER, true, false );
501 m_rExport.Characters( OUString( nChar ) );
505 void SvXMLNumFmtExport::WriteSecondsElement_Impl( bool bLong, sal_uInt16 nDecimals )
507 FinishTextElement_Impl();
509 AddStyleAttr_Impl( bLong ); // adds to pAttrList
510 if ( nDecimals > 0 )
512 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
513 OUString::number( nDecimals ) );
516 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_SECONDS,
517 true, false );
520 void SvXMLNumFmtExport::WriteAMPMElement_Impl()
522 FinishTextElement_Impl();
524 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_AM_PM,
525 true, false );
528 // numbers
530 void SvXMLNumFmtExport::WriteIntegerElement_Impl(
531 sal_Int32 nInteger, sal_Int32 nBlankInteger, bool bGrouping )
533 // integer digits: '0' and '?'
534 if ( nInteger >= 0 ) // negative = automatic
536 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
537 OUString::number( nInteger ) );
539 SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
540 // blank integer digits: '?'
541 if ( nBlankInteger > 0 && ( (eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0 ) )
543 m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_MAX_BLANK_INTEGER_DIGITS,
544 OUString::number( nBlankInteger ) );
546 // (automatic) grouping separator
547 if ( bGrouping )
549 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
553 void SvXMLNumFmtExport::WriteNumberElement_Impl(
554 sal_Int32 nDecimals, sal_Int32 nMinDecimals,
555 sal_Int32 nInteger, sal_Int32 nBlankInteger, const OUString& rDashStr,
556 bool bGrouping, sal_Int32 nTrailingThousands,
557 const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries )
559 FinishTextElement_Impl();
561 // decimals
562 if ( nDecimals >= 0 ) // negative = automatic
564 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
565 OUString::number( nDecimals ) );
568 SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
569 if ( nMinDecimals >= 0 ) // negative = automatic
571 // Export only for 1.2 with extensions or 1.3 and later.
572 if (eVersion > SvtSaveOptions::ODFSVER_012)
574 // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
575 m_rExport.AddAttribute(
576 ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
577 XML_MIN_DECIMAL_PLACES,
578 OUString::number( nMinDecimals ) );
581 // decimal replacement (dashes) or variable decimals (#)
582 if ( !rDashStr.isEmpty() || nMinDecimals < nDecimals )
584 // full variable decimals means an empty replacement string
585 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_REPLACEMENT,
586 rDashStr );
589 WriteIntegerElement_Impl( nInteger, nBlankInteger, bGrouping );
591 // display-factor if there are trailing thousands separators
592 if ( nTrailingThousands )
594 // each separator character removes three digits
595 double fFactor = ::rtl::math::pow10Exp( 1.0, 3 * nTrailingThousands );
597 OUStringBuffer aFactStr;
598 ::sax::Converter::convertDouble( aFactStr, fFactor );
599 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DISPLAY_FACTOR, aFactStr.makeStringAndClear() );
602 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_NUMBER,
603 true, true );
605 // number:embedded-text as child elements
607 auto nEntryCount = rEmbeddedEntries.size();
608 for (decltype(nEntryCount) nEntry=0; nEntry < nEntryCount; ++nEntry)
610 const SvXMLEmbeddedTextEntry *const pObj = &rEmbeddedEntries[nEntry];
612 // position attribute
613 // position == 0 is between first integer digit and decimal separator
614 // position < 0 is inside decimal part
615 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_POSITION,
616 OUString::number( pObj->nFormatPos ) );
617 SvXMLElementExport aChildElem( m_rExport, XML_NAMESPACE_NUMBER, XML_EMBEDDED_TEXT,
618 true, false );
620 // text as element content
621 OUStringBuffer aContent( pObj->aText );
622 while ( nEntry+1 < nEntryCount && rEmbeddedEntries[nEntry+1].nFormatPos == pObj->nFormatPos )
624 // The array can contain several elements for the same position in the number
625 // (for example, literal text and space from underscores). They must be merged
626 // into a single embedded-text element.
627 aContent.append(rEmbeddedEntries[nEntry+1].aText);
628 ++nEntry;
630 m_rExport.Characters( aContent.makeStringAndClear() );
634 void SvXMLNumFmtExport::WriteScientificElement_Impl(
635 sal_Int32 nDecimals, sal_Int32 nMinDecimals, sal_Int32 nInteger, sal_Int32 nBlankInteger,
636 bool bGrouping, sal_Int32 nExp, sal_Int32 nExpInterval, bool bExpSign )
638 FinishTextElement_Impl();
640 // decimals
641 if ( nDecimals >= 0 ) // negative = automatic
643 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
644 OUString::number( nDecimals ) );
647 SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
648 if ( nMinDecimals >= 0 ) // negative = automatic
650 // Export only for 1.2 with extensions or 1.3 and later.
651 if (eVersion > SvtSaveOptions::ODFSVER_012)
653 // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
654 m_rExport.AddAttribute(
655 ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
656 XML_MIN_DECIMAL_PLACES,
657 OUString::number( nMinDecimals ) );
661 WriteIntegerElement_Impl( nInteger, nBlankInteger, bGrouping );
663 // exponent digits
664 if ( nExp >= 0 )
666 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_EXPONENT_DIGITS,
667 OUString::number( nExp ) );
670 // exponent interval for engineering notation
671 if ( nExpInterval >= 0 )
673 // Export only for 1.2 with extensions or 1.3 and later.
674 if (eVersion > SvtSaveOptions::ODFSVER_012)
676 // OFFICE-1828 For 1.2+ use loext namespace, for 1.3 use number namespace.
677 m_rExport.AddAttribute(
678 ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
679 XML_EXPONENT_INTERVAL, OUString::number( nExpInterval ) );
683 // exponent sign
684 // Export only for 1.2 with extensions or 1.3 and later.
685 if (eVersion > SvtSaveOptions::ODFSVER_012)
687 // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
688 m_rExport.AddAttribute(
689 ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
690 XML_FORCED_EXPONENT_SIGN,
691 bExpSign? XML_TRUE : XML_FALSE );
694 SvXMLElementExport aElem( m_rExport,
695 XML_NAMESPACE_NUMBER, XML_SCIENTIFIC_NUMBER,
696 true, false );
699 void SvXMLNumFmtExport::WriteFractionElement_Impl(
700 sal_Int32 nInteger, sal_Int32 nBlankInteger, bool bGrouping,
701 const SvNumberformat& rFormat, sal_uInt16 nPart )
703 FinishTextElement_Impl();
704 WriteIntegerElement_Impl( nInteger, nBlankInteger, bGrouping );
706 const OUString aNumeratorString = rFormat.GetNumeratorString( nPart );
707 const OUString aDenominatorString = rFormat.GetDenominatorString( nPart );
708 const OUString aIntegerFractionDelimiterString = rFormat.GetIntegerFractionDelimiterString( nPart );
709 sal_Int32 nMaxNumeratorDigits = aNumeratorString.getLength();
710 // Count '0' as '?'
711 sal_Int32 nMinNumeratorDigits = aNumeratorString.replaceAll("0","?").indexOf('?');
712 sal_Int32 nZerosNumeratorDigits = aNumeratorString.indexOf('0');
713 if ( nMinNumeratorDigits >= 0 )
714 nMinNumeratorDigits = nMaxNumeratorDigits - nMinNumeratorDigits;
715 else
716 nMinNumeratorDigits = 0;
717 if ( nZerosNumeratorDigits >= 0 )
718 nZerosNumeratorDigits = nMaxNumeratorDigits - nZerosNumeratorDigits;
719 else
720 nZerosNumeratorDigits = 0;
721 sal_Int32 nMaxDenominatorDigits = aDenominatorString.getLength();
722 sal_Int32 nMinDenominatorDigits = aDenominatorString.replaceAll("0","?").indexOf('?');
723 sal_Int32 nZerosDenominatorDigits = aDenominatorString.indexOf('0');
724 if ( nMinDenominatorDigits >= 0 )
725 nMinDenominatorDigits = nMaxDenominatorDigits - nMinDenominatorDigits;
726 else
727 nMinDenominatorDigits = 0;
728 if ( nZerosDenominatorDigits >= 0 )
729 nZerosDenominatorDigits = nMaxDenominatorDigits - nZerosDenominatorDigits;
730 else
731 nZerosDenominatorDigits = 0;
732 sal_Int32 nDenominator = aDenominatorString.toInt32();
734 SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
736 // integer/fraction delimiter
737 if ( !aIntegerFractionDelimiterString.isEmpty() && aIntegerFractionDelimiterString != " "
738 && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
739 { // Export only for 1.2/1.3 with extensions.
740 m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_INTEGER_FRACTION_DELIMITER,
741 aIntegerFractionDelimiterString );
744 // numerator digits
745 if ( nMinNumeratorDigits == 0 ) // at least one digit to keep compatibility with previous versions
746 nMinNumeratorDigits++;
747 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_NUMERATOR_DIGITS,
748 OUString::number( nMinNumeratorDigits ) );
749 // Export only for 1.2/1.3 with extensions.
750 if ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0)
752 // For extended ODF use loext namespace
753 m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_MAX_NUMERATOR_DIGITS,
754 OUString::number( nMaxNumeratorDigits ) );
756 if ( nZerosNumeratorDigits && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
757 m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_ZEROS_NUMERATOR_DIGITS,
758 OUString::number( nZerosNumeratorDigits ) );
760 if ( nDenominator )
762 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DENOMINATOR_VALUE,
763 OUString::number( nDenominator) );
765 // it's not necessary to export nDenominatorDigits
766 // if we have a forced denominator
767 else
769 if ( nMinDenominatorDigits == 0 ) // at least one digit to keep compatibility with previous versions
770 nMinDenominatorDigits++;
771 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_DENOMINATOR_DIGITS,
772 OUString::number( nMinDenominatorDigits ) );
773 if (eVersion > SvtSaveOptions::ODFSVER_012)
775 // OFFICE-3695 For 1.2+ use loext namespace, for 1.3 use number namespace.
776 m_rExport.AddAttribute(
777 ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
778 XML_MAX_DENOMINATOR_VALUE,
779 OUString::number( pow ( 10.0, nMaxDenominatorDigits ) - 1 ) ); // 9, 99 or 999
781 if ( nZerosDenominatorDigits && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
782 m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_ZEROS_DENOMINATOR_DIGITS,
783 OUString::number( nZerosDenominatorDigits ) );
786 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_FRACTION,
787 true, false );
790 // mapping (condition)
792 void SvXMLNumFmtExport::WriteMapElement_Impl( sal_Int32 nOp, double fLimit,
793 sal_Int32 nKey, sal_Int32 nPart )
795 FinishTextElement_Impl();
797 if ( nOp == NUMBERFORMAT_OP_NO )
798 return;
800 // style namespace
802 OUStringBuffer aCondStr(20);
803 aCondStr.append( "value()" ); //! define constant
804 switch ( nOp )
806 case NUMBERFORMAT_OP_EQ: aCondStr.append( '=' ); break;
807 case NUMBERFORMAT_OP_NE: aCondStr.append( "!=" ); break;
808 case NUMBERFORMAT_OP_LT: aCondStr.append( '<' ); break;
809 case NUMBERFORMAT_OP_LE: aCondStr.append( "<=" ); break;
810 case NUMBERFORMAT_OP_GT: aCondStr.append( '>' ); break;
811 case NUMBERFORMAT_OP_GE: aCondStr.append( ">=" ); break;
812 default:
813 OSL_FAIL("unknown operator");
815 ::rtl::math::doubleToUStringBuffer( aCondStr, fLimit,
816 rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
817 '.', true );
819 m_rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_CONDITION,
820 aCondStr.makeStringAndClear() );
822 m_rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_APPLY_STYLE_NAME,
823 m_rExport.EncodeStyleName( lcl_CreateStyleName( nKey, nPart, false,
824 m_sPrefix ) ) );
826 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_STYLE, XML_MAP,
827 true, false );
830 // for old (automatic) currency formats: parse currency symbol from text
832 static sal_Int32 lcl_FindSymbol( const OUString& sUpperStr, std::u16string_view sCurString )
834 // search for currency symbol
835 // Quoting as in ImpSvNumberformatScan::Symbol_Division
837 sal_Int32 nCPos = 0;
838 while (nCPos >= 0)
840 nCPos = sUpperStr.indexOf( sCurString, nCPos );
841 if (nCPos >= 0)
843 // in Quotes?
844 sal_Int32 nQ = SvNumberformat::GetQuoteEnd( sUpperStr, nCPos );
845 if ( nQ < 0 )
847 // dm can be escaped as "dm or \d
848 sal_Unicode c;
849 if ( nCPos == 0 )
850 return nCPos; // found
851 c = sUpperStr[nCPos-1];
852 if ( c != '"' && c != '\\')
854 return nCPos; // found
856 else
858 nCPos++; // continue
861 else
863 nCPos = nQ + 1; // continue after quote end
867 return -1;
870 bool SvXMLNumFmtExport::WriteTextWithCurrency_Impl( const OUString& rString,
871 const css::lang::Locale& rLocale )
873 // returns true if currency element was written
875 bool bRet = false;
877 LanguageTag aLanguageTag( rLocale );
878 m_pFormatter->ChangeIntl( aLanguageTag.getLanguageType( false) );
879 OUString sCurString, sDummy;
880 m_pFormatter->GetCompatibilityCurrency( sCurString, sDummy );
882 OUString sUpperStr = m_pFormatter->GetCharClass()->uppercase(rString);
883 sal_Int32 nPos = lcl_FindSymbol( sUpperStr, sCurString );
884 if ( nPos >= 0 )
886 sal_Int32 nLength = rString.getLength();
887 sal_Int32 nCurLen = sCurString.getLength();
888 sal_Int32 nCont = nPos + nCurLen;
890 // text before currency symbol
891 if ( nPos > 0 )
893 AddToTextElement_Impl( rString.subView( 0, nPos ) );
895 // currency symbol (empty string -> default)
896 WriteCurrencyElement_Impl( "", u"" );
897 bRet = true;
899 // text after currency symbol
900 if ( nCont < nLength )
902 AddToTextElement_Impl( rString.subView( nCont, nLength-nCont ) );
905 else
907 AddToTextElement_Impl( rString ); // simple text
910 return bRet; // true: currency element written
913 static OUString lcl_GetDefaultCalendar( SvNumberFormatter const * pFormatter, LanguageType nLang )
915 // get name of first non-gregorian calendar for the language
917 OUString aCalendar;
918 CalendarWrapper* pCalendar = pFormatter->GetCalendar();
919 if (pCalendar)
921 lang::Locale aLocale( LanguageTag::convertToLocale( nLang ) );
923 const uno::Sequence<OUString> aCals = pCalendar->getAllCalendars( aLocale );
924 auto pCal = std::find_if(aCals.begin(), aCals.end(),
925 [](const OUString& rCal) { return rCal != "gregorian"; });
926 if (pCal != aCals.end())
927 aCalendar = *pCal;
929 return aCalendar;
932 static bool lcl_IsInEmbedded( const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries, sal_uInt16 nPos )
934 auto nCount = rEmbeddedEntries.size();
935 for (decltype(nCount) i=0; i<nCount; i++)
936 if ( rEmbeddedEntries[i].nSourcePos == nPos )
937 return true;
939 return false; // not found
942 static bool lcl_IsDefaultDateFormat( const SvNumberformat& rFormat, bool bSystemDate, NfIndexTableOffset eBuiltIn )
944 // make an extra loop to collect date elements, to check if it is a default format
945 // before adding the automatic-order attribute
947 SvXMLDateElementAttributes eDateDOW = XML_DEA_NONE;
948 SvXMLDateElementAttributes eDateDay = XML_DEA_NONE;
949 SvXMLDateElementAttributes eDateMonth = XML_DEA_NONE;
950 SvXMLDateElementAttributes eDateYear = XML_DEA_NONE;
951 SvXMLDateElementAttributes eDateHours = XML_DEA_NONE;
952 SvXMLDateElementAttributes eDateMins = XML_DEA_NONE;
953 SvXMLDateElementAttributes eDateSecs = XML_DEA_NONE;
954 bool bDateNoDefault = false;
956 sal_uInt16 nPos = 0;
957 bool bEnd = false;
958 short nLastType = 0;
959 while (!bEnd)
961 short nElemType = rFormat.GetNumForType( 0, nPos );
962 switch ( nElemType )
964 case 0:
965 if ( nLastType == NF_SYMBOLTYPE_STRING )
966 bDateNoDefault = true; // text at the end -> no default date format
967 bEnd = true; // end of format reached
968 break;
969 case NF_SYMBOLTYPE_STRING:
970 case NF_SYMBOLTYPE_DATESEP:
971 case NF_SYMBOLTYPE_TIMESEP:
972 case NF_SYMBOLTYPE_TIME100SECSEP:
973 // text is ignored, except at the end
974 break;
975 // same mapping as in SvXMLNumFormatContext::AddNfKeyword:
976 case NF_KEY_NN: eDateDOW = XML_DEA_SHORT; break;
977 case NF_KEY_NNN:
978 case NF_KEY_NNNN: eDateDOW = XML_DEA_LONG; break;
979 case NF_KEY_D: eDateDay = XML_DEA_SHORT; break;
980 case NF_KEY_DD: eDateDay = XML_DEA_LONG; break;
981 case NF_KEY_M: eDateMonth = XML_DEA_SHORT; break;
982 case NF_KEY_MM: eDateMonth = XML_DEA_LONG; break;
983 case NF_KEY_MMM: eDateMonth = XML_DEA_TEXTSHORT; break;
984 case NF_KEY_MMMM: eDateMonth = XML_DEA_TEXTLONG; break;
985 case NF_KEY_YY: eDateYear = XML_DEA_SHORT; break;
986 case NF_KEY_YYYY: eDateYear = XML_DEA_LONG; break;
987 case NF_KEY_H: eDateHours = XML_DEA_SHORT; break;
988 case NF_KEY_HH: eDateHours = XML_DEA_LONG; break;
989 case NF_KEY_MI: eDateMins = XML_DEA_SHORT; break;
990 case NF_KEY_MMI: eDateMins = XML_DEA_LONG; break;
991 case NF_KEY_S: eDateSecs = XML_DEA_SHORT; break;
992 case NF_KEY_SS: eDateSecs = XML_DEA_LONG; break;
993 case NF_KEY_AP:
994 case NF_KEY_AMPM: break; // AM/PM may or may not be in date/time formats -> ignore by itself
995 default:
996 bDateNoDefault = true; // any other element -> no default format
998 nLastType = nElemType;
999 ++nPos;
1002 if ( bDateNoDefault )
1003 return false; // additional elements
1004 else
1006 NfIndexTableOffset eFound = static_cast<NfIndexTableOffset>(SvXMLNumFmtDefaults::GetDefaultDateFormat(
1007 eDateDOW, eDateDay, eDateMonth, eDateYear, eDateHours, eDateMins, eDateSecs, bSystemDate ));
1009 return ( eFound == eBuiltIn );
1013 // export one part (condition)
1015 void SvXMLNumFmtExport::ExportPart_Impl( const SvNumberformat& rFormat, sal_uInt32 nKey, sal_uInt32 nRealKey,
1016 sal_uInt16 nPart, bool bDefPart )
1018 //! for the default part, pass the conditions from the other parts!
1020 // element name
1022 NfIndexTableOffset eBuiltIn = m_pFormatter->GetIndexTableOffset( nRealKey );
1024 SvNumFormatType nFmtType = SvNumFormatType::ALL;
1025 bool bThousand = false;
1026 sal_uInt16 nPrecision = 0;
1027 sal_uInt16 nLeading = 0;
1028 rFormat.GetNumForInfo( nPart, nFmtType, bThousand, nPrecision, nLeading);
1029 nFmtType &= ~SvNumFormatType::DEFINED;
1031 // special treatment of builtin formats that aren't detected by normal parsing
1032 // (the same formats that get the type set in SvNumberFormatter::ImpGenerateFormats)
1033 if ( eBuiltIn == NF_NUMBER_STANDARD )
1034 nFmtType = SvNumFormatType::NUMBER;
1035 else if ( eBuiltIn == NF_BOOLEAN )
1036 nFmtType = SvNumFormatType::LOGICAL;
1037 else if ( eBuiltIn == NF_TEXT )
1038 nFmtType = SvNumFormatType::TEXT;
1040 // #101606# An empty subformat is a valid number-style resulting in an
1041 // empty display string for the condition of the subformat.
1043 XMLTokenEnum eType = XML_TOKEN_INVALID;
1044 switch ( nFmtType )
1046 // Type UNDEFINED likely is a crappy format string for that we could
1047 // not decide on any format type (and maybe could try harder?), but the
1048 // resulting XMLTokenEnum should be something valid, so make that
1049 // number-style.
1050 case SvNumFormatType::UNDEFINED:
1051 SAL_WARN("xmloff.style","UNDEFINED number format: '" << rFormat.GetFormatstring() << "'");
1052 [[fallthrough]];
1053 // Type is 0 if a format contains no recognized elements
1054 // (like text only) - this is handled as a number-style.
1055 case SvNumFormatType::ALL:
1056 case SvNumFormatType::EMPTY:
1057 case SvNumFormatType::NUMBER:
1058 case SvNumFormatType::SCIENTIFIC:
1059 case SvNumFormatType::FRACTION:
1060 eType = XML_NUMBER_STYLE;
1061 break;
1062 case SvNumFormatType::PERCENT:
1063 eType = XML_PERCENTAGE_STYLE;
1064 break;
1065 case SvNumFormatType::CURRENCY:
1066 eType = XML_CURRENCY_STYLE;
1067 break;
1068 case SvNumFormatType::DATE:
1069 case SvNumFormatType::DATETIME:
1070 eType = XML_DATE_STYLE;
1071 break;
1072 case SvNumFormatType::TIME:
1073 eType = XML_TIME_STYLE;
1074 break;
1075 case SvNumFormatType::TEXT:
1076 eType = XML_TEXT_STYLE;
1077 break;
1078 case SvNumFormatType::LOGICAL:
1079 eType = XML_BOOLEAN_STYLE;
1080 break;
1081 default: break;
1083 SAL_WARN_IF( eType == XML_TOKEN_INVALID, "xmloff.style", "unknown format type" );
1085 OUString sAttrValue;
1086 bool bUserDef( rFormat.GetType() & SvNumFormatType::DEFINED );
1088 // common attributes for format
1090 // format name (generated from key) - style namespace
1091 m_rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_NAME,
1092 lcl_CreateStyleName( nKey, nPart, bDefPart, m_sPrefix ) );
1094 // "volatile" attribute for styles used only in maps
1095 if ( !bDefPart )
1096 m_rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_VOLATILE, XML_TRUE );
1098 // language / country
1099 LanguageType nLang = rFormat.GetLanguage();
1100 AddLanguageAttr_Impl( nLang ); // adds to pAttrList
1102 // title (comment)
1103 // titles for builtin formats are not written
1104 sAttrValue = rFormat.GetComment();
1105 if ( !sAttrValue.isEmpty() && bUserDef && bDefPart )
1107 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TITLE, sAttrValue );
1110 // automatic ordering for currency and date formats
1111 // only used for some built-in formats
1112 bool bAutoOrder = ( eBuiltIn == NF_CURRENCY_1000INT || eBuiltIn == NF_CURRENCY_1000DEC2 ||
1113 eBuiltIn == NF_CURRENCY_1000INT_RED || eBuiltIn == NF_CURRENCY_1000DEC2_RED ||
1114 eBuiltIn == NF_CURRENCY_1000DEC2_DASHED ||
1115 eBuiltIn == NF_DATE_SYSTEM_SHORT || eBuiltIn == NF_DATE_SYSTEM_LONG ||
1116 eBuiltIn == NF_DATE_SYS_MMYY || eBuiltIn == NF_DATE_SYS_DDMMM ||
1117 eBuiltIn == NF_DATE_SYS_DDMMYYYY || eBuiltIn == NF_DATE_SYS_DDMMYY ||
1118 eBuiltIn == NF_DATE_SYS_DMMMYY || eBuiltIn == NF_DATE_SYS_DMMMYYYY ||
1119 eBuiltIn == NF_DATE_SYS_DMMMMYYYY || eBuiltIn == NF_DATE_SYS_NNDMMMYY ||
1120 eBuiltIn == NF_DATE_SYS_NNDMMMMYYYY || eBuiltIn == NF_DATE_SYS_NNNNDMMMMYYYY ||
1121 eBuiltIn == NF_DATETIME_SYSTEM_SHORT_HHMM || eBuiltIn == NF_DATETIME_SYS_DDMMYYYY_HHMM ||
1122 eBuiltIn == NF_DATETIME_SYS_DDMMYYYY_HHMMSS );
1124 // format source (for date and time formats)
1125 // only used for some built-in formats
1126 bool bSystemDate = ( eBuiltIn == NF_DATE_SYSTEM_SHORT ||
1127 eBuiltIn == NF_DATE_SYSTEM_LONG ||
1128 eBuiltIn == NF_DATETIME_SYSTEM_SHORT_HHMM );
1129 bool bLongSysDate = ( eBuiltIn == NF_DATE_SYSTEM_LONG );
1131 // check if the format definition matches the key
1132 if ( bAutoOrder && ( nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) &&
1133 !lcl_IsDefaultDateFormat( rFormat, bSystemDate, eBuiltIn ) )
1135 bAutoOrder = bSystemDate = bLongSysDate = false; // don't write automatic-order attribute then
1138 if ( bAutoOrder &&
1139 ( nFmtType == SvNumFormatType::CURRENCY || nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) )
1141 // #85109# format type must be checked to avoid dtd errors if
1142 // locale data contains other format types at the built-in positions
1144 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_AUTOMATIC_ORDER,
1145 XML_TRUE );
1148 if ( bSystemDate && bAutoOrder &&
1149 ( nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) )
1151 // #85109# format type must be checked to avoid dtd errors if
1152 // locale data contains other format types at the built-in positions
1154 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_FORMAT_SOURCE,
1155 XML_LANGUAGE );
1158 // overflow for time formats as in [hh]:mm
1159 // controlled by bThousand from number format info
1160 // default for truncate-on-overflow is true
1161 if ( nFmtType == SvNumFormatType::TIME && bThousand )
1163 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRUNCATE_ON_OVERFLOW,
1164 XML_FALSE );
1167 // Native number transliteration
1168 css::i18n::NativeNumberXmlAttributes2 aAttr;
1169 rFormat.GetNatNumXml( aAttr, nPart );
1170 if ( !aAttr.Format.isEmpty() )
1172 assert(aAttr.Spellout.isEmpty()); // mutually exclusive
1174 /* FIXME-BCP47: ODF defines no transliteration-script or
1175 * transliteration-rfc-language-tag */
1176 LanguageTag aLanguageTag( aAttr.Locale);
1177 OUString aLanguage, aScript, aCountry;
1178 aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
1179 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_FORMAT,
1180 aAttr.Format );
1181 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_LANGUAGE,
1182 aLanguage );
1183 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_COUNTRY,
1184 aCountry );
1185 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_STYLE,
1186 aAttr.Style );
1189 if ( !aAttr.Spellout.isEmpty() )
1191 const bool bWriteSpellout = aAttr.Format.isEmpty();
1192 assert(bWriteSpellout); // mutually exclusive
1194 // Export only for 1.2 and later with extensions
1195 SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
1196 // Also ensure that duplicated transliteration-language and
1197 // transliteration-country attributes never escape into the wild with
1198 // releases.
1199 if ( (eVersion & SvtSaveOptions::ODFSVER_EXTENDED) && bWriteSpellout )
1201 /* FIXME-BCP47: ODF defines no transliteration-script or
1202 * transliteration-rfc-language-tag */
1203 LanguageTag aLanguageTag( aAttr.Locale);
1204 OUString aLanguage, aScript, aCountry;
1205 aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
1206 // For 1.2/1.3+ use loext namespace.
1207 m_rExport.AddAttribute( /*((eVersion < SvtSaveOptions::ODFSVER_)
1208 ? */ XML_NAMESPACE_LO_EXT /*: XML_NAMESPACE_NUMBER)*/,
1209 XML_TRANSLITERATION_SPELLOUT, aAttr.Spellout );
1210 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_LANGUAGE,
1211 aLanguage );
1212 m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_COUNTRY,
1213 aCountry );
1217 // The element
1218 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, eType,
1219 true, true );
1221 // color (properties element)
1223 const Color* pCol = rFormat.GetColor( nPart );
1224 if (pCol)
1225 WriteColorElement_Impl(*pCol);
1227 // detect if there is "real" content, excluding color and maps
1228 //! move to implementation of Write... methods?
1229 bool bAnyContent = false;
1231 // format elements
1233 SvXMLEmbeddedTextEntryArr aEmbeddedEntries;
1234 if ( eBuiltIn == NF_NUMBER_STANDARD )
1236 // default number format contains just one number element
1237 WriteNumberElement_Impl( -1, -1, 1, -1, OUString(), false, 0, aEmbeddedEntries );
1238 bAnyContent = true;
1240 else if ( eBuiltIn == NF_BOOLEAN )
1242 // boolean format contains just one boolean element
1243 WriteBooleanElement_Impl();
1244 bAnyContent = true;
1246 else if (eType == XML_BOOLEAN_STYLE)
1248 // <number:boolean-style> may contain only <number:boolean> and
1249 // <number:text> elements.
1250 sal_uInt16 nPos = 0;
1251 bool bEnd = false;
1252 while (!bEnd)
1254 const short nElemType = rFormat.GetNumForType( nPart, nPos );
1255 switch (nElemType)
1257 case 0:
1258 bEnd = true; // end of format reached
1259 if (m_bHasText && m_sTextContent.isEmpty())
1260 m_bHasText = false; // don't write trailing empty text
1261 break;
1262 case NF_SYMBOLTYPE_STRING:
1264 const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1265 if (pElemStr)
1266 AddToTextElement_Impl( *pElemStr );
1268 break;
1269 case NF_KEY_BOOLEAN:
1270 WriteBooleanElement_Impl();
1271 bAnyContent = true;
1272 break;
1274 ++nPos;
1277 else
1279 // first loop to collect attributes
1281 bool bDecDashes = false;
1282 bool bExpFound = false;
1283 bool bCurrFound = false;
1284 bool bInInteger = true;
1285 bool bExpSign = true;
1286 bool bDecAlign = false; // decimal alignment with "?"
1287 sal_Int32 nExpDigits = 0;
1288 sal_Int32 nIntegerSymbols = 0; // for embedded-text, including "#"
1289 sal_Int32 nTrailingThousands = 0; // thousands-separators after all digits
1290 sal_Int32 nMinDecimals = nPrecision;
1291 sal_Int32 nBlankInteger = 0;
1292 OUString sCurrExt;
1293 OUString aCalendar;
1294 bool bImplicitOtherCalendar = false;
1295 bool bExplicitCalendar = false;
1296 sal_uInt16 nPos = 0;
1297 bool bEnd = false;
1298 while (!bEnd)
1300 short nElemType = rFormat.GetNumForType( nPart, nPos );
1301 const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1303 switch ( nElemType )
1305 case 0:
1306 bEnd = true; // end of format reached
1307 break;
1308 case NF_SYMBOLTYPE_DIGIT:
1309 if ( bExpFound && pElemStr )
1310 nExpDigits += pElemStr->getLength();
1311 else if ( !bDecDashes && pElemStr && (*pElemStr)[0] == '-' )
1313 bDecDashes = true;
1314 nMinDecimals = 0;
1316 else if ( nFmtType != SvNumFormatType::FRACTION && !bInInteger && pElemStr )
1318 for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 ; i-- )
1320 sal_Unicode aChar = (*pElemStr)[i];
1321 if ( aChar == '#' || aChar == '?' )
1323 nMinDecimals --;
1324 if ( aChar == '?' )
1325 bDecAlign = true;
1327 else
1328 break;
1331 if ( bInInteger && pElemStr )
1333 nIntegerSymbols += pElemStr->getLength();
1334 for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 ; i-- )
1336 if ( (*pElemStr)[i] == '?' )
1337 nBlankInteger ++;
1340 nTrailingThousands = 0;
1341 break;
1342 case NF_SYMBOLTYPE_FRACBLANK:
1343 case NF_SYMBOLTYPE_DECSEP:
1344 bInInteger = false;
1345 break;
1346 case NF_SYMBOLTYPE_THSEP:
1347 if (pElemStr)
1348 nTrailingThousands += pElemStr->getLength(); // is reset to 0 if digits follow
1349 break;
1350 case NF_SYMBOLTYPE_EXP:
1351 bExpFound = true; // following digits are exponent digits
1352 bInInteger = false;
1353 if ( pElemStr && ( pElemStr->getLength() == 1
1354 || ( pElemStr->getLength() == 2 && (*pElemStr)[1] == '-' ) ) )
1355 bExpSign = false; // for 0.00E0 or 0.00E-00
1356 break;
1357 case NF_SYMBOLTYPE_CURRENCY:
1358 bCurrFound = true;
1359 break;
1360 case NF_SYMBOLTYPE_CURREXT:
1361 if (pElemStr)
1362 sCurrExt = *pElemStr;
1363 break;
1365 // E, EE, R, RR: select non-gregorian calendar
1366 // AAA, AAAA: calendar is switched at the position of the element
1367 case NF_KEY_EC:
1368 case NF_KEY_EEC:
1369 case NF_KEY_R:
1370 case NF_KEY_RR:
1371 if (aCalendar.isEmpty())
1373 aCalendar = lcl_GetDefaultCalendar( m_pFormatter, nLang );
1374 bImplicitOtherCalendar = true;
1376 break;
1378 ++nPos;
1381 // collect strings for embedded-text (must be known before number element is written)
1383 bool bAllowEmbedded = ( nFmtType == SvNumFormatType::ALL || nFmtType == SvNumFormatType::NUMBER ||
1384 nFmtType == SvNumFormatType::CURRENCY ||
1385 nFmtType == SvNumFormatType::PERCENT );
1386 if ( bAllowEmbedded )
1388 sal_Int32 nDigitsPassed = 0;
1389 sal_Int32 nEmbeddedPositionsMax = nIntegerSymbols;
1390 // Enable embedded text in decimal part only if there's a decimal part
1391 if ( nPrecision )
1392 nEmbeddedPositionsMax += nPrecision + 1;
1393 nPos = 0;
1394 bEnd = false;
1395 while (!bEnd)
1397 short nElemType = rFormat.GetNumForType( nPart, nPos );
1398 const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1400 switch ( nElemType )
1402 case 0:
1403 bEnd = true; // end of format reached
1404 break;
1405 case NF_SYMBOLTYPE_DIGIT:
1406 if ( pElemStr )
1407 nDigitsPassed += pElemStr->getLength();
1408 break;
1409 case NF_SYMBOLTYPE_DECSEP:
1410 nDigitsPassed++;
1411 break;
1412 case NF_SYMBOLTYPE_STRING:
1413 case NF_SYMBOLTYPE_BLANK:
1414 case NF_SYMBOLTYPE_PERCENT:
1415 if ( 0 < nDigitsPassed && nDigitsPassed < nEmbeddedPositionsMax && pElemStr )
1417 // text (literal or underscore) within the integer (>=0) or decimal (<0) part of a number:number element
1419 OUString aEmbeddedStr;
1420 if ( nElemType == NF_SYMBOLTYPE_STRING || nElemType == NF_SYMBOLTYPE_PERCENT )
1422 aEmbeddedStr = *pElemStr;
1424 else if (pElemStr->getLength() >= 2)
1426 SvNumberformat::InsertBlanks( aEmbeddedStr, 0, (*pElemStr)[1] );
1428 sal_Int32 nEmbedPos = nIntegerSymbols - nDigitsPassed;
1430 aEmbeddedEntries.push_back(
1431 SvXMLEmbeddedTextEntry(nPos, nEmbedPos, aEmbeddedStr));
1433 break;
1435 ++nPos;
1439 // final loop to write elements
1441 bool bNumWritten = false;
1442 bool bCurrencyWritten = false;
1443 short nPrevType = 0;
1444 nPos = 0;
1445 bEnd = false;
1446 while (!bEnd)
1448 short nElemType = rFormat.GetNumForType( nPart, nPos );
1449 const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1451 switch ( nElemType )
1453 case 0:
1454 bEnd = true; // end of format reached
1455 if (m_bHasText && m_sTextContent.isEmpty())
1456 m_bHasText = false; // don't write trailing empty text
1457 break;
1458 case NF_SYMBOLTYPE_STRING:
1459 case NF_SYMBOLTYPE_DATESEP:
1460 case NF_SYMBOLTYPE_TIMESEP:
1461 case NF_SYMBOLTYPE_TIME100SECSEP:
1462 case NF_SYMBOLTYPE_PERCENT:
1463 if (pElemStr)
1465 if ( ( nElemType == NF_SYMBOLTYPE_TIME100SECSEP ) &&
1466 ( nPrevType == NF_KEY_S || nPrevType == NF_KEY_SS ||
1467 ( nPos > 0 && (*rFormat.GetNumForString( nPart, nPos-1 ))[0] == ']' &&
1468 ( nFmtType == SvNumFormatType::TIME || nFmtType == SvNumFormatType::DATETIME ) ) ) &&
1469 nPrecision > 0 )
1471 // decimal separator after seconds or [SS] is implied by
1472 // "decimal-places" attribute and must not be written
1473 // as text element
1474 //! difference between '.' and ',' is lost here
1476 else if ( lcl_IsInEmbedded( aEmbeddedEntries, nPos ) )
1478 // text is written as embedded-text child of the number,
1479 // don't create a text element
1481 else if ( nFmtType == SvNumFormatType::CURRENCY && !bCurrFound && !bCurrencyWritten )
1483 // automatic currency symbol is implemented as part of
1484 // normal text -> search for the symbol
1485 bCurrencyWritten = WriteTextWithCurrency_Impl( *pElemStr,
1486 LanguageTag::convertToLocale( nLang ) );
1487 bAnyContent = true;
1489 else
1490 AddToTextElement_Impl( *pElemStr );
1492 break;
1493 case NF_SYMBOLTYPE_BLANK:
1494 if ( pElemStr && !lcl_IsInEmbedded( aEmbeddedEntries, nPos ) )
1496 // turn "_x" into the number of spaces used for x in InsertBlanks in the NumberFormat
1497 // (#i20396# the spaces may also be in embedded-text elements)
1499 OUString aBlanks;
1500 if (pElemStr->getLength() >= 2)
1501 SvNumberformat::InsertBlanks( aBlanks, 0, (*pElemStr)[1] );
1502 AddToTextElement_Impl( aBlanks );
1504 break;
1505 case NF_KEY_GENERAL :
1506 WriteNumberElement_Impl( -1, -1, 1, -1, OUString(), false, 0, aEmbeddedEntries );
1507 bAnyContent = true;
1508 break;
1509 case NF_KEY_CCC:
1510 if (pElemStr)
1512 if ( bCurrencyWritten )
1513 AddToTextElement_Impl( *pElemStr ); // never more than one currency element
1514 else
1516 //! must be different from short automatic format
1517 //! but should still be empty (meaning automatic)
1518 // pElemStr is "CCC"
1520 WriteCurrencyElement_Impl( *pElemStr, u"" );
1521 bAnyContent = true;
1522 bCurrencyWritten = true;
1525 break;
1526 case NF_SYMBOLTYPE_CURRENCY:
1527 if (pElemStr)
1529 if ( bCurrencyWritten )
1530 AddToTextElement_Impl( *pElemStr ); // never more than one currency element
1531 else
1533 WriteCurrencyElement_Impl( *pElemStr, sCurrExt );
1534 bAnyContent = true;
1535 bCurrencyWritten = true;
1538 break;
1539 case NF_SYMBOLTYPE_DIGIT:
1540 if (!bNumWritten) // write number part
1542 switch ( nFmtType )
1544 // for type 0 (not recognized as a special type),
1545 // write a "normal" number
1546 case SvNumFormatType::ALL:
1547 case SvNumFormatType::NUMBER:
1548 case SvNumFormatType::CURRENCY:
1549 case SvNumFormatType::PERCENT:
1551 // decimals
1552 // only some built-in formats have automatic decimals
1553 sal_Int32 nDecimals = nPrecision; // from GetFormatSpecialInfo
1554 if ( eBuiltIn == NF_NUMBER_STANDARD ||
1555 eBuiltIn == NF_CURRENCY_1000DEC2 ||
1556 eBuiltIn == NF_CURRENCY_1000DEC2_RED ||
1557 eBuiltIn == NF_CURRENCY_1000DEC2_CCC ||
1558 eBuiltIn == NF_CURRENCY_1000DEC2_DASHED )
1559 nDecimals = -1;
1561 // integer digits
1562 // only one built-in format has automatic integer digits
1563 sal_Int32 nInteger = nLeading;
1564 if ( eBuiltIn == NF_NUMBER_SYSTEM )
1566 nInteger = -1;
1567 nBlankInteger = -1;
1570 // string for decimal replacement
1571 // has to be taken from nPrecision
1572 // (positive number even for automatic decimals)
1573 OUStringBuffer sDashStr;
1574 if (bDecDashes && nPrecision > 0)
1575 comphelper::string::padToLength(sDashStr, nPrecision, '-');
1576 // "?" in decimal part are replaced by space character
1577 if (bDecAlign && nPrecision > 0)
1578 sDashStr = " ";
1580 WriteNumberElement_Impl(nDecimals, nMinDecimals, nInteger, nBlankInteger, sDashStr.makeStringAndClear(),
1581 bThousand, nTrailingThousands, aEmbeddedEntries);
1582 bAnyContent = true;
1584 break;
1585 case SvNumFormatType::SCIENTIFIC:
1586 // #i43959# for scientific numbers, count all integer symbols ("0", "?" and "#")
1587 // as integer digits: use nIntegerSymbols instead of nLeading
1588 // nIntegerSymbols represents exponent interval (for engineering notation)
1589 WriteScientificElement_Impl( nPrecision, nMinDecimals, nLeading, nBlankInteger, bThousand, nExpDigits, nIntegerSymbols, bExpSign );
1590 bAnyContent = true;
1591 break;
1592 case SvNumFormatType::FRACTION:
1594 sal_Int32 nInteger = nLeading;
1595 if ( rFormat.GetNumForNumberElementCount( nPart ) == 3 )
1597 // If there is only two numbers + fraction in format string
1598 // the fraction doesn't have an integer part, and no
1599 // min-integer-digits attribute must be written.
1600 nInteger = -1;
1601 nBlankInteger = -1;
1603 WriteFractionElement_Impl( nInteger, nBlankInteger, bThousand, rFormat, nPart );
1604 bAnyContent = true;
1606 break;
1607 default: break;
1610 bNumWritten = true;
1612 break;
1613 case NF_SYMBOLTYPE_DECSEP:
1614 if ( pElemStr && nPrecision == 0 )
1616 // A decimal separator after the number, without following decimal digits,
1617 // isn't modelled as part of the number element, so it's written as text
1618 // (the distinction between a quoted and non-quoted, locale-dependent
1619 // character is lost here).
1621 AddToTextElement_Impl( *pElemStr );
1623 break;
1624 case NF_SYMBOLTYPE_DEL:
1625 if ( pElemStr && *pElemStr == "@" )
1627 WriteTextContentElement_Impl();
1628 bAnyContent = true;
1630 break;
1632 case NF_SYMBOLTYPE_CALENDAR:
1633 if ( pElemStr )
1635 aCalendar = *pElemStr;
1636 bExplicitCalendar = true;
1638 break;
1640 // date elements:
1642 case NF_KEY_D:
1643 case NF_KEY_DD:
1645 bool bLong = ( nElemType == NF_KEY_DD );
1646 WriteDayElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1647 bAnyContent = true;
1649 break;
1650 case NF_KEY_DDD:
1651 case NF_KEY_DDDD:
1652 case NF_KEY_NN:
1653 case NF_KEY_NNN:
1654 case NF_KEY_NNNN:
1655 case NF_KEY_AAA:
1656 case NF_KEY_AAAA:
1658 OUString aCalAttr = aCalendar;
1659 if ( nElemType == NF_KEY_AAA || nElemType == NF_KEY_AAAA )
1661 // calendar attribute for AAA and AAAA is switched only for this element
1662 if (aCalAttr.isEmpty())
1663 aCalAttr = lcl_GetDefaultCalendar( m_pFormatter, nLang );
1666 bool bLong = ( nElemType == NF_KEY_NNN || nElemType == NF_KEY_NNNN ||
1667 nElemType == NF_KEY_DDDD || nElemType == NF_KEY_AAAA );
1668 WriteDayOfWeekElement_Impl( aCalAttr, ( bSystemDate ? bLongSysDate : bLong ) );
1669 bAnyContent = true;
1670 if ( nElemType == NF_KEY_NNNN )
1672 // write additional text element for separator
1673 m_pLocaleData.reset( new LocaleDataWrapper( m_pFormatter->GetComponentContext(),
1674 LanguageTag( nLang ) ) );
1675 AddToTextElement_Impl( m_pLocaleData->getLongDateDayOfWeekSep() );
1678 break;
1679 case NF_KEY_M:
1680 case NF_KEY_MM:
1681 case NF_KEY_MMM:
1682 case NF_KEY_MMMM:
1683 case NF_KEY_MMMMM: //! first letter of month name, no attribute available
1685 bool bLong = ( nElemType == NF_KEY_MM || nElemType == NF_KEY_MMMM );
1686 bool bText = ( nElemType == NF_KEY_MMM || nElemType == NF_KEY_MMMM ||
1687 nElemType == NF_KEY_MMMMM );
1688 WriteMonthElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ), bText );
1689 bAnyContent = true;
1691 break;
1692 case NF_KEY_YY:
1693 case NF_KEY_YYYY:
1694 case NF_KEY_EC:
1695 case NF_KEY_EEC:
1696 case NF_KEY_R: //! R acts as EE, no attribute available
1698 //! distinguish EE and R
1699 // Calendar attribute for E and EE and R is set in
1700 // first loop. If set and not an explicit calendar and
1701 // YY or YYYY is encountered, switch temporarily to
1702 // Gregorian.
1703 bool bLong = ( nElemType == NF_KEY_YYYY || nElemType == NF_KEY_EEC ||
1704 nElemType == NF_KEY_R );
1705 WriteYearElement_Impl(
1706 ((bImplicitOtherCalendar && !bExplicitCalendar
1707 && (nElemType == NF_KEY_YY || nElemType == NF_KEY_YYYY)) ? "gregorian" : aCalendar),
1708 (bSystemDate ? bLongSysDate : bLong));
1709 bAnyContent = true;
1711 break;
1712 case NF_KEY_G:
1713 case NF_KEY_GG:
1714 case NF_KEY_GGG:
1715 case NF_KEY_RR: //! RR acts as GGGEE, no attribute available
1717 //! distinguish GG and GGG and RR
1718 bool bLong = ( nElemType == NF_KEY_GGG || nElemType == NF_KEY_RR );
1719 WriteEraElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1720 bAnyContent = true;
1721 if ( nElemType == NF_KEY_RR )
1723 // calendar attribute for RR is set in first loop
1724 WriteYearElement_Impl( aCalendar, ( bSystemDate || bLongSysDate ) );
1727 break;
1728 case NF_KEY_Q:
1729 case NF_KEY_QQ:
1731 bool bLong = ( nElemType == NF_KEY_QQ );
1732 WriteQuarterElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1733 bAnyContent = true;
1735 break;
1736 case NF_KEY_WW:
1737 WriteWeekElement_Impl( aCalendar );
1738 bAnyContent = true;
1739 break;
1741 // time elements (bSystemDate is not used):
1743 case NF_KEY_H:
1744 case NF_KEY_HH:
1745 WriteHoursElement_Impl( nElemType == NF_KEY_HH );
1746 bAnyContent = true;
1747 break;
1748 case NF_KEY_MI:
1749 case NF_KEY_MMI:
1750 WriteMinutesElement_Impl( nElemType == NF_KEY_MMI );
1751 bAnyContent = true;
1752 break;
1753 case NF_KEY_S:
1754 case NF_KEY_SS:
1755 WriteSecondsElement_Impl( ( nElemType == NF_KEY_SS ), nPrecision );
1756 bAnyContent = true;
1757 break;
1758 case NF_KEY_AMPM:
1759 case NF_KEY_AP:
1760 WriteAMPMElement_Impl(); // short/long?
1761 bAnyContent = true;
1762 break;
1763 case NF_SYMBOLTYPE_STAR :
1764 // export only if ODF 1.2 extensions are enabled
1765 if (m_rExport.getSaneDefaultVersion() > SvtSaveOptions::ODFSVER_012)
1767 if ( pElemStr && pElemStr->getLength() > 1 )
1768 WriteRepeatedElement_Impl( (*pElemStr)[1] );
1770 break;
1772 nPrevType = nElemType;
1773 ++nPos;
1777 if ( !m_sTextContent.isEmpty() )
1778 bAnyContent = true; // element written in FinishTextElement_Impl
1780 FinishTextElement_Impl(); // final text element - before maps
1782 if ( !bAnyContent )
1784 // for an empty format, write an empty text element
1785 SvXMLElementExport aTElem( m_rExport, XML_NAMESPACE_NUMBER, XML_TEXT,
1786 true, false );
1789 // mapping (conditions) must be last elements
1791 if (!bDefPart)
1792 return;
1794 SvNumberformatLimitOps eOp1, eOp2;
1795 double fLimit1, fLimit2;
1796 rFormat.GetConditions( eOp1, fLimit1, eOp2, fLimit2 );
1798 WriteMapElement_Impl( eOp1, fLimit1, nKey, 0 );
1799 WriteMapElement_Impl( eOp2, fLimit2, nKey, 1 );
1801 if ( !rFormat.HasTextFormat() )
1802 return;
1804 // 4th part is for text -> make an "all other numbers" condition for the 3rd part
1805 // by reversing the 2nd condition.
1806 // For a trailing text format like 0;@ that has no conditions
1807 // use a "less or equal than biggest" condition for the number
1808 // part, ODF can't store subformats (style maps) without
1809 // conditions.
1811 SvNumberformatLimitOps eOp3 = NUMBERFORMAT_OP_NO;
1812 double fLimit3 = fLimit2;
1813 sal_uInt16 nLastPart = 2;
1814 SvNumberformatLimitOps eOpLast = eOp2;
1815 if (eOp2 == NUMBERFORMAT_OP_NO)
1817 eOpLast = eOp1;
1818 fLimit3 = fLimit1;
1819 nLastPart = (eOp1 == NUMBERFORMAT_OP_NO) ? 0 : 1;
1821 switch ( eOpLast )
1823 case NUMBERFORMAT_OP_EQ: eOp3 = NUMBERFORMAT_OP_NE; break;
1824 case NUMBERFORMAT_OP_NE: eOp3 = NUMBERFORMAT_OP_EQ; break;
1825 case NUMBERFORMAT_OP_LT: eOp3 = NUMBERFORMAT_OP_GE; break;
1826 case NUMBERFORMAT_OP_LE: eOp3 = NUMBERFORMAT_OP_GT; break;
1827 case NUMBERFORMAT_OP_GT: eOp3 = NUMBERFORMAT_OP_LE; break;
1828 case NUMBERFORMAT_OP_GE: eOp3 = NUMBERFORMAT_OP_LT; break;
1829 case NUMBERFORMAT_OP_NO: eOp3 = NUMBERFORMAT_OP_LE; fLimit3 = DBL_MAX; break;
1832 if ( fLimit1 == fLimit2 &&
1833 ( ( eOp1 == NUMBERFORMAT_OP_LT && eOp2 == NUMBERFORMAT_OP_GT ) ||
1834 ( eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_LT ) ) )
1836 // For <x and >x, add =x as last condition
1837 // (just for readability, <=x would be valid, too)
1839 eOp3 = NUMBERFORMAT_OP_EQ;
1842 WriteMapElement_Impl( eOp3, fLimit3, nKey, nLastPart );
1845 // export one format
1847 void SvXMLNumFmtExport::ExportFormat_Impl( const SvNumberformat& rFormat, sal_uInt32 nKey, sal_uInt32 nRealKey )
1849 const sal_uInt16 XMLNUM_MAX_PARTS = 4;
1850 bool bParts[XMLNUM_MAX_PARTS] = { false, false, false, false };
1851 sal_uInt16 nUsedParts = 0;
1852 for (sal_uInt16 nPart=0; nPart<XMLNUM_MAX_PARTS; ++nPart)
1854 if (rFormat.GetNumForInfoScannedType( nPart) != SvNumFormatType::UNDEFINED)
1856 bParts[nPart] = true;
1857 nUsedParts = nPart + 1;
1861 SvNumberformatLimitOps eOp1, eOp2;
1862 double fLimit1, fLimit2;
1863 rFormat.GetConditions( eOp1, fLimit1, eOp2, fLimit2 );
1865 // if conditions are set, even empty formats must be written
1867 if ( eOp1 != NUMBERFORMAT_OP_NO )
1869 bParts[1] = true;
1870 if (nUsedParts < 2)
1871 nUsedParts = 2;
1873 if ( eOp2 != NUMBERFORMAT_OP_NO )
1875 bParts[2] = true;
1876 if (nUsedParts < 3)
1877 nUsedParts = 3;
1879 if ( rFormat.HasTextFormat() )
1881 bParts[3] = true;
1882 if (nUsedParts < 4)
1883 nUsedParts = 4;
1886 for (sal_uInt16 nPart=0; nPart<XMLNUM_MAX_PARTS; ++nPart)
1888 if (bParts[nPart])
1890 bool bDefault = ( nPart+1 == nUsedParts ); // last = default
1891 ExportPart_Impl( rFormat, nKey, nRealKey, nPart, bDefault );
1896 // export method called by application
1898 void SvXMLNumFmtExport::Export( bool bIsAutoStyle )
1900 if ( !m_pFormatter )
1901 return; // no formatter -> no entries
1903 sal_uInt32 nKey;
1904 const SvNumberformat* pFormat = nullptr;
1905 bool bNext(m_pUsedList->GetFirstUsed(nKey));
1906 while(bNext)
1908 // ODF has its notation of system formats, so obtain the "real" already
1909 // substituted format but use the original key for style name.
1910 sal_uInt32 nRealKey = nKey;
1911 pFormat = m_pFormatter->GetSubstitutedEntry( nKey, nRealKey);
1912 if(pFormat)
1913 ExportFormat_Impl( *pFormat, nKey, nRealKey );
1914 bNext = m_pUsedList->GetNextUsed(nKey);
1916 if (!bIsAutoStyle)
1918 std::vector<LanguageType> aLanguages;
1919 m_pFormatter->GetUsedLanguages( aLanguages );
1920 for (const auto& nLang : aLanguages)
1922 sal_uInt32 nDefaultIndex = 0;
1923 SvNumberFormatTable& rTable = m_pFormatter->GetEntryTable(
1924 SvNumFormatType::DEFINED, nDefaultIndex, nLang );
1925 for (const auto& rTableEntry : rTable)
1927 nKey = rTableEntry.first;
1928 pFormat = rTableEntry.second;
1929 if (!m_pUsedList->IsUsed(nKey))
1931 DBG_ASSERT((pFormat->GetType() & SvNumFormatType::DEFINED), "a not user defined numberformat found");
1932 sal_uInt32 nRealKey = nKey;
1933 if (pFormat->IsSubstituted())
1935 pFormat = m_pFormatter->GetSubstitutedEntry( nKey, nRealKey); // export the "real" format
1936 assert(pFormat);
1938 // user-defined and used formats are exported
1939 ExportFormat_Impl( *pFormat, nKey, nRealKey );
1940 // if it is a user-defined Format it will be added else nothing will happen
1941 m_pUsedList->SetUsed(nKey);
1946 m_pUsedList->Export();
1949 OUString SvXMLNumFmtExport::GetStyleName( sal_uInt32 nKey )
1951 if(m_pUsedList->IsUsed(nKey) || m_pUsedList->IsWasUsed(nKey))
1952 return lcl_CreateStyleName( nKey, 0, true, m_sPrefix );
1953 else
1955 OSL_FAIL("There is no written Data-Style");
1956 return OUString();
1960 void SvXMLNumFmtExport::SetUsed( sal_uInt32 nKey )
1962 SAL_WARN_IF( m_pFormatter == nullptr, "xmloff.style", "missing formatter" );
1963 if( !m_pFormatter )
1964 return;
1966 if (m_pFormatter->GetEntry(nKey))
1967 m_pUsedList->SetUsed( nKey );
1968 else {
1969 OSL_FAIL("no existing Numberformat found with this key");
1973 uno::Sequence<sal_Int32> SvXMLNumFmtExport::GetWasUsed() const
1975 if (m_pUsedList)
1976 return m_pUsedList->GetWasUsed();
1977 return uno::Sequence<sal_Int32>();
1980 void SvXMLNumFmtExport::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed)
1982 if (m_pUsedList)
1983 m_pUsedList->SetWasUsed(rWasUsed);
1986 static const SvNumberformat* lcl_GetFormat( SvNumberFormatter const * pFormatter,
1987 sal_uInt32 nKey )
1989 return ( pFormatter != nullptr ) ? pFormatter->GetEntry( nKey ) : nullptr;
1992 sal_uInt32 SvXMLNumFmtExport::ForceSystemLanguage( sal_uInt32 nKey )
1994 sal_uInt32 nRet = nKey;
1996 const SvNumberformat* pFormat = lcl_GetFormat( m_pFormatter, nKey );
1997 if( pFormat != nullptr )
1999 SAL_WARN_IF( m_pFormatter == nullptr, "xmloff.style", "format without formatter?" );
2001 SvNumFormatType nType = pFormat->GetType();
2003 sal_uInt32 nNewKey = m_pFormatter->GetFormatForLanguageIfBuiltIn(
2004 nKey, LANGUAGE_SYSTEM );
2006 if( nNewKey != nKey )
2008 nRet = nNewKey;
2010 else
2012 OUString aFormatString( pFormat->GetFormatstring() );
2013 sal_Int32 nErrorPos;
2014 m_pFormatter->PutandConvertEntry(
2015 aFormatString,
2016 nErrorPos, nType, nNewKey,
2017 pFormat->GetLanguage(), LANGUAGE_SYSTEM, true);
2019 // success? Then use new key.
2020 if( nErrorPos == 0 )
2021 nRet = nNewKey;
2025 return nRet;
2028 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */