PageLabelInfo.cc 5.44 KB
Newer Older
1 2 3 4 5
//========================================================================
//
// This file is under the GPLv2 or later license
//
// Copyright (C) 2005-2006 Kristian Høgsberg <krh@redhat.com>
Albert Astals Cid's avatar
Albert Astals Cid committed
6
// Copyright (C) 2005, 2009, 2013, 2017 Albert Astals Cid <aacid@kde.org>
7
// Copyright (C) 2011 Simon Kellner <kellner@kit.edu>
8
// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
9 10 11 12 13 14
//
// 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
//
//========================================================================

Albert Astals Cid's avatar
Albert Astals Cid committed
15
#include <config.h>
16 17 18 19 20 21
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include "PageLabelInfo.h"
22
#include "PageLabelInfo_p.h"
23 24 25

PageLabelInfo::Interval::Interval(Object *dict, int baseA) {
  style = None;
Albert Astals Cid's avatar
Albert Astals Cid committed
26 27
  Object obj = dict->dictLookup("S");
  if (obj.isName()) {
28 29 30 31 32 33 34 35 36 37 38 39 40
    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;
    }
  }

Albert Astals Cid's avatar
Albert Astals Cid committed
41 42
  obj = dict->dictLookup("P");
  if (obj.isString())
43
    prefix = obj.getString()->copy();
44
  else
45
    prefix = new GooString("");
46

Albert Astals Cid's avatar
Albert Astals Cid committed
47 48
  obj = dict->dictLookup("St");
  if (obj.isInt())
49 50 51 52 53 54 55
    first = obj.getInt();
  else
    first = 1;

  base = baseA;
}

56
PageLabelInfo::Interval::~Interval() {
57
  delete prefix;
58 59
}

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
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;
    }
75
    if (interval->length < 0) interval->length = 0;
76 77 78
  }
}

79 80 81 82 83 84 85
PageLabelInfo::~PageLabelInfo() {
  int i;
  for (i = 0; i < intervals.getLength(); ++i) {
    delete (Interval*)intervals.get(i);
  }
}

86 87
void PageLabelInfo::parse(Object *tree) {
  // leaf node
Albert Astals Cid's avatar
Albert Astals Cid committed
88 89 90 91 92
  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()) {
93 94
	continue;
      }
Albert Astals Cid's avatar
Albert Astals Cid committed
95 96 97
      int base = obj.getInt();
      obj = nums.arrayGet(i + 1);
      if (!obj.isDict()) {
98 99 100
	continue;
      }

Albert Astals Cid's avatar
Albert Astals Cid committed
101
      intervals.append(new Interval(&obj, base));
102 103 104
    }
  }

Albert Astals Cid's avatar
Albert Astals Cid committed
105 106 107 108 109
  Object kids = tree->dictLookup("Kids");
  if (kids.isArray()) {
    for (int i = 0; i < kids.arrayGetLength(); ++i) {
      Object kid = kids.arrayGet(i);
      if (kid.isDict())
110 111 112 113 114 115 116 117 118 119
	parse(&kid);
    }
  }
}

GBool PageLabelInfo::labelToIndex(GooString *label, int *index)
{
  Interval *interval;
  char *str = label->getCString(), *end;
  int prefixLength;
120
  int i, number;
121 122 123

  for (i = 0; i < intervals.getLength(); i++) {
    interval = (Interval *) intervals.get(i);
124
    const int base = interval->base;
125 126
    prefixLength = interval->prefix->getLength();
    if (label->cmpN(interval->prefix, prefixLength) != 0)
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
      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;
    }
  }

  return gFalse;
}

GBool PageLabelInfo::indexToLabel(int index, GooString *label)
{
  char buffer[32];
  int i, base, number;
  Interval *interval;
166
  GooString number_string;
167 168

  base = 0;
169
  interval = nullptr;
170 171 172 173 174 175 176 177 178 179 180 181 182 183
  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);
184
    number_string.append(buffer);
185 186
    break;
  case Interval::LowercaseRoman:
187
    toRoman(number, &number_string, gFalse);
188 189
    break;
  case Interval::UppercaseRoman:
190
    toRoman(number, &number_string, gTrue);
191 192
    break;
  case Interval::LowercaseLatin:
193 194 195 196
    toLatin(number, &number_string, gFalse);
    break;
  case Interval::UppercaseLatin:
    toLatin(number, &number_string, gTrue);
197 198 199 200 201
    break;
  case Interval::None:
    break;
  }

202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
  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);
      }
  } else {
      label->append(&number_string);
  }

219 220
  return gTrue;
}