(OTF_check_features): If language is specified but not found, return 0
[m17n/libotf.git] / src / otfopen.c
index f6a0832..8aa062d 100644 (file)
@@ -1,6 +1,6 @@
 /* otfopen.c -- OpenType font reader.
 
 /* otfopen.c -- OpenType font reader.
 
-Copyright (C) 2003, 2004
+Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010
   National Institute of Advanced Industrial Science and Technology (AIST)
   Registration Number H15PRO167
 
   National Institute of Advanced Industrial Science and Technology (AIST)
   Registration Number H15PRO167
 
@@ -27,7 +27,9 @@ write to the Free Software Foundation, Inc., 59 Temple Place, Suite
 #include <config.h>
 
 #include "otf.h"
 #include <config.h>
 
 #include "otf.h"
-#include "otferror.h"
+#include "internal.h"
+
+#include FT_TRUETYPE_TABLES_H
 
 /***
     Table of contents (almost parallel to otf.h):
 
 /***
     Table of contents (almost parallel to otf.h):
@@ -55,6 +57,15 @@ write to the Free Software Foundation, Inc., 59 Temple Place, Suite
 
     (5) API miscellaneous
 */
 
     (5) API miscellaneous
 */
+
+int debug_flag = -1;
+
+static void
+set_debug_flag ()
+{
+  debug_flag = getenv ("LIBOTF_DEBUG") != NULL;
+}
+
 \f
 /* (0) Stream handler
 
 \f
 /* (0) Stream handler
 
@@ -80,8 +91,7 @@ write to the Free Software Foundation, Inc., 59 Temple Place, Suite
 
 typedef struct
 {
 
 typedef struct
 {
-  FILE *fp;
-  char *name;
+  const char *name;
   long pos;
   long bufsize;
   long allocated;
   long pos;
   long bufsize;
   long allocated;
@@ -90,8 +100,8 @@ typedef struct
 
 typedef long OTF_StreamState;
 
 
 typedef long OTF_StreamState;
 
-OTF_Stream *
-make_stream ()
+static OTF_Stream *
+make_stream (const char *name)
 {
   OTF_Stream *stream;
   char *errfmt = "stream creation%s";
 {
   OTF_Stream *stream;
   char *errfmt = "stream creation%s";
@@ -100,17 +110,16 @@ make_stream ()
   stream = calloc (1, sizeof (OTF_Stream));
   if (! stream)
     OTF_ERROR (OTF_ERROR_MEMORY, "");
   stream = calloc (1, sizeof (OTF_Stream));
   if (! stream)
     OTF_ERROR (OTF_ERROR_MEMORY, "");
+  stream->name = name;
   return stream;
 }
 
   return stream;
 }
 
-int
-setup_stream (OTF_Stream *stream, FILE *fp, long offset, int nbytes,
-             char *name)
+static int
+setup_stream (OTF_Stream *stream, FILE *fp, long offset, int nbytes)
 {
   char *errfmt = "stream setup for %s";
   int errret = -1;
 
 {
   char *errfmt = "stream setup for %s";
   int errret = -1;
 
-  stream->name = name;
   stream->pos = 0;
   if (stream->allocated < nbytes)
     {
   stream->pos = 0;
   if (stream->allocated < nbytes)
     {
@@ -131,11 +140,41 @@ setup_stream (OTF_Stream *stream, FILE *fp, long offset, int nbytes,
   return 0;
 }
 
   return 0;
 }
 
+static OTF_Stream *
+make_stream_from_ft_face (FT_Face face, const char *name)
+{
+  char *errfmt = "FT_Face stream creation for %s";
+  void *errret = NULL;
+  FT_ULong nbytes = 0;
+  unsigned char *buf;
+  OTF_Stream *stream;
+  FT_ULong tag = FT_MAKE_TAG (name[0], name[1], name[2], name[3]);
 
 
-void
+  if (FT_Load_Sfnt_Table (face, tag, 0, NULL, &nbytes))
+    return NULL;
+  buf = malloc (nbytes);
+  if (! buf)
+    OTF_ERROR (OTF_ERROR_MEMORY, name);
+  if (FT_Load_Sfnt_Table (face, tag, 0, buf, &nbytes))
+    {
+      free (buf);
+      OTF_ERROR (OTF_ERROR_FT_FACE, name);
+    }
+  stream = make_stream (name);
+  if (! stream)
+    return NULL;
+  stream->pos = 0;
+  stream->buf = buf;
+  stream->allocated = nbytes;
+  stream->bufsize = nbytes;
+  return stream;
+}
+
+static void
 free_stream (OTF_Stream *stream)
 {
 free_stream (OTF_Stream *stream)
 {
-  free (stream->buf);
+  if (stream->buf)
+    free (stream->buf);
   free (stream);
 }
 
   free (stream);
 }
 
@@ -170,6 +209,15 @@ free_stream (OTF_Stream *stream)
     (stream)->pos += 2;                                                \
   } while (0)
 
     (stream)->pos += 2;                                                \
   } while (0)
 
+#define READ_UINT24(stream, var)                       \
+  do {                                                 \
+    STREAM_CHECK_SIZE ((stream), 3);                   \
+    (var) =  (((stream)->buf[(stream)->pos ] << 16)    \
+             | ((stream)->buf[(stream)->pos + 1] << 8) \
+             | (stream)->buf[(stream)->pos + 2]);      \
+    (stream)->pos += 3;                                        \
+  } while (0)
+
 #define READ_ULONG(stream, var)                                \
   do {                                                 \
     STREAM_CHECK_SIZE ((stream), 4);                   \
 #define READ_ULONG(stream, var)                                \
   do {                                                 \
     STREAM_CHECK_SIZE ((stream), 4);                   \
@@ -239,15 +287,36 @@ struct OTF_MemoryRecord
 
 typedef struct OTF_MemoryRecord OTF_MemoryRecord;
 
 
 typedef struct OTF_MemoryRecord OTF_MemoryRecord;
 
-typedef struct
+enum OTF_ReaderFlag
+  {
+    OTF_READ_FULL,
+    OTF_READ_SCRIPTS,
+    OTF_READ_FEATURES,
+    OTF_READ_MAX
+  };
+
+struct _OTF_TableInfo;
+typedef struct _OTF_TableInfo OTF_TableInfo;
+
+struct _OTF_TableInfo
 {
   /* Points to one of OTF->head, OTF->name, etc.  */
   void **address;
   /* Function to read one of OTF tables.  */
 {
   /* Points to one of OTF->head, OTF->name, etc.  */
   void **address;
   /* Function to read one of OTF tables.  */
-  void *(*reader) (OTF *otf, OTF_Stream *stream);
+  void *(*reader) (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag);
   /* Stream given to <reader>.  */
   OTF_Stream *stream;
   /* Stream given to <reader>.  */
   OTF_Stream *stream;
-} OTF_TableInfo;
+};
+
+struct _OTF_ApplicationData
+{
+  char *id;
+  void *data;
+  void (*freer) (void *data);
+  struct _OTF_ApplicationData *next;
+};
+
+typedef struct _OTF_ApplicationData OTF_ApplicationData;
 
 struct OTF_InternalData
 {
 
 struct OTF_InternalData
 {
@@ -259,8 +328,10 @@ struct OTF_InternalData
 
   /* Records of allocated memories.  */
   OTF_MemoryRecord *memory_record;
 
   /* Records of allocated memories.  */
   OTF_MemoryRecord *memory_record;
-};
 
 
+  /* Root of application data chain.  */
+  OTF_ApplicationData *app_data;
+};
 
 static OTF_MemoryRecord *
 allocate_memory_record (OTF *otf)
 
 static OTF_MemoryRecord *
 allocate_memory_record (OTF *otf)
