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