/* ft2sswf.c - written by Alexis Wilke for Made to Order Software Corp. (c) 2002-2009 */

/*

Copyright (c) 2002-2009 Made to Order Software Corp.

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/


#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<errno.h>
#include	<ctype.h>

#include	<ft2build.h>
#include	FT_FREETYPE_H

/* defines the wc_t type for the printf(3C) */
#include	"sswf/libsswf-config.h"


#ifdef _MSVC
#define	strcasecmp		stricmp
// VC8 defines MB_CUR_MAX as a variable!
#ifndef MB_CUR_MAX
#define MB_CUR_MAX	64
//#elif !MB_CUR_MAX
//#undef MB_CUR_MAX
//#define MB_CUR_MAX	64
#endif
#endif


const char *		progname = "ft2sswf";		/* default name of this tool */
const char *		version = TO_STR(SSWF_VERSION);	/* version of this tool */
const char *		glyphs;				/* glyhps to print out */
wchar_t *		sorted_glyphs;			/* the list of glyphs to print out - sorted and unique */
long			errcnt;				/* count errors, if 1 or more occurs, then exit with 1 */
long			encoding;			/* whether to print out the encoding (0, 1 or 2) */
long			verbose;			/* writes out what we're doing */
long			output_control;			/* if true, print out the control characters too */
long			comment;			/* whether comments are generated */
long			include_null;			/* by default skip any empty character (except the space) */
double			factor;				/* multiply all the points (positions) by this factor */
double			advance;			/* the default number of pixels to the advance to the next character */
FILE *			output;				/* the output file (default stdout) */
FT_Library		library;			/* the font library pointer */
FT_Encoding		char_map_encoding;		/* what character map to use to read the chars (default -1 -- i.e. use 1st entry available) */

struct option_to_char {
	size_t		length;		/* total length so far */
	int		index;		/* start at 0 */
	const char *	glyphs;		/* the original string */
	wchar_t		letter;		/* the last letter returned */
};

struct encoding_list {
	const char *	name;
	FT_Encoding	encoding;
};

struct encoding_list		list_of_encodings[] = {
	{ "default",		-1 },
	{ "none",		ft_encoding_none },
	{ "symbol",		ft_encoding_symbol },
	{ "unicode",		ft_encoding_unicode },
	{ "latin 1",		ft_encoding_latin_1 },
	{ "latin1",		ft_encoding_latin_1 },
	{ "latin 2",		ft_encoding_latin_2 },
	{ "latin2",		ft_encoding_latin_2 },
	{ "japanese",		ft_encoding_sjis },
	{ "sjis",		ft_encoding_sjis },
	{ "gb2312",		ft_encoding_gb2312 },
	{ "big 5",		ft_encoding_big5 },
	{ "big5",		ft_encoding_big5 },
	{ "wansung",		ft_encoding_wansung },
	{ "johab",		ft_encoding_johab },
	{ "adobe standard",	ft_encoding_adobe_standard },
	{ "adobe-standard",	ft_encoding_adobe_standard },
	{ "adobe std",		ft_encoding_adobe_standard },
	{ "adobe-std",		ft_encoding_adobe_standard },
	{ "adobe expert",	ft_encoding_adobe_expert },
	{ "adobe-expert",	ft_encoding_adobe_expert },
	{ "adobe custom",	ft_encoding_adobe_custom },
	{ "adobe-custom",	ft_encoding_adobe_custom },
	{ "apple roman",	ft_encoding_apple_roman },
	{ "apple-roman",	ft_encoding_apple_roman },
	{ "apple",		ft_encoding_apple_roman }
};
#define	MAX_ENCODINGS	(sizeof(list_of_encodings) / sizeof(struct encoding_list))


const char *encoding_to_name(FT_Encoding value, char *enc)
{
	struct encoding_list	*l;
	int			i;

	enc[0] = value >> 24;
	enc[1] = value >> 16;
	enc[2] = value >> 8;
	enc[3] = value;
	enc[4] = '\0';

	l = list_of_encodings;
	for(i = 0; i < MAX_ENCODINGS; i++, l++) {
		if(value == l->encoding) {
			return l->name;
		}
	}

	return "(unknown at time of compilation)";
}


wchar_t escape_char(const char **s)
{
	const char	*esc;
	unsigned char	c;
	wchar_t		r;

	esc = *s;
	c = *esc++;
	if(c != '\\') {
		*s = esc;
		return c;
	}
	c = *esc++;
	switch(c) {
	case '\0':
		/* special case of a backslash at the end of a string */
		esc--;
		r = '\0';
		break;

	case 'a':
		r = '\a';
		break;

	case 'b':
		r = '\b';
		break;

	case 'f':
		r = '\f';
		break;

	case 'n':
		r = '\n';
		break;

	case 'r':
		r = '\r';
		break;

	case 't':
		r = '\t';
		break;

	case 'v':
		r = '\v';
		break;

	case 'x':
		r = 0;
		for(;;) {
			if(*esc >= '0' && *esc <= '9') {
				r = r * 16 + (*esc & 15);
			}
			else if(*esc >= 'a' && *esc <= 'f') {
				r = r * 16 + (*esc - 'a' + 10);
			}
			else if(*esc >= 'A' && *esc <= 'F') {
				r = r * 16 + (*esc - 'A' + 10);
			}
			else {
				break;
			}
			esc++;
		}
		break;

	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
		r = c & 7;
		while(*esc >= '0' && *esc <= '7') {
			r = r * 8 + (*esc & 7);
			esc++;
		}
		break;

	default:
		/* copy any other character as is (including '\\') */
		r = c;
		break;

	}

	*s = esc;

	return r;
}


