
/*=========================================================================
 *
 * bdf2psf, a utility to convert BDF fonts to PSF version 2 format.
 * Copyright (C) 2003-2005,2007-2008 Zachary T. Smith, fbui@comcast.net
 *
 * This module is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This module is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this module; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * (See the file COPYING in the main directory of this archive for
 * more details.)
 *
 *=======================================================================*/


/* This BDF font reader code is derived from my FBUI library.
 * See http://home.comcast.net/~fbui.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/stat.h>

#include "bdf_font.h"


static char *read_buffer = NULL;
static int dpi = 100;


Font *
Font_new (void)
{
	Font *nu;

	nu = (Font*) malloc(sizeof (Font));
	if (!nu)
		fatal_memory (__FUNCTION__);
	else {
		bzero (nu, sizeof (Font));
	}

	int nchars = 256;

	nu->last_char = nu->first_char + nchars - 1;

	nu->bitmaps = malloc (sizeof(char*)*nchars);
	nu->bbw = malloc (nchars);
	nu->bbh = malloc (nchars);
	nu->bbxoff = malloc (nchars);
	nu->bbyoff = malloc (nchars);

	if (!nu->bitmaps || !nu->bbh || !nu->bbw ||
	    !nu->bbyoff || !nu->bbxoff)
		fatal_memory(__FUNCTION__);

	bzero (nu->bitmaps, sizeof(char*)*nchars);
	bzero (nu->bbw, nchars);
	bzero (nu->bbh, nchars);
	bzero (nu->bbyoff, nchars);
	bzero (nu->bbxoff, nchars);
	return nu;
}

void
Font_free (Font* font)
{
	if (!font)
		return; // error

	if (font->bbw) 
		free ((void*) font->bbw);
	if (font->bbh) 
		free ((void*) font->bbh);
	if (font->bbxoff) 
		free ((void*) font->bbxoff);
	if (font->bbyoff) 
		free ((void*) font->bbyoff);
	if (font->bitmaps) 
		free ((void*) font->bitmaps);

	/* XX Need to free individual bitmaps */

	free ((void*)font);
}


void 
Font_char_dims (Font *font, u8 ch, short *w, short *asc, short *desc)
{
	if (!font || !w || !asc || !desc)
		return; // error

	ch -= font->first_char;
	*w = font->bbw[ch];
	*asc = font->ascent;
	*desc = font->descent;
}


void
Font_string_dims (Font *font, unsigned char *str, short *w, short *a, short *d)
{
	if (!font || !str || !w || !a || !d) {
		if (w)
			*w = 0;
		if (a)
			*a = 0;
		if (d)
			*d = 0;
		return; // error
	}

	short w0 = 0;
	int ch;

	while ((ch = *str++)) {
		ch -= font->first_char;
		if (ch >= 0)
			w0 += font->bbw[ch];
	}

	*w = w0;
	*a = font->ascent;
	*d = font->descent;
}



/* Process property line.
 */
bool
process_line (Font *f, char *line, char *end)
{
	char *param = strchr(line, ' ');

	if (param) {
		*param++ = 0;

		if (*param == '"') {
			char *p;
			param++;
			p = strchr(param, '"');
			if(p) *p=0;
		}
	}

// printf ("LINE %s PARAM %s\n", line,param);
	if (param && !strcmp(line, "FULL_NAME"))
		f->fullname = strdup (param);
	else if (param && !strcmp(line, "SIZE")) {
		int a,b,c;
		if (3==sscanf(param,"%d %d %d",&a,&b,&c))
			f->size = a;
		else
			warning ("improper BDF size information");
	}
	else if (param && !strcmp(line, "FOUNDRY")) {
		f->foundry = strdup (param);
	}
	else if (param && !strcmp(line, "WEIGHT_NAME")) {
		if (!strcmp(param, "BOLD"))
			f->weight = FONT_WEIGHT_BOLD;
		else
			f->weight = FONT_WEIGHT_LIGHT;
	}
	else if (param && !strcmp(line, "FACE_NAME"))
		f->name = strdup(param);
	else if (param && !strcmp(line, "FONT_ASCENT"))
		f->ascent = atoi(param);
	else if (param && !strcmp(line, "FONT_DESCENT"))
		f->descent = atoi(param);
	else if (param && !strcmp(line, "CHARS")) {
		f->nchars = atoi(param);
		if (f->nchars > 255)
			fatal ("too many chars");
		return true;
	}
	else if (!strcmp(line, "ENDPROPERTIES"))
		;
	return false;
}