@@ -317,8 +388,9 @@ allocate_memory_record (OTF *otf)
 /*** (1-2) "head" table */
 
 static void *
 /*** (1-2) "head" table */
 
 static void *
-read_head_table (OTF *otf, OTF_Stream *stream)
+read_head_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
 {
 {
+  OTF_Stream *stream = table->stream;
   char *errfmt = "head%s";
   void *errret = NULL;
   OTF_head *head;
   char *errfmt = "head%s";
   void *errret = NULL;
   OTF_head *head;
@@ -331,6 +403,7 @@ read_head_table (OTF *otf, OTF_Stream *stream)
   READ_USHORT (stream, head->flags);
   READ_USHORT (stream, head->unitsPerEm);
 
   READ_USHORT (stream, head->flags);
   READ_USHORT (stream, head->unitsPerEm);
 
+  *table->address = head;
   return head;
 }
 
   return head;
 }
 
@@ -340,13 +413,14 @@ read_head_table (OTF *otf, OTF_Stream *stream)
 static int
 read_name (OTF *otf, OTF_Stream *stream, OTF_NameRecord *rec)
 {
 static int
 read_name (OTF *otf, OTF_Stream *stream, OTF_NameRecord *rec)
 {
-  char *errfmt = "nameID (%d)";
+  char errfmt[256];
   int errret = -1;
   OTF_StreamState state;
   int ucs = 0;
   int ascii = 0;
   int i;
 
   int errret = -1;
   OTF_StreamState state;
   int ucs = 0;
   int ascii = 0;
   int i;
 
+  sprintf (errfmt, "nameID (%d)%%s", rec->nameID);
   if (rec->platformID == 0)
     ucs = (rec->encodingID <= 3) ? 2 : 4;
   else if (rec->platformID == 1 && rec->encodingID == 0)
   if (rec->platformID == 0)
     ucs = (rec->encodingID <= 3) ? 2 : 4;
   else if (rec->platformID == 1 && rec->encodingID == 0)
@@ -356,7 +430,7 @@ read_name (OTF *otf, OTF_Stream *stream, OTF_NameRecord *rec)
           : rec->encodingID == 10 ? 4
           : 0);
 
           : rec->encodingID == 10 ? 4
           : 0);
 
-  OTF_MALLOC (rec->name, rec->length + 1, (void *) rec->nameID);
+  OTF_MALLOC (rec->name, rec->length + 1, "");
   SAVE_STREAM (stream, state);
   SEEK_STREAM (stream, stream->pos + rec->offset);
   READ_BYTES (stream, rec->name, rec->length);
   SAVE_STREAM (stream, state);
   SEEK_STREAM (stream, stream->pos + rec->offset);
   READ_BYTES (stream, rec->name, rec->length);
@@ -408,8 +482,9 @@ read_name (OTF *otf, OTF_Stream *stream, OTF_NameRecord *rec)
 }
 
 static void *
 }
 
 static void *
-read_name_table (OTF *otf, OTF_Stream *stream)
+read_name_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
 {
 {
+  OTF_Stream *stream = table->stream;
   char *errfmt = "name%s";
   void *errret = NULL;
   OTF_name *name;
   char *errfmt = "name%s";
   void *errret = NULL;
   OTF_name *name;
@@ -445,15 +520,96 @@ read_name_table (OTF *otf, OTF_Stream *stream)
        name->name[nameID] = (char *) rec->name;
     }
 
        name->name[nameID] = (char *) rec->name;
     }
 
+  *table->address = name;
   return name;
 }
 
 \f
 /*** (1-4) "cmap" table */
 
   return name;
 }
 
 \f
 /*** (1-4) "cmap" table */
 
+static OTF_EncodingSubtable14 *
+read_cmap_uvs_table (OTF *otf, OTF_Stream *stream, OTF_Offset offset)
+{
+  OTF_EncodingSubtable14 *sub14;
+  char *errfmt = "cmap-uvs%s";
+  void *errret = NULL;
+  unsigned nRecords;
+  unsigned i,j;
+
+  OTF_MALLOC (sub14, 1, " (EncodingSubtable14)");
+  READ_ULONG (stream, nRecords);
+  sub14->nRecords = nRecords;
+  OTF_MALLOC (sub14->Records, nRecords, "(EncodingSubtable14-Records)");
+  for (i = 0; i < sub14->nRecords; i++)
+    {
+      unsigned varSelector=0, defaultUVSOffset, nonDefaultUVSOffset;
+
+      READ_UINT24 (stream, varSelector);
+      sub14->Records[i].varSelector = varSelector;
+      READ_ULONG (stream, defaultUVSOffset);
+      sub14->Records[i].defaultUVSOffset = defaultUVSOffset;
+      READ_ULONG (stream, nonDefaultUVSOffset);
+      sub14->Records[i].nonDefaultUVSOffset = nonDefaultUVSOffset;
+    }
+  for (i = 0; i < sub14->nRecords; i++)
+    {
+      OTF_VariationSelectorRecord *record = &sub14->Records[i];
+      unsigned defaultUVSOffset = record->defaultUVSOffset;
+      unsigned nonDefaultUVSOffset = record->nonDefaultUVSOffset;
+
+      if (defaultUVSOffset)
+       {
+         unsigned numUnicodeValueRanges;
+
+         SEEK_STREAM (stream, offset+defaultUVSOffset);
+         READ_ULONG (stream, numUnicodeValueRanges);
+         record->numUnicodeValueRanges = numUnicodeValueRanges;
+         OTF_MALLOC (record->unicodeValueRanges,
+                     numUnicodeValueRanges,
+                     "(EncodingSubtable14-Records-unicodeValueRanges)");
+         for (j = 0; j < numUnicodeValueRanges; j++)
+           {
+             OTF_UnicodeValueRange *unicodeValueRange
+               = &record->unicodeValueRanges[j];
+             unsigned startUnicodeValue;
+             char additionalCount;
+
+             READ_UINT24 (stream, startUnicodeValue);
+             unicodeValueRange->startUnicodeValue=startUnicodeValue;
+             READ_BYTES (stream, &additionalCount, 1);
+             unicodeValueRange->additionalCount
+               = (unsigned short) additionalCount;
+           }
+       }
+      if (nonDefaultUVSOffset)
+       {
+         unsigned numUVSMappings;
+
+         SEEK_STREAM (stream, offset+nonDefaultUVSOffset);
+         READ_ULONG (stream, numUVSMappings);
+         record->numUVSMappings = numUVSMappings;
+         OTF_MALLOC (record->uvsMappings, numUVSMappings,
+                     "(EncodingSubtable14-Records-uvsMappings)");
+         for (j = 0; j < numUVSMappings; j++)
+           {
+             OTF_UVSMapping *uvsMapping = &record->uvsMappings[j];
+             unsigned unicodeValue;
+             unsigned short glyphID;
+
+             READ_UINT24 (stream, unicodeValue);
+             uvsMapping->unicodeValue = unicodeValue;
+             READ_USHORT (stream, glyphID);
+             uvsMapping->glyphID = glyphID;
+           }
+       }
+    }
+  return sub14;
+}
+
 static void *
 static void *
-read_cmap_table (OTF *otf, OTF_Stream *stream)
+read_cmap_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
 {
 {
+  OTF_Stream *stream = table->stream;
   char *errfmt = "cmap%s";
   void *errret = NULL;
   OTF_cmap *cmap;
   char *errfmt = "cmap%s";
   void *errret = NULL;
   OTF_cmap *cmap;
@@ -488,6 +644,8 @@ read_cmap_table (OTF *otf, OTF_Stream *stream)
            unicode_full_index = i;
        }
     }
            unicode_full_index = i;
        }
     }
+  cmap->table_index = unicode_full_index;
+
   for (i = 0; i < cmap->numTables; i++)
     {
       unsigned format;
   for (i = 0; i < cmap->numTables; i++)
     {
       unsigned format;
@@ -495,15 +653,23 @@ read_cmap_table (OTF *otf, OTF_Stream *stream)
       SEEK_STREAM (stream, cmap->EncodingRecord[i].offset);
       READ_USHORT (stream, format);
       cmap->EncodingRecord[i].subtable.format = format;
       SEEK_STREAM (stream, cmap->EncodingRecord[i].offset);
       READ_USHORT (stream, format);
       cmap->EncodingRecord[i].subtable.format = format;
-      READ_USHORT (stream, cmap->EncodingRecord[i].subtable.length);
-      if (format == 8 || format == 10 || format == 12)
+      if (format == 14)
        {
          READ_ULONG (stream, cmap->EncodingRecord[i].subtable.length);
        {
          READ_ULONG (stream, cmap->EncodingRecord[i].subtable.length);
-         READ_ULONG (stream, cmap->EncodingRecord[i].subtable.language);
+         cmap->EncodingRecord[i].subtable.language = 0;
        }
       else
        {
        }
       else
        {
-         READ_USHORT (stream, cmap->EncodingRecord[i].subtable.language);
+         READ_USHORT (stream, cmap->EncodingRecord[i].subtable.length);
+         if (format == 8 || format == 10 || format == 12)
+           {
+             READ_ULONG (stream, cmap->EncodingRecord[i].subtable.length);
+             READ_ULONG (stream, cmap->EncodingRecord[i].subtable.language);
+           }
+         else
+           {
+             READ_USHORT (stream, cmap->EncodingRecord[i].subtable.language);
+           }
        }
       switch (format)
        {
        }
       switch (format)
        {
@@ -518,7 +684,36 @@ read_cmap_table (OTF *otf, OTF_Stream *stream)
          break;
 
        case 2:
          break;
 
        case 2:
-         OTF_ERROR (OTF_ERROR_TABLE, " (not yet supported)");
+         {
+           OTF_EncodingSubtable2 *sub2;
+           int j, max_key;
+
+           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:
          break;
 
        case 4:
@@ -547,7 +742,7 @@ read_cmap_table (OTF *otf, OTF_Stream *stream)
              {
                unsigned off;
                unsigned rest = 2 * (segCount - j);
              {
                unsigned off;
                unsigned rest = 2 * (segCount - j);
-               
+
                READ_USHORT (stream, off);
                if (off == 0)
                  sub4->segments[j].idRangeOffset = 0xFFFF;
                READ_USHORT (stream, off);
                if (off == 0)
                  sub4->segments[j].idRangeOffset = 0xFFFF;
@@ -624,13 +819,21 @@ read_cmap_table (OTF *otf, OTF_Stream *stream)
            OTF_MALLOC (sub12->Groups, sub12->nGroups, " (Groups)");
            for (j = 0; j < sub12->nGroups; j++)
              {
            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);
+               READ_ULONG (stream, sub12->Groups[j].startCharCode);
+               READ_ULONG (stream, sub12->Groups[j].endCharCode);
+               READ_ULONG (stream, sub12->Groups[j].startGlyphID);
              }
          }
          break;
 
              }
          }
          break;
 
+       case 14:
+         {
+           cmap->EncodingRecord[i].subtable.f.f14
+             = read_cmap_uvs_table (otf, stream,
+                                    cmap->EncodingRecord[i].offset);
+           break;
+         }
+
        default:
          OTF_ERROR (OTF_ERROR_TABLE, " (invalid Subtable format)");
        }
        default:
          OTF_ERROR (OTF_ERROR_TABLE, " (invalid Subtable format)");
        }
@@ -651,13 +854,13 @@ read_cmap_table (OTF *otf, OTF_Stream *stream)
 
            for (i = 0; i < segCount; i++)
              {
 
            for (i = 0; i < segCount; i++)
              {
-               OTF_cmapSegument *seg = sub4->segments + i;
+               OTF_cmapSegment *seg = sub4->segments + i;
                int c;
 
                if (seg->idRangeOffset == 0xFFFF)
                  for (c = seg->startCount; c <= seg->endCount; c++)
                    {
                int c;
 
                if (seg->idRangeOffset == 0xFFFF)
                  for (c = seg->startCount; c <= seg->endCount; c++)
                    {
-                     glyph_id = c + seg->idDelta;
+                     glyph_id = (c + seg->idDelta) % 0x10000;
                      cmap->unicode_table[c] = glyph_id;
                      if (glyph_id > max_glyph_id)
                        max_glyph_id = glyph_id;
                      cmap->unicode_table[c] = glyph_id;
                      if (glyph_id > max_glyph_id)
                        max_glyph_id = glyph_id;
@@ -683,6 +886,7 @@ read_cmap_table (OTF *otf, OTF_Stream *stream)
       cmap->max_glyph_id = max_glyph_id;
     }
 
       cmap->max_glyph_id = max_glyph_id;
     }
 
+  *table->address = cmap;
   return cmap;
 }
 
   return cmap;
 }
 
@@ -876,41 +1080,48 @@ read_device_table (OTF *otf, OTF_Stream *stream, long offset,
   READ_UINT16 (stream, table->EndSize);
   READ_UINT16 (stream, table->DeltaFormat);
   num = table->EndSize - table->StartSize + 1;
   READ_UINT16 (stream, table->EndSize);
   READ_UINT16 (stream, table->DeltaFormat);
   num = table->EndSize - table->StartSize + 1;
-  OTF_MALLOC (table->DeltaValue, num, "");
+  if (num > 0 && table->DeltaFormat >= 1 && table->DeltaFormat <= 3)
+    {
+      OTF_MALLOC (table->DeltaValue, num, "");
 
 
-  if (table->DeltaFormat == 1)
-    for (i = 0; i < num; i++)
-      {
-       if ((i % 8) == 0)
-         READ_UINT16 (stream, val);
-       intval.int2 = (val >> (14 - (i % 8) * 2)) & 0x03;
-       table->DeltaValue[i] = intval.int2;
-      }
-  else if (table->DeltaFormat == 2)
-    for (i = 0; i < num; i++)
-      {
-       if ((i % 4) == 0)
-         READ_UINT16 (stream, val);
-       intval.int4 = (val >> (12 - (i % 4) * 4)) & 0x0F;
-       table->DeltaValue[i] = intval.int4;
-      }
-  else if (table->DeltaFormat == 3)
-    for (i = 0; i < num; i++)
-      {
-       if ((i % 2) == 0)
+      if (table->DeltaFormat == 1)
+       for (i = 0; i < num; i++)
          {
          {
-           READ_UINT16 (stream, val);
-           intval.int8 = val >> 8;
-           table->DeltaValue[i] = intval.int8;
+           if ((i % 8) == 0)
+             READ_UINT16 (stream, val);
+           intval.int2 = (val >> (14 - (i % 8) * 2)) & 0x03;
+           table->DeltaValue[i] = intval.int2;
          }
          }
-       else
+      else if (table->DeltaFormat == 2)
+       for (i = 0; i < num; i++)
          {
          {
-           intval.int8 = val >> 8;
-           table->DeltaValue[i] = intval.int8;
+           if ((i % 4) == 0)
+             READ_UINT16 (stream, val);
+           intval.int4 = (val >> (12 - (i % 4) * 4)) & 0x0F;
+           table->DeltaValue[i] = intval.int4;
          }
          }
-      }
+      else                             /* (table->DeltaFormat == 3) */
+       for (i = 0; i < num; i++)
+         {
+           if ((i % 2) == 0)
+             {
+               READ_UINT16 (stream, val);
+               intval.int8 = val >> 8;
+               table->DeltaValue[i] = intval.int8;
+             }
+           else
+             {
+               intval.int8 = val >> 8;
+               table->DeltaValue[i] = intval.int8;
+             }
+         }
+    }
   else
   else
-    OTF_ERROR (OTF_ERROR_TABLE, " (Invalid format)");
+    {
+      /* Invalid DeltaFormat but several fonts has such values (bug of
+        fontforge?).  So accept it with NULL delta values.  */
+      table->DeltaValue = NULL;
+    }
   return 0;
 }
 
   return 0;
 }
 
