Home | History | Annotate | Download | only in subtly
      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  ******************************************************************************/
     41 GlyphId::GlyphId(int32_t glyph_id, FontId font_id)
     42     : glyph_id_(glyph_id),
     43       font_id_(font_id) {
     44 }
     45 
     46 bool GlyphId::operator==(const GlyphId& other) const {
     47   return glyph_id_ == other.glyph_id();
     48 }
     49 
     50 bool GlyphId::operator<(const GlyphId& other) const {
     51   return glyph_id_ < other.glyph_id();
     52 }
     53 
     54 /******************************************************************************
     55  * FontInfo class
     56  ******************************************************************************/
     57 FontInfo::FontInfo()
     58     : chars_to_glyph_ids_(new CharacterMap),
     59       resolved_glyph_ids_(new GlyphIdSet),
     60       fonts_(new FontIdMap) {
     61 }
     62 
     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 
     73 FontInfo::~FontInfo() {
     74   delete chars_to_glyph_ids_;
     75   delete resolved_glyph_ids_;
     76   delete fonts_;
     77 }
     78 
     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 
     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 
     97 void FontInfo::set_chars_to_glyph_ids(CharacterMap* chars_to_glyph_ids) {
     98   *chars_to_glyph_ids_ = *chars_to_glyph_ids;
     99 }
    100 
    101 void FontInfo::set_resolved_glyph_ids(GlyphIdSet* resolved_glyph_ids) {
    102   *resolved_glyph_ids_ = *resolved_glyph_ids;
    103 }
    104 
    105 void FontInfo::set_fonts(FontIdMap* fonts) {
    106   *fonts_ = *fonts;
    107 }
    108 
    109 /******************************************************************************
    110  * FontSourcedInfoBuilder class
    111  ******************************************************************************/
    112 FontSourcedInfoBuilder::FontSourcedInfoBuilder(Font* font, FontId font_id)
    113     : font_(font),
    114       font_id_(font_id),
    115       predicate_(NULL) {
    116   Initialize();
    117 }
    118 
    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 
    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 
    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 
    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
    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