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// type.h needs to be included first because of building issues on Windows
18// Type aliases we delcare are defined in other headers and make the build
19// fail otherwise.
20#include "sfntly/port/type.h"
21#include "sfntly/table/core/cmap_table.h"
22
23#include <stdio.h>
24#include <stdlib.h>
25
26#include <utility>
27
28#include "sfntly/font.h"
29#include "sfntly/math/font_math.h"
30#include "sfntly/port/endian.h"
31#include "sfntly/port/exception_type.h"
32#include "sfntly/table/core/name_table.h"
33
34namespace sfntly {
35
36const int32_t CMapTable::NOTDEF = 0;
37
38CMapTable::CMapId CMapTable::WINDOWS_BMP = {
39  PlatformId::kWindows,
40  WindowsEncodingId::kUnicodeUCS2
41};
42CMapTable::CMapId CMapTable::WINDOWS_UCS4 = {
43  PlatformId::kWindows,
44  WindowsEncodingId::kUnicodeUCS4
45};
46CMapTable::CMapId CMapTable::MAC_ROMAN = {
47  PlatformId::kWindows,
48  MacintoshEncodingId::kRoman
49};
50
51/******************************************************************************
52 * CMapTable class
53 ******************************************************************************/
54CMapTable::CMapTable(Header* header, ReadableFontData* data)
55  : SubTableContainerTable(header, data) {
56}
57
58CMapTable::~CMapTable() {}
59
60CALLER_ATTACH CMapTable::CMap* CMapTable::GetCMap(const int32_t index) {
61  if (index < 0 || index > NumCMaps()) {
62#ifndef SFNTLY_NO_EXCEPTION
63    throw IndexOutOfBoundException("Requested CMap index is out of bounds.");
64#else
65    return NULL;
66#endif
67  }
68  int32_t platform_id = PlatformId(index);
69  int32_t encoding_id = EncodingId(index);
70  CMapId cmap_id = NewCMapId(platform_id, encoding_id);
71  int32_t offset_ = Offset(index);
72  Ptr<FontDataTable::Builder> cmap_builder =
73      (CMap::Builder::GetBuilder(data_, offset_, cmap_id));
74  if (!cmap_builder) {
75#ifndef SFNTLY_NO_EXCEPTION
76    throw NoSuchElementException("Cannot find builder for requested CMap.");
77#else
78    return NULL;
79#endif
80  }
81  return down_cast<CMapTable::CMap*>(cmap_builder->Build());
82}
83
84CALLER_ATTACH CMapTable::CMap* CMapTable::GetCMap(const int32_t platform_id,
85                                                  const int32_t encoding_id) {
86  return GetCMap(NewCMapId(platform_id, encoding_id));
87}
88
89CALLER_ATTACH CMapTable::CMap*
90CMapTable::GetCMap(const CMapTable::CMapId cmap_id) {
91  CMapIdFilter id_filter(cmap_id);
92  CMapIterator cmap_iterator(this, &id_filter);
93  // There can only be one cmap with a particular CMapId
94  if (cmap_iterator.HasNext()) {
95    Ptr<CMapTable::CMap> cmap;
96    cmap.Attach(cmap_iterator.Next());
97    return cmap.Detach();
98  }
99#ifndef SFNTLY_NO_EXCEPTION
100  throw NoSuchElementException();
101#else
102  return NULL;
103#endif
104}
105
106int32_t CMapTable::Version() {
107  return data_->ReadUShort(Offset::kVersion);
108}
109
110int32_t CMapTable::NumCMaps() {
111  return data_->ReadUShort(Offset::kNumTables);
112}
113
114CMapTable::CMapId CMapTable::GetCMapId(int32_t index) {
115  return NewCMapId(PlatformId(index), EncodingId(index));
116}
117
118int32_t CMapTable::PlatformId(int32_t index) {
119  return data_->ReadUShort(Offset::kEncodingRecordPlatformId +
120                           OffsetForEncodingRecord(index));
121}
122
123int32_t CMapTable::EncodingId(int32_t index) {
124  return data_->ReadUShort(Offset::kEncodingRecordEncodingId +
125                           OffsetForEncodingRecord(index));
126}
127
128int32_t CMapTable::Offset(int32_t index) {
129  return data_->ReadULongAsInt(Offset::kEncodingRecordOffset +
130                               OffsetForEncodingRecord(index));
131}
132
133int32_t CMapTable::OffsetForEncodingRecord(int32_t index) {
134  return Offset::kEncodingRecordStart + index * Offset::kEncodingRecordSize;
135}
136
137CMapTable::CMapId CMapTable::NewCMapId(int32_t platform_id,
138                                       int32_t encoding_id) {
139  CMapId result;
140  result.platform_id = platform_id;
141  result.encoding_id = encoding_id;
142  return result;
143}
144
145CMapTable::CMapId CMapTable::NewCMapId(const CMapId& obj) {
146  CMapId result;
147  result.platform_id = obj.platform_id;
148  result.encoding_id = obj.encoding_id;
149  return result;
150}
151
152/******************************************************************************
153 * CMapTable::CMapIterator class
154 ******************************************************************************/
155CMapTable::CMapIterator::CMapIterator(CMapTable* table,
156                                      const CMapFilter* filter)
157    : table_index_(0), filter_(filter), table_(table) {
158}
159
160bool CMapTable::CMapIterator::HasNext() {
161  if (!filter_) {
162    if (table_index_ < table_->NumCMaps()) {
163      return true;
164    }
165    return false;
166  }
167
168  for (; table_index_ < table_->NumCMaps(); ++table_index_) {
169    if (filter_->accept(table_->GetCMapId(table_index_))) {
170      return true;
171    }
172  }
173  return false;
174}
175
176CALLER_ATTACH CMapTable::CMap* CMapTable::CMapIterator::Next() {
177  if (!HasNext()) {
178#ifndef SFNTLY_NO_EXCEPTION
179    throw NoSuchElementException();
180#else
181    return NULL;
182#endif
183  }
184  CMapPtr next_cmap;
185  next_cmap.Attach(table_->GetCMap(table_index_++));
186  if (next_cmap == NULL) {
187#ifndef SFNTLY_NO_EXCEPTION
188    throw NoSuchElementException("Error during the creation of the CMap");
189#else
190    return NULL;
191#endif
192  }
193  return next_cmap.Detach();
194}
195
196/******************************************************************************
197 * CMapTable::CMapId class
198 ******************************************************************************/
199
200/******************************************************************************
201 * CMapTable::CMapIdComparator class
202 ******************************************************************************/
203
204bool CMapTable::CMapIdComparator::operator()(const CMapId& lhs,
205                                             const CMapId& rhs) const {
206  return ((lhs.platform_id << 8 | lhs.encoding_id) >
207      (rhs.platform_id << 8 | rhs.encoding_id));
208}
209
210/******************************************************************************
211 * CMapTable::CMapIdFilter class
212 ******************************************************************************/
213CMapTable::CMapIdFilter::CMapIdFilter(const CMapId wanted_id)
214    : wanted_id_(wanted_id),
215      comparator_(NULL) {
216}
217
218CMapTable::CMapIdFilter::CMapIdFilter(const CMapId wanted_id,
219                                      const CMapIdComparator* comparator)
220    : wanted_id_(wanted_id),
221      comparator_(comparator) {
222}
223
224bool CMapTable::CMapIdFilter::accept(const CMapId& cmap_id) const {
225  if (!comparator_)
226    return wanted_id_ == cmap_id;
227  return (*comparator_)(wanted_id_, cmap_id);
228}
229
230/******************************************************************************
231 * CMapTable::CMap class
232 ******************************************************************************/
233CMapTable::CMap::CMap(ReadableFontData* data, int32_t format,
234                      const CMapId& cmap_id)
235    : SubTable(data), format_(format), cmap_id_(cmap_id) {
236}
237
238CMapTable::CMap::~CMap() {
239}
240
241/******************************************************************************
242 * CMapTable::CMap::Builder class
243 ******************************************************************************/
244CMapTable::CMap::Builder::~Builder() {
245}
246
247CALLER_ATTACH CMapTable::CMap::Builder*
248    CMapTable::CMap::Builder::GetBuilder(ReadableFontData* data, int32_t offset,
249                                         const CMapId& cmap_id) {
250  // NOT IMPLEMENTED: Java enum value validation
251  int32_t format = data->ReadUShort(offset);
252  CMapBuilderPtr builder;
253  switch (format) {
254    case CMapFormat::kFormat0:
255      builder.Attach(CMapFormat0::Builder::NewInstance(data, offset, cmap_id));
256      break;
257    case CMapFormat::kFormat2:
258#if defined (SFNTLY_DEBUG_CMAP)
259      fprintf(stderr, "Requesting Format2 builder, but it's unsupported; "
260              "returning NULL\n");
261#endif
262      break;
263    case CMapFormat::kFormat4:
264      builder.Attach(CMapFormat4::Builder::NewInstance(data, offset, cmap_id));
265      break;
266    default:
267#ifdef SFNTLY_DEBUG_CMAP
268      fprintf(stderr, "Unknown builder format requested\n");
269#endif
270      break;
271  }
272  return builder.Detach();
273}
274
275CALLER_ATTACH CMapTable::CMap::Builder*
276CMapTable::CMap::Builder::GetBuilder(int32_t format, const CMapId& cmap_id) {
277  Ptr<CMapTable::CMap::Builder> builder;
278  switch (format) {
279    case CMapFormat::kFormat0:
280      builder.Attach(CMapFormat0::Builder::NewInstance(cmap_id));
281      break;
282    case CMapFormat::kFormat2:
283#if defined (SFNTLY_DEBUG_CMAP)
284      fprintf(stderr, "Requesting Format2 builder, but it's unsupported; "
285              "returning NULL\n");
286#endif
287      break;
288    case CMapFormat::kFormat4:
289      builder.Attach(CMapFormat4::Builder::NewInstance(cmap_id));
290      break;
291    default:
292#ifdef SFNTLY_DEBUG_CMAP
293      fprintf(stderr, "Unknown builder format requested\n");
294#endif
295      break;
296  }
297  return builder.Detach();
298}
299
300CMapTable::CMap::Builder::Builder(ReadableFontData* data,
301                                  int32_t format,
302                                  const CMapId& cmap_id)
303    : SubTable::Builder(data),
304      format_(format),
305      cmap_id_(cmap_id),
306      language_(0) {
307}
308
309CMapTable::CMap::Builder::Builder(WritableFontData* data,
310                                  int32_t format,
311                                  const CMapId& cmap_id)
312    : SubTable::Builder(data),
313      format_(format),
314      cmap_id_(cmap_id),
315      language_(0) {
316}
317
318int32_t CMapTable::CMap::Builder::SubSerialize(WritableFontData* new_data) {
319  return InternalReadData()->CopyTo(new_data);
320}
321
322bool CMapTable::CMap::Builder::SubReadyToSerialize() {
323  return true;
324}
325
326int32_t CMapTable::CMap::Builder::SubDataSizeToSerialize() {
327  ReadableFontDataPtr read_data = InternalReadData();
328  if (!read_data)
329    return 0;
330  return read_data->Length();
331}
332
333void CMapTable::CMap::Builder::SubDataSet() {
334  // NOP
335}
336
337/******************************************************************************
338 * CMapTable::CMapFormat0
339 ******************************************************************************/
340CMapTable::CMapFormat0::~CMapFormat0() {
341}
342
343int32_t CMapTable::CMapFormat0::Language() {
344  return 0;
345}
346
347int32_t CMapTable::CMapFormat0::GlyphId(int32_t character) {
348  if (character < 0 || character > 255) {
349    return CMapTable::NOTDEF;
350  }
351  return data_->ReadUByte(character + Offset::kFormat0GlyphIdArray);
352}
353
354CMapTable::CMapFormat0::CMapFormat0(ReadableFontData* data,
355                                    const CMapId& cmap_id)
356    : CMap(data, CMapFormat::kFormat0, cmap_id) {
357}
358
359CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat0::Iterator() {
360  return new CMapTable::CMapFormat0::CharacterIterator(0, 0xff);
361}
362
363
364/******************************************************************************
365 * CMapTable::CMapFormat0::CharacterIterator
366 ******************************************************************************/
367CMapTable::CMapFormat0::CharacterIterator::CharacterIterator(int32_t start,
368                                                             int32_t end)
369    : character_(start),
370    max_character_(end) {
371}
372
373CMapTable::CMapFormat0::CharacterIterator::~CharacterIterator() {}
374
375bool CMapTable::CMapFormat0::CharacterIterator::HasNext() {
376  return character_ < max_character_;
377}
378
379int32_t CMapTable::CMapFormat0::CharacterIterator::Next() {
380  if (HasNext())
381    return character_++;
382#ifndef SFNTLY_NO_EXCEPTION
383  throw NoSuchElementException("No more characters to iterate.");
384#endif
385  return -1;
386}
387
388/******************************************************************************
389 * CMapTable::CMapFormat0::Builder
390 ******************************************************************************/
391// static
392CALLER_ATTACH CMapTable::CMapFormat0::Builder*
393CMapTable::CMapFormat0::Builder::NewInstance(WritableFontData* data,
394                                             int32_t offset,
395                                             const CMapId& cmap_id) {
396  WritableFontDataPtr wdata;
397  if (data) {
398    wdata.Attach(down_cast<WritableFontData*>(
399        data->Slice(offset,
400                    data->ReadUShort(offset + Offset::kFormat0Length))));
401  }
402  return new Builder(wdata, CMapFormat::kFormat0, cmap_id);
403}
404
405// static
406CALLER_ATTACH CMapTable::CMapFormat0::Builder*
407CMapTable::CMapFormat0::Builder::NewInstance(ReadableFontData* data,
408                                             int32_t offset,
409                                             const CMapId& cmap_id) {
410  ReadableFontDataPtr rdata;
411  if (data) {
412    rdata.Attach(down_cast<ReadableFontData*>(
413        data->Slice(offset,
414                    data->ReadUShort(offset + Offset::kFormat0Length))));
415  }
416  return new Builder(rdata, CMapFormat::kFormat0, cmap_id);
417}
418
419// static
420CALLER_ATTACH CMapTable::CMapFormat0::Builder*
421CMapTable::CMapFormat0::Builder::NewInstance(const CMapId& cmap_id) {
422  return new Builder(cmap_id);
423}
424
425// Always call NewInstance instead of the constructor for creating a new builder
426// object! This refactoring avoids memory leaks when slicing the font data.
427CMapTable::CMapFormat0::Builder::Builder(WritableFontData* data, int32_t offset,
428                                         const CMapId& cmap_id)
429    : CMapTable::CMap::Builder(data, CMapFormat::kFormat0, cmap_id) {
430  UNREFERENCED_PARAMETER(offset);
431}
432
433CMapTable::CMapFormat0::Builder::Builder(
434    ReadableFontData* data,
435    int32_t offset,
436    const CMapId& cmap_id)
437    : CMapTable::CMap::Builder(data, CMapFormat::kFormat0, cmap_id) {
438  UNREFERENCED_PARAMETER(offset);
439}
440
441CMapTable::CMapFormat0::Builder::Builder(const CMapId& cmap_id)
442    : CMap::Builder(reinterpret_cast<ReadableFontData*>(NULL),
443                    CMapFormat::kFormat0,
444                    cmap_id) {
445}
446
447CMapTable::CMapFormat0::Builder::~Builder() {
448}
449
450CALLER_ATTACH FontDataTable*
451    CMapTable::CMapFormat0::Builder::SubBuildTable(ReadableFontData* data) {
452  FontDataTablePtr table = new CMapFormat0(data, cmap_id());
453  return table.Detach();
454}
455
456/******************************************************************************
457 * CMapTable::CMapFormat2
458 ******************************************************************************/
459CMapTable::CMapFormat2::~CMapFormat2() {
460}
461
462int32_t CMapTable::CMapFormat2::Language() {
463  return 0;
464}
465
466int32_t CMapTable::CMapFormat2::GlyphId(int32_t character) {
467  if (character > 0xffff) {
468    return CMapTable::NOTDEF;
469  }
470
471  uint32_t c = ToBE32(character);
472  byte_t high_byte = (c >> 8) & 0xff;
473  byte_t low_byte = c & 0xff;
474  int32_t offset = SubHeaderOffset(high_byte);
475
476  if (offset == 0) {
477    low_byte = high_byte;
478    high_byte = 0;
479  }
480
481  int32_t first_code = FirstCode(high_byte);
482  int32_t entry_count = EntryCount(high_byte);
483
484  if (low_byte < first_code || low_byte >= first_code + entry_count) {
485    return CMapTable::NOTDEF;
486  }
487
488  int32_t id_range_offset = IdRangeOffset(high_byte);
489
490  // position of idRangeOffset + value of idRangeOffset + index for low byte
491  // = firstcode
492  int32_t p_location = (offset + Offset::kFormat2SubHeader_idRangeOffset) +
493      id_range_offset +
494      (low_byte - first_code) * DataSize::kUSHORT;
495  int p = data_->ReadUShort(p_location);
496  if (p == 0) {
497    return CMapTable::NOTDEF;
498  }
499
500  if (offset == 0) {
501    return p;
502  }
503  int id_delta = IdDelta(high_byte);
504  return (p + id_delta) % 65536;
505}
506
507int32_t CMapTable::CMapFormat2::BytesConsumed(int32_t character) {
508  uint32_t c = ToBE32(character);
509  int32_t high_byte = (c >> 8) & 0xff;
510  int32_t offset = SubHeaderOffset(high_byte);
511  return (offset == 0) ? 1 : 2;
512}
513
514CMapTable::CMapFormat2::CMapFormat2(ReadableFontData* data,
515                                    const CMapId& cmap_id)
516    : CMap(data, CMapFormat::kFormat2, cmap_id) {
517}
518
519int32_t CMapTable::CMapFormat2::SubHeaderOffset(int32_t sub_header_index) {
520  return data_->ReadUShort(Offset::kFormat2SubHeaderKeys +
521                           sub_header_index * DataSize::kUSHORT);
522}
523
524int32_t CMapTable::CMapFormat2::FirstCode(int32_t sub_header_index) {
525  int32_t sub_header_offset = SubHeaderOffset(sub_header_index);
526  return data_->ReadUShort(sub_header_offset +
527                           Offset::kFormat2SubHeaderKeys +
528                           Offset::kFormat2SubHeader_firstCode);
529}
530
531int32_t CMapTable::CMapFormat2::EntryCount(int32_t sub_header_index) {
532  int32_t sub_header_offset = SubHeaderOffset(sub_header_index);
533  return data_->ReadUShort(sub_header_offset +
534                           Offset::kFormat2SubHeaderKeys +
535                           Offset::kFormat2SubHeader_entryCount);
536}
537
538int32_t CMapTable::CMapFormat2::IdRangeOffset(int32_t sub_header_index) {
539  int32_t sub_header_offset = SubHeaderOffset(sub_header_index);
540  return data_->ReadUShort(sub_header_offset +
541                           Offset::kFormat2SubHeaderKeys +
542                           Offset::kFormat2SubHeader_idRangeOffset);
543}
544
545int32_t CMapTable::CMapFormat2::IdDelta(int32_t sub_header_index) {
546  int32_t sub_header_offset = SubHeaderOffset(sub_header_index);
547  return data_->ReadUShort(sub_header_offset +
548                           Offset::kFormat2SubHeaderKeys +
549                           Offset::kFormat2SubHeader_idDelta);
550}
551
552CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat2::Iterator() {
553  // UNIMPLEMENTED
554  return NULL;
555}
556
557/******************************************************************************
558 * CMapTable::CMapFormat2::Builder
559 ******************************************************************************/
560CMapTable::CMapFormat2::Builder::Builder(WritableFontData* data,
561                                         int32_t offset,
562                                         const CMapId& cmap_id)
563    : CMapTable::CMap::Builder(data ? down_cast<WritableFontData*>(
564                                   data->Slice(offset, data->ReadUShort(
565                                       offset + Offset::kFormat0Length)))
566                               : reinterpret_cast<WritableFontData*>(NULL),
567                               CMapFormat::kFormat2, cmap_id) {
568  // TODO(arthurhsu): FIXIT: heavy lifting and leak, need fix.
569}
570
571CMapTable::CMapFormat2::Builder::Builder(ReadableFontData* data,
572                                         int32_t offset,
573                                         const CMapId& cmap_id)
574    : CMapTable::CMap::Builder(data ? down_cast<ReadableFontData*>(
575                                   data->Slice(offset, data->ReadUShort(
576                                       offset + Offset::kFormat0Length)))
577                               : reinterpret_cast<ReadableFontData*>(NULL),
578                               CMapFormat::kFormat2, cmap_id) {
579  // TODO(arthurhsu): FIXIT: heavy lifting and leak, need fix.
580}
581
582CMapTable::CMapFormat2::Builder::~Builder() {
583}
584
585CALLER_ATTACH FontDataTable*
586    CMapTable::CMapFormat2::Builder::SubBuildTable(ReadableFontData* data) {
587  FontDataTablePtr table = new CMapFormat2(data, cmap_id());
588  return table.Detach();
589}
590
591/******************************************************************************
592 * CMapTable::CMapFormat4
593 ******************************************************************************/
594CMapTable::CMapFormat4::CMapFormat4(ReadableFontData* data,
595                                    const CMapId& cmap_id)
596    : CMap(data, CMapFormat::kFormat4, cmap_id),
597      seg_count_(SegCount(data)),
598      start_code_offset_(StartCodeOffset(seg_count_)),
599      id_delta_offset_(IdDeltaOffset(seg_count_)),
600      glyph_id_array_offset_(GlyphIdArrayOffset(seg_count_)) {
601}
602
603CMapTable::CMapFormat4::~CMapFormat4() {
604}
605
606int32_t CMapTable::CMapFormat4::GlyphId(int32_t character) {
607  int32_t segment = data_->SearchUShort(StartCodeOffset(seg_count_),
608                                        DataSize::kUSHORT,
609                                        Offset::kFormat4EndCount,
610                                        DataSize::kUSHORT,
611                                        seg_count_,
612                                        character);
613  if (segment == -1) {
614    return CMapTable::NOTDEF;
615  }
616  int32_t start_code = StartCode(segment);
617  return RetrieveGlyphId(segment, start_code, character);
618}
619
620int32_t CMapTable::CMapFormat4::RetrieveGlyphId(int32_t segment,
621                                                int32_t start_code,
622                                                int32_t character) {
623  if (character < start_code) {
624    return CMapTable::NOTDEF;
625  }
626  int32_t id_range_offset = IdRangeOffset(segment);
627  if (id_range_offset == 0) {
628    return (character + IdDelta(segment)) % 65536;
629  }
630  return data_->ReadUShort(id_range_offset +
631                           IdRangeOffsetLocation(segment) +
632                           2 * (character - start_code));
633}
634
635int32_t CMapTable::CMapFormat4::seg_count() {
636  return seg_count_;
637}
638
639int32_t CMapTable::CMapFormat4::Length() {
640  return Length(data_);
641}
642
643int32_t CMapTable::CMapFormat4::StartCode(int32_t segment) {
644  if (!IsValidIndex(segment)) {
645    return -1;
646  }
647  return StartCode(data_.p_, seg_count_, segment);
648}
649
650// static
651int32_t CMapTable::CMapFormat4::Language(ReadableFontData* data) {
652  int32_t language = data->ReadUShort(Offset::kFormat4Language);
653  return language;
654}
655
656// static
657int32_t CMapTable::CMapFormat4::Length(ReadableFontData* data) {
658  int32_t length = data->ReadUShort(Offset::kFormat4Length);
659  return length;
660}
661
662// static
663int32_t CMapTable::CMapFormat4::SegCount(ReadableFontData* data) {
664  int32_t seg_count = data->ReadUShort(Offset::kFormat4SegCountX2) / 2;
665  return seg_count;
666}
667
668// static
669int32_t CMapTable::CMapFormat4::StartCode(ReadableFontData* data,
670                                          int32_t seg_count,
671                                          int32_t index) {
672  int32_t start_code = data->ReadUShort(StartCodeOffset(seg_count) +
673                                        index * DataSize::kUSHORT);
674  return start_code;
675}
676
677// static
678int32_t CMapTable::CMapFormat4::StartCodeOffset(int32_t seg_count) {
679  int32_t start_code_offset = Offset::kFormat4EndCount +
680      (seg_count + 1) * DataSize::kUSHORT;
681  return start_code_offset;
682}
683
684// static
685int32_t CMapTable::CMapFormat4::EndCode(ReadableFontData* data,
686                                        int32_t seg_count,
687                                        int32_t index) {
688  UNREFERENCED_PARAMETER(seg_count);
689  int32_t end_code = data->ReadUShort(Offset::kFormat4EndCount +
690                                      index * DataSize::kUSHORT);
691  return end_code;
692}
693
694// static
695int32_t CMapTable::CMapFormat4::IdDelta(ReadableFontData* data,
696                                        int32_t seg_count,
697                                        int32_t index) {
698  int32_t id_delta = data->ReadUShort(IdDeltaOffset(seg_count) +
699                                      index * DataSize::kUSHORT);
700  return id_delta;
701}
702
703// static
704int32_t CMapTable::CMapFormat4::IdDeltaOffset(int32_t seg_count) {
705  int32_t id_delta_offset =
706      Offset::kFormat4EndCount + (2 * seg_count + 1) * DataSize::kUSHORT;
707  return id_delta_offset;
708}
709
710// static
711int32_t CMapTable::CMapFormat4::IdRangeOffset(ReadableFontData* data,
712                                              int32_t seg_count,
713                                              int32_t index) {
714  int32_t id_range_offset =
715      data->ReadUShort(IdRangeOffsetOffset(seg_count)
716                       + index * DataSize::kUSHORT);
717  return id_range_offset;
718}
719
720// static
721int32_t CMapTable::CMapFormat4::IdRangeOffsetOffset(int32_t seg_count) {
722  int32_t id_range_offset_offset =
723      Offset::kFormat4EndCount + (2 * seg_count + 1) * DataSize::kUSHORT +
724      seg_count * DataSize::kSHORT;
725  return id_range_offset_offset;
726}
727
728// static
729int32_t CMapTable::CMapFormat4::GlyphIdArrayOffset(int32_t seg_count) {
730  int32_t glyph_id_array_offset =
731      Offset::kFormat4EndCount + (3 * seg_count + 1) * DataSize::kUSHORT +
732      seg_count * DataSize::kSHORT;
733  return glyph_id_array_offset;
734}
735
736int32_t CMapTable::CMapFormat4::EndCode(int32_t segment) {
737  if (IsValidIndex(segment)) {
738    return EndCode(data_, seg_count_, segment);
739  }
740#if defined (SFNTLY_NO_EXCEPTION)
741  return -1;
742#else
743  throw IllegalArgumentException();
744#endif
745}
746
747bool CMapTable::CMapFormat4::IsValidIndex(int32_t segment) {
748  if (segment < 0 || segment >= seg_count_) {
749#if defined (SFNTLY_NO_EXCEPTION)
750    return false;
751#else
752    throw IllegalArgumentException();
753#endif
754  }
755  return true;
756}
757
758int32_t CMapTable::CMapFormat4::IdDelta(int32_t segment) {
759  if (IsValidIndex(segment))
760    return IdDelta(data_, seg_count_, segment);
761  return -1;
762}
763
764int32_t CMapTable::CMapFormat4::IdRangeOffset(int32_t segment) {
765  if (IsValidIndex(segment))
766    return data_->ReadUShort(IdRangeOffsetLocation(segment));
767  return -1;
768}
769
770int32_t CMapTable::CMapFormat4::IdRangeOffsetLocation(int32_t segment) {
771  if (IsValidIndex(segment))
772    return IdRangeOffsetOffset(seg_count_) + segment * DataSize::kUSHORT;
773  return -1;
774}
775
776int32_t CMapTable::CMapFormat4::GlyphIdArray(int32_t index) {
777  return data_->ReadUShort(glyph_id_array_offset_ + index * DataSize::kUSHORT);
778}
779
780int32_t CMapTable::CMapFormat4::Language() {
781  return Language(data_);
782}
783
784
785CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat4::Iterator() {
786  return new CharacterIterator(this);
787}
788
789/******************************************************************************
790 * CMapTable::CMapFormat4::CharacterIterator class
791 ******************************************************************************/
792CMapTable::CMapFormat4::CharacterIterator::CharacterIterator(
793    CMapFormat4* parent)
794    : parent_(parent),
795      segment_index_(0),
796      first_char_in_segment_(-1),
797      last_char_in_segment_(-1),
798      next_char_(-1),
799      next_char_set_(false) {
800}
801
802bool CMapTable::CMapFormat4::CharacterIterator::HasNext() {
803  if (next_char_set_)
804    return true;
805  while (segment_index_ < parent_->seg_count_) {
806    if (first_char_in_segment_ < 0) {
807      first_char_in_segment_ = parent_->StartCode(segment_index_);
808      last_char_in_segment_ = parent_->EndCode(segment_index_);
809      next_char_ = first_char_in_segment_;
810      next_char_set_ = true;
811      return true;
812    }
813    if (next_char_ < last_char_in_segment_) {
814      next_char_++;
815      next_char_set_ = true;
816      return true;
817    }
818    segment_index_++;
819    first_char_in_segment_ = -1;
820  }
821  return false;
822}
823
824int32_t CMapTable::CMapFormat4::CharacterIterator::Next() {
825  if (!next_char_set_) {
826    if (!HasNext()) {
827#if defined (SFNTLY_NO_EXCEPTION)
828      return -1;
829#else
830      throw NoSuchElementException("No more characters to iterate.");
831#endif
832    }
833  }
834  next_char_set_ = false;
835  return next_char_;
836}
837
838/******************************************************************************
839 * CMapTable::CMapFormat4::Builder::Segment class
840 ******************************************************************************/
841CMapTable::CMapFormat4::Builder::Segment::Segment() {}
842
843CMapTable::CMapFormat4::Builder::Segment::Segment(Segment* other)
844    : start_count_(other->start_count_),
845      end_count_(other->end_count_),
846      id_delta_(other->id_delta_),
847      id_range_offset_(other->id_range_offset_) {
848}
849
850CMapTable::CMapFormat4::Builder::Segment::Segment(int32_t start_count,
851                                                  int32_t end_count,
852                                                  int32_t id_delta,
853                                                  int32_t id_range_offset)
854    : start_count_(start_count),
855      end_count_(end_count),
856      id_delta_(id_delta),
857      id_range_offset_(id_range_offset) {
858}
859
860CMapTable::CMapFormat4::Builder::Segment::~Segment() {}
861
862int32_t CMapTable::CMapFormat4::Builder::Segment::start_count() {
863  return start_count_;
864}
865
866void
867CMapTable::CMapFormat4::Builder::Segment::set_start_count(int32_t start_count) {
868  start_count_ = start_count;
869}
870
871int32_t CMapTable::CMapFormat4::Builder::Segment::end_count() {
872  return end_count_;
873}
874
875void
876CMapTable::CMapFormat4::Builder::Segment::set_end_count(int32_t end_count) {
877  end_count_ = end_count;
878}
879
880int32_t CMapTable::CMapFormat4::Builder::Segment::id_delta() {
881  return id_delta_;
882}
883
884void
885CMapTable::CMapFormat4::Builder::Segment::set_id_delta(int32_t id_delta) {
886  id_delta_ = id_delta;
887}
888
889int32_t CMapTable::CMapFormat4::Builder::Segment::id_range_offset() {
890  return id_range_offset_;
891}
892
893void
894CMapTable::CMapFormat4::Builder::Segment::
895set_id_range_offset(int32_t id_range_offset) {
896  id_range_offset_ = id_range_offset;
897}
898
899// static
900CALLER_ATTACH SegmentList*
901CMapTable::CMapFormat4::Builder::Segment::DeepCopy(SegmentList* original) {
902  SegmentList* list = new SegmentList;
903  for (SegmentList::iterator it = original->begin(),
904           e = original->end(); it != e; ++it) {
905    list->push_back(*it);
906  }
907  return list;
908}
909
910/******************************************************************************
911 * CMapTable::CMapFormat4::Builder class
912 ******************************************************************************/
913CALLER_ATTACH CMapTable::CMapFormat4::Builder*
914CMapTable::CMapFormat4::Builder::NewInstance(ReadableFontData* data,
915                                             int32_t offset,
916                                             const CMapId& cmap_id) {
917  ReadableFontDataPtr rdata;
918  if (data) {
919    rdata.Attach
920        (down_cast<ReadableFontData*>
921         (data->Slice(offset,
922                      data->ReadUShort(offset + Offset::kFormat4Length))));
923  }
924  return new Builder(rdata, CMapFormat::kFormat4, cmap_id);
925}
926
927CALLER_ATTACH CMapTable::CMapFormat4::Builder*
928CMapTable::CMapFormat4::Builder::NewInstance(WritableFontData* data,
929                                             int32_t offset,
930                                             const CMapId& cmap_id) {
931  WritableFontDataPtr wdata;
932  if (data) {
933    wdata.Attach
934        (down_cast<WritableFontData*>
935         (data->Slice(offset,
936                      data->ReadUShort(offset + Offset::kFormat4Length))));
937  }
938  return new Builder(wdata, CMapFormat::kFormat4, cmap_id);
939}
940
941CALLER_ATTACH CMapTable::CMapFormat4::Builder*
942CMapTable::CMapFormat4::Builder::NewInstance(const CMapId& cmap_id) {
943  return new Builder(cmap_id);
944}
945
946CMapTable::CMapFormat4::Builder::Builder(ReadableFontData* data, int32_t offset,
947                                         const CMapId& cmap_id)
948    : CMap::Builder(data, CMapFormat::kFormat4, cmap_id) {
949  UNREFERENCED_PARAMETER(offset);
950}
951
952CMapTable::CMapFormat4::Builder::Builder(WritableFontData* data, int32_t offset,
953                                         const CMapId& cmap_id)
954    : CMap::Builder(data, CMapFormat::kFormat4, cmap_id) {
955  UNREFERENCED_PARAMETER(offset);
956}
957
958CMapTable::CMapFormat4::Builder::Builder(SegmentList* segments,
959                                         IntegerList* glyph_id_array,
960                                         const CMapId& cmap_id)
961    : CMap::Builder(reinterpret_cast<ReadableFontData*>(NULL),
962                    CMapFormat::kFormat4, cmap_id),
963      segments_(segments->begin(), segments->end()),
964      glyph_id_array_(glyph_id_array->begin(), glyph_id_array->end()) {
965  set_model_changed();
966}
967
968CMapTable::CMapFormat4::Builder::Builder(const CMapId& cmap_id)
969    : CMap::Builder(reinterpret_cast<ReadableFontData*>(NULL),
970                    CMapFormat::kFormat4, cmap_id) {
971}
972
973CMapTable::CMapFormat4::Builder::~Builder() {}
974
975void CMapTable::CMapFormat4::Builder::Initialize(ReadableFontData* data) {
976  if (data == NULL || data->Length() == 0)
977    return;
978
979  // build segments
980  int32_t seg_count = CMapFormat4::SegCount(data);
981  for (int32_t index = 0; index < seg_count; ++index) {
982    Ptr<Segment> segment = new Segment;
983    segment->set_start_count(CMapFormat4::StartCode(data, seg_count, index));
984#if defined SFNTLY_DEBUG_CMAP
985    fprintf(stderr, "Segment %d; start %d\n", index, segment->start_count());
986#endif
987    segment->set_end_count(CMapFormat4::EndCode(data, seg_count, index));
988    segment->set_id_delta(CMapFormat4::IdDelta(data, seg_count, index));
989    segment->set_id_range_offset(CMapFormat4::IdRangeOffset(data,
990                                                           seg_count,
991                                                           index));
992    segments_.push_back(segment);
993  }
994
995  // build glyph id array
996  int32_t glyph_id_array_offset = CMapFormat4::GlyphIdArrayOffset(seg_count);
997  int32_t glyph_id_array_length =
998      (CMapFormat4::Length(data) - glyph_id_array_offset)
999      / DataSize::kUSHORT;
1000  fprintf(stderr, "id array size %d\n", glyph_id_array_length);
1001  for (int32_t i = 0; i < glyph_id_array_length; i += DataSize::kUSHORT) {
1002    glyph_id_array_.push_back(data->ReadUShort(glyph_id_array_offset + i));
1003  }
1004}
1005
1006SegmentList* CMapTable::CMapFormat4::Builder::segments() {
1007  if (segments_.empty()) {
1008    Initialize(InternalReadData());
1009    set_model_changed();
1010  }
1011  return &segments_;
1012}
1013
1014void CMapTable::CMapFormat4::Builder::set_segments(SegmentList* segments) {
1015  segments_.assign(segments->begin(), segments->end());
1016  set_model_changed();
1017}
1018
1019IntegerList* CMapTable::CMapFormat4::Builder::glyph_id_array() {
1020  if (glyph_id_array_.empty()) {
1021    Initialize(InternalReadData());
1022    set_model_changed();
1023  }
1024  return &glyph_id_array_;
1025}
1026
1027void CMapTable::CMapFormat4::Builder::
1028set_glyph_id_array(IntegerList* glyph_id_array) {
1029  glyph_id_array_.assign(glyph_id_array->begin(), glyph_id_array->end());
1030  set_model_changed();
1031}
1032
1033CALLER_ATTACH FontDataTable*
1034CMapTable::CMapFormat4::Builder::SubBuildTable(ReadableFontData* data) {
1035  FontDataTablePtr table = new CMapFormat4(data, cmap_id());
1036  return table.Detach();
1037}
1038
1039void CMapTable::CMapFormat4::Builder::SubDataSet() {
1040  segments_.clear();
1041  glyph_id_array_.clear();
1042  set_model_changed();
1043}
1044
1045int32_t CMapTable::CMapFormat4::Builder::SubDataSizeToSerialize() {
1046  if (!model_changed()) {
1047    return CMap::Builder::SubDataSizeToSerialize();
1048  }
1049  int32_t size = Offset::kFormat4FixedSize + segments_.size()
1050      * (3 * DataSize::kUSHORT + DataSize::kSHORT)
1051      + glyph_id_array_.size() * DataSize::kSHORT;
1052  return size;
1053}
1054
1055bool CMapTable::CMapFormat4::Builder::SubReadyToSerialize() {
1056  if (!model_changed()) {
1057    return CMap::Builder::SubReadyToSerialize();
1058  }
1059  if (!segments()->empty()) {
1060    return true;
1061  }
1062  return false;
1063}
1064
1065int32_t
1066CMapTable::CMapFormat4::Builder::SubSerialize(WritableFontData* new_data) {
1067  if (!model_changed()) {
1068    return CMap::Builder::SubSerialize(new_data);
1069  }
1070  int32_t index = 0;
1071  index += new_data->WriteUShort(index, CMapFormat::kFormat4);
1072  index += DataSize::kUSHORT;  // length - write this at the end
1073  index += new_data->WriteUShort(index, language());
1074
1075  int32_t seg_count = segments_.size();
1076  index += new_data->WriteUShort(index, seg_count * 2);
1077  int32_t log2_seg_count = FontMath::Log2(seg_count);
1078  int32_t search_range = 1 << (log2_seg_count + 1);
1079  index += new_data->WriteUShort(index, search_range);
1080  int32_t entry_selector = log2_seg_count;
1081  index += new_data->WriteUShort(index, entry_selector);
1082  int32_t range_shift = 2 * seg_count - search_range;
1083  index += new_data->WriteUShort(index, range_shift);
1084
1085  for (int32_t i = 0; i < seg_count; ++i) {
1086    index += new_data->WriteUShort(index, segments_[i]->end_count());
1087  }
1088  index += new_data->WriteUShort(index, 0);  // reserved ushort
1089  for (int32_t i = 0; i < seg_count; ++i) {
1090#if defined SFNTLY_DEBUG_CMAP
1091    fprintf(stderr, "Segment %d; start %d\n", i, segments_[i]->start_count());
1092#endif
1093    index += new_data->WriteUShort(index, segments_[i]->start_count());
1094  }
1095  for (int32_t i = 0; i < seg_count; ++i) {
1096    index += new_data->WriteShort(index, segments_[i]->id_delta());
1097  }
1098  for (int32_t i = 0; i < seg_count; ++i) {
1099    index += new_data->WriteUShort(index, segments_[i]->id_range_offset());
1100  }
1101
1102#if defined SFNTLY_DEBUG_CMAP
1103  fprintf(stderr, "Glyph id array size %lu\n", glyph_id_array_.size());
1104#endif
1105  for (size_t i = 0; i < glyph_id_array_.size(); ++i) {
1106    index += new_data->WriteUShort(index, glyph_id_array_[i]);
1107  }
1108
1109  new_data->WriteUShort(Offset::kFormat4Length, index);
1110  return index;
1111}
1112
1113/******************************************************************************
1114 * CMapTable::Builder class
1115 ******************************************************************************/
1116CMapTable::Builder::Builder(Header* header, WritableFontData* data)
1117    : SubTableContainerTable::Builder(header, data), version_(0) {
1118}
1119
1120CMapTable::Builder::Builder(Header* header, ReadableFontData* data)
1121    : SubTableContainerTable::Builder(header, data), version_(0) {
1122}
1123
1124CMapTable::Builder::~Builder() {
1125}
1126
1127int32_t CMapTable::Builder::SubSerialize(WritableFontData* new_data) {
1128  int32_t size = new_data->WriteUShort(CMapTable::Offset::kVersion,
1129                                       version_);
1130  size += new_data->WriteUShort(CMapTable::Offset::kNumTables,
1131                                GetCMapBuilders()->size());
1132
1133  int32_t index_offset = size;
1134  size += GetCMapBuilders()->size() * CMapTable::Offset::kEncodingRecordSize;
1135  for (CMapBuilderMap::iterator it = GetCMapBuilders()->begin(),
1136           e = GetCMapBuilders()->end(); it != e; ++it) {
1137    CMapBuilderPtr b = it->second;
1138    // header entry
1139    index_offset += new_data->WriteUShort(index_offset, b->platform_id());
1140    index_offset += new_data->WriteUShort(index_offset, b->encoding_id());
1141    index_offset += new_data->WriteULong(index_offset, size);
1142
1143    // cmap
1144    FontDataPtr slice;
1145    slice.Attach(new_data->Slice(size));
1146    size += b->SubSerialize(down_cast<WritableFontData*>(slice.p_));
1147  }
1148  return size;
1149}
1150
1151bool CMapTable::Builder::SubReadyToSerialize() {
1152  if (GetCMapBuilders()->empty())
1153    return false;
1154
1155  // check each table
1156  for (CMapBuilderMap::iterator it = GetCMapBuilders()->begin(),
1157           e = GetCMapBuilders()->end(); it != e; ++it) {
1158    if (!it->second->SubReadyToSerialize())
1159      return false;
1160  }
1161  return true;
1162}
1163
1164int32_t CMapTable::Builder::SubDataSizeToSerialize() {
1165  if (GetCMapBuilders()->empty())
1166    return 0;
1167
1168  bool variable = false;
1169  int32_t size = CMapTable::Offset::kEncodingRecordStart +
1170      GetCMapBuilders()->size() * CMapTable::Offset::kEncodingRecordSize;
1171
1172  // calculate size of each table
1173  for (CMapBuilderMap::iterator it = GetCMapBuilders()->begin(),
1174           e = GetCMapBuilders()->end(); it != e; ++it) {
1175    int32_t cmap_size = it->second->SubDataSizeToSerialize();
1176    size += abs(cmap_size);
1177    variable |= cmap_size <= 0;
1178  }
1179  return variable ? -size : size;
1180}
1181
1182void CMapTable::Builder::SubDataSet() {
1183  GetCMapBuilders()->clear();
1184  Table::Builder::set_model_changed();
1185}
1186
1187CALLER_ATTACH FontDataTable*
1188    CMapTable::Builder::SubBuildTable(ReadableFontData* data) {
1189  FontDataTablePtr table = new CMapTable(header(), data);
1190  return table.Detach();
1191}
1192
1193CALLER_ATTACH CMapTable::Builder*
1194    CMapTable::Builder::CreateBuilder(Header* header,
1195                                      WritableFontData* data) {
1196  Ptr<CMapTable::Builder> builder;
1197  builder = new CMapTable::Builder(header, data);
1198  return builder.Detach();
1199}
1200
1201// static
1202CALLER_ATTACH CMapTable::CMap::Builder*
1203    CMapTable::Builder::CMapBuilder(ReadableFontData* data, int32_t index) {
1204  if (index < 0 || index > NumCMaps(data)) {
1205#if !defined (SFNTLY_NO_EXCEPTION)
1206    throw IndexOutOfBoundException(
1207              "CMap table is outside of the bounds of the known tables.");
1208#endif
1209    return NULL;
1210  }
1211
1212  int32_t platform_id = data->ReadUShort(Offset::kEncodingRecordPlatformId +
1213                                         OffsetForEncodingRecord(index));
1214  int32_t encoding_id = data->ReadUShort(Offset::kEncodingRecordEncodingId +
1215                                         OffsetForEncodingRecord(index));
1216  int32_t offset = data->ReadULongAsInt(Offset::kEncodingRecordOffset +
1217                                        OffsetForEncodingRecord(index));
1218  return CMap::Builder::GetBuilder(data, offset,
1219                                   NewCMapId(platform_id, encoding_id));
1220}
1221
1222// static
1223int32_t CMapTable::Builder::NumCMaps(ReadableFontData* data) {
1224  if (data == NULL) {
1225    return 0;
1226  }
1227  return data->ReadUShort(Offset::kNumTables);
1228}
1229
1230int32_t CMapTable::Builder::NumCMaps() {
1231  return GetCMapBuilders()->size();
1232}
1233
1234void CMapTable::Builder::Initialize(ReadableFontData* data) {
1235  int32_t num_cmaps = NumCMaps(data);
1236  for (int32_t i = 0; i < num_cmaps; ++i) {
1237    CMapTable::CMap::Builder* cmap_builder = CMapBuilder(data, i);
1238    if (!cmap_builder)
1239      continue;
1240    cmap_builders_[cmap_builder->cmap_id()] = cmap_builder;
1241  }
1242}
1243
1244CMapTable::CMap::Builder* CMapTable::Builder::NewCMapBuilder(
1245    const CMapId& cmap_id,
1246    ReadableFontData* data) {
1247  Ptr<WritableFontData> wfd;
1248  wfd.Attach(WritableFontData::CreateWritableFontData(data->Size()));
1249  data->CopyTo(wfd.p_);
1250  CMapTable::CMapBuilderPtr builder;
1251  builder.Attach(CMap::Builder::GetBuilder(wfd.p_, 0, cmap_id));
1252  CMapBuilderMap* cmap_builders = CMapTable::Builder::GetCMapBuilders();
1253  cmap_builders->insert(std::make_pair(cmap_id, builder.p_));
1254  return builder.Detach();
1255}
1256
1257CMapTable::CMap::Builder*
1258CMapTable::Builder::NewCMapBuilder(int32_t format, const CMapId& cmap_id) {
1259  Ptr<CMapTable::CMap::Builder> cmap_builder;
1260  cmap_builder.Attach(CMap::Builder::GetBuilder(format, cmap_id));
1261  CMapBuilderMap* cmap_builders = CMapTable::Builder::GetCMapBuilders();
1262  cmap_builders->insert(std::make_pair(cmap_id, cmap_builder.p_));
1263  return cmap_builder.Detach();
1264}
1265
1266CMapTable::CMap::Builder*
1267CMapTable::Builder::CMapBuilder(const CMapId& cmap_id) {
1268  CMapBuilderMap* cmap_builders = this->GetCMapBuilders();
1269  CMapBuilderMap::iterator builder = cmap_builders->find(cmap_id);
1270  if (builder != cmap_builders->end())
1271    return builder->second;
1272#ifndef SFNTLY_NO_EXCEPTION
1273  throw NoSuchElementException("No builder found for cmap_id");
1274#else
1275  return NULL;
1276#endif
1277}
1278
1279CMapTable::CMapBuilderMap* CMapTable::Builder::GetCMapBuilders() {
1280  if (cmap_builders_.empty()) {
1281    Initialize(InternalReadData());
1282    set_model_changed();
1283  }
1284  return &cmap_builders_;
1285}
1286
1287}  // namespace sfntly
1288