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 <i18nlangtag/languagetag.hxx> 21 #include <i18nlangtag/mslangid.hxx> 22 #include <rtl/ustrbuf.hxx> 23 #include <sal/macros.h> 24 #include <nativenumbersupplier.hxx> 25 #include <localedata.hxx> 26 #include "data/numberchar.h" 27 #include <comphelper/processfactory.hxx> 28 #include <cppuhelper/supportsservice.hxx> 29 #include <map> 30 #include <memory> 31 #include <string_view> 32 #include <unordered_map> 33 #include <com/sun/star/i18n/CharacterClassification.hpp> 34 #include <com/sun/star/i18n/NativeNumberMode.hpp> 35 #include <com/sun/star/linguistic2/NumberText.hpp> 36 37 using namespace ::com::sun::star::uno; 38 using namespace ::com::sun::star::i18n; 39 using namespace ::com::sun::star::lang; 40 41 namespace { 42 43 struct Number { 44 sal_Int16 number; 45 const sal_Unicode *multiplierChar; 46 sal_Int16 numberFlag; 47 sal_Int16 exponentCount; 48 const sal_Int16 *multiplierExponent; 49 }; 50 51 } 52 53 #define NUMBER_OMIT_ZERO (1 << 0) 54 #define NUMBER_OMIT_ONLY_ZERO (1 << 1) 55 #define NUMBER_OMIT_ONE_1 (1 << 2) 56 #define NUMBER_OMIT_ONE_2 (1 << 3) 57 #define NUMBER_OMIT_ONE_3 (1 << 4) 58 #define NUMBER_OMIT_ONE_4 (1 << 5) 59 #define NUMBER_OMIT_ONE_5 (1 << 6) 60 #define NUMBER_OMIT_ONE_6 (1 << 7) 61 #define NUMBER_OMIT_ONE_7 (1 << 8) 62 #define NUMBER_OMIT_ONE (NUMBER_OMIT_ONE_1|NUMBER_OMIT_ONE_2|NUMBER_OMIT_ONE_3|NUMBER_OMIT_ONE_4|NUMBER_OMIT_ONE_5|NUMBER_OMIT_ONE_6|NUMBER_OMIT_ONE_7) 63 #define NUMBER_OMIT_ONE_CHECK(bit) (1 << (2 + bit)) 64 #define NUMBER_OMIT_ALL ( NUMBER_OMIT_ZERO|NUMBER_OMIT_ONE|NUMBER_OMIT_ONLY_ZERO ) 65 #define NUMBER_OMIT_ZERO_ONE ( NUMBER_OMIT_ZERO|NUMBER_OMIT_ONE ) 66 #define NUMBER_OMIT_ONE_67 (NUMBER_OMIT_ONE_6|NUMBER_OMIT_ONE_7) 67 #define NUMBER_OMIT_ZERO_ONE_67 ( NUMBER_OMIT_ZERO|NUMBER_OMIT_ONE_67 ) 68 69 namespace i18npool { 70 71 namespace { 72 73 struct theNatNumMutex : public rtl::Static<osl::Mutex, theNatNumMutex> {}; 74 75 } 76 77 static OUString getHebrewNativeNumberString(const OUString& aNumberString, bool useGeresh); 78 79 static OUString getCyrillicNativeNumberString(const OUString& aNumberString); 80 81 /// @throws RuntimeException 82 static OUString AsciiToNativeChar( const OUString& inStr, sal_Int32 nCount, 83 Sequence< sal_Int32 >& offset, bool useOffset, sal_Int16 number ) 84 { 85 const sal_Unicode *src = inStr.getStr(); 86 rtl_uString *newStr = rtl_uString_alloc(nCount); 87 if (useOffset) 88 offset.realloc(nCount); 89 90 for (sal_Int32 i = 0; i < nCount; i++) 91 { 92 sal_Unicode ch = src[i]; 93 if (isNumber(ch)) 94 newStr->buffer[i] = NumberChar[number][ ch - NUMBER_ZERO ]; 95 else if (i+1 < nCount && isNumber(src[i+1])) { 96 if (i > 0 && isNumber(src[i-1]) && isSeparator(ch)) 97 newStr->buffer[i] = SeparatorChar[number] ? SeparatorChar[number] : ch; 98 else 99 newStr->buffer[i] = isDecimal(ch) ? (DecimalChar[number] ? DecimalChar[number] : ch) : 100 isMinus(ch) ? (MinusChar[number] ? MinusChar[number] : ch) : ch; 101 } 102 else 103 newStr->buffer[i] = ch; 104 if (useOffset) 105 offset[i] = i; 106 } 107 return OUString(newStr, SAL_NO_ACQUIRE); // take ownership 108 } 109 110 static bool AsciiToNative_numberMaker(const sal_Unicode *str, sal_Int32 begin, sal_Int32 len, 111 sal_Unicode *dst, sal_Int32& count, sal_Int16 multiChar_index, Sequence< sal_Int32 >& offset, bool useOffset, sal_Int32 startPos, 112 const Number *number, const sal_Unicode* numberChar) 113 { 114 sal_Unicode multiChar = (multiChar_index == -1 ? 0 : number->multiplierChar[multiChar_index]); 115 if ( len <= number->multiplierExponent[number->exponentCount-1] ) { 116 if (number->multiplierExponent[number->exponentCount-1] > 1) { 117 bool bNotZero = false; 118 for (const sal_Int32 end = begin+len; begin < end; begin++) { 119 if (bNotZero || str[begin] != NUMBER_ZERO) { 120 dst[count] = numberChar[str[begin] - NUMBER_ZERO]; 121 if (useOffset) 122 offset[count] = begin + startPos; 123 count++; 124 bNotZero = true; 125 } 126 } 127 if (bNotZero && multiChar > 0) { 128 dst[count] = multiChar; 129 if (useOffset) 130 offset[count] = begin + startPos; 131 count++; 132 } 133 return bNotZero; 134 } else if (str[begin] != NUMBER_ZERO) { 135 if (!(number->numberFlag & (multiChar_index < 0 ? 0 : NUMBER_OMIT_ONE_CHECK(multiChar_index))) || str[begin] != NUMBER_ONE) { 136 dst[count] = numberChar[str[begin] - NUMBER_ZERO]; 137 if (useOffset) 138 offset[count] = begin + startPos; 139 count++; 140 } 141 if (multiChar > 0) { 142 dst[count] = multiChar; 143 if (useOffset) 144 offset[count] = begin + startPos; 145 count++; 146 } 147 } else if (!(number->numberFlag & NUMBER_OMIT_ZERO) && count > 0 && dst[count-1] != numberChar[0]) { 148 dst[count] = numberChar[0]; 149 if (useOffset) 150 offset[count] = begin + startPos; 151 count++; 152 } 153 return str[begin] != NUMBER_ZERO; 154 } else { 155 bool bPrintPower = false; 156 // sal_Int16 last = 0; 157 for (sal_Int16 i = 1; i <= number->exponentCount; i++) { 158 sal_Int32 tmp = len - (i == number->exponentCount ? 0 : number->multiplierExponent[i]); 159 if (tmp > 0) { 160 bPrintPower |= AsciiToNative_numberMaker(str, begin, tmp, dst, count, 161 (i == number->exponentCount ? -1 : i), offset, useOffset, startPos, number, numberChar); 162 begin += tmp; 163 len -= tmp; 164 } 165 } 166 if (bPrintPower) { 167 if (count > 0 && number->multiplierExponent[number->exponentCount-1] == 1 && 168 dst[count-1] == numberChar[0]) 169 count--; 170 if (multiChar > 0) { 171 dst[count] = multiChar; 172 if (useOffset) 173 offset[count] = begin + startPos; 174 count++; 175 } 176 } 177 return bPrintPower; 178 } 179 } 180 181 /// @throws RuntimeException 182 static OUString AsciiToNative( const OUString& inStr, sal_Int32 nCount, 183 Sequence< sal_Int32 >& offset, bool useOffset, const Number* number ) 184 { 185 OUString aRet; 186 187 sal_Int32 strLen = inStr.getLength(); 188 const sal_Unicode *numberChar = NumberChar[number->number]; 189 190 if (nCount > strLen) 191 nCount = strLen; 192 193 if (nCount > 0) 194 { 195 const sal_Unicode *str = inStr.getStr(); 196 std::unique_ptr<sal_Unicode[]> newStr(new sal_Unicode[nCount * 2 + 1]); 197 std::unique_ptr<sal_Unicode[]> srcStr(new sal_Unicode[nCount + 1]); // for keeping number without comma 198 sal_Int32 i, len = 0, count = 0; 199 200 if (useOffset) 201 offset.realloc( nCount * 2 ); 202 bool bDoDecimal = false; 203 204 for (i = 0; i <= nCount; i++) 205 { 206 if (i < nCount && isNumber(str[i])) { 207 if (bDoDecimal) { 208 newStr[count] = numberChar[str[i] - NUMBER_ZERO]; 209 if (useOffset) 210 offset[count] = i; 211 count++; 212 } 213 else 214 srcStr[len++] = str[i]; 215 } else { 216 if (len > 0) { 217 if (i < nCount-1 && isSeparator(str[i]) && isNumber(str[i+1])) 218 continue; // skip comma inside number string 219 bool bNotZero = false; 220 for (sal_Int32 begin = 0, end = len % number->multiplierExponent[0]; 221 end <= len; begin = end, end += number->multiplierExponent[0]) { 222 if (end == 0) continue; 223 sal_Int32 _count = count; 224 bNotZero |= AsciiToNative_numberMaker(srcStr.get(), begin, end - begin, newStr.get(), count, 225 end == len ? -1 : 0, offset, useOffset, i - len, number, numberChar); 226 if (count > 0 && number->multiplierExponent[number->exponentCount-1] == 1 && 227 newStr[count-1] == numberChar[0]) 228 count--; 229 if (bNotZero && _count == count && end != len) { 230 newStr[count] = number->multiplierChar[0]; 231 if (useOffset) 232 offset[count] = i - len; 233 count++; 234 } 235 } 236 if (! bNotZero && ! (number->numberFlag & NUMBER_OMIT_ONLY_ZERO)) { 237 newStr[count] = numberChar[0]; 238 if (useOffset) 239 offset[count] = i - len; 240 count++; 241 } 242 len = 0; 243 } 244 if (i < nCount) { 245 bDoDecimal = (!bDoDecimal && i < nCount-1 && isDecimal(str[i]) && isNumber(str[i+1])); 246 if (bDoDecimal) 247 newStr[count] = (DecimalChar[number->number] ? DecimalChar[number->number] : str[i]); 248 else if (i < nCount-1 && isMinus(str[i]) && isNumber(str[i+1])) 249 newStr[count] = (MinusChar[number->number] ? MinusChar[number->number] : str[i]); 250 else if (i < nCount-1 && isSeparator(str[i]) && isNumber(str[i+1])) 251 newStr[count] = (SeparatorChar[number->number] ? SeparatorChar[number->number] : str[i]); 252 else 253 newStr[count] = str[i]; 254 if (useOffset) 255 offset[count] = i; 256 count++; 257 } 258 } 259 } 260 261 if (useOffset) 262 offset.realloc(count); 263 aRet = OUString(newStr.get(), count); 264 } 265 return aRet; 266 } 267 268 namespace 269 { 270 void NativeToAscii_numberMaker(sal_Int16 max, sal_Int16 prev, const sal_Unicode *str, 271 sal_Int32& i, sal_Int32 nCount, sal_Unicode *dst, sal_Int32& count, Sequence< sal_Int32 >& offset, bool useOffset, 272 OUString& numberChar, OUString& multiplierChar) 273 { 274 sal_Int16 curr = 0, num = 0, end = 0, shift = 0; 275 while (++i < nCount) { 276 if ((curr = sal::static_int_cast<sal_Int16>( numberChar.indexOf(str[i]) )) >= 0) { 277 if (num > 0) 278 break; 279 num = curr % 10; 280 } else if ((curr = sal::static_int_cast<sal_Int16>( multiplierChar.indexOf(str[i]) )) >= 0) { 281 curr = MultiplierExponent_7_CJK[curr % ExponentCount_7_CJK]; 282 if (prev > curr && num == 0) num = 1; // One may be omitted in informal format 283 shift = end = 0; 284 if (curr >= max) 285 max = curr; 286 else if (curr > prev) 287 shift = max - curr; 288 else 289 end = curr; 290 while (end++ < prev) { 291 dst[count] = NUMBER_ZERO + (end == prev ? num : 0); 292 if (useOffset) 293 offset[count] = i; 294 count++; 295 } 296 if (shift) { 297 count -= max; 298 for (const sal_Int32 countEnd = count+shift; count < countEnd; count++) { 299 dst[count] = dst[count + curr]; 300 if (useOffset) 301 offset[count] = offset[count + curr]; 302 } 303 max = curr; 304 } 305 NativeToAscii_numberMaker(max, curr, str, i, nCount, dst, 306 count, offset, useOffset, numberChar, multiplierChar); 307 return; 308 } else 309 break; 310 } 311 while (end++ < prev) { 312 dst[count] = NUMBER_ZERO + (end == prev ? num : 0); 313 if (useOffset) 314 offset[count] = i - 1; 315 count++; 316 } 317 } 318 319 /// @throws RuntimeException 320 OUString NativeToAscii(const OUString& inStr, 321 sal_Int32 nCount, Sequence< sal_Int32 >& offset, bool useOffset ) 322 { 323 OUString aRet; 324 325 sal_Int32 strLen = inStr.getLength(); 326 327 if (nCount > strLen) 328 nCount = strLen; 329 330 if (nCount > 0) { 331 const sal_Unicode *str = inStr.getStr(); 332 std::unique_ptr<sal_Unicode[]> newStr(new sal_Unicode[nCount * MultiplierExponent_7_CJK[0] + 2]); 333 if (useOffset) 334 offset.realloc( nCount * MultiplierExponent_7_CJK[0] + 1 ); 335 sal_Int32 count = 0, index; 336 sal_Int32 i; 337 338 OUString numberChar, multiplierChar, decimalChar, minusChar, separatorChar; 339 numberChar = OUString(NumberChar[0], 10*NumberChar_Count); 340 multiplierChar = OUString(MultiplierChar_7_CJK[0], ExponentCount_7_CJK*Multiplier_Count); 341 decimalChar = OUString(DecimalChar, NumberChar_Count); 342 minusChar = OUString(MinusChar, NumberChar_Count); 343 separatorChar = OUString( 344 reinterpret_cast<sal_Unicode *>(SeparatorChar), NumberChar_Count); 345 346 for ( i = 0; i < nCount; i++) { 347 if ((index = multiplierChar.indexOf(str[i])) >= 0) { 348 if (count == 0 || !isNumber(newStr[count-1])) { // add 1 in front of multiplier 349 newStr[count] = NUMBER_ONE; 350 if (useOffset) 351 offset[count] = i; 352 count++; 353 } 354 index = MultiplierExponent_7_CJK[index % ExponentCount_7_CJK]; 355 NativeToAscii_numberMaker( 356 sal::static_int_cast<sal_Int16>( index ), sal::static_int_cast<sal_Int16>( index ), 357 str, i, nCount, newStr.get(), count, offset, useOffset, 358 numberChar, multiplierChar); 359 } else { 360 if ((index = numberChar.indexOf(str[i])) >= 0) 361 newStr[count] = sal::static_int_cast<sal_Unicode>( (index % 10) + NUMBER_ZERO ); 362 else if ((index = separatorChar.indexOf(str[i])) >= 0 && 363 (i < nCount-1 && (numberChar.indexOf(str[i+1]) >= 0 || 364 multiplierChar.indexOf(str[i+1]) >= 0))) 365 newStr[count] = SeparatorChar[NumberChar_HalfWidth]; 366 else if ((index = decimalChar.indexOf(str[i])) >= 0 && 367 (i < nCount-1 && (numberChar.indexOf(str[i+1]) >= 0 || 368 multiplierChar.indexOf(str[i+1]) >= 0))) 369 // Only when decimal point is followed by numbers, 370 // it will be convert to ASCII decimal point 371 newStr[count] = DecimalChar[NumberChar_HalfWidth]; 372 else if ((index = minusChar.indexOf(str[i])) >= 0 && 373 (i < nCount-1 && (numberChar.indexOf(str[i+1]) >= 0 || 374 multiplierChar.indexOf(str[i+1]) >= 0))) 375 // Only when minus is followed by numbers, 376 // it will be convert to ASCII minus sign 377 newStr[count] = MinusChar[NumberChar_HalfWidth]; 378 else 379 newStr[count] = str[i]; 380 if (useOffset) 381 offset[count] = i; 382 count++; 383 } 384 } 385 386 if (useOffset) { 387 offset.realloc(count); 388 } 389 aRet = OUString(newStr.get(), count); 390 } 391 return aRet; 392 } 393 394 const Number natnum4[4] = { 395 { NumberChar_Lower_zh, MultiplierChar_6_CJK[Multiplier_Lower_zh], 0, 396 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 397 { NumberChar_Lower_zh, MultiplierChar_6_CJK[Multiplier_Lower_zh_TW], 0, 398 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 399 { NumberChar_Modern_ja, MultiplierChar_7_CJK[Multiplier_Modern_ja], NUMBER_OMIT_ZERO_ONE_67, 400 ExponentCount_7_CJK, MultiplierExponent_7_CJK }, 401 { NumberChar_Lower_ko, MultiplierChar_6_CJK[Multiplier_Lower_ko], NUMBER_OMIT_ZERO, 402 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 403 }; 404 405 const Number natnum5[4] = { 406 { NumberChar_Upper_zh, MultiplierChar_6_CJK[Multiplier_Upper_zh], 0, 407 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 408 { NumberChar_Upper_zh_TW, MultiplierChar_6_CJK[Multiplier_Upper_zh_TW], 0, 409 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 410 { NumberChar_Traditional_ja, MultiplierChar_7_CJK[Multiplier_Traditional_ja], NUMBER_OMIT_ZERO_ONE_67, 411 ExponentCount_7_CJK, MultiplierExponent_7_CJK }, 412 { NumberChar_Upper_ko, MultiplierChar_6_CJK[Multiplier_Upper_zh_TW], NUMBER_OMIT_ZERO, 413 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 414 }; 415 416 const Number natnum6[4] = { 417 { NumberChar_FullWidth, MultiplierChar_6_CJK[Multiplier_Lower_zh], 0, 418 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 419 { NumberChar_FullWidth, MultiplierChar_6_CJK[Multiplier_Lower_zh_TW], 0, 420 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 421 { NumberChar_FullWidth, MultiplierChar_7_CJK[Multiplier_Modern_ja], NUMBER_OMIT_ZERO_ONE_67, 422 ExponentCount_7_CJK, MultiplierExponent_7_CJK }, 423 { NumberChar_FullWidth, MultiplierChar_6_CJK[Multiplier_Hangul_ko], NUMBER_OMIT_ZERO, 424 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 425 }; 426 427 const Number natnum7[4] = { 428 { NumberChar_Lower_zh, MultiplierChar_6_CJK[Multiplier_Lower_zh], NUMBER_OMIT_ALL, 429 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 430 { NumberChar_Lower_zh, MultiplierChar_6_CJK[Multiplier_Lower_zh_TW], NUMBER_OMIT_ALL, 431 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 432 { NumberChar_Modern_ja, MultiplierChar_2_CJK[Multiplier_Modern_ja], NUMBER_OMIT_ZERO_ONE, 433 ExponentCount_2_CJK, MultiplierExponent_2_CJK }, 434 { NumberChar_Lower_ko, MultiplierChar_6_CJK[Multiplier_Lower_ko], NUMBER_OMIT_ALL, 435 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 436 }; 437 438 const Number natnum8[4] = { 439 { NumberChar_Upper_zh, MultiplierChar_6_CJK[Multiplier_Upper_zh], NUMBER_OMIT_ALL, 440 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 441 { NumberChar_Upper_zh_TW, MultiplierChar_6_CJK[Multiplier_Upper_zh_TW], NUMBER_OMIT_ALL, 442 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 443 { NumberChar_Traditional_ja, MultiplierChar_2_CJK[Multiplier_Traditional_ja], NUMBER_OMIT_ZERO_ONE, 444 ExponentCount_2_CJK, MultiplierExponent_2_CJK }, 445 { NumberChar_Upper_ko, MultiplierChar_6_CJK[Multiplier_Upper_zh_TW], NUMBER_OMIT_ALL, 446 ExponentCount_6_CJK, MultiplierExponent_6_CJK }, 447 }; 448 449 const Number natnum10 = { NumberChar_Hangul_ko, MultiplierChar_6_CJK[Multiplier_Hangul_ko], NUMBER_OMIT_ZERO, 450 ExponentCount_6_CJK, MultiplierExponent_6_CJK }; 451 const Number natnum11 = { NumberChar_Hangul_ko, MultiplierChar_6_CJK[Multiplier_Hangul_ko], NUMBER_OMIT_ALL, 452 ExponentCount_6_CJK, MultiplierExponent_6_CJK }; 453 454 //! ATTENTION: Do not change order of elements! 455 //! Append new languages to the end of the list! 456 const char *natnum1Locales[] = { 457 "zh_CN", 458 "zh_TW", 459 "ja", 460 "ko", 461 "he", 462 "ar", 463 "th", 464 "hi", 465 "or", 466 "mr", 467 "bn", 468 "pa", 469 "gu", 470 "ta", 471 "te", 472 "kn", 473 "ml", 474 "lo", 475 "bo", 476 "my", 477 "km", 478 "mn", 479 "ne", 480 "dz", 481 "fa", 482 "cu" 483 }; 484 const sal_Int16 nbOfLocale = SAL_N_ELEMENTS(natnum1Locales); 485 486 //! ATTENTION: Do not change order of elements! 487 //! Number and order must match elements of natnum1Locales! 488 const sal_Int16 natnum1[] = { 489 NumberChar_Lower_zh, 490 NumberChar_Lower_zh, 491 NumberChar_Modern_ja, 492 NumberChar_Lower_ko, 493 NumberChar_he, 494 NumberChar_Indic_ar, 495 NumberChar_th, 496 NumberChar_hi, 497 NumberChar_or, 498 NumberChar_mr, 499 NumberChar_bn, 500 NumberChar_pa, 501 NumberChar_gu, 502 NumberChar_ta, 503 NumberChar_te, 504 NumberChar_kn, 505 NumberChar_ml, 506 NumberChar_lo, 507 NumberChar_bo, 508 NumberChar_my, 509 NumberChar_km, 510 NumberChar_mn, 511 NumberChar_ne, 512 NumberChar_dz, 513 NumberChar_EastIndic_ar, 514 NumberChar_cu 515 }; 516 const sal_Int16 sizeof_natnum1 = SAL_N_ELEMENTS(natnum1); 517 518 //! ATTENTION: Do not change order of elements! 519 //! Order must match first elements of natnum1Locales! 520 const sal_Int16 natnum2[] = { 521 NumberChar_Upper_zh, 522 NumberChar_Upper_zh_TW, 523 NumberChar_Traditional_ja, 524 NumberChar_Upper_ko, 525 NumberChar_he 526 }; 527 const sal_Int16 sizeof_natnum2 = SAL_N_ELEMENTS(natnum2); 528 529 sal_Int16 getLanguageNumber( const Locale& rLocale) 530 { 531 // return zh_TW for TW, HK and MO, return zh_CN for other zh locales. 532 if (rLocale.Language == "zh") return MsLangId::isTraditionalChinese(rLocale) ? 1 : 0; 533 534 for (sal_Int16 i = 2; i < nbOfLocale; i++) 535 if (rLocale.Language.equalsAsciiL(natnum1Locales[i], 2)) 536 return i; 537 538 return -1; 539 } 540 541 struct Separators 542 { 543 sal_Unicode DecimalSeparator; 544 sal_Unicode ThousandSeparator; 545 Separators(const Locale& rLocale) 546 { 547 LocaleDataItem aLocaleItem = LocaleDataImpl::get()->getLocaleItem(rLocale); 548 DecimalSeparator = aLocaleItem.decimalSeparator.toChar(); 549 ThousandSeparator = aLocaleItem.thousandSeparator.toChar(); 550 } 551 }; 552 553 Separators getLocaleSeparators(const Locale& rLocale, const OUString& rLocStr) 554 { 555 // Guard the static variable below. 556 osl::MutexGuard aGuard(theNatNumMutex::get()); 557 // Maximum a couple hundred of pairs with 4-byte structs - so no need for smart managing 558 static std::unordered_map<OUString, Separators> aLocaleSeparatorsBuf; 559 auto it = aLocaleSeparatorsBuf.find(rLocStr); 560 if (it == aLocaleSeparatorsBuf.end()) 561 { 562 it = aLocaleSeparatorsBuf.emplace(rLocStr, Separators(rLocale)).first; 563 } 564 return it->second; 565 } 566 567 OUString getNumberText(const Locale& rLocale, const OUString& rNumberString, 568 const OUString& sNumberTextParams) 569 { 570 sal_Int32 i, count = 0; 571 const sal_Int32 len = rNumberString.getLength(); 572 const sal_Unicode* src = rNumberString.getStr(); 573 574 OUString aLoc = LanguageTag::convertToBcp47(rLocale); 575 Separators aSeparators = getLocaleSeparators(rLocale, aLoc); 576 577 OUStringBuffer sBuf(len); 578 for (i = 0; i < len; i++) 579 { 580 sal_Unicode ch = src[i]; 581 if (isNumber(ch)) 582 { 583 ++count; 584 sBuf.append(ch); 585 } 586 else if (ch == aSeparators.DecimalSeparator) 587 // Convert any decimal separator to point - in case libnumbertext has a different one 588 // for this locale (it seems that point is supported for all locales in libnumbertext) 589 sBuf.append('.'); 590 else if (ch == aSeparators.ThousandSeparator && count > 0) 591 continue; 592 else if (isMinus(ch) && count == 0) 593 sBuf.append(ch); 594 else 595 break; 596 } 597 598 // Handle also month and day names for NatNum12 date formatting 599 const OUString& rNumberStr = (count == 0) ? rNumberString : sBuf.makeStringAndClear(); 600 601 // Guard the static variables below. 602 osl::MutexGuard aGuard( theNatNumMutex::get()); 603 604 static auto xNumberText 605 = css::linguistic2::NumberText::create(comphelper::getProcessComponentContext()); 606 OUString numbertext_prefix; 607 // default "cardinal" gets empty prefix 608 if (!sNumberTextParams.isEmpty() && sNumberTextParams != "cardinal") 609 numbertext_prefix = sNumberTextParams + " "; 610 // Several hundreds of headings could result typing lags because 611 // of the continuous update of the multiple number names during typing. 612 // We fix this by buffering the result of the conversion. 613 static std::unordered_map<OUString, std::map<OUString, OUString>> aBuff; 614 auto& rItems = aBuff[rNumberStr]; 615 auto& rItem = rItems[numbertext_prefix + aLoc]; 616 if (rItem.isEmpty()) 617 { 618 rItem = xNumberText->getNumberText(numbertext_prefix + rNumberStr, rLocale); 619 // use number at missing number to text conversion 620 if (rItem.isEmpty()) 621 rItem = rNumberStr; 622 } 623 OUString sResult = rItem; 624 if (i != 0 && i < len) 625 sResult += rNumberString.copy(i); 626 return sResult; 627 } 628 } 629 630 OUString NativeNumberSupplierService::getNativeNumberString(const OUString& aNumberString, const Locale& rLocale, 631 sal_Int16 nNativeNumberMode, 632 Sequence<sal_Int32>& offset, 633 const OUString& rNativeNumberParams) 634 { 635 if (!isValidNatNum(rLocale, nNativeNumberMode)) 636 return aNumberString; 637 638 if (nNativeNumberMode == NativeNumberMode::NATNUM12) 639 { 640 // handle capitalization prefixes "capitalize", "upper" and "title" 641 642 enum WhichCasing 643 { 644 CAPITALIZE, 645 UPPER, 646 TITLE 647 }; 648 649 struct CasingEntry 650 { 651 OUStringLiteral aLiteral; 652 WhichCasing eCasing; 653 }; 654 655 static const CasingEntry Casings[] = 656 { 657 { OUStringLiteral("capitalize"), CAPITALIZE }, 658 { OUStringLiteral("upper"), UPPER }, 659 { OUStringLiteral("title"), TITLE } 660 }; 661 662 sal_Int32 nStripCase = 0; 663 size_t nCasing; 664 for (nCasing = 0; nCasing < SAL_N_ELEMENTS(Casings); ++nCasing) 665 { 666 if (rNativeNumberParams.startsWith( Casings[nCasing].aLiteral)) 667 { 668 nStripCase = Casings[nCasing].aLiteral.size; 669 break; 670 } 671 } 672 673 if (nStripCase > 0 && (rNativeNumberParams.getLength() == nStripCase || 674 rNativeNumberParams[nStripCase++] == ' ')) 675 { 676 OUString aStr = getNumberText(rLocale, aNumberString, rNativeNumberParams.copy(nStripCase)); 677 678 if (!xCharClass.is()) 679 xCharClass = CharacterClassification::create(comphelper::getProcessComponentContext()); 680 681 switch (Casings[nCasing].eCasing) 682 { 683 case CAPITALIZE: 684 return xCharClass->toTitle(aStr, 0, 1, aLocale) + 685 (aStr.getLength() > 1 ? aStr.copy(1) : OUString()); 686 case UPPER: 687 return xCharClass->toUpper(aStr, 0, aStr.getLength(), aLocale); 688 case TITLE: 689 return xCharClass->toTitle(aStr, 0, aStr.getLength(), aLocale); 690 } 691 } 692 else 693 { 694 return getNumberText(rLocale, aNumberString, rNativeNumberParams); 695 } 696 } 697 698 sal_Int16 langnum = getLanguageNumber(rLocale); 699 if (langnum == -1) 700 return aNumberString; 701 702 const Number *number = nullptr; 703 sal_Int16 num = -1; 704 705 switch (nNativeNumberMode) 706 { 707 case NativeNumberMode::NATNUM0: // Ascii 708 return NativeToAscii(aNumberString, aNumberString.getLength(), offset, useOffset); 709 case NativeNumberMode::NATNUM1: // Char, Lower 710 num = natnum1[langnum]; 711 break; 712 case NativeNumberMode::NATNUM2: // Char, Upper 713 num = natnum2[langnum]; 714 break; 715 case NativeNumberMode::NATNUM3: // Char, FullWidth 716 num = NumberChar_FullWidth; 717 break; 718 case NativeNumberMode::NATNUM4: // Text, Lower, Long 719 number = &natnum4[langnum]; 720 break; 721 case NativeNumberMode::NATNUM5: // Text, Upper, Long 722 number = &natnum5[langnum]; 723 break; 724 case NativeNumberMode::NATNUM6: // Text, FullWidth 725 number = &natnum6[langnum]; 726 break; 727 case NativeNumberMode::NATNUM7: // Text. Lower, Short 728 number = &natnum7[langnum]; 729 break; 730 case NativeNumberMode::NATNUM8: // Text, Upper, Short 731 number = &natnum8[langnum]; 732 break; 733 case NativeNumberMode::NATNUM9: // Char, Hangul 734 num = NumberChar_Hangul_ko; 735 break; 736 case NativeNumberMode::NATNUM10: // Text, Hangul, Long 737 number = &natnum10; 738 break; 739 case NativeNumberMode::NATNUM11: // Text, Hangul, Short 740 number = &natnum11; 741 break; 742 default: 743 break; 744 } 745 746 if (number || num >= 0) { 747 if (aLocale.Language != rLocale.Language || 748 aLocale.Country != rLocale.Country || 749 aLocale.Variant != rLocale.Variant) { 750 LocaleDataItem item = LocaleDataImpl::get()->getLocaleItem( rLocale ); 751 aLocale = rLocale; 752 DecimalChar[NumberChar_HalfWidth]=item.decimalSeparator.toChar(); 753 if (DecimalChar[NumberChar_HalfWidth] > 0x7E || DecimalChar[NumberChar_HalfWidth] < 0x21) 754 DecimalChar[NumberChar_FullWidth]=0xFF0E; 755 else 756 DecimalChar[NumberChar_FullWidth]=DecimalChar[NumberChar_HalfWidth]+0xFEE0; 757 SeparatorChar[NumberChar_HalfWidth]=item.thousandSeparator.toChar(); 758 if (SeparatorChar[NumberChar_HalfWidth] > 0x7E || SeparatorChar[NumberChar_HalfWidth] < 0x21) 759 SeparatorChar[NumberChar_FullWidth]=0xFF0C; 760 else 761 SeparatorChar[NumberChar_FullWidth]=SeparatorChar[NumberChar_HalfWidth]+0xFEE0; 762 } 763 if (number) 764 return AsciiToNative( aNumberString, aNumberString.getLength(), offset, useOffset, number ); 765 else if (num == NumberChar_he) 766 return getHebrewNativeNumberString(aNumberString, 767 nNativeNumberMode == NativeNumberMode::NATNUM2); 768 else if (num == NumberChar_cu) 769 return getCyrillicNativeNumberString(aNumberString); 770 else 771 return AsciiToNativeChar(aNumberString, aNumberString.getLength(), offset, useOffset, num); 772 } 773 else 774 return aNumberString; 775 } 776 777 OUString SAL_CALL NativeNumberSupplierService::getNativeNumberString(const OUString& aNumberString, const Locale& rLocale, 778 sal_Int16 nNativeNumberMode) 779 { 780 Sequence< sal_Int32 > offset; 781 return getNativeNumberString(aNumberString, rLocale, nNativeNumberMode, offset); 782 } 783 784 OUString SAL_CALL NativeNumberSupplierService::getNativeNumberStringParams( 785 const OUString& rNumberString, const css::lang::Locale& rLocale, sal_Int16 nNativeNumberMode, 786 const OUString& rNativeNumberParams) 787 { 788 Sequence<sal_Int32> offset; 789 return getNativeNumberString(rNumberString, rLocale, nNativeNumberMode, offset, rNativeNumberParams); 790 } 791 792 sal_Unicode NativeNumberSupplierService::getNativeNumberChar( const sal_Unicode inChar, const Locale& rLocale, sal_Int16 nNativeNumberMode ) 793 { 794 if (nNativeNumberMode == NativeNumberMode::NATNUM0) { // Ascii 795 for (const auto & i : NumberChar) 796 for (sal_Int16 j = 0; j < 10; j++) 797 if (inChar == i[j]) 798 return j; 799 return inChar; 800 } 801 802 if (!isNumber(inChar)) 803 return inChar; 804 805 if (!isValidNatNum(rLocale, nNativeNumberMode)) 806 return inChar; 807 808 sal_Int16 langnum = getLanguageNumber(rLocale); 809 if (langnum == -1) 810 return inChar; 811 812 switch (nNativeNumberMode) 813 { 814 case NativeNumberMode::NATNUM1: // Char, Lower 815 case NativeNumberMode::NATNUM4: // Text, Lower, Long 816 case NativeNumberMode::NATNUM7: // Text. Lower, Short 817 return NumberChar[natnum1[langnum]][inChar - NUMBER_ZERO]; 818 case NativeNumberMode::NATNUM2: // Char, Upper 819 case NativeNumberMode::NATNUM5: // Text, Upper, Long 820 case NativeNumberMode::NATNUM8: // Text, Upper, Short 821 return NumberChar[natnum2[langnum]][inChar - NUMBER_ZERO]; 822 case NativeNumberMode::NATNUM3: // Char, FullWidth 823 case NativeNumberMode::NATNUM6: // Text, FullWidth 824 return NumberChar[NumberChar_FullWidth][inChar - NUMBER_ZERO]; 825 case NativeNumberMode::NATNUM9: // Char, Hangul 826 case NativeNumberMode::NATNUM10: // Text, Hangul, Long 827 case NativeNumberMode::NATNUM11: // Text, Hangul, Short 828 return NumberChar[NumberChar_Hangul_ko][inChar - NUMBER_ZERO]; 829 default: 830 break; 831 } 832 833 return inChar; 834 } 835 836 sal_Bool SAL_CALL NativeNumberSupplierService::isValidNatNum( const Locale& rLocale, sal_Int16 nNativeNumberMode ) 837 { 838 sal_Int16 langnum = getLanguageNumber(rLocale); 839 840 switch (nNativeNumberMode) { 841 case NativeNumberMode::NATNUM0: // Ascii 842 case NativeNumberMode::NATNUM3: // Char, FullWidth 843 case NativeNumberMode::NATNUM12: // spell out numbers, dates and money amounts 844 return true; 845 case NativeNumberMode::NATNUM1: // Char, Lower 846 return (langnum >= 0); 847 case NativeNumberMode::NATNUM2: // Char, Upper 848 if (langnum == 4) // Hebrew numbering 849 return true; 850 [[fallthrough]]; 851 case NativeNumberMode::NATNUM4: // Text, Lower, Long 852 case NativeNumberMode::NATNUM5: // Text, Upper, Long 853 case NativeNumberMode::NATNUM6: // Text, FullWidth 854 case NativeNumberMode::NATNUM7: // Text. Lower, Short 855 case NativeNumberMode::NATNUM8: // Text, Upper, Short 856 return (langnum >= 0 && langnum < 4); // CJK numbering 857 case NativeNumberMode::NATNUM9: // Char, Hangul 858 case NativeNumberMode::NATNUM10: // Text, Hangul, Long 859 case NativeNumberMode::NATNUM11: // Text, Hangul, Short 860 return (langnum == 3); // Korean numbering 861 } 862 return false; 863 } 864 865 NativeNumberXmlAttributes SAL_CALL NativeNumberSupplierService::convertToXmlAttributes( const Locale& rLocale, sal_Int16 nNativeNumberMode ) 866 { 867 static const sal_Int16 attShort = 0; 868 static const sal_Int16 attMedium = 1; 869 static const sal_Int16 attLong = 2; 870 static const char *attType[] = { "short", "medium", "long" }; 871 872 sal_Int16 number = NumberChar_HalfWidth, type = attShort; 873 874 sal_Int16 langnum = -1; 875 if (isValidNatNum(rLocale, nNativeNumberMode)) { 876 langnum = getLanguageNumber(rLocale); 877 } 878 if (langnum != -1) { 879 switch (nNativeNumberMode) { 880 case NativeNumberMode::NATNUM0: // Ascii 881 number = NumberChar_HalfWidth; 882 type = attShort; 883 break; 884 case NativeNumberMode::NATNUM1: // Char, Lower 885 number = natnum1[langnum]; 886 type = attShort; 887 break; 888 case NativeNumberMode::NATNUM2: // Char, Upper 889 number = natnum2[langnum]; 890 type = number == NumberChar_he ? attMedium : attShort; 891 break; 892 case NativeNumberMode::NATNUM3: // Char, FullWidth 893 number = NumberChar_FullWidth; 894 type = attShort; 895 break; 896 case NativeNumberMode::NATNUM4: // Text, Lower, Long 897 number = natnum1[langnum]; 898 type = attLong; 899 break; 900 case NativeNumberMode::NATNUM5: // Text, Upper, Long 901 number = natnum2[langnum]; 902 type = attLong; 903 break; 904 case NativeNumberMode::NATNUM6: // Text, FullWidth 905 number = NumberChar_FullWidth; 906 type = attLong; 907 break; 908 case NativeNumberMode::NATNUM7: // Text. Lower, Short 909 number = natnum1[langnum]; 910 type = attMedium; 911 break; 912 case NativeNumberMode::NATNUM8: // Text, Upper, Short 913 number = natnum2[langnum]; 914 type = attMedium; 915 break; 916 case NativeNumberMode::NATNUM9: // Char, Hangul 917 number = NumberChar_Hangul_ko; 918 type = attShort; 919 break; 920 case NativeNumberMode::NATNUM10: // Text, Hangul, Long 921 number = NumberChar_Hangul_ko; 922 type = attLong; 923 break; 924 case NativeNumberMode::NATNUM11: // Text, Hangul, Short 925 number = NumberChar_Hangul_ko; 926 type = attMedium; 927 break; 928 default: 929 break; 930 } 931 } 932 return NativeNumberXmlAttributes(rLocale, OUString(&NumberChar[number][1], 1), 933 OUString::createFromAscii(attType[type])); 934 } 935 936 static bool natNumIn(sal_Int16 num, const sal_Int16 natnum[], sal_Int16 len) 937 { 938 for (sal_Int16 i = 0; i < len; i++) 939 if (natnum[i] == num) 940 return true; 941 return false; 942 } 943 944 sal_Int16 SAL_CALL NativeNumberSupplierService::convertFromXmlAttributes( const NativeNumberXmlAttributes& aAttr ) 945 { 946 sal_Unicode numberChar[NumberChar_Count]; 947 for (sal_Int16 i = 0; i < NumberChar_Count; i++) 948 numberChar[i] = NumberChar[i][1]; 949 OUString number(numberChar, NumberChar_Count); 950 951 sal_Int16 num = sal::static_int_cast<sal_Int16>( number.indexOf(aAttr.Format) ); 952 953 if ( aAttr.Style == "short" ) { 954 if (num == NumberChar_FullWidth) 955 return NativeNumberMode::NATNUM3; 956 else if (num == NumberChar_Hangul_ko) 957 return NativeNumberMode::NATNUM9; 958 else if (natNumIn(num, natnum1, sizeof_natnum1)) 959 return NativeNumberMode::NATNUM1; 960 else if (natNumIn(num, natnum2, sizeof_natnum2)) 961 return NativeNumberMode::NATNUM2; 962 } else if ( aAttr.Style == "medium" ) { 963 if (num == NumberChar_Hangul_ko) 964 return NativeNumberMode::NATNUM11; 965 else if (num == NumberChar_he) 966 return NativeNumberMode::NATNUM2; 967 else if (natNumIn(num, natnum1, sizeof_natnum1)) 968 return NativeNumberMode::NATNUM7; 969 else if (natNumIn(num, natnum2, sizeof_natnum2)) 970 return NativeNumberMode::NATNUM8; 971 } else if ( aAttr.Style == "long" ) { 972 if (num == NumberChar_FullWidth) 973 return NativeNumberMode::NATNUM6; 974 else if (num == NumberChar_Hangul_ko) 975 return NativeNumberMode::NATNUM10; 976 else if (natNumIn(num, natnum1, sizeof_natnum1)) 977 return NativeNumberMode::NATNUM4; 978 else if (natNumIn(num, natnum2, sizeof_natnum2)) 979 return NativeNumberMode::NATNUM5; 980 } else { 981 throw RuntimeException(); 982 } 983 return NativeNumberMode::NATNUM0; 984 } 985 986 987 // Following code generates Hebrew Number, 988 // see numerical system in the Hebrew Numbering System in following link for details, 989 // http://smontagu.org/writings/HebrewNumbers.html 990 991 namespace { 992 993 struct HebrewNumberChar { 994 sal_Unicode code; 995 sal_Int16 value; 996 }; 997 998 } 999 1000 HebrewNumberChar const HebrewNumberCharArray[] = { 1001 { 0x05ea, 400 }, 1002 { 0x05ea, 400 }, 1003 { 0x05e9, 300 }, 1004 { 0x05e8, 200 }, 1005 { 0x05e7, 100 }, 1006 { 0x05e6, 90 }, 1007 { 0x05e4, 80 }, 1008 { 0x05e2, 70 }, 1009 { 0x05e1, 60 }, 1010 { 0x05e0, 50 }, 1011 { 0x05de, 40 }, 1012 { 0x05dc, 30 }, 1013 { 0x05db, 20 }, 1014 { 0x05d9, 10 }, 1015 { 0x05d8, 9 }, 1016 { 0x05d7, 8 }, 1017 { 0x05d6, 7 }, 1018 { 0x05d5, 6 }, 1019 { 0x05d4, 5 }, 1020 { 0x05d3, 4 }, 1021 { 0x05d2, 3 }, 1022 { 0x05d1, 2 }, 1023 { 0x05d0, 1 } 1024 }; 1025 1026 static const sal_Unicode thousand[] = {0x05d0, 0x05dc, 0x05e3, 0x0}; 1027 static const sal_Unicode thousands[] = {0x05d0, 0x05dc, 0x05e4, 0x05d9, 0x0}; 1028 static const sal_Unicode thousands_last[] = {0x05d0, 0x05dc, 0x05e4, 0x05d9, 0x05dd, 0x0}; 1029 static const sal_Unicode geresh = 0x05f3; 1030 static const sal_Unicode gershayim = 0x05f4; 1031 1032 static void makeHebrewNumber(sal_Int64 value, OUStringBuffer& output, bool isLast, bool useGeresh) 1033 { 1034 sal_Int16 num = sal::static_int_cast<sal_Int16>(value % 1000); 1035 1036 if (value > 1000) { 1037 makeHebrewNumber(value / 1000, output, num != 0, useGeresh); 1038 output.append(" "); 1039 } 1040 if (num == 0) { 1041 output.append(value == 1000 ? thousand : isLast ? thousands_last : thousands); 1042 } else { 1043 sal_Int16 nbOfChar = 0; 1044 for (sal_Int32 j = 0; num > 0 && j < sal_Int32(SAL_N_ELEMENTS(HebrewNumberCharArray)); j++) { 1045 if (num - HebrewNumberCharArray[j].value >= 0) { 1046 nbOfChar++; 1047 // https://en.wikipedia.org/wiki/Hebrew_numerals#Key_exceptions 1048 // By convention, the numbers 15 and 16 are represented as 9 + 6 and 9 + 7 1049 if (num == 15 || num == 16) // substitution for 15 and 16 1050 j++; 1051 assert(j < sal_Int32(SAL_N_ELEMENTS(HebrewNumberCharArray))); 1052 num = sal::static_int_cast<sal_Int16>( num - HebrewNumberCharArray[j].value ); 1053 output.append(HebrewNumberCharArray[j].code); 1054 } 1055 } 1056 if (useGeresh) { 1057 if (nbOfChar > 1) // a number is written as more than one character 1058 output.insert(output.getLength() - 1, gershayim); 1059 else if (nbOfChar == 1) // a number is written as a single character 1060 output.append(geresh); 1061 } 1062 } 1063 } 1064 1065 OUString getHebrewNativeNumberString(const OUString& aNumberString, bool useGeresh) 1066 { 1067 sal_Int64 value = 0; 1068 sal_Int32 i, count = 0, len = aNumberString.getLength(); 1069 const sal_Unicode *src = aNumberString.getStr(); 1070 1071 for (i = 0; i < len; i++) { 1072 sal_Unicode ch = src[i]; 1073 if (isNumber(ch)) { 1074 if (++count >= 20) // Number is too long, could not be handled. 1075 return aNumberString; 1076 value = value * 10 + (ch - NUMBER_ZERO); 1077 } 1078 else if (isSeparator(ch) && count > 0) continue; 1079 else if (isMinus(ch) && count == 0) continue; 1080 else break; 1081 } 1082 1083 if (value > 0) { 1084 OUStringBuffer output(count*2 + 2 + len - i); 1085 1086 makeHebrewNumber(value, output, true, useGeresh); 1087 1088 if (i < len) 1089 output.append(std::u16string_view(aNumberString).substr(i)); 1090 1091 return output.makeStringAndClear(); 1092 } 1093 else 1094 return aNumberString; 1095 } 1096 1097 // Support for Cyrillic Numerals 1098 // See UTN 41 for implementation information 1099 // http://www.unicode.org/notes/tn41/ 1100 1101 static const sal_Unicode cyrillicThousandsMark = 0x0482; 1102 static const sal_Unicode cyrillicTitlo = 0x0483; 1103 static const sal_Unicode cyrillicTen = 0x0456; 1104 1105 namespace { 1106 1107 struct CyrillicNumberChar { 1108 sal_Unicode code; 1109 sal_Int16 value; 1110 }; 1111 1112 } 1113 1114 CyrillicNumberChar const CyrillicNumberCharArray[] = { 1115 { 0x0446, 900 }, 1116 { 0x047f, 800 }, 1117 { 0x0471, 700 }, 1118 { 0x0445, 600 }, 1119 { 0x0444, 500 }, 1120 { 0x0443, 400 }, 1121 { 0x0442, 300 }, 1122 { 0x0441, 200 }, 1123 { 0x0440, 100 }, 1124 { 0x0447, 90 }, 1125 { 0x043f, 80 }, 1126 { 0x047b, 70 }, 1127 { 0x046f, 60 }, 1128 { 0x043d, 50 }, 1129 { 0x043c, 40 }, 1130 { 0x043b, 30 }, 1131 { 0x043a, 20 }, 1132 { 0x0456, 10 }, 1133 { 0x0473, 9 }, 1134 { 0x0438, 8 }, 1135 { 0x0437, 7 }, 1136 { 0x0455, 6 }, 1137 { 0x0454, 5 }, 1138 { 0x0434, 4 }, 1139 { 0x0433, 3 }, 1140 { 0x0432, 2 }, 1141 { 0x0430, 1 } 1142 }; 1143 1144 static void makeCyrillicNumber(sal_Int64 value, OUStringBuffer& output, bool addTitlo) 1145 { 1146 sal_Int16 num = sal::static_int_cast<sal_Int16>(value % 1000); 1147 if (value >= 1000) { 1148 output.append(cyrillicThousandsMark); 1149 makeCyrillicNumber(value / 1000, output, false); 1150 if (value >= 10000 && (value - 10000) % 1000 != 0) { 1151 output.append(" "); 1152 } 1153 if (value % 1000 == 0) 1154 addTitlo = false; 1155 } 1156 1157 for (sal_Int32 j = 0; num > 0 && j < sal_Int32(SAL_N_ELEMENTS(CyrillicNumberCharArray)); j++) { 1158 if (num < 20 && num > 10) { 1159 num -= 10; 1160 makeCyrillicNumber(num, output, false); 1161 output.append(cyrillicTen); 1162 break; 1163 } 1164 1165 if (CyrillicNumberCharArray[j].value <= num) { 1166 output.append(CyrillicNumberCharArray[j].code); 1167 num = sal::static_int_cast<sal_Int16>( num - CyrillicNumberCharArray[j].value ); 1168 } 1169 } 1170 1171 if (addTitlo) { 1172 if (output.getLength() == 1) { 1173 output.append(cyrillicTitlo); 1174 } else if (output.getLength() == 2) { 1175 if (value > 800 && value < 900) { 1176 output.append(cyrillicTitlo); 1177 } else { 1178 output.insert(1, cyrillicTitlo); 1179 } 1180 } else if (output.getLength() > 2) { 1181 if (output.indexOf(" ") == output.getLength() - 2) { 1182 output.append(cyrillicTitlo); 1183 } else { 1184 output.insert(output.getLength() - 1, cyrillicTitlo); 1185 } 1186 } 1187 } 1188 } 1189 1190 OUString getCyrillicNativeNumberString(const OUString& aNumberString) 1191 { 1192 sal_Int64 value = 0; 1193 sal_Int32 i, count = 0, len = aNumberString.getLength(); 1194 const sal_Unicode *src = aNumberString.getStr(); 1195 1196 for (i = 0; i < len; i++) { 1197 sal_Unicode ch = src[i]; 1198 if (isNumber(ch)) { 1199 if (++count >= 8) // Number is too long, could not be handled. 1200 return aNumberString; 1201 value = value * 10 + (ch - NUMBER_ZERO); 1202 } 1203 else if (isSeparator(ch) && count > 0) continue; 1204 else if (isMinus(ch) && count == 0) continue; 1205 else break; 1206 } 1207 1208 if (value > 0) { 1209 OUStringBuffer output(count*2 + 2 + len - i); 1210 1211 makeCyrillicNumber(value, output, true); 1212 1213 if (i < len) 1214 output.append(std::u16string_view(aNumberString).substr(i)); 1215 1216 return output.makeStringAndClear(); 1217 } 1218 else 1219 return aNumberString; 1220 } 1221 1222 static const char implementationName[] = "com.sun.star.i18n.NativeNumberSupplier"; 1223 1224 OUString SAL_CALL NativeNumberSupplierService::getImplementationName() 1225 { 1226 return implementationName; 1227 } 1228 1229 sal_Bool SAL_CALL 1230 NativeNumberSupplierService::supportsService(const OUString& rServiceName) 1231 { 1232 return cppu::supportsService(this, rServiceName); 1233 } 1234 1235 Sequence< OUString > SAL_CALL 1236 NativeNumberSupplierService::getSupportedServiceNames() 1237 { 1238 return {implementationName, "com.sun.star.i18n.NativeNumberSupplier2"}; 1239 } 1240 1241 } 1242 1243 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * 1244 com_sun_star_i18n_NativeNumberSupplier_get_implementation( 1245 css::uno::XComponentContext *, 1246 css::uno::Sequence<css::uno::Any> const &) 1247 { 1248 return cppu::acquire(new i18npool::NativeNumberSupplierService()); 1249 } 1250 1251 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1252
