1 // Copyright 2013 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // Glyph normalization 16 17 #include "./normalize.h" 18 19 #include <inttypes.h> 20 #include <stddef.h> 21 22 #include "./buffer.h" 23 #include "./port.h" 24 #include "./font.h" 25 #include "./glyph.h" 26 #include "./round.h" 27 #include "./store_bytes.h" 28 #include "./table_tags.h" 29 30 namespace woff2 { 31 32 namespace { 33 34 void StoreLoca(int index_fmt, uint32_t value, size_t* offset, uint8_t* dst) { 35 if (index_fmt == 0) { 36 Store16(value >> 1, offset, dst); 37 } else { 38 StoreU32(value, offset, dst); 39 } 40 } 41 42 void NormalizeSimpleGlyphBoundingBox(Glyph* glyph) { 43 if (glyph->contours.empty() || glyph->contours[0].empty()) { 44 return; 45 } 46 int16_t x_min = glyph->contours[0][0].x; 47 int16_t y_min = glyph->contours[0][0].y; 48 int16_t x_max = x_min; 49 int16_t y_max = y_min; 50 for (const auto& contour : glyph->contours) { 51 for (const auto& point : contour) { 52 if (point.x < x_min) x_min = point.x; 53 if (point.x > x_max) x_max = point.x; 54 if (point.y < y_min) y_min = point.y; 55 if (point.y > y_max) y_max = point.y; 56 } 57 } 58 glyph->x_min = x_min; 59 glyph->y_min = y_min; 60 glyph->x_max = x_max; 61 glyph->y_max = y_max; 62 } 63 64 } // namespace 65 66 bool NormalizeGlyphs(Font* font) { 67 Font::Table* head_table = font->FindTable(kHeadTableTag); 68 Font::Table* glyf_table = font->FindTable(kGlyfTableTag); 69 Font::Table* loca_table = font->FindTable(kLocaTableTag); 70 if (head_table == NULL || loca_table == NULL || glyf_table == NULL) { 71 return FONT_COMPRESSION_FAILURE(); 72 } 73 int index_fmt = head_table->data[51]; 74 int num_glyphs = NumGlyphs(*font); 75 76 // We need to allocate a bit more than its original length for the normalized 77 // glyf table, since it can happen that the glyphs in the original table are 78 // 2-byte aligned, while in the normalized table they are 4-byte aligned. 79 // That gives a maximum of 2 bytes increase per glyph. However, there is no 80 // theoretical guarantee that the total size of the flags plus the coordinates 81 // is the smallest possible in the normalized version, so we have to allow 82 // some general overhead. 83 // TODO(user) Figure out some more precise upper bound on the size of 84 // the overhead. 85 size_t max_normalized_glyf_size = 1.1 * glyf_table->length + 2 * num_glyphs; 86 87 glyf_table->buffer.resize(max_normalized_glyf_size); 88 loca_table->buffer.resize(Round4(loca_table->length)); 89 uint8_t* glyf_dst = &glyf_table->buffer[0]; 90 uint8_t* loca_dst = &loca_table->buffer[0]; 91 uint32_t glyf_offset = 0; 92 size_t loca_offset = 0; 93 94 for (int i = 0; i < num_glyphs; ++i) { 95 StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst); 96 Glyph glyph; 97 const uint8_t* glyph_data; 98 size_t glyph_size; 99 if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) || 100 (glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) { 101 return FONT_COMPRESSION_FAILURE(); 102 } 103 NormalizeSimpleGlyphBoundingBox(&glyph); 104 size_t glyf_dst_size = glyf_table->buffer.size() - glyf_offset; 105 if (!StoreGlyph(glyph, glyf_dst + glyf_offset, &glyf_dst_size)) { 106 return FONT_COMPRESSION_FAILURE(); 107 } 108 glyf_dst_size = Round4(glyf_dst_size); 109 if (glyf_dst_size > std::numeric_limits<uint32_t>::max() || 110 glyf_offset + static_cast<uint32_t>(glyf_dst_size) < glyf_offset || 111 (index_fmt == 0 && glyf_offset + glyf_dst_size >= (1UL << 17))) { 112 return FONT_COMPRESSION_FAILURE(); 113 } 114 glyf_offset += glyf_dst_size; 115 } 116 StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst); 117 118 glyf_table->buffer.resize(glyf_offset); 119 glyf_table->data = &glyf_table->buffer[0]; 120 glyf_table->length = glyf_offset; 121 loca_table->data = &loca_table->buffer[0]; 122 123 return true; 124 } 125 126 bool NormalizeOffsets(Font* font) { 127 uint32_t offset = 12 + 16 * font->num_tables; 128 for (auto& i : font->tables) { 129 i.second.offset = offset; 130 offset += Round4(i.second.length); 131 } 132 return true; 133 } 134 135 namespace { 136 137 uint32_t ComputeChecksum(const uint8_t* buf, size_t size) { 138 uint32_t checksum = 0; 139 for (size_t i = 0; i < size; i += 4) { 140 checksum += ((buf[i] << 24) | 141 (buf[i + 1] << 16) | 142 (buf[i + 2] << 8) | 143 buf[i + 3]); 144 } 145 return checksum; 146 } 147 148 uint32_t ComputeHeaderChecksum(const Font& font) { 149 uint32_t checksum = font.flavor; 150 uint16_t max_pow2 = font.num_tables ? Log2Floor(font.num_tables) : 0; 151 uint16_t search_range = max_pow2 ? 1 << (max_pow2 + 4) : 0; 152 uint16_t range_shift = (font.num_tables << 4) - search_range; 153 checksum += (font.num_tables << 16 | search_range); 154 checksum += (max_pow2 << 16 | range_shift); 155 for (const auto& i : font.tables) { 156 checksum += i.second.tag; 157 checksum += i.second.checksum; 158 checksum += i.second.offset; 159 checksum += i.second.length; 160 } 161 return checksum; 162 } 163 164 } // namespace 165 166 bool FixChecksums(Font* font) { 167 Font::Table* head_table = font->FindTable(kHeadTableTag); 168 if (head_table == NULL || head_table->length < 12) { 169 return FONT_COMPRESSION_FAILURE(); 170 } 171 head_table->buffer.resize(Round4(head_table->length)); 172 uint8_t* head_buf = &head_table->buffer[0]; 173 memcpy(head_buf, head_table->data, Round4(head_table->length)); 174 head_table->data = head_buf; 175 size_t offset = 8; 176 StoreU32(0, &offset, head_buf); 177 uint32_t file_checksum = 0; 178 for (auto& i : font->tables) { 179 Font::Table* table = &i.second; 180 table->checksum = ComputeChecksum(table->data, table->length); 181 file_checksum += table->checksum; 182 } 183 file_checksum += ComputeHeaderChecksum(*font); 184 offset = 8; 185 StoreU32(0xb1b0afba - file_checksum, &offset, head_buf); 186 return true; 187 } 188 189 bool NormalizeFont(Font* font) { 190 return (RemoveDigitalSignature(font) && 191 NormalizeGlyphs(font) && 192 NormalizeOffsets(font) && 193 FixChecksums(font)); 194 } 195 196 } // namespace woff2 197