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