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