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  ******************************************************************************/
CMapTable(Header * header,ReadableFontData * data)54 CMapTable::CMapTable(Header* header, ReadableFontData* data)
55   : SubTableContainerTable(header, data) {
56 }
57 
~CMapTable()58 CMapTable::~CMapTable() {}
59 
GetCMap(const int32_t index)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 
GetCMap(const int32_t platform_id,const int32_t encoding_id)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*
GetCMap(const CMapTable::CMapId cmap_id)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 
Version()106 int32_t CMapTable::Version() {
107   return data_->ReadUShort(Offset::kVersion);
108 }
109 
NumCMaps()110 int32_t CMapTable::NumCMaps() {
111   return data_->ReadUShort(Offset::kNumTables);
112 }
113 
GetCMapId(int32_t index)114 CMapTable::CMapId CMapTable::GetCMapId(int32_t index) {
115   return NewCMapId(PlatformId(index), EncodingId(index));
116 }
117 
PlatformId(int32_t index)118 int32_t CMapTable::PlatformId(int32_t index) {
119   return data_->ReadUShort(Offset::kEncodingRecordPlatformId +
120                            OffsetForEncodingRecord(index));
121 }
122 
EncodingId(int32_t index)123 int32_t CMapTable::EncodingId(int32_t index) {
124   return data_->ReadUShort(Offset::kEncodingRecordEncodingId +
125                            OffsetForEncodingRecord(index));
126 }
127 
Offset(int32_t index)128 int32_t CMapTable::Offset(int32_t index) {
129   return data_->ReadULongAsInt(Offset::kEncodingRecordOffset +
130                                OffsetForEncodingRecord(index));
131 }
132 
OffsetForEncodingRecord(int32_t index)133 int32_t CMapTable::OffsetForEncodingRecord(int32_t index) {
134   return Offset::kEncodingRecordStart + index * Offset::kEncodingRecordSize;
135 }
136 
NewCMapId(int32_t platform_id,int32_t encoding_id)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 
NewCMapId(const CMapId & obj)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  ******************************************************************************/
CMapIterator(CMapTable * table,const CMapFilter * filter)155 CMapTable::CMapIterator::CMapIterator(CMapTable* table,
156                                       const CMapFilter* filter)
157     : table_index_(0), filter_(filter), table_(table) {
158 }
159 
HasNext()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 
Next()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 
operator ()(const CMapId & lhs,const CMapId & rhs) const204 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  ******************************************************************************/
CMapIdFilter(const CMapId wanted_id)213 CMapTable::CMapIdFilter::CMapIdFilter(const CMapId wanted_id)
214     : wanted_id_(wanted_id),
215       comparator_(NULL) {
216 }
217 
CMapIdFilter(const CMapId wanted_id,const CMapIdComparator * comparator)218 CMapTable::CMapIdFilter::CMapIdFilter(const CMapId wanted_id,
219                                       const CMapIdComparator* comparator)
220     : wanted_id_(wanted_id),
221       comparator_(comparator) {
222 }
223 
accept(const CMapId & cmap_id) const224 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  ******************************************************************************/
CMap(ReadableFontData * data,int32_t format,const CMapId & cmap_id)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 
~CMap()238 CMapTable::CMap::~CMap() {
239 }
240 
241 /******************************************************************************
242  * CMapTable::CMap::Builder class
243  ******************************************************************************/
~Builder()244 CMapTable::CMap::Builder::~Builder() {
245 }
246 
247 CALLER_ATTACH CMapTable::CMap::Builder*
GetBuilder(ReadableFontData * data,int32_t offset,const CMapId & cmap_id)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*
GetBuilder(int32_t format,const CMapId & cmap_id)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 
Builder(ReadableFontData * data,int32_t format,const CMapId & cmap_id)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 
Builder(WritableFontData * data,int32_t format,const CMapId & cmap_id)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 
SubSerialize(WritableFontData * new_data)318 int32_t CMapTable::CMap::Builder::SubSerialize(WritableFontData* new_data) {
319   return InternalReadData()->CopyTo(new_data);
320 }
321 
SubReadyToSerialize()322 bool CMapTable::CMap::Builder::SubReadyToSerialize() {
323   return true;
324 }
325 
SubDataSizeToSerialize()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 
SubDataSet()333 void CMapTable::CMap::Builder::SubDataSet() {
334   // NOP
335 }
336 
337 /******************************************************************************
338  * CMapTable::CMapFormat0
339  ******************************************************************************/
~CMapFormat0()340 CMapTable::CMapFormat0::~CMapFormat0() {
341 }
342 
Language()343 int32_t CMapTable::CMapFormat0::Language() {
344   return 0;
345 }
346 
GlyphId(int32_t character)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 
CMapFormat0(ReadableFontData * data,const CMapId & cmap_id)354 CMapTable::CMapFormat0::CMapFormat0(ReadableFontData* data,
355                                     const CMapId& cmap_id)
356     : CMap(data, CMapFormat::kFormat0, cmap_id) {
357 }
358 
Iterator()359 CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat0::Iterator() {
360   return new CMapTable::CMapFormat0::CharacterIterator(0, 0xff);
361 }
362 
363 
364 /******************************************************************************
365  * CMapTable::CMapFormat0::CharacterIterator
366  ******************************************************************************/
CharacterIterator(int32_t start,int32_t end)367 CMapTable::CMapFormat0::CharacterIterator::CharacterIterator(int32_t start,
368                                                              int32_t end)
369     : character_(start),
370     max_character_(end) {
371 }
372 
~CharacterIterator()373 CMapTable::CMapFormat0::CharacterIterator::~CharacterIterator() {}
374 
HasNext()375 bool CMapTable::CMapFormat0::CharacterIterator::HasNext() {
376   return character_ < max_character_;
377 }
378 
Next()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*
NewInstance(WritableFontData * data,int32_t offset,const CMapId & cmap_id)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*
NewInstance(ReadableFontData * data,int32_t offset,const CMapId & cmap_id)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*
NewInstance(const CMapId & cmap_id)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.
Builder(WritableFontData * data,int32_t offset,const CMapId & cmap_id)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 
Builder(ReadableFontData * data,int32_t offset,const CMapId & cmap_id)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 
Builder(const CMapId & cmap_id)441 CMapTable::CMapFormat0::Builder::Builder(const CMapId& cmap_id)
442     : CMap::Builder(reinterpret_cast<ReadableFontData*>(NULL),
443                     CMapFormat::kFormat0,
444                     cmap_id) {
445 }
446 
~Builder()447 CMapTable::CMapFormat0::Builder::~Builder() {
448 }
449 
450 CALLER_ATTACH FontDataTable*
SubBuildTable(ReadableFontData * data)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  ******************************************************************************/
~CMapFormat2()459 CMapTable::CMapFormat2::~CMapFormat2() {
460 }
461 
Language()462 int32_t CMapTable::CMapFormat2::Language() {
463   return 0;
464 }
465 
GlyphId(int32_t character)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 
BytesConsumed(int32_t character)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 
CMapFormat2(ReadableFontData * data,const CMapId & cmap_id)514 CMapTable::CMapFormat2::CMapFormat2(ReadableFontData* data,
515                                     const CMapId& cmap_id)
516     : CMap(data, CMapFormat::kFormat2, cmap_id) {
517 }
518 
SubHeaderOffset(int32_t sub_header_index)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 
FirstCode(int32_t sub_header_index)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 
EntryCount(int32_t sub_header_index)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 
IdRangeOffset(int32_t sub_header_index)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 
IdDelta(int32_t sub_header_index)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 
Iterator()552 CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat2::Iterator() {
553   // UNIMPLEMENTED
554   return NULL;
555 }
556 
557 /******************************************************************************
558  * CMapTable::CMapFormat2::Builder
559  ******************************************************************************/
Builder(WritableFontData * data,int32_t offset,const CMapId & cmap_id)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 
Builder(ReadableFontData * data,int32_t offset,const CMapId & cmap_id)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 
~Builder()582 CMapTable::CMapFormat2::Builder::~Builder() {
583 }
584 
585 CALLER_ATTACH FontDataTable*
SubBuildTable(ReadableFontData * data)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  ******************************************************************************/
CMapFormat4(ReadableFontData * data,const CMapId & cmap_id)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 
~CMapFormat4()603 CMapTable::CMapFormat4::~CMapFormat4() {
604 }
605 
GlyphId(int32_t character)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 
RetrieveGlyphId(int32_t segment,int32_t start_code,int32_t character)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 
seg_count()635 int32_t CMapTable::CMapFormat4::seg_count() {
636   return seg_count_;
637 }
638 
Length()639 int32_t CMapTable::CMapFormat4::Length() {
640   return Length(data_);
641 }
642 
StartCode(int32_t segment)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
Language(ReadableFontData * data)651 int32_t CMapTable::CMapFormat4::Language(ReadableFontData* data) {
652   int32_t language = data->ReadUShort(Offset::kFormat4Language);
653   return language;
654 }
655 
656 // static
Length(ReadableFontData * data)657 int32_t CMapTable::CMapFormat4::Length(ReadableFontData* data) {
658   int32_t length = data->ReadUShort(Offset::kFormat4Length);
659   return length;
660 }
661 
662 // static
SegCount(ReadableFontData * data)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
StartCode(ReadableFontData * data,int32_t seg_count,int32_t index)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
StartCodeOffset(int32_t seg_count)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
EndCode(ReadableFontData * data,int32_t seg_count,int32_t index)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
IdDelta(ReadableFontData * data,int32_t seg_count,int32_t index)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
IdDeltaOffset(int32_t seg_count)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
IdRangeOffset(ReadableFontData * data,int32_t seg_count,int32_t index)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
IdRangeOffsetOffset(int32_t seg_count)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
GlyphIdArrayOffset(int32_t seg_count)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 
EndCode(int32_t segment)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 
IsValidIndex(int32_t segment)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 
IdDelta(int32_t segment)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 
IdRangeOffset(int32_t segment)764 int32_t CMapTable::CMapFormat4::IdRangeOffset(int32_t segment) {
765   if (IsValidIndex(segment))
766     return data_->ReadUShort(IdRangeOffsetLocation(segment));
767   return -1;
768 }
769 
IdRangeOffsetLocation(int32_t segment)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 
GlyphIdArray(int32_t index)776 int32_t CMapTable::CMapFormat4::GlyphIdArray(int32_t index) {
777   return data_->ReadUShort(glyph_id_array_offset_ + index * DataSize::kUSHORT);
778 }
779 
Language()780 int32_t CMapTable::CMapFormat4::Language() {
781   return Language(data_);
782 }
783 
784 
Iterator()785 CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat4::Iterator() {
786   return new CharacterIterator(this);
787 }
788 
789 /******************************************************************************
790  * CMapTable::CMapFormat4::CharacterIterator class
791  ******************************************************************************/
CharacterIterator(CMapFormat4 * parent)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 
HasNext()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 
Next()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  ******************************************************************************/
Segment()841 CMapTable::CMapFormat4::Builder::Segment::Segment() {}
842 
Segment(Segment * other)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 
Segment(int32_t start_count,int32_t end_count,int32_t id_delta,int32_t id_range_offset)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 
~Segment()860 CMapTable::CMapFormat4::Builder::Segment::~Segment() {}
861 
start_count()862 int32_t CMapTable::CMapFormat4::Builder::Segment::start_count() {
863   return start_count_;
864 }
865 
866 void
set_start_count(int32_t start_count)867 CMapTable::CMapFormat4::Builder::Segment::set_start_count(int32_t start_count) {
868   start_count_ = start_count;
869 }
870 
end_count()871 int32_t CMapTable::CMapFormat4::Builder::Segment::end_count() {
872   return end_count_;
873 }
874 
875 void
set_end_count(int32_t end_count)876 CMapTable::CMapFormat4::Builder::Segment::set_end_count(int32_t end_count) {
877   end_count_ = end_count;
878 }
879 
id_delta()880 int32_t CMapTable::CMapFormat4::Builder::Segment::id_delta() {
881   return id_delta_;
882 }
883 
884 void
set_id_delta(int32_t id_delta)885 CMapTable::CMapFormat4::Builder::Segment::set_id_delta(int32_t id_delta) {
886   id_delta_ = id_delta;
887 }
888 
id_range_offset()889 int32_t CMapTable::CMapFormat4::Builder::Segment::id_range_offset() {
890   return id_range_offset_;
891 }
892 
893 void
894 CMapTable::CMapFormat4::Builder::Segment::
set_id_range_offset(int32_t id_range_offset)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*
DeepCopy(SegmentList * original)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*
NewInstance(ReadableFontData * data,int32_t offset,const CMapId & cmap_id)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*
NewInstance(WritableFontData * data,int32_t offset,const CMapId & cmap_id)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*
NewInstance(const CMapId & cmap_id)942 CMapTable::CMapFormat4::Builder::NewInstance(const CMapId& cmap_id) {
943   return new Builder(cmap_id);
944 }
945 
Builder(ReadableFontData * data,int32_t offset,const CMapId & cmap_id)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 
Builder(WritableFontData * data,int32_t offset,const CMapId & cmap_id)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 
Builder(SegmentList * segments,IntegerList * glyph_id_array,const CMapId & cmap_id)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 
Builder(const CMapId & cmap_id)968 CMapTable::CMapFormat4::Builder::Builder(const CMapId& cmap_id)
969     : CMap::Builder(reinterpret_cast<ReadableFontData*>(NULL),
970                     CMapFormat::kFormat4, cmap_id) {
971 }
972 
~Builder()973 CMapTable::CMapFormat4::Builder::~Builder() {}
974 
Initialize(ReadableFontData * data)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 
segments()1006 SegmentList* CMapTable::CMapFormat4::Builder::segments() {
1007   if (segments_.empty()) {
1008     Initialize(InternalReadData());
1009     set_model_changed();
1010   }
1011   return &segments_;
1012 }
1013 
set_segments(SegmentList * segments)1014 void CMapTable::CMapFormat4::Builder::set_segments(SegmentList* segments) {
1015   segments_.assign(segments->begin(), segments->end());
1016   set_model_changed();
1017 }
1018 
glyph_id_array()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::
set_glyph_id_array(IntegerList * glyph_id_array)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*
SubBuildTable(ReadableFontData * data)1034 CMapTable::CMapFormat4::Builder::SubBuildTable(ReadableFontData* data) {
1035   FontDataTablePtr table = new CMapFormat4(data, cmap_id());
1036   return table.Detach();
1037 }
1038 
SubDataSet()1039 void CMapTable::CMapFormat4::Builder::SubDataSet() {
1040   segments_.clear();
1041   glyph_id_array_.clear();
1042   set_model_changed();
1043 }
1044 
SubDataSizeToSerialize()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 
SubReadyToSerialize()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
SubSerialize(WritableFontData * new_data)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  ******************************************************************************/
Builder(Header * header,WritableFontData * data)1116 CMapTable::Builder::Builder(Header* header, WritableFontData* data)
1117     : SubTableContainerTable::Builder(header, data), version_(0) {
1118 }
1119 
Builder(Header * header,ReadableFontData * data)1120 CMapTable::Builder::Builder(Header* header, ReadableFontData* data)
1121     : SubTableContainerTable::Builder(header, data), version_(0) {
1122 }
1123 
~Builder()1124 CMapTable::Builder::~Builder() {
1125 }
1126 
SubSerialize(WritableFontData * new_data)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 
SubReadyToSerialize()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 
SubDataSizeToSerialize()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 
SubDataSet()1182 void CMapTable::Builder::SubDataSet() {
1183   GetCMapBuilders()->clear();
1184   Table::Builder::set_model_changed();
1185 }
1186 
1187 CALLER_ATTACH FontDataTable*
SubBuildTable(ReadableFontData * data)1188     CMapTable::Builder::SubBuildTable(ReadableFontData* data) {
1189   FontDataTablePtr table = new CMapTable(header(), data);
1190   return table.Detach();
1191 }
1192 
1193 CALLER_ATTACH CMapTable::Builder*
CreateBuilder(Header * header,WritableFontData * data)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*
CMapBuilder(ReadableFontData * data,int32_t index)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
NumCMaps(ReadableFontData * data)1223 int32_t CMapTable::Builder::NumCMaps(ReadableFontData* data) {
1224   if (data == NULL) {
1225     return 0;
1226   }
1227   return data->ReadUShort(Offset::kNumTables);
1228 }
1229 
NumCMaps()1230 int32_t CMapTable::Builder::NumCMaps() {
1231   return GetCMapBuilders()->size();
1232 }
1233 
Initialize(ReadableFontData * data)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 
NewCMapBuilder(const CMapId & cmap_id,ReadableFontData * data)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*
NewCMapBuilder(int32_t format,const CMapId & cmap_id)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*
CMapBuilder(const CMapId & cmap_id)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 
GetCMapBuilders()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