1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <comphelper/sequence.hxx>
21 #include <comphelper/string.hxx>
22 #include <svl/zforlist.hxx>
23 #include <svl/zformat.hxx>
24 #include <svl/numuno.hxx>
25 #include <i18nlangtag/mslangid.hxx>
26 #include <i18nlangtag/languagetag.hxx>
27 #include <tools/debug.hxx>
28 #include <rtl/math.hxx>
29 #include <unotools/calendarwrapper.hxx>
30 #include <unotools/charclass.hxx>
31 #include <com/sun/star/lang/Locale.hpp>
32 #include <rtl/ustrbuf.hxx>
33 #include <sal/log.hxx>
34 #include <osl/diagnose.h>
35 #include <tools/color.hxx>
36 #include <sax/tools/converter.hxx>
37 
38 #include <com/sun/star/i18n/NativeNumberXmlAttributes2.hpp>
39 
40 #include <xmloff/xmlnumfe.hxx>
41 #include <xmloff/xmlnamespace.hxx>
42 #include <xmloff/xmlnumfi.hxx>
43 
44 #include <svl/nfsymbol.hxx>
45 #include <xmloff/xmltoken.hxx>
46 #include <xmloff/xmlexp.hxx>
47 
48 #include <float.h>
49 #include <set>
50 #include <string_view>
51 #include <vector>
52 
53 using namespace ::com::sun::star;
54 using namespace ::xmloff::token;
55 using namespace ::svt;
56 
57 typedef std::set< sal_uInt32 >  SvXMLuInt32Set;
58 
59 namespace {
60 
61 struct SvXMLEmbeddedTextEntry
62 {
63     sal_uInt16      nSourcePos;     // position in NumberFormat (to skip later)
64     sal_Int32       nFormatPos;     // resulting position in embedded-text element
65     OUString   aText;
66 
SvXMLEmbeddedTextEntry__anon7a9efb550111::SvXMLEmbeddedTextEntry67     SvXMLEmbeddedTextEntry( sal_uInt16 nSP, sal_Int32 nFP, const OUString& rT ) :
68         nSourcePos(nSP), nFormatPos(nFP), aText(rT) {}
69 };
70 
71 }
72 
73 class SvXMLEmbeddedTextEntryArr
74 {
75     typedef std::vector<SvXMLEmbeddedTextEntry> DataType;
76     DataType maData;
77 
78 public:
79 
push_back(SvXMLEmbeddedTextEntry const & r)80     void push_back( SvXMLEmbeddedTextEntry const& r )
81     {
82         maData.push_back(r);
83     }
84 
operator [](size_t i) const85     const SvXMLEmbeddedTextEntry& operator[] ( size_t i ) const
86     {
87         return maData[i];
88     }
89 
size() const90     size_t size() const
91     {
92         return maData.size();
93     }
94 };
95 
96 class SvXMLNumUsedList_Impl
97 {
98     SvXMLuInt32Set              aUsed;
99     SvXMLuInt32Set              aWasUsed;
100     SvXMLuInt32Set::iterator    aCurrentUsedPos;
101     sal_uInt32                  nUsedCount;
102     sal_uInt32                  nWasUsedCount;
103 
104 public:
105             SvXMLNumUsedList_Impl();
106 
107     void    SetUsed( sal_uInt32 nKey );
108     bool    IsUsed( sal_uInt32 nKey ) const;
109     bool    IsWasUsed( sal_uInt32 nKey ) const;
110     void    Export();
111 
112     bool    GetFirstUsed(sal_uInt32& nKey);
113     bool    GetNextUsed(sal_uInt32& nKey);
114 
115     uno::Sequence<sal_Int32> GetWasUsed() const;
116     void SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed);
117 };
118 
119 //! SvXMLNumUsedList_Impl should be optimized!
120 
SvXMLNumUsedList_Impl()121 SvXMLNumUsedList_Impl::SvXMLNumUsedList_Impl() :
122     nUsedCount(0),
123     nWasUsedCount(0)
124 {
125 }
126 
SetUsed(sal_uInt32 nKey)127 void SvXMLNumUsedList_Impl::SetUsed( sal_uInt32 nKey )
128 {
129     if ( !IsWasUsed(nKey) )
130     {
131         std::pair<SvXMLuInt32Set::iterator, bool> aPair = aUsed.insert( nKey );
132         if (aPair.second)
133             nUsedCount++;
134     }
135 }
136 
IsUsed(sal_uInt32 nKey) const137 bool SvXMLNumUsedList_Impl::IsUsed( sal_uInt32 nKey ) const
138 {
139     SvXMLuInt32Set::const_iterator aItr = aUsed.find(nKey);
140     return (aItr != aUsed.end());
141 }
142 
IsWasUsed(sal_uInt32 nKey) const143 bool SvXMLNumUsedList_Impl::IsWasUsed( sal_uInt32 nKey ) const
144 {
145     SvXMLuInt32Set::const_iterator aItr = aWasUsed.find(nKey);
146     return (aItr != aWasUsed.end());
147 }
148 
Export()149 void SvXMLNumUsedList_Impl::Export()
150 {
151     SvXMLuInt32Set::const_iterator aItr = aUsed.begin();
152     while (aItr != aUsed.end())
153     {
154         std::pair<SvXMLuInt32Set::const_iterator, bool> aPair = aWasUsed.insert( *aItr );
155         if (aPair.second)
156             nWasUsedCount++;
157         ++aItr;
158     }
159     aUsed.clear();
160     nUsedCount = 0;
161 }
162 
GetFirstUsed(sal_uInt32 & nKey)163 bool SvXMLNumUsedList_Impl::GetFirstUsed(sal_uInt32& nKey)
164 {
165     bool bRet(false);
166     aCurrentUsedPos = aUsed.begin();
167     if(nUsedCount)
168     {
169         DBG_ASSERT(aCurrentUsedPos != aUsed.end(), "something went wrong");
170         nKey = *aCurrentUsedPos;
171         bRet = true;
172     }
173     return bRet;
174 }
175 
GetNextUsed(sal_uInt32 & nKey)176 bool SvXMLNumUsedList_Impl::GetNextUsed(sal_uInt32& nKey)
177 {
178     bool bRet(false);
179     if (aCurrentUsedPos != aUsed.end())
180     {
181         ++aCurrentUsedPos;
182         if (aCurrentUsedPos != aUsed.end())
183         {
184             nKey = *aCurrentUsedPos;
185             bRet = true;
186         }
187     }
188     return bRet;
189 }
190 
GetWasUsed() const191 uno::Sequence<sal_Int32> SvXMLNumUsedList_Impl::GetWasUsed() const
192 {
193     return comphelper::containerToSequence<sal_Int32>(aWasUsed);
194 }
195 
SetWasUsed(const uno::Sequence<sal_Int32> & rWasUsed)196 void SvXMLNumUsedList_Impl::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed)
197 {
198     DBG_ASSERT(nWasUsedCount == 0, "WasUsed should be empty");
199     for (const auto nWasUsed : rWasUsed)
200     {
201         std::pair<SvXMLuInt32Set::const_iterator, bool> aPair = aWasUsed.insert( nWasUsed );
202         if (aPair.second)
203             nWasUsedCount++;
204     }
205 }
206 
SvXMLNumFmtExport(SvXMLExport & rExp,const uno::Reference<util::XNumberFormatsSupplier> & rSupp)207 SvXMLNumFmtExport::SvXMLNumFmtExport(
208             SvXMLExport& rExp,
209             const uno::Reference< util::XNumberFormatsSupplier >& rSupp ) :
210     rExport( rExp ),
211     sPrefix( OUString("N") ),
212     pFormatter( nullptr ),
213     bHasText( false )
214 {
215     //  supplier must be SvNumberFormatsSupplierObj
216     SvNumberFormatsSupplierObj* pObj =
217                     comphelper::getUnoTunnelImplementation<SvNumberFormatsSupplierObj>( rSupp );
218     if (pObj)
219         pFormatter = pObj->GetNumberFormatter();
220 
221     if ( pFormatter )
222     {
223         pCharClass.reset( new CharClass( pFormatter->GetComponentContext(),
224             pFormatter->GetLanguageTag() ) );
225         pLocaleData.reset( new LocaleDataWrapper( pFormatter->GetComponentContext(),
226             pFormatter->GetLanguageTag() ) );
227     }
228     else
229     {
230         LanguageTag aLanguageTag( MsLangId::getSystemLanguage() );
231 
232         pCharClass.reset( new CharClass( rExport.getComponentContext(), aLanguageTag ) );
233         pLocaleData.reset( new LocaleDataWrapper( rExport.getComponentContext(), aLanguageTag ) );
234     }
235 
236     pUsedList.reset(new SvXMLNumUsedList_Impl);
237 }
238 
SvXMLNumFmtExport(SvXMLExport & rExp,const css::uno::Reference<css::util::XNumberFormatsSupplier> & rSupp,const OUString & rPrefix)239 SvXMLNumFmtExport::SvXMLNumFmtExport(
240                        SvXMLExport& rExp,
241                        const css::uno::Reference< css::util::XNumberFormatsSupplier >& rSupp,
242                        const OUString& rPrefix ) :
243     rExport( rExp ),
244     sPrefix( rPrefix ),
245     pFormatter( nullptr ),
246     bHasText( false )
247 {
248     //  supplier must be SvNumberFormatsSupplierObj
249     SvNumberFormatsSupplierObj* pObj =
250                     comphelper::getUnoTunnelImplementation<SvNumberFormatsSupplierObj>( rSupp );
251     if (pObj)
252         pFormatter = pObj->GetNumberFormatter();
253 
254     if ( pFormatter )
255     {
256         pCharClass.reset( new CharClass( pFormatter->GetComponentContext(),
257             pFormatter->GetLanguageTag() ) );
258         pLocaleData.reset( new LocaleDataWrapper( pFormatter->GetComponentContext(),
259             pFormatter->GetLanguageTag() ) );
260     }
261     else
262     {
263         LanguageTag aLanguageTag( MsLangId::getSystemLanguage() );
264 
265         pCharClass.reset( new CharClass( rExport.getComponentContext(), aLanguageTag ) );
266         pLocaleData.reset( new LocaleDataWrapper( rExport.getComponentContext(), aLanguageTag ) );
267     }
268 
269     pUsedList.reset(new SvXMLNumUsedList_Impl);
270 }
271 
~SvXMLNumFmtExport()272 SvXMLNumFmtExport::~SvXMLNumFmtExport()
273 {
274 }
275 
276 //  helper methods
277 
lcl_CreateStyleName(sal_Int32 nKey,sal_Int32 nPart,bool bDefPart,std::u16string_view rPrefix)278 static OUString lcl_CreateStyleName( sal_Int32 nKey, sal_Int32 nPart, bool bDefPart, std::u16string_view rPrefix )
279 {
280     OUStringBuffer aFmtName(10);
281     aFmtName.append( rPrefix );
282     aFmtName.append( nKey );
283     if (!bDefPart)
284     {
285         aFmtName.append( 'P' );
286         aFmtName.append( nPart );
287     }
288     return aFmtName.makeStringAndClear();
289 }
290 
AddCalendarAttr_Impl(const OUString & rCalendar)291 void SvXMLNumFmtExport::AddCalendarAttr_Impl( const OUString& rCalendar )
292 {
293     if ( !rCalendar.isEmpty() )
294     {
295         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_CALENDAR, rCalendar );
296     }
297 }
298 
AddStyleAttr_Impl(bool bLong)299 void SvXMLNumFmtExport::AddStyleAttr_Impl( bool bLong )
300 {
301     if ( bLong )            // short is default
302     {
303         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_STYLE, XML_LONG );
304     }
305 }
306 
AddLanguageAttr_Impl(LanguageType nLang)307 void SvXMLNumFmtExport::AddLanguageAttr_Impl( LanguageType nLang )
308 {
309     if ( nLang != LANGUAGE_SYSTEM )
310     {
311         rExport.AddLanguageTagAttributes( XML_NAMESPACE_NUMBER, XML_NAMESPACE_NUMBER,
312                 LanguageTag( nLang), false);
313     }
314 }
315 
316 //  methods to write individual elements within a format
317 
AddToTextElement_Impl(std::u16string_view rString)318 void SvXMLNumFmtExport::AddToTextElement_Impl( std::u16string_view rString )
319 {
320     //  append to sTextContent, write element in FinishTextElement_Impl
321     //  to avoid several text elements following each other
322 
323     sTextContent.append( rString );
324     // Also empty string leads to a number:text element as it may separate
325     // keywords of the same letter (e.g. MM""MMM) that otherwise would be
326     // concatenated when reading back in.
327     bHasText = true;
328 }
329 
FinishTextElement_Impl(bool bUseExtensionNS)330 void SvXMLNumFmtExport::FinishTextElement_Impl(bool bUseExtensionNS)
331 {
332     if ( bHasText )
333     {
334         sal_uInt16 nNS = bUseExtensionNS ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER;
335         SvXMLElementExport aElem( rExport, nNS, XML_TEXT,
336                                   true, false );
337         rExport.Characters( sTextContent.makeStringAndClear() );
338         bHasText = false;
339     }
340 }
341 
WriteColorElement_Impl(const Color & rColor)342 void SvXMLNumFmtExport::WriteColorElement_Impl( const Color& rColor )
343 {
344     FinishTextElement_Impl();
345 
346     OUStringBuffer aColStr( 7 );
347     ::sax::Converter::convertColor( aColStr, rColor );
348     rExport.AddAttribute( XML_NAMESPACE_FO, XML_COLOR,
349                           aColStr.makeStringAndClear() );
350 
351     SvXMLElementExport aElem( rExport, XML_NAMESPACE_STYLE, XML_TEXT_PROPERTIES,
352                               true, false );
353 }
354 
WriteCurrencyElement_Impl(const OUString & rString,const OUString & rExt)355 void SvXMLNumFmtExport::WriteCurrencyElement_Impl( const OUString& rString,
356                                                     const OUString& rExt )
357 {
358     FinishTextElement_Impl();
359 
360     if ( !rExt.isEmpty() )
361     {
362         // rExt should be a 16-bit hex value max FFFF which may contain a
363         // leading "-" separator (that is not a minus sign, but toInt32 can be
364         // used to parse it, with post-processing as necessary):
365         sal_Int32 nLang = rExt.toInt32(16);
366         if ( nLang < 0 )
367             nLang = -nLang;
368         AddLanguageAttr_Impl( LanguageType(nLang) );          // adds to pAttrList
369     }
370 
371     SvXMLElementExport aElem( rExport,
372                               XML_NAMESPACE_NUMBER, XML_CURRENCY_SYMBOL,
373                               true, false );
374     rExport.Characters( rString );
375 }
376 
WriteBooleanElement_Impl()377 void SvXMLNumFmtExport::WriteBooleanElement_Impl()
378 {
379     FinishTextElement_Impl();
380 
381     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_BOOLEAN,
382                               true, false );
383 }
384 
WriteTextContentElement_Impl()385 void SvXMLNumFmtExport::WriteTextContentElement_Impl()
386 {
387     FinishTextElement_Impl();
388 
389     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_TEXT_CONTENT,
390                               true, false );
391 }
392 
393 //  date elements
394 
WriteDayElement_Impl(const OUString & rCalendar,bool bLong)395 void SvXMLNumFmtExport::WriteDayElement_Impl( const OUString& rCalendar, bool bLong )
396 {
397     FinishTextElement_Impl();
398 
399     AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
400     AddStyleAttr_Impl( bLong );     // adds to pAttrList
401 
402     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_DAY,
403                               true, false );
404 }
405 
WriteMonthElement_Impl(const OUString & rCalendar,bool bLong,bool bText)406 void SvXMLNumFmtExport::WriteMonthElement_Impl( const OUString& rCalendar, bool bLong, bool bText )
407 {
408     FinishTextElement_Impl();
409 
410     AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
411     AddStyleAttr_Impl( bLong );     // adds to pAttrList
412     if ( bText )
413     {
414         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TEXTUAL, XML_TRUE );
415     }
416 
417     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_MONTH,
418                               true, false );
419 }
420 
WriteYearElement_Impl(const OUString & rCalendar,bool bLong)421 void SvXMLNumFmtExport::WriteYearElement_Impl( const OUString& rCalendar, bool bLong )
422 {
423     FinishTextElement_Impl();
424 
425     AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
426     AddStyleAttr_Impl( bLong );     // adds to pAttrList
427 
428     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_YEAR,
429                               true, false );
430 }
431 
WriteEraElement_Impl(const OUString & rCalendar,bool bLong)432 void SvXMLNumFmtExport::WriteEraElement_Impl( const OUString& rCalendar, bool bLong )
433 {
434     FinishTextElement_Impl();
435 
436     AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
437     AddStyleAttr_Impl( bLong );     // adds to pAttrList
438 
439     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_ERA,
440                               true, false );
441 }
442 
WriteDayOfWeekElement_Impl(const OUString & rCalendar,bool bLong)443 void SvXMLNumFmtExport::WriteDayOfWeekElement_Impl( const OUString& rCalendar, bool bLong )
444 {
445     FinishTextElement_Impl();
446 
447     AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
448     AddStyleAttr_Impl( bLong );     // adds to pAttrList
449 
450     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_DAY_OF_WEEK,
451                               true, false );
452 }
453 
WriteWeekElement_Impl(const OUString & rCalendar)454 void SvXMLNumFmtExport::WriteWeekElement_Impl( const OUString& rCalendar )
455 {
456     FinishTextElement_Impl();
457 
458     AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
459 
460     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_WEEK_OF_YEAR,
461                               true, false );
462 }
463 
WriteQuarterElement_Impl(const OUString & rCalendar,bool bLong)464 void SvXMLNumFmtExport::WriteQuarterElement_Impl( const OUString& rCalendar, bool bLong )
465 {
466     FinishTextElement_Impl();
467 
468     AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
469     AddStyleAttr_Impl( bLong );     // adds to pAttrList
470 
471     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_QUARTER,
472                               true, false );
473 }
474 
475 //  time elements
476 
WriteHoursElement_Impl(bool bLong)477 void SvXMLNumFmtExport::WriteHoursElement_Impl( bool bLong )
478 {
479     FinishTextElement_Impl();
480 
481     AddStyleAttr_Impl( bLong );     // adds to pAttrList
482 
483     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_HOURS,
484                               true, false );
485 }
486 
WriteMinutesElement_Impl(bool bLong)487 void SvXMLNumFmtExport::WriteMinutesElement_Impl( bool bLong )
488 {
489     FinishTextElement_Impl();
490 
491     AddStyleAttr_Impl( bLong );     // adds to pAttrList
492 
493     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_MINUTES,
494                               true, false );
495 }
496 
WriteRepeatedElement_Impl(sal_Unicode nChar)497 void SvXMLNumFmtExport::WriteRepeatedElement_Impl( sal_Unicode nChar )
498 {
499     // Export only for 1.2 with extensions or 1.3 and later.
500     SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion();
501     if (eVersion > SvtSaveOptions::ODFSVER_012)
502     {
503         FinishTextElement_Impl(eVersion < SvtSaveOptions::ODFSVER_013);
504         // OFFICE-3765 For 1.2+ use loext namespace, for 1.3 use number namespace.
505         SvXMLElementExport aElem( rExport,
506                                   ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
507                                   XML_FILL_CHARACTER, true, false );
508         rExport.Characters( OUString( nChar ) );
509     }
510 }
511 
WriteSecondsElement_Impl(bool bLong,sal_uInt16 nDecimals)512 void SvXMLNumFmtExport::WriteSecondsElement_Impl( bool bLong, sal_uInt16 nDecimals )
513 {
514     FinishTextElement_Impl();
515 
516     AddStyleAttr_Impl( bLong );     // adds to pAttrList
517     if ( nDecimals > 0 )
518     {
519         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
520                               OUString::number(  nDecimals ) );
521     }
522 
523     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_SECONDS,
524                               true, false );
525 }
526 
WriteAMPMElement_Impl()527 void SvXMLNumFmtExport::WriteAMPMElement_Impl()
528 {
529     FinishTextElement_Impl();
530 
531     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_AM_PM,
532                               true, false );
533 }
534 
535 //  numbers
536 
WriteNumberElement_Impl(sal_Int32 nDecimals,sal_Int32 nMinDecimals,sal_Int32 nInteger,const OUString & rDashStr,bool bGrouping,sal_Int32 nTrailingThousands,const SvXMLEmbeddedTextEntryArr & rEmbeddedEntries)537 void SvXMLNumFmtExport::WriteNumberElement_Impl(
538                             sal_Int32 nDecimals, sal_Int32 nMinDecimals,
539                             sal_Int32 nInteger, const OUString& rDashStr,
540                             bool bGrouping, sal_Int32 nTrailingThousands,
541                             const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries )
542 {
543     FinishTextElement_Impl();
544 
545     //  decimals
546     if ( nDecimals >= 0 )   // negative = automatic
547     {
548         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
549                               OUString::number( nDecimals ) );
550     }
551 
552     if ( nMinDecimals >= 0 )   // negative = automatic
553     {
554         // Export only for 1.2 with extensions or 1.3 and later.
555         SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion();
556         if (eVersion > SvtSaveOptions::ODFSVER_012)
557         {
558             // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
559             rExport.AddAttribute(
560                 ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
561                                  XML_MIN_DECIMAL_PLACES,
562                                  OUString::number( nMinDecimals ) );
563         }
564     }
565 
566     //  integer digits
567     if ( nInteger >= 0 )    // negative = automatic
568     {
569         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
570                               OUString::number( nInteger ) );
571     }
572 
573     //  decimal replacement (dashes) or variable decimals (#)
574     if ( !rDashStr.isEmpty() ||  nMinDecimals < nDecimals )
575     {
576         // full variable decimals means an empty replacement string
577         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_REPLACEMENT,
578                               rDashStr );
579     }
580 
581     //  (automatic) grouping separator
582     if ( bGrouping )
583     {
584         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
585     }
586 
587     //  display-factor if there are trailing thousands separators
588     if ( nTrailingThousands )
589     {
590         //  each separator character removes three digits
591         double fFactor = ::rtl::math::pow10Exp( 1.0, 3 * nTrailingThousands );
592 
593         OUStringBuffer aFactStr;
594         ::sax::Converter::convertDouble( aFactStr, fFactor );
595         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DISPLAY_FACTOR, aFactStr.makeStringAndClear() );
596     }
597 
598     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_NUMBER,
599                               true, true );
600 
601     //  number:embedded-text as child elements
602 
603     auto nEntryCount = rEmbeddedEntries.size();
604     for (decltype(nEntryCount) nEntry=0; nEntry < nEntryCount; ++nEntry)
605     {
606         const SvXMLEmbeddedTextEntry *const pObj = &rEmbeddedEntries[nEntry];
607 
608         //  position attribute
609         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_POSITION,
610                                 OUString::number( pObj->nFormatPos ) );
611         SvXMLElementExport aChildElem( rExport, XML_NAMESPACE_NUMBER, XML_EMBEDDED_TEXT,
612                                           true, false );
613 
614         //  text as element content
615         OUStringBuffer aContent( pObj->aText );
616         while ( nEntry+1 < nEntryCount && rEmbeddedEntries[nEntry+1].nFormatPos == pObj->nFormatPos )
617         {
618             // The array can contain several elements for the same position in the number
619             // (for example, literal text and space from underscores). They must be merged
620             // into a single embedded-text element.
621             aContent.append(rEmbeddedEntries[nEntry+1].aText);
622             ++nEntry;
623         }
624         rExport.Characters( aContent.makeStringAndClear() );
625     }
626 }
627 
WriteScientificElement_Impl(sal_Int32 nDecimals,sal_Int32 nMinDecimals,sal_Int32 nInteger,bool bGrouping,sal_Int32 nExp,sal_Int32 nExpInterval,bool bExpSign)628 void SvXMLNumFmtExport::WriteScientificElement_Impl(
629                             sal_Int32 nDecimals, sal_Int32 nMinDecimals, sal_Int32 nInteger,
630                             bool bGrouping, sal_Int32 nExp, sal_Int32 nExpInterval, bool bExpSign )
631 {
632     FinishTextElement_Impl();
633 
634     //  decimals
635     if ( nDecimals >= 0 )   // negative = automatic
636     {
637         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
638                               OUString::number( nDecimals ) );
639     }
640 
641     SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion();
642     if ( nMinDecimals >= 0 )   // negative = automatic
643     {
644         // Export only for 1.2 with extensions or 1.3 and later.
645         if (eVersion > SvtSaveOptions::ODFSVER_012)
646         {
647             // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
648             rExport.AddAttribute(
649                 ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
650                                  XML_MIN_DECIMAL_PLACES,
651                                  OUString::number( nMinDecimals ) );
652         }
653     }
654 
655     //  integer digits
656     if ( nInteger >= 0 )    // negative = automatic
657     {
658         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
659                               OUString::number( nInteger ) );
660     }
661 
662     //  (automatic) grouping separator
663     if ( bGrouping )
664     {
665         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
666     }
667 
668     //  exponent digits
669     if ( nExp >= 0 )
670     {
671         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_EXPONENT_DIGITS,
672                               OUString::number( nExp ) );
673     }
674 
675     //  exponent interval for engineering notation
676     if ( nExpInterval >= 0 )
677     {
678         // Export only for 1.2 with extensions or 1.3 and later.
679         if (eVersion > SvtSaveOptions::ODFSVER_012)
680         {
681             // OFFICE-1828 For 1.2+ use loext namespace, for 1.3 use number namespace.
682             rExport.AddAttribute(
683                     ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
684                     XML_EXPONENT_INTERVAL, OUString::number( nExpInterval ) );
685         }
686     }
687 
688     //  exponent sign
689     // Export only for 1.2 with extensions or 1.3 and later.
690     if (eVersion > SvtSaveOptions::ODFSVER_012)
691     {
692         // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
693         rExport.AddAttribute(
694             ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
695                              XML_FORCED_EXPONENT_SIGN,
696                              bExpSign? XML_TRUE : XML_FALSE );
697     }
698 
699     SvXMLElementExport aElem( rExport,
700                               XML_NAMESPACE_NUMBER, XML_SCIENTIFIC_NUMBER,
701                               true, false );
702 }
703 
WriteFractionElement_Impl(sal_Int32 nInteger,bool bGrouping,const SvNumberformat & rFormat,sal_uInt16 nPart)704 void SvXMLNumFmtExport::WriteFractionElement_Impl(
705                             sal_Int32 nInteger, bool bGrouping,
706                             const SvNumberformat& rFormat, sal_uInt16 nPart )
707 {
708     FinishTextElement_Impl();
709     const OUString aNumeratorString = rFormat.GetNumeratorString( nPart );
710     const OUString aDenominatorString = rFormat.GetDenominatorString( nPart );
711     const OUString aIntegerFractionDelimiterString = rFormat.GetIntegerFractionDelimiterString( nPart );
712     sal_Int32 nMaxNumeratorDigits = aNumeratorString.getLength();
713     // Count '0' as '?'
714     sal_Int32 nMinNumeratorDigits = aNumeratorString.replaceAll("0","?").indexOf('?');
715     sal_Int32 nZerosNumeratorDigits = aNumeratorString.indexOf('0');
716     if ( nMinNumeratorDigits >= 0 )
717         nMinNumeratorDigits = nMaxNumeratorDigits - nMinNumeratorDigits;
718     else
719         nMinNumeratorDigits = 0;
720     if ( nZerosNumeratorDigits >= 0 )
721         nZerosNumeratorDigits = nMaxNumeratorDigits - nZerosNumeratorDigits;
722     else
723         nZerosNumeratorDigits = 0;
724     sal_Int32 nMaxDenominatorDigits = aDenominatorString.getLength();
725     sal_Int32 nMinDenominatorDigits = aDenominatorString.replaceAll("0","?").indexOf('?');
726     sal_Int32 nZerosDenominatorDigits = aDenominatorString.indexOf('0');
727     if ( nMinDenominatorDigits >= 0 )
728         nMinDenominatorDigits = nMaxDenominatorDigits - nMinDenominatorDigits;
729     else
730         nMinDenominatorDigits = 0;
731     if ( nZerosDenominatorDigits >= 0 )
732         nZerosDenominatorDigits = nMaxDenominatorDigits - nZerosDenominatorDigits;
733     else
734         nZerosDenominatorDigits = 0;
735     sal_Int32 nDenominator = aDenominatorString.toInt32();
736 
737     //  integer digits
738     if ( nInteger >= 0 )        // negative = default (no integer part)
739     {
740         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
741                               OUString::number( nInteger ) );
742     }
743 
744     //  (automatic) grouping separator
745     if ( bGrouping )
746     {
747         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
748     }
749 
750     // integer/fraction delimiter
751     SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion();
752     if ( !aIntegerFractionDelimiterString.isEmpty() && aIntegerFractionDelimiterString != " "
753         && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
754     {   // Export only for 1.2/1.3 with extensions.
755         rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_INTEGER_FRACTION_DELIMITER,
756                               aIntegerFractionDelimiterString );
757     }
758 
759     //  numerator digits
760     if ( nMinNumeratorDigits == 0 ) // at least one digit to keep compatibility with previous versions
761         nMinNumeratorDigits++;
762     rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_NUMERATOR_DIGITS,
763                           OUString::number( nMinNumeratorDigits ) );
764     // Export only for 1.2/1.3 with extensions.
765     if ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0)
766     {
767         // For extended ODF use loext namespace
768         rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_MAX_NUMERATOR_DIGITS,
769                               OUString::number( nMaxNumeratorDigits ) );
770     }
771     if ( nZerosNumeratorDigits && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
772         rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_ZEROS_NUMERATOR_DIGITS,
773                               OUString::number( nZerosNumeratorDigits ) );
774 
775     if ( nDenominator )
776     {
777         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DENOMINATOR_VALUE,
778                               OUString::number( nDenominator) );
779     }
780     //  it's not necessary to export nDenominatorDigits
781     //  if we have a forced denominator
782     else
783     {
784         if ( nMinDenominatorDigits == 0 ) // at least one digit to keep compatibility with previous versions
785             nMinDenominatorDigits++;
786         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_DENOMINATOR_DIGITS,
787                               OUString::number( nMinDenominatorDigits ) );
788         if (eVersion > SvtSaveOptions::ODFSVER_012)
789         {
790             // OFFICE-3695 For 1.2+ use loext namespace, for 1.3 use number namespace.
791             rExport.AddAttribute(
792                 ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
793                                  XML_MAX_DENOMINATOR_VALUE,
794                                  OUString::number( pow ( 10.0, nMaxDenominatorDigits ) - 1 ) ); // 9, 99 or 999
795         }
796         if ( nZerosDenominatorDigits && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
797             rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_ZEROS_DENOMINATOR_DIGITS,
798                                   OUString::number( nZerosDenominatorDigits ) );
799     }
800 
801     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_FRACTION,
802                               true, false );
803 }
804 
805 //  mapping (condition)
806 
WriteMapElement_Impl(sal_Int32 nOp,double fLimit,sal_Int32 nKey,sal_Int32 nPart)807 void SvXMLNumFmtExport::WriteMapElement_Impl( sal_Int32 nOp, double fLimit,
808                                                 sal_Int32 nKey, sal_Int32 nPart )
809 {
810     FinishTextElement_Impl();
811 
812     if ( nOp == NUMBERFORMAT_OP_NO )
813         return;
814 
815     // style namespace
816 
817     OUStringBuffer aCondStr(20);
818     aCondStr.append( "value()" );          //! define constant
819     switch ( nOp )
820     {
821         case NUMBERFORMAT_OP_EQ: aCondStr.append( '=' );  break;
822         case NUMBERFORMAT_OP_NE: aCondStr.append( "!=" );          break;
823         case NUMBERFORMAT_OP_LT: aCondStr.append( '<' );  break;
824         case NUMBERFORMAT_OP_LE: aCondStr.append( "<=" );          break;
825         case NUMBERFORMAT_OP_GT: aCondStr.append( '>' );  break;
826         case NUMBERFORMAT_OP_GE: aCondStr.append( ">=" );          break;
827         default:
828             OSL_FAIL("unknown operator");
829     }
830     ::rtl::math::doubleToUStringBuffer( aCondStr, fLimit,
831             rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
832             '.', true );
833 
834     rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_CONDITION,
835                           aCondStr.makeStringAndClear() );
836 
837     rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_APPLY_STYLE_NAME,
838                           rExport.EncodeStyleName( lcl_CreateStyleName( nKey, nPart, false,
839                                                sPrefix ) ) );
840 
841     SvXMLElementExport aElem( rExport, XML_NAMESPACE_STYLE, XML_MAP,
842                               true, false );
843 }
844 
845 //  for old (automatic) currency formats: parse currency symbol from text
846 
lcl_FindSymbol(const OUString & sUpperStr,std::u16string_view sCurString)847 static sal_Int32 lcl_FindSymbol( const OUString& sUpperStr, std::u16string_view sCurString )
848 {
849     //  search for currency symbol
850     //  Quoting as in ImpSvNumberformatScan::Symbol_Division
851 
852     sal_Int32 nCPos = 0;
853     while (nCPos >= 0)
854     {
855         nCPos = sUpperStr.indexOf( sCurString, nCPos );
856         if (nCPos >= 0)
857         {
858             // in Quotes?
859             sal_Int32 nQ = SvNumberformat::GetQuoteEnd( sUpperStr, nCPos );
860             if ( nQ < 0 )
861             {
862                 //  dm can be escaped as "dm or \d
863                 sal_Unicode c;
864                 if ( nCPos == 0 )
865                     return nCPos;                   // found
866                 c = sUpperStr[nCPos-1];
867                 if ( c != '"' && c != '\\')
868                 {
869                     return nCPos;                   // found
870                 }
871                 else
872                 {
873                     nCPos++;                        // continue
874                 }
875             }
876             else
877             {
878                 nCPos = nQ + 1;                     // continue after quote end
879             }
880         }
881     }
882     return -1;
883 }
884 
WriteTextWithCurrency_Impl(const OUString & rString,const css::lang::Locale & rLocale)885 bool SvXMLNumFmtExport::WriteTextWithCurrency_Impl( const OUString& rString,
886                             const css::lang::Locale& rLocale )
887 {
888     //  returns true if currency element was written
889 
890     bool bRet = false;
891 
892     LanguageTag aLanguageTag( rLocale );
893     pFormatter->ChangeIntl( aLanguageTag.getLanguageType( false) );
894     OUString sCurString, sDummy;
895     pFormatter->GetCompatibilityCurrency( sCurString, sDummy );
896 
897     pCharClass->setLanguageTag( aLanguageTag );
898     OUString sUpperStr = pCharClass->uppercase(rString);
899     sal_Int32 nPos = lcl_FindSymbol( sUpperStr, sCurString );
900     if ( nPos >= 0 )
901     {
902         sal_Int32 nLength = rString.getLength();
903         sal_Int32 nCurLen = sCurString.getLength();
904         sal_Int32 nCont = nPos + nCurLen;
905 
906         //  text before currency symbol
907         if ( nPos > 0 )
908         {
909             AddToTextElement_Impl( rString.subView( 0, nPos ) );
910         }
911         //  currency symbol (empty string -> default)
912         WriteCurrencyElement_Impl( "", "" );
913         bRet = true;
914 
915         //  text after currency symbol
916         if ( nCont < nLength )
917         {
918             AddToTextElement_Impl( rString.subView( nCont, nLength-nCont ) );
919         }
920     }
921     else
922     {
923         AddToTextElement_Impl( rString );       // simple text
924     }
925 
926     return bRet;        // true: currency element written
927 }
928 
lcl_GetDefaultCalendar(SvNumberFormatter const * pFormatter,LanguageType nLang)929 static OUString lcl_GetDefaultCalendar( SvNumberFormatter const * pFormatter, LanguageType nLang )
930 {
931     //  get name of first non-gregorian calendar for the language
932 
933     OUString aCalendar;
934     CalendarWrapper* pCalendar = pFormatter->GetCalendar();
935     if (pCalendar)
936     {
937         lang::Locale aLocale( LanguageTag::convertToLocale( nLang ) );
938 
939         uno::Sequence<OUString> aCals = pCalendar->getAllCalendars( aLocale );
940         auto pCal = std::find_if(aCals.begin(), aCals.end(),
941             [](const OUString& rCal) { return rCal != "gregorian"; });
942         if (pCal != aCals.end())
943             aCalendar = *pCal;
944     }
945     return aCalendar;
946 }
947 
lcl_IsInEmbedded(const SvXMLEmbeddedTextEntryArr & rEmbeddedEntries,sal_uInt16 nPos)948 static bool lcl_IsInEmbedded( const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries, sal_uInt16 nPos )
949 {
950     auto nCount = rEmbeddedEntries.size();
951     for (decltype(nCount) i=0; i<nCount; i++)
952         if ( rEmbeddedEntries[i].nSourcePos == nPos )
953             return true;
954 
955     return false;       // not found
956 }
957 
lcl_IsDefaultDateFormat(const SvNumberformat & rFormat,bool bSystemDate,NfIndexTableOffset eBuiltIn)958 static bool lcl_IsDefaultDateFormat( const SvNumberformat& rFormat, bool bSystemDate, NfIndexTableOffset eBuiltIn )
959 {
960     //  make an extra loop to collect date elements, to check if it is a default format
961     //  before adding the automatic-order attribute
962 
963     SvXMLDateElementAttributes eDateDOW = XML_DEA_NONE;
964     SvXMLDateElementAttributes eDateDay = XML_DEA_NONE;
965     SvXMLDateElementAttributes eDateMonth = XML_DEA_NONE;
966     SvXMLDateElementAttributes eDateYear = XML_DEA_NONE;
967     SvXMLDateElementAttributes eDateHours = XML_DEA_NONE;
968     SvXMLDateElementAttributes eDateMins = XML_DEA_NONE;
969     SvXMLDateElementAttributes eDateSecs = XML_DEA_NONE;
970     bool bDateNoDefault = false;
971 
972     sal_uInt16 nPos = 0;
973     bool bEnd = false;
974     short nLastType = 0;
975     while (!bEnd)
976     {
977         short nElemType = rFormat.GetNumForType( 0, nPos );
978         switch ( nElemType )
979         {
980             case 0:
981                 if ( nLastType == NF_SYMBOLTYPE_STRING )
982                     bDateNoDefault = true;  // text at the end -> no default date format
983                 bEnd = true;                // end of format reached
984                 break;
985             case NF_SYMBOLTYPE_STRING:
986             case NF_SYMBOLTYPE_DATESEP:
987             case NF_SYMBOLTYPE_TIMESEP:
988             case NF_SYMBOLTYPE_TIME100SECSEP:
989                 // text is ignored, except at the end
990                 break;
991             // same mapping as in SvXMLNumFormatContext::AddNfKeyword:
992             case NF_KEY_NN:     eDateDOW = XML_DEA_SHORT;       break;
993             case NF_KEY_NNN:
994             case NF_KEY_NNNN:   eDateDOW = XML_DEA_LONG;        break;
995             case NF_KEY_D:      eDateDay = XML_DEA_SHORT;       break;
996             case NF_KEY_DD:     eDateDay = XML_DEA_LONG;        break;
997             case NF_KEY_M:      eDateMonth = XML_DEA_SHORT;     break;
998             case NF_KEY_MM:     eDateMonth = XML_DEA_LONG;      break;
999             case NF_KEY_MMM:    eDateMonth = XML_DEA_TEXTSHORT; break;
1000             case NF_KEY_MMMM:   eDateMonth = XML_DEA_TEXTLONG;  break;
1001             case NF_KEY_YY:     eDateYear = XML_DEA_SHORT;      break;
1002             case NF_KEY_YYYY:   eDateYear = XML_DEA_LONG;       break;
1003             case NF_KEY_H:      eDateHours = XML_DEA_SHORT;     break;
1004             case NF_KEY_HH:     eDateHours = XML_DEA_LONG;      break;
1005             case NF_KEY_MI:     eDateMins = XML_DEA_SHORT;      break;
1006             case NF_KEY_MMI:    eDateMins = XML_DEA_LONG;       break;
1007             case NF_KEY_S:      eDateSecs = XML_DEA_SHORT;      break;
1008             case NF_KEY_SS:     eDateSecs = XML_DEA_LONG;       break;
1009             case NF_KEY_AP:
1010             case NF_KEY_AMPM:   break;          // AM/PM may or may not be in date/time formats -> ignore by itself
1011             default:
1012                 bDateNoDefault = true;      // any other element -> no default format
1013         }
1014         nLastType = nElemType;
1015         ++nPos;
1016     }
1017 
1018     if ( bDateNoDefault )
1019         return false;                       // additional elements
1020     else
1021     {
1022         NfIndexTableOffset eFound = static_cast<NfIndexTableOffset>(SvXMLNumFmtDefaults::GetDefaultDateFormat(
1023                 eDateDOW, eDateDay, eDateMonth, eDateYear, eDateHours, eDateMins, eDateSecs, bSystemDate ));
1024 
1025         return ( eFound == eBuiltIn );
1026     }
1027 }
1028 
1029 //  export one part (condition)
1030 
ExportPart_Impl(const SvNumberformat & rFormat,sal_uInt32 nKey,sal_uInt32 nRealKey,sal_uInt16 nPart,bool bDefPart)1031 void SvXMLNumFmtExport::ExportPart_Impl( const SvNumberformat& rFormat, sal_uInt32 nKey, sal_uInt32 nRealKey,
1032                                             sal_uInt16 nPart, bool bDefPart )
1033 {
1034     //! for the default part, pass the conditions from the other parts!
1035 
1036     //  element name
1037 
1038     NfIndexTableOffset eBuiltIn = pFormatter->GetIndexTableOffset( nRealKey );
1039 
1040     SvNumFormatType nFmtType = SvNumFormatType::ALL;
1041     bool bThousand = false;
1042     sal_uInt16 nPrecision = 0;
1043     sal_uInt16 nLeading = 0;
1044     rFormat.GetNumForInfo( nPart, nFmtType, bThousand, nPrecision, nLeading);
1045     nFmtType &= ~SvNumFormatType::DEFINED;
1046 
1047     //  special treatment of builtin formats that aren't detected by normal parsing
1048     //  (the same formats that get the type set in SvNumberFormatter::ImpGenerateFormats)
1049     if ( eBuiltIn == NF_NUMBER_STANDARD )
1050         nFmtType = SvNumFormatType::NUMBER;
1051     else if ( eBuiltIn == NF_BOOLEAN )
1052         nFmtType = SvNumFormatType::LOGICAL;
1053     else if ( eBuiltIn == NF_TEXT )
1054         nFmtType = SvNumFormatType::TEXT;
1055 
1056     // #101606# An empty subformat is a valid number-style resulting in an
1057     // empty display string for the condition of the subformat.
1058 
1059     XMLTokenEnum eType = XML_TOKEN_INVALID;
1060     switch ( nFmtType )
1061     {
1062         // Type UNDEFINED likely is a crappy format string for that we could
1063         // not decide on any format type (and maybe could try harder?), but the
1064         // resulting XMLTokenEnum should be something valid, so make that
1065         // number-style.
1066         case SvNumFormatType::UNDEFINED:
1067             SAL_WARN("xmloff.style","UNDEFINED number format: '" << rFormat.GetFormatstring() << "'");
1068             [[fallthrough]];
1069         // Type is 0 if a format contains no recognized elements
1070         // (like text only) - this is handled as a number-style.
1071         case SvNumFormatType::ALL:
1072         case SvNumFormatType::EMPTY:
1073         case SvNumFormatType::NUMBER:
1074         case SvNumFormatType::SCIENTIFIC:
1075         case SvNumFormatType::FRACTION:
1076             eType = XML_NUMBER_STYLE;
1077             break;
1078         case SvNumFormatType::PERCENT:
1079             eType = XML_PERCENTAGE_STYLE;
1080             break;
1081         case SvNumFormatType::CURRENCY:
1082             eType = XML_CURRENCY_STYLE;
1083             break;
1084         case SvNumFormatType::DATE:
1085         case SvNumFormatType::DATETIME:
1086             eType = XML_DATE_STYLE;
1087             break;
1088         case SvNumFormatType::TIME:
1089             eType = XML_TIME_STYLE;
1090             break;
1091         case SvNumFormatType::TEXT:
1092             eType = XML_TEXT_STYLE;
1093             break;
1094         case SvNumFormatType::LOGICAL:
1095             eType = XML_BOOLEAN_STYLE;
1096             break;
1097         default: break;
1098     }
1099     SAL_WARN_IF( eType == XML_TOKEN_INVALID, "xmloff.style", "unknown format type" );
1100 
1101     OUString sAttrValue;
1102     bool bUserDef( rFormat.GetType() & SvNumFormatType::DEFINED );
1103 
1104     //  common attributes for format
1105 
1106     //  format name (generated from key) - style namespace
1107     rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_NAME,
1108                         lcl_CreateStyleName( nKey, nPart, bDefPart, sPrefix ) );
1109 
1110     //  "volatile" attribute for styles used only in maps
1111     if ( !bDefPart )
1112         rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_VOLATILE, XML_TRUE );
1113 
1114     //  language / country
1115     LanguageType nLang = rFormat.GetLanguage();
1116     AddLanguageAttr_Impl( nLang );                  // adds to pAttrList
1117 
1118     //  title (comment)
1119     //  titles for builtin formats are not written
1120     sAttrValue = rFormat.GetComment();
1121     if ( !sAttrValue.isEmpty() && bUserDef && bDefPart )
1122     {
1123         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TITLE, sAttrValue );
1124     }
1125 
1126     //  automatic ordering for currency and date formats
1127     //  only used for some built-in formats
1128     bool bAutoOrder = ( eBuiltIn == NF_CURRENCY_1000INT     || eBuiltIn == NF_CURRENCY_1000DEC2 ||
1129                         eBuiltIn == NF_CURRENCY_1000INT_RED || eBuiltIn == NF_CURRENCY_1000DEC2_RED ||
1130                         eBuiltIn == NF_CURRENCY_1000DEC2_DASHED ||
1131                         eBuiltIn == NF_DATE_SYSTEM_SHORT    || eBuiltIn == NF_DATE_SYSTEM_LONG ||
1132                         eBuiltIn == NF_DATE_SYS_MMYY        || eBuiltIn == NF_DATE_SYS_DDMMM ||
1133                         eBuiltIn == NF_DATE_SYS_DDMMYYYY    || eBuiltIn == NF_DATE_SYS_DDMMYY ||
1134                         eBuiltIn == NF_DATE_SYS_DMMMYY      || eBuiltIn == NF_DATE_SYS_DMMMYYYY ||
1135                         eBuiltIn == NF_DATE_SYS_DMMMMYYYY   || eBuiltIn == NF_DATE_SYS_NNDMMMYY ||
1136                         eBuiltIn == NF_DATE_SYS_NNDMMMMYYYY || eBuiltIn == NF_DATE_SYS_NNNNDMMMMYYYY ||
1137                         eBuiltIn == NF_DATETIME_SYSTEM_SHORT_HHMM || eBuiltIn == NF_DATETIME_SYS_DDMMYYYY_HHMM ||
1138                         eBuiltIn == NF_DATETIME_SYS_DDMMYYYY_HHMMSS );
1139 
1140     //  format source (for date and time formats)
1141     //  only used for some built-in formats
1142     bool bSystemDate = ( eBuiltIn == NF_DATE_SYSTEM_SHORT ||
1143                          eBuiltIn == NF_DATE_SYSTEM_LONG  ||
1144                          eBuiltIn == NF_DATETIME_SYSTEM_SHORT_HHMM );
1145     bool bLongSysDate = ( eBuiltIn == NF_DATE_SYSTEM_LONG );
1146 
1147     // check if the format definition matches the key
1148     if ( bAutoOrder && ( nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) &&
1149             !lcl_IsDefaultDateFormat( rFormat, bSystemDate, eBuiltIn ) )
1150     {
1151         bAutoOrder = bSystemDate = bLongSysDate = false;        // don't write automatic-order attribute then
1152     }
1153 
1154     if ( bAutoOrder &&
1155         ( nFmtType == SvNumFormatType::CURRENCY || nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) )
1156     {
1157         //  #85109# format type must be checked to avoid dtd errors if
1158         //  locale data contains other format types at the built-in positions
1159 
1160         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_AUTOMATIC_ORDER,
1161                               XML_TRUE );
1162     }
1163 
1164     if ( bSystemDate && bAutoOrder &&
1165         ( nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) )
1166     {
1167         //  #85109# format type must be checked to avoid dtd errors if
1168         //  locale data contains other format types at the built-in positions
1169 
1170         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_FORMAT_SOURCE,
1171                               XML_LANGUAGE );
1172     }
1173 
1174     //  overflow for time formats as in [hh]:mm
1175     //  controlled by bThousand from number format info
1176     //  default for truncate-on-overflow is true
1177     if ( nFmtType == SvNumFormatType::TIME && bThousand )
1178     {
1179         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRUNCATE_ON_OVERFLOW,
1180                               XML_FALSE );
1181     }
1182 
1183     // Native number transliteration
1184     css::i18n::NativeNumberXmlAttributes2 aAttr;
1185     rFormat.GetNatNumXml( aAttr, nPart );
1186     if ( !aAttr.Format.isEmpty() )
1187     {
1188         assert(aAttr.Spellout.isEmpty());   // mutually exclusive
1189 
1190         /* FIXME-BCP47: ODF defines no transliteration-script or
1191          * transliteration-rfc-language-tag */
1192         LanguageTag aLanguageTag( aAttr.Locale);
1193         OUString aLanguage, aScript, aCountry;
1194         aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
1195         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_FORMAT,
1196                               aAttr.Format );
1197         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_LANGUAGE,
1198                               aLanguage );
1199         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_COUNTRY,
1200                               aCountry );
1201         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_STYLE,
1202                               aAttr.Style );
1203     }
1204 
1205     if ( !aAttr.Spellout.isEmpty() )
1206     {
1207         const bool bWriteSpellout = aAttr.Format.isEmpty();
1208         assert(bWriteSpellout);     // mutually exclusive
1209 
1210         // Export only for 1.2 with extensions or 1.3 and later.
1211         SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion();
1212         // Also ensure that duplicated transliteration-language and
1213         // transliteration-country attributes never escape into the wild with
1214         // releases.
1215         if (eVersion > SvtSaveOptions::ODFSVER_012 && bWriteSpellout)
1216         {
1217             /* FIXME-BCP47: ODF defines no transliteration-script or
1218              * transliteration-rfc-language-tag */
1219             LanguageTag aLanguageTag( aAttr.Locale);
1220             OUString aLanguage, aScript, aCountry;
1221             aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
1222             // For 1.2/1.3+ use loext namespace.
1223             rExport.AddAttribute( /*((eVersion < SvtSaveOptions::ODFSVER_)
1224                         ? */ XML_NAMESPACE_LO_EXT /*: XML_NAMESPACE_NUMBER)*/,
1225                     XML_TRANSLITERATION_SPELLOUT, aAttr.Spellout );
1226             rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_LANGUAGE,
1227                                   aLanguage );
1228             rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_COUNTRY,
1229                                   aCountry );
1230         }
1231     }
1232 
1233     // The element
1234     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, eType,
1235                               true, true );
1236 
1237     //  color (properties element)
1238 
1239     const Color* pCol = rFormat.GetColor( nPart );
1240     if (pCol)
1241         WriteColorElement_Impl(*pCol);
1242 
1243     //  detect if there is "real" content, excluding color and maps
1244     //! move to implementation of Write... methods?
1245     bool bAnyContent = false;
1246 
1247     //  format elements
1248 
1249     SvXMLEmbeddedTextEntryArr aEmbeddedEntries;
1250     if ( eBuiltIn == NF_NUMBER_STANDARD )
1251     {
1252         //  default number format contains just one number element
1253         WriteNumberElement_Impl( -1, -1, 1, OUString(), false, 0, aEmbeddedEntries );
1254         bAnyContent = true;
1255     }
1256     else if ( eBuiltIn == NF_BOOLEAN )
1257     {
1258         //  boolean format contains just one boolean element
1259         WriteBooleanElement_Impl();
1260         bAnyContent = true;
1261     }
1262     else
1263     {
1264         //  first loop to collect attributes
1265 
1266         bool bDecDashes  = false;
1267         bool bExpFound   = false;
1268         bool bCurrFound  = false;
1269         bool bInInteger  = true;
1270         bool bExpSign = true;
1271         bool bDecAlign   = false;               // decimal alignment with "?"
1272         sal_Int32 nExpDigits = 0;
1273         sal_Int32 nIntegerSymbols = 0;          // for embedded-text, including "#"
1274         sal_Int32 nTrailingThousands = 0;       // thousands-separators after all digits
1275         sal_Int32 nMinDecimals = nPrecision;
1276         OUString sCurrExt;
1277         OUString aCalendar;
1278         bool bImplicitOtherCalendar = false;
1279         bool bExplicitCalendar = false;
1280         sal_uInt16 nPos = 0;
1281         bool bEnd = false;
1282         while (!bEnd)
1283         {
1284             short nElemType = rFormat.GetNumForType( nPart, nPos );
1285             const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1286 
1287             switch ( nElemType )
1288             {
1289                 case 0:
1290                     bEnd = true;                // end of format reached
1291                     break;
1292                 case NF_SYMBOLTYPE_DIGIT:
1293                     if ( bExpFound && pElemStr )
1294                         nExpDigits += pElemStr->getLength();
1295                     else if ( !bDecDashes && pElemStr && (*pElemStr)[0] == '-' )
1296                     {
1297                         bDecDashes = true;
1298                         nMinDecimals = 0;
1299                     }
1300                     else if ( !bInInteger && pElemStr )
1301                     {
1302                         for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 ; i-- )
1303                         {
1304                             sal_Unicode aChar = (*pElemStr)[i];
1305                             if ( aChar == '#' || aChar == '?' )
1306                             {
1307                                 nMinDecimals --;
1308                                 if ( aChar == '?' )
1309                                     bDecAlign = true;
1310                             }
1311                             else
1312                                 break;
1313                         }
1314                     }
1315                     if ( bInInteger && pElemStr )
1316                         nIntegerSymbols += pElemStr->getLength();
1317                     nTrailingThousands = 0;
1318                     break;
1319                 case NF_SYMBOLTYPE_DECSEP:
1320                     bInInteger = false;
1321                     break;
1322                 case NF_SYMBOLTYPE_THSEP:
1323                     if (pElemStr)
1324                         nTrailingThousands += pElemStr->getLength();      // is reset to 0 if digits follow
1325                     break;
1326                 case NF_SYMBOLTYPE_EXP:
1327                     bExpFound = true;           // following digits are exponent digits
1328                     bInInteger = false;
1329                     if ( pElemStr && ( pElemStr->getLength() == 1
1330                                   || ( pElemStr->getLength() == 2 && (*pElemStr)[1] == '-' ) ) )
1331                         bExpSign = false;       // for 0.00E0 or 0.00E-00
1332                     break;
1333                 case NF_SYMBOLTYPE_CURRENCY:
1334                     bCurrFound = true;
1335                     break;
1336                 case NF_SYMBOLTYPE_CURREXT:
1337                     if (pElemStr)
1338                         sCurrExt = *pElemStr;
1339                     break;
1340 
1341                 // E, EE, R, RR: select non-gregorian calendar
1342                 // AAA, AAAA: calendar is switched at the position of the element
1343                 case NF_KEY_EC:
1344                 case NF_KEY_EEC:
1345                 case NF_KEY_R:
1346                 case NF_KEY_RR:
1347                     if (aCalendar.isEmpty())
1348                     {
1349                         aCalendar = lcl_GetDefaultCalendar( pFormatter, nLang );
1350                         bImplicitOtherCalendar = true;
1351                     }
1352                     break;
1353             }
1354             ++nPos;
1355         }
1356 
1357         //  collect strings for embedded-text (must be known before number element is written)
1358 
1359         bool bAllowEmbedded = ( nFmtType == SvNumFormatType::ALL || nFmtType == SvNumFormatType::NUMBER ||
1360                                         nFmtType == SvNumFormatType::CURRENCY ||
1361                                         nFmtType == SvNumFormatType::PERCENT );
1362         if ( bAllowEmbedded )
1363         {
1364             sal_Int32 nDigitsPassed = 0;
1365             nPos = 0;
1366             bEnd = false;
1367             while (!bEnd)
1368             {
1369                 short nElemType = rFormat.GetNumForType( nPart, nPos );
1370                 const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1371 
1372                 switch ( nElemType )
1373                 {
1374                     case 0:
1375                         bEnd = true;                // end of format reached
1376                         break;
1377                     case NF_SYMBOLTYPE_DIGIT:
1378                         if ( pElemStr )
1379                             nDigitsPassed += pElemStr->getLength();
1380                         break;
1381                     case NF_SYMBOLTYPE_STRING:
1382                     case NF_SYMBOLTYPE_BLANK:
1383                     case NF_SYMBOLTYPE_PERCENT:
1384                         if ( nDigitsPassed > 0 && nDigitsPassed < nIntegerSymbols && pElemStr )
1385                         {
1386                             //  text (literal or underscore) within the integer part of a number:number element
1387 
1388                             OUString aEmbeddedStr;
1389                             if ( nElemType == NF_SYMBOLTYPE_STRING || nElemType == NF_SYMBOLTYPE_PERCENT )
1390                             {
1391                                 aEmbeddedStr = *pElemStr;
1392                             }
1393                             else if (pElemStr->getLength() >= 2)
1394                             {
1395                                 SvNumberformat::InsertBlanks( aEmbeddedStr, 0, (*pElemStr)[1] );
1396                             }
1397                             sal_Int32 nEmbedPos = nIntegerSymbols - nDigitsPassed;
1398 
1399                             aEmbeddedEntries.push_back(
1400                                 SvXMLEmbeddedTextEntry(nPos, nEmbedPos, aEmbeddedStr));
1401                         }
1402                         break;
1403                 }
1404                 ++nPos;
1405             }
1406         }
1407 
1408         //  final loop to write elements
1409 
1410         bool bNumWritten = false;
1411         bool bCurrencyWritten = false;
1412         short nPrevType = 0;
1413         nPos = 0;
1414         bEnd = false;
1415         while (!bEnd)
1416         {
1417             short nElemType = rFormat.GetNumForType( nPart, nPos );
1418             const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1419 
1420             switch ( nElemType )
1421             {
1422                 case 0:
1423                     bEnd = true;                // end of format reached
1424                     if (bHasText && sTextContent.isEmpty())
1425                         bHasText = false;       // don't write trailing empty text
1426                     break;
1427                 case NF_SYMBOLTYPE_STRING:
1428                 case NF_SYMBOLTYPE_DATESEP:
1429                 case NF_SYMBOLTYPE_TIMESEP:
1430                 case NF_SYMBOLTYPE_TIME100SECSEP:
1431                 case NF_SYMBOLTYPE_PERCENT:
1432                     if (pElemStr)
1433                     {
1434                         if ( ( nPrevType == NF_KEY_S || nPrevType == NF_KEY_SS ) &&
1435                              ( nElemType == NF_SYMBOLTYPE_TIME100SECSEP ) &&
1436                              nPrecision > 0 )
1437                         {
1438                             //  decimal separator after seconds is implied by
1439                             //  "decimal-places" attribute and must not be written
1440                             //  as text element
1441                             //! difference between '.' and ',' is lost here
1442                         }
1443                         else if ( lcl_IsInEmbedded( aEmbeddedEntries, nPos ) )
1444                         {
1445                             //  text is written as embedded-text child of the number,
1446                             //  don't create a text element
1447                         }
1448                         else if ( nFmtType == SvNumFormatType::CURRENCY && !bCurrFound && !bCurrencyWritten )
1449                         {
1450                             //  automatic currency symbol is implemented as part of
1451                             //  normal text -> search for the symbol
1452                             bCurrencyWritten = WriteTextWithCurrency_Impl( *pElemStr,
1453                                 LanguageTag::convertToLocale( nLang ) );
1454                             bAnyContent = true;
1455                         }
1456                         else
1457                             AddToTextElement_Impl( *pElemStr );
1458                     }
1459                     break;
1460                 case NF_SYMBOLTYPE_BLANK:
1461                     if ( pElemStr && !lcl_IsInEmbedded( aEmbeddedEntries, nPos ) )
1462                     {
1463                         //  turn "_x" into the number of spaces used for x in InsertBlanks in the NumberFormat
1464                         //  (#i20396# the spaces may also be in embedded-text elements)
1465 
1466                         OUString aBlanks;
1467                         if (pElemStr->getLength() >= 2)
1468                             SvNumberformat::InsertBlanks( aBlanks, 0, (*pElemStr)[1] );
1469                         AddToTextElement_Impl( aBlanks );
1470                     }
1471                     break;
1472                 case NF_KEY_GENERAL :
1473                         WriteNumberElement_Impl( -1, -1, 1, OUString(), false, 0, aEmbeddedEntries );
1474                         bAnyContent = true;
1475                     break;
1476                 case NF_KEY_CCC:
1477                     if (pElemStr)
1478                     {
1479                         if ( bCurrencyWritten )
1480                             AddToTextElement_Impl( *pElemStr );     // never more than one currency element
1481                         else
1482                         {
1483                             //! must be different from short automatic format
1484                             //! but should still be empty (meaning automatic)
1485                             //  pElemStr is "CCC"
1486 
1487                             WriteCurrencyElement_Impl( *pElemStr, OUString() );
1488                             bAnyContent = true;
1489                             bCurrencyWritten = true;
1490                         }
1491                     }
1492                     break;
1493                 case NF_SYMBOLTYPE_CURRENCY:
1494                     if (pElemStr)
1495                     {
1496                         if ( bCurrencyWritten )
1497                             AddToTextElement_Impl( *pElemStr );     // never more than one currency element
1498                         else
1499                         {
1500                             WriteCurrencyElement_Impl( *pElemStr, sCurrExt );
1501                             bAnyContent = true;
1502                             bCurrencyWritten = true;
1503                         }
1504                     }
1505                     break;
1506                 case NF_SYMBOLTYPE_DIGIT:
1507                     if (!bNumWritten)           // write number part
1508                     {
1509                         switch ( nFmtType )
1510                         {
1511                             // for type 0 (not recognized as a special type),
1512                             // write a "normal" number
1513                             case SvNumFormatType::ALL:
1514                             case SvNumFormatType::NUMBER:
1515                             case SvNumFormatType::CURRENCY:
1516                             case SvNumFormatType::PERCENT:
1517                                 {
1518                                     //  decimals
1519                                     //  only some built-in formats have automatic decimals
1520                                     sal_Int32 nDecimals = nPrecision;   // from GetFormatSpecialInfo
1521                                     if ( eBuiltIn == NF_NUMBER_STANDARD ||
1522                                          eBuiltIn == NF_CURRENCY_1000DEC2 ||
1523                                          eBuiltIn == NF_CURRENCY_1000DEC2_RED ||
1524                                          eBuiltIn == NF_CURRENCY_1000DEC2_CCC ||
1525                                          eBuiltIn == NF_CURRENCY_1000DEC2_DASHED )
1526                                         nDecimals = -1;
1527 
1528                                     //  integer digits
1529                                     //  only one built-in format has automatic integer digits
1530                                     sal_Int32 nInteger = nLeading;
1531                                     if ( eBuiltIn == NF_NUMBER_SYSTEM )
1532                                         nInteger = -1;
1533 
1534                                     //  string for decimal replacement
1535                                     //  has to be taken from nPrecision
1536                                     //  (positive number even for automatic decimals)
1537                                     OUStringBuffer sDashStr;
1538                                     if (bDecDashes && nPrecision > 0)
1539                                         comphelper::string::padToLength(sDashStr, nPrecision, '-');
1540                                     // "?" in decimal part are replaced by space character
1541                                     if (bDecAlign && nPrecision > 0)
1542                                         sDashStr = " ";
1543 
1544                                     WriteNumberElement_Impl(nDecimals, nMinDecimals, nInteger, sDashStr.makeStringAndClear(),
1545                                         bThousand, nTrailingThousands, aEmbeddedEntries);
1546                                     bAnyContent = true;
1547                                 }
1548                                 break;
1549                             case SvNumFormatType::SCIENTIFIC:
1550                                 // #i43959# for scientific numbers, count all integer symbols ("0" and "#")
1551                                 // as integer digits: use nIntegerSymbols instead of nLeading
1552                                 // nIntegerSymbols represents exponent interval (for engineering notation)
1553                                 WriteScientificElement_Impl( nPrecision, nMinDecimals, nLeading, bThousand, nExpDigits, nIntegerSymbols, bExpSign );
1554                                 bAnyContent = true;
1555                                 break;
1556                             case SvNumFormatType::FRACTION:
1557                                 {
1558                                     sal_Int32 nInteger = nLeading;
1559                                     if ( rFormat.GetNumForNumberElementCount( nPart ) == 3 )
1560                                     {
1561                                         //  If there is only two numbers + fraction in format string
1562                                         //  the fraction doesn't have an integer part, and no
1563                                         //  min-integer-digits attribute must be written.
1564                                         nInteger = -1;
1565                                     }
1566                                     WriteFractionElement_Impl( nInteger, bThousand,  rFormat, nPart );
1567                                     bAnyContent = true;
1568                                 }
1569                                 break;
1570                             default: break;
1571                         }
1572 
1573                         bNumWritten = true;
1574                     }
1575                     break;
1576                 case NF_SYMBOLTYPE_DECSEP:
1577                     if ( pElemStr && nPrecision == 0 )
1578                     {
1579                         //  A decimal separator after the number, without following decimal digits,
1580                         //  isn't modelled as part of the number element, so it's written as text
1581                         //  (the distinction between a quoted and non-quoted, locale-dependent
1582                         //  character is lost here).
1583 
1584                         AddToTextElement_Impl( *pElemStr );
1585                     }
1586                     break;
1587                 case NF_SYMBOLTYPE_DEL:
1588                     if ( pElemStr && *pElemStr == "@" )
1589                     {
1590                         WriteTextContentElement_Impl();
1591                         bAnyContent = true;
1592                     }
1593                     break;
1594 
1595                 case NF_SYMBOLTYPE_CALENDAR:
1596                     if ( pElemStr )
1597                     {
1598                         aCalendar = *pElemStr;
1599                         bExplicitCalendar = true;
1600                     }
1601                     break;
1602 
1603                 // date elements:
1604 
1605                 case NF_KEY_D:
1606                 case NF_KEY_DD:
1607                     {
1608                         bool bLong = ( nElemType == NF_KEY_DD );
1609                         WriteDayElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1610                         bAnyContent = true;
1611                     }
1612                     break;
1613                 case NF_KEY_DDD:
1614                 case NF_KEY_DDDD:
1615                 case NF_KEY_NN:
1616                 case NF_KEY_NNN:
1617                 case NF_KEY_NNNN:
1618                 case NF_KEY_AAA:
1619                 case NF_KEY_AAAA:
1620                     {
1621                         OUString aCalAttr = aCalendar;
1622                         if ( nElemType == NF_KEY_AAA || nElemType == NF_KEY_AAAA )
1623                         {
1624                             //  calendar attribute for AAA and AAAA is switched only for this element
1625                             if (aCalAttr.isEmpty())
1626                                 aCalAttr = lcl_GetDefaultCalendar( pFormatter, nLang );
1627                         }
1628 
1629                         bool bLong = ( nElemType == NF_KEY_NNN || nElemType == NF_KEY_NNNN ||
1630                                            nElemType == NF_KEY_DDDD || nElemType == NF_KEY_AAAA );
1631                         WriteDayOfWeekElement_Impl( aCalAttr, ( bSystemDate ? bLongSysDate : bLong ) );
1632                         bAnyContent = true;
1633                         if ( nElemType == NF_KEY_NNNN )
1634                         {
1635                             //  write additional text element for separator
1636                             pLocaleData.reset( new LocaleDataWrapper( pFormatter->GetComponentContext(),
1637                                 LanguageTag( nLang ) ) );
1638                             AddToTextElement_Impl( pLocaleData->getLongDateDayOfWeekSep() );
1639                         }
1640                     }
1641                     break;
1642                 case NF_KEY_M:
1643                 case NF_KEY_MM:
1644                 case NF_KEY_MMM:
1645                 case NF_KEY_MMMM:
1646                 case NF_KEY_MMMMM:      //! first letter of month name, no attribute available
1647                     {
1648                         bool bLong = ( nElemType == NF_KEY_MM  || nElemType == NF_KEY_MMMM );
1649                         bool bText = ( nElemType == NF_KEY_MMM || nElemType == NF_KEY_MMMM ||
1650                                             nElemType == NF_KEY_MMMMM );
1651                         WriteMonthElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ), bText );
1652                         bAnyContent = true;
1653                     }
1654                     break;
1655                 case NF_KEY_YY:
1656                 case NF_KEY_YYYY:
1657                 case NF_KEY_EC:
1658                 case NF_KEY_EEC:
1659                 case NF_KEY_R:      //! R acts as EE, no attribute available
1660                     {
1661                         //! distinguish EE and R
1662                         // Calendar attribute for E and EE and R is set in
1663                         // first loop. If set and not an explicit calendar and
1664                         // YY or YYYY is encountered, switch temporarily to
1665                         // Gregorian.
1666                         bool bLong = ( nElemType == NF_KEY_YYYY || nElemType == NF_KEY_EEC ||
1667                                             nElemType == NF_KEY_R );
1668                         WriteYearElement_Impl(
1669                                 ((bImplicitOtherCalendar && !bExplicitCalendar
1670                                   && (nElemType == NF_KEY_YY || nElemType == NF_KEY_YYYY)) ? "gregorian" : aCalendar),
1671                                 (bSystemDate ? bLongSysDate : bLong));
1672                         bAnyContent = true;
1673                     }
1674                     break;
1675                 case NF_KEY_G:
1676                 case NF_KEY_GG:
1677                 case NF_KEY_GGG:
1678                 case NF_KEY_RR:     //! RR acts as GGGEE, no attribute available
1679                     {
1680                         //! distinguish GG and GGG and RR
1681                         bool bLong = ( nElemType == NF_KEY_GGG || nElemType == NF_KEY_RR );
1682                         WriteEraElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1683                         bAnyContent = true;
1684                         if ( nElemType == NF_KEY_RR )
1685                         {
1686                             //  calendar attribute for RR is set in first loop
1687                             WriteYearElement_Impl( aCalendar, ( bSystemDate || bLongSysDate ) );
1688                         }
1689                     }
1690                     break;
1691                 case NF_KEY_Q:
1692                 case NF_KEY_QQ:
1693                     {
1694                         bool bLong = ( nElemType == NF_KEY_QQ );
1695                         WriteQuarterElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1696                         bAnyContent = true;
1697                     }
1698                     break;
1699                 case NF_KEY_WW:
1700                     WriteWeekElement_Impl( aCalendar );
1701                     bAnyContent = true;
1702                     break;
1703 
1704                 // time elements (bSystemDate is not used):
1705 
1706                 case NF_KEY_H:
1707                 case NF_KEY_HH:
1708                     WriteHoursElement_Impl( nElemType == NF_KEY_HH );
1709                     bAnyContent = true;
1710                     break;
1711                 case NF_KEY_MI:
1712                 case NF_KEY_MMI:
1713                     WriteMinutesElement_Impl( nElemType == NF_KEY_MMI );
1714                     bAnyContent = true;
1715                     break;
1716                 case NF_KEY_S:
1717                 case NF_KEY_SS:
1718                     WriteSecondsElement_Impl( ( nElemType == NF_KEY_SS ), nPrecision );
1719                     bAnyContent = true;
1720                     break;
1721                 case NF_KEY_AMPM:
1722                 case NF_KEY_AP:
1723                     WriteAMPMElement_Impl();        // short/long?
1724                     bAnyContent = true;
1725                     break;
1726                 case NF_SYMBOLTYPE_STAR :
1727                     // export only if ODF 1.2 extensions are enabled
1728                     if (rExport.getSaneDefaultVersion() > SvtSaveOptions::ODFSVER_012)
1729                     {
1730                         if ( pElemStr && pElemStr->getLength() > 1 )
1731                             WriteRepeatedElement_Impl( (*pElemStr)[1] );
1732                     }
1733                     break;
1734             }
1735             nPrevType = nElemType;
1736             ++nPos;
1737         }
1738     }
1739 
1740     if ( !sTextContent.isEmpty() )
1741         bAnyContent = true;     // element written in FinishTextElement_Impl
1742 
1743     FinishTextElement_Impl();       // final text element - before maps
1744 
1745     if ( !bAnyContent )
1746     {
1747         //  for an empty format, write an empty text element
1748         SvXMLElementExport aTElem( rExport, XML_NAMESPACE_NUMBER, XML_TEXT,
1749                                    true, false );
1750     }
1751 
1752     //  mapping (conditions) must be last elements
1753 
1754     if (!bDefPart)
1755         return;
1756 
1757     SvNumberformatLimitOps eOp1, eOp2;
1758     double fLimit1, fLimit2;
1759     rFormat.GetConditions( eOp1, fLimit1, eOp2, fLimit2 );
1760 
1761     WriteMapElement_Impl( eOp1, fLimit1, nKey, 0 );
1762     WriteMapElement_Impl( eOp2, fLimit2, nKey, 1 );
1763 
1764     if ( !rFormat.HasTextFormat() )
1765         return;
1766 
1767     //  4th part is for text -> make an "all other numbers" condition for the 3rd part
1768     //  by reversing the 2nd condition.
1769     //  For a trailing text format like  0;@  that has no conditions
1770     //  use a "less or equal than biggest" condition for the number
1771     //  part, ODF can't store subformats (style maps) without
1772     //  conditions.
1773 
1774     SvNumberformatLimitOps eOp3 = NUMBERFORMAT_OP_NO;
1775     double fLimit3 = fLimit2;
1776     sal_uInt16 nLastPart = 2;
1777     SvNumberformatLimitOps eOpLast = eOp2;
1778     if (eOp2 == NUMBERFORMAT_OP_NO)
1779     {
1780         eOpLast = eOp1;
1781         fLimit3 = fLimit1;
1782         nLastPart = (eOp1 == NUMBERFORMAT_OP_NO) ? 0 : 1;
1783     }
1784     switch ( eOpLast )
1785     {
1786         case NUMBERFORMAT_OP_EQ: eOp3 = NUMBERFORMAT_OP_NE; break;
1787         case NUMBERFORMAT_OP_NE: eOp3 = NUMBERFORMAT_OP_EQ; break;
1788         case NUMBERFORMAT_OP_LT: eOp3 = NUMBERFORMAT_OP_GE; break;
1789         case NUMBERFORMAT_OP_LE: eOp3 = NUMBERFORMAT_OP_GT; break;
1790         case NUMBERFORMAT_OP_GT: eOp3 = NUMBERFORMAT_OP_LE; break;
1791         case NUMBERFORMAT_OP_GE: eOp3 = NUMBERFORMAT_OP_LT; break;
1792         case NUMBERFORMAT_OP_NO: eOp3 = NUMBERFORMAT_OP_LE; fLimit3 = DBL_MAX; break;
1793     }
1794 
1795     if ( fLimit1 == fLimit2 &&
1796             ( ( eOp1 == NUMBERFORMAT_OP_LT && eOp2 == NUMBERFORMAT_OP_GT ) ||
1797               ( eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_LT ) ) )
1798     {
1799         //  For <x and >x, add =x as last condition
1800         //  (just for readability, <=x would be valid, too)
1801 
1802         eOp3 = NUMBERFORMAT_OP_EQ;
1803     }
1804 
1805     WriteMapElement_Impl( eOp3, fLimit3, nKey, nLastPart );
1806 }
1807 
1808 //  export one format
1809 
ExportFormat_Impl(const SvNumberformat & rFormat,sal_uInt32 nKey,sal_uInt32 nRealKey)1810 void SvXMLNumFmtExport::ExportFormat_Impl( const SvNumberformat& rFormat, sal_uInt32 nKey, sal_uInt32 nRealKey )
1811 {
1812     const sal_uInt16 XMLNUM_MAX_PARTS = 4;
1813     bool bParts[XMLNUM_MAX_PARTS] = { false, false, false, false };
1814     sal_uInt16 nUsedParts = 0;
1815     for (sal_uInt16 nPart=0; nPart<XMLNUM_MAX_PARTS; ++nPart)
1816     {
1817         if (rFormat.GetNumForInfoScannedType( nPart) != SvNumFormatType::UNDEFINED)
1818         {
1819             bParts[nPart] = true;
1820             nUsedParts = nPart + 1;
1821         }
1822     }
1823 
1824     SvNumberformatLimitOps eOp1, eOp2;
1825     double fLimit1, fLimit2;
1826     rFormat.GetConditions( eOp1, fLimit1, eOp2, fLimit2 );
1827 
1828     //  if conditions are set, even empty formats must be written
1829 
1830     if ( eOp1 != NUMBERFORMAT_OP_NO )
1831     {
1832         bParts[1] = true;
1833         if (nUsedParts < 2)
1834             nUsedParts = 2;
1835     }
1836     if ( eOp2 != NUMBERFORMAT_OP_NO )
1837     {
1838         bParts[2] = true;
1839         if (nUsedParts < 3)
1840             nUsedParts = 3;
1841     }
1842     if ( rFormat.HasTextFormat() )
1843     {
1844         bParts[3] = true;
1845         if (nUsedParts < 4)
1846             nUsedParts = 4;
1847     }
1848 
1849     for (sal_uInt16 nPart=0; nPart<XMLNUM_MAX_PARTS; ++nPart)
1850     {
1851         if (bParts[nPart])
1852         {
1853             bool bDefault = ( nPart+1 == nUsedParts );          // last = default
1854             ExportPart_Impl( rFormat, nKey, nRealKey, nPart, bDefault );
1855         }
1856     }
1857 }
1858 
1859 //  export method called by application
1860 
Export(bool bIsAutoStyle)1861 void SvXMLNumFmtExport::Export( bool bIsAutoStyle )
1862 {
1863     if ( !pFormatter )
1864         return;                         // no formatter -> no entries
1865 
1866     sal_uInt32 nKey;
1867     const SvNumberformat* pFormat = nullptr;
1868     bool bNext(pUsedList->GetFirstUsed(nKey));
1869     while(bNext)
1870     {
1871         // ODF has its notation of system formats, so obtain the "real" already
1872         // substituted format but use the original key for style name.
1873         sal_uInt32 nRealKey = nKey;
1874         pFormat = pFormatter->GetSubstitutedEntry( nKey, nRealKey);
1875         if(pFormat)
1876             ExportFormat_Impl( *pFormat, nKey, nRealKey );
1877         bNext = pUsedList->GetNextUsed(nKey);
1878     }
1879     if (!bIsAutoStyle)
1880     {
1881         std::vector<LanguageType> aLanguages;
1882         pFormatter->GetUsedLanguages( aLanguages );
1883         for (const auto& nLang : aLanguages)
1884         {
1885             sal_uInt32 nDefaultIndex = 0;
1886             SvNumberFormatTable& rTable = pFormatter->GetEntryTable(
1887                                          SvNumFormatType::DEFINED, nDefaultIndex, nLang );
1888             for (const auto& rTableEntry : rTable)
1889             {
1890                 nKey = rTableEntry.first;
1891                 pFormat = rTableEntry.second;
1892                 if (!pUsedList->IsUsed(nKey))
1893                 {
1894                     DBG_ASSERT((pFormat->GetType() & SvNumFormatType::DEFINED), "a not user defined numberformat found");
1895                     sal_uInt32 nRealKey = nKey;
1896                     if (pFormat->IsSubstituted())
1897                     {
1898                         pFormat = pFormatter->GetSubstitutedEntry( nKey, nRealKey); // export the "real" format
1899                         assert(pFormat);
1900                     }
1901                     //  user-defined and used formats are exported
1902                     ExportFormat_Impl( *pFormat, nKey, nRealKey );
1903                     // if it is a user-defined Format it will be added else nothing will happen
1904                     pUsedList->SetUsed(nKey);
1905                 }
1906             }
1907         }
1908     }
1909     pUsedList->Export();
1910 }
1911 
GetStyleName(sal_uInt32 nKey)1912 OUString SvXMLNumFmtExport::GetStyleName( sal_uInt32 nKey )
1913 {
1914     if(pUsedList->IsUsed(nKey) || pUsedList->IsWasUsed(nKey))
1915         return lcl_CreateStyleName( nKey, 0, true, sPrefix );
1916     else
1917     {
1918         OSL_FAIL("There is no written Data-Style");
1919         return OUString();
1920     }
1921 }
1922 
SetUsed(sal_uInt32 nKey)1923 void SvXMLNumFmtExport::SetUsed( sal_uInt32 nKey )
1924 {
1925     SAL_WARN_IF( pFormatter == nullptr, "xmloff.style", "missing formatter" );
1926     if( !pFormatter )
1927         return;
1928 
1929     if (pFormatter->GetEntry(nKey))
1930         pUsedList->SetUsed( nKey );
1931     else {
1932         OSL_FAIL("no existing Numberformat found with this key");
1933     }
1934 }
1935 
GetWasUsed() const1936 uno::Sequence<sal_Int32> SvXMLNumFmtExport::GetWasUsed() const
1937 {
1938     if (pUsedList)
1939         return pUsedList->GetWasUsed();
1940     return uno::Sequence<sal_Int32>();
1941 }
1942 
SetWasUsed(const uno::Sequence<sal_Int32> & rWasUsed)1943 void SvXMLNumFmtExport::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed)
1944 {
1945     if (pUsedList)
1946         pUsedList->SetWasUsed(rWasUsed);
1947 }
1948 
lcl_GetFormat(SvNumberFormatter const * pFormatter,sal_uInt32 nKey)1949 static const SvNumberformat* lcl_GetFormat( SvNumberFormatter const * pFormatter,
1950                            sal_uInt32 nKey )
1951 {
1952     return ( pFormatter != nullptr ) ? pFormatter->GetEntry( nKey ) : nullptr;
1953 }
1954 
ForceSystemLanguage(sal_uInt32 nKey)1955 sal_uInt32 SvXMLNumFmtExport::ForceSystemLanguage( sal_uInt32 nKey )
1956 {
1957     sal_uInt32 nRet = nKey;
1958 
1959     const SvNumberformat* pFormat = lcl_GetFormat( pFormatter, nKey );
1960     if( pFormat != nullptr )
1961     {
1962         SAL_WARN_IF( pFormatter == nullptr, "xmloff.style", "format without formatter?" );
1963 
1964         SvNumFormatType nType = pFormat->GetType();
1965 
1966         sal_uInt32 nNewKey = pFormatter->GetFormatForLanguageIfBuiltIn(
1967                        nKey, LANGUAGE_SYSTEM );
1968 
1969         if( nNewKey != nKey )
1970         {
1971             nRet = nNewKey;
1972         }
1973         else
1974         {
1975             OUString aFormatString( pFormat->GetFormatstring() );
1976             sal_Int32 nErrorPos;
1977             pFormatter->PutandConvertEntry(
1978                             aFormatString,
1979                             nErrorPos, nType, nNewKey,
1980                             pFormat->GetLanguage(), LANGUAGE_SYSTEM, true);
1981 
1982             // success? Then use new key.
1983             if( nErrorPos == 0 )
1984                 nRet = nNewKey;
1985         }
1986     }
1987 
1988     return nRet;
1989 }
1990 
1991 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1992