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