summaryrefslogtreecommitdiff
path: root/src/gxlayout/gxstatetbl.c
blob: 53dafefd87df10dd9a729856f7a4083947781837 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
/***************************************************************************/
/*                                                                         */
/*  gxstatetbl.c                                                           */
/*                                                                         */
/*    AAT/TrueTypeGX state table related types and functions               */
/*    (body).                                                              */
/*                                                                         */
/*  Copyright 2003 by                                                      */
/*  Masatake YAMATO and Redhat K.K.                                        */
/*                                                                         */
/*  This file may only be used,                                            */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/* Development of the code in this file is support of                      */
/* Information-technology Promotion Agency, Japan.                         */
/***************************************************************************/

#include <ft2build.h>

#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_MEMORY_H

#include "gxstatetbl.h"
#include "gxlookuptbl.h"
#include "gxerrors.h"

  static FT_Error
  gx_StateTable_load_header( GX_Face face,
			     FT_Stream stream,
			     GX_StateHeader header )
  {
    FT_Error error;
    const FT_Frame_Field state_header_fields[] =
      {
#undef  FT_STRUCTURE
#define FT_STRUCTURE GX_StateHeaderRec
       FT_FRAME_START ( 8 ),
	   FT_FRAME_USHORT ( stateSize ),
	   FT_FRAME_USHORT ( classTable ),
	   FT_FRAME_USHORT ( stateArray ),
	   FT_FRAME_USHORT ( entryTable ),
       FT_FRAME_END
      };
    header->position    = FT_STREAM_POS();
    return FT_STREAM_READ_FIELDS( state_header_fields, header );
  }

  static FT_Error
  gx_StateTable_load_class_subtable( GX_Face face,
				     FT_Stream stream,
				     FT_ULong pos,
				     GX_ClassSubtable subtable )
  {
    FT_Error error;
    FT_Memory memory = stream->memory;
    FT_Byte * classArray;
    FT_Int i; 
    const FT_Frame_Field class_subtable_fields[] =
      {
#undef  FT_STRUCTURE
#define FT_STRUCTURE GX_ClassSubtableRec
	FT_FRAME_START ( 4 ),
	    FT_FRAME_USHORT ( firstGlyph ),
	    FT_FRAME_USHORT ( nGlyphs ),
	FT_FRAME_END
      }; 
    
    subtable->classArray = NULL;

    if ( FT_STREAM_SEEK(pos) )
      goto Exit;
    
    if ( FT_STREAM_READ_FIELDS ( class_subtable_fields,  subtable ) )
      goto Exit;

    if ( FT_NEW_ARRAY ( classArray, subtable->nGlyphs ) )
      goto Exit;

    if ( FT_FRAME_ENTER( sizeof (classArray[0]) * subtable->nGlyphs ) )
      goto Failure;
    
    for ( i = 0; i < subtable->nGlyphs; i++ )
      classArray[i] = FT_GET_BYTE();
    
    FT_FRAME_EXIT();
    subtable->classArray = classArray;
  Exit:
    return error;
  Failure:
    FT_FREE( classArray );
    return error;
  }

  static void
  gx_StateTable_free_class_subtable( FT_Memory memory,
				     GX_ClassSubtable subtable )
  {
    FT_FREE(subtable->classArray);
    subtable->classArray = NULL;
  }

  static FT_Error
  gx_StateTable_load_state_array( GX_Face   face,
				  FT_Stream stream,
				  FT_ULong  pos,
				  FT_UShort length,
				  FT_Byte * state_array )
  {
    FT_Error error;
    FT_Int i;

    if ( FT_STREAM_SEEK(pos) )
      goto Exit;
    
    if ( FT_FRAME_ENTER( sizeof (state_array[0]) * length ) )
      goto Exit;

    for ( i = 0; i < length; i++ )
      state_array[i] = FT_GET_BYTE();
	
    FT_FRAME_EXIT();
  Exit:
    return error;
  }

