/* TrueType font file processing */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#if defined(MAC)
#include <console.h>
#define FSCFG_BIG_ENDIAN
#else
#define PC_OS
#endif

#ifndef SEEK_SET
#define SEEK_SET        0       /* seek from start of file      */
#define SEEK_CUR        1       /* relative to current position */
#define SEEK_END        2       /* relative to end of file      */
#endif

#define EPSILON 1.0e-10
#define MAX_ITERATIONS 50
#define COEFF_LIMIT 1.0e-20

#define ONCURVE             0x01
#define XSHORT              0x02
#define YSHORT              0x04
#define REPEAT_FLAGS        0x08 /* repeat flag n times */
#define SHORT_X_IS_POS      0x10 /* the short vector is positive */
#define NEXT_X_IS_ZERO      0x10 /* the relative x coordinate is zero */
#define SHORT_Y_IS_POS      0x20 /* the short vector is positive */
#define NEXT_Y_IS_ZERO      0x20 /* the relative y coordinate is zero */

typedef char int8;
typedef unsigned char uint8;
typedef short int16;
typedef unsigned short uint16;
typedef long int32;
typedef unsigned long uint32;

typedef short FUnit;
typedef unsigned short uFUnit;

#ifdef PC_OS
typedef long Fixed;
#endif

#ifndef FAR 
#define FAR 
#endif 

#ifndef NEAR 
#define NEAR 
#endif 

#if defined(FSCFG_BIG_ENDIAN)
/* target byte order matches Motorola 68000 */
#define SWAPL(a)        (a)
#define SWAPW(a)        (a)
#else
/* Macros to turn little endian words/longs into big endian words/longs */
#define FS_2BYTE(p)  (((unsigned short)((p)[0]) << 8) |  (p)[1])
#define FS_4BYTE(p)  (FS_2BYTE((p)+2) | ( (FS_2BYTE(p)+0L) << 16))
#define SWAPW(a) ((short) FS_2BYTE((unsigned char FAR *)(&a)))
#define SWAPL(a) ((long) FS_4BYTE((unsigned char FAR *)(&a)))
#endif

/* Constants defined in Motorola order (use SWAPL to correct) */
static uint32 tag_CharToIndexMap = 0x636d6170; /* 'cmap' */
static uint32 tag_FontHeader     = 0x68656164; /* 'head' */
static uint32 tag_GlyphData      = 0x676c7966; /* 'glyf' */
static uint32 tag_IndexToLoc     = 0x6c6f6361; /* 'loca' */
static uint32 tag_Kerning        = 0x6b65726e; /* 'kern' */
static uint32 tag_MaxProfile     = 0x6d617870; /* 'maxp' */

typedef struct {
   uint32 bc;
   uint32 ad;
   } BigDate;

typedef struct {
   int32  tag;
   uint32 checkSum;
   uint32 offset;
   uint32 length;
   } sfnt_DirectoryEntry;

typedef struct {
   int32 version;                  /* 0x10000 (1.0) */
   uint16 numOffsets;              /* number of tables */
   uint16 searchRange;             /* (max2 <= numOffsets)*16 */
   uint16 entrySelector;           /* log2 (max2 <= numOffsets) */
   uint16 rangeShift;              /* numOffsets*16-searchRange*/
   sfnt_DirectoryEntry table[1];   /* table[numOffsets] */
   } sfnt_OffsetTable;

typedef struct {
   Fixed       version;            /* for this table, set to 1.0 */
   Fixed       fontRevision;       /* For Font Manufacturer */
   uint32      checkSumAdjustment;
   uint32      magicNumber;        /* signature, must be 0x5F0F3CF5 == MAGIC */
   uint16      flags;
   uint16      unitsPerEm;         /* How many in Font Units per EM */

   BigDate     created;
   BigDate     modified;

   /** This is the font wide bounding box in ideal space
       (baselines and metrics are NOT worked into these numbers) **/
   FUnit       xMin;
   FUnit       yMin;
   FUnit       xMax;
   FUnit       yMax;

   uint16      macStyle;               /* macintosh style word */
   uint16      lowestRecPPEM;          /* lowest recommended pixels per Em */

   /* 0: fully mixed directional glyphs,
      1: only strongly L->R or T->B glyphs, 
     -1: only strongly R->L or B->T glyphs,
      2: like 1 but also contains neutrals,
     -2: like -1 but also contains neutrals */
   int16       fontDirectionHint;

   int16       indexToLocFormat;
   int16       glyphDataFormat;
   } sfnt_FontHeader;

