(gstring_subst): Correctly set temp.f.index.
[m17n/libotf.git] / src / otfopen.c
index dbf990f..54091d0 100644 (file)
@@ -1,29 +1,30 @@
 /* otfopen.c -- OpenType font reader.
 
-Copyright (C) 2003
-  by AIST (National Institute of Advanced Industrial Science and Technology)
-  Registration Number H15PRO???
+Copyright (C) 2003, 2004
+  National Institute of Advanced Industrial Science and Technology (AIST)
+  Registration Number H15PRO167
 
-This file is part of the OTF library.
+This file is part of libotf.
 
-The OTF library is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2, or (at
-your option) any later version.
+Libotf is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published
+by the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
 
-The OTF library is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-General Public License for more details.
+Libotf is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
 
-You should have received a copy of the GNU General Public License
-along with the OTF library; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+You should have received a copy of the GNU Lesser General Public
+License along with this library, in a file named COPYING; if not,
+write to the Free Software Foundation, Inc., 59 Temple Place, Suite
+330, Boston, MA 02111-1307, USA.  */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <config.h>
 
 #include "otf.h"
 #include "otferror.h"
@@ -50,6 +51,7 @@ Boston, MA 02111-1307, USA.  */
     (2-1) OTF_open()
     (2-2) OTF_close()
     (2-3) OTF_get_table()
+    (2-4) OTF_check_table()
 
     (5) API miscellaneous
 */
@@ -335,52 +337,74 @@ read_head_table (OTF *otf, OTF_Stream *stream)
 \f
 /*** (1-3) "name" table */
 
-static char *
-read_name (OTF *otf, OTF_Stream *stream, OTF_NameRecord *rec, int bytes)
+static int
+read_name (OTF *otf, OTF_Stream *stream, OTF_NameRecord *rec)
 {
   char *errfmt = "nameID (%d)";
-  void *errret = NULL;
+  int errret = -1;
   OTF_StreamState state;
-  char *str;
+  int ucs = 0;
+  int ascii = 0;
   int i;
-  int c;
 
+  if (rec->platformID == 0)
+    ucs = (rec->encodingID <= 3) ? 2 : 4;
+  else if (rec->platformID == 1 && rec->encodingID == 0)
+    ascii = 1;
+  else if (rec->platformID == 3)
+    ucs = (rec->encodingID == 1  ? 2
+          : rec->encodingID == 10 ? 4
+          : 0);
+
+  OTF_MALLOC (rec->name, rec->length + 1, (void *) rec->nameID);
   SAVE_STREAM (stream, state);
   SEEK_STREAM (stream, stream->pos + rec->offset);
+  READ_BYTES (stream, rec->name, rec->length);
+  RESTORE_STREAM (stream, state);
+  rec->name[rec->length] = 0;
 
-  if (bytes == 1)
+  if (ascii)
     {
-      OTF_MALLOC (str, rec->length + 1, (void *) rec->nameID);
-      READ_BYTES (stream, str, rec->length);
-      for (i = 0; i < rec->length; i++)
-       if (str[i] < 0)
-         str[i] = '?';
+      rec->ascii = 1;
     }
-  else if (bytes == 2)
+  else if (ucs == 2)
     {
-      OTF_MALLOC (str, rec->length / 2 + 1, (void *) rec->nameID);
+      rec->ascii = 1;
       for (i = 0; i < rec->length / 2; i++)
        {
-         READ_USHORT (stream, c);
-         if (c >= 128)
-           c = '?';
-         str[i] = c;
+         if (rec->name[i * 2] > 0
+             || rec->name[i * 2 + 1] >= 128)
+           {
+             rec->ascii = 0;
+             break;
+           }
        }
+      if (rec->ascii)
+       for (i = 0; i < rec->length / 2; i++)
+         rec->name[i] = rec->name[i * 2 + 1];
+      rec->name[i] = 0;
     }
-  else if (bytes == 4)
+  else if (ucs == 4)
     {
-      OTF_MALLOC (str, rec->length / 4 + 1, (void *) rec->nameID);
+      rec->ascii = 1;
       for (i = 0; i < rec->length / 4; i++)
        {
-         READ_ULONG (stream, c);
-         if (c >= 128)
-           c = '?';
-         str[i] = c;
+         if (rec->name[i * 4] > 0
+             || rec->name[i * 4 + 1] > 0
+             || rec->name[i * 4 + 2] > 0
+             || rec->name[i * 2 + 3] >= 128)
+           {
+             rec->ascii = 0;
+             break;
+           }
        }
+      if (rec->ascii)
+       for (i = 0; i < rec->length / 4; i++)
+         rec->name[i] = rec->name[i * 4 + 3];
+      rec->name[i] = 0;
     }
-  str[i] = '\0';
-  RESTORE_STREAM (stream, state);
-  return str;
+
+  return 0;
 }
 
 static void *
@@ -412,20 +436,13 @@ read_name_table (OTF *otf, OTF_Stream *stream)
       OTF_NameRecord *rec = name->nameRecord + i;
       int nameID = rec->nameID;
 
-      if (nameID <= OTF_max_nameID
-         && ! name->name[nameID])
-       {
-         if (rec->platformID == 0)
-           name->name[nameID] = read_name (otf, stream, rec,
-                                           rec->encodingID <= 3 ? 2 : 4);
-         else if (rec->platformID == 1
-                  && rec->encodingID == 0)
-           name->name[nameID] = read_name (otf, stream, rec, 1);
-         else if (rec->platformID == 3
-                  && (rec->encodingID == 1 || rec->encodingID == 10))
-           name->name[nameID] = read_name (otf, stream,
-                                           rec, rec->encodingID == 1 ? 2 : 4);
-       }
+      read_name (otf, stream, rec);
+
+      if (nameID >= OTF_max_nameID)
+       continue;
+      if (! name->name[nameID]
+         && rec->ascii)
+       name->name[nameID] = (char *) rec->name;
     }
 
   return name;
@@ -440,6 +457,7 @@ read_cmap_table (OTF *otf, OTF_Stream *stream)
   char *errfmt = "cmap%s";
   void *errret = NULL;
   OTF_cmap *cmap;
+  int unicode_bmp_index = -1, unicode_full_index = -1;
   int i;
 
   OTF_CALLOC (cmap, 1, "");
@@ -448,12 +466,27 @@ read_cmap_table (OTF *otf, OTF_Stream *stream)
   OTF_MALLOC (cmap->EncodingRecord, cmap->numTables, "");
   for (i = 0; i < cmap->numTables; i++)
     {
-      READ_USHORT (stream, cmap->EncodingRecord[i].platformID);
-      READ_USHORT (stream, cmap->EncodingRecord[i].encodingID);
+      unsigned platformID, encodingID;
+
+      READ_USHORT (stream, platformID);
+      cmap->EncodingRecord[i].platformID = platformID;
+      READ_USHORT (stream, encodingID);
+      cmap->EncodingRecord[i].encodingID = encodingID;
       READ_ULONG (stream, cmap->EncodingRecord[i].offset);
-      if (cmap->EncodingRecord[i].platformID == 3
-         && cmap->EncodingRecord[i].encodingID == 1)
-       cmap->Unicode = cmap->EncodingRecord + i;
+      if (platformID == 0)
+       {
+         if (encodingID <= 3)
+           unicode_bmp_index = i;
+         else
+           unicode_full_index = i;
+       }
+      else if (platformID == 3)
+       {
+         if (encodingID == 1)
+           unicode_bmp_index = i;
+         else if (encodingID == 10)
+           unicode_full_index = i;
+       }
     }
   for (i = 0; i < cmap->numTables; i++)
     {
@@ -485,6 +518,36 @@ read_cmap_table (OTF *otf, OTF_Stream *stream)
          break;
 
        case 2:
+         {
+           OTF_EncodingSubtable2 *sub2;
+           int j, max_key, remaining_bytes;
+
+           OTF_MALLOC (sub2, 1, " (EncodingSubtable2)");
+           cmap->EncodingRecord[i].subtable.f.f2 = sub2;
+           for (j = 0, max_key = 0; j < 256; j++)
+             {
+               READ_USHORT (stream, sub2->subHeaderKeys[j]);
+               if (max_key < sub2->subHeaderKeys[j])
+                 max_key = sub2->subHeaderKeys[j];
+             }
+           max_key += 8;
+           sub2->subHeaderCount = max_key / 8;
+           OTF_MALLOC (sub2->subHeaders, max_key / 8, " (subHeaders)");
+           for (j = 0; j < sub2->subHeaderCount; j++)
+             {
+               READ_USHORT (stream, sub2->subHeaders[j].firstCode);
+               READ_USHORT (stream, sub2->subHeaders[j].entryCount);
+               READ_SHORT (stream, sub2->subHeaders[j].idDelta);
+               READ_USHORT (stream, sub2->subHeaders[j].idRangeOffset);
+               /* Make it offset from sub2->glyphIndexArray.  */
+               sub2->subHeaders[j].idRangeOffset -= max_key - (j * 8 + 6);
+             }
+           sub2->glyphIndexCount = (cmap->EncodingRecord[i].subtable.length
+                                    - 262 - max_key);
+           OTF_MALLOC (sub2->glyphIndexArray, sub2->glyphIndexCount,
+                       " (glyphIndexArray)");
+           READ_BYTES (stream, sub2->glyphIndexArray, sub2->glyphIndexCount);
+         }
          break;
 
        case 4:
@@ -527,8 +590,128 @@ read_cmap_table (OTF *otf, OTF_Stream *stream)
            for (j = 0; j < sub4->GlyphCount; j++)
              READ_USHORT (stream, sub4->glyphIdArray[j]);
          }
