1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <string_view> 21 22 #include <o3tl/sprintf.hxx> 23 #include <o3tl/string_view.hxx> 24 #include <comphelper/string.hxx> 25 #include <sal/log.hxx> 26 #include <tools/debug.hxx> 27 #include <tools/long.hxx> 28 #include <i18nlangtag/mslangid.hxx> 29 #include <rtl/math.hxx> 30 #include <unotools/charclass.hxx> 31 #include <unotools/calendarwrapper.hxx> 32 #include <unotools/nativenumberwrapper.hxx> 33 #include <com/sun/star/i18n/CalendarFieldIndex.hpp> 34 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp> 35 #include <com/sun/star/i18n/CalendarDisplayCode.hpp> 36 #include <com/sun/star/i18n/AmPmValue.hpp> 37 #include <com/sun/star/i18n/NativeNumberMode.hpp> 38 #include <com/sun/star/i18n/NativeNumberXmlAttributes2.hpp> 39 40 #include <svl/zformat.hxx> 41 #include "zforscan.hxx" 42 43 #include "zforfind.hxx" 44 #include <svl/zforlist.hxx> 45 #include <unotools/digitgroupingiterator.hxx> 46 #include <svl/nfsymbol.hxx> 47 48 #include <cmath> 49 #include <array> 50 51 using namespace svt; 52 53 namespace { 54 55 constexpr OUStringLiteral GREGORIAN = u"gregorian"; 56 57 const sal_uInt16 UPPER_PRECISION = 300; // entirely arbitrary... 58 const double EXP_LOWER_BOUND = 1.0E-4; // prefer scientific notation below this value. 59 const double EXP_ABS_UPPER_BOUND = 1.0E15; // use exponential notation above that absolute value. 60 // Back in time was E16 that lead 61 // to display rounding errors, see 62 // also sal/rtl/math.cxx 63 // doubleToString() 64 65 constexpr sal_Int32 kTimeSignificantRound = 7; // Round (date+)time at 7 decimals 66 // (+5 of 86400 == 12 significant digits). 67 } // namespace 68 69 const double D_MAX_U_INT32 = double(0xffffffff); // 4294967295.0 70 constexpr double D_MAX_INTEGER = (sal_uInt64(1) << 53) - 1; 71 72 const double D_MAX_D_BY_100 = 1.7E306; 73 const double D_MIN_M_BY_1000 = 2.3E-305; 74 75 const 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 85 sal_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 102 static 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 127 /** 128 * SvNumberformatInfo 129 * */ 130 131 void 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 146 const 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 152 sal_uInt8 SvNumberNatNum::MapDBNumToNatNum( sal_uInt8 nDBNum, LanguageType eLang, bool bDate ) 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 181 const 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 187 sal_uInt8 SvNumberNatNum::MapNatNumToDBNum( sal_uInt8 nNatNum, LanguageType eLang, bool bDate ) 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 215 /** 216 * SvNumFor 217 */ 218 219 ImpSvNumFor::ImpSvNumFor() 220 { 221 nStringsCnt = 0; 222 aI.eScannedType = SvNumFormatType::UNDEFINED; 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 231 ImpSvNumFor::~ImpSvNumFor() 232 { 233 } 234 235 void 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 245 void 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 261 bool ImpSvNumFor::HasNewCurrency() const 262 { 263 for ( sal_uInt16 j=0; j<nStringsCnt; j++ ) 264 { 265 if ( aI.nTypeArray[j] == NF_SYMBOLTYPE_CURRENCY ) 266 { 267 return true; 268 } 269 } 270 return false; 271 } 272 273 bool ImpSvNumFor::GetNewCurrencySymbol( OUString& rSymbol, 274 OUString& rExtension ) const 275 { 276 for ( sal_uInt16 j=0; j<nStringsCnt; j++ ) 277 { 278 if ( aI.nTypeArray[j] == NF_SYMBOLTYPE_CURRENCY ) 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 } 292 //! No Erase at rSymbol, rExtension 293 return false; 294 } 295 296 /** 297 * SvNumberformat 298 */ 299 300 namespace { 301 302 enum 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 341 void SvNumberformat::ImpCopyNumberformat( const SvNumberformat& rFormat ) 342 { 343 sFormatstring = rFormat.sFormatstring; 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; 353 bAdditionalBuiltin = rFormat.bAdditionalBuiltin; 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 364 SvNumberformat::SvNumberformat( SvNumberformat const & rFormat ) 365 : rScan(rFormat.rScan), bStarFlag( rFormat.bStarFlag ) 366 { 367 ImpCopyNumberformat( rFormat ); 368 } 369 370 SvNumberformat::SvNumberformat( SvNumberformat const & rFormat, ImpSvNumberformatScan& rSc ) 371 : rScan(rSc) 372 , bStarFlag( rFormat.bStarFlag ) 373 { 374 ImpCopyNumberformat( rFormat ); 375 } 376 377 static 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 421 /** Import extended LCID from Excel 422 */ 423 OUString SvNumberformat::ImpObtainCalendarAndNumerals( OUStringBuffer& rString, sal_Int32 nPos, 424 LanguageType& nLang, const LocaleType& aTmpLocale ) 425 { 426 OUString sCalendar; 427 sal_uInt16 nNatNum = 0; 428 LanguageType nLocaleLang = MsLangId::getRealLanguage( maLocale.meLanguage ); 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 { 454 nLang = maLocale.meLanguage = LANGUAGE_JAPANESE; 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 { 462 nLang = maLocale.meLanguage = LANGUAGE_KOREAN; 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 { 479 nLang = maLocale.meLanguage = LANGUAGE_ARABIC_SAUDI_ARABIA; 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 { 494 nLang = maLocale.meLanguage = LANGUAGE_THAI; 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 } 512 /** Reference language for each numeral ID */ 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 532 LANGUAGE_TIGRIGNA_ETHIOPIA, // 0x11 533 LANGUAGE_KHMER, // 0x12 534 LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA, // 0x13 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 545 LANGUAGE_CHINESE_SIMPLIFIED, // 0x1E 546 LANGUAGE_CHINESE_SIMPLIFIED, // 0x1F 547 LANGUAGE_CHINESE_SIMPLIFIED, // 0x20 548 LANGUAGE_CHINESE_TRADITIONAL, // 0x21 549 LANGUAGE_CHINESE_TRADITIONAL, // 0x22 550 LANGUAGE_CHINESE_TRADITIONAL, // 0x23 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 { 603 nLang = maLocale.meLanguage = LANGUAGE_HINDI; 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 { 621 nLang = maLocale.meLanguage = LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA; 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 { 687 nLang = maLocale.meLanguage = LANGUAGE_CHINESE_SIMPLIFIED; 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 { 704 nLang = maLocale.meLanguage = LANGUAGE_CHINESE_TRADITIONAL; 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 716 namespace 717 { 718 bool 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=")? 726 static 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 750 SvNumberformat::SvNumberformat(OUString& rString, 751 ImpSvNumberformatScan* pSc, 752 ImpSvNumberInputScan* pISc, 753 sal_Int32& nCheckPos, 754 LanguageType& eLan, 755 bool bReplaceBooleanEquivalent) 756 : rScan(*pSc) 757 , bAdditionalBuiltin( false ) 758 , bStarFlag( false ) 759 { 760 if (bReplaceBooleanEquivalent) 761 rScan.ReplaceBooleanEquivalent( rString); 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(); 785 maLocale.meLanguage = rScan.GetNewLnge(); 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; 796 eOp1 = NUMBERFORMAT_OP_NO; 797 eOp2 = NUMBERFORMAT_OP_NO; 798 eType = SvNumFormatType::DEFINED; 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 { 814 rScan.GetNumberformatter()->ChangeIntl(rScan.GetTmpLnge()); 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; 846 SvNumFormatType F_Type = SvNumFormatType::UNDEFINED; 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 } 952 //! eSymbolType is negative 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 { 991 //! eSymbolType is negative 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 || 1027 SvNumberFormatter::IsLocaleInstalled( aTmpLocale.meLanguage))) 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. 1073 maLocale.meLanguageWithoutLocaleData = aTmpLocale.meLanguage; 1074 1075 sStr = "$-" + aTmpLocale.generateCode(); 1076 } 1077 NumFor[nIndex].SetNatNumLang( MsLangId::getRealLanguage( aTmpLocale.meLanguage)); 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. 1134 NumFor[nIndex].Info().eScannedType = SvNumFormatType::EMPTY; 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;@ 1190 if (pSc->GetScannedType() == SvNumFormatType::TEXT) 1191 { 1192 // Reset conditions, reverting any set above. 1193 if (nIndex == 1) 1194 eOp1 = NUMBERFORMAT_OP_NO; 1195 else if (nIndex == 2) 1196 eOp2 = NUMBERFORMAT_OP_NO; 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()) ) 1207 eType = SvNumFormatType::CURRENCY; 1208 else 1209 eType = NumFor[nIndex].Info().eScannedType; 1210 } 1211 else if (nIndex == 3) 1212 { // #77026# Everything recognized IS text 1213 NumFor[nIndex].Info().eScannedType = SvNumFormatType::TEXT; 1214 } 1215 else if ( NumFor[nIndex].Info().eScannedType != eType) 1216 { 1217 eType = SvNumFormatType::DEFINED; 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 } 1267 NumFor[nIndex+1].Info().eScannedType = SvNumFormatType::EMPTY; 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() 1275 NumFor[3].Info().eScannedType = SvNumFormatType::TEXT; 1276 } 1277 bCancel = true; 1278 } 1279 if ( NumFor[nIndex].GetNatNum().IsSet() ) 1280 { 1281 NumFor[nIndex].SetNatNumDate( bool(NumFor[nIndex].Info().eScannedType & SvNumFormatType::DATE) ); 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 { 1291 if ((eType & ~SvNumFormatType::DEFINED) != SvNumFormatType::TIME) 1292 nCheckPos = std::max<sal_Int32>( sBuff.indexOf(']') + 1, 1); 1293 } 1294 else if (IsSystemLongDateFormat()) 1295 { 1296 if ((eType & ~SvNumFormatType::DEFINED) != SvNumFormatType::DATE) 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(";"); 1338 sBuff.append(aAdd); 1339 } 1340 } 1341 } 1342 else if ( nIndex == 2 && NumFor[nIndex].GetCount() == 0 && 1343 sBuff[sBuff.getLength() - 1] != ';' && 1344 eOp2 != NUMBERFORMAT_OP_NO ) 1345 { 1346 // No trailing third subformat => GENERAL but not if specified empty 1347 OUString aAdd( pSc->GetStandardName() ); 1348 if ( !pSc->ScanFormat( aAdd ) ) 1349 { 1350 sal_uInt16 nCnt = pSc->GetResultStringsCnt(); 1351 if ( nCnt ) 1352 { 1353 NumFor[nIndex].Enlarge(nCnt); 1354 pSc->CopyInfo( &(NumFor[nIndex].Info()), nCnt ); 1355 sBuff.append(";"); 1356 sBuff.append(aAdd); 1357 } 1358 } 1359 } 1360 } 1361 rString = sBuff.makeStringAndClear(); 1362 sFormatstring = rString; 1363 1364 if (NumFor[2].GetCount() == 0 && // No third partial string 1365 eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_NO && 1366 fLimit1 == 0.0 && fLimit2 == 0.0) 1367 { 1368 eOp1 = NUMBERFORMAT_OP_GE; // Add 0 to the first format 1369 } 1370 1371 } 1372 1373 SvNumberformat::~SvNumberformat() 1374 { 1375 } 1376 1377 /** 1378 * Next_Symbol 1379 * 1380 * Splits up the symbols for further processing (by the Turing machine) 1381 * 1382 * Start state = SsStart, * = Special state 1383 * ---------------+-------------------+----------------------------+--------------- 1384 * Old State | Symbol read | Event | New state 1385 * ---------------+-------------------+----------------------------+--------------- 1386 * SsStart | " | Symbol += Character | SsGetQuoted 1387 * | ; | Pos-- | SsGetString 1388 * | [ | Symbol += Character | SsGetBracketed 1389 * | ] | Error | SsStop 1390 * | BLANK | | 1391 * | Else | Symbol += Character | SsGetString 1392 * ---------------+-------------------+----------------------------+--------------- 1393 * SsGetString | " | Symbol += Character | SsGetQuoted 1394 * | ; | | SsStop 1395 * | Else | Symbol += Character | 1396 * ---------------+-------------------+----------------------------+--------------- 1397 * SsGetQuoted | " | Symbol += Character | SsGetString 1398 * | Else | Symbol += Character | 1399 * ---------------+-------------------+----------------------------+--------------- 1400 * SsGetBracketed | <, > = | del [ | 1401 * | | Symbol += Character | SsGetCon 1402 * | BLANK | | 1403 * | h, H, m, M, s, S | Symbol += Character | SsGetTime 1404 * | Else | del [ | 1405 * | | Symbol += Character | SsGetPrefix 1406 * ---------------+-------------------+----------------------------+--------------- 1407 * SsGetTime | ] | Symbol += Character | SsGetString 1408 * | h, H, m, M, s, S | Symbol += Character, * | SsGetString 1409 * | Else | del [; Symbol += Character | SsGetPrefix 1410 * ---------------+-------------------+----------------------------+--------------- 1411 * SsGetPrefix | ] | | SsStop 1412 * | Else | Symbol += Character | 1413 * ---------------+-------------------+----------------------------+--------------- 1414 * SsGetCon | >, = | Symbol += Character | 1415 * | ] | | SsStop 1416 * | Else | Error | SsStop 1417 * ---------------+-------------------+----------------------------+--------------- 1418 */ 1419 1420 namespace { 1421 1422 enum ScanState 1423 { 1424 SsStop, 1425 SsStart, 1426 SsGetCon, // condition 1427 SsGetString, // format string 1428 SsGetPrefix, // color or NatNumN 1429 SsGetTime, // [HH] for time 1430 SsGetBracketed, // any [...] not decided yet 1431 SsGetQuoted // quoted text 1432 }; 1433 1434 } 1435 1436 // read a string until ']' and delete spaces in input 1437 // static 1438 sal_Int32 SvNumberformat::ImpGetNumber(OUStringBuffer& rString, 1439 sal_Int32& nPos, 1440 OUString& sSymbol) 1441 { 1442 sal_Int32 nStartPos = nPos; 1443 sal_Unicode cToken; 1444 sal_Int32 nLen = rString.getLength(); 1445 OUStringBuffer sBuffSymbol; 1446 while ( nPos < nLen ) 1447 { 1448 cToken = rString[nPos]; 1449 if (cToken == ']') 1450 break; 1451 if (cToken == ' ') 1452 { // delete spaces 1453 rString.remove(nPos,1); 1454 nLen--; 1455 } 1456 else 1457 { 1458 nPos++; 1459 sBuffSymbol.append(cToken); 1460 } 1461 } 1462 sSymbol = sBuffSymbol.makeStringAndClear(); 1463 return nPos - nStartPos; 1464 } 1465 1466 namespace { 1467 1468 sal_Unicode toUniChar(sal_uInt8 n) 1469 { 1470 char c; 1471 if (n < 10) 1472 { 1473 c = '0' + n; 1474 } 1475 else 1476 { 1477 c = 'A' + n - 10; 1478 } 1479 return sal_Unicode(c); 1480 } 1481 1482 bool IsCombiningSymbol( OUStringBuffer& rStringBuffer, sal_Int32 nPos ) 1483 { 1484 bool bRet = false; 1485 while (nPos >= 0) 1486 { 1487 switch (rStringBuffer[nPos]) 1488 { 1489 case '*': 1490 case '\\': 1491 case '_': 1492 bRet = !bRet; 1493 --nPos; 1494 break; 1495 default: 1496 return bRet; 1497 } 1498 } 1499 return bRet; 1500 } 1501 1502 } // namespace 1503 1504 OUString SvNumberformat::LocaleType::generateCode() const 1505 { 1506 OUStringBuffer aBuf; 1507 #if 0 1508 // TODO: We may re-enable this later. Don't remove it! --Kohei 1509 if (mnNumeralShape) 1510 { 1511 sal_uInt8 nVal = mnNumeralShape; 1512 for (sal_uInt8 i = 0; i < 2; ++i) 1513 { 1514 sal_uInt8 n = (nVal & 0xF0) >> 4; 1515 if (n || aBuf.getLength()) 1516 { 1517 aBuf.append(toUniChar(n)); 1518 } 1519 nVal = nVal << 4; 1520 } 1521 } 1522 1523 if (mnNumeralShape || mnCalendarType) 1524 { 1525 sal_uInt8 nVal = mnCalendarType; 1526 for (sal_uInt8 i = 0; i < 2; ++i) 1527 { 1528 sal_uInt8 n = (nVal & 0xF0) >> 4; 1529 if (n || aBuf.getLength()) 1530 { 1531 aBuf.append(toUniChar(n)); 1532 } 1533 nVal = nVal << 4; 1534 } 1535 } 1536 #endif 1537 1538 sal_uInt16 n16 = static_cast<sal_uInt16>( 1539 (meLanguageWithoutLocaleData == LANGUAGE_DONTKNOW) ? meLanguage : 1540 meLanguageWithoutLocaleData); 1541 if (meLanguage == LANGUAGE_SYSTEM) 1542 { 1543 switch (meSubstitute) 1544 { 1545 case Substitute::NONE: 1546 ; // nothing 1547 break; 1548 case Substitute::TIME: 1549 n16 = static_cast<sal_uInt16>(LANGUAGE_NF_SYSTEM_TIME); 1550 break; 1551 case Substitute::LONGDATE: 1552 n16 = static_cast<sal_uInt16>(LANGUAGE_NF_SYSTEM_DATE); 1553 break; 1554 } 1555 } 1556 for (sal_uInt8 i = 0; i < 4; ++i) 1557 { 1558 sal_uInt8 n = static_cast<sal_uInt8>((n16 & 0xF000) >> 12); 1559 // Omit leading zeros for consistency. 1560 if (n || !aBuf.isEmpty() || i == 3) 1561 { 1562 aBuf.append(toUniChar(n)); 1563 } 1564 n16 = n16 << 4; 1565 } 1566 1567 return aBuf.makeStringAndClear(); 1568 } 1569 1570 SvNumberformat::LocaleType::LocaleType() 1571 : meLanguage(LANGUAGE_DONTKNOW) 1572 , meLanguageWithoutLocaleData(LANGUAGE_DONTKNOW) 1573 , meSubstitute(Substitute::NONE) 1574 , mnNumeralShape(0) 1575 , mnCalendarType(0) 1576 { 1577 } 1578 1579 SvNumberformat::LocaleType::LocaleType(sal_uInt32 nRawNum) 1580 : meLanguage(LANGUAGE_DONTKNOW) 1581 , meLanguageWithoutLocaleData(LANGUAGE_DONTKNOW) 1582 , meSubstitute(Substitute::NONE) 1583 , mnNumeralShape(0) 1584 , mnCalendarType(0) 1585 { 1586 meLanguage = static_cast<LanguageType>(nRawNum & 0x0000FFFF); 1587 if (meLanguage == LANGUAGE_NF_SYSTEM_TIME) 1588 { 1589 meSubstitute = Substitute::TIME; 1590 meLanguage = LANGUAGE_SYSTEM; 1591 } 1592 else if (meLanguage == LANGUAGE_NF_SYSTEM_DATE) 1593 { 1594 meSubstitute = Substitute::LONGDATE; 1595 meLanguage = LANGUAGE_SYSTEM; 1596 } 1597 nRawNum = (nRawNum >> 16); 1598 mnCalendarType = static_cast<sal_uInt8>(nRawNum & 0xFF); 1599 nRawNum = (nRawNum >> 8); 1600 mnNumeralShape = static_cast<sal_uInt8>(nRawNum & 0xFF); 1601 } 1602 1603 bool SvNumberformat::LocaleType::isPlainLocale() const 1604 { 1605 return meSubstitute == Substitute::NONE && !mnCalendarType && !mnNumeralShape; 1606 } 1607 1608 // static 1609 SvNumberformat::LocaleType SvNumberformat::ImpGetLocaleType(std::u16string_view rString, sal_Int32& nPos ) 1610 { 1611 sal_uInt32 nNum = 0; 1612 sal_Unicode cToken = 0; 1613 sal_Int32 nStart = nPos; 1614 sal_Int32 nLen = rString.size(); 1615 while ( nPos < nLen && (nPos - nStart < 8) ) 1616 { 1617 cToken = rString[nPos]; 1618 if (cToken == ']') 1619 break; 1620 if ( '0' <= cToken && cToken <= '9' ) 1621 { 1622 nNum *= 16; 1623 nNum += cToken - '0'; 1624 } 1625 else if ( 'a' <= cToken && cToken <= 'f' ) 1626 { 1627 nNum *= 16; 1628 nNum += cToken - 'a' + 10; 1629 } 1630 else if ( 'A' <= cToken && cToken <= 'F' ) 1631 { 1632 nNum *= 16; 1633 nNum += cToken - 'A' + 10; 1634 } 1635 else 1636 { 1637 return LocaleType(); // LANGUAGE_DONTKNOW; 1638 } 1639 ++nPos; 1640 } 1641 1642 return (cToken == ']' || nPos == nLen) ? LocaleType(nNum) : LocaleType(); 1643 } 1644 1645 static bool lcl_matchKeywordAndGetNumber( std::u16string_view rString, const sal_Int32 nPos, 1646 std::u16string_view rKeyword, sal_Int32 & nNumber ) 1647 { 1648 if (0 <= nPos && nPos + static_cast<sal_Int32>(rKeyword.size()) < static_cast<sal_Int32>(rString.size()) && o3tl::matchIgnoreAsciiCase( rString, rKeyword, nPos)) 1649 { 1650 nNumber = o3tl::toInt32(rString.substr( nPos + rKeyword.size())); 1651 return true; 1652 } 1653 else 1654 { 1655 nNumber = 0; 1656 return false; 1657 } 1658 } 1659 1660 short SvNumberformat::ImpNextSymbol(OUStringBuffer& rString, 1661 sal_Int32& nPos, 1662 OUString& sSymbol) const 1663 { 1664 short eSymbolType = BRACKET_SYMBOLTYPE_FORMAT; 1665 sal_Unicode cToken; 1666 sal_Unicode cLetter = ' '; // Preliminary result 1667 sal_Int32 nLen = rString.getLength(); 1668 ScanState eState = SsStart; 1669 OUStringBuffer sBuffSymbol(128); 1670 1671 const NfKeywordTable & rKeywords = rScan.GetKeywords(); 1672 while (nPos < nLen && eState != SsStop) 1673 { 1674 cToken = rString[nPos]; 1675 nPos++; 1676 switch (eState) 1677 { 1678 case SsStart: 1679 if (cToken == '\"') 1680 { 1681 eState = SsGetQuoted; 1682 sBuffSymbol.append(cToken); 1683 } 1684 else if (cToken == '[') 1685 { 1686 eState = SsGetBracketed; 1687 sBuffSymbol.append(cToken); 1688 } 1689 else if (cToken == ';') 1690 { 1691 eState = SsGetString; 1692 nPos--; 1693 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT; 1694 } 1695 else if (cToken == ']') 1696 { 1697 eState = SsStop; 1698 eSymbolType = BRACKET_SYMBOLTYPE_ERROR; 1699 } 1700 else if (cToken == ' ') // Skip Blanks 1701 { 1702 nPos--; 1703 rString.remove(nPos, 1); 1704 nLen--; 1705 } 1706 else 1707 { 1708 sBuffSymbol.append(cToken); 1709 eState = SsGetString; 1710 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT; 1711 } 1712 break; 1713 case SsGetBracketed: 1714 switch (cToken) 1715 { 1716 case '<': 1717 case '>': 1718 case '=': 1719 sBuffSymbol.stripStart('['); 1720 sBuffSymbol.append(cToken); 1721 cLetter = cToken; 1722 eState = SsGetCon; 1723 switch (cToken) 1724 { 1725 case '<': 1726 eSymbolType = NUMBERFORMAT_OP_LT; 1727 break; 1728 case '>': 1729 eSymbolType = NUMBERFORMAT_OP_GT; 1730 break; 1731 case '=': 1732 eSymbolType = NUMBERFORMAT_OP_EQ; 1733 break; 1734 } 1735 break; 1736 case ' ': 1737 nPos--; 1738 rString.remove(nPos, 1); 1739 nLen--; 1740 break; 1741 case '$' : 1742 if ( nPos < nLen && rString[nPos] == '-' ) 1743 { 1744 // [$-xxx] locale 1745 sBuffSymbol.stripStart('['); 1746 eSymbolType = BRACKET_SYMBOLTYPE_LOCALE; 1747 eState = SsGetPrefix; 1748 } 1749 else 1750 { // currency 1751 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT; 1752 eState = SsGetString; 1753 } 1754 sBuffSymbol.append(cToken); 1755 break; 1756 case '~' : 1757 // calendarID 1758 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT; 1759 sBuffSymbol.append(cToken); 1760 eState = SsGetString; 1761 break; 1762 default: 1763 { 1764 static const OUStringLiteral aNatNum(u"NATNUM"); 1765 static const OUStringLiteral aDBNum(u"DBNUM"); 1766 const OUString aBufStr( rString.toString()); 1767 sal_Int32 nNatNumNum; 1768 sal_Int32 nDBNum; 1769 if ( lcl_matchKeywordAndGetNumber( aBufStr, nPos-1, aNatNum, nNatNumNum) && 1770 0 <= nNatNumNum && nNatNumNum <= 19 ) 1771 { 1772 sBuffSymbol.stripStart('['); 1773 sBuffSymbol.append( aBufStr.subView(--nPos, aNatNum.getLength()+1) ); 1774 nPos += aNatNum.getLength()+1; 1775 //! SymbolType is negative 1776 eSymbolType = static_cast<short>(BRACKET_SYMBOLTYPE_NATNUM0 - nNatNumNum); 1777 eState = SsGetPrefix; 1778 } 1779 else if ( lcl_matchKeywordAndGetNumber( aBufStr, nPos-1, aDBNum, nDBNum) && 1780 1 <= nDBNum && nDBNum <= 9 ) 1781 { 1782 sBuffSymbol.stripStart('['); 1783 sBuffSymbol.append( aBufStr.subView(--nPos, aDBNum.getLength()+1) ); 1784 nPos += aDBNum.getLength()+1; 1785 //! SymbolType is negative 1786 eSymbolType = sal::static_int_cast< short >( BRACKET_SYMBOLTYPE_DBNUM1 - (nDBNum - 1) ); 1787 eState = SsGetPrefix; 1788 } 1789 else 1790 { 1791 sal_Unicode cUpper = rChrCls().uppercase( aBufStr, nPos-1, 1)[0]; 1792 if ( cUpper == rKeywords[NF_KEY_H][0] || // H 1793 cUpper == rKeywords[NF_KEY_MI][0] || // M 1794 cUpper == rKeywords[NF_KEY_S][0] ) // S 1795 { 1796 sBuffSymbol.append(cToken); 1797 eState = SsGetTime; 1798 cLetter = cToken; 1799 } 1800 else 1801 { 1802 sBuffSymbol.stripStart('['); 1803 sBuffSymbol.append(cToken); 1804 eSymbolType = BRACKET_SYMBOLTYPE_COLOR; 1805 eState = SsGetPrefix; 1806 } 1807 } 1808 } 1809 } 1810 break; 1811 case SsGetString: 1812 if (cToken == '\"') 1813 { 1814 eState = SsGetQuoted; 1815 sBuffSymbol.append(cToken); 1816 } 1817 else if (cToken == ';' && (nPos < 2 || !IsCombiningSymbol( rString, nPos-2))) 1818 { 1819 eState = SsStop; 1820 } 1821 else 1822 { 1823 sBuffSymbol.append(cToken); 1824 } 1825 break; 1826 case SsGetQuoted: 1827 if (cToken == '\"') 1828 { 1829 eState = SsGetString; 1830 sBuffSymbol.append(cToken); 1831 } 1832 else 1833 { 1834 sBuffSymbol.append(cToken); 1835 } 1836 break; 1837 case SsGetTime: 1838 if (cToken == ']') 1839 { 1840 sBuffSymbol.append(cToken); 1841 eState = SsGetString; 1842 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT; 1843 } 1844 else 1845 { 1846 sal_Unicode cUpper = rChrCls().uppercase(rString.toString(), nPos-1, 1)[0]; 1847 if (cUpper == rKeywords[NF_KEY_H][0] || // H 1848 cUpper == rKeywords[NF_KEY_MI][0] || // M 1849 cUpper == rKeywords[NF_KEY_S][0] ) // S 1850 { 1851 if (cLetter == cToken) 1852 { 1853 sBuffSymbol.append(cToken); 1854 cLetter = ' '; 1855 } 1856 else 1857 { 1858 sBuffSymbol.stripStart('['); 1859 sBuffSymbol.append(cToken); 1860 eState = SsGetPrefix; 1861 } 1862 } 1863 else 1864 { 1865 sBuffSymbol.stripStart('['); 1866 sBuffSymbol.append(cToken); 1867 eSymbolType = BRACKET_SYMBOLTYPE_COLOR; 1868 eState = SsGetPrefix; 1869 } 1870 } 1871 break; 1872 case SsGetCon: 1873 switch (cToken) 1874 { 1875 case '<': 1876 eState = SsStop; 1877 eSymbolType = BRACKET_SYMBOLTYPE_ERROR; 1878 break; 1879 case '>': 1880 if (cLetter == '<') 1881 { 1882 sBuffSymbol.append(cToken); 1883 cLetter = ' '; 1884 eState = SsStop; 1885 eSymbolType = NUMBERFORMAT_OP_NE; 1886 } 1887 else 1888 { 1889 eState = SsStop; 1890 eSymbolType = BRACKET_SYMBOLTYPE_ERROR; 1891 } 1892 break; 1893 case '=': 1894 if (cLetter == '<') 1895 { 1896 sBuffSymbol.append(cToken); 1897 cLetter = ' '; 1898 eSymbolType = NUMBERFORMAT_OP_LE; 1899 } 1900 else if (cLetter == '>') 1901 { 1902 sBuffSymbol.append(cToken); 1903 cLetter = ' '; 1904 eSymbolType = NUMBERFORMAT_OP_GE; 1905 } 1906 else 1907 { 1908 eState = SsStop; 1909 eSymbolType = BRACKET_SYMBOLTYPE_ERROR; 1910 } 1911 break; 1912 case ' ': 1913 nPos--; 1914 rString.remove(nPos,1); 1915 nLen--; 1916 break; 1917 default: 1918 eState = SsStop; 1919 nPos--; 1920 break; 1921 } 1922 break; 1923 case SsGetPrefix: 1924 if (cToken == ']') 1925 { 1926 eState = SsStop; 1927 } 1928 else 1929 { 1930 sBuffSymbol.append(cToken); 1931 } 1932 break; 1933 default: 1934 break; 1935 } // of switch 1936 } // of while 1937 sSymbol = sBuffSymbol.makeStringAndClear(); 1938 return eSymbolType; 1939 } 1940 1941 void SvNumberformat::ConvertLanguage( SvNumberFormatter& rConverter, 1942 LanguageType eConvertFrom, 1943 LanguageType eConvertTo ) 1944 { 1945 sal_Int32 nCheckPos; 1946 sal_uInt32 nKey; 1947 SvNumFormatType nType = eType; 1948 OUString aFormatString( sFormatstring ); 1949 rConverter.PutandConvertEntry( aFormatString, nCheckPos, nType, 1950 nKey, eConvertFrom, eConvertTo, false); 1951 const SvNumberformat* pFormat = rConverter.GetEntry( nKey ); 1952 DBG_ASSERT( pFormat, "SvNumberformat::ConvertLanguage: Conversion without format" ); 1953 if ( pFormat ) 1954 { 1955 ImpCopyNumberformat( *pFormat ); 1956 // Reset values taken over from Formatter/Scanner 1957 // pColor still points to table in temporary Formatter/Scanner 1958 for (ImpSvNumFor & rFormatter : NumFor) 1959 { 1960 OUString aColorName = rFormatter.GetColorName(); 1961 const Color* pColor = rScan.GetColor( aColorName ); 1962 rFormatter.SetColor( pColor, aColorName ); 1963 } 1964 } 1965 } 1966 1967 bool SvNumberformat::HasNewCurrency() const 1968 { 1969 for (const auto & j : NumFor) 1970 { 1971 if ( j.HasNewCurrency() ) 1972 { 1973 return true; 1974 } 1975 } 1976 return false; 1977 } 1978 1979 bool SvNumberformat::GetNewCurrencySymbol( OUString& rSymbol, 1980 OUString& rExtension ) const 1981 { 1982 for (const auto & j : NumFor) 1983 { 1984 if ( j.GetNewCurrencySymbol( rSymbol, rExtension ) ) 1985 { 1986 return true; 1987 } 1988 } 1989 rSymbol.clear(); 1990 rExtension.clear(); 1991 return false; 1992 } 1993 1994 // static 1995 OUString SvNumberformat::StripNewCurrencyDelimiters( const OUString& rStr ) 1996 { 1997 OUStringBuffer aTmp(rStr.getLength()); 1998 sal_Int32 nStartPos, nPos, nLen; 1999 nLen = rStr.getLength(); 2000 nStartPos = 0; 2001 while ( (nPos = rStr.indexOf( "[$", nStartPos )) >= 0 ) 2002 { 2003 sal_Int32 nEnd; 2004 if ( (nEnd = GetQuoteEnd( rStr, nPos )) >= 0 ) 2005 { 2006 aTmp.append(rStr.subView( nStartPos, ++nEnd - nStartPos )); 2007 nStartPos = nEnd; 2008 } 2009 else 2010 { 2011 aTmp.append(rStr.subView(nStartPos, nPos - nStartPos) ); 2012 nStartPos = nPos + 2; 2013 sal_Int32 nDash; 2014 nEnd = nStartPos - 1; 2015 do 2016 { 2017 nDash = rStr.indexOf( '-', ++nEnd ); 2018 nEnd = GetQuoteEnd( rStr, nDash ); 2019 } 2020 while ( nEnd >= 0 ); 2021 sal_Int32 nClose; 2022 nEnd = nStartPos - 1; 2023 do 2024 { 2025 nClose = rStr.indexOf( ']', ++nEnd ); 2026 nEnd = GetQuoteEnd( rStr, nClose ); 2027 } 2028 while ( nEnd >= 0 ); 2029 2030 if(nClose < 0) 2031 { 2032 /* there should always be a closing ] 2033 * but the old String class would have hidden 2034 * that. so be conservative too 2035 */ 2036 nClose = nLen; 2037 } 2038 2039 nPos = nClose; 2040 if(nDash >= 0 && nDash < nClose) 2041 { 2042 nPos = nDash; 2043 } 2044 aTmp.append(rStr.subView(nStartPos, nPos - nStartPos) ); 2045 nStartPos = nClose + 1; 2046 } 2047 } 2048 if ( nLen > nStartPos ) 2049 { 2050 aTmp.append(rStr.subView(nStartPos, nLen - nStartPos) ); 2051 } 2052 return aTmp.makeStringAndClear(); 2053 } 2054 2055 void SvNumberformat::ImpGetOutputStandard(double& fNumber, OUStringBuffer& rOutString) const 2056 { 2057 OUString sTemp; 2058 ImpGetOutputStandard(fNumber, sTemp); 2059 rOutString = sTemp; 2060 } 2061 2062 void SvNumberformat::ImpGetOutputStandard(double& fNumber, OUString& rOutString) const 2063 { 2064 sal_uInt16 nStandardPrec = rScan.GetStandardPrec(); 2065 2066 if ( fabs(fNumber) > EXP_ABS_UPPER_BOUND ) 2067 { 2068 nStandardPrec = ::std::min(nStandardPrec, static_cast<sal_uInt16>(14)); // limits to 14 decimals 2069 rOutString = ::rtl::math::doubleToUString( fNumber, 2070 rtl_math_StringFormat_E2, nStandardPrec /*2*/, 2071 GetFormatter().GetNumDecimalSep()[0]); 2072 } 2073 else 2074 { 2075 ImpGetOutputStdToPrecision(fNumber, rOutString, nStandardPrec); 2076 } 2077 } 2078 2079 namespace 2080 { 2081 2082 template<typename T> 2083 bool checkForAll0s(const T& rString, sal_Int32 nIdx=0) 2084 { 2085 if (nIdx>=rString.getLength()) 2086 return false; 2087 2088 do 2089 { 2090 if (rString[nIdx]!='0') 2091 return false; 2092 } 2093 while (++nIdx<rString.getLength()); 2094 2095 return true; 2096 } 2097 2098 } 2099 2100 void SvNumberformat::ImpGetOutputStdToPrecision(double& rNumber, OUString& rOutString, sal_uInt16 nPrecision) const 2101 { 2102 // Make sure the precision doesn't go over the maximum allowable precision. 2103 nPrecision = ::std::min(UPPER_PRECISION, nPrecision); 2104 2105 // We decided to strip trailing zeros unconditionally, since binary 2106 // double-precision rounding error makes it impossible to determine e.g. 2107 // whether 844.10000000000002273737 is what the user has typed, or the 2108 // user has typed 844.1 but IEEE 754 represents it that way internally. 2109 2110 rOutString = ::rtl::math::doubleToUString( rNumber, 2111 rtl_math_StringFormat_F, nPrecision /*2*/, 2112 GetFormatter().GetNumDecimalSep()[0], true ); 2113 if (rOutString[0] == '-' && checkForAll0s(rOutString, 1)) 2114 { 2115 rOutString = comphelper::string::stripStart(rOutString, '-'); // not -0 2116 } 2117 rOutString = impTransliterate(rOutString, NumFor[0].GetNatNum()); 2118 } 2119 2120 void SvNumberformat::ImpGetOutputInputLine(double fNumber, OUString& OutString) const 2121 { 2122 bool bModified = false; 2123 if ( (eType & SvNumFormatType::PERCENT) && (fabs(fNumber) < D_MAX_D_BY_100)) 2124 { 2125 if (fNumber == 0.0) 2126 { 2127 OutString = "0%"; 2128 return; 2129 } 2130 fNumber *= 100; 2131 bModified = true; 2132 } 2133 2134 if (fNumber == 0.0) 2135 { 2136 OutString = "0"; 2137 return; 2138 } 2139 2140 OutString = ::rtl::math::doubleToUString( fNumber, 2141 rtl_math_StringFormat_Automatic, 2142 rtl_math_DecimalPlaces_Max, 2143 GetFormatter().GetNumDecimalSep()[0], true ); 2144 2145 if ( eType & SvNumFormatType::PERCENT && bModified) 2146 { 2147 OutString += "%"; 2148 } 2149 } 2150 2151 short SvNumberformat::ImpCheckCondition(double fNumber, 2152 double fLimit, 2153 SvNumberformatLimitOps eOp) 2154 { 2155 switch(eOp) 2156 { 2157 case NUMBERFORMAT_OP_NO: 2158 return -1; 2159 case NUMBERFORMAT_OP_EQ: 2160 return static_cast<short>(fNumber == fLimit); 2161 case NUMBERFORMAT_OP_NE: 2162 return static_cast<short>(fNumber != fLimit); 2163 case NUMBERFORMAT_OP_LT: 2164 return static_cast<short>(fNumber < fLimit); 2165 case NUMBERFORMAT_OP_LE: 2166 return static_cast<short>(fNumber <= fLimit); 2167 case NUMBERFORMAT_OP_GT: 2168 return static_cast<short>(fNumber > fLimit); 2169 case NUMBERFORMAT_OP_GE: 2170 return static_cast<short>(fNumber >= fLimit); 2171 default: 2172 return -1; 2173 } 2174 } 2175 2176 static bool lcl_appendStarFillChar( OUStringBuffer& rBuf, std::u16string_view rStr ) 2177 { 2178 // Right during user input the star symbol is the very 2179 // last character before the user enters another one. 2180 if (rStr.size() > 1) 2181 { 2182 rBuf.append(u'\x001B'); 2183 rBuf.append(rStr[1]); 2184 return true; 2185 } 2186 return false; 2187 } 2188 2189 static bool lcl_insertStarFillChar( OUStringBuffer& rBuf, sal_Int32 nPos, std::u16string_view rStr ) 2190 { 2191 if (rStr.size() > 1) 2192 { 2193 rBuf.insert( nPos, rStr[1]); 2194 rBuf.insert( nPos, u'\x001B'); 2195 return true; 2196 } 2197 return false; 2198 } 2199 2200 void SvNumberformat::GetOutputString(std::u16string_view sString, 2201 OUString& OutString, 2202 const Color** ppColor) 2203 { 2204 OUStringBuffer sOutBuff; 2205 sal_uInt16 nIx; 2206 if (eType & SvNumFormatType::TEXT) 2207 { 2208 nIx = 0; 2209 } 2210 else if (NumFor[3].GetCount() > 0) 2211 { 2212 nIx = 3; 2213 } 2214 else 2215 { 2216 *ppColor = nullptr; // no change of color 2217 return; 2218 } 2219 *ppColor = NumFor[nIx].GetColor(); 2220 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info(); 2221 if (rInfo.eScannedType == SvNumFormatType::TEXT) 2222 { 2223 const sal_uInt16 nCnt = NumFor[nIx].GetCount(); 2224 for (sal_uInt16 i = 0; i < nCnt; i++) 2225 { 2226 switch (rInfo.nTypeArray[i]) 2227 { 2228 case NF_SYMBOLTYPE_STAR: 2229 if( bStarFlag ) 2230 { 2231 lcl_appendStarFillChar( sOutBuff, rInfo.sStrArray[i]); 2232 } 2233 break; 2234 case NF_SYMBOLTYPE_BLANK: 2235 if (rInfo.sStrArray[i].getLength() >= 2) 2236 InsertBlanks( sOutBuff, sOutBuff.getLength(), rInfo.sStrArray[i][1] ); 2237 break; 2238 case NF_KEY_GENERAL : // #77026# "General" is the same as "@" 2239 case NF_SYMBOLTYPE_DEL : 2240 sOutBuff.append(sString); 2241 break; 2242 default: 2243 sOutBuff.append(rInfo.sStrArray[i]); 2244 } 2245 } 2246 } 2247 OutString = sOutBuff.makeStringAndClear(); 2248 } 2249 2250 namespace { 2251 2252 void lcl_GetOutputStringScientific(double fNumber, sal_uInt16 nCharCount, 2253 const SvNumberFormatter& rFormatter, OUString& rOutString) 2254 { 2255 bool bSign = std::signbit(fNumber); 2256 2257 // 1.000E+015 (one digit and the decimal point, and the two chars + 2258 // nExpDigit for the exponential part, totalling 6 or 7). 2259 double fExp = log10( fabs(fNumber) ); 2260 if( fExp < 0.0 ) 2261 fExp = 1.0 - fExp; 2262 sal_uInt16 nCharFormat = 6 + (fExp >= 100.0 ? 1 : 0); 2263 sal_uInt16 nPrec = nCharCount > nCharFormat ? nCharCount - nCharFormat : 0; 2264 if (nPrec && bSign) 2265 { 2266 // Make room for the negative sign. 2267 --nPrec; 2268 } 2269 nPrec = ::std::min(nPrec, static_cast<sal_uInt16>(14)); // limit to 14 decimals. 2270 2271 rOutString = ::rtl::math::doubleToUString(fNumber, rtl_math_StringFormat_E2, 2272 nPrec, rFormatter.GetNumDecimalSep()[0], true ); 2273 } 2274 2275 OUString lcl_GetPercentString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt) 2276 { 2277 sal_Int32 i; 2278 OUStringBuffer aPercentString; 2279 for( i = 0; i < nCnt; i++ ) 2280 { 2281 if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_PERCENT ) 2282 { 2283 aPercentString.append( rInfo.sStrArray[i] ); 2284 bool bStringFound = false; 2285 for( i--; i >= 0 && rInfo.nTypeArray[i] == NF_SYMBOLTYPE_STRING ; i-- ) 2286 { 2287 if( !bStringFound ) 2288 { 2289 bStringFound = true; 2290 aPercentString.insert( 0, "\"" ); 2291 } 2292 aPercentString.insert( 0, rInfo.sStrArray[i] ); 2293 } 2294 i = nCnt; 2295 if( bStringFound ) 2296 aPercentString.insert( 0, "\"" ); 2297 } 2298 } 2299 return aPercentString.makeStringAndClear(); 2300 } 2301 2302 OUString lcl_GetDenominatorString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt) 2303 { 2304 sal_Int32 i; 2305 OUStringBuffer aDenominatorString; 2306 for( i = 0; i < nCnt; i++ ) 2307 { 2308 if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRAC ) 2309 { 2310 while ( ( ++i < nCnt ) && rInfo.nTypeArray[i] != NF_SYMBOLTYPE_FRAC_FDIV 2311 && rInfo.nTypeArray[i] != NF_SYMBOLTYPE_DIGIT ); 2312 for( ; i < nCnt; i++ ) 2313 { 2314 if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRAC_FDIV || rInfo.nTypeArray[i] == NF_SYMBOLTYPE_DIGIT ) 2315 aDenominatorString.append( rInfo.sStrArray[i] ); 2316 else 2317 i = nCnt; 2318 } 2319 } 2320 } 2321 return aDenominatorString.makeStringAndClear(); 2322 } 2323 2324 OUString lcl_GetNumeratorString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt) 2325 { 2326 sal_Int32 i; 2327 OUStringBuffer aNumeratorString; 2328 for( i = 0; i < nCnt; i++ ) 2329 { 2330 if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRAC ) 2331 { 2332 for( i--; i >= 0 && rInfo.nTypeArray[i] == NF_SYMBOLTYPE_DIGIT ; i-- ) 2333 { 2334 aNumeratorString.insert( 0, rInfo.sStrArray[i] ); 2335 } 2336 i = nCnt; 2337 } 2338 } 2339 return aNumeratorString.makeStringAndClear(); 2340 } 2341 2342 OUString lcl_GetFractionIntegerString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt) 2343 { 2344 sal_Int32 i; 2345 OUStringBuffer aIntegerString; 2346 for( i = 0; i < nCnt; i++ ) 2347 { 2348 if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRACBLANK ) 2349 { 2350 for( i--; i >= 0 && ( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_DIGIT 2351 || rInfo.nTypeArray[i] == NF_SYMBOLTYPE_THSEP ); i-- ) 2352 { 2353 aIntegerString.insert( 0, rInfo.sStrArray[i] ); 2354 } 2355 i = nCnt; 2356 } 2357 } 2358 return aIntegerString.makeStringAndClear(); 2359 } 2360 2361 OUString lcl_GetIntegerFractionDelimiterString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt) 2362 { 2363 sal_uInt16 i; 2364 for( i = 0; i < nCnt; i++ ) 2365 { 2366 if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRACBLANK ) 2367 { 2368 return rInfo.sStrArray[i]; 2369 } 2370 } 2371 return OUString(); 2372 } 2373 2374 } 2375 2376 OUString SvNumberformat::GetPercentString( sal_uInt16 nNumFor ) const 2377 { 2378 const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info(); 2379 sal_uInt16 nCnt = NumFor[nNumFor].GetCount(); 2380 return lcl_GetPercentString( rInfo, nCnt ); 2381 } 2382 2383 OUString SvNumberformat::GetDenominatorString( sal_uInt16 nNumFor ) const 2384 { 2385 const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info(); 2386 sal_uInt16 nCnt = NumFor[nNumFor].GetCount(); 2387 return lcl_GetDenominatorString( rInfo, nCnt ); 2388 } 2389 2390 OUString SvNumberformat::GetNumeratorString( sal_uInt16 nNumFor ) const 2391 { 2392 const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info(); 2393 sal_uInt16 nCnt = NumFor[nNumFor].GetCount(); 2394 return lcl_GetNumeratorString( rInfo, nCnt ); 2395 } 2396 2397 OUString SvNumberformat::GetIntegerFractionDelimiterString( sal_uInt16 nNumFor ) const 2398 { 2399 const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info(); 2400 sal_uInt16 nCnt = NumFor[nNumFor].GetCount(); 2401 return lcl_GetIntegerFractionDelimiterString( rInfo, nCnt ); 2402 } 2403 2404 bool SvNumberformat::GetOutputString(double fNumber, sal_uInt16 nCharCount, OUString& rOutString) const 2405 { 2406 if (eType != SvNumFormatType::NUMBER) 2407 { 2408 return false; 2409 } 2410 double fTestNum = fNumber; 2411 bool bSign = std::signbit(fTestNum); 2412 if (bSign) 2413 { 2414 fTestNum = -fTestNum; 2415 } 2416 if (fTestNum < EXP_LOWER_BOUND) 2417 { 2418 lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString); 2419 return true; 2420 } 2421 2422 double fExp = log10(fTestNum); 2423 // Values < 1.0 always have one digit before the decimal point. 2424 sal_uInt16 nDigitPre = fExp >= 0.0 ? static_cast<sal_uInt16>(ceil(fExp)) : 1; 2425 2426 if (nDigitPre > 15) 2427 { 2428 lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString); 2429 return true; 2430 } 2431 2432 sal_uInt16 nPrec = nCharCount >= nDigitPre ? nCharCount - nDigitPre : 0; 2433 if (nPrec && bSign) 2434 { 2435 // Subtract the negative sign. 2436 --nPrec; 2437 } 2438 if (nPrec) 2439 { 2440 // Subtract the decimal point. 2441 --nPrec; 2442 } 2443 ImpGetOutputStdToPrecision(fNumber, rOutString, nPrec); 2444 if (rOutString.getLength() > nCharCount) 2445 { 2446 // String still wider than desired. Switch to scientific notation. 2447 lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString); 2448 } 2449 return true; 2450 } 2451 2452 sal_uInt16 SvNumberformat::GetSubformatIndex (double fNumber ) const 2453 { 2454 sal_uInt16 nIx; // Index of the partial format 2455 double fLimit_1 = fLimit1; 2456 short nCheck = ImpCheckCondition(fNumber, fLimit_1, eOp1); 2457 if (nCheck == -1 || nCheck == 1) // Only 1 String or True 2458 { 2459 nIx = 0; 2460 } 2461 else 2462 { 2463 double fLimit_2 = fLimit2; 2464 nCheck = ImpCheckCondition(fNumber, fLimit_2, eOp2); 2465 if (nCheck == -1 || nCheck == 1) 2466 { 2467 nIx = 1; 2468 } 2469 else 2470 { 2471 nIx = 2; 2472 } 2473 } 2474 return nIx; 2475 } 2476 2477 bool SvNumberformat::GetOutputString(double fNumber, 2478 OUString& OutString, 2479 const Color** ppColor) 2480 { 2481 bool bRes = false; 2482 OutString.clear(); 2483 *ppColor = nullptr; // No color change 2484 if (eType & SvNumFormatType::LOGICAL && sFormatstring == rScan.GetKeywords()[NF_KEY_BOOLEAN]) 2485 { 2486 if (fNumber) 2487 { 2488 OutString = rScan.GetTrueString(); 2489 } 2490 else 2491 { 2492 OutString = rScan.GetFalseString(); 2493 } 2494 return false; 2495 } 2496 OUStringBuffer sBuff(64); 2497 if (eType & SvNumFormatType::TEXT) 2498 { 2499 ImpGetOutputStandard(fNumber, sBuff); 2500 OutString = sBuff.makeStringAndClear(); 2501 return false; 2502 } 2503 bool bHadStandard = false; 2504 if (bStandard) // Individual standard formats 2505 { 2506 if (rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION) // All number format InputLine 2507 { 2508 ImpGetOutputInputLine(fNumber, OutString); 2509 return false; 2510 } 2511 switch (eType) 2512 { 2513 case SvNumFormatType::NUMBER: // Standard number format 2514 if (rScan.GetStandardPrec() == SvNumberFormatter::UNLIMITED_PRECISION) 2515 { 2516 if (std::signbit(fNumber)) 2517 { 2518 if (!(fNumber < 0.0)) 2519 fNumber = -fNumber; // do not display -0.0 2520 } 2521 if (fNumber == 0.0) 2522 { 2523 OutString = "0"; 2524 } 2525 else if (fNumber < 1.0 && fNumber > -1.0) 2526 { 2527 // Decide whether to display as 0.000000123... or 1.23...e-07 2528 bool bFix = (fNumber < -EXP_LOWER_BOUND || EXP_LOWER_BOUND < fNumber); 2529 if (!bFix) 2530 { 2531 // Arbitrary, not too many 0s visually, start E2 at 1E-10. 2532 constexpr sal_Int32 kMaxExp = 9; 2533 const sal_Int32 nExp = static_cast<sal_Int32>(ceil( -log10( fabs( fNumber)))); 2534 if (nExp <= kMaxExp && rtl::math::approxEqual( 2535 rtl::math::round( fNumber, 16), rtl::math::round( fNumber, nExp + 16))) 2536 { 2537 // Not too many significant digits or accuracy 2538 // artefacts, otherwise leave everything to E2 2539 // format. 2540 bFix = true; 2541 } 2542 } 2543 if (bFix) 2544 OutString = ::rtl::math::doubleToUString( fNumber, 2545 rtl_math_StringFormat_F, 2546 rtl_math_DecimalPlaces_Max, 2547 GetFormatter().GetNumDecimalSep()[0], true); 2548 else 2549 OutString = ::rtl::math::doubleToUString( fNumber, 2550 rtl_math_StringFormat_E2, 2551 rtl_math_DecimalPlaces_Max, 2552 GetFormatter().GetNumDecimalSep()[0], true); 2553 } 2554 else 2555 { 2556 OutString = ::rtl::math::doubleToUString( fNumber, 2557 rtl_math_StringFormat_Automatic, 2558 rtl_math_DecimalPlaces_Max, 2559 GetFormatter().GetNumDecimalSep()[0], true); 2560 } 2561 return false; 2562 } 2563 ImpGetOutputStandard(fNumber, sBuff); 2564 bHadStandard = true; 2565 break; 2566 case SvNumFormatType::DATE: 2567 bRes |= ImpGetDateOutput(fNumber, 0, sBuff); 2568 bHadStandard = true; 2569 break; 2570 case SvNumFormatType::TIME: 2571 bRes |= ImpGetTimeOutput(fNumber, 0, sBuff); 2572 bHadStandard = true; 2573 break; 2574 case SvNumFormatType::DATETIME: 2575 bRes |= ImpGetDateTimeOutput(fNumber, 0, sBuff); 2576 bHadStandard = true; 2577 break; 2578 default: break; 2579 } 2580 } 2581 if ( !bHadStandard ) 2582 { 2583 sal_uInt16 nIx = GetSubformatIndex ( fNumber ); // Index of the partial format 2584 if (fNumber < 0.0 && 2585 ((nIx == 0 && IsFirstSubformatRealNegative()) || // 1st, usually positive subformat 2586 (nIx == 1 && IsSecondSubformatRealNegative()))) // 2nd, usually negative subformat 2587 { 2588 fNumber = -fNumber; // eliminate sign 2589 } 2590 *ppColor = NumFor[nIx].GetColor(); 2591 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info(); 2592 const sal_uInt16 nCnt = NumFor[nIx].GetCount(); 2593 if (nCnt == 0 && rInfo.eScannedType == SvNumFormatType::EMPTY) 2594 { 2595 return false; // Empty => nothing 2596 } 2597 else if (nCnt == 0) // Else Standard Format 2598 { 2599 ImpGetOutputStandard(fNumber, sBuff); 2600 OutString = sBuff.makeStringAndClear(); 2601 return false; 2602 } 2603 switch (rInfo.eScannedType) 2604 { 2605 case SvNumFormatType::TEXT: 2606 case SvNumFormatType::DEFINED: 2607 for (sal_uInt16 i = 0; i < nCnt; i++) 2608 { 2609 switch (rInfo.nTypeArray[i]) 2610 { 2611 case NF_SYMBOLTYPE_STAR: 2612 if( bStarFlag ) 2613 { 2614 bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]); 2615 } 2616 break; 2617 case NF_SYMBOLTYPE_BLANK: 2618 if (rInfo.sStrArray[i].getLength() >= 2) 2619 InsertBlanks(sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] ); 2620 break; 2621 case NF_SYMBOLTYPE_STRING: 2622 case NF_SYMBOLTYPE_CURRENCY: 2623 sBuff.append(rInfo.sStrArray[i]); 2624 break; 2625 case NF_SYMBOLTYPE_THSEP: 2626 if (rInfo.nThousand == 0) 2627 { 2628 sBuff.append(rInfo.sStrArray[i]); 2629 } 2630 break; 2631 default: 2632 break; 2633 } 2634 } 2635 break; 2636 case SvNumFormatType::DATE: 2637 bRes |= ImpGetDateOutput(fNumber, nIx, sBuff); 2638 break; 2639 case SvNumFormatType::TIME: 2640 bRes |= ImpGetTimeOutput(fNumber, nIx, sBuff); 2641 break; 2642 case SvNumFormatType::DATETIME: 2643 bRes |= ImpGetDateTimeOutput(fNumber, nIx, sBuff); 2644 break; 2645 case SvNumFormatType::NUMBER: 2646 case SvNumFormatType::PERCENT: 2647 case SvNumFormatType::CURRENCY: 2648 bRes |= ImpGetNumberOutput(fNumber, nIx, sBuff); 2649 break; 2650 case SvNumFormatType::LOGICAL: 2651 bRes |= ImpGetLogicalOutput(fNumber, nIx, sBuff); 2652 break; 2653 case SvNumFormatType::FRACTION: 2654 bRes |= ImpGetFractionOutput(fNumber, nIx, sBuff); 2655 break; 2656 case SvNumFormatType::SCIENTIFIC: 2657 bRes |= ImpGetScientificOutput(fNumber, nIx, sBuff); 2658 break; 2659 default: break; 2660 } 2661 } 2662 OutString = sBuff.makeStringAndClear(); 2663 return bRes; 2664 } 2665 2666 bool SvNumberformat::ImpGetScientificOutput(double fNumber, 2667 sal_uInt16 nIx, 2668 OUStringBuffer& sStr) 2669 { 2670 bool bRes = false; 2671 bool bSign = false; 2672 2673 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info(); 2674 const sal_uInt16 nCnt = NumFor[nIx].GetCount(); 2675 2676 if (fNumber < 0) 2677 { 2678 if (nIx == 0) // Not in the ones at the end 2679 { 2680 bSign = true; // Formats 2681 } 2682 fNumber = -fNumber; 2683 } 2684 2685 sStr = ::rtl::math::doubleToUString( fNumber, 2686 rtl_math_StringFormat_E, 2687 rInfo.nCntPre + rInfo.nCntPost - 1, '.' ); 2688 OUStringBuffer ExpStr; 2689 short nExpSign = 1; 2690 sal_Int32 nExPos = sStr.indexOf('E'); 2691 sal_Int32 nDecPos = -1; 2692 2693 if ( nExPos >= 0 ) 2694 { 2695 // split into mantissa and exponent and get rid of "E+" or "E-" 2696 sal_Int32 nExpStart = nExPos + 1; 2697 2698 switch ( sStr[ nExpStart ] ) 2699 { 2700 case '-' : 2701 nExpSign = -1; 2702 [[fallthrough]]; 2703 case '+' : 2704 ++nExpStart; 2705 break; 2706 } 2707 ExpStr = sStr.subView( nExpStart ); // part following the "E+" 2708 sStr.truncate( nExPos ); 2709 2710 if ( rInfo.nCntPre != 1 ) // rescale Exp 2711 { 2712 sal_Int32 nExp = OUString::unacquired(ExpStr).toInt32() * nExpSign; 2713 sal_Int32 nRescale = (rInfo.nCntPre != 0) ? nExp % static_cast<sal_Int32>(rInfo.nCntPre) : -1; 2714 if( nRescale < 0 && rInfo.nCntPre != 0 ) 2715 nRescale += static_cast<sal_Int32>(rInfo.nCntPre); 2716 nExp -= nRescale; 2717 if ( nExp < 0 ) 2718 { 2719 nExpSign = -1; 2720 nExp = -nExp; 2721 } 2722 else 2723 { 2724 nExpSign = 1; 2725 } 2726 ExpStr = OUString::number( nExp ); 2727 const sal_Unicode cFirstDigit = sStr[0]; 2728 // rescale mantissa 2729 sStr = ::rtl::math::doubleToUString( fNumber, 2730 rtl_math_StringFormat_E, 2731 nRescale + rInfo.nCntPost, '.' ); 2732 2733 // sStr now may contain a rounded-up value shifted into the next 2734 // magnitude, for example 1.000E+02 (4 digits) for fNumber 99.995 2735 // (9.9995E+02 rounded to 3 decimals) but we want the final result 2736 // to be 100.00E+00 (5 digits), so for the following fill routines 2737 // below to work correctly append a zero decimal. 2738 /* TODO: this is awkward, could an engineering notation mode be 2739 * introduced to rtl_math_doubleToUString()? */ 2740 sStr.truncate( sStr.indexOf('E') ); 2741 if (sStr[0] == '1' && cFirstDigit != '1') 2742 sStr.append('0'); 2743 } 2744 2745 // cut any decimal delimiter 2746 sal_Int32 index = 0; 2747 2748 while((index = sStr.indexOf('.', index)) >= 0) 2749 { 2750 if (nDecPos < 0) 2751 nDecPos = index; 2752 sStr.remove(index, 1); 2753 } 2754 } 2755 2756 sal_uInt16 j = nCnt-1; // Last symbol 2757 sal_Int32 k; // Position in ExpStr 2758 sal_Int32 nZeros = 0; // Erase leading zeros 2759 2760 bRes |= ImpNumberFill(ExpStr, fNumber, k, j, nIx, NF_SYMBOLTYPE_EXP); 2761 2762 while (nZeros < k && ExpStr[nZeros] == '0') 2763 { 2764 ++nZeros; 2765 } 2766 if (nZeros) 2767 { 2768 ExpStr.remove( 0, nZeros); 2769 } 2770 2771 bool bCont = true; 2772 2773 if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_EXP) 2774 { 2775 const OUString& rStr = rInfo.sStrArray[j]; 2776 if (nExpSign == -1) 2777 { 2778 ExpStr.insert(0, '-'); 2779 } 2780 else if (rStr.getLength() > 1 && rStr[1] == '+') 2781 { 2782 ExpStr.insert(0, '+'); 2783 } 2784 ExpStr.insert(0, rStr[0]); 2785 if ( j ) 2786 { 2787 j--; 2788 } 2789 else 2790 { 2791 bCont = false; 2792 } 2793 } 2794 // Continue main number: 2795 if ( !bCont ) 2796 { 2797 sStr.truncate(); 2798 } 2799 else 2800 { 2801 bRes |= ImpDecimalFill(sStr, fNumber, nDecPos, j, nIx, false); 2802 } 2803 2804 if (bSign) 2805 { 2806 sStr.insert(0, '-'); 2807 } 2808 sStr.append(ExpStr); 2809 2810 return bRes; 2811 } 2812 2813 double SvNumberformat::GetRoundFractionValue ( double fNumber ) const 2814 { 2815 sal_uInt16 nIx = GetSubformatIndex ( fNumber ); 2816 double fIntPart = 0.0; // integer part of fraction 2817 sal_Int64 nFrac = 0, nDiv = 1; // numerator and denominator 2818 double fSign = (fNumber < 0.0) ? -1.0 : 1.0; 2819 // fNumber is modified in ImpGetFractionElements to absolute fractional part 2820 ImpGetFractionElements ( fNumber, nIx, fIntPart, nFrac, nDiv ); 2821 if ( nDiv > 0 ) 2822 return fSign * ( fIntPart + static_cast<double>(nFrac) / static_cast<double>(nDiv) ); 2823 else 2824 return fSign * fIntPart; 2825 } 2826 2827 void SvNumberformat::ImpGetFractionElements ( double& fNumber, sal_uInt16 nIx, 2828 double& fIntPart, sal_Int64& nFrac, sal_Int64& nDiv ) const 2829 { 2830 if ( fNumber < 0.0 ) 2831 fNumber = -fNumber; 2832 fIntPart = floor(fNumber); // Integral part 2833 fNumber -= fIntPart; // Fractional part 2834 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info(); 2835 nDiv = lcl_GetDenominatorString( rInfo, NumFor[nIx].GetCount() ).toInt32(); 2836 if( nDiv > 0 ) 2837 { // Forced Denominator 2838 nFrac = static_cast<sal_Int64>(floor ( fNumber * nDiv )); 2839 double fFracNew = static_cast<double>(nFrac) / static_cast<double>(nDiv); 2840 double fFracNew1 = static_cast<double>(nFrac + 1) / static_cast<double>(nDiv); 2841 double fDiff = fNumber - fFracNew; 2842 if( fDiff > ( fFracNew1 - fNumber ) ) 2843 { 2844 nFrac++; 2845 } 2846 } 2847 else // Calculated Denominator 2848 { 2849 nDiv = 1; 2850 sal_Int64 nBasis = static_cast<sal_Int64>(floor( pow(10.0,rInfo.nCntExp))) - 1; // 9, 99, 999 ,... 2851 sal_Int64 nFracPrev = 1, nDivPrev = 0, nFracNext, nDivNext, nPartialDenom; 2852 double fRemainder = fNumber; 2853 2854 // Use continued fraction representation of fNumber 2855 // See https://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximations 2856 while ( fRemainder > 0.0 ) 2857 { 2858 double fTemp = 1.0 / fRemainder; // 64bits precision required when fRemainder is very weak 2859 nPartialDenom = static_cast<sal_Int64>(floor(fTemp)); // due to floating point notation with double precision 2860 fRemainder = fTemp - static_cast<double>(nPartialDenom); 2861 nDivNext = nPartialDenom * nDiv + nDivPrev; 2862 if ( nDivNext <= nBasis ) // continue loop 2863 { 2864 nFracNext = nPartialDenom * nFrac + nFracPrev; 2865 nFracPrev = nFrac; 2866 nFrac = nFracNext; 2867 nDivPrev = nDiv; 2868 nDiv = nDivNext; 2869 } 2870 else // calculate collateral fraction and exit 2871 { 2872 sal_Int64 nCollat = (nBasis - nDivPrev) / nDiv; 2873 if ( 2 * nCollat >= nPartialDenom ) 2874 { 2875 sal_Int64 nFracTest = nCollat * nFrac + nFracPrev; 2876 sal_Int64 nDivTest = nCollat * nDiv + nDivPrev; 2877 double fSign = (static_cast<double>(nFrac) > fNumber * static_cast<double>(nDiv))?1.0:-1.0; 2878 if ( fSign * ( double(nFrac * nDivTest + nDiv * nFracTest) - 2.0 * double(nDiv * nDivTest) * fNumber ) > 0.0 ) 2879 { 2880 nFrac = nFracTest; 2881 nDiv = nDivTest; 2882 } 2883 } 2884 fRemainder = 0.0; // exit while loop 2885 } 2886 } 2887 } 2888 if (nFrac >= nDiv) 2889 { 2890 ++fIntPart; 2891 nFrac = nDiv = 0; 2892 } 2893 } 2894 2895 bool SvNumberformat::ImpGetFractionOutput(double fNumber, 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 { 2913 sBuff = ImpSvNumberformatScan::sErrStr; 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 { 2931 sBuff = ImpSvNumberformatScan::sErrStr; 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 3052 sal_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 3083 bool SvNumberformat::ImpGetTimeOutput(double fNumber, 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; 3103 if ( rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION && 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 { 3138 sBuff = ImpSvNumberformatScan::sErrStr; 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; 3206 case NF_SYMBOLTYPE_BLANK: 3207 if (rInfo.sStrArray[i].getLength() >= 2) 3208 InsertBlanks(sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] ); 3209 break; 3210 case NF_SYMBOLTYPE_STRING: 3211 case NF_SYMBOLTYPE_CURRENCY: 3212 case NF_SYMBOLTYPE_DATESEP: 3213 case NF_SYMBOLTYPE_TIMESEP: 3214 case NF_SYMBOLTYPE_TIME100SECSEP: 3215 sBuff.append(rInfo.sStrArray[i]); 3216 break; 3217 case NF_SYMBOLTYPE_DIGIT: 3218 nLen = ( bInputLine && i > 0 && 3219 (rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING || 3220 rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_TIME100SECSEP) ? 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(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 3287 /** If a day of month occurs within the format, the month name is in possessive 3288 genitive case if the day follows the month, and partitive case if the day 3289 precedes the month. If there is no day of month the nominative case (noun) 3290 is returned. Also if the month is immediately preceded or followed by a 3291 literal string other than space and not followed by a comma, the nominative 3292 name is used, this prevents duplicated casing for MMMM\t\a and such in 3293 documents imported from (e.g. Finnish) Excel or older LibO/OOo releases. 3294 */ 3295 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 3300 sal_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 3406 bool SvNumberformat::ImpIsOtherCalendar( const ImpSvNumFor& rNumFor ) const 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 { 3419 case NF_SYMBOLTYPE_CALENDAR : 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 3436 void 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( 3445 rLoc().getLanguageTag().getLocale() ); 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 3464 void 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 3475 bool 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 3508 bool 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 3534 void 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 3572 bool SvNumberformat::ImpIsIso8601( const ImpSvNumFor& rNumFor ) const 3573 { 3574 bool bIsIso = false; 3575 if (eType & SvNumFormatType::DATE) 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; 3626 case NF_SYMBOLTYPE_STRING: 3627 case NF_SYMBOLTYPE_DATESEP: 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 3660 static 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 3678 static 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. */ 3686 static 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 = aMin - aCE; 3692 static const double fMax = aMax - aCE; 3693 // Value must be representable in our tools::Date proleptic Gregorian 3694 // calendar as well. 3695 const double fOff = (rNullDate - aCE) + fNumber; 3696 // Add diff between epochs to serial date number. 3697 const double fDiff = rNullDate - rEpochStart; 3698 fNumber += fDiff; 3699 return fMin <= fOff && fOff <= fMax; 3700 } 3701 3702 bool SvNumberformat::ImpGetDateOutput(double fNumber, 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 { 3712 sBuff = ImpSvNumberformatScan::sErrStr; 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 { 3756 case NF_SYMBOLTYPE_CALENDAR : 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; 3772 case NF_SYMBOLTYPE_BLANK: 3773 if (rInfo.sStrArray[i].getLength() >= 2) 3774 InsertBlanks( sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] ); 3775 break; 3776 case NF_SYMBOLTYPE_STRING: 3777 case NF_SYMBOLTYPE_CURRENCY: 3778 case NF_SYMBOLTYPE_DATESEP: 3779 case NF_SYMBOLTYPE_TIMESEP: 3780 case NF_SYMBOLTYPE_TIME100SECSEP: 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 3972 bool SvNumberformat::ImpGetDateTimeOutput(double fNumber, 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 { 3982 sBuff = ImpSvNumberformatScan::sErrStr; 3983 return false; 3984 } 3985 3986 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info(); 3987 bool bInputLine; 3988 sal_Int32 nCntPost, nFirstRounding; 3989 if ( rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION && 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 { 4100 case NF_SYMBOLTYPE_CALENDAR : 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; 4116 case NF_SYMBOLTYPE_BLANK: 4117 if (rInfo.sStrArray[i].getLength() >= 2) 4118 InsertBlanks( sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] ); 4119 break; 4120 case NF_SYMBOLTYPE_STRING: 4121 case NF_SYMBOLTYPE_CURRENCY: 4122 case NF_SYMBOLTYPE_DATESEP: 4123 case NF_SYMBOLTYPE_TIMESEP: 4124 case NF_SYMBOLTYPE_TIME100SECSEP: 4125 sBuff.append(rInfo.sStrArray[i]); 4126 break; 4127 case NF_SYMBOLTYPE_DIGIT: 4128 nLen = ( bInputLine && i > 0 && 4129 (rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING || 4130 rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_TIME100SECSEP) ? 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 4326 bool SvNumberformat::ImpGetLogicalOutput(double fNumber, 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; 4340 case NF_SYMBOLTYPE_STRING: 4341 sStr.append( rInfo.sStrArray[j]); 4342 break; 4343 } 4344 } 4345 impTransliterate(sStr, NumFor[nIx].GetNatNum()); 4346 return bRes; 4347 } 4348 4349 bool SvNumberformat::ImpGetNumberOutput(double fNumber, 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(); 4369 if (rInfo.eScannedType == SvNumFormatType::PERCENT) 4370 { 4371 if (fNumber < D_MAX_D_BY_100) 4372 { 4373 fNumber *= 100.0; 4374 } 4375 else 4376 { 4377 sStr = ImpSvNumberformatScan::sErrStr; 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 4464 bool 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; 4491 case NF_SYMBOLTYPE_BLANK: 4492 if (rInfo.sStrArray[j].getLength() >= 2) 4493 /*k = */ InsertBlanks(sStr, k, rInfo.sStrArray[j][1] ); 4494 break; 4495 case NF_SYMBOLTYPE_STRING: 4496 case NF_SYMBOLTYPE_CURRENCY: 4497 case NF_SYMBOLTYPE_PERCENT: 4498 sStr.insert(k, rInfo.sStrArray[j]); 4499 break; 4500 case NF_SYMBOLTYPE_THSEP: 4501 if (rInfo.nThousand == 0) 4502 { 4503 sStr.insert(k, rInfo.sStrArray[j]); 4504 } 4505 break; 4506 case NF_SYMBOLTYPE_DIGIT: 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 4585 bool 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 { 4610 case NF_SYMBOLTYPE_DECSEP: 4611 aGrouping.reset(); 4612 [[fallthrough]]; 4613 case NF_SYMBOLTYPE_STRING: 4614 case NF_SYMBOLTYPE_CURRENCY: 4615 case NF_SYMBOLTYPE_PERCENT: 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; 4629 case NF_SYMBOLTYPE_BLANK: 4630 if (rInfo.sStrArray[j].getLength() >= 2) 4631 /*k = */ InsertBlanks(sBuff, k, rInfo.sStrArray[j][1] ); 4632 break; 4633 case NF_SYMBOLTYPE_THSEP: 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; 4689 case NF_SYMBOLTYPE_DIGIT: 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 4746 void 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 4773 bool 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; 4803 case NF_SYMBOLTYPE_BLANK: 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; 4811 case NF_SYMBOLTYPE_THSEP: 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; 4828 case NF_SYMBOLTYPE_DIGIT: 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 4889 void SvNumberformat::GetFormatSpecialInfo(bool& bThousand, 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 4906 void 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; 4923 if ( rInfo.eScannedType == SvNumFormatType::FRACTION && 4924 ( (nPosHash += GetDenominatorString(nNumFor).indexOf('#')) > 0 ) ) 4925 nPrecision -= nPosHash; 4926 if (bStandard && rInfo.eScannedType == SvNumFormatType::NUMBER) 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 4954 || nType == NF_SYMBOLTYPE_EXP 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 4964 const 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 5017 short 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 5039 bool SvNumberformat::IsNegativeWithoutSign() const 5040 { 5041 if ( IsSecondSubformatRealNegative() ) 5042 { 5043 const OUString* pStr = GetNumForString( 1, 0, true ); 5044 if ( pStr ) 5045 { 5046 return !HasStringNegativeSign( *pStr ); 5047 } 5048 } 5049 return false; 5050 } 5051 5052 bool SvNumberformat::IsNegativeInBracket() const 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 5063 bool SvNumberformat::HasPositiveBracketPlaceholder() const 5064 { 5065 sal_uInt16 nCnt = NumFor[0].GetCount(); 5066 return NumFor[0].Info().sStrArray[nCnt-1] == "_)"; 5067 } 5068 5069 DateOrder SvNumberformat::GetDateOrder() const 5070 { 5071 if ( eType & SvNumFormatType::DATE ) 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 5105 sal_uInt32 SvNumberformat::GetExactDateOrder() const 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 5147 void SvNumberformat::GetConditions( SvNumberformatLimitOps& rOper1, double& rVal1, 5148 SvNumberformatLimitOps& rOper2, double& rVal2 ) const 5149 { 5150 rOper1 = eOp1; 5151 rOper2 = eOp2; 5152 rVal1 = fLimit1; 5153 rVal2 = fLimit2; 5154 } 5155 5156 const Color* SvNumberformat::GetColor( sal_uInt16 nNumFor ) const 5157 { 5158 if ( nNumFor > 3 ) 5159 { 5160 return nullptr; 5161 } 5162 return NumFor[nNumFor].GetColor(); 5163 } 5164 5165 static void lcl_SvNumberformat_AddLimitStringImpl( OUString& rStr, 5166 SvNumberformatLimitOps eOp, 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 5202 static 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 5222 /** Increment nAlphabetID for CJK numerals 5223 * +1 for financial numerals [NatNum2] 5224 * +2 for Arabic fullwidth numerals [NatNum3] 5225 * */ 5226 static 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 5235 OUString SvNumberformat::GetMappedFormatstring( const NfKeywordTable& rKeywords, 5236 const LocaleDataWrapper& rLocWrp, 5237 LanguageType nOriginalLang /* =LANGUAGE_DONTKNOW */, 5238 bool bSystemLanguage /* =false */ ) const 5239 { 5240 OUStringBuffer aStr; 5241 if (maLocale.meSubstitute != LocaleType::Substitute::NONE) 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 5267 if ( eOp1 != NUMBERFORMAT_OP_NO && eOp2 == NUMBERFORMAT_OP_NO ) 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 : 5308 lcl_SvNumberformat_AddLimitStringImpl( aPrefix, eOp1, 5309 fLimit1, rLocWrp.getNumDecimalSep() ); 5310 break; 5311 case 1 : 5312 lcl_SvNumberformat_AddLimitStringImpl( aPrefix, eOp2, 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 { 5405 case NF_SYMBOLTYPE_DECSEP : 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; 5419 case NF_SYMBOLTYPE_DATESEP : 5420 aStr.append( rLocWrp.getDateSep() ); 5421 break; 5422 case NF_SYMBOLTYPE_TIMESEP : 5423 aStr.append( rLocWrp.getTimeSep() ); 5424 break; 5425 case NF_SYMBOLTYPE_TIME100SECSEP : 5426 aStr.append( rLocWrp.getTime100SecSep() ); 5427 break; 5428 case NF_SYMBOLTYPE_FRACBLANK : 5429 case NF_SYMBOLTYPE_STRING : 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( '"' ); 5439 aStr.append( rStrArray[j] ); 5440 aStr.append( '"' ); 5441 } 5442 break; 5443 case NF_SYMBOLTYPE_CALDEL : 5444 if (j + 1 >= nCnt) 5445 break; 5446 if ( rStrArray[j+1] == "gengou" ) 5447 { 5448 nCalendarID = kCalGengou; 5449 } 5450 else if ( rStrArray[j+1] == "hijri" ) 5451 { 5452 nCalendarID = 0x0060000; 5453 } 5454 else if ( rStrArray[j+1] == "buddhist" ) 5455 { 5456 nCalendarID = 0x0070000; 5457 } 5458 else if ( rStrArray[j+1] == "jewish" ) 5459 { 5460 nCalendarID = 0x0080000; 5461 } 5462 // Other calendars (see tdf#36038) not corresponding between LibO and XL. 5463 // However, skip any calendar modifier and don't write 5464 // as format code (if not as literal string). 5465 j += 2; 5466 break; 5467 case NF_SYMBOLTYPE_CURREXT : 5468 nPosHaveLCID = aStr.getLength(); 5469 aStr.append( rStrArray[j] ); 5470 break; 5471 default: 5472 aStr.append( rStrArray[j] ); 5473 } 5474 } 5475 } 5476 } 5477 sal_uInt32 nAlphabetID = 0x0000000; // Excel ID of alphabet used for numerals see tdf#36038 5478 LanguageType nLanguageID = LANGUAGE_SYSTEM; 5479 if ( aNatNum.IsComplete() ) 5480 { 5481 nLanguageID = MsLangId::getRealLanguage( aNatNum.GetLang()); 5482 if ( aNatNum.GetNatNum() == 0 ) 5483 { 5484 nAlphabetID = 0x01000000; // Arabic-european numerals 5485 } 5486 else if ( nCalendarID > 0 || aNatNum.GetDBNum() == 0 || aNatNum.GetDBNum() == aNatNum.GetNatNum() ) 5487 { // if no DBNum code then use long LCID 5488 // if DBNum value != NatNum value, use DBNum and not extended LCID 5489 // if calendar, then DBNum will be removed 5490 LanguageType pri = primary(nLanguageID); 5491 if ( pri == LANGUAGE_ARABIC_PRIMARY_ONLY ) 5492 nAlphabetID = 0x02000000; // Arabic-indic numerals 5493 else if ( pri == primary(LANGUAGE_FARSI) ) 5494 nAlphabetID = 0x03000000; // Farsi numerals 5495 else if ( pri.anyOf( 5496 primary(LANGUAGE_HINDI), 5497 primary(LANGUAGE_MARATHI), 5498 primary(LANGUAGE_NEPALI) )) 5499 nAlphabetID = 0x04000000; // Devanagari numerals 5500 else if ( pri == primary(LANGUAGE_BENGALI) ) 5501 nAlphabetID = 0x05000000; // Bengali numerals 5502 else if ( pri == primary(LANGUAGE_PUNJABI) ) 5503 { 5504 if ( nLanguageID == LANGUAGE_PUNJABI_ARABIC_LSO ) 5505 nAlphabetID = 0x02000000; // Arabic-indic numerals 5506 else 5507 nAlphabetID = 0x06000000; // Punjabi numerals 5508 } 5509 else if ( pri == primary(LANGUAGE_GUJARATI) ) 5510 nAlphabetID = 0x07000000; // Gujarati numerals 5511 else if ( pri == primary(LANGUAGE_ODIA)) 5512 nAlphabetID = 0x08000000; // Odia (Oriya) numerals 5513 else if ( pri == primary(LANGUAGE_TAMIL)) 5514 nAlphabetID = 0x09000000; // Tamil numerals 5515 else if ( pri == primary(LANGUAGE_TELUGU)) 5516 nAlphabetID = 0x0A000000; // Telugu numerals 5517 else if ( pri == primary(LANGUAGE_KANNADA)) 5518 nAlphabetID = 0x0B000000; // Kannada numerals 5519 else if ( pri == primary(LANGUAGE_MALAYALAM)) 5520 nAlphabetID = 0x0C000000; // Malayalam numerals 5521 else if ( pri == primary(LANGUAGE_THAI)) 5522 { 5523 // The Thai T NatNum modifier during Xcl export. 5524 if ( rKeywords[NF_KEY_THAI_T] == "T" ) 5525 nAlphabetID = 0x0D000000; // Thai numerals 5526 } 5527 else if ( pri == primary(LANGUAGE_LAO)) 5528 nAlphabetID = 0x0E000000; // Lao numerals 5529 else if ( pri == primary(LANGUAGE_TIBETAN)) 5530 nAlphabetID = 0x0F000000; // Tibetan numerals 5531 else if ( pri == primary(LANGUAGE_BURMESE)) 5532 nAlphabetID = 0x10000000; // Burmese numerals 5533 else if ( pri == primary(LANGUAGE_TIGRIGNA_ETHIOPIA)) 5534 nAlphabetID = 0x11000000; // Tigrigna numerals 5535 else if ( pri == primary(LANGUAGE_KHMER)) 5536 nAlphabetID = 0x12000000; // Khmer numerals 5537 else if ( pri == primary(LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA)) 5538 { 5539 if ( nLanguageID != LANGUAGE_MONGOLIAN_CYRILLIC_MONGOLIA 5540 && nLanguageID != LANGUAGE_MONGOLIAN_CYRILLIC_LSO ) 5541 nAlphabetID = 0x13000000; // Mongolian numerals 5542 } 5543 // CJK numerals 5544 else if ( pri == primary(LANGUAGE_JAPANESE)) 5545 { 5546 nAlphabetID = 0x1B; 5547 lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() ); 5548 } 5549 else if ( pri == primary(LANGUAGE_CHINESE)) 5550 { 5551 if ( nLanguageID == LANGUAGE_CHINESE_TRADITIONAL 5552 || nLanguageID == LANGUAGE_CHINESE_HONGKONG 5553 || nLanguageID == LANGUAGE_CHINESE_MACAU ) 5554 { 5555 nAlphabetID = 0x21; 5556 lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() ); 5557 } 5558 else // LANGUAGE_CHINESE_SIMPLIFIED 5559 { 5560 nAlphabetID = 0x1E; 5561 lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() ); 5562 } 5563 } 5564 else if ( pri == primary(LANGUAGE_KOREAN)) 5565 { 5566 if ( aNatNum.GetNatNum() == 9 ) // Hangul 5567 { 5568 nAlphabetID = 0x27000000; 5569 } 5570 else 5571 { 5572 nAlphabetID = 0x24; 5573 lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() ); 5574 } 5575 } 5576 } 5577 // Add LCID to DBNum 5578 if ( aNatNum.GetDBNum() > 0 && nLanguageID == LANGUAGE_SYSTEM ) 5579 nLanguageID = MsLangId::getRealLanguage( aNatNum.GetLang()); 5580 } 5581 else if (nPosHaveLCID < 0) 5582 { 5583 // Do not insert a duplicated LCID that was already given with a 5584 // currency format as [$R-1C09] 5585 if (!bSystemLanguage && nOriginalLang != LANGUAGE_DONTKNOW) 5586 { 5587 // Explicit locale, write only to the first subformat. 5588 if (n == 0) 5589 nLanguageID = MsLangId::getRealLanguage( nOriginalLang); 5590 } 5591 else if (bSystemLanguage && maLocale.meLanguageWithoutLocaleData != LANGUAGE_DONTKNOW) 5592 { 5593 // Explicit locale but no locale data thus assigned to system 5594 // locale, preserve for roundtrip, write only to the first 5595 // subformat. 5596 if (n == 0) 5597 nLanguageID = maLocale.meLanguageWithoutLocaleData; 5598 } 5599 } 5600 if ( nCalendarID > 0 ) 5601 { // Add alphabet and language to calendar 5602 if ( nAlphabetID == 0 ) 5603 nAlphabetID = 0x01000000; 5604 if ( nLanguageID == LANGUAGE_SYSTEM && nOriginalLang != LANGUAGE_DONTKNOW ) 5605 nLanguageID = nOriginalLang; 5606 } 5607 lcl_insertLCID( aStr, nAlphabetID + nCalendarID + static_cast<sal_uInt16>(nLanguageID), nPosInsertLCID, 5608 bDBNumInserted); 5609 } 5610 for ( ; nSub<4 && bDefault[nSub]; ++nSub ) 5611 { // append empty subformats 5612 aStr.append( ';' ); 5613 } 5614 return aStr.makeStringAndClear(); 5615 } 5616 5617 OUString SvNumberformat::ImpGetNatNumString( const SvNumberNatNum& rNum, 5618 sal_Int64 nVal, sal_uInt16 nMinDigits ) const 5619 { 5620 OUString aStr; 5621 if ( nMinDigits ) 5622 { 5623 if ( nMinDigits == 2 ) 5624 { 5625 // speed up the most common case 5626 if ( 0 <= nVal && nVal < 10 ) 5627 { 5628 sal_Unicode aBuf[2]; 5629 aBuf[0] = '0'; 5630 aBuf[1] = '0' + nVal; 5631 aStr = OUString(aBuf, SAL_N_ELEMENTS(aBuf)); 5632 } 5633 else 5634 { 5635 aStr = OUString::number( nVal ); 5636 } 5637 } 5638 else 5639 { 5640 OUString aValStr( OUString::number( nVal ) ); 5641 if ( aValStr.getLength() >= nMinDigits ) 5642 { 5643 aStr = aValStr; 5644 } 5645 else 5646 { 5647 OUStringBuffer aBuf; 5648 for(sal_Int32 index = 0; index < nMinDigits - aValStr.getLength(); ++index) 5649 { 5650 aBuf.append('0'); 5651 } 5652 aBuf.append(aValStr); 5653 aStr = aBuf.makeStringAndClear(); 5654 } 5655 } 5656 } 5657 else 5658 { 5659 aStr = OUString::number( nVal ); 5660 } 5661 return impTransliterate(aStr, rNum); 5662 } 5663 5664 OUString SvNumberformat::impTransliterateImpl(const OUString& rStr, 5665 const SvNumberNatNum& rNum ) const 5666 { 5667 css::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() ); 5668 return GetFormatter().GetNatNum()->getNativeNumberStringParams(rStr, aLocale, rNum.GetNatNum(), 5669 rNum.GetParams()); 5670 } 5671 5672 void SvNumberformat::impTransliterateImpl(OUStringBuffer& rStr, 5673 const SvNumberNatNum& rNum ) const 5674 { 5675 css::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() ); 5676 5677 rStr = GetFormatter().GetNatNum()->getNativeNumberStringParams( 5678 OUString::unacquired(rStr), aLocale, rNum.GetNatNum(), rNum.GetParams()); 5679 } 5680 5681 OUString SvNumberformat::impTransliterateImpl(const OUString& rStr, 5682 const SvNumberNatNum& rNum, 5683 const sal_uInt16 nDateKey) const 5684 { 5685 // no KEYWORD=argument list in NatNum12 5686 if (rNum.GetParams().indexOf('=') == -1) 5687 return impTransliterateImpl( rStr, rNum); 5688 5689 const NfKeywordTable & rKeywords = rScan.GetKeywords(); 5690 5691 // Format: KEYWORD=numbertext_prefix, ..., for example: 5692 // [NatNum12 YYYY=title ordinal,MMMM=article, D=ordinal-number] 5693 sal_Int32 nField = -1; 5694 do 5695 { 5696 nField = rNum.GetParams().indexOf(Concat2View(rKeywords[nDateKey] + "="), ++nField); 5697 } 5698 while (nField != -1 && nField != 0 && 5699 (rNum.GetParams()[nField - 1] != ',' && 5700 rNum.GetParams()[nField - 1] != ' ')); 5701 5702 // no format specified for actual keyword 5703 if (nField == -1) 5704 return rStr; 5705 5706 sal_Int32 nKeywordLen = rKeywords[nDateKey].getLength() + 1; 5707 sal_Int32 nFieldEnd = rNum.GetParams().indexOf(',', nField); 5708 5709 if (nFieldEnd == -1) 5710 nFieldEnd = rNum.GetParams().getLength(); 5711 5712 css::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() ); 5713 5714 return GetFormatter().GetNatNum()->getNativeNumberStringParams( 5715 rStr, aLocale, rNum.GetNatNum(), 5716 rNum.GetParams().copy(nField + nKeywordLen, nFieldEnd - nField - nKeywordLen)); 5717 } 5718 5719 void SvNumberformat::GetNatNumXml( css::i18n::NativeNumberXmlAttributes2& rAttr, 5720 sal_uInt16 nNumFor ) const 5721 { 5722 if ( nNumFor <= 3 ) 5723 { 5724 const SvNumberNatNum& rNum = NumFor[nNumFor].GetNatNum(); 5725 if ( rNum.IsSet() ) 5726 { 5727 css::lang::Locale aLocale( 5728 LanguageTag( rNum.GetLang() ).getLocale() ); 5729 5730 /* TODO: a new XNativeNumberSupplier2::convertToXmlAttributes() 5731 * should rather return NativeNumberXmlAttributes2 and places 5732 * adapted, and whether to fill Spellout or something different 5733 * should be internal there. */ 5734 css::i18n::NativeNumberXmlAttributes aTmp( 5735 GetFormatter().GetNatNum()->convertToXmlAttributes( 5736 aLocale, rNum.GetNatNum())); 5737 rAttr.Locale = aTmp.Locale; 5738 rAttr.Format = aTmp.Format; 5739 rAttr.Style = aTmp.Style; 5740 if ( NatNumTakesParameters(rNum.GetNatNum()) ) 5741 { 5742 // NatNum12 spell out numbers, dates and money amounts 5743 rAttr.Spellout = rNum.GetParams(); 5744 // Mutually exclusive. 5745 rAttr.Format.clear(); 5746 rAttr.Style.clear(); 5747 } 5748 else 5749 { 5750 rAttr.Spellout.clear(); 5751 } 5752 } 5753 else 5754 { 5755 rAttr = css::i18n::NativeNumberXmlAttributes2(); 5756 } 5757 } 5758 else 5759 { 5760 rAttr = css::i18n::NativeNumberXmlAttributes2(); 5761 } 5762 } 5763 5764 OUString SvNumberformat::GetNatNumModifierString( sal_uInt16 nNumFor ) const 5765 { 5766 if ( nNumFor > 3 ) 5767 return ""; 5768 const SvNumberNatNum& rNum = NumFor[nNumFor].GetNatNum(); 5769 if ( !rNum.IsSet() ) 5770 return ""; 5771 OUStringBuffer sNatNumModifier = "[NatNum"; 5772 const sal_Int32 nNum = rNum.GetNatNum(); 5773 sNatNumModifier.append( nNum ); 5774 if ( NatNumTakesParameters( nNum ) ) 5775 { 5776 sNatNumModifier.append( " " ); 5777 sNatNumModifier.append( rNum.GetParams() ); 5778 } 5779 sNatNumModifier.append( "]" ); 5780 5781 return sNatNumModifier.makeStringAndClear(); 5782 } 5783 5784 // static 5785 bool SvNumberformat::HasStringNegativeSign( const OUString& rStr ) 5786 { 5787 // For Sign '-' needs to be at the start or at the end of the string (blanks ignored) 5788 sal_Int32 nLen = rStr.getLength(); 5789 if ( !nLen ) 5790 { 5791 return false; 5792 } 5793 const sal_Unicode* const pBeg = rStr.getStr(); 5794 const sal_Unicode* const pEnd = pBeg + nLen; 5795 const sal_Unicode* p = pBeg; 5796 do 5797 { // Start 5798 if ( *p == '-' ) 5799 { 5800 return true; 5801 } 5802 } 5803 while ( *p == ' ' && ++p < pEnd ); 5804 5805 p = pEnd - 1; 5806 5807 do 5808 { // End 5809 if ( *p == '-' ) 5810 { 5811 return true; 5812 } 5813 } 5814 while ( *p == ' ' && pBeg < --p ); 5815 return false; 5816 } 5817 5818 // static 5819 bool SvNumberformat::IsInQuote( const OUString& rStr, sal_Int32 nPos, 5820 sal_Unicode cQuote, sal_Unicode cEscIn, sal_Unicode cEscOut ) 5821 { 5822 sal_Int32 nLen = rStr.getLength(); 5823 if ( nPos >= nLen ) 5824 { 5825 return false; 5826 } 5827 const sal_Unicode* p0 = rStr.getStr(); 5828 const sal_Unicode* p = p0; 5829 const sal_Unicode* p1 = p0 + nPos; 5830 bool bQuoted = false; 5831 while ( p <= p1 ) 5832 { 5833 if ( *p == cQuote ) 5834 { 5835 if ( p == p0 ) 5836 { 5837 bQuoted = true; 5838 } 5839 else if ( bQuoted ) 5840 { 5841 if ( *(p-1) != cEscIn ) 5842 { 5843 bQuoted = false; 5844 } 5845 } 5846 else 5847 { 5848 if ( *(p-1) != cEscOut ) 5849 { 5850 bQuoted = true; 5851 } 5852 } 5853 } 5854 p++; 5855 } 5856 return bQuoted; 5857 } 5858 5859 // static 5860 sal_Int32 SvNumberformat::GetQuoteEnd( const OUString& rStr, sal_Int32 nPos, 5861 sal_Unicode cQuote, sal_Unicode cEscIn ) 5862 { 5863 if ( nPos < 0 ) 5864 { 5865 return -1; 5866 } 5867 sal_Int32 nLen = rStr.getLength(); 5868 if ( nPos >= nLen ) 5869 { 5870 return -1; 5871 } 5872 if ( !IsInQuote( rStr, nPos, cQuote, cEscIn ) ) 5873 { 5874 if ( rStr[ nPos ] == cQuote ) 5875 { 5876 return nPos; // Closing cQuote 5877 } 5878 return -1; 5879 } 5880 const sal_Unicode* p0 = rStr.getStr(); 5881 const sal_Unicode* p = p0 + nPos; 5882 const sal_Unicode* p1 = p0 + nLen; 5883 while ( p < p1 ) 5884 { 5885 if ( *p == cQuote && p > p0 && *(p-1) != cEscIn ) 5886 { 5887 return sal::static_int_cast< sal_Int32 >(p - p0); 5888 } 5889 p++; 5890 } 5891 return nLen; // End of String 5892 } 5893 5894 sal_uInt16 SvNumberformat::GetNumForNumberElementCount( sal_uInt16 nNumFor ) const 5895 { 5896 if ( nNumFor < 4 ) 5897 { 5898 sal_uInt16 nCnt = NumFor[nNumFor].GetCount(); 5899 return nCnt - ImpGetNumForStringElementCount( nNumFor ); 5900 } 5901 return 0; 5902 } 5903 5904 sal_uInt16 SvNumberformat::ImpGetNumForStringElementCount( sal_uInt16 nNumFor ) const 5905 { 5906 sal_uInt16 nCnt = 0; 5907 sal_uInt16 nNumForCnt = NumFor[nNumFor].GetCount(); 5908 auto& rTypeArray = NumFor[nNumFor].Info().nTypeArray; 5909 for ( sal_uInt16 j=0; j<nNumForCnt; ++j ) 5910 { 5911 switch ( rTypeArray[j] ) 5912 { 5913 case NF_SYMBOLTYPE_STRING: 5914 case NF_SYMBOLTYPE_CURRENCY: 5915 case NF_SYMBOLTYPE_DATESEP: 5916 case NF_SYMBOLTYPE_TIMESEP: 5917 case NF_SYMBOLTYPE_TIME100SECSEP: 5918 case NF_SYMBOLTYPE_PERCENT: 5919 ++nCnt; 5920 break; 5921 } 5922 } 5923 return nCnt; 5924 } 5925 5926 bool SvNumberformat::IsMinuteSecondFormat() const 5927 { 5928 if (GetMaskedType() != SvNumFormatType::TIME) 5929 return false; 5930 5931 constexpr sal_uInt16 k00 = 0x00; // Nada, Nilch 5932 constexpr sal_uInt16 kLB = 0x01; // '[' Left Bracket 5933 constexpr sal_uInt16 kRB = 0x02; // ']' Right Bracket 5934 constexpr sal_uInt16 kMM = 0x04; // M or MM 5935 constexpr sal_uInt16 kTS = 0x08; // Time Separator 5936 constexpr sal_uInt16 kSS = 0x10; // S or SS 5937 #define HAS_MINUTE_SECOND(state) ((state) == (kMM|kTS|kSS) || (state) == (kLB|kMM|kRB|kTS|kSS)) 5938 // Also (kMM|kTS|kLB|kSS|kRB) but those are the same bits. 5939 5940 sal_uInt16 nState = k00; 5941 bool bSep = false; 5942 sal_uInt16 nNumForCnt = NumFor[0].GetCount(); 5943 auto const & rTypeArray = NumFor[0].Info().nTypeArray; 5944 for (sal_uInt16 j=0; j < nNumForCnt; ++j) 5945 { 5946 switch (rTypeArray[j]) 5947 { 5948 case NF_SYMBOLTYPE_DEL: 5949 { 5950 // '[' or ']' before/after MM or SS 5951 const OUString& rStr = NumFor[0].Info().sStrArray[j]; 5952 if (rStr == "[") 5953 { 5954 if (nState != k00 && nState != (kMM|kTS)) 5955 return false; 5956 nState |= kLB; 5957 } 5958 else if (rStr == "]") 5959 { 5960 if (nState != (kLB|kMM) && nState != (kMM|kTS|kLB|kSS)) 5961 return false; 5962 nState |= kRB; 5963 } 5964 else 5965 return false; 5966 } 5967 break; 5968 case NF_KEY_MI: 5969 case NF_KEY_MMI: 5970 if (nState != k00 && nState != kLB) 5971 return false; 5972 nState |= kMM; 5973 break; 5974 case NF_SYMBOLTYPE_TIMESEP: 5975 if (nState != kMM && nState != (kLB|kMM|kRB)) 5976 return false; 5977 nState |= kTS; 5978 break; 5979 case NF_KEY_S: 5980 case NF_KEY_SS: 5981 if (nState != (kMM|kTS) && nState != (kLB|kMM|kRB|kTS) && nState != (kMM|kTS|kLB)) 5982 return false; 5983 nState |= kSS; 5984 break; 5985 case NF_SYMBOLTYPE_TIME100SECSEP: 5986 // Trailing fraction of seconds allowed. 5987 if (!HAS_MINUTE_SECOND(nState)) 5988 return false; 5989 bSep = true; 5990 break; 5991 case NF_SYMBOLTYPE_DIGIT: 5992 if (!bSep) 5993 return false; 5994 break; 5995 case NF_SYMBOLTYPE_STRING: 5996 // nothing, display literal 5997 break; 5998 default: 5999 return false; 6000 } 6001 } 6002 return HAS_MINUTE_SECOND(nState); 6003 #undef HAS_MINUTE_SECOND 6004 } 6005 6006 OUString SvNumberformat::GetFormatStringForTimePrecision( int nPrecision ) const 6007 { 6008 OUStringBuffer sString; 6009 using comphelper::string::padToLength; 6010 6011 sal_uInt16 nNumForCnt = NumFor[0].GetCount(); 6012 auto const & rTypeArray = NumFor[0].Info().nTypeArray; 6013 for (sal_uInt16 j=0; j < nNumForCnt; ++j) 6014 { 6015 switch (rTypeArray[j]) 6016 { 6017 case NF_KEY_S : 6018 case NF_KEY_SS: 6019 sString.append( NumFor[0].Info().sStrArray[j] ); 6020 if ( j > 0 && rTypeArray[j-1] == NF_SYMBOLTYPE_DEL && j < nNumForCnt-1 ) 6021 { 6022 j++; 6023 sString.append( NumFor[0].Info().sStrArray[j] ); 6024 } 6025 if (nPrecision > 0) 6026 { 6027 sString.append( rLoc().getTime100SecSep() ); 6028 padToLength(sString, sString.getLength() + nPrecision, '0'); 6029 } 6030 break; 6031 case NF_SYMBOLTYPE_TIME100SECSEP: 6032 case NF_SYMBOLTYPE_DIGIT: 6033 break; 6034 case NF_SYMBOLTYPE_STRING: 6035 sString.append( "\"" ); 6036 [[fallthrough]]; 6037 default: 6038 sString.append( NumFor[0].Info().sStrArray[j] ); 6039 if (rTypeArray[j] == NF_SYMBOLTYPE_STRING) 6040 { 6041 sString.append( "\"" ); 6042 } 6043 } 6044 } 6045 6046 return sString.makeStringAndClear(); 6047 } 6048 6049 sal_uInt16 SvNumberformat::GetThousandDivisorPrecision( sal_uInt16 nIx ) const 6050 { 6051 if (nIx >= 4) 6052 return 0; 6053 6054 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info(); 6055 6056 if (rInfo.eScannedType != SvNumFormatType::NUMBER && rInfo.eScannedType != SvNumFormatType::CURRENCY) 6057 return 0; 6058 6059 if (rInfo.nThousand == FLAG_STANDARD_IN_FORMAT) 6060 return SvNumberFormatter::UNLIMITED_PRECISION; 6061 6062 return rInfo.nThousand * 3; 6063 } 6064 6065 const CharClass& SvNumberformat::rChrCls() const 6066 { 6067 return rScan.GetChrCls(); 6068 } 6069 6070 const LocaleDataWrapper& SvNumberformat::rLoc() const 6071 { 6072 return rScan.GetLoc(); 6073 } 6074 6075 CalendarWrapper& SvNumberformat::GetCal() const 6076 { 6077 return rScan.GetCal(); 6078 } 6079 6080 const SvNumberFormatter& SvNumberformat::GetFormatter() const 6081 { 6082 return *rScan.GetNumberformatter(); 6083 } 6084 6085 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 6086