wchar_t next_char(struct option_to_char *o2c)
{
	const char	*g, *save;
	wchar_t		c1, c2, c3;

	/* here we determine the next character
	 * note that the o2c->index isn't an index from
	 * the start but instead in the current o2c->glyphs
	 * also the oc2->glyphs pointer is modified as we go
	 * this makes things much faster
	 */

	g = o2c->glyphs;

	/* end of string? nothing to do here! */
	if(g == NULL || *g == '\0') {
		o2c->letter = L'\0';
		return L'\0';
	}

	o2c->length++;

	/* determine the 1st character */
	c1 = escape_char(&g);
	save = g;

	c2 = escape_char(&g);
	if(c2 != L'-') {
		/* no range, return c1 and save g after we read c1 */
		o2c->glyphs = save;
		o2c->index = 0;
		o2c->letter = c1;
		return c1;
	}

	c3 = escape_char(&g);
	if(c1 > c3) {
		/* swap so it is ordered */
		c2 = c1;
		c1 = c3;
		c3 = c2;
	}

	c2 = c1 + o2c->index;

	if(o2c->index + 1 > c3 - c1) {
		/* Ha! We are done - next time read the following character */
		o2c->glyphs = g;
		o2c->index = 0;
	}
	else {
		o2c->index++;
	}

	o2c->letter = c2;
	return c2;
}



void sort_glyphs(void)
{
	wchar_t			wc, swap, *d;
	struct option_to_char	o2c;

	/* first we sort the glyphs */
	o2c.length = 0;
	o2c.index = 0;
	o2c.glyphs = glyphs;
	o2c.letter = 0;

	/* define the total length including duplicates */
	while((wc = next_char(&o2c)) != L'\0');

	/* allocate the destination */
	if(sorted_glyphs != NULL) {
		/* first free previous instance */
		free(sorted_glyphs);
	}
	/*
	 * Note: this may allocate a HUGE buffer, the only
	 * way around would be to regenerate the ranges
	 * in the output string... We will see with usage
	 * whether this would be necessary or not.
	 */
	sorted_glyphs = malloc((o2c.length + 1) * sizeof(wchar_t));
	if(sorted_glyphs == NULL) {
		fprintf(stderr, "ERROR: out of memory trying to allocate a buffer to sort glyphs.\n");
		exit(1);
	}

	/* reset the o2c parameters which we use next */
	o2c.index = 0;
	o2c.glyphs = glyphs;
	*sorted_glyphs = L'\0';
	while((wc = next_char(&o2c)) != L'\0') {
		d = sorted_glyphs;
		while(*d != '\0') {
			if(*d == wc) {
				goto skip_duplicate;
			}
			if(*d > wc) {
				break;
			}
			d++;
		}
		/* insert this character */
		wc = o2c.letter;
		do {
			swap = *d;
			*d = wc;
			d++;
			wc = swap;
		} while(wc != '\0');
		*d = '\0';
skip_duplicate:;
	}
}



struct point {
	double		x;
	double		y;
};

struct entry {
	long		count;		/* the number of points */
	long		points_index;	/* index to the first point in pts array */
};

struct array {
	long		max_points;	/* total size of pts */
	long		idx_points;	/* next available pts structure */
	struct point	*points;

	long		max_entries;	/* total size of entries */
	long		idx_entries;	/* next available entry */
	struct entry	*entries;
};


void init_array(struct array *array, long points)
{
	memset(array, 0, sizeof(array));

	array->max_points = points * 2;
	array->idx_points = 0;
	array->points = malloc(array->max_points * sizeof(struct point));

	array->max_entries = 100;
	array->idx_entries = 0;
	array->entries = malloc(array->max_entries * sizeof(struct entry));

	if(array->points == NULL || array->entries == NULL) {
		fprintf(stderr, "ERROR: out of memory trying to allocate an array for a glyph outline\n");
		exit(1);
	}
}


void alloc_point(struct array *array, long x, long y)
{
	struct point	*p;

	if(array->idx_points >= array->max_points) {
		/* enlarge buffer */
		array->max_points += 100;
		array->points = realloc(array->points, array->max_points * sizeof(struct point));
		if(array->points == NULL) {
			fprintf(stderr, "ERROR: out of memory trying to enlarge an array of points for a glyph outline\n");
			exit(1);
		}
	}

	/* one more point in this set of entries */
	array->entries[array->idx_entries - 1].count++;

	p = array->points + array->idx_points++;
	p->x = x;
	p->y = y;
}


void alloc_entry(struct array *array)
{
	long		i;

	if(array->idx_entries >= array->max_entries) {
		/* enlarge buffer */
		array->max_entries += 100;
		array->entries = realloc(array->entries, array->max_entries * sizeof(struct entry));
		if(array->entries == NULL) {
			fprintf(stderr, "ERROR: out of memory trying to enlarge an array of entires for a glyph outline\n");
			exit(1);
		}
	}

	i = array->idx_entries++;

	array->entries[i].count = 0;
	array->entries[i].points_index = array->idx_points;
}