@@ -1016,31 +1227,38 @@ read_gdef_header (OTF_Stream *stream, OTF_GDEFHeader *header)
 }
 
 static void *
 }
 
 static void *
-read_gdef_table (OTF *otf, OTF_Stream *stream)
+read_gdef_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
 {
 {
+  OTF_Stream *stream = table->stream;
   char *errfmt = "GDEF%s";
   void *errret = NULL;
   OTF_GDEF *gdef;
 
   OTF_CALLOC (gdef, 1, "");
   char *errfmt = "GDEF%s";
   void *errret = NULL;
   OTF_GDEF *gdef;
 
   OTF_CALLOC (gdef, 1, "");
-  read_gdef_header (stream, (OTF_GDEFHeader *) &gdef->header);
-  if (gdef->header.GlyphClassDef)
-    {
-      gdef->glyph_class_def.offset = gdef->header.GlyphClassDef;
-      read_class_def_without_offset (otf, stream, &gdef->glyph_class_def);
-    }
-  if (gdef->header.AttachList)
-    read_attach_list (otf, stream, gdef->header.AttachList,
-                     &gdef->attach_list);
-  if (gdef->header.LigCaretList)
-    read_lig_caret_list (otf, stream, gdef->header.LigCaretList,
-                        &gdef->lig_caret_list);
-  if (gdef->header.MarkAttachClassDef)
+  if (stream->buf)
     {
     {
-      gdef->mark_attach_class_def.offset = gdef->header.MarkAttachClassDef;
-      read_class_def_without_offset (otf, stream, &gdef->mark_attach_class_def);
+      read_gdef_header (stream, (OTF_GDEFHeader *) &gdef->header);
+      if (gdef->header.GlyphClassDef)
+       {
+         gdef->glyph_class_def.offset = gdef->header.GlyphClassDef;
+         read_class_def_without_offset (otf, stream,
+                                        &gdef->glyph_class_def);
+       }
+      if (gdef->header.AttachList)
+       read_attach_list (otf, stream, gdef->header.AttachList,
+                         &gdef->attach_list);
+      if (gdef->header.LigCaretList)
+       read_lig_caret_list (otf, stream, gdef->header.LigCaretList,
+                            &gdef->lig_caret_list);
+      if (gdef->header.MarkAttachClassDef)
+       {
+         gdef->mark_attach_class_def.offset = gdef->header.MarkAttachClassDef;
+         read_class_def_without_offset (otf, stream,
+                                        &gdef->mark_attach_class_def);
+       }
     }
 
     }
 
+  *table->address = gdef;
   return gdef;
 }
 
   return gdef;
 }
 
@@ -1121,6 +1339,7 @@ read_feature_list (OTF *otf, OTF_Stream *stream, long offset,
   int errret = -1;
   int i, j;
 
   int errret = -1;
   int i, j;
 
+  SEEK_STREAM (stream, offset);
   READ_UINT16 (stream, list->FeatureCount);
   OTF_CALLOC (list->Feature, list->FeatureCount, "");
   for (i = 0; i < list->FeatureCount; i++)
   READ_UINT16 (stream, list->FeatureCount);
   OTF_CALLOC (list->Feature, list->FeatureCount, "");
   for (i = 0; i < list->FeatureCount; i++)
@@ -1351,7 +1570,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)");
   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);
   for (i = 0; i < count; i++)
     /* Offset can be zero.  */
     READ_OFFSET (stream, (*set)[i].offset);
@@ -1622,25 +1841,46 @@ read_chain_context3 (OTF *otf, OTF_Stream *stream, long offset,
 }
 
 static void *
 }
 
 static void *
