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 
StoreLoca(int index_fmt,uint32_t value,size_t * offset,uint8_t * dst)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 
WriteNormalizedLoca(int index_fmt,int num_glyphs,Font * font)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 
MakeEditableBuffer(Font * font,int tableTag)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 
NormalizeGlyphs(Font * font)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 
NormalizeOffsets(Font * font)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 
ComputeHeaderChecksum(const Font & font)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 
FixChecksums(Font * font)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 {
MarkTransformed(Font * font)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 
NormalizeWithoutFixingChecksums(Font * font)258 bool NormalizeWithoutFixingChecksums(Font* font) {
259   return (MakeEditableBuffer(font, kHeadTableTag) &&
260           RemoveDigitalSignature(font) &&
261           MarkTransformed(font) &&
262           NormalizeGlyphs(font) &&
263           NormalizeOffsets(font));
264 }
265 
NormalizeFont(Font * font)266 bool NormalizeFont(Font* font) {
267   return (NormalizeWithoutFixingChecksums(font) &&
268           FixChecksums(font));
269 }
270 
NormalizeFontCollection(FontCollection * font_collection)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
315