sdk-world v2026.11.05
PS2 SDK - world tier (copyleft-allowed, non-essential packages)
Loading...
Searching...
No Matches
afgsub.c
1/****************************************************************************
2 *
3 * afgsub.c
4 *
5 * Auto-fitter routines to parse the GSUB table (body).
6 *
7 * Copyright (C) 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#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
19
20
21#include <freetype/freetype.h>
22#include <freetype/tttables.h>
23#include <freetype/tttags.h>
24
26
27#include "afglobal.h"
28#include "afgsub.h"
29#include "aftypes.h"
30
31
32 /*********************************/
33 /******** ********/
34 /******** GSUB validation ********/
35 /******** ********/
36 /*********************************/
37
38
39 static FT_Bool
40 af_validate_coverage( FT_Byte* table,
41 FT_Byte* table_limit,
42 FT_UInt *num_glyphs )
43 {
44 FT_UInt format;
45
46 FT_Byte* p = table;
47 FT_UInt count = 0;
48
49
50 if ( table_limit < p + 4 )
51 return FALSE;
52
53 format = FT_NEXT_USHORT( p );
54 if ( format == 1 )
55 {
56 FT_UInt glyphCount = FT_NEXT_USHORT( p );
57
58
59 /* We don't validate glyph IDs. */
60 if ( table_limit < p + glyphCount * 2 )
61 return FALSE;
62
63 count += glyphCount;
64 }
65 else if ( format == 2 )
66 {
67 FT_UInt rangeCount = FT_NEXT_USHORT( p );
68 FT_Byte* limit = p + rangeCount * 6;
69
70
71 if ( table_limit < limit )
72 return FALSE;
73
74 while ( p < limit )
75 {
76 FT_UInt startGlyphID = FT_NEXT_USHORT( p );
77 FT_UInt endGlyphID = FT_NEXT_USHORT( p );
78
79
80 if ( startGlyphID > endGlyphID )
81 return FALSE;
82
83 count += endGlyphID - startGlyphID + 1;
84
85 /* We don't validate coverage indices. */
86 p += 2;
87 }
88 }
89 else
90 return FALSE;
91
92 if ( num_glyphs )
93 *num_glyphs = count;
94
95 return TRUE;
96 }
97
98
99 static FT_Bool
100 af_validate_single_subst1( FT_Byte* table,
101 FT_Byte* table_limit )
102 {
103 FT_Byte* coverage;
104
105
106 /* Subtable format is already checked. */
107
108 /* The four bytes for the coverage table offset */
109 /* and the glyph ID delta are already checked. */
110 coverage = table + FT_PEEK_USHORT( table + 2 );
111 if ( !af_validate_coverage( coverage, table_limit, NULL ) )
112 return FALSE;
113
114 /* We don't validate glyph IDs. */
115
116 return TRUE;
117 }
118
119
120 static FT_Bool
121 af_validate_single_subst2( FT_Byte* table,
122 FT_Byte* table_limit )
123 {
124 FT_Byte* coverage;
125 FT_UInt glyphCount;
126 FT_UInt num_glyphs;
127
128 /* Subtable format is already checked. */
129 FT_Byte* p = table + 2;
130
131
132 /* The four bytes for the coverage table offset */
133 /* and `glyphCount` are already checked. */
134 coverage = table + FT_NEXT_USHORT( p );
135 if ( !af_validate_coverage( coverage, table_limit, &num_glyphs ) )
136 return FALSE;
137
138 glyphCount = FT_NEXT_USHORT( p );
139 /* We don't validate glyph IDs. */
140 if ( table_limit < p + glyphCount * 2 )
141 return FALSE;
142
143 if ( glyphCount != num_glyphs )
144 return FALSE;
145
146 return TRUE;
147 }
148
149
150 static FT_Bool
151 af_validate_alternate( FT_Byte* table,
152 FT_Byte* table_limit )
153 {
154 FT_Byte* coverage;
155 FT_UInt alternateSetCount;
156 FT_UInt num_glyphs;
157
158 /* Subtable format is already checked. */
159 FT_Byte* p = table + 2;
160 FT_Byte* limit;
161
162
163 /* The four bytes for the coverage table offset */
164 /* and `alternateSetCount` are already checked. */
165 coverage = table + FT_NEXT_USHORT( p );
166 if ( !af_validate_coverage( coverage, table_limit, &num_glyphs ) )
167 return FALSE;
168
169 alternateSetCount = FT_NEXT_USHORT( p );
170 limit = p + alternateSetCount * 2;
171 if ( table_limit < limit )
172 return FALSE;
173
174 if ( alternateSetCount != num_glyphs )
175 return FALSE;
176
177 while ( p < limit )
178 {
179 FT_Byte* alternate_set;
180 FT_UInt glyphCount;
181
182
183 alternate_set = table + FT_NEXT_USHORT( p );
184 if ( table_limit < alternate_set + 2 )
185 return FALSE;
186
187 glyphCount = FT_PEEK_USHORT( alternate_set );
188 /* We don't validate glyph IDs. */
189 if ( table_limit < alternate_set + 2 + glyphCount * 2 )
190 return FALSE;
191 }
192
193 return TRUE;
194 }
195
196
197 /* Validate 'SingleSubst' and 'AlternateSubst' lookup tables. */
198 static FT_Bool
199 af_validate_lookup_table( FT_Byte* table,
200 FT_Byte* table_limit )
201 {
202 FT_UInt lookupType;
203 FT_UInt real_lookupType = 0;
204 FT_UInt subtableCount;
205
206 FT_Byte* p = table;
207 FT_Byte* limit;
208
209
210 if ( table_limit < p + 6 )
211 return FALSE;
212
213 lookupType = FT_NEXT_USHORT( p );
214
215 p += 2; /* Skip `lookupFlag`. */
216
217 subtableCount = FT_NEXT_USHORT( p );
218 limit = p + subtableCount * 2;
219 if ( table_limit < limit )
220 return FALSE;
221
222 while ( p < limit )
223 {
224 FT_Byte* subtable = table + FT_NEXT_USHORT( p );
225 FT_UInt format;
226
227
228 if ( lookupType == 7 )
229 {
230 /* Substitution extension. */
231 FT_Byte* q = subtable;
232
233
234 if ( table_limit < q + 8 )
235 return FALSE;
236
237 if ( FT_NEXT_USHORT( q ) != 1 ) /* format */
238 return FALSE;
239
240 if ( real_lookupType == 0 )
241 real_lookupType = FT_NEXT_USHORT( q );
242 else if ( real_lookupType != FT_NEXT_USHORT( q ) )
243 return FALSE;
244
245 subtable += FT_PEEK_ULONG( q );
246 }
247 else
248 real_lookupType = lookupType;
249
250 /* Ensure the first six bytes of all subtable formats. */
251 if ( table_limit < subtable + 6 )
252 return FALSE;
253
254 format = FT_PEEK_USHORT( subtable );
255
256 if ( real_lookupType == 1 )
257 {
258 if ( format == 1 )
259 {
260 if ( !af_validate_single_subst1( subtable, table_limit ) )
261 return FALSE;
262 }
263 else if ( format == 2 )
264 {
265 if ( !af_validate_single_subst2( subtable, table_limit ) )
266 return FALSE;
267 }
268 else
269 return FALSE;
270 }
271 else if ( real_lookupType == 3 )
272 {
273 if ( format == 1 )
274 {
275 if ( !af_validate_alternate( subtable, table_limit ) )
276 return FALSE;
277 }
278 else
279 return FALSE;
280 }
281 else
282 return FALSE;
283 }
284
285 return TRUE;
286 }
287
288
289 FT_LOCAL_DEF( void )
290 af_parse_gsub( AF_FaceGlobals globals )
291 {
292 FT_Error error = FT_Err_Ok;
293
294 FT_Face face = globals->face;
295 FT_Memory memory = face->memory;
296
297 FT_ULong gsub_length;
298 FT_Byte* gsub;
299 FT_Byte* gsub_limit;
300
301 FT_UInt32* gsub_lookups_single_alternate;
302
303 FT_UInt lookupListOffset;
304 FT_Byte* lookup_list;
305 FT_UInt lookupCount;
306
307 FT_UInt idx;
308
309 FT_Byte* p;
310 FT_Byte* limit;
311
312
313 globals->gsub = NULL;
314 globals->gsub_lookups_single_alternate = NULL;
315
316 /* No error if we can't load or parse GSUB data. */
317
318 gsub = NULL;
319 gsub_lookups_single_alternate = NULL;
320
321 gsub_length = 0;
322 if ( FT_Load_Sfnt_Table( face, TTAG_GSUB, 0, NULL, &gsub_length ) )
323 goto Fail;
324
325 if ( FT_QALLOC( gsub, gsub_length ) )
326 goto Fail;
327
328 if ( FT_Load_Sfnt_Table( face, TTAG_GSUB, 0, gsub, &gsub_length ) )
329 goto Fail;
330
331 if ( gsub_length < 10 )
332 goto Fail;
333
334 lookupListOffset = FT_PEEK_USHORT( gsub + 8 );
335 if ( gsub_length < lookupListOffset + 2 )
336 goto Fail;
337
338 lookupCount = FT_PEEK_USHORT( gsub + lookupListOffset );
339 if ( gsub_length < lookupListOffset + 2 + lookupCount * 2 )
340 goto Fail;
341
342 if ( FT_NEW_ARRAY( gsub_lookups_single_alternate, lookupCount ) )
343 goto Fail;
344
345 gsub_limit = gsub + gsub_length;
346 lookup_list = gsub + lookupListOffset;
347 p = lookup_list + 2;
348 limit = p + lookupCount * 2;
349 idx = 0;
350 while ( p < limit )
351 {
352 FT_UInt lookupOffset = FT_NEXT_USHORT( p );
353
354
355 if ( af_validate_lookup_table( lookup_list + lookupOffset,
356 gsub_limit ) )
357 {
358 /* We store offsets relative to the start of the GSUB table. */
359 gsub_lookups_single_alternate[idx] = lookupListOffset + lookupOffset;
360 }
361
362 idx++;
363 }
364
365 globals->gsub = gsub;
366 globals->gsub_lookups_single_alternate = gsub_lookups_single_alternate;
367
368 return;
369
370 Fail:
371 FT_FREE( gsub );
372 FT_FREE( gsub_lookups_single_alternate );
373 }
374
375
376 /*********************************/
377 /******** ********/
378 /******** GSUB access ********/
379 /******** ********/
380 /*********************************/
381
382
383 static FT_UInt
384 af_coverage_format( FT_Byte* coverage )
385 {
386 return FT_PEEK_USHORT( coverage );
387 }
388
389
390 static FT_Byte*
391 af_coverage_start( FT_Byte* coverage )
392 {
393 return coverage + 4;
394 }
395
396
397 static FT_Byte*
398 af_coverage_limit( FT_Byte* coverage )
399 {
400 if ( af_coverage_format( coverage ) == 1 )
401 {
402 FT_UInt glyphCount = FT_PEEK_USHORT( coverage + 2 );
403
404
405 return af_coverage_start( coverage ) + glyphCount * 2;
406 }
407 else
408 {
409 FT_UInt rangeCount = FT_PEEK_USHORT( coverage + 2 );
410
411
412 return af_coverage_start( coverage ) + rangeCount * 6;
413 }
414 }
415
416
417 typedef struct AF_CoverageIteratorRec_* AF_CoverageIterator;
418
419 typedef struct AF_CoverageIteratorRec_
420 {
421 FT_UInt format;
422
423 FT_Byte* p;
424 FT_Byte* limit;
425
426 FT_UInt16 glyph;
427 FT_UInt16 glyph_limit;
428
429 } AF_CoverageIteratorRec;
430
431
432 static FT_Bool
433 af_coverage_iterator( AF_CoverageIterator iter,
434 FT_UInt16* glyph )
435 {
436 if ( iter->p >= iter->limit )
437 return FALSE;
438
439 if ( iter->format == 1 )
440 *glyph = FT_NEXT_USHORT( iter->p );
441 else
442 {
443 if ( iter->glyph > iter->glyph_limit )
444 {
445 iter->glyph = FT_NEXT_USHORT( iter->p );
446 iter->glyph_limit = FT_NEXT_USHORT( iter->p );
447
448 iter->p += 2;
449 }
450
451 *glyph = iter->glyph++;
452 }
453
454 return TRUE;
455 }
456
457
458 static AF_CoverageIteratorRec
459 af_coverage_iterator_init( FT_Byte* coverage )
460 {
461 AF_CoverageIteratorRec iterator;
462
463
464 iterator.format = af_coverage_format( coverage );
465 iterator.p = af_coverage_start( coverage );
466 iterator.limit = af_coverage_limit( coverage );
467 iterator.glyph = 1;
468 iterator.glyph_limit = 0;
469
470 return iterator;
471 }
472
473
474 /*
475 Because we merge all single and alternate substitution mappings into
476 one, large hash, we need the possibility to have multiple glyphs as
477 values. We utilize that we have 32bit integers but only 16bit glyph
478 indices, using the following scheme.
479
480 If glyph G maps to a single substitute S, the entry in the map is
481
482 G -> S
483
484 If glyph G maps to multiple substitutes S1, S2, ..., Sn, we do
485
486 G -> S1 + ((n - 1) << 16)
487 G + (1 << 16) -> S2
488 G + (2 << 16) -> S3
489 ...
490 G + ((n - 1) << 16) -> Sn
491 */
492 static FT_Error
493 af_hash_insert( FT_UInt16 glyph,
494 FT_UInt16 substitute,
495 FT_Hash map,
496 FT_Memory memory )
497 {
498 FT_Error error;
499
500 size_t* value = ft_hash_num_lookup( glyph, map );
501
502
503 if ( !value )
504 {
505 error = ft_hash_num_insert( glyph, substitute, map, memory );
506 if ( error )
507 return error;
508 }
509 else
510 {
511 /* Get number of substitutes, increased by one... */
512 FT_UInt mask = ( (FT_UInt)*value & 0xFFFF0000U ) + 0x10000U;
513
514
515 /* ... which becomes the new key mask. */
516 error = ft_hash_num_insert( (FT_Int)( glyph | mask ),
517 substitute,
518 map,
519 memory );
520 if ( error )
521 return error;
522
523 /* Update number of substitutes. */
524 *value += 0x10000U;
525 }
526
527 return FT_Err_Ok;
528 }
529
530
531 static FT_Error
532 af_map_single_subst1( FT_Hash map,
533 FT_Byte* table,
534 FT_Memory memory )
535 {
536 FT_Error error;
537
538 FT_Byte* coverage = table + FT_PEEK_USHORT( table + 2 );
539 FT_UInt deltaGlyphID = FT_PEEK_USHORT( table + 4 );
540
541 AF_CoverageIteratorRec iterator = af_coverage_iterator_init( coverage );
542
543 FT_UInt16 glyph;
544
545
546 while ( af_coverage_iterator( &iterator, &glyph ) )
547 {
548 /* `deltaGlyphID` requires modulo 65536 arithmetic. */
549 FT_UInt16 subst = (FT_UInt16)( ( glyph + deltaGlyphID ) % 0x10000U );
550
551
552 error = af_hash_insert( glyph, subst, map, memory );
553 if ( error )
554 return error;
555 }
556
557 return FT_Err_Ok;
558 }
559
560
561 static FT_Error
562 af_map_single_subst2( FT_Hash map,
563 FT_Byte* table,
564 FT_Memory memory )
565 {
566 FT_Error error;
567
568 FT_Byte* coverage = table + FT_PEEK_USHORT( table + 2 );
569
570 AF_CoverageIteratorRec iterator = af_coverage_iterator_init( coverage );
571
572 FT_UInt16 glyph;
573 FT_Byte* p = table + 6;
574
575
576 while ( af_coverage_iterator( &iterator, &glyph ) )
577 {
578 FT_UInt16 subst = FT_NEXT_USHORT( p );
579
580
581 error = af_hash_insert( glyph, subst, map, memory );
582 if ( error )
583 return error;
584 }
585
586 return FT_Err_Ok;
587 }
588
589
590 static FT_Error
591 af_map_alternate( FT_Hash map,
592 FT_Byte* table,
593 FT_Memory memory )
594 {
595 FT_Error error;
596
597 FT_Byte* coverage = table + FT_PEEK_USHORT( table + 2 );
598
599 AF_CoverageIteratorRec iterator = af_coverage_iterator_init( coverage );
600
601 FT_UInt16 glyph;
602 FT_Byte* p = table + 6;
603
604
605 while ( af_coverage_iterator( &iterator, &glyph ) )
606 {
607 FT_Byte* alternate_set = table + FT_NEXT_USHORT( p );
608
609 FT_Byte* q = alternate_set;
610 FT_UInt glyphCount = FT_NEXT_USHORT( q );
611
612 FT_UInt i;
613
614
615 for ( i = 0; i < glyphCount; i++ )
616 {
617 FT_UInt16 subst = FT_NEXT_USHORT( q );
618
619
620 error = af_hash_insert( glyph, subst, map, memory );
621 if ( error )
622 return error;
623 }
624 }
625
626 return FT_Err_Ok;
627 }
628
629
630 /* Map 'SingleSubst' and 'AlternateSubst' lookup tables. */
631 FT_LOCAL_DEF( FT_Error )
632 af_map_lookup( AF_FaceGlobals globals,
633 FT_Hash map,
634 FT_UInt32 lookup_offset )
635 {
636 FT_Face face = globals->face;
637 FT_Memory memory = face->memory;
638
639 FT_Byte* table = globals->gsub + lookup_offset;
640
641 FT_UInt lookupType = FT_PEEK_USHORT( table );
642 FT_UInt subtableCount = FT_PEEK_USHORT( table + 4 );
643
644 FT_Byte* p = table + 6;
645 FT_Byte* limit = p + subtableCount * 2;
646
647
648 while ( p < limit )
649 {
650 FT_Error error;
651
652 FT_UInt real_lookupType = lookupType;
653
654 FT_Byte* subtable = table + FT_NEXT_USHORT( p );
655
656
657 if ( lookupType == 7 )
658 {
659 FT_Byte* q = subtable + 2;
660
661
662 real_lookupType = FT_NEXT_USHORT( q );
663 subtable += FT_PEEK_ULONG( q );
664 }
665
666 if ( real_lookupType == 1 )
667 {
668 FT_UInt format = FT_PEEK_USHORT( subtable );
669
670
671 error = ( format == 1 )
672 ? af_map_single_subst1( map, subtable, memory )
673 : af_map_single_subst2( map, subtable, memory );
674 }
675 else
676 error = af_map_alternate( map, subtable, memory );
677
678 if ( error )
679 return error;
680 }
681
682 return FT_Err_Ok;
683 }
684
685
686#else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
687
688/* ANSI C doesn't like empty source files */
689typedef int afgsub_dummy_;
690
691#endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
692
693/* END */