-read_gsub_gpos_table (OTF *otf, OTF_Stream *stream, int gsubp)
+read_gsub_gpos_table (OTF *otf, OTF_TableInfo *table, int gsubp,
+                     enum OTF_ReaderFlag flag)
 {
 {
+  OTF_Stream *stream = table->stream;
   char *errfmt = gsubp ? "GSUB%s" : "GPOS%s";
   void *errret = NULL;
   char *errfmt = gsubp ? "GSUB%s" : "GPOS%s";
   void *errret = NULL;
-  OTF_GSUB_GPOS *gsub_gpos;
-
-  OTF_CALLOC (gsub_gpos, 1, "");
-  READ_FIXED (stream, gsub_gpos->Version);
-  READ_OFFSET (stream, gsub_gpos->ScriptList.offset);
-  READ_OFFSET (stream, gsub_gpos->FeatureList.offset);
-  READ_OFFSET (stream, gsub_gpos->LookupList.offset);
-
-  if (read_script_list (otf, stream, gsub_gpos->ScriptList.offset,
-                       &gsub_gpos->ScriptList) < 0
-      || read_feature_list (otf, stream, gsub_gpos->FeatureList.offset,
-                           &gsub_gpos->FeatureList) < 0
-      || read_lookup_list (otf, stream, gsub_gpos->LookupList.offset,
-                          &gsub_gpos->LookupList, gsubp) < 0)
+  OTF_GSUB_GPOS *gsub_gpos = *table->address;
+
+  if (gsub_gpos)
+    SEEK_STREAM (stream, 10);
+  else
+    {
+      SEEK_STREAM (stream, 0);
+      OTF_CALLOC (gsub_gpos, 1, "");
+      READ_FIXED (stream, gsub_gpos->Version);
+      READ_OFFSET (stream, gsub_gpos->ScriptList.offset);
+      READ_OFFSET (stream, gsub_gpos->FeatureList.offset);
+      READ_OFFSET (stream, gsub_gpos->LookupList.offset);
+      *table->address = gsub_gpos;
+    }
+
+  if (! gsub_gpos->ScriptList.Script
+      && read_script_list (otf, stream, gsub_gpos->ScriptList.offset,
+                          &gsub_gpos->ScriptList) < 0)
     return NULL;
     return NULL;
+  if (flag != OTF_READ_SCRIPTS)
+    {
+      if (! gsub_gpos->FeatureList.Feature
+         && read_feature_list (otf, stream, gsub_gpos->FeatureList.offset,
+                               &gsub_gpos->FeatureList) < 0)
+       return NULL;
+      if (flag != OTF_READ_FEATURES)
+       {
+         if (! gsub_gpos->LookupList.Lookup
+             && read_lookup_list (otf, stream, gsub_gpos->LookupList.offset,
+                                  &gsub_gpos->LookupList, gsubp) < 0)
+           return NULL;
+       }
+    }
+
   return gsub_gpos;
 }
 
   return gsub_gpos;
 }
 
@@ -1778,7 +2018,7 @@ read_reverse_chain1 (OTF *otf, OTF_Stream *stream, long offset,
   count = read_glyph_ids (otf, stream, &reverse_chain->Substitute, 0, -1);
   if (count <= 0)
     return -1;
   count = read_glyph_ids (otf, stream, &reverse_chain->Substitute, 0, -1);
   if (count <= 0)
     return -1;
-  reverse_chain->GlyphCount = count;  
+  reverse_chain->GlyphCount = count;
   return 0;
 }
 
   return 0;
 }
 
@@ -1942,9 +2182,9 @@ read_lookup_subtable_gsub (OTF *otf, OTF_Stream *stream, long offset,
 }
 
 static void *
 }
 
 static void *
-read_gsub_table (OTF *otf, OTF_Stream *stream)
+read_gsub_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
 {
 {
-  return read_gsub_gpos_table (otf, stream, 1);
+  return read_gsub_gpos_table (otf, table, 1, flag);
 }
 
 \f
 }
 
 \f
@@ -1967,7 +2207,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)
     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);
     READ_INT16 (stream, value_record->YPlacement);
   if (bit & OTF_XAdvance)
     READ_INT16 (stream, value_record->XAdvance);
@@ -2213,9 +2453,16 @@ read_ligature_attach (OTF *otf, OTF_Stream *stream, long offset,
     }
   for (i = 0; i < attach->ComponentCount; i++)
     for (j = 0; j < ClassCount; j++)
     }
   for (i = 0; i < attach->ComponentCount; i++)
     for (j = 0; j < ClassCount; j++)
-      if (read_anchor (otf, stream, offset + attach->offset,
-                      &attach->ComponentRecord[i].LigatureAnchor[j]) < 0)
-       return -1;
+      {
+       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;
 }
 
   return 0;
 }
 
@@ -2463,9 +2710,9 @@ read_lookup_subtable_gpos (OTF *otf, OTF_Stream *stream,
 }
 
 static void *
 }
 
 static void *
-read_gpos_table (OTF *otf, OTF_Stream *stream)
+read_gpos_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
 {
 {
-  return read_gsub_gpos_table (otf, stream, 0);
+  return read_gsub_gpos_table (otf, table, 0, flag);
 }
 
 \f
 }
 
 \f
@@ -2498,7 +2745,7 @@ read_jstf_table (OTF_Stream *stream, long offset)
 \f
 /*** (1-11) Structure for OTF */
 
 \f
 /*** (1-11) Structure for OTF */
 
-int
+static int
 read_offset_table (OTF *otf, OTF_Stream *stream, OTF_OffsetTable *table)
 {
   int errret = -1;
 read_offset_table (OTF *otf, OTF_Stream *stream, OTF_OffsetTable *table)
 {
   int errret = -1;
@@ -2521,9 +2768,9 @@ read_table_directory (OTF_Stream *stream, OTF_TableDirectory *table)
   table->tag = tag;
   table->name[0] = tag >> 24;
   table->name[1] = (tag >> 16) & 0xFF;
   table->tag = tag;
   table->name[0] = tag >> 24;
   table->name[1] = (tag >> 16) & 0xFF;
-  table->name[0] = (tag >> 8) & 0xFF;
-  table->name[0] = tag >> 8;
-  table->name[0] = '\0';
+  table->name[2] = (tag >> 8) & 0xFF;
+  table->name[3] = tag & 0xFF;
+  table->name[4] = '\0';
   READ_ULONG (stream, table->checkSum);
   READ_ULONG (stream, table->offset);
   READ_ULONG (stream, table->length);
   READ_ULONG (stream, table->checkSum);
   READ_ULONG (stream, table->offset);
   READ_ULONG (stream, table->length);
@@ -2531,12 +2778,10 @@ read_table_directory (OTF_Stream *stream, OTF_TableDirectory *table)
 }
 
 static int
 }
 
 static int
