blob: ca35048ef52779ab502e1b31c3b9dfe2dd119d17 [file] [log] [blame]
Selim Gurun30d4e1f2013-08-15 12:46:15 -07001/*
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
Colin Cross13a82432017-04-07 10:50:33 -070043namespace {
44
45const int32_t kSFNTVersionMajor = 1;
46const int32_t kSFNTVersionMinor = 0;
47
48const int32_t kMaxTableSize = 200 * 1024 * 1024;
49
50} // namespace
Selim Gurun30d4e1f2013-08-15 12:46:15 -070051
52/******************************************************************************
53 * Font class
54 ******************************************************************************/
55Font::~Font() {}
56
Colin Cross13a82432017-04-07 10:50:33 -070057bool Font::HasTable(int32_t tag) const {
58 return tables_.find(tag) != tables_.end();
Selim Gurun30d4e1f2013-08-15 12:46:15 -070059}
60
61Table* Font::GetTable(int32_t tag) {
Colin Cross13a82432017-04-07 10:50:33 -070062 if (!HasTable(tag))
Selim Gurun30d4e1f2013-08-15 12:46:15 -070063 return NULL;
Selim Gurun30d4e1f2013-08-15 12:46:15 -070064 return tables_[tag];
65}
66
67const TableMap* Font::GetTableMap() {
68 return &tables_;
69}
70
71void 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
83Font::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
90void 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
116void 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
141void 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
166void 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
198void 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 ******************************************************************************/
216Font::Builder::~Builder() {}
217
218CALLER_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
225CALLER_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
234CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder(
235 FontFactory* factory) {
236 FontBuilderPtr builder = new Builder(factory);
237 return builder.Detach();
238}
239
240bool 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
257CALLER_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
271void Font::Builder::SetDigest(ByteVector* digest) {
272 digest_.clear();
273 digest_ = *digest;
274}
275
276void Font::Builder::ClearTableBuilders() {
277 table_builders_.clear();
278}
279
280bool Font::Builder::HasTableBuilder(int32_t tag) {
281 return (table_builders_.find(tag) != table_builders_.end());
282}
283
284Table::Builder* Font::Builder::GetTableBuilder(int32_t tag) {
285 if (HasTableBuilder(tag))
286 return table_builders_[tag];
287 return NULL;
288}
289
290Table::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
298Table::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
313void Font::Builder::RemoveTableBuilder(int32_t tag) {
Colin Cross13a82432017-04-07 10:50:33 -0700314 table_builders_.erase(tag);
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700315}
316
317Font::Builder::Builder(FontFactory* factory)
318 : factory_(factory),
Colin Cross13a82432017-04-07 10:50:33 -0700319 sfnt_version_(Fixed1616::Fixed(kSFNTVersionMajor, kSFNTVersionMinor)) {
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700320}
321
322void 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
333void 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
343int32_t Font::Builder::SfntWrapperSize() {
344 return Offset::kSfntHeaderSize +
345 (Offset::kTableRecordSize * table_builders_.size());
346}
347
348void 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
360CALLER_ATTACH
361Table::Builder* Font::Builder::GetTableBuilder(Header* header,
362 WritableFontData* data) {
363 return Table::Builder::GetBuilder(header, data);
364}
365
366void 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
395static Table::Builder* GetBuilder(TableBuilderMap* builder_map, int32_t tag) {
Colin Cross13a82432017-04-07 10:50:33 -0700396 if (!builder_map)
397 return NULL;
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700398
Colin Cross13a82432017-04-07 10:50:33 -0700399 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.
407static 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;
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700413}
414
415void Font::Builder::InterRelateBuilders(TableBuilderMap* builder_map) {
Colin Cross13a82432017-04-07 10:50:33 -0700416 Table::Builder* raw_head_builder = GetReadBuilder(builder_map, Tag::head);
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700417 FontHeaderTableBuilderPtr header_table_builder;
418 if (raw_head_builder != NULL) {
Colin Cross13a82432017-04-07 10:50:33 -0700419 header_table_builder =
420 down_cast<FontHeaderTable::Builder*>(raw_head_builder);
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700421 }
422
Colin Cross13a82432017-04-07 10:50:33 -0700423 Table::Builder* raw_hhea_builder = GetReadBuilder(builder_map, Tag::hhea);
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700424 HorizontalHeaderTableBuilderPtr horizontal_header_builder;
425 if (raw_head_builder != NULL) {
Colin Cross13a82432017-04-07 10:50:33 -0700426 horizontal_header_builder =
427 down_cast<HorizontalHeaderTable::Builder*>(raw_hhea_builder);
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700428 }
429
Colin Cross13a82432017-04-07 10:50:33 -0700430 Table::Builder* raw_maxp_builder = GetReadBuilder(builder_map, Tag::maxp);
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700431 MaximumProfileTableBuilderPtr max_profile_builder;
432 if (raw_maxp_builder != NULL) {
Colin Cross13a82432017-04-07 10:50:33 -0700433 max_profile_builder =
434 down_cast<MaximumProfileTable::Builder*>(raw_maxp_builder);
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700435 }
436
437 Table::Builder* raw_loca_builder = GetBuilder(builder_map, Tag::loca);
438 LocaTableBuilderPtr loca_table_builder;
439 if (raw_loca_builder != NULL) {
Colin Cross13a82432017-04-07 10:50:33 -0700440 loca_table_builder = down_cast<LocaTable::Builder*>(raw_loca_builder);
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700441 }
442
443 Table::Builder* raw_hmtx_builder = GetBuilder(builder_map, Tag::hmtx);
444 HorizontalMetricsTableBuilderPtr horizontal_metrics_builder;
445 if (raw_hmtx_builder != NULL) {
Colin Cross13a82432017-04-07 10:50:33 -0700446 horizontal_metrics_builder =
447 down_cast<HorizontalMetricsTable::Builder*>(raw_hmtx_builder);
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700448 }
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) {
Colin Cross13a82432017-04-07 10:50:33 -0700454 hdmx_table_builder =
455 down_cast<HorizontalDeviceMetricsTable::Builder*>(raw_hdmx_builder);
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700456 }
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
489void 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
510void 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
533void Font::Builder::LoadTableData(HeaderOffsetSortedSet* headers,
534 FontInputStream* is,
535 DataBlockMap* table_data) {
536 assert(table_data);
Colin Cross13a82432017-04-07 10:50:33 -0700537 for (HeaderOffsetSortedSet::iterator it = headers->begin(),
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700538 table_end = headers->end();
Colin Cross13a82432017-04-07 10:50:33 -0700539 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());
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700547 WritableFontDataPtr data;
Colin Cross13a82432017-04-07 10:50:33 -0700548 data.Attach(WritableFontData::CreateWritableFontData(header->length()));
549 data->CopyFrom(&table_is, header->length());
550 table_data->insert(DataBlockEntry(header, data));
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700551 }
552}
553
554void Font::Builder::LoadTableData(HeaderOffsetSortedSet* headers,
555 WritableFontData* fd,
556 DataBlockMap* table_data) {
Colin Cross13a82432017-04-07 10:50:33 -0700557 for (HeaderOffsetSortedSet::iterator it = headers->begin(),
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700558 table_end = headers->end();
Colin Cross13a82432017-04-07 10:50:33 -0700559 it != table_end;
560 ++it) {
561 const Ptr<Header> header = *it;
562 if (header->length() > kMaxTableSize)
563 continue;
564
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700565 FontDataPtr sliced_data;
Colin Cross13a82432017-04-07 10:50:33 -0700566 sliced_data.Attach(fd->Slice(header->offset(), header->length()));
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700567 WritableFontDataPtr data = down_cast<WritableFontData*>(sliced_data.p_);
Colin Cross13a82432017-04-07 10:50:33 -0700568 table_data->insert(DataBlockEntry(header, data));
Selim Gurun30d4e1f2013-08-15 12:46:15 -0700569 }
570}
571
572} // namespace sfntly