OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2011 Google Inc. All Rights Reserved. | 2 * Copyright 2011 Google Inc. All Rights Reserved. |
3 * | 3 * |
4 * Licensed under the Apache License, Version 2.0 (the "License"); | 4 * Licensed under the Apache License, Version 2.0 (the "License"); |
5 * you may not use this file except in compliance with 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 | 6 * You may obtain a copy of the License at |
7 * | 7 * |
8 * http://www.apache.org/licenses/LICENSE-2.0 | 8 * http://www.apache.org/licenses/LICENSE-2.0 |
9 * | 9 * |
10 * Unless required by applicable law or agreed to in writing, software | 10 * Unless required by applicable law or agreed to in writing, software |
11 * distributed under the License is distributed on an "AS IS" BASIS, | 11 * distributed under the License is distributed on an "AS IS" BASIS, |
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 * See the License for the specific language governing permissions and | 13 * See the License for the specific language governing permissions and |
14 * limitations under the License. | 14 * limitations under the License. |
15 */ | 15 */ |
16 | 16 |
17 #include "third_party/sfntly/src/subsetter/subsetter_impl.h" | 17 #include "third_party/sfntly/src/subsetter/subsetter_impl.h" |
18 | 18 |
19 #include <string.h> | 19 #include <string.h> |
20 | 20 |
21 #include <algorithm> | |
22 #include <iterator> | |
21 #include <map> | 23 #include <map> |
22 #include <set> | 24 #include <set> |
23 | 25 |
26 #include "third_party/sfntly/src/sfntly/table/bitmap/eblc_table.h" | |
27 #include "third_party/sfntly/src/sfntly/table/bitmap/ebdt_table.h" | |
28 #include "third_party/sfntly/src/sfntly/table/bitmap/index_sub_table.h" | |
29 #include "third_party/sfntly/src/sfntly/table/bitmap/index_sub_table_format1.h" | |
30 #include "third_party/sfntly/src/sfntly/table/bitmap/index_sub_table_format2.h" | |
31 #include "third_party/sfntly/src/sfntly/table/bitmap/index_sub_table_format3.h" | |
32 #include "third_party/sfntly/src/sfntly/table/bitmap/index_sub_table_format4.h" | |
33 #include "third_party/sfntly/src/sfntly/table/bitmap/index_sub_table_format5.h" | |
24 #include "third_party/sfntly/src/sfntly/table/core/name_table.h" | 34 #include "third_party/sfntly/src/sfntly/table/core/name_table.h" |
25 #include "third_party/sfntly/src/sfntly/table/truetype/glyph_table.h" | 35 #include "third_party/sfntly/src/sfntly/table/truetype/glyph_table.h" |
26 #include "third_party/sfntly/src/sfntly/table/truetype/loca_table.h" | 36 #include "third_party/sfntly/src/sfntly/table/truetype/loca_table.h" |
27 #include "third_party/sfntly/src/sfntly/tag.h" | 37 #include "third_party/sfntly/src/sfntly/tag.h" |
28 #include "third_party/sfntly/src/sfntly/data/memory_byte_array.h" | 38 #include "third_party/sfntly/src/sfntly/data/memory_byte_array.h" |
29 #include "third_party/sfntly/src/sfntly/port/memory_input_stream.h" | 39 #include "third_party/sfntly/src/sfntly/port/memory_input_stream.h" |
30 #include "third_party/sfntly/src/sfntly/port/memory_output_stream.h" | 40 #include "third_party/sfntly/src/sfntly/port/memory_output_stream.h" |
31 | 41 |
42 #if defined U_USING_ICU_NAMESPACE | |
43 U_NAMESPACE_USE | |
44 #endif | |
45 | |
46 namespace { | |
47 | |
48 // The bitmap tables must be greater than 16KB to trigger bitmap subsetter. | |
49 static const int BITMAP_SIZE_THRESHOLD = 16384; | |
50 | |
51 } | |
52 | |
53 // Have namespace sfntly since upstream has test cases testing the functions. | |
32 namespace sfntly { | 54 namespace sfntly { |
33 | 55 |
34 void ConstructName(UChar* name_part, UnicodeString* name, int32_t name_id) { | 56 void ConstructName(UChar* name_part, UnicodeString* name, int32_t name_id) { |
35 switch(name_id) { | 57 switch (name_id) { |
36 case NameId::kFullFontName: | 58 case NameId::kFullFontName: |
37 *name = name_part; | 59 *name = name_part; |
38 break; | 60 break; |
39 case NameId::kFontFamilyName: | 61 case NameId::kFontFamilyName: |
40 case NameId::kPreferredFamily: | 62 case NameId::kPreferredFamily: |
41 case NameId::kWWSFamilyName: { | 63 case NameId::kWWSFamilyName: { |
42 UnicodeString original = *name; | 64 UnicodeString original = *name; |
43 *name = name_part; | 65 *name = name_part; |
44 *name += original; | 66 *name += original; |
45 break; | 67 break; |
(...skipping 18 matching lines...) Expand all Loading... | |
64 } else if (name_id == NameId::kPreferredFamily || | 86 } else if (name_id == NameId::kPreferredFamily || |
65 name_id == NameId::kPreferredSubfamily) { | 87 name_id == NameId::kPreferredSubfamily) { |
66 result |= 0xf; | 88 result |= 0xf; |
67 } else if (name_id == NameId::kWWSFamilyName || | 89 } else if (name_id == NameId::kWWSFamilyName || |
68 name_id == NameId::kWWSSubfamilyName) { | 90 name_id == NameId::kWWSSubfamilyName) { |
69 result |= 1; | 91 result |= 1; |
70 } | 92 } |
71 return result; | 93 return result; |
72 } | 94 } |
73 | 95 |
74 SubsetterImpl::SubsetterImpl() { | 96 bool HasName(const char* font_name, Font* font) { |
75 } | |
76 | |
77 SubsetterImpl::~SubsetterImpl() { | |
78 } | |
79 | |
80 bool SubsetterImpl::LoadFont(const char* font_name, | |
81 const unsigned char* original_font, | |
82 size_t font_size) { | |
83 MemoryInputStream mis; | |
84 mis.Attach(original_font, font_size); | |
85 if (factory_ == NULL) { | |
86 factory_.Attach(FontFactory::GetInstance()); | |
87 } | |
88 | |
89 FontArray font_array; | |
90 factory_->LoadFonts(&mis, &font_array); | |
91 font_ = FindFont(font_name, font_array); | |
92 if (font_ == NULL) { | |
93 return false; | |
94 } | |
95 | |
96 return true; | |
97 } | |
98 | |
99 int SubsetterImpl::SubsetFont(const unsigned int* glyph_ids, | |
100 size_t glyph_count, | |
101 unsigned char** output_buffer) { | |
102 if (factory_ == NULL || font_ == NULL) { | |
103 return -1; | |
104 } | |
105 | |
106 IntegerSet glyph_id_processed; | |
107 if (!ResolveCompositeGlyphs(glyph_ids, glyph_count, &glyph_id_processed) || | |
108 glyph_id_processed.empty()) { | |
109 return 0; | |
110 } | |
111 | |
112 FontPtr new_font; | |
113 new_font.Attach(Subset(glyph_id_processed)); | |
114 if (new_font == NULL) { | |
115 return 0; | |
116 } | |
117 | |
118 MemoryOutputStream output_stream; | |
119 factory_->SerializeFont(new_font, &output_stream); | |
120 int length = static_cast<int>(output_stream.Size()); | |
121 if (length > 0) { | |
122 *output_buffer = new unsigned char[length]; | |
123 memcpy(*output_buffer, output_stream.Get(), length); | |
124 } | |
125 | |
126 return length; | |
127 } | |
128 | |
129 Font* SubsetterImpl::FindFont(const char* font_name, | |
130 const FontArray& font_array) { | |
131 if (font_array.empty() || font_array[0] == NULL) { | |
132 return NULL; | |
133 } | |
134 | |
135 if (font_name && strlen(font_name)) { | |
136 for (FontArray::const_iterator b = font_array.begin(), e = font_array.end(); | |
137 b != e; ++b) { | |
138 if (HasName(font_name, (*b).p_)) { | |
139 return (*b).p_; | |
140 } | |
141 } | |
142 } | |
143 | |
144 return font_array[0].p_; | |
145 } | |
146 | |
147 bool SubsetterImpl::HasName(const char* font_name, Font* font) { | |
148 UnicodeString font_string = UnicodeString::fromUTF8(font_name); | 97 UnicodeString font_string = UnicodeString::fromUTF8(font_name); |
149 if (font_string.isEmpty()) | 98 if (font_string.isEmpty()) |
150 return false; | 99 return false; |
151 UnicodeString regular_suffix = UnicodeString::fromUTF8(" Regular"); | 100 UnicodeString regular_suffix = UnicodeString::fromUTF8(" Regular"); |
152 UnicodeString alt_font_string = font_string; | 101 UnicodeString alt_font_string = font_string; |
153 alt_font_string += regular_suffix; | 102 alt_font_string += regular_suffix; |
154 | 103 |
155 typedef std::map<int32_t, UnicodeString> NameMap; | 104 typedef std::map<int32_t, UnicodeString> NameMap; |
156 NameMap names; | 105 NameMap names; |
157 NameTablePtr name_table = down_cast<NameTable*>(font->GetTable(Tag::name)); | 106 NameTablePtr name_table = down_cast<NameTable*>(font->GetTable(Tag::name)); |
158 if (name_table == NULL) { | 107 if (name_table == NULL) { |
159 return false; | 108 return false; |
160 } | 109 } |
161 | 110 |
162 for (int32_t i = 0; i < name_table->NameCount(); ++i) { | 111 for (int32_t i = 0; i < name_table->NameCount(); ++i) { |
163 switch(name_table->NameId(i)) { | 112 switch (name_table->NameId(i)) { |
164 case NameId::kFontFamilyName: | 113 case NameId::kFontFamilyName: |
165 case NameId::kFontSubfamilyName: | 114 case NameId::kFontSubfamilyName: |
166 case NameId::kFullFontName: | 115 case NameId::kFullFontName: |
167 case NameId::kPreferredFamily: | 116 case NameId::kPreferredFamily: |
168 case NameId::kPreferredSubfamily: | 117 case NameId::kPreferredSubfamily: |
169 case NameId::kWWSFamilyName: | 118 case NameId::kWWSFamilyName: |
170 case NameId::kWWSSubfamilyName: { | 119 case NameId::kWWSSubfamilyName: { |
171 int32_t hash_code = HashCode(name_table->PlatformId(i), | 120 int32_t hash_code = HashCode(name_table->PlatformId(i), |
172 name_table->EncodingId(i), | 121 name_table->EncodingId(i), |
173 name_table->LanguageId(i), | 122 name_table->LanguageId(i), |
174 name_table->NameId(i)); | 123 name_table->NameId(i)); |
175 UChar* name_part = name_table->Name(i); | 124 UChar* name_part = name_table->Name(i); |
176 ConstructName(name_part, &(names[hash_code]), name_table->NameId(i)); | 125 ConstructName(name_part, &(names[hash_code]), name_table->NameId(i)); |
177 delete[] name_part; | 126 delete[] name_part; |
178 break; | 127 break; |
179 } | 128 } |
180 default: | 129 default: |
181 break; | 130 break; |
182 } | 131 } |
183 } | 132 } |
184 | 133 |
185 if (!names.empty()) { | 134 if (!names.empty()) { |
186 for (NameMap::iterator b = names.begin(), e = names.end(); b != e; ++b) { | 135 for (NameMap::iterator i = names.begin(), e = names.end(); i != e; ++i) { |
187 if (b->second.caseCompare(font_string, 0) == 0 || | 136 if (i->second.caseCompare(font_string, 0) == 0 || |
188 b->second.caseCompare(alt_font_string, 0) == 0) { | 137 i->second.caseCompare(alt_font_string, 0) == 0) { |
189 return true; | 138 return true; |
190 } | 139 } |
191 } | 140 } |
192 } | 141 } |
193 return false; | 142 return false; |
194 } | 143 } |
195 | 144 |
196 bool SubsetterImpl::ResolveCompositeGlyphs(const unsigned int* glyph_ids, | 145 Font* FindFont(const char* font_name, const FontArray& font_array) { |
197 size_t glyph_count, | 146 if (font_array.empty() || font_array[0] == NULL) { |
198 IntegerSet* glyph_id_processed) { | 147 return NULL; |
199 if (glyph_ids == NULL || glyph_count == 0 || glyph_id_processed == NULL) { | 148 } |
149 | |
150 if (font_name && strlen(font_name)) { | |
151 for (FontArray::const_iterator i = font_array.begin(), e = font_array.end(); | |
152 i != e; ++i) { | |
153 if (HasName(font_name, i->p_)) { | |
154 return i->p_; | |
155 } | |
156 } | |
157 } | |
158 | |
159 return font_array[0].p_; | |
160 } | |
161 | |
162 bool ResolveCompositeGlyphs(GlyphTable* glyf, | |
163 LocaTable* loca, | |
164 const unsigned int* glyph_ids, | |
165 size_t glyph_count, | |
166 IntegerSet* glyph_id_processed) { | |
167 if (glyf == NULL || loca == NULL || glyph_ids == NULL || glyph_count == 0 || | |
168 glyph_id_processed == NULL) { | |
200 return false; | 169 return false; |
201 } | 170 } |
202 | 171 |
203 // Find glyf and loca table. | 172 // Find glyf and loca table. |
204 GlyphTablePtr glyph_table = | 173 GlyphTablePtr glyph_table = glyf; |
205 down_cast<GlyphTable*>(font_->GetTable(Tag::glyf)); | 174 LocaTablePtr loca_table = loca; |
206 LocaTablePtr loca_table = down_cast<LocaTable*>(font_->GetTable(Tag::loca)); | |
207 if (glyph_table == NULL || loca_table == NULL) { | |
208 // The font is invalid. | |
209 return false; | |
210 } | |
211 | 175 |
212 // Sort and uniquify glyph ids. | 176 // Sort and uniquify glyph ids. |
213 IntegerSet glyph_id_remaining; | 177 IntegerSet glyph_id_remaining; |
214 glyph_id_remaining.insert(0); // Always include glyph id 0. | 178 glyph_id_remaining.insert(0); // Always include glyph id 0. |
215 for (size_t i = 0; i < glyph_count; ++i) { | 179 for (size_t i = 0; i < glyph_count; ++i) { |
216 glyph_id_remaining.insert(glyph_ids[i]); | 180 glyph_id_remaining.insert(glyph_ids[i]); |
217 } | 181 } |
218 | 182 |
219 // Identify if any given glyph id maps to a composite glyph. If so, include | 183 // Identify if any given glyph id maps to a composite glyph. If so, include |
220 // the glyphs referenced by that composite glyph. | 184 // the glyphs referenced by that composite glyph. |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
256 glyph_id_processed->insert(*i); | 220 glyph_id_processed->insert(*i); |
257 } | 221 } |
258 | 222 |
259 glyph_id_remaining.clear(); | 223 glyph_id_remaining.clear(); |
260 glyph_id_remaining = comp_glyph_id; | 224 glyph_id_remaining = comp_glyph_id; |
261 } | 225 } |
262 | 226 |
263 return true; | 227 return true; |
264 } | 228 } |
265 | 229 |
266 CALLER_ATTACH Font* SubsetterImpl::Subset(const IntegerSet& glyph_ids) { | 230 bool SetupGlyfBuilders(Font::Builder* builder, |
231 GlyphTable* glyf, | |
232 LocaTable* loca, | |
233 const IntegerSet& glyph_ids) { | |
234 if (!builder || !glyf || !loca) { | |
235 return false; | |
236 } | |
237 | |
267 // The tables are already checked in ResolveCompositeGlyphs(). | 238 // The tables are already checked in ResolveCompositeGlyphs(). |
268 GlyphTablePtr glyph_table = | 239 GlyphTablePtr glyph_table = glyf; |
269 down_cast<GlyphTable*>(font_->GetTable(Tag::glyf)); | 240 LocaTablePtr loca_table = loca; |
270 LocaTablePtr loca_table = down_cast<LocaTable*>(font_->GetTable(Tag::loca)); | |
271 | 241 |
272 // Setup font builders we need. | 242 FontBuilderPtr font_builder = builder; |
273 FontBuilderPtr font_builder; | |
274 font_builder.Attach(factory_->NewFontBuilder()); | |
275 | |
276 GlyphTableBuilderPtr glyph_table_builder = | 243 GlyphTableBuilderPtr glyph_table_builder = |
277 down_cast<GlyphTable::Builder*>(font_builder->NewTableBuilder(Tag::glyf)); | 244 down_cast<GlyphTable::Builder*>(font_builder->NewTableBuilder(Tag::glyf)); |
278 LocaTableBuilderPtr loca_table_builder = | 245 LocaTableBuilderPtr loca_table_builder = |
279 down_cast<LocaTable::Builder*>(font_builder->NewTableBuilder(Tag::loca)); | 246 down_cast<LocaTable::Builder*>(font_builder->NewTableBuilder(Tag::loca)); |
280 if (glyph_table_builder == NULL || loca_table_builder == NULL) { | 247 if (glyph_table_builder == NULL || loca_table_builder == NULL) { |
281 // Out of memory. | 248 // Out of memory. |
282 return NULL; | 249 return false; |
283 } | 250 } |
284 | 251 |
285 // Extract glyphs and setup loca list. | 252 // Extract glyphs and setup loca list. |
286 IntegerList loca_list; | 253 IntegerList loca_list; |
287 loca_list.resize(loca_table->num_glyphs()); | 254 loca_list.resize(loca_table->num_glyphs()); |
288 loca_list.push_back(0); | 255 loca_list.push_back(0); |
289 int32_t last_glyph_id = 0; | 256 int32_t last_glyph_id = 0; |
290 int32_t last_offset = 0; | 257 int32_t last_offset = 0; |
291 GlyphTable::GlyphBuilderList* glyph_builders = | 258 GlyphTable::GlyphBuilderList* glyph_builders = |
292 glyph_table_builder->GlyphBuilders(); | 259 glyph_table_builder->GlyphBuilders(); |
(...skipping 20 matching lines...) Expand all Loading... | |
313 } | 280 } |
314 last_offset += length; | 281 last_offset += length; |
315 loca_list[*i + 1] = last_offset; | 282 loca_list[*i + 1] = last_offset; |
316 last_glyph_id = *i; | 283 last_glyph_id = *i; |
317 } | 284 } |
318 for (int32_t j = last_glyph_id + 1; j <= loca_table->num_glyphs(); ++j) { | 285 for (int32_t j = last_glyph_id + 1; j <= loca_table->num_glyphs(); ++j) { |
319 loca_list[j] = last_offset; | 286 loca_list[j] = last_offset; |
320 } | 287 } |
321 loca_table_builder->SetLocaList(&loca_list); | 288 loca_table_builder->SetLocaList(&loca_list); |
322 | 289 |
290 return true; | |
291 } | |
292 | |
293 bool HasOverlap(int32_t range_begin, int32_t range_end, | |
294 const IntegerSet& glyph_ids) { | |
295 if (range_begin == range_end) { | |
296 return glyph_ids.find(range_begin) != glyph_ids.end(); | |
297 } else if (range_end > range_begin) { | |
298 IntegerSet::const_iterator left = glyph_ids.lower_bound(range_begin); | |
299 IntegerSet::const_iterator right = glyph_ids.lower_bound(range_end); | |
300 return right != left; | |
301 } | |
302 return false; | |
303 } | |
304 | |
305 // Initialize builder, returns false if glyph_id subset is not covered. | |
306 bool InitializeBitmapBuilder(EbdtTable::Builder* ebdt, EblcTable::Builder* eblc, | |
307 const IntegerSet& glyph_ids) { | |
308 EblcTableBuilderPtr eblc_builder = eblc; | |
309 EbdtTableBuilderPtr ebdt_builder = ebdt; | |
310 | |
311 BitmapLocaList loca_list; | |
312 BitmapSizeTableBuilderList* strikes = eblc_builder->BitmapSizeBuilders(); | |
313 | |
314 // Note: Do not call eblc_builder->GenerateLocaList(&loca_list) and then | |
315 // ebdt_builder->SetLoca(loca_list). For fonts like SimSun, there are | |
316 // >28K glyphs inside, where a typical usage will be <1K glyphs. Doing | |
317 // the calls improperly will result in creation of >100K objects that | |
318 // will be destroyed immediately, inducing significant slowness. | |
319 IntegerList removed_strikes; | |
320 for (size_t i = 0; i < strikes->size(); i++) { | |
321 if (!HasOverlap((*strikes)[i]->StartGlyphIndex(), | |
322 (*strikes)[i]->EndGlyphIndex(), glyph_ids)) { | |
323 removed_strikes.push_back(i); | |
324 continue; | |
325 } | |
326 | |
327 IndexSubTableBuilderList* index_builders = | |
328 (*strikes)[i]->IndexSubTableBuilders(); | |
329 IntegerList removed_indexes; | |
330 BitmapGlyphInfoMap info_map; | |
331 for (size_t j = 0; j < index_builders->size(); ++j) { | |
332 int32_t first_glyph_id = (*index_builders)[j]->first_glyph_index(); | |
333 int32_t last_glyph_id = (*index_builders)[j]->last_glyph_index(); | |
334 if (!HasOverlap(first_glyph_id, last_glyph_id, glyph_ids)) { | |
335 removed_indexes.push_back(j); | |
336 continue; | |
337 } | |
338 for (IntegerSet::const_iterator gid = glyph_ids.begin(), | |
339 gid_end = glyph_ids.end(); | |
340 gid != gid_end; gid++) { | |
341 if (*gid < first_glyph_id) { | |
342 continue; | |
343 } | |
344 if (*gid > last_glyph_id) { | |
345 break; | |
346 } | |
347 BitmapGlyphInfoPtr info; | |
348 info.Attach((*index_builders)[j]->GlyphInfo(*gid)); | |
349 if (info && info->length()) { // Do not include gid without bitmap | |
350 info_map[*gid] = info; | |
351 } | |
352 } | |
353 } | |
354 if (!info_map.empty()) { | |
355 loca_list.push_back(info_map); | |
356 } else { | |
357 removed_strikes.push_back(i); // Detected null entries. | |
358 } | |
359 | |
360 // Remove unused index sub tables | |
361 for (IntegerList::reverse_iterator j = removed_indexes.rbegin(), | |
362 e = removed_indexes.rend(); | |
363 j != e; j++) { | |
364 index_builders->erase(index_builders->begin() + *j); | |
365 } | |
366 } | |
367 if (removed_strikes.size() == strikes->size() || loca_list.empty()) { | |
368 return false; | |
369 } | |
370 | |
371 for (IntegerList::reverse_iterator i = removed_strikes.rbegin(), | |
372 e = removed_strikes.rend(); i != e; i++) { | |
373 strikes->erase(strikes->begin() + *i); | |
374 } | |
375 | |
376 if (strikes->empty()) { // no glyph covered, can safely drop the builders. | |
377 return false; | |
378 } | |
379 | |
380 ebdt_builder->SetLoca(&loca_list); | |
381 ebdt_builder->GlyphBuilders(); // Initialize the builder. | |
382 return true; | |
383 } | |
384 | |
385 void CopyBigGlyphMetrics(BigGlyphMetrics::Builder* source, | |
386 BigGlyphMetrics::Builder* target) { | |
387 target->SetHeight(static_cast<byte_t>(source->Height())); | |
388 target->SetWidth(static_cast<byte_t>(source->Width())); | |
389 target->SetHoriBearingX(static_cast<byte_t>(source->HoriBearingX())); | |
390 target->SetHoriBearingY(static_cast<byte_t>(source->HoriBearingY())); | |
391 target->SetHoriAdvance(static_cast<byte_t>(source->HoriAdvance())); | |
392 target->SetVertBearingX(static_cast<byte_t>(source->VertBearingX())); | |
393 target->SetVertBearingY(static_cast<byte_t>(source->VertBearingY())); | |
394 target->SetVertAdvance(static_cast<byte_t>(source->VertAdvance())); | |
395 } | |
396 | |
397 CALLER_ATTACH IndexSubTable::Builder* | |
398 ConstructIndexFormat4(IndexSubTable::Builder* b, const BitmapGlyphInfoMap& loca, | |
399 int32_t* image_data_offset) { | |
400 IndexSubTableFormat4BuilderPtr builder4; | |
401 builder4.Attach(IndexSubTableFormat4::Builder::CreateBuilder()); | |
402 CodeOffsetPairBuilderList offset_pairs; | |
403 | |
404 size_t offset = 0; | |
405 int32_t lower_bound = b->first_glyph_index(); | |
406 int32_t upper_bound = b->last_glyph_index(); | |
407 bool lower_bound_reached = false; | |
408 bool upper_bound_reached = false; | |
409 int32_t last_gid = -1; | |
410 for (BitmapGlyphInfoMap::const_iterator i = loca.lower_bound(lower_bound), | |
411 e = loca.end(); | |
412 i != e && !upper_bound_reached; i++) { | |
413 int32_t gid = i->first; | |
414 if (!lower_bound_reached) { | |
vandebo (ex-Chrome)
2011/12/01 21:50:28
You can pull this above the loop.
arthurhsu
2011/12/02 00:06:47
Done.
| |
415 builder4->set_first_glyph_index(gid); | |
416 builder4->set_image_format(b->image_format()); | |
417 builder4->set_image_data_offset(*image_data_offset); | |
418 last_gid = gid; | |
419 lower_bound_reached = true; | |
420 } | |
421 if (gid > upper_bound) { | |
422 upper_bound_reached = true; | |
vandebo (ex-Chrome)
2011/12/01 21:50:28
Change this to a break and remove the next conditi
arthurhsu
2011/12/02 00:06:47
Done.
| |
423 } | |
424 if (!upper_bound_reached) { | |
425 offset_pairs.push_back( | |
426 IndexSubTableFormat4::CodeOffsetPairBuilder(gid, offset)); | |
427 offset += i->second->length(); | |
428 last_gid = gid; | |
429 } | |
430 } | |
431 offset_pairs.push_back( | |
432 IndexSubTableFormat4::CodeOffsetPairBuilder(-1, offset)); | |
433 builder4->set_last_glyph_index(last_gid); | |
434 *image_data_offset += offset; | |
435 builder4->SetOffsetArray(offset_pairs); | |
436 | |
437 return builder4.Detach(); | |
438 } | |
439 | |
440 CALLER_ATTACH IndexSubTable::Builder* | |
441 ConstructIndexFormat5(IndexSubTable::Builder* b, const BitmapGlyphInfoMap& loca, | |
442 int32_t* image_data_offset) { | |
443 IndexSubTableFormat5BuilderPtr new_builder; | |
444 new_builder.Attach(IndexSubTableFormat5::Builder::CreateBuilder()); | |
445 | |
446 // Copy BigMetrics | |
447 int32_t image_size = 0; | |
448 if (b->index_format() == IndexSubTable::Format::FORMAT_2) { | |
449 IndexSubTableFormat2BuilderPtr builder2 = | |
450 down_cast<IndexSubTableFormat2::Builder*>(b); | |
451 CopyBigGlyphMetrics(builder2->BigMetrics(), new_builder->BigMetrics()); | |
452 image_size = builder2->ImageSize(); | |
453 } else { | |
454 IndexSubTableFormat5BuilderPtr builder5 = | |
455 down_cast<IndexSubTableFormat5::Builder*>(b); | |
456 BigGlyphMetricsBuilderPtr metrics_builder; | |
457 CopyBigGlyphMetrics(builder5->BigMetrics(), new_builder->BigMetrics()); | |
458 image_size = builder5->ImageSize(); | |
459 } | |
460 | |
461 IntegerList* glyph_array = new_builder->GlyphArray(); | |
462 size_t offset = 0; | |
463 int32_t lower_bound = b->first_glyph_index(); | |
464 int32_t upper_bound = b->last_glyph_index(); | |
465 bool lower_bound_reached = false; | |
466 bool upper_bound_reached = false; | |
467 int32_t last_gid = -1; | |
468 for (BitmapGlyphInfoMap::const_iterator i = loca.lower_bound(lower_bound), | |
469 e = loca.end(); | |
470 i != e && !upper_bound_reached; i++) { | |
471 int32_t gid = i->first; | |
472 if (!lower_bound_reached) { | |
vandebo (ex-Chrome)
2011/12/01 21:50:28
Same here.
arthurhsu
2011/12/02 00:06:47
Done.
| |
473 new_builder->set_first_glyph_index(gid); | |
474 new_builder->set_image_format(b->image_format()); | |
475 new_builder->set_image_data_offset(*image_data_offset); | |
476 new_builder->SetImageSize(image_size); | |
477 last_gid = gid; | |
478 lower_bound_reached = true; | |
479 } | |
480 if (gid > upper_bound) { | |
481 upper_bound_reached = true; | |
vandebo (ex-Chrome)
2011/12/01 21:50:28
And here.
arthurhsu
2011/12/02 00:06:47
Done.
| |
482 } | |
483 if (!upper_bound_reached) { | |
484 glyph_array->push_back(gid); | |
485 offset += i->second->length(); | |
486 last_gid = gid; | |
487 } | |
488 } | |
489 new_builder->set_last_glyph_index(last_gid); | |
490 *image_data_offset += offset; | |
491 return new_builder.Detach(); | |
492 } | |
493 | |
494 CALLER_ATTACH IndexSubTable::Builder* | |
495 SubsetIndexSubTable(IndexSubTable::Builder* builder, | |
496 const BitmapGlyphInfoMap& loca, | |
497 int32_t* image_data_offset) { | |
498 switch (builder->index_format()) { | |
499 case IndexSubTable::Format::FORMAT_1: | |
500 case IndexSubTable::Format::FORMAT_3: | |
501 case IndexSubTable::Format::FORMAT_4: | |
502 return ConstructIndexFormat4(builder, loca, image_data_offset); | |
503 case IndexSubTable::Format::FORMAT_2: | |
504 case IndexSubTable::Format::FORMAT_5: | |
505 return ConstructIndexFormat5(builder, loca, image_data_offset); | |
506 default: | |
507 assert(false); | |
508 break; | |
509 } | |
510 return NULL; | |
511 } | |
512 | |
513 void SubsetEBLC(EblcTable::Builder* eblc, const BitmapLocaList& new_loca) { | |
514 EblcTableBuilderPtr eblc_builder = eblc; | |
515 BitmapSizeTableBuilderList* size_builders = eblc->BitmapSizeBuilders(); | |
516 if (size_builders == NULL) { | |
517 return; | |
518 } | |
519 | |
520 int32_t image_data_offset = EbdtTable::Offset::kHeaderLength; | |
521 for (size_t strike = 0; strike < size_builders->size(); ++strike) { | |
522 IndexSubTableBuilderList* index_builders = | |
523 (*size_builders)[strike]->IndexSubTableBuilders(); | |
524 for (size_t index = 0; index < index_builders->size(); ++index) { | |
525 IndexSubTable::Builder* new_builder_raw = | |
526 SubsetIndexSubTable((*index_builders)[index], new_loca[strike], | |
527 &image_data_offset); | |
528 if (NULL != new_builder_raw) { | |
529 (*index_builders)[index].Attach(new_builder_raw); | |
530 } | |
531 } | |
532 } | |
533 } | |
534 | |
535 // EBLC structure (from stuartg) | |
536 // header | |
537 // bitmapSizeTable[] | |
538 // one per strike | |
539 // holds strike metrics - sbitLineMetrics | |
540 // holds info about indexSubTableArray | |
541 // indexSubTableArray[][] | |
542 // one per strike and then one per indexSubTable for that strike | |
543 // holds info about the indexSubTable | |
544 // the indexSubTable entries pointed to can be of different formats | |
545 // indexSubTable | |
546 // one per indexSubTableArray entry | |
547 // tells how to get the glyphs | |
548 // may hold the glyph metrics if they are uniform for all the glyphs in range | |
549 // | |
550 // There is nothing that says that the indexSubTableArray entries and/or the | |
551 // indexSubTable items need to be unique. They may be shared between strikes. | |
552 // | |
553 // EBDT structure: | |
554 // header | |
555 // glyphs | |
556 // amorphous blob of data | |
557 // different glyphs that are only able to be figured out from the EBLC table | |
558 // may hold metrics - depends on the EBLC entry that pointed to them | |
559 | |
560 // Subsetting EBLC table (from arthurhsu) | |
561 // Most pages use only a fraction (hundreds or less) glyphs out of a given font | |
562 // (which can have >20K glyphs for CJK). It's safe to assume that the subset | |
563 // font will have sparse bitmap glyphs. As a result, the EBLC table shall be | |
564 // reconstructed to either format 4 or 5. | |
565 | |
566 bool SetupBitmapBuilders(Font* font, Font::Builder* builder, | |
567 const IntegerSet& glyph_ids, bool use_ebdt) { | |
568 if (!font || !builder) { | |
569 return false; | |
570 } | |
571 | |
572 EbdtTablePtr ebdt_table = | |
573 down_cast<EbdtTable*>(font->GetTable(use_ebdt ? Tag::EBDT : Tag::bdat)); | |
574 EblcTablePtr eblc_table = | |
575 down_cast<EblcTable*>(font->GetTable(use_ebdt ? Tag::EBLC : Tag::bloc)); | |
576 | |
577 // If the bitmap table's size is too small, skip subsetting. | |
578 if (ebdt_table->DataLength() + eblc_table->DataLength() < | |
579 BITMAP_SIZE_THRESHOLD) { | |
580 return true; | |
581 } | |
582 | |
583 // Get the builders. | |
584 FontBuilderPtr font_builder = builder; | |
585 EbdtTableBuilderPtr ebdt_table_builder = down_cast<EbdtTable::Builder*>( | |
586 font_builder->NewTableBuilder(use_ebdt ? Tag::EBDT : Tag::bdat, | |
587 ebdt_table->ReadFontData())); | |
588 EblcTableBuilderPtr eblc_table_builder = down_cast<EblcTable::Builder*>( | |
589 font_builder->NewTableBuilder(use_ebdt ? Tag::EBLC : Tag::bloc, | |
590 eblc_table->ReadFontData())); | |
591 if (ebdt_table_builder == NULL || eblc_table_builder == NULL) { | |
592 // Out of memory. | |
593 return false; | |
594 } | |
595 | |
596 if (!InitializeBitmapBuilder(ebdt_table_builder, eblc_table_builder, glyph_ids )) { | |
597 // Bitmap tables do not cover the glyphs in our subset. | |
598 font_builder->RemoveTableBuilder(use_ebdt ? Tag::EBLC : Tag::bloc); | |
599 font_builder->RemoveTableBuilder(use_ebdt ? Tag::EBDT : Tag::bdat); | |
600 return false; | |
601 } | |
602 | |
603 BitmapLocaList new_loca; | |
604 ebdt_table_builder->GenerateLocaList(&new_loca); | |
605 SubsetEBLC(eblc_table_builder, new_loca); | |
606 | |
607 return true; | |
608 } | |
609 | |
610 enum BitmapDetection { | |
611 kNotFound, | |
612 kEBDTFound, | |
613 kOnlyBDATFound | |
614 }; | |
615 | |
616 // Some fonts have both EBDT/EBLC and bdat/bloc, we need only one set of them. | |
617 int DetectBitmapBuilders(Font* font) { | |
618 // Check if bitmap table exists. | |
619 EbdtTablePtr ebdt_table = down_cast<EbdtTable*>(font->GetTable(Tag::EBDT)); | |
620 EblcTablePtr eblc_table = down_cast<EblcTable*>(font->GetTable(Tag::EBLC)); | |
621 if (ebdt_table == NULL && eblc_table == NULL) { | |
622 // Check BDAT variants. | |
623 ebdt_table = down_cast<EbdtTable*>(font->GetTable(Tag::bdat)); | |
624 eblc_table = down_cast<EblcTable*>(font->GetTable(Tag::bloc)); | |
625 if (ebdt_table == NULL || eblc_table == NULL) { | |
626 // There's no bitmap tables. | |
627 return kNotFound; | |
628 } | |
629 return kOnlyBDATFound; | |
630 } | |
631 return kEBDTFound; | |
632 } | |
633 | |
634 SubsetterImpl::SubsetterImpl() { | |
635 } | |
636 | |
637 SubsetterImpl::~SubsetterImpl() { | |
638 } | |
639 | |
640 bool SubsetterImpl::LoadFont(const char* font_name, | |
641 const unsigned char* original_font, | |
642 size_t font_size) { | |
643 MemoryInputStream mis; | |
644 mis.Attach(original_font, font_size); | |
645 if (factory_ == NULL) { | |
646 factory_.Attach(FontFactory::GetInstance()); | |
647 } | |
648 | |
649 FontArray font_array; | |
650 factory_->LoadFonts(&mis, &font_array); | |
651 font_ = FindFont(font_name, font_array); | |
652 if (font_ == NULL) { | |
653 return false; | |
654 } | |
655 | |
656 return true; | |
657 } | |
658 | |
659 int SubsetterImpl::SubsetFont(const unsigned int* glyph_ids, | |
660 size_t glyph_count, | |
661 unsigned char** output_buffer) { | |
662 if (factory_ == NULL || font_ == NULL) { | |
663 return -1; | |
664 } | |
665 | |
666 // Find glyf and loca table. | |
667 GlyphTablePtr glyph_table = | |
668 down_cast<GlyphTable*>(font_->GetTable(Tag::glyf)); | |
669 LocaTablePtr loca_table = down_cast<LocaTable*>(font_->GetTable(Tag::loca)); | |
670 if (glyph_table == NULL || loca_table == NULL) { | |
671 // We are not able to subset the font. | |
672 return 0; | |
673 } | |
674 | |
675 IntegerSet glyph_id_processed; | |
676 if (!ResolveCompositeGlyphs(glyph_table, loca_table, | |
677 glyph_ids, glyph_count, &glyph_id_processed) || | |
678 glyph_id_processed.empty()) { | |
679 return 0; | |
680 } | |
681 | |
682 FontPtr new_font; | |
683 new_font.Attach(Subset(glyph_id_processed)); | |
684 if (new_font == NULL) { | |
685 return 0; | |
686 } | |
687 | |
688 MemoryOutputStream output_stream; | |
689 factory_->SerializeFont(new_font, &output_stream); | |
690 int length = static_cast<int>(output_stream.Size()); | |
691 if (length > 0) { | |
692 *output_buffer = new unsigned char[length]; | |
693 memcpy(*output_buffer, output_stream.Get(), length); | |
694 } | |
695 | |
696 return length; | |
697 } | |
698 | |
699 // Long comments regarding TTF tables and PDF (from stuartg) | |
700 // | |
701 // According to PDF spec (section 9.9), the following tables must present: | |
702 // head, hhea, loca, maxp, cvt, prep, glyf, hmtx, fpgm | |
703 // cmap if font is used as a TTF and not a CIDFont dict | |
704 // | |
705 // Other tables we need to keep for PDF rendering to support zoom in/out: | |
706 // bdat, bloc, ebdt, eblc, ebsc, gasp | |
707 // | |
708 // Special table: | |
709 // CFF - if you have this table then you shouldn't have a glyf table and this | |
710 // is the table with all the glyphs. Shall skip subsetting completely | |
711 // since sfntly is not capable of subsetting it for now. | |
712 // post - extra info here for printing on PostScript printers but maybe not | |
713 // enough to outweigh the space taken by the names | |
714 // | |
715 // Tables to break apart: | |
716 // name - could throw away all but one language and one platform strings/ might | |
717 // throw away some of the name entries | |
718 // cmap - could strip out non-needed cmap subtables | |
719 // - format 4 subtable can be subsetted as well using sfntly | |
720 // | |
721 // Graphite tables: | |
722 // silf, glat, gloc, feat - shall be okay to strip out | |
723 // | |
724 // Tables that can be discarded: | |
725 // OS/2 - everything here is for layout and description of the font that is | |
726 // elsewhere (some in the PDF objects) | |
727 // BASE, GDEF, GSUB, GPOS, JSTF - all used for layout | |
728 // kern - old style layout | |
729 // DSIG - this will be invalid after subsetting | |
730 // hdmx - layout | |
731 // PCLT - metadata that's not needed | |
732 // vmtx - layout | |
733 // vhea - layout | |
734 // VDMX | |
735 // VORG - not used by TT/OT - used by CFF | |
736 // hsty - would be surprised if you saw one of these - used on the Newton | |
737 // AAT tables - mort, morx, feat, acnt, bsin, just, lcar, fdsc, fmtx, prop, | |
738 // Zapf, opbd, trak, fvar, gvar, avar, cvar | |
739 // - these are all layout tables and once layout happens are not | |
740 // needed anymore | |
741 // LTSH - layout | |
742 | |
743 CALLER_ATTACH Font* SubsetterImpl::Subset(const IntegerSet& glyph_ids) { | |
744 // The const is initialized here to workaround VC bug of rendering all Tag::* | |
745 // as 0. These tags represents the TTF tables that we will embed in subset | |
746 // font. | |
747 const int32_t TABLES_IN_SUBSET[] = { | |
748 Tag::head, Tag::hhea, Tag::loca, Tag::maxp, Tag::cvt, | |
749 Tag::prep, Tag::glyf, Tag::hmtx, Tag::fpgm, Tag::EBDT, | |
750 Tag::EBLC, Tag::EBSC, Tag::bdat, Tag::bloc, Tag::bhed, | |
751 Tag::cmap, // Keep here for future tagged PDF development. | |
752 Tag::name, // Keep here due to legal concerns: copyright info inside. | |
753 }; | |
754 | |
755 // Setup font builders we need. | |
756 FontBuilderPtr font_builder; | |
757 font_builder.Attach(factory_->NewFontBuilder()); | |
758 IntegerSet remove_tags; | |
759 | |
760 GlyphTablePtr glyph_table = | |
761 down_cast<GlyphTable*>(font_->GetTable(Tag::glyf)); | |
762 LocaTablePtr loca_table = down_cast<LocaTable*>(font_->GetTable(Tag::loca)); | |
763 | |
764 if (SetupGlyfBuilders(font_builder, glyph_table, loca_table, glyph_ids)) { | |
765 remove_tags.insert(Tag::glyf); | |
766 remove_tags.insert(Tag::loca); | |
767 } | |
768 | |
769 int bitmap_table_type = DetectBitmapBuilders(font_); | |
770 if (bitmap_table_type != kNotFound) { | |
771 bool use_ebdt = (bitmap_table_type == kEBDTFound); | |
772 bool subset_success = | |
773 SetupBitmapBuilders(font_, font_builder, glyph_ids, use_ebdt); | |
vandebo (ex-Chrome)
2011/12/01 21:50:28
It seems like combining SetupBitmapBuilders and De
arthurhsu
2011/12/02 00:06:47
Done.
| |
774 | |
775 if (use_ebdt || !subset_success) { | |
776 remove_tags.insert(Tag::bdat); | |
777 remove_tags.insert(Tag::bloc); | |
778 remove_tags.insert(Tag::bhed); | |
779 } | |
780 if (use_ebdt && !subset_success) { | |
781 remove_tags.insert(Tag::EBDT); | |
782 remove_tags.insert(Tag::EBLC); | |
783 remove_tags.insert(Tag::EBSC); | |
784 } | |
785 } | |
786 | |
787 IntegerSet allowed_tags; | |
788 for (size_t i = 0; i < sizeof(TABLES_IN_SUBSET) / sizeof(int32_t); ++i) { | |
789 allowed_tags.insert(TABLES_IN_SUBSET[i]); | |
790 } | |
791 | |
792 IntegerSet result; | |
793 std::set_difference(allowed_tags.begin(), allowed_tags.end(), | |
794 remove_tags.begin(), remove_tags.end(), | |
795 std::inserter(result, result.end())); | |
796 allowed_tags = result; | |
797 | |
323 // Setup remaining builders. | 798 // Setup remaining builders. |
324 for (TableMap::const_iterator i = font_->GetTableMap()->begin(), | 799 for (IntegerSet::iterator i = allowed_tags.begin(), e = allowed_tags.end(); |
325 e = font_->GetTableMap()->end(); i != e; ++i) { | 800 i != e; ++i) { |
326 // We already build the builder for glyph and loca. | 801 Table* table = font_->GetTable(*i); |
327 if (i->first != Tag::glyf && i->first != Tag::loca) { | 802 if (table) { |
328 font_builder->NewTableBuilder(i->first, i->second->ReadFontData()); | 803 font_builder->NewTableBuilder(*i, table->ReadFontData()); |
329 } | 804 } |
330 } | 805 } |
331 | 806 |
332 return font_builder->Build(); | 807 return font_builder->Build(); |
333 } | 808 } |
334 | 809 |
335 } // namespace sfntly | 810 } // namespace sfntly |
OLD | NEW |