/* - gx_StateTable_load_entry_subtable
   I assume FUNCS is not NULL. Set any kind of dummy in the caller if
   necessary. 
   To support both a state table and an extended state table, the type of nEntries 
   is FT_ULong. */
  static FT_Error
  gx_StateTable_load_entry_subtable ( GX_Face   face,
				      FT_Stream stream,
				      FT_ULong  pos,
				      FT_ULong  nEntries,
				      GX_EntrySubtable entry_subtable,
				      GX_StateTable_Entry_Load_Funcs funcs,
				      FT_Pointer user )
  {
    FT_Error error;
    FT_Memory memory = face->root.driver->root.memory;
    FT_ULong i, j;
    
    const FT_Frame_Field entry_subtable_fields[] =
      {
#undef  FT_STRUCTURE
#define FT_STRUCTURE GX_EntrySubtableRec
	FT_FRAME_START ( 4 ),
	    FT_FRAME_USHORT ( newState ),
	    FT_FRAME_USHORT ( flags ),
	FT_FRAME_END
      }; 

    if ( FT_STREAM_SEEK( pos ) )
      goto Exit;

    for ( i = 0; i < nEntries; i++ )
      {
	if ( FT_STREAM_READ_FIELDS ( entry_subtable_fields,  &entry_subtable[i] ) )
	  goto Failure;
	if (( error = funcs->loader(face, stream, &entry_subtable[i], user) ))
	  goto Failure;
      }
  Exit:
    return error;
  Failure:
    for ( j = i; j > 0; j-- )
      funcs->finalizer(memory, &entry_subtable[j - 1], user);
    return error;
  }

  static void
  gx_StateTable_free_entry_subtable ( FT_Memory memory,
				      FT_Byte   nEntries,
				      GX_EntrySubtable entry_subtable,
				      GX_StateTable_Entry_Finalizer finalizer,
				      FT_Pointer user )
  {
    FT_Int i;
    for ( i = 0; i < nEntries; i++ )
      finalizer(memory, &entry_subtable[i], user);
  }

  static FT_Error 
  gx_StateTable_Entry_default_loader ( GX_Face face,
				       FT_Stream stream,
				       GX_EntrySubtable entry_subtable,
				       FT_Pointer user )
  {
    entry_subtable->glyphOffsets.any = NULL;
    return GX_Err_Ok;
  }

  static void
  gx_StateTable_Entry_default_finalizer ( FT_Memory memory,
					  GX_EntrySubtable entry_subtable,
					  FT_Pointer user )
 {
   /* Do nothing */;
 }

  static FT_Byte
  gx_StateTable_find_state_array_max_index (FT_UShort array_length, FT_Byte state_array[])
  {
    FT_Int i;
    FT_Byte max_index = 0;
    for ( i = 0; i < array_length; i++ )
      {
	/* fprintf(stderr, "->%u\n", state_array[i]); */
	if ( state_array[i] > max_index )
	  max_index = state_array [i];
      }
    return max_index;
  }
				      
  FT_LOCAL_DEF ( FT_Error )
  gx_face_load_StateTable ( GX_Face face,
			    FT_Stream stream,
			    GX_StateTable state_table,
			    GX_StateTable_Entry_Load_Funcs funcs,
			    FT_Pointer user )

  {
    FT_Error error;
    FT_Memory memory = stream->memory;
    GX_StateHeader header;
    GX_ClassSubtable class_subtable;
    FT_Byte * state_array = NULL;
    FT_UShort state_array_len;
    GX_EntrySubtable entry_subtable = NULL;
    FT_ULong pos;
    GX_StateTable_Entry_Load_FuncsRec default_funcs = GX_STATE_TABLE_ENTRY_LOAD_FUNCS_ZERO;

    default_funcs.loader    = gx_StateTable_Entry_default_loader;
    default_funcs.finalizer = gx_StateTable_Entry_default_finalizer;

    state_table->state_array = NULL;
    state_table->entry_subtable = NULL;
    
    /* 1. Header */
    header = &state_table->header;
    if (( error  = gx_StateTable_load_header( face, stream,
					      header ) ))
      goto Exit;

    /* 2. class subtable */    
    pos 	   = header->position + header->classTable;
    class_subtable = &state_table->class_subtable;
    if (( error = gx_StateTable_load_class_subtable( face,
						     stream,
						     pos,
						     class_subtable ) ))
      goto Exit;


    /* 3. state array */    
    /* To calculate the length of stateArray, we assume
       the order of fields placement is classTable, stateArray, 
       entryTable */
    FT_ASSERT( header->classTable < header->stateArray );
    FT_ASSERT( header->stateArray < header->entryTable );

    pos 	    = header->position + header->stateArray;
    state_array_len = header->entryTable - header->stateArray;
    state_table->nStates = state_array_len / header->stateSize;
    /* Calculate state_array_len again. 
       state_array_len must be a multiple of header->stateSize. */
    state_array_len = state_table->nStates  * header->stateSize;
    if (( FT_NEW_ARRAY ( state_array, state_array_len ) ))
      goto Failure;
    
    if (( error = gx_StateTable_load_state_array( face,
						  stream,
						  pos,
						  state_array_len,
						  state_array ) ))
      goto Failure;
    
    /* 4. entry subtable */
    if ( funcs )
      {
	if (! funcs->loader )
	  funcs->loader = gx_StateTable_Entry_default_loader;
	if (! funcs->finalizer )
	  funcs->finalizer = gx_StateTable_Entry_default_finalizer;
      }      
    else
      funcs = &default_funcs;

    pos = header->position + header->entryTable;
    /* gx_StateTable_find_state_array_max_index returns the max index into an array
       which starts from 0. By adding 1 to the max index, get the length of the array. */
    state_table->nEntries = 1+ gx_StateTable_find_state_array_max_index(state_array_len,
									state_array);
    
    if (( FT_NEW_ARRAY( entry_subtable, state_table->nEntries) ))
      goto Failure;
    if (( error = gx_StateTable_load_entry_subtable( face, 
						     stream,
						     pos,
						     state_table->nEntries,
						     entry_subtable,
						     funcs,
						     user) ))
      goto Failure;

    state_table->state_array 	= state_array;
    state_table->entry_subtable = entry_subtable;
  Exit:
    return error;
  Failure:
    if ( entry_subtable )
      FT_FREE ( entry_subtable );
    if ( state_array )
      FT_FREE(state_array);
    gx_StateTable_free_class_subtable (memory, class_subtable);
    return error;
  }

  FT_LOCAL_DEF ( void )
  gx_StateTable_free ( GX_StateTable state_table,
		       FT_Memory memory,
		       GX_StateTable_Entry_Finalizer finalizer,
		       FT_Pointer user )
  {
    if ( finalizer == NULL )
      finalizer = gx_StateTable_Entry_default_finalizer;
    gx_StateTable_free_entry_subtable ( memory, 
					state_table->nEntries,
					state_table->entry_subtable,
					finalizer,
					user );
    FT_FREE( state_table->entry_subtable );
    state_table->entry_subtable = NULL;

    FT_FREE( state_table->state_array );
    state_table->state_array = NULL;

    gx_StateTable_free_class_subtable( memory, &state_table->class_subtable );
  }

  static FT_Error
  gx_EntrySubtable_traverse( GX_EntrySubtable entries,
			     FT_Long nEntries,
			     GX_StateTable_Entry_Action action,
			     FT_Pointer user )
  {
    FT_Error error = GX_Err_Ok;
    FT_Int i;

    if (!action)
      return error;

    for ( i = 0; i < nEntries; i++ )
      {
	error = action( &entries[i], user );
	if ( error )
	  return error;
      }
    return error;
  }

