font.cc revision 2ac95dcf52d76634fba3f8fa81c575424f01d992
1244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani/* 2244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani * Copyright 2011 Google Inc. All Rights Reserved. 3244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani * 4244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani * Licensed under the Apache License, Version 2.0 (the "License"); 5244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani * you may not use this file except in compliance with the License. 6244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani * You may obtain a copy of the License at 7244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani * 8244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani * http://www.apache.org/licenses/LICENSE-2.0 9244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani * 10244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani * Unless required by applicable law or agreed to in writing, software 11244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani * distributed under the License is distributed on an "AS IS" BASIS, 12244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani * See the License for the specific language governing permissions and 14244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani * limitations under the License. 15244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani */ 16244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani 17244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani#include "sfntly/font.h" 18244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani 19244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani#include <functional> 20244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani#include <algorithm> 21244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani#include <map> 223718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani#include <typeinfo> 233718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani 243718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani#include "sfntly/data/font_input_stream.h" 253718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani#include "sfntly/font_factory.h" 263718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani#include "sfntly/math/fixed1616.h" 273718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani#include "sfntly/math/font_math.h" 283718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani#include "sfntly/font_header_table.h" 293718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani#include "sfntly/horizontal_header_table.h" 303718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani#include "sfntly/horizontal_metrics_table.h" 313718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani#include "sfntly/loca_table.h" 323718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani#include "sfntly/maximum_profile_table.h" 333718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani#include "sfntly/port/exception_type.h" 343718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani#include "sfntly/tag.h" 353718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani 363718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasaninamespace sfntly { 373718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani 383718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasaniconst int32_t SFNTVERSION_1 = Fixed1616::Fixed(1, 0); 393718aaabe6259dcf86a3666ff92d16e4be5da555Amith Yamasani 40244fa5c05b2cc8c4c0754aeed4ee42c588ea89d1Amith Yamasani/****************************************************************************** 41 * Font class 42 ******************************************************************************/ 43Font::~Font() {} 44 45bool Font::HasTable(int32_t tag) { 46 TableMap::const_iterator result = tables_.find(tag); 47 TableMap::const_iterator end = tables_.end(); 48 return (result != end); 49} 50 51Table* Font::GetTable(int32_t tag) { 52 if (!HasTable(tag)) { 53 return NULL; 54 } 55 return tables_[tag]; 56} 57 58TableMap* Font::Tables() { 59 return &tables_; 60} 61 62void Font::Serialize(OutputStream* os, IntegerList* table_ordering) { 63 assert(table_ordering); 64 IntegerList final_table_ordering; 65 TableOrdering(table_ordering, &final_table_ordering); 66 TableHeaderList table_records; 67 BuildTableHeadersForSerialization(&final_table_ordering, &table_records); 68 69 FontOutputStream fos(os); 70 SerializeHeader(&fos, &table_records); 71 SerializeTables(&fos, &table_records); 72} 73 74CALLER_ATTACH WritableFontData* Font::GetNewData(int32_t size) { 75 return factory_->GetNewData(size); 76} 77 78Font::Font(FontFactory* factory, int32_t sfnt_version, ByteVector* digest, 79 TableMap* tables) 80 : factory_(factory), 81 sfnt_version_(sfnt_version) { 82 // non-trivial assignments that makes debugging hard if placed in 83 // initialization list 84 digest_ = *digest; 85 tables_ = *tables; 86} 87 88void Font::BuildTableHeadersForSerialization(IntegerList* table_ordering, 89 TableHeaderList* table_headers) { 90 assert(table_headers); 91 assert(table_ordering); 92 93 IntegerList final_table_ordering; 94 TableOrdering(table_ordering, &final_table_ordering); 95 int32_t table_offset = Offset::kTableRecordBegin + num_tables() * 96 Offset::kTableRecordSize; 97 for (IntegerList::iterator tag = final_table_ordering.begin(), 98 tag_end = final_table_ordering.end(); 99 tag != tag_end; ++tag) { 100 TablePtr table = tables_[*tag]; 101 if (table != NULL) { 102 TableHeaderPtr header = 103 new Table::Header(*tag, table->CalculatedChecksum(), table_offset, 104 table->Length()); 105 table_headers->push_back(header); 106 table_offset += (table->Length() + 3) & ~3; 107 } 108 } 109} 110 111void Font::SerializeHeader(FontOutputStream* fos, 112 TableHeaderList* table_headers) { 113 fos->WriteFixed(sfnt_version_); 114 fos->WriteUShort(table_headers->size()); 115 int32_t log2_of_max_power_of_2 = FontMath::Log2(table_headers->size()); 116 int32_t search_range = 2 << (log2_of_max_power_of_2 - 1 + 4); 117 fos->WriteUShort(search_range); 118 fos->WriteUShort(log2_of_max_power_of_2); 119 fos->WriteUShort((table_headers->size() * 16) - search_range); 120 121 for (TableHeaderList::iterator record = table_headers->begin(), 122 record_end = table_headers->end(); 123 record != record_end; ++record) { 124 fos->WriteULong((*record)->tag()); 125 fos->WriteULong((int32_t)((*record)->checksum())); 126 fos->WriteULong((*record)->offset()); 127 fos->WriteULong((*record)->length()); 128 } 129} 130 131void Font::SerializeTables(FontOutputStream* fos, 132 TableHeaderList* table_headers) { 133 ByteVector SERIALIZATION_FILLER(3); 134 std::fill(SERIALIZATION_FILLER.begin(), SERIALIZATION_FILLER.end(), 0); 135 for (TableHeaderList::iterator record = table_headers->begin(), 136 end_of_headers = table_headers->end(); 137 record != end_of_headers; ++record) { 138 TablePtr target_table = GetTable((*record)->tag()); 139 if (target_table == NULL) { 140#if defined (SFNTLY_NO_EXCEPTION) 141 return; 142#else 143 throw IOException("Table out of sync with font header."); 144#endif 145 } 146 int32_t table_size = target_table->Serialize(fos); 147 if (table_size != (*record)->length()) { 148 assert(false); 149 } 150 int32_t filler_size = ((table_size + 3) & ~3) - table_size; 151 fos->Write(&SERIALIZATION_FILLER, 0, filler_size); 152 } 153} 154 155void Font::TableOrdering(IntegerList* default_table_ordering, 156 IntegerList* table_ordering) { 157 assert(default_table_ordering); 158 assert(table_ordering); 159 table_ordering->clear(); 160 if (default_table_ordering->empty()) { 161 DefaultTableOrdering(default_table_ordering); 162 } 163 164 typedef std::map<int32_t, bool> Int2Bool; 165 typedef std::pair<int32_t, bool> Int2BoolEntry; 166 Int2Bool tables_in_font; 167 for (TableMap::iterator table = tables_.begin(), table_end = tables_.end(); 168 table != table_end; ++table) { 169 tables_in_font.insert(Int2BoolEntry(table->first, false)); 170 } 171 for (IntegerList::iterator tag = default_table_ordering->begin(), 172 tag_end = default_table_ordering->end(); 173 tag != tag_end; ++tag) { 174 if (HasTable(*tag)) { 175 table_ordering->push_back(*tag); 176 tables_in_font[*tag] = true; 177 } 178 } 179 for (Int2Bool::iterator table = tables_in_font.begin(), 180 table_end = tables_in_font.end(); 181 table != table_end; ++table) { 182 if (table->second == false) 183 table_ordering->push_back(table->first); 184 } 185} 186 187void Font::DefaultTableOrdering(IntegerList* default_table_ordering) { 188 assert(default_table_ordering); 189 default_table_ordering->clear(); 190 if (HasTable(Tag::CFF)) { 191 default_table_ordering->resize(CFF_TABLE_ORDERING_SIZE); 192 std::copy(CFF_TABLE_ORDERING, CFF_TABLE_ORDERING + CFF_TABLE_ORDERING_SIZE, 193 default_table_ordering->begin()); 194 return; 195 } 196 default_table_ordering->resize(TRUE_TYPE_TABLE_ORDERING_SIZE); 197 std::copy(TRUE_TYPE_TABLE_ORDERING, 198 TRUE_TYPE_TABLE_ORDERING + TRUE_TYPE_TABLE_ORDERING_SIZE, 199 default_table_ordering->begin()); 200} 201 202/****************************************************************************** 203 * Font::Builder class 204 ******************************************************************************/ 205Font::Builder::~Builder() {} 206 207CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder( 208 FontFactory* factory, InputStream* is) { 209 FontBuilderPtr builder = new Builder(factory); 210 builder->LoadFont(is); 211 return builder.Detach(); 212} 213 214CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder( 215 FontFactory* factory, ByteArray* ba, int32_t offset_to_offset_table) { 216 FontBuilderPtr builder = new Builder(factory); 217 builder->LoadFont(ba, offset_to_offset_table); 218 return builder.Detach(); 219} 220 221CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder( 222 FontFactory* factory) { 223 FontBuilderPtr builder = new Builder(factory); 224 return builder.Detach(); 225} 226 227bool Font::Builder::ReadyToBuild() { 228 // just read in data with no manipulation 229 if (table_builders_.empty() && !data_blocks_.empty()) { 230 return true; 231 } 232 233 // TODO(stuartg): font level checks - required tables etc. 234 for (TableBuilderMap::iterator table_builder = table_builders_.begin(), 235 table_builder_end = table_builders_.end(); 236 table_builder != table_builder_end; 237 ++table_builder) { 238 if (!table_builder->second->ReadyToBuild()) 239 return false; 240 } 241 return true; 242} 243 244CALLER_ATTACH Font* Font::Builder::Build() { 245 TableMap tables; 246 if (!table_builders_.empty()) { 247 BuildTablesFromBuilders(&table_builders_, &tables); 248 } 249 FontPtr font = new Font(factory_, sfnt_version_, &digest_, &tables); 250 table_builders_.clear(); 251 data_blocks_.clear(); 252 return font.Detach(); 253} 254 255CALLER_ATTACH WritableFontData* Font::Builder::GetNewData(int32_t capacity) { 256 return factory_->GetNewData(capacity); 257} 258 259CALLER_ATTACH WritableFontData* Font::Builder::GetNewGrowableData( 260 ReadableFontData* src_data) { 261 return factory_->GetNewGrowableData(src_data); 262} 263 264void Font::Builder::SetDigest(ByteVector* digest) { 265 digest_.clear(); 266 digest_ = *digest; 267} 268 269void Font::Builder::CleanTableBuilders() { 270 table_builders_.clear(); 271} 272 273bool Font::Builder::HasTableBuilder(int32_t tag) { 274 return (table_builders_.find(tag) != table_builders_.end()); 275} 276 277Table::Builder* Font::Builder::GetTableBuilder(int32_t tag) { 278 if (HasTableBuilder(tag)) 279 return table_builders_[tag]; 280 return NULL; 281} 282 283CALLER_ATTACH Table::Builder* Font::Builder::NewTableBuilder(int32_t tag) { 284 TableHeaderPtr header = new Table::Header(tag); 285 TableBuilderPtr builder = Table::Builder::GetBuilder(this, header, NULL); 286 table_builders_.insert(TableBuilderEntry(header->tag(), builder)); 287 return builder; 288} 289 290CALLER_ATTACH Table::Builder* 291 Font::Builder::NewTableBuilder(int32_t tag, ReadableFontData* src_data) { 292 WritableFontDataPtr data; 293 data.Attach(GetNewGrowableData(src_data)); 294 TableHeaderPtr header = new Table::Header(tag); 295 TableBuilderPtr builder = Table::Builder::GetBuilder(this, header, data); 296 table_builders_.insert(TableBuilderEntry(tag, builder)); 297 return builder; 298} 299 300void Font::Builder::TableBuilderTags(IntegerSet* key_set) { 301 assert(key_set); 302 key_set->clear(); 303 for (TableBuilderMap::iterator i = table_builders_.begin(), 304 e = table_builders_.end(); i != e; ++i) { 305 key_set->insert(i->first); 306 } 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 TableHeaderSortedSet 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(ByteArray* ba, 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(ba); 335 WritableFontDataPtr fd = new WritableFontData(ba); 336 TableHeaderSortedSet records; 337 ReadHeader(fd, offset_to_offset_table, &records); 338 LoadTableData(&records, fd, &data_blocks_); 339 BuildAllTableBuilders(&data_blocks_, &table_builders_); 340} 341 342int32_t Font::Builder::SfntWrapperSize() { 343 return Offset::kSfntHeaderSize + 344 (Offset::kTableRecordSize * table_builders_.size()); 345} 346 347void Font::Builder::BuildAllTableBuilders(DataBlockMap* table_data, 348 TableBuilderMap* builder_map) { 349 for (DataBlockMap::iterator record = table_data->begin(), 350 record_end = table_data->end(); 351 record != record_end; ++record) { 352 TableBuilderPtr builder; 353 builder.Attach(GetTableBuilder(record->first.p_, record->second.p_)); 354 builder_map->insert(TableBuilderEntry(record->first->tag(), builder)); 355 } 356 InterRelateBuilders(&table_builders_); 357} 358 359CALLER_ATTACH Table::Builder* 360 Font::Builder::GetTableBuilder(Table::Header* header, 361 WritableFontData* data) { 362 return Table::Builder::GetBuilder(this, header, data); 363} 364 365void Font::Builder::BuildTablesFromBuilders(TableBuilderMap* builder_map, 366 TableMap* table_map) { 367 InterRelateBuilders(builder_map); 368 369 // Now build all the tables. 370 for (TableBuilderMap::iterator builder = builder_map->begin(), 371 builder_end = builder_map->end(); 372 builder != builder_end; ++builder) { 373 TablePtr table; 374 if (builder->second && builder->second->ReadyToBuild()) { 375#if !defined (SFNTLY_NO_EXCEPTION) 376 try { 377#endif 378 table.Attach(down_cast<Table*>(builder->second->Build())); 379#if !defined (SFNTLY_NO_EXCEPTION) 380 } catch(IOException& e) { 381 std::string builder_string = "Unable to build table - "; 382 builder_string += typeid(builder->second).name(); 383 builder_string += e.what(); 384 throw RuntimeException(builder_string.c_str()); 385 } 386#endif 387 } 388 if (table == NULL) { 389#if defined (SFNTLY_NO_EXCEPTION) 390 table_map->clear(); 391 return; 392#else 393 std::string builder_string = "Unable to build table - "; 394 builder_string += typeid(builder->second).name(); 395 throw RuntimeException(builder_string.c_str()); 396#endif 397 } 398 table_map->insert(TableMapEntry(table->header()->tag(), table)); 399 } 400} 401 402static Table::Builder* GetBuilder(TableBuilderMap* builder_map, int32_t tag) { 403 if (builder_map) { 404 TableBuilderMap::iterator target = builder_map->find(tag); 405 if (target != builder_map->end()) { 406 return target->second.p_; 407 } 408 } 409 410 return NULL; 411} 412 413void Font::Builder::InterRelateBuilders(TableBuilderMap* builder_map) { 414 Table::Builder* raw_head_builder = GetBuilder(builder_map, Tag::head); 415 FontHeaderTableBuilderPtr header_table_builder; 416 if (raw_head_builder != NULL) { 417 header_table_builder = 418 down_cast<FontHeaderTable::Builder*>(raw_head_builder); 419 } 420 421 Table::Builder* raw_hhea_builder = GetBuilder(builder_map, Tag::hhea); 422 HorizontalHeaderTableBuilderPtr horizontal_header_builder; 423 if (raw_head_builder != NULL) { 424 horizontal_header_builder = 425 down_cast<HorizontalHeaderTable::Builder*>(raw_hhea_builder); 426 } 427 428 Table::Builder* raw_maxp_builder = GetBuilder(builder_map, Tag::maxp); 429 MaximumProfileTableBuilderPtr max_profile_builder; 430 if (raw_maxp_builder != NULL) { 431 max_profile_builder = 432 down_cast<MaximumProfileTable::Builder*>(raw_maxp_builder); 433 } 434 435 Table::Builder* raw_loca_builder = GetBuilder(builder_map, Tag::loca); 436 LocaTableBuilderPtr loca_table_builder; 437 if (raw_loca_builder != NULL) { 438 loca_table_builder = down_cast<LocaTable::Builder*>(raw_loca_builder); 439 } 440 441 Table::Builder* raw_hmtx_builder = GetBuilder(builder_map, Tag::hmtx); 442 HorizontalMetricsTableBuilderPtr horizontal_metrics_builder; 443 if (raw_hmtx_builder != NULL) { 444 horizontal_metrics_builder = 445 down_cast<HorizontalMetricsTable::Builder*>(raw_hmtx_builder); 446 } 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->SetFormatVersion( 466 header_table_builder->IndexToLocFormat()); 467 } 468 } 469} 470 471void Font::Builder::ReadHeader(FontInputStream* is, 472 TableHeaderSortedSet* records) { 473 assert(records); 474 sfnt_version_ = is->ReadFixed(); 475 num_tables_ = is->ReadUShort(); 476 search_range_ = is->ReadUShort(); 477 entry_selector_ = is->ReadUShort(); 478 range_shift_ = is->ReadUShort(); 479 480 for (int32_t table_number = 0; table_number < num_tables_; ++table_number) { 481 // Need to use temporary vars here. C++ evaluates function parameters from 482 // right to left and thus breaks the order of input stream. 483 int32_t tag = is->ReadULongAsInt(); 484 int64_t checksum = is->ReadULong(); 485 int32_t offset = is->ReadULongAsInt(); 486 int32_t length = is->ReadULongAsInt(); 487 TableHeaderPtr table = new Table::Header(tag, checksum, offset, length); 488 records->insert(table); 489 } 490} 491 492void Font::Builder::ReadHeader(ReadableFontData* fd, 493 int32_t offset, 494 TableHeaderSortedSet* records) { 495 assert(records); 496 sfnt_version_ = fd->ReadFixed(offset + Offset::kSfntVersion); 497 num_tables_ = fd->ReadUShort(offset + Offset::kNumTables); 498 search_range_ = fd->ReadUShort(offset + Offset::kSearchRange); 499 entry_selector_ = fd->ReadUShort(offset + Offset::kEntrySelector); 500 range_shift_ = fd->ReadUShort(offset + Offset::kRangeShift); 501 502 int32_t table_offset = offset + Offset::kTableRecordBegin; 503 for (int32_t table_number = 0; 504 table_number < num_tables_; 505 table_number++, table_offset += Offset::kTableRecordSize) { 506 int32_t tag = fd->ReadULongAsInt(table_offset + Offset::kTableTag); 507 int64_t checksum = fd->ReadULong(table_offset + Offset::kTableCheckSum); 508 int32_t offset = fd->ReadULongAsInt(table_offset + Offset::kTableOffset); 509 int32_t length = fd->ReadULongAsInt(table_offset + Offset::kTableLength); 510 TableHeaderPtr table = new Table::Header(tag, checksum, offset, length); 511 records->insert(table); 512 } 513} 514 515void Font::Builder::LoadTableData(TableHeaderSortedSet* headers, 516 FontInputStream* is, 517 DataBlockMap* table_data) { 518 assert(table_data); 519 for (TableHeaderSortedSet::iterator 520 table_header = headers->begin(), table_end = headers->end(); 521 table_header != table_end; ++table_header) { 522 is->Skip((*table_header)->offset() - is->position()); 523 FontInputStream table_is(is, (*table_header)->length()); 524 int32_t roundup_length = ((*table_header)->length() + 3) & ~3; 525 ByteArrayPtr array; 526 array.Attach(factory_->GetNewArray(roundup_length)); 527 array->CopyFrom(&table_is, (*table_header)->length()); 528 WritableFontDataPtr data = new WritableFontData(array); 529 table_data->insert(DataBlockEntry(*table_header, data)); 530 } 531} 532 533void Font::Builder::LoadTableData(TableHeaderSortedSet* headers, 534 WritableFontData* fd, 535 DataBlockMap* table_data) { 536 for (TableHeaderSortedSet::iterator 537 table_header = headers->begin(), table_end = headers->end(); 538 table_header != table_end; ++table_header) { 539 int32_t roundup_length = ((*table_header)->length() + 3) & ~3; 540 FontDataPtr sliced_data; 541 sliced_data.Attach(fd->Slice((*table_header)->offset(), roundup_length)); 542 WritableFontDataPtr data = down_cast<WritableFontData*>(sliced_data.p_); 543 table_data->insert(DataBlockEntry(*table_header, data)); 544 } 545} 546 547} // namespace sfntly 548