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/bitmap/index_sub_table.h"
18
19#include "sfntly/table/bitmap/eblc_table.h"
20#include "sfntly/table/bitmap/index_sub_table_format1.h"
21#include "sfntly/table/bitmap/index_sub_table_format2.h"
22#include "sfntly/table/bitmap/index_sub_table_format3.h"
23#include "sfntly/table/bitmap/index_sub_table_format4.h"
24#include "sfntly/table/bitmap/index_sub_table_format5.h"
25
26namespace sfntly {
27/******************************************************************************
28 * IndexSubTable class
29 ******************************************************************************/
30CALLER_ATTACH BitmapGlyphInfo* IndexSubTable::GlyphInfo(int32_t glyph_id) {
31  int32_t loca = CheckGlyphRange(glyph_id);
32  if (loca == -1) {
33    return NULL;
34  }
35  if (GlyphStartOffset(glyph_id) == -1) {
36    return NULL;
37  }
38  BitmapGlyphInfoPtr output = new BitmapGlyphInfo(glyph_id,
39                                                  image_data_offset(),
40                                                  GlyphStartOffset(glyph_id),
41                                                  GlyphLength(glyph_id),
42                                                  image_format());
43  return output.Detach();
44}
45
46int32_t IndexSubTable::GlyphOffset(int32_t glyph_id) {
47  int32_t glyph_start_offset = GlyphStartOffset(glyph_id);
48  if (glyph_start_offset == -1) {
49    return -1;
50  }
51  return image_data_offset() + glyph_start_offset;
52}
53
54// static
55CALLER_ATTACH IndexSubTable*
56    IndexSubTable::CreateIndexSubTable(ReadableFontData* data,
57                                       int32_t offset_to_index_sub_table_array,
58                                       int32_t array_index) {
59  IndexSubTableBuilderPtr builder;
60  builder.Attach(IndexSubTable::Builder::CreateBuilder(
61      data, offset_to_index_sub_table_array, array_index));
62  return down_cast<IndexSubTable*>(builder->Build());
63}
64
65IndexSubTable::IndexSubTable(ReadableFontData* data,
66                             int32_t first_glyph_index,
67                             int32_t last_glyph_index)
68    : SubTable(data),
69      first_glyph_index_(first_glyph_index),
70      last_glyph_index_(last_glyph_index) {
71  index_format_ =
72      data_->ReadUShort(EblcTable::Offset::kIndexSubHeader_indexFormat);
73  image_format_ =
74      data_->ReadUShort(EblcTable::Offset::kIndexSubHeader_imageFormat);
75  image_data_offset_ =
76      data_->ReadULongAsInt(EblcTable::Offset::kIndexSubHeader_imageDataOffset);
77}
78
79int32_t IndexSubTable::CheckGlyphRange(int32_t glyph_id) {
80  return CheckGlyphRange(glyph_id, first_glyph_index(), last_glyph_index());
81}
82
83// static
84int32_t IndexSubTable::CheckGlyphRange(int32_t glyph_id,
85                                       int32_t first_glyph_id,
86                                       int32_t last_glyph_id) {
87  if (glyph_id < first_glyph_id || glyph_id > last_glyph_id) {
88#if !defined (SFNTLY_NO_EXCEPTION)
89    throw IndexOutOfBoundException("Glyph ID is outside of the allowed range.");
90#endif
91    return -1;
92  }
93  return glyph_id - first_glyph_id;
94}
95
96/******************************************************************************
97 * IndexSubTable::Builder class
98 ******************************************************************************/
99IndexSubTable::Builder::~Builder() {
100}
101
102void IndexSubTable::Builder::Revert() {
103  set_model_changed(false);
104  Initialize(InternalReadData());
105}
106
107CALLER_ATTACH BitmapGlyphInfo* IndexSubTable::Builder::GlyphInfo(
108    int32_t glyph_id) {
109  BitmapGlyphInfoPtr glyph_info =
110      new BitmapGlyphInfo(glyph_id,
111                          image_data_offset(),
112                          GlyphStartOffset(glyph_id),
113                          GlyphLength(glyph_id),
114                          image_format());
115  return glyph_info.Detach();
116}
117
118int32_t IndexSubTable::Builder::GlyphOffset(int32_t glyph_id) {
119  return image_data_offset() + GlyphStartOffset(glyph_id);
120}
121
122// static
123CALLER_ATTACH IndexSubTable::Builder*
124IndexSubTable::Builder::CreateBuilder(int32_t index_format) {
125  switch (index_format) {
126    case Format::FORMAT_1:
127      return IndexSubTableFormat1::Builder::CreateBuilder();
128    case Format::FORMAT_2:
129      return IndexSubTableFormat2::Builder::CreateBuilder();
130    case Format::FORMAT_3:
131      return IndexSubTableFormat3::Builder::CreateBuilder();
132    case Format::FORMAT_4:
133      return IndexSubTableFormat4::Builder::CreateBuilder();
134    case Format::FORMAT_5:
135      return IndexSubTableFormat5::Builder::CreateBuilder();
136    default:
137#if !defined (SFNTLY_NO_EXCEPTION)
138      throw IllegalArgumentException("Invalid index subtable format");
139#endif
140      return NULL;
141  }
142}
143
144// static
145CALLER_ATTACH IndexSubTable::Builder*
146IndexSubTable::Builder::CreateBuilder(ReadableFontData* data,
147    int32_t offset_to_index_sub_table_array, int32_t array_index) {
148  int32_t index_sub_table_entry_offset =
149      offset_to_index_sub_table_array +
150      array_index * EblcTable::Offset::kIndexSubTableEntryLength;
151  int32_t first_glyph_index =
152      data->ReadUShort(index_sub_table_entry_offset +
153                       EblcTable::Offset::kIndexSubTableEntry_firstGlyphIndex);
154  int32_t last_glyph_index =
155      data->ReadUShort(index_sub_table_entry_offset +
156                       EblcTable::Offset::kIndexSubTableEntry_lastGlyphIndex);
157  int32_t additional_offset_to_index_subtable = data->ReadULongAsInt(
158      index_sub_table_entry_offset +
159      EblcTable::Offset::kIndexSubTableEntry_additionalOffsetToIndexSubTable);
160  int32_t index_sub_table_offset = offset_to_index_sub_table_array +
161                                   additional_offset_to_index_subtable;
162  int32_t index_format = data->ReadUShort(index_sub_table_offset);
163  switch (index_format) {
164    case 1:
165      return IndexSubTableFormat1::Builder::CreateBuilder(
166          data, index_sub_table_offset, first_glyph_index, last_glyph_index);
167    case 2:
168      return IndexSubTableFormat2::Builder::CreateBuilder(
169          data, index_sub_table_offset, first_glyph_index, last_glyph_index);
170    case 3:
171      return IndexSubTableFormat3::Builder::CreateBuilder(
172          data, index_sub_table_offset, first_glyph_index, last_glyph_index);
173    case 4:
174      return IndexSubTableFormat4::Builder::CreateBuilder(
175          data, index_sub_table_offset, first_glyph_index, last_glyph_index);
176    case 5:
177      return IndexSubTableFormat5::Builder::CreateBuilder(
178          data, index_sub_table_offset, first_glyph_index, last_glyph_index);
179    default:
180      // Unknown format and unable to process.
181#if !defined (SFNTLY_NO_EXCEPTION)
182      throw IllegalArgumentException("Invalid Index Subtable Format");
183#endif
184      break;
185  }
186  return NULL;
187}
188
189CALLER_ATTACH
190FontDataTable* IndexSubTable::Builder::SubBuildTable(ReadableFontData* data) {
191  UNREFERENCED_PARAMETER(data);
192  return NULL;
193}
194
195void IndexSubTable::Builder::SubDataSet() {
196  // NOP
197}
198
199int32_t IndexSubTable::Builder::SubDataSizeToSerialize() {
200  return 0;
201}
202
203bool IndexSubTable::Builder::SubReadyToSerialize() {
204  return false;
205}
206
207int32_t IndexSubTable::Builder::SubSerialize(WritableFontData* new_data) {
208  UNREFERENCED_PARAMETER(new_data);
209  return 0;
210}
211
212IndexSubTable::Builder::Builder(int32_t data_size, int32_t index_format)
213    : SubTable::Builder(data_size),
214      first_glyph_index_(0),
215      last_glyph_index_(0),
216      index_format_(index_format),
217      image_format_(0),
218      image_data_offset_(0) {
219}
220
221IndexSubTable::Builder::Builder(int32_t index_format,
222                                int32_t image_format,
223                                int32_t image_data_offset,
224                                int32_t data_size)
225    : SubTable::Builder(data_size),
226      first_glyph_index_(0),
227      last_glyph_index_(0),
228      index_format_(index_format),
229      image_format_(image_format),
230      image_data_offset_(image_data_offset) {
231}
232
233IndexSubTable::Builder::Builder(WritableFontData* data,
234                                int32_t first_glyph_index,
235                                int32_t last_glyph_index)
236    : SubTable::Builder(data),
237      first_glyph_index_(first_glyph_index),
238      last_glyph_index_(last_glyph_index) {
239  Initialize(data);
240}
241
242IndexSubTable::Builder::Builder(ReadableFontData* data,
243                                int32_t first_glyph_index,
244                                int32_t last_glyph_index)
245    : SubTable::Builder(data),
246      first_glyph_index_(first_glyph_index),
247      last_glyph_index_(last_glyph_index) {
248  Initialize(data);
249}
250
251int32_t IndexSubTable::Builder::CheckGlyphRange(int32_t glyph_id) {
252  return IndexSubTable::CheckGlyphRange(glyph_id,
253                                        first_glyph_index(),
254                                        last_glyph_index());
255}
256
257int32_t IndexSubTable::Builder::SerializeIndexSubHeader(
258    WritableFontData* data) {
259  int32_t size =
260      data->WriteUShort(EblcTable::Offset::kIndexSubHeader_indexFormat,
261                        index_format());
262  size += data->WriteUShort(EblcTable::Offset::kIndexSubHeader_imageFormat,
263                            image_format());
264  size += data->WriteULong(EblcTable::Offset::kIndexSubHeader_imageDataOffset,
265                           image_data_offset());
266  return size;
267}
268
269void IndexSubTable::Builder::Initialize(ReadableFontData* data) {
270  index_format_ =
271      data->ReadUShort(EblcTable::Offset::kIndexSubHeader_indexFormat);
272  image_format_ =
273      data->ReadUShort(EblcTable::Offset::kIndexSubHeader_imageFormat);
274  image_data_offset_ =
275      data->ReadULongAsInt(EblcTable::Offset::kIndexSubHeader_imageDataOffset);
276}
277
278}  // namespace sfntly
279