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