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