normalize.cc (9114B)
1 /* Copyright 2013 Google Inc. All Rights Reserved. 2 3 Distributed under MIT license. 4 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 */ 6 7 /* Glyph normalization */ 8 9 #include "./normalize.h" 10 11 #include <inttypes.h> 12 #include <stddef.h> 13 14 #include "./buffer.h" 15 #include "./port.h" 16 #include "./font.h" 17 #include "./glyph.h" 18 #include "./round.h" 19 #include "./store_bytes.h" 20 #include "./table_tags.h" 21 #include "./woff2_common.h" 22 23 namespace woff2 { 24 25 namespace { 26 27 void StoreLoca(int index_fmt, uint32_t value, size_t* offset, uint8_t* dst) { 28 if (index_fmt == 0) { 29 Store16(value >> 1, offset, dst); 30 } else { 31 StoreU32(value, offset, dst); 32 } 33 } 34 35 } // namespace 36 37 namespace { 38 39 bool WriteNormalizedLoca(int index_fmt, int num_glyphs, Font* font) { 40 Font::Table* glyf_table = font->FindTable(kGlyfTableTag); 41 Font::Table* loca_table = font->FindTable(kLocaTableTag); 42 43 int glyph_sz = index_fmt == 0 ? 2 : 4; 44 loca_table->buffer.resize(Round4(num_glyphs + 1) * glyph_sz); 45 loca_table->length = (num_glyphs + 1) * glyph_sz; 46 47 uint8_t* glyf_dst = num_glyphs ? &glyf_table->buffer[0] : NULL; 48 uint8_t* loca_dst = &loca_table->buffer[0]; 49 uint32_t glyf_offset = 0; 50 size_t loca_offset = 0; 51 52 for (int i = 0; i < num_glyphs; ++i) { 53 StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst); 54 Glyph glyph; 55 const uint8_t* glyph_data; 56 size_t glyph_size; 57 if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) || 58 (glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) { 59 return FONT_COMPRESSION_FAILURE(); 60 } 61 size_t glyf_dst_size = glyf_table->buffer.size() - glyf_offset; 62 if (!StoreGlyph(glyph, glyf_dst + glyf_offset, &glyf_dst_size)) { 63 return FONT_COMPRESSION_FAILURE(); 64 } 65 glyf_dst_size = Round4(glyf_dst_size); 66 if (glyf_dst_size > std::numeric_limits<uint32_t>::max() || 67 glyf_offset + static_cast<uint32_t>(glyf_dst_size) < glyf_offset || 68 (index_fmt == 0 && glyf_offset + glyf_dst_size >= (1UL << 17))) { 69 return FONT_COMPRESSION_FAILURE(); 70 } 71 glyf_offset += glyf_dst_size; 72 } 73 74 StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst); 75 76 glyf_table->buffer.resize(glyf_offset); 77 glyf_table->data = glyf_offset ? &glyf_table->buffer[0] : NULL; 78 glyf_table->length = glyf_offset; 79 loca_table->data = loca_offset ? &loca_table->buffer[0] : NULL; 80 81 return true; 82 } 83 84 } // namespace 85 86 namespace { 87 88 bool MakeEditableBuffer(Font* font, int tableTag) { 89 Font::Table* table = font->FindTable(tableTag); 90 if (table == NULL) { 91 return FONT_COMPRESSION_FAILURE(); 92 } 93 if (table->IsReused()) { 94 return true; 95 } 96 int sz = Round4(table->length); 97 table->buffer.resize(sz); 98 uint8_t* buf = &table->buffer[0]; 99 memcpy(buf, table->data, table->length); 100 if (PREDICT_FALSE(sz > table->length)) { 101 memset(buf + table->length, 0, sz - table->length); 102 } 103 table->data = buf; 104 return true; 105 } 106 107 } // namespace 108 109 bool NormalizeGlyphs(Font* font) { 110 Font::Table* head_table = font->FindTable(kHeadTableTag); 111 Font::Table* glyf_table = font->FindTable(kGlyfTableTag); 112 Font::Table* loca_table = font->FindTable(kLocaTableTag); 113 if (head_table == NULL) { 114 return FONT_COMPRESSION_FAILURE(); 115 } 116 // If you don't have glyf/loca this transform isn't very interesting 117 if (loca_table == NULL && glyf_table == NULL) { 118 return true; 119 } 120 // It would be best if you didn't have just one of glyf/loca 121 if ((glyf_table == NULL) != (loca_table == NULL)) { 122 return FONT_COMPRESSION_FAILURE(); 123 } 124 // Must share neither or both loca & glyf 125 if (loca_table->IsReused() != glyf_table->IsReused()) { 126 return FONT_COMPRESSION_FAILURE(); 127 } 128 if (loca_table->IsReused()) { 129 return true; 130 } 131 132 int index_fmt = head_table->data[51]; 133 int num_glyphs = NumGlyphs(*font); 134 135 // We need to allocate a bit more than its original length for the normalized 136 // glyf table, since it can happen that the glyphs in the original table are 137 // 2-byte aligned, while in the normalized table they are 4-byte aligned. 138 // That gives a maximum of 2 bytes increase per glyph. However, there is no 139 // theoretical guarantee that the total size of the flags plus the coordinates 140 // is the smallest possible in the normalized version, so we have to allow 141 // some general overhead. 142 // TODO(user) Figure out some more precise upper bound on the size of 143 // the overhead. 144 size_t max_normalized_glyf_size = 1.1 * glyf_table->length + 2 * num_glyphs; 145 146 glyf_table->buffer.resize(max_normalized_glyf_size); 147 148 // if we can't write a loca using short's (index_fmt 0) 149 // try again using longs (index_fmt 1) 150 if (!WriteNormalizedLoca(index_fmt, num_glyphs, font)) { 151 if (index_fmt != 0) { 152 return FONT_COMPRESSION_FAILURE(); 153 } 154 155 // Rewrite loca with 4-byte entries & update head to match 156 index_fmt = 1; 157 if (!WriteNormalizedLoca(index_fmt, num_glyphs, font)) { 158 return FONT_COMPRESSION_FAILURE(); 159 } 160 head_table->buffer[51] = 1; 161 } 162 163 return true; 164 } 165 166 bool NormalizeOffsets(Font* font) { 167 uint32_t offset = 12 + 16 * font->num_tables; 168 for (auto tag : font->OutputOrderedTags()) { 169 auto& table = font->tables[tag]; 170 table.offset = offset; 171 offset += Round4(table.length); 172 } 173 return true; 174 } 175 176 namespace { 177 178 uint32_t ComputeHeaderChecksum(const Font& font) { 179 uint32_t checksum = font.flavor; 180 uint16_t max_pow2 = font.num_tables ? Log2Floor(font.num_tables) : 0; 181 uint16_t search_range = max_pow2 ? 1 << (max_pow2 + 4) : 0; 182 uint16_t range_shift = (font.num_tables << 4) - search_range; 183 checksum += (font.num_tables << 16 | search_range); 184 checksum += (max_pow2 << 16 | range_shift); 185 for (const auto& i : font.tables) { 186 const Font::Table* table = &i.second; 187 if (table->IsReused()) { 188 table = table->reuse_of; 189 } 190 checksum += table->tag; 191 checksum += table->checksum; 192 checksum += table->offset; 193 checksum += table->length; 194 } 195 return checksum; 196 } 197 198 } // namespace 199 200 bool FixChecksums(Font* font) { 201 Font::Table* head_table = font->FindTable(kHeadTableTag); 202 if (head_table == NULL) { 203 return FONT_COMPRESSION_FAILURE(); 204 } 205 if (head_table->reuse_of != NULL) { 206 head_table = head_table->reuse_of; 207 } 208 if (head_table->length < 12) { 209 return FONT_COMPRESSION_FAILURE(); 210 } 211 212 uint8_t* head_buf = &head_table->buffer[0]; 213 size_t offset = 8; 214 StoreU32(0, &offset, head_buf); 215 uint32_t file_checksum = 0; 216 uint32_t head_checksum = 0; 217 for (auto& i : font->tables) { 218 Font::Table* table = &i.second; 219 if (table->IsReused()) { 220 table = table->reuse_of; 221 } 222 table->checksum = ComputeULongSum(table->data, table->length); 223 file_checksum += table->checksum; 224 225 if (table->tag == kHeadTableTag) { 226 head_checksum = table->checksum; 227 } 228 } 229 230 file_checksum += ComputeHeaderChecksum(*font); 231 offset = 8; 232 StoreU32(0xb1b0afba - file_checksum, &offset, head_buf); 233 234 return true; 235 } 236 237 namespace { 238 bool MarkTransformed(Font* font) { 239 Font::Table* head_table = font->FindTable(kHeadTableTag); 240 if (head_table == NULL) { 241 return FONT_COMPRESSION_FAILURE(); 242 } 243 if (head_table->reuse_of != NULL) { 244 head_table = head_table->reuse_of; 245 } 246 if (head_table->length < 17) { 247 return FONT_COMPRESSION_FAILURE(); 248 } 249 // set bit 11 of head table 'flags' to indicate that font has undergone 250 // lossless modifying transform 251 int head_flags = head_table->data[16]; 252 head_table->buffer[16] = head_flags | 0x08; 253 return true; 254 } 255 } // namespace 256 257 258 bool NormalizeWithoutFixingChecksums(Font* font) { 259 return (MakeEditableBuffer(font, kHeadTableTag) && 260 RemoveDigitalSignature(font) && 261 MarkTransformed(font) && 262 NormalizeGlyphs(font) && 263 NormalizeOffsets(font)); 264 } 265 266 bool NormalizeFont(Font* font) { 267 return (NormalizeWithoutFixingChecksums(font) && 268 FixChecksums(font)); 269 } 270 271 bool NormalizeFontCollection(FontCollection* font_collection) { 272 if (font_collection->fonts.size() == 1) { 273 return NormalizeFont(&font_collection->fonts[0]); 274 } 275 276 uint32_t offset = CollectionHeaderSize(font_collection->header_version, 277 font_collection->fonts.size()); 278 for (auto& font : font_collection->fonts) { 279 if (!NormalizeWithoutFixingChecksums(&font)) { 280 #ifdef FONT_COMPRESSION_BIN 281 fprintf(stderr, "Font normalization failed.\n"); 282 #endif 283 return FONT_COMPRESSION_FAILURE(); 284 } 285 offset += kSfntHeaderSize + kSfntEntrySize * font.num_tables; 286 } 287 288 // Start table offsets after TTC Header and Sfnt Headers 289 for (auto& font : font_collection->fonts) { 290 for (auto tag : font.OutputOrderedTags()) { 291 Font::Table& table = font.tables[tag]; 292 if (table.IsReused()) { 293 table.offset = table.reuse_of->offset; 294 } else { 295 table.offset = offset; 296 offset += Round4(table.length); 297 } 298 } 299 } 300 301 // Now we can fix the checksums 302 for (auto& font : font_collection->fonts) { 303 if (!FixChecksums(&font)) { 304 #ifdef FONT_COMPRESSION_BIN 305 fprintf(stderr, "Failed to fix checksums\n"); 306 #endif 307 return FONT_COMPRESSION_FAILURE(); 308 } 309 } 310 311 return true; 312 } 313 314 } // namespace woff2