/*
 * The inputs are u and points
 * u will vary from 0.0 (start point) to 1.0 (end point)
 * the points array must point to 4 points - 2 anchors and 2 control points
 * (start anchor, ctrl1, ctrl2, and end anchor)
 *
 * This function was simplified as much as possible (as far as I can tell)
 * and thus it doesn't look clear at, does it?
 *
 * The computation is as follow:
 *
 * R = (U . B) / 6 . P
 *
 * Where the matrix U is defined as:
 *
 *     | u ** 3 |
 * U = | u ** 2 |
 *     |   u    |
 *     |   1    |
 *
 * The matrix B is defined as:
 *
 *     | -1  3 -3  1 |
 * B = |  3 -6  3  0 |
 *     | -3  0  3  0 |
 *     |  1  4  1  0 |
 *
 * And P is defined from the input points - this works in 3 dimensional
 * space, and since we only have an X and Y set of coordinates, we assume
 * Z is always zero.
 *
 *     | x1 y1 0 1 |
 * P = | x2 y2 0 1 |
 *     | x3 y3 0 1 |
 *     | x4 y4 0 1 |
 *
 * R is the result of the products.
 *
 * Note: you can reuse this function with a Quadratic bezier curve by
 *       setting the P matrix as follow (repeat the control point twice):
 *
 *     | x1 y1 0 1 |
 * P = | x2 y2 0 1 |
 *     | x2 y2 0 1 |
 *     | x3 y3 0 1 |
 *
 */
void bspline_point_at_u(double u, const struct point *points, struct point *result)
{
	double		u2, u3, u3_div_6, r0, r1, r2;

#define	u1	u
#define	r3	u3_div_6

	/* u2 - u1 square, u3 - u1 cube */
	u3_div_6 = (u3 = (u2 = u1 * u1) * u1) / 6.0;

	r0 = 0.5 * (u2 - u1) - u3_div_6 + 1.0 / 6.0;
	r1 = u3 * 0.5 - u2 + 2.0 / 3.0;
	r2 = 0.5 * (u2 - u3 + u1) + 1.0 / 6.0;

	result->x =	r0 * points[0].x +
			r1 * points[1].x +
			r2 * points[2].x +
			r3 * points[3].x;

	result->y =	r0 * points[0].y +
			r1 * points[1].y +
			r2 * points[2].y +
			r3 * points[3].y;

#undef	u1
#undef	r3
}


void close_entry(struct array *array)
{
	long		i, idx, index, max;
	struct point	*points, r, p;

	i = array->idx_entries - 1;

	max = array->entries[i].count;
	if(max <= 3) {
		/*
		 * a line or a curve which already is a quadratic curve
		 * doesn't require any changes
		 */
		return;
	}

	/* in this case we have a "complex" bezier curve which requires simplification */
	points = malloc(max * sizeof(struct point));
	if(points == NULL) {
		fprintf(stderr, "ERROR: out of memory in trying to allocate a buffer to copy a the points of a bezier curve.\n");
		exit(1);
	}

	/* copy the curve in a temp. buffer, then free the entry and its points */
	index = array->entries[i].points_index;
	memcpy(points, array->points + index, max * sizeof(struct point));
	array->idx_points = index;
	array->idx_entries = i;		/* we will re-allocate this entry later */

	/* now we compute the new curve with only quadratic bezier curves */

	/*
	 * The algorithm supports any degree of B-spline curves and transforms
	 * them in only quadratic Bezier curves. At this time the precision is
	 * no good!
	 */

	p = points[0];
	max -= 2;
	for(idx = 1; idx < max; idx++) {
		/* compute the middle point -- this is a really bad approximation at times! */
		bspline_point_at_u(0.5, points + idx - 1, &r);

		/* TODO: before to save that we should check to see if the precision is
		 * good enough (it usually is fairly close but at times you can see that
		 * the curve isn't that good)
		 */
		alloc_entry(array);
		alloc_point(array, (long) p.x, (long) p.y);
		alloc_point(array, (long) points[idx].x, (long) points[idx].y);
		alloc_point(array, (long) r.x, (long) r.y);

		p = r;
	}

	alloc_entry(array);
	alloc_point(array, (long) r.x, (long) r.y);
	alloc_point(array, (long) points[idx].x, (long) points[idx].y);
	alloc_point(array, (long) points[idx + 1].x, (long) points[idx + 1].y);

	free(points);
}


void add_point(struct array *array, double x, double y, long ctrl)
{
	long		i;

	i = array->idx_entries;
	if(ctrl) {
		if(i <= 0) {
			/* changed from ERROR to WARNING proposed by Steve Crook - Sept 2003 */
			fprintf(stderr, "WARNING: found a glyph which starts with a control point.\n");
			printf("\n/* NOTE: this glyph starts with a control point */\n");
			/* exit(1); */
			alloc_entry(array);
		}
		alloc_point(array, (long) x, (long) y);
	}
	else {
		if(i > 0) {
			/* "close" this entry with this new point then "open" a new entry */
			alloc_point(array, (long) x, (long) y);
			close_entry(array);
		}
		alloc_entry(array);
		alloc_point(array, (long) x, (long) y);
	}
}


int empty_glyph(FT_Face face, wchar_t wc)
{
	return include_null == 0 && wc != ' '
		&& face->glyph->outline.n_points == 0;
}


