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