Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(88)

Side by Side Diff: third_party/sfntly/src/subsetter/subsetter_impl.cc

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

Powered by Google App Engine
This is Rietveld 408576698