1 /**
2 * This file is part of the HTML rendering engine for KDE.
3 *
4 * Copyright (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
5 *
6 * (C) Hebrew algorithm by herouth@netvision.net.il
7 * and schlpbch@iam.unibe.ch
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 *
24 */
25
26 #include "rendering/enumerate.h"
27
28 #include <QCharRef>
29 #include <QList>
30
31 namespace khtml
32 {
33
34 namespace Enumerate
35 {
36
toRoman(int number,bool upper)37 QString toRoman(int number, bool upper)
38 {
39 if (number < 1 || number > 3999) {
40 return QString::number(number);
41 }
42 QString roman;
43 static const QChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
44 static const QChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
45 const QChar *digits = upper ? udigits : ldigits;
46 int i, d = 0;
47
48 do {
49 int num = number % 10;
50
51 if (num % 5 < 4)
52 for (i = num % 5; i > 0; i--) {
53 roman.prepend(digits[ d ]);
54 }
55
56 if (num >= 4 && num <= 8) {
57 roman.prepend(digits[ d + 1 ]);
58 }
59
60 if (num == 9) {
61 roman.prepend(digits[ d + 2 ]);
62 }
63
64 if (num % 5 == 4) {
65 roman.prepend(digits[ d ]);
66 }
67
68 number /= 10;
69 d += 2;
70 } while (number);
71
72 return roman;
73 }
74
toGeorgian(int number)75 QString toGeorgian(int number)
76 {
77 // numbers from table at http://xml-maiden.com/numbering/table.xhtml
78 QString georgian;
79 const QChar tenthousand = 0x10F5;
80 static const QChar thousands[9] = {0x10E9, 0x10EA, 0x10EB, 0x10EC,
81 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
82 };
83 static const QChar hundreds[9] = {0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4,
84 0x10E5, 0x10E6, 0x10E7, 0x10E8
85 };
86 static const QChar tens[9] = {0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC,
87 0x10F2, 0x10DD, 0x10DE, 0x10DF
88 };
89 static const QChar units[9] = {0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4,
90 0x10D5, 0x10D6, 0x10F1, 0x10D7
91 };
92
93 if (number < 1 || number > 19999) {
94 return QString::number(number);
95 }
96 if (number >= 10000) {
97 georgian.append(tenthousand);
98 number = number - 10000;
99 }
100 if (number >= 1000) {
101 georgian.append(thousands[number / 1000 - 1]);
102 number = number % 1000;
103 }
104 if (number >= 100) {
105 georgian.append(hundreds[number / 100 - 1]);
106 number = number % 100;
107 }
108 if (number >= 10) {
109 georgian.append(tens[number / 10 - 1]);
110 number = number % 10;
111 }
112 if (number >= 1) {
113 georgian.append(units[number - 1]);
114 }
115
116 return georgian;
117 }
118
toArmenian(int number)119 QString toArmenian(int number)
120 {
121 QString armenian;
122 int thousands = 0x57b;
123 int hundreds = 0x572;
124 int tens = 0x569;
125 int units = 0x560;
126
127 // The standard defines values upto 9999, but 7000 is odd
128 if (number < 1 || number > 6999) {
129 return QString::number(number);
130 }
131 if (number >= 1000) {
132 armenian.append(QChar(thousands + number / 1000));
133 number = number % 1000;
134 }
135 if (number >= 100) {
136 armenian.append(QChar(hundreds + number / 100));
137 number = number % 100;
138 }
139 if (number >= 10) {
140 armenian.append(QChar(tens + number / 10));
141 number = number % 10;
142 }
143 if (number >= 1) {
144 armenian.append(QChar(units + number));
145 }
146
147 return armenian;
148 }
149
toHebrew(int number)150 QString toHebrew(int number)
151 {
152 static const QChar tenDigit[] = {1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510};
153
154 QString letter;
155 if (number < 1) {
156 return QString::number(number);
157 }
158 if (number > 999) {
159 letter = toHebrew(number / 1000) + QLatin1Char('\'');
160 number = number % 1000;
161 }
162
163 int hunderts = (number / 400);
164 if (hunderts > 0) {
165 for (int i = 0; i < hunderts; i++) {
166 letter += QChar(1511 + 3);
167 }
168 }
169 number = number % 400;
170 if ((number / 100) != 0) {
171 letter += QChar(1511 + (number / 100) - 1);
172 }
173 number = number % 100;
174 int tens = number / 10;
175 if (tens > 0 && !(number == 15 || number == 16)) {
176 letter += tenDigit[tens - 1];
177 }
178 if (number == 15 || number == 16) { // special because of religious
179 letter += QChar(1487 + 9); // reasons
180 letter += QChar(1487 + number - 9);
181 } else {
182 number = number % 10;
183 if (number != 0) {
184 letter += QChar(1487 + number);
185 }
186 }
187 return letter;
188 }
189
toLatin(int number,int base)190 static inline QString toLatin(int number, int base)
191 {
192 if (number < 1) {
193 return QString::number(number);
194 }
195 QList<QChar> letters;
196 while (number > 0) {
197 number--; // number 0 is letter a
198 QChar letter = (QChar)(base + (number % 26));
199 letters.prepend(letter);
200 number /= 26;
201 }
202 QString str;
203 str.reserve(letters.size());
204 int i = 0;
205 while (!letters.isEmpty()) {
206 str[i++] = letters.front();
207 letters.pop_front();
208 }
209 return str;
210 }
211
toLowerLatin(int number)212 QString toLowerLatin(int number)
213 {
214 return toLatin(number, 'a');
215 }
216
toUpperLatin(int number)217 QString toUpperLatin(int number)
218 {
219 return toLatin(number, 'A');
220 }
221
toAlphabetic(int number,int base,const QChar alphabet[])222 static inline QString toAlphabetic(int number, int base, const QChar alphabet[])
223 {
224 if (number < 1) {
225 return QString::number(number);
226 }
227 QList<QChar> letters;
228 while (number > 0) {
229 number--; // number 0 is letter 1
230 QChar letter = alphabet[number % base];
231 letters.prepend(letter);
232 number /= base;
233 }
234 QString str;
235 str.reserve(letters.size());
236 int i = 0;
237 while (!letters.isEmpty()) {
238 str[i++] = letters.front();
239 letters.pop_front();
240 }
241 return str;
242 }
243
toHiragana(int number)244 QString toHiragana(int number)
245 {
246 static const QChar hiragana[48] = {0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D,
247 0x304F, 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D,
248 0x305F, 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B,
249 0x306C, 0x306D, 0x306E, 0x306F, 0x3072, 0x3075, 0x3078,
250 0x307B, 0x307E, 0x307F, 0x3080, 0x3081, 0x3082, 0x3084, 0x3086,
251 0x3088, 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308F,
252 0x3090, 0x3091, 0x9092, 0x3093
253 };
254 return toAlphabetic(number, 48, hiragana);
255 }
256
toHiraganaIroha(int number)257 QString toHiraganaIroha(int number)
258 {
259 static const QChar hiragana[47] = {0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068,
260 0x3061, 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B,
261 0x3088, 0x305F, 0x308C, 0x305D, 0x3064, 0x306D, 0x306A,
262 0x3089, 0x3080, 0x3046, 0x3090, 0x306E, 0x304A, 0x304F, 0x3084,
263 0x307E, 0x3051, 0x3075, 0x3053, 0x3048, 0x3066, 0x3042, 0x3055,
264 0x304D, 0x3086, 0x3081, 0x307F, 0x3057, 0x3091, 0x3072, 0x3082,
265 0x305B, 0x3059
266 };
267 return toAlphabetic(number, 47, hiragana);
268 }
269
toKatakana(int number)270 QString toKatakana(int number)
271 {
272 static const QChar katakana[48] = {0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD,
273 0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB,
274 0x30BD, 0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA,
275 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5,
276 0x30D8, 0x30DB, 0x30DE, 0x30DF, 0x30E0, 0x30E1, 0x30E2,
277 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA, 0x30EB, 0x30EC,
278 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x90F2, 0x30F3
279 };
280 return toAlphabetic(number, 48, katakana);
281 }
282
toKatakanaIroha(int number)283 QString toKatakanaIroha(int number)
284 {
285 static const QChar katakana[47] = {0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8,
286 0x30C1, 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB,
287 0x30E8, 0x30BF, 0x30EC, 0x30ED, 0x30C4, 0x30CD, 0x30CA,
288 0x30E9, 0x30E0, 0x30A6, 0x30F0, 0x30CE, 0x30AA, 0x30AF,
289 0x30E4, 0x30DE, 0x30B1, 0x30D5, 0x30B3, 0x30A8, 0x30C6,
290 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1, 0x30DF, 0x30B7,
291 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x90B9
292 };
293 return toAlphabetic(number, 47, katakana);
294 }
295
toLowerGreek(int number)296 QString toLowerGreek(int number)
297 {
298 static const QChar greek[24] = { 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6,
299 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC,
300 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1, 0x3C3,
301 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9
302 };
303
304 return toAlphabetic(number, 24, greek);
305 }
306
toUpperGreek(int number)307 QString toUpperGreek(int number)
308 {
309 // The standard claims to be base 24, but only lists 19 letters.
310 static const QChar greek[19] = { 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398,
311 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F,
312 0x3A0, 0x3A1, 0x3A3, 0x3A9
313 };
314
315 return toAlphabetic(number, 19, greek);
316 }
317
toNumeric(int number,int base)318 static inline QString toNumeric(int number, int base)
319 {
320 QString letter = QString::number(number);
321 for (int i = 0; i < letter.length(); i++) {
322 if (letter[i].isDigit()) {
323 letter[i] = QChar(letter[i].digitValue() + base);
324 }
325 }
326 return letter;
327 }
328
toArabicIndic(int number)329 QString toArabicIndic(int number)
330 {
331 return toNumeric(number, 0x660);
332 }
333
toPersianUrdu(int number)334 QString toPersianUrdu(int number)
335 {
336 return toNumeric(number, 0x6F0);
337 }
338
toLao(int number)339 QString toLao(int number)
340 {
341 return toNumeric(number, 0xED0);
342 }
343
toThai(int number)344 QString toThai(int number)
345 {
346 return toNumeric(number, 0xE50);
347 }
348
toTibetan(int number)349 QString toTibetan(int number)
350 {
351 return toNumeric(number, 0xF20);
352 }
353
toIdeographic(int number,const QChar digits[],const QChar digitmarkers[])354 static inline QString toIdeographic(int number, const QChar digits[], const QChar digitmarkers[])
355 {
356 if (number < 0 || number > 9999) {
357 return QString::number(number);
358 }
359
360 QString grp = QString::number(number);
361
362 // ### Append group markers to handle numbers > 9999
363
364 QString str;
365
366 // special case
367 if (number < 20 && number >= 10) {
368 str.append(digitmarkers[0]);
369 str.append(digits[grp[1].digitValue()]);
370 return str;
371 }
372
373 int len = grp.length();
374 bool collapseZero = false;
375 for (int i = 0; i < len; i++) {
376 int digit = grp[i].digitValue();
377 // Add digit markers to digits > 0
378 if ((len - i - 1) > 0 && digit > 0) {
379 str.append(digitmarkers[(len - i - 2)]);
380 }
381 // Add digit, but collapse consecutive zeros
382 if (!collapseZero || digit > 0) {
383 str.append(digits[digit]);
384
385 if (digit == 0) {
386 collapseZero = true;
387 } else {
388 collapseZero = false;
389 }
390 }
391 }
392 return str;
393 }
394
toTradChineseFormal(int number)395 QString toTradChineseFormal(int number)
396 {
397 // static const QChar groupMarkers[3] = {0x4e07, 0x4ebf, 0x5146};
398 static const QChar digitMarkers[3] = {0x4e07, 0x4ebf, 0x5146};
399 static const QChar digits[10] = {0x96f6, 0x4e00,
400 0x4ebc, 0x4e09,
401 0x56db, 0x4e94,
402 0x516d, 0x4e03,
403 0x516b, 0x4e5d
404 };
405 return toIdeographic(number, digits, digitMarkers);
406 }
407
toTradChineseInformal(int number)408 QString toTradChineseInformal(int number)
409 {
410 // static const QChar groupMarkers[3] = {0x842c, 0x5104, 0x5146};
411 static const QChar digitMarkers[3] = {0x842c, 0x5104, 0x5146};
412 static const QChar digits[10] = {0x96f6, 0x4e00,
413 0x4ebc, 0x4e09,
414 0x56db, 0x4e94,
415 0x516d, 0x4e03,
416 0x516b, 0x4e5d
417 };
418 return toIdeographic(number, digits, digitMarkers);
419 }
420
toSimpChineseFormal(int number)421 QString toSimpChineseFormal(int number)
422 {
423 // static const QChar groupMarkers[3] = {0x4e07, 0x5104, 0x5146};
424 static const QChar digitMarkers[3] = {0x4e07, 0x4ebf, 0x5146};
425 static const QChar digits[10] = {0x96f6, 0x58f9,
426 0x8cb3, 0x53c3,
427 0x8086, 0x4f0d,
428 0x9678, 0x67d2,
429 0x634c, 0x7396
430 };
431 return toIdeographic(number, digits, digitMarkers);
432 }
433
toSimpChineseInformal(int number)434 QString toSimpChineseInformal(int number)
435 {
436 // static const QChar groupMarkers[3] = {0x842c, 0x5104, 0x5146};
437 static const QChar digitMarkers[3] = {0x842c, 0x5104, 0x5146};
438 static const QChar digits[10] = {0x96f6, 0x58f9,
439 0x8cb3, 0x53c3,
440 0x8086, 0x4f0d,
441 0x9678, 0x67d2,
442 0x634c, 0x7396
443 };
444 return toIdeographic(number, digits, digitMarkers);
445 }
446
toJapaneseFormal(int number)447 QString toJapaneseFormal(int number)
448 {
449 // static const QChar groupMarkers[3] = {0x4e07, 0x5104, 0x5146};
450 static const QChar digitMarkers[3] = {0x62fe, 0x4f70, 0x4edf};
451 static const QChar digits[10] = {0x96f6, 0x58f9,
452 0x8cb3, 0x53c3,
453 0x8086, 0x4f0d,
454 0x9678, 0x67d2,
455 0x634c, 0x7396
456 };
457 return toIdeographic(number, digits, digitMarkers);
458 }
459
toJapaneseInformal(int number)460 QString toJapaneseInformal(int number)
461 {
462 // static const QChar groupMarkers[3] = {0x842c, 0x5104, 0x5146};
463 static const QChar digitMarkers[3] = {0x842c, 0x5104, 0x5146};
464 static const QChar digits[10] = {0x96f6, 0x58f9,
465 0x8d30, 0x53c1,
466 0x8086, 0x4f0d,
467 0x9646, 0x67d2,
468 0x634c, 0x7396
469 };
470 return toIdeographic(number, digits, digitMarkers);
471 }
472
473 }
474 } // namespace
475