void print_glyph(FT_Face face, wchar_t wc)
{
	char		buf[256], *s, *flg;
	int		c, i, maxcnt, maxpts, done, sx, sy;
	FT_Vector	*pts;
	struct array	array;
	struct point	*points;

	/* skip empty glyphs as required */
	if(empty_glyph(face, wc)) {
		return;
	}

	/* give the name of this letter in a comment */
	s = buf;
	*s++ = '\'';
	if(wc < ' ') {
		*s++ = '^';
		*s++ = wc + '@';
	}
	else if(wc < 0x7F) {
		*s++ = (char) wc;
	}
	else if(wc == 0x7F) {
		*s++ = '^';
		*s++ = '?';
	}
	else if(wc < 0xA0) {
		*s++ = '?';
		*s++ = wc - 0x40;
	}
	/* This only works if you're in ISO-8859-1.
	else if(wc < 0xFF) {
		*s++ = (char) wc;
	}
	*/
	else {
		s += sprintf(s, "\\x%lX", (unsigned long) wc);
	}
	*s++ = '\'';
	*s = '\0';

	if(face->glyph->format != ft_glyph_format_outline) {
		fprintf(stderr, "ERROR: glyph format for \"%s\" not supported (only outlines are supported).\n", buf);
		return;
	}

	/*
	 * the pts and flg pointers are always increased in the following loop
	 * and never reset
	 */
	pts = face->glyph->outline.points;
	flg = face->glyph->outline.tags;
	init_array(&array, face->glyph->outline.n_points);
	done = 0;
	maxcnt = face->glyph->outline.n_contours;
	fprintf(output, "\t\t\t\t/* Glyph: %s */\n", buf);
	fprintf(output, "\t\t\t\tglyph \"c_%04lX\" {\n", (unsigned long) wc);
	fprintf(output, "\t\t\t\t\tdefinitions.fill_glyph;\n");

/* define the set of points */
	for(c = 0; c < maxcnt; c++) {

		/*
		 * get the number of points in this entry
		 * then convert the font points in an array
		 * that we understand; finally we save the
		 * result in the output
		 */
		array.idx_points = 0;		/* reset the counters */
		array.idx_entries = 0;
		maxpts = face->glyph->outline.contours[c] + 1 - done;
		done += maxpts;
		sx = pts->x;
		sy = pts->y;
		while(maxpts > 0) {
			maxpts--;
			add_point(&array, (double) (pts->x - face->bbox.xMin) / factor, (double) (face->bbox.yMax - pts->y) / factor, (*flg & 1) == 0);
			pts++;
			flg++;
		}
		/* we now need to close the shape with a last call */
		alloc_point(&array, (long) ((double) (sx - face->bbox.xMin) / factor), (long) ((double) (face->bbox.yMax - sy) / factor));
		close_entry(&array);

/*
 * We have to use the face->bbox.yMax to invert the Y coordinates because the
 * descender may not include the entire font (it can be smaller). The descender
 * + ascender should be used to draw multiple lines of text, but can otherwise
 * be ignored.
printf("Ascender %d descender %d - bbox.yMin %ld, bbox.yMax %ld\n",
		face->ascender, face->descender, face->bbox.yMin, face->bbox.yMax);
*/
		/*
		 * at this point any curve which wasn't a quadratic bezier curve
		 * was already reduced
		 */

		fprintf(output, "\t\t\t\t\tmove: %g, %g;\n", array.points[0].x, array.points[0].y);

		fprintf(output, "\t\t\t\t\tpoints {");
		maxpts = array.idx_entries;
		for(i = 0; i < maxpts; i++) {
			/* once we reach here we have only two types of entries: 2 points or 3 points */
			points = array.points + array.entries[i].points_index;
			if(array.entries[i].count == 2) {
				fprintf(output, " %g, %g;",
					points[1].x, points[1].y);
			}
			else {
				fprintf(output, " %g, %g, %g, %g;",
					points[1].x, points[1].y,
					points[2].x, points[2].y);
			}
		}
		fprintf(output, " };\n");
	}

	/* save the full bounding box (when SSWF knows how to compute this, we should remove it from here) */
	fprintf(output, "\t\t\t\t\trect { %d, %ld, %ld, %ld };\n",
			0,
			-face->bbox.yMax * 1024 / face->units_per_EM,
			face->bbox.xMax - face->bbox.xMin,
			-face->bbox.yMin * 1024 / face->units_per_EM);
	fprintf(output, "\t\t\t\t};\n");
}


void ref_glyph(FT_Face face, FT_ULong wc, double adv)
{
	double		width;

	/* skip empty glyphs as required */
	if(empty_glyph(face, (wchar_t) wc)) {
		return;
	}

	fprintf(output, "\t\t\t\t\tchar: \"");
	if(wc >= ' ' && wc <= 0x7E) {
		if(wc == '"' || wc == '\\') {
			fprintf(output, "\\%c", (unsigned char) wc);
		}
		else {
			fprintf(output, "%c", (unsigned char) wc);
		}
	}
	else {
		fprintf(output, "\\\\x%04lX", wc);
	}
	fprintf(output, "\", c_%04lX", (unsigned long) wc);

	width = (double) face->glyph->advance.x / factor;
	if(width != adv) {
		fprintf(output, ", %g", width);
	}
	fprintf(output, ";\n");
}


void font_to_sswf(FT_Face face, const char *filename)
{
	FT_Error		error;
	FT_UInt			gindex;
	FT_ULong		wc;
	int			c, len, all;
	double			adv;
	char			*name, *d, enc[5];
#ifdef _MSVC
	char			*mbs;
#else
	char			mbs[MB_CUR_MAX];
#endif
	const char		*s, *encoding_name;
	const wchar_t		*ws;

	/* fix the name of the font so we can use it
	 * in SSWF scripts without the $( ... ) operator
	 */
	len = strlen(face->family_name);
	name = malloc(len + 2);
	if(name == NULL) {
		fprintf(stderr, "ERROR: out of memory trying to allocate a buffer for the font name.\n");
		exit(1);
	}
	s = face->family_name;
	d = name;
	/* can't start with a digit or a period */
	if((*s >= '0' && *s <= '9') || *s == '.') {
		*d++ = '_';
	}
	while(*s != '\0') {
		if(strchr(" !@#$%^&*()-+={}[]\\|:;\"'`<>,./?~", *s) == NULL) {
			*d = *s;
		}
		else if(d == name || d[-1] != '_') {
			/* we try to avoid having multiple '_' in a row */
			*d = '_';
		}
		d++;
		s++;
	}
	*d = '\0';

	/* the user wants to know the encodings only */
	if(encoding) {
		fprintf(output, "Family: \"%s\"\n", name);
		for(c = 0; c < face->num_charmaps; c++) {
			encoding_name = encoding_to_name(face->charmaps[c]->encoding, enc);
			fprintf(output, "\t%s - %s\n", enc, encoding_name);
		}
		free(name);
		return;
	}

	if(face->num_charmaps <= 0) {
		fprintf(stderr, "ERROR: font \"%s\" doesn't have any encoding information.\n", name);
		free(name);
		return;
	}

	/* select the encoding now */
	if(char_map_encoding == ft_encoding_none || char_map_encoding == (FT_Encoding) -1) {
		/* use 1st available encoding */
		FT_Set_Charmap(face, face->charmaps[0]);
	}
	else {
		error = FT_Select_Charmap(face, char_map_encoding);
		if(error) {
			encoding_name = encoding_to_name(char_map_encoding, enc);
			fprintf(stderr, "ERROR: can't find specified encoding (%s/%s) in this font (%s).\n",
					enc, encoding_name, name);
			free(name);
			return;
		}
	}

	/*
	 * The file is actually only in ASCII, but defining the encoding as
	 * ISO-8859-1 is better because then the parser doesn't do any
	 * translation (the parser isn't smart enough to just understand
	 * ASCII...)
	 */
	fprintf(output, "/* encoding=\"iso8859-1\" */\n");
	fprintf(output, "block sswf {\n");
	fprintf(output, "\tblock fnt {\n");
	fprintf(output, "\t\tlist \"%s\" {\n", name);

	/* the following is really only so we can reference a fill - the color isn't used */
	fprintf(output, "\t\t\tlist definitions {\n");
	fprintf(output, "\t\t\t\tfill style \"fill_glyph\" { color { 0, 0, 0 } };\n");
	fprintf(output, "\t\t\t};\n");
	fprintf(output, "\t\t\tlist insert {\n");



	// Ask for the font to be setup according to the Flash
	// movies specifications (i.e. an EM of 1024x1024). The
	// DPI isn't specified so we try for 75x75 since that's
	// the usual to draw on a screen device.
	FT_Set_Char_Size(face, 1024 << 6, 1024 << 6, 75, 75);
	//FT_Set_Pixel_Sizes(face, 1024, 1024);

	// in the following we first generate the list of glyphs
	// note that we use the 'glyphs' variable as defined by
	// the end user to know which glyphs to print out; note
	// however that the special case "*" means all in which
	// case we go through the entire map
	all = glyphs == NULL || strcmp(glyphs, "*") == 0;
	if(all) {
		if(comment != 0) {
			fprintf(output, "\t\t\t\t/* Extract all the available characters */\n");
		}
		wc = FT_Get_First_Char(face, &gindex);
		while(gindex != 0) {
			/*
			 * skip ^@ at all times - it doesn't work well in C strings anyway
			 * also, keep control characters only if requested to do so
			 */
			if(wc != 0 && (output_control || wc >= ' ')) {
				error = FT_Load_Glyph(face, gindex, FT_LOAD_NO_BITMAP);
				if(error) {
					fprintf(stderr, "ERROR: couldn't load glyph 0x%04X from file \"%s\" (Error: %d).\n", (int) wc, filename, error);
				}
				else if(empty_glyph(face, (wchar_t) wc)) {
					fprintf(stderr, "WARNING: the requested character 0x%04X from file \"%s\" is empty.\n", (int) wc, filename);
				}
				else {
					print_glyph(face, (wchar_t) wc);
				}
			}
			wc = FT_Get_Next_Char(face, wc, &gindex);
		}
	}
	else {
		if(comment != 0) {
			/* TODO: the 2nd string needs control characters to be remapped */
			fprintf(output, "\t\t\t\t/* Extracted characters: \"%s\" (%ls) */\n", glyphs, sorted_glyphs);
		}
		ws = sorted_glyphs;
		while(*ws != L'\0') {
			wc = *ws;
			error = FT_Load_Char(face, wc, FT_LOAD_NO_BITMAP);
			len = wctomb(NULL, wc);
#ifdef _MSVC
			mbs = malloc(len + 1);
#endif
			mbs[wctomb(mbs, wc)] = '\0';
			if(error) {
				fprintf(stderr, "ERROR: couldn't load char #%s (0x%04X) from file \"%s\" (Error: %d).\n", mbs, (int) wc, filename, error);
			}
			else if(empty_glyph(face, (wchar_t) wc)) {
				fprintf(stderr, "WARNING: the requested character %s (0x%04X) from file \"%s\" is empty.\n", mbs, (int) wc, filename);
			}
			else {
				print_glyph(face, (wchar_t) wc);
			}
#ifdef _MSVC
			free(mbs);
#endif
			ws++;
		}
	}

	fprintf(output, "\t\t\t\tfont \"fnt\" {\n");
	fprintf(output, "\t\t\t\t\t\"%s\";\n", face->family_name);

	/* try to determine the width of the space character */
	adv = advance;
	error = FT_Load_Char(face, ' ', FT_LOAD_NO_BITMAP);
	if(error == 0) {
		fprintf(output, "\t\t\t\t\tspace: %g;\n", (double) face->glyph->advance.x / factor);
		if(advance == 0.0) {
			/* use the space advance as the default when user didn't define one */
			/* TODO: once we have a list of all the glyphs advance then we could
			 * compute the one which is used the most and thus will be useful
			 * to declare here
			 */
			adv = (double) face->glyph->advance.x / factor;
		}
	}
	if(adv != 0.0) {
		fprintf(output, "\t\t\t\t\tadvance: %g;\n", adv);
	}
	fprintf(output, "\t\t\t\t\tlayout: %d, %ld, %d; // Original units per EM was %u\n",
			face->ascender * 1024 / face->units_per_EM,
			labs(face->descender) * 1024 / face->units_per_EM,	/* the FT_... doc says it should be positive... but it isn't! */
			face->height * 1024 / face->units_per_EM,
			face->units_per_EM);

	switch(face->charmap->encoding) {
	case ft_encoding_unicode:
		fprintf(output, "\t\t\t\t\ttype: \"unicode\"");
		break;

	case ft_encoding_sjis:
		fprintf(output, "\t\t\t\t\ttype: \"shiftjis\"");
		break;

	default:
		fprintf(output, "\t\t\t\t\ttype: \"ascii\"");
		break;

	}
	if(face->style_flags & FT_STYLE_FLAG_BOLD) {
		fprintf(output, ", \"bold\"");
	}
	if(face->style_flags & FT_STYLE_FLAG_ITALIC) {
		fprintf(output, ", \"italic\"");
	}
	fprintf(output, ";\n");

	if(all) {
		wc = FT_Get_First_Char(face, &gindex);
		while(gindex != 0) {
			if(wc != 0 && (output_control || wc >= ' ')) {
				error = FT_Load_Glyph(face, gindex, FT_LOAD_NO_BITMAP);
				if(error == 0 && !empty_glyph(face, (wchar_t) wc)) {
					ref_glyph(face, wc, advance);
				}
			}
			wc = FT_Get_Next_Char(face, wc, &gindex);
		}
	}
	else {
		ws = sorted_glyphs;
		while(*ws != L'\0') {
			wc = *ws;
			error = FT_Load_Char(face, wc, FT_LOAD_NO_BITMAP);
			if(error == 0 && !empty_glyph(face, (wchar_t) wc)) {
				ref_glyph(face, wc, advance);
			}
			ws++;
		}
	}



	fprintf(output, "\t\t\t\t};\n");

	fprintf(output, "\t\t\t} px;\n");	/* closes the list \"font name\" ... */
	fprintf(output, "\t\t};\n");	/* closes the list \"font name\" ... */
	fprintf(output, "\t};\n");
	fprintf(output, "};\n");

	free(name);
}



