Since Xft may only be available statically without shlib deps, check for
[apps/core/preloaded/video-player.git] / src / ftxgdef.c
1 /*******************************************************************
2  *
3  *  ftxgdef.c
4  *
5  *    TrueType Open GDEF table support.
6  *
7  *  Copyright 1996-2000 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 #include <freetype/tttags.h>
19
20 #include <freetype/internal/ftstream.h>
21 #include <freetype/internal/ftmemory.h>
22 #include <freetype/internal/tterrors.h>
23 #include <freetype/internal/tttypes.h>
24
25 #include "ftxopen.h"
26 #include "ftxopenf.h"
27
28 #define TTAG_GDEF  FT_MAKE_TAG( 'G', 'D', 'E', 'F' )
29
30   static FT_Error  Load_AttachList( TTO_AttachList*  al,
31                                     FT_Stream        stream );
32   static FT_Error  Load_LigCaretList( TTO_LigCaretList*  lcl,
33                                       FT_Stream          stream );
34
35   static void  Free_AttachList( TTO_AttachList*  al,
36                                 FT_Memory        memory );
37   static void  Free_LigCaretList( TTO_LigCaretList*  lcl,
38                                   FT_Memory          memory );
39
40   static void  Free_NewGlyphClasses( TTO_GDEFHeader*  gdef,
41                                      FT_Memory        memory );
42
43
44
45   /**********************
46    * Extension Functions
47    **********************/
48
49 #if 0
50 #define GDEF_ID  Build_Extension_ID( 'G', 'D', 'E', 'F' )
51
52
53   static FT_Error  GDEF_Create( void*  ext,
54                                 PFace  face )
55   {
56     DEFINE_LOAD_LOCALS( face->stream );
57
58     TTO_GDEFHeader*  gdef = (TTO_GDEFHeader*)ext;
59     Long             table;
60
61
62     /* by convention */
63
64     if ( !gdef )
65       return TT_Err_Ok;
66
67     /* a null offset indicates that there is no GDEF table */
68
69     gdef->offset = 0;
70
71     /* we store the start offset and the size of the subtable */
72
73     table = TT_LookUp_Table( face, TTAG_GDEF );
74     if ( table < 0 )
75       return TT_Err_Ok;             /* The table is optional */
76
77     if ( FILE_Seek( face->dirTables[table].Offset ) ||
78          ACCESS_Frame( 4L ) )
79       return error;
80
81     gdef->offset  = FILE_Pos() - 4L;    /* undo ACCESS_Frame() */
82     gdef->Version = GET_ULong();
83
84     FORGET_Frame();
85
86     gdef->loaded = FALSE;
87
88     return TT_Err_Ok;
89   }
90
91
92   static FT_Error  GDEF_Destroy( void*  ext,
93                                  PFace  face )
94   {
95     TTO_GDEFHeader*  gdef = (TTO_GDEFHeader*)ext;
96
97
98     /* by convention */
99
100     if ( !gdef )
101       return TT_Err_Ok;
102
103     if ( gdef->loaded )
104     {
105       Free_LigCaretList( &gdef->LigCaretList, memory );
106       Free_AttachList( &gdef->AttachList, memory );
107       Free_ClassDefinition( &gdef->GlyphClassDef, memory );
108       Free_ClassDefinition( &gdef->MarkAttachClassDef, memory );
109
110       Free_NewGlyphClasses( gdef, memory );
111     }
112
113     return TT_Err_Ok;
114   }
115
116
117   EXPORT_FUNC
118   FT_Error  TT_Init_GDEF_Extension( TT_Engine  engine )
119   {
120     PEngine_Instance  _engine = HANDLE_Engine( engine );
121
122
123     if ( !_engine )
124       return TT_Err_Invalid_Engine;
125
126     return  TT_Register_Extension( _engine,
127                                    GDEF_ID,
128                                    sizeof ( TTO_GDEFHeader ),
129                                    GDEF_Create,
130                                    GDEF_Destroy );
131   }
132 #endif
133
134   EXPORT_FUNC
135   FT_Error  TT_Load_GDEF_Table( FT_Face          face,
136                                 TTO_GDEFHeader** retptr )
137   {
138     FT_Error         error;
139     FT_Memory        memory = face->memory;
140     FT_Stream        stream = face->stream;
141     TT_Face          tt_face = (TT_Face)face;
142     FT_ULong         cur_offset, new_offset, base_offset;
143
144     TTO_GDEFHeader*  gdef;
145
146
147     if ( !retptr )
148       return TT_Err_Invalid_Argument;
149
150     if (( error = tt_face->goto_table( tt_face, TTAG_GDEF, stream, 0 ) ))
151       return error;
152
153     if ( ALLOC( gdef, sizeof( *gdef ) ) )
154       return error;
155
156     gdef->memory = face->memory;
157
158     base_offset = FILE_Pos();
159
160     /* skip version */
161
162     if ( FILE_Seek( base_offset + 4L ) ||
163          ACCESS_Frame( 2L ) )
164       goto Fail0;
165
166     new_offset = GET_UShort();
167
168     FORGET_Frame();
169
170     /* all GDEF subtables are optional */
171
172     if ( new_offset )
173     {
174       new_offset += base_offset;
175
176       /* only classes 1-4 are allowed here */
177
178       cur_offset = FILE_Pos();
179       if ( FILE_Seek( new_offset ) ||
180            ( error = Load_ClassDefinition( &gdef->GlyphClassDef, 5,
181                                            stream ) ) != TT_Err_Ok )
182         goto Fail0;
183       (void)FILE_Seek( cur_offset );
184     }
185     else
186       gdef->GlyphClassDef.loaded = FALSE;
187
188     if ( ACCESS_Frame( 2L ) )
189       goto Fail0;
190
191     new_offset = GET_UShort();
192
193     FORGET_Frame();
194
195     if ( new_offset )
196     {
197       new_offset += base_offset;
198
199       cur_offset = FILE_Pos();
200       if ( FILE_Seek( new_offset ) ||
201            ( error = Load_AttachList( &gdef->AttachList,
202                                       stream ) ) != TT_Err_Ok )
203         goto Fail1;
204       (void)FILE_Seek( cur_offset );
205     }
206     else
207       gdef->AttachList.loaded = FALSE;
208
209     if ( ACCESS_Frame( 2L ) )
210       return error;
211
212     new_offset = GET_UShort();
213
214     FORGET_Frame();
215
216     if ( new_offset )
217     {
218       new_offset += base_offset;
219
220       cur_offset = FILE_Pos();
221       if ( FILE_Seek( new_offset ) ||
222            ( error = Load_LigCaretList( &gdef->LigCaretList,
223                                         stream ) ) != TT_Err_Ok )
224         goto Fail2;
225       (void)FILE_Seek( cur_offset );
226     }
227     else
228       gdef->LigCaretList.loaded = FALSE;
229
230     /* OpenType 1.2 has introduced the `MarkAttachClassDef' field.  We
231        first have to scan the LookupFlag values to find out whether we
232        must load it or not.  Here we only store the current file offset. */
233
234     gdef->MarkAttachClassDef_offset = FILE_Pos();
235     gdef->MarkAttachClassDef.loaded = FALSE;
236
237     gdef->LastGlyph       = 0;
238     gdef->NewGlyphClasses = NULL;
239
240     *retptr = gdef;
241     DONE_Stream( stream );
242
243     return TT_Err_Ok;
244
245   Fail2:
246     Free_AttachList( &gdef->AttachList, memory );
247
248   Fail1:
249     Free_ClassDefinition( &gdef->GlyphClassDef, memory );
250
251   Fail0:
252     FREE( gdef );
253
254     return error;
255   }
256
257   EXPORT_FUNC
258   FT_Error  TT_Done_GDEF_Table ( TTO_GDEFHeader* gdef ) 
259   {
260     FT_Memory memory = gdef->memory;
261     
262     Free_LigCaretList( &gdef->LigCaretList, memory );
263     Free_AttachList( &gdef->AttachList, memory );
264     Free_ClassDefinition( &gdef->GlyphClassDef, memory );
265     Free_ClassDefinition( &gdef->MarkAttachClassDef, memory );
266     
267     Free_NewGlyphClasses( gdef, memory );
268
269     return TT_Err_Ok;
270   }
271
272
273
274
275   /*******************************
276    * AttachList related functions
277    *******************************/
278
279
280   /* AttachPoint */
281
282   static FT_Error  Load_AttachPoint( TTO_AttachPoint*  ap,
283                                      FT_Stream         stream )
284   {
285     FT_Memory memory = stream->memory;
286     FT_Error  error;
287
288     FT_UShort   n, count;
289     FT_UShort*  pi;
290
291
292     if ( ACCESS_Frame( 2L ) )
293       return error;
294
295     count = ap->PointCount = GET_UShort();
296
297     FORGET_Frame();
298
299     ap->PointIndex = NULL;
300
301     if ( count )
302     {
303       if ( ALLOC_ARRAY( ap->PointIndex, count, FT_UShort ) )
304         return error;
305
306       pi = ap->PointIndex;
307
308       if ( ACCESS_Frame( count * 2L ) )
309       {
310         FREE( pi );
311         return error;
312       }
313
314       for ( n = 0; n < count; n++ )
315         pi[n] = GET_UShort();
316
317       FORGET_Frame();
318     }
319
320     return TT_Err_Ok;
321   }
322
323
324   static void  Free_AttachPoint( TTO_AttachPoint*  ap,
325                                  FT_Memory        memory )
326   {
327     FREE( ap->PointIndex );
328   }
329
330
331   /* AttachList */
332
333   static FT_Error  Load_AttachList( TTO_AttachList*  al,
334                                     FT_Stream        stream )
335   {
336     FT_Memory memory = stream->memory;
337     FT_Error  error;
338
339     FT_UShort         n, count;
340     FT_ULong          cur_offset, new_offset, base_offset;
341
342     TTO_AttachPoint*  ap;
343
344
345     base_offset = FILE_Pos();
346
347     if ( ACCESS_Frame( 2L ) )
348       return error;
349
350     new_offset = GET_UShort() + base_offset;
351
352     FORGET_Frame();
353
354     cur_offset = FILE_Pos();
355     if ( FILE_Seek( new_offset ) ||
356          ( error = Load_Coverage( &al->Coverage, stream ) ) != TT_Err_Ok )
357       return error;
358     (void)FILE_Seek( cur_offset );
359
360     if ( ACCESS_Frame( 2L ) )
361       goto Fail2;
362
363     count = al->GlyphCount = GET_UShort();
364
365     FORGET_Frame();
366
367     al->AttachPoint = NULL;
368
369     if ( ALLOC_ARRAY( al->AttachPoint, count, TTO_AttachPoint ) )
370       goto Fail2;
371
372     ap = al->AttachPoint;
373
374     for ( n = 0; n < count; n++ )
375     {
376       if ( ACCESS_Frame( 2L ) )
377         goto Fail1;
378
379       new_offset = GET_UShort() + base_offset;
380
381       FORGET_Frame();
382
383       cur_offset = FILE_Pos();
384       if ( FILE_Seek( new_offset ) ||
385            ( error = Load_AttachPoint( &ap[n], stream ) ) != TT_Err_Ok )
386         goto Fail1;
387       (void)FILE_Seek( cur_offset );
388     }
389
390     al->loaded = TRUE;
391
392     return TT_Err_Ok;
393
394   Fail1:
395     for ( n = 0; n < count; n++ )
396       Free_AttachPoint( &ap[n], memory );
397
398     FREE( ap );
399
400   Fail2:
401     Free_Coverage( &al->Coverage, memory );
402     return error;
403   }
404
405
406   static void  Free_AttachList( TTO_AttachList*  al,
407                                 FT_Memory        memory )
408   {
409     FT_UShort         n, count;
410
411     TTO_AttachPoint*  ap;
412
413
414     if ( !al->loaded )
415       return;
416
417     if ( al->AttachPoint )
418     {
419       count = al->GlyphCount;
420       ap    = al->AttachPoint;
421
422       for ( n = 0; n < count; n++ )
423         Free_AttachPoint( &ap[n], memory );
424
425       FREE( ap );
426     }
427
428     Free_Coverage( &al->Coverage, memory );
429   }
430
431
432
433   /*********************************
434    * LigCaretList related functions
435    *********************************/
436
437
438   /* CaretValueFormat1 */
439   /* CaretValueFormat2 */
440   /* CaretValueFormat3 */
441   /* CaretValueFormat4 */
442
443   static FT_Error  Load_CaretValue( TTO_CaretValue*  cv,
444                                     FT_Stream        stream )
445   {
446     FT_Error  error;
447
448     FT_ULong cur_offset, new_offset, base_offset;
449
450
451     base_offset = FILE_Pos();
452
453     if ( ACCESS_Frame( 2L ) )
454       return error;
455
456     cv->CaretValueFormat = GET_UShort();
457
458     FORGET_Frame();
459
460     switch ( cv->CaretValueFormat )
461     {
462     case 1:
463       if ( ACCESS_Frame( 2L ) )
464         return error;
465
466       cv->cvf.cvf1.Coordinate = GET_Short();
467
468       FORGET_Frame();
469
470       break;
471
472     case 2:
473       if ( ACCESS_Frame( 2L ) )
474         return error;
475
476       cv->cvf.cvf2.CaretValuePoint = GET_UShort();
477
478       FORGET_Frame();
479
480       break;
481
482     case 3:
483       if ( ACCESS_Frame( 4L ) )
484         return error;
485
486       cv->cvf.cvf3.Coordinate = GET_Short();
487
488       new_offset = GET_UShort() + base_offset;
489
490       FORGET_Frame();
491
492       cur_offset = FILE_Pos();
493       if ( FILE_Seek( new_offset ) ||
494            ( error = Load_Device( &cv->cvf.cvf3.Device,
495                                   stream ) ) != TT_Err_Ok )
496         return error;
497       (void)FILE_Seek( cur_offset );
498
499       break;
500
501     case 4:
502       if ( ACCESS_Frame( 2L ) )
503         return error;
504
505       cv->cvf.cvf4.IdCaretValue = GET_UShort();
506
507       FORGET_Frame();
508       break;
509
510     default:
511       return TTO_Err_Invalid_GDEF_SubTable_Format;
512     }
513
514     return TT_Err_Ok;
515   }
516
517
518   static void  Free_CaretValue( TTO_CaretValue*  cv,
519                                 FT_Memory        memory )
520   {
521     if ( cv->CaretValueFormat == 3 )
522       Free_Device( &cv->cvf.cvf3.Device, memory );
523   }
524
525
526   /* LigGlyph */
527
528   static FT_Error  Load_LigGlyph( TTO_LigGlyph*  lg,
529                                   FT_Stream      stream )
530   {
531     FT_Memory memory = stream->memory;
532     FT_Error  error;
533
534     FT_UShort        n, count;
535     FT_ULong         cur_offset, new_offset, base_offset;
536
537     TTO_CaretValue*  cv;
538
539
540     base_offset = FILE_Pos();
541
542     if ( ACCESS_Frame( 2L ) )
543       return error;
544
545     count = lg->CaretCount = GET_UShort();
546
547     FORGET_Frame();
548
549     lg->CaretValue = NULL;
550
551     if ( ALLOC_ARRAY( lg->CaretValue, count, TTO_CaretValue ) )
552       return error;
553
554     cv = lg->CaretValue;
555
556     for ( n = 0; n < count; n++ )
557     {
558       if ( ACCESS_Frame( 2L ) )
559         goto Fail;
560
561       new_offset = GET_UShort() + base_offset;
562
563       FORGET_Frame();
564
565       cur_offset = FILE_Pos();
566       if ( FILE_Seek( new_offset ) ||
567            ( error = Load_CaretValue( &cv[n], stream ) ) != TT_Err_Ok )
568         goto Fail;
569       (void)FILE_Seek( cur_offset );
570     }
571
572     return TT_Err_Ok;
573
574   Fail:
575     for ( n = 0; n < count; n++ )
576       Free_CaretValue( &cv[n], memory );
577
578     FREE( cv );
579     return error;
580   }
581
582
583   static void  Free_LigGlyph( TTO_LigGlyph*  lg,
584                               FT_Memory      memory )
585   {
586     FT_UShort        n, count;
587
588     TTO_CaretValue*  cv;
589
590
591     if ( lg->CaretValue )
592     {
593       count = lg->CaretCount;
594       cv    = lg->CaretValue;
595
596       for ( n = 0; n < count; n++ )
597         Free_CaretValue( &cv[n], memory );
598
599       FREE( cv );
600     }
601   }
602
603
604   /* LigCaretList */
605
606   static FT_Error  Load_LigCaretList( TTO_LigCaretList*  lcl,
607                                       FT_Stream          stream )
608   {
609     FT_Memory memory = stream->memory;
610     FT_Error  error;
611
612     FT_UShort      n, count;
613     FT_ULong       cur_offset, new_offset, base_offset;
614
615     TTO_LigGlyph*  lg;
616
617
618     base_offset = FILE_Pos();
619
620     if ( ACCESS_Frame( 2L ) )
621       return error;
622
623     new_offset = GET_UShort() + base_offset;
624
625     FORGET_Frame();
626
627     cur_offset = FILE_Pos();
628     if ( FILE_Seek( new_offset ) ||
629          ( error = Load_Coverage( &lcl->Coverage, stream ) ) != TT_Err_Ok )
630       return error;
631     (void)FILE_Seek( cur_offset );
632
633     if ( ACCESS_Frame( 2L ) )
634       goto Fail2;
635
636     count = lcl->LigGlyphCount = GET_UShort();
637
638     FORGET_Frame();
639
640     lcl->LigGlyph = NULL;
641
642     if ( ALLOC_ARRAY( lcl->LigGlyph, count, TTO_LigGlyph ) )
643       goto Fail2;
644
645     lg = lcl->LigGlyph;
646
647     for ( n = 0; n < count; n++ )
648     {
649       if ( ACCESS_Frame( 2L ) )
650         goto Fail1;
651
652       new_offset = GET_UShort() + base_offset;
653
654       FORGET_Frame();
655
656       cur_offset = FILE_Pos();
657       if ( FILE_Seek( new_offset ) ||
658            ( error = Load_LigGlyph( &lg[n], stream ) ) != TT_Err_Ok )
659         goto Fail1;
660       (void)FILE_Seek( cur_offset );
661     }
662
663     lcl->loaded = TRUE;
664
665     return TT_Err_Ok;
666
667   Fail1:
668     for ( n = 0; n < count; n++ )
669       Free_LigGlyph( &lg[n], memory );
670
671     FREE( lg );
672
673   Fail2:
674     Free_Coverage( &lcl->Coverage, memory );
675     return error;
676   }
677
678
679   static void  Free_LigCaretList( TTO_LigCaretList*  lcl,
680                                   FT_Memory           memory )
681   {
682     FT_UShort      n, count;
683
684     TTO_LigGlyph*  lg;
685
686
687     if ( !lcl->loaded )
688       return;
689
690     if ( lcl->LigGlyph )
691     {
692       count = lcl->LigGlyphCount;
693       lg    = lcl->LigGlyph;
694
695       for ( n = 0; n < count; n++ )
696         Free_LigGlyph( &lg[n], memory );
697
698       FREE( lg );
699     }
700
701     Free_Coverage( &lcl->Coverage, memory );
702   }
703
704
705
706   /***********
707    * GDEF API
708    ***********/
709
710
711   static FT_UShort  Get_New_Class( TTO_GDEFHeader*  gdef,
712                                    FT_UShort        glyphID,
713                                    FT_UShort        index )
714   {
715     FT_UShort              glyph_index, array_index;
716     FT_UShort              byte, bits;
717     
718     TTO_ClassRangeRecord*  gcrr;
719     FT_UShort**            ngc;
720
721
722     if ( glyphID >= gdef->LastGlyph )
723       return 0;
724
725     gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord;
726     ngc  = gdef->NewGlyphClasses;
727
728     if ( glyphID < gcrr[index].Start )
729     {
730       array_index = 0;
731       if ( index == 0 )
732         glyph_index = glyphID;
733       else
734         glyph_index = glyphID - gcrr[index - 1].End - 1;
735     }
736     else
737     {
738       array_index = index + 1;
739       glyph_index = glyphID - gcrr[index].End - 1;
740     }
741
742     byte = ngc[array_index][glyph_index / 4 + 1];
743     bits = byte >> ( 16 - ( glyph_index % 4 + 1 ) * 4 );
744
745     return bits & 0x000F;
746   }
747
748
749   EXPORT_FUNC
750   FT_Error  TT_GDEF_Get_Glyph_Property( TTO_GDEFHeader*  gdef,
751                                         FT_UShort        glyphID,
752                                         FT_UShort*       property )
753   {
754     FT_UShort class, index;
755
756     FT_Error  error;
757
758
759     if ( !gdef || !property )
760       return TT_Err_Invalid_Argument;
761
762     /* first, we check for mark attach classes */
763
764     if ( gdef->MarkAttachClassDef.loaded )
765     {
766       error = Get_Class( &gdef->MarkAttachClassDef, glyphID, &class, &index );
767       if ( error && error != TTO_Err_Not_Covered )
768         return error;
769       if ( !error )
770       {
771         *property = class << 8;
772         return TT_Err_Ok;
773       }
774     }
775
776     error = Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index );
777     if ( error && error != TTO_Err_Not_Covered )
778       return error;
779
780     /* if we have a constructed class table, check whether additional
781        values have been assigned                                      */
782
783     if ( error == TTO_Err_Not_Covered && gdef->NewGlyphClasses )
784       class = Get_New_Class( gdef, glyphID, index );
785
786     switch ( class )
787     {
788     case UNCLASSIFIED_GLYPH:
789       *property = 0;
790       break;
791
792     case SIMPLE_GLYPH:
793       *property = TTO_BASE_GLYPH;
794       break;
795
796     case LIGATURE_GLYPH:
797       *property = TTO_LIGATURE;
798       break;
799
800     case MARK_GLYPH:
801       *property = TTO_MARK;
802       break;
803
804     case COMPONENT_GLYPH:
805       *property = TTO_COMPONENT;
806       break;
807     }
808
809     return TT_Err_Ok;
810   }
811
812
813   static FT_Error  Make_ClassRange( TTO_ClassDefinition*  cd,
814                                     FT_UShort             start,
815                                     FT_UShort             end,
816                                     FT_UShort             class,
817                                     FT_Memory             memory )
818   {
819     FT_Error               error;
820     FT_UShort              index;
821
822     TTO_ClassDefFormat2*   cdf2;
823     TTO_ClassRangeRecord*  crr;
824
825
826     cdf2 = &cd->cd.cd2;
827
828     if ( REALLOC_ARRAY( cdf2->ClassRangeRecord,
829                         cdf2->ClassRangeCount,
830                         cdf2->ClassRangeCount + 1 ,
831                         TTO_ClassRangeRecord ) )
832       return error;
833
834     cdf2->ClassRangeCount++;
835
836     crr   = cdf2->ClassRangeRecord;
837     index = cdf2->ClassRangeCount - 1;
838
839     crr[index].Start = start;
840     crr[index].End   = end;
841     crr[index].Class = class;
842
843     cd->Defined[class] = TRUE;
844
845     return TT_Err_Ok;
846   }
847
848
849   EXPORT_FUNC
850   FT_Error  TT_GDEF_Build_ClassDefinition( TTO_GDEFHeader*  gdef,
851                                            FT_UShort        num_glyphs,
852                                            FT_UShort        glyph_count,
853                                            FT_UShort*       glyph_array,
854                                            FT_UShort*       class_array )
855   {
856     FT_UShort              start, curr_glyph, curr_class;
857     FT_UShort              n, count;
858     FT_Error               error;
859     FT_Memory              memory = gdef->memory;
860
861     TTO_ClassDefinition*   gcd;
862     TTO_ClassRangeRecord*  gcrr;
863     FT_UShort**            ngc;
864
865
866     if ( !gdef || !glyph_array || !class_array )
867       return TT_Err_Invalid_Argument;
868
869     gcd = &gdef->GlyphClassDef;
870
871     /* We build a format 2 table */
872
873     gcd->ClassFormat = 2;
874
875     /* A GlyphClassDef table contains at most 5 different class values */
876
877     if ( ALLOC_ARRAY( gcd->Defined, 5, FT_Bool ) )
878       return error;
879
880     gcd->cd.cd2.ClassRangeCount  = 0;
881     gcd->cd.cd2.ClassRangeRecord = NULL;
882
883     start      = glyph_array[0];
884     curr_class = class_array[0];
885     curr_glyph = start;
886
887     if ( curr_class >= 5 )
888     {
889       error = TT_Err_Invalid_Argument;
890       goto Fail4;
891     }
892
893     glyph_count--;
894
895     for ( n = 0; n <= glyph_count; n++ )
896     {
897       if ( curr_glyph == glyph_array[n] && curr_class == class_array[n] )
898       {
899         if ( n == glyph_count )
900         {
901           if ( ( error = Make_ClassRange( gcd, start,
902                                           curr_glyph,
903                                           curr_class,
904                                           memory ) ) != TT_Err_Ok )
905             goto Fail3;
906         }
907         else
908         {
909           if ( curr_glyph == 0xFFFF )
910           {
911             error = TT_Err_Invalid_Argument;
912             goto Fail3;
913           }
914           else
915             curr_glyph++;
916         }
917       }
918       else
919       {
920         if ( ( error = Make_ClassRange( gcd, start,
921                                         curr_glyph - 1,
922                                         curr_class,
923                                         memory ) ) != TT_Err_Ok )
924           goto Fail3;
925
926         if ( curr_glyph > glyph_array[n] )
927         {
928           error = TT_Err_Invalid_Argument;
929           goto Fail3;
930         }
931
932         start      = glyph_array[n];
933         curr_class = class_array[n];
934         curr_glyph = start;
935
936         if ( curr_class >= 5 )
937         {
938           error = TT_Err_Invalid_Argument;
939           goto Fail3;
940         }
941
942         if ( n == glyph_count )
943         {
944           if ( ( error = Make_ClassRange( gcd, start,
945                                           curr_glyph,
946                                           curr_class,
947                                           memory ) ) != TT_Err_Ok )
948             goto Fail3;
949         }
950         else
951         {
952           if ( curr_glyph == 0xFFFF )
953           {
954             error = TT_Err_Invalid_Argument;
955             goto Fail3;
956           }
957           else
958             curr_glyph++;
959         }
960       }
961     }
962
963     /* now prepare the arrays for class values assigned during the lookup
964        process                                                            */
965
966     if ( ALLOC_ARRAY( gdef->NewGlyphClasses,
967                       gcd->cd.cd2.ClassRangeCount + 1, FT_UShort* ) )
968       goto Fail2;
969
970     count = gcd->cd.cd2.ClassRangeCount;
971     gcrr  = gcd->cd.cd2.ClassRangeRecord;
972     ngc   = gdef->NewGlyphClasses;
973
974     /* We allocate arrays for all glyphs not covered by the class range
975        records.  Each element holds four class values.                  */
976
977     if ( gcrr[0].Start )
978     {
979       if ( ALLOC_ARRAY( ngc[0], gcrr[0].Start / 4 + 1, FT_UShort ) )
980         goto Fail1;
981     }
982
983     for ( n = 1; n < count; n++ )
984     {
985       if ( gcrr[n].Start - gcrr[n - 1].End > 1 )
986         if ( ALLOC_ARRAY( ngc[n],
987                           ( gcrr[n].Start - gcrr[n - 1].End - 1 ) / 4 + 1,
988                           FT_UShort ) )
989           goto Fail1;
990     }
991
992     if ( gcrr[count - 1].End != num_glyphs - 1 )
993     {
994       if ( ALLOC_ARRAY( ngc[count],
995                         ( num_glyphs - gcrr[count - 1].End - 1 ) / 4 + 1,
996                         FT_UShort ) )
997         goto Fail1;
998     }
999
1000     gdef->LastGlyph = num_glyphs - 1;
1001
1002     gdef->MarkAttachClassDef_offset = 0L;
1003     gdef->MarkAttachClassDef.loaded = FALSE;
1004
1005     return TT_Err_Ok;
1006
1007   Fail1:
1008     for ( n = 0; n < count; n++ )
1009       FREE( ngc[n] );
1010
1011   Fail2:
1012     FREE( gdef->NewGlyphClasses );
1013
1014   Fail3:
1015     FREE( gcd->cd.cd2.ClassRangeRecord );
1016
1017   Fail4:
1018     FREE( gcd->Defined );
1019     return error;
1020   }
1021
1022
1023   static void  Free_NewGlyphClasses( TTO_GDEFHeader*  gdef,
1024                                      FT_Memory        memory )
1025   {
1026     FT_UShort**  ngc;
1027     FT_UShort    n, count;
1028
1029
1030     if ( gdef->NewGlyphClasses )
1031     {
1032       count = gdef->GlyphClassDef.cd.cd2.ClassRangeCount + 1;
1033       ngc   = gdef->NewGlyphClasses;
1034
1035       for ( n = 0; n < count; n++ )
1036         FREE( ngc[n] );
1037
1038       FREE( ngc );
1039     }
1040   }
1041
1042
1043   FT_Error  Add_Glyph_Property( TTO_GDEFHeader*  gdef,
1044                                 FT_UShort        glyphID,
1045                                 FT_UShort        property )
1046   {
1047     FT_Error               error;
1048     FT_UShort              class, new_class, index;
1049     FT_UShort              byte, bits, mask;
1050     FT_UShort              array_index, glyph_index;
1051
1052     TTO_ClassRangeRecord*  gcrr;
1053     FT_UShort**            ngc;
1054
1055
1056     error = Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index );
1057     if ( error && error != TTO_Err_Not_Covered )
1058       return error;
1059
1060     /* we don't accept glyphs covered in `GlyphClassDef' */
1061
1062     if ( !error )
1063       return TTO_Err_Not_Covered;
1064
1065     switch ( property )
1066     {
1067     case 0:
1068       new_class = UNCLASSIFIED_GLYPH;
1069       break;
1070
1071     case TTO_BASE_GLYPH:
1072       new_class = SIMPLE_GLYPH;
1073       break;
1074
1075     case TTO_LIGATURE:
1076       new_class = LIGATURE_GLYPH;
1077       break;
1078
1079     case TTO_MARK:
1080       new_class = MARK_GLYPH;
1081       break;
1082
1083     case TTO_COMPONENT:
1084       new_class = COMPONENT_GLYPH;
1085       break;
1086
1087     default:
1088       return TT_Err_Invalid_Argument;
1089     }
1090
1091     gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord;
1092     ngc  = gdef->NewGlyphClasses;
1093
1094     if ( glyphID < gcrr[index].Start )
1095     {
1096       array_index = 0;
1097       if ( index == 0 )
1098         glyph_index = glyphID;
1099       else
1100         glyph_index = glyphID - gcrr[index - 1].End - 1;
1101     }
1102     else
1103     {
1104       array_index = index + 1;
1105       glyph_index = glyphID - gcrr[index].End - 1;
1106     }
1107
1108     byte  = ngc[array_index][glyph_index / 4 + 1];
1109     bits  = byte >> ( 16 - ( glyph_index % 4 + 1 ) * 4 );
1110     class = bits & 0x000F;
1111
1112     /* we don't overwrite existing entries */
1113
1114     if ( !class )
1115     {
1116       bits = new_class << ( 16 - ( glyph_index % 4 + 1 ) * 4 );
1117       mask = ~( 0x000F << ( 16 - ( glyph_index % 4 + 1 ) * 4 ) );
1118
1119       ngc[array_index][glyph_index / 4 + 1] &= mask;
1120       ngc[array_index][glyph_index / 4 + 1] |= bits;
1121     }
1122
1123     return TT_Err_Ok;
1124   }
1125
1126
1127   FT_Error  Check_Property( TTO_GDEFHeader*  gdef,
1128                             FT_UShort        index,
1129                             FT_UShort        flags,
1130                             FT_UShort*       property )
1131   {
1132     FT_Error  error;
1133
1134
1135     if ( gdef )
1136     {
1137       error = TT_GDEF_Get_Glyph_Property( gdef, index, property );
1138       if ( error )
1139         return error;
1140
1141       /* This is OpenType 1.2 */
1142
1143       if ( flags & IGNORE_SPECIAL_MARKS )
1144         if ( (flags & 0xFF00) != *property )
1145           return TTO_Err_Not_Covered;
1146
1147       if ( flags & *property )
1148         return TTO_Err_Not_Covered;
1149     }
1150
1151     return TT_Err_Ok;
1152   }
1153
1154
1155 /* END */