typedef struct {
   uint16  format;
   uint16  length;
   uint16  version;
   } sfnt_mappingTable;

typedef struct {
   uint16  platformID;
   uint16  specificID;
   uint32  offset;
   } sfnt_platformEntry;

typedef sfnt_platformEntry FAR *sfnt_platformEntryPtr;
typedef sfnt_DirectoryEntry FAR *sfnt_DirectoryEntryPtr;

typedef struct {
    int16 numContours;
    int16 xMin;
    int16 yMin;
    int16 xMax;
    int16 yMax;
    } GlyphHeader;

typedef struct {
   GlyphHeader header;
   uint16 numPoints;
   uint16 *endPoints;
   uint8  *flags;
   float  *x, *y;
   } GlyphOutline;

typedef struct {
   uint8 inside_flag;  /* 1 if this an inside contour, 0 if outside */
   uint16 count;       /* Number of points in the contour */
   uint8  *flags;      /* On/off curve flags */
   float  *x, *y;      /* Coordinates of control vertices */
   } Contour;

typedef struct FontFileInfoStruct *FontFileInfoPtr;
typedef struct GlyphStruct *GlyphPtr;

/* Contour information for a single glyph */
typedef struct GlyphStruct {
   GlyphHeader header;     /* Count and sizing information about this glyph */
   uint16 glyph_index;     /* Internal glyph index for this character */
   Contour *contours;      /* Array of outline contours */
   FontFileInfoPtr parent; /* Parent font for this glyph */
   GlyphPtr next;          /* Next cached glyph */
   char   c;               /* Character code */
   } Glyph;

/* Useful general data about this font */
typedef struct FontFileInfoStruct {
   char *filename;
   FILE *fp;
   uint16 numGlyphs;
   uint16 unitsPerEm;
   int16  loca_format;
   uint32 *loca_table;
   uint32 loca_table_offset;
   uint32 cmap_table_offset;
   uint32 glyph_table_offset;
   GlyphPtr glyphs;                   /* Cached info for this font */
   struct FontFileInfoStruct *next; /* Next font */
   } FontFileInfo;

static FontFileInfo *TTFonts = NULL;

static FontFileInfo *
OpenFontFile(char *filename)
{
   int i;
   FontFileInfo *fontlist;

   /* First look to see if we have already opened this font */
   for (fontlist = TTFonts;fontlist!=NULL;fontlist=fontlist->next)
      if (!strcmp(filename, fontlist->filename))
         break;

   if (fontlist != NULL) {
      /* We have a match, use the previous information */
      if ((fontlist->fp == NULL) &&
          (fontlist->fp = fopen(fontlist->filename, "rb")) == NULL) {
         fprintf(stderr, "Can't open font file: '%s'\n",
                 fontlist->filename);
         exit(1);
         }
      }
   else {
      /* We haven't looked at this font before, let's allocate a
         holder for the information and set some defaults */
      fontlist = malloc(sizeof(FontFileInfo));
      if (fontlist == NULL) {
         fprintf(stderr, "Out of memory\n");
         exit(1);
         }
      i = strlen(filename) + 1;
      fontlist->filename = malloc(i * sizeof(char));
      strcpy(fontlist->filename, filename);
      if ((fontlist->fp = fopen(fontlist->filename, "rb")) == NULL) {
         fprintf(stderr, "Can't open font file: '%s'\n",
                 fontlist->filename);
         exit(1);
         }
      fontlist->numGlyphs = 0;
      fontlist->unitsPerEm = 16384;
      fontlist->loca_format = 0;
      fontlist->loca_table_offset = 0;
      fontlist->loca_table = NULL;
      fontlist->cmap_table_offset = 0;
      fontlist->glyph_table_offset = 0;
      fontlist->glyphs = NULL;
      fontlist->next = NULL;
      }

   return fontlist;
}

