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 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 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 
fromDecimal(const std::string & str,const bool unicode)27 static std::pair<int, bool> fromDecimal(const std::string &str, const bool unicode)
28 {
29     if (unicode && (str.size() % 2 == 0)) {
30         if (GooString::hasUnicodeMarker(str)) {
31             // strip the marker if it is there
32             return fromDecimal(str.substr(2), true /*unicode*/);
33         }
34 
35         // Since we only care about numbers here, the first byte needs to be
36         // 0 and second will be the actual ascii number, so we're going to reconstruct a
37         // non unicode string that then we will use strtol to "translate"
38         std::string newString;
39         bool allGood = true;
40         for (size_t i = 0; allGood && i < str.size(); i += 2) {
41             if (str[i] == 0) {
42                 newString += str[i + 1];
43             } else {
44                 allGood = false;
45             }
46         }
47 
48         if (allGood) {
49             return fromDecimal(newString, false /*unicode*/);
50         }
51     }
52 
53     const char *const begin = str.data();
54     const char *const end = begin + str.size();
55 
56     char *parsed;
57     const int number = std::strtol(begin, &parsed, 10);
58     return std::make_pair(number, parsed >= end);
59 }
60 
fromRoman(const char * buffer)61 static int fromRoman(const char *buffer)
62 {
63     int digit_value, prev_digit_value, value;
64     int i;
65 
66     prev_digit_value = INT_MAX;
67     value = 0;
68     for (i = 0; buffer[i] != '\0'; i++) {
69         switch (buffer[i]) {
70         case 'm':
71         case 'M':
72             digit_value = 1000;
73             break;
74         case 'd':
75         case 'D':
76             digit_value = 500;
77             break;
78         case 'c':
79         case 'C':
80             digit_value = 100;
81             break;
82         case 'l':
83         case 'L':
84             digit_value = 50;
85             break;
86         case 'x':
87         case 'X':
88             digit_value = 10;
89             break;
90         case 'v':
91         case 'V':
92             digit_value = 5;
93             break;
94         case 'i':
95         case 'I':
96             digit_value = 1;
97             break;
98         default:
99             return -1;
100         }
101 
102         if (digit_value <= prev_digit_value)
103             value += digit_value;
104         else
105             value += digit_value - prev_digit_value * 2;
106         prev_digit_value = digit_value;
107     }
108 
109     return value;
110 }
111 
toRoman(int number,GooString * str,bool uppercase)112 static void toRoman(int number, GooString *str, bool uppercase)
113 {
114     static const char uppercaseNumerals[] = "IVXLCDM";
115     static const char lowercaseNumerals[] = "ivxlcdm";
116     int divisor;
117     int i, j, k;
118     const char *wh;
119 
120     if (number >= 4000) {
121         error(errUnimplemented, -1, "Conversion to roman numerals of numbers >= 4000 not implemented");
122         return;
123     }
124 
125     if (uppercase)
126         wh = uppercaseNumerals;
127     else
128         wh = lowercaseNumerals;
129 
130     divisor = 1000;
131     for (k = 3; k >= 0; k--) {
132         i = number / divisor;
133         number = number % divisor;
134 
135         switch (i) {
136         case 0:
137             break;
138         case 5:
139             str->append(wh[2 * k + 1]);
140             break;
141         case 9:
142             str->append(wh[2 * k + 0]);
143             str->append(wh[2 * k + 2]);
144             break;
145         case 4:
146             str->append(wh[2 * k + 0]);
147             str->append(wh[2 * k + 1]);
148             break;
149         default:
150             if (i > 5) {
151                 str->append(wh[2 * k + 1]);
152                 i -= 5;
153             }
154             for (j = 0; j < i; j++) {
155                 str->append(wh[2 * k + 0]);
156             }
157         }
158 
159         divisor = divisor / 10;
160     }
161 }
162 
fromLatin(const char * buffer)163 static int fromLatin(const char *buffer)
164 {
165     int count;
166     const char *p;
167 
168     for (p = buffer; *p; p++) {
169         if (*p != buffer[0])
170             return -1;
171     }
172 
173     count = p - buffer;
174     if (buffer[0] >= 'a' && buffer[0] <= 'z')
175         return 26 * (count - 1) + buffer[0] - 'a' + 1;
176     if (buffer[0] >= 'A' && buffer[0] <= 'Z')
177         return 26 * (count - 1) + buffer[0] - 'A' + 1;
178 
179     return -1;
180 }
181 
toLatin(int number,GooString * str,bool uppercase)182 static void toLatin(int number, GooString *str, bool uppercase)
183 {
184     char base, letter;
185     int i, count;
186 
187     if (uppercase)
188         base = 'A';
189     else
190         base = 'a';
191 
192     count = (number - 1) / 26 + 1;
193     letter = base + (number - 1) % 26;
194 
195     for (i = 0; i < count; i++)
196         str->append(letter);
197 }
198 
199 #endif
200