1 /*******************************************************************
2  *
3  *  ftxgsub.c
4  *
5  *    TrueType Open GSUB table support.
6  *
7  *  Copyright 1996-1999 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 /* XXX There is *a lot* of duplicated code (cf. formats 5 and 6), but
19        I don't care currently.  I believe that it would be possible to
20        save about 50% of TTO code by carefully designing the structures,
21        sharing as much as possible with extensive use of macros.  This
22        is something for a volunteer :-)                                  */
23 
24 #include "tttypes.h"
25 #include "tttags.h"
26 #include "ttload.h"
27 #include "ttextend.h"
28 #include "ttmemory.h"
29 #include "ttfile.h"
30 
31 #include "ftxopen.h"
32 #include "ftxopenf.h"
33 
34 
35 #define GSUB_ID  Build_Extension_ID( 'G', 'S', 'U', 'B' )
36 
37 
38 #define ADD_String( in, num_in, out, num_out, data )                \
39           ( ( error = TT_GSUB_Add_String( (in), (num_in),           \
40                                           (out), (num_out),         \
41                                           (data) ) ) != TT_Err_Ok )
42 
43 #define CHECK_Property( gdef, index, flags, property )              \
44           ( ( error = Check_Property( (gdef), (index), (flags),     \
45                                       (property) ) ) != TT_Err_Ok )
46 
47 
48   static TT_Error  Do_Glyph_Lookup( TTO_GSUBHeader*   gsub,
49                                     UShort            lookup_index,
50                                     TTO_GSUB_String*  in,
51                                     TTO_GSUB_String*  out,
52                                     UShort            context_length,
53                                     int               nesting_level );
54 
55 
56 
57   /**********************
58    * Auxiliary functions
59    **********************/
60 
61 
62   /* The following function copies `num_out' elements from `data' to
63      `out', advancing the array pointer in the `in' structure by `num_in'
64      elements and in `out' by `num_out' elements.  If the string (resp.
65      the properties) array in `out' is empty or too small, it allocates
66      resp. reallocates the string (and properties) array.  Finally, it
67      sets the `length' field of `out' equal to `pos' of the `out'
68      structure.
69 
70      The properties (if defined) for all replaced glyphs are taken from
71      the glyph at position `in->pos'.                                     */
72 
73   EXPORT_FUNC
TT_GSUB_Add_String(TTO_GSUB_String * in,UShort num_in,TTO_GSUB_String * out,UShort num_out,UShort * data)74   TT_Error  TT_GSUB_Add_String( TTO_GSUB_String*  in,
75                                 UShort            num_in,
76                                 TTO_GSUB_String*  out,
77                                 UShort            num_out,
78                                 UShort*           data )
79   {
80     TT_Error  error;
81     UShort    i;
82     UShort    p_in;
83     UShort*   p_out;
84 
85 
86     /* sanity check */
87 
88     if ( !in || !out ||
89          in->length == 0 || in->pos >= in->length ||
90          in->length < in->pos + num_in )
91       return TT_Err_Invalid_Argument;
92 
93     if ( out->pos + num_out >= out->allocated )
94     {
95       ULong  size = out->pos + num_out + 256L;
96 
97 
98       /* The following works because all fields in `out' must be
99          initialized to zero (including the `string' field) for the
100          first use.                                               */
101 
102       if ( REALLOC( out->string, size * sizeof ( UShort ) ) )
103         return error;
104       if ( in->properties )
105         if ( REALLOC( out->properties, size * sizeof ( UShort ) ) )
106           return error;
107       out->allocated = size;
108     }
109 
110     if ( num_out )
111     {
112       MEM_Copy( &out->string[out->pos], data, num_out * sizeof ( UShort ) );
113       if ( in->properties )
114       {
115         p_in  = in->properties[in->pos];
116         p_out = out->properties;
117 
118         for ( i = out->pos; i < out->pos + num_out; i++ )
119           p_out[i] = p_in;
120       }
121     }
122 
123     in->pos  += num_in;
124     out->pos += num_out;
125 
126     out->length = out->pos;
127 
128     return TT_Err_Ok;
129   }
130 
131 
Check_Property(TTO_GDEFHeader * gdef,UShort index,UShort flags,UShort * property)132   static TT_Error  Check_Property( TTO_GDEFHeader*  gdef,
133                                    UShort           index,
134                                    UShort           flags,
135                                    UShort*          property )
136   {
137     TT_Error  error;
138 
139 
140     if ( gdef )
141     {
142       error = TT_GDEF_Get_Glyph_Property( gdef, index, property );
143       if ( error )
144         return error;
145 
146       /* This is OpenType 1.2 */
147 
148       if ( flags & IGNORE_SPECIAL_MARKS )
149         if ( (flags & 0xFF00) != *property )
150           return TTO_Err_Not_Covered;
151 
152       if ( flags & *property )
153         return TTO_Err_Not_Covered;
154     }
155 
156     return TT_Err_Ok;
157   }
158 
159 
160 
161   /**********************
162    * Extension Functions
163    **********************/
164 
165 
GSUB_Create(void * ext,PFace face)166   static TT_Error  GSUB_Create( void*  ext,
167                                 PFace  face )
168   {
169     DEFINE_LOAD_LOCALS( face->stream );
170 
171     TTO_GSUBHeader*  gsub = (TTO_GSUBHeader*)ext;
172     Long             table;
173 
174 
175     /* by convention */
176 
177     if ( !gsub )
178       return TT_Err_Ok;
179 
180     /* a null offset indicates that there is no GSUB table */
181 
182     gsub->offset = 0;
183 
184     /* we store the start offset and the size of the subtable */
185 
186     table = TT_LookUp_Table( face, TTAG_GSUB );
187     if ( table < 0 )
188       return TT_Err_Ok;             /* The table is optional */
189 
190     if ( FILE_Seek( face->dirTables[table].Offset ) ||
191          ACCESS_Frame( 4L ) )
192       return error;
193 
194     gsub->offset  = FILE_Pos() - 4L;    /* undo ACCESS_Frame() */
195     gsub->Version = GET_ULong();
196 
197     FORGET_Frame();
198 
199     gsub->loaded = FALSE;
200 
201     return TT_Err_Ok;
202   }
203 
204 
GSUB_Destroy(void * ext,PFace face)205   static TT_Error  GSUB_Destroy( void*  ext,
206                                  PFace  face )
207   {
208     TTO_GSUBHeader*  gsub = (TTO_GSUBHeader*)ext;
209 
210 
211     /* by convention */
212 
213     if ( !gsub )
214       return TT_Err_Ok;
215 
216     if ( gsub->loaded )
217     {
218       Free_LookupList( &gsub->LookupList, GSUB );
219       Free_FeatureList( &gsub->FeatureList );
220       Free_ScriptList( &gsub->ScriptList );
221     }
222 
223     return TT_Err_Ok;
224   }
225 
226 
227   EXPORT_FUNC
TT_Init_GSUB_Extension(TT_Engine engine)228   TT_Error  TT_Init_GSUB_Extension( TT_Engine  engine )
229   {
230     PEngine_Instance  _engine = HANDLE_Engine( engine );
231 
232 
233     if ( !_engine )
234       return TT_Err_Invalid_Engine;
235 
236     return  TT_Register_Extension( _engine,
237                                    GSUB_ID,
238                                    sizeof ( TTO_GSUBHeader ),
239                                    GSUB_Create,
240                                    GSUB_Destroy );
241   }
242 
243 
244   EXPORT_FUNC
TT_Load_GSUB_Table(TT_Face face,TTO_GSUBHeader * retptr,TTO_GDEFHeader * gdef)245   TT_Error  TT_Load_GSUB_Table( TT_Face          face,
246                                 TTO_GSUBHeader*  retptr,
247                                 TTO_GDEFHeader*  gdef )
248   {
249     ULong            cur_offset, new_offset, base_offset;
250 
251     TT_UShort        i, num_lookups;
252     TT_Error         error;
253     TT_Stream        stream;
254     TTO_GSUBHeader*  gsub;
255     TTO_Lookup*      lo;
256 
257     PFace  faze = HANDLE_Face( face );
258 
259 
260     if ( !retptr )
261       return TT_Err_Invalid_Argument;
262 
263     if ( !faze )
264       return TT_Err_Invalid_Face_Handle;
265 
266     error = TT_Extension_Get( faze, GSUB_ID, (void**)&gsub );
267     if ( error )
268       return error;
269 
270     if ( gsub->offset == 0 )
271       return TT_Err_Table_Missing;      /* no GSUB table; nothing to do */
272 
273     /* now access stream */
274 
275     if ( USE_Stream( faze->stream, stream ) )
276       return error;
277 
278     base_offset = gsub->offset;
279 
280     /* skip version */
281 
282     if ( FILE_Seek( base_offset + 4L ) ||
283          ACCESS_Frame( 2L ) )
284       return error;
285 
286     new_offset = GET_UShort() + base_offset;
287 
288     FORGET_Frame();
289 
290     cur_offset = FILE_Pos();
291     if ( FILE_Seek( new_offset ) ||
292          ( error = Load_ScriptList( &gsub->ScriptList,
293                                     faze ) ) != TT_Err_Ok )
294       return error;
295     (void)FILE_Seek( cur_offset );
296 
297     if ( ACCESS_Frame( 2L ) )
298       goto Fail3;
299 
300     new_offset = GET_UShort() + base_offset;
301 
302     FORGET_Frame();
303 
304     cur_offset = FILE_Pos();
305     if ( FILE_Seek( new_offset ) ||
306          ( error = Load_FeatureList( &gsub->FeatureList,
307                                      faze ) ) != TT_Err_Ok )
308       goto Fail3;
309     (void)FILE_Seek( cur_offset );
310 
311     if ( ACCESS_Frame( 2L ) )
312       goto Fail2;
313 
314     new_offset = GET_UShort() + base_offset;
315 
316     FORGET_Frame();
317 
318     cur_offset = FILE_Pos();
319     if ( FILE_Seek( new_offset ) ||
320          ( error = Load_LookupList( &gsub->LookupList,
321                                     faze, GSUB ) ) != TT_Err_Ok )
322       goto Fail2;
323 
324     gsub->gdef = gdef;      /* can be NULL */
325 
326     /* We now check the LookupFlags for values larger than 0xFF to find
327        out whether we need to load the `MarkAttachClassDef' field of the
328        GDEF table -- this hack is necessary for OpenType 1.2 tables since
329        the version field of the GDEF table hasn't been incremented.
330 
331        For constructed GDEF tables, we only load it if
332        `MarkAttachClassDef_offset' is not zero (nevertheless, a build of
333        a constructed mark attach table is not supported currently).       */
334 
335     if ( gdef &&
336          gdef->MarkAttachClassDef_offset && !gdef->MarkAttachClassDef.loaded )
337     {
338       lo          = gsub->LookupList.Lookup;
339       num_lookups = gsub->LookupList.LookupCount;
340 
341       for ( i = 0; i < num_lookups; i++ )
342       {
343         if ( lo[i].LookupFlag & IGNORE_SPECIAL_MARKS )
344         {
345           if ( FILE_Seek( gdef->MarkAttachClassDef_offset ) ||
346                ACCESS_Frame( 2L ) )
347             goto Fail1;
348 
349           new_offset = GET_UShort();
350 
351           FORGET_Frame();
352 
353           if ( !new_offset )
354             return TTO_Err_Invalid_GDEF_SubTable;
355 
356           new_offset += base_offset;
357 
358           if ( FILE_Seek( new_offset ) ||
359                ( error = Load_ClassDefinition( &gdef->MarkAttachClassDef,
360                                                256, faze ) ) != TT_Err_Ok )
361             goto Fail1;
362 
363           break;
364         }
365       }
366     }
367 
368     gsub->loaded = TRUE;
369     *retptr = *gsub;
370     DONE_Stream( stream );
371 
372     return TT_Err_Ok;
373 
374   Fail1:
375     Free_LookupList( &gsub->LookupList, GSUB );
376 
377   Fail2:
378     Free_FeatureList( &gsub->FeatureList );
379 
380   Fail3:
381     Free_ScriptList( &gsub->ScriptList );
382 
383     /* release stream */
384 
385     DONE_Stream( stream );
386 
387     return error;
388   }
389 
390 
391 
392   /*****************************
393    * SubTable related functions
394    *****************************/
395 
396 
397   /* LookupType 1 */
398 
399   /* SingleSubstFormat1 */
400   /* SingleSubstFormat2 */
401 
Load_SingleSubst(TTO_SingleSubst * ss,PFace input)402   TT_Error  Load_SingleSubst( TTO_SingleSubst*  ss,
403                               PFace             input )
404   {
405     DEFINE_LOAD_LOCALS( input->stream );
406 
407     UShort   n, count;
408     ULong    cur_offset, new_offset, base_offset;
409 
410     UShort*  s;
411 
412 
413     base_offset = FILE_Pos();
414 
415     if ( ACCESS_Frame( 4L ) )
416       return error;
417 
418     ss->SubstFormat = GET_UShort();
419     new_offset      = GET_UShort() + base_offset;
420 
421     FORGET_Frame();
422 
423     cur_offset = FILE_Pos();
424     if ( FILE_Seek( new_offset ) ||
425          ( error = Load_Coverage( &ss->Coverage, input ) ) != TT_Err_Ok )
426       return error;
427     (void)FILE_Seek( cur_offset );
428 
429     switch ( ss->SubstFormat )
430     {
431     case 1:
432       if ( ACCESS_Frame( 2L ) )
433         goto Fail2;
434 
435       ss->ssf.ssf1.DeltaGlyphID = GET_UShort();
436 
437       FORGET_Frame();
438 
439       break;
440 
441     case 2:
442       if ( ACCESS_Frame( 2L ) )
443         goto Fail2;
444 
445       count = ss->ssf.ssf2.GlyphCount = GET_UShort();
446 
447       FORGET_Frame();
448 
449       ss->ssf.ssf2.Substitute = NULL;
450 
451       if ( ALLOC_ARRAY( ss->ssf.ssf2.Substitute, count, UShort ) )
452         goto Fail2;
453 
454       s = ss->ssf.ssf2.Substitute;
455 
456       if ( ACCESS_Frame( count * 2L ) )
457         goto Fail1;
458 
459       for ( n = 0; n < count; n++ )
460         s[n] = GET_UShort();
461 
462       FORGET_Frame();
463 
464       break;
465 
466     default:
467       return TTO_Err_Invalid_GSUB_SubTable_Format;
468     }
469 
470     return TT_Err_Ok;
471 
472   Fail1:
473     FREE( s );
474 
475   Fail2:
476     Free_Coverage( &ss->Coverage );
477     return error;
478   }
479 
480 
Free_SingleSubst(TTO_SingleSubst * ss)481   void  Free_SingleSubst( TTO_SingleSubst*  ss )
482   {
483     switch ( ss->SubstFormat )
484     {
485     case 1:
486       break;
487 
488     case 2:
489       FREE( ss->ssf.ssf2.Substitute );
490       break;
491     }
492 
493     Free_Coverage( &ss->Coverage );
494   }
495 
496 
Lookup_SingleSubst(TTO_SingleSubst * ss,TTO_GSUB_String * in,TTO_GSUB_String * out,UShort flags,UShort context_length,TTO_GDEFHeader * gdef)497   static TT_Error  Lookup_SingleSubst( TTO_SingleSubst*  ss,
498                                        TTO_GSUB_String*  in,
499                                        TTO_GSUB_String*  out,
500                                        UShort            flags,
501                                        UShort            context_length,
502                                        TTO_GDEFHeader*   gdef )
503   {
504     UShort    index, value[1], property;
505     TT_Error  error;
506 
507 
508     if ( context_length != 0xFFFF && context_length < 1 )
509       return TTO_Err_Not_Covered;
510 
511     if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
512       return error;
513 
514     error = Coverage_Index( &ss->Coverage, in->string[in->pos], &index );
515     if ( error )
516       return error;
517 
518     switch ( ss->SubstFormat )
519     {
520     case 1:
521       value[0] = ( in->string[in->pos] + ss->ssf.ssf1.DeltaGlyphID ) & 0xFFFF;
522       if ( ADD_String( in, 1, out, 1, value ) )
523         return error;
524       break;
525 
526     case 2:
527       if ( index >= ss->ssf.ssf2.GlyphCount )
528         return TTO_Err_Invalid_GSUB_SubTable;
529       value[0] = ss->ssf.ssf2.Substitute[index];
530       if ( ADD_String( in, 1, out, 1, value ) )
531         return error;
532       break;
533 
534     default:
535       return TTO_Err_Invalid_GSUB_SubTable;
536     }
537 
538     if ( gdef && gdef->NewGlyphClasses )
539     {
540       /* we inherit the old glyph class to the substituted glyph */
541 
542       error = Add_Glyph_Property( gdef, value[0], property );
543       if ( error && error != TTO_Err_Not_Covered )
544         return error;
545     }
546 
547     return TT_Err_Ok;
548   }
549 
550 
551   /* LookupType 2 */
552 
553   /* Sequence */
554 
Load_Sequence(TTO_Sequence * s,PFace input)555   static TT_Error  Load_Sequence( TTO_Sequence*  s,
556                                   PFace          input )
557   {
558     DEFINE_LOAD_LOCALS( input->stream );
559 
560     UShort   n, count;
561     UShort*  sub;
562 
563 
564     if ( ACCESS_Frame( 2L ) )
565       return error;
566 
567     count = s->GlyphCount = GET_UShort();
568 
569     FORGET_Frame();
570 
571     s->Substitute = NULL;
572 
573     if ( count )
574     {
575       if ( ALLOC_ARRAY( s->Substitute, count, UShort ) )
576         return error;
577 
578       sub = s->Substitute;
579 
580       if ( ACCESS_Frame( count * 2L ) )
581       {
582         FREE( sub );
583         return error;
584       }
585 
586       for ( n = 0; n < count; n++ )
587         sub[n] = GET_UShort();
588 
589       FORGET_Frame();
590     }
591 
592     return TT_Err_Ok;
593   }
594 
595 
Free_Sequence(TTO_Sequence * s)596   static void  Free_Sequence( TTO_Sequence*  s )
597   {
598     FREE( s->Substitute );
599   }
600 
601 
602   /* MultipleSubstFormat1 */
603 
Load_MultipleSubst(TTO_MultipleSubst * ms,PFace input)604   TT_Error  Load_MultipleSubst( TTO_MultipleSubst*  ms,
605                                 PFace               input )
606   {
607     DEFINE_LOAD_LOCALS( input->stream );
608 
609     UShort         n, count;
610     ULong          cur_offset, new_offset, base_offset;
611 
612     TTO_Sequence*  s;
613 
614 
615     base_offset = FILE_Pos();
616 
617     if ( ACCESS_Frame( 4L ) )
618       return error;
619 
620     ms->SubstFormat = GET_UShort();             /* should be 1 */
621     new_offset      = GET_UShort() + base_offset;
622 
623     FORGET_Frame();
624 
625     cur_offset = FILE_Pos();
626     if ( FILE_Seek( new_offset ) ||
627          ( error = Load_Coverage( &ms->Coverage, input ) ) != TT_Err_Ok )
628       return error;
629     (void)FILE_Seek( cur_offset );
630 
631     if ( ACCESS_Frame( 2L ) )
632       goto Fail2;
633 
634     count = ms->SequenceCount = GET_UShort();
635 
636     FORGET_Frame();
637 
638     ms->Sequence = NULL;
639 
640     if ( ALLOC_ARRAY( ms->Sequence, count, TTO_Sequence ) )
641       goto Fail2;
642 
643     s = ms->Sequence;
644 
645     for ( n = 0; n < count; n++ )
646     {
647       if ( ACCESS_Frame( 2L ) )
648         goto Fail1;
649 
650       new_offset = GET_UShort() + base_offset;
651 
652       FORGET_Frame();
653 
654       cur_offset = FILE_Pos();
655       if ( FILE_Seek( new_offset ) ||
656            ( error = Load_Sequence( &s[n], input ) ) != TT_Err_Ok )
657         goto Fail1;
658       (void)FILE_Seek( cur_offset );
659     }
660 
661     return TT_Err_Ok;
662 
663   Fail1:
664     for ( n = 0; n < count; n++ )
665       Free_Sequence( &s[n] );
666 
667     FREE( s );
668 
669   Fail2:
670     Free_Coverage( &ms->Coverage );
671     return error;
672   }
673 
674 
Free_MultipleSubst(TTO_MultipleSubst * ms)675   void  Free_MultipleSubst( TTO_MultipleSubst*  ms )
676   {
677     UShort         n, count;
678 
679     TTO_Sequence*  s;
680 
681 
682     if ( ms->Sequence )
683     {
684       count = ms->SequenceCount;
685       s     = ms->Sequence;
686 
687       for ( n = 0; n < count; n++ )
688         Free_Sequence( &s[n] );
689 
690       FREE( s );
691     }
692 
693     Free_Coverage( &ms->Coverage );
694   }
695 
696 
Lookup_MultipleSubst(TTO_MultipleSubst * ms,TTO_GSUB_String * in,TTO_GSUB_String * out,UShort flags,UShort context_length,TTO_GDEFHeader * gdef)697   static TT_Error  Lookup_MultipleSubst( TTO_MultipleSubst*  ms,
698                                          TTO_GSUB_String*    in,
699                                          TTO_GSUB_String*    out,
700                                          UShort              flags,
701                                          UShort              context_length,
702                                          TTO_GDEFHeader*     gdef )
703   {
704     TT_Error  error;
705     UShort    index, property, n, count;
706     UShort*   s;
707 
708 
709     if ( context_length != 0xFFFF && context_length < 1 )
710       return TTO_Err_Not_Covered;
711 
712     if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
713       return error;
714 
715     error = Coverage_Index( &ms->Coverage, in->string[in->pos], &index );
716     if ( error )
717       return error;
718 
719     if ( index >= ms->SequenceCount )
720       return TTO_Err_Invalid_GSUB_SubTable;
721 
722     count = ms->Sequence[index].GlyphCount;
723     s     = ms->Sequence[index].Substitute;
724 
725     if ( ADD_String( in, 1, out, count, s ) )
726       return error;
727 
728     if ( gdef && gdef->NewGlyphClasses )
729     {
730       /* this is a guess only ... */
731 
732       if ( property == TTO_LIGATURE )
733         property = TTO_BASE_GLYPH;
734 
735       for ( n = 0; n < count; n++ )
736       {
737         error = Add_Glyph_Property( gdef, s[n], property );
738         if ( error && error != TTO_Err_Not_Covered )
739           return error;
740       }
741     }
742 
743     return TT_Err_Ok;
744   }
745 
746 
747   /* LookupType 3 */
748 
749   /* AlternateSet */
750 
Load_AlternateSet(TTO_AlternateSet * as,PFace input)751   static TT_Error  Load_AlternateSet( TTO_AlternateSet*  as,
752                                       PFace              input )
753   {
754     DEFINE_LOAD_LOCALS( input->stream );
755 
756     UShort   n, count;
757     UShort*  a;
758 
759 
760     if ( ACCESS_Frame( 2L ) )
761       return error;
762 
763     count = as->GlyphCount = GET_UShort();
764 
765     FORGET_Frame();
766 
767     as->Alternate = NULL;
768 
769     if ( ALLOC_ARRAY( as->Alternate, count, UShort ) )
770       return error;
771 
772     a = as->Alternate;
773 
774     if ( ACCESS_Frame( count * 2L ) )
775     {
776       FREE( a );
777       return error;
778     }
779 
780     for ( n = 0; n < count; n++ )
781       a[n] = GET_UShort();
782 
783     FORGET_Frame();
784 
785     return TT_Err_Ok;
786   }
787 
788 
Free_AlternateSet(TTO_AlternateSet * as)789   static void  Free_AlternateSet( TTO_AlternateSet*  as )
790   {
791     FREE( as->Alternate );
792   }
793 
794 
795   /* AlternateSubstFormat1 */
796 
Load_AlternateSubst(TTO_AlternateSubst * as,PFace input)797   TT_Error  Load_AlternateSubst( TTO_AlternateSubst*  as,
798                                  PFace                input )
799   {
800     DEFINE_LOAD_LOCALS( input->stream );
801 
802     UShort             n, count;
803     ULong              cur_offset, new_offset, base_offset;
804 
805     TTO_AlternateSet*  aset;
806 
807 
808     base_offset = FILE_Pos();
809 
810     if ( ACCESS_Frame( 4L ) )
811       return error;
812 
813     as->SubstFormat = GET_UShort();             /* should be 1 */
814     new_offset      = GET_UShort() + base_offset;
815 
816     FORGET_Frame();
817 
818     cur_offset = FILE_Pos();
819     if ( FILE_Seek( new_offset ) ||
820          ( error = Load_Coverage( &as->Coverage, input ) ) != TT_Err_Ok )
821       return error;
822     (void)FILE_Seek( cur_offset );
823 
824     if ( ACCESS_Frame( 2L ) )
825       goto Fail2;
826 
827     count = as->AlternateSetCount = GET_UShort();
828 
829     FORGET_Frame();
830 
831     as->AlternateSet = NULL;
832 
833     if ( ALLOC_ARRAY( as->AlternateSet, count, TTO_AlternateSet ) )
834       goto Fail2;
835 
836     aset = as->AlternateSet;
837 
838     for ( n = 0; n < count; n++ )
839     {
840       if ( ACCESS_Frame( 2L ) )
841         goto Fail1;
842 
843       new_offset = GET_UShort() + base_offset;
844 
845       FORGET_Frame();
846 
847       cur_offset = FILE_Pos();
848       if ( FILE_Seek( new_offset ) ||
849            ( error = Load_AlternateSet( &aset[n], input ) ) != TT_Err_Ok )
850         goto Fail1;
851       (void)FILE_Seek( cur_offset );
852     }
853 
854     return TT_Err_Ok;
855 
856   Fail1:
857     for ( n = 0; n < count; n++ )
858       Free_AlternateSet( &aset[n] );
859 
860     FREE( aset );
861 
862   Fail2:
863     Free_Coverage( &as->Coverage );
864     return error;
865   }
866 
867 
Free_AlternateSubst(TTO_AlternateSubst * as)868   void  Free_AlternateSubst( TTO_AlternateSubst*  as )
869   {
870     UShort             n, count;
871 
872     TTO_AlternateSet*  aset;
873 
874 
875     if ( as->AlternateSet )
876     {
877       count = as->AlternateSetCount;
878       aset  = as->AlternateSet;
879 
880       for ( n = 0; n < count; n++ )
881         Free_AlternateSet( &aset[n] );
882 
883       FREE( aset );
884     }
885 
886     Free_Coverage( &as->Coverage );
887   }
888 
889 
Lookup_AlternateSubst(TTO_GSUBHeader * gsub,TTO_AlternateSubst * as,TTO_GSUB_String * in,TTO_GSUB_String * out,UShort flags,UShort context_length,TTO_GDEFHeader * gdef)890   static TT_Error  Lookup_AlternateSubst( TTO_GSUBHeader*      gsub,
891                                           TTO_AlternateSubst*  as,
892                                           TTO_GSUB_String*     in,
893                                           TTO_GSUB_String*     out,
894                                           UShort               flags,
895                                           UShort               context_length,
896                                           TTO_GDEFHeader*      gdef )
897   {
898     TT_Error          error;
899     UShort            index, alt_index, property;
900 
901     TTO_AlternateSet  aset;
902 
903 
904     if ( context_length != 0xFFFF && context_length < 1 )
905       return TTO_Err_Not_Covered;
906 
907     if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
908       return error;
909 
910     error = Coverage_Index( &as->Coverage, in->string[in->pos], &index );
911     if ( error )
912       return error;
913 
914     aset = as->AlternateSet[index];
915 
916     /* we use a user-defined callback function to get the alternate index */
917 
918     if ( gsub->alt )
919       alt_index = (gsub->alt)( out->pos, in->string[in->pos],
920                                aset.GlyphCount, aset.Alternate,
921                                gsub->data );
922     else
923       alt_index = 0;
924 
925     if ( ADD_String( in, 1, out, 1, &aset.Alternate[alt_index] ) )
926       return error;
927 
928     if ( gdef && gdef->NewGlyphClasses )
929     {
930       /* we inherit the old glyph class to the substituted glyph */
931 
932       error = Add_Glyph_Property( gdef, aset.Alternate[alt_index],
933                                   property );
934       if ( error && error != TTO_Err_Not_Covered )
935         return error;
936     }
937 
938     return TT_Err_Ok;
939   }
940 
941 
942   /* LookupType 4 */
943 
944   /* Ligature */
945 
Load_Ligature(TTO_Ligature * l,PFace input)946   static TT_Error  Load_Ligature( TTO_Ligature*  l,
947                                   PFace          input )
948   {
949     DEFINE_LOAD_LOCALS( input->stream );
950 
951     UShort   n, count;
952     UShort*  c;
953 
954 
955     if ( ACCESS_Frame( 4L ) )
956       return error;
957 
958     l->LigGlyph       = GET_UShort();
959     l->ComponentCount = GET_UShort();
960 
961     FORGET_Frame();
962 
963     l->Component = NULL;
964 
965     count = l->ComponentCount - 1;      /* only ComponentCount - 1 elements */
966 
967     if ( ALLOC_ARRAY( l->Component, count, UShort ) )
968       return error;
969 
970     c = l->Component;
971 
972     if ( ACCESS_Frame( count * 2L ) )
973     {
974       FREE( c );
975       return error;
976     }
977 
978     for ( n = 0; n < count; n++ )
979       c[n] = GET_UShort();
980 
981     FORGET_Frame();
982 
983     return TT_Err_Ok;
984   }
985 
986 
Free_Ligature(TTO_Ligature * l)987   static void  Free_Ligature( TTO_Ligature*  l )
988   {
989     FREE( l->Component );
990   }
991 
992 
993   /* LigatureSet */
994 
Load_LigatureSet(TTO_LigatureSet * ls,PFace input)995   static TT_Error  Load_LigatureSet( TTO_LigatureSet*  ls,
996                                      PFace             input )
997   {
998     DEFINE_LOAD_LOCALS( input->stream );
999 
1000     UShort         n, count;
1001     ULong          cur_offset, new_offset, base_offset;
1002 
1003     TTO_Ligature*  l;
1004 
1005 
1006     base_offset = FILE_Pos();
1007 
1008     if ( ACCESS_Frame( 2L ) )
1009       return error;
1010 
1011     count = ls->LigatureCount = GET_UShort();
1012 
1013     FORGET_Frame();
1014 
1015     ls->Ligature = NULL;
1016 
1017     if ( ALLOC_ARRAY( ls->Ligature, count, TTO_Ligature ) )
1018       return error;
1019 
1020     l = ls->Ligature;
1021 
1022     for ( n = 0; n < count; n++ )
1023     {
1024       if ( ACCESS_Frame( 2L ) )
1025         goto Fail;
1026 
1027       new_offset = GET_UShort() + base_offset;
1028 
1029       FORGET_Frame();
1030 
1031       cur_offset = FILE_Pos();
1032       if ( FILE_Seek( new_offset ) ||
1033            ( error = Load_Ligature( &l[n], input ) ) != TT_Err_Ok )
1034         goto Fail;
1035       (void)FILE_Seek( cur_offset );
1036     }
1037 
1038     return TT_Err_Ok;
1039 
1040   Fail:
1041     for ( n = 0; n < count; n++ )
1042       Free_Ligature( &l[n] );
1043 
1044     FREE( l );
1045     return error;
1046   }
1047 
1048 
Free_LigatureSet(TTO_LigatureSet * ls)1049   static void  Free_LigatureSet( TTO_LigatureSet*  ls )
1050   {
1051     UShort         n, count;
1052 
1053     TTO_Ligature*  l;
1054 
1055 
1056     if ( ls->Ligature )
1057     {
1058       count = ls->LigatureCount;
1059       l     = ls->Ligature;
1060 
1061       for ( n = 0; n < count; n++ )
1062         Free_Ligature( &l[n] );
1063 
1064       FREE( l );
1065     }
1066   }
1067 
1068 
1069   /* LigatureSubstFormat1 */
1070 
Load_LigatureSubst(TTO_LigatureSubst * ls,PFace input)1071   TT_Error  Load_LigatureSubst( TTO_LigatureSubst*  ls,
1072                                 PFace               input )
1073   {
1074     DEFINE_LOAD_LOCALS( input->stream );
1075 
1076     UShort            n, count;
1077     ULong             cur_offset, new_offset, base_offset;
1078 
1079     TTO_LigatureSet*  lset;
1080 
1081 
1082     base_offset = FILE_Pos();
1083 
1084     if ( ACCESS_Frame( 4L ) )
1085       return error;
1086 
1087     ls->SubstFormat = GET_UShort();             /* should be 1 */
1088     new_offset      = GET_UShort() + base_offset;
1089 
1090     FORGET_Frame();
1091 
1092     cur_offset = FILE_Pos();
1093     if ( FILE_Seek( new_offset ) ||
1094          ( error = Load_Coverage( &ls->Coverage, input ) ) != TT_Err_Ok )
1095       return error;
1096     (void)FILE_Seek( cur_offset );
1097 
1098     if ( ACCESS_Frame( 2L ) )
1099       goto Fail2;
1100 
1101     count = ls->LigatureSetCount = GET_UShort();
1102 
1103     FORGET_Frame();
1104 
1105     ls->LigatureSet = NULL;
1106 
1107     if ( ALLOC_ARRAY( ls->LigatureSet, count, TTO_LigatureSet ) )
1108       goto Fail2;
1109 
1110     lset = ls->LigatureSet;
1111 
1112     for ( n = 0; n < count; n++ )
1113     {
1114       if ( ACCESS_Frame( 2L ) )
1115         goto Fail1;
1116 
1117       new_offset = GET_UShort() + base_offset;
1118 
1119       FORGET_Frame();
1120 
1121       cur_offset = FILE_Pos();
1122       if ( FILE_Seek( new_offset ) ||
1123            ( error = Load_LigatureSet( &lset[n], input ) ) != TT_Err_Ok )
1124         goto Fail1;
1125       (void)FILE_Seek( cur_offset );
1126     }
1127 
1128     return TT_Err_Ok;
1129 
1130   Fail1:
1131     for ( n = 0; n < count; n++ )
1132       Free_LigatureSet( &lset[n] );
1133 
1134     FREE( lset );
1135 
1136   Fail2:
1137     Free_Coverage( &ls->Coverage );
1138     return error;
1139   }
1140 
1141 
Free_LigatureSubst(TTO_LigatureSubst * ls)1142   void  Free_LigatureSubst( TTO_LigatureSubst*  ls )
1143   {
1144     UShort            n, count;
1145 
1146     TTO_LigatureSet*  lset;
1147 
1148 
1149     if ( ls->LigatureSet )
1150     {
1151       count = ls->LigatureSetCount;
1152       lset  = ls->LigatureSet;
1153 
1154       for ( n = 0; n < count; n++ )
1155         Free_LigatureSet( &lset[n] );
1156 
1157       FREE( lset );
1158     }
1159 
1160     Free_Coverage( &ls->Coverage );
1161   }
1162 
1163 
Lookup_LigatureSubst(TTO_LigatureSubst * ls,TTO_GSUB_String * in,TTO_GSUB_String * out,UShort flags,UShort context_length,TTO_GDEFHeader * gdef)1164   static TT_Error  Lookup_LigatureSubst( TTO_LigatureSubst*  ls,
1165                                          TTO_GSUB_String*    in,
1166                                          TTO_GSUB_String*    out,
1167                                          UShort              flags,
1168                                          UShort              context_length,
1169                                          TTO_GDEFHeader*     gdef )
1170   {
1171     UShort         index, property;
1172     TT_Error       error;
1173     UShort         numlig, i, j;
1174     UShort*        s_in;
1175     UShort*        c;
1176 
1177     TTO_Ligature*  lig;
1178 
1179 
1180     if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
1181       return error;
1182 
1183     error = Coverage_Index( &ls->Coverage, in->string[in->pos], &index );
1184     if ( error )
1185       return error;
1186 
1187     if ( index >= ls->LigatureSetCount )
1188        return TTO_Err_Invalid_GSUB_SubTable;
1189 
1190     lig = ls->LigatureSet[index].Ligature;
1191 
1192     for ( numlig = ls->LigatureSet[index].LigatureCount;
1193           numlig;
1194           numlig--, lig++ )
1195     {
1196       if ( in->pos + lig->ComponentCount > in->length )
1197         continue;                         /* Not enough glyphs in input */
1198 
1199       s_in = &in->string[in->pos];
1200       c    = lig->Component;
1201 
1202       if ( context_length != 0xFFFF && context_length < lig->ComponentCount )
1203         break;
1204 
1205       for ( i = 1, j = 1; i < lig->ComponentCount; i++, j++ )
1206       {
1207         while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
1208         {
1209           if ( error && error != TTO_Err_Not_Covered )
1210             return error;
1211 
1212           if ( in->pos + j < in->length )
1213             j++;
1214           else
1215             break;
1216         }
1217 
1218         if ( s_in[j] != c[i - 1] )
1219           break;
1220       }
1221 
1222       if ( i == lig->ComponentCount )
1223       {
1224         if ( ADD_String( in, lig->ComponentCount, out, 1, &lig->LigGlyph ) )
1225           return error;
1226 
1227         if ( gdef && gdef->NewGlyphClasses )
1228         {
1229           /* this is just a guess ... */
1230 
1231           error = Add_Glyph_Property( gdef, lig->LigGlyph, TTO_LIGATURE );
1232           if ( error && error != TTO_Err_Not_Covered )
1233             return error;
1234         }
1235 
1236         return TT_Err_Ok;
1237       }
1238     }
1239 
1240     return TTO_Err_Not_Covered;
1241   }
1242 
1243 
1244   /* Do the actual substitution for a context substitution (either format
1245      5 or 6).  This is only called after we've determined that the input
1246      matches the subrule.                                                 */
1247 
Do_ContextSubst(TTO_GSUBHeader * gsub,UShort GlyphCount,UShort SubstCount,TTO_SubstLookupRecord * subst,TTO_GSUB_String * in,TTO_GSUB_String * out,int nesting_level)1248   static TT_Error  Do_ContextSubst( TTO_GSUBHeader*         gsub,
1249                                     UShort                  GlyphCount,
1250                                     UShort                  SubstCount,
1251                                     TTO_SubstLookupRecord*  subst,
1252                                     TTO_GSUB_String*        in,
1253                                     TTO_GSUB_String*        out,
1254                                     int                     nesting_level )
1255   {
1256     TT_Error  error;
1257     UShort    i, old_pos;
1258 
1259 
1260     i = 0;
1261 
1262     while ( i < GlyphCount )
1263     {
1264       if ( SubstCount && i == subst->SequenceIndex )
1265       {
1266         old_pos = in->pos;
1267 
1268         /* Do a substitution */
1269 
1270         error = Do_Glyph_Lookup( gsub, subst->LookupListIndex, in, out,
1271                                  GlyphCount, nesting_level );
1272 
1273         subst++;
1274         SubstCount--;
1275         i += in->pos - old_pos;
1276 
1277         if ( error == TTO_Err_Not_Covered )
1278         {
1279           /* XXX "can't happen" -- but don't count on it */
1280 
1281           if ( ADD_String( in, 1, out, 1, &in->string[in->pos] ) )
1282             return error;
1283           i++;
1284         }
1285         else if ( error )
1286           return error;
1287       }
1288       else
1289       {
1290         /* No substitution for this index */
1291 
1292         if ( ADD_String( in, 1, out, 1, &in->string[in->pos] ) )
1293           return error;
1294         i++;
1295       }
1296     }
1297 
1298     return TT_Err_Ok;
1299   }
1300 
1301 
1302   /* LookupType 5 */
1303 
1304   /* SubRule */
1305 
Load_SubRule(TTO_SubRule * sr,PFace input)1306   static TT_Error  Load_SubRule( TTO_SubRule*  sr,
1307                                  PFace         input )
1308   {
1309     DEFINE_LOAD_LOCALS( input->stream );
1310 
1311     UShort                  n, count;
1312     UShort*                 i;
1313 
1314     TTO_SubstLookupRecord*  slr;
1315 
1316 
1317     if ( ACCESS_Frame( 4L ) )
1318       return error;
1319 
1320     sr->GlyphCount = GET_UShort();
1321     sr->SubstCount = GET_UShort();
1322 
1323     FORGET_Frame();
1324 
1325     sr->Input = NULL;
1326 
1327     count = sr->GlyphCount - 1;         /* only GlyphCount - 1 elements */
1328 
1329     if ( ALLOC_ARRAY( sr->Input, count, UShort ) )
1330       return error;
1331 
1332     i = sr->Input;
1333 
1334     if ( ACCESS_Frame( count * 2L ) )
1335       goto Fail2;
1336 
1337     for ( n = 0; n < count; n++ )
1338       i[n] = GET_UShort();
1339 
1340     FORGET_Frame();
1341 
1342     sr->SubstLookupRecord = NULL;
1343 
1344     count = sr->SubstCount;
1345 
1346     if ( ALLOC_ARRAY( sr->SubstLookupRecord, count, TTO_SubstLookupRecord ) )
1347       goto Fail2;
1348 
1349     slr = sr->SubstLookupRecord;
1350 
1351     if ( ACCESS_Frame( count * 4L ) )
1352       goto Fail1;
1353 
1354     for ( n = 0; n < count; n++ )
1355     {
1356       slr[n].SequenceIndex   = GET_UShort();
1357       slr[n].LookupListIndex = GET_UShort();
1358     }
1359 
1360     FORGET_Frame();
1361 
1362     return TT_Err_Ok;
1363 
1364   Fail1:
1365     FREE( slr );
1366 
1367   Fail2:
1368     FREE( i );
1369     return error;
1370   }
1371 
1372 
Free_SubRule(TTO_SubRule * sr)1373   static void  Free_SubRule( TTO_SubRule*  sr )
1374   {
1375     FREE( sr->SubstLookupRecord );
1376     FREE( sr->Input );
1377   }
1378 
1379 
1380   /* SubRuleSet */
1381 
Load_SubRuleSet(TTO_SubRuleSet * srs,PFace input)1382   static TT_Error  Load_SubRuleSet( TTO_SubRuleSet*  srs,
1383                                     PFace            input )
1384   {
1385     DEFINE_LOAD_LOCALS( input->stream );
1386 
1387     UShort        n, count;
1388     ULong         cur_offset, new_offset, base_offset;
1389 
1390     TTO_SubRule*  sr;
1391 
1392 
1393     base_offset = FILE_Pos();
1394 
1395     if ( ACCESS_Frame( 2L ) )
1396       return error;
1397 
1398     count = srs->SubRuleCount = GET_UShort();
1399 
1400     FORGET_Frame();
1401 
1402     srs->SubRule = NULL;
1403 
1404     if ( ALLOC_ARRAY( srs->SubRule, count, TTO_SubRule ) )
1405       return error;
1406 
1407     sr = srs->SubRule;
1408 
1409     for ( n = 0; n < count; n++ )
1410     {
1411       if ( ACCESS_Frame( 2L ) )
1412         goto Fail;
1413 
1414       new_offset = GET_UShort() + base_offset;
1415 
1416       FORGET_Frame();
1417 
1418       cur_offset = FILE_Pos();
1419       if ( FILE_Seek( new_offset ) ||
1420            ( error = Load_SubRule( &sr[n], input ) ) != TT_Err_Ok )
1421         goto Fail;
1422       (void)FILE_Seek( cur_offset );
1423     }
1424 
1425     return TT_Err_Ok;
1426 
1427   Fail:
1428     for ( n = 0; n < count; n++ )
1429       Free_SubRule( &sr[n] );
1430 
1431     FREE( sr );
1432     return error;
1433   }
1434 
1435 
Free_SubRuleSet(TTO_SubRuleSet * srs)1436   static void  Free_SubRuleSet( TTO_SubRuleSet*  srs )
1437   {
1438     UShort        n, count;
1439 
1440     TTO_SubRule*  sr;
1441 
1442 
1443     if ( srs->SubRule )
1444     {
1445       count = srs->SubRuleCount;
1446       sr    = srs->SubRule;
1447 
1448       for ( n = 0; n < count; n++ )
1449         Free_SubRule( &sr[n] );
1450 
1451       FREE( sr );
1452     }
1453   }
1454 
1455 
1456   /* ContextSubstFormat1 */
1457 
Load_ContextSubst1(TTO_ContextSubstFormat1 * csf1,PFace input)1458   static TT_Error  Load_ContextSubst1( TTO_ContextSubstFormat1*  csf1,
1459                                        PFace                     input )
1460   {
1461     DEFINE_LOAD_LOCALS( input->stream );
1462 
1463     UShort           n, count;
1464     ULong            cur_offset, new_offset, base_offset;
1465 
1466     TTO_SubRuleSet*  srs;
1467 
1468 
1469     base_offset = FILE_Pos() - 2L;
1470 
1471     if ( ACCESS_Frame( 2L ) )
1472       return error;
1473 
1474     new_offset = GET_UShort() + base_offset;
1475 
1476     FORGET_Frame();
1477 
1478     cur_offset = FILE_Pos();
1479     if ( FILE_Seek( new_offset ) ||
1480          ( error = Load_Coverage( &csf1->Coverage, input ) ) != TT_Err_Ok )
1481       return error;
1482     (void)FILE_Seek( cur_offset );
1483 
1484     if ( ACCESS_Frame( 2L ) )
1485       goto Fail2;
1486 
1487     count = csf1->SubRuleSetCount = GET_UShort();
1488 
1489     FORGET_Frame();
1490 
1491     csf1->SubRuleSet = NULL;
1492 
1493     if ( ALLOC_ARRAY( csf1->SubRuleSet, count, TTO_SubRuleSet ) )
1494       goto Fail2;
1495 
1496     srs = csf1->SubRuleSet;
1497 
1498     for ( n = 0; n < count; n++ )
1499     {
1500       if ( ACCESS_Frame( 2L ) )
1501         goto Fail1;
1502 
1503       new_offset = GET_UShort() + base_offset;
1504 
1505       FORGET_Frame();
1506 
1507       cur_offset = FILE_Pos();
1508       if ( FILE_Seek( new_offset ) ||
1509            ( error = Load_SubRuleSet( &srs[n], input ) ) != TT_Err_Ok )
1510         goto Fail1;
1511       (void)FILE_Seek( cur_offset );
1512     }
1513 
1514     return TT_Err_Ok;
1515 
1516   Fail1:
1517     for ( n = 0; n < count; n++ )
1518       Free_SubRuleSet( &srs[n] );
1519 
1520     FREE( srs );
1521 
1522   Fail2:
1523     Free_Coverage( &csf1->Coverage );
1524     return error;
1525   }
1526 
1527 
Free_Context1(TTO_ContextSubstFormat1 * csf1)1528   static void  Free_Context1( TTO_ContextSubstFormat1*  csf1 )
1529   {
1530     UShort           n, count;
1531 
1532     TTO_SubRuleSet*  srs;
1533 
1534 
1535     if ( csf1->SubRuleSet )
1536     {
1537       count = csf1->SubRuleSetCount;
1538       srs   = csf1->SubRuleSet;
1539 
1540       for ( n = 0; n < count; n++ )
1541         Free_SubRuleSet( &srs[n] );
1542 
1543       FREE( srs );
1544     }
1545 
1546     Free_Coverage( &csf1->Coverage );
1547   }
1548 
1549 
1550   /* SubClassRule */
1551 
Load_SubClassRule(TTO_ContextSubstFormat2 * csf2,TTO_SubClassRule * scr,PFace input)1552   static TT_Error  Load_SubClassRule( TTO_ContextSubstFormat2*  csf2,
1553                                       TTO_SubClassRule*         scr,
1554                                       PFace                     input )
1555   {
1556     DEFINE_LOAD_LOCALS( input->stream );
1557 
1558     UShort                  n, count;
1559 
1560     UShort*                 c;
1561     TTO_SubstLookupRecord*  slr;
1562     Bool*                   d;
1563 
1564 
1565     if ( ACCESS_Frame( 4L ) )
1566       return error;
1567 
1568     scr->GlyphCount = GET_UShort();
1569     scr->SubstCount = GET_UShort();
1570 
1571     if ( scr->GlyphCount > csf2->MaxContextLength )
1572       csf2->MaxContextLength = scr->GlyphCount;
1573 
1574     FORGET_Frame();
1575 
1576     scr->Class = NULL;
1577 
1578     count = scr->GlyphCount - 1;        /* only GlyphCount - 1 elements */
1579 
1580     if ( ALLOC_ARRAY( scr->Class, count, UShort ) )
1581       return error;
1582 
1583     c = scr->Class;
1584     d = csf2->ClassDef.Defined;
1585 
1586     if ( ACCESS_Frame( count * 2L ) )
1587       goto Fail2;
1588 
1589     for ( n = 0; n < count; n++ )
1590     {
1591       c[n] = GET_UShort();
1592 
1593       /* We check whether the specific class is used at all.  If not,
1594          class 0 is used instead.                                     */
1595 
1596       if ( !d[c[n]] )
1597         c[n] = 0;
1598     }
1599 
1600     FORGET_Frame();
1601 
1602     scr->SubstLookupRecord = NULL;
1603 
1604     count = scr->SubstCount;
1605 
1606     if ( ALLOC_ARRAY( scr->SubstLookupRecord, count, TTO_SubstLookupRecord ) )
1607       goto Fail2;
1608 
1609     slr = scr->SubstLookupRecord;
1610 
1611     if ( ACCESS_Frame( count * 4L ) )
1612       goto Fail1;
1613 
1614     for ( n = 0; n < count; n++ )
1615     {
1616       slr[n].SequenceIndex   = GET_UShort();
1617       slr[n].LookupListIndex = GET_UShort();
1618     }
1619 
1620     FORGET_Frame();
1621 
1622     return TT_Err_Ok;
1623 
1624   Fail1:
1625     FREE( slr );
1626 
1627   Fail2:
1628     FREE( c );
1629     return error;
1630   }
1631 
1632 
Free_SubClassRule(TTO_SubClassRule * scr)1633   static void  Free_SubClassRule( TTO_SubClassRule*  scr )
1634   {
1635     FREE( scr->SubstLookupRecord );
1636     FREE( scr->Class );
1637   }
1638 
1639 
1640   /* SubClassSet */
1641 
Load_SubClassSet(TTO_ContextSubstFormat2 * csf2,TTO_SubClassSet * scs,PFace input)1642   static TT_Error  Load_SubClassSet( TTO_ContextSubstFormat2*  csf2,
1643                                      TTO_SubClassSet*          scs,
1644                                      PFace                     input )
1645   {
1646     DEFINE_LOAD_LOCALS( input->stream );
1647 
1648     UShort             n, count;
1649     ULong              cur_offset, new_offset, base_offset;
1650 
1651     TTO_SubClassRule*  scr;
1652 
1653 
1654     base_offset = FILE_Pos();
1655 
1656     if ( ACCESS_Frame( 2L ) )
1657       return error;
1658 
1659     count = scs->SubClassRuleCount = GET_UShort();
1660 
1661     FORGET_Frame();
1662 
1663     scs->SubClassRule = NULL;
1664 
1665     if ( ALLOC_ARRAY( scs->SubClassRule, count, TTO_SubClassRule ) )
1666       return error;
1667 
1668     scr = scs->SubClassRule;
1669 
1670     for ( n = 0; n < count; n++ )
1671     {
1672       if ( ACCESS_Frame( 2L ) )
1673         goto Fail;
1674 
1675       new_offset = GET_UShort() + base_offset;
1676 
1677       FORGET_Frame();
1678 
1679       cur_offset = FILE_Pos();
1680       if ( FILE_Seek( new_offset ) ||
1681            ( error = Load_SubClassRule( csf2, &scr[n],
1682                                         input ) ) != TT_Err_Ok )
1683         goto Fail;
1684       (void)FILE_Seek( cur_offset );
1685     }
1686 
1687     return TT_Err_Ok;
1688 
1689   Fail:
1690     for ( n = 0; n < count; n++ )
1691       Free_SubClassRule( &scr[n] );
1692 
1693     FREE( scr );
1694     return error;
1695   }
1696 
1697 
Free_SubClassSet(TTO_SubClassSet * scs)1698   static void  Free_SubClassSet( TTO_SubClassSet*  scs )
1699   {
1700     UShort             n, count;
1701 
1702     TTO_SubClassRule*  scr;
1703 
1704 
1705     if ( scs->SubClassRule )
1706     {
1707       count = scs->SubClassRuleCount;
1708       scr   = scs->SubClassRule;
1709 
1710       for ( n = 0; n < count; n++ )
1711         Free_SubClassRule( &scr[n] );
1712 
1713       FREE( scr );
1714     }
1715   }
1716 
1717 
1718   /* ContextSubstFormat2 */
1719 
Load_ContextSubst2(TTO_ContextSubstFormat2 * csf2,PFace input)1720   static TT_Error  Load_ContextSubst2( TTO_ContextSubstFormat2*  csf2,
1721                                        PFace                     input )
1722   {
1723     DEFINE_LOAD_LOCALS( input->stream );
1724 
1725     UShort            n, count;
1726     ULong             cur_offset, new_offset, base_offset;
1727 
1728     TTO_SubClassSet*  scs;
1729 
1730 
1731     base_offset = FILE_Pos() - 2;
1732 
1733     if ( ACCESS_Frame( 2L ) )
1734       return error;
1735 
1736     new_offset = GET_UShort() + base_offset;
1737 
1738     FORGET_Frame();
1739 
1740     cur_offset = FILE_Pos();
1741     if ( FILE_Seek( new_offset ) ||
1742          ( error = Load_Coverage( &csf2->Coverage, input ) ) != TT_Err_Ok )
1743       return error;
1744     (void)FILE_Seek( cur_offset );
1745 
1746     if ( ACCESS_Frame( 4L ) )
1747       goto Fail3;
1748 
1749     new_offset = GET_UShort() + base_offset;
1750 
1751     /* `SubClassSetCount' is the upper limit for class values, thus we
1752        read it now to make an additional safety check.                 */
1753 
1754     count = csf2->SubClassSetCount = GET_UShort();
1755 
1756     FORGET_Frame();
1757 
1758     cur_offset = FILE_Pos();
1759     if ( FILE_Seek( new_offset ) ||
1760          ( error = Load_ClassDefinition( &csf2->ClassDef, count,
1761                                          input ) ) != TT_Err_Ok )
1762       goto Fail3;
1763     (void)FILE_Seek( cur_offset );
1764 
1765     csf2->SubClassSet      = NULL;
1766     csf2->MaxContextLength = 0;
1767 
1768     if ( ALLOC_ARRAY( csf2->SubClassSet, count, TTO_SubClassSet ) )
1769       goto Fail2;
1770 
1771     scs = csf2->SubClassSet;
1772 
1773     for ( n = 0; n < count; n++ )
1774     {
1775       if ( ACCESS_Frame( 2L ) )
1776         goto Fail1;
1777 
1778       new_offset = GET_UShort() + base_offset;
1779 
1780       FORGET_Frame();
1781 
1782       if ( new_offset != base_offset )      /* not a NULL offset */
1783       {
1784         cur_offset = FILE_Pos();
1785         if ( FILE_Seek( new_offset ) ||
1786              ( error = Load_SubClassSet( csf2, &scs[n],
1787                                          input ) ) != TT_Err_Ok )
1788           goto Fail1;
1789         (void)FILE_Seek( cur_offset );
1790       }
1791       else
1792       {
1793         /* we create a SubClassSet table with no entries */
1794 
1795         csf2->SubClassSet[n].SubClassRuleCount = 0;
1796         csf2->SubClassSet[n].SubClassRule      = NULL;
1797       }
1798     }
1799 
1800     return TT_Err_Ok;
1801 
1802   Fail1:
1803     for ( n = 0; n < count; n++ )
1804       Free_SubClassSet( &scs[n] );
1805 
1806     FREE( scs );
1807 
1808   Fail2:
1809     Free_ClassDefinition( &csf2->ClassDef );
1810 
1811   Fail3:
1812     Free_Coverage( &csf2->Coverage );
1813     return error;
1814   }
1815 
1816 
Free_Context2(TTO_ContextSubstFormat2 * csf2)1817   static void  Free_Context2( TTO_ContextSubstFormat2*  csf2 )
1818   {
1819     UShort            n, count;
1820 
1821     TTO_SubClassSet*  scs;
1822 
1823 
1824     if ( csf2->SubClassSet )
1825     {
1826       count = csf2->SubClassSetCount;
1827       scs   = csf2->SubClassSet;
1828 
1829       for ( n = 0; n < count; n++ )
1830         Free_SubClassSet( &scs[n] );
1831 
1832       FREE( scs );
1833     }
1834 
1835     Free_ClassDefinition( &csf2->ClassDef );
1836     Free_Coverage( &csf2->Coverage );
1837   }
1838 
1839 
1840   /* ContextSubstFormat3 */
1841 
Load_ContextSubst3(TTO_ContextSubstFormat3 * csf3,PFace input)1842   static TT_Error  Load_ContextSubst3( TTO_ContextSubstFormat3*  csf3,
1843                                        PFace                     input )
1844   {
1845     DEFINE_LOAD_LOCALS( input->stream );
1846 
1847     UShort                  n, count;
1848     ULong                   cur_offset, new_offset, base_offset;
1849 
1850     TTO_Coverage*           c;
1851     TTO_SubstLookupRecord*  slr;
1852 
1853 
1854     base_offset = FILE_Pos() - 2L;
1855 
1856     if ( ACCESS_Frame( 4L ) )
1857       return error;
1858 
1859     csf3->GlyphCount = GET_UShort();
1860     csf3->SubstCount = GET_UShort();
1861 
1862     FORGET_Frame();
1863 
1864     csf3->Coverage = NULL;
1865 
1866     count = csf3->GlyphCount;
1867 
1868     if ( ALLOC_ARRAY( csf3->Coverage, count, TTO_Coverage ) )
1869       return error;
1870 
1871     c = csf3->Coverage;
1872 
1873     for ( n = 0; n < count; n++ )
1874     {
1875       if ( ACCESS_Frame( 2L ) )
1876         goto Fail2;
1877 
1878       new_offset = GET_UShort() + base_offset;
1879 
1880       FORGET_Frame();
1881 
1882       cur_offset = FILE_Pos();
1883       if ( FILE_Seek( new_offset ) ||
1884            ( error = Load_Coverage( &c[n], input ) ) != TT_Err_Ok )
1885         goto Fail2;
1886       (void)FILE_Seek( cur_offset );
1887     }
1888 
1889     csf3->SubstLookupRecord = NULL;
1890 
1891     count = csf3->SubstCount;
1892 
1893     if ( ALLOC_ARRAY( csf3->SubstLookupRecord, count,
1894                       TTO_SubstLookupRecord ) )
1895       goto Fail2;
1896 
1897     slr = csf3->SubstLookupRecord;
1898 
1899     if ( ACCESS_Frame( count * 4L ) )
1900       goto Fail1;
1901 
1902     for ( n = 0; n < count; n++ )
1903     {
1904       slr[n].SequenceIndex   = GET_UShort();
1905       slr[n].LookupListIndex = GET_UShort();
1906     }
1907 
1908     FORGET_Frame();
1909 
1910     return TT_Err_Ok;
1911 
1912   Fail1:
1913     FREE( slr );
1914 
1915   Fail2:
1916     for ( n = 0; n < count; n++ )
1917       Free_Coverage( &c[n] );
1918 
1919     FREE( c );
1920     return error;
1921   }
1922 
1923 
Free_Context3(TTO_ContextSubstFormat3 * csf3)1924   static void  Free_Context3( TTO_ContextSubstFormat3*  csf3 )
1925   {
1926     UShort         n, count;
1927 
1928     TTO_Coverage*  c;
1929 
1930 
1931     FREE( csf3->SubstLookupRecord );
1932 
1933     if ( csf3->Coverage )
1934     {
1935       count = csf3->GlyphCount;
1936       c     = csf3->Coverage;
1937 
1938       for ( n = 0; n < count; n++ )
1939         Free_Coverage( &c[n] );
1940 
1941       FREE( c );
1942     }
1943   }
1944 
1945 
1946   /* ContextSubst */
1947 
Load_ContextSubst(TTO_ContextSubst * cs,PFace input)1948   TT_Error  Load_ContextSubst( TTO_ContextSubst*  cs,
1949                                PFace              input )
1950   {
1951     DEFINE_LOAD_LOCALS( input->stream );
1952 
1953 
1954     if ( ACCESS_Frame( 2L ) )
1955       return error;
1956 
1957     cs->SubstFormat = GET_UShort();
1958 
1959     FORGET_Frame();
1960 
1961     switch ( cs->SubstFormat )
1962     {
1963     case 1:
1964       return Load_ContextSubst1( &cs->csf.csf1, input );
1965 
1966     case 2:
1967       return Load_ContextSubst2( &cs->csf.csf2, input );
1968 
1969     case 3:
1970       return Load_ContextSubst3( &cs->csf.csf3, input );
1971 
1972     default:
1973       return TTO_Err_Invalid_GSUB_SubTable_Format;
1974     }
1975 
1976     return TT_Err_Ok;               /* never reached */
1977   }
1978 
1979 
Free_ContextSubst(TTO_ContextSubst * cs)1980   void  Free_ContextSubst( TTO_ContextSubst*  cs )
1981   {
1982     switch ( cs->SubstFormat )
1983     {
1984     case 1:
1985       Free_Context1( &cs->csf.csf1 );
1986       break;
1987 
1988     case 2:
1989       Free_Context2( &cs->csf.csf2 );
1990       break;
1991 
1992     case 3:
1993       Free_Context3( &cs->csf.csf3 );
1994       break;
1995     }
1996   }
1997 
1998 
Lookup_ContextSubst1(TTO_GSUBHeader * gsub,TTO_ContextSubstFormat1 * csf1,TTO_GSUB_String * in,TTO_GSUB_String * out,UShort flags,UShort context_length,int nesting_level)1999   static TT_Error  Lookup_ContextSubst1(
2000                      TTO_GSUBHeader*           gsub,
2001                      TTO_ContextSubstFormat1*  csf1,
2002                      TTO_GSUB_String*          in,
2003                      TTO_GSUB_String*          out,
2004                      UShort                    flags,
2005                      UShort                    context_length,
2006                      int                       nesting_level )
2007   {
2008     UShort           index, property;
2009     UShort           i, j, k, numsr;
2010     TT_Error         error;
2011     UShort*          s_in;
2012 
2013     TTO_SubRule*     sr;
2014     TTO_GDEFHeader*  gdef;
2015 
2016 
2017     gdef = gsub->gdef;
2018 
2019     if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
2020       return error;
2021 
2022     error = Coverage_Index( &csf1->Coverage, in->string[in->pos], &index );
2023     if ( error )
2024       return error;
2025 
2026     sr    = csf1->SubRuleSet[index].SubRule;
2027     numsr = csf1->SubRuleSet[index].SubRuleCount;
2028 
2029     for ( k = 0; k < numsr; k++ )
2030     {
2031       if ( context_length != 0xFFFF && context_length < sr[k].GlyphCount )
2032         continue;
2033 
2034       if ( in->pos + sr[k].GlyphCount > in->length )
2035         continue;                           /* context is too long */
2036 
2037       s_in = &in->string[in->pos];
2038 
2039       for ( i = 1, j = 1; i < sr[k].GlyphCount; i++, j++ )
2040       {
2041         while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
2042         {
2043           if ( error && error != TTO_Err_Not_Covered )
2044             return error;
2045 
2046           if ( in->pos + j < in->length )
2047             j++;
2048           else
2049             break;
2050         }
2051 
2052         if ( s_in[j] != sr[k].Input[i - 1] )
2053           break;
2054       }
2055 
2056       if ( i == sr[k].GlyphCount )
2057         return Do_ContextSubst( gsub, sr[k].GlyphCount,
2058                                 sr[k].SubstCount, sr[k].SubstLookupRecord,
2059                                 in, out,
2060                                 nesting_level );
2061     }
2062 
2063     return TTO_Err_Not_Covered;
2064   }
2065 
2066 
Lookup_ContextSubst2(TTO_GSUBHeader * gsub,TTO_ContextSubstFormat2 * csf2,TTO_GSUB_String * in,TTO_GSUB_String * out,UShort flags,UShort context_length,int nesting_level)2067   static TT_Error  Lookup_ContextSubst2(
2068                      TTO_GSUBHeader*           gsub,
2069                      TTO_ContextSubstFormat2*  csf2,
2070                      TTO_GSUB_String*          in,
2071                      TTO_GSUB_String*          out,
2072                      UShort                    flags,
2073                      UShort                    context_length,
2074                      int                       nesting_level )
2075   {
2076     UShort             index, property;
2077     TT_Error           error;
2078     UShort             i, j, k, known_classes;
2079 
2080     UShort*            classes;
2081     UShort*            s_in;
2082     UShort*            cl;
2083 
2084     TTO_SubClassSet*   scs;
2085     TTO_SubClassRule*  sr;
2086     TTO_GDEFHeader*    gdef;
2087 
2088 
2089     gdef = gsub->gdef;
2090 
2091     if ( ALLOC_ARRAY( classes, csf2->MaxContextLength, UShort ) )
2092       return error;
2093 
2094     if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
2095       return error;
2096 
2097     /* Note: The coverage table in format 2 doesn't give an index into
2098              anything.  It just lets us know whether or not we need to
2099              do any lookup at all.                                     */
2100 
2101     error = Coverage_Index( &csf2->Coverage, in->string[in->pos], &index );
2102     if ( error )
2103       goto End;
2104 
2105     error = Get_Class( &csf2->ClassDef, in->string[in->pos],
2106                        &classes[0], NULL );
2107     if ( error )
2108       goto End;
2109     known_classes = 0;
2110 
2111     scs = &csf2->SubClassSet[classes[0]];
2112     if ( !scs )
2113     {
2114       error = TTO_Err_Invalid_GSUB_SubTable;
2115       goto End;
2116     }
2117 
2118     for ( k = 0; k < scs->SubClassRuleCount; k++ )
2119     {
2120       sr  = &scs->SubClassRule[k];
2121 
2122       if ( context_length != 0xFFFF && context_length < sr->GlyphCount )
2123         continue;
2124 
2125       if ( in->pos + sr->GlyphCount > in->length )
2126         continue;                           /* context is too long */
2127 
2128       s_in = &in->string[in->pos];
2129       cl   = sr->Class;
2130 
2131       /* Start at 1 because [0] is implied */
2132 
2133       for ( i = 1, j = 1; i < sr->GlyphCount; i++, j++ )
2134       {
2135         while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
2136         {
2137           if ( error && error != TTO_Err_Not_Covered )
2138             return error;
2139 
2140           if ( in->pos + j < in->length )
2141             j++;
2142           else
2143             break;
2144         }
2145 
2146         if ( i > known_classes )
2147         {
2148           /* Keeps us from having to do this for each rule */
2149 
2150           error = Get_Class( &csf2->ClassDef, s_in[j], &classes[i], NULL );
2151           if ( error && error != TTO_Err_Not_Covered )
2152             return error;
2153           known_classes = i;
2154         }
2155 
2156         if ( cl[i - 1] != classes[i] )
2157           break;
2158       }
2159 
2160       if ( i == sr->GlyphCount )
2161       {
2162         error = Do_ContextSubst( gsub, sr->GlyphCount,
2163                                  sr->SubstCount, sr->SubstLookupRecord,
2164                                  in, out,
2165                                  nesting_level );
2166         goto End;
2167       }
2168     }
2169 
2170     error = TTO_Err_Not_Covered;
2171 
2172   End:
2173     FREE( classes );
2174     return error;
2175   }
2176 
2177 
Lookup_ContextSubst3(TTO_GSUBHeader * gsub,TTO_ContextSubstFormat3 * csf3,TTO_GSUB_String * in,TTO_GSUB_String * out,UShort flags,UShort context_length,int nesting_level)2178   static TT_Error  Lookup_ContextSubst3(
2179                      TTO_GSUBHeader*           gsub,
2180                      TTO_ContextSubstFormat3*  csf3,
2181                      TTO_GSUB_String*          in,
2182                      TTO_GSUB_String*          out,
2183                      UShort                    flags,
2184                      UShort                    context_length,
2185                      int                       nesting_level )
2186   {
2187     TT_Error         error;
2188     UShort           index, i, j, property;
2189     UShort*          s_in;
2190 
2191     TTO_Coverage*    c;
2192     TTO_GDEFHeader*  gdef;
2193 
2194 
2195     gdef = gsub->gdef;
2196 
2197     if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
2198       return error;
2199 
2200     if ( context_length != 0xFFFF && context_length < csf3->GlyphCount )
2201       return TTO_Err_Not_Covered;
2202 
2203     if ( in->pos + csf3->GlyphCount > in->length )
2204       return TTO_Err_Not_Covered;         /* context is too long */
2205 
2206     s_in = &in->string[in->pos];
2207     c    = csf3->Coverage;
2208 
2209     for ( i = 1, j = 1; i < csf3->GlyphCount; i++, j++ )
2210     {
2211       while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
2212       {
2213         if ( error && error != TTO_Err_Not_Covered )
2214           return error;
2215 
2216         if ( in->pos + j < in->length )
2217           j++;
2218         else
2219           return TTO_Err_Not_Covered;
2220       }
2221 
2222       error = Coverage_Index( &c[i], s_in[j], &index );
2223       if ( error )
2224         return error;
2225     }
2226 
2227     return Do_ContextSubst( gsub, csf3->GlyphCount,
2228                             csf3->SubstCount, csf3->SubstLookupRecord,
2229                             in, out,
2230                             nesting_level );
2231   }
2232 
2233 
Lookup_ContextSubst(TTO_GSUBHeader * gsub,TTO_ContextSubst * cs,TTO_GSUB_String * in,TTO_GSUB_String * out,UShort flags,UShort context_length,int nesting_level)2234   static TT_Error  Lookup_ContextSubst( TTO_GSUBHeader*    gsub,
2235                                         TTO_ContextSubst*  cs,
2236                                         TTO_GSUB_String*   in,
2237                                         TTO_GSUB_String*   out,
2238                                         UShort             flags,
2239                                         UShort             context_length,
2240                                         int                nesting_level )
2241   {
2242     switch ( cs->SubstFormat )
2243     {
2244     case 1:
2245       return Lookup_ContextSubst1( gsub, &cs->csf.csf1, in, out,
2246                                    flags, context_length, nesting_level );
2247 
2248     case 2:
2249       return Lookup_ContextSubst2( gsub, &cs->csf.csf2, in, out,
2250                                    flags, context_length, nesting_level );
2251 
2252     case 3:
2253       return Lookup_ContextSubst3( gsub, &cs->csf.csf3, in, out,
2254                                    flags, context_length, nesting_level );
2255 
2256     default:
2257       return TTO_Err_Invalid_GSUB_SubTable_Format;
2258     }
2259 
2260     return TT_Err_Ok;               /* never reached */
2261   }
2262 
2263 
2264   /* LookupType 6 */
2265 
2266   /* ChainSubRule */
2267 
Load_ChainSubRule(TTO_ChainSubRule * csr,PFace input)2268   static TT_Error  Load_ChainSubRule( TTO_ChainSubRule*  csr,
2269                                       PFace              input )
2270   {
2271     DEFINE_LOAD_LOCALS( input->stream );
2272 
2273     UShort                  n, count;
2274     UShort*                 b;
2275     UShort*                 i;
2276     UShort*                 l;
2277 
2278     TTO_SubstLookupRecord*  slr;
2279 
2280 
2281     if ( ACCESS_Frame( 2L ) )
2282       return error;
2283 
2284     csr->BacktrackGlyphCount = GET_UShort();
2285 
2286     FORGET_Frame();
2287 
2288     csr->Backtrack = NULL;
2289 
2290     count = csr->BacktrackGlyphCount;
2291 
2292     if ( ALLOC_ARRAY( csr->Backtrack, count, UShort ) )
2293       return error;
2294 
2295     b = csr->Backtrack;
2296 
2297     if ( ACCESS_Frame( count * 2L ) )
2298       goto Fail4;
2299 
2300     for ( n = 0; n < count; n++ )
2301       b[n] = GET_UShort();
2302 
2303     FORGET_Frame();
2304 
2305     if ( ACCESS_Frame( 2L ) )
2306       goto Fail4;
2307 
2308     csr->InputGlyphCount = GET_UShort();
2309 
2310     FORGET_Frame();
2311 
2312     csr->Input = NULL;
2313 
2314     count = csr->InputGlyphCount - 1;  /* only InputGlyphCount - 1 elements */
2315 
2316     if ( ALLOC_ARRAY( csr->Input, count, UShort ) )
2317       goto Fail4;
2318 
2319     i = csr->Input;
2320 
2321     if ( ACCESS_Frame( count * 2L ) )
2322       goto Fail3;
2323 
2324     for ( n = 0; n < count; n++ )
2325       i[n] = GET_UShort();
2326 
2327     FORGET_Frame();
2328 
2329     if ( ACCESS_Frame( 2L ) )
2330       goto Fail3;
2331 
2332     csr->LookaheadGlyphCount = GET_UShort();
2333 
2334     FORGET_Frame();
2335 
2336     csr->Lookahead = NULL;
2337 
2338     count = csr->LookaheadGlyphCount;
2339 
2340     if ( ALLOC_ARRAY( csr->Lookahead, count, UShort ) )
2341       goto Fail3;
2342 
2343     l = csr->Lookahead;
2344 
2345     if ( ACCESS_Frame( count * 2L ) )
2346       goto Fail2;
2347 
2348     for ( n = 0; n < count; n++ )
2349       l[n] = GET_UShort();
2350 
2351     FORGET_Frame();
2352 
2353     if ( ACCESS_Frame( 2L ) )
2354       goto Fail2;
2355 
2356     csr->SubstCount = GET_UShort();
2357 
2358     FORGET_Frame();
2359 
2360     csr->SubstLookupRecord = NULL;
2361 
2362     count = csr->SubstCount;
2363 
2364     if ( ALLOC_ARRAY( csr->SubstLookupRecord, count, TTO_SubstLookupRecord ) )
2365       goto Fail2;
2366 
2367     slr = csr->SubstLookupRecord;
2368 
2369     if ( ACCESS_Frame( count * 4L ) )
2370       goto Fail1;
2371 
2372     for ( n = 0; n < count; n++ )
2373     {
2374       slr[n].SequenceIndex   = GET_UShort();
2375       slr[n].LookupListIndex = GET_UShort();
2376     }
2377 
2378     FORGET_Frame();
2379 
2380     return TT_Err_Ok;
2381 
2382   Fail1:
2383     FREE( slr );
2384 
2385   Fail2:
2386     FREE( l );
2387 
2388   Fail3:
2389     FREE( i );
2390 
2391   Fail4:
2392     FREE( b );
2393     return error;
2394   }
2395 
2396 
Free_ChainSubRule(TTO_ChainSubRule * csr)2397   static void  Free_ChainSubRule( TTO_ChainSubRule*  csr )
2398   {
2399     FREE( csr->SubstLookupRecord );
2400     FREE( csr->Lookahead );
2401     FREE( csr->Input );
2402     FREE( csr->Backtrack );
2403   }
2404 
2405 
2406   /* ChainSubRuleSet */
2407 
Load_ChainSubRuleSet(TTO_ChainSubRuleSet * csrs,PFace input)2408   static TT_Error  Load_ChainSubRuleSet( TTO_ChainSubRuleSet*  csrs,
2409                                          PFace                 input )
2410   {
2411     DEFINE_LOAD_LOCALS( input->stream );
2412 
2413     UShort             n, count;
2414     ULong              cur_offset, new_offset, base_offset;
2415 
2416     TTO_ChainSubRule*  csr;
2417 
2418 
2419     base_offset = FILE_Pos();
2420 
2421     if ( ACCESS_Frame( 2L ) )
2422       return error;
2423 
2424     count = csrs->ChainSubRuleCount = GET_UShort();
2425 
2426     FORGET_Frame();
2427 
2428     csrs->ChainSubRule = NULL;
2429 
2430     if ( ALLOC_ARRAY( csrs->ChainSubRule, count, TTO_ChainSubRule ) )
2431       return error;
2432 
2433     csr = csrs->ChainSubRule;
2434 
2435     for ( n = 0; n < count; n++ )
2436     {
2437       if ( ACCESS_Frame( 2L ) )
2438         goto Fail;
2439 
2440       new_offset = GET_UShort() + base_offset;
2441 
2442       FORGET_Frame();
2443 
2444       cur_offset = FILE_Pos();
2445       if ( FILE_Seek( new_offset ) ||
2446            ( error = Load_ChainSubRule( &csr[n], input ) ) != TT_Err_Ok )
2447         goto Fail;
2448       (void)FILE_Seek( cur_offset );
2449     }
2450 
2451     return TT_Err_Ok;
2452 
2453   Fail:
2454     for ( n = 0; n < count; n++ )
2455       Free_ChainSubRule( &csr[n] );
2456 
2457     FREE( csr );
2458     return error;
2459   }
2460 
2461 
Free_ChainSubRuleSet(TTO_ChainSubRuleSet * csrs)2462   static void  Free_ChainSubRuleSet( TTO_ChainSubRuleSet*  csrs )
2463   {
2464     UShort             n, count;
2465 
2466     TTO_ChainSubRule*  csr;
2467 
2468 
2469     if ( csrs->ChainSubRule )
2470     {
2471       count = csrs->ChainSubRuleCount;
2472       csr   = csrs->ChainSubRule;
2473 
2474       for ( n = 0; n < count; n++ )
2475         Free_ChainSubRule( &csr[n] );
2476 
2477       FREE( csr );
2478     }
2479   }
2480 
2481 
2482   /* ChainContextSubstFormat1 */
2483 
Load_ChainContextSubst1(TTO_ChainContextSubstFormat1 * ccsf1,PFace input)2484   static TT_Error  Load_ChainContextSubst1(
2485                      TTO_ChainContextSubstFormat1*  ccsf1,
2486                      PFace                          input )
2487   {
2488     DEFINE_LOAD_LOCALS( input->stream );
2489 
2490     UShort                n, count;
2491     ULong                 cur_offset, new_offset, base_offset;
2492 
2493     TTO_ChainSubRuleSet*  csrs;
2494 
2495 
2496     base_offset = FILE_Pos() - 2L;
2497 
2498     if ( ACCESS_Frame( 2L ) )
2499       return error;
2500 
2501     new_offset = GET_UShort() + base_offset;
2502 
2503     FORGET_Frame();
2504 
2505     cur_offset = FILE_Pos();
2506     if ( FILE_Seek( new_offset ) ||
2507          ( error = Load_Coverage( &ccsf1->Coverage, input ) ) != TT_Err_Ok )
2508       return error;
2509     (void)FILE_Seek( cur_offset );
2510 
2511     if ( ACCESS_Frame( 2L ) )
2512       goto Fail2;
2513 
2514     count = ccsf1->ChainSubRuleSetCount = GET_UShort();
2515 
2516     FORGET_Frame();
2517 
2518     ccsf1->ChainSubRuleSet = NULL;
2519 
2520     if ( ALLOC_ARRAY( ccsf1->ChainSubRuleSet, count, TTO_ChainSubRuleSet ) )
2521       goto Fail2;
2522 
2523     csrs = ccsf1->ChainSubRuleSet;
2524 
2525     for ( n = 0; n < count; n++ )
2526     {
2527       if ( ACCESS_Frame( 2L ) )
2528         goto Fail1;
2529 
2530       new_offset = GET_UShort() + base_offset;
2531 
2532       FORGET_Frame();
2533 
2534       cur_offset = FILE_Pos();
2535       if ( FILE_Seek( new_offset ) ||
2536            ( error = Load_ChainSubRuleSet( &csrs[n], input ) ) != TT_Err_Ok )
2537         goto Fail1;
2538       (void)FILE_Seek( cur_offset );
2539     }
2540 
2541     return TT_Err_Ok;
2542 
2543   Fail1:
2544     for ( n = 0; n < count; n++ )
2545       Free_ChainSubRuleSet( &csrs[n] );
2546 
2547     FREE( csrs );
2548 
2549   Fail2:
2550     Free_Coverage( &ccsf1->Coverage );
2551     return error;
2552   }
2553 
2554 
Free_ChainContext1(TTO_ChainContextSubstFormat1 * ccsf1)2555   static void  Free_ChainContext1( TTO_ChainContextSubstFormat1*  ccsf1 )
2556   {
2557     UShort                n, count;
2558 
2559     TTO_ChainSubRuleSet*  csrs;
2560 
2561 
2562     if ( ccsf1->ChainSubRuleSet )
2563     {
2564       count = ccsf1->ChainSubRuleSetCount;
2565       csrs  = ccsf1->ChainSubRuleSet;
2566 
2567       for ( n = 0; n < count; n++ )
2568         Free_ChainSubRuleSet( &csrs[n] );
2569 
2570       FREE( csrs );
2571     }
2572 
2573     Free_Coverage( &ccsf1->Coverage );
2574   }
2575 
2576 
2577   /* ChainSubClassRule */
2578 
Load_ChainSubClassRule(TTO_ChainContextSubstFormat2 * ccsf2,TTO_ChainSubClassRule * cscr,PFace input)2579   static TT_Error  Load_ChainSubClassRule(
2580                      TTO_ChainContextSubstFormat2*  ccsf2,
2581                      TTO_ChainSubClassRule*         cscr,
2582                      PFace                          input )
2583   {
2584     DEFINE_LOAD_LOCALS( input->stream );
2585 
2586     UShort                  n, count;
2587 
2588     UShort*                 b;
2589     UShort*                 i;
2590     UShort*                 l;
2591     TTO_SubstLookupRecord*  slr;
2592     Bool*                   d;
2593 
2594 
2595     if ( ACCESS_Frame( 2L ) )
2596       return error;
2597 
2598     cscr->BacktrackGlyphCount = GET_UShort();
2599 
2600     FORGET_Frame();
2601 
2602     if ( cscr->BacktrackGlyphCount > ccsf2->MaxBacktrackLength )
2603       ccsf2->MaxBacktrackLength = cscr->BacktrackGlyphCount;
2604 
2605     cscr->Backtrack = NULL;
2606 
2607     count = cscr->BacktrackGlyphCount;
2608 
2609     if ( ALLOC_ARRAY( cscr->Backtrack, count, UShort ) )
2610       return error;
2611 
2612     b = cscr->Backtrack;
2613     d = ccsf2->BacktrackClassDef.Defined;
2614 
2615     if ( ACCESS_Frame( count * 2L ) )
2616       goto Fail4;
2617 
2618     for ( n = 0; n < count; n++ )
2619     {
2620       b[n] = GET_UShort();
2621 
2622       /* We check whether the specific class is used at all.  If not,
2623          class 0 is used instead.                                     */
2624 
2625       if ( !d[b[n]] )
2626         b[n] = 0;
2627     }
2628 
2629     FORGET_Frame();
2630 
2631     if ( ACCESS_Frame( 2L ) )
2632       goto Fail4;
2633 
2634     cscr->InputGlyphCount = GET_UShort();
2635 
2636     FORGET_Frame();
2637 
2638     if ( cscr->InputGlyphCount > ccsf2->MaxInputLength )
2639       ccsf2->MaxInputLength = cscr->InputGlyphCount;
2640 
2641     cscr->Input = NULL;
2642 
2643     count = cscr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */
2644 
2645     if ( ALLOC_ARRAY( cscr->Input, count, UShort ) )
2646       goto Fail4;
2647 
2648     i = cscr->Input;
2649     d = ccsf2->InputClassDef.Defined;
2650 
2651     if ( ACCESS_Frame( count * 2L ) )
2652       goto Fail3;
2653 
2654     for ( n = 0; n < count; n++ )
2655     {
2656       i[n] = GET_UShort();
2657 
2658       if ( !d[i[n]] )
2659         i[n] = 0;
2660     }
2661 
2662     FORGET_Frame();
2663 
2664     if ( ACCESS_Frame( 2L ) )
2665       goto Fail3;
2666 
2667     cscr->LookaheadGlyphCount = GET_UShort();
2668 
2669     FORGET_Frame();
2670 
2671     if ( cscr->LookaheadGlyphCount > ccsf2->MaxLookaheadLength )
2672       ccsf2->MaxLookaheadLength = cscr->LookaheadGlyphCount;
2673 
2674     cscr->Lookahead = NULL;
2675 
2676     count = cscr->LookaheadGlyphCount;
2677 
2678     if ( ALLOC_ARRAY( cscr->Lookahead, count, UShort ) )
2679       goto Fail3;
2680 
2681     l = cscr->Lookahead;
2682     d = ccsf2->LookaheadClassDef.Defined;
2683 
2684     if ( ACCESS_Frame( count * 2L ) )
2685       goto Fail2;
2686 
2687     for ( n = 0; n < count; n++ )
2688     {
2689       l[n] = GET_UShort();
2690 
2691       if ( !d[l[n]] )
2692         l[n] = 0;
2693     }
2694 
2695     FORGET_Frame();
2696 
2697     if ( ACCESS_Frame( 2L ) )
2698       goto Fail2;
2699 
2700     cscr->SubstCount = GET_UShort();
2701 
2702     FORGET_Frame();
2703 
2704     cscr->SubstLookupRecord = NULL;
2705 
2706     count = cscr->SubstCount;
2707 
2708     if ( ALLOC_ARRAY( cscr->SubstLookupRecord, count,
2709                       TTO_SubstLookupRecord ) )
2710       goto Fail2;
2711 
2712     slr = cscr->SubstLookupRecord;
2713 
2714     if ( ACCESS_Frame( count * 4L ) )
2715       goto Fail1;
2716 
2717     for ( n = 0; n < count; n++ )
2718     {
2719       slr[n].SequenceIndex   = GET_UShort();
2720       slr[n].LookupListIndex = GET_UShort();
2721     }
2722 
2723     FORGET_Frame();
2724 
2725     return TT_Err_Ok;
2726 
2727   Fail1:
2728     FREE( slr );
2729 
2730   Fail2:
2731     FREE( l );
2732 
2733   Fail3:
2734     FREE( i );
2735 
2736   Fail4:
2737     FREE( b );
2738     return error;
2739   }
2740 
2741 
Free_ChainSubClassRule(TTO_ChainSubClassRule * cscr)2742   static void  Free_ChainSubClassRule( TTO_ChainSubClassRule*  cscr )
2743   {
2744     FREE( cscr->SubstLookupRecord );
2745     FREE( cscr->Lookahead );
2746     FREE( cscr->Input );
2747     FREE( cscr->Backtrack );
2748   }
2749 
2750 
2751   /* SubClassSet */
2752 
Load_ChainSubClassSet(TTO_ChainContextSubstFormat2 * ccsf2,TTO_ChainSubClassSet * cscs,PFace input)2753   static TT_Error  Load_ChainSubClassSet(
2754                      TTO_ChainContextSubstFormat2*  ccsf2,
2755                      TTO_ChainSubClassSet*          cscs,
2756                      PFace                          input )
2757   {
2758     DEFINE_LOAD_LOCALS( input->stream );
2759 
2760     UShort                  n, count;
2761     ULong                   cur_offset, new_offset, base_offset;
2762 
2763     TTO_ChainSubClassRule*  cscr;
2764 
2765 
2766     base_offset = FILE_Pos();
2767 
2768     if ( ACCESS_Frame( 2L ) )
2769       return error;
2770 
2771     count = cscs->ChainSubClassRuleCount = GET_UShort();
2772 
2773     FORGET_Frame();
2774 
2775     cscs->ChainSubClassRule = NULL;
2776 
2777     if ( ALLOC_ARRAY( cscs->ChainSubClassRule, count,
2778                       TTO_ChainSubClassRule ) )
2779       return error;
2780 
2781     cscr = cscs->ChainSubClassRule;
2782 
2783     for ( n = 0; n < count; n++ )
2784     {
2785       if ( ACCESS_Frame( 2L ) )
2786         goto Fail;
2787 
2788       new_offset = GET_UShort() + base_offset;
2789 
2790       FORGET_Frame();
2791 
2792       cur_offset = FILE_Pos();
2793       if ( FILE_Seek( new_offset ) ||
2794            ( error = Load_ChainSubClassRule( ccsf2, &cscr[n],
2795                                              input ) ) != TT_Err_Ok )
2796         goto Fail;
2797       (void)FILE_Seek( cur_offset );
2798     }
2799 
2800     return TT_Err_Ok;
2801 
2802   Fail:
2803     for ( n = 0; n < count; n++ )
2804       Free_ChainSubClassRule( &cscr[n] );
2805 
2806     FREE( cscr );
2807     return error;
2808   }
2809 
2810 
Free_ChainSubClassSet(TTO_ChainSubClassSet * cscs)2811   static void  Free_ChainSubClassSet( TTO_ChainSubClassSet*  cscs )
2812   {
2813     UShort                  n, count;
2814 
2815     TTO_ChainSubClassRule*  cscr;
2816 
2817 
2818     if ( cscs->ChainSubClassRule )
2819     {
2820       count = cscs->ChainSubClassRuleCount;
2821       cscr  = cscs->ChainSubClassRule;
2822 
2823       for ( n = 0; n < count; n++ )
2824         Free_ChainSubClassRule( &cscr[n] );
2825 
2826       FREE( cscr );
2827     }
2828   }
2829 
2830 
2831   /* ChainContextSubstFormat2 */
2832 
Load_ChainContextSubst2(TTO_ChainContextSubstFormat2 * ccsf2,PFace input)2833   static TT_Error  Load_ChainContextSubst2(
2834                      TTO_ChainContextSubstFormat2*  ccsf2,
2835                      PFace                          input )
2836   {
2837     DEFINE_LOAD_LOCALS( input->stream );
2838 
2839     UShort                 n, count;
2840     ULong                  cur_offset, new_offset, base_offset;
2841     ULong                  backtrack_offset, input_offset, lookahead_offset;
2842 
2843     TTO_ChainSubClassSet*  cscs;
2844 
2845 
2846     base_offset = FILE_Pos() - 2;
2847 
2848     if ( ACCESS_Frame( 2L ) )
2849       return error;
2850 
2851     new_offset = GET_UShort() + base_offset;
2852 
2853     FORGET_Frame();
2854 
2855     cur_offset = FILE_Pos();
2856     if ( FILE_Seek( new_offset ) ||
2857          ( error = Load_Coverage( &ccsf2->Coverage, input ) ) != TT_Err_Ok )
2858       return error;
2859     (void)FILE_Seek( cur_offset );
2860 
2861     if ( ACCESS_Frame( 8L ) )
2862       goto Fail5;
2863 
2864     backtrack_offset = GET_UShort() + base_offset;
2865     input_offset     = GET_UShort() + base_offset;
2866     lookahead_offset = GET_UShort() + base_offset;
2867 
2868     /* `ChainSubClassSetCount' is the upper limit for input class values,
2869        thus we read it now to make an additional safety check.            */
2870 
2871     count = ccsf2->ChainSubClassSetCount = GET_UShort();
2872 
2873     FORGET_Frame();
2874 
2875     cur_offset = FILE_Pos();
2876     if ( FILE_Seek( backtrack_offset ) ||
2877          ( error = Load_ClassDefinition( &ccsf2->BacktrackClassDef, count,
2878                                          input ) ) != TT_Err_Ok )
2879       goto Fail5;
2880     if ( FILE_Seek( input_offset ) ||
2881          ( error = Load_ClassDefinition( &ccsf2->InputClassDef, count,
2882                                          input ) ) != TT_Err_Ok )
2883       goto Fail4;
2884     if ( FILE_Seek( lookahead_offset ) ||
2885          ( error = Load_ClassDefinition( &ccsf2->LookaheadClassDef, count,
2886                                          input ) ) != TT_Err_Ok )
2887       goto Fail3;
2888     (void)FILE_Seek( cur_offset );
2889 
2890     ccsf2->ChainSubClassSet   = NULL;
2891     ccsf2->MaxBacktrackLength = 0;
2892     ccsf2->MaxInputLength     = 0;
2893     ccsf2->MaxLookaheadLength = 0;
2894 
2895     if ( ALLOC_ARRAY( ccsf2->ChainSubClassSet, count, TTO_ChainSubClassSet ) )
2896       goto Fail2;
2897 
2898     cscs = ccsf2->ChainSubClassSet;
2899 
2900     for ( n = 0; n < count; n++ )
2901     {
2902       if ( ACCESS_Frame( 2L ) )
2903         goto Fail1;
2904 
2905       new_offset = GET_UShort() + base_offset;
2906 
2907       FORGET_Frame();
2908 
2909       if ( new_offset != base_offset )      /* not a NULL offset */
2910       {
2911         cur_offset = FILE_Pos();
2912         if ( FILE_Seek( new_offset ) ||
2913              ( error = Load_ChainSubClassSet( ccsf2, &cscs[n],
2914                                               input ) ) != TT_Err_Ok )
2915           goto Fail1;
2916         (void)FILE_Seek( cur_offset );
2917       }
2918       else
2919       {
2920         /* we create a ChainSubClassSet table with no entries */
2921 
2922         ccsf2->ChainSubClassSet[n].ChainSubClassRuleCount = 0;
2923         ccsf2->ChainSubClassSet[n].ChainSubClassRule      = NULL;
2924       }
2925     }
2926 
2927     return TT_Err_Ok;
2928 
2929   Fail1:
2930     for ( n = 0; n < count; n++ )
2931       Free_ChainSubClassSet( &cscs[n] );
2932 
2933     FREE( cscs );
2934 
2935   Fail2:
2936     Free_ClassDefinition( &ccsf2->LookaheadClassDef );
2937 
2938   Fail3:
2939     Free_ClassDefinition( &ccsf2->InputClassDef );
2940 
2941   Fail4:
2942     Free_ClassDefinition( &ccsf2->BacktrackClassDef );
2943 
2944   Fail5:
2945     Free_Coverage( &ccsf2->Coverage );
2946     return error;
2947   }
2948 
2949 
Free_ChainContext2(TTO_ChainContextSubstFormat2 * ccsf2)2950   static void  Free_ChainContext2( TTO_ChainContextSubstFormat2*  ccsf2 )
2951   {
2952     UShort                 n, count;
2953 
2954     TTO_ChainSubClassSet*  cscs;
2955 
2956 
2957     if ( ccsf2->ChainSubClassSet )
2958     {
2959       count = ccsf2->ChainSubClassSetCount;
2960       cscs  = ccsf2->ChainSubClassSet;
2961 
2962       for ( n = 0; n < count; n++ )
2963         Free_ChainSubClassSet( &cscs[n] );
2964 
2965       FREE( cscs );
2966     }
2967 
2968     Free_ClassDefinition( &ccsf2->LookaheadClassDef );
2969     Free_ClassDefinition( &ccsf2->InputClassDef );
2970     Free_ClassDefinition( &ccsf2->BacktrackClassDef );
2971 
2972     Free_Coverage( &ccsf2->Coverage );
2973   }
2974 
2975 
2976   /* ChainContextSubstFormat3 */
2977 
Load_ChainContextSubst3(TTO_ChainContextSubstFormat3 * ccsf3,PFace input)2978   static TT_Error  Load_ChainContextSubst3(
2979                      TTO_ChainContextSubstFormat3*  ccsf3,
2980                      PFace                          input )
2981   {
2982     DEFINE_LOAD_LOCALS( input->stream );
2983 
2984     UShort                  n, count;
2985     UShort                  backtrack_count, input_count, lookahead_count;
2986     ULong                   cur_offset, new_offset, base_offset;
2987 
2988     TTO_Coverage*           b;
2989     TTO_Coverage*           i;
2990     TTO_Coverage*           l;
2991     TTO_SubstLookupRecord*  slr;
2992 
2993 
2994     base_offset = FILE_Pos() - 2L;
2995 
2996     if ( ACCESS_Frame( 2L ) )
2997       return error;
2998 
2999     ccsf3->BacktrackGlyphCount = GET_UShort();
3000 
3001     FORGET_Frame();
3002 
3003     ccsf3->BacktrackCoverage = NULL;
3004 
3005     backtrack_count = ccsf3->BacktrackGlyphCount;
3006 
3007     if ( ALLOC_ARRAY( ccsf3->BacktrackCoverage, backtrack_count,
3008                       TTO_Coverage ) )
3009       return error;
3010 
3011     b = ccsf3->BacktrackCoverage;
3012 
3013     for ( n = 0; n < backtrack_count; n++ )
3014     {
3015       if ( ACCESS_Frame( 2L ) )
3016         goto Fail4;
3017 
3018       new_offset = GET_UShort() + base_offset;
3019 
3020       FORGET_Frame();
3021 
3022       cur_offset = FILE_Pos();
3023       if ( FILE_Seek( new_offset ) ||
3024            ( error = Load_Coverage( &b[n], input ) ) != TT_Err_Ok )
3025         goto Fail4;
3026       (void)FILE_Seek( cur_offset );
3027     }
3028 
3029     if ( ACCESS_Frame( 2L ) )
3030       goto Fail4;
3031 
3032     ccsf3->InputGlyphCount = GET_UShort();
3033 
3034     FORGET_Frame();
3035 
3036     ccsf3->InputCoverage = NULL;
3037 
3038     input_count = ccsf3->InputGlyphCount;
3039 
3040     if ( ALLOC_ARRAY( ccsf3->InputCoverage, input_count, TTO_Coverage ) )
3041       goto Fail4;
3042 
3043     i = ccsf3->InputCoverage;
3044 
3045     for ( n = 0; n < input_count; n++ )
3046     {
3047       if ( ACCESS_Frame( 2L ) )
3048         goto Fail3;
3049 
3050       new_offset = GET_UShort() + base_offset;
3051 
3052       FORGET_Frame();
3053 
3054       cur_offset = FILE_Pos();
3055       if ( FILE_Seek( new_offset ) ||
3056            ( error = Load_Coverage( &i[n], input ) ) != TT_Err_Ok )
3057         goto Fail3;
3058       (void)FILE_Seek( cur_offset );
3059     }
3060 
3061     if ( ACCESS_Frame( 2L ) )
3062       goto Fail3;
3063 
3064     ccsf3->LookaheadGlyphCount = GET_UShort();
3065 
3066     FORGET_Frame();
3067 
3068     ccsf3->LookaheadCoverage = NULL;
3069 
3070     lookahead_count = ccsf3->LookaheadGlyphCount;
3071 
3072     if ( ALLOC_ARRAY( ccsf3->LookaheadCoverage, lookahead_count,
3073                       TTO_Coverage ) )
3074       goto Fail3;
3075 
3076     l = ccsf3->LookaheadCoverage;
3077 
3078     for ( n = 0; n < lookahead_count; n++ )
3079     {
3080       if ( ACCESS_Frame( 2L ) )
3081         goto Fail2;
3082 
3083       new_offset = GET_UShort() + base_offset;
3084 
3085       FORGET_Frame();
3086 
3087       cur_offset = FILE_Pos();
3088       if ( FILE_Seek( new_offset ) ||
3089            ( error = Load_Coverage( &l[n], input ) ) != TT_Err_Ok )
3090         goto Fail2;
3091       (void)FILE_Seek( cur_offset );
3092     }
3093 
3094     if ( ACCESS_Frame( 2L ) )
3095       goto Fail2;
3096 
3097     ccsf3->SubstCount = GET_UShort();
3098 
3099     FORGET_Frame();
3100 
3101     ccsf3->SubstLookupRecord = NULL;
3102 
3103     count = ccsf3->SubstCount;
3104 
3105     if ( ALLOC_ARRAY( ccsf3->SubstLookupRecord, count,
3106                       TTO_SubstLookupRecord ) )
3107       goto Fail2;
3108 
3109     slr = ccsf3->SubstLookupRecord;
3110 
3111     if ( ACCESS_Frame( count * 4L ) )
3112       goto Fail1;
3113 
3114     for ( n = 0; n < count; n++ )
3115     {
3116       slr[n].SequenceIndex   = GET_UShort();
3117       slr[n].LookupListIndex = GET_UShort();
3118     }
3119 
3120     FORGET_Frame();
3121 
3122     return TT_Err_Ok;
3123 
3124   Fail1:
3125     FREE( slr );
3126 
3127   Fail2:
3128     for ( n = 0; n < lookahead_count; n++ )
3129       Free_Coverage( &l[n] );
3130 
3131     FREE( l );
3132 
3133   Fail3:
3134     for ( n = 0; n < input_count; n++ )
3135       Free_Coverage( &i[n] );
3136 
3137     FREE( i );
3138 
3139   Fail4:
3140     for ( n = 0; n < backtrack_count; n++ )
3141       Free_Coverage( &b[n] );
3142 
3143     FREE( b );
3144     return error;
3145   }
3146 
3147 
Free_ChainContext3(TTO_ChainContextSubstFormat3 * ccsf3)3148   static void  Free_ChainContext3( TTO_ChainContextSubstFormat3*  ccsf3 )
3149   {
3150     UShort         n, count;
3151 
3152     TTO_Coverage*  c;
3153 
3154 
3155     FREE( ccsf3->SubstLookupRecord );
3156 
3157     if ( ccsf3->LookaheadCoverage )
3158     {
3159       count = ccsf3->LookaheadGlyphCount;
3160       c     = ccsf3->LookaheadCoverage;
3161 
3162       for ( n = 0; n < count; n++ )
3163         Free_Coverage( &c[n] );
3164 
3165       FREE( c );
3166     }
3167 
3168     if ( ccsf3->InputCoverage )
3169     {
3170       count = ccsf3->InputGlyphCount;
3171       c     = ccsf3->InputCoverage;
3172 
3173       for ( n = 0; n < count; n++ )
3174         Free_Coverage( &c[n] );
3175 
3176       FREE( c );
3177     }
3178 
3179     if ( ccsf3->BacktrackCoverage )
3180     {
3181       count = ccsf3->BacktrackGlyphCount;
3182       c     = ccsf3->BacktrackCoverage;
3183 
3184       for ( n = 0; n < count; n++ )
3185         Free_Coverage( &c[n] );
3186 
3187       FREE( c );
3188     }
3189   }
3190 
3191 
3192   /* ChainContextSubst */
3193 
Load_ChainContextSubst(TTO_ChainContextSubst * ccs,PFace input)3194   TT_Error  Load_ChainContextSubst( TTO_ChainContextSubst*  ccs,
3195                                     PFace                   input )
3196   {
3197     DEFINE_LOAD_LOCALS( input->stream );
3198 
3199 
3200     if ( ACCESS_Frame( 2L ) )
3201       return error;
3202 
3203     ccs->SubstFormat = GET_UShort();
3204 
3205     FORGET_Frame();
3206 
3207     switch ( ccs->SubstFormat )
3208     {
3209     case 1:
3210       return Load_ChainContextSubst1( &ccs->ccsf.ccsf1, input );
3211 
3212     case 2:
3213       return Load_ChainContextSubst2( &ccs->ccsf.ccsf2, input );
3214 
3215     case 3:
3216       return Load_ChainContextSubst3( &ccs->ccsf.ccsf3, input );
3217 
3218     default:
3219       return TTO_Err_Invalid_GSUB_SubTable_Format;
3220     }
3221 
3222     return TT_Err_Ok;               /* never reached */
3223   }
3224 
3225 
Free_ChainContextSubst(TTO_ChainContextSubst * ccs)3226   void  Free_ChainContextSubst( TTO_ChainContextSubst*  ccs )
3227   {
3228     switch ( ccs->SubstFormat )
3229     {
3230     case 1:
3231       Free_ChainContext1( &ccs->ccsf.ccsf1 );
3232       break;
3233 
3234     case 2:
3235       Free_ChainContext2( &ccs->ccsf.ccsf2 );
3236       break;
3237 
3238     case 3:
3239       Free_ChainContext3( &ccs->ccsf.ccsf3 );
3240       break;
3241     }
3242   }
3243 
3244 
Lookup_ChainContextSubst1(TTO_GSUBHeader * gsub,TTO_ChainContextSubstFormat1 * ccsf1,TTO_GSUB_String * in,TTO_GSUB_String * out,UShort flags,UShort context_length,int nesting_level)3245   static TT_Error  Lookup_ChainContextSubst1(
3246                      TTO_GSUBHeader*                gsub,
3247                      TTO_ChainContextSubstFormat1*  ccsf1,
3248                      TTO_GSUB_String*               in,
3249                      TTO_GSUB_String*               out,
3250                      UShort                         flags,
3251                      UShort                         context_length,
3252                      int                            nesting_level )
3253   {
3254     UShort             index, property;
3255     UShort             i, j, k, num_csr, curr_pos;
3256     UShort             bgc, igc, lgc;
3257     TT_Error           error;
3258     UShort*            s_in;
3259 
3260     TTO_ChainSubRule*  csr;
3261     TTO_ChainSubRule   curr_csr;
3262     TTO_GDEFHeader*    gdef;
3263 
3264 
3265     gdef = gsub->gdef;
3266 
3267     if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
3268       return error;
3269 
3270     error = Coverage_Index( &ccsf1->Coverage, in->string[in->pos], &index );
3271     if ( error )
3272       return error;
3273 
3274     csr     = ccsf1->ChainSubRuleSet[index].ChainSubRule;
3275     num_csr = ccsf1->ChainSubRuleSet[index].ChainSubRuleCount;
3276 
3277     for ( k = 0; k < num_csr; k++ )
3278     {
3279       curr_csr = csr[k];
3280       bgc      = curr_csr.BacktrackGlyphCount;
3281       igc      = curr_csr.InputGlyphCount;
3282       lgc      = curr_csr.LookaheadGlyphCount;
3283 
3284       if ( context_length != 0xFFFF && context_length < igc )
3285         continue;
3286 
3287       /* check whether context is too long; it is a first guess only */
3288 
3289       if ( bgc > in->pos || in->pos + igc + lgc > in->length )
3290         continue;
3291 
3292       if ( bgc )
3293       {
3294         /* Since we don't know in advance the number of glyphs to inspect,
3295            we search backwards for matches in the backtrack glyph array    */
3296 
3297         curr_pos = 0;
3298         s_in     = &in->string[curr_pos];
3299 
3300         for ( i = bgc, j = in->pos - 1; i > 0; i--, j-- )
3301         {
3302           while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
3303           {
3304             if ( error && error != TTO_Err_Not_Covered )
3305               return error;
3306 
3307             if ( j > curr_pos )
3308               j--;
3309             else
3310               break;
3311           }
3312 
3313           if ( s_in[j] != curr_csr.Backtrack[i - 1] )
3314             break;
3315         }
3316 
3317         if ( i != 0 )
3318           continue;
3319       }
3320 
3321       curr_pos = in->pos;
3322       s_in     = &in->string[curr_pos];
3323 
3324       /* Start at 1 because [0] is implied */
3325 
3326       for ( i = 1, j = 1; i < igc; i++, j++ )
3327       {
3328         while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
3329         {
3330           if ( error && error != TTO_Err_Not_Covered )
3331             return error;
3332 
3333           if ( curr_pos + j < in->length )
3334             j++;
3335           else
3336             break;
3337         }
3338 
3339         if ( s_in[j] != curr_csr.Input[i - 1] )
3340           break;
3341       }
3342 
3343       if ( i != igc )
3344         continue;
3345 
3346       /* we are starting to check for lookahead glyphs right after the
3347          last context glyph                                            */
3348 
3349       curr_pos = j;
3350       s_in     = &in->string[curr_pos];
3351 
3352       for ( i = 0, j = 0; i < lgc; i++, j++ )
3353       {
3354         while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
3355         {
3356           if ( error && error != TTO_Err_Not_Covered )
3357             return error;
3358 
3359           if ( curr_pos + j < in->length )
3360             j++;
3361           else
3362             break;
3363         }
3364 
3365         if ( s_in[j] != curr_csr.Lookahead[i] )
3366           break;
3367       }
3368 
3369       if ( i == lgc )
3370         return Do_ContextSubst( gsub, igc,
3371                                 curr_csr.SubstCount,
3372                                 curr_csr.SubstLookupRecord,
3373                                 in, out,
3374                                 nesting_level );
3375     }
3376 
3377     return TTO_Err_Not_Covered;
3378   }
3379 
3380 
Lookup_ChainContextSubst2(TTO_GSUBHeader * gsub,TTO_ChainContextSubstFormat2 * ccsf2,TTO_GSUB_String * in,TTO_GSUB_String * out,UShort flags,UShort context_length,int nesting_level)3381   static TT_Error  Lookup_ChainContextSubst2(
3382                      TTO_GSUBHeader*                gsub,
3383                      TTO_ChainContextSubstFormat2*  ccsf2,
3384                      TTO_GSUB_String*               in,
3385                      TTO_GSUB_String*               out,
3386                      UShort                         flags,
3387                      UShort                         context_length,
3388                      int                            nesting_level )
3389   {
3390     UShort                 index, property;
3391     TT_Error               error;
3392     UShort                 i, j, k, curr_pos;
3393     UShort                 bgc, igc, lgc;
3394     UShort                 known_backtrack_classes,
3395                            known_input_classes,
3396                            known_lookahead_classes;
3397 
3398     UShort*                backtrack_classes;
3399     UShort*                input_classes;
3400     UShort*                lookahead_classes;
3401 
3402     UShort*                s_in;
3403 
3404     UShort*                bc;
3405     UShort*                ic;
3406     UShort*                lc;
3407 
3408     TTO_ChainSubClassSet*  cscs;
3409     TTO_ChainSubClassRule  ccsr;
3410     TTO_GDEFHeader*        gdef;
3411 
3412 
3413     gdef = gsub->gdef;
3414 
3415     if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
3416       return error;
3417 
3418     /* Note: The coverage table in format 2 doesn't give an index into
3419              anything.  It just lets us know whether or not we need to
3420              do any lookup at all.                                     */
3421 
3422     error = Coverage_Index( &ccsf2->Coverage, in->string[in->pos], &index );
3423     if ( error )
3424       return error;
3425 
3426     if ( ALLOC_ARRAY( backtrack_classes, ccsf2->MaxBacktrackLength, UShort ) )
3427       return error;
3428     known_backtrack_classes = 0;
3429 
3430     if ( ALLOC_ARRAY( input_classes, ccsf2->MaxInputLength, UShort ) )
3431       goto End3;
3432     known_input_classes = 1;
3433 
3434     if ( ALLOC_ARRAY( lookahead_classes, ccsf2->MaxLookaheadLength, UShort ) )
3435       goto End2;
3436     known_lookahead_classes = 0;
3437 
3438     error = Get_Class( &ccsf2->InputClassDef, in->string[in->pos],
3439                        &input_classes[0], NULL );
3440     if ( error )
3441       goto End1;
3442 
3443     cscs = &ccsf2->ChainSubClassSet[input_classes[0]];
3444     if ( !cscs )
3445     {
3446       error = TTO_Err_Invalid_GSUB_SubTable;
3447       goto End1;
3448     }
3449 
3450     for ( k = 0; k < cscs->ChainSubClassRuleCount; k++ )
3451     {
3452       ccsr = cscs->ChainSubClassRule[k];
3453       bgc  = ccsr.BacktrackGlyphCount;
3454       igc  = ccsr.InputGlyphCount;
3455       lgc  = ccsr.LookaheadGlyphCount;
3456 
3457       if ( context_length != 0xFFFF && context_length < igc )
3458         continue;
3459 
3460       /* check whether context is too long; it is a first guess only */
3461 
3462       if ( bgc > in->pos || in->pos + igc + lgc > in->length )
3463         continue;
3464 
3465       if ( bgc )
3466       {
3467         /* Since we don't know in advance the number of glyphs to inspect,
3468            we search backwards for matches in the backtrack glyph array.
3469            Note that `known_backtrack_classes' starts at index 0.         */
3470 
3471         curr_pos = 0;
3472         s_in     = &in->string[curr_pos];
3473         bc       = ccsr.Backtrack;
3474 
3475         for ( i = 0, j = in->pos - 1; i < bgc; i++, j-- )
3476         {
3477           while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
3478           {
3479             if ( error && error != TTO_Err_Not_Covered )
3480               return error;
3481 
3482             if ( j > curr_pos )
3483               j--;
3484             else
3485               break;
3486           }
3487 
3488           if ( i >= known_backtrack_classes )
3489           {
3490             /* Keeps us from having to do this for each rule */
3491 
3492             error = Get_Class( &ccsf2->BacktrackClassDef, s_in[j],
3493                                &backtrack_classes[i], NULL );
3494             if ( error && error != TTO_Err_Not_Covered )
3495               goto End1;
3496             known_backtrack_classes = i;
3497           }
3498 
3499           if ( bc[bgc - 1 - i] != backtrack_classes[i] )
3500             break;
3501         }
3502 
3503         if ( i != bgc )
3504           continue;
3505       }
3506 
3507       curr_pos = in->pos;
3508       s_in     = &in->string[curr_pos];
3509       ic       = ccsr.Input;
3510 
3511       /* Start at 1 because [0] is implied */
3512 
3513       for ( i = 1, j = 1; i < igc; i++, j++ )
3514       {
3515         while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
3516         {
3517           if ( error && error != TTO_Err_Not_Covered )
3518             goto End1;
3519 
3520           if ( curr_pos + j < in->length )
3521             j++;
3522           else
3523             break;
3524         }
3525 
3526         if ( i >= known_input_classes )
3527         {
3528           error = Get_Class( &ccsf2->InputClassDef, s_in[j],
3529                              &input_classes[i], NULL );
3530           if ( error && error != TTO_Err_Not_Covered )
3531             goto End1;
3532           known_input_classes = i;
3533         }
3534 
3535         if ( ic[i - 1] != input_classes[i] )
3536           break;
3537       }
3538 
3539       if ( i != igc )
3540         continue;
3541 
3542       /* we are starting to check for lookahead glyphs right after the
3543          last context glyph                                            */
3544 
3545       curr_pos = j;
3546       s_in     = &in->string[curr_pos];
3547       lc       = ccsr.Lookahead;
3548 
3549       for ( i = 0, j = 0; i < lgc; i++, j++ )
3550       {
3551         while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
3552         {
3553           if ( error && error != TTO_Err_Not_Covered )
3554             return error;
3555 
3556           if ( curr_pos + j < in->length )
3557             j++;
3558           else
3559             break;
3560         }
3561 
3562         if ( i >= known_lookahead_classes )
3563         {
3564           error = Get_Class( &ccsf2->LookaheadClassDef, s_in[j],
3565                              &lookahead_classes[i], NULL );
3566           if ( error && error != TTO_Err_Not_Covered )
3567             goto End1;
3568           known_lookahead_classes = i;
3569         }
3570 
3571         if ( lc[i] != lookahead_classes[i] )
3572           break;
3573       }
3574 
3575       if ( i == lgc )
3576       {
3577         error = Do_ContextSubst( gsub, igc,
3578                                  ccsr.SubstCount,
3579                                  ccsr.SubstLookupRecord,
3580                                  in, out,
3581                                  nesting_level );
3582         goto End1;
3583       }
3584     }
3585 
3586     error = TTO_Err_Not_Covered;
3587 
3588   End1:
3589     FREE( lookahead_classes );
3590 
3591   End2:
3592     FREE( input_classes );
3593 
3594   End3:
3595     FREE( backtrack_classes );
3596     return error;
3597   }
3598 
3599 
Lookup_ChainContextSubst3(TTO_GSUBHeader * gsub,TTO_ChainContextSubstFormat3 * ccsf3,TTO_GSUB_String * in,TTO_GSUB_String * out,UShort flags,UShort context_length,int nesting_level)3600   static TT_Error  Lookup_ChainContextSubst3(
3601                      TTO_GSUBHeader*                gsub,
3602                      TTO_ChainContextSubstFormat3*  ccsf3,
3603                      TTO_GSUB_String*               in,
3604                      TTO_GSUB_String*               out,
3605                      UShort                         flags,
3606                      UShort                         context_length,
3607                      int                            nesting_level )
3608   {
3609     UShort           index, i, j, curr_pos, property;
3610     UShort           bgc, igc, lgc;
3611     TT_Error         error;
3612     UShort*          s_in;
3613 
3614     TTO_Coverage*    bc;
3615     TTO_Coverage*    ic;
3616     TTO_Coverage*    lc;
3617     TTO_GDEFHeader*  gdef;
3618 
3619 
3620     gdef = gsub->gdef;
3621 
3622     if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
3623       return error;
3624 
3625     bgc = ccsf3->BacktrackGlyphCount;
3626     igc = ccsf3->InputGlyphCount;
3627     lgc = ccsf3->LookaheadGlyphCount;
3628 
3629     if ( context_length != 0xFFFF && context_length < igc )
3630       return TTO_Err_Not_Covered;
3631 
3632     /* check whether context is too long; it is a first guess only */
3633 
3634     if ( bgc > in->pos || in->pos + igc + lgc > in->length )
3635       return TTO_Err_Not_Covered;
3636 
3637     if ( bgc )
3638     {
3639       /* Since we don't know in advance the number of glyphs to inspect,
3640          we search backwards for matches in the backtrack glyph array    */
3641 
3642       curr_pos = 0;
3643       s_in     = &in->string[curr_pos];
3644       bc       = ccsf3->BacktrackCoverage;
3645 
3646       for ( i = bgc, j = in->pos - 1; i > 0; i--, j-- )
3647       {
3648         while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
3649         {
3650           if ( error && error != TTO_Err_Not_Covered )
3651             return error;
3652 
3653           if ( j > curr_pos )
3654             j--;
3655           else
3656             return TTO_Err_Not_Covered;
3657         }
3658 
3659         error = Coverage_Index( &bc[i - 1], s_in[j], &index );
3660         if ( error )
3661           return error;
3662       }
3663     }
3664 
3665     curr_pos = in->pos;
3666     s_in     = &in->string[curr_pos];
3667     ic       = ccsf3->InputCoverage;
3668 
3669     /* Start at 1 because [0] is implied */
3670 
3671     for ( i = 1, j = 1; i < igc; i++, j++ )
3672     {
3673       while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
3674       {
3675         if ( error && error != TTO_Err_Not_Covered )
3676           return error;
3677 
3678         if ( curr_pos + j < in->length )
3679           j++;
3680         else
3681           return TTO_Err_Not_Covered;
3682       }
3683 
3684       error = Coverage_Index( &ic[i], s_in[j], &index );
3685       if ( error )
3686         return error;
3687     }
3688 
3689     /* we are starting for lookahead glyphs right after the last context
3690        glyph                                                             */
3691 
3692     curr_pos = j;
3693     s_in     = &in->string[curr_pos];
3694     lc       = ccsf3->LookaheadCoverage;
3695 
3696     for ( i = 0, j = 0; i < lgc; i++, j++ )
3697     {
3698       while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
3699       {
3700         if ( error && error != TTO_Err_Not_Covered )
3701           return error;
3702 
3703         if ( curr_pos + j < in->length )
3704           j++;
3705         else
3706           return TTO_Err_Not_Covered;
3707       }
3708 
3709       error = Coverage_Index( &lc[i], s_in[j], &index );
3710       if ( error )
3711         return error;
3712     }
3713 
3714     return Do_ContextSubst( gsub, igc,
3715                             ccsf3->SubstCount,
3716                             ccsf3->SubstLookupRecord,
3717                             in, out,
3718                             nesting_level );
3719   }
3720 
3721 
Lookup_ChainContextSubst(TTO_GSUBHeader * gsub,TTO_ChainContextSubst * ccs,TTO_GSUB_String * in,TTO_GSUB_String * out,UShort flags,UShort context_length,int nesting_level)3722   static TT_Error  Lookup_ChainContextSubst(
3723                      TTO_GSUBHeader*         gsub,
3724                      TTO_ChainContextSubst*  ccs,
3725                      TTO_GSUB_String*        in,
3726                      TTO_GSUB_String*        out,
3727                      UShort                  flags,
3728                      UShort                  context_length,
3729                      int                     nesting_level )
3730   {
3731     switch ( ccs->SubstFormat )
3732     {
3733     case 1:
3734       return Lookup_ChainContextSubst1( gsub, &ccs->ccsf.ccsf1, in, out,
3735                                         flags, context_length,
3736                                         nesting_level );
3737 
3738     case 2:
3739       return Lookup_ChainContextSubst2( gsub, &ccs->ccsf.ccsf2, in, out,
3740                                         flags, context_length,
3741                                         nesting_level );
3742 
3743     case 3:
3744       return Lookup_ChainContextSubst3( gsub, &ccs->ccsf.ccsf3, in, out,
3745                                         flags, context_length,
3746                                         nesting_level );
3747 
3748     default:
3749       return TTO_Err_Invalid_GSUB_SubTable_Format;
3750     }
3751 
3752     return TT_Err_Ok;               /* never reached */
3753   }
3754 
3755 
3756 
3757   /***********
3758    * GSUB API
3759    ***********/
3760 
3761 
3762   EXPORT_FUNC
TT_GSUB_Select_Script(TTO_GSUBHeader * gsub,TT_ULong script_tag,TT_UShort * script_index)3763   TT_Error  TT_GSUB_Select_Script( TTO_GSUBHeader*  gsub,
3764                                    TT_ULong         script_tag,
3765                                    TT_UShort*       script_index )
3766   {
3767     UShort             n;
3768 
3769     TTO_ScriptList*    sl;
3770     TTO_ScriptRecord*  sr;
3771 
3772 
3773     if ( !gsub || !script_index )
3774       return TT_Err_Invalid_Argument;
3775 
3776     sl = &gsub->ScriptList;
3777     sr = sl->ScriptRecord;
3778 
3779     for ( n = 0; n < sl->ScriptCount; n++ )
3780       if ( script_tag == sr[n].ScriptTag )
3781       {
3782         *script_index = n;
3783 
3784         return TT_Err_Ok;
3785       }
3786 
3787     return TTO_Err_Not_Covered;
3788   }
3789 
3790 
3791   EXPORT_FUNC
TT_GSUB_Select_Language(TTO_GSUBHeader * gsub,TT_ULong language_tag,TT_UShort script_index,TT_UShort * language_index,TT_UShort * req_feature_index)3792   TT_Error  TT_GSUB_Select_Language( TTO_GSUBHeader*  gsub,
3793                                      TT_ULong         language_tag,
3794                                      TT_UShort        script_index,
3795                                      TT_UShort*       language_index,
3796                                      TT_UShort*       req_feature_index )
3797   {
3798     UShort              n;
3799 
3800     TTO_ScriptList*     sl;
3801     TTO_ScriptRecord*   sr;
3802     TTO_Script*         s;
3803     TTO_LangSysRecord*  lsr;
3804 
3805 
3806     if ( !gsub || !language_index || !req_feature_index )
3807       return TT_Err_Invalid_Argument;
3808 
3809     sl = &gsub->ScriptList;
3810     sr = sl->ScriptRecord;
3811 
3812     if ( script_index >= sl->ScriptCount )
3813       return TT_Err_Invalid_Argument;
3814 
3815     s   = &sr[script_index].Script;
3816     lsr = s->LangSysRecord;
3817 
3818     for ( n = 0; n < s->LangSysCount; n++ )
3819       if ( language_tag == lsr[n].LangSysTag )
3820       {
3821         *language_index = n;
3822         *req_feature_index = lsr[n].LangSys.ReqFeatureIndex;
3823 
3824         return TT_Err_Ok;
3825       }
3826 
3827     return TTO_Err_Not_Covered;
3828   }
3829 
3830 
3831   /* selecting 0xFFFF for language_index asks for the values of the
3832      default language (DefaultLangSys)                              */
3833 
3834   EXPORT_FUNC
TT_GSUB_Select_Feature(TTO_GSUBHeader * gsub,TT_ULong feature_tag,TT_UShort script_index,TT_UShort language_index,TT_UShort * feature_index)3835   TT_Error  TT_GSUB_Select_Feature( TTO_GSUBHeader*  gsub,
3836                                     TT_ULong         feature_tag,
3837                                     TT_UShort        script_index,
3838                                     TT_UShort        language_index,
3839                                     TT_UShort*       feature_index )
3840   {
3841     UShort              n;
3842 
3843     TTO_ScriptList*     sl;
3844     TTO_ScriptRecord*   sr;
3845     TTO_Script*         s;
3846     TTO_LangSysRecord*  lsr;
3847     TTO_LangSys*        ls;
3848     UShort*             fi;
3849 
3850     TTO_FeatureList*    fl;
3851     TTO_FeatureRecord*  fr;
3852 
3853 
3854     if ( !gsub || !feature_index )
3855       return TT_Err_Invalid_Argument;
3856 
3857     sl = &gsub->ScriptList;
3858     sr = sl->ScriptRecord;
3859 
3860     fl = &gsub->FeatureList;
3861     fr = fl->FeatureRecord;
3862 
3863     if ( script_index >= sl->ScriptCount )
3864       return TT_Err_Invalid_Argument;
3865 
3866     s   = &sr[script_index].Script;
3867     lsr = s->LangSysRecord;
3868 
3869     if ( language_index == 0xFFFF )
3870       ls = &s->DefaultLangSys;
3871     else
3872     {
3873       if ( language_index >= s->LangSysCount )
3874         return TT_Err_Invalid_Argument;
3875 
3876       ls = &lsr[language_index].LangSys;
3877     }
3878 
3879     fi = ls->FeatureIndex;
3880 
3881     for ( n = 0; n < ls->FeatureCount; n++ )
3882     {
3883       if ( fi[n] >= fl->FeatureCount )
3884         return TTO_Err_Invalid_GSUB_SubTable_Format;
3885 
3886       if ( feature_tag == fr[fi[n]].FeatureTag )
3887       {
3888         *feature_index = fi[n];
3889 
3890         return TT_Err_Ok;
3891       }
3892     }
3893 
3894     return TTO_Err_Not_Covered;
3895   }
3896 
3897 
3898   /* The next three functions return a null-terminated list */
3899 
3900   EXPORT_FUNC
TT_GSUB_Query_Scripts(TTO_GSUBHeader * gsub,TT_ULong ** script_tag_list)3901   TT_Error  TT_GSUB_Query_Scripts( TTO_GSUBHeader*  gsub,
3902                                    TT_ULong**       script_tag_list )
3903   {
3904     UShort             n;
3905     TT_Error           error;
3906     ULong*             stl;
3907 
3908     TTO_ScriptList*    sl;
3909     TTO_ScriptRecord*  sr;
3910 
3911 
3912     if ( !gsub || !script_tag_list )
3913       return TT_Err_Invalid_Argument;
3914 
3915     sl = &gsub->ScriptList;
3916     sr = sl->ScriptRecord;
3917 
3918     if ( ALLOC_ARRAY( stl, sl->ScriptCount + 1, ULong ) )
3919       return error;
3920 
3921     for ( n = 0; n < sl->ScriptCount; n++ )
3922       stl[n] = sr[n].ScriptTag;
3923     stl[n] = 0;
3924 
3925     *script_tag_list = stl;
3926 
3927     return TT_Err_Ok;
3928   }
3929 
3930 
3931   EXPORT_FUNC
TT_GSUB_Query_Languages(TTO_GSUBHeader * gsub,TT_UShort script_index,TT_ULong ** language_tag_list)3932   TT_Error  TT_GSUB_Query_Languages( TTO_GSUBHeader*  gsub,
3933                                      TT_UShort        script_index,
3934                                      TT_ULong**       language_tag_list )
3935   {
3936     UShort              n;
3937     TT_Error            error;
3938     ULong*              ltl;
3939 
3940     TTO_ScriptList*     sl;
3941     TTO_ScriptRecord*   sr;
3942     TTO_Script*         s;
3943     TTO_LangSysRecord*  lsr;
3944 
3945 
3946     if ( !gsub || !language_tag_list )
3947       return TT_Err_Invalid_Argument;
3948 
3949     sl = &gsub->ScriptList;
3950     sr = sl->ScriptRecord;
3951 
3952     if ( script_index >= sl->ScriptCount )
3953       return TT_Err_Invalid_Argument;
3954 
3955     s   = &sr[script_index].Script;
3956     lsr = s->LangSysRecord;
3957 
3958     if ( ALLOC_ARRAY( ltl, s->LangSysCount + 1, ULong ) )
3959       return error;
3960 
3961     for ( n = 0; n < s->LangSysCount; n++ )
3962       ltl[n] = lsr[n].LangSysTag;
3963     ltl[n] = 0;
3964 
3965     *language_tag_list = ltl;
3966 
3967     return TT_Err_Ok;
3968   }
3969 
3970 
3971   /* selecting 0xFFFF for language_index asks for the values of the
3972      default language (DefaultLangSys)                              */
3973 
3974   EXPORT_FUNC
TT_GSUB_Query_Features(TTO_GSUBHeader * gsub,TT_UShort script_index,TT_UShort language_index,TT_ULong ** feature_tag_list)3975   TT_Error  TT_GSUB_Query_Features( TTO_GSUBHeader*  gsub,
3976                                     TT_UShort        script_index,
3977                                     TT_UShort        language_index,
3978                                     TT_ULong**       feature_tag_list )
3979   {
3980     UShort              n;
3981     TT_Error            error;
3982     ULong*              ftl;
3983 
3984     TTO_ScriptList*     sl;
3985     TTO_ScriptRecord*   sr;
3986     TTO_Script*         s;
3987     TTO_LangSysRecord*  lsr;
3988     TTO_LangSys*        ls;
3989     UShort*             fi;
3990 
3991     TTO_FeatureList*    fl;
3992     TTO_FeatureRecord*  fr;
3993 
3994 
3995     if ( !gsub || !feature_tag_list )
3996       return TT_Err_Invalid_Argument;
3997 
3998     sl = &gsub->ScriptList;
3999     sr = sl->ScriptRecord;
4000 
4001     fl = &gsub->FeatureList;
4002     fr = fl->FeatureRecord;
4003 
4004     if ( script_index >= sl->ScriptCount )
4005       return TT_Err_Invalid_Argument;
4006 
4007     s   = &sr[script_index].Script;
4008     lsr = s->LangSysRecord;
4009 
4010     if ( language_index == 0xFFFF )
4011       ls = &s->DefaultLangSys;
4012     else
4013     {
4014       if ( language_index >= s->LangSysCount )
4015         return TT_Err_Invalid_Argument;
4016 
4017       ls = &lsr[language_index].LangSys;
4018     }
4019 
4020     fi = ls->FeatureIndex;
4021 
4022     if ( ALLOC_ARRAY( ftl, ls->FeatureCount + 1, ULong ) )
4023       return error;
4024 
4025     for ( n = 0; n < ls->FeatureCount; n++ )
4026     {
4027       if ( fi[n] >= fl->FeatureCount )
4028       {
4029         FREE( ftl );
4030         return TTO_Err_Invalid_GSUB_SubTable_Format;
4031       }
4032       ftl[n] = fr[fi[n]].FeatureTag;
4033     }
4034     ftl[n] = 0;
4035 
4036     *feature_tag_list = ftl;
4037 
4038     return TT_Err_Ok;
4039   }
4040 
4041 
4042   /* Do an individual subtable lookup.  Returns TT_Err_Ok if substitution
4043      has been done, or TTO_Err_Not_Covered if not.                        */
4044 
Do_Glyph_Lookup(TTO_GSUBHeader * gsub,UShort lookup_index,TTO_GSUB_String * in,TTO_GSUB_String * out,UShort context_length,int nesting_level)4045   static TT_Error  Do_Glyph_Lookup( TTO_GSUBHeader*   gsub,
4046                                     UShort            lookup_index,
4047                                     TTO_GSUB_String*  in,
4048                                     TTO_GSUB_String*  out,
4049                                     UShort            context_length,
4050                                     int               nesting_level )
4051   {
4052     TT_Error     error = TT_Err_Ok;
4053     UShort       i, flags;
4054     TTO_Lookup*  lo;
4055 
4056 
4057     nesting_level++;
4058 
4059     if ( nesting_level > TTO_MAX_NESTING_LEVEL )
4060       return TTO_Err_Too_Many_Nested_Contexts;
4061 
4062     lo    = &gsub->LookupList.Lookup[lookup_index];
4063     flags = lo->LookupFlag;
4064 
4065     for ( i = 0; i < lo->SubTableCount; i++ )
4066     {
4067       switch ( lo->LookupType )
4068       {
4069       case GSUB_LOOKUP_SINGLE:
4070         error = Lookup_SingleSubst( &lo->SubTable[i].st.gsub.single,
4071                                     in, out,
4072                                     flags, context_length, gsub->gdef );
4073         break;
4074 
4075       case GSUB_LOOKUP_MULTIPLE:
4076         error = Lookup_MultipleSubst( &lo->SubTable[i].st.gsub.multiple,
4077                                       in, out,
4078                                       flags, context_length, gsub->gdef );
4079         break;
4080 
4081       case GSUB_LOOKUP_ALTERNATE:
4082         error = Lookup_AlternateSubst( gsub,
4083                                        &lo->SubTable[i].st.gsub.alternate,
4084                                        in, out,
4085                                        flags, context_length, gsub->gdef );
4086         break;
4087 
4088       case GSUB_LOOKUP_LIGATURE:
4089         error = Lookup_LigatureSubst( &lo->SubTable[i].st.gsub.ligature,
4090                                       in, out,
4091                                       flags, context_length, gsub->gdef );
4092         break;
4093 
4094       case GSUB_LOOKUP_CONTEXT:
4095         error = Lookup_ContextSubst( gsub, &lo->SubTable[i].st.gsub.context,
4096                                      in, out,
4097                                      flags, context_length, nesting_level );
4098         break;
4099 
4100       case GSUB_LOOKUP_CHAIN:
4101         error = Lookup_ChainContextSubst( gsub,
4102                                           &lo->SubTable[i].st.gsub.chain,
4103                                           in, out,
4104                                           flags, context_length,
4105                                           nesting_level );
4106         break;
4107       }
4108 
4109       /* Check whether we have a successful substitution or an error other
4110          than TTO_Err_Not_Covered                                          */
4111 
4112       if ( error != TTO_Err_Not_Covered )
4113         return error;
4114     }
4115 
4116     return TTO_Err_Not_Covered;
4117   }
4118 
4119 
4120   /* apply one lookup to the input string object */
4121 
Do_String_Lookup(TTO_GSUBHeader * gsub,UShort lookup_index,TTO_GSUB_String * in,TTO_GSUB_String * out)4122   static TT_Error  Do_String_Lookup( TTO_GSUBHeader*   gsub,
4123                                      UShort            lookup_index,
4124                                      TTO_GSUB_String*  in,
4125                                      TTO_GSUB_String*  out )
4126   {
4127     TT_Error  error = TTO_Err_Not_Covered;
4128 
4129     UShort*  properties = gsub->LookupList.Properties;
4130     UShort*  p_in       = in->properties;
4131     UShort*  s_in       = in->string;
4132 
4133     int      nesting_level = 0;
4134 
4135 
4136     while ( in->pos < in->length )
4137     {
4138       if ( ~p_in[in->pos] & properties[lookup_index] )
4139       {
4140         /* 0xFFFF indicates that we don't have a context length yet */
4141         error = Do_Glyph_Lookup( gsub, lookup_index, in, out,
4142                                  0xFFFF, nesting_level );
4143         if ( error && error != TTO_Err_Not_Covered )
4144           return error;
4145       }
4146       else
4147         error = TTO_Err_Not_Covered;
4148 
4149       if ( error == TTO_Err_Not_Covered )
4150         if ( ADD_String( in, 1, out, 1, &s_in[in->pos] ) )
4151           return error;
4152     }
4153 
4154     return error;
4155   }
4156 
4157 
4158   EXPORT_FUNC
TT_GSUB_Add_Feature(TTO_GSUBHeader * gsub,TT_UShort feature_index,TT_UShort property)4159   TT_Error  TT_GSUB_Add_Feature( TTO_GSUBHeader*  gsub,
4160                                  TT_UShort        feature_index,
4161                                  TT_UShort        property )
4162   {
4163     UShort       i;
4164 
4165     TTO_Feature  feature;
4166     UShort*      properties;
4167     UShort*      index;
4168 
4169 
4170     if ( !gsub ||
4171          feature_index >= gsub->FeatureList.FeatureCount )
4172       return TT_Err_Invalid_Argument;
4173 
4174     properties = gsub->LookupList.Properties;
4175 
4176     feature = gsub->FeatureList.FeatureRecord[feature_index].Feature;
4177     index   = feature.LookupListIndex;
4178 
4179     for ( i = 0; i < feature.LookupListCount; i++ )
4180       properties[index[i]] |= property;
4181 
4182     return TT_Err_Ok;
4183   }
4184 
4185 
4186   EXPORT_FUNC
TT_GSUB_Clear_Features(TTO_GSUBHeader * gsub)4187   TT_Error  TT_GSUB_Clear_Features( TTO_GSUBHeader*  gsub )
4188   {
4189     UShort   i;
4190 
4191     UShort*  properties;
4192 
4193 
4194     if ( !gsub )
4195       return TT_Err_Invalid_Argument;
4196 
4197     properties = gsub->LookupList.Properties;
4198 
4199     for ( i = 0; i < gsub->LookupList.LookupCount; i++ )
4200       properties[i] = 0;
4201 
4202     return TT_Err_Ok;
4203   }
4204 
4205 
4206   EXPORT_FUNC
TT_GSUB_Register_Alternate_Function(TTO_GSUBHeader * gsub,TTO_AltFunction alt,void * data)4207   TT_Error  TT_GSUB_Register_Alternate_Function( TTO_GSUBHeader*  gsub,
4208                                                  TTO_AltFunction  alt,
4209                                                  void*            data )
4210   {
4211     if ( !gsub )
4212       return TT_Err_Invalid_Argument;
4213 
4214     gsub->alt  = alt;
4215     gsub->data = data;
4216 
4217     return TT_Err_Ok;
4218   }
4219 
4220 
4221   EXPORT_FUNC
TT_GSUB_Apply_String(TTO_GSUBHeader * gsub,TTO_GSUB_String * in,TTO_GSUB_String * out)4222   TT_Error  TT_GSUB_Apply_String( TTO_GSUBHeader*   gsub,
4223                                   TTO_GSUB_String*  in,
4224                                   TTO_GSUB_String*  out )
4225   {
4226     TT_Error          error = TTO_Err_Not_Covered;
4227     UShort            j;
4228 
4229     TTO_GSUB_String   tmp1;
4230     TTO_GSUB_String*  ptmp1;
4231     TTO_GSUB_String   tmp2;
4232     TTO_GSUB_String*  ptmp2;
4233     TTO_GSUB_String*  t;
4234 
4235     UShort*           properties;
4236 
4237 
4238     if ( !gsub ||
4239          !in || !out || in->length == 0 || in->pos >= in->length )
4240       return TT_Err_Invalid_Argument;
4241 
4242     properties = gsub->LookupList.Properties;
4243 
4244     tmp1.length    = in->length;
4245     tmp1.allocated = in->length;
4246     tmp1.pos       = in->pos;
4247 
4248     if ( ALLOC_ARRAY( tmp1.string, tmp1.length, UShort ) )
4249       return error;
4250     MEM_Copy( tmp1.string, in->string, in->length * sizeof ( UShort ) );
4251 
4252     /* make sure that we always have a `properties' array in the string
4253        object                                                           */
4254 
4255     if ( ALLOC_ARRAY( tmp1.properties, tmp1.length, UShort ) )
4256       return error;
4257     if ( in->properties )
4258       MEM_Copy( tmp1.properties, in->properties,
4259                 in->length * sizeof( UShort ) );
4260 
4261     tmp2.allocated  = 0;
4262     tmp2.pos        = 0;
4263     tmp2.string     = NULL;
4264     tmp2.properties = NULL;
4265 
4266     ptmp1 = &tmp1;
4267     ptmp2 = &tmp2;
4268 
4269     for ( j = 0; j < gsub->LookupList.LookupCount; j++ )
4270       if ( properties[j] )
4271       {
4272         error = Do_String_Lookup( gsub, j, ptmp1, ptmp2 );
4273         if ( error && error != TTO_Err_Not_Covered )
4274           return error;
4275 
4276         /* flipping `in' and `out', preparing for the next loop */
4277 
4278         ptmp1->pos    = in->pos;
4279         ptmp2->length = ptmp2->pos;
4280         ptmp2->pos    = in->pos;
4281 
4282         t     = ptmp2;
4283         ptmp2 = ptmp1;
4284         ptmp1 = t;
4285       }
4286 
4287     out->length    = ptmp1->length;
4288     out->pos       = 0;
4289     out->allocated = ptmp1->allocated;
4290     out->string    = ptmp1->string;
4291 
4292     if ( in->properties )
4293       out->properties = ptmp1->properties;
4294     else
4295     {
4296       free( ptmp1->properties );
4297       out->properties = NULL;
4298     }
4299 
4300     free( ptmp2->string );
4301     free( ptmp2->properties );
4302 
4303     return error;
4304   }
4305 
4306 
4307 /* END */
4308