#if 0
  FT_LOCAL_DEF ( FT_Error )
  gx_StateTable_traverse_entries( GX_StateTable state_table,
				  GX_StateTable_Entry_Action action,
				  FT_Pointer user )
 {
      return gx_EntrySubtable_traverse( state_table->entry_subtable,
					state_table->nEntries,
					action, 
					user );
  }
#endif /* 0 */

  FT_LOCAL_DEF ( FT_Byte )
  gx_StateTable_get_class ( GX_StateTable state_table,
			    FT_UShort glyph )
  {
    GX_ClassSubtable class_subtable;
    FT_Byte class_code;
    FT_UShort first_glyph, last_glyph;
    
    class_subtable = &state_table->class_subtable;
    first_glyph    = class_subtable->firstGlyph;
    last_glyph     = first_glyph + class_subtable->nGlyphs;
    
    if ( glyph == GX_DELETED_GLYPH_INDEX )
      class_code = GX_CLASS_DELETED_GLYPH;
    else if ( ( first_glyph <= glyph ) && ( glyph < last_glyph ) )
      class_code = class_subtable->classArray[glyph - first_glyph];
    else
      class_code = GX_CLASS_OUT_OF_BOUNDS;
    return class_code;
  }
	
  FT_LOCAL_DEF ( GX_EntrySubtable )
  gx_StateTable_get_entry_subtable ( GX_StateTable state_table,
				     FT_UShort current_state,
				     FT_Byte class_code )
  {
    GX_StateHeader header 	    = &state_table->header;
    FT_Byte * state_array 	    = state_table->state_array;
    GX_EntrySubtable entry_subtable = state_table->entry_subtable;
    FT_Byte * state_array_for_current_state;
    FT_Byte entry_index;
    
    state_array_for_current_state = &state_array[current_state - header->stateArray];
    entry_index 		  = state_array_for_current_state[class_code];
    return &entry_subtable[entry_index];
  }

