Home | History | Annotate | Download | only in core
      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 
     34 namespace sfntly {
     35 
     36 const int32_t CMapTable::NOTDEF = 0;
     37 
     38 CMapTable::CMapId CMapTable::WINDOWS_BMP = {
     39   PlatformId::kWindows,
     40   WindowsEncodingId::kUnicodeUCS2
     41 };
     42 CMapTable::CMapId CMapTable::WINDOWS_UCS4 = {
     43   PlatformId::kWindows,
     44   WindowsEncodingId::kUnicodeUCS4
     45 };
     46 CMapTable::CMapId CMapTable::MAC_ROMAN = {
     47   PlatformId::kWindows,
     48   MacintoshEncodingId::kRoman
     49 };
     50 
     51 /******************************************************************************
     52  * CMapTable class
     53  ******************************************************************************/
     54 CMapTable::CMapTable(Header* header, ReadableFontData* data)
     55   : SubTableContainerTable(header, data) {
     56 }
     57 
     58 CMapTable::~CMapTable() {}
     59 
     60 CALLER_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 
     84 CALLER_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 
     89 CALLER_ATTACH CMapTable::CMap*
     90 CMapTable::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 
    106 int32_t CMapTable::Version() {
    107   return data_->ReadUShort(Offset::kVersion);
    108 }
    109 
    110 int32_t CMapTable::NumCMaps() {
    111   return data_->ReadUShort(Offset::kNumTables);
    112 }
    113 
    114 CMapTable::CMapId CMapTable::GetCMapId(int32_t index) {
    115   return NewCMapId(PlatformId(index), EncodingId(index));
    116 }
    117 
    118 int32_t CMapTable::PlatformId(int32_t index) {
    119   return data_->ReadUShort(Offset::kEncodingRecordPlatformId +
    120                            OffsetForEncodingRecord(index));
    121 }
    122 
    123 int32_t CMapTable::EncodingId(int32_t index) {
    124   return data_->ReadUShort(Offset::kEncodingRecordEncodingId +
    125                            OffsetForEncodingRecord(index));
    126 }
    127 
    128 int32_t CMapTable::Offset(int32_t index) {
    129   return data_->ReadULongAsInt(Offset::kEncodingRecordOffset +
    130                                OffsetForEncodingRecord(index));
    131 }
    132 
    133 int32_t CMapTable::OffsetForEncodingRecord(int32_t index) {
    134   return Offset::kEncodingRecordStart + index * Offset::kEncodingRecordSize;
    135 }
    136 
    137 CMapTable::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 
    145 CMapTable::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  ******************************************************************************/
    155 CMapTable::CMapIterator::CMapIterator(CMapTable* table,
    156                                       const CMapFilter* filter)
    157     : table_index_(0), filter_(filter), table_(table) {
    158 }
    159 
    160 bool 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 
    176 CALLER_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 
    204 bool 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  ******************************************************************************/
    213 CMapTable::CMapIdFilter::CMapIdFilter(const CMapId wanted_id)
    214     : wanted_id_(wanted_id),
    215       comparator_(NULL) {
    216 }
    217 
    218 CMapTable::CMapIdFilter::CMapIdFilter(const CMapId wanted_id,
    219                                       const CMapIdComparator* comparator)
    220     : wanted_id_(wanted_id),
    221       comparator_(comparator) {
    222 }
    223 
    224 bool 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  ******************************************************************************/
    233 CMapTable::CMap::CMap(ReadableFontData* data, int32_t format,
    234                       const CMapId& cmap_id)
    235     : SubTable(data), format_(format), cmap_id_(cmap_id) {
    236 }
    237 
    238 CMapTable::CMap::~CMap() {
    239 }
    240 
    241 /******************************************************************************
    242  * CMapTable::CMap::Builder class
    243  ******************************************************************************/
    244 CMapTable::CMap::Builder::~Builder() {
    245 }
    246 
    247 CALLER_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 
    275 CALLER_ATTACH CMapTable::CMap::Builder*
    276 CMapTable::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 
    300 CMapTable::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 
    309 CMapTable::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 
    318 int32_t CMapTable::CMap::Builder::SubSerialize(WritableFontData* new_data) {
    319   return InternalReadData()->CopyTo(new_data);
    320 }
    321 
    322 bool CMapTable::CMap::Builder::SubReadyToSerialize() {
    323   return true;
    324 }
    325 
    326 int32_t CMapTable::CMap::Builder::SubDataSizeToSerialize() {
    327   ReadableFontDataPtr read_data = InternalReadData();
    328   if (!read_data)
    329     return 0;
    330   return read_data->Length();
    331 }
    332 
    333 void CMapTable::CMap::Builder::SubDataSet() {
    334   // NOP
    335 }
    336 
    337 /******************************************************************************
    338  * CMapTable::CMapFormat0
    339  ******************************************************************************/
    340 CMapTable::CMapFormat0::~CMapFormat0() {
    341 }
    342 
    343 int32_t CMapTable::CMapFormat0::Language() {
    344   return 0;
    345 }
    346 
    347 int32_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 
    354 CMapTable::CMapFormat0::CMapFormat0(ReadableFontData* data,
    355                                     const CMapId& cmap_id)
    356     : CMap(data, CMapFormat::kFormat0, cmap_id) {
    357 }
    358 
    359 CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat0::Iterator() {
    360   return new CMapTable::CMapFormat0::CharacterIterator(0, 0xff);
    361 }
    362 
    363 
    364 /******************************************************************************
    365  * CMapTable::CMapFormat0::CharacterIterator
    366  ******************************************************************************/
    367 CMapTable::CMapFormat0::CharacterIterator::CharacterIterator(int32_t start,
    368                                                              int32_t end)
    369     : character_(start),
    370     max_character_(end) {
    371 }
    372 
    373 CMapTable::CMapFormat0::CharacterIterator::~CharacterIterator() {}
    374 
    375 bool CMapTable::CMapFormat0::CharacterIterator::HasNext() {
    376   return character_ < max_character_;
    377 }
    378 
    379 int32_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
    392 CALLER_ATTACH CMapTable::CMapFormat0::Builder*
    393 CMapTable::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
    406 CALLER_ATTACH CMapTable::CMapFormat0::Builder*
    407 CMapTable::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
    420 CALLER_ATTACH CMapTable::CMapFormat0::Builder*
    421 CMapTable::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.
    427 CMapTable::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 
    433 CMapTable::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 
    441 CMapTable::CMapFormat0::Builder::Builder(const CMapId& cmap_id)
    442     : CMap::Builder(reinterpret_cast<ReadableFontData*>(NULL),
    443                     CMapFormat::kFormat0,
    444                     cmap_id) {
    445 }
    446 
    447 CMapTable::CMapFormat0::Builder::~Builder() {
    448 }
    449 
    450 CALLER_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  ******************************************************************************/
    459 CMapTable::CMapFormat2::~CMapFormat2() {
    460 }
    461 
    462 int32_t CMapTable::CMapFormat2::Language() {
    463   return 0;
    464 }
    465 
    466 int32_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 
    507 int32_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 
    514 CMapTable::CMapFormat2::CMapFormat2(ReadableFontData* data,
    515                                     const CMapId& cmap_id)
    516     : CMap(data, CMapFormat::kFormat2, cmap_id) {
    517 }
    518 
    519 int32_t CMapTable::CMapFormat2::SubHeaderOffset(int32_t sub_header_index) {
    520   return data_->ReadUShort(Offset::kFormat2SubHeaderKeys +
    521                            sub_header_index * DataSize::kUSHORT);
    522 }
    523 
    524 int32_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 
    531 int32_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 
    538 int32_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 
    545 int32_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 
    552 CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat2::Iterator() {
    553   // UNIMPLEMENTED
    554   return NULL;
    555 }
    556 
    557 /******************************************************************************
    558  * CMapTable::CMapFormat2::Builder
    559  ******************************************************************************/
    560 CMapTable::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 
    571 CMapTable::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 
    582 CMapTable::CMapFormat2::Builder::~Builder() {
    583 }
    584 
    585 CALLER_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  ******************************************************************************/
    594 CMapTable::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 
    603 CMapTable::CMapFormat4::~CMapFormat4() {
    604 }
    605 
    606 int32_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 
    620 int32_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 
    635 int32_t CMapTable::CMapFormat4::seg_count() {
    636   return seg_count_;
    637 }
    638 
    639 int32_t CMapTable::CMapFormat4::Length() {
    640   return Length(data_);
    641 }
    642 
    643 int32_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
    651 int32_t CMapTable::CMapFormat4::Language(ReadableFontData* data) {
    652   int32_t language = data->ReadUShort(Offset::kFormat4Language);
    653   return language;
    654 }
    655 
    656 // static
    657 int32_t CMapTable::CMapFormat4::Length(ReadableFontData* data) {
    658   int32_t length = data->ReadUShort(Offset::kFormat4Length);
    659   return length;
    660 }
    661 
    662 // static
    663 int32_t CMapTable::CMapFormat4::SegCount(ReadableFontData* data) {
    664   int32_t seg_count = data->ReadUShort(Offset::kFormat4SegCountX2) / 2;
    665   return seg_count;
    666 }
    667 
    668 // static
    669 int32_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
    678 int32_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
    685 int32_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
    695 int32_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
    704 int32_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
    711 int32_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
    721 int32_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
    729 int32_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 
    736 int32_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 
    747 bool 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 
    758 int32_t CMapTable::CMapFormat4::IdDelta(int32_t segment) {
    759   if (IsValidIndex(segment))
    760     return IdDelta(data_, seg_count_, segment);
    761   return -1;
    762 }
    763 
    764 int32_t CMapTable::CMapFormat4::IdRangeOffset(int32_t segment) {
    765   if (IsValidIndex(segment))
    766     return data_->ReadUShort(IdRangeOffsetLocation(segment));
    767   return -1;
    768 }
    769 
    770 int32_t CMapTable::CMapFormat4::IdRangeOffsetLocation(int32_t segment) {
    771   if (IsValidIndex(segment))
    772     return IdRangeOffsetOffset(seg_count_) + segment * DataSize::kUSHORT;
    773   return -1;
    774 }
    775 
    776 int32_t CMapTable::CMapFormat4::GlyphIdArray(int32_t index) {
    777   return data_->ReadUShort(glyph_id_array_offset_ + index * DataSize::kUSHORT);
    778 }
    779 
    780 int32_t CMapTable::CMapFormat4::Language() {
    781   return Language(data_);
    782 }
    783 
    784 
    785 CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat4::Iterator() {
    786   return new CharacterIterator(this);
    787 }
    788 
    789 /******************************************************************************
    790  * CMapTable::CMapFormat4::CharacterIterator class
    791  ******************************************************************************/
    792 CMapTable::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 
    802 bool 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 
    824 int32_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  ******************************************************************************/
    841 CMapTable::CMapFormat4::Builder::Segment::Segment() {}
    842 
    843 CMapTable::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 
    850 CMapTable::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 
    860 CMapTable::CMapFormat4::Builder::Segment::~Segment() {}
    861 
    862 int32_t CMapTable::CMapFormat4::Builder::Segment::start_count() {
    863   return start_count_;
    864 }
    865 
    866 void
    867 CMapTable::CMapFormat4::Builder::Segment::set_start_count(int32_t start_count) {
    868   start_count_ = start_count;
    869 }
    870 
    871 int32_t CMapTable::CMapFormat4::Builder::Segment::end_count() {
    872   return end_count_;
    873 }
    874 
    875 void
    876 CMapTable::CMapFormat4::Builder::Segment::set_end_count(int32_t end_count) {
    877   end_count_ = end_count;
    878 }
    879 
    880 int32_t CMapTable::CMapFormat4::Builder::Segment::id_delta() {
    881   return id_delta_;
    882 }
    883 
    884 void
    885 CMapTable::CMapFormat4::Builder::Segment::set_id_delta(int32_t id_delta) {
    886   id_delta_ = id_delta;
    887 }
    888 
    889 int32_t CMapTable::CMapFormat4::Builder::Segment::id_range_offset() {
    890   return id_range_offset_;
    891 }
    892 
    893 void
    894 CMapTable::CMapFormat4::Builder::Segment::
    895 set_id_range_offset(int32_t id_range_offset) {
    896   id_range_offset_ = id_range_offset;
    897 }
    898 
    899 // static
    900 CALLER_ATTACH SegmentList*
    901 CMapTable::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  ******************************************************************************/
    913 CALLER_ATTACH CMapTable::CMapFormat4::Builder*
    914 CMapTable::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 
    927 CALLER_ATTACH CMapTable::CMapFormat4::Builder*
    928 CMapTable::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 
    941 CALLER_ATTACH CMapTable::CMapFormat4::Builder*
    942 CMapTable::CMapFormat4::Builder::NewInstance(const CMapId& cmap_id) {
    943   return new Builder(cmap_id);
    944 }
    945 
    946 CMapTable::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 
    952 CMapTable::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 
    958 CMapTable::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 
    968 CMapTable::CMapFormat4::Builder::Builder(const CMapId& cmap_id)
    969     : CMap::Builder(reinterpret_cast<ReadableFontData*>(NULL),
    970                     CMapFormat::kFormat4, cmap_id) {
    971 }
    972 
    973 CMapTable::CMapFormat4::Builder::~Builder() {}
    974 
    975 void 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 
   1006 SegmentList* CMapTable::CMapFormat4::Builder::segments() {
   1007   if (segments_.empty()) {
   1008     Initialize(InternalReadData());
   1009     set_model_changed();
   1010   }
   1011   return &segments_;
   1012 }
   1013 
   1014 void CMapTable::CMapFormat4::Builder::set_segments(SegmentList* segments) {
   1015   segments_.assign(segments->begin(), segments->end());
   1016   set_model_changed();
   1017 }
   1018 
   1019 IntegerList* 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 
   1027 void CMapTable::CMapFormat4::Builder::
   1028 set_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 
   1033 CALLER_ATTACH FontDataTable*
   1034 CMapTable::CMapFormat4::Builder::SubBuildTable(ReadableFontData* data) {
   1035   FontDataTablePtr table = new CMapFormat4(data, cmap_id());
   1036   return table.Detach();
   1037 }
   1038 
   1039 void CMapTable::CMapFormat4::Builder::SubDataSet() {
   1040   segments_.clear();
   1041   glyph_id_array_.clear();
   1042   set_model_changed();
   1043 }
   1044 
   1045 int32_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 
   1055 bool 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 
   1065 int32_t
   1066 CMapTable::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  ******************************************************************************/
   1116 CMapTable::Builder::Builder(Header* header, WritableFontData* data)
   1117     : SubTableContainerTable::Builder(header, data), version_(0) {
   1118 }
   1119 
   1120 CMapTable::Builder::Builder(Header* header, ReadableFontData* data)
   1121     : SubTableContainerTable::Builder(header, data), version_(0) {
   1122 }
   1123 
   1124 CMapTable::Builder::~Builder() {
   1125 }
   1126 
   1127 int32_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 
   1151 bool 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 
   1164 int32_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 
   1182 void CMapTable::Builder::SubDataSet() {
   1183   GetCMapBuilders()->clear();
   1184   Table::Builder::set_model_changed();
   1185 }
   1186 
   1187 CALLER_ATTACH FontDataTable*
   1188     CMapTable::Builder::SubBuildTable(ReadableFontData* data) {
   1189   FontDataTablePtr table = new CMapTable(header(), data);
   1190   return table.Detach();
   1191 }
   1192 
   1193 CALLER_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
   1202 CALLER_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
   1223 int32_t CMapTable::Builder::NumCMaps(ReadableFontData* data) {
   1224   if (data == NULL) {
   1225     return 0;
   1226   }
   1227   return data->ReadUShort(Offset::kNumTables);
   1228 }
   1229 
   1230 int32_t CMapTable::Builder::NumCMaps() {
   1231   return GetCMapBuilders()->size();
   1232 }
   1233 
   1234 void 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 
   1244 CMapTable::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 
   1257 CMapTable::CMap::Builder*
   1258 CMapTable::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 
   1266 CMapTable::CMap::Builder*
   1267 CMapTable::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 
   1279 CMapTable::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