/* * Copyright 2011 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // type.h needs to be included first because of building issues on Windows // Type aliases we delcare are defined in other headers and make the build // fail otherwise. #include "sfntly/port/type.h" #include "sfntly/table/core/cmap_table.h" #include #include #include #include "sfntly/font.h" #include "sfntly/math/font_math.h" #include "sfntly/port/endian.h" #include "sfntly/port/exception_type.h" #include "sfntly/table/core/name_table.h" namespace sfntly { const int32_t CMapTable::NOTDEF = 0; CMapTable::CMapId CMapTable::WINDOWS_BMP = { PlatformId::kWindows, WindowsEncodingId::kUnicodeUCS2 }; CMapTable::CMapId CMapTable::WINDOWS_UCS4 = { PlatformId::kWindows, WindowsEncodingId::kUnicodeUCS4 }; CMapTable::CMapId CMapTable::MAC_ROMAN = { PlatformId::kWindows, MacintoshEncodingId::kRoman }; /****************************************************************************** * CMapTable class ******************************************************************************/ CMapTable::CMapTable(Header* header, ReadableFontData* data) : SubTableContainerTable(header, data) { } CMapTable::~CMapTable() {} CALLER_ATTACH CMapTable::CMap* CMapTable::GetCMap(const int32_t index) { if (index < 0 || index > NumCMaps()) { #ifndef SFNTLY_NO_EXCEPTION throw IndexOutOfBoundException("Requested CMap index is out of bounds."); #else return NULL; #endif } int32_t platform_id = PlatformId(index); int32_t encoding_id = EncodingId(index); CMapId cmap_id = NewCMapId(platform_id, encoding_id); int32_t offset_ = Offset(index); Ptr cmap_builder = (CMap::Builder::GetBuilder(data_, offset_, cmap_id)); if (!cmap_builder) { #ifndef SFNTLY_NO_EXCEPTION throw NoSuchElementException("Cannot find builder for requested CMap."); #else return NULL; #endif } return down_cast(cmap_builder->Build()); } CALLER_ATTACH CMapTable::CMap* CMapTable::GetCMap(const int32_t platform_id, const int32_t encoding_id) { return GetCMap(NewCMapId(platform_id, encoding_id)); } CALLER_ATTACH CMapTable::CMap* CMapTable::GetCMap(const CMapTable::CMapId cmap_id) { CMapIdFilter id_filter(cmap_id); CMapIterator cmap_iterator(this, &id_filter); // There can only be one cmap with a particular CMapId if (cmap_iterator.HasNext()) { Ptr cmap; cmap.Attach(cmap_iterator.Next()); return cmap.Detach(); } #ifndef SFNTLY_NO_EXCEPTION throw NoSuchElementException(); #else return NULL; #endif } int32_t CMapTable::Version() { return data_->ReadUShort(Offset::kVersion); } int32_t CMapTable::NumCMaps() { return data_->ReadUShort(Offset::kNumTables); } CMapTable::CMapId CMapTable::GetCMapId(int32_t index) { return NewCMapId(PlatformId(index), EncodingId(index)); } int32_t CMapTable::PlatformId(int32_t index) { return data_->ReadUShort(Offset::kEncodingRecordPlatformId + OffsetForEncodingRecord(index)); } int32_t CMapTable::EncodingId(int32_t index) { return data_->ReadUShort(Offset::kEncodingRecordEncodingId + OffsetForEncodingRecord(index)); } int32_t CMapTable::Offset(int32_t index) { return data_->ReadULongAsInt(Offset::kEncodingRecordOffset + OffsetForEncodingRecord(index)); } int32_t CMapTable::OffsetForEncodingRecord(int32_t index) { return Offset::kEncodingRecordStart + index * Offset::kEncodingRecordSize; } CMapTable::CMapId CMapTable::NewCMapId(int32_t platform_id, int32_t encoding_id) { CMapId result; result.platform_id = platform_id; result.encoding_id = encoding_id; return result; } CMapTable::CMapId CMapTable::NewCMapId(const CMapId& obj) { CMapId result; result.platform_id = obj.platform_id; result.encoding_id = obj.encoding_id; return result; } /****************************************************************************** * CMapTable::CMapIterator class ******************************************************************************/ CMapTable::CMapIterator::CMapIterator(CMapTable* table, const CMapFilter* filter) : table_index_(0), filter_(filter), table_(table) { } bool CMapTable::CMapIterator::HasNext() { if (!filter_) { if (table_index_ < table_->NumCMaps()) { return true; } return false; } for (; table_index_ < table_->NumCMaps(); ++table_index_) { if (filter_->accept(table_->GetCMapId(table_index_))) { return true; } } return false; } CALLER_ATTACH CMapTable::CMap* CMapTable::CMapIterator::Next() { if (!HasNext()) { #ifndef SFNTLY_NO_EXCEPTION throw NoSuchElementException(); #else return NULL; #endif } CMapPtr next_cmap; next_cmap.Attach(table_->GetCMap(table_index_++)); if (next_cmap == NULL) { #ifndef SFNTLY_NO_EXCEPTION throw NoSuchElementException("Error during the creation of the CMap"); #else return NULL; #endif } return next_cmap.Detach(); } /****************************************************************************** * CMapTable::CMapId class ******************************************************************************/ /****************************************************************************** * CMapTable::CMapIdComparator class ******************************************************************************/ bool CMapTable::CMapIdComparator::operator()(const CMapId& lhs, const CMapId& rhs) const { return ((lhs.platform_id << 8 | lhs.encoding_id) > (rhs.platform_id << 8 | rhs.encoding_id)); } /****************************************************************************** * CMapTable::CMapIdFilter class ******************************************************************************/ CMapTable::CMapIdFilter::CMapIdFilter(const CMapId wanted_id) : wanted_id_(wanted_id), comparator_(NULL) { } CMapTable::CMapIdFilter::CMapIdFilter(const CMapId wanted_id, const CMapIdComparator* comparator) : wanted_id_(wanted_id), comparator_(comparator) { } bool CMapTable::CMapIdFilter::accept(const CMapId& cmap_id) const { if (!comparator_) return wanted_id_ == cmap_id; return (*comparator_)(wanted_id_, cmap_id); } /****************************************************************************** * CMapTable::CMap class ******************************************************************************/ CMapTable::CMap::CMap(ReadableFontData* data, int32_t format, const CMapId& cmap_id) : SubTable(data), format_(format), cmap_id_(cmap_id) { } CMapTable::CMap::~CMap() { } /****************************************************************************** * CMapTable::CMap::Builder class ******************************************************************************/ CMapTable::CMap::Builder::~Builder() { } CALLER_ATTACH CMapTable::CMap::Builder* CMapTable::CMap::Builder::GetBuilder(ReadableFontData* data, int32_t offset, const CMapId& cmap_id) { // NOT IMPLEMENTED: Java enum value validation int32_t format = data->ReadUShort(offset); CMapBuilderPtr builder; switch (format) { case CMapFormat::kFormat0: builder.Attach(CMapFormat0::Builder::NewInstance(data, offset, cmap_id)); break; case CMapFormat::kFormat2: #if defined (SFNTLY_DEBUG_CMAP) fprintf(stderr, "Requesting Format2 builder, but it's unsupported; " "returning NULL\n"); #endif break; case CMapFormat::kFormat4: builder.Attach(CMapFormat4::Builder::NewInstance(data, offset, cmap_id)); break; default: #ifdef SFNTLY_DEBUG_CMAP fprintf(stderr, "Unknown builder format requested\n"); #endif break; } return builder.Detach(); } CALLER_ATTACH CMapTable::CMap::Builder* CMapTable::CMap::Builder::GetBuilder(int32_t format, const CMapId& cmap_id) { Ptr builder; switch (format) { case CMapFormat::kFormat0: builder.Attach(CMapFormat0::Builder::NewInstance(cmap_id)); break; case CMapFormat::kFormat2: #if defined (SFNTLY_DEBUG_CMAP) fprintf(stderr, "Requesting Format2 builder, but it's unsupported; " "returning NULL\n"); #endif break; case CMapFormat::kFormat4: builder.Attach(CMapFormat4::Builder::NewInstance(cmap_id)); break; default: #ifdef SFNTLY_DEBUG_CMAP fprintf(stderr, "Unknown builder format requested\n"); #endif break; } return builder.Detach(); } CMapTable::CMap::Builder::Builder(ReadableFontData* data, int32_t format, const CMapId& cmap_id) : SubTable::Builder(data), format_(format), cmap_id_(cmap_id), language_(0) { } CMapTable::CMap::Builder::Builder(WritableFontData* data, int32_t format, const CMapId& cmap_id) : SubTable::Builder(data), format_(format), cmap_id_(cmap_id), language_(0) { } int32_t CMapTable::CMap::Builder::SubSerialize(WritableFontData* new_data) { return InternalReadData()->CopyTo(new_data); } bool CMapTable::CMap::Builder::SubReadyToSerialize() { return true; } int32_t CMapTable::CMap::Builder::SubDataSizeToSerialize() { ReadableFontDataPtr read_data = InternalReadData(); if (!read_data) return 0; return read_data->Length(); } void CMapTable::CMap::Builder::SubDataSet() { // NOP } /****************************************************************************** * CMapTable::CMapFormat0 ******************************************************************************/ CMapTable::CMapFormat0::~CMapFormat0() { } int32_t CMapTable::CMapFormat0::Language() { return 0; } int32_t CMapTable::CMapFormat0::GlyphId(int32_t character) { if (character < 0 || character > 255) { return CMapTable::NOTDEF; } return data_->ReadUByte(character + Offset::kFormat0GlyphIdArray); } CMapTable::CMapFormat0::CMapFormat0(ReadableFontData* data, const CMapId& cmap_id) : CMap(data, CMapFormat::kFormat0, cmap_id) { } CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat0::Iterator() { return new CMapTable::CMapFormat0::CharacterIterator(0, 0xff); } /****************************************************************************** * CMapTable::CMapFormat0::CharacterIterator ******************************************************************************/ CMapTable::CMapFormat0::CharacterIterator::CharacterIterator(int32_t start, int32_t end) : character_(start), max_character_(end) { } CMapTable::CMapFormat0::CharacterIterator::~CharacterIterator() {} bool CMapTable::CMapFormat0::CharacterIterator::HasNext() { return character_ < max_character_; } int32_t CMapTable::CMapFormat0::CharacterIterator::Next() { if (HasNext()) return character_++; #ifndef SFNTLY_NO_EXCEPTION throw NoSuchElementException("No more characters to iterate."); #endif return -1; } /****************************************************************************** * CMapTable::CMapFormat0::Builder ******************************************************************************/ // static CALLER_ATTACH CMapTable::CMapFormat0::Builder* CMapTable::CMapFormat0::Builder::NewInstance(WritableFontData* data, int32_t offset, const CMapId& cmap_id) { WritableFontDataPtr wdata; if (data) { wdata.Attach(down_cast( data->Slice(offset, data->ReadUShort(offset + Offset::kFormat0Length)))); } return new Builder(wdata, CMapFormat::kFormat0, cmap_id); } // static CALLER_ATTACH CMapTable::CMapFormat0::Builder* CMapTable::CMapFormat0::Builder::NewInstance(ReadableFontData* data, int32_t offset, const CMapId& cmap_id) { ReadableFontDataPtr rdata; if (data) { rdata.Attach(down_cast( data->Slice(offset, data->ReadUShort(offset + Offset::kFormat0Length)))); } return new Builder(rdata, CMapFormat::kFormat0, cmap_id); } // static CALLER_ATTACH CMapTable::CMapFormat0::Builder* CMapTable::CMapFormat0::Builder::NewInstance(const CMapId& cmap_id) { return new Builder(cmap_id); } // Always call NewInstance instead of the constructor for creating a new builder // object! This refactoring avoids memory leaks when slicing the font data. CMapTable::CMapFormat0::Builder::Builder(WritableFontData* data, int32_t offset, const CMapId& cmap_id) : CMapTable::CMap::Builder(data, CMapFormat::kFormat0, cmap_id) { UNREFERENCED_PARAMETER(offset); } CMapTable::CMapFormat0::Builder::Builder( ReadableFontData* data, int32_t offset, const CMapId& cmap_id) : CMapTable::CMap::Builder(data, CMapFormat::kFormat0, cmap_id) { UNREFERENCED_PARAMETER(offset); } CMapTable::CMapFormat0::Builder::Builder(const CMapId& cmap_id) : CMap::Builder(reinterpret_cast(NULL), CMapFormat::kFormat0, cmap_id) { } CMapTable::CMapFormat0::Builder::~Builder() { } CALLER_ATTACH FontDataTable* CMapTable::CMapFormat0::Builder::SubBuildTable(ReadableFontData* data) { FontDataTablePtr table = new CMapFormat0(data, cmap_id()); return table.Detach(); } /****************************************************************************** * CMapTable::CMapFormat2 ******************************************************************************/ CMapTable::CMapFormat2::~CMapFormat2() { } int32_t CMapTable::CMapFormat2::Language() { return 0; } int32_t CMapTable::CMapFormat2::GlyphId(int32_t character) { if (character > 0xffff) { return CMapTable::NOTDEF; } uint32_t c = ToBE32(character); byte_t high_byte = (c >> 8) & 0xff; byte_t low_byte = c & 0xff; int32_t offset = SubHeaderOffset(high_byte); if (offset == 0) { low_byte = high_byte; high_byte = 0; } int32_t first_code = FirstCode(high_byte); int32_t entry_count = EntryCount(high_byte); if (low_byte < first_code || low_byte >= first_code + entry_count) { return CMapTable::NOTDEF; } int32_t id_range_offset = IdRangeOffset(high_byte); // position of idRangeOffset + value of idRangeOffset + index for low byte // = firstcode int32_t p_location = (offset + Offset::kFormat2SubHeader_idRangeOffset) + id_range_offset + (low_byte - first_code) * DataSize::kUSHORT; int p = data_->ReadUShort(p_location); if (p == 0) { return CMapTable::NOTDEF; } if (offset == 0) { return p; } int id_delta = IdDelta(high_byte); return (p + id_delta) % 65536; } int32_t CMapTable::CMapFormat2::BytesConsumed(int32_t character) { uint32_t c = ToBE32(character); int32_t high_byte = (c >> 8) & 0xff; int32_t offset = SubHeaderOffset(high_byte); return (offset == 0) ? 1 : 2; } CMapTable::CMapFormat2::CMapFormat2(ReadableFontData* data, const CMapId& cmap_id) : CMap(data, CMapFormat::kFormat2, cmap_id) { } int32_t CMapTable::CMapFormat2::SubHeaderOffset(int32_t sub_header_index) { return data_->ReadUShort(Offset::kFormat2SubHeaderKeys + sub_header_index * DataSize::kUSHORT); } int32_t CMapTable::CMapFormat2::FirstCode(int32_t sub_header_index) { int32_t sub_header_offset = SubHeaderOffset(sub_header_index); return data_->ReadUShort(sub_header_offset + Offset::kFormat2SubHeaderKeys + Offset::kFormat2SubHeader_firstCode); } int32_t CMapTable::CMapFormat2::EntryCount(int32_t sub_header_index) { int32_t sub_header_offset = SubHeaderOffset(sub_header_index); return data_->ReadUShort(sub_header_offset + Offset::kFormat2SubHeaderKeys + Offset::kFormat2SubHeader_entryCount); } int32_t CMapTable::CMapFormat2::IdRangeOffset(int32_t sub_header_index) { int32_t sub_header_offset = SubHeaderOffset(sub_header_index); return data_->ReadUShort(sub_header_offset + Offset::kFormat2SubHeaderKeys + Offset::kFormat2SubHeader_idRangeOffset); } int32_t CMapTable::CMapFormat2::IdDelta(int32_t sub_header_index) { int32_t sub_header_offset = SubHeaderOffset(sub_header_index); return data_->ReadUShort(sub_header_offset + Offset::kFormat2SubHeaderKeys + Offset::kFormat2SubHeader_idDelta); } CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat2::Iterator() { // UNIMPLEMENTED return NULL; } /****************************************************************************** * CMapTable::CMapFormat2::Builder ******************************************************************************/ CMapTable::CMapFormat2::Builder::Builder(WritableFontData* data, int32_t offset, const CMapId& cmap_id) : CMapTable::CMap::Builder(data ? down_cast( data->Slice(offset, data->ReadUShort( offset + Offset::kFormat0Length))) : reinterpret_cast(NULL), CMapFormat::kFormat2, cmap_id) { // TODO(arthurhsu): FIXIT: heavy lifting and leak, need fix. } CMapTable::CMapFormat2::Builder::Builder(ReadableFontData* data, int32_t offset, const CMapId& cmap_id) : CMapTable::CMap::Builder(data ? down_cast( data->Slice(offset, data->ReadUShort( offset + Offset::kFormat0Length))) : reinterpret_cast(NULL), CMapFormat::kFormat2, cmap_id) { // TODO(arthurhsu): FIXIT: heavy lifting and leak, need fix. } CMapTable::CMapFormat2::Builder::~Builder() { } CALLER_ATTACH FontDataTable* CMapTable::CMapFormat2::Builder::SubBuildTable(ReadableFontData* data) { FontDataTablePtr table = new CMapFormat2(data, cmap_id()); return table.Detach(); } /****************************************************************************** * CMapTable::CMapFormat4 ******************************************************************************/ CMapTable::CMapFormat4::CMapFormat4(ReadableFontData* data, const CMapId& cmap_id) : CMapTable::CMap::CMap(data, CMapFormat::kFormat4, cmap_id), seg_count_(SegCount(data)), start_code_offset_(StartCodeOffset(seg_count_)), end_code_offset_(Offset::kFormat4EndCount), id_delta_offset_(IdDeltaOffset(seg_count_)), glyph_id_array_offset_(GlyphIdArrayOffset(seg_count_)) { } CMapTable::CMapFormat4::~CMapFormat4() { } int32_t CMapTable::CMapFormat4::GlyphId(int32_t character) { int32_t segment = data_->SearchUShort(StartCodeOffset(seg_count_), DataSize::kUSHORT, Offset::kFormat4EndCount, DataSize::kUSHORT, seg_count_, character); if (segment == -1) { return CMapTable::NOTDEF; } int32_t start_code = StartCode(segment); return RetrieveGlyphId(segment, start_code, character); } int32_t CMapTable::CMapFormat4::RetrieveGlyphId(int32_t segment, int32_t start_code, int32_t character) { if (character < start_code) { return CMapTable::NOTDEF; } int32_t id_range_offset = IdRangeOffset(segment); if (id_range_offset == 0) { return (character + IdDelta(segment)) % 65536; } return data_->ReadUShort(id_range_offset + IdRangeOffsetLocation(segment) + 2 * (character - start_code)); } int32_t CMapTable::CMapFormat4::seg_count() { return seg_count_; } int32_t CMapTable::CMapFormat4::Length() { return Length(data_); } int32_t CMapTable::CMapFormat4::StartCode(int32_t segment) { if (!IsValidIndex(segment)) { return -1; } return StartCode(data_.p_, seg_count_, segment); } // static int32_t CMapTable::CMapFormat4::Language(ReadableFontData* data) { int32_t language = data->ReadUShort(Offset::kFormat4Language); return language; } // static int32_t CMapTable::CMapFormat4::Length(ReadableFontData* data) { int32_t length = data->ReadUShort(Offset::kFormat4Length); return length; } // static int32_t CMapTable::CMapFormat4::SegCount(ReadableFontData* data) { int32_t seg_count = data->ReadUShort(Offset::kFormat4SegCountX2) / 2; return seg_count; } // static int32_t CMapTable::CMapFormat4::StartCode(ReadableFontData* data, int32_t seg_count, int32_t index) { int32_t start_code = data->ReadUShort(StartCodeOffset(seg_count) + index * DataSize::kUSHORT); return start_code; } // static int32_t CMapTable::CMapFormat4::StartCodeOffset(int32_t seg_count) { int32_t start_code_offset = Offset::kFormat4EndCount + (seg_count + 1) * DataSize::kUSHORT; return start_code_offset; } // static int32_t CMapTable::CMapFormat4::EndCode(ReadableFontData* data, int32_t seg_count, int32_t index) { int32_t end_code = data->ReadUShort(Offset::kFormat4EndCount + index * DataSize::kUSHORT); return end_code; } // static int32_t CMapTable::CMapFormat4::IdDelta(ReadableFontData* data, int32_t seg_count, int32_t index) { int32_t id_delta = data->ReadUShort(IdDeltaOffset(seg_count) + index * DataSize::kUSHORT); return id_delta; } // static int32_t CMapTable::CMapFormat4::IdDeltaOffset(int32_t seg_count) { int32_t id_delta_offset = Offset::kFormat4EndCount + (2 * seg_count + 1) * DataSize::kUSHORT; return id_delta_offset; } // static int32_t CMapTable::CMapFormat4::IdRangeOffset(ReadableFontData* data, int32_t seg_count, int32_t index) { int32_t id_range_offset = data->ReadUShort(IdRangeOffsetOffset(seg_count) + index * DataSize::kUSHORT); return id_range_offset; } // static int32_t CMapTable::CMapFormat4::IdRangeOffsetOffset(int32_t seg_count) { int32_t id_range_offset_offset = Offset::kFormat4EndCount + (2 * seg_count + 1) * DataSize::kUSHORT + seg_count * DataSize::kSHORT; return id_range_offset_offset; } // static int32_t CMapTable::CMapFormat4::GlyphIdArrayOffset(int32_t seg_count) { int32_t glyph_id_array_offset = Offset::kFormat4EndCount + (3 * seg_count + 1) * DataSize::kUSHORT + seg_count * DataSize::kSHORT; return glyph_id_array_offset; } int32_t CMapTable::CMapFormat4::EndCode(int32_t segment) { if (IsValidIndex(segment)) { return EndCode(data_, seg_count_, segment); } #if defined (SFNTLY_NO_EXCEPTION) return -1; #else throw IllegalArgumentException(); #endif } bool CMapTable::CMapFormat4::IsValidIndex(int32_t segment) { if (segment < 0 || segment >= seg_count_) { #if defined (SFNTLY_NO_EXCEPTION) return false; #else throw IllegalArgumentException(); #endif } return true; } int32_t CMapTable::CMapFormat4::IdDelta(int32_t segment) { if (IsValidIndex(segment)) return IdDelta(data_, seg_count_, segment); return -1; } int32_t CMapTable::CMapFormat4::IdRangeOffset(int32_t segment) { if (IsValidIndex(segment)) return data_->ReadUShort(IdRangeOffsetLocation(segment)); return -1; } int32_t CMapTable::CMapFormat4::IdRangeOffsetLocation(int32_t segment) { if (IsValidIndex(segment)) return IdRangeOffsetOffset(seg_count_) + segment * DataSize::kUSHORT; return -1; } int32_t CMapTable::CMapFormat4::GlyphIdArray(int32_t index) { return data_->ReadUShort(glyph_id_array_offset_ + index * DataSize::kUSHORT); } int32_t CMapTable::CMapFormat4::Language() { return Language(data_); } CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat4::Iterator() { return new CharacterIterator(this); } /****************************************************************************** * CMapTable::CMapFormat4::CharacterIterator class ******************************************************************************/ CMapTable::CMapFormat4::CharacterIterator::CharacterIterator( CMapFormat4* parent) : parent_(parent), segment_index_(0), first_char_in_segment_(-1), last_char_in_segment_(-1), next_char_(-1), next_char_set_(false) { } bool CMapTable::CMapFormat4::CharacterIterator::HasNext() { if (next_char_set_) return true; while (segment_index_ < parent_->seg_count_) { if (first_char_in_segment_ < 0) { first_char_in_segment_ = parent_->StartCode(segment_index_); last_char_in_segment_ = parent_->EndCode(segment_index_); next_char_ = first_char_in_segment_; next_char_set_ = true; return true; } if (next_char_ < last_char_in_segment_) { next_char_++; next_char_set_ = true; return true; } segment_index_++; first_char_in_segment_ = -1; } return false; } int32_t CMapTable::CMapFormat4::CharacterIterator::Next() { if (!next_char_set_) { if (!HasNext()) { #if defined (SFNTLY_NO_EXCEPTION) return -1; #else throw NoSuchElementException("No more characters to iterate."); #endif } } next_char_set_ = false; return next_char_; } /****************************************************************************** * CMapTable::CMapFormat4::Builder::Segment class ******************************************************************************/ CMapTable::CMapFormat4::Builder::Segment::Segment() {} CMapTable::CMapFormat4::Builder::Segment::Segment(Segment* other) : start_count_(other->start_count_), end_count_(other->end_count_), id_delta_(other->id_delta_), id_range_offset_(other->id_range_offset_) { } CMapTable::CMapFormat4::Builder::Segment::Segment(int32_t start_count, int32_t end_count, int32_t id_delta, int32_t id_range_offset) : start_count_(start_count), end_count_(end_count), id_delta_(id_delta), id_range_offset_(id_range_offset) { } CMapTable::CMapFormat4::Builder::Segment::~Segment() {} int32_t CMapTable::CMapFormat4::Builder::Segment::start_count() { return start_count_; } void CMapTable::CMapFormat4::Builder::Segment::set_start_count(int32_t start_count) { start_count_ = start_count; } int32_t CMapTable::CMapFormat4::Builder::Segment::end_count() { return end_count_; } void CMapTable::CMapFormat4::Builder::Segment::set_end_count(int32_t end_count) { end_count_ = end_count; } int32_t CMapTable::CMapFormat4::Builder::Segment::id_delta() { return id_delta_; } void CMapTable::CMapFormat4::Builder::Segment::set_id_delta(int32_t id_delta) { id_delta_ = id_delta; } int32_t CMapTable::CMapFormat4::Builder::Segment::id_range_offset() { return id_range_offset_; } void CMapTable::CMapFormat4::Builder::Segment:: set_id_range_offset(int32_t id_range_offset) { id_range_offset_ = id_range_offset; } // static CALLER_ATTACH SegmentList* CMapTable::CMapFormat4::Builder::Segment::DeepCopy(SegmentList* original) { SegmentList* list = new SegmentList; for (SegmentList::iterator it = original->begin(), e = original->end(); it != e; ++it) { list->push_back(*it); } return list; } /****************************************************************************** * CMapTable::CMapFormat4::Builder class ******************************************************************************/ CALLER_ATTACH CMapTable::CMapFormat4::Builder* CMapTable::CMapFormat4::Builder::NewInstance(ReadableFontData* data, int32_t offset, const CMapId& cmap_id) { ReadableFontDataPtr rdata; if (data) { rdata.Attach (down_cast (data->Slice(offset, data->ReadUShort(offset + Offset::kFormat4Length)))); } return new Builder(rdata, CMapFormat::kFormat4, cmap_id); } CALLER_ATTACH CMapTable::CMapFormat4::Builder* CMapTable::CMapFormat4::Builder::NewInstance(WritableFontData* data, int32_t offset, const CMapId& cmap_id) { WritableFontDataPtr wdata; if (data) { wdata.Attach (down_cast (data->Slice(offset, data->ReadUShort(offset + Offset::kFormat4Length)))); } return new Builder(wdata, CMapFormat::kFormat4, cmap_id); } CALLER_ATTACH CMapTable::CMapFormat4::Builder* CMapTable::CMapFormat4::Builder::NewInstance(const CMapId& cmap_id) { return new Builder(cmap_id); } CMapTable::CMapFormat4::Builder::Builder(ReadableFontData* data, int32_t offset, const CMapId& cmap_id) : CMap::Builder(data, CMapFormat::kFormat4, cmap_id) { } CMapTable::CMapFormat4::Builder::Builder(WritableFontData* data, int32_t offset, const CMapId& cmap_id) : CMap::Builder(data, CMapFormat::kFormat4, cmap_id) { } CMapTable::CMapFormat4::Builder::Builder(SegmentList* segments, IntegerList* glyph_id_array, const CMapId& cmap_id) : CMap::Builder(reinterpret_cast(NULL), CMapFormat::kFormat4, cmap_id), segments_(segments->begin(), segments->end()), glyph_id_array_(glyph_id_array->begin(), glyph_id_array->end()) { set_model_changed(); } CMapTable::CMapFormat4::Builder::Builder(const CMapId& cmap_id) : CMap::Builder(reinterpret_cast(NULL), CMapFormat::kFormat4, cmap_id) { } CMapTable::CMapFormat4::Builder::~Builder() {} void CMapTable::CMapFormat4::Builder::Initialize(ReadableFontData* data) { if (data == NULL || data->Length() == 0) return; // build segments int32_t seg_count = CMapFormat4::SegCount(data); for (int32_t index = 0; index < seg_count; ++index) { Ptr segment = new Segment; segment->set_start_count(CMapFormat4::StartCode(data, seg_count, index)); #if defined SFNTLY_DEBUG_CMAP fprintf(stderr, "Segment %d; start %d\n", index, segment->start_count()); #endif segment->set_end_count(CMapFormat4::EndCode(data, seg_count, index)); segment->set_id_delta(CMapFormat4::IdDelta(data, seg_count, index)); segment->set_id_range_offset(CMapFormat4::IdRangeOffset(data, seg_count, index)); segments_.push_back(segment); } // build glyph id array int32_t glyph_id_array_offset = CMapFormat4::GlyphIdArrayOffset(seg_count); int32_t glyph_id_array_length = (CMapFormat4::Length(data) - glyph_id_array_offset) / DataSize::kUSHORT; fprintf(stderr, "id array size %d\n", glyph_id_array_length); for (int32_t i = 0; i < glyph_id_array_length; i += DataSize::kUSHORT) { glyph_id_array_.push_back(data->ReadUShort(glyph_id_array_offset + i)); } } SegmentList* CMapTable::CMapFormat4::Builder::segments() { if (segments_.empty()) { Initialize(InternalReadData()); set_model_changed(); } return &segments_; } void CMapTable::CMapFormat4::Builder::set_segments(SegmentList* segments) { segments_.assign(segments->begin(), segments->end()); set_model_changed(); } IntegerList* CMapTable::CMapFormat4::Builder::glyph_id_array() { if (glyph_id_array_.empty()) { Initialize(InternalReadData()); set_model_changed(); } return &glyph_id_array_; } void CMapTable::CMapFormat4::Builder:: set_glyph_id_array(IntegerList* glyph_id_array) { glyph_id_array_.assign(glyph_id_array->begin(), glyph_id_array->end()); set_model_changed(); } CALLER_ATTACH FontDataTable* CMapTable::CMapFormat4::Builder::SubBuildTable(ReadableFontData* data) { FontDataTablePtr table = new CMapFormat4(data, cmap_id()); return table.Detach(); } void CMapTable::CMapFormat4::Builder::SubDataSet() { segments_.clear(); glyph_id_array_.clear(); set_model_changed(); } int32_t CMapTable::CMapFormat4::Builder::SubDataSizeToSerialize() { if (!model_changed()) { return CMap::Builder::SubDataSizeToSerialize(); } int32_t size = Offset::kFormat4FixedSize + segments_.size() * (3 * DataSize::kUSHORT + DataSize::kSHORT) + glyph_id_array_.size() * DataSize::kSHORT; return size; } bool CMapTable::CMapFormat4::Builder::SubReadyToSerialize() { if (!model_changed()) { return CMap::Builder::SubReadyToSerialize(); } if (!segments()->empty()) { return true; } return false; } int32_t CMapTable::CMapFormat4::Builder::SubSerialize(WritableFontData* new_data) { if (!model_changed()) { return CMap::Builder::SubSerialize(new_data); } int32_t index = 0; index += new_data->WriteUShort(index, CMapFormat::kFormat4); index += DataSize::kUSHORT; // length - write this at the end index += new_data->WriteUShort(index, language()); int32_t seg_count = segments_.size(); index += new_data->WriteUShort(index, seg_count * 2); int32_t log2_seg_count = FontMath::Log2(seg_count); int32_t search_range = 1 << (log2_seg_count + 1); index += new_data->WriteUShort(index, search_range); int32_t entry_selector = log2_seg_count; index += new_data->WriteUShort(index, entry_selector); int32_t range_shift = 2 * seg_count - search_range; index += new_data->WriteUShort(index, range_shift); for (int32_t i = 0; i < seg_count; ++i) { index += new_data->WriteUShort(index, segments_[i]->end_count()); } index += new_data->WriteUShort(index, 0); // reserved ushort for (int32_t i = 0; i < seg_count; ++i) { #if defined SFNTLY_DEBUG_CMAP fprintf(stderr, "Segment %d; start %d\n", i, segments_[i]->start_count()); #endif index += new_data->WriteUShort(index, segments_[i]->start_count()); } for (int32_t i = 0; i < seg_count; ++i) { index += new_data->WriteShort(index, segments_[i]->id_delta()); } for (int32_t i = 0; i < seg_count; ++i) { index += new_data->WriteUShort(index, segments_[i]->id_range_offset()); } #if defined SFNTLY_DEBUG_CMAP fprintf(stderr, "Glyph id array size %lu\n", glyph_id_array_.size()); #endif for (size_t i = 0; i < glyph_id_array_.size(); ++i) { index += new_data->WriteUShort(index, glyph_id_array_[i]); } new_data->WriteUShort(Offset::kFormat4Length, index); return index; } /****************************************************************************** * CMapTable::Builder class ******************************************************************************/ CMapTable::Builder::Builder(Header* header, WritableFontData* data) : SubTableContainerTable::Builder(header, data), version_(0) { } CMapTable::Builder::Builder(Header* header, ReadableFontData* data) : SubTableContainerTable::Builder(header, data), version_(0) { } CMapTable::Builder::~Builder() { } int32_t CMapTable::Builder::SubSerialize(WritableFontData* new_data) { int32_t size = new_data->WriteUShort(CMapTable::Offset::kVersion, version_); size += new_data->WriteUShort(CMapTable::Offset::kNumTables, GetCMapBuilders()->size()); int32_t index_offset = size; size += GetCMapBuilders()->size() * CMapTable::Offset::kEncodingRecordSize; for (CMapBuilderMap::iterator it = GetCMapBuilders()->begin(), e = GetCMapBuilders()->end(); it != e; ++it) { CMapBuilderPtr b = it->second; // header entry index_offset += new_data->WriteUShort(index_offset, b->platform_id()); index_offset += new_data->WriteUShort(index_offset, b->encoding_id()); index_offset += new_data->WriteULong(index_offset, size); // cmap FontDataPtr slice; slice.Attach(new_data->Slice(size)); size += b->SubSerialize(down_cast(slice.p_)); } return size; } bool CMapTable::Builder::SubReadyToSerialize() { if (GetCMapBuilders()->empty()) return false; // check each table for (CMapBuilderMap::iterator it = GetCMapBuilders()->begin(), e = GetCMapBuilders()->end(); it != e; ++it) { if (!it->second->SubReadyToSerialize()) return false; } return true; } int32_t CMapTable::Builder::SubDataSizeToSerialize() { if (GetCMapBuilders()->empty()) return 0; bool variable = false; int32_t size = CMapTable::Offset::kEncodingRecordStart + GetCMapBuilders()->size() * CMapTable::Offset::kEncodingRecordSize; // calculate size of each table for (CMapBuilderMap::iterator it = GetCMapBuilders()->begin(), e = GetCMapBuilders()->end(); it != e; ++it) { int32_t cmap_size = it->second->SubDataSizeToSerialize(); size += abs(cmap_size); variable |= cmap_size <= 0; } return variable ? -size : size; } void CMapTable::Builder::SubDataSet() { GetCMapBuilders()->clear(); Table::Builder::set_model_changed(); } CALLER_ATTACH FontDataTable* CMapTable::Builder::SubBuildTable(ReadableFontData* data) { FontDataTablePtr table = new CMapTable(header(), data); return table.Detach(); } CALLER_ATTACH CMapTable::Builder* CMapTable::Builder::CreateBuilder(Header* header, WritableFontData* data) { Ptr builder; builder = new CMapTable::Builder(header, data); return builder.Detach(); } // static CALLER_ATTACH CMapTable::CMap::Builder* CMapTable::Builder::CMapBuilder(ReadableFontData* data, int32_t index) { if (index < 0 || index > NumCMaps(data)) { #if !defined (SFNTLY_NO_EXCEPTION) throw IndexOutOfBoundException( "CMap table is outside of the bounds of the known tables."); #endif return NULL; } int32_t platform_id = data->ReadUShort(Offset::kEncodingRecordPlatformId + OffsetForEncodingRecord(index)); int32_t encoding_id = data->ReadUShort(Offset::kEncodingRecordEncodingId + OffsetForEncodingRecord(index)); int32_t offset = data->ReadULongAsInt(Offset::kEncodingRecordOffset + OffsetForEncodingRecord(index)); return CMap::Builder::GetBuilder(data, offset, NewCMapId(platform_id, encoding_id)); } // static int32_t CMapTable::Builder::NumCMaps(ReadableFontData* data) { if (data == NULL) { return 0; } return data->ReadUShort(Offset::kNumTables); } int32_t CMapTable::Builder::NumCMaps() { return GetCMapBuilders()->size(); } void CMapTable::Builder::Initialize(ReadableFontData* data) { int32_t num_cmaps = NumCMaps(data); for (int32_t i = 0; i < num_cmaps; ++i) { CMapTable::CMap::Builder* cmap_builder = CMapBuilder(data, i); if (!cmap_builder) continue; cmap_builders_[cmap_builder->cmap_id()] = cmap_builder; } } CMapTable::CMap::Builder* CMapTable::Builder::NewCMapBuilder( const CMapId& cmap_id, ReadableFontData* data) { Ptr wfd; wfd.Attach(WritableFontData::CreateWritableFontData(data->Size())); data->CopyTo(wfd.p_); CMapTable::CMapBuilderPtr builder; builder.Attach(CMap::Builder::GetBuilder(wfd.p_, 0, cmap_id)); CMapBuilderMap* cmap_builders = CMapTable::Builder::GetCMapBuilders(); cmap_builders->insert(std::make_pair(cmap_id, builder.p_)); return builder.Detach(); } CMapTable::CMap::Builder* CMapTable::Builder::NewCMapBuilder(int32_t format, const CMapId& cmap_id) { Ptr cmap_builder; cmap_builder.Attach(CMap::Builder::GetBuilder(format, cmap_id)); CMapBuilderMap* cmap_builders = CMapTable::Builder::GetCMapBuilders(); cmap_builders->insert(std::make_pair(cmap_id, cmap_builder.p_)); return cmap_builder.Detach(); } CMapTable::CMap::Builder* CMapTable::Builder::CMapBuilder(const CMapId& cmap_id) { CMapBuilderMap* cmap_builders = this->GetCMapBuilders(); CMapBuilderMap::iterator builder = cmap_builders->find(cmap_id); if (builder != cmap_builders->end()) return builder->second; #ifndef SFNTLY_NO_EXCEPTION throw NoSuchElementException("No builder found for cmap_id"); #else return NULL; #endif } CMapTable::CMapBuilderMap* CMapTable::Builder::GetCMapBuilders() { if (cmap_builders_.empty()) { Initialize(InternalReadData()); set_model_changed(); } return &cmap_builders_; } } // namespace sfntly