blob: 1928d5f82107d8f38b59699e6fa237b0e770b97f [file] [log] [blame]
//========================================================================
//
// 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 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 <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#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) {
parse(tree);
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(Object *tree) {
// 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;
}
int base = obj.getInt();
obj = nums.arrayGet(i + 1);
if (!obj.isDict()) {
continue;
}
intervals.emplace_back(&obj, base);
}
}
Object kids = tree->dictLookup("Kids");
if (kids.isArray()) {
for (int i = 0; i < kids.arrayGetLength(); ++i) {
Object kid = kids.arrayGet(i);
if (kid.isDict())
parse(&kid);
}
}
}
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(str + prefixLen, str + strLen, 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:
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;
}