static void
FreeFontInfo()
{
   int i;
   FontFileInfo *oldfont, *tempfont;
   GlyphPtr glyphs, tempglyph;

   for (oldfont=TTFonts;oldfont!=NULL;) {
      if (oldfont->fp != NULL)
         fclose(oldfont->fp);
      free(oldfont->filename);
      if (oldfont->loca_table != NULL)
         free(oldfont->loca_table);
      for (glyphs=oldfont->glyphs;glyphs!=NULL;) {
         for (i=0;i<glyphs->header.numContours;i++) {
            free(glyphs->contours[i].flags);
            free(glyphs->contours[i].x);
            free(glyphs->contours[i].y);
            }
         free(glyphs->contours);
         tempglyph = glyphs;
         glyphs = glyphs->next;
         free(tempglyph);
         }
      tempfont = oldfont;
      oldfont = oldfont->next;
      free(tempfont);
      }
   TTFonts = NULL;
}

/* Transform a glyph from TrueType storage format to something a little
   easier to manage */
static GlyphPtr
ConvertOutlineToGlyph(FontFileInfo *ffile, GlyphOutline *ttglyph)
{
   GlyphPtr glyph;
   float *temp_x, *temp_y;
   uint8 *temp_f;
   uint16 i, j, last_j;

   /* Create storage for this glyph */
   if ((glyph = malloc(sizeof(Glyph))) == NULL)
      exit(1);
   glyph->contours = malloc(ttglyph->header.numContours * sizeof(Contour));
   if (glyph->contours == NULL)
      exit(1);

   /* Copy sizing information about this glyph */
   memcpy(&glyph->header, &ttglyph->header, sizeof(GlyphHeader));

   /* Keep track of the parent font for this glyph */
   glyph->parent = ffile;

   /* Now copy the vertex information into the contours */
   for (i=0, last_j=0;i<ttglyph->header.numContours;i++) {
      /* Figure out number of points in contour */
      j = ttglyph->endPoints[i] - last_j + 1;

      /* Copy the coordinate information into the glyph */
      temp_x = malloc((j+1) * sizeof(float));
      temp_y = malloc((j+1) * sizeof(float));
      temp_f = malloc((j+1) * sizeof(uint8));
      if (temp_x == NULL || temp_y == NULL || temp_f == NULL)
         exit(1);
      memcpy(temp_x, &ttglyph->x[last_j], j * sizeof(float));
      memcpy(temp_y, &ttglyph->y[last_j], j * sizeof(float));
      memcpy(temp_f, &ttglyph->flags[last_j], j * sizeof(uint8));
      temp_x[j] = ttglyph->x[last_j];
      temp_y[j] = ttglyph->y[last_j];
      temp_f[j] = ttglyph->flags[last_j];

      /* Figure out if this is an inside or outside contour */
      glyph->contours[i].inside_flag = 0;

      /* Plug in the reset of the contour components into the glyph */
      glyph->contours[i].count = j;
      glyph->contours[i].x = temp_x;
      glyph->contours[i].y = temp_y;
      glyph->contours[i].flags = temp_f;

      /* Set last_j to point to the beginning of the next contour's
         coordinate information */
      last_j = ttglyph->endPoints[i] + 1;
      }

/* Show statistics about this glyph */
/*
printf("Number of contours: %u\n",
       glyph->header.numContours);
printf("X extent: [%f, %f]\n",
       (float)glyph->header.xMin / (float)glyph->parent->unitsPerEm,
       (float)glyph->header.xMax / (float)glyph->parent->unitsPerEm);
printf("Y extent: [%f, %f]\n",
       (float)glyph->header.yMin / (float)glyph->parent->unitsPerEm,
       (float)glyph->header.yMax / (float)glyph->parent->unitsPerEm);
printf("Converted coord list(%d):\n", (int)glyph->header.numContours);
for (i=0;i<glyph->header.numContours;i++) {
   for (j=0;j<=glyph->contours[i].count;j++)
      printf("  %c[%f, %f]\n",
             (glyph->contours[i].flags[j] & ONCURVE ? '*' : ' '),
             glyph->contours[i].x[j], glyph->contours[i].y[j]);
   printf("\n");
   }
*/

   return glyph;
}

/* Read the contour information for a specific glyph */
static GlyphPtr
ExtractGlyphInfo(FontFileInfo *ffile, uint16 glyph_index, unsigned char c)
{
   int i, j;
   uint8 temp8, flag, repeat_count;
   uint16 temp16, n, nc;
   int16 coord, itemp16;
   Fixed tempF;
   GlyphOutline *ttglyph;
   GlyphPtr glyph;

   if ((ttglyph = malloc(sizeof(GlyphOutline))) == NULL) {
      fprintf(stderr, "Out of memory\n");
      exit(1);
      }

   fseek(ffile->fp, ffile->glyph_table_offset +
                    ffile->loca_table[glyph_index], SEEK_SET);
   fread(&ttglyph->header, 1, sizeof(GlyphHeader), ffile->fp);
   ttglyph->header.numContours = SWAPW(ttglyph->header.numContours);
   ttglyph->header.xMax        = SWAPW(ttglyph->header.xMax);
   ttglyph->header.yMax        = SWAPW(ttglyph->header.yMax);
   ttglyph->header.xMin        = SWAPW(ttglyph->header.xMin);
   ttglyph->header.yMin        = SWAPW(ttglyph->header.yMin);

   nc = ttglyph->header.numContours;
   if (nc > 0) {
      /* Grab the contour endpoints */
      if ((ttglyph->endPoints = malloc(nc * sizeof(uint16))) == NULL) {
         fprintf(stderr, "Out of memory\n");
         exit(1);
         }
      for (i=0;i<nc;i++) {
         fread(&temp16, 1, sizeof(temp16), ffile->fp);
         ttglyph->endPoints[i] = SWAPW(temp16);
         }

      /* Skip over the instructions */
      fread(&temp16, 1, sizeof(temp16), ffile->fp);
      fseek(ffile->fp, SWAPW(temp16), SEEK_CUR);

      /* Determine the number of points making up this glyph */
      n = ttglyph->numPoints = ttglyph->endPoints[nc-1] + 1;

      /* Read the flags */
      if ((ttglyph->flags = malloc(n * sizeof(uint8))) == NULL) {
         fprintf(stderr, "Out of memory\n");
         exit(1);
         }
      for (i=0;i<ttglyph->numPoints;i++) {
         fread(&ttglyph->flags[i], 1, 1, ffile->fp);
         if (ttglyph->flags[i] & REPEAT_FLAGS) {
            fread(&repeat_count, 1, 1, ffile->fp);
            for (;repeat_count>0;repeat_count--,i++) {
               ttglyph->flags[i+1] = ttglyph->flags[i];
               }
            }
         }

      /* Read the coordinate vectors */
      if ((ttglyph->x = malloc(n * sizeof(float))) == NULL ||
          (ttglyph->y = malloc(n * sizeof(float))) == NULL) {
         fprintf(stderr, "Out of memory\n");
         exit(1);
         }
      coord = 0;
      for (i=0;i<ttglyph->numPoints;i++) {
         /* Read each x coordinate */
         flag = ttglyph->flags[i];
         if (flag & XSHORT) {
            fread(&temp8, 1, 1, ffile->fp);
            if (flag & SHORT_X_IS_POS)
               coord += temp8;
            else
               coord -= temp8;
            }
         else if (!(flag & NEXT_X_IS_ZERO)) {
            fread(&itemp16, 1, 2, ffile->fp);
            coord += SWAPW(itemp16);
            }
         ttglyph->x[i] = (float)coord / (float)ffile->unitsPerEm;
         }

      coord = 0;
      for (i=0;i<ttglyph->numPoints;i++) {
         /* Read each y coordinate */
         flag = ttglyph->flags[i];
         if (flag & YSHORT) {
            fread(&temp8, 1, 1, ffile->fp);
            if (flag & SHORT_Y_IS_POS)
               coord += temp8;
            else
               coord -= temp8;
            }
         else if (!(flag & NEXT_Y_IS_ZERO)) {
            fread(&itemp16, 1, 2, ffile->fp);
            coord += SWAPW(itemp16);
            }
         ttglyph->y[i] = (float)coord / (float)ffile->unitsPerEm;
         }

      /* Convert the glyph outline information from TrueType layout
         into a more easily processed format */
      glyph = ConvertOutlineToGlyph(ffile, ttglyph);
      glyph->c = c;
      glyph->glyph_index = glyph_index;

      /* Free up outline information */
      free(ttglyph->y);
      free(ttglyph->x);
      free(ttglyph->endPoints);
      free(ttglyph->flags);
      }
   else if (nc == 0) {
      /* Do nothing */
      }
   else {
      printf("Can't handle multiple component glyphs\n");
      }

   return glyph;
}

/* The file pointer must be pointing immediately after the version entry
   in the encoding table for the next two functions to work. */
static uint16
ProcessFormat0Glyph(FILE *fp, unsigned char search_char)
{
   uint8 temp_index;

   fseek(fp, search_char, SEEK_CUR);
   fread(&temp_index, 1, 1, fp); /* Each index is exactly 1 byte */
   return (uint16)temp_index;
}

static uint16
ProcessFormat4Glyph(FILE *fp, unsigned char search_char)
{
   int i;
   uint16  glyph_index;
   long glyphIDoffset;
   uint16 temp16;
   uint16 segCount, searchRange, entrySelector, rangeShift;
   uint16 *endCount, *startCount, *idDelta, *idRangeOffset;

   fread(&temp16, 1, sizeof(uint16), fp);
   segCount = SWAPW(temp16) >> 1;
   fread(&temp16, 1, sizeof(uint16), fp);
   searchRange = SWAPW(temp16);
   fread(&temp16, 1, sizeof(uint16), fp);
   entrySelector = SWAPW(temp16);
   fread(&temp16, 1, sizeof(uint16), fp);
   rangeShift = SWAPW(temp16);

   /* Now allocate and read in the segment arrays */
   endCount      = malloc(segCount * sizeof(uint16));
   startCount    = malloc(segCount * sizeof(uint16));
   idDelta       = malloc(segCount * sizeof(uint16));
   idRangeOffset = malloc(segCount * sizeof(uint16));
   if (endCount == NULL || startCount == NULL || idDelta == NULL ||
       idRangeOffset == NULL) {
      fprintf(stderr, "Out of memory\n");
      exit(1);
      }
   for (i=0;i<segCount;i++) {
      fread(&temp16, 1, sizeof(uint16), fp);
      endCount[i] = SWAPW(temp16);
      }
   fread(&temp16, 1, sizeof(uint16), fp); /* Skip over 'reservedPad' */
   for (i=0;i<segCount;i++) {
      fread(&temp16, 1, sizeof(uint16), fp);
      startCount[i] = SWAPW(temp16);
      }
   for (i=0;i<segCount;i++) {
      fread(&temp16, 1, sizeof(uint16), fp);
      idDelta[i] = SWAPW(temp16);
      }
   glyphIDoffset = ftell(fp);
   for (i=0;i<segCount;i++) {
      fread(&temp16, 1, sizeof(uint16), fp);
      idRangeOffset[i] = SWAPW(temp16);
      }

   /* Now search the segments for our character */
   glyph_index = 0;
   for (i=0;i<segCount;i++) {
      if ((uint8)search_char <= endCount[i]) {
         if ((uint8)search_char > startCount[i]) {
            /* Found mapping */
            if (idRangeOffset[i] == 0)
               glyph_index = (uint16)search_char + idDelta[i];
            else {
               /* Alternate encoding of glyph indices, relies on a quite
                  unusual way of storing the offsets.  Not really sure about
                  the division by 2 on idRangeOffset, but the Microsoft
                  manual seemed to do it that way. */
               glyphIDoffset += i * sizeof(uint16) + idRangeOffset[i]/2 +
                                (search_char - startCount[i]);
               fseek(fp, glyphIDoffset, SEEK_SET);
               fread(&temp16, 1, sizeof(uint16), fp);
               glyph_index = SWAPW(temp16);
               if (glyph_index != 0)
                  glyph_index = glyph_index + idDelta[i];
               }
            }
         break;
         }
      }

   /* Deallocate the memory we used for the segment arrays */
   free(endCount);
   free(startCount);
   free(idDelta);
   free(idRangeOffset);

   return glyph_index;
}

