Bug 528272 – "Error loading GPOS table 5503" when using katakana
[framework/uifw/harfbuzz.git] / src / harfbuzz-gpos.c
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-gpos-private.h"
31 #include "harfbuzz-open-private.h"
32 #include "harfbuzz-gdef-private.h"
33
34 struct  GPOS_Instance_
35 {
36   HB_GPOSHeader*  gpos;
37   HB_Font          font;
38   HB_Bool          dvi;
39   HB_UShort        load_flags;  /* how the glyph should be loaded */
40   HB_Bool          r2l;
41
42   HB_UShort        last;        /* the last valid glyph -- used
43                                    with cursive positioning     */
44   HB_Fixed           anchor_x;    /* the coordinates of the anchor point */
45   HB_Fixed           anchor_y;    /* of the last valid glyph             */
46 };
47
48 typedef struct GPOS_Instance_  GPOS_Instance;
49
50
51 static HB_Error  GPOS_Do_Glyph_Lookup( GPOS_Instance*    gpi,
52                                        HB_UShort         lookup_index,
53                                        HB_Buffer        buffer,
54                                        HB_UShort         context_length,
55                                        int               nesting_level );
56
57
58
59 /* the client application must replace this with something more
60    meaningful if multiple master fonts are to be supported.     */
61
62 static HB_Error  default_mmfunc( HB_Font      font,
63                                  HB_UShort    metric_id,
64                                  HB_Fixed*      metric_value,
65                                  void*        data )
66 {
67   HB_UNUSED(font);
68   HB_UNUSED(metric_id);
69   HB_UNUSED(metric_value);
70   HB_UNUSED(data);
71   return ERR(HB_Err_Not_Covered); /* ERR() call intended */
72 }
73
74
75
76 HB_Error  HB_Load_GPOS_Table( HB_Font          font,
77                               HB_GPOSHeader** retptr,
78                               HB_GDEFHeader*  gdef )
79 {
80   HB_UInt         cur_offset, new_offset, base_offset;
81
82   HB_GPOSHeader*  gpos;
83
84   HB_Stream  stream = font->stream;
85   HB_Error   error;
86
87
88   if ( !retptr )
89     return ERR(HB_Err_Invalid_Argument);
90
91   if ( GOTO_Table( TTAG_GPOS ) )
92     return error;
93
94   base_offset = FILE_Pos();
95
96   if ( ALLOC ( gpos, sizeof( *gpos ) ) )
97     return error;
98
99   gpos->gfunc = FT_Load_Glyph;
100   gpos->mmfunc = default_mmfunc;
101
102   /* skip version */
103
104   if ( FILE_Seek( base_offset + 4L ) ||
105        ACCESS_Frame( 2L ) )
106     goto Fail4;
107
108   new_offset = GET_UShort() + base_offset;
109
110   FORGET_Frame();
111
112   cur_offset = FILE_Pos();
113   if ( FILE_Seek( new_offset ) ||
114        ( error = _HB_OPEN_Load_ScriptList( &gpos->ScriptList,
115                                   stream ) ) != HB_Err_Ok )
116     goto Fail4;
117   (void)FILE_Seek( cur_offset );
118
119   if ( ACCESS_Frame( 2L ) )
120     goto Fail3;
121
122   new_offset = GET_UShort() + base_offset;
123
124   FORGET_Frame();
125
126   cur_offset = FILE_Pos();
127   if ( FILE_Seek( new_offset ) ||
128        ( error = _HB_OPEN_Load_FeatureList( &gpos->FeatureList,
129                                    stream ) ) != HB_Err_Ok )
130     goto Fail3;
131   (void)FILE_Seek( cur_offset );
132
133   if ( ACCESS_Frame( 2L ) )
134     goto Fail2;
135
136   new_offset = GET_UShort() + base_offset;
137
138   FORGET_Frame();
139
140   cur_offset = FILE_Pos();
141   if ( FILE_Seek( new_offset ) ||
142        ( error = _HB_OPEN_Load_LookupList( &gpos->LookupList,
143                                   stream, HB_Type_GPOS ) ) != HB_Err_Ok )
144     goto Fail2;
145
146   gpos->gdef = gdef;      /* can be NULL */
147
148   if ( ( error =  _HB_GDEF_LoadMarkAttachClassDef_From_LookupFlags( gdef, stream,
149                                                                      gpos->LookupList.Lookup,
150                                                                      gpos->LookupList.LookupCount ) ) )
151           goto Fail1;
152
153   *retptr = gpos;
154
155   return HB_Err_Ok;
156
157 Fail1:
158   _HB_OPEN_Free_LookupList( &gpos->LookupList, HB_Type_GPOS );
159
160 Fail2:
161   _HB_OPEN_Free_FeatureList( &gpos->FeatureList );
162
163 Fail3:
164   _HB_OPEN_Free_ScriptList( &gpos->ScriptList );
165
166 Fail4:
167   FREE( gpos );
168
169   return error;
170 }
171
172
173 HB_Error  HB_Done_GPOS_Table( HB_GPOSHeader* gpos )
174 {
175   _HB_OPEN_Free_LookupList( &gpos->LookupList, HB_Type_GPOS );
176   _HB_OPEN_Free_FeatureList( &gpos->FeatureList );
177   _HB_OPEN_Free_ScriptList( &gpos->ScriptList );
178
179   FREE( gpos );
180
181   return HB_Err_Ok;
182 }
183
184
185 /*****************************
186  * SubTable related functions
187  *****************************/
188
189 /* shared tables */
190
191 /* ValueRecord */
192
193 /* There is a subtle difference in the specs between a `table' and a
194    `record' -- offsets for device tables in ValueRecords are taken from
195    the parent table and not the parent record.                          */
196
197 static HB_Error  Load_ValueRecord( HB_ValueRecord*  vr,
198                                    HB_UShort         format,
199                                    HB_UInt          base_offset,
200                                    HB_Stream         stream )
201 {
202   HB_Error  error;
203
204   HB_UInt cur_offset, new_offset;
205
206
207   if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT )
208   {
209     if ( ACCESS_Frame( 2L ) )
210       return error;
211
212     vr->XPlacement = GET_Short();
213
214     FORGET_Frame();
215   }
216   else
217     vr->XPlacement = 0;
218
219   if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT )
220   {
221     if ( ACCESS_Frame( 2L ) )
222       return error;
223
224     vr->YPlacement = GET_Short();
225
226     FORGET_Frame();
227   }
228   else
229     vr->YPlacement = 0;
230
231   if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE )
232   {
233     if ( ACCESS_Frame( 2L ) )
234       return error;
235
236     vr->XAdvance = GET_Short();
237
238     FORGET_Frame();
239   }
240   else
241     vr->XAdvance = 0;
242
243   if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE )
244   {
245     if ( ACCESS_Frame( 2L ) )
246       return error;
247
248     vr->YAdvance = GET_Short();
249
250     FORGET_Frame();
251   }
252   else
253     vr->YAdvance = 0;
254
255   if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
256   {
257     if ( ACCESS_Frame( 2L ) )
258       return error;
259
260     new_offset = GET_UShort();
261
262     FORGET_Frame();
263
264     if ( new_offset )
265     {
266       new_offset += base_offset;
267
268       cur_offset = FILE_Pos();
269       if ( FILE_Seek( new_offset ) ||
270            ( error = _HB_OPEN_Load_Device( &vr->XPlacementDevice,
271                                   stream ) ) != HB_Err_Ok )
272         return error;
273       (void)FILE_Seek( cur_offset );
274     }
275     else
276       goto empty1;
277   }
278   else
279   {
280   empty1:
281     vr->XPlacementDevice.StartSize  = 0;
282     vr->XPlacementDevice.EndSize    = 0;
283     vr->XPlacementDevice.DeltaValue = NULL;
284   }
285
286   if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
287   {
288     if ( ACCESS_Frame( 2L ) )
289       goto Fail3;
290
291     new_offset = GET_UShort();
292
293     FORGET_Frame();
294
295     if ( new_offset )
296     {
297       new_offset += base_offset;
298
299       cur_offset = FILE_Pos();
300       if ( FILE_Seek( new_offset ) ||
301            ( error = _HB_OPEN_Load_Device( &vr->YPlacementDevice,
302                                   stream ) ) != HB_Err_Ok )
303         goto Fail3;
304       (void)FILE_Seek( cur_offset );
305     }
306     else
307       goto empty2;
308   }
309   else
310   {
311   empty2:
312     vr->YPlacementDevice.StartSize  = 0;
313     vr->YPlacementDevice.EndSize    = 0;
314     vr->YPlacementDevice.DeltaValue = NULL;
315   }
316
317   if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
318   {
319     if ( ACCESS_Frame( 2L ) )
320       goto Fail2;
321
322     new_offset = GET_UShort();
323
324     FORGET_Frame();
325
326     if ( new_offset )
327     {
328       new_offset += base_offset;
329
330       cur_offset = FILE_Pos();
331       if ( FILE_Seek( new_offset ) ||
332            ( error = _HB_OPEN_Load_Device( &vr->XAdvanceDevice,
333                                   stream ) ) != HB_Err_Ok )
334         goto Fail2;
335       (void)FILE_Seek( cur_offset );
336     }
337     else
338       goto empty3;
339   }
340   else
341   {
342   empty3:
343     vr->XAdvanceDevice.StartSize  = 0;
344     vr->XAdvanceDevice.EndSize    = 0;
345     vr->XAdvanceDevice.DeltaValue = NULL;
346   }
347
348   if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
349   {
350     if ( ACCESS_Frame( 2L ) )
351       goto Fail1;
352
353     new_offset = GET_UShort();
354
355     FORGET_Frame();
356
357     if ( new_offset )
358     {
359       new_offset += base_offset;
360
361       cur_offset = FILE_Pos();
362       if ( FILE_Seek( new_offset ) ||
363            ( error = _HB_OPEN_Load_Device( &vr->YAdvanceDevice,
364                                   stream ) ) != HB_Err_Ok )
365         goto Fail1;
366       (void)FILE_Seek( cur_offset );
367     }
368     else
369       goto empty4;
370   }
371   else
372   {
373   empty4:
374     vr->YAdvanceDevice.StartSize  = 0;
375     vr->YAdvanceDevice.EndSize    = 0;
376     vr->YAdvanceDevice.DeltaValue = NULL;
377   }
378
379   if ( format & HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT )
380   {
381     if ( ACCESS_Frame( 2L ) )
382       goto Fail1;
383
384     vr->XIdPlacement = GET_UShort();
385
386     FORGET_Frame();
387   }
388   else
389     vr->XIdPlacement = 0;
390
391   if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT )
392   {
393     if ( ACCESS_Frame( 2L ) )
394       goto Fail1;
395
396     vr->YIdPlacement = GET_UShort();
397
398     FORGET_Frame();
399   }
400   else
401     vr->YIdPlacement = 0;
402
403   if ( format & HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE )
404   {
405     if ( ACCESS_Frame( 2L ) )
406       goto Fail1;
407
408     vr->XIdAdvance = GET_UShort();
409
410     FORGET_Frame();
411   }
412   else
413     vr->XIdAdvance = 0;
414
415   if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE )
416   {
417     if ( ACCESS_Frame( 2L ) )
418       goto Fail1;
419
420     vr->YIdAdvance = GET_UShort();
421
422     FORGET_Frame();
423   }
424   else
425     vr->YIdAdvance = 0;
426
427   return HB_Err_Ok;
428
429 Fail1:
430   _HB_OPEN_Free_Device( &vr->YAdvanceDevice );
431
432 Fail2:
433   _HB_OPEN_Free_Device( &vr->XAdvanceDevice );
434
435 Fail3:
436   _HB_OPEN_Free_Device( &vr->YPlacementDevice );
437   return error;
438 }
439
440
441 static void  Free_ValueRecord( HB_ValueRecord*  vr,
442                                HB_UShort         format )
443 {
444   if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
445     _HB_OPEN_Free_Device( &vr->YAdvanceDevice );
446   if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
447     _HB_OPEN_Free_Device( &vr->XAdvanceDevice );
448   if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
449     _HB_OPEN_Free_Device( &vr->YPlacementDevice );
450   if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
451     _HB_OPEN_Free_Device( &vr->XPlacementDevice );
452 }
453
454
455 static HB_Error  Get_ValueRecord( GPOS_Instance*    gpi,
456                                   HB_ValueRecord*  vr,
457                                   HB_UShort         format,
458                                   HB_Position      gd )
459 {
460   HB_Fixed           value;
461   HB_Short         pixel_value;
462   HB_Error         error = HB_Err_Ok;
463   HB_GPOSHeader*  gpos = gpi->gpos;
464
465   HB_UShort  x_ppem, y_ppem;
466   HB_16Dot16   x_scale, y_scale;
467
468
469   if ( !format )
470     return HB_Err_Ok;
471
472   x_ppem  = gpi->font->size->metrics.x_ppem;
473   y_ppem  = gpi->font->size->metrics.y_ppem;
474   x_scale = gpi->font->size->metrics.x_scale;
475   y_scale = gpi->font->size->metrics.y_scale;
476
477   /* design units -> fractional pixel */
478
479   if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT )
480     gd->x_pos += x_scale * vr->XPlacement / 0x10000;
481   if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT )
482     gd->y_pos += y_scale * vr->YPlacement / 0x10000;
483   if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE )
484     gd->x_advance += x_scale * vr->XAdvance / 0x10000;
485   if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE )
486     gd->y_advance += y_scale * vr->YAdvance / 0x10000;
487
488   if ( !gpi->dvi )
489   {
490     /* pixel -> fractional pixel */
491
492     if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
493     {
494       _HB_OPEN_Get_Device( &vr->XPlacementDevice, x_ppem, &pixel_value );
495       gd->x_pos += pixel_value << 6;
496     }
497     if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
498     {
499       _HB_OPEN_Get_Device( &vr->YPlacementDevice, y_ppem, &pixel_value );
500       gd->y_pos += pixel_value << 6;
501     }
502     if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
503     {
504       _HB_OPEN_Get_Device( &vr->XAdvanceDevice, x_ppem, &pixel_value );
505       gd->x_advance += pixel_value << 6;
506     }
507     if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
508     {
509       _HB_OPEN_Get_Device( &vr->YAdvanceDevice, y_ppem, &pixel_value );
510       gd->y_advance += pixel_value << 6;
511     }
512   }
513
514   /* values returned from mmfunc() are already in fractional pixels */
515
516   if ( format & HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT )
517   {
518     error = (gpos->mmfunc)( gpi->font, vr->XIdPlacement,
519                             &value, gpos->data );
520     if ( error )
521       return error;
522     gd->x_pos += value;
523   }
524   if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT )
525   {
526     error = (gpos->mmfunc)( gpi->font, vr->YIdPlacement,
527                             &value, gpos->data );
528     if ( error )
529       return error;
530     gd->y_pos += value;
531   }
532   if ( format & HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE )
533   {
534     error = (gpos->mmfunc)( gpi->font, vr->XIdAdvance,
535                             &value, gpos->data );
536     if ( error )
537       return error;
538     gd->x_advance += value;
539   }
540   if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE )
541   {
542     error = (gpos->mmfunc)( gpi->font, vr->YIdAdvance,
543                             &value, gpos->data );
544     if ( error )
545       return error;
546     gd->y_advance += value;
547   }
548
549   return error;
550 }
551
552
553 /* AnchorFormat1 */
554 /* AnchorFormat2 */
555 /* AnchorFormat3 */
556 /* AnchorFormat4 */
557
558 static HB_Error  Load_Anchor( HB_Anchor*  an,
559                               HB_Stream    stream )
560 {
561   HB_Error  error;
562
563   HB_UInt cur_offset, new_offset, base_offset;
564
565
566   base_offset = FILE_Pos();
567
568   if ( ACCESS_Frame( 2L ) )
569     return error;
570
571   an->PosFormat = GET_UShort();
572
573   FORGET_Frame();
574
575   switch ( an->PosFormat )
576   {
577   case 1:
578     if ( ACCESS_Frame( 4L ) )
579       return error;
580
581     an->af.af1.XCoordinate = GET_Short();
582     an->af.af1.YCoordinate = GET_Short();
583
584     FORGET_Frame();
585     break;
586
587   case 2:
588     if ( ACCESS_Frame( 6L ) )
589       return error;
590
591     an->af.af2.XCoordinate = GET_Short();
592     an->af.af2.YCoordinate = GET_Short();
593     an->af.af2.AnchorPoint = GET_UShort();
594
595     FORGET_Frame();
596     break;
597
598   case 3:
599     if ( ACCESS_Frame( 6L ) )
600       return error;
601
602     an->af.af3.XCoordinate = GET_Short();
603     an->af.af3.YCoordinate = GET_Short();
604
605     new_offset = GET_UShort();
606
607     FORGET_Frame();
608
609     if ( new_offset )
610     {
611       new_offset += base_offset;
612
613       cur_offset = FILE_Pos();
614       if ( FILE_Seek( new_offset ) ||
615            ( error = _HB_OPEN_Load_Device( &an->af.af3.XDeviceTable,
616                                   stream ) ) != HB_Err_Ok )
617         return error;
618       (void)FILE_Seek( cur_offset );
619     }
620     else
621     {
622       an->af.af3.XDeviceTable.StartSize  = 0;
623       an->af.af3.XDeviceTable.EndSize    = 0;
624       an->af.af3.XDeviceTable.DeltaValue = NULL;
625     }
626
627     if ( ACCESS_Frame( 2L ) )
628       goto Fail;
629
630     new_offset = GET_UShort();
631
632     FORGET_Frame();
633
634     if ( new_offset )
635     {
636       new_offset += base_offset;
637
638       cur_offset = FILE_Pos();
639       if ( FILE_Seek( new_offset ) ||
640            ( error = _HB_OPEN_Load_Device( &an->af.af3.YDeviceTable,
641                                   stream ) ) != HB_Err_Ok )
642         goto Fail;
643       (void)FILE_Seek( cur_offset );
644     }
645     else
646     {
647       an->af.af3.YDeviceTable.StartSize  = 0;
648       an->af.af3.YDeviceTable.EndSize    = 0;
649       an->af.af3.YDeviceTable.DeltaValue = NULL;
650     }
651     break;
652
653   case 4:
654     if ( ACCESS_Frame( 4L ) )
655       return error;
656
657     an->af.af4.XIdAnchor = GET_UShort();
658     an->af.af4.YIdAnchor = GET_UShort();
659
660     FORGET_Frame();
661     break;
662
663   default:
664     return ERR(HB_Err_Invalid_SubTable_Format);
665   }
666
667   return HB_Err_Ok;
668
669 Fail:
670   _HB_OPEN_Free_Device( &an->af.af3.XDeviceTable );
671   return error;
672 }
673
674
675 static void  Free_Anchor( HB_Anchor*  an )
676 {
677   if ( an->PosFormat == 3 )
678   {
679     _HB_OPEN_Free_Device( &an->af.af3.YDeviceTable );
680     _HB_OPEN_Free_Device( &an->af.af3.XDeviceTable );
681   }
682 }
683
684
685 static HB_Error  Get_Anchor( GPOS_Instance*   gpi,
686                              HB_Anchor*      an,
687                              HB_UShort        glyph_index,
688                              HB_Fixed*          x_value,
689                              HB_Fixed*          y_value )
690 {
691   HB_Error  error = HB_Err_Ok;
692
693   FT_Outline       outline;
694   HB_GPOSHeader*  gpos = gpi->gpos;
695   HB_UShort        ap;
696
697   HB_Short         pixel_value;
698   HB_UShort        load_flags;
699
700   HB_UShort        x_ppem, y_ppem;
701   HB_16Dot16         x_scale, y_scale;
702
703
704   x_ppem  = gpi->font->size->metrics.x_ppem;
705   y_ppem  = gpi->font->size->metrics.y_ppem;
706   x_scale = gpi->font->size->metrics.x_scale;
707   y_scale = gpi->font->size->metrics.y_scale;
708
709   switch ( an->PosFormat )
710   {
711   case 0:
712     /* The special case of an empty AnchorTable */
713   default:
714
715     return HB_Err_Not_Covered;
716
717   case 1:
718     *x_value = x_scale * an->af.af1.XCoordinate / 0x10000;
719     *y_value = y_scale * an->af.af1.YCoordinate / 0x10000;
720     break;
721
722   case 2:
723     /* glyphs must be scaled */
724
725     load_flags = gpi->load_flags & ~FT_LOAD_NO_SCALE;
726
727     if ( !gpi->dvi )
728     {
729       error = (gpos->gfunc)( gpi->font, glyph_index, load_flags );
730       if ( error )
731         return error;
732
733       if ( gpi->font->glyph->format != ft_glyph_format_outline )
734         return ERR(HB_Err_Invalid_SubTable);
735
736       ap = an->af.af2.AnchorPoint;
737
738       outline = gpi->font->glyph->outline;
739
740       /* if n_points is set to zero, we use the design coordinate value pair.
741        * This can happen e.g. for sbit glyphs. */ 
742       if ( !outline.n_points )
743         goto no_contour_point;
744
745       if ( ap >= outline.n_points )
746         return ERR(HB_Err_Invalid_SubTable);
747
748       *x_value = outline.points[ap].x;
749       *y_value = outline.points[ap].y;
750     }
751     else
752     {
753     no_contour_point:
754       *x_value = x_scale * an->af.af3.XCoordinate / 0x10000;
755       *y_value = y_scale * an->af.af3.YCoordinate / 0x10000;
756     }
757     break;
758
759   case 3:
760     if ( !gpi->dvi )
761     {
762       _HB_OPEN_Get_Device( &an->af.af3.XDeviceTable, x_ppem, &pixel_value );
763       *x_value = pixel_value << 6;
764       _HB_OPEN_Get_Device( &an->af.af3.YDeviceTable, y_ppem, &pixel_value );
765       *y_value = pixel_value << 6;
766     }
767     else
768       *x_value = *y_value = 0;
769
770     *x_value += x_scale * an->af.af3.XCoordinate / 0x10000;
771     *y_value += y_scale * an->af.af3.YCoordinate / 0x10000;
772     break;
773
774   case 4:
775     error = (gpos->mmfunc)( gpi->font, an->af.af4.XIdAnchor,
776                             x_value, gpos->data );
777     if ( error )
778       return error;
779
780     error = (gpos->mmfunc)( gpi->font, an->af.af4.YIdAnchor,
781                             y_value, gpos->data );
782     if ( error )
783       return error;
784     break;
785   }
786
787   return error;
788 }
789
790
791 /* MarkArray */
792
793 static HB_Error  Load_MarkArray ( HB_MarkArray*  ma,
794                                   HB_Stream       stream )
795 {
796   HB_Error  error;
797
798   HB_UShort        n, m, count;
799   HB_UInt         cur_offset, new_offset, base_offset;
800
801   HB_MarkRecord*  mr;
802
803
804   base_offset = FILE_Pos();
805
806   if ( ACCESS_Frame( 2L ) )
807     return error;
808
809   count = ma->MarkCount = GET_UShort();
810
811   FORGET_Frame();
812
813   ma->MarkRecord = NULL;
814
815   if ( ALLOC_ARRAY( ma->MarkRecord, count, HB_MarkRecord ) )
816     return error;
817
818   mr = ma->MarkRecord;
819
820   for ( n = 0; n < count; n++ )
821   {
822     if ( ACCESS_Frame( 4L ) )
823       goto Fail;
824
825     mr[n].Class = GET_UShort();
826     new_offset  = GET_UShort() + base_offset;
827
828     FORGET_Frame();
829
830     cur_offset = FILE_Pos();
831     if ( FILE_Seek( new_offset ) ||
832          ( error = Load_Anchor( &mr[n].MarkAnchor, stream ) ) != HB_Err_Ok )
833       goto Fail;
834     (void)FILE_Seek( cur_offset );
835   }
836
837   return HB_Err_Ok;
838
839 Fail:
840   for ( m = 0; m < n; m++ )
841     Free_Anchor( &mr[m].MarkAnchor );
842
843   FREE( mr );
844   return error;
845 }
846
847
848 static void  Free_MarkArray( HB_MarkArray*  ma )
849 {
850   HB_UShort        n, count;
851
852   HB_MarkRecord*  mr;
853
854
855   if ( ma->MarkRecord )
856   {
857     count = ma->MarkCount;
858     mr    = ma->MarkRecord;
859
860     for ( n = 0; n < count; n++ )
861       Free_Anchor( &mr[n].MarkAnchor );
862
863     FREE( mr );
864   }
865 }
866
867
868 /* LookupType 1 */
869
870 /* SinglePosFormat1 */
871 /* SinglePosFormat2 */
872
873 static HB_Error  Load_SinglePos( HB_GPOS_SubTable* st,
874                                  HB_Stream       stream )
875 {
876   HB_Error  error;
877   HB_SinglePos*   sp = &st->single;
878
879   HB_UShort         n, m, count, format;
880   HB_UInt          cur_offset, new_offset, base_offset;
881
882   HB_ValueRecord*  vr;
883
884
885   base_offset = FILE_Pos();
886
887   if ( ACCESS_Frame( 6L ) )
888     return error;
889
890   sp->PosFormat = GET_UShort();
891   new_offset    = GET_UShort() + base_offset;
892
893   format = sp->ValueFormat = GET_UShort();
894
895   FORGET_Frame();
896
897   if ( !format )
898     return ERR(HB_Err_Invalid_SubTable);
899
900   cur_offset = FILE_Pos();
901   if ( FILE_Seek( new_offset ) ||
902        ( error = _HB_OPEN_Load_Coverage( &sp->Coverage, stream ) ) != HB_Err_Ok )
903     return error;
904   (void)FILE_Seek( cur_offset );
905
906   switch ( sp->PosFormat )
907   {
908   case 1:
909     error = Load_ValueRecord( &sp->spf.spf1.Value, format,
910                               base_offset, stream );
911     if ( error )
912       goto Fail2;
913     break;
914
915   case 2:
916     if ( ACCESS_Frame( 2L ) )
917       goto Fail2;
918
919     count = sp->spf.spf2.ValueCount = GET_UShort();
920
921     FORGET_Frame();
922
923     sp->spf.spf2.Value = NULL;
924
925     if ( ALLOC_ARRAY( sp->spf.spf2.Value, count, HB_ValueRecord ) )
926       goto Fail2;
927
928     vr = sp->spf.spf2.Value;
929
930     for ( n = 0; n < count; n++ )
931     {
932       error = Load_ValueRecord( &vr[n], format, base_offset, stream );
933       if ( error )
934         goto Fail1;
935     }
936     break;
937
938   default:
939     return ERR(HB_Err_Invalid_SubTable_Format);
940   }
941
942   return HB_Err_Ok;
943
944 Fail1:
945   for ( m = 0; m < n; m++ )
946     Free_ValueRecord( &vr[m], format );
947
948   FREE( vr );
949
950 Fail2:
951   _HB_OPEN_Free_Coverage( &sp->Coverage );
952   return error;
953 }
954
955
956 static void  Free_SinglePos( HB_GPOS_SubTable* st )
957 {
958   HB_UShort         n, count, format;
959   HB_SinglePos*   sp = &st->single;
960
961   HB_ValueRecord*  v;
962
963
964   format = sp->ValueFormat;
965
966   switch ( sp->PosFormat )
967   {
968   case 1:
969     Free_ValueRecord( &sp->spf.spf1.Value, format );
970     break;
971
972   case 2:
973     if ( sp->spf.spf2.Value )
974     {
975       count = sp->spf.spf2.ValueCount;
976       v     = sp->spf.spf2.Value;
977
978       for ( n = 0; n < count; n++ )
979         Free_ValueRecord( &v[n], format );
980
981       FREE( v );
982     }
983     break;
984   default:
985     break;
986   }
987
988   _HB_OPEN_Free_Coverage( &sp->Coverage );
989 }
990
991 static HB_Error  Lookup_SinglePos( GPOS_Instance*    gpi,
992                                    HB_GPOS_SubTable* st,
993                                    HB_Buffer        buffer,
994                                    HB_UShort         flags,
995                                    HB_UShort         context_length,
996                                    int               nesting_level )
997 {
998   HB_UShort        index, property;
999   HB_Error         error;
1000   HB_GPOSHeader*  gpos = gpi->gpos;
1001   HB_SinglePos*   sp = &st->single;
1002
1003   HB_UNUSED(nesting_level);
1004
1005   if ( context_length != 0xFFFF && context_length < 1 )
1006     return HB_Err_Not_Covered;
1007
1008   if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
1009     return error;
1010
1011   error = _HB_OPEN_Coverage_Index( &sp->Coverage, IN_CURGLYPH(), &index );
1012   if ( error )
1013     return error;
1014
1015   switch ( sp->PosFormat )
1016   {
1017   case 1:
1018     error = Get_ValueRecord( gpi, &sp->spf.spf1.Value,
1019                              sp->ValueFormat, POSITION( buffer->in_pos ) );
1020     if ( error )
1021       return error;
1022     break;
1023
1024   case 2:
1025     if ( index >= sp->spf.spf2.ValueCount )
1026       return ERR(HB_Err_Invalid_SubTable);
1027     error = Get_ValueRecord( gpi, &sp->spf.spf2.Value[index],
1028                              sp->ValueFormat, POSITION( buffer->in_pos ) );
1029     if ( error )
1030       return error;
1031     break;
1032
1033   default:
1034     return ERR(HB_Err_Invalid_SubTable);
1035   }
1036
1037   (buffer->in_pos)++;
1038
1039   return HB_Err_Ok;
1040 }
1041
1042
1043 /* LookupType 2 */
1044
1045 /* PairSet */
1046
1047 static HB_Error  Load_PairSet ( HB_PairSet*  ps,
1048                                 HB_UShort     format1,
1049                                 HB_UShort     format2,
1050                                 HB_Stream     stream )
1051 {
1052   HB_Error  error;
1053
1054   HB_UShort             n, m, count;
1055   HB_UInt              base_offset;
1056
1057   HB_PairValueRecord*  pvr;
1058
1059
1060   base_offset = FILE_Pos();
1061
1062   if ( ACCESS_Frame( 2L ) )
1063     return error;
1064
1065   count = ps->PairValueCount = GET_UShort();
1066
1067   FORGET_Frame();
1068
1069   ps->PairValueRecord = NULL;
1070
1071   if ( ALLOC_ARRAY( ps->PairValueRecord, count, HB_PairValueRecord ) )
1072     return error;
1073
1074   pvr = ps->PairValueRecord;
1075
1076   for ( n = 0; n < count; n++ )
1077   {
1078     if ( ACCESS_Frame( 2L ) )
1079       goto Fail;
1080
1081     pvr[n].SecondGlyph = GET_UShort();
1082
1083     FORGET_Frame();
1084
1085     if ( format1 )
1086     {
1087       error = Load_ValueRecord( &pvr[n].Value1, format1,
1088                                 base_offset, stream );
1089       if ( error )
1090         goto Fail;
1091     }
1092     if ( format2 )
1093     {
1094       error = Load_ValueRecord( &pvr[n].Value2, format2,
1095                                 base_offset, stream );
1096       if ( error )
1097       {
1098         if ( format1 )
1099           Free_ValueRecord( &pvr[n].Value1, format1 );
1100         goto Fail;
1101       }
1102     }
1103   }
1104
1105   return HB_Err_Ok;
1106
1107 Fail:
1108   for ( m = 0; m < n; m++ )
1109   {
1110     if ( format1 )
1111       Free_ValueRecord( &pvr[m].Value1, format1 );
1112     if ( format2 )
1113       Free_ValueRecord( &pvr[m].Value2, format2 );
1114   }
1115
1116   FREE( pvr );
1117   return error;
1118 }
1119
1120
1121 static void  Free_PairSet( HB_PairSet*  ps,
1122                            HB_UShort     format1,
1123                            HB_UShort     format2 )
1124 {
1125   HB_UShort             n, count;
1126
1127   HB_PairValueRecord*  pvr;
1128
1129
1130   if ( ps->PairValueRecord )
1131   {
1132     count = ps->PairValueCount;
1133     pvr   = ps->PairValueRecord;
1134
1135     for ( n = 0; n < count; n++ )
1136     {
1137       if ( format1 )
1138         Free_ValueRecord( &pvr[n].Value1, format1 );
1139       if ( format2 )
1140         Free_ValueRecord( &pvr[n].Value2, format2 );
1141     }
1142
1143     FREE( pvr );
1144   }
1145 }
1146
1147
1148 /* PairPosFormat1 */
1149
1150 static HB_Error  Load_PairPos1( HB_PairPosFormat1*  ppf1,
1151                                 HB_UShort            format1,
1152                                 HB_UShort            format2,
1153                                 HB_Stream            stream )
1154 {
1155   HB_Error  error;
1156
1157   HB_UShort     n, m, count;
1158   HB_UInt      cur_offset, new_offset, base_offset;
1159
1160   HB_PairSet*  ps;
1161
1162
1163   base_offset = FILE_Pos() - 8L;
1164
1165   if ( ACCESS_Frame( 2L ) )
1166     return error;
1167
1168   count = ppf1->PairSetCount = GET_UShort();
1169
1170   FORGET_Frame();
1171
1172   ppf1->PairSet = NULL;
1173
1174   if ( ALLOC_ARRAY( ppf1->PairSet, count, HB_PairSet ) )
1175     return error;
1176
1177   ps = ppf1->PairSet;
1178
1179   for ( n = 0; n < count; n++ )
1180   {
1181     if ( ACCESS_Frame( 2L ) )
1182       goto Fail;
1183
1184     new_offset = GET_UShort() + base_offset;
1185
1186     FORGET_Frame();
1187
1188     cur_offset = FILE_Pos();
1189     if ( FILE_Seek( new_offset ) ||
1190          ( error = Load_PairSet( &ps[n], format1,
1191                                  format2, stream ) ) != HB_Err_Ok )
1192       goto Fail;
1193     (void)FILE_Seek( cur_offset );
1194   }
1195
1196   return HB_Err_Ok;
1197
1198 Fail:
1199   for ( m = 0; m < n; m++ )
1200     Free_PairSet( &ps[m], format1, format2 );
1201
1202   FREE( ps );
1203   return error;
1204 }
1205
1206
1207 static void  Free_PairPos1( HB_PairPosFormat1*  ppf1,
1208                             HB_UShort            format1,
1209                             HB_UShort            format2 )
1210 {
1211   HB_UShort     n, count;
1212
1213   HB_PairSet*  ps;
1214
1215
1216   if ( ppf1->PairSet )
1217   {
1218     count = ppf1->PairSetCount;
1219     ps    = ppf1->PairSet;
1220
1221     for ( n = 0; n < count; n++ )
1222       Free_PairSet( &ps[n], format1, format2 );
1223
1224     FREE( ps );
1225   }
1226 }
1227
1228
1229 /* PairPosFormat2 */
1230
1231 static HB_Error  Load_PairPos2( HB_PairPosFormat2*  ppf2,
1232                                 HB_UShort            format1,
1233                                 HB_UShort            format2,
1234                                 HB_Stream            stream )
1235 {
1236   HB_Error  error;
1237
1238   HB_UShort          m, n, k, count1, count2;
1239   HB_UInt           cur_offset, new_offset1, new_offset2, base_offset;
1240
1241   HB_Class1Record*  c1r;
1242   HB_Class2Record*  c2r;
1243
1244
1245   base_offset = FILE_Pos() - 8L;
1246
1247   if ( ACCESS_Frame( 8L ) )
1248     return error;
1249
1250   new_offset1 = GET_UShort() + base_offset;
1251   new_offset2 = GET_UShort() + base_offset;
1252
1253   /* `Class1Count' and `Class2Count' are the upper limits for class
1254      values, thus we read it now to make additional safety checks.  */
1255
1256   count1 = ppf2->Class1Count = GET_UShort();
1257   count2 = ppf2->Class2Count = GET_UShort();
1258
1259   FORGET_Frame();
1260
1261   cur_offset = FILE_Pos();
1262   if ( FILE_Seek( new_offset1 ) ||
1263        ( error = _HB_OPEN_Load_ClassDefinition( &ppf2->ClassDef1, count1,
1264                                        stream ) ) != HB_Err_Ok )
1265     return error;
1266   if ( FILE_Seek( new_offset2 ) ||
1267        ( error = _HB_OPEN_Load_ClassDefinition( &ppf2->ClassDef2, count2,
1268                                        stream ) ) != HB_Err_Ok )
1269     goto Fail3;
1270   (void)FILE_Seek( cur_offset );
1271
1272   ppf2->Class1Record = NULL;
1273
1274   if ( ALLOC_ARRAY( ppf2->Class1Record, count1, HB_Class1Record ) )
1275     goto Fail2;
1276
1277   c1r = ppf2->Class1Record;
1278
1279   for ( m = 0; m < count1; m++ )
1280   {
1281     c1r[m].Class2Record = NULL;
1282
1283     if ( ALLOC_ARRAY( c1r[m].Class2Record, count2, HB_Class2Record ) )
1284       goto Fail1;
1285
1286     c2r = c1r[m].Class2Record;
1287
1288     for ( n = 0; n < count2; n++ )
1289     {
1290       if ( format1 )
1291       {
1292         error = Load_ValueRecord( &c2r[n].Value1, format1,
1293                                   base_offset, stream );
1294         if ( error )
1295           goto Fail0;
1296       }
1297       if ( format2 )
1298       {
1299         error = Load_ValueRecord( &c2r[n].Value2, format2,
1300                                   base_offset, stream );
1301         if ( error )
1302         {
1303           if ( format1 )
1304             Free_ValueRecord( &c2r[n].Value1, format1 );
1305           goto Fail0;
1306         }
1307       }
1308     }
1309
1310     continue;
1311
1312   Fail0:
1313     for ( k = 0; k < n; k++ )
1314     {
1315       if ( format1 )
1316         Free_ValueRecord( &c2r[k].Value1, format1 );
1317       if ( format2 )
1318         Free_ValueRecord( &c2r[k].Value2, format2 );
1319     }
1320     goto Fail1;
1321   }
1322
1323   return HB_Err_Ok;
1324
1325 Fail1:
1326   for ( k = 0; k < m; k++ )
1327   {
1328     c2r = c1r[k].Class2Record;
1329
1330     for ( n = 0; n < count2; n++ )
1331     {
1332       if ( format1 )
1333         Free_ValueRecord( &c2r[n].Value1, format1 );
1334       if ( format2 )
1335         Free_ValueRecord( &c2r[n].Value2, format2 );
1336     }
1337
1338     FREE( c2r );
1339   }
1340
1341   FREE( c1r );
1342 Fail2:
1343
1344   _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef2 );
1345
1346 Fail3:
1347   _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef1 );
1348   return error;
1349 }
1350
1351
1352 static void  Free_PairPos2( HB_PairPosFormat2*  ppf2,
1353                             HB_UShort            format1,
1354                             HB_UShort            format2 )
1355 {
1356   HB_UShort          m, n, count1, count2;
1357
1358   HB_Class1Record*  c1r;
1359   HB_Class2Record*  c2r;
1360
1361
1362   if ( ppf2->Class1Record )
1363   {
1364     c1r    = ppf2->Class1Record;
1365     count1 = ppf2->Class1Count;
1366     count2 = ppf2->Class2Count;
1367
1368     for ( m = 0; m < count1; m++ )
1369     {
1370       c2r = c1r[m].Class2Record;
1371
1372       for ( n = 0; n < count2; n++ )
1373       {
1374         if ( format1 )
1375           Free_ValueRecord( &c2r[n].Value1, format1 );
1376         if ( format2 )
1377           Free_ValueRecord( &c2r[n].Value2, format2 );
1378       }
1379
1380       FREE( c2r );
1381     }
1382
1383     FREE( c1r );
1384
1385     _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef2 );
1386     _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef1 );
1387   }
1388 }
1389
1390
1391 static HB_Error  Load_PairPos( HB_GPOS_SubTable* st,
1392                                HB_Stream     stream )
1393 {
1394   HB_Error  error;
1395   HB_PairPos*     pp = &st->pair;
1396
1397   HB_UShort         format1, format2;
1398   HB_UInt          cur_offset, new_offset, base_offset;
1399
1400
1401   base_offset = FILE_Pos();
1402
1403   if ( ACCESS_Frame( 8L ) )
1404     return error;
1405
1406   pp->PosFormat = GET_UShort();
1407   new_offset    = GET_UShort() + base_offset;
1408
1409   format1 = pp->ValueFormat1 = GET_UShort();
1410   format2 = pp->ValueFormat2 = GET_UShort();
1411
1412   FORGET_Frame();
1413
1414   cur_offset = FILE_Pos();
1415   if ( FILE_Seek( new_offset ) ||
1416        ( error = _HB_OPEN_Load_Coverage( &pp->Coverage, stream ) ) != HB_Err_Ok )
1417     return error;
1418   (void)FILE_Seek( cur_offset );
1419
1420   switch ( pp->PosFormat )
1421   {
1422   case 1:
1423     error = Load_PairPos1( &pp->ppf.ppf1, format1, format2, stream );
1424     if ( error )
1425       goto Fail;
1426     break;
1427
1428   case 2:
1429     error = Load_PairPos2( &pp->ppf.ppf2, format1, format2, stream );
1430     if ( error )
1431       goto Fail;
1432     break;
1433
1434   default:
1435     return ERR(HB_Err_Invalid_SubTable_Format);
1436   }
1437
1438   return HB_Err_Ok;
1439
1440 Fail:
1441   _HB_OPEN_Free_Coverage( &pp->Coverage );
1442   return error;
1443 }
1444
1445
1446 static void  Free_PairPos( HB_GPOS_SubTable* st )
1447 {
1448   HB_UShort  format1, format2;
1449   HB_PairPos*     pp = &st->pair;
1450
1451
1452   format1 = pp->ValueFormat1;
1453   format2 = pp->ValueFormat2;
1454
1455   switch ( pp->PosFormat )
1456   {
1457   case 1:
1458     Free_PairPos1( &pp->ppf.ppf1, format1, format2 );
1459     break;
1460
1461   case 2:
1462     Free_PairPos2( &pp->ppf.ppf2, format1, format2 );
1463     break;
1464
1465   default:
1466     break;
1467   }
1468
1469   _HB_OPEN_Free_Coverage( &pp->Coverage );
1470 }
1471
1472
1473 static HB_Error  Lookup_PairPos1( GPOS_Instance*       gpi,
1474                                   HB_PairPosFormat1*  ppf1,
1475                                   HB_Buffer           buffer,
1476                                   HB_UInt             first_pos,
1477                                   HB_UShort            index,
1478                                   HB_UShort            format1,
1479                                   HB_UShort            format2 )
1480 {
1481   HB_Error              error;
1482   HB_UShort             numpvr, glyph2;
1483
1484   HB_PairValueRecord*  pvr;
1485
1486
1487   if ( index >= ppf1->PairSetCount )
1488      return ERR(HB_Err_Invalid_SubTable);
1489
1490   pvr = ppf1->PairSet[index].PairValueRecord;
1491   if ( !pvr )
1492     return ERR(HB_Err_Invalid_SubTable);
1493
1494   glyph2 = IN_CURGLYPH();
1495
1496   for ( numpvr = ppf1->PairSet[index].PairValueCount;
1497         numpvr;
1498         numpvr--, pvr++ )
1499   {
1500     if ( glyph2 == pvr->SecondGlyph )
1501     {
1502       error = Get_ValueRecord( gpi, &pvr->Value1, format1,
1503                                POSITION( first_pos ) );
1504       if ( error )
1505         return error;
1506       return Get_ValueRecord( gpi, &pvr->Value2, format2,
1507                               POSITION( buffer->in_pos ) );
1508     }
1509   }
1510
1511   return HB_Err_Not_Covered;
1512 }
1513
1514
1515 static HB_Error  Lookup_PairPos2( GPOS_Instance*       gpi,
1516                                   HB_PairPosFormat2*  ppf2,
1517                                   HB_Buffer           buffer,
1518                                   HB_UInt             first_pos,
1519                                   HB_UShort            format1,
1520                                   HB_UShort            format2 )
1521 {
1522   HB_Error           error;
1523   HB_UShort          cl1 = 0, cl2 = 0; /* shut compiler up */
1524
1525   HB_Class1Record*  c1r;
1526   HB_Class2Record*  c2r;
1527
1528
1529   error = _HB_OPEN_Get_Class( &ppf2->ClassDef1, IN_GLYPH( first_pos ),
1530                      &cl1, NULL );
1531   if ( error && error != HB_Err_Not_Covered )
1532     return error;
1533   error = _HB_OPEN_Get_Class( &ppf2->ClassDef2, IN_CURGLYPH(),
1534                      &cl2, NULL );
1535   if ( error && error != HB_Err_Not_Covered )
1536     return error;
1537
1538   c1r = &ppf2->Class1Record[cl1];
1539   if ( !c1r )
1540     return ERR(HB_Err_Invalid_SubTable);
1541   c2r = &c1r->Class2Record[cl2];
1542
1543   error = Get_ValueRecord( gpi, &c2r->Value1, format1, POSITION( first_pos ) );
1544   if ( error )
1545     return error;
1546   return Get_ValueRecord( gpi, &c2r->Value2, format2, POSITION( buffer->in_pos ) );
1547 }
1548
1549
1550 static HB_Error  Lookup_PairPos( GPOS_Instance*    gpi,
1551                                  HB_GPOS_SubTable* st,
1552                                  HB_Buffer        buffer,
1553                                  HB_UShort         flags,
1554                                  HB_UShort         context_length,
1555                                  int               nesting_level )
1556 {
1557   HB_Error         error;
1558   HB_UShort        index, property;
1559   HB_UInt         first_pos;
1560   HB_GPOSHeader*  gpos = gpi->gpos;
1561   HB_PairPos*     pp = &st->pair;
1562
1563   HB_UNUSED(nesting_level);
1564
1565   if ( buffer->in_pos >= buffer->in_length - 1 )
1566     return HB_Err_Not_Covered;           /* Not enough glyphs in stream */
1567
1568   if ( context_length != 0xFFFF && context_length < 2 )
1569     return HB_Err_Not_Covered;
1570
1571   if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
1572     return error;
1573
1574   error = _HB_OPEN_Coverage_Index( &pp->Coverage, IN_CURGLYPH(), &index );
1575   if ( error )
1576     return error;
1577
1578   /* second glyph */
1579
1580   first_pos = buffer->in_pos;
1581   (buffer->in_pos)++;
1582
1583   while ( CHECK_Property( gpos->gdef, IN_CURITEM(),
1584                           flags, &property ) )
1585   {
1586     if ( error && error != HB_Err_Not_Covered )
1587       return error;
1588
1589     if ( buffer->in_pos == buffer->in_length )
1590       {
1591         buffer->in_pos = first_pos;
1592         return HB_Err_Not_Covered;
1593       }
1594     (buffer->in_pos)++;
1595
1596   }
1597
1598   switch ( pp->PosFormat )
1599   {
1600   case 1:
1601     error = Lookup_PairPos1( gpi, &pp->ppf.ppf1, buffer,
1602                              first_pos, index,
1603                              pp->ValueFormat1, pp->ValueFormat2 );
1604     break;
1605
1606   case 2:
1607     error = Lookup_PairPos2( gpi, &pp->ppf.ppf2, buffer, first_pos,
1608                              pp->ValueFormat1, pp->ValueFormat2 );
1609     break;
1610
1611   default:
1612     return ERR(HB_Err_Invalid_SubTable_Format);
1613   }
1614
1615   /* if we don't have coverage for the second glyph don't skip it for
1616      further lookups but reset in_pos back to the first_glyph and let
1617      the caller in Do_String_Lookup increment in_pos */
1618   if ( error == HB_Err_Not_Covered )
1619       buffer->in_pos = first_pos;
1620
1621   /* adjusting the `next' glyph */
1622
1623   if ( pp->ValueFormat2 )
1624     (buffer->in_pos)++;
1625
1626   return error;
1627 }
1628
1629
1630 /* LookupType 3 */
1631
1632 /* CursivePosFormat1 */
1633
1634 static HB_Error  Load_CursivePos( HB_GPOS_SubTable* st,
1635                                   HB_Stream        stream )
1636 {
1637   HB_Error  error;
1638   HB_CursivePos*  cp = &st->cursive;
1639
1640   HB_UShort             n, m, count;
1641   HB_UInt              cur_offset, new_offset, base_offset;
1642
1643   HB_EntryExitRecord*  eer;
1644
1645
1646   base_offset = FILE_Pos();
1647
1648   if ( ACCESS_Frame( 4L ) )
1649     return error;
1650
1651   cp->PosFormat = GET_UShort();
1652   new_offset    = GET_UShort() + base_offset;
1653
1654   FORGET_Frame();
1655
1656   cur_offset = FILE_Pos();
1657   if ( FILE_Seek( new_offset ) ||
1658        ( error = _HB_OPEN_Load_Coverage( &cp->Coverage, stream ) ) != HB_Err_Ok )
1659     return error;
1660   (void)FILE_Seek( cur_offset );
1661
1662   if ( ACCESS_Frame( 2L ) )
1663     goto Fail2;
1664
1665   count = cp->EntryExitCount = GET_UShort();
1666
1667   FORGET_Frame();
1668
1669   cp->EntryExitRecord = NULL;
1670
1671   if ( ALLOC_ARRAY( cp->EntryExitRecord, count, HB_EntryExitRecord ) )
1672     goto Fail2;
1673
1674   eer = cp->EntryExitRecord;
1675
1676   for ( n = 0; n < count; n++ )
1677   {
1678     HB_UInt entry_offset;
1679
1680     if ( ACCESS_Frame( 2L ) )
1681       return error;
1682
1683     entry_offset = new_offset = GET_UShort();
1684
1685     FORGET_Frame();
1686
1687     if ( new_offset )
1688     {
1689       new_offset += base_offset;
1690
1691       cur_offset = FILE_Pos();
1692       if ( FILE_Seek( new_offset ) ||
1693            ( error = Load_Anchor( &eer[n].EntryAnchor,
1694                                   stream ) ) != HB_Err_Ok )
1695         goto Fail1;
1696       (void)FILE_Seek( cur_offset );
1697     }
1698     else
1699       eer[n].EntryAnchor.PosFormat   = 0;
1700
1701     if ( ACCESS_Frame( 2L ) )
1702       return error;
1703
1704     new_offset = GET_UShort();
1705
1706     FORGET_Frame();
1707
1708     if ( new_offset )
1709     {
1710       new_offset += base_offset;
1711
1712       cur_offset = FILE_Pos();
1713       if ( FILE_Seek( new_offset ) ||
1714            ( error = Load_Anchor( &eer[n].ExitAnchor,
1715                                   stream ) ) != HB_Err_Ok )
1716       {
1717         if ( entry_offset )
1718           Free_Anchor( &eer[n].EntryAnchor );
1719         goto Fail1;
1720       }
1721       (void)FILE_Seek( cur_offset );
1722     }
1723     else
1724       eer[n].ExitAnchor.PosFormat   = 0;
1725   }
1726
1727   return HB_Err_Ok;
1728
1729 Fail1:
1730   for ( m = 0; m < n; m++ )
1731   {
1732     Free_Anchor( &eer[m].EntryAnchor );
1733     Free_Anchor( &eer[m].ExitAnchor );
1734   }
1735
1736   FREE( eer );
1737
1738 Fail2:
1739   _HB_OPEN_Free_Coverage( &cp->Coverage );
1740   return error;
1741 }
1742
1743
1744 static void  Free_CursivePos( HB_GPOS_SubTable* st )
1745 {
1746   HB_UShort             n, count;
1747   HB_CursivePos*  cp = &st->cursive;
1748
1749   HB_EntryExitRecord*  eer;
1750
1751
1752   if ( cp->EntryExitRecord )
1753   {
1754     count = cp->EntryExitCount;
1755     eer   = cp->EntryExitRecord;
1756
1757     for ( n = 0; n < count; n++ )
1758     {
1759       Free_Anchor( &eer[n].EntryAnchor );
1760       Free_Anchor( &eer[n].ExitAnchor );
1761     }
1762
1763     FREE( eer );
1764   }
1765
1766   _HB_OPEN_Free_Coverage( &cp->Coverage );
1767 }
1768
1769
1770 static HB_Error  Lookup_CursivePos( GPOS_Instance*    gpi,
1771                                     HB_GPOS_SubTable* st,
1772                                     HB_Buffer        buffer,
1773                                     HB_UShort         flags,
1774                                     HB_UShort         context_length,
1775                                     int               nesting_level )
1776 {
1777   HB_UShort        index, property;
1778   HB_Error         error;
1779   HB_GPOSHeader*  gpos = gpi->gpos;
1780   HB_CursivePos*  cp = &st->cursive;
1781
1782   HB_EntryExitRecord*  eer;
1783   HB_Fixed                entry_x, entry_y;
1784   HB_Fixed                exit_x, exit_y;
1785
1786   HB_UNUSED(nesting_level);
1787
1788   if ( context_length != 0xFFFF && context_length < 1 )
1789   {
1790     gpi->last = 0xFFFF;
1791     return HB_Err_Not_Covered;
1792   }
1793
1794   /* Glyphs not having the right GDEF properties will be ignored, i.e.,
1795      gpi->last won't be reset (contrary to user defined properties). */
1796
1797   if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
1798     return error;
1799
1800   /* We don't handle mark glyphs here.  According to Andrei, this isn't
1801      possible, but who knows...                                         */
1802
1803   if ( property == HB_GDEF_MARK )
1804   {
1805     gpi->last = 0xFFFF;
1806     return HB_Err_Not_Covered;
1807   }
1808
1809   error = _HB_OPEN_Coverage_Index( &cp->Coverage, IN_CURGLYPH(), &index );
1810   if ( error )
1811   {
1812     gpi->last = 0xFFFF;
1813     return error;
1814   }
1815
1816   if ( index >= cp->EntryExitCount )
1817     return ERR(HB_Err_Invalid_SubTable);
1818
1819   eer = &cp->EntryExitRecord[index];
1820
1821   /* Now comes the messiest part of the whole OpenType
1822      specification.  At first glance, cursive connections seem easy
1823      to understand, but there are pitfalls!  The reason is that
1824      the specs don't mention how to compute the advance values
1825      resp. glyph offsets.  I was told it would be an omission, to
1826      be fixed in the next OpenType version...  Again many thanks to
1827      Andrei Burago <andreib@microsoft.com> for clarifications.
1828
1829      Consider the following example:
1830
1831                       |  xadv1    |
1832                        +---------+
1833                        |         |
1834                  +-----+--+ 1    |
1835                  |     | .|      |
1836                  |    0+--+------+
1837                  |   2    |
1838                  |        |
1839                 0+--------+
1840                 |  xadv2   |
1841
1842        glyph1: advance width = 12
1843                anchor point = (3,1)
1844
1845        glyph2: advance width = 11
1846                anchor point = (9,4)
1847
1848        LSB is 1 for both glyphs (so the boxes drawn above are glyph
1849        bboxes).  Writing direction is R2L; `0' denotes the glyph's
1850        coordinate origin.
1851
1852      Now the surprising part: The advance width of the *left* glyph
1853      (resp. of the *bottom* glyph) will be modified, no matter
1854      whether the writing direction is L2R or R2L (resp. T2B or
1855      B2T)!  This assymetry is caused by the fact that the glyph's
1856      coordinate origin is always the lower left corner for all
1857      writing directions.
1858
1859      Continuing the above example, we can compute the new
1860      (horizontal) advance width of glyph2 as
1861
1862        9 - 3 = 6  ,
1863
1864      and the new vertical offset of glyph2 as
1865
1866        1 - 4 = -3  .
1867
1868
1869      Vertical writing direction is far more complicated:
1870
1871      a) Assuming that we recompute the advance height of the lower glyph:
1872
1873                                   --
1874                        +---------+
1875               --       |         |
1876                  +-----+--+ 1    | yadv1
1877                  |     | .|      |
1878            yadv2 |    0+--+------+        -- BSB1  --
1879                  |   2    |       --      --        y_offset
1880                  |        |
1881    BSB2 --      0+--------+                        --
1882         --    --
1883
1884        glyph1: advance height = 6
1885                anchor point = (3,1)
1886
1887        glyph2: advance height = 7
1888                anchor point = (9,4)
1889
1890        TSB is 1 for both glyphs; writing direction is T2B.
1891
1892
1893          BSB1     = yadv1 - (TSB1 + ymax1)
1894          BSB2     = yadv2 - (TSB2 + ymax2)
1895          y_offset = y2 - y1
1896
1897        vertical advance width of glyph2
1898          = y_offset + BSB2 - BSB1
1899          = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
1900          = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
1901          = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
1902
1903
1904      b) Assuming that we recompute the advance height of the upper glyph:
1905
1906                                   --      --
1907                        +---------+        -- TSB1
1908         --    --       |         |
1909    TSB2 --       +-----+--+ 1    | yadv1   ymax1
1910                  |     | .|      |
1911            yadv2 |    0+--+------+        --       --
1912     ymax2        |   2    |       --                y_offset
1913                  |        |
1914         --      0+--------+                        --
1915               --
1916
1917        glyph1: advance height = 6
1918                anchor point = (3,1)
1919
1920        glyph2: advance height = 7
1921                anchor point = (9,4)
1922
1923        TSB is 1 for both glyphs; writing direction is T2B.
1924
1925        y_offset = y2 - y1
1926
1927        vertical advance width of glyph2
1928          = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
1929          = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
1930
1931
1932      Comparing a) with b) shows that b) is easier to compute.  I'll wait
1933      for a reply from Andrei to see what should really be implemented...
1934
1935      Since horizontal advance widths or vertical advance heights
1936      can be used alone but not together, no ambiguity occurs.        */
1937
1938   if ( gpi->last == 0xFFFF )
1939     goto end;
1940
1941   /* Get_Anchor() returns HB_Err_Not_Covered if there is no anchor
1942      table.                                                         */
1943
1944   error = Get_Anchor( gpi, &eer->EntryAnchor, IN_CURGLYPH(),
1945                       &entry_x, &entry_y );
1946   if ( error == HB_Err_Not_Covered )
1947     goto end;
1948   if ( error )
1949     return error;
1950
1951   if ( gpi->r2l )
1952   {
1953     POSITION( buffer->in_pos )->x_advance   = entry_x - gpi->anchor_x;
1954     POSITION( buffer->in_pos )->new_advance = TRUE;
1955   }
1956   else
1957   {
1958     POSITION( gpi->last )->x_advance   = gpi->anchor_x - entry_x;
1959     POSITION( gpi->last )->new_advance = TRUE;
1960   }
1961
1962   if ( flags & HB_LOOKUP_FLAG_RIGHT_TO_LEFT )
1963   {
1964     POSITION( gpi->last )->cursive_chain = gpi->last - buffer->in_pos;
1965     POSITION( gpi->last )->y_pos = entry_y - gpi->anchor_y;
1966   }
1967   else
1968   {
1969     POSITION( buffer->in_pos )->cursive_chain = buffer->in_pos - gpi->last;
1970     POSITION( buffer->in_pos )->y_pos = gpi->anchor_y - entry_y;
1971   }
1972
1973 end:
1974   error = Get_Anchor( gpi, &eer->ExitAnchor, IN_CURGLYPH(),
1975                       &exit_x, &exit_y );
1976   if ( error == HB_Err_Not_Covered )
1977     gpi->last = 0xFFFF;
1978   else
1979   {
1980     gpi->last     = buffer->in_pos;
1981     gpi->anchor_x = exit_x;
1982     gpi->anchor_y = exit_y;
1983   }
1984   if ( error )
1985     return error;
1986
1987   (buffer->in_pos)++;
1988
1989   return HB_Err_Ok;
1990 }
1991
1992
1993 /* LookupType 4 */
1994
1995 /* BaseArray */
1996
1997 static HB_Error  Load_BaseArray( HB_BaseArray*  ba,
1998                                  HB_UShort       num_classes,
1999                                  HB_Stream       stream )
2000 {
2001   HB_Error  error;
2002
2003   HB_UShort        m, n, k, count;
2004   HB_UInt         cur_offset, new_offset, base_offset;
2005
2006   HB_BaseRecord*  br;
2007   HB_Anchor*      ban;
2008
2009
2010   base_offset = FILE_Pos();
2011
2012   if ( ACCESS_Frame( 2L ) )
2013     return error;
2014
2015   count = ba->BaseCount = GET_UShort();
2016
2017   FORGET_Frame();
2018
2019   ba->BaseRecord = NULL;
2020
2021   if ( ALLOC_ARRAY( ba->BaseRecord, count, HB_BaseRecord ) )
2022     return error;
2023
2024   br = ba->BaseRecord;
2025
2026   for ( m = 0; m < count; m++ )
2027   {
2028     br[m].BaseAnchor = NULL;
2029
2030     if ( ALLOC_ARRAY( br[m].BaseAnchor, num_classes, HB_Anchor ) )
2031       goto Fail;
2032
2033     ban = br[m].BaseAnchor;
2034
2035     for ( n = 0; n < num_classes; n++ )
2036     {
2037       if ( ACCESS_Frame( 2L ) )
2038         goto Fail0;
2039
2040       new_offset = GET_UShort() + base_offset;
2041
2042       FORGET_Frame();
2043
2044       if (new_offset == base_offset) {
2045         /* XXX
2046          * Doulos SIL Regular is buggy and has zero offsets here.
2047          * Skip it
2048          */
2049         ban[n].PosFormat = 0;
2050         continue;
2051       }
2052
2053       cur_offset = FILE_Pos();
2054       if ( FILE_Seek( new_offset ) ||
2055            ( error = Load_Anchor( &ban[n], stream ) ) != HB_Err_Ok )
2056         goto Fail0;
2057       (void)FILE_Seek( cur_offset );
2058     }
2059
2060     continue;
2061   Fail0:
2062     for ( k = 0; k < n; k++ )
2063       Free_Anchor( &ban[k] );
2064     goto Fail;
2065   }
2066
2067   return HB_Err_Ok;
2068
2069 Fail:
2070   for ( k = 0; k < m; k++ )
2071   {
2072     ban = br[k].BaseAnchor;
2073
2074     for ( n = 0; n < num_classes; n++ )
2075       Free_Anchor( &ban[n] );
2076
2077     FREE( ban );
2078   }
2079
2080   FREE( br );
2081   return error;
2082 }
2083
2084
2085 static void  Free_BaseArray( HB_BaseArray*  ba,
2086                              HB_UShort       num_classes )
2087 {
2088   HB_UShort        m, n, count;
2089
2090   HB_BaseRecord*  br;
2091   HB_Anchor*      ban;
2092
2093
2094   if ( ba->BaseRecord )
2095   {
2096     count = ba->BaseCount;
2097     br    = ba->BaseRecord;
2098
2099     for ( m = 0; m < count; m++ )
2100     {
2101       ban = br[m].BaseAnchor;
2102
2103       for ( n = 0; n < num_classes; n++ )
2104         Free_Anchor( &ban[n] );
2105
2106       FREE( ban );
2107     }
2108
2109     FREE( br );
2110   }
2111 }
2112
2113
2114 /* MarkBasePosFormat1 */
2115
2116 static HB_Error  Load_MarkBasePos( HB_GPOS_SubTable* st,
2117                                    HB_Stream         stream )
2118 {
2119   HB_Error  error;
2120   HB_MarkBasePos* mbp = &st->markbase;
2121
2122   HB_UInt  cur_offset, new_offset, base_offset;
2123
2124
2125   base_offset = FILE_Pos();
2126
2127   if ( ACCESS_Frame( 4L ) )
2128     return error;
2129
2130   mbp->PosFormat = GET_UShort();
2131   new_offset     = GET_UShort() + base_offset;
2132
2133   FORGET_Frame();
2134
2135   if (mbp->PosFormat != 1)
2136     return ERR(HB_Err_Invalid_SubTable_Format);
2137
2138   cur_offset = FILE_Pos();
2139   if ( FILE_Seek( new_offset ) ||
2140        ( error = _HB_OPEN_Load_Coverage( &mbp->MarkCoverage, stream ) ) != HB_Err_Ok )
2141     return error;
2142   (void)FILE_Seek( cur_offset );
2143
2144   if ( ACCESS_Frame( 2L ) )
2145     goto Fail3;
2146
2147   new_offset = GET_UShort() + base_offset;
2148
2149   FORGET_Frame();
2150
2151   cur_offset = FILE_Pos();
2152   if ( FILE_Seek( new_offset ) ||
2153        ( error = _HB_OPEN_Load_Coverage( &mbp->BaseCoverage, stream ) ) != HB_Err_Ok )
2154     goto Fail3;
2155   (void)FILE_Seek( cur_offset );
2156
2157   if ( ACCESS_Frame( 4L ) )
2158     goto Fail2;
2159
2160   mbp->ClassCount = GET_UShort();
2161   new_offset      = GET_UShort() + base_offset;
2162
2163   FORGET_Frame();
2164
2165   cur_offset = FILE_Pos();
2166   if ( FILE_Seek( new_offset ) ||
2167        ( error = Load_MarkArray( &mbp->MarkArray, stream ) ) != HB_Err_Ok )
2168     goto Fail2;
2169   (void)FILE_Seek( cur_offset );
2170
2171   if ( ACCESS_Frame( 2L ) )
2172     goto Fail1;
2173
2174   new_offset = GET_UShort() + base_offset;
2175
2176   FORGET_Frame();
2177
2178   cur_offset = FILE_Pos();
2179   if ( FILE_Seek( new_offset ) ||
2180        ( error = Load_BaseArray( &mbp->BaseArray, mbp->ClassCount,
2181                                  stream ) ) != HB_Err_Ok )
2182     goto Fail1;
2183
2184   return HB_Err_Ok;
2185
2186 Fail1:
2187   Free_MarkArray( &mbp->MarkArray );
2188
2189 Fail2:
2190   _HB_OPEN_Free_Coverage( &mbp->BaseCoverage );
2191
2192 Fail3:
2193   _HB_OPEN_Free_Coverage( &mbp->MarkCoverage );
2194   return error;
2195 }
2196
2197
2198 static void  Free_MarkBasePos( HB_GPOS_SubTable* st )
2199 {
2200   HB_MarkBasePos* mbp = &st->markbase;
2201
2202   Free_BaseArray( &mbp->BaseArray, mbp->ClassCount );
2203   Free_MarkArray( &mbp->MarkArray );
2204   _HB_OPEN_Free_Coverage( &mbp->BaseCoverage );
2205   _HB_OPEN_Free_Coverage( &mbp->MarkCoverage );
2206 }
2207
2208
2209 static HB_Error  Lookup_MarkBasePos( GPOS_Instance*    gpi,
2210                                      HB_GPOS_SubTable* st,
2211                                      HB_Buffer        buffer,
2212                                      HB_UShort         flags,
2213                                      HB_UShort         context_length,
2214                                      int               nesting_level )
2215 {
2216   HB_UShort        i, j, mark_index, base_index, property, class;
2217   HB_Fixed           x_mark_value, y_mark_value, x_base_value, y_base_value;
2218   HB_Error         error;
2219   HB_GPOSHeader*  gpos = gpi->gpos;
2220   HB_MarkBasePos* mbp = &st->markbase;
2221
2222   HB_MarkArray*   ma;
2223   HB_BaseArray*   ba;
2224   HB_BaseRecord*  br;
2225   HB_Anchor*      mark_anchor;
2226   HB_Anchor*      base_anchor;
2227
2228   HB_Position     o;
2229
2230   HB_UNUSED(nesting_level);
2231
2232   if ( context_length != 0xFFFF && context_length < 1 )
2233     return HB_Err_Not_Covered;
2234
2235   if ( flags & HB_LOOKUP_FLAG_IGNORE_BASE_GLYPHS )
2236     return HB_Err_Not_Covered;
2237
2238   if ( CHECK_Property( gpos->gdef, IN_CURITEM(),
2239                        flags, &property ) )
2240     return error;
2241
2242   error = _HB_OPEN_Coverage_Index( &mbp->MarkCoverage, IN_CURGLYPH(),
2243                           &mark_index );
2244   if ( error )
2245     return error;
2246
2247   /* now we search backwards for a non-mark glyph */
2248
2249   i = 1;
2250   j = buffer->in_pos - 1;
2251
2252   while ( i <= buffer->in_pos )
2253   {
2254     error = HB_GDEF_Get_Glyph_Property( gpos->gdef, IN_GLYPH( j ),
2255                                         &property );
2256     if ( error )
2257       return error;
2258
2259     if ( !( property == HB_GDEF_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
2260       break;
2261
2262     i++;
2263     j--;
2264   }
2265
2266   /* The following assertion is too strong -- at least for mangal.ttf. */
2267 #if 0
2268   if ( property != HB_GDEF_BASE_GLYPH )
2269     return HB_Err_Not_Covered;
2270 #endif
2271
2272   if ( i > buffer->in_pos )
2273     return HB_Err_Not_Covered;
2274
2275   error = _HB_OPEN_Coverage_Index( &mbp->BaseCoverage, IN_GLYPH( j ),
2276                           &base_index );
2277   if ( error )
2278     return error;
2279
2280   ma = &mbp->MarkArray;
2281
2282   if ( mark_index >= ma->MarkCount )
2283     return ERR(HB_Err_Invalid_SubTable);
2284
2285   class       = ma->MarkRecord[mark_index].Class;
2286   mark_anchor = &ma->MarkRecord[mark_index].MarkAnchor;
2287
2288   if ( class >= mbp->ClassCount )
2289     return ERR(HB_Err_Invalid_SubTable);
2290
2291   ba = &mbp->BaseArray;
2292
2293   if ( base_index >= ba->BaseCount )
2294     return ERR(HB_Err_Invalid_SubTable);
2295
2296   br          = &ba->BaseRecord[base_index];
2297   base_anchor = &br->BaseAnchor[class];
2298
2299   error = Get_Anchor( gpi, mark_anchor, IN_CURGLYPH(),
2300                       &x_mark_value, &y_mark_value );
2301   if ( error )
2302     return error;
2303
2304   error = Get_Anchor( gpi, base_anchor, IN_GLYPH( j ),
2305                       &x_base_value, &y_base_value );
2306   if ( error )
2307     return error;
2308
2309   /* anchor points are not cumulative */
2310
2311   o = POSITION( buffer->in_pos );
2312
2313   o->x_pos     = x_base_value - x_mark_value;
2314   o->y_pos     = y_base_value - y_mark_value;
2315   o->x_advance = 0;
2316   o->y_advance = 0;
2317   o->back      = i;
2318
2319   (buffer->in_pos)++;
2320
2321   return HB_Err_Ok;
2322 }
2323
2324
2325 /* LookupType 5 */
2326
2327 /* LigatureAttach */
2328
2329 static HB_Error  Load_LigatureAttach( HB_LigatureAttach*  lat,
2330                                       HB_UShort            num_classes,
2331                                       HB_Stream            stream )
2332 {
2333   HB_Error  error;
2334
2335   HB_UShort             m, n, k, count;
2336   HB_UInt              cur_offset, new_offset, base_offset;
2337
2338   HB_ComponentRecord*  cr;
2339   HB_Anchor*           lan;
2340
2341
2342   base_offset = FILE_Pos();
2343
2344   if ( ACCESS_Frame( 2L ) )
2345     return error;
2346
2347   count = lat->ComponentCount = GET_UShort();
2348
2349   FORGET_Frame();
2350
2351   lat->ComponentRecord = NULL;
2352
2353   if ( ALLOC_ARRAY( lat->ComponentRecord, count, HB_ComponentRecord ) )
2354     return error;
2355
2356   cr = lat->ComponentRecord;
2357
2358   for ( m = 0; m < count; m++ )
2359   {
2360     cr[m].LigatureAnchor = NULL;
2361
2362     if ( ALLOC_ARRAY( cr[m].LigatureAnchor, num_classes, HB_Anchor ) )
2363       goto Fail;
2364
2365     lan = cr[m].LigatureAnchor;
2366
2367     for ( n = 0; n < num_classes; n++ )
2368     {
2369       if ( ACCESS_Frame( 2L ) )
2370         goto Fail0;
2371
2372       new_offset = GET_UShort();
2373
2374       FORGET_Frame();
2375
2376       if ( new_offset )
2377       {
2378         new_offset += base_offset;
2379
2380         cur_offset = FILE_Pos();
2381         if ( FILE_Seek( new_offset ) ||
2382              ( error = Load_Anchor( &lan[n], stream ) ) != HB_Err_Ok )
2383           goto Fail0;
2384         (void)FILE_Seek( cur_offset );
2385       }
2386       else
2387         lan[n].PosFormat = 0;
2388     }
2389
2390     continue;
2391   Fail0:
2392     for ( k = 0; k < n; k++ )
2393       Free_Anchor( &lan[k] );
2394     goto Fail;
2395   }
2396
2397   return HB_Err_Ok;
2398
2399 Fail:
2400   for ( k = 0; k < m; k++ )
2401   {
2402     lan = cr[k].LigatureAnchor;
2403
2404     for ( n = 0; n < num_classes; n++ )
2405       Free_Anchor( &lan[n] );
2406
2407     FREE( lan );
2408   }
2409
2410   FREE( cr );
2411   return error;
2412 }
2413
2414
2415 static void  Free_LigatureAttach( HB_LigatureAttach*  lat,
2416                                   HB_UShort            num_classes )
2417 {
2418   HB_UShort        m, n, count;
2419
2420   HB_ComponentRecord*  cr;
2421   HB_Anchor*           lan;
2422
2423
2424   if ( lat->ComponentRecord )
2425   {
2426     count = lat->ComponentCount;
2427     cr    = lat->ComponentRecord;
2428
2429     for ( m = 0; m < count; m++ )
2430     {
2431       lan = cr[m].LigatureAnchor;
2432
2433       for ( n = 0; n < num_classes; n++ )
2434         Free_Anchor( &lan[n] );
2435
2436       FREE( lan );
2437     }
2438
2439     FREE( cr );
2440   }
2441 }
2442
2443
2444 /* LigatureArray */
2445
2446 static HB_Error  Load_LigatureArray( HB_LigatureArray*  la,
2447                                      HB_UShort           num_classes,
2448                                      HB_Stream           stream )
2449 {
2450   HB_Error  error;
2451
2452   HB_UShort            n, m, count;
2453   HB_UInt             cur_offset, new_offset, base_offset;
2454
2455   HB_LigatureAttach*  lat;
2456
2457
2458   base_offset = FILE_Pos();
2459
2460   if ( ACCESS_Frame( 2L ) )
2461     return error;
2462
2463   count = la->LigatureCount = GET_UShort();
2464
2465   FORGET_Frame();
2466
2467   la->LigatureAttach = NULL;
2468
2469   if ( ALLOC_ARRAY( la->LigatureAttach, count, HB_LigatureAttach ) )
2470     return error;
2471
2472   lat = la->LigatureAttach;
2473
2474   for ( n = 0; n < count; n++ )
2475   {
2476     if ( ACCESS_Frame( 2L ) )
2477       goto Fail;
2478
2479     new_offset = GET_UShort() + base_offset;
2480
2481     FORGET_Frame();
2482
2483     cur_offset = FILE_Pos();
2484     if ( FILE_Seek( new_offset ) ||
2485          ( error = Load_LigatureAttach( &lat[n], num_classes,
2486                                         stream ) ) != HB_Err_Ok )
2487       goto Fail;
2488     (void)FILE_Seek( cur_offset );
2489   }
2490
2491   return HB_Err_Ok;
2492
2493 Fail:
2494   for ( m = 0; m < n; m++ )
2495     Free_LigatureAttach( &lat[m], num_classes );
2496
2497   FREE( lat );
2498   return error;
2499 }
2500
2501
2502 static void  Free_LigatureArray( HB_LigatureArray*  la,
2503                                  HB_UShort          num_classes )
2504 {
2505   HB_UShort            n, count;
2506
2507   HB_LigatureAttach*  lat;
2508
2509
2510   if ( la->LigatureAttach )
2511   {
2512     count = la->LigatureCount;
2513     lat   = la->LigatureAttach;
2514
2515     for ( n = 0; n < count; n++ )
2516       Free_LigatureAttach( &lat[n], num_classes );
2517
2518     FREE( lat );
2519   }
2520 }
2521
2522
2523 /* MarkLigPosFormat1 */
2524
2525 static HB_Error  Load_MarkLigPos( HB_GPOS_SubTable* st,
2526                                   HB_Stream        stream )
2527 {
2528   HB_Error  error;
2529   HB_MarkLigPos*  mlp = &st->marklig;
2530
2531   HB_UInt  cur_offset, new_offset, base_offset;
2532
2533
2534   base_offset = FILE_Pos();
2535
2536   if ( ACCESS_Frame( 4L ) )
2537     return error;
2538
2539   mlp->PosFormat = GET_UShort();
2540   new_offset     = GET_UShort() + base_offset;
2541
2542   FORGET_Frame();
2543
2544   cur_offset = FILE_Pos();
2545   if ( FILE_Seek( new_offset ) ||
2546        ( error = _HB_OPEN_Load_Coverage( &mlp->MarkCoverage, stream ) ) != HB_Err_Ok )
2547     return error;
2548   (void)FILE_Seek( cur_offset );
2549
2550   if ( ACCESS_Frame( 2L ) )
2551     goto Fail3;
2552
2553   new_offset = GET_UShort() + base_offset;
2554
2555   FORGET_Frame();
2556
2557   cur_offset = FILE_Pos();
2558   if ( FILE_Seek( new_offset ) ||
2559        ( error = _HB_OPEN_Load_Coverage( &mlp->LigatureCoverage,
2560                                 stream ) ) != HB_Err_Ok )
2561     goto Fail3;
2562   (void)FILE_Seek( cur_offset );
2563
2564   if ( ACCESS_Frame( 4L ) )
2565     goto Fail2;
2566
2567   mlp->ClassCount = GET_UShort();
2568   new_offset      = GET_UShort() + base_offset;
2569
2570   FORGET_Frame();
2571
2572   cur_offset = FILE_Pos();
2573   if ( FILE_Seek( new_offset ) ||
2574        ( error = Load_MarkArray( &mlp->MarkArray, stream ) ) != HB_Err_Ok )
2575     goto Fail2;
2576   (void)FILE_Seek( cur_offset );
2577
2578   if ( ACCESS_Frame( 2L ) )
2579     goto Fail1;
2580
2581   new_offset = GET_UShort() + base_offset;
2582
2583   FORGET_Frame();
2584
2585   cur_offset = FILE_Pos();
2586   if ( FILE_Seek( new_offset ) ||
2587        ( error = Load_LigatureArray( &mlp->LigatureArray, mlp->ClassCount,
2588                                      stream ) ) != HB_Err_Ok )
2589     goto Fail1;
2590
2591   return HB_Err_Ok;
2592
2593 Fail1:
2594   Free_MarkArray( &mlp->MarkArray );
2595
2596 Fail2:
2597   _HB_OPEN_Free_Coverage( &mlp->LigatureCoverage );
2598
2599 Fail3:
2600   _HB_OPEN_Free_Coverage( &mlp->MarkCoverage );
2601   return error;
2602 }
2603
2604
2605 static void  Free_MarkLigPos( HB_GPOS_SubTable* st )
2606 {
2607   HB_MarkLigPos*  mlp = &st->marklig;
2608
2609   Free_LigatureArray( &mlp->LigatureArray, mlp->ClassCount );
2610   Free_MarkArray( &mlp->MarkArray );
2611   _HB_OPEN_Free_Coverage( &mlp->LigatureCoverage );
2612   _HB_OPEN_Free_Coverage( &mlp->MarkCoverage );
2613 }
2614
2615
2616 static HB_Error  Lookup_MarkLigPos( GPOS_Instance*    gpi,
2617                                     HB_GPOS_SubTable* st,
2618                                     HB_Buffer        buffer,
2619                                     HB_UShort         flags,
2620                                     HB_UShort         context_length,
2621                                     int               nesting_level )
2622 {
2623   HB_UShort        i, j, mark_index, lig_index, property, class;
2624   HB_UShort        mark_glyph;
2625   HB_Fixed           x_mark_value, y_mark_value, x_lig_value, y_lig_value;
2626   HB_Error         error;
2627   HB_GPOSHeader*  gpos = gpi->gpos;
2628   HB_MarkLigPos*  mlp = &st->marklig;
2629
2630   HB_MarkArray*        ma;
2631   HB_LigatureArray*    la;
2632   HB_LigatureAttach*   lat;
2633   HB_ComponentRecord*  cr;
2634   HB_UShort             comp_index;
2635   HB_Anchor*           mark_anchor;
2636   HB_Anchor*           lig_anchor;
2637
2638   HB_Position    o;
2639
2640   HB_UNUSED(nesting_level);
2641
2642   if ( context_length != 0xFFFF && context_length < 1 )
2643     return HB_Err_Not_Covered;
2644
2645   if ( flags & HB_LOOKUP_FLAG_IGNORE_LIGATURES )
2646     return HB_Err_Not_Covered;
2647
2648   mark_glyph = IN_CURGLYPH();
2649
2650   if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
2651     return error;
2652
2653   error = _HB_OPEN_Coverage_Index( &mlp->MarkCoverage, mark_glyph, &mark_index );
2654   if ( error )
2655     return error;
2656
2657   /* now we search backwards for a non-mark glyph */
2658
2659   i = 1;
2660   j = buffer->in_pos - 1;
2661
2662   while ( i <= buffer->in_pos )
2663   {
2664     error = HB_GDEF_Get_Glyph_Property( gpos->gdef, IN_GLYPH( j ),
2665                                         &property );
2666     if ( error )
2667       return error;
2668
2669     if ( !( property == HB_GDEF_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
2670       break;
2671
2672     i++;
2673     j--;
2674   }
2675
2676   /* Similar to Lookup_MarkBasePos(), I suspect that this assertion is
2677      too strong, thus it is commented out.                             */
2678 #if 0
2679   if ( property != HB_GDEF_LIGATURE )
2680     return HB_Err_Not_Covered;
2681 #endif
2682
2683   if ( i > buffer->in_pos )
2684     return HB_Err_Not_Covered;
2685
2686   error = _HB_OPEN_Coverage_Index( &mlp->LigatureCoverage, IN_GLYPH( j ),
2687                           &lig_index );
2688   if ( error )
2689     return error;
2690
2691   ma = &mlp->MarkArray;
2692
2693   if ( mark_index >= ma->MarkCount )
2694     return ERR(HB_Err_Invalid_SubTable);
2695
2696   class       = ma->MarkRecord[mark_index].Class;
2697   mark_anchor = &ma->MarkRecord[mark_index].MarkAnchor;
2698
2699   if ( class >= mlp->ClassCount )
2700     return ERR(HB_Err_Invalid_SubTable);
2701
2702   la = &mlp->LigatureArray;
2703
2704   if ( lig_index >= la->LigatureCount )
2705     return ERR(HB_Err_Invalid_SubTable);
2706
2707   lat = &la->LigatureAttach[lig_index];
2708
2709   /* We must now check whether the ligature ID of the current mark glyph
2710      is identical to the ligature ID of the found ligature.  If yes, we
2711      can directly use the component index.  If not, we attach the mark
2712      glyph to the last component of the ligature.                        */
2713
2714   if ( IN_LIGID( j ) == IN_LIGID( buffer->in_pos) )
2715   {
2716     comp_index = IN_COMPONENT( buffer->in_pos );
2717     if ( comp_index >= lat->ComponentCount )
2718       return HB_Err_Not_Covered;
2719   }
2720   else
2721     comp_index = lat->ComponentCount - 1;
2722
2723   cr         = &lat->ComponentRecord[comp_index];
2724   lig_anchor = &cr->LigatureAnchor[class];
2725
2726   error = Get_Anchor( gpi, mark_anchor, IN_CURGLYPH(),
2727                       &x_mark_value, &y_mark_value );
2728   if ( error )
2729     return error;
2730   error = Get_Anchor( gpi, lig_anchor, IN_GLYPH( j ),
2731                       &x_lig_value, &y_lig_value );
2732   if ( error )
2733     return error;
2734
2735   /* anchor points are not cumulative */
2736
2737   o = POSITION( buffer->in_pos );
2738
2739   o->x_pos     = x_lig_value - x_mark_value;
2740   o->y_pos     = y_lig_value - y_mark_value;
2741   o->x_advance = 0;
2742   o->y_advance = 0;
2743   o->back      = i;
2744
2745   (buffer->in_pos)++;
2746
2747   return HB_Err_Ok;
2748 }
2749
2750
2751 /* LookupType 6 */
2752
2753 /* Mark2Array */
2754
2755 static HB_Error  Load_Mark2Array( HB_Mark2Array*  m2a,
2756                                   HB_UShort        num_classes,
2757                                   HB_Stream        stream )
2758 {
2759   HB_Error  error;
2760
2761   HB_UShort         k, m, n, count;
2762   HB_UInt          cur_offset, new_offset, base_offset;
2763
2764   HB_Mark2Record*  m2r;
2765   HB_Anchor*       m2an;
2766
2767
2768   base_offset = FILE_Pos();
2769
2770   if ( ACCESS_Frame( 2L ) )
2771     return error;
2772
2773   count = m2a->Mark2Count = GET_UShort();
2774
2775   FORGET_Frame();
2776
2777   m2a->Mark2Record = NULL;
2778
2779   if ( ALLOC_ARRAY( m2a->Mark2Record, count, HB_Mark2Record ) )
2780     return error;
2781
2782   m2r = m2a->Mark2Record;
2783
2784   for ( m = 0; m < count; m++ )
2785   {
2786     m2r[m].Mark2Anchor = NULL;
2787
2788     if ( ALLOC_ARRAY( m2r[m].Mark2Anchor, num_classes, HB_Anchor ) )
2789       goto Fail;
2790
2791     m2an = m2r[m].Mark2Anchor;
2792
2793     for ( n = 0; n < num_classes; n++ )
2794     {
2795       if ( ACCESS_Frame( 2L ) )
2796         goto Fail0;
2797
2798       new_offset = GET_UShort() + base_offset;
2799
2800       FORGET_Frame();
2801
2802       if (new_offset == base_offset) {
2803         /* Anchor table not provided.  Skip loading.
2804          * Some versions of FreeSans hit this. */
2805         m2an[n].PosFormat = 0;
2806         continue;
2807       }
2808
2809       cur_offset = FILE_Pos();
2810       if ( FILE_Seek( new_offset ) ||
2811            ( error = Load_Anchor( &m2an[n], stream ) ) != HB_Err_Ok )
2812         goto Fail0;
2813       (void)FILE_Seek( cur_offset );
2814     }
2815
2816     continue;
2817   Fail0:
2818     for ( k = 0; k < n; k++ )
2819       Free_Anchor( &m2an[k] );
2820     goto Fail;
2821   }
2822
2823   return HB_Err_Ok;
2824
2825 Fail:
2826   for ( k = 0; k < m; k++ )
2827   {
2828     m2an = m2r[k].Mark2Anchor;
2829
2830     for ( n = 0; n < num_classes; n++ )
2831       Free_Anchor( &m2an[n] );
2832
2833     FREE( m2an );
2834   }
2835
2836   FREE( m2r );
2837   return error;
2838 }
2839
2840
2841 static void  Free_Mark2Array( HB_Mark2Array*  m2a,
2842                               HB_UShort        num_classes )
2843 {
2844   HB_UShort         m, n, count;
2845
2846   HB_Mark2Record*  m2r;
2847   HB_Anchor*       m2an;
2848
2849
2850   if ( m2a->Mark2Record )
2851   {
2852     count = m2a->Mark2Count;
2853     m2r   = m2a->Mark2Record;
2854
2855     for ( m = 0; m < count; m++ )
2856     {
2857       m2an = m2r[m].Mark2Anchor;
2858
2859       for ( n = 0; n < num_classes; n++ )
2860         Free_Anchor( &m2an[n] );
2861
2862       FREE( m2an );
2863     }
2864
2865     FREE( m2r );
2866   }
2867 }
2868
2869
2870 /* MarkMarkPosFormat1 */
2871
2872 static HB_Error  Load_MarkMarkPos( HB_GPOS_SubTable* st,
2873                                    HB_Stream         stream )
2874 {
2875   HB_Error  error;
2876   HB_MarkMarkPos* mmp = &st->markmark;
2877
2878   HB_UInt  cur_offset, new_offset, base_offset;
2879
2880
2881   base_offset = FILE_Pos();
2882
2883   if ( ACCESS_Frame( 4L ) )
2884     return error;
2885
2886   mmp->PosFormat = GET_UShort();
2887   new_offset     = GET_UShort() + base_offset;
2888
2889   FORGET_Frame();
2890
2891   cur_offset = FILE_Pos();
2892   if ( FILE_Seek( new_offset ) ||
2893        ( error = _HB_OPEN_Load_Coverage( &mmp->Mark1Coverage,
2894                                 stream ) ) != HB_Err_Ok )
2895     return error;
2896   (void)FILE_Seek( cur_offset );
2897
2898   if ( ACCESS_Frame( 2L ) )
2899     goto Fail3;
2900
2901   new_offset = GET_UShort() + base_offset;
2902
2903   FORGET_Frame();
2904
2905   cur_offset = FILE_Pos();
2906   if ( FILE_Seek( new_offset ) ||
2907        ( error = _HB_OPEN_Load_Coverage( &mmp->Mark2Coverage,
2908                                 stream ) ) != HB_Err_Ok )
2909     goto Fail3;
2910   (void)FILE_Seek( cur_offset );
2911
2912   if ( ACCESS_Frame( 4L ) )
2913     goto Fail2;
2914
2915   mmp->ClassCount = GET_UShort();
2916   new_offset      = GET_UShort() + base_offset;
2917
2918   FORGET_Frame();
2919
2920   cur_offset = FILE_Pos();
2921   if ( FILE_Seek( new_offset ) ||
2922        ( error = Load_MarkArray( &mmp->Mark1Array, stream ) ) != HB_Err_Ok )
2923     goto Fail2;
2924   (void)FILE_Seek( cur_offset );
2925
2926   if ( ACCESS_Frame( 2L ) )
2927     goto Fail1;
2928
2929   new_offset = GET_UShort() + base_offset;
2930
2931   FORGET_Frame();
2932
2933   cur_offset = FILE_Pos();
2934   if ( FILE_Seek( new_offset ) ||
2935        ( error = Load_Mark2Array( &mmp->Mark2Array, mmp->ClassCount,
2936                                   stream ) ) != HB_Err_Ok )
2937     goto Fail1;
2938
2939   return HB_Err_Ok;
2940
2941 Fail1:
2942   Free_MarkArray( &mmp->Mark1Array );
2943
2944 Fail2:
2945   _HB_OPEN_Free_Coverage( &mmp->Mark2Coverage );
2946
2947 Fail3:
2948   _HB_OPEN_Free_Coverage( &mmp->Mark1Coverage );
2949   return error;
2950 }
2951
2952
2953 static void  Free_MarkMarkPos( HB_GPOS_SubTable* st )
2954 {
2955   HB_MarkMarkPos* mmp = &st->markmark;
2956
2957   Free_Mark2Array( &mmp->Mark2Array, mmp->ClassCount );
2958   Free_MarkArray( &mmp->Mark1Array );
2959   _HB_OPEN_Free_Coverage( &mmp->Mark2Coverage );
2960   _HB_OPEN_Free_Coverage( &mmp->Mark1Coverage );
2961 }
2962
2963
2964 static HB_Error  Lookup_MarkMarkPos( GPOS_Instance*    gpi,
2965                                      HB_GPOS_SubTable* st,
2966                                      HB_Buffer        buffer,
2967                                      HB_UShort         flags,
2968                                      HB_UShort         context_length,
2969                                      int               nesting_level )
2970 {
2971   HB_UShort        i, j, mark1_index, mark2_index, property, class;
2972   HB_Fixed           x_mark1_value, y_mark1_value,
2973                    x_mark2_value, y_mark2_value;
2974   HB_Error         error;
2975   HB_GPOSHeader*  gpos = gpi->gpos;
2976   HB_MarkMarkPos* mmp = &st->markmark;
2977
2978   HB_MarkArray*    ma1;
2979   HB_Mark2Array*   ma2;
2980   HB_Mark2Record*  m2r;
2981   HB_Anchor*       mark1_anchor;
2982   HB_Anchor*       mark2_anchor;
2983
2984   HB_Position    o;
2985
2986   HB_UNUSED(nesting_level);
2987
2988   if ( context_length != 0xFFFF && context_length < 1 )
2989     return HB_Err_Not_Covered;
2990
2991   if ( flags & HB_LOOKUP_FLAG_IGNORE_MARKS )
2992     return HB_Err_Not_Covered;
2993
2994   if ( CHECK_Property( gpos->gdef, IN_CURITEM(),
2995                        flags, &property ) )
2996     return error;
2997
2998   error = _HB_OPEN_Coverage_Index( &mmp->Mark1Coverage, IN_CURGLYPH(),
2999                           &mark1_index );
3000   if ( error )
3001     return error;
3002
3003   /* now we search backwards for a suitable mark glyph until a non-mark
3004      glyph                                                */
3005
3006   if ( buffer->in_pos == 0 )
3007     return HB_Err_Not_Covered;
3008
3009   i = 1;
3010   j = buffer->in_pos - 1;
3011   while ( i <= buffer->in_pos )
3012   {
3013     error = HB_GDEF_Get_Glyph_Property( gpos->gdef, IN_GLYPH( j ),
3014                                         &property );
3015     if ( error )
3016       return error;
3017
3018     if ( !( property == HB_GDEF_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
3019       return HB_Err_Not_Covered;
3020
3021     if ( flags & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS )
3022     {
3023       if ( property == (flags & 0xFF00) )
3024         break;
3025     }
3026     else
3027       break;
3028
3029     i++;
3030     j--;
3031   }
3032
3033   error = _HB_OPEN_Coverage_Index( &mmp->Mark2Coverage, IN_GLYPH( j ),
3034                           &mark2_index );
3035   if ( error )
3036     return error;
3037
3038   ma1 = &mmp->Mark1Array;
3039
3040   if ( mark1_index >= ma1->MarkCount )
3041     return ERR(HB_Err_Invalid_SubTable);
3042
3043   class        = ma1->MarkRecord[mark1_index].Class;
3044   mark1_anchor = &ma1->MarkRecord[mark1_index].MarkAnchor;
3045
3046   if ( class >= mmp->ClassCount )
3047     return ERR(HB_Err_Invalid_SubTable);
3048
3049   ma2 = &mmp->Mark2Array;
3050
3051   if ( mark2_index >= ma2->Mark2Count )
3052     return ERR(HB_Err_Invalid_SubTable);
3053
3054   m2r          = &ma2->Mark2Record[mark2_index];
3055   mark2_anchor = &m2r->Mark2Anchor[class];
3056
3057   error = Get_Anchor( gpi, mark1_anchor, IN_CURGLYPH(),
3058                       &x_mark1_value, &y_mark1_value );
3059   if ( error )
3060     return error;
3061   error = Get_Anchor( gpi, mark2_anchor, IN_GLYPH( j ),
3062                       &x_mark2_value, &y_mark2_value );
3063   if ( error )
3064     return error;
3065
3066   /* anchor points are not cumulative */
3067
3068   o = POSITION( buffer->in_pos );
3069
3070   o->x_pos     = x_mark2_value - x_mark1_value;
3071   o->y_pos     = y_mark2_value - y_mark1_value;
3072   o->x_advance = 0;
3073   o->y_advance = 0;
3074   o->back      = 1;
3075
3076   (buffer->in_pos)++;
3077
3078   return HB_Err_Ok;
3079 }
3080
3081
3082 /* Do the actual positioning for a context positioning (either format
3083    7 or 8).  This is only called after we've determined that the stream
3084    matches the subrule.                                                 */
3085
3086 static HB_Error  Do_ContextPos( GPOS_Instance*        gpi,
3087                                 HB_UShort             GlyphCount,
3088                                 HB_UShort             PosCount,
3089                                 HB_PosLookupRecord*  pos,
3090                                 HB_Buffer            buffer,
3091                                 int                   nesting_level )
3092 {
3093   HB_Error  error;
3094   HB_UInt i, old_pos;
3095
3096
3097   i = 0;
3098
3099   while ( i < GlyphCount )
3100   {
3101     if ( PosCount && i == pos->SequenceIndex )
3102     {
3103       old_pos = buffer->in_pos;
3104
3105       /* Do a positioning */
3106
3107       error = GPOS_Do_Glyph_Lookup( gpi, pos->LookupListIndex, buffer,
3108                                     GlyphCount, nesting_level );
3109
3110       if ( error )
3111         return error;
3112
3113       pos++;
3114       PosCount--;
3115       i += buffer->in_pos - old_pos;
3116     }
3117     else
3118     {
3119       i++;
3120       (buffer->in_pos)++;
3121     }
3122   }
3123
3124   return HB_Err_Ok;
3125 }
3126
3127
3128 /* LookupType 7 */
3129
3130 /* PosRule */
3131
3132 static HB_Error  Load_PosRule( HB_PosRule*  pr,
3133                                HB_Stream     stream )
3134 {
3135   HB_Error  error;
3136
3137   HB_UShort             n, count;
3138   HB_UShort*            i;
3139
3140   HB_PosLookupRecord*  plr;
3141
3142
3143   if ( ACCESS_Frame( 4L ) )
3144     return error;
3145
3146   pr->GlyphCount = GET_UShort();
3147   pr->PosCount   = GET_UShort();
3148
3149   FORGET_Frame();
3150
3151   pr->Input = NULL;
3152
3153   count = pr->GlyphCount - 1;         /* only GlyphCount - 1 elements */
3154
3155   if ( ALLOC_ARRAY( pr->Input, count, HB_UShort ) )
3156     return error;
3157
3158   i = pr->Input;
3159
3160   if ( ACCESS_Frame( count * 2L ) )
3161     goto Fail2;
3162
3163   for ( n = 0; n < count; n++ )
3164     i[n] = GET_UShort();
3165
3166   FORGET_Frame();
3167
3168   pr->PosLookupRecord = NULL;
3169
3170   count = pr->PosCount;
3171
3172   if ( ALLOC_ARRAY( pr->PosLookupRecord, count, HB_PosLookupRecord ) )
3173     goto Fail2;
3174
3175   plr = pr->PosLookupRecord;
3176
3177   if ( ACCESS_Frame( count * 4L ) )
3178     goto Fail1;
3179
3180   for ( n = 0; n < count; n++ )
3181   {
3182     plr[n].SequenceIndex   = GET_UShort();
3183     plr[n].LookupListIndex = GET_UShort();
3184   }
3185
3186   FORGET_Frame();
3187
3188   return HB_Err_Ok;
3189
3190 Fail1:
3191   FREE( plr );
3192
3193 Fail2:
3194   FREE( i );
3195   return error;
3196 }
3197
3198
3199 static void  Free_PosRule( HB_PosRule*  pr )
3200 {
3201   FREE( pr->PosLookupRecord );
3202   FREE( pr->Input );
3203 }
3204
3205
3206 /* PosRuleSet */
3207
3208 static HB_Error  Load_PosRuleSet( HB_PosRuleSet*  prs,
3209                                   HB_Stream        stream )
3210 {
3211   HB_Error  error;
3212
3213   HB_UShort     n, m, count;
3214   HB_UInt      cur_offset, new_offset, base_offset;
3215
3216   HB_PosRule*  pr;
3217
3218
3219   base_offset = FILE_Pos();
3220
3221   if ( ACCESS_Frame( 2L ) )
3222     return error;
3223
3224   count = prs->PosRuleCount = GET_UShort();
3225
3226   FORGET_Frame();
3227
3228   prs->PosRule = NULL;
3229
3230   if ( ALLOC_ARRAY( prs->PosRule, count, HB_PosRule ) )
3231     return error;
3232
3233   pr = prs->PosRule;
3234
3235   for ( n = 0; n < count; n++ )
3236   {
3237     if ( ACCESS_Frame( 2L ) )
3238       goto Fail;
3239
3240     new_offset = GET_UShort() + base_offset;
3241
3242     FORGET_Frame();
3243
3244     cur_offset = FILE_Pos();
3245     if ( FILE_Seek( new_offset ) ||
3246          ( error = Load_PosRule( &pr[n], stream ) ) != HB_Err_Ok )
3247       goto Fail;
3248     (void)FILE_Seek( cur_offset );
3249   }
3250
3251   return HB_Err_Ok;
3252
3253 Fail:
3254   for ( m = 0; m < n; m++ )
3255     Free_PosRule( &pr[m] );
3256
3257   FREE( pr );
3258   return error;
3259 }
3260
3261
3262 static void  Free_PosRuleSet( HB_PosRuleSet*  prs )
3263 {
3264   HB_UShort     n, count;
3265
3266   HB_PosRule*  pr;
3267
3268
3269   if ( prs->PosRule )
3270   {
3271     count = prs->PosRuleCount;
3272     pr    = prs->PosRule;
3273
3274     for ( n = 0; n < count; n++ )
3275       Free_PosRule( &pr[n] );
3276
3277     FREE( pr );
3278   }
3279 }
3280
3281
3282 /* ContextPosFormat1 */
3283
3284 static HB_Error  Load_ContextPos1( HB_ContextPosFormat1*  cpf1,
3285                                    HB_Stream               stream )
3286 {
3287   HB_Error  error;
3288
3289   HB_UShort        n, m, count;
3290   HB_UInt         cur_offset, new_offset, base_offset;
3291
3292   HB_PosRuleSet*  prs;
3293
3294
3295   base_offset = FILE_Pos() - 2L;
3296
3297   if ( ACCESS_Frame( 2L ) )
3298     return error;
3299
3300   new_offset = GET_UShort() + base_offset;
3301
3302   FORGET_Frame();
3303
3304   cur_offset = FILE_Pos();
3305   if ( FILE_Seek( new_offset ) ||
3306        ( error = _HB_OPEN_Load_Coverage( &cpf1->Coverage, stream ) ) != HB_Err_Ok )
3307     return error;
3308   (void)FILE_Seek( cur_offset );
3309
3310   if ( ACCESS_Frame( 2L ) )
3311     goto Fail2;
3312
3313   count = cpf1->PosRuleSetCount = GET_UShort();
3314
3315   FORGET_Frame();
3316
3317   cpf1->PosRuleSet = NULL;
3318
3319   if ( ALLOC_ARRAY( cpf1->PosRuleSet, count, HB_PosRuleSet ) )
3320     goto Fail2;
3321
3322   prs = cpf1->PosRuleSet;
3323
3324   for ( n = 0; n < count; n++ )
3325   {
3326     if ( ACCESS_Frame( 2L ) )
3327       goto Fail1;
3328
3329     new_offset = GET_UShort() + base_offset;
3330
3331     FORGET_Frame();
3332
3333     cur_offset = FILE_Pos();
3334     if ( FILE_Seek( new_offset ) ||
3335          ( error = Load_PosRuleSet( &prs[n], stream ) ) != HB_Err_Ok )
3336       goto Fail1;
3337     (void)FILE_Seek( cur_offset );
3338   }
3339
3340   return HB_Err_Ok;
3341
3342 Fail1:
3343   for ( m = 0; m < n; m++ )
3344     Free_PosRuleSet( &prs[m] );
3345
3346   FREE( prs );
3347
3348 Fail2:
3349   _HB_OPEN_Free_Coverage( &cpf1->Coverage );
3350   return error;
3351 }
3352
3353
3354 static void  Free_ContextPos1( HB_ContextPosFormat1*  cpf1 )
3355 {
3356   HB_UShort        n, count;
3357
3358   HB_PosRuleSet*  prs;
3359
3360
3361   if ( cpf1->PosRuleSet )
3362   {
3363     count = cpf1->PosRuleSetCount;
3364     prs   = cpf1->PosRuleSet;
3365
3366     for ( n = 0; n < count; n++ )
3367       Free_PosRuleSet( &prs[n] );
3368
3369     FREE( prs );
3370   }
3371
3372   _HB_OPEN_Free_Coverage( &cpf1->Coverage );
3373 }
3374
3375
3376 /* PosClassRule */
3377
3378 static HB_Error  Load_PosClassRule( HB_ContextPosFormat2*  cpf2,
3379                                     HB_PosClassRule*       pcr,
3380                                     HB_Stream               stream )
3381 {
3382   HB_Error  error;
3383
3384   HB_UShort             n, count;
3385
3386   HB_UShort*            c;
3387   HB_PosLookupRecord*  plr;
3388   HB_Bool*              d;
3389
3390
3391   if ( ACCESS_Frame( 4L ) )
3392     return error;
3393
3394   pcr->GlyphCount = GET_UShort();
3395   pcr->PosCount   = GET_UShort();
3396
3397   FORGET_Frame();
3398
3399   if ( pcr->GlyphCount > cpf2->MaxContextLength )
3400     cpf2->MaxContextLength = pcr->GlyphCount;
3401
3402   pcr->Class = NULL;
3403
3404   count = pcr->GlyphCount - 1;        /* only GlyphCount - 1 elements */
3405
3406   if ( ALLOC_ARRAY( pcr->Class, count, HB_UShort ) )
3407     return error;
3408
3409   c = pcr->Class;
3410   d = cpf2->ClassDef.Defined;
3411
3412   if ( ACCESS_Frame( count * 2L ) )
3413     goto Fail2;
3414
3415   for ( n = 0; n < count; n++ )
3416   {
3417     c[n] = GET_UShort();
3418
3419     /* We check whether the specific class is used at all.  If not,
3420        class 0 is used instead.                                     */
3421
3422     if ( !d[c[n]] )
3423       c[n] = 0;
3424   }
3425
3426   FORGET_Frame();
3427
3428   pcr->PosLookupRecord = NULL;
3429
3430   count = pcr->PosCount;
3431
3432   if ( ALLOC_ARRAY( pcr->PosLookupRecord, count, HB_PosLookupRecord ) )
3433     goto Fail2;
3434
3435   plr = pcr->PosLookupRecord;
3436
3437   if ( ACCESS_Frame( count * 4L ) )
3438     goto Fail1;
3439
3440   for ( n = 0; n < count; n++ )
3441   {
3442     plr[n].SequenceIndex   = GET_UShort();
3443     plr[n].LookupListIndex = GET_UShort();
3444   }
3445
3446   FORGET_Frame();
3447
3448   return HB_Err_Ok;
3449
3450 Fail1:
3451   FREE( plr );
3452
3453 Fail2:
3454   FREE( c );
3455   return error;
3456 }
3457
3458
3459 static void  Free_PosClassRule( HB_PosClassRule*  pcr )
3460 {
3461   FREE( pcr->PosLookupRecord );
3462   FREE( pcr->Class );
3463 }
3464
3465
3466 /* PosClassSet */
3467
3468 static HB_Error  Load_PosClassSet( HB_ContextPosFormat2*  cpf2,
3469                                    HB_PosClassSet*        pcs,
3470                                    HB_Stream               stream )
3471 {
3472   HB_Error  error;
3473
3474   HB_UShort          n, m, count;
3475   HB_UInt           cur_offset, new_offset, base_offset;
3476
3477   HB_PosClassRule*  pcr;
3478
3479
3480   base_offset = FILE_Pos();
3481
3482   if ( ACCESS_Frame( 2L ) )
3483     return error;
3484
3485   count = pcs->PosClassRuleCount = GET_UShort();
3486
3487   FORGET_Frame();
3488
3489   pcs->PosClassRule = NULL;
3490
3491   if ( ALLOC_ARRAY( pcs->PosClassRule, count, HB_PosClassRule ) )
3492     return error;
3493
3494   pcr = pcs->PosClassRule;
3495
3496   for ( n = 0; n < count; n++ )
3497   {
3498     if ( ACCESS_Frame( 2L ) )
3499       goto Fail;
3500
3501     new_offset = GET_UShort() + base_offset;
3502
3503     FORGET_Frame();
3504
3505     cur_offset = FILE_Pos();
3506     if ( FILE_Seek( new_offset ) ||
3507          ( error = Load_PosClassRule( cpf2, &pcr[n],
3508                                       stream ) ) != HB_Err_Ok )
3509       goto Fail;
3510     (void)FILE_Seek( cur_offset );
3511   }
3512
3513   return HB_Err_Ok;
3514
3515 Fail:
3516   for ( m = 0; m < n; m++ )
3517     Free_PosClassRule( &pcr[m] );
3518
3519   FREE( pcr );
3520   return error;
3521 }
3522
3523
3524 static void  Free_PosClassSet( HB_PosClassSet*  pcs )
3525 {
3526   HB_UShort          n, count;
3527
3528   HB_PosClassRule*  pcr;
3529
3530
3531   if ( pcs->PosClassRule )
3532   {
3533     count = pcs->PosClassRuleCount;
3534     pcr   = pcs->PosClassRule;
3535
3536     for ( n = 0; n < count; n++ )
3537       Free_PosClassRule( &pcr[n] );
3538
3539     FREE( pcr );
3540   }
3541 }
3542
3543
3544 /* ContextPosFormat2 */
3545
3546 static HB_Error  Load_ContextPos2( HB_ContextPosFormat2*  cpf2,
3547                                    HB_Stream               stream )
3548 {
3549   HB_Error  error;
3550
3551   HB_UShort         n, m, count;
3552   HB_UInt          cur_offset, new_offset, base_offset;
3553
3554   HB_PosClassSet*  pcs;
3555
3556
3557   base_offset = FILE_Pos() - 2;
3558
3559   if ( ACCESS_Frame( 2L ) )
3560     return error;
3561
3562   new_offset = GET_UShort() + base_offset;
3563
3564   FORGET_Frame();
3565
3566   cur_offset = FILE_Pos();
3567   if ( FILE_Seek( new_offset ) ||
3568        ( error = _HB_OPEN_Load_Coverage( &cpf2->Coverage, stream ) ) != HB_Err_Ok )
3569     return error;
3570   (void)FILE_Seek( cur_offset );
3571
3572   if ( ACCESS_Frame( 4L ) )
3573     goto Fail3;
3574
3575   new_offset = GET_UShort() + base_offset;
3576
3577   /* `PosClassSetCount' is the upper limit for class values, thus we
3578      read it now to make an additional safety check.                 */
3579
3580   count = cpf2->PosClassSetCount = GET_UShort();
3581
3582   FORGET_Frame();
3583
3584   cur_offset = FILE_Pos();
3585   if ( FILE_Seek( new_offset ) ||
3586        ( error = _HB_OPEN_Load_ClassDefinition( &cpf2->ClassDef, count,
3587                                        stream ) ) != HB_Err_Ok )
3588     goto Fail3;
3589   (void)FILE_Seek( cur_offset );
3590
3591   cpf2->PosClassSet      = NULL;
3592   cpf2->MaxContextLength = 0;
3593
3594   if ( ALLOC_ARRAY( cpf2->PosClassSet, count, HB_PosClassSet ) )
3595     goto Fail2;
3596
3597   pcs = cpf2->PosClassSet;
3598
3599   for ( n = 0; n < count; n++ )
3600   {
3601     if ( ACCESS_Frame( 2L ) )
3602       goto Fail1;
3603
3604     new_offset = GET_UShort() + base_offset;
3605
3606     FORGET_Frame();
3607
3608     if ( new_offset != base_offset )      /* not a NULL offset */
3609     {
3610       cur_offset = FILE_Pos();
3611       if ( FILE_Seek( new_offset ) ||
3612            ( error = Load_PosClassSet( cpf2, &pcs[n],
3613                                        stream ) ) != HB_Err_Ok )
3614         goto Fail1;
3615       (void)FILE_Seek( cur_offset );
3616     }
3617     else
3618     {
3619       /* we create a PosClassSet table with no entries */
3620
3621       cpf2->PosClassSet[n].PosClassRuleCount = 0;
3622       cpf2->PosClassSet[n].PosClassRule      = NULL;
3623     }
3624   }
3625
3626   return HB_Err_Ok;
3627
3628 Fail1:
3629   for ( m = 0; m < n; n++ )
3630     Free_PosClassSet( &pcs[m] );
3631
3632   FREE( pcs );
3633
3634 Fail2:
3635   _HB_OPEN_Free_ClassDefinition( &cpf2->ClassDef );
3636
3637 Fail3:
3638   _HB_OPEN_Free_Coverage( &cpf2->Coverage );
3639   return error;
3640 }
3641
3642
3643 static void  Free_ContextPos2( HB_ContextPosFormat2*  cpf2 )
3644 {
3645   HB_UShort         n, count;
3646
3647   HB_PosClassSet*  pcs;
3648
3649
3650   if ( cpf2->PosClassSet )
3651   {
3652     count = cpf2->PosClassSetCount;
3653     pcs   = cpf2->PosClassSet;
3654
3655     for ( n = 0; n < count; n++ )
3656       Free_PosClassSet( &pcs[n] );
3657
3658     FREE( pcs );
3659   }
3660
3661   _HB_OPEN_Free_ClassDefinition( &cpf2->ClassDef );
3662   _HB_OPEN_Free_Coverage( &cpf2->Coverage );
3663 }
3664
3665
3666 /* ContextPosFormat3 */
3667
3668 static HB_Error  Load_ContextPos3( HB_ContextPosFormat3*  cpf3,
3669                                    HB_Stream               stream )
3670 {
3671   HB_Error  error;
3672
3673   HB_UShort             n, count;
3674   HB_UInt              cur_offset, new_offset, base_offset;
3675
3676   HB_Coverage*         c;
3677   HB_PosLookupRecord*  plr;
3678
3679
3680   base_offset = FILE_Pos() - 2L;
3681
3682   if ( ACCESS_Frame( 4L ) )
3683     return error;
3684
3685   cpf3->GlyphCount = GET_UShort();
3686   cpf3->PosCount   = GET_UShort();
3687
3688   FORGET_Frame();
3689
3690   cpf3->Coverage = NULL;
3691
3692   count = cpf3->GlyphCount;
3693
3694   if ( ALLOC_ARRAY( cpf3->Coverage, count, HB_Coverage ) )
3695     return error;
3696
3697   c = cpf3->Coverage;
3698
3699   for ( n = 0; n < count; n++ )
3700   {
3701     if ( ACCESS_Frame( 2L ) )
3702       goto Fail2;
3703
3704     new_offset = GET_UShort() + base_offset;
3705
3706     FORGET_Frame();
3707
3708     cur_offset = FILE_Pos();
3709     if ( FILE_Seek( new_offset ) ||
3710          ( error = _HB_OPEN_Load_Coverage( &c[n], stream ) ) != HB_Err_Ok )
3711       goto Fail2;
3712     (void)FILE_Seek( cur_offset );
3713   }
3714
3715   cpf3->PosLookupRecord = NULL;
3716
3717   count = cpf3->PosCount;
3718
3719   if ( ALLOC_ARRAY( cpf3->PosLookupRecord, count, HB_PosLookupRecord ) )
3720     goto Fail2;
3721
3722   plr = cpf3->PosLookupRecord;
3723
3724   if ( ACCESS_Frame( count * 4L ) )
3725     goto Fail1;
3726
3727   for ( n = 0; n < count; n++ )
3728   {
3729     plr[n].SequenceIndex   = GET_UShort();
3730     plr[n].LookupListIndex = GET_UShort();
3731   }
3732
3733   FORGET_Frame();
3734
3735   return HB_Err_Ok;
3736
3737 Fail1:
3738   FREE( plr );
3739
3740 Fail2:
3741   for ( n = 0; n < count; n++ )
3742     _HB_OPEN_Free_Coverage( &c[n] );
3743
3744   FREE( c );
3745   return error;
3746 }
3747
3748
3749 static void  Free_ContextPos3( HB_ContextPosFormat3*  cpf3 )
3750 {
3751   HB_UShort      n, count;
3752
3753   HB_Coverage*  c;
3754
3755
3756   FREE( cpf3->PosLookupRecord );
3757
3758   if ( cpf3->Coverage )
3759   {
3760     count = cpf3->GlyphCount;
3761     c     = cpf3->Coverage;
3762
3763     for ( n = 0; n < count; n++ )
3764       _HB_OPEN_Free_Coverage( &c[n] );
3765
3766     FREE( c );
3767   }
3768 }
3769
3770
3771 /* ContextPos */
3772
3773 static HB_Error  Load_ContextPos( HB_GPOS_SubTable* st,
3774                                   HB_Stream        stream )
3775 {
3776   HB_Error  error;
3777   HB_ContextPos*   cp = &st->context;
3778
3779
3780   if ( ACCESS_Frame( 2L ) )
3781     return error;
3782
3783   cp->PosFormat = GET_UShort();
3784
3785   FORGET_Frame();
3786
3787   switch ( cp->PosFormat )
3788   {
3789   case 1:
3790     return Load_ContextPos1( &cp->cpf.cpf1, stream );
3791
3792   case 2:
3793     return Load_ContextPos2( &cp->cpf.cpf2, stream );
3794
3795   case 3:
3796     return Load_ContextPos3( &cp->cpf.cpf3, stream );
3797
3798   default:
3799     return ERR(HB_Err_Invalid_SubTable_Format);
3800   }
3801
3802   return HB_Err_Ok;               /* never reached */
3803 }
3804
3805
3806 static void  Free_ContextPos( HB_GPOS_SubTable* st )
3807 {
3808   HB_ContextPos*   cp = &st->context;
3809
3810   switch ( cp->PosFormat )
3811   {
3812   case 1:  Free_ContextPos1( &cp->cpf.cpf1 ); break;
3813   case 2:  Free_ContextPos2( &cp->cpf.cpf2 ); break;
3814   case 3:  Free_ContextPos3( &cp->cpf.cpf3 ); break;
3815   default:                                            break;
3816   }
3817 }
3818
3819
3820 static HB_Error  Lookup_ContextPos1( GPOS_Instance*          gpi,
3821                                      HB_ContextPosFormat1*  cpf1,
3822                                      HB_Buffer              buffer,
3823                                      HB_UShort               flags,
3824                                      HB_UShort               context_length,
3825                                      int                     nesting_level )
3826 {
3827   HB_UShort        index, property;
3828   HB_UShort        i, j, k, numpr;
3829   HB_Error         error;
3830   HB_GPOSHeader*  gpos = gpi->gpos;
3831
3832   HB_PosRule*     pr;
3833   HB_GDEFHeader*  gdef;
3834
3835
3836   gdef = gpos->gdef;
3837
3838   if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
3839     return error;
3840
3841   error = _HB_OPEN_Coverage_Index( &cpf1->Coverage, IN_CURGLYPH(), &index );
3842   if ( error )
3843     return error;
3844
3845   pr    = cpf1->PosRuleSet[index].PosRule;
3846   numpr = cpf1->PosRuleSet[index].PosRuleCount;
3847
3848   for ( k = 0; k < numpr; k++ )
3849   {
3850     if ( context_length != 0xFFFF && context_length < pr[k].GlyphCount )
3851       goto next_posrule;
3852
3853     if ( buffer->in_pos + pr[k].GlyphCount > buffer->in_length )
3854       goto next_posrule;                       /* context is too long */
3855
3856     for ( i = 1, j = buffer->in_pos + 1; i < pr[k].GlyphCount; i++, j++ )
3857     {
3858       while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
3859       {
3860         if ( error && error != HB_Err_Not_Covered )
3861           return error;
3862
3863         if ( j + pr[k].GlyphCount - i == (HB_Int)buffer->in_length )
3864           goto next_posrule;
3865         j++;
3866       }
3867
3868       if ( IN_GLYPH( j ) != pr[k].Input[i - 1] )
3869         goto next_posrule;
3870     }
3871
3872     return Do_ContextPos( gpi, pr[k].GlyphCount,
3873                           pr[k].PosCount, pr[k].PosLookupRecord,
3874                           buffer,
3875                           nesting_level );
3876
3877     next_posrule:
3878       ;
3879   }
3880
3881   return HB_Err_Not_Covered;
3882 }
3883
3884
3885 static HB_Error  Lookup_ContextPos2( GPOS_Instance*          gpi,
3886                                      HB_ContextPosFormat2*  cpf2,
3887                                      HB_Buffer              buffer,
3888                                      HB_UShort               flags,
3889                                      HB_UShort               context_length,
3890                                      int                     nesting_level )
3891 {
3892   HB_UShort          index, property;
3893   HB_Error           error;
3894   HB_UShort          i, j, k, known_classes;
3895
3896   HB_UShort*         classes;
3897   HB_UShort*         cl;
3898   HB_GPOSHeader*    gpos = gpi->gpos;
3899
3900   HB_PosClassSet*   pcs;
3901   HB_PosClassRule*  pr;
3902   HB_GDEFHeader*    gdef;
3903
3904
3905   gdef = gpos->gdef;
3906
3907   if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
3908     return error;
3909
3910   /* Note: The coverage table in format 2 doesn't give an index into
3911            anything.  It just lets us know whether or not we need to
3912            do any lookup at all.                                     */
3913
3914   error = _HB_OPEN_Coverage_Index( &cpf2->Coverage, IN_CURGLYPH(), &index );
3915   if ( error )
3916     return error;
3917
3918   if ( ALLOC_ARRAY( classes, cpf2->MaxContextLength, HB_UShort ) )
3919     return error;
3920
3921   error = _HB_OPEN_Get_Class( &cpf2->ClassDef, IN_CURGLYPH(),
3922                      &classes[0], NULL );
3923   if ( error && error != HB_Err_Not_Covered )
3924     goto End;
3925   known_classes = 0;
3926
3927   pcs = &cpf2->PosClassSet[classes[0]];
3928   if ( !pcs )
3929   {
3930     error = ERR(HB_Err_Invalid_SubTable);
3931     goto End;
3932   }
3933
3934   for ( k = 0; k < pcs->PosClassRuleCount; k++ )
3935   {
3936     pr = &pcs->PosClassRule[k];
3937
3938     if ( context_length != 0xFFFF && context_length < pr->GlyphCount )
3939       goto next_posclassrule;
3940
3941     if ( buffer->in_pos + pr->GlyphCount > buffer->in_length )
3942       goto next_posclassrule;                /* context is too long */
3943
3944     cl   = pr->Class;
3945
3946     /* Start at 1 because [0] is implied */
3947
3948     for ( i = 1, j = buffer->in_pos + 1; i < pr->GlyphCount; i++, j++ )
3949     {
3950       while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
3951       {
3952         if ( error && error != HB_Err_Not_Covered )
3953           goto End;
3954
3955         if ( j + pr->GlyphCount - i == (HB_Int)buffer->in_length )
3956           goto next_posclassrule;
3957         j++;
3958       }
3959
3960       if ( i > known_classes )
3961       {
3962         /* Keeps us from having to do this for each rule */
3963
3964         error = _HB_OPEN_Get_Class( &cpf2->ClassDef, IN_GLYPH( j ), &classes[i], NULL );
3965         if ( error && error != HB_Err_Not_Covered )
3966           goto End;
3967         known_classes = i;
3968       }
3969
3970       if ( cl[i - 1] != classes[i] )
3971         goto next_posclassrule;
3972     }
3973
3974     error = Do_ContextPos( gpi, pr->GlyphCount,
3975                            pr->PosCount, pr->PosLookupRecord,
3976                            buffer,
3977                            nesting_level );
3978     goto End;
3979
3980   next_posclassrule:
3981     ;
3982   }
3983
3984   error = HB_Err_Not_Covered;
3985
3986 End:
3987   FREE( classes );
3988   return error;
3989 }
3990
3991
3992 static HB_Error  Lookup_ContextPos3( GPOS_Instance*          gpi,
3993                                      HB_ContextPosFormat3*  cpf3,
3994                                      HB_Buffer              buffer,
3995                                      HB_UShort               flags,
3996                                      HB_UShort               context_length,
3997                                      int                     nesting_level )
3998 {
3999   HB_Error         error;
4000   HB_UShort        index, i, j, property;
4001   HB_GPOSHeader*  gpos = gpi->gpos;
4002
4003   HB_Coverage*    c;
4004   HB_GDEFHeader*  gdef;
4005
4006
4007   gdef = gpos->gdef;
4008
4009   if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
4010     return error;
4011
4012   if ( context_length != 0xFFFF && context_length < cpf3->GlyphCount )
4013     return HB_Err_Not_Covered;
4014
4015   if ( buffer->in_pos + cpf3->GlyphCount > buffer->in_length )
4016     return HB_Err_Not_Covered;         /* context is too long */
4017
4018   c    = cpf3->Coverage;
4019
4020   for ( i = 1, j = 1; i < cpf3->GlyphCount; i++, j++ )
4021   {
4022     while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
4023     {
4024       if ( error && error != HB_Err_Not_Covered )
4025         return error;
4026
4027       if ( j + cpf3->GlyphCount - i == (HB_Int)buffer->in_length )
4028         return HB_Err_Not_Covered;
4029       j++;
4030     }
4031
4032     error = _HB_OPEN_Coverage_Index( &c[i], IN_GLYPH( j ), &index );
4033     if ( error )
4034       return error;
4035   }
4036
4037   return Do_ContextPos( gpi, cpf3->GlyphCount,
4038                         cpf3->PosCount, cpf3->PosLookupRecord,
4039                         buffer,
4040                         nesting_level );
4041 }
4042
4043
4044 static HB_Error  Lookup_ContextPos( GPOS_Instance*    gpi,
4045                                     HB_GPOS_SubTable* st,
4046                                     HB_Buffer        buffer,
4047                                     HB_UShort         flags,
4048                                     HB_UShort         context_length,
4049                                     int               nesting_level )
4050 {
4051   HB_ContextPos*   cp = &st->context;
4052
4053   switch ( cp->PosFormat )
4054   {
4055   case 1:
4056     return Lookup_ContextPos1( gpi, &cp->cpf.cpf1, buffer,
4057                                flags, context_length, nesting_level );
4058
4059   case 2:
4060     return Lookup_ContextPos2( gpi, &cp->cpf.cpf2, buffer,
4061                                flags, context_length, nesting_level );
4062
4063   case 3:
4064     return Lookup_ContextPos3( gpi, &cp->cpf.cpf3, buffer,
4065                                flags, context_length, nesting_level );
4066
4067   default:
4068     return ERR(HB_Err_Invalid_SubTable_Format);
4069   }
4070
4071   return HB_Err_Ok;               /* never reached */
4072 }
4073
4074
4075 /* LookupType 8 */
4076
4077 /* ChainPosRule */
4078
4079 static HB_Error  Load_ChainPosRule( HB_ChainPosRule*  cpr,
4080                                     HB_Stream          stream )
4081 {
4082   HB_Error  error;
4083
4084   HB_UShort             n, count;
4085   HB_UShort*            b;
4086   HB_UShort*            i;
4087   HB_UShort*            l;
4088
4089   HB_PosLookupRecord*  plr;
4090
4091
4092   if ( ACCESS_Frame( 2L ) )
4093     return error;
4094
4095   cpr->BacktrackGlyphCount = GET_UShort();
4096
4097   FORGET_Frame();
4098
4099   cpr->Backtrack = NULL;
4100
4101   count = cpr->BacktrackGlyphCount;
4102
4103   if ( ALLOC_ARRAY( cpr->Backtrack, count, HB_UShort ) )
4104     return error;
4105
4106   b = cpr->Backtrack;
4107
4108   if ( ACCESS_Frame( count * 2L ) )
4109     goto Fail4;
4110
4111   for ( n = 0; n < count; n++ )
4112     b[n] = GET_UShort();
4113
4114   FORGET_Frame();
4115
4116   if ( ACCESS_Frame( 2L ) )
4117     goto Fail4;
4118
4119   cpr->InputGlyphCount = GET_UShort();
4120
4121   FORGET_Frame();
4122
4123   cpr->Input = NULL;
4124
4125   count = cpr->InputGlyphCount - 1;  /* only InputGlyphCount - 1 elements */
4126
4127   if ( ALLOC_ARRAY( cpr->Input, count, HB_UShort ) )
4128     goto Fail4;
4129
4130   i = cpr->Input;
4131
4132   if ( ACCESS_Frame( count * 2L ) )
4133     goto Fail3;
4134
4135   for ( n = 0; n < count; n++ )
4136     i[n] = GET_UShort();
4137
4138   FORGET_Frame();
4139
4140   if ( ACCESS_Frame( 2L ) )
4141     goto Fail3;
4142
4143   cpr->LookaheadGlyphCount = GET_UShort();
4144
4145   FORGET_Frame();
4146
4147   cpr->Lookahead = NULL;
4148
4149   count = cpr->LookaheadGlyphCount;
4150
4151   if ( ALLOC_ARRAY( cpr->Lookahead, count, HB_UShort ) )
4152     goto Fail3;
4153
4154   l = cpr->Lookahead;
4155
4156   if ( ACCESS_Frame( count * 2L ) )
4157     goto Fail2;
4158
4159   for ( n = 0; n < count; n++ )
4160     l[n] = GET_UShort();
4161
4162   FORGET_Frame();
4163
4164   if ( ACCESS_Frame( 2L ) )
4165     goto Fail2;
4166
4167   cpr->PosCount = GET_UShort();
4168
4169   FORGET_Frame();
4170
4171   cpr->PosLookupRecord = NULL;
4172
4173   count = cpr->PosCount;
4174
4175   if ( ALLOC_ARRAY( cpr->PosLookupRecord, count, HB_PosLookupRecord ) )
4176     goto Fail2;
4177
4178   plr = cpr->PosLookupRecord;
4179
4180   if ( ACCESS_Frame( count * 4L ) )
4181     goto Fail1;
4182
4183   for ( n = 0; n < count; n++ )
4184   {
4185     plr[n].SequenceIndex   = GET_UShort();
4186     plr[n].LookupListIndex = GET_UShort();
4187   }
4188
4189   FORGET_Frame();
4190
4191   return HB_Err_Ok;
4192
4193 Fail1:
4194   FREE( plr );
4195
4196 Fail2:
4197   FREE( l );
4198
4199 Fail3:
4200   FREE( i );
4201
4202 Fail4:
4203   FREE( b );
4204   return error;
4205 }
4206
4207
4208 static void  Free_ChainPosRule( HB_ChainPosRule*  cpr )
4209 {
4210   FREE( cpr->PosLookupRecord );
4211   FREE( cpr->Lookahead );
4212   FREE( cpr->Input );
4213   FREE( cpr->Backtrack );
4214 }
4215
4216
4217 /* ChainPosRuleSet */
4218
4219 static HB_Error  Load_ChainPosRuleSet( HB_ChainPosRuleSet*  cprs,
4220                                        HB_Stream             stream )
4221 {
4222   HB_Error  error;
4223
4224   HB_UShort          n, m, count;
4225   HB_UInt           cur_offset, new_offset, base_offset;
4226
4227   HB_ChainPosRule*  cpr;
4228
4229
4230   base_offset = FILE_Pos();
4231
4232   if ( ACCESS_Frame( 2L ) )
4233     return error;
4234
4235   count = cprs->ChainPosRuleCount = GET_UShort();
4236
4237   FORGET_Frame();
4238
4239   cprs->ChainPosRule = NULL;
4240
4241   if ( ALLOC_ARRAY( cprs->ChainPosRule, count, HB_ChainPosRule ) )
4242     return error;
4243
4244   cpr = cprs->ChainPosRule;
4245
4246   for ( n = 0; n < count; n++ )
4247   {
4248     if ( ACCESS_Frame( 2L ) )
4249       goto Fail;
4250
4251     new_offset = GET_UShort() + base_offset;
4252
4253     FORGET_Frame();
4254
4255     cur_offset = FILE_Pos();
4256     if ( FILE_Seek( new_offset ) ||
4257          ( error = Load_ChainPosRule( &cpr[n], stream ) ) != HB_Err_Ok )
4258       goto Fail;
4259     (void)FILE_Seek( cur_offset );
4260   }
4261
4262   return HB_Err_Ok;
4263
4264 Fail:
4265   for ( m = 0; m < n; m++ )
4266     Free_ChainPosRule( &cpr[m] );
4267
4268   FREE( cpr );
4269   return error;
4270 }
4271
4272
4273 static void  Free_ChainPosRuleSet( HB_ChainPosRuleSet*  cprs )
4274 {
4275   HB_UShort          n, count;
4276
4277   HB_ChainPosRule*  cpr;
4278
4279
4280   if ( cprs->ChainPosRule )
4281   {
4282     count = cprs->ChainPosRuleCount;
4283     cpr   = cprs->ChainPosRule;
4284
4285     for ( n = 0; n < count; n++ )
4286       Free_ChainPosRule( &cpr[n] );
4287
4288     FREE( cpr );
4289   }
4290 }
4291
4292
4293 /* ChainContextPosFormat1 */
4294
4295 static HB_Error  Load_ChainContextPos1( HB_ChainContextPosFormat1*  ccpf1,
4296                                         HB_Stream                    stream )
4297 {
4298   HB_Error  error;
4299
4300   HB_UShort             n, m, count;
4301   HB_UInt              cur_offset, new_offset, base_offset;
4302
4303   HB_ChainPosRuleSet*  cprs;
4304
4305
4306   base_offset = FILE_Pos() - 2L;
4307
4308   if ( ACCESS_Frame( 2L ) )
4309     return error;
4310
4311   new_offset = GET_UShort() + base_offset;
4312
4313   FORGET_Frame();
4314
4315   cur_offset = FILE_Pos();
4316   if ( FILE_Seek( new_offset ) ||
4317        ( error = _HB_OPEN_Load_Coverage( &ccpf1->Coverage, stream ) ) != HB_Err_Ok )
4318     return error;
4319   (void)FILE_Seek( cur_offset );
4320
4321   if ( ACCESS_Frame( 2L ) )
4322     goto Fail2;
4323
4324   count = ccpf1->ChainPosRuleSetCount = GET_UShort();
4325
4326   FORGET_Frame();
4327
4328   ccpf1->ChainPosRuleSet = NULL;
4329
4330   if ( ALLOC_ARRAY( ccpf1->ChainPosRuleSet, count, HB_ChainPosRuleSet ) )
4331     goto Fail2;
4332
4333   cprs = ccpf1->ChainPosRuleSet;
4334
4335   for ( n = 0; n < count; n++ )
4336   {
4337     if ( ACCESS_Frame( 2L ) )
4338       goto Fail1;
4339
4340     new_offset = GET_UShort() + base_offset;
4341
4342     FORGET_Frame();
4343
4344     cur_offset = FILE_Pos();
4345     if ( FILE_Seek( new_offset ) ||
4346          ( error = Load_ChainPosRuleSet( &cprs[n], stream ) ) != HB_Err_Ok )
4347       goto Fail1;
4348     (void)FILE_Seek( cur_offset );
4349   }
4350
4351   return HB_Err_Ok;
4352
4353 Fail1:
4354   for ( m = 0; m < n; m++ )
4355     Free_ChainPosRuleSet( &cprs[m] );
4356
4357   FREE( cprs );
4358
4359 Fail2:
4360   _HB_OPEN_Free_Coverage( &ccpf1->Coverage );
4361   return error;
4362 }
4363
4364
4365 static void  Free_ChainContextPos1( HB_ChainContextPosFormat1*  ccpf1 )
4366 {
4367   HB_UShort             n, count;
4368
4369   HB_ChainPosRuleSet*  cprs;
4370
4371
4372   if ( ccpf1->ChainPosRuleSet )
4373   {
4374     count = ccpf1->ChainPosRuleSetCount;
4375     cprs  = ccpf1->ChainPosRuleSet;
4376
4377     for ( n = 0; n < count; n++ )
4378       Free_ChainPosRuleSet( &cprs[n] );
4379
4380     FREE( cprs );
4381   }
4382
4383   _HB_OPEN_Free_Coverage( &ccpf1->Coverage );
4384 }
4385
4386
4387 /* ChainPosClassRule */
4388
4389 static HB_Error  Load_ChainPosClassRule(
4390                    HB_ChainContextPosFormat2*  ccpf2,
4391                    HB_ChainPosClassRule*       cpcr,
4392                    HB_Stream                    stream )
4393 {
4394   HB_Error  error;
4395
4396   HB_UShort             n, count;
4397
4398   HB_UShort*            b;
4399   HB_UShort*            i;
4400   HB_UShort*            l;
4401   HB_PosLookupRecord*  plr;
4402   HB_Bool*              d;
4403
4404
4405   if ( ACCESS_Frame( 2L ) )
4406     return error;
4407
4408   cpcr->BacktrackGlyphCount = GET_UShort();
4409
4410   FORGET_Frame();
4411
4412   if ( cpcr->BacktrackGlyphCount > ccpf2->MaxBacktrackLength )
4413     ccpf2->MaxBacktrackLength = cpcr->BacktrackGlyphCount;
4414
4415   cpcr->Backtrack = NULL;
4416
4417   count = cpcr->BacktrackGlyphCount;
4418
4419   if ( ALLOC_ARRAY( cpcr->Backtrack, count, HB_UShort ) )
4420     return error;
4421
4422   b = cpcr->Backtrack;
4423   d = ccpf2->BacktrackClassDef.Defined;
4424
4425   if ( ACCESS_Frame( count * 2L ) )
4426     goto Fail4;
4427
4428   for ( n = 0; n < count; n++ )
4429   {
4430     b[n] = GET_UShort();
4431
4432     /* We check whether the specific class is used at all.  If not,
4433        class 0 is used instead.                                     */
4434
4435     if ( !d[b[n]] )
4436       b[n] = 0;
4437   }
4438
4439   FORGET_Frame();
4440
4441   if ( ACCESS_Frame( 2L ) )
4442     goto Fail4;
4443
4444   cpcr->InputGlyphCount = GET_UShort();
4445
4446   if ( cpcr->InputGlyphCount > ccpf2->MaxInputLength )
4447     ccpf2->MaxInputLength = cpcr->InputGlyphCount;
4448
4449   FORGET_Frame();
4450
4451   cpcr->Input = NULL;
4452
4453   count = cpcr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */
4454
4455   if ( ALLOC_ARRAY( cpcr->Input, count, HB_UShort ) )
4456     goto Fail4;
4457
4458   i = cpcr->Input;
4459   d = ccpf2->InputClassDef.Defined;
4460
4461   if ( ACCESS_Frame( count * 2L ) )
4462     goto Fail3;
4463
4464   for ( n = 0; n < count; n++ )
4465   {
4466     i[n] = GET_UShort();
4467
4468     if ( !d[i[n]] )
4469       i[n] = 0;
4470   }
4471
4472   FORGET_Frame();
4473
4474   if ( ACCESS_Frame( 2L ) )
4475     goto Fail3;
4476
4477   cpcr->LookaheadGlyphCount = GET_UShort();
4478
4479   FORGET_Frame();
4480
4481   if ( cpcr->LookaheadGlyphCount > ccpf2->MaxLookaheadLength )
4482     ccpf2->MaxLookaheadLength = cpcr->LookaheadGlyphCount;
4483
4484   cpcr->Lookahead = NULL;
4485
4486   count = cpcr->LookaheadGlyphCount;
4487
4488   if ( ALLOC_ARRAY( cpcr->Lookahead, count, HB_UShort ) )
4489     goto Fail3;
4490
4491   l = cpcr->Lookahead;
4492   d = ccpf2->LookaheadClassDef.Defined;
4493
4494   if ( ACCESS_Frame( count * 2L ) )
4495     goto Fail2;
4496
4497   for ( n = 0; n < count; n++ )
4498   {
4499     l[n] = GET_UShort();
4500
4501     if ( !d[l[n]] )
4502       l[n] = 0;
4503   }
4504
4505   FORGET_Frame();
4506
4507   if ( ACCESS_Frame( 2L ) )
4508     goto Fail2;
4509
4510   cpcr->PosCount = GET_UShort();
4511
4512   FORGET_Frame();
4513
4514   cpcr->PosLookupRecord = NULL;
4515
4516   count = cpcr->PosCount;
4517
4518   if ( ALLOC_ARRAY( cpcr->PosLookupRecord, count, HB_PosLookupRecord ) )
4519     goto Fail2;
4520
4521   plr = cpcr->PosLookupRecord;
4522
4523   if ( ACCESS_Frame( count * 4L ) )
4524     goto Fail1;
4525
4526   for ( n = 0; n < count; n++ )
4527   {
4528     plr[n].SequenceIndex   = GET_UShort();
4529     plr[n].LookupListIndex = GET_UShort();
4530   }
4531
4532   FORGET_Frame();
4533
4534   return HB_Err_Ok;
4535
4536 Fail1:
4537   FREE( plr );
4538
4539 Fail2:
4540   FREE( l );
4541
4542 Fail3:
4543   FREE( i );
4544
4545 Fail4:
4546   FREE( b );
4547   return error;
4548 }
4549
4550
4551 static void  Free_ChainPosClassRule( HB_ChainPosClassRule*  cpcr )
4552 {
4553   FREE( cpcr->PosLookupRecord );
4554   FREE( cpcr->Lookahead );
4555   FREE( cpcr->Input );
4556   FREE( cpcr->Backtrack );
4557 }
4558
4559
4560 /* PosClassSet */
4561
4562 static HB_Error  Load_ChainPosClassSet(
4563                    HB_ChainContextPosFormat2*  ccpf2,
4564                    HB_ChainPosClassSet*        cpcs,
4565                    HB_Stream                    stream )
4566 {
4567   HB_Error  error;
4568
4569   HB_UShort               n, m, count;
4570   HB_UInt                cur_offset, new_offset, base_offset;
4571
4572   HB_ChainPosClassRule*  cpcr;
4573
4574
4575   base_offset = FILE_Pos();
4576
4577   if ( ACCESS_Frame( 2L ) )
4578     return error;
4579
4580   count = cpcs->ChainPosClassRuleCount = GET_UShort();
4581
4582   FORGET_Frame();
4583
4584   cpcs->ChainPosClassRule = NULL;
4585
4586   if ( ALLOC_ARRAY( cpcs->ChainPosClassRule, count,
4587                     HB_ChainPosClassRule ) )
4588     return error;
4589
4590   cpcr = cpcs->ChainPosClassRule;
4591
4592   for ( n = 0; n < count; n++ )
4593   {
4594     if ( ACCESS_Frame( 2L ) )
4595       goto Fail;
4596
4597     new_offset = GET_UShort() + base_offset;
4598
4599     FORGET_Frame();
4600
4601     cur_offset = FILE_Pos();
4602     if ( FILE_Seek( new_offset ) ||
4603          ( error = Load_ChainPosClassRule( ccpf2, &cpcr[n],
4604                                            stream ) ) != HB_Err_Ok )
4605       goto Fail;
4606     (void)FILE_Seek( cur_offset );
4607   }
4608
4609   return HB_Err_Ok;
4610
4611 Fail:
4612   for ( m = 0; m < n; m++ )
4613     Free_ChainPosClassRule( &cpcr[m] );
4614
4615   FREE( cpcr );
4616   return error;
4617 }
4618
4619
4620 static void  Free_ChainPosClassSet( HB_ChainPosClassSet*  cpcs )
4621 {
4622   HB_UShort               n, count;
4623
4624   HB_ChainPosClassRule*  cpcr;
4625
4626
4627   if ( cpcs->ChainPosClassRule )
4628   {
4629     count = cpcs->ChainPosClassRuleCount;
4630     cpcr  = cpcs->ChainPosClassRule;
4631
4632     for ( n = 0; n < count; n++ )
4633       Free_ChainPosClassRule( &cpcr[n] );
4634
4635     FREE( cpcr );
4636   }
4637 }
4638
4639
4640 /* ChainContextPosFormat2 */
4641
4642 static HB_Error  Load_ChainContextPos2( HB_ChainContextPosFormat2*  ccpf2,
4643                                         HB_Stream                    stream )
4644 {
4645   HB_Error  error;
4646
4647   HB_UShort              n, m, count;
4648   HB_UInt               cur_offset, new_offset, base_offset;
4649   HB_UInt               backtrack_offset, input_offset, lookahead_offset;
4650
4651   HB_ChainPosClassSet*  cpcs;
4652
4653
4654   base_offset = FILE_Pos() - 2;
4655
4656   if ( ACCESS_Frame( 2L ) )
4657     return error;
4658
4659   new_offset = GET_UShort() + base_offset;
4660
4661   FORGET_Frame();
4662
4663   cur_offset = FILE_Pos();
4664   if ( FILE_Seek( new_offset ) ||
4665        ( error = _HB_OPEN_Load_Coverage( &ccpf2->Coverage, stream ) ) != HB_Err_Ok )
4666     return error;
4667   (void)FILE_Seek( cur_offset );
4668
4669   if ( ACCESS_Frame( 8L ) )
4670     goto Fail5;
4671
4672   backtrack_offset = GET_UShort();
4673   input_offset     = GET_UShort();
4674   lookahead_offset = GET_UShort();
4675
4676   /* `ChainPosClassSetCount' is the upper limit for input class values,
4677      thus we read it now to make an additional safety check. No limit
4678      is known or needed for the other two class definitions          */
4679
4680   count = ccpf2->ChainPosClassSetCount = GET_UShort();
4681
4682   FORGET_Frame();
4683
4684   if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccpf2->BacktrackClassDef, 65535,
4685                                                        backtrack_offset, base_offset,
4686                                                        stream ) ) != HB_Err_Ok )
4687     goto Fail5;
4688   if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccpf2->InputClassDef, count,
4689                                                        input_offset, base_offset,
4690                                                        stream ) ) != HB_Err_Ok )
4691     goto Fail4;
4692   if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccpf2->LookaheadClassDef, 65535,
4693                                                        lookahead_offset, base_offset,
4694                                                        stream ) ) != HB_Err_Ok )
4695     goto Fail3;
4696
4697   ccpf2->ChainPosClassSet   = NULL;
4698   ccpf2->MaxBacktrackLength = 0;
4699   ccpf2->MaxInputLength     = 0;
4700   ccpf2->MaxLookaheadLength = 0;
4701
4702   if ( ALLOC_ARRAY( ccpf2->ChainPosClassSet, count, HB_ChainPosClassSet ) )
4703     goto Fail2;
4704
4705   cpcs = ccpf2->ChainPosClassSet;
4706
4707   for ( n = 0; n < count; n++ )
4708   {
4709     if ( ACCESS_Frame( 2L ) )
4710       goto Fail1;
4711
4712     new_offset = GET_UShort() + base_offset;
4713
4714     FORGET_Frame();
4715
4716     if ( new_offset != base_offset )      /* not a NULL offset */
4717     {
4718       cur_offset = FILE_Pos();
4719       if ( FILE_Seek( new_offset ) ||
4720            ( error = Load_ChainPosClassSet( ccpf2, &cpcs[n],
4721                                             stream ) ) != HB_Err_Ok )
4722         goto Fail1;
4723       (void)FILE_Seek( cur_offset );
4724     }
4725     else
4726     {
4727       /* we create a ChainPosClassSet table with no entries */
4728
4729       ccpf2->ChainPosClassSet[n].ChainPosClassRuleCount = 0;
4730       ccpf2->ChainPosClassSet[n].ChainPosClassRule      = NULL;
4731     }
4732   }
4733
4734   return HB_Err_Ok;
4735
4736 Fail1:
4737   for ( m = 0; m < n; m++ )
4738     Free_ChainPosClassSet( &cpcs[m] );
4739
4740   FREE( cpcs );
4741
4742 Fail2:
4743   _HB_OPEN_Free_ClassDefinition( &ccpf2->LookaheadClassDef );
4744
4745 Fail3:
4746   _HB_OPEN_Free_ClassDefinition( &ccpf2->InputClassDef );
4747
4748 Fail4:
4749   _HB_OPEN_Free_ClassDefinition( &ccpf2->BacktrackClassDef );
4750
4751 Fail5:
4752   _HB_OPEN_Free_Coverage( &ccpf2->Coverage );
4753   return error;
4754 }
4755
4756
4757 static void  Free_ChainContextPos2( HB_ChainContextPosFormat2*  ccpf2 )
4758 {
4759   HB_UShort              n, count;
4760
4761   HB_ChainPosClassSet*  cpcs;
4762
4763
4764   if ( ccpf2->ChainPosClassSet )
4765   {
4766     count = ccpf2->ChainPosClassSetCount;
4767     cpcs  = ccpf2->ChainPosClassSet;
4768
4769     for ( n = 0; n < count; n++ )
4770       Free_ChainPosClassSet( &cpcs[n] );
4771
4772     FREE( cpcs );
4773   }
4774
4775   _HB_OPEN_Free_ClassDefinition( &ccpf2->LookaheadClassDef );
4776   _HB_OPEN_Free_ClassDefinition( &ccpf2->InputClassDef );
4777   _HB_OPEN_Free_ClassDefinition( &ccpf2->BacktrackClassDef );
4778
4779   _HB_OPEN_Free_Coverage( &ccpf2->Coverage );
4780 }
4781
4782
4783 /* ChainContextPosFormat3 */
4784
4785 static HB_Error  Load_ChainContextPos3( HB_ChainContextPosFormat3*  ccpf3,
4786                                         HB_Stream                    stream )
4787 {
4788   HB_Error  error;
4789
4790   HB_UShort             n, nb, ni, nl, m, count;
4791   HB_UShort             backtrack_count, input_count, lookahead_count;
4792   HB_UInt              cur_offset, new_offset, base_offset;
4793
4794   HB_Coverage*         b;
4795   HB_Coverage*         i;
4796   HB_Coverage*         l;
4797   HB_PosLookupRecord*  plr;
4798
4799
4800   base_offset = FILE_Pos() - 2L;
4801
4802   if ( ACCESS_Frame( 2L ) )
4803     return error;
4804
4805   ccpf3->BacktrackGlyphCount = GET_UShort();
4806
4807   FORGET_Frame();
4808
4809   ccpf3->BacktrackCoverage = NULL;
4810
4811   backtrack_count = ccpf3->BacktrackGlyphCount;
4812
4813   if ( ALLOC_ARRAY( ccpf3->BacktrackCoverage, backtrack_count,
4814                     HB_Coverage ) )
4815     return error;
4816
4817   b = ccpf3->BacktrackCoverage;
4818
4819   for ( nb = 0; nb < backtrack_count; nb++ )
4820   {
4821     if ( ACCESS_Frame( 2L ) )
4822       goto Fail4;
4823
4824     new_offset = GET_UShort() + base_offset;
4825
4826     FORGET_Frame();
4827
4828     cur_offset = FILE_Pos();
4829     if ( FILE_Seek( new_offset ) ||
4830          ( error = _HB_OPEN_Load_Coverage( &b[nb], stream ) ) != HB_Err_Ok )
4831       goto Fail4;
4832     (void)FILE_Seek( cur_offset );
4833   }
4834
4835   if ( ACCESS_Frame( 2L ) )
4836     goto Fail4;
4837
4838   ccpf3->InputGlyphCount = GET_UShort();
4839
4840   FORGET_Frame();
4841
4842   ccpf3->InputCoverage = NULL;
4843
4844   input_count = ccpf3->InputGlyphCount;
4845
4846   if ( ALLOC_ARRAY( ccpf3->InputCoverage, input_count, HB_Coverage ) )
4847     goto Fail4;
4848
4849   i = ccpf3->InputCoverage;
4850
4851   for ( ni = 0; ni < input_count; ni++ )
4852   {
4853     if ( ACCESS_Frame( 2L ) )
4854       goto Fail3;
4855
4856     new_offset = GET_UShort() + base_offset;
4857
4858     FORGET_Frame();
4859
4860     cur_offset = FILE_Pos();
4861     if ( FILE_Seek( new_offset ) ||
4862          ( error = _HB_OPEN_Load_Coverage( &i[ni], stream ) ) != HB_Err_Ok )
4863       goto Fail3;
4864     (void)FILE_Seek( cur_offset );
4865   }
4866
4867   if ( ACCESS_Frame( 2L ) )
4868     goto Fail3;
4869
4870   ccpf3->LookaheadGlyphCount = GET_UShort();
4871
4872   FORGET_Frame();
4873
4874   ccpf3->LookaheadCoverage = NULL;
4875
4876   lookahead_count = ccpf3->LookaheadGlyphCount;
4877
4878   if ( ALLOC_ARRAY( ccpf3->LookaheadCoverage, lookahead_count,
4879                     HB_Coverage ) )
4880     goto Fail3;
4881
4882   l = ccpf3->LookaheadCoverage;
4883
4884   for ( nl = 0; nl < lookahead_count; nl++ )
4885   {
4886     if ( ACCESS_Frame( 2L ) )
4887       goto Fail2;
4888
4889     new_offset = GET_UShort() + base_offset;
4890
4891     FORGET_Frame();
4892
4893     cur_offset = FILE_Pos();
4894     if ( FILE_Seek( new_offset ) ||
4895          ( error = _HB_OPEN_Load_Coverage( &l[nl], stream ) ) != HB_Err_Ok )
4896       goto Fail2;
4897     (void)FILE_Seek( cur_offset );
4898   }
4899
4900   if ( ACCESS_Frame( 2L ) )
4901     goto Fail2;
4902
4903   ccpf3->PosCount = GET_UShort();
4904
4905   FORGET_Frame();
4906
4907   ccpf3->PosLookupRecord = NULL;
4908
4909   count = ccpf3->PosCount;
4910
4911   if ( ALLOC_ARRAY( ccpf3->PosLookupRecord, count, HB_PosLookupRecord ) )
4912     goto Fail2;
4913
4914   plr = ccpf3->PosLookupRecord;
4915
4916   if ( ACCESS_Frame( count * 4L ) )
4917     goto Fail1;
4918
4919   for ( n = 0; n < count; n++ )
4920   {
4921     plr[n].SequenceIndex   = GET_UShort();
4922     plr[n].LookupListIndex = GET_UShort();
4923   }
4924
4925   FORGET_Frame();
4926
4927   return HB_Err_Ok;
4928
4929 Fail1:
4930   FREE( plr );
4931
4932 Fail2:
4933   for ( m = 0; m < nl; m++ )
4934     _HB_OPEN_Free_Coverage( &l[m] );
4935
4936   FREE( l );
4937
4938 Fail3:
4939   for ( m = 0; m < ni; m++ )
4940     _HB_OPEN_Free_Coverage( &i[m] );
4941
4942   FREE( i );
4943
4944 Fail4:
4945   for ( m = 0; m < nb; m++ )
4946     _HB_OPEN_Free_Coverage( &b[m] );
4947
4948   FREE( b );
4949   return error;
4950 }
4951
4952
4953 static void  Free_ChainContextPos3( HB_ChainContextPosFormat3*  ccpf3 )
4954 {
4955   HB_UShort      n, count;
4956
4957   HB_Coverage*  c;
4958
4959
4960   FREE( ccpf3->PosLookupRecord );
4961
4962   if ( ccpf3->LookaheadCoverage )
4963   {
4964     count = ccpf3->LookaheadGlyphCount;
4965     c     = ccpf3->LookaheadCoverage;
4966
4967     for ( n = 0; n < count; n++ )
4968       _HB_OPEN_Free_Coverage( &c[n] );
4969
4970     FREE( c );
4971   }
4972
4973   if ( ccpf3->InputCoverage )
4974   {
4975     count = ccpf3->InputGlyphCount;
4976     c     = ccpf3->InputCoverage;
4977
4978     for ( n = 0; n < count; n++ )
4979       _HB_OPEN_Free_Coverage( &c[n] );
4980
4981     FREE( c );
4982   }
4983
4984   if ( ccpf3->BacktrackCoverage )
4985   {
4986     count = ccpf3->BacktrackGlyphCount;
4987     c     = ccpf3->BacktrackCoverage;
4988
4989     for ( n = 0; n < count; n++ )
4990       _HB_OPEN_Free_Coverage( &c[n] );
4991
4992     FREE( c );
4993   }
4994 }
4995
4996
4997 /* ChainContextPos */
4998
4999 static HB_Error  Load_ChainContextPos( HB_GPOS_SubTable* st,
5000                                        HB_Stream             stream )
5001 {
5002   HB_Error  error;
5003   HB_ChainContextPos*  ccp = &st->chain;
5004
5005
5006   if ( ACCESS_Frame( 2L ) )
5007     return error;
5008
5009   ccp->PosFormat = GET_UShort();
5010
5011   FORGET_Frame();
5012
5013   switch ( ccp->PosFormat )
5014   {
5015   case 1:
5016     return Load_ChainContextPos1( &ccp->ccpf.ccpf1, stream );
5017
5018   case 2:
5019     return Load_ChainContextPos2( &ccp->ccpf.ccpf2, stream );
5020
5021   case 3:
5022     return Load_ChainContextPos3( &ccp->ccpf.ccpf3, stream );
5023
5024   default:
5025     return ERR(HB_Err_Invalid_SubTable_Format);
5026   }
5027
5028   return HB_Err_Ok;               /* never reached */
5029 }
5030
5031
5032 static void  Free_ChainContextPos( HB_GPOS_SubTable* st )
5033 {
5034   HB_ChainContextPos*  ccp = &st->chain;
5035
5036   switch ( ccp->PosFormat )
5037   {
5038   case 1:  Free_ChainContextPos1( &ccp->ccpf.ccpf1 ); break;
5039   case 2:  Free_ChainContextPos2( &ccp->ccpf.ccpf2 ); break;
5040   case 3:  Free_ChainContextPos3( &ccp->ccpf.ccpf3 ); break;
5041   default:                                                    break;
5042   }
5043 }
5044
5045
5046 static HB_Error  Lookup_ChainContextPos1(
5047                    GPOS_Instance*               gpi,
5048                    HB_ChainContextPosFormat1*  ccpf1,
5049                    HB_Buffer                   buffer,
5050                    HB_UShort                    flags,
5051                    HB_UShort                    context_length,
5052                    int                          nesting_level )
5053 {
5054   HB_UShort          index, property;
5055   HB_UShort          i, j, k, num_cpr;
5056   HB_UShort          bgc, igc, lgc;
5057   HB_Error           error;
5058   HB_GPOSHeader*    gpos = gpi->gpos;
5059
5060   HB_ChainPosRule*  cpr;
5061   HB_ChainPosRule   curr_cpr;
5062   HB_GDEFHeader*    gdef;
5063
5064
5065   gdef = gpos->gdef;
5066
5067   if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
5068     return error;
5069
5070   error = _HB_OPEN_Coverage_Index( &ccpf1->Coverage, IN_CURGLYPH(), &index );
5071   if ( error )
5072     return error;
5073
5074   cpr     = ccpf1->ChainPosRuleSet[index].ChainPosRule;
5075   num_cpr = ccpf1->ChainPosRuleSet[index].ChainPosRuleCount;
5076
5077   for ( k = 0; k < num_cpr; k++ )
5078   {
5079     curr_cpr = cpr[k];
5080     bgc      = curr_cpr.BacktrackGlyphCount;
5081     igc      = curr_cpr.InputGlyphCount;
5082     lgc      = curr_cpr.LookaheadGlyphCount;
5083
5084     if ( context_length != 0xFFFF && context_length < igc )
5085       goto next_chainposrule;
5086
5087     /* check whether context is too long; it is a first guess only */
5088
5089     if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
5090       goto next_chainposrule;
5091
5092     if ( bgc )
5093     {
5094       /* Since we don't know in advance the number of glyphs to inspect,
5095          we search backwards for matches in the backtrack glyph array    */
5096
5097       for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
5098       {
5099         while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
5100         {
5101           if ( error && error != HB_Err_Not_Covered )
5102             return error;
5103
5104           if ( j + 1 == bgc - i )
5105             goto next_chainposrule;
5106           j--;
5107         }
5108
5109         /* In OpenType 1.3, it is undefined whether the offsets of
5110            backtrack glyphs is in logical order or not.  Version 1.4
5111            will clarify this:
5112
5113              Logical order -      a  b  c  d  e  f  g  h  i  j
5114                                               i
5115              Input offsets -                  0  1
5116              Backtrack offsets -  3  2  1  0
5117              Lookahead offsets -                    0  1  2  3           */
5118
5119         if ( IN_GLYPH( j ) != curr_cpr.Backtrack[i] )
5120           goto next_chainposrule;
5121       }
5122     }
5123
5124     /* Start at 1 because [0] is implied */
5125
5126     for ( i = 1, j = buffer->in_pos + 1; i < igc; i++, j++ )
5127     {
5128       while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
5129       {
5130         if ( error && error != HB_Err_Not_Covered )
5131           return error;
5132
5133         if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
5134           goto next_chainposrule;
5135         j++;
5136       }
5137
5138       if ( IN_GLYPH( j ) != curr_cpr.Input[i - 1] )
5139         goto next_chainposrule;
5140     }
5141
5142     /* we are starting to check for lookahead glyphs right after the
5143        last context glyph                                            */
5144
5145     for ( i = 0; i < lgc; i++, j++ )
5146     {
5147       while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
5148       {
5149         if ( error && error != HB_Err_Not_Covered )
5150           return error;
5151
5152         if ( j + lgc - i == (HB_Int)buffer->in_length )
5153           goto next_chainposrule;
5154         j++;
5155       }
5156
5157       if ( IN_GLYPH( j ) != curr_cpr.Lookahead[i] )
5158         goto next_chainposrule;
5159     }
5160
5161     return Do_ContextPos( gpi, igc,
5162                           curr_cpr.PosCount,
5163                           curr_cpr.PosLookupRecord,
5164                           buffer,
5165                           nesting_level );
5166
5167   next_chainposrule:
5168     ;
5169   }
5170
5171   return HB_Err_Not_Covered;
5172 }
5173
5174
5175 static HB_Error  Lookup_ChainContextPos2(
5176                    GPOS_Instance*               gpi,
5177                    HB_ChainContextPosFormat2*  ccpf2,
5178                    HB_Buffer                   buffer,
5179                    HB_UShort                    flags,
5180                    HB_UShort                    context_length,
5181                    int                          nesting_level )
5182 {
5183   HB_UShort              index, property;
5184   HB_Error               error;
5185   HB_UShort              i, j, k;
5186   HB_UShort              bgc, igc, lgc;
5187   HB_UShort              known_backtrack_classes,
5188                          known_input_classes,
5189                          known_lookahead_classes;
5190
5191   HB_UShort*             backtrack_classes;
5192   HB_UShort*             input_classes;
5193   HB_UShort*             lookahead_classes;
5194
5195   HB_UShort*             bc;
5196   HB_UShort*             ic;
5197   HB_UShort*             lc;
5198   HB_GPOSHeader*        gpos = gpi->gpos;
5199
5200   HB_ChainPosClassSet*  cpcs;
5201   HB_ChainPosClassRule  cpcr;
5202   HB_GDEFHeader*        gdef;
5203
5204
5205   gdef = gpos->gdef;
5206
5207   if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
5208     return error;
5209
5210   /* Note: The coverage table in format 2 doesn't give an index into
5211            anything.  It just lets us know whether or not we need to
5212            do any lookup at all.                                     */
5213
5214   error = _HB_OPEN_Coverage_Index( &ccpf2->Coverage, IN_CURGLYPH(), &index );
5215   if ( error )
5216     return error;
5217
5218   if ( ALLOC_ARRAY( backtrack_classes, ccpf2->MaxBacktrackLength, HB_UShort ) )
5219     return error;
5220   known_backtrack_classes = 0;
5221
5222   if ( ALLOC_ARRAY( input_classes, ccpf2->MaxInputLength, HB_UShort ) )
5223     goto End3;
5224   known_input_classes = 1;
5225
5226   if ( ALLOC_ARRAY( lookahead_classes, ccpf2->MaxLookaheadLength, HB_UShort ) )
5227     goto End2;
5228   known_lookahead_classes = 0;
5229
5230   error = _HB_OPEN_Get_Class( &ccpf2->InputClassDef, IN_CURGLYPH(),
5231                      &input_classes[0], NULL );
5232   if ( error && error != HB_Err_Not_Covered )
5233     goto End1;
5234
5235   cpcs = &ccpf2->ChainPosClassSet[input_classes[0]];
5236   if ( !cpcs )
5237   {
5238     error = ERR(HB_Err_Invalid_SubTable);
5239     goto End1;
5240   }
5241
5242   for ( k = 0; k < cpcs->ChainPosClassRuleCount; k++ )
5243   {
5244     cpcr = cpcs->ChainPosClassRule[k];
5245     bgc  = cpcr.BacktrackGlyphCount;
5246     igc  = cpcr.InputGlyphCount;
5247     lgc  = cpcr.LookaheadGlyphCount;
5248
5249     if ( context_length != 0xFFFF && context_length < igc )
5250       goto next_chainposclassrule;
5251
5252     /* check whether context is too long; it is a first guess only */
5253
5254     if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
5255       goto next_chainposclassrule;
5256
5257     if ( bgc )
5258     {
5259       /* Since we don't know in advance the number of glyphs to inspect,
5260          we search backwards for matches in the backtrack glyph array.
5261          Note that `known_backtrack_classes' starts at index 0.         */
5262
5263       bc       = cpcr.Backtrack;
5264
5265       for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
5266       {
5267         while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
5268         {
5269           if ( error && error != HB_Err_Not_Covered )
5270             goto End1;
5271
5272           if ( j + 1 == bgc - i )
5273             goto next_chainposclassrule;
5274           j++;
5275         }
5276
5277         if ( i >= known_backtrack_classes )
5278         {
5279           /* Keeps us from having to do this for each rule */
5280
5281           error = _HB_OPEN_Get_Class( &ccpf2->BacktrackClassDef, IN_GLYPH( j ),
5282                              &backtrack_classes[i], NULL );
5283           if ( error && error != HB_Err_Not_Covered )
5284             goto End1;
5285           known_backtrack_classes = i;
5286         }
5287
5288         if ( bc[i] != backtrack_classes[i] )
5289           goto next_chainposclassrule;
5290       }
5291     }
5292
5293     ic       = cpcr.Input;
5294
5295     /* Start at 1 because [0] is implied */
5296
5297     for ( i = 1, j = buffer->in_pos + 1; i < igc; i++, j++ )
5298     {
5299       while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
5300       {
5301         if ( error && error != HB_Err_Not_Covered )
5302           goto End1;
5303
5304         if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
5305           goto next_chainposclassrule;
5306         j++;
5307       }
5308
5309       if ( i >= known_input_classes )
5310       {
5311         error = _HB_OPEN_Get_Class( &ccpf2->InputClassDef, IN_GLYPH( j ),
5312                            &input_classes[i], NULL );
5313         if ( error && error != HB_Err_Not_Covered )
5314           goto End1;
5315         known_input_classes = i;
5316       }
5317
5318       if ( ic[i - 1] != input_classes[i] )
5319         goto next_chainposclassrule;
5320     }
5321
5322     /* we are starting to check for lookahead glyphs right after the
5323        last context glyph                                            */
5324
5325     lc       = cpcr.Lookahead;
5326
5327     for ( i = 0; i < lgc; i++, j++ )
5328     {
5329       while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
5330       {
5331         if ( error && error != HB_Err_Not_Covered )
5332           goto End1;
5333
5334         if ( j + lgc - i == (HB_Int)buffer->in_length )
5335           goto next_chainposclassrule;
5336         j++;
5337       }
5338
5339       if ( i >= known_lookahead_classes )
5340       {
5341         error = _HB_OPEN_Get_Class( &ccpf2->LookaheadClassDef, IN_GLYPH( j ),
5342                            &lookahead_classes[i], NULL );
5343         if ( error && error != HB_Err_Not_Covered )
5344           goto End1;
5345         known_lookahead_classes = i;
5346       }
5347
5348       if ( lc[i] != lookahead_classes[i] )
5349         goto next_chainposclassrule;
5350     }
5351
5352     error = Do_ContextPos( gpi, igc,
5353                            cpcr.PosCount,
5354                            cpcr.PosLookupRecord,
5355                            buffer,
5356                            nesting_level );
5357     goto End1;
5358
5359   next_chainposclassrule:
5360     ;
5361   }
5362
5363   error = HB_Err_Not_Covered;
5364
5365 End1:
5366   FREE( lookahead_classes );
5367
5368 End2:
5369   FREE( input_classes );
5370
5371 End3:
5372   FREE( backtrack_classes );
5373   return error;
5374 }
5375
5376
5377 static HB_Error  Lookup_ChainContextPos3(
5378                    GPOS_Instance*               gpi,
5379                    HB_ChainContextPosFormat3*  ccpf3,
5380                    HB_Buffer                   buffer,
5381                    HB_UShort                    flags,
5382                    HB_UShort                    context_length,
5383                    int                          nesting_level )
5384 {
5385   HB_UShort        index, i, j, property;
5386   HB_UShort        bgc, igc, lgc;
5387   HB_Error         error;
5388   HB_GPOSHeader*  gpos = gpi->gpos;
5389
5390   HB_Coverage*    bc;
5391   HB_Coverage*    ic;
5392   HB_Coverage*    lc;
5393   HB_GDEFHeader*  gdef;
5394
5395
5396   gdef = gpos->gdef;
5397
5398   if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
5399     return error;
5400
5401   bgc = ccpf3->BacktrackGlyphCount;
5402   igc = ccpf3->InputGlyphCount;
5403   lgc = ccpf3->LookaheadGlyphCount;
5404
5405   if ( context_length != 0xFFFF && context_length < igc )
5406     return HB_Err_Not_Covered;
5407
5408   /* check whether context is too long; it is a first guess only */
5409
5410   if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
5411     return HB_Err_Not_Covered;
5412
5413   if ( bgc )
5414   {
5415     /* Since we don't know in advance the number of glyphs to inspect,
5416        we search backwards for matches in the backtrack glyph array    */
5417
5418     bc       = ccpf3->BacktrackCoverage;
5419
5420     for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
5421     {
5422       while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
5423       {
5424         if ( error && error != HB_Err_Not_Covered )
5425           return error;
5426
5427         if ( j + 1 == bgc - i )
5428           return HB_Err_Not_Covered;
5429         j--;
5430       }
5431
5432       error = _HB_OPEN_Coverage_Index( &bc[i], IN_GLYPH( j ), &index );
5433       if ( error )
5434         return error;
5435     }
5436   }
5437
5438   ic       = ccpf3->InputCoverage;
5439
5440   for ( i = 0, j = buffer->in_pos; i < igc; i++, j++ )
5441   {
5442     /* We already called CHECK_Property for IN_GLYPH ( buffer->in_pos ) */
5443     while ( j > buffer->in_pos && CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
5444     {
5445       if ( error && error != HB_Err_Not_Covered )
5446         return error;
5447
5448       if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
5449         return HB_Err_Not_Covered;
5450       j++;
5451     }
5452
5453     error = _HB_OPEN_Coverage_Index( &ic[i], IN_GLYPH( j ), &index );
5454     if ( error )
5455       return error;
5456   }
5457
5458   /* we are starting to check for lookahead glyphs right after the
5459      last context glyph                                            */
5460
5461   lc       = ccpf3->LookaheadCoverage;
5462
5463   for ( i = 0; i < lgc; i++, j++ )
5464   {
5465     while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
5466     {
5467       if ( error && error != HB_Err_Not_Covered )
5468         return error;
5469
5470       if ( j + lgc - i == (HB_Int)buffer->in_length )
5471         return HB_Err_Not_Covered;
5472       j++;
5473     }
5474
5475     error = _HB_OPEN_Coverage_Index( &lc[i], IN_GLYPH( j ), &index );
5476     if ( error )
5477       return error;
5478   }
5479
5480   return Do_ContextPos( gpi, igc,
5481                         ccpf3->PosCount,
5482                         ccpf3->PosLookupRecord,
5483                         buffer,
5484                         nesting_level );
5485 }
5486
5487
5488 static HB_Error  Lookup_ChainContextPos(
5489                    GPOS_Instance*        gpi,
5490                    HB_GPOS_SubTable* st,
5491                    HB_Buffer            buffer,
5492                    HB_UShort             flags,
5493                    HB_UShort             context_length,
5494                    int                   nesting_level )
5495 {
5496   HB_ChainContextPos*  ccp = &st->chain;
5497
5498   switch ( ccp->PosFormat )
5499   {
5500   case 1:
5501     return Lookup_ChainContextPos1( gpi, &ccp->ccpf.ccpf1, buffer,
5502                                     flags, context_length,
5503                                     nesting_level );
5504
5505   case 2:
5506     return Lookup_ChainContextPos2( gpi, &ccp->ccpf.ccpf2, buffer,
5507                                     flags, context_length,
5508                                     nesting_level );
5509
5510   case 3:
5511     return Lookup_ChainContextPos3( gpi, &ccp->ccpf.ccpf3, buffer,
5512                                     flags, context_length,
5513                                     nesting_level );
5514
5515   default:
5516     return ERR(HB_Err_Invalid_SubTable_Format);
5517   }
5518
5519   return HB_Err_Ok;               /* never reached */
5520 }
5521
5522
5523
5524 /***********
5525  * GPOS API
5526  ***********/
5527
5528
5529
5530 HB_Error  HB_GPOS_Select_Script( HB_GPOSHeader*  gpos,
5531                                  HB_UInt         script_tag,
5532                                  HB_UShort*       script_index )
5533 {
5534   HB_UShort          n;
5535
5536   HB_ScriptList*    sl;
5537   HB_ScriptRecord*  sr;
5538
5539
5540   if ( !gpos || !script_index )
5541     return ERR(HB_Err_Invalid_Argument);
5542
5543   sl = &gpos->ScriptList;
5544   sr = sl->ScriptRecord;
5545
5546   for ( n = 0; n < sl->ScriptCount; n++ )
5547     if ( script_tag == sr[n].ScriptTag )
5548     {
5549       *script_index = n;
5550
5551       return HB_Err_Ok;
5552     }
5553
5554   return HB_Err_Not_Covered;
5555 }
5556
5557
5558
5559 HB_Error  HB_GPOS_Select_Language( HB_GPOSHeader*  gpos,
5560                                    HB_UInt         language_tag,
5561                                    HB_UShort        script_index,
5562                                    HB_UShort*       language_index,
5563                                    HB_UShort*       req_feature_index )
5564 {
5565   HB_UShort           n;
5566
5567   HB_ScriptList*     sl;
5568   HB_ScriptRecord*   sr;
5569   HB_ScriptTable*         s;
5570   HB_LangSysRecord*  lsr;
5571
5572
5573   if ( !gpos || !language_index || !req_feature_index )
5574     return ERR(HB_Err_Invalid_Argument);
5575
5576   sl = &gpos->ScriptList;
5577   sr = sl->ScriptRecord;
5578
5579   if ( script_index >= sl->ScriptCount )
5580     return ERR(HB_Err_Invalid_Argument);
5581
5582   s   = &sr[script_index].Script;
5583   lsr = s->LangSysRecord;
5584
5585   for ( n = 0; n < s->LangSysCount; n++ )
5586     if ( language_tag == lsr[n].LangSysTag )
5587     {
5588       *language_index = n;
5589       *req_feature_index = lsr[n].LangSys.ReqFeatureIndex;
5590
5591       return HB_Err_Ok;
5592     }
5593
5594   return HB_Err_Not_Covered;
5595 }
5596
5597
5598 /* selecting 0xFFFF for language_index asks for the values of the
5599    default language (DefaultLangSys)                              */
5600
5601
5602 HB_Error  HB_GPOS_Select_Feature( HB_GPOSHeader*  gpos,
5603                                   HB_UInt         feature_tag,
5604                                   HB_UShort        script_index,
5605                                   HB_UShort        language_index,
5606                                   HB_UShort*       feature_index )
5607 {
5608   HB_UShort           n;
5609
5610   HB_ScriptList*     sl;
5611   HB_ScriptRecord*   sr;
5612   HB_ScriptTable*         s;
5613   HB_LangSysRecord*  lsr;
5614   HB_LangSys*        ls;
5615   HB_UShort*          fi;
5616
5617   HB_FeatureList*    fl;
5618   HB_FeatureRecord*  fr;
5619
5620
5621   if ( !gpos || !feature_index )
5622     return ERR(HB_Err_Invalid_Argument);
5623
5624   sl = &gpos->ScriptList;
5625   sr = sl->ScriptRecord;
5626
5627   fl = &gpos->FeatureList;
5628   fr = fl->FeatureRecord;
5629
5630   if ( script_index >= sl->ScriptCount )
5631     return ERR(HB_Err_Invalid_Argument);
5632
5633   s   = &sr[script_index].Script;
5634   lsr = s->LangSysRecord;
5635
5636   if ( language_index == 0xFFFF )
5637     ls = &s->DefaultLangSys;
5638   else
5639   {
5640     if ( language_index >= s->LangSysCount )
5641       return ERR(HB_Err_Invalid_Argument);
5642
5643     ls = &lsr[language_index].LangSys;
5644   }
5645
5646   fi = ls->FeatureIndex;
5647
5648   for ( n = 0; n < ls->FeatureCount; n++ )
5649   {
5650     if ( fi[n] >= fl->FeatureCount )
5651       return ERR(HB_Err_Invalid_SubTable_Format);
5652
5653     if ( feature_tag == fr[fi[n]].FeatureTag )
5654     {
5655       *feature_index = fi[n];
5656
5657       return HB_Err_Ok;
5658     }
5659   }
5660
5661   return HB_Err_Not_Covered;
5662 }
5663
5664
5665 /* The next three functions return a null-terminated list */
5666
5667
5668 HB_Error  HB_GPOS_Query_Scripts( HB_GPOSHeader*  gpos,
5669                                  HB_UInt**       script_tag_list )
5670 {
5671   HB_Error           error;
5672   HB_UShort          n;
5673   HB_UInt*          stl;
5674
5675   HB_ScriptList*    sl;
5676   HB_ScriptRecord*  sr;
5677
5678
5679   if ( !gpos || !script_tag_list )
5680     return ERR(HB_Err_Invalid_Argument);
5681
5682   sl = &gpos->ScriptList;
5683   sr = sl->ScriptRecord;
5684
5685   if ( ALLOC_ARRAY( stl, sl->ScriptCount + 1, HB_UInt ) )
5686     return error;
5687
5688   for ( n = 0; n < sl->ScriptCount; n++ )
5689     stl[n] = sr[n].ScriptTag;
5690   stl[n] = 0;
5691
5692   *script_tag_list = stl;
5693
5694   return HB_Err_Ok;
5695 }
5696
5697
5698
5699 HB_Error  HB_GPOS_Query_Languages( HB_GPOSHeader*  gpos,
5700                                    HB_UShort        script_index,
5701                                    HB_UInt**       language_tag_list )
5702 {
5703   HB_Error            error;
5704   HB_UShort           n;
5705   HB_UInt*           ltl;
5706
5707   HB_ScriptList*     sl;
5708   HB_ScriptRecord*   sr;
5709   HB_ScriptTable*         s;
5710   HB_LangSysRecord*  lsr;
5711
5712
5713   if ( !gpos || !language_tag_list )
5714     return ERR(HB_Err_Invalid_Argument);
5715
5716   sl = &gpos->ScriptList;
5717   sr = sl->ScriptRecord;
5718
5719   if ( script_index >= sl->ScriptCount )
5720     return ERR(HB_Err_Invalid_Argument);
5721
5722   s   = &sr[script_index].Script;
5723   lsr = s->LangSysRecord;
5724
5725   if ( ALLOC_ARRAY( ltl, s->LangSysCount + 1, HB_UInt ) )
5726     return error;
5727
5728   for ( n = 0; n < s->LangSysCount; n++ )
5729     ltl[n] = lsr[n].LangSysTag;
5730   ltl[n] = 0;
5731
5732   *language_tag_list = ltl;
5733
5734   return HB_Err_Ok;
5735 }
5736
5737
5738 /* selecting 0xFFFF for language_index asks for the values of the
5739    default language (DefaultLangSys)                              */
5740
5741
5742 HB_Error  HB_GPOS_Query_Features( HB_GPOSHeader*  gpos,
5743                                   HB_UShort        script_index,
5744                                   HB_UShort        language_index,
5745                                   HB_UInt**       feature_tag_list )
5746 {
5747   HB_UShort           n;
5748   HB_Error            error;
5749   HB_UInt*           ftl;
5750
5751   HB_ScriptList*     sl;
5752   HB_ScriptRecord*   sr;
5753   HB_ScriptTable*         s;
5754   HB_LangSysRecord*  lsr;
5755   HB_LangSys*        ls;
5756   HB_UShort*          fi;
5757
5758   HB_FeatureList*    fl;
5759   HB_FeatureRecord*  fr;
5760
5761
5762   if ( !gpos || !feature_tag_list )
5763     return ERR(HB_Err_Invalid_Argument);
5764
5765   sl = &gpos->ScriptList;
5766   sr = sl->ScriptRecord;
5767
5768   fl = &gpos->FeatureList;
5769   fr = fl->FeatureRecord;
5770
5771   if ( script_index >= sl->ScriptCount )
5772     return ERR(HB_Err_Invalid_Argument);
5773
5774   s   = &sr[script_index].Script;
5775   lsr = s->LangSysRecord;
5776
5777   if ( language_index == 0xFFFF )
5778     ls = &s->DefaultLangSys;
5779   else
5780   {
5781     if ( language_index >= s->LangSysCount )
5782       return ERR(HB_Err_Invalid_Argument);
5783
5784     ls = &lsr[language_index].LangSys;
5785   }
5786
5787   fi = ls->FeatureIndex;
5788
5789   if ( ALLOC_ARRAY( ftl, ls->FeatureCount + 1, HB_UInt ) )
5790     return error;
5791
5792   for ( n = 0; n < ls->FeatureCount; n++ )
5793   {
5794     if ( fi[n] >= fl->FeatureCount )
5795     {
5796       FREE( ftl );
5797       return ERR(HB_Err_Invalid_SubTable_Format);
5798     }
5799     ftl[n] = fr[fi[n]].FeatureTag;
5800   }
5801   ftl[n] = 0;
5802
5803   *feature_tag_list = ftl;
5804
5805   return HB_Err_Ok;
5806 }
5807
5808
5809 /* Do an individual subtable lookup.  Returns HB_Err_Ok if positioning
5810    has been done, or HB_Err_Not_Covered if not.                        */
5811 static HB_Error  GPOS_Do_Glyph_Lookup( GPOS_Instance*    gpi,
5812                                        HB_UShort         lookup_index,
5813                                        HB_Buffer        buffer,
5814                                        HB_UShort         context_length,
5815                                        int               nesting_level )
5816 {
5817   HB_Error             error = HB_Err_Not_Covered;
5818   HB_UShort            i, flags, lookup_count;
5819   HB_GPOSHeader*       gpos = gpi->gpos;
5820   HB_Lookup*           lo;
5821   int                  lookup_type;
5822
5823
5824   nesting_level++;
5825
5826   if ( nesting_level > HB_MAX_NESTING_LEVEL )
5827     return ERR(HB_Err_Not_Covered); /* ERR() call intended */
5828
5829   lookup_count = gpos->LookupList.LookupCount;
5830   if (lookup_index >= lookup_count)
5831     return error;
5832
5833   lo    = &gpos->LookupList.Lookup[lookup_index];
5834   flags = lo->LookupFlag;
5835   lookup_type = lo->LookupType;
5836
5837   for ( i = 0; i < lo->SubTableCount; i++ )
5838   {
5839     HB_GPOS_SubTable *st = &lo->SubTable[i].st.gpos;
5840
5841     switch (lookup_type) {
5842       case HB_GPOS_LOOKUP_SINGLE:
5843         error = Lookup_SinglePos        ( gpi, st, buffer, flags, context_length, nesting_level ); break;
5844       case HB_GPOS_LOOKUP_PAIR:
5845         error = Lookup_PairPos          ( gpi, st, buffer, flags, context_length, nesting_level ); break;
5846       case HB_GPOS_LOOKUP_CURSIVE:
5847         error = Lookup_CursivePos       ( gpi, st, buffer, flags, context_length, nesting_level ); break;
5848       case HB_GPOS_LOOKUP_MARKBASE:
5849         error = Lookup_MarkBasePos      ( gpi, st, buffer, flags, context_length, nesting_level ); break;
5850       case HB_GPOS_LOOKUP_MARKLIG:
5851         error = Lookup_MarkLigPos       ( gpi, st, buffer, flags, context_length, nesting_level ); break;
5852       case HB_GPOS_LOOKUP_MARKMARK:
5853         error = Lookup_MarkMarkPos      ( gpi, st, buffer, flags, context_length, nesting_level ); break;
5854       case HB_GPOS_LOOKUP_CONTEXT:
5855         error = Lookup_ContextPos       ( gpi, st, buffer, flags, context_length, nesting_level ); break;
5856       case HB_GPOS_LOOKUP_CHAIN:
5857         error = Lookup_ChainContextPos  ( gpi, st, buffer, flags, context_length, nesting_level ); break;
5858     /*case HB_GPOS_LOOKUP_EXTENSION:
5859         error = Lookup_ExtensionPos     ( gpi, st, buffer, flags, context_length, nesting_level ); break;*/
5860       default:
5861         error = HB_Err_Not_Covered;
5862     }
5863
5864     /* Check whether we have a successful positioning or an error other
5865        than HB_Err_Not_Covered                                         */
5866     if ( error != HB_Err_Not_Covered )
5867       return error;
5868   }
5869
5870   return HB_Err_Not_Covered;
5871 }
5872
5873
5874 HB_INTERNAL HB_Error
5875 _HB_GPOS_Load_SubTable( HB_GPOS_SubTable* st,
5876                         HB_Stream         stream,
5877                         HB_UShort         lookup_type )
5878 {
5879   switch ( lookup_type ) {
5880     case HB_GPOS_LOOKUP_SINGLE:         return Load_SinglePos           ( st, stream );
5881     case HB_GPOS_LOOKUP_PAIR:           return Load_PairPos             ( st, stream );
5882     case HB_GPOS_LOOKUP_CURSIVE:        return Load_CursivePos          ( st, stream );
5883     case HB_GPOS_LOOKUP_MARKBASE:       return Load_MarkBasePos         ( st, stream );
5884     case HB_GPOS_LOOKUP_MARKLIG:        return Load_MarkLigPos          ( st, stream );
5885     case HB_GPOS_LOOKUP_MARKMARK:       return Load_MarkMarkPos         ( st, stream );
5886     case HB_GPOS_LOOKUP_CONTEXT:        return Load_ContextPos          ( st, stream );
5887     case HB_GPOS_LOOKUP_CHAIN:          return Load_ChainContextPos     ( st, stream );
5888   /*case HB_GPOS_LOOKUP_EXTENSION:      return Load_ExtensionPos        ( st, stream );*/
5889     default:                            return ERR(HB_Err_Invalid_SubTable_Format);
5890   }
5891 }
5892
5893
5894 HB_INTERNAL void
5895 _HB_GPOS_Free_SubTable( HB_GPOS_SubTable* st,
5896                         HB_UShort         lookup_type )
5897 {
5898   switch ( lookup_type ) {
5899     case HB_GPOS_LOOKUP_SINGLE:         Free_SinglePos          ( st ); return;
5900     case HB_GPOS_LOOKUP_PAIR:           Free_PairPos            ( st ); return;
5901     case HB_GPOS_LOOKUP_CURSIVE:        Free_CursivePos         ( st ); return;
5902     case HB_GPOS_LOOKUP_MARKBASE:       Free_MarkBasePos        ( st ); return;
5903     case HB_GPOS_LOOKUP_MARKLIG:        Free_MarkLigPos         ( st ); return;
5904     case HB_GPOS_LOOKUP_MARKMARK:       Free_MarkMarkPos        ( st ); return;
5905     case HB_GPOS_LOOKUP_CONTEXT:        Free_ContextPos         ( st ); return;
5906     case HB_GPOS_LOOKUP_CHAIN:          Free_ChainContextPos    ( st ); return;
5907   /*case HB_GPOS_LOOKUP_EXTENSION:      Free_ExtensionPos       ( st ); return;*/
5908     default:                                                                    return;
5909   }
5910 }
5911
5912
5913 /* apply one lookup to the input string object */
5914
5915 static HB_Error  GPOS_Do_String_Lookup( GPOS_Instance*    gpi,
5916                                    HB_UShort         lookup_index,
5917                                    HB_Buffer        buffer )
5918 {
5919   HB_Error         error, retError = HB_Err_Not_Covered;
5920   HB_GPOSHeader*  gpos = gpi->gpos;
5921
5922   HB_UInt*  properties = gpos->LookupList.Properties;
5923
5924   const int       nesting_level = 0;
5925   /* 0xFFFF indicates that we don't have a context length yet */
5926   const HB_UShort context_length = 0xFFFF;
5927
5928
5929   gpi->last  = 0xFFFF;     /* no last valid glyph for cursive pos. */
5930
5931   buffer->in_pos = 0;
5932   while ( buffer->in_pos < buffer->in_length )
5933   {
5934     if ( ~IN_PROPERTIES( buffer->in_pos ) & properties[lookup_index] )
5935     {
5936       /* Note that the connection between mark and base glyphs hold
5937          exactly one (string) lookup.  For example, it would be possible
5938          that in the first lookup, mark glyph X is attached to base
5939          glyph A, and in the next lookup it is attached to base glyph B.
5940          It is up to the font designer to provide meaningful lookups and
5941          lookup order.                                                   */
5942
5943       error = GPOS_Do_Glyph_Lookup( gpi, lookup_index, buffer, context_length, nesting_level );
5944       if ( error && error != HB_Err_Not_Covered )
5945         return error;
5946     }
5947     else
5948     {
5949       /* Contrary to properties defined in GDEF, user-defined properties
5950          will always stop a possible cursive positioning.                */
5951       gpi->last = 0xFFFF;
5952
5953       error = HB_Err_Not_Covered;
5954     }
5955
5956     if ( error == HB_Err_Not_Covered )
5957       (buffer->in_pos)++;
5958     else
5959       retError = error;
5960   }
5961
5962   return retError;
5963 }
5964
5965
5966 static HB_Error  Position_CursiveChain ( HB_Buffer     buffer )
5967 {
5968   HB_UInt   i, j;
5969   HB_Position positions = buffer->positions;
5970
5971   /* First handle all left-to-right connections */
5972   for (j = 0; j < buffer->in_length; j++)
5973   {
5974     if (positions[j].cursive_chain > 0)
5975       positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
5976   }
5977
5978   /* Then handle all right-to-left connections */
5979   for (i = buffer->in_length; i > 0; i--)
5980   {
5981     j = i - 1;
5982
5983     if (positions[j].cursive_chain < 0)
5984       positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
5985   }
5986
5987   return HB_Err_Ok;
5988 }
5989
5990
5991 HB_Error  HB_GPOS_Add_Feature( HB_GPOSHeader*  gpos,
5992                                HB_UShort        feature_index,
5993                                HB_UInt          property )
5994 {
5995   HB_UShort    i;
5996
5997   HB_Feature  feature;
5998   HB_UInt*     properties;
5999   HB_UShort*   index;
6000   HB_UShort    lookup_count;
6001
6002   /* Each feature can only be added once */
6003
6004   if ( !gpos ||
6005        feature_index >= gpos->FeatureList.FeatureCount ||
6006        gpos->FeatureList.ApplyCount == gpos->FeatureList.FeatureCount )
6007     return ERR(HB_Err_Invalid_Argument);
6008
6009   gpos->FeatureList.ApplyOrder[gpos->FeatureList.ApplyCount++] = feature_index;
6010
6011   properties = gpos->LookupList.Properties;
6012
6013   feature = gpos->FeatureList.FeatureRecord[feature_index].Feature;
6014   index   = feature.LookupListIndex;
6015   lookup_count = gpos->LookupList.LookupCount;
6016
6017   for ( i = 0; i < feature.LookupListCount; i++ )
6018   {
6019     HB_UShort lookup_index = index[i];
6020     if (lookup_index < lookup_count)
6021       properties[lookup_index] |= property;
6022   }
6023
6024   return HB_Err_Ok;
6025 }
6026
6027
6028
6029 HB_Error  HB_GPOS_Clear_Features( HB_GPOSHeader*  gpos )
6030 {
6031   HB_UShort i;
6032
6033   HB_UInt*  properties;
6034
6035
6036   if ( !gpos )
6037     return ERR(HB_Err_Invalid_Argument);
6038
6039   gpos->FeatureList.ApplyCount = 0;
6040
6041   properties = gpos->LookupList.Properties;
6042
6043   for ( i = 0; i < gpos->LookupList.LookupCount; i++ )
6044     properties[i] = 0;
6045
6046   return HB_Err_Ok;
6047 }
6048
6049
6050
6051 HB_Error  HB_GPOS_Register_Glyph_Function( HB_GPOSHeader*    gpos,
6052                                            HB_GlyphFunction  gfunc )
6053 {
6054   if ( !gpos )
6055     return ERR(HB_Err_Invalid_Argument);
6056
6057   gpos->gfunc = gfunc;
6058
6059   return HB_Err_Ok;
6060 }
6061
6062
6063
6064 HB_Error  HB_GPOS_Register_MM_Function( HB_GPOSHeader*  gpos,
6065                                         HB_MMFunction   mmfunc,
6066                                         void*            data )
6067 {
6068   if ( !gpos )
6069     return ERR(HB_Err_Invalid_Argument);
6070
6071   gpos->mmfunc = mmfunc;
6072   gpos->data   = data;
6073
6074   return HB_Err_Ok;
6075 }
6076
6077 /* If `dvi' is TRUE, glyph contour points for anchor points and device
6078    tables are ignored -- you will get device independent values.         */
6079
6080
6081 HB_Error  HB_GPOS_Apply_String( HB_Font           font,
6082                                 HB_GPOSHeader*    gpos,
6083                                 HB_UShort         load_flags,
6084                                 HB_Buffer         buffer,
6085                                 HB_Bool           dvi,
6086                                 HB_Bool           r2l )
6087 {
6088   HB_Error       error, retError = HB_Err_Not_Covered;
6089   GPOS_Instance  gpi;
6090   int            i, j, lookup_count, num_features;
6091
6092   if ( !font || !gpos || !buffer )
6093     return ERR(HB_Err_Invalid_Argument);
6094
6095   if ( buffer->in_length == 0 )
6096     return HB_Err_Not_Covered;
6097
6098   gpi.font       = font;
6099   gpi.gpos       = gpos;
6100   gpi.load_flags = load_flags;
6101   gpi.r2l        = r2l;
6102   gpi.dvi        = dvi;
6103
6104   lookup_count = gpos->LookupList.LookupCount;
6105   num_features = gpos->FeatureList.ApplyCount;
6106
6107   if ( num_features )
6108     {
6109       error = _hb_buffer_clear_positions( buffer );
6110       if ( error )
6111         return error;
6112     }
6113
6114   for ( i = 0; i < num_features; i++ )
6115   {
6116     HB_UShort  feature_index = gpos->FeatureList.ApplyOrder[i];
6117     HB_Feature feature = gpos->FeatureList.FeatureRecord[feature_index].Feature;
6118
6119     for ( j = 0; j < feature.LookupListCount; j++ )
6120     {
6121       HB_UShort lookup_index = feature.LookupListIndex[j];
6122
6123       /* Skip nonexistant lookups */
6124       if (lookup_index >= lookup_count)
6125        continue;
6126
6127       error = GPOS_Do_String_Lookup( &gpi, lookup_index, buffer );
6128       if ( error )
6129       {
6130         if ( error != HB_Err_Not_Covered )
6131           return error;
6132       }
6133       else
6134         retError = error;
6135     }
6136   }
6137
6138   if ( num_features )
6139     {
6140       error = Position_CursiveChain ( buffer );
6141       if ( error )
6142         return error;
6143     }
6144
6145   return retError;
6146 }
6147
6148 /* END */