#include #include #include #include #include "UGooString.h" #include "PageLabelInfo.h" /* http://mathworld.wolfram.com/RomanNumerals.html */ static int fromRoman(const char *buffer) { int digit_value, prev_digit_value, value; int i; prev_digit_value = INT_MAX; value = 0; for (i = 0; buffer[i] != '\0'; i++) { switch (buffer[i]) { case 'm': case 'M': digit_value = 1000; break; case 'd': case 'D': digit_value = 500; break; case 'c': case 'C': digit_value = 100; break; case 'l': case 'L': digit_value = 50; break; case 'x': case 'X': digit_value = 10; break; case 'v': case 'V': digit_value = 5; break; case 'i': case 'I': digit_value = 1; break; default: return -1; } if (digit_value <= prev_digit_value) value += digit_value; else value += digit_value - prev_digit_value * 2; prev_digit_value = digit_value; } return value; } static void toRoman(int number, GooString *str, GBool uppercase) { static const char uppercaseNumerals[] = "IVXLCDM"; static const char lowercaseNumerals[] = "ivxlcdm"; int divisor; int i, j, k; const char *wh; if (uppercase) wh = uppercaseNumerals; else wh = lowercaseNumerals; divisor = 1000; for (k = 3; k >= 0; k--) { i = number / divisor; number = number % divisor; switch (i) { case 0: break; case 5: str->append(wh[2 * k + 1]); break; case 9: str->append(wh[2 * k + 0]); str->append(wh[ 2 * k + 2]); break; case 4: str->append(wh[2 * k + 0]); str->append(wh[2 * k + 1]); break; default: if (i > 5) { str->append(wh[2 * k + 1]); i -= 5; } for (j = 0; j < i; j++) { str->append(wh[2 * k + 0]); } } divisor = divisor / 10; } } static int fromLatin(const char *buffer) { int count; const char *p; for (p = buffer; *p; p++) { if (*p != buffer[0]) return -1; } count = p - buffer; if (buffer[0] >= 'a' && buffer[0] <= 'z') return 26 * (count - 1) + buffer[0] - 'a' + 1; if (buffer[0] >= 'A' && buffer[0] <= 'Z') return 26 * (count - 1) + buffer[0] - 'A' + 1; return -1; } static void toLatin(int number, GooString *str, GBool uppercase) { char base, letter; int i, count; if (uppercase) base = 'A'; else base = 'a'; count = (number - 1) / 26 + 1; letter = base + (number - 1) % 26; for (i = 0; i < count; i++) str->append(letter); } PageLabelInfo::Interval::Interval(Object *dict, int baseA) { Object obj; style = None; if (dict->dictLookup("S", &obj)->isName()) { if (obj.isName("D")) { style = Arabic; } else if (obj.isName("R")) { style = UppercaseRoman; } else if (obj.isName("r")) { style = LowercaseRoman; } else if (obj.isName("A")) { style = UppercaseLatin; } else if (obj.isName("a")) { style = LowercaseLatin; } } obj.free(); if (dict->dictLookup("P", &obj)->isString()) prefix = obj.getString()->copy(); else prefix = new GooString(""); obj.free(); if (dict->dictLookup("St", &obj)->isInt()) first = obj.getInt(); else first = 1; obj.free(); base = baseA; } PageLabelInfo::Interval::~Interval() { delete prefix; } PageLabelInfo::PageLabelInfo(Object *tree, int numPages) { int i; Interval *interval, *next; parse(tree); for (i = 0; i < intervals.getLength(); i++) { interval = (Interval *) intervals.get(i); if (i + 1 < intervals.getLength()) { next = (Interval *) intervals.get(i + 1); interval->length = next->base - interval->base; } else { interval->length = numPages - interval->base; } } } PageLabelInfo::~PageLabelInfo() { int i; for (i = 0; i < intervals.getLength(); ++i) { delete (Interval*)intervals.get(i); } } void PageLabelInfo::parse(Object *tree) { Object nums, obj; Object kids, kid, limits, low, high; int i, base; Interval *interval; // leaf node if (tree->dictLookup("Nums", &nums)->isArray()) { for (i = 0; i < nums.arrayGetLength(); i += 2) { if (!nums.arrayGet(i, &obj)->isInt()) { obj.free(); continue; } base = obj.getInt(); obj.free(); if (!nums.arrayGet(i + 1, &obj)->isDict()) { obj.free(); continue; } interval = new Interval(&obj, base); obj.free(); intervals.append(interval); } } nums.free(); if (tree->dictLookup("Kids", &kids)->isArray()) { for (i = 0; i < kids.arrayGetLength(); ++i) { if (kids.arrayGet(i, &kid)->isDict()) parse(&kid); kid.free(); } } kids.free(); } GBool PageLabelInfo::labelToIndex(GooString *label, int *index) { Interval *interval; char *str = label->getCString(), *end; int prefixLength; int i, base, number; base = 0; for (i = 0; i < intervals.getLength(); i++) { interval = (Interval *) intervals.get(i); prefixLength = interval->prefix->getLength(); if (label->cmpN(interval->prefix, prefixLength) != 0) continue; switch (interval->style) { case Interval::Arabic: number = strtol(str + prefixLength, &end, 10); if (*end == '\0' && number - interval->first < interval->length) { *index = base + number - interval->first; return gTrue; } break; case Interval::LowercaseRoman: case Interval::UppercaseRoman: number = fromRoman(str + prefixLength); if (number >= 0 && number - interval->first < interval->length) { *index = base + number - interval->first; return gTrue; } break; case Interval::UppercaseLatin: case Interval::LowercaseLatin: number = fromLatin(str + prefixLength); if (number >= 0 && number - interval->first < interval->length) { *index = base + number - interval->first; return gTrue; } break; case Interval::None: break; } base += interval->length; } return gFalse; } GBool PageLabelInfo::indexToLabel(int index, GooString *label) { char buffer[32]; int i, base, number; Interval *interval; GooString number_string; base = 0; interval = NULL; for (i = 0; i < intervals.getLength(); i++) { interval = (Interval *) intervals.get(i); if (base <= index && index < base + interval->length) break; base += interval->length; } if (i == intervals.getLength()) return gFalse; number = index - base + interval->first; switch (interval->style) { case Interval::Arabic: snprintf (buffer, sizeof(buffer), "%d", number); number_string.append(buffer); break; case Interval::LowercaseRoman: toRoman(number, &number_string, gFalse); break; case Interval::UppercaseRoman: toRoman(number, &number_string, gTrue); break; case Interval::UppercaseLatin: case Interval::LowercaseLatin: number = 0; break; case Interval::None: break; } label->clear(); label->append(interval->prefix); if (label->hasUnicodeMarker()) { int i, len; char ucs2_char[2]; /* Convert the ascii number string to ucs2 and append. */ len = number_string.getLength (); ucs2_char[0] = 0; for (i = 0; i < len; ++i) { ucs2_char[1] = number_string.getChar(i); label->append(ucs2_char, 2); } ucs2_char[1] = 0; label->append(ucs2_char, 2); } else { label->append(&number_string); } return gTrue; } #ifdef TEST int main(int argc, char *argv[]) { { GooString str; toRoman(177, &str, gFalse); assert (str.cmp("clxxvii") == 0); } { GooString roman("clxxvii"); assert (fromRoman(roman.getCString()) == 177); } { GooString str; toLatin(54, &str, gFalse); assert (str.cmp("bbb") == 0); } { GooString latin("ddd"); assert (fromLatin(latin.getCString()) == 56); } } #endif