1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <string_view>
23 #include <comphelper/string.hxx>
24 #include <sal/log.hxx>
25 #include <tools/debug.hxx>
26 #include <tools/long.hxx>
27 #include <i18nlangtag/mslangid.hxx>
28 #include <rtl/math.hxx>
29 #include <unotools/charclass.hxx>
30 #include <unotools/calendarwrapper.hxx>
31 #include <unotools/nativenumberwrapper.hxx>
32 #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
33 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
34 #include <com/sun/star/i18n/CalendarDisplayCode.hpp>
35 #include <com/sun/star/i18n/AmPmValue.hpp>
36 #include <com/sun/star/i18n/NativeNumberMode.hpp>
37 #include <com/sun/star/i18n/NativeNumberXmlAttributes2.hpp>
39 #include <svl/zformat.hxx>
40 #include "zforscan.hxx"
42 #include "zforfind.hxx"
43 #include <svl/zforlist.hxx>
44 #include <unotools/digitgroupingiterator.hxx>
45 #include <svl/nfsymbol.hxx>
54 constexpr OUStringLiteral GREGORIAN
= u
"gregorian";
56 const sal_uInt16 UPPER_PRECISION
= 300; // entirely arbitrary...
57 const double EXP_LOWER_BOUND
= 1.0E-4; // prefer scientific notation below this value.
58 const double EXP_ABS_UPPER_BOUND
= 1.0E15
; // use exponential notation above that absolute value.
59 // Back in time was E16 that lead
60 // to display rounding errors, see
61 // also sal/rtl/math.cxx
66 const double D_MAX_U_INT32
= double(0xffffffff); // 4294967295.0
67 constexpr double D_MAX_INTEGER
= (sal_uInt64(1) << 53) - 1;
69 const double D_MAX_D_BY_100
= 1.7E306
;
70 const double D_MIN_M_BY_1000
= 2.3E-305;
72 const sal_uInt8 cCharWidths
[ 128-32 ] = {
73 1,1,1,2,2,3,2,1,1,1,1,2,1,1,1,1,
74 2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,
75 3,2,2,2,2,2,2,3,2,1,2,2,2,3,3,3,
76 2,3,2,2,2,2,2,3,2,2,2,1,1,1,2,2,
77 1,2,2,2,2,2,1,2,2,1,1,2,1,3,2,2,
78 2,2,1,2,1,2,2,2,2,2,2,1,1,1,2,1
82 sal_Int32
SvNumberformat::InsertBlanks( OUStringBuffer
& r
, sal_Int32 nPos
, sal_Unicode c
)
86 int n
= 2; // Default for chars > 128 (HACK!)
89 n
= static_cast<int>(cCharWidths
[ c
- 32 ]);
93 r
.insert( nPos
++, ' ');
99 static tools::Long
GetPrecExp( double fAbsVal
)
101 DBG_ASSERT( fAbsVal
> 0.0, "GetPrecExp: fAbsVal <= 0.0" );
102 if ( fAbsVal
< 1e-7 || fAbsVal
> 1e7
)
104 // Shear: whether it's faster or not, falls in between 1e6 and 1e7
105 return static_cast<tools::Long
>(floor( log10( fAbsVal
) )) + 1;
109 tools::Long nPrecExp
= 1;
115 while( fAbsVal
>= 10 )
128 void ImpSvNumberformatInfo::Copy( const ImpSvNumberformatInfo
& rNumFor
, sal_uInt16 nCnt
)
130 for (sal_uInt16 i
= 0; i
< nCnt
; ++i
)
132 sStrArray
[i
] = rNumFor
.sStrArray
[i
];
133 nTypeArray
[i
] = rNumFor
.nTypeArray
[i
];
135 eScannedType
= rNumFor
.eScannedType
;
136 bThousand
= rNumFor
.bThousand
;
137 nThousand
= rNumFor
.nThousand
;
138 nCntPre
= rNumFor
.nCntPre
;
139 nCntPost
= rNumFor
.nCntPost
;
140 nCntExp
= rNumFor
.nCntExp
;
143 const std::map
<LanguageType
, std::array
<sal_uInt8
, 4>> tblDBNumToNatNum
144 = { { primary(LANGUAGE_CHINESE
), { 4, 5, 3, 0 } },
145 { primary(LANGUAGE_JAPANESE
), { 4, 5, 3, 0 } },
146 { primary(LANGUAGE_KOREAN
), { 4, 5, 6, 10 } } };
149 sal_uInt8
SvNumberNatNum::MapDBNumToNatNum( sal_uInt8 nDBNum
, LanguageType eLang
, bool bDate
)
151 sal_uInt8 nNatNum
= 0;
152 eLang
= MsLangId::getRealLanguage( eLang
); // resolve SYSTEM etc.
153 eLang
= primary(eLang
); // 10 bit primary language
156 if ( nDBNum
== 4 && eLang
== primary(LANGUAGE_KOREAN
) )
160 else if ( nDBNum
<= 3 )
162 nNatNum
= nDBNum
; // known to be good for: zh,ja,ko / 1,2,3
167 if (1 <= nDBNum
&& nDBNum
<= 4)
169 auto const it
= tblDBNumToNatNum
.find(eLang
);
170 if (it
!= tblDBNumToNatNum
.end())
171 nNatNum
= it
->second
[nDBNum
- 1];
178 const std::map
<LanguageType
, std::array
<sal_uInt8
, 9>> tblNatNumToDBNum
179 = { { primary(LANGUAGE_CHINESE
), { 1, 0, 0, 1, 2, 3, 0, 0, 0 } },
180 { primary(LANGUAGE_JAPANESE
), { 1, 2, 3, 1, 2, 3, 1, 2, 0 } },
181 { primary(LANGUAGE_KOREAN
), { 1, 2, 3, 1, 2, 3, 1, 2, 4 } } };
184 sal_uInt8
SvNumberNatNum::MapNatNumToDBNum( sal_uInt8 nNatNum
, LanguageType eLang
, bool bDate
)
186 sal_uInt8 nDBNum
= 0;
187 eLang
= MsLangId::getRealLanguage( eLang
); // resolve SYSTEM etc.
188 eLang
= primary(eLang
); // 10 bit primary language
191 if ( nNatNum
== 10 && eLang
== primary(LANGUAGE_KOREAN
) )
195 else if ( nNatNum
<= 3 )
197 nDBNum
= nNatNum
; // known to be good for: zh,ja,ko / 1,2,3
202 if (1 <= nNatNum
&& nNatNum
<= 9)
204 auto const it
= tblNatNumToDBNum
.find(eLang
);
205 if (it
!= tblNatNumToDBNum
.end())
206 nDBNum
= it
->second
[nNatNum
- 1];
216 ImpSvNumFor::ImpSvNumFor()
219 aI
.eScannedType
= SvNumFormatType::UNDEFINED
;
220 aI
.bThousand
= false;
228 ImpSvNumFor::~ImpSvNumFor()
232 void ImpSvNumFor::Enlarge(sal_uInt16 nCnt
)
234 if ( nStringsCnt
!= nCnt
)
237 aI
.nTypeArray
.resize(nCnt
);
238 aI
.sStrArray
.resize(nCnt
);
242 void ImpSvNumFor::Copy( const ImpSvNumFor
& rNumFor
, const ImpSvNumberformatScan
* pSc
)
244 Enlarge( rNumFor
.nStringsCnt
);
245 aI
.Copy( rNumFor
.aI
, nStringsCnt
);
246 sColorName
= rNumFor
.sColorName
;
249 pColor
= pSc
->GetColor( sColorName
); // #121103# don't copy pointer between documents
253 pColor
= rNumFor
.pColor
;
255 aNatNum
= rNumFor
.aNatNum
;
258 bool ImpSvNumFor::HasNewCurrency() const
260 for ( sal_uInt16 j
=0; j
<nStringsCnt
; j
++ )
262 if ( aI
.nTypeArray
[j
] == NF_SYMBOLTYPE_CURRENCY
)
270 bool ImpSvNumFor::GetNewCurrencySymbol( OUString
& rSymbol
,
271 OUString
& rExtension
) const
273 for ( sal_uInt16 j
=0; j
<nStringsCnt
; j
++ )
275 if ( aI
.nTypeArray
[j
] == NF_SYMBOLTYPE_CURRENCY
)
277 rSymbol
= aI
.sStrArray
[j
];
278 if ( j
< nStringsCnt
-1 && aI
.nTypeArray
[j
+1] == NF_SYMBOLTYPE_CURREXT
)
280 rExtension
= aI
.sStrArray
[j
+1];
289 //! No Erase at rSymbol, rExtension
299 enum BracketFormatSymbolType
301 BRACKET_SYMBOLTYPE_FORMAT
= -1, // subformat string
302 BRACKET_SYMBOLTYPE_COLOR
= -2, // color
303 BRACKET_SYMBOLTYPE_ERROR
= -3, // error
304 BRACKET_SYMBOLTYPE_DBNUM1
= -4, // DoubleByteNumber, represent numbers
305 BRACKET_SYMBOLTYPE_DBNUM2
= -5, // using CJK characters, Excel compatible
306 BRACKET_SYMBOLTYPE_DBNUM3
= -6,
307 BRACKET_SYMBOLTYPE_DBNUM4
= -7,
308 BRACKET_SYMBOLTYPE_DBNUM5
= -8,
309 BRACKET_SYMBOLTYPE_DBNUM6
= -9,
310 BRACKET_SYMBOLTYPE_DBNUM7
= -10,
311 BRACKET_SYMBOLTYPE_DBNUM8
= -11,
312 BRACKET_SYMBOLTYPE_DBNUM9
= -12,
313 BRACKET_SYMBOLTYPE_LOCALE
= -13,
314 BRACKET_SYMBOLTYPE_NATNUM0
= -14, // Our NativeNumber support, ASCII
315 BRACKET_SYMBOLTYPE_NATNUM1
= -15, // Our NativeNumber support, represent
316 BRACKET_SYMBOLTYPE_NATNUM2
= -16, // numbers using CJK, CTL, ...
317 BRACKET_SYMBOLTYPE_NATNUM3
= -17,
318 BRACKET_SYMBOLTYPE_NATNUM4
= -18,
319 BRACKET_SYMBOLTYPE_NATNUM5
= -19,
320 BRACKET_SYMBOLTYPE_NATNUM6
= -20,
321 BRACKET_SYMBOLTYPE_NATNUM7
= -21,
322 BRACKET_SYMBOLTYPE_NATNUM8
= -22,
323 BRACKET_SYMBOLTYPE_NATNUM9
= -23,
324 BRACKET_SYMBOLTYPE_NATNUM10
= -24,
325 BRACKET_SYMBOLTYPE_NATNUM11
= -25,
326 BRACKET_SYMBOLTYPE_NATNUM12
= -26,
327 BRACKET_SYMBOLTYPE_NATNUM13
= -27,
328 BRACKET_SYMBOLTYPE_NATNUM14
= -28,
329 BRACKET_SYMBOLTYPE_NATNUM15
= -29,
330 BRACKET_SYMBOLTYPE_NATNUM16
= -30,
331 BRACKET_SYMBOLTYPE_NATNUM17
= -31,
332 BRACKET_SYMBOLTYPE_NATNUM18
= -32,
333 BRACKET_SYMBOLTYPE_NATNUM19
= -33
338 void SvNumberformat::ImpCopyNumberformat( const SvNumberformat
& rFormat
)
340 sFormatstring
= rFormat
.sFormatstring
;
341 eType
= rFormat
.eType
;
342 maLocale
= rFormat
.maLocale
;
343 fLimit1
= rFormat
.fLimit1
;
344 fLimit2
= rFormat
.fLimit2
;
347 bStandard
= rFormat
.bStandard
;
348 bIsUsed
= rFormat
.bIsUsed
;
349 sComment
= rFormat
.sComment
;
350 bAdditionalBuiltin
= rFormat
.bAdditionalBuiltin
;
352 // #121103# when copying between documents, get color pointers from own scanner
353 ImpSvNumberformatScan
* pColorSc
= ( &rScan
!= &rFormat
.rScan
) ? &rScan
: nullptr;
355 for (sal_uInt16 i
= 0; i
< 4; i
++)
357 NumFor
[i
].Copy(rFormat
.NumFor
[i
], pColorSc
);
361 SvNumberformat::SvNumberformat( SvNumberformat
const & rFormat
)
362 : rScan(rFormat
.rScan
), bStarFlag( rFormat
.bStarFlag
)
364 ImpCopyNumberformat( rFormat
);
367 SvNumberformat::SvNumberformat( SvNumberformat
const & rFormat
, ImpSvNumberformatScan
& rSc
)
369 , bStarFlag( rFormat
.bStarFlag
)
371 ImpCopyNumberformat( rFormat
);
374 static bool lcl_SvNumberformat_IsBracketedPrefix( short nSymbolType
)
376 if ( nSymbolType
> 0 )
378 return true; // conditions
380 switch ( nSymbolType
)
382 case BRACKET_SYMBOLTYPE_COLOR
:
383 case BRACKET_SYMBOLTYPE_DBNUM1
:
384 case BRACKET_SYMBOLTYPE_DBNUM2
:
385 case BRACKET_SYMBOLTYPE_DBNUM3
:
386 case BRACKET_SYMBOLTYPE_DBNUM4
:
387 case BRACKET_SYMBOLTYPE_DBNUM5
:
388 case BRACKET_SYMBOLTYPE_DBNUM6
:
389 case BRACKET_SYMBOLTYPE_DBNUM7
:
390 case BRACKET_SYMBOLTYPE_DBNUM8
:
391 case BRACKET_SYMBOLTYPE_DBNUM9
:
392 case BRACKET_SYMBOLTYPE_LOCALE
:
393 case BRACKET_SYMBOLTYPE_NATNUM0
:
394 case BRACKET_SYMBOLTYPE_NATNUM1
:
395 case BRACKET_SYMBOLTYPE_NATNUM2
:
396 case BRACKET_SYMBOLTYPE_NATNUM3
:
397 case BRACKET_SYMBOLTYPE_NATNUM4
:
398 case BRACKET_SYMBOLTYPE_NATNUM5
:
399 case BRACKET_SYMBOLTYPE_NATNUM6
:
400 case BRACKET_SYMBOLTYPE_NATNUM7
:
401 case BRACKET_SYMBOLTYPE_NATNUM8
:
402 case BRACKET_SYMBOLTYPE_NATNUM9
:
403 case BRACKET_SYMBOLTYPE_NATNUM10
:
404 case BRACKET_SYMBOLTYPE_NATNUM11
:
405 case BRACKET_SYMBOLTYPE_NATNUM12
:
406 case BRACKET_SYMBOLTYPE_NATNUM13
:
407 case BRACKET_SYMBOLTYPE_NATNUM14
:
408 case BRACKET_SYMBOLTYPE_NATNUM15
:
409 case BRACKET_SYMBOLTYPE_NATNUM16
:
410 case BRACKET_SYMBOLTYPE_NATNUM17
:
411 case BRACKET_SYMBOLTYPE_NATNUM18
:
412 case BRACKET_SYMBOLTYPE_NATNUM19
:
418 /** Import extended LCID from Excel
420 OUString
SvNumberformat::ImpObtainCalendarAndNumerals( OUStringBuffer
& rString
, sal_Int32 nPos
,
421 LanguageType
& nLang
, const LocaleType
& aTmpLocale
)
424 sal_uInt16 nNatNum
= 0;
425 LanguageType nLocaleLang
= MsLangId::getRealLanguage( maLocale
.meLanguage
);
426 LanguageType nTmpLocaleLang
= MsLangId::getRealLanguage( aTmpLocale
.meLanguage
);
427 /* NOTE: enhancement to allow other possible locale dependent
428 * calendars and numerals. BUT only if our locale data allows it! For LCID
429 * numerals and calendars see
430 * http://office.microsoft.com/en-us/excel/HA010346351033.aspx
431 * Calendar is inserted after
432 * all prefixes have been consumed as it is actually a format modifier
434 * Currently calendars are tied to the locale of the entire number
435 * format, e.g. [~buddhist] in en_US doesn't work.
436 * => Having different locales in sub formats does not work!
438 /* TODO: calendars could be tied to a sub format's NatNum info
439 * instead, or even better be available for any locale. Needs a
440 * different implementation of GetCal() and locale data calendars.
442 switch ( aTmpLocale
.mnCalendarType
& 0x7F )
444 case 0x03 : // Gengou calendar
445 // Only Japanese language support Gengou calendar.
446 // It is an implicit "other" calendar where E, EE, R and RR
447 // automatically switch to and YY and YYYY switch to Gregorian. Do
448 // not add the "[~gengou]" modifier.
449 if ( nLocaleLang
!= LANGUAGE_JAPANESE
)
451 nLang
= maLocale
.meLanguage
= LANGUAGE_JAPANESE
;
454 case 0x05 : // Korean Dangi calendar
455 sCalendar
= "[~dangi]";
456 // Only Korean language support dangi calendar
457 if ( nLocaleLang
!= LANGUAGE_KOREAN
)
459 nLang
= maLocale
.meLanguage
= LANGUAGE_KOREAN
;
462 case 0x06 : // Hijri calendar
464 sCalendar
= "[~hijri]";
465 // Only Arabic or Farsi languages support Hijri calendar
466 if ( ( primary( nLocaleLang
) != LANGUAGE_ARABIC_PRIMARY_ONLY
)
467 && nLocaleLang
!= LANGUAGE_FARSI
)
469 if ( ( primary( nTmpLocaleLang
) == LANGUAGE_ARABIC_PRIMARY_ONLY
)
470 || nTmpLocaleLang
== LANGUAGE_FARSI
)
472 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
476 nLang
= maLocale
.meLanguage
= LANGUAGE_ARABIC_SAUDI_ARABIA
;
480 case 0x07 : // Buddhist calendar
481 sCalendar
="[~buddhist]";
482 // Only Thai or Lao languages support Buddhist calendar
483 if ( nLocaleLang
!= LANGUAGE_THAI
&& nLocaleLang
!= LANGUAGE_LAO
)
485 if ( nTmpLocaleLang
== LANGUAGE_THAI
|| nTmpLocaleLang
== LANGUAGE_LAO
)
487 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
491 nLang
= maLocale
.meLanguage
= LANGUAGE_THAI
;
495 case 0x08 : // Hebrew calendar
496 sCalendar
= "[~jewish]";
497 // Many languages (but not all) support Jewish calendar
498 // Unable to find any logic => keep same language
500 case 0x0E : // unknown calendar
501 case 0x0F : // unknown calendar
502 case 0x10 : // Indian calendar (unsupported)
503 case 0x11 : // unknown calendar
504 case 0x12 : // unknown calendar
505 case 0x13 : // unknown calendar
506 default : // other calendars (see tdf#36038) are not handle by LibO
509 /** Reference language for each numeral ID */
510 static const LanguageType aNumeralIDtoLanguage
[]=
512 LANGUAGE_DONTKNOW
, // 0x00
513 LANGUAGE_ENGLISH_US
, // 0x01
514 LANGUAGE_ARABIC_SAUDI_ARABIA
, // 0x02 + all Arabic
515 LANGUAGE_FARSI
, // 0x03
516 LANGUAGE_HINDI
, // 0x04 + Devanagari
517 LANGUAGE_BENGALI
, // 0x05
518 LANGUAGE_PUNJABI
, // 0x06
519 LANGUAGE_GUJARATI
, // 0x07
520 LANGUAGE_ODIA
, // 0x08
521 LANGUAGE_TAMIL
, // 0x09
522 LANGUAGE_TELUGU
, // 0x0A
523 LANGUAGE_KANNADA
, // 0x0B
524 LANGUAGE_MALAYALAM
, // 0x0C
525 LANGUAGE_THAI
, // 0x0D
526 LANGUAGE_LAO
, // 0x0E
527 LANGUAGE_TIBETAN
, // 0x0F
528 LANGUAGE_BURMESE
, // 0x10
529 LANGUAGE_TIGRIGNA_ETHIOPIA
, // 0x11
530 LANGUAGE_KHMER
, // 0x12
531 LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
, // 0x13
532 LANGUAGE_DONTKNOW
, // 0x14
533 LANGUAGE_DONTKNOW
, // 0x15
534 LANGUAGE_DONTKNOW
, // 0x16
535 LANGUAGE_DONTKNOW
, // 0x17
536 LANGUAGE_DONTKNOW
, // 0x18
537 LANGUAGE_DONTKNOW
, // 0x19
538 LANGUAGE_DONTKNOW
, // 0x1A
539 LANGUAGE_JAPANESE
, // 0x1B
540 LANGUAGE_JAPANESE
, // 0x1C
541 LANGUAGE_JAPANESE
, // 0x1D
542 LANGUAGE_CHINESE_SIMPLIFIED
, // 0x1E
543 LANGUAGE_CHINESE_SIMPLIFIED
, // 0x1F
544 LANGUAGE_CHINESE_SIMPLIFIED
, // 0x20
545 LANGUAGE_CHINESE_TRADITIONAL
, // 0x21
546 LANGUAGE_CHINESE_TRADITIONAL
, // 0x22
547 LANGUAGE_CHINESE_TRADITIONAL
, // 0x23
548 LANGUAGE_KOREAN
, // 0x24
549 LANGUAGE_KOREAN
, // 0x25
550 LANGUAGE_KOREAN
, // 0x26
551 LANGUAGE_KOREAN
// 0x27
554 sal_uInt16 nNumeralID
= aTmpLocale
.mnNumeralShape
& 0x7F;
555 LanguageType nReferenceLanguage
= nNumeralID
<= 0x27 ? aNumeralIDtoLanguage
[nNumeralID
] : LANGUAGE_DONTKNOW
;
557 switch ( nNumeralID
)
559 // Regular cases: all languages with same primary mask use same numerals
560 case 0x03 : // Perso-Arabic (Farsi) numerals
561 case 0x05 : // Bengali numerals
562 case 0x06 : // Punjabi numerals
563 case 0x07 : // Gujarati numerals
564 case 0x08 : // Odia (Orya) numerals
565 case 0x09 : // Tamil numerals
566 case 0x0A : // Telugu numerals
567 case 0x0B : // Kannada numerals
568 case 0x0C : // Malayalam numerals
569 case 0x0D : // Thai numerals
570 case 0x0E : // Lao numerals
571 case 0x0F : // Tibetan numerals
572 case 0x10 : // Burmese (Myanmar) numerals
573 case 0x11 : // Tigrigna (Ethiopia) numerals
574 case 0x12 : // Khmer numerals
575 if ( primary( nLocaleLang
) != primary( nReferenceLanguage
) )
577 if ( primary( nTmpLocaleLang
) == primary( nReferenceLanguage
) )
579 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
583 nLang
= maLocale
.meLanguage
= nReferenceLanguage
;
588 case 0x04 : // Devanagari (Hindi) numerals
589 // same numerals (Devanagari) for languages with different primary masks
590 if ( nLocaleLang
!= LANGUAGE_HINDI
&& nLocaleLang
!= LANGUAGE_MARATHI
591 && primary( nLocaleLang
) != primary( LANGUAGE_NEPALI
) )
593 if ( nTmpLocaleLang
== LANGUAGE_HINDI
|| nTmpLocaleLang
== LANGUAGE_MARATHI
594 || primary( nTmpLocaleLang
) == primary( LANGUAGE_NEPALI
) )
596 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
600 nLang
= maLocale
.meLanguage
= LANGUAGE_HINDI
;
604 case 0x13 : // Mongolian numerals
605 // not all Mongolian languages use Mongolian numerals
606 if ( nLocaleLang
!= LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
607 && nLocaleLang
!= LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA
608 && nLocaleLang
!= LANGUAGE_MONGOLIAN_MONGOLIAN_LSO
)
610 if ( nTmpLocaleLang
== LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
611 || nTmpLocaleLang
== LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA
612 || nTmpLocaleLang
== LANGUAGE_MONGOLIAN_MONGOLIAN_LSO
)
614 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
618 nLang
= maLocale
.meLanguage
= LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
;
622 case 0x02 : // Eastern-Arabic numerals
623 // all arabic primary mask + LANGUAGE_PUNJABI_ARABIC_LSO
624 if ( primary( nLocaleLang
) != LANGUAGE_ARABIC_PRIMARY_ONLY
625 && nLocaleLang
!= LANGUAGE_PUNJABI_ARABIC_LSO
)
627 if ( primary( nTmpLocaleLang
) == LANGUAGE_ARABIC_PRIMARY_ONLY
628 || nTmpLocaleLang
!= LANGUAGE_PUNJABI_ARABIC_LSO
)
630 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
634 nLang
= maLocale
.meLanguage
= nReferenceLanguage
;
639 case 0x1B : // simple Asian numerals, Japanese
640 case 0x1C : // financial Asian numerals, Japanese
641 case 0x1D : // Arabic fullwidth numerals, Japanese
642 case 0x24 : // simple Asian numerals, Korean
643 case 0x25 : // financial Asian numerals, Korean
644 case 0x26 : // Arabic fullwidth numerals, Korean
645 case 0x27 : // Korean Hangul numerals
646 // Japanese and Korean are regular
647 if ( primary( nLocaleLang
) != primary( nReferenceLanguage
) )
649 if ( primary( nTmpLocaleLang
) == primary( nReferenceLanguage
) )
651 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
655 nLang
= maLocale
.meLanguage
= nReferenceLanguage
;
659 case 0x1E : // simple Asian numerals, Chinese-PRC
660 case 0x1F : // financial Asian numerals, Chinese-PRC
661 case 0x20 : // Arabic fullwidth numerals, Chinese-PRC
662 case 0x21 : // simple Asian numerals, Chinese-Taiwan
663 case 0x22 : // financial Asian numerals, Chinese-Taiwan
664 case 0x23 : // Arabic fullwidth numerals, Chinese-Taiwan
665 nNatNum
= nNumeralID
== 0x27 ? 9 : ( ( nNumeralID
- 0x1B ) % 3 ) + 1;
666 // [NatNum1] simple numerals
667 // [natNum2] financial numerals
668 // [NatNum3] Arabic fullwidth numerals
669 // Chinese simplified and Chinese traditional have same primary mask
671 if ( nReferenceLanguage
== LANGUAGE_CHINESE_SIMPLIFIED
672 && nLocaleLang
!= LANGUAGE_CHINESE_SIMPLIFIED
673 && nLocaleLang
!= LANGUAGE_CHINESE_SINGAPORE
674 && nLocaleLang
!= LANGUAGE_CHINESE_LSO
)
676 if ( nTmpLocaleLang
== LANGUAGE_CHINESE_SIMPLIFIED
677 || nTmpLocaleLang
== LANGUAGE_CHINESE_SINGAPORE
678 || nTmpLocaleLang
== LANGUAGE_CHINESE_LSO
)
680 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
684 nLang
= maLocale
.meLanguage
= LANGUAGE_CHINESE_SIMPLIFIED
;
688 else if ( nReferenceLanguage
== LANGUAGE_CHINESE_TRADITIONAL
689 && nLocaleLang
!= LANGUAGE_CHINESE_TRADITIONAL
690 && nLocaleLang
!= LANGUAGE_CHINESE_HONGKONG
691 && nLocaleLang
!= LANGUAGE_CHINESE_MACAU
)
693 if ( nTmpLocaleLang
== LANGUAGE_CHINESE_TRADITIONAL
694 || nTmpLocaleLang
== LANGUAGE_CHINESE_HONGKONG
695 || nTmpLocaleLang
== LANGUAGE_CHINESE_MACAU
)
697 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
701 nLang
= maLocale
.meLanguage
= LANGUAGE_CHINESE_TRADITIONAL
;
706 if ( nNumeralID
>= 0x02 && nNumeralID
<= 0x13 )
709 rString
.insert( nPos
, OUStringConcatenation("[NatNum"+OUString::number(nNatNum
)+"]"));
715 bool NatNumTakesParameters(sal_Int16 nNum
)
717 return (nNum
== css::i18n::NativeNumberMode::NATNUM12
);
721 SvNumberformat::SvNumberformat(OUString
& rString
,
722 ImpSvNumberformatScan
* pSc
,
723 ImpSvNumberInputScan
* pISc
,
724 sal_Int32
& nCheckPos
,
726 bool bReplaceBooleanEquivalent
)
728 , bAdditionalBuiltin( false )
731 if (bReplaceBooleanEquivalent
)
732 rScan
.ReplaceBooleanEquivalent( rString
);
734 OUStringBuffer
sBuff(rString
);
736 // If the group (AKA thousand) separator is a No-Break Space (French)
737 // replace all occurrences by a simple space.
738 // The same for Narrow No-Break Space just in case some locale uses it.
739 // The tokens will be changed to the LocaleData separator again later on.
740 const OUString
& rThSep
= GetFormatter().GetNumThousandSep();
741 if ( rThSep
.getLength() == 1)
743 const sal_Unicode cNBSp
= 0xA0;
744 const sal_Unicode cNNBSp
= 0x202F;
745 if (rThSep
[0] == cNBSp
)
746 sBuff
.replace( cNBSp
, ' ');
747 else if (rThSep
[0] == cNNBSp
)
748 sBuff
.replace( cNNBSp
, ' ');
751 OUString aConvertFromDecSep
;
752 OUString aConvertToDecSep
;
753 if (rScan
.GetConvertMode())
755 aConvertFromDecSep
= GetFormatter().GetNumDecimalSep();
756 maLocale
.meLanguage
= rScan
.GetNewLnge();
757 eLan
= maLocale
.meLanguage
; // Make sure to return switch
761 maLocale
.meLanguage
= eLan
;
767 eOp1
= NUMBERFORMAT_OP_NO
;
768 eOp2
= NUMBERFORMAT_OP_NO
;
769 eType
= SvNumFormatType::DEFINED
;
771 bool bCancel
= false;
772 bool bCondition
= false;
778 // Split into 4 sub formats
780 for ( nIndex
= 0; nIndex
< 4 && !bCancel
; nIndex
++ )
782 // Original language/country may have to be reestablished
783 if (rScan
.GetConvertMode())
785 rScan
.GetNumberformatter()->ChangeIntl(rScan
.GetTmpLnge());
787 OUString sInsertCalendar
; // a calendar resulting from parsing LCID
789 nPosOld
= nPos
; // Start position of substring
790 // first get bracketed prefixes; e.g. conditions, color
793 eSymbolType
= ImpNextSymbol(sBuff
, nPos
, sStr
);
794 if (eSymbolType
> 0) // condition
796 if ( nIndex
== 0 && !bCondition
)
799 eOp1
= static_cast<SvNumberformatLimitOps
>(eSymbolType
);
801 else if ( nIndex
== 1 && bCondition
)
803 eOp2
= static_cast<SvNumberformatLimitOps
>(eSymbolType
);
807 bCancel
= true; // break for
813 sal_Int32 nCntChars
= ImpGetNumber(sBuff
, nPos
, sStr
);
817 SvNumFormatType F_Type
= SvNumFormatType::UNDEFINED
;
818 if (!pISc
->IsNumberFormat(sStr
, F_Type
, fNumber
, nullptr, SvNumInputOptions::NONE
) ||
819 ( F_Type
!= SvNumFormatType::NUMBER
&&
820 F_Type
!= SvNumFormatType::SCIENTIFIC
) )
823 nPos
= nPos
- nCntChars
;
824 sBuff
.remove(nPos
, nCntChars
);
825 sBuff
.insert(nPos
, '0');
828 else if (rScan
.GetConvertMode() && ((nDecPos
= sStr
.indexOf( aConvertFromDecSep
)) >= 0))
830 if (aConvertToDecSep
.isEmpty())
831 aConvertToDecSep
= GetFormatter().GetLangDecimalSep( rScan
.GetNewLnge());
832 if (aConvertToDecSep
!= aConvertFromDecSep
)
834 const OUString
aStr( sStr
.replaceAt( nDecPos
,
835 aConvertFromDecSep
.getLength(), aConvertToDecSep
));
836 nPos
= nPos
- nCntChars
;
837 sBuff
.remove(nPos
, nCntChars
);
838 sBuff
.insert(nPos
, aStr
);
839 nPos
+= aStr
.getLength();
846 sBuff
.insert(nPos
++, '0');
856 if ( nPos
< sBuff
.getLength() && sBuff
[nPos
] == ']' )
862 bCancel
= true; // break for
866 nPosOld
= nPos
; // position before string
868 else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType
) )
870 OUString
sSymbol( sStr
);
871 switch ( eSymbolType
)
873 case BRACKET_SYMBOLTYPE_COLOR
:
874 if ( NumFor
[nIndex
].GetColor() != nullptr )
875 { // error, more than one color
876 bCancel
= true; // break for
881 const Color
* pColor
= pSc
->GetColor( sStr
);
882 NumFor
[nIndex
].SetColor( pColor
, sStr
);
883 if (pColor
== nullptr)
885 bCancel
= true; // break for
890 case BRACKET_SYMBOLTYPE_NATNUM0
:
891 case BRACKET_SYMBOLTYPE_NATNUM1
:
892 case BRACKET_SYMBOLTYPE_NATNUM2
:
893 case BRACKET_SYMBOLTYPE_NATNUM3
:
894 case BRACKET_SYMBOLTYPE_NATNUM4
:
895 case BRACKET_SYMBOLTYPE_NATNUM5
:
896 case BRACKET_SYMBOLTYPE_NATNUM6
:
897 case BRACKET_SYMBOLTYPE_NATNUM7
:
898 case BRACKET_SYMBOLTYPE_NATNUM8
:
899 case BRACKET_SYMBOLTYPE_NATNUM9
:
900 case BRACKET_SYMBOLTYPE_NATNUM10
:
901 case BRACKET_SYMBOLTYPE_NATNUM11
:
902 case BRACKET_SYMBOLTYPE_NATNUM12
:
903 case BRACKET_SYMBOLTYPE_NATNUM13
:
904 case BRACKET_SYMBOLTYPE_NATNUM14
:
905 case BRACKET_SYMBOLTYPE_NATNUM15
:
906 case BRACKET_SYMBOLTYPE_NATNUM16
:
907 case BRACKET_SYMBOLTYPE_NATNUM17
:
908 case BRACKET_SYMBOLTYPE_NATNUM18
:
909 case BRACKET_SYMBOLTYPE_NATNUM19
:
910 if ( NumFor
[nIndex
].GetNatNum().IsSet() )
912 bCancel
= true; // break for
918 sal_Int32 nSpacePos
= sStr
.indexOf(' ');
921 sParams
= sStr
.copy(nSpacePos
+1).trim();
923 //! eSymbolType is negative
924 sal_uInt8 nNum
= static_cast<sal_uInt8
>(0 - (eSymbolType
- BRACKET_SYMBOLTYPE_NATNUM0
));
925 if (!sParams
.isEmpty() && !NatNumTakesParameters(nNum
))
927 bCancel
= true; // break for
931 sStr
= "NatNum" + OUString::number(nNum
);
932 NumFor
[nIndex
].SetNatNumNum( nNum
, false );
933 // NatNum12 supports arguments
936 if (sParams
.isEmpty())
937 sParams
= "cardinal"; // default NatNum12 format is "cardinal"
938 NumFor
[nIndex
].SetNatNumParams(sParams
);
939 sStr
+= " " + sParams
;
943 case BRACKET_SYMBOLTYPE_DBNUM1
:
944 case BRACKET_SYMBOLTYPE_DBNUM2
:
945 case BRACKET_SYMBOLTYPE_DBNUM3
:
946 case BRACKET_SYMBOLTYPE_DBNUM4
:
947 case BRACKET_SYMBOLTYPE_DBNUM5
:
948 case BRACKET_SYMBOLTYPE_DBNUM6
:
949 case BRACKET_SYMBOLTYPE_DBNUM7
:
950 case BRACKET_SYMBOLTYPE_DBNUM8
:
951 case BRACKET_SYMBOLTYPE_DBNUM9
:
952 if ( NumFor
[nIndex
].GetNatNum().IsSet() )
954 bCancel
= true; // break for
959 //! eSymbolType is negative
960 sal_uInt8 nNum
= static_cast<sal_uInt8
>(1 - (eSymbolType
- BRACKET_SYMBOLTYPE_DBNUM1
));
961 sStr
= "DBNum" + OUStringChar(sal_Unicode('0' + nNum
));
962 NumFor
[nIndex
].SetNatNumNum( nNum
, true );
965 case BRACKET_SYMBOLTYPE_LOCALE
:
966 if ( NumFor
[nIndex
].GetNatNum().GetLang() != LANGUAGE_DONTKNOW
||
967 sBuff
[nPos
-1] != ']' )
968 // Check also for ']' to avoid pulling in
969 // locale data for the preview string for not
970 // yet completed LCIDs in the dialog.
972 bCancel
= true; // break for
978 LocaleType
aTmpLocale( ImpGetLocaleType( sStr
, nTmp
));
979 if (aTmpLocale
.meLanguage
== LANGUAGE_DONTKNOW
)
981 bCancel
= true; // break for
986 // Only the first sub format's locale will be
987 // used as the format's overall locale.
988 // Sorts this also under the corresponding
989 // locale for the dialog.
990 // If we don't support the locale this would
991 // result in an unknown (empty) language
992 // listbox entry and the user would never see
994 if (nIndex
== 0 && (aTmpLocale
.meLanguage
== LANGUAGE_SYSTEM
||
995 SvNumberFormatter::IsLocaleInstalled( aTmpLocale
.meLanguage
)))
997 maLocale
= aTmpLocale
;
998 eLan
= aTmpLocale
.meLanguage
; // return to caller
1000 // Set new target locale also at scanner.
1001 // We have to do this because switching locale
1002 // may make replacing keywords and separators
1004 // We can do this because it's the first
1005 // subformat and we're still at parsing the
1006 // modifiers, not keywords.
1007 rScan
.SetNewLnge( eLan
);
1008 // We can not force conversion though because
1009 // the caller may have explicitly not set it.
1010 // In the usual case the target locale is the
1011 // originating locale the conversion is not
1012 // necessary, when reading alien documents
1013 // conversion is enabled anyway.
1015 /* TODO: fiddle with scanner to make this
1016 * known? A change in the locale may affect
1017 * separators and keywords. On the other
1018 * hand they may have been entered as used
1019 * in the originating locale, there's no
1020 * way to predict other than analyzing the
1021 * format code, we assume here the current
1022 * context is used, which is most likely
1026 // Strip a plain locale identifier if locale
1027 // data is available to avoid duplicated
1028 // formats with and without LCID for the same
1029 // locale. Besides it looks ugly and confusing
1030 // and is unnecessary as the format will be
1031 // listed for the resulting locale.
1032 if (aTmpLocale
.isPlainLocale())
1035 sStr
= "$-" + aTmpLocale
.generateCode();
1040 // Locale data not available, remember.
1041 maLocale
.meLanguageWithoutLocaleData
= aTmpLocale
.meLanguage
;
1043 sStr
= "$-" + aTmpLocale
.generateCode();
1045 NumFor
[nIndex
].SetNatNumLang( MsLangId::getRealLanguage( aTmpLocale
.meLanguage
));
1047 // "$-NNCCLLLL" Numerals and Calendar
1048 if (sSymbol
.getLength() > 6)
1050 sInsertCalendar
= ImpObtainCalendarAndNumerals( sBuff
, nPos
, eLan
, aTmpLocale
);
1052 /* NOTE: there can be only one calendar
1053 * inserted so the last one wins, though
1054 * our own calendar modifiers support
1055 * multiple calendars within one sub format
1056 * code if at different positions. */
1063 if (sStr
== sSymbol
)
1069 sBuff
.remove(nPosOld
, nPos
- nPosOld
);
1070 if (!sStr
.isEmpty())
1072 sBuff
.insert(nPosOld
, sStr
);
1073 nPos
= nPosOld
+ sStr
.getLength();
1074 sBuff
.insert(nPos
, "]");
1075 sBuff
.insert(nPosOld
, "[");
1077 nPosOld
= nPos
; // position before string
1081 nPos
= nPosOld
; // prefix removed for whatever reason
1087 while ( !bCancel
&& lcl_SvNumberformat_IsBracketedPrefix( eSymbolType
) );
1089 // The remaining format code string
1092 if (eSymbolType
== BRACKET_SYMBOLTYPE_FORMAT
)
1094 if (nIndex
== 1 && eOp1
== NUMBERFORMAT_OP_NO
)
1096 eOp1
= NUMBERFORMAT_OP_GT
; // undefined condition, default: > 0
1098 else if (nIndex
== 2 && eOp2
== NUMBERFORMAT_OP_NO
)
1100 eOp2
= NUMBERFORMAT_OP_LT
; // undefined condition, default: < 0
1104 // Empty sub format.
1105 NumFor
[nIndex
].Info().eScannedType
= SvNumFormatType::EMPTY
;
1109 if (!sInsertCalendar
.isEmpty())
1111 sStr
= sInsertCalendar
+ sStr
;
1113 sal_Int32 nStrPos
= pSc
->ScanFormat( sStr
);
1114 sal_uInt16 nCnt
= pSc
->GetResultStringsCnt();
1115 if (nCnt
== 0 && nStrPos
== 0) // error
1119 if (nStrPos
== 0) // ok
1121 // e.g. Thai T speciality
1122 if (pSc
->GetNatNumModifier() && !NumFor
[nIndex
].GetNatNum().IsSet())
1124 sStr
= "[NatNum" + OUString::number( pSc
->GetNatNumModifier()) + "]" + sStr
;
1125 NumFor
[nIndex
].SetNatNumNum( pSc
->GetNatNumModifier(), false );
1127 // #i53826# #i42727# For the Thai T speciality we need
1128 // to freeze the locale and immunize it against
1129 // conversions during exports, just in case we want to
1130 // save to Xcl. This disables the feature of being able
1131 // to convert a NatNum to another locale. You can't
1133 // FIXME: implement a specialized export conversion
1134 // that works on tokens (have to tokenize all first)
1135 // and doesn't use the format string and
1136 // PutandConvertEntry() to LANGUAGE_ENGLISH_US in
1137 // sc/source/filter/excel/xestyle.cxx
1138 // XclExpNumFmtBuffer::WriteFormatRecord().
1139 LanguageType eLanguage
;
1140 if (NumFor
[nIndex
].GetNatNum().GetNatNum() == 1 &&
1141 ((eLanguage
= MsLangId::getRealLanguage( eLan
)) == LANGUAGE_THAI
) &&
1142 NumFor
[nIndex
].GetNatNum().GetLang() == LANGUAGE_DONTKNOW
)
1144 sStr
= "[$-" + OUString::number( sal_uInt16(eLanguage
), 16 ).toAsciiUpperCase() + "]" + sStr
;
1145 NumFor
[nIndex
].SetNatNumLang( eLanguage
);
1147 sBuff
.remove(nPosOld
, nPos
- nPosOld
);
1148 sBuff
.insert(nPosOld
, sStr
);
1149 nPos
= nPosOld
+ sStr
.getLength();
1150 if (nPos
< sBuff
.getLength())
1152 sBuff
.insert(nPos
, ";");
1155 else if (nIndex
> 0)
1157 // The last subformat. If it is a trailing text
1158 // format the omitted subformats act like they were
1159 // not specified and "inherited" the first format,
1160 // e.g. 0;@ behaves like 0;-0;0;@
1161 if (pSc
->GetScannedType() == SvNumFormatType::TEXT
)
1163 // Reset conditions, reverting any set above.
1165 eOp1
= NUMBERFORMAT_OP_NO
;
1166 else if (nIndex
== 2)
1167 eOp2
= NUMBERFORMAT_OP_NO
;
1171 NumFor
[nIndex
].Enlarge(nCnt
);
1172 pSc
->CopyInfo(&(NumFor
[nIndex
].Info()), nCnt
);
1176 eType
= NumFor
[nIndex
].Info().eScannedType
;
1178 else if (nIndex
== 3)
1179 { // #77026# Everything recognized IS text
1180 NumFor
[nIndex
].Info().eScannedType
= SvNumFormatType::TEXT
;
1182 else if ( NumFor
[nIndex
].Info().eScannedType
!= eType
)
1184 eType
= SvNumFormatType::DEFINED
;
1189 nCheckPos
= nPosOld
+ nStrPos
; // error in string
1190 bCancel
= true; // break for
1194 else if (eSymbolType
== BRACKET_SYMBOLTYPE_ERROR
) // error
1196 nCheckPos
= nPosOld
;
1199 else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType
) )
1201 nCheckPos
= nPosOld
+ 1; // error, prefix in string
1202 bCancel
= true; // break for
1205 if ( bCancel
&& !nCheckPos
)
1207 nCheckPos
= 1; // nCheckPos is used as an error condition
1211 if ( NumFor
[nIndex
].GetNatNum().IsSet() &&
1212 NumFor
[nIndex
].GetNatNum().GetLang() == LANGUAGE_DONTKNOW
)
1214 NumFor
[nIndex
].SetNatNumLang( eLan
);
1217 if (sBuff
.getLength() == nPos
)
1219 if (nIndex
< 3 && rString
[rString
.getLength()-1] == ';')
1221 // A trailing ';' is significant and specifies the following
1222 // subformat to be empty. We don't enter the scanning loop
1223 // above again though.
1224 // Note that the operators apply to the current last scanned
1226 if (nIndex
== 0 && eOp1
== NUMBERFORMAT_OP_NO
)
1228 eOp1
= NUMBERFORMAT_OP_GT
; // undefined condition, default: > 0
1230 else if (nIndex
== 1 && eOp2
== NUMBERFORMAT_OP_NO
)
1232 eOp2
= NUMBERFORMAT_OP_LT
; // undefined condition, default: < 0
1234 NumFor
[nIndex
+1].Info().eScannedType
= SvNumFormatType::EMPTY
;
1235 if (sBuff
[nPos
-1] != ';')
1236 sBuff
.insert( nPos
++, ';');
1238 if (nIndex
== 2 && eSymbolType
== BRACKET_SYMBOLTYPE_FORMAT
&& sBuff
[nPos
-1] == ';')
1240 // #83510# A 4th subformat explicitly specified to be empty
1241 // hides any text. Need the type here for HasTextFormat()
1242 NumFor
[3].Info().eScannedType
= SvNumFormatType::TEXT
;
1246 if ( NumFor
[nIndex
].GetNatNum().IsSet() )
1248 NumFor
[nIndex
].SetNatNumDate( bool(NumFor
[nIndex
].Info().eScannedType
& SvNumFormatType::DATE
) );
1252 if (!nCheckPos
&& IsSubstituted())
1254 // For to be substituted formats the scanned type must match the
1256 if (IsSystemTimeFormat())
1258 if ((eType
& ~SvNumFormatType::DEFINED
) != SvNumFormatType::TIME
)
1259 nCheckPos
= std::max
<sal_Int32
>( sBuff
.indexOf(']') + 1, 1);
1261 else if (IsSystemLongDateFormat())
1263 if ((eType
& ~SvNumFormatType::DEFINED
) != SvNumFormatType::DATE
)
1264 nCheckPos
= std::max
<sal_Int32
>( sBuff
.indexOf(']') + 1, 1);
1267 assert(!"unhandled substitute");
1270 if ( bCondition
&& !nCheckPos
)
1272 if ( nIndex
== 1 && NumFor
[0].GetCount() == 0 &&
1273 sBuff
[sBuff
.getLength() - 1] != ';' )
1275 // No format code => GENERAL but not if specified empty
1276 OUString
aAdd( pSc
->GetStandardName() );
1277 if ( !pSc
->ScanFormat( aAdd
) )
1279 sal_uInt16 nCnt
= pSc
->GetResultStringsCnt();
1282 NumFor
[0].Enlarge(nCnt
);
1283 pSc
->CopyInfo( &(NumFor
[0].Info()), nCnt
);
1288 else if ( nIndex
== 1 && NumFor
[nIndex
].GetCount() == 0 &&
1289 sBuff
[sBuff
.getLength() - 1] != ';' &&
1290 (NumFor
[0].GetCount() > 1 ||
1291 (NumFor
[0].GetCount() == 1 &&
1292 NumFor
[0].Info().nTypeArray
[0] != NF_KEY_GENERAL
)) )
1294 // No trailing second subformat => GENERAL but not if specified empty
1295 // and not if first subformat is GENERAL
1296 OUString
aAdd( pSc
->GetStandardName() );
1297 if ( !pSc
->ScanFormat( aAdd
) )
1299 sal_uInt16 nCnt
= pSc
->GetResultStringsCnt();
1302 NumFor
[nIndex
].Enlarge(nCnt
);
1303 pSc
->CopyInfo( &(NumFor
[nIndex
].Info()), nCnt
);
1309 else if ( nIndex
== 2 && NumFor
[nIndex
].GetCount() == 0 &&
1310 sBuff
[sBuff
.getLength() - 1] != ';' &&
1311 eOp2
!= NUMBERFORMAT_OP_NO
)
1313 // No trailing third subformat => GENERAL but not if specified empty
1314 OUString
aAdd( pSc
->GetStandardName() );
1315 if ( !pSc
->ScanFormat( aAdd
) )
1317 sal_uInt16 nCnt
= pSc
->GetResultStringsCnt();
1320 NumFor
[nIndex
].Enlarge(nCnt
);
1321 pSc
->CopyInfo( &(NumFor
[nIndex
].Info()), nCnt
);
1328 rString
= sBuff
.makeStringAndClear();
1329 sFormatstring
= rString
;
1331 if (NumFor
[2].GetCount() == 0 && // No third partial string
1332 eOp1
== NUMBERFORMAT_OP_GT
&& eOp2
== NUMBERFORMAT_OP_NO
&&
1333 fLimit1
== 0.0 && fLimit2
== 0.0)
1335 eOp1
= NUMBERFORMAT_OP_GE
; // Add 0 to the first format
1340 SvNumberformat::~SvNumberformat()
1347 * Splits up the symbols for further processing (by the Turing machine)
1349 * Start state = SsStart, * = Special state
1350 * ---------------+-------------------+----------------------------+---------------
1351 * Old State | Symbol read | Event | New state
1352 * ---------------+-------------------+----------------------------+---------------
1353 * SsStart | " | Symbol += Character | SsGetQuoted
1354 * | ; | Pos-- | SsGetString
1355 * | [ | Symbol += Character | SsGetBracketed
1356 * | ] | Error | SsStop
1358 * | Else | Symbol += Character | SsGetString
1359 * ---------------+-------------------+----------------------------+---------------
1360 * SsGetString | " | Symbol += Character | SsGetQuoted
1362 * | Else | Symbol += Character |
1363 * ---------------+-------------------+----------------------------+---------------
1364 * SsGetQuoted | " | Symbol += Character | SsGetString
1365 * | Else | Symbol += Character |
1366 * ---------------+-------------------+----------------------------+---------------
1367 * SsGetBracketed | <, > = | del [ |
1368 * | | Symbol += Character | SsGetCon
1370 * | h, H, m, M, s, S | Symbol += Character | SsGetTime
1372 * | | Symbol += Character | SsGetPrefix
1373 * ---------------+-------------------+----------------------------+---------------
1374 * SsGetTime | ] | Symbol += Character | SsGetString
1375 * | h, H, m, M, s, S | Symbol += Character, * | SsGetString
1376 * | Else | del [; Symbol += Character | SsGetPrefix
1377 * ---------------+-------------------+----------------------------+---------------
1378 * SsGetPrefix | ] | | SsStop
1379 * | Else | Symbol += Character |
1380 * ---------------+-------------------+----------------------------+---------------
1381 * SsGetCon | >, = | Symbol += Character |
1383 * | Else | Error | SsStop
1384 * ---------------+-------------------+----------------------------+---------------
1393 SsGetCon
, // condition
1394 SsGetString
, // format string
1395 SsGetPrefix
, // color or NatNumN
1396 SsGetTime
, // [HH] for time
1397 SsGetBracketed
, // any [...] not decided yet
1398 SsGetQuoted
// quoted text
1403 // read a string until ']' and delete spaces in input
1405 sal_Int32
SvNumberformat::ImpGetNumber(OUStringBuffer
& rString
,
1409 sal_Int32 nStartPos
= nPos
;
1411 sal_Int32 nLen
= rString
.getLength();
1412 OUStringBuffer sBuffSymbol
;
1413 while ( nPos
< nLen
)
1415 cToken
= rString
[nPos
];
1420 rString
.remove(nPos
,1);
1426 sBuffSymbol
.append(cToken
);
1429 sSymbol
= sBuffSymbol
.makeStringAndClear();
1430 return nPos
- nStartPos
;
1435 sal_Unicode
toUniChar(sal_uInt8 n
)
1446 return sal_Unicode(c
);
1449 bool IsCombiningSymbol( OUStringBuffer
& rStringBuffer
, sal_Int32 nPos
)
1454 switch (rStringBuffer
[nPos
])
1471 OUString
SvNumberformat::LocaleType::generateCode() const
1473 OUStringBuffer aBuf
;
1475 // TODO: We may re-enable this later. Don't remove it! --Kohei
1478 sal_uInt8 nVal
= mnNumeralShape
;
1479 for (sal_uInt8 i
= 0; i
< 2; ++i
)
1481 sal_uInt8 n
= (nVal
& 0xF0) >> 4;
1482 if (n
|| aBuf
.getLength())
1484 aBuf
.append(toUniChar(n
));
1490 if (mnNumeralShape
|| mnCalendarType
)
1492 sal_uInt8 nVal
= mnCalendarType
;
1493 for (sal_uInt8 i
= 0; i
< 2; ++i
)
1495 sal_uInt8 n
= (nVal
& 0xF0) >> 4;
1496 if (n
|| aBuf
.getLength())
1498 aBuf
.append(toUniChar(n
));
1505 sal_uInt16 n16
= static_cast<sal_uInt16
>(
1506 (meLanguageWithoutLocaleData
== LANGUAGE_DONTKNOW
) ? meLanguage
:
1507 meLanguageWithoutLocaleData
);
1508 if (meLanguage
== LANGUAGE_SYSTEM
)
1510 switch (meSubstitute
)
1512 case Substitute::NONE
:
1515 case Substitute::TIME
:
1516 n16
= static_cast<sal_uInt16
>(LANGUAGE_NF_SYSTEM_TIME
);
1518 case Substitute::LONGDATE
:
1519 n16
= static_cast<sal_uInt16
>(LANGUAGE_NF_SYSTEM_DATE
);
1523 for (sal_uInt8 i
= 0; i
< 4; ++i
)
1525 sal_uInt8 n
= static_cast<sal_uInt8
>((n16
& 0xF000) >> 12);
1526 // Omit leading zeros for consistency.
1527 if (n
|| !aBuf
.isEmpty() || i
== 3)
1529 aBuf
.append(toUniChar(n
));
1534 return aBuf
.makeStringAndClear();
1537 SvNumberformat::LocaleType::LocaleType()
1538 : meLanguage(LANGUAGE_DONTKNOW
)
1539 , meLanguageWithoutLocaleData(LANGUAGE_DONTKNOW
)
1540 , meSubstitute(Substitute::NONE
)
1546 SvNumberformat::LocaleType::LocaleType(sal_uInt32 nRawNum
)
1547 : meLanguage(LANGUAGE_DONTKNOW
)
1548 , meLanguageWithoutLocaleData(LANGUAGE_DONTKNOW
)
1549 , meSubstitute(Substitute::NONE
)
1553 meLanguage
= static_cast<LanguageType
>(nRawNum
& 0x0000FFFF);
1554 if (meLanguage
== LANGUAGE_NF_SYSTEM_TIME
)
1556 meSubstitute
= Substitute::TIME
;
1557 meLanguage
= LANGUAGE_SYSTEM
;
1559 else if (meLanguage
== LANGUAGE_NF_SYSTEM_DATE
)
1561 meSubstitute
= Substitute::LONGDATE
;
1562 meLanguage
= LANGUAGE_SYSTEM
;
1564 nRawNum
= (nRawNum
>> 16);
1565 mnCalendarType
= static_cast<sal_uInt8
>(nRawNum
& 0xFF);
1566 nRawNum
= (nRawNum
>> 8);
1567 mnNumeralShape
= static_cast<sal_uInt8
>(nRawNum
& 0xFF);
1570 bool SvNumberformat::LocaleType::isPlainLocale() const
1572 return meSubstitute
== Substitute::NONE
&& !mnCalendarType
&& !mnNumeralShape
;
1576 SvNumberformat::LocaleType
SvNumberformat::ImpGetLocaleType(const OUString
& rString
, sal_Int32
& nPos
)
1578 sal_uInt32 nNum
= 0;
1579 sal_Unicode cToken
= 0;
1580 sal_Int32 nStart
= nPos
;
1581 sal_Int32 nLen
= rString
.getLength();
1582 while ( nPos
< nLen
&& (nPos
- nStart
< 8) )
1584 cToken
= rString
[nPos
];
1587 if ( '0' <= cToken
&& cToken
<= '9' )
1590 nNum
+= cToken
- '0';
1592 else if ( 'a' <= cToken
&& cToken
<= 'f' )
1595 nNum
+= cToken
- 'a' + 10;
1597 else if ( 'A' <= cToken
&& cToken
<= 'F' )
1600 nNum
+= cToken
- 'A' + 10;
1604 return LocaleType(); // LANGUAGE_DONTKNOW;
1609 return (cToken
== ']' || nPos
== nLen
) ? LocaleType(nNum
) : LocaleType();
1612 static bool lcl_matchKeywordAndGetNumber( const OUString
& rString
, const sal_Int32 nPos
,
1613 const OUString
& rKeyword
, sal_Int32
& nNumber
)
1615 if (0 <= nPos
&& nPos
+ rKeyword
.getLength() < rString
.getLength() && rString
.matchIgnoreAsciiCase( rKeyword
, nPos
))
1617 nNumber
= rString
.copy( nPos
+ rKeyword
.getLength()).toInt32();
1627 short SvNumberformat::ImpNextSymbol(OUStringBuffer
& rString
,
1629 OUString
& sSymbol
) const
1631 short eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1633 sal_Unicode cLetter
= ' '; // Preliminary result
1634 sal_Int32 nLen
= rString
.getLength();
1635 ScanState eState
= SsStart
;
1636 OUStringBuffer
sBuffSymbol(128);
1638 const NfKeywordTable
& rKeywords
= rScan
.GetKeywords();
1639 while (nPos
< nLen
&& eState
!= SsStop
)
1641 cToken
= rString
[nPos
];
1648 eState
= SsGetQuoted
;
1649 sBuffSymbol
.append(cToken
);
1651 else if (cToken
== '[')
1653 eState
= SsGetBracketed
;
1654 sBuffSymbol
.append(cToken
);
1656 else if (cToken
== ';')
1658 eState
= SsGetString
;
1660 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1662 else if (cToken
== ']')
1665 eSymbolType
= BRACKET_SYMBOLTYPE_ERROR
;
1667 else if (cToken
== ' ') // Skip Blanks
1670 rString
.remove(nPos
, 1);
1675 sBuffSymbol
.append(cToken
);
1676 eState
= SsGetString
;
1677 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1680 case SsGetBracketed
:
1686 sBuffSymbol
.stripStart('[');
1687 sBuffSymbol
.append(cToken
);
1693 eSymbolType
= NUMBERFORMAT_OP_LT
;
1696 eSymbolType
= NUMBERFORMAT_OP_GT
;
1699 eSymbolType
= NUMBERFORMAT_OP_EQ
;
1705 rString
.remove(nPos
, 1);
1709 if ( nPos
< nLen
&& rString
[nPos
] == '-' )
1712 sBuffSymbol
.stripStart('[');
1713 eSymbolType
= BRACKET_SYMBOLTYPE_LOCALE
;
1714 eState
= SsGetPrefix
;
1718 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1719 eState
= SsGetString
;
1721 sBuffSymbol
.append(cToken
);
1725 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1726 sBuffSymbol
.append(cToken
);
1727 eState
= SsGetString
;
1731 static const OUStringLiteral
aNatNum(u
"NATNUM");
1732 static const OUStringLiteral
aDBNum(u
"DBNUM");
1733 const OUString
aBufStr( rString
.toString());
1734 sal_Int32 nNatNumNum
;
1736 if ( lcl_matchKeywordAndGetNumber( aBufStr
, nPos
-1, aNatNum
, nNatNumNum
) &&
1737 0 <= nNatNumNum
&& nNatNumNum
<= 19 )
1739 sBuffSymbol
.stripStart('[');
1740 sBuffSymbol
.append( aBufStr
.subView(--nPos
, aNatNum
.getLength()+1) );
1741 nPos
+= aNatNum
.getLength()+1;
1742 //! SymbolType is negative
1743 eSymbolType
= static_cast<short>(BRACKET_SYMBOLTYPE_NATNUM0
- nNatNumNum
);
1744 eState
= SsGetPrefix
;
1746 else if ( lcl_matchKeywordAndGetNumber( aBufStr
, nPos
-1, aDBNum
, nDBNum
) &&
1747 1 <= nDBNum
&& nDBNum
<= 9 )
1749 sBuffSymbol
.stripStart('[');
1750 sBuffSymbol
.append( aBufStr
.subView(--nPos
, aDBNum
.getLength()+1) );
1751 nPos
+= aDBNum
.getLength()+1;
1752 //! SymbolType is negative
1753 eSymbolType
= sal::static_int_cast
< short >( BRACKET_SYMBOLTYPE_DBNUM1
- (nDBNum
- 1) );
1754 eState
= SsGetPrefix
;
1758 sal_Unicode cUpper
= rChrCls().uppercase( aBufStr
, nPos
-1, 1)[0];
1759 if ( cUpper
== rKeywords
[NF_KEY_H
][0] || // H
1760 cUpper
== rKeywords
[NF_KEY_MI
][0] || // M
1761 cUpper
== rKeywords
[NF_KEY_S
][0] ) // S
1763 sBuffSymbol
.append(cToken
);
1769 sBuffSymbol
.stripStart('[');
1770 sBuffSymbol
.append(cToken
);
1771 eSymbolType
= BRACKET_SYMBOLTYPE_COLOR
;
1772 eState
= SsGetPrefix
;
1781 eState
= SsGetQuoted
;
1782 sBuffSymbol
.append(cToken
);
1784 else if (cToken
== ';' && (nPos
< 2 || !IsCombiningSymbol( rString
, nPos
-2)))
1790 sBuffSymbol
.append(cToken
);
1796 eState
= SsGetString
;
1797 sBuffSymbol
.append(cToken
);
1801 sBuffSymbol
.append(cToken
);
1807 sBuffSymbol
.append(cToken
);
1808 eState
= SsGetString
;
1809 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1813 sal_Unicode cUpper
= rChrCls().uppercase(rString
.toString(), nPos
-1, 1)[0];
1814 if (cUpper
== rKeywords
[NF_KEY_H
][0] || // H
1815 cUpper
== rKeywords
[NF_KEY_MI
][0] || // M
1816 cUpper
== rKeywords
[NF_KEY_S
][0] ) // S
1818 if (cLetter
== cToken
)
1820 sBuffSymbol
.append(cToken
);
1825 sBuffSymbol
.stripStart('[');
1826 sBuffSymbol
.append(cToken
);
1827 eState
= SsGetPrefix
;
1832 sBuffSymbol
.stripStart('[');
1833 sBuffSymbol
.append(cToken
);
1834 eSymbolType
= BRACKET_SYMBOLTYPE_COLOR
;
1835 eState
= SsGetPrefix
;
1844 eSymbolType
= BRACKET_SYMBOLTYPE_ERROR
;
1849 sBuffSymbol
.append(cToken
);
1852 eSymbolType
= NUMBERFORMAT_OP_NE
;
1857 eSymbolType
= BRACKET_SYMBOLTYPE_ERROR
;
1863 sBuffSymbol
.append(cToken
);
1865 eSymbolType
= NUMBERFORMAT_OP_LE
;
1867 else if (cLetter
== '>')
1869 sBuffSymbol
.append(cToken
);
1871 eSymbolType
= NUMBERFORMAT_OP_GE
;
1876 eSymbolType
= BRACKET_SYMBOLTYPE_ERROR
;
1881 rString
.remove(nPos
,1);
1897 sBuffSymbol
.append(cToken
);
1904 sSymbol
= sBuffSymbol
.makeStringAndClear();
1908 void SvNumberformat::ConvertLanguage( SvNumberFormatter
& rConverter
,
1909 LanguageType eConvertFrom
,
1910 LanguageType eConvertTo
)
1912 sal_Int32 nCheckPos
;
1914 SvNumFormatType nType
= eType
;
1915 OUString
aFormatString( sFormatstring
);
1916 rConverter
.PutandConvertEntry( aFormatString
, nCheckPos
, nType
,
1917 nKey
, eConvertFrom
, eConvertTo
, false);
1918 const SvNumberformat
* pFormat
= rConverter
.GetEntry( nKey
);
1919 DBG_ASSERT( pFormat
, "SvNumberformat::ConvertLanguage: Conversion without format" );
1922 ImpCopyNumberformat( *pFormat
);
1923 // Reset values taken over from Formatter/Scanner
1924 // pColor still points to table in temporary Formatter/Scanner
1925 for (ImpSvNumFor
& rFormatter
: NumFor
)
1927 OUString aColorName
= rFormatter
.GetColorName();
1928 const Color
* pColor
= rScan
.GetColor( aColorName
);
1929 rFormatter
.SetColor( pColor
, aColorName
);
1934 bool SvNumberformat::HasNewCurrency() const
1936 for (const auto & j
: NumFor
)
1938 if ( j
.HasNewCurrency() )
1946 bool SvNumberformat::GetNewCurrencySymbol( OUString
& rSymbol
,
1947 OUString
& rExtension
) const
1949 for (const auto & j
: NumFor
)
1951 if ( j
.GetNewCurrencySymbol( rSymbol
, rExtension
) )
1962 OUString
SvNumberformat::StripNewCurrencyDelimiters( const OUString
& rStr
)
1964 OUStringBuffer
aTmp(rStr
.getLength());
1965 sal_Int32 nStartPos
, nPos
, nLen
;
1966 nLen
= rStr
.getLength();
1968 while ( (nPos
= rStr
.indexOf( "[$", nStartPos
)) >= 0 )
1971 if ( (nEnd
= GetQuoteEnd( rStr
, nPos
)) >= 0 )
1973 aTmp
.append(rStr
.subView( nStartPos
, ++nEnd
- nStartPos
));
1978 aTmp
.append(rStr
.subView(nStartPos
, nPos
- nStartPos
) );
1979 nStartPos
= nPos
+ 2;
1981 nEnd
= nStartPos
- 1;
1984 nDash
= rStr
.indexOf( '-', ++nEnd
);
1985 nEnd
= GetQuoteEnd( rStr
, nDash
);
1987 while ( nEnd
>= 0 );
1989 nEnd
= nStartPos
- 1;
1992 nClose
= rStr
.indexOf( ']', ++nEnd
);
1993 nEnd
= GetQuoteEnd( rStr
, nClose
);
1995 while ( nEnd
>= 0 );
1999 /* there should always be a closing ]
2000 * but the old String class would have hidden
2001 * that. so be conservative too
2007 if(nDash
>= 0 && nDash
< nClose
)
2011 aTmp
.append(rStr
.subView(nStartPos
, nPos
- nStartPos
) );
2012 nStartPos
= nClose
+ 1;
2015 if ( nLen
> nStartPos
)
2017 aTmp
.append(rStr
.subView(nStartPos
, nLen
- nStartPos
) );
2019 return aTmp
.makeStringAndClear();
2022 void SvNumberformat::ImpGetOutputStandard(double& fNumber
, OUStringBuffer
& rOutString
) const
2025 ImpGetOutputStandard(fNumber
, sTemp
);
2029 void SvNumberformat::ImpGetOutputStandard(double& fNumber
, OUString
& rOutString
) const
2031 sal_uInt16 nStandardPrec
= rScan
.GetStandardPrec();
2033 if ( fabs(fNumber
) > EXP_ABS_UPPER_BOUND
)
2035 nStandardPrec
= ::std::min(nStandardPrec
, static_cast<sal_uInt16
>(14)); // limits to 14 decimals
2036 rOutString
= ::rtl::math::doubleToUString( fNumber
,
2037 rtl_math_StringFormat_E2
, nStandardPrec
/*2*/,
2038 GetFormatter().GetNumDecimalSep()[0]);
2042 ImpGetOutputStdToPrecision(fNumber
, rOutString
, nStandardPrec
);
2049 template<typename T
>
2050 bool checkForAll0s(const T
& rString
, sal_Int32 nIdx
=0)
2052 if (nIdx
>=rString
.getLength())
2057 if (rString
[nIdx
]!='0')
2060 while (++nIdx
<rString
.getLength());
2067 void SvNumberformat::ImpGetOutputStdToPrecision(double& rNumber
, OUString
& rOutString
, sal_uInt16 nPrecision
) const
2069 // Make sure the precision doesn't go over the maximum allowable precision.
2070 nPrecision
= ::std::min(UPPER_PRECISION
, nPrecision
);
2072 // We decided to strip trailing zeros unconditionally, since binary
2073 // double-precision rounding error makes it impossible to determine e.g.
2074 // whether 844.10000000000002273737 is what the user has typed, or the
2075 // user has typed 844.1 but IEEE 754 represents it that way internally.
2077 rOutString
= ::rtl::math::doubleToUString( rNumber
,
2078 rtl_math_StringFormat_F
, nPrecision
/*2*/,
2079 GetFormatter().GetNumDecimalSep()[0], true );
2080 if (rOutString
[0] == '-' && checkForAll0s(rOutString
, 1))
2082 rOutString
= comphelper::string::stripStart(rOutString
, '-'); // not -0
2084 rOutString
= impTransliterate(rOutString
, NumFor
[0].GetNatNum());
2087 void SvNumberformat::ImpGetOutputInputLine(double fNumber
, OUString
& OutString
) const
2089 bool bModified
= false;
2090 if ( (eType
& SvNumFormatType::PERCENT
) && (fabs(fNumber
) < D_MAX_D_BY_100
))
2107 OutString
= ::rtl::math::doubleToUString( fNumber
,
2108 rtl_math_StringFormat_Automatic
,
2109 rtl_math_DecimalPlaces_Max
,
2110 GetFormatter().GetNumDecimalSep()[0], true );
2112 if ( eType
& SvNumFormatType::PERCENT
&& bModified
)
2118 short SvNumberformat::ImpCheckCondition(double fNumber
,
2120 SvNumberformatLimitOps eOp
)
2124 case NUMBERFORMAT_OP_NO
:
2126 case NUMBERFORMAT_OP_EQ
:
2127 return static_cast<short>(fNumber
== fLimit
);
2128 case NUMBERFORMAT_OP_NE
:
2129 return static_cast<short>(fNumber
!= fLimit
);
2130 case NUMBERFORMAT_OP_LT
:
2131 return static_cast<short>(fNumber
< fLimit
);
2132 case NUMBERFORMAT_OP_LE
:
2133 return static_cast<short>(fNumber
<= fLimit
);
2134 case NUMBERFORMAT_OP_GT
:
2135 return static_cast<short>(fNumber
> fLimit
);
2136 case NUMBERFORMAT_OP_GE
:
2137 return static_cast<short>(fNumber
>= fLimit
);
2143 static bool lcl_appendStarFillChar( OUStringBuffer
& rBuf
, const OUString
& rStr
)
2145 // Right during user input the star symbol is the very
2146 // last character before the user enters another one.
2147 if (rStr
.getLength() > 1)
2149 rBuf
.append(u
'\x001B');
2150 rBuf
.append(rStr
[1]);
2156 static bool lcl_insertStarFillChar( OUStringBuffer
& rBuf
, sal_Int32 nPos
, const OUString
& rStr
)
2158 if (rStr
.getLength() > 1)
2160 rBuf
.insert( nPos
, rStr
[1]);
2161 rBuf
.insert( nPos
, u
'\x001B');
2167 void SvNumberformat::GetOutputString(std::u16string_view sString
,
2168 OUString
& OutString
,
2169 const Color
** ppColor
)
2171 OUStringBuffer sOutBuff
;
2173 if (eType
& SvNumFormatType::TEXT
)
2177 else if (NumFor
[3].GetCount() > 0)
2183 *ppColor
= nullptr; // no change of color
2186 *ppColor
= NumFor
[nIx
].GetColor();
2187 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
2188 if (rInfo
.eScannedType
== SvNumFormatType::TEXT
)
2190 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
2191 for (sal_uInt16 i
= 0; i
< nCnt
; i
++)
2193 switch (rInfo
.nTypeArray
[i
])
2195 case NF_SYMBOLTYPE_STAR
:
2198 lcl_appendStarFillChar( sOutBuff
, rInfo
.sStrArray
[i
]);
2201 case NF_SYMBOLTYPE_BLANK
:
2202 if (rInfo
.sStrArray
[i
].getLength() >= 2)
2203 InsertBlanks( sOutBuff
, sOutBuff
.getLength(), rInfo
.sStrArray
[i
][1] );
2205 case NF_KEY_GENERAL
: // #77026# "General" is the same as "@"
2206 case NF_SYMBOLTYPE_DEL
:
2207 sOutBuff
.append(sString
);
2210 sOutBuff
.append(rInfo
.sStrArray
[i
]);
2214 OutString
= sOutBuff
.makeStringAndClear();
2219 void lcl_GetOutputStringScientific(double fNumber
, sal_uInt16 nCharCount
,
2220 const SvNumberFormatter
& rFormatter
, OUString
& rOutString
)
2222 bool bSign
= std::signbit(fNumber
);
2224 // 1.000E+015 (one digit and the decimal point, and the two chars +
2225 // nExpDigit for the exponential part, totalling 6 or 7).
2226 double fExp
= log10( fabs(fNumber
) );
2229 sal_uInt16 nCharFormat
= 6 + (fExp
>= 100.0 ? 1 : 0);
2230 sal_uInt16 nPrec
= nCharCount
> nCharFormat
? nCharCount
- nCharFormat
: 0;
2233 // Make room for the negative sign.
2236 nPrec
= ::std::min(nPrec
, static_cast<sal_uInt16
>(14)); // limit to 14 decimals.
2238 rOutString
= ::rtl::math::doubleToUString(fNumber
, rtl_math_StringFormat_E2
,
2239 nPrec
, rFormatter
.GetNumDecimalSep()[0], true );
2242 OUString
lcl_GetPercentString(const ImpSvNumberformatInfo
&rInfo
, sal_uInt16 nCnt
)
2245 OUStringBuffer aPercentString
;
2246 for( i
= 0; i
< nCnt
; i
++ )
2248 if( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_PERCENT
)
2250 aPercentString
.append( rInfo
.sStrArray
[i
] );
2251 bool bStringFound
= false;
2252 for( i
--; i
>= 0 && rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
; i
-- )
2256 bStringFound
= true;
2257 aPercentString
.insert( 0, "\"" );
2259 aPercentString
.insert( 0, rInfo
.sStrArray
[i
] );
2263 aPercentString
.insert( 0, "\"" );
2266 return aPercentString
.makeStringAndClear();
2269 OUString
lcl_GetDenominatorString(const ImpSvNumberformatInfo
&rInfo
, sal_uInt16 nCnt
)
2272 OUStringBuffer aDenominatorString
;
2273 for( i
= 0; i
< nCnt
; i
++ )
2275 if( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_FRAC
)
2277 while ( ( ++i
< nCnt
) && rInfo
.nTypeArray
[i
] != NF_SYMBOLTYPE_FRAC_FDIV
2278 && rInfo
.nTypeArray
[i
] != NF_SYMBOLTYPE_DIGIT
);
2279 for( ; i
< nCnt
; i
++ )
2281 if( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_FRAC_FDIV
|| rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_DIGIT
)
2282 aDenominatorString
.append( rInfo
.sStrArray
[i
] );
2288 return aDenominatorString
.makeStringAndClear();
2291 OUString
lcl_GetNumeratorString(const ImpSvNumberformatInfo
&rInfo
, sal_uInt16 nCnt
)
2294 OUStringBuffer aNumeratorString
;
2295 for( i
= 0; i
< nCnt
; i
++ )
2297 if( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_FRAC
)
2299 for( i
--; i
>= 0 && rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_DIGIT
; i
-- )
2301 aNumeratorString
.insert( 0, rInfo
.sStrArray
[i
] );
2306 return aNumeratorString
.makeStringAndClear();
2309 OUString
lcl_GetFractionIntegerString(const ImpSvNumberformatInfo
&rInfo
, sal_uInt16 nCnt
)
2312 OUStringBuffer aIntegerString
;
2313 for( i
= 0; i
< nCnt
; i
++ )
2315 if( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_FRACBLANK
)
2317 for( i
--; i
>= 0 && ( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_DIGIT
2318 || rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_THSEP
); i
-- )
2320 aIntegerString
.insert( 0, rInfo
.sStrArray
[i
] );
2325 return aIntegerString
.makeStringAndClear();
2328 OUString
lcl_GetIntegerFractionDelimiterString(const ImpSvNumberformatInfo
&rInfo
, sal_uInt16 nCnt
)
2331 for( i
= 0; i
< nCnt
; i
++ )
2333 if( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_FRACBLANK
)
2335 return rInfo
.sStrArray
[i
];
2343 OUString
SvNumberformat::GetPercentString( sal_uInt16 nNumFor
) const
2345 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nNumFor
].Info();
2346 sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
2347 return lcl_GetPercentString( rInfo
, nCnt
);
2350 OUString
SvNumberformat::GetDenominatorString( sal_uInt16 nNumFor
) const
2352 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nNumFor
].Info();
2353 sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
2354 return lcl_GetDenominatorString( rInfo
, nCnt
);
2357 OUString
SvNumberformat::GetNumeratorString( sal_uInt16 nNumFor
) const
2359 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nNumFor
].Info();
2360 sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
2361 return lcl_GetNumeratorString( rInfo
, nCnt
);
2364 OUString
SvNumberformat::GetIntegerFractionDelimiterString( sal_uInt16 nNumFor
) const
2366 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nNumFor
].Info();
2367 sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
2368 return lcl_GetIntegerFractionDelimiterString( rInfo
, nCnt
);
2371 bool SvNumberformat::GetOutputString(double fNumber
, sal_uInt16 nCharCount
, OUString
& rOutString
) const
2373 if (eType
!= SvNumFormatType::NUMBER
)
2377 double fTestNum
= fNumber
;
2378 bool bSign
= std::signbit(fTestNum
);
2381 fTestNum
= -fTestNum
;
2383 if (fTestNum
< EXP_LOWER_BOUND
)
2385 lcl_GetOutputStringScientific(fNumber
, nCharCount
, GetFormatter(), rOutString
);
2389 double fExp
= log10(fTestNum
);
2390 // Values < 1.0 always have one digit before the decimal point.
2391 sal_uInt16 nDigitPre
= fExp
>= 0.0 ? static_cast<sal_uInt16
>(ceil(fExp
)) : 1;
2395 lcl_GetOutputStringScientific(fNumber
, nCharCount
, GetFormatter(), rOutString
);
2399 sal_uInt16 nPrec
= nCharCount
>= nDigitPre
? nCharCount
- nDigitPre
: 0;
2402 // Subtract the negative sign.
2407 // Subtract the decimal point.
2410 ImpGetOutputStdToPrecision(fNumber
, rOutString
, nPrec
);
2411 if (rOutString
.getLength() > nCharCount
)
2413 // String still wider than desired. Switch to scientific notation.
2414 lcl_GetOutputStringScientific(fNumber
, nCharCount
, GetFormatter(), rOutString
);
2419 sal_uInt16
SvNumberformat::GetSubformatIndex (double fNumber
) const
2421 sal_uInt16 nIx
; // Index of the partial format
2422 double fLimit_1
= fLimit1
;
2423 short nCheck
= ImpCheckCondition(fNumber
, fLimit_1
, eOp1
);
2424 if (nCheck
== -1 || nCheck
== 1) // Only 1 String or True
2430 double fLimit_2
= fLimit2
;
2431 nCheck
= ImpCheckCondition(fNumber
, fLimit_2
, eOp2
);
2432 if (nCheck
== -1 || nCheck
== 1)
2444 bool SvNumberformat::GetOutputString(double fNumber
,
2445 OUString
& OutString
,
2446 const Color
** ppColor
)
2450 *ppColor
= nullptr; // No color change
2451 if (eType
& SvNumFormatType::LOGICAL
&& sFormatstring
== rScan
.GetKeywords()[NF_KEY_BOOLEAN
])
2455 OutString
= rScan
.GetTrueString();
2459 OutString
= rScan
.GetFalseString();
2463 OUStringBuffer
sBuff(64);
2464 if (eType
& SvNumFormatType::TEXT
)
2466 ImpGetOutputStandard(fNumber
, sBuff
);
2467 OutString
= sBuff
.makeStringAndClear();
2470 bool bHadStandard
= false;
2471 if (bStandard
) // Individual standard formats
2473 if (rScan
.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION
) // All number format InputLine
2475 ImpGetOutputInputLine(fNumber
, OutString
);
2480 case SvNumFormatType::NUMBER
: // Standard number format
2481 if (rScan
.GetStandardPrec() == SvNumberFormatter::UNLIMITED_PRECISION
)
2483 if (std::signbit(fNumber
))
2485 if (!(fNumber
< 0.0))
2486 fNumber
= -fNumber
; // do not display -0.0
2492 else if (fNumber
< 1.0 && fNumber
> -1.0)
2494 // Decide whether to display as 0.000000123... or 1.23...e-07
2495 bool bFix
= (fNumber
< -EXP_LOWER_BOUND
|| EXP_LOWER_BOUND
< fNumber
);
2498 // Arbitrary, not too many 0s visually, start E2 at 1E-10.
2499 constexpr sal_Int32 kMaxExp
= 9;
2500 const sal_Int32 nExp
= static_cast<sal_Int32
>(ceil( -log10( fabs( fNumber
))));
2501 if (nExp
<= kMaxExp
&& rtl::math::approxEqual(
2502 rtl::math::round( fNumber
, 16), rtl::math::round( fNumber
, nExp
+ 16)))
2504 // Not too many significant digits or accuracy
2505 // artefacts, otherwise leave everything to E2
2511 OutString
= ::rtl::math::doubleToUString( fNumber
,
2512 rtl_math_StringFormat_F
,
2513 rtl_math_DecimalPlaces_Max
,
2514 GetFormatter().GetNumDecimalSep()[0], true);
2516 OutString
= ::rtl::math::doubleToUString( fNumber
,
2517 rtl_math_StringFormat_E2
,
2518 rtl_math_DecimalPlaces_Max
,
2519 GetFormatter().GetNumDecimalSep()[0], true);
2523 OutString
= ::rtl::math::doubleToUString( fNumber
,
2524 rtl_math_StringFormat_Automatic
,
2525 rtl_math_DecimalPlaces_Max
,
2526 GetFormatter().GetNumDecimalSep()[0], true);
2530 ImpGetOutputStandard(fNumber
, sBuff
);
2531 bHadStandard
= true;
2533 case SvNumFormatType::DATE
:
2534 bRes
|= ImpGetDateOutput(fNumber
, 0, sBuff
);
2535 bHadStandard
= true;
2537 case SvNumFormatType::TIME
:
2538 bRes
|= ImpGetTimeOutput(fNumber
, 0, sBuff
);
2539 bHadStandard
= true;
2541 case SvNumFormatType::DATETIME
:
2542 bRes
|= ImpGetDateTimeOutput(fNumber
, 0, sBuff
);
2543 bHadStandard
= true;
2548 if ( !bHadStandard
)
2550 sal_uInt16 nIx
= GetSubformatIndex ( fNumber
); // Index of the partial format
2551 if (fNumber
< 0.0 &&
2552 ((nIx
== 0 && IsFirstSubformatRealNegative()) || // 1st, usually positive subformat
2553 (nIx
== 1 && IsSecondSubformatRealNegative()))) // 2nd, usually negative subformat
2555 fNumber
= -fNumber
; // eliminate sign
2557 *ppColor
= NumFor
[nIx
].GetColor();
2558 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
2559 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
2560 if (nCnt
== 0 && rInfo
.eScannedType
== SvNumFormatType::EMPTY
)
2562 return false; // Empty => nothing
2564 else if (nCnt
== 0) // Else Standard Format
2566 ImpGetOutputStandard(fNumber
, sBuff
);
2567 OutString
= sBuff
.makeStringAndClear();
2570 switch (rInfo
.eScannedType
)
2572 case SvNumFormatType::TEXT
:
2573 case SvNumFormatType::DEFINED
:
2574 for (sal_uInt16 i
= 0; i
< nCnt
; i
++)
2576 switch (rInfo
.nTypeArray
[i
])
2578 case NF_SYMBOLTYPE_STAR
:
2581 bRes
= lcl_appendStarFillChar( sBuff
, rInfo
.sStrArray
[i
]);
2584 case NF_SYMBOLTYPE_BLANK
:
2585 if (rInfo
.sStrArray
[i
].getLength() >= 2)
2586 InsertBlanks(sBuff
, sBuff
.getLength(), rInfo
.sStrArray
[i
][1] );
2588 case NF_SYMBOLTYPE_STRING
:
2589 case NF_SYMBOLTYPE_CURRENCY
:
2590 sBuff
.append(rInfo
.sStrArray
[i
]);
2592 case NF_SYMBOLTYPE_THSEP
:
2593 if (rInfo
.nThousand
== 0)
2595 sBuff
.append(rInfo
.sStrArray
[i
]);
2603 case SvNumFormatType::DATE
:
2604 bRes
|= ImpGetDateOutput(fNumber
, nIx
, sBuff
);
2606 case SvNumFormatType::TIME
:
2607 bRes
|= ImpGetTimeOutput(fNumber
, nIx
, sBuff
);
2609 case SvNumFormatType::DATETIME
:
2610 bRes
|= ImpGetDateTimeOutput(fNumber
, nIx
, sBuff
);
2612 case SvNumFormatType::NUMBER
:
2613 case SvNumFormatType::PERCENT
:
2614 case SvNumFormatType::CURRENCY
:
2615 bRes
|= ImpGetNumberOutput(fNumber
, nIx
, sBuff
);
2617 case SvNumFormatType::LOGICAL
:
2618 bRes
|= ImpGetLogicalOutput(fNumber
, nIx
, sBuff
);
2620 case SvNumFormatType::FRACTION
:
2621 bRes
|= ImpGetFractionOutput(fNumber
, nIx
, sBuff
);
2623 case SvNumFormatType::SCIENTIFIC
:
2624 bRes
|= ImpGetScientificOutput(fNumber
, nIx
, sBuff
);
2629 OutString
= sBuff
.makeStringAndClear();
2633 bool SvNumberformat::ImpGetScientificOutput(double fNumber
,
2635 OUStringBuffer
& sStr
)
2640 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
2641 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
2645 if (nIx
== 0) // Not in the ones at the end
2647 bSign
= true; // Formats
2652 sStr
= ::rtl::math::doubleToUString( fNumber
,
2653 rtl_math_StringFormat_E
,
2654 rInfo
.nCntPre
+ rInfo
.nCntPost
- 1, '.' );
2655 OUStringBuffer ExpStr
;
2657 sal_Int32 nExPos
= sStr
.indexOf('E');
2658 sal_Int32 nDecPos
= -1;
2662 // split into mantissa and exponent and get rid of "E+" or "E-"
2663 sal_Int32 nExpStart
= nExPos
+ 1;
2665 switch ( sStr
[ nExpStart
] )
2674 ExpStr
= sStr
.subView( nExpStart
); // part following the "E+"
2675 sStr
.truncate( nExPos
);
2677 if ( rInfo
.nCntPre
!= 1 ) // rescale Exp
2679 sal_Int32 nExp
= ExpStr
.toString().toInt32() * nExpSign
;
2680 sal_Int32 nRescale
= (rInfo
.nCntPre
!= 0) ? nExp
% static_cast<sal_Int32
>(rInfo
.nCntPre
) : -1;
2681 if( nRescale
< 0 && rInfo
.nCntPre
!= 0 )
2682 nRescale
+= static_cast<sal_Int32
>(rInfo
.nCntPre
);
2693 ExpStr
= OUString::number( nExp
);
2694 const sal_Unicode cFirstDigit
= sStr
[0];
2696 sStr
= ::rtl::math::doubleToUString( fNumber
,
2697 rtl_math_StringFormat_E
,
2698 nRescale
+ rInfo
.nCntPost
, '.' );
2700 // sStr now may contain a rounded-up value shifted into the next
2701 // magnitude, for example 1.000E+02 (4 digits) for fNumber 99.995
2702 // (9.9995E+02 rounded to 3 decimals) but we want the final result
2703 // to be 100.00E+00 (5 digits), so for the following fill routines
2704 // below to work correctly append a zero decimal.
2705 /* TODO: this is awkward, could an engineering notation mode be
2706 * introduced to rtl_math_doubleToUString()? */
2707 sStr
.truncate( sStr
.indexOf('E') );
2708 if (sStr
[0] == '1' && cFirstDigit
!= '1')
2712 // cut any decimal delimiter
2713 sal_Int32 index
= 0;
2715 while((index
= sStr
.indexOf('.', index
)) >= 0)
2719 sStr
.remove(index
, 1);
2723 sal_uInt16 j
= nCnt
-1; // Last symbol
2724 sal_Int32 k
; // Position in ExpStr
2725 sal_Int32 nZeros
= 0; // Erase leading zeros
2727 bRes
|= ImpNumberFill(ExpStr
, fNumber
, k
, j
, nIx
, NF_SYMBOLTYPE_EXP
);
2729 while (nZeros
< k
&& ExpStr
[nZeros
] == '0')
2735 ExpStr
.remove( 0, nZeros
);
2740 if (rInfo
.nTypeArray
[j
] == NF_SYMBOLTYPE_EXP
)
2742 const OUString
& rStr
= rInfo
.sStrArray
[j
];
2745 ExpStr
.insert(0, '-');
2747 else if (rStr
.getLength() > 1 && rStr
[1] == '+')
2749 ExpStr
.insert(0, '+');
2751 ExpStr
.insert(0, rStr
[0]);
2761 // Continue main number:
2768 bRes
|= ImpDecimalFill(sStr
, fNumber
, nDecPos
, j
, nIx
, false);
2773 sStr
.insert(0, '-');
2775 sStr
.append(ExpStr
);
2780 double SvNumberformat::GetRoundFractionValue ( double fNumber
) const
2782 sal_uInt16 nIx
= GetSubformatIndex ( fNumber
);
2783 double fIntPart
= 0.0; // integer part of fraction
2784 sal_Int64 nFrac
= 0, nDiv
= 1; // numerator and denominator
2785 double fSign
= (fNumber
< 0.0) ? -1.0 : 1.0;
2786 // fNumber is modified in ImpGetFractionElements to absolute fractional part
2787 ImpGetFractionElements ( fNumber
, nIx
, fIntPart
, nFrac
, nDiv
);
2789 return fSign
* ( fIntPart
+ static_cast<double>(nFrac
) / static_cast<double>(nDiv
) );
2791 return fSign
* fIntPart
;
2794 void SvNumberformat::ImpGetFractionElements ( double& fNumber
, sal_uInt16 nIx
,
2795 double& fIntPart
, sal_Int64
& nFrac
, sal_Int64
& nDiv
) const
2797 if ( fNumber
< 0.0 )
2799 fIntPart
= floor(fNumber
); // Integral part
2800 fNumber
-= fIntPart
; // Fractional part
2801 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
2802 nDiv
= lcl_GetDenominatorString( rInfo
, NumFor
[nIx
].GetCount() ).toInt32();
2804 { // Forced Denominator
2805 nFrac
= static_cast<sal_Int64
>(floor ( fNumber
* nDiv
));
2806 double fFracNew
= static_cast<double>(nFrac
) / static_cast<double>(nDiv
);
2807 double fFracNew1
= static_cast<double>(nFrac
+ 1) / static_cast<double>(nDiv
);
2808 double fDiff
= fNumber
- fFracNew
;
2809 if( fDiff
> ( fFracNew1
- fNumber
) )
2814 else // Calculated Denominator
2817 sal_Int64 nBasis
= static_cast<sal_Int64
>(floor( pow(10.0,rInfo
.nCntExp
))) - 1; // 9, 99, 999 ,...
2818 sal_Int64 nFracPrev
= 1, nDivPrev
= 0, nFracNext
, nDivNext
, nPartialDenom
;
2819 double fRemainder
= fNumber
;
2821 // Use continued fraction representation of fNumber
2822 // See https://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximations
2823 while ( fRemainder
> 0.0 )
2825 double fTemp
= 1.0 / fRemainder
; // 64bits precision required when fRemainder is very weak
2826 nPartialDenom
= static_cast<sal_Int64
>(floor(fTemp
)); // due to floating point notation with double precision
2827 fRemainder
= fTemp
- static_cast<double>(nPartialDenom
);
2828 nDivNext
= nPartialDenom
* nDiv
+ nDivPrev
;
2829 if ( nDivNext
<= nBasis
) // continue loop
2831 nFracNext
= nPartialDenom
* nFrac
+ nFracPrev
;
2837 else // calculate collateral fraction and exit
2839 sal_Int64 nCollat
= (nBasis
- nDivPrev
) / nDiv
;
2840 if ( 2 * nCollat
>= nPartialDenom
)
2842 sal_Int64 nFracTest
= nCollat
* nFrac
+ nFracPrev
;
2843 sal_Int64 nDivTest
= nCollat
* nDiv
+ nDivPrev
;
2844 double fSign
= (static_cast<double>(nFrac
) > fNumber
* static_cast<double>(nDiv
))?1.0:-1.0;
2845 if ( fSign
* ( double(nFrac
* nDivTest
+ nDiv
* nFracTest
) - 2.0 * double(nDiv
* nDivTest
) * fNumber
) > 0.0 )
2851 fRemainder
= 0.0; // exit while loop
2862 bool SvNumberformat::ImpGetFractionOutput(double fNumber
,
2864 OUStringBuffer
& sBuff
)
2867 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
2868 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
2869 OUStringBuffer sStr
, sFrac
, sDiv
; // Strings, value for Integral part Numerator and denominator
2870 bool bSign
= ( (fNumber
< 0) && (nIx
== 0) ); // sign Not in the ones at the end
2871 const OUString sIntegerFormat
= lcl_GetFractionIntegerString(rInfo
, nCnt
);
2872 const OUString sNumeratorFormat
= lcl_GetNumeratorString(rInfo
, nCnt
);
2873 const OUString sDenominatorFormat
= lcl_GetDenominatorString(rInfo
, nCnt
);
2875 sal_Int64 nFrac
= 0, nDiv
= 1;
2876 double fNum
= floor(fNumber
); // Integral part
2878 if (fNum
> D_MAX_U_INT32
|| rInfo
.nCntExp
> 9) // Too large
2880 sBuff
= ImpSvNumberformatScan::sErrStr
;
2883 if (rInfo
.nCntExp
== 0)
2885 SAL_WARN( "svl.numbers", "SvNumberformat:: Fraction, nCntExp == 0");
2890 ImpGetFractionElements( fNumber
, nIx
, fNum
, nFrac
, nDiv
);
2892 if (rInfo
.nCntPre
== 0) // Improper fraction
2894 double fNum1
= fNum
* static_cast<double>(nDiv
) + static_cast<double>(nFrac
);
2896 if (fNum1
> D_MAX_INTEGER
)
2898 sBuff
= ImpSvNumberformatScan::sErrStr
;
2901 nFrac
= static_cast<sal_Int64
>(floor(fNum1
));
2903 else if (fNum
== 0.0 && nFrac
!= 0)
2909 sprintf( aBuf
, "%.f", fNum
); // simple rounded integer (#100211# - checked)
2910 sStr
.appendAscii( aBuf
);
2911 impTransliterate(sStr
, NumFor
[nIx
].GetNatNum());
2913 bool bHideFraction
= (rInfo
.nCntPre
> 0 && nFrac
== 0
2914 && (sNumeratorFormat
.indexOf('0') < 0)
2915 && (sDenominatorFormat
.indexOf('0') < 0
2916 || sDenominatorFormat
.toInt32() > 0) );
2917 if ( bHideFraction
)
2921 else // if there are some '0' in format, force display of fraction
2923 sFrac
= ImpIntToString( nIx
, nFrac
);
2924 sDiv
= ImpIntToString( nIx
, nDiv
);
2927 sal_uInt16 j
= nCnt
-1; // Last symbol -> backwards
2928 sal_Int32 k
; // Denominator
2930 bRes
|= ImpNumberFill(sDiv
, fNumber
, k
, j
, nIx
, NF_SYMBOLTYPE_FRAC
, true);
2933 if (rInfo
.nTypeArray
[j
] == NF_SYMBOLTYPE_FRAC
)
2935 if ( bHideFraction
)
2936 { // do not insert blank for fraction if there is no '?'
2937 if ( sNumeratorFormat
.indexOf('?') >= 0
2938 || sDenominatorFormat
.indexOf('?') >= 0 )
2939 sDiv
.insert(0, ' ');
2943 sDiv
.insert(0, rInfo
.sStrArray
[j
][0]);
2954 // Further numerators:
2961 bRes
|= ImpNumberFill(sFrac
, fNumber
, k
, j
, nIx
, NF_SYMBOLTYPE_FRACBLANK
);
2962 bCont
= false; // there is no integer part?
2963 if (rInfo
.nTypeArray
[j
] == NF_SYMBOLTYPE_FRACBLANK
)
2967 if ( bHideFraction
)
2968 { // '?' in any format force display of blank as delimiter
2969 if ( sIntegerFormat
.indexOf('?') >= 0
2970 || sNumeratorFormat
.indexOf('?') >= 0
2971 || sDenominatorFormat
.indexOf('?') >= 0 )
2973 for (sal_Int32 i
= 0; i
< rInfo
.sStrArray
[j
].getLength(); i
++)
2974 sFrac
.insert(0, ' ');
2979 if ( fNum
!= 0.0 || sIntegerFormat
.indexOf('0') >= 0 )
2980 sFrac
.insert(0, rInfo
.sStrArray
[j
]); // insert Blank string only if there are both integer and fraction
2983 if ( sIntegerFormat
.indexOf('?') >= 0
2984 || sNumeratorFormat
.indexOf('?') >= 0 )
2986 for (sal_Int32 i
= 0; i
< rInfo
.sStrArray
[j
].getLength(); i
++)
2987 sFrac
.insert(0, ' ');
2992 bCont
= true; // Yes, there is an integer
2995 sFrac
.insert(0, rInfo
.sStrArray
[j
]);
2998 // Continue integer part
3005 k
= sStr
.getLength(); // After last figure
3006 bRes
|= ImpNumberFillWithThousands(sStr
, fNumber
, k
, j
, nIx
,
3009 if (bSign
&& (nFrac
!= 0 || fNum
!= 0.0))
3011 sBuff
.insert(0, '-'); // Not -0
3014 sBuff
.append(sFrac
);
3019 sal_uInt16
SvNumberformat::ImpGetFractionOfSecondString( OUStringBuffer
& rBuf
, double fFractionOfSecond
,
3020 int nFractionDecimals
, bool bAddOneRoundingDecimal
, sal_uInt16 nIx
, sal_uInt16 nMinimumInputLineDecimals
)
3022 if (!nFractionDecimals
)
3025 // nFractionDecimals+1 to not round up what Time::GetClock() carefully
3027 rBuf
.append( rtl::math::doubleToUString( fFractionOfSecond
, rtl_math_StringFormat_F
,
3028 (bAddOneRoundingDecimal
? nFractionDecimals
+ 1 : nFractionDecimals
), '.'));
3029 rBuf
.stripStart('0');
3030 rBuf
.stripStart('.');
3031 if (bAddOneRoundingDecimal
&& rBuf
.getLength() > nFractionDecimals
)
3032 rBuf
.truncate( nFractionDecimals
); // the digit appended because of nFractionDecimals+1
3033 if (nMinimumInputLineDecimals
)
3036 for (sal_Int32 index
= rBuf
.getLength(); index
< nMinimumInputLineDecimals
; ++index
)
3040 impTransliterate(rBuf
, NumFor
[nIx
].GetNatNum());
3041 nFractionDecimals
= rBuf
.getLength();
3045 impTransliterate(rBuf
, NumFor
[nIx
].GetNatNum());
3047 return static_cast<sal_uInt16
>(nFractionDecimals
);
3050 bool SvNumberformat::ImpGetTimeOutput(double fNumber
,
3052 OUStringBuffer
& sBuff
)
3054 using namespace ::com::sun::star::i18n
;
3055 bool bCalendarSet
= false;
3056 const double fNumberOrig
= fNumber
;
3067 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
3070 if ( rScan
.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION
&&
3071 0 < rInfo
.nCntPost
&& rInfo
.nCntPost
< 7 )
3072 { // round at 7 decimals (+5 of 86400 == 12 significant digits)
3079 nCntPost
= rInfo
.nCntPost
;
3082 OUStringBuffer sSecStr
;
3083 sal_Int32 nSecPos
= 0; // For figure by figure processing
3084 sal_uInt32 nHour
, nMin
, nSec
;
3085 if (!rInfo
.bThousand
) // No [] format
3087 sal_uInt16 nCHour
, nCMinute
, nCSecond
;
3088 double fFractionOfSecond
;
3089 tools::Time::GetClock( fNumberOrig
, nCHour
, nCMinute
, nCSecond
, fFractionOfSecond
, nCntPost
);
3093 nCntPost
= ImpGetFractionOfSecondString( sSecStr
, fFractionOfSecond
, nCntPost
, true, nIx
,
3094 (bInputLine
? rInfo
.nCntPost
: 0));
3098 const double fTime
= rtl::math::round( fNumber
* 86400.0, int(nCntPost
));
3099 if (bSign
&& fTime
== 0.0)
3101 bSign
= false; // Not -00:00:00
3103 if (fTime
> D_MAX_U_INT32
)
3105 sBuff
= ImpSvNumberformatScan::sErrStr
;
3108 sal_uInt32 nSeconds
= static_cast<sal_uInt32
>(fTime
);
3110 nCntPost
= ImpGetFractionOfSecondString( sSecStr
, fTime
- nSeconds
, nCntPost
, false, nIx
,
3111 (bInputLine
? rInfo
.nCntPost
: 0));
3113 if (rInfo
.nThousand
== 3) // [ss]
3119 else if (rInfo
.nThousand
== 2) // [mm]:ss
3122 nMin
= nSeconds
/ 60;
3123 nSec
= nSeconds
% 60;
3125 else if (rInfo
.nThousand
== 1) // [hh]:mm:ss
3127 nHour
= nSeconds
/ 3600;
3128 nMin
= (nSeconds
%3600) / 60;
3133 // TODO What should these be set to?
3140 sal_Unicode cAmPm
= ' '; // a or p
3141 if (rInfo
.nCntExp
) // AM/PM
3148 else if (nHour
< 12)
3161 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
3162 for (sal_uInt16 i
= 0; i
< nCnt
; i
++)
3165 switch (rInfo
.nTypeArray
[i
])
3167 case NF_SYMBOLTYPE_STAR
:
3170 bRes
= lcl_appendStarFillChar( sBuff
, rInfo
.sStrArray
[i
]);
3173 case NF_SYMBOLTYPE_BLANK
:
3174 if (rInfo
.sStrArray
[i
].getLength() >= 2)
3175 InsertBlanks(sBuff
, sBuff
.getLength(), rInfo
.sStrArray
[i
][1] );
3177 case NF_SYMBOLTYPE_STRING
:
3178 case NF_SYMBOLTYPE_CURRENCY
:
3179 case NF_SYMBOLTYPE_DATESEP
:
3180 case NF_SYMBOLTYPE_TIMESEP
:
3181 case NF_SYMBOLTYPE_TIME100SECSEP
:
3182 sBuff
.append(rInfo
.sStrArray
[i
]);
3184 case NF_SYMBOLTYPE_DIGIT
:
3185 nLen
= ( bInputLine
&& i
> 0 &&
3186 (rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_STRING
||
3187 rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_TIME100SECSEP
) ?
3188 nCntPost
: rInfo
.sStrArray
[i
].getLength() );
3189 for (sal_Int32 j
= 0; j
< nLen
&& nSecPos
< nCntPost
&& nSecPos
< sSecStr
.getLength(); ++j
)
3191 sBuff
.append(sSecStr
[nSecPos
]);
3195 case NF_KEY_AMPM
: // AM/PM
3196 if ( !bCalendarSet
)
3198 double fDiff
= DateTime(rScan
.GetNullDate()) - GetCal().getEpochStart();
3199 fDiff
+= fNumberOrig
;
3200 GetCal().setLocalDateTime( fDiff
);
3201 bCalendarSet
= true;
3205 sBuff
.append(GetCal().getDisplayName(
3206 CalendarDisplayIndex::AM_PM
, AmPmValue::AM
, 0 ));
3210 sBuff
.append(GetCal().getDisplayName(
3211 CalendarDisplayIndex::AM_PM
, AmPmValue::PM
, 0 ));
3214 case NF_KEY_AP
: // A/P
3224 case NF_KEY_MI
: // M
3225 sBuff
.append(ImpIntToString( nIx
, nMin
));
3227 case NF_KEY_MMI
: // MM
3228 sBuff
.append(ImpIntToString( nIx
, nMin
, 2 ));
3231 sBuff
.append(ImpIntToString( nIx
, nHour
));
3233 case NF_KEY_HH
: // HH
3234 sBuff
.append(ImpIntToString( nIx
, nHour
, 2 ));
3237 sBuff
.append(ImpIntToString( nIx
, nSec
));
3239 case NF_KEY_SS
: // SS
3240 sBuff
.append(ImpIntToString( nIx
, nSec
, 2 ));
3246 if (bSign
&& rInfo
.bThousand
)
3248 sBuff
.insert(0, '-');
3254 /** If a day of month occurs within the format, the month name is in possessive
3255 genitive case if the day follows the month, and partitive case if the day
3256 precedes the month. If there is no day of month the nominative case (noun)
3257 is returned. Also if the month is immediately preceded or followed by a
3258 literal string other than space and not followed by a comma, the nominative
3259 name is used, this prevents duplicated casing for MMMM\t\a and such in
3260 documents imported from (e.g. Finnish) Excel or older LibO/OOo releases.
3263 // IDEA: instead of eCodeType pass the index to nTypeArray and restrict
3264 // inspection of month name around that one, that would enable different month
3265 // cases in one format. Though probably the most rare use case ever...
3267 sal_Int32
SvNumberformat::ImpUseMonthCase( int & io_nState
, const ImpSvNumFor
& rNumFor
, NfKeywordIndex eCodeType
)
3269 using namespace ::com::sun::star::i18n
;
3272 bool bMonthSeen
= false;
3273 bool bDaySeen
= false;
3274 const ImpSvNumberformatInfo
& rInfo
= rNumFor
.Info();
3275 const sal_uInt16 nCount
= rNumFor
.GetCount();
3276 for (sal_uInt16 i
= 0; i
< nCount
&& io_nState
== 0; ++i
)
3279 switch (rInfo
.nTypeArray
[i
])
3295 if ((i
< nCount
-1 &&
3296 rInfo
.nTypeArray
[i
+1] == NF_SYMBOLTYPE_STRING
&&
3297 // Literal following, not empty, space nor comma.
3298 !rInfo
.sStrArray
[i
+1].isEmpty() &&
3299 rInfo
.sStrArray
[i
+1][0] != ' ' && rInfo
.sStrArray
[i
+1][0] != ',') ||
3300 (i
> 0 && rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_STRING
&&
3301 ((nLen
= rInfo
.sStrArray
[i
-1].getLength()) > 0) &&
3302 // Literal preceding, not space.
3303 rInfo
.sStrArray
[i
-1][nLen
-1] != ' '))
3320 io_nState
= 1; // No day of month
3326 // No day of month or forced nominative
3330 return CalendarDisplayCode::SHORT_MONTH_NAME
;
3332 return CalendarDisplayCode::LONG_MONTH_NAME
;
3334 return CalendarDisplayCode::NARROW_MONTH_NAME
;
3340 // Day of month follows month (the month's 17th)
3344 return CalendarDisplayCode::SHORT_GENITIVE_MONTH_NAME
;
3346 return CalendarDisplayCode::LONG_GENITIVE_MONTH_NAME
;
3348 return CalendarDisplayCode::NARROW_GENITIVE_MONTH_NAME
;
3354 // Day of month precedes month (17 of month)
3358 return CalendarDisplayCode::SHORT_PARTITIVE_MONTH_NAME
;
3360 return CalendarDisplayCode::LONG_PARTITIVE_MONTH_NAME
;
3362 return CalendarDisplayCode::NARROW_PARTITIVE_MONTH_NAME
;
3368 SAL_WARN( "svl.numbers", "ImpUseMonthCase: unhandled keyword index eCodeType");
3369 return CalendarDisplayCode::LONG_MONTH_NAME
;
3373 bool SvNumberformat::ImpIsOtherCalendar( const ImpSvNumFor
& rNumFor
) const
3375 if ( GetCal().getUniqueID() != GREGORIAN
)
3379 const ImpSvNumberformatInfo
& rInfo
= rNumFor
.Info();
3380 const sal_uInt16 nCnt
= rNumFor
.GetCount();
3382 for ( i
= 0; i
< nCnt
; i
++ )
3384 switch ( rInfo
.nTypeArray
[i
] )
3386 case NF_SYMBOLTYPE_CALENDAR
:
3403 void SvNumberformat::SwitchToOtherCalendar( OUString
& rOrgCalendar
,
3404 double& fOrgDateTime
) const
3406 CalendarWrapper
& rCal
= GetCal();
3407 if ( rCal
.getUniqueID() != GREGORIAN
)
3410 using namespace ::com::sun::star::i18n
;
3411 const css::uno::Sequence
< OUString
> xCals
= rCal
.getAllCalendars(
3412 rLoc().getLanguageTag().getLocale() );
3413 sal_Int32 nCnt
= xCals
.getLength();
3417 auto pCal
= std::find_if(xCals
.begin(), xCals
.end(),
3418 [](const OUString
& rCalName
) { return rCalName
!= GREGORIAN
; });
3419 if (pCal
== xCals
.end())
3422 if ( !rOrgCalendar
.getLength() )
3424 rOrgCalendar
= rCal
.getUniqueID();
3425 fOrgDateTime
= rCal
.getDateTime();
3427 rCal
.loadCalendar( *pCal
, rLoc().getLanguageTag().getLocale() );
3428 rCal
.setDateTime( fOrgDateTime
);
3431 void SvNumberformat::SwitchToGregorianCalendar( const OUString
& rOrgCalendar
,
3432 double fOrgDateTime
) const
3434 CalendarWrapper
& rCal
= GetCal();
3435 if ( rOrgCalendar
.getLength() && rCal
.getUniqueID() != GREGORIAN
)
3437 rCal
.loadCalendar( GREGORIAN
, rLoc().getLanguageTag().getLocale() );
3438 rCal
.setDateTime( fOrgDateTime
);
3442 bool SvNumberformat::ImpFallBackToGregorianCalendar( OUString
& rOrgCalendar
, double& fOrgDateTime
)
3444 using namespace ::com::sun::star::i18n
;
3445 CalendarWrapper
& rCal
= GetCal();
3446 if ( rCal
.getUniqueID() != GREGORIAN
)
3448 sal_Int16 nVal
= rCal
.getValue( CalendarFieldIndex::ERA
);
3449 if ( nVal
== 0 && rCal
.getLoadedCalendar().Eras
[0].ID
== "Dummy" )
3451 if ( !rOrgCalendar
.getLength() )
3453 rOrgCalendar
= rCal
.getUniqueID();
3454 fOrgDateTime
= rCal
.getDateTime();
3456 else if ( rOrgCalendar
== GREGORIAN
)
3458 rOrgCalendar
.clear();
3460 rCal
.loadCalendar( GREGORIAN
, rLoc().getLanguageTag().getLocale() );
3461 rCal
.setDateTime( fOrgDateTime
);
3470 /* XXX NOTE: even if the ImpSwitchToSpecifiedCalendar method is currently
3471 * unused please don't remove it, it would be needed by
3472 * SwitchToSpecifiedCalendar(), see comment in
3473 * ImpSvNumberInputScan::GetDateRef() */
3475 bool SvNumberformat::ImpSwitchToSpecifiedCalendar( OUString
& rOrgCalendar
,
3476 double& fOrgDateTime
,
3477 const ImpSvNumFor
& rNumFor
) const
3479 const ImpSvNumberformatInfo
& rInfo
= rNumFor
.Info();
3480 const sal_uInt16 nCnt
= rNumFor
.GetCount();
3481 for ( sal_uInt16 i
= 0; i
< nCnt
; i
++ )
3483 if ( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_CALENDAR
)
3485 CalendarWrapper
& rCal
= GetCal();
3486 if ( !rOrgCalendar
.getLength() )
3488 rOrgCalendar
= rCal
.getUniqueID();
3489 fOrgDateTime
= rCal
.getDateTime();
3491 rCal
.loadCalendar( rInfo
.sStrArray
[i
], rLoc().getLocale() );
3492 rCal
.setDateTime( fOrgDateTime
);
3501 void SvNumberformat::ImpAppendEraG( OUStringBuffer
& OutString
,
3502 const CalendarWrapper
& rCal
,
3505 using namespace ::com::sun::star::i18n
;
3506 if ( rCal
.getUniqueID() == "gengou" )
3509 sal_Int16 nVal
= rCal
.getValue( CalendarFieldIndex::ERA
);
3531 OutString
.append(cEra
);
3535 OutString
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_ERA
, nNatNum
));
3539 bool SvNumberformat::ImpIsIso8601( const ImpSvNumFor
& rNumFor
) const
3541 bool bIsIso
= false;
3542 if (eType
& SvNumFormatType::DATE
)
3553 State eState
= eNone
;
3554 auto & rTypeArray
= rNumFor
.Info().nTypeArray
;
3555 sal_uInt16 nCnt
= rNumFor
.GetCount();
3556 for (sal_uInt16 i
=0; i
< nCnt
&& !bIsIso
&& eState
!= eNotIso
; ++i
)
3558 switch ( rTypeArray
[i
] )
3560 case NF_KEY_YY
: // two digits not strictly ISO 8601
3562 if (eState
!= eNone
)
3571 case NF_KEY_M
: // single digit not strictly ISO 8601
3573 if (eState
!= eAtSep1
)
3582 case NF_KEY_D
: // single digit not strictly ISO 8601
3584 if (eState
!= eAtSep2
)
3593 case NF_SYMBOLTYPE_STRING
:
3594 case NF_SYMBOLTYPE_DATESEP
:
3595 if (rNumFor
.Info().sStrArray
[i
] == "-")
3597 if (eState
== eAtYear
)
3601 else if (eState
== eAtMonth
)
3622 SAL_WARN( "svl.numbers", "SvNumberformat::ImpIsIso8601: no date" );
3627 static bool lcl_hasEra( const ImpSvNumFor
& rNumFor
)
3629 const ImpSvNumberformatInfo
& rInfo
= rNumFor
.Info();
3630 const sal_uInt16 nCnt
= rNumFor
.GetCount();
3631 for ( sal_uInt16 i
= 0; i
< nCnt
; i
++ )
3633 switch ( rInfo
.nTypeArray
[i
] )
3645 static bool lcl_isSignedYear( const CalendarWrapper
& rCal
, const ImpSvNumFor
& rNumFor
)
3647 return rCal
.getValue( css::i18n::CalendarFieldIndex::ERA
) == 0 &&
3648 rCal
.getUniqueID() == GREGORIAN
&& !lcl_hasEra( rNumFor
);
3651 /* XXX: if needed this could be stripped from rEpochStart and diff adding and
3652 * moved to tools' DateTime to be reused elsewhere. */
3653 static bool lcl_getValidDate( const DateTime
& rNullDate
, const DateTime
& rEpochStart
, double& fNumber
)
3655 static const DateTime
aCE( Date(1,1,1));
3656 static const DateTime
aMin( Date(1,1, SAL_MIN_INT16
));
3657 static const DateTime
aMax( Date(31,12, SAL_MAX_INT16
), tools::Time(23,59,59, tools::Time::nanoSecPerSec
- 1));
3658 static const double fMin
= aMin
- aCE
;
3659 static const double fMax
= aMax
- aCE
;
3660 // Value must be representable in our tools::Date proleptic Gregorian
3661 // calendar as well.
3662 const double fOff
= (rNullDate
- aCE
) + fNumber
;
3663 // Add diff between epochs to serial date number.
3664 const double fDiff
= rNullDate
- rEpochStart
;
3666 return fMin
<= fOff
&& fOff
<= fMax
;
3669 bool SvNumberformat::ImpGetDateOutput(double fNumber
,
3671 OUStringBuffer
& sBuff
)
3673 using namespace ::com::sun::star::i18n
;
3676 CalendarWrapper
& rCal
= GetCal();
3677 if (!lcl_getValidDate( rScan
.GetNullDate(), rCal
.getEpochStart(), fNumber
))
3679 sBuff
= ImpSvNumberformatScan::sErrStr
;
3682 rCal
.setLocalDateTime( fNumber
);
3683 int nUseMonthCase
= 0; // Not decided yet
3684 OUString aOrgCalendar
; // empty => not changed yet
3686 double fOrgDateTime(0.0);
3687 bool bOtherCalendar
= ImpIsOtherCalendar( NumFor
[nIx
] );
3688 if ( bOtherCalendar
)
3690 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
3692 if ( ImpFallBackToGregorianCalendar( aOrgCalendar
, fOrgDateTime
) )
3694 bOtherCalendar
= false;
3696 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
3697 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
3698 sal_Int16 nNatNum
= NumFor
[nIx
].GetNatNum().GetNatNum();
3701 // NatNum12: if the date format contains more than a date
3702 // field, it needs to specify in NatNum12 argument
3703 // which date element needs special formatting:
3705 // '[NatNum12 ordinal-number]D' -> "1st"
3706 // '[NatNum12 D=ordinal-number]D" of "MMMM' -> "1st of April"
3707 // '[NatNum12 D=ordinal]D" of "MMMM' -> "first of April"
3708 // '[NatNum12 YYYY=year,D=ordinal]D" of "MMMM", "YYYY' -> "first of April, nineteen ninety"
3710 // Note: set only for YYYY, MMMM, M, DDDD, D and NNN/AAAA in date formats.
3711 // Additionally for MMMMM, MMM, DDD and NN/AA to support at least
3712 // capitalize, upper, lower, title.
3713 // XXX It's possible to extend this for other keywords and date + time
3714 // combinations, as required.
3716 bool bUseSpellout
= NatNumTakesParameters(nNatNum
) &&
3717 (nCnt
== 1 || NumFor
[nIx
].GetNatNum().GetParams().indexOf('=') > -1);
3719 for (sal_uInt16 i
= 0; i
< nCnt
; i
++)
3721 switch (rInfo
.nTypeArray
[i
])
3723 case NF_SYMBOLTYPE_CALENDAR
:
3724 if ( !aOrgCalendar
.getLength() )
3726 aOrgCalendar
= rCal
.getUniqueID();
3727 fOrgDateTime
= rCal
.getDateTime();
3729 rCal
.loadCalendar( rInfo
.sStrArray
[i
], rLoc().getLanguageTag().getLocale() );
3730 rCal
.setDateTime( fOrgDateTime
);
3731 ImpFallBackToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
3733 case NF_SYMBOLTYPE_STAR
:
3736 bRes
= lcl_appendStarFillChar( sBuff
, rInfo
.sStrArray
[i
]);
3739 case NF_SYMBOLTYPE_BLANK
:
3740 if (rInfo
.sStrArray
[i
].getLength() >= 2)
3741 InsertBlanks( sBuff
, sBuff
.getLength(), rInfo
.sStrArray
[i
][1] );
3743 case NF_SYMBOLTYPE_STRING
:
3744 case NF_SYMBOLTYPE_CURRENCY
:
3745 case NF_SYMBOLTYPE_DATESEP
:
3746 case NF_SYMBOLTYPE_TIMESEP
:
3747 case NF_SYMBOLTYPE_TIME100SECSEP
:
3748 sBuff
.append(rInfo
.sStrArray
[i
]);
3751 aStr
= rCal
.getDisplayString( CalendarDisplayCode::SHORT_MONTH
, nNatNum
);
3752 // NatNum12: support variants of preposition, suffixation or article
3753 // for example, Catalan "de març", but "d'abril" etc.
3756 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
]);
3760 case NF_KEY_MM
: // MM
3761 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_MONTH
, nNatNum
));
3763 case NF_KEY_MMM
: // MMM
3764 case NF_KEY_MMMM
: // MMMM
3765 case NF_KEY_MMMMM
: // MMMMM
3766 // NatNum12: support variants of preposition, suffixation or
3767 // article, or capitalize, upper, lower, title.
3768 // Note: result of the "spell out" conversion can depend from the optional
3769 // PartitiveMonths or GenitiveMonths defined in the locale data,
3770 // see description of ImpUseMonthCase(), and locale data in
3771 // i18npool/source/localedata/data/ and libnumbertext
3772 aStr
= rCal
.getDisplayString( ImpUseMonthCase( nUseMonthCase
, NumFor
[nIx
],
3773 static_cast<NfKeywordIndex
>(rInfo
.nTypeArray
[i
])),
3777 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
]);
3782 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_QUARTER
, nNatNum
));
3784 case NF_KEY_QQ
: // QQ
3785 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_QUARTER
, nNatNum
));
3788 aStr
= rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY
, nNatNum
);
3789 // NatNum12: support variants of preposition, suffixation or article
3792 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
]);
3796 case NF_KEY_DD
: // DD
3797 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY
, nNatNum
));
3799 case NF_KEY_DDD
: // DDD
3800 if ( bOtherCalendar
)
3802 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
3804 aStr
= rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME
, nNatNum
);
3805 // NatNum12: support at least capitalize, upper, lower, title
3808 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
]);
3811 if ( bOtherCalendar
)
3813 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
3816 case NF_KEY_DDDD
: // DDDD
3817 if ( bOtherCalendar
)
3819 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
3821 aStr
= rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
);
3822 // NatNum12: support variants of preposition, suffixation or article
3825 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
]);
3828 if ( bOtherCalendar
)
3830 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
3833 case NF_KEY_YY
: // YY
3834 if ( bOtherCalendar
)
3836 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
3838 // Prepend a minus sign if Gregorian BCE and era is not displayed.
3839 if (lcl_isSignedYear( rCal
, NumFor
[nIx
] ))
3843 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_YEAR
, nNatNum
));
3844 if ( bOtherCalendar
)
3846 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
3849 case NF_KEY_YYYY
: // YYYY
3850 if ( bOtherCalendar
)
3852 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
3854 // Prepend a minus sign if Gregorian BCE and era is not displayed.
3855 if (lcl_isSignedYear( rCal
, NumFor
[nIx
] ))
3859 aStr
= rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR
, nNatNum
);
3860 if (aStr
.getLength() < 4 && !lcl_hasEra(NumFor
[nIx
]))
3862 using namespace comphelper::string
;
3863 // Ensure that year consists of at least 4 digits, so it
3864 // can be distinguished from 2 digits display and edited
3865 // without suddenly being hit by the 2-digit year magic.
3866 OUStringBuffer aBuf
;
3867 padToLength(aBuf
, 4 - aStr
.getLength(), '0');
3868 impTransliterate(aBuf
, NumFor
[nIx
].GetNatNum());
3870 aStr
= aBuf
.makeStringAndClear();
3872 // NatNum12: support variants of preposition, suffixation or article
3875 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
]);
3878 if ( bOtherCalendar
)
3880 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
3883 case NF_KEY_EC
: // E
3884 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_YEAR
, nNatNum
));
3886 case NF_KEY_EEC
: // EE
3888 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR
, nNatNum
));
3890 case NF_KEY_NN
: // NN
3891 case NF_KEY_AAA
: // AAA
3892 aStr
= rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME
, nNatNum
);
3893 // NatNum12: support at least capitalize, upper, lower, title
3896 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
]);
3900 case NF_KEY_NNN
: // NNN
3901 case NF_KEY_AAAA
: // AAAA
3902 aStr
= rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
);
3903 // NatNum12: support variants of preposition, suffixation or article
3906 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
]);
3910 case NF_KEY_NNNN
: // NNNN
3911 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
));
3912 sBuff
.append(rLoc().getLongDateDayOfWeekSep());
3914 case NF_KEY_WW
: // WW
3915 sBuff
.append(ImpIntToString( nIx
,
3916 rCal
.getValue( CalendarFieldIndex::WEEK_OF_YEAR
)));
3919 ImpAppendEraG(sBuff
, rCal
, nNatNum
);
3921 case NF_KEY_GG
: // GG
3922 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_ERA
, nNatNum
));
3924 case NF_KEY_GGG
: // GGG
3925 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_ERA
, nNatNum
));
3927 case NF_KEY_RR
: // RR => GGGEE
3928 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA
, nNatNum
));
3932 if ( aOrgCalendar
.getLength() )
3934 rCal
.loadCalendar( aOrgCalendar
, rLoc().getLanguageTag().getLocale() ); // restore calendar
3939 bool SvNumberformat::ImpGetDateTimeOutput(double fNumber
,
3941 OUStringBuffer
& sBuff
)
3943 using namespace ::com::sun::star::i18n
;
3946 CalendarWrapper
& rCal
= GetCal();
3947 if (!lcl_getValidDate( rScan
.GetNullDate(), rCal
.getEpochStart(), fNumber
))
3949 sBuff
= ImpSvNumberformatScan::sErrStr
;
3953 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
3956 if ( rScan
.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION
&&
3957 0 < rInfo
.nCntPost
&& rInfo
.nCntPost
< 7 )
3959 // round at 7 decimals (+5 of 86400 == 12 significant digits)
3966 nCntPost
= rInfo
.nCntPost
;
3968 double fTime
= (fNumber
- floor( fNumber
)) * 86400.0;
3969 fTime
= ::rtl::math::round( fTime
, int(nCntPost
) );
3970 if (fTime
>= 86400.0)
3972 // result of fNumber==x.999999999... rounded up, use correct date/time
3974 fNumber
= floor( fNumber
+ 0.5) + fTime
;
3976 rCal
.setLocalDateTime( fNumber
);
3978 int nUseMonthCase
= 0; // Not decided yet
3979 OUString aOrgCalendar
; // empty => not changed yet
3980 double fOrgDateTime(0.0);
3981 bool bOtherCalendar
= ImpIsOtherCalendar( NumFor
[nIx
] );
3982 if ( bOtherCalendar
)
3984 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
3986 if ( ImpFallBackToGregorianCalendar( aOrgCalendar
, fOrgDateTime
) )
3988 bOtherCalendar
= false;
3990 sal_Int16 nNatNum
= NumFor
[nIx
].GetNatNum().GetNatNum();
3992 OUStringBuffer sSecStr
;
3993 sal_Int32 nSecPos
= 0; // For figure by figure processing
3994 sal_uInt32 nHour
, nMin
, nSec
;
3995 if (!rInfo
.bThousand
) // No [] format
3997 sal_uInt16 nCHour
, nCMinute
, nCSecond
;
3998 double fFractionOfSecond
;
3999 tools::Time::GetClock( fNumber
, nCHour
, nCMinute
, nCSecond
, fFractionOfSecond
, nCntPost
);
4003 nCntPost
= ImpGetFractionOfSecondString( sSecStr
, fFractionOfSecond
, nCntPost
, true, nIx
,
4004 (bInputLine
? rInfo
.nCntPost
: 0));
4008 sal_uInt32 nSeconds
= static_cast<sal_uInt32
>(floor( fTime
));
4010 nCntPost
= ImpGetFractionOfSecondString( sSecStr
, fTime
- nSeconds
, nCntPost
, false, nIx
,
4011 (bInputLine
? rInfo
.nCntPost
: 0));
4013 if (rInfo
.nThousand
== 3) // [ss]
4019 else if (rInfo
.nThousand
== 2) // [mm]:ss
4022 nMin
= nSeconds
/ 60;
4023 nSec
= nSeconds
% 60;
4025 else if (rInfo
.nThousand
== 1) // [hh]:mm:ss
4027 nHour
= nSeconds
/ 3600;
4028 nMin
= (nSeconds
%3600) / 60;
4033 nHour
= 0; // TODO What should these values be?
4038 sal_Unicode cAmPm
= ' '; // a or p
4039 if (rInfo
.nCntExp
) // AM/PM
4046 else if (nHour
< 12)
4059 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
4062 for (sal_uInt16 i
= 0; i
< nCnt
; i
++)
4064 switch (rInfo
.nTypeArray
[i
])
4066 case NF_SYMBOLTYPE_CALENDAR
:
4067 if ( !aOrgCalendar
.getLength() )
4069 aOrgCalendar
= rCal
.getUniqueID();
4070 fOrgDateTime
= rCal
.getDateTime();
4072 rCal
.loadCalendar( rInfo
.sStrArray
[i
], rLoc().getLanguageTag().getLocale() );
4073 rCal
.setDateTime( fOrgDateTime
);
4074 ImpFallBackToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
4076 case NF_SYMBOLTYPE_STAR
:
4079 bRes
= lcl_appendStarFillChar( sBuff
, rInfo
.sStrArray
[i
]);
4082 case NF_SYMBOLTYPE_BLANK
:
4083 if (rInfo
.sStrArray
[i
].getLength() >= 2)
4084 InsertBlanks( sBuff
, sBuff
.getLength(), rInfo
.sStrArray
[i
][1] );
4086 case NF_SYMBOLTYPE_STRING
:
4087 case NF_SYMBOLTYPE_CURRENCY
:
4088 case NF_SYMBOLTYPE_DATESEP
:
4089 case NF_SYMBOLTYPE_TIMESEP
:
4090 case NF_SYMBOLTYPE_TIME100SECSEP
:
4091 sBuff
.append(rInfo
.sStrArray
[i
]);
4093 case NF_SYMBOLTYPE_DIGIT
:
4094 nLen
= ( bInputLine
&& i
> 0 &&
4095 (rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_STRING
||
4096 rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_TIME100SECSEP
) ?
4097 nCntPost
: rInfo
.sStrArray
[i
].getLength() );
4098 for (sal_Int32 j
= 0; j
< nLen
&& nSecPos
< nCntPost
&& nSecPos
< sSecStr
.getLength(); ++j
)
4100 sBuff
.append(sSecStr
[ nSecPos
]);
4104 case NF_KEY_AMPM
: // AM/PM
4107 sBuff
.append(rCal
.getDisplayName( CalendarDisplayIndex::AM_PM
,
4108 AmPmValue::AM
, 0 ));
4112 sBuff
.append(rCal
.getDisplayName( CalendarDisplayIndex::AM_PM
,
4113 AmPmValue::PM
, 0 ));
4116 case NF_KEY_AP
: // A/P
4126 case NF_KEY_MI
: // M
4127 sBuff
.append(ImpIntToString( nIx
, nMin
));
4129 case NF_KEY_MMI
: // MM
4130 sBuff
.append(ImpIntToString( nIx
, nMin
, 2 ));
4133 sBuff
.append(ImpIntToString( nIx
, nHour
));
4135 case NF_KEY_HH
: // HH
4136 sBuff
.append(ImpIntToString( nIx
, nHour
, 2 ));
4139 sBuff
.append(ImpIntToString( nIx
, nSec
));
4141 case NF_KEY_SS
: // SS
4142 sBuff
.append(ImpIntToString( nIx
, nSec
, 2 ));
4145 sBuff
.append(rCal
.getDisplayString(
4146 CalendarDisplayCode::SHORT_MONTH
, nNatNum
));
4148 case NF_KEY_MM
: // MM
4149 sBuff
.append(rCal
.getDisplayString(
4150 CalendarDisplayCode::LONG_MONTH
, nNatNum
));
4152 case NF_KEY_MMM
: // MMM
4153 sBuff
.append(rCal
.getDisplayString( ImpUseMonthCase( nUseMonthCase
, NumFor
[nIx
],
4154 static_cast<NfKeywordIndex
>(rInfo
.nTypeArray
[i
])),
4157 case NF_KEY_MMMM
: // MMMM
4158 sBuff
.append(rCal
.getDisplayString( ImpUseMonthCase( nUseMonthCase
, NumFor
[nIx
],
4159 static_cast<NfKeywordIndex
>(rInfo
.nTypeArray
[i
])),
4162 case NF_KEY_MMMMM
: // MMMMM
4163 sBuff
.append(rCal
.getDisplayString( ImpUseMonthCase( nUseMonthCase
, NumFor
[nIx
],
4164 static_cast<NfKeywordIndex
>(rInfo
.nTypeArray
[i
])),
4168 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_QUARTER
, nNatNum
));
4170 case NF_KEY_QQ
: // QQ
4171 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_QUARTER
, nNatNum
));
4174 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY
, nNatNum
));
4176 case NF_KEY_DD
: // DD
4177 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY
, nNatNum
));
4179 case NF_KEY_DDD
: // DDD
4180 if ( bOtherCalendar
)
4182 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
4184 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME
, nNatNum
));
4185 if ( bOtherCalendar
)
4187 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
4190 case NF_KEY_DDDD
: // DDDD
4191 if ( bOtherCalendar
)
4193 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
4195 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
));
4196 if ( bOtherCalendar
)
4198 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
4201 case NF_KEY_YY
: // YY
4202 if ( bOtherCalendar
)
4204 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
4206 // Prepend a minus sign if Gregorian BCE and era is not displayed.
4207 if (lcl_isSignedYear( rCal
, NumFor
[nIx
] ))
4211 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_YEAR
, nNatNum
));
4212 if ( bOtherCalendar
)
4214 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
4217 case NF_KEY_YYYY
: // YYYY
4218 if ( bOtherCalendar
)
4220 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
);
4222 // Prepend a minus sign if Gregorian BCE and era is not displayed.
4223 if (lcl_isSignedYear( rCal
, NumFor
[nIx
] ))
4227 aYear
= rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR
, nNatNum
);
4228 if (aYear
.getLength() < 4 && !lcl_hasEra(NumFor
[nIx
]))
4230 using namespace comphelper::string
;
4231 // Ensure that year consists of at least 4 digits, so it
4232 // can be distinguished from 2 digits display and edited
4233 // without suddenly being hit by the 2-digit year magic.
4234 OUStringBuffer aBuf
;
4235 padToLength(aBuf
, 4 - aYear
.getLength(), '0');
4236 impTransliterate(aBuf
, NumFor
[nIx
].GetNatNum());
4242 sBuff
.append(aYear
);
4244 if ( bOtherCalendar
)
4246 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
4249 case NF_KEY_EC
: // E
4250 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_YEAR
, nNatNum
));
4252 case NF_KEY_EEC
: // EE
4254 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR
, nNatNum
));
4256 case NF_KEY_NN
: // NN
4257 case NF_KEY_AAA
: // AAA
4258 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME
, nNatNum
));
4260 case NF_KEY_NNN
: // NNN
4261 case NF_KEY_AAAA
: // AAAA
4262 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
));
4264 case NF_KEY_NNNN
: // NNNN
4265 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
));
4266 sBuff
.append(rLoc().getLongDateDayOfWeekSep());
4268 case NF_KEY_WW
: // WW
4269 sBuff
.append(ImpIntToString( nIx
, rCal
.getValue( CalendarFieldIndex::WEEK_OF_YEAR
)));
4272 ImpAppendEraG( sBuff
, rCal
, nNatNum
);
4274 case NF_KEY_GG
: // GG
4275 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_ERA
, nNatNum
));
4277 case NF_KEY_GGG
: // GGG
4278 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_ERA
, nNatNum
));
4280 case NF_KEY_RR
: // RR => GGGEE
4281 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA
, nNatNum
));
4285 if ( aOrgCalendar
.getLength() )
4287 rCal
.loadCalendar( aOrgCalendar
, rLoc().getLanguageTag().getLocale() ); // restore calendar
4292 bool SvNumberformat::ImpGetLogicalOutput(double fNumber
,
4294 OUStringBuffer
& sStr
)
4297 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
4298 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
4299 for (sal_uInt16 j
= 0; j
< nCnt
; ++j
)
4301 switch (rInfo
.nTypeArray
[j
])
4303 case NF_KEY_BOOLEAN
:
4304 sStr
.append( fNumber
? rScan
.GetTrueString() : rScan
.GetFalseString());
4306 case NF_SYMBOLTYPE_STRING
:
4307 sStr
.append( rInfo
.sStrArray
[j
]);
4311 impTransliterate(sStr
, NumFor
[nIx
].GetNatNum());
4315 bool SvNumberformat::ImpGetNumberOutput(double fNumber
,
4317 OUStringBuffer
& sStr
)
4323 bSign
= (nIx
== 0); // Not in the ones at the back;
4329 if ( std::signbit( fNumber
) )
4331 fNumber
= -fNumber
; // yes, -0.0 is possible, eliminate '-'
4334 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
4335 if (rInfo
.eScannedType
== SvNumFormatType::PERCENT
)
4337 if (fNumber
< D_MAX_D_BY_100
)
4343 sStr
= ImpSvNumberformatScan::sErrStr
;
4348 sal_Int32 nDecPos
= -1;
4349 bool bInteger
= false;
4350 if ( rInfo
.nThousand
!= FLAG_STANDARD_IN_FORMAT
)
4352 // Special formatting only if no GENERAL keyword in format code
4353 const sal_uInt16 nThousand
= rInfo
.nThousand
;
4354 tools::Long nPrecExp
;
4355 for (i
= 0; i
< nThousand
; i
++)
4357 if (fNumber
> D_MIN_M_BY_1000
)
4368 nPrecExp
= GetPrecExp( fNumber
);
4374 if (rInfo
.nCntPost
) // Decimal places
4376 if ((rInfo
.nCntPost
+ nPrecExp
) > 15 && nPrecExp
< 15)
4378 sStr
= ::rtl::math::doubleToUString( fNumber
, rtl_math_StringFormat_F
, 15-nPrecExp
, '.');
4379 for (tools::Long l
= 15-nPrecExp
; l
< static_cast<tools::Long
>(rInfo
.nCntPost
); l
++)
4386 sStr
= ::rtl::math::doubleToUString( fNumber
, rtl_math_StringFormat_F
, rInfo
.nCntPost
, '.' );
4388 sStr
.stripStart('0'); // Strip leading zeros
4390 else if (fNumber
== 0.0) // Null
4392 // Nothing to be done here, keep empty string sStr,
4393 // ImpNumberFillWithThousands does the rest
4397 sStr
= ::rtl::math::doubleToUString( fNumber
, rtl_math_StringFormat_F
, 0, '.');
4398 sStr
.stripStart('0'); // Strip leading zeros
4400 nDecPos
= sStr
.indexOf('.' );
4403 const sal_Unicode
* p
= sStr
.getStr() + nDecPos
;
4404 while ( *++p
== '0' )
4410 sStr
.remove( nDecPos
, 1 ); // Remove .
4412 if (bSign
&& (sStr
.isEmpty() || checkForAll0s(sStr
))) // Only 00000
4414 bSign
= false; // Not -0.00
4416 } // End of != FLAG_STANDARD_IN_FORMAT
4419 j
= NumFor
[nIx
].GetCount()-1; // Last symbol
4421 bRes
|= ImpDecimalFill( sStr
, fNumber
, nDecPos
, j
, nIx
, bInteger
);
4424 sStr
.insert(0, '-');
4426 impTransliterate(sStr
, NumFor
[nIx
].GetNatNum());
4430 bool SvNumberformat::ImpDecimalFill( OUStringBuffer
& sStr
, // number string
4431 double& rNumber
, // number
4432 sal_Int32 nDecPos
, // decimals start
4433 sal_uInt16 j
, // symbol index within format code
4434 sal_uInt16 nIx
, // subformat index
4435 bool bInteger
) // is integer
4438 bool bFilled
= false; // Was filled?
4439 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
4440 sal_Int32 k
= sStr
.getLength(); // After last figure
4442 if (rInfo
.nCntPost
> 0)
4444 bool bTrailing
= true; // Trailing zeros?
4446 while (j
> 0 && // Backwards
4447 (nType
= rInfo
.nTypeArray
[j
]) != NF_SYMBOLTYPE_DECSEP
)
4451 case NF_SYMBOLTYPE_STAR
:
4454 bRes
= lcl_insertStarFillChar( sStr
, k
, rInfo
.sStrArray
[j
]);
4457 case NF_SYMBOLTYPE_BLANK
:
4458 if (rInfo
.sStrArray
[j
].getLength() >= 2)
4459 /*k = */ InsertBlanks(sStr
, k
, rInfo
.sStrArray
[j
][1] );
4461 case NF_SYMBOLTYPE_STRING
:
4462 case NF_SYMBOLTYPE_CURRENCY
:
4463 case NF_SYMBOLTYPE_PERCENT
:
4464 sStr
.insert(k
, rInfo
.sStrArray
[j
]);
4466 case NF_SYMBOLTYPE_THSEP
:
4467 if (rInfo
.nThousand
== 0)
4469 sStr
.insert(k
, rInfo
.sStrArray
[j
]);
4472 case NF_SYMBOLTYPE_DIGIT
:
4474 const OUString
& rStr
= rInfo
.sStrArray
[j
];
4475 const sal_Unicode
* p1
= rStr
.getStr();
4476 const sal_Unicode
* p
= p1
+ rStr
.getLength();
4477 // In case the number of decimals passed are less than the
4478 // "digits" given, append trailing '0' characters, which here
4479 // means insert them because literal strings may have been
4480 // appended already. If they weren't to be '0' characters
4481 // they'll be changed below, as if decimals with trailing zeros
4483 if (nDecPos
>= 0 && nDecPos
<= k
)
4485 sal_Int32 nAppend
= rStr
.getLength() - (k
- nDecPos
);
4486 while (nAppend
-- > 0)
4488 sStr
.insert( k
++, '0');
4491 while (k
&& p1
< p
--)
4493 const sal_Unicode c
= *p
;
4495 if ( sStr
[k
] != '0' )
4506 else if ( c
== '-' )
4514 else if ( c
== '?' )
4519 else if ( !bFilled
) // #
4527 case NF_KEY_CCC
: // CCC currency
4528 sStr
.insert(k
, rScan
.GetCurAbbrev());
4530 case NF_KEY_GENERAL
: // Standard in the String
4532 OUStringBuffer sNum
;
4533 ImpGetOutputStandard(rNumber
, sNum
);
4534 sNum
.stripStart('-');
4535 sStr
.insert(k
, sNum
.makeStringAndClear());
4543 } // of decimal places
4545 bRes
|= ImpNumberFillWithThousands(sStr
, rNumber
, k
, j
, nIx
, // Fill with . if needed
4546 rInfo
.nCntPre
, bFilled
);
4551 bool SvNumberformat::ImpNumberFillWithThousands( OUStringBuffer
& sBuff
, // number string
4552 double& rNumber
, // number
4553 sal_Int32 k
, // position within string
4554 sal_uInt16 j
, // symbol index within format code
4555 sal_uInt16 nIx
, // subformat index
4556 sal_Int32 nDigCnt
, // count of integer digits in format
4557 bool bAddDecSep
) // add decimal separator if necessary
4560 sal_Int32 nLeadingStringChars
= 0; // inserted StringChars before number
4561 sal_Int32 nDigitCount
= 0; // count of integer digits from the right
4563 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
4564 // no normal thousands separators if number divided by thousands
4565 bool bDoThousands
= (rInfo
.nThousand
== 0);
4566 utl::DigitGroupingIterator
aGrouping( GetFormatter().GetLocaleData()->getDigitGrouping());
4568 while (!bStop
) // backwards
4574 switch (rInfo
.nTypeArray
[j
])
4576 case NF_SYMBOLTYPE_DECSEP
:
4579 case NF_SYMBOLTYPE_STRING
:
4580 case NF_SYMBOLTYPE_CURRENCY
:
4581 case NF_SYMBOLTYPE_PERCENT
:
4582 if ( rInfo
.nTypeArray
[j
] != NF_SYMBOLTYPE_DECSEP
|| bAddDecSep
)
4583 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4586 nLeadingStringChars
= nLeadingStringChars
+ rInfo
.sStrArray
[j
].getLength();
4589 case NF_SYMBOLTYPE_STAR
:
4592 bRes
= lcl_insertStarFillChar( sBuff
, k
, rInfo
.sStrArray
[j
]);
4595 case NF_SYMBOLTYPE_BLANK
:
4596 if (rInfo
.sStrArray
[j
].getLength() >= 2)
4597 /*k = */ InsertBlanks(sBuff
, k
, rInfo
.sStrArray
[j
][1] );
4599 case NF_SYMBOLTYPE_THSEP
:
4600 // #i7284# #102685# Insert separator also if number is divided
4601 // by thousands and the separator is specified somewhere in
4602 // between and not only at the end.
4603 // #i12596# But do not insert if it's a parenthesized negative
4605 // In fact, do not insert if divided and regex [0#,],[^0#] and
4606 // no other digit symbol follows (which was already detected
4607 // during scan of format code, otherwise there would be no
4608 // division), else do insert. Same in ImpNumberFill() below.
4609 if ( !bDoThousands
&& j
< NumFor
[nIx
].GetCount()-1 )
4611 bDoThousands
= ((j
== 0) ||
4612 (rInfo
.nTypeArray
[j
-1] != NF_SYMBOLTYPE_DIGIT
&&
4613 rInfo
.nTypeArray
[j
-1] != NF_SYMBOLTYPE_THSEP
) ||
4614 (rInfo
.nTypeArray
[j
+1] == NF_SYMBOLTYPE_DIGIT
));
4620 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4622 else if (nDigitCount
< nDigCnt
)
4624 // Leading '#' displays nothing (e.g. no leading
4625 // separator for numbers <1000 with #,##0 format).
4626 // Leading '?' displays blank.
4627 // Everything else, including nothing, displays the
4629 sal_Unicode cLeader
= 0;
4630 if (j
> 0 && rInfo
.nTypeArray
[j
-1] == NF_SYMBOLTYPE_DIGIT
)
4632 const OUString
& rStr
= rInfo
.sStrArray
[j
-1];
4633 sal_Int32 nLen
= rStr
.getLength();
4636 cLeader
= rStr
[ nLen
- 1 ];
4645 // replace thousand separator with blank
4646 sBuff
.insert(k
, ' ');
4649 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4652 aGrouping
.advance();
4655 case NF_SYMBOLTYPE_DIGIT
:
4657 const OUString
& rStr
= rInfo
.sStrArray
[j
];
4658 const sal_Unicode
* p1
= rStr
.getStr();
4659 const sal_Unicode
* p
= p1
+ rStr
.getLength();
4672 sBuff
.insert(0, '0');
4675 sBuff
.insert(0, ' ');
4679 if (nDigitCount
== nDigCnt
&& k
> 0)
4681 // more digits than specified
4682 ImpDigitFill(sBuff
, 0, k
, nIx
, nDigitCount
, aGrouping
);
4687 case NF_KEY_CCC
: // CCC currency
4688 sBuff
.insert(k
, rScan
.GetCurAbbrev());
4690 case NF_KEY_GENERAL
: // "General" in string
4692 OUStringBuffer sNum
;
4693 ImpGetOutputStandard(rNumber
, sNum
);
4694 sNum
.stripStart('-');
4695 sBuff
.insert(k
, sNum
.makeStringAndClear());
4701 j
--; // next format code string
4704 k
= k
+ nLeadingStringChars
; // MSC converts += to int and then warns, so ...
4705 if (k
> nLeadingStringChars
)
4707 ImpDigitFill(sBuff
, nLeadingStringChars
, k
, nIx
, nDigitCount
, aGrouping
);
4712 void SvNumberformat::ImpDigitFill(OUStringBuffer
& sStr
, // number string
4713 sal_Int32 nStart
, // start of digits
4714 sal_Int32
& k
, // position within string
4715 sal_uInt16 nIx
, // subformat index
4716 sal_Int32
& nDigitCount
, // count of integer digits from the right so far
4717 utl::DigitGroupingIterator
& rGrouping
) // current grouping
4719 if (NumFor
[nIx
].Info().bThousand
) // Only if grouping fill in separators
4721 const OUString
& rThousandSep
= GetFormatter().GetNumThousandSep();
4724 if (nDigitCount
== rGrouping
.getPos())
4726 sStr
.insert( k
, rThousandSep
);
4727 rGrouping
.advance();
4739 bool SvNumberformat::ImpNumberFill( OUStringBuffer
& sBuff
, // number string
4740 double& rNumber
, // number for "General" format
4741 sal_Int32
& k
, // position within string
4742 sal_uInt16
& j
, // symbol index within format code
4743 sal_uInt16 nIx
, // subformat index
4744 short eSymbolType
, // type of stop condition
4745 bool bInsertRightBlank
)// insert blank on right for denominator (default = false)
4749 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
4750 // no normal thousands separators if number divided by thousands
4751 bool bDoThousands
= (rInfo
.nThousand
== 0);
4752 bool bFoundNumber
= false;
4755 k
= sBuff
.getLength(); // behind last digit
4757 while (!bStop
&& (nType
= rInfo
.nTypeArray
[j
]) != eSymbolType
) // Backwards
4761 case NF_SYMBOLTYPE_STAR
:
4764 if ( bFoundNumber
&& eSymbolType
!= NF_SYMBOLTYPE_EXP
)
4765 k
= 0; // tdf#100842 jump to beginning of number before inserting something else
4766 bRes
= lcl_insertStarFillChar( sBuff
, k
, rInfo
.sStrArray
[j
]);
4769 case NF_SYMBOLTYPE_BLANK
:
4770 if (rInfo
.sStrArray
[j
].getLength() >= 2)
4772 if ( bFoundNumber
&& eSymbolType
!= NF_SYMBOLTYPE_EXP
)
4773 k
= 0; // tdf#100842 jump to beginning of number before inserting something else
4774 k
= InsertBlanks(sBuff
, k
, rInfo
.sStrArray
[j
][1] );
4777 case NF_SYMBOLTYPE_THSEP
:
4778 // Same as in ImpNumberFillWithThousands() above, do not insert
4779 // if divided and regex [0#,],[^0#] and no other digit symbol
4780 // follows (which was already detected during scan of format
4781 // code, otherwise there would be no division), else do insert.
4782 if ( !bDoThousands
&& j
< NumFor
[nIx
].GetCount()-1 )
4784 bDoThousands
= ((j
== 0) ||
4785 (rInfo
.nTypeArray
[j
-1] != NF_SYMBOLTYPE_DIGIT
&&
4786 rInfo
.nTypeArray
[j
-1] != NF_SYMBOLTYPE_THSEP
) ||
4787 (rInfo
.nTypeArray
[j
+1] == NF_SYMBOLTYPE_DIGIT
));
4789 if ( bDoThousands
&& k
> 0 )
4791 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4794 case NF_SYMBOLTYPE_DIGIT
:
4796 bFoundNumber
= true;
4797 sal_uInt16 nPosInsertBlank
= bInsertRightBlank
? k
: 0; // left alignment of denominator
4798 const OUString
& rStr
= rInfo
.sStrArray
[j
];
4799 const sal_Unicode
* p1
= rStr
.getStr();
4800 const sal_Unicode
* p
= p1
+ rStr
.getLength();
4812 sBuff
.insert(0, '0');
4815 sBuff
.insert(nPosInsertBlank
, ' ');
4822 case NF_KEY_CCC
: // CCC currency
4823 sBuff
.insert(k
, rScan
.GetCurAbbrev());
4825 case NF_KEY_GENERAL
: // Standard in the String
4827 OUStringBuffer sNum
;
4828 bFoundNumber
= true;
4829 ImpGetOutputStandard(rNumber
, sNum
);
4830 sNum
.stripStart('-');
4831 sBuff
.insert(k
, sNum
.makeStringAndClear());
4834 case NF_SYMBOLTYPE_FRAC_FDIV
: // Do Nothing
4842 if ( bFoundNumber
&& eSymbolType
!= NF_SYMBOLTYPE_EXP
)
4843 k
= 0; // tdf#100842 jump to beginning of number before inserting something else
4844 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4855 void SvNumberformat::GetFormatSpecialInfo(bool& bThousand
,
4857 sal_uInt16
& nPrecision
,
4858 sal_uInt16
& nLeadingCnt
) const
4860 // as before: take info from nNumFor=0 for whole format (for dialog etc.)
4862 SvNumFormatType nDummyType
;
4863 GetNumForInfo( 0, nDummyType
, bThousand
, nPrecision
, nLeadingCnt
);
4865 // "negative in red" is only useful for the whole format
4867 const Color
* pColor
= NumFor
[1].GetColor();
4868 IsRed
= fLimit1
== 0.0 && fLimit2
== 0.0 && pColor
4869 && (*pColor
== ImpSvNumberformatScan::GetRedColor());
4872 void SvNumberformat::GetNumForInfo( sal_uInt16 nNumFor
, SvNumFormatType
& rScannedType
,
4873 bool& bThousand
, sal_uInt16
& nPrecision
, sal_uInt16
& nLeadingCnt
) const
4875 // take info from a specified sub-format (for XML export)
4882 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nNumFor
].Info();
4883 rScannedType
= rInfo
.eScannedType
;
4884 bThousand
= rInfo
.bThousand
;
4885 nPrecision
= (rInfo
.eScannedType
== SvNumFormatType::FRACTION
)
4886 ? rInfo
.nCntExp
// number of denominator digits for fraction
4888 sal_Int32 nPosHash
= 1;
4889 if ( rInfo
.eScannedType
== SvNumFormatType::FRACTION
&&
4890 ( (nPosHash
+= GetDenominatorString(nNumFor
).indexOf('#')) > 0 ) )
4891 nPrecision
-= nPosHash
;
4892 if (bStandard
&& rInfo
.eScannedType
== SvNumFormatType::NUMBER
)
4902 const sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
4903 while (!bStop
&& i
< nCnt
)
4905 short nType
= rInfo
.nTypeArray
[i
];
4906 if ( nType
== NF_SYMBOLTYPE_DIGIT
)
4908 const sal_Unicode
* p
= rInfo
.sStrArray
[i
].getStr();
4913 while ( *p
++ == '0' )
4918 else if (nType
== NF_SYMBOLTYPE_DECSEP
4919 || nType
== NF_SYMBOLTYPE_EXP
4920 || nType
== NF_SYMBOLTYPE_FRACBLANK
) // Fraction: stop after integer part,
4921 { // do not count '0' of fraction
4929 const OUString
* SvNumberformat::GetNumForString( sal_uInt16 nNumFor
, sal_uInt16 nPos
,
4930 bool bString
/* = false */ ) const
4936 sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
4941 if ( nPos
== 0xFFFF )
4946 short const * pType
= NumFor
[nNumFor
].Info().nTypeArray
.data() + nPos
;
4947 while ( nPos
> 0 && (*pType
!= NF_SYMBOLTYPE_STRING
) &&
4948 (*pType
!= NF_SYMBOLTYPE_CURRENCY
) )
4953 if ( (*pType
!= NF_SYMBOLTYPE_STRING
) && (*pType
!= NF_SYMBOLTYPE_CURRENCY
) )
4959 else if ( nPos
> nCnt
- 1 )
4966 short const * pType
= NumFor
[nNumFor
].Info().nTypeArray
.data() + nPos
;
4967 while ( nPos
< nCnt
&& (*pType
!= NF_SYMBOLTYPE_STRING
) &&
4968 (*pType
!= NF_SYMBOLTYPE_CURRENCY
) )
4973 if ( nPos
>= nCnt
|| ((*pType
!= NF_SYMBOLTYPE_STRING
) &&
4974 (*pType
!= NF_SYMBOLTYPE_CURRENCY
)) )
4979 return &NumFor
[nNumFor
].Info().sStrArray
[nPos
];
4982 short SvNumberformat::GetNumForType( sal_uInt16 nNumFor
, sal_uInt16 nPos
) const
4988 sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
4993 if ( nPos
== 0xFFFF )
4997 else if ( nPos
> nCnt
- 1 )
5001 return NumFor
[nNumFor
].Info().nTypeArray
[nPos
];
5004 bool SvNumberformat::IsNegativeWithoutSign() const
5006 if ( IsSecondSubformatRealNegative() )
5008 const OUString
* pStr
= GetNumForString( 1, 0, true );
5011 return !HasStringNegativeSign( *pStr
);
5017 bool SvNumberformat::IsNegativeInBracket() const
5019 sal_uInt16 nCnt
= NumFor
[1].GetCount();
5024 auto& tmp
= NumFor
[1].Info().sStrArray
;
5025 return tmp
[0] == "(" && tmp
[nCnt
-1] == ")";
5028 bool SvNumberformat::HasPositiveBracketPlaceholder() const
5030 sal_uInt16 nCnt
= NumFor
[0].GetCount();
5031 return NumFor
[0].Info().sStrArray
[nCnt
-1] == "_)";
5034 DateOrder
SvNumberformat::GetDateOrder() const
5036 if ( eType
& SvNumFormatType::DATE
)
5038 auto& rTypeArray
= NumFor
[0].Info().nTypeArray
;
5039 sal_uInt16 nCnt
= NumFor
[0].GetCount();
5040 for ( sal_uInt16 j
=0; j
<nCnt
; j
++ )
5042 switch ( rTypeArray
[j
] )
5046 return DateOrder::DMY
;
5052 return DateOrder::MDY
;
5059 return DateOrder::YMD
;
5065 SAL_WARN( "svl.numbers", "SvNumberformat::GetDateOrder: no date" );
5067 return rLoc().getDateOrder();
5070 sal_uInt32
SvNumberformat::GetExactDateOrder() const
5072 sal_uInt32 nRet
= 0;
5073 if ( !(eType
& SvNumFormatType::DATE
) )
5075 SAL_WARN( "svl.numbers", "SvNumberformat::GetExactDateOrder: no date" );
5078 auto& rTypeArray
= NumFor
[0].Info().nTypeArray
;
5079 sal_uInt16 nCnt
= NumFor
[0].GetCount();
5081 for ( sal_uInt16 j
=0; j
<nCnt
&& nShift
< 3; j
++ )
5083 switch ( rTypeArray
[j
] )
5087 nRet
= (nRet
<< 8) | 'D';
5095 nRet
= (nRet
<< 8) | 'M';
5104 nRet
= (nRet
<< 8) | 'Y';
5112 void SvNumberformat::GetConditions( SvNumberformatLimitOps
& rOper1
, double& rVal1
,
5113 SvNumberformatLimitOps
& rOper2
, double& rVal2
) const
5121 const Color
* SvNumberformat::GetColor( sal_uInt16 nNumFor
) const
5127 return NumFor
[nNumFor
].GetColor();
5130 static void lcl_SvNumberformat_AddLimitStringImpl( OUString
& rStr
,
5131 SvNumberformatLimitOps eOp
,
5132 double fLimit
, std::u16string_view rDecSep
)
5134 if ( eOp
== NUMBERFORMAT_OP_NO
)
5139 case NUMBERFORMAT_OP_EQ
:
5142 case NUMBERFORMAT_OP_NE
:
5145 case NUMBERFORMAT_OP_LT
:
5148 case NUMBERFORMAT_OP_LE
:
5151 case NUMBERFORMAT_OP_GT
:
5154 case NUMBERFORMAT_OP_GE
:
5158 SAL_WARN( "svl.numbers", "unsupported number format" );
5161 rStr
+= ::rtl::math::doubleToUString( fLimit
,
5162 rtl_math_StringFormat_Automatic
, rtl_math_DecimalPlaces_Max
,
5167 static void lcl_insertLCID( OUStringBuffer
& rFormatStr
, sal_uInt32 nLCID
, sal_Int32 nPosInsertLCID
, bool bDBNumInserted
)
5171 if (nPosInsertLCID
== rFormatStr
.getLength() && !bDBNumInserted
)
5172 // No format code, no locale.
5175 OUStringBuffer aLCIDString
= OUString::number( nLCID
, 16 ).toAsciiUpperCase();
5176 // Search for only last DBNum which is the last element before insertion position
5177 if ( bDBNumInserted
&& nPosInsertLCID
>= 8
5178 && aLCIDString
.getLength() > 4
5179 && rFormatStr
.indexOf( "[DBNum", nPosInsertLCID
-8) == nPosInsertLCID
-8 )
5180 { // remove DBNumX code if long LCID
5181 nPosInsertLCID
-= 8;
5182 rFormatStr
.remove( nPosInsertLCID
, 8 );
5184 aLCIDString
.insert( 0, "[$-" );
5185 aLCIDString
.append( "]" );
5186 rFormatStr
.insert( nPosInsertLCID
, aLCIDString
.toString() );
5189 /** Increment nAlphabetID for CJK numerals
5190 * +1 for financial numerals [NatNum2]
5191 * +2 for Arabic fullwidth numerals [NatNum3]
5193 static void lcl_incrementAlphabetWithNatNum ( sal_uInt32
& nAlphabetID
, sal_uInt32 nNatNum
)
5195 if ( nNatNum
== 2) // financial
5197 else if ( nNatNum
== 3)
5199 nAlphabetID
= nAlphabetID
<< 24;
5202 OUString
SvNumberformat::GetMappedFormatstring( const NfKeywordTable
& rKeywords
,
5203 const LocaleDataWrapper
& rLocWrp
,
5204 LanguageType nOriginalLang
/* =LANGUAGE_DONTKNOW */,
5205 bool bSystemLanguage
/* =false */ ) const
5207 OUStringBuffer aStr
;
5208 if (maLocale
.meSubstitute
!= LocaleType::Substitute::NONE
)
5210 // XXX: theoretically this could clash with the first subformat's
5211 // lcl_insertLCID() below, in practice as long as it is used for system
5212 // time and date modifiers it shouldn't (i.e. there is no calendar or
5213 // numeral specified as well).
5214 aStr
.append("[$-" + maLocale
.generateCode() + "]");
5217 // 1 subformat matches all if no condition specified,
5218 bDefault
[0] = ( NumFor
[1].GetCount() == 0 && eOp1
== NUMBERFORMAT_OP_NO
);
5219 // with 2 subformats [>=0];[<0] is implied if no condition specified
5220 bDefault
[1] = ( !bDefault
[0] && NumFor
[2].GetCount() == 0 &&
5221 eOp1
== NUMBERFORMAT_OP_GE
&& fLimit1
== 0.0 &&
5222 eOp2
== NUMBERFORMAT_OP_NO
&& fLimit2
== 0.0 );
5223 // with 3 or more subformats [>0];[<0];[=0] is implied if no condition specified,
5224 // note that subformats may be empty (;;;) and NumFor[2].GetCount()>0 is not checked.
5225 bDefault
[2] = ( !bDefault
[0] && !bDefault
[1] &&
5226 eOp1
== NUMBERFORMAT_OP_GT
&& fLimit1
== 0.0 &&
5227 eOp2
== NUMBERFORMAT_OP_LT
&& fLimit2
== 0.0 );
5228 bool bDefaults
= bDefault
[0] || bDefault
[1] || bDefault
[2];
5229 // from now on bDefault[] values are used to append empty subformats at the end
5230 bDefault
[3] = false;
5233 // conditions specified
5234 if ( eOp1
!= NUMBERFORMAT_OP_NO
&& eOp2
== NUMBERFORMAT_OP_NO
)
5236 bDefault
[0] = bDefault
[1] = true; // [];x
5238 else if ( eOp1
!= NUMBERFORMAT_OP_NO
&& eOp2
!= NUMBERFORMAT_OP_NO
&&
5239 NumFor
[2].GetCount() == 0 )
5241 bDefault
[0] = bDefault
[1] = bDefault
[2] = bDefault
[3] = true; // [];[];;
5243 // nothing to do if conditions specified for every subformat
5245 else if ( bDefault
[0] )
5247 bDefault
[0] = false; // a single unconditional subformat is never delimited
5251 if ( bDefault
[2] && NumFor
[2].GetCount() == 0 && NumFor
[1].GetCount() > 0 )
5253 bDefault
[3] = true; // special cases x;x;; and ;x;;
5255 for ( int i
=0; i
<3 && !bDefault
[i
]; ++i
)
5260 int nSem
= 0; // needed ';' delimiters
5261 int nSub
= 0; // subformats delimited so far
5262 for ( int n
=0; n
<4; n
++ )
5264 if ( n
> 0 && NumFor
[n
].Info().eScannedType
!= SvNumFormatType::UNDEFINED
)
5275 lcl_SvNumberformat_AddLimitStringImpl( aPrefix
, eOp1
,
5276 fLimit1
, rLocWrp
.getNumDecimalSep() );
5279 lcl_SvNumberformat_AddLimitStringImpl( aPrefix
, eOp2
,
5280 fLimit2
, rLocWrp
.getNumDecimalSep() );
5285 const OUString
& rColorName
= NumFor
[n
].GetColorName();
5286 if ( !rColorName
.isEmpty() )
5288 const NfKeywordTable
& rKey
= rScan
.GetKeywords();
5289 for ( int j
= NF_KEY_FIRSTCOLOR
; j
<= NF_KEY_LASTCOLOR
; j
++ )
5291 if ( rKey
[j
] == rColorName
)
5293 aPrefix
+= "[" + rKeywords
[j
] + "]";
5299 SvNumberNatNum aNatNum
= NumFor
[n
].GetNatNum();
5300 bool bDBNumInserted
= false;
5301 if (aNatNum
.IsComplete() && (aNatNum
.GetDBNum() > 0 || nOriginalLang
!= LANGUAGE_DONTKNOW
))
5302 { // GetFormatStringForExcel() may have changed language to en_US
5303 if (aNatNum
.GetLang() == LANGUAGE_ENGLISH_US
&& nOriginalLang
!= LANGUAGE_DONTKNOW
)
5304 aNatNum
.SetLang( nOriginalLang
);
5305 if ( aNatNum
.GetDBNum() > 0 )
5307 aPrefix
+= "[DBNum" + OUString::number( aNatNum
.GetDBNum() ) + "]";
5308 bDBNumInserted
= true;
5312 sal_uInt16 nCnt
= NumFor
[n
].GetCount();
5313 if ( nSem
&& (nCnt
|| !aPrefix
.isEmpty()) )
5315 for ( ; nSem
; --nSem
)
5319 for ( ; nSub
<= n
; ++nSub
)
5321 bDefault
[nSub
] = false;
5325 if ( !aPrefix
.isEmpty() )
5327 aStr
.append( aPrefix
);
5329 sal_Int32 nPosHaveLCID
= -1;
5330 sal_Int32 nPosInsertLCID
= aStr
.getLength();
5331 sal_uInt32 nCalendarID
= 0x0000000; // Excel ID of calendar used in sub-format see tdf#36038
5332 constexpr sal_uInt32 kCalGengou
= 0x0030000;
5335 auto& rTypeArray
= NumFor
[n
].Info().nTypeArray
;
5336 auto& rStrArray
= NumFor
[n
].Info().sStrArray
;
5337 for ( sal_uInt16 j
=0; j
<nCnt
; j
++ )
5339 if ( 0 <= rTypeArray
[j
] && rTypeArray
[j
] < NF_KEYWORD_ENTRIES_COUNT
)
5341 aStr
.append( rKeywords
[rTypeArray
[j
]] );
5342 if( NF_KEY_NNNN
== rTypeArray
[j
] )
5344 aStr
.append( rLocWrp
.getLongDateDayOfWeekSep() );
5346 switch (rTypeArray
[j
])
5352 // Implicit secondary (non-gregorian) calendar.
5353 // Currently only for ja-JP.
5354 /* TODO: same for all locales in
5355 * LocaleDataWrapper::doesSecondaryCalendarUseEC() ?
5356 * Should split the locales off that then. */
5359 const LanguageType nLang
= MsLangId::getRealLanguage( nOriginalLang
);
5360 if (nLang
== LANGUAGE_JAPANESE
)
5361 nCalendarID
= kCalGengou
;
5370 switch ( rTypeArray
[j
] )
5372 case NF_SYMBOLTYPE_DECSEP
:
5373 aStr
.append( rLocWrp
.getNumDecimalSep() );
5375 case NF_SYMBOLTYPE_THSEP
:
5376 aStr
.append( rLocWrp
.getNumThousandSep() );
5378 case NF_SYMBOLTYPE_EXP
:
5379 aStr
.append( rKeywords
[NF_KEY_E
] );
5380 if ( rStrArray
[j
].getLength() > 1 && rStrArray
[j
][1] == '+' )
5383 // tdf#102370: Excel code for exponent without sign
5386 case NF_SYMBOLTYPE_DATESEP
:
5387 aStr
.append( rLocWrp
.getDateSep() );
5389 case NF_SYMBOLTYPE_TIMESEP
:
5390 aStr
.append( rLocWrp
.getTimeSep() );
5392 case NF_SYMBOLTYPE_TIME100SECSEP
:
5393 aStr
.append( rLocWrp
.getTime100SecSep() );
5395 case NF_SYMBOLTYPE_FRACBLANK
:
5396 case NF_SYMBOLTYPE_STRING
:
5397 if ( rStrArray
[j
].getLength() == 1 )
5399 if ( rTypeArray
[j
] == NF_SYMBOLTYPE_STRING
)
5400 aStr
.append( '\\' );
5401 aStr
.append( rStrArray
[j
] );
5406 aStr
.append( rStrArray
[j
] );
5410 case NF_SYMBOLTYPE_CALDEL
:
5413 if ( rStrArray
[j
+1] == "gengou" )
5415 nCalendarID
= kCalGengou
;
5417 else if ( rStrArray
[j
+1] == "hijri" )
5419 nCalendarID
= 0x0060000;
5421 else if ( rStrArray
[j
+1] == "buddhist" )
5423 nCalendarID
= 0x0070000;
5425 else if ( rStrArray
[j
+1] == "jewish" )
5427 nCalendarID
= 0x0080000;
5429 // Other calendars (see tdf#36038) not corresponding between LibO and XL.
5430 // However, skip any calendar modifier and don't write
5431 // as format code (if not as literal string).
5434 case NF_SYMBOLTYPE_CURREXT
:
5435 nPosHaveLCID
= aStr
.getLength();
5436 aStr
.append( rStrArray
[j
] );
5439 aStr
.append( rStrArray
[j
] );
5444 sal_uInt32 nAlphabetID
= 0x0000000; // Excel ID of alphabet used for numerals see tdf#36038
5445 LanguageType nLanguageID
= LANGUAGE_SYSTEM
;
5446 if ( aNatNum
.IsComplete() )
5448 nLanguageID
= MsLangId::getRealLanguage( aNatNum
.GetLang());
5449 if ( aNatNum
.GetNatNum() == 0 )
5451 nAlphabetID
= 0x01000000; // Arabic-european numerals
5453 else if ( nCalendarID
> 0 || aNatNum
.GetDBNum() == 0 || aNatNum
.GetDBNum() == aNatNum
.GetNatNum() )
5454 { // if no DBNum code then use long LCID
5455 // if DBNum value != NatNum value, use DBNum and not extended LCID
5456 // if calendar, then DBNum will be removed
5457 LanguageType pri
= primary(nLanguageID
);
5458 if ( pri
== LANGUAGE_ARABIC_PRIMARY_ONLY
)
5459 nAlphabetID
= 0x02000000; // Arabic-indic numerals
5460 else if ( pri
== primary(LANGUAGE_FARSI
) )
5461 nAlphabetID
= 0x03000000; // Farsi numerals
5462 else if ( pri
.anyOf(
5463 primary(LANGUAGE_HINDI
),
5464 primary(LANGUAGE_MARATHI
),
5465 primary(LANGUAGE_NEPALI
) ))
5466 nAlphabetID
= 0x04000000; // Devanagari numerals
5467 else if ( pri
== primary(LANGUAGE_BENGALI
) )
5468 nAlphabetID
= 0x05000000; // Bengali numerals
5469 else if ( pri
== primary(LANGUAGE_PUNJABI
) )
5471 if ( nLanguageID
== LANGUAGE_PUNJABI_ARABIC_LSO
)
5472 nAlphabetID
= 0x02000000; // Arabic-indic numerals
5474 nAlphabetID
= 0x06000000; // Punjabi numerals
5476 else if ( pri
== primary(LANGUAGE_GUJARATI
) )
5477 nAlphabetID
= 0x07000000; // Gujarati numerals
5478 else if ( pri
== primary(LANGUAGE_ODIA
))
5479 nAlphabetID
= 0x08000000; // Odia (Oriya) numerals
5480 else if ( pri
== primary(LANGUAGE_TAMIL
))
5481 nAlphabetID
= 0x09000000; // Tamil numerals
5482 else if ( pri
== primary(LANGUAGE_TELUGU
))
5483 nAlphabetID
= 0x0A000000; // Telugu numerals
5484 else if ( pri
== primary(LANGUAGE_KANNADA
))
5485 nAlphabetID
= 0x0B000000; // Kannada numerals
5486 else if ( pri
== primary(LANGUAGE_MALAYALAM
))
5487 nAlphabetID
= 0x0C000000; // Malayalam numerals
5488 else if ( pri
== primary(LANGUAGE_THAI
))
5490 // The Thai T NatNum modifier during Xcl export.
5491 if ( rKeywords
[NF_KEY_THAI_T
] == "T" )
5492 nAlphabetID
= 0x0D000000; // Thai numerals
5494 else if ( pri
== primary(LANGUAGE_LAO
))
5495 nAlphabetID
= 0x0E000000; // Lao numerals
5496 else if ( pri
== primary(LANGUAGE_TIBETAN
))
5497 nAlphabetID
= 0x0F000000; // Tibetan numerals
5498 else if ( pri
== primary(LANGUAGE_BURMESE
))
5499 nAlphabetID
= 0x10000000; // Burmese numerals
5500 else if ( pri
== primary(LANGUAGE_TIGRIGNA_ETHIOPIA
))
5501 nAlphabetID
= 0x11000000; // Tigrigna numerals
5502 else if ( pri
== primary(LANGUAGE_KHMER
))
5503 nAlphabetID
= 0x12000000; // Khmer numerals
5504 else if ( pri
== primary(LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
))
5506 if ( nLanguageID
!= LANGUAGE_MONGOLIAN_CYRILLIC_MONGOLIA
5507 && nLanguageID
!= LANGUAGE_MONGOLIAN_CYRILLIC_LSO
)
5508 nAlphabetID
= 0x13000000; // Mongolian numerals
5511 else if ( pri
== primary(LANGUAGE_JAPANESE
))
5514 lcl_incrementAlphabetWithNatNum ( nAlphabetID
, aNatNum
.GetNatNum() );
5516 else if ( pri
== primary(LANGUAGE_CHINESE
))
5518 if ( nLanguageID
== LANGUAGE_CHINESE_TRADITIONAL
5519 || nLanguageID
== LANGUAGE_CHINESE_HONGKONG
5520 || nLanguageID
== LANGUAGE_CHINESE_MACAU
)
5523 lcl_incrementAlphabetWithNatNum ( nAlphabetID
, aNatNum
.GetNatNum() );
5525 else // LANGUAGE_CHINESE_SIMPLIFIED
5528 lcl_incrementAlphabetWithNatNum ( nAlphabetID
, aNatNum
.GetNatNum() );
5531 else if ( pri
== primary(LANGUAGE_KOREAN
))
5533 if ( aNatNum
.GetNatNum() == 9 ) // Hangul
5535 nAlphabetID
= 0x27000000;
5540 lcl_incrementAlphabetWithNatNum ( nAlphabetID
, aNatNum
.GetNatNum() );
5544 // Add LCID to DBNum
5545 if ( aNatNum
.GetDBNum() > 0 && nLanguageID
== LANGUAGE_SYSTEM
)
5546 nLanguageID
= MsLangId::getRealLanguage( aNatNum
.GetLang());
5548 else if (nPosHaveLCID
< 0)
5550 // Do not insert a duplicated LCID that was already given with a
5551 // currency format as [$R-1C09]
5552 if (!bSystemLanguage
&& nOriginalLang
!= LANGUAGE_DONTKNOW
)
5554 // Explicit locale, write only to the first subformat.
5556 nLanguageID
= MsLangId::getRealLanguage( nOriginalLang
);
5558 else if (bSystemLanguage
&& maLocale
.meLanguageWithoutLocaleData
!= LANGUAGE_DONTKNOW
)
5560 // Explicit locale but no locale data thus assigned to system
5561 // locale, preserve for roundtrip, write only to the first
5564 nLanguageID
= maLocale
.meLanguageWithoutLocaleData
;
5567 if ( nCalendarID
> 0 )
5568 { // Add alphabet and language to calendar
5569 if ( nAlphabetID
== 0 )
5570 nAlphabetID
= 0x01000000;
5571 if ( nLanguageID
== LANGUAGE_SYSTEM
&& nOriginalLang
!= LANGUAGE_DONTKNOW
)
5572 nLanguageID
= nOriginalLang
;
5574 lcl_insertLCID( aStr
, nAlphabetID
+ nCalendarID
+ static_cast<sal_uInt16
>(nLanguageID
), nPosInsertLCID
,
5577 for ( ; nSub
<4 && bDefault
[nSub
]; ++nSub
)
5578 { // append empty subformats
5581 return aStr
.makeStringAndClear();
5584 OUString
SvNumberformat::ImpGetNatNumString( const SvNumberNatNum
& rNum
,
5585 sal_Int64 nVal
, sal_uInt16 nMinDigits
) const
5590 if ( nMinDigits
== 2 )
5592 // speed up the most common case
5593 if ( 0 <= nVal
&& nVal
< 10 )
5595 sal_Unicode aBuf
[2];
5597 aBuf
[1] = '0' + nVal
;
5598 aStr
= OUString(aBuf
, SAL_N_ELEMENTS(aBuf
));
5602 aStr
= OUString::number( nVal
);
5607 OUString
aValStr( OUString::number( nVal
) );
5608 if ( aValStr
.getLength() >= nMinDigits
)
5614 OUStringBuffer aBuf
;
5615 for(sal_Int32 index
= 0; index
< nMinDigits
- aValStr
.getLength(); ++index
)
5619 aBuf
.append(aValStr
);
5620 aStr
= aBuf
.makeStringAndClear();
5626 aStr
= OUString::number( nVal
);
5628 return impTransliterate(aStr
, rNum
);
5631 OUString
SvNumberformat::impTransliterateImpl(const OUString
& rStr
,
5632 const SvNumberNatNum
& rNum
) const
5634 css::lang::Locale
aLocale( LanguageTag( rNum
.GetLang() ).getLocale() );
5635 return GetFormatter().GetNatNum()->getNativeNumberStringParams(rStr
, aLocale
, rNum
.GetNatNum(),
5639 void SvNumberformat::impTransliterateImpl(OUStringBuffer
& rStr
,
5640 const SvNumberNatNum
& rNum
) const
5642 css::lang::Locale
aLocale( LanguageTag( rNum
.GetLang() ).getLocale() );
5644 OUString
sTemp(rStr
.toString());
5645 sTemp
= GetFormatter().GetNatNum()->getNativeNumberStringParams(
5646 sTemp
, aLocale
, rNum
.GetNatNum(), rNum
.GetParams());
5650 OUString
SvNumberformat::impTransliterateImpl(const OUString
& rStr
,
5651 const SvNumberNatNum
& rNum
,
5652 const sal_uInt16 nDateKey
) const
5654 // no KEYWORD=argument list in NatNum12
5655 if (rNum
.GetParams().indexOf('=') == -1)
5656 return impTransliterateImpl( rStr
, rNum
);
5658 const NfKeywordTable
& rKeywords
= rScan
.GetKeywords();
5660 // Format: KEYWORD=numbertext_prefix, ..., for example:
5661 // [NatNum12 YYYY=title ordinal,MMMM=article, D=ordinal-number]
5662 sal_Int32 nField
= -1;
5665 nField
= rNum
.GetParams().indexOf(OUStringConcatenation(rKeywords
[nDateKey
] + "="), ++nField
);
5667 while (nField
!= -1 && nField
!= 0 &&
5668 (rNum
.GetParams()[nField
- 1] != ',' &&
5669 rNum
.GetParams()[nField
- 1] != ' '));
5671 // no format specified for actual keyword
5675 sal_Int32 nKeywordLen
= rKeywords
[nDateKey
].getLength() + 1;
5676 sal_Int32 nFieldEnd
= rNum
.GetParams().indexOf(',', nField
);
5678 if (nFieldEnd
== -1)
5679 nFieldEnd
= rNum
.GetParams().getLength();
5681 css::lang::Locale
aLocale( LanguageTag( rNum
.GetLang() ).getLocale() );
5683 return GetFormatter().GetNatNum()->getNativeNumberStringParams(
5684 rStr
, aLocale
, rNum
.GetNatNum(),
5685 rNum
.GetParams().copy(nField
+ nKeywordLen
, nFieldEnd
- nField
- nKeywordLen
));
5688 void SvNumberformat::GetNatNumXml( css::i18n::NativeNumberXmlAttributes2
& rAttr
,
5689 sal_uInt16 nNumFor
) const
5693 const SvNumberNatNum
& rNum
= NumFor
[nNumFor
].GetNatNum();
5696 css::lang::Locale
aLocale(
5697 LanguageTag( rNum
.GetLang() ).getLocale() );
5699 /* TODO: a new XNativeNumberSupplier2::convertToXmlAttributes()
5700 * should rather return NativeNumberXmlAttributes2 and places
5701 * adapted, and whether to fill Spellout or something different
5702 * should be internal there. */
5703 css::i18n::NativeNumberXmlAttributes
aTmp(
5704 GetFormatter().GetNatNum()->convertToXmlAttributes(
5705 aLocale
, rNum
.GetNatNum()));
5706 rAttr
.Locale
= aTmp
.Locale
;
5707 rAttr
.Format
= aTmp
.Format
;
5708 rAttr
.Style
= aTmp
.Style
;
5709 if ( NatNumTakesParameters(rNum
.GetNatNum()) )
5711 // NatNum12 spell out numbers, dates and money amounts
5712 rAttr
.Spellout
= rNum
.GetParams();
5713 // Mutually exclusive.
5714 rAttr
.Format
.clear();
5715 rAttr
.Style
.clear();
5719 rAttr
.Spellout
.clear();
5724 rAttr
= css::i18n::NativeNumberXmlAttributes2();
5729 rAttr
= css::i18n::NativeNumberXmlAttributes2();
5734 bool SvNumberformat::HasStringNegativeSign( const OUString
& rStr
)
5736 // For Sign '-' needs to be at the start or at the end of the string (blanks ignored)
5737 sal_Int32 nLen
= rStr
.getLength();
5742 const sal_Unicode
* const pBeg
= rStr
.getStr();
5743 const sal_Unicode
* const pEnd
= pBeg
+ nLen
;
5744 const sal_Unicode
* p
= pBeg
;
5752 while ( *p
== ' ' && ++p
< pEnd
);
5763 while ( *p
== ' ' && pBeg
< --p
);
5768 bool SvNumberformat::IsInQuote( const OUString
& rStr
, sal_Int32 nPos
,
5769 sal_Unicode cQuote
, sal_Unicode cEscIn
, sal_Unicode cEscOut
)
5771 sal_Int32 nLen
= rStr
.getLength();
5776 const sal_Unicode
* p0
= rStr
.getStr();
5777 const sal_Unicode
* p
= p0
;
5778 const sal_Unicode
* p1
= p0
+ nPos
;
5779 bool bQuoted
= false;
5790 if ( *(p
-1) != cEscIn
)
5797 if ( *(p
-1) != cEscOut
)
5809 sal_Int32
SvNumberformat::GetQuoteEnd( const OUString
& rStr
, sal_Int32 nPos
,
5810 sal_Unicode cQuote
, sal_Unicode cEscIn
)
5816 sal_Int32 nLen
= rStr
.getLength();
5821 if ( !IsInQuote( rStr
, nPos
, cQuote
, cEscIn
) )
5823 if ( rStr
[ nPos
] == cQuote
)
5825 return nPos
; // Closing cQuote
5829 const sal_Unicode
* p0
= rStr
.getStr();
5830 const sal_Unicode
* p
= p0
+ nPos
;
5831 const sal_Unicode
* p1
= p0
+ nLen
;
5834 if ( *p
== cQuote
&& p
> p0
&& *(p
-1) != cEscIn
)
5836 return sal::static_int_cast
< sal_Int32
>(p
- p0
);
5840 return nLen
; // End of String
5843 sal_uInt16
SvNumberformat::GetNumForNumberElementCount( sal_uInt16 nNumFor
) const
5847 sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
5848 return nCnt
- ImpGetNumForStringElementCount( nNumFor
);
5853 sal_uInt16
SvNumberformat::ImpGetNumForStringElementCount( sal_uInt16 nNumFor
) const
5855 sal_uInt16 nCnt
= 0;
5856 sal_uInt16 nNumForCnt
= NumFor
[nNumFor
].GetCount();
5857 auto& rTypeArray
= NumFor
[nNumFor
].Info().nTypeArray
;
5858 for ( sal_uInt16 j
=0; j
<nNumForCnt
; ++j
)
5860 switch ( rTypeArray
[j
] )
5862 case NF_SYMBOLTYPE_STRING
:
5863 case NF_SYMBOLTYPE_CURRENCY
:
5864 case NF_SYMBOLTYPE_DATESEP
:
5865 case NF_SYMBOLTYPE_TIMESEP
:
5866 case NF_SYMBOLTYPE_TIME100SECSEP
:
5867 case NF_SYMBOLTYPE_PERCENT
:
5875 bool SvNumberformat::IsMinuteSecondFormat() const
5877 if (GetMaskedType() != SvNumFormatType::TIME
)
5880 constexpr sal_uInt16 k00
= 0x00; // Nada, Nilch
5881 constexpr sal_uInt16 kLB
= 0x01; // '[' Left Bracket
5882 constexpr sal_uInt16 kRB
= 0x02; // ']' Right Bracket
5883 constexpr sal_uInt16 kMM
= 0x04; // M or MM
5884 constexpr sal_uInt16 kTS
= 0x08; // Time Separator
5885 constexpr sal_uInt16 kSS
= 0x10; // S or SS
5886 #define HAS_MINUTE_SECOND(state) ((state) == (kMM|kTS|kSS) || (state) == (kLB|kMM|kRB|kTS|kSS))
5887 // Also (kMM|kTS|kLB|kSS|kRB) but those are the same bits.
5889 sal_uInt16 nState
= k00
;
5891 sal_uInt16 nNumForCnt
= NumFor
[0].GetCount();
5892 auto const & rTypeArray
= NumFor
[0].Info().nTypeArray
;
5893 for (sal_uInt16 j
=0; j
< nNumForCnt
; ++j
)
5895 switch (rTypeArray
[j
])
5897 case NF_SYMBOLTYPE_DEL
:
5899 // '[' or ']' before/after MM or SS
5900 const OUString
& rStr
= NumFor
[0].Info().sStrArray
[j
];
5903 if (nState
!= k00
&& nState
!= (kMM
|kTS
))
5907 else if (rStr
== "]")
5909 if (nState
!= (kLB
|kMM
) && nState
!= (kMM
|kTS
|kLB
|kSS
))
5919 if (nState
!= k00
&& nState
!= kLB
)
5923 case NF_SYMBOLTYPE_TIMESEP
:
5924 if (nState
!= kMM
&& nState
!= (kLB
|kMM
|kRB
))
5930 if (nState
!= (kMM
|kTS
) && nState
!= (kLB
|kMM
|kRB
|kTS
) && nState
!= (kMM
|kTS
|kLB
))
5934 case NF_SYMBOLTYPE_TIME100SECSEP
:
5935 // Trailing fraction of seconds allowed.
5936 if (!HAS_MINUTE_SECOND(nState
))
5940 case NF_SYMBOLTYPE_DIGIT
:
5944 case NF_SYMBOLTYPE_STRING
:
5945 // nothing, display literal
5951 return HAS_MINUTE_SECOND(nState
);
5952 #undef HAS_MINUTE_SECOND
5955 OUString
SvNumberformat::GetFormatStringForTimePrecision( int nPrecision
) const
5957 OUStringBuffer sString
;
5958 using comphelper::string::padToLength
;
5960 sal_uInt16 nNumForCnt
= NumFor
[0].GetCount();
5961 auto const & rTypeArray
= NumFor
[0].Info().nTypeArray
;
5962 for (sal_uInt16 j
=0; j
< nNumForCnt
; ++j
)
5964 switch (rTypeArray
[j
])
5968 sString
.append( NumFor
[0].Info().sStrArray
[j
] );
5969 if ( j
> 0 && rTypeArray
[j
-1] == NF_SYMBOLTYPE_DEL
&& j
< nNumForCnt
-1 )
5972 sString
.append( NumFor
[0].Info().sStrArray
[j
] );
5976 sString
.append( rLoc().getTime100SecSep() );
5977 padToLength(sString
, sString
.getLength() + nPrecision
, '0');
5980 case NF_SYMBOLTYPE_TIME100SECSEP
:
5981 case NF_SYMBOLTYPE_DIGIT
:
5983 case NF_SYMBOLTYPE_STRING
:
5984 sString
.append( "\"" );
5987 sString
.append( NumFor
[0].Info().sStrArray
[j
] );
5988 if (rTypeArray
[j
] == NF_SYMBOLTYPE_STRING
)
5990 sString
.append( "\"" );
5995 return sString
.makeStringAndClear();
5998 sal_uInt16
SvNumberformat::GetThousandDivisorPrecision( sal_uInt16 nIx
) const
6003 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
6005 if (rInfo
.eScannedType
!= SvNumFormatType::NUMBER
&& rInfo
.eScannedType
!= SvNumFormatType::CURRENCY
)
6008 if (rInfo
.nThousand
== FLAG_STANDARD_IN_FORMAT
)
6009 return SvNumberFormatter::UNLIMITED_PRECISION
;
6011 return rInfo
.nThousand
* 3;
6014 const CharClass
& SvNumberformat::rChrCls() const
6016 return rScan
.GetChrCls();
6019 const LocaleDataWrapper
& SvNumberformat::rLoc() const
6021 return rScan
.GetLoc();
6024 CalendarWrapper
& SvNumberformat::GetCal() const
6026 return rScan
.GetCal();
6029 const SvNumberFormatter
& SvNumberformat::GetFormatter() const
6031 return *rScan
.GetNumberformatter();
6034 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */