shithub: freetype+ttf2subf

Download patch

ref: 8cd8907311da7ee6c09e22d9b8e26124d365db2a
parent: 948ee807e1d1d3270dc4d3a9c09615c11024b9c3
author: David Turner <[email protected]>
date: Sat May 4 14:02:59 EDT 2002

* src/truetype/ttgload.c (TT_Load_Glyph): finally fixing the last
          bug that prevented FreeType 2.x and FreeType 1.x to produce
          bit-by-bit identical monochrome glyph bitmaps with native TrueType
          hinting. The culprit was a single-bit flag that wasn't set
          correctly by the TrueType glyph loader !!

        * src/otlayout/otlayout.h,
          src/otlayout/otlbase.c,
          src/otlayout/otlbase.h,
          src/otlayout/otlconf.h,
          src/otlayout/otlgdef.c,
          src/otlayout/otlgdef.h,
          src/otlayout/otlgpos.c,
          src/otlayout/otlgpos.h,
          src/otlayout/otlgsub.c,
          src/otlayout/otlgsub.h,
          src/otlayout/otljstf.c,
          src/otlayout/otljstf.h,
          src/otlayout/otltable.c,
          src/otlayout/otltable.h,
          src/otlayout/otltags.h:

            adding OpenType Layout source files. Module is still incomplete

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+2002-05-04  David Turner  <[email protected]>
+
+        * src/truetype/ttgload.c (TT_Load_Glyph): finally fixing the last
+          bug that prevented FreeType 2.x and FreeType 1.x to produce
+          bit-by-bit identical monochrome glyph bitmaps with native TrueType
+          hinting. The culprit was a single-bit flag that wasn't set
+          correctly by the TrueType glyph loader !!
+
+        * src/otlayout/otlayout.h,
+          src/otlayout/otlbase.c,
+          src/otlayout/otlbase.h,
+          src/otlayout/otlconf.h,
+          src/otlayout/otlgdef.c,
+          src/otlayout/otlgdef.h,
+          src/otlayout/otlgpos.c,
+          src/otlayout/otlgpos.h,
+          src/otlayout/otlgsub.c,
+          src/otlayout/otlgsub.h,
+          src/otlayout/otljstf.c,
+          src/otlayout/otljstf.h,
+          src/otlayout/otltable.c,
+          src/otlayout/otltable.h,
+          src/otlayout/otltags.h:
+          
+            adding OpenType Layout source files. Module is still incomplete
+            
+
 2002-05-02  Werner Lemberg  <[email protected]>
 
 	* src/sfnt/ttcmap0.c (tt_cmap4_char_index): Fix serious typo
