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 .
20 #include <string_view>
22 #include <o3tl/sprintf.hxx>
23 #include <o3tl/string_view.hxx>
24 #include <comphelper/string.hxx>
25 #include <sal/log.hxx>
26 #include <tools/debug.hxx>
27 #include <tools/long.hxx>
28 #include <i18nlangtag/mslangid.hxx>
29 #include <rtl/math.hxx>
30 #include <unotools/charclass.hxx>
31 #include <unotools/calendarwrapper.hxx>
32 #include <unotools/nativenumberwrapper.hxx>
33 #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
34 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
35 #include <com/sun/star/i18n/CalendarDisplayCode.hpp>
36 #include <com/sun/star/i18n/AmPmValue.hpp>
37 #include <com/sun/star/i18n/NativeNumberMode.hpp>
38 #include <com/sun/star/i18n/NativeNumberXmlAttributes2.hpp>
40 #include <svl/zformat.hxx>
41 #include "zforscan.hxx"
43 #include "zforfind.hxx"
44 #include <svl/zforlist.hxx>
45 #include <unotools/digitgroupingiterator.hxx>
46 #include <svl/nfsymbol.hxx>
55 constexpr OUString GREGORIAN
= u
"gregorian"_ustr
;
57 const sal_uInt16 UPPER_PRECISION
= 300; // entirely arbitrary...
58 const double EXP_LOWER_BOUND
= 1.0E-4; // prefer scientific notation below this value.
59 const double EXP_ABS_UPPER_BOUND
= 1.0E15
; // use exponential notation above that absolute value.
60 // Back in time was E16 that lead
61 // to display rounding errors, see
62 // also sal/rtl/math.cxx
65 constexpr sal_Int32 kTimeSignificantRound
= 7; // Round (date+)time at 7 decimals
66 // (+5 of 86400 == 12 significant digits).
68 const sal_Unicode cBlankDigit
= 0x2007; // tdf#158890 use figure space for '?'
71 const double D_MAX_U_INT32
= double(0xffffffff); // 4294967295.0
72 constexpr double D_MAX_INTEGER
= (sal_uInt64(1) << 53) - 1;
74 const double D_MAX_D_BY_100
= 1.7E306
;
75 const double D_MIN_M_BY_1000
= 2.3E-305;
77 const sal_uInt8 cCharWidths
[ 128-32 ] = {
78 1,1,1,2,2,3,2,1,1,1,1,2,1,1,1,1,
79 2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,
80 3,2,2,2,2,2,2,3,2,1,2,2,2,3,3,3,
81 2,3,2,2,2,2,2,3,2,2,2,1,1,1,2,2,
82 1,2,2,2,2,2,1,2,2,1,1,2,1,3,2,2,
83 2,2,1,2,1,2,2,2,2,2,2,1,1,1,2,1
87 sal_Int32
SvNumberformat::InsertBlanks( OUStringBuffer
& r
, sal_Int32 nPos
, sal_Unicode c
)
91 int n
= 2; // Default for chars > 128 (HACK!)
94 n
= static_cast<int>(cCharWidths
[ c
- 32 ]);
98 r
.insert( nPos
++, ' ');
104 static tools::Long
GetPrecExp( double fAbsVal
)
106 DBG_ASSERT( fAbsVal
> 0.0, "GetPrecExp: fAbsVal <= 0.0" );
107 if ( fAbsVal
< 1e-7 || fAbsVal
> 1e7
)
109 // Shear: whether it's faster or not, falls in between 1e6 and 1e7
110 return static_cast<tools::Long
>(floor( log10( fAbsVal
) )) + 1;
114 tools::Long nPrecExp
= 1;
120 while( fAbsVal
>= 10 )
133 void ImpSvNumberformatInfo::Copy( const ImpSvNumberformatInfo
& rNumFor
, sal_uInt16 nCnt
)
135 for (sal_uInt16 i
= 0; i
< nCnt
; ++i
)
137 sStrArray
[i
] = rNumFor
.sStrArray
[i
];
138 nTypeArray
[i
] = rNumFor
.nTypeArray
[i
];
140 eScannedType
= rNumFor
.eScannedType
;
141 bThousand
= rNumFor
.bThousand
;
142 nThousand
= rNumFor
.nThousand
;
143 nCntPre
= rNumFor
.nCntPre
;
144 nCntPost
= rNumFor
.nCntPost
;
145 nCntExp
= rNumFor
.nCntExp
;
148 const std::map
<LanguageType
, std::array
<sal_uInt8
, 4>> tblDBNumToNatNum
149 = { { primary(LANGUAGE_CHINESE
), { 4, 5, 3, 0 } },
150 { primary(LANGUAGE_JAPANESE
), { 4, 5, 3, 0 } },
151 { primary(LANGUAGE_KOREAN
), { 4, 5, 6, 10 } } };
154 sal_uInt8
SvNumberNatNum::MapDBNumToNatNum( sal_uInt8 nDBNum
, LanguageType eLang
, bool bDate
)
156 sal_uInt8 nNatNum
= 0;
157 eLang
= MsLangId::getRealLanguage( eLang
); // resolve SYSTEM etc.
158 eLang
= primary(eLang
); // 10 bit primary language
161 if ( nDBNum
== 4 && eLang
== primary(LANGUAGE_KOREAN
) )
165 else if ( nDBNum
<= 3 )
167 nNatNum
= nDBNum
; // known to be good for: zh,ja,ko / 1,2,3
172 if (1 <= nDBNum
&& nDBNum
<= 4)
174 auto const it
= tblDBNumToNatNum
.find(eLang
);
175 if (it
!= tblDBNumToNatNum
.end())
176 nNatNum
= it
->second
[nDBNum
- 1];
183 const std::map
<LanguageType
, std::array
<sal_uInt8
, 9>> tblNatNumToDBNum
184 = { { primary(LANGUAGE_CHINESE
), { 1, 0, 0, 1, 2, 3, 0, 0, 0 } },
185 { primary(LANGUAGE_JAPANESE
), { 1, 2, 3, 1, 2, 3, 1, 2, 0 } },
186 { primary(LANGUAGE_KOREAN
), { 1, 2, 3, 1, 2, 3, 1, 2, 4 } } };
189 sal_uInt8
SvNumberNatNum::MapNatNumToDBNum( sal_uInt8 nNatNum
, LanguageType eLang
, bool bDate
)
191 sal_uInt8 nDBNum
= 0;
192 eLang
= MsLangId::getRealLanguage( eLang
); // resolve SYSTEM etc.
193 eLang
= primary(eLang
); // 10 bit primary language
196 if ( nNatNum
== 10 && eLang
== primary(LANGUAGE_KOREAN
) )
200 else if ( nNatNum
<= 3 )
202 nDBNum
= nNatNum
; // known to be good for: zh,ja,ko / 1,2,3
207 if (1 <= nNatNum
&& nNatNum
<= 9)
209 auto const it
= tblNatNumToDBNum
.find(eLang
);
210 if (it
!= tblNatNumToDBNum
.end())
211 nDBNum
= it
->second
[nNatNum
- 1];
221 ImpSvNumFor::ImpSvNumFor()
224 aI
.eScannedType
= SvNumFormatType::UNDEFINED
;
225 aI
.bThousand
= false;
233 ImpSvNumFor::~ImpSvNumFor()
237 void ImpSvNumFor::Enlarge(sal_uInt16 nCnt
)
239 if ( nStringsCnt
!= nCnt
)
242 aI
.nTypeArray
.resize(nCnt
);
243 aI
.sStrArray
.resize(nCnt
);
247 void ImpSvNumFor::Copy( const ImpSvNumFor
& rNumFor
, const ImpSvNumberformatScan
* pSc
)
249 Enlarge( rNumFor
.nStringsCnt
);
250 aI
.Copy( rNumFor
.aI
, nStringsCnt
);
251 sColorName
= rNumFor
.sColorName
;
254 pColor
= pSc
->GetColor( sColorName
); // #121103# don't copy pointer between documents
258 pColor
= rNumFor
.pColor
;
260 aNatNum
= rNumFor
.aNatNum
;
263 bool ImpSvNumFor::HasNewCurrency() const
265 for ( sal_uInt16 j
=0; j
<nStringsCnt
; j
++ )
267 if ( aI
.nTypeArray
[j
] == NF_SYMBOLTYPE_CURRENCY
)
275 bool ImpSvNumFor::GetNewCurrencySymbol( OUString
& rSymbol
,
276 OUString
& rExtension
) const
278 for ( sal_uInt16 j
=0; j
<nStringsCnt
; j
++ )
280 if ( aI
.nTypeArray
[j
] == NF_SYMBOLTYPE_CURRENCY
)
282 rSymbol
= aI
.sStrArray
[j
];
283 if ( j
< nStringsCnt
-1 && aI
.nTypeArray
[j
+1] == NF_SYMBOLTYPE_CURREXT
)
285 rExtension
= aI
.sStrArray
[j
+1];
294 //! No Erase at rSymbol, rExtension
304 enum BracketFormatSymbolType
306 BRACKET_SYMBOLTYPE_FORMAT
= -1, // subformat string
307 BRACKET_SYMBOLTYPE_COLOR
= -2, // color
308 BRACKET_SYMBOLTYPE_ERROR
= -3, // error
309 BRACKET_SYMBOLTYPE_DBNUM1
= -4, // DoubleByteNumber, represent numbers
310 BRACKET_SYMBOLTYPE_DBNUM2
= -5, // using CJK characters, Excel compatible
311 BRACKET_SYMBOLTYPE_DBNUM3
= -6,
312 BRACKET_SYMBOLTYPE_DBNUM4
= -7,
313 BRACKET_SYMBOLTYPE_DBNUM5
= -8,
314 BRACKET_SYMBOLTYPE_DBNUM6
= -9,
315 BRACKET_SYMBOLTYPE_DBNUM7
= -10,
316 BRACKET_SYMBOLTYPE_DBNUM8
= -11,
317 BRACKET_SYMBOLTYPE_DBNUM9
= -12,
318 BRACKET_SYMBOLTYPE_LOCALE
= -13,
319 BRACKET_SYMBOLTYPE_NATNUM0
= -14, // Our NativeNumber support, ASCII
320 BRACKET_SYMBOLTYPE_NATNUM1
= -15, // Our NativeNumber support, represent
321 BRACKET_SYMBOLTYPE_NATNUM2
= -16, // numbers using CJK, CTL, ...
322 BRACKET_SYMBOLTYPE_NATNUM3
= -17,
323 BRACKET_SYMBOLTYPE_NATNUM4
= -18,
324 BRACKET_SYMBOLTYPE_NATNUM5
= -19,
325 BRACKET_SYMBOLTYPE_NATNUM6
= -20,
326 BRACKET_SYMBOLTYPE_NATNUM7
= -21,
327 BRACKET_SYMBOLTYPE_NATNUM8
= -22,
328 BRACKET_SYMBOLTYPE_NATNUM9
= -23,
329 BRACKET_SYMBOLTYPE_NATNUM10
= -24,
330 BRACKET_SYMBOLTYPE_NATNUM11
= -25,
331 BRACKET_SYMBOLTYPE_NATNUM12
= -26,
332 BRACKET_SYMBOLTYPE_NATNUM13
= -27,
333 BRACKET_SYMBOLTYPE_NATNUM14
= -28,
334 BRACKET_SYMBOLTYPE_NATNUM15
= -29,
335 BRACKET_SYMBOLTYPE_NATNUM16
= -30,
336 BRACKET_SYMBOLTYPE_NATNUM17
= -31,
337 BRACKET_SYMBOLTYPE_NATNUM18
= -32,
338 BRACKET_SYMBOLTYPE_NATNUM19
= -33
343 void SvNumberformat::ImpCopyNumberformat( const SvNumberformat
& rFormat
)
345 sFormatstring
= rFormat
.sFormatstring
;
346 eType
= rFormat
.eType
;
347 maLocale
= rFormat
.maLocale
;
348 fLimit1
= rFormat
.fLimit1
;
349 fLimit2
= rFormat
.fLimit2
;
352 bStandard
= rFormat
.bStandard
;
353 bIsUsed
= rFormat
.bIsUsed
;
354 sComment
= rFormat
.sComment
;
355 bAdditionalBuiltin
= rFormat
.bAdditionalBuiltin
;
357 // #121103# when copying between documents, get color pointers from own scanner
358 ImpSvNumberformatScan
* pColorSc
= ( &rScan
!= &rFormat
.rScan
) ? &rScan
: nullptr;
360 for (sal_uInt16 i
= 0; i
< 4; i
++)
362 NumFor
[i
].Copy(rFormat
.NumFor
[i
], pColorSc
);
366 SvNumberformat::SvNumberformat( SvNumberformat
const & rFormat
)
367 : rScan(rFormat
.rScan
)
369 ImpCopyNumberformat( rFormat
);
372 SvNumberformat::SvNumberformat( SvNumberformat
const & rFormat
, ImpSvNumberformatScan
& rSc
)
375 ImpCopyNumberformat( rFormat
);
378 static bool lcl_SvNumberformat_IsBracketedPrefix( short nSymbolType
)
380 if ( nSymbolType
> 0 )
382 return true; // conditions
384 switch ( nSymbolType
)
386 case BRACKET_SYMBOLTYPE_COLOR
:
387 case BRACKET_SYMBOLTYPE_DBNUM1
:
388 case BRACKET_SYMBOLTYPE_DBNUM2
:
389 case BRACKET_SYMBOLTYPE_DBNUM3
:
390 case BRACKET_SYMBOLTYPE_DBNUM4
:
391 case BRACKET_SYMBOLTYPE_DBNUM5
:
392 case BRACKET_SYMBOLTYPE_DBNUM6
:
393 case BRACKET_SYMBOLTYPE_DBNUM7
:
394 case BRACKET_SYMBOLTYPE_DBNUM8
:
395 case BRACKET_SYMBOLTYPE_DBNUM9
:
396 case BRACKET_SYMBOLTYPE_LOCALE
:
397 case BRACKET_SYMBOLTYPE_NATNUM0
:
398 case BRACKET_SYMBOLTYPE_NATNUM1
:
399 case BRACKET_SYMBOLTYPE_NATNUM2
:
400 case BRACKET_SYMBOLTYPE_NATNUM3
:
401 case BRACKET_SYMBOLTYPE_NATNUM4
:
402 case BRACKET_SYMBOLTYPE_NATNUM5
:
403 case BRACKET_SYMBOLTYPE_NATNUM6
:
404 case BRACKET_SYMBOLTYPE_NATNUM7
:
405 case BRACKET_SYMBOLTYPE_NATNUM8
:
406 case BRACKET_SYMBOLTYPE_NATNUM9
:
407 case BRACKET_SYMBOLTYPE_NATNUM10
:
408 case BRACKET_SYMBOLTYPE_NATNUM11
:
409 case BRACKET_SYMBOLTYPE_NATNUM12
:
410 case BRACKET_SYMBOLTYPE_NATNUM13
:
411 case BRACKET_SYMBOLTYPE_NATNUM14
:
412 case BRACKET_SYMBOLTYPE_NATNUM15
:
413 case BRACKET_SYMBOLTYPE_NATNUM16
:
414 case BRACKET_SYMBOLTYPE_NATNUM17
:
415 case BRACKET_SYMBOLTYPE_NATNUM18
:
416 case BRACKET_SYMBOLTYPE_NATNUM19
:
422 /** Import extended LCID from Excel
424 OUString
SvNumberformat::ImpObtainCalendarAndNumerals( OUStringBuffer
& rString
, sal_Int32 nPos
,
425 LanguageType
& nLang
, const LocaleType
& aTmpLocale
)
428 sal_uInt16 nNatNum
= 0;
429 LanguageType nLocaleLang
= MsLangId::getRealLanguage( maLocale
.meLanguage
);
430 LanguageType nTmpLocaleLang
= MsLangId::getRealLanguage( aTmpLocale
.meLanguage
);
431 /* NOTE: enhancement to allow other possible locale dependent
432 * calendars and numerals. BUT only if our locale data allows it! For LCID
433 * numerals and calendars see
434 * http://office.microsoft.com/en-us/excel/HA010346351033.aspx
435 * Calendar is inserted after
436 * all prefixes have been consumed as it is actually a format modifier
438 * Currently calendars are tied to the locale of the entire number
439 * format, e.g. [~buddhist] in en_US doesn't work.
440 * => Having different locales in sub formats does not work!
442 /* TODO: calendars could be tied to a sub format's NatNum info
443 * instead, or even better be available for any locale. Needs a
444 * different implementation of GetCal() and locale data calendars.
446 switch ( aTmpLocale
.mnCalendarType
& 0x7F )
448 case 0x03 : // Gengou calendar
449 // Only Japanese language support Gengou calendar.
450 // It is an implicit "other" calendar where E, EE, R and RR
451 // automatically switch to and YY and YYYY switch to Gregorian. Do
452 // not add the "[~gengou]" modifier.
453 if ( nLocaleLang
!= LANGUAGE_JAPANESE
)
455 nLang
= maLocale
.meLanguage
= LANGUAGE_JAPANESE
;
458 case 0x05 : // Korean Dangi calendar
459 sCalendar
= "[~dangi]";
460 // Only Korean language support dangi calendar
461 if ( nLocaleLang
!= LANGUAGE_KOREAN
)
463 nLang
= maLocale
.meLanguage
= LANGUAGE_KOREAN
;
466 case 0x06 : // Hijri calendar
468 sCalendar
= "[~hijri]";
469 // Only Arabic or Farsi languages support Hijri calendar
470 if ( ( primary( nLocaleLang
) != LANGUAGE_ARABIC_PRIMARY_ONLY
)
471 && nLocaleLang
!= LANGUAGE_FARSI
)
473 if ( ( primary( nTmpLocaleLang
) == LANGUAGE_ARABIC_PRIMARY_ONLY
)
474 || nTmpLocaleLang
== LANGUAGE_FARSI
)
476 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
480 nLang
= maLocale
.meLanguage
= LANGUAGE_ARABIC_SAUDI_ARABIA
;
484 case 0x07 : // Buddhist calendar
485 sCalendar
="[~buddhist]";
486 // Only Thai or Lao languages support Buddhist calendar
487 if ( nLocaleLang
!= LANGUAGE_THAI
&& nLocaleLang
!= LANGUAGE_LAO
)
489 if ( nTmpLocaleLang
== LANGUAGE_THAI
|| nTmpLocaleLang
== LANGUAGE_LAO
)
491 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
495 nLang
= maLocale
.meLanguage
= LANGUAGE_THAI
;
499 case 0x08 : // Hebrew calendar
500 sCalendar
= "[~jewish]";
501 // Many languages (but not all) support Jewish calendar
502 // Unable to find any logic => keep same language
504 case 0x0E : // unknown calendar
505 case 0x0F : // unknown calendar
506 case 0x10 : // Indian calendar (unsupported)
507 case 0x11 : // unknown calendar
508 case 0x12 : // unknown calendar
509 case 0x13 : // unknown calendar
510 default : // other calendars (see tdf#36038) are not handle by LibO
513 /** Reference language for each numeral ID */
514 static const LanguageType aNumeralIDtoLanguage
[]=
516 LANGUAGE_DONTKNOW
, // 0x00
517 LANGUAGE_ENGLISH_US
, // 0x01
518 LANGUAGE_ARABIC_SAUDI_ARABIA
, // 0x02 + all Arabic
519 LANGUAGE_FARSI
, // 0x03
520 LANGUAGE_HINDI
, // 0x04 + Devanagari
521 LANGUAGE_BENGALI
, // 0x05
522 LANGUAGE_PUNJABI
, // 0x06
523 LANGUAGE_GUJARATI
, // 0x07
524 LANGUAGE_ODIA
, // 0x08
525 LANGUAGE_TAMIL
, // 0x09
526 LANGUAGE_TELUGU
, // 0x0A
527 LANGUAGE_KANNADA
, // 0x0B
528 LANGUAGE_MALAYALAM
, // 0x0C
529 LANGUAGE_THAI
, // 0x0D
530 LANGUAGE_LAO
, // 0x0E
531 LANGUAGE_TIBETAN
, // 0x0F
532 LANGUAGE_BURMESE
, // 0x10
533 LANGUAGE_TIGRIGNA_ETHIOPIA
, // 0x11
534 LANGUAGE_KHMER
, // 0x12
535 LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
, // 0x13
536 LANGUAGE_DONTKNOW
, // 0x14
537 LANGUAGE_DONTKNOW
, // 0x15
538 LANGUAGE_DONTKNOW
, // 0x16
539 LANGUAGE_DONTKNOW
, // 0x17
540 LANGUAGE_DONTKNOW
, // 0x18
541 LANGUAGE_DONTKNOW
, // 0x19
542 LANGUAGE_DONTKNOW
, // 0x1A
543 LANGUAGE_JAPANESE
, // 0x1B
544 LANGUAGE_JAPANESE
, // 0x1C
545 LANGUAGE_JAPANESE
, // 0x1D
546 LANGUAGE_CHINESE_SIMPLIFIED
, // 0x1E
547 LANGUAGE_CHINESE_SIMPLIFIED
, // 0x1F
548 LANGUAGE_CHINESE_SIMPLIFIED
, // 0x20
549 LANGUAGE_CHINESE_TRADITIONAL
, // 0x21
550 LANGUAGE_CHINESE_TRADITIONAL
, // 0x22
551 LANGUAGE_CHINESE_TRADITIONAL
, // 0x23
552 LANGUAGE_KOREAN
, // 0x24
553 LANGUAGE_KOREAN
, // 0x25
554 LANGUAGE_KOREAN
, // 0x26
555 LANGUAGE_KOREAN
// 0x27
558 sal_uInt16 nNumeralID
= aTmpLocale
.mnNumeralShape
& 0x7F;
559 LanguageType nReferenceLanguage
= nNumeralID
<= 0x27 ? aNumeralIDtoLanguage
[nNumeralID
] : LANGUAGE_DONTKNOW
;
561 switch ( nNumeralID
)
563 // Regular cases: all languages with same primary mask use same numerals
564 case 0x03 : // Perso-Arabic (Farsi) numerals
565 case 0x05 : // Bengali numerals
566 case 0x06 : // Punjabi numerals
567 case 0x07 : // Gujarati numerals
568 case 0x08 : // Odia (Orya) numerals
569 case 0x09 : // Tamil numerals
570 case 0x0A : // Telugu numerals
571 case 0x0B : // Kannada numerals
572 case 0x0C : // Malayalam numerals
573 case 0x0D : // Thai numerals
574 case 0x0E : // Lao numerals
575 case 0x0F : // Tibetan numerals
576 case 0x10 : // Burmese (Myanmar) numerals
577 case 0x11 : // Tigrigna (Ethiopia) numerals
578 case 0x12 : // Khmer numerals
579 if ( primary( nLocaleLang
) != primary( nReferenceLanguage
) )
581 if ( primary( nTmpLocaleLang
) == primary( nReferenceLanguage
) )
583 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
587 nLang
= maLocale
.meLanguage
= nReferenceLanguage
;
592 case 0x04 : // Devanagari (Hindi) numerals
593 // same numerals (Devanagari) for languages with different primary masks
594 if ( nLocaleLang
!= LANGUAGE_HINDI
&& nLocaleLang
!= LANGUAGE_MARATHI
595 && primary( nLocaleLang
) != primary( LANGUAGE_NEPALI
) )
597 if ( nTmpLocaleLang
== LANGUAGE_HINDI
|| nTmpLocaleLang
== LANGUAGE_MARATHI
598 || primary( nTmpLocaleLang
) == primary( LANGUAGE_NEPALI
) )
600 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
604 nLang
= maLocale
.meLanguage
= LANGUAGE_HINDI
;
608 case 0x13 : // Mongolian numerals
609 // not all Mongolian languages use Mongolian numerals
610 if ( nLocaleLang
!= LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
611 && nLocaleLang
!= LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA
612 && nLocaleLang
!= LANGUAGE_MONGOLIAN_MONGOLIAN_LSO
)
614 if ( nTmpLocaleLang
== LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
615 || nTmpLocaleLang
== LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA
616 || nTmpLocaleLang
== LANGUAGE_MONGOLIAN_MONGOLIAN_LSO
)
618 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
622 nLang
= maLocale
.meLanguage
= LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
;
626 case 0x02 : // Eastern-Arabic numerals
627 // all arabic primary mask + LANGUAGE_PUNJABI_ARABIC_LSO
628 if ( primary( nLocaleLang
) != LANGUAGE_ARABIC_PRIMARY_ONLY
629 && nLocaleLang
!= LANGUAGE_PUNJABI_ARABIC_LSO
)
631 if ( primary( nTmpLocaleLang
) == LANGUAGE_ARABIC_PRIMARY_ONLY
632 || nTmpLocaleLang
!= LANGUAGE_PUNJABI_ARABIC_LSO
)
634 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
638 nLang
= maLocale
.meLanguage
= nReferenceLanguage
;
643 case 0x1B : // simple Asian numerals, Japanese
644 case 0x1C : // financial Asian numerals, Japanese
645 case 0x1D : // Arabic fullwidth numerals, Japanese
646 case 0x24 : // simple Asian numerals, Korean
647 case 0x25 : // financial Asian numerals, Korean
648 case 0x26 : // Arabic fullwidth numerals, Korean
649 case 0x27 : // Korean Hangul numerals
650 // Japanese and Korean are regular
651 if ( primary( nLocaleLang
) != primary( nReferenceLanguage
) )
653 if ( primary( nTmpLocaleLang
) == primary( nReferenceLanguage
) )
655 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
659 nLang
= maLocale
.meLanguage
= nReferenceLanguage
;
663 case 0x1E : // simple Asian numerals, Chinese-PRC
664 case 0x1F : // financial Asian numerals, Chinese-PRC
665 case 0x20 : // Arabic fullwidth numerals, Chinese-PRC
666 case 0x21 : // simple Asian numerals, Chinese-Taiwan
667 case 0x22 : // financial Asian numerals, Chinese-Taiwan
668 case 0x23 : // Arabic fullwidth numerals, Chinese-Taiwan
669 nNatNum
= nNumeralID
== 0x27 ? 9 : ( ( nNumeralID
- 0x1B ) % 3 ) + 1;
670 // [NatNum1] simple numerals
671 // [natNum2] financial numerals
672 // [NatNum3] Arabic fullwidth numerals
673 // Chinese simplified and Chinese traditional have same primary mask
675 if ( nReferenceLanguage
== LANGUAGE_CHINESE_SIMPLIFIED
676 && nLocaleLang
!= LANGUAGE_CHINESE_SIMPLIFIED
677 && nLocaleLang
!= LANGUAGE_CHINESE_SINGAPORE
678 && nLocaleLang
!= LANGUAGE_CHINESE_LSO
)
680 if ( nTmpLocaleLang
== LANGUAGE_CHINESE_SIMPLIFIED
681 || nTmpLocaleLang
== LANGUAGE_CHINESE_SINGAPORE
682 || nTmpLocaleLang
== LANGUAGE_CHINESE_LSO
)
684 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
688 nLang
= maLocale
.meLanguage
= LANGUAGE_CHINESE_SIMPLIFIED
;
692 else if ( nReferenceLanguage
== LANGUAGE_CHINESE_TRADITIONAL
693 && nLocaleLang
!= LANGUAGE_CHINESE_TRADITIONAL
694 && nLocaleLang
!= LANGUAGE_CHINESE_HONGKONG
695 && nLocaleLang
!= LANGUAGE_CHINESE_MACAU
)
697 if ( nTmpLocaleLang
== LANGUAGE_CHINESE_TRADITIONAL
698 || nTmpLocaleLang
== LANGUAGE_CHINESE_HONGKONG
699 || nTmpLocaleLang
== LANGUAGE_CHINESE_MACAU
)
701 nLang
= maLocale
.meLanguage
= aTmpLocale
.meLanguage
;
705 nLang
= maLocale
.meLanguage
= LANGUAGE_CHINESE_TRADITIONAL
;
710 if ( nNumeralID
>= 0x02 && nNumeralID
<= 0x13 )
713 rString
.insert(nPos
, "[NatNum" + OUString::number(nNatNum
) + "]");
719 bool NatNumTakesParameters(sal_Int16 nNum
)
721 return (nNum
== css::i18n::NativeNumberMode::NATNUM12
);
725 // is there a 3-letter bank code in NatNum12 param (but not
726 // followed by an equal mark, like in the date code "NNN=")?
727 static bool lcl_isNatNum12Currency( std::u16string_view sParam
)
729 sal_Int32 nUpper
= 0;
730 sal_Int32 nLen
= sParam
.size();
731 for (sal_Int32 n
= 0; n
< nLen
; ++n
)
733 sal_Unicode c
= sParam
[n
];
734 if ( 'A' <= c
&& c
<= 'Z' )
738 else if ( c
== ' ' && nUpper
== 3 && (n
== 3 || sParam
[n
- 4] == ' ') )
748 return nUpper
== 3 && (nLen
== 3 || sParam
[nLen
- 4] == ' ');
751 SvNumberformat::SvNumberformat(OUString
& rString
,
752 ImpSvNumberformatScan
* pSc
,
753 ImpSvNumberInputScan
* pISc
,
754 const NativeNumberWrapper
& rNatNum
,
755 sal_Int32
& nCheckPos
,
757 bool bReplaceBooleanEquivalent
)
759 , bAdditionalBuiltin( false )
761 if (bReplaceBooleanEquivalent
)
762 rScan
.ReplaceBooleanEquivalent( rString
);
764 OUStringBuffer
sBuff(rString
);
766 // If the group (AKA thousand) separator is a No-Break Space (French)
767 // replace all occurrences by a simple space.
768 // The same for Narrow No-Break Space just in case some locale uses it.
769 // The tokens will be changed to the LocaleData separator again later on.
770 const OUString
& rThSep
= GetCurrentLanguageData().GetNumThousandSep();
771 if ( rThSep
.getLength() == 1)
773 const sal_Unicode cNBSp
= 0xA0;
774 const sal_Unicode cNNBSp
= 0x202F;
775 if (rThSep
[0] == cNBSp
)
776 sBuff
.replace( cNBSp
, ' ');
777 else if (rThSep
[0] == cNNBSp
)
778 sBuff
.replace( cNNBSp
, ' ');
781 OUString aConvertFromDecSep
;
782 OUString aConvertToDecSep
;
783 if (rScan
.GetConvertMode())
785 aConvertFromDecSep
= GetCurrentLanguageData().GetNumDecimalSep();
786 maLocale
.meLanguage
= rScan
.GetNewLnge();
787 eLan
= maLocale
.meLanguage
; // Make sure to return switch
791 maLocale
.meLanguage
= eLan
;
797 eOp1
= NUMBERFORMAT_OP_NO
;
798 eOp2
= NUMBERFORMAT_OP_NO
;
799 eType
= SvNumFormatType::DEFINED
;
801 bool bCancel
= false;
802 bool bCondition
= false;
808 // Split into 4 sub formats
810 for ( nIndex
= 0; nIndex
< 4 && !bCancel
; nIndex
++ )
812 // Original language/country may have to be reestablished
813 if (rScan
.GetConvertMode())
815 rScan
.GetCurrentLanguageData().ChangeIntl(rScan
.GetTmpLnge());
817 OUString sInsertCalendar
; // a calendar resulting from parsing LCID
819 nPosOld
= nPos
; // Start position of substring
820 // first get bracketed prefixes; e.g. conditions, color
823 eSymbolType
= ImpNextSymbol(sBuff
, nPos
, sStr
);
824 if (eSymbolType
> 0) // condition
826 if ( nIndex
== 0 && !bCondition
)
829 eOp1
= static_cast<SvNumberformatLimitOps
>(eSymbolType
);
831 else if ( nIndex
== 1 && bCondition
)
833 eOp2
= static_cast<SvNumberformatLimitOps
>(eSymbolType
);
837 bCancel
= true; // break for
843 sal_Int32 nCntChars
= ImpGetNumber(sBuff
, nPos
, sStr
);
847 SvNumFormatType F_Type
= SvNumFormatType::UNDEFINED
;
848 if (!pISc
->IsNumberFormat(sStr
, F_Type
, fNumber
, nullptr, rNatNum
, SvNumInputOptions::NONE
) ||
849 ( F_Type
!= SvNumFormatType::NUMBER
&&
850 F_Type
!= SvNumFormatType::SCIENTIFIC
) )
853 nPos
= nPos
- nCntChars
;
854 sBuff
.remove(nPos
, nCntChars
);
855 sBuff
.insert(nPos
, '0');
858 else if (rScan
.GetConvertMode() && ((nDecPos
= sStr
.indexOf( aConvertFromDecSep
)) >= 0))
860 if (aConvertToDecSep
.isEmpty())
861 aConvertToDecSep
= rScan
.GetCurrentLanguageData().GetLangDecimalSep( rScan
.GetNewLnge());
862 if (aConvertToDecSep
!= aConvertFromDecSep
)
864 const OUString
aStr( sStr
.replaceAt( nDecPos
,
865 aConvertFromDecSep
.getLength(), aConvertToDecSep
));
866 nPos
= nPos
- nCntChars
;
867 sBuff
.remove(nPos
, nCntChars
);
868 sBuff
.insert(nPos
, aStr
);
869 nPos
+= aStr
.getLength();
876 sBuff
.insert(nPos
++, '0');
886 if ( nPos
< sBuff
.getLength() && sBuff
[nPos
] == ']' )
892 bCancel
= true; // break for
896 nPosOld
= nPos
; // position before string
898 else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType
) )
900 OUString
sSymbol( sStr
);
901 switch ( eSymbolType
)
903 case BRACKET_SYMBOLTYPE_COLOR
:
904 if ( NumFor
[nIndex
].GetColor() != nullptr )
905 { // error, more than one color
906 bCancel
= true; // break for
911 const Color
* pColor
= pSc
->GetColor( sStr
);
912 NumFor
[nIndex
].SetColor( pColor
, sStr
);
913 if (pColor
== nullptr)
915 bCancel
= true; // break for
920 case BRACKET_SYMBOLTYPE_NATNUM0
:
921 case BRACKET_SYMBOLTYPE_NATNUM1
:
922 case BRACKET_SYMBOLTYPE_NATNUM2
:
923 case BRACKET_SYMBOLTYPE_NATNUM3
:
924 case BRACKET_SYMBOLTYPE_NATNUM4
:
925 case BRACKET_SYMBOLTYPE_NATNUM5
:
926 case BRACKET_SYMBOLTYPE_NATNUM6
:
927 case BRACKET_SYMBOLTYPE_NATNUM7
:
928 case BRACKET_SYMBOLTYPE_NATNUM8
:
929 case BRACKET_SYMBOLTYPE_NATNUM9
:
930 case BRACKET_SYMBOLTYPE_NATNUM10
:
931 case BRACKET_SYMBOLTYPE_NATNUM11
:
932 case BRACKET_SYMBOLTYPE_NATNUM12
:
933 case BRACKET_SYMBOLTYPE_NATNUM13
:
934 case BRACKET_SYMBOLTYPE_NATNUM14
:
935 case BRACKET_SYMBOLTYPE_NATNUM15
:
936 case BRACKET_SYMBOLTYPE_NATNUM16
:
937 case BRACKET_SYMBOLTYPE_NATNUM17
:
938 case BRACKET_SYMBOLTYPE_NATNUM18
:
939 case BRACKET_SYMBOLTYPE_NATNUM19
:
940 if ( NumFor
[nIndex
].GetNatNum().IsSet() )
942 bCancel
= true; // break for
948 sal_Int32 nSpacePos
= sStr
.indexOf(' ');
951 sParams
= o3tl::trim(sStr
.subView(nSpacePos
+1));
953 //! eSymbolType is negative
954 sal_uInt8 nNum
= static_cast<sal_uInt8
>(0 - (eSymbolType
- BRACKET_SYMBOLTYPE_NATNUM0
));
955 if (!sParams
.isEmpty() && !NatNumTakesParameters(nNum
))
957 bCancel
= true; // break for
961 sStr
= "NatNum" + OUString::number(nNum
);
962 NumFor
[nIndex
].SetNatNumNum( nNum
, false );
963 // NatNum12 supports arguments
966 if (sParams
.isEmpty())
967 sParams
= "cardinal"; // default NatNum12 format is "cardinal"
968 else if (sParams
.indexOf("CURRENCY") >= 0)
969 sParams
= sParams
.replaceAll("CURRENCY",
970 rLoc().getCurrBankSymbol());
971 NumFor
[nIndex
].SetNatNumParams(sParams
);
972 sStr
+= " " + sParams
;
976 case BRACKET_SYMBOLTYPE_DBNUM1
:
977 case BRACKET_SYMBOLTYPE_DBNUM2
:
978 case BRACKET_SYMBOLTYPE_DBNUM3
:
979 case BRACKET_SYMBOLTYPE_DBNUM4
:
980 case BRACKET_SYMBOLTYPE_DBNUM5
:
981 case BRACKET_SYMBOLTYPE_DBNUM6
:
982 case BRACKET_SYMBOLTYPE_DBNUM7
:
983 case BRACKET_SYMBOLTYPE_DBNUM8
:
984 case BRACKET_SYMBOLTYPE_DBNUM9
:
985 if ( NumFor
[nIndex
].GetNatNum().IsSet() )
987 bCancel
= true; // break for
992 //! eSymbolType is negative
993 sal_uInt8 nNum
= static_cast<sal_uInt8
>(1 - (eSymbolType
- BRACKET_SYMBOLTYPE_DBNUM1
));
994 sStr
= "DBNum" + OUStringChar(sal_Unicode('0' + nNum
));
995 NumFor
[nIndex
].SetNatNumNum( nNum
, true );
998 case BRACKET_SYMBOLTYPE_LOCALE
:
999 if ( NumFor
[nIndex
].GetNatNum().GetLang() != LANGUAGE_DONTKNOW
||
1000 sBuff
[nPos
-1] != ']' )
1001 // Check also for ']' to avoid pulling in
1002 // locale data for the preview string for not
1003 // yet completed LCIDs in the dialog.
1005 bCancel
= true; // break for
1006 nCheckPos
= nPosOld
;
1011 LocaleType
aTmpLocale( ImpGetLocaleType( sStr
, nTmp
));
1012 if (aTmpLocale
.meLanguage
== LANGUAGE_DONTKNOW
)
1014 bCancel
= true; // break for
1015 nCheckPos
= nPosOld
;
1019 // Only the first sub format's locale will be
1020 // used as the format's overall locale.
1021 // Sorts this also under the corresponding
1022 // locale for the dialog.
1023 // If we don't support the locale this would
1024 // result in an unknown (empty) language
1025 // listbox entry and the user would never see
1027 if (nIndex
== 0 && (aTmpLocale
.meLanguage
== LANGUAGE_SYSTEM
||
1028 SvNumberFormatter::IsLocaleInstalled( aTmpLocale
.meLanguage
)))
1030 maLocale
= aTmpLocale
;
1031 eLan
= aTmpLocale
.meLanguage
; // return to caller
1033 // Set new target locale also at scanner.
1034 // We have to do this because switching locale
1035 // may make replacing keywords and separators
1037 // We can do this because it's the first
1038 // subformat and we're still at parsing the
1039 // modifiers, not keywords.
1040 rScan
.SetNewLnge( eLan
);
1041 // We can not force conversion though because
1042 // the caller may have explicitly not set it.
1043 // In the usual case the target locale is the
1044 // originating locale the conversion is not
1045 // necessary, when reading alien documents
1046 // conversion is enabled anyway.
1048 /* TODO: fiddle with scanner to make this
1049 * known? A change in the locale may affect
1050 * separators and keywords. On the other
1051 * hand they may have been entered as used
1052 * in the originating locale, there's no
1053 * way to predict other than analyzing the
1054 * format code, we assume here the current
1055 * context is used, which is most likely
1059 // Strip a plain locale identifier if locale
1060 // data is available to avoid duplicated
1061 // formats with and without LCID for the same
1062 // locale. Besides it looks ugly and confusing
1063 // and is unnecessary as the format will be
1064 // listed for the resulting locale.
1065 if (aTmpLocale
.isPlainLocale())
1068 sStr
= "$-" + aTmpLocale
.generateCode();
1073 // Locale data not available, remember.
1074 maLocale
.meLanguageWithoutLocaleData
= aTmpLocale
.meLanguage
;
1076 sStr
= "$-" + aTmpLocale
.generateCode();
1078 NumFor
[nIndex
].SetNatNumLang( MsLangId::getRealLanguage( aTmpLocale
.meLanguage
));
1080 // "$-NNCCLLLL" Numerals and Calendar
1081 if (sSymbol
.getLength() > 6)
1083 sInsertCalendar
= ImpObtainCalendarAndNumerals( sBuff
, nPos
, eLan
, aTmpLocale
);
1085 /* NOTE: there can be only one calendar
1086 * inserted so the last one wins, though
1087 * our own calendar modifiers support
1088 * multiple calendars within one sub format
1089 * code if at different positions. */
1096 if (sStr
== sSymbol
)
1102 sBuff
.remove(nPosOld
, nPos
- nPosOld
);
1103 if (!sStr
.isEmpty())
1105 sBuff
.insert(nPosOld
, "[" + sStr
+ "]");
1106 nPos
= nPosOld
+ sStr
.getLength() + 2;
1107 nPosOld
= nPos
; // position before string
1111 nPos
= nPosOld
; // prefix removed for whatever reason
1117 while ( !bCancel
&& lcl_SvNumberformat_IsBracketedPrefix( eSymbolType
) );
1119 // The remaining format code string
1122 if (eSymbolType
== BRACKET_SYMBOLTYPE_FORMAT
)
1124 if (nIndex
== 1 && eOp1
== NUMBERFORMAT_OP_NO
)
1126 eOp1
= NUMBERFORMAT_OP_GT
; // undefined condition, default: > 0
1128 else if (nIndex
== 2 && eOp2
== NUMBERFORMAT_OP_NO
)
1130 eOp2
= NUMBERFORMAT_OP_LT
; // undefined condition, default: < 0
1134 // Empty sub format.
1135 NumFor
[nIndex
].Info().eScannedType
= SvNumFormatType::EMPTY
;
1139 if (!sInsertCalendar
.isEmpty())
1141 sStr
= sInsertCalendar
+ sStr
;
1143 sal_Int32 nStrPos
= pSc
->ScanFormat( sStr
);
1144 sal_uInt16 nCnt
= pSc
->GetResultStringsCnt();
1145 if (nCnt
== 0 && nStrPos
== 0) // error
1149 if (nStrPos
== 0) // ok
1151 // e.g. Thai T speciality
1152 if (pSc
->GetNatNumModifier() && !NumFor
[nIndex
].GetNatNum().IsSet())
1154 sStr
= "[NatNum" + OUString::number( pSc
->GetNatNumModifier()) + "]" + sStr
;
1155 NumFor
[nIndex
].SetNatNumNum( pSc
->GetNatNumModifier(), false );
1157 // #i53826# #i42727# For the Thai T speciality we need
1158 // to freeze the locale and immunize it against
1159 // conversions during exports, just in case we want to
1160 // save to Xcl. This disables the feature of being able
1161 // to convert a NatNum to another locale. You can't
1163 // FIXME: implement a specialized export conversion
1164 // that works on tokens (have to tokenize all first)
1165 // and doesn't use the format string and
1166 // PutandConvertEntry() to LANGUAGE_ENGLISH_US in
1167 // sc/source/filter/excel/xestyle.cxx
1168 // XclExpNumFmtBuffer::WriteFormatRecord().
1169 LanguageType eLanguage
;
1170 if (NumFor
[nIndex
].GetNatNum().GetNatNum() == 1 &&
1171 ((eLanguage
= MsLangId::getRealLanguage( eLan
)) == LANGUAGE_THAI
) &&
1172 NumFor
[nIndex
].GetNatNum().GetLang() == LANGUAGE_DONTKNOW
)
1174 sStr
= "[$-" + OUString::number( sal_uInt16(eLanguage
), 16 ).toAsciiUpperCase() + "]" + sStr
;
1175 NumFor
[nIndex
].SetNatNumLang( eLanguage
);
1177 sBuff
.remove(nPosOld
, nPos
- nPosOld
);
1178 sBuff
.insert(nPosOld
, sStr
);
1179 nPos
= nPosOld
+ sStr
.getLength();
1180 if (nPos
< sBuff
.getLength())
1182 sBuff
.insert(nPos
, ";");
1185 else if (nIndex
> 0)
1187 // The last subformat. If it is a trailing text
1188 // format the omitted subformats act like they were
1189 // not specified and "inherited" the first format,
1190 // e.g. 0;@ behaves like 0;-0;0;@
1191 if (pSc
->GetScannedType() == SvNumFormatType::TEXT
)
1193 // Reset conditions, reverting any set above.
1195 eOp1
= NUMBERFORMAT_OP_NO
;
1196 else if (nIndex
== 2)
1197 eOp2
= NUMBERFORMAT_OP_NO
;
1201 NumFor
[nIndex
].Enlarge(nCnt
);
1202 pSc
->CopyInfo(&(NumFor
[nIndex
].Info()), nCnt
);
1206 if ( NumFor
[nIndex
].GetNatNum().GetNatNum() == 12 &&
1207 lcl_isNatNum12Currency(NumFor
[nIndex
].GetNatNum().GetParams()) )
1208 eType
= SvNumFormatType::CURRENCY
;
1210 eType
= NumFor
[nIndex
].Info().eScannedType
;
1212 else if (nIndex
== 3)
1213 { // #77026# Everything recognized IS text
1214 NumFor
[nIndex
].Info().eScannedType
= SvNumFormatType::TEXT
;
1216 else if ( NumFor
[nIndex
].Info().eScannedType
!= eType
)
1218 eType
= SvNumFormatType::DEFINED
;
1223 nCheckPos
= nPosOld
+ nStrPos
; // error in string
1224 bCancel
= true; // break for
1228 else if (eSymbolType
== BRACKET_SYMBOLTYPE_ERROR
) // error
1230 nCheckPos
= nPosOld
;
1233 else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType
) )
1235 nCheckPos
= nPosOld
+ 1; // error, prefix in string
1236 bCancel
= true; // break for
1239 if ( bCancel
&& !nCheckPos
)
1241 nCheckPos
= 1; // nCheckPos is used as an error condition
1245 if ( NumFor
[nIndex
].GetNatNum().IsSet() &&
1246 NumFor
[nIndex
].GetNatNum().GetLang() == LANGUAGE_DONTKNOW
)
1248 NumFor
[nIndex
].SetNatNumLang( eLan
);
1251 if (sBuff
.getLength() == nPos
)
1253 if (nIndex
< 3 && rString
[rString
.getLength()-1] == ';')
1255 // A trailing ';' is significant and specifies the following
1256 // subformat to be empty. We don't enter the scanning loop
1257 // above again though.
1258 // Note that the operators apply to the current last scanned
1260 if (nIndex
== 0 && eOp1
== NUMBERFORMAT_OP_NO
)
1262 eOp1
= NUMBERFORMAT_OP_GT
; // undefined condition, default: > 0
1264 else if (nIndex
== 1 && eOp2
== NUMBERFORMAT_OP_NO
)
1266 eOp2
= NUMBERFORMAT_OP_LT
; // undefined condition, default: < 0
1268 NumFor
[nIndex
+1].Info().eScannedType
= SvNumFormatType::EMPTY
;
1269 if (sBuff
[nPos
-1] != ';')
1270 sBuff
.insert( nPos
++, ';');
1272 if (nIndex
== 2 && eSymbolType
== BRACKET_SYMBOLTYPE_FORMAT
&& sBuff
[nPos
-1] == ';')
1274 // #83510# A 4th subformat explicitly specified to be empty
1275 // hides any text. Need the type here for HasTextFormat()
1276 NumFor
[3].Info().eScannedType
= SvNumFormatType::TEXT
;
1280 if ( NumFor
[nIndex
].GetNatNum().IsSet() )
1282 NumFor
[nIndex
].SetNatNumDate( bool(NumFor
[nIndex
].Info().eScannedType
& SvNumFormatType::DATE
) );
1286 if (!nCheckPos
&& IsSubstituted())
1288 // For to be substituted formats the scanned type must match the
1290 if (IsSystemTimeFormat())
1292 if ((eType
& ~SvNumFormatType::DEFINED
) != SvNumFormatType::TIME
)
1293 nCheckPos
= std::max
<sal_Int32
>( sBuff
.indexOf(']') + 1, 1);
1295 else if (IsSystemLongDateFormat())
1297 if ((eType
& ~SvNumFormatType::DEFINED
) != SvNumFormatType::DATE
)
1298 nCheckPos
= std::max
<sal_Int32
>( sBuff
.indexOf(']') + 1, 1);
1301 assert(!"unhandled substitute");
1304 if ( bCondition
&& !nCheckPos
)
1306 if ( nIndex
== 1 && NumFor
[0].GetCount() == 0 &&
1307 sBuff
[sBuff
.getLength() - 1] != ';' )
1309 // No format code => GENERAL but not if specified empty
1310 OUString
aAdd( pSc
->GetStandardName() );
1311 if ( !pSc
->ScanFormat( aAdd
) )
1313 sal_uInt16 nCnt
= pSc
->GetResultStringsCnt();
1316 NumFor
[0].Enlarge(nCnt
);
1317 pSc
->CopyInfo( &(NumFor
[0].Info()), nCnt
);
1322 else if ( nIndex
== 1 && NumFor
[nIndex
].GetCount() == 0 &&
1323 sBuff
[sBuff
.getLength() - 1] != ';' &&
1324 (NumFor
[0].GetCount() > 1 ||
1325 (NumFor
[0].GetCount() == 1 &&
1326 NumFor
[0].Info().nTypeArray
[0] != NF_KEY_GENERAL
)) )
1328 // No trailing second subformat => GENERAL but not if specified empty
1329 // and not if first subformat is GENERAL
1330 OUString
aAdd( pSc
->GetStandardName() );
1331 if ( !pSc
->ScanFormat( aAdd
) )
1333 sal_uInt16 nCnt
= pSc
->GetResultStringsCnt();
1336 NumFor
[nIndex
].Enlarge(nCnt
);
1337 pSc
->CopyInfo( &(NumFor
[nIndex
].Info()), nCnt
);
1338 sBuff
.append(";" + aAdd
);
1342 else if ( nIndex
== 2 && NumFor
[nIndex
].GetCount() == 0 &&
1343 sBuff
[sBuff
.getLength() - 1] != ';' &&
1344 eOp2
!= NUMBERFORMAT_OP_NO
)
1346 // No trailing third subformat => GENERAL but not if specified empty
1347 OUString
aAdd( pSc
->GetStandardName() );
1348 if ( !pSc
->ScanFormat( aAdd
) )
1350 sal_uInt16 nCnt
= pSc
->GetResultStringsCnt();
1353 NumFor
[nIndex
].Enlarge(nCnt
);
1354 pSc
->CopyInfo( &(NumFor
[nIndex
].Info()), nCnt
);
1355 sBuff
.append(";" + aAdd
);
1360 rString
= sBuff
.makeStringAndClear();
1361 sFormatstring
= rString
;
1363 if (NumFor
[2].GetCount() == 0 && // No third partial string
1364 eOp1
== NUMBERFORMAT_OP_GT
&& eOp2
== NUMBERFORMAT_OP_NO
&&
1365 fLimit1
== 0.0 && fLimit2
== 0.0)
1367 eOp1
= NUMBERFORMAT_OP_GE
; // Add 0 to the first format
1372 SvNumberformat::~SvNumberformat()
1379 * Splits up the symbols for further processing (by the Turing machine)
1381 * Start state = SsStart, * = Special state
1382 * ---------------+-------------------+----------------------------+---------------
1383 * Old State | Symbol read | Event | New state
1384 * ---------------+-------------------+----------------------------+---------------
1385 * SsStart | " | Symbol += Character | SsGetQuoted
1386 * | ; | Pos-- | SsGetString
1387 * | [ | Symbol += Character | SsGetBracketed
1388 * | ] | Error | SsStop
1390 * | Else | Symbol += Character | SsGetString
1391 * ---------------+-------------------+----------------------------+---------------
1392 * SsGetString | " | Symbol += Character | SsGetQuoted
1394 * | Else | Symbol += Character |
1395 * ---------------+-------------------+----------------------------+---------------
1396 * SsGetQuoted | " | Symbol += Character | SsGetString
1397 * | Else | Symbol += Character |
1398 * ---------------+-------------------+----------------------------+---------------
1399 * SsGetBracketed | <, > = | del [ |
1400 * | | Symbol += Character | SsGetCon
1402 * | h, H, m, M, s, S | Symbol += Character | SsGetTime
1404 * | | Symbol += Character | SsGetPrefix
1405 * ---------------+-------------------+----------------------------+---------------
1406 * SsGetTime | ] | Symbol += Character | SsGetString
1407 * | h, H, m, M, s, S | Symbol += Character, * | SsGetString
1408 * | Else | del [; Symbol += Character | SsGetPrefix
1409 * ---------------+-------------------+----------------------------+---------------
1410 * SsGetPrefix | ] | | SsStop
1411 * | Else | Symbol += Character |
1412 * ---------------+-------------------+----------------------------+---------------
1413 * SsGetCon | >, = | Symbol += Character |
1415 * | Else | Error | SsStop
1416 * ---------------+-------------------+----------------------------+---------------
1425 SsGetCon
, // condition
1426 SsGetString
, // format string
1427 SsGetPrefix
, // color or NatNumN
1428 SsGetTime
, // [HH] for time
1429 SsGetBracketed
, // any [...] not decided yet
1430 SsGetQuoted
// quoted text
1435 // read a string until ']' and delete spaces in input
1437 sal_Int32
SvNumberformat::ImpGetNumber(OUStringBuffer
& rString
,
1441 sal_Int32 nStartPos
= nPos
;
1443 sal_Int32 nLen
= rString
.getLength();
1444 OUStringBuffer sBuffSymbol
;
1445 while ( nPos
< nLen
)
1447 cToken
= rString
[nPos
];
1452 rString
.remove(nPos
,1);
1458 sBuffSymbol
.append(cToken
);
1461 sSymbol
= sBuffSymbol
.makeStringAndClear();
1462 return nPos
- nStartPos
;
1467 sal_Unicode
toUniChar(sal_uInt8 n
)
1470 return static_cast<sal_Unicode
>('0' + n
);
1472 return static_cast<sal_Unicode
>('A' + n
- 10);
1475 bool IsCombiningSymbol( OUStringBuffer
& rStringBuffer
, sal_Int32 nPos
)
1480 switch (rStringBuffer
[nPos
])
1497 OUString
SvNumberformat::LocaleType::generateCode() const
1499 OUStringBuffer aBuf
;
1501 // TODO: We may re-enable this later. Don't remove it! --Kohei
1504 sal_uInt8 nVal
= mnNumeralShape
;
1505 for (sal_uInt8 i
= 0; i
< 2; ++i
)
1507 sal_uInt8 n
= (nVal
& 0xF0) >> 4;
1508 if (n
|| aBuf
.getLength())
1510 aBuf
.append(toUniChar(n
));
1516 if (mnNumeralShape
|| mnCalendarType
)
1518 sal_uInt8 nVal
= mnCalendarType
;
1519 for (sal_uInt8 i
= 0; i
< 2; ++i
)
1521 sal_uInt8 n
= (nVal
& 0xF0) >> 4;
1522 if (n
|| aBuf
.getLength())
1524 aBuf
.append(toUniChar(n
));
1531 sal_uInt16 n16
= static_cast<sal_uInt16
>(
1532 (meLanguageWithoutLocaleData
== LANGUAGE_DONTKNOW
) ? meLanguage
:
1533 meLanguageWithoutLocaleData
);
1534 if (meLanguage
== LANGUAGE_SYSTEM
)
1536 switch (meSubstitute
)
1538 case Substitute::NONE
:
1541 case Substitute::TIME
:
1542 n16
= static_cast<sal_uInt16
>(LANGUAGE_NF_SYSTEM_TIME
);
1544 case Substitute::LONGDATE
:
1545 n16
= static_cast<sal_uInt16
>(LANGUAGE_NF_SYSTEM_DATE
);
1549 for (sal_uInt8 i
= 0; i
< 4; ++i
)
1551 sal_uInt8 n
= static_cast<sal_uInt8
>((n16
& 0xF000) >> 12);
1552 // Omit leading zeros for consistency.
1553 if (n
|| !aBuf
.isEmpty() || i
== 3)
1555 aBuf
.append(toUniChar(n
));
1557 n16
= (n16
<< 4) & 0xFFFF;
1560 return aBuf
.makeStringAndClear();
1563 SvNumberformat::LocaleType::LocaleType()
1564 : meLanguage(LANGUAGE_DONTKNOW
)
1565 , meLanguageWithoutLocaleData(LANGUAGE_DONTKNOW
)
1566 , meSubstitute(Substitute::NONE
)
1572 SvNumberformat::LocaleType::LocaleType(sal_uInt32 nRawNum
)
1573 : meLanguage(LANGUAGE_DONTKNOW
)
1574 , meLanguageWithoutLocaleData(LANGUAGE_DONTKNOW
)
1575 , meSubstitute(Substitute::NONE
)
1579 meLanguage
= static_cast<LanguageType
>(nRawNum
& 0x0000FFFF);
1580 if (meLanguage
== LANGUAGE_NF_SYSTEM_TIME
)
1582 meSubstitute
= Substitute::TIME
;
1583 meLanguage
= LANGUAGE_SYSTEM
;
1585 else if (meLanguage
== LANGUAGE_NF_SYSTEM_DATE
)
1587 meSubstitute
= Substitute::LONGDATE
;
1588 meLanguage
= LANGUAGE_SYSTEM
;
1590 nRawNum
= (nRawNum
>> 16);
1591 mnCalendarType
= static_cast<sal_uInt8
>(nRawNum
& 0xFF);
1592 nRawNum
= (nRawNum
>> 8);
1593 mnNumeralShape
= static_cast<sal_uInt8
>(nRawNum
& 0xFF);
1596 bool SvNumberformat::LocaleType::isPlainLocale() const
1598 return meSubstitute
== Substitute::NONE
&& !mnCalendarType
&& !mnNumeralShape
;
1602 SvNumberformat::LocaleType
SvNumberformat::ImpGetLocaleType(std::u16string_view rString
, sal_Int32
& nPos
)
1604 sal_uInt32 nNum
= 0;
1605 sal_Unicode cToken
= 0;
1606 sal_Int32 nStart
= nPos
;
1607 sal_Int32 nLen
= rString
.size();
1608 while ( nPos
< nLen
&& (nPos
- nStart
< 8) )
1610 cToken
= rString
[nPos
];
1613 if ( '0' <= cToken
&& cToken
<= '9' )
1616 nNum
+= cToken
- '0';
1618 else if ( 'a' <= cToken
&& cToken
<= 'f' )
1621 nNum
+= cToken
- 'a' + 10;
1623 else if ( 'A' <= cToken
&& cToken
<= 'F' )
1626 nNum
+= cToken
- 'A' + 10;
1630 return LocaleType(); // LANGUAGE_DONTKNOW;
1635 return (cToken
== ']' || nPos
== nLen
) ? LocaleType(nNum
) : LocaleType();
1638 static bool lcl_matchKeywordAndGetNumber( std::u16string_view rString
, const sal_Int32 nPos
,
1639 std::u16string_view rKeyword
, sal_Int32
& nNumber
)
1641 if (0 <= nPos
&& nPos
+ static_cast<sal_Int32
>(rKeyword
.size()) < static_cast<sal_Int32
>(rString
.size()) && o3tl::matchIgnoreAsciiCase( rString
, rKeyword
, nPos
))
1643 nNumber
= o3tl::toInt32(rString
.substr( nPos
+ rKeyword
.size()));
1653 short SvNumberformat::ImpNextSymbol(OUStringBuffer
& rString
,
1655 OUString
& sSymbol
) const
1657 short eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1659 sal_Unicode cLetter
= ' '; // Preliminary result
1660 sal_Int32 nLen
= rString
.getLength();
1661 ScanState eState
= SsStart
;
1662 OUStringBuffer
sBuffSymbol(128);
1664 const NfKeywordTable
& rKeywords
= rScan
.GetKeywords();
1665 while (nPos
< nLen
&& eState
!= SsStop
)
1667 cToken
= rString
[nPos
];
1674 eState
= SsGetQuoted
;
1675 sBuffSymbol
.append(cToken
);
1677 else if (cToken
== '[')
1679 eState
= SsGetBracketed
;
1680 sBuffSymbol
.append(cToken
);
1682 else if (cToken
== ';')
1684 eState
= SsGetString
;
1686 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1688 else if (cToken
== ']')
1691 eSymbolType
= BRACKET_SYMBOLTYPE_ERROR
;
1693 else if (cToken
== ' ') // Skip Blanks
1696 rString
.remove(nPos
, 1);
1701 sBuffSymbol
.append(cToken
);
1702 eState
= SsGetString
;
1703 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1706 case SsGetBracketed
:
1712 sBuffSymbol
.stripStart('[');
1713 sBuffSymbol
.append(cToken
);
1719 eSymbolType
= NUMBERFORMAT_OP_LT
;
1722 eSymbolType
= NUMBERFORMAT_OP_GT
;
1725 eSymbolType
= NUMBERFORMAT_OP_EQ
;
1731 rString
.remove(nPos
, 1);
1735 if ( nPos
< nLen
&& rString
[nPos
] == '-' )
1738 sBuffSymbol
.stripStart('[');
1739 eSymbolType
= BRACKET_SYMBOLTYPE_LOCALE
;
1740 eState
= SsGetPrefix
;
1744 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1745 eState
= SsGetString
;
1747 sBuffSymbol
.append(cToken
);
1751 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1752 sBuffSymbol
.append(cToken
);
1753 eState
= SsGetString
;
1757 static constexpr OUString
aNatNum(u
"NATNUM"_ustr
);
1758 static constexpr OUString
aDBNum(u
"DBNUM"_ustr
);
1759 const OUString
aBufStr( rString
.toString());
1760 sal_Int32 nNatNumNum
;
1762 if ( lcl_matchKeywordAndGetNumber( aBufStr
, nPos
-1, aNatNum
, nNatNumNum
) &&
1763 0 <= nNatNumNum
&& nNatNumNum
<= 19 )
1765 sBuffSymbol
.stripStart('[');
1766 sBuffSymbol
.append( aBufStr
.subView(--nPos
, aNatNum
.getLength()+1) );
1767 nPos
+= aNatNum
.getLength()+1;
1768 //! SymbolType is negative
1769 eSymbolType
= static_cast<short>(BRACKET_SYMBOLTYPE_NATNUM0
- nNatNumNum
);
1770 eState
= SsGetPrefix
;
1772 else if ( lcl_matchKeywordAndGetNumber( aBufStr
, nPos
-1, aDBNum
, nDBNum
) &&
1773 1 <= nDBNum
&& nDBNum
<= 9 )
1775 sBuffSymbol
.stripStart('[');
1776 sBuffSymbol
.append( aBufStr
.subView(--nPos
, aDBNum
.getLength()+1) );
1777 nPos
+= aDBNum
.getLength()+1;
1778 //! SymbolType is negative
1779 eSymbolType
= sal::static_int_cast
< short >( BRACKET_SYMBOLTYPE_DBNUM1
- (nDBNum
- 1) );
1780 eState
= SsGetPrefix
;
1784 sal_Unicode cUpper
= rChrCls().uppercase( aBufStr
, nPos
-1, 1)[0];
1785 if ( cUpper
== rKeywords
[NF_KEY_H
][0] || // H
1786 cUpper
== rKeywords
[NF_KEY_MI
][0] || // M
1787 cUpper
== rKeywords
[NF_KEY_S
][0] ) // S
1789 sBuffSymbol
.append(cToken
);
1795 sBuffSymbol
.stripStart('[');
1796 sBuffSymbol
.append(cToken
);
1797 eSymbolType
= BRACKET_SYMBOLTYPE_COLOR
;
1798 eState
= SsGetPrefix
;
1807 eState
= SsGetQuoted
;
1808 sBuffSymbol
.append(cToken
);
1810 else if (cToken
== ';' && (nPos
< 2 || !IsCombiningSymbol( rString
, nPos
-2)))
1816 sBuffSymbol
.append(cToken
);
1822 eState
= SsGetString
;
1823 sBuffSymbol
.append(cToken
);
1827 sBuffSymbol
.append(cToken
);
1833 sBuffSymbol
.append(cToken
);
1834 eState
= SsGetString
;
1835 eSymbolType
= BRACKET_SYMBOLTYPE_FORMAT
;
1839 sal_Unicode cUpper
= rChrCls().uppercase(rString
.toString(), nPos
-1, 1)[0];
1840 if (cUpper
== rKeywords
[NF_KEY_H
][0] || // H
1841 cUpper
== rKeywords
[NF_KEY_MI
][0] || // M
1842 cUpper
== rKeywords
[NF_KEY_S
][0] ) // S
1844 if (cLetter
== cToken
)
1846 sBuffSymbol
.append(cToken
);
1851 sBuffSymbol
.stripStart('[');
1852 sBuffSymbol
.append(cToken
);
1853 eState
= SsGetPrefix
;
1858 sBuffSymbol
.stripStart('[');
1859 sBuffSymbol
.append(cToken
);
1860 eSymbolType
= BRACKET_SYMBOLTYPE_COLOR
;
1861 eState
= SsGetPrefix
;
1870 eSymbolType
= BRACKET_SYMBOLTYPE_ERROR
;
1875 sBuffSymbol
.append(cToken
);
1878 eSymbolType
= NUMBERFORMAT_OP_NE
;
1883 eSymbolType
= BRACKET_SYMBOLTYPE_ERROR
;
1889 sBuffSymbol
.append(cToken
);
1891 eSymbolType
= NUMBERFORMAT_OP_LE
;
1893 else if (cLetter
== '>')
1895 sBuffSymbol
.append(cToken
);
1897 eSymbolType
= NUMBERFORMAT_OP_GE
;
1902 eSymbolType
= BRACKET_SYMBOLTYPE_ERROR
;
1907 rString
.remove(nPos
,1);
1923 sBuffSymbol
.append(cToken
);
1930 sSymbol
= sBuffSymbol
.makeStringAndClear();
1934 void SvNumberformat::ConvertLanguage( SvNumberFormatter
& rConverter
,
1935 LanguageType eConvertFrom
,
1936 LanguageType eConvertTo
)
1938 sal_Int32 nCheckPos
;
1940 SvNumFormatType nType
= eType
;
1941 OUString
aFormatString( sFormatstring
);
1942 rConverter
.PutandConvertEntry( aFormatString
, nCheckPos
, nType
,
1943 nKey
, eConvertFrom
, eConvertTo
, false);
1944 const SvNumberformat
* pFormat
= rConverter
.GetEntry( nKey
);
1945 DBG_ASSERT( pFormat
, "SvNumberformat::ConvertLanguage: Conversion without format" );
1948 ImpCopyNumberformat( *pFormat
);
1949 // Reset values taken over from Formatter/Scanner
1950 // pColor still points to table in temporary Formatter/Scanner
1951 for (ImpSvNumFor
& rFormatter
: NumFor
)
1953 OUString aColorName
= rFormatter
.GetColorName();
1954 const Color
* pColor
= rScan
.GetColor( aColorName
);
1955 rFormatter
.SetColor( pColor
, aColorName
);
1960 bool SvNumberformat::HasNewCurrency() const
1962 for (const auto & j
: NumFor
)
1964 if ( j
.HasNewCurrency() )
1972 bool SvNumberformat::GetNewCurrencySymbol( OUString
& rSymbol
,
1973 OUString
& rExtension
) const
1975 for (const auto & j
: NumFor
)
1977 if ( j
.GetNewCurrencySymbol( rSymbol
, rExtension
) )
1988 OUString
SvNumberformat::StripNewCurrencyDelimiters( const OUString
& rStr
)
1990 OUStringBuffer
aTmp(rStr
.getLength());
1991 sal_Int32 nStartPos
, nPos
, nLen
;
1992 nLen
= rStr
.getLength();
1994 while ( (nPos
= rStr
.indexOf( "[$", nStartPos
)) >= 0 )
1997 if ( (nEnd
= GetQuoteEnd( rStr
, nPos
)) >= 0 )
1999 aTmp
.append(rStr
.subView( nStartPos
, ++nEnd
- nStartPos
));
2004 aTmp
.append(rStr
.subView(nStartPos
, nPos
- nStartPos
) );
2005 nStartPos
= nPos
+ 2;
2007 nEnd
= nStartPos
- 1;
2010 nDash
= rStr
.indexOf( '-', ++nEnd
);
2011 nEnd
= GetQuoteEnd( rStr
, nDash
);
2013 while ( nEnd
>= 0 );
2015 nEnd
= nStartPos
- 1;
2018 nClose
= rStr
.indexOf( ']', ++nEnd
);
2019 nEnd
= GetQuoteEnd( rStr
, nClose
);
2021 while ( nEnd
>= 0 );
2025 /* there should always be a closing ]
2026 * but the old String class would have hidden
2027 * that. so be conservative too
2033 if(nDash
>= 0 && nDash
< nClose
)
2037 aTmp
.append(rStr
.subView(nStartPos
, nPos
- nStartPos
) );
2038 nStartPos
= nClose
+ 1;
2041 if ( nLen
> nStartPos
)
2043 aTmp
.append(rStr
.subView(nStartPos
, nLen
- nStartPos
) );
2045 return aTmp
.makeStringAndClear();
2048 void SvNumberformat::ImpGetOutputStandard(double& fNumber
, OUStringBuffer
& rOutString
,
2049 const NativeNumberWrapper
& rNatNum
) const
2052 ImpGetOutputStandard(fNumber
, sTemp
, rNatNum
);
2056 void SvNumberformat::ImpGetOutputStandard(double& fNumber
, OUString
& rOutString
,
2057 const NativeNumberWrapper
& rNatNum
) const
2059 sal_uInt16 nStandardPrec
= rScan
.GetStandardPrec();
2061 if ( fabs(fNumber
) > EXP_ABS_UPPER_BOUND
)
2063 nStandardPrec
= ::std::min(nStandardPrec
, static_cast<sal_uInt16
>(14)); // limits to 14 decimals
2064 rOutString
= ::rtl::math::doubleToUString( fNumber
,
2065 rtl_math_StringFormat_E2
, nStandardPrec
/*2*/,
2066 GetCurrentLanguageData().GetNumDecimalSep()[0]);
2070 ImpGetOutputStdToPrecision(fNumber
, rOutString
, nStandardPrec
, rNatNum
);
2077 template<typename T
>
2078 bool checkForAll0s(const T
& rString
, sal_Int32 nIdx
=0)
2080 if (nIdx
>=rString
.getLength())
2085 if (rString
[nIdx
]!='0')
2088 while (++nIdx
<rString
.getLength());
2093 OUString
impTransliterateImpl(const OUString
& rStr
,
2094 const SvNumberNatNum
& rNum
,
2095 const NativeNumberWrapper
& rNatNum
)
2097 css::lang::Locale
aLocale( LanguageTag( rNum
.GetLang() ).getLocale() );
2098 return rNatNum
.getNativeNumberStringParams(rStr
, aLocale
, rNum
.GetNatNum(), rNum
.GetParams());
2101 void impTransliterateImpl(OUStringBuffer
& rStr
,
2102 const SvNumberNatNum
& rNum
,
2103 const NativeNumberWrapper
& rNatNum
)
2105 css::lang::Locale
aLocale( LanguageTag( rNum
.GetLang() ).getLocale() );
2107 rStr
= rNatNum
.getNativeNumberStringParams(
2108 OUString::unacquired(rStr
), aLocale
, rNum
.GetNatNum(), rNum
.GetParams());
2111 OUString
impTransliterate(const OUString
& rStr
, const SvNumberNatNum
& rNum
, const NativeNumberWrapper
& rNatNum
)
2113 return rNum
.IsComplete() ? impTransliterateImpl(rStr
, rNum
, rNatNum
) : rStr
;
2116 void impTransliterate(OUStringBuffer
& rStr
, const SvNumberNatNum
& rNum
, const NativeNumberWrapper
& rNatNum
)
2118 if(rNum
.IsComplete())
2120 impTransliterateImpl(rStr
, rNum
, rNatNum
);
2126 void SvNumberformat::ImpGetOutputStdToPrecision(double& rNumber
, OUString
& rOutString
, sal_uInt16 nPrecision
,
2127 const NativeNumberWrapper
& rNatNum
) const
2129 // Make sure the precision doesn't go over the maximum allowable precision.
2130 nPrecision
= ::std::min(UPPER_PRECISION
, nPrecision
);
2132 // We decided to strip trailing zeros unconditionally, since binary
2133 // double-precision rounding error makes it impossible to determine e.g.
2134 // whether 844.10000000000002273737 is what the user has typed, or the
2135 // user has typed 844.1 but IEEE 754 represents it that way internally.
2137 rOutString
= ::rtl::math::doubleToUString( rNumber
,
2138 rtl_math_StringFormat_F
, nPrecision
/*2*/,
2139 GetCurrentLanguageData().GetNumDecimalSep()[0], true );
2140 if (rOutString
[0] == '-' && checkForAll0s(rOutString
, 1))
2142 rOutString
= comphelper::string::stripStart(rOutString
, '-'); // not -0
2144 rOutString
= ::impTransliterate(rOutString
, NumFor
[0].GetNatNum(), rNatNum
);
2147 void SvNumberformat::ImpGetOutputInputLine(double fNumber
, OUString
& OutString
) const
2149 bool bModified
= false;
2150 if ( (eType
& SvNumFormatType::PERCENT
) && (fabs(fNumber
) < D_MAX_D_BY_100
))
2167 OutString
= ::rtl::math::doubleToUString( fNumber
,
2168 rtl_math_StringFormat_Automatic
,
2169 rtl_math_DecimalPlaces_Max
,
2170 GetCurrentLanguageData().GetNumDecimalSep()[0], true );
2172 if ( eType
& SvNumFormatType::PERCENT
&& bModified
)
2178 short SvNumberformat::ImpCheckCondition(double fNumber
,
2180 SvNumberformatLimitOps eOp
)
2184 case NUMBERFORMAT_OP_NO
:
2186 case NUMBERFORMAT_OP_EQ
:
2187 return static_cast<short>(fNumber
== fLimit
);
2188 case NUMBERFORMAT_OP_NE
:
2189 return static_cast<short>(fNumber
!= fLimit
);
2190 case NUMBERFORMAT_OP_LT
:
2191 return static_cast<short>(fNumber
< fLimit
);
2192 case NUMBERFORMAT_OP_LE
:
2193 return static_cast<short>(fNumber
<= fLimit
);
2194 case NUMBERFORMAT_OP_GT
:
2195 return static_cast<short>(fNumber
> fLimit
);
2196 case NUMBERFORMAT_OP_GE
:
2197 return static_cast<short>(fNumber
>= fLimit
);
2203 static bool lcl_appendStarFillChar( OUStringBuffer
& rBuf
, std::u16string_view rStr
)
2205 // Right during user input the star symbol is the very
2206 // last character before the user enters another one.
2207 if (rStr
.size() > 1)
2209 rBuf
.append(u
'\x001B');
2210 rBuf
.append(rStr
[1]);
2216 static bool lcl_insertStarFillChar( OUStringBuffer
& rBuf
, sal_Int32 nPos
, std::u16string_view rStr
)
2218 if (rStr
.size() > 1)
2220 rBuf
.insert( nPos
, rStr
[1]);
2221 rBuf
.insert( nPos
, u
'\x001B');
2227 void SvNumberformat::GetOutputString(std::u16string_view sString
,
2228 OUString
& OutString
,
2229 const Color
** ppColor
,
2230 bool bStarFlag
) const
2232 OUStringBuffer sOutBuff
;
2234 if (eType
& SvNumFormatType::TEXT
)
2238 else if (NumFor
[3].GetCount() > 0)
2244 *ppColor
= nullptr; // no change of color
2247 *ppColor
= NumFor
[nIx
].GetColor();
2248 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
2249 if (rInfo
.eScannedType
== SvNumFormatType::TEXT
)
2251 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
2252 for (sal_uInt16 i
= 0; i
< nCnt
; i
++)
2254 switch (rInfo
.nTypeArray
[i
])
2256 case NF_SYMBOLTYPE_STAR
:
2259 lcl_appendStarFillChar( sOutBuff
, rInfo
.sStrArray
[i
]);
2262 case NF_SYMBOLTYPE_BLANK
:
2263 if (rInfo
.sStrArray
[i
].getLength() >= 2)
2264 InsertBlanks( sOutBuff
, sOutBuff
.getLength(), rInfo
.sStrArray
[i
][1] );
2266 case NF_KEY_GENERAL
: // #77026# "General" is the same as "@"
2267 case NF_SYMBOLTYPE_DEL
:
2268 sOutBuff
.append(sString
);
2271 sOutBuff
.append(rInfo
.sStrArray
[i
]);
2275 OutString
= sOutBuff
.makeStringAndClear();
2280 void lcl_GetOutputStringScientific(double fNumber
, sal_uInt16 nCharCount
,
2281 const SvNFLanguageData
& rLanguageData
, OUString
& rOutString
)
2283 bool bSign
= std::signbit(fNumber
);
2285 // 1.000E+015 (one digit and the decimal point, and the two chars +
2286 // nExpDigit for the exponential part, totalling 6 or 7).
2287 double fExp
= log10( fabs(fNumber
) );
2290 sal_uInt16 nCharFormat
= 6 + (fExp
>= 100.0 ? 1 : 0);
2291 sal_uInt16 nPrec
= nCharCount
> nCharFormat
? nCharCount
- nCharFormat
: 0;
2294 // Make room for the negative sign.
2297 nPrec
= ::std::min(nPrec
, static_cast<sal_uInt16
>(14)); // limit to 14 decimals.
2299 rOutString
= ::rtl::math::doubleToUString(fNumber
, rtl_math_StringFormat_E2
,
2300 nPrec
, rLanguageData
.GetNumDecimalSep()[0], true );
2303 OUString
lcl_GetPercentString(const ImpSvNumberformatInfo
&rInfo
, sal_uInt16 nCnt
)
2306 OUStringBuffer aPercentString
;
2307 for( i
= 0; i
< nCnt
; i
++ )
2309 if( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_PERCENT
)
2311 aPercentString
.append( rInfo
.sStrArray
[i
] );
2312 bool bStringFound
= false;
2313 for( i
--; i
>= 0 && rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
; i
-- )
2317 bStringFound
= true;
2318 aPercentString
.insert( 0, "\"" );
2320 aPercentString
.insert( 0, rInfo
.sStrArray
[i
] );
2324 aPercentString
.insert( 0, "\"" );
2327 return aPercentString
.makeStringAndClear();
2330 OUString
lcl_GetDenominatorString(const ImpSvNumberformatInfo
&rInfo
, sal_uInt16 nCnt
)
2333 OUStringBuffer aDenominatorString
;
2334 for( i
= 0; i
< nCnt
; i
++ )
2336 if( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_FRAC
)
2338 while ( ( ++i
< nCnt
) && rInfo
.nTypeArray
[i
] != NF_SYMBOLTYPE_FRAC_FDIV
2339 && rInfo
.nTypeArray
[i
] != NF_SYMBOLTYPE_DIGIT
);
2340 for( ; i
< nCnt
; i
++ )
2342 if( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_FRAC_FDIV
|| rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_DIGIT
)
2343 aDenominatorString
.append( rInfo
.sStrArray
[i
] );
2349 return aDenominatorString
.makeStringAndClear();
2352 OUString
lcl_GetNumeratorString(const ImpSvNumberformatInfo
&rInfo
, sal_uInt16 nCnt
)
2355 OUStringBuffer aNumeratorString
;
2356 for( i
= 0; i
< nCnt
; i
++ )
2358 if( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_FRAC
)
2360 for( i
--; i
>= 0 && rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_DIGIT
; i
-- )
2362 aNumeratorString
.insert( 0, rInfo
.sStrArray
[i
] );
2367 return aNumeratorString
.makeStringAndClear();
2370 OUString
lcl_GetFractionIntegerString(const ImpSvNumberformatInfo
&rInfo
, sal_uInt16 nCnt
)
2373 OUStringBuffer aIntegerString
;
2374 for( i
= 0; i
< nCnt
; i
++ )
2376 if( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_FRACBLANK
)
2378 for( i
--; i
>= 0 && ( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_DIGIT
2379 || rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_THSEP
); i
-- )
2381 aIntegerString
.insert( 0, rInfo
.sStrArray
[i
] );
2386 return aIntegerString
.makeStringAndClear();
2389 OUString
lcl_GetIntegerFractionDelimiterString(const ImpSvNumberformatInfo
&rInfo
, sal_uInt16 nCnt
)
2392 for( i
= 0; i
< nCnt
; i
++ )
2394 if( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_FRACBLANK
)
2396 return rInfo
.sStrArray
[i
];
2404 OUString
SvNumberformat::GetPercentString( sal_uInt16 nNumFor
) const
2406 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nNumFor
].Info();
2407 sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
2408 return lcl_GetPercentString( rInfo
, nCnt
);
2411 OUString
SvNumberformat::GetDenominatorString( sal_uInt16 nNumFor
) const
2413 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nNumFor
].Info();
2414 sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
2415 return lcl_GetDenominatorString( rInfo
, nCnt
);
2418 OUString
SvNumberformat::GetNumeratorString( sal_uInt16 nNumFor
) const
2420 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nNumFor
].Info();
2421 sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
2422 return lcl_GetNumeratorString( rInfo
, nCnt
);
2425 OUString
SvNumberformat::GetIntegerFractionDelimiterString( sal_uInt16 nNumFor
) const
2427 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nNumFor
].Info();
2428 sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
2429 return lcl_GetIntegerFractionDelimiterString( rInfo
, nCnt
);
2432 bool SvNumberformat::GetOutputString(double fNumber
, sal_uInt16 nCharCount
, OUString
& rOutString
, const NativeNumberWrapper
& rNatNum
) const
2434 if (eType
!= SvNumFormatType::NUMBER
)
2438 double fTestNum
= fNumber
;
2439 bool bSign
= std::signbit(fTestNum
);
2442 fTestNum
= -fTestNum
;
2444 if (fTestNum
< EXP_LOWER_BOUND
)
2446 lcl_GetOutputStringScientific(fNumber
, nCharCount
, GetCurrentLanguageData(), rOutString
);
2450 double fExp
= log10(fTestNum
);
2451 // Values < 1.0 always have one digit before the decimal point.
2452 sal_uInt16 nDigitPre
= fExp
>= 0.0 ? static_cast<sal_uInt16
>(ceil(fExp
)) : 1;
2456 lcl_GetOutputStringScientific(fNumber
, nCharCount
, GetCurrentLanguageData(), rOutString
);
2460 sal_uInt16 nPrec
= nCharCount
>= nDigitPre
? nCharCount
- nDigitPre
: 0;
2463 // Subtract the negative sign.
2468 // Subtract the decimal point.
2471 ImpGetOutputStdToPrecision(fNumber
, rOutString
, nPrec
, rNatNum
);
2472 if (rOutString
.getLength() > nCharCount
)
2474 // String still wider than desired. Switch to scientific notation.
2475 lcl_GetOutputStringScientific(fNumber
, nCharCount
, GetCurrentLanguageData(), rOutString
);
2480 sal_uInt16
SvNumberformat::GetSubformatIndex (double fNumber
) const
2482 sal_uInt16 nIx
; // Index of the partial format
2483 double fLimit_1
= fLimit1
;
2484 short nCheck
= ImpCheckCondition(fNumber
, fLimit_1
, eOp1
);
2485 if (nCheck
== -1 || nCheck
== 1) // Only 1 String or True
2491 double fLimit_2
= fLimit2
;
2492 nCheck
= ImpCheckCondition(fNumber
, fLimit_2
, eOp2
);
2493 if (nCheck
== -1 || nCheck
== 1)
2505 bool SvNumberformat::GetOutputString(double fNumber
,
2506 OUString
& OutString
,
2507 const Color
** ppColor
,
2508 const NativeNumberWrapper
& rNatNum
,
2509 const SvNFLanguageData
& rCurrentLang
,
2510 bool bStarFlag
) const
2514 *ppColor
= nullptr; // No color change
2515 if (eType
& SvNumFormatType::LOGICAL
&& sFormatstring
== rScan
.GetKeywords()[NF_KEY_BOOLEAN
])
2519 OutString
= rScan
.GetTrueString();
2523 OutString
= rScan
.GetFalseString();
2527 OUStringBuffer
sBuff(64);
2528 if (eType
& SvNumFormatType::TEXT
)
2530 ImpGetOutputStandard(fNumber
, sBuff
, rNatNum
);
2531 OutString
= sBuff
.makeStringAndClear();
2534 bool bHadStandard
= false;
2535 if (bStandard
) // Individual standard formats
2537 if (rScan
.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION
) // All number format InputLine
2539 ImpGetOutputInputLine(fNumber
, OutString
);
2544 case SvNumFormatType::NUMBER
: // Standard number format
2545 if (rScan
.GetStandardPrec() == SvNumberFormatter::UNLIMITED_PRECISION
)
2547 if (std::signbit(fNumber
))
2549 if (!(fNumber
< 0.0))
2550 fNumber
= -fNumber
; // do not display -0.0
2556 else if (fNumber
< 1.0 && fNumber
> -1.0)
2558 // Decide whether to display as 0.000000123... or 1.23...e-07
2559 bool bFix
= (fNumber
< -EXP_LOWER_BOUND
|| EXP_LOWER_BOUND
< fNumber
);
2562 // Arbitrary, not too many 0s visually, start E2 at 1E-10.
2563 constexpr sal_Int32 kMaxExp
= 9;
2564 const sal_Int32 nExp
= static_cast<sal_Int32
>(ceil( -log10( fabs( fNumber
))));
2565 if (nExp
<= kMaxExp
&& rtl::math::approxEqual(
2566 rtl::math::round( fNumber
, 16), rtl::math::round( fNumber
, nExp
+ 16)))
2568 // Not too many significant digits or accuracy
2569 // artefacts, otherwise leave everything to E2
2575 OutString
= ::rtl::math::doubleToUString( fNumber
,
2576 rtl_math_StringFormat_F
,
2577 rtl_math_DecimalPlaces_Max
,
2578 GetCurrentLanguageData().GetNumDecimalSep()[0], true);
2580 OutString
= ::rtl::math::doubleToUString( fNumber
,
2581 rtl_math_StringFormat_E2
,
2582 rtl_math_DecimalPlaces_Max
,
2583 GetCurrentLanguageData().GetNumDecimalSep()[0], true);
2587 OutString
= ::rtl::math::doubleToUString( fNumber
,
2588 rtl_math_StringFormat_Automatic
,
2589 rtl_math_DecimalPlaces_Max
,
2590 GetCurrentLanguageData().GetNumDecimalSep()[0], true);
2594 ImpGetOutputStandard(fNumber
, sBuff
, rNatNum
);
2595 bHadStandard
= true;
2597 case SvNumFormatType::DATE
:
2598 bRes
|= ImpGetDateOutput(fNumber
, 0, bStarFlag
, rNatNum
, rCurrentLang
, sBuff
);
2599 bHadStandard
= true;
2601 case SvNumFormatType::TIME
:
2602 bRes
|= ImpGetTimeOutput(fNumber
, 0, bStarFlag
, rNatNum
, rCurrentLang
, sBuff
);
2603 bHadStandard
= true;
2605 case SvNumFormatType::DATETIME
:
2606 bRes
|= ImpGetDateTimeOutput(fNumber
, 0, bStarFlag
, rNatNum
, rCurrentLang
, sBuff
);
2607 bHadStandard
= true;
2612 if ( !bHadStandard
)
2614 sal_uInt16 nIx
= GetSubformatIndex ( fNumber
); // Index of the partial format
2615 if (fNumber
< 0.0 &&
2616 ((nIx
== 0 && IsFirstSubformatRealNegative()) || // 1st, usually positive subformat
2617 (nIx
== 1 && IsSecondSubformatRealNegative()))) // 2nd, usually negative subformat
2619 fNumber
= -fNumber
; // eliminate sign
2621 *ppColor
= NumFor
[nIx
].GetColor();
2622 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
2623 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
2624 if (nCnt
== 0 && rInfo
.eScannedType
== SvNumFormatType::EMPTY
)
2626 return false; // Empty => nothing
2628 else if (nCnt
== 0) // Else Standard Format
2630 ImpGetOutputStandard(fNumber
, sBuff
, rNatNum
);
2631 OutString
= sBuff
.makeStringAndClear();
2634 switch (rInfo
.eScannedType
)
2636 case SvNumFormatType::TEXT
:
2637 case SvNumFormatType::DEFINED
:
2638 for (sal_uInt16 i
= 0; i
< nCnt
; i
++)
2640 switch (rInfo
.nTypeArray
[i
])
2642 case NF_SYMBOLTYPE_STAR
:
2645 bRes
= lcl_appendStarFillChar( sBuff
, rInfo
.sStrArray
[i
]);
2648 case NF_SYMBOLTYPE_BLANK
:
2649 if (rInfo
.sStrArray
[i
].getLength() >= 2)
2650 InsertBlanks(sBuff
, sBuff
.getLength(), rInfo
.sStrArray
[i
][1] );
2652 case NF_SYMBOLTYPE_STRING
:
2653 case NF_SYMBOLTYPE_CURRENCY
:
2654 sBuff
.append(rInfo
.sStrArray
[i
]);
2656 case NF_SYMBOLTYPE_THSEP
:
2657 if (rInfo
.nThousand
== 0)
2659 sBuff
.append(rInfo
.sStrArray
[i
]);
2667 case SvNumFormatType::DATE
:
2668 bRes
|= ImpGetDateOutput(fNumber
, nIx
, bStarFlag
, rNatNum
, rCurrentLang
, sBuff
);
2670 case SvNumFormatType::TIME
:
2671 bRes
|= ImpGetTimeOutput(fNumber
, nIx
, bStarFlag
, rNatNum
, rCurrentLang
, sBuff
);
2673 case SvNumFormatType::DATETIME
:
2674 bRes
|= ImpGetDateTimeOutput(fNumber
, nIx
, bStarFlag
, rNatNum
, rCurrentLang
, sBuff
);
2676 case SvNumFormatType::NUMBER
:
2677 case SvNumFormatType::PERCENT
:
2678 case SvNumFormatType::CURRENCY
:
2679 bRes
|= ImpGetNumberOutput(fNumber
, nIx
, bStarFlag
, rNatNum
, sBuff
);
2681 case SvNumFormatType::LOGICAL
:
2682 bRes
|= ImpGetLogicalOutput(fNumber
, nIx
, rNatNum
, sBuff
);
2684 case SvNumFormatType::FRACTION
:
2685 bRes
|= ImpGetFractionOutput(fNumber
, nIx
, bStarFlag
, rNatNum
, sBuff
);
2687 case SvNumFormatType::SCIENTIFIC
:
2688 bRes
|= ImpGetScientificOutput(fNumber
, nIx
, bStarFlag
, rNatNum
, sBuff
);
2693 OutString
= sBuff
.makeStringAndClear();
2697 bool SvNumberformat::ImpGetScientificOutput(double fNumber
,
2700 const NativeNumberWrapper
& rNatNum
,
2701 OUStringBuffer
& sStr
) const
2706 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
2707 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
2711 if (nIx
== 0) // Not in the ones at the end
2713 bSign
= true; // Formats
2718 sStr
= ::rtl::math::doubleToUString( fNumber
,
2719 rtl_math_StringFormat_E
,
2720 rInfo
.nCntPre
+ rInfo
.nCntPost
- 1, '.' );
2721 OUStringBuffer ExpStr
;
2723 sal_Int32 nExPos
= sStr
.indexOf('E');
2724 sal_Int32 nDecPos
= -1;
2728 // split into mantissa and exponent and get rid of "E+" or "E-"
2729 sal_Int32 nExpStart
= nExPos
+ 1;
2731 switch ( sStr
[ nExpStart
] )
2740 ExpStr
= sStr
.subView( nExpStart
); // part following the "E+"
2741 sStr
.truncate( nExPos
);
2743 if ( rInfo
.nCntPre
!= 1 ) // rescale Exp
2745 sal_Int32 nExp
= OUString::unacquired(ExpStr
).toInt32() * nExpSign
;
2746 sal_Int32 nRescale
= (rInfo
.nCntPre
!= 0) ? nExp
% static_cast<sal_Int32
>(rInfo
.nCntPre
) : -1;
2747 if( nRescale
< 0 && rInfo
.nCntPre
!= 0 )
2748 nRescale
+= static_cast<sal_Int32
>(rInfo
.nCntPre
);
2759 ExpStr
= OUString::number( nExp
);
2760 const sal_Unicode cFirstDigit
= sStr
[0];
2762 sStr
= ::rtl::math::doubleToUString( fNumber
,
2763 rtl_math_StringFormat_E
,
2764 nRescale
+ rInfo
.nCntPost
, '.' );
2766 // sStr now may contain a rounded-up value shifted into the next
2767 // magnitude, for example 1.000E+02 (4 digits) for fNumber 99.995
2768 // (9.9995E+02 rounded to 3 decimals) but we want the final result
2769 // to be 100.00E+00 (5 digits), so for the following fill routines
2770 // below to work correctly append a zero decimal.
2771 /* TODO: this is awkward, could an engineering notation mode be
2772 * introduced to rtl_math_doubleToUString()? */
2773 sStr
.truncate( sStr
.indexOf('E') );
2774 if (sStr
[0] == '1' && cFirstDigit
!= '1')
2778 // cut any decimal delimiter
2779 sal_Int32 index
= 0;
2781 while((index
= sStr
.indexOf('.', index
)) >= 0)
2785 sStr
.remove(index
, 1);
2789 sal_uInt16 j
= nCnt
-1; // Last symbol
2790 sal_Int32 k
= ExpStr
.getLength() - 1; // Position in ExpStr
2791 sal_Int32 nZeros
= 0; // Erase leading zeros
2793 // erase all leading zeros except last one
2794 while (nZeros
< k
&& ExpStr
[nZeros
] == '0')
2800 ExpStr
.remove( 0, nZeros
);
2803 // restore leading zeros or blanks according to format '0' or '?' tdf#156449
2804 bRes
|= ImpNumberFill(rNatNum
, ExpStr
, fNumber
, k
, j
, nIx
, NF_SYMBOLTYPE_EXP
, bStarFlag
);
2808 if (rInfo
.nTypeArray
[j
] == NF_SYMBOLTYPE_EXP
)
2810 const OUString
& rStr
= rInfo
.sStrArray
[j
];
2813 ExpStr
.insert(0, '-');
2815 else if (rStr
.getLength() > 1 && rStr
[1] == '+')
2817 ExpStr
.insert(0, '+');
2819 ExpStr
.insert(0, rStr
[0]);
2829 // Continue main number:
2836 bRes
|= ImpDecimalFill(rNatNum
, sStr
, fNumber
, nDecPos
, j
, nIx
, false, bStarFlag
);
2841 sStr
.insert(0, '-');
2843 sStr
.append(ExpStr
);
2848 double SvNumberformat::GetRoundFractionValue ( double fNumber
) const
2850 sal_uInt16 nIx
= GetSubformatIndex ( fNumber
);
2851 double fIntPart
= 0.0; // integer part of fraction
2852 sal_Int64 nFrac
= 0, nDiv
= 1; // numerator and denominator
2853 double fSign
= (fNumber
< 0.0) ? -1.0 : 1.0;
2854 // fNumber is modified in ImpGetFractionElements to absolute fractional part
2855 ImpGetFractionElements ( fNumber
, nIx
, fIntPart
, nFrac
, nDiv
);
2857 return fSign
* ( fIntPart
+ static_cast<double>(nFrac
) / static_cast<double>(nDiv
) );
2859 return fSign
* fIntPart
;
2862 void SvNumberformat::ImpGetFractionElements ( double& fNumber
, sal_uInt16 nIx
,
2863 double& fIntPart
, sal_Int64
& nFrac
, sal_Int64
& nDiv
) const
2865 if ( fNumber
< 0.0 )
2867 fIntPart
= floor(fNumber
); // Integral part
2868 fNumber
-= fIntPart
; // Fractional part
2869 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
2870 sal_Int64 nForcedDiv
= lcl_GetDenominatorString( rInfo
, NumFor
[nIx
].GetCount() ).toInt32();
2871 if( nForcedDiv
> 0 )
2872 { // Forced Denominator
2874 nFrac
= static_cast<sal_Int64
>(floor ( fNumber
* nDiv
));
2875 double fFracNew
= static_cast<double>(nFrac
) / static_cast<double>(nDiv
);
2876 double fFracNew1
= static_cast<double>(nFrac
+ 1) / static_cast<double>(nDiv
);
2877 double fDiff
= fNumber
- fFracNew
;
2878 if( fDiff
> ( fFracNew1
- fNumber
) )
2883 else // Calculated Denominator
2886 sal_Int64 nBasis
= static_cast<sal_Int64
>(floor( pow(10.0,rInfo
.nCntExp
))) - 1; // 9, 99, 999 ,...
2887 sal_Int64 nFracPrev
= 1, nDivPrev
= 0, nFracNext
, nDivNext
, nPartialDenom
;
2888 double fRemainder
= fNumber
;
2890 // Use continued fraction representation of fNumber
2891 // See https://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximations
2892 while ( fRemainder
> 0.0 )
2894 double fTemp
= 1.0 / fRemainder
; // 64bits precision required when fRemainder is very weak
2895 nPartialDenom
= static_cast<sal_Int64
>(floor(fTemp
)); // due to floating point notation with double precision
2896 fRemainder
= fTemp
- static_cast<double>(nPartialDenom
);
2897 nDivNext
= nPartialDenom
* nDiv
+ nDivPrev
;
2898 if ( nDivNext
<= nBasis
) // continue loop
2900 nFracNext
= nPartialDenom
* nFrac
+ nFracPrev
;
2906 else // calculate collateral fraction and exit
2908 sal_Int64 nCollat
= (nBasis
- nDivPrev
) / nDiv
;
2909 if ( 2 * nCollat
>= nPartialDenom
)
2911 sal_Int64 nFracTest
= nCollat
* nFrac
+ nFracPrev
;
2912 sal_Int64 nDivTest
= nCollat
* nDiv
+ nDivPrev
;
2913 double fSign
= (static_cast<double>(nFrac
) > fNumber
* static_cast<double>(nDiv
))?1.0:-1.0;
2914 if ( fSign
* ( double(nFrac
* nDivTest
+ nDiv
* nFracTest
) - 2.0 * double(nDiv
* nDivTest
) * fNumber
) > 0.0 )
2920 fRemainder
= 0.0; // exit while loop
2928 nDiv
= ( nForcedDiv
> 0 ) ? nForcedDiv
: 1;
2932 bool SvNumberformat::ImpGetFractionOutput(double fNumber
,
2935 const NativeNumberWrapper
& rNatNum
,
2936 OUStringBuffer
& sBuff
) const
2939 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
2940 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
2941 OUStringBuffer sStr
, sFrac
, sDiv
; // Strings, value for Integral part Numerator and denominator
2942 bool bSign
= ( (fNumber
< 0) && (nIx
== 0) ); // sign Not in the ones at the end
2943 const OUString sIntegerFormat
= lcl_GetFractionIntegerString(rInfo
, nCnt
);
2944 const OUString sNumeratorFormat
= lcl_GetNumeratorString(rInfo
, nCnt
);
2945 const OUString sDenominatorFormat
= lcl_GetDenominatorString(rInfo
, nCnt
);
2947 sal_Int64 nFrac
= 0, nDiv
= 1;
2948 double fNum
= floor(fNumber
); // Integral part
2950 if (fNum
> D_MAX_U_INT32
|| rInfo
.nCntExp
> 9) // Too large
2952 sBuff
= ImpSvNumberformatScan::sErrStr
;
2955 if (rInfo
.nCntExp
== 0)
2957 SAL_WARN( "svl.numbers", "SvNumberformat:: Fraction, nCntExp == 0");
2962 ImpGetFractionElements( fNumber
, nIx
, fNum
, nFrac
, nDiv
);
2964 if (rInfo
.nCntPre
== 0) // Improper fraction
2966 double fNum1
= fNum
* static_cast<double>(nDiv
) + static_cast<double>(nFrac
);
2968 if (fNum1
> D_MAX_INTEGER
)
2970 sBuff
= ImpSvNumberformatScan::sErrStr
;
2973 nFrac
= static_cast<sal_Int64
>(floor(fNum1
));
2975 else if (fNum
== 0.0 && nFrac
!= 0)
2981 o3tl::sprintf( aBuf
, "%.f", fNum
); // simple rounded integer
2982 sStr
.appendAscii( aBuf
);
2983 ::impTransliterate(sStr
, NumFor
[nIx
].GetNatNum(), rNatNum
);
2985 bool bHideFraction
= (rInfo
.nCntPre
> 0 && nFrac
== 0
2986 && (sNumeratorFormat
.indexOf('0') < 0)
2987 && (sDenominatorFormat
.indexOf('0') < 0
2988 || sDenominatorFormat
.toInt32() > 0) );
2989 if ( bHideFraction
)
2993 else // if there are some '0' in format, force display of fraction
2995 sFrac
= ImpIntToString(rNatNum
, nIx
, nFrac
);
2996 sDiv
= ImpIntToString(rNatNum
, nIx
, nDiv
);
2999 sal_uInt16 j
= nCnt
-1; // Last symbol -> backwards
3000 sal_Int32 k
; // Denominator
3002 bRes
|= ImpNumberFill(rNatNum
, sDiv
, fNumber
, k
, j
, nIx
, NF_SYMBOLTYPE_FRAC
, bStarFlag
, true);
3005 if (rInfo
.nTypeArray
[j
] == NF_SYMBOLTYPE_FRAC
)
3007 if ( bHideFraction
)
3008 { // do not insert blank for fraction if there is no '?'
3009 if ( sNumeratorFormat
.indexOf('?') >= 0
3010 || sDenominatorFormat
.indexOf('?') >= 0 )
3011 sDiv
.insert(0, ' ');
3015 sDiv
.insert(0, rInfo
.sStrArray
[j
][0]);
3026 // Further numerators:
3033 bRes
|= ImpNumberFill(rNatNum
, sFrac
, fNumber
, k
, j
, nIx
, NF_SYMBOLTYPE_FRACBLANK
, bStarFlag
);
3034 bCont
= false; // there is no integer part?
3035 if (rInfo
.nTypeArray
[j
] == NF_SYMBOLTYPE_FRACBLANK
)
3039 if ( bHideFraction
)
3040 { // '?' in any format force display of blank as delimiter
3041 if ( sIntegerFormat
.indexOf('?') >= 0
3042 || sNumeratorFormat
.indexOf('?') >= 0
3043 || sDenominatorFormat
.indexOf('?') >= 0 )
3045 for (sal_Int32 i
= 0; i
< rInfo
.sStrArray
[j
].getLength(); i
++)
3046 sFrac
.insert(0, ' ');
3051 if ( fNum
!= 0.0 || sIntegerFormat
.indexOf('0') >= 0 )
3052 sFrac
.insert(0, rInfo
.sStrArray
[j
]); // insert Blank string only if there are both integer and fraction
3055 if ( sIntegerFormat
.indexOf('?') >= 0
3056 || sNumeratorFormat
.indexOf('?') >= 0 )
3058 for (sal_Int32 i
= 0; i
< rInfo
.sStrArray
[j
].getLength(); i
++)
3059 sFrac
.insert(0, ' ');
3064 bCont
= true; // Yes, there is an integer
3067 sFrac
.insert(0, rInfo
.sStrArray
[j
]);
3070 // Continue integer part
3077 k
= sStr
.getLength(); // After last figure
3078 bRes
|= ImpNumberFillWithThousands(rNatNum
, sStr
, fNumber
, k
, j
, nIx
,
3079 rInfo
.nCntPre
, bStarFlag
);
3081 if (bSign
&& (nFrac
!= 0 || fNum
!= 0.0))
3083 sBuff
.insert(0, '-'); // Not -0
3086 sBuff
.append(sFrac
);
3091 sal_uInt16
SvNumberformat::ImpGetFractionOfSecondString( OUStringBuffer
& rBuf
, double fFractionOfSecond
,
3092 int nFractionDecimals
, bool bAddOneRoundingDecimal
, sal_uInt16 nIx
, sal_uInt16 nMinimumInputLineDecimals
,
3093 const NativeNumberWrapper
& rNatNum
) const
3095 if (!nFractionDecimals
)
3098 // nFractionDecimals+1 to not round up what Time::GetClock() carefully
3100 rBuf
.append( rtl::math::doubleToUString( fFractionOfSecond
, rtl_math_StringFormat_F
,
3101 (bAddOneRoundingDecimal
? nFractionDecimals
+ 1 : nFractionDecimals
), '.'));
3102 rBuf
.stripStart('0');
3103 rBuf
.stripStart('.');
3104 if (bAddOneRoundingDecimal
&& rBuf
.getLength() > nFractionDecimals
)
3105 rBuf
.truncate( nFractionDecimals
); // the digit appended because of nFractionDecimals+1
3106 if (nMinimumInputLineDecimals
)
3109 for (sal_Int32 index
= rBuf
.getLength(); index
< nMinimumInputLineDecimals
; ++index
)
3113 ::impTransliterate(rBuf
, NumFor
[nIx
].GetNatNum(), rNatNum
);
3114 nFractionDecimals
= rBuf
.getLength();
3118 ::impTransliterate(rBuf
, NumFor
[nIx
].GetNatNum(), rNatNum
);
3120 return static_cast<sal_uInt16
>(nFractionDecimals
);
3123 bool SvNumberformat::ImpGetTimeOutput(double fNumber
,
3126 const NativeNumberWrapper
& rNatNum
,
3127 const SvNFLanguageData
& rCurrentLang
,
3128 OUStringBuffer
& sBuff
) const
3130 using namespace ::com::sun::star::i18n
;
3131 bool bCalendarSet
= false;
3132 const double fNumberOrig
= fNumber
;
3143 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
3146 if ( rScan
.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION
&&
3147 0 < rInfo
.nCntPost
&& rInfo
.nCntPost
< kTimeSignificantRound
)
3150 nCntPost
= kTimeSignificantRound
;
3155 nCntPost
= rInfo
.nCntPost
;
3158 OUStringBuffer sSecStr
;
3159 sal_Int32 nSecPos
= 0; // For figure by figure processing
3160 sal_uInt32 nHour
, nMin
, nSec
;
3161 if (!rInfo
.bThousand
) // No [] format
3163 sal_uInt16 nCHour
, nCMinute
, nCSecond
;
3164 double fFractionOfSecond
;
3165 tools::Time::GetClock( fNumberOrig
, nCHour
, nCMinute
, nCSecond
, fFractionOfSecond
, nCntPost
);
3169 nCntPost
= ImpGetFractionOfSecondString( sSecStr
, fFractionOfSecond
, nCntPost
, true, nIx
,
3170 (bInputLine
? rInfo
.nCntPost
: 0), rNatNum
);
3174 const double fTime
= rtl::math::round( fNumber
* 86400.0, int(nCntPost
));
3175 if (bSign
&& fTime
== 0.0)
3177 bSign
= false; // Not -00:00:00
3179 if (fTime
> D_MAX_U_INT32
)
3181 sBuff
= ImpSvNumberformatScan::sErrStr
;
3184 sal_uInt32 nSeconds
= static_cast<sal_uInt32
>(fTime
);
3186 nCntPost
= ImpGetFractionOfSecondString( sSecStr
, fTime
- nSeconds
, nCntPost
, false, nIx
,
3187 (bInputLine
? rInfo
.nCntPost
: 0), rNatNum
);
3189 if (rInfo
.nThousand
== 3) // [ss]
3195 else if (rInfo
.nThousand
== 2) // [mm]:ss
3198 nMin
= nSeconds
/ 60;
3199 nSec
= nSeconds
% 60;
3201 else if (rInfo
.nThousand
== 1) // [hh]:mm:ss
3203 nHour
= nSeconds
/ 3600;
3204 nMin
= (nSeconds
%3600) / 60;
3209 // TODO What should these be set to?
3216 sal_Unicode cAmPm
= ' '; // a or p
3217 if (rInfo
.nCntExp
) // AM/PM
3224 else if (nHour
< 12)
3237 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
3238 for (sal_uInt16 i
= 0; i
< nCnt
; i
++)
3241 switch (rInfo
.nTypeArray
[i
])
3243 case NF_SYMBOLTYPE_STAR
:
3246 bRes
= lcl_appendStarFillChar( sBuff
, rInfo
.sStrArray
[i
]);
3249 case NF_SYMBOLTYPE_BLANK
:
3250 if (rInfo
.sStrArray
[i
].getLength() >= 2)
3251 InsertBlanks(sBuff
, sBuff
.getLength(), rInfo
.sStrArray
[i
][1] );
3253 case NF_SYMBOLTYPE_STRING
:
3254 case NF_SYMBOLTYPE_CURRENCY
:
3255 case NF_SYMBOLTYPE_DATESEP
:
3256 case NF_SYMBOLTYPE_TIMESEP
:
3257 case NF_SYMBOLTYPE_TIME100SECSEP
:
3258 sBuff
.append(rInfo
.sStrArray
[i
]);
3260 case NF_SYMBOLTYPE_DIGIT
:
3261 nLen
= ( bInputLine
&& i
> 0 &&
3262 (rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_STRING
||
3263 rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_TIME100SECSEP
) ?
3264 nCntPost
: rInfo
.sStrArray
[i
].getLength() );
3265 for (sal_Int32 j
= 0; j
< nLen
&& nSecPos
< nCntPost
&& nSecPos
< sSecStr
.getLength(); ++j
)
3267 sBuff
.append(sSecStr
[nSecPos
]);
3271 case NF_KEY_AMPM
: // AM/PM
3273 CalendarWrapper
& rCal
= *rCurrentLang
.GetCalendar();
3274 if ( !bCalendarSet
)
3276 double fDiff
= DateTime::Sub( DateTime(rScan
.GetNullDate()), rCal
.getEpochStart());
3277 fDiff
+= fNumberOrig
;
3278 rCal
.setLocalDateTime( fDiff
);
3279 bCalendarSet
= true;
3283 sBuff
.append(rCal
.getDisplayName(
3284 CalendarDisplayIndex::AM_PM
, AmPmValue::AM
, 0 ));
3288 sBuff
.append(rCal
.getDisplayName(
3289 CalendarDisplayIndex::AM_PM
, AmPmValue::PM
, 0 ));
3293 case NF_KEY_AP
: // A/P
3303 case NF_KEY_MI
: // M
3304 sBuff
.append(ImpIntToString(rNatNum
, nIx
, nMin
));
3306 case NF_KEY_MMI
: // MM
3307 sBuff
.append(ImpIntToString(rNatNum
, nIx
, nMin
, 2 ));
3310 sBuff
.append(ImpIntToString(rNatNum
, nIx
, nHour
));
3312 case NF_KEY_HH
: // HH
3313 sBuff
.append(ImpIntToString(rNatNum
, nIx
, nHour
, 2 ));
3316 sBuff
.append(ImpIntToString(rNatNum
, nIx
, nSec
));
3318 case NF_KEY_SS
: // SS
3319 sBuff
.append(ImpIntToString(rNatNum
, nIx
, nSec
, 2 ));
3325 if (bSign
&& rInfo
.bThousand
)
3327 sBuff
.insert(0, '-');
3333 /** If a day of month occurs within the format, the month name is in possessive
3334 genitive case if the day follows the month, and partitive case if the day
3335 precedes the month. If there is no day of month the nominative case (noun)
3336 is returned. Also if the month is immediately preceded or followed by a
3337 literal string other than space and not followed by a comma, the nominative
3338 name is used, this prevents duplicated casing for MMMM\t\a and such in
3339 documents imported from (e.g. Finnish) Excel or older LibO/OOo releases.
3342 // IDEA: instead of eCodeType pass the index to nTypeArray and restrict
3343 // inspection of month name around that one, that would enable different month
3344 // cases in one format. Though probably the most rare use case ever...
3346 sal_Int32
SvNumberformat::ImpUseMonthCase( int & io_nState
, const ImpSvNumFor
& rNumFor
, NfKeywordIndex eCodeType
)
3348 using namespace ::com::sun::star::i18n
;
3351 bool bMonthSeen
= false;
3352 bool bDaySeen
= false;
3353 const ImpSvNumberformatInfo
& rInfo
= rNumFor
.Info();
3354 const sal_uInt16 nCount
= rNumFor
.GetCount();
3355 for (sal_uInt16 i
= 0; i
< nCount
&& io_nState
== 0; ++i
)
3358 switch (rInfo
.nTypeArray
[i
])
3374 if ((i
< nCount
-1 &&
3375 rInfo
.nTypeArray
[i
+1] == NF_SYMBOLTYPE_STRING
&&
3376 // Literal following, not empty, space nor comma.
3377 !rInfo
.sStrArray
[i
+1].isEmpty() &&
3378 rInfo
.sStrArray
[i
+1][0] != ' ' && rInfo
.sStrArray
[i
+1][0] != ',') ||
3379 (i
> 0 && rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_STRING
&&
3380 ((nLen
= rInfo
.sStrArray
[i
-1].getLength()) > 0) &&
3381 // Literal preceding, not space.
3382 rInfo
.sStrArray
[i
-1][nLen
-1] != ' '))
3399 io_nState
= 1; // No day of month
3405 // No day of month or forced nominative
3409 return CalendarDisplayCode::SHORT_MONTH_NAME
;
3411 return CalendarDisplayCode::LONG_MONTH_NAME
;
3413 return CalendarDisplayCode::NARROW_MONTH_NAME
;
3419 // Day of month follows month (the month's 17th)
3423 return CalendarDisplayCode::SHORT_GENITIVE_MONTH_NAME
;
3425 return CalendarDisplayCode::LONG_GENITIVE_MONTH_NAME
;
3427 return CalendarDisplayCode::NARROW_GENITIVE_MONTH_NAME
;
3433 // Day of month precedes month (17 of month)
3437 return CalendarDisplayCode::SHORT_PARTITIVE_MONTH_NAME
;
3439 return CalendarDisplayCode::LONG_PARTITIVE_MONTH_NAME
;
3441 return CalendarDisplayCode::NARROW_PARTITIVE_MONTH_NAME
;
3447 SAL_WARN( "svl.numbers", "ImpUseMonthCase: unhandled keyword index eCodeType");
3448 return CalendarDisplayCode::LONG_MONTH_NAME
;
3453 bool ImpIsOtherCalendar( const ImpSvNumFor
& rNumFor
, const CalendarWrapper
& rCal
)
3455 if ( rCal
.getUniqueID() != GREGORIAN
)
3459 const ImpSvNumberformatInfo
& rInfo
= rNumFor
.Info();
3460 const sal_uInt16 nCnt
= rNumFor
.GetCount();
3462 for ( i
= 0; i
< nCnt
; i
++ )
3464 switch ( rInfo
.nTypeArray
[i
] )
3466 case NF_SYMBOLTYPE_CALENDAR
:
3485 void SvNumberformat::SwitchToOtherCalendar( OUString
& rOrgCalendar
,
3486 double& fOrgDateTime
,
3487 CalendarWrapper
& rCal
) const
3489 if ( rCal
.getUniqueID() != GREGORIAN
)
3492 using namespace ::com::sun::star::i18n
;
3493 const css::uno::Sequence
< OUString
> xCals
= rCal
.getAllCalendars(
3494 rLoc().getLanguageTag().getLocale() );
3495 sal_Int32 nCnt
= xCals
.getLength();
3499 auto pCal
= std::find_if(xCals
.begin(), xCals
.end(),
3500 [](const OUString
& rCalName
) { return rCalName
!= GREGORIAN
; });
3501 if (pCal
== xCals
.end())
3504 if ( !rOrgCalendar
.getLength() )
3506 rOrgCalendar
= rCal
.getUniqueID();
3507 fOrgDateTime
= rCal
.getDateTime();
3509 rCal
.loadCalendar( *pCal
, rLoc().getLanguageTag().getLocale() );
3510 rCal
.setDateTime( fOrgDateTime
);
3513 void SvNumberformat::SwitchToGregorianCalendar( std::u16string_view rOrgCalendar
,
3514 double fOrgDateTime
,
3515 CalendarWrapper
& rCal
) const
3517 if ( rOrgCalendar
.size() && rCal
.getUniqueID() != GREGORIAN
)
3519 rCal
.loadCalendar( GREGORIAN
, rLoc().getLanguageTag().getLocale() );
3520 rCal
.setDateTime( fOrgDateTime
);
3524 bool SvNumberformat::ImpFallBackToGregorianCalendar( OUString
& rOrgCalendar
,
3525 double& fOrgDateTime
,
3526 CalendarWrapper
& rCal
) const
3528 using namespace ::com::sun::star::i18n
;
3529 if ( rCal
.getUniqueID() != GREGORIAN
)
3531 sal_Int16 nVal
= rCal
.getValue( CalendarFieldIndex::ERA
);
3532 if ( nVal
== 0 && rCal
.getLoadedCalendar().Eras
[0].ID
== "Dummy" )
3534 if ( !rOrgCalendar
.getLength() )
3536 rOrgCalendar
= rCal
.getUniqueID();
3537 fOrgDateTime
= rCal
.getDateTime();
3539 else if ( rOrgCalendar
== GREGORIAN
)
3541 rOrgCalendar
.clear();
3543 rCal
.loadCalendar( GREGORIAN
, rLoc().getLanguageTag().getLocale() );
3544 rCal
.setDateTime( fOrgDateTime
);
3553 /* XXX NOTE: even if the ImpSwitchToSpecifiedCalendar method is currently
3554 * unused please don't remove it, it would be needed by
3555 * SwitchToSpecifiedCalendar(), see comment in
3556 * ImpSvNumberInputScan::GetDateRef() */
3558 bool SvNumberformat::ImpSwitchToSpecifiedCalendar( OUString
& rOrgCalendar
,
3559 double& fOrgDateTime
,
3560 const ImpSvNumFor
& rNumFor
) const
3562 const ImpSvNumberformatInfo
& rInfo
= rNumFor
.Info();
3563 const sal_uInt16 nCnt
= rNumFor
.GetCount();
3564 for ( sal_uInt16 i
= 0; i
< nCnt
; i
++ )
3566 if ( rInfo
.nTypeArray
[i
] == NF_SYMBOLTYPE_CALENDAR
)
3568 CalendarWrapper
& rCal
= GetCal();
3569 if ( !rOrgCalendar
.getLength() )
3571 rOrgCalendar
= rCal
.getUniqueID();
3572 fOrgDateTime
= rCal
.getDateTime();
3574 rCal
.loadCalendar( rInfo
.sStrArray
[i
], rLoc().getLocale() );
3575 rCal
.setDateTime( fOrgDateTime
);
3584 void SvNumberformat::ImpAppendEraG( OUStringBuffer
& OutString
,
3585 const CalendarWrapper
& rCal
,
3588 using namespace ::com::sun::star::i18n
;
3589 if ( rCal
.getUniqueID() == "gengou" )
3592 sal_Int16 nVal
= rCal
.getValue( CalendarFieldIndex::ERA
);
3614 OutString
.append(cEra
);
3618 OutString
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_ERA
, nNatNum
));
3622 bool SvNumberformat::ImpIsIso8601( const ImpSvNumFor
& rNumFor
) const
3624 bool bIsIso
= false;
3625 if (eType
& SvNumFormatType::DATE
)
3636 State eState
= eNone
;
3637 auto & rTypeArray
= rNumFor
.Info().nTypeArray
;
3638 sal_uInt16 nCnt
= rNumFor
.GetCount();
3639 for (sal_uInt16 i
=0; i
< nCnt
&& !bIsIso
&& eState
!= eNotIso
; ++i
)
3641 switch ( rTypeArray
[i
] )
3643 case NF_KEY_YY
: // two digits not strictly ISO 8601
3645 if (eState
!= eNone
)
3654 case NF_KEY_M
: // single digit not strictly ISO 8601
3656 if (eState
!= eAtSep1
)
3665 case NF_KEY_D
: // single digit not strictly ISO 8601
3667 if (eState
!= eAtSep2
)
3676 case NF_SYMBOLTYPE_STRING
:
3677 case NF_SYMBOLTYPE_DATESEP
:
3678 if (rNumFor
.Info().sStrArray
[i
] == "-")
3680 if (eState
== eAtYear
)
3684 else if (eState
== eAtMonth
)
3705 SAL_WARN( "svl.numbers", "SvNumberformat::ImpIsIso8601: no date" );
3710 static bool lcl_hasEra( const ImpSvNumFor
& rNumFor
)
3712 const ImpSvNumberformatInfo
& rInfo
= rNumFor
.Info();
3713 const sal_uInt16 nCnt
= rNumFor
.GetCount();
3714 for ( sal_uInt16 i
= 0; i
< nCnt
; i
++ )
3716 switch ( rInfo
.nTypeArray
[i
] )
3728 static bool lcl_isSignedYear( const CalendarWrapper
& rCal
, const ImpSvNumFor
& rNumFor
)
3730 return rCal
.getValue( css::i18n::CalendarFieldIndex::ERA
) == 0 &&
3731 rCal
.getUniqueID() == GREGORIAN
&& !lcl_hasEra( rNumFor
);
3734 /* XXX: if needed this could be stripped from rEpochStart and diff adding and
3735 * moved to tools' DateTime to be reused elsewhere. */
3736 static bool lcl_getValidDate( const DateTime
& rNullDate
, const DateTime
& rEpochStart
, double& fNumber
)
3738 static const DateTime
aCE( Date(1,1,1));
3739 static const DateTime
aMin( Date(1,1, SAL_MIN_INT16
));
3740 static const DateTime
aMax( Date(31,12, SAL_MAX_INT16
), tools::Time(23,59,59, tools::Time::nanoSecPerSec
- 1));
3741 static const double fMin
= DateTime::Sub( aMin
, aCE
);
3742 static const double fMax
= DateTime::Sub( aMax
, aCE
);
3743 // Value must be representable in our tools::Date proleptic Gregorian
3744 // calendar as well.
3745 const double fOff
= DateTime::Sub( rNullDate
, aCE
) + fNumber
;
3746 // Add diff between epochs to serial date number.
3747 const double fDiff
= DateTime::Sub( rNullDate
, rEpochStart
);
3749 return fMin
<= fOff
&& fOff
<= fMax
;
3752 bool SvNumberformat::ImpGetDateOutput(double fNumber
,
3755 const NativeNumberWrapper
& rNatNum
,
3756 const SvNFLanguageData
& rCurrentLang
,
3757 OUStringBuffer
& sBuff
) const
3759 using namespace ::com::sun::star::i18n
;
3762 CalendarWrapper
& rCal
= *rCurrentLang
.GetCalendar();
3763 if (!lcl_getValidDate( DateTime( rScan
.GetNullDate() ), rCal
.getEpochStart(), fNumber
))
3765 sBuff
= ImpSvNumberformatScan::sErrStr
;
3768 rCal
.setLocalDateTime( fNumber
);
3769 int nUseMonthCase
= 0; // Not decided yet
3770 OUString aOrgCalendar
; // empty => not changed yet
3772 double fOrgDateTime(0.0);
3773 bool bOtherCalendar
= ImpIsOtherCalendar( NumFor
[nIx
], *rCurrentLang
.GetCalendar() );
3774 if ( bOtherCalendar
)
3776 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
3778 if ( ImpFallBackToGregorianCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() ) )
3780 bOtherCalendar
= false;
3782 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
3783 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
3784 sal_Int16 nNatNum
= NumFor
[nIx
].GetNatNum().GetNatNum();
3787 // NatNum12: if the date format contains more than a date
3788 // field, it needs to specify in NatNum12 argument
3789 // which date element needs special formatting:
3791 // '[NatNum12 ordinal-number]D' -> "1st"
3792 // '[NatNum12 D=ordinal-number]D" of "MMMM' -> "1st of April"
3793 // '[NatNum12 D=ordinal]D" of "MMMM' -> "first of April"
3794 // '[NatNum12 YYYY=year,D=ordinal]D" of "MMMM", "YYYY' -> "first of April, nineteen ninety"
3796 // Note: set only for YYYY, MMMM, M, DDDD, D and NNN/AAAA in date formats.
3797 // Additionally for MMMMM, MMM, DDD and NN/AA to support at least
3798 // capitalize, upper, lower, title.
3799 // XXX It's possible to extend this for other keywords and date + time
3800 // combinations, as required.
3802 bool bUseSpellout
= NatNumTakesParameters(nNatNum
) &&
3803 (nCnt
== 1 || NumFor
[nIx
].GetNatNum().GetParams().indexOf('=') > -1);
3805 for (sal_uInt16 i
= 0; i
< nCnt
; i
++)
3807 switch (rInfo
.nTypeArray
[i
])
3809 case NF_SYMBOLTYPE_CALENDAR
:
3810 if ( !aOrgCalendar
.getLength() )
3812 aOrgCalendar
= rCal
.getUniqueID();
3813 fOrgDateTime
= rCal
.getDateTime();
3815 rCal
.loadCalendar( rInfo
.sStrArray
[i
], rLoc().getLanguageTag().getLocale() );
3816 rCal
.setDateTime( fOrgDateTime
);
3817 ImpFallBackToGregorianCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
3819 case NF_SYMBOLTYPE_STAR
:
3822 bRes
= lcl_appendStarFillChar( sBuff
, rInfo
.sStrArray
[i
]);
3825 case NF_SYMBOLTYPE_BLANK
:
3826 if (rInfo
.sStrArray
[i
].getLength() >= 2)
3827 InsertBlanks( sBuff
, sBuff
.getLength(), rInfo
.sStrArray
[i
][1] );
3829 case NF_SYMBOLTYPE_STRING
:
3830 case NF_SYMBOLTYPE_CURRENCY
:
3831 case NF_SYMBOLTYPE_DATESEP
:
3832 case NF_SYMBOLTYPE_TIMESEP
:
3833 case NF_SYMBOLTYPE_TIME100SECSEP
:
3834 sBuff
.append(rInfo
.sStrArray
[i
]);
3837 aStr
= rCal
.getDisplayString( CalendarDisplayCode::SHORT_MONTH
, nNatNum
);
3838 // NatNum12: support variants of preposition, suffixation or article
3839 // for example, Catalan "de març", but "d'abril" etc.
3842 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
], rNatNum
);
3846 case NF_KEY_MM
: // MM
3847 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_MONTH
, nNatNum
));
3849 case NF_KEY_MMM
: // MMM
3850 case NF_KEY_MMMM
: // MMMM
3851 case NF_KEY_MMMMM
: // MMMMM
3852 // NatNum12: support variants of preposition, suffixation or
3853 // article, or capitalize, upper, lower, title.
3854 // Note: result of the "spell out" conversion can depend from the optional
3855 // PartitiveMonths or GenitiveMonths defined in the locale data,
3856 // see description of ImpUseMonthCase(), and locale data in
3857 // i18npool/source/localedata/data/ and libnumbertext
3858 aStr
= rCal
.getDisplayString( ImpUseMonthCase( nUseMonthCase
, NumFor
[nIx
],
3859 static_cast<NfKeywordIndex
>(rInfo
.nTypeArray
[i
])),
3863 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
], rNatNum
);
3868 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_QUARTER
, nNatNum
));
3870 case NF_KEY_QQ
: // QQ
3871 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_QUARTER
, nNatNum
));
3874 aStr
= rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY
, nNatNum
);
3875 // NatNum12: support variants of preposition, suffixation or article
3878 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
], rNatNum
);
3882 case NF_KEY_DD
: // DD
3883 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY
, nNatNum
));
3885 case NF_KEY_DDD
: // DDD
3886 if ( bOtherCalendar
)
3888 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
3890 aStr
= rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME
, nNatNum
);
3891 // NatNum12: support at least capitalize, upper, lower, title
3894 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
], rNatNum
);
3897 if ( bOtherCalendar
)
3899 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
3902 case NF_KEY_DDDD
: // DDDD
3903 if ( bOtherCalendar
)
3905 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
3907 aStr
= rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
);
3908 // NatNum12: support variants of preposition, suffixation or article
3911 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
], rNatNum
);
3914 if ( bOtherCalendar
)
3916 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
3919 case NF_KEY_YY
: // YY
3920 if ( bOtherCalendar
)
3922 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
3924 // Prepend a minus sign if Gregorian BCE and era is not displayed.
3925 if (lcl_isSignedYear( rCal
, NumFor
[nIx
] ))
3929 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_YEAR
, nNatNum
));
3930 if ( bOtherCalendar
)
3932 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
3935 case NF_KEY_YYYY
: // YYYY
3936 if ( bOtherCalendar
)
3938 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
3940 // Prepend a minus sign if Gregorian BCE and era is not displayed.
3941 if (lcl_isSignedYear( rCal
, NumFor
[nIx
] ))
3945 aStr
= rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR
, nNatNum
);
3946 if (aStr
.getLength() < 4 && !lcl_hasEra(NumFor
[nIx
]))
3948 using namespace comphelper::string
;
3949 // Ensure that year consists of at least 4 digits, so it
3950 // can be distinguished from 2 digits display and edited
3951 // without suddenly being hit by the 2-digit year magic.
3952 OUStringBuffer aBuf
;
3953 padToLength(aBuf
, 4 - aStr
.getLength(), '0');
3954 ::impTransliterate(aBuf
, NumFor
[nIx
].GetNatNum(), rNatNum
);
3956 aStr
= aBuf
.makeStringAndClear();
3958 // NatNum12: support variants of preposition, suffixation or article
3961 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
], rNatNum
);
3964 if ( bOtherCalendar
)
3966 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
3969 case NF_KEY_EC
: // E
3970 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_YEAR
, nNatNum
));
3972 case NF_KEY_EEC
: // EE
3974 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR
, nNatNum
));
3976 case NF_KEY_NN
: // NN
3977 case NF_KEY_AAA
: // AAA
3978 aStr
= rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME
, nNatNum
);
3979 // NatNum12: support at least capitalize, upper, lower, title
3982 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
], rNatNum
);
3986 case NF_KEY_NNN
: // NNN
3987 case NF_KEY_AAAA
: // AAAA
3988 aStr
= rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
);
3989 // NatNum12: support variants of preposition, suffixation or article
3992 aStr
= impTransliterate(aStr
, NumFor
[nIx
].GetNatNum(), rInfo
.nTypeArray
[i
], rNatNum
);
3996 case NF_KEY_NNNN
: // NNNN
3997 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
));
3998 sBuff
.append(rLoc().getLongDateDayOfWeekSep());
4000 case NF_KEY_WW
: // WW
4001 sBuff
.append(ImpIntToString(rNatNum
, nIx
,
4002 rCal
.getValue( CalendarFieldIndex::WEEK_OF_YEAR
)));
4005 ImpAppendEraG(sBuff
, rCal
, nNatNum
);
4007 case NF_KEY_GG
: // GG
4008 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_ERA
, nNatNum
));
4010 case NF_KEY_GGG
: // GGG
4011 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_ERA
, nNatNum
));
4013 case NF_KEY_RR
: // RR => GGGEE
4014 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA
, nNatNum
));
4018 if ( aOrgCalendar
.getLength() )
4020 rCal
.loadCalendar( aOrgCalendar
, rLoc().getLanguageTag().getLocale() ); // restore calendar
4025 bool SvNumberformat::ImpGetDateTimeOutput(double fNumber
,
4028 const NativeNumberWrapper
& rNatNum
,
4029 const SvNFLanguageData
& rCurrentLang
,
4030 OUStringBuffer
& sBuff
) const
4032 using namespace ::com::sun::star::i18n
;
4035 CalendarWrapper
& rCal
= *rCurrentLang
.GetCalendar();
4036 if (!lcl_getValidDate( DateTime( rScan
.GetNullDate() ), rCal
.getEpochStart(), fNumber
))
4038 sBuff
= ImpSvNumberformatScan::sErrStr
;
4042 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
4044 sal_Int32 nCntPost
, nFirstRounding
;
4045 if ( rScan
.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION
&&
4046 0 < rInfo
.nCntPost
&& rInfo
.nCntPost
< kTimeSignificantRound
)
4049 nCntPost
= nFirstRounding
= kTimeSignificantRound
;
4054 nCntPost
= rInfo
.nCntPost
;
4055 // For clock format (not []) do not round up to seconds and thus days.
4056 nFirstRounding
= (rInfo
.bThousand
? nCntPost
: kTimeSignificantRound
);
4058 double fTime
= (fNumber
- floor( fNumber
)) * 86400.0;
4059 fTime
= ::rtl::math::round( fTime
, int(nFirstRounding
) );
4060 if (fTime
>= 86400.0)
4062 // result of fNumber==x.999999999... rounded up, use correct date/time
4064 fNumber
= floor( fNumber
+ 0.5) + fTime
;
4066 rCal
.setLocalDateTime( fNumber
);
4068 int nUseMonthCase
= 0; // Not decided yet
4069 OUString aOrgCalendar
; // empty => not changed yet
4070 double fOrgDateTime(0.0);
4071 bool bOtherCalendar
= ImpIsOtherCalendar( NumFor
[nIx
], *rCurrentLang
.GetCalendar() );
4072 if ( bOtherCalendar
)
4074 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
4076 if ( ImpFallBackToGregorianCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() ) )
4078 bOtherCalendar
= false;
4080 sal_Int16 nNatNum
= NumFor
[nIx
].GetNatNum().GetNatNum();
4082 OUStringBuffer sSecStr
;
4083 sal_Int32 nSecPos
= 0; // For figure by figure processing
4084 sal_uInt32 nHour
, nMin
, nSec
;
4085 if (!rInfo
.bThousand
) // No [] format
4087 sal_uInt16 nCHour
, nCMinute
, nCSecond
;
4088 double fFractionOfSecond
;
4089 tools::Time::GetClock( fNumber
, nCHour
, nCMinute
, nCSecond
, fFractionOfSecond
, nCntPost
);
4093 nCntPost
= ImpGetFractionOfSecondString( sSecStr
, fFractionOfSecond
, nCntPost
, true, nIx
,
4094 (bInputLine
? rInfo
.nCntPost
: 0), rNatNum
);
4098 sal_uInt32 nSeconds
= static_cast<sal_uInt32
>(floor( fTime
));
4100 nCntPost
= ImpGetFractionOfSecondString( sSecStr
, fTime
- nSeconds
, nCntPost
, false, nIx
,
4101 (bInputLine
? rInfo
.nCntPost
: 0), rNatNum
);
4103 if (rInfo
.nThousand
== 3) // [ss]
4109 else if (rInfo
.nThousand
== 2) // [mm]:ss
4112 nMin
= nSeconds
/ 60;
4113 nSec
= nSeconds
% 60;
4115 else if (rInfo
.nThousand
== 1) // [hh]:mm:ss
4117 nHour
= nSeconds
/ 3600;
4118 nMin
= (nSeconds
%3600) / 60;
4123 nHour
= 0; // TODO What should these values be?
4128 sal_Unicode cAmPm
= ' '; // a or p
4129 if (rInfo
.nCntExp
) // AM/PM
4136 else if (nHour
< 12)
4149 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
4152 for (sal_uInt16 i
= 0; i
< nCnt
; i
++)
4154 switch (rInfo
.nTypeArray
[i
])
4156 case NF_SYMBOLTYPE_CALENDAR
:
4157 if ( !aOrgCalendar
.getLength() )
4159 aOrgCalendar
= rCal
.getUniqueID();
4160 fOrgDateTime
= rCal
.getDateTime();
4162 rCal
.loadCalendar( rInfo
.sStrArray
[i
], rLoc().getLanguageTag().getLocale() );
4163 rCal
.setDateTime( fOrgDateTime
);
4164 ImpFallBackToGregorianCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
4166 case NF_SYMBOLTYPE_STAR
:
4169 bRes
= lcl_appendStarFillChar( sBuff
, rInfo
.sStrArray
[i
]);
4172 case NF_SYMBOLTYPE_BLANK
:
4173 if (rInfo
.sStrArray
[i
].getLength() >= 2)
4174 InsertBlanks( sBuff
, sBuff
.getLength(), rInfo
.sStrArray
[i
][1] );
4176 case NF_SYMBOLTYPE_STRING
:
4177 case NF_SYMBOLTYPE_CURRENCY
:
4178 case NF_SYMBOLTYPE_DATESEP
:
4179 case NF_SYMBOLTYPE_TIMESEP
:
4180 case NF_SYMBOLTYPE_TIME100SECSEP
:
4181 sBuff
.append(rInfo
.sStrArray
[i
]);
4183 case NF_SYMBOLTYPE_DIGIT
:
4184 nLen
= ( bInputLine
&& i
> 0 &&
4185 (rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_STRING
||
4186 rInfo
.nTypeArray
[i
-1] == NF_SYMBOLTYPE_TIME100SECSEP
) ?
4187 nCntPost
: rInfo
.sStrArray
[i
].getLength() );
4188 for (sal_Int32 j
= 0; j
< nLen
&& nSecPos
< nCntPost
&& nSecPos
< sSecStr
.getLength(); ++j
)
4190 sBuff
.append(sSecStr
[ nSecPos
]);
4194 case NF_KEY_AMPM
: // AM/PM
4197 sBuff
.append(rCal
.getDisplayName( CalendarDisplayIndex::AM_PM
,
4198 AmPmValue::AM
, 0 ));
4202 sBuff
.append(rCal
.getDisplayName( CalendarDisplayIndex::AM_PM
,
4203 AmPmValue::PM
, 0 ));
4206 case NF_KEY_AP
: // A/P
4216 case NF_KEY_MI
: // M
4217 sBuff
.append(ImpIntToString(rNatNum
, nIx
, nMin
));
4219 case NF_KEY_MMI
: // MM
4220 sBuff
.append(ImpIntToString(rNatNum
, nIx
, nMin
, 2 ));
4223 sBuff
.append(ImpIntToString(rNatNum
, nIx
, nHour
));
4225 case NF_KEY_HH
: // HH
4226 sBuff
.append(ImpIntToString(rNatNum
, nIx
, nHour
, 2 ));
4229 sBuff
.append(ImpIntToString(rNatNum
, nIx
, nSec
));
4231 case NF_KEY_SS
: // SS
4232 sBuff
.append(ImpIntToString(rNatNum
, nIx
, nSec
, 2 ));
4235 sBuff
.append(rCal
.getDisplayString(
4236 CalendarDisplayCode::SHORT_MONTH
, nNatNum
));
4238 case NF_KEY_MM
: // MM
4239 sBuff
.append(rCal
.getDisplayString(
4240 CalendarDisplayCode::LONG_MONTH
, nNatNum
));
4242 case NF_KEY_MMM
: // MMM
4243 sBuff
.append(rCal
.getDisplayString( ImpUseMonthCase( nUseMonthCase
, NumFor
[nIx
],
4244 static_cast<NfKeywordIndex
>(rInfo
.nTypeArray
[i
])),
4247 case NF_KEY_MMMM
: // MMMM
4248 sBuff
.append(rCal
.getDisplayString( ImpUseMonthCase( nUseMonthCase
, NumFor
[nIx
],
4249 static_cast<NfKeywordIndex
>(rInfo
.nTypeArray
[i
])),
4252 case NF_KEY_MMMMM
: // MMMMM
4253 sBuff
.append(rCal
.getDisplayString( ImpUseMonthCase( nUseMonthCase
, NumFor
[nIx
],
4254 static_cast<NfKeywordIndex
>(rInfo
.nTypeArray
[i
])),
4258 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_QUARTER
, nNatNum
));
4260 case NF_KEY_QQ
: // QQ
4261 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_QUARTER
, nNatNum
));
4264 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY
, nNatNum
));
4266 case NF_KEY_DD
: // DD
4267 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY
, nNatNum
));
4269 case NF_KEY_DDD
: // DDD
4270 if ( bOtherCalendar
)
4272 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
4274 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME
, nNatNum
));
4275 if ( bOtherCalendar
)
4277 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
4280 case NF_KEY_DDDD
: // DDDD
4281 if ( bOtherCalendar
)
4283 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
4285 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
));
4286 if ( bOtherCalendar
)
4288 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
4291 case NF_KEY_YY
: // YY
4292 if ( bOtherCalendar
)
4294 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
4296 // Prepend a minus sign if Gregorian BCE and era is not displayed.
4297 if (lcl_isSignedYear( rCal
, NumFor
[nIx
] ))
4301 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_YEAR
, nNatNum
));
4302 if ( bOtherCalendar
)
4304 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
4307 case NF_KEY_YYYY
: // YYYY
4308 if ( bOtherCalendar
)
4310 SwitchToGregorianCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
4312 // Prepend a minus sign if Gregorian BCE and era is not displayed.
4313 if (lcl_isSignedYear( rCal
, NumFor
[nIx
] ))
4317 aYear
= rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR
, nNatNum
);
4318 if (aYear
.getLength() < 4 && !lcl_hasEra(NumFor
[nIx
]))
4320 using namespace comphelper::string
;
4321 // Ensure that year consists of at least 4 digits, so it
4322 // can be distinguished from 2 digits display and edited
4323 // without suddenly being hit by the 2-digit year magic.
4324 OUStringBuffer aBuf
;
4325 padToLength(aBuf
, 4 - aYear
.getLength(), '0');
4326 ::impTransliterate(aBuf
, NumFor
[nIx
].GetNatNum(), rNatNum
);
4332 sBuff
.append(aYear
);
4334 if ( bOtherCalendar
)
4336 SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
, *rCurrentLang
.GetCalendar() );
4339 case NF_KEY_EC
: // E
4340 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_YEAR
, nNatNum
));
4342 case NF_KEY_EEC
: // EE
4344 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR
, nNatNum
));
4346 case NF_KEY_NN
: // NN
4347 case NF_KEY_AAA
: // AAA
4348 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME
, nNatNum
));
4350 case NF_KEY_NNN
: // NNN
4351 case NF_KEY_AAAA
: // AAAA
4352 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
));
4354 case NF_KEY_NNNN
: // NNNN
4355 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME
, nNatNum
));
4356 sBuff
.append(rLoc().getLongDateDayOfWeekSep());
4358 case NF_KEY_WW
: // WW
4359 sBuff
.append(ImpIntToString(rNatNum
, nIx
, rCal
.getValue( CalendarFieldIndex::WEEK_OF_YEAR
)));
4362 ImpAppendEraG( sBuff
, rCal
, nNatNum
);
4364 case NF_KEY_GG
: // GG
4365 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::SHORT_ERA
, nNatNum
));
4367 case NF_KEY_GGG
: // GGG
4368 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_ERA
, nNatNum
));
4370 case NF_KEY_RR
: // RR => GGGEE
4371 sBuff
.append(rCal
.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA
, nNatNum
));
4375 if ( aOrgCalendar
.getLength() )
4377 rCal
.loadCalendar( aOrgCalendar
, rLoc().getLanguageTag().getLocale() ); // restore calendar
4382 bool SvNumberformat::ImpGetLogicalOutput(double fNumber
,
4384 const NativeNumberWrapper
& rNatNum
,
4385 OUStringBuffer
& sStr
) const
4388 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
4389 const sal_uInt16 nCnt
= NumFor
[nIx
].GetCount();
4390 for (sal_uInt16 j
= 0; j
< nCnt
; ++j
)
4392 switch (rInfo
.nTypeArray
[j
])
4394 case NF_KEY_BOOLEAN
:
4395 sStr
.append( fNumber
? rScan
.GetTrueString() : rScan
.GetFalseString());
4397 case NF_SYMBOLTYPE_STRING
:
4398 sStr
.append( rInfo
.sStrArray
[j
]);
4402 ::impTransliterate(sStr
, NumFor
[nIx
].GetNatNum(), rNatNum
);
4406 bool SvNumberformat::ImpGetNumberOutput(double fNumber
,
4409 const NativeNumberWrapper
& rNatNum
,
4410 OUStringBuffer
& sStr
) const
4416 bSign
= (nIx
== 0); // Not in the ones at the back;
4422 if ( std::signbit( fNumber
) )
4424 fNumber
= -fNumber
; // yes, -0.0 is possible, eliminate '-'
4427 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
4428 if (rInfo
.eScannedType
== SvNumFormatType::PERCENT
)
4430 if (fNumber
< D_MAX_D_BY_100
)
4436 sStr
= ImpSvNumberformatScan::sErrStr
;
4441 sal_Int32 nDecPos
= -1;
4442 bool bInteger
= false;
4443 if ( rInfo
.nThousand
!= FLAG_STANDARD_IN_FORMAT
)
4445 // Special formatting only if no GENERAL keyword in format code
4446 const sal_uInt16 nThousand
= rInfo
.nThousand
;
4447 tools::Long nPrecExp
;
4448 for (i
= 0; i
< nThousand
; i
++)
4450 if (fNumber
> D_MIN_M_BY_1000
)
4461 nPrecExp
= GetPrecExp( fNumber
);
4467 // Make sure that Calc's ROUND and formatted output agree
4468 fNumber
= rtl_math_round(fNumber
, rInfo
.nCntPost
, rtl_math_RoundingMode_Corrected
);
4469 if (rInfo
.nCntPost
) // Decimal places
4471 if ((rInfo
.nCntPost
+ nPrecExp
) > 15 && nPrecExp
< 15)
4473 sStr
= ::rtl::math::doubleToUString( fNumber
, rtl_math_StringFormat_F
, 15-nPrecExp
, '.');
4474 for (tools::Long l
= 15-nPrecExp
; l
< static_cast<tools::Long
>(rInfo
.nCntPost
); l
++)
4481 sStr
= ::rtl::math::doubleToUString( fNumber
, rtl_math_StringFormat_F
, rInfo
.nCntPost
, '.' );
4483 sStr
.stripStart('0'); // Strip leading zeros
4485 else if (fNumber
== 0.0) // Null
4487 // Nothing to be done here, keep empty string sStr,
4488 // ImpNumberFillWithThousands does the rest
4492 sStr
= ::rtl::math::doubleToUString( fNumber
, rtl_math_StringFormat_F
, 0, '.');
4493 sStr
.stripStart('0'); // Strip leading zeros
4495 nDecPos
= sStr
.indexOf('.' );
4498 const sal_Unicode
* p
= sStr
.getStr() + nDecPos
;
4499 while ( *++p
== '0' )
4505 sStr
.remove( nDecPos
, 1 ); // Remove .
4507 if (bSign
&& (sStr
.isEmpty() || checkForAll0s(sStr
))) // Only 00000
4509 bSign
= false; // Not -0.00
4511 } // End of != FLAG_STANDARD_IN_FORMAT
4514 j
= NumFor
[nIx
].GetCount()-1; // Last symbol
4516 bRes
|= ImpDecimalFill(rNatNum
, sStr
, fNumber
, nDecPos
, j
, nIx
, bInteger
, bStarFlag
);
4519 sStr
.insert(0, '-');
4521 ::impTransliterate(sStr
, NumFor
[nIx
].GetNatNum(), rNatNum
);
4525 bool SvNumberformat::ImpDecimalFill(const NativeNumberWrapper
& rNatNum
,
4526 OUStringBuffer
& sStr
, // number string
4527 double& rNumber
, // number
4528 sal_Int32 nDecPos
, // decimals start
4529 sal_uInt16 j
, // symbol index within format code
4530 sal_uInt16 nIx
, // subformat index
4531 bool bInteger
, // is integer
4532 bool bStarFlag
) const
4535 bool bFilled
= false; // Was filled?
4536 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
4537 sal_Int32 k
= sStr
.getLength(); // After last figure
4539 if (rInfo
.nCntPost
> 0)
4541 bool bTrailing
= true; // Trailing zeros?
4543 while (j
> 0 && // Backwards
4544 (nType
= rInfo
.nTypeArray
[j
]) != NF_SYMBOLTYPE_DECSEP
)
4548 case NF_SYMBOLTYPE_STAR
:
4551 bRes
= lcl_insertStarFillChar( sStr
, k
, rInfo
.sStrArray
[j
]);
4554 case NF_SYMBOLTYPE_BLANK
:
4555 if (rInfo
.sStrArray
[j
].getLength() >= 2)
4556 /*k = */ InsertBlanks(sStr
, k
, rInfo
.sStrArray
[j
][1] );
4558 case NF_SYMBOLTYPE_STRING
:
4559 case NF_SYMBOLTYPE_CURRENCY
:
4560 case NF_SYMBOLTYPE_PERCENT
:
4561 sStr
.insert(k
, rInfo
.sStrArray
[j
]);
4563 case NF_SYMBOLTYPE_THSEP
:
4564 if (rInfo
.nThousand
== 0)
4566 sStr
.insert(k
, rInfo
.sStrArray
[j
]);
4569 case NF_SYMBOLTYPE_DIGIT
:
4571 const OUString
& rStr
= rInfo
.sStrArray
[j
];
4572 const sal_Unicode
* p1
= rStr
.getStr();
4573 const sal_Unicode
* p
= p1
+ rStr
.getLength();
4574 // In case the number of decimals passed are less than the
4575 // "digits" given, append trailing '0' characters, which here
4576 // means insert them because literal strings may have been
4577 // appended already. If they weren't to be '0' characters
4578 // they'll be changed below, as if decimals with trailing zeros
4580 if (nDecPos
>= 0 && nDecPos
<= k
)
4582 sal_Int32 nAppend
= rStr
.getLength() - (k
- nDecPos
);
4583 while (nAppend
-- > 0)
4585 sStr
.insert( k
++, '0');
4588 while (k
&& p1
< p
--)
4590 const sal_Unicode c
= *p
;
4592 if ( sStr
[k
] != '0' )
4603 else if ( c
== '-' )
4611 else if ( c
== '?' )
4613 sStr
[ k
] = cBlankDigit
;
4616 else if ( !bFilled
) // #
4624 case NF_KEY_CCC
: // CCC currency
4625 sStr
.insert(k
, rScan
.GetCurAbbrev());
4627 case NF_KEY_GENERAL
: // Standard in the String
4629 OUStringBuffer sNum
;
4630 ImpGetOutputStandard(rNumber
, sNum
, rNatNum
);
4631 sNum
.stripStart('-');
4632 sStr
.insert(k
, sNum
);
4640 } // of decimal places
4642 bRes
|= ImpNumberFillWithThousands(rNatNum
, sStr
, rNumber
, k
, j
, nIx
, // Fill with . if needed
4643 rInfo
.nCntPre
, bStarFlag
, bFilled
);
4648 bool SvNumberformat::ImpNumberFillWithThousands( const NativeNumberWrapper
& rNatNum
,
4649 OUStringBuffer
& sBuff
, // number string
4650 double& rNumber
, // number
4651 sal_Int32 k
, // position within string
4652 sal_uInt16 j
, // symbol index within format code
4653 sal_uInt16 nIx
, // subformat index
4654 sal_Int32 nDigCnt
, // count of integer digits in format
4656 bool bAddDecSep
) const // add decimal separator if necessary
4659 sal_Int32 nLeadingStringChars
= 0; // inserted StringChars before number
4660 sal_Int32 nDigitCount
= 0; // count of integer digits from the right
4662 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
4663 // no normal thousands separators if number divided by thousands
4664 bool bDoThousands
= (rInfo
.nThousand
== 0);
4665 utl::DigitGroupingIterator
aGrouping( GetCurrentLanguageData().GetLocaleData()->getDigitGrouping());
4667 while (!bStop
) // backwards
4673 switch (rInfo
.nTypeArray
[j
])
4675 case NF_SYMBOLTYPE_DECSEP
:
4678 case NF_SYMBOLTYPE_STRING
:
4679 if ( rInfo
.nTypeArray
[j
] == NF_SYMBOLTYPE_STRING
&& nDigCnt
== 0 )
4681 // tdf#159930 no integer in format ".###"
4682 k
= 0; // insert string at the beginning
4685 case NF_SYMBOLTYPE_CURRENCY
:
4686 case NF_SYMBOLTYPE_PERCENT
:
4687 if ( rInfo
.nTypeArray
[j
] != NF_SYMBOLTYPE_DECSEP
|| bAddDecSep
)
4688 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4691 nLeadingStringChars
= nLeadingStringChars
+ rInfo
.sStrArray
[j
].getLength();
4694 case NF_SYMBOLTYPE_STAR
:
4697 bRes
= lcl_insertStarFillChar( sBuff
, k
, rInfo
.sStrArray
[j
]);
4700 case NF_SYMBOLTYPE_BLANK
:
4701 if (rInfo
.sStrArray
[j
].getLength() >= 2)
4702 /*k = */ InsertBlanks(sBuff
, k
, rInfo
.sStrArray
[j
][1] );
4704 case NF_SYMBOLTYPE_THSEP
:
4705 // #i7284# #102685# Insert separator also if number is divided
4706 // by thousands and the separator is specified somewhere in
4707 // between and not only at the end.
4708 // #i12596# But do not insert if it's a parenthesized negative
4710 // In fact, do not insert if divided and regex [0#,],[^0#] and
4711 // no other digit symbol follows (which was already detected
4712 // during scan of format code, otherwise there would be no
4713 // division), else do insert. Same in ImpNumberFill() below.
4714 if ( !bDoThousands
&& j
< NumFor
[nIx
].GetCount()-1 )
4716 bDoThousands
= ((j
== 0) ||
4717 (rInfo
.nTypeArray
[j
-1] != NF_SYMBOLTYPE_DIGIT
&&
4718 rInfo
.nTypeArray
[j
-1] != NF_SYMBOLTYPE_THSEP
) ||
4719 (rInfo
.nTypeArray
[j
+1] == NF_SYMBOLTYPE_DIGIT
));
4725 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4727 else if (nDigitCount
< nDigCnt
)
4729 // Leading '#' displays nothing (e.g. no leading
4730 // separator for numbers <1000 with #,##0 format).
4731 // Leading '?' displays blank.
4732 // Everything else, including nothing, displays the
4734 sal_Unicode cLeader
= 0;
4735 if (j
> 0 && rInfo
.nTypeArray
[j
-1] == NF_SYMBOLTYPE_DIGIT
)
4737 const OUString
& rStr
= rInfo
.sStrArray
[j
-1];
4738 sal_Int32 nLen
= rStr
.getLength();
4741 cLeader
= rStr
[ nLen
- 1 ];
4750 // replace thousand separator with blank
4751 sBuff
.insert(k
, ' ');
4754 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4757 aGrouping
.advance();
4760 case NF_SYMBOLTYPE_DIGIT
:
4762 const OUString
& rStr
= rInfo
.sStrArray
[j
];
4763 const sal_Unicode
* p1
= rStr
.getStr();
4764 const sal_Unicode
* p
= p1
+ rStr
.getLength();
4777 sBuff
.insert(0, '0');
4780 sBuff
.insert(0, cBlankDigit
);
4784 if (nDigitCount
== nDigCnt
&& k
> 0)
4786 // more digits than specified
4787 ImpDigitFill(sBuff
, 0, k
, nIx
, nDigitCount
, aGrouping
);
4792 case NF_KEY_CCC
: // CCC currency
4793 sBuff
.insert(k
, rScan
.GetCurAbbrev());
4795 case NF_KEY_GENERAL
: // "General" in string
4797 OUStringBuffer sNum
;
4798 ImpGetOutputStandard(rNumber
, sNum
, rNatNum
);
4799 sNum
.stripStart('-');
4800 sBuff
.insert(k
, sNum
);
4806 j
--; // next format code string
4809 k
= k
+ nLeadingStringChars
; // MSC converts += to int and then warns, so ...
4810 if (k
> nLeadingStringChars
)
4812 ImpDigitFill(sBuff
, nLeadingStringChars
, k
, nIx
, nDigitCount
, aGrouping
);
4817 void SvNumberformat::ImpDigitFill(OUStringBuffer
& sStr
, // number string
4818 sal_Int32 nStart
, // start of digits
4819 sal_Int32
& k
, // position within string
4820 sal_uInt16 nIx
, // subformat index
4821 sal_Int32
& nDigitCount
, // count of integer digits from the right so far
4822 utl::DigitGroupingIterator
& rGrouping
) const // current grouping
4824 if (NumFor
[nIx
].Info().bThousand
) // Only if grouping fill in separators
4826 const OUString
& rThousandSep
= GetCurrentLanguageData().GetNumThousandSep();
4829 if (nDigitCount
== rGrouping
.getPos())
4831 sStr
.insert( k
, rThousandSep
);
4832 rGrouping
.advance();
4844 bool SvNumberformat::ImpNumberFill( const NativeNumberWrapper
& rNatNum
,
4845 OUStringBuffer
& sBuff
, // number string
4846 double& rNumber
, // number for "General" format
4847 sal_Int32
& k
, // position within string
4848 sal_uInt16
& j
, // symbol index within format code
4849 sal_uInt16 nIx
, // subformat index
4850 short eSymbolType
, // type of stop condition
4852 bool bInsertRightBlank
) const // insert blank on right for denominator (default = false)
4856 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
4857 // no normal thousands separators if number divided by thousands
4858 bool bDoThousands
= (rInfo
.nThousand
== 0);
4859 bool bFoundNumber
= false;
4862 k
= sBuff
.getLength(); // behind last digit
4864 while (!bStop
&& (nType
= rInfo
.nTypeArray
[j
]) != eSymbolType
) // Backwards
4868 case NF_SYMBOLTYPE_STAR
:
4871 if ( bFoundNumber
&& eSymbolType
!= NF_SYMBOLTYPE_EXP
)
4872 k
= 0; // tdf#100842 jump to beginning of number before inserting something else
4873 bRes
= lcl_insertStarFillChar( sBuff
, k
, rInfo
.sStrArray
[j
]);
4876 case NF_SYMBOLTYPE_BLANK
:
4877 if (rInfo
.sStrArray
[j
].getLength() >= 2)
4879 if ( bFoundNumber
&& eSymbolType
!= NF_SYMBOLTYPE_EXP
)
4880 k
= 0; // tdf#100842 jump to beginning of number before inserting something else
4881 k
= InsertBlanks(sBuff
, k
, rInfo
.sStrArray
[j
][1] );
4884 case NF_SYMBOLTYPE_THSEP
:
4885 // Same as in ImpNumberFillWithThousands() above, do not insert
4886 // if divided and regex [0#,],[^0#] and no other digit symbol
4887 // follows (which was already detected during scan of format
4888 // code, otherwise there would be no division), else do insert.
4889 if ( !bDoThousands
&& j
< NumFor
[nIx
].GetCount()-1 )
4891 bDoThousands
= ((j
== 0) ||
4892 (rInfo
.nTypeArray
[j
-1] != NF_SYMBOLTYPE_DIGIT
&&
4893 rInfo
.nTypeArray
[j
-1] != NF_SYMBOLTYPE_THSEP
) ||
4894 (rInfo
.nTypeArray
[j
+1] == NF_SYMBOLTYPE_DIGIT
));
4896 if ( bDoThousands
&& k
> 0 )
4898 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4901 case NF_SYMBOLTYPE_DIGIT
:
4903 bFoundNumber
= true;
4904 sal_uInt16 nPosInsertBlank
= bInsertRightBlank
? k
: 0; // left alignment of denominator
4905 const OUString
& rStr
= rInfo
.sStrArray
[j
];
4906 const sal_Unicode
* p1
= rStr
.getStr();
4907 const sal_Unicode
* p
= p1
+ rStr
.getLength();
4919 sBuff
.insert(0, '0');
4922 sBuff
.insert(nPosInsertBlank
, cBlankDigit
);
4929 case NF_KEY_CCC
: // CCC currency
4930 sBuff
.insert(k
, rScan
.GetCurAbbrev());
4932 case NF_KEY_GENERAL
: // Standard in the String
4934 OUStringBuffer sNum
;
4935 bFoundNumber
= true;
4936 ImpGetOutputStandard(rNumber
, sNum
, rNatNum
);
4937 sNum
.stripStart('-');
4938 sBuff
.insert(k
, sNum
);
4941 case NF_SYMBOLTYPE_FRAC_FDIV
: // Do Nothing
4949 if ( bFoundNumber
&& eSymbolType
!= NF_SYMBOLTYPE_EXP
)
4950 k
= 0; // tdf#100842 jump to beginning of number before inserting something else
4951 sBuff
.insert(k
, rInfo
.sStrArray
[j
]);
4962 void SvNumberformat::GetFormatSpecialInfo(bool& bThousand
,
4964 sal_uInt16
& nPrecision
,
4965 sal_uInt16
& nLeadingCnt
) const
4967 // as before: take info from nNumFor=0 for whole format (for dialog etc.)
4969 SvNumFormatType nDummyType
;
4970 GetNumForInfo( 0, nDummyType
, bThousand
, nPrecision
, nLeadingCnt
);
4972 // "negative in red" is only useful for the whole format
4974 const Color
* pColor
= NumFor
[1].GetColor();
4975 IsRed
= fLimit1
== 0.0 && fLimit2
== 0.0 && pColor
4976 && (*pColor
== ImpSvNumberformatScan::GetRedColor());
4979 void SvNumberformat::GetNumForInfo( sal_uInt16 nNumFor
, SvNumFormatType
& rScannedType
,
4980 bool& bThousand
, sal_uInt16
& nPrecision
, sal_uInt16
& nLeadingCnt
) const
4982 // take info from a specified sub-format (for XML export)
4989 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nNumFor
].Info();
4990 rScannedType
= rInfo
.eScannedType
;
4991 bThousand
= rInfo
.bThousand
;
4992 nPrecision
= (rInfo
.eScannedType
== SvNumFormatType::FRACTION
)
4993 ? rInfo
.nCntExp
// number of denominator digits for fraction
4995 sal_Int32 nPosHash
= 1;
4996 if ( rInfo
.eScannedType
== SvNumFormatType::FRACTION
&&
4997 ( (nPosHash
+= GetDenominatorString(nNumFor
).indexOf('#')) > 0 ) )
4998 nPrecision
-= nPosHash
;
4999 if (bStandard
&& rInfo
.eScannedType
== SvNumFormatType::NUMBER
)
5009 const sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
5010 while (!bStop
&& i
< nCnt
)
5012 short nType
= rInfo
.nTypeArray
[i
];
5013 if ( nType
== NF_SYMBOLTYPE_DIGIT
)
5015 const sal_Unicode
* p
= rInfo
.sStrArray
[i
].getStr();
5020 while ( *p
== '0' || *p
== '?' )
5026 else if (nType
== NF_SYMBOLTYPE_DECSEP
5027 || nType
== NF_SYMBOLTYPE_EXP
5028 || nType
== NF_SYMBOLTYPE_FRACBLANK
) // Fraction: stop after integer part,
5029 { // do not count '0' of fraction
5037 const OUString
* SvNumberformat::GetNumForString( sal_uInt16 nNumFor
, sal_uInt16 nPos
,
5038 bool bString
/* = false */ ) const
5044 sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
5049 if ( nPos
== 0xFFFF )
5054 short const * pType
= NumFor
[nNumFor
].Info().nTypeArray
.data() + nPos
;
5055 while ( nPos
> 0 && (*pType
!= NF_SYMBOLTYPE_STRING
) &&
5056 (*pType
!= NF_SYMBOLTYPE_CURRENCY
) )
5061 if ( (*pType
!= NF_SYMBOLTYPE_STRING
) && (*pType
!= NF_SYMBOLTYPE_CURRENCY
) )
5067 else if ( nPos
> nCnt
- 1 )
5074 short const * pType
= NumFor
[nNumFor
].Info().nTypeArray
.data() + nPos
;
5075 while ( nPos
< nCnt
&& (*pType
!= NF_SYMBOLTYPE_STRING
) &&
5076 (*pType
!= NF_SYMBOLTYPE_CURRENCY
) )
5081 if ( nPos
>= nCnt
|| ((*pType
!= NF_SYMBOLTYPE_STRING
) &&
5082 (*pType
!= NF_SYMBOLTYPE_CURRENCY
)) )
5087 return &NumFor
[nNumFor
].Info().sStrArray
[nPos
];
5090 short SvNumberformat::GetNumForType( sal_uInt16 nNumFor
, sal_uInt16 nPos
) const
5096 sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
5101 if ( nPos
== 0xFFFF )
5105 else if ( nPos
> nCnt
- 1 )
5109 return NumFor
[nNumFor
].Info().nTypeArray
[nPos
];
5112 bool SvNumberformat::IsNegativeWithoutSign() const
5114 if ( IsSecondSubformatRealNegative() )
5116 const OUString
* pStr
= GetNumForString( 1, 0, true );
5119 return !HasStringNegativeSign( *pStr
);
5125 bool SvNumberformat::IsNegativeInBracket() const
5127 sal_uInt16 nCnt
= NumFor
[1].GetCount();
5132 auto& tmp
= NumFor
[1].Info().sStrArray
;
5133 return tmp
[0] == "(" && tmp
[nCnt
-1] == ")";
5136 bool SvNumberformat::HasPositiveBracketPlaceholder() const
5138 sal_uInt16 nCnt
= NumFor
[0].GetCount();
5139 return NumFor
[0].Info().sStrArray
[nCnt
-1] == "_)";
5142 DateOrder
SvNumberformat::GetDateOrder() const
5144 if ( eType
& SvNumFormatType::DATE
)
5146 auto& rTypeArray
= NumFor
[0].Info().nTypeArray
;
5147 sal_uInt16 nCnt
= NumFor
[0].GetCount();
5148 for ( sal_uInt16 j
=0; j
<nCnt
; j
++ )
5150 switch ( rTypeArray
[j
] )
5154 return DateOrder::DMY
;
5160 return DateOrder::MDY
;
5167 return DateOrder::YMD
;
5173 SAL_WARN( "svl.numbers", "SvNumberformat::GetDateOrder: no date" );
5175 return rLoc().getDateOrder();
5178 sal_uInt32
SvNumberformat::GetExactDateOrder() const
5180 sal_uInt32 nRet
= 0;
5181 if ( !(eType
& SvNumFormatType::DATE
) )
5183 SAL_WARN( "svl.numbers", "SvNumberformat::GetExactDateOrder: no date" );
5186 auto& rTypeArray
= NumFor
[0].Info().nTypeArray
;
5187 sal_uInt16 nCnt
= NumFor
[0].GetCount();
5189 for ( sal_uInt16 j
=0; j
<nCnt
&& nShift
< 3; j
++ )
5191 switch ( rTypeArray
[j
] )
5195 nRet
= (nRet
<< 8) | 'D';
5203 nRet
= (nRet
<< 8) | 'M';
5212 nRet
= (nRet
<< 8) | 'Y';
5220 void SvNumberformat::GetConditions( SvNumberformatLimitOps
& rOper1
, double& rVal1
,
5221 SvNumberformatLimitOps
& rOper2
, double& rVal2
) const
5229 const Color
* SvNumberformat::GetColor( sal_uInt16 nNumFor
) const
5235 return NumFor
[nNumFor
].GetColor();
5238 static void lcl_SvNumberformat_AddLimitStringImpl( OUString
& rStr
,
5239 SvNumberformatLimitOps eOp
,
5240 double fLimit
, std::u16string_view rDecSep
)
5242 if ( eOp
== NUMBERFORMAT_OP_NO
)
5247 case NUMBERFORMAT_OP_EQ
:
5250 case NUMBERFORMAT_OP_NE
:
5253 case NUMBERFORMAT_OP_LT
:
5256 case NUMBERFORMAT_OP_LE
:
5259 case NUMBERFORMAT_OP_GT
:
5262 case NUMBERFORMAT_OP_GE
:
5266 SAL_WARN( "svl.numbers", "unsupported number format" );
5269 rStr
+= ::rtl::math::doubleToUString( fLimit
,
5270 rtl_math_StringFormat_Automatic
, rtl_math_DecimalPlaces_Max
,
5275 static void lcl_insertLCID( OUStringBuffer
& rFormatStr
, sal_uInt32 nLCID
, sal_Int32 nPosInsertLCID
, bool bDBNumInserted
)
5279 if (nPosInsertLCID
== rFormatStr
.getLength() && !bDBNumInserted
)
5280 // No format code, no locale.
5283 auto aLCIDString
= OUString::number( nLCID
, 16 ).toAsciiUpperCase();
5284 // Search for only last DBNum which is the last element before insertion position
5285 if ( bDBNumInserted
&& nPosInsertLCID
>= 8
5286 && aLCIDString
.length
> 4
5287 && OUString::unacquired(rFormatStr
).match( "[DBNum", nPosInsertLCID
-8) )
5288 { // remove DBNumX code if long LCID
5289 nPosInsertLCID
-= 8;
5290 rFormatStr
.remove( nPosInsertLCID
, 8 );
5292 rFormatStr
.insert( nPosInsertLCID
, "[$-" + aLCIDString
+ "]" );
5295 /** Increment nAlphabetID for CJK numerals
5296 * +1 for financial numerals [NatNum2]
5297 * +2 for Arabic fullwidth numerals [NatNum3]
5299 static void lcl_incrementAlphabetWithNatNum ( sal_uInt32
& nAlphabetID
, sal_uInt32 nNatNum
)
5301 if ( nNatNum
== 2) // financial
5303 else if ( nNatNum
== 3)
5305 nAlphabetID
= nAlphabetID
<< 24;
5308 OUString
SvNumberformat::GetMappedFormatstring( const NfKeywordTable
& rKeywords
,
5309 const LocaleDataWrapper
& rLocWrp
,
5310 LanguageType nOriginalLang
/* =LANGUAGE_DONTKNOW */,
5311 bool bSystemLanguage
/* =false */ ) const
5313 OUStringBuffer aStr
;
5314 if (maLocale
.meSubstitute
!= LocaleType::Substitute::NONE
)
5316 // XXX: theoretically this could clash with the first subformat's
5317 // lcl_insertLCID() below, in practice as long as it is used for system
5318 // time and date modifiers it shouldn't (i.e. there is no calendar or
5319 // numeral specified as well).
5320 aStr
.append("[$-" + maLocale
.generateCode() + "]");
5323 // 1 subformat matches all if no condition specified,
5324 bDefault
[0] = ( NumFor
[1].GetCount() == 0 && eOp1
== NUMBERFORMAT_OP_NO
);
5325 // with 2 subformats [>=0];[<0] is implied if no condition specified
5326 bDefault
[1] = ( !bDefault
[0] && NumFor
[2].GetCount() == 0 &&
5327 eOp1
== NUMBERFORMAT_OP_GE
&& fLimit1
== 0.0 &&
5328 eOp2
== NUMBERFORMAT_OP_NO
&& fLimit2
== 0.0 );
5329 // with 3 or more subformats [>0];[<0];[=0] is implied if no condition specified,
5330 // note that subformats may be empty (;;;) and NumFor[2].GetCount()>0 is not checked.
5331 bDefault
[2] = ( !bDefault
[0] && !bDefault
[1] &&
5332 eOp1
== NUMBERFORMAT_OP_GT
&& fLimit1
== 0.0 &&
5333 eOp2
== NUMBERFORMAT_OP_LT
&& fLimit2
== 0.0 );
5334 bool bDefaults
= bDefault
[0] || bDefault
[1] || bDefault
[2];
5335 // from now on bDefault[] values are used to append empty subformats at the end
5336 bDefault
[3] = false;
5339 // conditions specified
5340 if ( eOp1
!= NUMBERFORMAT_OP_NO
&& eOp2
== NUMBERFORMAT_OP_NO
)
5342 bDefault
[0] = bDefault
[1] = true; // [];x
5344 else if ( eOp1
!= NUMBERFORMAT_OP_NO
&& eOp2
!= NUMBERFORMAT_OP_NO
&&
5345 NumFor
[2].GetCount() == 0 )
5347 bDefault
[0] = bDefault
[1] = bDefault
[2] = bDefault
[3] = true; // [];[];;
5349 // nothing to do if conditions specified for every subformat
5351 else if ( bDefault
[0] )
5353 bDefault
[0] = false; // a single unconditional subformat is never delimited
5357 if ( bDefault
[2] && NumFor
[2].GetCount() == 0 && NumFor
[1].GetCount() > 0 )
5359 bDefault
[3] = true; // special cases x;x;; and ;x;;
5361 for ( int i
=0; i
<3 && !bDefault
[i
]; ++i
)
5366 int nSem
= 0; // needed ';' delimiters
5367 int nSub
= 0; // subformats delimited so far
5368 for ( int n
=0; n
<4; n
++ )
5370 if ( n
> 0 && NumFor
[n
].Info().eScannedType
!= SvNumFormatType::UNDEFINED
)
5381 lcl_SvNumberformat_AddLimitStringImpl( aPrefix
, eOp1
,
5382 fLimit1
, rLocWrp
.getNumDecimalSep() );
5385 lcl_SvNumberformat_AddLimitStringImpl( aPrefix
, eOp2
,
5386 fLimit2
, rLocWrp
.getNumDecimalSep() );
5391 const OUString
& rColorName
= NumFor
[n
].GetColorName();
5392 if ( !rColorName
.isEmpty() )
5394 const NfKeywordTable
& rKey
= rScan
.GetKeywords();
5395 for ( int j
= NF_KEY_FIRSTCOLOR
; j
<= NF_KEY_LASTCOLOR
; j
++ )
5397 if ( rKey
[j
] == rColorName
)
5399 aPrefix
+= "[" + rKeywords
[j
] + "]";
5405 SvNumberNatNum aNatNum
= NumFor
[n
].GetNatNum();
5406 bool bDBNumInserted
= false;
5407 if (aNatNum
.IsComplete() && (aNatNum
.GetDBNum() > 0 || nOriginalLang
!= LANGUAGE_DONTKNOW
))
5408 { // GetFormatStringForExcel() may have changed language to en_US
5409 if (aNatNum
.GetLang() == LANGUAGE_ENGLISH_US
&& nOriginalLang
!= LANGUAGE_DONTKNOW
)
5410 aNatNum
.SetLang( nOriginalLang
);
5411 if ( aNatNum
.GetDBNum() > 0 )
5413 aPrefix
+= "[DBNum" + OUString::number( aNatNum
.GetDBNum() ) + "]";
5414 bDBNumInserted
= true;
5418 sal_uInt16 nCnt
= NumFor
[n
].GetCount();
5419 if ( nSem
&& (nCnt
|| !aPrefix
.isEmpty()) )
5421 for ( ; nSem
; --nSem
)
5425 for ( ; nSub
<= n
; ++nSub
)
5427 bDefault
[nSub
] = false;
5431 if ( !aPrefix
.isEmpty() )
5433 aStr
.append( aPrefix
);
5435 sal_Int32 nPosHaveLCID
= -1;
5436 sal_Int32 nPosInsertLCID
= aStr
.getLength();
5437 sal_uInt32 nCalendarID
= 0x0000000; // Excel ID of calendar used in sub-format see tdf#36038
5438 constexpr sal_uInt32 kCalGengou
= 0x0030000;
5441 auto& rTypeArray
= NumFor
[n
].Info().nTypeArray
;
5442 auto& rStrArray
= NumFor
[n
].Info().sStrArray
;
5443 for ( sal_uInt16 j
=0; j
<nCnt
; j
++ )
5445 if ( 0 <= rTypeArray
[j
] && rTypeArray
[j
] < NF_KEYWORD_ENTRIES_COUNT
)
5447 aStr
.append( rKeywords
[rTypeArray
[j
]] );
5448 if( NF_KEY_NNNN
== rTypeArray
[j
] )
5450 aStr
.append( rLocWrp
.getLongDateDayOfWeekSep() );
5452 switch (rTypeArray
[j
])
5458 // Implicit secondary (non-gregorian) calendar.
5459 // Currently only for ja-JP.
5460 /* TODO: same for all locales in
5461 * LocaleDataWrapper::doesSecondaryCalendarUseEC() ?
5462 * Should split the locales off that then. */
5465 const LanguageType nLang
= MsLangId::getRealLanguage( nOriginalLang
);
5466 if (nLang
== LANGUAGE_JAPANESE
)
5467 nCalendarID
= kCalGengou
;
5476 switch ( rTypeArray
[j
] )
5478 case NF_SYMBOLTYPE_DECSEP
:
5479 aStr
.append( rLocWrp
.getNumDecimalSep() );
5481 case NF_SYMBOLTYPE_THSEP
:
5482 aStr
.append( rLocWrp
.getNumThousandSep() );
5484 case NF_SYMBOLTYPE_EXP
:
5485 aStr
.append( rKeywords
[NF_KEY_E
] );
5486 if ( rStrArray
[j
].getLength() > 1 && rStrArray
[j
][1] == '+' )
5489 // tdf#102370: Excel code for exponent without sign
5492 case NF_SYMBOLTYPE_DATESEP
:
5493 aStr
.append( rLocWrp
.getDateSep() );
5495 case NF_SYMBOLTYPE_TIMESEP
:
5496 aStr
.append( rLocWrp
.getTimeSep() );
5498 case NF_SYMBOLTYPE_TIME100SECSEP
:
5499 aStr
.append( rLocWrp
.getTime100SecSep() );
5501 case NF_SYMBOLTYPE_FRACBLANK
:
5502 case NF_SYMBOLTYPE_STRING
:
5503 if ( rStrArray
[j
].getLength() == 1 )
5505 if ( rTypeArray
[j
] == NF_SYMBOLTYPE_STRING
)
5506 aStr
.append( '\\' );
5507 aStr
.append( rStrArray
[j
] );
5511 aStr
.append( "\"" + rStrArray
[j
] + "\"" );
5514 case NF_SYMBOLTYPE_CALDEL
:
5517 if ( rStrArray
[j
+1] == "gengou" )
5519 nCalendarID
= kCalGengou
;
5521 else if ( rStrArray
[j
+1] == "hijri" )
5523 nCalendarID
= 0x0060000;
5525 else if ( rStrArray
[j
+1] == "buddhist" )
5527 nCalendarID
= 0x0070000;
5529 else if ( rStrArray
[j
+1] == "jewish" )
5531 nCalendarID
= 0x0080000;
5533 // Other calendars (see tdf#36038) not corresponding between LibO and XL.
5534 // However, skip any calendar modifier and don't write
5535 // as format code (if not as literal string).
5538 case NF_SYMBOLTYPE_CURREXT
:
5539 nPosHaveLCID
= aStr
.getLength();
5540 aStr
.append( rStrArray
[j
] );
5543 aStr
.append( rStrArray
[j
] );
5548 sal_uInt32 nAlphabetID
= 0x0000000; // Excel ID of alphabet used for numerals see tdf#36038
5549 LanguageType nLanguageID
= LANGUAGE_SYSTEM
;
5550 if ( aNatNum
.IsComplete() )
5552 nLanguageID
= MsLangId::getRealLanguage( aNatNum
.GetLang());
5553 if ( aNatNum
.GetNatNum() == 0 )
5555 nAlphabetID
= 0x01000000; // Arabic-european numerals
5557 else if ( nCalendarID
> 0 || aNatNum
.GetDBNum() == 0 || aNatNum
.GetDBNum() == aNatNum
.GetNatNum() )
5558 { // if no DBNum code then use long LCID
5559 // if DBNum value != NatNum value, use DBNum and not extended LCID
5560 // if calendar, then DBNum will be removed
5561 LanguageType pri
= primary(nLanguageID
);
5562 if ( pri
== LANGUAGE_ARABIC_PRIMARY_ONLY
)
5563 nAlphabetID
= 0x02000000; // Arabic-indic numerals
5564 else if ( pri
== primary(LANGUAGE_FARSI
) )
5565 nAlphabetID
= 0x03000000; // Farsi numerals
5566 else if ( pri
.anyOf(
5567 primary(LANGUAGE_HINDI
),
5568 primary(LANGUAGE_MARATHI
),
5569 primary(LANGUAGE_NEPALI
) ))
5570 nAlphabetID
= 0x04000000; // Devanagari numerals
5571 else if ( pri
== primary(LANGUAGE_BENGALI
) )
5572 nAlphabetID
= 0x05000000; // Bengali numerals
5573 else if ( pri
== primary(LANGUAGE_PUNJABI
) )
5575 if ( nLanguageID
== LANGUAGE_PUNJABI_ARABIC_LSO
)
5576 nAlphabetID
= 0x02000000; // Arabic-indic numerals
5578 nAlphabetID
= 0x06000000; // Punjabi numerals
5580 else if ( pri
== primary(LANGUAGE_GUJARATI
) )
5581 nAlphabetID
= 0x07000000; // Gujarati numerals
5582 else if ( pri
== primary(LANGUAGE_ODIA
))
5583 nAlphabetID
= 0x08000000; // Odia (Oriya) numerals
5584 else if ( pri
== primary(LANGUAGE_TAMIL
))
5585 nAlphabetID
= 0x09000000; // Tamil numerals
5586 else if ( pri
== primary(LANGUAGE_TELUGU
))
5587 nAlphabetID
= 0x0A000000; // Telugu numerals
5588 else if ( pri
== primary(LANGUAGE_KANNADA
))
5589 nAlphabetID
= 0x0B000000; // Kannada numerals
5590 else if ( pri
== primary(LANGUAGE_MALAYALAM
))
5591 nAlphabetID
= 0x0C000000; // Malayalam numerals
5592 else if ( pri
== primary(LANGUAGE_THAI
))
5594 // The Thai T NatNum modifier during Xcl export.
5595 if ( rKeywords
[NF_KEY_THAI_T
] == "T" )
5596 nAlphabetID
= 0x0D000000; // Thai numerals
5598 else if ( pri
== primary(LANGUAGE_LAO
))
5599 nAlphabetID
= 0x0E000000; // Lao numerals
5600 else if ( pri
== primary(LANGUAGE_TIBETAN
))
5601 nAlphabetID
= 0x0F000000; // Tibetan numerals
5602 else if ( pri
== primary(LANGUAGE_BURMESE
))
5603 nAlphabetID
= 0x10000000; // Burmese numerals
5604 else if ( pri
== primary(LANGUAGE_TIGRIGNA_ETHIOPIA
))
5605 nAlphabetID
= 0x11000000; // Tigrigna numerals
5606 else if ( pri
== primary(LANGUAGE_KHMER
))
5607 nAlphabetID
= 0x12000000; // Khmer numerals
5608 else if ( pri
== primary(LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
))
5610 if ( nLanguageID
!= LANGUAGE_MONGOLIAN_CYRILLIC_MONGOLIA
5611 && nLanguageID
!= LANGUAGE_MONGOLIAN_CYRILLIC_LSO
)
5612 nAlphabetID
= 0x13000000; // Mongolian numerals
5615 else if ( pri
== primary(LANGUAGE_JAPANESE
))
5618 lcl_incrementAlphabetWithNatNum ( nAlphabetID
, aNatNum
.GetNatNum() );
5620 else if ( pri
== primary(LANGUAGE_CHINESE
))
5622 if ( nLanguageID
== LANGUAGE_CHINESE_TRADITIONAL
5623 || nLanguageID
== LANGUAGE_CHINESE_HONGKONG
5624 || nLanguageID
== LANGUAGE_CHINESE_MACAU
)
5627 lcl_incrementAlphabetWithNatNum ( nAlphabetID
, aNatNum
.GetNatNum() );
5629 else // LANGUAGE_CHINESE_SIMPLIFIED
5632 lcl_incrementAlphabetWithNatNum ( nAlphabetID
, aNatNum
.GetNatNum() );
5635 else if ( pri
== primary(LANGUAGE_KOREAN
))
5637 if ( aNatNum
.GetNatNum() == 9 ) // Hangul
5639 nAlphabetID
= 0x27000000;
5644 lcl_incrementAlphabetWithNatNum ( nAlphabetID
, aNatNum
.GetNatNum() );
5648 // Add LCID to DBNum
5649 if ( aNatNum
.GetDBNum() > 0 && nLanguageID
== LANGUAGE_SYSTEM
)
5650 nLanguageID
= MsLangId::getRealLanguage( aNatNum
.GetLang());
5652 else if (nPosHaveLCID
< 0)
5654 // Do not insert a duplicated LCID that was already given with a
5655 // currency format as [$R-1C09]
5656 if (!bSystemLanguage
&& nOriginalLang
!= LANGUAGE_DONTKNOW
)
5658 // Explicit locale, write only to the first subformat.
5660 nLanguageID
= MsLangId::getRealLanguage( nOriginalLang
);
5662 else if (bSystemLanguage
&& maLocale
.meLanguageWithoutLocaleData
!= LANGUAGE_DONTKNOW
)
5664 // Explicit locale but no locale data thus assigned to system
5665 // locale, preserve for roundtrip, write only to the first
5668 nLanguageID
= maLocale
.meLanguageWithoutLocaleData
;
5671 if ( nCalendarID
> 0 )
5672 { // Add alphabet and language to calendar
5673 if ( nAlphabetID
== 0 )
5674 nAlphabetID
= 0x01000000;
5675 if ( nLanguageID
== LANGUAGE_SYSTEM
&& nOriginalLang
!= LANGUAGE_DONTKNOW
)
5676 nLanguageID
= nOriginalLang
;
5678 lcl_insertLCID( aStr
, nAlphabetID
+ nCalendarID
+ static_cast<sal_uInt16
>(nLanguageID
), nPosInsertLCID
,
5681 for ( ; nSub
<4 && bDefault
[nSub
]; ++nSub
)
5682 { // append empty subformats
5685 return aStr
.makeStringAndClear();
5689 OUString
SvNumberformat::ImpGetNatNumString(const SvNumberNatNum
& rNum
,
5690 sal_Int64 nVal
, sal_uInt16 nMinDigits
,
5691 const NativeNumberWrapper
& rNatNum
)
5696 if ( nMinDigits
== 2 )
5698 // speed up the most common case
5699 if ( 0 <= nVal
&& nVal
< 10 )
5701 sal_Unicode aBuf
[2];
5703 aBuf
[1] = '0' + nVal
;
5704 aStr
= OUString(aBuf
, SAL_N_ELEMENTS(aBuf
));
5708 aStr
= OUString::number( nVal
);
5713 OUString
aValStr( OUString::number( nVal
) );
5714 if ( aValStr
.getLength() >= nMinDigits
)
5720 OUStringBuffer aBuf
;
5721 for(sal_Int32 index
= 0; index
< nMinDigits
- aValStr
.getLength(); ++index
)
5725 aBuf
.append(aValStr
);
5726 aStr
= aBuf
.makeStringAndClear();
5732 aStr
= OUString::number( nVal
);
5734 return ::impTransliterate(aStr
, rNum
, rNatNum
);
5737 OUString
SvNumberformat::impTransliterateImpl(const OUString
& rStr
,
5738 const SvNumberNatNum
& rNum
,
5739 const sal_uInt16 nDateKey
,
5740 const NativeNumberWrapper
& rNatNum
) const
5742 // no KEYWORD=argument list in NatNum12
5743 if (rNum
.GetParams().indexOf('=') == -1)
5744 return ::impTransliterateImpl( rStr
, rNum
, rNatNum
);
5746 const NfKeywordTable
& rKeywords
= rScan
.GetKeywords();
5748 // Format: KEYWORD=numbertext_prefix, ..., for example:
5749 // [NatNum12 YYYY=title ordinal,MMMM=article, D=ordinal-number]
5750 sal_Int32 nField
= -1;
5753 nField
= rNum
.GetParams().indexOf(Concat2View(rKeywords
[nDateKey
] + "="), ++nField
);
5755 while (nField
!= -1 && nField
!= 0 &&
5756 (rNum
.GetParams()[nField
- 1] != ',' &&
5757 rNum
.GetParams()[nField
- 1] != ' '));
5759 // no format specified for actual keyword
5763 sal_Int32 nKeywordLen
= rKeywords
[nDateKey
].getLength() + 1;
5764 sal_Int32 nFieldEnd
= rNum
.GetParams().indexOf(',', nField
);
5766 if (nFieldEnd
== -1)
5767 nFieldEnd
= rNum
.GetParams().getLength();
5769 css::lang::Locale
aLocale( LanguageTag( rNum
.GetLang() ).getLocale() );
5771 return rNatNum
.getNativeNumberStringParams(
5772 rStr
, aLocale
, rNum
.GetNatNum(),
5773 rNum
.GetParams().copy(nField
+ nKeywordLen
, nFieldEnd
- nField
- nKeywordLen
));
5776 void SvNumberformat::GetNatNumXml( css::i18n::NativeNumberXmlAttributes2
& rAttr
,
5777 sal_uInt16 nNumFor
, const NativeNumberWrapper
& rNatNum
) const
5781 const SvNumberNatNum
& rNum
= NumFor
[nNumFor
].GetNatNum();
5784 css::lang::Locale
aLocale(
5785 LanguageTag( rNum
.GetLang() ).getLocale() );
5787 /* TODO: a new XNativeNumberSupplier2::convertToXmlAttributes()
5788 * should rather return NativeNumberXmlAttributes2 and places
5789 * adapted, and whether to fill Spellout or something different
5790 * should be internal there. */
5791 css::i18n::NativeNumberXmlAttributes
aTmp(
5792 rNatNum
.convertToXmlAttributes(
5793 aLocale
, rNum
.GetNatNum()));
5794 rAttr
.Locale
= aTmp
.Locale
;
5795 rAttr
.Format
= aTmp
.Format
;
5796 rAttr
.Style
= aTmp
.Style
;
5797 if ( NatNumTakesParameters(rNum
.GetNatNum()) )
5799 // NatNum12 spell out numbers, dates and money amounts
5800 rAttr
.Spellout
= rNum
.GetParams();
5801 // Mutually exclusive.
5802 rAttr
.Format
.clear();
5803 rAttr
.Style
.clear();
5807 rAttr
.Spellout
.clear();
5812 rAttr
= css::i18n::NativeNumberXmlAttributes2();
5817 rAttr
= css::i18n::NativeNumberXmlAttributes2();
5821 OUString
SvNumberformat::GetNatNumModifierString( sal_uInt16 nNumFor
) const
5825 const SvNumberNatNum
& rNum
= NumFor
[nNumFor
].GetNatNum();
5826 if ( !rNum
.IsSet() )
5828 const sal_Int32 nNum
= rNum
.GetNatNum();
5829 OUStringBuffer sNatNumModifier
= "[NatNum" + OUString::number( nNum
);
5830 if ( NatNumTakesParameters( nNum
) )
5832 sNatNumModifier
.append( " " + rNum
.GetParams() );
5834 sNatNumModifier
.append( "]" );
5836 return sNatNumModifier
.makeStringAndClear();
5840 bool SvNumberformat::HasStringNegativeSign( const OUString
& rStr
)
5842 // For Sign '-' needs to be at the start or at the end of the string (blanks ignored)
5843 sal_Int32 nLen
= rStr
.getLength();
5848 const sal_Unicode
* const pBeg
= rStr
.getStr();
5849 const sal_Unicode
* const pEnd
= pBeg
+ nLen
;
5850 const sal_Unicode
* p
= pBeg
;
5858 while ( *p
== ' ' && ++p
< pEnd
);
5869 while ( *p
== ' ' && pBeg
< --p
);
5874 bool SvNumberformat::IsInQuote( const OUString
& rStr
, sal_Int32 nPos
,
5875 sal_Unicode cQuote
, sal_Unicode cEscIn
, sal_Unicode cEscOut
)
5877 sal_Int32 nLen
= rStr
.getLength();
5882 const sal_Unicode
* p0
= rStr
.getStr();
5883 const sal_Unicode
* p
= p0
;
5884 const sal_Unicode
* p1
= p0
+ nPos
;
5885 bool bQuoted
= false;
5896 if ( *(p
-1) != cEscIn
)
5903 if ( *(p
-1) != cEscOut
)
5915 sal_Int32
SvNumberformat::GetQuoteEnd( const OUString
& rStr
, sal_Int32 nPos
,
5916 sal_Unicode cQuote
, sal_Unicode cEscIn
)
5922 sal_Int32 nLen
= rStr
.getLength();
5927 if ( !IsInQuote( rStr
, nPos
, cQuote
, cEscIn
) )
5929 if ( rStr
[ nPos
] == cQuote
)
5931 return nPos
; // Closing cQuote
5935 const sal_Unicode
* p0
= rStr
.getStr();
5936 const sal_Unicode
* p
= p0
+ nPos
;
5937 const sal_Unicode
* p1
= p0
+ nLen
;
5940 if ( *p
== cQuote
&& p
> p0
&& *(p
-1) != cEscIn
)
5942 return sal::static_int_cast
< sal_Int32
>(p
- p0
);
5946 return nLen
; // End of String
5949 sal_uInt16
SvNumberformat::GetNumForNumberElementCount( sal_uInt16 nNumFor
) const
5953 sal_uInt16 nCnt
= NumFor
[nNumFor
].GetCount();
5954 return nCnt
- ImpGetNumForStringElementCount( nNumFor
);
5959 sal_uInt16
SvNumberformat::ImpGetNumForStringElementCount( sal_uInt16 nNumFor
) const
5961 sal_uInt16 nCnt
= 0;
5962 sal_uInt16 nNumForCnt
= NumFor
[nNumFor
].GetCount();
5963 auto& rTypeArray
= NumFor
[nNumFor
].Info().nTypeArray
;
5964 for ( sal_uInt16 j
=0; j
<nNumForCnt
; ++j
)
5966 switch ( rTypeArray
[j
] )
5968 case NF_SYMBOLTYPE_STRING
:
5969 case NF_SYMBOLTYPE_CURRENCY
:
5970 case NF_SYMBOLTYPE_DATESEP
:
5971 case NF_SYMBOLTYPE_TIMESEP
:
5972 case NF_SYMBOLTYPE_TIME100SECSEP
:
5973 case NF_SYMBOLTYPE_PERCENT
:
5981 bool SvNumberformat::IsMinuteSecondFormat() const
5983 if (GetMaskedType() != SvNumFormatType::TIME
)
5986 constexpr sal_uInt16 k00
= 0x00; // Nada, Nilch
5987 constexpr sal_uInt16 kLB
= 0x01; // '[' Left Bracket
5988 constexpr sal_uInt16 kRB
= 0x02; // ']' Right Bracket
5989 constexpr sal_uInt16 kMM
= 0x04; // M or MM
5990 constexpr sal_uInt16 kTS
= 0x08; // Time Separator
5991 constexpr sal_uInt16 kSS
= 0x10; // S or SS
5992 #define HAS_MINUTE_SECOND(state) ((state) == (kMM|kTS|kSS) || (state) == (kLB|kMM|kRB|kTS|kSS))
5993 // Also (kMM|kTS|kLB|kSS|kRB) but those are the same bits.
5995 sal_uInt16 nState
= k00
;
5997 sal_uInt16 nNumForCnt
= NumFor
[0].GetCount();
5998 auto const & rTypeArray
= NumFor
[0].Info().nTypeArray
;
5999 for (sal_uInt16 j
=0; j
< nNumForCnt
; ++j
)
6001 switch (rTypeArray
[j
])
6003 case NF_SYMBOLTYPE_DEL
:
6005 // '[' or ']' before/after MM or SS
6006 const OUString
& rStr
= NumFor
[0].Info().sStrArray
[j
];
6009 if (nState
!= k00
&& nState
!= (kMM
|kTS
))
6013 else if (rStr
== "]")
6015 if (nState
!= (kLB
|kMM
) && nState
!= (kMM
|kTS
|kLB
|kSS
))
6025 if (nState
!= k00
&& nState
!= kLB
)
6029 case NF_SYMBOLTYPE_TIMESEP
:
6030 if (nState
!= kMM
&& nState
!= (kLB
|kMM
|kRB
))
6036 if (nState
!= (kMM
|kTS
) && nState
!= (kLB
|kMM
|kRB
|kTS
) && nState
!= (kMM
|kTS
|kLB
))
6040 case NF_SYMBOLTYPE_TIME100SECSEP
:
6041 // Trailing fraction of seconds allowed.
6042 if (!HAS_MINUTE_SECOND(nState
))
6046 case NF_SYMBOLTYPE_DIGIT
:
6050 case NF_SYMBOLTYPE_STRING
:
6051 // nothing, display literal
6057 return HAS_MINUTE_SECOND(nState
);
6058 #undef HAS_MINUTE_SECOND
6061 OUString
SvNumberformat::GetFormatStringForTimePrecision( int nPrecision
) const
6063 OUStringBuffer sString
;
6064 using comphelper::string::padToLength
;
6066 sal_uInt16 nNumForCnt
= NumFor
[0].GetCount();
6067 auto const & rTypeArray
= NumFor
[0].Info().nTypeArray
;
6068 for (sal_uInt16 j
=0; j
< nNumForCnt
; ++j
)
6070 switch (rTypeArray
[j
])
6074 sString
.append( NumFor
[0].Info().sStrArray
[j
] );
6075 if ( j
> 0 && rTypeArray
[j
-1] == NF_SYMBOLTYPE_DEL
&& j
< nNumForCnt
-1 )
6078 sString
.append( NumFor
[0].Info().sStrArray
[j
] );
6082 sString
.append( rLoc().getTime100SecSep() );
6083 padToLength(sString
, sString
.getLength() + nPrecision
, '0');
6086 case NF_SYMBOLTYPE_TIME100SECSEP
:
6087 case NF_SYMBOLTYPE_DIGIT
:
6089 case NF_SYMBOLTYPE_STRING
:
6090 sString
.append( "\"" );
6093 sString
.append( NumFor
[0].Info().sStrArray
[j
] );
6094 if (rTypeArray
[j
] == NF_SYMBOLTYPE_STRING
)
6096 sString
.append( "\"" );
6101 return sString
.makeStringAndClear();
6104 sal_uInt16
SvNumberformat::GetThousandDivisorPrecision( sal_uInt16 nIx
) const
6109 const ImpSvNumberformatInfo
& rInfo
= NumFor
[nIx
].Info();
6111 if (rInfo
.eScannedType
!= SvNumFormatType::NUMBER
&& rInfo
.eScannedType
!= SvNumFormatType::CURRENCY
)
6114 if (rInfo
.nThousand
== FLAG_STANDARD_IN_FORMAT
)
6115 return SvNumberFormatter::UNLIMITED_PRECISION
;
6117 return rInfo
.nThousand
* 3;
6120 const CharClass
& SvNumberformat::rChrCls() const
6122 return rScan
.GetChrCls();
6125 const LocaleDataWrapper
& SvNumberformat::rLoc() const
6127 return rScan
.GetLoc();
6130 const SvNFLanguageData
& SvNumberformat::GetCurrentLanguageData() const
6132 return rScan
.GetCurrentLanguageData();
6135 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */