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