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