/*
 * Extended State Table
 */

#define gx_XStateTable_load_entry_subtable gx_StateTable_load_entry_subtable 
#define gx_XStateTable_free_entry_subtable gx_StateTable_free_entry_subtable 

#define gx_XStateTable_Entry_default_loader gx_StateTable_Entry_default_loader
#define gx_XStateTable_Entry_default_finalizer gx_StateTable_Entry_default_finalizer
#define gx_XStateTable_Entry_default_finalizer gx_StateTable_Entry_default_finalizer

  static FT_Error
  gx_XStateTable_load_header( GX_Face face,
			     FT_Stream stream,
			     GX_XStateHeader header )
  {
    FT_Error error;
    const FT_Frame_Field xstate_header_fields[] =
      {
#undef FT_STRUCTURE
#define FT_STRUCTURE GX_XStateHeaderRec
	FT_FRAME_START ( 16 ),
	    FT_FRAME_ULONG( nClasses ),
	    FT_FRAME_ULONG( classTableOffset ),
	    FT_FRAME_ULONG( stateArrayOffset ),
	    FT_FRAME_ULONG( entryTableOffset ),
	FT_FRAME_END
      };
    header->position = FT_STREAM_POS();
    return FT_STREAM_READ_FIELDS( xstate_header_fields, header );
  }

  static FT_Error
  gx_XStateTable_load_state_array( GX_Face   face,
				   FT_Stream  stream,
				   FT_ULong   pos,
				   FT_ULong   length,
				   FT_UShort* state_array )
  {
    FT_Error error;
    FT_Int i;

    if ( FT_STREAM_SEEK(pos) )
      goto Exit;
    
    if ( FT_FRAME_ENTER( sizeof (state_array[0]) * length ) )
      goto Exit;

    for ( i = 0; i < length; i++ )
      state_array[i] = FT_GET_USHORT();
	
    FT_FRAME_EXIT();
  Exit:
    return error;
  }

  static FT_UShort
  gx_XStateTable_find_state_array_max_index (FT_ULong array_length, FT_UShort state_array[])
  {
    FT_ULong i;
    FT_UShort max_index = 0;
    for ( i = 0; i < array_length; i++ )
      {
	if ( state_array[i] > max_index )
	  max_index = state_array [i];
      }
    return max_index;
  }

