blob: 1c83d2e7d0e06937878a4cbc99a1eb2077e8f6c1 [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.
*/
// 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 <stdio.h>
#include <stdlib.h>
#include <utility>
#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<FontDataTable::Builder> 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<CMapTable::CMap*>(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<CMapTable::CMap> 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<CMapTable::CMap::Builder> 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<WritableFontData*>(
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<ReadableFontData*>(
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<ReadableFontData*>(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<WritableFontData*>(
data->Slice(offset, data->ReadUShort(
offset + Offset::kFormat0Length)))
: reinterpret_cast<WritableFontData*>(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<ReadableFontData*>(
data->Slice(offset, data->ReadUShort(
offset + Offset::kFormat0Length)))
: reinterpret_cast<ReadableFontData*>(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)
: CMap(data, CMapFormat::kFormat4, cmap_id),
seg_count_(SegCount(data)),
start_code_offset_(StartCodeOffset(seg_count_)),
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) {
UNREFERENCED_PARAMETER(seg_count);
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<ReadableFontData*>
(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<WritableFontData*>
(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) {
UNREFERENCED_PARAMETER(offset);
}
CMapTable::CMapFormat4::Builder::Builder(WritableFontData* data, int32_t offset,
const CMapId& cmap_id)
: CMap::Builder(data, CMapFormat::kFormat4, cmap_id) {
UNREFERENCED_PARAMETER(offset);
}
CMapTable::CMapFormat4::Builder::Builder(SegmentList* segments,
IntegerList* glyph_id_array,
const CMapId& cmap_id)
: CMap::Builder(reinterpret_cast<ReadableFontData*>(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<ReadableFontData*>(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> 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<WritableFontData*>(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<CMapTable::Builder> 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<WritableFontData> 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<CMapTable::CMap::Builder> 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