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