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