1/*
2 * Copyright 2011 Google Inc. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "sfntly/table/truetype/loca_table.h"
18#include "sfntly/port/exception_type.h"
19
20namespace sfntly {
21/******************************************************************************
22 * LocaTable class
23 ******************************************************************************/
24LocaTable::~LocaTable() {}
25
26int32_t LocaTable::GlyphOffset(int32_t glyph_id) {
27  if (glyph_id < 0 || glyph_id >= num_glyphs_) {
28#if !defined (SFNTLY_NO_EXCEPTION)
29    throw IndexOutOfBoundException("Glyph ID is out of bounds.");
30#endif
31    return 0;
32  }
33  return Loca(glyph_id);
34}
35
36int32_t LocaTable::GlyphLength(int32_t glyph_id) {
37  if (glyph_id < 0 || glyph_id >= num_glyphs_) {
38#if !defined (SFNTLY_NO_EXCEPTION)
39    throw IndexOutOfBoundException("Glyph ID is out of bounds.");
40#endif
41    return 0;
42  }
43  return Loca(glyph_id + 1) - Loca(glyph_id);
44}
45
46int32_t LocaTable::NumLocas() {
47  return num_glyphs_ + 1;
48}
49
50int32_t LocaTable::Loca(int32_t index) {
51  if (index > num_glyphs_) {
52#if !defined (SFNTLY_NO_EXCEPTION)
53    throw IndexOutOfBoundException();
54#endif
55    return 0;
56  }
57  if (format_version_ == IndexToLocFormat::kShortOffset) {
58    return 2 * data_->ReadUShort(index * DataSize::kUSHORT);
59  }
60  return data_->ReadULongAsInt(index * DataSize::kULONG);
61}
62
63LocaTable::LocaTable(Header* header,
64                     ReadableFontData* data,
65                     int32_t format_version,
66                     int32_t num_glyphs)
67    : Table(header, data),
68      format_version_(format_version),
69      num_glyphs_(num_glyphs) {
70}
71
72/******************************************************************************
73 * LocaTable::Iterator class
74 ******************************************************************************/
75LocaTable::LocaIterator::LocaIterator(LocaTable* table)
76    : PODIterator<int32_t, LocaTable>(table), index_(-1) {
77}
78
79bool LocaTable::LocaIterator::HasNext() {
80  return index_ <= container()->num_glyphs_;
81}
82
83int32_t LocaTable::LocaIterator::Next() {
84  return container()->Loca(index_++);
85}
86
87/******************************************************************************
88 * LocaTable::Builder class
89 ******************************************************************************/
90LocaTable::Builder::Builder(Header* header, WritableFontData* data)
91    : Table::Builder(header, data),
92      format_version_(IndexToLocFormat::kLongOffset),
93      num_glyphs_(-1) {
94}
95
96LocaTable::Builder::Builder(Header* header, ReadableFontData* data)
97    : Table::Builder(header, data),
98      format_version_(IndexToLocFormat::kLongOffset),
99      num_glyphs_(-1) {
100}
101
102LocaTable::Builder::~Builder() {}
103
104CALLER_ATTACH
105LocaTable::Builder* LocaTable::Builder::CreateBuilder(Header* header,
106                                                      WritableFontData* data) {
107  Ptr<LocaTable::Builder> builder;
108  builder = new LocaTable::Builder(header, data);
109  return builder.Detach();
110}
111
112IntegerList* LocaTable::Builder::LocaList() {
113  return GetLocaList();
114}
115
116void LocaTable::Builder::SetLocaList(IntegerList* list) {
117  loca_.clear();
118  if (list) {
119    loca_ = *list;
120    set_model_changed();
121  }
122}
123
124int32_t LocaTable::Builder::GlyphOffset(int32_t glyph_id) {
125  if (CheckGlyphRange(glyph_id) == -1) {
126    return 0;
127  }
128  return GetLocaList()->at(glyph_id);
129}
130
131int32_t LocaTable::Builder::GlyphLength(int32_t glyph_id) {
132  if (CheckGlyphRange(glyph_id) == -1) {
133    return 0;
134  }
135  return GetLocaList()->at(glyph_id + 1) - GetLocaList()->at(glyph_id);
136}
137
138void LocaTable::Builder::SetNumGlyphs(int32_t num_glyphs) {
139  num_glyphs_ = num_glyphs;
140}
141
142int32_t LocaTable::Builder::NumGlyphs() {
143  return LastGlyphIndex() - 1;
144}
145
146void LocaTable::Builder::Revert() {
147  loca_.clear();
148  set_model_changed(false);
149}
150
151int32_t LocaTable::Builder::NumLocas() {
152  return GetLocaList()->size();
153}
154
155int32_t LocaTable::Builder::Loca(int32_t index) {
156  return GetLocaList()->at(index);
157}
158
159CALLER_ATTACH
160FontDataTable* LocaTable::Builder::SubBuildTable(ReadableFontData* data) {
161  FontDataTablePtr table =
162      new LocaTable(header(), data, format_version_, num_glyphs_);
163  return table.Detach();
164}
165
166void LocaTable::Builder::SubDataSet() {
167  Initialize(InternalReadData());
168}
169
170int32_t LocaTable::Builder::SubDataSizeToSerialize() {
171  if (loca_.empty()) {
172    return 0;
173  }
174  if (format_version_ == IndexToLocFormat::kLongOffset) {
175    return loca_.size() * DataSize::kULONG;
176  }
177  return loca_.size() * DataSize::kUSHORT;
178}
179
180bool LocaTable::Builder::SubReadyToSerialize() {
181  return !loca_.empty();
182}
183
184int32_t LocaTable::Builder::SubSerialize(WritableFontData* new_data) {
185  int32_t size = 0;
186  for (IntegerList::iterator l = loca_.begin(), end = loca_.end();
187                             l != end; ++l) {
188    if (format_version_ == IndexToLocFormat::kLongOffset) {
189      size += new_data->WriteULong(size, *l);
190    } else {
191      size += new_data->WriteUShort(size, *l / 2);
192    }
193  }
194  num_glyphs_ = loca_.size() - 1;
195  return size;
196}
197
198void LocaTable::Builder::Initialize(ReadableFontData* data) {
199  ClearLoca(false);
200  if (data) {
201    if (NumGlyphs() < 0) {
202#if !defined (SFNTLY_NO_EXCEPTION)
203      throw IllegalStateException("numglyphs not set on LocaTable Builder.");
204#endif
205      return;
206    }
207    LocaTablePtr table =
208        new LocaTable(header(), data, format_version_, num_glyphs_);
209    Ptr<LocaTable::LocaIterator> loca_iter =
210        new LocaTable::LocaIterator(table);
211    while (loca_iter->HasNext()) {
212      loca_.push_back(loca_iter->Next());
213    }
214  }
215}
216
217int32_t LocaTable::Builder::CheckGlyphRange(int32_t glyph_id) {
218  if (glyph_id < 0 || glyph_id > LastGlyphIndex()) {
219#if !defined (SFNTLY_NO_EXCEPTION)
220    throw IndexOutOfBoundsException("Glyph ID is outside of the allowed range");
221#endif
222    return -1;
223  }
224  return glyph_id;
225}
226
227int32_t LocaTable::Builder::LastGlyphIndex() {
228  return !loca_.empty() ? loca_.size() - 2 : num_glyphs_ - 1;
229}
230
231IntegerList* LocaTable::Builder::GetLocaList() {
232  if (loca_.empty()) {
233    Initialize(InternalReadData());
234    set_model_changed();
235  }
236  return &loca_;
237}
238
239void LocaTable::Builder::ClearLoca(bool nullify) {
240  // Note: in C++ port, nullify is not used at all.
241  UNREFERENCED_PARAMETER(nullify);
242  loca_.clear();
243  set_model_changed(false);
244}
245
246}  // namespace sfntly
247