Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/i18nutil/source/utility/unicode.cxx
Line
Count
Source
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 <com/sun/star/i18n/UnicodeType.hpp>
21
#include <com/sun/star/i18n/ScriptType.hpp>
22
#include <i18nlangtag/languagetag.hxx>
23
#include <i18nlangtag/languagetagicu.hxx>
24
#include <i18nutil/unicode.hxx>
25
#include <sal/log.hxx>
26
#include <unicode/numfmt.h>
27
#include <unicode/uchar.h>
28
#include "unicode_data.h"
29
#include <rtl/character.hxx>
30
#include <o3tl/string_view.hxx>
31
#include <memory>
32
33
// Workaround for glibc braindamage:
34
// glibc 2.4's langinfo.h does "#define CURRENCY_SYMBOL __CURRENCY_SYMBOL"
35
// which (obviously) breaks UnicodeType::CURRENCY_SYMBOL
36
#undef CURRENCY_SYMBOL
37
38
using namespace ::com::sun::star::i18n;
39
40
template<class L, typename T>
41
289
static T getScriptType( const sal_Unicode ch, const L* typeList, T unknownType ) {
42
43
289
    sal_Int16 i = 0;
44
289
    css::i18n::UnicodeScript type = typeList[0].to;
45
289
    while (type < UnicodeScript_kScriptCount && ch > UnicodeScriptType[static_cast<int>(type)][UnicodeScriptTypeTo]) {
46
0
        type = typeList[++i].to;
47
0
    }
48
49
289
    return (type < UnicodeScript_kScriptCount &&
50
289
            ch >= UnicodeScriptType[static_cast<int>(typeList[i].from)][int(UnicodeScriptTypeFrom)]) ?
51
278
            typeList[i].value : unknownType;
52
289
}
53
54
sal_Int16
55
289
unicode::getUnicodeScriptType( const sal_Unicode ch, const ScriptTypeList* typeList, sal_Int16 unknownType ) {
56
289
    return getScriptType(ch, typeList, unknownType);
57
289
}
58
59
sal_Unicode
60
0
unicode::getUnicodeScriptStart( UnicodeScript type) {
61
0
    return UnicodeScriptType[static_cast<int>(type)][UnicodeScriptTypeFrom];
62
0
}
63
64
sal_Unicode
65
0
unicode::getUnicodeScriptEnd( UnicodeScript type) {
66
0
    return UnicodeScriptType[static_cast<int>(type)][UnicodeScriptTypeTo];
67
0
}
68
69
sal_Int16
70
unicode::getUnicodeType(const sal_uInt32 ch)
71
5.37M
{
72
5.37M
    static thread_local sal_uInt32 c = 0x00;
73
5.37M
    static thread_local sal_Int16 r = 0x00;
74
75
5.37M
    if (ch == c) return r;
76
4.72M
    else c = ch;
77
78
4.72M
    switch (u_charType(ch))
79
4.72M
    {
80
242k
        case U_UNASSIGNED:
81
242k
            r = css::i18n::UnicodeType::UNASSIGNED;
82
242k
            break;
83
306k
        case U_UPPERCASE_LETTER:
84
306k
            r = css::i18n::UnicodeType::UPPERCASE_LETTER;
85
306k
            break;
86
404k
        case U_LOWERCASE_LETTER:
87
404k
            r = css::i18n::UnicodeType::LOWERCASE_LETTER;
88
404k
            break;
89
5.16k
        case U_TITLECASE_LETTER:
90
5.16k
            r = css::i18n::UnicodeType::TITLECASE_LETTER;
91
5.16k
            break;
92
19.7k
        case U_MODIFIER_LETTER:
93
19.7k
            r = css::i18n::UnicodeType::MODIFIER_LETTER;
94
19.7k
            break;
95
2.20M
        case U_OTHER_LETTER:
96
2.20M
            r = css::i18n::UnicodeType::OTHER_LETTER;
97
2.20M
            break;
98
191k
        case U_NON_SPACING_MARK:
99
191k
            r = css::i18n::UnicodeType::NON_SPACING_MARK;
100
191k
            break;
101
359
        case U_ENCLOSING_MARK:
102
359
            r = css::i18n::UnicodeType::ENCLOSING_MARK;
103
359
            break;
104
31.6k
        case U_COMBINING_SPACING_MARK:
105
31.6k
            r = css::i18n::UnicodeType::COMBINING_SPACING_MARK;
106
31.6k
            break;
107
57.7k
        case U_DECIMAL_DIGIT_NUMBER:
108
57.7k
            r = css::i18n::UnicodeType::DECIMAL_DIGIT_NUMBER;
109
57.7k
            break;
110
9.39k
        case U_LETTER_NUMBER:
111
9.39k
            r = css::i18n::UnicodeType::LETTER_NUMBER;
112
9.39k
            break;
113
15.9k
        case U_OTHER_NUMBER:
114
15.9k
            r = css::i18n::UnicodeType::OTHER_NUMBER;
115
15.9k
            break;
116
6.61k
        case U_SPACE_SEPARATOR:
117
6.61k
            r = css::i18n::UnicodeType::SPACE_SEPARATOR;
118
6.61k
            break;
119
0
        case U_LINE_SEPARATOR:
120
0
            r = css::i18n::UnicodeType::LINE_SEPARATOR;
121
0
            break;
122
0
        case U_PARAGRAPH_SEPARATOR:
123
0
            r = css::i18n::UnicodeType::PARAGRAPH_SEPARATOR;
124
0
            break;
125
766k
        case U_CONTROL_CHAR:
126
766k
            r = css::i18n::UnicodeType::CONTROL;
127
766k
            break;
128
58.1k
        case U_FORMAT_CHAR:
129
58.1k
            r = css::i18n::UnicodeType::FORMAT;
130
58.1k
            break;
131
5
        case U_PRIVATE_USE_CHAR:
132
5
            r = css::i18n::UnicodeType::PRIVATE_USE;
133
5
            break;
134
3
        case U_SURROGATE:
135
3
            r = css::i18n::UnicodeType::SURROGATE;
136
3
            break;
137
13.6k
        case U_DASH_PUNCTUATION:
138
13.6k
            r = css::i18n::UnicodeType::DASH_PUNCTUATION;
139
13.6k
            break;
140
810
        case U_INITIAL_PUNCTUATION:
141
810
            r = css::i18n::UnicodeType::INITIAL_PUNCTUATION;
142
810
            break;
143
11.2k
        case U_FINAL_PUNCTUATION:
144
11.2k
            r = css::i18n::UnicodeType::FINAL_PUNCTUATION;
145
11.2k
            break;
146
623
        case U_CONNECTOR_PUNCTUATION:
147
623
            r = css::i18n::UnicodeType::CONNECTOR_PUNCTUATION;
148
623
            break;
149
161k
        case U_OTHER_PUNCTUATION:
150
161k
            r = css::i18n::UnicodeType::OTHER_PUNCTUATION;
151
161k
            break;
152
31.5k
        case U_MATH_SYMBOL:
153
31.5k
            r = css::i18n::UnicodeType::MATH_SYMBOL;
154
31.5k
            break;
155
13.3k
        case U_CURRENCY_SYMBOL:
156
13.3k
            r = css::i18n::UnicodeType::CURRENCY_SYMBOL;
157
13.3k
            break;
158
23.3k
        case U_MODIFIER_SYMBOL:
159
23.3k
            r = css::i18n::UnicodeType::MODIFIER_SYMBOL;
160
23.3k
            break;
161
118k
        case U_OTHER_SYMBOL:
162
118k
            r = css::i18n::UnicodeType::OTHER_SYMBOL;
163
118k
            break;
164
21.0k
        case U_START_PUNCTUATION:
165
21.0k
            r = css::i18n::UnicodeType::START_PUNCTUATION;
166
21.0k
            break;
167
8.43k
        case U_END_PUNCTUATION:
168
8.43k
            r = css::i18n::UnicodeType::END_PUNCTUATION;
169
8.43k
            break;
170
4.72M
    }
171
172
4.72M
    return r;
173
4.72M
}
174
175
sal_uInt8
176
0
unicode::getUnicodeDirection( const sal_Unicode ch ) {
177
0
    static thread_local sal_Unicode c = 0x00;
178
0
    static thread_local sal_uInt8 r = 0x00;
179
180
0
    if (ch == c) return r;
181
0
    else c = ch;
182
183
0
    sal_Int16 address = UnicodeDirectionIndex[ch >> 8];
184
0
    r = (address < UnicodeDirectionNumberBlock)
185
0
            ? UnicodeDirectionBlockValue[address]
186
0
            : UnicodeDirectionValue[((address - UnicodeDirectionNumberBlock) << 8) + (ch & 0xff)];
187
0
    return r;
188
0
}
189
190
0
sal_uInt32 unicode::GetMirroredChar(sal_uInt32 nChar) {
191
0
    nChar = u_charMirror(nChar);
192
0
    return nChar;
193
0
}
194
195
0
static constexpr auto bit(sal_Int16 name) { return 1U << name; }
196
197
bool unicode::isControl(const sal_uInt32 ch)
198
0
{
199
0
    const auto CONTROLMASK = bit(css::i18n::UnicodeType::CONTROL) |
200
0
                             bit(css::i18n::UnicodeType::FORMAT) |
201
0
                             bit(css::i18n::UnicodeType::LINE_SEPARATOR) |
202
0
                             bit(css::i18n::UnicodeType::PARAGRAPH_SEPARATOR);
203
0
    return (bit(getUnicodeType(ch)) & CONTROLMASK) != 0;
204
0
}
205
206
bool unicode::isAlpha(const sal_uInt32 ch)
207
0
{
208
0
    const auto ALPHAMASK = bit(css::i18n::UnicodeType::UPPERCASE_LETTER) |
209
0
                           bit(css::i18n::UnicodeType::LOWERCASE_LETTER) |
210
0
                           bit(css::i18n::UnicodeType::TITLECASE_LETTER) |
211
0
                           bit(css::i18n::UnicodeType::MODIFIER_LETTER) |
212
0
                           bit(css::i18n::UnicodeType::OTHER_LETTER);
213
0
    return (bit(getUnicodeType(ch)) & ALPHAMASK) != 0;
214
0
}
215
216
0
bool unicode::isSpace(const sal_uInt32 ch) {
217
0
    const auto SPACEMASK = bit(css::i18n::UnicodeType::SPACE_SEPARATOR) |
218
0
                           bit(css::i18n::UnicodeType::LINE_SEPARATOR) |
219
0
                           bit(css::i18n::UnicodeType::PARAGRAPH_SEPARATOR);
220
0
    return (bit(getUnicodeType(ch)) & SPACEMASK) != 0;
221
0
}
222
223
bool unicode::isWhiteSpace(const sal_uInt32 ch)
224
0
{
225
0
    const auto CONTROLSPACE = bit(0x09) | bit(0x0a) | bit(0x0b) | bit(0x0c) | bit(0x0d) | bit(0x1c)
226
0
                              | bit(0x1d) | bit(0x1e) | bit(0x1f);
227
0
    return (ch != 0xa0 && isSpace(ch)) || (ch <= 0x1F && (bit(ch) & CONTROLSPACE));
228
0
}
229
230
sal_Int16 unicode::getScriptClassFromUScriptCode(UScriptCode eScript)
231
75.9M
{
232
    //See unicode/uscript.h
233
75.9M
    sal_Int16 nRet;
234
75.9M
    switch (eScript)
235
75.9M
    {
236
0
        case USCRIPT_INVALID_CODE:
237
18.6M
        case USCRIPT_COMMON:
238
21.8M
        case USCRIPT_INHERITED:
239
21.8M
        case USCRIPT_UNWRITTEN_LANGUAGES:
240
59.2M
        case USCRIPT_UNKNOWN:
241
59.2M
        case USCRIPT_MATHEMATICAL_NOTATION:
242
59.2M
        case USCRIPT_SYMBOLS:
243
59.2M
        case USCRIPT_CODE_LIMIT:
244
59.2M
            nRet = ScriptType::WEAK;
245
59.2M
            break;
246
6.33k
        case USCRIPT_ARMENIAN:
247
111k
        case USCRIPT_CHEROKEE:
248
189k
        case USCRIPT_COPTIC:
249
4.31M
        case USCRIPT_CYRILLIC:
250
4.78M
        case USCRIPT_GEORGIAN:
251
4.78M
        case USCRIPT_GOTHIC:
252
4.81M
        case USCRIPT_GREEK:
253
5.51M
        case USCRIPT_LATIN:
254
5.51M
        case USCRIPT_OGHAM:
255
5.51M
        case USCRIPT_OLD_ITALIC:
256
5.51M
        case USCRIPT_RUNIC:
257
5.67M
        case USCRIPT_CANADIAN_ABORIGINAL:
258
6.50M
        case USCRIPT_BRAILLE:
259
6.51M
        case USCRIPT_CYPRIOT:
260
6.51M
        case USCRIPT_OSMANYA:
261
6.51M
        case USCRIPT_SHAVIAN:
262
6.51M
        case USCRIPT_KATAKANA_OR_HIRAGANA:
263
6.79M
        case USCRIPT_GLAGOLITIC:
264
6.79M
        case USCRIPT_CIRTH:
265
6.79M
        case USCRIPT_OLD_CHURCH_SLAVONIC_CYRILLIC:
266
6.79M
        case USCRIPT_OLD_HUNGARIAN:
267
6.79M
        case USCRIPT_LATIN_FRAKTUR:
268
6.79M
        case USCRIPT_LATIN_GAELIC:
269
6.79M
            nRet = ScriptType::LATIN;
270
6.79M
            break;
271
0
        case USCRIPT_BOPOMOFO:
272
6.01k
        case USCRIPT_HAN:
273
129k
        case USCRIPT_HANGUL:
274
130k
        case USCRIPT_HIRAGANA:
275
166k
        case USCRIPT_KATAKANA:
276
166k
        case USCRIPT_YI:
277
166k
        case USCRIPT_SIMPLIFIED_HAN:
278
166k
        case USCRIPT_TRADITIONAL_HAN:
279
166k
        case USCRIPT_JAPANESE:
280
166k
        case USCRIPT_KOREAN:
281
167k
        case USCRIPT_TANGUT:
282
167k
        case USCRIPT_KHITAN_SMALL_SCRIPT:
283
167k
            nRet = ScriptType::ASIAN;
284
167k
            break;
285
1.49M
        case USCRIPT_ARABIC:
286
1.49M
        case USCRIPT_BENGALI:
287
1.49M
        case USCRIPT_DESERET:
288
1.53M
        case USCRIPT_DEVANAGARI:
289
1.86M
        case USCRIPT_ETHIOPIC:
290
1.86M
        case USCRIPT_GUJARATI:
291
1.86M
        case USCRIPT_GURMUKHI:
292
1.92M
        case USCRIPT_HEBREW:
293
1.92M
        case USCRIPT_KANNADA:
294
1.96M
        case USCRIPT_KHMER:
295
1.96M
        case USCRIPT_LAO:
296
1.96M
        case USCRIPT_MALAYALAM:
297
1.96M
        case USCRIPT_MONGOLIAN:
298
2.04M
        case USCRIPT_MYANMAR:
299
2.04M
        case USCRIPT_ORIYA:
300
2.04M
        case USCRIPT_SINHALA:
301
2.09M
        case USCRIPT_SYRIAC:
302
2.09M
        case USCRIPT_TAMIL:
303
2.09M
        case USCRIPT_TELUGU:
304
2.09M
        case USCRIPT_THAANA:
305
2.09M
        case USCRIPT_THAI:
306
2.09M
        case USCRIPT_TIBETAN:
307
2.22M
        case USCRIPT_TAGALOG:
308
2.25M
        case USCRIPT_HANUNOO:
309
2.27M
        case USCRIPT_BUHID:
310
2.30M
        case USCRIPT_TAGBANWA:
311
2.54M
        case USCRIPT_LIMBU:
312
2.55M
        case USCRIPT_LINEAR_B:
313
2.58M
        case USCRIPT_TAI_LE:
314
2.58M
        case USCRIPT_UGARITIC:
315
2.80M
        case USCRIPT_BUGINESE:
316
2.80M
        case USCRIPT_KHAROSHTHI:
317
2.97M
        case USCRIPT_SYLOTI_NAGRI:
318
3.06M
        case USCRIPT_NEW_TAI_LUE:
319
3.23M
        case USCRIPT_TIFINAGH:
320
3.23M
        case USCRIPT_OLD_PERSIAN:
321
3.68M
        case USCRIPT_BALINESE:
322
3.74M
        case USCRIPT_BATAK:
323
3.74M
        case USCRIPT_BLISSYMBOLS:
324
3.74M
        case USCRIPT_BRAHMI:
325
4.00M
        case USCRIPT_CHAM:
326
4.00M
        case USCRIPT_DEMOTIC_EGYPTIAN:
327
4.00M
        case USCRIPT_HIERATIC_EGYPTIAN:
328
4.00M
        case USCRIPT_EGYPTIAN_HIEROGLYPHS:
329
4.00M
        case USCRIPT_KHUTSURI:
330
4.00M
        case USCRIPT_PAHAWH_HMONG:
331
4.00M
        case USCRIPT_HARAPPAN_INDUS:
332
4.11M
        case USCRIPT_JAVANESE:
333
4.20M
        case USCRIPT_KAYAH_LI:
334
4.77M
        case USCRIPT_LEPCHA:
335
4.77M
        case USCRIPT_LINEAR_A:
336
4.86M
        case USCRIPT_MANDAEAN:
337
4.86M
        case USCRIPT_MAYAN_HIEROGLYPHS:
338
4.86M
        case USCRIPT_MEROITIC:
339
5.34M
        case USCRIPT_NKO:
340
5.34M
        case USCRIPT_ORKHON:
341
5.34M
        case USCRIPT_OLD_PERMIC:
342
5.40M
        case USCRIPT_PHAGS_PA:
343
5.40M
        case USCRIPT_PHOENICIAN:
344
5.40M
        case USCRIPT_PHONETIC_POLLARD:
345
5.40M
        case USCRIPT_RONGORONGO:
346
5.40M
        case USCRIPT_SARATI:
347
5.40M
        case USCRIPT_ESTRANGELO_SYRIAC:
348
5.40M
        case USCRIPT_WESTERN_SYRIAC:
349
5.40M
        case USCRIPT_EASTERN_SYRIAC:
350
5.40M
        case USCRIPT_TENGWAR:
351
5.86M
        case USCRIPT_VAI:
352
5.86M
        case USCRIPT_VISIBLE_SPEECH:
353
5.86M
        case USCRIPT_CUNEIFORM:
354
5.86M
        case USCRIPT_CARIAN:
355
6.07M
        case USCRIPT_LANNA:
356
6.07M
        case USCRIPT_LYCIAN:
357
6.07M
        case USCRIPT_LYDIAN:
358
6.12M
        case USCRIPT_OL_CHIKI:
359
6.18M
        case USCRIPT_REJANG:
360
6.27M
        case USCRIPT_SAURASHTRA:
361
6.27M
        case USCRIPT_SIGN_WRITING:
362
6.34M
        case USCRIPT_SUNDANESE:
363
6.34M
        case USCRIPT_MOON:
364
6.42M
        case USCRIPT_MEITEI_MAYEK:
365
6.42M
        case USCRIPT_IMPERIAL_ARAMAIC:
366
6.42M
        case USCRIPT_AVESTAN:
367
6.42M
        case USCRIPT_CHAKMA:
368
6.42M
        case USCRIPT_KAITHI:
369
6.42M
        case USCRIPT_MANICHAEAN:
370
6.42M
        case USCRIPT_INSCRIPTIONAL_PAHLAVI:
371
6.42M
        case USCRIPT_PSALTER_PAHLAVI:
372
6.42M
        case USCRIPT_BOOK_PAHLAVI:
373
6.42M
        case USCRIPT_INSCRIPTIONAL_PARTHIAN:
374
9.39M
        case USCRIPT_SAMARITAN:
375
9.52M
        case USCRIPT_TAI_VIET:
376
9.64M
        case USCRIPT_BAMUM:
377
9.69M
        case USCRIPT_LISU:
378
9.69M
        case USCRIPT_NAKHI_GEBA:
379
9.69M
        case USCRIPT_OLD_SOUTH_ARABIAN:
380
9.69M
        case USCRIPT_BASSA_VAH:
381
9.69M
        case USCRIPT_DUPLOYAN_SHORTAND:
382
9.69M
        case USCRIPT_ELBASAN:
383
9.69M
        case USCRIPT_GRANTHA:
384
9.69M
        case USCRIPT_KPELLE:
385
9.69M
        case USCRIPT_LOMA:
386
9.77M
        case USCRIPT_MENDE:
387
9.77M
        case USCRIPT_MEROITIC_CURSIVE:
388
9.77M
        case USCRIPT_OLD_NORTH_ARABIAN:
389
9.77M
        case USCRIPT_NABATAEAN:
390
9.77M
        case USCRIPT_PALMYRENE:
391
9.77M
        case USCRIPT_SINDHI:
392
9.77M
        case USCRIPT_WARANG_CITI:
393
9.77M
        default:         // anything new is going to be pretty wild
394
9.77M
            nRet = ScriptType::COMPLEX;
395
9.77M
            break;
396
75.9M
    }
397
75.9M
    return nRet;
398
75.9M
}
399
400
sal_Int16 unicode::getScriptClassFromLanguageTag( const LanguageTag& rLanguageTag )
401
0
{
402
0
    constexpr int32_t nBuf = 42;
403
0
    UScriptCode aBuf[nBuf];
404
0
    if (rLanguageTag.hasScript())
405
0
    {
406
0
        aBuf[0] = static_cast<UScriptCode>(u_getPropertyValueEnum( UCHAR_SCRIPT,
407
0
                OUStringToOString( rLanguageTag.getScript(), RTL_TEXTENCODING_ASCII_US).getStr()));
408
0
    }
409
0
    else
410
0
    {
411
0
        OUString aName;
412
0
        if (rLanguageTag.getCountry().isEmpty())
413
0
            aName = rLanguageTag.getLanguage();
414
0
        else
415
0
            aName = rLanguageTag.getLanguage() + "-" + rLanguageTag.getCountry();
416
0
        UErrorCode status = U_ZERO_ERROR;
417
0
        const int32_t nScripts = uscript_getCode(
418
0
                OUStringToOString( aName, RTL_TEXTENCODING_ASCII_US).getStr(),
419
0
                aBuf, nBuf, &status);
420
        // U_BUFFER_OVERFLOW_ERROR would be set with too many scripts for buffer
421
        // and required capacity returned, but really..
422
0
        if (nScripts == 0 || !U_SUCCESS(status))
423
0
            return css::i18n::ScriptType::LATIN;
424
0
    }
425
0
    return getScriptClassFromUScriptCode( aBuf[0]);
426
0
}
427
428
OString unicode::getExemplarLanguageForUScriptCode(UScriptCode eScript)
429
0
{
430
0
    OString sRet;
431
0
    switch (eScript)
432
0
    {
433
0
        case USCRIPT_CODE_LIMIT:
434
0
        case USCRIPT_INVALID_CODE:
435
0
        case USCRIPT_MATHEMATICAL_NOTATION:
436
0
        case USCRIPT_SYMBOLS:
437
0
            sRet = "zxx"_ostr;
438
0
            break;
439
0
        case USCRIPT_COMMON:
440
0
        case USCRIPT_INHERITED:
441
0
        case USCRIPT_UNWRITTEN_LANGUAGES:
442
0
        case USCRIPT_UNKNOWN:
443
0
            sRet = "und"_ostr;
444
0
            break;
445
0
        case USCRIPT_ARABIC:
446
0
            sRet = "ar"_ostr;
447
0
            break;
448
0
        case USCRIPT_ARMENIAN:
449
0
            sRet = "hy"_ostr;
450
0
            break;
451
0
        case USCRIPT_BENGALI:
452
0
            sRet = "bn"_ostr;
453
0
            break;
454
0
        case USCRIPT_BOPOMOFO:
455
0
            sRet = "zh"_ostr;
456
0
            break;
457
0
        case USCRIPT_CHEROKEE:
458
0
            sRet = "chr"_ostr;
459
0
            break;
460
0
        case USCRIPT_COPTIC:
461
0
            sRet = "cop"_ostr;
462
0
            break;
463
0
        case USCRIPT_CYRILLIC:
464
0
            sRet = "ru"_ostr;
465
0
            break;
466
0
        case USCRIPT_DESERET:
467
0
            sRet = "en"_ostr;
468
0
            break;
469
0
        case USCRIPT_DEVANAGARI:
470
0
            sRet = "hi"_ostr;
471
0
            break;
472
0
        case USCRIPT_ETHIOPIC:
473
0
            sRet = "am"_ostr;
474
0
            break;
475
0
        case USCRIPT_GEORGIAN:
476
0
        case USCRIPT_KHUTSURI:
477
0
            sRet = "ka"_ostr;
478
0
            break;
479
0
        case USCRIPT_GOTHIC:
480
0
            sRet = "got"_ostr;
481
0
            break;
482
0
        case USCRIPT_GREEK:
483
0
            sRet = "el"_ostr;
484
0
            break;
485
0
        case USCRIPT_GUJARATI:
486
0
        case USCRIPT_KHOJKI:
487
0
            sRet = "gu"_ostr;
488
0
            break;
489
0
        case USCRIPT_GURMUKHI:
490
0
            sRet = "pa"_ostr;
491
0
            break;
492
0
        case USCRIPT_HAN:
493
0
            sRet = "zh"_ostr;
494
0
            break;
495
0
        case USCRIPT_HANGUL:
496
0
        case USCRIPT_KOREAN:
497
0
        case USCRIPT_JAMO:
498
0
            sRet = "ko"_ostr;   // Jamo - elements of Hangul Syllables
499
0
            break;
500
0
        case USCRIPT_HEBREW:
501
0
            sRet = "hr"_ostr;
502
0
            break;
503
0
        case USCRIPT_HIRAGANA:
504
0
            sRet = "ja"_ostr;
505
0
            break;
506
0
        case USCRIPT_KANNADA:
507
0
            sRet = "kn"_ostr;
508
0
            break;
509
0
        case USCRIPT_KATAKANA:
510
0
            sRet = "ja"_ostr;
511
0
            break;
512
0
        case USCRIPT_KHMER:
513
0
            sRet = "km"_ostr;
514
0
            break;
515
0
        case USCRIPT_LAO:
516
0
            sRet = "lo"_ostr;
517
0
            break;
518
0
        case USCRIPT_LATIN:
519
0
            sRet = "en"_ostr;
520
0
            break;
521
0
        case USCRIPT_MALAYALAM:
522
0
            sRet = "ml"_ostr;
523
0
            break;
524
0
        case USCRIPT_MONGOLIAN:
525
0
            sRet = "mn"_ostr;
526
0
            break;
527
0
        case USCRIPT_MYANMAR:
528
0
            sRet = "my"_ostr;
529
0
            break;
530
0
        case USCRIPT_OGHAM:
531
0
            sRet = "pgl"_ostr;
532
0
            break;
533
0
        case USCRIPT_OLD_ITALIC:
534
0
            sRet = "osc"_ostr;
535
0
            break;
536
0
        case USCRIPT_ORIYA:
537
0
            sRet = "or"_ostr;
538
0
            break;
539
0
        case USCRIPT_RUNIC:
540
0
            sRet = "ang"_ostr;
541
0
            break;
542
0
        case USCRIPT_SINHALA:
543
0
            sRet = "si"_ostr;
544
0
            break;
545
0
        case USCRIPT_SYRIAC:
546
0
        case USCRIPT_ESTRANGELO_SYRIAC:
547
0
            sRet = "syr"_ostr;
548
0
            break;
549
0
        case USCRIPT_TAMIL:
550
0
        case USCRIPT_GRANTHA:
551
0
            sRet = "ta"_ostr;
552
0
            break;
553
0
        case USCRIPT_TELUGU:
554
0
            sRet = "te"_ostr;
555
0
            break;
556
0
        case USCRIPT_THAANA:
557
0
            sRet = "dv"_ostr;
558
0
            break;
559
0
        case USCRIPT_THAI:
560
0
            sRet = "th"_ostr;
561
0
            break;
562
0
        case USCRIPT_TIBETAN:
563
0
            sRet = "bo"_ostr;
564
0
            break;
565
0
        case USCRIPT_CANADIAN_ABORIGINAL:
566
0
            sRet = "iu"_ostr;
567
0
            break;
568
0
        case USCRIPT_YI:
569
0
            sRet = "ii"_ostr;
570
0
            break;
571
0
        case USCRIPT_TAGALOG:
572
0
            sRet = "tl"_ostr;
573
0
            break;
574
0
        case USCRIPT_HANUNOO:
575
0
            sRet = "hnn"_ostr;
576
0
            break;
577
0
        case USCRIPT_BUHID:
578
0
            sRet = "bku"_ostr;
579
0
            break;
580
0
        case USCRIPT_TAGBANWA:
581
0
            sRet = "tbw"_ostr;
582
0
            break;
583
0
        case USCRIPT_BRAILLE:
584
0
            sRet = "en"_ostr;
585
0
            break;
586
0
        case USCRIPT_CYPRIOT:
587
0
            sRet = "ecy"_ostr;
588
0
            break;
589
0
        case USCRIPT_LIMBU:
590
0
            sRet = "lif"_ostr;
591
0
            break;
592
0
        case USCRIPT_LINEAR_B:
593
0
            sRet = "gmy"_ostr;
594
0
            break;
595
0
        case USCRIPT_OSMANYA:
596
0
            sRet = "so"_ostr;
597
0
            break;
598
0
        case USCRIPT_SHAVIAN:
599
0
            sRet = "en"_ostr;
600
0
            break;
601
0
        case USCRIPT_TAI_LE:
602
0
            sRet = "tdd"_ostr;
603
0
            break;
604
0
        case USCRIPT_UGARITIC:
605
0
            sRet = "uga"_ostr;
606
0
            break;
607
0
        case USCRIPT_KATAKANA_OR_HIRAGANA:
608
0
            sRet = "ja"_ostr;
609
0
            break;
610
0
        case USCRIPT_BUGINESE:
611
0
            sRet = "bug"_ostr;
612
0
            break;
613
0
        case USCRIPT_GLAGOLITIC:
614
0
            sRet = "ch"_ostr;
615
0
            break;
616
0
        case USCRIPT_KHAROSHTHI:
617
0
        case USCRIPT_BRAHMI:
618
0
            sRet = "pra"_ostr;
619
0
            break;
620
0
        case USCRIPT_SYLOTI_NAGRI:
621
0
            sRet = "syl"_ostr;
622
0
            break;
623
0
        case USCRIPT_NEW_TAI_LUE:
624
0
            sRet = "khb"_ostr;
625
0
            break;
626
0
        case USCRIPT_TIFINAGH:
627
0
            sRet = "tmh"_ostr;
628
0
            break;
629
0
        case USCRIPT_OLD_PERSIAN:
630
0
            sRet = "peo"_ostr;
631
0
            break;
632
0
        case USCRIPT_BALINESE:
633
0
            sRet = "ban"_ostr;
634
0
            break;
635
0
        case USCRIPT_BATAK:
636
0
            sRet = "btk"_ostr;
637
0
            break;
638
0
        case USCRIPT_BLISSYMBOLS:
639
0
            sRet = "en"_ostr;
640
0
            break;
641
0
        case USCRIPT_CHAM:
642
0
            sRet = "cja"_ostr;
643
0
            break;
644
0
        case USCRIPT_CIRTH:
645
0
        case USCRIPT_TENGWAR:
646
0
            sRet = "sjn"_ostr;
647
0
            break;
648
0
        case USCRIPT_OLD_CHURCH_SLAVONIC_CYRILLIC:
649
0
            sRet = "cu"_ostr;
650
0
            break;
651
0
        case USCRIPT_DEMOTIC_EGYPTIAN:
652
0
        case USCRIPT_HIERATIC_EGYPTIAN:
653
0
        case USCRIPT_EGYPTIAN_HIEROGLYPHS:
654
0
            sRet = "egy"_ostr;
655
0
            break;
656
0
        case USCRIPT_SIMPLIFIED_HAN:
657
0
            sRet = "zh"_ostr;
658
0
            break;
659
0
        case USCRIPT_TRADITIONAL_HAN:
660
0
            sRet = "zh"_ostr;
661
0
            break;
662
0
        case USCRIPT_PAHAWH_HMONG:
663
0
            sRet = "blu"_ostr;
664
0
            break;
665
0
        case USCRIPT_OLD_HUNGARIAN:
666
0
            sRet = "ohu"_ostr;
667
0
            break;
668
0
        case USCRIPT_HARAPPAN_INDUS:
669
0
            sRet = "xiv"_ostr;
670
0
            break;
671
0
        case USCRIPT_JAVANESE:
672
0
            sRet = "kaw"_ostr;
673
0
            break;
674
0
        case USCRIPT_KAYAH_LI:
675
0
            sRet = "eky"_ostr;
676
0
            break;
677
0
        case USCRIPT_LATIN_FRAKTUR:
678
0
            sRet = "de"_ostr;
679
0
            break;
680
0
        case USCRIPT_LATIN_GAELIC:
681
0
            sRet = "ga"_ostr;
682
0
            break;
683
0
        case USCRIPT_LEPCHA:
684
0
            sRet = "lep"_ostr;
685
0
            break;
686
0
        case USCRIPT_LINEAR_A:
687
0
            sRet = "ecr"_ostr;
688
0
            break;
689
0
        case USCRIPT_MAYAN_HIEROGLYPHS:
690
0
            sRet = "myn"_ostr;
691
0
            break;
692
0
        case USCRIPT_MEROITIC_CURSIVE:
693
0
        case USCRIPT_MEROITIC:
694
0
            sRet = "xmr"_ostr;
695
0
            break;
696
0
        case USCRIPT_NKO:
697
0
            sRet = "nqo"_ostr;
698
0
            break;
699
0
        case USCRIPT_ORKHON:
700
0
            sRet = "otk"_ostr;
701
0
            break;
702
0
        case USCRIPT_OLD_PERMIC:
703
0
            sRet = "kv"_ostr;
704
0
            break;
705
0
        case USCRIPT_PHAGS_PA:
706
0
            sRet = "xng"_ostr;
707
0
            break;
708
0
        case USCRIPT_PHOENICIAN:
709
0
            sRet = "phn"_ostr;
710
0
            break;
711
0
        case USCRIPT_PHONETIC_POLLARD:
712
0
            sRet = "hmd"_ostr;
713
0
            break;
714
0
        case USCRIPT_RONGORONGO:
715
0
            sRet = "rap"_ostr;
716
0
            break;
717
0
        case USCRIPT_SARATI:
718
0
            sRet = "qya"_ostr;
719
0
            break;
720
0
        case USCRIPT_WESTERN_SYRIAC:
721
0
            sRet = "tru"_ostr;
722
0
            break;
723
0
        case USCRIPT_EASTERN_SYRIAC:
724
0
            sRet = "aii"_ostr;
725
0
            break;
726
0
        case USCRIPT_VAI:
727
0
            sRet = "vai"_ostr;
728
0
            break;
729
0
        case USCRIPT_VISIBLE_SPEECH:
730
0
            sRet = "en"_ostr;
731
0
            break;
732
0
        case USCRIPT_CUNEIFORM:
733
0
            sRet = "akk"_ostr;
734
0
            break;
735
0
        case USCRIPT_CARIAN:
736
0
            sRet = "xcr"_ostr;
737
0
            break;
738
0
        case USCRIPT_JAPANESE:
739
0
            sRet = "ja"_ostr;
740
0
            break;
741
0
        case USCRIPT_LANNA:
742
0
            sRet = "nod"_ostr;
743
0
            break;
744
0
        case USCRIPT_LYCIAN:
745
0
            sRet = "xlc"_ostr;
746
0
            break;
747
0
        case USCRIPT_LYDIAN:
748
0
            sRet = "xld"_ostr;
749
0
            break;
750
0
        case USCRIPT_OL_CHIKI:
751
0
            sRet = "sat"_ostr;
752
0
            break;
753
0
        case USCRIPT_REJANG:
754
0
            sRet = "rej"_ostr;
755
0
            break;
756
0
        case USCRIPT_SAURASHTRA:
757
0
            sRet = "saz"_ostr;
758
0
            break;
759
0
        case USCRIPT_SIGN_WRITING:
760
0
            sRet = "en"_ostr;
761
0
            break;
762
0
        case USCRIPT_SUNDANESE:
763
0
            sRet = "su"_ostr;
764
0
            break;
765
0
        case USCRIPT_MOON:
766
0
            sRet = "en"_ostr;
767
0
            break;
768
0
        case USCRIPT_MEITEI_MAYEK:
769
0
            sRet = "mni"_ostr;
770
0
            break;
771
0
        case USCRIPT_IMPERIAL_ARAMAIC:
772
0
            sRet = "arc"_ostr;
773
0
            break;
774
0
        case USCRIPT_AVESTAN:
775
0
            sRet = "ae"_ostr;
776
0
            break;
777
0
        case USCRIPT_CHAKMA:
778
0
            sRet = "ccp"_ostr;
779
0
            break;
780
0
        case USCRIPT_KAITHI:
781
0
            sRet = "awa"_ostr;
782
0
            break;
783
0
        case USCRIPT_MANICHAEAN:
784
0
            sRet = "xmn"_ostr;
785
0
            break;
786
0
        case USCRIPT_INSCRIPTIONAL_PAHLAVI:
787
0
        case USCRIPT_PSALTER_PAHLAVI:
788
0
        case USCRIPT_BOOK_PAHLAVI:
789
0
        case USCRIPT_INSCRIPTIONAL_PARTHIAN:
790
0
            sRet = "xpr"_ostr;
791
0
            break;
792
0
        case USCRIPT_SAMARITAN:
793
0
            sRet = "heb"_ostr;
794
0
            break;
795
0
        case USCRIPT_TAI_VIET:
796
0
            sRet = "blt"_ostr;
797
0
            break;
798
0
        case USCRIPT_MANDAEAN: /* Aliased to USCRIPT_MANDAIC in icu 4.6. */
799
0
            sRet = "mic"_ostr;
800
0
            break;
801
0
        case USCRIPT_NABATAEAN:
802
0
            sRet = "mis-Nbat"_ostr;  // Uncoded with script
803
0
            break;
804
0
        case USCRIPT_PALMYRENE:
805
0
            sRet = "mis-Palm"_ostr;  // Uncoded with script
806
0
            break;
807
0
        case USCRIPT_BAMUM:
808
0
            sRet = "bax"_ostr;
809
0
            break;
810
0
        case USCRIPT_LISU:
811
0
            sRet = "lis"_ostr;
812
0
            break;
813
0
        case USCRIPT_NAKHI_GEBA:
814
0
            sRet = "nxq"_ostr;
815
0
            break;
816
0
        case USCRIPT_OLD_SOUTH_ARABIAN:
817
0
            sRet = "xsa"_ostr;
818
0
            break;
819
0
        case USCRIPT_BASSA_VAH:
820
0
            sRet = "bsq"_ostr;
821
0
            break;
822
0
        case USCRIPT_DUPLOYAN_SHORTAND:
823
0
            sRet = "fr"_ostr;
824
0
            break;
825
0
        case USCRIPT_ELBASAN:
826
0
            sRet = "sq"_ostr;
827
0
            break;
828
0
        case USCRIPT_KPELLE:
829
0
            sRet = "kpe"_ostr;
830
0
            break;
831
0
        case USCRIPT_LOMA:
832
0
            sRet = "lom"_ostr;
833
0
            break;
834
0
        case USCRIPT_MENDE:
835
0
            sRet = "men"_ostr;
836
0
            break;
837
0
        case USCRIPT_OLD_NORTH_ARABIAN:
838
0
            sRet = "xna"_ostr;
839
0
            break;
840
0
        case USCRIPT_SINDHI:
841
0
            sRet = "sd"_ostr;
842
0
            break;
843
0
        case USCRIPT_WARANG_CITI:
844
0
            sRet = "hoc"_ostr;
845
0
            break;
846
0
        case USCRIPT_AFAKA:
847
0
            sRet = "djk"_ostr;
848
0
            break;
849
0
        case USCRIPT_JURCHEN:
850
0
            sRet = "juc"_ostr;
851
0
            break;
852
0
        case USCRIPT_MRO:
853
0
            sRet = "cmr"_ostr;
854
0
            break;
855
0
        case USCRIPT_NUSHU:
856
0
            sRet = "mis-Nshu"_ostr;  // Uncoded with script
857
0
            break;
858
0
        case USCRIPT_SHARADA:
859
0
            sRet = "sa"_ostr;
860
0
            break;
861
0
        case USCRIPT_SORA_SOMPENG:
862
0
            sRet = "srb"_ostr;
863
0
            break;
864
0
        case USCRIPT_TAKRI:
865
0
            sRet = "doi"_ostr;
866
0
            break;
867
0
        case USCRIPT_TANGUT:
868
0
            sRet = "txg"_ostr;
869
0
            break;
870
0
        case USCRIPT_WOLEAI:
871
0
            sRet = "woe"_ostr;
872
0
            break;
873
0
        case USCRIPT_ANATOLIAN_HIEROGLYPHS:
874
0
            sRet = "hlu"_ostr;
875
0
            break;
876
0
        case USCRIPT_TIRHUTA:
877
0
            sRet = "mai"_ostr;
878
0
            break;
879
0
        case USCRIPT_CAUCASIAN_ALBANIAN:
880
0
            sRet = "xag"_ostr;
881
0
            break;
882
0
        case USCRIPT_MAHAJANI:
883
0
            sRet = "mwr"_ostr;
884
0
            break;
885
0
        case USCRIPT_AHOM:
886
0
            sRet = "aho"_ostr;
887
0
            break;
888
0
        case USCRIPT_HATRAN:
889
0
            sRet = "qly-Hatr"_ostr;
890
0
            break;
891
0
        case USCRIPT_MODI:
892
0
            sRet = "mr-Modi"_ostr;
893
0
            break;
894
0
        case USCRIPT_MULTANI:
895
0
            sRet = "skr-Mutl"_ostr;
896
0
            break;
897
0
        case USCRIPT_PAU_CIN_HAU:
898
0
            sRet = "ctd-Pauc"_ostr;
899
0
            break;
900
0
        case USCRIPT_SIDDHAM:
901
0
            sRet = "sa-Sidd"_ostr;
902
0
            break;
903
0
        case USCRIPT_ADLAM:
904
0
            sRet = "mis-Adlm"_ostr;   // Adlam for Fulani, no language code
905
0
            break;
906
0
        case USCRIPT_BHAIKSUKI:
907
0
            sRet = "mis-Bhks"_ostr;   // Bhaiksuki for some Buddhist texts, no language code
908
0
            break;
909
0
        case USCRIPT_MARCHEN:
910
0
            sRet = "bo-Marc"_ostr;
911
0
            break;
912
0
        case USCRIPT_NEWA:
913
0
            sRet = "new-Newa"_ostr;
914
0
            break;
915
0
        case USCRIPT_OSAGE:
916
0
            sRet = "osa-Osge"_ostr;
917
0
            break;
918
0
        case USCRIPT_HAN_WITH_BOPOMOFO:
919
0
            sRet = "mis-Hanb"_ostr;   // Han with Bopomofo, zh-Hanb ?
920
0
            break;
921
0
        case USCRIPT_SYMBOLS_EMOJI:
922
0
            sRet = "mis-Zsye"_ostr;   // Emoji variant
923
0
            break;
924
0
        case USCRIPT_MASARAM_GONDI:
925
0
            sRet = "gon-Gonm"_ostr;  // macro language code, could be wsg,esg,gno
926
0
            break;
927
0
        case USCRIPT_SOYOMBO:
928
0
            sRet = "mn-Soyo"_ostr;   // abugida to write Mongolian, also Tibetan and Sanskrit
929
0
            break;
930
0
        case USCRIPT_ZANABAZAR_SQUARE:
931
0
            sRet = "mn-Zanb"_ostr;   // abugida to write Mongolian
932
0
            break;
933
0
        case USCRIPT_DOGRA:
934
0
            sRet = "dgo"_ostr;       // Dogri proper
935
0
            break;
936
0
        case USCRIPT_GUNJALA_GONDI:
937
0
            sRet = "wsg"_ostr;       // Adilabad Gondi
938
0
            break;
939
0
        case USCRIPT_MAKASAR:
940
0
            sRet = "mak"_ostr;
941
0
            break;
942
0
        case USCRIPT_MEDEFAIDRIN:
943
0
            sRet = "dmf-Medf"_ostr;
944
0
            break;
945
0
        case USCRIPT_HANIFI_ROHINGYA:
946
0
            sRet = "rhg"_ostr;
947
0
            break;
948
0
        case USCRIPT_SOGDIAN:
949
0
        case USCRIPT_OLD_SOGDIAN:
950
0
            sRet = "sog"_ostr;
951
0
            break;
952
0
        case USCRIPT_ELYMAIC:
953
0
            sRet = "arc-Elym"_ostr;
954
0
            break;
955
0
        case USCRIPT_NYIAKENG_PUACHUE_HMONG:
956
0
            sRet = "hmn-Hmnp"_ostr;  // macrolanguage code
957
0
            break;
958
0
        case USCRIPT_NANDINAGARI:
959
0
            sRet = "sa-Nand"_ostr;
960
0
            break;
961
0
        case USCRIPT_WANCHO:
962
0
            sRet = "nnp-Wcho"_ostr;
963
0
            break;
964
0
        case USCRIPT_CHORASMIAN:
965
0
            sRet = "xco-Chrs"_ostr;
966
0
            break;
967
0
        case USCRIPT_DIVES_AKURU:
968
0
            sRet = "dv-Diak"_ostr;
969
0
            break;
970
0
        case USCRIPT_KHITAN_SMALL_SCRIPT:
971
0
            sRet = "zkt-Kits"_ostr;
972
0
            break;
973
0
        case USCRIPT_YEZIDI:
974
0
            sRet = "kmr-Yezi"_ostr;
975
0
            break;
976
0
#if (U_ICU_VERSION_MAJOR_NUM >= 70)
977
0
        case USCRIPT_CYPRO_MINOAN:
978
0
            sRet = "mis-Cpmn"_ostr;  // Uncoded with script
979
0
            break;
980
0
        case USCRIPT_OLD_UYGHUR:
981
0
            sRet = "oui-Ougr"_ostr;
982
0
            break;
983
0
        case USCRIPT_TANGSA:
984
0
            sRet = "nst-Tnsa"_ostr;
985
0
            break;
986
0
        case USCRIPT_TOTO:
987
0
            sRet = "txo-Toto"_ostr;
988
0
            break;
989
0
        case USCRIPT_VITHKUQI:
990
0
            sRet = "sq-Vith"_ostr;   // macrolanguage code
991
0
            break;
992
0
#endif
993
0
#if (U_ICU_VERSION_MAJOR_NUM >= 72)
994
0
        case USCRIPT_KAWI:
995
0
            sRet = "mis-Kawi"_ostr;  // Uncoded with script
996
0
            break;
997
0
        case USCRIPT_NAG_MUNDARI:
998
0
            sRet = "unr-Nagm"_ostr;
999
0
            break;
1000
0
#endif
1001
0
#if (U_ICU_VERSION_MAJOR_NUM >= 75)
1002
0
        case USCRIPT_ARABIC_NASTALIQ:
1003
0
            sRet = "fa-Aran"_ostr;
1004
0
            break;
1005
0
#endif
1006
0
#if (U_ICU_VERSION_MAJOR_NUM >= 76)
1007
0
        case USCRIPT_GARAY:
1008
0
            sRet = "wo-Gara"_ostr;
1009
0
            break;
1010
0
        case USCRIPT_GURUNG_KHEMA:
1011
0
            sRet = "gvr-Gukh"_ostr;
1012
0
            break;
1013
0
        case USCRIPT_KIRAT_RAI:
1014
0
            sRet = "bap-Krai"_ostr;
1015
0
            break;
1016
0
        case USCRIPT_OL_ONAL:
1017
0
            sRet = "unr-Onao"_ostr;
1018
0
            break;
1019
0
        case USCRIPT_SUNUWAR:
1020
0
            sRet = "suz-Sunu"_ostr;
1021
0
            break;
1022
0
        case USCRIPT_TODHRI:
1023
0
            sRet = "sq-Todr"_ostr;
1024
0
            break;
1025
0
        case USCRIPT_TULU_TIGALARI:
1026
0
            sRet = "sa-Tutg"_ostr;
1027
0
            break;
1028
0
#endif
1029
0
#if (U_ICU_VERSION_MAJOR_NUM >= 78)
1030
0
        case USCRIPT_BERIA_ERFE:
1031
0
            sRet = "zag-Berf"_ostr;
1032
0
            break;
1033
0
        case USCRIPT_SIDETIC:
1034
0
            sRet = "xsd-Sidt"_ostr;
1035
0
            break;
1036
0
        case USCRIPT_TAI_YO:
1037
0
            sRet = "tyj-Tayo"_ostr;
1038
0
            break;
1039
0
        case USCRIPT_TOLONG_SIKI:
1040
0
            sRet = "kru-Tols"_ostr;
1041
0
            break;
1042
0
        case USCRIPT_TRADITIONAL_HAN_WITH_LATIN:
1043
0
            sRet = "zh-Hntl"_ostr;
1044
0
            break;
1045
0
#endif
1046
0
#if (U_ICU_VERSION_MAJOR_NUM >= 79)
1047
0
        case USCRIPT_PROTO_CUNEIFORM:
1048
0
            sRet = "mis-Pcun"_ostr;
1049
0
            break;
1050
0
        case USCRIPT_SEAL:
1051
0
            sRet = "zh-Zslh"_ostr;
1052
0
            break;
1053
0
#endif
1054
0
    }
1055
0
    return sRet;
1056
0
}
1057
1058
//Format a number as a percentage according to the rules of the given
1059
//language, e.g. 100 -> "100%" for en-US vs "100 %" for de-DE
1060
OUString unicode::formatPercent(double dNumber,
1061
    const LanguageTag &rLangTag)
