1 /**************************************************************************** 2 * 3 * sfwoff.c 4 * 5 * WOFF format management (base). 6 * 7 * Copyright (C) 1996-2020 by 8 * David Turner, Robert Wilhelm, and Werner Lemberg. 9 * 10 * This file is part of the FreeType project, and may only be used, 11 * modified, and distributed under the terms of the FreeType project 12 * license, LICENSE.TXT. By continuing to use, modify, or distribute 13 * this file you indicate that you have read the license and 14 * understand and accept it fully. 15 * 16 */ 17 18 19 #include <ft2build.h> 20 #include "sfwoff.h" 21 #include FT_TRUETYPE_TAGS_H 22 #include FT_INTERNAL_DEBUG_H 23 #include FT_INTERNAL_STREAM_H 24 #include FT_GZIP_H 25 26 27 /************************************************************************** 28 * 29 * The macro FT_COMPONENT is used in trace mode. It is an implicit 30 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log 31 * messages during execution. 32 */ 33 #undef FT_COMPONENT 34 #define FT_COMPONENT sfwoff 35 36 37 #define WRITE_USHORT( p, v ) \ 38 do \ 39 { \ 40 *(p)++ = (FT_Byte)( (v) >> 8 ); \ 41 *(p)++ = (FT_Byte)( (v) >> 0 ); \ 42 \ 43 } while ( 0 ) 44 45 #define WRITE_ULONG( p, v ) \ 46 do \ 47 { \ 48 *(p)++ = (FT_Byte)( (v) >> 24 ); \ 49 *(p)++ = (FT_Byte)( (v) >> 16 ); \ 50 *(p)++ = (FT_Byte)( (v) >> 8 ); \ 51 *(p)++ = (FT_Byte)( (v) >> 0 ); \ 52 \ 53 } while ( 0 ) 54 55 56 static void 57 sfnt_stream_close( FT_Stream stream ) 58 { 59 FT_Memory memory = stream->memory; 60 61 62 FT_FREE( stream->base ); 63 64 stream->size = 0; 65 stream->base = NULL; 66 stream->close = NULL; 67 } 68 69 70 FT_CALLBACK_DEF( int ) 71 compare_offsets( const void* a, 72 const void* b ) 73 { 74 WOFF_Table table1 = *(WOFF_Table*)a; 75 WOFF_Table table2 = *(WOFF_Table*)b; 76 77 FT_ULong offset1 = table1->Offset; 78 FT_ULong offset2 = table2->Offset; 79 80 81 if ( offset1 > offset2 ) 82 return 1; 83 else if ( offset1 < offset2 ) 84 return -1; 85 else 86 return 0; 87 } 88 89 90 /* Replace `face->root.stream' with a stream containing the extracted */ 91 /* SFNT of a WOFF font. */ 92 93 FT_LOCAL_DEF( FT_Error ) 94 woff_open_font( FT_Stream stream, 95 TT_Face face ) 96 { 97 FT_Memory memory = stream->memory; 98 FT_Error error = FT_Err_Ok; 99 100 WOFF_HeaderRec woff; 101 WOFF_Table tables = NULL; 102 WOFF_Table* indices = NULL; 103 104 FT_ULong woff_offset; 105 106 FT_Byte* sfnt = NULL; 107 FT_Stream sfnt_stream = NULL; 108 109 FT_Byte* sfnt_header; 110 FT_ULong sfnt_offset; 111 112 FT_Int nn; 113 FT_ULong old_tag = 0; 114 115 static const FT_Frame_Field woff_header_fields[] = 116 { 117 #undef FT_STRUCTURE 118 #define FT_STRUCTURE WOFF_HeaderRec 119 120 FT_FRAME_START( 44 ), 121 FT_FRAME_ULONG ( signature ), 122 FT_FRAME_ULONG ( flavor ), 123 FT_FRAME_ULONG ( length ), 124 FT_FRAME_USHORT( num_tables ), 125 FT_FRAME_USHORT( reserved ), 126 FT_FRAME_ULONG ( totalSfntSize ), 127 FT_FRAME_USHORT( majorVersion ), 128 FT_FRAME_USHORT( minorVersion ), 129 FT_FRAME_ULONG ( metaOffset ), 130 FT_FRAME_ULONG ( metaLength ), 131 FT_FRAME_ULONG ( metaOrigLength ), 132 FT_FRAME_ULONG ( privOffset ), 133 FT_FRAME_ULONG ( privLength ), 134 FT_FRAME_END 135 }; 136 137 138 FT_ASSERT( stream == face->root.stream ); 139 FT_ASSERT( FT_STREAM_POS() == 0 ); 140 141 if ( FT_STREAM_READ_FIELDS( woff_header_fields, &woff ) ) 142 return error; 143 144 /* Make sure we don't recurse back here or hit TTC code. */ 145 if ( woff.flavor == TTAG_wOFF || woff.flavor == TTAG_ttcf ) 146 return FT_THROW( Invalid_Table ); 147 148 /* Miscellaneous checks. */ 149 if ( woff.length != stream->size || 150 woff.num_tables == 0 || 151 44 + woff.num_tables * 20UL >= woff.length || 152 12 + woff.num_tables * 16UL >= woff.totalSfntSize || 153 ( woff.totalSfntSize & 3 ) != 0 || 154 ( woff.metaOffset == 0 && ( woff.metaLength != 0 || 155 woff.metaOrigLength != 0 ) ) || 156 ( woff.metaLength != 0 && woff.metaOrigLength == 0 ) || 157 ( woff.privOffset == 0 && woff.privLength != 0 ) ) 158 { 159 FT_ERROR(( "woff_font_open: invalid WOFF header\n" )); 160 return FT_THROW( Invalid_Table ); 161 } 162 163 /* Don't trust `totalSfntSize' before thorough checks. */ 164 if ( FT_ALLOC( sfnt, 12 + woff.num_tables * 16UL ) || 165 FT_NEW( sfnt_stream ) ) 166 goto Exit; 167 168 sfnt_header = sfnt; 169 170 /* Write sfnt header. */ 171 { 172 FT_UInt searchRange, entrySelector, rangeShift, x; 173 174 175 x = woff.num_tables; 176 entrySelector = 0; 177 while ( x ) 178 { 179 x >>= 1; 180 entrySelector += 1; 181 } 182 entrySelector--; 183 184 searchRange = ( 1 << entrySelector ) * 16; 185 rangeShift = woff.num_tables * 16 - searchRange; 186 187 WRITE_ULONG ( sfnt_header, woff.flavor ); 188 WRITE_USHORT( sfnt_header, woff.num_tables ); 189 WRITE_USHORT( sfnt_header, searchRange ); 190 WRITE_USHORT( sfnt_header, entrySelector ); 191 WRITE_USHORT( sfnt_header, rangeShift ); 192 } 193 194 /* While the entries in the sfnt header must be sorted by the */ 195 /* tag value, the tables themselves are not. We thus have to */ 196 /* sort them by offset and check that they don't overlap. */ 197 198 if ( FT_NEW_ARRAY( tables, woff.num_tables ) || 199 FT_NEW_ARRAY( indices, woff.num_tables ) ) 200 goto Exit; 201 202 FT_TRACE2(( "\n" 203 " tag offset compLen origLen checksum\n" 204 " -------------------------------------------\n" )); 205 206 if ( FT_FRAME_ENTER( 20L * woff.num_tables ) ) 207 goto Exit; 208 209 for ( nn = 0; nn < woff.num_tables; nn++ ) 210 { 211 WOFF_Table table = tables + nn; 212 213 table->Tag = FT_GET_TAG4(); 214 table->Offset = FT_GET_ULONG(); 215 table->CompLength = FT_GET_ULONG(); 216 table->OrigLength = FT_GET_ULONG(); 217 table->CheckSum = FT_GET_ULONG(); 218 219 FT_TRACE2(( " %c%c%c%c %08lx %08lx %08lx %08lx\n", 220 (FT_Char)( table->Tag >> 24 ), 221 (FT_Char)( table->Tag >> 16 ), 222 (FT_Char)( table->Tag >> 8 ), 223 (FT_Char)( table->Tag ), 224 table->Offset, 225 table->CompLength, 226 table->OrigLength, 227 table->CheckSum )); 228 229 if ( table->Tag <= old_tag ) 230 { 231 FT_FRAME_EXIT(); 232 233 FT_ERROR(( "woff_font_open: table tags are not sorted\n" )); 234 error = FT_THROW( Invalid_Table ); 235 goto Exit; 236 } 237 238 old_tag = table->Tag; 239 indices[nn] = table; 240 } 241 242 FT_FRAME_EXIT(); 243 244 /* Sort by offset. */ 245 246 ft_qsort( indices, 247 woff.num_tables, 248 sizeof ( WOFF_Table ), 249 compare_offsets ); 250 251 /* Check offsets and lengths. */ 252 253 woff_offset = 44 + woff.num_tables * 20L; 254 sfnt_offset = 12 + woff.num_tables * 16L; 255 256 for ( nn = 0; nn < woff.num_tables; nn++ ) 257 { 258 WOFF_Table table = indices[nn]; 259 260 261 if ( table->Offset != woff_offset || 262 table->CompLength > woff.length || 263 table->Offset > woff.length - table->CompLength || 264 table->OrigLength > woff.totalSfntSize || 265 sfnt_offset > woff.totalSfntSize - table->OrigLength || 266 table->CompLength > table->OrigLength ) 267 { 268 FT_ERROR(( "woff_font_open: invalid table offsets\n" )); 269 error = FT_THROW( Invalid_Table ); 270 goto Exit; 271 } 272 273 table->OrigOffset = sfnt_offset; 274 275 /* The offsets must be multiples of 4. */ 276 woff_offset += ( table->CompLength + 3 ) & ~3U; 277 sfnt_offset += ( table->OrigLength + 3 ) & ~3U; 278 } 279 280 /* 281 * Final checks! 282 * 283 * We don't decode and check the metadata block. 284 * We don't check table checksums either. 285 * But other than those, I think we implement all 286 * `MUST' checks from the spec. 287 */ 288 289 if ( woff.metaOffset ) 290 { 291 if ( woff.metaOffset != woff_offset || 292 woff.metaOffset + woff.metaLength > woff.length ) 293 { 294 FT_ERROR(( "woff_font_open:" 295 " invalid `metadata' offset or length\n" )); 296 error = FT_THROW( Invalid_Table ); 297 goto Exit; 298 } 299 300 /* We have padding only ... */ 301 woff_offset += woff.metaLength; 302 } 303 304 if ( woff.privOffset ) 305 { 306 /* ... if it isn't the last block. */ 307 woff_offset = ( woff_offset + 3 ) & ~3U; 308 309 if ( woff.privOffset != woff_offset || 310 woff.privOffset + woff.privLength > woff.length ) 311 { 312 FT_ERROR(( "woff_font_open: invalid `private' offset or length\n" )); 313 error = FT_THROW( Invalid_Table ); 314 goto Exit; 315 } 316 317 /* No padding for the last block. */ 318 woff_offset += woff.privLength; 319 } 320 321 if ( sfnt_offset != woff.totalSfntSize || 322 woff_offset != woff.length ) 323 { 324 FT_ERROR(( "woff_font_open: invalid `sfnt' table structure\n" )); 325 error = FT_THROW( Invalid_Table ); 326 goto Exit; 327 } 328 329 /* Now use `totalSfntSize'. */ 330 if ( FT_REALLOC( sfnt, 331 12 + woff.num_tables * 16UL, 332 woff.totalSfntSize ) ) 333 goto Exit; 334 335 sfnt_header = sfnt + 12; 336 337 /* Write the tables. */ 338 339 for ( nn = 0; nn < woff.num_tables; nn++ ) 340 { 341 WOFF_Table table = tables + nn; 342 343 344 /* Write SFNT table entry. */ 345 WRITE_ULONG( sfnt_header, table->Tag ); 346 WRITE_ULONG( sfnt_header, table->CheckSum ); 347 WRITE_ULONG( sfnt_header, table->OrigOffset ); 348 WRITE_ULONG( sfnt_header, table->OrigLength ); 349 350 /* Write table data. */ 351 if ( FT_STREAM_SEEK( table->Offset ) || 352 FT_FRAME_ENTER( table->CompLength ) ) 353 goto Exit; 354 355 if ( table->CompLength == table->OrigLength ) 356 { 357 /* Uncompressed data; just copy. */ 358 ft_memcpy( sfnt + table->OrigOffset, 359 stream->cursor, 360 table->OrigLength ); 361 } 362 else 363 { 364 #ifdef FT_CONFIG_OPTION_USE_ZLIB 365 366 /* Uncompress with zlib. */ 367 FT_ULong output_len = table->OrigLength; 368 369 370 error = FT_Gzip_Uncompress( memory, 371 sfnt + table->OrigOffset, &output_len, 372 stream->cursor, table->CompLength ); 373 if ( error ) 374 goto Exit1; 375 if ( output_len != table->OrigLength ) 376 { 377 FT_ERROR(( "woff_font_open: compressed table length mismatch\n" )); 378 error = FT_THROW( Invalid_Table ); 379 goto Exit1; 380 } 381 382 #else /* !FT_CONFIG_OPTION_USE_ZLIB */ 383 384 error = FT_THROW( Unimplemented_Feature ); 385 goto Exit1; 386 387 #endif /* !FT_CONFIG_OPTION_USE_ZLIB */ 388 } 389 390 FT_FRAME_EXIT(); 391 392 /* We don't check whether the padding bytes in the WOFF file are */ 393 /* actually '\0'. For the output, however, we do set them properly. */ 394 sfnt_offset = table->OrigOffset + table->OrigLength; 395 while ( sfnt_offset & 3 ) 396 { 397 sfnt[sfnt_offset] = '\0'; 398 sfnt_offset++; 399 } 400 } 401 402 /* Ok! Finally ready. Swap out stream and return. */ 403 FT_Stream_OpenMemory( sfnt_stream, sfnt, woff.totalSfntSize ); 404 sfnt_stream->memory = stream->memory; 405 sfnt_stream->close = sfnt_stream_close; 406 407 FT_Stream_Free( 408 face->root.stream, 409 ( face->root.face_flags & FT_FACE_FLAG_EXTERNAL_STREAM ) != 0 ); 410 411 face->root.stream = sfnt_stream; 412 413 face->root.face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM; 414 415 Exit: 416 FT_FREE( tables ); 417 FT_FREE( indices ); 418 419 if ( error ) 420 { 421 FT_FREE( sfnt ); 422 FT_Stream_Close( sfnt_stream ); 423 FT_FREE( sfnt_stream ); 424 } 425 426 return error; 427 428 Exit1: 429 FT_FRAME_EXIT(); 430 goto Exit; 431 } 432 433 434 #undef WRITE_USHORT 435 #undef WRITE_ULONG 436 437 438 /* END */