1 | //======================================================================== |
2 | // |
3 | // This file is under the GPLv2 or later license |
4 | // |
5 | // Copyright (C) 2005-2006 Kristian Høgsberg <krh@redhat.com> |
6 | // Copyright (C) 2005, 2009, 2014, 2019, 2020, 2022 Albert Astals Cid <aacid@kde.org> |
7 | // Copyright (C) 2011 Simon Kellner <kellner@kit.edu> |
8 | // Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it> |
9 | // Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de> |
10 | // Copyright (C) 2019, 2024 Oliver Sander <oliver.sander@tu-dresden.de> |
11 | // |
12 | // To see a description of the changes please see the Changelog file that |
13 | // came with your tarball or type make ChangeLog if you are building from git |
14 | // |
15 | //======================================================================== |
16 | |
17 | #ifndef PAGELABELINFO_P_H |
18 | #define PAGELABELINFO_P_H |
19 | |
20 | /* http://mathworld.wolfram.com/RomanNumerals.html */ |
21 | |
22 | #include "config.h" |
23 | |
24 | #include "goo/GooString.h" |
25 | #include "Error.h" |
26 | #include "UTF.h" |
27 | |
28 | static std::pair<int, bool> fromDecimal(const std::string &str, const bool unicode) |
29 | { |
30 | if (unicode && (str.size() % 2 == 0)) { |
31 | if (hasUnicodeByteOrderMark(s: str)) { |
32 | // strip the marker if it is there |
33 | return fromDecimal(str: str.substr(pos: 2), unicode: true /*unicode*/); |
34 | } |
35 | |
36 | // Since we only care about numbers here, the first byte needs to be |
37 | // 0 and second will be the actual ascii number, so we're going to reconstruct a |
38 | // non unicode string that then we will use strtol to "translate" |
39 | std::string newString; |
40 | bool allGood = true; |
41 | for (size_t i = 0; allGood && i < str.size(); i += 2) { |
42 | if (str[i] == 0) { |
43 | newString += str[i + 1]; |
44 | } else { |
45 | allGood = false; |
46 | } |
47 | } |
48 | |
49 | if (allGood) { |
50 | return fromDecimal(str: newString, unicode: false /*unicode*/); |
51 | } |
52 | } |
53 | |
54 | const char *const begin = str.data(); |
55 | const char *const end = begin + str.size(); |
56 | |
57 | char *parsed; |
58 | const int number = std::strtol(nptr: begin, endptr: &parsed, base: 10); |
59 | return std::make_pair(x: number, y: parsed >= end); |
60 | } |
61 | |
62 | static int fromRoman(const char *buffer) |
63 | { |
64 | int digit_value, prev_digit_value, value; |
65 | int i; |
66 | |
67 | prev_digit_value = INT_MAX; |
68 | value = 0; |
69 | for (i = 0; buffer[i] != '\0'; i++) { |
70 | switch (buffer[i]) { |
71 | case 'm': |
72 | case 'M': |
73 | digit_value = 1000; |
74 | break; |
75 | case 'd': |
76 | case 'D': |
77 | digit_value = 500; |
78 | break; |
79 | case 'c': |
80 | case 'C': |
81 | digit_value = 100; |
82 | break; |
83 | case 'l': |
84 | case 'L': |
85 | digit_value = 50; |
86 | break; |
87 | case 'x': |
88 | case 'X': |
89 | digit_value = 10; |
90 | break; |
91 | case 'v': |
92 | case 'V': |
93 | digit_value = 5; |
94 | break; |
95 | case 'i': |
96 | case 'I': |
97 | digit_value = 1; |
98 | break; |
99 | default: |
100 | return -1; |
101 | } |
102 | |
103 | if (digit_value <= prev_digit_value) { |
104 | value += digit_value; |
105 | } else { |
106 | value += digit_value - prev_digit_value * 2; |
107 | } |
108 | prev_digit_value = digit_value; |
109 | } |
110 | |
111 | return value; |
112 | } |
113 | |
114 | static void toRoman(int number, GooString *str, bool uppercase) |
115 | { |
116 | static const char uppercaseNumerals[] = "IVXLCDM" ; |
117 | static const char lowercaseNumerals[] = "ivxlcdm" ; |
118 | int divisor; |
119 | int i, j, k; |
120 | const char *wh; |
121 | |
122 | if (number >= 4000) { |
123 | error(category: errUnimplemented, pos: -1, msg: "Conversion to roman numerals of numbers >= 4000 not implemented" ); |
124 | return; |
125 | } |
126 | |
127 | if (uppercase) { |
128 | wh = uppercaseNumerals; |
129 | } else { |
130 | wh = lowercaseNumerals; |
131 | } |
132 | |
133 | divisor = 1000; |
134 | for (k = 3; k >= 0; k--) { |
135 | i = number / divisor; |
136 | number = number % divisor; |
137 | |
138 | switch (i) { |
139 | case 0: |
140 | break; |
141 | case 5: |
142 | str->append(c: wh[2 * k + 1]); |
143 | break; |
144 | case 9: |
145 | str->append(c: wh[2 * k + 0]); |
146 | str->append(c: wh[2 * k + 2]); |
147 | break; |
148 | case 4: |
149 | str->append(c: wh[2 * k + 0]); |
150 | str->append(c: wh[2 * k + 1]); |
151 | break; |
152 | default: |
153 | if (i > 5) { |
154 | str->append(c: wh[2 * k + 1]); |
155 | i -= 5; |
156 | } |
157 | for (j = 0; j < i; j++) { |
158 | str->append(c: wh[2 * k + 0]); |
159 | } |
160 | } |
161 | |
162 | divisor = divisor / 10; |
163 | } |
164 | } |
165 | |
166 | static int fromLatin(const char *buffer) |
167 | { |
168 | const char *p; |
169 | |
170 | for (p = buffer; *p; p++) { |
171 | if (*p != buffer[0]) { |
172 | return -1; |
173 | } |
174 | } |
175 | |
176 | const intptr_t diff = p - buffer; |
177 | if (unlikely(diff > std::numeric_limits<int>::max() / 100)) { |
178 | error(category: errUnimplemented, pos: -1, msg: "Something went wrong in fromLatin conversion" ); |
179 | return -1; |
180 | } |
181 | const int count = static_cast<int>(diff); |
182 | if (buffer[0] >= 'a' && buffer[0] <= 'z') { |
183 | return 26 * (count - 1) + buffer[0] - 'a' + 1; |
184 | } |
185 | if (buffer[0] >= 'A' && buffer[0] <= 'Z') { |
186 | return 26 * (count - 1) + buffer[0] - 'A' + 1; |
187 | } |
188 | |
189 | return -1; |
190 | } |
191 | |
192 | static void toLatin(int number, GooString *str, bool uppercase) |
193 | { |
194 | char base, letter; |
195 | int i, count; |
196 | |
197 | if (uppercase) { |
198 | base = 'A'; |
199 | } else { |
200 | base = 'a'; |
201 | } |
202 | |
203 | count = (number - 1) / 26 + 1; |
204 | letter = base + (number - 1) % 26; |
205 | |
206 | for (i = 0; i < count; i++) { |
207 | str->append(c: letter); |
208 | } |
209 | } |
210 | |
211 | #endif |
212 | |