(read_cmap_table): Fix bug of handling
[m17n/libotf.git] / src / otfopen.c
index bcd0d0d..aacbc30 100644 (file)
@@ -1,6 +1,6 @@
 /* otfopen.c -- OpenType font reader.
 
 /* otfopen.c -- OpenType font reader.
 
-Copyright (C) 2003, 2004, 2005
+Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009
   National Institute of Advanced Industrial Science and Technology (AIST)
   Registration Number H15PRO167
 
   National Institute of Advanced Industrial Science and Technology (AIST)
   Registration Number H15PRO167
 
@@ -29,6 +29,8 @@ write to the Free Software Foundation, Inc., 59 Temple Place, Suite
 #include "otf.h"
 #include "otferror.h"
 
 #include "otf.h"
 #include "otferror.h"
 
+#include FT_TRUETYPE_TABLES_H
+
 /***
     Table of contents (almost parallel to otf.h):
 
 /***
     Table of contents (almost parallel to otf.h):
 
@@ -80,8 +82,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 +91,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 +101,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 +131,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 +200,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);                   \
@@ -260,6 +299,16 @@ struct _OTF_TableInfo
   OTF_Stream *stream;
 };
 
   OTF_Stream *stream;
 };
 
+struct _OTF_ApplicationData
+{
+  char *id;
+  void *data;
+  void (*freer) (void *data);
+  struct _OTF_ApplicationData *next;
+};
+
+typedef struct _OTF_ApplicationData OTF_ApplicationData;
+
 struct OTF_InternalData
 {
   /* Information about each OTF table.  */
 struct OTF_InternalData
 {
   /* Information about each OTF table.  */
@@ -270,6 +319,9 @@ 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;
 };
 
 
 };
 
 
@@ -353,13 +405,14 @@ read_head_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
 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)
@@ -369,7 +422,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);
@@ -466,6 +519,85 @@ read_name_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
 \f
 /*** (1-4) "cmap" table */
 
 \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 *
 read_cmap_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
 {
 static void *
 read_cmap_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
 {
@@ -504,6 +636,8 @@ read_cmap_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
            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;
@@ -511,15 +645,23 @@ read_cmap_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
       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.language);
+         READ_ULONG (stream, cmap->EncodingRecord[i].subtable.length);     
+         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)
        {
@@ -669,13 +811,21 @@ read_cmap_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
            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)");
        }
@@ -696,13 +846,13 @@ read_cmap_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
 
            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;
@@ -922,41 +1072,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;
 }
 
@@ -1070,22 +1227,27 @@ read_gdef_table (OTF *otf, OTF_TableInfo *table, enum OTF_ReaderFlag flag)
   OTF_GDEF *gdef;
 
   OTF_CALLOC (gdef, 1, "");
   OTF_GDEF *gdef;
 
   OTF_CALLOC (gdef, 1, "");
-  read_gdef_header (stream, (OTF_GDEFHeader *) &gdef->header);
-  if (gdef->header.GlyphClassDef)
+  if (stream->buf)
     {
     {
-      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);
+      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;
     }
 
   *table->address = gdef;
@@ -2575,7 +2737,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;
@@ -2599,8 +2761,8 @@ read_table_directory (OTF_Stream *stream, OTF_TableDirectory *table)
   table->name[0] = tag >> 24;
   table->name[1] = (tag >> 16) & 0xFF;
   table->name[2] = (tag >> 8) & 0xFF;
   table->name[0] = tag >> 24;
   table->name[1] = (tag >> 16) & 0xFF;
   table->name[2] = (tag >> 8) & 0xFF;
-  table->name[3] = tag >> 8;
-  table->name[0] = '\0';
+  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);
@@ -2608,12 +2770,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;
 
@@ -2630,69 +2790,93 @@ 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");
-
-  stream = make_stream ();
-  if (! stream)
-    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;
-  if (read_offset_table (otf, stream, &otf->offset_table) < 0)
-    return -1;
+  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");
 
 
-  /* 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 (! stream)
+       return -1;
+      internal_data->header_stream = stream;
 
 
-  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 Offset Table is 12 bytes.  */
+      if (setup_stream (stream, fp, 0, 12) < 0)
+       return -1;
+      if (read_offset_table (otf, stream, &otf->offset_table) < 0)
+       return -1;
 
 
-      if (! tag)
+      /* Size of each Table Directory is 16 bytes.  */
+      if (setup_stream (stream, fp, 12, 16 * otf->offset_table.numTables) < 0)
        return -1;
        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)
+
+      OTF_CALLOC (otf->table_dirs, otf->offset_table.numTables,
+                 " (OffsetTable)");
+      for (i = 0; i < otf->offset_table.numTables; i++)
        {
        {
-         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)
+         OTF_Tag tag = read_table_directory (stream, otf->table_dirs + i);
+         OTF_TableInfo *table_info = NULL;
+
+         if (! tag)
            return -1;
            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;
+           }
        }
        }
+
+      internal_data->header_stream = NULL;
+      free_stream (stream);
     }
     }
+  else
+    {
+      OTF_Stream *stream;
 
 
-  internal_data->header_stream = NULL;
-  free_stream (stream);
+      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;
+    }
+
+  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;
@@ -2739,7 +2923,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)";
@@ -2780,7 +2964,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);
@@ -2791,6 +2975,37 @@ 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 (! 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
@@ -2802,6 +3017,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);
@@ -2810,6 +3026,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;
@@ -2819,6 +3039,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)
@@ -2829,7 +3050,7 @@ 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);
   void *address;
 {
   OTF_TableInfo *table_info = get_table_info (otf, name);
   void *address;
@@ -2854,7 +3075,7 @@ 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);
 }
@@ -2917,7 +3138,7 @@ OTF_get_features (OTF *otf, int gsubp)
 
 int
 OTF_check_features (OTF *otf, int gsubp,
 
 int
 OTF_check_features (OTF *otf, int gsubp,
-                   OTF_Tag script, OTF_Tag language, OTF_Tag *features,
+                   OTF_Tag script, OTF_Tag language, const OTF_Tag *features,
                    int n_features)
 {
   OTF_ScriptList *script_list;
                    int n_features)
 {
   OTF_ScriptList *script_list;
@@ -2992,9 +3213,9 @@ OTF_check_features (OTF *otf, int gsubp,
 /*** (5) API miscellaneous ***/
 
 OTF_Tag
 /*** (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;
@@ -3014,3 +3235,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;
+}