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