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 "subtly/font_info.h"
18
19#include <stdio.h>
20
21#include <set>
22#include <map>
23
24#include "subtly/character_predicate.h"
25
26#include "sfntly/tag.h"
27#include "sfntly/font.h"
28#include "sfntly/font_factory.h"
29#include "sfntly/table/core/cmap_table.h"
30#include "sfntly/table/truetype/loca_table.h"
31#include "sfntly/table/truetype/glyph_table.h"
32#include "sfntly/table/core/maximum_profile_table.h"
33#include "sfntly/port/type.h"
34#include "sfntly/port/refcount.h"
35
36namespace subtly {
37using namespace sfntly;
38/******************************************************************************
39 * GlyphId class
40 ******************************************************************************/
41GlyphId::GlyphId(int32_t glyph_id, FontId font_id)
42    : glyph_id_(glyph_id),
43      font_id_(font_id) {
44}
45
46bool GlyphId::operator==(const GlyphId& other) const {
47  return glyph_id_ == other.glyph_id();
48}
49
50bool GlyphId::operator<(const GlyphId& other) const {
51  return glyph_id_ < other.glyph_id();
52}
53
54/******************************************************************************
55 * FontInfo class
56 ******************************************************************************/
57FontInfo::FontInfo()
58    : chars_to_glyph_ids_(new CharacterMap),
59      resolved_glyph_ids_(new GlyphIdSet),
60      fonts_(new FontIdMap) {
61}
62
63FontInfo::FontInfo(CharacterMap* chars_to_glyph_ids,
64                   GlyphIdSet* resolved_glyph_ids,
65                   FontIdMap* fonts) {
66  chars_to_glyph_ids_ = new CharacterMap(chars_to_glyph_ids->begin(),
67                                         chars_to_glyph_ids->end());
68  resolved_glyph_ids_ = new GlyphIdSet(resolved_glyph_ids->begin(),
69                                       resolved_glyph_ids->end());
70  fonts_ = new FontIdMap(fonts->begin(), fonts->end());
71}
72
73FontInfo::~FontInfo() {
74  delete chars_to_glyph_ids_;
75  delete resolved_glyph_ids_;
76  delete fonts_;
77}
78
79FontDataTable* FontInfo::GetTable(FontId font_id, int32_t tag) {
80  if (!fonts_)
81    return NULL;
82  FontIdMap::iterator it = fonts_->find(font_id);
83  if (it == fonts_->end())
84    return NULL;
85  return it->second->GetTable(tag);
86}
87
88const TableMap* FontInfo::GetTableMap(FontId font_id) {
89  if (!fonts_)
90    return NULL;
91  FontIdMap::iterator it = fonts_->find(font_id);
92  if (it == fonts_->end())
93    return NULL;
94  return it->second->GetTableMap();
95}
96
97void FontInfo::set_chars_to_glyph_ids(CharacterMap* chars_to_glyph_ids) {
98  *chars_to_glyph_ids_ = *chars_to_glyph_ids;
99}
100
101void FontInfo::set_resolved_glyph_ids(GlyphIdSet* resolved_glyph_ids) {
102  *resolved_glyph_ids_ = *resolved_glyph_ids;
103}
104
105void FontInfo::set_fonts(FontIdMap* fonts) {
106  *fonts_ = *fonts;
107}
108
109/******************************************************************************
110 * FontSourcedInfoBuilder class
111 ******************************************************************************/
112FontSourcedInfoBuilder::FontSourcedInfoBuilder(Font* font, FontId font_id)
113    : font_(font),
114      font_id_(font_id),
115      predicate_(NULL) {
116  Initialize();
117}
118
119FontSourcedInfoBuilder::FontSourcedInfoBuilder(Font* font,
120                                               FontId font_id,
121                                               CharacterPredicate* predicate)
122    : font_(font),
123      font_id_(font_id),
124      predicate_(predicate) {
125  Initialize();
126}
127
128void FontSourcedInfoBuilder::Initialize() {
129  Ptr<CMapTable> cmap_table = down_cast<CMapTable*>(font_->GetTable(Tag::cmap));
130  // We prefer Windows BMP format 4 cmaps.
131  cmap_.Attach(cmap_table->GetCMap(CMapTable::WINDOWS_BMP));
132  // But if none is found,
133  if (!cmap_) {
134    return;
135  }
136  loca_table_ = down_cast<LocaTable*>(font_->GetTable(Tag::loca));
137  glyph_table_ = down_cast<GlyphTable*>(font_->GetTable(Tag::glyf));
138}
139
140CALLER_ATTACH FontInfo* FontSourcedInfoBuilder::GetFontInfo() {
141  CharacterMap* chars_to_glyph_ids = new CharacterMap;
142  bool success = GetCharacterMap(chars_to_glyph_ids);
143  if (!success) {
144    delete chars_to_glyph_ids;
145#if defined (SUBTLY_DEBUG)
146    fprintf(stderr, "Error creating character map.\n");
147#endif
148    return NULL;
149  }
150  GlyphIdSet* resolved_glyph_ids = new GlyphIdSet;
151  success = ResolveCompositeGlyphs(chars_to_glyph_ids, resolved_glyph_ids);
152  if (!success) {
153    delete chars_to_glyph_ids;
154    delete resolved_glyph_ids;
155#if defined (SUBTLY_DEBUG)
156    fprintf(stderr, "Error resolving composite glyphs.\n");
157#endif
158    return NULL;
159  }
160  Ptr<FontInfo> font_info = new FontInfo;
161  font_info->set_chars_to_glyph_ids(chars_to_glyph_ids);
162  font_info->set_resolved_glyph_ids(resolved_glyph_ids);
163  FontIdMap* font_id_map = new FontIdMap;
164  font_id_map->insert(std::make_pair(font_id_, font_));
165  font_info->set_fonts(font_id_map);
166  delete chars_to_glyph_ids;
167  delete resolved_glyph_ids;
168  delete font_id_map;
169  return font_info.Detach();
170}
171
172bool FontSourcedInfoBuilder::GetCharacterMap(CharacterMap* chars_to_glyph_ids) {
173  if (!cmap_ || !chars_to_glyph_ids)
174    return false;
175  chars_to_glyph_ids->clear();
176  CMapTable::CMap::CharacterIterator* character_iterator = cmap_->Iterator();
177  if (!character_iterator)
178    return false;
179  while (character_iterator->HasNext()) {
180    int32_t character = character_iterator->Next();
181    if (!predicate_ || (*predicate_)(character)) {
182      chars_to_glyph_ids->insert
183          (std::make_pair(character,
184                          GlyphId(cmap_->GlyphId(character), font_id_)));
185    }
186  }
187  delete character_iterator;
188  return true;
189}
190
191bool
192FontSourcedInfoBuilder::ResolveCompositeGlyphs(CharacterMap* chars_to_glyph_ids,
193                                               GlyphIdSet* resolved_glyph_ids) {
194  if (!chars_to_glyph_ids || !resolved_glyph_ids)
195    return false;
196  resolved_glyph_ids->clear();
197  resolved_glyph_ids->insert(GlyphId(0, font_id_));
198  IntegerSet* unresolved_glyph_ids = new IntegerSet;
199  // Since composite glyph elements might themselves be composite, we would need
200  // to recursively resolve the elements too. To avoid the recursion we
201  // create two sets, |unresolved_glyph_ids| for the unresolved glyphs,
202  // initially containing all the ids and |resolved_glyph_ids|, initially empty.
203  // We'll remove glyph ids from |unresolved_glyph_ids| until it is empty and,
204  // if the glyph is composite, add its elements to the unresolved set.
205  for (CharacterMap::iterator it = chars_to_glyph_ids->begin(),
206           e = chars_to_glyph_ids->end(); it != e; ++it) {
207    unresolved_glyph_ids->insert(it->second.glyph_id());
208  }
209  // As long as there are unresolved glyph ids.
210  while (!unresolved_glyph_ids->empty()) {
211    // Get the corresponding glyph.
212    int32_t glyph_id = *(unresolved_glyph_ids->begin());
213    unresolved_glyph_ids->erase(unresolved_glyph_ids->begin());
214    if (glyph_id < 0 || glyph_id > loca_table_->num_glyphs()) {
215#if defined (SUBTLY_DEBUG)
216      fprintf(stderr, "%d larger than %d or smaller than 0\n", glyph_id,
217              loca_table_->num_glyphs());
218#endif
219      continue;
220    }
221    int32_t length = loca_table_->GlyphLength(glyph_id);
222    if (length == 0) {
223#if defined (SUBTLY_DEBUG)
224      fprintf(stderr, "Zero length glyph %d\n", glyph_id);
225#endif
226      continue;
227    }
228    int32_t offset = loca_table_->GlyphOffset(glyph_id);
229    GlyphPtr glyph;
230    glyph.Attach(glyph_table_->GetGlyph(offset, length));
231    if (glyph == NULL) {
232#if defined (SUBTLY_DEBUG)
233      fprintf(stderr, "GetGlyph returned NULL for %d\n", glyph_id);
234#endif
235      continue;
236    }
237    // Mark the glyph as resolved.
238    resolved_glyph_ids->insert(GlyphId(glyph_id, font_id_));
239    // If it is composite, add all its components to the unresolved glyph set.
240    if (glyph->GlyphType() == GlyphType::kComposite) {
241      Ptr<GlyphTable::CompositeGlyph> composite_glyph =
242          down_cast<GlyphTable::CompositeGlyph*>(glyph.p_);
243      int32_t num_glyphs = composite_glyph->NumGlyphs();
244      for (int32_t i = 0; i < num_glyphs; ++i) {
245        int32_t glyph_id = composite_glyph->GlyphIndex(i);
246        if (resolved_glyph_ids->find(GlyphId(glyph_id, -1))
247            == resolved_glyph_ids->end()) {
248          unresolved_glyph_ids->insert(glyph_id);
249        }
250      }
251    }
252  }
253  delete unresolved_glyph_ids;
254  return true;
255}
256}
257