Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 <string.h> | |
| 18 | |
| 19 // Must include this file before unistr.h to avoid compilation issue in VS2008. | |
| 20 #include "sfntly/port/type.h" | |
| 21 | |
| 22 #include <unicode/ustring.h> | |
| 23 #include <unicode/unistr.h> | |
| 24 | |
| 25 #include <map> | |
| 26 #include <set> | |
| 27 | |
| 28 #include "subsetter_impl.h" | |
| 29 | |
| 30 #include "sfntly/glyph_table.h" | |
| 31 #include "sfntly/loca_table.h" | |
| 32 #include "sfntly/name_table.h" | |
| 33 #include "sfntly/tag.h" | |
| 34 #include "sfntly/data/memory_byte_array.h" | |
| 35 #include "sfntly/port/memory_output_stream.h" | |
| 36 | |
| 37 #if defined U_USING_ICU_NAMESPACE | |
| 38 U_NAMESPACE_USE | |
| 39 #endif | |
| 40 | |
| 41 namespace sfntly { | |
| 42 | |
| 43 void constructName(UChar* name_part, UnicodeString* name, int32_t name_id) { | |
| 44 switch(name_id) { | |
| 45 case NameId::kFullFontName: | |
| 46 *name = name_part; | |
| 47 break; | |
| 48 case NameId::kFontFamilyName: | |
| 49 case NameId::kPreferredFamily: | |
| 50 case NameId::kWWSFamilyName: { | |
| 51 UnicodeString original = *name; | |
| 52 *name = name_part; | |
| 53 *name += original; | |
| 54 } | |
| 55 break; | |
| 56 case NameId::kFontSubfamilyName: | |
| 57 case NameId::kPreferredSubfamily: | |
| 58 case NameId::kWWSSubfamilyName: | |
| 59 *name += name_part; | |
| 60 break; | |
| 61 default: | |
| 62 break; | |
| 63 } | |
| 64 } | |
| 65 | |
| 66 int32_t hashCode(int32_t platform_id, int32_t encoding_id, int32_t language_id, | |
| 67 int32_t name_id) { | |
| 68 int32_t result = platform_id << 24 | encoding_id << 16 | language_id << 8; | |
| 69 if (name_id == NameId::kFullFontName) { | |
| 70 result |= 0xff; | |
| 71 } else if (name_id == NameId::kPreferredFamily || | |
| 72 name_id == NameId::kPreferredSubfamily) { | |
| 73 result |= 0xf; | |
| 74 } else if (name_id == NameId::kWWSFamilyName || | |
| 75 name_id == NameId::kWWSSubfamilyName) { | |
| 76 result |= 1; | |
| 77 } | |
| 78 return result; | |
| 79 } | |
| 80 | |
| 81 SubsetterImpl::SubsetterImpl() { | |
| 82 } | |
| 83 | |
| 84 SubsetterImpl::~SubsetterImpl() { | |
| 85 } | |
| 86 | |
| 87 bool SubsetterImpl::loadFont(const char* font_name, | |
| 88 const unsigned char* original_font, | |
|
brettw
2011/07/19 05:09:42
Be sure these are aligned.
arthurhsu
2011/07/19 18:34:32
Done.
| |
| 89 const size_t font_size) { | |
| 90 ByteArrayPtr raw_font = | |
| 91 new MemoryByteArray((byte_t*)original_font, font_size); | |
| 92 if (factory_ == NULL) { | |
| 93 factory_.attach(FontFactory::getInstance()); | |
| 94 } | |
| 95 | |
| 96 FontArray font_array; | |
| 97 factory_->loadFonts(raw_font, &font_array); | |
| 98 font_ = findFont(font_name, font_array); | |
| 99 if (font_ == NULL) { | |
| 100 return false; | |
| 101 } | |
| 102 | |
| 103 return true; | |
| 104 } | |
| 105 | |
| 106 int SubsetterImpl::subsetFont(const unsigned int* glyph_ids, | |
| 107 const size_t glyph_count, | |
| 108 unsigned char** output_buffer) { | |
| 109 if (factory_ == NULL || font_ == NULL) { | |
| 110 return -1; | |
| 111 } | |
| 112 | |
| 113 FontPtr new_font; | |
|
brettw
2011/07/19 05:09:42
Can you avoid the "Ptr" things and just write Font
arthurhsu
2011/07/19 18:34:32
The Ptr is a smart pointer class that acts like CC
| |
| 114 new_font.attach(subset(glyph_ids, glyph_count)); | |
| 115 if (new_font == NULL) { | |
| 116 return 0; | |
| 117 } | |
| 118 | |
| 119 MemoryOutputStream output_stream; | |
| 120 factory_->serializeFont(new_font, &output_stream); | |
| 121 int length = (int)output_stream.size(); | |
|
brettw
2011/07/19 05:09:42
C++ casts. Same in a few other places in this file
arthurhsu
2011/07/19 18:34:32
Done.
| |
| 122 if (length > 0) { | |
| 123 *output_buffer = new unsigned char[length]; | |
| 124 memcpy(*output_buffer, output_stream.get(), length); | |
| 125 } | |
| 126 | |
| 127 return length; | |
| 128 } | |
| 129 | |
| 130 Font* SubsetterImpl::findFont(const char* font_name, FontArray& font_array) { | |
|
brettw
2011/07/19 05:09:42
If the FontArray is an input parameter, it should
arthurhsu
2011/07/19 18:34:32
Done.
| |
| 131 if (font_array.empty() || font_array[0] == NULL) { | |
| 132 return NULL; | |
| 133 } | |
| 134 | |
| 135 if (font_name && strlen(font_name)) { | |
| 136 for (FontArray::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); | |
| 149 if (font_string.isEmpty()) | |
| 150 return false; | |
| 151 UnicodeString regular_suffix = UnicodeString::fromUTF8(" Regular"); | |
| 152 UnicodeString alt_font_string = font_string; | |
| 153 alt_font_string += regular_suffix; | |
| 154 | |
| 155 typedef std::map<int32_t, UnicodeString> NameMap; | |
| 156 NameMap names; | |
| 157 NameTablePtr name_table = down_cast<NameTable*>(font->table(Tag::name)); | |
| 158 | |
| 159 for (int32_t i = 0; i < name_table->nameCount(); ++i) { | |
| 160 switch(name_table->nameId(i)) { | |
| 161 case NameId::kFontFamilyName: | |
| 162 case NameId::kFontSubfamilyName: | |
| 163 case NameId::kFullFontName: | |
| 164 case NameId::kPreferredFamily: | |
| 165 case NameId::kPreferredSubfamily: | |
| 166 case NameId::kWWSFamilyName: | |
| 167 case NameId::kWWSSubfamilyName: { | |
| 168 int32_t hash_code = hashCode(name_table->platformId(i), | |
|
brettw
2011/07/19 05:09:42
This should only be indented 2 spaces, and put the
arthurhsu
2011/07/19 18:34:32
Done.
| |
| 169 name_table->encodingId(i), | |
| 170 name_table->languageId(i), | |
| 171 name_table->nameId(i)); | |
| 172 UChar* name_part = name_table->name(i); | |
| 173 constructName(name_part, &(names[hash_code]), name_table->nameId(i)); | |
| 174 } | |
| 175 break; | |
| 176 default: | |
| 177 break; | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 if (!names.empty()) { | |
| 182 for (NameMap::iterator b = names.begin(), e = names.end(); b != e; ++b) { | |
| 183 if (b->second.caseCompare(font_string, 0) == 0 || | |
| 184 b->second.caseCompare(alt_font_string, 0) == 0) { | |
| 185 return true; | |
| 186 } | |
| 187 } | |
| 188 } | |
| 189 return false; | |
| 190 } | |
| 191 | |
| 192 CALLER_ATTACH Font* SubsetterImpl::subset(const unsigned int* glyph_ids, | |
| 193 const size_t glyph_count) { | |
| 194 if (glyph_ids == NULL || glyph_count == 0) { | |
| 195 return NULL; | |
| 196 } | |
| 197 | |
| 198 // Find glyf and loca table. | |
| 199 GlyphTablePtr glyph_table = down_cast<GlyphTable*>(font_->table(Tag::glyf)); | |
| 200 LocaTablePtr loca_table = down_cast<LocaTable*>(font_->table(Tag::loca)); | |
| 201 if (glyph_table == NULL || loca_table == NULL) { | |
| 202 // The font is invalid. | |
| 203 return NULL; | |
| 204 } | |
| 205 | |
| 206 // Setup font builders we need. | |
| 207 FontBuilderPtr font_builder; | |
| 208 font_builder.attach(factory_->newFontBuilder()); | |
| 209 | |
| 210 GlyphTableBuilderPtr glyph_table_builder; | |
| 211 glyph_table_builder.attach(down_cast<GlyphTable::Builder*>( | |
| 212 font_builder->newTableBuilder(Tag::glyf))); | |
|
brettw
2011/07/19 05:09:42
These are indented 5 spaces.
arthurhsu
2011/07/19 18:34:32
Done.
| |
| 213 LocaTableBuilderPtr loca_table_builder; | |
| 214 loca_table_builder.attach(down_cast<LocaTable::Builder*>( | |
| 215 font_builder->newTableBuilder(Tag::loca))); | |
| 216 if (glyph_table_builder == NULL || loca_table_builder == NULL) { | |
| 217 // Out of memory. | |
| 218 return NULL; | |
| 219 } | |
| 220 | |
| 221 // Sort and uniquify glyph ids. | |
| 222 IntegerSet glyph_id_set; | |
| 223 glyph_id_set.insert(0); // always include glyph id 0 | |
|
brettw
2011/07/19 05:09:42
Be sure all the comments begin with uppercase and
arthurhsu
2011/07/19 18:34:32
Done.
| |
| 224 for (size_t i = 0; i < glyph_count; ++i) { | |
| 225 glyph_id_set.insert(glyph_ids[i]); | |
| 226 } | |
| 227 | |
| 228 // Extract glyphs and setup loca list. | |
| 229 IntegerList loca_list; | |
| 230 loca_list.resize(loca_table->numLocas()); | |
| 231 loca_list.push_back(0); | |
| 232 int32_t last_glyph_id = 0; | |
| 233 int32_t last_offset = 0; | |
| 234 GlyphTable::GlyphBuilderList* glyph_builders = | |
| 235 glyph_table_builder->glyphBuilders(); | |
| 236 for (IntegerSet::iterator i = glyph_id_set.begin(), e = glyph_id_set.end(); | |
| 237 i != e; ++i) { | |
| 238 if (*i < 0 || *i >= loca_table->numGlyphs()) { | |
| 239 // Invalid glyph id, ignore. | |
| 240 continue; | |
| 241 } | |
| 242 | |
| 243 int32_t length = loca_table->glyphLength(*i); | |
| 244 if (length == 0) { | |
| 245 // Empty glyph, ignore. | |
| 246 continue; | |
| 247 } | |
| 248 int32_t offset = loca_table->glyphOffset(*i); | |
| 249 | |
| 250 GlyphPtr glyph; | |
|
brettw
2011/07/19 05:09:42
Ditto with no "Ptr"
| |
| 251 glyph.attach(glyph_table->glyph(offset, length)); | |
| 252 if (glyph == NULL) { | |
| 253 // Error finding glyph, ignore. | |
| 254 continue; | |
| 255 } | |
| 256 | |
| 257 // Add glyph to new glyf table. | |
| 258 ReadableFontDataPtr data = glyph->readFontData(); | |
| 259 WritableFontDataPtr copy_data; | |
| 260 copy_data.attach(font_builder->getNewData(data->length())); | |
| 261 data->copyTo(copy_data); | |
| 262 GlyphBuilderPtr glyph_builder; | |
| 263 glyph_builder.attach(glyph_table_builder->glyphBuilder(copy_data)); | |
| 264 glyph_builders->push_back(glyph_builder); | |
| 265 | |
| 266 // Configure loca list. | |
| 267 for (int32_t j = last_glyph_id + 1; j <= *i; ++j) { | |
| 268 loca_list[j] = last_offset; | |
| 269 } | |
| 270 last_offset = offset; | |
| 271 loca_list[*i + 1] = offset; | |
| 272 last_glyph_id = *i; | |
| 273 } | |
| 274 for (int32_t j = last_glyph_id + 1; j <= loca_table->numGlyphs(); ++j) { | |
| 275 loca_list[j] = last_offset; | |
| 276 } | |
| 277 loca_table_builder->setLocaList(&loca_list); | |
| 278 | |
| 279 // Setup remaining builders. | |
| 280 for (TableMap::iterator i = font_->tables()->begin(), | |
| 281 e = font_->tables()->end(); i != e; ++i) { | |
| 282 // We already build the builder for glyph and loca. | |
| 283 if (i->first != Tag::glyf && i->first != Tag::loca) { | |
| 284 // The newTableBuilder() call will alter internal state of font_builder | |
| 285 // AND the reference count of returned object. Therefore we need to | |
| 286 // dereference it. | |
| 287 TableBuilderPtr dereference; | |
| 288 dereference.attach( | |
| 289 font_builder->newTableBuilder(i->first, i->second->readFontData())); | |
| 290 } | |
| 291 } | |
| 292 | |
| 293 return font_builder->build(); | |
| 294 } | |
| 295 | |
| 296 } // namespace sfntly | |
| OLD | NEW |