blob: 347e0c13e972e0cecb741d8eecb5d10a64f45c6b [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/font.h"
#include <stdio.h>
#include <functional>
#include <algorithm>
#include <map>
#include <string>
#include <typeinfo>
#include <iterator>
#include "sfntly/data/font_input_stream.h"
#include "sfntly/font_factory.h"
#include "sfntly/math/fixed1616.h"
#include "sfntly/math/font_math.h"
#include "sfntly/port/exception_type.h"
#include "sfntly/table/core/font_header_table.h"
#include "sfntly/table/core/horizontal_device_metrics_table.h"
#include "sfntly/table/core/horizontal_header_table.h"
#include "sfntly/table/core/horizontal_metrics_table.h"
#include "sfntly/table/core/maximum_profile_table.h"
#include "sfntly/table/truetype/loca_table.h"
#include "sfntly/tag.h"
namespace sfntly {
const int32_t SFNTVERSION_MAJOR = 1;
const int32_t SFNTVERSION_MINOR = 0;
/******************************************************************************
* Font class
******************************************************************************/
Font::~Font() {}
bool Font::HasTable(int32_t tag) {
TableMap::const_iterator result = tables_.find(tag);
TableMap::const_iterator end = tables_.end();
return (result != end);
}
Table* Font::GetTable(int32_t tag) {
if (!HasTable(tag)) {
return NULL;
}
return tables_[tag];
}
const TableMap* Font::GetTableMap() {
return &tables_;
}
void Font::Serialize(OutputStream* os, IntegerList* table_ordering) {
assert(table_ordering);
IntegerList final_table_ordering;
GenerateTableOrdering(table_ordering, &final_table_ordering);
TableHeaderList table_records;
BuildTableHeadersForSerialization(&final_table_ordering, &table_records);
FontOutputStream fos(os);
SerializeHeader(&fos, &table_records);
SerializeTables(&fos, &table_records);
}
Font::Font(int32_t sfnt_version, ByteVector* digest)
: sfnt_version_(sfnt_version) {
// non-trivial assignments that makes debugging hard if placed in
// initialization list
digest_ = *digest;
}
void Font::BuildTableHeadersForSerialization(IntegerList* table_ordering,
TableHeaderList* table_headers) {
assert(table_headers);
assert(table_ordering);
IntegerList final_table_ordering;
GenerateTableOrdering(table_ordering, &final_table_ordering);
int32_t table_offset = Offset::kTableRecordBegin + num_tables() *
Offset::kTableRecordSize;
for (IntegerList::iterator tag = final_table_ordering.begin(),
tag_end = final_table_ordering.end();
tag != tag_end; ++tag) {
if (tables_.find(*tag) == tables_.end()) {
continue;
}
TablePtr table = tables_[*tag];
if (table != NULL) {
HeaderPtr header =
new Header(*tag, table->CalculatedChecksum(), table_offset,
table->header()->length());
table_headers->push_back(header);
table_offset += (table->DataLength() + 3) & ~3;
}
}
}
void Font::SerializeHeader(FontOutputStream* fos,
TableHeaderList* table_headers) {
fos->WriteFixed(sfnt_version_);
fos->WriteUShort(table_headers->size());
int32_t log2_of_max_power_of_2 = FontMath::Log2(table_headers->size());
int32_t search_range = 2 << (log2_of_max_power_of_2 - 1 + 4);
fos->WriteUShort(search_range);
fos->WriteUShort(log2_of_max_power_of_2);
fos->WriteUShort((table_headers->size() * 16) - search_range);
HeaderTagSortedSet sorted_headers;
std::copy(table_headers->begin(),
table_headers->end(),
std::inserter(sorted_headers, sorted_headers.end()));
for (HeaderTagSortedSet::iterator record = sorted_headers.begin(),
record_end = sorted_headers.end();
record != record_end; ++record) {
fos->WriteULong((*record)->tag());
fos->WriteULong((int32_t)((*record)->checksum()));
fos->WriteULong((*record)->offset());
fos->WriteULong((*record)->length());
}
}
void Font::SerializeTables(FontOutputStream* fos,
TableHeaderList* table_headers) {
assert(fos);
assert(table_headers);
for (TableHeaderList::iterator record = table_headers->begin(),
end_of_headers = table_headers->end();
record != end_of_headers; ++record) {
TablePtr target_table = GetTable((*record)->tag());
if (target_table == NULL) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IOException("Table out of sync with font header.");
#endif
return;
}
int32_t table_size = target_table->Serialize(fos);
if (table_size != (*record)->length()) {
assert(false);
}
int32_t filler_size = ((table_size + 3) & ~3) - table_size;
for (int32_t i = 0; i < filler_size; ++i) {
fos->Write(static_cast<byte_t>(0));
}
}
}
void Font::GenerateTableOrdering(IntegerList* default_table_ordering,
IntegerList* table_ordering) {
assert(default_table_ordering);
assert(table_ordering);
table_ordering->clear();
if (default_table_ordering->empty()) {
DefaultTableOrdering(default_table_ordering);
}
typedef std::map<int32_t, bool> Int2Bool;
typedef std::pair<int32_t, bool> Int2BoolEntry;
Int2Bool tables_in_font;
for (TableMap::iterator table = tables_.begin(), table_end = tables_.end();
table != table_end; ++table) {
tables_in_font.insert(Int2BoolEntry(table->first, false));
}
for (IntegerList::iterator tag = default_table_ordering->begin(),
tag_end = default_table_ordering->end();
tag != tag_end; ++tag) {
if (HasTable(*tag)) {
table_ordering->push_back(*tag);
tables_in_font[*tag] = true;
}
}
for (Int2Bool::iterator table = tables_in_font.begin(),
table_end = tables_in_font.end();
table != table_end; ++table) {
if (table->second == false)
table_ordering->push_back(table->first);
}
}
void Font::DefaultTableOrdering(IntegerList* default_table_ordering) {
assert(default_table_ordering);
default_table_ordering->clear();
if (HasTable(Tag::CFF)) {
default_table_ordering->resize(CFF_TABLE_ORDERING_SIZE);
std::copy(CFF_TABLE_ORDERING, CFF_TABLE_ORDERING + CFF_TABLE_ORDERING_SIZE,
default_table_ordering->begin());
return;
}
default_table_ordering->resize(TRUE_TYPE_TABLE_ORDERING_SIZE);
std::copy(TRUE_TYPE_TABLE_ORDERING,
TRUE_TYPE_TABLE_ORDERING + TRUE_TYPE_TABLE_ORDERING_SIZE,
default_table_ordering->begin());
}
/******************************************************************************
* Font::Builder class
******************************************************************************/
Font::Builder::~Builder() {}
CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder(FontFactory* factory,
InputStream* is) {
FontBuilderPtr builder = new Builder(factory);
builder->LoadFont(is);
return builder.Detach();
}
CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder(
FontFactory* factory,
WritableFontData* wfd,
int32_t offset_to_offset_table) {
FontBuilderPtr builder = new Builder(factory);
builder->LoadFont(wfd, offset_to_offset_table);
return builder.Detach();
}
CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder(
FontFactory* factory) {
FontBuilderPtr builder = new Builder(factory);
return builder.Detach();
}
bool Font::Builder::ReadyToBuild() {
// just read in data with no manipulation
if (table_builders_.empty() && !data_blocks_.empty()) {
return true;
}
// TODO(stuartg): font level checks - required tables etc?
for (TableBuilderMap::iterator table_builder = table_builders_.begin(),
table_builder_end = table_builders_.end();
table_builder != table_builder_end;
++table_builder) {
if (!table_builder->second->ReadyToBuild())
return false;
}
return true;
}
CALLER_ATTACH Font* Font::Builder::Build() {
FontPtr font = new Font(sfnt_version_, &digest_);
if (!table_builders_.empty()) {
// Note: Different from Java. Directly use font->tables_ here to avoid
// STL container copying.
BuildTablesFromBuilders(font, &table_builders_, &font->tables_);
}
table_builders_.clear();
data_blocks_.clear();
return font.Detach();
}
void Font::Builder::SetDigest(ByteVector* digest) {
digest_.clear();
digest_ = *digest;
}
void Font::Builder::ClearTableBuilders() {
table_builders_.clear();
}
bool Font::Builder::HasTableBuilder(int32_t tag) {
return (table_builders_.find(tag) != table_builders_.end());
}
Table::Builder* Font::Builder::GetTableBuilder(int32_t tag) {
if (HasTableBuilder(tag))
return table_builders_[tag];
return NULL;
}
Table::Builder* Font::Builder::NewTableBuilder(int32_t tag) {
HeaderPtr header = new Header(tag);
TableBuilderPtr builder;
builder.Attach(Table::Builder::GetBuilder(header, NULL));
table_builders_.insert(TableBuilderEntry(header->tag(), builder));
return builder;
}
Table::Builder* Font::Builder::NewTableBuilder(int32_t tag,
ReadableFontData* src_data) {
assert(src_data);
WritableFontDataPtr data;
data.Attach(WritableFontData::CreateWritableFontData(src_data->Length()));
// TODO(stuarg): take over original data instead?
src_data->CopyTo(data);
HeaderPtr header = new Header(tag, data->Length());
TableBuilderPtr builder;
builder.Attach(Table::Builder::GetBuilder(header, data));
table_builders_.insert(TableBuilderEntry(tag, builder));
return builder;
}
void Font::Builder::RemoveTableBuilder(int32_t tag) {
TableBuilderMap::iterator target = table_builders_.find(tag);
if (target != table_builders_.end()) {
table_builders_.erase(target);
}
}
Font::Builder::Builder(FontFactory* factory)
: factory_(factory),
sfnt_version_(Fixed1616::Fixed(SFNTVERSION_MAJOR, SFNTVERSION_MINOR)) {
}
void Font::Builder::LoadFont(InputStream* is) {
// Note: we do not throw exception here for is. This is more of an assertion.
assert(is);
FontInputStream font_is(is);
HeaderOffsetSortedSet records;
ReadHeader(&font_is, &records);
LoadTableData(&records, &font_is, &data_blocks_);
BuildAllTableBuilders(&data_blocks_, &table_builders_);
font_is.Close();
}
void Font::Builder::LoadFont(WritableFontData* wfd,
int32_t offset_to_offset_table) {
// Note: we do not throw exception here for is. This is more of an assertion.
assert(wfd);
HeaderOffsetSortedSet records;
ReadHeader(wfd, offset_to_offset_table, &records);
LoadTableData(&records, wfd, &data_blocks_);
BuildAllTableBuilders(&data_blocks_, &table_builders_);
}
int32_t Font::Builder::SfntWrapperSize() {
return Offset::kSfntHeaderSize +
(Offset::kTableRecordSize * table_builders_.size());
}
void Font::Builder::BuildAllTableBuilders(DataBlockMap* table_data,
TableBuilderMap* builder_map) {
for (DataBlockMap::iterator record = table_data->begin(),
record_end = table_data->end();
record != record_end; ++record) {
TableBuilderPtr builder;
builder.Attach(GetTableBuilder(record->first.p_, record->second.p_));
builder_map->insert(TableBuilderEntry(record->first->tag(), builder));
}
InterRelateBuilders(&table_builders_);
}
CALLER_ATTACH
Table::Builder* Font::Builder::GetTableBuilder(Header* header,
WritableFontData* data) {
return Table::Builder::GetBuilder(header, data);
}
void Font::Builder::BuildTablesFromBuilders(Font* font,
TableBuilderMap* builder_map,
TableMap* table_map) {
UNREFERENCED_PARAMETER(font);
InterRelateBuilders(builder_map);
// Now build all the tables.
for (TableBuilderMap::iterator builder = builder_map->begin(),
builder_end = builder_map->end();
builder != builder_end; ++builder) {
TablePtr table;
if (builder->second && builder->second->ReadyToBuild()) {
table.Attach(down_cast<Table*>(builder->second->Build()));
}
if (table == NULL) {
table_map->clear();
#if !defined (SFNTLY_NO_EXCEPTION)
std::string builder_string = "Unable to build table - ";
char* table_name = TagToString(builder->first);
builder_string += table_name;
delete[] table_name;
throw RuntimeException(builder_string.c_str());
#endif
return;
}
table_map->insert(TableMapEntry(table->header()->tag(), table));
}
}
static Table::Builder* GetBuilder(TableBuilderMap* builder_map, int32_t tag) {
if (builder_map) {
TableBuilderMap::iterator target = builder_map->find(tag);
if (target != builder_map->end()) {
return target->second.p_;
}
}
return NULL;
}
void Font::Builder::InterRelateBuilders(TableBuilderMap* builder_map) {
Table::Builder* raw_head_builder = GetBuilder(builder_map, Tag::head);
FontHeaderTableBuilderPtr header_table_builder;
if (raw_head_builder != NULL) {
header_table_builder =
down_cast<FontHeaderTable::Builder*>(raw_head_builder);
}
Table::Builder* raw_hhea_builder = GetBuilder(builder_map, Tag::hhea);
HorizontalHeaderTableBuilderPtr horizontal_header_builder;
if (raw_head_builder != NULL) {
horizontal_header_builder =
down_cast<HorizontalHeaderTable::Builder*>(raw_hhea_builder);
}
Table::Builder* raw_maxp_builder = GetBuilder(builder_map, Tag::maxp);
MaximumProfileTableBuilderPtr max_profile_builder;
if (raw_maxp_builder != NULL) {
max_profile_builder =
down_cast<MaximumProfileTable::Builder*>(raw_maxp_builder);
}
Table::Builder* raw_loca_builder = GetBuilder(builder_map, Tag::loca);
LocaTableBuilderPtr loca_table_builder;
if (raw_loca_builder != NULL) {
loca_table_builder = down_cast<LocaTable::Builder*>(raw_loca_builder);
}
Table::Builder* raw_hmtx_builder = GetBuilder(builder_map, Tag::hmtx);
HorizontalMetricsTableBuilderPtr horizontal_metrics_builder;
if (raw_hmtx_builder != NULL) {
horizontal_metrics_builder =
down_cast<HorizontalMetricsTable::Builder*>(raw_hmtx_builder);
}
#if defined (SFNTLY_EXPERIMENTAL)
Table::Builder* raw_hdmx_builder = GetBuilder(builder_map, Tag::hdmx);
HorizontalDeviceMetricsTableBuilderPtr hdmx_table_builder;
if (raw_hdmx_builder != NULL) {
hdmx_table_builder =
down_cast<HorizontalDeviceMetricsTable::Builder*>(raw_hdmx_builder);
}
#endif
// set the inter table data required to build certain tables
if (horizontal_metrics_builder != NULL) {
if (max_profile_builder != NULL) {
horizontal_metrics_builder->SetNumGlyphs(
max_profile_builder->NumGlyphs());
}
if (horizontal_header_builder != NULL) {
horizontal_metrics_builder->SetNumberOfHMetrics(
horizontal_header_builder->NumberOfHMetrics());
}
}
if (loca_table_builder != NULL) {
if (max_profile_builder != NULL) {
loca_table_builder->SetNumGlyphs(max_profile_builder->NumGlyphs());
}
if (header_table_builder != NULL) {
loca_table_builder->set_format_version(
header_table_builder->IndexToLocFormat());
}
}
#if defined (SFNTLY_EXPERIMENTAL)
// Note: In C++, hdmx_table_builder can be NULL in a subsetter.
if (max_profile_builder != NULL && hdmx_table_builder != NULL) {
hdmx_table_builder->SetNumGlyphs(max_profile_builder->NumGlyphs());
}
#endif
}
void Font::Builder::ReadHeader(FontInputStream* is,
HeaderOffsetSortedSet* records) {
assert(records);
sfnt_version_ = is->ReadFixed();
num_tables_ = is->ReadUShort();
search_range_ = is->ReadUShort();
entry_selector_ = is->ReadUShort();
range_shift_ = is->ReadUShort();
for (int32_t table_number = 0; table_number < num_tables_; ++table_number) {
// Need to use temporary vars here. C++ evaluates function parameters from
// right to left and thus breaks the order of input stream.
int32_t tag = is->ReadULongAsInt();
int64_t checksum = is->ReadULong();
int32_t offset = is->ReadULongAsInt();
int32_t length = is->ReadULongAsInt();
HeaderPtr table = new Header(tag, checksum, offset, length);
records->insert(table);
}
}
void Font::Builder::ReadHeader(ReadableFontData* fd,
int32_t offset,
HeaderOffsetSortedSet* records) {
assert(records);
sfnt_version_ = fd->ReadFixed(offset + Offset::kSfntVersion);
num_tables_ = fd->ReadUShort(offset + Offset::kNumTables);
search_range_ = fd->ReadUShort(offset + Offset::kSearchRange);
entry_selector_ = fd->ReadUShort(offset + Offset::kEntrySelector);
range_shift_ = fd->ReadUShort(offset + Offset::kRangeShift);
int32_t table_offset = offset + Offset::kTableRecordBegin;
for (int32_t table_number = 0;
table_number < num_tables_;
table_number++, table_offset += Offset::kTableRecordSize) {
int32_t tag = fd->ReadULongAsInt(table_offset + Offset::kTableTag);
int64_t checksum = fd->ReadULong(table_offset + Offset::kTableCheckSum);
int32_t offset = fd->ReadULongAsInt(table_offset + Offset::kTableOffset);
int32_t length = fd->ReadULongAsInt(table_offset + Offset::kTableLength);
HeaderPtr table = new Header(tag, checksum, offset, length);
records->insert(table);
}
}
void Font::Builder::LoadTableData(HeaderOffsetSortedSet* headers,
FontInputStream* is,
DataBlockMap* table_data) {
assert(table_data);
for (HeaderOffsetSortedSet::iterator table_header = headers->begin(),
table_end = headers->end();
table_header != table_end;
++table_header) {
is->Skip((*table_header)->offset() - is->position());
FontInputStream table_is(is, (*table_header)->length());
WritableFontDataPtr data;
data.Attach(
WritableFontData::CreateWritableFontData((*table_header)->length()));
data->CopyFrom(&table_is, (*table_header)->length());
table_data->insert(DataBlockEntry(*table_header, data));
}
}
void Font::Builder::LoadTableData(HeaderOffsetSortedSet* headers,
WritableFontData* fd,
DataBlockMap* table_data) {
for (HeaderOffsetSortedSet::iterator table_header = headers->begin(),
table_end = headers->end();
table_header != table_end;
++table_header) {
FontDataPtr sliced_data;
sliced_data.Attach(
fd->Slice((*table_header)->offset(), (*table_header)->length()));
WritableFontDataPtr data = down_cast<WritableFontData*>(sliced_data.p_);
table_data->insert(DataBlockEntry(*table_header, data));
}
}
} // namespace sfntly