1062
0
{
1063
    // get a currency formatter for this locale ID
1064
0
    UErrorCode errorCode=U_ZERO_ERROR;
1065
1066
0
    LanguageTag aLangTag(rLangTag);
1067
1068
    // As of CLDR Version 24 these languages were not listed as using spacing
1069
    // between number and % but are reported as such by our l10n groups
1070
    // http://www.unicode.org/cldr/charts/24/by_type/numbers.number_formatting_patterns.html
1071
    // so format using French which has the desired rules
1072
0
    if (aLangTag.getLanguage() == "es" || aLangTag.getLanguage() == "sl")
1073
0
        aLangTag.reset(u"fr-FR"_ustr);
1074
1075
0
    icu::Locale aLocale = LanguageTagIcu::getIcuLocale(aLangTag);
1076
1077
0
    std::unique_ptr<icu::NumberFormat> xF(
1078
0
        icu::NumberFormat::createPercentInstance(aLocale, errorCode));
1079
0
    if(U_FAILURE(errorCode))
1080
0
    {
1081
0
        SAL_WARN("i18n", "icu::NumberFormat::createPercentInstance failed");
1082
0
        return OUString::number(dNumber) + "%";
1083
0
    }
1084
1085
0
    icu::UnicodeString output;
1086
0
    xF->format(dNumber/100, output);
1087
0
    OUString aRet(reinterpret_cast<const sal_Unicode *>(output.getBuffer()),
1088
0
        output.length());
1089
0
    if (rLangTag.getLanguage() == "de")
1090
0
    {
1091
        //narrow no-break space instead of (normal) no-break space
1092
0
        return aRet.replace(0x00A0, 0x202F);
1093
0
    }
1094
0
    return aRet;
1095
0
}
1096
1097
bool ToggleUnicodeCodepoint::AllowMoreInput(sal_uInt32 uChar)
1098
0
{
1099
0
    assert(!mbInputEnded);
1100
1101
0
    if (uChar == 0)
1102
0
        return false;
1103
1104
    //arbitrarily chosen maximum length allowed - normal max usage would be around 30.
1105
0
    if( maInput.getLength() > 255 )
1106
0
        return false;
1107
1108
0
    bool bPreventNonHex = false;
1109
0
    if( maInput.indexOf("U+") != -1 )
1110
0
        bPreventNonHex = true;
1111
1112
0
    switch ( unicode::getUnicodeType(uChar) )
1113
0
    {
1114
0
        case css::i18n::UnicodeType::SURROGATE:
1115
0
            if (bPreventNonHex || mbIsHexString)
1116
0
            {
1117
0
                return false;
1118
0
            }
1119
1120
0
            if( rtl::isLowSurrogate(uChar) && maUtf16.isEmpty() && maInput.isEmpty()  )
1121
0
            {
1122
0
                maUtf16.append(sal_Unicode(uChar));
1123
0
                return true;
1124
0
            }
1125
0
            if( rtl::isHighSurrogate(uChar) && maInput.isEmpty() )
1126
0
                maUtf16.insert(0, sal_Unicode(uChar));
1127
0
            if (maUtf16.getLength() == 2)
1128
0
            {
1129
0
                assert(rtl::isHighSurrogate(maUtf16[0]) && rtl::isLowSurrogate(maUtf16[1]));
1130
                // The resulting codepoint may itself be combining, so may allow more
1131
0
                sal_uInt32 nUCS4 = rtl::combineSurrogates(maUtf16[0], maUtf16[1]);
1132
0
                maUtf16.setLength(0);
1133
0
                return AllowMoreInput(nUCS4);
1134
0
            }
1135
            // unexpected order of high/low, so don't accept more
1136
0
            if( !maUtf16.isEmpty() )
1137
0
                maInput.append(maUtf16);
1138
0
            if( !maCombining.isEmpty() )
1139
0
                maInput.append(maCombining);
1140
0
            return false;
1141
1142
0
        case css::i18n::UnicodeType::NON_SPACING_MARK:
1143
0
        case css::i18n::UnicodeType::COMBINING_SPACING_MARK:
1144
0
            if (bPreventNonHex || mbIsHexString)
1145
0
            {
1146
0
                return false;
1147
0
            }
1148
1149
            //extreme edge case: already invalid high/low surrogates with preceding combining chars, and now an extra combining mark.
1150
0
            if( !maUtf16.isEmpty() )
1151
0
            {
1152
0
                maInput = maUtf16;
1153
0
                if( !maCombining.isEmpty() )
1154
0
                    maInput.append(maCombining);
1155
0
                return false;
1156
0
            }
1157
0
            maCombining.insertUtf32(0, uChar);
1158
0
            break;
1159
1160
0
        default:
1161
            //extreme edge case: already invalid high/low surrogates with preceding combining chars, and now an extra character.
1162
0
            if( !maUtf16.isEmpty() )
1163
0
            {
1164
0
                maInput = maUtf16;
1165
0
                if( !maCombining.isEmpty() )
1166
0
                    maInput.append(maCombining);
1167
0
                return false;
1168
0
            }
1169
1170
0
            if( !maCombining.isEmpty() )
1171
0
            {
1172
0
                maCombining.insertUtf32(0, uChar);
1173
0
                maInput = maCombining;
1174
0
                return false;
1175
0
            }
1176
1177
            // 0 - 1f are control characters.  Do not process those.
1178
0
            if( uChar < 0x20 )
1179
0
            {
1180
0
                return false;
1181
0
            }
1182
1183
0
            switch( uChar )
1184
0
            {
1185
0
                case 'u':
1186
0
                case 'U':
1187
                    // U+ notation found.  Continue looking for another one.
1188
0
                    if( mbRequiresU )
1189
0
                    {
1190
0
                        mbRequiresU = false;
1191
0
                        maInput.insert(0,"U+");
1192
0
                    }
1193
                    // treat as a normal character
1194
0
                    else
1195
0
                    {
1196
0
                        if( !bPreventNonHex )
1197
0
                            maInput.insertUtf32(0, uChar);
1198
0
                        return false;
1199
0
                    }
1200
0
                    break;
1201
0
                case '+':
1202
                    // + already found: skip when not U, or edge case of +U+xxxx
1203
0
                    if( mbRequiresU || (maInput.indexOf("U+") == 0) )
1204
0
                        return false;
1205
                    // hex chars followed by '+' - now require a 'U'
1206
0
                    else if ( !maInput.isEmpty() )
1207
0
                        mbRequiresU = true;
1208
                    // treat as a normal character
1209
0
                    else
1210
0
                    {
1211
0
                        if( !bPreventNonHex )
1212
0
                            maInput.insertUtf32(0, uChar);
1213
0
                        return false;
1214
0
                    }
1215
0
                    break;
1216
0
                default:
1217
                    // + already found. Since not U, cancel further input
1218
0
                    if( mbRequiresU )
1219
0
                        return false;
1220
                    // maximum digits per notation is 8: only one notation
1221
0
                    else if( maInput.indexOf("U+") == -1 && maInput.getLength() == 8 )
1222
0
                        return false;
1223
                    // maximum digits per notation is 8: previous notation found
1224
0
                    else if( maInput.indexOf("U+") == 8 )
1225
0
                        return false;
1226
                    // a hex character. Add to string.
1227
0
                    else if( rtl::isAsciiHexDigit(uChar) )
1228
0
                    {
1229
0
                        mbIsHexString = true;
1230
0
                        maInput.insertUtf32(0, uChar);
1231
0
                    }
1232
                    // not a hex character: stop input. keep if it is the first input provided
1233
0
                    else
1234
0
                    {
1235
0
                        if( maInput.isEmpty() )
1236
0
                            maInput.insertUtf32(0, uChar);
1237
0
                        return false;
1238
0
                    }
1239
0
            }
1240
0
    }
1241
0
    return true;
1242
0
}
1243
1244
OUString ToggleUnicodeCodepoint::StringToReplace()
1245
0
{
1246
    // this function potentially modifies the input string. No more addition of characters
1247
#ifndef NDEBUG
1248
    mbInputEnded = true;
1249
#endif
1250
1251
0
    if( maInput.isEmpty() )
1252
0
    {
1253
        //edge case - input finished with incomplete low surrogate or combining characters without a base
1254
0
        if (!maUtf16.isEmpty())
1255
0
            maInput = maUtf16;
1256
0
        if (!maCombining.isEmpty())
1257
0
            maInput.append(maCombining);
1258
0
        return maInput.toString();
1259
0
    }
1260
1261
0
    if( !mbIsHexString )
1262
0
        return maInput.toString();
1263
1264
    //validate unicode notation.
1265
0
    OUString sIn;
1266
0
    sal_uInt32 nUnicode = 0;
1267
0
    sal_Int32 nUPlus = maInput.indexOf("U+");
1268
    //if U+ notation used, strip off all extra chars added not in U+ notation
1269
0
    if( nUPlus != -1 )
1270
0
    {
1271
0
        maInput.remove(0, nUPlus);
1272
0
        sIn = maInput.copy(2).makeStringAndClear();
1273
0
        nUPlus = sIn.indexOf("U+");
1274
0
    }
1275
0
    else
1276
0
        sIn = maInput.toString();
1277
0
    while( nUPlus != -1 )
1278
0
    {
1279
0
        nUnicode = o3tl::toUInt32(sIn.subView(0, nUPlus), 16);
1280
        //prevent creating control characters or invalid Unicode values
1281
0
        if( !rtl::isUnicodeCodePoint(nUnicode) || nUnicode < 0x20  )
1282
0
            maInput = sIn.subView(nUPlus);
1283
0
        sIn = sIn.copy(nUPlus+2);
1284
0
        nUPlus =  sIn.indexOf("U+");
1285
0
    }
1286
1287
0
    nUnicode = sIn.toUInt32(16);
1288
0
    if( !rtl::isUnicodeCodePoint(nUnicode) || nUnicode < 0x20 )
1289
0
       maInput.truncate().append( sIn[sIn.getLength()-1] );
1290
0
    return maInput.toString();
1291
0
}
1292
1293
OUString ToggleUnicodeCodepoint::ReplacementString()
1294
0
{
1295
0
    OUString sIn = StringToReplace();
1296
0
    OUStringBuffer output = "";
1297
0
    sal_Int32 nUPlus = sIn.indexOf("U+");
1298
    // convert from hex notation to glyph
1299
0
    if( nUPlus != -1 || (sIn.getLength() > 1 && mbIsHexString) )
1300
0
    {
1301
0
        sal_uInt32 nUnicode = 0;
1302
0
        if( nUPlus == 0)
1303
0
        {
1304
0
            sIn = sIn.copy(2);
1305
0
            nUPlus = sIn.indexOf("U+");
1306
0
        }
1307
0
        while( nUPlus > 0 )
1308
0
        {
1309
0
            nUnicode = o3tl::toUInt32(sIn.subView(0, nUPlus), 16);
1310
0
            output.appendUtf32( nUnicode );
1311
1312
0
            sIn = sIn.copy(nUPlus+2);
1313
0
            nUPlus = sIn.indexOf("U+");
1314
0
        }
1315
0
        nUnicode = sIn.toUInt32(16);
1316
0
        output.appendUtf32( nUnicode );
1317
0
    }
1318
    // convert from glyph to hex notation
1319
0
    else
1320
0
    {
1321
0
        sal_Int32 nPos = 0;
1322
0
        while( nPos < sIn.getLength() )
1323
0
        {
1324
0
            OUStringBuffer aTmp = OUString::number(sIn.iterateCodePoints(&nPos),16);
1325
            //pad with zeros - minimum length of 4.
1326
0
            for( sal_Int32 i = 4 - aTmp.getLength(); i > 0; --i )
1327
0
                aTmp.insert( 0,"0" );
1328
0
            output.append( "U+" + aTmp );
1329
0
        }
1330
0
    }
1331
0
    return output.makeStringAndClear();
1332
0
}
1333
1334
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */