/* example1.c                                                      */
/*                                                                 */
/* This small program shows how to print a rotated string with the */
/* FreeType 2 library.                                             */


#include <stdio.h>
#include <string.h>
#include <math.h>

#include <ft2build.h>
#include FT_FREETYPE_H

typedef unsigned char UB;
typedef unsigned short UH;

struct strdef {
  char *label;
  UH *xstr;
} string_tab[500];

struct font_header {
  UH mincode;
  UH maxcode;
  UH height;
  short ascender;
  short descender;
  UH numchars;
} __attribute__((packed));

struct bitmap_info {
  UB width;
  UB height;
  UB pitch;
  UB top;
  UB left;
  UB advance;
  int offset;
} __attribute__((packed));

#define	XCHARLSTSIZE	1000

UH xchar_list[XCHARLSTSIZE];

int str_index;
int num_xchars;
int lineno;

void add_xchar(UH xch)
{
  int ix;
  UH *sp = xchar_list;

  for (ix = 0; ix < XCHARLSTSIZE; ix++) {
    if (*sp == xch)
      return;
    if (*sp == 0) {
      *sp = xch;
      num_xchars++;
      return;
    }
    sp++;
  }
}

int find_xchar(UH xch)
{
  int ix;
  UH *sp = xchar_list;

  ix = 0;
  while (*sp) {
    if (*sp == xch)
      return ix;
    sp++;
    ix++;
  }
  return 0;
}

int getutf(FILE *fp)
{
  int c, code, b1, b2, b3;

  c = getc(fp);
  if (c == '\n') lineno++;
  if (c == EOF) return EOF;
  if (c < 0x80) return c;
  
  switch (c & 0xe0) {
  case 0xc0:	/* 2 bytes */
    b1 = c & 0x1f;
    b2 = getc(fp);
    if (b2 == EOF) return EOF;
    if ((b2 & 0xc0) != 0x80) {
	fprintf(stderr, "bad 2nd byte!\n");
	return EOF;
    }
    code = (b1 << 6) | (b2 & 0x3f);
    break;
  case 0xe0:	/* 3 bytes */
    b1 = c & 0x0f;
    b2 = getc(fp);
    if (b2 == EOF) return EOF;
    if ((b2 & 0xc0) != 0x80) {
	fprintf(stderr, "bad 2nd byte! %x @ line %d\n", b2, lineno);
	return EOF;
    }
    b3 = getc(fp);
    if ((b3 & 0xc0) != 0x80) {
	fprintf(stderr, "bad 3rd byte!\n");
	return EOF;
    }
    code = (b1 << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3f);
    break;
  }
  return code;
}

void add_string(char *label, UH *xstr, int xlen)
{
  void *mp;

  string_tab[str_index].label = strdup(label);
  mp = calloc(xlen + 1, sizeof(short));
  string_tab[str_index].xstr = mp;
  memcpy(mp, xstr, (xlen + 1)*2);
  str_index++;
  while (xlen > 0) {
    add_xchar(*xstr++);
    xlen--;
  }
}

void
read_filter(FILE *fp)
{
  int c;
  int state = 0;
  int cpos = 0;
  char label[100];
  UH xstr[100];

  while ((c = getutf(fp)) != EOF) {
    switch (state) {
    case 0:	/* reading label */
      if (c != ':') {
	label[cpos++] = c;
      } else {
	label[cpos] = '\0';
	cpos = 0;
	state++;
      }
      break;
    case 1:	/* skipping tab */
      if (c == '\t') {
	break;
      }
      state++;
      /* fall through */
    case 2:
      if (c == '\r')
	break;
      if (c == '\n') {
	xstr[cpos] = 0;
	add_string(label, xstr, cpos);
	cpos = 0;
	state = 0;
      } else {
	xstr[cpos++] = c;
      }
      break;
    }
  }
}

void dump_xstr(char *fullname)
{
  FILE *fp;
  UH *sp;
  int i;

  fp = fopen(fullname, "w");
  if (fp == NULL) {
    fprintf(stderr, "can't open xstrdef.h\n");
    exit(1);
  }
  for (i = 0; i < str_index; i++) {
    sp = string_tab[i].xstr;
    fprintf(fp, "UH XStr%s[] = {", string_tab[i].label);
    while (*sp) {
      fprintf(fp, " 0x%04x,", find_xchar(*sp++) + 0x20);
    }
    fprintf(fp, " 0 };\n");
  }
}

void hex_dump(FILE *fp, char *cp, int nb)
{
    static int col;

    while (nb > 0) {
	fprintf(fp, " 0x%02x,", *cp++ & 0xff);
	if (++col == 8) {
	    col = 0;
	    fprintf(fp, "\n");
	}
	nb--;
    }
}

/* Replace this function with something useful. */
void
draw_bitmap(FILE *fp, FT_Bitmap*  bitmap)
{
  FT_Int  i, j, p, q;
  int nb;
  UB val, mask, *pb;

printf("width: %d, height: %d, mode: %d\n",
 bitmap->width, bitmap->rows, bitmap->pixel_mode);

  if (bitmap->pixel_mode == ft_pixel_mode_mono) {
    nb = bitmap->pitch;
    hex_dump(fp, bitmap->buffer, nb * bitmap->rows);
#define CHDUMP
#ifdef CHDUMP
    for (q = 0; q < bitmap->rows; q++) {
      pb = bitmap->buffer + nb * q;
      for (p = 0; p < bitmap->width; p++) {
	mask = 0x80 >> (p & 7);
	putchar((*pb & mask)? '*' : ' ');
	if (mask == 1) pb++;
      }
      putchar('\n');
    }
#endif
  } else {
    hex_dump(fp, bitmap->buffer, bitmap->width * bitmap->rows);
#ifdef CHDUMP
    for (q = 0; q < bitmap->rows; q++) {
      for (p = 0; p < bitmap->width; p++) {
        val = bitmap->buffer[q * bitmap->width + p];
        putchar(val == 0? ' ' : (val < 128? '+' : '*'));
      }
      putchar('\n');
    }
#endif
  }
}

int
main( int     argc,
      char**  argv )
{
  FT_Library    library;
  FT_Face       face;

  FT_GlyphSlot  slot;
  FT_Error      error;

  char*         filename;
  char*         filter;

  int           n;
  int		ptsize;
  FILE		*fp;
  int		offset;

  FT_UInt gindex;
  struct font_header fheader;
  struct bitmap_info *binfo, *bp;
  char fullname[100];


  if ( argc != 4 )
  {
    fprintf ( stderr, "usage: %s font size filter-file\n", argv[0] );
    exit( 1 );
  }

  filename      = argv[1];                           /* first argument     */
  ptsize = atoi(argv[2]);
  filter         = argv[3];                           /* second argument    */

  fp = fopen(filter, "r");
  if (fp == NULL) {
    fprintf( stderr, "can't open %s\n", filter);
    exit(1);
  }
  read_filter(fp);
  fclose(fp);

  sprintf(fullname, "xstr_%s%d.h", filename, ptsize);
  dump_xstr(fullname);

  sprintf(fullname, "fdata_%s%d.c", filename, ptsize);
  fp = fopen(fullname, "w");
  if (fp == NULL) {
    fprintf( stderr, "can't open fontdata\n", filter);
    exit(1);
  }
  fprintf(fp, "const unsigned char fdata_%s%d[] = {\n", filename, ptsize);

  fheader.mincode = 0x20;
  fheader.maxcode = 0x20 + num_xchars - 1;
  fheader.numchars = num_xchars;

  error = FT_Init_FreeType( &library );              /* initialize library */
  /* error handling omitted */

  sprintf(fullname, "%s.ttf", filename);
  error = FT_New_Face( library, fullname, 0, &face ); /* create face object */
  /* error handling omitted */

  error = FT_Set_Char_Size( face, ptsize * 64, 0,
                            100, 0 );                /* set character size */
  /* error handling omitted */

  slot = face->glyph;
  fheader.height = face->size->metrics.height >> 6;
  fheader.descender = face->size->metrics.descender >> 6;
  fheader.ascender = face->size->metrics.ascender >> 6;
printf("height: %d (%d, %d)\n", fheader.height, fheader.descender, fheader.ascender);

  binfo = (struct bitmap_info *)calloc(sizeof(struct bitmap_info), num_xchars);
  bp = binfo;
  offset = sizeof(fheader) + sizeof(struct bitmap_info) * num_xchars;


  for ( n = 0; n < num_xchars; n++ ) {
    /* load glyph image into the slot (erase previous one) */
#ifdef USE_MONO
#ifdef USE_LOAD_CHAR
    error = FT_Load_Char( face, xchar_list[n], FT_LOAD_RENDER|FT_RENDER_MODE_MONO|FT_LOAD_MONOCHROME);
#else
    gindex = FT_Get_Char_Index(face, xchar_list[n]);
    error = FT_Load_Glyph(face, gindex, FT_LOAD_MONOCHROME);
    if (error)
	continue;
    FT_GlyphSlot_Embolden(face->glyph);
    error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO);
#endif
#else
#ifdef USE_LOAD_CHAR
    error = FT_Load_Char( face, xchar_list[n], FT_LOAD_RENDER);
#else
    gindex = FT_Get_Char_Index(face, xchar_list[n]);
    error = FT_Load_Glyph(face, gindex, FT_LOAD_DEFAULT);
    if (error)
	continue;
    FT_GlyphSlot_Embolden(face->glyph);
    error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
#endif
#endif
    if ( error )
      continue;                 /* ignore errors */

    /* now, draw to our target surface (convert position) */
    bp->left = slot->bitmap_left;
    bp->top = slot->bitmap_top;
    bp->advance = slot->advance.x >> 6;
    bp->width = slot->bitmap.width;
    bp->height = slot->bitmap.rows;
    bp->pitch = slot->bitmap.pitch;
    bp->offset = offset;
    offset += bp->pitch * bp->height;
printf("bitmap_left: %d, bitmap_top: %d\n", slot->bitmap_left, slot->bitmap_top);
printf("height: %d, advance: %d\n", slot->bitmap.rows, slot->advance.x >> 6);
    bp++;
  }

  hex_dump(fp, (char *)&fheader, sizeof(fheader));

  bp = binfo;
  hex_dump(fp, (UB *)binfo, sizeof(struct bitmap_info) * num_xchars);

  for ( n = 0; n < num_xchars; n++ ) {
    /* load glyph image into the slot (erase previous one) */
#ifdef USE_MONO
#ifdef USE_LOAD_CHAR
    error = FT_Load_Char( face, xchar_list[n], FT_LOAD_RENDER|FT_RENDER_MODE_MONO|FT_LOAD_MONOCHROME);
#else
    gindex = FT_Get_Char_Index(face, xchar_list[n]);
    error = FT_Load_Glyph(face, gindex, FT_LOAD_MONOCHROME);
    if (error)
	continue;
    FT_GlyphSlot_Embolden(face->glyph);
    error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO);
#endif
#else
#ifdef USE_LOAD_CHAR
    error = FT_Load_Char( face, xchar_list[n], FT_LOAD_RENDER);
#else
    gindex = FT_Get_Char_Index(face, xchar_list[n]);
    error = FT_Load_Glyph(face, gindex, FT_LOAD_DEFAULT);
    if (error)
	continue;
    FT_GlyphSlot_Embolden(face->glyph);
    error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
#endif
#endif
    if ( error )
      continue;                 /* ignore errors */
    draw_bitmap(fp, &slot->bitmap);
printf("advance: %d\n", slot->advance);
  }

  fprintf(fp, "};\n");

  FT_Done_Face    ( face );
  FT_Done_FreeType( library );

  fclose(fp);

  return 0;
}

/* EOF */
