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