--- /dev/null
+++ b/src/otlayout/otlayout.h
@@ -1,0 +1,168 @@
+#ifndef __OT_LAYOUT_H__
+#define __OT_LAYOUT_H__
+
+
+#include "otlconf.h"
+
+OTL_BEGIN_HEADER
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                       BASE DATA TYPES                        *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  typedef unsigned char     OTL_Byte;
+  typedef const OTL_Byte*   OTL_Bytes;
+
+  typedef int               OTL_Error;
+
+  typedef void*             OTL_Pointer;
+
+  typedef int               OTL_Int;
+  typedef unsigned int      OTL_UInt;
+
+  typedef short             OTL_Int16;
+  typedef unsigned short    OTL_UInt16;
+
+#if OTL_SIZEOF_INT == 4
+
+  typedef int               OTL_Int32;
+  typedef unsigned int      OTL_UInt32;
+
+#elif OTL_SIZEOF_LONG == 4
+
+  typedef long              OTL_Int32;
+  typedef unsigned long     OTL_UInt32;
+
+#else
+#  error "no 32-bits type found"
+#endif
+
+  typedef OTL_UInt32        OTL_Tag;
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                       ERROR CODES                            *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  typedef enum
+  {
+    OTL_Err_Ok = 0,
+    OTL_Err_InvalidArgument,
+    OTL_Err_InvalidFormat,
+    OTL_Err_InvalidOffset,
+
+  } OTL_Error;
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                        ENUMERATIONS                          *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+#define  OTL_MAKE_TAG(c1,c2,c3,c4)         \
+           ( ( (OTL_UInt32)(c1) << 24 ) |  \
+               (OTL_UInt32)(c2) << 16 ) |  \
+               (OTL_UInt32)(c3) <<  8 ) |  \
+               (OTL_UInt32)(c4)         )
+
+  typedef enum OTL_ScriptTag_
+  {
+    OTL_SCRIPT_NONE = 0,
+
+#define OTL_SCRIPT_TAG(c1,c2,c3,c4,s,n)  OTL_SCRIPT_TAG_ ## n = OTL_MAKE_TAG(c1,c2,c3,c4),
+#include "otltags.h"
+
+    OTL_SCRIPT_MAX
+
+  } OTL_ScriptTag;
+
+
+  typedef enum OTL_LangTag_
+  {
+    OTL_LANG_DEFAULT = 0,
+
+#define OTL_LANG_TAG(c1,c2,c3,c4,s,n)  OTL_LANG_TAG_ ## n = OTL_MAKE_TAG(c1,c2,c3,c4),
+#include "otltags.h"
+
+    OTL_LANG_MAX
+
+  } OTL_LangTag;
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                       MEMORY READS                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+#define  OTL_PEEK_USHORT(p)  ( ((OTL_UInt)((p)[0]) << 8) |  \
+                               ((OTL_UInt)((p)[1])     ) )
+
+#define  OTL_PEEK_ULONG(p)   ( ((OTL_UInt32)((p)[0]) << 24) |  \
+                               ((OTL_UInt32)((p)[1]) << 16) |  \
+                               ((OTL_UInt32)((p)[2]) <<  8) |  \
+                               ((OTL_UInt32)((p)[3])      ) )
+
+#define  OTL_PEEK_SHORT(p)     ((OTL_Int16)OTL_PEEK_USHORT(p))
+
+#define  OTL_PEEK_LONG(p)      ((OTL_Int32)OTL_PEEK_ULONG(p))
+
+#define  OTL_NEXT_USHORT(p)  ( (p) += 2, OTL_PEEK_USHORT((p)-2) )
+#define  OTL_NEXT_ULONG(p)   ( (p) += 4, OTL_PEEK_ULONG((p)-4) )
+
+#define  OTL_NEXT_SHORT(p)   ((OTL_Int16)OTL_NEXT_USHORT(p))
+#define  OTL_NEXT_LONG(p)    ((OTL_Int32)OTL_NEXT_ULONG(p))
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                        VALIDATION                            *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  typedef struct OTL_ValidatorRec_*  OTL_Validator;
+
+  typedef struct OTL_ValidatorRec_
+  {
+    OTL_Bytes    limit;
+    OTL_Bytes    base;
+    OTL_Error    error;
+    OTL_jmp_buf  jump_buffer;
+
+  } OTL_ValidatorRec;
+
+  typedef void  (*OTL_ValidateFunc)( OTL_Bytes  table,
+                                     OTL_Valid  valid );
+
+  OTL_API( void )
+  otl_validator_error( OTL_Validator  validator,
+                       OTL_Error      error );
+
+#define  OTL_INVALID(e)  otl_validator_error( valid, e )
+
+#define  OTL_INVALID_TOO_SHORT  OTL_INVALID( OTL_Err_InvalidOffset );
+#define  OTL_INVALID_DATA       OTL_INVALID( OTL_Err_InvalidFormat );
+
+#define  OTL_CHECK(_count)   OTL_BEGIN_STMNT                       \
+                               if ( p + (_count) > valid->limit )  \
+                                 OTL_INVALID_TOO_SHORT;            \
+                             OTL_END_STMNT
+
+ /* */
+
+OTL_END_HEADER
+
+#endif /* __OPENTYPE_LAYOUT_H__ */
--- /dev/null
+++ b/src/otlayout/otlbase.c
@@ -1,0 +1,181 @@
+#include "otlbase.h"
+#include "otlcommn.h"
+
+  static void
+  otl_base_coord_validate( OTL_Bytes      table,
+                           OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format;
+
+    OTL_CHECK( 4 );
+
+    format = OTL_NEXT_USHORT( p );
+    p += 2;
+
+    switch ( format )
+    {
+      case 1:
+        break;
+
+      case 2:
+        OTL_CHECK( 4 );
+        break;
+
+      case 3:
+        OTL_CHECK( 2 );
+        otl_device_table_validate( table + OTL_PEEK_USHORT( p ) );
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+
+  static void
+  otl_base_tag_list_validate( OTL_Bytes      table,
+                              OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK(2);
+
+    count = OTL_NEXT_USHORT( p );
+    OTL_CHECK( count*4 );
+  }
+
+
+
+  static void
+  otl_base_values_validate( OTL_Bytes      table,
+                            OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 4 );
+
+    p    += 2;  /* skip default index */
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( count*2 );
+
+    for ( ; count > 0; count-- )
+      otl_base_coord_validate( table + OTL_NEXT_USHORT( p ), valid );
+  }
+
+
+  static void
+  otl_base_minmax_validate( OTL_Bytes      table,
+                            OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   min_coord, max_coord, count;
+
+    OTL_CHECK(6);
+    min_coord = OTL_NEXT_USHORT( p );
+    max_coord = OTL_NEXT_USHORT( p );
+    count     = OTL_NEXT_USHORT( p );
+
+    if ( min_coord )
+      otl_base_coord_validate( table + min_coord, valid );
+
+    if ( max_coord )
+      otl_base_coord_validate( table + max_coord, valid );
+
+    OTL_CHECK( count*8 );
+    for ( ; count > 0; count-- )
+    {
+      p += 4;  /* ignore tag */
+      min_coord = OTL_NEXT_USHORT( p );
+      max_coord = OTL_NEXT_USHORT( p );
+
+      if ( min_coord )
+        otl_base_coord_validate( table + min_coord, valid );
+
+      if ( max_coord )
+        otl_base_coord_validate( table + max_coord, valid );
+    }
+  }
+
+
+  static void
+  otl_base_script_validate( OTL_Bytes      table,
+                            OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   values, default_minmax;
+
+    OTL_CHECK(6);
+
+    values         = OTL_NEXT_USHORT( p );
+    default_minmax = OTL_NEXT_USHORT( p );
+    count          = OTL_NEXT_USHORT( p );
+
+    if ( values )
+      otl_base_values_validate( table + values, valid );
+
+    if ( default_minmax )
+      otl_base_minmax_validate( table + default_minmax, valid );
+
+    OTL_CHECK( count*6 );
+    for ( ; count > 0; count-- )
+    {
+      p += 4;  /* ignore tag */
+      otl_base_minmax_validate( table + OTL_NEXT_USHORT( p ), valid );
+    }
+  }
+
+
+  static void
+  otl_base_script_list_validate( OTL_Bytes      table,
+                                 OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK(2);
+
+    count = OTL_NEXT_USHORT( p );
+    OTL_CHECK(count*6);
+
+    for ( ; count > 0; count-- )
+    {
+      p += 4;  /* ignore script tag */
+      otl_base_script_validate( table + OTL_NEXT_USHORT( p ) );
+    }
+  }
+
+  static void
+  otl_axis_table_validate( OTL_Bytes      table,
+                           OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   tags;
+
+    OTL_CHECK(4);
+
+    tags = OTL_NEXT_USHORT( p );
+    if ( tags )
+      otl_base_tag_list_validate   ( table + tags );
+
+    otl_base_script_list_validate( table + OTL_NEXT_USHORT( p ) );
+  }
+
+
+  OTL_LOCALDEF( void )
+  otl_base_validate( OTL_Bytes      table,
+                     OTL_Validator  valid )
+  {
+    OTL_Bytes p = table;
+
+    OTL_CHECK(6);
+
+    if ( OTL_NEXT_ULONG( p ) != 0x10000UL )
+      OTL_INVALID_DATA;
+
+    otl_axis_table_validate( table + OTL_NEXT_USHORT( p ) );
+    otl_axis_table_validate( table + OTL_NEXT_USHORT( p ) );
+  }
\ No newline at end of file
--- /dev/null
+++ b/src/otlayout/otlbase.h
@@ -1,0 +1,14 @@
+#ifndef __OTL_BASE_H__
+#define __OTL_BASE_H__
+
+#include "otlayout.h"
+
+OTL_BEGIN_HEADER
+
+  OTL_LOCAL( void )
+  otl_base_validate( OTL_Bytes      table,
+                     OTL_Validator  valid );
+
+OTL_END_HEADER
+
+#endif /* __OTL_BASE_H__ */
--- a/src/otlayout/otlcommn.c
+++ b/src/otlayout/otlcommn.c
@@ -38,12 +38,12 @@
     if ( table + 4 > valid->limit )
       OTL_INVALID_TOO_SHORT;
 
-    format = OTL_NEXT_UShort( p );
+    format = OTL_NEXT_USHORT( p );
     switch ( format )
     {
     case 1:
       {
-        OTL_UInt  count = OTL_NEXT_UShort( p );
+        OTL_UInt  count = OTL_NEXT_USHORT( p );
 
 
         if ( p + count * 2 >= valid->limit )
@@ -55,7 +55,7 @@
 
     case 2:
       {
-        OTL_UInt  n, num_ranges = OTL_NEXT_UShort( p );
+        OTL_UInt  n, num_ranges = OTL_NEXT_USHORT( p );
         OTL_UInt  start, end, start_cover, total = 0, last = 0;
 
 
@@ -64,9 +64,9 @@
 
         for ( n = 0; n < num_ranges; n++ )
         {
-          start       = OTL_NEXT_UShort( p );
-          end         = OTL_NEXT_UShort( p );
-          start_cover = OTL_NEXT_UShort( p );
+          start       = OTL_NEXT_USHORT( p );
+          end         = OTL_NEXT_USHORT( p );
+          start_cover = OTL_NEXT_USHORT( p );
 
           if ( start > end || start_cover != total )
             OTL_INVALID_DATA;
@@ -90,8 +90,8 @@
   otl_coverage_get_count( OTL_Bytes  table )
   {
     OTL_Bytes  p      = table;
-    OTL_UInt   format = OTL_NEXT_UShort( p );
-    OTL_UInt   count  = OTL_NEXT_UShort( p );
+    OTL_UInt   format = OTL_NEXT_USHORT( p );
+    OTL_UInt   count  = OTL_NEXT_USHORT( p );
     OTL_UInt   result = 0;
 
 
@@ -107,8 +107,8 @@
 
         for ( ; count > 0; count-- )
         {
-          start = OTL_NEXT_UShort( p );
-          end   = OTL_NEXT_UShort( p );
+          start = OTL_NEXT_USHORT( p );
+          end   = OTL_NEXT_USHORT( p );
           p    += 2;                    /* skip start_index */
 
           result += end - start + 1;
@@ -129,8 +129,8 @@
                           OTL_UInt   glyph_index )
   {
     OTL_Bytes  p      = table;
-    OTL_UInt   format = OTL_NEXT_UShort( p );
-    OTL_UInt   count  = OTL_NEXT_UShort( p );
+    OTL_UInt   format = OTL_NEXT_USHORT( p );
+    OTL_UInt   count  = OTL_NEXT_USHORT( p );
 
 
     switch ( format )
@@ -145,7 +145,7 @@
         {
           mid    = ( min + max ) >> 1;
           p      = table + 2 * mid;
-          gindex = OTL_PEEK_UShort( p );
+          gindex = OTL_PEEK_USHORT( p );
 
           if ( glyph_index == gindex )
             return (OTL_Int)mid;
@@ -169,8 +169,8 @@
         {
           mid    = ( min + max ) >> 1;
           p      = table + 6 * mid;
-          start  = OTL_NEXT_UShort( p );
-          end    = OTL_NEXT_UShort( p );
+          start  = OTL_NEXT_USHORT( p );
+          end    = OTL_NEXT_USHORT( p );
 
           if ( glyph_index < start )
             max = mid;
@@ -177,7 +177,7 @@
           else if ( glyph_index > end )
             min = mid + 1;
           else
-            return (OTL_Int)( glyph_index + OTL_NEXT_UShort( p ) - start );
+            return (OTL_Int)( glyph_index + OTL_NEXT_USHORT( p ) - start );
         }
       }
       break;
@@ -209,18 +209,18 @@
     if ( p + 4 > valid->limit )
       OTL_INVALID_TOO_SHORT;
 
-    format = OTL_NEXT_UShort( p );
+    format = OTL_NEXT_USHORT( p );
     switch ( format )
     {
     case 1:
       {
-        OTL_UInt  count, start = OTL_NEXT_UShort( p );
+        OTL_UInt  count, start = OTL_NEXT_USHORT( p );
 
 
         if ( p + 2 > valid->limit )
           OTL_INVALID_TOO_SHORT;
 
-        count = OTL_NEXT_UShort( p );
+        count = OTL_NEXT_USHORT( p );
 
         if ( p + count * 2 > valid->limit )
           OTL_INVALID_TOO_SHORT;
@@ -231,7 +231,7 @@
 
     case 2:
       {
-        OTL_UInt  n, num_ranges = OTL_NEXT_UShort( p );
+        OTL_UInt  n, num_ranges = OTL_NEXT_USHORT( p );
         OTL_UInt  start, end, value, last = 0;
 
 
@@ -240,9 +240,9 @@
 
         for ( n = 0; n < num_ranges; n++ )
         {
-          start = OTL_NEXT_UShort( p );
-          end   = OTL_NEXT_UShort( p );
-          value = OTL_NEXT_UShort( p );  /* ignored */
+          start = OTL_NEXT_USHORT( p );
+          end   = OTL_NEXT_USHORT( p );
+          value = OTL_NEXT_USHORT( p );  /* ignored */
 
           if ( start > end || ( n > 0 && start <= last ) )
             OTL_INVALID_DATA;
@@ -263,7 +263,7 @@
                                   OTL_UInt   glyph_index )
   {
     OTL_Bytes  p      = table;
-    OTL_UInt   format = OTL_NEXT_UShort( p );
+    OTL_UInt   format = OTL_NEXT_USHORT( p );
 
 
     switch ( format )
@@ -270,8 +270,8 @@
     {
     case 1:
       {
-        OTL_UInt  start = OTL_NEXT_UShort( p );
-        OTL_UInt  count = OTL_NEXT_UShort( p );
+        OTL_UInt  start = OTL_NEXT_USHORT( p );
+        OTL_UInt  count = OTL_NEXT_USHORT( p );
         OTL_UInt  idx   = (OTL_UInt)( glyph_index - start );
 
 
@@ -278,7 +278,7 @@
         if ( idx < count )
         {
           p += 2 * idx;
-          return OTL_PEEK_UShort( p );
+          return OTL_PEEK_USHORT( p );
         }
       }
       break;
@@ -285,7 +285,7 @@
 
     case 2:
       {
-        OTL_UInt  count = OTL_NEXT_UShort( p );
+        OTL_UInt  count = OTL_NEXT_USHORT( p );
         OTL_UInt  min = 0, max = count, mid, gindex;
 
 
@@ -294,8 +294,8 @@
         {
           mid   = ( min + max ) >> 1;
           p     = table + 6 * mid;
-          start = OTL_NEXT_UShort( p );
-          end   = OTL_NEXT_UShort( p );
+          start = OTL_NEXT_USHORT( p );
+          end   = OTL_NEXT_USHORT( p );
 
           if ( glyph_index < start )
             max = mid;
@@ -302,7 +302,7 @@
           else if ( glyph_index > end )
             min = mid + 1;
           else
-            return OTL_PEEK_UShort( p );
+            return OTL_PEEK_USHORT( p );
         }
       }
       break;
@@ -334,9 +334,9 @@
     if ( p + 8 > valid->limit )
       OTL_INVALID_TOO_SHORT;
 
-    start  = OTL_NEXT_UShort( p );
-    end    = OTL_NEXT_UShort( p );
-    format = OTL_NEXT_UShort( p );
+    start  = OTL_NEXT_USHORT( p );
+    end    = OTL_NEXT_USHORT( p );
+    format = OTL_NEXT_USHORT( p );
 
     if ( format < 1 || format > 3 || end < start )
       OTL_INVALID_DATA;
@@ -354,7 +354,7 @@
     OTL_Bytes  p = table;
 
 
-    return OTL_PEEK_UShort( p );
+    return OTL_PEEK_USHORT( p );
   }
 
 
@@ -364,7 +364,7 @@
     OTL_Bytes  p = table + 2;
 
 
-    return OTL_PEEK_UShort( p );
+    return OTL_PEEK_USHORT( p );
   }
 
 
@@ -377,9 +377,9 @@
     OTL_UInt   start, end, format, idx, value;
 
 
-    start  = OTL_NEXT_UShort( p );
-    end    = OTL_NEXT_UShort( p );
-    format = OTL_NEXT_UShort( p );
+    start  = OTL_NEXT_USHORT( p );
+    end    = OTL_NEXT_USHORT( p );
+    format = OTL_NEXT_USHORT( p );
 
     if ( size >= start && size <= end )
     {
@@ -391,7 +391,7 @@
       case 1:
         idx    = (OTL_UInt)( ( size - start ) * 2 );
         p     += idx / 16;
-        value  = OTL_PEEK_UShort( p );
+        value  = OTL_PEEK_USHORT( p );
         shift  = idx & 15;
         result = (OTL_Short)( value << shift ) >> ( 14 - shift );
 
@@ -400,7 +400,7 @@
       case 2:
         idx    = (OTL_UInt)( ( size - start ) * 4 );
         p     += idx / 16;
-        value  = OTL_PEEK_UShort( p );
+        value  = OTL_PEEK_USHORT( p );
         shift  = idx & 15;
         result = (OTL_Short)( value << shift ) >> ( 12 - shift );
 
@@ -409,7 +409,7 @@
       case 3:
         idx    = (OTL_UInt)( ( size - start ) * 8 );
         p     += idx / 16;
-        value  = OTL_PEEK_UShort( p );
+        value  = OTL_PEEK_USHORT( p );
         shift  = idx & 15;
         result = (OTL_Short)( value << shift ) >> ( 8 - shift );
 
@@ -444,7 +444,7 @@
       OTL_INVALID_TOO_SHORT;
 
     p += 4;
-    num_tables = OTL_NEXT_UShort( p );
+    num_tables = OTL_NEXT_USHORT( p );
 
     if ( p + num_tables * 2 > valid->limit )
       OTL_INVALID_TOO_SHORT;
@@ -451,7 +451,7 @@
 
     for ( ; num_tables > 0; num_tables-- )
     {
-      offset = OTL_NEXT_UShort( p );
+      offset = OTL_NEXT_USHORT( p );
 
       if ( table + offset >= valid->limit )
         OTL_INVALID_OFFSET;
@@ -467,7 +467,7 @@
     OTL_Bytes  p = table + 4;
 
 
-    return OTL_PEEK_UShort( p );
+    return OTL_PEEK_USHORT( p );
   }
 
 
@@ -480,11 +480,11 @@
 
 
     p     = table + 4;
-    count = OTL_NEXT_UShort( p );
+    count = OTL_NEXT_USHORT( p );
     if ( idx < count )
     {
       p     += idx * 2;
-      result = table + OTL_PEEK_UShort( p );
+      result = table + OTL_PEEK_USHORT( p );
     }
 
     return result;
@@ -510,7 +510,7 @@
     if ( p + 2 > valid->limit )
       OTL_INVALID_TOO_SHORT;
 
-    num_lookups = OTL_NEXT_UShort( p );
+    num_lookups = OTL_NEXT_USHORT( p );
 
     if ( p + num_lookups * 2 > valid->limit )
       OTL_INVALID_TOO_SHORT;
@@ -517,7 +517,7 @@
 
     for ( ; num_lookups > 0; num_lookups-- )
     {
-      offset = OTL_NEXT_UShort( p );
+      offset = OTL_NEXT_USHORT( p );
 
       otl_lookup_validate( table + offset, valid );
     }
@@ -530,7 +530,7 @@
     OTL_Bytes  p = table;
 
 
-    return OTL_PEEK_UShort( p );
+    return OTL_PEEK_USHORT( p );
   }
 
 
@@ -543,11 +543,11 @@
 
 
     p     = table;
-    count = OTL_NEXT_UShort( p );
+    count = OTL_NEXT_USHORT( p );
     if ( idx < count )
     {
       p     += idx * 2;
-      result = table + OTL_PEEK_UShort( p );
+      result = table + OTL_PEEK_USHORT( p );
     }
 
     return result;
@@ -576,7 +576,7 @@
                            OTL_Pointer      func_data )
   {
     OTL_Bytes  p     = table;
-    OTL_UInt   count = OTL_NEXT_UShort( p );
+    OTL_UInt   count = OTL_NEXT_USHORT( p );
 
 
     for ( ; count > 0; count-- )
@@ -603,8 +603,8 @@
     if ( p + 4 > valid->limit )
       OTL_INVALID_TOO_SHORT;
 
-    feat_params = OTL_NEXT_UShort( p );  /* ignored */
-    num_lookups = OTL_NEXT_UShort( p );
+    feat_params = OTL_NEXT_USHORT( p );  /* ignored */
+    num_lookups = OTL_NEXT_USHORT( p );
 
     if ( p + num_lookups * 2 > valid->limit )
       OTL_INVALID_TOO_SHORT;
@@ -619,7 +619,7 @@
     OTL_Bytes  p = table + 4;
 
 
-    return OTL_PEEK_UShort( p );
+    return OTL_PEEK_USHORT( p );
   }
 
 
@@ -634,13 +634,13 @@
 
 
     p            = table + 4;
-    num_features = OTL_NEXT_UShort( p );
+    num_features = OTL_NEXT_USHORT( p );
 
     p += start * 2;
 
     for ( ; count > 0 && start < num_features; count--, start++ )
     {
-      lookups[0] = OTL_NEXT_UShort(p);
+      lookups[0] = OTL_NEXT_USHORT(p);
       lookups++;
       result++;
     }
@@ -668,7 +668,7 @@
     if ( table + 2 > valid->limit )
       OTL_INVALID_TOO_SHORT;
 
-    num_features = OTL_NEXT_UShort( p );
+    num_features = OTL_NEXT_USHORT( p );
 
     if ( p + num_features * 2 > valid->limit )
       OTL_INVALID_TOO_SHORT;
@@ -676,7 +676,7 @@
     for ( ; num_features > 0; num_features-- )
     {
       p     += 4;                       /* skip tag */
-      offset = OTL_NEXT_UShort( p );
+      offset = OTL_NEXT_USHORT( p );
 
       otl_feature_table_validate( table + offset, valid );
     }
@@ -689,7 +689,7 @@
     OTL_Bytes  p = table;
 
 
-    return OTL_PEEK_UShort( p );
+    return OTL_PEEK_USHORT( p );
   }
 
 
@@ -702,12 +702,12 @@
 
 
     p     = table;
-    count = OTL_NEXT_UShort( p );
+    count = OTL_NEXT_USHORT( p );
 
     if ( idx < count )
     {
       p     += idx * 2;
-      result = table + OTL_PEEK_UShort( p );
+      result = table + OTL_PEEK_USHORT( p );
     }
 
     return result;
@@ -724,10 +724,10 @@
 
 
     p = table;
-    count = OTL_NEXT_UShort( p );
+    count = OTL_NEXT_USHORT( p );
 
     for ( ; count > 0; count-- )
-      func( table + OTL_NEXT_UShort( p ), func_data );
+      func( table + OTL_NEXT_USHORT( p ), func_data );
   }
 
 
@@ -753,9 +753,9 @@
     if ( table + 6 >= valid->limit )
       OTL_INVALID_TOO_SHORT;
 
-    lookup_order = OTL_NEXT_UShort( p );
-    req_feature  = OTL_NEXT_UShort( p );
-    num_features = OTL_NEXT_UShort( p );
+    lookup_order = OTL_NEXT_USHORT( p );
+    req_feature  = OTL_NEXT_USHORT( p );
+    num_features = OTL_NEXT_USHORT( p );
 
     /* XXX: check req_feature if not 0xFFFFU */
 
@@ -769,10 +769,19 @@
   OTL_LOCALDEF( OTL_UInt )
   otl_lang_get_count( OTL_Bytes  table )
   {
-    OTL_Bytes  p  = table + 4;
+    OTL_Bytes  p = table + 4;
 
+    return OTL_PEEK_USHORT( p );
+  }
 
-    return OTL_PEEK_UShort( p );
+
+  OTL_LOCALDEF( OTL_UInt )
+  otl_lang_get_req_feature( OTL_Bytes  table )
+  {
+    OTL_Bytes  p = table + 2;
+
+
+    return OTL_PEEK_USHORT( p );
   }
 
 
@@ -783,7 +792,7 @@
                          OTL_UInt  *features )
   {
     OTL_Bytes  p            = table + 4;
-    OTL_UInt   num_features = OTL_NEXT_UShort( p );
+    OTL_UInt   num_features = OTL_NEXT_USHORT( p );
     OTL_UInt   result       = 0;
 
 
@@ -791,7 +800,7 @@
 
     for ( ; count > 0 && start < num_features; start++, count-- )
     {
-      features[0] = OTL_NEXT_UShort( p );
+      features[0] = OTL_NEXT_USHORT( p );
       features++;
       result++;
     }
@@ -800,16 +809,8 @@
   }
 
 
-  OTL_LOCALDEF( OTL_UInt )
-  otl_lang_get_req_feature( OTL_Bytes  table )
-  {
-    OTL_Bytes  p = table + 2;
 
 
-    return OTL_PEEK_UShort( p );
-  }
-
-
   /*************************************************************************/
   /*************************************************************************/
   /*****                                                               *****/
@@ -820,8 +821,8 @@
 
 
   OTL_LOCALDEF( void )
-  otl_script_table_validate( OTL_Bytes      table,
-                             OTL_Validator  valid )
+  otl_script_validate( OTL_Bytes      table,
+                       OTL_Validator  valid )
   {
     OTL_UInt   default_lang;
     OTL_Bytes  p = table;
@@ -830,8 +831,8 @@
     if ( table + 4 > valid->limit )
       OTL_INVALID_TOO_SHORT;
 
-    default_lang = OTL_NEXT_UShort( p );
-    num_langs    = OTL_NEXT_UShort( p );
+    default_lang = OTL_NEXT_USHORT( p );
+    num_langs    = OTL_NEXT_USHORT( p );
 
     if ( default_lang != 0 )
     {
@@ -848,7 +849,7 @@
 
 
       p     += 4;  /* skip tag */
-      offset = OTL_NEXT_UShort( p );
+      offset = OTL_NEXT_USHORT( p );
 
       otl_lang_validate( table + offset, valid );
     }
@@ -866,7 +867,7 @@
     if ( list + 2 > valid->limit )
       OTL_INVALID_TOO_SHORT;
 
-    num_scripts = OTL_NEXT_UShort( p );
+    num_scripts = OTL_NEXT_USHORT( p );
 
     if ( p + num_scripts * 6 > valid->limit )
       OTL_INVALID_TOO_SHORT;
@@ -877,11 +878,63 @@
 
 
       p     += 4;                       /* skip tag */
-      offset = OTL_NEXT_UShort( p );
+      offset = OTL_NEXT_USHORT( p );
 
       otl_script_table_validate( list + offset, valid );
     }
   }
 
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                         LOOKUP LISTS                          *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  static void
+  otl_lookup_table_validate( OTL_Bytes          table,
+                             OTL_UInt           type_count,
+                             OTL_ValidateFunc*  type_funcs,
+                             OTL_Validator      valid )
+  {
+    OTL_Bytes         p = table;
+    OTL_UInt          lookup_type, lookup_flag, count;
+    OTL_ValidateFunc  validate;
+
+    OTL_CHECK( 6 );
+    lookup_type = OTL_NEXT_USHORT( p );
+    lookup_flag = OTL_NEXT_USHORT( p );
+    count       = OTL_NEXT_USHORT( p );
+
+    if ( lookup_type == 0 || lookup_type >= type_count )
+      OTL_INVALID_DATA;
+
+    validate = type_funcs[ lookup_type - 1 ];
+
+    OTL_CHECK( 2*count );
+    for ( ; count > 0; count-- )
+      validate( table + OTL_NEXT_USHORT( p ), valid );
+  }
+
+
+  OTL_LOCALDEF( void )
+  otl_lookup_list_validate( OTL_Bytes          table,
+                            OTL_UInt           type_count,
+                            OTL_ValidateFunc*  type_funcs,
+                            OTL_Validator      valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( 2*count );
+    for ( ; count > 0; count-- )
+      otl_lookup_table_validate( table + OTL_NEXT_USHORT( p ),
+                                 type_count, type_funcs, valid );
+  }
 
 /* END */
--- a/src/otlayout/otlcommn.h
+++ b/src/otlayout/otlcommn.h
@@ -208,6 +208,65 @@
                             OTL_ForeachFunc  func,
                             OTL_Pointer      func_data );
 
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                       LANGUAGE SYSTEM                         *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  OTL_LOCAL( void )
+  otl_lang_validate( OTL_Bytes      table,
+                     OTL_Validator  valid );
+
+
+  OTL_LOCAL( OTL_UInt )
+  otl_lang_get_req_feature( OTL_Bytes  table );
+
+
+  OTL_LOCAL( OTL_UInt )
+  otl_lang_get_count( OTL_Bytes  table );
+
+
+  OTL_LOCAL( OTL_UInt )
+  otl_lang_get_features( OTL_Bytes  table,
+                         OTL_UInt   start,
+                         OTL_UInt   count,
+                         OTL_UInt  *features );
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                           SCRIPTS                             *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  OTL_LOCAL( void )
+  otl_script_list_validate( OTL_Bytes          list,
+                            OTL_Validator      valid );
+
+  OTL_LOCAL( OTL_Bytes )
+  otl_script_list_get_script( OTL_Bytes  table );
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                         LOOKUP LISTS                          *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  OTL_LOCAL( void )
+  otl_lookup_list_validate( OTL_Bytes          list,
+                            OTL_UInt           type_count,
+                            OTL_ValidateFunc*  type_funcs,
+                            OTL_Validator      valid );
+
  /* */
 
 OTL_END_HEADER
--- /dev/null
+++ b/src/otlayout/otlconf.h
@@ -1,0 +1,73 @@
+#ifndef __OT_LAYOUT_CONFIG_H__
+#define __OT_LAYOUT_CONFIG_H__
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                    CONFIGURATION MACROS                      *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+#ifdef __cplusplus
+#  define OTL_BEGIN_HEADER  extern "C" {
+#else
+#  define OTL_BEGIN_HEADER  /* nothing */
+#endif
+
+#ifdef __cplusplus
+#  define OTL_END_HEADER   }
+#else
+#  define OTL_END_HEADER   /* nothing */
+#endif
+
+#ifndef OTL_API
+#  ifdef __cplusplus
+#    define  OTL_API( x )   extern "C"
+#  else
+#    define  OTL_API( x )   extern x
+#  endif
+#endif
+
+#ifndef OTL_APIDEF
+#  define  OTL_APIDEF( x )  x
+#endif
+
+#ifndef OTL_LOCAL
+#  define OTL_LOCAL( x )   extern x
+#endif
+
+#ifndef OTL_LOCALDEF
+#  define OTL_LOCALDEF( x )  x
+#endif
+
+#define  OTL_BEGIN_STMNT  do {
+#define  OTL_END_STMNT    } while (0)
+#define  OTL_DUMMY_STMNT  do { } while (0)
+
+#define  OTL_UNUSED( x )       (x)=(x)
+#define  OTL_UNUSED_CONST(x)  (void)(x)
+
+
+#include <limits.h>
+#if UINT_MAX == 0xFFFFU
+#  define OTL_SIZEOF_INT  2
+#elif UINT_MAX == 0xFFFFFFFFU
+#  define OTL_SIZEOF_INT  4
+#elif UINT_MAX > 0xFFFFFFFFU && UINT_MAX == 0xFFFFFFFFFFFFFFFFU
+#  define OTL_SIZEOF_INT  8
+#else
+#  error  "unsupported number of bytes in 'int' type!"
+#endif
+
+#if ULONG_MAX == 0xFFFFFFFFU
+#  define  OTL_SIZEOF_LONG  4
+#elif ULONG_MAX > 0xFFFFFFFFU && ULONG_MAX == 0xFFFFFFFFFFFFFFFFU
+#  define  OTL_SIZEOF_LONG  8
+#else
+#  error  "unsupported number of bytes in 'long' type!"
+#endif
+
+/* */
+
+#endif /* __OT_LAYOUT_CONFIG_H__ */
--- /dev/null
+++ b/src/otlayout/otlgdef.c
@@ -1,0 +1,175 @@
+#include "otlgdef.h"
+#include "otlcommn.h"
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                      ATTACHMENTS LIST                        *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_attach_point_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    if ( p + 2 > valid->limit )
+      OTL_INVALID_TOO_SHORT;
+
+    count = OTL_NEXT_USHORT( p );
+    if ( table + count*2 > valid->limit )
+      OTL_INVALID_TOO_SHORT;
+  }
+
+
+  static void
+  otl_attach_list_validate( OTL_Bytes      table,
+                            OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_Bytes  coverage;
+    OTL_UInt   count;
+
+    if ( p + 4 > valid->limit )
+      OTL_INVALID_TOO_SHORT;
+
+    coverage = table + OTL_NEXT_USHORT( p );
+    count    = OTL_NEXT_USHORT( p );
+
+    otl_coverage_validate( coverage, valid );
+    if ( count != otl_coverage_get_count( coverage ) )
+      OTL_INVALID_DATA;
+
+    if ( p + count*2 > valid->limit )
+      OTL_INVALID_TOO_SHORT;
+
+    for ( ; count > 0; count-- )
+      otl_attach_point_validate( table + OTL_NEXT_USHORT( p ) );
+  }
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                      LIGATURE CARETS                         *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_caret_value_validate( OTL_Bytes      table,
+                            OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+
+    if ( p + 4 > valid->limit )
+      OTL_INVALID_TOO_SHORT;
+
+    format = OTL_NEXT_USHORT( p );
+    switch ( format )
+    {
+      case 1:
+      case 2:
+        break;
+
+      case 3:
+        {
+          OTL_Bytes  device;
+
+          p += 2;
+          if ( p + 2 > valid->limit )
+            OTL_INVALID_TOO_SHORT;
+
+          otl_device_table_validate( table + OTL_PEEK_USHORT( p ) );
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+
+  static void
+  otl_ligature_glyph_validate( OTL_Bytes      table,
+                               OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    if ( p + 2 > valid->limit )
+      OTL_INVALID_TOO_SHORT;
+
+    count = OTL_NEXT_USHORT( p );
+
+    if ( p + count*2 > valid->limit )
+      OTL_INVALID_TOO_SHORT;
+
+    for ( ; count > 0; count-- )
+      otl_caret_value_validate( table + OTL_NEXT_USHORT( p ) );
+  }
+
+
+  static void
+  otl_ligature_caret_list_validate( OTL_Bytes      table,
+                                    OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_Bytes  coverage;
+    OTL_UInt   count;
+
+    if ( p + 4 > valid->limit )
+      OTL_INVALID_TOO_SHORT;
+
+    coverage = table + OTL_NEXT_USHORT( p );
+    count    = OTL_NEXT_USHORT( p );
+
+    otl_coverage_validate( coverage, valid );
+    if ( count != otl_coverage_get_count( coverage ) )
+      OTL_INVALID_DATA;
+
+    if ( p + count*2 > valid->limit )
+      OTL_INVALID_TOO_SHORT;
+
+    for ( ; count > 0; count-- )
+      otl_ligature_glyph_validate( table + OTL_NEXT_USHORT( p ) );
+  }
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                         GDEF TABLE                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  OTL_APIDEF( void )
+  otl_gdef_validate( OTL_Bytes      table,
+                     OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+
+    if ( p + 12 > valid->limit )
+      OTL_INVALID_TOO_SHORT;
+
+    /* check format */
+    if ( OTL_NEXT_ULONG( p ) != 0x00010000UL )
+      OTL_INVALID_FORMAT;
+
+    /* validate class definition table */
+    otl_class_definition_validate( table + OTL_NEXT_USHORT( p ) );
+
+    /* validate attachment point list */
+    otl_attach_list_validate( table + OTL_NEXT_USHORT( p ) );
+
+    /* validate ligature caret list */
+    otl_ligature_caret_list_validate( table + OTL_NEXT_USHORT( p ) );
+
+    /* validate mark attach class */
+    otl_class_definition_validate( table + OTL_NEXT_USHORT( p ) );
+  }
+
--- /dev/null
+++ b/src/otlayout/otlgdef.h
@@ -1,0 +1,14 @@
+#ifndef __OTL_GDEF_H__
+#define __OTL_GDEF_H__
+
+#include "otltable.h"
+
+OTL_BEGIN_HEADER
+
+  OTL_API( void )
+  otl_gdef_validate( OTL_Bytes  table,
+                     OTL_Valid  valid );
+
+OTL_END_HEADER
+
+#endif /* __OTL_GDEF_H__ */
--- /dev/null
+++ b/src/otlayout/otlgpos.c
@@ -1,0 +1,980 @@
+#include "otlgpos.h"
+#include "otlcommn.h"
+
+ /* forward declaration */
+  static OTL_ValidateFunc  otl_gpos_validate_funcs[];
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                         VALUE RECORDS                        *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static OTL_UInt
+  otl_value_length( OTL_UInt  format )
+  {
+    FT_UInt  count;
+
+    count = (( format & 0xAA ) >> 1) + ( format & 0x55 );
+    count = (( count  & 0xCC ) >> 2) + ( count  & 0x33 );
+    count = (( count  & 0xF0 ) >> 4) + ( count  & 0x0F );
+
+    return count;
+  }
+
+
+  static void
+  otl_value_validate( OTL_Bytes      table,
+                      OTL_Bytes      pos_table,
+                      OTL_UInt       format,
+                      OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count, device;
+
+    if ( format >= 0x100U )
+      OTL_INVALID_DATA;
+
+    for ( count = 4; count > 0; count-- )
+    {
+      if ( format & 1 )
+      {
+        OTL_CHECK( 2 );
+        p += 2;
+      }
+
+      format >>= 1;
+    }
+
+    for ( count = 4; count > 0; count-- )
+    {
+      if ( format & 1 )
+      {
+        OTL_CHECK( 2 );
+        device = OTL_NEXT_USHORT( p );
+        if ( device )
+          otl_device_table_validate( pos_table + device, valid );
+      }
+      format >>= 1;
+    }
+  }
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                          ANCHORS                             *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_anchor_validate( OTL_Bytes      table,
+                       OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format;
+
+    OTL_CHECK( 6 );
+    format = OTL_NEXT_USHORT( p );
+    p += 4;
+
+    switch ( format )
+    {
+      case 1:
+        break;
+
+      case 2:
+        OTL_CHECK( 2 );  /* anchor point */
+        break;
+
+      case 3:
+        {
+          OTL_UInt  x_device, y_device;
+
+          OTL_CHECK( 4 );
+          x_device = OTL_NEXT_USHORT( p );
+          y_device = OTL_NEXT_USHORT( p );
+
+          if ( x_device )
+            otl_device_table_validate( table + x_device, valid );
+
+          if ( y_device )
+            otl_device_table_validate( table + y_device, valid );
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                           MARK ARRAY                         *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_mark_array_validate( OTL_Bytes      table,
+                           OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+
+    count = OTL_NEXT_USHORT( p );
+    OTL_CHECK( count * 4 );
+    for ( ; count > 0; count-- )
+    {
+      p += 2;  /* ignore class index */
+      otl_anchor_validate( table + OTL_NEXT_USHORT( p ), valid );
+    }
+  }
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GPOS LOOKUP TYPE 1                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_gpos_lookup1_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch ( format )
+    {
+      case 1:
+        {
+          FT_UInt  coverage, value_format;
+
+          OTL_CHECK( 4 );
+          coverage     = OTL_NEXT_USHORT( p );
+          value_format = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage, valid );
+          otl_value_validate( p, table, value_format, valid );
+        }
+        break;
+
+      case 2:
+        {
+          FT_UInt  coverage, value_format, count, len;
+
+          OTL_CHECK( 6 );
+          coverage     = OTL_NEXT_USHORT( p );
+          value_format = OTL_NEXT_USHORT( p );
+          count        = OTL_NEXT_USHORT( p );
+          len          = otl_value_length( value_format );
+
+          otl_coverage_validate( table + coverage, valid );
+
+          OTL_CHECK( count * len );
+          for ( ; count > 0; count-- )
+          {
+            otl_value_validate( p, table, value_format, valid );
+            p += len;
+          }
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GPOS LOOKUP TYPE 2                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static otl_gpos_pairset_validate( OTL_Bytes      table,
+                                    OTL_Bytes      pos_table,
+                                    OTL_UInt       format1,
+                                    OTL_UInt       format2,
+                                    OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   len1, len2, count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+    len1  = otl_value_length( format1 );
+    len2  = otl_value_length( format2 );
+
+    OTL_CHECK( count * (len1+len2+2) );
+    for ( ; count > 0; count-- )
+    {
+      p += 2;  /* ignore glyph id */
+      otl_value_validate( p, pos_table, format1, valid );
+      p += len1;
+
+      otl_value_validate( p, pos_table, format2, valid );
+      p += len2;
+    }
+  }
+
+  static void
+  otl_gpos_lookup2_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch (format)
+    {
+      case 1:
+        {
+          OTL_UInt  coverage, value1, value2, count;
+
+          OTL_CHECK( 8 );
+          coverage = OTL_NEXT_USHORT( p );
+          value1   = OTL_NEXT_USHORT( p );
+          value2   = OTL_NEXT_USHORT( p );
+          count    = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage, valid );
+
+          OTL_CHECK( count*2 );
+          for ( ; count > 0; count-- )
+          {
+            otl_gpos_pairset_validate( table + OTL_NEXT_USHORT( p ),
+                                       table, value1, value2, valid );
+          }
+        }
+        break;
+
+      case 2:
+        {
+          OTL_UInt  coverage, value1, value2, class1, class2, count1, count2;
+          OTL_UInt  len1, len2;
+
+          OTL_CHECK( 14 );
+          coverage = OTL_NEXT_USHORT( p );
+          value1   = OTL_NEXT_USHORT( p );
+          value2   = OTL_NEXT_USHORT( p );
+          class1   = OTL_NEXT_USHORT( p );
+          class2   = OTL_NEXT_USHORT( p );
+          count1   = OTL_NEXT_USHORT( p );
+          count2   = OTL_NEXT_USHORT( p );
+
+          len1 = otl_value_length( value1 );
+          len2 = otl_value_length( value2 );
+
+          otl_coverage_validate( table + coverage, valid );
+
+          OTL_CHECK( count1*count2*(len1+len2) );
+          for ( ; count1 > 0; count1-- )
+          {
+            for ( ; count2 > 0; count2-- )
+            {
+              otl_value_validate( p, table, value1, valid );
+              p += len1;
+
+              otl_value_validate( p, table, value2, valid );
+              p += len2;
+            }
+          }
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GPOS LOOKUP TYPE 3                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_gpos_lookup3_validate( OTL_Bytes  table,
+                             OTL_Valid  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch (format)
+    {
+      case 1:
+        {
+          OTL_UInt  coverage, count, anchor1, anchor2;
+
+          OTL_CHECK( 4 );
+          coverage = OTL_NEXT_USHORT( p );
+          count    = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage, valid );
+
+          OTL_CHECK( count*4 );
+          for ( ; count > 0; count-- )
+          {
+            anchor1 = OTL_NEXT_USHORT( p );
+            anchor2 = OTL_NEXT_USHORT( p );
+
+            if ( anchor1 )
+              otl_anchor_validate( table + anchor1, valid );
+
+            if ( anchor2 )
+              otl_anchor_validate( table + anchor2, valid );
+          }
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GPOS LOOKUP TYPE 4                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_base_array_validate( OTL_Bytes      table,
+                           OTL_UInt       class_count,
+                           OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count, count2;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( count*class_count*2 );
+    for ( ; count > 0; count-- )
+      for ( count2 = class_count; count2 > 0; count2-- )
+        otl_anchor_validate( table + OTL_NEXT_USHORT( p ) );
+  }
+
+
+  static void
+  otl_gpos_lookup4_validate( OTL_Bytes  table,
+                             OTL_Valid  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch (format)
+    {
+      case 1:
+        {
+          OTL_UInt  mark_coverage, base_coverage, class_count;
+          OTL_UInt  mark_array, base_array;
+
+          OTL_CHECK( 10 );
+          mark_coverage = OTL_NEXT_USHORT( p );
+          base_coverage = OTL_NEXT_USHORT( p );
+          class_count   = OTL_NEXT_USHORT( p );
+          mark_array    = OTL_NEXT_USHORT( p );
+          base_array    = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + mark_coverage, valid );
+          otl_coverage_validate( table + base_coverage, valid );
+
+          otl_mark_array_validate( table + mark_array, valid );
+          otl_base_array_validate( table, class_count, valid );
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GPOS LOOKUP TYPE 5                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_liga_attach_validate( OTL_Bytes      table,
+                            OTL_UInt       class_count,
+                            OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count, count2;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( count*class_count*2 );
+    for ( ; count > 0; count-- )
+      for ( count2 = class_count; class_count > 0; class_count-- )
+        otl_anchor_validate( table + OTL_NEXT_USHORT( p ), valid );
+  }
+
+
+  static void
+  otl_liga_array_validate( OTL_Bytes      table,
+                           OTL_UInt       class_count,
+                           OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count, count2;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( count*2 );
+    for ( ; count > 0; count-- )
+      otl_liga_attach_validate( table + OTL_NEXT_USHORT( p ), valid );
+  }
+
+
+  static void
+  otl_gpos_lookup5_validate( OTL_Bytes  table,
+                             OTL_Valid  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch (format)
+    {
+      case 1:
+        {
+          OTL_UInt  mark_coverage, lig_coverage, class_count;
+          OTL_UInt  mar_array, lig_array;
+
+          OTL_CHECK( 10 );
+          mark_coverage = OTL_NEXT_USHORT( p );
+          liga_coverage = OTL_NEXT_USHORT( p );
+          class_count   = OTL_NEXT_USHORT( p );
+          mark_array    = OTL_NEXT_USHORT( p );
+          liga_array    = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + mark_coverage, valid );
+          otl_coverage_validate( table + liga_coverage, valid );
+
+          otl_mark_array_validate( table + mark_array, valid );
+          otl_liga_array_validate( table + liga_array, class_count, valid );
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GPOS LOOKUP TYPE 6                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+
+  static void
+  otl_mark2_array_validate( OTL_Bytes      table,
+                            OTL_UInt       class_count,
+                            OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count, count2;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( count*class_count*2 );
+    for ( ; count > 0; count-- )
+      for ( count2 = class_count; class_count > 0; class_count-- )
+        otl_anchor_validate( table + OTL_NEXT_USHORT( p ), valid );
+  }
+
+
+  static void
+  otl_gpos_lookup6_validate( OTL_Bytes  table,
+                             OTL_Valid  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch (format)
+    {
+      case 1:
+        {
+          OTL_UInt  coverage1, coverage2, class_count, array1, array2;
+
+          OTL_CHECK( 10 );
+          coverage1   = OTL_NEXT_USHORT( p );
+          coverage2   = OTL_NEXT_USHORT( p );
+          class_count = OTL_NEXT_USHORT( p );
+          array1      = OTL_NEXT_USHORT( p );
+          array2      = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage1, valid );
+          otl_coverage_validate( table + coverage2, valid );
+
+          otl_mark_array_validate( table + array1, valid );
+          otl_mark2_array_validate( table + array2, valid );
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GPOS LOOKUP TYPE 7                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_pos_rule_validate( OTL_Bytes      table,
+                         OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   glyph_count, pos_count;
+
+    OTL_CHECK( 4 );
+    glyph_count = OTL_NEXT_USHORT( p );
+    pos_count   = OTL_NEXT_USHORT( p );
+
+    if ( glyph_count == 0 )
+      OTL_INVALID_DATA;
+
+    OTL_CHECK( (glyph_count-1)*2 + pos_count*4 );
+
+    /* XXX: check glyph indices and pos lookups */
+  }
+
+
+  static void
+  otl_pos_rule_set_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( count*2 );
+    for ( ; count > 0; count-- )
+      otl_pos_rule_validate( table + OTL_NEXT_USHORT(p), valid );
+  }
+
+
+
+  static void
+  otl_pos_class_rule_validate( OTL_Bytes      table,
+                               OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   glyph_count, pos_count;
+
+    OTL_CHECK( 4 );
+    glyph_count = OTL_NEXT_USHORT( p );
+    pos_count   = OTL_NEXT_USHORT( p );
+
+    if ( glyph_count == 0 )
+      OTL_INVALID_DATA;
+
+    OTL_CHECK( (glyph_count-1)*2 + pos_count*4 );
+
+    /* XXX: check glyph indices and pos lookups */
+  }
+
+
+  static void
+  otl_pos_class_set_validate( OTL_Bytes      table,
+                              OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( count*2 );
+    for ( ; count > 0; count-- )
+      otl_pos_rule_validate( table + OTL_NEXT_USHORT(p), valid );
+  }
+
+
+  static void
+  otl_gpos_lookup7_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch (format)
+    {
+      case 1:
+        {
+          OTL_UInt  coverage, count;
+
+          OTL_CHECK( 4 );
+          coverage = OTL_NEXT_USHORT( p );
+          count    = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage, valid );
+
+          OTL_CHECK( count*2 );
+          for ( ; count > 0; count-- )
+            otl_pos_rule_set_validate( table + OTL_NEXT_USHORT( p ), valid );
+        }
+        break;
+
+      case 2:
+        {
+          OTL_UInt  coverage, class_def, count;
+
+          OTL_CHECK( 6 );
+          coverage  = OTL_NEXT_USHORT( p );
+          class_def = OTL_NEXT_USHORT( p );
+          count     = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate        ( table + coverage, valid );
+          otl_class_definition_validate( table + class_def, valid );
+
+          OTL_CHECK( count*2 );
+          for ( ; count > 0; count-- )
+            otl_
+        }
+        break;
+
+      case 3:
+        {
+          OTL_UInt  glyph_count, pos_count;
+
+          OTL_CHECK( 4 );
+          glyph_count = OTL_NEXT_USHORT( p );
+          pos_count   = OTL_NEXT_USHORT( p );
+
+          OTL_CHECK( glyph_count*2 + pos_count*4 );
+          for ( ; glyph_count > 0; glyph_count )
+            otl_coverage_validate( table + OTL_NEXT_USHORT( p ), valid );
+
+          /* XXX: check pos lookups */
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GPOS LOOKUP TYPE 8                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_chain_pos_rule_validate( OTL_Bytes      table,
+                               OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   back_count, input_count, ahead_count, pos_count;
+
+    OTL_CHECK( 2 );
+    back_count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( back_count*2 + 2 );
+    p += back_count*2;
+
+    input_count = OTL_NEXT_USHORT( p );
+    if ( input_count == 0 )
+      OTL_INVALID_DATA;
+
+    OTL_CHECK( input_count*2 );
+    p += (input_count-1)*2;
+
+    ahead_count = OTL_NEXT_USHORT( p );
+    OTL_CHECK( ahead_count*2 + 2 );
+    p += ahead_count*2;
+
+    pos_count = OTL_NEXT_USHORT( p );
+    OTL_CHECK( pos_count*4 );
+  }
+
+
+  static void
+  otl_chain_pos_rule_set_validate( OTL_Bytes      table,
+                                   OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( 2*count );
+    for ( ; count > 0; count-- )
+      otl_chain_pos_rule_validate( table + OTL_NEXT_USHORT( p ), valid );
+  }
+
+
+
+  static void
+  otl_chain_pos_class_rule_validate( OTL_Bytes      table,
+                                     OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   back_count, input_count, ahead_count, pos_count;
+
+    OTL_CHECK( 2 );
+    back_count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( back_count*2 + 2 );
+    p += back_count*2;
+
+    input_count = OTL_NEXT_USHORT( p );
+    if ( input_count == 0 )
+      OTL_INVALID_DATA;
+
+    OTL_CHECK( input_count*2 );
+    p += (input_count-1)*2;
+
+    ahead_count = OTL_NEXT_USHORT( p );
+    OTL_CHECK( ahead_count*2 + 2 );
+    p += ahead_count*2;
+
+    pos_count = OTL_NEXT_USHORT( p );
+    OTL_CHECK( pos_count*4 );
+  }
+
+
+  static void
+  otl_chain_pos_class_set_validate( OTL_Bytes      table,
+                                   OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( 2*count );
+    for ( ; count > 0; count-- )
+      otl_chain_pos_class_rule_validate( table + OTL_NEXT_USHORT( p ), valid );
+  }
+
+
+  static void
+  otl_gpos_lookup8_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch (format)
+    {
+      case 1:
+        {
+          OTL_UInt  coverage, count;
+
+          OTL_CHECK( 4 );
+          coverage = OTL_NEXT_USHORT( p );
+          count    = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage, valid );
+
+          OTL_CHECK( count*2 );
+          for ( ; count > 0; count-- )
+            otl_chain_pos_rule_set_validate( table + OTL_NEXT_USHORT( p ),
+                                             valid );
+        }
+        break;
+
+      case 2:
+        {
+          OTL_UInt  coverage, back_class, input_class, ahead_class, count;
+
+          OTL_CHECK( 10 );
+          coverage    = OTL_NEXT_USHORT( p );
+          back_class  = OTL_NEXT_USHORT( p );
+          input_class = OTL_NEXT_USHORT( p );
+          ahead_class = OTL_NEXT_USHORT( p );
+          count       = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage, valid );
+
+          otl_class_definition_validate( table + back_class,  valid );
+          otl_class_definition_validate( table + input_class, valid );
+          otl_class_definition_validate( table + ahead_class, valid );
+
+          OTL_CHECK( count*2 );
+          for ( ; count > 0; count-- )
+            otl_chain_pos_class_set_validate( table + OTL_NEXT_USHORT( p ),
+                                              valid );
+        }
+        break;
+
+      case 3:
+        {
+          OTL_UInt  back_count, input_count, ahead_count, pos_count, count;
+
+          OTL_CHECK( 2 );
+          back_count = OTL_NEXT_USHORT( p );
+
+          OTL_CHECK( 2*back_count+2 );
+          for ( count = back_count; count > 0; count-- )
+            otl_coverage_validate( table + OTL_NEXT_USHORT( p ), valid );
+
+          input_count = OTL_NEXT_USHORT( p );
+
+          OTL_CHECK( 2*input_count+2 );
+          for ( count = input_count; count > 0; count-- )
+            otl_coverage_validate( table + OTL_NEXT_USHORT( p ), valid );
+
+          ahead_count = OTL_NEXT_USHORT( p );
+
+          OTL_CHECK( 2*ahead_count+2 );
+          for ( count = ahead_count; count > 0; count-- )
+            otl_coverage_validate( table + OTL_NEXT_USHORT( p ), valid );
+
+          pos_count = OTL_NEXT_USHORT( p );
+          OTL_CHECK( pos_count*4 );
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GPOS LOOKUP TYPE 9                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_gpos_lookup9_validate( OTL_Bytes  table,
+                             OTL_Valid  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch (format)
+    {
+      case 1:
+        {
+          OTL_UInt          lookup_type, lookup_offset;
+          OTL_ValidateFunc  validate;
+
+          OTL_CHECK( 6 );
+          lookup_type   = OTL_NEXT_USHORT( p );
+          lookup_offset = OTL_NEXT_ULONG( p );
+
+          if ( lookup_type == 0 || lookup_type >= 9 )
+            OTL_INVALID_DATA;
+
+          validate = otl_gpos_validate_funcs[ lookup_type-1 ];
+          validate( table + lookup_offset, valid );
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+  static OTL_ValidateFunc  otl_gpos_validate_funcs[ 9 ] =
+  {
+    otl_gpos_lookup1_validate,
+    otl_gpos_lookup2_validate,
+    otl_gpos_lookup3_validate,
+    otl_gpos_lookup4_validate,
+    otl_gpos_lookup5_validate,
+    otl_gpos_lookup6_validate,
+    otl_gpos_lookup7_validate,
+    otl_gpos_lookup8_validate,
+    otl_gpos_lookup9_validate,
+  };
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                         GPOS TABLE                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+
+  OTL_LOCALDEF( void )
+  otl_gpos_validate( OTL_Bytes      table,
+                     OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   scripts, features, lookups;
+
+    OTL_CHECK( 10 );
+
+    if ( OTL_NEXT_USHORT( p ) != 0x10000UL )
+      OTL_INVALID_DATA;
+
+    scripts  = OTL_NEXT_USHORT( p );
+    features = OTL_NEXT_USHORT( p );
+    lookups  = OTL_NEXT_USHORT( p );
+
+    otl_script_list_validate ( table + scripts, valid );
+    otl_feature_list_validate( table + features, valid );
+
+    otl_lookup_list_validate( table + lookups, 9, otl_gpos_validate_funcs,
+                              valid );
+  }
+  
\ No newline at end of file
--- /dev/null
+++ b/src/otlayout/otlgpos.h
@@ -1,0 +1,14 @@
+#ifndef __OTL_GPOS_H__
+#define __OTL_GPOS_H__
+
+#include "otlayout.h"
+
+OTL_BEGIN_HEADER
+
+  OTL_LOCAL( void )
+  otl_gpos_validate( OTL_Bytes      table,
+                     OTL_Validator  valid );
+
+OTL_END_HEADER
+
+#endif /* __OTL_GPOS_H__ */
--- /dev/null
+++ b/src/otlayout/otlgsub.c
@@ -1,0 +1,654 @@
+#include "otlgsub.h"
+#include "otlcommn.h"
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GSUB LOOKUP TYPE 1                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_gsub_lookup1_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch ( format )
+    {
+      case 1:
+        {
+          OTL_UInt  coverage;
+
+          OTL_CHECK( 4 );
+          coverage = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage, valid );
+        }
+        break;
+
+      case 2:
+        {
+          OTL_UInt  coverage, count;
+
+          OTL_CHECK( 4 );
+          coverage = OTL_NEXT_USHORT( p );
+          count    = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage, valid );
+
+          OTL_CHECK( 2*count );
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GSUB LOOKUP TYPE 2                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_seq_validate( OTL_Bytes      table,
+                    OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( 2*count );
+    /* check glyph indices */
+  }
+
+
+  static void
+  otl_gsub_lookup2_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format, coverage;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch ( format )
+    {
+      case 1:
+        {
+          OTL_UInt  coverage, seq_count;
+
+          OTL_CHECK( 4 );
+          coverage  = OTL_NEXT_USHORT( p );
+          seq_count = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage, valid );
+
+          OTL_CHECK( seq_count*2 );
+          for ( ; seq_count > 0; seq_count-- )
+            otl_seq_validate( table + OTL_NEXT_USHORT( p ), valid );
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GSUB LOOKUP TYPE 3                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_alternate_set_validate( OTL_Bytes      table,
+                              OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( 2*count );
+    /* XXX: check glyph indices */
+  }
+
+
+  static void
+  otl_gsub_lookup3_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format, coverage;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch ( format )
+    {
+      case 1:
+        {
+          OTL_UInt  coverage, count;
+
+          OTL_CHECK( 4 );
+          coverage = OTL_NEXT_USHORT( p );
+          count    = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage, valid );
+
+          OTL_CHECK( 2*count );
+          for ( ; count > 0; count-- )
+            otl_alternate_set_validate( table + OTL_NEXT_USHORT( p ), valid );
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GSUB LOOKUP TYPE 4                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_ligature_validate( OTL_Bytes      table,
+                         OTL_Validator  valid )
+  {
+    OTL_UInt  glyph_id, count;
+
+    OTL_CHECK( 4 );
+    glyph_id = OTL_NEXT_USHORT( p );
+    count    = OTL_NEXT_USHORT( p );
+
+    if ( count == 0 )
+      OTL_INVALID_DATA;
+
+    OTL_CHECK( 2*(count-1) );
+    /* XXX: check glyph indices */
+  }
+
+
+  static void
+  otl_ligature_set_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( 2*count );
+    for ( ; count > 0; count-- )
+      otl_ligature_validate( table + OTL_NEXT_USHORT( p ), valid );
+  }
+
+
+  static void
+  otl_gsub_lookup4_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format, coverage;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch ( format )
+    {
+      case 1:
+        {
+          OTL_UInt  coverage, count;
+
+          OTL_CHECK( 4 );
+          coverage = OTL_NEXT_USHORT( p );
+          count    = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage, valid );
+
+          OTL_CHECK( 2*count );
+          for ( ; count > 0; count-- )
+            otl_ligature_set_validate( table + OTL_NEXT_USHORT( p ), valid );
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GSUB LOOKUP TYPE 5                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+
+  static void
+  otl_sub_rule_validate( OTL_Bytes      table,
+                         OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   glyph_count, subst_count;
+
+    OTL_CHECK( 4 );
+    glyph_count = OTL_NEXT_USHORT( p );
+    subst_count = OTL_NEXT_USHORT( p );
+
+    if ( glyph_count == 0 )
+      OTL_INVALID_DATA;
+
+    OTL_CHECK( (glyph_count-1)*2 + substcount*4 );
+
+    /* XXX: check glyph indices and subst lookups */
+  }
+
+
+  static void
+  otl_sub_rule_set_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( 2*count );
+    for ( ; count > 0; count-- )
+      otl_sub_rule_validate( table + OTL_NEXT_USHORT( p ), valid );
+  }
+
+
+  static void
+  otl_sub_class_rule_validate( OTL_Bytes      table,
+                               OTL_Validator  valid )
+  {
+    OTL_UInt  glyph_count, subst_count;
+
+    OTL_CHECK( 4 );
+    glyph_count = OTL_NEXT_USHORT( p );
+    subst_count = OTL_NEXT_USHORT( p );
+
+    if ( glyph_count == 0 )
+      OTL_INVALID_DATA;
+
+    OTL_CHECK( (glyph_count-1)*2 + substcount*4 );
+
+    /* XXX: check glyph indices and subst lookups */
+  }
+
+
+  static void
+  otl_sub_class_rule_set_validate( OTL_Bytes      table,
+                                   OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( 2*count );
+    for ( ; count > 0; count-- )
+      otl_sub_class_rule_validate( table + OTL_NEXT_USHORT( p ), valid );
+  }
+
+
+  static void
+  otl_gsub_lookup5_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format, coverage;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch ( format )
+    {
+      case 1:
+        {
+          OTL_UInt  coverage, count;
+
+          OTL_CHECK( 4 );
+          coverage = OTL_NEXT_USHORT( p );
+          count    = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage, valid );
+
+          OTL_CHECK( 2*count );
+          for ( ; count > 0; count-- )
+            otl_sub_rule_set_validate( table + coverage, valid );
+        }
+        break;
+
+      case 2:
+        {
+          OTL_UInt  coverage, class_def, count;
+
+          OTL_CHECK( 6 );
+          coverage  = OTL_NEXT_USHORT( p );
+          class_def = OTL_NEXT_USHORT( p );
+          count     = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate        ( table + coverage, valid );
+          otl_class_definition_validate( table + class_def, valid );
+
+          OTL_CHECK( 2*count );
+          for ( ; count > 0; count-- )
+            otl_sub_class_rule_set_validate( table + coveragen valid );
+        }
+        break;
+
+      case 3:
+        {
+          OTL_UInt  glyph_count, subst_count, count;
+
+          OTL_CHECK( 4 );
+          glyph_count = OTL_NEXT_USHORT( p );
+          subst_count = OTL_NEXT_USHORT( p );
+
+          OTL_CHECK( 2*glyph_count + 4*subst_count );
+          for ( count = glyph_count; count > 0; count-- )
+            otl_coverage_validate( table + OTL_NEXT_USHORT( p ), valid );
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GSUB LOOKUP TYPE 6                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+
+  static void
+  otl_chain_sub_rule_validate( OTL_Bytes      table,
+                               OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   back_count, input_count, ahead_count, subst_count, count;
+
+    OTL_CHECK( 2 );
+    back_count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( 2*back_count+2 );
+    p += 2*back_count;
+
+    input_count = OTL_NEXT_USHORT( p );
+    if ( input_count == 0 )
+      OTL_INVALID_DATA;
+
+    OTL_CHECK( 2*input_count );
+    p += 2*(input_count-1);
+
+    ahead_count = OTL_NEXT_USHORT( p );
+    OTL_CHECK( 2*ahead_count + 2 );
+    p += 2*ahead_count;
+
+    count = OTL_NEXT_USHORT( p );
+    OTL_CHECK( 4*count );
+
+    /* XXX: check glyph indices and subst lookups */
+  }
+
+
+  static void
+  otl_chain_sub_rule_set_validate( OTL_Bytes      table,
+                                   OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( 2*count );
+    for ( ; count > 0; count-- )
+      otl_chain_sub_rule_validate( table + OTL_NEXT_USHORT( p ), valid );
+  }
+
+
+  static void
+  otl_chain_sub_class_rule_validate( OTL_Bytes      table,
+                                     OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   back_count, input_count, ahead_count, subst_count, count;
+
+    OTL_CHECK( 2 );
+    back_count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( 2*back_count+2 );
+    p += 2*back_count;
+
+    input_count = OTL_NEXT_USHORT( p );
+    if ( input_count == 0 )
+      OTL_INVALID_DATA;
+
+    OTL_CHECK( 2*input_count );
+    p += 2*(input_count-1);
+
+    ahead_count = OTL_NEXT_USHORT( p );
+    OTL_CHECK( 2*ahead_count + 2 );
+    p += 2*ahead_count;
+
+    count = OTL_NEXT_USHORT( p );
+    OTL_CHECK( 4*count );
+
+    /* XXX: check class indices and subst lookups */
+  }
+
+
+
+  static void
+  otl_chain_sub_class_set_validate( OTL_Bytes      table,
+                                    OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( 2*count );
+    for ( ; count > 0; count-- )
+      otl_chain_sub_rule_validate( table + OTL_NEXT_USHORT( p ), valid );
+  }
+
+
+  static void
+  otl_gsub_lookup6_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format, coverage;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch ( format )
+    {
+      case 1:
+        {
+          OTL_UInt  coverage, count;
+
+          OTL_CHECK( 4 );
+          coverage = OTL_NEXT_USHORT( p );
+          count    = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage, valid );
+
+          OTL_CHECK( 2*count );
+          for ( ; count > 0; count-- )
+            otl_chain_sub_rule_set_validate( table + coverage, valid );
+        }
+        break;
+
+      case 2:
+        {
+          OTL_UInt  coverage, back_class, input_class, ahead_class, count;
+
+          OTL_CHECK( 10 );
+          coverage    = OTL_NEXT_USHORT( p );
+          back_class  = OTL_NEXT_USHORT( p );
+          input_class = OTL_NEXT_USHORT( p );
+          ahead_class = OTL_NEXT_USHORT( p );
+          count       = OTL_NEXT_USHORT( p );
+
+          otl_coverage_validate( table + coverage, valid );
+
+          otl_class_definition_validate( table + back_class,  valid );
+          otl_class_definition_validate( table + input_class, valid );
+          otl_class_definition_validate( table + ahead_class, valid );
+
+          OTL_CHECK( 2*count );
+          for ( ; count > 0; count-- )
+            otl_chain_sub_class_set( table + OTL_NEXT_USHORT( p ), valid );
+        }
+        break;
+
+      case 3:
+        {
+          OTL_UInt  back_count, input_count, ahead_count, subst_count, count;
+
+          OTL_CHECK( 2 );
+          back_count = OTL_NEXT_USHORT( p );
+
+          OTL_CHECK( 2*back_count+2 );
+          for ( count = back_count; count > 0; count-- )
+            otl_coverage_validate( table + OTL_NEXT_USHORT( p ), valid );
+
+          input_count = OTL_NEXT_USHORT( p );
+
+          OTL_CHECK( 2*input_count+2 );
+          for ( count = input_count; count > 0; count-- )
+            otl_coverage_validate( table + OTL_NEXT_USHORT( p ), valid );
+
+          ahead_count = OTL_NEXT_USHORT( p );
+
+          OTL_CHECK( 2*ahead_count+2 );
+          for ( count = ahead_count; count > 0; count-- )
+            otl_coverage_validate( table + OTL_NEXT_USHORT( p ), valid );
+
+          subst_count = OTL_NEXT_USHORT( p );
+          OTL_CHECK( subst_count*4 );
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                 GSUB LOOKUP TYPE 6                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+  static void
+  otl_gsub_lookup7_validate( OTL_Bytes      table,
+                             OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   format, coverage;
+
+    OTL_CHECK( 2 );
+    format = OTL_NEXT_USHORT( p );
+    switch ( format )
+    {
+      case 1:
+        {
+          OTL_UInt          lookup_type, lookup_offset;
+          OTL_ValidateFunc  validate;
+
+          OTL_CHECK( 6 );
+          lookup_type   = OTL_NEXT_USHORT( p );
+          lookup_offset = OTL_NEXT_ULONG( p );
+
+          if ( lookup_type == 0 || lookup_type >= 7 )
+            OTL_INVALID_DATA;
+
+          validate = otl_gsub_validate_funcs[ lookup_type-1 ];
+          validate( table + lookup_offset, valid );
+        }
+        break;
+
+      default:
+        OTL_INVALID_DATA;
+    }
+  }
+
+
+  static const OTL_ValidateFunc  otl_gsub_validate_funcs[ 7 ] =
+  {
+    otl_gsub_lookup1_validate,
+    otl_gsub_lookup2_validate,
+    otl_gsub_lookup3_validate,
+    otl_gsub_lookup4_validate,
+    otl_gsub_lookup5_validate,
+    otl_gsub_lookup6_validate
+  };
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                         GSUB TABLE                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+
+  OTL_LOCALDEF( void )
+  otl_gsub_validate( OTL_Bytes      table,
+                     OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   scripts, features, lookups;
+
+    OTL_CHECK( 10 );
+
+    if ( OTL_NEXT_USHORT( p ) != 0x10000UL )
+      OTL_INVALID_DATA;
+
+    scripts  = OTL_NEXT_USHORT( p );
+    features = OTL_NEXT_USHORT( p );
+    lookups  = OTL_NEXT_USHORT( p );
+
+    otl_script_list_validate ( table + scripts, valid );
+    otl_feature_list_validate( table + features, valid );
+
+    otl_lookup_list_validate( table + lookups, 7, otl_gsub_validate_funcs,
+                              valid );
+  }
--- /dev/null
+++ b/src/otlayout/otlgsub.h
@@ -1,0 +1,14 @@
+#ifndef __OTL_GSUB_H__
+#define __OTL_GSUB_H__
+
+#include "otlayout.h"
+
+OTL_BEGIN_HEADER
+
+  OTL_LOCAL( void )
+  otl_gsub_validate( OTL_Bytes      table,
+                     OTL_Validator  valid );
+
+OTL_END_HEADER
+
+#endif /* __OTL_GSUB_H__ */
--- /dev/null
+++ b/src/otlayout/otljstf.c
@@ -1,0 +1,189 @@
+#include "otljstf.h"
+#include "otlcommn.h"
+#include "otlgpos.h"
+
+  static void
+  otl_jstf_extender_validate( OTL_Bytes      table,
+                              OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( count*2 );
+  }
+
+
+  static void
+  otl_jstf_gsub_mods_validate( OTL_Bytes      table,
+                               OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+    OTL_CHECK( count*2 );
+
+    /* XXX: check GSUB lookup indices */
+  }
+
+
+  static void
+  otl_jstf_gpos_mods_validate( OTL_Bytes      table,
+                               OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+    count = OTL_NEXT_USHORT( p );
+    OTL_CHECK( count*2 );
+
+    /* XXX: check GPOS lookup indices */
+  }
+
+
+  static void
+  otl_jstf_max_validate( OTL_Bytes      table,
+                         OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( count*2 );
+    for ( ; count > 0; count-- )
+      otl_gpos_subtable_check( table + OTL_NEXT_USHORT( p ), valid );
+  }
+
+
+  static void
+  otl_jstf_priority_validate( OTL_Bytes      table,
+                              OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   offset;
+
+    OTL_CHECK( 20 );
+
+    /* shrinkage GSUB enable/disable */
+    val = OTL_NEXT_USHORT( p );
+    if ( val )
+      otl_jstf_gsub_mods_validate( table + val, valid );
+
+    val = OTL_NEXT_USHORT( p );
+    if ( val )
+      otl_jstf_gsub_mods_validate( table + val, valid );
+
+    /* shrinkage GPOS enable/disable */
+    val = OTL_NEXT_USHORT( p );
+    if ( val )
+      otl_jstf_gpos_mods_validate( table + val, valid );
+
+    val = OTL_NEXT_USHORT( p );
+    if ( val )
+      otl_jstf_gpos_mods_validate( table + val, valid );
+
+    /* shrinkage JSTF max */
+    val = OTL_NEXT_USHORT( p );
+    if ( val )
+      otl_jstf_max_validate( table + val, valid );
+
+    /* extension GSUB enable/disable */
+    val = OTL_NEXT_USHORT( p );
+    if ( val )
+      otl_jstf_gsub_mods_validate( table + val, valid );
+
+    val = OTL_NEXT_USHORT( p );
+    if ( val )
+      otl_jstf_gsub_mods_validate( table + val, valid );
+
+    /* extension GPOS enable/disable */
+    val = OTL_NEXT_USHORT( p );
+    if ( val )
+      otl_jstf_gpos_mods_validate( table + val, valid );
+
+    val = OTL_NEXT_USHORT( p );
+    if ( val )
+      otl_jstf_gpos_mods_validate( table + val, valid );
+
+    /* extension JSTF max */
+    val = OTL_NEXT_USHORT( p );
+    if ( val )
+      otl_jstf_max_validate( table + val, valid );
+  }
+
+  static void
+  otl_jstf_lang_validate( OTL_Bytes      table,
+                          OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 2 );
+
+    count = OTL_NEXT_USHORT( p );
+
+    OTL_CHECK( count*2 );
+    for ( ; count > 0; count-- )
+      otl_jstf_priority_validate( table + OTL_NEXT_USHORT( p ), valid );
+  }
+
+
+  static void
+  otl_jstf_script_validate( OTL_Bytes      table,
+                            OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count, extender, default_lang;
+
+    OTL_CHECK( 6 );
+    extender     = OTL_NEXT_USHORT( p );
+    default_lang = OTL_NEXT_USHORT( p );
+    count        = OTL_NEXT_USHORT( p );
+
+    if ( extender )
+      otl_jstf_extender_validate( table + extender, valid );
+
+    if ( default_lang )
+      otl_jstf_lang_validate( table + default_lang, valid );
+
+    OTL_CHECK( 6*count );
+
+    for ( ; count > 0; count-- )
+    {
+      p += 4;  /* ignore tag */
+      otl_jstf_lang_validate( table + OTL_NEXT_USHORT( p ), valid );
+    }
+  }
+
+
+  OTL_LOCALDEF( void )
+  otl_jstf_validate( OTL_Bytes      table,
+                     OTL_Validator  valid )
+  {
+    OTL_Bytes  p = table;
+    OTL_UInt   count;
+
+    OTL_CHECK( 4 );
+
+    if ( OTL_NEXT_ULONG( p ) != 0x10000UL )
+      OTL_INVALID_DATA;
+
+    count = OTL_NEXT_USHORT( p );
+    OTL_CHECK( count*6 );
+
+    for ( ; count > 0; count++ )
+    {
+      p += 4;  /* ignore tag */
+      otl_jstf_script_validate( table + OTL_NEXT_USHORT( p ), valid );
+    }
+  }
+  
\ No newline at end of file
--- /dev/null
+++ b/src/otlayout/otljstf.h
@@ -1,0 +1,14 @@
+#ifndef __OTL_JSTF_H__
+#define __OTL_JSTF_H__
+
+#include "otlayout.h"
+
+OTL_BEGIN_HEADER
+
+  OTL_LOCAL( void )
+  otl_jstf_validate( OTL_Bytes      table,
+                     OTL_Validator  valid );
+
+OTL_END_HEADER
+
+#endif /* __OTL_JSTF_H__ */
\ No newline at end of file
--- /dev/null
+++ b/src/otlayout/otltable.h
@@ -1,0 +1,60 @@
+#ifndef __OTL_TABLE_H__
+#define __OTL_TABLE_H__
+
+#include "otlayout.h"
+
+OTL_BEGIN_HEADER
+
+  typedef struct OTL_TableRec_*    OTL_Table;
+
+  typedef enum
+  {
+    OTL_TABLE_TYPE_GDEF = 1,
+    OTL_TABLE_TYPE_GSUB,
+    OTL_TABLE_TYPE_GPOS,
+    OTL_TABLE_TYPE_BASE,
+    OTL_TABLE_TYPE_JSTF
+
+  } OTL_TableType;
+
+
+ /* this may become a private structure later */
+  typedef struct OTL_TableRec_
+  {
+    OTL_TableType  type;
+    OTL_Bytes      base;
+    OTL_Bytes      limit;
+
+    OTL_Tag        script_tag;
+    OTL_Tag        lang_tag;
+
+    OTL_UInt       lookup_count;
+    OTL_Byte*      lookup_flags;
+
+    OTL_UInt       feature_count;
+    OTL_Tag        feature_tags;
+    OTL_Byte*      feature_flags;
+
+  } OTL_TableRec;
+
+
+  OTL_API( OTL_Error )
+  otl_table_validate( OTL_Bytes      table,
+                      OTL_Size       size,
+                      OTL_TableType  type,
+                      OTL_Size      *abyte_size );
+
+  OTL_API( void )
+  otl_table_init( OTL_Table      table,
+                  OTL_TableType  type,
+                  OTL_Bytes      base,
+                  OTL_Size       size );
+
+  OTL_API( void )
+  otl_table_set_script( OTL_Table      table,
+                        OTL_ScriptTag  script,
+                        OTL_LangTag    language );
+
+OTL_END_HEADER
+
+#endif /* __OTL_TABLE_H__ */
--- /dev/null
+++ b/src/otlayout/otltags.h
@@ -1,0 +1,86 @@
+/* this file may be included several times by other parts of */
+/* the OpenType Layout library.. don't add #ifdef .. #endif  */
+/* delimiters to it...                                       */
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                       SCRIPT TAGS                            *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+#ifndef OTL_SCRIPT_TAG
+#define OTL_SCRIPT_TAG(c1,c2,c3,c4,s,n)  /* void */
+#endif
+
+OTL_SCRIPT_TAG( 'a','r','a','b', "Arabic", ARABIC )
+OTL_SCRIPT_TAG( 'a','r','m','n', "Armenian", ARMENIAN )
+OTL_SCRIPT_TAG( 'b','e','n','g', "Bengali", BENGALI )
+OTL_SCRIPT_TAG( 'b','o','p','o', "Bopomofo", BOPOMOFO )
+OTL_SCRIPT_TAG( 'b','r','a','i', "Braille", BRAILLE )
+OTL_SCRIPT_TAG( 'c','a','n','s', "Canadian Syllabic", CANADIAN )
+OTL_SCRIPT_TAG( 'c','h','e','r', "Cherokee", CHEROKEE )
+OTL_SCRIPT_TAG( 'h','a','n','i', "CJK Ideographic", CJK )
+OTL_SCRIPT_TAG( 'c','y','r','l', "Cyrillic", CYRILLIC )
+OTL_SCRIPT_TAG( 'd','e','v','a', "Devanagari", DEVANAGARI )
+OTL_SCRIPT_TAG( 'e','t','h','i', "Ethiopic", ETHIOPIC )
+OTL_SCRIPT_TAG( 'g','e','o','r', "Georgian", GEORGIAN )
+OTL_SCRIPT_TAG( 'g','r','e','k', "Greek", GREEK )
+OTL_SCRIPT_TAG( 'g','u','j','r', "Gujarati", GUJARATI )
+OTL_SCRIPT_TAG( 'g','u','r','u', "Gurmukhi", GURMUKHI )
+OTL_SCRIPT_TAG( 'j','a','m','o', "Hangul Jamo", JAMO )
+OTL_SCRIPT_TAG( 'h','a','n','g', "Hangul", HANGUL )
+OTL_SCRIPT_TAG( 'h','e','b','r', "Hebrew", HEBREW )
+OTL_SCRIPT_TAG( 'h','i','r','a', "Hiragana", HIRAGANA )
+OTL_SCRIPT_TAG( 'k','n','d','a', "Kannada", KANNADA )
+OTL_SCRIPT_TAG( 'k','a','n','a', "Katakana", KATAKANA )
+OTL_SCRIPT_TAG( 'k','h','m','r', "Khmer", KHMER )
+OTL_SCRIPT_TAG( 'l','a','o',' ', "Lao", LAO )
+OTL_SCRIPT_TAG( 'l','a','t','n', "Latin", LATIN )
+OTL_SCRIPT_TAG( 'm','l','y','m', "Malayalam", MALAYALAM )
+OTL_SCRIPT_TAG( 'm','o','n','g', "Mongolian", MONGOLIAN )
+OTL_SCRIPT_TAG( 'm','y','m','r', "Myanmar", MYANMAR )
+OTL_SCRIPT_TAG( 'o','g','a','m', "Ogham", OGHAM )
+OTL_SCRIPT_TAG( 'o','r','y','a', "Oriya", ORIYA )
+OTL_SCRIPT_TAG( 'r','u','n','r', "Runic", RUNIC )
+OTL_SCRIPT_TAG( 's','i','n','h', "Sinhala", SINHALA )
+OTL_SCRIPT_TAG( 's','y','r','c', "Syriac", SYRIAC )
+OTL_SCRIPT_TAG( 't','a','m','l', "Tamil", TAMIL )
+OTL_SCRIPT_TAG( 't','e','l','u', "Telugu", TELUGU )
+OTL_SCRIPT_TAG( 't','h','a','a', "Thaana", THAANA )
+OTL_SCRIPT_TAG( 't','h','a','i', "Thai", THAI )
+OTL_SCRIPT_TAG( 't','i','b','t', "Tibetan", TIBETAN )
+OTL_SCRIPT_TAG( 'y','i',' ',' ', "Yi", YI )
+
+#undef OTL_SCRIPT_TAG
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                       LANGUAGE TAGS                          *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+#ifndef OTL_LANG_TAG
+#define OTL_LANG_TAG(c1,c2,c3,c4,s,n)  /* void */
+#endif
+
+#undef OTL_LANG_TAG
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****                       FEATURE TAGS                           *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
+
+#ifndef OTL_FEATURE_TAG
+#define OTL_FEATURE_TAG(c1,c2,c3,c4,s,n)  /* void */
+#endif
+
+#undef OTL_FEATURE_TAG
+
--- a/src/truetype/ttgload.c
+++ b/src/truetype/ttgload.c
@@ -1543,9 +1543,6 @@
     /* clear all outline flags, except the `owner' one */
     glyph->outline.flags = 0;
 
-    if ( size && size->root.metrics.y_ppem < 24 )
-      glyph->outline.flags |= ft_outline_high_precision;
-
     /* let's initialize the rest of our loader now */
 
     loader.load_flags    = load_flags;
@@ -1580,6 +1577,14 @@
       TT_Done_Context( loader.exec );
 
 #endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
+
+    /* Don't forget to set the 'high precision' bit flag !!        */
+    /* This is _critical_ to get correct output for monochrome     */
+    /* TrueType glyphs at all sizes using the bytecode interpreter */
+    /*                                                             */
+    if ( size && size->root.metrics.y_ppem < 24 )
+      glyph->outline.flags |= ft_outline_high_precision;
+
 
   Exit:
     return error;