/* Trimmed table mapping */
static uint16
ProcessFormat6Glyph(FILE *fp, unsigned char search_char)
{
   uint16 temp16, firstCode, entryCount;
   uint8 glyph_index;

   fread(&temp16, 1, 2, fp);
   firstCode = SWAPW(temp16);
   fread(&temp16, 1, 2, fp);
   entryCount = SWAPW(temp16);
   if (search_char >= firstCode && search_char < firstCode + entryCount) {
      fseek(fp, search_char - firstCode, SEEK_CUR);
      fread(&temp16, 1, 2, fp);
      glyph_index = SWAPW(temp16);
      }
   else
      glyph_index = 0;
   return glyph_index;
}


/* find the character mapping for 'search_char' */
static uint16
ProcessCharMap(FontFileInfo *ffile, unsigned char search_char)
{
   long old_table_offset;
   uint16 glyph_index, temp16;
   uint16 entry_ID, entry_EID;
   uint32 entry_offset;
   sfnt_platformEntry cmapEntry;
   sfnt_mappingTable encodingTable;
   int i, table_count;

   /* Move to the start of the character map, skipping over the format
      entry, right to the number of entries. */
   fseek(ffile->fp, ffile->cmap_table_offset + sizeof(uint16), SEEK_SET);
   fread(&temp16, 1, sizeof(uint16), ffile->fp);
   table_count = SWAPW(temp16);

   /* Search the tables until we find the glyph index for the search
      character.  Just return the first one we find... */
   for (i=0;i<table_count;i++) {
      fread(&cmapEntry, 1, sizeof(cmapEntry), ffile->fp);
      entry_ID     = SWAPW(cmapEntry.platformID);
      entry_EID    = SWAPW(cmapEntry.specificID);
      entry_offset = SWAPL(cmapEntry.offset);
      old_table_offset = ftell(ffile->fp);

      fseek(ffile->fp, ffile->cmap_table_offset + entry_offset, SEEK_SET);
      fread(&encodingTable, 1, sizeof(encodingTable), ffile->fp);
      /*
      printf("Encoding table, format: %u, length: %u, version: %u\n",
             SWAPW(encodingTable.format),
             SWAPW(encodingTable.length),
             SWAPW(encodingTable.version));
      */

      if (SWAPW(encodingTable.format) == 0) {
         /* Translation is simple - add 'entry_char' to the start
            of the table and grab what's there */
         /* printf("Apple standard index mapping\n"); */
         glyph_index = ProcessFormat0Glyph(ffile->fp, search_char);
         }
      else if (SWAPW(encodingTable.format) == 4) {
         /* Microsoft UGL encoding */
         /* printf("Microsoft standard index mapping\n"); */
         glyph_index = ProcessFormat4Glyph(ffile->fp, search_char);
         }
      else if (SWAPW(encodingTable.format) == 6) {
         /* printf("Trimmed table mapping\n"); */
         glyph_index = ProcessFormat6Glyph(ffile->fp, search_char);
         }
      else
         printf("Unsupported index mapping format: %u\n",
                SWAPW(encodingTable.format));

      return glyph_index;

      /* Go back to the set of table descriptions at the beginning of
         the cmap table. */
      fseek(ffile->fp, old_table_offset, SEEK_SET);
      }

   /* No character mapping was found - very odd, we should really have
      had the character in at least one table.  Perhaps getting here
      means we didn't have any character mapping tables */
   return 0;
}

/* This routine determines the total number of glyphs in a TrueType file.
   Necessary so that we can allocate the proper amount of storage for
   the glyph location table.  */
static void
ProcessMaxpTable(FontFileInfo *ffile, sfnt_DirectoryEntry *Table)
{
   long old_fp_offset;
   Fixed temp_fixed;
   uint16 temp16;

   old_fp_offset = ftell(ffile->fp);
   fseek(ffile->fp, SWAPL(Table->offset), SEEK_SET);
   fread(&temp_fixed, 1, sizeof(temp_fixed), ffile->fp);
   fread(&temp16, 1, sizeof(temp16), ffile->fp);
   ffile->numGlyphs = SWAPW(temp16);
   fseek(ffile->fp, old_fp_offset, SEEK_SET);
}