#define LOOKUP_TABLE_CB_DATA_ZERO {NULL, NULL}
  typedef struct lookup_table_cb_data_rec_
  {
    FT_Stream stream;
    GX_LookupTable lookup_table;
    FT_Long        table_end;
  } lookup_table_cb_data_rec, *lookup_table_cb_data;

  static FT_Error
  gx_XStateTable_LookupTable_segment_array_loader( GX_LookupTable_Format format,
						   FT_UShort lastGlyph,
						   FT_UShort firstGlyph,
						   GX_LookupValue value,
						   FT_Pointer user )
  {
    /* generic_lookup_table_segment_array_loader */
    FT_Error error;
    lookup_table_cb_data lookup_data = user;
    FT_Stream stream                 = lookup_data->stream;
    GX_LookupTable lookup_table      = lookup_data->lookup_table;
    FT_Memory memory = stream->memory;
    FT_Short  value_offset  = value->raw.s;
    FT_UShort segment_count = lastGlyph - firstGlyph + 1;
    FT_UShort * segment;
    
    FT_Int i;

    /* -----------------------------------------------------------------
     * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 
     * This code is correct?
     * In spec: "Sometimes they are 16-bit offsets from the start of 
     * the table to the data. " The table? Is the table the lookup table
     * or a table that uses the lookup table?
     * Here I assume the table is the table that uses the lookup table.
     * However, I have no conviction. 
     * It seems that  pfaedit uses lookup_table_offset + value_offset.
     * ----------------------------------------------------------------- */
    FT_ASSERT(lookup_table->position + value_offset < lookup_data->table_end);
    if (FT_STREAM_SEEK( lookup_table->position + value_offset ) ) 
      goto Exit;
    
    if ( FT_NEW_ARRAY(segment, segment_count ) )
      goto Exit;

    if ( FT_FRAME_ENTER ( sizeof( segment[0] ) * segment_count ) )
      goto Failure;
    for ( i = 0; i < segment_count; i++ )
      segment[i] = FT_GET_USHORT();
    FT_FRAME_EXIT();
    value->extra.word = segment;
  Exit:
    return error;
  Failure:
    /* TODO
       Other value->extra.wordS loaded before the visitation to this 
       value->extra.word must be freed if an error is occurred during 
       traverse. */
    FT_FREE(segment);
    return error;
  }

  FT_LOCAL_DEF ( FT_Error )
  gx_face_load_XStateTable ( GX_Face face,
			     FT_Stream stream,
			     GX_XStateTable state_table,
			     GX_XStateTable_Entry_Load_Funcs funcs,
			     FT_Pointer user )
  {
    FT_Error error;
    FT_Memory memory = stream->memory;
    GX_XStateHeader header;
    FT_ULong state_array_len;	/* in word */
    FT_UShort * state_array = NULL;
    GX_EntrySubtable entry_subtable = NULL;
    FT_ULong pos;
    GX_XStateTable_Entry_Load_FuncsRec default_XStateTable_funcs = GX_XSTATE_TABLE_ENTRY_LOAD_FUNCS_ZERO;
    GX_LookupTable_FuncsRec default_LookupTable_funcs = GX_LOOKUP_TABLE_FUNC_ZERO;
    lookup_table_cb_data_rec lookup_table_cb_data = LOOKUP_TABLE_CB_DATA_ZERO;

    default_XStateTable_funcs.loader = gx_XStateTable_Entry_default_loader;
    default_XStateTable_funcs.finalizer = gx_XStateTable_Entry_default_finalizer;

    default_LookupTable_funcs.segment_array_func = gx_XStateTable_LookupTable_segment_array_loader;

    state_table->state_array 			  = NULL;
    state_table->entry_subtable 		  = NULL;

    
    /* 1. Header */
    header = &state_table->header;
    if (( error = gx_XStateTable_load_header( face, stream, header ) ))
      goto Exit;

    /* 2. class subtable */        
    pos = header->position + header->classTableOffset;
    if ( FT_STREAM_SEEK( pos ) )
      goto Exit;

    if (( error = gx_face_load_LookupTable( face, 
					    stream, 
					    &state_table->class_subtable ) ))
      goto Exit;

    if ( state_table->class_subtable.format == GX_LOOKUPTABLE_SEGMENT_ARRAY )
      {
	
	lookup_table_cb_data.stream       = stream;
	lookup_table_cb_data.lookup_table = &state_table->class_subtable;
	lookup_table_cb_data.table_end = header->position + header->stateArrayOffset;
	if (( error = gx_LookupTable_traverse_low( & state_table->class_subtable,
						    & default_LookupTable_funcs,
						    & lookup_table_cb_data ) ))
	  goto Failure;
      }
    
    /* 3. state array */    
    /* To calculate the length of stateArray, we assume
       the order of fields placement is classTable, stateArray, 
       entryTable */
    FT_ASSERT( header->classTableOffset < header->stateArrayOffset );
    FT_ASSERT( header->stateArrayOffset < header->entryTableOffset );
    
    pos = header->position + header->stateArrayOffset;
    state_array_len = (header->entryTableOffset - header->stateArrayOffset) / sizeof( state_array[0] ); 
    state_table->nStates = state_array_len / header->nClasses;
    /* Calculate state_array_len again. 
       state_array_len must be a multiple of header->nClasses. */    
    state_array_len = state_table->nStates  * header->nClasses;

    if (( FT_NEW_ARRAY ( state_array, state_array_len ) ))
      goto Failure;
    
    if (( error = gx_XStateTable_load_state_array( face,
						   stream,
						   pos,
						   state_array_len,
						   state_array ) ))
      goto Failure;

    /* 4. entry subtable */
    if ( funcs )
      {
	if (! funcs->loader )
	  funcs->loader = gx_XStateTable_Entry_default_loader;
	if (! funcs->finalizer )
	  funcs->finalizer = gx_XStateTable_Entry_default_finalizer;
      }      
    else
      funcs = &default_XStateTable_funcs;

    pos = header->position + header->entryTableOffset;
    state_table->nEntries = 1+ gx_XStateTable_find_state_array_max_index( state_array_len,
									  state_array );
    if (( FT_NEW_ARRAY( entry_subtable, state_table->nEntries) ))
      goto Failure;
    if (( error = gx_XStateTable_load_entry_subtable( face, 
						      stream,
						      pos,
						      state_table->nEntries,
						      entry_subtable,
						      funcs,
						      user) ))
      goto Failure;
    state_table->state_array 	= state_array;
    state_table->entry_subtable = entry_subtable;
  Exit:
    return error;
  Failure:
    if ( entry_subtable )
      FT_FREE ( entry_subtable );
    if ( state_array )
      FT_FREE(state_array);
    gx_LookupTable_free( & state_table->class_subtable, memory );
    return error;
  }

  FT_LOCAL_DEF ( FT_Error )
  gx_XStateTable_traverse_entries( GX_XStateTable state_table,
				   GX_XStateTable_Entry_Action action,
				   FT_Pointer user )
  {
    return gx_EntrySubtable_traverse( state_table->entry_subtable,
				      state_table->nEntries,
				      action, 
				      user );    
  }

  static FT_Error
  gx_XStateTable_LookupTable_segment_array_finalizer ( GX_LookupTable_Format format,
						       FT_UShort lastGlyph,
						       FT_UShort firstGlyph,
						       GX_LookupValue value,
						       FT_Pointer user )
  {
    /* TODO: Merge generic_lookup_table_segment_array_finalizer */
    FT_Memory memory = user;
    FT_UShort * segment = value->extra.word;
    if ( !segment )
      return GX_Err_Ok;

    value->extra.word = NULL;
    FT_FREE(segment);
    return GX_Err_Ok; 
  }

  FT_LOCAL_DEF ( void )
  gx_XStateTable_free ( GX_XStateTable state_table,
			FT_Memory memory,
			GX_XStateTable_Entry_Finalizer finalizer,
			FT_Pointer user )
  {
    GX_LookupTable_FuncsRec lookup_table_funcs = GX_LOOKUP_TABLE_FUNC_ZERO;

    lookup_table_funcs.segment_array_func = gx_XStateTable_LookupTable_segment_array_finalizer;
    
    if ( finalizer == NULL )
      finalizer = gx_StateTable_Entry_default_finalizer;
    gx_XStateTable_free_entry_subtable ( memory, 
					 state_table->nEntries,
					 state_table->entry_subtable,
					 finalizer,
					 user );
    FT_FREE( state_table->entry_subtable );
    state_table->entry_subtable = NULL;

    FT_FREE( state_table->state_array );
    state_table->state_array = NULL;

    
    if ( state_table->class_subtable.format == GX_LOOKUPTABLE_SEGMENT_ARRAY )
      gx_LookupTable_traverse_low( & state_table->class_subtable,
				   &lookup_table_funcs,
				   memory );
    gx_LookupTable_free( & state_table->class_subtable, memory );
  }

  FT_LOCAL_DEF ( FT_UShort )
  gx_XStateTable_get_class ( GX_XStateTable state_table,
			     FT_UShort glyph )
  {
    GX_LookupTable class_subtable;
    GX_LookupResultRec result;
    FT_UShort class_code;
    
    class_subtable = &state_table->class_subtable;
    result 	   = gx_LookupTable_lookup ( class_subtable,
					     glyph );
    
    if ( result.value == NULL )
      class_code = GX_CLASS_OUT_OF_BOUNDS;
    else if ( result.firstGlyph == GX_LOOKUP_RESULT_NO_FIRST_GLYPH )
      class_code = result.value->raw.u;
    else
      class_code = result.value->extra.word[glyph - result.firstGlyph];
    return class_code;
  }

  FT_LOCAL ( GX_EntrySubtable )
  gx_XStateTable_get_entry_subtable ( GX_XStateTable state_table,
				     FT_UShort current_state,
				     FT_UShort class_code )
 {
    GX_XStateHeader header  = &state_table->header; 
    FT_UShort * state_array = state_table->state_array;
    FT_UShort * state_array_for_current_state;
    FT_UShort entry_index;
    GX_EntrySubtable entry_subtable;
    
    FT_ASSERT( current_state < state_table->nStates );
    FT_ASSERT( class_code < header->nClasses );
    state_array_for_current_state = &state_array[current_state * header->nClasses];
    entry_index = state_array_for_current_state[class_code];
    FT_ASSERT( entry_index < state_table->nEntries );
    entry_subtable = &state_table->entry_subtable[entry_index];
    return entry_subtable;
  }

/* END */