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