/* Process the font header table */
static void
ProcessHeadTable(FontFileInfo *ffile, sfnt_DirectoryEntry *Table)
{
   long old_fp_offset;
   sfnt_FontHeader fontHeader;

   old_fp_offset = ftell(ffile->fp);
   fseek(ffile->fp, SWAPL(Table->offset), SEEK_SET);

   fread(&fontHeader, 1, sizeof(fontHeader), ffile->fp);
   ffile->loca_format = SWAPW(fontHeader.indexToLocFormat);
   ffile->unitsPerEm = SWAPW(fontHeader.unitsPerEm);

   /* Back to where we started */
   fseek(ffile->fp, old_fp_offset, SEEK_SET);
}

/* Determine the relative offsets of glyphs */
static void
ProcessLocaTable(FontFileInfo *ffile)
{
   int i;
   uint16 temp16;
   uint32 temp32;

   /* Move to location of table in file */
   fseek(ffile->fp, ffile->loca_table_offset, SEEK_SET);

   /* Make sure we understand the size of the entries in the table */
   ffile->loca_table = malloc((ffile->numGlyphs + 1) * sizeof(uint32));
   if (ffile->loca_table == NULL) {
      fprintf(stderr, "Out of memory\n");
      exit(1);
      }

   /* Now read and save the loca table */
   for (i=0;i<ffile->numGlyphs;i++) {
      if (ffile->loca_format == 0) {
         fread(&temp16, 1, sizeof(uint16), ffile->fp);
         ffile->loca_table[i] = (uint32)SWAPW(temp16) << 1;
         }
      else {
         fread(&temp32, 1, sizeof(uint32), ffile->fp);
         ffile->loca_table[i] = SWAPL(temp32);
         }
      }
}

/* Gather information about a specific font.  The font file must
   be opened prior to calling this routine. */
static FontFileInfo *
ProcessFontFile(char *fontfilename)
{
   unsigned i, c;
   sfnt_OffsetTable OffsetTable;
   sfnt_DirectoryEntry Table;
   FontFileInfo *ffile;

   /* Open the font file */
   ffile = OpenFontFile(fontfilename);

   /* Read the initial directory header on the TTF.  The numOffsets variable
      tells us how many tables are present in this file. */
   fread(&OffsetTable, 1, sizeof(OffsetTable) - sizeof(sfnt_DirectoryEntry),
         ffile->fp);
   c = (unsigned)SWAPW(OffsetTable.numOffsets);

   /* Process general font information and save it. */
   for (i = 0; i < c && i < 40; i++) {
      if (fread(&Table, 1, sizeof(Table), ffile->fp) != sizeof(Table)) {
         printf("Read failed on table #%d\n", i);
         exit(-1);
         }
      if (Table.tag == SWAPL(tag_CharToIndexMap))
         ffile->cmap_table_offset = SWAPL(Table.offset);
      else if (Table.tag == SWAPL(tag_MaxProfile))
         ProcessMaxpTable(ffile, &Table);
      else if (Table.tag == SWAPL(tag_IndexToLoc))
         ffile->loca_table_offset = SWAPL(Table.offset);
      else if (Table.tag == SWAPL(tag_FontHeader))
         ProcessHeadTable(ffile, &Table);
      else if (Table.tag == SWAPL(tag_GlyphData))
         ffile->glyph_table_offset = SWAPL(Table.offset);
      }

   /* Close the font file descriptor (don't want to waste them) */
   fclose(ffile->fp);
   ffile->fp = NULL;

   /* Return the information about this font */
   return ffile;
}


static GlyphPtr
ProcessCharacter(FontFileInfo *ffile, unsigned char search_char)
{
   uint16 glyph_index;
   GlyphOutline *ttglyph;
   GlyphPtr glyph;

   /* See if we have already processed this glyph */
   if (ffile->glyphs != NULL) {
      for (glyph=ffile->glyphs;glyph!=NULL;glyph=glyph->next)
         if (glyph->c == search_char) {
            /* Found it, no need to do any more work */
            return glyph;
            }
      }
      
   /* Open the font file (if necessary) */
   if ((ffile->fp == NULL) &&
       (ffile->fp = fopen(ffile->filename, "rb")) == NULL) {
      fprintf(stderr, "Can't open font file: '%s'\n", ffile->filename);
      exit(1);
      }

   /* Now go extract information about the search character */
   if (ffile->numGlyphs > 0 && ffile->loca_table_offset > 0 &&
       ffile->cmap_table_offset > 0 && ffile->glyph_table_offset > 0) {
      /* We have enough information on the file layout to go look for
         the information about the search character */
      ProcessLocaTable(ffile);
      glyph_index = ProcessCharMap(ffile, search_char);
      glyph = ExtractGlyphInfo(ffile, glyph_index, search_char);

      /* Add this glyph to the ones we already know about */
      glyph->next = ffile->glyphs;
      ffile->glyphs = glyph;
      }

   /* Close the font file */
   fclose(ffile->fp);
   ffile->fp = NULL;

   /* Glyph is all built */
   return glyph;
}