+         break;
+
+       case 6:
+         {
+           OTF_EncodingSubtable6 *sub6;
+           int j;
+
+           OTF_MALLOC (sub6, 1, " (EncodingSubtable6)");
+           cmap->EncodingRecord[i].subtable.f.f6 = sub6;
+           READ_USHORT (stream, sub6->firstCode);
+           READ_USHORT (stream, sub6->entryCount);
+           OTF_MALLOC (sub6->glyphIdArray, sub6->entryCount, " (GlyphCount)");
+           for (j = 0; j < sub6->entryCount; j++)
+             READ_USHORT (stream, sub6->glyphIdArray[j]);
+         }
+         break;
+
+       case 8:
+         {
+           OTF_EncodingSubtable8 *sub8;
+           int j;
+
+           OTF_MALLOC (sub8, 1, " (EncodingSubtable8)");
+           cmap->EncodingRecord[i].subtable.f.f8 = sub8;
+           for (j = 0; j < 8192; j++)
+             READ_BYTES (stream, sub8->is32, 8192);
+           READ_ULONG (stream, sub8->nGroups);
+           OTF_MALLOC (sub8->Groups, sub8->nGroups, " (Groups)");
+           for (j = 0; j < sub8->nGroups; j++)
+             {
+               READ_ULONG (stream, sub8->Groups[i].startCharCode);
+               READ_ULONG (stream, sub8->Groups[i].endCharCode);
+               READ_ULONG (stream, sub8->Groups[i].startGlyphID);
+             }
+         }
+         break;
+
+       case 10:
+         {
+           OTF_EncodingSubtable10 *sub10;
+           int j;
+
+           OTF_MALLOC (sub10, 1, " (EncodingSubtable10)");
+           cmap->EncodingRecord[i].subtable.f.f10 = sub10;
+           READ_ULONG (stream, sub10->startCharCode);
+           READ_ULONG (stream, sub10->numChars);
+           OTF_MALLOC (sub10->glyphs, sub10->numChars, " (GlyphCount)");
+           for (j = 0; j < sub10->numChars; j++)
+             READ_USHORT (stream, sub10->glyphs[j]);
+         }
+         break;
+
+       case 12:
+         {
+           OTF_EncodingSubtable12 *sub12;
+           int j;
+
+           OTF_MALLOC (sub12, 1, " (EncodingSubtable12)");
+           cmap->EncodingRecord[i].subtable.f.f12 = sub12;
+           READ_ULONG (stream, sub12->nGroups);
+           OTF_MALLOC (sub12->Groups, sub12->nGroups, " (Groups)");
+           for (j = 0; j < sub12->nGroups; j++)
+             {
+               READ_ULONG (stream, sub12->Groups[i].startCharCode);
+               READ_ULONG (stream, sub12->Groups[i].endCharCode);
+               READ_ULONG (stream, sub12->Groups[i].startGlyphID);
+             }
+         }
+         break;
+
+       default:
+         OTF_ERROR (OTF_ERROR_TABLE, " (invalid Subtable format)");
+       }
+    }
+
+  if (unicode_bmp_index >= 0)
+    {
+      OTF_EncodingRecord *rec = cmap->EncodingRecord + unicode_bmp_index;
+      OTF_GlyphID glyph_id, max_glyph_id = 0;
+
+      OTF_CALLOC (cmap->unicode_table, 0x10000, "");
+      switch (rec->subtable.format)
+       {
+       case 4:
+         {
+           OTF_EncodingSubtable4 *sub4 = rec->subtable.f.f4;
+           int segCount = sub4->segCountX2 / 2;
+
+           for (i = 0; i < segCount; i++)
+             {
+               OTF_cmapSegument *seg = sub4->segments + i;
+               int c;
+
+               if (seg->idRangeOffset == 0xFFFF)
+                 for (c = seg->startCount; c <= seg->endCount; c++)
+                   {
+                     glyph_id = c + seg->idDelta;
+                     cmap->unicode_table[c] = glyph_id;
+                     if (glyph_id > max_glyph_id)
+                       max_glyph_id = glyph_id;
+                   }
+               else
+                 for (c = seg->startCount; c <= seg->endCount && c != 0xFFFF;
+                      c++)
+                   {
+                     glyph_id = sub4->glyphIdArray[seg->idRangeOffset
+                                                   + (c - seg->startCount)];
+                     cmap->unicode_table[c] = glyph_id;
+                     if (glyph_id > max_glyph_id)
+                       max_glyph_id = glyph_id;
+                   }
+             }
+         }
        }
+
+      OTF_CALLOC (cmap->decode_table, max_glyph_id + 1, "");
+      for (i = 0; i < 0x10000; i++)
+       if (cmap->unicode_table[i])
+         cmap->decode_table[cmap->unicode_table[i]] = i;
+      cmap->max_glyph_id = max_glyph_id;
     }
+
   return cmap;
 }
 
@@ -557,7 +740,7 @@ read_glyph_ids (OTF *otf, OTF_Stream *stream, OTF_GlyphID **ids,
     READ_GLYPHID (stream, (*ids)[i]);
   return count;
 }