FT_Byte *read_stdin(size_t *size)
{
	int		c, left;
	size_t		max;
	FT_Byte		*buf, *s;

	buf = malloc(1024);
	if(buf == NULL) {
		fprintf(stderr, "ERROR: can't allocate a memory buffer for the stdin file.\n");
		exit(1);
	}
	max = 1024;
	left = 1024;
	s = buf;
	while((c = getchar()) != EOF) {
		if(left <= 0) {
			max += 1024;
			buf = realloc(buf, max);
			if(buf == NULL) {
				fprintf(stderr, "ERROR: can't allocate a memory buffer for the stdin file.\n");
				exit(1);
			}
			left = 1024;
			s = buf + max - left;
		}
		*s++ = c;
		left--;
	}

	*size = max - left;

	return buf;
}


void run_font(const char *filename)
{
	FT_Error	error;
	FT_Face		face;
	size_t		size;
	FT_Byte		*font_buffer;

	if(strcmp(filename, "-") == 0) {
		/* we have to read the stdin file instead */
		if(verbose != 0) {
			fprintf(stderr, "INFO: reading the standard input.\n");
		}
		font_buffer = read_stdin(&size);
		error = FT_New_Memory_Face(library, font_buffer, size, 0, &face);
		if(error) {
			fprintf(stderr, "ERROR: the standard input doesn't represent a valid font.\n");
			errcnt++;
			free(font_buffer);
			return;
		}
		font_to_sswf(face, filename);
		error = FT_Done_Face(face);
		if(error) {
			fprintf(stderr, "WARNING: the standard input font face generated an error on closure.\n");
		}
		free(font_buffer);
	}
	else {
		if(verbose != 0) {
			fprintf(stderr, "INFO: reading the font from file \"%s\".\n", filename);
		}
		error = FT_New_Face(library, filename, 0, &face);
		if(error) {
			fprintf(stderr, "ERROR: the file \"%s\" can't either be open, read or is an invalid font file.\n", filename);
			errcnt++;
			return;
		}
		font_to_sswf(face, filename);
		error = FT_Done_Face(face);
		if(error) {
			fprintf(stderr, "WARNING: the font face read from the file \"%s\" generated an error on closure.\n", filename);
		}
	}
}


void show_encodings(void)
{
	int		i, j;
	char		enc[5];

	enc[4] = '\0';
	for(i = 0; i < MAX_ENCODINGS; i++) {
		enc[0] = list_of_encodings[i].encoding >> 24;
		enc[1] = list_of_encodings[i].encoding >> 16;
		enc[2] = list_of_encodings[i].encoding >> 8;
		enc[3] = list_of_encodings[i].encoding;
		for(j = 0; j < 4; j++) {
			if(enc[j] == (char) 0xFF) {
				enc[j] = '?';
			}
			else if(enc[j] == (char) 0) {
				enc[j] = ' ';
			}
			else if(enc[j] < ' ' || enc[j] >= 0x7F) {
				enc[j] = '.';
			}
		}
		printf("%5d. %s - %s\n", i + 1, enc, list_of_encodings[i].name);
	}

	exit(1);
}


