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_assembler.h" 18 19 #include <stdio.h> 20 21 #include <set> 22 #include <map> 23 24 #include "sfntly/tag.h" 25 #include "sfntly/font.h" 26 #include "sfntly/font_factory.h" 27 #include "sfntly/table/core/cmap_table.h" 28 #include "sfntly/table/truetype/loca_table.h" 29 #include "sfntly/table/truetype/glyph_table.h" 30 #include "sfntly/table/core/maximum_profile_table.h" 31 #include "sfntly/port/type.h" 32 #include "sfntly/port/refcount.h" 33 #include "subtly/font_info.h" 34 35 namespace subtly { 36 using namespace sfntly; 37 38 FontAssembler::FontAssembler(FontInfo* font_info, 39 IntegerSet* table_blacklist) 40 : table_blacklist_(table_blacklist) { 41 font_info_ = font_info; 42 Initialize(); 43 } 44 45 FontAssembler::FontAssembler(FontInfo* font_info) 46 : table_blacklist_(NULL) { 47 font_info_ = font_info; 48 Initialize(); 49 } 50 51 void FontAssembler::Initialize() { 52 font_factory_.Attach(sfntly::FontFactory::GetInstance()); 53 font_builder_.Attach(font_factory_->NewFontBuilder()); 54 } 55 56 CALLER_ATTACH Font* FontAssembler::Assemble() { 57 // Assemble tables we can subset. 58 if (!AssembleCMapTable() || !AssembleGlyphAndLocaTables()) { 59 return NULL; 60 } 61 // For all other tables, either include them unmodified or don't at all. 62 const TableMap* common_table_map = 63 font_info_->GetTableMap(font_info_->fonts()->begin()->first); 64 for (TableMap::const_iterator it = common_table_map->begin(), 65 e = common_table_map->end(); it != e; ++it) { 66 if (table_blacklist_ 67 && table_blacklist_->find(it->first) != table_blacklist_->end()) { 68 continue; 69 } 70 font_builder_->NewTableBuilder(it->first, it->second->ReadFontData()); 71 } 72 return font_builder_->Build(); 73 } 74 75 bool FontAssembler::AssembleCMapTable() { 76 // Creating the new CMapTable and the new format 4 CMap 77 Ptr<CMapTable::Builder> cmap_table_builder = 78 down_cast<CMapTable::Builder*> 79 (font_builder_->NewTableBuilder(Tag::cmap)); 80 if (!cmap_table_builder) 81 return false; 82 Ptr<CMapTable::CMapFormat4::Builder> cmap_builder = 83 down_cast<CMapTable::CMapFormat4::Builder*> 84 (cmap_table_builder->NewCMapBuilder(CMapFormat::kFormat4, 85 CMapTable::WINDOWS_BMP)); 86 if (!cmap_builder) 87 return false; 88 // Creating the segments and the glyph id array 89 CharacterMap* chars_to_glyph_ids = font_info_->chars_to_glyph_ids(); 90 SegmentList* segment_list = new SegmentList; 91 IntegerList* glyph_id_array = new IntegerList; 92 int32_t last_chararacter = -2; 93 int32_t last_offset = 0; 94 Ptr<CMapTable::CMapFormat4::Builder::Segment> current_segment; 95 96 // For simplicity, we will have one segment per contiguous range. 97 // To test the algorithm, we've replaced the original CMap with the CMap 98 // generated by this code without removing any character. 99 // Tuffy.ttf: CMap went from 3146 to 3972 bytes (1.7% to 2.17% of file) 100 // AnonymousPro.ttf: CMap went from 1524 to 1900 bytes (0.96% to 1.2%) 101 for (CharacterMap::iterator it = chars_to_glyph_ids->begin(), 102 e = chars_to_glyph_ids->end(); it != e; ++it) { 103 int32_t character = it->first; 104 int32_t glyph_id = it->second.glyph_id(); 105 if (character != last_chararacter + 1) { // new segment 106 if (current_segment != NULL) { 107 current_segment->set_end_count(last_chararacter); 108 segment_list->push_back(current_segment); 109 } 110 // start_code = character 111 // end_code = -1 (unknown for now) 112 // id_delta = 0 (we don't use id_delta for this representation) 113 // id_range_offset = last_offset (offset into the glyph_id_array) 114 current_segment = 115 new CMapTable::CMapFormat4::Builder:: 116 Segment(character, -1, 0, last_offset); 117 } 118 glyph_id_array->push_back(glyph_id); 119 last_offset += DataSize::kSHORT; 120 last_chararacter = character; 121 } 122 // The last segment is still open. 123 current_segment->set_end_count(last_chararacter); 124 segment_list->push_back(current_segment); 125 // Updating the id_range_offset for every segment. 126 for (int32_t i = 0, num_segs = segment_list->size(); i < num_segs; ++i) { 127 Ptr<CMapTable::CMapFormat4::Builder::Segment> segment = segment_list->at(i); 128 segment->set_id_range_offset(segment->id_range_offset() 129 + (num_segs - i + 1) * DataSize::kSHORT); 130 } 131 // Adding the final, required segment. 132 current_segment = 133 new CMapTable::CMapFormat4::Builder::Segment(0xffff, 0xffff, 1, 0); 134 segment_list->push_back(current_segment); 135 // Writing the segments and glyph id array to the CMap 136 cmap_builder->set_segments(segment_list); 137 cmap_builder->set_glyph_id_array(glyph_id_array); 138 delete segment_list; 139 delete glyph_id_array; 140 return true; 141 } 142 143 bool FontAssembler::AssembleGlyphAndLocaTables() { 144 Ptr<LocaTable::Builder> loca_table_builder = 145 down_cast<LocaTable::Builder*> 146 (font_builder_->NewTableBuilder(Tag::loca)); 147 Ptr<GlyphTable::Builder> glyph_table_builder = 148 down_cast<GlyphTable::Builder*> 149 (font_builder_->NewTableBuilder(Tag::glyf)); 150 151 GlyphIdSet* resolved_glyph_ids = font_info_->resolved_glyph_ids(); 152 IntegerList loca_list; 153 // Basic sanity check: all LOCA tables are of the same size 154 // This is necessary but not suficient! 155 int32_t previous_size = -1; 156 for (FontIdMap::iterator it = font_info_->fonts()->begin(); 157 it != font_info_->fonts()->end(); ++it) { 158 Ptr<LocaTable> loca_table = 159 down_cast<LocaTable*>(font_info_->GetTable(it->first, Tag::loca)); 160 int32_t current_size = loca_table->header_length(); 161 if (previous_size != -1 && current_size != previous_size) { 162 return false; 163 } 164 previous_size = current_size; 165 } 166 167 // Assuming all fonts referenced by the FontInfo are the subsets of the same 168 // font, their loca tables should all have the same sizes. 169 // We'll just get the size of the first font's LOCA table for simplicty. 170 Ptr<LocaTable> first_loca_table = 171 down_cast<LocaTable*> 172 (font_info_->GetTable(font_info_->fonts()->begin()->first, Tag::loca)); 173 int32_t num_loca_glyphs = first_loca_table->num_glyphs(); 174 loca_list.resize(num_loca_glyphs); 175 loca_list.push_back(0); 176 int32_t last_glyph_id = 0; 177 int32_t last_offset = 0; 178 GlyphTable::GlyphBuilderList* glyph_builders = 179 glyph_table_builder->GlyphBuilders(); 180 181 for (GlyphIdSet::iterator it = resolved_glyph_ids->begin(), 182 e = resolved_glyph_ids->end(); it != e; ++it) { 183 // Get the glyph for this resolved_glyph_id. 184 int32_t resolved_glyph_id = it->glyph_id(); 185 int32_t font_id = it->font_id(); 186 // Get the LOCA table for the current glyph id. 187 Ptr<LocaTable> loca_table = 188 down_cast<LocaTable*> 189 (font_info_->GetTable(font_id, Tag::loca)); 190 int32_t length = loca_table->GlyphLength(resolved_glyph_id); 191 int32_t offset = loca_table->GlyphOffset(resolved_glyph_id); 192 193 // Get the GLYF table for the current glyph id. 194 Ptr<GlyphTable> glyph_table = 195 down_cast<GlyphTable*> 196 (font_info_->GetTable(font_id, Tag::glyf)); 197 GlyphPtr glyph; 198 glyph.Attach(glyph_table->GetGlyph(offset, length)); 199 200 // The data reference by the glyph is copied into a new glyph and 201 // added to the glyph_builders belonging to the glyph_table_builder. 202 // When Build gets called, all the glyphs will be built. 203 Ptr<ReadableFontData> data = glyph->ReadFontData(); 204 Ptr<WritableFontData> copy_data; 205 copy_data.Attach(WritableFontData::CreateWritableFontData(data->Length())); 206 data->CopyTo(copy_data); 207 GlyphBuilderPtr glyph_builder; 208 glyph_builder.Attach(glyph_table_builder->GlyphBuilder(copy_data)); 209 glyph_builders->push_back(glyph_builder); 210 211 // If there are missing glyphs between the last glyph_id and the 212 // current resolved_glyph_id, since the LOCA table needs to have the same 213 // size, the offset is kept the same. 214 for (int32_t i = last_glyph_id + 1; i <= resolved_glyph_id; ++i) 215 loca_list[i] = last_offset; 216 last_offset += length; 217 loca_list[resolved_glyph_id + 1] = last_offset; 218 last_glyph_id = resolved_glyph_id + 1; 219 } 220 // If there are missing glyph ids, their loca entries must all point 221 // to the same offset as the last valid glyph id making them all zero length. 222 for (int32_t i = last_glyph_id + 1; i <= num_loca_glyphs; ++i) 223 loca_list[i] = last_offset; 224 loca_table_builder->SetLocaList(&loca_list); 225 return true; 226 } 227 } 228