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