-read_header_part (OTF *otf, FILE *fp)
+read_header_part (OTF *otf, FILE *fp, FT_Face face)
 {
   char *errfmt = "otf header%s";
   int errret = -1;
 {
   char *errfmt = "otf header%s";
   int errret = -1;
-  OTF_Tag head_tag, name_tag, cmap_tag, gdef_tag, gsub_tag, gpos_tag;
-  OTF_Stream *stream;
   int i;
   OTF_InternalData *internal_data = (OTF_InternalData *) otf->internal_data;
 
   int i;
   OTF_InternalData *internal_data = (OTF_InternalData *) otf->internal_data;
 
@@ -2553,69 +2798,108 @@ read_header_part (OTF *otf, FILE *fp)
   internal_data->table_info[OTF_TABLE_TYPE_GPOS].address = (void *) &otf->gpos;
   internal_data->table_info[OTF_TABLE_TYPE_GPOS].reader = read_gpos_table;
 
   internal_data->table_info[OTF_TABLE_TYPE_GPOS].address = (void *) &otf->gpos;
   internal_data->table_info[OTF_TABLE_TYPE_GPOS].reader = read_gpos_table;
 
-  head_tag = OTF_tag ("head");
-  name_tag = OTF_tag ("name");
-  cmap_tag = OTF_tag ("cmap");
-  gdef_tag = OTF_tag ("GDEF");
-  gsub_tag = OTF_tag ("GSUB");
-  gpos_tag = OTF_tag ("GPOS");
+  if (fp)
+    {
+      OTF_Tag head_tag = OTF_tag ("head");
+      OTF_Tag name_tag = OTF_tag ("name");
+      OTF_Tag cmap_tag = OTF_tag ("cmap");
+      OTF_Tag gdef_tag = OTF_tag ("GDEF");
+      OTF_Tag gsub_tag = OTF_tag ("GSUB");
+      OTF_Tag gpos_tag = OTF_tag ("GPOS");
+      OTF_Stream *stream = make_stream ("Offset Table");
+      unsigned char ttctag[4];
 
 
-  stream = make_stream ();
-  if (! stream)
-    return -1;
+      if (! stream)
+       return -1;
+      internal_data->header_stream = stream;
 
 
-  internal_data->header_stream = stream;
+      /* Size of Offset Table is 12 bytes.  Size of TTC header
+        (including only the an offset of the first font) is 16.  */
+      if (setup_stream (stream, fp, 0, 16) < 0)
+       return -1;
+      READ_BYTES (stream, ttctag, 4);
+      if (memcmp (ttctag, "ttcf", 4) == 0)
+       {
+         /* This is a TrueType Collection file.  We extract the first font.  */
+         unsigned version, numfonts, offset;
+         READ_ULONG (stream, version);
+         READ_ULONG (stream, numfonts);
+         READ_ULONG (stream, offset); /* Offset of the first font.  */
+         if (setup_stream (stream, fp, offset, 12) < 0)
+           return -1;
+       }
+      else
+       SEEK_STREAM (stream, 0L);
+      if (read_offset_table (otf, stream, &otf->offset_table) < 0)
+       return -1;
+      /* Size of each Table Directory is 16 bytes.  */
+      if (setup_stream (stream, fp, stream->pos,
+                       16 * otf->offset_table.numTables) < 0)
+       return -1;
 
 
-  /* Size of Offset Table is 12 bytes.  */
-  if (setup_stream (stream, fp, 0, 12, "Offset Table") < 0)
-    return -1;
-  if (read_offset_table (otf, stream, &otf->offset_table) < 0)
-    return -1;
+      OTF_CALLOC (otf->table_dirs, otf->offset_table.numTables,
+                 " (OffsetTable)");
+      for (i = 0; i < otf->offset_table.numTables; i++)
+       {
+         OTF_Tag tag = read_table_directory (stream, otf->table_dirs + i);
+         OTF_TableInfo *table_info = NULL;
 
 
-  /* Size of each Table Directory is 16 bytes.  */
-  if (setup_stream (stream, fp, 12, 16 * otf->offset_table.numTables,
-                   "Table Directory") < 0)
-    return -1;
+         if (! tag)
+           return -1;
+         if (tag == head_tag)
+           table_info = internal_data->table_info + OTF_TABLE_TYPE_HEAD;
+         else if (tag == name_tag)
+           table_info = internal_data->table_info + OTF_TABLE_TYPE_NAME;
+         else if (tag == cmap_tag)
+           table_info = internal_data->table_info + OTF_TABLE_TYPE_CMAP;
+         else if (tag == gdef_tag)
+           table_info = internal_data->table_info + OTF_TABLE_TYPE_GDEF;
+         else if (tag == gsub_tag)
+           table_info = internal_data->table_info + OTF_TABLE_TYPE_GSUB;
+         else if (tag == gpos_tag)
+           table_info = internal_data->table_info + OTF_TABLE_TYPE_GPOS;
+
+         if (table_info)
+           {
+             table_info->stream = make_stream (otf->table_dirs[i].name);
+             if (setup_stream (table_info->stream, fp,
+                               otf->table_dirs[i].offset,
+                               otf->table_dirs[i].length) < 0)
+               return -1;
+           }
+       }
 
 
-  OTF_CALLOC (otf->table_dirs, otf->offset_table.numTables, " (OffsetTable)");
-  for (i = 0; i < otf->offset_table.numTables; i++)
+      internal_data->header_stream = NULL;
+      free_stream (stream);
+    }
+  else
     {
     {
-      OTF_Tag tag = read_table_directory (stream, otf->table_dirs + i);
-      OTF_TableInfo *table_info = NULL;
+      OTF_Stream *stream;
 
 
-      if (! tag)
-       return -1;
-      if (tag == head_tag)
-       table_info = internal_data->table_info + OTF_TABLE_TYPE_HEAD;
-      else if (tag == name_tag)
-       table_info = internal_data->table_info + OTF_TABLE_TYPE_NAME;
-      else if (tag == cmap_tag)
-       table_info = internal_data->table_info + OTF_TABLE_TYPE_CMAP;
-      else if (tag == gdef_tag)
-       table_info = internal_data->table_info + OTF_TABLE_TYPE_GDEF;
-      else if (tag == gsub_tag)
-       table_info = internal_data->table_info + OTF_TABLE_TYPE_GSUB;
-      else if (tag == gpos_tag)
-       table_info = internal_data->table_info + OTF_TABLE_TYPE_GPOS;
-
-      if (table_info)
-       {
-         table_info->stream = make_stream ();
-         if (setup_stream (table_info->stream, fp,
-                           otf->table_dirs[i].offset,
-                           otf->table_dirs[i].length,
-                           otf->table_dirs[i].name) < 0)
-           return -1;
-       }
+      internal_data->header_stream = NULL;
+      if ((stream = make_stream_from_ft_face (face, "head")))
+       internal_data->table_info[OTF_TABLE_TYPE_HEAD].stream = stream;
+      if ((stream = make_stream_from_ft_face (face, "name")))
+       internal_data->table_info[OTF_TABLE_TYPE_NAME].stream = stream;
+      if ((stream = make_stream_from_ft_face (face, "cmap")))
+       internal_data->table_info[OTF_TABLE_TYPE_CMAP].stream = stream;
+      if ((stream = make_stream_from_ft_face (face, "GDEF")))
+       internal_data->table_info[OTF_TABLE_TYPE_GDEF].stream = stream;
+      if ((stream = make_stream_from_ft_face (face, "GSUB")))
+       internal_data->table_info[OTF_TABLE_TYPE_GSUB].stream = stream;
+      if ((stream = make_stream_from_ft_face (face, "GPOS")))
+       internal_data->table_info[OTF_TABLE_TYPE_GPOS].stream = stream;
     }
 
     }
 
-  internal_data->header_stream = NULL;
-  free_stream (stream);
+  if (! internal_data->table_info[OTF_TABLE_TYPE_GDEF].stream)
+    /* We can simulate the GDEF table.  */
+    internal_data->table_info[OTF_TABLE_TYPE_GDEF].stream
+      = make_stream ("GDEF");
   return 0;
 }
 
 static OTF_TableInfo *
   return 0;
 }
 
 static OTF_TableInfo *
