mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-04-10 19:15:13 +00:00
235 lines
6.9 KiB
Plaintext
235 lines
6.9 KiB
Plaintext
//========================================================================
|
|
//
|
|
// This file is under the GPLv2 or later license
|
|
//
|
|
// Copyright (C) 2005-2006 Kristian Høgsberg <krh@redhat.com>
|
|
// Copyright (C) 2005, 2009, 2013, 2017, 2018, 2020, 2021 Albert Astals Cid <aacid@kde.org>
|
|
// Copyright (C) 2011 Simon Kellner <kellner@kit.edu>
|
|
// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
|
|
// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
|
|
//
|
|
// 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
|
|
//
|
|
//========================================================================
|
|
|
|
#include <config.h>
|
|
#include <climits>
|
|
#include <cstdlib>
|
|
#include <cstdio>
|
|
#include <cassert>
|
|
|
|
#include <algorithm>
|
|
|
|
#include "PageLabelInfo.h"
|
|
#include "PageLabelInfo_p.h"
|
|
|
|
PageLabelInfo::Interval::Interval(Object *dict, int baseA)
|
|
{
|
|
style = None;
|
|
Object obj = dict->dictLookup("S");
|
|
if (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 = dict->dictLookup("P");
|
|
if (obj.isString()) {
|
|
const auto str = obj.getString();
|
|
prefix.assign(str->c_str(), str->getLength());
|
|
}
|
|
|
|
obj = dict->dictLookup("St");
|
|
if (obj.isInt()) {
|
|
first = obj.getInt();
|
|
} else {
|
|
first = 1;
|
|
}
|
|
|
|
base = baseA;
|
|
}
|
|
|
|
PageLabelInfo::PageLabelInfo(Object *tree, int numPages)
|
|
{
|
|
std::set<int> alreadyParsedRefs;
|
|
parse(tree, alreadyParsedRefs);
|
|
|
|
if (intervals.empty()) {
|
|
return;
|
|
}
|
|
|
|
auto curr = intervals.begin();
|
|
for (auto next = curr + 1; next != intervals.end(); ++next, ++curr) {
|
|
curr->length = std::max(0, next->base - curr->base);
|
|
}
|
|
curr->length = std::max(0, numPages - curr->base);
|
|
}
|
|
|
|
void PageLabelInfo::parse(const Object *tree, std::set<int> &alreadyParsedRefs)
|
|
{
|
|
// leaf node
|
|
Object nums = tree->dictLookup("Nums");
|
|
if (nums.isArray()) {
|
|
for (int i = 0; i < nums.arrayGetLength(); i += 2) {
|
|
Object obj = nums.arrayGet(i);
|
|
if (!obj.isInt()) {
|
|
continue;
|
|
}
|
|
const int base = obj.getInt();
|
|
if (base < 0) {
|
|
continue;
|
|
}
|
|
obj = nums.arrayGet(i + 1);
|
|
if (!obj.isDict()) {
|
|
continue;
|
|
}
|
|
|
|
intervals.emplace_back(&obj, base);
|
|
}
|
|
}
|
|
|
|
Object kids = tree->dictLookup("Kids");
|
|
if (kids.isArray()) {
|
|
const Array *kidsArray = kids.getArray();
|
|
for (int i = 0; i < kidsArray->getLength(); ++i) {
|
|
Ref ref;
|
|
const Object kid = kidsArray->get(i, &ref);
|
|
if (ref != Ref::INVALID()) {
|
|
const int numObj = ref.num;
|
|
if (alreadyParsedRefs.find(numObj) != alreadyParsedRefs.end()) {
|
|
error(errSyntaxError, -1, "loop in PageLabelInfo (numObj: {0:d})", numObj);
|
|
continue;
|
|
}
|
|
alreadyParsedRefs.insert(numObj);
|
|
}
|
|
if (kid.isDict()) {
|
|
parse(&kid, alreadyParsedRefs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool PageLabelInfo::labelToIndex(GooString *label, int *index) const
|
|
{
|
|
const char *const str = label->c_str();
|
|
const std::size_t strLen = label->getLength();
|
|
const bool strUnicode = label->hasUnicodeMarker();
|
|
int number;
|
|
bool ok;
|
|
|
|
for (const auto &interval : intervals) {
|
|
const std::size_t prefixLen = interval.prefix.size();
|
|
if (strLen < prefixLen || interval.prefix.compare(0, prefixLen, str, prefixLen) != 0) {
|
|
continue;
|
|
}
|
|
|
|
switch (interval.style) {
|
|
case Interval::Arabic:
|
|
std::tie(number, ok) = fromDecimal(label->toStr().substr(prefixLen), strUnicode);
|
|
if (ok && number - interval.first < interval.length) {
|
|
*index = interval.base + number - interval.first;
|
|
return true;
|
|
}
|
|
break;
|
|
case Interval::LowercaseRoman:
|
|
case Interval::UppercaseRoman:
|
|
number = fromRoman(str + prefixLen);
|
|
if (number >= 0 && number - interval.first < interval.length) {
|
|
*index = interval.base + number - interval.first;
|
|
return true;
|
|
}
|
|
break;
|
|
case Interval::UppercaseLatin:
|
|
case Interval::LowercaseLatin:
|
|
number = fromLatin(str + prefixLen);
|
|
if (number >= 0 && number - interval.first < interval.length) {
|
|
*index = interval.base + number - interval.first;
|
|
return true;
|
|
}
|
|
break;
|
|
case Interval::None:
|
|
if (interval.length == 1 && label->toStr() == interval.prefix) {
|
|
*index = interval.base;
|
|
return true;
|
|
} else {
|
|
error(errSyntaxError, -1, "asking to convert label to page index in an unknown scenario, report a bug");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool PageLabelInfo::indexToLabel(int index, GooString *label) const
|
|
{
|
|
char buffer[32];
|
|
int base, number;
|
|
const Interval *matching_interval;
|
|
GooString number_string;
|
|
|
|
base = 0;
|
|
matching_interval = nullptr;
|
|
for (const auto &interval : intervals) {
|
|
if (base <= index && index < base + interval.length) {
|
|
matching_interval = &interval;
|
|
break;
|
|
}
|
|
base += interval.length;
|
|
}
|
|
|
|
if (!matching_interval) {
|
|
return false;
|
|
}
|
|
|
|
number = index - base + matching_interval->first;
|
|
switch (matching_interval->style) {
|
|
case Interval::Arabic:
|
|
snprintf(buffer, sizeof(buffer), "%d", number);
|
|
number_string.append(buffer);
|
|
break;
|
|
case Interval::LowercaseRoman:
|
|
toRoman(number, &number_string, false);
|
|
break;
|
|
case Interval::UppercaseRoman:
|
|
toRoman(number, &number_string, true);
|
|
break;
|
|
case Interval::LowercaseLatin:
|
|
toLatin(number, &number_string, false);
|
|
break;
|
|
case Interval::UppercaseLatin:
|
|
toLatin(number, &number_string, true);
|
|
break;
|
|
case Interval::None:
|
|
break;
|
|
}
|
|
|
|
label->clear();
|
|
label->append(matching_interval->prefix.c_str(), matching_interval->prefix.size());
|
|
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);
|
|
}
|
|
} else {
|
|
label->append(&number_string);
|
|
}
|
|
|
|
return true;
|
|
}
|