-     
+
 static unsigned
 read_range_records (OTF *otf, OTF_Stream *stream, OTF_RangeRecord **record)
 {
@@ -588,7 +771,7 @@ read_coverage (OTF *otf, OTF_Stream *stream, long offset,
   int errret = -1;
   OTF_StreamState state;
   int count;
-  
+
   READ_OFFSET (stream, coverage->offset);
   SAVE_STREAM (stream, state);
   SEEK_STREAM (stream, offset + coverage->offset);
@@ -668,7 +851,7 @@ read_class_def (OTF *otf, OTF_Stream *stream, long offset, OTF_ClassDef *class)
   char *errfmt = "ClassDef%s";
   int errret = -1;
   OTF_StreamState state;
-  
+
   READ_OFFSET (stream, class->offset);
   if (! class->offset)
     return 0;
@@ -939,7 +1122,7 @@ read_script_list (OTF *otf, OTF_Stream *stream, long offset,
          for (k = 0; k < langsys->FeatureCount; k++)
            READ_USHORT (stream, langsys->FeatureIndex[k]);
        }
-         
+
       for (j = 0; j < script->LangSysCount; j++)
        {
          OTF_LangSys *langsys = script->LangSys + j;
@@ -1197,7 +1380,7 @@ read_class_set_list (OTF *otf, OTF_Stream *stream, long offset,
   READ_UINT16 (stream, count);
   if (! count)
     OTF_ERROR (OTF_ERROR_TABLE, " (zero count)");
-  OTF_MALLOC (*set, count, "");
+  OTF_CALLOC (*set, count, "");
   for (i = 0; i < count; i++)
     /* Offset can be zero.  */
     READ_OFFSET (stream, (*set)[i].offset);
@@ -1320,7 +1503,7 @@ read_chain_class_rule_list (OTF *otf, OTF_Stream *stream, long offset,
        = read_glyph_ids (otf, stream,
                          (OTF_GlyphID **) &(*rule)[i].LookAhead, 0, -1);
       (*rule)[i].LookupCount
-       = read_lookup_record_list (otf, stream, 
+       = read_lookup_record_list (otf, stream,
                                   &(*rule)[i].LookupRecord, -1);
       if (! (*rule)[i].LookupCount)
        return errret;
@@ -1579,7 +1762,7 @@ read_alternate_set_list (OTF *otf, OTF_Stream *stream, long offset,
                         OTF_AlternateSet **altset)
 {
   char *errfmt = "AlternateSet%s";
-  int errret = -1;
+  int errret = 0;
   unsigned count;
   int i;
 
@@ -1602,7 +1785,33 @@ read_alternate_set_list (OTF *otf, OTF_Stream *stream, long offset,
   return count;
 }
 
-static int 
+static int
+read_reverse_chain1 (OTF *otf, OTF_Stream *stream, long offset,
+                    OTF_Coverage *coverage,
+                    OTF_GSUB_ReverseChain1 *reverse_chain)
+{
+  int count;
+
+  if (read_coverage (otf, stream, offset, coverage) < 0)
+    return -1;
+  count = read_coverage_list (otf, stream, offset,
+                             &reverse_chain->Backtrack, -1);
+  if (count < 0)
+    return -1;
+  reverse_chain->BacktrackGlyphCount = (unsigned) count;
+  count = read_coverage_list (otf, stream, offset,
+                             &reverse_chain->LookAhead, -1);
+  if (count <= 0)
+    return -1;
+  reverse_chain->LookaheadGlyphCount = (unsigned) count;
+  count = read_glyph_ids (otf, stream, &reverse_chain->Substitute, 0, -1);
+  if (count <= 0)
+    return -1;
+  reverse_chain->GlyphCount = count;  
+  return 0;
+}
+
+static int
 read_lookup_subtable_gsub (OTF *otf, OTF_Stream *stream, long offset,
                           unsigned type, OTF_LookupSubTableGSUB *subtable)
 {
@@ -1610,7 +1819,7 @@ read_lookup_subtable_gsub (OTF *otf, OTF_Stream *stream, long offset,
   int errret = -1;
 
   SEEK_STREAM (stream, offset);
-  READ_UINT16 (stream, subtable->Format);
+  READ_USHORT (stream, subtable->Format);
   sprintf (errfmt, "GSUB Lookup %d-%d%%s", type, subtable->Format);
   switch (type)
     {
@@ -1646,7 +1855,7 @@ read_lookup_subtable_gsub (OTF *otf, OTF_Stream *stream, long offset,
       else
        OTF_ERROR (OTF_ERROR_TABLE, " (Invalid SubFormat)");
       break;
-      
+
     case 3:
       if (subtable->Format == 1)
        {
@@ -1699,7 +1908,7 @@ read_lookup_subtable_gsub (OTF *otf, OTF_Stream *stream, long offset,
       else
        OTF_ERROR (OTF_ERROR_TABLE, " (Invalid SubFormat)");
       break;
-      
+
     case 6:
       if (subtable->Format == 1)
        {
@@ -1722,10 +1931,37 @@ read_lookup_subtable_gsub (OTF *otf, OTF_Stream *stream, long offset,
       else
        OTF_ERROR (OTF_ERROR_TABLE, " (Invalid SubFormat)");
       break;
-      
+
     case 7:
+      if (subtable->Format == 1)
+       {
+         unsigned ex_type;
+         long ex_offset;
+         OTF_LookupSubTableGSUB *ex_subtable;
+
+         READ_USHORT (stream, ex_type);
+         READ_ULONG (stream, ex_offset);
+         OTF_CALLOC (ex_subtable, 1, " (SubTable)");
+         if (read_lookup_subtable_gsub (otf, stream, offset + ex_offset,
+                                        ex_type, ex_subtable) < 0)
+           return errret;
+         subtable->u.extension1.ExtensionLookupType = ex_type;
+         subtable->u.extension1.ExtensionOffset = ex_offset;
+         subtable->u.extension1.ExtensionSubtable = ex_subtable;
+       }
+      else
+       OTF_ERROR (OTF_ERROR_TABLE, " (Invalid SubFormat)");
+      break;
+
     case 8:
-      OTF_ERROR (OTF_ERROR_TABLE, " (not yet supported)");      
+      if (subtable->Format == 1)
+       {
+         if (read_reverse_chain1 (otf, stream, offset, &subtable->Coverage,
+                                  &subtable->u.reverse_chain1) < 0)
+           return errret;
+       }
+      else
+       OTF_ERROR (OTF_ERROR_TABLE, " (Invalid SubFormat)");
       break;
 
     default:
@@ -1751,6 +1987,7 @@ read_value_record (OTF *otf, OTF_Stream *stream, long offset,
   OTF_StreamState state;
   int size, i;
 
+  memset (value_record, 0, sizeof (OTF_ValueRecord));
   if (! bit)
     return 0;
   for (i = 0, size = 0; i < 8; i++)
@@ -1759,7 +1996,7 @@ read_value_record (OTF *otf, OTF_Stream *stream, long offset,
 
   if (bit & OTF_XPlacement)
     READ_INT16 (stream, value_record->XPlacement);
-  if (bit & OTF_XPlacement)
+  if (bit & OTF_YPlacement)
     READ_INT16 (stream, value_record->YPlacement);
   if (bit & OTF_XAdvance)
     READ_INT16 (stream, value_record->XAdvance);
@@ -1846,7 +2083,7 @@ read_mark_array (OTF *otf, OTF_Stream *stream, long offset,
   int errret = -1;
   OTF_StreamState state;
   int i;
-  
+
   READ_OFFSET (stream, array->offset);
   SAVE_STREAM (stream, state);
   SEEK_STREAM (stream, offset + array->offset);
@@ -1873,7 +2110,7 @@ read_anchor_array (OTF *otf, OTF_Stream *stream, long offset,
   int errret = -1;
   OTF_StreamState state;
   int i, j;
-  
+
   READ_OFFSET (stream, array->offset);
   SAVE_STREAM (stream, state);
   SEEK_STREAM (stream, offset + array->offset);
@@ -1895,6 +2132,37 @@ read_anchor_array (OTF *otf, OTF_Stream *stream, long offset,
   return 0;
 }
 
+static OTF_PairSet *
+read_pair_set_list (OTF *otf, OTF_Stream *stream, long offset, unsigned num,
+                   enum OTF_ValueFormat bit1, enum OTF_ValueFormat bit2)
+{
+  char *errfmt = "PairSet%s";
+  void *errret = NULL;
+  OTF_StreamState state;
+  OTF_PairSet *set;
+  int i, j;
+
+  OTF_MALLOC (set, num, "");
+  for (i = 0; i < num; i++)
+    READ_OFFSET (stream, set[i].offset);
+  SAVE_STREAM (stream, state);
+  for (i = 0; i < num; i++)
+    {
+      SEEK_STREAM (stream, offset + set[i].offset);
+      READ_UINT16 (stream, set[i].PairValueCount);
+      OTF_MALLOC (set[i].PairValueRecord, set[i].PairValueCount, "");
+      for (j = 0; j < set[i].PairValueCount; j++)
+       {
+         OTF_PairValueRecord *rec = set[i].PairValueRecord + j;
+
+         READ_UINT16 (stream, rec->SecondGlyph);
+         read_value_record (otf, stream, offset, bit1, &rec->Value1);
+         read_value_record (otf, stream, offset, bit2, &rec->Value2);
+       }
+    }
+  RESTORE_STREAM (stream, state);
+  return set;
+}
 
 static OTF_Class1Record *
 read_class1_record_list (OTF *otf, OTF_Stream *stream, long offset,
@@ -1922,7 +2190,95 @@ read_class1_record_list (OTF *otf, OTF_Stream *stream, long offset,
   return rec;
 }
 
-static int 
+static unsigned
+read_entry_exit_list (OTF *otf, OTF_Stream *stream, long offset,
+                     OTF_EntryExitRecord **rec)
+{
+  char *errfmt = "EntryExitSet%s";
+  int errret = 0;
+  unsigned count;
+  int i;
+  OTF_StreamState state;
+
+  READ_UINT16 (stream, count);
+  if (! count)
+    OTF_ERROR (OTF_ERROR_TABLE, " (zero count)");
+  OTF_MALLOC (*rec, count, "");
+  for (i = 0; i < count; i++)
+    {
+      READ_OFFSET (stream, (*rec)[i].EntryAnchor.offset);
+      READ_OFFSET (stream, (*rec)[i].ExitAnchor.offset);
+    }
+  SAVE_STREAM (stream, state);
+  for (i = 0; i < count; i++)
+    {
+      if (read_anchor (otf, stream, offset, &(*rec)[i].EntryAnchor) < 0)
+       return -1;
+      if (read_anchor (otf, stream, offset, &(*rec)[i].ExitAnchor) < 0)
+       return -1;
+    }
+  RESTORE_STREAM (stream, state);
+  return count;
+}
+
+static int
+read_ligature_attach (OTF *otf, OTF_Stream *stream, long offset,
+                     unsigned ClassCount, OTF_LigatureAttach *attach)
+{
+  char *errfmt = "LigatureAttach%s";
+  int errret = 1;
+  int i, j;
+
+  SEEK_STREAM (stream, offset + attach->offset);
+  READ_UINT16 (stream, attach->ComponentCount);
+  OTF_MALLOC (attach->ComponentRecord, attach->ComponentCount, "");
+  for (i = 0; i < attach->ComponentCount; i++)
+    {
+      OTF_MALLOC (attach->ComponentRecord[i].LigatureAnchor, ClassCount,
+                 " (ComponentRecord)");
+      for (j = 0; j < ClassCount; j++)
+       READ_OFFSET (stream,
+                    attach->ComponentRecord[i].LigatureAnchor[j].offset);
+    }
+  for (i = 0; i < attach->ComponentCount; i++)
+    for (j = 0; j < ClassCount; j++)
+      {
+       if (attach->ComponentRecord[i].LigatureAnchor[j].offset)
+         {
+           if (read_anchor (otf, stream, offset + attach->offset,
+                            &attach->ComponentRecord[i].LigatureAnchor[j]) < 0)
+             return -1;
+         }
+       else
+         attach->ComponentRecord[i].LigatureAnchor[j].AnchorFormat = 0;
+      }
+  return 0;
+}
+
+static int
+read_ligature_array (OTF *otf, OTF_Stream *stream, long offset,
+                    unsigned class_count, OTF_LigatureArray *array)
+{
+  char *errfmt = "LigatureArray%s";
+  int errret = -1;
+  OTF_StreamState state;
+  int i;
+
+  READ_OFFSET (stream, array->offset);
+  SAVE_STREAM (stream, state);
+  SEEK_STREAM (stream, offset + array->offset);
+  READ_UINT16 (stream, array->LigatureCount);
+  OTF_MALLOC (array->LigatureAttach, array->LigatureCount, "");
+  for (i = 0; i < array->LigatureCount; i++)
+    READ_OFFSET (stream, array->LigatureAttach[i].offset);
+  for (i = 0; i < array->LigatureCount; i++)
+    read_ligature_attach (otf, stream, offset + array->offset,
+                         class_count, array->LigatureAttach + i);
+  RESTORE_STREAM (stream, state);
+  return 0;
+}
+
+static int
 read_lookup_subtable_gpos (OTF *otf, OTF_Stream *stream,
                           long offset, unsigned type,
                           OTF_LookupSubTableGPOS *subtable)
@@ -1938,6 +2294,8 @@ read_lookup_subtable_gpos (OTF *otf, OTF_Stream *stream,
     case 1:
       if (subtable->Format == 1)
        {
+         if (read_coverage (otf, stream, offset, &subtable->Coverage) < 0)
+           return -1;
          READ_UINT16 (stream, subtable->u.single1.ValueFormat);
          read_value_record (otf, stream, offset,
                             subtable->u.single1.ValueFormat,
@@ -1948,6 +2306,8 @@ read_lookup_subtable_gpos (OTF *otf, OTF_Stream *stream,
          OTF_GPOS_Single2 *single2 = &subtable->u.single2;
          int i;
 
+         if (read_coverage (otf, stream, offset, &subtable->Coverage) < 0)
+           return -1;
          READ_UINT16 (stream, single2->ValueFormat);
          READ_UINT16 (stream, single2->ValueCount);
          OTF_CALLOC (single2->Value, single2->ValueCount," (ValueRecord)");
@@ -1962,18 +2322,30 @@ read_lookup_subtable_gpos (OTF *otf, OTF_Stream *stream,
     case 2:
       if (subtable->Format == 1)
        {
-         OTF_ERROR (OTF_ERROR_TABLE, " (not yet supported)");
+         if (read_coverage (otf, stream, offset, &subtable->Coverage) < 0)
+           return -1;
+         READ_UINT16 (stream, subtable->u.pair1.ValueFormat1);
+         READ_UINT16 (stream, subtable->u.pair1.ValueFormat2);
+         READ_UINT16 (stream, subtable->u.pair1.PairSetCount);
+         subtable->u.pair1.PairSet
+           = read_pair_set_list (otf, stream, offset,
+                                 subtable->u.pair1.PairSetCount,
+                                 subtable->u.pair1.ValueFormat1,
+                                 subtable->u.pair1.ValueFormat2);
+         if (! subtable->u.pair1.PairSet)
+           return -1;
        }
       else if (subtable->Format == 2)
        {
-         SEEK_STREAM (stream, offset + 2);
-         read_coverage (otf, stream, offset, &subtable->Coverage);
+         if (read_coverage (otf, stream, offset, &subtable->Coverage) < 0)
+           return -1;
          READ_UINT16 (stream, subtable->u.pair2.ValueFormat1);
          READ_UINT16 (stream, subtable->u.pair2.ValueFormat2);
-         read_class_def (otf, stream, offset,
-                         &subtable->u.pair2.ClassDef1);
-         read_class_def (otf, stream, offset,
-                         &subtable->u.pair2.ClassDef2);
+         if (read_class_def (otf, stream, offset,
+                             &subtable->u.pair2.ClassDef1) < 0
+             || read_class_def (otf, stream, offset,
+                                &subtable->u.pair2.ClassDef2) < 0)
+           return -1;
          READ_UINT16 (stream, subtable->u.pair2.Class1Count);
          READ_UINT16 (stream, subtable->u.pair2.Class2Count);
          subtable->u.pair2.Class1Record
@@ -1982,13 +2354,26 @@ read_lookup_subtable_gpos (OTF *otf, OTF_Stream *stream,
                                       subtable->u.pair2.ValueFormat1,
                                       subtable->u.pair2.Class2Count,
                                       subtable->u.pair2.ValueFormat2);
+         if (! subtable->u.pair2.Class1Record)
+           return -1;
        }
       else
        OTF_ERROR (OTF_ERROR_TABLE, " (Invalid SubFormat)");
       break;
-      
+
     case 3:
-      OTF_ERROR (OTF_ERROR_TABLE, " (not yet supported)");
+      if (subtable->Format == 1)
+       {
+         if (read_coverage (otf, stream, offset, &subtable->Coverage) < 0)
+           return -1;
+         subtable->u.cursive1.EntryExitCount
+           = read_entry_exit_list (otf, stream, offset,
+                                   &subtable->u.cursive1.EntryExitRecord);
+         if (! subtable->u.cursive1.EntryExitCount)
+           return -1;
+       }
+      else
+       OTF_ERROR (OTF_ERROR_TABLE, " (Invalid SubFormat)");
       break;
 
     case 4:
@@ -2009,7 +2394,18 @@ read_lookup_subtable_gpos (OTF *otf, OTF_Stream *stream,
       break;
 
     case 5:
-      OTF_ERROR (OTF_ERROR_TABLE, " (not yet supported)");
+      if (subtable->Format == 1)
+       {
+         read_coverage (otf, stream, offset, &subtable->Coverage);
+         read_coverage (otf, stream, offset,
+                        &subtable->u.mark_lig1.LigatureCoverage);
+         READ_UINT16 (stream, subtable->u.mark_lig1.ClassCount);
+         read_mark_array (otf, stream, offset,
+                          &subtable->u.mark_lig1.MarkArray);
+         read_ligature_array (otf, stream, offset,
+                              subtable->u.mark_lig1.ClassCount,
+                              &subtable->u.mark_lig1.LigatureArray);
+       }
       break;
 
     case 6:
@@ -2076,7 +2472,24 @@ read_lookup_subtable_gpos (OTF *otf, OTF_Stream *stream,
       break;
 
     case 9:
-      OTF_ERROR (OTF_ERROR_TABLE, " (not yet supported)");
+      if (subtable->Format == 1)
+       {
+         unsigned ex_type;
+         long ex_offset;
+         OTF_LookupSubTableGPOS *ex_subtable;
+
+         READ_USHORT (stream, ex_type);
+         READ_ULONG (stream, ex_offset);
+         OTF_CALLOC (ex_subtable, 1, " (SubTable)");
+         if (read_lookup_subtable_gpos (otf, stream, offset + ex_offset,
+                                        ex_type, ex_subtable) < 0)
+           return errret;
+         subtable->u.extension1.ExtensionLookupType = ex_type;
+         subtable->u.extension1.ExtensionOffset = ex_offset;
+         subtable->u.extension1.ExtensionSubtable = ex_subtable;
+       }
+      else
+       OTF_ERROR (OTF_ERROR_TABLE, " (Invalid SubFormat)");
       break;
 
     default:
@@ -2123,7 +2536,7 @@ read_jstf_table (OTF_Stream *stream, long offset)
 
 int
 read_offset_table (OTF *otf, OTF_Stream *stream, OTF_OffsetTable *table)
-{  
+{
   int errret = -1;
 
   READ_FIXED (stream, table->sfnt_version);
@@ -2188,7 +2601,7 @@ read_header_part (OTF *otf, FILE *fp)
     return -1;
 
   internal_data->header_stream = stream;
-  
+
   /* Size of Offset Table is 12 bytes.  */
   if (setup_stream (stream, fp, 0, 12, "Offset Table") < 0)
     return -1;
@@ -2237,6 +2650,44 @@ read_header_part (OTF *otf, FILE *fp)
   return 0;
 }
 
+static OTF_TableInfo *
+get_table_info (OTF *otf, char *name)
+{
+  char *errfmt = "OTF Table Read%s";
+  OTF_TableInfo *errret = NULL;
+  OTF_InternalData *internal_data = otf->internal_data;
+  OTF_TableInfo *table_info;
+  OTF_Tag tag = OTF_tag (name);
+
+  if (! tag)
+    OTF_ERROR (OTF_ERROR_TABLE, " (invalid table name)");
+
+  if (tag == OTF_tag ("head"))
+    table_info = internal_data->table_info + OTF_TABLE_TYPE_HEAD;
+  else if (tag == OTF_tag ("name"))
+    table_info = internal_data->table_info + OTF_TABLE_TYPE_NAME;
+  else if (tag == OTF_tag ("cmap"))
+    table_info = internal_data->table_info + OTF_TABLE_TYPE_CMAP;
+  else if (tag == OTF_tag ("GDEF"))
+    table_info = internal_data->table_info + OTF_TABLE_TYPE_GDEF;
+  else if (tag == OTF_tag ("GSUB"))
+    table_info = internal_data->table_info + OTF_TABLE_TYPE_GSUB;
+  else if (tag == OTF_tag ("GPOS"))
+    table_info = internal_data->table_info + OTF_TABLE_TYPE_GPOS;
+  else
+    OTF_ERROR (OTF_ERROR_TABLE, " (unsupported table name)");
+
+  if (*table_info->address)
+    /* Already read.  */
+    return table_info;
+  if (! table_info->stream)
+    OTF_ERROR (OTF_ERROR_TABLE, " (table not found)");
+  if (! table_info->reader)
+    OTF_ERROR (OTF_ERROR_TABLE, " (invalid contents)");
+  return table_info;
+}
+
+
 \f
 /*** (2) API for reading OTF */
 
@@ -2254,7 +2705,15 @@ OTF_open (char *otf_name)
   void *errret = NULL;
   OTF *otf;
   OTF_InternalData *internal_data;
-
+  int len = strlen (otf_name);
+  const char *ext = otf_name + (len - 4);
+
+  if (len < 4
+      || ext[0] != '.'
+      || (ext[1] != 'O' && ext[1] != 'T' && ext[1] != 'o' && ext[1] != 't')
+      || (ext[2] != 'T' && ext[2] != 't')
+      || (ext[3] != 'F' && ext[3] != 'f'))
+    OTF_ERROR (OTF_ERROR_FILE, otf_name);
   fp = fopen (otf_name, "r");
   if (! fp)
     OTF_ERROR (OTF_ERROR_FILE, otf_name);
@@ -2321,6 +2780,8 @@ OTF_close (OTF *otf)
        }
       free (internal_data);
     }
+  if (otf->filename)
+    free (otf->filename);
   free (otf);
 }
 
@@ -2329,37 +2790,10 @@ OTF_close (OTF *otf)
 int
 OTF_get_table (OTF *otf, char *name)
 {
-  char *errfmt = "OTF Table Read%s";
-  int errret = -1;
-  OTF_InternalData *internal_data = otf->internal_data;
-  OTF_TableInfo *table_info;
-  OTF_Tag tag = OTF_tag (name);
-
-  if (! tag)
-    OTF_ERROR (OTF_ERROR_TABLE, " (invalid table name)");
-
-  if (tag == OTF_tag ("head"))
-    table_info = internal_data->table_info + OTF_TABLE_TYPE_HEAD;
-  else if (tag == OTF_tag ("name"))
-    table_info = internal_data->table_info + OTF_TABLE_TYPE_NAME;
-  else if (tag == OTF_tag ("cmap"))
-    table_info = internal_data->table_info + OTF_TABLE_TYPE_CMAP;
-  else if (tag == OTF_tag ("GDEF"))
-    table_info = internal_data->table_info + OTF_TABLE_TYPE_GDEF;
-  else if (tag == OTF_tag ("GSUB"))
-    table_info = internal_data->table_info + OTF_TABLE_TYPE_GSUB;
-  else if (tag == OTF_tag ("GPOS"))
-    table_info = internal_data->table_info + OTF_TABLE_TYPE_GPOS;
-  else
-    OTF_ERROR (OTF_ERROR_TABLE, " (not yet supported table name)");
+  OTF_TableInfo *table_info = get_table_info (otf, name);
 
-  if (*table_info->address)
-    /* Already read.  */
-    return 0;
-  if (! table_info->stream)
-    OTF_ERROR (OTF_ERROR_TABLE, " (table not found)");
-  if (! table_info->reader)
-    OTF_ERROR (OTF_ERROR_TABLE, " (invalid contents)");
+  if (! table_info)
+    return -1;
 
   *table_info->address = (*table_info->reader) (otf, table_info->stream);
   free_stream (table_info->stream);
@@ -2367,12 +2801,21 @@ OTF_get_table (OTF *otf, char *name)
   if (! *table_info->address)
     {
       table_info->reader = NULL;
-      return errret;
+      return -1;
     }
-
   return 0;
 }
 
+/*** (2-4) OTF_check_table() */
+
+int
+OTF_check_table (OTF *otf, char *name)
+{
+  return (get_table_info (otf, name) ? 0 : -1);
+}
+
+
+
 \f
 /*** (5) API miscellaneous ***/