FT_Encoding string_to_encoding(const char *str)
{
	struct encoding_list	*l;
	int			i;
	char			*s, buf[256];
	FT_Encoding		value;

	/* first we clip the string (remove starting and ending spaces) */
	while(isspace((unsigned char) *str)) {
		str++;
	}
	strncpy(buf, str, sizeof(buf) - 1);
	buf[sizeof(buf) - 1] = '\0';
	s = buf + strlen(buf);
	while(s > buf && isspace((unsigned char) s[-1])) {
		s--;
	}
	*s = '\0';

	/* people may give us a name or directly an encoding */
	if(s - buf <= 4) {
		value = 0;
		s = buf;
		for(i = 0; i < 4; i++) {
			if(*s == '\0') {
				/* fill with spaces */
				value = (value << 8) + ' ';
			}
			else {
				value = (value << 8) + *s;
				s++;
			}
		}
	}
	else {
		/* no encoding equals -1 -- so use -2 as a "bad" encoding */
		value = (FT_Encoding) -2;
	}

	l = list_of_encodings;
	for(i = 0; i < MAX_ENCODINGS; i++) {
		if(strcasecmp(str, l[i].name) == 0 || l[i].encoding == value) {
			return l[i].encoding;
		}
	}

	fprintf(stderr, "ERROR: can't determine what encoding \"%s\" is.\n", str);

	return -1;
}


