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