/* * Copyright 2011 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "subtly/font_info.h" #include <stdio.h> #include <set> #include <map> #include "subtly/character_predicate.h" #include "sfntly/tag.h" #include "sfntly/font.h" #include "sfntly/font_factory.h" #include "sfntly/table/core/cmap_table.h" #include "sfntly/table/truetype/loca_table.h" #include "sfntly/table/truetype/glyph_table.h" #include "sfntly/table/core/maximum_profile_table.h" #include "sfntly/port/type.h" #include "sfntly/port/refcount.h" namespace subtly { using namespace sfntly; /****************************************************************************** * GlyphId class ******************************************************************************/ GlyphId::GlyphId(int32_t glyph_id, FontId font_id) : glyph_id_(glyph_id), font_id_(font_id) { } bool GlyphId::operator==(const GlyphId& other) const { return glyph_id_ == other.glyph_id(); } bool GlyphId::operator<(const GlyphId& other) const { return glyph_id_ < other.glyph_id(); } /****************************************************************************** * FontInfo class ******************************************************************************/ FontInfo::FontInfo() : chars_to_glyph_ids_(new CharacterMap), resolved_glyph_ids_(new GlyphIdSet), fonts_(new FontIdMap) { } FontInfo::FontInfo(CharacterMap* chars_to_glyph_ids, GlyphIdSet* resolved_glyph_ids, FontIdMap* fonts) { chars_to_glyph_ids_ = new CharacterMap(chars_to_glyph_ids->begin(), chars_to_glyph_ids->end()); resolved_glyph_ids_ = new GlyphIdSet(resolved_glyph_ids->begin(), resolved_glyph_ids->end()); fonts_ = new FontIdMap(fonts->begin(), fonts->end()); } FontInfo::~FontInfo() { delete chars_to_glyph_ids_; delete resolved_glyph_ids_; delete fonts_; } FontDataTable* FontInfo::GetTable(FontId font_id, int32_t tag) { if (!fonts_) return NULL; FontIdMap::iterator it = fonts_->find(font_id); if (it == fonts_->end()) return NULL; return it->second->GetTable(tag); } const TableMap* FontInfo::GetTableMap(FontId font_id) { if (!fonts_) return NULL; FontIdMap::iterator it = fonts_->find(font_id); if (it == fonts_->end()) return NULL; return it->second->GetTableMap(); } void FontInfo::set_chars_to_glyph_ids(CharacterMap* chars_to_glyph_ids) { *chars_to_glyph_ids_ = *chars_to_glyph_ids; } void FontInfo::set_resolved_glyph_ids(GlyphIdSet* resolved_glyph_ids) { *resolved_glyph_ids_ = *resolved_glyph_ids; } void FontInfo::set_fonts(FontIdMap* fonts) { *fonts_ = *fonts; } /****************************************************************************** * FontSourcedInfoBuilder class ******************************************************************************/ FontSourcedInfoBuilder::FontSourcedInfoBuilder(Font* font, FontId font_id) : font_(font), font_id_(font_id), predicate_(NULL) { Initialize(); } FontSourcedInfoBuilder::FontSourcedInfoBuilder(Font* font, FontId font_id, CharacterPredicate* predicate) : font_(font), font_id_(font_id), predicate_(predicate) { Initialize(); } void FontSourcedInfoBuilder::Initialize() { Ptr<CMapTable> cmap_table = down_cast<CMapTable*>(font_->GetTable(Tag::cmap)); // We prefer Windows BMP format 4 cmaps. cmap_.Attach(cmap_table->GetCMap(CMapTable::WINDOWS_BMP)); // But if none is found, if (!cmap_) { return; } loca_table_ = down_cast<LocaTable*>(font_->GetTable(Tag::loca)); glyph_table_ = down_cast<GlyphTable*>(font_->GetTable(Tag::glyf)); } CALLER_ATTACH FontInfo* FontSourcedInfoBuilder::GetFontInfo() { CharacterMap* chars_to_glyph_ids = new CharacterMap; bool success = GetCharacterMap(chars_to_glyph_ids); if (!success) { delete chars_to_glyph_ids; #if defined (SUBTLY_DEBUG) fprintf(stderr, "Error creating character map.\n"); #endif return NULL; } GlyphIdSet* resolved_glyph_ids = new GlyphIdSet; success = ResolveCompositeGlyphs(chars_to_glyph_ids, resolved_glyph_ids); if (!success) { delete chars_to_glyph_ids; delete resolved_glyph_ids; #if defined (SUBTLY_DEBUG) fprintf(stderr, "Error resolving composite glyphs.\n"); #endif return NULL; } Ptr<FontInfo> font_info = new FontInfo; font_info->set_chars_to_glyph_ids(chars_to_glyph_ids); font_info->set_resolved_glyph_ids(resolved_glyph_ids); FontIdMap* font_id_map = new FontIdMap; font_id_map->insert(std::make_pair(font_id_, font_)); font_info->set_fonts(font_id_map); delete chars_to_glyph_ids; delete resolved_glyph_ids; delete font_id_map; return font_info.Detach(); } bool FontSourcedInfoBuilder::GetCharacterMap(CharacterMap* chars_to_glyph_ids) { if (!cmap_ || !chars_to_glyph_ids) return false; chars_to_glyph_ids->clear(); CMapTable::CMap::CharacterIterator* character_iterator = cmap_->Iterator(); if (!character_iterator) return false; while (character_iterator->HasNext()) { int32_t character = character_iterator->Next(); if (!predicate_ || (*predicate_)(character)) { chars_to_glyph_ids->insert (std::make_pair(character, GlyphId(cmap_->GlyphId(character), font_id_))); } } delete character_iterator; return true; } bool FontSourcedInfoBuilder::ResolveCompositeGlyphs(CharacterMap* chars_to_glyph_ids, GlyphIdSet* resolved_glyph_ids) { if (!chars_to_glyph_ids || !resolved_glyph_ids) return false; resolved_glyph_ids->clear(); resolved_glyph_ids->insert(GlyphId(0, font_id_)); IntegerSet* unresolved_glyph_ids = new IntegerSet; // Since composite glyph elements might themselves be composite, we would need // to recursively resolve the elements too. To avoid the recursion we // create two sets, |unresolved_glyph_ids| for the unresolved glyphs, // initially containing all the ids and |resolved_glyph_ids|, initially empty. // We'll remove glyph ids from |unresolved_glyph_ids| until it is empty and, // if the glyph is composite, add its elements to the unresolved set. for (CharacterMap::iterator it = chars_to_glyph_ids->begin(), e = chars_to_glyph_ids->end(); it != e; ++it) { unresolved_glyph_ids->insert(it->second.glyph_id()); } // As long as there are unresolved glyph ids. while (!unresolved_glyph_ids->empty()) { // Get the corresponding glyph. int32_t glyph_id = *(unresolved_glyph_ids->begin()); unresolved_glyph_ids->erase(unresolved_glyph_ids->begin()); if (glyph_id < 0 || glyph_id > loca_table_->num_glyphs()) { #if defined (SUBTLY_DEBUG) fprintf(stderr, "%d larger than %d or smaller than 0\n", glyph_id, loca_table_->num_glyphs()); #endif continue; } int32_t length = loca_table_->GlyphLength(glyph_id); if (length == 0) { #if defined (SUBTLY_DEBUG) fprintf(stderr, "Zero length glyph %d\n", glyph_id); #endif continue; } int32_t offset = loca_table_->GlyphOffset(glyph_id); GlyphPtr glyph; glyph.Attach(glyph_table_->GetGlyph(offset, length)); if (glyph == NULL) { #if defined (SUBTLY_DEBUG) fprintf(stderr, "GetGlyph returned NULL for %d\n", glyph_id); #endif continue; } // Mark the glyph as resolved. resolved_glyph_ids->insert(GlyphId(glyph_id, font_id_)); // If it is composite, add all its components to the unresolved glyph set. if (glyph->GlyphType() == GlyphType::kComposite) { Ptr<GlyphTable::CompositeGlyph> composite_glyph = down_cast<GlyphTable::CompositeGlyph*>(glyph.p_); int32_t num_glyphs = composite_glyph->NumGlyphs(); for (int32_t i = 0; i < num_glyphs; ++i) { int32_t glyph_id = composite_glyph->GlyphIndex(i); if (resolved_glyph_ids->find(GlyphId(glyph_id, -1)) == resolved_glyph_ids->end()) { unresolved_glyph_ids->insert(glyph_id); } } } } delete unresolved_glyph_ids; return true; } }