//======================================================================== // // This file is under the GPLv2 or later license // // Copyright (C) 2005-2006 Kristian Høgsberg // Copyright (C) 2005, 2009, 2014, 2019, 2020, 2022 Albert Astals Cid // Copyright (C) 2011 Simon Kellner // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019, 2024 Oliver Sander // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git // //======================================================================== #ifndef PAGELABELINFO_P_H #define PAGELABELINFO_P_H /* http://mathworld.wolfram.com/RomanNumerals.html */ #include "config.h" #include "goo/GooString.h" #include "Error.h" #include "UTF.h" static std::pair fromDecimal(const std::string &str, const bool unicode) { if (unicode && (str.size() % 2 == 0)) { if (hasUnicodeByteOrderMark(str)) { // strip the marker if it is there return fromDecimal(str.substr(2), true /*unicode*/); } // Since we only care about numbers here, the first byte needs to be // 0 and second will be the actual ascii number, so we're going to reconstruct a // non unicode string that then we will use strtol to "translate" std::string newString; bool allGood = true; for (size_t i = 0; allGood && i < str.size(); i += 2) { if (str[i] == 0) { newString += str[i + 1]; } else { allGood = false; } } if (allGood) { return fromDecimal(newString, false /*unicode*/); } } const char *const begin = str.data(); const char *const end = begin + str.size(); char *parsed; const int number = std::strtol(begin, &parsed, 10); return std::make_pair(number, parsed >= end); } 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, bool uppercase) { static const char uppercaseNumerals[] = "IVXLCDM"; static const char lowercaseNumerals[] = "ivxlcdm"; int divisor; int i, j, k; const char *wh; if (number >= 4000) { error(errUnimplemented, -1, "Conversion to roman numerals of numbers >= 4000 not implemented"); return; } 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) { const char *p; for (p = buffer; *p; p++) { if (*p != buffer[0]) { return -1; } } const intptr_t diff = p - buffer; if (unlikely(diff > std::numeric_limits::max() / 100)) { error(errUnimplemented, -1, "Something went wrong in fromLatin conversion"); return -1; } const int count = static_cast(diff); 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, bool 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); } } #endif