static void
OutputPolyrayGlyph(FontFileInfo *ffile, Glyph *glyph, float offset)
{
   int i, j;

   printf("object { // Char: '%c'\n", (char)glyph->c);
   printf("   glyph %u\n", glyph->header.numContours);
   for (i=0;i<glyph->header.numContours;i++) {
      printf("      contour %d, ", (int)glyph->contours[i].count);
      for (j=0;j<glyph->contours[i].count;j++) {
         printf("<%f, %f, %c>",
                glyph->contours[i].x[j] + offset, glyph->contours[i].y[j],
                (glyph->contours[i].flags[j] & ONCURVE ? '0' : '1'));
         if (j < glyph->contours[i].count - 1) {
            printf(", ");
	    if (j > 0 && !(j % 5))
	       printf("\n");
	    }
         else
            printf("\n");
         }
      }
printf("   }\n");
}

int
main(int argc, char **argv) 
{
   FontFileInfo *ffile;
   int i, slen;
   unsigned char *search_string = NULL;
   GlyphPtr glyph;
   float toffset, offset;

#ifdef MAC
   /* Special case for Macintosh - this command pulls up a
      dialog that simulates a command line */
   argc = ccommand(&argv);
#endif

   if (argc < 2) {
      printf("usage: ttfx *.ttf string [char offset]\n");
      printf("e.g.\n");
      printf("       ttfx arialb.ttf Foo 0.1\n");
      printf("       ttfx \\windows\\system\\times.ttf Foo\n");
      exit(-1);
      }
   else if (argc > 2) {
      slen = strlen(argv[2]);
      search_string = malloc((slen + 1) * sizeof(unsigned char));
      for (i=0;i<=slen;i++)
         search_string[i] = (unsigned char)argv[2][i];
      if (argc > 3)
         offset = atof(argv[3]);
      else
         offset = 0.0;
      }
   else {
      slen = 1;
      search_string = malloc(2 * sizeof(unsigned char));
      search_string[0] = 'X';
      search_string[1] = '\0';
      offset = 0.0;
      }

   /* Get general font info */
   ffile = ProcessFontFile(argv[1]);

   printf("// Font: '%s'\n", ffile->filename);
   if (slen > 1)
      /* Need to create the string as the union of the glyphs that
         make up each character in the string */
      printf("object {\n");

   /* Get info about each character in the string */
   for (i=0,toffset=0.0;i<slen;i++) {
      glyph = ProcessCharacter(ffile, search_string[i]);
/* printf("Kick: %f\n",
	  glyph->header.xMin / (float)glyph->parent->unitsPerEm); */
      /* Align this character to x = 0 on the left */
      toffset -= glyph->header.xMin / (float)glyph->parent->unitsPerEm;

      /* Additional spacing based on the kerning table could go right here */

      /* Now we convert the glyph into Polyray format */
      OutputPolyrayGlyph(ffile, glyph, toffset);

/*
printf("Bump: %f\n", offset +
                (glyph->header.xMax - glyph->header.xMin) /
                   (float)glyph->parent->unitsPerEm);
*/
      /* Adjust character spacing to account for the width of the character,
         plus the user-specified character spacing */
      toffset += offset +
                (glyph->header.xMax - glyph->header.xMin) /
                   (float)glyph->parent->unitsPerEm;

      /* If this is a union, then need to emit the "+" character */
      if (slen > 1 && i < slen - 1)
         printf("+\n");
      }

   /* Close up the union of glyphs */
   if (slen > 1)
      printf("   }\n");

   FreeFontInfo();

   if (search_string != NULL)
      free(search_string);

   return 0;
}
