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 
36 namespace subtly {
37 using namespace sfntly;
38 /******************************************************************************
39  * GlyphId class
40  ******************************************************************************/
GlyphId(int32_t glyph_id,FontId font_id)41 GlyphId::GlyphId(int32_t glyph_id, FontId font_id)
42     : glyph_id_(glyph_id),
43       font_id_(font_id) {
44 }
45 
operator ==(const GlyphId & other) const46 bool GlyphId::operator==(const GlyphId& other) const {
47   return glyph_id_ == other.glyph_id();
48 }
49 
operator <(const GlyphId & other) const50 bool GlyphId::operator<(const GlyphId& other) const {
51   return glyph_id_ < other.glyph_id();
52 }
53 
54 /******************************************************************************
55  * FontInfo class
56  ******************************************************************************/
FontInfo()57 FontInfo::FontInfo()
58     : chars_to_glyph_ids_(new CharacterMap),
59       resolved_glyph_ids_(new GlyphIdSet),
60       fonts_(new FontIdMap) {
61 }
62 
FontInfo(CharacterMap * chars_to_glyph_ids,GlyphIdSet * resolved_glyph_ids,FontIdMap * fonts)63 FontInfo::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 
~FontInfo()73 FontInfo::~FontInfo() {
74   delete chars_to_glyph_ids_;
75   delete resolved_glyph_ids_;
76   delete fonts_;
77 }
78 
GetTable(FontId font_id,int32_t tag)79 FontDataTable* 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 
GetTableMap(FontId font_id)88 const 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 
set_chars_to_glyph_ids(CharacterMap * chars_to_glyph_ids)97 void FontInfo::set_chars_to_glyph_ids(CharacterMap* chars_to_glyph_ids) {
98   *chars_to_glyph_ids_ = *chars_to_glyph_ids;
99 }
100 
set_resolved_glyph_ids(GlyphIdSet * resolved_glyph_ids)101 void FontInfo::set_resolved_glyph_ids(GlyphIdSet* resolved_glyph_ids) {
102   *resolved_glyph_ids_ = *resolved_glyph_ids;
103 }
104 
set_fonts(FontIdMap * fonts)105 void FontInfo::set_fonts(FontIdMap* fonts) {
106   *fonts_ = *fonts;
107 }
108 
109 /******************************************************************************
110  * FontSourcedInfoBuilder class
111  ******************************************************************************/
FontSourcedInfoBuilder(Font * font,FontId font_id)112 FontSourcedInfoBuilder::FontSourcedInfoBuilder(Font* font, FontId font_id)
113     : font_(font),
114       font_id_(font_id),
115       predicate_(NULL) {
116   Initialize();
117 }
118 
FontSourcedInfoBuilder(Font * font,FontId font_id,CharacterPredicate * predicate)119 FontSourcedInfoBuilder::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 
Initialize()128 void 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 
GetFontInfo()140 CALLER_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 
GetCharacterMap(CharacterMap * chars_to_glyph_ids)172 bool 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 
191 bool
ResolveCompositeGlyphs(CharacterMap * chars_to_glyph_ids,GlyphIdSet * resolved_glyph_ids)192 FontSourcedInfoBuilder::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