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
FontAssembler(FontInfo * font_info,IntegerSet * table_blacklist)38 FontAssembler::FontAssembler(FontInfo* font_info,
39 IntegerSet* table_blacklist)
40 : table_blacklist_(table_blacklist) {
41 font_info_ = font_info;
42 Initialize();
43 }
44
FontAssembler(FontInfo * font_info)45 FontAssembler::FontAssembler(FontInfo* font_info)
46 : table_blacklist_(NULL) {
47 font_info_ = font_info;
48 Initialize();
49 }
50
Initialize()51 void FontAssembler::Initialize() {
52 font_factory_.Attach(sfntly::FontFactory::GetInstance());
53 font_builder_.Attach(font_factory_->NewFontBuilder());
54 }
55
Assemble()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
AssembleCMapTable()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
AssembleGlyphAndLocaTables()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 loca_list.resize(std::max(loca_list.size(),
215 static_cast<size_t>(resolved_glyph_id + 2)));
216 for (int32_t i = last_glyph_id + 1; i <= resolved_glyph_id; ++i)
217 loca_list[i] = last_offset;
218 last_offset += length;
219 loca_list[resolved_glyph_id + 1] = last_offset;
220 last_glyph_id = resolved_glyph_id + 1;
221 }
222 // If there are missing glyph ids, their loca entries must all point
223 // to the same offset as the last valid glyph id making them all zero length.
224 for (int32_t i = last_glyph_id + 1; i <= num_loca_glyphs; ++i)
225 loca_list[i] = last_offset;
226 loca_table_builder->SetLocaList(&loca_list);
227 return true;
228 }
229 }
230