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