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 <defaultnumberingprovider.hxx>
21 #include <transliterationImpl.hxx>
22 #include <com/sun/star/i18n/NativeNumberMode.hpp>
23 #include <com/sun/star/lang/IllegalArgumentException.hpp>
24 #include <com/sun/star/style/NumberingType.hpp>
25 #include <com/sun/star/beans/PropertyValue.hpp>
26 #include <com/sun/star/configuration/theDefaultProvider.hpp>
27 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
28 #include <osl/diagnose.h>
29 #include <rtl/ref.hxx>
30 #include <localedata.hxx>
31 #include <nativenumbersupplier.hxx>
32 #include <string.h>
33 #include <comphelper/propertysequence.hxx>
34 #include <cppuhelper/supportsservice.hxx>
35 #include <officecfg/Office/Common.hxx>
36 
37 // Cyrillic upper case
38 #define C_CYR_A "\xD0\x90"
39 #define C_CYR_B "\xD0\x91"
40 // Cyrillic lower case
41 #define S_CYR_A "\xD0\xB0"
42 #define S_CYR_B "\xD0\xB1"
43 
44 //Greek upper case
45 #define C_GR_A "\xCE\x91"
46 #define C_GR_B "\xCE\x92"
47 //Greek lower case
48 #define S_GR_A "\xCE\xB1"
49 #define S_GR_B "\xCE\xB2"
50 
51 //Hebrew
52 #define S_HE_ALEPH "\xD7\x90"
53 #define S_HE_YOD "\xD7\x99"
54 #define S_HE_QOF "\xD7\xA7"
55 
56 //Arabic-Indic
57 #define S_AR_ONE "\xd9\xa1"
58 #define S_AR_TWO "\xd9\xa2"
59 #define S_AR_THREE "\xd9\xa3"
60 
61 // East Arabic-Indic
62 #define S_FA_ONE "\xDB\xB1"
63 #define S_FA_TWO "\xDB\xB2"
64 #define S_FA_THREE "\xDB\xB3"
65 
66 // Indic Devanagari
67 #define S_HI_ONE "\xE0\xA5\xA7"
68 #define S_HI_TWO "\xE0\xA5\xA8"
69 #define S_HI_THREE "\xE0\xA5\xA9"
70 
71 // Chicago footnote symbols
72 #define S_DAGGER "\xE2\x80\xA0"
73 #define S_DBL_DAGGER "\xE2\x80\xA1"
74 #define S_SECTION "\xC2\xA7"
75 
76 #include <sal/macros.h>
77 #include <rtl/ustring.hxx>
78 #include <rtl/ustrbuf.hxx>
79 
80 #include <bullet.h>
81 
82 using namespace com::sun::star;
83 using namespace com::sun::star::uno;
84 using namespace ::com::sun::star::i18n;
85 using namespace com::sun::star::lang;
86 
87 namespace i18npool {
88 
89 const sal_Unicode table_Alphabet_ar[] = {
90     0x0623, 0x0628, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E,
91     0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635,
92     0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x0642,
93     0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649
94 };
95 
96 const sal_Unicode table_Alphabet_ar_abjad[] = {
97     0x0627, 0x0628, 0x062c, 0x062f, 0x0647, 0x0648, 0x0632, 0x062d,
98     0x0637, 0x064a, 0x0643, 0x0644, 0x0645, 0x0646, 0x0633, 0x0639,
99     0x0641, 0x0635, 0x0642, 0x0631, 0x0634, 0x062a, 0x062b, 0x062e,
100     0x0630, 0x0636, 0x0638, 0x063a
101 };
102 
103 const sal_Unicode table_Alphabet_th[] = {
104     0x0E01, 0x0E02, 0x0E04, 0x0E07,
105     0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F,
106     0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17,
107     0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F,
108     0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,
109     0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E
110 };
111 
112 const sal_Unicode table_Alphabet_he[] = {
113     0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
114     0x05D8, 0x05D9, 0x05DB, 0x05DC, 0x05DE, 0x05E0, 0x05E1, 0x05E2,
115     0x05E4, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA
116 };
117 
118 const sal_Unicode table_Alphabet_ne[] = {
119     0x0915, 0x0916, 0x0917, 0x0918, 0x0919, 0x091A, 0x091B, 0x091C,
120     0x091D, 0x091E, 0x091F, 0x0920, 0x0921, 0x0922, 0x0923, 0x0924,
121     0x0925, 0x0926, 0x0927, 0x0928, 0x092A, 0x092B, 0x092C, 0x092D,
122     0x092E, 0x092F, 0x0930, 0x0932, 0x0935, 0x0936, 0x0937, 0x0938,
123     0x0939
124 };
125 
126 const sal_Unicode table_Alphabet_km[] = {
127     0x1780, 0x1781, 0x1782, 0x1783, 0x1784, 0x1785, 0x1786, 0x1787,
128     0x1788, 0x1789, 0x178A, 0x178B, 0x178C, 0x178D, 0x178E, 0x178F,
129     0x1790, 0x1791, 0x1792, 0x1793, 0x1794, 0x1795, 0x1796, 0x1797,
130     0x1798, 0x1799, 0x179A, 0x179B, 0x179C, 0x179F,
131     0x17A0, 0x17A1, 0x17A2
132 };
133 
134 const sal_Unicode table_Alphabet_lo[] = {
135     0x0E81, 0x0E82, 0x0E84, 0x0E87, 0x0E88, 0x0E8A, 0x0E8D, 0x0E94,
136     0x0E95, 0x0E96, 0x0E97, 0x0E99, 0x0E9A, 0x0E9B, 0x0E9C,
137     0x0E9D, 0x0E9E, 0x0E9F, 0x0EA1, 0x0EA2, 0x0EA3, 0x0EA5, 0x0EA7,
138     0x0EAA, 0x0EAB, 0x0EAD, 0x0EAE, 0x0EAF, 0x0EAE, 0x0EDC, 0x0EDD
139 };
140 
141 const sal_Unicode table_Alphabet_dz[] = {
142     0x0F40, 0x0F41, 0x0F42, 0x0F44, 0x0F45, 0x0F46, 0x0F47, 0x0F49,
143     0x0F4F, 0x0F50, 0x0F51, 0x0F53, 0x0F54, 0x0F55, 0x0F56, 0x0F58,
144     0x0F59, 0x0F5A, 0x0F5B, 0x0F5D, 0x0F5E, 0x0F5F, 0x0F60, 0x0F61,
145     0x0F62, 0x0F63, 0x0F64, 0x0F66, 0x0F67, 0x0F68
146 };
147 
148 const sal_Unicode table_Alphabet_my[] = {
149     0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007,
150     0x1008,/*0x1009,*/0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F,
151     0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017,
152     0x1018, 0x1019, 0x101A, 0x101B, 0x101C, 0x101D, 0x101E, 0x101F,
153     0x1020, 0x1021
154 };
155 
156 // Bulgarian Cyrillic upper case letters
157 const sal_Unicode table_CyrillicUpperLetter_bg[] = {
158     0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418,
159     0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422,
160     0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042E,
161     0x042F
162 };
163 
164 // Bulgarian cyrillic lower case letters
165 const sal_Unicode table_CyrillicLowerLetter_bg[] = {
166     0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438,
167     0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442,
168     0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044E,
169     0x044F
170 };
171 
172 // Russian Cyrillic upper letters
173 const sal_Unicode table_CyrillicUpperLetter_ru[] = {
174     0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
175     0x0418, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420,
176     0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428,
177     0x0429, 0x042B, 0x042D, 0x042E, 0x042F
178 };
179 
180 // Russian cyrillic lower letters
181 const sal_Unicode table_CyrillicLowerLetter_ru[] = {
182     0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
183     0x0438, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x0440,
184     0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448,
185     0x0449, 0x044B, 0x044D, 0x044E, 0x044F
186 };
187 
188 // Serbian Cyrillic upper letters
189 const sal_Unicode table_CyrillicUpperLetter_sr[] = {
190     0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0402, 0x0415, 0x0416,
191     0x0417, 0x0418, 0x0408, 0x041A, 0x041B, 0x0409, 0x041C, 0x041D,
192     0x040A, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x040B, 0x0423,
193     0x0424, 0x0425, 0x0426, 0x0427, 0x040F, 0x0428
194 };
195 
196 // Serbian cyrillic lower letters
197 const sal_Unicode table_CyrillicLowerLetter_sr[] = {
198     0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0452, 0x0435, 0x0436,
199     0x0437, 0x0438, 0x0458, 0x043A, 0x043B, 0x0459, 0x043C, 0x043D,
200     0x045A, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x045B, 0x0443,
201     0x0444, 0x0445, 0x0446, 0x0447, 0x045F, 0x0448
202 };
203 
204 // Ukrainian Cyrillic upper letters
205 const sal_Unicode table_CyrillicUpperLetter_uk[] = {
206     0x0410, 0x0411, 0x0412, 0x0413, 0x0490, 0x0414, 0x0415, 0x0404,
207     0x0416, 0x0417, 0x0418, 0x0406, 0x0407, 0x0419, 0x041A, 0x041B,
208     0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423,
209     0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042C, 0x042E,
210     0x042F
211 };
212 
213 // Ukrainian cyrillic lower letters
214 const sal_Unicode table_CyrillicLowerLetter_uk[] = {
215     0x0430, 0x0431, 0x0432, 0x0433, 0x0491, 0x0434, 0x0435, 0x0454,
216     0x0436, 0x0437, 0x0438, 0x0456, 0x0457, 0x0439, 0x043A, 0x043B,
217     0x043C, 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443,
218     0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044C, 0x044E,
219     0x044F
220 };
221 
222 const sal_Unicode table_GreekUpperLetter[] = {
223     0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x03DB, 0x0396, 0x0397, 0x0398,
224     0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03DF,
225     0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03E0
226 };
227 
228 const sal_Unicode table_GreekLowerLetter[] = {
229     0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03DB, 0x03B6, 0x03B7, 0x03B8,
230     0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03DF,
231     0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03E1
232 };
233 
234 const sal_Unicode table_Alphabet_fa[] = {
235     0x0622, 0x0628, 0x067E, 0x062A, 0x062B, 0x062C, 0x0686, 0x062D,
236     0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0698, 0x0633, 0x0634,
237     0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x0640, 0x0641, 0x0642,
238     0x06A9, 0x06AF, 0x0644, 0x0645, 0x0646, 0x0648, 0x0647, 0x06CC
239 };
240 
241 const sal_Unicode upperLetter[] = {
242     0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
243     0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
244     0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A
245 };
246 
247 const sal_Unicode lowerLetter[] = {
248     0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
249     0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
250     0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A
251 };
252 
253 const sal_Unicode table_Chicago[] = {
254     0x002a, 0x2020, 0x2021, 0x00a7
255 };
256 
257 // Tables used for numbering in persian words
258 const sal_Unicode table_PersianWord_decade1[][7]={
259     {0},                                                 // 0
260     {0x06cc, 0x06a9, 0},                                 // 1
261     {0x062f, 0x0648, 0},                                 // 2
262     {0x0633, 0x0647, 0},                                 // 3
263     {0x0686, 0x0647, 0x0627, 0x0631, 0},                 // 4
264     {0x067e, 0x0646, 0x062c, 0},                         // 5
265     {0x0634, 0x0634, 0},                                 // 6
266     {0x0647, 0x0641, 0x062a, 0},                         // 7
267     {0x0647, 0x0634, 0x062a, 0},                         // 8
268     {0x0646, 0x0647, 0},                                 // 9
269     {0x062f, 0x0647, 0},                                 // 10
270     {0x06cc, 0x0627, 0x0632, 0x062f, 0x0647, 0},         // 11
271     {0x062f, 0x0648, 0x0627, 0x0632, 0x062f, 0x0647, 0}, // 12
272     {0x0633, 0x06cc, 0x0632, 0x062f, 0x0647, 0},         // 13
273     {0x0686, 0x0647, 0x0627, 0x0631, 0x062f, 0x0647, 0}, // 14
274     {0x067e, 0x0627, 0x0646, 0x0632, 0x062f, 0x0647, 0}, // 15
275     {0x0634, 0x0627, 0x0646, 0x0632, 0x062f, 0x0647, 0}, // 16
276     {0x0647, 0x0641, 0x062f, 0x0647, 0},                 // 17
277     {0x0647, 0x062c, 0x062f, 0x0647, 0},                 // 18
278     {0x0646, 0x0648, 0x0632, 0x062f, 0x0647, 0}          // 19
279 };
280 
281 const sal_Unicode table_PersianWord_decade2[][6]={
282     {0x0628, 0x06cc, 0x0633, 0x062a, 0},                 // 20
283     {0x0633, 0x06cc, 0},                                 // 30
284     {0x0686, 0x0647, 0x0644, 0},                         // 40
285     {0x067e, 0x0646, 0x062c, 0x0627, 0x0647, 0},         // 50
286     {0x0634, 0x0635, 0x062a, 0},                         // 60
287     {0x0647, 0x0641, 0x062a, 0x0627, 0x062f, 0},         // 70
288     {0x0647, 0x0634, 0x062a, 0x0627, 0x062f, 0},         // 80
289     {0x0646, 0x0648, 0x062f, 0}                          // 90
290 };
291 
292 const sal_Unicode table_PersianWord_decade3[][7]={
293     {0x0635, 0x062f, 0},                                 // 100
294     {0x062f, 0x0648, 0x06cc, 0x0633, 0x062a, 0},         // 200
295     {0x0633, 0x06cc, 0x0635, 0x062f, 0},                 // 300
296     {0x0686, 0x0647, 0x0627, 0x0631, 0x0635, 0x062f, 0}, // 400
297     {0x067e, 0x0627, 0x0646, 0x0635, 0x062f, 0},         // 500
298     {0x0634, 0x0635, 0x062f, 0},                         // 600
299     {0x0647, 0x0641, 0x062a, 0x0635, 0x062f, 0},         // 700
300     {0x0647, 0x0634, 0x062a, 0x0635, 0x062f, 0},         // 800
301     {0x0646, 0x0647, 0x0635, 0x062f, 0}                  // 900
302 };
303 
304 const sal_Unicode table_PersianWord_decadeX[][8]={
305     {0x0647, 0x0632, 0x0627, 0x0631, 0},                        // 1000
306     {0x0645, 0x06cc, 0x0644, 0x06cc, 0x0648, 0x0646, 0},        // 1000000
307     {0x0645, 0x06cc, 0x0644, 0x06cc, 0x0627, 0x0631, 0x062f, 0} // 1000000000
308 };
309 
310 const sal_Unicode table_KoreanLegalWord_decade1[][3] = {
311     {0xd558, 0xb098, 0}, // 1
312     {0xb458, 0},         // 2
313     {0xc14b, 0},         // 3
314     {0xb137, 0},         // 4
315     {0xb2e4, 0xc12f, 0}, // 5
316     {0xc5ec, 0xc12f, 0}, // 6
317     {0xc77c, 0xacf1, 0}, // 7
318     {0xc5ec, 0xb35f, 0}, // 8
319     {0xc544, 0xd649, 0}  // 9
320 };
321 
322 const sal_Unicode table_KoreanLegalWord_decade2[][3] = {
323     {0xc5f4, 0},          // 10
324     {0xc2a4, 0xbb3c, 0},  // 20
325     {0xc11c, 0xb978, 0},  // 30
326     {0xb9c8, 0xd754, 0},  // 40
327     {0xc270, 0},          // 50
328     {0xc608, 0xc21c, 0},  // 60
329     {0xc77c, 0xd754, 0},  // 70
330     {0xc5ec, 0xb4e0, 0},  // 80
331     {0xc544, 0xd754, 0}   // 90
332 };
333 
DefaultNumberingProvider(const Reference<XComponentContext> & rxContext)334 DefaultNumberingProvider::DefaultNumberingProvider( const Reference < XComponentContext >& rxContext ) : m_xContext(rxContext)
335 {
336 
337 }
338 
~DefaultNumberingProvider()339 DefaultNumberingProvider::~DefaultNumberingProvider()
340 {
341 }
342 
343 Sequence< Reference<container::XIndexAccess> >
getDefaultOutlineNumberings(const Locale & rLocale)344 DefaultNumberingProvider::getDefaultOutlineNumberings(const Locale& rLocale )
345 {
346      return LocaleDataImpl::get()->getOutlineNumberingLevels( rLocale );
347 }
348 
349 Sequence< Sequence<beans::PropertyValue> >
getDefaultContinuousNumberingLevels(const Locale & rLocale)350 DefaultNumberingProvider::getDefaultContinuousNumberingLevels( const Locale& rLocale )
351 {
352      return LocaleDataImpl::get()->getContinuousNumberingLevels( rLocale );
353 }
354 
toRoman(sal_Int32 n)355 static OUString toRoman( sal_Int32 n )
356 {
357 
358 //              i, ii, iii, iv, v, vi, vii, vii, viii, ix
359 //                                                      (Dummy),1000,500,100,50,10,5,1
360     static const char coRomanArr[] = "MDCLXVI--";       // +2 Dummy entries !!
361     const char* cRomanStr = coRomanArr;
362     sal_uInt16 nMask = 1000;
363     sal_uInt32 nOver1000 = n / nMask;
364     n -= ( nOver1000 * nMask );
365 
366     OUStringBuffer sTmp;
367     while(nOver1000--)
368         sTmp.append(*coRomanArr);
369 
370     while( nMask )
371     {
372         sal_uInt8 nNumber = sal_uInt8( n / nMask );
373         sal_uInt8 nDiff = 1;
374         n %= nMask;
375 
376         if( 5 < nNumber )
377         {
378             if( nNumber < 9 )
379                 sTmp.append(*(cRomanStr-1));
380             ++nDiff;
381             nNumber -= 5;
382         }
383         switch( nNumber )
384         {
385             case 3: sTmp.append(*cRomanStr);           [[fallthrough]];
386             case 2: sTmp.append(*cRomanStr);           [[fallthrough]];
387             case 1: sTmp.append(*cRomanStr);           break;
388             case 4: sTmp.append(OUStringChar(*cRomanStr) + OUStringChar(*(cRomanStr-nDiff))); break;
389             case 5: sTmp.append(*(cRomanStr-nDiff));   break;
390         }
391 
392         nMask /= 10;                    // to the next decade
393         cRomanStr += 2;
394     }
395     return sTmp.makeStringAndClear();
396 }
397 
398 // not used:
399 
400 static
lcl_formatChars(const sal_Unicode table[],int tableSize,int n,OUString & s)401 void lcl_formatChars( const sal_Unicode table[], int tableSize, int n, OUString& s )
402 {
403      // string representation of n is appended to s.
404      // if A=='A' then 0=>A, 1=>B, ..., 25=>Z, 26=>AA, 27=>AB, ...
405      // if A=='a' then 0=>a, 1=>b, ..., 25=>z, 26=>aa, 27=>ab, ...
406 
407      if( n>=tableSize ) lcl_formatChars( table, tableSize, (n-tableSize)/tableSize, s );
408 
409      s += OUStringChar( table[ n % tableSize ] );
410 }
411 
412 static
lcl_formatChars1(const sal_Unicode table[],int tableSize,int n,OUString & s)413 void lcl_formatChars1( const sal_Unicode table[], int tableSize, int n, OUString& s )
414 {
415      // string representation of n is appended to s.
416      // if A=='A' then 0=>A, 1=>B, ..., 25=>Z, 26=>AA, 27=>BB, ...
417      // if A=='a' then 0=>a, 1=>b, ..., 25=>z, 26=>aa, 27=>bb, ...
418 
419      int repeat_count = n / tableSize + 1;
420 
421      for( int i=0; i<repeat_count; i++ )
422          s += OUStringChar( table[ n%tableSize ] );
423 }
424 
425 static
lcl_formatChars2(const sal_Unicode table_capital[],const sal_Unicode table_small[],int tableSize,int n,OUString & s)426 void lcl_formatChars2( const sal_Unicode table_capital[], const sal_Unicode table_small[], int tableSize, int n, OUString& s )
427 {
428      // string representation of n is appended to s.
429      // if A=='A' then 0=>A, 1=>B, ..., 25=>Z, 26=>Aa, 27=>Ab, ...
430 
431      if( n>=tableSize )
432      {
433           lcl_formatChars2( table_capital, table_small, tableSize, (n-tableSize)/tableSize, s );
434           s += OUStringChar( table_small[ n % tableSize ] );
435      } else
436           s += OUStringChar( table_capital[ n % tableSize ] );
437 }
438 
439 static
lcl_formatChars3(const sal_Unicode table_capital[],const sal_Unicode table_small[],int tableSize,int n,OUString & s)440 void lcl_formatChars3( const sal_Unicode table_capital[], const sal_Unicode table_small[], int tableSize, int n, OUString& s )
441 {
442      // string representation of n is appended to s.
443      // if A=='A' then 0=>A, 1=>B, ..., 25=>Z, 26=>Aa, 27=>Bb, ...
444 
445      int repeat_count = n / tableSize + 1;
446      s += OUStringChar( table_capital[ n%tableSize ] );
447 
448      for( int i=1; i<repeat_count; i++ )
449          s += OUStringChar( table_small[ n%tableSize ] );
450 }
451 
452 
453 /** Returns number's representation in persian words up to 999999999999
454     respectively limited by sal_Int32 >=0.
455     The caller assures that nNumber is not negative.
456 
457     @throws IllegalArgumentException
458     @throws RuntimeException
459  */
460 static
lcl_formatPersianWord(sal_Int32 nNumber,OUString & rsResult)461 void lcl_formatPersianWord( sal_Int32 nNumber, OUString& rsResult )
462 {
463     OUStringBuffer aTemp(64);
464     static constexpr OUStringLiteral asPersianWord_conjunction_data = u" \u0648 ";
465     OUString asPersianWord_conjunction( asPersianWord_conjunction_data );
466     unsigned char nSection = 0;
467 
468     while (int nPart = nNumber % 1000)
469     {
470         if (nSection)
471         {
472             if (nSection > SAL_N_ELEMENTS( table_PersianWord_decadeX))
473                 throw IllegalArgumentException();   // does not happen with sal_Int32
474             aTemp.insert( 0, table_PersianWord_decadeX[nSection-1] + asPersianWord_conjunction );
475         }
476 
477         unsigned int nDigit;
478         if ((nDigit = nPart % 100) < 20)
479         {
480             if (!aTemp.isEmpty())
481                 aTemp.insert( 0, u' ');
482             aTemp.insert( 0, table_PersianWord_decade1[nDigit]);
483         }
484         else
485         {
486             if ((nDigit = nPart % 10) != 0)
487             {
488                 if (!aTemp.isEmpty())
489                     aTemp.insert( 0, asPersianWord_conjunction);
490                 aTemp.insert( 0, table_PersianWord_decade1[nDigit]);
491             }
492             if ((nDigit = (nPart / 10) % 10) != 0)
493             {
494                 if (!aTemp.isEmpty())
495                     aTemp.insert( 0, asPersianWord_conjunction);
496                 aTemp.insert( 0, table_PersianWord_decade2[nDigit-2]);
497             }
498         }
499 
500         if ((nDigit = nPart / 100) != 0)
501         {
502             if (!aTemp.isEmpty())
503                 aTemp.insert( 0, asPersianWord_conjunction);
504             aTemp.insert( 0, table_PersianWord_decade3[nDigit-1]);
505         }
506 
507         nNumber /= 1000;
508         nSection++;
509     }
510     rsResult += aTemp;
511 }
512 
lcl_formatKoreanLegalWord(sal_Int32 nNumber,OUString & rsResult)513 static void lcl_formatKoreanLegalWord(sal_Int32 nNumber, OUString& rsResult) {
514     OUStringBuffer aTemp(64);
515     int digit1 = nNumber % 10;
516     int digit2 = nNumber / 10;
517     if (digit1 > 0)
518         aTemp.insert(0, (table_KoreanLegalWord_decade1[digit1 - 1]));
519     if (digit2 > 0)
520         aTemp.insert(0, (table_KoreanLegalWord_decade2[digit2 - 1]));
521     rsResult += aTemp;
522 }
523 
524 // Greek Letter Numbering
525 
526 // KERAIA separates numerals from other text
527 #define STIGMA        u'\x03DB'
528 #define LEFT_KERAIA   u'\x0375'
529 #define MYRIAD_SYM    u'\x039C'
530 #define DOT_SYM       u'.'
531 #define SIGMA_OFFSET  19
532 #define TAU_OFFSET    20
533 #define MYRIAD        10000
534 
535 /*
536 * Return the 1-999999 number's representation in the Greek numbering system.
537 * Adding a "left keraia" to represent numbers in the range 10000 ... 999999 is
538 * not orthodox, so it's better to use the myriad notation and call this method
539 * only for numbers up to 9999.
540 */
541 static
gr_smallNum(const sal_Unicode table[],int n)542 OUString gr_smallNum(const sal_Unicode table[], int n)
543 {
544     if (n > 9999)
545         throw IllegalArgumentException();
546 
547     int i = 0;
548     OUStringBuffer sb;
549     for (int v = n; v > 0; v /= 10, i++) {
550         int digit = v % 10;
551         if (digit == 0)
552             continue;
553 
554         sal_Unicode sign = table[(digit - 1) + 9 * (i % 3)];
555         if (sign == STIGMA) {
556             sb.insert(0, table[TAU_OFFSET]);
557             sb.insert(0, table[SIGMA_OFFSET]);
558         } else {
559             sb.insert(0, sign);
560         }
561 
562         if (i > 2)
563             sb.insert(0, LEFT_KERAIA);
564     }
565 
566     return sb.makeStringAndClear();
567 }
568 
569 static
lcl_formatCharsGR(const sal_Unicode table[],int n,OUString & s)570 void lcl_formatCharsGR(const sal_Unicode table[], int n, OUString& s )
571 {
572     OUStringBuffer sb;
573     int myriadPower = 2;
574 
575     for (int divisor = MYRIAD * MYRIAD; divisor > 1; divisor /= MYRIAD, myriadPower--) {
576         if (n > divisor - 1) {
577             /*
578              * Follow the Diophantus representation of:
579              *   A myriad sign, M(10000) as many times as the power
580              *   followed by the multiplier for the myriad
581              *   followed by a dot
582              *   followed by the rest
583              *   This is enough for 32-bit integers
584              */
585             for (int i = 0; i < myriadPower; i++)
586                 sb.append(MYRIAD_SYM);
587 
588             sb.append(gr_smallNum(table, n/divisor));
589             n %= divisor;
590 
591             if (n > 0)
592                 sb.append(DOT_SYM);
593         }
594     }
595     sb.append(gr_smallNum(table,n));
596 
597     s += sb;
598 }
599 
600 static
should_ignore(std::u16string_view s)601 bool should_ignore( std::u16string_view s )
602 {
603         // return true if blank or null
604         return s == u" " || (!s.empty() && s[0]==0);
605 }
606 
607 /**
608  * Turn nNumber into a string and pad the result to nLimit by inserting zero characters at the
609  * start.
610  */
lcl_formatArabicZero(sal_Int32 nNumber,sal_Int32 nLimit)611 static OUString lcl_formatArabicZero(sal_Int32 nNumber, sal_Int32 nLimit)
612 {
613     OUString aRet = OUString::number(nNumber);
614     sal_Int32 nDiff = nLimit - aRet.getLength();
615 
616     if (nDiff <= 0)
617     {
618         return aRet;
619     }
620 
621     OUStringBuffer aBuffer;
622     aBuffer.setLength(nDiff);
623     for (sal_Int32 i = 0; i < nDiff; ++i)
624     {
625         aBuffer[i] = '0';
626     }
627     aBuffer.append(aRet);
628     return aBuffer.makeStringAndClear();
629 }
630 
631 static
getPropertyByName(const Sequence<beans::PropertyValue> & aProperties,const char * name)632 Any getPropertyByName( const Sequence<beans::PropertyValue>& aProperties,
633                                                 const char* name )
634 {
635         auto pProp = std::find_if(aProperties.begin(), aProperties.end(),
636             [&name](const beans::PropertyValue& rProp) { return rProp.Name.equalsAscii(name); });
637         if (pProp != aProperties.end())
638             return pProp->Value;
639         throw IllegalArgumentException();
640 }
641 
642 //XNumberingFormatter
643 OUString
makeNumberingString(const Sequence<beans::PropertyValue> & aProperties,const Locale & aLocale)644 DefaultNumberingProvider::makeNumberingString( const Sequence<beans::PropertyValue>& aProperties,
645                                                const Locale& aLocale )
646 {
647      // the Sequence of PropertyValues is expected to have at least 4 elements:
648      // elt Name              Type             purpose
649 
650 
651      // 0.  "Prefix"          OUString
652      // 1.  "NumberingType"   sal_Int16        type of formatting from style::NumberingType (roman, arabic, etc)
653      // 2.  "Suffix"          OUString
654      // ... ...               ...
655      // n.  "Value"           sal_Int32        the number to be formatted
656      // example:
657      // given the Sequence { '(', NumberingType::ROMAN_UPPER, ')', ..., 7 }
658      // makeNumberingString() returns the string "(VII)".
659 
660      // Q: why is the type of numType sal_Int16 instead of style::NumberingType?
661      // A: an Any can't hold a style::NumberingType for some reason.
662         //      add.: style::NumberingType holds constants of type sal_Int16, it's not an enum type
663 
664      sal_Int16 natNum = 0;
665      sal_Int16 tableSize = 0;
666      const sal_Unicode *table = nullptr;     // initialize to avoid compiler warning
667      bool bRecycleSymbol = false;
668      OUString sNatNumParams;
669      Locale locale;
670 
671      OUString  prefix;
672      sal_Int16        numType = -1; // type of formatting from style::NumberingType (roman, arabic, etc)
673      OUString  suffix;
674      sal_Int32        number = -1; // the number that needs to be formatted.
675 
676 //     int nProperties = aProperties.getLength();
677 //     int last        = nProperties-1;
678 
679      for (auto const & prop : aProperties)
680      {
681          if (prop.Name == "Prefix")
682              prop.Value >>= prefix;
683          else if (prop.Name == "Suffix")
684              prop.Value >>= suffix;
685          else if (prop.Name == "NumberingType")
686              prop.Value >>= numType;
687          else if (prop.Name == "Value")
688              prop.Value >>= number;
689      }
690 
691      if( number <= 0 )
692           throw IllegalArgumentException();
693 
694      // start empty
695      OUString result;
696 
697      // append prefix
698      if( !should_ignore(prefix) ) result += prefix;
699 
700      // append formatted number
701      using namespace style::NumberingType;
702      switch( numType )
703      {
704           case CHARS_UPPER_LETTER:
705                lcl_formatChars( upperLetter, 26, number-1, result ); // 1=>A, 2=>B, ..., 26=>Z, 27=>AA, 28=>AB, ...
706                break;
707           case CHARS_LOWER_LETTER:
708                lcl_formatChars( lowerLetter, 26, number-1, result );
709                break;
710           case TEXT_NUMBER: // ordinal indicators (1st, 2nd, 3rd, ...)
711                natNum = NativeNumberMode::NATNUM12;
712                sNatNumParams = "capitalize ordinal-number";
713                locale = aLocale;
714                break;
715           case TEXT_CARDINAL: // cardinal number names (One, Two, Three, ...)
716                natNum = NativeNumberMode::NATNUM12;
717                sNatNumParams = "capitalize";
718                locale = aLocale;
719                break;
720           case TEXT_ORDINAL: // ordinal number names (First, Second, Third, ...)
721                natNum = NativeNumberMode::NATNUM12;
722                sNatNumParams = "capitalize ordinal";
723                locale = aLocale;
724                break;
725           case ROMAN_UPPER:
726                result += toRoman( number );
727                break;
728           case ROMAN_LOWER:
729                result += toRoman( number ).toAsciiLowerCase();
730                break;
731           case ARABIC:
732                result += OUString::number( number );
733                break;
734           case NUMBER_NONE:
735                return OUString(); // ignore prefix and suffix
736           case CHAR_SPECIAL:
737                // apparently, we're supposed to return an empty string in this case...
738                return OUString(); // ignore prefix and suffix
739           case PAGE_DESCRIPTOR:
740           case BITMAP:
741                OSL_ASSERT(false);
742                throw IllegalArgumentException();
743           case CHARS_UPPER_LETTER_N:
744                lcl_formatChars1( upperLetter, 26, number-1, result ); // 1=>A, 2=>B, ..., 26=>Z, 27=>AA, 28=>BB, ...
745                break;
746           case CHARS_LOWER_LETTER_N:
747                lcl_formatChars1( lowerLetter, 26,  number-1, result ); // 1=>A, 2=>B, ..., 26=>Z, 27=>AA, 28=>BB, ...
748                break;
749           case TRANSLITERATION:
750                try {
751                     const OUString &tmp = OUString::number( number );
752                     OUString transliteration;
753                     getPropertyByName(aProperties, "Transliteration") >>= transliteration;
754                     if ( !translit )
755                         translit = new TransliterationImpl(m_xContext);
756                     translit->loadModuleByImplName(transliteration, aLocale);
757                     result += translit->transliterateString2String(tmp, 0, tmp.getLength());
758                } catch (Exception& ) {
759                     // When transliteration property is missing, return default number (bug #101141#)
760                     result += OUString::number( number );
761                     // OSL_ASSERT(0);
762                     // throw IllegalArgumentException();
763                }
764                break;
765           case NATIVE_NUMBERING:
766                 natNum = NativeNumberMode::NATNUM1;
767                 locale = aLocale;
768                 break;
769           case FULLWIDTH_ARABIC:
770                 natNum = NativeNumberMode::NATNUM3;
771                 locale = aLocale;
772                 break;
773           case NUMBER_LOWER_ZH:
774                 natNum = NativeNumberMode::NATNUM12;
775                 locale.Language = "zh";
776                 break;
777           case NUMBER_UPPER_ZH:
778                 natNum = NativeNumberMode::NATNUM5;
779                 locale.Language = "zh";
780                 break;
781           case NUMBER_UPPER_ZH_TW:
782                 natNum = NativeNumberMode::NATNUM5;
783                 locale.Language = "zh";
784                 locale.Country = "TW";
785                 break;
786           case NUMBER_TRADITIONAL_JA:
787                 natNum = NativeNumberMode::NATNUM8;
788                 locale.Language = "ja";
789                 break;
790           case NUMBER_UPPER_KO:
791                 natNum = NativeNumberMode::NATNUM8;
792                 locale.Language = "ko";
793                 break;
794           case NUMBER_HANGUL_KO:
795                 natNum = NativeNumberMode::NATNUM11;
796                 locale.Language = "ko";
797                 break;
798           case NUMBER_DIGITAL_KO:
799               natNum = NativeNumberMode::NATNUM9;
800               locale.Language = "ko";
801               break;
802           case NUMBER_DIGITAL2_KO:
803               natNum = NativeNumberMode::NATNUM1;
804               locale.Language = "ko";
805               break;
806           case NUMBER_LEGAL_KO:
807               if (number < 1 || number >= 100)
808               {
809                   natNum = NativeNumberMode::NATNUM11;
810                   locale.Language = "ko";
811               }
812               else
813               {
814                   lcl_formatKoreanLegalWord(number, result);
815               }
816               break;
817 
818           case CIRCLE_NUMBER:
819               table = table_CircledNumber;
820               tableSize = SAL_N_ELEMENTS(table_CircledNumber);
821               break;
822           case TIAN_GAN_ZH:
823               table = table_TianGan_zh;
824               tableSize = SAL_N_ELEMENTS(table_TianGan_zh);
825               break;
826           case DI_ZI_ZH:
827               table = table_DiZi_zh;
828               tableSize = SAL_N_ELEMENTS(table_DiZi_zh);
829               break;
830           case AIU_FULLWIDTH_JA:
831               table = table_AIUFullWidth_ja_JP;
832               tableSize = SAL_N_ELEMENTS(table_AIUFullWidth_ja_JP);
833               bRecycleSymbol = true;
834               break;
835           case AIU_HALFWIDTH_JA:
836               table = table_AIUHalfWidth_ja_JP;
837               tableSize = SAL_N_ELEMENTS(table_AIUHalfWidth_ja_JP);
838               bRecycleSymbol = true;
839               break;
840           case IROHA_FULLWIDTH_JA:
841               table = table_IROHAFullWidth_ja_JP;
842               tableSize = SAL_N_ELEMENTS(table_IROHAFullWidth_ja_JP);
843               bRecycleSymbol = true;
844               break;
845           case IROHA_HALFWIDTH_JA:
846               table = table_IROHAHalfWidth_ja_JP;
847               tableSize = SAL_N_ELEMENTS(table_IROHAHalfWidth_ja_JP);
848               bRecycleSymbol = true;
849               break;
850           case HANGUL_JAMO_KO:
851               table = table_HangulJamo_ko;
852               tableSize = SAL_N_ELEMENTS(table_HangulJamo_ko);
853               bRecycleSymbol = true;
854               break;
855           case HANGUL_SYLLABLE_KO:
856               table = table_HangulSyllable_ko;
857               tableSize = SAL_N_ELEMENTS(table_HangulSyllable_ko);
858               bRecycleSymbol = true;
859               break;
860           case HANGUL_CIRCLED_JAMO_KO:
861               table = table_HangulCircledJamo_ko;
862               tableSize = SAL_N_ELEMENTS(table_HangulCircledJamo_ko);
863               bRecycleSymbol = true;
864               break;
865           case HANGUL_CIRCLED_SYLLABLE_KO:
866               table = table_HangulCircledSyllable_ko;
867               tableSize = SAL_N_ELEMENTS(table_HangulCircledSyllable_ko);
868               bRecycleSymbol = true;
869               break;
870           case CHARS_ARABIC:
871               lcl_formatChars(table_Alphabet_ar, SAL_N_ELEMENTS(table_Alphabet_ar), number - 1, result);
872               break;
873           case CHARS_ARABIC_ABJAD:
874               lcl_formatChars(table_Alphabet_ar_abjad, SAL_N_ELEMENTS(table_Alphabet_ar_abjad), number - 1, result);
875               break;
876           case NUMBER_ARABIC_INDIC:
877               natNum = NativeNumberMode::NATNUM1;
878               locale.Language = "ar";
879               break;
880           case NUMBER_EAST_ARABIC_INDIC:
881               natNum = NativeNumberMode::NATNUM1;
882               locale.Language = "fa";
883               break;
884           case NUMBER_INDIC_DEVANAGARI:
885               natNum = NativeNumberMode::NATNUM1;
886               locale.Language = "hi";
887               break;
888           case CHARS_THAI:
889               lcl_formatChars(table_Alphabet_th, SAL_N_ELEMENTS(table_Alphabet_th), number - 1, result);
890               break;
891           case CHARS_HEBREW:
892               lcl_formatChars(table_Alphabet_he, SAL_N_ELEMENTS(table_Alphabet_he), number - 1, result);
893               break;
894           case NUMBER_HEBREW:
895               natNum = NativeNumberMode::NATNUM1;
896               locale.Language = "he";
897               break;
898           case CHARS_NEPALI:
899               lcl_formatChars(table_Alphabet_ne, SAL_N_ELEMENTS(table_Alphabet_ne), number - 1, result);
900               break;
901           case CHARS_KHMER:
902               lcl_formatChars(table_Alphabet_km, SAL_N_ELEMENTS(table_Alphabet_km), number - 1, result);
903               break;
904           case CHARS_LAO:
905               lcl_formatChars(table_Alphabet_lo, SAL_N_ELEMENTS(table_Alphabet_lo), number - 1, result);
906               break;
907           case CHARS_MYANMAR:
908               lcl_formatChars(table_Alphabet_my, SAL_N_ELEMENTS(table_Alphabet_my), number - 1, result);
909               break;
910          case CHARS_TIBETAN:
911               lcl_formatChars(table_Alphabet_dz, SAL_N_ELEMENTS(table_Alphabet_dz), number - 1, result);
912               break;
913          case CHARS_CYRILLIC_UPPER_LETTER_BG:
914               lcl_formatChars2( table_CyrillicUpperLetter_bg,
915                       table_CyrillicLowerLetter_bg,
916                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_bg), number-1,
917                       result); // 1=>a, 2=>b, ..., 28=>z, 29=>Aa, 30=>Ab, ...
918               break;
919          case CHARS_CYRILLIC_LOWER_LETTER_BG:
920               lcl_formatChars( table_CyrillicLowerLetter_bg,
921                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_bg), number-1,
922                       result); // 1=>a, 2=>b, ..., 28=>z, 29=>aa, 30=>ab, ...
923               break;
924          case CHARS_CYRILLIC_UPPER_LETTER_N_BG:
925               lcl_formatChars3( table_CyrillicUpperLetter_bg,
926                       table_CyrillicLowerLetter_bg,
927                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_bg), number-1,
928                       result); // 1=>a, 2=>b, ..., 28=>z, 29=>Aa, 30=>Bb, ...
929               break;
930          case CHARS_CYRILLIC_LOWER_LETTER_N_BG:
931               lcl_formatChars1( table_CyrillicLowerLetter_bg,
932                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_bg), number-1,
933                       result); // 1=>a, 2=>b, ..., 28=>z, 29=>aa, 30=>bb, ...
934               break;
935          case CHARS_CYRILLIC_UPPER_LETTER_RU:
936               lcl_formatChars2( table_CyrillicUpperLetter_ru,
937                       table_CyrillicLowerLetter_ru,
938                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_ru), number-1,
939                       result); // 1=>a, 2=>b, ..., 27=>z, 28=>Aa, 29=>Ab, ...
940               break;
941          case CHARS_CYRILLIC_LOWER_LETTER_RU:
942               lcl_formatChars( table_CyrillicLowerLetter_ru,
943                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_ru), number-1,
944                       result); // 1=>a, 2=>b, ..., 27=>z, 28=>aa, 29=>ab, ...
945               break;
946          case CHARS_CYRILLIC_UPPER_LETTER_N_RU:
947               lcl_formatChars3( table_CyrillicUpperLetter_ru,
948                       table_CyrillicLowerLetter_ru,
949                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_ru), number-1,
950                       result); // 1=>a, 2=>b, ..., 27=>z, 28=>Aa, 29=>Bb, ...
951               break;
952          case CHARS_CYRILLIC_LOWER_LETTER_N_RU:
953               lcl_formatChars1( table_CyrillicLowerLetter_ru,
954                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_ru), number-1,
955                       result); // 1=>a, 2=>b, ..., 27=>z, 28=>aa, 29=>bb, ...
956               break;
957          case CHARS_CYRILLIC_UPPER_LETTER_SR:
958               lcl_formatChars2( table_CyrillicUpperLetter_sr,
959                       table_CyrillicLowerLetter_sr,
960                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_sr), number-1,
961                       result); // 1=>a, 2=>b, ..., 27=>z, 28=>Aa, 29=>Ab, ...
962               break;
963          case CHARS_CYRILLIC_LOWER_LETTER_SR:
964               lcl_formatChars( table_CyrillicLowerLetter_sr,
965                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_sr), number-1,
966                       result); // 1=>a, 2=>b, ..., 27=>z, 28=>aa, 29=>ab, ...
967               break;
968          case CHARS_CYRILLIC_UPPER_LETTER_N_SR:
969               lcl_formatChars3( table_CyrillicUpperLetter_sr,
970                       table_CyrillicLowerLetter_sr,
971                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_sr), number-1,
972                       result); // 1=>a, 2=>b, ..., 27=>z, 28=>Aa, 29=>Bb, ...
973               break;
974          case CHARS_CYRILLIC_LOWER_LETTER_N_SR:
975               lcl_formatChars1( table_CyrillicLowerLetter_sr,
976                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_sr), number-1,
977                       result); // 1=>a, 2=>b, ..., 27=>z, 28=>aa, 29=>bb, ...
978               break;
979 
980          case CHARS_CYRILLIC_UPPER_LETTER_UK:
981               lcl_formatChars2( table_CyrillicUpperLetter_uk,
982                       table_CyrillicLowerLetter_uk,
983                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_uk), number-1,
984                       result);
985               break;
986          case CHARS_CYRILLIC_LOWER_LETTER_UK:
987               lcl_formatChars( table_CyrillicLowerLetter_uk,
988                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_uk), number-1,
989                       result);
990               break;
991          case CHARS_CYRILLIC_UPPER_LETTER_N_UK:
992               lcl_formatChars3( table_CyrillicUpperLetter_uk,
993                       table_CyrillicLowerLetter_uk,
994                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_uk), number-1,
995                       result);
996               break;
997          case CHARS_CYRILLIC_LOWER_LETTER_N_UK:
998               lcl_formatChars1( table_CyrillicLowerLetter_uk,
999                       SAL_N_ELEMENTS(table_CyrillicLowerLetter_uk), number-1,
1000                       result);
1001               break;
1002 
1003           case CHARS_GREEK_LOWER_LETTER:
1004               lcl_formatCharsGR( table_GreekLowerLetter, number, result);
1005               break;
1006 
1007           case CHARS_GREEK_UPPER_LETTER:
1008               lcl_formatCharsGR( table_GreekUpperLetter, number, result);
1009               break;
1010 
1011           case CHARS_PERSIAN:
1012               lcl_formatChars(table_Alphabet_fa, SAL_N_ELEMENTS(table_Alphabet_fa), number - 1, result);
1013               break;
1014 
1015           case CHARS_PERSIAN_WORD:
1016               lcl_formatPersianWord(number, result);
1017               break;
1018 
1019           case SYMBOL_CHICAGO:
1020              lcl_formatChars1( table_Chicago, 4, number-1, result );  // *, +, |, S, **, ++, ...
1021              break;
1022 
1023           case ARABIC_ZERO:
1024                result += lcl_formatArabicZero(number, 2);
1025                break;
1026 
1027           case ARABIC_ZERO3:
1028                result += lcl_formatArabicZero(number, 3);
1029                break;
1030 
1031           case ARABIC_ZERO4:
1032                result += lcl_formatArabicZero(number, 4);
1033                break;
1034 
1035           case ARABIC_ZERO5:
1036                result += lcl_formatArabicZero(number, 5);
1037                break;
1038 
1039           case SZEKELY_ROVAS: // Old Hungarian
1040                natNum = NativeNumberMode::NATNUM12;
1041                locale.Language = "hu-Hung";
1042                break;
1043 
1044           default:
1045                OSL_ASSERT(false);
1046                throw IllegalArgumentException();
1047      }
1048 
1049      if (natNum) {
1050             if (!mxNatNum)
1051                 mxNatNum.set(new NativeNumberSupplierService);
1052             result += mxNatNum->getNativeNumberStringParams(OUString::number(number), locale,
1053                                                                  natNum, sNatNumParams);
1054      } else if (tableSize) {
1055             if ( number > tableSize && !bRecycleSymbol)
1056                 result += OUString::number( number);
1057             else
1058                 result += OUStringChar(table[--number % tableSize]);
1059      }
1060 
1061      // append suffix
1062      if( !should_ignore(suffix) ) result += suffix;
1063 
1064      return result;
1065 }
1066 
1067 #define LANG_ALL        (1 << 0)
1068 #define LANG_CJK        (1 << 1)
1069 #define LANG_CTL        (1 << 2)
1070 
1071 struct Supported_NumberingType
1072 {
1073     const char*  cSymbol;
1074     sal_Int16    nType;
1075     sal_Int16    langOption;
Supported_NumberingTypei18npool::Supported_NumberingType1076     Supported_NumberingType(sal_Int16 nType_, const char* pSymbol, sal_Int16 opt)
1077         : cSymbol(pSymbol), nType(nType_), langOption(opt) {}
1078 };
1079 const Supported_NumberingType aSupportedTypes[] =
1080 {
1081         {style::NumberingType::CHARS_UPPER_LETTER,      "A", LANG_ALL},
1082         {style::NumberingType::CHARS_LOWER_LETTER,      "a", LANG_ALL},
1083         {style::NumberingType::ROMAN_UPPER,                     "I", LANG_ALL},
1084         {style::NumberingType::ROMAN_LOWER,                     "i", LANG_ALL},
1085         {style::NumberingType::ARABIC,                          "1", LANG_ALL},
1086         {style::NumberingType::NUMBER_NONE,                     "''", LANG_ALL},
1087         {style::NumberingType::CHAR_SPECIAL,                    "Bullet", LANG_ALL},
1088         {style::NumberingType::PAGE_DESCRIPTOR,                 "Page", LANG_ALL},
1089         {style::NumberingType::BITMAP,                          "Bitmap", LANG_ALL},
1090         {style::NumberingType::SYMBOL_CHICAGO,          "*, " S_DAGGER ", " S_DBL_DAGGER ", " S_SECTION ", **, " S_DAGGER S_DAGGER ", ...", LANG_ALL},
1091         {style::NumberingType::TEXT_NUMBER,             "1st", LANG_ALL},
1092         {style::NumberingType::TEXT_CARDINAL,           "One", LANG_ALL},
1093         {style::NumberingType::TEXT_ORDINAL,            "First", LANG_ALL},
1094         {style::NumberingType::CHARS_UPPER_LETTER_N,    "AAA", LANG_ALL},
1095         {style::NumberingType::CHARS_LOWER_LETTER_N,    "aaa", LANG_ALL},
1096         {style::NumberingType::NATIVE_NUMBERING,        "Native Numbering", LANG_CJK|LANG_CTL},
1097         {style::NumberingType::FULLWIDTH_ARABIC,        nullptr, LANG_CJK},
1098         {style::NumberingType::CIRCLE_NUMBER,           nullptr, LANG_CJK},
1099         // The cSymbol is defined here for compatibility with files created by old releases.
1100         // Otherwise if nullptr, these 3 digits may change as NATNUM12 depends on 3rd-party lib.
1101         {style::NumberingType::NUMBER_LOWER_ZH,         "一, 二, 三, ...", LANG_CJK},
1102         {style::NumberingType::NUMBER_UPPER_ZH,         nullptr, LANG_CJK},
1103         {style::NumberingType::NUMBER_UPPER_ZH_TW,      nullptr, LANG_CJK},
1104         {style::NumberingType::TIAN_GAN_ZH,             nullptr, LANG_CJK},
1105         {style::NumberingType::DI_ZI_ZH,                nullptr, LANG_CJK},
1106         {style::NumberingType::NUMBER_TRADITIONAL_JA,   nullptr, LANG_CJK},
1107         {style::NumberingType::AIU_FULLWIDTH_JA,        nullptr, LANG_CJK},
1108         {style::NumberingType::AIU_HALFWIDTH_JA,        nullptr, LANG_CJK},
1109         {style::NumberingType::IROHA_FULLWIDTH_JA,      nullptr, LANG_CJK},
1110         {style::NumberingType::IROHA_HALFWIDTH_JA,      nullptr, LANG_CJK},
1111         {style::NumberingType::NUMBER_UPPER_KO,         nullptr, LANG_CJK},
1112         {style::NumberingType::NUMBER_HANGUL_KO,        nullptr, LANG_CJK},
1113         {style::NumberingType::HANGUL_JAMO_KO,          nullptr, LANG_CJK},
1114         {style::NumberingType::HANGUL_SYLLABLE_KO,      nullptr, LANG_CJK},
1115         {style::NumberingType::HANGUL_CIRCLED_JAMO_KO,  nullptr, LANG_CJK},
1116         {style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO,      nullptr, LANG_CJK},
1117         {style::NumberingType::NUMBER_LEGAL_KO,         nullptr, LANG_CJK},
1118         {style::NumberingType::NUMBER_DIGITAL_KO,       nullptr, LANG_CJK},
1119         {style::NumberingType::NUMBER_DIGITAL2_KO,      nullptr, LANG_CJK},
1120         {style::NumberingType::CHARS_ARABIC,    nullptr, LANG_CTL},
1121         {style::NumberingType::CHARS_ARABIC_ABJAD,   nullptr, LANG_CTL},
1122         {style::NumberingType::NUMBER_ARABIC_INDIC,    S_AR_ONE ", " S_AR_TWO ", " S_AR_THREE ", ...", LANG_CTL},
1123         {style::NumberingType::NUMBER_EAST_ARABIC_INDIC,    S_FA_ONE ", " S_FA_TWO ", " S_FA_THREE ", ...", LANG_CTL},
1124         {style::NumberingType::NUMBER_INDIC_DEVANAGARI,    S_HI_ONE ", " S_HI_TWO ", " S_HI_THREE ", ...", LANG_CTL},
1125         {style::NumberingType::CHARS_THAI,      nullptr, LANG_CTL},
1126         {style::NumberingType::CHARS_HEBREW,    nullptr, LANG_CTL},
1127         {style::NumberingType::NUMBER_HEBREW,    S_HE_ALEPH ", " S_HE_YOD ", " S_HE_QOF ", ...", LANG_CTL},
1128         {style::NumberingType::CHARS_NEPALI,    nullptr, LANG_CTL},
1129         {style::NumberingType::CHARS_KHMER,     nullptr, LANG_CTL},
1130         {style::NumberingType::CHARS_LAO,       nullptr, LANG_CTL},
1131         {style::NumberingType::CHARS_MYANMAR,   nullptr, LANG_CTL},
1132         {style::NumberingType::CHARS_TIBETAN,   nullptr, LANG_CTL},
1133         {style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_BG,   C_CYR_A ", " C_CYR_B ", .., " C_CYR_A S_CYR_A ", " C_CYR_A S_CYR_B ", ... (bg)", LANG_ALL},
1134         {style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_BG,   S_CYR_A ", " S_CYR_B ", .., " S_CYR_A S_CYR_A ", " S_CYR_A S_CYR_B ", ... (bg)", LANG_ALL},
1135         {style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_BG, C_CYR_A ", " C_CYR_B ", .., " C_CYR_A S_CYR_A ", " C_CYR_B S_CYR_B ", ... (bg)", LANG_ALL},
1136         {style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_BG, S_CYR_A ", " S_CYR_B ", .., " S_CYR_A S_CYR_A ", " S_CYR_B S_CYR_B ", ... (bg)", LANG_ALL},
1137         {style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_RU,   C_CYR_A ", " C_CYR_B ", .., " C_CYR_A S_CYR_A ", " C_CYR_A S_CYR_B ", ... (ru)", LANG_ALL},
1138         {style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_RU,   S_CYR_A ", " S_CYR_B ", .., " S_CYR_A S_CYR_A ", " S_CYR_A S_CYR_B ", ... (ru)", LANG_ALL},
1139         {style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_RU, C_CYR_A ", " C_CYR_B ", .., " C_CYR_A S_CYR_A ", " C_CYR_B S_CYR_B ", ... (ru)", LANG_ALL},
1140         {style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_RU, S_CYR_A ", " S_CYR_B ", .., " S_CYR_A S_CYR_A ", " S_CYR_B S_CYR_B ", ... (ru)", LANG_ALL},
1141         {style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_SR,   C_CYR_A ", " C_CYR_B ", .., " C_CYR_A S_CYR_A ", " C_CYR_A S_CYR_B ", ... (sr)", LANG_ALL},
1142         {style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_SR,   S_CYR_A ", " S_CYR_B ", .., " S_CYR_A S_CYR_A ", " S_CYR_A S_CYR_B ", ... (sr)", LANG_ALL},
1143         {style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_SR, C_CYR_A ", " C_CYR_B ", .., " C_CYR_A S_CYR_A ", " C_CYR_B S_CYR_B ", ... (sr)", LANG_ALL},
1144         {style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_SR, S_CYR_A ", " S_CYR_B ", .., " S_CYR_A S_CYR_A ", " S_CYR_B S_CYR_B ", ... (sr)", LANG_ALL},
1145         {style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_UK,   C_CYR_A ", " C_CYR_B ", .., " C_CYR_A S_CYR_A ", " C_CYR_A S_CYR_B ", ... (uk)", LANG_ALL},
1146         {style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_UK,   S_CYR_A ", " S_CYR_B ", .., " S_CYR_A S_CYR_A ", " S_CYR_A S_CYR_B ", ... (uk)", LANG_ALL},
1147         {style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_UK, C_CYR_A ", " C_CYR_B ", .., " C_CYR_A S_CYR_A ", " C_CYR_B S_CYR_B ", ... (uk)", LANG_ALL},
1148         {style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_UK, S_CYR_A ", " S_CYR_B ", .., " S_CYR_A S_CYR_A ", " S_CYR_B S_CYR_B ", ... (uk)", LANG_ALL},
1149         {style::NumberingType::CHARS_PERSIAN,        nullptr, LANG_CTL},
1150         {style::NumberingType::CHARS_PERSIAN_WORD,   nullptr, LANG_CTL},
1151         {style::NumberingType::SZEKELY_ROVAS,        nullptr, LANG_CTL},
1152         {style::NumberingType::CHARS_GREEK_UPPER_LETTER,   C_GR_A ", " C_GR_B ", ... (gr)", LANG_ALL},
1153         {style::NumberingType::CHARS_GREEK_LOWER_LETTER,   S_GR_A ", " S_GR_B ", ... (gr)", LANG_ALL},
1154         {style::NumberingType::ARABIC_ZERO, "01, 02, 03, ...", LANG_ALL},
1155         {style::NumberingType::ARABIC_ZERO3, "001, 002, 003, ...", LANG_ALL},
1156         {style::NumberingType::ARABIC_ZERO4, "0001, 0002, 0003, ...", LANG_ALL},
1157         {style::NumberingType::ARABIC_ZERO5, "00001, 00002, 00003, ...", LANG_ALL},
1158 };
1159 const sal_Int32 nSupported_NumberingTypes = SAL_N_ELEMENTS(aSupportedTypes);
1160 
makeNumberingIdentifier(sal_Int16 index)1161 OUString DefaultNumberingProvider::makeNumberingIdentifier(sal_Int16 index)
1162 {
1163     if (index < 0 || index >= nSupported_NumberingTypes)
1164         throw RuntimeException();
1165 
1166     if (aSupportedTypes[index].cSymbol)
1167         return OUString(aSupportedTypes[index].cSymbol, strlen(aSupportedTypes[index].cSymbol), RTL_TEXTENCODING_UTF8);
1168     else {
1169         OUStringBuffer result;
1170         Locale aLocale(u"en"_ustr, OUString(), OUString());
1171         Sequence<beans::PropertyValue> aProperties(2);
1172         auto aPropertiesRange = asNonConstRange(aProperties);
1173         aPropertiesRange[0].Name = "NumberingType";
1174         aPropertiesRange[0].Value <<= aSupportedTypes[index].nType;
1175         aPropertiesRange[1].Name = "Value";
1176         for (sal_Int32 j = 1; j <= 3; j++) {
1177             aPropertiesRange[1].Value <<= j;
1178             result.append( makeNumberingString( aProperties, aLocale ) + ", " );
1179         }
1180         result.append("...");
1181         // Make known duplicate generated identifiers unique.
1182         // Note this alone works only for newly added numberings, if duplicates
1183         // are in the wild further handling is needed when loading documents
1184         // and asking for numberings.
1185         switch (aSupportedTypes[index].nType)
1186         {
1187             case css::style::NumberingType::NUMBER_DIGITAL_KO:
1188                 // Duplicate of NUMBER_HANGUL_KO.
1189                 result.append(" (ko-x-digital)");
1190             break;
1191             case css::style::NumberingType::NUMBER_DIGITAL2_KO:
1192                 // Duplicate of NUMBER_LOWER_ZH.
1193                 result.append(" (ko)");
1194             break;
1195             default:
1196                 ; // nothing
1197         }
1198         return result.makeStringAndClear();
1199     }
1200 }
1201 
1202 bool
isScriptFlagEnabled(const OUString & aName)1203 DefaultNumberingProvider::isScriptFlagEnabled(const OUString& aName)
1204 {
1205     if (! xHierarchicalNameAccess.is())
1206         xHierarchicalNameAccess = officecfg::Office::Common::I18N::get();
1207 
1208     Any aEnabled = xHierarchicalNameAccess->getByHierarchicalName(aName);
1209 
1210     bool enabled = false;
1211 
1212     aEnabled >>= enabled;
1213 
1214     return enabled;
1215 }
1216 
getSupportedNumberingTypes()1217 Sequence< sal_Int16 > DefaultNumberingProvider::getSupportedNumberingTypes(  )
1218 {
1219     Sequence< sal_Int16 > aRet(nSupported_NumberingTypes );
1220     sal_Int16* pArray = aRet.getArray();
1221 
1222     bool cjkEnabled = isScriptFlagEnabled(u"CJK/CJKFont"_ustr);
1223     bool ctlEnabled = isScriptFlagEnabled(u"CTL/CTLFont"_ustr);
1224 
1225     for(sal_Int16 i = 0; i < nSupported_NumberingTypes; i++) {
1226         if ( (aSupportedTypes[i].langOption & LANG_ALL) ||
1227                 ((aSupportedTypes[i].langOption & LANG_CJK) && cjkEnabled) ||
1228                 ((aSupportedTypes[i].langOption & LANG_CTL) && ctlEnabled) )
1229             pArray[i] = aSupportedTypes[i].nType;
1230     }
1231     return aRet;
1232 }
1233 
getNumberingType(const OUString & rNumberingIdentifier)1234 sal_Int16 DefaultNumberingProvider::getNumberingType( const OUString& rNumberingIdentifier )
1235 {
1236     auto it = maSupportedTypesCache.find(rNumberingIdentifier);
1237     if (it != maSupportedTypesCache.end())
1238         return it->second->nType;
1239     for(sal_Int16 i = 0; i < nSupported_NumberingTypes; i++)
1240         if(rNumberingIdentifier == makeNumberingIdentifier(i))
1241         {
1242             maSupportedTypesCache.emplace(rNumberingIdentifier, &aSupportedTypes[i]);
1243             return aSupportedTypes[i].nType;
1244         }
1245     throw RuntimeException();
1246 }
1247 
hasNumberingType(const OUString & rNumberingIdentifier)1248 sal_Bool DefaultNumberingProvider::hasNumberingType( const OUString& rNumberingIdentifier )
1249 {
1250     auto it = maSupportedTypesCache.find(rNumberingIdentifier);
1251     if (it != maSupportedTypesCache.end())
1252         return true;
1253     for(sal_Int16 i = 0; i < nSupported_NumberingTypes; i++)
1254         if(rNumberingIdentifier == makeNumberingIdentifier(i))
1255         {
1256             maSupportedTypesCache.emplace(rNumberingIdentifier, &aSupportedTypes[i]);
1257             return true;
1258         }
1259     return false;
1260 }
1261 
getNumberingIdentifier(sal_Int16 nNumberingType)1262 OUString DefaultNumberingProvider::getNumberingIdentifier( sal_Int16 nNumberingType )
1263 {
1264     for(sal_Int16 i = 0; i < nSupported_NumberingTypes; i++)
1265         if(nNumberingType == aSupportedTypes[i].nType)
1266             return makeNumberingIdentifier(i);
1267     return OUString();
1268 }
1269 
getImplementationName()1270 OUString DefaultNumberingProvider::getImplementationName()
1271 {
1272     return u"com.sun.star.text.DefaultNumberingProvider"_ustr;
1273 }
1274 
supportsService(const OUString & rServiceName)1275 sal_Bool DefaultNumberingProvider::supportsService(const OUString& rServiceName)
1276 {
1277     return cppu::supportsService(this, rServiceName);
1278 }
1279 
getSupportedServiceNames()1280 Sequence< OUString > DefaultNumberingProvider::getSupportedServiceNames()
1281 {
1282     Sequence< OUString > aRet { u"com.sun.star.text.DefaultNumberingProvider"_ustr };
1283     return aRet;
1284 }
1285 
1286 }
1287 
1288 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_text_DefaultNumberingProvider_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)1289 com_sun_star_text_DefaultNumberingProvider_get_implementation(
1290     css::uno::XComponentContext *context,
1291     css::uno::Sequence<css::uno::Any> const &)
1292 {
1293     return cppu::acquire(new i18npool::DefaultNumberingProvider(context));
1294 }
1295 
1296 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1297