void usage(const char *cmd)
{
	fprintf(stderr, "%s V%s -- Written by Alexis Wilke for Made to Order Software Corp. (c) 2002-2009\n", progname, version);
	fprintf(stderr, "Usage: %s [-opt] <font-file> ...\n", cmd);
	fprintf(stderr, "\n");
	fprintf(stderr, "<font-file> can be \"-\" to process the standard input as a font.\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "The special option \"--\" can be used to stop parsing options and take\n");
	fprintf(stderr, "all the other parameters as font filenames\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "-opt can be one or more of the following:\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "  NOTE: options given after a given font-file don't apply to that\n");
	fprintf(stderr, "        font-file but only the following ones\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "  --advance(=| )<value>\n");
	fprintf(stderr, "                      specify the default number of pixels to use to advanced\n");
	fprintf(stderr, "                      to the next character.\n");
	fprintf(stderr, "  -c[0|1]             print comments in output file\n");
	fprintf(stderr, "  --control           print out the control characters (otherwise starts at\n");
	fprintf(stderr, "                      0x20 -- space)\n");
	fprintf(stderr, "  -e[0|1|2]           output the encodings only\n");
	fprintf(stderr, "  -f[ ]<factor>       multiply all of the font coordinates by factor\n");
	fprintf(stderr, "                      (a floating-point value)\n");
	fprintf(stderr, "  -g[ ]<glyphs> or --glyphs(=| )[<glyphs>]\n");
	fprintf(stderr, "                      list of characters to extract from the font (ISO-8859-1 only)\n");
	fprintf(stderr, "  -h or --help        prints out this help screen\n");
	fprintf(stderr, "  -m[ ]<name> or --map(=| )[<name>]\n");
	fprintf(stderr, "                      specify which encoding map to use; use -e to have a list\n");
	fprintf(stderr, "                      of available encodings in a given font; by default, use\n");
	fprintf(stderr, "                      the 1st entry\n");
	fprintf(stderr, "  -n[0|1] or --include-null or --no-null\n");
	fprintf(stderr, "                      include all characters even when empty\n");
	fprintf(stderr, "  -o[ ][<filename>] or --output(=| )[<filename>]\n");
	fprintf(stderr, "                      specify a filename where the result is written; the\n");
	fprintf(stderr, "                      default is stdout\n");
	fprintf(stderr, "  -q                  quiet, i.e. no verbosity (default)\n");
	fprintf(stderr, "  -v                  increase verbosity by one\n");
	fprintf(stderr, "  --version           print out the version of this tool and exit\n");
	exit(1);
}

void run_command_line(int argc, char *argv[])
{
	int		i, j, len, moreoptions;
	char		*s, *output_filename;

	// define the current program name
	len = strlen(argv[0]);
	s = argv[0] + len;
	while(s > argv[0]) {
		if(s[-1] == '/') {
			break;
		}
		s--;
	}
	progname = s;

	output = stdout;			/* the default output */
	char_map_encoding = (FT_Encoding) -1;	/* use the 1st entry by default */
	factor = 64.0;

	moreoptions = 1;
	for(i = 1; i < argc; i++) {
		if(argv[i][0] == '-' && moreoptions != 0) {
			if(argv[i][1] == '-') {
				/* full word options */
				if(argv[i][2] == '\0') {
					moreoptions = 0;
				}
				else if(strncmp(argv[i] + 2, "advance", 7) == 0) {
					advance = 0.0;
					if(argv[i][2 + 7] == '=') {
						/* name stuck with the option */
						advance = strtod(argv[i] + 2 + 7 + 1, NULL);
					}
					else {
						/* get the next argv[] unless it starts with a '-' */
						if(i + 1 < argc
						&& argv[i + 1][0] != '-') {
							i++;
							advance = strtod(argv[i], NULL);
						}
					}
				}
				else if(strcmp(argv[i] + 2, "control") == 0) {
					output_control = 1;
				}
				else if(strncmp(argv[i] + 2, "glyphs", 6) == 0) {
					glyphs = NULL;
					if(argv[i][2 + 6] == '=') {
						/* name stuck with the option */
						glyphs = argv[i] + 2 + 6 + 1;
						sort_glyphs();
					}
					else {
						/* get the next argv[] unless it starts with a '-' */
						if(i + 1 < argc
						&& argv[i + 1][0] != '-') {
							i++;
							glyphs = argv[i];
							sort_glyphs();
						}
					}
				}
				else if(strcmp(argv[i] + 2, "include-null") == 0) {
					include_null = 1;
				}
				else if(strncmp(argv[i] + 2, "map", 3) == 0) {
					/* first, we revert to the default */
					char_map_encoding = (FT_Encoding) -1;
					if(argv[i][2 + 3] == '=') {
						/* name stuck with the option */
						char_map_encoding = string_to_encoding(argv[i] + 2 + 3 + 1);
					}
					else {
						/* get the next argv[] unless it starts with a '-' */
						if(i + 1 < argc
						&& argv[i + 1][0] != '-') {
							i++;
							char_map_encoding = string_to_encoding(argv[i]);
						}
					}
				}
				else if(strcmp(argv[i] + 2, "no-null") == 0) {
					include_null = 0;
				}
				else if(strncmp(argv[i] + 2, "output", 6) == 0) {
					/* first, we revert to the default */
					if(output != stdout) {
						fclose(output);
						output = stdout;
					}
					output_filename = NULL;
					if(argv[i][2 + 6] == '=') {
						/* name stuck with the option */
						output_filename = argv[i] + 2 + 6 + 1;
						if(output_filename[0] != '\0'
						&& strcmp(output_filename, "-") != 0) {
							output = fopen(output_filename, "w");
						}
					}
					else {
						/* get the next argv[] unless it starts with a '-' */
						if(i + 1 < argc
						&& argv[i + 1][0] != '-') {
							i++;
							output_filename = argv[i];
							output = fopen(output_filename, "w");
						}
					}
					if(output == NULL) {
						fprintf(stderr, "ERROR: can't open ouptut file \"%s\" (errno: %d).\n", output_filename, errno);
						exit(1);
					}
				}
				else if(strcmp(argv[i] + 2, "version") == 0) {
					printf("%s\n", version);
					exit(1);
				}
				else if(strcmp(argv[i] + 2, "help") == 0) {
					usage(argv[0]);
					/*NOTREACHED*/
				}
				else {
					fprintf(stderr, "ERROR: unknown option \"%s\", process stopped.\n", argv[i]);
					exit(1);
				}
			}
			else if(argv[i][1] == '\0') {
				// got a request to process stdin as a font...
				run_font("-");
			}
			else {
				len = strlen(argv[i]);
				for(j = 1; j < len; j++) {
					switch(argv[i][j]) {
					case 'c':
						switch(argv[i][j + 1]) {
						case '0':
							comment = 0;
							j++;
							break;

						case '1':
							comment = 1;
							j++;
							break;

						default:
							comment = 1;
							break;

						}
						break;

					case 'e':
						switch(argv[i][j + 1]) {
						case '0':
							encoding = 0;
							j++;
							break;

						case '1':
							encoding = 1;
							j++;
							break;

						case '2':
							show_encodings();
							/*NOTREACHED*/
							break;

						default:
							encoding = 1;
							break;

						}
						break;

					case 'f':
						if(argv[i][j + 1] != '\0') {
							factor = strtod(argv[i] + j + 1, NULL) * 64.0;
						}
						else if(i + 1 < argc) {
							i++;
							factor = strtod(argv[i], NULL) * 64.0;
						}
						else {
							factor = 64.0;
						}
						j = len;
						break;

					case 'g':
						if(argv[i][j + 1] != '\0') {
							glyphs = argv[i] + j + 1;
							sort_glyphs();
						}
						else if(i + 1 < argc) {
							i++;
							glyphs = argv[i];
							sort_glyphs();
						}
						else {
							glyphs = NULL;
						}
						j = len;
						break;

					case 'h':
						usage(argv[0]);
						/*NOTREACHED*/
						break;

					case 'm':
						if(argv[i][j + 1] != '\0') {
							char_map_encoding = string_to_encoding(argv[i] + j + 1);
						}
						else if(i + 1 < argc) {
							i++;
							char_map_encoding = string_to_encoding(argv[i]);
						}
						else {
							char_map_encoding = (FT_Encoding) -1;
						}
						j = len;
						break;

					case 'n':
						include_null = 1;
						switch(argv[i][j + 1]) {
						case '0':
							include_null = 0;
							j++;
							break;

						case '1':
							j++;
							break;

						}
						break;

					case 'o':
						if(output != stdout) {
							fclose(output);
							output = stdout;
						}
						output_filename = NULL;
						if(argv[i][j + 1] != '\0') {
							output_filename = argv[i] + j + 1;
						}
						else if(i + 1 < argc) {
							i++;
							output_filename = argv[i];
						}
						j = len;
						if(output_filename != NULL) {
							output = fopen(output_filename, "w");
							if(output == NULL) {
								fprintf(stderr, "ERROR: can't open ouptut file \"%s\" (errno: %d).\n", output_filename, errno);
								exit(1);
							}
						}
						break;

					case 'q':
						verbose = 0;
						break;

					case 'v':
						verbose++;
						break;

					default:
						fprintf(stderr, "ERROR: unknown option \"-%c\", process stopped.\n", argv[i][j]);
						exit(1);

					}
				}
			}
		}
		else {
			/* got a filename, process this font */
			run_font(argv[i]);
		}
	}
}


int main(int argc, char *argv[])
{
	FT_Error	error;

	/* we need to initialize the free font library only once */
	error = FT_Init_FreeType(&library);
	if(error) {
		fprintf(stderr, "ERROR: can't initialize the free type library.\n");
		exit(1);
	}

	run_command_line(argc, argv);

	error = FT_Done_FreeType(library);
	if(error) {
		fprintf(stderr, "WARNING: the release of the FreeFont library failed.\n");
	}

	exit(errcnt == 0 ? 0 : 1);
	return 0;
}