-get_table_info (OTF *otf, char *name)
+get_table_info (OTF *otf, const char *name)
 {
   char *errfmt = "OTF Table Read%s";
   OTF_TableInfo *errret = NULL;
 {
   char *errfmt = "OTF Table Read%s";
   OTF_TableInfo *errret = NULL;
@@ -2662,7 +2946,7 @@ get_table_info (OTF *otf, char *name)
    freeing memory previously allocated.  */
 
 OTF *
    freeing memory previously allocated.  */
 
 OTF *
-OTF_open (char *otf_name)
+OTF_open (const char *otf_name)
 {
   FILE *fp;
   char *errfmt = "opening otf (%s)";
 {
   FILE *fp;
   char *errfmt = "opening otf (%s)";
@@ -2672,11 +2956,14 @@ OTF_open (char *otf_name)
   int len = strlen (otf_name);
   const char *ext = otf_name + (len - 4);
 
   int len = strlen (otf_name);
   const char *ext = otf_name + (len - 4);
 
+  if (debug_flag < 0)
+    set_debug_flag ();
+
   if (len < 4
       || ext[0] != '.'
   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'))
+      || (strncasecmp (ext + 1, "otf", 3)
+         && strncasecmp (ext + 1, "ttf", 3)
+         && strncasecmp (ext + 1, "ttc", 3)))
     OTF_ERROR (OTF_ERROR_FILE, otf_name);
   fp = fopen (otf_name, "r");
   if (! fp)
     OTF_ERROR (OTF_ERROR_FILE, otf_name);
   fp = fopen (otf_name, "r");
   if (! fp)
@@ -2703,7 +2990,7 @@ OTF_open (char *otf_name)
      otf->internal_data->memory_record except for what allocated by
      the functions allocate_memory_record and make_stream.  */
 
      otf->internal_data->memory_record except for what allocated by
      the functions allocate_memory_record and make_stream.  */
 
-  if (read_header_part (otf, fp) < 0)
+  if (read_header_part (otf, fp, NULL) < 0)
     {
       OTF_close (otf);
       fclose (fp);
     {
       OTF_close (otf);
       fclose (fp);
@@ -2714,6 +3001,40 @@ OTF_open (char *otf_name)
   return otf;
 }
 
   return otf;
 }
 
+OTF *
+OTF_open_ft_face (FT_Face face)
+{
+  char *errfmt = "opening otf from Freetype (%s)";
+  void *errret = NULL;
+  OTF *otf;
+  OTF_InternalData *internal_data;
+
+  if (debug_flag < 0)
+    set_debug_flag ();
+
+  if (! FT_IS_SFNT (face))
+    OTF_ERROR (OTF_ERROR_FILE, (char *) face->family_name);
+  otf = calloc (1, sizeof (OTF));
+  if (! otf)
+    OTF_ERROR (OTF_ERROR_MEMORY, "body allocation");
+  otf->filename = NULL;
+
+  internal_data = calloc (1, sizeof (OTF_InternalData));
+  if (! internal_data)
+    OTF_ERROR (OTF_ERROR_MEMORY, " (InternalData");
+  otf->internal_data = internal_data;
+  if (! allocate_memory_record (otf))
+    OTF_ERROR (OTF_ERROR_MEMORY, " (InternalData)");
+
+  if (read_header_part (otf, NULL, face) < 0)
+    {
+      OTF_close (otf);
+      return NULL;
+    }
+
+  return otf;
+}
+
 /*** (2-2) OTF_close() */
 
 void
 /*** (2-2) OTF_close() */
 
 void
@@ -2725,6 +3046,7 @@ OTF_close (OTF *otf)
   if (internal_data)
     {
       OTF_MemoryRecord *memrec = internal_data->memory_record;
   if (internal_data)
     {
       OTF_MemoryRecord *memrec = internal_data->memory_record;
+      OTF_ApplicationData *app_data = internal_data->app_data;
 
       if (internal_data->header_stream)
        free_stream (internal_data->header_stream);
 
       if (internal_data->header_stream)
        free_stream (internal_data->header_stream);
@@ -2733,6 +3055,10 @@ OTF_close (OTF *otf)
        if (internal_data->table_info[i].stream)
          free_stream (internal_data->table_info[i].stream);
 
        if (internal_data->table_info[i].stream)
          free_stream (internal_data->table_info[i].stream);
 
+      for (; app_data; app_data = app_data->next)
+       if (app_data->data && app_data->freer)
+         app_data->freer (app_data->data);
+
       while (memrec)
        {
          OTF_MemoryRecord *next = memrec->next;
       while (memrec)
        {
          OTF_MemoryRecord *next = memrec->next;
@@ -2742,6 +3068,7 @@ OTF_close (OTF *otf)
          free (memrec);
          memrec = next;
        }
          free (memrec);
          memrec = next;
        }
+
       free (internal_data);
     }
   if (otf->filename)
       free (internal_data);
     }
   if (otf->filename)
@@ -2752,17 +3079,21 @@ OTF_close (OTF *otf)
 /*** (2-3) OTF_get_table() */
 
 int
 /*** (2-3) OTF_get_table() */
 
 int
-OTF_get_table (OTF *otf, char *name)
+OTF_get_table (OTF *otf, const char *name)
 {
   OTF_TableInfo *table_info = get_table_info (otf, name);
 {
   OTF_TableInfo *table_info = get_table_info (otf, name);
+  void *address;
 
   if (! table_info)
     return -1;
 
   if (! table_info)
     return -1;
+  if (! table_info->stream)
+    /* Already fully loaded.  */
+    return 0;
 
 
-  *table_info->address = (*table_info->reader) (otf, table_info->stream);
+  address = (*table_info->reader) (otf, table_info, OTF_READ_FULL);
   free_stream (table_info->stream);
   table_info->stream = NULL;
   free_stream (table_info->stream);
   table_info->stream = NULL;
-  if (! *table_info->address)
+  if (! address)
     {
       table_info->reader = NULL;
       return -1;
     {
       table_info->reader = NULL;
       return -1;
@@ -2773,24 +3104,158 @@ OTF_get_table (OTF *otf, char *name)
 /*** (2-4) OTF_check_table() */
 
 int
 /*** (2-4) OTF_check_table() */
 
 int
-OTF_check_table (OTF *otf, char *name)
+OTF_check_table (OTF *otf, const char *name)
 {
   return (get_table_info (otf, name) ? 0 : -1);
 }
 
 {
   return (get_table_info (otf, name) ? 0 : -1);
 }
 
+/*** (2-5) OTF_get_scripts()  */
 
 
+int
+OTF_get_scripts (OTF *otf, int gsubp)
+{
+  OTF_TableInfo *table_info
+    = (otf->internal_data->table_info
+       + (gsubp ? OTF_TABLE_TYPE_GSUB : OTF_TABLE_TYPE_GPOS));
+  void *address;
+
+  if (! table_info->reader)
+    return -1;
+  if (! table_info->stream)
+    /* Already fully loaded.  */
+    return 0;
+
+  address = (*table_info->reader) (otf, table_info, OTF_READ_SCRIPTS);
+  if (! address)
+    {
+      table_info->reader = NULL;
+      return -1;
+    }
+  return 0;
+}
+
+/*** (2-6) OTF_get_features()  */
+
+int
+OTF_get_features (OTF *otf, int gsubp)
+{
+  OTF_TableInfo *table_info
+    = (otf->internal_data->table_info
+       + (gsubp ? OTF_TABLE_TYPE_GSUB : OTF_TABLE_TYPE_GPOS));
+  void *address;
+
+  if (! table_info->reader)
+    return -1;
+  if (! table_info->stream)
+    {
+      if (*table_info->address)
+       /* Already fully loaded.  */
+       return 0;
+      return -1;
+    }
+
+  address = (*table_info->reader) (otf, table_info, OTF_READ_FEATURES);
+  if (! address)
+    {
+      table_info->reader = NULL;
+      return -1;
+    }
+  return 0;
+}
+
+/*** (2-7) OTF_check_features  */
+
+int
+OTF_check_features (OTF *otf, int gsubp,
+                   OTF_Tag script, OTF_Tag language, const OTF_Tag *features,
+                   int n_features)
+{
+  OTF_ScriptList *script_list;
+  OTF_Script *Script = NULL;
+  OTF_LangSys *LangSys = NULL;
+  OTF_FeatureList *feature_list;
+  int i, j;
+
+  if (OTF_get_features (otf, gsubp) < 0)
+    {
+      if (gsubp ? ! otf->gsub : ! otf->gpos)
+       return 0;
+      for (i = 0; i < n_features; i++)
+       {
+         OTF_Tag feature = features[i];
+
+         if (feature == 0)
+           continue;
+         if ((((unsigned) feature) & 0x80000000) == 0)
+           return -1;
+       }
+    }
+  if (gsubp)
+    {
+      script_list = &otf->gsub->ScriptList;
+      feature_list = &otf->gsub->FeatureList;
+    }
+  else
+    {
+      script_list = &otf->gpos->ScriptList;
+      feature_list = &otf->gpos->FeatureList;
+    }
+  for (i = 0; i < script_list->ScriptCount && ! Script; i++)
+    if (script_list->Script[i].ScriptTag == script)
+      Script = script_list->Script + i;
+  if (! Script)
+    return 0;
+  if (language)
+    {
+      for (i = 0; i < Script->LangSysCount && ! LangSys; i++)
+       if (Script->LangSysRecord[i].LangSysTag == language)
+         LangSys = Script->LangSys + i;
+      if (! LangSys)
+       return 0;
+    }
+  else
+    LangSys = &Script->DefaultLangSys;
+  for (j = 0; j < n_features; j++)
+    {
+      OTF_Tag feature = features[j];
+      int negate = 0;
+
+      if (feature == 0)
+       continue;
+      if (((unsigned) feature) & 0x80000000)
+       {
+         feature = (OTF_Tag) (((unsigned) feature) & 0x7FFFFFFF);
+         negate = 1;
+       }
+      for (i = 0; i < LangSys->FeatureCount; i++)
+       if (feature_list->Feature[LangSys->FeatureIndex[i]].FeatureTag
+           == feature)
+         {
+           if (negate)
+             return 0;
+           break;
+         }
+      if (i == LangSys->FeatureCount)
+       return 0;
+    }
+  return 1;
+}
 
 \f
 /*** (5) API miscellaneous ***/
 
 OTF_Tag
 
 \f
 /*** (5) API miscellaneous ***/
 
 OTF_Tag
-OTF_tag (char *name)
+OTF_tag (const char *name)
 {
 {
-  unsigned char *p = (unsigned char *) name;
+  const unsigned char *p = (unsigned char *) name;
 
   if (! name)
     return (OTF_Tag) 0;
 
   if (! name)
     return (OTF_Tag) 0;
-  return (OTF_Tag) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
+  return (OTF_Tag) ((p[0] << 24)
+                   | (! p[1] ? 0
+                      : ((p[1] << 16)
+                         | (! p[2] ? 0
+                            : (p[2] << 8) | p[3]))));
 }
 
 void
 }
 
 void
@@ -2802,3 +3267,44 @@ OTF_tag_name (OTF_Tag tag, char *name)
   name[3] = (char) (tag & 0xFF);
   name[4] = '\0';
 }
   name[3] = (char) (tag & 0xFF);
   name[4] = '\0';
 }
+
+int
+OTF_put_data (OTF *otf, char *id, void *data, void (*freer) (void *data))
+{
+  char *errfmt = "appdata %s";
+  int errret = -1;
+  OTF_InternalData *internal_data = (OTF_InternalData *) otf->internal_data;
+  OTF_ApplicationData *app_data = internal_data->app_data;
+  int len = strlen (id) + 1;
+
+  for (; app_data; app_data = app_data->next)
+    if (memcmp (app_data->id, id, len) == 0)
+      {
+       if (app_data->data && app_data->freer)
+         app_data->freer (app_data->data);
+       break;
+      }
+  if (! app_data)
+    {
+      OTF_MALLOC (app_data, sizeof (OTF_ApplicationData), id);
+      app_data->next = internal_data->app_data;
+      internal_data->app_data = app_data;
+      OTF_MALLOC (app_data->id, len, id);
+      memcpy (app_data->id, id, len);
+    }
+  app_data->data = data;
+  app_data->freer = freer;
+  return 0;
+}
+
+void *
+OTF_get_data (OTF *otf, char *id)
+{
+  OTF_InternalData *internal_data = (OTF_InternalData *) otf->internal_data;
+  OTF_ApplicationData *app_data = internal_data->app_data;
+
+  for (; app_data; app_data = app_data->next)
+    if (strcmp (app_data->id, id) == 0)
+      return app_data->data;
+  return NULL;
+}