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