SmartPDF - lightweight pdf viewer app for rosapps
[reactos.git] / rosapps / smartpdf / poppler / poppler / PageLabelInfo.cc
1 #include <config.h>
2 #include <limits.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <assert.h>
6 #include "UGooString.h"
7
8 #include "PageLabelInfo.h"
9
10 /* http://mathworld.wolfram.com/RomanNumerals.html */
11
12 static int fromRoman(const char *buffer) {
13 int digit_value, prev_digit_value, value;
14 int i;
15
16 prev_digit_value = INT_MAX;
17 value = 0;
18 for (i = 0; buffer[i] != '\0'; i++) {
19 switch (buffer[i]) {
20 case 'm':
21 case 'M':
22 digit_value = 1000;
23 break;
24 case 'd':
25 case 'D':
26 digit_value = 500;
27 break;
28 case 'c':
29 case 'C':
30 digit_value = 100;
31 break;
32 case 'l':
33 case 'L':
34 digit_value = 50;
35 break;
36 case 'x':
37 case 'X':
38 digit_value = 10;
39 break;
40 case 'v':
41 case 'V':
42 digit_value = 5;
43 break;
44 case 'i':
45 case 'I':
46 digit_value = 1;
47 break;
48 default:
49 return -1;
50 }
51
52 if (digit_value <= prev_digit_value)
53 value += digit_value;
54 else
55 value += digit_value - prev_digit_value * 2;
56 prev_digit_value = digit_value;
57 }
58
59 return value;
60 }
61
62 static void toRoman(int number, GooString *str, GBool uppercase) {
63 static const char uppercaseNumerals[] = "IVXLCDM";
64 static const char lowercaseNumerals[] = "ivxlcdm";
65 int divisor;
66 int i, j, k;
67 const char *wh;
68
69 if (uppercase)
70 wh = uppercaseNumerals;
71 else
72 wh = lowercaseNumerals;
73
74 divisor = 1000;
75 for (k = 3; k >= 0; k--) {
76 i = number / divisor;
77 number = number % divisor;
78
79 switch (i) {
80 case 0:
81 break;
82 case 5:
83 str->append(wh[2 * k + 1]);
84 break;
85 case 9:
86 str->append(wh[2 * k + 0]);
87 str->append(wh[ 2 * k + 2]);
88 break;
89 case 4:
90 str->append(wh[2 * k + 0]);
91 str->append(wh[2 * k + 1]);
92 break;
93 default:
94 if (i > 5) {
95 str->append(wh[2 * k + 1]);
96 i -= 5;
97 }
98 for (j = 0; j < i; j++) {
99 str->append(wh[2 * k + 0]);
100 }
101 }
102
103 divisor = divisor / 10;
104 }
105 }
106
107 static int fromLatin(const char *buffer)
108 {
109 int count;
110 const char *p;
111
112 for (p = buffer; *p; p++) {
113 if (*p != buffer[0])
114 return -1;
115 }
116
117 count = p - buffer;
118 if (buffer[0] >= 'a' && buffer[0] <= 'z')
119 return 26 * (count - 1) + buffer[0] - 'a' + 1;
120 if (buffer[0] >= 'A' && buffer[0] <= 'Z')
121 return 26 * (count - 1) + buffer[0] - 'A' + 1;
122
123 return -1;
124 }
125
126 static void toLatin(int number, GooString *str, GBool uppercase) {
127 char base, letter;
128 int i, count;
129
130 if (uppercase)
131 base = 'A';
132 else
133 base = 'a';
134
135 count = (number - 1) / 26 + 1;
136 letter = base + (number - 1) % 26;
137
138 for (i = 0; i < count; i++)
139 str->append(letter);
140 }
141
142 PageLabelInfo::Interval::Interval(Object *dict, int baseA) {
143 Object obj;
144
145 style = None;
146 if (dict->dictLookup("S", &obj)->isName()) {
147 if (obj.isName("D")) {
148 style = Arabic;
149 } else if (obj.isName("R")) {
150 style = UppercaseRoman;
151 } else if (obj.isName("r")) {
152 style = LowercaseRoman;
153 } else if (obj.isName("A")) {
154 style = UppercaseLatin;
155 } else if (obj.isName("a")) {
156 style = LowercaseLatin;
157 }
158 }
159 obj.free();
160
161 if (dict->dictLookup("P", &obj)->isString())
162 prefix = obj.getString()->copy();
163 else
164 prefix = new GooString("");
165 obj.free();
166
167 if (dict->dictLookup("St", &obj)->isInt())
168 first = obj.getInt();
169 else
170 first = 1;
171 obj.free();
172
173 base = baseA;
174 }
175
176 PageLabelInfo::Interval::~Interval() {
177 delete prefix;
178 }
179
180 PageLabelInfo::PageLabelInfo(Object *tree, int numPages) {
181 int i;
182 Interval *interval, *next;
183
184 parse(tree);
185
186 for (i = 0; i < intervals.getLength(); i++) {
187 interval = (Interval *) intervals.get(i);
188
189 if (i + 1 < intervals.getLength()) {
190 next = (Interval *) intervals.get(i + 1);
191 interval->length = next->base - interval->base;
192 } else {
193 interval->length = numPages - interval->base;
194 }
195 }
196 }
197
198 PageLabelInfo::~PageLabelInfo() {
199 int i;
200 for (i = 0; i < intervals.getLength(); ++i) {
201 delete (Interval*)intervals.get(i);
202 }
203 }
204
205 void PageLabelInfo::parse(Object *tree) {
206 Object nums, obj;
207 Object kids, kid, limits, low, high;
208 int i, base;
209 Interval *interval;
210
211 // leaf node
212 if (tree->dictLookup("Nums", &nums)->isArray()) {
213 for (i = 0; i < nums.arrayGetLength(); i += 2) {
214 if (!nums.arrayGet(i, &obj)->isInt()) {
215 obj.free();
216 continue;
217 }
218 base = obj.getInt();
219 obj.free();
220 if (!nums.arrayGet(i + 1, &obj)->isDict()) {
221 obj.free();
222 continue;
223 }
224
225 interval = new Interval(&obj, base);
226 obj.free();
227 intervals.append(interval);
228 }
229 }
230 nums.free();
231
232 if (tree->dictLookup("Kids", &kids)->isArray()) {
233 for (i = 0; i < kids.arrayGetLength(); ++i) {
234 if (kids.arrayGet(i, &kid)->isDict())
235 parse(&kid);
236 kid.free();
237 }
238 }
239 kids.free();
240 }
241
242 GBool PageLabelInfo::labelToIndex(GooString *label, int *index)
243 {
244 Interval *interval;
245 char *str = label->getCString(), *end;
246 int prefixLength;
247 int i, base, number;
248
249 base = 0;
250 for (i = 0; i < intervals.getLength(); i++) {
251 interval = (Interval *) intervals.get(i);
252 prefixLength = interval->prefix->getLength();
253 if (label->cmpN(interval->prefix, prefixLength) != 0)
254 continue;
255
256 switch (interval->style) {
257 case Interval::Arabic:
258 number = strtol(str + prefixLength, &end, 10);
259 if (*end == '\0' && number - interval->first < interval->length) {
260 *index = base + number - interval->first;
261 return gTrue;
262 }
263 break;
264 case Interval::LowercaseRoman:
265 case Interval::UppercaseRoman:
266 number = fromRoman(str + prefixLength);
267 if (number >= 0 && number - interval->first < interval->length) {
268 *index = base + number - interval->first;
269 return gTrue;
270 }
271 break;
272 case Interval::UppercaseLatin:
273 case Interval::LowercaseLatin:
274 number = fromLatin(str + prefixLength);
275 if (number >= 0 && number - interval->first < interval->length) {
276 *index = base + number - interval->first;
277 return gTrue;
278 }
279 break;
280 case Interval::None:
281 break;
282 }
283
284 base += interval->length;
285 }
286
287 return gFalse;
288 }
289
290 GBool PageLabelInfo::indexToLabel(int index, GooString *label)
291 {
292 char buffer[32];
293 int i, base, number;
294 Interval *interval;
295 GooString number_string;
296
297 base = 0;
298 interval = NULL;
299 for (i = 0; i < intervals.getLength(); i++) {
300 interval = (Interval *) intervals.get(i);
301 if (base <= index && index < base + interval->length)
302 break;
303 base += interval->length;
304 }
305
306 if (i == intervals.getLength())
307 return gFalse;
308
309 number = index - base + interval->first;
310 switch (interval->style) {
311 case Interval::Arabic:
312 snprintf (buffer, sizeof(buffer), "%d", number);
313 number_string.append(buffer);
314 break;
315 case Interval::LowercaseRoman:
316 toRoman(number, &number_string, gFalse);
317 break;
318 case Interval::UppercaseRoman:
319 toRoman(number, &number_string, gTrue);
320 break;
321 case Interval::UppercaseLatin:
322 case Interval::LowercaseLatin:
323 number = 0;
324 break;
325 case Interval::None:
326 break;
327 }
328
329 label->clear();
330 label->append(interval->prefix);
331 if (label->hasUnicodeMarker()) {
332 int i, len;
333 char ucs2_char[2];
334
335 /* Convert the ascii number string to ucs2 and append. */
336 len = number_string.getLength ();
337 ucs2_char[0] = 0;
338 for (i = 0; i < len; ++i) {
339 ucs2_char[1] = number_string.getChar(i);
340 label->append(ucs2_char, 2);
341 }
342 ucs2_char[1] = 0;
343 label->append(ucs2_char, 2);
344 } else {
345 label->append(&number_string);
346 }
347
348 return gTrue;
349 }
350
351 #ifdef TEST
352 int main(int argc, char *argv[])
353 {
354 {
355 GooString str;
356 toRoman(177, &str, gFalse);
357 assert (str.cmp("clxxvii") == 0);
358 }
359
360 {
361 GooString roman("clxxvii");
362 assert (fromRoman(roman.getCString()) == 177);
363 }
364
365 {
366 GooString str;
367 toLatin(54, &str, gFalse);
368 assert (str.cmp("bbb") == 0);
369 }
370
371 {
372 GooString latin("ddd");
373 assert (fromLatin(latin.getCString()) == 56);
374 }
375 }
376 #endif