6 #include "UGooString.h"
8 #include "PageLabelInfo.h"
10 /* http://mathworld.wolfram.com/RomanNumerals.html */
12 static int fromRoman(const char *buffer
) {
13 int digit_value
, prev_digit_value
, value
;
16 prev_digit_value
= INT_MAX
;
18 for (i
= 0; buffer
[i
] != '\0'; i
++) {
52 if (digit_value
<= prev_digit_value
)
55 value
+= digit_value
- prev_digit_value
* 2;
56 prev_digit_value
= digit_value
;
62 static void toRoman(int number
, GooString
*str
, GBool uppercase
) {
63 static const char uppercaseNumerals
[] = "IVXLCDM";
64 static const char lowercaseNumerals
[] = "ivxlcdm";
70 wh
= uppercaseNumerals
;
72 wh
= lowercaseNumerals
;
75 for (k
= 3; k
>= 0; k
--) {
77 number
= number
% divisor
;
83 str
->append(wh
[2 * k
+ 1]);
86 str
->append(wh
[2 * k
+ 0]);
87 str
->append(wh
[ 2 * k
+ 2]);
90 str
->append(wh
[2 * k
+ 0]);
91 str
->append(wh
[2 * k
+ 1]);
95 str
->append(wh
[2 * k
+ 1]);
98 for (j
= 0; j
< i
; j
++) {
99 str
->append(wh
[2 * k
+ 0]);
103 divisor
= divisor
/ 10;
107 static int fromLatin(const char *buffer
)
112 for (p
= buffer
; *p
; p
++) {
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;
126 static void toLatin(int number
, GooString
*str
, GBool uppercase
) {
135 count
= (number
- 1) / 26 + 1;
136 letter
= base
+ (number
- 1) % 26;
138 for (i
= 0; i
< count
; i
++)
142 PageLabelInfo::Interval::Interval(Object
*dict
, int baseA
) {
146 if (dict
->dictLookup("S", &obj
)->isName()) {
147 if (obj
.isName("D")) {
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
;
161 if (dict
->dictLookup("P", &obj
)->isString())
162 prefix
= obj
.getString()->copy();
164 prefix
= new GooString("");
167 if (dict
->dictLookup("St", &obj
)->isInt())
168 first
= obj
.getInt();
176 PageLabelInfo::Interval::~Interval() {
180 PageLabelInfo::PageLabelInfo(Object
*tree
, int numPages
) {
182 Interval
*interval
, *next
;
186 for (i
= 0; i
< intervals
.getLength(); i
++) {
187 interval
= (Interval
*) intervals
.get(i
);
189 if (i
+ 1 < intervals
.getLength()) {
190 next
= (Interval
*) intervals
.get(i
+ 1);
191 interval
->length
= next
->base
- interval
->base
;
193 interval
->length
= numPages
- interval
->base
;
198 PageLabelInfo::~PageLabelInfo() {
200 for (i
= 0; i
< intervals
.getLength(); ++i
) {
201 delete (Interval
*)intervals
.get(i
);
205 void PageLabelInfo::parse(Object
*tree
) {
207 Object kids
, kid
, limits
, low
, high
;
212 if (tree
->dictLookup("Nums", &nums
)->isArray()) {
213 for (i
= 0; i
< nums
.arrayGetLength(); i
+= 2) {
214 if (!nums
.arrayGet(i
, &obj
)->isInt()) {
220 if (!nums
.arrayGet(i
+ 1, &obj
)->isDict()) {
225 interval
= new Interval(&obj
, base
);
227 intervals
.append(interval
);
232 if (tree
->dictLookup("Kids", &kids
)->isArray()) {
233 for (i
= 0; i
< kids
.arrayGetLength(); ++i
) {
234 if (kids
.arrayGet(i
, &kid
)->isDict())
242 GBool
PageLabelInfo::labelToIndex(GooString
*label
, int *index
)
245 char *str
= label
->getCString(), *end
;
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)
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
;
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
;
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
;
284 base
+= interval
->length
;
290 GBool
PageLabelInfo::indexToLabel(int index
, GooString
*label
)
295 GooString number_string
;
299 for (i
= 0; i
< intervals
.getLength(); i
++) {
300 interval
= (Interval
*) intervals
.get(i
);
301 if (base
<= index
&& index
< base
+ interval
->length
)
303 base
+= interval
->length
;
306 if (i
== intervals
.getLength())
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
);
315 case Interval::LowercaseRoman
:
316 toRoman(number
, &number_string
, gFalse
);
318 case Interval::UppercaseRoman
:
319 toRoman(number
, &number_string
, gTrue
);
321 case Interval::UppercaseLatin
:
322 case Interval::LowercaseLatin
:
330 label
->append(interval
->prefix
);
331 if (label
->hasUnicodeMarker()) {
335 /* Convert the ascii number string to ucs2 and append. */
336 len
= number_string
.getLength ();
338 for (i
= 0; i
< len
; ++i
) {
339 ucs2_char
[1] = number_string
.getChar(i
);
340 label
->append(ucs2_char
, 2);
343 label
->append(ucs2_char
, 2);
345 label
->append(&number_string
);
352 int main(int argc
, char *argv
[])
356 toRoman(177, &str
, gFalse
);
357 assert (str
.cmp("clxxvii") == 0);
361 GooString
roman("clxxvii");
362 assert (fromRoman(roman
.getCString()) == 177);
367 toLatin(54, &str
, gFalse
);
368 assert (str
.cmp("bbb") == 0);
372 GooString
latin("ddd");
373 assert (fromLatin(latin
.getCString()) == 56);