1 // Copyright 2014 Google Inc. All Rights Reserved.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 // Library for converting TTF format font files to their WOFF2 versions.
17 #include "./woff2_enc.h"
29 #include "./normalize.h"
31 #include "./store_bytes.h"
32 #include "./table_tags.h"
33 #include "./transform.h"
34 #include "./woff2_common.h"
44 const size_t kWoff2HeaderSize
= 48;
45 const size_t kWoff2EntrySize
= 20;
47 size_t Base128Size(size_t n
) {
49 for (; n
>= 128; n
>>= 7) ++size
;
53 void StoreBase128(size_t len
, size_t* offset
, uint8_t* dst
) {
54 size_t size
= Base128Size(len
);
55 for (int i
= 0; i
< size
; ++i
) {
56 int b
= static_cast<int>((len
>> (7 * (size
- i
- 1))) & 0x7f);
64 bool Compress(const uint8_t* data
, const size_t len
,
65 uint8_t* result
, uint32_t* result_len
,
66 brotli::BrotliParams::Mode mode
) {
67 size_t compressed_len
= *result_len
;
68 brotli::BrotliParams params
;
70 if (brotli::BrotliCompressBuffer(params
, len
, data
, &compressed_len
, result
)
74 *result_len
= compressed_len
;
78 bool Woff2Compress(const uint8_t* data
, const size_t len
,
79 uint8_t* result
, uint32_t* result_len
) {
80 return Compress(data
, len
, result
, result_len
,
81 brotli::BrotliParams::MODE_FONT
);
84 bool TextCompress(const uint8_t* data
, const size_t len
,
85 uint8_t* result
, uint32_t* result_len
) {
86 return Compress(data
, len
, result
, result_len
,
87 brotli::BrotliParams::MODE_TEXT
);
90 bool ReadLongDirectory(Buffer
* file
, std::vector
<Table
>* tables
,
92 for (size_t i
= 0; i
< num_tables
; ++i
) {
93 Table
* table
= &(*tables
)[i
];
94 if (!file
->ReadU32(&table
->tag
) ||
95 !file
->ReadU32(&table
->flags
) ||
96 !file
->ReadU32(&table
->src_length
) ||
97 !file
->ReadU32(&table
->transform_length
) ||
98 !file
->ReadU32(&table
->dst_length
)) {
99 return FONT_COMPRESSION_FAILURE();
105 int KnownTableIndex(uint32_t tag
) {
106 for (int i
= 0; i
< 63; ++i
) {
107 if (tag
== kKnownTags
[i
]) return i
;
112 void StoreTableEntry(const Table
& table
, size_t* offset
, uint8_t* dst
) {
113 uint8_t flag_byte
= KnownTableIndex(table
.tag
);
114 dst
[(*offset
)++] = flag_byte
;
115 // The index here is treated as a set of flag bytes because
116 // bits 6 and 7 of the byte are reserved for future use as flags.
117 // 0x3f or 63 means an arbitrary table tag.
118 if ((flag_byte
& 0x3f) == 0x3f) {
119 StoreU32(table
.tag
, offset
, dst
);
121 StoreBase128(table
.src_length
, offset
, dst
);
122 if ((table
.flags
& kWoff2FlagsTransform
) != 0) {
123 StoreBase128(table
.transform_length
, offset
, dst
);
127 size_t TableEntrySize(const Table
& table
) {
128 uint8_t flag_byte
= KnownTableIndex(table
.tag
);
129 size_t size
= ((flag_byte
& 0x3f) != 0x3f) ? 1 : 5;
130 size
+= Base128Size(table
.src_length
);
131 if ((table
.flags
& kWoff2FlagsTransform
) != 0) {
132 size
+= Base128Size(table
.transform_length
);
137 size_t ComputeWoff2Length(const std::vector
<Table
>& tables
,
138 size_t extended_metadata_length
) {
139 size_t size
= kWoff2HeaderSize
;
140 for (const auto& table
: tables
) {
141 size
+= TableEntrySize(table
);
143 for (const auto& table
: tables
) {
144 size
+= table
.dst_length
;
147 size
+= extended_metadata_length
;
151 size_t ComputeTTFLength(const std::vector
<Table
>& tables
) {
152 size_t size
= 12 + 16 * tables
.size(); // sfnt header
153 for (const auto& table
: tables
) {
154 size
+= Round4(table
.src_length
);
159 size_t ComputeTotalTransformLength(const Font
& font
) {
161 for (const auto& i
: font
.tables
) {
162 const Font::Table
& table
= i
.second
;
163 if (table
.tag
& 0x80808080 || !font
.FindTable(table
.tag
^ 0x80808080)) {
164 // Count transformed tables and non-transformed tables that do not have
165 // transformed versions.
166 total
+= table
.length
;
174 size_t MaxWOFF2CompressedSize(const uint8_t* data
, size_t length
) {
175 return MaxWOFF2CompressedSize(data
, length
, "");
178 size_t MaxWOFF2CompressedSize(const uint8_t* data
, size_t length
,
179 const string
& extended_metadata
) {
180 // Except for the header size, which is 32 bytes larger in woff2 format,
181 // all other parts should be smaller (table header in short format,
182 // transformations and compression). Just to be sure, we will give some
184 return length
+ 1024 + extended_metadata
.length();
187 uint32_t CompressedBufferSize(uint32_t original_size
) {
188 return 1.2 * original_size
+ 10240;
191 bool ConvertTTFToWOFF2(const uint8_t *data
, size_t length
,
192 uint8_t *result
, size_t *result_length
) {
193 return ConvertTTFToWOFF2(data
, length
, result
, result_length
, "");
196 bool ConvertTTFToWOFF2(const uint8_t *data
, size_t length
,
197 uint8_t *result
, size_t *result_length
,
198 const string
& extended_metadata
) {
200 if (!ReadFont(data
, length
, &font
)) {
201 fprintf(stderr
, "Parsing of the input font failed.\n");
205 if (!NormalizeFont(&font
)) {
206 fprintf(stderr
, "Font normalization failed.\n");
210 if (!TransformGlyfAndLocaTables(&font
)) {
211 fprintf(stderr
, "Font transformation failed.\n");
215 const Font::Table
* head_table
= font
.FindTable(kHeadTableTag
);
216 if (head_table
== NULL
) {
217 fprintf(stderr
, "Missing head table.\n");
221 // Although the compressed size of each table in the final woff2 file won't
222 // be larger than its transform_length, we have to allocate a large enough
223 // buffer for the compressor, since the compressor can potentially increase
224 // the size. If the compressor overflows this, it should return false and
225 // then this function will also return false.
226 size_t total_transform_length
= ComputeTotalTransformLength(font
);
227 size_t compression_buffer_size
= CompressedBufferSize(total_transform_length
);
228 std::vector
<uint8_t> compression_buf(compression_buffer_size
);
229 uint32_t total_compressed_length
= compression_buffer_size
;
231 // Collect all transformed data into one place.
232 std::vector
<uint8_t> transform_buf(total_transform_length
);
233 size_t transform_offset
= 0;
234 for (const auto& i
: font
.tables
) {
235 if (i
.second
.tag
& 0x80808080) continue;
236 const Font::Table
* table
= font
.FindTable(i
.second
.tag
^ 0x80808080);
237 if (table
== NULL
) table
= &i
.second
;
238 StoreBytes(table
->data
, table
->length
,
239 &transform_offset
, &transform_buf
[0]);
241 // Compress all transformed data in one stream.
242 if (!Woff2Compress(transform_buf
.data(), total_transform_length
,
244 &total_compressed_length
)) {
245 fprintf(stderr
, "Compression of combined table failed.\n");
249 // Compress the extended metadata
250 uint32_t compressed_metadata_buf_length
=
251 CompressedBufferSize(extended_metadata
.length());
252 std::vector
<uint8_t> compressed_metadata_buf(compressed_metadata_buf_length
);
254 if (extended_metadata
.length() > 0) {
255 if (!TextCompress((const uint8_t*)extended_metadata
.data(),
256 extended_metadata
.length(),
257 compressed_metadata_buf
.data(),
258 &compressed_metadata_buf_length
)) {
259 fprintf(stderr
, "Compression of extended metadata failed.\n");
263 compressed_metadata_buf_length
= 0;
266 std::vector
<Table
> tables
;
267 for (const auto& i
: font
.tables
) {
268 const Font::Table
& src_table
= i
.second
;
269 if (src_table
.tag
& 0x80808080) {
270 // This is a transformed table, we will write it together with the
275 table
.tag
= src_table
.tag
;
277 table
.src_length
= src_table
.length
;
278 table
.transform_length
= src_table
.length
;
279 const uint8_t* transformed_data
= src_table
.data
;
280 const Font::Table
* transformed_table
=
281 font
.FindTable(src_table
.tag
^ 0x80808080);
282 if (transformed_table
!= NULL
) {
283 table
.flags
|= kWoff2FlagsTransform
;
284 table
.transform_length
= transformed_table
->length
;
285 transformed_data
= transformed_table
->data
;
287 if (tables
.empty()) {
288 table
.dst_length
= total_compressed_length
;
289 table
.dst_data
= &compression_buf
[0];
291 table
.dst_length
= 0;
292 table
.dst_data
= NULL
;
293 table
.flags
|= kWoff2FlagsContinueStream
;
295 tables
.push_back(table
);
298 size_t woff2_length
=
299 ComputeWoff2Length(tables
, compressed_metadata_buf_length
);
300 if (woff2_length
> *result_length
) {
301 fprintf(stderr
, "Result allocation was too small (%zd vs %zd bytes).\n",
302 *result_length
, woff2_length
);
305 *result_length
= woff2_length
;
308 StoreU32(kWoff2Signature
, &offset
, result
);
309 StoreU32(font
.flavor
, &offset
, result
);
310 StoreU32(woff2_length
, &offset
, result
);
311 Store16(tables
.size(), &offset
, result
);
312 Store16(0, &offset
, result
); // reserved
313 StoreU32(ComputeTTFLength(tables
), &offset
, result
);
314 StoreU32(total_compressed_length
, &offset
, result
);
315 StoreBytes(head_table
->data
+ 4, 4, &offset
, result
); // font revision
316 if (compressed_metadata_buf_length
> 0) {
317 StoreU32(woff2_length
- compressed_metadata_buf_length
,
318 &offset
, result
); // metaOffset
319 StoreU32(compressed_metadata_buf_length
, &offset
, result
); // metaLength
320 StoreU32(extended_metadata
.length(), &offset
, result
); // metaOrigLength
322 StoreU32(0, &offset
, result
); // metaOffset
323 StoreU32(0, &offset
, result
); // metaLength
324 StoreU32(0, &offset
, result
); // metaOrigLength
326 StoreU32(0, &offset
, result
); // privOffset
327 StoreU32(0, &offset
, result
); // privLength
328 for (const auto& table
: tables
) {
329 StoreTableEntry(table
, &offset
, result
);
331 for (const auto& table
: tables
) {
332 StoreBytes(table
.dst_data
, table
.dst_length
, &offset
, result
);
333 offset
= Round4(offset
);
335 StoreBytes(compressed_metadata_buf
.data(), compressed_metadata_buf_length
,
338 if (*result_length
!= offset
) {
339 fprintf(stderr
, "Mismatch between computed and actual length "
340 "(%zd vs %zd)\n", *result_length
, offset
);