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