/* Font reader.
 * Takes an empty Font & a file path.
 */
bool
Font_read_bdf (Font* font, char *path)
{
	int i;
	FILE *f;
	struct stat statbuf;
	u32 size;

	if (stat (path, &statbuf)) {
		perror("stat");
		return false;
	}

	size = statbuf.st_size;

	if (font->path) 
		free(font->path);
	font->path = strdup (path);

	/*-----------------------------------
	 * Read in entire file into memory.
	 */
	f = fopen (path, "rb");
	if (!f)
		return false;

        read_buffer = malloc (size);
        if (!read_buffer)
		fatal_memory (__FUNCTION__);
        i = fread (read_buffer,1,size,f);
        if (i != size) {
		warning ("cannot read entire font file");
		return false;
	}
        fclose (f);
        f = NULL;
        char *ptr = read_buffer;
	char *end = ptr + size;

	/*-----------------------------------
	 * Process the properties.
	 */
	while (ptr < end) {
		char *next = ptr;

		while (next < end && *next != '\n')
			next++;
		if (next < end)
			*next++ = 0;

		if (process_line (font, ptr, end))
			break;

		ptr = next;
	}

	if (ptr >= end)
		fatal ("oops, BDF file lacks bitmaps");

	/*-----------------------------------
	 * Process the characters.
	 */
	bool in_bitmap = false;
	int start_char = 999;
	int end_char = 0;
#define MAXFONTHEIGHT 32
	u32 bitmap_rows[MAXFONTHEIGHT];
	unsigned short bitmap_ix = 0;
	int bitmap_rownum = 0;
	int encoding = -1;
	int width, height, xoff, yoff;

	while (ptr < end) {
		char *next = ptr;

		while (next < end && *next != '\n')
			next++;
		if (next < end)
			*next++ = 0;

		char *p = strchr(ptr, ' ');
		if (p) {
			*p++ = 0;
		}

		if (!in_bitmap) {
			bitmap_ix = 0;
			bitmap_rownum = 0;
			if (!strcmp(ptr,"ENCODING")) {
				encoding = atoi(p);
				if (encoding < 0) {
					/* Negative encoding# chars usually
					 * follow positive. Force loop exit.
					 */
					ptr = end;
					continue;
				}
				if (encoding > end_char)
					end_char = encoding;
				if (encoding < start_char)
					start_char = encoding;
			}
			else if (!strcmp(ptr, "BBX")) {
				if (4 == sscanf(p, "%d %d %d %d",
					&width, &height, &xoff, &yoff)) {
				}
			}
			else if (!strcmp(ptr, "BITMAP"))
				in_bitmap = true;
			else
				; // printf ("UNKNOWN '%s'\n", ptr);

		} else {
			unsigned int bytes;

			if (!strcmp(ptr, "ENDCHAR")) {
				u32 *buf = malloc(bitmap_rownum*4);
				if (!buf)
					fatal_memory(__FUNCTION__);

				in_bitmap = false;

// printf ("width=%d, ht=%d, encoding=%d\n", width, bitheight,encoding);
				font->bitmaps[encoding] = buf;
				memcpy (buf, bitmap_rows, bitmap_rownum*4);

				font->bbw[encoding] = width;
				font->bbh[encoding] = height;
				font->bbxoff[encoding] = xoff;
				font->bbyoff[encoding] = yoff;

				int ascent = height + yoff;
				int descent = -yoff;
				if (ascent > font->ascent)
					font->ascent = ascent;
				if (descent > font->descent)
					font->descent = descent;
				
				bitmap_ix = 0;
				bitmap_rownum = 0;
				continue;
			}

			bytes = strlen(ptr) / 2;
			u32 row;
			if (bytes>0 && bytes<=4) {
				int k;
				char *ptn[4]={"%02lx","%04lx","%06lx","%08lx"};
				unsigned char shift[4]={24, 16, 8, 0};
				k=sscanf(ptr,ptn[bytes-1],&row);
				row <<= shift[bytes-1];
			} else
				fatal("font is too wide");

			if (bitmap_rownum >= MAXFONTHEIGHT)
				fatal ("font is too tall");
			bitmap_rows[bitmap_rownum++] = row;
		}

		ptr = next;
	}
	free (read_buffer);

	printf ("Loaded font '%s%s%s %dpt'.\n",
		font->name,
		font->weight == FONT_WEIGHT_BOLD ? " bold" : "",
		font->italic ? " italic" : "",
		font->size);

	font->loaded = 1;
	font->dpi = dpi;

	return true;
}

