xref: /core/svl/source/numbers/zformat.cxx (revision 2cb2e2b9)
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 <string_view>
21 
22 #include <o3tl/sprintf.hxx>
23 #include <o3tl/string_view.hxx>
24 #include <comphelper/string.hxx>
25 #include <sal/log.hxx>
26 #include <tools/debug.hxx>
27 #include <tools/long.hxx>
28 #include <i18nlangtag/mslangid.hxx>
29 #include <rtl/math.hxx>
30 #include <unotools/charclass.hxx>
31 #include <unotools/calendarwrapper.hxx>
32 #include <unotools/nativenumberwrapper.hxx>
33 #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
34 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
35 #include <com/sun/star/i18n/CalendarDisplayCode.hpp>
36 #include <com/sun/star/i18n/AmPmValue.hpp>
37 #include <com/sun/star/i18n/NativeNumberMode.hpp>
38 #include <com/sun/star/i18n/NativeNumberXmlAttributes2.hpp>
39 
40 #include <svl/zformat.hxx>
41 #include "zforscan.hxx"
42 
43 #include "zforfind.hxx"
44 #include <svl/zforlist.hxx>
45 #include <unotools/digitgroupingiterator.hxx>
46 #include <svl/nfsymbol.hxx>
47 
48 #include <cmath>
49 #include <array>
50 
51 using namespace svt;
52 
53 namespace {
54 
55 constexpr OUStringLiteral GREGORIAN = u"gregorian";
56 
57 const sal_uInt16 UPPER_PRECISION = 300; // entirely arbitrary...
58 const double EXP_LOWER_BOUND = 1.0E-4; // prefer scientific notation below this value.
59 const double EXP_ABS_UPPER_BOUND = 1.0E15;  // use exponential notation above that absolute value.
60                                             // Back in time was E16 that lead
61                                             // to display rounding errors, see
62                                             // also sal/rtl/math.cxx
63                                             // doubleToString()
64 
65 constexpr sal_Int32 kTimeSignificantRound = 7;  // Round (date+)time at 7 decimals
66                                                 // (+5 of 86400 == 12 significant digits).
67 } // namespace
68 
69 const double D_MAX_U_INT32 = double(0xffffffff);      // 4294967295.0
70 constexpr double D_MAX_INTEGER = (sal_uInt64(1) << 53) - 1;
71 
72 const double D_MAX_D_BY_100  = 1.7E306;
73 const double D_MIN_M_BY_1000 = 2.3E-305;
74 
75 const sal_uInt8 cCharWidths[ 128-32 ] = {
76     1,1,1,2,2,3,2,1,1,1,1,2,1,1,1,1,
77     2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,
78     3,2,2,2,2,2,2,3,2,1,2,2,2,3,3,3,
79     2,3,2,2,2,2,2,3,2,2,2,1,1,1,2,2,
80     1,2,2,2,2,2,1,2,2,1,1,2,1,3,2,2,
81     2,2,1,2,1,2,2,2,2,2,2,1,1,1,2,1
82 };
83 
84 // static
85 sal_Int32 SvNumberformat::InsertBlanks( OUStringBuffer& r, sal_Int32 nPos, sal_Unicode c )
86 {
87     if( c >= 32 )
88     {
89         int n = 2;   // Default for chars > 128 (HACK!)
90         if( c <= 127 )
91         {
92             n = static_cast<int>(cCharWidths[ c - 32 ]);
93         }
94         while( n-- )
95         {
96             r.insert( nPos++, ' ');
97         }
98     }
99     return nPos;
100 }
101 
102 static tools::Long GetPrecExp( double fAbsVal )
103 {
104     DBG_ASSERT( fAbsVal > 0.0, "GetPrecExp: fAbsVal <= 0.0" );
105     if ( fAbsVal < 1e-7 || fAbsVal > 1e7 )
106     {
107         // Shear: whether it's faster or not, falls in between 1e6 and 1e7
108         return static_cast<tools::Long>(floor( log10( fAbsVal ) )) + 1;
109     }
110     else
111     {
112         tools::Long nPrecExp = 1;
113         while( fAbsVal < 1 )
114         {
115             fAbsVal *= 10;
116             nPrecExp--;
117         }
118         while( fAbsVal >= 10 )
119         {
120             fAbsVal /= 10;
121             nPrecExp++;
122         }
123         return nPrecExp;
124     }
125 }
126 
127 /**
128  * SvNumberformatInfo
129  * */
130 
131 void ImpSvNumberformatInfo::Copy( const ImpSvNumberformatInfo& rNumFor, sal_uInt16 nCnt )
132 {
133     for (sal_uInt16 i = 0; i < nCnt; ++i)
134     {
135         sStrArray[i]  = rNumFor.sStrArray[i];
136         nTypeArray[i] = rNumFor.nTypeArray[i];
137     }
138     eScannedType = rNumFor.eScannedType;
139     bThousand    = rNumFor.bThousand;
140     nThousand    = rNumFor.nThousand;
141     nCntPre      = rNumFor.nCntPre;
142     nCntPost     = rNumFor.nCntPost;
143     nCntExp      = rNumFor.nCntExp;
144 }
145 
146 const std::map<LanguageType, std::array<sal_uInt8, 4>> tblDBNumToNatNum
147     = { { primary(LANGUAGE_CHINESE),    { 4, 5, 3, 0 } },
148         { primary(LANGUAGE_JAPANESE),   { 4, 5, 3, 0 } },
149         { primary(LANGUAGE_KOREAN),     { 4, 5, 6, 10 } } };
150 
151 // static
152 sal_uInt8 SvNumberNatNum::MapDBNumToNatNum( sal_uInt8 nDBNum, LanguageType eLang, bool bDate )
153 {
154     sal_uInt8 nNatNum = 0;
155     eLang = MsLangId::getRealLanguage( eLang );  // resolve SYSTEM etc.
156     eLang = primary(eLang);    // 10 bit primary language
157     if ( bDate )
158     {
159         if ( nDBNum == 4 && eLang == primary(LANGUAGE_KOREAN) )
160         {
161             nNatNum = 10;
162         }
163         else if ( nDBNum <= 3 )
164         {
165             nNatNum = nDBNum;   // known to be good for: zh,ja,ko / 1,2,3
166         }
167     }
168     else
169     {
170         if (1 <= nDBNum && nDBNum <= 4)
171         {
172             auto const it = tblDBNumToNatNum.find(eLang);
173             if (it != tblDBNumToNatNum.end())
174                 nNatNum = it->second[nDBNum - 1];
175 
176         }
177     }
178     return nNatNum;
179 }
180 
181 const std::map<LanguageType, std::array<sal_uInt8, 9>> tblNatNumToDBNum
182     = { { primary(LANGUAGE_CHINESE),    { 1, 0, 0, 1, 2, 3, 0, 0, 0 } },
183         { primary(LANGUAGE_JAPANESE),   { 1, 2, 3, 1, 2, 3, 1, 2, 0 } },
184         { primary(LANGUAGE_KOREAN),     { 1, 2, 3, 1, 2, 3, 1, 2, 4 } } };
185 
186 // static
187 sal_uInt8 SvNumberNatNum::MapNatNumToDBNum( sal_uInt8 nNatNum, LanguageType eLang, bool bDate )
188 {
189     sal_uInt8 nDBNum = 0;
190     eLang = MsLangId::getRealLanguage( eLang );  // resolve SYSTEM etc.
191     eLang = primary(eLang);    // 10 bit primary language
192     if ( bDate )
193     {
194         if ( nNatNum == 10 && eLang == primary(LANGUAGE_KOREAN) )
195         {
196             nDBNum = 4;
197         }
198         else if ( nNatNum <= 3 )
199         {
200             nDBNum = nNatNum;   // known to be good for: zh,ja,ko / 1,2,3
201         }
202     }
203     else
204     {
205         if (1 <= nNatNum && nNatNum <= 9)
206         {
207             auto const it = tblNatNumToDBNum.find(eLang);
208             if (it != tblNatNumToDBNum.end())
209                 nDBNum = it->second[nNatNum - 1];
210         }
211     }
212     return nDBNum;
213 }
214 
215 /**
216  * SvNumFor
217  */
218 
219 ImpSvNumFor::ImpSvNumFor()
220 {
221     nStringsCnt = 0;
222     aI.eScannedType = SvNumFormatType::UNDEFINED;
223     aI.bThousand = false;
224     aI.nThousand = 0;
225     aI.nCntPre = 0;
226     aI.nCntPost = 0;
227     aI.nCntExp = 0;
228     pColor = nullptr;
229 }
230 
231 ImpSvNumFor::~ImpSvNumFor()
232 {
233 }
234 
235 void ImpSvNumFor::Enlarge(sal_uInt16 nCnt)
236 {
237     if ( nStringsCnt != nCnt )
238     {
239         nStringsCnt = nCnt;
240         aI.nTypeArray.resize(nCnt);
241         aI.sStrArray.resize(nCnt);
242     }
243 }
244 
245 void ImpSvNumFor::Copy( const ImpSvNumFor& rNumFor, const ImpSvNumberformatScan* pSc )
246 {
247     Enlarge( rNumFor.nStringsCnt );
248     aI.Copy( rNumFor.aI, nStringsCnt );
249     sColorName = rNumFor.sColorName;
250     if ( pSc )
251     {
252         pColor = pSc->GetColor( sColorName );   // #121103# don't copy pointer between documents
253     }
254     else
255     {
256         pColor = rNumFor.pColor;
257     }
258     aNatNum = rNumFor.aNatNum;
259 }
260 
261 bool ImpSvNumFor::HasNewCurrency() const
262 {
263     for ( sal_uInt16 j=0; j<nStringsCnt; j++ )
264     {
265         if ( aI.nTypeArray[j] == NF_SYMBOLTYPE_CURRENCY )
266         {
267             return true;
268         }
269     }
270     return false;
271 }
272 
273 bool ImpSvNumFor::GetNewCurrencySymbol( OUString& rSymbol,
274                                         OUString& rExtension ) const
275 {
276     for ( sal_uInt16 j=0; j<nStringsCnt; j++ )
277     {
278         if ( aI.nTypeArray[j] == NF_SYMBOLTYPE_CURRENCY )
279         {
280             rSymbol = aI.sStrArray[j];
281             if ( j < nStringsCnt-1 && aI.nTypeArray[j+1] == NF_SYMBOLTYPE_CURREXT )
282             {
283                 rExtension = aI.sStrArray[j+1];
284             }
285             else
286             {
287                 rExtension.clear();
288             }
289             return true;
290         }
291     }
292     //! No Erase at rSymbol, rExtension
293     return false;
294 }
295 
296 /**
297  * SvNumberformat
298  */
299 
300 namespace {
301 
302 enum BracketFormatSymbolType
303 {
304     BRACKET_SYMBOLTYPE_FORMAT   = -1,   // subformat string
305     BRACKET_SYMBOLTYPE_COLOR    = -2,   // color
306     BRACKET_SYMBOLTYPE_ERROR    = -3,   // error
307     BRACKET_SYMBOLTYPE_DBNUM1   = -4,   // DoubleByteNumber, represent numbers
308     BRACKET_SYMBOLTYPE_DBNUM2   = -5,   // using CJK characters, Excel compatible
309     BRACKET_SYMBOLTYPE_DBNUM3   = -6,
310     BRACKET_SYMBOLTYPE_DBNUM4   = -7,
311     BRACKET_SYMBOLTYPE_DBNUM5   = -8,
312     BRACKET_SYMBOLTYPE_DBNUM6   = -9,
313     BRACKET_SYMBOLTYPE_DBNUM7   = -10,
314     BRACKET_SYMBOLTYPE_DBNUM8   = -11,
315     BRACKET_SYMBOLTYPE_DBNUM9   = -12,
316     BRACKET_SYMBOLTYPE_LOCALE   = -13,
317     BRACKET_SYMBOLTYPE_NATNUM0  = -14,  // Our NativeNumber support, ASCII
318     BRACKET_SYMBOLTYPE_NATNUM1  = -15,  // Our NativeNumber support, represent
319     BRACKET_SYMBOLTYPE_NATNUM2  = -16,  // numbers using CJK, CTL, ...
320     BRACKET_SYMBOLTYPE_NATNUM3  = -17,
321     BRACKET_SYMBOLTYPE_NATNUM4  = -18,
322     BRACKET_SYMBOLTYPE_NATNUM5  = -19,
323     BRACKET_SYMBOLTYPE_NATNUM6  = -20,
324     BRACKET_SYMBOLTYPE_NATNUM7  = -21,
325     BRACKET_SYMBOLTYPE_NATNUM8  = -22,
326     BRACKET_SYMBOLTYPE_NATNUM9  = -23,
327     BRACKET_SYMBOLTYPE_NATNUM10 = -24,
328     BRACKET_SYMBOLTYPE_NATNUM11 = -25,
329     BRACKET_SYMBOLTYPE_NATNUM12 = -26,
330     BRACKET_SYMBOLTYPE_NATNUM13 = -27,
331     BRACKET_SYMBOLTYPE_NATNUM14 = -28,
332     BRACKET_SYMBOLTYPE_NATNUM15 = -29,
333     BRACKET_SYMBOLTYPE_NATNUM16 = -30,
334     BRACKET_SYMBOLTYPE_NATNUM17 = -31,
335     BRACKET_SYMBOLTYPE_NATNUM18 = -32,
336     BRACKET_SYMBOLTYPE_NATNUM19 = -33
337 };
338 
339 }
340 
341 void SvNumberformat::ImpCopyNumberformat( const SvNumberformat& rFormat )
342 {
343     sFormatstring = rFormat.sFormatstring;
344     eType         = rFormat.eType;
345     maLocale      = rFormat.maLocale;
346     fLimit1       = rFormat.fLimit1;
347     fLimit2       = rFormat.fLimit2;
348     eOp1          = rFormat.eOp1;
349     eOp2          = rFormat.eOp2;
350     bStandard     = rFormat.bStandard;
351     bIsUsed       = rFormat.bIsUsed;
352     sComment      = rFormat.sComment;
353     bAdditionalBuiltin = rFormat.bAdditionalBuiltin;
354 
355     // #121103# when copying between documents, get color pointers from own scanner
356     ImpSvNumberformatScan* pColorSc = ( &rScan != &rFormat.rScan ) ? &rScan : nullptr;
357 
358     for (sal_uInt16 i = 0; i < 4; i++)
359     {
360         NumFor[i].Copy(rFormat.NumFor[i], pColorSc);
361     }
362 }
363 
364 SvNumberformat::SvNumberformat( SvNumberformat const & rFormat )
365     : rScan(rFormat.rScan), bStarFlag( rFormat.bStarFlag )
366 {
367     ImpCopyNumberformat( rFormat );
368 }
369 
370 SvNumberformat::SvNumberformat( SvNumberformat const & rFormat, ImpSvNumberformatScan& rSc )
371     : rScan(rSc)
372     , bStarFlag( rFormat.bStarFlag )
373 {
374     ImpCopyNumberformat( rFormat );
375 }
376 
377 static bool lcl_SvNumberformat_IsBracketedPrefix( short nSymbolType )
378 {
379     if ( nSymbolType > 0  )
380     {
381         return true; // conditions
382     }
383     switch ( nSymbolType )
384     {
385     case BRACKET_SYMBOLTYPE_COLOR :
386     case BRACKET_SYMBOLTYPE_DBNUM1 :
387     case BRACKET_SYMBOLTYPE_DBNUM2 :
388     case BRACKET_SYMBOLTYPE_DBNUM3 :
389     case BRACKET_SYMBOLTYPE_DBNUM4 :
390     case BRACKET_SYMBOLTYPE_DBNUM5 :
391     case BRACKET_SYMBOLTYPE_DBNUM6 :
392     case BRACKET_SYMBOLTYPE_DBNUM7 :
393     case BRACKET_SYMBOLTYPE_DBNUM8 :
394     case BRACKET_SYMBOLTYPE_DBNUM9 :
395     case BRACKET_SYMBOLTYPE_LOCALE :
396     case BRACKET_SYMBOLTYPE_NATNUM0 :
397     case BRACKET_SYMBOLTYPE_NATNUM1 :
398     case BRACKET_SYMBOLTYPE_NATNUM2 :
399     case BRACKET_SYMBOLTYPE_NATNUM3 :
400     case BRACKET_SYMBOLTYPE_NATNUM4 :
401     case BRACKET_SYMBOLTYPE_NATNUM5 :
402     case BRACKET_SYMBOLTYPE_NATNUM6 :
403     case BRACKET_SYMBOLTYPE_NATNUM7 :
404     case BRACKET_SYMBOLTYPE_NATNUM8 :
405     case BRACKET_SYMBOLTYPE_NATNUM9 :
406     case BRACKET_SYMBOLTYPE_NATNUM10 :
407     case BRACKET_SYMBOLTYPE_NATNUM11 :
408     case BRACKET_SYMBOLTYPE_NATNUM12 :
409     case BRACKET_SYMBOLTYPE_NATNUM13 :
410     case BRACKET_SYMBOLTYPE_NATNUM14 :
411     case BRACKET_SYMBOLTYPE_NATNUM15 :
412     case BRACKET_SYMBOLTYPE_NATNUM16 :
413     case BRACKET_SYMBOLTYPE_NATNUM17 :
414     case BRACKET_SYMBOLTYPE_NATNUM18 :
415     case BRACKET_SYMBOLTYPE_NATNUM19 :
416         return true;
417     }
418     return false;
419 }
420 
421 /** Import extended LCID from Excel
422  */
423 OUString SvNumberformat::ImpObtainCalendarAndNumerals( OUStringBuffer& rString, sal_Int32 nPos,
424                                                        LanguageType& nLang, const LocaleType& aTmpLocale )
425 {
426     OUString sCalendar;
427     sal_uInt16 nNatNum = 0;
428     LanguageType nLocaleLang = MsLangId::getRealLanguage( maLocale.meLanguage );
429     LanguageType nTmpLocaleLang = MsLangId::getRealLanguage( aTmpLocale.meLanguage );
430     /* NOTE: enhancement to allow other possible locale dependent
431      * calendars and numerals. BUT only if our locale data allows it! For LCID
432      * numerals and calendars see
433      * http://office.microsoft.com/en-us/excel/HA010346351033.aspx
434      * Calendar is inserted after
435      * all prefixes have been consumed as it is actually a format modifier
436      * and not a prefix.
437      * Currently calendars are tied to the locale of the entire number
438      * format, e.g. [~buddhist] in en_US doesn't work.
439      * => Having different locales in sub formats does not work!
440      * */
441     /* TODO: calendars could be tied to a sub format's NatNum info
442      * instead, or even better be available for any locale. Needs a
443      * different implementation of GetCal() and locale data calendars.
444      * */
445     switch ( aTmpLocale.mnCalendarType & 0x7F )
446     {
447         case 0x03 : // Gengou calendar
448             // Only Japanese language support Gengou calendar.
449             // It is an implicit "other" calendar where E, EE, R and RR
450             // automatically switch to and YY and YYYY switch to Gregorian. Do
451             // not add the "[~gengou]" modifier.
452             if ( nLocaleLang != LANGUAGE_JAPANESE )
453             {
454                 nLang = maLocale.meLanguage = LANGUAGE_JAPANESE;
455             }
456             break;
457         case 0x05 : // Korean Dangi calendar
458             sCalendar = "[~dangi]";
459             // Only Korean language support dangi calendar
460             if ( nLocaleLang != LANGUAGE_KOREAN )
461             {
462                 nLang = maLocale.meLanguage = LANGUAGE_KOREAN;
463             }
464             break;
465         case 0x06 : // Hijri calendar
466         case 0x17 : // same?
467             sCalendar = "[~hijri]";
468             // Only Arabic or Farsi languages support Hijri calendar
469             if ( ( primary( nLocaleLang ) != LANGUAGE_ARABIC_PRIMARY_ONLY )
470                   && nLocaleLang != LANGUAGE_FARSI )
471             {
472                 if ( ( primary( nTmpLocaleLang ) == LANGUAGE_ARABIC_PRIMARY_ONLY )
473                       || nTmpLocaleLang == LANGUAGE_FARSI )
474                 {
475                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
476                 }
477                 else
478                 {
479                     nLang = maLocale.meLanguage = LANGUAGE_ARABIC_SAUDI_ARABIA;
480                 }
481             }
482             break;
483         case 0x07 : // Buddhist calendar
484             sCalendar="[~buddhist]";
485             // Only Thai or Lao languages support Buddhist calendar
486             if ( nLocaleLang != LANGUAGE_THAI && nLocaleLang != LANGUAGE_LAO )
487             {
488                 if ( nTmpLocaleLang == LANGUAGE_THAI || nTmpLocaleLang == LANGUAGE_LAO )
489                 {
490                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
491                 }
492                 else
493                 {
494                     nLang = maLocale.meLanguage = LANGUAGE_THAI;
495                 }
496             }
497             break;
498         case 0x08 : // Hebrew calendar
499             sCalendar = "[~jewish]";
500             // Many languages (but not all) support Jewish calendar
501             // Unable to find any logic => keep same language
502             break;
503         case 0x0E : // unknown calendar
504         case 0x0F : // unknown calendar
505         case 0x10 : // Indian calendar (unsupported)
506         case 0x11 : // unknown calendar
507         case 0x12 : // unknown calendar
508         case 0x13 : // unknown calendar
509         default : // other calendars (see tdf#36038) are not handle by LibO
510             break;
511     }
512     /** Reference language for each numeral ID */
513     static const LanguageType aNumeralIDtoLanguage []=
514     {
515         LANGUAGE_DONTKNOW,              // 0x00
516         LANGUAGE_ENGLISH_US,            // 0x01
517         LANGUAGE_ARABIC_SAUDI_ARABIA,   // 0x02 + all Arabic
518         LANGUAGE_FARSI,                 // 0x03
519         LANGUAGE_HINDI,                 // 0x04 + Devanagari
520         LANGUAGE_BENGALI,               // 0x05
521         LANGUAGE_PUNJABI,               // 0x06
522         LANGUAGE_GUJARATI,              // 0x07
523         LANGUAGE_ODIA,                  // 0x08
524         LANGUAGE_TAMIL,                 // 0x09
525         LANGUAGE_TELUGU,                // 0x0A
526         LANGUAGE_KANNADA,               // 0x0B
527         LANGUAGE_MALAYALAM,             // 0x0C
528         LANGUAGE_THAI,                  // 0x0D
529         LANGUAGE_LAO,                   // 0x0E
530         LANGUAGE_TIBETAN,               // 0x0F
531         LANGUAGE_BURMESE,               // 0x10
532         LANGUAGE_TIGRIGNA_ETHIOPIA,     // 0x11
533         LANGUAGE_KHMER,                 // 0x12
534         LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA, // 0x13
535         LANGUAGE_DONTKNOW,              // 0x14
536         LANGUAGE_DONTKNOW,              // 0x15
537         LANGUAGE_DONTKNOW,              // 0x16
538         LANGUAGE_DONTKNOW,              // 0x17
539         LANGUAGE_DONTKNOW,              // 0x18
540         LANGUAGE_DONTKNOW,              // 0x19
541         LANGUAGE_DONTKNOW,              // 0x1A
542         LANGUAGE_JAPANESE,              // 0x1B
543         LANGUAGE_JAPANESE,              // 0x1C
544         LANGUAGE_JAPANESE,              // 0x1D
545         LANGUAGE_CHINESE_SIMPLIFIED,    // 0x1E
546         LANGUAGE_CHINESE_SIMPLIFIED,    // 0x1F
547         LANGUAGE_CHINESE_SIMPLIFIED,    // 0x20
548         LANGUAGE_CHINESE_TRADITIONAL,   // 0x21
549         LANGUAGE_CHINESE_TRADITIONAL,   // 0x22
550         LANGUAGE_CHINESE_TRADITIONAL,   // 0x23
551         LANGUAGE_KOREAN,                // 0x24
552         LANGUAGE_KOREAN,                // 0x25
553         LANGUAGE_KOREAN,                // 0x26
554         LANGUAGE_KOREAN                 // 0x27
555     };
556 
557     sal_uInt16 nNumeralID = aTmpLocale.mnNumeralShape & 0x7F;
558     LanguageType nReferenceLanguage = nNumeralID <= 0x27 ? aNumeralIDtoLanguage[nNumeralID] : LANGUAGE_DONTKNOW;
559 
560     switch ( nNumeralID )
561     {
562         // Regular cases: all languages with same primary mask use same numerals
563         case 0x03 : // Perso-Arabic (Farsi) numerals
564         case 0x05 : // Bengali numerals
565         case 0x06 : // Punjabi numerals
566         case 0x07 : // Gujarati numerals
567         case 0x08 : // Odia (Orya) numerals
568         case 0x09 : // Tamil numerals
569         case 0x0A : // Telugu numerals
570         case 0x0B : // Kannada numerals
571         case 0x0C : // Malayalam numerals
572         case 0x0D : // Thai numerals
573         case 0x0E : // Lao numerals
574         case 0x0F : // Tibetan numerals
575         case 0x10 : // Burmese (Myanmar) numerals
576         case 0x11 : // Tigrigna (Ethiopia) numerals
577         case 0x12 : // Khmer numerals
578             if ( primary( nLocaleLang ) != primary( nReferenceLanguage ) )
579             {
580                 if ( primary( nTmpLocaleLang ) == primary( nReferenceLanguage ) )
581                 {
582                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
583                 }
584                 else
585                 {
586                     nLang = maLocale.meLanguage = nReferenceLanguage;
587                 }
588             }
589             break;
590         // Special cases
591         case 0x04 : // Devanagari (Hindi) numerals
592             // same numerals (Devanagari) for languages with different primary masks
593             if ( nLocaleLang != LANGUAGE_HINDI    && nLocaleLang != LANGUAGE_MARATHI
594             && primary( nLocaleLang ) != primary( LANGUAGE_NEPALI ) )
595             {
596                 if ( nTmpLocaleLang == LANGUAGE_HINDI || nTmpLocaleLang == LANGUAGE_MARATHI
597                 || primary( nTmpLocaleLang ) == primary( LANGUAGE_NEPALI ) )
598                 {
599                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
600                 }
601                 else
602                 {
603                     nLang = maLocale.meLanguage = LANGUAGE_HINDI;
604                 }
605             }
606             break;
607         case 0x13 : // Mongolian numerals
608             // not all Mongolian languages use Mongolian numerals
609             if ( nLocaleLang != LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
610               && nLocaleLang != LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA
611               && nLocaleLang != LANGUAGE_MONGOLIAN_MONGOLIAN_LSO )
612             {
613                 if ( nTmpLocaleLang == LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
614                   || nTmpLocaleLang == LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA
615                   || nTmpLocaleLang == LANGUAGE_MONGOLIAN_MONGOLIAN_LSO )
616                 {
617                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
618                 }
619                 else
620                 {
621                     nLang = maLocale.meLanguage = LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA;
622                 }
623             }
624             break;
625         case 0x02 : // Eastern-Arabic numerals
626             // all arabic primary mask + LANGUAGE_PUNJABI_ARABIC_LSO
627             if ( primary( nLocaleLang ) != LANGUAGE_ARABIC_PRIMARY_ONLY
628                 && nLocaleLang != LANGUAGE_PUNJABI_ARABIC_LSO )
629             {
630                 if ( primary( nTmpLocaleLang ) == LANGUAGE_ARABIC_PRIMARY_ONLY
631                     || nTmpLocaleLang != LANGUAGE_PUNJABI_ARABIC_LSO )
632                 {
633                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
634                 }
635                 else
636                 {
637                     nLang = maLocale.meLanguage = nReferenceLanguage;
638                 }
639             }
640             break;
641         // CJK numerals
642         case 0x1B : // simple Asian numerals, Japanese
643         case 0x1C : // financial Asian numerals, Japanese
644         case 0x1D : // Arabic fullwidth numerals, Japanese
645         case 0x24 : // simple Asian numerals, Korean
646         case 0x25 : // financial Asian numerals, Korean
647         case 0x26 : // Arabic fullwidth numerals, Korean
648         case 0x27 : // Korean Hangul numerals
649             // Japanese and Korean are regular
650             if ( primary( nLocaleLang ) != primary( nReferenceLanguage ) )
651             {
652                 if ( primary( nTmpLocaleLang ) == primary( nReferenceLanguage ) )
653                 {
654                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
655                 }
656                 else
657                 {
658                     nLang = maLocale.meLanguage = nReferenceLanguage;
659                 }
660             }
661             [[fallthrough]];
662         case 0x1E : // simple Asian numerals, Chinese-PRC
663         case 0x1F : // financial Asian numerals, Chinese-PRC
664         case 0x20 : // Arabic fullwidth numerals, Chinese-PRC
665         case 0x21 : // simple Asian numerals, Chinese-Taiwan
666         case 0x22 : // financial Asian numerals, Chinese-Taiwan
667         case 0x23 : // Arabic fullwidth numerals, Chinese-Taiwan
668             nNatNum = nNumeralID == 0x27 ? 9 : ( ( nNumeralID - 0x1B ) % 3 ) + 1;
669             // [NatNum1] simple numerals
670             // [natNum2] financial numerals
671             // [NatNum3] Arabic fullwidth numerals
672             // Chinese simplified and Chinese traditional have same primary mask
673             // Chinese-PRC
674             if ( nReferenceLanguage == LANGUAGE_CHINESE_SIMPLIFIED
675               && nLocaleLang != LANGUAGE_CHINESE_SIMPLIFIED
676               && nLocaleLang != LANGUAGE_CHINESE_SINGAPORE
677               && nLocaleLang != LANGUAGE_CHINESE_LSO )
678             {
679                 if ( nTmpLocaleLang == LANGUAGE_CHINESE_SIMPLIFIED
680                   || nTmpLocaleLang == LANGUAGE_CHINESE_SINGAPORE
681                   || nTmpLocaleLang == LANGUAGE_CHINESE_LSO )
682                 {
683                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
684                 }
685                 else
686                 {
687                     nLang = maLocale.meLanguage = LANGUAGE_CHINESE_SIMPLIFIED;
688                 }
689             }
690             // Chinese-Taiwan
691             else if ( nReferenceLanguage == LANGUAGE_CHINESE_TRADITIONAL
692                    && nLocaleLang != LANGUAGE_CHINESE_TRADITIONAL
693                    && nLocaleLang != LANGUAGE_CHINESE_HONGKONG
694                    && nLocaleLang != LANGUAGE_CHINESE_MACAU )
695             {
696                 if ( nTmpLocaleLang == LANGUAGE_CHINESE_TRADITIONAL
697                   || nTmpLocaleLang == LANGUAGE_CHINESE_HONGKONG
698                   || nTmpLocaleLang == LANGUAGE_CHINESE_MACAU )
699                 {
700                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
701                 }
702                 else
703                 {
704                     nLang = maLocale.meLanguage = LANGUAGE_CHINESE_TRADITIONAL;
705                 }
706             }
707             break;
708     }
709     if ( nNumeralID >= 0x02 && nNumeralID <= 0x13 )
710         nNatNum = 1;
711     if ( nNatNum )
712         rString.insert(nPos, "[NatNum" + OUString::number(nNatNum) + "]");
713     return sCalendar;
714 }
715 
716 namespace
717 {
718 bool NatNumTakesParameters(sal_Int16 nNum)
719 {
720     return (nNum == css::i18n::NativeNumberMode::NATNUM12);
721 }
722 }
723 
724 // is there a 3-letter bank code in NatNum12 param (but not
725 // followed by an equal mark, like in the date code "NNN=")?
726 static bool lcl_isNatNum12Currency( const OUString& sParam )
727 {
728     sal_Int32 nUpper = 0;
729     sal_Int32 nLen = sParam.getLength();
730     for (sal_Int32 n = 0; n < nLen; ++n)
731     {
732         sal_Unicode c = sParam[n];
733         if ( 'A' <= c && c <= 'Z' )
734         {
735             ++nUpper;
736         }
737         else if ( c == ' ' && nUpper == 3 && (n == 3 || sParam[n - 4] == ' ') )
738         {
739             return true;
740         }
741         else
742         {
743             nUpper = 0;
744         }
745     }
746 
747     return nUpper == 3 && (nLen == 3 || sParam[nLen - 4] == ' ');
748 }
749 
750 SvNumberformat::SvNumberformat(OUString& rString,
751                                ImpSvNumberformatScan* pSc,
752                                ImpSvNumberInputScan* pISc,
753                                sal_Int32& nCheckPos,
754                                LanguageType& eLan,
755                                bool bReplaceBooleanEquivalent)
756         : rScan(*pSc)
757         , bAdditionalBuiltin( false )
758         , bStarFlag( false )
759 {
760     if (bReplaceBooleanEquivalent)
761         rScan.ReplaceBooleanEquivalent( rString);
762 
763     OUStringBuffer sBuff(rString);
764 
765     // If the group (AKA thousand) separator is a No-Break Space (French)
766     // replace all occurrences by a simple space.
767     // The same for Narrow No-Break Space just in case some locale uses it.
768     // The tokens will be changed to the LocaleData separator again later on.
769     const OUString& rThSep = GetFormatter().GetNumThousandSep();
770     if ( rThSep.getLength() == 1)
771     {
772         const sal_Unicode cNBSp = 0xA0;
773         const sal_Unicode cNNBSp = 0x202F;
774         if (rThSep[0] == cNBSp )
775             sBuff.replace( cNBSp, ' ');
776         else if (rThSep[0] == cNNBSp )
777             sBuff.replace( cNNBSp, ' ');
778     }
779 
780     OUString aConvertFromDecSep;
781     OUString aConvertToDecSep;
782     if (rScan.GetConvertMode())
783     {
784         aConvertFromDecSep = GetFormatter().GetNumDecimalSep();
785         maLocale.meLanguage = rScan.GetNewLnge();
786         eLan = maLocale.meLanguage; // Make sure to return switch
787     }
788     else
789     {
790         maLocale.meLanguage = eLan;
791     }
792     bStandard = false;
793     bIsUsed = false;
794     fLimit1 = 0.0;
795     fLimit2 = 0.0;
796     eOp1 = NUMBERFORMAT_OP_NO;
797     eOp2 = NUMBERFORMAT_OP_NO;
798     eType = SvNumFormatType::DEFINED;
799 
800     bool bCancel = false;
801     bool bCondition = false;
802     short eSymbolType;
803     sal_Int32 nPos = 0;
804     sal_Int32 nPosOld;
805     nCheckPos = 0;
806 
807     // Split into 4 sub formats
808     sal_uInt16 nIndex;
809     for ( nIndex = 0; nIndex < 4 && !bCancel; nIndex++ )
810     {
811         // Original language/country may have to be reestablished
812         if (rScan.GetConvertMode())
813         {
814             rScan.GetNumberformatter()->ChangeIntl(rScan.GetTmpLnge());
815         }
816         OUString sInsertCalendar; // a calendar resulting from parsing LCID
817         OUString sStr;
818         nPosOld = nPos; // Start position of substring
819         // first get bracketed prefixes; e.g. conditions, color
820         do
821         {
822             eSymbolType = ImpNextSymbol(sBuff, nPos, sStr);
823             if (eSymbolType > 0) // condition
824             {
825                 if ( nIndex == 0 && !bCondition )
826                 {
827                     bCondition = true;
828                     eOp1 = static_cast<SvNumberformatLimitOps>(eSymbolType);
829                 }
830                 else if ( nIndex == 1 && bCondition )
831                 {
832                     eOp2 = static_cast<SvNumberformatLimitOps>(eSymbolType);
833                 }
834                 else                                // error
835                 {
836                     bCancel = true;                 // break for
837                     nCheckPos = nPosOld;
838                 }
839                 if (!bCancel)
840                 {
841                     double fNumber;
842                     sal_Int32 nCntChars = ImpGetNumber(sBuff, nPos, sStr);
843                     if (nCntChars > 0)
844                     {
845                         sal_Int32 nDecPos;
846                         SvNumFormatType F_Type = SvNumFormatType::UNDEFINED;
847                         if (!pISc->IsNumberFormat(sStr, F_Type, fNumber, nullptr, SvNumInputOptions::NONE) ||
848                             ( F_Type != SvNumFormatType::NUMBER &&
849                               F_Type != SvNumFormatType::SCIENTIFIC) )
850                         {
851                             fNumber = 0.0;
852                             nPos = nPos - nCntChars;
853                             sBuff.remove(nPos, nCntChars);
854                             sBuff.insert(nPos, '0');
855                             nPos++;
856                         }
857                         else if (rScan.GetConvertMode() && ((nDecPos = sStr.indexOf( aConvertFromDecSep)) >= 0))
858                         {
859                             if (aConvertToDecSep.isEmpty())
860                                 aConvertToDecSep = GetFormatter().GetLangDecimalSep( rScan.GetNewLnge());
861                             if (aConvertToDecSep != aConvertFromDecSep)
862                             {
863                                 const OUString aStr( sStr.replaceAt( nDecPos,
864                                             aConvertFromDecSep.getLength(), aConvertToDecSep));
865                                 nPos = nPos - nCntChars;
866                                 sBuff.remove(nPos, nCntChars);
867                                 sBuff.insert(nPos, aStr);
868                                 nPos += aStr.getLength();
869                             }
870                         }
871                     }
872                     else
873                     {
874                         fNumber = 0.0;
875                         sBuff.insert(nPos++, '0');
876                     }
877                     if (nIndex == 0)
878                     {
879                         fLimit1 = fNumber;
880                     }
881                     else
882                     {
883                         fLimit2 = fNumber;
884                     }
885                     if ( nPos < sBuff.getLength() && sBuff[nPos] == ']' )
886                     {
887                         nPos++;
888                     }
889                     else
890                     {
891                         bCancel = true;             // break for
892                         nCheckPos = nPos;
893                     }
894                 }
895                 nPosOld = nPos;                     // position before string
896             }
897             else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) )
898             {
899                 OUString sSymbol( sStr);
900                 switch ( eSymbolType )
901                 {
902                 case BRACKET_SYMBOLTYPE_COLOR :
903                     if ( NumFor[nIndex].GetColor() != nullptr )
904                     {                           // error, more than one color
905                         bCancel = true;         // break for
906                         nCheckPos = nPosOld;
907                     }
908                     else
909                     {
910                         const Color* pColor = pSc->GetColor( sStr);
911                         NumFor[nIndex].SetColor( pColor, sStr);
912                         if (pColor == nullptr)
913                         {                       // error
914                             bCancel = true;     // break for
915                             nCheckPos = nPosOld;
916                         }
917                     }
918                     break;
919                 case BRACKET_SYMBOLTYPE_NATNUM0 :
920                 case BRACKET_SYMBOLTYPE_NATNUM1 :
921                 case BRACKET_SYMBOLTYPE_NATNUM2 :
922                 case BRACKET_SYMBOLTYPE_NATNUM3 :
923                 case BRACKET_SYMBOLTYPE_NATNUM4 :
924                 case BRACKET_SYMBOLTYPE_NATNUM5 :
925                 case BRACKET_SYMBOLTYPE_NATNUM6 :
926                 case BRACKET_SYMBOLTYPE_NATNUM7 :
927                 case BRACKET_SYMBOLTYPE_NATNUM8 :
928                 case BRACKET_SYMBOLTYPE_NATNUM9 :
929                 case BRACKET_SYMBOLTYPE_NATNUM10 :
930                 case BRACKET_SYMBOLTYPE_NATNUM11 :
931                 case BRACKET_SYMBOLTYPE_NATNUM12 :
932                 case BRACKET_SYMBOLTYPE_NATNUM13 :
933                 case BRACKET_SYMBOLTYPE_NATNUM14 :
934                 case BRACKET_SYMBOLTYPE_NATNUM15 :
935                 case BRACKET_SYMBOLTYPE_NATNUM16 :
936                 case BRACKET_SYMBOLTYPE_NATNUM17 :
937                 case BRACKET_SYMBOLTYPE_NATNUM18 :
938                 case BRACKET_SYMBOLTYPE_NATNUM19 :
939                     if ( NumFor[nIndex].GetNatNum().IsSet() )
940                     {
941                         bCancel = true;         // break for
942                         nCheckPos = nPosOld;
943                     }
944                     else
945                     {
946                         OUString sParams;
947                         sal_Int32 nSpacePos = sStr.indexOf(' ');
948                         if (nSpacePos >= 0)
949                         {
950                             sParams = o3tl::trim(sStr.subView(nSpacePos+1));
951                         }
952                         //! eSymbolType is negative
953                         sal_uInt8 nNum = static_cast<sal_uInt8>(0 - (eSymbolType - BRACKET_SYMBOLTYPE_NATNUM0));
954                         if (!sParams.isEmpty() && !NatNumTakesParameters(nNum))
955                         {
956                             bCancel = true; // break for
957                             nCheckPos = nPosOld;
958                             break;
959                         }
960                         sStr = "NatNum" + OUString::number(nNum);
961                         NumFor[nIndex].SetNatNumNum( nNum, false );
962                         // NatNum12 supports arguments
963                         if (nNum == 12)
964                         {
965                             if (sParams.isEmpty())
966                                 sParams = "cardinal"; // default NatNum12 format is "cardinal"
967                             else if (sParams.indexOf("CURRENCY") >= 0)
968                                 sParams = sParams.replaceAll("CURRENCY",
969                                     rLoc().getCurrBankSymbol());
970                             NumFor[nIndex].SetNatNumParams(sParams);
971                             sStr += " " + sParams;
972                         }
973                     }
974                     break;
975                 case BRACKET_SYMBOLTYPE_DBNUM1 :
976                 case BRACKET_SYMBOLTYPE_DBNUM2 :
977                 case BRACKET_SYMBOLTYPE_DBNUM3 :
978                 case BRACKET_SYMBOLTYPE_DBNUM4 :
979                 case BRACKET_SYMBOLTYPE_DBNUM5 :
980                 case BRACKET_SYMBOLTYPE_DBNUM6 :
981                 case BRACKET_SYMBOLTYPE_DBNUM7 :
982                 case BRACKET_SYMBOLTYPE_DBNUM8 :
983                 case BRACKET_SYMBOLTYPE_DBNUM9 :
984                     if ( NumFor[nIndex].GetNatNum().IsSet() )
985                     {
986                         bCancel = true;         // break for
987                         nCheckPos = nPosOld;
988                     }
989                     else
990                     {
991                         //! eSymbolType is negative
992                         sal_uInt8 nNum = static_cast<sal_uInt8>(1 - (eSymbolType - BRACKET_SYMBOLTYPE_DBNUM1));
993                         sStr = "DBNum" + OUStringChar(sal_Unicode('0' + nNum));
994                         NumFor[nIndex].SetNatNumNum( nNum, true );
995                     }
996                     break;
997                 case BRACKET_SYMBOLTYPE_LOCALE :
998                     if ( NumFor[nIndex].GetNatNum().GetLang() != LANGUAGE_DONTKNOW ||
999                          sBuff[nPos-1] != ']' )
1000                         // Check also for ']' to avoid pulling in
1001                         // locale data for the preview string for not
1002                         // yet completed LCIDs in the dialog.
1003                     {
1004                         bCancel = true;         // break for
1005                         nCheckPos = nPosOld;
1006                     }
1007                     else
1008                     {
1009                         sal_Int32 nTmp = 2;
1010                         LocaleType aTmpLocale( ImpGetLocaleType( sStr, nTmp));
1011                         if (aTmpLocale.meLanguage == LANGUAGE_DONTKNOW)
1012                         {
1013                             bCancel = true;         // break for
1014                             nCheckPos = nPosOld;
1015                         }
1016                         else
1017                         {
1018                             // Only the first sub format's locale will be
1019                             // used as the format's overall locale.
1020                             // Sorts this also under the corresponding
1021                             // locale for the dialog.
1022                             // If we don't support the locale this would
1023                             // result in an unknown (empty) language
1024                             // listbox entry and the user would never see
1025                             // this format.
1026                             if (nIndex == 0 && (aTmpLocale.meLanguage == LANGUAGE_SYSTEM ||
1027                                                 SvNumberFormatter::IsLocaleInstalled( aTmpLocale.meLanguage)))
1028                             {
1029                                 maLocale = aTmpLocale;
1030                                 eLan = aTmpLocale.meLanguage;   // return to caller
1031 
1032                                 // Set new target locale also at scanner.
1033                                 // We have to do this because switching locale
1034                                 // may make replacing keywords and separators
1035                                 // necessary.
1036                                 // We can do this because it's the first
1037                                 // subformat and we're still at parsing the
1038                                 // modifiers, not keywords.
1039                                 rScan.SetNewLnge( eLan);
1040                                 // We can not force conversion though because
1041                                 // the caller may have explicitly not set it.
1042                                 // In the usual case the target locale is the
1043                                 // originating locale the conversion is not
1044                                 // necessary, when reading alien documents
1045                                 // conversion is enabled anyway.
1046 
1047                                 /* TODO: fiddle with scanner to make this
1048                                  * known? A change in the locale may affect
1049                                  * separators and keywords. On the other
1050                                  * hand they may have been entered as used
1051                                  * in the originating locale, there's no
1052                                  * way to predict other than analyzing the
1053                                  * format code, we assume here the current
1054                                  * context is used, which is most likely
1055                                  * the case.
1056                                  * */
1057 
1058                                 // Strip a plain locale identifier if locale
1059                                 // data is available to avoid duplicated
1060                                 // formats with and without LCID for the same
1061                                 // locale. Besides it looks ugly and confusing
1062                                 // and is unnecessary as the format will be
1063                                 // listed for the resulting locale.
1064                                 if (aTmpLocale.isPlainLocale())
1065                                     sStr.clear();
1066                                 else
1067                                     sStr = "$-" + aTmpLocale.generateCode();
1068                             }
1069                             else
1070                             {
1071                                 if (nIndex == 0)
1072                                     // Locale data not available, remember.
1073                                     maLocale.meLanguageWithoutLocaleData = aTmpLocale.meLanguage;
1074 
1075                                 sStr = "$-" + aTmpLocale.generateCode();
1076                             }
1077                             NumFor[nIndex].SetNatNumLang( MsLangId::getRealLanguage( aTmpLocale.meLanguage));
1078 
1079                             // "$-NNCCLLLL" Numerals and Calendar
1080                             if (sSymbol.getLength() > 6)
1081                             {
1082                                 sInsertCalendar = ImpObtainCalendarAndNumerals( sBuff, nPos, eLan, aTmpLocale);
1083                             }
1084                             /* NOTE: there can be only one calendar
1085                              * inserted so the last one wins, though
1086                              * our own calendar modifiers support
1087                              * multiple calendars within one sub format
1088                              * code if at different positions. */
1089                         }
1090                     }
1091                     break;
1092                 }
1093                 if ( !bCancel )
1094                 {
1095                     if (sStr == sSymbol)
1096                     {
1097                         nPosOld = nPos;
1098                     }
1099                     else
1100                     {
1101                         sBuff.remove(nPosOld, nPos - nPosOld);
1102                         if (!sStr.isEmpty())
1103                         {
1104                             sBuff.insert(nPosOld, "[" + sStr + "]");
1105                             nPos = nPosOld + sStr.getLength() + 2;
1106                             nPosOld = nPos;     // position before string
1107                         }
1108                         else
1109                         {
1110                             nPos = nPosOld;     // prefix removed for whatever reason
1111                         }
1112                     }
1113                 }
1114             }
1115         }
1116         while ( !bCancel && lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) );
1117 
1118         // The remaining format code string
1119         if ( !bCancel )
1120         {
1121             if (eSymbolType == BRACKET_SYMBOLTYPE_FORMAT)
1122             {
1123                 if (nIndex == 1 && eOp1 == NUMBERFORMAT_OP_NO)
1124                 {
1125                     eOp1 = NUMBERFORMAT_OP_GT;  // undefined condition, default: > 0
1126                 }
1127                 else if (nIndex == 2 && eOp2 == NUMBERFORMAT_OP_NO)
1128                 {
1129                     eOp2 = NUMBERFORMAT_OP_LT;  // undefined condition, default: < 0
1130                 }
1131                 if (sStr.isEmpty())
1132                 {
1133                     // Empty sub format.
1134                     NumFor[nIndex].Info().eScannedType = SvNumFormatType::EMPTY;
1135                 }
1136                 else
1137                 {
1138                     if (!sInsertCalendar.isEmpty())
1139                     {
1140                         sStr = sInsertCalendar + sStr;
1141                     }
1142                     sal_Int32 nStrPos = pSc->ScanFormat( sStr);
1143                     sal_uInt16 nCnt = pSc->GetResultStringsCnt();
1144                     if (nCnt == 0 && nStrPos == 0)  // error
1145                     {
1146                         nStrPos = 1;
1147                     }
1148                     if (nStrPos == 0)               // ok
1149                     {
1150                         // e.g. Thai T speciality
1151                         if (pSc->GetNatNumModifier() && !NumFor[nIndex].GetNatNum().IsSet())
1152                         {
1153                             sStr = "[NatNum"  + OUString::number( pSc->GetNatNumModifier()) + "]" + sStr;
1154                             NumFor[nIndex].SetNatNumNum( pSc->GetNatNumModifier(), false );
1155                         }
1156                         // #i53826# #i42727# For the Thai T speciality we need
1157                         // to freeze the locale and immunize it against
1158                         // conversions during exports, just in case we want to
1159                         // save to Xcl. This disables the feature of being able
1160                         // to convert a NatNum to another locale. You can't
1161                         // have both.
1162                         // FIXME: implement a specialized export conversion
1163                         // that works on tokens (have to tokenize all first)
1164                         // and doesn't use the format string and
1165                         // PutandConvertEntry() to LANGUAGE_ENGLISH_US in
1166                         // sc/source/filter/excel/xestyle.cxx
1167                         // XclExpNumFmtBuffer::WriteFormatRecord().
1168                         LanguageType eLanguage;
1169                         if (NumFor[nIndex].GetNatNum().GetNatNum() == 1 &&
1170                             ((eLanguage = MsLangId::getRealLanguage( eLan)) == LANGUAGE_THAI) &&
1171                             NumFor[nIndex].GetNatNum().GetLang() == LANGUAGE_DONTKNOW)
1172                         {
1173                             sStr = "[$-" + OUString::number( sal_uInt16(eLanguage), 16 ).toAsciiUpperCase() + "]" + sStr;
1174                             NumFor[nIndex].SetNatNumLang( eLanguage);
1175                         }
1176                         sBuff.remove(nPosOld, nPos - nPosOld);
1177                         sBuff.insert(nPosOld, sStr);
1178                         nPos = nPosOld + sStr.getLength();
1179                         if (nPos < sBuff.getLength())
1180                         {
1181                             sBuff.insert(nPos, ";");
1182                             nPos++;
1183                         }
1184                         else if (nIndex > 0)
1185                         {
1186                             // The last subformat. If it is a trailing text
1187                             // format the omitted subformats act like they were
1188                             // not specified and "inherited" the first format,
1189                             // e.g.  0;@  behaves like  0;-0;0;@
1190                             if (pSc->GetScannedType() == SvNumFormatType::TEXT)
1191                             {
1192                                 // Reset conditions, reverting any set above.
1193                                 if (nIndex == 1)
1194                                     eOp1 = NUMBERFORMAT_OP_NO;
1195                                 else if (nIndex == 2)
1196                                     eOp2 = NUMBERFORMAT_OP_NO;
1197                                 nIndex = 3;
1198                             }
1199                         }
1200                         NumFor[nIndex].Enlarge(nCnt);
1201                         pSc->CopyInfo(&(NumFor[nIndex].Info()), nCnt);
1202                         // type check
1203                         if (nIndex == 0)
1204                         {
1205                             if ( NumFor[nIndex].GetNatNum().GetNatNum() == 12 &&
1206                                     lcl_isNatNum12Currency(NumFor[nIndex].GetNatNum().GetParams()) )
1207                                 eType = SvNumFormatType::CURRENCY;
1208                             else
1209                                 eType = NumFor[nIndex].Info().eScannedType;
1210                         }
1211                         else if (nIndex == 3)
1212                         {   // #77026# Everything recognized IS text
1213                             NumFor[nIndex].Info().eScannedType = SvNumFormatType::TEXT;
1214                         }
1215                         else if ( NumFor[nIndex].Info().eScannedType != eType)
1216                         {
1217                             eType = SvNumFormatType::DEFINED;
1218                         }
1219                     }
1220                     else
1221                     {
1222                         nCheckPos = nPosOld + nStrPos;  // error in string
1223                         bCancel = true;                 // break for
1224                     }
1225                 }
1226             }
1227             else if (eSymbolType == BRACKET_SYMBOLTYPE_ERROR)   // error
1228             {
1229                 nCheckPos = nPosOld;
1230                 bCancel = true;
1231             }
1232             else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) )
1233             {
1234                 nCheckPos = nPosOld + 1;                // error, prefix in string
1235                 bCancel = true;                         // break for
1236             }
1237         }
1238         if ( bCancel && !nCheckPos )
1239         {
1240             nCheckPos = 1;      // nCheckPos is used as an error condition
1241         }
1242         if ( !bCancel )
1243         {
1244             if ( NumFor[nIndex].GetNatNum().IsSet() &&
1245                  NumFor[nIndex].GetNatNum().GetLang() == LANGUAGE_DONTKNOW )
1246             {
1247                  NumFor[nIndex].SetNatNumLang( eLan );
1248             }
1249         }
1250         if (sBuff.getLength() == nPos)
1251         {
1252             if (nIndex < 3 && rString[rString.getLength()-1] == ';')
1253             {
1254                 // A trailing ';' is significant and specifies the following
1255                 // subformat to be empty. We don't enter the scanning loop
1256                 // above again though.
1257                 // Note that the operators apply to the current last scanned
1258                 // subformat.
1259                 if (nIndex == 0 && eOp1 == NUMBERFORMAT_OP_NO)
1260                 {
1261                     eOp1 = NUMBERFORMAT_OP_GT;  // undefined condition, default: > 0
1262                 }
1263                 else if (nIndex == 1 && eOp2 == NUMBERFORMAT_OP_NO)
1264                 {
1265                     eOp2 = NUMBERFORMAT_OP_LT;  // undefined condition, default: < 0
1266                 }
1267                 NumFor[nIndex+1].Info().eScannedType = SvNumFormatType::EMPTY;
1268                 if (sBuff[nPos-1] != ';')
1269                     sBuff.insert( nPos++, ';');
1270             }
1271             if (nIndex == 2 && eSymbolType == BRACKET_SYMBOLTYPE_FORMAT && sBuff[nPos-1] == ';')
1272             {
1273                 // #83510# A 4th subformat explicitly specified to be empty
1274                 // hides any text. Need the type here for HasTextFormat()
1275                 NumFor[3].Info().eScannedType = SvNumFormatType::TEXT;
1276             }
1277             bCancel = true;
1278         }
1279         if ( NumFor[nIndex].GetNatNum().IsSet() )
1280         {
1281             NumFor[nIndex].SetNatNumDate( bool(NumFor[nIndex].Info().eScannedType & SvNumFormatType::DATE) );
1282         }
1283     }
1284 
1285     if (!nCheckPos && IsSubstituted())
1286     {
1287         // For to be substituted formats the scanned type must match the
1288         // substitute type.
1289         if (IsSystemTimeFormat())
1290         {
1291             if ((eType & ~SvNumFormatType::DEFINED) != SvNumFormatType::TIME)
1292                 nCheckPos = std::max<sal_Int32>( sBuff.indexOf(']') + 1, 1);
1293         }
1294         else if (IsSystemLongDateFormat())
1295         {
1296             if ((eType & ~SvNumFormatType::DEFINED) != SvNumFormatType::DATE)
1297                 nCheckPos = std::max<sal_Int32>( sBuff.indexOf(']') + 1, 1);
1298         }
1299         else
1300             assert(!"unhandled substitute");
1301     }
1302 
1303     if ( bCondition && !nCheckPos )
1304     {
1305         if ( nIndex == 1 && NumFor[0].GetCount() == 0 &&
1306              sBuff[sBuff.getLength() - 1] != ';' )
1307         {
1308             // No format code => GENERAL but not if specified empty
1309             OUString aAdd( pSc->GetStandardName() );
1310             if ( !pSc->ScanFormat( aAdd ) )
1311             {
1312                 sal_uInt16 nCnt = pSc->GetResultStringsCnt();
1313                 if ( nCnt )
1314                 {
1315                     NumFor[0].Enlarge(nCnt);
1316                     pSc->CopyInfo( &(NumFor[0].Info()), nCnt );
1317                     sBuff.append(aAdd);
1318                 }
1319             }
1320         }
1321         else if ( nIndex == 1 && NumFor[nIndex].GetCount() == 0 &&
1322                   sBuff[sBuff.getLength() - 1] != ';' &&
1323                   (NumFor[0].GetCount() > 1 ||
1324                    (NumFor[0].GetCount() == 1 &&
1325                     NumFor[0].Info().nTypeArray[0] != NF_KEY_GENERAL)) )
1326         {
1327             // No trailing second subformat => GENERAL but not if specified empty
1328             // and not if first subformat is GENERAL
1329             OUString aAdd( pSc->GetStandardName() );
1330             if ( !pSc->ScanFormat( aAdd ) )
1331             {
1332                 sal_uInt16 nCnt = pSc->GetResultStringsCnt();
1333                 if ( nCnt )
1334                 {
1335                     NumFor[nIndex].Enlarge(nCnt);
1336                     pSc->CopyInfo( &(NumFor[nIndex].Info()), nCnt );
1337                     sBuff.append(";");
1338                     sBuff.append(aAdd);
1339                 }
1340             }
1341         }
1342         else if ( nIndex == 2 && NumFor[nIndex].GetCount() == 0 &&
1343                   sBuff[sBuff.getLength() - 1] != ';' &&
1344                   eOp2 != NUMBERFORMAT_OP_NO )
1345         {
1346             // No trailing third subformat => GENERAL but not if specified empty
1347             OUString aAdd( pSc->GetStandardName() );
1348             if ( !pSc->ScanFormat( aAdd ) )
1349             {
1350                 sal_uInt16 nCnt = pSc->GetResultStringsCnt();
1351                 if ( nCnt )
1352                 {
1353                     NumFor[nIndex].Enlarge(nCnt);
1354                     pSc->CopyInfo( &(NumFor[nIndex].Info()), nCnt );
1355                     sBuff.append(";");
1356                     sBuff.append(aAdd);
1357                 }
1358             }
1359         }
1360     }
1361     rString = sBuff.makeStringAndClear();
1362     sFormatstring = rString;
1363 
1364     if (NumFor[2].GetCount() == 0 && // No third partial string
1365         eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_NO &&
1366         fLimit1 == 0.0 && fLimit2 == 0.0)
1367     {
1368         eOp1 = NUMBERFORMAT_OP_GE; // Add 0 to the first format
1369     }
1370 
1371 }
1372 
1373 SvNumberformat::~SvNumberformat()
1374 {
1375 }
1376 
1377 /**
1378  * Next_Symbol
1379  *
1380  * Splits up the symbols for further processing (by the Turing machine)
1381  *
1382  * Start state = SsStart, * = Special state
1383  * ---------------+-------------------+----------------------------+---------------
1384  *  Old State     | Symbol read       | Event                      | New state
1385  * ---------------+-------------------+----------------------------+---------------
1386  *  SsStart       | "                 | Symbol += Character        | SsGetQuoted
1387  *                | ;                 | Pos--                      | SsGetString
1388  *                | [                 | Symbol += Character        | SsGetBracketed
1389  *                | ]                 | Error                      | SsStop
1390  *                | BLANK             |                            |
1391  *                | Else              | Symbol += Character        | SsGetString
1392  * ---------------+-------------------+----------------------------+---------------
1393  *  SsGetString   | "                 | Symbol += Character        | SsGetQuoted
1394  *                | ;                 |                            | SsStop
1395  *                | Else              | Symbol += Character        |
1396  * ---------------+-------------------+----------------------------+---------------
1397  *  SsGetQuoted   | "                 | Symbol += Character        | SsGetString
1398  *                | Else              | Symbol += Character        |
1399  * ---------------+-------------------+----------------------------+---------------
1400  * SsGetBracketed | <, > =            | del [                      |
1401  *                |                   | Symbol += Character        | SsGetCon
1402  *                | BLANK             |                            |
1403  *                | h, H, m, M, s, S  | Symbol += Character        | SsGetTime
1404  *                | Else              | del [                      |
1405  *                |                   | Symbol += Character        | SsGetPrefix
1406  * ---------------+-------------------+----------------------------+---------------
1407  *  SsGetTime     | ]                 | Symbol += Character        | SsGetString
1408  *                | h, H, m, M, s, S  | Symbol += Character, *     | SsGetString
1409  *                | Else              | del [; Symbol += Character | SsGetPrefix
1410  * ---------------+-------------------+----------------------------+---------------
1411  *  SsGetPrefix   | ]                 |                            | SsStop
1412  *                | Else              | Symbol += Character        |
1413  * ---------------+-------------------+----------------------------+---------------
1414  *  SsGetCon      | >, =              | Symbol += Character        |
1415  *                | ]                 |                            | SsStop
1416  *                | Else              | Error                      | SsStop
1417  * ---------------+-------------------+----------------------------+---------------
1418  */
1419 
1420 namespace {
1421 
1422 enum ScanState
1423 {
1424     SsStop,
1425     SsStart,
1426     SsGetCon,           // condition
1427     SsGetString,        // format string
1428     SsGetPrefix,        // color or NatNumN
1429     SsGetTime,          // [HH] for time
1430     SsGetBracketed,     // any [...] not decided yet
1431     SsGetQuoted         // quoted text
1432 };
1433 
1434 }
1435 
1436 // read a string until ']' and delete spaces in input
1437 // static
1438 sal_Int32 SvNumberformat::ImpGetNumber(OUStringBuffer& rString,
1439                                        sal_Int32& nPos,
1440                                        OUString& sSymbol)
1441 {
1442     sal_Int32 nStartPos = nPos;
1443     sal_Unicode cToken;
1444     sal_Int32 nLen = rString.getLength();
1445     OUStringBuffer sBuffSymbol;
1446     while ( nPos < nLen )
1447     {
1448         cToken = rString[nPos];
1449         if (cToken == ']')
1450             break;
1451         if (cToken == ' ')
1452         {                                               // delete spaces
1453             rString.remove(nPos,1);
1454             nLen--;
1455         }
1456         else
1457         {
1458             nPos++;
1459             sBuffSymbol.append(cToken);
1460         }
1461     }
1462     sSymbol = sBuffSymbol.makeStringAndClear();
1463     return nPos - nStartPos;
1464 }
1465 
1466 namespace {
1467 
1468 sal_Unicode toUniChar(sal_uInt8 n)
1469 {
1470     char c;
1471     if (n < 10)
1472     {
1473         c = '0' + n;
1474     }
1475     else
1476     {
1477         c = 'A' + n - 10;
1478     }
1479     return sal_Unicode(c);
1480 }
1481 
1482 bool IsCombiningSymbol( OUStringBuffer& rStringBuffer, sal_Int32 nPos )
1483 {
1484     bool bRet = false;
1485     while (nPos >= 0)
1486     {
1487         switch (rStringBuffer[nPos])
1488         {
1489             case '*':
1490             case '\\':
1491             case '_':
1492                 bRet = !bRet;
1493                 --nPos;
1494                 break;
1495             default:
1496                 return bRet;
1497         }
1498     }
1499     return bRet;
1500 }
1501 
1502 } // namespace
1503 
1504 OUString SvNumberformat::LocaleType::generateCode() const
1505 {
1506     OUStringBuffer aBuf;
1507 #if 0
1508     // TODO: We may re-enable this later. Don't remove it! --Kohei
1509     if (mnNumeralShape)
1510     {
1511         sal_uInt8 nVal = mnNumeralShape;
1512         for (sal_uInt8 i = 0; i < 2; ++i)
1513         {
1514             sal_uInt8 n = (nVal & 0xF0) >> 4;
1515             if (n || aBuf.getLength())
1516             {
1517                 aBuf.append(toUniChar(n));
1518             }
1519             nVal = nVal << 4;
1520         }
1521     }
1522 
1523     if (mnNumeralShape || mnCalendarType)
1524     {
1525         sal_uInt8 nVal = mnCalendarType;
1526         for (sal_uInt8 i = 0; i < 2; ++i)
1527         {
1528             sal_uInt8 n = (nVal & 0xF0) >> 4;
1529             if (n || aBuf.getLength())
1530             {
1531                 aBuf.append(toUniChar(n));
1532             }
1533             nVal = nVal << 4;
1534         }
1535     }
1536 #endif
1537 
1538     sal_uInt16 n16 = static_cast<sal_uInt16>(
1539             (meLanguageWithoutLocaleData == LANGUAGE_DONTKNOW) ? meLanguage :
1540             meLanguageWithoutLocaleData);
1541     if (meLanguage == LANGUAGE_SYSTEM)
1542     {
1543         switch (meSubstitute)
1544         {
1545             case Substitute::NONE:
1546                 ;   // nothing
1547                 break;
1548             case Substitute::TIME:
1549                 n16 = static_cast<sal_uInt16>(LANGUAGE_NF_SYSTEM_TIME);
1550                 break;
1551             case Substitute::LONGDATE:
1552                 n16 = static_cast<sal_uInt16>(LANGUAGE_NF_SYSTEM_DATE);
1553                 break;
1554         }
1555     }
1556     for (sal_uInt8 i = 0; i < 4; ++i)
1557     {
1558         sal_uInt8 n = static_cast<sal_uInt8>((n16 & 0xF000) >> 12);
1559         // Omit leading zeros for consistency.
1560         if (n || !aBuf.isEmpty() || i == 3)
1561         {
1562             aBuf.append(toUniChar(n));
1563         }
1564         n16 = n16 << 4;
1565     }
1566 
1567     return aBuf.makeStringAndClear();
1568 }
1569 
1570 SvNumberformat::LocaleType::LocaleType()
1571     : meLanguage(LANGUAGE_DONTKNOW)
1572     , meLanguageWithoutLocaleData(LANGUAGE_DONTKNOW)
1573     , meSubstitute(Substitute::NONE)
1574     , mnNumeralShape(0)
1575     , mnCalendarType(0)
1576 {
1577 }
1578 
1579 SvNumberformat::LocaleType::LocaleType(sal_uInt32 nRawNum)
1580     : meLanguage(LANGUAGE_DONTKNOW)
1581     , meLanguageWithoutLocaleData(LANGUAGE_DONTKNOW)
1582     , meSubstitute(Substitute::NONE)
1583     , mnNumeralShape(0)
1584     , mnCalendarType(0)
1585 {
1586     meLanguage = static_cast<LanguageType>(nRawNum & 0x0000FFFF);
1587     if (meLanguage == LANGUAGE_NF_SYSTEM_TIME)
1588     {
1589         meSubstitute = Substitute::TIME;
1590         meLanguage = LANGUAGE_SYSTEM;
1591     }
1592     else if (meLanguage == LANGUAGE_NF_SYSTEM_DATE)
1593     {
1594         meSubstitute = Substitute::LONGDATE;
1595         meLanguage = LANGUAGE_SYSTEM;
1596     }
1597     nRawNum = (nRawNum >> 16);
1598     mnCalendarType = static_cast<sal_uInt8>(nRawNum & 0xFF);
1599     nRawNum = (nRawNum >> 8);
1600     mnNumeralShape = static_cast<sal_uInt8>(nRawNum & 0xFF);
1601 }
1602 
1603 bool SvNumberformat::LocaleType::isPlainLocale() const
1604 {
1605     return meSubstitute == Substitute::NONE && !mnCalendarType && !mnNumeralShape;
1606 }
1607 
1608 // static
1609 SvNumberformat::LocaleType SvNumberformat::ImpGetLocaleType(std::u16string_view rString, sal_Int32& nPos )
1610 {
1611     sal_uInt32 nNum = 0;
1612     sal_Unicode cToken = 0;
1613     sal_Int32 nStart = nPos;
1614     sal_Int32 nLen = rString.size();
1615     while ( nPos < nLen && (nPos - nStart < 8) )
1616     {
1617         cToken = rString[nPos];
1618         if (cToken == ']')
1619             break;
1620         if ( '0' <= cToken && cToken <= '9' )
1621         {
1622             nNum *= 16;
1623             nNum += cToken - '0';
1624         }
1625         else if ( 'a' <= cToken && cToken <= 'f' )
1626         {
1627             nNum *= 16;
1628             nNum += cToken - 'a' + 10;
1629         }
1630         else if ( 'A' <= cToken && cToken <= 'F' )
1631         {
1632             nNum *= 16;
1633             nNum += cToken - 'A' + 10;
1634         }
1635         else
1636         {
1637             return LocaleType(); // LANGUAGE_DONTKNOW;
1638         }
1639         ++nPos;
1640     }
1641 
1642     return (cToken == ']' || nPos == nLen) ? LocaleType(nNum) : LocaleType();
1643 }
1644 
1645 static bool lcl_matchKeywordAndGetNumber( std::u16string_view rString, const sal_Int32 nPos,
1646         std::u16string_view rKeyword, sal_Int32 & nNumber )
1647 {
1648     if (0 <= nPos && nPos + static_cast<sal_Int32>(rKeyword.size()) < static_cast<sal_Int32>(rString.size()) && o3tl::matchIgnoreAsciiCase( rString, rKeyword, nPos))
1649     {
1650         nNumber = o3tl::toInt32(rString.substr( nPos + rKeyword.size()));
1651         return true;
1652     }
1653     else
1654     {
1655         nNumber = 0;
1656         return false;
1657     }
1658 }
1659 
1660 short SvNumberformat::ImpNextSymbol(OUStringBuffer& rString,
1661                                     sal_Int32& nPos,
1662                                     OUString& sSymbol) const
1663 {
1664     short eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1665     sal_Unicode cToken;
1666     sal_Unicode cLetter = ' '; // Preliminary result
1667     sal_Int32 nLen = rString.getLength();
1668     ScanState eState = SsStart;
1669     OUStringBuffer sBuffSymbol(128);
1670 
1671     const NfKeywordTable & rKeywords = rScan.GetKeywords();
1672     while (nPos < nLen && eState != SsStop)
1673     {
1674         cToken = rString[nPos];
1675         nPos++;
1676         switch (eState)
1677         {
1678         case SsStart:
1679             if (cToken == '\"')
1680             {
1681                 eState = SsGetQuoted;
1682                 sBuffSymbol.append(cToken);
1683             }
1684             else if (cToken == '[')
1685             {
1686                 eState = SsGetBracketed;
1687                 sBuffSymbol.append(cToken);
1688             }
1689             else if (cToken == ';')
1690             {
1691                 eState = SsGetString;
1692                 nPos--;
1693                 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1694             }
1695             else if (cToken == ']')
1696             {
1697                 eState = SsStop;
1698                 eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1699             }
1700             else if (cToken == ' ') // Skip Blanks
1701             {
1702                 nPos--;
1703                 rString.remove(nPos, 1);
1704                 nLen--;
1705             }
1706             else
1707             {
1708                 sBuffSymbol.append(cToken);
1709                 eState = SsGetString;
1710                 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1711             }
1712             break;
1713         case SsGetBracketed:
1714             switch (cToken)
1715             {
1716             case '<':
1717             case '>':
1718             case '=':
1719                 sBuffSymbol.stripStart('[');
1720                 sBuffSymbol.append(cToken);
1721                 cLetter = cToken;
1722                 eState = SsGetCon;
1723                 switch (cToken)
1724                 {
1725                 case '<':
1726                     eSymbolType = NUMBERFORMAT_OP_LT;
1727                     break;
1728                 case '>':
1729                     eSymbolType = NUMBERFORMAT_OP_GT;
1730                     break;
1731                 case '=':
1732                     eSymbolType = NUMBERFORMAT_OP_EQ;
1733                     break;
1734                 }
1735                 break;
1736             case ' ':
1737                 nPos--;
1738                 rString.remove(nPos, 1);
1739                 nLen--;
1740                 break;
1741             case '$' :
1742                 if ( nPos < nLen && rString[nPos] == '-' )
1743                 {
1744                     // [$-xxx] locale
1745                     sBuffSymbol.stripStart('[');
1746                     eSymbolType = BRACKET_SYMBOLTYPE_LOCALE;
1747                     eState = SsGetPrefix;
1748                 }
1749                 else
1750                 {   // currency
1751                     eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1752                     eState = SsGetString;
1753                 }
1754                 sBuffSymbol.append(cToken);
1755                 break;
1756             case '~' :
1757                 // calendarID
1758                 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1759                 sBuffSymbol.append(cToken);
1760                 eState = SsGetString;
1761                 break;
1762             default:
1763             {
1764                 static const OUStringLiteral aNatNum(u"NATNUM");
1765                 static const OUStringLiteral aDBNum(u"DBNUM");
1766                 const OUString aBufStr( rString.toString());
1767                 sal_Int32 nNatNumNum;
1768                 sal_Int32 nDBNum;
1769                 if ( lcl_matchKeywordAndGetNumber( aBufStr, nPos-1, aNatNum, nNatNumNum) &&
1770                         0 <= nNatNumNum && nNatNumNum <= 19 )
1771                 {
1772                     sBuffSymbol.stripStart('[');
1773                     sBuffSymbol.append( aBufStr.subView(--nPos, aNatNum.getLength()+1) );
1774                     nPos += aNatNum.getLength()+1;
1775                     //! SymbolType is negative
1776                     eSymbolType = static_cast<short>(BRACKET_SYMBOLTYPE_NATNUM0 - nNatNumNum);
1777                     eState = SsGetPrefix;
1778                 }
1779                 else if ( lcl_matchKeywordAndGetNumber( aBufStr, nPos-1, aDBNum, nDBNum) &&
1780                         1 <= nDBNum && nDBNum <= 9 )
1781                 {
1782                     sBuffSymbol.stripStart('[');
1783                     sBuffSymbol.append( aBufStr.subView(--nPos, aDBNum.getLength()+1) );
1784                     nPos += aDBNum.getLength()+1;
1785                     //! SymbolType is negative
1786                     eSymbolType = sal::static_int_cast< short >( BRACKET_SYMBOLTYPE_DBNUM1 - (nDBNum - 1) );
1787                     eState = SsGetPrefix;
1788                 }
1789                 else
1790                 {
1791                     sal_Unicode cUpper = rChrCls().uppercase( aBufStr, nPos-1, 1)[0];
1792                     if (    cUpper == rKeywords[NF_KEY_H][0] ||     // H
1793                             cUpper == rKeywords[NF_KEY_MI][0] ||    // M
1794                             cUpper == rKeywords[NF_KEY_S][0] )      // S
1795                     {
1796                         sBuffSymbol.append(cToken);
1797                         eState = SsGetTime;
1798                         cLetter = cToken;
1799                     }
1800                     else
1801                     {
1802                         sBuffSymbol.stripStart('[');
1803                         sBuffSymbol.append(cToken);
1804                         eSymbolType = BRACKET_SYMBOLTYPE_COLOR;
1805                         eState = SsGetPrefix;
1806                     }
1807                 }
1808             }
1809             }
1810             break;
1811         case SsGetString:
1812             if (cToken == '\"')
1813             {
1814                 eState = SsGetQuoted;
1815                 sBuffSymbol.append(cToken);
1816             }
1817             else if (cToken == ';' && (nPos < 2 || !IsCombiningSymbol( rString, nPos-2)))
1818             {
1819                 eState = SsStop;
1820             }
1821             else
1822             {
1823                 sBuffSymbol.append(cToken);
1824             }
1825             break;
1826         case SsGetQuoted:
1827             if (cToken == '\"')
1828             {
1829                 eState = SsGetString;
1830                 sBuffSymbol.append(cToken);
1831             }
1832             else
1833             {
1834                 sBuffSymbol.append(cToken);
1835             }
1836             break;
1837         case SsGetTime:
1838             if (cToken == ']')
1839             {
1840                 sBuffSymbol.append(cToken);
1841                 eState = SsGetString;
1842                 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1843             }
1844             else
1845             {
1846                 sal_Unicode cUpper = rChrCls().uppercase(rString.toString(), nPos-1, 1)[0];
1847                 if (cUpper == rKeywords[NF_KEY_H][0] ||   // H
1848                     cUpper == rKeywords[NF_KEY_MI][0] ||  // M
1849                     cUpper == rKeywords[NF_KEY_S][0] )    // S
1850                 {
1851                     if (cLetter == cToken)
1852                     {
1853                         sBuffSymbol.append(cToken);
1854                         cLetter = ' ';
1855                     }
1856                     else
1857                     {
1858                         sBuffSymbol.stripStart('[');
1859                         sBuffSymbol.append(cToken);
1860                         eState = SsGetPrefix;
1861                     }
1862                 }
1863                 else
1864                 {
1865                     sBuffSymbol.stripStart('[');
1866                     sBuffSymbol.append(cToken);
1867                     eSymbolType = BRACKET_SYMBOLTYPE_COLOR;
1868                     eState = SsGetPrefix;
1869                 }
1870             }
1871             break;
1872         case SsGetCon:
1873             switch (cToken)
1874             {
1875             case '<':
1876                 eState = SsStop;
1877                 eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1878                 break;
1879             case '>':
1880                 if (cLetter == '<')
1881                 {
1882                     sBuffSymbol.append(cToken);
1883                     cLetter = ' ';
1884                     eState = SsStop;
1885                     eSymbolType = NUMBERFORMAT_OP_NE;
1886                 }
1887                 else
1888                 {
1889                     eState = SsStop;
1890                     eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1891                 }
1892                 break;
1893             case '=':
1894                 if (cLetter == '<')
1895                 {
1896                     sBuffSymbol.append(cToken);
1897                     cLetter = ' ';
1898                     eSymbolType = NUMBERFORMAT_OP_LE;
1899                 }
1900                 else if (cLetter == '>')
1901                 {
1902                     sBuffSymbol.append(cToken);
1903                     cLetter = ' ';
1904                     eSymbolType = NUMBERFORMAT_OP_GE;
1905                 }
1906                 else
1907                 {
1908                     eState = SsStop;
1909                     eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1910                 }
1911                 break;
1912             case ' ':
1913                 nPos--;
1914                 rString.remove(nPos,1);
1915                 nLen--;
1916                 break;
1917             default:
1918                 eState = SsStop;
1919                 nPos--;
1920                 break;
1921             }
1922             break;
1923         case SsGetPrefix:
1924             if (cToken == ']')
1925             {
1926                 eState = SsStop;
1927             }
1928             else
1929             {
1930                 sBuffSymbol.append(cToken);
1931             }
1932             break;
1933         default:
1934             break;
1935         } // of switch
1936     } // of while
1937     sSymbol = sBuffSymbol.makeStringAndClear();
1938     return eSymbolType;
1939 }
1940 
1941 void SvNumberformat::ConvertLanguage( SvNumberFormatter& rConverter,
1942                                       LanguageType eConvertFrom,
1943                                       LanguageType eConvertTo )
1944 {
1945     sal_Int32 nCheckPos;
1946     sal_uInt32 nKey;
1947     SvNumFormatType nType = eType;
1948     OUString aFormatString( sFormatstring );
1949     rConverter.PutandConvertEntry( aFormatString, nCheckPos, nType,
1950                                    nKey, eConvertFrom, eConvertTo, false);
1951     const SvNumberformat* pFormat = rConverter.GetEntry( nKey );
1952     DBG_ASSERT( pFormat, "SvNumberformat::ConvertLanguage: Conversion without format" );
1953     if ( pFormat )
1954     {
1955         ImpCopyNumberformat( *pFormat );
1956         // Reset values taken over from Formatter/Scanner
1957         // pColor still points to table in temporary Formatter/Scanner
1958         for (ImpSvNumFor & rFormatter : NumFor)
1959         {
1960             OUString aColorName = rFormatter.GetColorName();
1961             const Color* pColor = rScan.GetColor( aColorName );
1962             rFormatter.SetColor( pColor, aColorName );
1963         }
1964     }
1965 }
1966 
1967 bool SvNumberformat::HasNewCurrency() const
1968 {
1969     for (const auto & j : NumFor)
1970     {
1971         if ( j.HasNewCurrency() )
1972         {
1973             return true;
1974         }
1975     }
1976     return false;
1977 }
1978 
1979 bool SvNumberformat::GetNewCurrencySymbol( OUString& rSymbol,
1980                                            OUString& rExtension ) const
1981 {
1982     for (const auto & j : NumFor)
1983     {
1984         if ( j.GetNewCurrencySymbol( rSymbol, rExtension ) )
1985         {
1986             return true;
1987         }
1988     }
1989     rSymbol.clear();
1990     rExtension.clear();
1991     return false;
1992 }
1993 
1994 // static
1995 OUString SvNumberformat::StripNewCurrencyDelimiters( const OUString& rStr )
1996 {
1997     OUStringBuffer aTmp(rStr.getLength());
1998     sal_Int32 nStartPos, nPos, nLen;
1999     nLen = rStr.getLength();
2000     nStartPos = 0;
2001     while ( (nPos = rStr.indexOf( "[$", nStartPos )) >= 0 )
2002     {
2003         sal_Int32 nEnd;
2004         if ( (nEnd = GetQuoteEnd( rStr, nPos )) >= 0 )
2005         {
2006             aTmp.append(rStr.subView( nStartPos, ++nEnd - nStartPos ));
2007             nStartPos = nEnd;
2008         }
2009         else
2010         {
2011             aTmp.append(rStr.subView(nStartPos, nPos - nStartPos) );
2012             nStartPos = nPos + 2;
2013             sal_Int32 nDash;
2014             nEnd = nStartPos - 1;
2015             do
2016             {
2017                 nDash = rStr.indexOf( '-', ++nEnd );
2018                 nEnd = GetQuoteEnd( rStr, nDash );
2019             }
2020             while ( nEnd >= 0 );
2021             sal_Int32 nClose;
2022             nEnd = nStartPos - 1;
2023             do
2024             {
2025                 nClose = rStr.indexOf( ']', ++nEnd );
2026                 nEnd = GetQuoteEnd( rStr, nClose );
2027             }
2028             while ( nEnd >= 0 );
2029 
2030             if(nClose < 0)
2031             {
2032                 /* there should always be a closing ]
2033                  * but the old String class would have hidden
2034                  * that. so be conservative too
2035                  */
2036                 nClose = nLen;
2037             }
2038 
2039             nPos = nClose;
2040             if(nDash >= 0 && nDash < nClose)
2041             {
2042                 nPos = nDash;
2043             }
2044             aTmp.append(rStr.subView(nStartPos, nPos - nStartPos) );
2045             nStartPos = nClose + 1;
2046         }
2047     }
2048     if ( nLen > nStartPos )
2049     {
2050         aTmp.append(rStr.subView(nStartPos, nLen - nStartPos) );
2051     }
2052     return aTmp.makeStringAndClear();
2053 }
2054 
2055 void SvNumberformat::ImpGetOutputStandard(double& fNumber, OUStringBuffer& rOutString) const
2056 {
2057     OUString sTemp;
2058     ImpGetOutputStandard(fNumber, sTemp);
2059     rOutString = sTemp;
2060 }
2061 
2062 void SvNumberformat::ImpGetOutputStandard(double& fNumber, OUString& rOutString) const
2063 {
2064     sal_uInt16 nStandardPrec = rScan.GetStandardPrec();
2065 
2066     if ( fabs(fNumber) > EXP_ABS_UPPER_BOUND )
2067     {
2068         nStandardPrec = ::std::min(nStandardPrec, static_cast<sal_uInt16>(14)); // limits to 14 decimals
2069         rOutString = ::rtl::math::doubleToUString( fNumber,
2070                                                   rtl_math_StringFormat_E2, nStandardPrec /*2*/,
2071                                                   GetFormatter().GetNumDecimalSep()[0]);
2072     }
2073     else
2074     {
2075         ImpGetOutputStdToPrecision(fNumber, rOutString, nStandardPrec);
2076     }
2077 }
2078 
2079 namespace
2080 {
2081 
2082 template<typename T>
2083 bool checkForAll0s(const T& rString, sal_Int32 nIdx=0)
2084 {
2085     if (nIdx>=rString.getLength())
2086         return false;
2087 
2088     do
2089     {
2090         if (rString[nIdx]!='0')
2091             return false;
2092     }
2093     while (++nIdx<rString.getLength());
2094 
2095     return true;
2096 }
2097 
2098 }
2099 
2100 void SvNumberformat::ImpGetOutputStdToPrecision(double& rNumber, OUString& rOutString, sal_uInt16 nPrecision) const
2101 {
2102     // Make sure the precision doesn't go over the maximum allowable precision.
2103     nPrecision = ::std::min(UPPER_PRECISION, nPrecision);
2104 
2105     // We decided to strip trailing zeros unconditionally, since binary
2106     // double-precision rounding error makes it impossible to determine e.g.
2107     // whether 844.10000000000002273737 is what the user has typed, or the
2108     // user has typed 844.1 but IEEE 754 represents it that way internally.
2109 
2110     rOutString = ::rtl::math::doubleToUString( rNumber,
2111                                                rtl_math_StringFormat_F, nPrecision /*2*/,
2112                                                GetFormatter().GetNumDecimalSep()[0], true );
2113     if (rOutString[0] == '-' && checkForAll0s(rOutString, 1))
2114     {
2115         rOutString = comphelper::string::stripStart(rOutString, '-'); // not -0
2116     }
2117     rOutString = impTransliterate(rOutString, NumFor[0].GetNatNum());
2118 }
2119 
2120 void SvNumberformat::ImpGetOutputInputLine(double fNumber, OUString& OutString) const
2121 {
2122     bool bModified = false;
2123     if ( (eType & SvNumFormatType::PERCENT) && (fabs(fNumber) < D_MAX_D_BY_100))
2124     {
2125         if (fNumber == 0.0)
2126         {
2127             OutString = "0%";
2128             return;
2129         }
2130         fNumber *= 100;
2131         bModified = true;
2132     }
2133 
2134     if (fNumber == 0.0)
2135     {
2136         OutString = "0";
2137         return;
2138     }
2139 
2140     OutString = ::rtl::math::doubleToUString( fNumber,
2141                                               rtl_math_StringFormat_Automatic,
2142                                               rtl_math_DecimalPlaces_Max,
2143                                               GetFormatter().GetNumDecimalSep()[0], true );
2144 
2145     if ( eType & SvNumFormatType::PERCENT && bModified)
2146     {
2147         OutString += "%";
2148     }
2149 }
2150 
2151 short SvNumberformat::ImpCheckCondition(double fNumber,
2152                                         double fLimit,
2153                                         SvNumberformatLimitOps eOp)
2154 {
2155     switch(eOp)
2156     {
2157     case NUMBERFORMAT_OP_NO:
2158         return -1;
2159     case NUMBERFORMAT_OP_EQ:
2160         return static_cast<short>(fNumber == fLimit);
2161     case NUMBERFORMAT_OP_NE:
2162         return static_cast<short>(fNumber != fLimit);
2163     case NUMBERFORMAT_OP_LT:
2164         return static_cast<short>(fNumber <  fLimit);
2165     case NUMBERFORMAT_OP_LE:
2166         return static_cast<short>(fNumber <= fLimit);
2167     case NUMBERFORMAT_OP_GT:
2168         return static_cast<short>(fNumber >  fLimit);
2169     case NUMBERFORMAT_OP_GE:
2170         return static_cast<short>(fNumber >= fLimit);
2171     default:
2172         return -1;
2173     }
2174 }
2175 
2176 static bool lcl_appendStarFillChar( OUStringBuffer& rBuf, std::u16string_view rStr )
2177 {
2178     // Right during user input the star symbol is the very
2179     // last character before the user enters another one.
2180     if (rStr.size() > 1)
2181     {
2182         rBuf.append(u'\x001B');
2183         rBuf.append(rStr[1]);
2184         return true;
2185     }
2186     return false;
2187 }
2188 
2189 static bool lcl_insertStarFillChar( OUStringBuffer& rBuf, sal_Int32 nPos, std::u16string_view rStr )
2190 {
2191     if (rStr.size() > 1)
2192     {
2193         rBuf.insert( nPos, rStr[1]);
2194         rBuf.insert( nPos, u'\x001B');
2195         return true;
2196     }
2197     return false;
2198 }
2199 
2200 void SvNumberformat::GetOutputString(std::u16string_view sString,
2201                                      OUString& OutString,
2202                                      const Color** ppColor)
2203 {
2204     OUStringBuffer sOutBuff;
2205     sal_uInt16 nIx;
2206     if (eType & SvNumFormatType::TEXT)
2207     {
2208         nIx = 0;
2209     }
2210     else if (NumFor[3].GetCount() > 0)
2211     {
2212         nIx = 3;
2213     }
2214     else
2215     {
2216         *ppColor = nullptr; // no change of color
2217         return;
2218     }
2219     *ppColor = NumFor[nIx].GetColor();
2220     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2221     if (rInfo.eScannedType == SvNumFormatType::TEXT)
2222     {
2223         const sal_uInt16 nCnt = NumFor[nIx].GetCount();
2224         for (sal_uInt16 i = 0; i < nCnt; i++)
2225         {
2226             switch (rInfo.nTypeArray[i])
2227             {
2228             case NF_SYMBOLTYPE_STAR:
2229                 if( bStarFlag )
2230                 {
2231                     lcl_appendStarFillChar( sOutBuff, rInfo.sStrArray[i]);
2232                 }
2233                 break;
2234             case NF_SYMBOLTYPE_BLANK:
2235                 if (rInfo.sStrArray[i].getLength() >= 2)
2236                     InsertBlanks( sOutBuff, sOutBuff.getLength(), rInfo.sStrArray[i][1] );
2237                 break;
2238             case NF_KEY_GENERAL :   // #77026# "General" is the same as "@"
2239             case NF_SYMBOLTYPE_DEL :
2240                 sOutBuff.append(sString);
2241                 break;
2242             default:
2243                 sOutBuff.append(rInfo.sStrArray[i]);
2244             }
2245         }
2246     }
2247     OutString = sOutBuff.makeStringAndClear();
2248 }
2249 
2250 namespace {
2251 
2252 void lcl_GetOutputStringScientific(double fNumber, sal_uInt16 nCharCount,
2253                                    const SvNumberFormatter& rFormatter, OUString& rOutString)
2254 {
2255     bool bSign = std::signbit(fNumber);
2256 
2257     // 1.000E+015 (one digit and the decimal point, and the two chars +
2258     // nExpDigit for the exponential part, totalling 6 or 7).
2259     double fExp = log10( fabs(fNumber) );
2260     if( fExp < 0.0 )
2261       fExp = 1.0 - fExp;
2262     sal_uInt16 nCharFormat = 6 + (fExp >= 100.0 ? 1 : 0);
2263     sal_uInt16 nPrec = nCharCount > nCharFormat ? nCharCount - nCharFormat : 0;
2264     if (nPrec && bSign)
2265     {
2266         // Make room for the negative sign.
2267         --nPrec;
2268     }
2269     nPrec = ::std::min(nPrec, static_cast<sal_uInt16>(14)); // limit to 14 decimals.
2270 
2271     rOutString = ::rtl::math::doubleToUString(fNumber, rtl_math_StringFormat_E2,
2272                                               nPrec, rFormatter.GetNumDecimalSep()[0], true );
2273 }
2274 
2275 OUString lcl_GetPercentString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2276 {
2277     sal_Int32 i;
2278     OUStringBuffer aPercentString;
2279     for( i = 0; i < nCnt; i++ )
2280     {
2281         if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_PERCENT )
2282         {
2283             aPercentString.append( rInfo.sStrArray[i] );
2284             bool bStringFound = false;
2285             for( i--; i >= 0 && rInfo.nTypeArray[i] == NF_SYMBOLTYPE_STRING ; i-- )
2286             {
2287                 if( !bStringFound )
2288                 {
2289                     bStringFound = true;
2290                     aPercentString.insert( 0, "\"" );
2291                 }
2292                 aPercentString.insert( 0, rInfo.sStrArray[i] );
2293             }
2294             i = nCnt;
2295             if( bStringFound )
2296                 aPercentString.insert( 0, "\"" );
2297         }
2298     }
2299     return aPercentString.makeStringAndClear();
2300 }
2301 
2302 OUString lcl_GetDenominatorString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2303 {
2304     sal_Int32 i;
2305     OUStringBuffer aDenominatorString;
2306     for( i = 0; i < nCnt; i++ )
2307     {
2308         if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRAC )
2309         {
2310             while ( ( ++i < nCnt ) && rInfo.nTypeArray[i] != NF_SYMBOLTYPE_FRAC_FDIV
2311                                    && rInfo.nTypeArray[i] != NF_SYMBOLTYPE_DIGIT );
2312             for( ; i < nCnt; i++ )
2313             {
2314                 if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRAC_FDIV || rInfo.nTypeArray[i] == NF_SYMBOLTYPE_DIGIT )
2315                     aDenominatorString.append( rInfo.sStrArray[i] );
2316                 else
2317                     i = nCnt;
2318             }
2319         }
2320     }
2321     return aDenominatorString.makeStringAndClear();
2322 }
2323 
2324 OUString lcl_GetNumeratorString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2325 {
2326     sal_Int32 i;
2327     OUStringBuffer aNumeratorString;
2328     for( i = 0; i < nCnt; i++ )
2329     {
2330         if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRAC )
2331         {
2332             for( i--; i >= 0 && rInfo.nTypeArray[i] == NF_SYMBOLTYPE_DIGIT ; i-- )
2333             {
2334                 aNumeratorString.insert( 0, rInfo.sStrArray[i] );
2335             }
2336             i = nCnt;
2337         }
2338     }
2339     return aNumeratorString.makeStringAndClear();
2340 }
2341 
2342 OUString lcl_GetFractionIntegerString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2343 {
2344     sal_Int32 i;
2345     OUStringBuffer aIntegerString;
2346     for( i = 0; i < nCnt; i++ )
2347     {
2348         if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRACBLANK )
2349         {
2350             for( i--; i >= 0 && ( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_DIGIT
2351                                || rInfo.nTypeArray[i] == NF_SYMBOLTYPE_THSEP ); i-- )
2352             {
2353                 aIntegerString.insert( 0, rInfo.sStrArray[i] );
2354             }
2355             i = nCnt;
2356         }
2357     }
2358     return aIntegerString.makeStringAndClear();
2359 }
2360 
2361 OUString lcl_GetIntegerFractionDelimiterString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2362 {
2363     sal_uInt16 i;
2364     for( i = 0; i < nCnt; i++ )
2365     {
2366         if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRACBLANK )
2367         {
2368             return rInfo.sStrArray[i];
2369         }
2370     }
2371     return OUString();
2372 }
2373 
2374 }
2375 
2376 OUString SvNumberformat::GetPercentString( sal_uInt16 nNumFor ) const
2377 {
2378     const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
2379     sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
2380     return lcl_GetPercentString( rInfo, nCnt );
2381 }
2382 
2383 OUString SvNumberformat::GetDenominatorString( sal_uInt16 nNumFor ) const
2384 {
2385     const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
2386     sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
2387     return lcl_GetDenominatorString( rInfo, nCnt );
2388 }
2389 
2390 OUString SvNumberformat::GetNumeratorString( sal_uInt16 nNumFor ) const
2391 {
2392     const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
2393     sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
2394     return lcl_GetNumeratorString( rInfo, nCnt );
2395 }
2396 
2397 OUString SvNumberformat::GetIntegerFractionDelimiterString( sal_uInt16 nNumFor ) const
2398 {
2399     const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
2400     sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
2401     return lcl_GetIntegerFractionDelimiterString( rInfo, nCnt );
2402 }
2403 
2404 bool SvNumberformat::GetOutputString(double fNumber, sal_uInt16 nCharCount, OUString& rOutString) const
2405 {
2406     if (eType != SvNumFormatType::NUMBER)
2407     {
2408         return false;
2409     }
2410     double fTestNum = fNumber;
2411     bool bSign = std::signbit(fTestNum);
2412     if (bSign)
2413     {
2414         fTestNum = -fTestNum;
2415     }
2416     if (fTestNum < EXP_LOWER_BOUND)
2417     {
2418         lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString);
2419         return true;
2420     }
2421 
2422     double fExp = log10(fTestNum);
2423     // Values < 1.0 always have one digit before the decimal point.
2424     sal_uInt16 nDigitPre = fExp >= 0.0 ? static_cast<sal_uInt16>(ceil(fExp)) : 1;
2425 
2426     if (nDigitPre > 15)
2427     {
2428         lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString);
2429         return true;
2430     }
2431 
2432     sal_uInt16 nPrec = nCharCount >= nDigitPre ? nCharCount - nDigitPre : 0;
2433     if (nPrec && bSign)
2434     {
2435         // Subtract the negative sign.
2436         --nPrec;
2437     }
2438     if (nPrec)
2439     {
2440         // Subtract the decimal point.
2441         --nPrec;
2442     }
2443     ImpGetOutputStdToPrecision(fNumber, rOutString, nPrec);
2444     if (rOutString.getLength() > nCharCount)
2445     {
2446         // String still wider than desired.  Switch to scientific notation.
2447         lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString);
2448     }
2449     return true;
2450 }
2451 
2452 sal_uInt16 SvNumberformat::GetSubformatIndex (double fNumber ) const
2453 {
2454     sal_uInt16 nIx; // Index of the partial format
2455     double fLimit_1 = fLimit1;
2456     short nCheck = ImpCheckCondition(fNumber, fLimit_1, eOp1);
2457     if (nCheck == -1 || nCheck == 1) // Only 1 String or True
2458     {
2459         nIx = 0;
2460     }
2461     else
2462     {
2463         double fLimit_2 = fLimit2;
2464         nCheck = ImpCheckCondition(fNumber, fLimit_2, eOp2);
2465         if (nCheck == -1 || nCheck == 1)
2466         {
2467             nIx = 1;
2468         }
2469         else
2470         {
2471             nIx = 2;
2472         }
2473     }
2474     return nIx;
2475 }
2476 
2477 bool SvNumberformat::GetOutputString(double fNumber,
2478                                      OUString& OutString,
2479                                      const Color** ppColor)
2480 {
2481     bool bRes = false;
2482     OutString.clear();
2483     *ppColor = nullptr; // No color change
2484     if (eType & SvNumFormatType::LOGICAL && sFormatstring == rScan.GetKeywords()[NF_KEY_BOOLEAN])
2485     {
2486         if (fNumber)
2487         {
2488             OutString = rScan.GetTrueString();
2489         }
2490         else
2491         {
2492             OutString = rScan.GetFalseString();
2493         }
2494         return false;
2495     }
2496     OUStringBuffer sBuff(64);
2497     if (eType & SvNumFormatType::TEXT)
2498     {
2499         ImpGetOutputStandard(fNumber, sBuff);
2500         OutString = sBuff.makeStringAndClear();
2501         return false;
2502     }
2503     bool bHadStandard = false;
2504     if (bStandard) // Individual standard formats
2505     {
2506         if (rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION) // All number format InputLine
2507         {
2508             ImpGetOutputInputLine(fNumber, OutString);
2509             return false;
2510         }
2511         switch (eType)
2512         {
2513         case SvNumFormatType::NUMBER: // Standard number format
2514             if (rScan.GetStandardPrec() == SvNumberFormatter::UNLIMITED_PRECISION)
2515             {
2516                 if (std::signbit(fNumber))
2517                 {
2518                     if (!(fNumber < 0.0))
2519                         fNumber = -fNumber;     // do not display -0.0
2520                 }
2521                 if (fNumber == 0.0)
2522                 {
2523                     OutString = "0";
2524                 }
2525                 else if (fNumber < 1.0 && fNumber > -1.0)
2526                 {
2527                     // Decide whether to display as 0.000000123... or 1.23...e-07
2528                     bool bFix = (fNumber < -EXP_LOWER_BOUND || EXP_LOWER_BOUND < fNumber);
2529                     if (!bFix)
2530                     {
2531                         // Arbitrary, not too many 0s visually, start E2 at 1E-10.
2532                         constexpr sal_Int32 kMaxExp = 9;
2533                         const sal_Int32 nExp = static_cast<sal_Int32>(ceil( -log10( fabs( fNumber))));
2534                         if (nExp <= kMaxExp && rtl::math::approxEqual(
2535                                     rtl::math::round( fNumber, 16), rtl::math::round( fNumber, nExp + 16)))
2536                         {
2537                             // Not too many significant digits or accuracy
2538                             // artefacts, otherwise leave everything to E2
2539                             // format.
2540                             bFix = true;
2541                         }
2542                     }
2543                     if (bFix)
2544                         OutString = ::rtl::math::doubleToUString( fNumber,
2545                                 rtl_math_StringFormat_F,
2546                                 rtl_math_DecimalPlaces_Max,
2547                                 GetFormatter().GetNumDecimalSep()[0], true);
2548                     else
2549                         OutString = ::rtl::math::doubleToUString( fNumber,
2550                                 rtl_math_StringFormat_E2,
2551                                 rtl_math_DecimalPlaces_Max,
2552                                 GetFormatter().GetNumDecimalSep()[0], true);
2553                 }
2554                 else
2555                 {
2556                     OutString = ::rtl::math::doubleToUString( fNumber,
2557                                 rtl_math_StringFormat_Automatic,
2558                                 rtl_math_DecimalPlaces_Max,
2559                                 GetFormatter().GetNumDecimalSep()[0], true);
2560                 }
2561                 return false;
2562             }
2563             ImpGetOutputStandard(fNumber, sBuff);
2564             bHadStandard = true;
2565             break;
2566         case SvNumFormatType::DATE:
2567             bRes |= ImpGetDateOutput(fNumber, 0, sBuff);
2568             bHadStandard = true;
2569             break;
2570         case SvNumFormatType::TIME:
2571             bRes |= ImpGetTimeOutput(fNumber, 0, sBuff);
2572             bHadStandard = true;
2573             break;
2574         case SvNumFormatType::DATETIME:
2575             bRes |= ImpGetDateTimeOutput(fNumber, 0, sBuff);
2576             bHadStandard = true;
2577             break;
2578         default: break;
2579         }
2580     }
2581     if ( !bHadStandard )
2582     {
2583         sal_uInt16 nIx = GetSubformatIndex ( fNumber ); // Index of the partial format
2584         if (fNumber < 0.0 &&
2585                 ((nIx == 0 && IsFirstSubformatRealNegative()) || // 1st, usually positive subformat
2586                  (nIx == 1 && IsSecondSubformatRealNegative()))) // 2nd, usually negative subformat
2587         {
2588             fNumber = -fNumber; // eliminate sign
2589         }
2590         *ppColor = NumFor[nIx].GetColor();
2591         const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2592         const sal_uInt16 nCnt = NumFor[nIx].GetCount();
2593         if (nCnt == 0 && rInfo.eScannedType == SvNumFormatType::EMPTY)
2594         {
2595             return false; // Empty => nothing
2596         }
2597         else if (nCnt == 0) // Else Standard Format
2598         {
2599             ImpGetOutputStandard(fNumber, sBuff);
2600             OutString = sBuff.makeStringAndClear();
2601             return false;
2602         }
2603         switch (rInfo.eScannedType)
2604         {
2605         case SvNumFormatType::TEXT:
2606         case SvNumFormatType::DEFINED:
2607             for (sal_uInt16 i = 0; i < nCnt; i++)
2608             {
2609                 switch (rInfo.nTypeArray[i])
2610                 {
2611                 case NF_SYMBOLTYPE_STAR:
2612                     if( bStarFlag )
2613                     {
2614                         bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
2615                     }
2616                     break;
2617                 case NF_SYMBOLTYPE_BLANK:
2618                     if (rInfo.sStrArray[i].getLength() >= 2)
2619                         InsertBlanks(sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] );
2620                     break;
2621                 case NF_SYMBOLTYPE_STRING:
2622                 case NF_SYMBOLTYPE_CURRENCY:
2623                     sBuff.append(rInfo.sStrArray[i]);
2624                     break;
2625                 case NF_SYMBOLTYPE_THSEP:
2626                     if (rInfo.nThousand == 0)
2627                     {
2628                         sBuff.append(rInfo.sStrArray[i]);
2629                     }
2630                     break;
2631                 default:
2632                     break;
2633                 }
2634             }
2635             break;
2636         case SvNumFormatType::DATE:
2637             bRes |= ImpGetDateOutput(fNumber, nIx, sBuff);
2638             break;
2639         case SvNumFormatType::TIME:
2640             bRes |= ImpGetTimeOutput(fNumber, nIx, sBuff);
2641                 break;
2642         case SvNumFormatType::DATETIME:
2643             bRes |= ImpGetDateTimeOutput(fNumber, nIx, sBuff);
2644             break;
2645         case SvNumFormatType::NUMBER:
2646         case SvNumFormatType::PERCENT:
2647         case SvNumFormatType::CURRENCY:
2648             bRes |= ImpGetNumberOutput(fNumber, nIx, sBuff);
2649             break;
2650         case SvNumFormatType::LOGICAL:
2651             bRes |= ImpGetLogicalOutput(fNumber, nIx, sBuff);
2652             break;
2653         case SvNumFormatType::FRACTION:
2654             bRes |= ImpGetFractionOutput(fNumber, nIx, sBuff);
2655             break;
2656         case SvNumFormatType::SCIENTIFIC:
2657             bRes |= ImpGetScientificOutput(fNumber, nIx, sBuff);
2658             break;
2659         default: break;
2660         }
2661     }
2662     OutString = sBuff.makeStringAndClear();
2663     return bRes;
2664 }
2665 
2666 bool SvNumberformat::ImpGetScientificOutput(double fNumber,
2667                                             sal_uInt16 nIx,
2668                                             OUStringBuffer& sStr)
2669 {
2670     bool bRes = false;
2671     bool bSign = false;
2672 
2673     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2674     const sal_uInt16 nCnt = NumFor[nIx].GetCount();
2675 
2676     if (fNumber < 0)
2677     {
2678         if (nIx == 0) // Not in the ones at the end
2679         {
2680             bSign = true; // Formats
2681         }
2682         fNumber = -fNumber;
2683     }
2684 
2685     sStr = ::rtl::math::doubleToUString( fNumber,
2686                                          rtl_math_StringFormat_E,
2687                                          rInfo.nCntPre + rInfo.nCntPost - 1, '.' );
2688     OUStringBuffer ExpStr;
2689     short nExpSign = 1;
2690     sal_Int32 nExPos = sStr.indexOf('E');
2691     sal_Int32 nDecPos = -1;
2692 
2693     if ( nExPos >= 0 )
2694     {
2695         // split into mantissa and exponent and get rid of "E+" or "E-"
2696         sal_Int32 nExpStart = nExPos + 1;
2697 
2698         switch ( sStr[ nExpStart ] )
2699         {
2700         case '-' :
2701             nExpSign = -1;
2702             [[fallthrough]];
2703         case '+' :
2704             ++nExpStart;
2705             break;
2706         }
2707         ExpStr = sStr.subView( nExpStart );    // part following the "E+"
2708         sStr.truncate( nExPos );
2709 
2710         if ( rInfo.nCntPre != 1 ) // rescale Exp
2711         {
2712             sal_Int32 nExp = OUString::unacquired(ExpStr).toInt32() * nExpSign;
2713             sal_Int32 nRescale = (rInfo.nCntPre != 0) ? nExp % static_cast<sal_Int32>(rInfo.nCntPre) : -1;
2714             if( nRescale < 0 && rInfo.nCntPre != 0 )
2715                 nRescale += static_cast<sal_Int32>(rInfo.nCntPre);
2716             nExp -= nRescale;
2717             if ( nExp < 0 )
2718             {
2719                 nExpSign = -1;
2720                 nExp = -nExp;
2721             }
2722             else
2723             {
2724                 nExpSign = 1;
2725             }
2726             ExpStr = OUString::number( nExp );
2727             const sal_Unicode cFirstDigit = sStr[0];
2728             // rescale mantissa
2729             sStr = ::rtl::math::doubleToUString( fNumber,
2730                                          rtl_math_StringFormat_E,
2731                                          nRescale + rInfo.nCntPost, '.' );
2732 
2733             // sStr now may contain a rounded-up value shifted into the next
2734             // magnitude, for example 1.000E+02 (4 digits) for fNumber 99.995
2735             // (9.9995E+02 rounded to 3 decimals) but we want the final result
2736             // to be 100.00E+00 (5 digits), so for the following fill routines
2737             // below to work correctly append a zero decimal.
2738             /* TODO: this is awkward, could an engineering notation mode be
2739              * introduced to rtl_math_doubleToUString()? */
2740             sStr.truncate( sStr.indexOf('E') );
2741             if (sStr[0] == '1' && cFirstDigit != '1')
2742                 sStr.append('0');
2743         }
2744 
2745         // cut any decimal delimiter
2746         sal_Int32 index = 0;
2747 
2748         while((index = sStr.indexOf('.', index)) >= 0)
2749         {
2750             if (nDecPos < 0)
2751                 nDecPos = index;
2752             sStr.remove(index, 1);
2753         }
2754     }
2755 
2756     sal_uInt16 j = nCnt-1;  // Last symbol
2757     sal_Int32 k;  // Position in ExpStr
2758     sal_Int32 nZeros = 0; // Erase leading zeros
2759 
2760     bRes |= ImpNumberFill(ExpStr, fNumber, k, j, nIx, NF_SYMBOLTYPE_EXP);
2761 
2762     while (nZeros < k && ExpStr[nZeros] == '0')
2763     {
2764         ++nZeros;
2765     }
2766     if (nZeros)
2767     {
2768         ExpStr.remove( 0, nZeros);
2769     }
2770 
2771     bool bCont = true;
2772 
2773     if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_EXP)
2774     {
2775         const OUString& rStr = rInfo.sStrArray[j];
2776         if (nExpSign == -1)
2777         {
2778             ExpStr.insert(0, '-');
2779         }
2780         else if (rStr.getLength() > 1 && rStr[1] == '+')
2781         {
2782             ExpStr.insert(0, '+');
2783         }
2784         ExpStr.insert(0, rStr[0]);
2785         if ( j )
2786         {
2787             j--;
2788         }
2789         else
2790         {
2791             bCont = false;
2792         }
2793     }
2794     // Continue main number:
2795     if ( !bCont )
2796     {
2797         sStr.truncate();
2798     }
2799     else
2800     {
2801         bRes |= ImpDecimalFill(sStr, fNumber, nDecPos, j, nIx, false);
2802     }
2803 
2804     if (bSign)
2805     {
2806         sStr.insert(0, '-');
2807     }
2808     sStr.append(ExpStr);
2809 
2810     return bRes;
2811 }
2812 
2813 double SvNumberformat::GetRoundFractionValue ( double fNumber ) const
2814 {
2815     sal_uInt16 nIx = GetSubformatIndex ( fNumber );
2816     double fIntPart = 0.0;           // integer part of fraction
2817     sal_Int64 nFrac = 0, nDiv = 1;  // numerator and denominator
2818     double fSign = (fNumber < 0.0) ? -1.0 : 1.0;
2819     // fNumber is modified in ImpGetFractionElements to absolute fractional part
2820     ImpGetFractionElements ( fNumber, nIx, fIntPart, nFrac, nDiv );
2821     if ( nDiv > 0 )
2822         return fSign * ( fIntPart + static_cast<double>(nFrac) / static_cast<double>(nDiv) );
2823     else
2824         return fSign * fIntPart;
2825 }
2826 
2827 void SvNumberformat::ImpGetFractionElements ( double& fNumber, sal_uInt16 nIx,
2828                                               double& fIntPart, sal_Int64& nFrac, sal_Int64& nDiv ) const
2829 {
2830     if ( fNumber < 0.0 )
2831         fNumber = -fNumber;
2832     fIntPart = floor(fNumber); // Integral part
2833     fNumber -= fIntPart;         // Fractional part
2834     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2835     nDiv = lcl_GetDenominatorString( rInfo, NumFor[nIx].GetCount() ).toInt32();
2836     if( nDiv > 0 )
2837     {   // Forced Denominator
2838         nFrac = static_cast<sal_Int64>(floor ( fNumber * nDiv ));
2839         double fFracNew = static_cast<double>(nFrac) / static_cast<double>(nDiv);
2840         double fFracNew1 = static_cast<double>(nFrac + 1) / static_cast<double>(nDiv);
2841         double fDiff = fNumber - fFracNew;
2842         if( fDiff > ( fFracNew1 - fNumber ) )
2843         {
2844             nFrac++;
2845         }
2846     }
2847     else // Calculated Denominator
2848     {
2849         nDiv = 1;
2850         sal_Int64 nBasis = static_cast<sal_Int64>(floor( pow(10.0,rInfo.nCntExp))) - 1; // 9, 99, 999 ,...
2851         sal_Int64 nFracPrev = 1, nDivPrev = 0, nFracNext, nDivNext, nPartialDenom;
2852         double fRemainder = fNumber;
2853 
2854         // Use continued fraction representation of fNumber
2855         // See https://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximations
2856         while ( fRemainder > 0.0 )
2857         {
2858             double fTemp = 1.0 / fRemainder;             // 64bits precision required when fRemainder is very weak
2859             nPartialDenom = static_cast<sal_Int64>(floor(fTemp));   // due to floating point notation with double precision
2860             fRemainder = fTemp - static_cast<double>(nPartialDenom);
2861             nDivNext = nPartialDenom * nDiv + nDivPrev;
2862             if ( nDivNext <= nBasis )  // continue loop
2863             {
2864                 nFracNext = nPartialDenom * nFrac + nFracPrev;
2865                 nFracPrev = nFrac;
2866                 nFrac = nFracNext;
2867                 nDivPrev = nDiv;
2868                 nDiv = nDivNext;
2869             }
2870             else // calculate collateral fraction and exit
2871             {
2872                 sal_Int64 nCollat = (nBasis - nDivPrev) / nDiv;
2873                 if ( 2 * nCollat >= nPartialDenom )
2874                 {
2875                     sal_Int64 nFracTest = nCollat * nFrac + nFracPrev;
2876                     sal_Int64 nDivTest  = nCollat * nDiv  + nDivPrev;
2877                     double fSign = (static_cast<double>(nFrac) > fNumber * static_cast<double>(nDiv))?1.0:-1.0;
2878                     if ( fSign * ( double(nFrac * nDivTest + nDiv * nFracTest) - 2.0 * double(nDiv * nDivTest) * fNumber ) > 0.0 )
2879                     {
2880                         nFrac = nFracTest;
2881                         nDiv  = nDivTest;
2882                     }
2883                 }
2884                 fRemainder = 0.0; // exit while loop
2885             }
2886         }
2887     }
2888     if (nFrac >= nDiv)
2889     {
2890         ++fIntPart;
2891         nFrac = nDiv = 0;
2892     }
2893 }
2894 
2895 bool SvNumberformat::ImpGetFractionOutput(double fNumber,
2896                                           sal_uInt16 nIx,
2897                                           OUStringBuffer& sBuff)
2898 {
2899     bool bRes = false;
2900     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2901     const sal_uInt16 nCnt = NumFor[nIx].GetCount();
2902     OUStringBuffer sStr, sFrac, sDiv; // Strings, value for Integral part Numerator and denominator
2903     bool bSign = ( (fNumber < 0) && (nIx == 0) ); // sign Not in the ones at the end
2904     const OUString sIntegerFormat = lcl_GetFractionIntegerString(rInfo, nCnt);
2905     const OUString sNumeratorFormat = lcl_GetNumeratorString(rInfo, nCnt);
2906     const OUString sDenominatorFormat = lcl_GetDenominatorString(rInfo, nCnt);
2907 
2908     sal_Int64 nFrac = 0, nDiv = 1;
2909     double fNum = floor(fNumber); // Integral part
2910 
2911     if (fNum > D_MAX_U_INT32 || rInfo.nCntExp > 9) // Too large
2912     {
2913         sBuff = ImpSvNumberformatScan::sErrStr;
2914         return false;
2915     }
2916     if (rInfo.nCntExp == 0)
2917     {
2918         SAL_WARN( "svl.numbers", "SvNumberformat:: Fraction, nCntExp == 0");
2919         sBuff.truncate();
2920         return false;
2921     }
2922 
2923     ImpGetFractionElements( fNumber, nIx, fNum, nFrac, nDiv);
2924 
2925     if (rInfo.nCntPre == 0) // Improper fraction
2926     {
2927         double fNum1 = fNum * static_cast<double>(nDiv) + static_cast<double>(nFrac);
2928 
2929         if (fNum1 > D_MAX_INTEGER)
2930         {
2931             sBuff = ImpSvNumberformatScan::sErrStr;
2932             return false;
2933         }
2934         nFrac = static_cast<sal_Int64>(floor(fNum1));
2935     }
2936     else if (fNum == 0.0 && nFrac != 0)
2937     {
2938     }
2939     else
2940     {
2941         char aBuf[100];
2942         o3tl::sprintf( aBuf, "%.f", fNum ); // simple rounded integer
2943         sStr.appendAscii( aBuf );
2944         impTransliterate(sStr, NumFor[nIx].GetNatNum());
2945     }
2946     bool bHideFraction = (rInfo.nCntPre > 0 && nFrac == 0
2947                         && (sNumeratorFormat.indexOf('0') < 0)
2948                         && (sDenominatorFormat.indexOf('0') < 0
2949                         || sDenominatorFormat.toInt32() > 0) );
2950     if ( bHideFraction )
2951     {
2952         sDiv.truncate();
2953     }
2954     else  // if there are some '0' in format, force display of fraction
2955     {
2956         sFrac = ImpIntToString( nIx, nFrac );
2957         sDiv = ImpIntToString( nIx, nDiv );
2958     }
2959 
2960     sal_uInt16 j = nCnt-1; // Last symbol -> backwards
2961     sal_Int32 k;           // Denominator
2962 
2963     bRes |= ImpNumberFill(sDiv, fNumber, k, j, nIx, NF_SYMBOLTYPE_FRAC, true);
2964 
2965     bool bCont = true;
2966     if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_FRAC)
2967     {
2968         if ( bHideFraction )
2969         {   // do not insert blank for fraction if there is no '?'
2970             if ( sNumeratorFormat.indexOf('?') >= 0
2971               || sDenominatorFormat.indexOf('?') >= 0 )
2972                 sDiv.insert(0, ' ');
2973         }
2974         else
2975         {
2976             sDiv.insert(0, rInfo.sStrArray[j][0]);
2977         }
2978         if ( j )
2979         {
2980             j--;
2981         }
2982         else
2983         {
2984             bCont = false;
2985         }
2986     }
2987     // Further numerators:
2988     if ( !bCont )
2989     {
2990         sFrac.truncate();
2991     }
2992     else
2993     {
2994         bRes |= ImpNumberFill(sFrac, fNumber, k, j, nIx, NF_SYMBOLTYPE_FRACBLANK);
2995         bCont = false;  // there is no integer part?
2996         if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_FRACBLANK)
2997         {
2998             if ( j )
2999             {
3000                 if ( bHideFraction )
3001                 {   // '?' in any format force display of blank as delimiter
3002                     if ( sIntegerFormat.indexOf('?') >= 0
3003                       || sNumeratorFormat.indexOf('?') >= 0
3004                       || sDenominatorFormat.indexOf('?') >= 0 )
3005                     {
3006                         for (sal_Int32 i = 0; i < rInfo.sStrArray[j].getLength(); i++)
3007                             sFrac.insert(0, ' ');
3008                     }
3009                 }
3010                 else
3011                 {
3012                     if ( fNum != 0.0 || sIntegerFormat.indexOf('0') >= 0 )
3013                         sFrac.insert(0, rInfo.sStrArray[j]); // insert Blank string only if there are both integer and fraction
3014                     else
3015                     {
3016                         if ( sIntegerFormat.indexOf('?') >= 0
3017                           || sNumeratorFormat.indexOf('?') >= 0 )
3018                         {
3019                             for (sal_Int32 i = 0; i < rInfo.sStrArray[j].getLength(); i++)
3020                                 sFrac.insert(0, ' ');
3021                         }
3022                     }
3023                 }
3024                 j--;
3025                 bCont = true;  // Yes, there is an integer
3026             }
3027             else
3028                 sFrac.insert(0, rInfo.sStrArray[j]);
3029         }
3030     }
3031     // Continue integer part
3032     if ( !bCont )
3033     {
3034         sStr.truncate();
3035     }
3036     else
3037     {
3038         k = sStr.getLength(); // After last figure
3039         bRes |= ImpNumberFillWithThousands(sStr, fNumber, k, j, nIx,
3040                                            rInfo.nCntPre);
3041     }
3042     if (bSign && (nFrac != 0 || fNum != 0.0))
3043     {
3044         sBuff.insert(0, '-'); // Not -0
3045     }
3046     sBuff.append(sStr);
3047     sBuff.append(sFrac);
3048     sBuff.append(sDiv);
3049     return bRes;
3050 }
3051 
3052 sal_uInt16 SvNumberformat::ImpGetFractionOfSecondString( OUStringBuffer& rBuf, double fFractionOfSecond,
3053         int nFractionDecimals, bool bAddOneRoundingDecimal, sal_uInt16 nIx, sal_uInt16 nMinimumInputLineDecimals )
3054 {
3055     if (!nFractionDecimals)
3056         return 0;
3057 
3058     // nFractionDecimals+1 to not round up what Time::GetClock() carefully
3059     // truncated.
3060     rBuf.append( rtl::math::doubleToUString( fFractionOfSecond, rtl_math_StringFormat_F,
3061                 (bAddOneRoundingDecimal ? nFractionDecimals + 1 : nFractionDecimals), '.'));
3062     rBuf.stripStart('0');
3063     rBuf.stripStart('.');
3064     if (bAddOneRoundingDecimal && rBuf.getLength() > nFractionDecimals)
3065         rBuf.truncate( nFractionDecimals); // the digit appended because of nFractionDecimals+1
3066     if (nMinimumInputLineDecimals)
3067     {
3068         rBuf.stripEnd('0');
3069         for (sal_Int32 index = rBuf.getLength(); index < nMinimumInputLineDecimals; ++index)
3070         {
3071             rBuf.append('0');
3072         }
3073         impTransliterate(rBuf, NumFor[nIx].GetNatNum());
3074         nFractionDecimals = rBuf.getLength();
3075     }
3076     else
3077     {
3078         impTransliterate(rBuf, NumFor[nIx].GetNatNum());
3079     }
3080     return static_cast<sal_uInt16>(nFractionDecimals);
3081 }
3082 
3083 bool SvNumberformat::ImpGetTimeOutput(double fNumber,
3084                                       sal_uInt16 nIx,
3085                                       OUStringBuffer& sBuff)
3086 {
3087     using namespace ::com::sun::star::i18n;
3088     bool bCalendarSet = false;
3089     const double fNumberOrig = fNumber;
3090     bool bRes = false;
3091     bool bSign = false;
3092     if (fNumber < 0.0)
3093     {
3094         fNumber = -fNumber;
3095         if (nIx == 0)
3096         {
3097             bSign = true;
3098         }
3099     }
3100     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
3101     bool bInputLine;
3102     sal_Int32 nCntPost;
3103     if ( rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION &&
3104          0 < rInfo.nCntPost && rInfo.nCntPost < kTimeSignificantRound )
3105     {
3106         bInputLine = true;
3107         nCntPost = kTimeSignificantRound;
3108     }
3109     else
3110     {
3111         bInputLine = false;
3112         nCntPost = rInfo.nCntPost;
3113     }
3114 
3115     OUStringBuffer sSecStr;
3116     sal_Int32 nSecPos = 0; // For figure by figure processing
3117     sal_uInt32 nHour, nMin, nSec;
3118     if (!rInfo.bThousand) // No [] format
3119     {
3120         sal_uInt16 nCHour, nCMinute, nCSecond;
3121         double fFractionOfSecond;
3122         tools::Time::GetClock( fNumberOrig, nCHour, nCMinute, nCSecond, fFractionOfSecond, nCntPost);
3123         nHour = nCHour;
3124         nMin = nCMinute;
3125         nSec = nCSecond;
3126         nCntPost = ImpGetFractionOfSecondString( sSecStr, fFractionOfSecond, nCntPost, true, nIx,
3127                 (bInputLine ? rInfo.nCntPost : 0));
3128     }
3129     else
3130     {
3131         const double fTime = rtl::math::round( fNumber * 86400.0, int(nCntPost));
3132         if (bSign && fTime == 0.0)
3133         {
3134             bSign = false; // Not -00:00:00
3135         }
3136         if (fTime > D_MAX_U_INT32)
3137         {
3138             sBuff = ImpSvNumberformatScan::sErrStr;
3139             return false;
3140         }
3141         sal_uInt32 nSeconds = static_cast<sal_uInt32>(fTime);
3142 
3143         nCntPost = ImpGetFractionOfSecondString( sSecStr, fTime - nSeconds, nCntPost, false, nIx,
3144                 (bInputLine ? rInfo.nCntPost : 0));
3145 
3146         if (rInfo.nThousand == 3) // [ss]
3147         {
3148             nHour = 0;
3149             nMin = 0;
3150             nSec = nSeconds;
3151         }
3152         else if (rInfo.nThousand == 2) // [mm]:ss
3153         {
3154             nHour = 0;
3155             nMin = nSeconds / 60;
3156             nSec = nSeconds % 60;
3157         }
3158         else if (rInfo.nThousand == 1) // [hh]:mm:ss
3159         {
3160             nHour = nSeconds / 3600;
3161             nMin = (nSeconds%3600) / 60;
3162             nSec = nSeconds%60;
3163         }
3164         else
3165         {
3166             // TODO  What should these be set to?
3167             nHour = 0;
3168             nMin  = 0;
3169             nSec  = 0;
3170         }
3171     }
3172 
3173     sal_Unicode cAmPm = ' '; // a or p
3174     if (rInfo.nCntExp) // AM/PM
3175     {
3176         if (nHour == 0)
3177         {
3178             nHour = 12;
3179             cAmPm = 'a';
3180         }
3181         else if (nHour < 12)
3182         {
3183             cAmPm = 'a';
3184         }
3185         else
3186         {
3187             cAmPm = 'p';
3188             if (nHour > 12)
3189             {
3190                 nHour -= 12;
3191             }
3192         }
3193     }
3194     const sal_uInt16 nCnt = NumFor[nIx].GetCount();
3195     for (sal_uInt16 i = 0; i < nCnt; i++)
3196     {
3197         sal_Int32 nLen;
3198         switch (rInfo.nTypeArray[i])
3199         {
3200         case NF_SYMBOLTYPE_STAR:
3201             if( bStarFlag )
3202             {
3203                 bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
3204             }
3205             break;
3206         case NF_SYMBOLTYPE_BLANK:
3207             if (rInfo.sStrArray[i].getLength() >= 2)
3208                 InsertBlanks(sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] );
3209             break;
3210         case NF_SYMBOLTYPE_STRING:
3211         case NF_SYMBOLTYPE_CURRENCY:
3212         case NF_SYMBOLTYPE_DATESEP:
3213         case NF_SYMBOLTYPE_TIMESEP:
3214         case NF_SYMBOLTYPE_TIME100SECSEP:
3215             sBuff.append(rInfo.sStrArray[i]);
3216             break;
3217         case NF_SYMBOLTYPE_DIGIT:
3218             nLen = ( bInputLine && i > 0 &&
3219                      (rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING ||
3220                       rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_TIME100SECSEP) ?
3221                      nCntPost : rInfo.sStrArray[i].getLength() );
3222             for (sal_Int32 j = 0; j < nLen && nSecPos < nCntPost && nSecPos < sSecStr.getLength(); ++j)
3223             {
3224                 sBuff.append(sSecStr[nSecPos]);
3225                 nSecPos++;
3226             }
3227             break;
3228         case NF_KEY_AMPM:               // AM/PM
3229             if ( !bCalendarSet )
3230             {
3231                 double fDiff = DateTime(rScan.GetNullDate()) - GetCal().getEpochStart();
3232                 fDiff += fNumberOrig;
3233                 GetCal().setLocalDateTime( fDiff );
3234                 bCalendarSet = true;
3235             }
3236             if (cAmPm == 'a')
3237             {
3238                 sBuff.append(GetCal().getDisplayName(
3239                                  CalendarDisplayIndex::AM_PM, AmPmValue::AM, 0 ));
3240             }
3241             else
3242             {
3243                 sBuff.append(GetCal().getDisplayName(
3244                                  CalendarDisplayIndex::AM_PM, AmPmValue::PM, 0 ));
3245             }
3246             break;
3247         case NF_KEY_AP:                 // A/P
3248             if (cAmPm == 'a')
3249             {
3250                 sBuff.append('a');
3251             }
3252             else
3253             {
3254                 sBuff.append('p');
3255             }
3256             break;
3257         case NF_KEY_MI:                 // M
3258             sBuff.append(ImpIntToString( nIx, nMin ));
3259             break;
3260         case NF_KEY_MMI:                // MM
3261             sBuff.append(ImpIntToString( nIx, nMin, 2 ));
3262             break;
3263         case NF_KEY_H:                  // H
3264             sBuff.append(ImpIntToString( nIx, nHour ));
3265             break;
3266         case NF_KEY_HH:                 // HH
3267             sBuff.append(ImpIntToString( nIx, nHour, 2 ));
3268             break;
3269         case NF_KEY_S:                  // S
3270             sBuff.append(ImpIntToString( nIx, nSec ));
3271             break;
3272         case NF_KEY_SS:                 // SS
3273             sBuff.append(ImpIntToString( nIx, nSec, 2 ));
3274             break;
3275         default:
3276             break;
3277         }
3278     }
3279     if (bSign && rInfo.bThousand)
3280     {
3281         sBuff.insert(0, '-');
3282     }
3283     return bRes;
3284 }
3285 
3286 
3287 /** If a day of month occurs within the format, the month name is in possessive
3288     genitive case if the day follows the month, and partitive case if the day
3289     precedes the month. If there is no day of month the nominative case (noun)
3290     is returned. Also if the month is immediately preceded or followed by a
3291     literal string other than space and not followed by a comma, the nominative
3292     name is used, this prevents duplicated casing for MMMM\t\a and such in
3293     documents imported from (e.g. Finnish) Excel or older LibO/OOo releases.
3294  */
3295 
3296 // IDEA: instead of eCodeType pass the index to nTypeArray and restrict
3297 // inspection of month name around that one, that would enable different month
3298 // cases in one format. Though probably the most rare use case ever...
3299 
3300 sal_Int32 SvNumberformat::ImpUseMonthCase( int & io_nState, const ImpSvNumFor& rNumFor, NfKeywordIndex eCodeType )
3301 {
3302     using namespace ::com::sun::star::i18n;
3303     if (!io_nState)
3304     {
3305         bool bMonthSeen = false;
3306         bool bDaySeen = false;
3307         const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3308         const sal_uInt16 nCount = rNumFor.GetCount();
3309         for (sal_uInt16 i = 0; i < nCount && io_nState == 0; ++i)
3310         {
3311             sal_Int32 nLen;
3312             switch (rInfo.nTypeArray[i])
3313             {
3314             case NF_KEY_D :
3315             case NF_KEY_DD :
3316                 if (bMonthSeen)
3317                 {
3318                     io_nState = 2;
3319                 }
3320                 else
3321                 {
3322                     bDaySeen = true;
3323                 }
3324                 break;
3325             case NF_KEY_MMM:
3326             case NF_KEY_MMMM:
3327             case NF_KEY_MMMMM:
3328                 if ((i < nCount-1 &&
3329                      rInfo.nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
3330                      // Literal following, not empty, space nor comma.
3331                      !rInfo.sStrArray[i+1].isEmpty() &&
3332                      rInfo.sStrArray[i+1][0] != ' ' && rInfo.sStrArray[i+1][0] != ',') ||
3333                     (i > 0 && rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING &&
3334                      ((nLen = rInfo.sStrArray[i-1].getLength()) > 0) &&
3335                      // Literal preceding, not space.
3336                      rInfo.sStrArray[i-1][nLen-1] != ' '))
3337                 {
3338                     io_nState = 1;
3339                 }
3340                 else if (bDaySeen)
3341                 {
3342                     io_nState = 3;
3343                 }
3344                 else
3345                 {
3346                     bMonthSeen = true;
3347                 }
3348                 break;
3349             }
3350         }
3351         if (io_nState == 0)
3352         {
3353             io_nState = 1; // No day of month
3354         }
3355     }
3356     switch (io_nState)
3357     {
3358     case 1:
3359         // No day of month or forced nominative
3360         switch (eCodeType)
3361         {
3362         case NF_KEY_MMM:
3363             return CalendarDisplayCode::SHORT_MONTH_NAME;
3364         case NF_KEY_MMMM:
3365             return CalendarDisplayCode::LONG_MONTH_NAME;
3366         case NF_KEY_MMMMM:
3367             return CalendarDisplayCode::NARROW_MONTH_NAME;
3368         default:
3369             ;   // nothing
3370         }
3371         break;
3372     case 2:
3373         // Day of month follows month (the month's 17th)
3374         switch (eCodeType)
3375         {
3376         case NF_KEY_MMM:
3377             return CalendarDisplayCode::SHORT_GENITIVE_MONTH_NAME;
3378         case NF_KEY_MMMM:
3379             return CalendarDisplayCode::LONG_GENITIVE_MONTH_NAME;
3380         case NF_KEY_MMMMM:
3381             return CalendarDisplayCode::NARROW_GENITIVE_MONTH_NAME;
3382         default:
3383             ;   // Nothing
3384         }
3385         break;
3386     case 3:
3387         // Day of month precedes month (17 of month)
3388         switch (eCodeType)
3389         {
3390         case NF_KEY_MMM:
3391             return CalendarDisplayCode::SHORT_PARTITIVE_MONTH_NAME;
3392         case NF_KEY_MMMM:
3393             return CalendarDisplayCode::LONG_PARTITIVE_MONTH_NAME;
3394         case NF_KEY_MMMMM:
3395             return CalendarDisplayCode::NARROW_PARTITIVE_MONTH_NAME;
3396         default:
3397             ;   // nothing
3398         }
3399         break;
3400     }
3401     SAL_WARN( "svl.numbers", "ImpUseMonthCase: unhandled keyword index eCodeType");
3402     return CalendarDisplayCode::LONG_MONTH_NAME;
3403 }
3404 
3405 
3406 bool SvNumberformat::ImpIsOtherCalendar( const ImpSvNumFor& rNumFor ) const
3407 {
3408     if ( GetCal().getUniqueID() != GREGORIAN )
3409     {
3410         return false;
3411     }
3412     const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3413     const sal_uInt16 nCnt = rNumFor.GetCount();
3414     sal_uInt16 i;
3415     for ( i = 0; i < nCnt; i++ )
3416     {
3417         switch ( rInfo.nTypeArray[i] )
3418         {
3419         case NF_SYMBOLTYPE_CALENDAR :
3420             return false;
3421         case NF_KEY_EC :
3422         case NF_KEY_EEC :
3423         case NF_KEY_R :
3424         case NF_KEY_RR :
3425         case NF_KEY_AAA :
3426         case NF_KEY_AAAA :
3427         case NF_KEY_G :
3428         case NF_KEY_GG :
3429         case NF_KEY_GGG :
3430             return true;
3431         }
3432     }
3433     return false;
3434 }
3435 
3436 void SvNumberformat::SwitchToOtherCalendar( OUString& rOrgCalendar,
3437                                             double& fOrgDateTime ) const
3438 {
3439     CalendarWrapper& rCal = GetCal();
3440     if ( rCal.getUniqueID() != GREGORIAN )
3441         return;
3442 
3443     using namespace ::com::sun::star::i18n;
3444     const css::uno::Sequence< OUString > xCals = rCal.getAllCalendars(
3445             rLoc().getLanguageTag().getLocale() );
3446     sal_Int32 nCnt = xCals.getLength();
3447     if ( nCnt <= 1 )
3448         return;
3449 
3450     auto pCal = std::find_if(xCals.begin(), xCals.end(),
3451         [](const OUString& rCalName) { return rCalName != GREGORIAN; });
3452     if (pCal == xCals.end())
3453         return;
3454 
3455     if ( !rOrgCalendar.getLength() )
3456     {
3457         rOrgCalendar = rCal.getUniqueID();
3458         fOrgDateTime = rCal.getDateTime();
3459     }
3460     rCal.loadCalendar( *pCal, rLoc().getLanguageTag().getLocale() );
3461     rCal.setDateTime( fOrgDateTime );
3462 }
3463 
3464 void SvNumberformat::SwitchToGregorianCalendar( std::u16string_view rOrgCalendar,
3465                                                 double fOrgDateTime ) const
3466 {
3467     CalendarWrapper& rCal = GetCal();
3468     if ( rOrgCalendar.size() && rCal.getUniqueID() != GREGORIAN )
3469     {
3470         rCal.loadCalendar( GREGORIAN, rLoc().getLanguageTag().getLocale() );
3471         rCal.setDateTime( fOrgDateTime );
3472     }
3473 }
3474 
3475 bool SvNumberformat::ImpFallBackToGregorianCalendar( OUString& rOrgCalendar, double& fOrgDateTime )
3476 {
3477     using namespace ::com::sun::star::i18n;
3478     CalendarWrapper& rCal = GetCal();
3479     if ( rCal.getUniqueID() != GREGORIAN )
3480     {
3481         sal_Int16 nVal = rCal.getValue( CalendarFieldIndex::ERA );
3482         if ( nVal == 0 && rCal.getLoadedCalendar().Eras[0].ID == "Dummy" )
3483         {
3484             if ( !rOrgCalendar.getLength() )
3485             {
3486                 rOrgCalendar = rCal.getUniqueID();
3487                 fOrgDateTime = rCal.getDateTime();
3488             }
3489             else if ( rOrgCalendar == GREGORIAN )
3490             {
3491                 rOrgCalendar.clear();
3492             }
3493             rCal.loadCalendar( GREGORIAN, rLoc().getLanguageTag().getLocale() );
3494             rCal.setDateTime( fOrgDateTime );
3495             return true;
3496         }
3497     }
3498     return false;
3499 }
3500 
3501 
3502 #ifdef THE_FUTURE
3503 /* XXX NOTE: even if the ImpSwitchToSpecifiedCalendar method is currently
3504  * unused please don't remove it, it would be needed by
3505  * SwitchToSpecifiedCalendar(), see comment in
3506  * ImpSvNumberInputScan::GetDateRef() */
3507 
3508 bool SvNumberformat::ImpSwitchToSpecifiedCalendar( OUString& rOrgCalendar,
3509                                                    double& fOrgDateTime,
3510                                                    const ImpSvNumFor& rNumFor ) const
3511 {
3512     const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3513     const sal_uInt16 nCnt = rNumFor.GetCount();
3514     for ( sal_uInt16 i = 0; i < nCnt; i++ )
3515     {
3516         if ( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_CALENDAR )
3517         {
3518             CalendarWrapper& rCal = GetCal();
3519             if ( !rOrgCalendar.getLength() )
3520             {
3521                 rOrgCalendar = rCal.getUniqueID();
3522                 fOrgDateTime = rCal.getDateTime();
3523             }
3524             rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLocale() );
3525             rCal.setDateTime( fOrgDateTime );
3526             return true;
3527         }
3528     }
3529     return false;
3530 }
3531 #endif
3532 
3533 // static
3534 void SvNumberformat::ImpAppendEraG( OUStringBuffer& OutString,
3535                                     const CalendarWrapper& rCal,
3536                                     sal_Int16 nNatNum )
3537 {
3538     using namespace ::com::sun::star::i18n;
3539     if ( rCal.getUniqueID() == "gengou" )
3540     {
3541         sal_Unicode cEra;
3542         sal_Int16 nVal = rCal.getValue( CalendarFieldIndex::ERA );
3543         switch ( nVal )
3544         {
3545         case 1:
3546             cEra = 'M';
3547             break;
3548         case 2:
3549             cEra = 'T';
3550             break;
3551         case 3:
3552             cEra = 'S';
3553             break;
3554         case 4:
3555             cEra = 'H';
3556             break;
3557         case 5:
3558             cEra = 'R';
3559             break;
3560         default:
3561             cEra = '?';
3562             break;
3563         }
3564         OutString.append(cEra);
3565     }
3566     else
3567     {
3568         OutString.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
3569     }
3570 }
3571 
3572 bool SvNumberformat::ImpIsIso8601( const ImpSvNumFor& rNumFor ) const
3573 {
3574     bool bIsIso = false;
3575     if (eType & SvNumFormatType::DATE)
3576     {
3577         enum State
3578         {
3579             eNone,
3580             eAtYear,
3581             eAtSep1,
3582             eAtMonth,
3583             eAtSep2,
3584             eNotIso
3585         };
3586         State eState = eNone;
3587         auto & rTypeArray = rNumFor.Info().nTypeArray;
3588         sal_uInt16 nCnt = rNumFor.GetCount();
3589         for (sal_uInt16 i=0; i < nCnt && !bIsIso && eState != eNotIso; ++i)
3590         {
3591             switch ( rTypeArray[i] )
3592             {
3593             case NF_KEY_YY:     // two digits not strictly ISO 8601
3594             case NF_KEY_YYYY:
3595                 if (eState != eNone)
3596                 {
3597                     eState = eNotIso;
3598                 }
3599                 else
3600                 {
3601                     eState = eAtYear;
3602                 }
3603                 break;
3604             case NF_KEY_M:      // single digit not strictly ISO 8601
3605             case NF_KEY_MM:
3606                 if (eState != eAtSep1)
3607                 {
3608                     eState = eNotIso;
3609                 }
3610                 else
3611                 {
3612                     eState = eAtMonth;
3613                 }
3614                 break;
3615             case NF_KEY_D:      // single digit not strictly ISO 8601
3616             case NF_KEY_DD:
3617                 if (eState != eAtSep2)
3618                 {
3619                     eState = eNotIso;
3620                 }
3621                 else
3622                 {
3623                     bIsIso = true;
3624                 }
3625                 break;
3626             case NF_SYMBOLTYPE_STRING:
3627             case NF_SYMBOLTYPE_DATESEP:
3628                 if (rNumFor.Info().sStrArray[i] == "-")
3629                 {
3630                     if (eState == eAtYear)
3631                     {
3632                         eState = eAtSep1;
3633                     }
3634                     else if (eState == eAtMonth)
3635                     {
3636                         eState = eAtSep2;
3637                     }
3638                     else
3639                     {
3640                         eState = eNotIso;
3641                     }
3642                 }
3643                 else
3644                 {
3645                     eState = eNotIso;
3646                 }
3647                 break;
3648             default:
3649                 eState = eNotIso;
3650             }
3651         }
3652     }
3653     else
3654     {
3655        SAL_WARN( "svl.numbers", "SvNumberformat::ImpIsIso8601: no date" );
3656     }
3657     return bIsIso;
3658 }
3659 
3660 static bool lcl_hasEra( const ImpSvNumFor& rNumFor )
3661 {
3662     const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3663     const sal_uInt16 nCnt = rNumFor.GetCount();
3664     for ( sal_uInt16 i = 0; i < nCnt; i++ )
3665     {
3666         switch ( rInfo.nTypeArray[i] )
3667         {
3668             case NF_KEY_RR :
3669             case NF_KEY_G :
3670             case NF_KEY_GG :
3671             case NF_KEY_GGG :
3672                 return true;
3673         }
3674     }
3675     return false;
3676 }
3677 
3678 static bool lcl_isSignedYear( const CalendarWrapper& rCal, const ImpSvNumFor& rNumFor )
3679 {
3680     return rCal.getValue( css::i18n::CalendarFieldIndex::ERA ) == 0 &&
3681         rCal.getUniqueID() == GREGORIAN && !lcl_hasEra( rNumFor );
3682 }
3683 
3684 /* XXX: if needed this could be stripped from rEpochStart and diff adding and
3685  * moved to tools' DateTime to be reused elsewhere. */
3686 static bool lcl_getValidDate( const DateTime& rNullDate, const DateTime& rEpochStart, double& fNumber )
3687 {
3688     static const DateTime aCE( Date(1,1,1));
3689     static const DateTime aMin( Date(1,1, SAL_MIN_INT16));
3690     static const DateTime aMax( Date(31,12, SAL_MAX_INT16), tools::Time(23,59,59, tools::Time::nanoSecPerSec - 1));
3691     static const double fMin = aMin - aCE;
3692     static const double fMax = aMax - aCE;
3693     // Value must be representable in our tools::Date proleptic Gregorian
3694     // calendar as well.
3695     const double fOff = (rNullDate - aCE) + fNumber;
3696     // Add diff between epochs to serial date number.
3697     const double fDiff = rNullDate - rEpochStart;
3698     fNumber += fDiff;
3699     return fMin <= fOff && fOff <= fMax;
3700 }
3701 
3702 bool SvNumberformat::ImpGetDateOutput(double fNumber,
3703                                       sal_uInt16 nIx,
3704                                       OUStringBuffer& sBuff)
3705 {
3706     using namespace ::com::sun::star::i18n;
3707     bool bRes = false;
3708 
3709     CalendarWrapper& rCal = GetCal();
3710     if (!lcl_getValidDate( rScan.GetNullDate(), rCal.getEpochStart(), fNumber))
3711     {
3712         sBuff = ImpSvNumberformatScan::sErrStr;
3713         return false;
3714     }
3715     rCal.setLocalDateTime( fNumber );
3716     int nUseMonthCase = 0; // Not decided yet
3717     OUString aOrgCalendar; // empty => not changed yet
3718 
3719     double fOrgDateTime(0.0);
3720     bool bOtherCalendar = ImpIsOtherCalendar( NumFor[nIx] );
3721     if ( bOtherCalendar )
3722     {
3723         SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3724     }
3725     if ( ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime ) )
3726     {
3727         bOtherCalendar = false;
3728     }
3729     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
3730     const sal_uInt16 nCnt = NumFor[nIx].GetCount();
3731     sal_Int16 nNatNum = NumFor[nIx].GetNatNum().GetNatNum();
3732     OUString aStr;
3733 
3734     // NatNum12: if the date format contains more than a date
3735     // field, it needs to specify in NatNum12 argument
3736     // which date element needs special formatting:
3737     //
3738     // '[NatNum12 ordinal-number]D'              -> "1st"
3739     // '[NatNum12 D=ordinal-number]D" of "MMMM'  -> "1st of April"
3740     // '[NatNum12 D=ordinal]D" of "MMMM'         -> "first of April"
3741     // '[NatNum12 YYYY=year,D=ordinal]D" of "MMMM", "YYYY' -> "first of April, nineteen ninety"
3742     //
3743     // Note: set only for YYYY, MMMM, M, DDDD, D and NNN/AAAA in date formats.
3744     // Additionally for MMMMM, MMM, DDD and NN/AA to support at least
3745     // capitalize, upper, lower, title.
3746     // XXX It's possible to extend this for other keywords and date + time
3747     // combinations, as required.
3748 
3749     bool bUseSpellout = NatNumTakesParameters(nNatNum) &&
3750             (nCnt == 1 || NumFor[nIx].GetNatNum().GetParams().indexOf('=') > -1);
3751 
3752     for (sal_uInt16 i = 0; i < nCnt; i++)
3753     {
3754         switch (rInfo.nTypeArray[i])
3755         {
3756         case NF_SYMBOLTYPE_CALENDAR :
3757             if ( !aOrgCalendar.getLength() )
3758             {
3759                 aOrgCalendar = rCal.getUniqueID();
3760                 fOrgDateTime = rCal.getDateTime();
3761             }
3762             rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLanguageTag().getLocale() );
3763             rCal.setDateTime( fOrgDateTime );
3764             ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3765             break;
3766         case NF_SYMBOLTYPE_STAR:
3767             if( bStarFlag )
3768             {
3769                 bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
3770             }
3771             break;
3772         case NF_SYMBOLTYPE_BLANK:
3773             if (rInfo.sStrArray[i].getLength() >= 2)
3774                 InsertBlanks( sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] );
3775             break;
3776         case NF_SYMBOLTYPE_STRING:
3777         case NF_SYMBOLTYPE_CURRENCY:
3778         case NF_SYMBOLTYPE_DATESEP:
3779         case NF_SYMBOLTYPE_TIMESEP:
3780         case NF_SYMBOLTYPE_TIME100SECSEP:
3781             sBuff.append(rInfo.sStrArray[i]);
3782             break;
3783         case NF_KEY_M:                  // M
3784             aStr = rCal.getDisplayString( CalendarDisplayCode::SHORT_MONTH, nNatNum );
3785             // NatNum12: support variants of preposition, suffixation or article
3786             // for example, Catalan "de març", but "d'abril" etc.
3787             if ( bUseSpellout )
3788             {
3789                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i]);
3790             }
3791             sBuff.append(aStr);
3792             break;
3793         case NF_KEY_MM:                 // MM
3794             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_MONTH, nNatNum ));
3795             break;
3796         case NF_KEY_MMM:                // MMM
3797         case NF_KEY_MMMM:               // MMMM
3798         case NF_KEY_MMMMM:              // MMMMM
3799             // NatNum12: support variants of preposition, suffixation or
3800             // article, or capitalize, upper, lower, title.
3801             // Note: result of the "spell out" conversion can depend from the optional
3802             // PartitiveMonths or GenitiveMonths defined in the locale data,
3803             // see description of ImpUseMonthCase(), and locale data in
3804             // i18npool/source/localedata/data/ and libnumbertext
3805             aStr = rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
3806                                                            static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
3807                                                            nNatNum);
3808             if ( bUseSpellout )
3809             {
3810                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i]);
3811             }
3812             sBuff.append(aStr);
3813             break;
3814         case NF_KEY_Q:                  // Q
3815             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_QUARTER, nNatNum ));
3816             break;
3817         case NF_KEY_QQ:                 // QQ
3818             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_QUARTER, nNatNum ));
3819             break;
3820         case NF_KEY_D:                  // D
3821             aStr = rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY, nNatNum );
3822             // NatNum12: support variants of preposition, suffixation or article
3823             if ( bUseSpellout )
3824             {
3825                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i]);
3826             }
3827             sBuff.append(aStr);
3828             break;
3829         case NF_KEY_DD:                 // DD
3830             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY, nNatNum ));
3831             break;
3832         case NF_KEY_DDD:                // DDD
3833             if ( bOtherCalendar )
3834             {
3835                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3836             }
3837             aStr = rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum );
3838             // NatNum12: support at least capitalize, upper, lower, title
3839             if ( bUseSpellout )
3840             {
3841                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i]);
3842             }
3843             sBuff.append(aStr);
3844             if ( bOtherCalendar )
3845             {
3846                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3847             }
3848             break;
3849         case NF_KEY_DDDD:               // DDDD
3850             if ( bOtherCalendar )
3851             {
3852                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3853             }
3854             aStr = rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum );
3855             // NatNum12: support variants of preposition, suffixation or article
3856             if ( bUseSpellout )
3857             {
3858                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i]);
3859             }
3860             sBuff.append(aStr);
3861             if ( bOtherCalendar )
3862             {
3863                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3864             }
3865             break;
3866         case NF_KEY_YY:                 // YY
3867             if ( bOtherCalendar )
3868             {
3869                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3870             }
3871             // Prepend a minus sign if Gregorian BCE and era is not displayed.
3872             if (lcl_isSignedYear( rCal, NumFor[nIx] ))
3873             {
3874                 sBuff.append('-');
3875             }
3876             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
3877             if ( bOtherCalendar )
3878             {
3879                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3880             }
3881             break;
3882         case NF_KEY_YYYY:               // YYYY
3883             if ( bOtherCalendar )
3884             {
3885                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3886             }
3887             // Prepend a minus sign if Gregorian BCE and era is not displayed.
3888             if (lcl_isSignedYear( rCal, NumFor[nIx] ))
3889             {
3890                 sBuff.append('-');
3891             }
3892             aStr = rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum );
3893             if (aStr.getLength() < 4 && !lcl_hasEra(NumFor[nIx]))
3894             {
3895                 using namespace comphelper::string;
3896                 // Ensure that year consists of at least 4 digits, so it
3897                 // can be distinguished from 2 digits display and edited
3898                 // without suddenly being hit by the 2-digit year magic.
3899                 OUStringBuffer aBuf;
3900                 padToLength(aBuf, 4 - aStr.getLength(), '0');
3901                 impTransliterate(aBuf, NumFor[nIx].GetNatNum());
3902                 aBuf.append(aStr);
3903                 aStr = aBuf.makeStringAndClear();
3904             }
3905             // NatNum12: support variants of preposition, suffixation or article
3906             if ( bUseSpellout )
3907             {
3908                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i]);
3909             }
3910             sBuff.append(aStr);
3911             if ( bOtherCalendar )
3912             {
3913                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3914             }
3915             break;
3916         case NF_KEY_EC:                 // E
3917             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
3918             break;
3919         case NF_KEY_EEC:                // EE
3920         case NF_KEY_R:                  // R
3921             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum ));
3922             break;
3923         case NF_KEY_NN:                 // NN
3924         case NF_KEY_AAA:                // AAA
3925             aStr = rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum );
3926             // NatNum12: support at least capitalize, upper, lower, title
3927             if ( bUseSpellout )
3928             {
3929                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i]);
3930             }
3931             sBuff.append(aStr);
3932             break;
3933         case NF_KEY_NNN:                // NNN
3934         case NF_KEY_AAAA:               // AAAA
3935             aStr = rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum );
3936             // NatNum12: support variants of preposition, suffixation or article
3937             if ( bUseSpellout )
3938             {
3939                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i]);
3940             }
3941             sBuff.append(aStr);
3942             break;
3943         case NF_KEY_NNNN:               // NNNN
3944             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
3945             sBuff.append(rLoc().getLongDateDayOfWeekSep());
3946             break;
3947         case NF_KEY_WW :                // WW
3948             sBuff.append(ImpIntToString( nIx,
3949                                          rCal.getValue( CalendarFieldIndex::WEEK_OF_YEAR )));
3950             break;
3951         case NF_KEY_G:                  // G
3952             ImpAppendEraG(sBuff, rCal, nNatNum );
3953             break;
3954         case NF_KEY_GG:                 // GG
3955             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
3956             break;
3957         case NF_KEY_GGG:                // GGG
3958             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_ERA, nNatNum ));
3959             break;
3960         case NF_KEY_RR:                 // RR => GGGEE
3961             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA, nNatNum ));
3962             break;
3963         }
3964     }
3965     if ( aOrgCalendar.getLength() )
3966     {
3967         rCal.loadCalendar( aOrgCalendar, rLoc().getLanguageTag().getLocale() );  // restore calendar
3968     }
3969     return bRes;
3970 }
3971 
3972 bool SvNumberformat::ImpGetDateTimeOutput(double fNumber,
3973                                           sal_uInt16 nIx,
3974                                           OUStringBuffer& sBuff)
3975 {
3976     using namespace ::com::sun::star::i18n;
3977     bool bRes = false;
3978 
3979     CalendarWrapper& rCal = GetCal();
3980     if (!lcl_getValidDate( rScan.GetNullDate(), rCal.getEpochStart(), fNumber))
3981     {
3982         sBuff = ImpSvNumberformatScan::sErrStr;
3983         return false;
3984     }
3985 
3986     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
3987     bool bInputLine;
3988     sal_Int32 nCntPost, nFirstRounding;
3989     if ( rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION &&
3990          0 < rInfo.nCntPost && rInfo.nCntPost < kTimeSignificantRound )
3991     {
3992         bInputLine = true;
3993         nCntPost = nFirstRounding = kTimeSignificantRound;
3994     }
3995     else
3996     {
3997         bInputLine = false;
3998         nCntPost = rInfo.nCntPost;
3999         // For clock format (not []) do not round up to seconds and thus days.
4000         nFirstRounding = (rInfo.bThousand ? nCntPost : kTimeSignificantRound);
4001     }
4002     double fTime = (fNumber - floor( fNumber )) * 86400.0;
4003     fTime = ::rtl::math::round( fTime, int(nFirstRounding) );
4004     if (fTime >= 86400.0)
4005     {
4006         // result of fNumber==x.999999999... rounded up, use correct date/time
4007         fTime -= 86400.0;
4008         fNumber = floor( fNumber + 0.5) + fTime;
4009     }
4010     rCal.setLocalDateTime( fNumber );
4011 
4012     int nUseMonthCase = 0; // Not decided yet
4013     OUString aOrgCalendar; // empty => not changed yet
4014     double fOrgDateTime(0.0);
4015     bool bOtherCalendar = ImpIsOtherCalendar( NumFor[nIx] );
4016     if ( bOtherCalendar )
4017     {
4018         SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
4019     }
4020     if ( ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime ) )
4021     {
4022         bOtherCalendar = false;
4023     }
4024     sal_Int16 nNatNum = NumFor[nIx].GetNatNum().GetNatNum();
4025 
4026     OUStringBuffer sSecStr;
4027     sal_Int32 nSecPos = 0; // For figure by figure processing
4028     sal_uInt32 nHour, nMin, nSec;
4029     if (!rInfo.bThousand) // No [] format
4030     {
4031         sal_uInt16 nCHour, nCMinute, nCSecond;
4032         double fFractionOfSecond;
4033         tools::Time::GetClock( fNumber, nCHour, nCMinute, nCSecond, fFractionOfSecond, nCntPost);
4034         nHour = nCHour;
4035         nMin = nCMinute;
4036         nSec = nCSecond;
4037         nCntPost = ImpGetFractionOfSecondString( sSecStr, fFractionOfSecond, nCntPost, true, nIx,
4038                 (bInputLine ? rInfo.nCntPost : 0));
4039     }
4040     else
4041     {
4042         sal_uInt32 nSeconds = static_cast<sal_uInt32>(floor( fTime ));
4043 
4044         nCntPost = ImpGetFractionOfSecondString( sSecStr, fTime - nSeconds, nCntPost, false, nIx,
4045                 (bInputLine ? rInfo.nCntPost : 0));
4046 
4047         if (rInfo.nThousand == 3) // [ss]
4048         {
4049             nHour = 0;
4050             nMin = 0;
4051             nSec = nSeconds;
4052         }
4053         else if (rInfo.nThousand == 2) // [mm]:ss
4054         {
4055             nHour = 0;
4056             nMin = nSeconds / 60;
4057             nSec = nSeconds % 60;
4058         }
4059         else if (rInfo.nThousand == 1) // [hh]:mm:ss
4060         {
4061             nHour = nSeconds / 3600;
4062             nMin = (nSeconds%3600) / 60;
4063             nSec = nSeconds%60;
4064         }
4065         else
4066         {
4067             nHour = 0;  // TODO What should these values be?
4068             nMin  = 0;
4069             nSec  = 0;
4070         }
4071     }
4072     sal_Unicode cAmPm = ' '; // a or p
4073     if (rInfo.nCntExp) // AM/PM
4074     {
4075         if (nHour == 0)
4076         {
4077             nHour = 12;
4078             cAmPm = 'a';
4079         }
4080         else if (nHour < 12)
4081         {
4082             cAmPm = 'a';
4083         }
4084         else
4085         {
4086             cAmPm = 'p';
4087             if (nHour > 12)
4088             {
4089                 nHour -= 12;
4090             }
4091         }
4092     }
4093     const sal_uInt16 nCnt = NumFor[nIx].GetCount();
4094     sal_Int32 nLen;
4095     OUString aYear;
4096     for (sal_uInt16 i = 0; i < nCnt; i++)
4097     {
4098         switch (rInfo.nTypeArray[i])
4099         {
4100         case NF_SYMBOLTYPE_CALENDAR :
4101             if ( !aOrgCalendar.getLength() )
4102             {
4103                 aOrgCalendar = rCal.getUniqueID();
4104                 fOrgDateTime = rCal.getDateTime();
4105             }
4106             rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLanguageTag().getLocale() );
4107             rCal.setDateTime( fOrgDateTime );
4108             ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime );
4109             break;
4110         case NF_SYMBOLTYPE_STAR:
4111             if( bStarFlag )
4112             {
4113                 bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
4114             }
4115             break;
4116         case NF_SYMBOLTYPE_BLANK:
4117             if (rInfo.sStrArray[i].getLength() >= 2)
4118                 InsertBlanks( sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] );
4119             break;
4120         case NF_SYMBOLTYPE_STRING:
4121         case NF_SYMBOLTYPE_CURRENCY:
4122         case NF_SYMBOLTYPE_DATESEP:
4123         case NF_SYMBOLTYPE_TIMESEP:
4124         case NF_SYMBOLTYPE_TIME100SECSEP:
4125             sBuff.append(rInfo.sStrArray[i]);
4126             break;
4127         case NF_SYMBOLTYPE_DIGIT:
4128             nLen = ( bInputLine && i > 0 &&
4129                      (rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING ||
4130                       rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_TIME100SECSEP) ?
4131                      nCntPost : rInfo.sStrArray[i].getLength() );
4132             for (sal_Int32 j = 0; j < nLen && nSecPos < nCntPost && nSecPos < sSecStr.getLength(); ++j)
4133             {
4134                 sBuff.append(sSecStr[ nSecPos ]);
4135                 nSecPos++;
4136             }
4137             break;
4138         case NF_KEY_AMPM:               // AM/PM
4139             if (cAmPm == 'a')
4140             {
4141                 sBuff.append(rCal.getDisplayName( CalendarDisplayIndex::AM_PM,
4142                                                   AmPmValue::AM, 0 ));
4143             }
4144             else
4145             {
4146                 sBuff.append(rCal.getDisplayName( CalendarDisplayIndex::AM_PM,
4147                                                   AmPmValue::PM, 0 ));
4148             }
4149             break;
4150         case NF_KEY_AP:                 // A/P
4151             if (cAmPm == 'a')
4152             {
4153                 sBuff.append('a');
4154             }
4155             else
4156             {
4157                 sBuff.append('p');
4158             }
4159             break;
4160         case NF_KEY_MI:                 // M
4161             sBuff.append(ImpIntToString( nIx, nMin ));
4162             break;
4163         case NF_KEY_MMI:                // MM
4164             sBuff.append(ImpIntToString( nIx, nMin, 2 ));
4165             break;
4166         case NF_KEY_H:                  // H
4167             sBuff.append(ImpIntToString( nIx, nHour ));
4168             break;
4169         case NF_KEY_HH:                 // HH
4170             sBuff.append(ImpIntToString( nIx, nHour, 2 ));
4171             break;
4172         case NF_KEY_S:                  // S
4173             sBuff.append(ImpIntToString( nIx, nSec ));
4174             break;
4175         case NF_KEY_SS:                 // SS
4176             sBuff.append(ImpIntToString( nIx, nSec, 2 ));
4177             break;
4178         case NF_KEY_M:                  // M
4179             sBuff.append(rCal.getDisplayString(
4180                              CalendarDisplayCode::SHORT_MONTH, nNatNum ));
4181             break;
4182         case NF_KEY_MM:                 // MM
4183             sBuff.append(rCal.getDisplayString(
4184                              CalendarDisplayCode::LONG_MONTH, nNatNum ));
4185             break;
4186         case NF_KEY_MMM:                // MMM
4187             sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
4188                                                                  static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
4189                                                 nNatNum));
4190             break;
4191         case NF_KEY_MMMM:               // MMMM
4192             sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
4193                                                                  static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
4194                                                 nNatNum));
4195             break;
4196         case NF_KEY_MMMMM:              // MMMMM
4197             sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
4198                                                                  static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
4199                                                 nNatNum));
4200             break;
4201         case NF_KEY_Q:                  // Q
4202             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_QUARTER, nNatNum ));
4203             break;
4204         case NF_KEY_QQ:                 // QQ
4205             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_QUARTER, nNatNum ));
4206             break;
4207         case NF_KEY_D:                  // D
4208             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY, nNatNum ));
4209             break;
4210         case NF_KEY_DD:                 // DD
4211             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY, nNatNum ));
4212             break;
4213         case NF_KEY_DDD:                // DDD
4214             if ( bOtherCalendar )
4215             {
4216                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
4217             }
4218             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ));
4219             if ( bOtherCalendar )
4220             {
4221                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
4222             }
4223             break;
4224         case NF_KEY_DDDD:               // DDDD
4225             if ( bOtherCalendar )
4226             {
4227                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
4228             }
4229             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
4230             if ( bOtherCalendar )
4231             {
4232                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
4233             }
4234             break;
4235         case NF_KEY_YY:                 // YY
4236             if ( bOtherCalendar )
4237             {
4238                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
4239             }
4240             // Prepend a minus sign if Gregorian BCE and era is not displayed.
4241             if (lcl_isSignedYear( rCal, NumFor[nIx] ))
4242             {
4243                 sBuff.append('-');
4244             }
4245             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
4246             if ( bOtherCalendar )
4247             {
4248                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
4249             }
4250             break;
4251         case NF_KEY_YYYY:               // YYYY
4252             if ( bOtherCalendar )
4253             {
4254                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
4255             }
4256             // Prepend a minus sign if Gregorian BCE and era is not displayed.
4257             if (lcl_isSignedYear( rCal, NumFor[nIx] ))
4258             {
4259                 sBuff.append('-');
4260             }
4261             aYear = rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum );
4262             if (aYear.getLength() < 4 && !lcl_hasEra(NumFor[nIx]))
4263             {
4264                 using namespace comphelper::string;
4265                 // Ensure that year consists of at least 4 digits, so it
4266                 // can be distinguished from 2 digits display and edited
4267                 // without suddenly being hit by the 2-digit year magic.
4268                 OUStringBuffer aBuf;
4269                 padToLength(aBuf, 4 - aYear.getLength(), '0');
4270                 impTransliterate(aBuf, NumFor[nIx].GetNatNum());
4271                 aBuf.append(aYear);
4272                 sBuff.append(aBuf);
4273             }
4274             else
4275             {
4276                 sBuff.append(aYear);
4277             }
4278             if ( bOtherCalendar )
4279             {
4280                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
4281             }
4282             break;
4283         case NF_KEY_EC:                 // E
4284             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
4285             break;
4286         case NF_KEY_EEC:                // EE
4287         case NF_KEY_R:                  // R
4288             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum ));
4289             break;
4290         case NF_KEY_NN:                 // NN
4291         case NF_KEY_AAA:                // AAA
4292             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ));
4293             break;
4294         case NF_KEY_NNN:                // NNN
4295         case NF_KEY_AAAA:               // AAAA
4296             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
4297             break;
4298         case NF_KEY_NNNN:               // NNNN
4299             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
4300             sBuff.append(rLoc().getLongDateDayOfWeekSep());
4301             break;
4302         case NF_KEY_WW :                // WW
4303             sBuff.append(ImpIntToString( nIx, rCal.getValue( CalendarFieldIndex::WEEK_OF_YEAR )));
4304             break;
4305         case NF_KEY_G:                  // G
4306             ImpAppendEraG( sBuff, rCal, nNatNum );
4307             break;
4308         case NF_KEY_GG:                 // GG
4309             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
4310             break;
4311         case NF_KEY_GGG:                // GGG
4312             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_ERA, nNatNum ));
4313             break;
4314         case NF_KEY_RR:                 // RR => GGGEE
4315             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA, nNatNum ));
4316             break;
4317         }
4318     }
4319     if ( aOrgCalendar.getLength() )
4320     {
4321         rCal.loadCalendar( aOrgCalendar, rLoc().getLanguageTag().getLocale() );  // restore calendar
4322     }
4323     return bRes;
4324 }
4325 
4326 bool SvNumberformat::ImpGetLogicalOutput(double fNumber,
4327                                          sal_uInt16 nIx,
4328                                          OUStringBuffer& sStr)
4329 {
4330     bool bRes = false;
4331     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4332     const sal_uInt16 nCnt = NumFor[nIx].GetCount();
4333     for (sal_uInt16 j = 0; j < nCnt; ++j)
4334     {
4335         switch (rInfo.nTypeArray[j])
4336         {
4337             case NF_KEY_BOOLEAN:
4338                 sStr.append( fNumber ? rScan.GetTrueString() : rScan.GetFalseString());
4339             break;
4340             case NF_SYMBOLTYPE_STRING:
4341                 sStr.append( rInfo.sStrArray[j]);
4342             break;
4343         }
4344     }
4345     impTransliterate(sStr, NumFor[nIx].GetNatNum());
4346     return bRes;
4347 }
4348 
4349 bool SvNumberformat::ImpGetNumberOutput(double fNumber,
4350                                         sal_uInt16 nIx,
4351                                         OUStringBuffer& sStr)
4352 {
4353     bool bRes = false;
4354     bool bSign;
4355     if (fNumber < 0.0)
4356     {
4357         bSign = (nIx == 0); // Not in the ones at the back;
4358         fNumber = -fNumber;
4359     }
4360     else
4361     {
4362         bSign = false;
4363         if ( std::signbit( fNumber ) )
4364         {
4365             fNumber = -fNumber; // yes, -0.0 is possible, eliminate '-'
4366         }
4367     }
4368     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4369     if (rInfo.eScannedType == SvNumFormatType::PERCENT)
4370     {
4371         if (fNumber < D_MAX_D_BY_100)
4372         {
4373             fNumber *= 100.0;
4374         }
4375         else
4376         {
4377             sStr = ImpSvNumberformatScan::sErrStr;
4378             return false;
4379         }
4380     }
4381     sal_uInt16 i, j;
4382     sal_Int32 nDecPos = -1;
4383     bool bInteger = false;
4384     if ( rInfo.nThousand != FLAG_STANDARD_IN_FORMAT )
4385     {
4386         // Special formatting only if no GENERAL keyword in format code
4387         const sal_uInt16 nThousand = rInfo.nThousand;
4388         tools::Long nPrecExp;
4389         for (i = 0; i < nThousand; i++)
4390         {
4391            if (fNumber > D_MIN_M_BY_1000)
4392            {
4393                fNumber /= 1000.0;
4394            }
4395            else
4396            {
4397                fNumber = 0.0;
4398            }
4399         }
4400         if (fNumber > 0.0)
4401         {
4402             nPrecExp = GetPrecExp( fNumber );
4403         }
4404         else
4405         {
4406             nPrecExp = 0;
4407         }
4408         if (rInfo.nCntPost) // Decimal places
4409         {
4410             if ((rInfo.nCntPost + nPrecExp) > 15 && nPrecExp < 15)
4411             {
4412                 sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, 15-nPrecExp, '.');
4413                 for (tools::Long l = 15-nPrecExp; l < static_cast<tools::Long>(rInfo.nCntPost); l++)
4414                 {
4415                     sStr.append('0');
4416                 }
4417             }
4418             else
4419             {
4420                 sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, rInfo.nCntPost, '.' );
4421             }
4422             sStr.stripStart('0'); // Strip leading zeros
4423         }
4424         else if (fNumber == 0.0) // Null
4425         {
4426             // Nothing to be done here, keep empty string sStr,
4427             // ImpNumberFillWithThousands does the rest
4428         }
4429         else // Integer
4430         {
4431             sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, 0, '.');
4432             sStr.stripStart('0'); // Strip leading zeros
4433         }
4434         nDecPos = sStr.indexOf('.' );
4435         if ( nDecPos >= 0)
4436         {
4437             const sal_Unicode* p = sStr.getStr() + nDecPos;
4438             while ( *++p == '0' )
4439                 ;
4440             if ( !*p )
4441             {
4442                 bInteger = true;
4443             }
4444             sStr.remove( nDecPos, 1 ); //  Remove .
4445         }
4446         if (bSign && (sStr.isEmpty() || checkForAll0s(sStr)))   // Only 00000
4447         {
4448             bSign = false;              // Not -0.00
4449         }
4450     }                                   // End of != FLAG_STANDARD_IN_FORMAT
4451 
4452                                         // Edit backwards:
4453     j = NumFor[nIx].GetCount()-1;       // Last symbol
4454                                         // Decimal places:
4455     bRes |= ImpDecimalFill( sStr, fNumber, nDecPos, j, nIx, bInteger );
4456     if (bSign)
4457     {
4458         sStr.insert(0, '-');
4459     }
4460     impTransliterate(sStr, NumFor[nIx].GetNatNum());
4461     return bRes;
4462 }
4463 
4464 bool SvNumberformat::ImpDecimalFill( OUStringBuffer& sStr,  // number string
4465                                    double& rNumber,       // number
4466                                    sal_Int32 nDecPos,     // decimals start
4467                                    sal_uInt16 j,          // symbol index within format code
4468                                    sal_uInt16 nIx,        // subformat index
4469                                    bool bInteger)         // is integer
4470 {
4471     bool bRes = false;
4472     bool bFilled = false;               // Was filled?
4473     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4474     sal_Int32 k = sStr.getLength();     // After last figure
4475                                         // Decimal places:
4476     if (rInfo.nCntPost > 0)
4477     {
4478         bool bTrailing = true;          // Trailing zeros?
4479         short nType;
4480         while (j > 0 &&                 // Backwards
4481                (nType = rInfo.nTypeArray[j]) != NF_SYMBOLTYPE_DECSEP)
4482         {
4483             switch ( nType )
4484             {
4485             case NF_SYMBOLTYPE_STAR:
4486                 if( bStarFlag )
4487                 {
4488                     bRes = lcl_insertStarFillChar( sStr, k, rInfo.sStrArray[j]);
4489                 }
4490                 break;
4491             case NF_SYMBOLTYPE_BLANK:
4492                 if (rInfo.sStrArray[j].getLength() >= 2)
4493                     /*k = */ InsertBlanks(sStr, k, rInfo.sStrArray[j][1] );
4494                 break;
4495             case NF_SYMBOLTYPE_STRING:
4496             case NF_SYMBOLTYPE_CURRENCY:
4497             case NF_SYMBOLTYPE_PERCENT:
4498                 sStr.insert(k, rInfo.sStrArray[j]);
4499                 break;
4500             case NF_SYMBOLTYPE_THSEP:
4501                 if (rInfo.nThousand == 0)
4502                 {
4503                     sStr.insert(k, rInfo.sStrArray[j]);
4504                 }
4505                 break;
4506             case NF_SYMBOLTYPE_DIGIT:
4507             {
4508                 const OUString& rStr = rInfo.sStrArray[j];
4509                 const sal_Unicode* p1 = rStr.getStr();
4510                 const sal_Unicode* p = p1 + rStr.getLength();
4511                 // In case the number of decimals passed are less than the
4512                 // "digits" given, append trailing '0' characters, which here
4513                 // means insert them because literal strings may have been
4514                 // appended already. If they weren't to be '0' characters
4515                 // they'll be changed below, as if decimals with trailing zeros
4516                 // were passed.
4517                 if (nDecPos >= 0 && nDecPos <= k)
4518                 {
4519                     sal_Int32 nAppend = rStr.getLength() - (k - nDecPos);
4520                     while (nAppend-- > 0)
4521                     {
4522                         sStr.insert( k++, '0');
4523                     }
4524                 }
4525                 while (k && p1 < p--)
4526                 {
4527                     const sal_Unicode c = *p;
4528                     k--;
4529                     if ( sStr[k] != '0' )
4530                     {
4531                         bTrailing = false;
4532                         bFilled = true;
4533                     }
4534                     if (bTrailing)
4535                     {
4536                         if ( c == '0' )
4537                         {
4538                             bFilled = true;
4539                         }
4540                         else if ( c == '-' )
4541                         {
4542                             if ( bInteger )
4543                             {
4544                                 sStr[ k ] = '-';
4545                             }
4546                             bFilled = true;
4547                         }
4548                         else if ( c == '?' )
4549                         {
4550                             sStr[ k ] = ' ';
4551                             bFilled = true;
4552                         }
4553                         else if ( !bFilled ) // #
4554                         {
4555                             sStr.remove(k,1);
4556                         }
4557                     }
4558                 } // of for
4559                 break;
4560             } // of case digi
4561             case NF_KEY_CCC: // CCC currency
4562                 sStr.insert(k, rScan.GetCurAbbrev());
4563                 break;
4564             case NF_KEY_GENERAL: // Standard in the String
4565             {
4566                 OUStringBuffer sNum;
4567                 ImpGetOutputStandard(rNumber, sNum);
4568                 sNum.stripStart('-');
4569                 sStr.insert(k, sNum);
4570                 break;
4571             }
4572             default:
4573                 break;
4574             } // of switch
4575             j--;
4576         } // of while
4577     } // of decimal places
4578 
4579     bRes |= ImpNumberFillWithThousands(sStr, rNumber, k, j, nIx, // Fill with . if needed
4580                                        rInfo.nCntPre, bFilled );
4581 
4582     return bRes;
4583 }
4584 
4585 bool SvNumberformat::ImpNumberFillWithThousands( OUStringBuffer& sBuff,  // number string
4586                                                  double& rNumber,       // number
4587                                                  sal_Int32 k,           // position within string
4588                                                  sal_uInt16 j,          // symbol index within format code
4589                                                  sal_uInt16 nIx,        // subformat index
4590                                                  sal_Int32 nDigCnt,     // count of integer digits in format
4591                                                  bool bAddDecSep)       // add decimal separator if necessary
4592 {
4593     bool bRes = false;
4594     sal_Int32 nLeadingStringChars = 0; // inserted StringChars before number
4595     sal_Int32 nDigitCount = 0;         // count of integer digits from the right
4596     bool bStop = false;
4597     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4598     // no normal thousands separators if number divided by thousands
4599     bool bDoThousands = (rInfo.nThousand == 0);
4600     utl::DigitGroupingIterator aGrouping( GetFormatter().GetLocaleData()->getDigitGrouping());
4601 
4602     while (!bStop) // backwards
4603     {
4604         if (j == 0)
4605         {
4606             bStop = true;
4607         }
4608         switch (rInfo.nTypeArray[j])
4609         {
4610         case NF_SYMBOLTYPE_DECSEP:
4611             aGrouping.reset();
4612             [[fallthrough]];
4613         case NF_SYMBOLTYPE_STRING:
4614         case NF_SYMBOLTYPE_CURRENCY:
4615         case NF_SYMBOLTYPE_PERCENT:
4616             if ( rInfo.nTypeArray[j] != NF_SYMBOLTYPE_DECSEP || bAddDecSep )
4617                 sBuff.insert(k, rInfo.sStrArray[j]);
4618             if ( k == 0 )
4619             {
4620                 nLeadingStringChars = nLeadingStringChars + rInfo.sStrArray[j].getLength();
4621             }
4622             break;
4623         case NF_SYMBOLTYPE_STAR:
4624             if( bStarFlag )
4625             {
4626                 bRes = lcl_insertStarFillChar( sBuff, k, rInfo.sStrArray[j]);
4627             }
4628             break;
4629         case NF_SYMBOLTYPE_BLANK:
4630             if (rInfo.sStrArray[j].getLength() >= 2)
4631                 /*k = */ InsertBlanks(sBuff, k, rInfo.sStrArray[j][1] );
4632             break;
4633         case NF_SYMBOLTYPE_THSEP:
4634             // #i7284# #102685# Insert separator also if number is divided
4635             // by thousands and the separator is specified somewhere in
4636             // between and not only at the end.
4637             // #i12596# But do not insert if it's a parenthesized negative
4638             // format like (#,)
4639             // In fact, do not insert if divided and regex [0#,],[^0#] and
4640             // no other digit symbol follows (which was already detected
4641             // during scan of format code, otherwise there would be no
4642             // division), else do insert. Same in ImpNumberFill() below.
4643             if ( !bDoThousands && j < NumFor[nIx].GetCount()-1 )
4644             {
4645                 bDoThousands = ((j == 0) ||
4646                                 (rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_DIGIT &&
4647                                  rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_THSEP) ||
4648                                 (rInfo.nTypeArray[j+1] == NF_SYMBOLTYPE_DIGIT));
4649             }
4650             if ( bDoThousands )
4651             {
4652                 if (k > 0)
4653                 {
4654                     sBuff.insert(k, rInfo.sStrArray[j]);
4655                 }
4656                 else if (nDigitCount < nDigCnt)
4657                 {
4658                     // Leading '#' displays nothing (e.g. no leading
4659                     // separator for numbers <1000 with #,##0 format).
4660                     // Leading '?' displays blank.
4661                     // Everything else, including nothing, displays the
4662                     // separator.
4663                     sal_Unicode cLeader = 0;
4664                     if (j > 0 && rInfo.nTypeArray[j-1] == NF_SYMBOLTYPE_DIGIT)
4665                     {
4666                         const OUString& rStr = rInfo.sStrArray[j-1];
4667                         sal_Int32 nLen = rStr.getLength();
4668                         if (nLen)
4669                         {
4670                             cLeader = rStr[ nLen - 1 ];
4671                         }
4672                     }
4673                     switch (cLeader)
4674                     {
4675                     case '#':
4676                         ;   // nothing
4677                         break;
4678                     case '?':
4679                         // replace thousand separator with blank
4680                         sBuff.insert(k, ' ');
4681                         break;
4682                     default:
4683                         sBuff.insert(k, rInfo.sStrArray[j]);
4684                     }
4685                 }
4686                 aGrouping.advance();
4687             }
4688             break;
4689         case NF_SYMBOLTYPE_DIGIT:
4690         {
4691             const OUString& rStr = rInfo.sStrArray[j];
4692             const sal_Unicode* p1 = rStr.getStr();
4693             const sal_Unicode* p = p1 + rStr.getLength();
4694             while ( p1 < p-- )
4695             {
4696                 nDigitCount++;
4697                 if (k > 0)
4698                 {
4699                     k--;
4700                 }
4701                 else
4702                 {
4703                     switch (*p)
4704                     {
4705                     case '0':
4706                         sBuff.insert(0, '0');
4707                         break;
4708                     case '?':
4709                         sBuff.insert(0, ' ');
4710                         break;
4711                     }
4712                 }
4713                 if (nDigitCount == nDigCnt && k > 0)
4714                 {
4715                     // more digits than specified
4716                     ImpDigitFill(sBuff, 0, k, nIx, nDigitCount, aGrouping);
4717                 }
4718             }
4719             break;
4720         }
4721         case NF_KEY_CCC: // CCC currency
4722             sBuff.insert(k, rScan.GetCurAbbrev());
4723             break;
4724         case NF_KEY_GENERAL: // "General" in string
4725         {
4726             OUStringBuffer sNum;
4727             ImpGetOutputStandard(rNumber, sNum);
4728             sNum.stripStart('-');
4729             sBuff.insert(k, sNum);
4730             break;
4731         }
4732         default:
4733             break;
4734         } // switch
4735         j--; // next format code string
4736     } // while
4737 
4738     k = k + nLeadingStringChars;    // MSC converts += to int and then warns, so ...
4739     if (k > nLeadingStringChars)
4740     {
4741         ImpDigitFill(sBuff, nLeadingStringChars, k, nIx, nDigitCount, aGrouping);
4742     }
4743     return bRes;
4744 }
4745 
4746 void SvNumberformat::ImpDigitFill(OUStringBuffer& sStr,     // number string
4747                                   sal_Int32 nStart,         // start of digits
4748                                   sal_Int32 & k,            // position within string
4749                                   sal_uInt16 nIx,           // subformat index
4750                                   sal_Int32 & nDigitCount,  // count of integer digits from the right so far
4751                                   utl::DigitGroupingIterator & rGrouping )  // current grouping
4752 {
4753     if (NumFor[nIx].Info().bThousand) // Only if grouping fill in separators
4754     {
4755         const OUString& rThousandSep = GetFormatter().GetNumThousandSep();
4756         while (k > nStart)
4757         {
4758             if (nDigitCount == rGrouping.getPos())
4759             {
4760                 sStr.insert( k, rThousandSep );
4761                 rGrouping.advance();
4762             }
4763             nDigitCount++;
4764             k--;
4765         }
4766     }
4767     else // simply skip
4768     {
4769         k = nStart;
4770     }
4771 }
4772 
4773 bool SvNumberformat::ImpNumberFill( OUStringBuffer& sBuff, // number string
4774                                     double& rNumber,       // number for "General" format
4775                                     sal_Int32& k,          // position within string
4776                                     sal_uInt16& j,         // symbol index within format code
4777                                     sal_uInt16 nIx,        // subformat index
4778                                     short eSymbolType,     // type of stop condition
4779                                     bool bInsertRightBlank)// insert blank on right for denominator (default = false)
4780 {
4781     bool bRes = false;
4782     bool bStop = false;
4783     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4784     // no normal thousands separators if number divided by thousands
4785     bool bDoThousands = (rInfo.nThousand == 0);
4786     bool bFoundNumber = false;
4787     short nType;
4788 
4789     k = sBuff.getLength(); // behind last digit
4790 
4791     while (!bStop && (nType = rInfo.nTypeArray[j]) != eSymbolType ) // Backwards
4792     {
4793         switch ( nType )
4794         {
4795         case NF_SYMBOLTYPE_STAR:
4796             if( bStarFlag )
4797             {
4798                 if ( bFoundNumber && eSymbolType != NF_SYMBOLTYPE_EXP )
4799                     k = 0; // tdf#100842 jump to beginning of number before inserting something else
4800                 bRes = lcl_insertStarFillChar( sBuff, k, rInfo.sStrArray[j]);
4801             }
4802             break;
4803         case NF_SYMBOLTYPE_BLANK:
4804             if (rInfo.sStrArray[j].getLength() >= 2)
4805             {
4806                 if ( bFoundNumber && eSymbolType != NF_SYMBOLTYPE_EXP )
4807                     k = 0; // tdf#100842 jump to beginning of number before inserting something else
4808                 k = InsertBlanks(sBuff, k, rInfo.sStrArray[j][1] );
4809             }
4810             break;
4811         case NF_SYMBOLTYPE_THSEP:
4812             // Same as in ImpNumberFillWithThousands() above, do not insert
4813             // if divided and regex [0#,],[^0#] and no other digit symbol
4814             // follows (which was already detected during scan of format
4815             // code, otherwise there would be no division), else do insert.
4816             if ( !bDoThousands && j < NumFor[nIx].GetCount()-1 )
4817             {
4818                 bDoThousands = ((j == 0) ||
4819                                 (rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_DIGIT &&
4820                                  rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_THSEP) ||
4821                                 (rInfo.nTypeArray[j+1] == NF_SYMBOLTYPE_DIGIT));
4822             }
4823             if ( bDoThousands && k > 0 )
4824             {
4825                 sBuff.insert(k, rInfo.sStrArray[j]);
4826             }
4827             break;
4828         case NF_SYMBOLTYPE_DIGIT:
4829         {
4830             bFoundNumber = true;
4831             sal_uInt16 nPosInsertBlank = bInsertRightBlank ? k : 0; // left alignment of denominator
4832             const OUString& rStr = rInfo.sStrArray[j];
4833             const sal_Unicode* p1 = rStr.getStr();
4834             const sal_Unicode* p = p1 + rStr.getLength();
4835             while ( p1 < p-- )
4836             {
4837                 if (k > 0)
4838                 {
4839                     k--;
4840                 }
4841                 else
4842                 {
4843                     switch (*p)
4844                     {
4845                     case '0':
4846                         sBuff.insert(0, '0');
4847                         break;
4848                     case '?':
4849                         sBuff.insert(nPosInsertBlank, ' ');
4850                         break;
4851                     }
4852                 }
4853             }
4854         }
4855         break;
4856         case NF_KEY_CCC:                // CCC currency
4857             sBuff.insert(k, rScan.GetCurAbbrev());
4858             break;
4859         case NF_KEY_GENERAL: // Standard in the String
4860         {
4861             OUStringBuffer sNum;
4862             bFoundNumber = true;
4863             ImpGetOutputStandard(rNumber, sNum);
4864             sNum.stripStart('-');
4865             sBuff.insert(k, sNum);
4866         }
4867         break;
4868         case NF_SYMBOLTYPE_FRAC_FDIV: // Do Nothing
4869             if (k > 0)
4870             {
4871                 k--;
4872             }
4873             break;
4874 
4875         default:
4876             if ( bFoundNumber && eSymbolType != NF_SYMBOLTYPE_EXP )
4877                 k = 0; // tdf#100842 jump to beginning of number before inserting something else
4878             sBuff.insert(k, rInfo.sStrArray[j]);
4879             break;
4880         } // of switch
4881         if ( j )
4882             j--; // Next String
4883         else
4884             bStop = true;
4885     } // of while
4886     return bRes;
4887 }
4888 
4889 void SvNumberformat::GetFormatSpecialInfo(bool& bThousand,
4890                                           bool& IsRed,
4891                                           sal_uInt16& nPrecision,
4892                                           sal_uInt16& nLeadingCnt) const
4893 {
4894     // as before: take info from nNumFor=0 for whole format (for dialog etc.)
4895 
4896     SvNumFormatType nDummyType;
4897     GetNumForInfo( 0, nDummyType, bThousand, nPrecision, nLeadingCnt );
4898 
4899     // "negative in red" is only useful for the whole format
4900 
4901     const Color* pColor = NumFor[1].GetColor();
4902     IsRed = fLimit1 == 0.0 && fLimit2 == 0.0 && pColor
4903         && (*pColor == ImpSvNumberformatScan::GetRedColor());
4904 }
4905 
4906 void SvNumberformat::GetNumForInfo( sal_uInt16 nNumFor, SvNumFormatType& rScannedType,
4907                     bool& bThousand, sal_uInt16& nPrecision, sal_uInt16& nLeadingCnt ) const
4908 {
4909     // take info from a specified sub-format (for XML export)
4910 
4911     if ( nNumFor > 3 )
4912     {
4913         return; // invalid
4914     }
4915 
4916     const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
4917     rScannedType = rInfo.eScannedType;
4918     bThousand = rInfo.bThousand;
4919     nPrecision = (rInfo.eScannedType == SvNumFormatType::FRACTION)
4920                     ? rInfo.nCntExp  // number of denominator digits for fraction
4921                     : rInfo.nCntPost;
4922     sal_Int32 nPosHash = 1;
4923     if ( rInfo.eScannedType == SvNumFormatType::FRACTION &&
4924             ( (nPosHash += GetDenominatorString(nNumFor).indexOf('#')) > 0 ) )
4925         nPrecision -= nPosHash;
4926     if (bStandard && rInfo.eScannedType == SvNumFormatType::NUMBER)
4927     {
4928         // StandardFormat
4929         nLeadingCnt = 1;
4930     }
4931     else
4932     {
4933         nLeadingCnt = 0;
4934         bool bStop = false;
4935         sal_uInt16 i = 0;
4936         const sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
4937         while (!bStop && i < nCnt)
4938         {
4939             short nType = rInfo.nTypeArray[i];
4940             if ( nType == NF_SYMBOLTYPE_DIGIT)
4941             {
4942                 const sal_Unicode* p = rInfo.sStrArray[i].getStr();
4943                 while ( *p == '#' )
4944                 {
4945                     p++;
4946                 }
4947                 while ( *p == '0' || *p == '?' )
4948                 {
4949                     nLeadingCnt++;
4950                     p++;
4951                 }
4952             }
4953             else if (nType == NF_SYMBOLTYPE_DECSEP
4954                   || nType == NF_SYMBOLTYPE_EXP
4955                   || nType == NF_SYMBOLTYPE_FRACBLANK)  // Fraction: stop after integer part,
4956             {                                           // do not count '0' of fraction
4957                 bStop = true;
4958             }
4959             i++;
4960         }
4961     }
4962 }
4963 
4964 const OUString* SvNumberformat::GetNumForString( sal_uInt16 nNumFor, sal_uInt16 nPos,
4965             bool bString /* = false */ ) const
4966 {
4967     if ( nNumFor > 3 )
4968     {
4969         return nullptr;
4970     }
4971     sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
4972     if ( !nCnt )
4973     {
4974         return nullptr;
4975     }
4976     if ( nPos == 0xFFFF )
4977     {
4978         nPos = nCnt - 1;
4979         if ( bString )
4980         {   // Backwards
4981             short const * pType = NumFor[nNumFor].Info().nTypeArray.data() + nPos;
4982             while ( nPos > 0 && (*pType != NF_SYMBOLTYPE_STRING) &&
4983                     (*pType != NF_SYMBOLTYPE_CURRENCY) )
4984             {
4985                 pType--;
4986                 nPos--;
4987             }
4988             if ( (*pType != NF_SYMBOLTYPE_STRING) && (*pType != NF_SYMBOLTYPE_CURRENCY) )
4989             {
4990                 return nullptr;
4991             }
4992         }
4993     }
4994     else if ( nPos > nCnt - 1 )
4995     {
4996         return nullptr;
4997     }
4998     else if ( bString )
4999     {
5000         // forward
5001         short const * pType = NumFor[nNumFor].Info().nTypeArray.data() + nPos;
5002         while ( nPos < nCnt && (*pType != NF_SYMBOLTYPE_STRING) &&
5003                 (*pType != NF_SYMBOLTYPE_CURRENCY) )
5004         {
5005             pType++;
5006             nPos++;
5007         }
5008         if ( nPos >= nCnt || ((*pType != NF_SYMBOLTYPE_STRING) &&
5009                               (*pType != NF_SYMBOLTYPE_CURRENCY)) )
5010         {
5011             return nullptr;
5012         }
5013     }
5014     return &NumFor[nNumFor].Info().sStrArray[nPos];
5015 }
5016 
5017 short SvNumberformat::GetNumForType( sal_uInt16 nNumFor, sal_uInt16 nPos ) const
5018 {
5019     if ( nNumFor > 3 )
5020     {
5021         return 0;
5022     }
5023     sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
5024     if ( !nCnt )
5025     {
5026         return 0;
5027     }
5028     if ( nPos == 0xFFFF )
5029     {
5030         nPos = nCnt - 1;
5031     }
5032     else if ( nPos > nCnt - 1 )
5033     {
5034         return 0;
5035     }
5036     return NumFor[nNumFor].Info().nTypeArray[nPos];
5037 }
5038 
5039 bool SvNumberformat::IsNegativeWithoutSign() const
5040 {
5041     if ( IsSecondSubformatRealNegative() )
5042     {
5043         const OUString* pStr = GetNumForString( 1, 0, true );
5044         if ( pStr )
5045         {
5046             return !HasStringNegativeSign( *pStr );
5047         }
5048     }
5049     return false;
5050 }
5051 
5052 bool SvNumberformat::IsNegativeInBracket() const
5053 {
5054     sal_uInt16 nCnt = NumFor[1].GetCount();
5055     if (!nCnt)
5056     {
5057         return false;
5058     }
5059     auto& tmp = NumFor[1].Info().sStrArray;
5060     return tmp[0] == "(" && tmp[nCnt-1] == ")";
5061 }
5062 
5063 bool SvNumberformat::HasPositiveBracketPlaceholder() const
5064 {
5065     sal_uInt16 nCnt = NumFor[0].GetCount();
5066     return NumFor[0].Info().sStrArray[nCnt-1] == "_)";
5067 }
5068 
5069 DateOrder SvNumberformat::GetDateOrder() const
5070 {
5071     if ( eType & SvNumFormatType::DATE )
5072     {
5073         auto& rTypeArray = NumFor[0].Info().nTypeArray;
5074         sal_uInt16 nCnt = NumFor[0].GetCount();
5075         for ( sal_uInt16 j=0; j<nCnt; j++ )
5076         {
5077             switch ( rTypeArray[j] )
5078             {
5079             case NF_KEY_D :
5080             case NF_KEY_DD :
5081                 return DateOrder::DMY;
5082             case NF_KEY_M :
5083             case NF_KEY_MM :
5084             case NF_KEY_MMM :
5085             case NF_KEY_MMMM :
5086             case NF_KEY_MMMMM :
5087                 return DateOrder::MDY;
5088             case NF_KEY_YY :
5089             case NF_KEY_YYYY :
5090             case NF_KEY_EC :
5091             case NF_KEY_EEC :
5092             case NF_KEY_R :
5093             case NF_KEY_RR :
5094                 return DateOrder::YMD;
5095             }
5096         }
5097     }
5098     else
5099     {
5100        SAL_WARN( "svl.numbers", "SvNumberformat::GetDateOrder: no date" );
5101     }
5102     return rLoc().getDateOrder();
5103 }
5104 
5105 sal_uInt32 SvNumberformat::GetExactDateOrder() const
5106 {
5107     sal_uInt32 nRet = 0;
5108     if ( !(eType & SvNumFormatType::DATE) )
5109     {
5110         SAL_WARN( "svl.numbers", "SvNumberformat::GetExactDateOrder: no date" );
5111         return nRet;
5112     }
5113     auto& rTypeArray = NumFor[0].Info().nTypeArray;
5114     sal_uInt16 nCnt = NumFor[0].GetCount();
5115     int nShift = 0;
5116     for ( sal_uInt16 j=0; j<nCnt && nShift < 3; j++ )
5117     {
5118         switch ( rTypeArray[j] )
5119         {
5120         case NF_KEY_D :
5121         case NF_KEY_DD :
5122             nRet = (nRet << 8) | 'D';
5123             ++nShift;
5124             break;
5125         case NF_KEY_M :
5126         case NF_KEY_MM :
5127         case NF_KEY_MMM :
5128         case NF_KEY_MMMM :
5129         case NF_KEY_MMMMM :
5130             nRet = (nRet << 8) | 'M';
5131             ++nShift;
5132             break;
5133         case NF_KEY_YY :
5134         case NF_KEY_YYYY :
5135         case NF_KEY_EC :
5136         case NF_KEY_EEC :
5137         case NF_KEY_R :
5138         case NF_KEY_RR :
5139             nRet = (nRet << 8) | 'Y';
5140             ++nShift;
5141             break;
5142         }
5143     }
5144     return nRet;
5145 }
5146 
5147 void SvNumberformat::GetConditions( SvNumberformatLimitOps& rOper1, double& rVal1,
5148                                     SvNumberformatLimitOps& rOper2, double& rVal2 ) const
5149 {
5150     rOper1 = eOp1;
5151     rOper2 = eOp2;
5152     rVal1  = fLimit1;
5153     rVal2  = fLimit2;
5154 }
5155 
5156 const Color* SvNumberformat::GetColor( sal_uInt16 nNumFor ) const
5157 {
5158     if ( nNumFor > 3 )
5159     {
5160         return nullptr;
5161     }
5162     return NumFor[nNumFor].GetColor();
5163 }
5164 
5165 static void lcl_SvNumberformat_AddLimitStringImpl( OUString& rStr,
5166                                                    SvNumberformatLimitOps eOp,
5167                                                    double fLimit, std::u16string_view rDecSep )
5168 {
5169     if ( eOp == NUMBERFORMAT_OP_NO )
5170         return;
5171 
5172     switch ( eOp )
5173     {
5174     case NUMBERFORMAT_OP_EQ :
5175         rStr = "[=";
5176         break;
5177     case NUMBERFORMAT_OP_NE :
5178         rStr = "[<>";
5179         break;
5180     case NUMBERFORMAT_OP_LT :
5181         rStr = "[<";
5182         break;
5183     case NUMBERFORMAT_OP_LE :
5184         rStr = "[<=";
5185         break;
5186     case NUMBERFORMAT_OP_GT :
5187         rStr = "[>";
5188         break;
5189     case NUMBERFORMAT_OP_GE :
5190         rStr = "[>=";
5191         break;
5192     default:
5193         SAL_WARN( "svl.numbers", "unsupported number format" );
5194         break;
5195     }
5196     rStr +=  ::rtl::math::doubleToUString( fLimit,
5197                                            rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
5198                                            rDecSep[0], true);
5199     rStr += "]";
5200 }
5201 
5202 static void lcl_insertLCID( OUStringBuffer& rFormatStr, sal_uInt32 nLCID, sal_Int32 nPosInsertLCID, bool bDBNumInserted )
5203 {
5204     if ( nLCID == 0 )
5205         return;
5206     if (nPosInsertLCID == rFormatStr.getLength() && !bDBNumInserted)
5207         // No format code, no locale.
5208         return;
5209 
5210     auto aLCIDString = OUString::number( nLCID , 16 ).toAsciiUpperCase();
5211     // Search for only last DBNum which is the last element before insertion position
5212     if ( bDBNumInserted && nPosInsertLCID >= 8
5213         && aLCIDString.length > 4
5214         && OUString::unacquired(rFormatStr).match( "[DBNum", nPosInsertLCID-8) )
5215     {   // remove DBNumX code if long LCID
5216         nPosInsertLCID -= 8;
5217         rFormatStr.remove( nPosInsertLCID, 8 );
5218     }
5219     rFormatStr.insert( nPosInsertLCID, "[$-" + aLCIDString + "]" );
5220 }
5221 
5222 /** Increment nAlphabetID for CJK numerals
5223  * +1 for financial numerals [NatNum2]
5224  * +2 for Arabic fullwidth numerals [NatNum3]
5225  * */
5226 static void lcl_incrementAlphabetWithNatNum ( sal_uInt32& nAlphabetID, sal_uInt32 nNatNum )
5227 {
5228     if ( nNatNum == 2) // financial
5229         nAlphabetID += 1;
5230     else if ( nNatNum == 3)
5231         nAlphabetID += 2;
5232     nAlphabetID = nAlphabetID << 24;
5233 }
5234 
5235 OUString SvNumberformat::GetMappedFormatstring( const NfKeywordTable& rKeywords,
5236                                                 const LocaleDataWrapper& rLocWrp,
5237                                                 LanguageType nOriginalLang /* =LANGUAGE_DONTKNOW */,
5238                                                 bool bSystemLanguage /* =false */ ) const
5239 {
5240     OUStringBuffer aStr;
5241     if (maLocale.meSubstitute != LocaleType::Substitute::NONE)
5242     {
5243         // XXX: theoretically this could clash with the first subformat's
5244         // lcl_insertLCID() below, in practice as long as it is used for system
5245         // time and date modifiers it shouldn't (i.e. there is no calendar or
5246         // numeral specified as well).
5247         aStr.append("[$-" + maLocale.generateCode() + "]");
5248     }
5249     bool bDefault[4];
5250     // 1 subformat matches all if no condition specified,
5251     bDefault[0] = ( NumFor[1].GetCount() == 0 && eOp1 == NUMBERFORMAT_OP_NO );
5252     // with 2 subformats [>=0];[<0] is implied if no condition specified
5253     bDefault[1] = ( !bDefault[0] && NumFor[2].GetCount() == 0 &&
5254                     eOp1 == NUMBERFORMAT_OP_GE && fLimit1 == 0.0 &&
5255                     eOp2 == NUMBERFORMAT_OP_NO && fLimit2 == 0.0 );
5256     // with 3 or more subformats [>0];[<0];[=0] is implied if no condition specified,
5257     // note that subformats may be empty (;;;) and NumFor[2].GetCount()>0 is not checked.
5258     bDefault[2] = ( !bDefault[0] && !bDefault[1] &&
5259                     eOp1 == NUMBERFORMAT_OP_GT && fLimit1 == 0.0 &&
5260                     eOp2 == NUMBERFORMAT_OP_LT && fLimit2 == 0.0 );
5261     bool bDefaults = bDefault[0] || bDefault[1] || bDefault[2];
5262     // from now on bDefault[] values are used to append empty subformats at the end
5263     bDefault[3] = false;
5264     if ( !bDefaults )
5265     {
5266         // conditions specified
5267         if ( eOp1 != NUMBERFORMAT_OP_NO && eOp2 == NUMBERFORMAT_OP_NO )
5268         {
5269             bDefault[0] = bDefault[1] = true;                               // [];x
5270         }
5271         else if ( eOp1 != NUMBERFORMAT_OP_NO && eOp2 != NUMBERFORMAT_OP_NO &&
5272                   NumFor[2].GetCount() == 0 )
5273         {
5274             bDefault[0] = bDefault[1] = bDefault[2] = bDefault[3] = true;   // [];[];;
5275         }
5276         // nothing to do if conditions specified for every subformat
5277     }
5278     else if ( bDefault[0] )
5279     {
5280         bDefault[0] = false; // a single unconditional subformat is never delimited
5281     }
5282     else
5283     {
5284         if ( bDefault[2] && NumFor[2].GetCount() == 0 && NumFor[1].GetCount() > 0 )
5285         {
5286             bDefault[3] = true; // special cases x;x;; and ;x;;
5287         }
5288         for ( int i=0; i<3 && !bDefault[i]; ++i )
5289         {
5290             bDefault[i] = true;
5291         }
5292     }
5293     int nSem = 0; // needed ';' delimiters
5294     int nSub = 0; // subformats delimited so far
5295     for ( int n=0; n<4; n++ )
5296     {
5297         if ( n > 0 && NumFor[n].Info().eScannedType != SvNumFormatType::UNDEFINED )
5298         {
5299             nSem++;
5300         }
5301         OUString aPrefix;
5302 
5303         if ( !bDefaults )
5304         {
5305             switch ( n )
5306             {
5307             case 0 :
5308                 lcl_SvNumberformat_AddLimitStringImpl( aPrefix, eOp1,
5309                                                        fLimit1, rLocWrp.getNumDecimalSep() );
5310                 break;
5311             case 1 :
5312                 lcl_SvNumberformat_AddLimitStringImpl( aPrefix, eOp2,
5313                                                        fLimit2, rLocWrp.getNumDecimalSep() );
5314                 break;
5315             }
5316         }
5317 
5318         const OUString& rColorName = NumFor[n].GetColorName();
5319         if ( !rColorName.isEmpty() )
5320         {
5321             const NfKeywordTable & rKey = rScan.GetKeywords();
5322             for ( int j = NF_KEY_FIRSTCOLOR; j <= NF_KEY_LASTCOLOR; j++ )
5323             {
5324                 if ( rKey[j] == rColorName )
5325                 {
5326                     aPrefix += "[" + rKeywords[j] + "]";
5327                     break;  // for
5328                 }
5329             }
5330         }
5331 
5332         SvNumberNatNum aNatNum = NumFor[n].GetNatNum();
5333         bool bDBNumInserted = false;
5334         if (aNatNum.IsComplete() && (aNatNum.GetDBNum() > 0 || nOriginalLang != LANGUAGE_DONTKNOW))
5335         {   // GetFormatStringForExcel() may have changed language to en_US
5336             if (aNatNum.GetLang() == LANGUAGE_ENGLISH_US && nOriginalLang != LANGUAGE_DONTKNOW)
5337                 aNatNum.SetLang( nOriginalLang );
5338             if ( aNatNum.GetDBNum() > 0 )
5339             {
5340                 aPrefix += "[DBNum" + OUString::number( aNatNum.GetDBNum() ) + "]";
5341                 bDBNumInserted = true;
5342             }
5343         }
5344 
5345         sal_uInt16 nCnt = NumFor[n].GetCount();
5346         if ( nSem && (nCnt || !aPrefix.isEmpty()) )
5347         {
5348             for ( ; nSem; --nSem )
5349             {
5350                 aStr.append( ';' );
5351             }
5352             for ( ; nSub <= n; ++nSub )
5353             {
5354                 bDefault[nSub] = false;
5355             }
5356         }
5357 
5358         if ( !aPrefix.isEmpty() )
5359         {
5360             aStr.append( aPrefix );
5361         }
5362         sal_Int32 nPosHaveLCID = -1;
5363         sal_Int32 nPosInsertLCID = aStr.getLength();
5364         sal_uInt32 nCalendarID = 0x0000000; // Excel ID of calendar used in sub-format see tdf#36038
5365         constexpr sal_uInt32 kCalGengou = 0x0030000;
5366         if ( nCnt )
5367         {
5368             auto& rTypeArray = NumFor[n].Info().nTypeArray;
5369             auto& rStrArray = NumFor[n].Info().sStrArray;
5370             for ( sal_uInt16 j=0; j<nCnt; j++ )
5371             {
5372                 if ( 0 <= rTypeArray[j] && rTypeArray[j] < NF_KEYWORD_ENTRIES_COUNT )
5373                 {
5374                     aStr.append( rKeywords[rTypeArray[j]] );
5375                     if( NF_KEY_NNNN == rTypeArray[j] )
5376                     {
5377                         aStr.append( rLocWrp.getLongDateDayOfWeekSep() );
5378                     }
5379                     switch (rTypeArray[j])
5380                     {
5381                         case NF_KEY_EC:
5382                         case NF_KEY_EEC:
5383                         case NF_KEY_R:
5384                         case NF_KEY_RR:
5385                             // Implicit secondary (non-gregorian) calendar.
5386                             // Currently only for ja-JP.
5387                             /* TODO: same for all locales in
5388                              * LocaleDataWrapper::doesSecondaryCalendarUseEC() ?
5389                              * Should split the locales off that then. */
5390                             if (!nCalendarID)
5391                             {
5392                                 const LanguageType nLang = MsLangId::getRealLanguage( nOriginalLang);
5393                                 if (nLang == LANGUAGE_JAPANESE)
5394                                     nCalendarID = kCalGengou;
5395                             }
5396                         break;
5397                         default:
5398                             ;   // nothing
5399                     }
5400                 }
5401                 else
5402                 {
5403                     switch ( rTypeArray[j] )
5404                     {
5405                     case NF_SYMBOLTYPE_DECSEP :
5406                         aStr.append( rLocWrp.getNumDecimalSep() );
5407                         break;
5408                     case NF_SYMBOLTYPE_THSEP :
5409                         aStr.append( rLocWrp.getNumThousandSep() );
5410                         break;
5411                     case NF_SYMBOLTYPE_EXP :
5412                         aStr.append( rKeywords[NF_KEY_E] );
5413                         if ( rStrArray[j].getLength() > 1 && rStrArray[j][1] == '+' )
5414                             aStr.append( "+" );
5415                         else
5416                         // tdf#102370: Excel code for exponent without sign
5417                             aStr.append( "-" );
5418                         break;
5419                     case NF_SYMBOLTYPE_DATESEP :
5420                         aStr.append( rLocWrp.getDateSep() );
5421                         break;
5422                     case NF_SYMBOLTYPE_TIMESEP :
5423                         aStr.append( rLocWrp.getTimeSep() );
5424                         break;
5425                     case NF_SYMBOLTYPE_TIME100SECSEP :
5426                         aStr.append( rLocWrp.getTime100SecSep() );
5427                         break;
5428                     case NF_SYMBOLTYPE_FRACBLANK :
5429                     case NF_SYMBOLTYPE_STRING :
5430                         if ( rStrArray[j].getLength() == 1 )
5431                         {
5432                             if ( rTypeArray[j] == NF_SYMBOLTYPE_STRING )
5433                                 aStr.append( '\\' );
5434                             aStr.append( rStrArray[j] );
5435                         }
5436                         else
5437                         {
5438                             aStr.append( '"' );
5439                             aStr.append( rStrArray[j] );
5440                             aStr.append( '"' );
5441                         }
5442                         break;
5443                     case NF_SYMBOLTYPE_CALDEL :
5444                         if (j + 1 >= nCnt)
5445                             break;
5446                         if ( rStrArray[j+1] == "gengou" )
5447                         {
5448                             nCalendarID = kCalGengou;
5449                         }
5450                         else if ( rStrArray[j+1] == "hijri" )
5451                         {
5452                             nCalendarID = 0x0060000;
5453                         }
5454                         else if ( rStrArray[j+1] == "buddhist" )
5455                         {
5456                             nCalendarID = 0x0070000;
5457                         }
5458                         else if ( rStrArray[j+1] == "jewish" )
5459                         {
5460                             nCalendarID = 0x0080000;
5461                         }
5462                         // Other calendars (see tdf#36038) not corresponding between LibO and XL.
5463                         // However, skip any calendar modifier and don't write
5464                         // as format code (if not as literal string).
5465                         j += 2;
5466                         break;
5467                     case NF_SYMBOLTYPE_CURREXT :
5468                         nPosHaveLCID = aStr.getLength();
5469                         aStr.append( rStrArray[j] );
5470                         break;
5471                     default:
5472                         aStr.append( rStrArray[j] );
5473                     }
5474                 }
5475             }
5476         }
5477         sal_uInt32 nAlphabetID = 0x0000000; // Excel ID of alphabet used for numerals see tdf#36038
5478         LanguageType nLanguageID = LANGUAGE_SYSTEM;
5479         if ( aNatNum.IsComplete() )
5480         {
5481             nLanguageID = MsLangId::getRealLanguage( aNatNum.GetLang());
5482             if ( aNatNum.GetNatNum() == 0 )
5483             {
5484                 nAlphabetID = 0x01000000;  // Arabic-european numerals
5485             }
5486             else if ( nCalendarID > 0 || aNatNum.GetDBNum() == 0 || aNatNum.GetDBNum() == aNatNum.GetNatNum() )
5487             {   // if no DBNum code then use long LCID
5488                 // if DBNum value != NatNum value, use DBNum and not extended LCID
5489                 // if calendar, then DBNum will be removed
5490                 LanguageType pri = primary(nLanguageID);
5491                 if ( pri == LANGUAGE_ARABIC_PRIMARY_ONLY )
5492                         nAlphabetID = 0x02000000;  // Arabic-indic numerals
5493                 else if ( pri == primary(LANGUAGE_FARSI) )
5494                         nAlphabetID = 0x03000000;  // Farsi numerals
5495                 else if ( pri.anyOf(
5496                     primary(LANGUAGE_HINDI),
5497                     primary(LANGUAGE_MARATHI),
5498                     primary(LANGUAGE_NEPALI) ))
5499                         nAlphabetID = 0x04000000;  // Devanagari numerals
5500                 else if ( pri == primary(LANGUAGE_BENGALI) )
5501                         nAlphabetID = 0x05000000;  // Bengali numerals
5502                 else if ( pri == primary(LANGUAGE_PUNJABI) )
5503                 {
5504                     if ( nLanguageID == LANGUAGE_PUNJABI_ARABIC_LSO )
5505                         nAlphabetID =  0x02000000;  // Arabic-indic numerals
5506                     else
5507                         nAlphabetID = 0x06000000;  // Punjabi numerals
5508                 }
5509                 else if ( pri == primary(LANGUAGE_GUJARATI) )
5510                         nAlphabetID = 0x07000000;  // Gujarati numerals
5511                 else if ( pri == primary(LANGUAGE_ODIA))
5512                         nAlphabetID = 0x08000000;  // Odia (Oriya) numerals
5513                 else if ( pri == primary(LANGUAGE_TAMIL))
5514                         nAlphabetID = 0x09000000;  // Tamil numerals
5515                 else if ( pri == primary(LANGUAGE_TELUGU))
5516                         nAlphabetID = 0x0A000000;  // Telugu numerals
5517                 else if ( pri == primary(LANGUAGE_KANNADA))
5518                         nAlphabetID = 0x0B000000;  // Kannada numerals
5519                 else if ( pri == primary(LANGUAGE_MALAYALAM))
5520                         nAlphabetID = 0x0C000000;  // Malayalam numerals
5521                 else if ( pri == primary(LANGUAGE_THAI))
5522                 {
5523                     // The Thai T NatNum modifier during Xcl export.
5524                     if ( rKeywords[NF_KEY_THAI_T] == "T" )
5525                         nAlphabetID = 0x0D000000;  // Thai numerals
5526                 }
5527                 else if ( pri == primary(LANGUAGE_LAO))
5528                         nAlphabetID = 0x0E000000;  // Lao numerals
5529                 else if ( pri == primary(LANGUAGE_TIBETAN))
5530                         nAlphabetID = 0x0F000000;  // Tibetan numerals
5531                 else if ( pri == primary(LANGUAGE_BURMESE))
5532                         nAlphabetID = 0x10000000;  // Burmese numerals
5533                 else if ( pri == primary(LANGUAGE_TIGRIGNA_ETHIOPIA))
5534                         nAlphabetID = 0x11000000;  // Tigrigna numerals
5535                 else if ( pri == primary(LANGUAGE_KHMER))
5536                         nAlphabetID = 0x12000000;  // Khmer numerals
5537                 else if ( pri == primary(LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA))
5538                 {
5539                     if ( nLanguageID != LANGUAGE_MONGOLIAN_CYRILLIC_MONGOLIA
5540                       && nLanguageID != LANGUAGE_MONGOLIAN_CYRILLIC_LSO )
5541                         nAlphabetID = 0x13000000;  // Mongolian numerals
5542                 }
5543                     // CJK numerals
5544                 else if ( pri == primary(LANGUAGE_JAPANESE))
5545                 {
5546                     nAlphabetID = 0x1B;
5547                     lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() );
5548                 }
5549                 else if ( pri == primary(LANGUAGE_CHINESE))
5550                 {
5551                     if ( nLanguageID == LANGUAGE_CHINESE_TRADITIONAL
5552                       || nLanguageID == LANGUAGE_CHINESE_HONGKONG
5553                       || nLanguageID == LANGUAGE_CHINESE_MACAU )
5554                     {
5555                         nAlphabetID = 0x21;
5556                         lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() );
5557                     }
5558                     else // LANGUAGE_CHINESE_SIMPLIFIED
5559                     {
5560                         nAlphabetID = 0x1E;
5561                         lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() );
5562                     }
5563                 }
5564                 else if ( pri == primary(LANGUAGE_KOREAN))
5565                 {
5566                     if ( aNatNum.GetNatNum() == 9 ) // Hangul
5567                     {
5568                         nAlphabetID = 0x27000000;
5569                     }
5570                     else
5571                     {
5572                         nAlphabetID = 0x24;
5573                         lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() );
5574                     }
5575                 }
5576             }
5577             // Add LCID to DBNum
5578             if ( aNatNum.GetDBNum() > 0 && nLanguageID == LANGUAGE_SYSTEM )
5579                 nLanguageID = MsLangId::getRealLanguage( aNatNum.GetLang());
5580         }
5581         else if (nPosHaveLCID < 0)
5582         {
5583             // Do not insert a duplicated LCID that was already given with a
5584             // currency format as [$R-1C09]
5585             if (!bSystemLanguage && nOriginalLang != LANGUAGE_DONTKNOW)
5586             {
5587                 // Explicit locale, write only to the first subformat.
5588                 if (n == 0)
5589                     nLanguageID = MsLangId::getRealLanguage( nOriginalLang);
5590             }
5591             else if (bSystemLanguage && maLocale.meLanguageWithoutLocaleData != LANGUAGE_DONTKNOW)
5592             {
5593                 // Explicit locale but no locale data thus assigned to system
5594                 // locale, preserve for roundtrip, write only to the first
5595                 // subformat.
5596                 if (n == 0)
5597                     nLanguageID = maLocale.meLanguageWithoutLocaleData;
5598             }
5599         }
5600         if ( nCalendarID > 0 )
5601         {   // Add alphabet and language to calendar
5602             if ( nAlphabetID == 0 )
5603                 nAlphabetID = 0x01000000;
5604             if ( nLanguageID == LANGUAGE_SYSTEM && nOriginalLang != LANGUAGE_DONTKNOW )
5605                 nLanguageID = nOriginalLang;
5606         }
5607         lcl_insertLCID( aStr, nAlphabetID + nCalendarID + static_cast<sal_uInt16>(nLanguageID), nPosInsertLCID,
5608                 bDBNumInserted);
5609     }
5610     for ( ; nSub<4 && bDefault[nSub]; ++nSub )
5611     {   // append empty subformats
5612         aStr.append( ';' );
5613     }
5614     return aStr.makeStringAndClear();
5615 }
5616 
5617 OUString SvNumberformat::ImpGetNatNumString( const SvNumberNatNum& rNum,
5618                                            sal_Int64 nVal, sal_uInt16 nMinDigits ) const
5619 {
5620     OUString aStr;
5621     if ( nMinDigits )
5622     {
5623         if ( nMinDigits == 2 )
5624         {
5625             // speed up the most common case
5626             if ( 0 <= nVal && nVal < 10 )
5627             {
5628                 sal_Unicode aBuf[2];
5629                 aBuf[0] = '0';
5630                 aBuf[1] = '0' + nVal;
5631                 aStr = OUString(aBuf, SAL_N_ELEMENTS(aBuf));
5632             }
5633             else
5634             {
5635                 aStr = OUString::number( nVal );
5636             }
5637         }
5638         else
5639         {
5640             OUString aValStr( OUString::number( nVal ) );
5641             if ( aValStr.getLength() >= nMinDigits )
5642             {
5643                 aStr = aValStr;
5644             }
5645             else
5646             {
5647                 OUStringBuffer aBuf;
5648                 for(sal_Int32 index = 0; index < nMinDigits - aValStr.getLength(); ++index)
5649                 {
5650                     aBuf.append('0');
5651                 }
5652                 aBuf.append(aValStr);
5653                 aStr = aBuf.makeStringAndClear();
5654             }
5655         }
5656     }
5657     else
5658     {
5659         aStr = OUString::number( nVal );
5660     }
5661     return impTransliterate(aStr, rNum);
5662 }
5663 
5664 OUString SvNumberformat::impTransliterateImpl(const OUString& rStr,
5665                                               const SvNumberNatNum& rNum ) const
5666 {
5667     css::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() );
5668     return GetFormatter().GetNatNum()->getNativeNumberStringParams(rStr, aLocale, rNum.GetNatNum(),
5669                                                                    rNum.GetParams());
5670 }
5671 
5672 void SvNumberformat::impTransliterateImpl(OUStringBuffer& rStr,
5673                                           const SvNumberNatNum& rNum ) const
5674 {
5675     css::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() );
5676 
5677     rStr = GetFormatter().GetNatNum()->getNativeNumberStringParams(
5678         OUString::unacquired(rStr), aLocale, rNum.GetNatNum(), rNum.GetParams());
5679 }
5680 
5681 OUString SvNumberformat::impTransliterateImpl(const OUString& rStr,
5682                                               const SvNumberNatNum& rNum,
5683                                               const sal_uInt16 nDateKey) const
5684 {
5685     // no KEYWORD=argument list in NatNum12
5686     if (rNum.GetParams().indexOf('=') == -1)
5687         return impTransliterateImpl( rStr, rNum);
5688 
5689     const NfKeywordTable & rKeywords = rScan.GetKeywords();
5690 
5691     // Format: KEYWORD=numbertext_prefix, ..., for example:
5692     // [NatNum12 YYYY=title ordinal,MMMM=article, D=ordinal-number]
5693     sal_Int32 nField = -1;
5694     do
5695     {
5696         nField = rNum.GetParams().indexOf(Concat2View(rKeywords[nDateKey] + "="), ++nField);
5697     }
5698     while (nField != -1 && nField != 0 &&
5699             (rNum.GetParams()[nField - 1] != ',' &&
5700               rNum.GetParams()[nField - 1] != ' '));
5701 
5702     // no format specified for actual keyword
5703     if (nField == -1)
5704         return rStr;
5705 
5706     sal_Int32 nKeywordLen = rKeywords[nDateKey].getLength() + 1;
5707     sal_Int32 nFieldEnd = rNum.GetParams().indexOf(',', nField);
5708 
5709     if (nFieldEnd == -1)
5710         nFieldEnd = rNum.GetParams().getLength();
5711 
5712     css::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() );
5713 
5714     return GetFormatter().GetNatNum()->getNativeNumberStringParams(
5715         rStr, aLocale, rNum.GetNatNum(),
5716         rNum.GetParams().copy(nField + nKeywordLen, nFieldEnd - nField - nKeywordLen));
5717 }
5718 
5719 void SvNumberformat::GetNatNumXml( css::i18n::NativeNumberXmlAttributes2& rAttr,
5720                                    sal_uInt16 nNumFor ) const
5721 {
5722     if ( nNumFor <= 3 )
5723     {
5724         const SvNumberNatNum& rNum = NumFor[nNumFor].GetNatNum();
5725         if ( rNum.IsSet() )
5726         {
5727             css::lang::Locale aLocale(
5728                     LanguageTag( rNum.GetLang() ).getLocale() );
5729 
5730             /* TODO: a new XNativeNumberSupplier2::convertToXmlAttributes()
5731              * should rather return NativeNumberXmlAttributes2 and places
5732              * adapted, and whether to fill Spellout or something different
5733              * should be internal there. */
5734             css::i18n::NativeNumberXmlAttributes aTmp(
5735                     GetFormatter().GetNatNum()->convertToXmlAttributes(
5736                         aLocale, rNum.GetNatNum()));
5737             rAttr.Locale = aTmp.Locale;
5738             rAttr.Format = aTmp.Format;
5739             rAttr.Style = aTmp.Style;
5740             if ( NatNumTakesParameters(rNum.GetNatNum()) )
5741             {
5742                 // NatNum12 spell out numbers, dates and money amounts
5743                 rAttr.Spellout = rNum.GetParams();
5744                 // Mutually exclusive.
5745                 rAttr.Format.clear();
5746                 rAttr.Style.clear();
5747             }
5748             else
5749             {
5750                 rAttr.Spellout.clear();
5751             }
5752         }
5753         else
5754         {
5755             rAttr = css::i18n::NativeNumberXmlAttributes2();
5756         }
5757     }
5758     else
5759     {
5760         rAttr = css::i18n::NativeNumberXmlAttributes2();
5761     }
5762 }
5763 
5764 OUString SvNumberformat::GetNatNumModifierString( sal_uInt16 nNumFor ) const
5765 {
5766     if ( nNumFor > 3 )
5767         return "";
5768     const SvNumberNatNum& rNum = NumFor[nNumFor].GetNatNum();
5769     if ( !rNum.IsSet() )
5770         return "";
5771     OUStringBuffer sNatNumModifier = "[NatNum";
5772     const sal_Int32 nNum = rNum.GetNatNum();
5773     sNatNumModifier.append( nNum );
5774     if ( NatNumTakesParameters( nNum ) )
5775     {
5776         sNatNumModifier.append( " " );
5777         sNatNumModifier.append( rNum.GetParams() );
5778     }
5779     sNatNumModifier.append( "]" );
5780 
5781     return sNatNumModifier.makeStringAndClear();
5782 }
5783 
5784 // static
5785 bool SvNumberformat::HasStringNegativeSign( const OUString& rStr )
5786 {
5787     // For Sign '-' needs to be at the start or at the end of the string (blanks ignored)
5788     sal_Int32 nLen = rStr.getLength();
5789     if ( !nLen )
5790     {
5791         return false;
5792     }
5793     const sal_Unicode* const pBeg = rStr.getStr();
5794     const sal_Unicode* const pEnd = pBeg + nLen;
5795     const sal_Unicode* p = pBeg;
5796     do
5797     {   // Start
5798         if ( *p == '-' )
5799         {
5800             return true;
5801         }
5802     }
5803     while ( *p == ' ' && ++p < pEnd );
5804 
5805     p = pEnd - 1;
5806 
5807     do
5808     {   // End
5809         if ( *p == '-' )
5810         {
5811             return true;
5812         }
5813     }
5814     while ( *p == ' ' && pBeg < --p );
5815     return false;
5816 }
5817 
5818 // static
5819 bool SvNumberformat::IsInQuote( const OUString& rStr, sal_Int32 nPos,
5820                                 sal_Unicode cQuote, sal_Unicode cEscIn, sal_Unicode cEscOut )
5821 {
5822     sal_Int32 nLen = rStr.getLength();
5823     if ( nPos >= nLen )
5824     {
5825         return false;
5826     }
5827     const sal_Unicode* p0 = rStr.getStr();
5828     const sal_Unicode* p = p0;
5829     const sal_Unicode* p1 = p0 + nPos;
5830     bool bQuoted = false;
5831     while ( p <= p1 )
5832     {
5833         if ( *p == cQuote )
5834         {
5835             if ( p == p0 )
5836             {
5837                 bQuoted = true;
5838             }
5839             else if ( bQuoted )
5840             {
5841                 if ( *(p-1) != cEscIn )
5842                 {
5843                     bQuoted = false;
5844                 }
5845             }
5846             else
5847             {
5848                 if ( *(p-1) != cEscOut )
5849                 {
5850                     bQuoted = true;
5851                 }
5852             }
5853         }
5854         p++;
5855     }
5856     return bQuoted;
5857 }
5858 
5859 // static
5860 sal_Int32 SvNumberformat::GetQuoteEnd( const OUString& rStr, sal_Int32 nPos,
5861                                        sal_Unicode cQuote, sal_Unicode cEscIn )
5862 {
5863     if ( nPos < 0 )
5864     {
5865         return -1;
5866     }
5867     sal_Int32 nLen = rStr.getLength();
5868     if ( nPos >= nLen )
5869     {
5870         return -1;
5871     }
5872     if ( !IsInQuote( rStr, nPos, cQuote, cEscIn ) )
5873     {
5874         if ( rStr[ nPos ] == cQuote )
5875         {
5876             return nPos; // Closing cQuote
5877         }
5878         return -1;
5879     }
5880     const sal_Unicode* p0 = rStr.getStr();
5881     const sal_Unicode* p = p0 + nPos;
5882     const sal_Unicode* p1 = p0 + nLen;
5883     while ( p < p1 )
5884     {
5885         if ( *p == cQuote && p > p0 && *(p-1) != cEscIn )
5886         {
5887             return sal::static_int_cast< sal_Int32 >(p - p0);
5888         }
5889         p++;
5890     }
5891     return nLen; // End of String
5892 }
5893 
5894 sal_uInt16 SvNumberformat::GetNumForNumberElementCount( sal_uInt16 nNumFor ) const
5895 {
5896     if ( nNumFor < 4 )
5897     {
5898         sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
5899         return nCnt - ImpGetNumForStringElementCount( nNumFor );
5900     }
5901     return 0;
5902 }
5903 
5904 sal_uInt16 SvNumberformat::ImpGetNumForStringElementCount( sal_uInt16 nNumFor ) const
5905 {
5906     sal_uInt16 nCnt = 0;
5907     sal_uInt16 nNumForCnt = NumFor[nNumFor].GetCount();
5908     auto& rTypeArray = NumFor[nNumFor].Info().nTypeArray;
5909     for ( sal_uInt16 j=0; j<nNumForCnt; ++j )
5910     {
5911         switch ( rTypeArray[j] )
5912         {
5913         case NF_SYMBOLTYPE_STRING:
5914         case NF_SYMBOLTYPE_CURRENCY:
5915         case NF_SYMBOLTYPE_DATESEP:
5916         case NF_SYMBOLTYPE_TIMESEP:
5917         case NF_SYMBOLTYPE_TIME100SECSEP:
5918         case NF_SYMBOLTYPE_PERCENT:
5919             ++nCnt;
5920             break;
5921         }
5922     }
5923     return nCnt;
5924 }
5925 
5926 bool SvNumberformat::IsMinuteSecondFormat() const
5927 {
5928     if (GetMaskedType() != SvNumFormatType::TIME)
5929         return false;
5930 
5931     constexpr sal_uInt16 k00 = 0x00;    // Nada, Nilch
5932     constexpr sal_uInt16 kLB = 0x01;    // '[' Left Bracket
5933     constexpr sal_uInt16 kRB = 0x02;    // ']' Right Bracket
5934     constexpr sal_uInt16 kMM = 0x04;    // M or MM
5935     constexpr sal_uInt16 kTS = 0x08;    // Time Separator
5936     constexpr sal_uInt16 kSS = 0x10;    // S or SS
5937 #define HAS_MINUTE_SECOND(state) ((state) == (kMM|kTS|kSS) || (state) == (kLB|kMM|kRB|kTS|kSS))
5938     // Also (kMM|kTS|kLB|kSS|kRB) but those are the same bits.
5939 
5940     sal_uInt16 nState = k00;
5941     bool bSep = false;
5942     sal_uInt16 nNumForCnt = NumFor[0].GetCount();
5943     auto const & rTypeArray = NumFor[0].Info().nTypeArray;
5944     for (sal_uInt16 j=0; j < nNumForCnt; ++j)
5945     {
5946         switch (rTypeArray[j])
5947         {
5948             case NF_SYMBOLTYPE_DEL:
5949                 {
5950                     // '[' or ']' before/after MM or SS
5951                     const OUString& rStr = NumFor[0].Info().sStrArray[j];
5952                     if (rStr == "[")
5953                     {
5954                         if (nState != k00 && nState != (kMM|kTS))
5955                             return false;
5956                         nState |= kLB;
5957                     }
5958                     else if (rStr == "]")
5959                     {
5960                         if (nState != (kLB|kMM) && nState != (kMM|kTS|kLB|kSS))
5961                             return false;
5962                         nState |= kRB;
5963                     }
5964                     else
5965                         return false;
5966                 }
5967             break;
5968             case NF_KEY_MI:
5969             case NF_KEY_MMI:
5970                 if (nState != k00 && nState != kLB)
5971                     return false;
5972                 nState |= kMM;
5973             break;
5974             case NF_SYMBOLTYPE_TIMESEP:
5975                 if (nState != kMM && nState != (kLB|kMM|kRB))
5976                     return false;
5977                 nState |= kTS;
5978             break;
5979             case NF_KEY_S:
5980             case NF_KEY_SS:
5981                 if (nState != (kMM|kTS) && nState != (kLB|kMM|kRB|kTS) && nState != (kMM|kTS|kLB))
5982                     return false;
5983                 nState |= kSS;
5984             break;
5985             case NF_SYMBOLTYPE_TIME100SECSEP:
5986                 // Trailing fraction of seconds allowed.
5987                 if (!HAS_MINUTE_SECOND(nState))
5988                     return false;
5989                 bSep = true;
5990             break;
5991             case NF_SYMBOLTYPE_DIGIT:
5992                 if (!bSep)
5993                     return false;
5994             break;
5995             case NF_SYMBOLTYPE_STRING:
5996                 // nothing, display literal
5997             break;
5998             default:
5999                 return false;
6000         }
6001     }
6002     return HAS_MINUTE_SECOND(nState);
6003 #undef HAS_MINUTE_SECOND
6004 }
6005 
6006 OUString SvNumberformat::GetFormatStringForTimePrecision( int nPrecision ) const
6007 {
6008     OUStringBuffer sString;
6009     using comphelper::string::padToLength;
6010 
6011     sal_uInt16 nNumForCnt = NumFor[0].GetCount();
6012     auto const & rTypeArray = NumFor[0].Info().nTypeArray;
6013     for (sal_uInt16 j=0; j < nNumForCnt; ++j)
6014     {
6015         switch (rTypeArray[j])
6016         {
6017             case NF_KEY_S :
6018             case NF_KEY_SS:
6019                 sString.append( NumFor[0].Info().sStrArray[j] );
6020                 if ( j > 0 && rTypeArray[j-1] == NF_SYMBOLTYPE_DEL && j < nNumForCnt-1 )
6021                 {
6022                     j++;
6023                     sString.append( NumFor[0].Info().sStrArray[j] );
6024                 }
6025                 if (nPrecision > 0)
6026                 {
6027                     sString.append( rLoc().getTime100SecSep() );
6028                     padToLength(sString, sString.getLength() + nPrecision, '0');
6029                 }
6030                 break;
6031             case NF_SYMBOLTYPE_TIME100SECSEP:
6032             case NF_SYMBOLTYPE_DIGIT:
6033                 break;
6034             case NF_SYMBOLTYPE_STRING:
6035                 sString.append( "\"" );
6036                 [[fallthrough]];
6037             default:
6038                 sString.append( NumFor[0].Info().sStrArray[j] );
6039                 if (rTypeArray[j] == NF_SYMBOLTYPE_STRING)
6040                 {
6041                     sString.append( "\"" );
6042                 }
6043         }
6044     }
6045 
6046     return sString.makeStringAndClear();
6047 }
6048 
6049 sal_uInt16 SvNumberformat::GetThousandDivisorPrecision( sal_uInt16 nIx ) const
6050 {
6051     if (nIx >= 4)
6052         return 0;
6053 
6054     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
6055 
6056     if (rInfo.eScannedType != SvNumFormatType::NUMBER && rInfo.eScannedType != SvNumFormatType::CURRENCY)
6057         return 0;
6058 
6059     if (rInfo.nThousand == FLAG_STANDARD_IN_FORMAT)
6060         return SvNumberFormatter::UNLIMITED_PRECISION;
6061 
6062     return rInfo.nThousand * 3;
6063 }
6064 
6065 const CharClass& SvNumberformat::rChrCls() const
6066 {
6067     return rScan.GetChrCls();
6068 }
6069 
6070 const LocaleDataWrapper& SvNumberformat::rLoc() const
6071 {
6072     return rScan.GetLoc();
6073 }
6074 
6075 CalendarWrapper& SvNumberformat::GetCal() const
6076 {
6077     return rScan.GetCal();
6078 }
6079 
6080 const SvNumberFormatter& SvNumberformat::GetFormatter() const
6081 {
6082     return *rScan.GetNumberformatter();
6083 }
6084 
6085 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
6086