/* * 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. */ #include "sfntly/table/core/name_table.h" #include #include #include #include "sfntly/font.h" #include "sfntly/port/exception_type.h" namespace sfntly { /****************************************************************************** * NameTable::NameEntryId class ******************************************************************************/ NameTable::NameEntryId::NameEntryId() : platform_id_(0), encoding_id_(0), language_id_(0), name_id_(0) { } NameTable::NameEntryId::NameEntryId(int32_t platform_id, int32_t encoding_id, int32_t language_id, int32_t name_id) : platform_id_(platform_id), encoding_id_(encoding_id), language_id_(language_id), name_id_(name_id) { } NameTable::NameEntryId::NameEntryId(const NameTable::NameEntryId& rhs) { *this = rhs; } const NameTable::NameEntryId& NameTable::NameEntryId::operator=(const NameTable::NameEntryId& rhs) const { platform_id_ = rhs.platform_id_; encoding_id_ = rhs.encoding_id_; language_id_ = rhs.language_id_; name_id_ = rhs.name_id_; return *this; } bool NameTable::NameEntryId::operator==(const NameEntryId& rhs) const { return platform_id_ == rhs.platform_id_ && encoding_id_ == rhs.encoding_id_ && language_id_ == rhs.language_id_ && name_id_ == rhs.name_id_; } bool NameTable::NameEntryId::operator<(const NameEntryId& rhs) const { if (platform_id_ != rhs.platform_id_) return platform_id_ < rhs.platform_id_; if (encoding_id_ != rhs.encoding_id_) return encoding_id_ < rhs.encoding_id_; if (language_id_ != rhs.language_id_) return language_id_ < rhs.language_id_; return name_id_ < rhs.name_id_; } /****************************************************************************** * NameTable::NameEntry class ******************************************************************************/ NameTable::NameEntry::NameEntry() { Init(0, 0, 0, 0, NULL); } NameTable::NameEntry::NameEntry(const NameEntryId& name_entry_id, const ByteVector& name_bytes) { Init(name_entry_id.platform_id(), name_entry_id.encoding_id(), name_entry_id.language_id(), name_entry_id.name_id(), &name_bytes); } NameTable::NameEntry::NameEntry(int32_t platform_id, int32_t encoding_id, int32_t language_id, int32_t name_id, const ByteVector& name_bytes) { Init(platform_id, encoding_id, language_id, name_id, &name_bytes); } NameTable::NameEntry::~NameEntry() {} ByteVector* NameTable::NameEntry::NameAsBytes() { return &name_bytes_; } int32_t NameTable::NameEntry::NameBytesLength() { return name_bytes_.size(); } UChar* NameTable::NameEntry::Name() { return NameTable::ConvertFromNameBytes(&name_bytes_, platform_id(), encoding_id()); } bool NameTable::NameEntry::operator==(const NameEntry& rhs) const { return (name_entry_id_ == rhs.name_entry_id_ && name_bytes_ == rhs.name_bytes_); } void NameTable::NameEntry::Init(int32_t platform_id, int32_t encoding_id, int32_t language_id, int32_t name_id, const ByteVector* name_bytes) { name_entry_id_ = NameEntryId(platform_id, encoding_id, language_id, name_id); if (name_bytes) { name_bytes_ = *name_bytes; } else { name_bytes_.clear(); } } /****************************************************************************** * NameTable::NameEntryBuilder class ******************************************************************************/ NameTable::NameEntryBuilder::NameEntryBuilder() { Init(0, 0, 0, 0, NULL); } NameTable::NameEntryBuilder::NameEntryBuilder(const NameEntryId& name_entry_id, const ByteVector& name_bytes) { Init(name_entry_id.platform_id(), name_entry_id.encoding_id(), name_entry_id.language_id(), name_entry_id.name_id(), &name_bytes); } NameTable::NameEntryBuilder::NameEntryBuilder( const NameEntryId& name_entry_id) { Init(name_entry_id.platform_id(), name_entry_id.encoding_id(), name_entry_id.language_id(), name_entry_id.name_id(), NULL); } NameTable::NameEntryBuilder::NameEntryBuilder(NameEntry* b) { Init(b->platform_id(), b->encoding_id(), b->language_id(), b->name_id(), b->NameAsBytes()); } NameTable::NameEntryBuilder::~NameEntryBuilder() {} void NameTable::NameEntryBuilder::SetName(const UChar* name) { if (name == NULL) { name_entry_->name_bytes_.clear(); return; } NameTable::ConvertToNameBytes(name, name_entry_->platform_id(), name_entry_->encoding_id(), &name_entry_->name_bytes_); } void NameTable::NameEntryBuilder::SetName(const ByteVector& name_bytes) { name_entry_->name_bytes_.clear(); std::copy(name_bytes.begin(), name_bytes.end(), name_entry_->name_bytes_.begin()); } void NameTable::NameEntryBuilder::SetName(const ByteVector& name_bytes, int32_t offset, int32_t length) { name_entry_->name_bytes_.clear(); std::copy(name_bytes.begin() + offset, name_bytes.begin() + offset + length, name_entry_->name_bytes_.begin()); } void NameTable::NameEntryBuilder::Init(int32_t platform_id, int32_t encoding_id, int32_t language_id, int32_t name_id, const ByteVector* name_bytes) { name_entry_ = new NameEntry(); name_entry_->Init(platform_id, encoding_id, language_id, name_id, name_bytes); } /****************************************************************************** * NameTable::NameEntryFilterInPlace class (C++ port only) ******************************************************************************/ NameTable::NameEntryFilterInPlace::NameEntryFilterInPlace(int32_t platform_id, int32_t encoding_id, int32_t language_id, int32_t name_id) : platform_id_(platform_id), encoding_id_(encoding_id), language_id_(language_id), name_id_(name_id) { } bool NameTable::NameEntryFilterInPlace::Accept(int32_t platform_id, int32_t encoding_id, int32_t language_id, int32_t name_id) { return (platform_id_ == platform_id && encoding_id_ == encoding_id && language_id_ == language_id && name_id_ == name_id); } /****************************************************************************** * NameTable::NameEntryIterator class ******************************************************************************/ NameTable::NameEntryIterator::NameEntryIterator(NameTable* table) : RefIterator(table), name_index_(0), filter_(NULL) { } NameTable::NameEntryIterator::NameEntryIterator(NameTable* table, NameEntryFilter* filter) : RefIterator(table), name_index_(0), filter_(filter) { } bool NameTable::NameEntryIterator::HasNext() { if (!filter_) { if (name_index_ < container()->NameCount()) { return true; } return false; } for (; name_index_ < container()->NameCount(); ++name_index_) { if (filter_->Accept(container()->PlatformId(name_index_), container()->EncodingId(name_index_), container()->LanguageId(name_index_), container()->NameId(name_index_))) { return true; } } return false; } CALLER_ATTACH NameTable::NameEntry* NameTable::NameEntryIterator::Next() { if (!HasNext()) return NULL; return container()->GetNameEntry(name_index_++); } /****************************************************************************** * NameTable::Builder class ******************************************************************************/ NameTable::Builder::Builder(Header* header, WritableFontData* data) : SubTableContainerTable::Builder(header, data) { } NameTable::Builder::Builder(Header* header, ReadableFontData* data) : SubTableContainerTable::Builder(header, data) { } CALLER_ATTACH NameTable::Builder* NameTable::Builder::CreateBuilder(Header* header, WritableFontData* data) { Ptr builder; builder = new NameTable::Builder(header, data); return builder.Detach(); } void NameTable::Builder::RevertNames() { name_entry_map_.clear(); set_model_changed(false); } int32_t NameTable::Builder::BuilderCount() { GetNameBuilders(); // Ensure name_entry_map_ is built. return (int32_t)name_entry_map_.size(); } bool NameTable::Builder::Has(int32_t platform_id, int32_t encoding_id, int32_t language_id, int32_t name_id) { NameEntryId probe(platform_id, encoding_id, language_id, name_id); GetNameBuilders(); // Ensure name_entry_map_ is built. return (name_entry_map_.find(probe) != name_entry_map_.end()); } CALLER_ATTACH NameTable::NameEntryBuilder* NameTable::Builder::NameBuilder(int32_t platform_id, int32_t encoding_id, int32_t language_id, int32_t name_id) { NameEntryId probe(platform_id, encoding_id, language_id, name_id); NameEntryBuilderMap builders; GetNameBuilders(); // Ensure name_entry_map_ is built. if (name_entry_map_.find(probe) != name_entry_map_.end()) { return name_entry_map_[probe]; } NameEntryBuilderPtr builder = new NameEntryBuilder(probe); name_entry_map_[probe] = builder; return builder.Detach(); } bool NameTable::Builder::Remove(int32_t platform_id, int32_t encoding_id, int32_t language_id, int32_t name_id) { NameEntryId probe(platform_id, encoding_id, language_id, name_id); GetNameBuilders(); // Ensure name_entry_map_ is built. NameEntryBuilderMap::iterator position = name_entry_map_.find(probe); if (position != name_entry_map_.end()) { name_entry_map_.erase(position); return true; } return false; } CALLER_ATTACH FontDataTable* NameTable::Builder::SubBuildTable(ReadableFontData* data) { FontDataTablePtr table = new NameTable(header(), data); return table.Detach(); } void NameTable::Builder::SubDataSet() { name_entry_map_.clear(); set_model_changed(false); } int32_t NameTable::Builder::SubDataSizeToSerialize() { if (name_entry_map_.empty()) { return 0; } int32_t size = NameTable::Offset::kNameRecordStart + name_entry_map_.size() * NameTable::Offset::kNameRecordSize; for (NameEntryBuilderMap::iterator b = name_entry_map_.begin(), end = name_entry_map_.end(); b != end; ++b) { NameEntryBuilderPtr p = b->second; NameEntry* entry = p->name_entry(); size += entry->NameBytesLength(); } return size; } bool NameTable::Builder::SubReadyToSerialize() { return !name_entry_map_.empty(); } int32_t NameTable::Builder::SubSerialize(WritableFontData* new_data) { int32_t string_table_start_offset = NameTable::Offset::kNameRecordStart + name_entry_map_.size() * NameTable::Offset::kNameRecordSize; // Header new_data->WriteUShort(NameTable::Offset::kFormat, 0); new_data->WriteUShort(NameTable::Offset::kCount, name_entry_map_.size()); new_data->WriteUShort(NameTable::Offset::kStringOffset, string_table_start_offset); int32_t name_record_offset = NameTable::Offset::kNameRecordStart; int32_t string_offset = 0; // Note: we offered operator< in NameEntryId, which will be used by std::less, // and therefore our map will act like TreeMap in Java to provide // sorted key set. for (NameEntryBuilderMap::iterator b = name_entry_map_.begin(), end = name_entry_map_.end(); b != end; ++b) { new_data->WriteUShort( name_record_offset + NameTable::Offset::kNameRecordPlatformId, b->first.platform_id()); new_data->WriteUShort( name_record_offset + NameTable::Offset::kNameRecordEncodingId, b->first.encoding_id()); new_data->WriteUShort( name_record_offset + NameTable::Offset::kNameRecordLanguageId, b->first.language_id()); new_data->WriteUShort( name_record_offset + NameTable::Offset::kNameRecordNameId, b->first.name_id()); NameEntry* builder_entry = b->second->name_entry(); new_data->WriteUShort( name_record_offset + NameTable::Offset::kNameRecordStringLength, builder_entry->NameBytesLength()); new_data->WriteUShort( name_record_offset + NameTable::Offset::kNameRecordStringOffset, string_offset); name_record_offset += NameTable::Offset::kNameRecordSize; string_offset += new_data->WriteBytes( string_offset + string_table_start_offset, builder_entry->NameAsBytes()); } return string_offset + string_table_start_offset; } void NameTable::Builder::Initialize(ReadableFontData* data) { if (data) { NameTablePtr table = new NameTable(header(), data); Ptr name_iter; name_iter.Attach(table->Iterator()); while (name_iter->HasNext()) { NameEntryPtr name_entry; name_entry.Attach(name_iter->Next()); NameEntryBuilderPtr name_entry_builder = new NameEntryBuilder(name_entry); NameEntry* builder_entry = name_entry_builder->name_entry(); NameEntryId probe = builder_entry->name_entry_id(); name_entry_map_[probe] = name_entry_builder; } } } NameTable::NameEntryBuilderMap* NameTable::Builder::GetNameBuilders() { if (name_entry_map_.empty()) { Initialize(InternalReadData()); } set_model_changed(); return &name_entry_map_; } /****************************************************************************** * NameTable class ******************************************************************************/ NameTable::~NameTable() {} int32_t NameTable::Format() { return data_->ReadUShort(Offset::kFormat); } int32_t NameTable::NameCount() { return data_->ReadUShort(Offset::kCount); } int32_t NameTable::PlatformId(int32_t index) { return data_->ReadUShort(Offset::kNameRecordPlatformId + OffsetForNameRecord(index)); } int32_t NameTable::EncodingId(int32_t index) { return data_->ReadUShort(Offset::kNameRecordEncodingId + OffsetForNameRecord(index)); } int32_t NameTable::LanguageId(int32_t index) { return data_->ReadUShort(Offset::kNameRecordLanguageId + OffsetForNameRecord(index)); } int32_t NameTable::NameId(int32_t index) { return data_->ReadUShort(Offset::kNameRecordNameId + OffsetForNameRecord(index)); } void NameTable::NameAsBytes(int32_t index, ByteVector* b) { assert(b); b->clear(); int32_t length = NameLength(index); if (length <= 0) return; b->resize(length); data_->ReadBytes(NameOffset(index), &((*b)[0]), 0, length); } void NameTable::NameAsBytes(int32_t platform_id, int32_t encoding_id, int32_t language_id, int32_t name_id, ByteVector* b) { assert(b); NameEntryPtr entry; entry.Attach(GetNameEntry(platform_id, encoding_id, language_id, name_id)); if (entry) { ByteVector* name = entry->NameAsBytes(); std::copy(name->begin(), name->end(), b->begin()); } } UChar* NameTable::Name(int32_t index) { ByteVector b; NameAsBytes(index, &b); return ConvertFromNameBytes(&b, PlatformId(index), EncodingId(index)); } UChar* NameTable::Name(int32_t platform_id, int32_t encoding_id, int32_t language_id, int32_t name_id) { NameEntryPtr entry; entry.Attach(GetNameEntry(platform_id, encoding_id, language_id, name_id)); if (entry) { return entry->Name(); } return NULL; } CALLER_ATTACH NameTable::NameEntry* NameTable::GetNameEntry(int32_t index) { ByteVector b; NameAsBytes(index, &b); NameEntryPtr instance = new NameEntry(PlatformId(index), EncodingId(index), LanguageId(index), NameId(index), b); return instance.Detach(); } CALLER_ATTACH NameTable::NameEntry* NameTable::GetNameEntry(int32_t platform_id, int32_t encoding_id, int32_t language_id, int32_t name_id) { NameTable::NameEntryFilterInPlace filter(platform_id, encoding_id, language_id, name_id); Ptr name_entry_iter; name_entry_iter.Attach(Iterator(&filter)); NameEntryPtr result; if (name_entry_iter->HasNext()) { result = name_entry_iter->Next(); } return result; } CALLER_ATTACH NameTable::NameEntryIterator* NameTable::Iterator() { Ptr output = new NameTable::NameEntryIterator(this); return output.Detach(); } CALLER_ATTACH NameTable::NameEntryIterator* NameTable::Iterator(NameEntryFilter* filter) { Ptr output = new NameTable::NameEntryIterator(this, filter); return output.Detach(); } NameTable::NameTable(Header* header, ReadableFontData* data) : SubTableContainerTable(header, data) {} int32_t NameTable::StringOffset() { return data_->ReadUShort(Offset::kStringOffset); } int32_t NameTable::OffsetForNameRecord(int32_t index) { return Offset::kNameRecordStart + index * Offset::kNameRecordSize; } int32_t NameTable::NameLength(int32_t index) { return data_->ReadUShort(Offset::kNameRecordStringLength + OffsetForNameRecord(index)); } int32_t NameTable::NameOffset(int32_t index) { return data_->ReadUShort(Offset::kNameRecordStringOffset + OffsetForNameRecord(index)) + StringOffset(); } const char* NameTable::GetEncodingName(int32_t platform_id, int32_t encoding_id) { switch (platform_id) { case PlatformId::kUnicode: return "UTF-16BE"; case PlatformId::kMacintosh: switch (encoding_id) { case MacintoshEncodingId::kRoman: return "MacRoman"; case MacintoshEncodingId::kJapanese: return "Shift-JIS"; case MacintoshEncodingId::kChineseTraditional: return "Big5"; case MacintoshEncodingId::kKorean: return "EUC-KR"; case MacintoshEncodingId::kArabic: return "MacArabic"; case MacintoshEncodingId::kHebrew: return "MacHebrew"; case MacintoshEncodingId::kGreek: return "MacGreek"; case MacintoshEncodingId::kRussian: return "MacCyrillic"; case MacintoshEncodingId::kRSymbol: return "MacSymbol"; case MacintoshEncodingId::kThai: return "MacThai"; case MacintoshEncodingId::kChineseSimplified: return "EUC-CN"; default: // Note: unknown/unconfirmed cases are not ported. break; } break; case PlatformId::kISO: break; case PlatformId::kWindows: switch (encoding_id) { case WindowsEncodingId::kSymbol: case WindowsEncodingId::kUnicodeUCS2: return "UTF-16BE"; case WindowsEncodingId::kShiftJIS: return "windows-933"; case WindowsEncodingId::kPRC: return "windows-936"; case WindowsEncodingId::kBig5: return "windows-950"; case WindowsEncodingId::kWansung: return "windows-949"; case WindowsEncodingId::kJohab: return "ms1361"; case WindowsEncodingId::kUnicodeUCS4: return "UCS-4"; } break; case PlatformId::kCustom: break; default: break; } return NULL; } UConverter* NameTable::GetCharset(int32_t platform_id, int32_t encoding_id) { UErrorCode error_code = U_ZERO_ERROR; UConverter* conv = ucnv_open(GetEncodingName(platform_id, encoding_id), &error_code); if (U_SUCCESS(error_code)) { return conv; } if (conv) { ucnv_close(conv); } return NULL; } void NameTable::ConvertToNameBytes(const UChar* name, int32_t platform_id, int32_t encoding_id, ByteVector* b) { assert(b); assert(name); b->clear(); UConverter* cs = GetCharset(platform_id, encoding_id); if (cs == NULL) { return; } // Preflight to get buffer size. UErrorCode error_code = U_ZERO_ERROR; int32_t length = ucnv_fromUChars(cs, NULL, 0, name, -1, &error_code); b->resize(length + 4); // The longest termination "\0" is 4 bytes. memset(&((*b)[0]), 0, length + 4); error_code = U_ZERO_ERROR; ucnv_fromUChars(cs, reinterpret_cast(&((*b)[0])), length + 4, name, -1, &error_code); if (!U_SUCCESS(error_code)) { b->clear(); } ucnv_close(cs); } UChar* NameTable::ConvertFromNameBytes(ByteVector* name_bytes, int32_t platform_id, int32_t encoding_id) { if (name_bytes == NULL || name_bytes->size() == 0) { return NULL; } UConverter* cs = GetCharset(platform_id, encoding_id); UErrorCode error_code = U_ZERO_ERROR; if (cs == NULL) { char buffer[11] = {0}; #if defined (WIN32) _itoa_s(platform_id, buffer, 16); #else snprintf(buffer, sizeof(buffer), "%x", platform_id); #endif UChar* result = new UChar[12]; memset(result, 0, sizeof(UChar) * 12); cs = ucnv_open("utf-8", &error_code); if (U_SUCCESS(error_code)) { ucnv_toUChars(cs, result, 12, buffer, 11, &error_code); ucnv_close(cs); if (U_SUCCESS(error_code)) { return result; } } delete[] result; return NULL; } // No preflight needed here, we will be bigger. UChar* output_buffer = new UChar[name_bytes->size() + 1]; memset(output_buffer, 0, sizeof(UChar) * (name_bytes->size() + 1)); int32_t length = ucnv_toUChars(cs, output_buffer, name_bytes->size(), reinterpret_cast(&((*name_bytes)[0])), name_bytes->size(), &error_code); ucnv_close(cs); if (length > 0) { return output_buffer; } delete[] output_buffer; return NULL; } } // namespace sfntly