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