sdk-world v2026.11.05
PS2 SDK - world tier (copyleft-allowed, non-essential packages)
Loading...
Searching...
No Matches
ttgpos.c
1/****************************************************************************
2 *
3 * ttgpos.c
4 *
5 * Routines to parse and access the 'GPOS' table for simple kerning (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
19#include <freetype/freetype.h>
20#include <freetype/tttables.h>
21#include <freetype/tttags.h>
22
25
26#include "ttgpos.h"
27
28
29#ifdef TT_CONFIG_OPTION_GPOS_KERNING
30
31
32 /**************************************************************************
33 *
34 * The macro FT_COMPONENT is used in trace mode. It is an implicit
35 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
36 * messages during execution.
37 */
38#undef FT_COMPONENT
39#define FT_COMPONENT ttgpos
40
41
42 /*********************************/
43 /******** ********/
44 /******** GPOS validation ********/
45 /******** ********/
46 /*********************************/
47
48 static FT_Bool
49 tt_face_validate_coverage( FT_Byte* table,
50 FT_Byte* table_limit,
51 FT_UInt max_num_coverage_indices )
52 {
53 FT_UInt format;
54
55 FT_Byte* p = table;
56 FT_Byte* limit;
57
58 FT_Long last_id = -1;
59
60
61 if ( table_limit < p + 4 )
62 return FALSE;
63
64 format = FT_NEXT_USHORT( p );
65 if ( format == 1 )
66 {
67 FT_UInt glyphCount = FT_NEXT_USHORT( p );
68
69
70 if ( glyphCount > max_num_coverage_indices )
71 return FALSE;
72
73 limit = p + glyphCount * 2;
74 if ( table_limit < limit )
75 return FALSE;
76
77 while ( p < limit )
78 {
79 FT_UInt id = FT_NEXT_USHORT( p );
80
81
82 if ( last_id >= id )
83 return FALSE;
84 last_id = id;
85 }
86 }
87 else if ( format == 2 )
88 {
89 FT_UInt rangeCount = FT_NEXT_USHORT( p );
90
91
92 limit = p + rangeCount * 6;
93 if ( table_limit < limit )
94 return FALSE;
95
96 while ( p < limit )
97 {
98 FT_UInt startGlyphID = FT_NEXT_USHORT( p );
99 FT_UInt endGlyphID = FT_NEXT_USHORT( p );
100 FT_UInt startCoverageIndex = FT_NEXT_USHORT( p );
101
102
103 if ( startGlyphID > endGlyphID )
104 return FALSE;
105
106 if ( last_id >= startGlyphID )
107 return FALSE;
108 last_id = endGlyphID;
109
110 /* XXX: Is this modulo 65536 arithmetic? */
111 if ( startCoverageIndex + endGlyphID - startGlyphID >=
112 max_num_coverage_indices )
113 return FALSE;
114 }
115 }
116 else
117 return FALSE;
118
119 return TRUE;
120 }
121
122
123 static FT_Bool
124 tt_face_validate_class_def( FT_Byte* table,
125 FT_Byte* table_limit,
126 FT_UInt num_classes )
127 {
128 FT_UInt format;
129
130 FT_Byte* p = table;
131 FT_Byte* limit;
132
133 FT_UInt max_class_value = 0;
134
135
136 if ( table_limit < p + 2 )
137 return FALSE;
138
139 format = FT_NEXT_USHORT( p );
140 if ( format == 1 )
141 {
142 FT_UInt glyphCount;
143
144
145 if ( table_limit < p + 4 )
146 return FALSE;
147
148 p += 2; /* Skip `startGlyphID`. */
149
150 glyphCount = FT_NEXT_USHORT( p );
151 limit = p + glyphCount * 2;
152 if ( table_limit < limit )
153 return FALSE;
154
155 while ( p < limit )
156 {
157 FT_UInt class_value = FT_NEXT_USHORT( p );
158
159
160 if ( class_value > max_class_value )
161 max_class_value = class_value;
162 }
163 }
164 else if ( format == 2 )
165 {
166 FT_UInt classRangeCount;
167 FT_Long last_id = -1;
168
169
170 if ( table_limit < p + 2 )
171 return FALSE;
172
173 classRangeCount = FT_NEXT_USHORT( p );
174 limit = p + classRangeCount * 6;
175 if ( table_limit < limit )
176 return FALSE;
177
178 while ( p < limit )
179 {
180 FT_UInt startGlyphID = FT_NEXT_USHORT( p );
181 FT_UInt endGlyphID = FT_NEXT_USHORT( p );
182 FT_UInt class_value = FT_NEXT_USHORT( p );
183
184
185 if ( startGlyphID > endGlyphID )
186 return FALSE;
187
188 if ( last_id >= startGlyphID )
189 return FALSE;
190 last_id = endGlyphID;
191
192 if ( class_value > max_class_value )
193 max_class_value = class_value;
194 }
195 }
196 else
197 return FALSE;
198
199 if ( max_class_value + 1 != num_classes )
200 return FALSE;
201
202 return TRUE;
203 }
204
205
206 static FT_Bool
207 tt_face_validate_feature( FT_Byte* table,
208 FT_Byte* table_limit,
209 FT_UInt use_lookup_table_size,
210 FT_Byte* use_lookup_table )
211 {
212 FT_UInt lookupIndexCount;
213
214 FT_Byte* p = table;
215 FT_Byte* limit;
216
217
218 if ( table_limit < p + 4 )
219 return FALSE;
220
221 p += 2; /* Skip `featureParamsOffset`. */
222
223 lookupIndexCount = FT_NEXT_USHORT( p );
224 limit = p + lookupIndexCount * 2;
225 if ( table_limit < limit )
226 return FALSE;
227
228 while ( p < limit )
229 {
230 FT_UInt lookup_index = FT_NEXT_USHORT( p );
231
232
233 if ( lookup_index >= use_lookup_table_size )
234 return FALSE;
235
236 use_lookup_table[lookup_index] = TRUE;
237 }
238
239 return TRUE;
240 }
241
242
243 static FT_Bool
244 tt_face_validate_feature_table( FT_Byte* table,
245 FT_Byte* table_limit,
246 FT_UInt use_lookup_table_size,
247 FT_Byte* use_lookup_table )
248 {
249 FT_UInt featureCount;
250
251 FT_Byte* p = table;
252 FT_Byte* limit;
253
254
255 if ( table_limit < p + 2 )
256 return FALSE;
257
258 featureCount = FT_NEXT_USHORT( p );
259 limit = p + featureCount * 6;
260 if ( table_limit < limit )
261 return FALSE;
262
263 /* We completely ignore GPOS script information */
264 /* and collect lookup tables of all 'kern' features. */
265 while ( p < limit )
266 {
267 FT_ULong featureTag = FT_NEXT_ULONG( p );
268 FT_UInt featureOffset = FT_NEXT_USHORT( p );
269
270
271 if ( featureTag == TTAG_kern )
272 {
273 if ( !tt_face_validate_feature( table + featureOffset,
274 table_limit,
275 use_lookup_table_size,
276 use_lookup_table ) )
277 return FALSE;
278 }
279 }
280
281 return TRUE;
282 }
283
284
285 static FT_Bool
286 tt_face_validate_pair_set( FT_Byte* table,
287 FT_Byte* table_limit )
288 {
289 FT_UInt pairValueCount;
290
291 FT_Byte* p = table;
292 FT_Byte* limit;
293
294 FT_Long last_id = -1;
295
296
297 if ( table_limit < p + 2 )
298 return FALSE;
299
300 /* For our purposes, the first value record only contains X advances */
301 /* while the second one is empty; a `PairValue` record has thus a */
302 /* size of four bytes. */
303 pairValueCount = FT_NEXT_USHORT( p );
304 limit = p + pairValueCount * 4;
305 if ( table_limit < limit )
306 return FALSE;
307
308 /* We validate the order of `secondGlyph` so that binary search works. */
309 while ( p < limit )
310 {
311 FT_UInt id = FT_NEXT_USHORT( p );
312
313
314 if ( last_id >= id )
315 return FALSE;
316
317 last_id = id;
318
319 p += 2; /* Skip `valueRecord1`. */
320 }
321
322 return TRUE;
323 }
324
325
326 static FT_Bool
327 tt_face_validate_pair_pos1( FT_Byte* table,
328 FT_Byte* table_limit,
329 FT_Bool* is_fitting )
330 {
331 FT_Byte* coverage;
332 FT_UInt valueFormat1;
333 FT_UInt valueFormat2;
334
335 /* Subtable format is already checked. */
336 FT_Byte* p = table + 2;
337 FT_Byte* limit;
338
339
340 /* The six bytes for the coverage table offset */
341 /* and the value formats are already checked. */
342 coverage = table + FT_NEXT_USHORT( p );
343
344 /* For the limited purpose of accessing the simplest type of kerning */
345 /* (similar to what FreeType's 'kern' table handling provides) we */
346 /* only consider tables that contains X advance values for the first */
347 /* glyph and no data for the second glyph. */
348 valueFormat1 = FT_NEXT_USHORT( p );
349 valueFormat2 = FT_NEXT_USHORT( p );
350 if ( valueFormat1 == 0x4 && valueFormat2 == 0 )
351 {
352 FT_UInt pairSetCount;
353
354
355 if ( table_limit < p + 2 )
356 return FALSE;
357
358 pairSetCount = FT_NEXT_USHORT( p );
359 limit = p + pairSetCount * 2;
360 if ( table_limit < limit )
361 return FALSE;
362
363 if ( !tt_face_validate_coverage( coverage,
364 table_limit,
365 pairSetCount ) )
366 return FALSE;
367
368 while ( p < limit )
369 {
370 FT_Byte* pair_set = table + FT_NEXT_USHORT( p );
371
372
373 if ( !tt_face_validate_pair_set( pair_set, table_limit ) )
374 return FALSE;
375 }
376
377 *is_fitting = TRUE;
378 }
379
380 return TRUE;
381 }
382
383
384 static FT_Bool
385 tt_face_validate_pair_pos2( FT_Byte* table,
386 FT_Byte* table_limit,
387 FT_Bool* is_fitting )
388 {
389 FT_Byte* coverage;
390 FT_UInt valueFormat1;
391 FT_UInt valueFormat2;
392
393 /* Subtable format is already checked. */
394 FT_Byte* p = table + 2;
395 FT_Byte* limit;
396
397
398 /* The six bytes for the coverage table offset */
399 /* and the value formats are already checked. */
400 coverage = table + FT_NEXT_USHORT( p );
401
402 valueFormat1 = FT_NEXT_USHORT( p );
403 valueFormat2 = FT_NEXT_USHORT( p );
404 if ( valueFormat1 == 0x4 && valueFormat2 == 0 )
405 {
406 FT_Byte* class_def1;
407 FT_Byte* class_def2;
408 FT_UInt class1Count;
409 FT_UInt class2Count;
410
411
412 /* The number of coverage indices is not relevant here. */
413 if ( !tt_face_validate_coverage( coverage, table_limit, FT_UINT_MAX ) )
414 return FALSE;
415
416 if ( table_limit < p + 8 )
417 return FALSE;
418
419 class_def1 = table + FT_NEXT_USHORT( p );
420 class_def2 = table + FT_NEXT_USHORT( p );
421 class1Count = FT_NEXT_USHORT( p );
422 class2Count = FT_NEXT_USHORT( p );
423
424 if ( !tt_face_validate_class_def( class_def1,
425 table_limit,
426 class1Count ) )
427 return FALSE;
428 if ( !tt_face_validate_class_def( class_def2,
429 table_limit,
430 class2Count ) )
431 return FALSE;
432
433 /* For our purposes, the first value record only contains */
434 /* X advances while the second one is empty. */
435 limit = p + class1Count * class2Count * 2;
436 if ( table_limit < limit )
437 return FALSE;
438
439 *is_fitting = TRUE;
440 }
441
442 return TRUE;
443 }
444
445
446 /* The return value is the number of fitting subtables. */
447 static FT_UInt
448 tt_face_validate_lookup_table( FT_Byte* table,
449 FT_Byte* table_limit )
450 {
451 FT_UInt lookupType;
452 FT_UInt real_lookupType = 0;
453 FT_UInt subtableCount;
454
455 FT_Byte* p = table;
456 FT_Byte* limit;
457
458 FT_UInt num_fitting_subtables = 0;
459
460
461 if ( table_limit < p + 6 )
462 return 0;
463
464 lookupType = FT_NEXT_USHORT( p );
465
466 p += 2; /* Skip `lookupFlag`. */
467
468 subtableCount = FT_NEXT_USHORT( p );
469 limit = p + subtableCount * 2;
470 if ( table_limit < limit )
471 return 0;
472
473 while ( p < limit )
474 {
475 FT_Byte* subtable = table + FT_NEXT_USHORT( p );
476 FT_UInt format;
477
478 FT_Bool is_fitting = FALSE;
479
480
481 if ( lookupType == 9 )
482 {
483 /* Positioning extension. */
484 FT_Byte* q = subtable;
485
486
487 if ( table_limit < q + 8 )
488 return 0;
489
490 if ( FT_NEXT_USHORT( q ) != 1 ) /* format */
491 return 0;
492
493 if ( real_lookupType == 0 )
494 real_lookupType = FT_NEXT_USHORT( q );
495 else if ( real_lookupType != FT_NEXT_USHORT( q ) )
496 return 0;
497
498 subtable += FT_PEEK_ULONG( q );
499 }
500 else
501 real_lookupType = lookupType;
502
503 /* Ensure the first eight bytes of the subtable formats. */
504 if ( table_limit < subtable + 8 )
505 return 0;
506
507 format = FT_PEEK_USHORT( subtable );
508
509 if ( real_lookupType == 2 )
510 {
511 if ( format == 1 )
512 {
513 if ( !tt_face_validate_pair_pos1( subtable,
514 table_limit,
515 &is_fitting ) )
516 return 0;
517 }
518 else if ( format == 2 )
519 {
520 if ( !tt_face_validate_pair_pos2( subtable,
521 table_limit,
522 &is_fitting ) )
523 return 0;
524 }
525 else
526 return 0;
527 }
528 else
529 return 0;
530
531 if ( is_fitting )
532 num_fitting_subtables++;
533 }
534
535 return num_fitting_subtables;
536 }
537
538
539 static void
540 tt_face_get_subtable_offsets( FT_Byte* table,
541 FT_Byte* gpos,
542 FT_UInt32* gpos_lookups_kerning,
543 FT_UInt* idx )
544 {
545 FT_UInt lookupType;
546 FT_UInt subtableCount;
547
548 FT_Byte* p = table;
549 FT_Byte* limit;
550
551
552 lookupType = FT_NEXT_USHORT( p );
553
554 p += 2;
555
556 subtableCount = FT_NEXT_USHORT( p );
557 limit = p + subtableCount * 2;
558 while ( p < limit )
559 {
560 FT_Byte* subtable = table + FT_NEXT_USHORT( p );
561 FT_UInt valueFormat1;
562 FT_UInt valueFormat2;
563
564
565 if ( lookupType == 9 )
566 subtable += FT_PEEK_ULONG( subtable + 4 );
567
568 /* Table offsets for `valueFormat[12]` values */
569 /* are identical for both subtable formats. */
570 valueFormat1 = FT_PEEK_USHORT( subtable + 4 );
571 valueFormat2 = FT_PEEK_USHORT( subtable + 6 );
572 if ( valueFormat1 == 0x4 && valueFormat2 == 0 )
573 {
574 /* We store offsets relative to the start of the GPOS table. */
575 gpos_lookups_kerning[(*idx)++] = (FT_UInt32)( subtable - gpos );
576 }
577 }
578 }
579
580
581 FT_LOCAL_DEF( FT_Error )
582 tt_face_load_gpos( TT_Face face,
583 FT_Stream stream )
584 {
585 FT_Error error;
586 FT_Memory memory = face->root.memory;
587
588 FT_ULong gpos_length;
589 FT_Byte* gpos;
590 FT_Byte* gpos_limit;
591
592 FT_UInt32* gpos_lookups_kerning;
593
594 FT_UInt featureListOffset;
595
596 FT_UInt lookupListOffset;
597 FT_Byte* lookup_list;
598 FT_UInt lookupCount;
599
600 FT_UInt i;
601
602 FT_Byte* use_lookup_table = NULL;
603 FT_UInt num_fitting_subtables;
604
605 FT_Byte* p;
606 FT_Byte* limit;
607
608
609 face->gpos_table = NULL;
610 face->gpos_lookups_kerning = NULL;
611 face->num_gpos_lookups_kerning = 0;
612
613 gpos = NULL;
614 gpos_lookups_kerning = NULL;
615
616 error = face->goto_table( face, TTAG_GPOS, stream, &gpos_length );
617 if ( error )
618 goto Fail;
619
620 if ( FT_FRAME_EXTRACT( gpos_length, gpos ) )
621 goto Fail;
622
623 if ( gpos_length < 10 )
624 goto Fail;
625
626 gpos_limit = gpos + gpos_length;
627
628 /* We first need the number of GPOS lookups. */
629 lookupListOffset = FT_PEEK_USHORT( gpos + 8 );
630
631 lookup_list = gpos + lookupListOffset;
632 p = lookup_list;
633 if ( gpos_limit < p + 2 )
634 goto Fail;
635
636 lookupCount = FT_NEXT_USHORT( p );
637 limit = p + lookupCount * 2;
638 if ( gpos_limit < limit )
639 goto Fail;
640
641 /* Allocate an auxiliary array for Boolean values that */
642 /* gets filled while walking over all 'kern' features. */
643 if ( FT_NEW_ARRAY( use_lookup_table, lookupCount ) )
644 goto Fail;
645
646 featureListOffset = FT_PEEK_USHORT( gpos + 6 );
647
648 if ( !tt_face_validate_feature_table( gpos + featureListOffset,
649 gpos_limit,
650 lookupCount,
651 use_lookup_table ) )
652 goto Fail;
653
654 /* Now walk over all lookup tables and get the */
655 /* number of fitting subtables. */
656 num_fitting_subtables = 0;
657 for ( i = 0; i < lookupCount; i++ )
658 {
659 FT_UInt lookupOffset;
660
661
662 if ( !use_lookup_table[i] )
663 continue;
664
665 lookupOffset = FT_PEEK_USHORT( p + i * 2 );
666
667 num_fitting_subtables +=
668 tt_face_validate_lookup_table( lookup_list + lookupOffset,
669 gpos_limit );
670
671 }
672
673 /* Loop again over all lookup tables and */
674 /* collect offsets to those subtables. */
675 if ( num_fitting_subtables )
676 {
677 FT_UInt idx;
678
679
680 if ( FT_QNEW_ARRAY( gpos_lookups_kerning, num_fitting_subtables ) )
681 goto Fail;
682
683 idx = 0;
684 for ( i = 0; i < lookupCount; i++ )
685 {
686 FT_UInt lookupOffset;
687
688
689 if ( !use_lookup_table[i] )
690 continue;
691
692 lookupOffset = FT_PEEK_USHORT( p + i * 2 );
693
694 tt_face_get_subtable_offsets( lookup_list + lookupOffset,
695 gpos,
696 gpos_lookups_kerning,
697 &idx );
698 }
699 }
700
701 FT_FREE( use_lookup_table );
702 use_lookup_table = NULL;
703
704 face->gpos_table = gpos;
705 face->gpos_lookups_kerning = gpos_lookups_kerning;
706 face->num_gpos_lookups_kerning = num_fitting_subtables;
707
708 Exit:
709 return error;
710
711 Fail:
712 FT_FREE( gpos );
713 FT_FREE( gpos_lookups_kerning );
714 FT_FREE( use_lookup_table );
715
716 /* If we don't have an explicit error code, set it to a generic value. */
717 if ( !error )
718 error = FT_THROW( Invalid_Table );
719
720 goto Exit;
721 }
722
723
724 FT_LOCAL_DEF( void )
725 tt_face_done_gpos( TT_Face face )
726 {
727 FT_Stream stream = face->root.stream;
728 FT_Memory memory = face->root.memory;
729
730
731 FT_FRAME_RELEASE( face->gpos_table );
732 FT_FREE( face->gpos_lookups_kerning );
733 }
734
735
736 /*********************************/
737 /******** ********/
738 /******** GPOS access ********/
739 /******** ********/
740 /*********************************/
741
742
743 static FT_Long
744 tt_face_get_coverage_index( FT_Byte* table,
745 FT_UInt glyph_index )
746 {
747 FT_Byte* p = table;
748 FT_UInt format = FT_NEXT_USHORT( p );
749 FT_UInt count = FT_NEXT_USHORT( p );
750
751 FT_UInt min, max;
752
753
754 min = 0;
755 max = count;
756
757 if ( format == 1 )
758 {
759 while ( min < max )
760 {
761 FT_UInt mid = min + ( max - min ) / 2;
762 FT_UInt mid_index = FT_PEEK_USHORT( p + mid * 2 );
763
764
765 if ( glyph_index > mid_index )
766 min = mid + 1;
767 else if ( glyph_index < mid_index )
768 max = mid;
769 else
770 return mid;
771 }
772 }
773 else
774 {
775 while ( min < max )
776 {
777 FT_UInt mid = min + ( max - min ) / 2;
778 FT_UInt startGlyphID = FT_PEEK_USHORT( p + mid * 6 );
779 FT_UInt endGlyphID = FT_PEEK_USHORT( p + mid * 6 + 2 );
780
781
782 if ( glyph_index > endGlyphID )
783 min = mid + 1;
784 else if ( glyph_index < startGlyphID )
785 max = mid;
786 else
787 {
788 FT_UInt startCoverageIndex = FT_PEEK_USHORT( p + mid * 6 + 4 );
789
790
791 return startCoverageIndex + glyph_index - startGlyphID;
792 }
793 }
794 }
795
796 return -1;
797 }
798
799
800 static FT_UInt
801 tt_face_get_class( FT_Byte* table,
802 FT_UInt glyph_index )
803 {
804 FT_Byte* p = table;
805 FT_UInt format = FT_NEXT_USHORT( p );
806
807
808 if ( format == 1 )
809 {
810 FT_UInt startGlyphID = FT_NEXT_USHORT( p );
811 FT_UInt glyphCount = FT_NEXT_USHORT( p );
812
813
814 /* XXX: Is this modulo 65536 arithmetic? */
815 if ( startGlyphID <= glyph_index &&
816 startGlyphID + glyphCount >= glyph_index )
817 return FT_PEEK_USHORT( p + ( glyph_index - startGlyphID ) * 2 );
818 }
819 else
820 {
821 FT_UInt count = FT_NEXT_USHORT( p );
822
823 FT_UInt min, max;
824
825
826 min = 0;
827 max = count;
828
829 while ( min < max )
830 {
831 FT_UInt mid = min + ( max - min ) / 2;
832 FT_UInt startGlyphID = FT_PEEK_USHORT( p + mid * 6 );
833 FT_UInt endGlyphID = FT_PEEK_USHORT( p + mid * 6 + 2 );
834
835
836 if ( glyph_index > endGlyphID )
837 min = mid + 1;
838 else if ( glyph_index < startGlyphID )
839 max = mid;
840 else
841 return FT_PEEK_USHORT( p + mid * 6 + 4 );
842 }
843 }
844
845 return 0;
846 }
847
848
849 static FT_Bool
850 tt_face_get_pair_pos1_kerning( FT_Byte* table,
851 FT_UInt first_glyph,
852 FT_UInt second_glyph,
853 FT_Int* kerning )
854 {
855 FT_Byte* coverage = table + FT_PEEK_USHORT( table + 2 );
856 FT_Long coverage_index = tt_face_get_coverage_index( coverage,
857 first_glyph );
858
859 FT_UInt pair_set_offset;
860 FT_Byte* p;
861 FT_UInt count;
862
863 FT_UInt min, max;
864
865
866 if ( coverage_index < 0 )
867 return FALSE;
868
869 pair_set_offset = FT_PEEK_USHORT( table + 10 + coverage_index * 2 );
870 p = table + pair_set_offset;
871 count = FT_NEXT_USHORT( p );
872
873 min = 0;
874 max = count;
875
876 while ( min < max )
877 {
878 FT_UInt mid = min + ( max - min ) / 2;
879 FT_UInt mid_index = FT_PEEK_USHORT( p + mid * 4 );
880
881
882 if ( second_glyph > mid_index )
883 min = max + 1;
884 else if ( second_glyph < mid_index )
885 max = mid;
886 else
887 {
888 *kerning = FT_PEEK_SHORT( p + mid * 4 + 2 );
889
890 return TRUE;
891 }
892 }
893
894 return FALSE;
895 }
896
897
898 static FT_Bool
899 tt_face_get_pair_pos2_kerning( FT_Byte* table,
900 FT_UInt first_glyph,
901 FT_UInt second_glyph,
902 FT_Int* kerning )
903 {
904 FT_Byte* coverage = table + FT_PEEK_USHORT( table + 2 );
905 FT_Long coverage_index = tt_face_get_coverage_index( coverage,
906 first_glyph );
907
908 FT_Byte* class_def1;
909 FT_Byte* class_def2;
910 FT_UInt first_class;
911 FT_UInt second_class;
912 FT_UInt class2Count;
913
914
915 if ( coverage_index < 0 )
916 return FALSE;
917
918 class_def1 = table + FT_PEEK_USHORT( table + 8 );
919 class_def2 = table + FT_PEEK_USHORT( table + 10 );
920
921 class2Count = FT_PEEK_USHORT( table + 14 );
922
923 first_class = tt_face_get_class( class_def1, first_glyph );
924 second_class = tt_face_get_class( class_def2, second_glyph );
925
926 *kerning =
927 FT_PEEK_SHORT( table + 16 +
928 ( first_class * class2Count + second_class ) * 2 );
929
930 return TRUE;
931 }
932
933
934 FT_LOCAL_DEF( FT_Int )
935 tt_face_get_gpos_kerning( TT_Face face,
936 FT_UInt first_glyph,
937 FT_UInt second_glyph )
938 {
939 FT_Int kerning = 0;
940
941 FT_UInt i;
942
943
944 /* We only have `PairPos` subtables. */
945 for ( i = 0; i < face->num_gpos_lookups_kerning; i++ )
946 {
947 FT_Byte* subtable = face->gpos_table + face->gpos_lookups_kerning[i];
948 FT_Byte* p = subtable;
949
950 FT_UInt format = FT_NEXT_USHORT( p );
951
952
953 if ( format == 1 )
954 {
955 if ( tt_face_get_pair_pos1_kerning( subtable,
956 first_glyph,
957 second_glyph,
958 &kerning ) )
959 break;
960 }
961 else
962 {
963 if ( tt_face_get_pair_pos2_kerning( subtable,
964 first_glyph,
965 second_glyph,
966 &kerning ) )
967 break;
968 }
969 }
970
971 return kerning;
972 }
973
974#else /* !TT_CONFIG_OPTION_GPOS_KERNING */
975
976 /* ANSI C doesn't like empty source files */
977 typedef int tt_gpos_dummy_;
978
979#endif /* !TT_CONFIG_OPTION_GPOS_KERNING */
980
981
982/* END */