font.cc revision 9e5bf57583793d6db93bb01fd982760ad110a50f
1/* 2 * Copyright 2011 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "sfntly/font.h" 18 19#include <stdio.h> 20 21#include <functional> 22#include <algorithm> 23#include <map> 24#include <string> 25#include <typeinfo> 26#include <iterator> 27 28#include "sfntly/data/font_input_stream.h" 29#include "sfntly/font_factory.h" 30#include "sfntly/math/fixed1616.h" 31#include "sfntly/math/font_math.h" 32#include "sfntly/port/exception_type.h" 33#include "sfntly/table/core/font_header_table.h" 34#include "sfntly/table/core/horizontal_device_metrics_table.h" 35#include "sfntly/table/core/horizontal_header_table.h" 36#include "sfntly/table/core/horizontal_metrics_table.h" 37#include "sfntly/table/core/maximum_profile_table.h" 38#include "sfntly/table/truetype/loca_table.h" 39#include "sfntly/tag.h" 40 41namespace sfntly { 42 43const int32_t SFNTVERSION_1 = Fixed1616::Fixed(1, 0); 44 45/****************************************************************************** 46 * Font class 47 ******************************************************************************/ 48Font::~Font() {} 49 50bool Font::HasTable(int32_t tag) { 51 TableMap::const_iterator result = tables_.find(tag); 52 TableMap::const_iterator end = tables_.end(); 53 return (result != end); 54} 55 56Table* Font::GetTable(int32_t tag) { 57 if (!HasTable(tag)) { 58 return NULL; 59 } 60 return tables_[tag]; 61} 62 63const TableMap* Font::GetTableMap() { 64 return &tables_; 65} 66 67void Font::Serialize(OutputStream* os, IntegerList* table_ordering) { 68 assert(table_ordering); 69 IntegerList final_table_ordering; 70 GenerateTableOrdering(table_ordering, &final_table_ordering); 71 TableHeaderList table_records; 72 BuildTableHeadersForSerialization(&final_table_ordering, &table_records); 73 74 FontOutputStream fos(os); 75 SerializeHeader(&fos, &table_records); 76 SerializeTables(&fos, &table_records); 77} 78 79Font::Font(int32_t sfnt_version, ByteVector* digest) 80 : sfnt_version_(sfnt_version) { 81 // non-trivial assignments that makes debugging hard if placed in 82 // initialization list 83 digest_ = *digest; 84} 85 86void Font::BuildTableHeadersForSerialization(IntegerList* table_ordering, 87 TableHeaderList* table_headers) { 88 assert(table_headers); 89 assert(table_ordering); 90 91 IntegerList final_table_ordering; 92 GenerateTableOrdering(table_ordering, &final_table_ordering); 93 int32_t table_offset = Offset::kTableRecordBegin + num_tables() * 94 Offset::kTableRecordSize; 95 for (IntegerList::iterator tag = final_table_ordering.begin(), 96 tag_end = final_table_ordering.end(); 97 tag != tag_end; ++tag) { 98 if (tables_.find(*tag) == tables_.end()) { 99 continue; 100 } 101 TablePtr table = tables_[*tag]; 102 if (table != NULL) { 103 HeaderPtr header = 104 new Header(*tag, table->CalculatedChecksum(), table_offset, 105 table->header()->length()); 106 table_headers->push_back(header); 107 table_offset += (table->DataLength() + 3) & ~3; 108 } 109 } 110} 111 112void Font::SerializeHeader(FontOutputStream* fos, 113 TableHeaderList* table_headers) { 114 fos->WriteFixed(sfnt_version_); 115 fos->WriteUShort(table_headers->size()); 116 int32_t log2_of_max_power_of_2 = FontMath::Log2(table_headers->size()); 117 int32_t search_range = 2 << (log2_of_max_power_of_2 - 1 + 4); 118 fos->WriteUShort(search_range); 119 fos->WriteUShort(log2_of_max_power_of_2); 120 fos->WriteUShort((table_headers->size() * 16) - search_range); 121 122 HeaderTagSortedSet sorted_headers; 123 std::copy(table_headers->begin(), 124 table_headers->end(), 125 std::inserter(sorted_headers, sorted_headers.end())); 126 127 for (HeaderTagSortedSet::iterator record = sorted_headers.begin(), 128 record_end = sorted_headers.end(); 129 record != record_end; ++record) { 130 fos->WriteULong((*record)->tag()); 131 fos->WriteULong((int32_t)((*record)->checksum())); 132 fos->WriteULong((*record)->offset()); 133 fos->WriteULong((*record)->length()); 134 } 135} 136 137void Font::SerializeTables(FontOutputStream* fos, 138 TableHeaderList* table_headers) { 139 assert(fos); 140 assert(table_headers); 141 for (TableHeaderList::iterator record = table_headers->begin(), 142 end_of_headers = table_headers->end(); 143 record != end_of_headers; ++record) { 144 TablePtr target_table = GetTable((*record)->tag()); 145 if (target_table == NULL) { 146#if !defined (SFNTLY_NO_EXCEPTION) 147 throw IOException("Table out of sync with font header."); 148#endif 149 return; 150 } 151 int32_t table_size = target_table->Serialize(fos); 152 if (table_size != (*record)->length()) { 153 assert(false); 154 } 155 int32_t filler_size = ((table_size + 3) & ~3) - table_size; 156 for (int32_t i = 0; i < filler_size; ++i) { 157 fos->Write(static_cast<byte_t>(0)); 158 } 159 } 160} 161 162void Font::GenerateTableOrdering(IntegerList* default_table_ordering, 163 IntegerList* table_ordering) { 164 assert(default_table_ordering); 165 assert(table_ordering); 166 table_ordering->clear(); 167 if (default_table_ordering->empty()) { 168 DefaultTableOrdering(default_table_ordering); 169 } 170 171 typedef std::map<int32_t, bool> Int2Bool; 172 typedef std::pair<int32_t, bool> Int2BoolEntry; 173 Int2Bool tables_in_font; 174 for (TableMap::iterator table = tables_.begin(), table_end = tables_.end(); 175 table != table_end; ++table) { 176 tables_in_font.insert(Int2BoolEntry(table->first, false)); 177 } 178 for (IntegerList::iterator tag = default_table_ordering->begin(), 179 tag_end = default_table_ordering->end(); 180 tag != tag_end; ++tag) { 181 if (HasTable(*tag)) { 182 table_ordering->push_back(*tag); 183 tables_in_font[*tag] = true; 184 } 185 } 186 for (Int2Bool::iterator table = tables_in_font.begin(), 187 table_end = tables_in_font.end(); 188 table != table_end; ++table) { 189 if (table->second == false) 190 table_ordering->push_back(table->first); 191 } 192} 193 194void Font::DefaultTableOrdering(IntegerList* default_table_ordering) { 195 assert(default_table_ordering); 196 default_table_ordering->clear(); 197 if (HasTable(Tag::CFF)) { 198 default_table_ordering->resize(CFF_TABLE_ORDERING_SIZE); 199 std::copy(CFF_TABLE_ORDERING, CFF_TABLE_ORDERING + CFF_TABLE_ORDERING_SIZE, 200 default_table_ordering->begin()); 201 return; 202 } 203 default_table_ordering->resize(TRUE_TYPE_TABLE_ORDERING_SIZE); 204 std::copy(TRUE_TYPE_TABLE_ORDERING, 205 TRUE_TYPE_TABLE_ORDERING + TRUE_TYPE_TABLE_ORDERING_SIZE, 206 default_table_ordering->begin()); 207} 208 209/****************************************************************************** 210 * Font::Builder class 211 ******************************************************************************/ 212Font::Builder::~Builder() {} 213 214CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder(FontFactory* factory, 215 InputStream* is) { 216 FontBuilderPtr builder = new Builder(factory); 217 builder->LoadFont(is); 218 return builder.Detach(); 219} 220 221CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder( 222 FontFactory* factory, 223 WritableFontData* wfd, 224 int32_t offset_to_offset_table) { 225 FontBuilderPtr builder = new Builder(factory); 226 builder->LoadFont(wfd, offset_to_offset_table); 227 return builder.Detach(); 228} 229 230CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder( 231 FontFactory* factory) { 232 FontBuilderPtr builder = new Builder(factory); 233 return builder.Detach(); 234} 235 236bool Font::Builder::ReadyToBuild() { 237 // just read in data with no manipulation 238 if (table_builders_.empty() && !data_blocks_.empty()) { 239 return true; 240 } 241 242 // TODO(stuartg): font level checks - required tables etc? 243 for (TableBuilderMap::iterator table_builder = table_builders_.begin(), 244 table_builder_end = table_builders_.end(); 245 table_builder != table_builder_end; 246 ++table_builder) { 247 if (!table_builder->second->ReadyToBuild()) 248 return false; 249 } 250 return true; 251} 252 253CALLER_ATTACH Font* Font::Builder::Build() { 254 FontPtr font = new Font(sfnt_version_, &digest_); 255 256 if (!table_builders_.empty()) { 257 // Note: Different from Java. Directly use font->tables_ here to avoid 258 // STL container copying. 259 BuildTablesFromBuilders(font, &table_builders_, &font->tables_); 260 } 261 262 table_builders_.clear(); 263 data_blocks_.clear(); 264 return font.Detach(); 265} 266 267void Font::Builder::SetDigest(ByteVector* digest) { 268 digest_.clear(); 269 digest_ = *digest; 270} 271 272void Font::Builder::ClearTableBuilders() { 273 table_builders_.clear(); 274} 275 276bool Font::Builder::HasTableBuilder(int32_t tag) { 277 return (table_builders_.find(tag) != table_builders_.end()); 278} 279 280Table::Builder* Font::Builder::GetTableBuilder(int32_t tag) { 281 if (HasTableBuilder(tag)) 282 return table_builders_[tag]; 283 return NULL; 284} 285 286Table::Builder* Font::Builder::NewTableBuilder(int32_t tag) { 287 HeaderPtr header = new Header(tag); 288 TableBuilderPtr builder; 289 builder.Attach(Table::Builder::GetBuilder(header, NULL)); 290 table_builders_.insert(TableBuilderEntry(header->tag(), builder)); 291 return builder; 292} 293 294Table::Builder* Font::Builder::NewTableBuilder(int32_t tag, 295 ReadableFontData* src_data) { 296 assert(src_data); 297 WritableFontDataPtr data; 298 data.Attach(WritableFontData::CreateWritableFontData(src_data->Length())); 299 // TODO(stuarg): take over original data instead? 300 src_data->CopyTo(data); 301 302 HeaderPtr header = new Header(tag, data->Length()); 303 TableBuilderPtr builder; 304 builder.Attach(Table::Builder::GetBuilder(header, data)); 305 table_builders_.insert(TableBuilderEntry(tag, builder)); 306 return builder; 307} 308 309void Font::Builder::RemoveTableBuilder(int32_t tag) { 310 TableBuilderMap::iterator target = table_builders_.find(tag); 311 if (target != table_builders_.end()) { 312 table_builders_.erase(target); 313 } 314} 315 316Font::Builder::Builder(FontFactory* factory) 317 : factory_(factory), sfnt_version_(SFNTVERSION_1) { 318} 319 320void Font::Builder::LoadFont(InputStream* is) { 321 // Note: we do not throw exception here for is. This is more of an assertion. 322 assert(is); 323 FontInputStream font_is(is); 324 HeaderOffsetSortedSet records; 325 ReadHeader(&font_is, &records); 326 LoadTableData(&records, &font_is, &data_blocks_); 327 BuildAllTableBuilders(&data_blocks_, &table_builders_); 328 font_is.Close(); 329} 330 331void Font::Builder::LoadFont(WritableFontData* wfd, 332 int32_t offset_to_offset_table) { 333 // Note: we do not throw exception here for is. This is more of an assertion. 334 assert(wfd); 335 HeaderOffsetSortedSet records; 336 ReadHeader(wfd, offset_to_offset_table, &records); 337 LoadTableData(&records, wfd, &data_blocks_); 338 BuildAllTableBuilders(&data_blocks_, &table_builders_); 339} 340 341int32_t Font::Builder::SfntWrapperSize() { 342 return Offset::kSfntHeaderSize + 343 (Offset::kTableRecordSize * table_builders_.size()); 344} 345 346void Font::Builder::BuildAllTableBuilders(DataBlockMap* table_data, 347 TableBuilderMap* builder_map) { 348 for (DataBlockMap::iterator record = table_data->begin(), 349 record_end = table_data->end(); 350 record != record_end; ++record) { 351 TableBuilderPtr builder; 352 builder.Attach(GetTableBuilder(record->first.p_, record->second.p_)); 353 builder_map->insert(TableBuilderEntry(record->first->tag(), builder)); 354 } 355 InterRelateBuilders(&table_builders_); 356} 357 358CALLER_ATTACH 359Table::Builder* Font::Builder::GetTableBuilder(Header* header, 360 WritableFontData* data) { 361 return Table::Builder::GetBuilder(header, data); 362} 363 364void Font::Builder::BuildTablesFromBuilders(Font* font, 365 TableBuilderMap* builder_map, 366 TableMap* table_map) { 367 UNREFERENCED_PARAMETER(font); 368 InterRelateBuilders(builder_map); 369 370 // Now build all the tables. 371 for (TableBuilderMap::iterator builder = builder_map->begin(), 372 builder_end = builder_map->end(); 373 builder != builder_end; ++builder) { 374 TablePtr table; 375 if (builder->second && builder->second->ReadyToBuild()) { 376 table.Attach(down_cast<Table*>(builder->second->Build())); 377 } 378 if (table == NULL) { 379 table_map->clear(); 380#if !defined (SFNTLY_NO_EXCEPTION) 381 std::string builder_string = "Unable to build table - "; 382 char* table_name = TagToString(builder->first); 383 builder_string += table_name; 384 delete[] table_name; 385 throw RuntimeException(builder_string.c_str()); 386#endif 387 return; 388 } 389 table_map->insert(TableMapEntry(table->header()->tag(), table)); 390 } 391} 392 393static Table::Builder* GetBuilder(TableBuilderMap* builder_map, int32_t tag) { 394 if (builder_map) { 395 TableBuilderMap::iterator target = builder_map->find(tag); 396 if (target != builder_map->end()) { 397 return target->second.p_; 398 } 399 } 400 401 return NULL; 402} 403 404void Font::Builder::InterRelateBuilders(TableBuilderMap* builder_map) { 405 Table::Builder* raw_head_builder = GetBuilder(builder_map, Tag::head); 406 FontHeaderTableBuilderPtr header_table_builder; 407 if (raw_head_builder != NULL) { 408 header_table_builder = 409 down_cast<FontHeaderTable::Builder*>(raw_head_builder); 410 } 411 412 Table::Builder* raw_hhea_builder = GetBuilder(builder_map, Tag::hhea); 413 HorizontalHeaderTableBuilderPtr horizontal_header_builder; 414 if (raw_head_builder != NULL) { 415 horizontal_header_builder = 416 down_cast<HorizontalHeaderTable::Builder*>(raw_hhea_builder); 417 } 418 419 Table::Builder* raw_maxp_builder = GetBuilder(builder_map, Tag::maxp); 420 MaximumProfileTableBuilderPtr max_profile_builder; 421 if (raw_maxp_builder != NULL) { 422 max_profile_builder = 423 down_cast<MaximumProfileTable::Builder*>(raw_maxp_builder); 424 } 425 426 Table::Builder* raw_loca_builder = GetBuilder(builder_map, Tag::loca); 427 LocaTableBuilderPtr loca_table_builder; 428 if (raw_loca_builder != NULL) { 429 loca_table_builder = down_cast<LocaTable::Builder*>(raw_loca_builder); 430 } 431 432 Table::Builder* raw_hmtx_builder = GetBuilder(builder_map, Tag::hmtx); 433 HorizontalMetricsTableBuilderPtr horizontal_metrics_builder; 434 if (raw_hmtx_builder != NULL) { 435 horizontal_metrics_builder = 436 down_cast<HorizontalMetricsTable::Builder*>(raw_hmtx_builder); 437 } 438 439#if defined (SFNTLY_EXPERIMENTAL) 440 Table::Builder* raw_hdmx_builder = GetBuilder(builder_map, Tag::hdmx); 441 HorizontalDeviceMetricsTableBuilderPtr hdmx_table_builder; 442 if (raw_hdmx_builder != NULL) { 443 hdmx_table_builder = 444 down_cast<HorizontalDeviceMetricsTable::Builder*>(raw_hdmx_builder); 445 } 446#endif 447 448 // set the inter table data required to build certain tables 449 if (horizontal_metrics_builder != NULL) { 450 if (max_profile_builder != NULL) { 451 horizontal_metrics_builder->SetNumGlyphs( 452 max_profile_builder->NumGlyphs()); 453 } 454 if (horizontal_header_builder != NULL) { 455 horizontal_metrics_builder->SetNumberOfHMetrics( 456 horizontal_header_builder->NumberOfHMetrics()); 457 } 458 } 459 460 if (loca_table_builder != NULL) { 461 if (max_profile_builder != NULL) { 462 loca_table_builder->SetNumGlyphs(max_profile_builder->NumGlyphs()); 463 } 464 if (header_table_builder != NULL) { 465 loca_table_builder->set_format_version( 466 header_table_builder->IndexToLocFormat()); 467 } 468 } 469 470#if defined (SFNTLY_EXPERIMENTAL) 471 // Note: In C++, hdmx_table_builder can be NULL in a subsetter. 472 if (max_profile_builder != NULL && hdmx_table_builder != NULL) { 473 hdmx_table_builder->SetNumGlyphs(max_profile_builder->NumGlyphs()); 474 } 475#endif 476} 477 478void Font::Builder::ReadHeader(FontInputStream* is, 479 HeaderOffsetSortedSet* records) { 480 assert(records); 481 sfnt_version_ = is->ReadFixed(); 482 num_tables_ = is->ReadUShort(); 483 search_range_ = is->ReadUShort(); 484 entry_selector_ = is->ReadUShort(); 485 range_shift_ = is->ReadUShort(); 486 487 for (int32_t table_number = 0; table_number < num_tables_; ++table_number) { 488 // Need to use temporary vars here. C++ evaluates function parameters from 489 // right to left and thus breaks the order of input stream. 490 int32_t tag = is->ReadULongAsInt(); 491 int64_t checksum = is->ReadULong(); 492 int32_t offset = is->ReadULongAsInt(); 493 int32_t length = is->ReadULongAsInt(); 494 HeaderPtr table = new Header(tag, checksum, offset, length); 495 records->insert(table); 496 } 497} 498 499void Font::Builder::ReadHeader(ReadableFontData* fd, 500 int32_t offset, 501 HeaderOffsetSortedSet* records) { 502 assert(records); 503 sfnt_version_ = fd->ReadFixed(offset + Offset::kSfntVersion); 504 num_tables_ = fd->ReadUShort(offset + Offset::kNumTables); 505 search_range_ = fd->ReadUShort(offset + Offset::kSearchRange); 506 entry_selector_ = fd->ReadUShort(offset + Offset::kEntrySelector); 507 range_shift_ = fd->ReadUShort(offset + Offset::kRangeShift); 508 509 int32_t table_offset = offset + Offset::kTableRecordBegin; 510 for (int32_t table_number = 0; 511 table_number < num_tables_; 512 table_number++, table_offset += Offset::kTableRecordSize) { 513 int32_t tag = fd->ReadULongAsInt(table_offset + Offset::kTableTag); 514 int64_t checksum = fd->ReadULong(table_offset + Offset::kTableCheckSum); 515 int32_t offset = fd->ReadULongAsInt(table_offset + Offset::kTableOffset); 516 int32_t length = fd->ReadULongAsInt(table_offset + Offset::kTableLength); 517 HeaderPtr table = new Header(tag, checksum, offset, length); 518 records->insert(table); 519 } 520} 521 522void Font::Builder::LoadTableData(HeaderOffsetSortedSet* headers, 523 FontInputStream* is, 524 DataBlockMap* table_data) { 525 assert(table_data); 526 for (HeaderOffsetSortedSet::iterator table_header = headers->begin(), 527 table_end = headers->end(); 528 table_header != table_end; 529 ++table_header) { 530 is->Skip((*table_header)->offset() - is->position()); 531 FontInputStream table_is(is, (*table_header)->length()); 532 WritableFontDataPtr data; 533 data.Attach( 534 WritableFontData::CreateWritableFontData((*table_header)->length())); 535 data->CopyFrom(&table_is, (*table_header)->length()); 536 table_data->insert(DataBlockEntry(*table_header, data)); 537 } 538} 539 540void Font::Builder::LoadTableData(HeaderOffsetSortedSet* headers, 541 WritableFontData* fd, 542 DataBlockMap* table_data) { 543 for (HeaderOffsetSortedSet::iterator table_header = headers->begin(), 544 table_end = headers->end(); 545 table_header != table_end; 546 ++table_header) { 547 FontDataPtr sliced_data; 548 sliced_data.Attach( 549 fd->Slice((*table_header)->offset(), (*table_header)->length())); 550 WritableFontDataPtr data = down_cast<WritableFontData*>(sliced_data.p_); 551 table_data->insert(DataBlockEntry(*table_header, data)); 552 } 553} 554 555} // namespace sfntly 556