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