blob: 985313033bbcb44f57cce64152dd01bfafed22a7 [file] [log] [blame]
/*
* 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 <stdio.h>
#include <string.h>
#include <unicode/unistr.h>
#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) {
Init(table, NULL);
}
NameTable::NameEntryIterator::NameEntryIterator(NameTable* table,
NameEntryFilter* filter) {
Init(table, filter);
}
bool NameTable::NameEntryIterator::HasNext() {
if (!filter_) {
if (name_index_ < table_->NameCount()) {
return true;
}
return false;
}
for (; name_index_ < table_->NameCount(); ++name_index_) {
if (filter_->Accept(table_->PlatformId(name_index_),
table_->EncodingId(name_index_),
table_->LanguageId(name_index_),
table_->NameId(name_index_))) {
return true;
}
}
return false;
}
CALLER_ATTACH NameTable::NameEntry* NameTable::NameEntryIterator::Next() {
if (!HasNext())
return NULL;
return table_->GetNameEntry(name_index_++);
}
void NameTable::NameEntryIterator::Remove() {
#if !defined (SFNTLY_NO_EXCEPTION)
throw UnsupportedOperationException(
"Cannot remove a name table from an existing font.");
#endif
}
void NameTable::NameEntryIterator::Init(NameTable* table,
NameEntryFilter* filter) {
table_ = table;
filter_ = filter;
name_index_ = 0;
}
/******************************************************************************
* 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<NameTable::Builder> 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);
NameEntryIterator name_iter(table, NULL);
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);
int32_t length = NameLength(index);
b->clear();
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);
NameTable::NameEntryIterator* name_entry_iter = Iterator(&filter);
NameEntryPtr result;
if (name_entry_iter->HasNext()) {
result = name_entry_iter->Next();
}
delete name_entry_iter;
return result;
}
NameTable::NameEntryIterator* NameTable::Iterator() {
return new NameTable::NameEntryIterator(this);
}
NameTable::NameEntryIterator* NameTable::Iterator(NameEntryFilter* filter) {
return new NameTable::NameEntryIterator(this, filter);
}
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<char*>(&((*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) {
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<char*>(&((*name_bytes)[0])),
name_bytes->size(),
&error_code);
ucnv_close(cs);
if (length > 0) {
return output_buffer;
}
delete[] output_buffer;
return NULL;
}
} // namespace sfntly